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
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions 1 clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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]


Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
4 changes: 4 additions & 0 deletions 4 clang/include/clang/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
6 changes: 4 additions & 2 deletions 6 clang/lib/Parse/ParseExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
64 changes: 64 additions & 0 deletions 64 clang/lib/Parse/ParseExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
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,

// 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.

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);
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.

}
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.");
Expand Down
82 changes: 82 additions & 0 deletions 82 clang/test/Parser/cxx0x-lambda-expressions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
}
};
Expand All @@ -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), "");
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]();

}
}
#endif


struct S {
template <typename T>
void m (T x =[0); // expected-error{{expected variable name or 'this' in lambda capture list}}
Expand Down
38 changes: 24 additions & 14 deletions 38 clang/test/Parser/cxx1z-constexpr-lambdas.cpp
Original file line number Diff line number Diff line change
@@ -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}}
Expand All @@ -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

}


44 changes: 25 additions & 19 deletions 44 clang/test/Parser/cxx2a-template-lambdas.cpp
Original file line number Diff line number Diff line change
@@ -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[]'}}
Expand Down
Loading
Morty Proxy This is a proxified and sanitized view of the page, visit original site.