diff options
Diffstat (limited to 'clang/lib')
-rw-r--r-- | clang/lib/AST/ExprConstant.cpp | 155 |
1 files changed, 122 insertions, 33 deletions
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index fa0fbbbf8dc..77f65337dde 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -213,7 +213,7 @@ namespace { // The order of this enum is important for diagnostics. enum CheckSubobjectKind { CSK_Base, CSK_Derived, CSK_Field, CSK_ArrayToPointer, CSK_ArrayIndex, - CSK_This, CSK_Real, CSK_Imag + CSK_Real, CSK_Imag }; /// A path from a glvalue to a subobject of that glvalue. @@ -1326,14 +1326,22 @@ void EvalInfo::addCallStack(unsigned Limit) { } } -/// Kinds of access we can perform on an object, for diagnostics. +/// Kinds of access we can perform on an object, for diagnostics. Note that +/// we consider a member function call to be a kind of access, even though +/// it is not formally an access of the object, because it has (largely) the +/// same set of semantic restrictions. enum AccessKinds { AK_Read, AK_Assign, AK_Increment, - AK_Decrement + AK_Decrement, + AK_MemberCall, }; +static bool isModification(AccessKinds AK) { + return AK != AK_Read && AK != AK_MemberCall; +} + namespace { struct ComplexValue { private: @@ -2820,6 +2828,31 @@ static bool diagnoseUnreadableFields(EvalInfo &Info, const Expr *E, return false; } +static bool lifetimeStartedInEvaluation(EvalInfo &Info, + APValue::LValueBase Base) { + // A temporary we created. + if (Base.getCallIndex()) + return true; + + auto *Evaluating = Info.EvaluatingDecl.dyn_cast<const ValueDecl*>(); + if (!Evaluating) + return false; + + // The variable whose initializer we're evaluating. + if (auto *BaseD = Base.dyn_cast<const ValueDecl*>()) + if (declaresSameEntity(Evaluating, BaseD)) + return true; + + // A temporary lifetime-extended by the variable whose initializer we're + // evaluating. + if (auto *BaseE = Base.dyn_cast<const Expr *>()) + if (auto *BaseMTE = dyn_cast<MaterializeTemporaryExpr>(BaseE)) + if (declaresSameEntity(BaseMTE->getExtendingDecl(), Evaluating)) + return true; + + return false; +} + namespace { /// A handle to a complete object (an object that is not a subobject of /// another object). @@ -2830,17 +2863,21 @@ struct CompleteObject { APValue *Value; /// The type of the complete object. QualType Type; - bool LifetimeStartedInEvaluation; CompleteObject() : Value(nullptr) {} - CompleteObject(APValue::LValueBase Base, APValue *Value, QualType Type, - bool LifetimeStartedInEvaluation) - : Base(Base), Value(Value), Type(Type), - LifetimeStartedInEvaluation(LifetimeStartedInEvaluation) { - assert(Value && "missing value for complete object"); + CompleteObject(APValue::LValueBase Base, APValue *Value, QualType Type) + : Base(Base), Value(Value), Type(Type) {} + + bool mayReadMutableMembers(EvalInfo &Info) const { + // In C++14 onwards, it is permitted to read a mutable member whose + // lifetime began within the evaluation. + // FIXME: Should we also allow this in C++11? + if (!Info.getLangOpts().CPlusPlus14) + return false; + return lifetimeStartedInEvaluation(Info, Base); } - explicit operator bool() const { return Value; } + explicit operator bool() const { return !Type.isNull(); } }; } // end anonymous namespace @@ -2880,8 +2917,6 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj, APValue *O = Obj.Value; QualType ObjType = Obj.Type; const FieldDecl *LastField = nullptr; - const bool MayReadMutableMembers = - Obj.LifetimeStartedInEvaluation && Info.getLangOpts().CPlusPlus14; const FieldDecl *VolatileField = nullptr; // Walk the designator's path to find the subobject. @@ -2910,7 +2945,8 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj, // If this is our last pass, check that the final object type is OK. if (I == N || (I == N - 1 && ObjType->isAnyComplexType())) { // Accesses to volatile objects are prohibited. - if (ObjType.isVolatileQualified()) { + if (ObjType.isVolatileQualified() && + handler.AccessKind != AK_MemberCall) { if (Info.getLangOpts().CPlusPlus) { int DiagKind; SourceLocation Loc; @@ -2942,7 +2978,8 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj, // cannot perform this read. (This only happens when performing a trivial // copy or assignment.) if (ObjType->isRecordType() && handler.AccessKind == AK_Read && - !MayReadMutableMembers && diagnoseUnreadableFields(Info, E, ObjType)) + !Obj.mayReadMutableMembers(Info) && + diagnoseUnreadableFields(Info, E, ObjType)) return handler.failed(); } @@ -2951,7 +2988,7 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj, return false; // If we modified a bit-field, truncate it to the right width. - if (handler.AccessKind != AK_Read && + if (isModification(handler.AccessKind) && LastField && LastField->isBitField() && !truncateBitfieldValue(Info, E, *O, LastField)) return false; @@ -3010,11 +3047,8 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj, : O->getComplexFloatReal(), ObjType); } } else if (const FieldDecl *Field = getAsField(Sub.Entries[I])) { - // In C++14 onwards, it is permitted to read a mutable member whose - // lifetime began within the evaluation. - // FIXME: Should we also allow this in C++11? if (Field->isMutable() && handler.AccessKind == AK_Read && - !MayReadMutableMembers) { + !Obj.mayReadMutableMembers(Info)) { Info.FFDiag(E, diag::note_constexpr_ltor_mutable, 1) << Field; Info.Note(Field->getLocation(), diag::note_declared_at); @@ -3226,7 +3260,7 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, // is not a constant expression (even if the object is non-volatile). We also // apply this rule to C++98, in order to conform to the expected 'volatile' // semantics. - if (LValType.isVolatileQualified()) { + if (AK != AK_MemberCall && LValType.isVolatileQualified()) { if (Info.getLangOpts().CPlusPlus) Info.FFDiag(E, diag::note_constexpr_access_volatile_type) << AK << LValType; @@ -3235,10 +3269,16 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, return CompleteObject(); } + // The wording is unclear on this, but for the purpose of determining the + // validity of a member function call, we assume that all objects whose + // lifetimes did not start within the constant evaluation are in fact within + // their lifetimes, so member calls on them are valid. (This simultaneously + // includes all members of a union!) + bool NeedValue = AK != AK_MemberCall; + // Compute value storage location and type of base object. APValue *BaseVal = nullptr; QualType BaseType = getType(LVal.Base); - bool LifetimeStartedInEvaluation = Frame; if (const ValueDecl *D = LVal.Base.dyn_cast<const ValueDecl*>()) { // In C++98, const, non-volatile integers initialized with ICEs are ICEs. @@ -3262,22 +3302,25 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, // the variable we're reading must be const. if (!Frame) { if (Info.getLangOpts().CPlusPlus14 && - VD == Info.EvaluatingDecl.dyn_cast<const ValueDecl *>()) { + declaresSameEntity( + VD, Info.EvaluatingDecl.dyn_cast<const ValueDecl *>())) { // OK, we can read and modify an object if we're in the process of // evaluating its initializer, because its lifetime began in this // evaluation. - LifetimeStartedInEvaluation = true; - } else if (AK != AK_Read) { - // All the remaining cases only permit reading. + } else if (isModification(AK)) { + // All the remaining cases do not permit modification of the object. Info.FFDiag(E, diag::note_constexpr_modify_global); return CompleteObject(); } else if (VD->isConstexpr()) { // OK, we can read this variable. } else if (BaseType->isIntegralOrEnumerationType()) { - // In OpenCL if a variable is in constant address space it is a const value. + // In OpenCL if a variable is in constant address space it is a const + // value. if (!(BaseType.isConstQualified() || (Info.getLangOpts().OpenCL && BaseType.getAddressSpace() == LangAS::opencl_constant))) { + if (!NeedValue) + return CompleteObject(LVal.getLValueBase(), nullptr, BaseType); if (Info.getLangOpts().CPlusPlus) { Info.FFDiag(E, diag::note_constexpr_ltor_non_const_int, 1) << VD; Info.Note(VD->getLocation(), diag::note_declared_at); @@ -3286,6 +3329,8 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, } return CompleteObject(); } + } else if (!NeedValue) { + return CompleteObject(LVal.getLValueBase(), nullptr, BaseType); } else if (BaseType->isFloatingType() && BaseType.isConstQualified()) { // We support folding of const floating-point types, in order to make // static const data members of such types (supported as an extension) @@ -3345,6 +3390,8 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, if (!(BaseType.isConstQualified() && BaseType->isIntegralOrEnumerationType()) && !(VD && VD->getCanonicalDecl() == ED->getCanonicalDecl())) { + if (!NeedValue) + return CompleteObject(LVal.getLValueBase(), nullptr, BaseType); Info.FFDiag(E, diag::note_constexpr_access_static_temporary, 1) << AK; Info.Note(MTE->getExprLoc(), diag::note_constexpr_temporary_here); return CompleteObject(); @@ -3352,8 +3399,9 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, BaseVal = Info.Ctx.getMaterializedTemporaryValue(MTE, false); assert(BaseVal && "got reference to unevaluated temporary"); - LifetimeStartedInEvaluation = true; } else { + if (!NeedValue) + return CompleteObject(LVal.getLValueBase(), nullptr, BaseType); Info.FFDiag(E); return CompleteObject(); } @@ -3370,11 +3418,10 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, // to be read here (but take care with 'mutable' fields). if ((Frame && Info.getLangOpts().CPlusPlus14 && Info.EvalStatus.HasSideEffects) || - (AK != AK_Read && Depth < Info.SpeculativeEvaluationDepth)) + (isModification(AK) && Depth < Info.SpeculativeEvaluationDepth)) return CompleteObject(); - return CompleteObject(LVal.getLValueBase(), BaseVal, BaseType, - LifetimeStartedInEvaluation); + return CompleteObject(LVal.getLValueBase(), BaseVal, BaseType); } /// Perform an lvalue-to-rvalue conversion on the given glvalue. This @@ -3408,7 +3455,7 @@ static bool handleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv, APValue Lit; if (!Evaluate(Lit, Info, CLE->getInitializer())) return false; - CompleteObject LitObj(LVal.Base, &Lit, Base->getType(), false); + CompleteObject LitObj(LVal.Base, &Lit, Base->getType()); return extractSubobject(Info, Conv, LitObj, LVal.Designator, RVal); } else if (isa<StringLiteral>(Base) || isa<PredefinedExpr>(Base)) { // Special-case character extraction so we don't have to construct an @@ -4454,6 +4501,48 @@ static bool CheckConstexprFunction(EvalInfo &Info, SourceLocation CallLoc, return false; } +namespace { +struct CheckMemberCallThisPointerHandler { + static const AccessKinds AccessKind = AK_MemberCall; + typedef bool result_type; + bool failed() { return false; } + bool found(APValue &Subobj, QualType SubobjType) { return true; } + bool found(APSInt &Value, QualType SubobjType) { return true; } + bool found(APFloat &Value, QualType SubobjType) { return true; } +}; +} // end anonymous namespace + +const AccessKinds CheckMemberCallThisPointerHandler::AccessKind; + +/// Check that the pointee of the 'this' pointer in a member function call is +/// either within its lifetime or in its period of construction or destruction. +static bool checkMemberCallThisPointer(EvalInfo &Info, const Expr *E, + const LValue &This) { + CompleteObject Obj = + findCompleteObject(Info, E, AK_MemberCall, This, QualType()); + + if (!Obj) + return false; + + if (!Obj.Value) { + // The object is not usable in constant expressions, so we can't inspect + // its value to see if it's in-lifetime or what the active union members + // are. We can still check for a one-past-the-end lvalue. + if (This.Designator.isOnePastTheEnd() || + This.Designator.isMostDerivedAnUnsizedArray()) { + Info.FFDiag(E, This.Designator.isOnePastTheEnd() + ? diag::note_constexpr_access_past_end + : diag::note_constexpr_access_unsized_array) + << AK_MemberCall; + return false; + } + return true; + } + + CheckMemberCallThisPointerHandler Handler; + return Obj && findSubobject(Info, E, Obj, This.Designator, Handler); +} + /// Determine if a class has any fields that might need to be copied by a /// trivial copy or move operation. static bool hasFields(const CXXRecordDecl *RD) { @@ -5038,7 +5127,7 @@ public: } else return Error(E); - if (This && !This->checkSubobject(Info, E, CSK_This)) + if (This && !checkMemberCallThisPointer(Info, E, *This)) return false; const FunctionDecl *Definition = nullptr; @@ -5093,7 +5182,7 @@ public: // Note: there is no lvalue base here. But this case should only ever // happen in C or in C++98, where we cannot be evaluating a constexpr // constructor, which is the only case the base matters. - CompleteObject Obj(APValue::LValueBase(), &Val, BaseTy, true); + CompleteObject Obj(APValue::LValueBase(), &Val, BaseTy); SubobjectDesignator Designator(BaseTy); Designator.addDeclUnchecked(FD); |