From 7a1ca4814b4b2220b652b89cf701d6f820b9a7ef Mon Sep 17 00:00:00 2001 From: Vipul Cariappa Date: Wed, 11 Jun 2025 15:04:46 +0200 Subject: [PATCH] fix: codegen need not instantiate func body if it can be resolved This is the case where the the instantiated function is present in one of the libraries loaded or the host application. --- lib/CppInterOp/CppInterOp.cpp | 36 ++++++++++------- .../CppInterOp/FunctionReflectionTest.cpp | 40 +++++++++++++++++++ 2 files changed, 62 insertions(+), 14 deletions(-) diff --git a/lib/CppInterOp/CppInterOp.cpp b/lib/CppInterOp/CppInterOp.cpp index e619750e3..b80f66fe6 100755 --- a/lib/CppInterOp/CppInterOp.cpp +++ b/lib/CppInterOp/CppInterOp.cpp @@ -1260,10 +1260,8 @@ TCppFuncAddr_t GetFunctionAddress(const char* mangled_name) { return nullptr; } -TCppFuncAddr_t GetFunctionAddress(TCppFunction_t method) { - auto* D = (Decl*)method; - - const auto get_mangled_name = [](FunctionDecl* FD) { +static TCppFuncAddr_t GetFunctionAddress(const FunctionDecl* FD) { + const auto get_mangled_name = [](const FunctionDecl* FD) { auto MangleCtxt = getASTContext().createMangleContext(); if (!MangleCtxt->shouldMangleDeclName(FD)) { @@ -1281,12 +1279,20 @@ TCppFuncAddr_t GetFunctionAddress(TCppFunction_t method) { return mangled_name; }; - if (auto* FD = llvm::dyn_cast_or_null(D)) + // Constructor and Destructors needs to be handled differently + if (!llvm::isa(FD) && !llvm::isa(FD)) return GetFunctionAddress(get_mangled_name(FD).c_str()); return 0; } +TCppFuncAddr_t GetFunctionAddress(TCppFunction_t method) { + auto* D = static_cast(method); + if (auto* FD = llvm::dyn_cast_or_null(D)) + return GetFunctionAddress(FD); + return nullptr; +} + bool IsVirtualMethod(TCppFunction_t method) { auto* D = (Decl*)method; if (auto* CXXMD = llvm::dyn_cast_or_null(D)) { @@ -2386,15 +2392,17 @@ int get_wrapper_code(compat::Interpreter& I, const FunctionDecl* FD, // header file. break; } - if (!Pattern->hasBody()) { - llvm::errs() << "TClingCallFunc::make_wrapper" - << ":" - << "Cannot make wrapper for a function template" - "instantiation with no body!"; - return 0; - } - if (FD->isImplicitlyInstantiable()) { - needInstantiation = true; + if (!GetFunctionAddress(FD)) { + if (!Pattern->hasBody()) { + llvm::errs() << "TClingCallFunc::make_wrapper" + << ":" + << "Cannot make wrapper for a function template " + << "instantiation with no body!"; + return 0; + } + if (FD->isImplicitlyInstantiable()) { + needInstantiation = true; + } } } break; case FunctionDecl::TK_DependentFunctionTemplateSpecialization: { diff --git a/unittests/CppInterOp/FunctionReflectionTest.cpp b/unittests/CppInterOp/FunctionReflectionTest.cpp index a4baee5a5..38d6e5462 100644 --- a/unittests/CppInterOp/FunctionReflectionTest.cpp +++ b/unittests/CppInterOp/FunctionReflectionTest.cpp @@ -1421,6 +1421,8 @@ TEST(FunctionReflectionTest, GetFunctionAddress) { std::stringstream address; address << Cpp::GetFunctionAddress(Decls[0]); EXPECT_EQ(address.str(), output); + + EXPECT_FALSE(Cpp::GetFunctionAddress(Cpp::GetGlobalScope())); } TEST(FunctionReflectionTest, IsVirtualMethod) { @@ -1489,6 +1491,15 @@ TEST(FunctionReflectionTest, JitCallAdvanced) { clang_Interpreter_dispose(I); } +template T instantiation_in_host() { return T(0); } +#if defined(_WIN32) +template __declspec(dllexport) int instantiation_in_host(); +#elif defined(__GNUC__) +template __attribute__((__visibility__("default"))) int +instantiation_in_host(); +#else +template int instantiation_in_host(); +#endif TEST(FunctionReflectionTest, GetFunctionCallWrapper) { #ifdef EMSCRIPTEN @@ -1824,6 +1835,35 @@ TEST(FunctionReflectionTest, GetFunctionCallWrapper) { auto fn_callable = Cpp::MakeFunctionCallable(fn); EXPECT_EQ(fn_callable.getKind(), Cpp::JitCall::kGenericCall); + + // instantiation in host, no template function body + Interp->process("template T instantiation_in_host();"); + + unresolved_candidate_methods.clear(); + Cpp::GetClassTemplatedMethods("instantiation_in_host", Cpp::GetGlobalScope(), + unresolved_candidate_methods); + EXPECT_EQ(unresolved_candidate_methods.size(), 1); + + Cpp::TCppScope_t instantiation_in_host = Cpp::BestOverloadFunctionMatch( + unresolved_candidate_methods, {Cpp::GetType("int")}, {}); + EXPECT_TRUE(instantiation_in_host); + + Cpp::JitCall instantiation_in_host_callable = + Cpp::MakeFunctionCallable(instantiation_in_host); + EXPECT_EQ(instantiation_in_host_callable.getKind(), + Cpp::JitCall::kGenericCall); + + instantiation_in_host = Cpp::BestOverloadFunctionMatch( + unresolved_candidate_methods, {Cpp::GetType("double")}, {}); + EXPECT_TRUE(instantiation_in_host); + + Cpp::BeginStdStreamCapture(Cpp::CaptureStreamKind::kStdErr); + instantiation_in_host_callable = + Cpp::MakeFunctionCallable(instantiation_in_host); + std::string err_msg = Cpp::EndStdStreamCapture(); + EXPECT_TRUE(err_msg.find("instantiation with no body") != std::string::npos); + EXPECT_EQ(instantiation_in_host_callable.getKind(), + Cpp::JitCall::kUnknown); // expect to fail } TEST(FunctionReflectionTest, IsConstMethod) {