From 684e800396099183f83379c41880b37b499a02bc Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Thu, 25 Sep 2025 20:54:04 -0400 Subject: [PATCH 01/17] [mypyc] feat: extend get_expr_length for enumerate, map, zip, and range This PR is pretty simple, I just extended get_expr_length to work for a few more obvious cases: - `builtins.enumerate` - `builtins.map` - `builtins.zip` - `builtins.range` This PR is ready for review. Are you going to want tests for all of these? I don't want to spend time now until I know for sure. --- mypyc/irbuild/for_helpers.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index 5edee6cb4df4..348765e690dd 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -1200,10 +1200,23 @@ def get_expr_length(expr: Expression) -> int | None: and expr.node.has_explicit_value ): return len(expr.node.final_value) + elif isinstance(expr, CallExpr) and isinstance(callee := expr.callee, NameExpr): + fullname = callee.fullname + if fullname == "builtins.enumerate" and len(expr.args) == 1: + return get_expr_length(expr.args[0]) + elif fullname == "builtins.map" and len(expr.args) == 2: + return get_expr_length(expr.args[1]) + elif fullname == "builtins.zip" and expr.args: + arg_lengths = [get_expr_length(arg) for arg in expr.args] + if all(arg is not None for arg in arg_lengths): + return min(arg_lengths) + elif fullname == "builtins.range" and all(isinstance(arg, IntExpr) for arg in expr.args): + return len(range(*(arg.value for arg in expr.args))) + # TODO: extend this, passing length of listcomp and genexp should have worthwhile # performance boost and can be (sometimes) figured out pretty easily. set and dict # comps *can* be done as well but will need special logic to consider the possibility - # of key conflicts. Range, enumerate, zip are all simple logic. + # of key conflicts. return None From c792337d2f98166dae6986385e4c835d1980f39d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 26 Sep 2025 00:55:59 +0000 Subject: [PATCH 02/17] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- 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 348765e690dd..2dd00d21861e 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -1212,7 +1212,7 @@ def get_expr_length(expr: Expression) -> int | None: return min(arg_lengths) elif fullname == "builtins.range" and all(isinstance(arg, IntExpr) for arg in expr.args): return len(range(*(arg.value for arg in expr.args))) - + # TODO: extend this, passing length of listcomp and genexp should have worthwhile # performance boost and can be (sometimes) figured out pretty easily. set and dict # comps *can* be done as well but will need special logic to consider the possibility From 63196179910b15ece7dac4c3e97b6ec742cb8aa2 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Thu, 25 Sep 2025 21:04:06 -0400 Subject: [PATCH 03/17] fix: missing import --- mypyc/irbuild/for_helpers.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index 2dd00d21861e..dc516ec72ab8 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -16,6 +16,7 @@ DictionaryComprehension, Expression, GeneratorExpr, + IntExpr, ListExpr, Lvalue, MemberExpr, From 48ebc0dc3b408e2c7d9f4dc86d75be716a60ceff Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Thu, 25 Sep 2025 21:12:18 -0400 Subject: [PATCH 04/17] fix mypy errs --- mypyc/irbuild/for_helpers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index dc516ec72ab8..00de814d77a5 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -1210,9 +1210,9 @@ def get_expr_length(expr: Expression) -> int | None: elif fullname == "builtins.zip" and expr.args: arg_lengths = [get_expr_length(arg) for arg in expr.args] if all(arg is not None for arg in arg_lengths): - return min(arg_lengths) + return min(arg_lengths) # type: ignore [type-var] elif fullname == "builtins.range" and all(isinstance(arg, IntExpr) for arg in expr.args): - return len(range(*(arg.value for arg in expr.args))) + return len(range(*(arg.value for arg in expr.args))) # type: ignore [attr-defined] # TODO: extend this, passing length of listcomp and genexp should have worthwhile # performance boost and can be (sometimes) figured out pretty easily. set and dict From 3e31189752435041d5190195c00e5714172e92ef Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Thu, 25 Sep 2025 22:07:00 -0400 Subject: [PATCH 05/17] support list and tuple call expr --- 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 00de814d77a5..f9e724006b39 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -1203,7 +1203,7 @@ def get_expr_length(expr: Expression) -> int | None: return len(expr.node.final_value) elif isinstance(expr, CallExpr) and isinstance(callee := expr.callee, NameExpr): fullname = callee.fullname - if fullname == "builtins.enumerate" and len(expr.args) == 1: + if fullname in ("builtins.list", "builtins.tuple", "builtins.enumerate") and len(expr.args) == 1: return get_expr_length(expr.args[0]) elif fullname == "builtins.map" and len(expr.args) == 2: return get_expr_length(expr.args[1]) From d38d4b4d5becb0190cfa2beb7e469778932caed8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 26 Sep 2025 02:08:21 +0000 Subject: [PATCH 06/17] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypyc/irbuild/for_helpers.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index f9e724006b39..186d545ae08b 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -1203,7 +1203,10 @@ def get_expr_length(expr: Expression) -> int | None: return len(expr.node.final_value) elif isinstance(expr, CallExpr) and isinstance(callee := expr.callee, NameExpr): fullname = callee.fullname - if fullname in ("builtins.list", "builtins.tuple", "builtins.enumerate") and len(expr.args) == 1: + if ( + fullname in ("builtins.list", "builtins.tuple", "builtins.enumerate") + and len(expr.args) == 1 + ): return get_expr_length(expr.args[0]) elif fullname == "builtins.map" and len(expr.args) == 2: return get_expr_length(expr.args[1]) From 4940cfc5f805b4c525479378a0a47a8cad444f4e Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Thu, 25 Sep 2025 23:19:28 -0400 Subject: [PATCH 07/17] validate arg kind --- 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 186d545ae08b..d83ddc10100c 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -1201,7 +1201,7 @@ def get_expr_length(expr: Expression) -> int | None: and expr.node.has_explicit_value ): return len(expr.node.final_value) - elif isinstance(expr, CallExpr) and isinstance(callee := expr.callee, NameExpr): + elif isinstance(expr, CallExpr) and isinstance(callee := expr.callee, NameExpr) and all(kind == ARG_POS for kind in expr.arg_kinds): fullname = callee.fullname if ( fullname in ("builtins.list", "builtins.tuple", "builtins.enumerate") From 50179797a631a006cdca443865a2d8325f7a9405 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 26 Sep 2025 03:20:47 +0000 Subject: [PATCH 08/17] [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 d83ddc10100c..7ebc0b26684b 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -1201,7 +1201,11 @@ def get_expr_length(expr: Expression) -> int | None: and expr.node.has_explicit_value ): return len(expr.node.final_value) - elif isinstance(expr, CallExpr) and isinstance(callee := expr.callee, NameExpr) and all(kind == ARG_POS for kind in expr.arg_kinds): + elif ( + isinstance(expr, CallExpr) + and isinstance(callee := expr.callee, NameExpr) + and all(kind == ARG_POS for kind in expr.arg_kinds) + ): fullname = callee.fullname if ( fullname in ("builtins.list", "builtins.tuple", "builtins.enumerate") From b6f098dfa5367540b05c5406143891d6d9a559b9 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Thu, 25 Sep 2025 23:21:59 -0400 Subject: [PATCH 09/17] support sorted and reversed --- 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 7ebc0b26684b..ddcd5e0ca267 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -1208,7 +1208,7 @@ def get_expr_length(expr: Expression) -> int | None: ): fullname = callee.fullname if ( - fullname in ("builtins.list", "builtins.tuple", "builtins.enumerate") + fullname in ("builtins.list", "builtins.tuple", "builtins.enumerate", "builtins.sorted", "builtins.reversed") and len(expr.args) == 1 ): return get_expr_length(expr.args[0]) From 3389573a39bb64fd4cf2a115c8bd52f6907ce626 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 26 Sep 2025 03:23:25 +0000 Subject: [PATCH 10/17] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypyc/irbuild/for_helpers.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index ddcd5e0ca267..cceece120cc7 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -1208,7 +1208,14 @@ def get_expr_length(expr: Expression) -> int | None: ): fullname = callee.fullname if ( - fullname in ("builtins.list", "builtins.tuple", "builtins.enumerate", "builtins.sorted", "builtins.reversed") + fullname + in ( + "builtins.list", + "builtins.tuple", + "builtins.enumerate", + "builtins.sorted", + "builtins.reversed", + ) and len(expr.args) == 1 ): return get_expr_length(expr.args[0]) From e788168bfed81a6b8b75045eb3751d855b63fcc8 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Fri, 26 Sep 2025 07:37:17 -0400 Subject: [PATCH 11/17] testTupleBuiltFromLengthCheckable --- mypyc/test-data/irbuild-tuple.test | 47 ++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/mypyc/test-data/irbuild-tuple.test b/mypyc/test-data/irbuild-tuple.test index 081cc1b174c9..b4333202ed82 100644 --- a/mypyc/test-data/irbuild-tuple.test +++ b/mypyc/test-data/irbuild-tuple.test @@ -829,6 +829,53 @@ L4: a = r1 return 1 +[case testTupleBuiltFromLengthCheckable] +from typing import Tuple + +def f(val: bool) -> bool: + return not val + +def test() -> None: + a = tuple((i, x) for i, (_, x) in zip(map(str, range(5)), enumerate(sorted(reversed("abc"))))) +[out] +def f(val): + val, r0 :: bool +L0: + r0 = val ^ 1 + return r0 +def test(source): + source :: tuple + r0 :: native_int + r1 :: tuple + r2 :: native_int + r3 :: bit + r4 :: object + r5, x, r6 :: bool + r7 :: object + r8 :: native_int + a :: tuple +L0: + r0 = var_object_size source + r1 = PyTuple_New(r0) + r2 = 0 +L1: + r3 = r2 < r0 :: signed + if r3 goto L2 else goto L4 :: bool +L2: + r4 = CPySequenceTuple_GetItemUnsafe(source, r2) + r5 = unbox(bool, r4) + x = r5 + r6 = f(x) + r7 = box(bool, r6) + CPySequenceTuple_SetItemUnsafe(r1, r2, r7) +L3: + r8 = r2 + 1 + r2 = r8 + goto L1 +L4: + a = r1 + return 1 + [case testTupleBuiltFromStars] from typing import Final From 68c81ce2e68093fdbb3fb192f1ca04b0d16bb2f3 Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Fri, 26 Sep 2025 12:54:53 +0000 Subject: [PATCH 12/17] fix: add map fixture --- mypyc/test-data/fixtures/ir.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mypyc/test-data/fixtures/ir.py b/mypyc/test-data/fixtures/ir.py index a4b4f3ce2b1f..55529ba51772 100644 --- a/mypyc/test-data/fixtures/ir.py +++ b/mypyc/test-data/fixtures/ir.py @@ -308,6 +308,11 @@ def __iter__(self) -> Iterator[int]: pass def __len__(self) -> int: pass def __next__(self) -> int: pass +class map(Iterator[_S]): + def __init__(self, func: Callable[[_T], _S], iterable: Iterable[_T]) -> None: pass + def __iter__(self) -> Self: pass + def __next__(self) -> _S: pass + class property: def __init__(self, fget: Optional[Callable[[Any], Any]] = ..., fset: Optional[Callable[[Any, Any], None]] = ..., From 5f4ef4df7b80eedfa542f43e0d1003768af4a368 Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Fri, 26 Sep 2025 12:57:59 +0000 Subject: [PATCH 13/17] fix: IR --- mypyc/test-data/irbuild-tuple.test | 125 +++++++++++++++++++++++------ 1 file changed, 99 insertions(+), 26 deletions(-) diff --git a/mypyc/test-data/irbuild-tuple.test b/mypyc/test-data/irbuild-tuple.test index b4333202ed82..4e9422d9c603 100644 --- a/mypyc/test-data/irbuild-tuple.test +++ b/mypyc/test-data/irbuild-tuple.test @@ -836,44 +836,117 @@ def f(val: bool) -> bool: return not val def test() -> None: - a = tuple((i, x) for i, (_, x) in zip(map(str, range(5)), enumerate(sorted(reversed("abc"))))) + # this tuple is created from a very complex genexp but we can still compute the length and preallocate the tuple + a = tuple( + x + for x + in zip( + map(str, range(5)), + enumerate(sorted(reversed(tuple("abcdefg")))) + ) + ) [out] def f(val): val, r0 :: bool L0: r0 = val ^ 1 return r0 -def test(source): - source :: tuple - r0 :: native_int - r1 :: tuple - r2 :: native_int - r3 :: bit - r4 :: object - r5, x, r6 :: bool - r7 :: object - r8 :: native_int - a :: tuple +def test(): + r0 :: list + r1, r2, r3 :: object + r4 :: object[1] + r5 :: object_ptr + r6 :: object + r7 :: range + r8 :: object + r9 :: str + r10 :: object + r11 :: object[2] + r12 :: object_ptr + r13 :: object + r14 :: str + r15 :: tuple + r16 :: object + r17 :: str + r18 :: object + r19 :: object[1] + r20 :: object_ptr + r21 :: object + r22 :: list + r23 :: object + r24 :: str + r25 :: object + r26 :: object[1] + r27 :: object_ptr + r28, r29 :: object + r30 :: str + r31 :: object + r32 :: object[2] + r33 :: object_ptr + r34, r35, r36 :: object + r37, x :: tuple[str, tuple[int, str]] + r38 :: object + r39 :: i32 + r40, r41 :: bit + r42, a :: tuple L0: - r0 = var_object_size source - r1 = PyTuple_New(r0) - r2 = 0 + r0 = PyList_New(0) + r1 = load_address PyUnicode_Type + r2 = load_address PyRange_Type + r3 = object 5 + r4 = [r3] + r5 = load_address r4 + r6 = PyObject_Vectorcall(r2, r5, 1, 0) + keep_alive r3 + r7 = cast(range, r6) + r8 = builtins :: module + r9 = 'map' + r10 = CPyObject_GetAttr(r8, r9) + r11 = [r1, r7] + r12 = load_address r11 + r13 = PyObject_Vectorcall(r10, r12, 2, 0) + keep_alive r1, r7 + r14 = 'abcdefg' + r15 = PySequence_Tuple(r14) + r16 = builtins :: module + r17 = 'reversed' + r18 = CPyObject_GetAttr(r16, r17) + r19 = [r15] + r20 = load_address r19 + r21 = PyObject_Vectorcall(r18, r20, 1, 0) + keep_alive r15 + r22 = CPySequence_Sort(r21) + r23 = builtins :: module + r24 = 'enumerate' + r25 = CPyObject_GetAttr(r23, r24) + r26 = [r22] + r27 = load_address r26 + r28 = PyObject_Vectorcall(r25, r27, 1, 0) + keep_alive r22 + r29 = builtins :: module + r30 = 'zip' + r31 = CPyObject_GetAttr(r29, r30) + r32 = [r13, r28] + r33 = load_address r32 + r34 = PyObject_Vectorcall(r31, r33, 2, 0) + keep_alive r13, r28 + r35 = PyObject_GetIter(r34) L1: - r3 = r2 < r0 :: signed - if r3 goto L2 else goto L4 :: bool + r36 = PyIter_Next(r35) + if is_error(r36) goto L4 else goto L2 L2: - r4 = CPySequenceTuple_GetItemUnsafe(source, r2) - r5 = unbox(bool, r4) - x = r5 - r6 = f(x) - r7 = box(bool, r6) - CPySequenceTuple_SetItemUnsafe(r1, r2, r7) + r37 = unbox(tuple[str, tuple[int, str]], r36) + x = r37 + r38 = box(tuple[str, tuple[int, str]], x) + r39 = PyList_Append(r0, r38) + r40 = r39 >= 0 :: signed L3: - r8 = r2 + 1 - r2 = r8 goto L1 L4: - a = r1 + r41 = CPy_NoErrOccurred() +L5: + r42 = PyList_AsTuple(r0) + a = r42 return 1 [case testTupleBuiltFromStars] From a847067708961300f9da8b893c7143bb5b005a87 Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Fri, 26 Sep 2025 12:56:09 +0000 Subject: [PATCH 14/17] [mypyc] feat: support non-sequence inputs with known len in `sequence_from_generator_preallocate_helper` --- mypyc/irbuild/for_helpers.py | 29 ++++- mypyc/test-data/irbuild-tuple.test | 201 ++++++++++++++++------------- 2 files changed, 136 insertions(+), 94 deletions(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index cceece120cc7..997f06108104 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -231,22 +231,41 @@ def sequence_from_generator_preallocate_helper( implementation. """ if len(gen.sequences) == 1 and len(gen.indices) == 1 and len(gen.condlists[0]) == 0: - rtype = builder.node_type(gen.sequences[0]) + sequence_expr = gen.sequences[0] + rtype = builder.node_type(sequence_expr) if is_sequence_rprimitive(rtype): - sequence = builder.accept(gen.sequences[0]) + sequence = builder.accept(sequence_expr) length = get_expr_length_value( - builder, gen.sequences[0], sequence, gen.line, use_pyssize_t=True + builder, sequence_expr, sequence, gen.line, use_pyssize_t=True ) target_op = empty_op_llbuilder(length, gen.line) - def set_item(item_index: Value) -> None: + def set_item_index(item_index: Value) -> None: e = builder.accept(gen.left_expr) builder.call_c(set_item_op, [target_op, item_index, e], gen.line) for_loop_helper_with_index( - builder, gen.indices[0], gen.sequences[0], sequence, set_item, gen.line, length + builder, gen.indices[0], sequence_expr, sequence, set_item_index, gen.line, length ) + return target_op + + expr_length = get_expr_length(sequence_expr) + if expr_length is not None: + item_index = Register(int_rprimitive) + builder.assign(item_index, Integer(0), gen.line) + def set_item_noindex() -> None: + e = builder.accept(gen.left_expr) + builder.call_c(set_item_op, [target_op, item_index, e], gen.line) + builder.assign( + item_index, + builder.binary_op(item_index, Integer(1), "+", gen.line), + gen.line, + ) + + length = Integer(expr_length, c_pyssize_t_rprimitive, gen.line) + target_op = empty_op_llbuilder(length, gen.line) + for_loop_helper(builder, gen.indices[0], sequence_expr, set_item_noindex, None, False, gen.line, ) return target_op return None diff --git a/mypyc/test-data/irbuild-tuple.test b/mypyc/test-data/irbuild-tuple.test index 4e9422d9c603..3ac18c1bc850 100644 --- a/mypyc/test-data/irbuild-tuple.test +++ b/mypyc/test-data/irbuild-tuple.test @@ -837,6 +837,7 @@ def f(val: bool) -> bool: def test() -> None: # this tuple is created from a very complex genexp but we can still compute the length and preallocate the tuple + # r1 = PyTuple_New(5) the shorter input to the zip(...) has len 5 a = tuple( x for x @@ -852,101 +853,123 @@ L0: r0 = val ^ 1 return r0 def test(): - r0 :: list - r1, r2, r3 :: object - r4 :: object[1] - r5 :: object_ptr - r6 :: object - r7 :: range - r8 :: object - r9 :: str - r10 :: object - r11 :: object[2] - r12 :: object_ptr - r13 :: object - r14 :: str - r15 :: tuple - r16 :: object - r17 :: str - r18 :: object - r19 :: object[1] - r20 :: object_ptr - r21 :: object - r22 :: list - r23 :: object - r24 :: str - r25 :: object - r26 :: object[1] - r27 :: object_ptr - r28, r29 :: object - r30 :: str - r31 :: object - r32 :: object[2] - r33 :: object_ptr - r34, r35, r36 :: object - r37, x :: tuple[str, tuple[int, str]] - r38 :: object - r39 :: i32 - r40, r41 :: bit - r42, a :: tuple + r0 :: int + r1 :: tuple + r2, r3, r4 :: object + r5 :: object[1] + r6 :: object_ptr + r7 :: object + r8 :: range + r9 :: object + r10 :: str + r11 :: object + r12 :: object[2] + r13 :: object_ptr + r14 :: object + r15 :: str + r16 :: tuple + r17 :: object + r18 :: str + r19 :: object + r20 :: object[1] + r21 :: object_ptr + r22 :: object + r23 :: list + r24 :: object + r25 :: str + r26 :: object + r27 :: object[1] + r28 :: object_ptr + r29, r30 :: object + r31 :: str + r32 :: object + r33 :: object[2] + r34 :: object_ptr + r35, r36, r37 :: object + r38, x :: tuple[str, tuple[int, str]] + r39 :: native_int + r40 :: bit + r41, r42 :: native_int + r43 :: ptr + r44 :: c_ptr + r45 :: i64 + r46 :: object + r47 :: int + r48 :: bit + a :: tuple L0: - r0 = PyList_New(0) - r1 = load_address PyUnicode_Type - r2 = load_address PyRange_Type - r3 = object 5 - r4 = [r3] - r5 = load_address r4 - r6 = PyObject_Vectorcall(r2, r5, 1, 0) - keep_alive r3 - r7 = cast(range, r6) - r8 = builtins :: module - r9 = 'map' - r10 = CPyObject_GetAttr(r8, r9) - r11 = [r1, r7] - r12 = load_address r11 - r13 = PyObject_Vectorcall(r10, r12, 2, 0) - keep_alive r1, r7 - r14 = 'abcdefg' - r15 = PySequence_Tuple(r14) - r16 = builtins :: module - r17 = 'reversed' - r18 = CPyObject_GetAttr(r16, r17) - r19 = [r15] - r20 = load_address r19 - r21 = PyObject_Vectorcall(r18, r20, 1, 0) - keep_alive r15 - r22 = CPySequence_Sort(r21) - r23 = builtins :: module - r24 = 'enumerate' - r25 = CPyObject_GetAttr(r23, r24) - r26 = [r22] - r27 = load_address r26 - r28 = PyObject_Vectorcall(r25, r27, 1, 0) - keep_alive r22 - r29 = builtins :: module - r30 = 'zip' - r31 = CPyObject_GetAttr(r29, r30) - r32 = [r13, r28] - r33 = load_address r32 - r34 = PyObject_Vectorcall(r31, r33, 2, 0) - keep_alive r13, r28 - r35 = PyObject_GetIter(r34) + r0 = 0 + r1 = PyTuple_New(5) + r2 = load_address PyUnicode_Type + r3 = load_address PyRange_Type + r4 = object 5 + r5 = [r4] + r6 = load_address r5 + r7 = PyObject_Vectorcall(r3, r6, 1, 0) + keep_alive r4 + r8 = cast(range, r7) + r9 = builtins :: module + r10 = 'map' + r11 = CPyObject_GetAttr(r9, r10) + r12 = [r2, r8] + r13 = load_address r12 + r14 = PyObject_Vectorcall(r11, r13, 2, 0) + keep_alive r2, r8 + r15 = 'abcdefg' + r16 = PySequence_Tuple(r15) + r17 = builtins :: module + r18 = 'reversed' + r19 = CPyObject_GetAttr(r17, r18) + r20 = [r16] + r21 = load_address r20 + r22 = PyObject_Vectorcall(r19, r21, 1, 0) + keep_alive r16 + r23 = CPySequence_Sort(r22) + r24 = builtins :: module + r25 = 'enumerate' + r26 = CPyObject_GetAttr(r24, r25) + r27 = [r23] + r28 = load_address r27 + r29 = PyObject_Vectorcall(r26, r28, 1, 0) + keep_alive r23 + r30 = builtins :: module + r31 = 'zip' + r32 = CPyObject_GetAttr(r30, r31) + r33 = [r14, r29] + r34 = load_address r33 + r35 = PyObject_Vectorcall(r32, r34, 2, 0) + keep_alive r14, r29 + r36 = PyObject_GetIter(r35) L1: - r36 = PyIter_Next(r35) - if is_error(r36) goto L4 else goto L2 + r37 = PyIter_Next(r36) + if is_error(r37) goto L7 else goto L2 L2: - r37 = unbox(tuple[str, tuple[int, str]], r36) - x = r37 - r38 = box(tuple[str, tuple[int, str]], x) - r39 = PyList_Append(r0, r38) - r40 = r39 >= 0 :: signed + r38 = unbox(tuple[str, tuple[int, str]], r37) + x = r38 + r39 = r0 & 1 + r40 = r39 == 0 + if r40 goto L3 else goto L4 :: bool L3: - goto L1 + r41 = r0 >> 1 + r42 = r41 + goto L5 L4: - r41 = CPy_NoErrOccurred() + r43 = r0 ^ 1 + r44 = r43 + r45 = CPyLong_AsInt64(r44) + r42 = r45 + keep_alive r0 L5: - r42 = PyList_AsTuple(r0) - a = r42 + r46 = box(tuple[str, tuple[int, str]], x) + CPySequenceTuple_SetItemUnsafe(r1, r42, r46) + r47 = CPyTagged_Add(r0, 2) + r0 = r47 +L6: + goto L1 +L7: + r48 = CPy_NoErrOccurred() +L8: + a = r1 return 1 [case testTupleBuiltFromStars] From 376151d1c09ac3b6f3b10a62a03e905c53f5ecd6 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 26 Sep 2025 13:02:25 +0000 Subject: [PATCH 15/17] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypyc/irbuild/for_helpers.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index 997f06108104..63d71018a900 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -248,7 +248,7 @@ def set_item_index(item_index: Value) -> None: builder, gen.indices[0], sequence_expr, sequence, set_item_index, gen.line, length ) return target_op - + expr_length = get_expr_length(sequence_expr) if expr_length is not None: item_index = Register(int_rprimitive) @@ -258,14 +258,14 @@ def set_item_noindex() -> None: e = builder.accept(gen.left_expr) builder.call_c(set_item_op, [target_op, item_index, e], gen.line) builder.assign( - item_index, - builder.binary_op(item_index, Integer(1), "+", gen.line), - gen.line, + item_index, builder.binary_op(item_index, Integer(1), "+", gen.line), gen.line ) length = Integer(expr_length, c_pyssize_t_rprimitive, gen.line) target_op = empty_op_llbuilder(length, gen.line) - for_loop_helper(builder, gen.indices[0], sequence_expr, set_item_noindex, None, False, gen.line, ) + for_loop_helper( + builder, gen.indices[0], sequence_expr, set_item_noindex, None, False, gen.line + ) return target_op return None From 0c56fd036925a7b88fdb16873d5d5c139bf4551d Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Fri, 26 Sep 2025 09:16:54 -0400 Subject: [PATCH 16/17] disable test on 32bit --- mypyc/test-data/irbuild-tuple.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypyc/test-data/irbuild-tuple.test b/mypyc/test-data/irbuild-tuple.test index 3ac18c1bc850..3df008e983a5 100644 --- a/mypyc/test-data/irbuild-tuple.test +++ b/mypyc/test-data/irbuild-tuple.test @@ -829,7 +829,7 @@ L4: a = r1 return 1 -[case testTupleBuiltFromLengthCheckable] +[case testTupleBuiltFromLengthCheckable_64bit] from typing import Tuple def f(val: bool) -> bool: From 0083566fadac48d12ec7347195308fc48c9cb053 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 4 Oct 2025 06:05:22 +0000 Subject: [PATCH 17/17] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypyc/irbuild/for_helpers.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index bcd7643f9bea..82ce782646d7 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -242,7 +242,9 @@ def sequence_from_generator_preallocate_helper( rtype = builder.node_type(sequence_expr) if is_sequence_rprimitive(rtype) or isinstance(rtype, RTuple): sequence = builder.accept(sequence_expr) - length = get_expr_length_value(builder, sequence_expr, sequence, line, use_pyssize_t=True) + length = get_expr_length_value( + builder, sequence_expr, sequence, line, use_pyssize_t=True + ) if isinstance(rtype, RTuple): # If input is RTuple, box it to tuple_rprimitive for generic iteration # TODO: this can be optimized a bit better with an unrolled ForRTuple helper @@ -271,7 +273,7 @@ def set_item(item_index: Value) -> None: ) return target_op - + elif (expr_length := get_expr_length(sequence_expr)) is not None: item_index = Register(int_rprimitive) builder.assign(item_index, Integer(0), line) @@ -289,7 +291,7 @@ def set_item_noindex() -> None: builder, gen.indices[0], sequence_expr, set_item_noindex, None, False, line ) return target_op - + return None