From 37a84f653c196aa0b73fad96707175a62dcb6a12 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Thu, 20 Jun 2013 03:00:05 +0000 Subject: [PATCH] PR16377: Allow evaluation of statement expressions in constant evaluation, why not. Apparently GCC supports this. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@184396 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticASTKinds.td | 5 ++- lib/AST/ExprConstant.cpp | 49 +++++++++++++++++++++- test/SemaCXX/constant-expression-cxx11.cpp | 21 ++++++++++ test/SemaCXX/constant-expression-cxx1y.cpp | 25 +++++++++++ 4 files changed, 97 insertions(+), 3 deletions(-) diff --git a/include/clang/Basic/DiagnosticASTKinds.td b/include/clang/Basic/DiagnosticASTKinds.td index f560069189..919245eccc 100644 --- a/include/clang/Basic/DiagnosticASTKinds.td +++ b/include/clang/Basic/DiagnosticASTKinds.td @@ -39,7 +39,7 @@ def note_constexpr_non_global : Note< "%select{pointer|reference}0 to %select{|subobject of }1" "%select{temporary|%3}2 is not a constant expression">; def note_constexpr_uninitialized : Note< - "subobject of type %0 is not initialized">; + "%select{|sub}0object of type %1 is not initialized">; def note_constexpr_array_index : Note<"cannot refer to element %0 of " "%select{array of %2 elements|non-array object}1 in a constant expression">; def note_constexpr_float_arithmetic : Note< @@ -126,6 +126,9 @@ def note_constexpr_access_static_temporary : Note< def note_constexpr_modify_global : Note< "a constant expression cannot modify an object that is visible outside " "that expression">; +def note_constexpr_stmt_expr_unsupported : Note< + "this use of statement expressions is not supported in a " + "constant expression">; def note_constexpr_calls_suppressed : Note< "(skipping %0 call%s0 in backtrace; use -fconstexpr-backtrace-limit=0 to " "see all)">; diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 72f1c21ee6..67c187c039 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -1133,7 +1133,8 @@ static bool CheckLiteralType(EvalInfo &Info, const Expr *E, static bool CheckConstantExpression(EvalInfo &Info, SourceLocation DiagLoc, QualType Type, const APValue &Value) { if (Value.isUninit()) { - Info.Diag(DiagLoc, diag::note_constexpr_uninitialized) << Type; + Info.Diag(DiagLoc, diag::note_constexpr_uninitialized) + << true << Type; return false; } @@ -2888,6 +2889,13 @@ static bool EvaluateDecl(EvalInfo &Info, const Decl *D) { Result.set(VD, Info.CurrentCall->Index); APValue &Val = Info.CurrentCall->Temporaries[VD]; + if (!VD->getInit()) { + Info.Diag(D->getLocStart(), diag::note_constexpr_uninitialized) + << false << VD->getType(); + Val = APValue(); + return false; + } + if (!EvaluateInPlace(Val, Info, Result, VD->getInit())) { // Wipe out any partially-computed value, to allow tracking that this // evaluation failed. @@ -2972,7 +2980,10 @@ static EvalStmtResult EvaluateSwitch(APValue &Result, EvalInfo &Info, case ESR_Returned: return ESR; case ESR_CaseNotFound: - llvm_unreachable("couldn't find switch case"); + // This can only happen if the switch case is nested within a statement + // expression. We have no intention of supporting that. + Info.Diag(Found->getLocStart(), diag::note_constexpr_stmt_expr_unsupported); + return ESR_Failed; } llvm_unreachable("Invalid EvalStmtResult!"); } @@ -3853,6 +3864,40 @@ public: return DerivedSuccess(RVal, UO); } + RetTy VisitStmtExpr(const StmtExpr *E) { + // We will have checked the full-expressions inside the statement expression + // when they were completed, and don't need to check them again now. + if (Info.getIntOverflowCheckMode()) + return Error(E); + + const CompoundStmt *CS = E->getSubStmt(); + for (CompoundStmt::const_body_iterator BI = CS->body_begin(), + BE = CS->body_end(); + /**/; ++BI) { + if (BI + 1 == BE) { + const Expr *FinalExpr = dyn_cast(*BI); + if (!FinalExpr) { + Info.Diag((*BI)->getLocStart(), + diag::note_constexpr_stmt_expr_unsupported); + return false; + } + return this->Visit(FinalExpr); + } + + APValue ReturnValue; + EvalStmtResult ESR = EvaluateStmt(ReturnValue, Info, *BI); + if (ESR != ESR_Succeeded) { + // FIXME: If the statement-expression terminated due to 'return', + // 'break', or 'continue', it would be nice to propagate that to + // the outer statement evaluation rather than bailing out. + if (ESR != ESR_Failed) + Info.Diag((*BI)->getLocStart(), + diag::note_constexpr_stmt_expr_unsupported); + return false; + } + } + } + /// Visit a value which is evaluated, but whose value is ignored. void VisitIgnoredValue(const Expr *E) { EvaluateIgnoredValue(Info, E); diff --git a/test/SemaCXX/constant-expression-cxx11.cpp b/test/SemaCXX/constant-expression-cxx11.cpp index b92888713e..4e2b7edb78 100644 --- a/test/SemaCXX/constant-expression-cxx11.cpp +++ b/test/SemaCXX/constant-expression-cxx11.cpp @@ -1649,3 +1649,24 @@ namespace InitializerList { } static_assert(sum({1, 2, 3, 4, 5}) == 15, ""); } + +namespace StmtExpr { + struct A { int k; }; + void f() { + static_assert(({ const int x = 5; x * 3; }) == 15, ""); // expected-warning {{extension}} + constexpr auto a = ({ A(); }); // expected-warning {{extension}} + } + constexpr int g(int k) { + return ({ // expected-warning {{extension}} + const int x = k; + x * x; + }); + } + static_assert(g(123) == 15129, ""); + constexpr int h() { // expected-error {{never produces a constant}} + return ({ // expected-warning {{extension}} + return 0; // expected-note {{not supported}} + 1; + }); + } +} diff --git a/test/SemaCXX/constant-expression-cxx1y.cpp b/test/SemaCXX/constant-expression-cxx1y.cpp index af44bf8d74..6043a1fffc 100644 --- a/test/SemaCXX/constant-expression-cxx1y.cpp +++ b/test/SemaCXX/constant-expression-cxx1y.cpp @@ -765,3 +765,28 @@ namespace InitializerList { } static_assert(sum({1, 2, 3, 4, 5}) == 15, ""); } + +namespace StmtExpr { + constexpr int f(int k) { + switch (k) { + case 0: + return 0; + + ({ + case 1: // expected-note {{not supported}} + return 1; + }); + } + } + static_assert(f(1) == 1, ""); // expected-error {{constant expression}} expected-note {{in call}} + + constexpr int g() { // expected-error {{never produces a constant}} + return ({ int n; n; }); // expected-note {{object of type 'int' is not initialized}} + } + + // FIXME: We should handle the void statement expression case. + constexpr int h() { // expected-error {{never produces a constant}} + ({ if (true) {} }); // expected-note {{not supported}} + return 0; + } +} -- 2.40.0