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&lt;class&gt;` 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

Reply via email to