Skip to content

Commit 72eea20

Browse files
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>
1 parent 48c7be4 commit 72eea20

File tree

3 files changed

+24
-3
lines changed

3 files changed

+24
-3
lines changed

include/pybind11/cast.h

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1009,6 +1009,8 @@ struct return_value_policy_override<
10091009
// Basic python -> C++ casting; throws if casting fails
10101010
template <typename T, typename SFINAE>
10111011
type_caster<T, SFINAE> &load_type(type_caster<T, SFINAE> &conv, const handle &handle) {
1012+
static_assert(!detail::is_pyobject<T>::value,
1013+
"Internal error: type_caster should only be used for C++ types");
10121014
if (!conv.load(handle, true)) {
10131015
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
10141016
throw cast_error("Unable to cast Python instance to C++ type (#define "
@@ -1099,21 +1101,30 @@ detail::enable_if_t<!detail::move_never<T>::value, T> move(object &&obj) {
10991101
// - If both movable and copyable, check ref count: if 1, move; otherwise copy
11001102
// - Otherwise (not movable), copy.
11011103
template <typename T>
1102-
detail::enable_if_t<detail::move_always<T>::value, T> cast(object &&object) {
1104+
detail::enable_if_t<!detail::is_pyobject<T>::value && detail::move_always<T>::value, T>
1105+
cast(object &&object) {
11031106
return move<T>(std::move(object));
11041107
}
11051108
template <typename T>
1106-
detail::enable_if_t<detail::move_if_unreferenced<T>::value, T> cast(object &&object) {
1109+
detail::enable_if_t<!detail::is_pyobject<T>::value && detail::move_if_unreferenced<T>::value, T>
1110+
cast(object &&object) {
11071111
if (object.ref_count() > 1) {
11081112
return cast<T>(object);
11091113
}
11101114
return move<T>(std::move(object));
11111115
}
11121116
template <typename T>
1113-
detail::enable_if_t<detail::move_never<T>::value, T> cast(object &&object) {
1117+
detail::enable_if_t<!detail::is_pyobject<T>::value && detail::move_never<T>::value, T>
1118+
cast(object &&object) {
11141119
return cast<T>(object);
11151120
}
11161121

1122+
// pytype rvalue -> pytype (calls converting constructor)
1123+
template <typename T>
1124+
detail::enable_if_t<detail::is_pyobject<T>::value, T> cast(object &&object) {
1125+
return T(std::move(object));
1126+
}
1127+
11171128
template <typename T>
11181129
T object::cast() const & {
11191130
return pybind11::cast<T>(*this);

tests/test_copy_move.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,4 +289,7 @@ TEST_SUBMODULE(copy_move_policies, m) {
289289
py::return_value_policy::move);
290290
m.def(
291291
"get_moveissue2", [](int i) { return MoveIssue2(i); }, py::return_value_policy::move);
292+
293+
// Make sure that cast from pytype rvalue to other pytype works
294+
m.def("get_pytype_rvalue_castissue", [](double i) { return py::float_(i).cast<py::int_>(); });
292295
}

tests/test_copy_move.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,3 +123,10 @@ def test_move_fallback():
123123
assert m1.value == 1
124124
m2 = m.get_moveissue2(2)
125125
assert m2.value == 2
126+
127+
128+
def test_pytype_rvalue_cast():
129+
"""Make sure that cast from pytype rvalue to other pytype works"""
130+
131+
value = m.get_pytype_rvalue_castissue(1.0)
132+
assert value == 1

0 commit comments

Comments
 (0)