Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
10 changes: 8 additions & 2 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -2590,8 +2590,14 @@ def err_auto_non_deduced_not_alone : Error<
def err_implied_std_initializer_list_not_found : Error<
"cannot deduce type of initializer list because std::initializer_list was "
"not found; include <initializer_list>">;
def err_malformed_std_initializer_list : Error<
"std::initializer_list must be a class template with a single type parameter">;
def err_malformed_std_initializer_list
: Error<"std::initializer_list %select{"
"must have exactly one template parameter|"
"cannot have associated constraints|"
"must have a type template parameter|"
"cannot have default template arguments|"
"cannot be a variadic template|"
"must be a class template}0">;
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you use "enum_select" format instead of "select" format?

See https://clang.llvm.org/docs/InternalsManual.html#formatting-a-diagnostic-argument.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, that is a lot nicer, thank you.

def err_auto_init_list_from_c : Error<
"cannot use %select{'auto'|<ERROR>|'__auto_type'}0 with "
"%select{initializer list|array}1 in C">;
Expand Down
49 changes: 39 additions & 10 deletions clang/lib/Sema/SemaDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12071,6 +12071,37 @@ NamespaceDecl *Sema::getOrCreateStdNamespace() {
return getStdNamespace();
}

/// Check that the template-head of this class template is acceptable for
/// a declaration of 'std::initializer_list', and optionally diagnose if
/// it is not.
/// \returns true if any issues were found.
static bool CheckStdInitializerList(Sema &S, ClassTemplateDecl *Template,
bool Diagnose) {
TemplateParameterList *Params = Template->getTemplateParameters();
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
TemplateParameterList *Params = Template->getTemplateParameters();
const TemplateParameterList *Params = Template->getTemplateParameters();

int ErrorKind = -1;

if (Params->size() != 1)
ErrorKind = 0; // must have exactly one template parameter
else if (Template->hasAssociatedConstraints())
ErrorKind = 1; // cannot have associated constraints
else {
auto *Param = dyn_cast<TemplateTypeParmDecl>(Params->getParam(0));
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
auto *Param = dyn_cast<TemplateTypeParmDecl>(Params->getParam(0));
const auto *Param = dyn_cast<TemplateTypeParmDecl>(Params->getParam(0));

I think the Template parameter can be const as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks; updated.

if (!Param)
ErrorKind = 2; // must have a type template parameter
else if (Param->hasDefaultArgument())
ErrorKind = 3; // cannot have default template arguments
else if (Param->isTemplateParameterPack())
ErrorKind = 4; // cannot be a variadic template
Copy link
Contributor

Choose a reason for hiding this comment

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

Please start all the comments with an uppercase latter and end them with a period.

else
return false;
}

if (Diagnose)
S.Diag(Template->getLocation(), diag::err_malformed_std_initializer_list)
<< Params->getSourceRange() << ErrorKind;
return true;
}

