From 33996d89b631dd9af57a1ff4ef9a4d8e9a35d50b Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Tue, 12 Aug 2025 20:11:59 +0000 Subject: [PATCH 01/22] [mypyc] feat: ForMap generator helper for `builtins.map` --- mypyc/doc/native_operations.rst | 1 + mypyc/irbuild/for_helpers.py | 83 +++++ mypyc/test-data/fixtures/ir.py | 24 ++ mypyc/test-data/irbuild-basic.test | 550 +++++++++++++++++++++++++++++ mypyc/test-data/run-loops.test | 135 +++++++ 5 files changed, 793 insertions(+) diff --git a/mypyc/doc/native_operations.rst b/mypyc/doc/native_operations.rst index 3255dbedd98a..c8335dec6af6 100644 --- a/mypyc/doc/native_operations.rst +++ b/mypyc/doc/native_operations.rst @@ -37,6 +37,7 @@ Functions * ``slice(start, stop, step)`` * ``globals()`` * ``sorted(obj)`` +* ``map(fn, iterable)`` Method decorators ----------------- diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index 5cf89f579ec4..278fe0e3b633 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -11,6 +11,7 @@ from mypy.nodes import ( ARG_POS, + LDEF, CallExpr, DictionaryComprehension, Expression, @@ -22,6 +23,7 @@ SetExpr, TupleExpr, TypeAlias, + Var, ) from mypyc.ir.ops import ( ERR_NEVER, @@ -490,6 +492,16 @@ def make_for_loop_generator( for_list = ForSequence(builder, index, body_block, loop_exit, line, nested) for_list.init(expr_reg, target_type, reverse=True) return for_list + + elif ( + expr.callee.fullname == "builtins.map" + and len(expr.args) >= 2 + and all(k == ARG_POS for k in expr.arg_kinds) + ): + for_map = ForMap(builder, index, body_block, loop_exit, line, nested) + for_map.init(expr.args[0], expr.args[1:]) + return for_map + if isinstance(expr, CallExpr) and isinstance(expr.callee, MemberExpr) and not expr.args: # Special cases for dictionary iterator methods, like dict.items(). rtype = builder.node_type(expr.callee.expr) @@ -1147,3 +1159,74 @@ def gen_step(self) -> None: def gen_cleanup(self) -> None: for gen in self.gens: gen.gen_cleanup() + +class ForMap(ForGenerator): + """Generate optimized IR for a for loop over map(f, ...).""" + + def need_cleanup(self) -> bool: + # The wrapped for loops might need cleanup. We might generate a + # redundant cleanup block, but that's okay. + return True + + def init(self, func: Expression, exprs: list[Expression]) -> None: + self.func_expr = func + self.func = self.builder.accept(func) + self.exprs = exprs + self.cond_blocks = [BasicBlock() for _ in range(len(exprs) - 1)] + [self.body_block] + + self.gens: list[ForGenerator] = [] + for i, iterable_expr in enumerate(exprs): + argname = f"_mypyc_map_arg_{i}" + var_type = self.builder._analyze_iterable_item_type(iterable_expr) + name_expr = NameExpr(argname) + name_expr.kind = LDEF + name_expr.node = Var(argname, var_type) + self.builder.add_local_reg(name_expr.node, self.builder.node_type(iterable_expr)) + self.gens.append( + make_for_loop_generator( + self.builder, + name_expr, + iterable_expr, + #self.gens[-1].body_block if self.gens else self.body_block, + self.cond_blocks[i], + self.loop_exit, + self.line, + is_async=False, + nested=True, + ) + ) + + def gen_condition(self) -> None: + for i, gen in enumerate(self.gens): + gen.gen_condition() + if i < len(self.gens) - 1: + self.builder.activate_block(self.cond_blocks[i]) + + def begin_body(self) -> None: + builder = self.builder + line = self.line + + for gen in self.gens: + gen.begin_body() + + # This goes here to prevent a circular import + from mypyc.irbuild.expression import transform_call_expr + + call_expr = CallExpr( + self.func_expr, + #items, + [gen.index for gen in self.gens], + [ARG_POS] * len(self.gens), + [None] * len(self.gens), + ) + + result = transform_call_expr(builder, call_expr) + builder.assign(builder.get_assignment_target(self.index), result, line) + + def gen_step(self) -> None: + for gen in self.gens: + gen.gen_step() + + def gen_cleanup(self) -> None: + for gen in self.gens: + gen.gen_cleanup() diff --git a/mypyc/test-data/fixtures/ir.py b/mypyc/test-data/fixtures/ir.py index 76afc1ea58cc..197b355a1335 100644 --- a/mypyc/test-data/fixtures/ir.py +++ b/mypyc/test-data/fixtures/ir.py @@ -7,6 +7,8 @@ overload, Mapping, Union, Callable, Sequence, FrozenSet, Protocol ) +from typing_extensions import Self + _T = TypeVar('_T') T_co = TypeVar('T_co', covariant=True) T_contra = TypeVar('T_contra', contravariant=True) @@ -405,3 +407,25 @@ class classmethod: pass class staticmethod: pass NotImplemented: Any = ... + +_T1 = TypeVar("_T1") +_T2 = TypeVar("_T2") +_T3 = TypeVar("_T3") + +class map(Generic[_S]): + @overload + def __new__(cls, func: Callable[[_T1], _S], iterable: Iterable[_T1], /) -> Self: ... + @overload + def __new__(cls, func: Callable[[_T1, _T2], _S], iterable: Iterable[_T1], iter2: Iterable[_T2], /) -> Self: ... + @overload + def __new__(cls, func: Callable[[_T1, _T2, _T3], _S], iterable: Iterable[_T1], iter2: Iterable[_T2], iter3: Iterable[_T3], /) -> Self: ... + def __iter__(self) -> Self: ... + def __next__(self) -> _S: ... + +class filter(Generic[_T]): + @overload + def __new__(cls, function: None, iterable: Iterable[_T | None], /) -> Self: ... + @overload + def __new__(cls, function: Callable[[_T], Any], iterable: Iterable[_T], /) -> Self: ... + def __iter__(self) -> Self: ... + def __next__(self) -> _T: ... diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index 4a7d315ec836..41c84c9f74b2 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -3546,3 +3546,553 @@ L0: r2 = PyObject_Vectorcall(r1, 0, 0, 0) r3 = box(None, 1) return r3 + +[case testForMapBasic] +def f(x: int) -> int: + return x * 2 +def g(a: list[int]) -> int: + s = 0 + for x in map(f, a): + s += x + return s +[out] +def f(x): + x, r0 :: int +L0: + r0 = CPyTagged_Multiply(x, 4) + return r0 +def g(a): + a :: list + s :: int + r0 :: dict + r1 :: str + r2 :: object + r3, r4 :: native_int + r5 :: bit + r6 :: object + r7 :: int + r8 :: object + _mypyc_map_arg_0 :: list + r9, r10, x, r11 :: int + r12 :: native_int +L0: + s = 0 + r0 = __main__.globals :: static + r1 = 'f' + r2 = CPyDict_GetItem(r0, r1) + r3 = 0 +L1: + r4 = var_object_size a + r5 = r3 < r4 :: signed + if r5 goto L2 else goto L4 :: bool +L2: + r6 = list_get_item_unsafe a, r3 + r7 = unbox(int, r6) + r8 = box(int, r7) + _mypyc_map_arg_0 = r8 + r9 = unbox(int, _mypyc_map_arg_0) + r10 = f(r9) + x = r10 + r11 = CPyTagged_Add(s, x) + s = r11 +L3: + r12 = r3 + 1 + r3 = r12 + goto L1 +L4: +L5: + return s + +[case testForMapMultiple] +from typing import Any +def f(x: int, y: int) -> int: + return x * y +def g(a: list[int], b: list[int]) -> int: + s = 0 + for x in map(f, a, b): + s += x + return s +[out] +def f(x, y): + x, y, r0 :: int +L0: + r0 = CPyTagged_Multiply(x, y) + return r0 +def g(a, b): + a, b :: list + s :: int + r0 :: dict + r1 :: str + r2 :: object + r3, r4, r5 :: native_int + r6 :: bit + r7 :: native_int + r8 :: bit + r9 :: object + r10 :: int + r11 :: object + _mypyc_map_arg_0 :: list + r12 :: object + r13 :: int + r14 :: object + _mypyc_map_arg_1 :: list + r15, r16, r17, x, r18 :: int + r19, r20 :: native_int +L0: + s = 0 + r0 = __main__.globals :: static + r1 = 'f' + r2 = CPyDict_GetItem(r0, r1) + r3 = 0 + r4 = 0 +L1: + r5 = var_object_size a + r6 = r3 < r5 :: signed + if r6 goto L2 else goto L5 :: bool +L2: + r7 = var_object_size b + r8 = r4 < r7 :: signed + if r8 goto L3 else goto L5 :: bool +L3: + r9 = list_get_item_unsafe a, r3 + r10 = unbox(int, r9) + r11 = box(int, r10) + _mypyc_map_arg_0 = r11 + r12 = list_get_item_unsafe b, r4 + r13 = unbox(int, r12) + r14 = box(int, r13) + _mypyc_map_arg_1 = r14 + r15 = unbox(int, _mypyc_map_arg_0) + r16 = unbox(int, _mypyc_map_arg_1) + r17 = f(r15, r16) + x = r17 + r18 = CPyTagged_Add(s, x) + s = r18 +L4: + r19 = r3 + 1 + r3 = r19 + r20 = r4 + 1 + r4 = r20 + goto L1 +L5: +L6: + return s + +[case testForFilter] +def f(x: int) -> bool: + return bool(x % 2) +def g(a: list[int]) -> int: + s = 0 + for x in filter(f, a): + s += x + return s +[out] +def f(x): + x, r0 :: int + r1 :: bit +L0: + r0 = CPyTagged_Remainder(x, 4) + r1 = r0 != 0 + return r1 +def g(a): + a :: list + s :: int + r0 :: dict + r1 :: str + r2 :: object + r3, r4 :: native_int + r5 :: bit + r6 :: object + r7, x :: int + r8 :: object + r9 :: object[1] + r10 :: object_ptr + r11 :: object + r12 :: i32 + r13 :: bit + r14 :: bool + r15 :: int + r16 :: native_int +L0: + s = 0 + r0 = __main__.globals :: static + r1 = 'f' + r2 = CPyDict_GetItem(r0, r1) + r3 = 0 +L1: + r4 = var_object_size a + r5 = r3 < r4 :: signed + if r5 goto L2 else goto L6 :: bool +L2: + r6 = list_get_item_unsafe a, r3 + r7 = unbox(int, r6) + x = r7 + r8 = box(int, x) + r9 = [r8] + r10 = load_address r9 + r11 = PyObject_Vectorcall(r2, r10, 1, 0) + keep_alive r8 + r12 = PyObject_IsTrue(r11) + r13 = r12 >= 0 :: signed + r14 = truncate r12: i32 to builtins.bool + if r14 goto L4 else goto L3 :: bool +L3: + goto L5 +L4: + r15 = CPyTagged_Add(s, x) + s = r15 +L5: + r16 = r3 + 1 + r3 = r16 + goto L1 +L6: +L7: + return s + +[case testForFilterNone] +def f(a: list[int]) -> int: + c = 0 + for x in filter(None, a): + c += 1 + return 0 + +[out] +def f(a): + a :: list + c :: int + r0, r1 :: native_int + r2 :: bit + r3 :: object + r4, x :: int + r5 :: bit + r6 :: int + r7 :: native_int +L0: + c = 0 + r0 = 0 +L1: + r1 = var_object_size a + r2 = r0 < r1 :: signed + if r2 goto L2 else goto L6 :: bool +L2: + r3 = list_get_item_unsafe a, r0 + r4 = unbox(int, r3) + x = r4 + r5 = x != 0 + if r5 goto L4 else goto L3 :: bool +L3: + goto L5 +L4: + r6 = CPyTagged_Add(c, 2) + c = r6 +L5: + r7 = r0 + 1 + r0 = r7 + goto L1 +L6: +L7: + return 0 + +[case testForMapThreeArgs] +def f(x: int, y: int, z: int) -> int: + return x + y + z +def g(a: list[int], b: list[int], c: list[int]) -> int: + s = 0 + for x in map(f, a, b, c): + s += x + return s +[out] +def f(x, y, z): + x, y, z, r0, r1 :: int +L0: + r0 = CPyTagged_Add(x, y) + r1 = CPyTagged_Add(r0, z) + return r1 +def g(a, b, c): + a, b, c :: list + s :: int + r0 :: dict + r1 :: str + r2 :: object + r3, r4, r5, r6 :: native_int + r7 :: bit + r8 :: native_int + r9 :: bit + r10 :: native_int + r11 :: bit + r12 :: object + r13 :: int + r14 :: object + _mypyc_map_arg_0 :: list + r15 :: object + r16 :: int + r17 :: object + _mypyc_map_arg_1 :: list + r18 :: object + r19 :: int + r20 :: object + _mypyc_map_arg_2 :: list + r21, r22, r23, r24, x, r25 :: int + r26, r27, r28 :: native_int +L0: + s = 0 + r0 = __main__.globals :: static + r1 = 'f' + r2 = CPyDict_GetItem(r0, r1) + r3 = 0 + r4 = 0 + r5 = 0 +L1: + r6 = var_object_size a + r7 = r3 < r6 :: signed + if r7 goto L2 else goto L6 :: bool +L2: + r8 = var_object_size b + r9 = r4 < r8 :: signed + if r9 goto L3 else goto L6 :: bool +L3: + r10 = var_object_size c + r11 = r5 < r10 :: signed + if r11 goto L4 else goto L6 :: bool +L4: + r12 = list_get_item_unsafe a, r3 + r13 = unbox(int, r12) + r14 = box(int, r13) + _mypyc_map_arg_0 = r14 + r15 = list_get_item_unsafe b, r4 + r16 = unbox(int, r15) + r17 = box(int, r16) + _mypyc_map_arg_1 = r17 + r18 = list_get_item_unsafe c, r5 + r19 = unbox(int, r18) + r20 = box(int, r19) + _mypyc_map_arg_2 = r20 + r21 = unbox(int, _mypyc_map_arg_0) + r22 = unbox(int, _mypyc_map_arg_1) + r23 = unbox(int, _mypyc_map_arg_2) + r24 = f(r21, r22, r23) + x = r24 + r25 = CPyTagged_Add(s, x) + s = r25 +L5: + r26 = r3 + 1 + r3 = r26 + r27 = r4 + 1 + r4 = r27 + r28 = r5 + 1 + r5 = r28 + goto L1 +L6: +L7: + return s + +[case testForMapEmpty] +def f(x: int) -> int: + return x + 1 +def g(a: list[int]) -> int: + s = 0 + for x in map(f, []): + s += x + return s +[out] +def f(x): + x, r0 :: int +L0: + r0 = CPyTagged_Add(x, 2) + return r0 +def g(a): + a :: list + s :: int + r0 :: dict + r1 :: str + r2 :: object + r3 :: list + r4, r5 :: native_int + r6 :: bit + r7 :: object + r8, _mypyc_map_arg_0 :: list + r9, r10, x, r11 :: int + r12 :: native_int +L0: + s = 0 + r0 = __main__.globals :: static + r1 = 'f' + r2 = CPyDict_GetItem(r0, r1) + r3 = PyList_New(0) + r4 = 0 +L1: + r5 = var_object_size r3 + r6 = r4 < r5 :: signed + if r6 goto L2 else goto L4 :: bool +L2: + r7 = list_get_item_unsafe r3, r4 + r8 = cast(list, r7) + _mypyc_map_arg_0 = r8 + r9 = unbox(int, _mypyc_map_arg_0) + r10 = f(r9) + x = r10 + r11 = CPyTagged_Add(s, x) + s = r11 +L3: + r12 = r4 + 1 + r4 = r12 + goto L1 +L4: +L5: + return s + +[case testForMapUnpack] +def f(x: int, y: int) -> tuple[int, int]: + return (x, y) +def g(a: list[int], b: list[int]) -> int: + s = 0 + for x, y in map(f, a, b): + s += x + y + return s +[out] +def f(x, y): + x, y :: int + r0 :: tuple[int, int] +L0: + r0 = (x, y) + return r0 +def g(a, b): + a, b :: list + s :: int + r0 :: dict + r1 :: str + r2 :: object + r3, r4, r5 :: native_int + r6 :: bit + r7 :: native_int + r8 :: bit + r9 :: object + r10 :: int + r11 :: object + _mypyc_map_arg_0 :: list + r12 :: object + r13 :: int + r14 :: object + _mypyc_map_arg_1 :: list + r15, r16 :: int + r17 :: tuple[int, int] + r18, x, r19, y, r20, r21 :: int + r22, r23 :: native_int +L0: + s = 0 + r0 = __main__.globals :: static + r1 = 'f' + r2 = CPyDict_GetItem(r0, r1) + r3 = 0 + r4 = 0 +L1: + r5 = var_object_size a + r6 = r3 < r5 :: signed + if r6 goto L2 else goto L5 :: bool +L2: + r7 = var_object_size b + r8 = r4 < r7 :: signed + if r8 goto L3 else goto L5 :: bool +L3: + r9 = list_get_item_unsafe a, r3 + r10 = unbox(int, r9) + r11 = box(int, r10) + _mypyc_map_arg_0 = r11 + r12 = list_get_item_unsafe b, r4 + r13 = unbox(int, r12) + r14 = box(int, r13) + _mypyc_map_arg_1 = r14 + r15 = unbox(int, _mypyc_map_arg_0) + r16 = unbox(int, _mypyc_map_arg_1) + r17 = f(r15, r16) + r18 = r17[0] + x = r18 + r19 = r17[1] + y = r19 + r20 = CPyTagged_Add(x, y) + r21 = CPyTagged_Add(s, r20) + s = r21 +L4: + r22 = r3 + 1 + r3 = r22 + r23 = r4 + 1 + r4 = r23 + goto L1 +L5: +L6: + return s + +[case testForMapComprehension] +def f(x: int, y: int) -> int: + return x * y +def g(a: list[int], b: list[int]) -> list[int]: + return [x for x in map(f, a, b)] +[out] +def f(x, y): + x, y, r0 :: int +L0: + r0 = CPyTagged_Multiply(x, y) + return r0 +def g(a, b): + a, b, r0 :: list + r1 :: dict + r2 :: str + r3 :: object + r4, r5, r6 :: native_int + r7 :: bit + r8 :: native_int + r9 :: bit + r10 :: object + r11 :: int + r12 :: object + _mypyc_map_arg_0 :: list + r13 :: object + r14 :: int + r15 :: object + _mypyc_map_arg_1 :: list + r16, r17, r18, x :: int + r19 :: object + r20 :: i32 + r21 :: bit + r22, r23 :: native_int +L0: + r0 = PyList_New(0) + r1 = __main__.globals :: static + r2 = 'f' + r3 = CPyDict_GetItem(r1, r2) + r4 = 0 + r5 = 0 +L1: + r6 = var_object_size a + r7 = r4 < r6 :: signed + if r7 goto L2 else goto L5 :: bool +L2: + r8 = var_object_size b + r9 = r5 < r8 :: signed + if r9 goto L3 else goto L5 :: bool +L3: + r10 = list_get_item_unsafe a, r4 + r11 = unbox(int, r10) + r12 = box(int, r11) + _mypyc_map_arg_0 = r12 + r13 = list_get_item_unsafe b, r5 + r14 = unbox(int, r13) + r15 = box(int, r14) + _mypyc_map_arg_1 = r15 + r16 = unbox(int, _mypyc_map_arg_0) + r17 = unbox(int, _mypyc_map_arg_1) + r18 = f(r16, r17) + x = r18 + r19 = box(int, x) + r20 = PyList_Append(r0, r19) + r21 = r20 >= 0 :: signed +L4: + r22 = r4 + 1 + r4 = r22 + r23 = r5 + 1 + r5 = r23 + goto L1 +L5: +L6: + return r0 + diff --git a/mypyc/test-data/run-loops.test b/mypyc/test-data/run-loops.test index 3cbb07297e6e..d8a2b2e09781 100644 --- a/mypyc/test-data/run-loops.test +++ b/mypyc/test-data/run-loops.test @@ -571,3 +571,138 @@ print([x for x in native.Vector2(4, -5.2)]) [out] Vector2(x=-2, y=3.1) \[4, -5.2] + +[case testRunForMap] +def f(a: list[int]) -> int: + s = 0 + for x in map(lambda x: x + 1, a): + s += x + return s + +def g(a: list[int], b: list[int]) -> int: + s = 0 + for x in map(lambda x, y: x * y, a, b): + s += x + return s + +[file driver.py] +from native import f, g +print(f([1, 2, 3])) +print(g([1, 2, 3], [4, 5, 6])) +print(g([1, 2], [10, 20, 30])) # Shortest wins: only 2 items + +[out] +9 +32 +50 + +[case testRunForMapEdgeCases] +def f(a: list[int], b: list[int]) -> int: + s = 0 + for x in map(lambda x, y: x + y, a, b): + s += x + return s + +[file driver.py] +from native import f +print(f([], [])) +print(f([1, 2], [])) +print(f([], [1, 2])) + +[out] +0 +0 +0 + +[case testRunForFilter] +def f(a: list[int]) -> int: + s = 0 + for x in filter(lambda x: x % 2 == 0, a): + s += x + return s + +[file driver.py] +from native import f +print(f([1, 2, 3, 4, 5, 6])) +print(f([1, 3, 5])) +print(f([])) + +[out] +12 +0 +0 + +[case testRunForFilterNone] +def f(a: list[int]) -> int: + c = 0 + for x in filter(None, a): + c += 1 + return c + +[file driver.py] +from native import f +print(f([0, 1, 2, 3, 4, 5, 6])) + +[out] +6 + +[case testRunForFilterEdgeCases] +def f(a: list[int]) -> int: + s = 0 + for x in filter(lambda x: x > 10, a): + s += x + return s + +[file driver.py] +from native import f +print(f([5, 15, 25])) +print(f([])) + +[out] +40 +0 + +[case testRunForMapThreeArgs] +def f(a: list[int], b: list[int], c: list[int]) -> int: + s = 0 + for x in map(lambda x, y, z: x * y + z, a, b, c): + s += x + return s + +[file driver.py] +from native import f +print(f([1, 2, 3], [4, 5, 6], [7, 8, 9])) +print(f([1, 2], [10, 20, 30], [100, 200, 300])) +print(f([], [], [])) +print(f([1, 2], [], [3, 4])) +[out] +56 +350 +0 +0 + +[case testRunForMapNested] +def f(a: list[int], b: list[int], c: list[int]) -> int: + s = 0 + for x in map(lambda x, y: x + y, map(lambda x, y: x * y, a, b), c): + s += x + return s + +[file driver.py] +from native import f +print(f([1, 2, 3], [4, 5, 6], [7, 8, 9])) +[out] +56 + +[case testRunForMapUnpack] +def f(a: list[int], b: list[int]) -> int: + s = 0 + for x, y in map(lambda x, y: (x, y), a, b): + s += x * 10 + y + return s + +[file driver.py] +from native import f +print(f([1, 2, 3], [4, 5, 6])) +[out] +75 From 56b5102e746d913b4b63d82f524ecc8ff4017b12 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 12 Aug 2025 20:13:48 +0000 Subject: [PATCH 02/22] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypyc/irbuild/for_helpers.py | 7 ++++--- mypyc/test-data/irbuild-basic.test | 1 - 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index 278fe0e3b633..76b746ddc225 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -1160,6 +1160,7 @@ def gen_cleanup(self) -> None: for gen in self.gens: gen.gen_cleanup() + class ForMap(ForGenerator): """Generate optimized IR for a for loop over map(f, ...).""" @@ -1187,7 +1188,7 @@ def init(self, func: Expression, exprs: list[Expression]) -> None: self.builder, name_expr, iterable_expr, - #self.gens[-1].body_block if self.gens else self.body_block, + # self.gens[-1].body_block if self.gens else self.body_block, self.cond_blocks[i], self.loop_exit, self.line, @@ -1208,13 +1209,13 @@ def begin_body(self) -> None: for gen in self.gens: gen.begin_body() - + # This goes here to prevent a circular import from mypyc.irbuild.expression import transform_call_expr call_expr = CallExpr( self.func_expr, - #items, + # items, [gen.index for gen in self.gens], [ARG_POS] * len(self.gens), [None] * len(self.gens), diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index 41c84c9f74b2..ff9a15149b93 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -4095,4 +4095,3 @@ L4: L5: L6: return r0 - From 8e12f83dde34e17b268c7f229bcb4fc7c591b1b2 Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Tue, 12 Aug 2025 20:31:14 +0000 Subject: [PATCH 03/22] fix: rtypes --- mypyc/irbuild/for_helpers.py | 2 +- mypyc/test-data/irbuild-basic.test | 149 +++++++++++++---------------- 2 files changed, 67 insertions(+), 84 deletions(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index 278fe0e3b633..c0049909e232 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -1181,7 +1181,7 @@ def init(self, func: Expression, exprs: list[Expression]) -> None: name_expr = NameExpr(argname) name_expr.kind = LDEF name_expr.node = Var(argname, var_type) - self.builder.add_local_reg(name_expr.node, self.builder.node_type(iterable_expr)) + self.builder.add_local_reg(name_expr.node, self.builder.type_to_rtype(var_type)) self.gens.append( make_for_loop_generator( self.builder, diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index 41c84c9f74b2..3ddd65773823 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -3570,9 +3570,8 @@ def g(a): r3, r4 :: native_int r5 :: bit r6 :: object - r7 :: int + r7, _mypyc_map_arg_0 :: int r8 :: object - _mypyc_map_arg_0 :: list r9, r10, x, r11 :: int r12 :: native_int L0: @@ -3588,9 +3587,9 @@ L1: L2: r6 = list_get_item_unsafe a, r3 r7 = unbox(int, r6) - r8 = box(int, r7) - _mypyc_map_arg_0 = r8 - r9 = unbox(int, _mypyc_map_arg_0) + _mypyc_map_arg_0 = r7 + r8 = box(int, _mypyc_map_arg_0) + r9 = unbox(int, r8) r10 = f(r9) x = r10 r11 = CPyTagged_Add(s, x) @@ -3629,13 +3628,10 @@ def g(a, b): r7 :: native_int r8 :: bit r9 :: object - r10 :: int + r10, _mypyc_map_arg_0 :: int r11 :: object - _mypyc_map_arg_0 :: list - r12 :: object - r13 :: int - r14 :: object - _mypyc_map_arg_1 :: list + r12, _mypyc_map_arg_1 :: int + r13, r14 :: object r15, r16, r17, x, r18 :: int r19, r20 :: native_int L0: @@ -3656,14 +3652,14 @@ L2: L3: r9 = list_get_item_unsafe a, r3 r10 = unbox(int, r9) - r11 = box(int, r10) - _mypyc_map_arg_0 = r11 - r12 = list_get_item_unsafe b, r4 - r13 = unbox(int, r12) - r14 = box(int, r13) - _mypyc_map_arg_1 = r14 - r15 = unbox(int, _mypyc_map_arg_0) - r16 = unbox(int, _mypyc_map_arg_1) + _mypyc_map_arg_0 = r10 + r11 = list_get_item_unsafe b, r4 + r12 = unbox(int, r11) + _mypyc_map_arg_1 = r12 + r13 = box(int, _mypyc_map_arg_0) + r14 = box(int, _mypyc_map_arg_1) + r15 = unbox(int, r13) + r16 = unbox(int, r14) r17 = f(r15, r16) x = r17 r18 = CPyTagged_Add(s, x) @@ -3821,17 +3817,12 @@ def g(a, b, c): r10 :: native_int r11 :: bit r12 :: object - r13 :: int + r13, _mypyc_map_arg_0 :: int r14 :: object - _mypyc_map_arg_0 :: list - r15 :: object - r16 :: int - r17 :: object - _mypyc_map_arg_1 :: list - r18 :: object - r19 :: int - r20 :: object - _mypyc_map_arg_2 :: list + r15, _mypyc_map_arg_1 :: int + r16 :: object + r17, _mypyc_map_arg_2 :: int + r18, r19, r20 :: object r21, r22, r23, r24, x, r25 :: int r26, r27, r28 :: native_int L0: @@ -3857,19 +3848,19 @@ L3: L4: r12 = list_get_item_unsafe a, r3 r13 = unbox(int, r12) - r14 = box(int, r13) - _mypyc_map_arg_0 = r14 - r15 = list_get_item_unsafe b, r4 - r16 = unbox(int, r15) - r17 = box(int, r16) - _mypyc_map_arg_1 = r17 - r18 = list_get_item_unsafe c, r5 - r19 = unbox(int, r18) - r20 = box(int, r19) - _mypyc_map_arg_2 = r20 - r21 = unbox(int, _mypyc_map_arg_0) - r22 = unbox(int, _mypyc_map_arg_1) - r23 = unbox(int, _mypyc_map_arg_2) + _mypyc_map_arg_0 = r13 + r14 = list_get_item_unsafe b, r4 + r15 = unbox(int, r14) + _mypyc_map_arg_1 = r15 + r16 = list_get_item_unsafe c, r5 + r17 = unbox(int, r16) + _mypyc_map_arg_2 = r17 + r18 = box(int, _mypyc_map_arg_0) + r19 = box(int, _mypyc_map_arg_1) + r20 = box(int, _mypyc_map_arg_2) + r21 = unbox(int, r18) + r22 = unbox(int, r19) + r23 = unbox(int, r20) r24 = f(r21, r22, r23) x = r24 r25 = CPyTagged_Add(s, x) @@ -3909,10 +3900,9 @@ def g(a): r3 :: list r4, r5 :: native_int r6 :: bit - r7 :: object - r8, _mypyc_map_arg_0 :: list - r9, r10, x, r11 :: int - r12 :: native_int + r7, _mypyc_map_arg_0 :: object + r8, r9, x, r10 :: int + r11 :: native_int L0: s = 0 r0 = __main__.globals :: static @@ -3926,16 +3916,15 @@ L1: if r6 goto L2 else goto L4 :: bool L2: r7 = list_get_item_unsafe r3, r4 - r8 = cast(list, r7) - _mypyc_map_arg_0 = r8 - r9 = unbox(int, _mypyc_map_arg_0) - r10 = f(r9) - x = r10 - r11 = CPyTagged_Add(s, x) - s = r11 + _mypyc_map_arg_0 = r7 + r8 = unbox(int, _mypyc_map_arg_0) + r9 = f(r8) + x = r9 + r10 = CPyTagged_Add(s, x) + s = r10 L3: - r12 = r4 + 1 - r4 = r12 + r11 = r4 + 1 + r4 = r11 goto L1 L4: L5: @@ -3967,13 +3956,10 @@ def g(a, b): r7 :: native_int r8 :: bit r9 :: object - r10 :: int + r10, _mypyc_map_arg_0 :: int r11 :: object - _mypyc_map_arg_0 :: list - r12 :: object - r13 :: int - r14 :: object - _mypyc_map_arg_1 :: list + r12, _mypyc_map_arg_1 :: int + r13, r14 :: object r15, r16 :: int r17 :: tuple[int, int] r18, x, r19, y, r20, r21 :: int @@ -3996,14 +3982,14 @@ L2: L3: r9 = list_get_item_unsafe a, r3 r10 = unbox(int, r9) - r11 = box(int, r10) - _mypyc_map_arg_0 = r11 - r12 = list_get_item_unsafe b, r4 - r13 = unbox(int, r12) - r14 = box(int, r13) - _mypyc_map_arg_1 = r14 - r15 = unbox(int, _mypyc_map_arg_0) - r16 = unbox(int, _mypyc_map_arg_1) + _mypyc_map_arg_0 = r10 + r11 = list_get_item_unsafe b, r4 + r12 = unbox(int, r11) + _mypyc_map_arg_1 = r12 + r13 = box(int, _mypyc_map_arg_0) + r14 = box(int, _mypyc_map_arg_1) + r15 = unbox(int, r13) + r16 = unbox(int, r14) r17 = f(r15, r16) r18 = r17[0] x = r18 @@ -4043,13 +4029,10 @@ def g(a, b): r8 :: native_int r9 :: bit r10 :: object - r11 :: int + r11, _mypyc_map_arg_0 :: int r12 :: object - _mypyc_map_arg_0 :: list - r13 :: object - r14 :: int - r15 :: object - _mypyc_map_arg_1 :: list + r13, _mypyc_map_arg_1 :: int + r14, r15 :: object r16, r17, r18, x :: int r19 :: object r20 :: i32 @@ -4073,14 +4056,14 @@ L2: L3: r10 = list_get_item_unsafe a, r4 r11 = unbox(int, r10) - r12 = box(int, r11) - _mypyc_map_arg_0 = r12 - r13 = list_get_item_unsafe b, r5 - r14 = unbox(int, r13) - r15 = box(int, r14) - _mypyc_map_arg_1 = r15 - r16 = unbox(int, _mypyc_map_arg_0) - r17 = unbox(int, _mypyc_map_arg_1) + _mypyc_map_arg_0 = r11 + r12 = list_get_item_unsafe b, r5 + r13 = unbox(int, r12) + _mypyc_map_arg_1 = r13 + r14 = box(int, _mypyc_map_arg_0) + r15 = box(int, _mypyc_map_arg_1) + r16 = unbox(int, r14) + r17 = unbox(int, r15) r18 = f(r16, r17) x = r18 r19 = box(int, x) From c09515faf8ed6239c3ab47d8ab1dace0b474f0b8 Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Tue, 12 Aug 2025 20:45:21 +0000 Subject: [PATCH 04/22] remove accidental commit --- mypyc/test-data/irbuild-basic.test | 115 ----------------------------- 1 file changed, 115 deletions(-) diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index 7123c4f0a095..07f5e71ed51e 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -3674,121 +3674,6 @@ L5: L6: return s -[case testForFilter] -def f(x: int) -> bool: - return bool(x % 2) -def g(a: list[int]) -> int: - s = 0 - for x in filter(f, a): - s += x - return s -[out] -def f(x): - x, r0 :: int - r1 :: bit -L0: - r0 = CPyTagged_Remainder(x, 4) - r1 = r0 != 0 - return r1 -def g(a): - a :: list - s :: int - r0 :: dict - r1 :: str - r2 :: object - r3, r4 :: native_int - r5 :: bit - r6 :: object - r7, x :: int - r8 :: object - r9 :: object[1] - r10 :: object_ptr - r11 :: object - r12 :: i32 - r13 :: bit - r14 :: bool - r15 :: int - r16 :: native_int -L0: - s = 0 - r0 = __main__.globals :: static - r1 = 'f' - r2 = CPyDict_GetItem(r0, r1) - r3 = 0 -L1: - r4 = var_object_size a - r5 = r3 < r4 :: signed - if r5 goto L2 else goto L6 :: bool -L2: - r6 = list_get_item_unsafe a, r3 - r7 = unbox(int, r6) - x = r7 - r8 = box(int, x) - r9 = [r8] - r10 = load_address r9 - r11 = PyObject_Vectorcall(r2, r10, 1, 0) - keep_alive r8 - r12 = PyObject_IsTrue(r11) - r13 = r12 >= 0 :: signed - r14 = truncate r12: i32 to builtins.bool - if r14 goto L4 else goto L3 :: bool -L3: - goto L5 -L4: - r15 = CPyTagged_Add(s, x) - s = r15 -L5: - r16 = r3 + 1 - r3 = r16 - goto L1 -L6: -L7: - return s - -[case testForFilterNone] -def f(a: list[int]) -> int: - c = 0 - for x in filter(None, a): - c += 1 - return 0 - -[out] -def f(a): - a :: list - c :: int - r0, r1 :: native_int - r2 :: bit - r3 :: object - r4, x :: int - r5 :: bit - r6 :: int - r7 :: native_int -L0: - c = 0 - r0 = 0 -L1: - r1 = var_object_size a - r2 = r0 < r1 :: signed - if r2 goto L2 else goto L6 :: bool -L2: - r3 = list_get_item_unsafe a, r0 - r4 = unbox(int, r3) - x = r4 - r5 = x != 0 - if r5 goto L4 else goto L3 :: bool -L3: - goto L5 -L4: - r6 = CPyTagged_Add(c, 2) - c = r6 -L5: - r7 = r0 + 1 - r0 = r7 - goto L1 -L6: -L7: - return 0 - [case testForMapThreeArgs] def f(x: int, y: int, z: int) -> int: return x + y + z From b3acdf8c7734fbcd106f4ce8bdd0965e8d3ed25b Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Wed, 13 Aug 2025 02:53:22 -0400 Subject: [PATCH 05/22] Update for_helpers.py --- mypyc/irbuild/for_helpers.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index 1e2063cc23cd..bfe96894b8ff 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -1188,7 +1188,6 @@ def init(self, func: Expression, exprs: list[Expression]) -> None: self.builder, name_expr, iterable_expr, - # self.gens[-1].body_block if self.gens else self.body_block, self.cond_blocks[i], self.loop_exit, self.line, From 5356bc1c416181a9d98ce1e8eb9784c38b08be1f Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Wed, 13 Aug 2025 03:35:01 -0400 Subject: [PATCH 06/22] Update for_helpers.py --- mypyc/irbuild/for_helpers.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index bfe96894b8ff..00ffa87a7e3b 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -1214,7 +1214,6 @@ def begin_body(self) -> None: call_expr = CallExpr( self.func_expr, - # items, [gen.index for gen in self.gens], [ARG_POS] * len(self.gens), [None] * len(self.gens), From fcc221efa913e08efd52a6a74ffb26413a485e63 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Thu, 14 Aug 2025 02:39:46 -0400 Subject: [PATCH 07/22] Update native_operations.rst --- mypyc/doc/native_operations.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypyc/doc/native_operations.rst b/mypyc/doc/native_operations.rst index c8335dec6af6..356a8930d656 100644 --- a/mypyc/doc/native_operations.rst +++ b/mypyc/doc/native_operations.rst @@ -37,7 +37,6 @@ Functions * ``slice(start, stop, step)`` * ``globals()`` * ``sorted(obj)`` -* ``map(fn, iterable)`` Method decorators ----------------- @@ -55,3 +54,4 @@ These variants of statements have custom implementations: * ``for ... in seq:`` (for loop over a sequence) * ``for ... in enumerate(...):`` * ``for ... in zip(...):`` +* ``for ... in map(...)`` From 684e4780faf309f7104000bbca4fd0f41625340e Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Fri, 15 Aug 2025 05:12:33 +0000 Subject: [PATCH 08/22] translate list map --- mypyc/irbuild/specialize.py | 48 +++++++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/mypyc/irbuild/specialize.py b/mypyc/irbuild/specialize.py index 3015640fb3fd..ff2d1daddf06 100644 --- a/mypyc/irbuild/specialize.py +++ b/mypyc/irbuild/specialize.py @@ -73,6 +73,7 @@ from mypyc.irbuild.builder import IRBuilder from mypyc.irbuild.for_helpers import ( comprehension_helper, + for_loop_helper, sequence_from_generator_preallocate_helper, translate_list_comprehension, translate_set_comprehension, @@ -93,7 +94,7 @@ ) from mypyc.primitives.float_ops import isinstance_float from mypyc.primitives.int_ops import isinstance_int -from mypyc.primitives.list_ops import isinstance_list, new_list_set_item_op +from mypyc.primitives.list_ops import isinstance_list, new_list_set_item_op, list_append_op from mypyc.primitives.misc_ops import isinstance_bool from mypyc.primitives.set_ops import isinstance_frozenset, isinstance_set from mypyc.primitives.str_ops import ( @@ -257,7 +258,7 @@ def dict_methods_fast_path(builder: IRBuilder, expr: CallExpr, callee: RefExpr) @specialize_function("builtins.list") -def translate_list_from_generator_call( +def translate_list_from_generator_expr( builder: IRBuilder, expr: CallExpr, callee: RefExpr ) -> Value | None: """Special case for simplest list comprehension. @@ -281,6 +282,49 @@ def translate_list_from_generator_call( return None +@specialize_function("builtins.list") +def translate_list_from_generator_call( + builder: IRBuilder, expr: CallExpr, callee: RefExpr +) -> Value | None: + """Special case for simplest list construction using one of our for_helpers. + + For example: + list(map(f, some_list/some_tuple/some_str)) + """ + if ( + len(expr.args) == 1 + and expr.arg_kinds[0] == ARG_POS + and isinstance(expr.args[0], CallExpr) + and isinstance(expr.args[0].callee, NameExpr) + and expr.args[0].callee.fullname in ( + # TODO: make constant for these vals + "builtins.map", + "builtins.filter", + "builtins.filterfalse", + ) + ): + call_expr = expr.args[0] + itemtype = builder._analyze_iterable_item_type(call_expr) + indextype = builder.type_to_rtype(itemtype) + index = Register(indextype, "__mypyc_list_helper__", line=call_expr.line) + + result = builder.new_list_op([], expr.line) + + def body_insts(): + builder.primitive_op(list_append_op, [result, index], expr.line) + + for_loop_helper( + builder=builder, + index=index, + expr=call_expr, + body_insts=body_insts, + else_insts=None, + is_async=False, + line=expr.line, + ) + return None + + @specialize_function("builtins.tuple") def translate_tuple_from_generator_call( builder: IRBuilder, expr: CallExpr, callee: RefExpr From 25ed8590f40384984b6502236ed628584efb2ba2 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Fri, 15 Aug 2025 22:28:44 -0400 Subject: [PATCH 09/22] Update for_helpers.py --- mypyc/irbuild/for_helpers.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index 2587c179432b..4e9cf6ad06be 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -1228,9 +1228,6 @@ def begin_body(self) -> None: for gen in self.gens: gen.begin_body() - # This goes here to prevent a circular import - from mypyc.irbuild.expression import transform_call_expr - call_expr = CallExpr( self.func_expr, [gen.index for gen in self.gens], @@ -1238,7 +1235,7 @@ def begin_body(self) -> None: [None] * len(self.gens), ) - result = transform_call_expr(builder, call_expr) + result = builder.accept(call_expr) builder.assign(builder.get_assignment_target(self.index), result, line) def gen_step(self) -> None: From 80d578d993f926a3f6bd7f661c8c1b2992846535 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Fri, 15 Aug 2025 22:29:37 -0400 Subject: [PATCH 10/22] Update ir.py --- mypyc/test-data/fixtures/ir.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/mypyc/test-data/fixtures/ir.py b/mypyc/test-data/fixtures/ir.py index 7abd7f509508..a2a174551b14 100644 --- a/mypyc/test-data/fixtures/ir.py +++ b/mypyc/test-data/fixtures/ir.py @@ -422,11 +422,3 @@ def __new__(cls, func: Callable[[_T1, _T2], _S], iterable: Iterable[_T1], iter2: def __new__(cls, func: Callable[[_T1, _T2, _T3], _S], iterable: Iterable[_T1], iter2: Iterable[_T2], iter3: Iterable[_T3], /) -> Self: ... def __iter__(self) -> Self: ... def __next__(self) -> _S: ... - -class filter(Generic[_T]): - @overload - def __new__(cls, function: None, iterable: Iterable[_T | None], /) -> Self: ... - @overload - def __new__(cls, function: Callable[[_T], Any], iterable: Iterable[_T], /) -> Self: ... - def __iter__(self) -> Self: ... - def __next__(self) -> _T: ... From ff594725f4426cf1aec435181de06254c341b830 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Fri, 15 Aug 2025 22:44:46 -0400 Subject: [PATCH 11/22] Update run-loops.test --- mypyc/test-data/run-loops.test | 48 ---------------------------------- 1 file changed, 48 deletions(-) diff --git a/mypyc/test-data/run-loops.test b/mypyc/test-data/run-loops.test index d8a2b2e09781..b902d1d7b018 100644 --- a/mypyc/test-data/run-loops.test +++ b/mypyc/test-data/run-loops.test @@ -614,54 +614,6 @@ print(f([], [1, 2])) 0 0 -[case testRunForFilter] -def f(a: list[int]) -> int: - s = 0 - for x in filter(lambda x: x % 2 == 0, a): - s += x - return s - -[file driver.py] -from native import f -print(f([1, 2, 3, 4, 5, 6])) -print(f([1, 3, 5])) -print(f([])) - -[out] -12 -0 -0 - -[case testRunForFilterNone] -def f(a: list[int]) -> int: - c = 0 - for x in filter(None, a): - c += 1 - return c - -[file driver.py] -from native import f -print(f([0, 1, 2, 3, 4, 5, 6])) - -[out] -6 - -[case testRunForFilterEdgeCases] -def f(a: list[int]) -> int: - s = 0 - for x in filter(lambda x: x > 10, a): - s += x - return s - -[file driver.py] -from native import f -print(f([5, 15, 25])) -print(f([])) - -[out] -40 -0 - [case testRunForMapThreeArgs] def f(a: list[int], b: list[int], c: list[int]) -> int: s = 0 From 026b2ef12e6e90a2e002360c8867a2ce2e6110f5 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Sat, 16 Aug 2025 10:25:41 -0400 Subject: [PATCH 12/22] Update irbuild-basic.test --- mypyc/test-data/irbuild-basic.test | 205 +---------------------------- 1 file changed, 1 insertion(+), 204 deletions(-) diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index 07f5e71ed51e..d62214b76ee2 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -3602,79 +3602,7 @@ L4: L5: return s -[case testForMapMultiple] -from typing import Any -def f(x: int, y: int) -> int: - return x * y -def g(a: list[int], b: list[int]) -> int: - s = 0 - for x in map(f, a, b): - s += x - return s -[out] -def f(x, y): - x, y, r0 :: int -L0: - r0 = CPyTagged_Multiply(x, y) - return r0 -def g(a, b): - a, b :: list - s :: int - r0 :: dict - r1 :: str - r2 :: object - r3, r4, r5 :: native_int - r6 :: bit - r7 :: native_int - r8 :: bit - r9 :: object - r10, _mypyc_map_arg_0 :: int - r11 :: object - r12, _mypyc_map_arg_1 :: int - r13, r14 :: object - r15, r16, r17, x, r18 :: int - r19, r20 :: native_int -L0: - s = 0 - r0 = __main__.globals :: static - r1 = 'f' - r2 = CPyDict_GetItem(r0, r1) - r3 = 0 - r4 = 0 -L1: - r5 = var_object_size a - r6 = r3 < r5 :: signed - if r6 goto L2 else goto L5 :: bool -L2: - r7 = var_object_size b - r8 = r4 < r7 :: signed - if r8 goto L3 else goto L5 :: bool -L3: - r9 = list_get_item_unsafe a, r3 - r10 = unbox(int, r9) - _mypyc_map_arg_0 = r10 - r11 = list_get_item_unsafe b, r4 - r12 = unbox(int, r11) - _mypyc_map_arg_1 = r12 - r13 = box(int, _mypyc_map_arg_0) - r14 = box(int, _mypyc_map_arg_1) - r15 = unbox(int, r13) - r16 = unbox(int, r14) - r17 = f(r15, r16) - x = r17 - r18 = CPyTagged_Add(s, x) - s = r18 -L4: - r19 = r3 + 1 - r3 = r19 - r20 = r4 + 1 - r4 = r20 - goto L1 -L5: -L6: - return s - -[case testForMapThreeArgs] +[case testForMapComplex] def f(x: int, y: int, z: int) -> int: return x + y + z def g(a: list[int], b: list[int], c: list[int]) -> int: @@ -3762,137 +3690,6 @@ L6: L7: return s -[case testForMapEmpty] -def f(x: int) -> int: - return x + 1 -def g(a: list[int]) -> int: - s = 0 - for x in map(f, []): - s += x - return s -[out] -def f(x): - x, r0 :: int -L0: - r0 = CPyTagged_Add(x, 2) - return r0 -def g(a): - a :: list - s :: int - r0 :: dict - r1 :: str - r2 :: object - r3 :: list - r4, r5 :: native_int - r6 :: bit - r7, _mypyc_map_arg_0 :: object - r8, r9, x, r10 :: int - r11 :: native_int -L0: - s = 0 - r0 = __main__.globals :: static - r1 = 'f' - r2 = CPyDict_GetItem(r0, r1) - r3 = PyList_New(0) - r4 = 0 -L1: - r5 = var_object_size r3 - r6 = r4 < r5 :: signed - if r6 goto L2 else goto L4 :: bool -L2: - r7 = list_get_item_unsafe r3, r4 - _mypyc_map_arg_0 = r7 - r8 = unbox(int, _mypyc_map_arg_0) - r9 = f(r8) - x = r9 - r10 = CPyTagged_Add(s, x) - s = r10 -L3: - r11 = r4 + 1 - r4 = r11 - goto L1 -L4: -L5: - return s - -[case testForMapUnpack] -def f(x: int, y: int) -> tuple[int, int]: - return (x, y) -def g(a: list[int], b: list[int]) -> int: - s = 0 - for x, y in map(f, a, b): - s += x + y - return s -[out] -def f(x, y): - x, y :: int - r0 :: tuple[int, int] -L0: - r0 = (x, y) - return r0 -def g(a, b): - a, b :: list - s :: int - r0 :: dict - r1 :: str - r2 :: object - r3, r4, r5 :: native_int - r6 :: bit - r7 :: native_int - r8 :: bit - r9 :: object - r10, _mypyc_map_arg_0 :: int - r11 :: object - r12, _mypyc_map_arg_1 :: int - r13, r14 :: object - r15, r16 :: int - r17 :: tuple[int, int] - r18, x, r19, y, r20, r21 :: int - r22, r23 :: native_int -L0: - s = 0 - r0 = __main__.globals :: static - r1 = 'f' - r2 = CPyDict_GetItem(r0, r1) - r3 = 0 - r4 = 0 -L1: - r5 = var_object_size a - r6 = r3 < r5 :: signed - if r6 goto L2 else goto L5 :: bool -L2: - r7 = var_object_size b - r8 = r4 < r7 :: signed - if r8 goto L3 else goto L5 :: bool -L3: - r9 = list_get_item_unsafe a, r3 - r10 = unbox(int, r9) - _mypyc_map_arg_0 = r10 - r11 = list_get_item_unsafe b, r4 - r12 = unbox(int, r11) - _mypyc_map_arg_1 = r12 - r13 = box(int, _mypyc_map_arg_0) - r14 = box(int, _mypyc_map_arg_1) - r15 = unbox(int, r13) - r16 = unbox(int, r14) - r17 = f(r15, r16) - r18 = r17[0] - x = r18 - r19 = r17[1] - y = r19 - r20 = CPyTagged_Add(x, y) - r21 = CPyTagged_Add(s, r20) - s = r21 -L4: - r22 = r3 + 1 - r3 = r22 - r23 = r4 + 1 - r4 = r23 - goto L1 -L5: -L6: - return s - [case testForMapComprehension] def f(x: int, y: int) -> int: return x * y From 8e0d73e31296776d2e7cf87b68bbc52e2247fb7c Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Sat, 16 Aug 2025 11:05:10 -0400 Subject: [PATCH 13/22] Update for_helpers.py --- 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 4e9cf6ad06be..25fb148eddaf 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -1223,7 +1223,6 @@ def gen_condition(self) -> None: def begin_body(self) -> None: builder = self.builder - line = self.line for gen in self.gens: gen.begin_body() @@ -1235,8 +1234,11 @@ def begin_body(self) -> None: [None] * len(self.gens), ) - result = builder.accept(call_expr) - builder.assign(builder.get_assignment_target(self.index), result, line) + # TODO: debug redundant box->unbox op in builder.accept and then replace this + from mypyc.irbuild.expression import transform_call_expr + + result = transform_call_expr(builder, call_expr) + builder.assign(builder.get_assignment_target(self.index), result, self.line) def gen_step(self) -> None: for gen in self.gens: From 260bf051729626bb5fb74ea4675264ff270e61d3 Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Sat, 16 Aug 2025 15:32:15 +0000 Subject: [PATCH 14/22] [mypyc] feat: new primitives for `bytes.rjust` and `bytes.ljust` --- mypyc/lib-rt/bytes_ops.c | 49 ++++++++++++++++++++++ mypyc/primitives/bytes_ops.py | 18 ++++++++ mypyc/test-data/irbuild-bytes.test | 66 ++++++++++++++++++++++++++++++ mypyc/test-data/run-bytes.test | 38 +++++++++++++++++ 4 files changed, 171 insertions(+) diff --git a/mypyc/lib-rt/bytes_ops.c b/mypyc/lib-rt/bytes_ops.c index 6ff34b021a9a..6983ba18875f 100644 --- a/mypyc/lib-rt/bytes_ops.c +++ b/mypyc/lib-rt/bytes_ops.c @@ -4,6 +4,7 @@ #include #include "CPy.h" +#include // Returns -1 on error, 0 on inequality, 1 on equality. // @@ -162,3 +163,51 @@ CPyTagged CPyBytes_Ord(PyObject *obj) { PyErr_SetString(PyExc_TypeError, "ord() expects a character"); return CPY_INT_TAG; } + +PyObject *CPyBytes_Rjust(PyObject *self, Py_ssize_t width, PyObject *fillbyte) { + if (!PyBytes_Check(self)) { + PyErr_SetString(PyExc_TypeError, "self must be bytes"); + return NULL; + } + if (!PyBytes_Check(fillbyte) || PyBytes_Size(fillbyte) != 1) { + PyErr_SetString(PyExc_TypeError, "fillbyte must be a single byte"); + return NULL; + } + Py_ssize_t len = PyBytes_Size(self); + if (width <= len) { + return PyBytes_FromStringAndSize(PyBytes_AsString(self), len); + } + char fill = PyBytes_AsString(fillbyte)[0]; + Py_ssize_t pad = width - len; + PyObject *result = PyBytes_FromStringAndSize(NULL, width); + if (!result) return NULL; + char *res_buf = PyBytes_AsString(result); + memset(res_buf, fill, pad); + memcpy(res_buf + pad, PyBytes_AsString(self), len); + return result; +} + +PyObject *CPyBytes_Ljust(PyObject *self, Py_ssize_t width, PyObject *fillbyte) { + if (!PyBytes_Check(self)) { + PyErr_SetString(PyExc_TypeError, "self must be bytes"); + return NULL; + } + if (!PyBytes_Check(fillbyte) || PyBytes_Size(fillbyte) != 1) { + PyErr_SetString(PyExc_TypeError, "fillbyte must be a single byte"); + return NULL; + } + Py_ssize_t len = PyBytes_Size(self); + if (width <= len) { + return PyBytes_FromStringAndSize(PyBytes_AsString(self), len); + } + char fill = PyBytes_AsString(fillbyte)[0]; + Py_ssize_t pad = width - len; + PyObject *result = PyBytes_FromStringAndSize(NULL, width); + if (!result) return NULL; + char *res_buf = PyBytes_AsString(result); + memcpy(res_buf, PyBytes_AsString(self), len); + memset(res_buf + len, fill, pad); + return result; +} + +// ... existing code ... diff --git a/mypyc/primitives/bytes_ops.py b/mypyc/primitives/bytes_ops.py index c88e89d1a2ba..88155110fc2d 100644 --- a/mypyc/primitives/bytes_ops.py +++ b/mypyc/primitives/bytes_ops.py @@ -126,3 +126,21 @@ c_function_name="CPyBytes_Ord", error_kind=ERR_MAGIC, ) + +# bytes.rjust(width, fillbyte=b' ') +method_op( + name="rjust", + arg_types=[bytes_rprimitive, int_rprimitive, bytes_rprimitive], + return_type=bytes_rprimitive, + c_function_name="CPyBytes_Rjust", + error_kind=ERR_MAGIC, +) + +# bytes.ljust(width, fillbyte=b' ') +method_op( + name="ljust", + arg_types=[bytes_rprimitive, int_rprimitive, bytes_rprimitive], + return_type=bytes_rprimitive, + c_function_name="CPyBytes_Ljust", + error_kind=ERR_MAGIC, +) diff --git a/mypyc/test-data/irbuild-bytes.test b/mypyc/test-data/irbuild-bytes.test index 476c5ac59f48..f79dffa69809 100644 --- a/mypyc/test-data/irbuild-bytes.test +++ b/mypyc/test-data/irbuild-bytes.test @@ -185,3 +185,69 @@ L0: r10 = CPyBytes_Build(2, var, r9) b4 = r10 return 1 + +[case testBytesRjustDefault] +def f(b: bytes) -> bytes: + return b.rjust(6) +[out] +def f(b): + b :: bytes + r0 :: bytes +L0: + r0 = b.rjust(6, b' ') + return r0 + +[case testBytesRjustCustom] +def f(b: bytes) -> bytes: + return b.rjust(8, b'0') +[out] +def f(b): + b :: bytes + r0 :: bytes +L0: + r0 = b.rjust(8, b'0') + return r0 + +[case testBytesLjustDefault] +def f(b: bytes) -> bytes: + return b.ljust(7) +[out] +def f(b): + b :: bytes + r0 :: bytes +L0: + r0 = b.ljust(7, b' ') + return r0 + +[case testBytesLjustCustom] +def f(b: bytes) -> bytes: + return b.ljust(10, b'_') +[out] +def f(b): + b :: bytes + r0 :: bytes +L0: + r0 = b.ljust(10, b'_') + return r0 + +[case testBytesRjustNoPad] +def f(b: bytes) -> bytes: + return b.rjust(2) +[out] +def f(b): + b :: bytes + r0 :: bytes +L0: + r0 = b.rjust(2, b' ') + return r0 + +[case testBytesLjustNoPad] +def f(b: bytes) -> bytes: + return b.ljust(1) +[out] +def f(b): + b :: bytes + r0 :: bytes +L0: + r0 = b.ljust(1, b' ') + return r0 diff --git a/mypyc/test-data/run-bytes.test b/mypyc/test-data/run-bytes.test index 5a285320c849..5d53402d2e33 100644 --- a/mypyc/test-data/run-bytes.test +++ b/mypyc/test-data/run-bytes.test @@ -374,3 +374,41 @@ class subbytearray(bytearray): [file userdefinedbytes.py] class bytes: pass + +[case testBytesRjustLjust] +def rjust_bytes(b: bytes, width: int, fill: bytes = b' ') -> bytes: + return b.rjust(width, fill) + +def ljust_bytes(b: bytes, width: int, fill: bytes = b' ') -> bytes: + return b.ljust(width, fill) + +def test_rjust_with_default_fill() -> None: + assert rjust_bytes(b'abc', 6) == b' abc' + assert rjust_bytes(b'abc', 3) == b'abc' + assert rjust_bytes(b'abc', 2) == b'abc' + assert rjust_bytes(b'', 4) == b' ' + +def test_rjust_with_custom_fill() -> None: + assert rjust_bytes(b'abc', 6, b'0') == b'000abc' + assert rjust_bytes(b'abc', 5, b'_') == b'__abc' + assert rjust_bytes(b'abc', 3, b'X') == b'abc' + +def test_ljust_with_default_fill() -> None: + assert ljust_bytes(b'abc', 6) == b'abc ' + assert ljust_bytes(b'abc', 3) == b'abc' + assert ljust_bytes(b'abc', 2) == b'abc' + assert ljust_bytes(b'', 4) == b' ' + +def test_ljust_with_custom_fill() -> None: + assert ljust_bytes(b'abc', 6, b'0') == b'abc000' + assert ljust_bytes(b'abc', 5, b'_') == b'abc__' + assert ljust_bytes(b'abc', 3, b'X') == b'abc' + +def test_edge_cases() -> None: + assert rjust_bytes(b'abc', 0) == b'abc' + assert ljust_bytes(b'abc', 0) == b'abc' + # fillbyte must be length 1 + with assertRaises(TypeError): + rjust_bytes(b'abc', 5, b'') + with assertRaises(TypeError): + ljust_bytes(b'abc', 5, b'12') From 8247ca64b70e755c46de29df5e9dd87e17909474 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 17 Aug 2025 08:15:07 +0000 Subject: [PATCH 15/22] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypyc/irbuild/specialize.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/mypyc/irbuild/specialize.py b/mypyc/irbuild/specialize.py index 751531869204..6e77328d4a8f 100644 --- a/mypyc/irbuild/specialize.py +++ b/mypyc/irbuild/specialize.py @@ -94,7 +94,7 @@ ) from mypyc.primitives.float_ops import isinstance_float from mypyc.primitives.int_ops import isinstance_int -from mypyc.primitives.list_ops import isinstance_list, new_list_set_item_op, list_append_op +from mypyc.primitives.list_ops import isinstance_list, list_append_op, new_list_set_item_op from mypyc.primitives.misc_ops import isinstance_bool from mypyc.primitives.set_ops import isinstance_frozenset, isinstance_set from mypyc.primitives.str_ops import ( @@ -296,7 +296,8 @@ def translate_list_from_generator_call( and expr.arg_kinds[0] == ARG_POS and isinstance(expr.args[0], CallExpr) and isinstance(expr.args[0].callee, NameExpr) - and expr.args[0].callee.fullname in ( + and expr.args[0].callee.fullname + in ( # TODO: make constant for these vals "builtins.map", "builtins.filter", @@ -307,7 +308,7 @@ def translate_list_from_generator_call( itemtype = builder._analyze_iterable_item_type(call_expr) indextype = builder.type_to_rtype(itemtype) index = Register(indextype, "__mypyc_list_helper__", line=call_expr.line) - + result = builder.new_list_op([], expr.line) def body_insts(): From abd80d2614f1b86ac801da2695bb0f8d498683c4 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Tue, 19 Aug 2025 12:27:00 -0400 Subject: [PATCH 16/22] Update specialize.py --- mypyc/irbuild/specialize.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypyc/irbuild/specialize.py b/mypyc/irbuild/specialize.py index 85b310506e0b..ea36d73466c2 100644 --- a/mypyc/irbuild/specialize.py +++ b/mypyc/irbuild/specialize.py @@ -316,7 +316,7 @@ def translate_list_from_generator_call( result = builder.new_list_op([], expr.line) - def body_insts(): + def body_insts() -> None: builder.primitive_op(list_append_op, [result, index], expr.line) for_loop_helper( From ff35a53249a326f451d56e2ae73dae2cd247bbda Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Tue, 19 Aug 2025 12:44:13 -0400 Subject: [PATCH 17/22] adapt run tests --- mypyc/test-data/run-loops.test | 94 +++++++++++----------------------- 1 file changed, 30 insertions(+), 64 deletions(-) diff --git a/mypyc/test-data/run-loops.test b/mypyc/test-data/run-loops.test index b902d1d7b018..d0ad970438f1 100644 --- a/mypyc/test-data/run-loops.test +++ b/mypyc/test-data/run-loops.test @@ -573,88 +573,54 @@ Vector2(x=-2, y=3.1) \[4, -5.2] [case testRunForMap] -def f(a: list[int]) -> int: +def single(a: list[int]) -> int: s = 0 for x in map(lambda x: x + 1, a): s += x return s - -def g(a: list[int], b: list[int]) -> int: +def double(a: list[int], b: list[int]) -> int: s = 0 for x in map(lambda x, y: x * y, a, b): s += x return s - -[file driver.py] -from native import f, g -print(f([1, 2, 3])) -print(g([1, 2, 3], [4, 5, 6])) -print(g([1, 2], [10, 20, 30])) # Shortest wins: only 2 items - -[out] -9 -32 -50 - -[case testRunForMapEdgeCases] -def f(a: list[int], b: list[int]) -> int: - s = 0 - for x in map(lambda x, y: x + y, a, b): - s += x - return s - -[file driver.py] -from native import f -print(f([], [])) -print(f([1, 2], [])) -print(f([], [1, 2])) - -[out] -0 -0 -0 - -[case testRunForMapThreeArgs] -def f(a: list[int], b: list[int], c: list[int]) -> int: +def triple(a: list[int], b: list[int], c: list[int]) -> int: s = 0 for x in map(lambda x, y, z: x * y + z, a, b, c): s += x return s - -[file driver.py] -from native import f -print(f([1, 2, 3], [4, 5, 6], [7, 8, 9])) -print(f([1, 2], [10, 20, 30], [100, 200, 300])) -print(f([], [], [])) -print(f([1, 2], [], [3, 4])) -[out] -56 -350 -0 -0 - -[case testRunForMapNested] -def f(a: list[int], b: list[int], c: list[int]) -> int: +def nested(a: list[int], b: list[int], c: list[int]) -> int: s = 0 for x in map(lambda x, y: x + y, map(lambda x, y: x * y, a, b), c): s += x return s - -[file driver.py] -from native import f -print(f([1, 2, 3], [4, 5, 6], [7, 8, 9])) -[out] -56 - -[case testRunForMapUnpack] -def f(a: list[int], b: list[int]) -> int: +def unpack(a: list[int], b: list[int]) -> int: s = 0 for x, y in map(lambda x, y: (x, y), a, b): s += x * 10 + y return s -[file driver.py] -from native import f -print(f([1, 2, 3], [4, 5, 6])) -[out] -75 +def test_single() -> None: + assert single([1, 2, 3]) == 9 +def test_double() -> None: + assert double([1, 2, 3], [4, 5, 6]) == 32 +def test_double_uneven() -> None: + # Shortest wins: only 2 items + assert double([1, 2], [10, 20, 30]) == 50 +def test_double_empty() -> None: + assert double([], []) == 0 +def test_double_empty_first() -> None: + assert double([], [1, 2]) == 0 +def test_double_empty_second() -> None: + assert double([1, 2], []) == 0 +def test_triple() -> None: + assert triple([1, 2, 3], [4, 5, 6], [7, 8, 9]) == 56 +def test_triple_uneven() -> None: + assert triple([1, 2], [10, 20, 30], [100, 200, 300]) == 350 +def test_triple_empty() -> None: + assert triple([], [], []) == 0 +def test_triple_one_empty() -> None: + assert triple([1, 2], [], [3, 4]) == 0 +def test_nested() -> None: + assert nested([1, 2, 3], [4, 5, 6], [7, 8, 9]) == 56 +def test_unpack() -> None: + assert unpack([1, 2, 3], [4, 5, 6]) == 75 From 854e8d8ba8c497556ebb7a403d4d88fda38bf658 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Wed, 20 Aug 2025 17:51:39 -0400 Subject: [PATCH 18/22] Update bytes_ops.c --- mypyc/lib-rt/bytes_ops.c | 49 ---------------------------------------- 1 file changed, 49 deletions(-) diff --git a/mypyc/lib-rt/bytes_ops.c b/mypyc/lib-rt/bytes_ops.c index 6983ba18875f..6ff34b021a9a 100644 --- a/mypyc/lib-rt/bytes_ops.c +++ b/mypyc/lib-rt/bytes_ops.c @@ -4,7 +4,6 @@ #include #include "CPy.h" -#include // Returns -1 on error, 0 on inequality, 1 on equality. // @@ -163,51 +162,3 @@ CPyTagged CPyBytes_Ord(PyObject *obj) { PyErr_SetString(PyExc_TypeError, "ord() expects a character"); return CPY_INT_TAG; } - -PyObject *CPyBytes_Rjust(PyObject *self, Py_ssize_t width, PyObject *fillbyte) { - if (!PyBytes_Check(self)) { - PyErr_SetString(PyExc_TypeError, "self must be bytes"); - return NULL; - } - if (!PyBytes_Check(fillbyte) || PyBytes_Size(fillbyte) != 1) { - PyErr_SetString(PyExc_TypeError, "fillbyte must be a single byte"); - return NULL; - } - Py_ssize_t len = PyBytes_Size(self); - if (width <= len) { - return PyBytes_FromStringAndSize(PyBytes_AsString(self), len); - } - char fill = PyBytes_AsString(fillbyte)[0]; - Py_ssize_t pad = width - len; - PyObject *result = PyBytes_FromStringAndSize(NULL, width); - if (!result) return NULL; - char *res_buf = PyBytes_AsString(result); - memset(res_buf, fill, pad); - memcpy(res_buf + pad, PyBytes_AsString(self), len); - return result; -} - -PyObject *CPyBytes_Ljust(PyObject *self, Py_ssize_t width, PyObject *fillbyte) { - if (!PyBytes_Check(self)) { - PyErr_SetString(PyExc_TypeError, "self must be bytes"); - return NULL; - } - if (!PyBytes_Check(fillbyte) || PyBytes_Size(fillbyte) != 1) { - PyErr_SetString(PyExc_TypeError, "fillbyte must be a single byte"); - return NULL; - } - Py_ssize_t len = PyBytes_Size(self); - if (width <= len) { - return PyBytes_FromStringAndSize(PyBytes_AsString(self), len); - } - char fill = PyBytes_AsString(fillbyte)[0]; - Py_ssize_t pad = width - len; - PyObject *result = PyBytes_FromStringAndSize(NULL, width); - if (!result) return NULL; - char *res_buf = PyBytes_AsString(result); - memcpy(res_buf, PyBytes_AsString(self), len); - memset(res_buf + len, fill, pad); - return result; -} - -// ... existing code ... From d8d7bcfcee7d4485ff357eea0925ff6229dbdb81 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Wed, 20 Aug 2025 17:51:56 -0400 Subject: [PATCH 19/22] Update bytes_ops.py --- mypyc/primitives/bytes_ops.py | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/mypyc/primitives/bytes_ops.py b/mypyc/primitives/bytes_ops.py index 88155110fc2d..c88e89d1a2ba 100644 --- a/mypyc/primitives/bytes_ops.py +++ b/mypyc/primitives/bytes_ops.py @@ -126,21 +126,3 @@ c_function_name="CPyBytes_Ord", error_kind=ERR_MAGIC, ) - -# bytes.rjust(width, fillbyte=b' ') -method_op( - name="rjust", - arg_types=[bytes_rprimitive, int_rprimitive, bytes_rprimitive], - return_type=bytes_rprimitive, - c_function_name="CPyBytes_Rjust", - error_kind=ERR_MAGIC, -) - -# bytes.ljust(width, fillbyte=b' ') -method_op( - name="ljust", - arg_types=[bytes_rprimitive, int_rprimitive, bytes_rprimitive], - return_type=bytes_rprimitive, - c_function_name="CPyBytes_Ljust", - error_kind=ERR_MAGIC, -) From 5c400d47bab0aebc7afee2fcf2bf144dad07e1cb Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Wed, 20 Aug 2025 17:52:24 -0400 Subject: [PATCH 20/22] Update run-bytes.test --- mypyc/test-data/run-bytes.test | 38 ---------------------------------- 1 file changed, 38 deletions(-) diff --git a/mypyc/test-data/run-bytes.test b/mypyc/test-data/run-bytes.test index 5d53402d2e33..5a285320c849 100644 --- a/mypyc/test-data/run-bytes.test +++ b/mypyc/test-data/run-bytes.test @@ -374,41 +374,3 @@ class subbytearray(bytearray): [file userdefinedbytes.py] class bytes: pass - -[case testBytesRjustLjust] -def rjust_bytes(b: bytes, width: int, fill: bytes = b' ') -> bytes: - return b.rjust(width, fill) - -def ljust_bytes(b: bytes, width: int, fill: bytes = b' ') -> bytes: - return b.ljust(width, fill) - -def test_rjust_with_default_fill() -> None: - assert rjust_bytes(b'abc', 6) == b' abc' - assert rjust_bytes(b'abc', 3) == b'abc' - assert rjust_bytes(b'abc', 2) == b'abc' - assert rjust_bytes(b'', 4) == b' ' - -def test_rjust_with_custom_fill() -> None: - assert rjust_bytes(b'abc', 6, b'0') == b'000abc' - assert rjust_bytes(b'abc', 5, b'_') == b'__abc' - assert rjust_bytes(b'abc', 3, b'X') == b'abc' - -def test_ljust_with_default_fill() -> None: - assert ljust_bytes(b'abc', 6) == b'abc ' - assert ljust_bytes(b'abc', 3) == b'abc' - assert ljust_bytes(b'abc', 2) == b'abc' - assert ljust_bytes(b'', 4) == b' ' - -def test_ljust_with_custom_fill() -> None: - assert ljust_bytes(b'abc', 6, b'0') == b'abc000' - assert ljust_bytes(b'abc', 5, b'_') == b'abc__' - assert ljust_bytes(b'abc', 3, b'X') == b'abc' - -def test_edge_cases() -> None: - assert rjust_bytes(b'abc', 0) == b'abc' - assert ljust_bytes(b'abc', 0) == b'abc' - # fillbyte must be length 1 - with assertRaises(TypeError): - rjust_bytes(b'abc', 5, b'') - with assertRaises(TypeError): - ljust_bytes(b'abc', 5, b'12') From 75dad007c18af10f1c3328ed9d0d35b6ef8f98a0 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Wed, 20 Aug 2025 17:52:54 -0400 Subject: [PATCH 21/22] Update irbuild-bytes.test --- mypyc/test-data/irbuild-bytes.test | 66 ------------------------------ 1 file changed, 66 deletions(-) diff --git a/mypyc/test-data/irbuild-bytes.test b/mypyc/test-data/irbuild-bytes.test index f79dffa69809..476c5ac59f48 100644 --- a/mypyc/test-data/irbuild-bytes.test +++ b/mypyc/test-data/irbuild-bytes.test @@ -185,69 +185,3 @@ L0: r10 = CPyBytes_Build(2, var, r9) b4 = r10 return 1 - -[case testBytesRjustDefault] -def f(b: bytes) -> bytes: - return b.rjust(6) -[out] -def f(b): - b :: bytes - r0 :: bytes -L0: - r0 = b.rjust(6, b' ') - return r0 - -[case testBytesRjustCustom] -def f(b: bytes) -> bytes: - return b.rjust(8, b'0') -[out] -def f(b): - b :: bytes - r0 :: bytes -L0: - r0 = b.rjust(8, b'0') - return r0 - -[case testBytesLjustDefault] -def f(b: bytes) -> bytes: - return b.ljust(7) -[out] -def f(b): - b :: bytes - r0 :: bytes -L0: - r0 = b.ljust(7, b' ') - return r0 - -[case testBytesLjustCustom] -def f(b: bytes) -> bytes: - return b.ljust(10, b'_') -[out] -def f(b): - b :: bytes - r0 :: bytes -L0: - r0 = b.ljust(10, b'_') - return r0 - -[case testBytesRjustNoPad] -def f(b: bytes) -> bytes: - return b.rjust(2) -[out] -def f(b): - b :: bytes - r0 :: bytes -L0: - r0 = b.rjust(2, b' ') - return r0 - -[case testBytesLjustNoPad] -def f(b: bytes) -> bytes: - return b.ljust(1) -[out] -def f(b): - b :: bytes - r0 :: bytes -L0: - r0 = b.ljust(1, b' ') - return r0 From d80223b6b8b02230de8a2c54541cebd8fb038c92 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 19:56:49 +0000 Subject: [PATCH 22/22] [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 403740bef057..0642fa1d9777 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -1259,7 +1259,7 @@ def gen_cleanup(self) -> None: for gen in self.gens: gen.gen_cleanup() - + def get_expr_length(expr: Expression) -> int | None: if isinstance(expr, (StrExpr, BytesExpr)): return len(expr.value)