From 6acdf58919d5e66809555acf5809b12c54ba79dd Mon Sep 17 00:00:00 2001 From: Emilia Dreamer Date: Fri, 17 Mar 2023 10:13:02 +0200 Subject: [PATCH] [clang] Properly parse variable template requires clause in lambda Since P0857, part of C++20, a *lambda-expression* can contain a *requires-clause* after its *template-parameter-list*. While support for this was added as part of eccc734a69c0c012ae3160887b65a535b35ead3e, one specific case isn't handled properly, where the *requires-clause* consists of an instantiation of a boolean variable template. This is due to a diagnostic check which was written with the assumption that a *requires-clause* can never be followed by a left parenthesis. This assumption no longer holds for lambdas. This diagnostic check would then attempt to perform a "recovery", but it does so in a valid parse state, resulting in an invalid parse state instead! This patch adds a special case when parsing requires clauses of lambda templates, to skip this diagnostic check. Fixes https://github.com/llvm/llvm-project/issues/61278 Fixes https://github.com/llvm/llvm-project/issues/61387 Reviewed By: erichkeane Differential Revision: https://reviews.llvm.org/D146140 --- clang/docs/ReleaseNotes.rst | 3 +++ clang/lib/Sema/SemaConcept.cpp | 50 +++++++++++++++++++++--------------- clang/test/SemaTemplate/concepts.cpp | 5 ++++ 3 files changed, 37 insertions(+), 21 deletions(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 24f5b62..53001f65 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -80,6 +80,9 @@ C++20 Feature Support ^^^^^^^^^^^^^^^^^^^^^ - Support for out-of-line definitions of constrained templates has been improved. This partially fixes `#49620 `_. +- Lambda templates with a requires clause directly after the template parameters now parse + correctly if the requires clause consists of a variable with a dependent type. + (`#61278 `_) C++2b Feature Support ^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index 4a79522..4ff8684 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -105,27 +105,35 @@ bool Sema::CheckConstraintExpression(const Expr *ConstraintExpression, QualType Type = ConstraintExpression->getType(); auto CheckForNonPrimary = [&] { - if (PossibleNonPrimary) - *PossibleNonPrimary = - // We have the following case: - // template requires func(0) struct S { }; - // The user probably isn't aware of the parentheses required around - // the function call, and we're only going to parse 'func' as the - // primary-expression, and complain that it is of non-bool type. - (NextToken.is(tok::l_paren) && - (IsTrailingRequiresClause || - (Type->isDependentType() && - isa(ConstraintExpression)) || - Type->isFunctionType() || - Type->isSpecificBuiltinType(BuiltinType::Overload))) || - // We have the following case: - // template requires size_ == 0 struct S { }; - // The user probably isn't aware of the parentheses required around - // the binary operator, and we're only going to parse 'func' as the - // first operand, and complain that it is of non-bool type. - getBinOpPrecedence(NextToken.getKind(), - /*GreaterThanIsOperator=*/true, - getLangOpts().CPlusPlus11) > prec::LogicalAnd; + if (!PossibleNonPrimary) + return; + + *PossibleNonPrimary = + // We have the following case: + // template requires func(0) struct S { }; + // The user probably isn't aware of the parentheses required around + // the function call, and we're only going to parse 'func' as the + // primary-expression, and complain that it is of non-bool type. + // + // However, if we're in a lambda, this might also be: + // [] requires var () {}; + // Which also looks like a function call due to the lambda parentheses, + // but unlike the first case, isn't an error, so this check is skipped. + (NextToken.is(tok::l_paren) && + (IsTrailingRequiresClause || + (Type->isDependentType() && + isa(ConstraintExpression) && + !dyn_cast_if_present(getCurFunction())) || + Type->isFunctionType() || + Type->isSpecificBuiltinType(BuiltinType::Overload))) || + // We have the following case: + // template requires size_ == 0 struct S { }; + // The user probably isn't aware of the parentheses required around + // the binary operator, and we're only going to parse 'func' as the + // first operand, and complain that it is of non-bool type. + getBinOpPrecedence(NextToken.getKind(), + /*GreaterThanIsOperator=*/true, + getLangOpts().CPlusPlus11) > prec::LogicalAnd; }; // An atomic constraint! diff --git a/clang/test/SemaTemplate/concepts.cpp b/clang/test/SemaTemplate/concepts.cpp index df4db9b..bf5896a 100644 --- a/clang/test/SemaTemplate/concepts.cpp +++ b/clang/test/SemaTemplate/concepts.cpp @@ -55,10 +55,15 @@ namespace PR45699 { } namespace P0857R0 { + template static constexpr bool V = true; + void f() { auto x = [] requires B {}; // expected-note {{constraints not satisfied}} expected-note {{false}} x.operator()(); x.operator()(); // expected-error {{no matching member function}} + + auto y = [] requires V () {}; + y.operator()(); // OK } template concept C = true; -- 2.7.4