Skip to content

Commit b739a53

Browse files
committed
Merge branch 'master' into sh_merge_master
2 parents bf79caa + ab955f1 commit b739a53

File tree

4 files changed

+73
-5
lines changed

4 files changed

+73
-5
lines changed

include/pybind11/cast.h

+13-2
Original file line numberDiff line numberDiff line change
@@ -1376,13 +1376,24 @@ enable_if_t<!cast_is_temporary_value_reference<T>::value, T> cast_ref(object &&,
13761376
// static_assert, even though if it's in dead code, so we provide a "trampoline" to pybind11::cast
13771377
// that only does anything in cases where pybind11::cast is valid.
13781378
template <typename T>
1379-
enable_if_t<cast_is_temporary_value_reference<T>::value, T> cast_safe(object &&) {
1379+
enable_if_t<cast_is_temporary_value_reference<T>::value
1380+
&& !detail::is_same_ignoring_cvref<T, PyObject *>::value,
1381+
T>
1382+
cast_safe(object &&) {
13801383
pybind11_fail("Internal error: cast_safe fallback invoked");
13811384
}
13821385
template <typename T>
13831386
enable_if_t<std::is_void<T>::value, void> cast_safe(object &&) {}
13841387
template <typename T>
1385-
enable_if_t<detail::none_of<cast_is_temporary_value_reference<T>, std::is_void<T>>::value, T>
1388+
enable_if_t<detail::is_same_ignoring_cvref<T, PyObject *>::value, PyObject *>
1389+
cast_safe(object &&o) {
1390+
return o.release().ptr();
1391+
}
1392+
template <typename T>
1393+
enable_if_t<detail::none_of<cast_is_temporary_value_reference<T>,
1394+
detail::is_same_ignoring_cvref<T, PyObject *>,
1395+
std::is_void<T>>::value,
1396+
T>
13861397
cast_safe(object &&o) {
13871398
return pybind11::cast<T>(std::move(o));
13881399
}

include/pybind11/pybind11.h

+5-1
Original file line numberDiff line numberDiff line change
@@ -3128,10 +3128,14 @@ function get_override(const T *this_ptr, const char *name) {
31283128
= pybind11::get_override(static_cast<const cname *>(this), name); \
31293129
if (override) { \
31303130
auto o = override(__VA_ARGS__); \
3131-
if (pybind11::detail::cast_is_temporary_value_reference<ret_type>::value) { \
3131+
PYBIND11_WARNING_PUSH \
3132+
PYBIND11_WARNING_DISABLE_MSVC(4127) \
3133+
if (pybind11::detail::cast_is_temporary_value_reference<ret_type>::value \
3134+
&& !pybind11::detail::is_same_ignoring_cvref<ret_type, PyObject *>::value) { \
31323135
static pybind11::detail::override_caster_t<ret_type> caster; \
31333136
return pybind11::detail::cast_ref<ret_type>(std::move(o), caster); \
31343137
} \
3138+
PYBIND11_WARNING_POP \
31353139
return pybind11::detail::cast_safe<ret_type>(std::move(o)); \
31363140
} \
31373141
} while (false)

tests/test_type_caster_pyobject_ptr.cpp

+39-2
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@
55
#include "pybind11_tests.h"
66

77
#include <cstddef>
8+
#include <string>
89
#include <vector>
910

10-
namespace {
11+
namespace test_type_caster_pyobject_ptr {
1112

1213
std::vector<PyObject *> make_vector_pyobject_ptr(const py::object &ValueHolder) {
1314
std::vector<PyObject *> vec_obj;
@@ -18,9 +19,39 @@ std::vector<PyObject *> make_vector_pyobject_ptr(const py::object &ValueHolder)
1819
return vec_obj;
1920
}
2021

21-
} // namespace
22+
struct WithPyObjectPtrReturn {
23+
#if defined(__clang_major__) && __clang_major__ < 4
24+
WithPyObjectPtrReturn() = default;
25+
WithPyObjectPtrReturn(const WithPyObjectPtrReturn &) = default;
26+
#endif
27+
virtual ~WithPyObjectPtrReturn() = default;
28+
virtual PyObject *return_pyobject_ptr() const = 0;
29+
};
30+
31+
struct WithPyObjectPtrReturnTrampoline : WithPyObjectPtrReturn {
32+
PyObject *return_pyobject_ptr() const override {
33+
PYBIND11_OVERRIDE_PURE(PyObject *, WithPyObjectPtrReturn, return_pyobject_ptr,
34+
/* no arguments */);
35+
}
36+
};
37+
38+
std::string call_return_pyobject_ptr(const WithPyObjectPtrReturn *base_class_ptr) {
39+
PyObject *returned_obj = base_class_ptr->return_pyobject_ptr();
40+
#if !defined(PYPY_VERSION) // It is not worth the trouble doing something special for PyPy.
41+
if (Py_REFCNT(returned_obj) != 1) {
42+
py::pybind11_fail(__FILE__ ":" PYBIND11_TOSTRING(__LINE__));
43+
}
44+
#endif
45+
auto ret_val = py::repr(returned_obj).cast<std::string>();
46+
Py_DECREF(returned_obj);
47+
return ret_val;
48+
}
49+
50+
} // namespace test_type_caster_pyobject_ptr
2251

2352
TEST_SUBMODULE(type_caster_pyobject_ptr, m) {
53+
using namespace test_type_caster_pyobject_ptr;
54+
2455
m.def("cast_from_pyobject_ptr", []() {
2556
PyObject *ptr = PyLong_FromLongLong(6758L);
2657
return py::cast(ptr, py::return_value_policy::take_ownership);
@@ -127,4 +158,10 @@ TEST_SUBMODULE(type_caster_pyobject_ptr, m) {
127158
(void) py::cast(*ptr);
128159
}
129160
#endif
161+
162+
py::class_<WithPyObjectPtrReturn, WithPyObjectPtrReturnTrampoline>(m, "WithPyObjectPtrReturn")
163+
.def(py::init<>())
164+
.def("return_pyobject_ptr", &WithPyObjectPtrReturn::return_pyobject_ptr);
165+
166+
m.def("call_return_pyobject_ptr", call_return_pyobject_ptr);
130167
}

tests/test_type_caster_pyobject_ptr.py

+16
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,19 @@ def test_return_list_pyobject_ptr_reference():
102102
def test_type_caster_name_via_incompatible_function_arguments_type_error():
103103
with pytest.raises(TypeError, match=r"1\. \(arg0: object, arg1: int\) -> None"):
104104
m.pass_pyobject_ptr_and_int(ValueHolder(101), ValueHolder(202))
105+
106+
107+
def test_trampoline_with_pyobject_ptr_return():
108+
class Drvd(m.WithPyObjectPtrReturn):
109+
def return_pyobject_ptr(self):
110+
return ["11", "22", "33"]
111+
112+
# Basic health check: First make sure this works as expected.
113+
d = Drvd()
114+
assert d.return_pyobject_ptr() == ["11", "22", "33"]
115+
116+
while True:
117+
# This failed before PR #5156: AddressSanitizer: heap-use-after-free ... in Py_DECREF
118+
d_repr = m.call_return_pyobject_ptr(d)
119+
assert d_repr == repr(["11", "22", "33"])
120+
break # Comment out for manual leak checking.

0 commit comments

Comments
 (0)