[clang] [clang][CodeGen] Emit file-scope compound literals in a constant context (PR #221390)
via cfe-commits
cfe-commits at lists.llvm.org
Sat Sep 5 00:16:24 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clangir
Author: Akash Manna (akash-manna-sky)
<details>
<summary>Changes</summary>
Fixes #<!-- -->212106
A file-scope compound literal is always a constant-initialized global, and Sema checks its initializer in a constant context, where `__builtin_constant_p` of something it can't fold just yields 0. CodeGen evaluated the same initializer under whatever context the *caller* was in. When the literal's address is taken by a global that needs dynamic initialization, that context is non-constant, `__builtin_constant_p` refuses to fold, and `tryEmitGlobalCompoundLiteral` asserts. The strict-FP variant of this was fixed in D131555 by propagating the caller's context, which didn't cover this entry point.
The literal's constant-ness is a property of the literal, not of whoever asks for its address, so `tryEmitGlobalCompoundLiteral` now sets the constant context itself for any file-scope literal. That covers both callers and keeps the assertion in place. The same two lines are mirrored in CIR, which has the identical check.
---
Full diff: https://github.com/llvm/llvm-project/pull/221390.diff
5 Files Affected:
- (modified) clang/docs/ReleaseNotes.md (+4)
- (modified) clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp (+6)
- (modified) clang/lib/CodeGen/CGExprConstant.cpp (+6)
- (modified) clang/test/AST/static-compound-literals-crash.cpp (+2-10)
- (added) clang/test/CodeGenCXX/GH212106.cpp (+49)
``````````diff
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 042d7112dbe7d..18de7e72abef4 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -652,6 +652,10 @@ features cannot lower the translation-unit ABI level;
- Fixed a crash when generating fake uses for parameters of bodyless destructors with `-fextend-variable-liveness`.
- Fixed an assertion failure when instantiating a block that captures
`this` via a member access through a dependent base class.
+- Fixed an assertion failure when the dynamic initializer of a global variable
+ takes the address of a file-scope compound literal whose initializer is only
+ constant under constant-evaluation rules, such as `__builtin_constant_p` of a
+ non-constant expression. (#GH212106)
### OpenACC Specific Changes
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
index 4bebe053f5768..d036d73388100 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
@@ -941,6 +941,12 @@ tryEmitGlobalCompoundLiteral(ConstantEmitter &emitter,
if (cir::GlobalOp addr = cgm.getAddrOfConstantCompoundLiteralIfEmitted(e))
return builder.getGlobalViewAttr(addr);
+ // A file-scope compound literal is a constant-initialized global, so emit
+ // its initializer under constant-evaluation rules even when reached from a
+ // non-constant context.
+ if (e->isFileScope())
+ emitter.setInConstantContext(true);
+
assert(!cir::MissingFeatures::addressSpace());
mlir::Attribute c =
emitter.tryEmitForInitializer(e->getInitializer(), e->getType());
diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp
index 48e80910ce577..cf2a4ff18cea6 100644
--- a/clang/lib/CodeGen/CGExprConstant.cpp
+++ b/clang/lib/CodeGen/CGExprConstant.cpp
@@ -1087,6 +1087,12 @@ tryEmitGlobalCompoundLiteral(ConstantEmitter &emitter,
CGM.getAddrOfConstantCompoundLiteralIfEmitted(E))
return ConstantAddress(Addr, Addr->getValueType(), Align);
+ // A file-scope compound literal is a constant-initialized global, so emit
+ // its initializer under constant-evaluation rules even when reached from a
+ // non-constant context such as the dynamic initializer of another global.
+ if (E->isFileScope())
+ emitter.setInConstantContext(true);
+
LangAS addressSpace = E->getType().getAddressSpace();
llvm::Constant *C = emitter.tryEmitForInitializer(E->getInitializer(),
addressSpace, E->getType());
diff --git a/clang/test/AST/static-compound-literals-crash.cpp b/clang/test/AST/static-compound-literals-crash.cpp
index f9c3bd82fd025..838d67b2f3f17 100644
--- a/clang/test/AST/static-compound-literals-crash.cpp
+++ b/clang/test/AST/static-compound-literals-crash.cpp
@@ -1,5 +1,5 @@
-// FIXME: These test cases currently crash during codegen, despite initializers
-// for CLEs being constant.
+// FIXME: This test case currently crashes during codegen, despite the
+// initializer for the CLE being constant.
// RUN: not --crash %clang_cc1 -verify -std=c++20 -emit-llvm %s -o -
// expected-no-diagnostics
namespace case1 {
@@ -7,11 +7,3 @@ struct RR { int&& r; };
struct Z { RR* x; };
constinit Z z = { (RR[1]){1} };
}
-
-
-namespace case2 {
-struct RR { int r; };
-struct Z { int x; const RR* y; int z; };
-inline int f() { return 0; }
-Z z2 = { 10, (const RR[1]){__builtin_constant_p(z2.x)}, z2.y->r+f() };
-}
diff --git a/clang/test/CodeGenCXX/GH212106.cpp b/clang/test/CodeGenCXX/GH212106.cpp
new file mode 100644
index 0000000000000..dfdf5dd96924d
--- /dev/null
+++ b/clang/test/CodeGenCXX/GH212106.cpp
@@ -0,0 +1,49 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -ffp-exception-behavior=strict -emit-llvm -o - %s | FileCheck %s
+
+// A file-scope compound literal is a constant-initialized global even when its
+// address is taken from a non-constant context, so its initializer must be
+// emitted under constant-evaluation rules.
+
+struct RR { int r; };
+struct Z { int x; const RR* y; int z; };
+inline int f() { return 0; }
+Z z2 = { 10, (const RR[1]){__builtin_constant_p(z2.x)}, z2.y->r+f() };
+
+// CHECK-DAG: @z2 = {{.*}}global %struct.Z zeroinitializer
+// CHECK-DAG: [[Z2CL:@.compoundliteral(\.[0-9]+)?]] = internal constant [1 x %struct.RR] zeroinitializer
+
+namespace reduced {
+struct Z { const int* y; int z; };
+int f();
+Z z2 = { (int[1]){__builtin_constant_p(z2.z)}, f() };
+}
+
+// CHECK-DAG: @_ZN7reduced2z2E = {{.*}}global %"struct.reduced::Z" zeroinitializer
+// CHECK-DAG: @.compoundliteral{{(\.[0-9]+)?}} = internal global [1 x i32] zeroinitializer
+
+struct F { int a; const float *fp; };
+int g();
+F fl = { g(), (float[1]){0.1} };
+
+// CHECK-DAG: @fl = {{.*}}global %struct.F zeroinitializer
+// CHECK-DAG: @.compoundliteral{{(\.[0-9]+)?}} = internal global [1 x float] [float 1.000000e-01]
+
+const RR *p = (const RR[1]){__builtin_constant_p(1)};
+
+// CHECK-DAG: @.compoundliteral{{(\.[0-9]+)?}} = internal constant [1 x %struct.RR] [%struct.RR { i32 1 }]
+// CHECK-DAG: @p = {{.*}}global ptr @.compoundliteral{{(\.[0-9]+)?}}
+
+// A default member initializer at namespace scope is also a file-scope compound
+// literal, reached here from a constructor emitted for a local variable.
+extern int n;
+struct Q { const int *m = (const int[1]){__builtin_constant_p(n)}; };
+void h() { Q q; }
+
+// CHECK-DAG: [[QCL:@.compoundliteral(\.[0-9]+)?]] = internal constant [1 x i32] zeroinitializer
+
+// CHECK-LABEL: define internal void @__cxx_global_var_init()
+// CHECK: store ptr [[Z2CL]], ptr getelementptr inbounds{{.*}}(i8, ptr @z2, i64 8)
+
+// CHECK-LABEL: define {{.*}}void @_ZN1QC2Ev(
+// CHECK: store ptr [[QCL]], ptr
``````````
</details>
https://github.com/llvm/llvm-project/pull/221390
More information about the cfe-commits
mailing list