https://github.com/offsetof updated https://github.com/llvm/llvm-project/pull/133822
>From b5798e04281fb6d9475a1ae6af8b94bc0ed85a43 Mon Sep 17 00:00:00 2001 From: offsetof <offse...@mailo.com> Date: Mon, 31 Mar 2025 23:17:47 +0000 Subject: [PATCH 1/2] [clang] Check `std::initializer_list` more strictly Require `std::initializer_list` to be a class template with a template-head equivalent to `template<class>` and no default arguments. --- .../clang/Basic/DiagnosticSemaKinds.td | 10 ++- clang/lib/Sema/SemaDeclCXX.cpp | 49 +++++++++--- .../SemaCXX/invalid-std-initializer-list.cpp | 77 +++++++++++++++++++ 3 files changed, 124 insertions(+), 12 deletions(-) create mode 100644 clang/test/SemaCXX/invalid-std-initializer-list.cpp 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}} +} >From 64690442bac8b05f0df03c94b22d70c429422db3 Mon Sep 17 00:00:00 2001 From: offsetof <offse...@mailo.com> Date: Wed, 2 Apr 2025 14:37:35 +0000 Subject: [PATCH 2/2] fixup! [clang] Check `std::initializer_list` more strictly --- .../clang/Basic/DiagnosticSemaKinds.td | 14 +++++++------- clang/lib/Sema/SemaDeclCXX.cpp | 19 ++++++++++--------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 4476751f9952a..575f13336e441 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2591,13 +2591,13 @@ 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 %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">; + : Error<"std::initializer_list %enum_select<MalformedStdInitializerList>{" + "%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'|<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 77f8b6e36fcb3..e51512283ccac 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" @@ -12075,23 +12076,23 @@ NamespaceDecl *Sema::getOrCreateStdNamespace() { /// 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, +static bool CheckStdInitializerList(Sema &S, const ClassTemplateDecl *Template, bool Diagnose) { - TemplateParameterList *Params = Template->getTemplateParameters(); + const TemplateParameterList *Params = Template->getTemplateParameters(); int ErrorKind = -1; if (Params->size() != 1) - ErrorKind = 0; // must have exactly one template parameter + ErrorKind = diag::MalformedStdInitializerList::TooManyParams; else if (Template->hasAssociatedConstraints()) - ErrorKind = 1; // cannot have associated constraints + ErrorKind = diag::MalformedStdInitializerList::Constrained; else { - auto *Param = dyn_cast<TemplateTypeParmDecl>(Params->getParam(0)); + const auto *Param = dyn_cast<TemplateTypeParmDecl>(Params->getParam(0)); if (!Param) - ErrorKind = 2; // must have a type template parameter + ErrorKind = diag::MalformedStdInitializerList::BadParamKind; else if (Param->hasDefaultArgument()) - ErrorKind = 3; // cannot have default template arguments + ErrorKind = diag::MalformedStdInitializerList::DefaultArg; else if (Param->isTemplateParameterPack()) - ErrorKind = 4; // cannot be a variadic template + ErrorKind = diag::MalformedStdInitializerList::ParamPack; else return false; } @@ -12185,7 +12186,7 @@ static ClassTemplateDecl *LookupStdInitializerList(Sema &S, // 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) - << 5 /* must be a class template */; + << diag::MalformedStdInitializerList::BadEntityKind; S.Diag(Loc, diag::note_used_here); return nullptr; } _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits