From 9a10e4b7c930cab5f4a8fc6f74e157e44fba807f Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Sun, 29 Sep 2019 05:58:31 +0000 Subject: [PATCH] Fix checking for permitted results of constant expressions. In the presence of mutable state, we need to check whether temporaries involved in a constant expression have permissible values at the end of the overall evaluation, rather than at the end of the evaluation of the initializer of the temporary. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@373160 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/AST/ExprConstant.cpp | 79 +++++++++++++++------- test/SemaCXX/constant-expression-cxx1y.cpp | 19 ++++++ 2 files changed, 73 insertions(+), 25 deletions(-) diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 33608db6e4..b0c0c2e8d9 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -1912,12 +1912,30 @@ static void NoteLValueLocation(EvalInfo &Info, APValue::LValueBase Base) { // We have no information to show for a typeid(T) object. } +enum class CheckEvaluationResultKind { + ConstantExpression, + FullyInitialized, +}; + +/// Materialized temporaries that we've already checked to determine if they're +/// initializsed by a constant expression. +using CheckedTemporaries = + llvm::SmallPtrSet; + +static bool CheckEvaluationResult(CheckEvaluationResultKind CERK, + EvalInfo &Info, SourceLocation DiagLoc, + QualType Type, const APValue &Value, + Expr::ConstExprUsage Usage, + SourceLocation SubobjectLoc, + CheckedTemporaries &CheckedTemps); + /// Check that this reference or pointer core constant expression is a valid /// value for an address or reference constant expression. Return true if we /// can fold this expression, whether or not it's a constant expression. static bool CheckLValueConstantExpression(EvalInfo &Info, SourceLocation Loc, QualType Type, const LValue &LVal, - Expr::ConstExprUsage Usage) { + Expr::ConstExprUsage Usage, + CheckedTemporaries &CheckedTemps) { bool IsReferenceType = Type->isReferenceType(); APValue::LValueBase Base = LVal.getLValueBase(); @@ -1978,6 +1996,16 @@ static bool CheckLValueConstantExpression(EvalInfo &Info, SourceLocation Loc, // FIXME: Diagnostic! return false; } + } else if (const auto *MTE = dyn_cast_or_null( + Base.dyn_cast())) { + if (CheckedTemps.insert(MTE).second) { + APValue *V = Info.Ctx.getMaterializedTemporaryValue(MTE, false); + assert(V && "evasluation result refers to uninitialised temporary"); + if (!CheckEvaluationResult(CheckEvaluationResultKind::ConstantExpression, + Info, MTE->getExprLoc(), getType(Base), *V, + Usage, SourceLocation(), CheckedTemps)) + return false; + } } // Allow address constant expressions to be past-the-end pointers. This is @@ -2050,16 +2078,12 @@ static bool CheckLiteralType(EvalInfo &Info, const Expr *E, return false; } -enum class CheckEvaluationResultKind { - ConstantExpression, - FullyInitialized, -}; - -static bool -CheckEvaluationResult(CheckEvaluationResultKind CERK, EvalInfo &Info, - SourceLocation DiagLoc, QualType Type, - const APValue &Value, Expr::ConstExprUsage Usage, - SourceLocation SubobjectLoc = SourceLocation()) { +static bool CheckEvaluationResult(CheckEvaluationResultKind CERK, + EvalInfo &Info, SourceLocation DiagLoc, + QualType Type, const APValue &Value, + Expr::ConstExprUsage Usage, + SourceLocation SubobjectLoc, + CheckedTemporaries &CheckedTemps) { if (!Value.hasValue()) { Info.FFDiag(DiagLoc, diag::note_constexpr_uninitialized) << true << Type; @@ -2081,18 +2105,20 @@ CheckEvaluationResult(CheckEvaluationResultKind CERK, EvalInfo &Info, for (unsigned I = 0, N = Value.getArrayInitializedElts(); I != N; ++I) { if (!CheckEvaluationResult(CERK, Info, DiagLoc, EltTy, Value.getArrayInitializedElt(I), Usage, - SubobjectLoc)) + SubobjectLoc, CheckedTemps)) return false; } if (!Value.hasArrayFiller()) return true; return CheckEvaluationResult(CERK, Info, DiagLoc, EltTy, - Value.getArrayFiller(), Usage, SubobjectLoc); + Value.getArrayFiller(), Usage, SubobjectLoc, + CheckedTemps); } if (Value.isUnion() && Value.getUnionField()) { return CheckEvaluationResult( CERK, Info, DiagLoc, Value.getUnionField()->getType(), - Value.getUnionValue(), Usage, Value.getUnionField()->getLocation()); + Value.getUnionValue(), Usage, Value.getUnionField()->getLocation(), + CheckedTemps); } if (Value.isStruct()) { RecordDecl *RD = Type->castAs()->getDecl(); @@ -2101,7 +2127,7 @@ CheckEvaluationResult(CheckEvaluationResultKind CERK, EvalInfo &Info, for (const CXXBaseSpecifier &BS : CD->bases()) { if (!CheckEvaluationResult(CERK, Info, DiagLoc, BS.getType(), Value.getStructBase(BaseIndex), Usage, - BS.getBeginLoc())) + BS.getBeginLoc(), CheckedTemps)) return false; ++BaseIndex; } @@ -2112,7 +2138,7 @@ CheckEvaluationResult(CheckEvaluationResultKind CERK, EvalInfo &Info, if (!CheckEvaluationResult(CERK, Info, DiagLoc, I->getType(), Value.getStructField(I->getFieldIndex()), - Usage, I->getLocation())) + Usage, I->getLocation(), CheckedTemps)) return false; } } @@ -2121,7 +2147,8 @@ CheckEvaluationResult(CheckEvaluationResultKind CERK, EvalInfo &Info, CERK == CheckEvaluationResultKind::ConstantExpression) { LValue LVal; LVal.setFrom(Info.Ctx, Value); - return CheckLValueConstantExpression(Info, DiagLoc, Type, LVal, Usage); + return CheckLValueConstantExpression(Info, DiagLoc, Type, LVal, Usage, + CheckedTemps); } if (Value.isMemberPointer() && @@ -2139,17 +2166,20 @@ static bool CheckConstantExpression(EvalInfo &Info, SourceLocation DiagLoc, QualType Type, const APValue &Value, Expr::ConstExprUsage Usage = Expr::EvaluateForCodeGen) { + CheckedTemporaries CheckedTemps; return CheckEvaluationResult(CheckEvaluationResultKind::ConstantExpression, - Info, DiagLoc, Type, Value, Usage); + Info, DiagLoc, Type, Value, Usage, + SourceLocation(), CheckedTemps); } /// Check that this evaluated value is fully-initialized and can be loaded by /// an lvalue-to-rvalue conversion. static bool CheckFullyInitialized(EvalInfo &Info, SourceLocation DiagLoc, QualType Type, const APValue &Value) { - return CheckEvaluationResult(CheckEvaluationResultKind::FullyInitialized, - Info, DiagLoc, Type, Value, - Expr::EvaluateForCodeGen); + CheckedTemporaries CheckedTemps; + return CheckEvaluationResult( + CheckEvaluationResultKind::FullyInitialized, Info, DiagLoc, Type, Value, + Expr::EvaluateForCodeGen, SourceLocation(), CheckedTemps); } /// Enforce C++2a [expr.const]/4.17, which disallows new-expressions unless @@ -7189,9 +7219,7 @@ bool LValueExprEvaluator::VisitMaterializeTemporaryExpr( QualType Type = Inner->getType(); // Materialize the temporary itself. - if (!EvaluateInPlace(*Value, Info, Result, Inner) || - (E->getStorageDuration() == SD_Static && - !CheckConstantExpression(Info, E->getExprLoc(), Type, *Value))) { + if (!EvaluateInPlace(*Value, Info, Result, Inner)) { *Value = APValue(); return false; } @@ -13218,11 +13246,12 @@ bool Expr::EvaluateAsLValue(EvalResult &Result, const ASTContext &Ctx, EvalInfo Info(Ctx, Result, EvalInfo::EM_ConstantFold); Info.InConstantContext = InConstantContext; LValue LV; + CheckedTemporaries CheckedTemps; if (!EvaluateLValue(this, LV, Info) || !Info.discardCleanups() || Result.HasSideEffects || !CheckLValueConstantExpression(Info, getExprLoc(), Ctx.getLValueReferenceType(getType()), LV, - Expr::EvaluateForCodeGen)) + Expr::EvaluateForCodeGen, CheckedTemps)) return false; LV.moveInto(Result.Val); diff --git a/test/SemaCXX/constant-expression-cxx1y.cpp b/test/SemaCXX/constant-expression-cxx1y.cpp index 1d4549b618..2a8304ebda 100644 --- a/test/SemaCXX/constant-expression-cxx1y.cpp +++ b/test/SemaCXX/constant-expression-cxx1y.cpp @@ -1223,3 +1223,22 @@ namespace PR39728 { ~Comment1() = default; }; } + +namespace TemporaryWithBadPointer { + constexpr int *get_bad_pointer() { + int n = 0; // expected-note 2{{here}} + return &n; // expected-warning {{stack}} + } + constexpr int *bad_pointer = get_bad_pointer(); // expected-error {{constant expression}} expected-note {{pointer to 'n' is not a constant expression}} + + struct DoBadThings { int *&℘ int n; }; + constexpr DoBadThings dbt = { // expected-error {{constant expression}} + nullptr, // expected-note {{pointer to 'n' is not a constant expression}} + (dbt.wp = get_bad_pointer(), 0) + }; + + constexpr DoBadThings dbt2 = { // ok + get_bad_pointer(), + (dbt2.wp = nullptr, 0) + }; +} -- 2.40.0