[clang] 642630a - [C++20] [Modules] Preserve the initializer for unused static variable in modules (#218304)
via cfe-commits
cfe-commits at lists.llvm.org
Sun Aug 23 20:04:59 PDT 2026
Author: Chuanqi Xu
Date: 2026-08-24T03:04:53Z
New Revision: 642630ac49da969f8129c8695f38bfe563ecfb26
URL: https://github.com/llvm/llvm-project/commit/642630ac49da969f8129c8695f38bfe563ecfb26
DIFF: https://github.com/llvm/llvm-project/commit/642630ac49da969f8129c8695f38bfe563ecfb26.diff
LOG: [C++20] [Modules] Preserve the initializer for unused static variable in modules (#218304)
Close https://github.com/llvm/llvm-project/issues/212170
See the discussion above for the details.
Added:
clang/test/Modules/pr212170.cppm
Modified:
clang/lib/Sema/SemaDecl.cpp
Removed:
################################################################################
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 5055ef3d1cbb1..a99fcb56d1138 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -15378,12 +15378,17 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) {
FinalizeVarWithDestructor(var, RD);
// If this variable must be emitted, add it as an initializer for the current
- // module.
- if (Context.DeclMustBeEmitted(var) && !ModuleScopes.empty() &&
- (ModuleScopes.back().Module->isHeaderLikeModule() ||
- // For named modules, we may only emit non discardable variables.
- !isDiscardableGVALinkage(Context.GetGVALinkageForVariable(var))))
- Context.addModuleInitializer(ModuleScopes.back().Module, var);
+ // module. For named modules, discardable inline variables may be deferred
+ // until they are odr-used. Non-inline variables that must be emitted,
+ // including those with side-effecting initialization, must still be emitted
+ // even if they have internal linkage.
+ if (Context.DeclMustBeEmitted(var) && !ModuleScopes.empty()) {
+ GVALinkage Linkage = Context.GetGVALinkageForVariable(var);
+ if (ModuleScopes.back().Module->isHeaderLikeModule() ||
+ !isDiscardableGVALinkage(Linkage) ||
+ (Linkage == GVA_Internal && !var->isInline()))
+ Context.addModuleInitializer(ModuleScopes.back().Module, var);
+ }
// Build the bindings if this is a structured binding declaration.
if (auto *DD = dyn_cast<DecompositionDecl>(var))
diff --git a/clang/test/Modules/pr212170.cppm b/clang/test/Modules/pr212170.cppm
new file mode 100644
index 0000000000000..65442d011cc53
--- /dev/null
+++ b/clang/test/Modules/pr212170.cppm
@@ -0,0 +1,54 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 \
+// RUN: -emit-module-interface %t/test.cppm -o %t/test.pcm
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 \
+// RUN: -emit-llvm %t/test.pcm -o - | FileCheck %t/test.cppm \
+// RUN: --implicit-check-not=inline_bar \
+// RUN: --implicit-check-not=static_inline_bar
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 \
+// RUN: -fmodule-file=test=%t/test.pcm -emit-llvm %t/use.cpp -o - | \
+// RUN: FileCheck %t/use.cpp
+
+// https://github.com/llvm/llvm-project/issues/212170
+
+//--- test.cppm
+
+export module test;
+
+int side_effect();
+
+static int bar = side_effect();
+[[gnu::used]] static int used_bar = side_effect();
+
+inline int inline_bar = side_effect();
+static inline int static_inline_bar = side_effect();
+
+// CHECK-DAG: @_ZL3bar = internal global i32 0
+// CHECK-DAG: @_ZL8used_bar = internal global i32 0
+
+// CHECK-LABEL: define internal void @__cxx_global_var_init()
+// CHECK: call{{.*}} @_ZW4test11side_effectv()
+// CHECK: store i32 {{.*}}, ptr @_ZL3bar
+
+// CHECK-LABEL: define internal void @__cxx_global_var_init.1()
+// CHECK: call{{.*}} @_ZW4test11side_effectv()
+// CHECK: store i32 {{.*}}, ptr @_ZL8used_bar
+
+// CHECK-LABEL: define void @_ZGIW4test()
+// CHECK: call void @__cxx_global_var_init()
+// CHECK: call void @__cxx_global_var_init.1()
+
+//--- use.cpp
+
+import test;
+
+int main() {}
+
+// The consumer calls the module initializer but does not emit copies of the
+// module's internal variables or their initialization functions.
+// CHECK-NOT: @_ZL3bar
+// CHECK-NOT: @_ZL8used_bar
+// CHECK: declare void @_ZGIW4test()
+// CHECK-LABEL: define internal void @_GLOBAL__sub_I_use.cpp()
+// CHECK: call void @_ZGIW4test()
More information about the cfe-commits
mailing list