diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 5e45482584946..575f13336e441 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -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 ">; -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 %enum_select{" + "%TooManyParams{must have exactly one template parameter}|" + "%Constrained{cannot have associated constraints}|" + "%BadParamKind{must have a type template parameter}|" + "%DefaultArg{cannot have default template arguments}|" + "%ParamPack{cannot be a variadic template}|" + "%BadEntityKind{must be a class template}}0">; def err_auto_init_list_from_c : Error< "cannot use %select{'auto'||'__auto_type'}0 with " "%select{initializer list|array}1 in C">; diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 676d53a1f4b45..b4dd16bf1e343 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -27,6 +27,7 @@ #include "clang/AST/TypeLoc.h" #include "clang/AST/TypeOrdering.h" #include "clang/Basic/AttributeCommonInfo.h" +#include "clang/Basic/DiagnosticSema.h" #include "clang/Basic/PartialDiagnostic.h" #include "clang/Basic/Specifiers.h" #include "clang/Basic/TargetInfo.h" @@ -12071,6 +12072,38 @@ 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, const ClassTemplateDecl *Template, + bool Diagnose) { + assert(Template && "Template can't be null"); + const TemplateParameterList *Params = Template->getTemplateParameters(); + int ErrorKind = -1; + + if (Params->size() != 1) + ErrorKind = diag::MalformedStdInitializerList::TooManyParams; + else if (Template->hasAssociatedConstraints()) + ErrorKind = diag::MalformedStdInitializerList::Constrained; + else { + const auto *Param = dyn_cast(Params->getParam(0)); + if (!Param) + ErrorKind = diag::MalformedStdInitializerList::BadParamKind; + else if (Param->hasDefaultArgument()) + ErrorKind = diag::MalformedStdInitializerList::DefaultArg; + else if (Param->isTemplateParameterPack()) + ErrorKind = diag::MalformedStdInitializerList::ParamPack; + 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++."); @@ -12118,10 +12151,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(Params->getParam(0))) + if (CheckStdInitializerList(*this, Template, /*Diagnose=*/false)) return false; // It's the right template. @@ -12137,7 +12167,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); @@ -12155,16 +12186,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) + << diag::MalformedStdInitializerList::BadEntityKind; + 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(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; } diff --git a/clang/test/SemaCXX/invalid-std-initializer-list.cpp b/clang/test/SemaCXX/invalid-std-initializer-list.cpp new file mode 100644 index 0000000000000..339accefbb6d1 --- /dev/null +++ b/clang/test/SemaCXX/invalid-std-initializer-list.cpp @@ -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 + +namespace std { + +#ifdef TYPE_PARAM +template class initializer_list; +// expected-note@-1 2 {{template is declared here}} +#elifdef CONSTANT_PARAM +template class initializer_list; +// expected-error@-1 2 {{std::initializer_list must have a type template parameter}} +#elifdef TYPE_TEMPLATE_PARAM +template class> class initializer_list; +// expected-error@-1 2 {{std::initializer_list must have a type template parameter}} +#elifdef DEFAULT_ARG +template class initializer_list; +// expected-error@-1 2 {{std::initializer_list cannot have default template arguments}} +#elifdef MULTIPLE_PARAMS +template class initializer_list; +// expected-error@-1 2 {{std::initializer_list must have exactly one template parameter}} +#elifdef PARAM_PACK +template class initializer_list; +// expected-error@-1 2 {{std::initializer_list cannot be a variadic template}} +#elifdef CONSTRAINED_PARAM +template concept C = true; +template class initializer_list; +// expected-error@-1 2 {{std::initializer_list cannot have associated constraints}} +#elifdef REQUIRES_CLAUSE +template requires true class initializer_list; +// expected-error@-1 2 {{std::initializer_list cannot have associated constraints}} +#elifdef NONCLASS_TEMPLATE +template class IL; +template using initializer_list = IL; +// 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 using A = double; + Test(std::initializer_list); // expected-note {{candidate constructor}} +#elifdef MULTIPLE_PARAMS + Test(std::initializer_list); // expected-note {{candidate constructor}} +#elifdef NONTEMPLATE + Test(std::initializer_list); // expected-note {{candidate constructor}} +#else + Test(std::initializer_list); // 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}} +}