Skip to content

fix JITcall codegen to handle move semantics #657

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

Merged
merged 1 commit into from
Jul 17, 2025
Merged
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
21 changes: 21 additions & 0 deletions lib/CppInterOp/CppInterOp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2151,12 +2151,33 @@ void make_narg_call(const FunctionDecl* FD, const std::string& return_type,
}
}

CXXRecordDecl* rtdecl = QT->getAsCXXRecordDecl();
if (refType != kNotReference) {
callbuf << "(" << type_name.c_str()
<< (refType == kLValueReference ? "&" : "&&") << ")*("
<< type_name.c_str() << "*)args[" << i << "]";
} else if (isPointer) {
callbuf << "*(" << type_name.c_str() << "**)args[" << i << "]";
} else if (rtdecl &&
(rtdecl->hasTrivialCopyConstructor() &&
!rtdecl->hasSimpleCopyConstructor()) &&
rtdecl->hasMoveConstructor()) {
// By-value construction; this may either copy or move, but there is no
// information here in terms of intent. Thus, simply assume that the
// intent is to move if there is no viable copy constructor (ie. if the
// code would otherwise fail to even compile). There does not appear to be
// a simple way of determining whether a viable copy constructor exists,
// so check for the most common case: the trivial one, but not uniquely
// available, while there is a move constructor.

// include utility header if not already included for std::move
DeclarationName DMove = &getASTContext().Idents.get("move");
auto result = getSema().getStdNamespace()->lookup(DMove);
if (result.empty())
Cpp::Declare("#include <utility>");

// move construction as needed for classes (note that this is implicit)
callbuf << "std::move(*(" << type_name.c_str() << "*)args[" << i << "])";
} else {
// pointer falls back to non-pointer case; the argument preserves
// the "pointerness" (i.e. doesn't reference the value).
Expand Down
25 changes: 25 additions & 0 deletions unittests/CppInterOp/FunctionReflectionTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2003,6 +2003,31 @@ TEST(FunctionReflectionTest, GetFunctionCallWrapper) {

auto bar_callable = Cpp::MakeFunctionCallable(bar);
EXPECT_EQ(bar_callable.getKind(), Cpp::JitCall::kGenericCall);

Cpp::Declare(R"(
struct A {
A() {}
A(A&& other) {};
};

A consumable;

template<typename T>
void consume(T t) {}
)");

unresolved_candidate_methods.clear();
Cpp::GetClassTemplatedMethods("consume", Cpp::GetGlobalScope(),
unresolved_candidate_methods);
EXPECT_EQ(unresolved_candidate_methods.size(), 1);

Cpp::TCppScope_t consume = Cpp::BestOverloadFunctionMatch(
unresolved_candidate_methods, {},
{Cpp::GetVariableType(Cpp::GetNamed("consumable"))});
EXPECT_TRUE(consume);

auto consume_callable = Cpp::MakeFunctionCallable(consume);
EXPECT_EQ(consume_callable.getKind(), Cpp::JitCall::kGenericCall);
}

TEST(FunctionReflectionTest, IsConstMethod) {
Expand Down
Loading