[clang] [Clang] [Sema] Fix crash instantiating a member variable template partial specialization when the enclosing class template is deserialized (PR #202958)
Rex Technology via cfe-commits
cfe-commits at lists.llvm.org
Thu Jun 11 12:00:05 PDT 2026
https://github.com/RexTechnology1 updated https://github.com/llvm/llvm-project/pull/202958
>From 64df0a761cb0a5849f514475d8241f13e53afeed Mon Sep 17 00:00:00 2001
From: rextechnology <rextechnology1a at gmail.com>
Date: Tue, 9 Jun 2026 21:32:36 +0300
Subject: [PATCH] [Clang][Sema] Fix crash instantiating a member variable
template partial specialization when the enclosing class template is
deserialized
VisitVarTemplatePartialSpecializationDecl assumes the primary member variable
template has already been instantiated into the current instantiation (Owner),
which is normally true because the primary is declared before its partial
specializations and is visited first while the enclosing class is instantiated.
When the enclosing class template's pattern is deserialized from a precompiled
preamble built with compiler errors (-fallow-pch-with-compiler-errors, which
clangd uses for preambles), the primary may not have been materialized into
Owner yet, leaving the lookup empty. This tripped the
assert(!Found.empty() && "Instantiation found nothing?") in assertions builds
and dereferenced a null Found.front() (SIGSEGV) in release builds.
This is hit in practice by clangd: completing std::expected<T, E> from
libstdc++ 15.x crashes whenever the preamble contains an unresolved #include,
a routine state in interactive editing (e.g. not-yet-generated headers). All
released clang versions tested (20, 21, 22) and current trunk are affected.
Fix: when the lookup is empty, instantiate the primary member variable template
on demand and look it up again, which restores the invariant the assertions
expect. The original invariant assertions are kept, so a genuinely-missing
primary is still flagged in +Asserts builds, with a null return as a
release-safe guard. The common (non-deserialized) path is unchanged.
Signed-off-by: rextechnology <rextechnology1a at gmail.com>
Assisted-by: Claude (Anthropic)
---
.../lib/Sema/SemaTemplateInstantiateDecl.cpp | 24 +++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index aa381f09138de..c11312843b18e 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -2558,10 +2558,34 @@ Decl *TemplateDeclInstantiator::VisitVarTemplatePartialSpecializationDecl(
// Lookup the already-instantiated declaration and return that.
DeclContext::lookup_result Found = Owner->lookup(VarTemplate->getDeclName());
+
+ // Normally the primary member variable template has already been instantiated
+ // into Owner, because it is declared before its partial specializations and
+ // so is visited first while instantiating the enclosing class. However, when
+ // the class template pattern is deserialized from a module or precompiled
+ // preamble, the primary may not have been materialized into Owner yet,
+ // leaving the lookup empty. Previously this asserted (and crashed release
+ // builds with a null dereference). Instantiate the primary on demand and look
+ // it up again.
+ if (Found.empty()) {
+ if (Decl *InstPrimary = Visit(VarTemplate))
+ if (auto *InstVTD = dyn_cast<VarTemplateDecl>(InstPrimary))
+ Found = Owner->lookup(InstVTD->getDeclName());
+ }
+
+ // After the on-demand instantiation above the primary must be present. Keep
+ // the original invariant assertions so a genuinely-missing primary is still
+ // flagged in +Asserts builds, with a null return as a release-safe guard so a
+ // stray case degrades gracefully instead of dereferencing an empty lookup
+ // result.
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?");
+ if (!InstVarTemplate)
+ return nullptr;
if (VarTemplatePartialSpecializationDecl *Result =
InstVarTemplate->findPartialSpecInstantiatedFromMember(D))
More information about the cfe-commits
mailing list