rymiel created this revision.
rymiel added a project: clang.
rymiel added reviewers: aaron.ballman, erichkeane.
Herald added a project: All.
rymiel requested review of this revision.
Herald added a subscriber: cfe-commits.

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 
<https://reviews.llvm.org/rGeccc734a69c0c012ae3160887b65a535b35ead3e>, 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


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D146140

Files:
  clang/include/clang/Parse/Parser.h
  clang/include/clang/Sema/Sema.h
  clang/lib/Parse/ParseExpr.cpp
  clang/lib/Parse/ParseExprCXX.cpp
  clang/lib/Sema/SemaConcept.cpp
  clang/test/SemaTemplate/concepts.cpp

Index: clang/test/SemaTemplate/concepts.cpp
===================================================================
--- clang/test/SemaTemplate/concepts.cpp
+++ clang/test/SemaTemplate/concepts.cpp
@@ -55,10 +55,15 @@
 }
 
 namespace P0857R0 {
+  template <typename T> static constexpr bool V = true;
+
   void f() {
     auto x = []<bool B> requires B {}; // expected-note {{constraints not satisfied}} expected-note {{false}}
     x.operator()<true>();
     x.operator()<false>(); // expected-error {{no matching member function}}
+
+    auto y = []<typename T> requires V<T> () {};
+    y.operator()<int>(); // OK
   }
 
   template<typename T> concept C = true;
Index: clang/lib/Sema/SemaConcept.cpp
===================================================================
--- clang/lib/Sema/SemaConcept.cpp
+++ clang/lib/Sema/SemaConcept.cpp
@@ -87,7 +87,8 @@
 
 bool Sema::CheckConstraintExpression(const Expr *ConstraintExpression,
                                      Token NextToken, bool *PossibleNonPrimary,
-                                     bool IsTrailingRequiresClause) {
+                                     bool IsTrailingRequiresClause,
+                                     bool IsLambdaRequiresClause) {
   // C++2a [temp.constr.atomic]p1
   // ..E shall be a constant expression of type bool.
 
@@ -112,7 +113,7 @@
           // 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) &&
+          (NextToken.is(tok::l_paren) && !IsLambdaRequiresClause &&
            (IsTrailingRequiresClause ||
             (Type->isDependentType() &&
              isa<UnresolvedLookupExpr>(ConstraintExpression)) ||
Index: clang/lib/Parse/ParseExprCXX.cpp
===================================================================
--- clang/lib/Parse/ParseExprCXX.cpp
+++ clang/lib/Parse/ParseExprCXX.cpp
@@ -1345,7 +1345,8 @@
       if (TryConsumeToken(tok::kw_requires)) {
         RequiresClause =
             Actions.ActOnRequiresClause(ParseConstraintLogicalOrExpression(
-                /*IsTrailingRequiresClause=*/false));
+                /*IsTrailingRequiresClause=*/false,
+                /*IsLambdaRequiresClause=*/true));
         if (RequiresClause.isInvalid())
           SkipUntil({tok::l_brace, tok::l_paren}, StopAtSemi | StopBeforeMatch);
       }
Index: clang/lib/Parse/ParseExpr.cpp
===================================================================
--- clang/lib/Parse/ParseExpr.cpp
+++ clang/lib/Parse/ParseExpr.cpp
@@ -255,7 +255,8 @@
 ///
 /// \endverbatim
 ExprResult
-Parser::ParseConstraintLogicalAndExpression(bool IsTrailingRequiresClause) {
+Parser::ParseConstraintLogicalAndExpression(bool IsTrailingRequiresClause,
+                                            bool IsLambdaRequiresClause) {
   EnterExpressionEvaluationContext ConstantEvaluated(
       Actions, Sema::ExpressionEvaluationContext::Unevaluated);
   bool NotPrimaryExpression = false;
@@ -299,9 +300,9 @@
       NotPrimaryExpression = false;
     }
     bool PossibleNonPrimary;
-    bool IsConstraintExpr =
-        Actions.CheckConstraintExpression(E.get(), Tok, &PossibleNonPrimary,
-                                          IsTrailingRequiresClause);
+    bool IsConstraintExpr = Actions.CheckConstraintExpression(
+        E.get(), Tok, &PossibleNonPrimary, IsTrailingRequiresClause,
+        IsLambdaRequiresClause);
     if (!IsConstraintExpr || PossibleNonPrimary) {
       // Atomic constraint might be an unparenthesized non-primary expression
       // (such as a binary operator), in which case we might get here (e.g. in
@@ -347,14 +348,16 @@
 ///
 /// \endverbatim
 ExprResult
-Parser::ParseConstraintLogicalOrExpression(bool IsTrailingRequiresClause) {
-  ExprResult LHS(ParseConstraintLogicalAndExpression(IsTrailingRequiresClause));
+Parser::ParseConstraintLogicalOrExpression(bool IsTrailingRequiresClause,
+                                           bool IsLambdaRequiresClause) {
+  ExprResult LHS(ParseConstraintLogicalAndExpression(IsTrailingRequiresClause,
+                                                     IsLambdaRequiresClause));
   if (!LHS.isUsable())
     return ExprError();
   while (Tok.is(tok::pipepipe)) {
     SourceLocation LogicalOrLoc = ConsumeToken();
-    ExprResult RHS =
-        ParseConstraintLogicalAndExpression(IsTrailingRequiresClause);
+    ExprResult RHS = ParseConstraintLogicalAndExpression(
+        IsTrailingRequiresClause, IsLambdaRequiresClause);
     if (!RHS.isUsable()) {
       Actions.CorrectDelayedTyposInExpr(LHS);
       return ExprError();
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -7253,7 +7253,8 @@
   /// non-primary expression being used as an atomic constraint.
   bool CheckConstraintExpression(const Expr *CE, Token NextToken = Token(),
                                  bool *PossibleNonPrimary = nullptr,
-                                 bool IsTrailingRequiresClause = false);
+                                 bool IsTrailingRequiresClause = false,
+                                 bool IsLambdaRequiresClause = false);
 
 private:
   /// Caches pairs of template-like decls whose associated constraints were
Index: clang/include/clang/Parse/Parser.h
===================================================================
--- clang/include/clang/Parse/Parser.h
+++ clang/include/clang/Parse/Parser.h
@@ -1776,8 +1776,11 @@
   ExprResult ParseCaseExpression(SourceLocation CaseLoc);
   ExprResult ParseConstraintExpression();
   ExprResult
-  ParseConstraintLogicalAndExpression(bool IsTrailingRequiresClause);
-  ExprResult ParseConstraintLogicalOrExpression(bool IsTrailingRequiresClause);
+  ParseConstraintLogicalAndExpression(bool IsTrailingRequiresClause,
+                                      bool IsLambdaRequiresClause = false);
+  ExprResult
+  ParseConstraintLogicalOrExpression(bool IsTrailingRequiresClause,
+                                     bool IsLambdaRequiresClause = false);
   // Expr that doesn't include commas.
   ExprResult ParseAssignmentExpression(TypeCastState isTypeCast = NotTypeCast);
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to