Skip to content

gh-149239: Deopt LOAD_ATTR_CLASS_WITH_METACLASS_CHECK on reassigning __class__#149269

Open
sobolevn wants to merge 5 commits intopython:mainfrom
sobolevn:issue-149239
Open

gh-149239: Deopt LOAD_ATTR_CLASS_WITH_METACLASS_CHECK on reassigning __class__#149269
sobolevn wants to merge 5 commits intopython:mainfrom
sobolevn:issue-149239

Conversation

@sobolevn
Copy link
Copy Markdown
Member

@sobolevn sobolevn commented May 2, 2026

Copy link
Copy Markdown
Member

@Fidget-Spinner Fidget-Spinner left a comment

Choose a reason for hiding this comment

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

LG, just one test nit.

Comment thread Lib/test/test_capi/test_opt.py Outdated
@read-the-docs-community
Copy link
Copy Markdown

read-the-docs-community Bot commented May 3, 2026

Documentation build overview

📚 cpython-previews | 🛠️ Build #32540399 | 📁 Comparing 945a561 against main (1fc2b38)

  🔍 Preview build  

38 files changed · + 1 added · ± 37 modified

+ Added

± Modified

Comment thread .jit-stamp Outdated
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Note: this file is still there for some reason 🤔
Should it be ignored in #149311 ?

Copy link
Copy Markdown
Member

@Fidget-Spinner Fidget-Spinner left a comment

Choose a reason for hiding this comment

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

LGTM. Let's wait for @markshannon though.

Comment thread Python/bytecodes.c
op(_LOAD_ATTR_CLASS, (descr/4, owner -- attr)) {
PyTypeObject *descr_type = Py_TYPE(descr);
PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner);
EXIT_IF((descr_type->tp_flags & Py_TPFLAGS_IMMUTABLETYPE) == 0
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why are we not exiting if (descr_type->tp_flags & Py_TPFLAGS_IMMUTABLETYPE) != 0?
If you made Descriptor immutable in the test above, this guard wouldn't trigger and the test would fail

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The enum specialization supports mutable types

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Should not EXIT_IF(descr_type != (PyTypeObject *)owner_o); be enough? I applied this change.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

With this change test_call_len_known_length starts to fail :/
I will revert it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You need to add a test case for changing the class of an object to an immutable descriptor class.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

So, here's the logic:

It is checked that type is mutable here: specialize_class_load_attr in Python/specialize.c

bool metaclass_check = false;
if ((Py_TYPE(cls)->tp_flags & Py_TPFLAGS_IMMUTABLETYPE) == 0) {
    metaclass_check = true;
}

So, we can rely on this check in _LOAD_ATTR_CLASS which is shared between LOAD_ATTR_CLASS and LOAD_ATTR_CLASS_WITH_METACLASS_CHECK. The separation is based on (Py_TYPE(cls)->tp_flags & Py_TPFLAGS_IMMUTABLETYPE) == 0

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

That checks that the class the object is changed from is immutable, but not whether the class it is changed to is immutable.

@markshannon markshannon changed the title gh-149239: Deopt LOAD_ATTR_CLASS_WITH_METACLASS_CHECK on reassining __class__ gh-149239: Deopt LOAD_ATTR_CLASS_WITH_METACLASS_CHECK on reassigning __class__ May 5, 2026
@sobolevn
Copy link
Copy Markdown
Member Author

sobolevn commented May 5, 2026

Turns out, there's another problem: when I moved tests from test_capi/test_opt.py to test_opcache.py, they now pass with and without my changes. And does not actually test anything 🙈

I need a bit of help here, @Fidget-Spinner :)

@NekoAsakura
Copy link
Copy Markdown
Contributor

Hi @sobolevn, the bug only fires when the same specialised instruction is re-executed after __class__ is reassigned. Don't split it into two functions, and assert_no_opcode is the wrong assertion. You need assert on the return value (self.assertEqual(f1(), "descr")) instead.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants