Skip to content

Commit 9576632

Browse files
committed
fixup! fixup! Replace global map holders_seen with local holder_type in registered type_info
Runtime costs of checking holder compatibility for function return types during can be limited to definition time (instead of calling time), if we require types to be registered before defining a function. This allows to lookup the type_info record and validate the holder type as expected. As we cannot call functions with an unregistered return type anyway, I think this is a good compromise.
1 parent d5c4386 commit 9576632

File tree

4 files changed

+29
-12
lines changed

4 files changed

+29
-12
lines changed

include/pybind11/cast.h

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1492,13 +1492,13 @@ struct holder_helper {
14921492
};
14931493

14941494
template <typename holder>
1495-
void check_for_holder_mismatch_impl() {
1495+
bool check_for_holder_mismatch_impl(bool throw_if_missing = true) {
14961496
using iholder = intrinsic_t<holder>;
14971497
using base_type = decltype(*holder_helper<iholder>::get(std::declval<iholder>()));
14981498
auto &holder_typeinfo = typeid(iholder);
1499-
auto base_info = detail::get_type_info(typeid(base_type));
1499+
auto base_info = detail::get_type_info(typeid(base_type), throw_if_missing);
15001500
if (!base_info)
1501-
return; // Don't complain if we see this type the first time
1501+
return false; // Return false if the type is not yet registered
15021502

15031503
auto debug = type_id<base_type>();
15041504
if (!same_type(*base_info->holder_type, holder_typeinfo)) {
@@ -1512,6 +1512,7 @@ void check_for_holder_mismatch_impl() {
15121512
" was declared using holder type " + holder_name);
15131513
#endif
15141514
}
1515+
return true;
15151516
}
15161517

15171518
/// Type caster for holder types like std::shared_ptr, etc.
@@ -1538,7 +1539,6 @@ struct copyable_holder_caster : public type_caster_base<type> {
15381539
explicit operator holder_type&() { return holder; }
15391540

15401541
static handle cast(const holder_type &src, return_value_policy, handle) {
1541-
check_for_holder_mismatch_impl<holder_type>();
15421542
const auto *ptr = holder_helper<holder_type>::get(src);
15431543
return type_caster_base<type>::cast_holder(ptr, &src);
15441544
}
@@ -1635,10 +1635,10 @@ template <typename holder> using is_holder = any_of<
16351635
is_template_base_of<copyable_holder_caster, make_caster<holder>>>;
16361636

16371637
template <typename holder>
1638-
void check_for_holder_mismatch(enable_if_t<!is_holder<holder>::value, int> = 0) {}
1638+
bool check_for_holder_mismatch(enable_if_t<!is_holder<holder>::value, int> = 0) { return true; }
16391639
template <typename holder>
1640-
void check_for_holder_mismatch(enable_if_t<is_holder<holder>::value, int> = 0) {
1641-
check_for_holder_mismatch_impl<holder>();
1640+
bool check_for_holder_mismatch(enable_if_t<is_holder<holder>::value, int> = 0) {
1641+
return check_for_holder_mismatch_impl<holder>(false);
16421642
}
16431643

16441644
template <typename T> struct handle_type_name { static constexpr auto name = _<T>(); };

include/pybind11/pybind11.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,12 @@ class cpp_function : public function {
158158
"The number of argument annotations does not match the number of function arguments");
159159

160160
// Fail if the type was previously registered with a different holder type
161-
detail::check_for_holder_mismatch<Return>();
161+
if (!detail::check_for_holder_mismatch<Return>()) {
162+
// If Return type was not yet registered, check_for_holder_mismatch() returns false w/o raising
163+
std::string tname(typeid(Return).name());
164+
detail::clean_type_id(tname);
165+
pybind11_fail("Cannot register function with not yet registered return type \"" + tname + "\"");
166+
}
162167

163168
/* Dispatch code which converts function arguments and performs the actual function call */
164169
rec->impl = [](function_call &call) -> handle {

tests/test_smart_ptr.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,15 @@ TEST_SUBMODULE(smart_ptr, m) {
377377
// Fails: the class was already registered with a shared_ptr holder
378378
m.def("bad1", []() { return std::unique_ptr<HeldByShared>(new HeldByShared()); });
379379
});
380-
m.def("return_shared", []() { return std::make_shared<HeldByUnique>(); });
380+
m.def("register_mismatch_class", [](py::module m) {
381+
// Fails: the class was already registered with a shared_ptr holder
382+
py::class_<HeldByShared, std::unique_ptr<HeldByShared>>(m, "bad");
383+
});
384+
385+
m.def("register_return_shared", [](py::module m) {
386+
// Fails if HeldByUnique is not yet registered or, if registered, due to mismatching holder
387+
m.def("return_shared", []() { return std::make_shared<HeldByUnique>(); });
388+
});
381389
m.def("register_HeldByUnique", [](py::module m) {
382390
py::class_<HeldByUnique>(m, "HeldByUnique");
383391
});

tests/test_smart_ptr.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -312,12 +312,16 @@ def test_holder_mismatch():
312312
with pytest.raises(RuntimeError) as excinfo:
313313
m.register_mismatch_return(m)
314314
assert "Mismatched holders detected" in str(excinfo)
315+
with pytest.raises(RuntimeError) as excinfo:
316+
m.register_mismatch_class(m)
317+
assert "is already registered" in str(excinfo)
315318

316-
with pytest.raises(TypeError) as excinfo:
317-
m.return_shared() # calling a function returning an unregistered type
319+
with pytest.raises(RuntimeError) as excinfo:
320+
m.register_return_shared(m) # Cannot register function returning an unregistered type
321+
assert "Cannot register function with not yet registered return type" in str(excinfo)
318322
m.register_HeldByUnique(m) # register the type
319323
with pytest.raises(RuntimeError) as excinfo:
320-
m.return_shared() # calling a function returning a mismatching holder type
324+
m.register_return_shared(m) # Cannot register function returning a mismatching holder type
321325
assert "Mismatched holders detected" in str(excinfo)
322326

323327
with pytest.raises(RuntimeError) as excinfo:

0 commit comments

Comments
 (0)