Improve handling of value dependent expressions in __attribute__((enable_if)), both in the condition expression and at the call site. Fixes PR20988!

llvm-svn: 224320
This commit is contained in:
Nick Lewycky 2014-12-16 06:12:01 +00:00
parent 417fc6b303
commit f0202ca775
3 changed files with 55 additions and 3 deletions

View File

@ -9088,7 +9088,8 @@ bool Expr::EvaluateWithSubstitution(APValue &Value, ASTContext &Ctx,
ArgVector ArgValues(Args.size());
for (ArrayRef<const Expr*>::iterator I = Args.begin(), E = Args.end();
I != E; ++I) {
if (!Evaluate(ArgValues[I - Args.begin()], Info, *I))
if ((*I)->isValueDependent() ||
!Evaluate(ArgValues[I - Args.begin()], Info, *I))
// If evaluation fails, throw away the argument entirely.
ArgValues[I - Args.begin()] = APValue();
if (Info.EvalStatus.HasSideEffects)

View File

@ -5792,6 +5792,7 @@ EnableIfAttr *Sema::CheckEnableIf(FunctionDecl *Function, ArrayRef<Expr *> Args,
// Convert the arguments.
SmallVector<Expr *, 16> ConvertedArgs;
bool InitializationFailed = false;
bool ContainsValueDependentExpr = false;
for (unsigned i = 0, e = Args.size(); i != e; ++i) {
if (i == 0 && !MissingImplicitThis && isa<CXXMethodDecl>(Function) &&
!cast<CXXMethodDecl>(Function)->isStatic() &&
@ -5804,6 +5805,7 @@ EnableIfAttr *Sema::CheckEnableIf(FunctionDecl *Function, ArrayRef<Expr *> Args,
InitializationFailed = true;
break;
}
ContainsValueDependentExpr |= R.get()->isValueDependent();
ConvertedArgs.push_back(R.get());
} else {
ExprResult R =
@ -5816,6 +5818,7 @@ EnableIfAttr *Sema::CheckEnableIf(FunctionDecl *Function, ArrayRef<Expr *> Args,
InitializationFailed = true;
break;
}
ContainsValueDependentExpr |= R.get()->isValueDependent();
ConvertedArgs.push_back(R.get());
}
}
@ -5826,9 +5829,16 @@ EnableIfAttr *Sema::CheckEnableIf(FunctionDecl *Function, ArrayRef<Expr *> Args,
for (AttrVec::iterator I = Attrs.begin(); I != E; ++I) {
APValue Result;
EnableIfAttr *EIA = cast<EnableIfAttr>(*I);
if (EIA->getCond()->isValueDependent()) {
// Don't even try now, we'll examine it after instantiation.
continue;
}
if (!EIA->getCond()->EvaluateWithSubstitution(
Result, Context, Function, llvm::makeArrayRef(ConvertedArgs)) ||
!Result.isInt() || !Result.getInt().getBoolValue()) {
Result, Context, Function, llvm::makeArrayRef(ConvertedArgs))) {
if (!ContainsValueDependentExpr)
return EIA;
} else if (!Result.isInt() || !Result.getInt().getBoolValue()) {
return EIA;
}
}

View File

@ -77,3 +77,44 @@ void test() {
typedep(1);
typedep(n); // expected-note{{in instantiation of function template specialization 'typedep<Nothing>' requested here}}
}
template <typename T> class C {
void f() __attribute__((enable_if(T::expr == 0, ""))) {}
void g() { f(); }
};
int fn3(bool b) __attribute__((enable_if(b, "")));
template <class T> void test3() {
fn3(sizeof(T) == 1);
}
// FIXME: issue an error (without instantiation) because ::h(T()) is not
// convertible to bool, because return types aren't overloadable.
void h(int);
template <typename T> void outer() {
void local_function() __attribute__((enable_if(::h(T()), "")));
local_function();
};
namespace PR20988 {
struct Integer {
Integer(int);
};
int fn1(const Integer &) __attribute__((enable_if(true, "")));
template <class T> void test1() {
int &expr = T::expr();
fn1(expr);
}
int fn2(const Integer &) __attribute__((enable_if(false, ""))); // expected-note{{candidate disabled}}
template <class T> void test2() {
int &expr = T::expr();
fn2(expr); // expected-error{{no matching function for call to 'fn2'}}
}
int fn3(bool b) __attribute__((enable_if(b, "")));
template <class T> void test3() {
fn3(sizeof(T) == 1);
}
}