[clang] Don't try to set MemberSpecialization on invalid var decl chain (PR #209604)
Erich Keane via cfe-commits
cfe-commits at lists.llvm.org
Wed Jul 15 06:21:02 PDT 2026
https://github.com/erichkeane updated https://github.com/llvm/llvm-project/pull/209604
>From 9b0c9adbc73af24daf5da4434cdbd208ed06ddd6 Mon Sep 17 00:00:00 2001
From: erichkeane <ekeane at nvidia.com>
Date: Tue, 14 Jul 2026 11:54:04 -0700
Subject: [PATCH 1/4] Don't try to set MemberSpecialization on invalid var decl
chain
Patch #200092 changed to no longer check the previous var template when
setting whether the current one is a member specialization. However, if
the previous one was actually an error case (see the example here and in
the report), we ended up trying to do that anyway, which caused an
assertion.
This patch modifies the check to make sure it is valid to do so first,
so we don't overly propagate this issue when the original declaration
didn't exist.
Fixes: #209432
---
clang/lib/Sema/SemaDecl.cpp | 7 ++++++-
.../test/SemaTemplate/class-template-spec.cpp | 21 +++++++++++++++++++
2 files changed, 27 insertions(+), 1 deletion(-)
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 7e1b23c971a9c..f410d85e3ec3b 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -8409,7 +8409,12 @@ NamedDecl *Sema::ActOnVariableDeclarator(
if (IsMemberSpecialization) {
if (NewTemplate && NewVD->getPreviousDecl()) {
- NewTemplate->setMemberSpecialization();
+ // In valid code, this should always hold, but in the case where a
+ // previous declaration was invalid/didn't have an instantiated-from
+ // field, this will not have been set, so skip setting
+ // MemberSpecialization if we don't have an instantiated-from.
+ if (NewTemplate->getInstantiatedFromMemberTemplate())
+ NewTemplate->setMemberSpecialization();
} else if (IsPartialSpecialization) {
cast<VarTemplatePartialSpecializationDecl>(NewVD)
->setMemberSpecialization();
diff --git a/clang/test/SemaTemplate/class-template-spec.cpp b/clang/test/SemaTemplate/class-template-spec.cpp
index e2486f912e2c4..1dfe7fb255ac9 100644
--- a/clang/test/SemaTemplate/class-template-spec.cpp
+++ b/clang/test/SemaTemplate/class-template-spec.cpp
@@ -1,6 +1,7 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++98 %s
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
+// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx14 -std=c++14 %s
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++26 %s
template<typename T, typename U = int> struct A; // expected-note {{template is declared here}} \
@@ -251,4 +252,24 @@ namespace VarTemplateMismatch {
template<> template<int> const int A<short>::x = 0;
// expected-error at -1 {{template non-type parameter has a different type 'int' in template redeclaration}}
} // namespace VarTemplateMismatch
+
+namespace VarTemplateNoMember {
+ template<typename T> struct S {};
+ // expected-error at +1{{no member named 'foo' in 'VarTemplateNoMember::S<long>'}}
+ template<> template<typename U> constexpr int S<long>::foo;
+ // In C++14, these are definitions, not declarations, so they get a
+ // redefinition error.
+ // cxx14-error at +2{{redefinition of 'foo'}}
+ // cxx14-note at -4{{previous definition is here}}
+ template<> template<typename U> constexpr int S<long>::foo;
+ // cxx14-error at +2{{redefinition of 'foo'}}
+ // cxx14-note at -2{{previous definition is here}}
+ template<> template<typename U> constexpr int S<long>::foo;
+ // cxx14-error at +2{{redefinition of 'foo'}}
+ // cxx14-note at -2{{previous definition is here}}
+ template<> template<typename U> constexpr int S<long>::foo;
+ // cxx14-error at +2{{redefinition of 'foo'}}
+ // cxx14-note at -2{{previous definition is here}}
+ template<> template<typename U> constexpr int S<long>::foo;
+} // namespace VarTemplateNoMember
#endif
>From 1dd556ec5688cee6281de9d5ce14ffc5f0360703 Mon Sep 17 00:00:00 2001
From: erichkeane <ekeane at nvidia.com>
Date: Tue, 14 Jul 2026 13:22:50 -0700
Subject: [PATCH 2/4] Try fixing the same issue by setting the
instantiated-from member on error
---
clang/lib/Sema/SemaDecl.cpp | 18 ++++++++++++------
1 file changed, 12 insertions(+), 6 deletions(-)
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index f410d85e3ec3b..d476ff311c7cf 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -8374,6 +8374,17 @@ NamedDecl *Sema::ActOnVariableDeclarator(
<< Name << computeDeclContext(D.getCXXScopeSpec(), true)
<< D.getCXXScopeSpec().getRange();
NewVD->setInvalidDecl();
+
+ // if this is a member specialization, we don't have any primary template
+ // to be instantiated from. We set ourselves to a 'fake' clone of this so
+ // that anything that attempts to refer to this invalid declaration can
+ // act as if there IS a primary instantiation.
+ if (NewTemplate && IsMemberSpecialization) {
+ VarTemplateDecl *FakeInstantiatedFrom = VarTemplateDecl::Create(
+ Context, DC, D.getIdentifierLoc(), Name, TemplateParams, NewVD);
+ FakeInstantiatedFrom->setInvalidDecl();
+ NewTemplate->setInstantiatedFromMemberTemplate(FakeInstantiatedFrom);
+ }
}
if (!IsPlaceholderVariable)
@@ -8409,12 +8420,7 @@ NamedDecl *Sema::ActOnVariableDeclarator(
if (IsMemberSpecialization) {
if (NewTemplate && NewVD->getPreviousDecl()) {
- // In valid code, this should always hold, but in the case where a
- // previous declaration was invalid/didn't have an instantiated-from
- // field, this will not have been set, so skip setting
- // MemberSpecialization if we don't have an instantiated-from.
- if (NewTemplate->getInstantiatedFromMemberTemplate())
- NewTemplate->setMemberSpecialization();
+ NewTemplate->setMemberSpecialization();
} else if (IsPartialSpecialization) {
cast<VarTemplatePartialSpecializationDecl>(NewVD)
->setMemberSpecialization();
>From c04d26f8df69dd577e068b569b5e43e8378dbb75 Mon Sep 17 00:00:00 2001
From: erichkeane <ekeane at nvidia.com>
Date: Tue, 14 Jul 2026 13:52:08 -0700
Subject: [PATCH 3/4] Add triple to test
---
clang/test/SemaTemplate/class-template-spec.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/test/SemaTemplate/class-template-spec.cpp b/clang/test/SemaTemplate/class-template-spec.cpp
index 1dfe7fb255ac9..e60763feb2e1f 100644
--- a/clang/test/SemaTemplate/class-template-spec.cpp
+++ b/clang/test/SemaTemplate/class-template-spec.cpp
@@ -1,7 +1,7 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++98 %s
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
-// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx14 -std=c++14 %s
+// RUN: %clang_cc1 -fsyntax-only -triple x86_64-linux-gnu -verify=expected,cxx14 -std=c++14 %s
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++26 %s
template<typename T, typename U = int> struct A; // expected-note {{template is declared here}} \
>From 37d12bebebcdb8e02b63ba3a8726f25ba37cb29e Mon Sep 17 00:00:00 2001
From: erichkeane <ekeane at nvidia.com>
Date: Tue, 14 Jul 2026 14:10:45 -0700
Subject: [PATCH 4/4] Fake the vardecl sa well
---
clang/lib/Sema/SemaDecl.cpp | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index d476ff311c7cf..c5920f03ed6e1 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -8380,8 +8380,12 @@ NamedDecl *Sema::ActOnVariableDeclarator(
// that anything that attempts to refer to this invalid declaration can
// act as if there IS a primary instantiation.
if (NewTemplate && IsMemberSpecialization) {
+ VarDecl *FakeVD =
+ VarDecl::Create(Context, DC, D.getBeginLoc(), D.getIdentifierLoc(),
+ II, R, TInfo, SC);
+ FakeVD->setInvalidDecl();
VarTemplateDecl *FakeInstantiatedFrom = VarTemplateDecl::Create(
- Context, DC, D.getIdentifierLoc(), Name, TemplateParams, NewVD);
+ Context, DC, D.getIdentifierLoc(), Name, TemplateParams, FakeVD);
FakeInstantiatedFrom->setInvalidDecl();
NewTemplate->setInstantiatedFromMemberTemplate(FakeInstantiatedFrom);
}
More information about the cfe-commits
mailing list