PR16377: Allow evaluation of statement expressions in constant evaluation,
why not. Apparently GCC supports this. llvm-svn: 184396
This commit is contained in:
parent
f032001b6e
commit
51f0317e52
|
@ -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)">;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue