From: Douglas Gregor Date: Wed, 5 Jul 2017 20:20:15 +0000 (+0000) Subject: Cope with Range-v3's CONCEPT_REQUIRES idiom X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=6907b9898ead736540d31fe32057102bb4106390;p=clang Cope with Range-v3's CONCEPT_REQUIRES idiom git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@307197 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 58614322f7..1485b80cf9 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/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/test/SemaTemplate/overload-candidates.cpp b/test/SemaTemplate/overload-candidates.cpp index edbbb84eca..8e0ddb0a5e 100644 --- a/test/SemaTemplate/overload-candidates.cpp +++ b/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'}} }