llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: None (offsetof) <details> <summary>Changes</summary> Require `std::initializer_list` to be a class template with a template-head equivalent to `template<class>` and no default arguments. --- Full diff: https://github.com/llvm/llvm-project/pull/133822.diff 3 Files Affected: - (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+8-2) - (modified) clang/lib/Sema/SemaDeclCXX.cpp (+39-10) - (added) clang/test/SemaCXX/invalid-std-initializer-list.cpp (+77) ``````````diff diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 5e45482584946..4476751f9952a 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 <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">; def err_auto_init_list_from_c : Error< "cannot use %select{'auto'|<ERROR>|'__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..77f8b6e36fcb3 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -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(); + 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)); + 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 + 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 +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. @@ -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); @@ -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; } 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> 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}} +} `````````` </details> https://github.com/llvm/llvm-project/pull/133822 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits