[clang] [clang][sema] Fix crash on class template instantiation when a member variable partial specialization has an invalid primary template (PR #202006)
Harlen Batagelo via cfe-commits
cfe-commits at lists.llvm.org
Mon Jul 13 19:05:24 PDT 2026
https://github.com/hbatagelo updated https://github.com/llvm/llvm-project/pull/202006
>From 36cdf84c20828a550e0334bb31834fe86f8f08aa Mon Sep 17 00:00:00 2001
From: Harlen Batagelo <hbatagelo at gmail.com>
Date: Sat, 6 Jun 2026 02:05:54 -0300
Subject: [PATCH 1/5] Bail out when the lookup finds nothing
---
clang/docs/ReleaseNotes.rst | 1 +
clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 3 ++-
clang/test/SemaTemplate/GH195988.cpp | 8 ++++++++
3 files changed, 11 insertions(+), 1 deletion(-)
create mode 100644 clang/test/SemaTemplate/GH195988.cpp
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index cf4826f50e5a5..774a650c2c517 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -731,6 +731,7 @@ Bug Fixes to C++ Support
- Fixed a use-after-free bug when parsing default arguments containing lambdas in declarations with template-id declarators. (#GH196725)
- Fixed a crash in constant evaluation using placement new on an array which was later initialized. (#GH196450)
- Fixed an issue where Clang incorrectly accepted invalid unqualified uses of local nested class names outside their declaring scope. (#GH184622)
+- Fixed a crash when instantiating a class template whose member variable partial specialization has an invalid primary template. (#GH195988)
Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 324d6bf3857c7..d6e0b63f71e54 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -2535,7 +2535,8 @@ Decl *TemplateDeclInstantiator::VisitVarTemplatePartialSpecializationDecl(
// Lookup the already-instantiated declaration and return that.
DeclContext::lookup_result Found = Owner->lookup(VarTemplate->getDeclName());
- assert(!Found.empty() && "Instantiation found nothing?");
+ if (Found.empty())
+ return nullptr;
VarTemplateDecl *InstVarTemplate = dyn_cast<VarTemplateDecl>(Found.front());
assert(InstVarTemplate && "Instantiation did not find a variable template?");
diff --git a/clang/test/SemaTemplate/GH195988.cpp b/clang/test/SemaTemplate/GH195988.cpp
new file mode 100644
index 0000000000000..1924a8c98c262
--- /dev/null
+++ b/clang/test/SemaTemplate/GH195988.cpp
@@ -0,0 +1,8 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+template <typename> struct A {
+ template <typename T> static B x; // expected-error {{unknown type name 'B'}}
+ template <typename T> static int x<T*>;
+};
+
+A<int> a;
>From 16c5b0ce33aecf4285152b2f139b859eed99c493 Mon Sep 17 00:00:00 2001
From: Harlen Batagelo <hbatagelo at gmail.com>
Date: Wed, 17 Jun 2026 00:45:25 -0300
Subject: [PATCH 2/5] Keep assertion and bail out before lookup
---
clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index d6e0b63f71e54..c58ed293deca5 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -2532,11 +2532,12 @@ Decl *TemplateDeclInstantiator::VisitVarTemplatePartialSpecializationDecl(
"Only static data member templates are allowed.");
VarTemplateDecl *VarTemplate = D->getSpecializedTemplate();
+ if (VarTemplate->isInvalidDecl())
+ return nullptr;
// Lookup the already-instantiated declaration and return that.
DeclContext::lookup_result Found = Owner->lookup(VarTemplate->getDeclName());
- if (Found.empty())
- return nullptr;
+ assert(!Found.empty() && "Instantiation found nothing?");
VarTemplateDecl *InstVarTemplate = dyn_cast<VarTemplateDecl>(Found.front());
assert(InstVarTemplate && "Instantiation did not find a variable template?");
>From b78cc188253ffbc591c8100247bce05fa868e46f Mon Sep 17 00:00:00 2001
From: Harlen Batagelo <hbatagelo at gmail.com>
Date: Fri, 26 Jun 2026 19:02:08 -0300
Subject: [PATCH 3/5] Fallback to int to keep a valid AST
---
clang/lib/Sema/SemaDecl.cpp | 4 +++-
clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 2 --
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index cddcf3a010279..495486656ae8e 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -8055,7 +8055,9 @@ NamedDecl *Sema::ActOnVariableDeclarator(
if (R->getContainedDeducedType())
ParsingInitForAutoVars.insert(NewVD);
- if (D.isInvalidType() || Invalid) {
+ // Mark the declaration as invalid on error, but exempt variable templates
+ // with invalid types for error recovery.
+ if ((D.isInvalidType() && !IsVariableTemplate) || Invalid) {
NewVD->setInvalidDecl();
if (NewTemplate)
NewTemplate->setInvalidDecl();
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index c58ed293deca5..324d6bf3857c7 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -2532,8 +2532,6 @@ Decl *TemplateDeclInstantiator::VisitVarTemplatePartialSpecializationDecl(
"Only static data member templates are allowed.");
VarTemplateDecl *VarTemplate = D->getSpecializedTemplate();
- if (VarTemplate->isInvalidDecl())
- return nullptr;
// Lookup the already-instantiated declaration and return that.
DeclContext::lookup_result Found = Owner->lookup(VarTemplate->getDeclName());
>From 6e21b630e3e0d761fa4443ee8086736ed04e03a3 Mon Sep 17 00:00:00 2001
From: Harlen Batagelo <hbatagelo at gmail.com>
Date: Wed, 1 Jul 2026 22:14:00 -0300
Subject: [PATCH 4/5] Fix InstantiateClassImpl instead
---
clang/lib/Sema/SemaDecl.cpp | 4 +---
clang/lib/Sema/SemaTemplateInstantiate.cpp | 7 ++++++-
2 files changed, 7 insertions(+), 4 deletions(-)
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 8effeab6c9281..d45c3eb35094f 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -8055,9 +8055,7 @@ NamedDecl *Sema::ActOnVariableDeclarator(
if (R->getContainedDeducedType())
ParsingInitForAutoVars.insert(NewVD);
- // Mark the declaration as invalid on error, but exempt variable templates
- // with invalid types for error recovery.
- if ((D.isInvalidType() && !IsVariableTemplate) || Invalid) {
+ if (D.isInvalidType() || Invalid) {
NewVD->setInvalidDecl();
if (NewTemplate)
NewTemplate->setInvalidDecl();
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index a77ea5fd3dfff..ad37903f03c9b 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -3623,7 +3623,9 @@ bool Sema::InstantiateClassImpl(
if (Member->isInvalidDecl()) {
Instantiation->setInvalidDecl();
- continue;
+ // Keep invalid variable templates for partial specialization lookup.
+ if (!isa<VarTemplateDecl>(Member))
+ continue;
}
Decl *NewMember = Instantiator.Visit(Member);
@@ -3655,6 +3657,9 @@ bool Sema::InstantiateClassImpl(
MightHaveConstexprVirtualFunctions = true;
}
+ if (Member->isInvalidDecl())
+ NewMember->setInvalidDecl();
+
if (NewMember->isInvalidDecl())
Instantiation->setInvalidDecl();
} else {
>From 722f46a1a578c278cefaed2203bc7641fd185fa6 Mon Sep 17 00:00:00 2001
From: Harlen Batagelo <hbatagelo at gmail.com>
Date: Mon, 13 Jul 2026 23:05:02 -0300
Subject: [PATCH 5/5] Update comment
---
clang/lib/Sema/SemaTemplateInstantiate.cpp | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index ad37903f03c9b..e7861400786ac 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -3623,7 +3623,10 @@ bool Sema::InstantiateClassImpl(
if (Member->isInvalidDecl()) {
Instantiation->setInvalidDecl();
- // Keep invalid variable templates for partial specialization lookup.
+ // Drop invalid members to prevent cascading diagnostic errors.
+ // We make an exception for VarTemplateDecl because the primary template
+ // is required for partial specialization lookup. Keeping it is safe from
+ // cascading errors due to the parser's type recovery.
if (!isa<VarTemplateDecl>(Member))
continue;
}
More information about the cfe-commits
mailing list