forked from OSchip/llvm-project
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
This commit is contained in:
parent
63a1098d73
commit
9defb7d6b1
|
@ -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<const ValueDecl*>()) {
|
||||
// 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*>();
|
||||
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<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);
|
||||
|
||||
|
|
|
@ -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@-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}}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue