Skip to content

Add full member function support in BestOverloadFunctionMatch#802

Open
Priyanshu3820 wants to merge 15 commits intocompiler-research:mainfrom
Priyanshu3820:main
Open

Add full member function support in BestOverloadFunctionMatch#802
Priyanshu3820 wants to merge 15 commits intocompiler-research:mainfrom
Priyanshu3820:main

Conversation

@Priyanshu3820
Copy link

Description

This PR enhances BestOverloadFunctionMatch to properly handle const-qualified member function overload resolution by utilizing Clang's AddMethodCandidate API instead of AddOverloadCandidate.
Fixes #590

Type of change

Please tick all options which are relevant.

  • [✅] Bug fix
  • [✅] New feature

Testing

Tested calling with both const object type and non-const object type

Checklist

  • [✅] I have read the contribution guide recently

@Priyanshu3820 Priyanshu3820 marked this pull request as ready for review February 5, 2026 19:49
Copilot AI review requested due to automatic review settings February 5, 2026 19:49
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR enhances the BestOverloadFunctionMatch function to properly support const-qualified member function overload resolution, addressing issue #590. The implementation switches from using Clang's AddOverloadCandidate API to AddMethodCandidate and AddMethodTemplateCandidate APIs when resolving member function overloads with an invoking object type specified.

Changes:

  • Added optional invoking_object_type parameter to BestOverloadFunctionMatch to enable overload resolution based on const-qualification and value category of the invoking object
  • Modified overload candidate selection logic to use appropriate Clang APIs (AddMethodCandidate for member functions, AddOverloadCandidate for regular functions)
  • Added comprehensive test coverage for const-qualified member function overload resolution
  • Applied code formatting improvements across test files

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
include/CppInterOp/CppInterOp.h Added optional invoking_object_type parameter with default value to maintain backward compatibility
lib/CppInterOp/CppInterOp.cpp Implemented logic to handle member function overload resolution with object type consideration; includes synthetic expression creation for the invoking object and refactored error message formatting
unittests/CppInterOp/FunctionReflectionTest.cpp Added new test case for const-qualified member function overload resolution and applied code formatting improvements throughout

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@Priyanshu3820 Priyanshu3820 mentioned this pull request Feb 5, 2026
10 tasks
@Priyanshu3820
Copy link
Author

Wasn't sure whether to check the new feature option but did it anyways. Also, a lot of diffs happened after running clang-format

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

clang-tidy made some suggestions

There were too many comments to post at once. Showing the first 10 out of 19. Check the log or trigger a new build to see more.

WrapperExpr() : OpaqueValueExpr(clang::Stmt::EmptyShell()) {}
};
auto* Exprs = new WrapperExpr[arg_types.size()];
// Check if we need to prepend the invoking object (for member functions)
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: initializing non-owner 'WrapperExpr *' with a newly created 'gsl::owner<>' [cppcoreguidelines-owning-memory]

  auto* Exprs = new WrapperExpr[num_exprs];
  ^

FunctionDecl* Result = Best != Overloads.end() ? Best->Function : nullptr;
FunctionDecl* Result = nullptr;

// If overload resolution succeeded or found an ambiguous match, use it
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: deleting a pointer through a type that is not marked 'gsl::owner<>'; consider using a smart pointer instead [cppcoreguidelines-owning-memory]

  delete[] Exprs;
  ^
Additional context

lib/CppInterOp/CppInterOp.cpp:1296: variable declared here

  auto* Exprs = new WrapperExpr[num_exprs];
  ^

EXPECT_EQ(Cpp::GetFunctionNumArgs(Decls[1]), (size_t) 4);
EXPECT_EQ(Cpp::GetFunctionNumArgs(Decls[2]), (size_t) 4);
EXPECT_EQ(Cpp::GetFunctionNumArgs(Decls[3]), (size_t) 4);
EXPECT_EQ(Cpp::GetFunctionNumArgs(Decls[0]), (size_t)0);
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 "size_t" is directly included [misc-include-cleaner]

unittests/CppInterOp/FunctionReflectionTest.cpp:9:

- #include <llvm/ADT/ArrayRef.h>
+ #include <cstddef>
+ #include <llvm/ADT/ArrayRef.h>


TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_IsPublicMethod) {
std::vector<Decl *> Decls, SubDecls;
std::vector<Decl*> Decls, SubDecls;
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: multiple declarations in a single statement reduces readability [readability-isolate-declaration]

Suggested change
std::vector<Decl*> Decls, SubDecls;
std::vector<Decl*> Decls;
std::vector<Decl*> SubDecls;


TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_IsProtectedMethod) {
std::vector<Decl *> Decls, SubDecls;
std::vector<Decl*> Decls, SubDecls;
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: multiple declarations in a single statement reduces readability [readability-isolate-declaration]

Suggested change
std::vector<Decl*> Decls, SubDecls;
std::vector<Decl*> Decls;
std::vector<Decl*> SubDecls;


TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_IsPrivateMethod) {
std::vector<Decl *> Decls, SubDecls;
std::vector<Decl*> Decls, SubDecls;
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: multiple declarations in a single statement reduces readability [readability-isolate-declaration]

Suggested change
std::vector<Decl*> Decls, SubDecls;
std::vector<Decl*> Decls;
std::vector<Decl*> SubDecls;


TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_IsConstructor) {
std::vector<Decl *> Decls, SubDecls;
std::vector<Decl*> Decls, SubDecls;
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: multiple declarations in a single statement reduces readability [readability-isolate-declaration]

Suggested change
std::vector<Decl*> Decls, SubDecls;
std::vector<Decl*> Decls;
std::vector<Decl*> SubDecls;


TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_IsDestructor) {
std::vector<Decl *> Decls, SubDecls;
std::vector<Decl*> Decls, SubDecls;
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: multiple declarations in a single statement reduces readability [readability-isolate-declaration]

Suggested change
std::vector<Decl*> Decls, SubDecls;
std::vector<Decl*> Decls;
std::vector<Decl*> SubDecls;


TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_IsStaticMethod) {
std::vector<Decl *> Decls, SubDecls;
std::vector<Decl*> Decls, SubDecls;
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: multiple declarations in a single statement reduces readability [readability-isolate-declaration]

Suggested change
std::vector<Decl*> Decls, SubDecls;
std::vector<Decl*> Decls;
std::vector<Decl*> SubDecls;

auto *CtorD
= (clang::CXXConstructorDecl*)Cpp::GetDefaultConstructor(Decls[0]);
auto* CtorD =
(clang::CXXConstructorDecl*)Cpp::GetDefaultConstructor(Decls[0]);
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: do not use C-style cast to convert between unrelated types [cppcoreguidelines-pro-type-cstyle-cast]

      (clang::CXXConstructorDecl*)Cpp::GetDefaultConstructor(Decls[0]);
      ^

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

clang-tidy made some suggestions

There were too many comments to post at once. Showing the first 10 out of 16. Check the log or trigger a new build to see more.

S.AddMethodTemplateCandidate(
FTD, DeclAccessPair::make(FTD, FTD->getAccess()),
cast<CXXRecordDecl>(FTD->getDeclContext()), &ExplicitTemplateArgs,
ObjectArg->getType(), ObjectArg->Classify(C), CallArgs, Overloads);
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: isa_and_nonnull<> is preferred over an explicit test for null followed by calling isa<> [llvm-prefer-isa-or-dyn-cast-in-conditionals]

Suggested change
ObjectArg->getType(), ObjectArg->Classify(C), CallArgs, Overloads);
if (isa_and_nonnull<CXXMethodDecl>(FTD->getTemplatedDecl())) {

std::string s("Hello World!\n");
void *args0[1] = { (void *) &i };
void *args1[1] = { (void *) &s };
void* args0[1] = {(void*)&i};
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: do not declare C-style arrays, use std::array<> instead [cppcoreguidelines-avoid-c-arrays]

  void* args0[1] = {(void*)&i};
  ^

void *args0[1] = { (void *) &i };
void *args1[1] = { (void *) &s };
void* args0[1] = {(void*)&i};
void* args1[1] = {(void*)&s};
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: do not declare C-style arrays, use std::array<> instead [cppcoreguidelines-avoid-c-arrays]

  void* args1[1] = {(void*)&s};
  ^

auto *CtorD = (clang::CXXConstructorDecl*)Cpp::GetDefaultConstructor(ClassC);
auto FCI_Ctor =
Cpp::MakeFunctionCallable(CtorD);
auto* CtorD = (clang::CXXConstructorDecl*)Cpp::GetDefaultConstructor(ClassC);
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: do not use C-style cast to convert between unrelated types [cppcoreguidelines-pro-type-cstyle-cast]

  auto* CtorD = (clang::CXXConstructorDecl*)Cpp::GetDefaultConstructor(ClassC);
                ^

auto *DtorD = (clang::CXXDestructorDecl*)Cpp::GetDestructor(ClassC);
auto FCI_Dtor =
Cpp::MakeFunctionCallable(DtorD);
auto* DtorD = (clang::CXXDestructorDecl*)Cpp::GetDestructor(ClassC);
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: do not use C-style cast to convert between unrelated types [cppcoreguidelines-pro-type-cstyle-cast]

  auto* DtorD = (clang::CXXDestructorDecl*)Cpp::GetDestructor(ClassC);
                ^

auto *DtorD = (clang::CXXDestructorDecl*)Cpp::GetDestructor(ClassC);
auto FCI_Dtor =
Cpp::MakeFunctionCallable(DtorD);
auto* DtorD = (clang::CXXDestructorDecl*)Cpp::GetDestructor(ClassC);
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::CXXDestructorDecl" is directly included [misc-include-cleaner]

unittests/CppInterOp/FunctionReflectionTest.cpp:9:

- #include <llvm/ADT/ArrayRef.h>
+ #include <clang/AST/DeclCXX.h>
+ #include <llvm/ADT/ArrayRef.h>

operators DFLT_OP_ARITY);
EXPECT_EQ(operators.size(), 2);

auto kp_int_type = Cpp::GetTypeFromScope(KlassProduct_int);
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: 'auto kp_int_type' can be declared as 'auto *kp_int_type' [llvm-qualified-auto]

Suggested change
auto kp_int_type = Cpp::GetTypeFromScope(KlassProduct_int);
auto *kp_int_type = Cpp::GetTypeFromScope(KlassProduct_int);

EXPECT_EQ(operators.size(), 2);

auto kp_int_type = Cpp::GetTypeFromScope(KlassProduct_int);
auto kp_int_lvalue = Cpp::GetReferencedType(kp_int_type, false);
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: 'auto kp_int_lvalue' can be declared as 'auto *kp_int_lvalue' [llvm-qualified-auto]

Suggested change
auto kp_int_lvalue = Cpp::GetReferencedType(kp_int_type, false);
auto *kp_int_lvalue = Cpp::GetReferencedType(kp_int_type, false);

EXPECT_TRUE(where == Cpp::Construct(scope, where DFLT_1));
// Check for the value of x which should be at the start of the object.
EXPECT_TRUE(*(int *)where == 12345);
EXPECT_TRUE(*(int*)where == 12345);
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: do not use C-style cast to convert between unrelated types [cppcoreguidelines-pro-type-cstyle-cast]

  EXPECT_TRUE(*(int*)where == 12345);
               ^

// destruct the rest
auto *new_head = reinterpret_cast<void*>(reinterpret_cast<char*>(where) +
(Cpp::SizeOf(scope) * 3));
auto* new_head = reinterpret_cast<void*>(reinterpret_cast<char*>(where) +
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: do not use reinterpret_cast [cppcoreguidelines-pro-type-reinterpret-cast]

  auto* new_head = reinterpret_cast<void*>(reinterpret_cast<char*>(where) +
                                           ^

TYPED_TEST(CPPINTEROP_TEST_MODE, Interpreter_CreateInterpreterCAPI) {
const char* argv[] = {"-std=c++17"};
auto *CXI = clang_createInterpreter(argv, 1);
auto* CXI = clang_createInterpreter(argv, 1);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Random change. Revert.

Comment on lines 378 to 379
EXPECT_DEATH(Cpp::UseExternalInterpreter(ExtInterp),
"sInterpreter already in use!");
Copy link
Collaborator

Choose a reason for hiding this comment

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

Random formatting change. Revert.

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

clang-tidy made some suggestions

// destruct the rest
auto *new_head = reinterpret_cast<void*>(reinterpret_cast<char*>(where) +
(Cpp::SizeOf(scope) * 3));
auto* new_head = reinterpret_cast<void*>(reinterpret_cast<char*>(where) +
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: do not use reinterpret_cast [cppcoreguidelines-pro-type-reinterpret-cast]

  auto* new_head = reinterpret_cast<void*>(reinterpret_cast<char*>(where) +
                   ^

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

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

Choose a reason for hiding this comment

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

warning: unused variable 'C' [clang-diagnostic-unused-variable]

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

if (llvm::sys::RunningOnValgrind())
GTEST_SKIP() << "XFAIL due to Valgrind report";
std::vector<const char*> interpreter_args = { "-include", "new", "-Xclang", "-iwithsysroot/include/compat" };
std::vector<const char*> interpreter_args = {"-include", "new", "-Xclang",
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 "std::vector" is directly included [misc-include-cleaner]

unittests/CppInterOp/InterpreterTest.cpp:2:

+ #include <vector>

TYPED_TEST(CPPINTEROP_TEST_MODE, Interpreter_CreateInterpreterCAPI) {
const char* argv[] = {"-std=c++17"};
auto *CXI = clang_createInterpreter(argv, 1);
auto* CXI = clang_createInterpreter(argv, 1);
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: do not implicitly decay an array into a pointer; consider using gsl::array_view or an explicit cast instead [cppcoreguidelines-pro-bounds-array-to-pointer-decay]

  auto* CXI = clang_createInterpreter(argv, 1);
                                      ^

#endif
const char* argv[] = {"-fsyntax-only", "-Xclang", "-invalid-plugin"};
auto *CXI = clang_createInterpreter(argv, 3);
auto* CXI = clang_createInterpreter(argv, 3);
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: do not implicitly decay an array into a pointer; consider using gsl::array_view or an explicit cast instead [cppcoreguidelines-pro-bounds-array-to-pointer-decay]

  auto* CXI = clang_createInterpreter(argv, 3);
                                      ^

Cpp::GetIncludePaths(includes);
EXPECT_NE(std::find(includes.begin(), includes.end(), "/non/existent/"),
std::end(includes));
std::end(includes));
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 "std::end" is directly included [misc-include-cleaner]

unittests/CppInterOp/InterpreterTest.cpp:2:

+ #include <iterator>

@Priyanshu3820
Copy link
Author

@mcbarton, actually these changes occured after running clang-format

@mcbarton
Copy link
Collaborator

mcbarton commented Feb 6, 2026

@mcbarton, actually these changes occured after running clang-format

@Priyanshu3820 clang-format on the code may suggest these changes, but we use git clang format to only apply clang-format to the changes in the PR/commits.

@Priyanshu3820
Copy link
Author

@mcbarton, actually these changes occured after running clang-format

@Priyanshu3820 clang-format on the code may suggest these changes, but we use git clang format to only apply clang-format to the changes in the PR/commits.

I get it now, I was supposed to run git-clang-format not clang-format on the files. I'll revert the changes(there are too many though)

@Priyanshu3820
Copy link
Author

@mcbarton, I've fixed the formatting

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

clang-tidy made some suggestions

auto kp_int_lvalue = Cpp::GetReferencedType(kp_int_type, false);
EXPECT_TRUE(kp_int_lvalue);
op = Cpp::BestOverloadFunctionMatch(
operators, {}, {{Cpp::GetTypeFromScope(KlassProduct_float)}});
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: too few arguments to function call, expected 4, have 3 [clang-diagnostic-error]

      operators, {}, {{Cpp::GetTypeFromScope(KlassProduct_float)}});
                                                                  ^

@Priyanshu3820
Copy link
Author

hi @mcbarton, can you please re-run the workflow once more, I am sure the tests would pass this time. All the tests pass locally.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature Request]: Full member function support in BestOverloadFunctionMatch

2 participants