https://github.com/erichkeane updated https://github.com/llvm/llvm-project/pull/209604
>From 9b0c9adbc73af24daf5da4434cdbd208ed06ddd6 Mon Sep 17 00:00:00 2001 From: erichkeane <[email protected]> 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@-1 {{template non-type parameter has a different type 'int' in template redeclaration}} } // namespace VarTemplateMismatch + +namespace VarTemplateNoMember { + template<typename T> struct S {}; + // expected-error@+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@+2{{redefinition of 'foo'}} + // cxx14-note@-4{{previous definition is here}} + template<> template<typename U> constexpr int S<long>::foo; + // cxx14-error@+2{{redefinition of 'foo'}} + // cxx14-note@-2{{previous definition is here}} + template<> template<typename U> constexpr int S<long>::foo; + // cxx14-error@+2{{redefinition of 'foo'}} + // cxx14-note@-2{{previous definition is here}} + template<> template<typename U> constexpr int S<long>::foo; + // cxx14-error@+2{{redefinition of 'foo'}} + // cxx14-note@-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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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); } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
