diff --git a/include/clang/Interpreter/CppInterOp.h b/include/clang/Interpreter/CppInterOp.h index 5137bb787..c2f7115cf 100644 --- a/include/clang/Interpreter/CppInterOp.h +++ b/include/clang/Interpreter/CppInterOp.h @@ -416,13 +416,27 @@ namespace Cpp { CPPINTEROP_API bool ExistsFunctionTemplate(const std::string& name, TCppScope_t parent = nullptr); + /// Sets a list of all the constructor for a scope/class that is + /// supplied as a parameter. + ///\param[in] name - This string is used as a constraint, that clients can use + /// to ensure the constructors match the name that they provide + ///\param[in] parent - Pointer to the scope/class for which the constructors + /// are being looked up + /// to be retrieved + ///\param[out] funcs - vector of handles to all constructors found under the + /// given scope + CPPINTEROP_API void LookupConstructors(const std::string& name, + TCppScope_t parent, + std::vector& funcs); + /// Sets a list of all the Templated Methods that are in the Class that is /// supplied as a parameter. + ///\returns true if the lookup succeeded, and false if there are no candidates ///\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 + CPPINTEROP_API bool GetClassTemplatedMethods(const std::string& name, TCppScope_t parent, std::vector& funcs); diff --git a/lib/Interpreter/CppInterOp.cpp b/lib/Interpreter/CppInterOp.cpp index 2c00d79ba..3575bb2fa 100755 --- a/lib/Interpreter/CppInterOp.cpp +++ b/lib/Interpreter/CppInterOp.cpp @@ -988,9 +988,10 @@ namespace Cpp { // encompassed in an anonymous namespace as follows. namespace { bool IsTemplatedFunction(Decl *D) { - if (llvm::isa_and_nonnull(D)) - return true; + return llvm::isa_and_nonnull(D); + } + bool IsTemplateInstantiationOrSpecialization(Decl* D) { if (auto *FD = llvm::dyn_cast_or_null(D)) { auto TK = FD->getTemplatedKind(); return TK == FunctionDecl::TemplatedKind:: @@ -1013,9 +1014,12 @@ namespace Cpp { bool IsTemplatedFunction(TCppFunction_t func) { auto *D = (Decl *) func; - return IsTemplatedFunction(D); + return IsTemplatedFunction(D) || IsTemplateInstantiationOrSpecialization(D); } + // FIXME: This lookup is broken, and should no longer be used in favour of + // `GetClassTemplatedMethods` If the candidate set returned is =1, that means + // the template function exists and >1 means overloads bool ExistsFunctionTemplate(const std::string& name, TCppScope_t parent) { @@ -1031,38 +1035,70 @@ namespace Cpp { return false; if ((intptr_t) ND != (intptr_t) -1) - return IsTemplatedFunction(ND); + return IsTemplatedFunction(ND) || + IsTemplateInstantiationOrSpecialization(ND); // FIXME: Cycle through the Decls and check if there is a templated function return true; } - void GetClassTemplatedMethods(const std::string& name, TCppScope_t parent, - std::vector& funcs) { - + // Looks up all constructors in the current DeclContext + void LookupConstructors(const std::string& name, TCppScope_t parent, + std::vector& funcs) { auto* D = (Decl*)parent; - if (!parent || name.empty()) - return; + if (auto* CXXRD = llvm::dyn_cast_or_null(D)) { + getSema().ForceDeclarationOfImplicitMembers(CXXRD); + DeclContextLookupResult Result = getSema().LookupConstructors(CXXRD); + // Obtaining all constructors when we intend to lookup a method under a + // scope can lead to crashes. We avoid that by accumulating constructors + // only if the Decl matches the lookup name. + for (auto* i : Result) + if (GetName(i) == name) + funcs.push_back(i); + } + } - D = GetUnderlyingScope(D); + bool GetClassTemplatedMethods(const std::string& name, TCppScope_t parent, + std::vector& funcs) { + auto* D = (Decl*)parent; + if (!D && name.empty()) + return false; - llvm::StringRef Name(name); + // Accumulate constructors + LookupConstructors(name, parent, funcs); auto& S = getSema(); + D = GetUnderlyingScope(D); + llvm::StringRef Name(name); DeclarationName DName = &getASTContext().Idents.get(name); clang::LookupResult R(S, DName, SourceLocation(), Sema::LookupOrdinaryName, For_Visible_Redeclaration); + auto* DC = clang::Decl::castToDeclContext(D); + Cpp_utils::Lookup::Named(&S, R, DC); - Cpp_utils::Lookup::Named(&S, R, Decl::castToDeclContext(D)); - - if (R.empty()) - return; + if (R.getResultKind() == clang::LookupResult::NotFound && funcs.empty()) + return false; - R.resolveKind(); + // Distinct match, single Decl + else if (R.getResultKind() == clang::LookupResult::Found) { + if (IsTemplatedFunction(R.getFoundDecl())) + funcs.push_back(R.getFoundDecl()); + } + // Loop over overload set + else if (R.getResultKind() == clang::LookupResult::FoundOverloaded) { + for (auto* Found : R) + if (IsTemplatedFunction(Found)) + funcs.push_back(Found); + } - for (auto* Found : R) - if (llvm::isa(Found)) - funcs.push_back(Found); + // TODO: Handle ambiguously found LookupResult + // else if (R.getResultKind() == clang::LookupResult::Ambiguous) { + // auto kind = R.getAmbiguityKind(); + // ... + // Produce a diagnostic describing the ambiguity that resulted + // from name lookup as done in Sema::DiagnoseAmbiguousLookup + // + return !funcs.empty(); } // Adapted from inner workings of Sema::BuildCallExpr @@ -1185,6 +1221,8 @@ namespace Cpp { bool IsConstructor(TCppConstFunction_t method) { const auto* D = static_cast(method); + if (const auto* FTD = dyn_cast(D)) + return IsConstructor(FTD->getTemplatedDecl()); return llvm::isa_and_nonnull(D); } diff --git a/unittests/CppInterOp/FunctionReflectionTest.cpp b/unittests/CppInterOp/FunctionReflectionTest.cpp index 72a157be1..ea8b4180d 100644 --- a/unittests/CppInterOp/FunctionReflectionTest.cpp +++ b/unittests/CppInterOp/FunctionReflectionTest.cpp @@ -296,19 +296,50 @@ TEST(FunctionReflectionTest, GetClassDecls) { GetAllSubDecls(Decls[0], SubDecls); std::vector methods; - std::vector template_methods; - Cpp::GetClassMethods(Decls[0], methods); + + EXPECT_EQ(methods.size(), 10); // includes structors and operators + EXPECT_EQ(Cpp::GetName(methods[0]), Cpp::GetName(SubDecls[4])); + EXPECT_EQ(Cpp::GetName(methods[1]), Cpp::GetName(SubDecls[5])); + EXPECT_EQ(Cpp::GetName(methods[2]), Cpp::GetName(SubDecls[7])); + EXPECT_EQ(Cpp::GetName(methods[3]), Cpp::GetName(SubDecls[8])); +} + +TEST(FunctionReflectionTest, GetFunctionTemplatedDecls) { + std::vector Decls, SubDecls; + std::string code = R"( + class MyTemplatedMethodClass { + template + long get_size(A, B, int i = 0) {} + + template + long get_float_size(int i, A a = A(), B b = B()) {} + + template + void get_char_size(long k, A, char ch = 'a', double l = 0.0) {} + + void f1() {} + void f2(int i, double d, long l, char ch) {} + + template + void get_size(long k, A, char ch = 'a', double l = 0.0) {} + + void f3(int i, double d, long l = 0, char ch = 'a') {} + void f4(int i = 0, double d = 0.0, long l = 0, char ch = 'a') {} + }; + )"; + + GetAllTopLevelDecls(code, Decls); + GetAllSubDecls(Decls[0], SubDecls); + + std::vector template_methods; Cpp::GetFunctionTemplatedDecls(Decls[0], template_methods); + EXPECT_EQ(template_methods.size(), 4); EXPECT_EQ(Cpp::GetName(template_methods[0]), Cpp::GetName(SubDecls[1])); EXPECT_EQ(Cpp::GetName(template_methods[1]), Cpp::GetName(SubDecls[2])); EXPECT_EQ(Cpp::GetName(template_methods[2]), Cpp::GetName(SubDecls[3])); - EXPECT_EQ(Cpp::GetName(methods[0]) , Cpp::GetName(SubDecls[4])); - EXPECT_EQ(Cpp::GetName(methods[1]) , Cpp::GetName(SubDecls[5])); EXPECT_EQ(Cpp::GetName(template_methods[3]), Cpp::GetName(SubDecls[6])); - EXPECT_EQ(Cpp::GetName(methods[2]) , Cpp::GetName(SubDecls[7])); - EXPECT_EQ(Cpp::GetName(methods[3]) , Cpp::GetName(SubDecls[8])); } TEST(FunctionReflectionTest, GetFunctionReturnType) { @@ -662,6 +693,159 @@ TEST(FunctionReflectionTest, InstantiateTemplateMethod) { EXPECT_TRUE(TA1.getAsType()->isIntegerType()); } +TEST(FunctionReflectionTest, LookupConstructors) { + if (llvm::sys::RunningOnValgrind()) + GTEST_SKIP() << "XFAIL due to Valgrind report"; + + std::vector Decls; + std::string code = R"( + class MyClass { + public: + MyClass(); + void helperMethod(); + MyClass(const MyClass&); + static void staticFunc(); + MyClass(MyClass&&); + template + MyClass(T); + ~MyClass(); + }; + + MyClass::MyClass() {} + void MyClass::helperMethod() {} + MyClass::MyClass(const MyClass&) {} + void MyClass::staticFunc() {} + MyClass::MyClass(MyClass&&) {} + template + MyClass::MyClass(T t) {} + )"; + + GetAllTopLevelDecls(code, Decls); + std::vector ctors; + Cpp::LookupConstructors("MyClass", Decls[0], ctors); + + EXPECT_EQ(ctors.size(), 4) + << "Constructor lookup did not retrieve the expected set"; + EXPECT_EQ(Cpp::GetFunctionSignature(ctors[0]), "MyClass::MyClass()"); + EXPECT_EQ(Cpp::GetFunctionSignature(ctors[1]), + "MyClass::MyClass(const MyClass &)"); + EXPECT_EQ(Cpp::GetFunctionSignature(ctors[2]), + "MyClass::MyClass(MyClass &&)"); + EXPECT_EQ(Cpp::GetFunctionSignature(ctors[3]), "MyClass::MyClass(T t)"); +} + +TEST(FunctionReflectionTest, GetClassTemplatedMethods) { + if (llvm::sys::RunningOnValgrind()) + GTEST_SKIP() << "XFAIL due to Valgrind report"; + + std::vector Decls; + std::string code = R"( + class MyClass { + public: + MyClass(); + void helperMethod(); + template + MyClass(T); + template + void templatedMethod(U param); + template + U templatedMethod(U a, V b); + static void staticFunc(); + template + static void templatedStaticMethod(T param); + ~MyClass(); + }; + + MyClass::MyClass() {} + void MyClass::helperMethod() {} + template + MyClass::MyClass(T t) {} + template + void MyClass::templatedMethod(U param) {} + template + U MyClass::templatedMethod(U a, V b) { return a * b; } + void MyClass::staticFunc() {} + template + void MyClass::templatedStaticMethod(T param) {} + )"; + + GetAllTopLevelDecls(code, Decls); + std::vector templatedMethods; + Cpp::GetClassTemplatedMethods("MyClass", Decls[0], templatedMethods); + Cpp::GetClassTemplatedMethods("templatedMethod", Decls[0], templatedMethods); + Cpp::GetClassTemplatedMethods("templatedStaticMethod", Decls[0], + templatedMethods); + + EXPECT_EQ(templatedMethods.size(), 6) + << "Templated methods lookup did not retrieve the expected set"; + EXPECT_EQ(Cpp::GetFunctionSignature(templatedMethods[0]), + "MyClass::MyClass()"); + EXPECT_EQ(Cpp::GetFunctionSignature(templatedMethods[1]), + "MyClass::MyClass(T t)"); + EXPECT_EQ(Cpp::GetFunctionSignature(templatedMethods[2]), + "inline constexpr MyClass::MyClass(const MyClass &)"); + EXPECT_EQ(Cpp::GetFunctionSignature(templatedMethods[3]), + "void MyClass::templatedMethod(U param)"); + EXPECT_EQ(Cpp::GetFunctionSignature(templatedMethods[4]), + "U MyClass::templatedMethod(U a, V b)"); + EXPECT_EQ(Cpp::GetFunctionSignature(templatedMethods[5]), + "void MyClass::templatedStaticMethod(T param)"); +} + +TEST(FunctionReflectionTest, GetClassTemplatedMethods_VariadicsAndOthers) { + std::vector Decls; + std::string code = R"( + class MyClass { + public: + template + void variadicMethod(Args... args); + + template + int fixedMethod(); + + template + T defaultMethod(T param); + + template + U variadicMethod(U first, V... rest); + + template + static void staticVariadic(T t, Args... args); + }; + + template + void MyClass::variadicMethod(Args... args) {} + template + int MyClass::fixedMethod() { return N; } + template + T MyClass::defaultMethod(T param) { return param; } + template + U MyClass::variadicMethod(U first, V... rest) { return first; } + template + void MyClass::staticVariadic(T t, Args... args) {} + )"; + + GetAllTopLevelDecls(code, Decls); + std::vector templatedMethods; + Cpp::GetClassTemplatedMethods("fixedMethod", Decls[0], templatedMethods); + Cpp::GetClassTemplatedMethods("defaultMethod", Decls[0], templatedMethods); + Cpp::GetClassTemplatedMethods("variadicMethod", Decls[0], templatedMethods); + Cpp::GetClassTemplatedMethods("staticVariadic", Decls[0], templatedMethods); + + EXPECT_EQ(templatedMethods.size(), 5) + << "Templated methods lookup did not retrieve the expected set"; + EXPECT_EQ(Cpp::GetFunctionSignature(templatedMethods[0]), + "int MyClass::fixedMethod()"); + EXPECT_EQ(Cpp::GetFunctionSignature(templatedMethods[1]), + "T MyClass::defaultMethod(T param)"); + EXPECT_EQ(Cpp::GetFunctionSignature(templatedMethods[2]), + "void MyClass::variadicMethod(Args ...args)"); + EXPECT_EQ(Cpp::GetFunctionSignature(templatedMethods[3]), + "U MyClass::variadicMethod(U first, V ...rest)"); + EXPECT_EQ(Cpp::GetFunctionSignature(templatedMethods[4]), + "void MyClass::staticVariadic(T t, Args ...args)"); +} + TEST(FunctionReflectionTest, BestOverloadFunctionMatch1) { std::vector Decls; std::string code = R"( @@ -1061,6 +1245,28 @@ TEST(FunctionReflectionTest, IsConstructor) { EXPECT_FALSE(Cpp::IsConstructor(SubDecls[4])); EXPECT_FALSE(Cpp::IsConstructor(SubDecls[6])); EXPECT_FALSE(Cpp::IsConstructor(SubDecls[8])); + + // Test for templated constructor + std::vector templDecls, templSubDecls; + std::string templCode = R"( + class T { + public: + template + T(U) {} + void func() {} + ~T() {} + }; + )"; + + GetAllTopLevelDecls(templCode, templDecls); + GetAllSubDecls(templDecls[0], templSubDecls); + + int templCtorCount = 0; + for (auto* decl : templSubDecls) { + if (Cpp::IsConstructor(decl)) + templCtorCount++; + } + EXPECT_EQ(templCtorCount, 1); } TEST(FunctionReflectionTest, IsDestructor) {