From ff24ef3666cca3141dedd818b2bfa9d824889e33 Mon Sep 17 00:00:00 2001 From: chetan22 Date: Fri, 7 May 2021 20:57:48 +0530 Subject: [PATCH 1/2] [mypyc] Match evaluation order of multiple assignment from iterable with Python (#793) Refactored to iterate rvalues first before performing any assignments. Closes mypyc/mypyc#793. --- mypyc/irbuild/builder.py | 14 ++- mypyc/test-data/irbuild-statements.test | 109 +++++++++++++++++++++--- 2 files changed, 109 insertions(+), 14 deletions(-) diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index 0b2a6537c5ea..6e2664a80b6b 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -579,7 +579,8 @@ def process_iterator_tuple_assignment(self, # 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 + values = [] for litem in target.items[:split_idx]: ritem = self.call_c(next_op, [iterator], line) error_block, ok_block = BasicBlock(), BasicBlock() @@ -592,9 +593,13 @@ def process_iterator_tuple_assignment(self, self.activate_block(ok_block) + values.append(ritem) + + # Assign read values to target lvalues + for litem, ritem in zip(target.items[:split_idx], values): self.assign(litem, ritem, line) - # 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.call_c(to_list, [iterator], line) @@ -612,8 +617,13 @@ def process_iterator_tuple_assignment(self, self.activate_block(ok_block) + values = [] for litem in reversed(post_star_vals): ritem = self.call_c(list_pop_last, [iter_list], line) + values.append(ritem) + + # Assign the read values to target lvalues + for litem, ritem in zip(reversed(post_star_vals), values): self.assign(litem, ritem, line) # Assign the starred value diff --git a/mypyc/test-data/irbuild-statements.test b/mypyc/test-data/irbuild-statements.test index e5e47c05877a..d16c57ed8144 100644 --- a/mypyc/test-data/irbuild-statements.test +++ b/mypyc/test-data/irbuild-statements.test @@ -521,9 +521,9 @@ L0: def from_any(a): a, r0, r1 :: object r2 :: bool - x, r3 :: object + r3 :: object r4 :: bool - y, r5 :: object + x, y, r5 :: object r6 :: bool L0: r0 = PyObject_GetIter(a) @@ -533,13 +533,13 @@ 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: + x = r1 y = r3 r5 = PyIter_Next(r0) if is_error(r5) goto L6 else goto L5 @@ -577,9 +577,9 @@ L0: def from_any(a): a, r0, r1 :: object r2 :: bool - r3, x :: int - r4 :: object - r5 :: bool + r3 :: object + r4 :: bool + r5, x :: int y, r6 :: object r7 :: bool L0: @@ -590,15 +590,15 @@ 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: - r5 = raise ValueError('not enough values to unpack') + r4 = raise ValueError('not enough values to unpack') unreachable L4: - y = r4 + r5 = unbox(int, r1) + x = r5 + y = r3 r6 = PyIter_Next(r0) if is_error(r6) goto L6 else goto L5 L5: @@ -607,6 +607,91 @@ L5: L6: 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 :: str + r9 :: int32 + r10 :: bit + r11 :: str + r12 :: int32 + r13 :: bit + r14 :: list + r15 :: ptr + r16 :: int64 + r17 :: short_int + r18 :: bit + r19 :: bool + r20, r21 :: object + r22 :: str + r23 :: int32 + r24 :: bit + r25 :: str + r26 :: int32 + r27 :: bit + r28 :: str + r29 :: int32 + r30 :: 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: + r8 = 'x' + r9 = PyObject_SetAttr(a, r8, r4) + r10 = r9 >= 0 :: signed + r11 = 'y' + r12 = PyObject_SetAttr(a, r11, r6) + r13 = r12 >= 0 :: signed + r14 = PySequence_List(r3) + r15 = get_element_ptr r14 ob_size :: PyVarObject + r16 = load_mem r15 :: int64* + keep_alive r14 + r17 = r16 << 1 + r18 = 4 <= r17 :: signed + if r18 goto L6 else goto L5 :: bool +L5: + r19 = raise ValueError('not enough values to unpack') + unreachable +L6: + r20 = CPyList_PopLast(r14) + r21 = CPyList_PopLast(r14) + r22 = 'w' + r23 = PyObject_SetAttr(a, r22, r20) + r24 = r23 >= 0 :: signed + r25 = 'u' + r26 = PyObject_SetAttr(a, r25, r21) + r27 = r26 >= 0 :: signed + r28 = 'z' + r29 = PyObject_SetAttr(a, r28, r14) + r30 = r29 >= 0 :: signed + return 1 + [case testMultiAssignmentNested] from typing import Tuple, Any, List From 32d2aad2b8547153a54d7f492b9fb24a19491fe0 Mon Sep 17 00:00:00 2001 From: chetan22 Date: Sat, 8 May 2021 12:29:44 +0530 Subject: [PATCH 2/2] changed int64 -> native_int --- mypyc/test-data/irbuild-statements.test | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mypyc/test-data/irbuild-statements.test b/mypyc/test-data/irbuild-statements.test index d16c57ed8144..66aa1b3a0416 100644 --- a/mypyc/test-data/irbuild-statements.test +++ b/mypyc/test-data/irbuild-statements.test @@ -631,7 +631,7 @@ def f(a): r13 :: bit r14 :: list r15 :: ptr - r16 :: int64 + r16 :: native_int r17 :: short_int r18 :: bit r19 :: bool @@ -670,7 +670,7 @@ L4: r13 = r12 >= 0 :: signed r14 = PySequence_List(r3) r15 = get_element_ptr r14 ob_size :: PyVarObject - r16 = load_mem r15 :: int64* + r16 = load_mem r15 :: native_int* keep_alive r14 r17 = r16 << 1 r18 = 4 <= r17 :: signed