https://github.com/pfeodrippe updated 
https://github.com/llvm/llvm-project/pull/169251

>From 737549f50465d5c4453cafec3dc74d0353712045 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/CGExprConstant.cpp          |  1 +
 clang/lib/CodeGen/CodeGenModule.cpp           | 37 +++++++++++++++++++
 clang/lib/CodeGen/CodeGenModule.h             | 13 +++++++
 .../test/Interpreter/static-const-member.cpp  | 36 ++++++++++++++++++
 .../unittests/Interpreter/InterpreterTest.cpp | 14 +++++++
 5 files changed, 101 insertions(+)
 create mode 100644 clang/test/Interpreter/static-const-member.cpp

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..1c01f5e280037 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -6302,6 +6302,43 @@ 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;
+
+  // Only materialize in-class initializers (constexpr or const integral types)
+  // Don't materialize out-of-class definitions
+  if (InitVD->isOutOfLine())
+    return InitVD;
+
+  StringRef MangledName = getMangledName(InitVD);
+  llvm::GlobalValue *GV = GetGlobalValue(MangledName);
+
+  // Check if we need to emit a definition
+  // Don't emit if a definition already exists (not just a declaration)
+  bool needsEmission = !GV || GV->isDeclaration();
+  if (!needsEmission) {
+    if (auto *GVVar = llvm::dyn_cast<llvm::GlobalVariable>(GV)) {
+      // Only re-emit if it's marked as available externally
+      needsEmission = GVVar->hasAvailableExternallyLinkage();
+    }
+  }
+
+  if (needsEmission) {
+    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..8cdd7b9f763dd 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -1839,6 +1839,19 @@ class CodeGenModule : public CodeGenTypeCache {
     return TrapReasonBuilder(&getDiags(), DiagID, TR);
   }
 
+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;
 
diff --git a/clang/test/Interpreter/static-const-member.cpp 
b/clang/test/Interpreter/static-const-member.cpp
new file mode 100644
index 0000000000000..f2dc24a1045db
--- /dev/null
+++ b/clang/test/Interpreter/static-const-member.cpp
@@ -0,0 +1,36 @@
+// RUN: cat %s | clang-repl | FileCheck %s
+
+extern "C" int printf(const char*, ...);
+
+struct Foo { static int const bar { 5 }; static int const baz { 10 }; };
+
+// Test 1: 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
+
+// Test 2: Materialize and use multiple static const members
+int const * q = &Foo::baz;
+printf("Second member test: %d\n", *q);
+// CHECK: Second member test: 10
+
+// Test 3: Verify the address is stable and consistent
+int const * p2 = &Foo::bar;
+printf("Address stability: %d\n", (*p == *p2) ? 1 : 0);
+// CHECK: Address stability: 1
+
+// Test 4: constexpr static members
+struct Qux { static constexpr int val = 99; };
+int const *p3 = &Qux::val;
+printf("Constexpr test: %d\n", *p3);
+// CHECK: Constexpr test: 99
+
+// Test 5: Non-const static member with out-of-class definition
+struct NonConst { static int value; };
+int NonConst::value = 42;
+int *p4 = &NonConst::value;
+printf("Non-const test: %d\n", *p4);
+// CHECK: Non-const test: 42
+
+%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