This commit is contained in:
sobolevn 2026-05-03 21:19:11 +01:00 committed by GitHub
commit 3638f52bde
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 89 additions and 1 deletions

0
.jit-stamp Normal file
View file

View file

@ -227,7 +227,7 @@ const uint32_t _PyUop_Flags[MAX_UOP_ID+1] = {
[_LOAD_ATTR_WITH_HINT] = HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_EXIT_FLAG,
[_LOAD_ATTR_SLOT] = HAS_EXIT_FLAG,
[_CHECK_ATTR_CLASS] = HAS_EXIT_FLAG,
[_LOAD_ATTR_CLASS] = HAS_ESCAPES_FLAG,
[_LOAD_ATTR_CLASS] = HAS_EXIT_FLAG | HAS_ESCAPES_FLAG,
[_LOAD_ATTR_PROPERTY_FRAME] = HAS_ARG_FLAG | HAS_EXIT_FLAG,
[_LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN_FRAME] = HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_EXIT_FLAG,
[_GUARD_DORV_NO_DICT] = HAS_EXIT_FLAG,

View file

@ -1,3 +1,4 @@
import enum
import copy
import pickle
import dis
@ -2114,6 +2115,45 @@ def load_enum_member():
self.assert_specialized(load_enum_member,
"LOAD_ATTR_CLASS_WITH_METACLASS_CHECK")
@cpython_only
@requires_specialization
def test_load_attr_class_with_metaclass_check_149239(self):
# LOAD_ATTR_CLASS_WITH_METACLASS_CHECK must check
# for `__class__` writes, see gh-149239
class ColorMeta(enum.EnumType):
pass
class Color(enum.IntEnum, metaclass=ColorMeta):
RED = 1
red = Color.RED
def f1():
for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD):
assert Color.RED == 1
f1()
self.assert_specialized(f1,
"LOAD_ATTR_CLASS_WITH_METACLASS_CHECK")
# Reassign the `__class__` attr to deopt:
class Descriptor(enum.IntEnum):
RED = 1
def __get__(self, obj, owner):
return "descr"
red.__class__ = Descriptor
def f2():
for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD):
assert Color.RED == 'descr'
f2()
self.assert_no_opcode(f2,
"LOAD_ATTR_CLASS_WITH_METACLASS_CHECK")
if __name__ == "__main__":
unittest.main()

View file

@ -0,0 +1,2 @@
Deopt ``LOAD_ATTR_CLASS_WITH_METACLASS_CHECK`` opcode on ``__class__``
reassigning.

View file

@ -8360,6 +8360,14 @@
// _LOAD_ATTR_CLASS
{
PyObject *descr = read_obj(&this_instr[6].cache);
PyTypeObject *descr_type = Py_TYPE(descr);
PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner);
if ((descr_type->tp_flags & Py_TPFLAGS_IMMUTABLETYPE) == 0
&& descr_type != (PyTypeObject *)owner_o) {
UPDATE_MISS_STATS(LOAD_ATTR);
assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR));
JUMP_TO_PREDICTED(LOAD_ATTR);
}
STAT_INC(LOAD_ATTR, hit);
assert(descr != NULL);
attr = PyStackRef_FromPyObjectNew(descr);
@ -8428,6 +8436,14 @@
// _LOAD_ATTR_CLASS
{
PyObject *descr = read_obj(&this_instr[6].cache);
PyTypeObject *descr_type = Py_TYPE(descr);
PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner);
if ((descr_type->tp_flags & Py_TPFLAGS_IMMUTABLETYPE) == 0
&& descr_type != (PyTypeObject *)owner_o) {
UPDATE_MISS_STATS(LOAD_ATTR);
assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR));
JUMP_TO_PREDICTED(LOAD_ATTR);
}
STAT_INC(LOAD_ATTR, hit);
assert(descr != NULL);
attr = PyStackRef_FromPyObjectNew(descr);

View file

@ -2976,6 +2976,11 @@ dummy_func(
}
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
&& descr_type != (PyTypeObject *)owner_o);
STAT_INC(LOAD_ATTR, hit);
assert(descr != NULL);
attr = PyStackRef_FromPyObjectNew(descr);

View file

@ -11967,6 +11967,15 @@
_PyStackRef _stack_item_0 = _tos_cache0;
owner = _stack_item_0;
PyObject *descr = (PyObject *)CURRENT_OPERAND0_64();
PyTypeObject *descr_type = Py_TYPE(descr);
PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner);
if ((descr_type->tp_flags & Py_TPFLAGS_IMMUTABLETYPE) == 0
&& descr_type != (PyTypeObject *)owner_o) {
UOP_STAT_INC(uopcode, miss);
_tos_cache0 = owner;
SET_CURRENT_CACHED_VALUES(1);
JUMP_TO_JUMP_TARGET();
}
STAT_INC(LOAD_ATTR, hit);
assert(descr != NULL);
attr = PyStackRef_FromPyObjectNew(descr);

View file

@ -8359,6 +8359,14 @@
// _LOAD_ATTR_CLASS
{
PyObject *descr = read_obj(&this_instr[6].cache);
PyTypeObject *descr_type = Py_TYPE(descr);
PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner);
if ((descr_type->tp_flags & Py_TPFLAGS_IMMUTABLETYPE) == 0
&& descr_type != (PyTypeObject *)owner_o) {
UPDATE_MISS_STATS(LOAD_ATTR);
assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR));
JUMP_TO_PREDICTED(LOAD_ATTR);
}
STAT_INC(LOAD_ATTR, hit);
assert(descr != NULL);
attr = PyStackRef_FromPyObjectNew(descr);
@ -8427,6 +8435,14 @@
// _LOAD_ATTR_CLASS
{
PyObject *descr = read_obj(&this_instr[6].cache);
PyTypeObject *descr_type = Py_TYPE(descr);
PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner);
if ((descr_type->tp_flags & Py_TPFLAGS_IMMUTABLETYPE) == 0
&& descr_type != (PyTypeObject *)owner_o) {
UPDATE_MISS_STATS(LOAD_ATTR);
assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR));
JUMP_TO_PREDICTED(LOAD_ATTR);
}
STAT_INC(LOAD_ATTR, hit);
assert(descr != NULL);
attr = PyStackRef_FromPyObjectNew(descr);