Repository navigation
Conversation
|
now that we evaluate the loop target correctly, there's an issue when the loop target affects the iterable that we iterate over: def index(items: list[int], calls: list[int]) -> int:
calls.append(1)
if len(calls) == 2:
items.clear()
return 0
def test_enumerate() -> None:
items = [10, 20]
target = [-1]
calls: list[int] = []
result: list[int] = []
for target[index(items, calls)], value in enumerate(items):
result.append(value)
assert items == []
assert result == [10, 20]
def test_zip() -> None:
items = [10, 20]
target = [-1]
calls: list[int] = []
result: list[int] = []
for target[index(items, calls)], value in zip(range(2), items):
result.append(value)
assert items == []
assert result == [10, 20]we fetch the value from |
|
@p-sawicki Sure, though I will say that seems like a related but different bug that appears on both master and this PR. I've gone ahead and created a commit for that though. I can drop it from here and add it to another PR if you'd like. When nested in Unrelated crashes found while testingPre-existing on master, I'll create issues and/or PRs for these when I have a bit more free time:
|
Fixes mypyc/mypyc#1230.
ForRange.init()assigned the start value to the loop variable before the first condition check. Aforloop over an emptyrange()therefore still set the variable, andfor obj.attr in range(3)called the property setter four times. CPython leaves the variable untouched when the range is empty, so a value assigned before the loop should survive, or the variable should stay unbound. The assignment was left over from when the loop variable was also the counter. Since #21098,begin_body()assigns the variable at the start of each iteration, so the initial assignment isn't needed. Without it, reading a variable that may be unbound after the loop raisesUnboundLocalError, as it does for other loops.ForRangeandForInfiniteCounter(the index ofenumerate()) also evaluated the loop target only once, before the loop. They now callget_assignment_target()inbegin_body(), like the other loop generators. A target such asa[f()]is now evaluated on every iteration, and not at all for an empty loop.The IR test changes drop the assignment before the loop, and with it a short int to
i64conversion intestVecI64ConstructFromRange. The loop variable's register is now first assigned in the loop body, so it's declared later.New run tests cover:
intandi64).range()insidezip()andenumerate().for self.x in range(n)in__init__, which leavesxundefined whennis 0.range()andenumerate()loops.