Skip to content

[mypyc] Allow native classes to support weak refs - #22387

Open
JukkaL wants to merge 1 commit into
masterfrom
mypyc-weakref
Open

JukkaL wants to merge 1 commit into
masterfrom
mypyc-weakref

Conversation

@JukkaL

@JukkaL JukkaL commented Nov 6, 2026

Copy link
Copy Markdown
Collaborator

Fixes mypyc/mypyc#1102.

Native classes support weak references if they opt in using @mypyc_attr(weakrefs=True), which adds a weak reference list field to the object struct. The base class of a hierarchy must opt in, since the offset of the field must be the same in all subclasses.

Native subclasses of non-native classes (other than dict/Exception subclasses) always support weak references, like interpreted subclasses.

Previously some of these classes were weak-referenceable (on 3.11 and earlier, or if the base class supported them), but tp_dealloc never cleared the weak references, so they resurrected freed memory. Now tp_dealloc clears weak references for all classes that support them.

On 3.11 and earlier, callable classes (used for nested functions, lambdas and singledispatch functions) and native dict/Exception subclasses used to accept weak references, but the result crashed or corrupted memory once the object died. They now reject weak references, as on 3.12+, and their instances are one pointer smaller.

Fixes mypyc/mypyc#1102.

Native classes support weak references if they opt in using
`@mypyc_attr(weakrefs=True)`, which adds a weak reference list field
to the object struct. The base class of a hierarchy must opt in, since
the offset of the field must be the same in all subclasses.

Native subclasses of non-native classes (other than dict/Exception
subclasses) always support weak references, like interpreted
subclasses. They already have `__dict__` storage, and the weak reference
list goes next to it at no additional memory cost: in the managed
pre-header on 3.12+ (Py_TPFLAGS_MANAGED_WEAKREF), and in a trailing slot
after `__dict__` on older versions. This works the same way whether or not
the base class supports weak references, so mypyc doesn't need to know
anything about the base class.

Previously some of these classes were weak-referenceable (on 3.11 and
earlier, or if the base class supported them), but tp_dealloc never cleared
the weak references, so they resurrected freed memory. Now tp_dealloc
clears weak references for all classes that support them.

On 3.11 and earlier, callable classes (used for nested functions, lambdas
and singledispatch functions) and native dict/Exception subclasses used to
accept weak references, but the result crashed or corrupted memory once the
object died. They now reject weak references, as on 3.12+, and their
instances are one pointer smaller.
Comment thread mypyc/ir/class_ir.py
or not self.is_generated
or self.has_dict
# For the __weakref__ getset
or (self.is_ext_class and not self.is_trait and self.supports_weakrefs())

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same condition as has_weakrefs in emitclass.py - could move it to a helper method.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Compatibility issues with weakrefs

2 participants