From 05b3d712bd29caee4926b99a0c465bcf416d692b Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Sun, 28 Sep 2025 21:55:24 +0000 Subject: [PATCH 01/10] [mypyc] feat: specialize any and all using for loop helpers if possible --- mypyc/irbuild/for_helpers.py | 24 ++++++ mypyc/irbuild/specialize.py | 96 ++++++++++++++++++----- mypyc/test-data/irbuild-basic.test | 118 +++++++++++++++++++++++++++++ mypyc/test-data/run-misc.test | 30 +++++++- 4 files changed, 249 insertions(+), 19 deletions(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index 5edee6cb4df4..14513fab10c7 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -11,6 +11,7 @@ from mypy.nodes import ( ARG_POS, + LDEF, BytesExpr, CallExpr, DictionaryComprehension, @@ -28,6 +29,7 @@ TypeAlias, Var, ) +from mypy.types import Type from mypyc.ir.ops import ( ERR_NEVER, BasicBlock, @@ -1218,3 +1220,25 @@ def get_expr_length_value( return builder.builder.builtin_len(expr_reg, line, use_pyssize_t=use_pyssize_t) # The expression result is known at compile time, so we can use a constant. return Integer(length, c_pyssize_t_rprimitive if use_pyssize_t else short_int_rprimitive) + + +def _is_supported_forloop_iter(builder: IRBuilder, expr: Expression) -> bool: + if is_sequence_rprimitive(builder.node_type(expr)): + return True + return ( + isinstance(expr, CallExpr) + and ( + (isinstance(expr.callee, RefExpr) and expr.callee.fullname in { + "builtins.range", "builtins.enumerate", "builtins.zip", "builtins.reversed" + }) + or (isinstance(expr.callee, MemberExpr) and expr.callee.name in {"keys", "values", "items"}) + ) + ) + + +def _create_iterable_lexpr(index_name: str, index_type: Type) -> NameExpr: + """This helper spoofs a NameExpr to use as the lvalue in one of the for loop helpers.""" + index = NameExpr(index_name) + index.kind = LDEF + index.node = Var(index_name, index_type) + return index diff --git a/mypyc/irbuild/specialize.py b/mypyc/irbuild/specialize.py index 29820787d10c..ba37b85fbb11 100644 --- a/mypyc/irbuild/specialize.py +++ b/mypyc/irbuild/specialize.py @@ -77,7 +77,10 @@ ) from mypyc.irbuild.builder import IRBuilder from mypyc.irbuild.for_helpers import ( + _is_supported_forloop_iter, + _create_iterable_lexpr, comprehension_helper, + for_loop_helper, sequence_from_generator_preallocate_helper, translate_list_comprehension, translate_set_comprehension, @@ -409,29 +412,86 @@ def translate_safe_generator_call( @specialize_function("builtins.any") def translate_any_call(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value | None: - if ( - len(expr.args) == 1 - and expr.arg_kinds == [ARG_POS] - and isinstance(expr.args[0], GeneratorExpr) - ): - return any_all_helper(builder, expr.args[0], builder.false, lambda x: x, builder.true) + if len(expr.args) == 1 and expr.arg_kinds == [ARG_POS]: + arg = expr.args[0] + if isinstance(arg, GeneratorExpr): + return any_all_helper(builder, arg, builder.false, lambda x: x, builder.true) + elif _is_supported_forloop_iter(builder, arg): + retval = Register(bool_rprimitive) + builder.assign(retval, builder.false(), -1) + loop_exit = BasicBlock() + index_name = "__mypyc_any_item__" + + def body_insts() -> None: + true_block = BasicBlock() + false_block = BasicBlock() + builder.add_bool_branch(builder.read(index_reg), true_block, false_block) + builder.activate_block(true_block) + builder.assign(retval, builder.true(), -1) + builder.goto(loop_exit) + builder.activate_block(false_block) + + index_type = builder._analyze_iterable_item_type(arg) + index = _create_iterable_lexpr(index_name, index_type) + index_reg = builder.add_local_reg(index.node, builder.type_to_rtype(index_type)) + + for_loop_helper( + builder, + index, + arg, + body_insts, + None, + is_async=False, + line=expr.line, + ) + builder.goto_and_activate(loop_exit) + return retval return None @specialize_function("builtins.all") def translate_all_call(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value | None: - if ( - len(expr.args) == 1 - and expr.arg_kinds == [ARG_POS] - and isinstance(expr.args[0], GeneratorExpr) - ): - return any_all_helper( - builder, - expr.args[0], - builder.true, - lambda x: builder.unary_op(x, "not", expr.line), - builder.false, - ) + if len(expr.args) == 1 and expr.arg_kinds == [ARG_POS]: + arg = expr.args[0] + if isinstance(arg, GeneratorExpr): + return any_all_helper( + builder, + arg, + builder.true, + lambda x: builder.unary_op(x, "not", expr.line), + builder.false, + ) + + elif _is_supported_forloop_iter(builder, arg): + retval = Register(bool_rprimitive) + builder.assign(retval, builder.true(), -1) + loop_exit = BasicBlock() + index_name = "__mypyc_all_item__" + + def body_insts() -> None: + true_block = BasicBlock() + false_block = BasicBlock() + builder.add_bool_branch(builder.read(index_reg), true_block, false_block) + builder.activate_block(false_block) + builder.assign(retval, builder.false(), -1) + builder.goto(loop_exit) + builder.activate_block(true_block) + + index_type = builder._analyze_iterable_item_type(arg) + index = _create_iterable_lexpr(index_name, index_type) + index_reg = builder.add_local_reg(index.node, builder.type_to_rtype(index_type)) + + for_loop_helper( + builder, + index, + arg, + body_insts, + None, + is_async=False, + line=expr.line, + ) + builder.goto_and_activate(loop_exit) + return retval return None diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index 612f3266fd79..8bb94eb84ca6 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -2812,6 +2812,12 @@ def call_any(l: Iterable[int]) -> bool: def call_all(l: Iterable[int]) -> bool: return all(i == 0 for i in l) +def call_any_helper(l: list[Iterable[int]]) -> bool: + return any([str(i) for i in l]) + +def call_all_helper(l: list[Iterable[int]]) -> bool: + return all([str(i) for i in l]) + [out] def call_any(l): l :: object @@ -2870,6 +2876,118 @@ L6: L7: L8: return r0 +def call_any_helper(l): + l :: list + r0 :: bool + r1 :: native_int + r2 :: list + r3, r4 :: native_int + r5 :: bit + r6, i :: object + r7 :: str + r8, r9, r10 :: native_int + r11 :: bit + r12 :: object + r13, __mypyc_any_item__ :: str + r14 :: bit + r15 :: native_int +L0: + r0 = 0 + r1 = var_object_size l + r2 = PyList_New(r1) + r3 = 0 +L1: + r4 = var_object_size l + r5 = r3 < r4 :: signed + if r5 goto L2 else goto L4 :: bool +L2: + r6 = list_get_item_unsafe l, r3 + i = r6 + r7 = PyObject_Str(i) + CPyList_SetItemUnsafe(r2, r3, r7) +L3: + r8 = r3 + 1 + r3 = r8 + goto L1 +L4: + r9 = 0 +L5: + r10 = var_object_size r2 + r11 = r9 < r10 :: signed + if r11 goto L6 else goto L10 :: bool +L6: + r12 = list_get_item_unsafe r2, r9 + r13 = cast(str, r12) + __mypyc_any_item__ = r13 + r14 = CPyStr_IsTrue(__mypyc_any_item__) + if r14 goto L7 else goto L8 :: bool +L7: + r0 = 1 + goto L11 +L8: +L9: + r15 = r9 + 1 + r9 = r15 + goto L5 +L10: +L11: + return r0 +def call_all_helper(l): + l :: list + r0 :: bool + r1 :: native_int + r2 :: list + r3, r4 :: native_int + r5 :: bit + r6, i :: object + r7 :: str + r8, r9, r10 :: native_int + r11 :: bit + r12 :: object + r13, __mypyc_all_item__ :: str + r14 :: bit + r15 :: native_int +L0: + r0 = 1 + r1 = var_object_size l + r2 = PyList_New(r1) + r3 = 0 +L1: + r4 = var_object_size l + r5 = r3 < r4 :: signed + if r5 goto L2 else goto L4 :: bool +L2: + r6 = list_get_item_unsafe l, r3 + i = r6 + r7 = PyObject_Str(i) + CPyList_SetItemUnsafe(r2, r3, r7) +L3: + r8 = r3 + 1 + r3 = r8 + goto L1 +L4: + r9 = 0 +L5: + r10 = var_object_size r2 + r11 = r9 < r10 :: signed + if r11 goto L6 else goto L10 :: bool +L6: + r12 = list_get_item_unsafe r2, r9 + r13 = cast(str, r12) + __mypyc_all_item__ = r13 + r14 = CPyStr_IsTrue(__mypyc_all_item__) + if r14 goto L8 else goto L7 :: bool +L7: + r0 = 0 + goto L11 +L8: +L9: + r15 = r9 + 1 + r9 = r15 + goto L5 +L10: +L11: + return r0 [case testSum] from typing import Callable, Iterable diff --git a/mypyc/test-data/run-misc.test b/mypyc/test-data/run-misc.test index 129946a4c330..64e9a849f135 100644 --- a/mypyc/test-data/run-misc.test +++ b/mypyc/test-data/run-misc.test @@ -794,8 +794,18 @@ def call_all(l: Iterable[int], val: int = 0) -> int: res = all(i == val for i in l) return 0 if res else 1 +def call_any_with_for_helper(l: Iterable[int], val: int = 0) -> int: + # this listcomp isnt a reasonable real world use but proves the any for loop specializer is good + res = any([i == val for i in l]) + return 0 if res else 1 + +def call_all_with_for_helper(l: Iterable[int], val: int = 0) -> int: + # this listcomp isnt a reasonable real world use but proves the all for loop specializer is good + res = all([i == val for i in l]) + return 0 if res else 1 + [file driver.py] -from native import call_any, call_all, call_any_nested +from native import call_any, call_all, call_any_nested, call_any_with_for_helper, call_all_with_for_helper zeros = [0, 0, 0] ones = [1, 1, 1] @@ -807,24 +817,42 @@ mixed_101 = [1, 0, 1] mixed_110 = [1, 1, 0] assert call_any([]) == 1 +assert call_any_with_for_helper([]) == 1 assert call_any(zeros) == 0 +assert call_any_with_for_helper(zeros) == 0 assert call_any(ones) == 1 +assert call_any_with_for_helper(ones) == 1 assert call_any(mixed_001) == 0 +assert call_any_with_for_helper(mixed_001) == 0 assert call_any(mixed_010) == 0 +assert call_any_with_for_helper(mixed_010) == 0 assert call_any(mixed_100) == 0 +assert call_any_with_for_helper(mixed_100) == 0 assert call_any(mixed_011) == 0 +assert call_any_with_for_helper(mixed_011) == 0 assert call_any(mixed_101) == 0 +assert call_any_with_for_helper(mixed_101) == 0 assert call_any(mixed_110) == 0 +assert call_any_with_for_helper(mixed_110) == 0 assert call_all([]) == 0 +assert call_all_with_for_helper([]) == 0 assert call_all(zeros) == 0 +assert call_all_with_for_helper(zeros) == 0 assert call_all(ones) == 1 +assert call_all_with_for_helper(ones) == 1 assert call_all(mixed_001) == 1 +assert call_all_with_for_helper(mixed_001) == 1 assert call_all(mixed_010) == 1 +assert call_all_with_for_helper(mixed_010) == 1 assert call_all(mixed_100) == 1 +assert call_all_with_for_helper(mixed_100) == 1 assert call_all(mixed_011) == 1 +assert call_all_with_for_helper(mixed_011) == 1 assert call_all(mixed_101) == 1 +assert call_all_with_for_helper(mixed_101) == 1 assert call_all(mixed_110) == 1 +assert call_all_with_for_helper(mixed_110) == 1 assert call_any_nested([[1, 1, 1], [1, 1], []]) == 1 assert call_any_nested([[1, 1, 1], [0, 1], []]) == 0 From 841878cbe7af46329100bb0817e7b36ba6bd339e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 28 Sep 2025 21:58:01 +0000 Subject: [PATCH 02/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypyc/irbuild/for_helpers.py | 17 +++++++++-------- mypyc/irbuild/specialize.py | 24 ++++-------------------- 2 files changed, 13 insertions(+), 28 deletions(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index 14513fab10c7..a936b913ed1f 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -1225,17 +1225,18 @@ def get_expr_length_value( def _is_supported_forloop_iter(builder: IRBuilder, expr: Expression) -> bool: if is_sequence_rprimitive(builder.node_type(expr)): return True - return ( - isinstance(expr, CallExpr) - and ( - (isinstance(expr.callee, RefExpr) and expr.callee.fullname in { - "builtins.range", "builtins.enumerate", "builtins.zip", "builtins.reversed" - }) - or (isinstance(expr.callee, MemberExpr) and expr.callee.name in {"keys", "values", "items"}) + return isinstance(expr, CallExpr) and ( + ( + isinstance(expr.callee, RefExpr) + and expr.callee.fullname + in {"builtins.range", "builtins.enumerate", "builtins.zip", "builtins.reversed"} + ) + or ( + isinstance(expr.callee, MemberExpr) and expr.callee.name in {"keys", "values", "items"} ) ) - + def _create_iterable_lexpr(index_name: str, index_type: Type) -> NameExpr: """This helper spoofs a NameExpr to use as the lvalue in one of the for loop helpers.""" index = NameExpr(index_name) diff --git a/mypyc/irbuild/specialize.py b/mypyc/irbuild/specialize.py index ba37b85fbb11..fa466b463999 100644 --- a/mypyc/irbuild/specialize.py +++ b/mypyc/irbuild/specialize.py @@ -77,8 +77,8 @@ ) from mypyc.irbuild.builder import IRBuilder from mypyc.irbuild.for_helpers import ( - _is_supported_forloop_iter, _create_iterable_lexpr, + _is_supported_forloop_iter, comprehension_helper, for_loop_helper, sequence_from_generator_preallocate_helper, @@ -435,15 +435,7 @@ def body_insts() -> None: index = _create_iterable_lexpr(index_name, index_type) index_reg = builder.add_local_reg(index.node, builder.type_to_rtype(index_type)) - for_loop_helper( - builder, - index, - arg, - body_insts, - None, - is_async=False, - line=expr.line, - ) + for_loop_helper(builder, index, arg, body_insts, None, is_async=False, line=expr.line) builder.goto_and_activate(loop_exit) return retval return None @@ -476,20 +468,12 @@ def body_insts() -> None: builder.assign(retval, builder.false(), -1) builder.goto(loop_exit) builder.activate_block(true_block) - + index_type = builder._analyze_iterable_item_type(arg) index = _create_iterable_lexpr(index_name, index_type) index_reg = builder.add_local_reg(index.node, builder.type_to_rtype(index_type)) - for_loop_helper( - builder, - index, - arg, - body_insts, - None, - is_async=False, - line=expr.line, - ) + for_loop_helper(builder, index, arg, body_insts, None, is_async=False, line=expr.line) builder.goto_and_activate(loop_exit) return retval return None From 02d47392cf3f572f0d3128df65c20325a776938b Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Sun, 28 Sep 2025 18:01:53 -0400 Subject: [PATCH 03/10] Update specialize.py --- mypyc/irbuild/specialize.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mypyc/irbuild/specialize.py b/mypyc/irbuild/specialize.py index fa466b463999..39211931037e 100644 --- a/mypyc/irbuild/specialize.py +++ b/mypyc/irbuild/specialize.py @@ -433,7 +433,7 @@ def body_insts() -> None: index_type = builder._analyze_iterable_item_type(arg) index = _create_iterable_lexpr(index_name, index_type) - index_reg = builder.add_local_reg(index.node, builder.type_to_rtype(index_type)) + index_reg = builder.add_local_reg(index.node, builder.type_to_rtype(index_type)) # type: ignore [arg-type] for_loop_helper(builder, index, arg, body_insts, None, is_async=False, line=expr.line) builder.goto_and_activate(loop_exit) @@ -471,7 +471,7 @@ def body_insts() -> None: index_type = builder._analyze_iterable_item_type(arg) index = _create_iterable_lexpr(index_name, index_type) - index_reg = builder.add_local_reg(index.node, builder.type_to_rtype(index_type)) + index_reg = builder.add_local_reg(index.node, builder.type_to_rtype(index_type)) # type: ignore [arg-type] for_loop_helper(builder, index, arg, body_insts, None, is_async=False, line=expr.line) builder.goto_and_activate(loop_exit) From b0edaeb987d9586a98212e032b77dfe39b9757c8 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Sun, 28 Sep 2025 18:09:30 -0400 Subject: [PATCH 04/10] Update for_helpers.py --- mypyc/irbuild/for_helpers.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index a936b913ed1f..d27b027799dd 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -1225,16 +1225,13 @@ def get_expr_length_value( def _is_supported_forloop_iter(builder: IRBuilder, expr: Expression) -> bool: if is_sequence_rprimitive(builder.node_type(expr)): return True - return isinstance(expr, CallExpr) and ( - ( - isinstance(expr.callee, RefExpr) - and expr.callee.fullname - in {"builtins.range", "builtins.enumerate", "builtins.zip", "builtins.reversed"} - ) - or ( - isinstance(expr.callee, MemberExpr) and expr.callee.name in {"keys", "values", "items"} - ) - ) + if not isinstance(expr, CallExpr): + return False + if isinstance(expr.callee, RefExpr): + return expr.callee.fullname in {"builtins.range", "builtins.enumerate", "builtins.zip", "builtins.reversed"} + elif isinstance(expr.callee, MemberExpr): + return expr.callee.fullname in {"keys", "values", "items"} + return False def _create_iterable_lexpr(index_name: str, index_type: Type) -> NameExpr: From 7d98f340055c4c85738795050ed4459c02e0d69d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 28 Sep 2025 22:10:55 +0000 Subject: [PATCH 05/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypyc/irbuild/for_helpers.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index d27b027799dd..cab26277071b 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -1228,7 +1228,12 @@ def _is_supported_forloop_iter(builder: IRBuilder, expr: Expression) -> bool: if not isinstance(expr, CallExpr): return False if isinstance(expr.callee, RefExpr): - return expr.callee.fullname in {"builtins.range", "builtins.enumerate", "builtins.zip", "builtins.reversed"} + return expr.callee.fullname in { + "builtins.range", + "builtins.enumerate", + "builtins.zip", + "builtins.reversed", + } elif isinstance(expr.callee, MemberExpr): return expr.callee.fullname in {"keys", "values", "items"} return False From 7ef9f182248e99d850993eb42703d294da40d1f3 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Mon, 29 Sep 2025 02:51:40 -0400 Subject: [PATCH 06/10] Update for_helpers.py --- mypyc/irbuild/for_helpers.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index cab26277071b..2c962e49167e 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -1222,7 +1222,7 @@ def get_expr_length_value( return Integer(length, c_pyssize_t_rprimitive if use_pyssize_t else short_int_rprimitive) -def _is_supported_forloop_iter(builder: IRBuilder, expr: Expression) -> bool: +def expr_has_specialized_for_helper(builder: IRBuilder, expr: Expression) -> bool: if is_sequence_rprimitive(builder.node_type(expr)): return True if not isinstance(expr, CallExpr): @@ -1232,14 +1232,13 @@ def _is_supported_forloop_iter(builder: IRBuilder, expr: Expression) -> bool: "builtins.range", "builtins.enumerate", "builtins.zip", - "builtins.reversed", } elif isinstance(expr.callee, MemberExpr): return expr.callee.fullname in {"keys", "values", "items"} return False -def _create_iterable_lexpr(index_name: str, index_type: Type) -> NameExpr: +def create_synthetic_nameexpr(index_name: str, index_type: Type) -> NameExpr: """This helper spoofs a NameExpr to use as the lvalue in one of the for loop helpers.""" index = NameExpr(index_name) index.kind = LDEF From 9c98174cdf07cde5877b452fa8f76ffaa35d3980 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Mon, 29 Sep 2025 02:52:54 -0400 Subject: [PATCH 07/10] Update specialize.py --- mypyc/irbuild/specialize.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/mypyc/irbuild/specialize.py b/mypyc/irbuild/specialize.py index 39211931037e..28e8cf14208e 100644 --- a/mypyc/irbuild/specialize.py +++ b/mypyc/irbuild/specialize.py @@ -77,9 +77,9 @@ ) from mypyc.irbuild.builder import IRBuilder from mypyc.irbuild.for_helpers import ( - _create_iterable_lexpr, - _is_supported_forloop_iter, comprehension_helper, + create_synthetic_nameexpr, + expr_has_specialized_for_helper, for_loop_helper, sequence_from_generator_preallocate_helper, translate_list_comprehension, @@ -416,7 +416,7 @@ def translate_any_call(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> V arg = expr.args[0] if isinstance(arg, GeneratorExpr): return any_all_helper(builder, arg, builder.false, lambda x: x, builder.true) - elif _is_supported_forloop_iter(builder, arg): + elif expr_has_specialized_for_helper(builder, arg): retval = Register(bool_rprimitive) builder.assign(retval, builder.false(), -1) loop_exit = BasicBlock() @@ -432,7 +432,7 @@ def body_insts() -> None: builder.activate_block(false_block) index_type = builder._analyze_iterable_item_type(arg) - index = _create_iterable_lexpr(index_name, index_type) + index = create_synthetic_nameexpr(index_name, index_type) index_reg = builder.add_local_reg(index.node, builder.type_to_rtype(index_type)) # type: ignore [arg-type] for_loop_helper(builder, index, arg, body_insts, None, is_async=False, line=expr.line) @@ -454,7 +454,7 @@ def translate_all_call(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> V builder.false, ) - elif _is_supported_forloop_iter(builder, arg): + elif expr_has_specialized_for_helper(builder, arg): retval = Register(bool_rprimitive) builder.assign(retval, builder.true(), -1) loop_exit = BasicBlock() @@ -470,7 +470,7 @@ def body_insts() -> None: builder.activate_block(true_block) index_type = builder._analyze_iterable_item_type(arg) - index = _create_iterable_lexpr(index_name, index_type) + index = create_synthetic_nameexpr(index_name, index_type) index_reg = builder.add_local_reg(index.node, builder.type_to_rtype(index_type)) # type: ignore [arg-type] for_loop_helper(builder, index, arg, body_insts, None, is_async=False, line=expr.line) From 819efe26367845701e901c1200129b24e17af04e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 29 Sep 2025 06:53:03 +0000 Subject: [PATCH 08/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypyc/irbuild/for_helpers.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index 2c962e49167e..af695d83bcda 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -1228,11 +1228,7 @@ def expr_has_specialized_for_helper(builder: IRBuilder, expr: Expression) -> boo if not isinstance(expr, CallExpr): return False if isinstance(expr.callee, RefExpr): - return expr.callee.fullname in { - "builtins.range", - "builtins.enumerate", - "builtins.zip", - } + return expr.callee.fullname in {"builtins.range", "builtins.enumerate", "builtins.zip"} elif isinstance(expr.callee, MemberExpr): return expr.callee.fullname in {"keys", "values", "items"} return False From 88ab77c34ad87efff28ac806f0e50b83e833ed0b Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Mon, 29 Sep 2025 11:31:33 -0400 Subject: [PATCH 09/10] Update for_helpers.py --- mypyc/irbuild/for_helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index 08c24f594b27..7f9d5db33cb1 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -1235,7 +1235,7 @@ def expr_has_specialized_for_helper(builder: IRBuilder, expr: Expression) -> boo if isinstance(expr.callee, RefExpr): return expr.callee.fullname in {"builtins.range", "builtins.enumerate", "builtins.zip"} elif isinstance(expr.callee, MemberExpr): - return expr.callee.fullname in {"keys", "values", "items"} + return expr.callee.fullname in {"builtins.dict.keys", "builtins.dict.values", "builtins.dict.items"} return False From 24a4d1f912d52f8cd855a0b7f7e71dc8bac7bc9b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 29 Sep 2025 15:32:57 +0000 Subject: [PATCH 10/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypyc/irbuild/for_helpers.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index 7f9d5db33cb1..e2763e40f571 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -1235,7 +1235,11 @@ def expr_has_specialized_for_helper(builder: IRBuilder, expr: Expression) -> boo if isinstance(expr.callee, RefExpr): return expr.callee.fullname in {"builtins.range", "builtins.enumerate", "builtins.zip"} elif isinstance(expr.callee, MemberExpr): - return expr.callee.fullname in {"builtins.dict.keys", "builtins.dict.values", "builtins.dict.items"} + return expr.callee.fullname in { + "builtins.dict.keys", + "builtins.dict.values", + "builtins.dict.items", + } return False