diff --git a/Include/internal/pycore_interp_structs.h b/Include/internal/pycore_interp_structs.h index 3ebc8967c3dc7f..1e69c64bcd1fc0 100644 --- a/Include/internal/pycore_interp_structs.h +++ b/Include/internal/pycore_interp_structs.h @@ -496,7 +496,7 @@ struct _py_func_state { /* For now we hard-code this to a value for which we are confident all the static builtin types will fit (for all builds). */ -#define _Py_MAX_MANAGED_STATIC_BUILTIN_TYPES 200 +#define _Py_MAX_MANAGED_STATIC_BUILTIN_TYPES 201 #define _Py_MAX_MANAGED_STATIC_EXT_TYPES 10 #define _Py_MAX_MANAGED_STATIC_TYPES \ (_Py_MAX_MANAGED_STATIC_BUILTIN_TYPES + _Py_MAX_MANAGED_STATIC_EXT_TYPES) diff --git a/Lib/test/test_opcache.py b/Lib/test/test_opcache.py index 343711ce3a9cef..1f5b0596107704 100644 --- a/Lib/test/test_opcache.py +++ b/Lib/test/test_opcache.py @@ -1548,6 +1548,27 @@ def contains_op_dict(): self.assert_specialized(contains_op_dict, "CONTAINS_OP_DICT") self.assert_no_opcode(contains_op_dict, "CONTAINS_OP") + def contains_op_frozen_dict(): + for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): + a, b = 1, frozendict({1: 2, 2: 5}) + self.assertTrue(a in b) + self.assertFalse(3 in b) + + contains_op_frozen_dict() + self.assert_specialized(contains_op_frozen_dict, "CONTAINS_OP_DICT") + self.assert_no_opcode(contains_op_frozen_dict, "CONTAINS_OP") + + def contains_op_frozen_dict_subclass(): + class MyFrozenDict(frozendict): + pass + for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): + a, b = 1, MyFrozenDict({1: 2, 2: 5}) + self.assertTrue(a in b) + self.assertFalse(3 in b) + + contains_op_frozen_dict_subclass() + self.assert_no_opcode(contains_op_frozen_dict_subclass, "CONTAINS_OP_DICT") + def contains_op_set(): for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): a, b = 1, {1, 2} @@ -1808,6 +1829,27 @@ def binary_subscr_dict(): self.assert_specialized(binary_subscr_dict, "BINARY_OP_SUBSCR_DICT") self.assert_no_opcode(binary_subscr_dict, "BINARY_OP") + def binary_subscr_frozen_dict(): + for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): + a = frozendict({1: 2, 2: 3}) + self.assertEqual(a[1], 2) + self.assertEqual(a[2], 3) + + binary_subscr_frozen_dict() + self.assert_specialized(binary_subscr_frozen_dict, "BINARY_OP_SUBSCR_DICT") + self.assert_no_opcode(binary_subscr_frozen_dict, "BINARY_OP") + + def binary_subscr_frozen_dict_subclass(): + class MyFrozenDict(frozendict): + pass + for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): + a = MyFrozenDict({1: 2, 2: 3}) + self.assertEqual(a[1], 2) + self.assertEqual(a[2], 3) + + binary_subscr_frozen_dict_subclass() + self.assert_no_opcode(binary_subscr_frozen_dict_subclass, "BINARY_OP_SUBSCR_DICT") + def binary_subscr_str_int(): for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): a = "foobar" diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-02-18-21-44-39.gh-issue-141510.7LST2O.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-02-18-21-44-39.gh-issue-141510.7LST2O.rst new file mode 100644 index 00000000000000..87d6a2a6df96a1 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-02-18-21-44-39.gh-issue-141510.7LST2O.rst @@ -0,0 +1 @@ +Update specializer to support frozendict. Patch by Donghee Na. diff --git a/Modules/_testinternalcapi/test_cases.c.h b/Modules/_testinternalcapi/test_cases.c.h index ddd8fcdc231bf1..a9cd0574a596a1 100644 --- a/Modules/_testinternalcapi/test_cases.c.h +++ b/Modules/_testinternalcapi/test_cases.c.h @@ -642,7 +642,7 @@ { nos = stack_pointer[-2]; PyObject *o = PyStackRef_AsPyObjectBorrow(nos); - if (!PyDict_CheckExact(o)) { + if (!PyAnyDict_CheckExact(o)) { UPDATE_MISS_STATS(BINARY_OP); assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); JUMP_TO_PREDICTED(BINARY_OP); @@ -655,7 +655,7 @@ dict_st = nos; PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); PyObject *dict = PyStackRef_AsPyObjectBorrow(dict_st); - assert(PyDict_CheckExact(dict)); + assert(PyAnyDict_CheckExact(dict)); STAT_INC(BINARY_OP, hit); PyObject *res_o; _PyFrame_SetStackPointer(frame, stack_pointer); @@ -5139,7 +5139,7 @@ { tos = stack_pointer[-1]; PyObject *o = PyStackRef_AsPyObjectBorrow(tos); - if (!PyDict_CheckExact(o)) { + if (!PyAnyDict_CheckExact(o)) { UPDATE_MISS_STATS(CONTAINS_OP); assert(_PyOpcode_Deopt[opcode] == (CONTAINS_OP)); JUMP_TO_PREDICTED(CONTAINS_OP); @@ -5152,7 +5152,7 @@ left = stack_pointer[-2]; PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - assert(PyDict_CheckExact(right_o)); + assert(PyAnyDict_CheckExact(right_o)); STAT_INC(CONTAINS_OP, hit); _PyFrame_SetStackPointer(frame, stack_pointer); int res = PyDict_Contains(right_o, left_o); @@ -11482,7 +11482,7 @@ { nos = stack_pointer[-2]; PyObject *o = PyStackRef_AsPyObjectBorrow(nos); - if (!PyDict_CheckExact(o)) { + if (!PyAnyDict_CheckExact(o)) { UPDATE_MISS_STATS(STORE_SUBSCR); assert(_PyOpcode_Deopt[opcode] == (STORE_SUBSCR)); JUMP_TO_PREDICTED(STORE_SUBSCR); diff --git a/Python/bytecodes.c b/Python/bytecodes.c index b461f9b5bea8a6..63a4222264985a 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1058,12 +1058,12 @@ dummy_func( op(_GUARD_NOS_DICT, (nos, unused -- nos, unused)) { PyObject *o = PyStackRef_AsPyObjectBorrow(nos); - EXIT_IF(!PyDict_CheckExact(o)); + EXIT_IF(!PyAnyDict_CheckExact(o)); } op(_GUARD_TOS_DICT, (tos -- tos)) { PyObject *o = PyStackRef_AsPyObjectBorrow(tos); - EXIT_IF(!PyDict_CheckExact(o)); + EXIT_IF(!PyAnyDict_CheckExact(o)); } macro(BINARY_OP_SUBSCR_DICT) = @@ -1073,7 +1073,7 @@ dummy_func( PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); PyObject *dict = PyStackRef_AsPyObjectBorrow(dict_st); - assert(PyDict_CheckExact(dict)); + assert(PyAnyDict_CheckExact(dict)); STAT_INC(BINARY_OP, hit); PyObject *res_o; int rc = PyDict_GetItemRef(dict, sub, &res_o); @@ -2940,7 +2940,7 @@ dummy_func( PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - assert(PyDict_CheckExact(right_o)); + assert(PyAnyDict_CheckExact(right_o)); STAT_INC(CONTAINS_OP, hit); int res = PyDict_Contains(right_o, left_o); if (res < 0) { diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 9dead4eecc7826..1b3de80e4443b1 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -5920,7 +5920,7 @@ _PyStackRef nos; nos = stack_pointer[-2]; PyObject *o = PyStackRef_AsPyObjectBorrow(nos); - if (!PyDict_CheckExact(o)) { + if (!PyAnyDict_CheckExact(o)) { UOP_STAT_INC(uopcode, miss); SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); @@ -5941,7 +5941,7 @@ _PyStackRef _stack_item_0 = _tos_cache0; nos = stack_pointer[-1]; PyObject *o = PyStackRef_AsPyObjectBorrow(nos); - if (!PyDict_CheckExact(o)) { + if (!PyAnyDict_CheckExact(o)) { UOP_STAT_INC(uopcode, miss); _tos_cache0 = _stack_item_0; SET_CURRENT_CACHED_VALUES(1); @@ -5964,7 +5964,7 @@ _PyStackRef _stack_item_1 = _tos_cache1; nos = _stack_item_0; PyObject *o = PyStackRef_AsPyObjectBorrow(nos); - if (!PyDict_CheckExact(o)) { + if (!PyAnyDict_CheckExact(o)) { UOP_STAT_INC(uopcode, miss); _tos_cache1 = _stack_item_1; _tos_cache0 = nos; @@ -5987,7 +5987,7 @@ _PyStackRef _stack_item_2 = _tos_cache2; nos = _stack_item_1; PyObject *o = PyStackRef_AsPyObjectBorrow(nos); - if (!PyDict_CheckExact(o)) { + if (!PyAnyDict_CheckExact(o)) { UOP_STAT_INC(uopcode, miss); _tos_cache2 = _stack_item_2; _tos_cache1 = nos; @@ -6009,7 +6009,7 @@ _PyStackRef tos; tos = stack_pointer[-1]; PyObject *o = PyStackRef_AsPyObjectBorrow(tos); - if (!PyDict_CheckExact(o)) { + if (!PyAnyDict_CheckExact(o)) { UOP_STAT_INC(uopcode, miss); SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); @@ -6029,7 +6029,7 @@ _PyStackRef _stack_item_0 = _tos_cache0; tos = _stack_item_0; PyObject *o = PyStackRef_AsPyObjectBorrow(tos); - if (!PyDict_CheckExact(o)) { + if (!PyAnyDict_CheckExact(o)) { UOP_STAT_INC(uopcode, miss); _tos_cache0 = tos; SET_CURRENT_CACHED_VALUES(1); @@ -6049,7 +6049,7 @@ _PyStackRef _stack_item_1 = _tos_cache1; tos = _stack_item_1; PyObject *o = PyStackRef_AsPyObjectBorrow(tos); - if (!PyDict_CheckExact(o)) { + if (!PyAnyDict_CheckExact(o)) { UOP_STAT_INC(uopcode, miss); _tos_cache1 = tos; _tos_cache0 = _stack_item_0; @@ -6072,7 +6072,7 @@ _PyStackRef _stack_item_2 = _tos_cache2; tos = _stack_item_2; PyObject *o = PyStackRef_AsPyObjectBorrow(tos); - if (!PyDict_CheckExact(o)) { + if (!PyAnyDict_CheckExact(o)) { UOP_STAT_INC(uopcode, miss); _tos_cache2 = tos; _tos_cache1 = _stack_item_1; @@ -6102,7 +6102,7 @@ dict_st = _stack_item_0; PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); PyObject *dict = PyStackRef_AsPyObjectBorrow(dict_st); - assert(PyDict_CheckExact(dict)); + assert(PyAnyDict_CheckExact(dict)); STAT_INC(BINARY_OP, hit); PyObject *res_o; stack_pointer[0] = dict_st; @@ -10393,7 +10393,7 @@ left = _stack_item_0; PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - assert(PyDict_CheckExact(right_o)); + assert(PyAnyDict_CheckExact(right_o)); STAT_INC(CONTAINS_OP, hit); stack_pointer[0] = left; stack_pointer[1] = right; diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 37fa6d679190dd..829a6988954e5f 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -642,7 +642,7 @@ { nos = stack_pointer[-2]; PyObject *o = PyStackRef_AsPyObjectBorrow(nos); - if (!PyDict_CheckExact(o)) { + if (!PyAnyDict_CheckExact(o)) { UPDATE_MISS_STATS(BINARY_OP); assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); JUMP_TO_PREDICTED(BINARY_OP); @@ -655,7 +655,7 @@ dict_st = nos; PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); PyObject *dict = PyStackRef_AsPyObjectBorrow(dict_st); - assert(PyDict_CheckExact(dict)); + assert(PyAnyDict_CheckExact(dict)); STAT_INC(BINARY_OP, hit); PyObject *res_o; _PyFrame_SetStackPointer(frame, stack_pointer); @@ -5139,7 +5139,7 @@ { tos = stack_pointer[-1]; PyObject *o = PyStackRef_AsPyObjectBorrow(tos); - if (!PyDict_CheckExact(o)) { + if (!PyAnyDict_CheckExact(o)) { UPDATE_MISS_STATS(CONTAINS_OP); assert(_PyOpcode_Deopt[opcode] == (CONTAINS_OP)); JUMP_TO_PREDICTED(CONTAINS_OP); @@ -5152,7 +5152,7 @@ left = stack_pointer[-2]; PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - assert(PyDict_CheckExact(right_o)); + assert(PyAnyDict_CheckExact(right_o)); STAT_INC(CONTAINS_OP, hit); _PyFrame_SetStackPointer(frame, stack_pointer); int res = PyDict_Contains(right_o, left_o); @@ -11479,7 +11479,7 @@ { nos = stack_pointer[-2]; PyObject *o = PyStackRef_AsPyObjectBorrow(nos); - if (!PyDict_CheckExact(o)) { + if (!PyAnyDict_CheckExact(o)) { UPDATE_MISS_STATS(STORE_SUBSCR); assert(_PyOpcode_Deopt[opcode] == (STORE_SUBSCR)); JUMP_TO_PREDICTED(STORE_SUBSCR); diff --git a/Python/specialize.c b/Python/specialize.c index 5ba016f83ea077..4d3ba4acbbf038 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -2287,7 +2287,7 @@ _Py_Specialize_BinaryOp(_PyStackRef lhs_st, _PyStackRef rhs_st, _Py_CODEUNIT *in } } } - if (PyDict_CheckExact(lhs)) { + if (PyAnyDict_CheckExact(lhs)) { specialize(instr, BINARY_OP_SUBSCR_DICT); return; } @@ -2767,7 +2767,7 @@ _Py_Specialize_ContainsOp(_PyStackRef value_st, _Py_CODEUNIT *instr) assert(ENABLE_SPECIALIZATION); assert(_PyOpcode_Caches[CONTAINS_OP] == INLINE_CACHE_ENTRIES_COMPARE_OP); - if (PyDict_CheckExact(value)) { + if (PyAnyDict_CheckExact(value)) { specialize(instr, CONTAINS_OP_DICT); return; }