More constant evaluation cleanup, and fix an issue where we'd override an

earlier 'non-constant' diagnostic with a later one if the earlier one was from
a side-effect we thought we could evaluate past.

llvm-svn: 194117
This commit is contained in:
Richard Smith 2013-11-06 02:19:10 +00:00
parent 34e2f0c4ea
commit 4e66f1faa1
2 changed files with 28 additions and 25 deletions

View File

@ -564,15 +564,17 @@ namespace {
// EM_ConstantFold mode. // EM_ConstantFold mode.
if (!EvalStatus.Diag->empty()) { if (!EvalStatus.Diag->empty()) {
switch (EvalMode) { switch (EvalMode) {
case EM_ConstantFold:
case EM_IgnoreSideEffects:
case EM_EvaluateForOverflow:
if (!EvalStatus.HasSideEffects)
break;
// We've had side-effects; we want the diagnostic from them, not
// some later problem.
case EM_ConstantExpression: case EM_ConstantExpression:
case EM_PotentialConstantExpression: case EM_PotentialConstantExpression:
HasActiveDiagnostic = false; HasActiveDiagnostic = false;
return OptionalDiagnostic(); return OptionalDiagnostic();
case EM_ConstantFold:
case EM_IgnoreSideEffects:
case EM_EvaluateForOverflow:
break;
} }
} }
@ -641,11 +643,11 @@ namespace {
/// couldn't model? /// couldn't model?
bool keepEvaluatingAfterSideEffect() { bool keepEvaluatingAfterSideEffect() {
switch (EvalMode) { switch (EvalMode) {
case EM_PotentialConstantExpression:
case EM_EvaluateForOverflow: case EM_EvaluateForOverflow:
case EM_IgnoreSideEffects: case EM_IgnoreSideEffects:
return true; return true;
case EM_PotentialConstantExpression:
case EM_ConstantExpression: case EM_ConstantExpression:
case EM_ConstantFold: case EM_ConstantFold:
return false; return false;
@ -1122,11 +1124,9 @@ static void describeCall(CallStackFrame *Frame, raw_ostream &Out) {
/// \return \c true if the caller should keep evaluating. /// \return \c true if the caller should keep evaluating.
static bool EvaluateIgnoredValue(EvalInfo &Info, const Expr *E) { static bool EvaluateIgnoredValue(EvalInfo &Info, const Expr *E) {
APValue Scratch; APValue Scratch;
if (!Evaluate(Scratch, Info, E)) { if (!Evaluate(Scratch, Info, E))
Info.EvalStatus.HasSideEffects = true; // We don't need the value, but we might have skipped a side effect here.
return Info.keepEvaluatingAfterFailure(); return Info.noteSideEffect();
// FIXME: return Info.noteSideEffect();
}
return true; return true;
} }
@ -6336,23 +6336,26 @@ bool DataRecursiveIntBinOpEvaluator::
if (E->getOpcode() == BO_Comma) { if (E->getOpcode() == BO_Comma) {
// Ignore LHS but note if we could not evaluate it. // Ignore LHS but note if we could not evaluate it.
if (LHSResult.Failed) if (LHSResult.Failed)
Info.EvalStatus.HasSideEffects = true; return Info.noteSideEffect();
return true; return true;
} }
if (E->isLogicalOp()) { if (E->isLogicalOp()) {
bool lhsResult; bool LHSAsBool;
if (HandleConversionToBool(LHSResult.Val, lhsResult)) { if (!LHSResult.Failed && HandleConversionToBool(LHSResult.Val, LHSAsBool)) {
// We were able to evaluate the LHS, see if we can get away with not // We were able to evaluate the LHS, see if we can get away with not
// evaluating the RHS: 0 && X -> 0, 1 || X -> 1 // evaluating the RHS: 0 && X -> 0, 1 || X -> 1
if (lhsResult == (E->getOpcode() == BO_LOr)) { if (LHSAsBool == (E->getOpcode() == BO_LOr)) {
Success(lhsResult, E, LHSResult.Val); Success(LHSAsBool, E, LHSResult.Val);
return false; // Ignore RHS return false; // Ignore RHS
} }
} else { } else {
LHSResult.Failed = true;
// Since we weren't able to evaluate the left hand side, it // Since we weren't able to evaluate the left hand side, it
// must have had side effects. // must have had side effects.
Info.EvalStatus.HasSideEffects = true; if (!Info.noteSideEffect())
return false;
// We can't evaluate the LHS; however, sometimes the result // We can't evaluate the LHS; however, sometimes the result
// is determined by the RHS: X && 0 -> 0, X || 1 -> 1. // is determined by the RHS: X && 0 -> 0, X || 1 -> 1.

View File

@ -128,10 +128,10 @@ constexpr int namespace_alias() {
namespace assign { namespace assign {
constexpr int a = 0; constexpr int a = 0;
const int b = 0; const int b = 0;
int c = 0; // expected-note 2{{here}} int c = 0; // expected-note {{here}}
constexpr void set(const int &a, int b) { constexpr void set(const int &a, int b) {
const_cast<int&>(a) = b; // expected-note 2{{constant expression cannot modify an object that is visible outside that expression}} const_cast<int&>(a) = b; // expected-note 3{{constant expression cannot modify an object that is visible outside that expression}}
} }
constexpr int wrap(int a, int b) { constexpr int wrap(int a, int b) {
set(a, b); set(a, b);
@ -140,7 +140,7 @@ namespace assign {
static_assert((set(a, 1), a) == 1, ""); // expected-error {{constant expression}} expected-note {{in call to 'set(a, 1)'}} static_assert((set(a, 1), a) == 1, ""); // expected-error {{constant expression}} expected-note {{in call to 'set(a, 1)'}}
static_assert((set(b, 1), b) == 1, ""); // expected-error {{constant expression}} expected-note {{in call to 'set(b, 1)'}} static_assert((set(b, 1), b) == 1, ""); // expected-error {{constant expression}} expected-note {{in call to 'set(b, 1)'}}
static_assert((set(c, 1), c) == 1, ""); // expected-error {{constant expression}} expected-note {{read of non-const variable 'c'}} static_assert((set(c, 1), c) == 1, ""); // expected-error {{constant expression}} expected-note {{in call to 'set(c, 1)'}}
static_assert(wrap(a, 1) == 1, ""); static_assert(wrap(a, 1) == 1, "");
static_assert(wrap(b, 1) == 1, ""); static_assert(wrap(b, 1) == 1, "");