diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 58614322f761..1485b80cf957 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -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(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(LHS->IgnoreParenImpCasts()); + if (!InnerBinOp) return Cond; + + if (InnerBinOp->getOpcode() != BO_EQ || + !isa(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 findFailedEnableIfCondition(Sema &S, Expr *Cond) { + Cond = lookThroughRangesV3Condition(S.PP, Cond); + // Separate out all of the terms in a conjunction. SmallVector Terms; collectConjunctionTerms(Cond, Terms); diff --git a/clang/test/SemaTemplate/overload-candidates.cpp b/clang/test/SemaTemplate/overload-candidates.cpp index edbbb84eca60..8e0ddb0a5e4c 100644 --- a/clang/test/SemaTemplate/overload-candidates.cpp +++ b/clang/test/SemaTemplate/overload-candidates.cpp @@ -137,4 +137,30 @@ namespace PR15673 { #endif void wibble() {} void wobble() { wibble(); } // expected-error {{no matching function for call to 'wibble'}} + + template + 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::value && some_trait::value), int>::type = 0> + void almost_rangesv3(); // expected-note{{candidate template ignored: requirement '42 == 43 || (some_passing_trait::value && some_trait::value)' was not satisfied}} + void test_almost_rangesv3() { almost_rangesv3(); } // 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::value && some_trait::value)> + void rangesv3(); // expected-note{{candidate template ignored: requirement 'some_trait::value' was not satisfied [with T = int, x = 42]}} + void test_rangesv3() { rangesv3(); } // expected-error{{no matching function for call to 'rangesv3'}} }