Skip to content

Commit ef47318

Browse files
committed
[Clang] Fix parsing of (auto(x)).
Allow auto(x) to appear in a parenthesis expression. The pattern (auto( can appear as part of a declarator, so the parser is modified to avoid the ambiguity, in a way consistent with the proposed resolution to CWG1223. Reviewed By: aaron.ballman, #clang-language-wg Differential Revision: https://reviews.llvm.org/D149276
1 parent 617c31c commit ef47318

File tree

7 files changed

+113
-30
lines changed

7 files changed

+113
-30
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,8 @@ Bug Fixes to C++ Support
449449
- Some predefined expressions are now treated as string literals in MSVC
450450
compatibility mode.
451451
(`#114 <https://github.com/llvm/llvm-project/issues/114>`_)
452+
- Fix parsing of `auto(x)`, when it is surrounded by parentheses.
453+
(`#62494 <https://github.com/llvm/llvm-project/issues/62494>`_)
452454

453455
Bug Fixes to AST Handling
454456
^^^^^^^^^^^^^^^^^^^^^^^^^

clang/include/clang/Parse/Parser.h

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2521,10 +2521,10 @@ class Parser : public CodeCompletionHandler {
25212521
enum TentativeCXXTypeIdContext {
25222522
TypeIdInParens,
25232523
TypeIdUnambiguous,
2524-
TypeIdAsTemplateArgument
2524+
TypeIdAsTemplateArgument,
2525+
TypeIdInTrailingReturnType,
25252526
};
25262527

2527-
25282528
/// isTypeIdInParens - Assumes that a '(' was parsed and now we want to know
25292529
/// whether the parens contain an expression or a type-id.
25302530
/// Returns true for a type-id and false for an expression.
@@ -2652,14 +2652,15 @@ class Parser : public CodeCompletionHandler {
26522652
TPResult TryParseProtocolQualifiers();
26532653
TPResult TryParsePtrOperatorSeq();
26542654
TPResult TryParseOperatorId();
2655-
TPResult TryParseInitDeclaratorList();
2655+
TPResult TryParseInitDeclaratorList(bool MayHaveTrailingReturnType = false);
26562656
TPResult TryParseDeclarator(bool mayBeAbstract, bool mayHaveIdentifier = true,
2657-
bool mayHaveDirectInit = false);
2657+
bool mayHaveDirectInit = false,
2658+
bool mayHaveTrailingReturnType = false);
26582659
TPResult TryParseParameterDeclarationClause(
26592660
bool *InvalidAsDeclaration = nullptr, bool VersusTemplateArg = false,
26602661
ImplicitTypenameContext AllowImplicitTypename =
26612662
ImplicitTypenameContext::No);
2662-
TPResult TryParseFunctionDeclarator();
2663+
TPResult TryParseFunctionDeclarator(bool MayHaveTrailingReturnType = false);
26632664
TPResult TryParseBracketDeclarator();
26642665
TPResult TryConsumeDeclarationSpecifier();
26652666

clang/lib/Parse/ParseDecl.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6494,8 +6494,9 @@ void Parser::ParseDirectDeclarator(Declarator &D) {
64946494
// that it's an initializer instead.
64956495
if (D.mayOmitIdentifier() && D.mayBeFollowedByCXXDirectInit()) {
64966496
RevertingTentativeParsingAction PA(*this);
6497-
if (TryParseDeclarator(true, D.mayHaveIdentifier(), true) ==
6498-
TPResult::False) {
6497+
if (TryParseDeclarator(true, D.mayHaveIdentifier(), true,
6498+
D.getDeclSpec().getTypeSpecType() == TST_auto) ==
6499+
TPResult::False) {
64996500
D.SetIdentifier(nullptr, Tok.getLocation());
65006501
goto PastIdentifier;
65016502
}

clang/lib/Parse/ParseTentative.cpp

Lines changed: 56 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,7 @@ Parser::TPResult Parser::TryConsumeDeclarationSpecifier() {
262262
/// attribute-specifier-seqopt type-specifier-seq declarator
263263
///
264264
Parser::TPResult Parser::TryParseSimpleDeclaration(bool AllowForRangeDecl) {
265+
bool DeclSpecifierIsAuto = Tok.is(tok::kw_auto);
265266
if (TryConsumeDeclarationSpecifier() == TPResult::Error)
266267
return TPResult::Error;
267268

@@ -277,7 +278,8 @@ Parser::TPResult Parser::TryParseSimpleDeclaration(bool AllowForRangeDecl) {
277278
assert(TPR == TPResult::False);
278279
}
279280

280-
TPResult TPR = TryParseInitDeclaratorList();
281+
TPResult TPR = TryParseInitDeclaratorList(
282+
/*mayHaveTrailingReturnType=*/DeclSpecifierIsAuto);
281283
if (TPR != TPResult::Ambiguous)
282284
return TPR;
283285

@@ -314,10 +316,15 @@ Parser::TPResult Parser::TryParseSimpleDeclaration(bool AllowForRangeDecl) {
314316
/// '{' initializer-list ','[opt] '}'
315317
/// '{' '}'
316318
///
317-
Parser::TPResult Parser::TryParseInitDeclaratorList() {
319+
Parser::TPResult
320+
Parser::TryParseInitDeclaratorList(bool MayHaveTrailingReturnType) {
318321
while (true) {
319322
// declarator
320-
TPResult TPR = TryParseDeclarator(false/*mayBeAbstract*/);
323+
TPResult TPR = TryParseDeclarator(
324+
/*mayBeAbstract=*/false,
325+
/*mayHaveIdentifier=*/true,
326+
/*mayHaveDirectInit=*/false,
327+
/*mayHaveTrailingReturnType=*/MayHaveTrailingReturnType);
321328
if (TPR != TPResult::Ambiguous)
322329
return TPR;
323330

@@ -532,13 +539,18 @@ Parser::isCXXConditionDeclarationOrInitStatement(bool CanBeInitStatement,
532539
RevertingTentativeParsingAction PA(*this);
533540

534541
// FIXME: A tag definition unambiguously tells us this is an init-statement.
542+
bool MayHaveTrailingReturnType = Tok.is(tok::kw_auto);
535543
if (State.update(TryConsumeDeclarationSpecifier()))
536544
return State.result();
537545
assert(Tok.is(tok::l_paren) && "Expected '('");
538546

539547
while (true) {
540548
// Consume a declarator.
541-
if (State.update(TryParseDeclarator(false/*mayBeAbstract*/)))
549+
if (State.update(TryParseDeclarator(
550+
/*mayBeAbstract=*/false,
551+
/*mayHaveIdentifier=*/true,
552+
/*mayHaveDirectInit=*/false,
553+
/*mayHaveTrailingReturnType=*/MayHaveTrailingReturnType)))
542554
return State.result();
543555

544556
// Attributes, asm label, or an initializer imply this is not an expression.
@@ -623,13 +635,16 @@ bool Parser::isCXXTypeId(TentativeCXXTypeIdContext Context, bool &isAmbiguous) {
623635
// We need tentative parsing...
624636

625637
RevertingTentativeParsingAction PA(*this);
638+
bool MayHaveTrailingReturnType = Tok.is(tok::kw_auto);
626639

627640
// type-specifier-seq
628641
TryConsumeDeclarationSpecifier();
629642
assert(Tok.is(tok::l_paren) && "Expected '('");
630643

631644
// declarator
632-
TPR = TryParseDeclarator(true/*mayBeAbstract*/, false/*mayHaveIdentifier*/);
645+
TPR = TryParseDeclarator(true /*mayBeAbstract*/, false /*mayHaveIdentifier*/,
646+
/*mayHaveDirectInit=*/false,
647+
MayHaveTrailingReturnType);
633648

634649
// In case of an error, let the declaration parsing code handle it.
635650
if (TPR == TPResult::Error)
@@ -658,6 +673,9 @@ bool Parser::isCXXTypeId(TentativeCXXTypeIdContext Context, bool &isAmbiguous) {
658673
TPR = TPResult::True;
659674
isAmbiguous = true;
660675

676+
} else if (Context == TypeIdInTrailingReturnType) {
677+
TPR = TPResult::True;
678+
isAmbiguous = true;
661679
} else
662680
TPR = TPResult::False;
663681
}
@@ -1042,7 +1060,8 @@ Parser::TPResult Parser::TryParseOperatorId() {
10421060
///
10431061
Parser::TPResult Parser::TryParseDeclarator(bool mayBeAbstract,
10441062
bool mayHaveIdentifier,
1045-
bool mayHaveDirectInit) {
1063+
bool mayHaveDirectInit,
1064+
bool mayHaveTrailingReturnType) {
10461065
// declarator:
10471066
// direct-declarator
10481067
// ptr-operator declarator
@@ -1084,7 +1103,7 @@ Parser::TPResult Parser::TryParseDeclarator(bool mayBeAbstract,
10841103
ImplicitTypenameContext::No))) { // 'int(int)' is a function.
10851104
// '(' parameter-declaration-clause ')' cv-qualifier-seq[opt]
10861105
// exception-specification[opt]
1087-
TPResult TPR = TryParseFunctionDeclarator();
1106+
TPResult TPR = TryParseFunctionDeclarator(mayHaveTrailingReturnType);
10881107
if (TPR != TPResult::Ambiguous)
10891108
return TPR;
10901109
} else {
@@ -1123,7 +1142,7 @@ Parser::TPResult Parser::TryParseDeclarator(bool mayBeAbstract,
11231142
// direct-declarator '(' parameter-declaration-clause ')'
11241143
// cv-qualifier-seq[opt] exception-specification[opt]
11251144
ConsumeParen();
1126-
TPR = TryParseFunctionDeclarator();
1145+
TPR = TryParseFunctionDeclarator(mayHaveTrailingReturnType);
11271146
} else if (Tok.is(tok::l_square)) {
11281147
// direct-declarator '[' constant-expression[opt] ']'
11291148
// direct-abstract-declarator[opt] '[' constant-expression[opt] ']'
@@ -1390,6 +1409,16 @@ Parser::isCXXDeclarationSpecifier(ImplicitTypenameContext AllowImplicitTypename,
13901409
return isCXXDeclarationSpecifier(ImplicitTypenameContext::Yes,
13911410
BracedCastResult, InvalidAsDeclSpec);
13921411

1412+
case tok::kw_auto: {
1413+
if (!getLangOpts().CPlusPlus23)
1414+
return TPResult::True;
1415+
if (NextToken().is(tok::l_brace))
1416+
return TPResult::False;
1417+
if (NextToken().is(tok::l_paren))
1418+
return TPResult::Ambiguous;
1419+
return TPResult::True;
1420+
}
1421+
13931422
case tok::coloncolon: { // ::foo::bar
13941423
const Token &Next = NextToken();
13951424
if (Next.isOneOf(tok::kw_new, // ::new
@@ -1423,7 +1452,6 @@ Parser::isCXXDeclarationSpecifier(ImplicitTypenameContext AllowImplicitTypename,
14231452
case tok::kw_static:
14241453
case tok::kw_extern:
14251454
case tok::kw_mutable:
1426-
case tok::kw_auto:
14271455
case tok::kw___thread:
14281456
case tok::kw_thread_local:
14291457
case tok::kw__Thread_local:
@@ -2023,7 +2051,10 @@ Parser::TPResult Parser::TryParseParameterDeclarationClause(
20232051

20242052
// declarator
20252053
// abstract-declarator[opt]
2026-
TPR = TryParseDeclarator(true/*mayBeAbstract*/);
2054+
TPR = TryParseDeclarator(/*mayBeAbstract=*/true,
2055+
/*mayHaveIdentifier=*/true,
2056+
/*mayHaveDirectInit=*/false,
2057+
/*mayHaveTrailingReturnType=*/true);
20272058
if (TPR != TPResult::Ambiguous)
20282059
return TPR;
20292060

@@ -2077,7 +2108,8 @@ Parser::TPResult Parser::TryParseParameterDeclarationClause(
20772108
/// exception-specification:
20782109
/// 'throw' '(' type-id-list[opt] ')'
20792110
///
2080-
Parser::TPResult Parser::TryParseFunctionDeclarator() {
2111+
Parser::TPResult
2112+
Parser::TryParseFunctionDeclarator(bool MayHaveTrailingReturnType) {
20812113
// The '(' is already parsed.
20822114

20832115
TPResult TPR = TryParseParameterDeclarationClause();
@@ -2122,6 +2154,19 @@ Parser::TPResult Parser::TryParseFunctionDeclarator() {
21222154
}
21232155
}
21242156

2157+
// attribute-specifier-seq
2158+
if (!TrySkipAttributes())
2159+
return TPResult::Ambiguous;
2160+
2161+
// trailing-return-type
2162+
if (Tok.is(tok::arrow) && MayHaveTrailingReturnType) {
2163+
if (TPR == TPResult::True)
2164+
return TPR;
2165+
ConsumeToken();
2166+
if (isCXXTypeId(TentativeCXXTypeIdContext::TypeIdInTrailingReturnType))
2167+
return TPResult::True;
2168+
}
2169+
21252170
return TPResult::Ambiguous;
21262171
}
21272172

clang/lib/Parse/Parser.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1950,7 +1950,7 @@ bool Parser::TryAnnotateTypeOrScopeToken(
19501950
assert((Tok.is(tok::identifier) || Tok.is(tok::coloncolon) ||
19511951
Tok.is(tok::kw_typename) || Tok.is(tok::annot_cxxscope) ||
19521952
Tok.is(tok::kw_decltype) || Tok.is(tok::annot_template_id) ||
1953-
Tok.is(tok::kw___super)) &&
1953+
Tok.is(tok::kw___super) || Tok.is(tok::kw_auto)) &&
19541954
"Cannot be a type or scope token!");
19551955

19561956
if (Tok.is(tok::kw_typename)) {

clang/test/Parser/cxx1z-decomposition.cpp

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
// RUN: %clang_cc1 -std=c++17 %s -verify -fcxx-exceptions
1+
// RUN: %clang_cc1 -std=c++17 %s -verify=expected,cxx17 -fcxx-exceptions
2+
// RUN: %clang_cc1 -std=c++2b %s -verify=expected,cxx2b -fcxx-exceptions
23
// RUN: not %clang_cc1 -std=c++17 %s -emit-llvm-only -fcxx-exceptions
34

45
struct S { int a, b, c; };
@@ -30,7 +31,7 @@ namespace ForRangeDecl {
3031
namespace OtherDecl {
3132
// A parameter-declaration is not a simple-declaration.
3233
// This parses as an array declaration.
33-
void f(auto [a, b, c]); // expected-error {{'auto' not allowed in function prototype}} expected-error {{'a'}}
34+
void f(auto [a, b, c]); // cxx17-error {{'auto' not allowed in function prototype}} expected-error {{'a'}}
3435

3536
void g() {
3637
// A condition is allowed as a Clang extension.
@@ -57,7 +58,7 @@ namespace OtherDecl {
5758
namespace GoodSpecifiers {
5859
void f() {
5960
int n[1];
60-
const volatile auto &[a] = n;
61+
const volatile auto &[a] = n; // cxx2b-warning {{volatile qualifier in structured binding declaration is deprecated}}
6162
}
6263
}
6364

@@ -67,8 +68,8 @@ namespace BadSpecifiers {
6768
struct S { int n; } s;
6869
void f() {
6970
// storage-class-specifiers
70-
static auto &[a] = n; // expected-warning {{declared 'static' is a C++20 extension}}
71-
thread_local auto &[b] = n; // expected-warning {{declared 'thread_local' is a C++20 extension}}
71+
static auto &[a] = n; // cxx17-warning {{declared 'static' is a C++20 extension}}
72+
thread_local auto &[b] = n; // cxx17-warning {{declared 'thread_local' is a C++20 extension}}
7273
extern auto &[c] = n; // expected-error {{cannot be declared 'extern'}} expected-error {{declaration of block scope identifier with linkage cannot have an initializer}}
7374
struct S {
7475
mutable auto &[d] = n; // expected-error {{not permitted in this context}}
@@ -85,16 +86,19 @@ namespace BadSpecifiers {
8586
}
8687

8788
static constexpr inline thread_local auto &[j1] = n; // expected-error {{cannot be declared with 'constexpr inline' specifiers}}
88-
static thread_local auto &[j2] = n; // expected-warning {{declared with 'static thread_local' specifiers is a C++20 extension}}
89+
static thread_local auto &[j2] = n; // cxx17-warning {{declared with 'static thread_local' specifiers is a C++20 extension}}
8990

9091
inline auto &[k] = n; // expected-error {{cannot be declared 'inline'}}
9192

9293
const int K = 5;
94+
auto ([c]) = s; // expected-error {{decomposition declaration cannot be declared with parentheses}}
9395
void g() {
9496
// defining-type-specifiers other than cv-qualifiers and 'auto'
9597
S [a] = s; // expected-error {{cannot be declared with type 'S'}}
9698
decltype(auto) [b] = s; // expected-error {{cannot be declared with type 'decltype(auto)'}}
97-
auto ([c]) = s; // expected-error {{cannot be declared with parentheses}}
99+
auto ([c2]) = s; // cxx17-error {{decomposition declaration cannot be declared with parenthese}} \
100+
// cxx2b-error {{use of undeclared identifier 'c2'}} \
101+
// cxx2b-error {{expected body of lambda expression}} \
98102
99103
// FIXME: This error is not very good.
100104
auto [d]() = s; // expected-error {{expected ';'}} expected-error {{expected expression}}

clang/test/Parser/cxx2b-auto-x.cpp

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,37 @@ struct looks_like_declaration {
1818

1919
using T = looks_like_declaration *;
2020
void f() { T(&a)->n = 1; }
21-
// FIXME: They should be deemed expressions without breaking function pointer
22-
// parameter declarations with trailing return types.
23-
// void g() { auto(&a)->n = 0; }
24-
// void h() { auto{&a}->n = 0; }
21+
void g() { auto(&a)->n = 0; } // cxx23-warning {{before C++23}} \
22+
// cxx20-error {{declaration of variable 'a' with deduced type 'auto (&)' requires an initializer}} \
23+
// cxx20-error {{expected ';' at end of declaration}}
24+
void h() { auto{&a}->n = 0; } // cxx23-warning {{before C++23}} \
25+
// cxx20-error {{expected unqualified-id}} \
26+
// cxx20-error {{expected expression}}
27+
28+
void e(auto (*p)(int y) -> decltype(y)) {}
29+
30+
struct M;
31+
struct S{
32+
S operator()();
33+
S* operator->();
34+
int N;
35+
int M;
36+
} s; // expected-note {{here}}
37+
38+
void test() {
39+
auto(s)()->N; // cxx23-warning {{expression result unused}} \
40+
// cxx23-warning {{before C++23}} \
41+
// cxx20-error {{unknown type name 'N'}}
42+
auto(s)()->M; // expected-error {{redefinition of 's' as different kind of symbol}}
43+
}
44+
45+
void test_paren() {
46+
int a = (auto(0)); // cxx23-warning {{before C++23}} \
47+
// cxx20-error {{expected expression}} \
48+
// cxx20-error {{expected ')'}} \
49+
// cxx20-note {{to match this '('}}
50+
int b = (auto{0}); // cxx23-warning {{before C++23}} \
51+
// cxx20-error {{expected expression}} \
52+
// cxx20-error {{expected ')'}} \
53+
// cxx20-note {{to match this '('}}
54+
}

0 commit comments

Comments
 (0)