https://github.com/pfeodrippe updated https://github.com/llvm/llvm-project/pull/169251
>From 4cabc02917447df5064a2c6ef9ae2cf49043338a 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 | 35 ++++++++++++++++++ clang/lib/CodeGen/CodeGenModule.h | 13 +++++++ .../test/Interpreter/static-const-member.cpp | 36 +++++++++++++++++++ .../unittests/Interpreter/InterpreterTest.cpp | 14 ++++++++ 6 files changed, 106 insertions(+), 1 deletion(-) 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..5a1b80a383b04 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -6302,6 +6302,41 @@ 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; + + // 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. + if (!getLangOpts().IncrementalExtensions) + return VD; + + // If the variable is not defined or has available_externally linkage, + // we need to emit the definition and ensure it has linkonce_odr linkage. + auto *GV = dyn_cast<llvm::GlobalVariable>( + GetAddrOfGlobalVar(InitVD)->stripPointerCasts()); + if (!GV || GV->isDeclaration() || GV->hasAvailableExternallyLinkage()) { + EmitGlobalVarDefinition(InitVD, /*IsTentative=*/false); + + GV = dyn_cast<llvm::GlobalVariable>( + GetAddrOfGlobalVar(InitVD)->stripPointerCasts()); + if (GV && GV->hasAvailableExternallyLinkage()) + GV->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
