r325663 - In C++14 onwards, it is permitted to read mutable members in constant
Richard Smith via cfe-commits
cfe-commits at lists.llvm.org
Tue Feb 20 19:38:30 PST 2018
Author: rsmith
Date: Tue Feb 20 19:38:30 2018
New Revision: 325663
URL: http://llvm.org/viewvc/llvm-project?rev=325663&view=rev
Log:
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.
Modified:
cfe/trunk/lib/AST/ExprConstant.cpp
cfe/trunk/test/SemaCXX/constant-expression-cxx1y.cpp
Modified: cfe/trunk/lib/AST/ExprConstant.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ExprConstant.cpp?rev=325663&r1=325662&r2=325663&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ExprConstant.cpp (original)
+++ cfe/trunk/lib/AST/ExprConstant.cpp Tue Feb 20 19:38:30 2018
@@ -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
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
// 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
: 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
// 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.
@@ -3131,7 +3141,7 @@ static CompleteObject findCompleteObject
// 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*>();
const ValueDecl *ED = MTE->getExtendingDecl();
if (!(BaseType.isConstQualified() &&
@@ -3144,6 +3154,7 @@ static CompleteObject findCompleteObject
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
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
(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 handleLValueToRValueConversi
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<StringLiteral>(Base) || isa<PredefinedExpr>(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<RecordType>()->getDecl()->getCanonicalDecl() ==
FD->getParent()->getCanonicalDecl() && "record / field mismatch");
- CompleteObject Obj(&Val, BaseTy);
+ CompleteObject Obj(&Val, BaseTy, true);
SubobjectDesignator Designator(BaseTy);
Designator.addDeclUnchecked(FD);
Modified: cfe/trunk/test/SemaCXX/constant-expression-cxx1y.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/constant-expression-cxx1y.cpp?rev=325663&r1=325662&r2=325663&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/constant-expression-cxx1y.cpp (original)
+++ cfe/trunk/test/SemaCXX/constant-expression-cxx1y.cpp Tue Feb 20 19:38:30 2018
@@ -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<A&&>(const_cast<A&>(a)) }; // expected-error {{constant expression}} expected-note at -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}}
+}
More information about the cfe-commits
mailing list