diff --git a/.github/workflows/cppinterop-diff.yml b/.github/workflows/cppinterop-diff.yml index fb8ff4c69eaf6..2b2b080481b0f 100644 --- a/.github/workflows/cppinterop-diff.yml +++ b/.github/workflows/cppinterop-diff.yml @@ -16,7 +16,7 @@ jobs: uses: actions/checkout@v4 with: repository: compiler-research/CppInterOp - ref: b0c3e360bdbea9618dd2744836671667c108bf97 + ref: 33bfa391c9322bd7cb79f954e54eedef486c999a path: CppInterOp - name: Drop directories that are not added to ROOT working-directory: CppInterOp diff --git a/interpreter/CppInterOp/CMakeLists.txt b/interpreter/CppInterOp/CMakeLists.txt index ddb199a217761..aae32a058d373 100644 --- a/interpreter/CppInterOp/CMakeLists.txt +++ b/interpreter/CppInterOp/CMakeLists.txt @@ -61,8 +61,8 @@ if( CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR ) endif() if (NOT DEFINED Clang_DIR) set(Clang_DIR ${Cling_DIR}) + endif() endif() -endif() include(GNUInstallDirs) ## Define supported version of clang and llvm diff --git a/interpreter/CppInterOp/VERSION b/interpreter/CppInterOp/VERSION index 3c1aaa2b0fe7e..314a097be26e5 100644 --- a/interpreter/CppInterOp/VERSION +++ b/interpreter/CppInterOp/VERSION @@ -1 +1 @@ -1.6.0;dev +1.7.0;dev diff --git a/interpreter/CppInterOp/docs/ReleaseNotes.md b/interpreter/CppInterOp/docs/ReleaseNotes.md index 8b53f0b3c1967..2686b3d56feb5 100644 --- a/interpreter/CppInterOp/docs/ReleaseNotes.md +++ b/interpreter/CppInterOp/docs/ReleaseNotes.md @@ -1,7 +1,7 @@ # Introduction This document contains the release notes for the language interoperability -library CppInterOp, release 1.6.0. CppInterOp is built on top of +library CppInterOp, release 1.7.0. CppInterOp is built on top of [Clang](http://clang.llvm.org) and [LLVM](http://llvm.org%3E) compiler infrastructure. Here we describe the status of CppInterOp in some detail, including major improvements from the previous release and new feature work. @@ -16,7 +16,7 @@ interoperability on the fly. In such scenarios CppInterOp can be used to provide the necessary introspection information to the other side helping the language cross talk. -## What's New in CppInterOp 1.6.0? +## What's New in CppInterOp 1.7.0? Some of the major new features and improvements to CppInterOp are listed here. Generic improvements to CppInterOp as a whole or to its underlying @@ -48,7 +48,7 @@ infrastructure are described first. [XXX](https://github.com/compiler-research/CppInterOp/issues/XXX) ## Special Kudos @@ -61,6 +61,6 @@ FirstName LastName (#commits) A B (N) diff --git a/interpreter/CppInterOp/include/clang-c/CXCppInterOp.h b/interpreter/CppInterOp/include/clang-c/CXCppInterOp.h index e5b057ddecdf7..f885e19c566ea 100644 --- a/interpreter/CppInterOp/include/clang-c/CXCppInterOp.h +++ b/interpreter/CppInterOp/include/clang-c/CXCppInterOp.h @@ -34,7 +34,8 @@ typedef struct CXInterpreterImpl* CXInterpreter; * * \returns a \c CXInterpreter. */ -CXInterpreter clang_createInterpreter(const char* const* argv, int argc); +CINDEX_LINKAGE CXInterpreter clang_createInterpreter(const char* const* argv, + int argc); typedef void* TInterp_t; @@ -43,27 +44,29 @@ typedef void* TInterp_t; * * \returns a \c CXInterpreter. */ -CXInterpreter clang_createInterpreterFromRawPtr(TInterp_t I); +CINDEX_LINKAGE CXInterpreter clang_createInterpreterFromRawPtr(TInterp_t I); /** * Returns a pointer to the underlying interpreter. */ -void* clang_Interpreter_getClangInterpreter(CXInterpreter I); +CINDEX_LINKAGE void* clang_Interpreter_getClangInterpreter(CXInterpreter I); /** * Returns a \c TInterp_t and takes the ownership. */ -TInterp_t clang_Interpreter_takeInterpreterAsPtr(CXInterpreter I); +CINDEX_LINKAGE TInterp_t +clang_Interpreter_takeInterpreterAsPtr(CXInterpreter I); /** * Undo N previous incremental inputs. */ -enum CXErrorCode clang_Interpreter_undo(CXInterpreter I, unsigned int N); +CINDEX_LINKAGE enum CXErrorCode clang_Interpreter_undo(CXInterpreter I, + unsigned int N); /** * Dispose of the given interpreter context. */ -void clang_Interpreter_dispose(CXInterpreter I); +CINDEX_LINKAGE void clang_Interpreter_dispose(CXInterpreter I); /** * Describes the return result of the different routines that do the incremental @@ -95,8 +98,9 @@ typedef enum { * * \param prepend Whether to prepend the directory to the search path. */ -void clang_Interpreter_addSearchPath(CXInterpreter I, const char* dir, - bool isUser, bool prepend); +CINDEX_LINKAGE void clang_Interpreter_addSearchPath(CXInterpreter I, + const char* dir, + bool isUser, bool prepend); /** * Add an include path. @@ -105,7 +109,8 @@ void clang_Interpreter_addSearchPath(CXInterpreter I, const char* dir, * * \param dir The directory to add. */ -void clang_Interpreter_addIncludePath(CXInterpreter I, const char* dir); +CINDEX_LINKAGE void clang_Interpreter_addIncludePath(CXInterpreter I, + const char* dir); /** * Declares a code snippet in \c code and does not execute it. @@ -118,8 +123,8 @@ void clang_Interpreter_addIncludePath(CXInterpreter I, const char* dir); * * \returns a \c CXErrorCode. */ -enum CXErrorCode clang_Interpreter_declare(CXInterpreter I, const char* code, - bool silent); +CINDEX_LINKAGE enum CXErrorCode +clang_Interpreter_declare(CXInterpreter I, const char* code, bool silent); /** * Declares and executes a code snippet in \c code. @@ -130,7 +135,8 @@ enum CXErrorCode clang_Interpreter_declare(CXInterpreter I, const char* code, * * \returns a \c CXErrorCode. */ -enum CXErrorCode clang_Interpreter_process(CXInterpreter I, const char* code); +CINDEX_LINKAGE enum CXErrorCode clang_Interpreter_process(CXInterpreter I, + const char* code); /** * An opaque pointer representing a lightweight struct that is used for carrying @@ -143,14 +149,14 @@ typedef void* CXValue; * * \returns a \c CXValue. */ -CXValue clang_createValue(void); +CINDEX_LINKAGE CXValue clang_createValue(void); /** * Dispose of the given CXValue. * * \param V The CXValue to dispose. */ -void clang_Value_dispose(CXValue V); +CINDEX_LINKAGE void clang_Value_dispose(CXValue V); /** * Declares, executes and stores the execution result to \c V. @@ -163,8 +169,8 @@ void clang_Value_dispose(CXValue V); * * \returns a \c CXErrorCode. */ -enum CXErrorCode clang_Interpreter_evaluate(CXInterpreter I, const char* code, - CXValue V); +CINDEX_LINKAGE enum CXErrorCode +clang_Interpreter_evaluate(CXInterpreter I, const char* code, CXValue V); /** * Looks up the library if access is enabled. @@ -175,7 +181,8 @@ enum CXErrorCode clang_Interpreter_evaluate(CXInterpreter I, const char* code, * * \returns the path to the library. */ -CXString clang_Interpreter_lookupLibrary(CXInterpreter I, const char* lib_name); +CINDEX_LINKAGE CXString clang_Interpreter_lookupLibrary(CXInterpreter I, + const char* lib_name); /** * Finds \c lib_stem considering the list of search paths and loads it by @@ -189,9 +196,8 @@ CXString clang_Interpreter_lookupLibrary(CXInterpreter I, const char* lib_name); * * \returns a \c CXInterpreter_CompilationResult. */ -CXInterpreter_CompilationResult -clang_Interpreter_loadLibrary(CXInterpreter I, const char* lib_stem, - bool lookup); +CINDEX_LINKAGE CXInterpreter_CompilationResult clang_Interpreter_loadLibrary( + CXInterpreter I, const char* lib_stem, bool lookup); /** * Finds \c lib_stem considering the list of search paths and unloads it by @@ -201,7 +207,8 @@ clang_Interpreter_loadLibrary(CXInterpreter I, const char* lib_stem, * * \param lib_stem The stem of the library to unload. */ -void clang_Interpreter_unloadLibrary(CXInterpreter I, const char* lib_stem); +CINDEX_LINKAGE void clang_Interpreter_unloadLibrary(CXInterpreter I, + const char* lib_stem); /** * @} @@ -226,40 +233,41 @@ typedef struct { } CXScope; // for debugging purposes -void clang_scope_dump(CXScope S); +CINDEX_LINKAGE void clang_scope_dump(CXScope S); /** * Checks if a class has a default constructor. */ -bool clang_hasDefaultConstructor(CXScope S); +CINDEX_LINKAGE bool clang_hasDefaultConstructor(CXScope S); /** * Returns the default constructor of a class, if any. */ -CXScope clang_getDefaultConstructor(CXScope S); +CINDEX_LINKAGE CXScope clang_getDefaultConstructor(CXScope S); /** * Returns the class destructor, if any. */ -CXScope clang_getDestructor(CXScope S); +CINDEX_LINKAGE CXScope clang_getDestructor(CXScope S); /** * Returns a stringified version of a given function signature in the form: * void N::f(int i, double d, long l = 0, char ch = 'a'). */ -CXString clang_getFunctionSignature(CXScope func); +CINDEX_LINKAGE CXString clang_getFunctionSignature(CXScope func); /** * Checks if a function is a templated function. */ -bool clang_isTemplatedFunction(CXScope func); +CINDEX_LINKAGE bool clang_isTemplatedFunction(CXScope func); /** * This function performs a lookup to check if there is a templated function of * that type. \c parent is mandatory, the global scope should be used as the * default value. */ -bool clang_existsFunctionTemplate(const char* name, CXScope parent); +CINDEX_LINKAGE bool clang_existsFunctionTemplate(const char* name, + CXScope parent); typedef struct { void* Type; @@ -282,9 +290,8 @@ typedef struct { * \returns a \c CXScope representing the instantiated templated * class/function/variable. */ -CXScope clang_instantiateTemplate(CXScope tmpl, - CXTemplateArgInfo* template_args, - size_t template_args_size); +CINDEX_LINKAGE CXScope clang_instantiateTemplate( + CXScope tmpl, CXTemplateArgInfo* template_args, size_t template_args_size); /** * A fake CXType for working with the interpreter. @@ -299,12 +306,12 @@ typedef struct { /** * Gets the string of the type that is passed as a parameter. */ -CXString clang_getTypeAsString(CXQualType type); +CINDEX_LINKAGE CXString clang_getTypeAsString(CXQualType type); /** * Returns the complex of the provided type. */ -CXQualType clang_getComplexType(CXQualType eltype); +CINDEX_LINKAGE CXQualType clang_getComplexType(CXQualType eltype); /** * An opaque pointer representing the object of a given type (\c CXScope). @@ -314,18 +321,18 @@ typedef void* CXObject; /** * Allocates memory for the given type. */ -CXObject clang_allocate(unsigned int n); +CINDEX_LINKAGE CXObject clang_allocate(unsigned int n); /** * Deallocates memory for a given class. */ -void clang_deallocate(CXObject address); +CINDEX_LINKAGE void clang_deallocate(CXObject address); /** * Creates an object of class \c scope and calls its default constructor. If \c * arena is set it uses placement new. */ -CXObject clang_construct(CXScope scope, void* arena); +CINDEX_LINKAGE CXObject clang_construct(CXScope scope, void* arena); /** * Creates a trampoline function and makes a call to a generic function or @@ -341,8 +348,8 @@ CXObject clang_construct(CXScope scope, void* arena); * * \param self The 'this pointer' of the object. */ -void clang_invoke(CXScope func, void* result, void** args, size_t n, - void* self); +CINDEX_LINKAGE void clang_invoke(CXScope func, void* result, void** args, + size_t n, void* self); /** * Calls the destructor of object of type \c type. When withFree is true it @@ -354,7 +361,7 @@ void clang_invoke(CXScope func, void* result, void** args, size_t n, * * \param withFree Whether to call operator delete/free or not. */ -void clang_destruct(CXObject This, CXScope S, bool withFree); +CINDEX_LINKAGE void clang_destruct(CXObject This, CXScope S, bool withFree); /** * @} diff --git a/interpreter/CppInterOp/include/clang/Interpreter/CppInterOp.h b/interpreter/CppInterOp/include/clang/Interpreter/CppInterOp.h index 96677dbe1512e..c0cb234aa392b 100644 --- a/interpreter/CppInterOp/include/clang/Interpreter/CppInterOp.h +++ b/interpreter/CppInterOp/include/clang/Interpreter/CppInterOp.h @@ -210,6 +210,9 @@ namespace Cpp { /// Checks if the scope is a class or not. CPPINTEROP_API bool IsClass(TCppScope_t scope); + /// Checks if the scope is a function. + CPPINTEROP_API bool IsFunction(TCppScope_t scope); + /// Checks if the type is a function pointer. CPPINTEROP_API bool IsFunctionPointerType(TCppType_t type); @@ -504,6 +507,18 @@ namespace Cpp { /// Checks if the provided parameter is a Plain Old Data Type (POD). CPPINTEROP_API bool IsPODType(TCppType_t type); + /// Checks if type is a pointer + CPPINTEROP_API bool IsPointerType(TCppType_t type); + + /// Get the underlying pointee type + CPPINTEROP_API TCppType_t GetPointeeType(TCppType_t type); + + /// Checks if type is a reference + CPPINTEROP_API bool IsReferenceType(TCppType_t type); + + /// Get the type that the reference refers to + CPPINTEROP_API TCppType_t GetNonReferenceType(TCppType_t type); + /// Gets the pure, Underlying Type (as opposed to the Using Type). CPPINTEROP_API TCppType_t GetUnderlyingType(TCppType_t type); @@ -694,17 +709,16 @@ namespace Cpp { CPPINTEROP_API TCppFunction_t InstantiateTemplateFunctionFromString(const char* function_template); - /// Finds best template match based on explicit template parameters and - /// argument types + /// Finds best overload match based on explicit template parameters (if any) + /// 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] candidates - vector of overloads that come under the + /// parent scope and have the same name ///\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, + BestOverloadFunctionMatch(const std::vector& candidates, const std::vector& explicit_types, const std::vector& arg_types); diff --git a/interpreter/CppInterOp/lib/Interpreter/CMakeLists.txt b/interpreter/CppInterOp/lib/Interpreter/CMakeLists.txt index f52eb0d9cdbac..a9f8fcaa6cfbf 100644 --- a/interpreter/CppInterOp/lib/Interpreter/CMakeLists.txt +++ b/interpreter/CppInterOp/lib/Interpreter/CMakeLists.txt @@ -133,6 +133,8 @@ else() LINK_LIBS ${link_libs} ) + + target_compile_definitions(clangCppInterOp PUBLIC "_CINDEX_LIB_") # workaround for the use of `CINDEX_LINKAGE` endif() string(REPLACE ";" "\;" _VER CPPINTEROP_VERSION) diff --git a/interpreter/CppInterOp/lib/Interpreter/CppInterOp.cpp b/interpreter/CppInterOp/lib/Interpreter/CppInterOp.cpp index ddf6b24676c31..e4ef404a74ee8 100755 --- a/interpreter/CppInterOp/lib/Interpreter/CppInterOp.cpp +++ b/interpreter/CppInterOp/lib/Interpreter/CppInterOp.cpp @@ -13,16 +13,29 @@ #include "clang/AST/CXXInheritance.h" #include "clang/AST/Decl.h" +#include "clang/AST/DeclAccessPair.h" +#include "clang/AST/DeclBase.h" #include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclarationName.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" #include "clang/AST/GlobalDecl.h" #include "clang/AST/Mangle.h" +#include "clang/AST/NestedNameSpecifier.h" #include "clang/AST/QualTypeNames.h" #include "clang/AST/RecordLayout.h" +#include "clang/AST/Stmt.h" +#include "clang/AST/Type.h" #include "clang/Basic/DiagnosticSema.h" #include "clang/Basic/Linkage.h" +#include "clang/Basic/OperatorKinds.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/Specifiers.h" #include "clang/Basic/Version.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Sema/Lookup.h" +#include "clang/Sema/Overload.h" +#include "clang/Sema/Ownership.h" #include "clang/Sema/Sema.h" #if CLANG_VERSION_MAJOR >= 19 #include "clang/Sema/Redeclaration.h" @@ -210,6 +223,11 @@ namespace Cpp { return isa(D); } + bool IsFunction(TCppScope_t scope) { + Decl* D = static_cast(scope); + return isa(D); + } + bool IsFunctionPointerType(TCppType_t type) { QualType QT = QualType::getFromOpaquePtr(type); return QT->isFunctionPointerType(); @@ -877,8 +895,19 @@ 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)) { + QualType Type = FD->getReturnType(); + if (Type->isUndeducedAutoType() && IsTemplatedFunction(FD) && + !FD->isDefined()) { +#ifdef CPPINTEROP_USE_CLING + cling::Interpreter::PushTransactionRAII RAII(&getInterp()); +#endif + getSema().InstantiateFunctionDefinition(SourceLocation(), FD, true, + true); + Type = FD->getReturnType(); + } + return Type.getAsOpaquePtr(); + } if (auto* FD = llvm::dyn_cast_or_null(D)) return (FD->getTemplatedDecl())->getReturnType().getAsOpaquePtr(); @@ -1026,62 +1055,89 @@ namespace Cpp { funcs.push_back(Found); } + // Adapted from inner workings of Sema::BuildCallExpr TCppFunction_t - BestTemplateFunctionMatch(const std::vector& candidates, + BestOverloadFunctionMatch(const std::vector& candidates, const std::vector& explicit_types, const std::vector& arg_types) { + auto& S = getSema(); + auto& C = S.getASTContext(); - for (const auto& candidate : candidates) { - auto* TFD = (FunctionTemplateDecl*)candidate; - clang::TemplateParameterList* tpl = TFD->getTemplateParameters(); +#ifdef CPPINTEROP_USE_CLING + cling::Interpreter::PushTransactionRAII RAII(&getInterp()); +#endif - // template parameter size does not match - if (tpl->size() < explicit_types.size()) - continue; + // The overload resolution interfaces in Sema require a list of expressions. + // However, unlike handwritten C++, we do not always have a expression. + // Here we synthesize a placeholder expression to be able to use + // Sema::AddOverloadCandidate. Made up expressions are fine because the + // interface uses the list size and the expression types. + struct WrapperExpr : public OpaqueValueExpr { + WrapperExpr() : OpaqueValueExpr(clang::Stmt::EmptyShell()) {} + }; + auto* Exprs = new WrapperExpr[arg_types.size()]; + llvm::SmallVector Args; + Args.reserve(arg_types.size()); + size_t idx = 0; + for (auto i : arg_types) { + QualType Type = QualType::getFromOpaquePtr(i.m_Type); + ExprValueKind ExprKind = ExprValueKind::VK_PRValue; + if (Type->isReferenceType()) + ExprKind = ExprValueKind::VK_LValue; + + new (&Exprs[idx]) OpaqueValueExpr(SourceLocation::getFromRawEncoding(1), + Type.getNonReferenceType(), ExprKind); + Args.push_back(&Exprs[idx]); + ++idx; + } - // right now uninstantiated functions give template typenames instead of - // actual types. We make this match solely based on count + // Create a list of template arguments. + llvm::SmallVector TemplateArgs; + TemplateArgs.reserve(explicit_types.size()); + for (auto explicit_type : explicit_types) { + QualType ArgTy = QualType::getFromOpaquePtr(explicit_type.m_Type); + if (explicit_type.m_IntegralValue) { + // We have a non-type template parameter. Create an integral value from + // the string representation. + auto Res = llvm::APSInt(explicit_type.m_IntegralValue); + Res = Res.extOrTrunc(C.getIntWidth(ArgTy)); + TemplateArgs.push_back(TemplateArgument(C, Res, ArgTy)); + } else { + TemplateArgs.push_back(ArgTy); + } + } - const FunctionDecl* func = TFD->getTemplatedDecl(); + TemplateArgumentListInfo ExplicitTemplateArgs{}; + for (auto TA : TemplateArgs) + ExplicitTemplateArgs.addArgument( + S.getTrivialTemplateArgumentLoc(TA, QualType(), SourceLocation())); + + OverloadCandidateSet Overloads( + SourceLocation(), OverloadCandidateSet::CandidateSetKind::CSK_Normal); + + for (void* i : candidates) { + Decl* D = static_cast(i); + if (auto* FD = dyn_cast(D)) { + S.AddOverloadCandidate(FD, DeclAccessPair::make(FD, FD->getAccess()), + Args, Overloads); + } else if (auto* FTD = dyn_cast(D)) { + // AddTemplateOverloadCandidate is causing a memory leak + // It is a known bug at clang + // call stack: AddTemplateOverloadCandidate -> MakeDeductionFailureInfo + // source: + // https://github.com/llvm/llvm-project/blob/release/19.x/clang/lib/Sema/SemaOverload.cpp#L731-L756 + S.AddTemplateOverloadCandidate( + FTD, DeclAccessPair::make(FTD, FTD->getAccess()), + &ExplicitTemplateArgs, Args, Overloads); + } + } -#ifdef CPPINTEROP_USE_CLING - if (func->getNumParams() > arg_types.size()) - continue; -#else // CLANG_REPL - if (func->getMinRequiredArguments() > arg_types.size()) - continue; -#endif + OverloadCandidateSet::iterator Best; + Overloads.BestViableFunction(S, SourceLocation(), Best); - // TODO(aaronj0) : first score based on the type similarity before forcing - // instantiation. - - 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; - - // join explicit and arg_types - std::vector total_arg_set; - total_arg_set.reserve(explicit_types.size() + arg_types.size()); - total_arg_set.insert(total_arg_set.end(), explicit_types.begin(), - explicit_types.end()); - total_arg_set.insert(total_arg_set.end(), arg_types.begin(), - arg_types.end()); - - instantiated = InstantiateTemplate(candidate, total_arg_set.data(), - total_arg_set.size()); - if (instantiated) - return instantiated; - } - return nullptr; + FunctionDecl* Result = Best != Overloads.end() ? Best->Function : nullptr; + delete[] Exprs; + return Result; } // Gets the AccessSpecifier of the function and checks if it is equal to @@ -1493,6 +1549,30 @@ namespace Cpp { return QT.isPODType(getASTContext()); } + bool IsPointerType(TCppType_t type) { + QualType QT = QualType::getFromOpaquePtr(type); + return QT->isPointerType(); + } + + TCppType_t GetPointeeType(TCppType_t type) { + if (!IsPointerType(type)) + return nullptr; + QualType QT = QualType::getFromOpaquePtr(type); + return QT->getPointeeType().getAsOpaquePtr(); + } + + bool IsReferenceType(TCppType_t type) { + QualType QT = QualType::getFromOpaquePtr(type); + return QT->isReferenceType(); + } + + TCppType_t GetNonReferenceType(TCppType_t type) { + if (!IsReferenceType(type)) + return nullptr; + QualType QT = QualType::getFromOpaquePtr(type); + return QT.getNonReferenceType().getAsOpaquePtr(); + } + TCppType_t GetUnderlyingType(TCppType_t type) { QualType QT = QualType::getFromOpaquePtr(type); @@ -1862,10 +1942,22 @@ namespace Cpp { { std::string name; { - llvm::raw_string_ostream stream(name); + std::string complete_name; + llvm::raw_string_ostream stream(complete_name); FD->getNameForDiagnostic(stream, FD->getASTContext().getPrintingPolicy(), /*Qualified=*/false); + + // insert space between template argument list and the function name + // this is require if the function is `operator<` + // `operator<` is invalid syntax + // whereas `operator< ` is valid + std::string simple_name = FD->getNameAsString(); + size_t idx = complete_name.find(simple_name, 0) + simple_name.size(); + std::string name_without_template_args = complete_name.substr(0, idx); + std::string template_args = complete_name.substr(idx); + name = name_without_template_args + + (template_args.empty() ? "" : " " + template_args); } callbuf << name; } diff --git a/interpreter/CppInterOp/unittests/CppInterOp/FunctionReflectionTest.cpp b/interpreter/CppInterOp/unittests/CppInterOp/FunctionReflectionTest.cpp index 55263b791e4ad..feb028cea4888 100644 --- a/interpreter/CppInterOp/unittests/CppInterOp/FunctionReflectionTest.cpp +++ b/interpreter/CppInterOp/unittests/CppInterOp/FunctionReflectionTest.cpp @@ -313,6 +313,11 @@ TEST(FunctionReflectionTest, GetFunctionReturnType) { return sizeof(A) + i; } }; + + template struct RTTest_TemplatedList {}; + template auto rttest_make_tlist(T ... args) { + return RTTest_TemplatedList{}; + } )"; GetAllTopLevelDecls(code, Decls, true); @@ -348,6 +353,16 @@ TEST(FunctionReflectionTest, GetFunctionReturnType) { EXPECT_EQ( Cpp::GetTypeAsString(Cpp::GetFunctionReturnType(TemplateSubDecls[3])), "long"); + + ASTContext& C = Interp->getCI()->getASTContext(); + std::vector args = {C.IntTy.getAsOpaquePtr(), + C.DoubleTy.getAsOpaquePtr()}; + std::vector explicit_args; + std::vector candidates = {Decls[14]}; + EXPECT_EQ( + Cpp::GetTypeAsString(Cpp::GetFunctionReturnType( + Cpp::BestOverloadFunctionMatch(candidates, explicit_args, args))), + "RTTest_TemplatedList"); } TEST(FunctionReflectionTest, GetFunctionNumArgs) { @@ -590,7 +605,7 @@ TEST(FunctionReflectionTest, InstantiateTemplateMethod) { EXPECT_TRUE(TA1.getAsType()->isIntegerType()); } -TEST(FunctionReflectionTest, BestTemplateFunctionMatch) { +TEST(FunctionReflectionTest, BestOverloadFunctionMatch1) { std::vector Decls; std::string code = R"( class MyTemplatedMethodClass { @@ -598,7 +613,8 @@ TEST(FunctionReflectionTest, BestTemplateFunctionMatch) { template long get_size(A&); template long get_size(); template long get_size(A a, B b); - template long add_size(float a); + template long get_size(float a); + template long get_size(T a); }; template @@ -612,7 +628,7 @@ TEST(FunctionReflectionTest, BestTemplateFunctionMatch) { } template - long MyTemplatedMethodClass::add_size(float a) { + long MyTemplatedMethodClass::get_size(float a) { return sizeof(A) + long(a); } @@ -620,6 +636,11 @@ TEST(FunctionReflectionTest, BestTemplateFunctionMatch) { long MyTemplatedMethodClass::get_size(A a, B b) { return sizeof(A) + sizeof(B); } + + template + long MyTemplatedMethodClass::get_size(T a) { + return N + sizeof(T) + a; + } )"; GetAllTopLevelDecls(code, Decls); @@ -631,17 +652,26 @@ TEST(FunctionReflectionTest, BestTemplateFunctionMatch) { ASTContext& C = Interp->getCI()->getASTContext(); std::vector args0; - std::vector args1 = {C.IntTy.getAsOpaquePtr()}; + std::vector args1 = { + C.getLValueReferenceType(C.IntTy).getAsOpaquePtr()}; std::vector args2 = {C.CharTy.getAsOpaquePtr(), C.FloatTy.getAsOpaquePtr()}; std::vector args3 = {C.FloatTy.getAsOpaquePtr()}; std::vector explicit_args0; std::vector explicit_args1 = {C.IntTy.getAsOpaquePtr()}; - - Cpp::TCppFunction_t func1 = Cpp::BestTemplateFunctionMatch(candidates, explicit_args0, args1); - Cpp::TCppFunction_t func2 = Cpp::BestTemplateFunctionMatch(candidates, explicit_args1, args0); - Cpp::TCppFunction_t func3 = Cpp::BestTemplateFunctionMatch(candidates, explicit_args0, args2); - Cpp::TCppFunction_t func4 = Cpp::BestTemplateFunctionMatch(candidates, explicit_args1, args3); + std::vector explicit_args2 = { + {C.IntTy.getAsOpaquePtr(), "1"}, C.IntTy.getAsOpaquePtr()}; + + Cpp::TCppFunction_t func1 = + Cpp::BestOverloadFunctionMatch(candidates, explicit_args0, args1); + Cpp::TCppFunction_t func2 = + Cpp::BestOverloadFunctionMatch(candidates, explicit_args1, args0); + Cpp::TCppFunction_t func3 = + Cpp::BestOverloadFunctionMatch(candidates, explicit_args0, args2); + Cpp::TCppFunction_t func4 = + Cpp::BestOverloadFunctionMatch(candidates, explicit_args1, args3); + Cpp::TCppFunction_t func5 = + Cpp::BestOverloadFunctionMatch(candidates, explicit_args2, args3); EXPECT_EQ(Cpp::GetFunctionSignature(func1), "template<> long MyTemplatedMethodClass::get_size(int &)"); @@ -650,7 +680,228 @@ TEST(FunctionReflectionTest, BestTemplateFunctionMatch) { EXPECT_EQ(Cpp::GetFunctionSignature(func3), "template<> long MyTemplatedMethodClass::get_size(char a, float b)"); EXPECT_EQ(Cpp::GetFunctionSignature(func4), - "template<> long MyTemplatedMethodClass::get_size(float &)"); + "template<> long MyTemplatedMethodClass::get_size(float a)"); + EXPECT_EQ(Cpp::GetFunctionSignature(func5), + "template<> long MyTemplatedMethodClass::get_size<1, int>(int a)"); +} + +TEST(FunctionReflectionTest, BestOverloadFunctionMatch2) { + std::vector Decls; + std::string code = R"( + template + struct A { T value; }; + + A a; + + template + void somefunc(A arg) {} + + template + void somefunc(T arg) {} + + template + void somefunc(A arg1, A arg2) {} + + template + void somefunc(T arg1, T arg2) {} + + void somefunc(int arg1, double arg2) {} + )"; + + GetAllTopLevelDecls(code, Decls); + std::vector candidates; + + for (auto decl : Decls) + if (Cpp::IsFunction(decl) || Cpp::IsTemplatedFunction(decl)) + candidates.push_back((Cpp::TCppFunction_t)decl); + + EXPECT_EQ(candidates.size(), 5); + + ASTContext& C = Interp->getCI()->getASTContext(); + + std::vector args1 = {C.IntTy.getAsOpaquePtr()}; + std::vector args2 = { + Cpp::GetVariableType(Cpp::GetNamed("a"))}; + std::vector args3 = {C.IntTy.getAsOpaquePtr(), + C.IntTy.getAsOpaquePtr()}; + std::vector args4 = { + Cpp::GetVariableType(Cpp::GetNamed("a")), + Cpp::GetVariableType(Cpp::GetNamed("a"))}; + std::vector args5 = {C.IntTy.getAsOpaquePtr(), + C.DoubleTy.getAsOpaquePtr()}; + + std::vector explicit_args; + + Cpp::TCppFunction_t func1 = + Cpp::BestOverloadFunctionMatch(candidates, explicit_args, args1); + Cpp::TCppFunction_t func2 = + Cpp::BestOverloadFunctionMatch(candidates, explicit_args, args2); + Cpp::TCppFunction_t func3 = + Cpp::BestOverloadFunctionMatch(candidates, explicit_args, args3); + Cpp::TCppFunction_t func4 = + Cpp::BestOverloadFunctionMatch(candidates, explicit_args, args4); + Cpp::TCppFunction_t func5 = + Cpp::BestOverloadFunctionMatch(candidates, explicit_args, args5); + + EXPECT_EQ(Cpp::GetFunctionSignature(func1), + "template<> void somefunc(int arg)"); + EXPECT_EQ(Cpp::GetFunctionSignature(func2), + "template<> void somefunc(A arg)"); + EXPECT_EQ(Cpp::GetFunctionSignature(func3), + "template<> void somefunc(int arg1, int arg2)"); + EXPECT_EQ(Cpp::GetFunctionSignature(func4), + "template<> void somefunc(A arg1, A arg2)"); + EXPECT_EQ(Cpp::GetFunctionSignature(func5), + "void somefunc(int arg1, double arg2)"); +} + +TEST(FunctionReflectionTest, BestOverloadFunctionMatch3) { + std::vector Decls; + std::string code = R"( + template + struct A { + T value; + + template + A operator-(A rhs) { + return A{value - rhs.value}; + } + }; + + A a; + + template + A operator+(A lhs, A rhs) { + return A{lhs.value + rhs.value}; + } + + template + A operator+(A lhs, int rhs) { + return A{lhs.value + rhs}; + } + )"; + + GetAllTopLevelDecls(code, Decls); + std::vector candidates; + + for (auto decl : Decls) + if (Cpp::IsTemplatedFunction(decl)) + candidates.push_back((Cpp::TCppFunction_t)decl); + + EXPECT_EQ(candidates.size(), 2); + + ASTContext& C = Interp->getCI()->getASTContext(); + + std::vector args1 = { + Cpp::GetVariableType(Cpp::GetNamed("a")), + Cpp::GetVariableType(Cpp::GetNamed("a"))}; + std::vector args2 = { + Cpp::GetVariableType(Cpp::GetNamed("a")), C.IntTy.getAsOpaquePtr()}; + std::vector args3 = { + Cpp::GetVariableType(Cpp::GetNamed("a")), C.DoubleTy.getAsOpaquePtr()}; + + std::vector explicit_args; + + Cpp::TCppFunction_t func1 = + Cpp::BestOverloadFunctionMatch(candidates, explicit_args, args1); + Cpp::TCppFunction_t func2 = + Cpp::BestOverloadFunctionMatch(candidates, explicit_args, args2); + Cpp::TCppFunction_t func3 = + Cpp::BestOverloadFunctionMatch(candidates, explicit_args, args3); + + candidates.clear(); + Cpp::GetOperator( + Cpp::GetScopeFromType(Cpp::GetVariableType(Cpp::GetNamed("a"))), + Cpp::Operator::OP_Minus, candidates); + + EXPECT_EQ(candidates.size(), 1); + + std::vector args4 = { + Cpp::GetVariableType(Cpp::GetNamed("a"))}; + + Cpp::TCppFunction_t func4 = + Cpp::BestOverloadFunctionMatch(candidates, explicit_args, args4); + + EXPECT_EQ(Cpp::GetFunctionSignature(func1), + "template<> A operator+(A lhs, A rhs)"); + EXPECT_EQ(Cpp::GetFunctionSignature(func2), + "template<> A operator+(A lhs, int rhs)"); + EXPECT_EQ(Cpp::GetFunctionSignature(func3), + "template<> A operator+(A lhs, int rhs)"); + + EXPECT_EQ(Cpp::GetFunctionSignature(func4), + "template<> A A::operator-(A rhs)"); +} + +TEST(FunctionReflectionTest, BestOverloadFunctionMatch4) { + std::vector Decls, SubDecls; + std::string code = R"( + template + struct A { T value; }; + + class B { + public: + void fn() {} + template + void fn(T x) {} + template + void fn(A x) {} + template + void fn(A x, A y) {} + }; + + A a; + A b; + )"; + + GetAllTopLevelDecls(code, Decls); + GetAllSubDecls(Decls[1], SubDecls); + std::vector candidates; + for (auto i : SubDecls) { + if ((Cpp::IsFunction(i) || Cpp::IsTemplatedFunction(i)) && + Cpp::GetName(i) == "fn") + candidates.push_back(i); + } + + EXPECT_EQ(candidates.size(), 4); + + ASTContext& C = Interp->getCI()->getASTContext(); + + std::vector args1 = {}; + std::vector args2 = {C.IntTy.getAsOpaquePtr()}; + std::vector args3 = { + Cpp::GetVariableType(Cpp::GetNamed("a"))}; + std::vector args4 = { + Cpp::GetVariableType(Cpp::GetNamed("a")), + Cpp::GetVariableType(Cpp::GetNamed("b"))}; + std::vector args5 = { + Cpp::GetVariableType(Cpp::GetNamed("a")), + Cpp::GetVariableType(Cpp::GetNamed("a"))}; + + std::vector explicit_args1; + std::vector explicit_args2 = {C.IntTy.getAsOpaquePtr(), + C.IntTy.getAsOpaquePtr()}; + + Cpp::TCppFunction_t func1 = + Cpp::BestOverloadFunctionMatch(candidates, explicit_args1, args1); + Cpp::TCppFunction_t func2 = + Cpp::BestOverloadFunctionMatch(candidates, explicit_args1, args2); + Cpp::TCppFunction_t func3 = + Cpp::BestOverloadFunctionMatch(candidates, explicit_args1, args3); + Cpp::TCppFunction_t func4 = + Cpp::BestOverloadFunctionMatch(candidates, explicit_args1, args4); + Cpp::TCppFunction_t func5 = + Cpp::BestOverloadFunctionMatch(candidates, explicit_args2, args5); + + EXPECT_EQ(Cpp::GetFunctionSignature(func1), "void B::fn()"); + EXPECT_EQ(Cpp::GetFunctionSignature(func2), + "template<> void B::fn(int x)"); + EXPECT_EQ(Cpp::GetFunctionSignature(func3), + "template<> void B::fn(A x)"); + EXPECT_EQ(Cpp::GetFunctionSignature(func4), + "template<> void B::fn(A x, A y)"); + EXPECT_EQ(Cpp::GetFunctionSignature(func5), + "template<> void B::fn(A x, A y)"); } TEST(FunctionReflectionTest, IsPublicMethod) { @@ -1043,6 +1294,34 @@ TEST(FunctionReflectionTest, GetFunctionCallWrapper) { FCI_f.Invoke(&res, {nullptr, 0}); EXPECT_TRUE(res); #endif + + // templated operators + Interp->process(R"( + class TOperator{ + public: + template + bool operator<(T t) { return true; } + }; + )"); + Cpp::TCppScope_t TOperator = Cpp::GetNamed("TOperator"); + + auto* TOperatorCtor = Cpp::GetDefaultConstructor(TOperator); + auto FCI_TOperatorCtor = Cpp::MakeFunctionCallable(TOperatorCtor); + void* toperator = nullptr; + FCI_TOperatorCtor.Invoke((void*)&toperator); + + EXPECT_TRUE(toperator); + std::vector operators; + Cpp::GetOperator(TOperator, Cpp::OP_Less, operators); + EXPECT_EQ(operators.size(), 1); + + Cpp::TCppScope_t op_templated = operators[0]; + auto TAI = Cpp::TemplateArgInfo(Cpp::GetType("int")); + Cpp::TCppScope_t op = Cpp::InstantiateTemplate(op_templated, &TAI, 1); + auto FCI_op = Cpp::MakeFunctionCallable(op); + bool boolean = false; + FCI_op.Invoke((void*)&boolean, {args, /*args_size=*/1}, object); + EXPECT_TRUE(boolean); } TEST(FunctionReflectionTest, IsConstMethod) { diff --git a/interpreter/CppInterOp/unittests/CppInterOp/VariableReflectionTest.cpp b/interpreter/CppInterOp/unittests/CppInterOp/VariableReflectionTest.cpp index 4a0940848db4f..8b76e7f5cc9a4 100644 --- a/interpreter/CppInterOp/unittests/CppInterOp/VariableReflectionTest.cpp +++ b/interpreter/CppInterOp/unittests/CppInterOp/VariableReflectionTest.cpp @@ -600,3 +600,67 @@ TEST(VariableReflectionTest, GetEnumConstantDatamembers) { Cpp::GetEnumConstantDatamembers(MyEnumClass, datamembers2, false); EXPECT_EQ(datamembers2.size(), 6); } + +TEST(VariableReflectionTest, Is_Get_Pointer) { + Cpp::CreateInterpreter(); + std::vector Decls; + std::string code = R"( + class A {}; + int a; + int *b; + double c; + double *d; + A e; + A *f; + )"; + + GetAllTopLevelDecls(code, Decls); + + EXPECT_FALSE(Cpp::IsPointerType(Cpp::GetVariableType(Decls[1]))); + EXPECT_TRUE(Cpp::IsPointerType(Cpp::GetVariableType(Decls[2]))); + EXPECT_FALSE(Cpp::IsPointerType(Cpp::GetVariableType(Decls[3]))); + EXPECT_TRUE(Cpp::IsPointerType(Cpp::GetVariableType(Decls[4]))); + EXPECT_FALSE(Cpp::IsPointerType(Cpp::GetVariableType(Decls[5]))); + EXPECT_TRUE(Cpp::IsPointerType(Cpp::GetVariableType(Decls[6]))); + + EXPECT_EQ(Cpp::GetPointeeType(Cpp::GetVariableType(Decls[2])), + Cpp::GetVariableType(Decls[1])); + EXPECT_EQ(Cpp::GetPointeeType(Cpp::GetVariableType(Decls[4])), + Cpp::GetVariableType(Decls[3])); + EXPECT_EQ(Cpp::GetPointeeType(Cpp::GetVariableType(Decls[6])), + Cpp::GetVariableType(Decls[5])); + + EXPECT_FALSE(Cpp::GetPointeeType(Cpp::GetVariableType(Decls[5]))); +} + +TEST(VariableReflectionTest, Is_Get_Reference) { + Cpp::CreateInterpreter(); + std::vector Decls; + std::string code = R"( + class A {}; + int a; + int &b = a; + double c; + double &d = c; + A e; + A &f = e; + )"; + + GetAllTopLevelDecls(code, Decls); + + EXPECT_FALSE(Cpp::IsReferenceType(Cpp::GetVariableType(Decls[1]))); + EXPECT_TRUE(Cpp::IsReferenceType(Cpp::GetVariableType(Decls[2]))); + EXPECT_FALSE(Cpp::IsReferenceType(Cpp::GetVariableType(Decls[3]))); + EXPECT_TRUE(Cpp::IsReferenceType(Cpp::GetVariableType(Decls[4]))); + EXPECT_FALSE(Cpp::IsReferenceType(Cpp::GetVariableType(Decls[5]))); + EXPECT_TRUE(Cpp::IsReferenceType(Cpp::GetVariableType(Decls[6]))); + + EXPECT_EQ(Cpp::GetNonReferenceType(Cpp::GetVariableType(Decls[2])), + Cpp::GetVariableType(Decls[1])); + EXPECT_EQ(Cpp::GetNonReferenceType(Cpp::GetVariableType(Decls[4])), + Cpp::GetVariableType(Decls[3])); + EXPECT_EQ(Cpp::GetNonReferenceType(Cpp::GetVariableType(Decls[6])), + Cpp::GetVariableType(Decls[5])); + + EXPECT_FALSE(Cpp::GetNonReferenceType(Cpp::GetVariableType(Decls[5]))); +}