@@ -489,7 +489,9 @@ class cpp_function : public function {
489489 // actual function lambda so that we can get code reuse for
490490 // functions with the same Return, Args, and Guard.
491491 template <typename Return, typename Guard, typename ArgsConverter, typename ... Args>
492- static handle call_impl (detail::function_call &call, detail::function_ref<Return(Args...)> f) {
492+ static handle call_impl (detail::function_call &call,
493+ detail::function_ref<Return(Args...)> f,
494+ void (*precall)(detail::function_call &)) {
493495 using namespace detail ;
494496 // Static assertion: function_ref must be trivially copyable to ensure safe pass-by-value.
495497 // Lifetime safety: The function_ref is created from cap->f which lives in the capture
@@ -505,6 +507,9 @@ class cpp_function : public function {
505507 return PYBIND11_TRY_NEXT_OVERLOAD ;
506508 }
507509
510+ /* Invoke call policy pre-call hook, only for the overload that matched */
511+ precall (call);
512+
508513 /* Override policy for rvalues -- usually to enforce rvp::move on an rvalue */
509514 return_value_policy policy
510515 = return_value_policy_override<Return>::policy (call.func .policy );
@@ -585,9 +590,6 @@ class cpp_function : public function {
585590
586591 /* Dispatch code which converts function arguments and performs the actual function call */
587592 rec->impl = [](function_call &call) -> handle {
588- /* Invoke call policy pre-call hook */
589- process_attributes<Extra...>::precall (call);
590-
591593 /* Get a pointer to the capture object */
592594 const auto *data = (sizeof (capture) <= sizeof (call.func .data ) ? &call.func .data
593595 : call.func .data [0 ]);
@@ -597,7 +599,9 @@ class cpp_function : public function {
597599 /* Function scope guard -- defaults to the compile-to-nothing
598600 `void_type` */
599601 extract_guard_t <Extra...>,
600- cast_in>(call, detail::function_ref<Return (Args...)>(cap->f ));
602+ cast_in>(call,
603+ detail::function_ref<Return (Args...)>(cap->f ),
604+ &process_attributes<Extra...>::precall);
601605
602606 /* Invoke call policy post-call hook */
603607 process_attributes<Extra...>::postcall (call, result);
@@ -3387,14 +3391,14 @@ PYBIND11_NOINLINE void keep_alive_impl(handle nurse, handle patient) {
33873391
33883392PYBIND11_NOINLINE void
33893393keep_alive_impl (size_t Nurse, size_t Patient, function_call &call, handle ret) {
3390- // The dispatcher runs postcall even when the overload produced no value: `ret` is the
3391- // PYBIND11_TRY_NEXT_OVERLOAD sentinel ((PyObject *) 1) if the overload bailed out of
3392- // `load_args`, and null if the return-value conversion failed (with the real error
3393- // already set). In both cases there was no successful call and there is no relationship
3394- // to establish. Checked here because the sentinel is neither null nor `Py_None`, so it
3395- // passes straight through the guards below and is dereferenced, and because a null `ret`
3396- // would be reported as "Could not activate keep_alive!", masking the real error.
3397- if (!ret || ret.ptr () == PYBIND11_TRY_NEXT_OVERLOAD ) {
3394+ // With index 0, this runs in postcall, which the dispatcher runs even when the overload
3395+ // produced no value: `ret` is the PYBIND11_TRY_NEXT_OVERLOAD sentinel ((PyObject *) 1) if
3396+ // the overload bailed out of `load_args`, and null if the return-value conversion failed
3397+ // (with the real error already set). There is no relationship to establish then. The
3398+ // sentinel would pass the guards below and be dereferenced, and a null `ret` would be
3399+ // reported as "Could not activate keep_alive!", masking the real error.
3400+ const bool uses_ret = Nurse == 0 || Patient == 0 ;
3401+ if (uses_ret && ( !ret || ret.ptr () == PYBIND11_TRY_NEXT_OVERLOAD ) ) {
33983402 return ;
33993403 }
34003404
@@ -3411,7 +3415,15 @@ keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret) {
34113415 return handle ();
34123416 };
34133417
3414- keep_alive_impl (get_arg (Nurse), get_arg (Patient));
3418+ try {
3419+ keep_alive_impl (get_arg (Nurse), get_arg (Patient));
3420+ } catch (...) {
3421+ // The dispatcher drops `ret` when postcall throws, so release it here.
3422+ if (uses_ret) {
3423+ ret.dec_ref ();
3424+ }
3425+ throw ;
3426+ }
34153427}
34163428
34173429inline std::pair<decltype (internals::registered_types_py)::iterator, bool >
0 commit comments