fix: avoid copy constructor instantiation in shared_ptr fallback cast#6028
Merged
rwgk merged 6 commits intopybind:masterfrom Apr 6, 2026
Merged
Conversation
f7b3345 to
ac840cf
Compare
rwgk
reviewed
Apr 5, 2026
Collaborator
rwgk
left a comment
There was a problem hiding this comment.
Thanks for the reproducer! I can confirm that it reproduces the compilation error on my workstation (Ubuntu clang version 18.1.3 (1ubuntu1)).
Looks good to me. My suggestions are just to make things more intuitive and easier to discover.
Name the non-owning generic cast path so callers do not have to rediscover that reference-like policies must pass null copy/move constructor callbacks. This keeps the shared_ptr reference_internal fallback self-documenting and points future maintainers toward the safe API. Made-with: Cursor
Contributor
Author
|
If you're happy then I'm happy too |
Use __has_warning for the Clang-only regression test so older compiler jobs skip unsupported warning groups instead of failing with -Wunknown-warning-option. A simple __clang_major__ >= 13 guard would be shorter, but it bakes in a version cutoff; __has_warning is slightly more verbose while being more robust to vendor builds, backports, and future packaging differences. Made-with: Cursor
Collaborator
|
I'm happy, but older clang versions need some more convincing: commit 87bf519 For easy reference: clang 5 and 11 failures observed here: https://github.com/pybind/pybind11/actions/runs/24018953764/job/70043719610?pr=6028 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Original bug at https://github.com/pytorch/pytorch/actions/runs/24007816974/job/70014373231.
I haven't manually validated this fix yet, it might be all slop. I don't think the tests are really in the right shape to test/validate the problem... I'll take a closer look tonight, but for nowHere's GPT-5.4's explanation:Problem and fix
Summary
A regression in pybind11's
std::shared_ptr<T>caster caused thereference_internalfallback path to instantiate copy/move constructor helpers even when no copy or move was actually needed.This broke downstream projects such as PyTorch when building with Clang warning settings like:
-Werror=deprecated-copy-with-user-provided-dtor-Werror=deprecated-copy-with-dtorWhat regressed
In
include/pybind11/cast.h, the fallback path for returningstd::shared_ptr<T>to Python whenTis bound with neitherstd::shared_ptrholder norpy::smart_holderwas changed to:That looks reasonable, but
type_caster_base<type>::cast(...)forwards through code that eagerly instantiates:make_copy_constructor(...)make_move_constructor(...)via
type_caster_base::cast(const cast_sources &, ...).Even though the
reference_internalpath does not use those constructors at runtime, their instantiation is enough for Clang to diagnose implicit copy construction for types that have a user-declared destructor.Downstream impact
PyTorch hit this in CI after updating pybind11. The failing patterns came directly from code such as:
torch/csrc/jit/python/python_tracer.cppm.def("_get_tracing_state", []() { return getTracingState(); });torch/csrc/jit/python/script_init.cppstd::shared_ptr<InterfaceType>fromCompilationUnitThe relevant types include user-declared destructors, e.g.:
TracingState::~TracingState();InterfaceType::~InterfaceType() override = default;Clang then reports errors originating from
pybind11/detail/type_caster_base.h:1682, where the copy-constructor helper lambda is instantiated.Root cause
The bug is not in lifetime handling itself. The lifetime model is correct: for this path, pybind11 should create a Python object referring to the existing C++ object and tie its lifetime to
parentusingreference_internal.The regression is that the implementation routed through a helper that always prepares copy/move constructor callbacks, even though this fallback path is strictly non-owning and does not need them.
Fix
Use
type_caster_generic::cast(...)directly for theparent/reference_internalfallback and pass null copy/move constructor callbacks:This preserves the intended behavior:
reference_internalkeep-alive behavior remains intactTests added
Two kinds of tests were added:
1. Existing smart-holder property aliasing coverage
In
tests/test_class_sh_property.py, the original aliasing test was kept and a stronger multi-read version was added:2. Direct downstream-style PyTorch regression reproducer
New files:
tests/pybind11_pytorch_regressions.cpptests/test_pytorch_downstream_regressions.pyThese extract the failing PyTorch patterns into a minimal pybind11 test module:
TracingState-like type returned from a free function asstd::shared_ptr<T>InterfaceType-like object returned from aCompilationUnitmethod asstd::shared_ptr<T>For the dedicated test target, Clang is given:
-Werror=deprecated-copy-with-user-provided-dtor-Werror=deprecated-copy-with-dtorThis ensures the regression is caught at compile time.
Suggested changelog entry: