fix: bind noexcept and ref-qualified methods from unregistered base classes#5992
fix: bind noexcept and ref-qualified methods from unregistered base classes#5992Skylion007 wants to merge 16 commits intopybind:masterfrom
Conversation
a7a3ed9 to
e8b0584
Compare
e8b0584 to
b303194
Compare
ad30bc0 to
f530adb
Compare
| // (which match plain Return(Args...)) work correctly with noexcept callables (issue #2234). | ||
| template <typename Function, typename F = remove_reference_t<Function>> | ||
| using function_signature_t = conditional_t< | ||
| using function_signature_t = remove_noexcept_t<conditional_t< |
There was a problem hiding this comment.
Should I normalize it here for all pybind11? Or keep it as utility that and create a new alias that always strips the noexcept
There was a problem hiding this comment.
on the flip side we could use the noexcept tag handling to optimize away exception handling for certain edge cases like def_buffer maybe?
|
|
||
| // Exercises cpp_function(Return (Class::*)(Args...) &&, ...) and | ||
| // cpp_function(Return (Class::*)(Args...) const &&, ...) via an unregistered base. | ||
| class RValueRefUnregisteredBase { |
There was a problem hiding this comment.
Also unsure if should encourage rvalue overloads, but they were always supported by manually calling them via lambdas sooo
|
Hi @Skylion007 could you please validate my high-level understanding of this PR? This is the result of me chatting with Cursor (GPT-5.3 Codex High) for a bit:
|
A production-code review after pybind#2234 showed that ref-qualified member pointers were still inconsistently handled across def_buffer, vectorize, and overload_cast, so this adds the missing overloads with focused tests for each newly-supported signature. Co-authored-by: Cursor <cursoragent@cursor.com>
These comments were added while reviewing the qualifier coverage follow-up, to document that buffer/vectorized calls operate on existing Python-owned instances and should not move-from self. Co-authored-by: Cursor <cursoragent@cursor.com>
This was added as a maintenance follow-up to the qualifier-consistency work, so future changes that introduce overload_cast ambiguity or wrong ref/noexcept resolution fail at compile time. Co-authored-by: Cursor <cursoragent@cursor.com>
As part of the qualifier-consistency maintenance follow-up, this reduces duplication in overload_cast_impl while preserving the same ref/noexcept coverage and keeping pedantic-clean macro expansion. Co-authored-by: Cursor <cursoragent@cursor.com>
…skip guards. This replaces hasattr-based optional assertions with skipif-gated noexcept-only tests so skipped coverage is visible in pytest output while keeping non-noexcept checks always active. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Hi @Skylion007 I added five Cursor-generated commits (all using GPT-5.3 Codex High). I'll do a manual review here after I see that the CI is still passing. |
Description
Fix issue #2234 . Fix overloading to accept noexcept function types too and strip them. Mostly authored by ClaudeCode with a lot of human intervention.
fix: support noexcept and ref-qualified methods from unregistered base classes (issue #2234)
Problem
In C++17,
noexceptbecame part of the function type (P0012R1). This meansint (Base::*)() const noexceptandint (Base::*)() constare distincttypes. When a derived class inherits methods from an unregistered base,
method_adaptormust recast the member function pointer fromBase::*toDerived::*. Without explicit template specializations for noexcept (andref-qualified) variants, the generic
F&&fallback was selected, leaving thepointer typed as
Base::*— causing pybind11 to look upBaseasself,which fails at runtime for unregistered base classes.
Similarly,
cpp_functionlacked constructors fornoexcept,& noexcept,const noexcept,const & noexcept,&&,const &&,&& noexcept, andconst && noexceptmember function pointer types, so those overloads silentlyfell through to the generic lambda constructor.
Solution
include/pybind11/pybind11.hcpp_functionconstructors for all missing qualifier combinations:&&,const &&(unconditional): usestd::move(*c).*fin the lambdanoexcept,const noexcept,& noexcept,const & noexcept,&& noexcept,const && noexcept(under#ifdef __cpp_noexcept_function_type)detail::rebind_member_ptr<Derived, T>type trait mappingReturn (Base::*)(Args...) qualifierstoReturn (Derived::*)(Args...) qualifiersacross all 12 qualifier combinations, generated via a local
PYBIND11_REBIND_MEMBER_PTRmacro.detail::adapt_member_ptr<Derived>(pmf)— sharedconstexprhelpercalled by all
method_adaptoroverloads: runs theis_accessible_base_ofstatic_assert then returns the implicitly-cast pointer.
method_adaptoroverloads with aPYBIND11_METHOD_ADAPTORmacro covering all 11 qualified variants (plus the no-qualifier overload
written explicitly to avoid MSVC C4003). All overloads are
constexpr.&/const &method_adaptoroverloads (no#ifdefguard) to fix C++14 builds where
noexceptis stripped from the type butthe
&qualifier is not.include/pybind11/numpy.hvectorizeoverloads fornoexceptfree function pointers.Tests
test_methods_and_attributes:NoexceptDerived/NoexceptUnregisteredBase(C++17 noexcept from unregistered base),
RValueRefDerived/RValueRefUnregisteredBase(&&/const &&methods that move-out state toconfirm
std::move(*c).*fsemantics),NoexceptOverloaded(overload_castwith noexcept), static_asserts verifying
method_adaptorpreserves allqualifier combinations.
test_buffers:def_bufferwithnoexceptmember function pointers.test_numpy_vectorize:vectorizewithnoexceptfree function pointers.Compatibility
cpp_functionconstructorsand
method_adaptoroverloads are additive and selected only for the newtype signatures.
__cpp_noexcept_function_type).rather than invoking macros with an empty argument.
bugprone-macro-parenthesessuppressed withNOLINTBEGIN/NOLINTENDaround the macro definitions (thequalifiersargument appearsin type position where parenthesizing it would be invalid C++).
Suggested changelog entry:
noexceptand ref-qualified (&,&&) methodsinherited from unregistered base classes in C++17, where
noexceptispart of the function type.
method_adaptornow has specializations forall qualifier combinations (
const,&,const &,&&,const &&, and theirnoexceptvariants), andcpp_functiongainsmatching constructors.
&&-qualified methods correctly usestd::move(*self).*fin the generated lambda.