Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
19 changes: 16 additions & 3 deletions mypyc/irbuild/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -931,7 +931,9 @@ def process_iterator_tuple_assignment(
# This may be the whole lvalue list if there is no starred value
split_idx = target.star_idx if target.star_idx is not None else len(target.items)

# Assign values before the first starred value
# Read values before the first starred value
left_values = []
right_values = []
for litem in target.items[:split_idx]:
ritem = self.call_c(next_op, [iterator], line)
error_block, ok_block = BasicBlock(), BasicBlock()
Expand All @@ -947,9 +949,9 @@ def process_iterator_tuple_assignment(

self.activate_block(ok_block)

self.assign(litem, ritem, line)
left_values.append(ritem)

# Assign the starred value and all values after it
# Read the starred value and all values after it
if target.star_idx is not None:
post_star_vals = target.items[split_idx + 1 :]
iter_list = self.primitive_op(to_list, [iterator], line)
Expand All @@ -972,6 +974,13 @@ def process_iterator_tuple_assignment(

for litem in reversed(post_star_vals):
ritem = self.primitive_op(list_pop_last, [iter_list], line)
right_values.append(ritem)

# Assign read values to target lvalues
for litem, ritem in zip(target.items[:split_idx], left_values):
self.assign(litem, ritem, line)

for litem, ritem in zip(reversed(post_star_vals), right_values):
self.assign(litem, ritem, line)

# Assign the starred value
Expand All @@ -994,6 +1003,10 @@ def process_iterator_tuple_assignment(

self.activate_block(ok_block)

# Assign read values to target lvalues
for litem, ritem in zip(target.items[:split_idx], left_values):
self.assign(litem, ritem, line)

def push_loop_stack(self, continue_block: BasicBlock, break_block: BasicBlock) -> None:
self.nonlocal_control.append(
LoopNonlocalControl(self.nonlocal_control[-1], continue_block, break_block)
Expand Down
118 changes: 101 additions & 17 deletions mypyc/test-data/irbuild-statements.test
Original file line number Diff line number Diff line change
Expand Up @@ -443,10 +443,11 @@ L0:
def from_any(a):
a, r0, r1 :: object
r2 :: bool
x, r3 :: object
r3 :: object
r4 :: bool
y, r5 :: object
r5 :: object
r6 :: bool
x, y :: object
L0:
r0 = PyObject_GetIter(a)
r1 = PyIter_Next(r0)
Expand All @@ -455,20 +456,20 @@ L1:
r2 = raise ValueError('not enough values to unpack')
unreachable
L2:
x = r1
r3 = PyIter_Next(r0)
if is_error(r3) goto L3 else goto L4
L3:
r4 = raise ValueError('not enough values to unpack')
unreachable
L4:
y = r3
r5 = PyIter_Next(r0)
if is_error(r5) goto L6 else goto L5
L5:
r6 = raise ValueError('too many values to unpack')
unreachable
L6:
x = r1
y = r3
return 1

[case testMultiAssignmentCoercions]
Expand Down Expand Up @@ -504,11 +505,12 @@ L0:
def from_any(a):
a, r0, r1 :: object
r2 :: bool
r3, x :: int
r4 :: object
r5 :: bool
y, r6 :: object
r7 :: bool
r3 :: object
r4 :: bool
r5 :: object
r6 :: bool
r7, x :: int
y :: object
L0:
r0 = PyObject_GetIter(a)
r1 = PyIter_Next(r0)
Expand All @@ -517,21 +519,103 @@ L1:
r2 = raise ValueError('not enough values to unpack')
unreachable
L2:
r3 = unbox(int, r1)
x = r3
r4 = PyIter_Next(r0)
if is_error(r4) goto L3 else goto L4
r3 = PyIter_Next(r0)
if is_error(r3) goto L3 else goto L4
L3:
r4 = raise ValueError('not enough values to unpack')
unreachable
L4:
r5 = PyIter_Next(r0)
if is_error(r5) goto L6 else goto L5
L5:
r6 = raise ValueError('too many values to unpack')
unreachable
L6:
r7 = unbox(int, r1)
x = r7
y = r3
return 1

[case testStarUnpack]
from typing import Any, List, Iterator

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

testMultiAssignmentStarUnpack might be a better name?


it: Iterator = iter(['x', 'y', 'z1', 'z2', 'z3', 'u', 'w'])

def f(a: Any) -> None:
a.x, a.y, *a.z, a.u, a.w = it
[out]
def f(a):
a :: object
r0 :: dict
r1 :: str
r2, r3, r4 :: object
r5 :: bool
r6 :: object
r7 :: bool
r8 :: list
r9 :: native_int
r10 :: short_int
r11 :: bit
r12 :: bool
r13, r14 :: object
r15 :: str
r16 :: i32
r17 :: bit
r18 :: str
r19 :: i32
r20 :: bit
r21 :: str
r22 :: i32
r23 :: bit
r24 :: str
r25 :: i32
r26 :: bit
r27 :: str
r28 :: i32
r29 :: bit
L0:
r0 = __main__.globals :: static
r1 = 'it'
r2 = CPyDict_GetItem(r0, r1)
r3 = PyObject_GetIter(r2)
r4 = PyIter_Next(r3)
if is_error(r4) goto L1 else goto L2
L1:
r5 = raise ValueError('not enough values to unpack')
unreachable
L2:
r6 = PyIter_Next(r3)
if is_error(r6) goto L3 else goto L4
L3:
r7 = raise ValueError('not enough values to unpack')
unreachable
L4:
y = r4
r6 = PyIter_Next(r0)
if is_error(r6) goto L6 else goto L5
r8 = PySequence_List(r3)
r9 = var_object_size r8
r10 = r9 << 1
r11 = int_le 4, r10
if r11 goto L6 else goto L5 :: bool
L5:
r7 = raise ValueError('too many values to unpack')
r12 = raise ValueError('not enough values to unpack')
unreachable
L6:
r13 = CPyList_PopLast(r8)
r14 = CPyList_PopLast(r8)
r15 = 'x'
r16 = PyObject_SetAttr(a, r15, r4)
r17 = r16 >= 0 :: signed
r18 = 'y'
r19 = PyObject_SetAttr(a, r18, r6)
r20 = r19 >= 0 :: signed
r21 = 'w'
r22 = PyObject_SetAttr(a, r21, r13)
r23 = r22 >= 0 :: signed
r24 = 'u'
r25 = PyObject_SetAttr(a, r24, r14)
r26 = r25 >= 0 :: signed
r27 = 'z'
r28 = PyObject_SetAttr(a, r27, r8)
r29 = r28 >= 0 :: signed
return 1

[case testMultiAssignmentNested]
Expand Down
Loading