[clang] Fix static const member address reference (PR #169251)
Paulo Rafael Feodrippe via cfe-commits
cfe-commits at lists.llvm.org
Sun Nov 23 14:36:42 PST 2025
https://github.com/pfeodrippe created https://github.com/llvm/llvm-project/pull/169251
Fixes https://github.com/llvm/llvm-project/issues/146956
>From 8e82977524958a53046bd8c7cd56fb8283fd402f Mon Sep 17 00:00:00 2001
From: Paulo Feodrippe <pfeodrippe at gmail.com>
Date: Sun, 23 Nov 2025 17:31:37 -0500
Subject: [PATCH] Fix static const member address reference
---
clang/lib/CodeGen/CGExpr.cpp | 8 ++++-
clang/lib/CodeGen/CGExprConstant.cpp | 1 +
clang/lib/CodeGen/CodeGenModule.cpp | 29 +++++++++++++++++++
clang/lib/CodeGen/CodeGenModule.h | 16 +++++++++-
.../test/Interpreter/static-const-member.cpp | 13 +++++++++
.../unittests/Interpreter/InterpreterTest.cpp | 14 +++++++++
6 files changed, 79 insertions(+), 2 deletions(-)
create mode 100644 clang/test/Interpreter/static-const-member.cpp
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index b33772919b8c8..0faa312185a1a 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -3111,7 +3111,13 @@ static LValue EmitGlobalVarDeclLValue(CodeGenFunction &CGF,
return CGF.MakeAddrLValue(Addr, T, AlignmentSource::Decl);
}
- llvm::Value *V = CGF.CGM.GetAddrOfGlobalVar(VD);
+ // For static data members with in-class initializers, ensure we emit a
+ // definition if one doesn't exist yet. This is necessary for interpreters
+ // where the member's address might be taken after the class definition,
+ // requiring the symbol to be materialized on demand.
+ const VarDecl *DefinitionVD = CGF.CGM.materializeStaticDataMember(VD);
+
+ llvm::Value *V = CGF.CGM.GetAddrOfGlobalVar(DefinitionVD);
if (VD->getTLSKind() != VarDecl::TLS_None)
V = CGF.Builder.CreateThreadLocalAddress(V);
diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp
index 6407afc3d9447..bb297c96566a1 100644
--- a/clang/lib/CodeGen/CGExprConstant.cpp
+++ b/clang/lib/CodeGen/CGExprConstant.cpp
@@ -2243,6 +2243,7 @@ ConstantLValueEmitter::tryEmitBase(const APValue::LValueBase &base) {
}
if (const auto *VD = dyn_cast<VarDecl>(D)) {
+ VD = CGM.materializeStaticDataMember(VD);
// We can never refer to a variable with local storage.
if (!VD->hasLocalStorage()) {
if (VD->isFileVarDecl() || VD->hasExternalStorage())
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 645b78a599f89..1d60287bcd031 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -6302,6 +6302,35 @@ CodeGenModule::getLLVMLinkageVarDefinition(const VarDecl *VD) {
return getLLVMLinkageForDeclarator(VD, Linkage);
}
+const VarDecl *CodeGenModule::materializeStaticDataMember(const VarDecl *VD) {
+ if (!VD->isStaticDataMember())
+ return VD;
+
+ const VarDecl *InitVD = nullptr;
+ if (!VD->getAnyInitializer(InitVD) || !InitVD)
+ return VD;
+
+ StringRef MangledName = getMangledName(InitVD);
+ auto needsEmission = [](llvm::GlobalValue *GV) {
+ if (!GV || GV->isDeclaration())
+ return true;
+ if (auto *GVVar = llvm::dyn_cast<llvm::GlobalVariable>(GV))
+ return GVVar->hasAvailableExternallyLinkage();
+ return false;
+ };
+
+ llvm::GlobalValue *GV = GetGlobalValue(MangledName);
+ if (needsEmission(GV)) {
+ EmitGlobalVarDefinition(InitVD, /*IsTentative=*/false);
+ GV = GetGlobalValue(MangledName);
+ if (auto *GVVar = llvm::dyn_cast_or_null<llvm::GlobalVariable>(GV))
+ if (GVVar->hasAvailableExternallyLinkage())
+ GVVar->setLinkage(llvm::GlobalValue::LinkOnceODRLinkage);
+ }
+
+ return InitVD;
+}
+
/// Replace the uses of a function that was declared with a non-proto type.
/// We want to silently drop extra arguments from call sites
static void replaceUsesOfNonProtoConstant(llvm::Constant *old,
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index a253bcda2d06c..7190691b8cba0 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -1839,6 +1839,21 @@ class CodeGenModule : public CodeGenTypeCache {
return TrapReasonBuilder(&getDiags(), DiagID, TR);
}
+ void EmitGlobalVarDefinition(const VarDecl *D, bool IsTentative = false);
+
+public:
+ /// Ensure a static data member with an in-class initializer is materialized.
+ ///
+ /// For static data members with in-class initializers, this ensures a
+ /// definition is emitted if one doesn't exist yet. This is necessary for
+ /// interpreters where the member's address might be taken after the class
+ /// definition, requiring the symbol to be materialized on demand.
+ ///
+ /// \param VD The variable declaration to materialize.
+ /// \returns The declaration that owns the emitted definition, or the
+ /// original declaration if no materialization is needed.
+ const VarDecl *materializeStaticDataMember(const VarDecl *VD);
+
private:
bool shouldDropDLLAttribute(const Decl *D, const llvm::GlobalValue *GV) const;
@@ -1889,7 +1904,6 @@ class CodeGenModule : public CodeGenTypeCache {
void EmitGlobalFunctionDefinition(GlobalDecl GD, llvm::GlobalValue *GV);
void EmitMultiVersionFunctionDefinition(GlobalDecl GD, llvm::GlobalValue *GV);
- void EmitGlobalVarDefinition(const VarDecl *D, bool IsTentative = false);
void EmitAliasDefinition(GlobalDecl GD);
void emitIFuncDefinition(GlobalDecl GD);
void emitCPUDispatchDefinition(GlobalDecl GD);
diff --git a/clang/test/Interpreter/static-const-member.cpp b/clang/test/Interpreter/static-const-member.cpp
new file mode 100644
index 0000000000000..756ef4363bcb4
--- /dev/null
+++ b/clang/test/Interpreter/static-const-member.cpp
@@ -0,0 +1,13 @@
+// RUN: cat %s | clang-repl | FileCheck %s
+
+extern "C" int printf(const char*, ...);
+
+struct Foo { static int const bar { 5 }; };
+
+// Taking the address of a static const member with in-class initializer
+// should materialize the symbol and allow dereferencing
+int const * p = &Foo::bar;
+printf("Address test: %d\n", *p);
+// CHECK: Address test: 5
+
+%quit
diff --git a/clang/unittests/Interpreter/InterpreterTest.cpp b/clang/unittests/Interpreter/InterpreterTest.cpp
index 9ff9092524d21..176f8d427d7d0 100644
--- a/clang/unittests/Interpreter/InterpreterTest.cpp
+++ b/clang/unittests/Interpreter/InterpreterTest.cpp
@@ -443,4 +443,18 @@ TEST_F(InterpreterTest, TranslationUnit_CanonicalDecl) {
sema.getASTContext().getTranslationUnitDecl()->getCanonicalDecl());
}
+TEST_F(InterpreterTest, StaticConstMemberAddress) {
+ std::unique_ptr<Interpreter> Interp = createInterpreter();
+
+ // Test taking the address of a static const member with in-class initializer
+ llvm::cantFail(
+ Interp->ParseAndExecute("struct Foo { static int const bar { 5 }; };"));
+
+ Value V;
+ llvm::cantFail(Interp->ParseAndExecute("int const * p = &Foo::bar; *p", &V));
+
+ // The value should be 5
+ EXPECT_EQ(V.getInt(), 5);
+}
+
} // end anonymous namespace
More information about the cfe-commits
mailing list