diff --git a/lib/CppInterOp/CppInterOp.cpp b/lib/CppInterOp/CppInterOp.cpp index 90c934d19..3f46c1c88 100644 --- a/lib/CppInterOp/CppInterOp.cpp +++ b/lib/CppInterOp/CppInterOp.cpp @@ -2568,6 +2568,33 @@ void collect_type_info(const FunctionDecl* FD, QualType& QT, get_type_as_string(QT, type_name, C, Policy); } +static bool IsCopyConstructorDeleted(QualType QT) { + CXXRecordDecl* RD = QT->getAsCXXRecordDecl(); + if (!RD) { + // For types that are not C++ records (such as PODs), we assume that they + // are copyable, ie their copy constructor is not deleted. + return false; + } + + RD = RD->getDefinition(); + assert(RD && "expecting a definition"); + + if (RD->hasSimpleCopyConstructor()) + return false; + + for (auto* Ctor : RD->ctors()) { + if (Ctor->isCopyConstructor()) { + return Ctor->isDeleted(); + } + } + + assert(0 && "did not find a copy constructor?"); + // Should never happen and the return value is somewhat arbitrary, but we did + // not see a deleted copy ctor. The user will be told if the generated code + // doesn't compile. + return false; +} + void make_narg_ctor(const FunctionDecl* FD, const unsigned N, std::ostringstream& typedefbuf, std::ostringstream& callbuf, const std::string& class_name, int indent_level, @@ -2612,7 +2639,18 @@ void make_narg_ctor(const FunctionDecl* FD, const unsigned N, } else if (isPointer) { callbuf << "*(" << type_name.c_str() << "**)args[" << i << "]"; } else { + // By-value construction: Figure out if the type can be + // copy-constructed. This is tricky and cannot be done in a fully + // reliable way, also because std::vector always defines a copy + // constructor, even if the type T is only moveable. As a heuristic, we + // only check if the copy constructor is deleted, or would be if + // implicit. + bool Move = IsCopyConstructorDeleted(QT); + if (Move) + callbuf << "static_cast<" << type_name << "&&>("; callbuf << "*(" << type_name.c_str() << "*)args[" << i << "]"; + if (Move) + callbuf << ")"; } } callbuf << ")"; diff --git a/unittests/CppInterOp/FunctionReflectionTest.cpp b/unittests/CppInterOp/FunctionReflectionTest.cpp index a01e019ac..34a1d95f1 100644 --- a/unittests/CppInterOp/FunctionReflectionTest.cpp +++ b/unittests/CppInterOp/FunctionReflectionTest.cpp @@ -2879,6 +2879,33 @@ TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_FailingTest1) { EXPECT_FALSE(Cpp::Declare("int y = x;")); } +TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_DeletedCopyConstrutor) { + std::vector Decls; + std::vector SubDecls; + std::string code = R"( + struct GH { + int fMember; + GH(int m = 1) : fMember(m) {} + GH(const GH&) = delete; + GH(GH&&) = default; + }; + struct GH_Default { + int fMember; + GH_Default(GH p = GH()) : fMember(p.fMember) {} + }; + )"; + + GetAllTopLevelDecls(code, Decls, false, {"-include", "new"}); + EXPECT_EQ(Decls.size(), 2); + + GetAllSubDecls(Decls[1], SubDecls); + EXPECT_EQ(SubDecls.size(), 3); + + EXPECT_TRUE(Cpp::IsConstructor(SubDecls[2])); + auto Fn = Cpp::MakeFunctionCallable(SubDecls[2]); + EXPECT_EQ(Fn.getKind(), Cpp::JitCall::kConstructorCall); +} + TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_IsExplicit) { std::vector Decls; std::vector SubDecls;