Fix __attribute__((enable_if)) to treat arguments with side-effects as

non-constant.

We previously failed the entire condition evaluation if an unmodeled
side-effect was encountered in an argument, even if that argument was
unused in the attribute's condition.
This commit is contained in:
Richard Smith 2019-10-30 13:32:52 -07:00
parent e62f91fb75
commit bb06149131
2 changed files with 57 additions and 12 deletions

View File

@ -1039,10 +1039,13 @@ namespace {
/// cleanups would have had a side-effect, note that as an unmodeled
/// side-effect and return false. Otherwise, return true.
bool discardCleanups() {
for (Cleanup &C : CleanupStack)
if (C.hasSideEffect())
if (!noteSideEffect())
return false;
for (Cleanup &C : CleanupStack) {
if (C.hasSideEffect() && !noteSideEffect()) {
CleanupStack.clear();
return false;
}
}
CleanupStack.clear();
return true;
}
@ -14340,27 +14343,41 @@ bool Expr::EvaluateWithSubstitution(APValue &Value, ASTContext &Ctx,
assert(MD && "Don't provide `this` for non-methods.");
assert(!MD->isStatic() && "Don't provide `this` for static methods.");
#endif
if (EvaluateObjectArgument(Info, This, ThisVal))
if (!This->isValueDependent() &&
EvaluateObjectArgument(Info, This, ThisVal) &&
!Info.EvalStatus.HasSideEffects)
ThisPtr = &ThisVal;
if (Info.EvalStatus.HasSideEffects)
return false;
// Ignore any side-effects from a failed evaluation. This is safe because
// they can't interfere with any other argument evaluation.
Info.EvalStatus.HasSideEffects = false;
}
ArgVector ArgValues(Args.size());
for (ArrayRef<const Expr*>::iterator I = Args.begin(), E = Args.end();
I != E; ++I) {
if ((*I)->isValueDependent() ||
!Evaluate(ArgValues[I - Args.begin()], Info, *I))
!Evaluate(ArgValues[I - Args.begin()], Info, *I) ||
Info.EvalStatus.HasSideEffects)
// If evaluation fails, throw away the argument entirely.
ArgValues[I - Args.begin()] = APValue();
if (Info.EvalStatus.HasSideEffects)
return false;
// Ignore any side-effects from a failed evaluation. This is safe because
// they can't interfere with any other argument evaluation.
Info.EvalStatus.HasSideEffects = false;
}
// Parameter cleanups happen in the caller and are not part of this
// evaluation.
Info.discardCleanups();
Info.EvalStatus.HasSideEffects = false;
// Build fake call to Callee.
CallStackFrame Frame(Info, Callee->getLocation(), Callee, ThisPtr,
ArgValues.data());
return Evaluate(Value, Info, this) && Info.discardCleanups() &&
// FIXME: Missing ExprWithCleanups in enable_if conditions?
FullExpressionRAII Scope(Info);
return Evaluate(Value, Info, this) && Scope.destroy() &&
!Info.EvalStatus.HasSideEffects;
}

View File

@ -1,5 +1,5 @@
// RUN: %clang_cc1 -std=c++11 -verify %s
// RUN: %clang_cc1 -std=c++2a -verify %s
typedef int (*fp)(int);
int surrogate(int);
struct Incomplete; // expected-note{{forward declaration of 'Incomplete'}} \
@ -533,3 +533,31 @@ namespace StringLiteralDetector {
}
}
namespace IgnoreUnusedArgSideEffects {
struct A { ~A(); };
void f(A a, bool b) __attribute__((enable_if(b, ""))); // expected-note 2-3{{disabled}}
void test() {
f(A(), true);
f(A(), false); // expected-error {{no matching function}}
int n;
f((n = 1, A()), true);
f(A(), (n = 1, true)); // expected-error {{no matching function}}
f(A(), (A(), true));
}
#if __cplusplus > 201702L
struct B { constexpr ~B() {} bool b; };
void g(B b) __attribute__((enable_if(b.b, ""))); // expected-note {{disabled}}
void test2() {
g(B{true});
g(B{false}); // expected-error {{no matching function}}
f(A(), B{true}.b);
f(A(), B{false}.b); // expected-error {{no matching function}}
}
// First condition is non-constant due to non-constexpr destructor of A.
int &h() __attribute__((enable_if((A(), true), "")));
float &h() __attribute__((enable_if((B(), true), "")));
float &x = h();
#endif
}