diff --git a/lib/CppInterOp/CppInterOp.cpp b/lib/CppInterOp/CppInterOp.cpp index 12b8da247..cb8df76c5 100755 --- a/lib/CppInterOp/CppInterOp.cpp +++ b/lib/CppInterOp/CppInterOp.cpp @@ -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 "); + + // 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). diff --git a/unittests/CppInterOp/FunctionReflectionTest.cpp b/unittests/CppInterOp/FunctionReflectionTest.cpp index ac050f59b..5332afa66 100644 --- a/unittests/CppInterOp/FunctionReflectionTest.cpp +++ b/unittests/CppInterOp/FunctionReflectionTest.cpp @@ -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 + 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) {