https://github.com/Z3rox-dev created 
https://github.com/llvm/llvm-project/pull/181614

## Summary

`checkMoreSpecializedThanPrimary()` emits 
`ext_partial_spec_not_more_specialized_than_primary` but did not mark the 
partial specialization as invalid. Additionally, 
`ActOnClassTemplateSpecialization()` unconditionally called 
`Specialization->setInvalidDecl(Invalid)` where `Invalid` was `false` for 
partial specializations, clearing any invalid flag set by 
`CheckTemplatePartialSpecialization`.

This caused the invalid partial specialization to still be selected during 
template argument deduction. Using it for instantiation produced dependent 
expressions (`CXXUnresolvedConstructExpr`) in non-dependent contexts, leading 
to an assertion failure:
```
assert(!Init->isValueDependent()) in VarDecl::evaluateValueImpl
```

## Root Cause

Two-part issue in `SemaTemplate.cpp`:

1. **Missing invalidation**: `checkMoreSpecializedThanPrimary()` emitted the 
diagnostic but never called `Partial->setInvalidDecl()`.
2. **Flag clearing**: `ActOnClassTemplateSpecialization()` called 
`Specialization->setInvalidDecl(Invalid)` at the end, where `Invalid` is 
`false` for partial specializations. Since `Specialization` and `Partial` are 
the same pointer (set at line 8987), this unconditionally cleared the invalid 
flag even if `CheckTemplatePartialSpecialization` had just set it.

The guard in `DeduceTemplateArguments()` (SemaTemplateDeduction.cpp:3406) 
already checks `Partial->isInvalidDecl()` and returns `TDK_Invalid` — so once 
the partial spec is properly marked invalid, it is excluded from deduction.

## Fix

1. Add `Partial->setInvalidDecl()` in `checkMoreSpecializedThanPrimary()` after 
the diagnostic.
2. Change `ActOnClassTemplateSpecialization()` to use `if (Invalid) 
Specialization->setInvalidDecl()` — only sets the flag, never clears it.

## Test Plan

