From 2db5a2ad703faedafcda092938bde12d9d52d6f3 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Fri, 12 Sep 2025 23:48:52 +0000 Subject: [PATCH 01/10] [mypyc] feat: exact_dict_rprimitive --- mypyc/ir/rtypes.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/mypyc/ir/rtypes.py b/mypyc/ir/rtypes.py index 34824a59cd5c..6ae26b5dedf9 100644 --- a/mypyc/ir/rtypes.py +++ b/mypyc/ir/rtypes.py @@ -487,8 +487,13 @@ def __hash__(self) -> int: "builtins.list", is_unboxed=False, is_refcounted=True, may_be_immortal=False ) -# Python dict object (or an instance of a subclass of dict). +# Python dict object. +exact_dict_rprimitive: Final = RPrimitive( + "builtins.dict[exact]", is_unboxed=False, is_refcounted=True) +) + dict_rprimitive: Final = RPrimitive("builtins.dict", is_unboxed=False, is_refcounted=True) +# An instance of a subclass of dict. # Python set object (or an instance of a subclass of set). set_rprimitive: Final = RPrimitive("builtins.set", is_unboxed=False, is_refcounted=True) @@ -608,7 +613,14 @@ def is_list_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]: def is_dict_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]: - return isinstance(rtype, RPrimitive) and rtype.name == "builtins.dict" + return isinstance(rtype, RPrimitive) and rtype.name in ( + "builtins.dict", + "builtins.dict[exact]", + ) + + +def is_exact_dict_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]: + return isinstance(rtype, RPrimitive) and rtype.name == "builtins.dict[exact]" def is_set_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]: From f43c0728e6a880aa1ecd38ed8414bcad747a739e Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Fri, 12 Sep 2025 23:48:52 +0000 Subject: [PATCH 02/10] Update rt_subtype.py --- mypyc/rt_subtype.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mypyc/rt_subtype.py b/mypyc/rt_subtype.py index 004e56ed75bc..01619158a954 100644 --- a/mypyc/rt_subtype.py +++ b/mypyc/rt_subtype.py @@ -27,6 +27,8 @@ RVoid, is_bit_rprimitive, is_bool_rprimitive, + is_dict_rprimitive, + is_exact_dict_rprimitive, is_int_rprimitive, is_short_int_rprimitive, ) @@ -58,6 +60,8 @@ def visit_rprimitive(self, left: RPrimitive) -> bool: return True if is_bit_rprimitive(left) and is_bool_rprimitive(self.right): return True + if is_exact_dict_rprimitive(left) and is_dict_rprimitive(self.right): + return True return left is self.right def visit_rtuple(self, left: RTuple) -> bool: From 3942c6289a2e123b8e0519faa84b0626fd6c65ad Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Fri, 12 Sep 2025 23:48:52 +0000 Subject: [PATCH 03/10] Update subtype.py --- mypyc/subtype.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mypyc/subtype.py b/mypyc/subtype.py index 726a48d7a01d..6ad4dcb1d19e 100644 --- a/mypyc/subtype.py +++ b/mypyc/subtype.py @@ -1,3 +1,4 @@ + """Subtype check for RTypes.""" from __future__ import annotations @@ -14,6 +15,8 @@ RVoid, is_bit_rprimitive, is_bool_rprimitive, + is_dict_rprimitive, + is_exact_dict_rprimitive, is_fixed_width_rtype, is_int_rprimitive, is_object_rprimitive, @@ -67,6 +70,9 @@ def visit_rprimitive(self, left: RPrimitive) -> bool: elif is_fixed_width_rtype(left): if is_int_rprimitive(right): return True + elif is_exact_dict_rprimitive(left): + if is_dict_rprimitive(right): + return True return left is right def visit_rtuple(self, left: RTuple) -> bool: From 11768a9301000cd13358e8ba06b88f649a06c369 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 12 Sep 2025 23:48:52 +0000 Subject: [PATCH 04/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypyc/subtype.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mypyc/subtype.py b/mypyc/subtype.py index 6ad4dcb1d19e..6feb4b83b5cf 100644 --- a/mypyc/subtype.py +++ b/mypyc/subtype.py @@ -1,4 +1,3 @@ - """Subtype check for RTypes.""" from __future__ import annotations From 555d83aab0d659e68f594930ded92e458c9b7631 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Fri, 12 Sep 2025 23:48:52 +0000 Subject: [PATCH 05/10] Update ircheck.py --- mypyc/analysis/ircheck.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mypyc/analysis/ircheck.py b/mypyc/analysis/ircheck.py index 6980c9cee419..0af0c4b0ec28 100644 --- a/mypyc/analysis/ircheck.py +++ b/mypyc/analysis/ircheck.py @@ -197,7 +197,10 @@ def can_coerce_to(src: RType, dest: RType) -> bool: if isinstance(src, RPrimitive): # If either src or dest is a disjoint type, then they must both be. if src.name in disjoint_types and dest.name in disjoint_types: - return src.name == dest.name + return src.name == dest.name or ( + src.name in ("builtins.dict", "builtins.dict[exact]") + and dest.name in ("builtins.dict", "builtins.dict[exact]") + ) return src.size == dest.size if isinstance(src, RInstance): return is_object_rprimitive(dest) From 665e2e6bcf0a5928e0df10aad70114409cb80fd7 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Fri, 12 Sep 2025 23:48:52 +0000 Subject: [PATCH 06/10] Update rtypes.py --- mypyc/ir/rtypes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypyc/ir/rtypes.py b/mypyc/ir/rtypes.py index 6ae26b5dedf9..e5db2211d859 100644 --- a/mypyc/ir/rtypes.py +++ b/mypyc/ir/rtypes.py @@ -492,8 +492,8 @@ def __hash__(self) -> int: "builtins.dict[exact]", is_unboxed=False, is_refcounted=True) ) -dict_rprimitive: Final = RPrimitive("builtins.dict", is_unboxed=False, is_refcounted=True) # An instance of a subclass of dict. +dict_rprimitive: Final = RPrimitive("builtins.dict", is_unboxed=False, is_refcounted=True) # Python set object (or an instance of a subclass of set). set_rprimitive: Final = RPrimitive("builtins.set", is_unboxed=False, is_refcounted=True) From 0e7ef8dce648e33cfa68e2daa178f026f3a2e960 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Fri, 12 Sep 2025 23:48:52 +0000 Subject: [PATCH 07/10] Update rtypes.py --- mypyc/ir/rtypes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypyc/ir/rtypes.py b/mypyc/ir/rtypes.py index e5db2211d859..7cb3d59e2ea9 100644 --- a/mypyc/ir/rtypes.py +++ b/mypyc/ir/rtypes.py @@ -489,7 +489,7 @@ def __hash__(self) -> int: # Python dict object. exact_dict_rprimitive: Final = RPrimitive( - "builtins.dict[exact]", is_unboxed=False, is_refcounted=True) + "builtins.dict[exact]", is_unboxed=False, is_refcounted=True ) # An instance of a subclass of dict. From 3aca0f3cd68815c9175daac2990ac11191c106a9 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Mon, 18 Aug 2025 17:10:36 -0400 Subject: [PATCH 08/10] Update misc_ops.py --- mypyc/primitives/misc_ops.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mypyc/primitives/misc_ops.py b/mypyc/primitives/misc_ops.py index 8e6e450c64dc..63a48b6e2906 100644 --- a/mypyc/primitives/misc_ops.py +++ b/mypyc/primitives/misc_ops.py @@ -13,6 +13,7 @@ c_pyssize_t_rprimitive, cstring_rprimitive, dict_rprimitive, + exact_dict_rprimitive, float_rprimitive, int_rprimitive, none_rprimitive, @@ -161,7 +162,7 @@ # Get the sys.modules dictionary get_module_dict_op = custom_op( arg_types=[], - return_type=dict_rprimitive, + return_type=exact_dict_rprimitive, c_function_name="PyImport_GetModuleDict", error_kind=ERR_NEVER, is_borrowed=True, From 328a11db21ce472b15d6d9d2e7987e33a5ba2737 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Tue, 19 Aug 2025 10:58:33 -0400 Subject: [PATCH 09/10] update IR --- mypyc/test-data/irbuild-basic.test | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index 612f3266fd79..340d1c230031 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -3290,7 +3290,7 @@ def root(): r4 :: str r5 :: object r6 :: str - r7 :: dict + r7 :: dict[exact] r8 :: str r9 :: object r10 :: i32 @@ -3301,7 +3301,7 @@ def root(): r16 :: str r17 :: object r18 :: str - r19 :: dict + r19 :: dict[exact] r20 :: str r21 :: object r22 :: i32 @@ -3347,12 +3347,12 @@ def submodule(): r4 :: str r5 :: object r6 :: str - r7 :: dict + r7 :: dict[exact] r8 :: str r9 :: object r10 :: i32 r11 :: bit - r12 :: dict + r12 :: dict[exact] r13 :: str r14 :: object r15 :: str From a16b0b96177c9b99b6354804af7d4b4c9b2e400e Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Fri, 3 Oct 2025 21:25:15 +0000 Subject: [PATCH 10/10] [mypyc] feat: use exact_dict_rprimitive for non-native class dict rtype --- mypyc/irbuild/classdef.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mypyc/irbuild/classdef.py b/mypyc/irbuild/classdef.py index 324b44b95dc4..34238bd7dc7b 100644 --- a/mypyc/irbuild/classdef.py +++ b/mypyc/irbuild/classdef.py @@ -50,7 +50,7 @@ from mypyc.ir.rtypes import ( RType, bool_rprimitive, - dict_rprimitive, + exact_dict_rprimitive, is_none_rprimitive, is_object_rprimitive, is_optional_type, @@ -611,7 +611,7 @@ def setup_non_ext_dict( py_hasattr_op, [metaclass, builder.load_str("__prepare__")], cdef.line ) - non_ext_dict = Register(dict_rprimitive) + non_ext_dict = Register(exact_dict_rprimitive) true_block, false_block, exit_block = BasicBlock(), BasicBlock(), BasicBlock() builder.add_bool_branch(has_prepare, true_block, false_block)