From: Richard Smith Date: Thu, 10 Nov 2011 06:34:14 +0000 (+0000) Subject: Constant expression evaluation: support for evaluation of structs and unions of X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=180f47959a066795cc0f409433023af448bb0328;p=clang 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 --- 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 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(this)->getStructBase(i); + } + const APValue &getStructField(unsigned i) const { + return const_cast(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(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 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()); } 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: "; - 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::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 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(); - - 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(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(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(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 Path) { @@ -55,7 +78,7 @@ namespace { IsArrayElement = T && T->isArrayType(); if (IsArrayElement) T = T->getBaseElementTypeUnsafe(); - else if (const FieldDecl *FD = dyn_cast(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(E)) { - if (isa(DRE->getDecl())) - return true; + switch (E->getStmtClass()) { + default: + return false; + case Expr::DeclRefExprClass: { + const DeclRefExpr *DRE = cast(E); + // ... the address of an object with static storage duration, if (const VarDecl *VD = dyn_cast(DRE->getDecl())) return VD->hasGlobalStorage(); + // ... to the address of a function, + if (isa(DRE->getDecl())) + return true; return false; } - - if (const CompoundLiteralExpr *CLE = dyn_cast(E)) - return CLE->isFileScope(); - - if (isa(E) || isa(E)) - return false; - - return true; + case Expr::CompoundLiteralExprClass: + return cast(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(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(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()->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(ObjType->castAs()->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()->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(VD) || !VD->getAnyInitializer()->isLValue()) @@ -758,6 +1014,20 @@ static EvalStmtResult EvaluateStmt(CCValue &Result, EvalInfo &Info, } } +namespace { +typedef SmallVector ArgVector; +} + +/// EvaluateArgs - Evaluate the arguments to a function call. +static bool EvaluateArgs(ArrayRef Args, ArgVector &ArgValues, + EvalInfo &Info) { + for (ArrayRef::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 Args, const Stmt *Body, EvalInfo &Info, CCValue &Result) { @@ -765,17 +1035,87 @@ static bool HandleFunctionCall(ArrayRef Args, const Stmt *Body, if (Info.NumCalls >= 1000000 || Info.CallStackDepth >= 512) return false; - SmallVector ArgValues(Args.size()); - // FIXME: Deal with default arguments and 'this'. - for (ArrayRef::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 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 { @@ -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(E->getMemberDecl()); + if (!FD) return false; + assert(!FD->getType()->isReferenceType() && "prvalue reference?"); + assert(BaseTy->getAs()->getDecl()->getCanonicalDecl() == + FD->getParent()->getCanonicalDecl() && "record / field mismatch"); + + SubobjectDesignator Designator; + Designator.addDecl(FD); + + return ExtractSubobject(Info, Val, BaseTy, Designator, E->getType()) && + DerivedSuccess(Val, E); + } + RetTy VisitCastExpr(const CastExpr *E) { switch (E->getCastKind()) { default: @@ -1075,6 +1432,7 @@ public: // * CompoundLiteralExpr in C // * StringLiteral // * PredefinedExpr +// * ObjCStringLiteralExpr // * ObjCEncodeExpr // * AddrLabelExpr // * BlockExpr @@ -1129,8 +1487,24 @@ public: Result.Designator.setInvalid(); return true; - // FIXME: Support CK_DerivedToBase and CK_UncheckedDerivedToBase. - // Reuse PointerExprEvaluator::VisitCastExpr for these. + case CK_DerivedToBase: + case CK_UncheckedDerivedToBase: { + if (!Visit(E->getSubExpr())) + return false; + + // Now figure out the necessary offset to add to the base LV to get from + // the derived class to the base class. + QualType Type = E->getSubExpr()->getType(); + + for (CastExpr::path_const_iterator PathI = E->path_begin(), + PathE = E->path_end(); PathI != PathE; ++PathI) { + if (!HandleLValueBase(Info, Result, Type->getAsCXXRecordDecl(), *PathI)) + return false; + Type = (*PathI)->getType(); + } + + return true; + } } } @@ -1169,7 +1543,7 @@ bool LValueExprEvaluator::VisitVarDecl(const Expr *E, const VarDecl *VD) { } CCValue V; - if (EvaluateVarDeclInit(Info, VD, Info.CurrentCall, V)) + if (EvaluateVarDeclInit(Info, E, VD, Info.CurrentCall, V)) return Success(V, E); return Error(E); @@ -1177,7 +1551,9 @@ bool LValueExprEvaluator::VisitVarDecl(const Expr *E, const VarDecl *VD) { bool LValueExprEvaluator::VisitMaterializeTemporaryExpr( const MaterializeTemporaryExpr *E) { - return MakeTemporary(E, E->GetTemporaryExpr(), Result); + Result.setExpr(E, Info.CurrentCall); + return EvaluateConstantExpression(Info.CurrentCall->Temporaries[E], Info, + Result, E->GetTemporaryExpr()); } bool @@ -1203,30 +1579,32 @@ bool LValueExprEvaluator::VisitMemberExpr(const MemberExpr *E) { } } - QualType Ty; + // Handle non-static data members. + QualType BaseTy; if (E->isArrow()) { if (!EvaluatePointer(E->getBase(), Result, Info)) return false; - Ty = E->getBase()->getType()->getAs()->getPointeeType(); + BaseTy = E->getBase()->getType()->getAs()->getPointeeType(); } else { if (!Visit(E->getBase())) return false; - Ty = E->getBase()->getType(); + BaseTy = E->getBase()->getType(); } - const RecordDecl *RD = Ty->getAs()->getDecl(); - const ASTRecordLayout &RL = Info.Ctx.getASTRecordLayout(RD); - const FieldDecl *FD = dyn_cast(E->getMemberDecl()); - if (!FD) // FIXME: deal with other kinds of member expressions - return false; + if (!FD) return false; + assert(BaseTy->getAs()->getDecl()->getCanonicalDecl() == + FD->getParent()->getCanonicalDecl() && "record / field mismatch"); + (void)BaseTy; - if (FD->getType()->isReferenceType()) - return false; + HandleLValueMember(Info, Result, FD); - unsigned i = FD->getFieldIndex(); - Result.Offset += Info.Ctx.toCharUnitsFromBits(RL.getFieldOffset(i)); - Result.Designator.addDecl(FD); + if (FD->getType()->isReferenceType()) { + CCValue RefValue; + if (!HandleLValueToRValueConversion(Info, FD->getType(), Result, RefValue)) + return false; + return Success(RefValue, E); + } return true; } @@ -1241,14 +1619,11 @@ bool LValueExprEvaluator::VisitArraySubscriptExpr(const ArraySubscriptExpr *E) { APSInt Index; if (!EvaluateInteger(E->getIdx(), Index, Info)) return false; - uint64_t IndexValue - = Index.isSigned() ? static_cast(Index.getSExtValue()) - : Index.getZExtValue(); + int64_t IndexValue + = Index.isSigned() ? Index.getSExtValue() + : static_cast(Index.getZExtValue()); - CharUnits ElementSize = Info.Ctx.getTypeSizeInChars(E->getType()); - Result.Offset += IndexValue * ElementSize; - Result.Designator.adjustIndex(IndexValue); - return true; + return HandleLValueArrayAdjustment(Info, Result, E->getType(), IndexValue); } bool LValueExprEvaluator::VisitUnaryDeref(const UnaryOperator *E) { @@ -1299,6 +1674,12 @@ public: } bool VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *E) { return ValueInitialization(E); } + bool VisitCXXThisExpr(const CXXThisExpr *E) { + if (!Info.CurrentCall->This) + return false; + Result = *Info.CurrentCall->This; + return true; + } // FIXME: Missing: @protocol, @selector }; @@ -1331,27 +1712,14 @@ bool PointerExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) { if (E->getOpcode() == BO_Sub) AdditionalOffset = -AdditionalOffset; - // Compute the new offset in the appropriate width. - QualType PointeeType = - PExp->getType()->getAs()->getPointeeType(); - CharUnits SizeOfPointee; - - // Explicitly handle GNU void* and function pointer arithmetic extensions. - if (PointeeType->isVoidType() || PointeeType->isFunctionType()) - SizeOfPointee = CharUnits::One(); - else - SizeOfPointee = Info.Ctx.getTypeSizeInChars(PointeeType); - - Result.Offset += AdditionalOffset * SizeOfPointee; - Result.Designator.adjustIndex(AdditionalOffset); - return true; + QualType Pointee = PExp->getType()->getAs()->getPointeeType(); + return HandleLValueArrayAdjustment(Info, Result, Pointee, AdditionalOffset); } bool PointerExprEvaluator::VisitUnaryAddrOf(const UnaryOperator *E) { return EvaluateLValue(E->getSubExpr(), Result, Info); } - bool PointerExprEvaluator::VisitCastExpr(const CastExpr* E) { const Expr* SubExpr = E->getSubExpr(); @@ -1373,31 +1741,18 @@ bool PointerExprEvaluator::VisitCastExpr(const CastExpr* E) { if (!EvaluatePointer(E->getSubExpr(), Result, Info)) return false; - // Now figure out the necessary offset to add to the baseLV to get from + // Now figure out the necessary offset to add to the base LV to get from // the derived class to the base class. - QualType Ty = E->getSubExpr()->getType(); - const CXXRecordDecl *DerivedDecl = - Ty->getAs()->getPointeeType()->getAsCXXRecordDecl(); + QualType Type = + E->getSubExpr()->getType()->castAs()->getPointeeType(); - for (CastExpr::path_const_iterator PathI = E->path_begin(), + for (CastExpr::path_const_iterator PathI = E->path_begin(), PathE = E->path_end(); PathI != PathE; ++PathI) { - const CXXBaseSpecifier *Base = *PathI; - - // FIXME: If the base is virtual, we'd need to determine the type of the - // most derived class and we don't support that right now. - if (Base->isVirtual()) + if (!HandleLValueBase(Info, Result, Type->getAsCXXRecordDecl(), *PathI)) return false; - - const CXXRecordDecl *BaseDecl = Base->getType()->getAsCXXRecordDecl(); - const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(DerivedDecl); - - Result.getLValueOffset() += Layout.getBaseClassOffset(BaseDecl); - DerivedDecl = BaseDecl; + Type = (*PathI)->getType(); } - // FIXME - Result.Designator.setInvalid(); - return true; } @@ -1441,15 +1796,112 @@ bool PointerExprEvaluator::VisitCastExpr(const CastExpr* E) { } bool PointerExprEvaluator::VisitCallExpr(const CallExpr *E) { - if (E->isBuiltinCall(Info.Ctx) == - Builtin::BI__builtin___CFStringMakeConstantString || - E->isBuiltinCall(Info.Ctx) == - Builtin::BI__builtin___NSStringMakeConstantString) + if (IsStringLiteralCall(E)) return Success(E); return ExprEvaluatorBaseTy::VisitCallExpr(E); } +//===----------------------------------------------------------------------===// +// Record Evaluation +//===----------------------------------------------------------------------===// + +namespace { + class RecordExprEvaluator + : public ExprEvaluatorBase { + const LValue &This; + APValue &Result; + public: + + RecordExprEvaluator(EvalInfo &info, const LValue &This, APValue &Result) + : ExprEvaluatorBaseTy(info), This(This), Result(Result) {} + + bool Success(const CCValue &V, const Expr *E) { + return CheckConstantExpression(V, Result); + } + bool Error(const Expr *E) { return false; } + + bool VisitInitListExpr(const InitListExpr *E); + bool VisitCXXConstructExpr(const CXXConstructExpr *E); + }; +} + +bool RecordExprEvaluator::VisitInitListExpr(const InitListExpr *E) { + const RecordDecl *RD = E->getType()->castAs()->getDecl(); + const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(RD); + + if (RD->isUnion()) { + Result = APValue(E->getInitializedFieldInUnion()); + if (!E->getNumInits()) + return true; + LValue Subobject = This; + HandleLValueMember(Info, Subobject, E->getInitializedFieldInUnion(), + &Layout); + return EvaluateConstantExpression(Result.getUnionValue(), Info, + Subobject, E->getInit(0)); + } + + assert((!isa(RD) || !cast(RD)->getNumBases()) && + "initializer list for class with base classes"); + Result = APValue(APValue::UninitStruct(), 0, + std::distance(RD->field_begin(), RD->field_end())); + unsigned ElementNo = 0; + for (RecordDecl::field_iterator Field = RD->field_begin(), + FieldEnd = RD->field_end(); Field != FieldEnd; ++Field) { + // Anonymous bit-fields are not considered members of the class for + // purposes of aggregate initialization. + if (Field->isUnnamedBitfield()) + continue; + + LValue Subobject = This; + HandleLValueMember(Info, Subobject, *Field, &Layout); + + if (ElementNo < E->getNumInits()) { + if (!EvaluateConstantExpression( + Result.getStructField((*Field)->getFieldIndex()), + Info, Subobject, E->getInit(ElementNo++))) + return false; + } else { + // Perform an implicit value-initialization for members beyond the end of + // the initializer list. + ImplicitValueInitExpr VIE(Field->getType()); + if (!EvaluateConstantExpression( + Result.getStructField((*Field)->getFieldIndex()), + Info, Subobject, &VIE)) + return false; + } + } + + return true; +} + +bool RecordExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E) { + const CXXConstructorDecl *FD = E->getConstructor(); + const FunctionDecl *Definition = 0; + FD->getBody(Definition); + + if (!Definition || !Definition->isConstexpr() || Definition->isInvalidDecl()) + return false; + + // FIXME: Elide the copy/move construction wherever we can. + if (E->isElidable()) + if (const MaterializeTemporaryExpr *ME + = dyn_cast(E->getArg(0))) + return Visit(ME->GetTemporaryExpr()); + + llvm::ArrayRef Args(E->getArgs(), E->getNumArgs()); + return HandleConstructorCall(Args, cast(Definition), + Info, This, Result); +} + +static bool EvaluateRecord(const Expr *E, const LValue &This, + APValue &Result, EvalInfo &Info) { + assert(E->isRValue() && E->getType()->isRecordType() && + E->getType()->isLiteralType() && + "can't evaluate expression as a record rvalue"); + return RecordExprEvaluator(Info, This, Result).Visit(E); +} + //===----------------------------------------------------------------------===// // Vector Evaluation //===----------------------------------------------------------------------===// @@ -1645,11 +2097,12 @@ bool VectorExprEvaluator::VisitUnaryImag(const UnaryOperator *E) { namespace { class ArrayExprEvaluator : public ExprEvaluatorBase { + const LValue &This; APValue &Result; public: - ArrayExprEvaluator(EvalInfo &Info, APValue &Result) - : ExprEvaluatorBaseTy(Info), Result(Result) {} + ArrayExprEvaluator(EvalInfo &Info, const LValue &This, APValue &Result) + : ExprEvaluatorBaseTy(Info), This(This), Result(Result) {} bool Success(const APValue &V, const Expr *E) { assert(V.isArray() && "Expected array type"); @@ -1658,14 +2111,35 @@ namespace { } bool Error(const Expr *E) { return false; } + bool ValueInitialization(const Expr *E) { + const ConstantArrayType *CAT = + Info.Ctx.getAsConstantArrayType(E->getType()); + if (!CAT) + return false; + + Result = APValue(APValue::UninitArray(), 0, + CAT->getSize().getZExtValue()); + if (!Result.hasArrayFiller()) return true; + + // Value-initialize all elements. + LValue Subobject = This; + Subobject.Designator.addIndex(0); + ImplicitValueInitExpr VIE(CAT->getElementType()); + return EvaluateConstantExpression(Result.getArrayFiller(), Info, + Subobject, &VIE); + } + + // FIXME: We also get CXXConstructExpr, in cases like: + // struct S { constexpr S(); }; constexpr S s[10]; bool VisitInitListExpr(const InitListExpr *E); }; } // end anonymous namespace -static bool EvaluateArray(const Expr* E, APValue& Result, EvalInfo &Info) { +static bool EvaluateArray(const Expr *E, const LValue &This, + APValue &Result, EvalInfo &Info) { assert(E->isRValue() && E->getType()->isArrayType() && E->getType()->isLiteralType() && "not a literal array rvalue"); - return ArrayExprEvaluator(Info, Result).Visit(E); + return ArrayExprEvaluator(Info, This, Result).Visit(E); } bool ArrayExprEvaluator::VisitInitListExpr(const InitListExpr *E) { @@ -1675,16 +2149,26 @@ bool ArrayExprEvaluator::VisitInitListExpr(const InitListExpr *E) { Result = APValue(APValue::UninitArray(), E->getNumInits(), CAT->getSize().getZExtValue()); + LValue Subobject = This; + Subobject.Designator.addIndex(0); + unsigned Index = 0; for (InitListExpr::const_iterator I = E->begin(), End = E->end(); - I != End; ++I) - if (!EvaluateConstantExpression(Result.getArrayInitializedElt(I-E->begin()), - Info, cast(*I))) + I != End; ++I, ++Index) { + if (!EvaluateConstantExpression(Result.getArrayInitializedElt(Index), + Info, Subobject, cast(*I))) + return false; + if (!HandleLValueArrayAdjustment(Info, Subobject, CAT->getElementType(), 1)) return false; + } if (!Result.hasArrayFiller()) return true; assert(E->hasArrayFiller() && "no array filler for incomplete init list"); + // FIXME: The Subobject here isn't necessarily right. This rarely matters, + // but sometimes does: + // struct S { constexpr S() : p(&p) {} void *p; }; + // S s[10] = {}; return EvaluateConstantExpression(Result.getArrayFiller(), Info, - E->getArrayFiller()); + Subobject, E->getArrayFiller()); } //===----------------------------------------------------------------------===// @@ -1979,7 +2463,7 @@ bool IntExprEvaluator::TryEvaluateBuiltinObjectSize(const CallExpr *E) { } bool IntExprEvaluator::VisitCallExpr(const CallExpr *E) { - switch (E->isBuiltinCall(Info.Ctx)) { + switch (E->isBuiltinCall()) { default: return ExprEvaluatorBaseTy::VisitCallExpr(E); @@ -2269,11 +2753,11 @@ bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) { QualType Type = E->getLHS()->getType(); QualType ElementType = Type->getAs()->getPointeeType(); - CharUnits ElementSize = CharUnits::One(); - if (!ElementType->isVoidType() && !ElementType->isFunctionType()) - ElementSize = Info.Ctx.getTypeSizeInChars(ElementType); + CharUnits ElementSize; + if (!HandleSizeof(Info, ElementType, ElementSize)) + return false; - CharUnits Diff = LHSValue.getLValueOffset() - + CharUnits Diff = LHSValue.getLValueOffset() - RHSValue.getLValueOffset(); return Success(Diff / ElementSize, E); } @@ -2453,17 +2937,10 @@ bool IntExprEvaluator::VisitUnaryExprOrTypeTraitExpr( if (const ReferenceType *Ref = SrcTy->getAs()) SrcTy = Ref->getPointeeType(); - // sizeof(void), __alignof__(void), sizeof(function) = 1 as a gcc - // extension. - if (SrcTy->isVoidType() || SrcTy->isFunctionType()) - return Success(1, E); - - // sizeof(vla) is not a constantexpr: C99 6.5.3.4p2. - if (!SrcTy->isConstantSizeType()) + CharUnits Sizeof; + if (!HandleSizeof(Info, SrcTy, Sizeof)) return false; - - // Get information about the size. - return Success(Info.Ctx.getTypeSizeInChars(SrcTy), E); + return Success(Sizeof, E); } } @@ -2796,7 +3273,7 @@ static bool TryEvaluateBuiltinNaN(const ASTContext &Context, } bool FloatExprEvaluator::VisitCallExpr(const CallExpr *E) { - switch (E->isBuiltinCall(Info.Ctx)) { + switch (E->isBuiltinCall()) { default: return ExprEvaluatorBaseTy::VisitCallExpr(E); @@ -3350,11 +3827,17 @@ static bool Evaluate(CCValue &Result, EvalInfo &Info, const Expr *E) { // FIXME: Implement evaluation of pointer-to-member types. return false; } else if (E->getType()->isArrayType() && E->getType()->isLiteralType()) { - if (!EvaluateArray(E, Result, Info)) + LValue LV; + LV.setExpr(E, Info.CurrentCall); + if (!EvaluateArray(E, LV, Info.CurrentCall->Temporaries[E], Info)) return false; + Result = Info.CurrentCall->Temporaries[E]; } else if (E->getType()->isRecordType() && E->getType()->isLiteralType()) { - // FIXME: Implement evaluation of record rvalues. - return false; + LValue LV; + LV.setExpr(E, Info.CurrentCall); + if (!EvaluateRecord(E, LV, Info.CurrentCall->Temporaries[E], Info)) + return false; + Result = Info.CurrentCall->Temporaries[E]; } else return false; @@ -3366,16 +3849,14 @@ static bool Evaluate(CCValue &Result, EvalInfo &Info, const Expr *E) { /// since later initializers for an object can indirectly refer to subobjects /// which were initialized earlier. static bool EvaluateConstantExpression(APValue &Result, EvalInfo &Info, - const Expr *E) { + const LValue &This, const Expr *E) { if (E->isRValue() && E->getType()->isLiteralType()) { // Evaluate arrays and record types in-place, so that later initializers can // refer to earlier-initialized members of the object. - if (E->getType()->isArrayType()) { - if (!EvaluateArray(E, Result, Info)) - return false; - } else if (E->getType()->isRecordType()) - // FIXME: Implement evaluation of record rvalues. - return false; + if (E->getType()->isArrayType()) + return EvaluateArray(E, This, Result, Info); + else if (E->getType()->isRecordType()) + return EvaluateRecord(E, This, Result, Info); } // For any other type, in-place evaluation is unimportant. @@ -3399,6 +3880,7 @@ bool Expr::EvaluateAsRValue(EvalResult &Result, const ASTContext &Ctx) const { EvalInfo Info(Ctx, Result); + // FIXME: If this is the initializer for an lvalue, pass that in. CCValue Value; if (!::Evaluate(Value, Info, this)) return false; @@ -3410,10 +3892,6 @@ bool Expr::EvaluateAsRValue(EvalResult &Result, const ASTContext &Ctx) const { return false; } - // Don't produce array constants until CodeGen is taught to handle them. - if (Value.isArray()) - return false; - // Check this core constant expression is a constant expression, and if so, // convert it to one. return CheckConstantExpression(Value, Result.Val); @@ -3628,7 +4106,7 @@ static ICEDiag CheckICE(const Expr* E, ASTContext &Ctx) { // constant expressions, but they can never be ICEs because an ICE cannot // contain an operand of (pointer to) function type. const CallExpr *CE = cast(E); - if (CE->isBuiltinCall(Ctx)) + if (CE->isBuiltinCall()) return CheckEvalInICE(E, Ctx); return ICEDiag(2, E->getLocStart()); } @@ -3867,7 +4345,7 @@ static ICEDiag CheckICE(const Expr* E, ASTContext &Ctx) { // extension. See GCC PR38377 for discussion. if (const CallExpr *CallCE = dyn_cast(Exp->getCond()->IgnoreParenCasts())) - if (CallCE->isBuiltinCall(Ctx) == Builtin::BI__builtin_constant_p) { + if (CallCE->isBuiltinCall() == Builtin::BI__builtin_constant_p) { Expr::EvalResult EVResult; if (!E->EvaluateAsRValue(EVResult, Ctx) || EVResult.HasSideEffects || !EVResult.Val.isInt()) { diff --git a/lib/CodeGen/CGExprConstant.cpp b/lib/CodeGen/CGExprConstant.cpp index 897ea02e58..fc87873687 100644 --- a/lib/CodeGen/CGExprConstant.cpp +++ b/lib/CodeGen/CGExprConstant.cpp @@ -908,7 +908,7 @@ public: } case Expr::CallExprClass: { CallExpr* CE = cast(E); - unsigned builtin = CE->isBuiltinCall(CGM.getContext()); + unsigned builtin = CE->isBuiltinCall(); if (builtin != Builtin::BI__builtin___CFStringMakeConstantString && builtin != @@ -1071,7 +1071,8 @@ llvm::Constant *CodeGenModule::EmitConstantExpr(const Expr *E, return llvm::ConstantVector::get(Inits); } case APValue::Array: - assert(0 && "shouldn't see array constants here yet"); + case APValue::Struct: + case APValue::Union: break; } } diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp index ad290fa568..f4968f697a 100644 --- a/lib/Sema/SemaChecking.cpp +++ b/lib/Sema/SemaChecking.cpp @@ -2439,7 +2439,7 @@ void Sema::CheckStrlcpycatArguments(const CallExpr *Call, else { // Look for 'strlcpy(dst, x, strlen(x))' if (const CallExpr *SizeCall = dyn_cast(SizeArg)) { - if (SizeCall->isBuiltinCall(Context) == Builtin::BIstrlen + if (SizeCall->isBuiltinCall() == Builtin::BIstrlen && SizeCall->getNumArgs() == 1) CompareWithSrc = ignoreLiteralAdditions(SizeCall->getArg(0), Context); } @@ -2887,12 +2887,12 @@ void Sema::CheckFloatComparison(SourceLocation Loc, Expr* LHS, Expr *RHS) { // Check for comparisons with builtin types. if (EmitWarning) if (CallExpr* CL = dyn_cast(LeftExprSansParen)) - if (CL->isBuiltinCall(Context)) + if (CL->isBuiltinCall()) EmitWarning = false; if (EmitWarning) if (CallExpr* CR = dyn_cast(RightExprSansParen)) - if (CR->isBuiltinCall(Context)) + if (CR->isBuiltinCall()) EmitWarning = false; // Emit the diagnostic. diff --git a/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp b/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp index ae28300d13..97cc44f0cb 100644 --- a/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp @@ -83,8 +83,6 @@ void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G, if (!C || !PM) return; - ASTContext &Ctx = B.getContext(); - // Find CFGBlocks that were not covered by any node for (CFG::const_iterator I = C->begin(), E = C->end(); I != E; ++I) { const CFGBlock *CB = *I; @@ -117,7 +115,7 @@ void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G, ci != ce; ++ci) { if (const CFGStmt *S = (*ci).getAs()) if (const CallExpr *CE = dyn_cast(S->getStmt())) { - if (CE->isBuiltinCall(Ctx) == Builtin::BI__builtin_unreachable) { + if (CE->isBuiltinCall() == Builtin::BI__builtin_unreachable) { foundUnreachable = true; break; } diff --git a/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p1.cpp b/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p1.cpp index 426756ad97..3f6f160221 100644 --- a/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p1.cpp +++ b/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p1.cpp @@ -117,8 +117,7 @@ constexpr int square(int x) { return x * x; } -// FIXME: The initializer is a constant expression. -constexpr pixel large(4); // unexpected-error {{must be initialized by a constant expression}} +constexpr pixel large(4); int next(constexpr int x) { // expected-error {{function parameter cannot be constexpr}} return x + 1; diff --git a/test/SemaCXX/constant-expression-cxx11.cpp b/test/SemaCXX/constant-expression-cxx11.cpp index e0bf841634..e111c658b1 100644 --- a/test/SemaCXX/constant-expression-cxx11.cpp +++ b/test/SemaCXX/constant-expression-cxx11.cpp @@ -33,6 +33,50 @@ struct MemberZero { constexpr int zero() { return 0; } }; +namespace DerivedToVBaseCast { + + struct U { int n; }; + struct V : U { int n; }; + struct A : virtual V { int n; }; + struct Aa { int n; }; + struct B : virtual A, Aa {}; + struct C : virtual A, Aa {}; + struct D : B, C {}; + + D d; + constexpr B *p = &d; + constexpr C *q = &d; + static_assert_fold((void*)p != (void*)q, ""); + static_assert_fold((A*)p == (A*)q, ""); + static_assert_fold((Aa*)p != (Aa*)q, ""); + + constexpr B &pp = d; + constexpr C &qq = d; + static_assert_fold((void*)&pp != (void*)&qq, ""); + static_assert_fold(&(A&)pp == &(A&)qq, ""); + static_assert_fold(&(Aa&)pp != &(Aa&)qq, ""); + + constexpr V *v = p; + constexpr V *w = q; + constexpr V *x = (A*)p; + static_assert_fold(v == w, ""); + static_assert_fold(v == x, ""); + + static_assert_fold((U*)&d == p, ""); + static_assert_fold((U*)&d == q, ""); + static_assert_fold((U*)&d == v, ""); + static_assert_fold((U*)&d == w, ""); + static_assert_fold((U*)&d == x, ""); + + struct X {}; + struct Y1 : virtual X {}; + struct Y2 : X {}; + struct Z : Y1, Y2 {}; + Z z; + static_assert_fold((X*)(Y1*)&z != (X*)(Y2*)&z, ""); + +} + namespace TemplateArgumentConversion { template struct IntParam {}; @@ -268,6 +312,10 @@ static_assert_fold(sameTemporary(9), ""); } +constexpr int strcmp_ce(const char *p, const char *q) { + return (!*p || *p != *q) ? *p - *q : strcmp_ce(p+1, q+1); +} + namespace StringLiteral { // FIXME: Refactor this once we support constexpr templates. @@ -309,6 +357,11 @@ constexpr const char *max = max_element(begin(str), end(str)); static_assert_fold(*max == 'z', ""); static_assert_fold(max == str + 38, ""); +static_assert_fold(strcmp_ce("hello world", "hello world") == 0, ""); +static_assert_fold(strcmp_ce("hello world", "hello clang") > 0, ""); +static_assert_fold(strcmp_ce("constexpr", "test") < 0, ""); +static_assert_fold(strcmp_ce("", " ") < 0, ""); + } namespace Array { @@ -350,6 +403,17 @@ static_assert_fold(zs[1][1][1][1] == 16, ""); static_assert_fold(zs[0][0][0][2] == 3, ""); // expected-error {{constant expression}} static_assert_fold((&zs[0][0][0][2])[-1] == 2, ""); static_assert_fold(**(**(zs + 1) + 1) == 11, ""); +static_assert_fold(*(&(&(*(*&(&zs[2] - 1)[0] + 2 - 2))[2])[-1][-1] + 1) == 11, ""); + +constexpr int arr[40] = { 1, 2, 3, [8] = 4 }; +constexpr int SumNonzero(const int *p) { + return *p + (*p ? SumNonzero(p+1) : 0); +} +constexpr int CountZero(const int *p, const int *q) { + return p == q ? 0 : (*p == 0) + CountZero(p+1, q); +} +static_assert_fold(SumNonzero(arr) == 6, ""); +static_assert_fold(CountZero(arr, arr + 40) == 36, ""); } @@ -367,3 +431,180 @@ template struct S { }; } + +namespace Class { + +struct A { constexpr A(int a, int b) : k(a + b) {} int k; }; +constexpr int fn(const A &a) { return a.k; } +static_assert_fold(fn(A(4,5)) == 9, ""); + +struct B { int n; int m; } constexpr b = { 0, b.n }; // expected-warning {{uninitialized}} +struct C { + constexpr C(C *this_) : m(42), n(this_->m) {} // ok + int m, n; +}; +struct D { + C c; + constexpr D() : c(&c) {} +}; +static_assert_fold(D().c.n == 42, ""); + +struct E { + constexpr E() : p(&p) {} + void *p; +}; +constexpr const E &e1 = E(); // expected-error {{constant expression}} +// This is a constant expression if we elide the copy constructor call, and +// is not a constant expression if we don't! But we do, so it is. +// FIXME: The move constructor is not currently implicitly defined as constexpr. +// We notice this when evaluating an expression which uses it, but not when +// checking its initializer. +constexpr E e2 = E(); // unexpected-error {{constant expression}} +static_assert_fold(e2.p == &e2.p, ""); // unexpected-error {{constant expression}} +// FIXME: We don't pass through the fact that 'this' is ::e3 when checking the +// initializer of this declaration. +constexpr E e3; // unexpected-error {{constant expression}} +static_assert_fold(e3.p == &e3.p, ""); + +extern const class F f; +struct F { + constexpr F() : p(&f.p) {} + const void *p; +}; +constexpr F f = F(); + +struct G { + struct T { + constexpr T(T *p) : u1(), u2(p) {} + union U1 { + constexpr U1() {} + int a, b = 42; + } u1; + union U2 { + constexpr U2(T *p) : c(p->u1.b) {} + int c, d; + } u2; + } t; + constexpr G() : t(&t) {} +} constexpr g; + +static_assert_fold(g.t.u1.a == 42, ""); // expected-error {{constant expression}} +static_assert_fold(g.t.u1.b == 42, ""); +static_assert_fold(g.t.u2.c == 42, ""); +static_assert_fold(g.t.u2.d == 42, ""); // expected-error {{constant expression}} + +struct S { + int a, b; + const S *p; + double d; + const char *q; + + constexpr S(int n, const S *p) : a(5), b(n), p(p), d(n), q("hello") {} +}; + +S global(43, &global); + +static_assert_fold(S(15, &global).b == 15, ""); + +constexpr bool CheckS(const S &s) { + return s.a == 5 && s.b == 27 && s.p == &global && s.d == 27. && s.q[3] == 'l'; +} +static_assert_fold(CheckS(S(27, &global)), ""); + +struct Arr { + char arr[3]; + constexpr Arr() : arr{'x', 'y', 'z'} {} +}; +constexpr int hash(Arr &&a) { + return a.arr[0] + a.arr[1] * 0x100 + a.arr[2] * 0x10000; +} +constexpr int k = hash(Arr()); +static_assert_fold(k == 0x007a7978, ""); + + +struct AggregateInit { + const char &c; + int n; + double d; + int arr[5]; + void *p; +}; + +constexpr AggregateInit agg1 = { "hello"[0] }; + +static_assert_fold(strcmp_ce(&agg1.c, "hello") == 0, ""); +static_assert_fold(agg1.n == 0, ""); +static_assert_fold(agg1.d == 0.0, ""); +static_assert_fold(agg1.arr[-1] == 0, ""); // expected-error {{constant expression}} +static_assert_fold(agg1.arr[0] == 0, ""); +static_assert_fold(agg1.arr[4] == 0, ""); +static_assert_fold(agg1.arr[5] == 0, ""); // expected-error {{constant expression}} +static_assert_fold(agg1.p == nullptr, ""); + +namespace SimpleDerivedClass { + +struct B { + constexpr B(int n) : a(n) {} + int a; +}; +struct D : B { + constexpr D(int n) : B(n) {} +}; +constexpr D d(3); +static_assert_fold(d.a == 3, ""); + +} + +struct Base { + constexpr Base(int a = 42, const char *b = "test") : a(a), b(b) {} + int a; + const char *b; +}; +struct Base2 { + constexpr Base2(const int &r) : r(r) {} + int q = 123; + // FIXME: When we track the global for which we are computing the initializer, + // use a reference here. + //const int &r; + int r; +}; +struct Derived : Base, Base2 { + constexpr Derived() : Base(76), Base2(a) {} + int c = r + b[1]; +}; + +constexpr bool operator==(const Base &a, const Base &b) { + return a.a == b.a && strcmp_ce(a.b, b.b) == 0; +} + +constexpr Base base; +constexpr Base base2(76); +constexpr Derived derived; +static_assert_fold(derived.a == 76, ""); +static_assert_fold(derived.b[2] == 's', ""); +static_assert_fold(derived.c == 76 + 'e', ""); +static_assert_fold(derived.q == 123, ""); +static_assert_fold(derived.r == 76, ""); +static_assert_fold(&derived.r == &derived.a, ""); // expected-error {{}} + +static_assert_fold(!(derived == base), ""); +static_assert_fold(derived == base2, ""); + +} + +namespace Union { + +union U { + int a; + int b; +}; + +constexpr U u[4] = { { .a = 0 }, { .b = 1 }, { .a = 2 }, { .b = 3 } }; +static_assert_fold(u[0].a == 0, ""); +static_assert_fold(u[0].b, ""); // expected-error {{constant expression}} +static_assert_fold(u[1].b == 1, ""); +static_assert_fold((&u[1].b)[1] == 2, ""); // expected-error {{constant expression}} +static_assert_fold(*(&(u[1].b) + 1 + 1) == 3, ""); // expected-error {{constant expression}} +static_assert_fold((&(u[1]) + 1 + 1)->b == 3, ""); + +}