diff options
author | Richard Smith <richard-llvm@metafoo.co.uk> | 2011-11-10 06:34:14 +0000 |
---|---|---|
committer | Richard Smith <richard-llvm@metafoo.co.uk> | 2011-11-10 06:34:14 +0000 |
commit | 180f47959a066795cc0f409433023af448bb0328 (patch) | |
tree | e5a8c5e3c47c68d4ee460e09489ebac5cf60b8a4 | |
parent | 64b4b43a23aa8b8009470e3cc451333f623d7d58 (diff) |
Constant expression evaluation: support for evaluation of structs and unions of
literal types, as well as derived-to-base casts for lvalues and
derived-to-virtual-base casts.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@144265 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | include/clang/AST/APValue.h | 85 | ||||
-rw-r--r-- | include/clang/AST/Expr.h | 2 | ||||
-rw-r--r-- | lib/AST/APValue.cpp | 90 | ||||
-rw-r--r-- | lib/AST/Decl.cpp | 24 | ||||
-rw-r--r-- | lib/AST/Expr.cpp | 25 | ||||
-rw-r--r-- | lib/AST/ExprConstant.cpp | 762 | ||||
-rw-r--r-- | lib/CodeGen/CGExprConstant.cpp | 5 | ||||
-rw-r--r-- | lib/Sema/SemaChecking.cpp | 6 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp | 4 | ||||
-rw-r--r-- | test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p1.cpp | 3 | ||||
-rw-r--r-- | test/SemaCXX/constant-expression-cxx11.cpp | 241 |
11 files changed, 1056 insertions, 191 deletions
diff --git a/include/clang/AST/APValue.h b/include/clang/AST/APValue.h index 57c89d62be..177d7baf4a 100644 --- a/include/clang/AST/APValue.h +++ b/include/clang/AST/APValue.h @@ -17,11 +17,13 @@ #include "clang/Basic/LLVM.h" #include "llvm/ADT/APSInt.h" #include "llvm/ADT/APFloat.h" +#include "llvm/ADT/PointerIntPair.h" namespace clang { class CharUnits; class DiagnosticBuilder; class Expr; + class FieldDecl; class Decl; /// APValue - This class implements a discriminated union of [uninitialized] @@ -39,14 +41,21 @@ public: ComplexFloat, LValue, Vector, - Array + Array, + Struct, + Union }; + typedef llvm::PointerIntPair<const Decl *, 1, bool> BaseOrMemberType; union LValuePathEntry { - const Decl *BaseOrMember; + /// BaseOrMember - The FieldDecl or CXXRecordDecl indicating the next item + /// in the path. An opaque value of type BaseOrMemberType. + void *BaseOrMember; + /// ArrayIndex - The array index of the next item in the path. uint64_t ArrayIndex; }; struct NoLValuePath {}; struct UninitArray {}; + struct UninitStruct {}; private: ValueKind Kind; @@ -71,6 +80,19 @@ private: Arr(unsigned NumElts, unsigned ArrSize); ~Arr(); }; + struct StructData { + APValue *Elts; + unsigned NumBases; + unsigned NumFields; + StructData(unsigned NumBases, unsigned NumFields); + ~StructData(); + }; + struct UnionData { + const FieldDecl *Field; + APValue *Value; + UnionData(); + ~UnionData(); + }; enum { MaxSize = (sizeof(ComplexAPSInt) > sizeof(ComplexAPFloat) ? @@ -114,6 +136,13 @@ public: APValue(UninitArray, unsigned InitElts, unsigned Size) : Kind(Uninitialized) { MakeArray(InitElts, Size); } + APValue(UninitStruct, unsigned B, unsigned M) : Kind(Uninitialized) { + MakeStruct(B, M); + } + explicit APValue(const FieldDecl *D, const APValue &V = APValue()) + : Kind(Uninitialized) { + MakeUnion(); setUnion(D, V); + } ~APValue() { MakeUninit(); @@ -128,6 +157,8 @@ public: bool isLValue() const { return Kind == LValue; } bool isVector() const { return Kind == Vector; } bool isArray() const { return Kind == Array; } + bool isStruct() const { return Kind == Struct; } + bool isUnion() const { return Kind == Union; } void print(raw_ostream &OS) const; void dump() const; @@ -229,6 +260,41 @@ public: return ((const Arr*)(const void *)Data)->ArrSize; } + unsigned getStructNumBases() const { + assert(isStruct() && "Invalid accessor"); + return ((StructData*)(char*)Data)->NumBases; + } + unsigned getStructNumFields() const { + assert(isStruct() && "Invalid accessor"); + return ((StructData*)(char*)Data)->NumFields; + } + APValue &getStructBase(unsigned i) { + assert(isStruct() && "Invalid accessor"); + return ((StructData*)(char*)Data)->Elts[i]; + } + APValue &getStructField(unsigned i) { + assert(isStruct() && "Invalid accessor"); + return ((StructData*)(char*)Data)->Elts[getStructNumBases() + i]; + } + const APValue &getStructBase(unsigned i) const { + return const_cast<APValue*>(this)->getStructBase(i); + } + const APValue &getStructField(unsigned i) const { + return const_cast<APValue*>(this)->getStructField(i); + } + + const FieldDecl *getUnionField() const { + assert(isUnion() && "Invalid accessor"); + return ((UnionData*)(char*)Data)->Field; + } + APValue &getUnionValue() { + assert(isUnion() && "Invalid accessor"); + return *((UnionData*)(char*)Data)->Value; + } + const APValue &getUnionValue() const { + return const_cast<APValue*>(this)->getUnionValue(); + } + void setInt(const APSInt &I) { assert(isInt() && "Invalid accessor"); *(APSInt*)(char*)Data = I; @@ -261,6 +327,11 @@ public: void setLValue(const Expr *B, const CharUnits &O, NoLValuePath); void setLValue(const Expr *B, const CharUnits &O, ArrayRef<LValuePathEntry> Path); + void setUnion(const FieldDecl *Field, const APValue &Value) { + assert(isUnion() && "Invalid accessor"); + ((UnionData*)(char*)Data)->Field = Field; + *((UnionData*)(char*)Data)->Value = Value; + } const APValue &operator=(const APValue &RHS); @@ -293,6 +364,16 @@ private: } void MakeLValue(); void MakeArray(unsigned InitElts, unsigned Size); + void MakeStruct(unsigned B, unsigned M) { + assert(isUninit() && "Bad state change"); + new ((void*)(char*)Data) StructData(B, M); + Kind = Struct; + } + void MakeUnion() { + assert(isUninit() && "Bad state change"); + new ((void*)(char*)Data) UnionData(); + Kind = Union; + } }; inline raw_ostream &operator<<(raw_ostream &OS, const APValue &V) { diff --git a/include/clang/AST/Expr.h b/include/clang/AST/Expr.h index f0a4802db1..33447f7716 100644 --- a/include/clang/AST/Expr.h +++ b/include/clang/AST/Expr.h @@ -2049,7 +2049,7 @@ public: /// isBuiltinCall - If this is a call to a builtin, return the builtin ID. If /// not, return 0. - unsigned isBuiltinCall(const ASTContext &Context) const; + unsigned isBuiltinCall() const; /// getCallReturnType - Get the return type of the call expr. This is not /// always the type of the expr itself, if the return type is a reference diff --git a/lib/AST/APValue.cpp b/lib/AST/APValue.cpp index af34642bea..e64bf7cbce 100644 --- a/lib/AST/APValue.cpp +++ b/lib/AST/APValue.cpp @@ -45,6 +45,7 @@ struct APValue::LV : LVBase { void allocPath() { if (hasPathPtr()) PathPtr = new LValuePathEntry[PathLength]; } + void freePath() { if (hasPathPtr()) delete [] PathPtr; } bool hasPath() const { return PathLength != (unsigned)-1; } bool hasPathPtr() const { return hasPath() && PathLength > InlinePathSpace; } @@ -62,13 +63,27 @@ APValue::Arr::Arr(unsigned NumElts, unsigned Size) : NumElts(NumElts), ArrSize(Size) {} APValue::Arr::~Arr() { delete [] Elts; } +APValue::StructData::StructData(unsigned NumBases, unsigned NumFields) : + Elts(new APValue[NumBases+NumFields]), + NumBases(NumBases), NumFields(NumFields) {} +APValue::StructData::~StructData() { + delete [] Elts; +} + +APValue::UnionData::UnionData() : Field(0), Value(new APValue) {} +APValue::UnionData::~UnionData () { + delete Value; +} + APValue::APValue(const Expr* B) : Kind(Uninitialized) { MakeLValue(); setLValue(B, CharUnits::Zero(), ArrayRef<LValuePathEntry>()); } const APValue &APValue::operator=(const APValue &RHS) { - if (Kind != RHS.Kind) { + if (this == &RHS) + return *this; + if (Kind != RHS.Kind || Kind == Array || Kind == Struct) { MakeUninit(); if (RHS.isInt()) MakeInt(); @@ -84,6 +99,10 @@ const APValue &APValue::operator=(const APValue &RHS) { MakeLValue(); else if (RHS.isArray()) MakeArray(RHS.getArrayInitializedElts(), RHS.getArraySize()); + else if (RHS.isStruct()) + MakeStruct(RHS.getStructNumBases(), RHS.getStructNumFields()); + else if (RHS.isUnion()) + MakeUnion(); } if (isInt()) setInt(RHS.getInt()); @@ -106,7 +125,13 @@ const APValue &APValue::operator=(const APValue &RHS) { getArrayInitializedElt(I) = RHS.getArrayInitializedElt(I); if (RHS.hasArrayFiller()) getArrayFiller() = RHS.getArrayFiller(); - } + } else if (isStruct()) { + for (unsigned I = 0, N = RHS.getStructNumBases(); I != N; ++I) + getStructBase(I) = RHS.getStructBase(I); + for (unsigned I = 0, N = RHS.getStructNumFields(); I != N; ++I) + getStructField(I) = RHS.getStructField(I); + } else if (isUnion()) + setUnion(RHS.getUnionField(), RHS.getUnionValue()); return *this; } @@ -125,6 +150,10 @@ void APValue::MakeUninit() { ((LV*)(char*)Data)->~LV(); else if (Kind == Array) ((Arr*)(char*)Data)->~Arr(); + else if (Kind == Struct) + ((StructData*)(char*)Data)->~StructData(); + else if (Kind == Union) + ((UnionData*)(char*)Data)->~UnionData(); Kind = Uninitialized; } @@ -143,7 +172,6 @@ static double GetApproxValue(const llvm::APFloat &F) { void APValue::print(raw_ostream &OS) const { switch (getKind()) { - default: llvm_unreachable("Unknown APValue kind!"); case Uninitialized: OS << "Uninitialized"; return; @@ -178,22 +206,38 @@ void APValue::print(raw_ostream &OS) const { OS << getArraySize() - getArrayInitializedElts() << " x " << getArrayFiller(); return; + case Struct: + OS << "Struct "; + if (unsigned N = getStructNumBases()) { + OS << " bases: " << getStructBase(0); + for (unsigned I = 1; I != N; ++I) + OS << ", " << getStructBase(I); + } + if (unsigned N = getStructNumFields()) { + OS << " fields: " << getStructField(0); + for (unsigned I = 1; I != N; ++I) + OS << ", " << getStructField(I); + } + return; + case Union: + OS << "Union: " << getUnionValue(); + return; } + llvm_unreachable("Unknown APValue kind!"); } static void WriteShortAPValueToStream(raw_ostream& Out, const APValue& V) { switch (V.getKind()) { - default: llvm_unreachable("Unknown APValue kind!"); case APValue::Uninitialized: Out << "Uninitialized"; - break; + return; case APValue::Int: Out << V.getInt(); - break; + return; case APValue::Float: Out << GetApproxValue(V.getFloat()); - break; + return; case APValue::Vector: Out << '['; WriteShortAPValueToStream(Out, V.getVectorElt(0)); @@ -202,17 +246,17 @@ static void WriteShortAPValueToStream(raw_ostream& Out, WriteShortAPValueToStream(Out, V.getVectorElt(i)); } Out << ']'; - break; + return; case APValue::ComplexInt: Out << V.getComplexIntReal() << "+" << V.getComplexIntImag() << "i"; - break; + return; case APValue::ComplexFloat: Out << GetApproxValue(V.getComplexFloatReal()) << "+" << GetApproxValue(V.getComplexFloatImag()) << "i"; - break; + return; case APValue::LValue: Out << "LValue: <todo>"; - break; + return; case APValue::Array: Out << '{'; if (unsigned N = V.getArrayInitializedElts()) { @@ -221,8 +265,28 @@ static void WriteShortAPValueToStream(raw_ostream& Out, Out << ", " << V.getArrayInitializedElt(I); } Out << '}'; - break; + return; + case APValue::Struct: + Out << '{'; + if (unsigned N = V.getStructNumBases()) { + Out << V.getStructBase(0); + for (unsigned I = 1; I != N; ++I) + Out << ", " << V.getStructBase(I); + if (V.getStructNumFields()) + Out << ", "; + } + if (unsigned N = V.getStructNumFields()) { + Out << V.getStructField(0); + for (unsigned I = 1; I != N; ++I) + Out << ", " << V.getStructField(I); + } + Out << '}'; + return; + case APValue::Union: + Out << '{' << V.getUnionValue() << '}'; + return; } + llvm_unreachable("Unknown APValue kind!"); } const DiagnosticBuilder &clang::operator<<(const DiagnosticBuilder &DB, @@ -257,6 +321,7 @@ ArrayRef<APValue::LValuePathEntry> APValue::getLValuePath() const { void APValue::setLValue(const Expr *B, const CharUnits &O, NoLValuePath) { assert(isLValue() && "Invalid accessor"); LV &LVal = *((LV*)(char*)Data); + LVal.freePath(); LVal.Base = B; LVal.Offset = O; LVal.PathLength = (unsigned)-1; @@ -266,6 +331,7 @@ void APValue::setLValue(const Expr *B, const CharUnits &O, ArrayRef<LValuePathEntry> Path) { assert(isLValue() && "Invalid accessor"); LV &LVal = *((LV*)(char*)Data); + LVal.freePath(); LVal.Base = B; LVal.Offset = O; LVal.PathLength = Path.size(); diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 095491aafe..299ddd4b72 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -2186,31 +2186,27 @@ unsigned FieldDecl::getBitWidthValue(const ASTContext &Ctx) const { unsigned FieldDecl::getFieldIndex() const { if (CachedFieldIndex) return CachedFieldIndex - 1; - unsigned index = 0; + unsigned Index = 0; const RecordDecl *RD = getParent(); const FieldDecl *LastFD = 0; bool IsMsStruct = RD->hasAttr<MsStructAttr>(); - - RecordDecl::field_iterator i = RD->field_begin(), e = RD->field_end(); - while (true) { - assert(i != e && "failed to find field in parent!"); - if (*i == this) - break; + + for (RecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end(); + I != E; ++I, ++Index) { + (*I)->CachedFieldIndex = Index + 1; if (IsMsStruct) { // Zero-length bitfields following non-bitfield members are ignored. - if (getASTContext().ZeroBitfieldFollowsNonBitfield((*i), LastFD)) { - ++i; + if (getASTContext().ZeroBitfieldFollowsNonBitfield((*I), LastFD)) { + --Index; continue; } - LastFD = (*i); + LastFD = (*I); } - ++i; - ++index; } - CachedFieldIndex = index + 1; - return index; + assert(CachedFieldIndex && "failed to find field in parent"); + return CachedFieldIndex - 1; } SourceRange FieldDecl::getSourceRange() const { diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index 92b05344ec..c2880a0ad1 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -834,7 +834,7 @@ void CallExpr::setNumArgs(ASTContext& C, unsigned NumArgs) { /// isBuiltinCall - If this is a call to a builtin, return the builtin ID. If /// not, return 0. -unsigned CallExpr::isBuiltinCall(const ASTContext &Context) const { +unsigned CallExpr::isBuiltinCall() const { // All simple function calls (e.g. func()) are implicitly cast to pointer to // function. As a result, we try and obtain the DeclRefExpr from the // ImplicitCastExpr. @@ -2475,15 +2475,20 @@ bool Expr::isConstantInitializer(ASTContext &Ctx, bool IsForRef) const { const CXXConstructExpr *CE = cast<CXXConstructExpr>(this); // Only if it's - // 1) an application of the trivial default constructor or - if (!CE->getConstructor()->isTrivial()) return false; - if (!CE->getNumArgs()) return true; - - // 2) an elidable trivial copy construction of an operand which is - // itself a constant initializer. Note that we consider the - // operand on its own, *not* as a reference binding. - return CE->isElidable() && - CE->getArg(0)->isConstantInitializer(Ctx, false); + if (CE->getConstructor()->isTrivial()) { + // 1) an application of the trivial default constructor or + if (!CE->getNumArgs()) return true; + + // 2) an elidable trivial copy construction of an operand which is + // itself a constant initializer. Note that we consider the + // operand on its own, *not* as a reference binding. + if (CE->isElidable() && + CE->getArg(0)->isConstantInitializer(Ctx, false)) + return true; + } + + // 3) a foldable constexpr constructor. + break; } case CompoundLiteralExprClass: { // This handles gcc's extension that allows global initializers like diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index f2e3d36d75..25c7a90caa 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -43,9 +43,32 @@ using llvm::APFloat; /// evaluate the expression regardless of what the RHS is, but C only allows /// certain things in certain situations. namespace { + struct LValue; struct CallStackFrame; struct EvalInfo; + /// Get an LValue path entry, which is known to not be an array index, as a + /// field declaration. + const FieldDecl *getAsField(APValue::LValuePathEntry E) { + APValue::BaseOrMemberType Value; + Value.setFromOpaqueValue(E.BaseOrMember); + return dyn_cast<FieldDecl>(Value.getPointer()); + } + /// Get an LValue path entry, which is known to not be an array index, as a + /// base class declaration. + const CXXRecordDecl *getAsBaseClass(APValue::LValuePathEntry E) { + APValue::BaseOrMemberType Value; + Value.setFromOpaqueValue(E.BaseOrMember); + return dyn_cast<CXXRecordDecl>(Value.getPointer()); + } + /// Determine whether this LValue path entry for a base class names a virtual + /// base class. + bool isVirtualBaseClass(APValue::LValuePathEntry E) { + APValue::BaseOrMemberType Value; + Value.setFromOpaqueValue(E.BaseOrMember); + return Value.getInt(); + } + /// Determine whether the described subobject is an array element. static bool SubobjectIsArrayElement(QualType Base, ArrayRef<APValue::LValuePathEntry> Path) { @@ -55,7 +78,7 @@ namespace { IsArrayElement = T && T->isArrayType(); if (IsArrayElement) T = T->getBaseElementTypeUnsafe(); - else if (const FieldDecl *FD = dyn_cast<FieldDecl>(Path[I].BaseOrMember)) + else if (const FieldDecl *FD = getAsField(Path[I])) T = FD->getType().getTypePtr(); else // Path[I] describes a base class. @@ -117,14 +140,15 @@ namespace { } /// Update this designator to refer to the given base or member of this /// object. - void addDecl(const Decl *D) { + void addDecl(const Decl *D, bool Virtual = false) { if (Invalid) return; if (OnePastTheEnd) { setInvalid(); return; } PathEntry Entry; - Entry.BaseOrMember = D; + APValue::BaseOrMemberType Value(D, Virtual); + Entry.BaseOrMember = Value.getOpaqueValue(); Entries.push_back(Entry); ArrayElement = false; } @@ -192,6 +216,9 @@ namespace { /// Parent - The caller of this stack frame. CallStackFrame *Caller; + /// This - The binding for the this pointer in this call, if any. + const LValue *This; + /// ParmBindings - Parameter bindings for this function call, indexed by /// parameters' function scope indices. const CCValue *Arguments; @@ -201,7 +228,8 @@ namespace { /// Temporaries - Temporary lvalues materialized within this stack frame. MapTy Temporaries; - CallStackFrame(EvalInfo &Info, const CCValue *Arguments); + CallStackFrame(EvalInfo &Info, const LValue *This, + const CCValue *Arguments); ~CallStackFrame(); }; @@ -229,10 +257,18 @@ namespace { /// initialized last. CallStackFrame BottomFrame; + /// EvaluatingDecl - This is the declaration whose initializer is being + /// evaluated, if any. + const VarDecl *EvaluatingDecl; + + /// EvaluatingDeclValue - This is the value being constructed for the + /// declaration whose initializer is being evaluated, if any. + APValue *EvaluatingDeclValue; + EvalInfo(const ASTContext &C, Expr::EvalStatus &S) : Ctx(C), EvalStatus(S), CurrentCall(0), NumCalls(0), CallStackDepth(0), - BottomFrame(*this, 0) {} + BottomFrame(*this, 0, 0), EvaluatingDecl(0), EvaluatingDeclValue(0) {} const CCValue *getOpaqueValue(const OpaqueValueExpr *e) const { MapTy::const_iterator i = OpaqueValues.find(e); @@ -240,11 +276,17 @@ namespace { return &i->second; } + void setEvaluatingDecl(const VarDecl *VD, APValue &Value) { + EvaluatingDecl = VD; + EvaluatingDeclValue = &Value; + } + const LangOptions &getLangOpts() { return Ctx.getLangOptions(); } }; - CallStackFrame::CallStackFrame(EvalInfo &Info, const CCValue *Arguments) - : Info(Info), Caller(Info.CurrentCall), Arguments(Arguments) { + CallStackFrame::CallStackFrame(EvalInfo &Info, const LValue *This, + const CCValue *Arguments) + : Info(Info), Caller(Info.CurrentCall), This(This), Arguments(Arguments) { Info.CurrentCall = this; ++Info.CallStackDepth; } @@ -330,7 +372,7 @@ namespace { static bool Evaluate(CCValue &Result, EvalInfo &Info, const Expr *E); static bool EvaluateConstantExpression(APValue &Result, EvalInfo &Info, - const Expr *E); + const LValue &This, const Expr *E); static bool EvaluateLValue(const Expr *E, LValue &Result, EvalInfo &Info); static bool EvaluatePointer(const Expr *E, LValue &Result, EvalInfo &Info); static bool EvaluateInteger(const Expr *E, APSInt &Result, EvalInfo &Info); @@ -343,24 +385,52 @@ static bool EvaluateComplex(const Expr *E, ComplexValue &Res, EvalInfo &Info); // Misc utilities //===----------------------------------------------------------------------===// +/// Should this call expression be treated as a string literal? +static bool IsStringLiteralCall(const CallExpr *E) { + unsigned Builtin = E->isBuiltinCall(); + return (Builtin == Builtin::BI__builtin___CFStringMakeConstantString || + Builtin == Builtin::BI__builtin___NSStringMakeConstantString); +} + static bool IsGlobalLValue(const Expr* E) { + // C++11 [expr.const]p3 An address constant expression is a prvalue core + // constant expression of pointer type that evaluates to... + + // ... a null pointer value, or a prvalue core constant expression of type + // std::nullptr_t. if (!E) return true; - if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) { - if (isa<FunctionDecl>(DRE->getDecl())) - return true; + switch (E->getStmtClass()) { + default: + return false; + case Expr::DeclRefExprClass: { + const DeclRefExpr *DRE = cast<DeclRefExpr>(E); + // ... the address of an object with static storage duration, if (const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl())) return VD->hasGlobalStorage(); + // ... to the address of a function, + if (isa<FunctionDecl>(DRE->getDecl())) + return true; return false; } - - if (const CompoundLiteralExpr *CLE = dyn_cast<CompoundLiteralExpr>(E)) - return CLE->isFileScope(); - - if (isa<MemberExpr>(E) || isa<MaterializeTemporaryExpr>(E)) - return false; - - return true; + case Expr::CompoundLiteralExprClass: + return cast<CompoundLiteralExpr>(E)->isFileScope(); + // A string literal has static storage duration. + case Expr::StringLiteralClass: + case Expr::PredefinedExprClass: + case Expr::ObjCStringLiteralClass: + case Expr::ObjCEncodeExprClass: + return true; + case Expr::CallExprClass: + return IsStringLiteralCall(cast<CallExpr>(E)); + // For GCC compatibility, &&label has static storage duration. + case Expr::AddrLabelExprClass: + return true; + // A Block literal expression may be used as the initialization value for + // Block variables at global or local static scope. + case Expr::BlockExprClass: + return !cast<BlockExpr>(E)->getBlockDecl()->hasCaptures(); + } } /// Check that this reference or pointer core constant expression is a valid @@ -381,6 +451,8 @@ static bool CheckLValueConstantExpression(const T &LVal, APValue &Value) { return true; } + // FIXME: Null references are not constant expressions. + Value = APValue(LVal.getLValueBase(), LVal.getLValueOffset(), Designator.Entries); return true; @@ -477,6 +549,8 @@ static bool HandleConversionToBool(const CCValue &Val, bool &Result) { } case APValue::Vector: case APValue::Array: + case APValue::Struct: + case APValue::Union: return false; } @@ -534,8 +608,142 @@ static APFloat HandleIntToFloatCast(QualType DestType, QualType SrcType, return Result; } +/// If the given LValue refers to a base subobject of some object, find the most +/// derived object and the corresponding complete record type. This is necessary +/// in order to find the offset of a virtual base class. +static bool ExtractMostDerivedObject(EvalInfo &Info, LValue &Result, + const CXXRecordDecl *&MostDerivedType) { + SubobjectDesignator &D = Result.Designator; + if (D.Invalid || !Result.Base) + return false; + + const Type *T = Result.Base->getType().getTypePtr(); + + // Find path prefix which leads to the most-derived subobject. + unsigned MostDerivedPathLength = 0; + MostDerivedType = T->getAsCXXRecordDecl(); + bool MostDerivedIsArrayElement = false; + + for (unsigned I = 0, N = D.Entries.size(); I != N; ++I) { + bool IsArray = T && T->isArrayType(); + if (IsArray) + T = T->getBaseElementTypeUnsafe(); + else if (const FieldDecl *FD = getAsField(D.Entries[I])) + T = FD->getType().getTypePtr(); + else + T = 0; + + if (T) { + MostDerivedType = T->getAsCXXRecordDecl(); + MostDerivedPathLength = I + 1; + MostDerivedIsArrayElement = IsArray; + } + } + + if (!MostDerivedType) + return false; + + // (B*)&d + 1 has no most-derived object. + if (D.OnePastTheEnd && MostDerivedPathLength != D.Entries.size()) + return false; + + // Remove the trailing base class path entries and their offsets. + const RecordDecl *RD = MostDerivedType; + for (unsigned I = MostDerivedPathLength, N = D.Entries.size(); I != N; ++I) { + const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(RD); + const CXXRecordDecl *Base = getAsBaseClass(D.Entries[I]); + if (isVirtualBaseClass(D.Entries[I])) { + assert(I == MostDerivedPathLength && + "virtual base class must be immediately after most-derived class"); + Result.Offset -= Layout.getVBaseClassOffset(Base); + } else + Result.Offset -= Layout.getBaseClassOffset(Base); + RD = Base; + } + D.Entries.resize(MostDerivedPathLength); + D.ArrayElement = MostDerivedIsArrayElement; + return true; +} + +static void HandleLValueDirectBase(EvalInfo &Info, LValue &Obj, + const CXXRecordDecl *Derived, + const CXXRecordDecl *Base, + const ASTRecordLayout *RL = 0) { + if (!RL) RL = &Info.Ctx.getASTRecordLayout(Derived); + Obj.getLValueOffset() += RL->getBaseClassOffset(Base); + Obj.Designator.addDecl(Base, /*Virtual*/ false); +} + +static bool HandleLValueBase(EvalInfo &Info, LValue &Obj, + const CXXRecordDecl *DerivedDecl, + const CXXBaseSpecifier *Base) { + const CXXRecordDecl *BaseDecl = Base->getType()->getAsCXXRecordDecl(); + + if (!Base->isVirtual()) { + HandleLValueDirectBase(Info, Obj, DerivedDecl, BaseDecl); + return true; + } + + // Extract most-derived object and corresponding type. + if (!ExtractMostDerivedObject(Info, Obj, DerivedDecl)) + return false; + + const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(DerivedDecl); + Obj.getLValueOffset() += Layout.getVBaseClassOffset(BaseDecl); + Obj.Designator.addDecl(BaseDecl, /*Virtual*/ true); + return true; +} + +/// Update LVal to refer to the given field, which must be a member of the type +/// currently described by LVal. +static void HandleLValueMember(EvalInfo &Info, LValue &LVal, + const FieldDecl *FD, + const ASTRecordLayout *RL = 0) { + if (!RL) + RL = &Info.Ctx.getASTRecordLayout(FD->getParent()); + + unsigned I = FD->getFieldIndex(); + LVal.Offset += Info.Ctx.toCharUnitsFromBits(RL->getFieldOffset(I)); + LVal.Designator.addDecl(FD); +} + +/// Get the size of the given type in char units. +static bool HandleSizeof(EvalInfo &Info, QualType Type, CharUnits &Size) { + // sizeof(void), __alignof__(void), sizeof(function) = 1 as a gcc + // extension. + if (Type->isVoidType() || Type->isFunctionType()) { + Size = CharUnits::One(); + return true; + } + + if (!Type->isConstantSizeType()) { + // sizeof(vla) is not a constantexpr: C99 6.5.3.4p2. + return false; + } + + Size = Info.Ctx.getTypeSizeInChars(Type); + return true; +} + +/// Update a pointer value to model pointer arithmetic. +/// \param Info - Information about the ongoing evaluation. +/// \param LVal - The pointer value to be updated. +/// \param EltTy - The pointee type represented by LVal. +/// \param Adjustment - The adjustment, in objects of type EltTy, to add. +static bool HandleLValueArrayAdjustment(EvalInfo &Info, LValue &LVal, + QualType EltTy, int64_t Adjustment) { + CharUnits SizeOfPointee; + if (!HandleSizeof(Info, EltTy, SizeOfPointee)) + return false; + + // Compute the new offset in the appropriate width. + LVal.Offset += Adjustment * SizeOfPointee; + LVal.Designator.adjustIndex(Adjustment); + return true; +} + /// Try to evaluate the initializer for a variable declaration. -static bool EvaluateVarDeclInit(EvalInfo &Info, const VarDecl *VD, +static bool EvaluateVarDeclInit(EvalInfo &Info, const Expr *E,const VarDecl *VD, CallStackFrame *Frame, CCValue &Result) { // If this is a parameter to an active constexpr function call, perform // argument substitution. @@ -546,6 +754,13 @@ static bool EvaluateVarDeclInit(EvalInfo &Info, const VarDecl *VD, return true; } + // If we're currently evaluating the initializer of this declaration, use that + // in-flight value. + if (Info.EvaluatingDecl == VD) { + Result = CCValue(*Info.EvaluatingDeclValue, CCValue::GlobalValue()); + return !Result.isUninit(); + } + // Never evaluate the initializer of a weak variable. We can't be sure that // this is the definition which will be used. if (IsWeakDecl(VD)) @@ -567,10 +782,13 @@ static bool EvaluateVarDeclInit(EvalInfo &Info, const VarDecl *VD, Expr::EvalStatus EStatus; EvalInfo InitInfo(Info.Ctx, EStatus); + APValue EvalResult; + InitInfo.setEvaluatingDecl(VD, EvalResult); + LValue LVal; + LVal.setExpr(E); // FIXME: The caller will need to know whether the value was a constant // expression. If not, we should propagate up a diagnostic. - APValue EvalResult; - if (!EvaluateConstantExpression(EvalResult, InitInfo, Init)) { + if (!EvaluateConstantExpression(EvalResult, InitInfo, LVal, Init)) { // FIXME: If the evaluation failure was not permanent (for instance, if we // hit a variable with no declaration yet, or a constexpr function with no // definition yet), the standard is unclear as to how we should behave. @@ -605,10 +823,10 @@ static bool ExtractSubobject(EvalInfo &Info, CCValue &Obj, QualType ObjType, assert(!Obj.isLValue() && "extracting subobject of lvalue"); const APValue *O = &Obj; + // Walk the designator's path to find the subobject. for (unsigned I = 0, N = Sub.Entries.size(); I != N; ++I) { - if (O->isUninit()) - return false; if (ObjType->isArrayType()) { + // Next subobject is an array element. const ConstantArrayType *CAT = Info.Ctx.getAsConstantArrayType(ObjType); if (!CAT) return false; @@ -620,10 +838,40 @@ static bool ExtractSubobject(EvalInfo &Info, CCValue &Obj, QualType ObjType, else O = &O->getArrayFiller(); ObjType = CAT->getElementType(); + } else if (const FieldDecl *Field = getAsField(Sub.Entries[I])) { + // Next subobject is a class, struct or union field. + RecordDecl *RD = ObjType->castAs<RecordType>()->getDecl(); + if (RD->isUnion()) { + const FieldDecl *UnionField = O->getUnionField(); + if (!UnionField || + UnionField->getCanonicalDecl() != Field->getCanonicalDecl()) + return false; + O = &O->getUnionValue(); + } else + O = &O->getStructField(Field->getFieldIndex()); + ObjType = Field->getType(); } else { - // FIXME: Support handling of subobjects of structs and unions. Also - // for vector elements, if we want to support those? + // Next subobject is a base class. + const CXXRecordDecl *RD = + cast<CXXRecordDecl>(ObjType->castAs<RecordType>()->getDecl()); + const CXXRecordDecl *Base = + getAsBaseClass(Sub.Entries[I])->getCanonicalDecl(); + unsigned Index = 0; + for (CXXRecordDecl::base_class_const_iterator I = RD->bases_begin(), + E = RD->bases_end(); I != E; ++I, ++Index) { + QualType BT = I->getType(); + if (BT->castAs<RecordType>()->getDecl()->getCanonicalDecl() == Base) { + O = &O->getStructBase(Index); + ObjType = BT; + break; + } + } + if (Index == RD->getNumBases()) + return false; } + + if (O->isUninit()) + return false; } assert(Info.Ctx.hasSameUnqualifiedType(ObjType, SubType) && @@ -632,6 +880,14 @@ static bool ExtractSubobject(EvalInfo &Info, CCValue &Obj, QualType O |