-
Notifications
You must be signed in to change notification settings - Fork 13.6k
[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
Conversation
@llvm/pr-subscribers-clang Author: cor3ntin (cor3ntin) ChangesWhen tentatively parsing cast expressions, we were assuming 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:
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]
|
Note that this does not address the case where |
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
f67af47
to
7b4f82c
Compare
clang/lib/Parse/ParseExprCXX.cpp
Outdated
return false; | ||
if (!Tok.is(tok::identifier)) | ||
return true; | ||
return isCXXTypeId(TentativeCXXTypeIdContext::InTrailingReturnType); |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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), ""); |
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// These are keyworks that can appear somewhere in a lambda declarator, | |
// These are keywords that can appear somewhere in a lambda declarator, |
Copy of a mail I sent to CWGThis 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 #1 is a parenthesized constructor call, followed by #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. There is no clever way to balance the tokens as could be a valid
Trying to determine if the thing after -> is a type may force us to do template instantiations. We should resolve that in favor of the thing after the arrow I think other cases can be resolved syntactically, No compiler gets that right currently, both GCC (afaict) and Clang assume [ after as cast is always a lambda |
This is the wrong approach; we should just assume a lambda never follows what could be a cast to a function |
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