Index: clang/lib/Serialization/ASTWriterDecl.cpp =================================================================== --- clang/lib/Serialization/ASTWriterDecl.cpp +++ clang/lib/Serialization/ASTWriterDecl.cpp @@ -1022,15 +1022,27 @@ if (Writer.WritingModule && !D->getDescribedVarTemplate() && !D->getMemberSpecializationInfo() && !isa(D)) { - // When building a C++ Modules TS module interface unit, a strong - // definition in the module interface is provided by the compilation of - // that module interface unit, not by its users. (Inline variables are - // still emitted in module users.) - ModulesCodegen = - (Writer.WritingModule->Kind == Module::ModuleInterfaceUnit || - (D->hasAttr() && - Writer.Context->getLangOpts().BuildingPCHWithObjectFile)) && - Writer.Context->GetGVALinkageForVariable(D) == GVA_StrongExternal; + // When building a C++20 Modules module interface unit, a strong + // definition in the module interface is provided by the module interface + // unit, not by its users. (Inline variables are still emitted in module + // users.) + auto Linkage = Writer.Context->GetGVALinkageForVariable(D); + if (Writer.WritingModule->Kind == Module::ModuleInterfaceUnit) { + ModulesCodegen = Linkage == GVA_StrongExternal; + if (Linkage == GVA_Internal) { + // FIXME: We emit internal variable with dynamic initializer here + // otherwise we would fail to compile a hello-world example. (See + // https://github.com/llvm/llvm-project/issues/51873). Look back here + // once we get rules in CXXABI for variables in named module. + const VarDecl *InitDecl; + const Expr *InitExpr = D->getAnyInitializer(InitDecl); + ModulesCodegen = InitExpr && InitDecl; + } + } else + ModulesCodegen = + D->hasAttr() && + Writer.Context->getLangOpts().BuildingPCHWithObjectFile && + Writer.Context->GetGVALinkageForVariable(D) == GVA_StrongExternal; } Record.push_back(ModulesCodegen); if (ModulesCodegen) Index: clang/test/CodeGenCXX/static-variable-in-module.cpp =================================================================== --- /dev/null +++ clang/test/CodeGenCXX/static-variable-in-module.cpp @@ -0,0 +1,34 @@ +// RUN: rm -fr %t +// RUN: mkdir %t +// RUN: echo "struct S { S(); };" >> %t/foo.h +// RUN: echo "static S s = S();" >> %t/foo.h +// RUN: echo "static int static_init_gmf = 43;" >> %t/foo.h +// RUN: %clang_cc1 -std=c++20 -I%t %s -emit-module-interface -o %t/m.pcm +// RUN: %clang_cc1 -std=c++20 %t/m.pcm -triple %itanium_abi_triple -S -emit-llvm -o - | FileCheck %s +module; +#include "foo.h" +export module m; +class A { +public: + A(); +}; +static A a = A(); +static int static_init = 43; + +// We shouldn't emit unused internal linkage variable. +// CHECK-NOT: static_init_gmf +// CHECK: @_ZW1mEL1s = internal global %struct.S zeroinitializer +// CHECK: @_ZW1mE1a = {{(dso_local )?}}global %class.A zeroinitializer +// This is surprising at the first sight. But given [basic.link]4.8: +// - otherwise, if the declaration of the name is attached to a named module ([module.unit]) and is not exported ([module.interface]), the name has module linkage; +// +// So static_init has module linkage instead of internal linkage. So we should emit it even if it is not used. +// CHECK: @_ZW1mE11static_init = global i32 43 +// CHECK: @llvm.global_ctors = appending global{{.*}}@_GLOBAL__sub_I_m.pcm +// CHECK: define {{.*}}__cxx_global_var_init[[SUFFIX:[^)]*]] +// CHECK: call void @_ZN1SC1Ev +// CHECK: define {{.*}}__cxx_global_var_init[[SUFFIX2:[^)]*]] +// CHECK: call void @_ZW1mEN1AC1Ev +// CHECK: define {{.*}}@_GLOBAL__sub_I_m.pcm +// CHECK: call void @__cxx_global_var_init[[SUFFIX]] +// CHECK: call void @__cxx_global_var_init[[SUFFIX2]]