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;
|
APValue *Value;
|
||||||
/// The type of the complete object.
|
/// The type of the complete object.
|
||||||
QualType Type;
|
QualType Type;
|
||||||
|
bool LifetimeStartedInEvaluation;
|
||||||
|
|
||||||
CompleteObject() : Value(nullptr) {}
|
CompleteObject() : Value(nullptr) {}
|
||||||
CompleteObject(APValue *Value, QualType Type)
|
CompleteObject(APValue *Value, QualType Type,
|
||||||
: Value(Value), Type(Type) {
|
bool LifetimeStartedInEvaluation)
|
||||||
|
: Value(Value), Type(Type),
|
||||||
|
LifetimeStartedInEvaluation(LifetimeStartedInEvaluation) {
|
||||||
assert(Value && "missing value for complete object");
|
assert(Value && "missing value for complete object");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2679,6 +2682,8 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj,
|
||||||
APValue *O = Obj.Value;
|
APValue *O = Obj.Value;
|
||||||
QualType ObjType = Obj.Type;
|
QualType ObjType = Obj.Type;
|
||||||
const FieldDecl *LastField = nullptr;
|
const FieldDecl *LastField = nullptr;
|
||||||
|
const bool MayReadMutableMembers =
|
||||||
|
Obj.LifetimeStartedInEvaluation && Info.getLangOpts().CPlusPlus14;
|
||||||
|
|
||||||
// Walk the designator's path to find the subobject.
|
// Walk the designator's path to find the subobject.
|
||||||
for (unsigned I = 0, N = Sub.Entries.size(); /**/; ++I) {
|
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
|
// cannot perform this read. (This only happens when performing a trivial
|
||||||
// copy or assignment.)
|
// copy or assignment.)
|
||||||
if (ObjType->isRecordType() && handler.AccessKind == AK_Read &&
|
if (ObjType->isRecordType() && handler.AccessKind == AK_Read &&
|
||||||
diagnoseUnreadableFields(Info, E, ObjType))
|
!MayReadMutableMembers && diagnoseUnreadableFields(Info, E, ObjType))
|
||||||
return handler.failed();
|
return handler.failed();
|
||||||
|
|
||||||
if (!handler.found(*O, ObjType))
|
if (!handler.found(*O, ObjType))
|
||||||
|
@ -2774,7 +2779,11 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj,
|
||||||
: O->getComplexFloatReal(), ObjType);
|
: O->getComplexFloatReal(), ObjType);
|
||||||
}
|
}
|
||||||
} else if (const FieldDecl *Field = getAsField(Sub.Entries[I])) {
|
} 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)
|
Info.FFDiag(E, diag::note_constexpr_ltor_mutable, 1)
|
||||||
<< Field;
|
<< Field;
|
||||||
Info.Note(Field->getLocation(), diag::note_declared_at);
|
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.
|
// Compute value storage location and type of base object.
|
||||||
APValue *BaseVal = nullptr;
|
APValue *BaseVal = nullptr;
|
||||||
QualType BaseType = getType(LVal.Base);
|
QualType BaseType = getType(LVal.Base);
|
||||||
|
bool LifetimeStartedInEvaluation = Frame;
|
||||||
|
|
||||||
if (const ValueDecl *D = LVal.Base.dyn_cast<const ValueDecl*>()) {
|
if (const ValueDecl *D = LVal.Base.dyn_cast<const ValueDecl*>()) {
|
||||||
// In C++98, const, non-volatile integers initialized with ICEs are ICEs.
|
// 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 &&r = 1;
|
||||||
// int x = ++r;
|
// int x = ++r;
|
||||||
// constexpr int k = 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 *VD = Info.EvaluatingDecl.dyn_cast<const ValueDecl*>();
|
||||||
const ValueDecl *ED = MTE->getExtendingDecl();
|
const ValueDecl *ED = MTE->getExtendingDecl();
|
||||||
if (!(BaseType.isConstQualified() &&
|
if (!(BaseType.isConstQualified() &&
|
||||||
|
@ -3144,6 +3154,7 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,
|
||||||
|
|
||||||
BaseVal = Info.Ctx.getMaterializedTemporaryValue(MTE, false);
|
BaseVal = Info.Ctx.getMaterializedTemporaryValue(MTE, false);
|
||||||
assert(BaseVal && "got reference to unevaluated temporary");
|
assert(BaseVal && "got reference to unevaluated temporary");
|
||||||
|
LifetimeStartedInEvaluation = true;
|
||||||
} else {
|
} else {
|
||||||
Info.FFDiag(E);
|
Info.FFDiag(E);
|
||||||
return CompleteObject();
|
return CompleteObject();
|
||||||
|
@ -3172,9 +3183,10 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,
|
||||||
if (Info.isEvaluatingConstructor(LVal.getLValueBase(), LVal.CallIndex)) {
|
if (Info.isEvaluatingConstructor(LVal.getLValueBase(), LVal.CallIndex)) {
|
||||||
BaseType = Info.Ctx.getCanonicalType(BaseType);
|
BaseType = Info.Ctx.getCanonicalType(BaseType);
|
||||||
BaseType.removeLocalConst();
|
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.
|
// evaluating after an unmodeled side effect.
|
||||||
//
|
//
|
||||||
// FIXME: Not all local state is mutable. Allow local constant subobjects
|
// 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))
|
(AK != AK_Read && Info.IsSpeculativelyEvaluating))
|
||||||
return CompleteObject();
|
return CompleteObject();
|
||||||
|
|
||||||
return CompleteObject(BaseVal, BaseType);
|
return CompleteObject(BaseVal, BaseType, LifetimeStartedInEvaluation);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \brief Perform an lvalue-to-rvalue conversion on the given glvalue. This
|
/// \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;
|
APValue Lit;
|
||||||
if (!Evaluate(Lit, Info, CLE->getInitializer()))
|
if (!Evaluate(Lit, Info, CLE->getInitializer()))
|
||||||
return false;
|
return false;
|
||||||
CompleteObject LitObj(&Lit, Base->getType());
|
CompleteObject LitObj(&Lit, Base->getType(), false);
|
||||||
return extractSubobject(Info, Conv, LitObj, LVal.Designator, RVal);
|
return extractSubobject(Info, Conv, LitObj, LVal.Designator, RVal);
|
||||||
} else if (isa<StringLiteral>(Base) || isa<PredefinedExpr>(Base)) {
|
} else if (isa<StringLiteral>(Base) || isa<PredefinedExpr>(Base)) {
|
||||||
// We represent a string literal array as an lvalue pointing at the
|
// We represent a string literal array as an lvalue pointing at the
|
||||||
// corresponding expression, rather than building an array of chars.
|
// corresponding expression, rather than building an array of chars.
|
||||||
// FIXME: Support ObjCEncodeExpr, MakeStringConstant
|
// FIXME: Support ObjCEncodeExpr, MakeStringConstant
|
||||||
APValue Str(Base, CharUnits::Zero(), APValue::NoLValuePath(), 0);
|
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);
|
return extractSubobject(Info, Conv, StrObj, LVal.Designator, RVal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4823,7 +4835,7 @@ public:
|
||||||
assert(BaseTy->castAs<RecordType>()->getDecl()->getCanonicalDecl() ==
|
assert(BaseTy->castAs<RecordType>()->getDecl()->getCanonicalDecl() ==
|
||||||
FD->getParent()->getCanonicalDecl() && "record / field mismatch");
|
FD->getParent()->getCanonicalDecl() && "record / field mismatch");
|
||||||
|
|
||||||
CompleteObject Obj(&Val, BaseTy);
|
CompleteObject Obj(&Val, BaseTy, true);
|
||||||
SubobjectDesignator Designator(BaseTy);
|
SubobjectDesignator Designator(BaseTy);
|
||||||
Designator.addDeclUnchecked(FD);
|
Designator.addDeclUnchecked(FD);
|
||||||
|
|
||||||
|
|
|
@ -1021,3 +1021,26 @@ constexpr bool evalNested() {
|
||||||
}
|
}
|
||||||
static_assert(evalNested(), "");
|
static_assert(evalNested(), "");
|
||||||
} // namespace PR19741
|
} // 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