Skip to content

Commit

Permalink
Add interfaces enabling template function logic
Browse files Browse the repository at this point in the history
  • Loading branch information
aaronj0 committed Apr 3, 2024
1 parent 3025984 commit b8eb819
Show file tree
Hide file tree
Showing 2 changed files with 234 additions and 23 deletions.
21 changes: 19 additions & 2 deletions include/clang/Interpreter/CppInterOp.h
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,9 @@ namespace Cpp {
/// supplied as a parameter.
CPPINTEROP_API std::vector<TCppFunction_t> GetClassMethods(TCppScope_t klass);

///\returns Template function pointer list to add proxies for un-instantiated/non-overloaded templated methods
std::vector<TCppFunction_t> GetTemplatedFuncs(TCppScope_t klass);

///\returns if a class has a default constructor.
CPPINTEROP_API bool HasDefaultConstructor(TCppScope_t scope);

Expand Down Expand Up @@ -330,6 +333,15 @@ namespace Cpp {
/// templated function of that type.
CPPINTEROP_API bool ExistsFunctionTemplate(const std::string& name,
TCppScope_t parent = 0);

CPPINTEROP_API std::vector<TCppFunction_t> 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);
Expand Down Expand Up @@ -534,9 +546,10 @@ namespace Cpp {
TemplateArgInfo(TCppScope_t type, const char* integral_value = nullptr)
: m_Type(type), m_IntegralValue(integral_value) {}
};
/// Builds a template instantiation for a given templated declaration.
CPPINTEROP_API TCppScope_t
InstantiateClassTemplate(TCppScope_t tmpl, TemplateArgInfo* template_args,
size_t template_args_size);
InstantiateTemplate(TCppScope_t tmpl, TemplateArgInfo* template_args,
size_t template_args_size);

/// Returns the class template instantiation arguments of \c templ_instance.
CPPINTEROP_API void
Expand All @@ -549,6 +562,10 @@ 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<TCppFunction_t> candidates,
std::vector<TemplateArgInfo> explicit_types, std::vector<TemplateArgInfo> arg_types);

CPPINTEROP_API std::vector<std::string> GetAllCppNames(TCppScope_t scope);

CPPINTEROP_API void DumpScope(TCppScope_t scope);
Expand Down
236 changes: 215 additions & 21 deletions lib/Interpreter/CppInterOp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Sema.h"
#include "clang/Sema/TemplateDeduction.h"

#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Casting.h"
Expand Down Expand Up @@ -745,6 +746,33 @@ namespace Cpp {
return methods;
}

// FIXME: We should make the std::vector<TCppFunction_t> an out parameter to
// avoid copies.
std::vector<TCppFunction_t> GetTemplatedFuncs(TCppScope_t klass)
{

if (!klass)
return {};

auto *D = (clang::Decl *) klass;

if (auto *TD = dyn_cast<TypedefNameDecl>(D))
D = GetScopeFromType(TD->getUnderlyingType());

std::vector<TCppFunction_t> methods;
if (auto *CXXRD = dyn_cast_or_null<CXXRecordDecl>(D)) {
getSema().ForceDeclarationOfImplicitMembers(CXXRD);
for (Decl* DI : CXXRD->decls()) {
if (auto* MD = dyn_cast<FunctionTemplateDecl>(DI))
methods.push_back(MD);
else if (auto* USD = dyn_cast<UsingShadowDecl>(DI))
if (auto* MD = dyn_cast<FunctionTemplateDecl>(USD->getTargetDecl()))
methods.push_back(MD);
}
}
return methods;
}

bool HasDefaultConstructor(TCppScope_t scope) {
auto *D = (clang::Decl *) scope;

Expand Down Expand Up @@ -821,6 +849,10 @@ namespace Cpp {
return FD->getReturnType().getAsOpaquePtr();
}

else if (auto *FD = llvm::dyn_cast_or_null<clang::FunctionTemplateDecl>(D)) {
return (FD->getTemplatedDecl())->getReturnType().getAsOpaquePtr();
}

return 0;
}

Expand All @@ -830,6 +862,10 @@ namespace Cpp {
if (auto *FD = llvm::dyn_cast_or_null<FunctionDecl>(D)) {
return FD->getNumParams();
}

else if (auto *FD = llvm::dyn_cast_or_null<clang::FunctionTemplateDecl>(D)) {
return (FD->getTemplatedDecl())->getNumParams();
}
return 0;
}

Expand All @@ -839,6 +875,10 @@ namespace Cpp {
if (auto *FD = llvm::dyn_cast_or_null<FunctionDecl> (D)) {
return FD->getMinRequiredArguments();
}

else if (auto *FD = llvm::dyn_cast_or_null<clang::FunctionTemplateDecl>(D)) {
return (FD->getTemplatedDecl())->getMinRequiredArguments();
}
return 0;
}

Expand All @@ -857,7 +897,7 @@ namespace Cpp {
}

