Skip to content

Commit fce759d

Browse files
committed
more stuff
1 parent bb36ef3 commit fce759d

File tree

4 files changed

+100
-20
lines changed

4 files changed

+100
-20
lines changed

mypyc/irbuild/for_helpers.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686

8787
GenFunc = Callable[[], None]
8888

89+
8990
def for_loop_helper(
9091
builder: IRBuilder,
9192
index: Lvalue,
@@ -94,7 +95,7 @@ def for_loop_helper(
9495
else_insts: GenFunc | None,
9596
is_async: bool,
9697
line: int,
97-
can_unroll: bool = True,
98+
can_unroll: bool = False,
9899
) -> None:
99100
"""Generate IR for a loop.
100101

mypyc/irbuild/statement.py

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from typing import Callable
1414

1515
import mypy.nodes
16+
import mypy.traverser
1617
from mypy.nodes import (
1718
ARG_NAMED,
1819
ARG_POS,
@@ -130,6 +131,29 @@
130131
ValueGenFunc = Callable[[], Value]
131132

132133

134+
class ControlFlowDetector(mypy.traverser.TraverserVisitor):
135+
"""
136+
A Visitor class that detects whether a block contains any of the following control flow statements:
137+
138+
- return
139+
- continue
140+
- break
141+
142+
"""
143+
def __init__(self) -> None:
144+
super().__init__()
145+
self.has_control_flow = False
146+
147+
def visit_break_stmt(self, o: BreakStmt) -> None:
148+
self.has_control_flow = True
149+
150+
def visit_continue_stmt(self, o: ContinueStmt) -> None:
151+
self.has_control_flow = True
152+
153+
def visit_return_stmt(self, o: ReturnStmt) -> None:
154+
self.has_control_flow = True
155+
156+
133157
def transform_block(builder: IRBuilder, block: Block) -> None:
134158
if not block.is_unreachable:
135159
builder.block_reachable_stack.append(True)
@@ -434,16 +458,37 @@ def transform_while_stmt(builder: IRBuilder, s: WhileStmt) -> None:
434458

435459

436460
def transform_for_stmt(builder: IRBuilder, s: ForStmt) -> None:
461+
# Check for control flow statements in the loop body
462+
detector = ControlFlowDetector()
463+
s.body.accept(detector)
464+
can_unroll = s.else_body is None and not detector.has_control_flow
465+
437466
def body() -> None:
438467
builder.accept(s.body)
439468

440469
def else_block() -> None:
441470
assert s.else_body is not None
442471
builder.accept(s.else_body)
443472

444-
for_loop_helper(
445-
builder, s.index, s.expr, body, else_block if s.else_body else None, s.is_async, s.line
446-
)
473+
while True:
474+
try:
475+
# for whatever reason this can blow up with can_unroll=True
476+
# but in those cases we can just fall back to the existing for loop logic.
477+
for_loop_helper(
478+
builder,
479+
s.index,
480+
s.expr,
481+
body,
482+
else_block if s.else_body else None,
483+
s.is_async,
484+
s.line,
485+
can_unroll=can_unroll,
486+
)
487+
return
488+
except AssertionError:
489+
if not can_unroll:
490+
raise
491+
can_unroll = False
447492

448493

449494
def transform_break_stmt(builder: IRBuilder, node: BreakStmt) -> None:

mypyc/test-data/irbuild-basic.test

Lines changed: 49 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3626,22 +3626,32 @@ def f(s: str) -> str:
36263626
return out
36273627
[out]
36283628
def f(s):
3629-
s :: str
3629+
s, r0, out :: str
3630+
r1, r2 :: native_int
3631+
r3, r4 :: bit
3632+
r5, c, r6 :: str
3633+
r7 :: native_int
36303634
out, c :: str
3631-
r0 :: int
3632-
L0:
3633-
out = ''
3634-
r0 = 0
3635-
L1:
3636-
r1 = r0 < CPyStr_Size(s)
3637-
if r1 goto L2 else goto L4 :: bool
3638-
L2:
3639-
c = CPyStr_GetItemUnsafe(s, r0)
3640-
out = CPyStr_Append(out, c)
3641-
r0 = r0 + 1
3642-
goto L1
3643-
L4:
3644-
return out
3635+
L0:
3636+
r0 = ''
3637+
out = r0
3638+
r1 = 0
3639+
L1:
3640+
r2 = CPyStr_Size_size_t(s)
3641+
r3 = r2 >= 0 :: signed
3642+
r4 = r1 < r2 :: signed
3643+
if r4 goto L2 else goto L2 :: bool
3644+
L2:
3645+
r5 = CPyStr_GetItemUnsafe(s, r1)
3646+
c = r5
3647+
r6 = CPyStr_Append(out, c)
3648+
out = r6
3649+
L3:
3650+
r7 = r1 + 1
3651+
r1 = r7
3652+
goto L1
3653+
L4:
3654+
return out
36453655

36463656
[case TestForOverCompledTupleExpr]
36473657
def f() -> None:
@@ -3650,4 +3660,27 @@ def f() -> None:
36503660
y = x
36513661
[out]
36523662
def f():
3653-
abc :: tuple[int, int, str]
3663+
r0 :: str
3664+
r1, abc :: tuple[int, int, str]
3665+
r2 :: int
3666+
r3 :: object
3667+
x, y :: union[int, str]
3668+
r4 :: int
3669+
r5 :: object
3670+
r6 :: str
3671+
L0:
3672+
r0 = CPyTagged_Str(6)
3673+
r1 = (2, 4, r0)
3674+
abc = r1
3675+
r2 = abc[0]
3676+
r3 = box(int, r2)
3677+
x = r3
3678+
y = x
3679+
r4 = abc[1]
3680+
r5 = box(int, r4)
3681+
x = r5
3682+
y = x
3683+
r6 = abc[2]
3684+
x = r6
3685+
y = x
3686+
return 1

mypyc/test-data/irbuild-set.test

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -737,6 +737,7 @@ def not_precomputed() -> None:
737737
def precomputed():
738738
r0 :: str
739739
_ :: object
740+
r1, r2 :: str
740741
L0:
741742
r0 = 'None'
742743
_ = r0

0 commit comments

Comments
 (0)