[clang] [Clang][CodeGen] Fix crash on value-dependent initializer during error recovery (PR #181561)
Giovanni B. via cfe-commits
cfe-commits at lists.llvm.org
Sun Feb 15 12:03:36 PST 2026
https://github.com/Z3rox-dev updated https://github.com/llvm/llvm-project/pull/181561
>From 5e07a4e662a671b6116e755ef8aa66fc9b050d8e Mon Sep 17 00:00:00 2001
From: Z3rox-dev <giocuffietta at gmail.com>
Date: Sun, 15 Feb 2026 21:03:16 +0100
Subject: [PATCH] [Clang][CodeGen] Fix crash on value-dependent initializer
during error recovery
In error-recovery scenarios (e.g., invalid template partial specializations),
a global variable's initializer can remain value-dependent. Previously this
triggered:
1. assert(!Init->isValueDependent()) in VarDecl::evaluateValueImpl
2. UNREACHABLE 'non-canonical or dependent type in IR-generation'
This patch adds defensive checks for value-dependent initializers in three
places in the CodeGen pipeline:
- VarDecl::evaluateValueImpl(): return nullptr instead of asserting
- ConstantEmitter::tryEmitPrivateForVarInit(): bail out early
- CodeGenModule::EmitGlobalVarDefinition(): handle dependent types and
skip global ctor registration for value-dependent inits
Fixes #181410
---
clang/lib/AST/Decl.cpp | 9 ++++--
clang/lib/CodeGen/CGExprConstant.cpp | 6 ++++
clang/lib/CodeGen/CodeGenModule.cpp | 8 +++++-
.../CodeGenCXX/crash-value-dependent-init.cpp | 28 +++++++++++++++++++
4 files changed, 48 insertions(+), 3 deletions(-)
create mode 100644 clang/test/CodeGenCXX/crash-value-dependent-init.cpp
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index 66c625f41158a..173d48135e43c 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -2593,7 +2593,11 @@ APValue *VarDecl::evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> &Notes,
EvaluatedStmt *Eval = ensureEvaluatedStmt();
const auto *Init = getInit();
- assert(!Init->isValueDependent());
+ // In error-recovery scenarios (e.g., invalid template specializations),
+ // the initializer may remain value-dependent. Bail out gracefully instead
+ // of crashing, since callers already handle nullptr returns.
+ if (!Init || Init->isValueDependent())
+ return nullptr;
// We only produce notes indicating why an initializer is non-constant the
// first time it is evaluated. FIXME: The notes won't always be emitted the
@@ -2684,7 +2688,8 @@ bool VarDecl::checkForConstantInitialization(
getASTContext().getLangOpts().C23) &&
"only meaningful in C++/C23");
- assert(!getInit()->isValueDependent());
+ if (!getInit() || getInit()->isValueDependent())
+ return false;
// Evaluate the initializer to check whether it's a constant expression.
Eval->HasConstantInitialization =
diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp
index c316642a87baf..4822eb47716a8 100644
--- a/clang/lib/CodeGen/CGExprConstant.cpp
+++ b/clang/lib/CodeGen/CGExprConstant.cpp
@@ -1873,6 +1873,12 @@ llvm::Constant *ConstantEmitter::tryEmitPrivateForVarInit(const VarDecl &D) {
const Expr *E = D.getInit();
assert(E && "No initializer to emit");
+ // In error-recovery scenarios (e.g., invalid template specializations),
+ // the initializer may remain value-dependent. We cannot evaluate or
+ // lower dependent expressions to LLVM IR, so bail out.
+ if (E->isValueDependent())
+ return nullptr;
+
if (!destType->isReferenceType()) {
QualType nonMemoryDestType = getNonMemoryType(CGM, destType);
if (llvm::Constant *C = ConstExprEmitter(*this).Visit(E, nonMemoryDestType))
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 6a087be3751f0..92bc3786f4631 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -6134,10 +6134,16 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D,
QualType T = InitExpr->getType();
if (D->getType()->isReferenceType())
T = D->getType();
+ // In error-recovery, the init expression's type may still be dependent.
+ // Fall back to the variable's declared type which should be concrete.
+ if (T->isDependentType())
+ T = D->getType();
if (getLangOpts().CPlusPlus) {
Init = EmitNullConstant(T);
- if (!IsDefinitionAvailableExternally)
+ // If the initializer is value-dependent (error recovery with invalid
+ // templates), we cannot lower it to IR, so skip the global ctor.
+ if (!IsDefinitionAvailableExternally && !InitExpr->isValueDependent())
NeedsGlobalCtor = true;
if (InitDecl->hasFlexibleArrayInit(getContext())) {
ErrorUnsupported(D, "flexible array initializer");
diff --git a/clang/test/CodeGenCXX/crash-value-dependent-init.cpp b/clang/test/CodeGenCXX/crash-value-dependent-init.cpp
new file mode 100644
index 0000000000000..b456004e656d1
--- /dev/null
+++ b/clang/test/CodeGenCXX/crash-value-dependent-init.cpp
@@ -0,0 +1,28 @@
+// RUN: not %clang_cc1 -std=c++20 -emit-llvm %s
+
+// Verify that we don't crash when a global variable has a value-dependent
+// initializer due to an invalid template partial specialization that the
+// compiler recovers from (crash-on-invalid).
+//
+// Previously this triggered:
+// 1. assert(!Init->isValueDependent()) in VarDecl::evaluateValueImpl
+// 2. UNREACHABLE "non-canonical or dependent type in IR-generation"
+
+template <int>
+struct integer_sequence {};
+
+template <int>
+struct array {};
+
+template <int*>
+struct MetaValuesHelper;
+
+template <typename TupleName, TupleName kValues>
+struct MetaValuesHelper<kValues> {
+ template <int... Is>
+ static array<0> MetaValuesFunc(integer_sequence<Is...>);
+};
+
+int kBaseIndexRegistersUsed;
+
+array<0> u = decltype(MetaValuesHelper<&kBaseIndexRegistersUsed>::MetaValuesFunc(integer_sequence<0>{})){};
More information about the cfe-commits
mailing list