cor3ntin created this revision.
Herald added a project: All.
cor3ntin requested review of this revision.
Herald added a reviewer: jdoerfert.
Herald added subscribers: cfe-commits, jplehr, sstefan1.
Herald added a project: clang.
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.
Repository:
rG LLVM Github Monorepo
https://reviews.llvm.org/D149276
Files:
clang/docs/ReleaseNotes.rst
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/include/clang/Parse/Parser.h
clang/lib/Parse/ParseDecl.cpp
clang/lib/Parse/ParseTentative.cpp
clang/lib/Parse/Parser.cpp
clang/lib/Sema/SemaDeclCXX.cpp
clang/test/Parser/cxx1z-decomposition.cpp
clang/test/Parser/cxx2b-auto-x.cpp
Index: clang/test/Parser/cxx2b-auto-x.cpp
===================================================================
--- clang/test/Parser/cxx2b-auto-x.cpp
+++ clang/test/Parser/cxx2b-auto-x.cpp
@@ -18,7 +18,25 @@
using T = looks_like_declaration *;
void f() { T(&a)->n = 1; }
-// FIXME: They should be deemed expressions without breaking function pointer
-// parameter declarations with trailing return types.
-// void g() { auto(&a)->n = 0; }
-// void h() { auto{&a}->n = 0; }
+void g() { auto(&a)->n = 0; }
+void h() { auto{&a}->n = 0; }
+
+void e(auto (*p)(int y) -> decltype(y)) {}
+
+struct M;
+struct S{
+ S operator()();
+ S* operator->();
+ int N;
+ int M;
+} s; // expected-note {{here}}
+
+void test() {
+ auto(s)()->N; // expected-warning {{expression result unused}}
+ auto(s)()->M; // expected-error {{redefinition of 's' as different kind of symbol}}
+}
+
+void test_paren() {
+ int a = (auto(0));
+ int b = (auto{0});
+}
Index: clang/test/Parser/cxx1z-decomposition.cpp
===================================================================
--- clang/test/Parser/cxx1z-decomposition.cpp
+++ clang/test/Parser/cxx1z-decomposition.cpp
@@ -94,7 +94,9 @@
// defining-type-specifiers other than cv-qualifiers and 'auto'
S [a] = s; // expected-error {{cannot be declared with type 'S'}}
decltype(auto) [b] = s; // expected-error {{cannot be declared with type 'decltype(auto)'}}
- auto ([c]) = s; // expected-error {{cannot be declared with parentheses}}
+ auto ([c]) = s; // expected-error {{'auto' not allowed here}} \
+ // expected-error {{use of undeclared identifier 'c'}} \
+ // expected-error {{expected body of lambda expression}}
// FIXME: This error is not very good.
auto [d]() = s; // expected-error {{expected ';'}} expected-error {{expected expression}}
Index: clang/lib/Sema/SemaDeclCXX.cpp
===================================================================
--- clang/lib/Sema/SemaDeclCXX.cpp
+++ clang/lib/Sema/SemaDeclCXX.cpp
@@ -840,13 +840,7 @@
D.hasGroupingParens() || D.getNumTypeObjects() > 1 ||
(D.getNumTypeObjects() == 1 &&
D.getTypeObject(0).Kind != DeclaratorChunk::Reference)) {
- Diag(Decomp.getLSquareLoc(),
- (D.hasGroupingParens() ||
- (D.getNumTypeObjects() &&
- D.getTypeObject(0).Kind == DeclaratorChunk::Paren))
- ? diag::err_decomp_decl_parens
- : diag::err_decomp_decl_type)
- << R;
+ Diag(Decomp.getLSquareLoc(), diag::err_decomp_decl_type) << R;
// In most cases, there's no actual problem with an explicitly-specified
// type, but a function type won't work here, and ActOnVariableDeclarator
Index: clang/lib/Parse/Parser.cpp
===================================================================
--- clang/lib/Parse/Parser.cpp
+++ clang/lib/Parse/Parser.cpp
@@ -1950,7 +1950,7 @@
assert((Tok.is(tok::identifier) || Tok.is(tok::coloncolon) ||
Tok.is(tok::kw_typename) || Tok.is(tok::annot_cxxscope) ||
Tok.is(tok::kw_decltype) || Tok.is(tok::annot_template_id) ||
- Tok.is(tok::kw___super)) &&
+ Tok.is(tok::kw___super) || Tok.is(tok::kw_auto)) &&
"Cannot be a type or scope token!");
if (Tok.is(tok::kw_typename)) {
Index: clang/lib/Parse/ParseTentative.cpp
===================================================================
--- clang/lib/Parse/ParseTentative.cpp
+++ clang/lib/Parse/ParseTentative.cpp
@@ -234,7 +234,7 @@
return TPResult::Error;
if (Tok.is(tok::annot_cxxscope))
ConsumeAnnotationToken();
- if (Tok.is(tok::identifier))
+ if (Tok.isOneOf(tok::identifier, tok::kw_auto))
ConsumeToken();
else if (Tok.is(tok::annot_template_id))
ConsumeAnnotationToken();
@@ -265,6 +265,7 @@
/// attribute-specifier-seqopt type-specifier-seq declarator
///
Parser::TPResult Parser::TryParseSimpleDeclaration(bool AllowForRangeDecl) {
+ bool DeclSpecifierIsAuto = Tok.is(tok::kw_auto);
if (TryConsumeDeclarationSpecifier() == TPResult::Error)
return TPResult::Error;
@@ -280,7 +281,8 @@
assert(TPR == TPResult::False);
}
- TPResult TPR = TryParseInitDeclaratorList();
+ TPResult TPR = TryParseInitDeclaratorList(
+ /*mayHaveTrailingReturnType=*/DeclSpecifierIsAuto);
if (TPR != TPResult::Ambiguous)
return TPR;
@@ -317,10 +319,15 @@
/// '{' initializer-list ','[opt] '}'
/// '{' '}'
///
-Parser::TPResult Parser::TryParseInitDeclaratorList() {
+Parser::TPResult
+Parser::TryParseInitDeclaratorList(bool mayHaveTrailingReturnType) {
while (true) {
// declarator
- TPResult TPR = TryParseDeclarator(false/*mayBeAbstract*/);
+ TPResult TPR = TryParseDeclarator(
+ /*mayBeAbstract=*/false,
+ /*mayHaveIdentifier=*/true,
+ /*mayHaveDirectInit=*/false,
+ /*mayHaveTrailingReturnType=*/mayHaveTrailingReturnType);
if (TPR != TPResult::Ambiguous)
return TPR;
@@ -535,13 +542,18 @@
RevertingTentativeParsingAction PA(*this);
// FIXME: A tag definition unambiguously tells us this is an init-statement.
+ bool mayHaveTrailingReturnType = Tok.is(tok::kw_auto);
if (State.update(TryConsumeDeclarationSpecifier()))
return State.result();
assert(Tok.is(tok::l_paren) && "Expected '('");
while (true) {
// Consume a declarator.
- if (State.update(TryParseDeclarator(false/*mayBeAbstract*/)))
+ if (State.update(TryParseDeclarator(
+ /*mayBeAbstract=*/false,
+ /*mayHaveIdentifier=*/true,
+ /*mayHaveDirectInit=*/false,
+ /*mayHaveTrailingReturnType=*/mayHaveTrailingReturnType)))
return State.result();
// Attributes, asm label, or an initializer imply this is not an expression.
@@ -626,13 +638,16 @@
// We need tentative parsing...
RevertingTentativeParsingAction PA(*this);
+ bool MayHaveTrailingReturnType = Tok.is(tok::kw_auto);
// type-specifier-seq
TryConsumeDeclarationSpecifier();
assert(Tok.is(tok::l_paren) && "Expected '('");
// declarator
- TPR = TryParseDeclarator(true/*mayBeAbstract*/, false/*mayHaveIdentifier*/);
+ TPR = TryParseDeclarator(true /*mayBeAbstract*/, false /*mayHaveIdentifier*/,
+ /*mayHaveDirectInit=*/false,
+ MayHaveTrailingReturnType);
// In case of an error, let the declaration parsing code handle it.
if (TPR == TPResult::Error)
@@ -661,6 +676,9 @@
TPR = TPResult::True;
isAmbiguous = true;
+ } else if (Context == TypeIdInTrailingReturnType) {
+ TPR = TPResult::True;
+ isAmbiguous = true;
} else
TPR = TPResult::False;
}
@@ -1045,7 +1063,8 @@
///
Parser::TPResult Parser::TryParseDeclarator(bool mayBeAbstract,
bool mayHaveIdentifier,
- bool mayHaveDirectInit) {
+ bool mayHaveDirectInit,
+ bool mayHaveTrailingReturnType) {
// declarator:
// direct-declarator
// ptr-operator declarator
@@ -1087,7 +1106,7 @@
ImplicitTypenameContext::No))) { // 'int(int)' is a function.
// '(' parameter-declaration-clause ')' cv-qualifier-seq[opt]
// exception-specification[opt]
- TPResult TPR = TryParseFunctionDeclarator();
+ TPResult TPR = TryParseFunctionDeclarator(mayHaveTrailingReturnType);
if (TPR != TPResult::Ambiguous)
return TPR;
} else {
@@ -1126,7 +1145,7 @@
// direct-declarator '(' parameter-declaration-clause ')'
// cv-qualifier-seq[opt] exception-specification[opt]
ConsumeParen();
- TPR = TryParseFunctionDeclarator();
+ TPR = TryParseFunctionDeclarator(mayHaveTrailingReturnType);
} else if (Tok.is(tok::l_square)) {
// direct-declarator '[' constant-expression[opt] ']'
// direct-abstract-declarator[opt] '[' constant-expression[opt] ']'
@@ -1393,6 +1412,15 @@
return isCXXDeclarationSpecifier(ImplicitTypenameContext::Yes,
BracedCastResult, InvalidAsDeclSpec);
+ case tok::kw_auto: {
+ if (NextToken().is(tok::l_brace))
+ return TPResult::False;
+ if (NextToken().is(tok::l_paren)) {
+ return TPResult::Ambiguous;
+ }
+ return TPResult::True;
+ }
+
case tok::coloncolon: { // ::foo::bar
const Token &Next = NextToken();
if (Next.isOneOf(tok::kw_new, // ::new
@@ -1400,6 +1428,7 @@
return TPResult::False;
[[fallthrough]];
}
+
case tok::kw___super:
case tok::kw_decltype:
// Annotate typenames and C++ scope specifiers. If we get one, just
@@ -1426,7 +1455,6 @@
case tok::kw_static:
case tok::kw_extern:
case tok::kw_mutable:
- case tok::kw_auto:
case tok::kw___thread:
case tok::kw_thread_local:
case tok::kw__Thread_local:
@@ -2026,7 +2054,10 @@
// declarator
// abstract-declarator[opt]
- TPR = TryParseDeclarator(true/*mayBeAbstract*/);
+ TPR = TryParseDeclarator(/*mayBeAbstract=*/true,
+ /*mayHaveIdentifier=*/true,
+ /*mayHaveDirectInit=*/false,
+ /*mayHaveTrailingReturnType=*/true);
if (TPR != TPResult::Ambiguous)
return TPR;
@@ -2080,7 +2111,8 @@
/// exception-specification:
/// 'throw' '(' type-id-list[opt] ')'
///
-Parser::TPResult Parser::TryParseFunctionDeclarator() {
+Parser::TPResult
+Parser::TryParseFunctionDeclarator(bool mayHaveTrailingReturnType) {
// The '(' is already parsed.
TPResult TPR = TryParseParameterDeclarationClause();
@@ -2125,6 +2157,19 @@
}
}
+ // attribute-specifier-seq
+ if (!TrySkipAttributes())
+ return TPResult::Ambiguous;
+
+ // trailing-return-type
+ if (Tok.is(tok::arrow) && mayHaveTrailingReturnType) {
+ if (TPR == TPResult::True)
+ return TPR;
+ ConsumeToken();
+ if (isCXXTypeId(TentativeCXXTypeIdContext::TypeIdInTrailingReturnType))
+ return TPResult::True;
+ }
+
return TPResult::Ambiguous;
}
Index: clang/lib/Parse/ParseDecl.cpp
===================================================================
--- clang/lib/Parse/ParseDecl.cpp
+++ clang/lib/Parse/ParseDecl.cpp
@@ -6491,8 +6491,9 @@
// that it's an initializer instead.
if (D.mayOmitIdentifier() && D.mayBeFollowedByCXXDirectInit()) {
RevertingTentativeParsingAction PA(*this);
- if (TryParseDeclarator(true, D.mayHaveIdentifier(), true) ==
- TPResult::False) {
+ if (TryParseDeclarator(true, D.mayHaveIdentifier(), true,
+ D.getDeclSpec().getTypeSpecType() == TST_auto) ==
+ TPResult::False) {
D.SetIdentifier(nullptr, Tok.getLocation());
goto PastIdentifier;
}
Index: clang/include/clang/Parse/Parser.h
===================================================================
--- clang/include/clang/Parse/Parser.h
+++ clang/include/clang/Parse/Parser.h
@@ -2521,10 +2521,10 @@
enum TentativeCXXTypeIdContext {
TypeIdInParens,
TypeIdUnambiguous,
- TypeIdAsTemplateArgument
+ TypeIdAsTemplateArgument,
+ TypeIdInTrailingReturnType,
};
-
/// isTypeIdInParens - Assumes that a '(' was parsed and now we want to know
/// whether the parens contain an expression or a type-id.
/// Returns true for a type-id and false for an expression.
@@ -2652,14 +2652,15 @@
TPResult TryParseProtocolQualifiers();
TPResult TryParsePtrOperatorSeq();
TPResult TryParseOperatorId();
- TPResult TryParseInitDeclaratorList();
+ TPResult TryParseInitDeclaratorList(bool mayHaveTrailingReturnType = false);
TPResult TryParseDeclarator(bool mayBeAbstract, bool mayHaveIdentifier = true,
- bool mayHaveDirectInit = false);
+ bool mayHaveDirectInit = false,
+ bool mayHaveTrailingReturnType = false);
TPResult TryParseParameterDeclarationClause(
bool *InvalidAsDeclaration = nullptr, bool VersusTemplateArg = false,
ImplicitTypenameContext AllowImplicitTypename =
ImplicitTypenameContext::No);
- TPResult TryParseFunctionDeclarator();
+ TPResult TryParseFunctionDeclarator(bool mayHaveTrailingReturnType = false);
TPResult TryParseBracketDeclarator();
TPResult TryConsumeDeclarationSpecifier();
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -496,8 +496,6 @@
"declared type must be 'auto' or reference to 'auto'">;
def err_decomp_decl_constraint : Error<
"decomposition declaration cannot be declared with constrained 'auto'">;
-def err_decomp_decl_parens : Error<
- "decomposition declaration cannot be declared with parentheses">;
def err_decomp_decl_template : Error<
"decomposition declaration template not supported">;
def err_decomp_decl_not_alone : Error<
Index: clang/docs/ReleaseNotes.rst
===================================================================
--- clang/docs/ReleaseNotes.rst
+++ clang/docs/ReleaseNotes.rst
@@ -307,8 +307,8 @@
not a type concept.
- Fix crash when a doc comment contains a line splicing.
(`#62054 <https://github.com/llvm/llvm-project/issues/62054>`_)
-- Work around with a clang coverage crash which happens when visiting
- expressions/statements with invalid source locations in non-assert builds.
+- Work around with a clang coverage crash which happens when visiting
+ expressions/statements with invalid source locations in non-assert builds.
Assert builds may still see assertions triggered from this.
- Fix a failed assertion due to an invalid source location when trying to form
a coverage report for an unresolved constructor expression.
@@ -357,6 +357,7 @@
- Fix bug in the computation of the ``__has_unique_object_representations``
builtin for types with unnamed bitfields.
(`#61336 <https://github.com/llvm/llvm-project/issues/61336>`_)
+- Fix parsing of `auto(x)`, when it is surrounded by parenthesese.
Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits