diff --git a/clang/include/clang/AST/APValue.h b/clang/include/clang/AST/APValue.h index 525d88cab2a1..664a78f5d01c 100644 --- a/clang/include/clang/AST/APValue.h +++ b/clang/include/clang/AST/APValue.h @@ -22,6 +22,7 @@ namespace clang { class CharUnits; class DiagnosticBuilder; class Expr; + class Decl; /// APValue - This class implements a discriminated union of [uninitialized] /// [APSInt] [APFloat], [Complex APSInt] [Complex APFloat], [Expr + Offset]. @@ -38,6 +39,11 @@ public: LValue, Vector }; + union LValuePathEntry { + const Decl *BaseOrMember; + uint64_t ArrayIndex; + }; + struct NoLValuePath {}; private: ValueKind Kind; @@ -57,6 +63,8 @@ private: ~Vec() { delete[] Elts; } }; + struct LV; + enum { MaxSize = (sizeof(ComplexAPSInt) > sizeof(ComplexAPFloat) ? sizeof(ComplexAPSInt) : sizeof(ComplexAPFloat)) @@ -87,10 +95,15 @@ public: APValue(const APValue &RHS) : Kind(Uninitialized) { *this = RHS; } - APValue(const Expr* B, const CharUnits &O) : Kind(Uninitialized) { - MakeLValue(); setLValue(B, O); + APValue(const Expr *B, const CharUnits &O, NoLValuePath N) + : Kind(Uninitialized) { + MakeLValue(); setLValue(B, O, N); } - APValue(const Expr* B); + APValue(const Expr *B, const CharUnits &O, ArrayRef Path) + : Kind(Uninitialized) { + MakeLValue(); setLValue(B, O, Path); + } + APValue(const Expr *B); ~APValue() { MakeUninit(); @@ -174,6 +187,8 @@ public: const CharUnits &getLValueOffset() const { return const_cast(this)->getLValueOffset(); } + bool hasLValuePath() const; + ArrayRef getLValuePath() const; void setInt(const APSInt &I) { assert(isInt() && "Invalid accessor"); @@ -204,7 +219,9 @@ public: ((ComplexAPFloat*)(char*)Data)->Real = R; ((ComplexAPFloat*)(char*)Data)->Imag = I; } - void setLValue(const Expr *B, const CharUnits &O); + void setLValue(const Expr *B, const CharUnits &O, NoLValuePath); + void setLValue(const Expr *B, const CharUnits &O, + ArrayRef Path); const APValue &operator=(const APValue &RHS); diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index 3ea590cd84b1..af46e1799c97 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -500,10 +500,6 @@ public: /// lvalue with link time known address, with no side-effects. bool EvaluateAsLValue(EvalResult &Result, const ASTContext &Ctx) const; - /// EvaluateAsLValue - Evaluate an expression to see if we can fold it to an - /// lvalue, even if the expression has side-effects. - bool EvaluateAsAnyLValue(EvalResult &Result, const ASTContext &Ctx) const; - /// \brief Enumeration used to describe the kind of Null pointer constant /// returned from \c isNullPointerConstant(). enum NullPointerConstantKind { diff --git a/clang/lib/AST/APValue.cpp b/clang/lib/AST/APValue.cpp index 0f315bbccda2..12420fd8091e 100644 --- a/clang/lib/AST/APValue.cpp +++ b/clang/lib/AST/APValue.cpp @@ -20,14 +20,41 @@ using namespace clang; namespace { - struct LV { - const Expr* Base; + struct LVBase { + const Expr *Base; CharUnits Offset; + unsigned PathLength; }; } +struct APValue::LV : LVBase { + static const unsigned InlinePathSpace = + (MaxSize - sizeof(LVBase)) / sizeof(LValuePathEntry); + + /// Path - The sequence of base classes, fields and array indices to follow to + /// walk from Base to the subobject. When performing GCC-style folding, there + /// may not be such a path. + union { + LValuePathEntry Path[InlinePathSpace]; + LValuePathEntry *PathPtr; + }; + + LV() { PathLength = (unsigned)-1; } + ~LV() { if (hasPathPtr()) delete [] PathPtr; } + + void allocPath() { + if (hasPathPtr()) PathPtr = new LValuePathEntry[PathLength]; + } + + bool hasPath() const { return PathLength != (unsigned)-1; } + bool hasPathPtr() const { return hasPath() && PathLength > InlinePathSpace; } + + LValuePathEntry *getPath() { return hasPathPtr() ? PathPtr : Path; } +}; + APValue::APValue(const Expr* B) : Kind(Uninitialized) { - MakeLValue(); setLValue(B, CharUnits::Zero()); + MakeLValue(); + setLValue(B, CharUnits::Zero(), ArrayRef()); } const APValue &APValue::operator=(const APValue &RHS) { @@ -57,8 +84,12 @@ const APValue &APValue::operator=(const APValue &RHS) { setComplexInt(RHS.getComplexIntReal(), RHS.getComplexIntImag()); else if (isComplexFloat()) setComplexFloat(RHS.getComplexFloatReal(), RHS.getComplexFloatImag()); - else if (isLValue()) - setLValue(RHS.getLValueBase(), RHS.getLValueOffset()); + else if (isLValue()) { + if (RHS.hasLValuePath()) + setLValue(RHS.getLValueBase(), RHS.getLValueOffset(),RHS.getLValuePath()); + else + setLValue(RHS.getLValueBase(), RHS.getLValueOffset(), NoLValuePath()); + } return *this; } @@ -174,14 +205,38 @@ CharUnits &APValue::getLValueOffset() { return ((LV*)(void*)Data)->Offset; } -void APValue::setLValue(const Expr *B, const CharUnits &O) { +bool APValue::hasLValuePath() const { assert(isLValue() && "Invalid accessor"); - ((LV*)(char*)Data)->Base = B; - ((LV*)(char*)Data)->Offset = O; + return ((LV*)(char*)Data)->hasPath(); +} + +ArrayRef APValue::getLValuePath() const { + assert(isLValue() && hasLValuePath() && "Invalid accessor"); + LV &LVal = *((LV*)(char*)Data); + return ArrayRef(LVal.getPath(), LVal.PathLength); +} + +void APValue::setLValue(const Expr *B, const CharUnits &O, NoLValuePath) { + assert(isLValue() && "Invalid accessor"); + LV &LVal = *((LV*)(char*)Data); + LVal.Base = B; + LVal.Offset = O; + LVal.PathLength = (unsigned)-1; +} + +void APValue::setLValue(const Expr *B, const CharUnits &O, + ArrayRef Path) { + assert(isLValue() && "Invalid accessor"); + LV &LVal = *((LV*)(char*)Data); + LVal.Base = B; + LVal.Offset = O; + LVal.PathLength = Path.size(); + memcpy(LVal.getPath(), Path.data(), Path.size() * sizeof(LValuePathEntry)); } void APValue::MakeLValue() { assert(isUninit() && "Bad state change"); + assert(sizeof(LV) <= MaxSize && "LV too big"); new ((void*)(char*)Data) LV(); Kind = LValue; } diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 6456376d48f6..8ec16217a707 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -46,6 +46,24 @@ namespace { struct CallStackFrame; struct EvalInfo; + /// Determine whether the described subobject is an array element. + static bool SubobjectIsArrayElement(QualType Base, + ArrayRef Path) { + bool IsArrayElement = false; + const Type *T = Base.getTypePtr(); + for (unsigned I = 0, N = Path.size(); I != N; ++I) { + IsArrayElement = T && T->isArrayType(); + if (IsArrayElement) + T = T->getBaseElementTypeUnsafe(); + else if (const FieldDecl *FD = dyn_cast(Path[I].BaseOrMember)) + T = FD->getType().getTypePtr(); + else + // Path[I] describes a base class. + T = 0; + } + return IsArrayElement; + } + /// A path from a glvalue to a subobject of that glvalue. struct SubobjectDesignator { /// True if the subobject was named in a manner not supported by C++11. Such @@ -59,20 +77,28 @@ namespace { /// Whether this designates 'one past the end' of the current subobject. bool OnePastTheEnd : 1; - union PathEntry { - /// If the current subobject is of class type, this indicates which - /// subobject of that type is accessed next. - const Decl *BaseOrMember; - /// If the current subobject is of array type, this indicates which index - /// within that array is accessed next. - uint64_t Index; - }; + typedef APValue::LValuePathEntry PathEntry; + /// The entries on the path from the glvalue to the designated subobject. SmallVector Entries; SubobjectDesignator() : Invalid(false), ArrayElement(false), OnePastTheEnd(false) {} + SubobjectDesignator(const APValue &V) : + Invalid(!V.isLValue() || !V.hasLValuePath()), ArrayElement(false), + OnePastTheEnd(false) { + if (!Invalid) { + ArrayRef VEntries = V.getLValuePath(); + Entries.insert(Entries.end(), VEntries.begin(), VEntries.end()); + if (V.getLValueBase()) + ArrayElement = SubobjectIsArrayElement(V.getLValueBase()->getType(), + V.getLValuePath()); + else + assert(V.getLValuePath().empty() &&"Null pointer with nonempty path"); + } + } + void setInvalid() { Invalid = true; Entries.clear(); @@ -85,7 +111,7 @@ namespace { return; } PathEntry Entry; - Entry.Index = N; + Entry.ArrayIndex = N; Entries.push_back(Entry); ArrayElement = true; } @@ -106,7 +132,7 @@ namespace { void adjustIndex(uint64_t N) { if (Invalid) return; if (ArrayElement) { - Entries.back().Index += N; + Entries.back().ArrayIndex += N; return; } if (OnePastTheEnd && N == (uint64_t)-1) @@ -141,9 +167,9 @@ namespace { CCValue(const CCValue &V) : APValue(V), CallFrame(V.CallFrame) {} CCValue(const Expr *B, const CharUnits &O, CallStackFrame *F, const SubobjectDesignator &D) : - APValue(B, O), CallFrame(F), Designator(D) {} + APValue(B, O, APValue::NoLValuePath()), CallFrame(F), Designator(D) {} CCValue(const APValue &V, GlobalValue) : - APValue(V), CallFrame(0), Designator() {} + APValue(V), CallFrame(0), Designator(V) {} CallStackFrame *getLValueFrame() const { assert(getKind() == LValue); @@ -336,15 +362,37 @@ static bool IsGlobalLValue(const Expr* E) { return true; } +/// Check that this reference or pointer core constant expression is a valid +/// value for a constant expression. Type T should be either LValue or CCValue. +template +static bool CheckLValueConstantExpression(const T &LVal, APValue &Value) { + if (!IsGlobalLValue(LVal.getLValueBase())) + return false; + + const SubobjectDesignator &Designator = LVal.getLValueDesignator(); + // A constant expression must refer to an object or be a null pointer. + if (Designator.Invalid || Designator.OnePastTheEnd || + (!LVal.getLValueBase() && !Designator.Entries.empty())) { + // FIXME: Check for out-of-bounds array indices. + // FIXME: This is not a constant expression. + Value = APValue(LVal.getLValueBase(), LVal.getLValueOffset(), + APValue::NoLValuePath()); + return true; + } + + Value = APValue(LVal.getLValueBase(), LVal.getLValueOffset(), + Designator.Entries); + return true; +} + /// Check that this core constant expression value is a valid value for a /// constant expression, and if it is, produce the corresponding constant value. static bool CheckConstantExpression(const CCValue &CCValue, APValue &Value) { - if (CCValue.isLValue() && !IsGlobalLValue(CCValue.getLValueBase())) - return false; - - // Slice off the extra bits. - Value = CCValue; - return true; + if (!CCValue.isLValue()) { + Value = CCValue; + return true; + } + return CheckLValueConstantExpression(CCValue, Value); } const ValueDecl *GetLValueBaseDecl(const LValue &LVal) { @@ -595,7 +643,7 @@ bool HandleLValueToRValueConversion(EvalInfo &Info, QualType Type, return false; assert(Type->isIntegerType() && "string element not integer type"); - uint64_t Index = Designator.Entries[0].Index; + uint64_t Index = Designator.Entries[0].ArrayIndex; if (Index > S->getLength()) return false; APSInt Value(S->getCharByteWidth() * Info.Ctx.getCharWidth(), @@ -1954,7 +2002,7 @@ static bool HasSameBase(const LValue &A, const LValue &B) { if (!ADecl) return false; const Decl *BDecl = GetLValueBaseDecl(B); - if (ADecl != BDecl) + if (!BDecl || ADecl->getCanonicalDecl() != BDecl->getCanonicalDecl()) return false; } @@ -3293,24 +3341,8 @@ bool Expr::EvaluateAsLValue(EvalResult &Result, const ASTContext &Ctx) const { EvalInfo Info(Ctx, Result); LValue LV; - if (EvaluateLValue(this, LV, Info) && !Result.HasSideEffects && - IsGlobalLValue(LV.Base)) { - Result.Val = APValue(LV.Base, LV.Offset); - return true; - } - return false; -} - -bool Expr::EvaluateAsAnyLValue(EvalResult &Result, - const ASTContext &Ctx) const { - EvalInfo Info(Ctx, Result); - - LValue LV; - if (EvaluateLValue(this, LV, Info)) { - Result.Val = APValue(LV.Base, LV.Offset); - return true; - } - return false; + return EvaluateLValue(this, LV, Info) && !Result.HasSideEffects && + CheckLValueConstantExpression(LV, Result.Val); } /// isEvaluatable - Call EvaluateAsRValue to see if this expression can be diff --git a/clang/test/SemaCXX/constant-expression-cxx11.cpp b/clang/test/SemaCXX/constant-expression-cxx11.cpp index 92159a7b6a2c..010914e6f4ba 100644 --- a/clang/test/SemaCXX/constant-expression-cxx11.cpp +++ b/clang/test/SemaCXX/constant-expression-cxx11.cpp @@ -261,4 +261,22 @@ constexpr char c2 = "nasty index"[12]; // expected-error {{must be initialized b constexpr char c3 = "negative index"[-1]; // expected-error {{must be initialized by a constant expression}} expected-warning {{indexes before the beginning}} constexpr char c4 = ((char*)(int*)"no reinterpret_casts allowed")[14]; // expected-error {{must be initialized by a constant expression}} +constexpr const char *p = "test" + 2; +static_assert_fold(*p == 's', ""); + +constexpr const char *max_iter(const char *a, const char *b) { + return *a < *b ? b : a; +} +constexpr const char *max_element(const char *a, const char *b) { + return (a+1 >= b) ? a : max_iter(a, max_element(a+1, b)); +} + +constexpr const char *begin(const char (&arr)[45]) { return arr; } +constexpr const char *end(const char (&arr)[45]) { return arr + 45; } + +constexpr char str[] = "the quick brown fox jumped over the lazy dog"; +constexpr const char *max = max_element(begin(str), end(str)); +static_assert_fold(*max == 'z', ""); +static_assert_fold(max == str + 38, ""); + }