diff --git a/include/clang/Interpreter/CppInterOp.h b/include/clang/Interpreter/CppInterOp.h index 522c37b3a..420b69ff4 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 @@ -284,9 +284,22 @@ 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 std::vector GetClassMethods(TCppScope_t klass); + ///\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); + + /// 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); ///\returns if a class has a default constructor. CPPINTEROP_API bool HasDefaultConstructor(TCppScope_t scope); @@ -329,7 +342,17 @@ 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); + + /// 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); @@ -535,11 +558,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, - 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); @@ -550,6 +586,20 @@ namespace Cpp { CPPINTEROP_API TCppFunction_t InstantiateTemplateFunctionFromString(const char* function_template); + /// 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, + const 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..eab23c818 100644 --- a/lib/Interpreter/CppInterOp.cpp +++ b/lib/Interpreter/CppInterOp.cpp @@ -719,39 +719,46 @@ 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) - { - + template + static void GetClassDecls(TCppScope_t klass, + std::vector& methods) { if (!klass) - return {}; + return; - auto *D = (clang::Decl *) klass; + auto* D = (clang::Decl*)klass; - if (auto *TD = dyn_cast(D)) + 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)) + 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); - } } - return methods; + } + + 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; } @@ -818,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; } @@ -832,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)) @@ -844,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(); @@ -868,8 +872,7 @@ namespace Cpp { return 0; } - std::string GetFunctionSignature(TCppFunction_t func) - { + std::string GetFunctionSignature(TCppFunction_t func) { if (!func) return ""; @@ -886,6 +889,7 @@ namespace Cpp { SS.flush(); return Signature; } + return ""; } @@ -925,7 +929,7 @@ namespace Cpp { { DeclContext *Within = 0; if (parent) { - auto *D = (Decl *)parent; + auto* D = (Decl*)parent; Within = llvm::dyn_cast(D); } @@ -941,6 +945,72 @@ namespace Cpp { return true; } + void GetClassTemplatedMethods(const std::string& name, TCppScope_t parent, + std::vector& funcs) { + + auto* D = (Decl*)parent; + + if (!parent || name.empty()) + return; + + D = GetUnderlyingScope(D); + + 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(); + + for (auto* Found : R) + if (llvm::isa(Found)) + funcs.push_back(Found); + } + + TCppFunction_t + BestTemplateFunctionMatch(const std::vector& candidates, + const std::vector& explicit_types, + const std::vector& arg_types) { + + for (const auto& candidate : candidates) { + auto* TFD = (FunctionTemplateDecl*)candidate; + clang::TemplateParameterList* tpl = TFD->getTemplateParameters(); + + // template parameter size does not match + if (tpl->size() < explicit_types.size()) + continue; + + // right now uninstantiated functions give template typenames instead of + // actual types. We make this match solely based on count + + const FunctionDecl* func = TFD->getTemplatedDecl(); + if (func->getNumParams() != arg_types.size()) + continue; + + // FIXME : 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; + } + // Gets the AccessSpecifier of the function and checks if it is equal to // the provided AccessSpecifier. bool CheckMethodAccess(TCppFunction_t method, AccessSpecifier AS) @@ -2836,7 +2906,7 @@ namespace Cpp { } TCppScope_t InstantiateTemplate(TCppScope_t tmpl, - TemplateArgInfo* template_args, + const TemplateArgInfo* template_args, size_t template_args_size) { ASTContext &C = getASTContext(); diff --git a/unittests/CppInterOp/FunctionReflectionTest.cpp b/unittests/CppInterOp/FunctionReflectionTest.cpp index 3d629e987..0eb0984af 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); } @@ -111,8 +117,9 @@ TEST(FunctionReflectionTest, ConstructorInGetClassMethods) { GetAllTopLevelDecls(code, Decls); - auto has_constructor = [](Decl *D) { - auto methods = Cpp::GetClassMethods(D); + auto has_constructor = [](Decl* D) { + std::vector methods; + Cpp::GetClassMethods(D, methods); for (auto method : methods) { if (Cpp::IsConstructor(method)) return true; @@ -480,6 +487,63 @@ TEST(FunctionReflectionTest, ExistsFunctionTemplate) { EXPECT_FALSE(Cpp::ExistsFunctionTemplate("f", Decls[2])); } +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; + 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"( 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)");