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 <[email protected]>
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

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to