Skip to content

Commit 28c3bf5

Browse files
authored
[Clang][Parser] Add a warning to ambiguous uses of T...[N] types (#116332)
`void f(T... [N])` is no longer treated as a function with a parameter of pack expansion type after the implementation of the pack indexing feature. This patch introduces a warning to clarify such cases while maintaining it as a pack indexing type in all language modes. Closes #115222
1 parent 5828aef commit 28c3bf5

File tree

6 files changed

+58
-7
lines changed

6 files changed

+58
-7
lines changed

clang/include/clang/Basic/DiagnosticParseKinds.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -721,6 +721,9 @@ def warn_empty_init_statement : Warning<
721721
"has no effect">, InGroup<EmptyInitStatement>, DefaultIgnore;
722722
def err_keyword_as_parameter : Error <
723723
"invalid parameter name: '%0' is a keyword">;
724+
def warn_pre_cxx26_ambiguous_pack_indexing_type : Warning<
725+
"%0 is no longer a pack expansion but a pack "
726+
"indexing type; add a name to specify a pack expansion">, InGroup<CXXPre26Compat>;
724727

725728
// C++ derived classes
726729
def err_dup_virtual : Error<"duplicate 'virtual' in base specifier">;

clang/lib/Parse/ParseExprCXX.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,8 @@ bool Parser::ParseOptionalCXXScopeSpecifier(
238238

239239
else if (!HasScopeSpecifier && Tok.is(tok::identifier) &&
240240
GetLookAheadToken(1).is(tok::ellipsis) &&
241-
GetLookAheadToken(2).is(tok::l_square)) {
241+
GetLookAheadToken(2).is(tok::l_square) &&
242+
!GetLookAheadToken(3).is(tok::r_square)) {
242243
SourceLocation Start = Tok.getLocation();
243244
DeclSpec DS(AttrFactory);
244245
SourceLocation CCLoc;
@@ -253,6 +254,19 @@ bool Parser::ParseOptionalCXXScopeSpecifier(
253254
if (Type.isNull())
254255
return false;
255256

257+
// C++ [cpp23.dcl.dcl-2]:
258+
// Previously, T...[n] would declare a pack of function parameters.
259+
// T...[n] is now a pack-index-specifier. [...] Valid C++ 2023 code that
260+
// declares a pack of parameters without specifying a declarator-id
261+
// becomes ill-formed.
262+
//
263+
// However, we still treat it as a pack indexing type because the use case
264+
// is fairly rare, to ensure semantic consistency given that we have
265+
// backported this feature to pre-C++26 modes.
266+
if (!Tok.is(tok::coloncolon) && !getLangOpts().CPlusPlus26 &&
267+
getCurScope()->isFunctionDeclarationScope())
268+
Diag(Start, diag::warn_pre_cxx26_ambiguous_pack_indexing_type) << Type;
269+
256270
if (!TryConsumeToken(tok::coloncolon, CCLoc)) {
257271
AnnotateExistingIndexedTypeNamePack(ParsedType::make(Type), Start,
258272
EndLoc);

clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p13.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,11 @@ template<typename T>
5757
void b(T[] ...);
5858

5959
template<typename T>
60-
void c(T ... []); // expected-error {{expected expression}} \
61-
// expected-error {{'T' does not refer to the name of a parameter pack}} \
62-
// expected-warning {{pack indexing is a C++2c extension}}
60+
void c(T ... []); // expected-error {{type 'T[]' of function parameter pack does not contain any unexpanded parameter packs}}
61+
62+
// A function that takes pointers to each type T as arguments, after any decay.
63+
template <typename ...T>
64+
void g(T...[]);
6365

6466
template<typename T>
6567
void d(T ... x[]); // expected-error{{type 'T[]' of function parameter pack does not contain any unexpanded parameter packs}}

clang/test/Parser/cxx0x-decl.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ struct MemberComponentOrder : Base {
214214
void NoMissingSemicolonHere(struct S
215215
[3]);
216216
template<int ...N> void NoMissingSemicolonHereEither(struct S... [N]);
217+
// expected-warning@-1 {{'S...[N]' is no longer a pack expansion but a pack indexing type; add a name to specify a pack expansion}} \
217218
// expected-error@-1 {{'S' does not refer to the name of a parameter pack}} \
218219
// expected-error@-1 {{declaration of anonymous struct must be a definition}} \
219220
// expected-error@-1 {{expected parameter declarator}} \

clang/test/Parser/cxx2c-pack-indexing.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@ struct S {
1212
// expected-note {{to match this '['}} \
1313
// expected-warning{{declaration does not declare anything}}
1414

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

1817
void f(auto... v) {
1918
decltype(v...[1]) a = v...[1];
@@ -65,7 +64,7 @@ int main() {
6564
}
6665

6766

68-
namespace GH11460 {
67+
namespace GH111460 {
6968
template <typename... T>
7069
requires( ); // expected-error {{expected expression}}
7170
struct SS {

clang/test/SemaCXX/cxx2c-pack-indexing-ext-diags.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,35 @@ void f(T... t) {
1919
// cxx11-warning@+1 {{pack indexing is a C++2c extension}}
2020
T...[0] c;
2121
}
22+
23+
template <typename... T>
24+
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}} \
25+
// cxx11-warning {{pack indexing is a C++2c extension}} \
26+
// cxx11-note {{candidate function template not viable}} \
27+
// cxx26-warning {{pack indexing is incompatible with C++ standards before C++2c}} \
28+
// cxx26-note {{candidate function template not viable}}
29+
30+
template <typename... T>
31+
void h(T... param[1]);
32+
33+
template <class T>
34+
struct S {
35+
using type = T;
36+
};
37+
38+
template <typename... T>
39+
void h(typename T... [1]::type); // cxx11-warning {{pack indexing is a C++2c extension}} \
40+
// cxx26-warning {{pack indexing is incompatible with C++ standards before C++2c}}
41+
42+
template <typename... T>
43+
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}} \
44+
// cxx11-warning {{pack indexing is a C++2c extension}} \
45+
// cxx26-warning {{pack indexing is incompatible with C++ standards before C++2c}}
46+
47+
void call() {
48+
g<int, double>(nullptr, nullptr); // cxx26-error {{no matching function for call to 'g'}} \
49+
// cxx11-error {{no matching function for call to 'g'}}
50+
h<int, double>(nullptr, nullptr);
51+
h<S<int>, S<const char *>>("hello");
52+
x<int*>(nullptr);
53+
}

0 commit comments

Comments
 (0)