PR16377: Allow evaluation of statement expressions in constant evaluation,

why not. Apparently GCC supports this.

llvm-svn: 184396
This commit is contained in:
Richard Smith 2013-06-20 03:00:05 +00:00
parent f032001b6e
commit 51f0317e52
4 changed files with 97 additions and 3 deletions

View File

@ -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)">;

View File

@ -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<Expr>(*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);

View File

@ -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;
});
}
}

View File

@ -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;
}
}