Skip to content

[Clang][Parser] Add a warning to ambiguous uses of T...[N] types #116332

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

Merged
merged 5 commits into from
Dec 13, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/DiagnosticParseKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -718,6 +718,9 @@ def warn_empty_init_statement : Warning<
"has no effect">, InGroup<EmptyInitStatement>, DefaultIgnore;
def err_keyword_as_parameter : Error <
"invalid parameter name: '%0' is a keyword">;
def warn_pre_cxx26_ambiguous_pack_indexing_type : Warning<
"%0 is no longer a pack expansion but a pack "
"indexing type; add a name to specify a pack expansion">, InGroup<CXXPre26Compat>;

// C++ derived classes
def err_dup_virtual : Error<"duplicate 'virtual' in base specifier">;
Expand Down
16 changes: 15 additions & 1 deletion clang/lib/Parse/ParseExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,8 @@ bool Parser::ParseOptionalCXXScopeSpecifier(

else if (!HasScopeSpecifier && Tok.is(tok::identifier) &&
GetLookAheadToken(1).is(tok::ellipsis) &&
GetLookAheadToken(2).is(tok::l_square)) {
GetLookAheadToken(2).is(tok::l_square) &&
!GetLookAheadToken(3).is(tok::r_square)) {
SourceLocation Start = Tok.getLocation();
DeclSpec DS(AttrFactory);
SourceLocation CCLoc;
Expand All @@ -253,6 +254,19 @@ bool Parser::ParseOptionalCXXScopeSpecifier(
if (Type.isNull())
return false;

// C++ [cpp23.dcl.dcl-2]:
// Previously, T...[n] would declare a pack of function parameters.
// T...[n] is now a pack-index-specifier. [...] Valid C++ 2023 code that
// declares a pack of parameters without specifying a declarator-id
// becomes ill-formed.
//
// However, we still treat it as a pack indexing type because the use case
// is fairly rare, to ensure semantic consistency given that we have
// backported this feature to pre-C++26 modes.
if (!Tok.is(tok::coloncolon) && !getLangOpts().CPlusPlus26 &&
getCurScope()->isFunctionDeclarationScope())
Diag(Start, diag::warn_pre_cxx26_ambiguous_pack_indexing_type) << Type;

if (!TryConsumeToken(tok::coloncolon, CCLoc)) {
AnnotateExistingIndexedTypeNamePack(ParsedType::make(Type), Start,
EndLoc);
Expand Down
8 changes: 5 additions & 3 deletions clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p13.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,11 @@ template<typename T>
void b(T[] ...);

template<typename T>
void c(T ... []); // expected-error {{expected expression}} \
// expected-error {{'T' does not refer to the name of a parameter pack}} \
// expected-warning {{pack indexing is a C++2c extension}}
void c(T ... []); // expected-error {{type 'T[]' of function parameter pack does not contain any unexpanded parameter packs}}

// A function that takes pointers to each type T as arguments, after any decay.
template <typename ...T>
void g(T...[]);

template<typename T>
void d(T ... x[]); // expected-error{{type 'T[]' of function parameter pack does not contain any unexpanded parameter packs}}
Expand Down
1 change: 1 addition & 0 deletions clang/test/Parser/cxx0x-decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ struct MemberComponentOrder : Base {
void NoMissingSemicolonHere(struct S
[3]);
template<int ...N> void NoMissingSemicolonHereEither(struct S... [N]);
// expected-warning@-1 {{'S...[N]' is no longer a pack expansion but a pack indexing type; add a name to specify a pack expansion}} \
// expected-error@-1 {{'S' does not refer to the name of a parameter pack}} \
// expected-error@-1 {{declaration of anonymous struct must be a definition}} \
// expected-error@-1 {{expected parameter declarator}} \
Expand Down
5 changes: 2 additions & 3 deletions clang/test/Parser/cxx2c-pack-indexing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ struct S {
// expected-note {{to match this '['}} \
// expected-warning{{declaration does not declare anything}}

T...[]; // expected-error{{expected expression}} \
// expected-warning{{declaration does not declare anything}}
T...[]; // expected-error{{expected member name or ';' after declaration specifiers}}

void f(auto... v) {
decltype(v...[1]) a = v...[1];
Expand Down Expand Up @@ -65,7 +64,7 @@ int main() {
}


namespace GH11460 {
namespace GH111460 {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

eh?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was a typo. The patch - 1ad5f31#diff-64f7b2e488768fb71e127d736dc81d9d817ec14b759a8ec23fab7416d70a31aeR68 - was actually fixing 111460

template <typename... T>
requires( ); // expected-error {{expected expression}}
struct SS {
Expand Down
32 changes: 32 additions & 0 deletions clang/test/SemaCXX/cxx2c-pack-indexing-ext-diags.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,35 @@ void f(T... t) {
// cxx11-warning@+1 {{pack indexing is a C++2c extension}}
T...[0] c;
}

template <typename... T>
void g(T... [1]); // cxx11-warning {{'T...[1]' is no longer a pack expansion but a pack indexing type; add a name to specify a pack expansion}} \
// cxx11-warning {{pack indexing is a C++2c extension}} \
// cxx11-note {{candidate function template not viable}} \
// cxx26-warning {{pack indexing is incompatible with C++ standards before C++2c}} \
// cxx26-note {{candidate function template not viable}}

template <typename... T>
void h(T... param[1]);

template <class T>
struct S {
using type = T;
};

template <typename... T>
void h(typename T... [1]::type); // cxx11-warning {{pack indexing is a C++2c extension}} \
// cxx26-warning {{pack indexing is incompatible with C++ standards before C++2c}}

template <typename... T>
void x(T... [0]); // cxx11-warning {{'T...[0]' is no longer a pack expansion but a pack indexing type; add a name to specify a pack expansion}} \
// cxx11-warning {{pack indexing is a C++2c extension}} \
// cxx26-warning {{pack indexing is incompatible with C++ standards before C++2c}}

void call() {
g<int, double>(nullptr, nullptr); // cxx26-error {{no matching function for call to 'g'}} \
// cxx11-error {{no matching function for call to 'g'}}
h<int, double>(nullptr, nullptr);
h<S<int>, S<const char *>>("hello");
x<int*>(nullptr);
}
Loading