diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index 51234a2e40f54f..2e7485e89fdac5 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -2695,6 +2695,28 @@ def recursive_wrapper_4569(): pass """)) + def test_attribute_changes_are_watched(self): + # Just running to make sure it doesn't crash. + script_helper.assert_python_ok("-c", textwrap.dedent(""" + from concurrent.futures import ThreadPoolExecutor + from unittest import TestCase + NTHREADS = 6 + BOTTOM = 0 + TOP = 1250000 + class A: + attr = 10**1000 + class TestType(TestCase): + def read(id0): + for _ in range(BOTTOM, TOP): + A.attr + def write(id0): + x = A.attr + x += 1 + A.attr = x + with ThreadPoolExecutor(NTHREADS) as pool: + pool.submit(read, (1,)) + pool.submit(write, (1,)) + """)) def global_identity(x): return x diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-12-05-14-33-54.gh-issue-142276.H4j8hP.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-05-14-33-54.gh-issue-142276.H4j8hP.rst new file mode 100644 index 00000000000000..aa8e3da33580c8 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-05-14-33-54.gh-issue-142276.H4j8hP.rst @@ -0,0 +1 @@ +Fix missing type watcher when promoting attribute loads to constants in the JIT. Patch by Ken Jin. Reproducer by Yuancheng Jiang. diff --git a/Python/optimizer_analysis.c b/Python/optimizer_analysis.c index 51722556554609..c4afc6bd29086f 100644 --- a/Python/optimizer_analysis.c +++ b/Python/optimizer_analysis.c @@ -242,7 +242,7 @@ eliminate_pop_guard(_PyUOpInstruction *this_instr, bool exit) } static JitOptRef -lookup_attr(JitOptContext *ctx, _PyUOpInstruction *this_instr, +lookup_attr(JitOptContext *ctx, _PyBloomFilter *dependencies, _PyUOpInstruction *this_instr, PyTypeObject *type, PyObject *name, uint16_t immortal, uint16_t mortal) { @@ -252,6 +252,8 @@ lookup_attr(JitOptContext *ctx, _PyUOpInstruction *this_instr, if (lookup) { int opcode = _Py_IsImmortal(lookup) ? immortal : mortal; REPLACE_OP(this_instr, opcode, 0, (uintptr_t)lookup); + PyType_Watch(TYPE_WATCHER_ID, (PyObject *)type); + _Py_BloomFilter_Add(dependencies, type); return sym_new_const(ctx, lookup); } } diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index 06fa8a4522a499..9eee3c69da7c86 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -607,7 +607,7 @@ dummy_func(void) { (void)descr; PyTypeObject *type = (PyTypeObject *)sym_get_const(ctx, owner); PyObject *name = get_co_name(ctx, oparg >> 1); - attr = lookup_attr(ctx, this_instr, type, name, + attr = lookup_attr(ctx, dependencies, this_instr, type, name, _POP_TOP_LOAD_CONST_INLINE_BORROW, _POP_TOP_LOAD_CONST_INLINE); } @@ -616,7 +616,7 @@ dummy_func(void) { (void)descr; PyTypeObject *type = sym_get_type(owner); PyObject *name = get_co_name(ctx, oparg >> 1); - attr = lookup_attr(ctx, this_instr, type, name, + attr = lookup_attr(ctx, dependencies, this_instr, type, name, _POP_TOP_LOAD_CONST_INLINE_BORROW, _POP_TOP_LOAD_CONST_INLINE); } @@ -625,7 +625,7 @@ dummy_func(void) { (void)descr; PyTypeObject *type = sym_get_type(owner); PyObject *name = get_co_name(ctx, oparg >> 1); - attr = lookup_attr(ctx, this_instr, type, name, + attr = lookup_attr(ctx, dependencies, this_instr, type, name, _POP_TOP_LOAD_CONST_INLINE_BORROW, _POP_TOP_LOAD_CONST_INLINE); } @@ -634,7 +634,7 @@ dummy_func(void) { (void)descr; PyTypeObject *type = sym_get_type(owner); PyObject *name = get_co_name(ctx, oparg >> 1); - attr = lookup_attr(ctx, this_instr, type, name, + attr = lookup_attr(ctx, dependencies, this_instr, type, name, _LOAD_CONST_UNDER_INLINE_BORROW, _LOAD_CONST_UNDER_INLINE); self = owner; @@ -644,7 +644,7 @@ dummy_func(void) { (void)descr; PyTypeObject *type = sym_get_type(owner); PyObject *name = get_co_name(ctx, oparg >> 1); - attr = lookup_attr(ctx, this_instr, type, name, + attr = lookup_attr(ctx, dependencies, this_instr, type, name, _LOAD_CONST_UNDER_INLINE_BORROW, _LOAD_CONST_UNDER_INLINE); self = owner; @@ -654,7 +654,7 @@ dummy_func(void) { (void)descr; PyTypeObject *type = sym_get_type(owner); PyObject *name = get_co_name(ctx, oparg >> 1); - attr = lookup_attr(ctx, this_instr, type, name, + attr = lookup_attr(ctx, dependencies, this_instr, type, name, _LOAD_CONST_UNDER_INLINE_BORROW, _LOAD_CONST_UNDER_INLINE); self = owner; diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index 85bebed58677ed..36130cdb893ab1 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -1848,7 +1848,7 @@ (void)descr; PyTypeObject *type = (PyTypeObject *)sym_get_const(ctx, owner); PyObject *name = get_co_name(ctx, oparg >> 1); - attr = lookup_attr(ctx, this_instr, type, name, + attr = lookup_attr(ctx, dependencies, this_instr, type, name, _POP_TOP_LOAD_CONST_INLINE_BORROW, _POP_TOP_LOAD_CONST_INLINE); stack_pointer[-1] = attr; @@ -2495,7 +2495,7 @@ (void)descr; PyTypeObject *type = sym_get_type(owner); PyObject *name = get_co_name(ctx, oparg >> 1); - attr = lookup_attr(ctx, this_instr, type, name, + attr = lookup_attr(ctx, dependencies, this_instr, type, name, _LOAD_CONST_UNDER_INLINE_BORROW, _LOAD_CONST_UNDER_INLINE); self = owner; @@ -2516,7 +2516,7 @@ (void)descr; PyTypeObject *type = sym_get_type(owner); PyObject *name = get_co_name(ctx, oparg >> 1); - attr = lookup_attr(ctx, this_instr, type, name, + attr = lookup_attr(ctx, dependencies, this_instr, type, name, _LOAD_CONST_UNDER_INLINE_BORROW, _LOAD_CONST_UNDER_INLINE); self = owner; @@ -2536,7 +2536,7 @@ (void)descr; PyTypeObject *type = sym_get_type(owner); PyObject *name = get_co_name(ctx, oparg >> 1); - attr = lookup_attr(ctx, this_instr, type, name, + attr = lookup_attr(ctx, dependencies, this_instr, type, name, _POP_TOP_LOAD_CONST_INLINE_BORROW, _POP_TOP_LOAD_CONST_INLINE); stack_pointer[-1] = attr; @@ -2551,7 +2551,7 @@ (void)descr; PyTypeObject *type = sym_get_type(owner); PyObject *name = get_co_name(ctx, oparg >> 1); - attr = lookup_attr(ctx, this_instr, type, name, + attr = lookup_attr(ctx, dependencies, this_instr, type, name, _POP_TOP_LOAD_CONST_INLINE_BORROW, _POP_TOP_LOAD_CONST_INLINE); stack_pointer[-1] = attr; @@ -2571,7 +2571,7 @@ (void)descr; PyTypeObject *type = sym_get_type(owner); PyObject *name = get_co_name(ctx, oparg >> 1); - attr = lookup_attr(ctx, this_instr, type, name, + attr = lookup_attr(ctx, dependencies, this_instr, type, name, _LOAD_CONST_UNDER_INLINE_BORROW, _LOAD_CONST_UNDER_INLINE); self = owner;