diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index 5ef8d8594c150..ac02ebd93ffbf 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -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() @@ -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) @@ -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 @@ -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) diff --git a/mypyc/test-data/irbuild-statements.test b/mypyc/test-data/irbuild-statements.test index d1e9f69a59e0c..e4da35ccb6bee 100644 --- a/mypyc/test-data/irbuild-statements.test +++ b/mypyc/test-data/irbuild-statements.test @@ -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) @@ -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] @@ -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) @@ -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 + +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]