Author: keinflue
Date: 2025-08-09T20:03:02-03:00
New Revision: 4c0a7b72f7fe7786e12264aec2b990177222778c

URL: 
https://github.com/llvm/llvm-project/commit/4c0a7b72f7fe7786e12264aec2b990177222778c
DIFF: 
https://github.com/llvm/llvm-project/commit/4c0a7b72f7fe7786e12264aec2b990177222778c.diff

LOG: [clang] Distinguish NTTPs with deduced types in variable template partial 
specializations (#152864)

If a template argument in a partial specialization of a variable
template directly refers to a NTTP of the specialization without
implicit type conversion it was assumed that the NTTP is identical to
that of the primary template.

This doesn't hold if the primary template's NTTP uses a deduced type, so
instead compare the types explicitly as well.

The affected function is used only to provide an improved early error if
the partial specialization has identical template arguments to the
primary template. The actual check that the partial specialization is
more specialized happens later.

Fixes #118190
Fixes #152750

Added: 
    

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/lib/Sema/SemaTemplate.cpp
    clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index c6b0d32853da8..a8b7a29933945 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -188,6 +188,7 @@ Bug Fixes to C++ Support
   (``[[assume(expr)]]``) creates temporary objects.
 - Fix the dynamic_cast to final class optimization to correctly handle
   casts that are guaranteed to fail (#GH137518).
+- Fix bug rejecting partial specialization of variable templates with auto 
NTTPs (#GH118190).
 
 Bug Fixes to AST Handling
 ^^^^^^^^^^^^^^^^^^^^^^^^^

diff  --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 3441613e1acc6..72d98ca391f4b 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -4083,7 +4083,6 @@ static bool CheckTemplateSpecializationScope(Sema &S, 
NamedDecl *Specialized,
 static TemplateSpecializationKind getTemplateSpecializationKind(Decl *D);
 
 static bool isTemplateArgumentTemplateParameter(const TemplateArgument &Arg,
-                                                NamedDecl *Param,
                                                 unsigned Depth,
                                                 unsigned Index) {
   switch (Arg.getKind()) {
@@ -4123,8 +4122,9 @@ static bool isTemplateArgumentTemplateParameter(const 
TemplateArgument &Arg,
 }
 
 static bool isSameAsPrimaryTemplate(TemplateParameterList *Params,
+                                    TemplateParameterList *SpecParams,
                                     ArrayRef<TemplateArgument> Args) {
-  if (Params->size() != Args.size())
+  if (Params->size() != Args.size() || Params->size() != SpecParams->size())
     return false;
 
   unsigned Depth = Params->getDepth();
@@ -4141,9 +4141,19 @@ static bool 
isSameAsPrimaryTemplate(TemplateParameterList *Params,
       Arg = Arg.pack_begin()->getPackExpansionPattern();
     }
 
-    if (!isTemplateArgumentTemplateParameter(Arg, Params->getParam(I), Depth,
-                                             I))
+    if (!isTemplateArgumentTemplateParameter(Arg, Depth, I))
       return false;
+
+    // For NTTPs further specialization is allowed via deduced types, so
+    // we need to make sure to only reject here if primary template and
+    // specialization use the same type for the NTTP.
+    if (auto *SpecNTTP =
+            dyn_cast<NonTypeTemplateParmDecl>(SpecParams->getParam(I))) {
+      auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(Params->getParam(I));
+      if (!NTTP || NTTP->getType().getCanonicalType() !=
+                       SpecNTTP->getType().getCanonicalType())
+        return false;
+    }
   }
 
   return true;
@@ -4341,7 +4351,7 @@ DeclResult Sema::ActOnVarTemplateSpecialization(
     }
 
     if (isSameAsPrimaryTemplate(VarTemplate->getTemplateParameters(),
-                                CTAI.CanonicalConverted) &&
+                                TemplateParams, CTAI.CanonicalConverted) &&
         (!Context.getLangOpts().CPlusPlus20 ||
          !TemplateParams->hasAssociatedConstraints())) {
       // C++ [temp.class.spec]p9b3:

diff  --git a/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp 
b/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp
index c35743b87adbc..9c25e26f43c36 100644
--- a/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp
+++ b/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp
@@ -621,3 +621,8 @@ namespace GH73460 {
   int j;
   template struct A<int&, j, j>;
 } // namespace GH73460
+
+namespace GH118190 {
+  template <auto> int x;
+  template <int i> int x<i>;
+}


        
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to