|
@@ -744,6 +744,15 @@ namespace {
|
|
|
/// evaluated, if any.
|
|
|
APValue::LValueBase EvaluatingDecl;
|
|
|
|
|
|
+ enum class EvaluatingDeclKind {
|
|
|
+ None,
|
|
|
+ /// We're evaluating the construction of EvaluatingDecl.
|
|
|
+ Ctor,
|
|
|
+ /// We're evaluating the destruction of EvaluatingDecl.
|
|
|
+ Dtor,
|
|
|
+ };
|
|
|
+ EvaluatingDeclKind IsEvaluatingDecl = EvaluatingDeclKind::None;
|
|
|
+
|
|
|
/// EvaluatingDeclValue - This is the value being constructed for the
|
|
|
/// declaration whose initializer is being evaluated, if any.
|
|
|
APValue *EvaluatingDeclValue;
|
|
@@ -902,8 +911,10 @@ namespace {
|
|
|
discardCleanups();
|
|
|
}
|
|
|
|
|
|
- void setEvaluatingDecl(APValue::LValueBase Base, APValue &Value) {
|
|
|
+ void setEvaluatingDecl(APValue::LValueBase Base, APValue &Value,
|
|
|
+ EvaluatingDeclKind EDK = EvaluatingDeclKind::Ctor) {
|
|
|
EvaluatingDecl = Base;
|
|
|
+ IsEvaluatingDecl = EDK;
|
|
|
EvaluatingDeclValue = &Value;
|
|
|
}
|
|
|
|
|
@@ -2913,8 +2924,8 @@ static bool isReadByLvalueToRvalueConversion(QualType T) {
|
|
|
|
|
|
/// Diagnose an attempt to read from any unreadable field within the specified
|
|
|
/// type, which might be a class type.
|
|
|
-static bool diagnoseUnreadableFields(EvalInfo &Info, const Expr *E,
|
|
|
- QualType T) {
|
|
|
+static bool diagnoseMutableFields(EvalInfo &Info, const Expr *E, AccessKinds AK,
|
|
|
+ QualType T) {
|
|
|
CXXRecordDecl *RD = T->getBaseElementTypeUnsafe()->getAsCXXRecordDecl();
|
|
|
if (!RD)
|
|
|
return false;
|
|
@@ -2929,17 +2940,17 @@ static bool diagnoseUnreadableFields(EvalInfo &Info, const Expr *E,
|
|
|
// FIXME: Add core issue number for the union case.
|
|
|
if (Field->isMutable() &&
|
|
|
(RD->isUnion() || isReadByLvalueToRvalueConversion(Field->getType()))) {
|
|
|
- Info.FFDiag(E, diag::note_constexpr_ltor_mutable, 1) << Field;
|
|
|
+ Info.FFDiag(E, diag::note_constexpr_access_mutable, 1) << AK << Field;
|
|
|
Info.Note(Field->getLocation(), diag::note_declared_at);
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
- if (diagnoseUnreadableFields(Info, E, Field->getType()))
|
|
|
+ if (diagnoseMutableFields(Info, E, AK, Field->getType()))
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
for (auto &BaseSpec : RD->bases())
|
|
|
- if (diagnoseUnreadableFields(Info, E, BaseSpec.getType()))
|
|
|
+ if (diagnoseMutableFields(Info, E, AK, BaseSpec.getType()))
|
|
|
return true;
|
|
|
|
|
|
// All mutable fields were empty, and thus not actually read.
|
|
@@ -2947,7 +2958,8 @@ static bool diagnoseUnreadableFields(EvalInfo &Info, const Expr *E,
|
|
|
}
|
|
|
|
|
|
static bool lifetimeStartedInEvaluation(EvalInfo &Info,
|
|
|
- APValue::LValueBase Base) {
|
|
|
+ APValue::LValueBase Base,
|
|
|
+ bool MutableSubobject = false) {
|
|
|
// A temporary we created.
|
|
|
if (Base.getCallIndex())
|
|
|
return true;
|
|
@@ -2956,19 +2968,42 @@ static bool lifetimeStartedInEvaluation(EvalInfo &Info,
|
|
|
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;
|
|
|
+ auto *BaseD = Base.dyn_cast<const ValueDecl*>();
|
|
|
|
|
|
- // 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;
|
|
|
+ switch (Info.IsEvaluatingDecl) {
|
|
|
+ case EvalInfo::EvaluatingDeclKind::None:
|
|
|
+ return false;
|
|
|
|
|
|
- return false;
|
|
|
+ case EvalInfo::EvaluatingDeclKind::Ctor:
|
|
|
+ // The variable whose initializer we're evaluating.
|
|
|
+ if (BaseD)
|
|
|
+ return declaresSameEntity(Evaluating, BaseD);
|
|
|
+
|
|
|
+ // 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))
|
|
|
+ return declaresSameEntity(BaseMTE->getExtendingDecl(), Evaluating);
|
|
|
+ return false;
|
|
|
+
|
|
|
+ case EvalInfo::EvaluatingDeclKind::Dtor:
|
|
|
+ // C++2a [expr.const]p6:
|
|
|
+ // [during constant destruction] the lifetime of a and its non-mutable
|
|
|
+ // subobjects (but not its mutable subobjects) [are] considered to start
|
|
|
+ // within e.
|
|
|
+ //
|
|
|
+ // FIXME: We can meaningfully extend this to cover non-const objects, but
|
|
|
+ // we will need special handling: we should be able to access only
|
|
|
+ // subobjects of such objects that are themselves declared const.
|
|
|
+ if (!BaseD ||
|
|
|
+ !(BaseD->getType().isConstQualified() ||
|
|
|
+ BaseD->getType()->isReferenceType()) ||
|
|
|
+ MutableSubobject)
|
|
|
+ return false;
|
|
|
+ return declaresSameEntity(Evaluating, BaseD);
|
|
|
+ }
|
|
|
+
|
|
|
+ llvm_unreachable("unknown evaluating decl kind");
|
|
|
}
|
|
|
|
|
|
namespace {
|
|
@@ -2986,13 +3021,13 @@ struct CompleteObject {
|
|
|
CompleteObject(APValue::LValueBase Base, APValue *Value, QualType Type)
|
|
|
: Base(Base), Value(Value), Type(Type) {}
|
|
|
|
|
|
- bool mayReadMutableMembers(EvalInfo &Info) const {
|
|
|
+ bool mayAccessMutableMembers(EvalInfo &Info, AccessKinds AK) 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);
|
|
|
+ return lifetimeStartedInEvaluation(Info, Base, /*MutableSubobject*/true);
|
|
|
}
|
|
|
|
|
|
explicit operator bool() const { return !Type.isNull(); }
|
|
@@ -3097,9 +3132,9 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj,
|
|
|
// 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() && isRead(handler.AccessKind) &&
|
|
|
- !Obj.mayReadMutableMembers(Info) &&
|
|
|
- diagnoseUnreadableFields(Info, E, ObjType))
|
|
|
+ if (ObjType->isRecordType() &&
|
|
|
+ !Obj.mayAccessMutableMembers(Info, handler.AccessKind) &&
|
|
|
+ diagnoseMutableFields(Info, E, handler.AccessKind, ObjType))
|
|
|
return handler.failed();
|
|
|
}
|
|
|
|
|
@@ -3167,10 +3202,10 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj,
|
|
|
: O->getComplexFloatReal(), ObjType);
|
|
|
}
|
|
|
} else if (const FieldDecl *Field = getAsField(Sub.Entries[I])) {
|
|
|
- if (Field->isMutable() && isRead(handler.AccessKind) &&
|
|
|
- !Obj.mayReadMutableMembers(Info)) {
|
|
|
- Info.FFDiag(E, diag::note_constexpr_ltor_mutable, 1)
|
|
|
- << Field;
|
|
|
+ if (Field->isMutable() &&
|
|
|
+ !Obj.mayAccessMutableMembers(Info, handler.AccessKind)) {
|
|
|
+ Info.FFDiag(E, diag::note_constexpr_access_mutable, 1)
|
|
|
+ << handler.AccessKind << Field;
|
|
|
Info.Note(Field->getLocation(), diag::note_declared_at);
|
|
|
return handler.failed();
|
|
|
}
|
|
@@ -3427,8 +3462,7 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,
|
|
|
// the variable we're reading must be const.
|
|
|
if (!Frame) {
|
|
|
if (Info.getLangOpts().CPlusPlus14 &&
|
|
|
- declaresSameEntity(
|
|
|
- VD, Info.EvaluatingDecl.dyn_cast<const ValueDecl *>())) {
|
|
|
+ lifetimeStartedInEvaluation(Info, LVal.Base)) {
|
|
|
// 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.
|
|
@@ -3518,11 +3552,14 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,
|
|
|
// int x = ++r;
|
|
|
// constexpr int k = r;
|
|
|
// 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();
|
|
|
+ //
|
|
|
+ // Note that temporaries whose lifetimes began while evaluating a
|
|
|
+ // variable's constructor are not usable while evaluating the
|
|
|
+ // corresponding destructor, not even if they're of const-qualified
|
|
|
+ // types.
|
|
|
if (!(BaseType.isConstQualified() &&
|
|
|
BaseType->isIntegralOrEnumerationType()) &&
|
|
|
- !(VD && VD->getCanonicalDecl() == ED->getCanonicalDecl())) {
|
|
|
+ !lifetimeStartedInEvaluation(Info, LVal.Base)) {
|
|
|
if (!IsAccess)
|
|
|
return CompleteObject(LVal.getLValueBase(), nullptr, BaseType);
|
|
|
Info.FFDiag(E, diag::note_constexpr_access_static_temporary, 1) << AK;
|
|
@@ -13282,6 +13319,41 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx,
|
|
|
CheckMemoryLeaks(Info);
|
|
|
}
|
|
|
|
|
|
+bool VarDecl::evaluateDestruction(
|
|
|
+ SmallVectorImpl<PartialDiagnosticAt> &Notes) const {
|
|
|
+ assert(getEvaluatedValue() && !getEvaluatedValue()->isAbsent() &&
|
|
|
+ "cannot evaluate destruction of non-constant-initialized variable");
|
|
|
+
|
|
|
+ Expr::EvalStatus EStatus;
|
|
|
+ EStatus.Diag = &Notes;
|
|
|
+
|
|
|
+ // Make a copy of the value for the destructor to mutate.
|
|
|
+ APValue DestroyedValue = *getEvaluatedValue();
|
|
|
+
|
|
|
+ EvalInfo Info(getASTContext(), EStatus, EvalInfo::EM_ConstantExpression);
|
|
|
+ Info.setEvaluatingDecl(this, DestroyedValue,
|
|
|
+ EvalInfo::EvaluatingDeclKind::Dtor);
|
|
|
+ Info.InConstantContext = true;
|
|
|
+
|
|
|
+ SourceLocation DeclLoc = getLocation();
|
|
|
+ QualType DeclTy = getType();
|
|
|
+
|
|
|
+ LValue LVal;
|
|
|
+ LVal.set(this);
|
|
|
+
|
|
|
+ // FIXME: Consider storing whether this variable has constant destruction in
|
|
|
+ // the EvaluatedStmt so that CodeGen can query it.
|
|
|
+ if (!HandleDestruction(Info, DeclLoc, LVal.Base, DestroyedValue, DeclTy) ||
|
|
|
+ EStatus.HasSideEffects)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ if (!Info.discardCleanups())
|
|
|
+ llvm_unreachable("Unhandled cleanup; missing full expression marker?");
|
|
|
+
|
|
|
+ ensureEvaluatedStmt()->HasConstantDestruction = true;
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
/// isEvaluatable - Call EvaluateAsRValue to see if this expression can be
|
|
|
/// constant folded, but discard the result.
|
|
|
bool Expr::isEvaluatable(const ASTContext &Ctx, SideEffectsKind SEK) const {
|