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 /lib | |
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
Diffstat (limited to 'lib')
-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 |
7 files changed, 730 insertions, 186 deletions
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 ObjType, return true; } +/// HandleLValueToRValueConversion - Perform an lvalue-to-rvalue conversion on +/// the given lvalue. This can also be used for 'lvalue-to-lvalue' conversions +/// for looking up the glvalue referred to by an entity of reference type. +/// +/// \param Info - Information about the ongoing evaluation. +/// \param Type - The type we expect this conversion to produce. +/// \param LVal - The glvalue on which we are attempting to perform this action. +/// \param RVal - The produced value will be placed here. static bool HandleLValueToRValueConversion(EvalInfo &Info, QualType Type, const LValue &LVal, CCValue &RVal) { const Expr *Base = LVal.Base; @@ -665,7 +921,7 @@ static bool HandleLValueToRValueConversion(EvalInfo &Info, QualType Type, !VD->isConstexpr()) return false; } - if (!EvaluateVarDeclInit(Info, VD, Frame, RVal)) + if (!EvaluateVarDeclInit(Info, LVal.Base, VD, Frame, RVal)) return false; if (isa<ParmVarDecl>(VD) || !VD->getAnyInitializer()->isLValue()) @@ -758,6 +1014,20 @@ static EvalStmtResult EvaluateStmt(CCValue &Result, EvalInfo &Info, } } +namespace { +typedef SmallVector<CCValue, 16> ArgVector; +} + +/// EvaluateArgs - Evaluate the arguments to a function call. +static bool EvaluateArgs(ArrayRef<const Expr*> Args, ArgVector &ArgValues, + EvalInfo &Info) { + for (ArrayRef<const Expr*>::iterator I = Args.begin(), E = Args.end(); + I != E; ++I) + if (!Evaluate(ArgValues[I - Args.begin()], Info, *I)) + return false; + return true; +} + /// Evaluate a function call. static bool HandleFunctionCall(ArrayRef<const Expr*> Args, const Stmt *Body, EvalInfo &Info, CCValue &Result) { @@ -765,17 +1035,87 @@ static bool HandleFunctionCall(ArrayRef<const Expr*> Args, const Stmt *Body, if (Info.NumCalls >= 1000000 || Info.CallStackDepth >= 512) return false; - SmallVector<CCValue, 16> ArgValues(Args.size()); - // FIXME: Deal with default arguments and 'this'. - for (ArrayRef<const Expr*>::iterator I = Args.begin(), E = Args.end(); - I != E; ++I) - if (!Evaluate(ArgValues[I - Args.begin()], Info, *I)) - return false; + ArgVector ArgValues(Args.size()); + if (!EvaluateArgs(Args, ArgValues, Info)) + return false; - CallStackFrame Frame(Info, ArgValues.data()); + // FIXME: Pass in 'this' for member functions. + const LValue *This = 0; + CallStackFrame Frame(Info, This, ArgValues.data()); return EvaluateStmt(Result, Info, Body) == ESR_Returned; } +/// Evaluate a constructor call. +static bool HandleConstructorCall(ArrayRef<const Expr*> Args, + const CXXConstructorDecl *Definition, + EvalInfo &Info, const LValue &This, + APValue &Result) { + if (Info.NumCalls >= 1000000 || Info.CallStackDepth >= 512) + return false; + + ArgVector ArgValues(Args.size()); + if (!EvaluateArgs(Args, ArgValues, Info)) + return false; + + CallStackFrame Frame(Info, &This, ArgValues.data()); + + // If it's a delegating constructor, just delegate. + if (Definition->isDelegatingConstructor()) { + CXXConstructorDecl::init_const_iterator I = Definition->init_begin(); + return EvaluateConstantExpression(Result, Info, This, (*I)->getInit()); + } + + // Reserve space for the struct members. + const CXXRecordDecl *RD = Definition->getParent(); + if (!RD->isUnion()) + Result = APValue(APValue::UninitStruct(), RD->getNumBases(), + std::distance(RD->field_begin(), RD->field_end())); + + const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(RD); + + unsigned BasesSeen = 0; +#ifndef NDEBUG + CXXRecordDecl::base_class_const_iterator BaseIt = RD->bases_begin(); +#endif + for (CXXConstructorDecl::init_const_iterator I = Definition->init_begin(), + E = Definition->init_end(); I != E; ++I) { + if ((*I)->isBaseInitializer()) { + QualType BaseType((*I)->getBaseClass(), 0); +#ifndef NDEBUG + // Non-virtual base classes are initialized in the order in the class + // definition. We cannot have a virtual base class for a literal type. + assert(!BaseIt->isVirtual() && "virtual base for literal type"); + assert(Info.Ctx.hasSameType(BaseIt->getType(), BaseType) && + "base class initializers not in expected order"); + ++BaseIt; +#endif + LValue Subobject = This; + HandleLValueDirectBase(Info, Subobject, RD, + BaseType->getAsCXXRecordDecl(), &Layout); + if (!EvaluateConstantExpression(Result.getStructBase(BasesSeen++), Info, + Subobject, (*I)->getInit())) + return false; + } else if (FieldDecl *FD = (*I)->getMember()) { + LValue Subobject = This; + HandleLValueMember(Info, Subobject, FD, &Layout); + if (RD->isUnion()) { + Result = APValue(FD); + if (!EvaluateConstantExpression(Result.getUnionValue(), Info, + Subobject, (*I)->getInit())) + return false; + } else if (!EvaluateConstantExpression( + Result.getStructField(FD->getFieldIndex()), + Info, Subobject, (*I)->getInit())) + return false; + } else { + // FIXME: handle indirect field initializers + return false; + } + } + + return true; +} + namespace { class HasSideEffect : public ConstStmtVisitor<HasSideEffect, bool> { @@ -907,12 +1247,6 @@ protected: RetTy ValueInitialization(const Expr *E) { return DerivedError(E); } - bool MakeTemporary(const Expr *Key, const Expr *Value, LValue &Result) { - if (!Evaluate(Info.CurrentCall->Temporaries[Key], Info, Value)) - return false; - Result.setExpr(Key, Info.CurrentCall); - return true; - } public: ExprEvaluatorBase(EvalInfo &Info) : Info(Info) {} @@ -1029,6 +1363,29 @@ public: return DerivedValueInitialization(E); } + /// A member expression where the object is a prvalue is itself a prvalue. + RetTy VisitMemberExpr(const MemberExpr *E) { + assert(!E->isArrow() && "missing call to bound member function?"); + + CCValue Val; + if (!Evaluate(Val, Info, E->getBase())) + return false; + + QualType BaseTy = E->getBase()->getType(); + + const FieldDecl *FD = dyn_cast<FieldDecl>(E->getMemberDecl()); + if (!FD) return false; + assert(!FD->getType()->isReferenceType() && "prvalue reference?"); + assert(BaseTy->getAs<RecordType>()->getDecl()->getCanonicalDecl() == + FD->getParent()->getCanonicalDecl() && "record / field mismatch"); + + SubobjectDesignator Designator; + Designator.addDecl(FD); + + return ExtractSubobject(Info, Val, BaseTy, Designator, E->getType()) && |