Skip to content

Commit 59b7c11

Browse files
Add BestOverloadFunctionMatch & IsFunction Remove BestTemplateFunctionMatch (#514)
* Add `BestOverloadFunctionMatch` & `IsFunction` Remove `BestTemplateFunctionMatch` Resolve and instantiate (if templated) overload from vector of overloads, for the given template parameter types and argument types With this changes we replicate clang's overload resolution and template instantiation * add test cases * skip using specilised function for methods Using `AddOverloadCandidate` over `AddMethodCandidate` and `AddTemplateOverloadCandidate` over `AddMethodTemplateCandidate` * brief memory leak at clang * avoid variable length array
1 parent 16c67f9 commit 59b7c11

File tree

3 files changed

+374
-65
lines changed

3 files changed

+374
-65
lines changed

include/clang/Interpreter/CppInterOp.h

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,9 @@ namespace Cpp {
210210
/// Checks if the scope is a class or not.
211211
CPPINTEROP_API bool IsClass(TCppScope_t scope);
212212

213+
/// Checks if the scope is a function.
214+
CPPINTEROP_API bool IsFunction(TCppScope_t scope);
215+
213216
/// Checks if the type is a function pointer.
214217
CPPINTEROP_API bool IsFunctionPointerType(TCppType_t type);
215218

@@ -706,17 +709,16 @@ namespace Cpp {
706709
CPPINTEROP_API TCppFunction_t
707710
InstantiateTemplateFunctionFromString(const char* function_template);
708711

709-
/// Finds best template match based on explicit template parameters and
710-
/// argument types
712+
/// Finds best overload match based on explicit template parameters (if any)
713+
/// and argument types.
711714
///
712-
///\param[in] candidates - Vector of suitable candidates that come under the
713-
/// parent scope and have the same name (obtained using
714-
/// GetClassTemplatedMethods)
715+
///\param[in] candidates - vector of overloads that come under the
716+
/// parent scope and have the same name
715717
///\param[in] explicit_types - set of expicitly instantiated template types
716718
///\param[in] arg_types - set of argument types
717719
///\returns Instantiated function pointer
718720
CPPINTEROP_API TCppFunction_t
719-
BestTemplateFunctionMatch(const std::vector<TCppFunction_t>& candidates,
721+
BestOverloadFunctionMatch(const std::vector<TCppFunction_t>& candidates,
720722
const std::vector<TemplateArgInfo>& explicit_types,
721723
const std::vector<TemplateArgInfo>& arg_types);
722724

lib/Interpreter/CppInterOp.cpp

Lines changed: 105 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,29 @@
1313

1414
#include "clang/AST/CXXInheritance.h"
1515
#include "clang/AST/Decl.h"
16+
#include "clang/AST/DeclAccessPair.h"
17+
#include "clang/AST/DeclBase.h"
1618
#include "clang/AST/DeclCXX.h"
19+
#include "clang/AST/DeclarationName.h"
20+
#include "clang/AST/Expr.h"
21+
#include "clang/AST/ExprCXX.h"
1722
#include "clang/AST/GlobalDecl.h"
1823
#include "clang/AST/Mangle.h"
24+
#include "clang/AST/NestedNameSpecifier.h"
1925
#include "clang/AST/QualTypeNames.h"
2026
#include "clang/AST/RecordLayout.h"
27+
#include "clang/AST/Stmt.h"
28+
#include "clang/AST/Type.h"
2129
#include "clang/Basic/DiagnosticSema.h"
2230
#include "clang/Basic/Linkage.h"
31+
#include "clang/Basic/OperatorKinds.h"
32+
#include "clang/Basic/SourceLocation.h"
33+
#include "clang/Basic/Specifiers.h"
2334
#include "clang/Basic/Version.h"
2435
#include "clang/Frontend/CompilerInstance.h"
2536
#include "clang/Sema/Lookup.h"
37+
#include "clang/Sema/Overload.h"
38+
#include "clang/Sema/Ownership.h"
2639
#include "clang/Sema/Sema.h"
2740
#if CLANG_VERSION_MAJOR >= 19
2841
#include "clang/Sema/Redeclaration.h"
@@ -210,6 +223,11 @@ namespace Cpp {
210223
return isa<CXXRecordDecl>(D);
211224
}
212225

226+
bool IsFunction(TCppScope_t scope) {
227+
Decl* D = static_cast<Decl*>(scope);
228+
return isa<FunctionDecl>(D);
229+
}
230+
213231
bool IsFunctionPointerType(TCppType_t type) {
214232
QualType QT = QualType::getFromOpaquePtr(type);
215233
return QT->isFunctionPointerType();
@@ -877,8 +895,19 @@ namespace Cpp {
877895
TCppType_t GetFunctionReturnType(TCppFunction_t func)
878896
{
879897
auto *D = (clang::Decl *) func;
880-
if (auto* FD = llvm::dyn_cast_or_null<clang::FunctionDecl>(D))
881-
return FD->getReturnType().getAsOpaquePtr();
898+
if (auto* FD = llvm::dyn_cast_or_null<clang::FunctionDecl>(D)) {
899+
QualType Type = FD->getReturnType();
900+
if (Type->isUndeducedAutoType() && IsTemplatedFunction(FD) &&
901+
!FD->isDefined()) {
902+
#ifdef CPPINTEROP_USE_CLING
903+
cling::Interpreter::PushTransactionRAII RAII(&getInterp());
904+
#endif
905+
getSema().InstantiateFunctionDefinition(SourceLocation(), FD, true,
906+
true);
907+
Type = FD->getReturnType();
908+
}
909+
return Type.getAsOpaquePtr();
910+
}
882911

883912
if (auto* FD = llvm::dyn_cast_or_null<clang::FunctionTemplateDecl>(D))
884913
return (FD->getTemplatedDecl())->getReturnType().getAsOpaquePtr();
@@ -1026,62 +1055,89 @@ namespace Cpp {
10261055
funcs.push_back(Found);
10271056
}
10281057

1058+
// Adapted from inner workings of Sema::BuildCallExpr
10291059
TCppFunction_t
1030-
BestTemplateFunctionMatch(const std::vector<TCppFunction_t>& candidates,
1060+
BestOverloadFunctionMatch(const std::vector<TCppFunction_t>& candidates,
10311061
const std::vector<TemplateArgInfo>& explicit_types,
10321062
const std::vector<TemplateArgInfo>& arg_types) {
1063+
auto& S = getSema();
1064+
auto& C = S.getASTContext();
10331065

1034-
for (const auto& candidate : candidates) {
1035-
auto* TFD = (FunctionTemplateDecl*)candidate;
1036-
clang::TemplateParameterList* tpl = TFD->getTemplateParameters();
1066+
#ifdef CPPINTEROP_USE_CLING
1067+
cling::Interpreter::PushTransactionRAII RAII(&getInterp());
1068+
#endif
10371069

1038-
// template parameter size does not match
1039-
if (tpl->size() < explicit_types.size())
1040-
continue;
1070+
// The overload resolution interfaces in Sema require a list of expressions.
1071+
// However, unlike handwritten C++, we do not always have a expression.
1072+
// Here we synthesize a placeholder expression to be able to use
1073+
// Sema::AddOverloadCandidate. Made up expressions are fine because the
1074+
// interface uses the list size and the expression types.
1075+
struct WrapperExpr : public OpaqueValueExpr {
1076+
WrapperExpr() : OpaqueValueExpr(clang::Stmt::EmptyShell()) {}
1077+
};
1078+
auto* Exprs = new WrapperExpr[arg_types.size()];
1079+
llvm::SmallVector<Expr*> Args;
1080+
Args.reserve(arg_types.size());
1081+
size_t idx = 0;
1082+
for (auto i : arg_types) {
1083+
QualType Type = QualType::getFromOpaquePtr(i.m_Type);
1084+
ExprValueKind ExprKind = ExprValueKind::VK_PRValue;
1085+
if (Type->isReferenceType())
1086+
ExprKind = ExprValueKind::VK_LValue;
1087+
1088+
new (&Exprs[idx]) OpaqueValueExpr(SourceLocation::getFromRawEncoding(1),
1089+
Type.getNonReferenceType(), ExprKind);
1090+
Args.push_back(&Exprs[idx]);
1091+
++idx;
1092+
}
10411093

1042-
// right now uninstantiated functions give template typenames instead of
1043-
// actual types. We make this match solely based on count
1094+
// Create a list of template arguments.
1095+
llvm::SmallVector<TemplateArgument> TemplateArgs;
1096+
TemplateArgs.reserve(explicit_types.size());
1097+
for (auto explicit_type : explicit_types) {
1098+
QualType ArgTy = QualType::getFromOpaquePtr(explicit_type.m_Type);
1099+
if (explicit_type.m_IntegralValue) {
1100+
// We have a non-type template parameter. Create an integral value from
1101+
// the string representation.
1102+
auto Res = llvm::APSInt(explicit_type.m_IntegralValue);
1103+
Res = Res.extOrTrunc(C.getIntWidth(ArgTy));
1104+
TemplateArgs.push_back(TemplateArgument(C, Res, ArgTy));
1105+
} else {
1106+
TemplateArgs.push_back(ArgTy);
1107+
}
1108+
}
10441109

1045-
const FunctionDecl* func = TFD->getTemplatedDecl();
1110+
TemplateArgumentListInfo ExplicitTemplateArgs{};
1111+
for (auto TA : TemplateArgs)
1112+
ExplicitTemplateArgs.addArgument(
1113+
S.getTrivialTemplateArgumentLoc(TA, QualType(), SourceLocation()));
1114+
1115+
OverloadCandidateSet Overloads(
1116+
SourceLocation(), OverloadCandidateSet::CandidateSetKind::CSK_Normal);
1117+
1118+
for (void* i : candidates) {
1119+
Decl* D = static_cast<Decl*>(i);
1120+
if (auto* FD = dyn_cast<FunctionDecl>(D)) {
1121+
S.AddOverloadCandidate(FD, DeclAccessPair::make(FD, FD->getAccess()),
1122+
Args, Overloads);
1123+
} else if (auto* FTD = dyn_cast<FunctionTemplateDecl>(D)) {
1124+
// AddTemplateOverloadCandidate is causing a memory leak
1125+
// It is a known bug at clang
1126+
// call stack: AddTemplateOverloadCandidate -> MakeDeductionFailureInfo
1127+
// source:
1128+
// https://github.com/llvm/llvm-project/blob/release/19.x/clang/lib/Sema/SemaOverload.cpp#L731-L756
1129+
S.AddTemplateOverloadCandidate(
1130+
FTD, DeclAccessPair::make(FTD, FTD->getAccess()),
1131+
&ExplicitTemplateArgs, Args, Overloads);
1132+
}
1133+
}
10461134

1047-
#ifdef CPPINTEROP_USE_CLING
1048-
if (func->getNumParams() > arg_types.size())
1049-
continue;
1050-
#else // CLANG_REPL
1051-
if (func->getMinRequiredArguments() > arg_types.size())
1052-
continue;
1053-
#endif
1135+
OverloadCandidateSet::iterator Best;
1136+
Overloads.BestViableFunction(S, SourceLocation(), Best);
10541137

1055-
// TODO(aaronj0) : first score based on the type similarity before forcing
1056-
// instantiation.
1057-
1058-
TCppFunction_t instantiated =
1059-
InstantiateTemplate(candidate, arg_types.data(), arg_types.size());
1060-
if (instantiated)
1061-
return instantiated;
1062-
1063-
// Force the instantiation with template params in case of no args
1064-
// maybe steer instantiation better with arg set returned from
1065-
// TemplateProxy?
1066-
instantiated = InstantiateTemplate(candidate, explicit_types.data(),
1067-
explicit_types.size());
1068-
if (instantiated)
1069-
return instantiated;
1070-
1071-
// join explicit and arg_types
1072-
std::vector<TemplateArgInfo> total_arg_set;
1073-
total_arg_set.reserve(explicit_types.size() + arg_types.size());
1074-
total_arg_set.insert(total_arg_set.end(), explicit_types.begin(),
1075-
explicit_types.end());
1076-
total_arg_set.insert(total_arg_set.end(), arg_types.begin(),
1077-
arg_types.end());
1078-
1079-
instantiated = InstantiateTemplate(candidate, total_arg_set.data(),
1080-
total_arg_set.size());
1081-
if (instantiated)
1082-
return instantiated;
1083-
}
1084-
return nullptr;
1138+
FunctionDecl* Result = Best != Overloads.end() ? Best->Function : nullptr;
1139+
delete[] Exprs;
1140+
return Result;
10851141
}
10861142

10871143
// Gets the AccessSpecifier of the function and checks if it is equal to

0 commit comments

Comments
 (0)