Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve template instantiate #511

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions include/clang/Interpreter/CppInterOp.h
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,15 @@
const char* m_IntegralValue;
TemplateArgInfo(TCppScope_t type, const char* integral_value = nullptr)
: m_Type(type), m_IntegralValue(integral_value) {}
friend bool operator==(const TemplateArgInfo& lhs,

Check warning on line 679 in include/clang/Interpreter/CppInterOp.h

View check run for this annotation

Codecov / codecov/patch

include/clang/Interpreter/CppInterOp.h#L679

Added line #L679 was not covered by tests
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why having a friend operator here?

const TemplateArgInfo& rhs) {
return (lhs.m_Type == rhs.m_Type &&
lhs.m_IntegralValue == rhs.m_IntegralValue);

Check warning on line 682 in include/clang/Interpreter/CppInterOp.h

View check run for this annotation

Codecov / codecov/patch

include/clang/Interpreter/CppInterOp.h#L681-L682

Added lines #L681 - L682 were not covered by tests
}
friend bool operator!=(const TemplateArgInfo& lhs,
const TemplateArgInfo& rhs) {
return !(lhs == rhs);
}
};
/// Builds a template instantiation for a given templated declaration.
/// Offers a single interface for instantiation of class, function and
Expand Down
104 changes: 104 additions & 0 deletions lib/Interpreter/CppInterOp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "clang/AST/CXXInheritance.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/GlobalDecl.h"
#include "clang/AST/Mangle.h"
#include "clang/AST/QualTypeNames.h"
Expand Down Expand Up @@ -42,6 +43,7 @@
#include <set>
#include <sstream>
#include <string>
#include <vector>

// Stream redirect.
#ifdef _WIN32
Expand Down Expand Up @@ -1026,11 +1028,103 @@
funcs.push_back(Found);
}

namespace {
inline void
collectUniqueTemplateArgs(const std::vector<TemplateArgInfo>& templ_types,
std::vector<TemplateArgInfo>& result) {
std::unique_copy(templ_types.begin(), templ_types.end(),
std::back_inserter(result));
}
bool
IsTemplateFunctionGoodMatch(const FunctionTemplateDecl* FTD,
const std::vector<TemplateArgInfo>& arg_types,
std::vector<TemplateArgInfo>& templ_types) {
const FunctionDecl* F = FTD->getTemplatedDecl();
clang::TemplateParameterList* tpl = FTD->getTemplateParameters();

if (arg_types.size() != F->getNumParams())
return false;

for (size_t i = 0; i < arg_types.size(); i++) {
QualType fn_arg_type = F->getParamDecl(i)->getType();
QualType arg_type = QualType::getFromOpaquePtr(arg_types[i].m_Type);

// dereference
if (fn_arg_type->isReferenceType())
fn_arg_type = fn_arg_type.getNonReferenceType();
if (arg_type->isReferenceType())
arg_type = arg_type.getNonReferenceType();

Check warning on line 1056 in lib/Interpreter/CppInterOp.cpp

View check run for this annotation

Codecov / codecov/patch

lib/Interpreter/CppInterOp.cpp#L1056

Added line #L1056 was not covered by tests

fn_arg_type = fn_arg_type.getCanonicalType();
arg_type = arg_type.getCanonicalType();

// matching parameter and argument types
// resolving parameter
const auto* fn_TST =
fn_arg_type->getAs<clang::TemplateSpecializationType>();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: no header providing "clang::TemplateSpecializationType" is directly included [misc-include-cleaner]

          fn_arg_type->getAs<clang::TemplateSpecializationType>();
                                    ^

const TemplateDecl* fn_TD = nullptr;
if (fn_TST)
fn_TD = fn_TST->getTemplateName().getAsTemplateDecl();

// resolving argument
const auto* arg_RT = arg_type->getAs<clang::RecordType>();
ClassTemplateSpecializationDecl* arg_CTSD = nullptr;
if (arg_RT)
arg_CTSD = llvm::dyn_cast<clang::ClassTemplateSpecializationDecl>(
arg_RT->getDecl());

if ((!arg_CTSD || !fn_TD) && (arg_CTSD || fn_TD))
return false;

// check if types match
if (arg_CTSD) {
auto* arg_D = arg_CTSD->getSpecializedTemplate()->getCanonicalDecl();
if (arg_D != fn_TD->getCanonicalDecl())
return false;

Check warning on line 1083 in lib/Interpreter/CppInterOp.cpp

View check run for this annotation

Codecov / codecov/patch

lib/Interpreter/CppInterOp.cpp#L1083

Added line #L1083 was not covered by tests
if (templ_types.size() < tpl->size()) {
Cpp::GetClassTemplateInstantiationArgs(arg_CTSD, templ_types);
break;
}
} else if (templ_types.size() < tpl->size()) {
templ_types.push_back(arg_types[i]);
}
}
return true;
}
} // namespace

TCppFunction_t
BestTemplateFunctionMatch(const std::vector<TCppFunction_t>& candidates,
const std::vector<TemplateArgInfo>& explicit_types,
const std::vector<TemplateArgInfo>& arg_types) {

/*
Try matching function with templated class as arguments first
Example:

template<typename T>
struct A { T value; };

template<typename T>
void somefunc(A<T> arg); // overload 1

template<typename T>
void somefunc(T arg); // overload 2

somefunc(A<int>()); // should call overload 1; resolve this first
somefunc(3); // should call overload 2
*/
for (const auto& candidate : candidates) {
std::vector<TemplateArgInfo> templ_types;
auto* TFD = static_cast<FunctionTemplateDecl*>(candidate);
if (IsTemplateFunctionGoodMatch(TFD, arg_types, templ_types)) {
TCppFunction_t instantiated = InstantiateTemplate(
candidate, templ_types.data(), templ_types.size());
if (instantiated)
return instantiated;
}
}

for (const auto& candidate : candidates) {
auto* TFD = (FunctionTemplateDecl*)candidate;
clang::TemplateParameterList* tpl = TFD->getTemplateParameters();
Expand Down Expand Up @@ -1060,9 +1154,19 @@
if (instantiated)
return instantiated;

std::vector<TemplateArgInfo> unique_arg_types;
collectUniqueTemplateArgs(arg_types, unique_arg_types);
instantiated = InstantiateTemplate(candidate, unique_arg_types.data(),
unique_arg_types.size());
if (instantiated)
return instantiated;

Check warning on line 1162 in lib/Interpreter/CppInterOp.cpp

View check run for this annotation

Codecov / codecov/patch

lib/Interpreter/CppInterOp.cpp#L1162

Added line #L1162 was not covered by tests

// Force the instantiation with template params in case of no args
// maybe steer instantiation better with arg set returned from
// TemplateProxy?
if (explicit_types.empty())
continue;

Check warning on line 1168 in lib/Interpreter/CppInterOp.cpp

View check run for this annotation

Codecov / codecov/patch

lib/Interpreter/CppInterOp.cpp#L1168

Added line #L1168 was not covered by tests

instantiated = InstantiateTemplate(candidate, explicit_types.data(),
explicit_types.size());
if (instantiated)
Expand Down
62 changes: 62 additions & 0 deletions unittests/CppInterOp/FunctionReflectionTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,68 @@ TEST(FunctionReflectionTest, BestTemplateFunctionMatch) {
"template<> long MyTemplatedMethodClass::get_size<float>(float &)");
}

TEST(FunctionReflectionTest, BestTemplateFunctionMatch2) {
std::vector<Decl*> Decls;
std::string code = R"(
template<typename T>
struct A { T value; };

A<int> a;

template<typename T>
void somefunc(A<T> arg) {}

template<typename T>
void somefunc(T arg) {}

template<typename T>
void somefunc(A<T> arg1, A<T> arg2) {}

template<typename T>
void somefunc(T arg1, T arg2) {}
)";

GetAllTopLevelDecls(code, Decls);
std::vector<Cpp::TCppFunction_t> candidates;

for (auto decl : Decls)
if (Cpp::IsTemplatedFunction(decl))
candidates.push_back((Cpp::TCppFunction_t)decl);

EXPECT_EQ(candidates.size(), 4);

ASTContext& C = Interp->getCI()->getASTContext();

std::vector<Cpp::TemplateArgInfo> args1 = {C.IntTy.getAsOpaquePtr()};
std::vector<Cpp::TemplateArgInfo> args2 = {
Cpp::GetVariableType(Cpp::GetNamed("a"))};
std::vector<Cpp::TemplateArgInfo> args3 = {C.IntTy.getAsOpaquePtr(),
C.IntTy.getAsOpaquePtr()};
std::vector<Cpp::TemplateArgInfo> args4 = {
Cpp::GetVariableType(Cpp::GetNamed("a")),
Cpp::GetVariableType(Cpp::GetNamed("a"))};

std::vector<Cpp::TemplateArgInfo> explicit_args;

Cpp::TCppFunction_t func1 =
Cpp::BestTemplateFunctionMatch(candidates, explicit_args, args1);
Cpp::TCppFunction_t func2 =
Cpp::BestTemplateFunctionMatch(candidates, explicit_args, args2);
Cpp::TCppFunction_t func3 =
Cpp::BestTemplateFunctionMatch(candidates, explicit_args, args3);
Cpp::TCppFunction_t func4 =
Cpp::BestTemplateFunctionMatch(candidates, explicit_args, args4);

EXPECT_EQ(Cpp::GetFunctionSignature(func1),
"template<> void somefunc<int>(int arg)");
EXPECT_EQ(Cpp::GetFunctionSignature(func2),
"template<> void somefunc<int>(A<int> arg)");
EXPECT_EQ(Cpp::GetFunctionSignature(func3),
"template<> void somefunc<int>(int arg1, int arg2)");
EXPECT_EQ(Cpp::GetFunctionSignature(func4),
"template<> void somefunc<int>(A<int> arg1, A<int> arg2)");
}

TEST(FunctionReflectionTest, IsPublicMethod) {
std::vector<Decl *> Decls, SubDecls;
std::string code = R"(
Expand Down
Loading