Skip to content
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
33996d8
[mypyc] feat: ForMap generator helper for `builtins.map`
BobTheBuidler Aug 12, 2025
56b5102
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 12, 2025
8e12f83
fix: rtypes
BobTheBuidler Aug 12, 2025
4c64885
Merge branch 'for-map' of https://github.com/BobTheBuidler/mypy into …
BobTheBuidler Aug 12, 2025
c09515f
remove accidental commit
BobTheBuidler Aug 12, 2025
b3acdf8
Update for_helpers.py
BobTheBuidler Aug 13, 2025
5356bc1
Update for_helpers.py
BobTheBuidler Aug 13, 2025
4f75c13
Merge branch 'master' into for-map
BobTheBuidler Aug 13, 2025
fcc221e
Update native_operations.rst
BobTheBuidler Aug 14, 2025
4712ea9
Merge branch 'master' into for-map
BobTheBuidler Aug 14, 2025
684e478
translate list map
BobTheBuidler Aug 15, 2025
25ed859
Update for_helpers.py
BobTheBuidler Aug 16, 2025
80d578d
Update ir.py
BobTheBuidler Aug 16, 2025
ff59472
Update run-loops.test
BobTheBuidler Aug 16, 2025
026b2ef
Update irbuild-basic.test
BobTheBuidler Aug 16, 2025
8e0d73e
Update for_helpers.py
BobTheBuidler Aug 16, 2025
260bf05
[mypyc] feat: new primitives for `bytes.rjust` and `bytes.ljust`
BobTheBuidler Aug 16, 2025
ff1d1d8
Merge branch 'for-map' of https://github.com/BobTheBuidler/mypy into …
BobTheBuidler Aug 17, 2025
8247ca6
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 17, 2025
71025a7
Merge branch 'master' into for-map
BobTheBuidler Aug 19, 2025
abd80d2
Update specialize.py
BobTheBuidler Aug 19, 2025
ff35a53
adapt run tests
BobTheBuidler Aug 19, 2025
85e549b
Merge branch 'master' into for-map
BobTheBuidler Aug 20, 2025
854e8d8
Update bytes_ops.c
BobTheBuidler Aug 20, 2025
d8d7bcf
Update bytes_ops.py
BobTheBuidler Aug 20, 2025
5c400d4
Update run-bytes.test
BobTheBuidler Aug 20, 2025
75dad00
Update irbuild-bytes.test
BobTheBuidler Aug 20, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions mypyc/doc/native_operations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -54,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(...)``
82 changes: 82 additions & 0 deletions mypyc/irbuild/for_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

from mypy.nodes import (
ARG_POS,
LDEF,
CallExpr,
DictionaryComprehension,
Expression,
Expand All @@ -22,6 +23,7 @@
SetExpr,
TupleExpr,
TypeAlias,
Var,
)
from mypyc.ir.ops import (
ERR_NEVER,
Expand Down Expand Up @@ -491,6 +493,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)
Expand Down Expand Up @@ -1166,3 +1178,73 @@ 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.type_to_rtype(var_type))
self.gens.append(
make_for_loop_generator(
self.builder,
name_expr,
iterable_expr,
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,
[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()
24 changes: 24 additions & 0 deletions mypyc/test-data/fixtures/ir.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -406,3 +408,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: ...
Loading
Loading