Skip to content

Commit d9a5115

Browse files
committed
gh-149239: Deopt LOAD_ATTR_CLASS_WITH_METACLASS_CHECK on reassining __class__
1 parent bb5e41e commit d9a5115

8 files changed

Lines changed: 94 additions & 1 deletion

File tree

.jit-stamp

Whitespace-only changes.

Include/internal/pycore_uop_metadata.h

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/test/test_capi/test_opt.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import contextlib
2+
import enum
23
import itertools
34
import sys
45
import textwrap
@@ -3511,6 +3512,50 @@ def f(n):
35113512
self.assertNotIn("_LOAD_ATTR_METHOD_NO_DICT", uops)
35123513
self.assertIn("_LOAD_CONST_INLINE_BORROW", uops)
35133514

3515+
def test_load_attr_class_with_metaclass_check(self):
3516+
# LOAD_ATTR_CLASS_WITH_METACLASS_CHECK must check
3517+
# for `__class__` writes, see gh-149239
3518+
class ColorMeta(enum.EnumType):
3519+
pass
3520+
3521+
class Color(enum.IntEnum, metaclass=ColorMeta):
3522+
RED = 1
3523+
3524+
red = Color.RED
3525+
3526+
def f1(n):
3527+
for _ in range(n):
3528+
assert Color.RED == 1
3529+
return n
3530+
3531+
res, ex = self._run_with_optimizer(f1, TIER2_THRESHOLD)
3532+
self.assertIsNotNone(ex)
3533+
self.assertEqual(res, TIER2_THRESHOLD)
3534+
uops = get_opnames(ex)
3535+
self.assertIn("_CHECK_ATTR_CLASS", uops)
3536+
self.assertIn("_GUARD_TYPE_VERSION", uops)
3537+
3538+
# Reassign the `__class__` attr to deopt:
3539+
class Descriptor(enum.IntEnum):
3540+
RED = 1
3541+
3542+
def __get__(self, obj, owner):
3543+
return "descr"
3544+
3545+
red.__class__ = Descriptor
3546+
3547+
def f2(n):
3548+
for _ in range(n):
3549+
assert Color.RED == 'descr'
3550+
return n
3551+
3552+
res, ex = self._run_with_optimizer(f2, TIER2_THRESHOLD)
3553+
self.assertIsNotNone(ex)
3554+
self.assertEqual(res, TIER2_THRESHOLD)
3555+
uops = get_opnames(ex)
3556+
self.assertNotIn("_CHECK_ATTR_CLASS", uops)
3557+
self.assertNotIn("_GUARD_TYPE_VERSION", uops)
3558+
35143559
def test_cached_load_special(self):
35153560
class CM:
35163561
def __enter__(self):
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Deopt ``LOAD_ATTR_CLASS_WITH_METACLASS_CHECK`` opcode on ``__class__``
2+
reassigning.

Modules/_testinternalcapi/test_cases.c.h

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/bytecodes.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2976,6 +2976,11 @@ dummy_func(
29762976
}
29772977

29782978
op(_LOAD_ATTR_CLASS, (descr/4, owner -- attr)) {
2979+
PyTypeObject *descr_type = Py_TYPE(descr);
2980+
PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner);
2981+
EXIT_IF((descr_type->tp_flags & Py_TPFLAGS_IMMUTABLETYPE) == 0
2982+
&& descr_type != (PyTypeObject *)owner_o);
2983+
29792984
STAT_INC(LOAD_ATTR, hit);
29802985
assert(descr != NULL);
29812986
attr = PyStackRef_FromPyObjectNew(descr);

Python/executor_cases.c.h

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/generated_cases.c.h

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)