[clang] [Clang][CodeGen] Fix crash on value-dependent initializer during error recovery (PR #181561)
via cfe-commits
cfe-commits at lists.llvm.org
Sun Feb 15 10:40:31 PST 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Giovanni B. (Z3rox-dev)
<details>
<summary>Changes</summary>
## Summary
Fix crash-on-invalid when a global variable has a value-dependent initializer due to error recovery from an invalid template partial specialization.
Fixes #<!-- -->181505
## Problem
When Clang encounters an invalid template partial specialization (e.g., one that is not more specialized than the primary template), it recovers and continues compilation. However, if a global variable's initializer depends on such an invalid specialization, the initializer remains **value-dependent** at codegen time. This previously triggered:
1. `assert(!Init->isValueDependent())` in `VarDecl::evaluateValueImpl`
2. `UNREACHABLE non-canonical or dependent type in IR-generation`
**Reproducer** ([godbolt](https://godbolt.org/z/aEqoxaWcG)):
```cpp
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>{})){};
```
## Fix
Added defensive checks for value-dependent initializers in three places along the CodeGen pipeline:
- **`VarDecl::evaluateValueImpl()`** (`clang/lib/AST/Decl.cpp`): Check `isValueDependent()` and return `nullptr` instead of asserting. Callers already handle null returns.
- **`ConstantEmitter::tryEmitPrivateForVarInit()`** (`clang/lib/CodeGen/CGExprConstant.cpp`): Bail out early if the initializer is value-dependent, since we cannot lower dependent expressions to LLVM IR.
- **`CodeGenModule::EmitGlobalVarDefinition()`** (`clang/lib/CodeGen/CodeGenModule.cpp`): Fall back to the variable's declared type when the init expression's type is dependent, and skip global constructor registration for value-dependent initializers.
## Test
Added `clang/test/CodeGenCXX/crash-value-dependent-init.cpp` - verifies that `clang -emit-llvm` with the reproducer exits with an error (from the diagnostic) but does not crash.
---
Full diff: https://github.com/llvm/llvm-project/pull/181561.diff
4 Files Affected:
- (modified) clang/lib/AST/Decl.cpp (+7-2)
- (modified) clang/lib/CodeGen/CGExprConstant.cpp (+6)
- (modified) clang/lib/CodeGen/CodeGenModule.cpp (+7-4)
- (added) clang/test/CodeGenCXX/crash-value-dependent-init.cpp (+28)
``````````diff
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..0b0e713d969e3 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -2928,9 +2928,6 @@ void CodeGenModule::SetLLVMFunctionAttributesForDefinition(const Decl *D,
B.addAttribute(llvm::Attribute::MinSize);
}
- if (D->hasAttr<NoOutlineAttr>())
- B.addAttribute(llvm::Attribute::NoOutline);
-
F->addFnAttrs(B);
unsigned alignment = D->getMaxAlignment() / Context.getCharWidth();
@@ -6134,10 +6131,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..f85c7ef1950e2
--- /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>{})){};
``````````
</details>
https://github.com/llvm/llvm-project/pull/181561
More information about the cfe-commits
mailing list