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

Commit f67af47

Browse filesBrowse files
committed
[Clang] Fix parsing of expressions of the form (T())[/*...*/]
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 #20723
1 parent 6fc0312 commit f67af47
Copy full SHA for f67af47

File tree

Expand file treeCollapse file tree

8 files changed

+278
-90
lines changed
Filter options
Expand file treeCollapse file tree

8 files changed

+278
-90
lines changed

‎clang/docs/ReleaseNotes.rst

Copy file name to clipboardExpand all lines: clang/docs/ReleaseNotes.rst
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -710,6 +710,7 @@ Bug Fixes to C++ Support
710710
- Clang now correctly parses arbitrary order of ``[[]]``, ``__attribute__`` and ``alignas`` attributes for declarations (#GH133107)
711711
- Fixed a crash when forming an invalid function type in a dependent context. (#GH138657) (#GH115725) (#GH68852)
712712
- Clang no longer segfaults when there is a configuration mismatch between modules and their users (http://crbug.com/400353616).
713+
- Fix parsing of expressions of the form ``((T))[expr]``. (#GH20723)
713714

714715
Bug Fixes to AST Handling
715716
^^^^^^^^^^^^^^^^^^^^^^^^^

‎clang/include/clang/Parse/Parser.h

Copy file name to clipboardExpand all lines: clang/include/clang/Parse/Parser.h
+4Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4639,6 +4639,10 @@ class Parser : public CodeCompletionHandler {
46394639
ParseLambdaIntroducer(LambdaIntroducer &Intro,
46404640
LambdaIntroducerTentativeParse *Tentative = nullptr);
46414641

4642+
/// Tries to determine if an expression of the form (S())[...]...
4643+
/// is a type-cast followed by a lambda, or a subscript expression
4644+
bool IsLambdaAfterTypeCast();
4645+
46424646
/// ParseLambdaExpressionAfterIntroducer - Parse the rest of a lambda
46434647
/// expression.
46444648
ExprResult ParseLambdaExpressionAfterIntroducer(LambdaIntroducer &Intro);

‎clang/lib/Parse/ParseExpr.cpp

Copy file name to clipboardExpand all lines: clang/lib/Parse/ParseExpr.cpp
+4-2Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1585,8 +1585,10 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind,
15851585
}
15861586
break;
15871587
}
1588-
Res = ParseLambdaExpression();
1589-
break;
1588+
if (isTypeCast != TypeCastState::IsTypeCast || IsLambdaAfterTypeCast()) {
1589+
Res = ParseLambdaExpression();
1590+
break;
1591+
}
15901592
}
15911593
if (getLangOpts().ObjC) {
15921594
Res = ParseObjCMessageExpression();

‎clang/lib/Parse/ParseExprCXX.cpp

Copy file name to clipboardExpand all lines: clang/lib/Parse/ParseExprCXX.cpp
+62Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -692,6 +692,68 @@ ExprResult Parser::ParseLambdaExpression() {
692692
return ParseLambdaExpressionAfterIntroducer(Intro);
693693
}
694694

695+
bool Parser::IsLambdaAfterTypeCast() {
696+
assert(getLangOpts().CPlusPlus && Tok.is(tok::l_square) &&
697+
"Not at the start of a possible lambda expression.");
698+
RevertingTentativeParsingAction TPA(*this);
699+
ConsumeBracket();
700+
// skip the introducer
701+
if (Tok.is(tok::equal) ||
702+
(Tok.is(tok::amp) && NextToken().isOneOf(tok::comma, tok::r_square)))
703+
SkipUntil(tok::r_square);
704+
705+
auto IsLambdaKWOrAttribute = [&]() {
706+
// These keyworks that either can appear somewhere in a lambda declarator,
707+
// or cannot appear in a cast expression and we recover in favor of lambdas
708+
if (Tok.isOneOf(tok::kw___declspec, tok::kw___noinline__, tok::kw_noexcept,
709+
tok::kw_throw, tok::kw_mutable, tok::kw___attribute,
710+
tok::kw_constexpr, tok::kw_consteval, tok::kw_static,
711+
tok::kw_inline, tok::kw_extern, tok::kw___private,
712+
tok::kw___global, tok::kw___local, tok::kw___constant,
713+
tok::kw___generic, tok::kw_groupshared, tok::kw_requires,
714+
tok::kw_noexcept))
715+
return true;
716+
return Tok.isRegularKeywordAttribute() ||
717+
isCXX11AttributeSpecifier() !=
718+
CXX11AttributeKind::NotAttributeSpecifier;
719+
};
720+
721+
if (Tok.is(tok::l_brace) || IsLambdaKWOrAttribute())
722+
return true;
723+
724+
// This is a generic lambda,
725+
if (Tok.is(tok::less)) {
726+
ConsumeToken();
727+
// Common cases. We consider <> as an invalid lambda.
728+
if (Tok.isOneOf(tok::greater, tok::kw_typename, tok::kw_auto,
729+
tok::kw_template))
730+
return true;
731+
if (isStartOfTemplateTypeParameter() != TPResult::False)
732+
return true;
733+
return isCXXDeclarationSpecifier(ImplicitTypenameContext::Yes) !=
734+
TPResult::False;
735+
}
736+
// skip the parameter list
737+
if (Tok.is(tok::l_paren)) {
738+
ConsumeParen();
739+
SkipUntil(tok::r_paren);
740+
}
741+
742+
if (IsLambdaKWOrAttribute())
743+
return true;
744+
745+
if (Tok.is(tok::arrow)) {
746+
ConsumeToken();
747+
// These cases are always id-expressions
748+
if (Tok.isOneOf(tok::kw_template, tok::kw_operator, tok::tilde))
749+
return false;
750+
if (!Tok.is(tok::identifier))
751+
return true;
752+
return isCXXTypeId(TentativeCXXTypeIdContext::InTrailingReturnType);
753+
}
754+
return Tok.is(tok::l_brace);
755+
}
756+
695757
ExprResult Parser::TryParseLambdaExpression() {
696758
assert(getLangOpts().CPlusPlus && Tok.is(tok::l_square) &&
697759
"Not at the start of a possible lambda expression.");

‎clang/test/Parser/cxx0x-lambda-expressions.cpp

Copy file name to clipboardExpand all lines: clang/test/Parser/cxx0x-lambda-expressions.cpp
+82Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,33 @@ class C {
4040
return 1;
4141
}
4242

43+
int type_cast() {
44+
int foo, bar;
45+
46+
(void)[]; // expected-error {{expected expression}}
47+
(void)[+] {}; // expected-error {{expected variable name or 'this' in lambda capture list}}
48+
(void)[foo+] {}; // expected-error {{expected ',' or ']' in lambda capture list}}
49+
(void)[foo,&this] {}; // expected-error {{'this' cannot be captured by reference}}
50+
(void)[&this] {}; // expected-error {{'this' cannot be captured by reference}}
51+
(void)[&,] {}; // expected-error {{expected variable name or 'this' in lambda capture list}}
52+
(void)[=,] {}; // expected-error {{expected variable name or 'this' in lambda capture list}}
53+
(void)[] {};
54+
(void)[=] (int i) {};
55+
(void)[&] (int) mutable -> void {};
56+
(void)[foo,bar] () { return 3; };
57+
(void)[=,&foo] () {};
58+
(void)[&,foo] () {};
59+
(void)[this] () {};
60+
(void)[] () -> class C { return C(); };
61+
(void)[] () -> enum E { return e; };
62+
63+
(void)[] -> int { return 0; }; // cxx23ext-warning {{lambda without a parameter clause is a C++23 extension}}
64+
(void)[] mutable -> int { return 0; }; // cxx23ext-warning {{is a C++23 extension}}
65+
66+
(void)[](int) -> {}; // PR13652 expected-error {{expected a type}}
67+
return 1;
68+
}
69+
4370
void designator_or_lambda() {
4471
typedef int T;
4572
const int b = 0;
@@ -125,10 +152,46 @@ class C {
125152
[][[]]{}; // cxx23ext-warning {{an attribute specifier sequence in this position is a C++23 extension}}
126153
}
127154

155+
void attributes_type_cast() {
156+
(void)[] __attribute__((noreturn)){}; // cxx23ext-warning {{lambda without a parameter clause is a C++23 extension}}
157+
158+
(void)[]() [[]]
159+
mutable {}; // expected-error {{expected body of lambda expression}}
160+
161+
(void)[]() [[]] {};
162+
(void)[]() [[]] -> void {};
163+
(void)[]() mutable [[]] -> void {};
164+
#if __cplusplus >= 201103L
165+
(void)[]() mutable noexcept [[]] -> void {};
166+
#endif
167+
168+
// Testing GNU-style attributes on lambdas -- the attribute is specified
169+
// before the mutable specifier instead of after (unlike C++11).
170+
(void)[]() __attribute__((noreturn)) mutable { while(1); };
171+
(void)[]() mutable
172+
__attribute__((noreturn)) { while(1); }; // expected-error {{expected body of lambda expression}}
173+
174+
// Testing support for P2173 on adding attributes to the declaration
175+
// rather than the type.
176+
(void)[][[]](){}; // cxx23ext-warning {{an attribute specifier sequence in this position is a C++23 extension}}
177+
178+
(void)[]<typename>[[]](){}; // cxx20ext-warning {{explicit template parameter list for lambdas is a C++20 extension}}
179+
// cxx23ext-warning@-1 {{an attribute specifier sequence in this position is a C++23 extension}}
180+
181+
(void)[][[]]{}; // cxx23ext-warning {{an attribute specifier sequence in this position is a C++23 extension}}
182+
}
183+
128184
void missing_parens() {
129185
[] mutable {}; // cxx23ext-warning {{is a C++23 extension}}
130186
#if __cplusplus >= 201103L
131187
[] noexcept {}; // cxx23ext-warning {{is a C++23 extension}}
188+
#endif
189+
}
190+
191+
void missing_parens_type_cast() {
192+
(void)[] mutable {}; // cxx23ext-warning {{is a C++23 extension}}
193+
#if __cplusplus >= 201103L
194+
(void)[] noexcept {}; // cxx23ext-warning {{is a C++23 extension}}
132195
#endif
133196
}
134197
};
@@ -150,6 +213,25 @@ struct S {
150213
};
151214
}
152215

216+
#if __cplusplus >= 201103L
217+
namespace GH20723 {
218+
struct S {
219+
S operator[](int);
220+
S operator()();
221+
S operator<(int);
222+
S* operator->();
223+
long a;
224+
};
225+
int n;
226+
void f() {
227+
static_assert(__is_same_as(decltype((S())[n]()), S), "");
228+
static_assert(__is_same_as(decltype((S())[n] < 0), S), "");
229+
static_assert(__is_same_as(decltype((S())[n]->a), long), "");
230+
}
231+
}
232+
#endif
233+
234+
153235
struct S {
154236
template <typename T>
155237
void m (T x =[0); // expected-error{{expected variable name or 'this' in lambda capture list}}
+24-14Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,34 @@
1-
// RUN: %clang_cc1 -std=c++23 %s -verify
2-
// RUN: %clang_cc1 -std=c++20 %s -verify
3-
// RUN: %clang_cc1 -std=c++17 %s -verify
4-
// RUN: %clang_cc1 -std=c++14 %s -verify
5-
// RUN: %clang_cc1 -std=c++11 %s -verify
1+
// RUN: %clang_cc1 -std=c++23 %s -verify -Wno-unused "-DTYPE_CAST="
2+
// RUN: %clang_cc1 -std=c++20 %s -verify -Wno-unused "-DTYPE_CAST="
3+
// RUN: %clang_cc1 -std=c++17 %s -verify -Wno-unused "-DTYPE_CAST="
4+
// RUN: %clang_cc1 -std=c++14 %s -verify -Wno-unused "-DTYPE_CAST="
5+
// RUN: %clang_cc1 -std=c++11 %s -verify -Wno-unused "-DTYPE_CAST="
66

7-
auto XL0 = [] constexpr { return true; };
7+
// RUN: %clang_cc1 -std=c++23 %s -verify "-DTYPE_CAST=(void)"
8+
// RUN: %clang_cc1 -std=c++20 %s -verify "-DTYPE_CAST=(void)"
9+
// RUN: %clang_cc1 -std=c++17 %s -verify "-DTYPE_CAST=(void)"
10+
// RUN: %clang_cc1 -std=c++14 %s -verify "-DTYPE_CAST=(void)"
11+
// RUN: %clang_cc1 -std=c++11 %s -verify "-DTYPE_CAST=(void)"
12+
13+
void test() {
14+
15+
TYPE_CAST [] constexpr { return true; };
816
#if __cplusplus <= 201402L
917
// expected-warning@-2 {{is a C++17 extension}}
1018
#endif
1119
#if __cplusplus <= 202002L
1220
// expected-warning@-5 {{lambda without a parameter clause is a C++23 extension}}
1321
#endif
14-
auto XL1 = []() mutable //
22+
TYPE_CAST []() mutable //
1523
mutable // expected-error{{cannot appear multiple times}}
1624
mutable {}; // expected-error{{cannot appear multiple times}}
1725

1826
#if __cplusplus > 201402L
19-
auto XL2 = [] () constexpr mutable constexpr { }; //expected-error{{cannot appear multiple times}}
20-
auto L = []() mutable constexpr { };
21-
auto L2 = []() constexpr { };
22-
auto L4 = []() constexpr mutable { };
23-
auto XL16 = [] () constexpr
27+
TYPE_CAST [] () constexpr mutable constexpr { }; //expected-error{{cannot appear multiple times}}
28+
TYPE_CAST []() mutable constexpr { };
29+
TYPE_CAST []() constexpr { };
30+
TYPE_CAST []() constexpr mutable { };
31+
TYPE_CAST [] () constexpr
2432
mutable
2533
constexpr //expected-error{{cannot appear multiple times}}
2634
mutable //expected-error{{cannot appear multiple times}}
@@ -31,8 +39,10 @@ auto XL16 = [] () constexpr
3139

3240
#else
3341
auto L = []() mutable constexpr {return 0; }; //expected-warning{{is a C++17 extension}}
34-
auto L2 = []() constexpr { return 0;};//expected-warning{{is a C++17 extension}}
35-
auto L4 = []() constexpr mutable { return 0; }; //expected-warning{{is a C++17 extension}}
42+
TYPE_CAST []() constexpr { return 0;};//expected-warning{{is a C++17 extension}}
43+
TYPE_CAST []() constexpr mutable { return 0; }; //expected-warning{{is a C++17 extension}}
3644
#endif
3745

46+
}
47+
3848

‎clang/test/Parser/cxx2a-template-lambdas.cpp

Copy file name to clipboardExpand all lines: clang/test/Parser/cxx2a-template-lambdas.cpp
+25-19Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,44 @@
1-
// RUN: %clang_cc1 -std=c++23 %s -verify
2-
// RUN: %clang_cc1 -std=c++20 %s -verify
1+
// RUN: %clang_cc1 -std=c++23 %s -verify -Wno-unused "-DTYPE_CAST="
2+
// RUN: %clang_cc1 -std=c++20 %s -verify -Wno-unused "-DTYPE_CAST="
3+
// RUN: %clang_cc1 -std=c++23 %s -verify "-DTYPE_CAST=(void)"
4+
// RUN: %clang_cc1 -std=c++20 %s -verify "-DTYPE_CAST=(void)"
35

4-
auto L0 = []<> { }; //expected-error {{cannot be empty}}
6+
void test() {
57

6-
auto L1 = []<typename T1, typename T2> { };
7-
auto L2 = []<typename T1, typename T2>(T1 arg1, T2 arg2) -> T1 { };
8-
auto L3 = []<typename T>(auto arg) { T t; };
9-
auto L4 = []<int I>() { };
8+
TYPE_CAST []<> { }; //expected-error {{cannot be empty}}
9+
10+
TYPE_CAST []<typename T1, typename T2> { };
11+
TYPE_CAST []<typename T1, typename T2>(T1 arg1, T2 arg2) -> T1 { };
12+
TYPE_CAST []<typename T>(auto arg) { T t; };
13+
TYPE_CAST []<int I>() { };
1014

1115
// http://llvm.org/PR49736
12-
auto L5 = []<auto>(){};
13-
auto L6 = []<auto>{};
14-
auto L7 = []<auto>() noexcept {};
15-
auto L8 = []<auto> noexcept {};
16+
TYPE_CAST []<auto>(){};
17+
TYPE_CAST []<auto>{};
18+
TYPE_CAST []<auto>() noexcept {};
19+
TYPE_CAST []<auto> noexcept {};
1620
#if __cplusplus <= 202002L
1721
// expected-warning@-2 {{lambda without a parameter clause is a C++23 extension}}
1822
#endif
19-
auto L9 = []<auto> requires true {};
20-
auto L10 = []<auto> requires true(){};
21-
auto L11 = []<auto> requires true() noexcept {};
22-
auto L12 = []<auto> requires true noexcept {};
23+
TYPE_CAST []<auto> requires true {};
24+
TYPE_CAST []<auto> requires true(){};
25+
TYPE_CAST []<auto> requires true() noexcept {};
26+
TYPE_CAST []<auto> requires true noexcept {};
2327
#if __cplusplus <= 202002L
2428
// expected-warning@-2 {{is a C++23 extension}}
2529
#endif
26-
auto L13 = []<auto>() noexcept requires true {};
27-
auto L14 = []<auto> requires true() noexcept requires true {};
30+
TYPE_CAST []<auto>() noexcept requires true {};
31+
TYPE_CAST []<auto> requires true() noexcept requires true {};
2832

29-
auto XL0 = []<auto> noexcept requires true {}; // expected-error {{expected body of lambda expression}}
30-
auto XL1 = []<auto> requires true noexcept requires true {}; // expected-error {{expected body}}
33+
TYPE_CAST []<auto> noexcept requires true {}; // expected-error {{expected body of lambda expression}}
34+
TYPE_CAST []<auto> requires true noexcept requires true {}; // expected-error {{expected body}}
3135
#if __cplusplus <= 202002L
3236
// expected-warning@-3 {{is a C++23 extension}}
3337
// expected-warning@-3 {{is a C++23 extension}}
3438
#endif
3539

40+
}
41+
3642
namespace GH64962 {
3743
void f() {
3844
[] <typename T>(T i) -> int[] // expected-error {{function cannot return array type 'int[]'}}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.