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 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
