llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: Giovanni B. (Z3rox-dev) <details> <summary>Changes</summary> ## 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 --- Full diff: https://github.com/llvm/llvm-project/pull/181614.diff 4 Files Affected: - (modified) clang/lib/Sema/SemaTemplate.cpp (+11-1) - (modified) clang/test/CXX/temp/temp.decls/temp.variadic/fixed-expansion.cpp (+3-3) - (added) clang/test/SemaTemplate/partial-spec-not-more-specialized-invalid.cpp (+30) - (modified) clang/test/SemaTemplate/temp_arg_nontype.cpp (+1-4) ``````````diff 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 { `````````` </details> https://github.com/llvm/llvm-project/pull/181614 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