- New regression test: 
`clang/test/SemaTemplate/partial-spec-not-more-specialized-invalid.cpp` (exact 
reproducer from #181410)
- Updated 2 existing tests to match new behavior (invalid partial specs are now 
excluded from deduction)
- Full `check-clang`: **50,534 tests, 0 failures** (46,308 passed, 28 
expectedly failed)

> AI tools were used to assist with root cause analysis and tracing the code 
> paths in this fix.

Fixes #181410


>From 856e6e0122bbd727d747f81c38eda8628566dc43 Mon Sep 17 00:00:00 2001
From: Giovanni Baldon <[email protected]>
Date: Mon, 16 Feb 2026 09:56:10 +0100
Subject: [PATCH] [Clang][Sema] Mark partial specializations invalid when not
 more specialized than primary

checkMoreSpecializedThanPrimary() emits 
ext_partial_spec_not_more_specialized_than_primary
but did not mark the partial specialization as invalid. Later, 
ActOnClassTemplateSpecialization()
unconditionally called Specialization->setInvalidDecl(Invalid) where Invalid 
was false for
partial specializations, clearing any invalid flag set by 
CheckTemplatePartialSpecialization.

This caused the invalid partial specialization to still be selected during 
template argument
deduction. Using it for instantiation produced dependent expressions 
(CXXUnresolvedConstructExpr)
in non-dependent contexts, leading to an assertion failure:
  assert(!Init->isValueDependent()) in VarDecl::evaluateValueImpl

Fix:
1. Add Partial->setInvalidDecl() in checkMoreSpecializedThanPrimary() after the 
diagnostic.
2. Change ActOnClassTemplateSpecialization() to only call setInvalidDecl() when 
Invalid is
   true, preventing it from clearing flags set earlier in the pipeline.

Fixes #181410

AI tools were used to assist with root cause analysis and tracing the code 
paths in this fix.
---
 clang/lib/Sema/SemaTemplate.cpp               | 12 +++++++-
 .../temp.variadic/fixed-expansion.cpp         |  6 ++--
 ...tial-spec-not-more-specialized-invalid.cpp | 30 +++++++++++++++++++
 clang/test/SemaTemplate/temp_arg_nontype.cpp  |  5 +---
 4 files changed, 45 insertions(+), 8 deletions(-)
 create mode 100644 
clang/test/SemaTemplate/partial-spec-not-more-specialized-invalid.cpp

diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 3497ff7856eed..5cdfa97c6c35e 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -4229,6 +4229,12 @@ static void checkMoreSpecializedThanPrimary(Sema &S, 
PartialSpecDecl *Partial) {
          diag::ext_partial_spec_not_more_specialized_than_primary)
       << isa<VarTemplateDecl>(Template);
 
+  // Mark the partial specialization as invalid so it won't be selected
+  // during template argument deduction. Using an invalid partial
+  // specialization for instantiation can produce dependent expressions
+  // in non-dependent contexts, leading to crashes in later phases.
+  Partial->setInvalidDecl();
+
   if (Info.hasSFINAEDiagnostic()) {
     PartialDiagnosticAt Diag = {SourceLocation(),
                                 PartialDiagnostic::NullDiagnostic()};
@@ -9108,7 +9114,11 @@ DeclResult Sema::ActOnClassTemplateSpecialization(
   if (SkipBody && SkipBody->ShouldSkip)
     return SkipBody->Previous;
 
-  Specialization->setInvalidDecl(Invalid);
+  // Only mark invalid if determined here; don't clear a flag already set
+  // by CheckTemplatePartialSpecialization (e.g., for a partial specialization
+  // that is not more specialized than the primary template).
+  if (Invalid)
+    Specialization->setInvalidDecl();
   inferGslOwnerPointerAttribute(Specialization);
   return Specialization;
 }
diff --git a/clang/test/CXX/temp/temp.decls/temp.variadic/fixed-expansion.cpp 
b/clang/test/CXX/temp/temp.decls/temp.variadic/fixed-expansion.cpp
index ab4c663d24c7d..0bc69909b51c5 100644
--- a/clang/test/CXX/temp/temp.decls/temp.variadic/fixed-expansion.cpp
+++ b/clang/test/CXX/temp/temp.decls/temp.variadic/fixed-expansion.cpp
@@ -108,15 +108,15 @@ namespace PR9021b {
 
 namespace PartialSpecialization {
   template<typename T, typename U, typename V = U>
-  struct X0; // expected-note 2{{template is declared here}}
+  struct X0; // expected-note 4{{template is declared here}}
 
   template<typename ...Ts>
   struct X0<Ts...> { // expected-error {{class template partial specialization 
is not more specialized than the primary template}}
   };
 
   X0<int> x0i; // expected-error{{too few template arguments for class 
template 'X0'}}
-  X0<int, float> x0if;
-  X0<int, float, double> x0ifd;
+  X0<int, float> x0if; // expected-error {{implicit instantiation of undefined 
template 'PartialSpecialization::X0<int, float>'}}
+  X0<int, float, double> x0ifd; // expected-error {{implicit instantiation of 
undefined template 'PartialSpecialization::X0<int, float, double>'}}
 }
 
 namespace FixedAliasTemplate {
diff --git 
a/clang/test/SemaTemplate/partial-spec-not-more-specialized-invalid.cpp 
b/clang/test/SemaTemplate/partial-spec-not-more-specialized-invalid.cpp
new file mode 100644
index 0000000000000..5439081a5dedf
--- /dev/null
+++ b/clang/test/SemaTemplate/partial-spec-not-more-specialized-invalid.cpp
@@ -0,0 +1,30 @@
+// RUN: %clang_cc1 -std=c++20 -verify -emit-llvm-only %s
+// Regression test for https://github.com/llvm/llvm-project/issues/181410
+//
+// A class template partial specialization diagnosed as "not more specialized
+// than the primary template" was not marked invalid, so it was still selected
+// during template argument deduction. Using it for instantiation produced
+// dependent expressions in non-dependent contexts, causing an assertion
+// failure in CodeGen: assert(!Init->isValueDependent()).
+
+template <int>
+struct integer_sequence {};
+
+template <int>
+struct array {};
+
+template <int*>
+struct MetaValuesHelper; // expected-note 2{{template is declared here}}
+
+template <typename TupleName, TupleName kValues>
+struct MetaValuesHelper<kValues> { // expected-error {{class template partial 
specialization is not more specialized than the primary template}}
+  template <int... Is>
+  static array<stdget<Is>(kValues)...> MetaValuesFunc(integer_sequence<Is...>);
+};
+
+int kBaseIndexRegistersUsed;
+
+// Previously this crashed with: assert(!Init->isValueDependent())
+// Now the invalid partial specialization is excluded from deduction,
+// and the primary template (which is only forward-declared) is used instead.
+array<0> u = 
decltype(MetaValuesHelper<&kBaseIndexRegistersUsed>::MetaValuesFunc(integer_sequence<0>{})){};
 // expected-error {{implicit instantiation of undefined template 
'MetaValuesHelper<&kBaseIndexRegistersUsed>'}}
diff --git a/clang/test/SemaTemplate/temp_arg_nontype.cpp 
b/clang/test/SemaTemplate/temp_arg_nontype.cpp
index bd0bf3cfdbc59..e94bd4e8aa1ee 100644
--- a/clang/test/SemaTemplate/temp_arg_nontype.cpp
+++ b/clang/test/SemaTemplate/temp_arg_nontype.cpp
@@ -391,10 +391,7 @@ namespace partial_order_different_types {
   template<int N, typename T, typename U, T V> struct A<0, N, T, U, V> {}; // 
#P1
   template<int N, typename T, typename U, U V> struct A<0, N, T, U, V>;    // 
#P2
   // expected-error@-1 {{class template partial specialization is not more 
specialized than the primary template}}
-  A<0, 0, int, int, 0> a;
-  // expected-error@-1 {{ambiguous partial specializations}}
-  // expected-note@#P1 {{partial specialization matches}}
-  // expected-note@#P2 {{partial specialization matches}}
+  A<0, 0, int, int, 0> a; // OK: #P2 is invalid and excluded; only #P1 matches.
 }
 
 namespace partial_order_references {

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to