You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Finally got a proper report from Fable 5.1. I think my Fable 5 one hit a downgrade.
馃 AI text below 馃
Review of pybind11 master (d87cf0b). Seven reviewers covered the headers, Python package, CMake, and CI. I verified every finding below against the source. Items marked "reproduced" were also exercised against the build tree by a reviewer. No files were changed. Reviewer repro scripts are in the session scratchpad.
Bugs
High
include/pybind11/pybind11.h:1156 and :1209: when named args follow py::args, the *args stub is pushed into call.args but its convert flag is appended last. Each kw-only arg gets its successor's flag, so noconvert() lands on the wrong argument. Reproduced. Fix: push false with the stub and append in step 4a only when appending the tuple.
pybind11.h:1249: the second-pass loop stops at pos_args, so overloads whose only convertible args are keyword-only never get a converting pass. Reproduced. Iterate to second_pass_convert.size().
include/pybind11/gil_safe_call_once.h:175-244: nothing resets the cached pointer or the initialized flag when an interpreter is finalized. After finalize_interpreter(); initialize_interpreter() the fast path returns freed memory. py::register_exception uses this class, so re-importing an embedded module that registers an exception hits it. Reproduced with ASan. Clear the owner's cache from ~call_once_storage.
include/pybind11/detail/struct_smart_holder.h:347: from_unique_ptr builds shared_ptr<T>(raw, deleter), which always hooks enable_shared_from_this. The void-cast intent from detail/init.h:224 is defeated, so factories returning unique_ptr<Trampoline> lose the bad_weak_ptr guard and the Python override can silently vanish. Regression from Fix smart_holder multiple/virtual inheritance bugs in shared_ptr and unique_ptr to-Python conversions聽#5836, reproduced. Take an explicit flag and construct the owner as shared_ptr<void>.
include/pybind11/eigen/matrix.h:181-194: strides that are not a multiple of sizeof(Scalar) are truncated by integer division. A packed structured-field view maps as contiguous and a mutable Ref writes into the neighbouring field. Reproduced. Treat a non-divisible stride as non-conformable.
include/pybind11/iostream.h:152: ~pythonbuf() calls into Python; a failing write throws out of a destructor and terminates the process. Reproduced. Catch and discard_as_unraisable.
CMakeLists.txt:387: cmake_path(RELATIVE_PATH ... BASE_DIRECTORY CMAKE_INSTALL_DATAROOTDIR) uses the literal string as the base, so an installed pybind11.pc has an empty prefix= on CMake 3.20+. Confirmed in the current build tree. Wheels are unaffected because pyproject sets it. Use the while() branch unconditionally.
Medium
include/pybind11/cast.h:992, :1246, detail/type_caster_base.h:869: the alias flag is read from src, but after an implicit conversion the loaded instance is the converter's temporary. src may not be a pybind11 instance at all. Record it in load_value from v_h.inst.
cast.h:1738: py::cast of an unregistered type returns a null object with a pending TypeError instead of throwing. Downstream use gives SystemError.
cast.h:1219: returning unique_ptr<T> for a shared_ptr-held or custom-holder T copy-constructs the holder from unique_ptr bytes. tinfo->holder_enum_v is available to throw cast_error instead.
include/pybind11/detail/class.h:396: tp_alloc result is unchecked and allocate_layout() can throw out of the extern "C"tp_new. Calling pybind11_object() from Python aborts. Reproduced.
include/pybind11/detail/common.h:90: __cpp_lib_launder is tested before <new> or <version> is included, so std::launder is never used with libstdc++ in C++17 mode. Move the block below the includes.
include/pybind11/stl.h:77-100, :199, :316, :451: the tp_name heuristic matches any user class named zip, map, or dict_keys; the following assert(isinstance<iterable>) aborts debug builds and release builds throw instead of returning false. Reproduced. Reject heap types and return false.
include/pybind11/stl_bind.h:470: stride-0 (broadcast) buffers give end == p, so the buffer constructor returns an empty vector. Reproduced.
iostream.h:251: calling __enter__ twice destroys the first redirect while the second holds its buffer pointer, then segfaults. Reproduced.
include/pybind11/chrono.h:46: days plus seconds plus microseconds is computed in int64 microseconds regardless of the target period, so timedelta.max overflows. Line 188 does not compile for floating-point durations. Line 171 leaves mktime unchecked.
chrono.h:105: the Annex-K localtime_s call uses MSVC's argument order, so it is a compile error on any libc that actually provides Annex K. Use localtime_r on POSIX and drop the global mutex.
include/pybind11/numpy.h:1453-1469: at() divides the byte offset by itemsize(), wrong for strides that are not multiples of sizeof(T). Reproduced. Use *data(index...).
numpy.h:1357: no negative-index check, so at(-1) reads before the buffer. Reproduced.
numpy.h:785-793: the numpy_scalar caster uses function-local statics that run import numpy under the static-init guard, the GIL deadlock this header warns about elsewhere. Use gil_safe_call_once_and_store.
include/pybind11/eigen/tensor.h:40: modulo by EIGEN_DEFAULT_ALIGN_BYTES is modulo zero with EIGEN_DONT_VECTORIZE.
eigen/matrix.h:678-695: the sparse loader throws instead of returning false (the null checks are dead) and calls mutable_data() on inputs it only reads. Reproduced.
include/pybind11/pytypes.h:2335: set::add returns false with the error still set. stl.h:213 then reports a generic conversion error instead of "unhashable type". Throw like list::append.
include/pybind11/detail/argument_vector.h:83, :377: ref_small_vector move leaves the inline source with its size intact, so both destructors DECREF. Latent because the only user relies on copy elision, and the comment asserts the opposite.
pybind11.h:1690: PyModule_GetFilenameObject returns a new reference stored in a handle; def_submodule leaks one per call. Reproduced.
pybind11.h:3926: PYBIND11_OVERRIDE_IMPL uses a function-static caster for reference returns, a data race under free-threading. thread_local is the cheap fix.
tools/pybind11Common.cmake:51 with CMakeLists.txt:107: the CMP0190 cross-compiling default flips to OFF on reconfigure because option() caches OFF and the "defaulted" marker is not cached. Reproduced.
tools/pybind11Common.cmake:223: COMPAT-mode PYTHON_* variables are plain set(), invisible to an add_subdirectory parent. Reproduced.
tools/pybind11Common.cmake:206: PYBIND11_FINDPYTHON=OLD is truthy, so the documented value selects the NEW mode.
tools/pybind11Common.cmake:375-395: the LTO fallbacks run even in the ppc64le/mips64 "do nothing" and Emscripten "no check" branches, and HAS_FLTO_THIN is reused for thin and full probes so -flto=thin is never actually tested.
Low
pytypes.h tuple, dict, list, set, int_, float_, bytes, and memoryview constructors call pybind11_fail after a NULL return, which asserts that no error is set.
pytypes.h:1469: PYBIND11_OBJECT_CVT on a null object calls the converter with nullptr (segfault for bool_, silent empty dict).
argument_vector.h:294: the assert should be idx < ha.size; exactly 64 kwargs aborts debug builds. Reproduced.
detail/function_record_pyobject.h:145-186: raw tp_new and tp_methods slots let C++ exceptions escape into CPython.
pytypes.h:2067, :2137: capsule destructor lambdas throw from a C callback, and only one installs error_scope.
type_caster_base.h:701-720: the new reference from find_registered_python_instance is held in a handle and leaked on the cast_error path.
type_caster_base.h:545: get_object_handle returns a borrowed pointer from the instance map, a use-after-free window under free-threading.
cast.h:767: tuple_caster::load keeps subcaster pointers into seq[Is] temporaries that die before implicit_cast.
detail/internals.h:517-527: nested-exception chaining for error_already_set and builtin_exception is overwritten by the following restore() or set_error(). Reproduced.
internals.h:702: the interpreter TLS key is updated before the state-dict fetch can throw, leaving the fast path pointing at the previous interpreter's internals.
internals.h:708, :884: instance_base check-and-create and internals_singleton_pp_ are unsynchronized under free-threading.
include/pybind11/conduit/pybind11_platform_abi_id.h:59: _GLIBCXX_DEBUG changes container layouts in internals but is not part of the ABI id.
common.h:1163: format_descriptor<PyObject*>::value has no pre-C++17 out-of-class definition; undefined symbol in C++14 when ODR-used.
struct_smart_holder.h:347: if the control-block allocation throws, the armed deleter and the still-owning unique_ptr both delete.
class.h:91, :579: pybind11_static_property on 3.12+ replaces property's GC slots, hiding fget and fset from the collector. Reproduced.
class.h:141, :176: _PyType_Lookup plus Py_INCREF races under free-threading; _PyType_LookupRef exists on 3.13+.
class.h:779: make_new_python_type leaks the heap type on every failure path.
pybind11.h:3688: PyErr_NewException result unchecked; pybind11.h:1735: PyModule_AddObject result ignored.
numpy.h:1088, :1267: PyArray_NewCopy_ and PyArray_Squeeze_ NULL results unchecked. numpy.h:148, :1750: registered_dtypes accessed without the internals lock. numpy.h:1415: array_t<T>(const buffer_info&) does not check T against the format.
eigen/tensor.h:158, :188, :384: non-default IndexType does not compile; dtypes compared by identity rather than equivalence.
include/pybind11/embed.h:95: PyConfig_SetBytesArgv runs unconditionally, clobbering a caller-populated argv. subinterpreter.h:237, :344: two unchecked C-API results.
CMakeLists.txt:172: .venv autodetect checks ENV{VIRTUALENV}; the real variable is VIRTUAL_ENV.
tools/pybind11NewTools.cmake:167 and tools/FindPythonLibsNew.cmake:206: message(AUTHOR_WARNING, ...) has a stray comma, demoting it to a plain message.
.pre-commit-config.yaml:94: the remove-tabs exclude regex matches every path, so the hook is a no-op.
tools/pybind11GuessPythonExtSuffix.cmake:75: the debug regex misses the t before d in cpython-313td, and its test script is not run anywhere in CI.
.github/workflows/tests-cibw.yml: no permissions: block. Third-party actions are tag-pinned, and the PyPI publish job with id-token: write uses a moving branch ref.
pybind11/setup_helpers.py:481: ParallelCompile patches the base compile, which MSVCCompiler overrides, so Windows builds stay serial. Line 309 matches package_dir by raw startswith.
CMakeLists.txt:33: the version-type regex expects .dev1; the v3 scheme never matches.
Performance
detail/function_record_pyobject.h:93 via pybind11.h:970: every dispatch calls get_function_record_PyTypeObject(), which takes the internals lock. A reviewer measured roughly 270 ns/call at 8 threads on 3.14t versus 50 ns with a direct cast. self is always the record object, so cast directly in dispatcher.
type_caster_base.h:62: loader_life_support holds an unordered_set by value; MSVC's default constructor allocates, so every call on Windows pays two allocations. Allocate lazily.
type_caster_base.h:1198: every failed load of a registered type does PyObject_HasAttrString plus getattr with a long key, materializing an AttributeError on older CPython. Intern the key and use _PyType_Lookup.
pytypes.h:2618: contains() goes through attr("__contains__"); PySequence_Contains is the in operator and handles the iteration fallback.
pytypes.h:1797: bytes(const str&) copies the UTF-8 buffer into a second bytes object. pytypes.h:1694: str::operator std::string round-trips through a temporary bytes where PyUnicode_AsUTF8AndSize (already used in cast.h:522) would do.
include/pybind11/functional.h:21: func_handle has no move constructor, so loading one std::function does three GIL-acquiring copies and three GIL-acquiring destructions.
pybind11.h:1413: keyword_index re-interns the name on every lookup and never checks for NULL. Intern once at binding time (a function_record layout change, so bump the record ABI id).
pybind11.h:3336: keep_alive_impl copies the type-info vector just to test .empty().
numpy.h:2200: vectorize runs the full buffer protocol per argument per call; line 2111 converts the F-style output through a second PyArray_FromAny; the contiguity check does not skip length-1 dims, so x[:, None] misses the trivial path.
eigen/matrix.h:488, :546: the Ref loader heap-allocates a Map that Ref never references again.
stl.h:327: list_caster iterates via PyIter_Next even for list/tuple. stl.h:107, :132 import collections.abc per miss. stl/filesystem.h:52 imports pathlib per cast.
CI: tests/CMakeLists.txt:285 clones full Eigen history in every job (tarball plus SHA256 is a few MB). No ccache anywhere. reusable-standard.yml:42 still forces a Python download for a June 2025 issue.
Simplifications and modernizations
internals.h:45 requires internals version 12+, so every >= 12 and <= 11 branch is dead (internals.h:311, 344, 424; class.h:229; type_caster_base.h:241-259; pybind11.h:1852, 2421).
stl_bind.h:539-577: an #if 0 block. stl_bind.h:136 and :332 duplicate the wrap_i lambda. stl.h:182 clears the set twice.
include/pybind11/gil.h:33-43: the forward declaration is redundant with internals.h:148.
pytypes.h:1438 and :2713: get_scope_module and get_module_name_if_available are byte-identical.
pytypes.h:549, :999, :1039: dead error branches. pytypes.h:2224 is the last PyObject_CallFunctionObjArgs; pybind11.h:3637 builds a 1-tuple for PyObject_Call. Both can use PyObject_CallOneArg. pytypes.h:1885: bool_::operator bool via PyLong_AsLong instead of comparing to Py_True.
pytypes.h:532-825: PyErr_Fetch and PyErr_Restore could use PyErr_GetRaisedException on 3.12+.
type_caster_base.h:728: a no-op branch with a misleading comment. numpy.h:317: the "Unused" comment now sits on a used slot.
common.h:110-138 asserts clang 3.3 and gcc 4.8 floors while CI tests GCC 9+. The experimental filesystem and optional paths and the Eigen 3.2 fallbacks are never built by CI. cast.h:262, :580: PyPy 3.8-era workarounds worth retesting.
tools/cmake_uninstall.cmake.in:12 uses deprecated exec_program. pybind11Common.cmake:277 keeps a pkg_resources fallback. pyproject.toml lists two classifiers twice and names ruff rules in per-file ignores that are never selected. tools/pybind11Config.cmake.in documents variables that are never set.
Suggested order of attack: the two dispatcher bugs and the ref_small_vector move fix are small and self-contained; the interpreter-restart use-after-free, the smart_holder shared_from_this regression, and the Eigen stride truncation each need a regression test plus a focused fix; the pybind11.pc prefix and the CMP0190 reconfigure flip are one-line CMake fixes.
Finally got a proper report from Fable 5.1. I think my Fable 5 one hit a downgrade.
馃 AI text below 馃
Review of pybind11 master (d87cf0b). Seven reviewers covered the headers, Python package, CMake, and CI. I verified every finding below against the source. Items marked "reproduced" were also exercised against the build tree by a reviewer. No files were changed. Reviewer repro scripts are in the session scratchpad.
Bugs
High
include/pybind11/pybind11.h:1156and:1209: when named args followpy::args, the*argsstub is pushed intocall.argsbut its convert flag is appended last. Each kw-only arg gets its successor's flag, sonoconvert()lands on the wrong argument. Reproduced. Fix: pushfalsewith the stub and append in step 4a only when appending the tuple.pybind11.h:1249: the second-pass loop stops atpos_args, so overloads whose only convertible args are keyword-only never get a converting pass. Reproduced. Iterate tosecond_pass_convert.size().include/pybind11/gil_safe_call_once.h:175-244: nothing resets the cached pointer or the initialized flag when an interpreter is finalized. Afterfinalize_interpreter(); initialize_interpreter()the fast path returns freed memory.py::register_exceptionuses this class, so re-importing an embedded module that registers an exception hits it. Reproduced with ASan. Clear the owner's cache from~call_once_storage.include/pybind11/detail/struct_smart_holder.h:347:from_unique_ptrbuildsshared_ptr<T>(raw, deleter), which always hooksenable_shared_from_this. The void-cast intent fromdetail/init.h:224is defeated, so factories returningunique_ptr<Trampoline>lose thebad_weak_ptrguard and the Python override can silently vanish. Regression from Fixsmart_holdermultiple/virtual inheritance bugs inshared_ptrandunique_ptrto-Python conversions聽#5836, reproduced. Take an explicit flag and construct the owner asshared_ptr<void>.include/pybind11/eigen/matrix.h:181-194: strides that are not a multiple ofsizeof(Scalar)are truncated by integer division. A packed structured-field view maps as contiguous and a mutableRefwrites into the neighbouring field. Reproduced. Treat a non-divisible stride as non-conformable.include/pybind11/iostream.h:152:~pythonbuf()calls into Python; a failingwritethrows out of a destructor and terminates the process. Reproduced. Catch anddiscard_as_unraisable.CMakeLists.txt:387:cmake_path(RELATIVE_PATH ... BASE_DIRECTORY CMAKE_INSTALL_DATAROOTDIR)uses the literal string as the base, so an installedpybind11.pchas an emptyprefix=on CMake 3.20+. Confirmed in the current build tree. Wheels are unaffected because pyproject sets it. Use thewhile()branch unconditionally.Medium
include/pybind11/cast.h:992,:1246,detail/type_caster_base.h:869: the alias flag is read fromsrc, but after an implicit conversion the loaded instance is the converter's temporary.srcmay not be a pybind11 instance at all. Record it inload_valuefromv_h.inst.cast.h:1738:py::castof an unregistered type returns a null object with a pending TypeError instead of throwing. Downstream use givesSystemError.cast.h:1219: returningunique_ptr<T>for ashared_ptr-held or custom-holderTcopy-constructs the holder from unique_ptr bytes.tinfo->holder_enum_vis available to throwcast_errorinstead.include/pybind11/detail/class.h:396:tp_allocresult is unchecked andallocate_layout()can throw out of theextern "C"tp_new. Callingpybind11_object()from Python aborts. Reproduced.include/pybind11/detail/common.h:90:__cpp_lib_launderis tested before<new>or<version>is included, sostd::launderis never used with libstdc++ in C++17 mode. Move the block below the includes.include/pybind11/stl.h:77-100,:199,:316,:451: thetp_nameheuristic matches any user class namedzip,map, ordict_keys; the followingassert(isinstance<iterable>)aborts debug builds and release builds throw instead of returning false. Reproduced. Reject heap types and return false.include/pybind11/stl_bind.h:470: stride-0 (broadcast) buffers giveend == p, so the buffer constructor returns an empty vector. Reproduced.iostream.h:251: calling__enter__twice destroys the first redirect while the second holds its buffer pointer, then segfaults. Reproduced.include/pybind11/chrono.h:46: days plus seconds plus microseconds is computed in int64 microseconds regardless of the target period, sotimedelta.maxoverflows. Line 188 does not compile for floating-point durations. Line 171 leavesmktimeunchecked.chrono.h:105: the Annex-Klocaltime_scall uses MSVC's argument order, so it is a compile error on any libc that actually provides Annex K. Uselocaltime_ron POSIX and drop the global mutex.include/pybind11/numpy.h:1453-1469:at()divides the byte offset byitemsize(), wrong for strides that are not multiples ofsizeof(T). Reproduced. Use*data(index...).numpy.h:1357: no negative-index check, soat(-1)reads before the buffer. Reproduced.numpy.h:785-793: thenumpy_scalarcaster uses function-local statics that runimport numpyunder the static-init guard, the GIL deadlock this header warns about elsewhere. Usegil_safe_call_once_and_store.include/pybind11/eigen/tensor.h:40: modulo byEIGEN_DEFAULT_ALIGN_BYTESis modulo zero withEIGEN_DONT_VECTORIZE.eigen/matrix.h:678-695: the sparse loader throws instead of returning false (the null checks are dead) and callsmutable_data()on inputs it only reads. Reproduced.include/pybind11/pytypes.h:2335:set::addreturns false with the error still set.stl.h:213then reports a generic conversion error instead of "unhashable type". Throw likelist::append.include/pybind11/detail/argument_vector.h:83,:377:ref_small_vectormove leaves the inline source with its size intact, so both destructors DECREF. Latent because the only user relies on copy elision, and the comment asserts the opposite.pybind11.h:1690:PyModule_GetFilenameObjectreturns a new reference stored in ahandle;def_submoduleleaks one per call. Reproduced.pybind11.h:3926:PYBIND11_OVERRIDE_IMPLuses a function-staticcaster for reference returns, a data race under free-threading.thread_localis the cheap fix.tools/pybind11Common.cmake:51withCMakeLists.txt:107: the CMP0190 cross-compiling default flips to OFF on reconfigure becauseoption()caches OFF and the "defaulted" marker is not cached. Reproduced.tools/pybind11Common.cmake:223: COMPAT-modePYTHON_*variables are plainset(), invisible to anadd_subdirectoryparent. Reproduced.tools/pybind11Common.cmake:206:PYBIND11_FINDPYTHON=OLDis truthy, so the documented value selects the NEW mode.tools/pybind11Common.cmake:375-395: the LTO fallbacks run even in the ppc64le/mips64 "do nothing" and Emscripten "no check" branches, andHAS_FLTO_THINis reused for thin and full probes so-flto=thinis never actually tested.Low
pytypes.htuple, dict, list, set, int_, float_, bytes, and memoryview constructors callpybind11_failafter a NULL return, which asserts that no error is set.pytypes.h:1469:PYBIND11_OBJECT_CVTon a null object calls the converter with nullptr (segfault forbool_, silent emptydict).argument_vector.h:294: the assert should beidx < ha.size; exactly 64 kwargs aborts debug builds. Reproduced.detail/function_record_pyobject.h:145-186: rawtp_newandtp_methodsslots let C++ exceptions escape into CPython.pytypes.h:2067,:2137: capsule destructor lambdas throw from a C callback, and only one installserror_scope.type_caster_base.h:701-720: the new reference fromfind_registered_python_instanceis held in ahandleand leaked on thecast_errorpath.type_caster_base.h:545:get_object_handlereturns a borrowed pointer from the instance map, a use-after-free window under free-threading.cast.h:767:tuple_caster::loadkeeps subcaster pointers intoseq[Is]temporaries that die beforeimplicit_cast.detail/internals.h:517-527: nested-exception chaining forerror_already_setandbuiltin_exceptionis overwritten by the followingrestore()orset_error(). Reproduced.internals.h:702: the interpreter TLS key is updated before the state-dict fetch can throw, leaving the fast path pointing at the previous interpreter's internals.internals.h:708,:884:instance_basecheck-and-create andinternals_singleton_pp_are unsynchronized under free-threading.include/pybind11/conduit/pybind11_platform_abi_id.h:59:_GLIBCXX_DEBUGchanges container layouts ininternalsbut is not part of the ABI id.common.h:1163:format_descriptor<PyObject*>::valuehas no pre-C++17 out-of-class definition; undefined symbol in C++14 when ODR-used.struct_smart_holder.h:347: if the control-block allocation throws, the armed deleter and the still-owningunique_ptrboth delete.class.h:91,:579:pybind11_static_propertyon 3.12+ replacesproperty's GC slots, hidingfgetandfsetfrom the collector. Reproduced.class.h:141,:176:_PyType_LookupplusPy_INCREFraces under free-threading;_PyType_LookupRefexists on 3.13+.class.h:779:make_new_python_typeleaks the heap type on every failure path.pybind11.h:3688:PyErr_NewExceptionresult unchecked;pybind11.h:1735:PyModule_AddObjectresult ignored.numpy.h:1088,:1267:PyArray_NewCopy_andPyArray_Squeeze_NULL results unchecked.numpy.h:148,:1750:registered_dtypesaccessed without the internals lock.numpy.h:1415:array_t<T>(const buffer_info&)does not checkTagainst the format.eigen/tensor.h:158,:188,:384: non-defaultIndexTypedoes not compile; dtypes compared by identity rather than equivalence.include/pybind11/embed.h:95:PyConfig_SetBytesArgvruns unconditionally, clobbering a caller-populatedargv.subinterpreter.h:237,:344: two unchecked C-API results.CMakeLists.txt:172:.venvautodetect checksENV{VIRTUALENV}; the real variable isVIRTUAL_ENV.tools/pybind11NewTools.cmake:167andtools/FindPythonLibsNew.cmake:206:message(AUTHOR_WARNING, ...)has a stray comma, demoting it to a plain message..pre-commit-config.yaml:94: theremove-tabsexclude regex matches every path, so the hook is a no-op.tools/pybind11GuessPythonExtSuffix.cmake:75: the debug regex misses thetbeforedincpython-313td, and its test script is not run anywhere in CI..github/workflows/tests-cibw.yml: nopermissions:block. Third-party actions are tag-pinned, and the PyPI publish job withid-token: writeuses a moving branch ref.pybind11/setup_helpers.py:481:ParallelCompilepatches the basecompile, whichMSVCCompileroverrides, so Windows builds stay serial. Line 309 matchespackage_dirby rawstartswith.CMakeLists.txt:33: the version-type regex expects.dev1; the v3 scheme never matches.Performance
detail/function_record_pyobject.h:93viapybind11.h:970: every dispatch callsget_function_record_PyTypeObject(), which takes the internals lock. A reviewer measured roughly 270 ns/call at 8 threads on 3.14t versus 50 ns with a direct cast.selfis always the record object, so cast directly indispatcher.type_caster_base.h:62:loader_life_supportholds anunordered_setby value; MSVC's default constructor allocates, so every call on Windows pays two allocations. Allocate lazily.type_caster_base.h:1198: every failed load of a registered type doesPyObject_HasAttrStringplusgetattrwith a long key, materializing an AttributeError on older CPython. Intern the key and use_PyType_Lookup.pytypes.h:2618:contains()goes throughattr("__contains__");PySequence_Containsis theinoperator and handles the iteration fallback.pytypes.h:1797:bytes(const str&)copies the UTF-8 buffer into a second bytes object.pytypes.h:1694:str::operator std::stringround-trips through a temporary bytes wherePyUnicode_AsUTF8AndSize(already used incast.h:522) would do.include/pybind11/functional.h:21:func_handlehas no move constructor, so loading onestd::functiondoes three GIL-acquiring copies and three GIL-acquiring destructions.pybind11.h:1413:keyword_indexre-interns the name on every lookup and never checks for NULL. Intern once at binding time (a function_record layout change, so bump the record ABI id).pybind11.h:3336:keep_alive_implcopies the type-info vector just to test.empty().numpy.h:2200:vectorizeruns the full buffer protocol per argument per call; line 2111 converts the F-style output through a secondPyArray_FromAny; the contiguity check does not skip length-1 dims, sox[:, None]misses the trivial path.eigen/matrix.h:488,:546: theRefloader heap-allocates aMapthatRefnever references again.stl.h:327:list_casteriterates viaPyIter_Nexteven for list/tuple.stl.h:107,:132importcollections.abcper miss.stl/filesystem.h:52importspathlibper cast.tests/CMakeLists.txt:285clones full Eigen history in every job (tarball plus SHA256 is a few MB). No ccache anywhere.reusable-standard.yml:42still forces a Python download for a June 2025 issue.Simplifications and modernizations
internals.h:45requires internals version 12+, so every>= 12and<= 11branch is dead (internals.h:311, 344, 424; class.h:229; type_caster_base.h:241-259; pybind11.h:1852, 2421).common.h:1387:PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100has been empty since feat!: drop support for Python 3.8, MSVC 2017聽#6110 but has 14 call sites.pybind11.h:3054-3100: thePYBIND11_ENUM_OP_*macros are defined and undefined with no uses since Improve performance of enum_ operators by going back to specific implementation聽#5887.stl_bind.h:539-577: an#if 0block.stl_bind.h:136and:332duplicate thewrap_ilambda.stl.h:182clears the set twice.include/pybind11/gil.h:33-43: the forward declaration is redundant withinternals.h:148.pytypes.h:1438and:2713:get_scope_moduleandget_module_name_if_availableare byte-identical.pytypes.h:549,:999,:1039: dead error branches.pytypes.h:2224is the lastPyObject_CallFunctionObjArgs;pybind11.h:3637builds a 1-tuple forPyObject_Call. Both can usePyObject_CallOneArg.pytypes.h:1885:bool_::operator boolviaPyLong_AsLonginstead of comparing toPy_True.pytypes.h:532-825:PyErr_FetchandPyErr_Restorecould usePyErr_GetRaisedExceptionon 3.12+.type_caster_base.h:728: a no-op branch with a misleading comment.numpy.h:317: the "Unused" comment now sits on a used slot.common.h:110-138asserts clang 3.3 and gcc 4.8 floors while CI tests GCC 9+. The experimental filesystem and optional paths and the Eigen 3.2 fallbacks are never built by CI.cast.h:262,:580: PyPy 3.8-era workarounds worth retesting.tools/cmake_uninstall.cmake.in:12uses deprecatedexec_program.pybind11Common.cmake:277keeps apkg_resourcesfallback.pyproject.tomllists two classifiers twice and names ruff rules in per-file ignores that are never selected.tools/pybind11Config.cmake.indocuments variables that are never set.Suggested order of attack: the two dispatcher bugs and the
ref_small_vectormove fix are small and self-contained; the interpreter-restart use-after-free, the smart_holder shared_from_this regression, and the Eigen stride truncation each need a regression test plus a focused fix; thepybind11.pcprefix and the CMP0190 reconfigure flip are one-line CMake fixes.