diff --git a/lib/CppInterOp/CppInterOp.cpp b/lib/CppInterOp/CppInterOp.cpp index 893f295ee..61abcb363 100644 --- a/lib/CppInterOp/CppInterOp.cpp +++ b/lib/CppInterOp/CppInterOp.cpp @@ -2779,9 +2779,21 @@ intptr_t GetVariableOffset(compat::Interpreter& I, Decl* D, address = I.getAddressOfGlobal(GD); if (!address) { if (!VD->hasInit()) { - compat::SynthesizingCodeRAII RAII(&getInterp()); - getSema().InstantiateVariableDefinition(SourceLocation(), VD); - VD = VD->getDefinition(); + // The initializer feeding the constexpr fast path below may live on + // an already-parsed out-of-line definition (a non-template class's + // static data member, e.g. std::partial_ordering::less): prefer it, + // with no Sema work at all. Only a variable instantiated from a + // template can need Sema::InstantiateVariableDefinition — and + // without an instantiation pattern that call dereferences a null + // VarDecl in release builds. + if (VarDecl* Def = VD->getDefinition()) { + VD = Def; + } else if (VD->getTemplateInstantiationPattern()) { + compat::SynthesizingCodeRAII RAII(&getInterp()); + getSema().InstantiateVariableDefinition(SourceLocation(), VD); + if (VarDecl* Inst = VD->getDefinition()) + VD = Inst; + } } if (VD->hasInit() && (VD->isConstexpr() || VD->getType().isConstQualified())) { diff --git a/unittests/CppInterOp/VariableReflectionTest.cpp b/unittests/CppInterOp/VariableReflectionTest.cpp index 7648cae52..ac51292bd 100644 --- a/unittests/CppInterOp/VariableReflectionTest.cpp +++ b/unittests/CppInterOp/VariableReflectionTest.cpp @@ -340,6 +340,42 @@ TYPED_TEST(CPPINTEROP_TEST_MODE, VariableReflection_GetVariableOffset) { EXPECT_TRUE(Cpp::GetVariableOffset(var)); } +TYPED_TEST(CPPINTEROP_TEST_MODE, + VariableReflection_GetVariableOffset_NonTemplateStaticNoInit) { + // A non-template class's static data member declared in-class and defined + // out-of-line reaches the no-initializer branch with no template + // instantiation pattern. Sema::InstantiateVariableDefinition requires a + // pattern and dereferences null without one (std::partial_ordering::less + // is the in-the-wild shape). Must not crash; must resolve the definition. + TestFixture::CreateInterpreter(); + Cpp::Declare(R"( + struct NonTemplateStatic { + static const NonTemplateStatic less; + int value; + }; + inline constexpr NonTemplateStatic NonTemplateStatic::less{-1}; + )"); + Cpp::DeclRef klass = Cpp::GetNamed("NonTemplateStatic"); + EXPECT_TRUE(klass); + Cpp::DeclRef var = Cpp::GetNamed("less", klass); + EXPECT_TRUE(var); + EXPECT_TRUE(Cpp::GetVariableOffset(var)); + + // Declared and never defined: still no pattern and now no definition + // either — the query must fail cleanly, not crash. + Cpp::Declare(R"( + struct NeverDefined { + static const NeverDefined missing; + int value; + }; + )"); + Cpp::DeclRef klass2 = Cpp::GetNamed("NeverDefined"); + EXPECT_TRUE(klass2); + Cpp::DeclRef var2 = Cpp::GetNamed("missing", klass2); + EXPECT_TRUE(var2); + EXPECT_FALSE(Cpp::GetVariableOffset(var2)); +} + #define CODE \ class BaseA { \ public: \