From: Richard Smith Date: Wed, 21 Feb 2018 03:38:30 +0000 (+0000) Subject: In C++14 onwards, it is permitted to read mutable members in constant X-Git-Tag: llvmorg-7.0.0-rc1~12368 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=9defb7d6b14df6db702da51ed03a8586fe8c060e;p=platform%2Fupstream%2Fllvm.git In C++14 onwards, it is permitted to read mutable members in constant expressions, if their lifetime began during the evaluation of the expression. This is technically not allowed in C++11, though we could consider permitting it there too, as an extension. llvm-svn: 325663 --- diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index e56569d..6ef6d56 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -2646,10 +2646,13 @@ struct CompleteObject { APValue *Value; /// The type of the complete object. QualType Type; + bool LifetimeStartedInEvaluation; CompleteObject() : Value(nullptr) {} - CompleteObject(APValue *Value, QualType Type) - : Value(Value), Type(Type) { + CompleteObject(APValue *Value, QualType Type, + bool LifetimeStartedInEvaluation) + : Value(Value), Type(Type), + LifetimeStartedInEvaluation(LifetimeStartedInEvaluation) { assert(Value && "missing value for complete object"); } @@ -2679,6 +2682,8 @@ 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; // Walk the designator's path to find the subobject. for (unsigned I = 0, N = Sub.Entries.size(); /**/; ++I) { @@ -2694,7 +2699,7 @@ 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 && - diagnoseUnreadableFields(Info, E, ObjType)) + !MayReadMutableMembers && diagnoseUnreadableFields(Info, E, ObjType)) return handler.failed(); if (!handler.found(*O, ObjType)) @@ -2774,7 +2779,11 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj, : O->getComplexFloatReal(), ObjType); } } else if (const FieldDecl *Field = getAsField(Sub.Entries[I])) { - if (Field->isMutable() && handler.AccessKind == AK_Read) { + // 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) { Info.FFDiag(E, diag::note_constexpr_ltor_mutable, 1) << Field; Info.Note(Field->getLocation(), diag::note_declared_at); @@ -3020,6 +3029,7 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, // 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()) { // In C++98, const, non-volatile integers initialized with ICEs are ICEs. @@ -3131,7 +3141,7 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, // int &&r = 1; // int x = ++r; // constexpr int k = r; - // Therefore we use the C++1y rules in C++11 too. + // Therefore we use the C++14 rules in C++11 too. const ValueDecl *VD = Info.EvaluatingDecl.dyn_cast(); const ValueDecl *ED = MTE->getExtendingDecl(); if (!(BaseType.isConstQualified() && @@ -3144,6 +3154,7 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, BaseVal = Info.Ctx.getMaterializedTemporaryValue(MTE, false); assert(BaseVal && "got reference to unevaluated temporary"); + LifetimeStartedInEvaluation = true; } else { Info.FFDiag(E); return CompleteObject(); @@ -3172,9 +3183,10 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, if (Info.isEvaluatingConstructor(LVal.getLValueBase(), LVal.CallIndex)) { BaseType = Info.Ctx.getCanonicalType(BaseType); BaseType.removeLocalConst(); + LifetimeStartedInEvaluation = true; } - // In C++1y, we can't safely access any mutable state when we might be + // In C++14, we can't safely access any mutable state when we might be // evaluating after an unmodeled side effect. // // FIXME: Not all local state is mutable. Allow local constant subobjects @@ -3184,7 +3196,7 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, (AK != AK_Read && Info.IsSpeculativelyEvaluating)) return CompleteObject(); - return CompleteObject(BaseVal, BaseType); + return CompleteObject(BaseVal, BaseType, LifetimeStartedInEvaluation); } /// \brief Perform an lvalue-to-rvalue conversion on the given glvalue. This @@ -3218,14 +3230,14 @@ static bool handleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv, APValue Lit; if (!Evaluate(Lit, Info, CLE->getInitializer())) return false; - CompleteObject LitObj(&Lit, Base->getType()); + CompleteObject LitObj(&Lit, Base->getType(), false); return extractSubobject(Info, Conv, LitObj, LVal.Designator, RVal); } else if (isa(Base) || isa(Base)) { // We represent a string literal array as an lvalue pointing at the // corresponding expression, rather than building an array of chars. // FIXME: Support ObjCEncodeExpr, MakeStringConstant APValue Str(Base, CharUnits::Zero(), APValue::NoLValuePath(), 0); - CompleteObject StrObj(&Str, Base->getType()); + CompleteObject StrObj(&Str, Base->getType(), false); return extractSubobject(Info, Conv, StrObj, LVal.Designator, RVal); } } @@ -4823,7 +4835,7 @@ public: assert(BaseTy->castAs()->getDecl()->getCanonicalDecl() == FD->getParent()->getCanonicalDecl() && "record / field mismatch"); - CompleteObject Obj(&Val, BaseTy); + CompleteObject Obj(&Val, BaseTy, true); SubobjectDesignator Designator(BaseTy); Designator.addDeclUnchecked(FD); diff --git a/clang/test/SemaCXX/constant-expression-cxx1y.cpp b/clang/test/SemaCXX/constant-expression-cxx1y.cpp index 12fec08..f81988c 100644 --- a/clang/test/SemaCXX/constant-expression-cxx1y.cpp +++ b/clang/test/SemaCXX/constant-expression-cxx1y.cpp @@ -1021,3 +1021,26 @@ constexpr bool evalNested() { } static_assert(evalNested(), ""); } // namespace PR19741 + +namespace Mutable { + struct A { mutable int n; }; // expected-note 2{{here}} + constexpr int k = A{123}.n; // ok + static_assert(k == 123, ""); + + struct Q { A &&a; int b = a.n; }; + constexpr Q q = { A{456} }; // ok + static_assert(q.b == 456, ""); + + constexpr A a = {123}; + constexpr int m = a.n; // expected-error {{constant expression}} expected-note {{mutable}} + + constexpr Q r = { static_cast(const_cast(a)) }; // expected-error {{constant expression}} expected-note@-7 {{mutable}} + + struct B { + mutable int n; // expected-note {{here}} + int m; + constexpr B() : n(1), m(n) {} // ok + }; + constexpr B b; + constexpr int p = b.n; // expected-error {{constant expression}} expected-note {{mutable}} +}