[clang] [Clang] Fix unexpanded packs in NTTP type constraints (PR #121296)

Younan Zhang via cfe-commits cfe-commits at lists.llvm.org
Fri Jan 3 03:10:26 PST 2025


https://github.com/zyn0217 updated https://github.com/llvm/llvm-project/pull/121296

>From 102e031cae56c130f48f08bcb316b6f451facf49 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Sun, 29 Dec 2024 23:13:52 +0800
Subject: [PATCH 1/6] [Clang] Diagnose unexpanded packs for NTTP type
 constraints

---
 clang/docs/ReleaseNotes.rst             |  1 +
 clang/lib/Sema/SemaTemplate.cpp         | 12 +++++--
 clang/test/SemaCXX/cxx2c-fold-exprs.cpp | 43 +++++++++++++++++++++++++
 3 files changed, 54 insertions(+), 2 deletions(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 8b984ecaefecaf..78ae4c8cab3a9e 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -885,6 +885,7 @@ Bug Fixes to C++ Support
 - Fixed recognition of ``std::initializer_list`` when it's surrounded with ``extern "C++"`` and exported
   out of a module (which is the case e.g. in MSVC's implementation of ``std`` module). (#GH118218)
 - Fixed a pack expansion issue in checking unexpanded parameter sizes. (#GH17042)
+- Clang now identifies unexpanded parameter packs within the type constraint on a non-type template parameter. (#GH88866)
 
 Bug Fixes to AST Handling
 ^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 5e7a3c8484c88f..eac184d468ce9a 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -1530,9 +1530,17 @@ NamedDecl *Sema::ActOnNonTypeTemplateParameter(Scope *S, Declarator &D,
   Param->setAccess(AS_public);
 
   if (AutoTypeLoc TL = TInfo->getTypeLoc().getContainedAutoTypeLoc())
-    if (TL.isConstrained())
-      if (AttachTypeConstraint(TL, Param, Param, D.getEllipsisLoc()))
+    if (TL.isConstrained()) {
+      if (const ASTTemplateArgumentListInfo *ArgumentList =
+              TL.getConceptReference()->getTemplateArgsAsWritten())
+        for (const TemplateArgumentLoc &Loc : ArgumentList->arguments()) {
+          Invalid |= DiagnoseUnexpandedParameterPack(
+              Loc, UnexpandedParameterPackContext::UPPC_TypeConstraint);
+        }
+      if (!Invalid &&
+          AttachTypeConstraint(TL, Param, Param, D.getEllipsisLoc()))
         Invalid = true;
+    }
 
   if (Invalid)
     Param->setInvalidDecl();
diff --git a/clang/test/SemaCXX/cxx2c-fold-exprs.cpp b/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
index 0674135aac483f..00236a8a839135 100644
--- a/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
+++ b/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
@@ -305,3 +305,46 @@ static_assert(__is_same_as(_Three_way_comparison_result_with_tuple_like<tuple<in
 static_assert(__is_same_as(_Three_way_comparison_result_with_tuple_like<tuple<int>, 0>::type, long));
 
 }
+
+namespace GH88866 {
+
+template <typename...Ts> struct index_by;
+
+template <typename T, typename Indices>
+concept InitFunc = true;
+
+namespace Invalid {
+
+template <typename Indices, InitFunc<Indices> auto... init>
+struct LazyLitMatrix;
+
+template <
+    typename...Indices,
+    InitFunc<index_by<Indices>> auto... init
+    // expected-error at -1 {{type constraint contains unexpanded parameter pack 'Indices'}}
+>
+struct LazyLitMatrix<index_by<Indices...>, init...> {
+};
+
+static_assert(
+    !__is_same(LazyLitMatrix<index_by<int, char>, 42, 43>, LazyLitMatrix<index_by<int, char>, 42, 43>));
+// expected-error at -1 {{static assertion failed}}
+}
+
+namespace Valid {
+
+template <typename Indices, InitFunc<Indices> auto... init>
+struct LazyLitMatrix;
+
+template <
+    typename...Indices,
+    InitFunc<index_by<Indices...>> auto... init
+>
+struct LazyLitMatrix<index_by<Indices...>, init...> {
+};
+
+static_assert(__is_same(LazyLitMatrix<index_by<int, char>, 42, 43>, 
+                        LazyLitMatrix<index_by<int, char>, 42, 43>));
+}
+
+}

>From 111d1002e081322fdb5f3fa8e94b15b99817b3b9 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Sun, 29 Dec 2024 23:30:57 +0800
Subject: [PATCH 2/6] Simplify the test

---
 clang/test/SemaCXX/cxx2c-fold-exprs.cpp | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/clang/test/SemaCXX/cxx2c-fold-exprs.cpp b/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
index 00236a8a839135..509922c0a8f4fe 100644
--- a/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
+++ b/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
@@ -326,9 +326,8 @@ template <
 struct LazyLitMatrix<index_by<Indices...>, init...> {
 };
 
-static_assert(
-    !__is_same(LazyLitMatrix<index_by<int, char>, 42, 43>, LazyLitMatrix<index_by<int, char>, 42, 43>));
-// expected-error at -1 {{static assertion failed}}
+using T = LazyLitMatrix<index_by<int, char>, 42, 43>;
+
 }
 
 namespace Valid {
@@ -343,8 +342,8 @@ template <
 struct LazyLitMatrix<index_by<Indices...>, init...> {
 };
 
-static_assert(__is_same(LazyLitMatrix<index_by<int, char>, 42, 43>, 
-                        LazyLitMatrix<index_by<int, char>, 42, 43>));
+using T = LazyLitMatrix<index_by<int, char>, 42, 43>;
+
 }
 
 }

>From 144f3c997def0a4d746edb111f2e7f90797b153e Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Tue, 31 Dec 2024 16:13:46 +0800
Subject: [PATCH 3/6] Let ... expand NTTP constraints

---
 clang/lib/AST/ASTContext.cpp             |  2 +-
 clang/lib/Sema/SemaTemplate.cpp          | 14 +++-----------
 clang/lib/Sema/SemaTemplateDeduction.cpp |  3 ++-
 3 files changed, 6 insertions(+), 13 deletions(-)

diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 8b4ae58e8427a9..a9ecb4ee9c76b2 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -6376,7 +6376,7 @@ ASTContext::getAutoType(QualType DeducedType, AutoTypeKeyword Keyword,
 }
 
 QualType ASTContext::getUnconstrainedType(QualType T) const {
-  QualType CanonT = T.getCanonicalType();
+  QualType CanonT = T.getNonPackExpansionType().getCanonicalType();
 
   // Remove a type-constraint from a top-level auto or decltype(auto).
   if (auto *AT = CanonT->getAs<AutoType>()) {
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index eac184d468ce9a..2e4a3060c0b391 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -1228,7 +1228,7 @@ bool Sema::AttachTypeConstraint(AutoTypeLoc TL,
                                 NonTypeTemplateParmDecl *NewConstrainedParm,
                                 NonTypeTemplateParmDecl *OrigConstrainedParm,
                                 SourceLocation EllipsisLoc) {
-  if (NewConstrainedParm->getType() != TL.getType() ||
+  if (NewConstrainedParm->getType().getNonPackExpansionType() != TL.getType() ||
       TL.getAutoKeyword() != AutoTypeKeyword::Auto) {
     Diag(NewConstrainedParm->getTypeSourceInfo()->getTypeLoc().getBeginLoc(),
          diag::err_unsupported_placeholder_constraint)
@@ -1530,17 +1530,9 @@ NamedDecl *Sema::ActOnNonTypeTemplateParameter(Scope *S, Declarator &D,
   Param->setAccess(AS_public);
 
   if (AutoTypeLoc TL = TInfo->getTypeLoc().getContainedAutoTypeLoc())
-    if (TL.isConstrained()) {
-      if (const ASTTemplateArgumentListInfo *ArgumentList =
-              TL.getConceptReference()->getTemplateArgsAsWritten())
-        for (const TemplateArgumentLoc &Loc : ArgumentList->arguments()) {
-          Invalid |= DiagnoseUnexpandedParameterPack(
-              Loc, UnexpandedParameterPackContext::UPPC_TypeConstraint);
-        }
-      if (!Invalid &&
-          AttachTypeConstraint(TL, Param, Param, D.getEllipsisLoc()))
+    if (TL.isConstrained())
+      if (AttachTypeConstraint(TL, Param, Param, D.getEllipsisLoc()))
         Invalid = true;
-    }
 
   if (Invalid)
     Param->setInvalidDecl();
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index fad20b37a7d9a2..b4a4ab35478a1b 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -857,7 +857,8 @@ class PackDeductionScope {
       if (auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(
               TemplateParams->getParam(Index))) {
         if (!NTTP->isExpandedParameterPack())
-          if (auto *Expansion = dyn_cast<PackExpansionType>(NTTP->getType()))
+          if (auto *Expansion = dyn_cast<PackExpansionType>(
+                  S.Context.getUnconstrainedType(NTTP->getType())))
             ExtraDeductions.push_back(Expansion->getPattern());
       }
       // FIXME: Also collect the unexpanded packs in any type and template

>From 2cb3e542e431f23e7827e939a066217524b9f0ae Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Tue, 31 Dec 2024 16:44:56 +0800
Subject: [PATCH 4/6] Additional tests

---
 clang/lib/Sema/SemaTemplate.cpp         | 14 ++++++-
 clang/test/SemaCXX/cxx2c-fold-exprs.cpp | 49 ++++++++++++++++++++++---
 2 files changed, 55 insertions(+), 8 deletions(-)

diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 2e4a3060c0b391..20ec2fbeaa6a8b 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -1530,9 +1530,19 @@ NamedDecl *Sema::ActOnNonTypeTemplateParameter(Scope *S, Declarator &D,
   Param->setAccess(AS_public);
 
   if (AutoTypeLoc TL = TInfo->getTypeLoc().getContainedAutoTypeLoc())
-    if (TL.isConstrained())
-      if (AttachTypeConstraint(TL, Param, Param, D.getEllipsisLoc()))
+    if (TL.isConstrained()) {
+      if (D.getEllipsisLoc().isInvalid() &&
+          T->containsUnexpandedParameterPack()) {
+        assert(TL.getConceptReference()->getTemplateArgsAsWritten());
+        for (auto &Loc :
+             TL.getConceptReference()->getTemplateArgsAsWritten()->arguments())
+          Invalid |= DiagnoseUnexpandedParameterPack(
+              Loc, UnexpandedParameterPackContext::UPPC_TypeConstraint);
+      }
+      if (!Invalid &&
+          AttachTypeConstraint(TL, Param, Param, D.getEllipsisLoc()))
         Invalid = true;
+    }
 
   if (Invalid)
     Param->setInvalidDecl();
diff --git a/clang/test/SemaCXX/cxx2c-fold-exprs.cpp b/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
index 509922c0a8f4fe..48061439941f23 100644
--- a/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
+++ b/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
@@ -313,24 +313,26 @@ template <typename...Ts> struct index_by;
 template <typename T, typename Indices>
 concept InitFunc = true;
 
-namespace Invalid {
+namespace ExpandsBoth {
 
 template <typename Indices, InitFunc<Indices> auto... init>
-struct LazyLitMatrix;
+struct LazyLitMatrix; // expected-note {{here}}
 
 template <
     typename...Indices,
     InitFunc<index_by<Indices>> auto... init
-    // expected-error at -1 {{type constraint contains unexpanded parameter pack 'Indices'}}
 >
 struct LazyLitMatrix<index_by<Indices...>, init...> {
 };
 
-using T = LazyLitMatrix<index_by<int, char>, 42, 43>;
+// FIXME: Explain why we didn't pick up the partial specialization - pack sizes don't match.
+template struct LazyLitMatrix<index_by<int, char>, 42>;
+// expected-error at -1 {{instantiation of undefined template}}
+template struct LazyLitMatrix<index_by<int, char>, 42, 43>;
 
 }
 
-namespace Valid {
+namespace ExpandsRespectively {
 
 template <typename Indices, InitFunc<Indices> auto... init>
 struct LazyLitMatrix;
@@ -342,7 +344,42 @@ template <
 struct LazyLitMatrix<index_by<Indices...>, init...> {
 };
 
-using T = LazyLitMatrix<index_by<int, char>, 42, 43>;
+template struct LazyLitMatrix<index_by<int, char>, 42>;
+template struct LazyLitMatrix<index_by<int, char>, 42, 43>;
+
+}
+
+namespace TypeParameter {
+
+template <typename Indices, InitFunc<Indices>... init>
+struct LazyLitMatrix; // expected-note {{here}}
+
+template <
+    typename...Indices,
+    InitFunc<index_by<Indices>>... init
+>
+struct LazyLitMatrix<index_by<Indices...>, init...> {
+};
+
+// FIXME: Explain why we didn't pick up the partial specialization - pack sizes don't match.
+template struct LazyLitMatrix<index_by<int, char>, float>;
+// expected-error at -1 {{instantiation of undefined template}}
+template struct LazyLitMatrix<index_by<int, char>, unsigned, float>;
+
+}
+
+namespace Invalid {
+
+template <typename Indices, InitFunc<Indices>... init>
+struct LazyLitMatrix;
+
+template <
+    typename...Indices,
+    InitFunc<index_by<Indices>> init
+    // expected-error at -1 {{unexpanded parameter pack 'Indices'}}
+>
+struct LazyLitMatrix<index_by<Indices...>, init> {
+};
 
 }
 

>From 88beac3af22b8f5a5ea58a31f4f0d13aafe1f0fc Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Fri, 3 Jan 2025 13:22:36 +0800
Subject: [PATCH 5/6] Add a note for CWG2982

---
 clang/lib/Sema/SemaTemplateDeduction.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index b4a4ab35478a1b..1c1f6e30ab7b83 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -857,6 +857,8 @@ class PackDeductionScope {
       if (auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(
               TemplateParams->getParam(Index))) {
         if (!NTTP->isExpandedParameterPack())
+          // FIXME: CWG2982 suggests a type-constraint forms a non-deduced
+          // context, however it is not yet resolved.
           if (auto *Expansion = dyn_cast<PackExpansionType>(
                   S.Context.getUnconstrainedType(NTTP->getType())))
             ExtraDeductions.push_back(Expansion->getPattern());

>From 7780ed619afe174a8d67eaf5818ff592c5d85c8c Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Fri, 3 Jan 2025 19:10:15 +0800
Subject: [PATCH 6/6] Remove extra \n

---
 clang/docs/ReleaseNotes.rst | 1 -
 1 file changed, 1 deletion(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index f5da5330bc6436..61d6aa2216cd06 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -889,7 +889,6 @@ Bug Fixes to C++ Support
 - Clang now identifies unexpanded parameter packs within the type constraint on a non-type template parameter. (#GH88866)
 - Fixed an issue while resolving type of expression indexing into a pack of values of non-dependent type (#GH121242)
 
-
 Bug Fixes to AST Handling
 ^^^^^^^^^^^^^^^^^^^^^^^^^
 



More information about the cfe-commits mailing list