diff --git a/mypyc/ir/rtypes.py b/mypyc/ir/rtypes.py index c0871bba258c..7a82a884256d 100644 --- a/mypyc/ir/rtypes.py +++ b/mypyc/ir/rtypes.py @@ -628,7 +628,19 @@ def is_range_rprimitive(rtype: RType) -> bool: def is_sequence_rprimitive(rtype: RType) -> bool: return isinstance(rtype, RPrimitive) and ( - is_list_rprimitive(rtype) or is_tuple_rprimitive(rtype) or is_str_rprimitive(rtype) + is_list_rprimitive(rtype) + or is_tuple_rprimitive(rtype) + or is_str_rprimitive(rtype) + or is_bytes_rprimitive(rtype) + ) + + +def is_immutable_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]: + return ( + is_str_rprimitive(rtype) + or is_bytes_rprimitive(rtype) + or is_tuple_rprimitive(rtype) + or is_frozenset_rprimitive(rtype) ) diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index 608c524b5d4f..4f2f539118d7 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -91,6 +91,7 @@ RType, RUnion, bitmap_rprimitive, + bytes_rprimitive, c_pyssize_t_rprimitive, dict_rprimitive, int_rprimitive, @@ -962,8 +963,12 @@ def get_sequence_type_from_type(self, target_type: Type) -> RType: elif isinstance(target_type, Instance): if target_type.type.fullname == "builtins.str": return str_rprimitive - else: + elif target_type.type.fullname == "builtins.bytes": + return bytes_rprimitive + try: return self.type_to_rtype(target_type.args[0]) + except IndexError: + raise ValueError(f"{target_type!r} is not a valid sequence.") from None # This elif-blocks are needed for iterating over classes derived from NamedTuple. elif isinstance(target_type, TypeVarLikeType): return self.get_sequence_type_from_type(target_type.upper_bound) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index 5cf89f579ec4..762b41866a05 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -48,6 +48,7 @@ int_rprimitive, is_dict_rprimitive, is_fixed_width_rtype, + is_immutable_rprimitive, is_list_rprimitive, is_sequence_rprimitive, is_short_int_rprimitive, @@ -205,9 +206,9 @@ def sequence_from_generator_preallocate_helper( there is no condition list in the generator and only one original sequence with one index is allowed. - e.g. (1) tuple(f(x) for x in a_list/a_tuple) - (2) list(f(x) for x in a_list/a_tuple) - (3) [f(x) for x in a_list/a_tuple] + e.g. (1) tuple(f(x) for x in a_list/a_tuple/a_str/a_bytes) + (2) list(f(x) for x in a_list/a_tuple/a_str/a_bytes) + (3) [f(x) for x in a_list/a_tuple/a_str/a_bytes] RTuple as an original sequence is not supported yet. Args: @@ -224,7 +225,7 @@ def sequence_from_generator_preallocate_helper( """ if len(gen.sequences) == 1 and len(gen.indices) == 1 and len(gen.condlists[0]) == 0: rtype = builder.node_type(gen.sequences[0]) - if is_list_rprimitive(rtype) or is_tuple_rprimitive(rtype) or is_str_rprimitive(rtype): + if is_sequence_rprimitive(rtype): sequence = builder.accept(gen.sequences[0]) length = builder.builder.builtin_len(sequence, gen.line, use_pyssize_t=True) target_op = empty_op_llbuilder(length, gen.line) @@ -785,17 +786,31 @@ class ForSequence(ForGenerator): Supports iterating in both forward and reverse. """ + length_reg: Value | AssignmentTarget | None + def init(self, expr_reg: Value, target_type: RType, reverse: bool) -> None: + assert is_sequence_rprimitive(expr_reg.type), expr_reg builder = self.builder self.reverse = reverse # Define target to contain the expression, along with the index that will be used # for the for-loop. If we are inside of a generator function, spill these into the # environment class. self.expr_target = builder.maybe_spill(expr_reg) + if is_immutable_rprimitive(expr_reg.type): + # If the expression is an immutable type, we can load the length just once. + self.length_reg = builder.maybe_spill(self.load_len(self.expr_target)) + else: + # Otherwise, even if the length is known, we must recalculate the length + # at every iteration for compatibility with python semantics. + self.length_reg = None if not reverse: index_reg: Value = Integer(0, c_pyssize_t_rprimitive) else: - index_reg = builder.builder.int_sub(self.load_len(self.expr_target), 1) + if self.length_reg is not None: + len_val = builder.read(self.length_reg) + else: + len_val = self.load_len(self.expr_target) + index_reg = builder.builder.int_sub(len_val, 1) self.index_target = builder.maybe_spill_assignable(index_reg) self.target_type = target_type @@ -814,9 +829,13 @@ def gen_condition(self) -> None: second_check = BasicBlock() builder.add_bool_branch(comparison, second_check, self.loop_exit) builder.activate_block(second_check) - # For compatibility with python semantics we recalculate the length - # at every iteration. - len_reg = self.load_len(self.expr_target) + if self.length_reg is None: + # For compatibility with python semantics we recalculate the length + # at every iteration. + len_reg = self.load_len(self.expr_target) + else: + # (unless input is immutable type). + len_reg = builder.read(self.length_reg, line) comparison = builder.binary_op(builder.read(self.index_target, line), len_reg, "<", line) builder.add_bool_branch(comparison, self.body_block, self.loop_exit) diff --git a/mypyc/irbuild/specialize.py b/mypyc/irbuild/specialize.py index 3015640fb3fd..748cda1256a7 100644 --- a/mypyc/irbuild/specialize.py +++ b/mypyc/irbuild/specialize.py @@ -288,7 +288,7 @@ def translate_tuple_from_generator_call( """Special case for simplest tuple creation from a generator. For example: - tuple(f(x) for x in some_list/some_tuple/some_str) + tuple(f(x) for x in some_list/some_tuple/some_str/some_bytes) 'translate_safe_generator_call()' would take care of other cases if this fails. """ diff --git a/mypyc/test-data/fixtures/ir.py b/mypyc/test-data/fixtures/ir.py index 76afc1ea58cc..661ae50fd5f3 100644 --- a/mypyc/test-data/fixtures/ir.py +++ b/mypyc/test-data/fixtures/ir.py @@ -172,6 +172,7 @@ def __getitem__(self, i: int) -> int: ... def __getitem__(self, i: slice) -> bytes: ... def join(self, x: Iterable[object]) -> bytes: ... def decode(self, x: str=..., y: str=...) -> str: ... + def __iter__(self) -> Iterator[int]: ... class bytearray: @overload diff --git a/mypyc/test-data/irbuild-generics.test b/mypyc/test-data/irbuild-generics.test index d39d47e397a1..038a92e0cd73 100644 --- a/mypyc/test-data/irbuild-generics.test +++ b/mypyc/test-data/irbuild-generics.test @@ -721,18 +721,18 @@ L0: r0 = __mypyc_self__.__mypyc_env__ r1 = var_object_size args r2 = PyList_New(r1) - r3 = 0 + r3 = var_object_size args + r4 = 0 L1: - r4 = var_object_size args - r5 = r3 < r4 :: signed + r5 = r4 < r3 :: signed if r5 goto L2 else goto L4 :: bool L2: - r6 = CPySequenceTuple_GetItemUnsafe(args, r3) + r6 = CPySequenceTuple_GetItemUnsafe(args, r4) x = r6 - CPyList_SetItemUnsafe(r2, r3, x) + CPyList_SetItemUnsafe(r2, r4, x) L3: - r7 = r3 + 1 - r3 = r7 + r7 = r4 + 1 + r4 = r7 goto L1 L4: can_listcomp = r2 diff --git a/mypyc/test-data/irbuild-lists.test b/mypyc/test-data/irbuild-lists.test index 06120e077af9..d83fb88390db 100644 --- a/mypyc/test-data/irbuild-lists.test +++ b/mypyc/test-data/irbuild-lists.test @@ -572,3 +572,357 @@ def sort_iterable(a): L0: r0 = CPySequence_Sort(a) return 1 + +[case testListBuiltFromStr] +def f2(val: str) -> str: + return val + "f2" + +def test() -> None: + source = "abc" + a = [f2(x) for x in source] +[out] +def f2(val): + val, r0, r1 :: str +L0: + r0 = 'f2' + r1 = PyUnicode_Concat(val, r0) + return r1 +def test(): + r0, source :: str + r1 :: native_int + r2 :: bit + r3 :: list + r4 :: native_int + r5 :: bit + r6 :: native_int + r7 :: bit + r8, x, r9 :: str + r10 :: native_int + a :: list +L0: + r0 = 'abc' + source = r0 + r1 = CPyStr_Size_size_t(source) + r2 = r1 >= 0 :: signed + r3 = PyList_New(r1) + r4 = CPyStr_Size_size_t(source) + r5 = r4 >= 0 :: signed + r6 = 0 +L1: + r7 = r6 < r4 :: signed + if r7 goto L2 else goto L4 :: bool +L2: + r8 = CPyStr_GetItemUnsafe(source, r6) + x = r8 + r9 = f2(x) + CPyList_SetItemUnsafe(r3, r6, r9) +L3: + r10 = r6 + 1 + r6 = r10 + goto L1 +L4: + a = r3 + return 1 + +[case testListBuiltFromStrExpr] +def f2(val: str) -> str: + return val + "f2" + +def test() -> None: + a = [f2(x) for x in "abc"] +[out] +def f2(val): + val, r0, r1 :: str +L0: + r0 = 'f2' + r1 = PyUnicode_Concat(val, r0) + return r1 +def test(): + r0 :: str + r1 :: native_int + r2 :: bit + r3 :: list + r4 :: native_int + r5 :: bit + r6 :: native_int + r7 :: bit + r8, x, r9 :: str + r10 :: native_int + a :: list +L0: + r0 = 'abc' + r1 = CPyStr_Size_size_t(r0) + r2 = r1 >= 0 :: signed + r3 = PyList_New(r1) + r4 = CPyStr_Size_size_t(r0) + r5 = r4 >= 0 :: signed + r6 = 0 +L1: + r7 = r6 < r4 :: signed + if r7 goto L2 else goto L4 :: bool +L2: + r8 = CPyStr_GetItemUnsafe(r0, r6) + x = r8 + r9 = f2(x) + CPyList_SetItemUnsafe(r3, r6, r9) +L3: + r10 = r6 + 1 + r6 = r10 + goto L1 +L4: + a = r3 + return 1 + +[case testListBuiltFromFinalStr] +from typing import Final + +source: Final = "abc" + +def f2(val: str) -> str: + return val + "f2" + +def test() -> None: + a = [f2(x) for x in source] +[out] +def f2(val): + val, r0, r1 :: str +L0: + r0 = 'f2' + r1 = PyUnicode_Concat(val, r0) + return r1 +def test(): + r0 :: str + r1 :: native_int + r2 :: bit + r3 :: list + r4 :: native_int + r5 :: bit + r6 :: native_int + r7 :: bit + r8, x, r9 :: str + r10 :: native_int + a :: list +L0: + r0 = 'abc' + r1 = CPyStr_Size_size_t(r0) + r2 = r1 >= 0 :: signed + r3 = PyList_New(r1) + r4 = CPyStr_Size_size_t(r0) + r5 = r4 >= 0 :: signed + r6 = 0 +L1: + r7 = r6 < r4 :: signed + if r7 goto L2 else goto L4 :: bool +L2: + r8 = CPyStr_GetItemUnsafe(r0, r6) + x = r8 + r9 = f2(x) + CPyList_SetItemUnsafe(r3, r6, r9) +L3: + r10 = r6 + 1 + r6 = r10 + goto L1 +L4: + a = r3 + return 1 + +[case testListBuiltFromBytes_64bit] +def f2(val: int) -> int: + return val + 2 + +def test() -> None: + source = b"abc" + a = [f2(x) for x in source] + +[out] +def f2(val): + val, r0 :: int +L0: + r0 = CPyTagged_Add(val, 4) + return r0 +def test(): + r0, source :: bytes + r1 :: native_int + r2 :: list + r3, r4 :: native_int + r5, r6, r7 :: bit + r8, r9, r10, r11 :: int + r12 :: object + r13, x, r14 :: int + r15 :: object + r16 :: native_int + a :: list +L0: + r0 = b'abc' + source = r0 + r1 = var_object_size source + r2 = PyList_New(r1) + r3 = var_object_size source + r4 = 0 +L1: + r5 = r4 < r3 :: signed + if r5 goto L2 else goto L8 :: bool +L2: + r6 = r4 <= 4611686018427387903 :: signed + if r6 goto L3 else goto L4 :: bool +L3: + r7 = r4 >= -4611686018427387904 :: signed + if r7 goto L5 else goto L4 :: bool +L4: + r8 = CPyTagged_FromInt64(r4) + r9 = r8 + goto L6 +L5: + r10 = r4 << 1 + r9 = r10 +L6: + r11 = CPyBytes_GetItem(source, r9) + r12 = box(int, r11) + r13 = unbox(int, r12) + x = r13 + r14 = f2(x) + r15 = box(int, r14) + CPyList_SetItemUnsafe(r2, r4, r15) +L7: + r16 = r4 + 1 + r4 = r16 + goto L1 +L8: + a = r2 + return 1 + +[case testListBuiltFromBytesExpr_64bit] +def f2(val: int) -> int: + return val + 2 + +def test() -> None: + a = [f2(x) for x in b"abc"] + +[out] +def f2(val): + val, r0 :: int +L0: + r0 = CPyTagged_Add(val, 4) + return r0 +def test(): + r0 :: bytes + r1 :: native_int + r2 :: list + r3, r4 :: native_int + r5, r6, r7 :: bit + r8, r9, r10, r11 :: int + r12 :: object + r13, x, r14 :: int + r15 :: object + r16 :: native_int + a :: list +L0: + r0 = b'abc' + r1 = var_object_size r0 + r2 = PyList_New(r1) + r3 = var_object_size r0 + r4 = 0 +L1: + r5 = r4 < r3 :: signed + if r5 goto L2 else goto L8 :: bool +L2: + r6 = r4 <= 4611686018427387903 :: signed + if r6 goto L3 else goto L4 :: bool +L3: + r7 = r4 >= -4611686018427387904 :: signed + if r7 goto L5 else goto L4 :: bool +L4: + r8 = CPyTagged_FromInt64(r4) + r9 = r8 + goto L6 +L5: + r10 = r4 << 1 + r9 = r10 +L6: + r11 = CPyBytes_GetItem(r0, r9) + r12 = box(int, r11) + r13 = unbox(int, r12) + x = r13 + r14 = f2(x) + r15 = box(int, r14) + CPyList_SetItemUnsafe(r2, r4, r15) +L7: + r16 = r4 + 1 + r4 = r16 + goto L1 +L8: + a = r2 + return 1 + +[case testListBuiltFromFinalBytes_64bit] +from typing import Final + +source: Final = b"abc" + +def f2(val: int) -> int: + return val + 2 + +def test() -> None: + a = [f2(x) for x in source] + +[out] +def f2(val): + val, r0 :: int +L0: + r0 = CPyTagged_Add(val, 4) + return r0 +def test(): + r0 :: bytes + r1 :: bool + r2 :: native_int + r3 :: list + r4, r5 :: native_int + r6, r7, r8 :: bit + r9, r10, r11, r12 :: int + r13 :: object + r14, x, r15 :: int + r16 :: object + r17 :: native_int + a :: list +L0: + r0 = __main__.source :: static + if is_error(r0) goto L1 else goto L2 +L1: + r1 = raise NameError('value for final name "source" was not set') + unreachable +L2: + r2 = var_object_size r0 + r3 = PyList_New(r2) + r4 = var_object_size r0 + r5 = 0 +L3: + r6 = r5 < r4 :: signed + if r6 goto L4 else goto L10 :: bool +L4: + r7 = r5 <= 4611686018427387903 :: signed + if r7 goto L5 else goto L6 :: bool +L5: + r8 = r5 >= -4611686018427387904 :: signed + if r8 goto L7 else goto L6 :: bool +L6: + r9 = CPyTagged_FromInt64(r5) + r10 = r9 + goto L8 +L7: + r11 = r5 << 1 + r10 = r11 +L8: + r12 = CPyBytes_GetItem(r0, r10) + r13 = box(int, r12) + r14 = unbox(int, r13) + x = r14 + r15 = f2(x) + r16 = box(int, r15) + CPyList_SetItemUnsafe(r3, r5, r16) +L9: + r17 = r5 + 1 + r5 = r17 + goto L3 +L10: + a = r3 + return 1 diff --git a/mypyc/test-data/irbuild-tuple.test b/mypyc/test-data/irbuild-tuple.test index 5c5ec27b1882..0342ec304c25 100644 --- a/mypyc/test-data/irbuild-tuple.test +++ b/mypyc/test-data/irbuild-tuple.test @@ -133,18 +133,18 @@ def f(xs): r4, x :: str r5 :: native_int L0: - r0 = 0 + r0 = var_object_size xs + r1 = 0 L1: - r1 = var_object_size xs - r2 = r0 < r1 :: signed + r2 = r1 < r0 :: signed if r2 goto L2 else goto L4 :: bool L2: - r3 = CPySequenceTuple_GetItemUnsafe(xs, r0) + r3 = CPySequenceTuple_GetItemUnsafe(xs, r1) r4 = cast(str, r3) x = r4 L3: - r5 = r0 + 1 - r0 = r5 + r5 = r1 + 1 + r1 = r5 goto L1 L4: return 1 @@ -291,8 +291,10 @@ def test(): r1 :: native_int r2 :: bit r3 :: tuple - r4, r5 :: native_int - r6, r7 :: bit + r4 :: native_int + r5 :: bit + r6 :: native_int + r7 :: bit r8, x, r9 :: str r10 :: native_int a :: tuple @@ -302,25 +304,437 @@ L0: r1 = CPyStr_Size_size_t(source) r2 = r1 >= 0 :: signed r3 = PyTuple_New(r1) - r4 = 0 + r4 = CPyStr_Size_size_t(source) + r5 = r4 >= 0 :: signed + r6 = 0 L1: - r5 = CPyStr_Size_size_t(source) - r6 = r5 >= 0 :: signed - r7 = r4 < r5 :: signed + r7 = r6 < r4 :: signed if r7 goto L2 else goto L4 :: bool L2: - r8 = CPyStr_GetItemUnsafe(source, r4) + r8 = CPyStr_GetItemUnsafe(source, r6) x = r8 r9 = f2(x) - CPySequenceTuple_SetItemUnsafe(r3, r4, r9) + CPySequenceTuple_SetItemUnsafe(r3, r6, r9) L3: - r10 = r4 + 1 - r4 = r10 + r10 = r6 + 1 + r6 = r10 goto L1 L4: a = r3 return 1 +[case testTupleBuiltFromStrExpr] +def f2(val: str) -> str: + return val + "f2" + +def test() -> None: + a = tuple(f2(x) for x in "abc") + +[out] +def f2(val): + val, r0, r1 :: str +L0: + r0 = 'f2' + r1 = PyUnicode_Concat(val, r0) + return r1 +def test(): + r0 :: str + r1 :: native_int + r2 :: bit + r3 :: tuple + r4 :: native_int + r5 :: bit + r6 :: native_int + r7 :: bit + r8, x, r9 :: str + r10 :: native_int + a :: tuple +L0: + r0 = 'abc' + r1 = CPyStr_Size_size_t(r0) + r2 = r1 >= 0 :: signed + r3 = PyTuple_New(r1) + r4 = CPyStr_Size_size_t(r0) + r5 = r4 >= 0 :: signed + r6 = 0 +L1: + r7 = r6 < r4 :: signed + if r7 goto L2 else goto L4 :: bool +L2: + r8 = CPyStr_GetItemUnsafe(r0, r6) + x = r8 + r9 = f2(x) + CPySequenceTuple_SetItemUnsafe(r3, r6, r9) +L3: + r10 = r6 + 1 + r6 = r10 + goto L1 +L4: + a = r3 + return 1 + +[case testTupleBuiltFromFinalStr] +from typing import Final + +source: Final = "abc" + +def f2(val: str) -> str: + return val + "f2" + +def test() -> None: + a = tuple(f2(x) for x in source) +[out] +def f2(val): + val, r0, r1 :: str +L0: + r0 = 'f2' + r1 = PyUnicode_Concat(val, r0) + return r1 +def test(): + r0 :: str + r1 :: native_int + r2 :: bit + r3 :: tuple + r4 :: native_int + r5 :: bit + r6 :: native_int + r7 :: bit + r8, x, r9 :: str + r10 :: native_int + a :: tuple +L0: + r0 = 'abc' + r1 = CPyStr_Size_size_t(r0) + r2 = r1 >= 0 :: signed + r3 = PyTuple_New(r1) + r4 = CPyStr_Size_size_t(r0) + r5 = r4 >= 0 :: signed + r6 = 0 +L1: + r7 = r6 < r4 :: signed + if r7 goto L2 else goto L4 :: bool +L2: + r8 = CPyStr_GetItemUnsafe(r0, r6) + x = r8 + r9 = f2(x) + CPySequenceTuple_SetItemUnsafe(r3, r6, r9) +L3: + r10 = r6 + 1 + r6 = r10 + goto L1 +L4: + a = r3 + return 1 + +[case testTupleBuiltFromBytes_64bit] +def f2(val: int) -> int: + return val + 2 + +def test() -> None: + source = b"abc" + a = tuple(f2(x) for x in source) + +[out] +def f2(val): + val, r0 :: int +L0: + r0 = CPyTagged_Add(val, 4) + return r0 +def test(): + r0, source :: bytes + r1 :: native_int + r2 :: tuple + r3, r4 :: native_int + r5, r6, r7 :: bit + r8, r9, r10, r11 :: int + r12 :: object + r13, x, r14 :: int + r15 :: object + r16 :: native_int + a :: tuple +L0: + r0 = b'abc' + source = r0 + r1 = var_object_size source + r2 = PyTuple_New(r1) + r3 = var_object_size source + r4 = 0 +L1: + r5 = r4 < r3 :: signed + if r5 goto L2 else goto L8 :: bool +L2: + r6 = r4 <= 4611686018427387903 :: signed + if r6 goto L3 else goto L4 :: bool +L3: + r7 = r4 >= -4611686018427387904 :: signed + if r7 goto L5 else goto L4 :: bool +L4: + r8 = CPyTagged_FromInt64(r4) + r9 = r8 + goto L6 +L5: + r10 = r4 << 1 + r9 = r10 +L6: + r11 = CPyBytes_GetItem(source, r9) + r12 = box(int, r11) + r13 = unbox(int, r12) + x = r13 + r14 = f2(x) + r15 = box(int, r14) + CPySequenceTuple_SetItemUnsafe(r2, r4, r15) +L7: + r16 = r4 + 1 + r4 = r16 + goto L1 +L8: + a = r2 + return 1 + +[case testTupleBuiltFromBytesExpr_64bit] +def f2(val: int) -> int: + return val + 2 + +def test() -> None: + a = tuple(f2(x) for x in b"abc") + +[out] +def f2(val): + val, r0 :: int +L0: + r0 = CPyTagged_Add(val, 4) + return r0 +def test(): + r0 :: bytes + r1 :: native_int + r2 :: tuple + r3, r4 :: native_int + r5, r6, r7 :: bit + r8, r9, r10, r11 :: int + r12 :: object + r13, x, r14 :: int + r15 :: object + r16 :: native_int + a :: tuple +L0: + r0 = b'abc' + r1 = var_object_size r0 + r2 = PyTuple_New(r1) + r3 = var_object_size r0 + r4 = 0 +L1: + r5 = r4 < r3 :: signed + if r5 goto L2 else goto L8 :: bool +L2: + r6 = r4 <= 4611686018427387903 :: signed + if r6 goto L3 else goto L4 :: bool +L3: + r7 = r4 >= -4611686018427387904 :: signed + if r7 goto L5 else goto L4 :: bool +L4: + r8 = CPyTagged_FromInt64(r4) + r9 = r8 + goto L6 +L5: + r10 = r4 << 1 + r9 = r10 +L6: + r11 = CPyBytes_GetItem(r0, r9) + r12 = box(int, r11) + r13 = unbox(int, r12) + x = r13 + r14 = f2(x) + r15 = box(int, r14) + CPySequenceTuple_SetItemUnsafe(r2, r4, r15) +L7: + r16 = r4 + 1 + r4 = r16 + goto L1 +L8: + a = r2 + return 1 + +[case testTupleBuiltFromFinalBytes_64bit] +from typing import Final + +source: Final = b"abc" + +def f2(val: int) -> int: + return val + 2 + +def test() -> None: + a = tuple(f2(x) for x in source) + +[out] +def f2(val): + val, r0 :: int +L0: + r0 = CPyTagged_Add(val, 4) + return r0 +def test(): + r0 :: bytes + r1 :: bool + r2 :: native_int + r3 :: tuple + r4, r5 :: native_int + r6, r7, r8 :: bit + r9, r10, r11, r12 :: int + r13 :: object + r14, x, r15 :: int + r16 :: object + r17 :: native_int + a :: tuple +L0: + r0 = __main__.source :: static + if is_error(r0) goto L1 else goto L2 +L1: + r1 = raise NameError('value for final name "source" was not set') + unreachable +L2: + r2 = var_object_size r0 + r3 = PyTuple_New(r2) + r4 = var_object_size r0 + r5 = 0 +L3: + r6 = r5 < r4 :: signed + if r6 goto L4 else goto L10 :: bool +L4: + r7 = r5 <= 4611686018427387903 :: signed + if r7 goto L5 else goto L6 :: bool +L5: + r8 = r5 >= -4611686018427387904 :: signed + if r8 goto L7 else goto L6 :: bool +L6: + r9 = CPyTagged_FromInt64(r5) + r10 = r9 + goto L8 +L7: + r11 = r5 << 1 + r10 = r11 +L8: + r12 = CPyBytes_GetItem(r0, r10) + r13 = box(int, r12) + r14 = unbox(int, r13) + x = r14 + r15 = f2(x) + r16 = box(int, r15) + CPySequenceTuple_SetItemUnsafe(r3, r5, r16) +L9: + r17 = r5 + 1 + r5 = r17 + goto L3 +L10: + a = r3 + return 1 + +[case testTupleBuiltFromFixedLengthTuple] +def f(val: int) -> bool: + return val % 2 == 0 + +def test() -> None: + source = (1, 2, 3) + a = tuple(f(x) for x in source) +[out] +def f(val): + val, r0 :: int + r1 :: bit +L0: + r0 = CPyTagged_Remainder(val, 4) + r1 = int_eq r0, 0 + return r1 +def test(): + r0, source :: tuple[int, int, int] + r1 :: list + r2, r3, r4 :: object + r5, x :: int + r6 :: bool + r7 :: object + r8 :: i32 + r9, r10 :: bit + r11, a :: tuple +L0: + r0 = (2, 4, 6) + source = r0 + r1 = PyList_New(0) + r2 = box(tuple[int, int, int], source) + r3 = PyObject_GetIter(r2) +L1: + r4 = PyIter_Next(r3) + if is_error(r4) goto L4 else goto L2 +L2: + r5 = unbox(int, r4) + x = r5 + r6 = f(x) + r7 = box(bool, r6) + r8 = PyList_Append(r1, r7) + r9 = r8 >= 0 :: signed +L3: + goto L1 +L4: + r10 = CPy_NoErrOccurred() +L5: + r11 = PyList_AsTuple(r1) + a = r11 + return 1 + +[case testTupleBuiltFromFinalFixedLengthTuple] +from typing import Final + +source: Final = (1, 2, 3) + +def f(val: int) -> bool: + return val % 2 == 0 + +def test() -> None: + a = tuple(f(x) for x in source) +[out] +def f(val): + val, r0 :: int + r1 :: bit +L0: + r0 = CPyTagged_Remainder(val, 4) + r1 = int_eq r0, 0 + return r1 +def test(): + r0 :: list + r1 :: tuple[int, int, int] + r2 :: bool + r3, r4, r5 :: object + r6, x :: int + r7 :: bool + r8 :: object + r9 :: i32 + r10, r11 :: bit + r12, a :: tuple +L0: + r0 = PyList_New(0) + r1 = __main__.source :: static + if is_error(r1) goto L1 else goto L2 +L1: + r2 = raise NameError('value for final name "source" was not set') + unreachable +L2: + r3 = box(tuple[int, int, int], r1) + r4 = PyObject_GetIter(r3) +L3: + r5 = PyIter_Next(r4) + if is_error(r5) goto L6 else goto L4 +L4: + r6 = unbox(int, r5) + x = r6 + r7 = f(x) + r8 = box(bool, r7) + r9 = PyList_Append(r0, r8) + r10 = r9 >= 0 :: signed +L5: + goto L3 +L6: + r11 = CPy_NoErrOccurred() +L7: + r12 = PyList_AsTuple(r0) + a = r12 + return 1 + [case testTupleBuiltFromVariableLengthTuple] from typing import Tuple @@ -349,21 +763,21 @@ def test(source): L0: r0 = var_object_size source r1 = PyTuple_New(r0) - r2 = 0 + r2 = var_object_size source + r3 = 0 L1: - r3 = var_object_size source - r4 = r2 < r3 :: signed + r4 = r3 < r2 :: signed if r4 goto L2 else goto L4 :: bool L2: - r5 = CPySequenceTuple_GetItemUnsafe(source, r2) + r5 = CPySequenceTuple_GetItemUnsafe(source, r3) r6 = unbox(bool, r5) x = r6 r7 = f(x) r8 = box(bool, r7) - CPySequenceTuple_SetItemUnsafe(r1, r2, r8) + CPySequenceTuple_SetItemUnsafe(r1, r3, r8) L3: - r9 = r2 + 1 - r2 = r9 + r9 = r3 + 1 + r3 = r9 goto L1 L4: a = r1