[clang] [Clang][Sema] Mark partial specializations invalid when not more specialized than primary (PR #181561)
Giovanni B. via cfe-commits
cfe-commits at lists.llvm.org
Mon Feb 16 01:42:05 PST 2026
https://github.com/Z3rox-dev updated https://github.com/llvm/llvm-project/pull/181561
>From 3604b58086acd17a9755224cafed05f95d3f345d Mon Sep 17 00:00:00 2001
From: Giovanni Baldon <116344382+Z3rox-dev at users.noreply.github.com>
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 | 7 ++++++-
.../temp.variadic/fixed-expansion.cpp | 6 +++---
...tial-spec-not-more-specialized-invalid.cpp | 21 +++++++++++++++++++
clang/test/SemaTemplate/temp_arg_nontype.cpp | 5 +----
4 files changed, 31 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..8884dccb6af00 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -4229,6 +4229,9 @@ static void checkMoreSpecializedThanPrimary(Sema &S, PartialSpecDecl *Partial) {
diag::ext_partial_spec_not_more_specialized_than_primary)
<< isa<VarTemplateDecl>(Template);
+ // An invalid partial specialization should not be deduced.
+ Partial->setInvalidDecl();
+
if (Info.hasSFINAEDiagnostic()) {
PartialDiagnosticAt Diag = {SourceLocation(),
PartialDiagnostic::NullDiagnostic()};
@@ -9108,7 +9111,9 @@ DeclResult Sema::ActOnClassTemplateSpecialization(
if (SkipBody && SkipBody->ShouldSkip)
return SkipBody->Previous;
- Specialization->setInvalidDecl(Invalid);
+ // Don't clear an invalid flag already set by earlier checks.
+ 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..13c0c72a60361
--- /dev/null
+++ b/clang/test/SemaTemplate/partial-spec-not-more-specialized-invalid.cpp
@@ -0,0 +1,21 @@
+// RUN: %clang_cc1 -std=c++20 -verify -emit-llvm-only %s
+// https://github.com/llvm/llvm-project/issues/181410
+
+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;
+
+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 at -1 {{class template partial specialization is not more specialized than the primary template}}
- A<0, 0, int, int, 0> a;
- // expected-error at -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 {
More information about the cfe-commits
mailing list