Skip to content

[mypyc] Fix allow_interpreted_subclasses not seeing subclass attrs#21013

Open
VaggelisD wants to merge 2 commits intopython:masterfrom
VaggelisD:fix-interpreted-subclass-attr
Open

[mypyc] Fix allow_interpreted_subclasses not seeing subclass attrs#21013
VaggelisD wants to merge 2 commits intopython:masterfrom
VaggelisD:fix-interpreted-subclass-attr

Conversation

@VaggelisD
Copy link
Contributor

When a compiled class with allow_interpreted_subclasses=True has methods that access self.ATTR via direct C struct slots, interpreted subclasses that override ATTR in their class __dict__ are ignored; the compiled method always reads the base class default from the slot.

The fix: In visit_get_attr for non-property attribute access, check if the instance is a mypyc-compiled type (via a new CPy_TPFLAGS_MYPYC_COMPILED tp_flags bit). If not, fall back to PyObject_GenericGetAttr which respects the MRO and finds the subclass override.

Using tp_flags rather than an exact type check ensures compiled subclasses retain fast direct struct access, while only interpreted subclasses hit the GenericGetAttr slow path.

For unboxed types (bool, int), the PyObject* result is unboxed to the expected C type.

VaggelisD and others added 2 commits March 12, 2026 15:15
…e overrides

When a compiled class with allow_interpreted_subclasses=True has methods that
access self.ATTR via direct C struct slots, interpreted subclasses that override
ATTR in their class __dict__ are ignored — the compiled method always reads the
base class default from the slot.

Fix: in visit_get_attr for non-property attribute access, check if the instance
is a mypyc-compiled type (via a new CPy_TPFLAGS_MYPYC_COMPILED tp_flags bit).
If not, fall back to PyObject_GenericGetAttr which respects the MRO and finds
the subclass override.

Using tp_flags rather than an exact type check ensures compiled subclasses
retain fast direct struct access, while only interpreted subclasses hit the
GenericGetAttr slow path.

For unboxed types (bool, int), the PyObject* result is unboxed to the expected
C type.
Comment on lines +5811 to +5813
# Test that compiled subclasses of a class with allow_interpreted_subclasses=True
# can correctly access parent instance attributes via direct struct access
# (not falling back to PyObject_GenericGetAttr).
Copy link
Collaborator

Choose a reason for hiding this comment

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

the run test doesn't prove that it's using direct struct access because it would work either way, just one way is slower. i don't think there's a way to test this well so maybe just reword the comment.

i guess one way to confirm it's still using the fast path is to benchmark the accesses on master and with this change. could you do that and add the results to the description?

Comment on lines +149 to +152
// Flag bit set on all mypyc-compiled types. Used to distinguish compiled
// subclasses (safe for direct struct access) from interpreted subclasses
// (need PyObject_GenericGetAttr fallback) in allow_interpreted_subclasses mode.
#define CPy_TPFLAGS_MYPYC_COMPILED (1UL << 20)
Copy link
Collaborator

Choose a reason for hiding this comment

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

this bit is already used by cpython https://github.com/python/cpython/blob/main/Include/object.h#L611. from what i saw the flag is only used by object_new which isn't used by mypyc-compiled types but i think it would be better to pick a bit that's not used at all in case there's compatibility issues later.

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.

2 participants