Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

[Clang] Fix parsing of expressions of the form (T())[/*...*/] #140053

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from

Conversation

cor3ntin
Copy link
Contributor

When tentatively parsing cast expressions, we were assuming [ was the start of a lambda expression.

However, the expression might instead by a subscripted parenthesized postfix expression.

Therefore, we need to check if the expression is in fact a lambda.

We do that by looking for an open brace, bailing early when possible.

Fixes #20723

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels May 15, 2025
@llvmbot
Copy link
Member

llvmbot commented May 15, 2025

@llvm/pr-subscribers-clang

Author: cor3ntin (cor3ntin)

Changes

When tentatively parsing cast expressions, we were assuming [ was the start of a lambda expression.

However, the expression might instead by a subscripted parenthesized postfix expression.

Therefore, we need to check if the expression is in fact a lambda.

We do that by looking for an open brace, bailing early when possible.

Fixes #20723


Patch is 24.73 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/140053.diff

8 Files Affected:

  • (modified) clang/docs/ReleaseNotes.rst (+1)
  • (modified) clang/include/clang/Parse/Parser.h (+4)
  • (modified) clang/lib/Parse/ParseExpr.cpp (+4-2)
  • (modified) clang/lib/Parse/ParseExprCXX.cpp (+62)
  • (modified) clang/test/Parser/cxx0x-lambda-expressions.cpp (+82)
  • (modified) clang/test/Parser/cxx1z-constexpr-lambdas.cpp (+24-14)
  • (modified) clang/test/Parser/cxx2a-template-lambdas.cpp (+25-19)
  • (modified) clang/test/Parser/cxx2b-lambdas.cpp (+76-55)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 31c517338c21f..a7c8b2514b226 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -710,6 +710,7 @@ Bug Fixes to C++ Support
 - Clang now correctly parses arbitrary order of ``[[]]``, ``__attribute__`` and ``alignas`` attributes for declarations (#GH133107)
 - Fixed a crash when forming an invalid function type in a dependent context. (#GH138657) (#GH115725) (#GH68852)
 - Clang no longer segfaults when there is a configuration mismatch between modules and their users (http://crbug.com/400353616).
+- Fix parsing of expressions of the form ``((T))[expr]``. (#GH20723)
 
 Bug Fixes to AST Handling
 ^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index e6492b81dfff8..016708de2bf4c 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -4639,6 +4639,10 @@ class Parser : public CodeCompletionHandler {
   ParseLambdaIntroducer(LambdaIntroducer &Intro,
                         LambdaIntroducerTentativeParse *Tentative = nullptr);
 
+  /// Tries to determine if an expression of the form (S())[...]...
+  /// is a type-cast followed by a lambda, or a subscript expression
+  bool IsLambdaAfterTypeCast();
+
   /// ParseLambdaExpressionAfterIntroducer - Parse the rest of a lambda
   /// expression.
   ExprResult ParseLambdaExpressionAfterIntroducer(LambdaIntroducer &Intro);
diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp
index 11cfbbe790418..09dabaad4e231 100644
--- a/clang/lib/Parse/ParseExpr.cpp
+++ b/clang/lib/Parse/ParseExpr.cpp
@@ -1585,8 +1585,10 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind,
         }
         break;
       }
-      Res = ParseLambdaExpression();
-      break;
+      if (isTypeCast != TypeCastState::IsTypeCast || IsLambdaAfterTypeCast()) {
+        Res = ParseLambdaExpression();
+        break;
+      }
     }
     if (getLangOpts().ObjC) {
       Res = ParseObjCMessageExpression();
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index d95260829e4a0..996833b3cc293 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -692,6 +692,68 @@ ExprResult Parser::ParseLambdaExpression() {
   return ParseLambdaExpressionAfterIntroducer(Intro);
 }
 
+bool Parser::IsLambdaAfterTypeCast() {
+  assert(getLangOpts().CPlusPlus && Tok.is(tok::l_square) &&
+         "Not at the start of a possible lambda expression.");
+  RevertingTentativeParsingAction TPA(*this);
+  ConsumeBracket();
+  // skip the introducer
+  if (Tok.is(tok::equal) ||
+      (Tok.is(tok::amp) && NextToken().isOneOf(tok::comma, tok::r_square)))
+    SkipUntil(tok::r_square);
+
+  auto IsLambdaKWOrAttribute = [&]() {
+    // These keyworks that either can appear somewhere in a lambda declarator,
+    // or cannot appear in a cast expression and we recover in favor of lambdas
+    if (Tok.isOneOf(tok::kw___declspec, tok::kw___noinline__, tok::kw_noexcept,
+                    tok::kw_throw, tok::kw_mutable, tok::kw___attribute,
+                    tok::kw_constexpr, tok::kw_consteval, tok::kw_static,
+                    tok::kw_inline, tok::kw_extern, tok::kw___private,
+                    tok::kw___global, tok::kw___local, tok::kw___constant,
+                    tok::kw___generic, tok::kw_groupshared, tok::kw_requires,
+                    tok::kw_noexcept))
+      return true;
+    return Tok.isRegularKeywordAttribute() ||
+           isCXX11AttributeSpecifier() !=
+               CXX11AttributeKind::NotAttributeSpecifier;
+  };
+
+  if (Tok.is(tok::l_brace) || IsLambdaKWOrAttribute())
+    return true;
+
+  // This is a generic lambda,
+  if (Tok.is(tok::less)) {
+    ConsumeToken();
+    // Common cases. We consider <> as an invalid lambda.
+    if (Tok.isOneOf(tok::greater, tok::kw_typename, tok::kw_auto,
+                    tok::kw_template))
+      return true;
+    if (isStartOfTemplateTypeParameter() != TPResult::False)
+      return true;
+    return isCXXDeclarationSpecifier(ImplicitTypenameContext::Yes) !=
+           TPResult::False;
+  }
+  // skip the parameter list
+  if (Tok.is(tok::l_paren)) {
+    ConsumeParen();
+    SkipUntil(tok::r_paren);
+  }
+
+  if (IsLambdaKWOrAttribute())
+    return true;
+
+  if (Tok.is(tok::arrow)) {
+    ConsumeToken();
+    // These cases are always id-expressions
+    if (Tok.isOneOf(tok::kw_template, tok::kw_operator, tok::tilde))
+      return false;
+    if (!Tok.is(tok::identifier))
+      return true;
+    return isCXXTypeId(TentativeCXXTypeIdContext::InTrailingReturnType);
+  }
+  return Tok.is(tok::l_brace);
+}
+
 ExprResult Parser::TryParseLambdaExpression() {
   assert(getLangOpts().CPlusPlus && Tok.is(tok::l_square) &&
          "Not at the start of a possible lambda expression.");
diff --git a/clang/test/Parser/cxx0x-lambda-expressions.cpp b/clang/test/Parser/cxx0x-lambda-expressions.cpp
index a786a964163e4..d5466d44d8fff 100644
--- a/clang/test/Parser/cxx0x-lambda-expressions.cpp
+++ b/clang/test/Parser/cxx0x-lambda-expressions.cpp
@@ -40,6 +40,33 @@ class C {
     return 1;
   }
 
+  int type_cast() {
+    int foo, bar;
+
+    (void)[]; // expected-error {{expected expression}}
+    (void)[+] {}; // expected-error {{expected variable name or 'this' in lambda capture list}}
+    (void)[foo+] {}; // expected-error {{expected ',' or ']' in lambda capture list}}
+    (void)[foo,&this] {}; // expected-error {{'this' cannot be captured by reference}}
+    (void)[&this] {}; // expected-error {{'this' cannot be captured by reference}}
+    (void)[&,] {}; // expected-error {{expected variable name or 'this' in lambda capture list}}
+    (void)[=,] {}; // expected-error {{expected variable name or 'this' in lambda capture list}}
+    (void)[] {};
+    (void)[=] (int i) {};
+    (void)[&] (int) mutable -> void {};
+    (void)[foo,bar] () { return 3; };
+    (void)[=,&foo] () {};
+    (void)[&,foo] () {};
+    (void)[this] () {};
+    (void)[] () -> class C { return C(); };
+    (void)[] () -> enum E { return e; };
+
+    (void)[] -> int { return 0; }; // cxx23ext-warning {{lambda without a parameter clause is a C++23 extension}}
+    (void)[] mutable -> int { return 0; }; // cxx23ext-warning {{is a C++23 extension}}
+
+    (void)[](int) -> {}; // PR13652 expected-error {{expected a type}}
+    return 1;
+  }
+
   void designator_or_lambda() {
     typedef int T;
     const int b = 0;
@@ -125,10 +152,46 @@ class C {
     [][[]]{}; // cxx23ext-warning {{an attribute specifier sequence in this position is a C++23 extension}}
   }
 
+  void attributes_type_cast() {
+    (void)[] __attribute__((noreturn)){}; // cxx23ext-warning {{lambda without a parameter clause is a C++23 extension}}
+
+    (void)[]() [[]]
+      mutable {}; // expected-error {{expected body of lambda expression}}
+
+    (void)[]() [[]] {};
+    (void)[]() [[]] -> void {};
+    (void)[]() mutable [[]] -> void {};
+#if __cplusplus >= 201103L
+    (void)[]() mutable noexcept [[]] -> void {};
+#endif
+
+    // Testing GNU-style attributes on lambdas -- the attribute is specified
+    // before the mutable specifier instead of after (unlike C++11).
+    (void)[]() __attribute__((noreturn)) mutable { while(1); };
+    (void)[]() mutable
+      __attribute__((noreturn)) { while(1); }; // expected-error {{expected body of lambda expression}}
+
+    // Testing support for P2173 on adding attributes to the declaration
+    // rather than the type.
+    (void)[][[]](){}; // cxx23ext-warning {{an attribute specifier sequence in this position is a C++23 extension}}
+
+    (void)[]<typename>[[]](){}; // cxx20ext-warning    {{explicit template parameter list for lambdas is a C++20 extension}}
+                          // cxx23ext-warning@-1 {{an attribute specifier sequence in this position is a C++23 extension}}
+
+    (void)[][[]]{}; // cxx23ext-warning {{an attribute specifier sequence in this position is a C++23 extension}}
+  }
+
   void missing_parens() {
     [] mutable {}; // cxx23ext-warning {{is a C++23 extension}}
 #if __cplusplus >= 201103L
     [] noexcept {}; // cxx23ext-warning {{is a C++23 extension}}
+#endif
+  }
+
+  void missing_parens_type_cast() {
+    (void)[] mutable {}; // cxx23ext-warning {{is a C++23 extension}}
+#if __cplusplus >= 201103L
+    (void)[] noexcept {}; // cxx23ext-warning {{is a C++23 extension}}
 #endif
   }
 };
@@ -150,6 +213,25 @@ struct S {
 };
 }
 
+#if __cplusplus >= 201103L
+namespace GH20723 {
+struct S {
+    S operator[](int);
+    S operator()();
+    S operator<(int);
+    S* operator->();
+    long a;
+};
+int n;
+void f() {
+    static_assert(__is_same_as(decltype((S())[n]()), S), "");
+    static_assert(__is_same_as(decltype((S())[n] < 0), S), "");
+    static_assert(__is_same_as(decltype((S())[n]->a), long), "");
+}
+}
+#endif
+
+
 struct S {
   template <typename T>
   void m (T x =[0); // expected-error{{expected variable name or 'this' in lambda capture list}}
diff --git a/clang/test/Parser/cxx1z-constexpr-lambdas.cpp b/clang/test/Parser/cxx1z-constexpr-lambdas.cpp
index 87584ee5ca91b..bba63b4925298 100644
--- a/clang/test/Parser/cxx1z-constexpr-lambdas.cpp
+++ b/clang/test/Parser/cxx1z-constexpr-lambdas.cpp
@@ -1,26 +1,34 @@
-// RUN: %clang_cc1 -std=c++23 %s -verify
-// RUN: %clang_cc1 -std=c++20 %s -verify
-// RUN: %clang_cc1 -std=c++17 %s -verify
-// RUN: %clang_cc1 -std=c++14 %s -verify
-// RUN: %clang_cc1 -std=c++11 %s -verify
+// RUN: %clang_cc1 -std=c++23 %s -verify -Wno-unused "-DTYPE_CAST="
+// RUN: %clang_cc1 -std=c++20 %s -verify -Wno-unused "-DTYPE_CAST="
+// RUN: %clang_cc1 -std=c++17 %s -verify -Wno-unused "-DTYPE_CAST="
+// RUN: %clang_cc1 -std=c++14 %s -verify -Wno-unused "-DTYPE_CAST="
+// RUN: %clang_cc1 -std=c++11 %s -verify -Wno-unused "-DTYPE_CAST="
 
-auto XL0 = [] constexpr { return true; };
+// RUN: %clang_cc1 -std=c++23 %s -verify "-DTYPE_CAST=(void)"
+// RUN: %clang_cc1 -std=c++20 %s -verify "-DTYPE_CAST=(void)"
+// RUN: %clang_cc1 -std=c++17 %s -verify "-DTYPE_CAST=(void)"
+// RUN: %clang_cc1 -std=c++14 %s -verify "-DTYPE_CAST=(void)"
+// RUN: %clang_cc1 -std=c++11 %s -verify "-DTYPE_CAST=(void)"
+
+void test() {
+
+TYPE_CAST [] constexpr { return true; };
 #if __cplusplus <= 201402L
 // expected-warning@-2 {{is a C++17 extension}}
 #endif
 #if __cplusplus <= 202002L
 // expected-warning@-5 {{lambda without a parameter clause is a C++23 extension}}
 #endif
-auto XL1 = []() mutable //
+TYPE_CAST []() mutable //
     mutable             // expected-error{{cannot appear multiple times}}
     mutable {};         // expected-error{{cannot appear multiple times}}
 
 #if __cplusplus > 201402L
-auto XL2 = [] () constexpr mutable constexpr { }; //expected-error{{cannot appear multiple times}}
-auto L = []() mutable constexpr { };
-auto L2 = []() constexpr { };
-auto L4 = []() constexpr mutable { }; 
-auto XL16 = [] () constexpr
+TYPE_CAST [] () constexpr mutable constexpr { }; //expected-error{{cannot appear multiple times}}
+TYPE_CAST []() mutable constexpr { };
+TYPE_CAST []() constexpr { };
+TYPE_CAST []() constexpr mutable { };
+TYPE_CAST [] () constexpr
                   mutable
                   constexpr   //expected-error{{cannot appear multiple times}}
                   mutable     //expected-error{{cannot appear multiple times}}
@@ -31,8 +39,10 @@ auto XL16 = [] () constexpr
 
 #else
 auto L = []() mutable constexpr {return 0; }; //expected-warning{{is a C++17 extension}}
-auto L2 = []() constexpr { return 0;};//expected-warning{{is a C++17 extension}}
-auto L4 = []() constexpr mutable { return 0; }; //expected-warning{{is a C++17 extension}}
+TYPE_CAST []() constexpr { return 0;};//expected-warning{{is a C++17 extension}}
+TYPE_CAST []() constexpr mutable { return 0; }; //expected-warning{{is a C++17 extension}}
 #endif
 
+}
+
 
diff --git a/clang/test/Parser/cxx2a-template-lambdas.cpp b/clang/test/Parser/cxx2a-template-lambdas.cpp
index 98c74a247b535..bb43101045e9e 100644
--- a/clang/test/Parser/cxx2a-template-lambdas.cpp
+++ b/clang/test/Parser/cxx2a-template-lambdas.cpp
@@ -1,38 +1,44 @@
-// RUN: %clang_cc1 -std=c++23 %s -verify
-// RUN: %clang_cc1 -std=c++20 %s -verify
+// RUN: %clang_cc1 -std=c++23 %s -verify -Wno-unused "-DTYPE_CAST="
+// RUN: %clang_cc1 -std=c++20 %s -verify -Wno-unused "-DTYPE_CAST="
+// RUN: %clang_cc1 -std=c++23 %s -verify "-DTYPE_CAST=(void)"
+// RUN: %clang_cc1 -std=c++20 %s -verify "-DTYPE_CAST=(void)"
 
-auto L0 = []<> { }; //expected-error {{cannot be empty}}
+void test() {
 
-auto L1 = []<typename T1, typename T2> { };
-auto L2 = []<typename T1, typename T2>(T1 arg1, T2 arg2) -> T1 { };
-auto L3 = []<typename T>(auto arg) { T t; };
-auto L4 = []<int I>() { };
+TYPE_CAST []<> { }; //expected-error {{cannot be empty}}
+
+TYPE_CAST []<typename T1, typename T2> { };
+TYPE_CAST []<typename T1, typename T2>(T1 arg1, T2 arg2) -> T1 { };
+TYPE_CAST []<typename T>(auto arg) { T t; };
+TYPE_CAST []<int I>() { };
 
 // http://llvm.org/PR49736
-auto L5 = []<auto>(){};
-auto L6 = []<auto>{};
-auto L7 = []<auto>() noexcept {};
-auto L8 = []<auto> noexcept {};
+TYPE_CAST []<auto>(){};
+TYPE_CAST []<auto>{};
+TYPE_CAST []<auto>() noexcept {};
+TYPE_CAST []<auto> noexcept {};
 #if __cplusplus <= 202002L
 // expected-warning@-2 {{lambda without a parameter clause is a C++23 extension}}
 #endif
-auto L9 = []<auto> requires true {};
-auto L10 = []<auto> requires true(){};
-auto L11 = []<auto> requires true() noexcept {};
-auto L12 = []<auto> requires true noexcept {};
+TYPE_CAST []<auto> requires true {};
+TYPE_CAST []<auto> requires true(){};
+TYPE_CAST []<auto> requires true() noexcept {};
+TYPE_CAST []<auto> requires true noexcept {};
 #if __cplusplus <= 202002L
 // expected-warning@-2 {{is a C++23 extension}}
 #endif
-auto L13 = []<auto>() noexcept requires true {};
-auto L14 = []<auto> requires true() noexcept requires true {};
+TYPE_CAST []<auto>() noexcept requires true {};
+TYPE_CAST []<auto> requires true() noexcept requires true {};
 
-auto XL0 = []<auto> noexcept requires true {};               // expected-error {{expected body of lambda expression}}
-auto XL1 = []<auto> requires true noexcept requires true {}; // expected-error {{expected body}}
+TYPE_CAST []<auto> noexcept requires true {};               // expected-error {{expected body of lambda expression}}
+TYPE_CAST []<auto> requires true noexcept requires true {}; // expected-error {{expected body}}
 #if __cplusplus <= 202002L
 // expected-warning@-3 {{is a C++23 extension}}
 // expected-warning@-3 {{is a C++23 extension}}
 #endif
 
+}
+
 namespace GH64962 {
 void f() {
   [] <typename T>(T i) -> int[] // expected-error {{function cannot return array type 'int[]'}}
diff --git a/clang/test/Parser/cxx2b-lambdas.cpp b/clang/test/Parser/cxx2b-lambdas.cpp
index 758ec9a42f56d..a769fbda36276 100644
--- a/clang/test/Parser/cxx2b-lambdas.cpp
+++ b/clang/test/Parser/cxx2b-lambdas.cpp
@@ -1,88 +1,109 @@
-// RUN: %clang_cc1 -std=c++03 %s -verify                -Wno-c++23-extensions -Wno-c++20-extensions -Wno-c++17-extensions -Wno-c++14-extensions -Wno-c++11-extensions
-// RUN: %clang_cc1 -std=c++11 %s -verify=expected,cxx11 -Wno-c++23-extensions -Wno-c++20-extensions -Wno-c++17-extensions -Wno-c++14-extensions
-// RUN: %clang_cc1 -std=c++14 %s -verify                -Wno-c++23-extensions -Wno-c++20-extensions -Wno-c++17-extensions
-// RUN: %clang_cc1 -std=c++17 %s -verify                -Wno-c++23-extensions -Wno-c++20-extensions
-// RUN: %clang_cc1 -std=c++20 %s -verify                -Wno-c++23-extensions
-// RUN: %clang_cc1 -std=c++23 %s -verify
-
-auto LL0 = [] {};
-auto LL1 = []() {};
-auto LL2 = []() mutable {};
+// RUN: %clang_cc1 -std=c++03 %s "-DTYPE_CAST=" -verify                -Wno-unused -Wno-c++23-extensions -Wno-c++20-extensions -Wno-c++17-extensions -Wno-c++14-extensions -Wno-c++11-extensions
+// RUN: %clang_cc1 -std=c++11 %s "-DTYPE_CAST=" -verify=expected,cxx11 -Wno-unused -Wno-c++23-extensions -Wno-c++20-extensions -Wno-c++17-extensions -Wno-c++14-extensions
+// RUN: %clang_cc1 -std=c++14 %s "-DTYPE_CAST=" -verify                -Wno-unused -Wno-c++23-extensions -Wno-c++20-extensions -Wno-c++17-extensions
+// RUN: %clang_cc1 -std=c++17 %s "-DTYPE_CAST=" -verify                -Wno-unused -Wno-c++23-extensions -Wno-c++20-extensions
+// RUN: %clang_cc1 -std=c++20 %s "-DTYPE_CAST=" -verify                -Wno-unused -Wno-c++23-extensions
+// RUN: %clang_cc1 -std=c++23 %s "-DTYPE_CAST=" -verify                -Wno-unused
+
+// RUN: %clang_cc1 -std=c++03 %s "-DTYPE_CAST=(void)" -verify                -Wno-unused -Wno-c++23-extensions -Wno-c++20-extensions -Wno-c++17-extensions -Wno-c++14-extensions -Wno-c++11-extensions
+// RUN: %clang_cc1 -std=c++11 %s "-DTYPE_CAST=(void)" -verify=expected,cxx11 -Wno-unused -Wno-c++23-extensions -Wno-c++20-extensions -Wno-c++17-extensions -Wno-c++14-extensions
+// RUN: %clang_cc1 -std=c++14 %s "-DTYPE_CAST=(void)" -verify                -Wno-unused -Wno-c++23-extensions -Wno-c++20-extensions -Wno-c++17-extensions
+// RUN: %clang_cc1 -std=c++17 %s "-DTYPE_CAST=(void)" -verify                -Wno-unused -Wno-c++23-extensions -Wno-c++20-extensions
+// RUN: %clang_cc1 -std=c++20 %s "-DTYPE_CAST=(void)" -verify                -Wno-unused -Wno-c++23-extensions
+// RUN: %clang_cc1 -std=c++23 %s "-DTYPE_CAST=(void)" -verify                -Wno-unused
+
+void test() {
+
+TYPE_CAST [] {};
+TYPE_CAST []() {};
+TYPE_CAST []() mutable {};
 #if __cplusplus >= 201103L
-auto LL3 = []() constexpr {}; // cxx11-error {{return type 'void' is not a literal type}}
+TYPE_CAST []() constexpr {}; // cxx11-error {{return type 'void' is not a literal type}}
 #endif
 
 #if __cplusplus >= 201103L
-auto L0 = [] constexpr {}; // cxx11-error {{return type 'void' is not a literal type}}
+TYPE_CAST [] constexpr {}; // cxx11-error {{return type 'void' is not a literal type}}
 #endif
-auto L1 = [] mutable {};
+TYPE_CAST [] mutable {};
 #if __cplusplus >= 201103L
-auto L2 = [] noexcept {};
-auto L3 = [] constexpr mutable {}; // cxx11-error {{return type 'void' is not a literal type}}
-auto L4 = [] mutable constexpr {}; // cxx11-error {{return type 'void' is not a literal type}}
-auto L5 = [] constexpr mutable noexcept {}; // cxx11-error {{return type 'void' is not a literal type}}
+TYPE_CAST [] noexcept {};
+TYPE_CAST [] constexpr mutable {}; // cxx11-error {{return type 'void' is not a literal type}}
+TYPE_CAST [] mutable constexpr {}; // cxx11-error {{return type 'void' is not a literal type}}
+TYPE_CAST [] constexpr mutable noexcept {}; // cxx11-error {{return type 'void' is not a literal type}}
 #endif
-auto L6 = [s = 1] mutable {};
+TYPE_CAST [s = 1] mutable {};
 #if __cplusplus >= 201103L
-auto L7 = [s = 1] constexpr mutable noexcept {}; // cxx11-error {{return type 'void' is not a literal type}}
+TYPE_CAST [s = 1] constexpr mutable noexcept {}; // cxx11-error {{return type 'void' is not a literal type}}
 #endif
-auto L8 = [] -> bool { return true; };
-auto L9 = []<typename T> { return true; };
+TYPE_CAST [] -> bool { return true; };
+TYPE_CAST []<typename T> { return true; };
 #if __cplusplus >= 201103L
-auto L10 = []<typename T> noexcept { return true; };
+TYPE_CAST []<typename T> noexcept { return true; };
 #endif
-auto L11 = []<typename T> -> bool { return true; };
+TYPE_CAST []<typename T> -> bool { return true; };
 #if __cplusplus >= 202002L
-auto L12 = [] consteval {};
-auto L13 = []() requires true {}; // expected-error{{non-templated function cannot have a requires clause}}
-auto L14 = []<auto> requires true() requires true {};
-auto L15 = []<auto> requires true noexcept {};
+TYPE_CAST [] consteval {};
+TYPE_CAST []() requires true {}; // expected-error{{non-templated function cannot have a requires clause}}
+TYPE_CAST []<auto> requires true() requires true {};
+TYPE_CAST []<auto> requires true noexcept {};
 #endif
-auto L16 = [] [[maybe_unused]]{};
+TYPE_CAST [] [[maybe_unused]]{};
 
 #if __cplusplus >= 201103L
-auto XL0 = [] mutable constexpr mutable {};    // expected-error{{cannot appear multiple times}} cxx11-error {{return type 'void' is not a literal type}}
-auto XL1 = [] constexpr mutable constexpr {};  // expected-error{{cannot appear multiple times}} cxx11-error {{return type 'void' is not a literal type}}
-auto XL2 = []) constexpr mutable constexpr {}; // expected-error{{expected body of lambda expression}}
-au...
[truncated]

@cor3ntin
Copy link
Contributor Author

Note that this does not address the case where (S())[ is parsed as the start of an Objective-C message

when checking cast expression, we were assuming `[` was the start
of a lambda expression.

However, the expression might instead by
a subscripted parenthesized postfix expression.

Therefore we need to check if the expression
is in fact a lambda.

We do that by looking an open brace, bailing early
when possible.

Fixes llvm#20723
@cor3ntin cor3ntin force-pushed the corentin/gh20723 branch from f67af47 to 7b4f82c Compare May 15, 2025 13:14
return false;
if (!Tok.is(tok::identifier))
return true;
return isCXXTypeId(TentativeCXXTypeIdContext::InTrailingReturnType);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it have any side effects like triggering template instantiation? I'm a bit concerned about potential waste of reverting annotated tokens.

@@ -710,6 +710,7 @@ Bug Fixes to C++ Support
- Clang now correctly parses arbitrary order of ``[[]]``, ``__attribute__`` and ``alignas`` attributes for declarations (#GH133107)
- Fixed a crash when forming an invalid function type in a dependent context. (#GH138657) (#GH115725) (#GH68852)
- Clang no longer segfaults when there is a configuration mismatch between modules and their users (http://crbug.com/400353616).
- Fix parsing of expressions of the form ``((T))[expr]``. (#GH20723)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(T())[expr]

auto IsLambdaKWOrAttribute = [&]() {
// These are keyworks that can appear somewhere in a lambda declarator,
// or cannot appear in a cast-expression and we recover in favor of lambdas
if (Tok.isOneOf(tok::kw___declspec, tok::kw___noinline__, tok::kw_noexcept,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kw_noexcept is in here twice.

void f() {
static_assert(__is_same_as(decltype((S())[n]()), S), "");
static_assert(__is_same_as(decltype((S())[n] < 0), S), "");
static_assert(__is_same_as(decltype((S())[n]->a), long), "");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe also add:

(S())[n=n]();
(S())[&n]();
(S())[n,n]();
(S())[this]();
(S())[this, n]();

SkipUntil(tok::r_square);

auto IsLambdaKWOrAttribute = [&]() {
// These are keyworks that can appear somewhere in a lambda declarator,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// These are keyworks that can appear somewhere in a lambda declarator,
// These are keywords that can appear somewhere in a lambda declarator,

@cor3ntin
Copy link
Contributor Author

Copy of a mail I sent to CWG

This issue comes from an old LLVM issue reported by Richard, which I thought I could fix in a few minutes.

I was wrong.

Consider the following:

(S())[]->A<int>; // #1

(S())[]->A<int> {return {};}; //#2

https://gcc.godbolt.org/z/a8j1ac9fj
This is yet another flavor of ambiguity caused by declarator syntax, which I don't think we have wording to resolve.

#1 is a parenthesized constructor call, followed by
calls to some operator[], operator->

#2 is an (invalid) C-cast of a lambda expression.

You would think that it is easy to tell these things apart because of the opening brace in #2.

However, this forces us to decide if the entity after the arrow is a type or an expression.
We also can't look into whatever the declaration context of the
type of result of a potential operator-> call that we have not resolved yet.

There is no clever way to balance the tokens as could be a valid
binary operator. eg

(S())[]->A < some expression;

Trying to determine if the thing after -> is a type may force us to do template instantiations.
So I think we need a rule in the spirit of https://eel.is/c++draft/dcl.ambig.res#2to deal with that case

We should resolve that in favor of the thing after the arrow
being a type, and forcing the expression to be a lambda, even though it might not be a valid one.

I think other cases can be resolved syntactically,
including (S())[] < expr; // We can tell expr is not a template parameter

No compiler gets that right currently, both GCC (afaict) and Clang assume [ after as cast is always a lambda

@cor3ntin cor3ntin marked this pull request as draft May 20, 2025 14:00
@cor3ntin
Copy link
Contributor Author

This is the wrong approach; we should just assume a lambda never follows what could be a cast to a function
see CWG2228

@cor3ntin cor3ntin closed this May 29, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

rejects-valid if parenthesized temporary is followed by array indexing
6 participants
Morty Proxy This is a proxified and sanitized view of the page, visit original site.