bool Sema::isStdInitializerList(QualType Ty, QualType *Element) {
assert(getLangOpts().CPlusPlus &&
"Looking for std::initializer_list outside of C++.");
Expand Down Expand Up @@ -12118,10 +12149,7 @@ bool Sema::isStdInitializerList(QualType Ty, QualType *Element) {
return false;
// This is a template called std::initializer_list, but is it the right
// template?
TemplateParameterList *Params = Template->getTemplateParameters();
if (Params->getMinRequiredArguments() != 1)
return false;
if (!isa<TemplateTypeParmDecl>(Params->getParam(0)))
if (CheckStdInitializerList(*this, Template, /*Diagnose=*/false))
return false;

// It's the right template.
Expand All @@ -12137,7 +12165,8 @@ bool Sema::isStdInitializerList(QualType Ty, QualType *Element) {
return true;
}

static ClassTemplateDecl *LookupStdInitializerList(Sema &S, SourceLocation Loc){
static ClassTemplateDecl *LookupStdInitializerList(Sema &S,
SourceLocation Loc) {
NamespaceDecl *Std = S.getStdNamespace();
if (!Std) {
S.Diag(Loc, diag::err_implied_std_initializer_list_not_found);
Expand All @@ -12155,16 +12184,16 @@ static ClassTemplateDecl *LookupStdInitializerList(Sema &S, SourceLocation Loc){
Result.suppressDiagnostics();
// We found something weird. Complain about the first thing we found.
NamedDecl *Found = *Result.begin();
S.Diag(Found->getLocation(), diag::err_malformed_std_initializer_list);
S.Diag(Found->getLocation(), diag::err_malformed_std_initializer_list)
<< 5 /* must be a class template */;
S.Diag(Loc, diag::note_used_here);
return nullptr;
}

// We found some template called std::initializer_list. Now verify that it's
// correct.
TemplateParameterList *Params = Template->getTemplateParameters();
if (Params->getMinRequiredArguments() != 1 ||
!isa<TemplateTypeParmDecl>(Params->getParam(0))) {
S.Diag(Template->getLocation(), diag::err_malformed_std_initializer_list);
if (CheckStdInitializerList(S, Template, /*Diagnose=*/true)) {
S.Diag(Loc, diag::note_used_here);
return nullptr;
}

Expand Down
77 changes: 77 additions & 0 deletions clang/test/SemaCXX/invalid-std-initializer-list.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// RUN: %clang_cc1 %s -verify=expected,type-param -std=c++23 -DTYPE_PARAM
// RUN: %clang_cc1 %s -verify=expected,others -std=c++23 -DCONSTANT_PARAM
// RUN: %clang_cc1 %s -verify=expected,others -std=c++23 -DTYPE_TEMPLATE_PARAM
// RUN: %clang_cc1 %s -verify=expected,others -std=c++23 -DDEFAULT_ARG
// RUN: %clang_cc1 %s -verify=expected,others -std=c++23 -DMULTIPLE_PARAMS
// RUN: %clang_cc1 %s -verify=expected,others -std=c++23 -DPARAM_PACK
// RUN: %clang_cc1 %s -verify=expected,others -std=c++23 -DCONSTRAINED_PARAM
// RUN: %clang_cc1 %s -verify=expected,others -std=c++23 -DREQUIRES_CLAUSE
// RUN: %clang_cc1 %s -verify=expected,others -std=c++23 -DNONCLASS_TEMPLATE
// RUN: %clang_cc1 %s -verify=expected,others -std=c++23 -DNONTEMPLATE

Comment on lines +1 to +11
Copy link
Contributor

Choose a reason for hiding this comment

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

Why isn't that a single test?

Copy link
Contributor

Choose a reason for hiding this comment

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

Because otherwise they're redefined

namespace std {

#ifdef TYPE_PARAM
template<class> class initializer_list;
// expected-note@-1 2 {{template is declared here}}
#elifdef CONSTANT_PARAM
template<int> class initializer_list;
// expected-error@-1 2 {{std::initializer_list must have a type template parameter}}
#elifdef TYPE_TEMPLATE_PARAM
template<template<class> class> class initializer_list;
// expected-error@-1 2 {{std::initializer_list must have a type template parameter}}
#elifdef DEFAULT_ARG
template<class = int> class initializer_list;
// expected-error@-1 2 {{std::initializer_list cannot have default template arguments}}
#elifdef MULTIPLE_PARAMS
template<class, class> class initializer_list;
// expected-error@-1 2 {{std::initializer_list must have exactly one template parameter}}
#elifdef PARAM_PACK
template<class...> class initializer_list;
// expected-error@-1 2 {{std::initializer_list cannot be a variadic template}}
#elifdef CONSTRAINED_PARAM
template<class> concept C = true;
template<C> class initializer_list;
// expected-error@-1 2 {{std::initializer_list cannot have associated constraints}}
#elifdef REQUIRES_CLAUSE
template<class> requires true class initializer_list;
// expected-error@-1 2 {{std::initializer_list cannot have associated constraints}}
#elifdef NONCLASS_TEMPLATE
template<class> class IL;
template<class T> using initializer_list = IL<T>;
// expected-error@-1 2 {{std::initializer_list must be a class template}}
#elifdef NONTEMPLATE
class initializer_list;
// expected-error@-1 2 {{std::initializer_list must be a class template}}
#else
#error Unexpected test kind
#endif

}

struct Test { // expected-note 2 {{candidate constructor}}
#ifdef CONSTANT_PARAM
Test(std::initializer_list<1>); // expected-note {{candidate constructor}}
#elifdef TYPE_TEMPLATE_PARAM
template<class> using A = double;
Test(std::initializer_list<A>); // expected-note {{candidate constructor}}
#elifdef MULTIPLE_PARAMS
Test(std::initializer_list<double, double>); // expected-note {{candidate constructor}}
#elifdef NONTEMPLATE
Test(std::initializer_list); // expected-note {{candidate constructor}}
#else
Test(std::initializer_list<double>); // expected-note {{candidate constructor}}
#endif
};
Test test {1.2, 3.4}; // expected-error {{no matching constructor}}

auto x = {1};
// type-param-error@-1 {{implicit instantiation of undefined template}}
// others-note@-2 {{used here}}

void f() {
for(int x : {1, 2});
// type-param-error@-1 {{implicit instantiation of undefined template}}
// type-param-error@-2 {{invalid range expression}}
// others-note@-3 {{used here}}
}