From c565a2a59efbc9dfca57362816170773209a03ad Mon Sep 17 00:00:00 2001 From: maximusron Date: Wed, 3 Apr 2024 18:20:13 +0200 Subject: [PATCH 1/8] Add interfaces enabling template function logic --- include/clang/Interpreter/CppInterOp.h | 21 +++ lib/Interpreter/CppInterOp.cpp | 158 +++++++++++++++++- .../CppInterOp/FunctionReflectionTest.cpp | 57 +++++++ 3 files changed, 234 insertions(+), 2 deletions(-) diff --git a/include/clang/Interpreter/CppInterOp.h b/include/clang/Interpreter/CppInterOp.h index 522c37b3a..2ab80fb2e 100644 --- a/include/clang/Interpreter/CppInterOp.h +++ b/include/clang/Interpreter/CppInterOp.h @@ -288,6 +288,10 @@ namespace Cpp { /// supplied as a parameter. CPPINTEROP_API std::vector GetClassMethods(TCppScope_t klass); + ///\returns Template function pointer list to add proxies for + /// un-instantiated/non-overloaded templated methods + std::vector GetTemplatedFuncs(TCppScope_t klass); + ///\returns if a class has a default constructor. CPPINTEROP_API bool HasDefaultConstructor(TCppScope_t scope); @@ -331,6 +335,16 @@ namespace Cpp { CPPINTEROP_API bool ExistsFunctionTemplate(const std::string& name, TCppScope_t parent = 0); + CPPINTEROP_API std::vector + GetTemplatedMethods(const std::string& name, TCppScope_t parent = 0, + const std::string& filter = ""); + + CPPINTEROP_API TCppIndex_t GetNumTemplatedMethods(TCppScope_t scope, + bool accept_namespace); + + CPPINTEROP_API std::string GetTemplatedMethodName(TCppScope_t scope, + TCppIndex_t imeth); + /// Checks if the provided parameter is a method. CPPINTEROP_API bool IsMethod(TCppConstFunction_t method); @@ -550,6 +564,13 @@ namespace Cpp { CPPINTEROP_API TCppFunction_t InstantiateTemplateFunctionFromString(const char* function_template); + // Find best template match based on explicit template parameters and arg + // types + CPPINTEROP_API TCppFunction_t + BestTemplateFunctionMatch(std::vector candidates, + std::vector explicit_types, + std::vector arg_types); + CPPINTEROP_API std::vector GetAllCppNames(TCppScope_t scope); CPPINTEROP_API void DumpScope(TCppScope_t scope); diff --git a/lib/Interpreter/CppInterOp.cpp b/lib/Interpreter/CppInterOp.cpp index d68be2447..d4bdf17ac 100644 --- a/lib/Interpreter/CppInterOp.cpp +++ b/lib/Interpreter/CppInterOp.cpp @@ -746,6 +746,32 @@ namespace Cpp { return methods; } + // FIXME: We should make the std::vector an out parameter to + // avoid copies. + std::vector GetTemplatedFuncs(TCppScope_t klass) { + + if (!klass) + return {}; + + auto* D = (clang::Decl*)klass; + + if (auto* TD = dyn_cast(D)) + D = GetScopeFromType(TD->getUnderlyingType()); + + std::vector methods; + if (auto* CXXRD = dyn_cast_or_null(D)) { + getSema().ForceDeclarationOfImplicitMembers(CXXRD); + for (Decl* DI : CXXRD->decls()) { + if (auto* MD = dyn_cast(DI)) + methods.push_back(MD); + else if (auto* USD = dyn_cast(DI)) + if (auto* MD = dyn_cast(USD->getTargetDecl())) + methods.push_back(MD); + } + } + return methods; + } + bool HasDefaultConstructor(TCppScope_t scope) { auto *D = (clang::Decl *) scope; @@ -868,8 +894,9 @@ namespace Cpp { return 0; } - std::string GetFunctionSignature(TCppFunction_t func) - { + std::string GetFunctionSignature( + TCppFunction_t func) { // FIXME : Doesn't work for template functions as + // it does in cling if (!func) return ""; @@ -886,6 +913,19 @@ namespace Cpp { SS.flush(); return Signature; } + + // FIXME: else if (auto *FD = llvm::dyn_cast(D)) { + // std::string Signature; + // raw_string_ostream SS(Signature); + // PrintingPolicy Policy = getASTContext().getPrintingPolicy(); + // // Skip printing the body + // Policy.TerseOutput = true; + // Policy.FullyQualifiedName = true; + // Policy.SuppressDefaultTemplateArgs = false; + // FD->print(SS, Policy); + // SS.flush(); + // return Signature; + // } return ""; } @@ -941,6 +981,120 @@ namespace Cpp { return true; } + std::vector GetTemplatedMethods(const std::string& name, + TCppScope_t parent, + const std::string& filter) { + + auto* D = (Decl*)parent; + + if (!parent || name.empty()) + return {}; + + D = GetUnderlyingScope(D); + + std::vector funcs; + llvm::StringRef Name(name); + auto& S = getSema(); + DeclarationName DName = &getASTContext().Idents.get(name); + clang::LookupResult R(S, DName, SourceLocation(), Sema::LookupOrdinaryName, + Sema::ForVisibleRedeclaration); + + Cpp_utils::Lookup::Named(&S, R, Decl::castToDeclContext(D)); + + if (R.empty()) + return {}; + + R.resolveKind(); + TCppFunction_t best_match; + + for (auto* Found : R) { + if (llvm::isa(Found)) + funcs.push_back(Found); + } + + if (!funcs.empty()) + return funcs; + + return {}; + } + + TCppIndex_t GetNumTemplatedMethods(TCppScope_t scope, bool accept_namespace) { + if (!accept_namespace && IsNamespace(scope)) + return (TCppIndex_t)0; // Enforce lazy + + std::vector mc = GetTemplatedFuncs(scope); + TCppIndex_t res = 0; + + for (auto method : mc) { + res += IsTemplatedFunction(method) ? 1 : 0; + } + return res; + } + + std::string GetTemplatedMethodName(TCppScope_t scope, TCppIndex_t imeth) { + std::vector mc = GetTemplatedFuncs(scope); + auto* D = (FunctionTemplateDecl*)mc[imeth]; + + return D->getNameAsString(); + } + + TCppFunction_t + BestTemplateFunctionMatch(std::vector candidates, + std::vector explicit_types, + std::vector arg_types) { + + int k = 1; + for (const auto& candidate : candidates) { + auto* TFD = (FunctionTemplateDecl*)candidate; + clang::TemplateParameterList* tpl = TFD->getTemplateParameters(); + + if (tpl->size() < explicit_types.size()) { + // template parameter size does not match + ++k; + continue; + } + + else { + // right now uninstantiated functions give template typenames instead of + // actual types We make this match solely based on count + + const FunctionDecl* func = TFD->getTemplatedDecl(); + const auto& params = func->parameters(); + + // Check if number of function parameters match + if (func->getNumParams() != arg_types.size()) { + ++k; + continue; + } + + else { + // TODO : first score based on the type similarity before forcing + // instantiation try instantiating + TCppFunction_t instantiated = InstantiateTemplate( + candidate, arg_types.data(), arg_types.size()); + if (instantiated) + return instantiated; + + // Force the instantiation with template params in case of no args + // maybe steer instantiation better with arg set returned from + // TemplateProxy? + else { + instantiated = InstantiateTemplate(candidate, explicit_types.data(), + explicit_types.size()); + if (instantiated) + return instantiated; + + else { + k++; + continue; + } + } + } + } + } + return nullptr; + } + // Gets the AccessSpecifier of the function and checks if it is equal to // the provided AccessSpecifier. bool CheckMethodAccess(TCppFunction_t method, AccessSpecifier AS) diff --git a/unittests/CppInterOp/FunctionReflectionTest.cpp b/unittests/CppInterOp/FunctionReflectionTest.cpp index 3d629e987..043555537 100644 --- a/unittests/CppInterOp/FunctionReflectionTest.cpp +++ b/unittests/CppInterOp/FunctionReflectionTest.cpp @@ -480,6 +480,63 @@ TEST(FunctionReflectionTest, ExistsFunctionTemplate) { EXPECT_FALSE(Cpp::ExistsFunctionTemplate("f", Decls[2])); } +// TEST(ScopeReflectionTest, InstantiateTemplateFunctionFromString) { +// Cpp::CreateInterpreter(); +// std::string code = R"(#include )"; +// Interp->process(code); +// const char* str = "std::make_unique"; +// auto* Instance1 = (Decl*)Cpp::InstantiateTemplateFunctionFromString(str); +// EXPECT_TRUE(Instance1); +// } + +TEST(FunctionReflectionTest, InstantiateFunctionTemplate) { + std::vector Decls; + std::string code = R"( +template T TrivialFnTemplate() { return T(); } +)"; + + GetAllTopLevelDecls(code, Decls); + ASTContext& C = Interp->getCI()->getASTContext(); + + std::vector args1 = {C.IntTy.getAsOpaquePtr()}; + auto Instance1 = Cpp::InstantiateTemplate(Decls[0], args1.data(), + /*type_size*/ args1.size()); + EXPECT_TRUE(isa((Decl*)Instance1)); + FunctionDecl* FD = cast((Decl*)Instance1); + FunctionDecl* FnTD1 = FD->getTemplateInstantiationPattern(); + EXPECT_TRUE(FnTD1->isThisDeclarationADefinition()); + TemplateArgument TA1 = FD->getTemplateSpecializationArgs()->get(0); + EXPECT_TRUE(TA1.getAsType()->isIntegerType()); +} + +TEST(FunctionReflectionTest, InstantiateTemplateMethod) { + std::vector Decls; + std::string code = R"( + class MyTemplatedMethodClass { + public: + template long get_size(A&); + }; + + template + long MyTemplatedMethodClass::get_size(A&) { + return sizeof(A); + } + )"; + + GetAllTopLevelDecls(code, Decls); + ASTContext& C = Interp->getCI()->getASTContext(); + + std::vector args1 = {C.IntTy.getAsOpaquePtr()}; + auto Instance1 = Cpp::InstantiateTemplate(Decls[1], args1.data(), + /*type_size*/ args1.size()); + EXPECT_TRUE(isa((Decl*)Instance1)); + FunctionDecl* FD = cast((Decl*)Instance1); + FunctionDecl* FnTD1 = FD->getTemplateInstantiationPattern(); + EXPECT_TRUE(FnTD1->isThisDeclarationADefinition()); + TemplateArgument TA1 = FD->getTemplateSpecializationArgs()->get(0); + EXPECT_TRUE(TA1.getAsType()->isIntegerType()); +} + TEST(FunctionReflectionTest, IsPublicMethod) { std::vector Decls, SubDecls; std::string code = R"( From 84fd0a68f532d343109cec44e615829575f0c79f Mon Sep 17 00:00:00 2001 From: maximusron Date: Wed, 3 Apr 2024 21:50:51 +0200 Subject: [PATCH 2/8] Update interfaces to pass return by ref(out param) --- include/clang/Interpreter/CppInterOp.h | 18 +++--- lib/Interpreter/CppInterOp.cpp | 82 +++++++++++--------------- 2 files changed, 44 insertions(+), 56 deletions(-) diff --git a/include/clang/Interpreter/CppInterOp.h b/include/clang/Interpreter/CppInterOp.h index 2ab80fb2e..e18225bec 100644 --- a/include/clang/Interpreter/CppInterOp.h +++ b/include/clang/Interpreter/CppInterOp.h @@ -246,7 +246,7 @@ namespace Cpp { /// passed as a parameter, and if the parent is not passed, /// then global scope will be assumed. CPPINTEROP_API TCppScope_t GetScope(const std::string& name, - TCppScope_t parent = 0); + TCppScope_t parent = nullptr); /// When the namespace is known, then the parent doesn't need /// to be specified. This will probably be phased-out in @@ -286,11 +286,11 @@ namespace Cpp { /// Gets a list of all the Methods that are in the Class that is /// supplied as a parameter. - CPPINTEROP_API std::vector GetClassMethods(TCppScope_t klass); + CPPINTEROP_API void GetClassMethods(TCppScope_t klass, std::vector &methods); ///\returns Template function pointer list to add proxies for /// un-instantiated/non-overloaded templated methods - std::vector GetTemplatedFuncs(TCppScope_t klass); + CPPINTEROP_API void GetFunctionTemplatedDecls(TCppScope_t klass, std::vector &methods); ///\returns if a class has a default constructor. CPPINTEROP_API bool HasDefaultConstructor(TCppScope_t scope); @@ -333,10 +333,10 @@ namespace Cpp { /// This function performs a lookup to check if there is a /// templated function of that type. CPPINTEROP_API bool ExistsFunctionTemplate(const std::string& name, - TCppScope_t parent = 0); + TCppScope_t parent = nullptr); CPPINTEROP_API std::vector - GetTemplatedMethods(const std::string& name, TCppScope_t parent = 0, + GetTemplatedMethods(const std::string& name, TCppScope_t parent = nullptr, const std::string& filter = ""); CPPINTEROP_API TCppIndex_t GetNumTemplatedMethods(TCppScope_t scope, @@ -550,7 +550,7 @@ namespace Cpp { }; /// Builds a template instantiation for a given templated declaration. CPPINTEROP_API TCppScope_t InstantiateTemplate(TCppScope_t tmpl, - TemplateArgInfo* template_args, + const TemplateArgInfo* template_args, size_t template_args_size); /// Returns the class template instantiation arguments of \c templ_instance. @@ -567,9 +567,9 @@ namespace Cpp { // Find best template match based on explicit template parameters and arg // types CPPINTEROP_API TCppFunction_t - BestTemplateFunctionMatch(std::vector candidates, - std::vector explicit_types, - std::vector arg_types); + BestTemplateFunctionMatch(const std::vector& candidates, + const std::vector& explicit_types, + const std::vector& arg_types); CPPINTEROP_API std::vector GetAllCppNames(TCppScope_t scope); diff --git a/lib/Interpreter/CppInterOp.cpp b/lib/Interpreter/CppInterOp.cpp index d4bdf17ac..16df0a1eb 100644 --- a/lib/Interpreter/CppInterOp.cpp +++ b/lib/Interpreter/CppInterOp.cpp @@ -719,57 +719,45 @@ namespace Cpp { return ComputeBaseOffset(getSema().getASTContext(), DCXXRD, Paths.front()); } - // FIXME: We should make the std::vector an out parameter to - // avoid copies. - std::vector GetClassMethods(TCppScope_t klass) + void GetClassMethods(TCppScope_t klass, std::vector &methods) { + if (klass) { + auto *D = (clang::Decl *) klass; - if (!klass) - return {}; - - auto *D = (clang::Decl *) klass; + if (auto *TD = dyn_cast(D)) + D = GetScopeFromType(TD->getUnderlyingType()); - if (auto *TD = dyn_cast(D)) - D = GetScopeFromType(TD->getUnderlyingType()); - - std::vector methods; - if (auto *CXXRD = dyn_cast_or_null(D)) { - getSema().ForceDeclarationOfImplicitMembers(CXXRD); - for (Decl* DI : CXXRD->decls()) { - if (auto* MD = dyn_cast(DI)) - methods.push_back(MD); - else if (auto* USD = dyn_cast(DI)) - if (auto* MD = dyn_cast(USD->getTargetDecl())) + if (auto *CXXRD = dyn_cast_or_null(D)) { + getSema().ForceDeclarationOfImplicitMembers(CXXRD); + for (Decl* DI : CXXRD->decls()) { + if (auto* MD = dyn_cast(DI)) methods.push_back(MD); + else if (auto* USD = dyn_cast(DI)) + if (auto* MD = dyn_cast(USD->getTargetDecl())) + methods.push_back(MD); + } } } - return methods; } - // FIXME: We should make the std::vector an out parameter to - // avoid copies. - std::vector GetTemplatedFuncs(TCppScope_t klass) { + void GetFunctionTemplatedDecls(TCppScope_t klass, std::vector &methods) { + if (klass) { + auto* D = (clang::Decl*)klass; - if (!klass) - return {}; - - auto* D = (clang::Decl*)klass; + if (auto* TD = dyn_cast(D)) + D = GetScopeFromType(TD->getUnderlyingType()); - if (auto* TD = dyn_cast(D)) - D = GetScopeFromType(TD->getUnderlyingType()); - - std::vector methods; - if (auto* CXXRD = dyn_cast_or_null(D)) { - getSema().ForceDeclarationOfImplicitMembers(CXXRD); - for (Decl* DI : CXXRD->decls()) { - if (auto* MD = dyn_cast(DI)) - methods.push_back(MD); - else if (auto* USD = dyn_cast(DI)) - if (auto* MD = dyn_cast(USD->getTargetDecl())) + if (auto* CXXRD = dyn_cast_or_null(D)) { + getSema().ForceDeclarationOfImplicitMembers(CXXRD); + for (Decl* DI : CXXRD->decls()) { + if (auto* MD = dyn_cast(DI)) methods.push_back(MD); + else if (auto* USD = dyn_cast(DI)) + if (auto* MD = dyn_cast(USD->getTargetDecl())) + methods.push_back(MD); + } } } - return methods; } bool HasDefaultConstructor(TCppScope_t scope) { @@ -1005,7 +993,6 @@ namespace Cpp { return {}; R.resolveKind(); - TCppFunction_t best_match; for (auto* Found : R) { if (llvm::isa(Found)) @@ -1022,7 +1009,8 @@ namespace Cpp { if (!accept_namespace && IsNamespace(scope)) return (TCppIndex_t)0; // Enforce lazy - std::vector mc = GetTemplatedFuncs(scope); + std::vector mc; + GetFunctionTemplatedDecls(scope, mc); TCppIndex_t res = 0; for (auto method : mc) { @@ -1032,16 +1020,17 @@ namespace Cpp { } std::string GetTemplatedMethodName(TCppScope_t scope, TCppIndex_t imeth) { - std::vector mc = GetTemplatedFuncs(scope); + std::vector mc; + GetFunctionTemplatedDecls(scope, mc); auto* D = (FunctionTemplateDecl*)mc[imeth]; return D->getNameAsString(); } TCppFunction_t - BestTemplateFunctionMatch(std::vector candidates, - std::vector explicit_types, - std::vector arg_types) { + BestTemplateFunctionMatch(const std::vector& candidates, + const std::vector& explicit_types, + const std::vector& arg_types) { int k = 1; for (const auto& candidate : candidates) { @@ -1056,10 +1045,9 @@ namespace Cpp { else { // right now uninstantiated functions give template typenames instead of - // actual types We make this match solely based on count + // actual types. We make this match solely based on count const FunctionDecl* func = TFD->getTemplatedDecl(); - const auto& params = func->parameters(); // Check if number of function parameters match if (func->getNumParams() != arg_types.size()) { @@ -2990,7 +2978,7 @@ namespace Cpp { } TCppScope_t InstantiateTemplate(TCppScope_t tmpl, - TemplateArgInfo* template_args, + const TemplateArgInfo* template_args, size_t template_args_size) { ASTContext &C = getASTContext(); From 5e8f4d53779dd752fcb84c8d6351f17ca3d3d065 Mon Sep 17 00:00:00 2001 From: maximusron Date: Thu, 4 Apr 2024 11:51:53 +0200 Subject: [PATCH 3/8] Remove redundant interfaces --- include/clang/Interpreter/CppInterOp.h | 8 +------- lib/Interpreter/CppInterOp.cpp | 24 +----------------------- 2 files changed, 2 insertions(+), 30 deletions(-) diff --git a/include/clang/Interpreter/CppInterOp.h b/include/clang/Interpreter/CppInterOp.h index e18225bec..8165312e9 100644 --- a/include/clang/Interpreter/CppInterOp.h +++ b/include/clang/Interpreter/CppInterOp.h @@ -338,13 +338,7 @@ namespace Cpp { CPPINTEROP_API std::vector GetTemplatedMethods(const std::string& name, TCppScope_t parent = nullptr, const std::string& filter = ""); - - CPPINTEROP_API TCppIndex_t GetNumTemplatedMethods(TCppScope_t scope, - bool accept_namespace); - - CPPINTEROP_API std::string GetTemplatedMethodName(TCppScope_t scope, - TCppIndex_t imeth); - + /// Checks if the provided parameter is a method. CPPINTEROP_API bool IsMethod(TCppConstFunction_t method); diff --git a/lib/Interpreter/CppInterOp.cpp b/lib/Interpreter/CppInterOp.cpp index 16df0a1eb..eb9fedf70 100644 --- a/lib/Interpreter/CppInterOp.cpp +++ b/lib/Interpreter/CppInterOp.cpp @@ -1004,29 +1004,7 @@ namespace Cpp { return {}; } - - TCppIndex_t GetNumTemplatedMethods(TCppScope_t scope, bool accept_namespace) { - if (!accept_namespace && IsNamespace(scope)) - return (TCppIndex_t)0; // Enforce lazy - - std::vector mc; - GetFunctionTemplatedDecls(scope, mc); - TCppIndex_t res = 0; - - for (auto method : mc) { - res += IsTemplatedFunction(method) ? 1 : 0; - } - return res; - } - - std::string GetTemplatedMethodName(TCppScope_t scope, TCppIndex_t imeth) { - std::vector mc; - GetFunctionTemplatedDecls(scope, mc); - auto* D = (FunctionTemplateDecl*)mc[imeth]; - - return D->getNameAsString(); - } - + TCppFunction_t BestTemplateFunctionMatch(const std::vector& candidates, const std::vector& explicit_types, From 5ce073e0238a4905304c57bf6f51e756758c0818 Mon Sep 17 00:00:00 2001 From: maximusron Date: Thu, 4 Apr 2024 12:30:53 +0200 Subject: [PATCH 4/8] Check scope before casting to CXXRecordDecl --- lib/Interpreter/CppInterOp.cpp | 49 +++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/lib/Interpreter/CppInterOp.cpp b/lib/Interpreter/CppInterOp.cpp index eb9fedf70..4871d2c13 100644 --- a/lib/Interpreter/CppInterOp.cpp +++ b/lib/Interpreter/CppInterOp.cpp @@ -719,46 +719,51 @@ namespace Cpp { return ComputeBaseOffset(getSema().getASTContext(), DCXXRD, Paths.front()); } - void GetClassMethods(TCppScope_t klass, std::vector &methods) - { - if (klass) { - auto *D = (clang::Decl *) klass; + void GetClassMethods(TCppScope_t klass, std::vector &methods) { + if (!klass) return; + + auto *D = (clang::Decl *) klass; - if (auto *TD = dyn_cast(D)) - D = GetScopeFromType(TD->getUnderlyingType()); + if (auto *TD = dyn_cast(D)) + D = GetScopeFromType(TD->getUnderlyingType()); - if (auto *CXXRD = dyn_cast_or_null(D)) { - getSema().ForceDeclarationOfImplicitMembers(CXXRD); + if (D && isa(D)) { + auto* CXXRD = dyn_cast(D); + getSema().ForceDeclarationOfImplicitMembers(CXXRD); for (Decl* DI : CXXRD->decls()) { if (auto* MD = dyn_cast(DI)) methods.push_back(MD); else if (auto* USD = dyn_cast(DI)) if (auto* MD = dyn_cast(USD->getTargetDecl())) methods.push_back(MD); - } } } } + + void GetFunctionTemplatedDecls(TCppScope_t klass, std::vector &methods) { - if (klass) { - auto* D = (clang::Decl*)klass; + if (!klass) return; - if (auto* TD = dyn_cast(D)) - D = GetScopeFromType(TD->getUnderlyingType()); + auto* D = (clang::Decl*)klass; - if (auto* CXXRD = dyn_cast_or_null(D)) { - getSema().ForceDeclarationOfImplicitMembers(CXXRD); - for (Decl* DI : CXXRD->decls()) { - if (auto* MD = dyn_cast(DI)) + if (auto* TD = dyn_cast(D)) + D = GetScopeFromType(TD->getUnderlyingType()); + + if (D && isa(D)) { + auto* CXXRD = dyn_cast(D); + getSema().ForceDeclarationOfImplicitMembers(CXXRD); + for (Decl* DI : CXXRD->decls()) { + if (auto* MD = dyn_cast(DI)) + methods.push_back(MD); + else if (auto* USD = dyn_cast(DI)) + if (auto* MD = dyn_cast(USD->getTargetDecl())) methods.push_back(MD); - else if (auto* USD = dyn_cast(DI)) - if (auto* MD = dyn_cast(USD->getTargetDecl())) - methods.push_back(MD); - } } } } + + bool HasDefaultConstructor(TCppScope_t scope) { auto *D = (clang::Decl *) scope; @@ -1004,7 +1009,7 @@ namespace Cpp { return {}; } - + TCppFunction_t BestTemplateFunctionMatch(const std::vector& candidates, const std::vector& explicit_types, From 0f4e8ea48efc574e4e23b647270bd418437d5d15 Mon Sep 17 00:00:00 2001 From: maximusron Date: Wed, 3 Apr 2024 21:51:15 +0200 Subject: [PATCH 5/8] Update tests to use new GetClassMethods interface --- .../CppInterOp/FunctionReflectionTest.cpp | 21 ++++++++++++------- unittests/CppInterOp/ScopeReflectionTest.cpp | 3 ++- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/unittests/CppInterOp/FunctionReflectionTest.cpp b/unittests/CppInterOp/FunctionReflectionTest.cpp index 043555537..2bb87e775 100644 --- a/unittests/CppInterOp/FunctionReflectionTest.cpp +++ b/unittests/CppInterOp/FunctionReflectionTest.cpp @@ -47,7 +47,8 @@ TEST(FunctionReflectionTest, GetClassMethods) { return Cpp::GetFunctionSignature(method); }; - auto methods0 = Cpp::GetClassMethods(Decls[0]); + std::vector methods0; + Cpp::GetClassMethods(Decls[0], methods0); EXPECT_EQ(methods0.size(), 11); EXPECT_EQ(get_method_name(methods0[0]), "int A::f1(int a, int b)"); @@ -62,7 +63,8 @@ TEST(FunctionReflectionTest, GetClassMethods) { EXPECT_EQ(get_method_name(methods0[9]), "inline constexpr A &A::operator=(A &&)"); EXPECT_EQ(get_method_name(methods0[10]), "inline A::~A()"); - auto methods1 = Cpp::GetClassMethods(Decls[1]); + std::vector methods1; + Cpp::GetClassMethods(Decls[1], methods1); EXPECT_EQ(methods0.size(), methods1.size()); EXPECT_EQ(methods0[0], methods1[0]); EXPECT_EQ(methods0[1], methods1[1]); @@ -70,7 +72,8 @@ TEST(FunctionReflectionTest, GetClassMethods) { EXPECT_EQ(methods0[3], methods1[3]); EXPECT_EQ(methods0[4], methods1[4]); - auto methods2 = Cpp::GetClassMethods(Decls[2]); + std::vector methods2; + Cpp::GetClassMethods(Decls[2], methods2); EXPECT_EQ(methods2.size(), 6); EXPECT_EQ(get_method_name(methods2[0]), "B::B(int n)"); @@ -80,7 +83,8 @@ TEST(FunctionReflectionTest, GetClassMethods) { EXPECT_EQ(get_method_name(methods2[4]), "inline B &B::operator=(const B &)"); EXPECT_EQ(get_method_name(methods2[5]), "inline B &B::operator=(B &&)"); - auto methods3 = Cpp::GetClassMethods(Decls[3]); + std::vector methods3; + Cpp::GetClassMethods(Decls[3], methods3); EXPECT_EQ(methods3.size(), 9); EXPECT_EQ(get_method_name(methods3[0]), "B::B(int n)"); @@ -93,10 +97,12 @@ TEST(FunctionReflectionTest, GetClassMethods) { EXPECT_EQ(get_method_name(methods3[8]), "inline C::~C()"); // Should not crash. - auto methods4 = Cpp::GetClassMethods(Decls[4]); + std::vector methods4; + Cpp::GetClassMethods(Decls[4], methods4); EXPECT_EQ(methods4.size(), 0); - auto methods5 = Cpp::GetClassMethods(nullptr); + std::vector methods5; + Cpp::GetClassMethods(nullptr, methods5); EXPECT_EQ(methods5.size(), 0); } @@ -112,7 +118,8 @@ TEST(FunctionReflectionTest, ConstructorInGetClassMethods) { GetAllTopLevelDecls(code, Decls); auto has_constructor = [](Decl *D) { - auto methods = Cpp::GetClassMethods(D); + std::vector methods; + Cpp::GetClassMethods(D, methods); for (auto method : methods) { if (Cpp::IsConstructor(method)) return true; diff --git a/unittests/CppInterOp/ScopeReflectionTest.cpp b/unittests/CppInterOp/ScopeReflectionTest.cpp index a193813cc..983364fba 100644 --- a/unittests/CppInterOp/ScopeReflectionTest.cpp +++ b/unittests/CppInterOp/ScopeReflectionTest.cpp @@ -879,7 +879,8 @@ TEST(ScopeReflectionTest, InstantiateTemplate) { EXPECT_TRUE(TA3_0.getAsType()->isIntegerType()); EXPECT_TRUE(Cpp::IsRecordType(TA3_1.getAsType().getAsOpaquePtr())); - auto Inst3_methods = Cpp::GetClassMethods(Instance3); + std::vector Inst3_methods; + Cpp::GetClassMethods(Instance3, Inst3_methods); EXPECT_EQ(Cpp::GetFunctionSignature(Inst3_methods[0]), "C1::C1(const C0 &val)"); From 488ef5fd7c8d2935891769f797f9689fcbeeb47b Mon Sep 17 00:00:00 2001 From: maximusron Date: Thu, 4 Apr 2024 12:49:45 +0200 Subject: [PATCH 6/8] Remove unused filter param --- include/clang/Interpreter/CppInterOp.h | 5 ++--- lib/Interpreter/CppInterOp.cpp | 7 +------ 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/include/clang/Interpreter/CppInterOp.h b/include/clang/Interpreter/CppInterOp.h index 8165312e9..bc6286881 100644 --- a/include/clang/Interpreter/CppInterOp.h +++ b/include/clang/Interpreter/CppInterOp.h @@ -336,9 +336,8 @@ namespace Cpp { TCppScope_t parent = nullptr); CPPINTEROP_API std::vector - GetTemplatedMethods(const std::string& name, TCppScope_t parent = nullptr, - const std::string& filter = ""); - + GetTemplatedMethods(const std::string& name, TCppScope_t parent = nullptr); + /// Checks if the provided parameter is a method. CPPINTEROP_API bool IsMethod(TCppConstFunction_t method); diff --git a/lib/Interpreter/CppInterOp.cpp b/lib/Interpreter/CppInterOp.cpp index 4871d2c13..d5fb4f55d 100644 --- a/lib/Interpreter/CppInterOp.cpp +++ b/lib/Interpreter/CppInterOp.cpp @@ -739,8 +739,6 @@ namespace Cpp { } } } - - void GetFunctionTemplatedDecls(TCppScope_t klass, std::vector &methods) { if (!klass) return; @@ -762,8 +760,6 @@ namespace Cpp { } } } - - bool HasDefaultConstructor(TCppScope_t scope) { auto *D = (clang::Decl *) scope; @@ -975,8 +971,7 @@ namespace Cpp { } std::vector GetTemplatedMethods(const std::string& name, - TCppScope_t parent, - const std::string& filter) { + TCppScope_t parent) { auto* D = (Decl*)parent; From 423b3e1e15f5950b7d139970c8c9351aa3a08f15 Mon Sep 17 00:00:00 2001 From: maximusron Date: Mon, 8 Apr 2024 14:30:09 +0200 Subject: [PATCH 7/8] Refactoring, move common code to static function --- include/clang/Interpreter/CppInterOp.h | 55 ++++-- lib/Interpreter/CppInterOp.cpp | 170 +++++++----------- .../CppInterOp/FunctionReflectionTest.cpp | 2 +- 3 files changed, 105 insertions(+), 122 deletions(-) diff --git a/include/clang/Interpreter/CppInterOp.h b/include/clang/Interpreter/CppInterOp.h index bc6286881..bad7eabac 100644 --- a/include/clang/Interpreter/CppInterOp.h +++ b/include/clang/Interpreter/CppInterOp.h @@ -284,13 +284,19 @@ namespace Cpp { CPPINTEROP_API int64_t GetBaseClassOffset(TCppScope_t derived, TCppScope_t base); - /// Gets a list of all the Methods that are in the Class that is + /// Sets a list of all the Methods that are in the Class that is /// supplied as a parameter. - CPPINTEROP_API void GetClassMethods(TCppScope_t klass, std::vector &methods); + ///\param[in] klass - Pointer to the scope/class under which the methods have + /// to be retrieved + ///\param[out] methods - Vector of methods in the class + CPPINTEROP_API void GetClassMethods(TCppScope_t klass, + std::vector& methods); ///\returns Template function pointer list to add proxies for /// un-instantiated/non-overloaded templated methods - CPPINTEROP_API void GetFunctionTemplatedDecls(TCppScope_t klass, std::vector &methods); + CPPINTEROP_API void + GetFunctionTemplatedDecls(TCppScope_t klass, + std::vector& methods); ///\returns if a class has a default constructor. CPPINTEROP_API bool HasDefaultConstructor(TCppScope_t scope); @@ -335,8 +341,15 @@ namespace Cpp { CPPINTEROP_API bool ExistsFunctionTemplate(const std::string& name, TCppScope_t parent = nullptr); - CPPINTEROP_API std::vector - GetTemplatedMethods(const std::string& name, TCppScope_t parent = nullptr); + /// Sets a list of all the Templated Methods that are in the Class that is + /// supplied as a parameter. + ///\param[in] name - method name + ///\param[in] parent - Pointer to the scope/class under which the methods have + /// to be retrieved + ///\param[out] funcs - vector of function pointers matching the name + CPPINTEROP_API void + GetClassTemplatedMethods(const std::string& name, TCppScope_t parent, + std::vector& funcs); /// Checks if the provided parameter is a method. CPPINTEROP_API bool IsMethod(TCppConstFunction_t method); @@ -542,11 +555,24 @@ namespace Cpp { : m_Type(type), m_IntegralValue(integral_value) {} }; /// Builds a template instantiation for a given templated declaration. - CPPINTEROP_API TCppScope_t InstantiateTemplate(TCppScope_t tmpl, - const TemplateArgInfo* template_args, - size_t template_args_size); + /// Offers a single interface for instantiation of class, function and + /// variable templates + /// + ///\param[in] tmpl - Uninstantiated template class/function + ///\param[in] template_args - Pointer to vector of template arguments stored + /// in the \c TemplateArgInfo struct + ///\param[in] template_args_size - Size of the vector of template arguments + /// passed as \c template_args + /// + ///\returns Instantiated templated class/function/variable pointer + CPPINTEROP_API TCppScope_t + InstantiateTemplate(TCppScope_t tmpl, const TemplateArgInfo* template_args, + size_t template_args_size); - /// Returns the class template instantiation arguments of \c templ_instance. + /// Sets the class template instantiation arguments of \c templ_instance. + /// + ///\param[in] templ_instance - Pointer to the template instance + ///\param[out] args - Vector of instantiation arguments CPPINTEROP_API void GetClassTemplateInstantiationArgs(TCppScope_t templ_instance, std::vector& args); @@ -557,8 +583,15 @@ namespace Cpp { CPPINTEROP_API TCppFunction_t InstantiateTemplateFunctionFromString(const char* function_template); - // Find best template match based on explicit template parameters and arg - // types + /// Finds best template match based on explicit template parameters and + /// argument types + /// + ///\param[in] candidates - Vector of suitable candidates that come under the + /// parent scope and have the same name (obtained using + /// GetClassTemplatedMethods) + ///\param[in] explicit_types - set of expicitly instantiated template types + ///\param[in] arg_types - set of argument types + ///\returns Instantiated function pointer CPPINTEROP_API TCppFunction_t BestTemplateFunctionMatch(const std::vector& candidates, const std::vector& explicit_types, diff --git a/lib/Interpreter/CppInterOp.cpp b/lib/Interpreter/CppInterOp.cpp index d5fb4f55d..ea08f55a4 100644 --- a/lib/Interpreter/CppInterOp.cpp +++ b/lib/Interpreter/CppInterOp.cpp @@ -719,54 +719,46 @@ namespace Cpp { return ComputeBaseOffset(getSema().getASTContext(), DCXXRD, Paths.front()); } - void GetClassMethods(TCppScope_t klass, std::vector &methods) { - if (!klass) return; - - auto *D = (clang::Decl *) klass; - - if (auto *TD = dyn_cast(D)) - D = GetScopeFromType(TD->getUnderlyingType()); - - if (D && isa(D)) { - auto* CXXRD = dyn_cast(D); - getSema().ForceDeclarationOfImplicitMembers(CXXRD); - for (Decl* DI : CXXRD->decls()) { - if (auto* MD = dyn_cast(DI)) - methods.push_back(MD); - else if (auto* USD = dyn_cast(DI)) - if (auto* MD = dyn_cast(USD->getTargetDecl())) - methods.push_back(MD); - } - } - } - - void GetFunctionTemplatedDecls(TCppScope_t klass, std::vector &methods) { - if (!klass) return; + template + static void GetClassDecls(TCppScope_t klass, + std::vector& methods) { + if (!klass) + return; auto* D = (clang::Decl*)klass; if (auto* TD = dyn_cast(D)) D = GetScopeFromType(TD->getUnderlyingType()); - if (D && isa(D)) { - auto* CXXRD = dyn_cast(D); - getSema().ForceDeclarationOfImplicitMembers(CXXRD); - for (Decl* DI : CXXRD->decls()) { - if (auto* MD = dyn_cast(DI)) + if (!D || !isa(D)) + return; + + auto* CXXRD = dyn_cast(D); + getSema().ForceDeclarationOfImplicitMembers(CXXRD); + for (Decl* DI : CXXRD->decls()) { + if (auto* MD = dyn_cast(DI)) + methods.push_back(MD); + else if (auto* USD = dyn_cast(DI)) + if (auto* MD = dyn_cast(USD->getTargetDecl())) methods.push_back(MD); - else if (auto* USD = dyn_cast(DI)) - if (auto* MD = dyn_cast(USD->getTargetDecl())) - methods.push_back(MD); - } } } + void GetClassMethods(TCppScope_t klass, + std::vector& methods) { + GetClassDecls(klass, methods); + } + + void GetFunctionTemplatedDecls(TCppScope_t klass, + std::vector& methods) { + GetClassDecls(klass, methods); + } + bool HasDefaultConstructor(TCppScope_t scope) { auto *D = (clang::Decl *) scope; - if (auto *CXXRD = llvm::dyn_cast_or_null(D)) { + if (auto* CXXRD = llvm::dyn_cast_or_null(D)) return CXXRD->hasDefaultConstructor(); - } return false; } @@ -833,13 +825,11 @@ namespace Cpp { TCppType_t GetFunctionReturnType(TCppFunction_t func) { auto *D = (clang::Decl *) func; - if (auto *FD = llvm::dyn_cast_or_null(D)) { - return FD->getReturnType().getAsOpaquePtr(); - } + if (auto* FD = llvm::dyn_cast_or_null(D)) + return FD->getReturnType().getAsOpaquePtr(); - if (auto* FD = llvm::dyn_cast_or_null(D)) { - return (FD->getTemplatedDecl())->getReturnType().getAsOpaquePtr(); - } + if (auto* FD = llvm::dyn_cast_or_null(D)) + return (FD->getTemplatedDecl())->getReturnType().getAsOpaquePtr(); return 0; } @@ -847,7 +837,7 @@ namespace Cpp { TCppIndex_t GetFunctionNumArgs(TCppFunction_t func) { auto *D = (clang::Decl *) func; - if (auto *FD = llvm::dyn_cast_or_null(D)) + if (auto* FD = llvm::dyn_cast_or_null(D)) return FD->getNumParams(); if (auto* FD = llvm::dyn_cast_or_null(D)) @@ -859,9 +849,8 @@ namespace Cpp { TCppIndex_t GetFunctionRequiredArgs(TCppConstFunction_t func) { auto *D = (const clang::Decl *) func; - if (auto *FD = llvm::dyn_cast_or_null (D)) { + if (auto* FD = llvm::dyn_cast_or_null(D)) return FD->getMinRequiredArguments(); - } if (auto* FD = llvm::dyn_cast_or_null(D)) return (FD->getTemplatedDecl())->getMinRequiredArguments(); @@ -883,9 +872,7 @@ namespace Cpp { return 0; } - std::string GetFunctionSignature( - TCppFunction_t func) { // FIXME : Doesn't work for template functions as - // it does in cling + std::string GetFunctionSignature(TCppFunction_t func) { if (!func) return ""; @@ -903,18 +890,6 @@ namespace Cpp { return Signature; } - // FIXME: else if (auto *FD = llvm::dyn_cast(D)) { - // std::string Signature; - // raw_string_ostream SS(Signature); - // PrintingPolicy Policy = getASTContext().getPrintingPolicy(); - // // Skip printing the body - // Policy.TerseOutput = true; - // Policy.FullyQualifiedName = true; - // Policy.SuppressDefaultTemplateArgs = false; - // FD->print(SS, Policy); - // SS.flush(); - // return Signature; - // } return ""; } @@ -954,7 +929,7 @@ namespace Cpp { { DeclContext *Within = 0; if (parent) { - auto *D = (Decl *)parent; + auto* D = (Decl*)parent; Within = llvm::dyn_cast(D); } @@ -970,17 +945,16 @@ namespace Cpp { return true; } - std::vector GetTemplatedMethods(const std::string& name, - TCppScope_t parent) { + void GetClassTemplatedMethods(const std::string& name, TCppScope_t parent, + std::vector& funcs) { auto* D = (Decl*)parent; if (!parent || name.empty()) - return {}; + return; D = GetUnderlyingScope(D); - std::vector funcs; llvm::StringRef Name(name); auto& S = getSema(); DeclarationName DName = &getASTContext().Idents.get(name); @@ -990,19 +964,13 @@ namespace Cpp { Cpp_utils::Lookup::Named(&S, R, Decl::castToDeclContext(D)); if (R.empty()) - return {}; + return; R.resolveKind(); - for (auto* Found : R) { + for (auto* Found : R) if (llvm::isa(Found)) funcs.push_back(Found); - } - - if (!funcs.empty()) - return funcs; - - return {}; } TCppFunction_t @@ -1010,53 +978,35 @@ namespace Cpp { const std::vector& explicit_types, const std::vector& arg_types) { - int k = 1; for (const auto& candidate : candidates) { auto* TFD = (FunctionTemplateDecl*)candidate; clang::TemplateParameterList* tpl = TFD->getTemplateParameters(); - if (tpl->size() < explicit_types.size()) { - // template parameter size does not match - ++k; + // template parameter size does not match + if (tpl->size() < explicit_types.size()) continue; - } - - else { - // right now uninstantiated functions give template typenames instead of - // actual types. We make this match solely based on count - const FunctionDecl* func = TFD->getTemplatedDecl(); + // right now uninstantiated functions give template typenames instead of + // actual types. We make this match solely based on count - // Check if number of function parameters match - if (func->getNumParams() != arg_types.size()) { - ++k; - continue; - } + const FunctionDecl* func = TFD->getTemplatedDecl(); + if (func->getNumParams() != arg_types.size()) + continue; - else { - // TODO : first score based on the type similarity before forcing - // instantiation try instantiating - TCppFunction_t instantiated = InstantiateTemplate( - candidate, arg_types.data(), arg_types.size()); - if (instantiated) - return instantiated; - - // Force the instantiation with template params in case of no args - // maybe steer instantiation better with arg set returned from - // TemplateProxy? - else { - instantiated = InstantiateTemplate(candidate, explicit_types.data(), - explicit_types.size()); - if (instantiated) - return instantiated; - - else { - k++; - continue; - } - } - } - } + // TODO : first score based on the type similarity before forcing + // instantiation try instantiating + TCppFunction_t instantiated = InstantiateTemplate( + candidate, arg_types.data(), arg_types.size()); + if (instantiated) + return instantiated; + + // Force the instantiation with template params in case of no args + // maybe steer instantiation better with arg set returned from + // TemplateProxy? + instantiated = InstantiateTemplate(candidate, explicit_types.data(), + explicit_types.size()); + if (instantiated) + return instantiated; } return nullptr; } diff --git a/unittests/CppInterOp/FunctionReflectionTest.cpp b/unittests/CppInterOp/FunctionReflectionTest.cpp index 2bb87e775..299e4ac2b 100644 --- a/unittests/CppInterOp/FunctionReflectionTest.cpp +++ b/unittests/CppInterOp/FunctionReflectionTest.cpp @@ -117,7 +117,7 @@ TEST(FunctionReflectionTest, ConstructorInGetClassMethods) { GetAllTopLevelDecls(code, Decls); - auto has_constructor = [](Decl *D) { + auto has_constructor = [](Decl* D) { std::vector methods; Cpp::GetClassMethods(D, methods); for (auto method : methods) { From 22c1e0de196f09a9acfd36b80584e7f4ad254667 Mon Sep 17 00:00:00 2001 From: maximusron Date: Wed, 10 Apr 2024 13:23:38 +0200 Subject: [PATCH 8/8] Update function documentation and tests --- include/clang/Interpreter/CppInterOp.h | 7 +++++-- lib/Interpreter/CppInterOp.cpp | 6 +++--- unittests/CppInterOp/FunctionReflectionTest.cpp | 16 ++++++++-------- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/include/clang/Interpreter/CppInterOp.h b/include/clang/Interpreter/CppInterOp.h index bad7eabac..420b69ff4 100644 --- a/include/clang/Interpreter/CppInterOp.h +++ b/include/clang/Interpreter/CppInterOp.h @@ -292,8 +292,11 @@ namespace Cpp { CPPINTEROP_API void GetClassMethods(TCppScope_t klass, std::vector& methods); - ///\returns Template function pointer list to add proxies for - /// un-instantiated/non-overloaded templated methods + /// Template function pointer list to add proxies for un-instantiated/ + /// non-overloaded templated methods + ///\param[in] klass - Pointer to the scope/class under which the methods have + /// to be retrieved + ///\param[out] methods - Vector of methods in the class CPPINTEROP_API void GetFunctionTemplatedDecls(TCppScope_t klass, std::vector& methods); diff --git a/lib/Interpreter/CppInterOp.cpp b/lib/Interpreter/CppInterOp.cpp index ea08f55a4..eab23c818 100644 --- a/lib/Interpreter/CppInterOp.cpp +++ b/lib/Interpreter/CppInterOp.cpp @@ -993,10 +993,10 @@ namespace Cpp { if (func->getNumParams() != arg_types.size()) continue; - // TODO : first score based on the type similarity before forcing + // FIXME : first score based on the type similarity before forcing // instantiation try instantiating - TCppFunction_t instantiated = InstantiateTemplate( - candidate, arg_types.data(), arg_types.size()); + TCppFunction_t instantiated = + InstantiateTemplate(candidate, arg_types.data(), arg_types.size()); if (instantiated) return instantiated; diff --git a/unittests/CppInterOp/FunctionReflectionTest.cpp b/unittests/CppInterOp/FunctionReflectionTest.cpp index 299e4ac2b..0eb0984af 100644 --- a/unittests/CppInterOp/FunctionReflectionTest.cpp +++ b/unittests/CppInterOp/FunctionReflectionTest.cpp @@ -487,14 +487,14 @@ TEST(FunctionReflectionTest, ExistsFunctionTemplate) { EXPECT_FALSE(Cpp::ExistsFunctionTemplate("f", Decls[2])); } -// TEST(ScopeReflectionTest, InstantiateTemplateFunctionFromString) { -// Cpp::CreateInterpreter(); -// std::string code = R"(#include )"; -// Interp->process(code); -// const char* str = "std::make_unique"; -// auto* Instance1 = (Decl*)Cpp::InstantiateTemplateFunctionFromString(str); -// EXPECT_TRUE(Instance1); -// } +TEST(FunctionReflectionTest, InstantiateTemplateFunctionFromString) { + Cpp::CreateInterpreter(); + std::string code = R"(#include )"; + Interp->process(code); + const char* str = "std::make_unique"; + auto* Instance1 = (Decl*)Cpp::InstantiateTemplateFunctionFromString(str); + EXPECT_TRUE(Instance1); +} TEST(FunctionReflectionTest, InstantiateFunctionTemplate) { std::vector Decls;