-
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
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
|
@@ -692,6 +692,70 @@ 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))) | ||||
return true; | ||||
|
||||
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 commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||
// 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 commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||
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); | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||||
} | ||||
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."); | ||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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), ""); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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](); |
||
} | ||
} | ||
#endif | ||
|
||
|
||
struct S { | ||
template <typename T> | ||
void m (T x =[0); // expected-error{{expected variable name or 'this' in lambda capture list}} | ||
|
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]