From 72eea20afd51e363fe115265043c2b2b6bcc523a Mon Sep 17 00:00:00 2001 From: Maarten Baert Date: Mon, 16 May 2022 22:51:01 +0200 Subject: [PATCH] Fix py::cast from pytype rvalue to pytype (#3949) * Fix py::cast from pytype rvalue to pytype Previously, py::cast blindly assumed that the destination type was a C++ type rather than a python type when the source type was an rvalue. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- include/pybind11/cast.h | 17 ++++++++++++++--- tests/test_copy_move.cpp | 3 +++ tests/test_copy_move.py | 7 +++++++ 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 376a6795..9a971704 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -1009,6 +1009,8 @@ struct return_value_policy_override< // Basic python -> C++ casting; throws if casting fails template type_caster &load_type(type_caster &conv, const handle &handle) { + static_assert(!detail::is_pyobject::value, + "Internal error: type_caster should only be used for C++ types"); if (!conv.load(handle, true)) { #if !defined(PYBIND11_DETAILED_ERROR_MESSAGES) throw cast_error("Unable to cast Python instance to C++ type (#define " @@ -1099,21 +1101,30 @@ detail::enable_if_t::value, T> move(object &&obj) { // - If both movable and copyable, check ref count: if 1, move; otherwise copy // - Otherwise (not movable), copy. template -detail::enable_if_t::value, T> cast(object &&object) { +detail::enable_if_t::value && detail::move_always::value, T> +cast(object &&object) { return move(std::move(object)); } template -detail::enable_if_t::value, T> cast(object &&object) { +detail::enable_if_t::value && detail::move_if_unreferenced::value, T> +cast(object &&object) { if (object.ref_count() > 1) { return cast(object); } return move(std::move(object)); } template -detail::enable_if_t::value, T> cast(object &&object) { +detail::enable_if_t::value && detail::move_never::value, T> +cast(object &&object) { return cast(object); } +// pytype rvalue -> pytype (calls converting constructor) +template +detail::enable_if_t::value, T> cast(object &&object) { + return T(std::move(object)); +} + template T object::cast() const & { return pybind11::cast(*this); diff --git a/tests/test_copy_move.cpp b/tests/test_copy_move.cpp index 6e1bdd85..28c24456 100644 --- a/tests/test_copy_move.cpp +++ b/tests/test_copy_move.cpp @@ -289,4 +289,7 @@ TEST_SUBMODULE(copy_move_policies, m) { py::return_value_policy::move); m.def( "get_moveissue2", [](int i) { return MoveIssue2(i); }, py::return_value_policy::move); + + // Make sure that cast from pytype rvalue to other pytype works + m.def("get_pytype_rvalue_castissue", [](double i) { return py::float_(i).cast(); }); } diff --git a/tests/test_copy_move.py b/tests/test_copy_move.py index f2c0cdce..9fef0893 100644 --- a/tests/test_copy_move.py +++ b/tests/test_copy_move.py @@ -123,3 +123,10 @@ def test_move_fallback(): assert m1.value == 1 m2 = m.get_moveissue2(2) assert m2.value == 2 + + +def test_pytype_rvalue_cast(): + """Make sure that cast from pytype rvalue to other pytype works""" + + value = m.get_pytype_rvalue_castissue(1.0) + assert value == 1