Skip to content

Commit ff24ef3

Browse files
committed
[mypyc] Match evaluation order of multiple assignment from iterable with Python (python#793)
Refactored to iterate rvalues first before performing any assignments. Closes mypyc/mypyc#793.
1 parent e8afde6 commit ff24ef3

File tree

2 files changed

+109
-14
lines changed

2 files changed

+109
-14
lines changed

mypyc/irbuild/builder.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -579,7 +579,8 @@ def process_iterator_tuple_assignment(self,
579579
# This may be the whole lvalue list if there is no starred value
580580
split_idx = target.star_idx if target.star_idx is not None else len(target.items)
581581

582-
# Assign values before the first starred value
582+
# Read values before the first starred value
583+
values = []
583584
for litem in target.items[:split_idx]:
584585
ritem = self.call_c(next_op, [iterator], line)
585586
error_block, ok_block = BasicBlock(), BasicBlock()
@@ -592,9 +593,13 @@ def process_iterator_tuple_assignment(self,
592593

593594
self.activate_block(ok_block)
594595

596+
values.append(ritem)
597+
598+
# Assign read values to target lvalues
599+
for litem, ritem in zip(target.items[:split_idx], values):
595600
self.assign(litem, ritem, line)
596601

597-
# Assign the starred value and all values after it
602+
# Read the starred value and all values after it
598603
if target.star_idx is not None:
599604
post_star_vals = target.items[split_idx + 1:]
600605
iter_list = self.call_c(to_list, [iterator], line)
@@ -612,8 +617,13 @@ def process_iterator_tuple_assignment(self,
612617

613618
self.activate_block(ok_block)
614619

620+
values = []
615621
for litem in reversed(post_star_vals):
616622
ritem = self.call_c(list_pop_last, [iter_list], line)
623+
values.append(ritem)
624+
625+
# Assign the read values to target lvalues
626+
for litem, ritem in zip(reversed(post_star_vals), values):
617627
self.assign(litem, ritem, line)
618628

619629
# Assign the starred value

mypyc/test-data/irbuild-statements.test

Lines changed: 97 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -521,9 +521,9 @@ L0:
521521
def from_any(a):
522522
a, r0, r1 :: object
523523
r2 :: bool
524-
x, r3 :: object
524+
r3 :: object
525525
r4 :: bool
526-
y, r5 :: object
526+
x, y, r5 :: object
527527
r6 :: bool
528528
L0:
529529
r0 = PyObject_GetIter(a)
@@ -533,13 +533,13 @@ L1:
533533
r2 = raise ValueError('not enough values to unpack')
534534
unreachable
535535
L2:
536-
x = r1
537536
r3 = PyIter_Next(r0)
538537
if is_error(r3) goto L3 else goto L4
539538
L3:
540539
r4 = raise ValueError('not enough values to unpack')
541540
unreachable
542541
L4:
542+
x = r1
543543
y = r3
544544
r5 = PyIter_Next(r0)
545545
if is_error(r5) goto L6 else goto L5
@@ -577,9 +577,9 @@ L0:
577577
def from_any(a):
578578
a, r0, r1 :: object
579579
r2 :: bool
580-
r3, x :: int
581-
r4 :: object
582-
r5 :: bool
580+
r3 :: object
581+
r4 :: bool
582+
r5, x :: int
583583
y, r6 :: object
584584
r7 :: bool
585585
L0:
@@ -590,15 +590,15 @@ L1:
590590
r2 = raise ValueError('not enough values to unpack')
591591
unreachable
592592
L2:
593-
r3 = unbox(int, r1)
594-
x = r3
595-
r4 = PyIter_Next(r0)
596-
if is_error(r4) goto L3 else goto L4
593+
r3 = PyIter_Next(r0)
594+
if is_error(r3) goto L3 else goto L4
597595
L3:
598-
r5 = raise ValueError('not enough values to unpack')
596+
r4 = raise ValueError('not enough values to unpack')
599597
unreachable
600598
L4:
601-
y = r4
599+
r5 = unbox(int, r1)
600+
x = r5
601+
y = r3
602602
r6 = PyIter_Next(r0)
603603
if is_error(r6) goto L6 else goto L5
604604
L5:
@@ -607,6 +607,91 @@ L5:
607607
L6:
608608
return 1
609609

610+
[case testStarUnpack]
611+
from typing import Any, List, Iterator
612+
613+
it: Iterator = iter(['x', 'y', 'z1', 'z2', 'z3', 'u', 'w'])
614+
615+
def f(a: Any) -> None:
616+
a.x, a.y, *a.z, a.u, a.w = it
617+
[out]
618+
def f(a):
619+
a :: object
620+
r0 :: dict
621+
r1 :: str
622+
r2, r3, r4 :: object
623+
r5 :: bool
624+
r6 :: object
625+
r7 :: bool
626+
r8 :: str
627+
r9 :: int32
628+
r10 :: bit
629+
r11 :: str
630+
r12 :: int32
631+
r13 :: bit
632+
r14 :: list
633+
r15 :: ptr
634+
r16 :: int64
635+
r17 :: short_int
636+
r18 :: bit
637+
r19 :: bool
638+
r20, r21 :: object
639+
r22 :: str
640+
r23 :: int32
641+
r24 :: bit
642+
r25 :: str
643+
r26 :: int32
644+
r27 :: bit
645+
r28 :: str
646+
r29 :: int32
647+
r30 :: bit
648+
L0:
649+
r0 = __main__.globals :: static
650+
r1 = 'it'
651+
r2 = CPyDict_GetItem(r0, r1)
652+
r3 = PyObject_GetIter(r2)
653+
r4 = PyIter_Next(r3)
654+
if is_error(r4) goto L1 else goto L2
655+
L1:
656+
r5 = raise ValueError('not enough values to unpack')
657+
unreachable
658+
L2:
659+
r6 = PyIter_Next(r3)
660+
if is_error(r6) goto L3 else goto L4
661+
L3:
662+
r7 = raise ValueError('not enough values to unpack')
663+
unreachable
664+
L4:
665+
r8 = 'x'
666+
r9 = PyObject_SetAttr(a, r8, r4)
667+
r10 = r9 >= 0 :: signed
668+
r11 = 'y'
669+
r12 = PyObject_SetAttr(a, r11, r6)
670+
r13 = r12 >= 0 :: signed
671+
r14 = PySequence_List(r3)
672+
r15 = get_element_ptr r14 ob_size :: PyVarObject
673+
r16 = load_mem r15 :: int64*
674+
keep_alive r14
675+
r17 = r16 << 1
676+
r18 = 4 <= r17 :: signed
677+
if r18 goto L6 else goto L5 :: bool
678+
L5:
679+
r19 = raise ValueError('not enough values to unpack')
680+
unreachable
681+
L6:
682+
r20 = CPyList_PopLast(r14)
683+
r21 = CPyList_PopLast(r14)
684+
r22 = 'w'
685+
r23 = PyObject_SetAttr(a, r22, r20)
686+
r24 = r23 >= 0 :: signed
687+
r25 = 'u'
688+
r26 = PyObject_SetAttr(a, r25, r21)
689+
r27 = r26 >= 0 :: signed
690+
r28 = 'z'
691+
r29 = PyObject_SetAttr(a, r28, r14)
692+
r30 = r29 >= 0 :: signed
693+
return 1
694+
610695
[case testMultiAssignmentNested]
611696
from typing import Tuple, Any, List
612697

0 commit comments

Comments
 (0)