std::string GetFunctionSignature(TCppFunction_t func)
{
{ // FIXME : Doesn't work for template functions as it does in cling
if (!func)
return "<unknown>";

Expand All @@ -874,6 +914,19 @@ namespace Cpp {
SS.flush();
return Signature;
}

// FIXME: else if (auto *FD = llvm::dyn_cast<FunctionTemplateDecl>(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 "<unknown>";
}

Expand Down Expand Up @@ -930,6 +983,118 @@ namespace Cpp {
return true;
}

std::vector<TCppFunction_t> 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<TCppFunction_t> 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<FunctionTemplateDecl>(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<TCppFunction_t> 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<TCppFunction_t> mc = GetTemplatedFuncs(scope);
auto *D = (FunctionTemplateDecl *) mc[imeth];

return D->getNameAsString();
}

TCppFunction_t BestTemplateFunctionMatch(std::vector<TCppFunction_t> candidates,
std::vector<TemplateArgInfo> explicit_types, std::vector<TemplateArgInfo> 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 if the function has not been instantiated these will give types like "A" or "B" that can take any 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)
Expand Down Expand Up @@ -2768,18 +2933,36 @@ namespace Cpp {
return getInterp().toString(type, obj);
}

static QualType InstantiateTemplate(TemplateDecl* ClassDecl,
TemplateArgumentListInfo& TLI, Sema &S) {
// This will instantiate tape<T> type and return it.
SourceLocation noLoc;
QualType TT = S.CheckTemplateIdType(TemplateName(ClassDecl), noLoc, TLI);

static Decl* InstantiateTemplate(TemplateDecl* TemplateD,
TemplateArgumentListInfo& TLI, Sema& S) {
// This is not right but we don't have a lot of options to choose from as a
// template instantiation requires a valid source location.
SourceLocation fakeLoc = GetValidSLoc(S);
if (auto* FunctionTemplate = dyn_cast<FunctionTemplateDecl>(TemplateD)) {
FunctionDecl* Specialization = nullptr;
clang::sema::TemplateDeductionInfo Info(fakeLoc);
if (Sema::TemplateDeductionResult Result = S.DeduceTemplateArguments(
FunctionTemplate, &TLI, Specialization, Info,
/*IsAddressOfFunction*/ true)) {
// FIXME: Diagnose what happened.
(void)Result;
}
return Specialization;
} else if (auto* VarTemplate = dyn_cast<VarTemplateDecl>(TemplateD)) {
DeclResult R = S.CheckVarTemplateId(VarTemplate, fakeLoc, fakeLoc, TLI);
if (R.isInvalid()) {
// FIXME: Diagnose
}
return R.get();
}

// This will instantiate tape<T> type and return it.
SourceLocation noLoc;
QualType TT = S.CheckTemplateIdType(TemplateName(TemplateD), noLoc, TLI);

// Perhaps we can extract this into a new interface.
S.RequireCompleteType(fakeLoc, TT, diag::err_tentative_def_incomplete_type);
return TT;
return GetScopeFromType(TT);

// ASTContext &C = S.getASTContext();
// // Get clad namespace and its identifier clad::.
Expand All @@ -2792,21 +2975,21 @@ namespace Cpp {
// return C.getElaboratedType(ETK_None, NS, TT);
}

static QualType InstantiateTemplate(TemplateDecl* ClassDecl,
ArrayRef<TemplateArgument> TemplateArgs,
Sema &S) {
static Decl* InstantiateTemplate(TemplateDecl* TemplateD,
ArrayRef<TemplateArgument> TemplateArgs,
Sema& S) {
// Create a list of template arguments.
TemplateArgumentListInfo TLI{};
for (auto TA : TemplateArgs)
TLI.addArgument(S.getTrivialTemplateArgumentLoc(TA,QualType(),
SourceLocation()));

return InstantiateTemplate(ClassDecl, TLI, S);
return InstantiateTemplate(TemplateD, TLI, S);
}

TCppScope_t InstantiateClassTemplate(TCppScope_t tmpl,
TemplateArgInfo* template_args,
size_t template_args_size) {
TCppScope_t InstantiateTemplate(TCppScope_t tmpl,
TemplateArgInfo* template_args,
size_t template_args_size) {
ASTContext &C = getASTContext();

llvm::SmallVector<TemplateArgument> TemplateArgs;
Expand All @@ -2830,8 +3013,7 @@ namespace Cpp {
#ifdef USE_CLING
cling::Interpreter::PushTransactionRAII RAII(&getInterp());
#endif
QualType Instance = InstantiateTemplate(TmplD, TemplateArgs, getSema());
return GetScopeFromType(Instance);
return InstantiateTemplate(TmplD, TemplateArgs, getSema());
}

void GetClassTemplateInstantiationArgs(TCppScope_t templ_instance,
Expand Down Expand Up @@ -2975,8 +3157,14 @@ namespace Cpp {
std::string GetFunctionArgDefault(TCppFunction_t func,
TCppIndex_t param_index) {
auto *D = (clang::Decl *)func;
auto *FD = llvm::dyn_cast_or_null<clang::FunctionDecl>(D);
auto PI = FD->getParamDecl(param_index);
clang::ParmVarDecl *PI;

if (auto *FD = llvm::dyn_cast_or_null<clang::FunctionDecl>(D)) {
PI = FD->getParamDecl(param_index);
}
else if (auto *FD = llvm::dyn_cast_or_null<clang::FunctionTemplateDecl>(D)) {
PI = (FD->getTemplatedDecl())->getParamDecl(param_index);
}

if (PI->hasDefaultArg())
{
Expand Down Expand Up @@ -3017,8 +3205,14 @@ namespace Cpp {
std::string GetFunctionArgName(TCppFunction_t func, TCppIndex_t param_index)
{
auto *D = (clang::Decl *)func;
auto *FD = llvm::cast<clang::FunctionDecl>(D);
auto PI = FD->getParamDecl(param_index);
clang::ParmVarDecl *PI;

if (auto *FD = llvm::dyn_cast_or_null<clang::FunctionDecl>(D)) {
PI = FD->getParamDecl(param_index);
}
else if (auto *FD = llvm::dyn_cast_or_null<clang::FunctionTemplateDecl>(D)) {
PI = (FD->getTemplatedDecl())->getParamDecl(param_index);
}

return PI->getNameAsString();
}
Expand Down

0 comments on commit b8eb819

Please sign in to comment.