diff options
-rw-r--r-- | clang/include/clang/AST/APValue.h | 10 | ||||
-rw-r--r-- | clang/include/clang/Basic/DiagnosticASTKinds.td | 26 | ||||
-rw-r--r-- | clang/lib/AST/APValue.cpp | 13 | ||||
-rw-r--r-- | clang/lib/AST/ExprConstant.cpp | 380 | ||||
-rw-r--r-- | clang/test/CXX/expr/expr.const/p2-0x.cpp | 4 | ||||
-rw-r--r-- | clang/test/SemaCXX/constant-expression-cxx11.cpp | 76 | ||||
-rw-r--r-- | clang/test/SemaCXX/constant-expression-cxx1y.cpp | 46 |
7 files changed, 145 insertions, 410 deletions
diff --git a/clang/include/clang/AST/APValue.h b/clang/include/clang/AST/APValue.h index d2664730684..5696bc2e17a 100644 --- a/clang/include/clang/AST/APValue.h +++ b/clang/include/clang/AST/APValue.h @@ -96,14 +96,10 @@ public: return Version; } - friend bool operator==(const LValueBase &LHS, const LValueBase &RHS) { - return LHS.Ptr == RHS.Ptr && LHS.CallIndex == RHS.CallIndex && - LHS.Version == RHS.Version; + bool operator==(const LValueBase &Other) const { + return Ptr == Other.Ptr && CallIndex == Other.CallIndex && + Version == Other.Version; } - friend bool operator!=(const LValueBase &LHS, const LValueBase &RHS) { - return !(LHS == RHS); - } - friend llvm::hash_code hash_value(const LValueBase &Base); private: PtrTy Ptr; diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td index 8801461a7e5..821dc7ccadf 100644 --- a/clang/include/clang/Basic/DiagnosticASTKinds.td +++ b/clang/include/clang/Basic/DiagnosticASTKinds.td @@ -67,13 +67,13 @@ def note_constexpr_past_end : Note< "%select{temporary|%2}1 is not a constant expression">; def note_constexpr_past_end_subobject : Note< "cannot %select{access base class of|access derived class of|access field of|" - "access array element of|ERROR|" + "access array element of|ERROR|call member function on|" "access real component of|access imaginary component of}0 " "pointer past the end of object">; def note_constexpr_null_subobject : Note< "cannot %select{access base class of|access derived class of|access field of|" "access array element of|perform pointer arithmetic on|" - "access real component of|" + "call member function on|access real component of|" "access imaginary component of}0 null pointer">; def note_constexpr_var_init_non_constant : Note< "initializer of %0 is not a constant expression">; @@ -96,10 +96,10 @@ def note_constexpr_this : Note< "%select{|implicit }0use of 'this' pointer is only allowed within the " "evaluation of a call to a 'constexpr' member function">; def note_constexpr_lifetime_ended : Note< - "%select{read of|assignment to|increment of|decrement of|member call on}0 " + "%select{read of|assignment to|increment of|decrement of}0 " "%select{temporary|variable}1 whose lifetime has ended">; def note_constexpr_access_uninit : Note< - "%select{read of|assignment to|increment of|decrement of|member call on}0 " + "%select{read of|assignment to|increment of|decrement of}0 " "object outside its lifetime is not allowed in a constant expression">; def note_constexpr_use_uninit_reference : Note< "use of reference outside its lifetime " @@ -108,14 +108,12 @@ def note_constexpr_modify_const_type : Note< "modification of object of const-qualified type %0 is not allowed " "in a constant expression">; def note_constexpr_access_volatile_type : Note< - "%select{read of|assignment to|increment of|decrement of|<ERROR>}0 " + "%select{read of|assignment to|increment of|decrement of}0 " "volatile-qualified type %1 is not allowed in a constant expression">; def note_constexpr_access_volatile_obj : Note< - "%select{read of|assignment to|increment of|decrement of|<ERROR>}0 volatile " + "%select{read of|assignment to|increment of|decrement of}0 volatile " "%select{temporary|object %2|member %2}1 is not allowed in " "a constant expression">; -def note_constexpr_volatile_here : Note< - "volatile %select{temporary created|object declared|member declared}0 here">; def note_constexpr_ltor_mutable : Note< "read of mutable member %0 is not allowed in a constant expression">; def note_constexpr_ltor_non_const_int : Note< @@ -125,21 +123,21 @@ def note_constexpr_ltor_non_constexpr : Note< def note_constexpr_ltor_incomplete_type : Note< "read of incomplete type %0 is not allowed in a constant expression">; def note_constexpr_access_null : Note< - "%select{read of|assignment to|increment of|decrement of|member call on}0 " + "%select{read of|assignment to|increment of|decrement of}0 " "dereferenced null pointer is not allowed in a constant expression">; def note_constexpr_access_past_end : Note< - "%select{read of|assignment to|increment of|decrement of|member call on}0 " + "%select{read of|assignment to|increment of|decrement of}0 " "dereferenced one-past-the-end pointer is not allowed in a constant expression">; def note_constexpr_access_unsized_array : Note< - "%select{read of|assignment to|increment of|decrement of|member call on}0 " - "element of array without known bound " + "%select{read of|assignment to|increment of|decrement of}0 " + "pointer to element of array without known bound " "is not allowed in a constant expression">; def note_constexpr_access_inactive_union_member : Note< - "%select{read of|assignment to|increment of|decrement of|member call on}0 " + "%select{read of|assignment to|increment of|decrement of}0 " "member %1 of union with %select{active member %3|no active member}2 " "is not allowed in a constant expression">; def note_constexpr_access_static_temporary : Note< - "%select{read of|assignment to|increment of|decrement of|<ERROR>}0 temporary " + "%select{read of|assignment to|increment of|decrement of}0 temporary " "is not allowed in a constant expression outside the expression that " "created the temporary">; def note_constexpr_modify_global : Note< diff --git a/clang/lib/AST/APValue.cpp b/clang/lib/AST/APValue.cpp index e7902e68780..9ed756d9d84 100644 --- a/clang/lib/AST/APValue.cpp +++ b/clang/lib/AST/APValue.cpp @@ -58,16 +58,13 @@ llvm::DenseMapInfo<clang::APValue::LValueBase>::getTombstoneKey() { DenseMapInfo<unsigned>::getTombstoneKey()); } -namespace clang { -llvm::hash_code hash_value(const APValue::LValueBase &Base) { - return llvm::hash_combine(Base.getOpaqueValue(), Base.getCallIndex(), - Base.getVersion()); -} -} - unsigned llvm::DenseMapInfo<clang::APValue::LValueBase>::getHashValue( const clang::APValue::LValueBase &Base) { - return hash_value(Base); + llvm::FoldingSetNodeID ID; + ID.AddPointer(Base.getOpaqueValue()); + ID.AddInteger(Base.getCallIndex()); + ID.AddInteger(Base.getVersion()); + return ID.ComputeHash(); } bool llvm::DenseMapInfo<clang::APValue::LValueBase>::isEqual( diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 77f65337dde..0c86ec65744 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_Real, CSK_Imag + CSK_This, CSK_Real, CSK_Imag }; /// A path from a glvalue to a subobject of that glvalue. @@ -622,40 +622,6 @@ namespace { } }; - /// A reference to an object whose construction we are currently evaluating. - struct ObjectUnderConstruction { - APValue::LValueBase Base; - ArrayRef<APValue::LValuePathEntry> Path; - friend bool operator==(const ObjectUnderConstruction &LHS, - const ObjectUnderConstruction &RHS) { - return LHS.Base == RHS.Base && LHS.Path == RHS.Path; - } - friend llvm::hash_code hash_value(const ObjectUnderConstruction &Obj) { - return llvm::hash_combine(Obj.Base, Obj.Path); - } - }; - enum class ConstructionPhase { None, Bases, AfterBases }; -} - -namespace llvm { -template<> struct DenseMapInfo<ObjectUnderConstruction> { - using Base = DenseMapInfo<APValue::LValueBase>; - static ObjectUnderConstruction getEmptyKey() { - return {Base::getEmptyKey(), {}}; } - static ObjectUnderConstruction getTombstoneKey() { - return {Base::getTombstoneKey(), {}}; - } - static unsigned getHashValue(const ObjectUnderConstruction &Object) { - return hash_value(Object); - } - static bool isEqual(const ObjectUnderConstruction &LHS, - const ObjectUnderConstruction &RHS) { - return LHS == RHS; - } -}; -} - -namespace { /// EvalInfo - This is a private struct used by the evaluator to capture /// information about a subexpression as it is folded. It retains information /// about the AST context, but also maintains information about the folded @@ -706,35 +672,32 @@ namespace { /// declaration whose initializer is being evaluated, if any. APValue *EvaluatingDeclValue; - /// Set of objects that are currently being constructed. - llvm::DenseMap<ObjectUnderConstruction, ConstructionPhase> - ObjectsUnderConstruction; + /// EvaluatingObject - Pair of the AST node that an lvalue represents and + /// the call index that that lvalue was allocated in. + typedef std::pair<APValue::LValueBase, std::pair<unsigned, unsigned>> + EvaluatingObject; + + /// EvaluatingConstructors - Set of objects that are currently being + /// constructed. + llvm::DenseSet<EvaluatingObject> EvaluatingConstructors; struct EvaluatingConstructorRAII { EvalInfo &EI; - ObjectUnderConstruction Object; + EvaluatingObject Object; bool DidInsert; - EvaluatingConstructorRAII(EvalInfo &EI, ObjectUnderConstruction Object, - bool HasBases) + EvaluatingConstructorRAII(EvalInfo &EI, EvaluatingObject Object) : EI(EI), Object(Object) { - DidInsert = - EI.ObjectsUnderConstruction - .insert({Object, HasBases ? ConstructionPhase::Bases - : ConstructionPhase::AfterBases}) - .second; - } - void finishedConstructingBases() { - EI.ObjectsUnderConstruction[Object] = ConstructionPhase::AfterBases; + DidInsert = EI.EvaluatingConstructors.insert(Object).second; } ~EvaluatingConstructorRAII() { - if (DidInsert) EI.ObjectsUnderConstruction.erase(Object); + if (DidInsert) EI.EvaluatingConstructors.erase(Object); } }; - ConstructionPhase - isEvaluatingConstructor(APValue::LValueBase Base, - ArrayRef<APValue::LValuePathEntry> Path) { - return ObjectsUnderConstruction.lookup({Base, Path}); + bool isEvaluatingConstructor(APValue::LValueBase Decl, unsigned CallIndex, + unsigned Version) { + return EvaluatingConstructors.count( + EvaluatingObject(Decl, {CallIndex, Version})); } /// If we're currently speculatively evaluating, the outermost call stack @@ -823,6 +786,7 @@ namespace { void setEvaluatingDecl(APValue::LValueBase Base, APValue &Value) { EvaluatingDecl = Base; EvaluatingDeclValue = &Value; + EvaluatingConstructors.insert({Base, {0, 0}}); } const LangOptions &getLangOpts() const { return Ctx.getLangOpts(); } @@ -1326,22 +1290,14 @@ void EvalInfo::addCallStack(unsigned Limit) { } } -/// 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. +/// Kinds of access we can perform on an object, for diagnostics. enum AccessKinds { AK_Read, AK_Assign, AK_Increment, - AK_Decrement, - AK_MemberCall, + AK_Decrement }; -static bool isModification(AccessKinds AK) { - return AK != AK_Read && AK != AK_MemberCall; -} - namespace { struct ComplexValue { private: @@ -2828,73 +2784,28 @@ 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). struct CompleteObject { - /// The identity of the object. - APValue::LValueBase Base; /// The value of the complete object. APValue *Value; /// The type of the complete object. QualType Type; + bool LifetimeStartedInEvaluation; CompleteObject() : Value(nullptr) {} - 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); + CompleteObject(APValue *Value, QualType Type, + bool LifetimeStartedInEvaluation) + : Value(Value), Type(Type), + LifetimeStartedInEvaluation(LifetimeStartedInEvaluation) { + assert(Value && "missing value for complete object"); } - explicit operator bool() const { return !Type.isNull(); } + explicit operator bool() const { return Value; } }; } // end anonymous namespace -static QualType getSubobjectType(QualType ObjType, QualType SubobjType, - bool IsMutable = false) { - // C++ [basic.type.qualifier]p1: - // - A const object is an object of type const T or a non-mutable subobject - // of a const object. - if (ObjType.isConstQualified() && !IsMutable) - SubobjType.addConst(); - // - A volatile object is an object of type const T or a subobject of a - // volatile object. - if (ObjType.isVolatileQualified()) - SubobjType.addVolatile(); - return SubobjType; -} - /// Find the designated sub-object of an rvalue. template<typename SubobjectHandler> typename SubobjectHandler::result_type @@ -2917,78 +2828,31 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj, APValue *O = Obj.Value; QualType ObjType = Obj.Type; const FieldDecl *LastField = nullptr; - const FieldDecl *VolatileField = nullptr; + const bool MayReadMutableMembers = + Obj.LifetimeStartedInEvaluation && Info.getLangOpts().CPlusPlus14; // Walk the designator's path to find the subobject. for (unsigned I = 0, N = Sub.Entries.size(); /**/; ++I) { if (O->isUninit()) { if (!Info.checkingPotentialConstantExpression()) - Info.FFDiag(E, diag::note_constexpr_access_uninit) - << handler.AccessKind; + Info.FFDiag(E, diag::note_constexpr_access_uninit) << handler.AccessKind; return handler.failed(); } - // C++ [class.ctor]p5: - // const and volatile semantics are not applied on an object under - // construction. - if ((ObjType.isConstQualified() || ObjType.isVolatileQualified()) && - ObjType->isRecordType() && - Info.isEvaluatingConstructor( - Obj.Base, llvm::makeArrayRef(Sub.Entries.begin(), - Sub.Entries.begin() + I)) != - ConstructionPhase::None) { - ObjType = Info.Ctx.getCanonicalType(ObjType); - ObjType.removeLocalConst(); - ObjType.removeLocalVolatile(); - } - - // 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() && - handler.AccessKind != AK_MemberCall) { - if (Info.getLangOpts().CPlusPlus) { - int DiagKind; - SourceLocation Loc; - const NamedDecl *Decl = nullptr; - if (VolatileField) { - DiagKind = 2; - Loc = VolatileField->getLocation(); - Decl = VolatileField; - } else if (auto *VD = Obj.Base.dyn_cast<const ValueDecl*>()) { - DiagKind = 1; - Loc = VD->getLocation(); - Decl = VD; - } else { - DiagKind = 0; - if (auto *E = Obj.Base.dyn_cast<const Expr *>()) - Loc = E->getExprLoc(); - } - Info.FFDiag(E, diag::note_constexpr_access_volatile_obj, 1) - << handler.AccessKind << DiagKind << Decl; - Info.Note(Loc, diag::note_constexpr_volatile_here) << DiagKind; - } else { - Info.FFDiag(E, diag::note_invalid_subexpr_in_const_expr); - } - return handler.failed(); - } - + if (I == N) { // If we are reading an object of class type, there may still be more // things we need to check: if there are any mutable subobjects, we // cannot perform this read. (This only happens when performing a trivial // copy or assignment.) if (ObjType->isRecordType() && handler.AccessKind == AK_Read && - !Obj.mayReadMutableMembers(Info) && - diagnoseUnreadableFields(Info, E, ObjType)) + !MayReadMutableMembers && diagnoseUnreadableFields(Info, E, ObjType)) return handler.failed(); - } - if (I == N) { if (!handler.found(*O, ObjType)) return false; // If we modified a bit-field, truncate it to the right width. - if (isModification(handler.AccessKind) && + if (handler.AccessKind != AK_Read && LastField && LastField->isBitField() && !truncateBitfieldValue(Info, E, *O, LastField)) return false; @@ -3034,8 +2898,10 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj, return handler.failed(); } - ObjType = getSubobjectType( - ObjType, ObjType->castAs<ComplexType>()->getElementType()); + bool WasConstQualified = ObjType.isConstQualified(); + ObjType = ObjType->castAs<ComplexType>()->getElementType(); + if (WasConstQualified) + ObjType.addConst(); assert(I == N - 1 && "extracting subobject of scalar?"); if (O->isComplexInt()) { @@ -3047,8 +2913,11 @@ 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 && - !Obj.mayReadMutableMembers(Info)) { + !MayReadMutableMembers) { Info.FFDiag(E, diag::note_constexpr_ltor_mutable, 1) << Field; Info.Note(Field->getLocation(), diag::note_declared_at); @@ -3069,17 +2938,34 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj, } else O = &O->getStructField(Field->getFieldIndex()); - ObjType = getSubobjectType(ObjType, Field->getType(), Field->isMutable()); + bool WasConstQualified = ObjType.isConstQualified(); + ObjType = Field->getType(); + if (WasConstQualified && !Field->isMutable()) + ObjType.addConst(); + + if (ObjType.isVolatileQualified()) { + if (Info.getLangOpts().CPlusPlus) { + // FIXME: Include a description of the path to the volatile subobject. + Info.FFDiag(E, diag::note_constexpr_access_volatile_obj, 1) + << handler.AccessKind << 2 << Field; + Info.Note(Field->getLocation(), diag::note_declared_at); + } else { + Info.FFDiag(E, diag::note_invalid_subexpr_in_const_expr); + } + return handler.failed(); + } + LastField = Field; - if (Field->getType().isVolatileQualified()) - VolatileField = Field; } else { // Next subobject is a base class. const CXXRecordDecl *Derived = ObjType->getAsCXXRecordDecl(); const CXXRecordDecl *Base = getAsBaseClass(Sub.Entries[I]); O = &O->getStructBase(getBaseIndex(Derived, Base)); - ObjType = getSubobjectType(ObjType, Info.Ctx.getRecordType(Base)); + bool WasConstQualified = ObjType.isConstQualified(); + ObjType = Info.Ctx.getRecordType(Base); + if (WasConstQualified) + ObjType.addConst(); } } } @@ -3260,7 +3146,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 (AK != AK_MemberCall && LValType.isVolatileQualified()) { + if (LValType.isVolatileQualified()) { if (Info.getLangOpts().CPlusPlus) Info.FFDiag(E, diag::note_constexpr_access_volatile_type) << AK << LValType; @@ -3269,16 +3155,10 @@ 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. @@ -3298,29 +3178,37 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, return CompleteObject(); } + // Accesses of volatile-qualified objects are not allowed. + if (BaseType.isVolatileQualified()) { + if (Info.getLangOpts().CPlusPlus) { + Info.FFDiag(E, diag::note_constexpr_access_volatile_obj, 1) + << AK << 1 << VD; + Info.Note(VD->getLocation(), diag::note_declared_at); + } else { + Info.FFDiag(E); + } + return CompleteObject(); + } + // Unless we're looking at a local variable or argument in a constexpr call, // the variable we're reading must be const. if (!Frame) { if (Info.getLangOpts().CPlusPlus14 && - declaresSameEntity( - VD, Info.EvaluatingDecl.dyn_cast<const ValueDecl *>())) { + 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. - } else if (isModification(AK)) { - // All the remaining cases do not permit modification of the object. + } else if (AK != AK_Read) { + // All the remaining cases only permit reading. 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); @@ -3329,8 +3217,6 @@ 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) @@ -3390,8 +3276,6 @@ 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(); @@ -3399,9 +3283,8 @@ 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(); } @@ -3409,6 +3292,29 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, BaseVal = Frame->getTemporary(Base, LVal.Base.getVersion()); assert(BaseVal && "missing value for temporary"); } + + // Volatile temporary objects cannot be accessed in constant expressions. + if (BaseType.isVolatileQualified()) { + if (Info.getLangOpts().CPlusPlus) { + Info.FFDiag(E, diag::note_constexpr_access_volatile_obj, 1) + << AK << 0; + Info.Note(Base->getExprLoc(), diag::note_constexpr_temporary_here); + } else { + Info.FFDiag(E); + } + return CompleteObject(); + } + } + + // During the construction of an object, it is not yet 'const'. + // FIXME: This doesn't do quite the right thing for const subobjects of the + // object under construction. + if (Info.isEvaluatingConstructor(LVal.getLValueBase(), + LVal.getLValueCallIndex(), + LVal.getLValueVersion())) { + BaseType = Info.Ctx.getCanonicalType(BaseType); + BaseType.removeLocalConst(); + LifetimeStartedInEvaluation = true; } // In C++14, we can't safely access any mutable state when we might be @@ -3418,10 +3324,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) || - (isModification(AK) && Depth < Info.SpeculativeEvaluationDepth)) + (AK != AK_Read && Depth < Info.SpeculativeEvaluationDepth)) return CompleteObject(); - return CompleteObject(LVal.getLValueBase(), BaseVal, BaseType); + return CompleteObject(BaseVal, BaseType, LifetimeStartedInEvaluation); } /// Perform an lvalue-to-rvalue conversion on the given glvalue. This @@ -3455,7 +3361,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()); + CompleteObject LitObj(&Lit, Base->getType(), false); 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 @@ -4501,48 +4407,6 @@ 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) { @@ -4656,9 +4520,8 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This, } EvalInfo::EvaluatingConstructorRAII EvalObj( - Info, - ObjectUnderConstruction{This.getLValueBase(), This.Designator.Entries}, - RD->getNumBases()); + Info, {This.getLValueBase(), + {This.getLValueCallIndex(), This.getLValueVersion()}}); CallStackFrame Frame(Info, CallLoc, Definition, &This, ArgValues); // FIXME: Creating an APValue just to hold a nonexistent return value is @@ -4732,11 +4595,6 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This, BaseType->getAsCXXRecordDecl(), &Layout)) return false; Value = &Result.getStructBase(BasesSeen++); - - // This is the point at which the dynamic type of the object becomes this - // class type. - if (BasesSeen == RD->getNumBases()) - EvalObj.finishedConstructingBases(); } else if ((FD = I->getMember())) { if (!HandleLValueMember(Info, I->getInit(), Subobject, FD, &Layout)) return false; @@ -5127,7 +4985,7 @@ public: } else return Error(E); - if (This && !checkMemberCallThisPointer(Info, E, *This)) + if (This && !This->checkSubobject(Info, E, CSK_This)) return false; const FunctionDecl *Definition = nullptr; @@ -5163,8 +5021,6 @@ public: /// A member expression where the object is a prvalue is itself a prvalue. bool VisitMemberExpr(const MemberExpr *E) { - assert(!Info.Ctx.getLangOpts().CPlusPlus11 && - "missing temporary materialization conversion"); assert(!E->isArrow() && "missing call to bound member function?"); APValue Val; @@ -5179,10 +5035,7 @@ public: assert(BaseTy->castAs<RecordType>()->getDecl()->getCanonicalDecl() == FD->getParent()->getCanonicalDecl() && "record / field mismatch"); - // 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); + CompleteObject Obj(&Val, BaseTy, true); SubobjectDesignator Designator(BaseTy); Designator.addDeclUnchecked(FD); @@ -6776,12 +6629,6 @@ bool RecordExprEvaluator::VisitInitListExpr(const InitListExpr *E) { const RecordDecl *RD = E->getType()->castAs<RecordType>()->getDecl(); if (RD->isInvalidDecl()) return false; const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(RD); - auto *CXXRD = dyn_cast<CXXRecordDecl>(RD); - - EvalInfo::EvaluatingConstructorRAII EvalObj( - Info, - ObjectUnderConstruction{This.getLValueBase(), This.Designator.Entries}, - CXXRD && CXXRD->getNumBases()); if (RD->isUnion()) { const FieldDecl *Field = E->getInitializedFieldInUnion(); @@ -6808,6 +6655,7 @@ bool RecordExprEvaluator::VisitInitListExpr(const InitListExpr *E) { return EvaluateInPlace(Result.getUnionValue(), Info, Subobject, InitExpr); } + auto *CXXRD = dyn_cast<CXXRecordDecl>(RD); if (Result.isUninit()) Result = APValue(APValue::UninitStruct(), CXXRD ? CXXRD->getNumBases() : 0, std::distance(RD->field_begin(), RD->field_end())); @@ -6815,7 +6663,7 @@ bool RecordExprEvaluator::VisitInitListExpr(const InitListExpr *E) { bool Success = true; // Initialize base classes. - if (CXXRD && CXXRD->getNumBases()) { + if (CXXRD) { for (const auto &Base : CXXRD->bases()) { assert(ElementNo < E->getNumInits() && "missing init for base class"); const Expr *Init = E->getInit(ElementNo); @@ -6832,8 +6680,6 @@ bool RecordExprEvaluator::VisitInitListExpr(const InitListExpr *E) { } ++ElementNo; } - - EvalObj.finishedConstructingBases(); } // Initialize members. diff --git a/clang/test/CXX/expr/expr.const/p2-0x.cpp b/clang/test/CXX/expr/expr.const/p2-0x.cpp index 2496845182f..4daed23bf9c 100644 --- a/clang/test/CXX/expr/expr.const/p2-0x.cpp +++ b/clang/test/CXX/expr/expr.const/p2-0x.cpp @@ -210,8 +210,8 @@ namespace UndefinedBehavior { constexpr int f() const { return 0; } } constexpr c = C(); constexpr int k1 = c.f(); // ok - constexpr int k2 = ((C*)nullptr)->f(); // expected-error {{constant expression}} expected-note {{member call on dereferenced null pointer}} - constexpr int k3 = (&c)[1].f(); // expected-error {{constant expression}} expected-note {{member call on dereferenced one-past-the-end pointer}} + constexpr int k2 = ((C*)nullptr)->f(); // expected-error {{constant expression}} expected-note {{cannot call member function on null pointer}} + constexpr int k3 = (&c)[1].f(); // expected-error {{constant expression}} expected-note {{cannot call member function on pointer past the end of object}} C c2; constexpr int k4 = c2.f(); // ok! diff --git a/clang/test/SemaCXX/constant-expression-cxx11.cpp b/clang/test/SemaCXX/constant-expression-cxx11.cpp index c136b4d2693..6af43854b52 100644 --- a/clang/test/SemaCXX/constant-expression-cxx11.cpp +++ b/clang/test/SemaCXX/constant-expression-cxx11.cpp @@ -192,25 +192,6 @@ namespace StaticMemberFunction { constexpr int (*sf1)(int) = &S::f; constexpr int (*sf2)(int) = &s.f; constexpr const int *sk = &s.k; - - // Note, out_of_lifetime returns an invalid pointer value, but we don't do - // anything with it (other than copy it around), so there's no UB there. - constexpr S *out_of_lifetime(S s) { return &s; } // expected-warning {{address of stack}} - static_assert(out_of_lifetime({})->k == 42, ""); - static_assert(out_of_lifetime({})->f(3) == 128, ""); - - // Similarly, using an inactive union member breaks no rules. - union U { - int n; - S s; - }; - constexpr U u = {0}; - static_assert(u.s.k == 42, ""); - static_assert(u.s.f(1) == 44, ""); - - // And likewise for a past-the-end pointer. - static_assert((&s)[1].k == 42, ""); - static_assert((&s)[1].f(1) == 44, ""); } namespace ParameterScopes { @@ -1748,10 +1729,19 @@ namespace PR14203 { constexpr duration() {} constexpr operator int() const { return 0; } }; - // These are valid per P0859R0 (moved as DR). template<typename T> void f() { + // If we want to evaluate this at the point of the template definition, we + // need to trigger the implicit definition of the move constructor at that + // point. + // FIXME: C++ does not permit us to implicitly define it at the appropriate + // times, since it is only allowed to be implicitly defined when it is + // odr-used. constexpr duration d = duration(); } + // FIXME: It's unclear whether this is valid. On the one hand, we're not + // allowed to generate a move constructor. On the other hand, if we did, + // this would be a constant expression. For now, we generate a move + // constructor here. int n = sizeof(short{duration(duration())}); } @@ -1912,52 +1902,6 @@ namespace Lifetime { }; constexpr int k1 = S().t; // expected-error {{constant expression}} expected-note {{in call}} constexpr int k2 = S(0).t; // expected-error {{constant expression}} expected-note {{in call}} - - struct Q { - int n = 0; - constexpr int f() const { return 0; } - }; - constexpr Q *out_of_lifetime(Q q) { return &q; } // expected-warning {{address of stack}} expected-note 2{{declared here}} - constexpr int k3 = out_of_lifetime({})->n; // expected-error {{constant expression}} expected-note {{read of variable whose lifetime has ended}} - constexpr int k4 = out_of_lifetime({})->f(); // expected-error {{constant expression}} expected-note {{member call on variable whose lifetime has ended}} - - constexpr int null = ((Q*)nullptr)->f(); // expected-error {{constant expression}} expected-note {{member call on dereferenced null pointer}} - - Q q; - Q qa[3]; - constexpr int pte0 = (&q)[0].f(); // ok - constexpr int pte1 = (&q)[1].f(); // expected-error {{constant expression}} expected-note {{member call on dereferenced one-past-the-end pointer}} - constexpr int pte2 = qa[2].f(); // ok - constexpr int pte3 = qa[3].f(); // expected-error {{constant expression}} expected-note {{member call on dereferenced one-past-the-end pointer}} - - constexpr Q cq; - constexpr Q cqa[3]; - constexpr int cpte0 = (&cq)[0].f(); // ok - constexpr int cpte1 = (&cq)[1].f(); // expected-error {{constant expression}} expected-note {{member call on dereferenced one-past-the-end pointer}} - constexpr int cpte2 = cqa[2].f(); // ok - constexpr int cpte3 = cqa[3].f(); // expected-error {{constant expression}} expected-note {{member call on dereferenced one-past-the-end pointer}} - - // FIXME: There's no way if we can tell if the first call here is valid; it - // depends on the active union member. Should we reject for that reason? - union U { - int n; - Q q; - }; - U u1 = {0}; - constexpr U u2 = {0}; - constexpr int union_member1 = u1.q.f(); - constexpr int union_member2 = u2.q.f(); // expected-error {{constant expression}} expected-note {{member call on member 'q' of union with active member 'n'}} - - struct R { // expected-note {{field init}} - struct Inner { constexpr int f() const { return 0; } }; - int a = b.f(); // expected-warning {{uninitialized}} expected-note {{member call on object outside its lifetime}} - Inner b; - }; - // FIXME: This should be rejected under DR2026. - constexpr R r; // expected-note {{default constructor}} - void rf() { - constexpr R r; // expected-error {{constant expression}} expected-note {{in call}} - } } namespace Bitfields { diff --git a/clang/test/SemaCXX/constant-expression-cxx1y.cpp b/clang/test/SemaCXX/constant-expression-cxx1y.cpp index fe69d105023..07ef7974d11 100644 --- a/clang/test/SemaCXX/constant-expression-cxx1y.cpp +++ b/clang/test/SemaCXX/constant-expression-cxx1y.cpp @@ -1159,49 +1159,3 @@ enum InEnum2 : int { enum class InEnum3 { THREE = indirect_builtin_constant_p("abc") }; - -// [class.ctor]p4: -// A constructor can be invoked for a const, volatile or const volatile -// object. const and volatile semantics are not applied on an object under -// construction. They come into effect when the constructor for the most -// derived object ends. -namespace ObjectsUnderConstruction { - struct A { - int n; - constexpr A() : n(1) { n = 2; } - }; - struct B { - const A a; - constexpr B(bool mutate) { - if (mutate) - const_cast<A &>(a).n = 3; // expected-note {{modification of object of const-qualified type 'const int'}} - } - }; - constexpr B b(false); - static_assert(b.a.n == 2, ""); - constexpr B bad(true); // expected-error {{must be initialized by a constant expression}} expected-note {{in call to 'B(true)'}} - - struct C { - int n; - constexpr C() : n(1) { n = 2; } - }; - constexpr int f(bool get) { - volatile C c; // expected-note {{here}} - return get ? const_cast<int&>(c.n) : 0; // expected-note {{read of volatile object 'c'}} - } - static_assert(f(false) == 0, ""); // ok, can modify volatile c.n during c's initialization: it's not volatile then - static_assert(f(true) == 2, ""); // expected-error {{constant}} expected-note {{in call}} - - struct Aggregate { - int x = 0; - int y = ++x; - }; - constexpr Aggregate aggr1; - static_assert(aggr1.x == 1 && aggr1.y == 1, ""); - // FIXME: This is not specified by the standard, but sanity requires it. - constexpr Aggregate aggr2 = {}; - static_assert(aggr2.x == 1 && aggr2.y == 1, ""); - - // The lifetime of 'n' begins at the initialization, not before. - constexpr int n = ++const_cast<int&>(n); // expected-error {{constant expression}} expected-note {{modification}} -} |