https://github.com/python/cpython/commit/3e2f5c133f37f13f627404f3cbd54a5fc163887a
commit: 3e2f5c133f37f13f627404f3cbd54a5fc163887a
branch: main
author: Donghee Na <[email protected]>
committer: corona10 <[email protected]>
date: 2026-02-19T01:10:53+09:00
summary:
gh-141510: Update specializer to support frozendict (gh-144949)
files:
A
Misc/NEWS.d/next/Core_and_Builtins/2026-02-18-21-44-39.gh-issue-141510.7LST2O.rst
M Include/internal/pycore_interp_structs.h
M Lib/test/test_opcache.py
M Modules/_testinternalcapi/test_cases.c.h
M Python/bytecodes.c
M Python/executor_cases.c.h
M Python/generated_cases.c.h
M Python/specialize.c
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;
}
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]