Cope with Range-v3's CONCEPT_REQUIRES idiom

llvm-svn: 307197
This commit is contained in:
Douglas Gregor 2017-07-05 20:20:15 +00:00
parent 00fa10b43f
commit bb33f57045
2 changed files with 60 additions and 0 deletions

View File

@ -2831,10 +2831,44 @@ static void collectConjunctionTerms(Expr *Clause,
Terms.push_back(Clause);
}
// The ranges-v3 library uses an odd pattern of a top-level "||" with
// a left-hand side that is value-dependent but never true. Identify
// the idiom and ignore that term.
static Expr *lookThroughRangesV3Condition(Preprocessor &PP, Expr *Cond) {
// Top-level '||'.
auto *BinOp = dyn_cast<BinaryOperator>(Cond->IgnoreParenImpCasts());
if (!BinOp) return Cond;
if (BinOp->getOpcode() != BO_LOr) return Cond;
// With an inner '==' that has a literal on the right-hand side.
Expr *LHS = BinOp->getLHS();
auto InnerBinOp = dyn_cast<BinaryOperator>(LHS->IgnoreParenImpCasts());
if (!InnerBinOp) return Cond;
if (InnerBinOp->getOpcode() != BO_EQ ||
!isa<IntegerLiteral>(InnerBinOp->getRHS()))
return Cond;
// If the inner binary operation came from a macro expansion named
// CONCEPT_REQUIRES or CONCEPT_REQUIRES_, return the right-hand side
// of the '||', which is the real, user-provided condition.
auto Loc = InnerBinOp->getExprLoc();
if (!Loc.isMacroID()) return Cond;
StringRef MacroName = PP.getImmediateMacroName(Loc);
if (MacroName == "CONCEPT_REQUIRES" || MacroName == "CONCEPT_REQUIRES_")
return BinOp->getRHS();
return Cond;
}
/// Find the failed subexpression within enable_if, and describe it
/// with a string.
static std::pair<Expr *, std::string>
findFailedEnableIfCondition(Sema &S, Expr *Cond) {
Cond = lookThroughRangesV3Condition(S.PP, Cond);
// Separate out all of the terms in a conjunction.
SmallVector<Expr *, 4> Terms;
collectConjunctionTerms(Cond, Terms);

View File

@ -137,4 +137,30 @@ namespace PR15673 {
#endif
void wibble() {}
void wobble() { wibble<int>(); } // expected-error {{no matching function for call to 'wibble'}}
template<typename T>
struct some_passing_trait : std::true_type {};
#if __cplusplus <= 199711L
// expected-warning@+4 {{default template arguments for a function template are a C++11 extension}}
// expected-warning@+4 {{default template arguments for a function template are a C++11 extension}}
#endif
template<typename T,
int n = 42,
typename std::enable_if<n == 43 || (some_passing_trait<T>::value && some_trait<T>::value), int>::type = 0>
void almost_rangesv3(); // expected-note{{candidate template ignored: requirement '42 == 43 || (some_passing_trait<int>::value && some_trait<int>::value)' was not satisfied}}
void test_almost_rangesv3() { almost_rangesv3<int>(); } // expected-error{{no matching function for call to 'almost_rangesv3'}}
#define CONCEPT_REQUIRES_(...) \
int x = 42, \
typename std::enable_if<(x == 43) || (__VA_ARGS__)>::type = 0
#if __cplusplus <= 199711L
// expected-warning@+4 {{default template arguments for a function template are a C++11 extension}}
// expected-warning@+4 {{default template arguments for a function template are a C++11 extension}}
#endif
template<typename T,
CONCEPT_REQUIRES_(some_passing_trait<T>::value && some_trait<T>::value)>
void rangesv3(); // expected-note{{candidate template ignored: requirement 'some_trait<int>::value' was not satisfied [with T = int, x = 42]}}
void test_rangesv3() { rangesv3<int>(); } // expected-error{{no matching function for call to 'rangesv3'}}
}