From 5f87a6986c5811d20e4251e5a2b6e8b4e7280f51 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Sat, 6 Dec 2025 16:09:19 -0400 Subject: [PATCH 01/10] fix: mypyc#20341 --- mypyc/irbuild/builder.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index 06634c4d98fd..2a4eb2fc70d7 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -990,8 +990,10 @@ def get_sequence_type_from_type(self, target_type: Type) -> RType: elif isinstance(target_type, TypeVarLikeType): return self.get_sequence_type_from_type(target_type.upper_bound) elif isinstance(target_type, TupleType): + items = target_type.items + assert items, f"This function does not support empty tuples" # Tuple might have elements of different types. - rtypes = {self.mapper.type_to_rtype(item) for item in target_type.items} + rtypes = set(map(self.mapper.type_to_rtype, items)) if len(rtypes) == 1: return rtypes.pop() else: From 379048a0e04bb777933710b4dc4aae231fe6f6a8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 6 Dec 2025 20:10:57 +0000 Subject: [PATCH 02/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypyc/irbuild/builder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index 2a4eb2fc70d7..88161f45bf03 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -991,7 +991,7 @@ def get_sequence_type_from_type(self, target_type: Type) -> RType: return self.get_sequence_type_from_type(target_type.upper_bound) elif isinstance(target_type, TupleType): items = target_type.items - assert items, f"This function does not support empty tuples" + assert items, "This function does not support empty tuples" # Tuple might have elements of different types. rtypes = set(map(self.mapper.type_to_rtype, items)) if len(rtypes) == 1: From 6ebce908d1adac2038d581d02d256c7b9aca8399 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Sat, 6 Dec 2025 16:25:51 -0400 Subject: [PATCH 03/10] Update for_helpers.py --- mypyc/irbuild/for_helpers.py | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index 8c34a14745f4..55bc2440cd55 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -242,24 +242,40 @@ def sequence_from_generator_preallocate_helper( rtype = builder.node_type(sequence_expr) if not (is_sequence_rprimitive(rtype) or isinstance(rtype, RTuple)): return None - sequence = builder.accept(sequence_expr) - length = get_expr_length_value(builder, sequence_expr, sequence, line, use_pyssize_t=True) + if isinstance(rtype, RTuple): # If input is RTuple, box it to tuple_rprimitive for generic iteration # TODO: this can be optimized a bit better with an unrolled ForRTuple helper proper_type = get_proper_type(builder.types[sequence_expr]) assert isinstance(proper_type, TupleType), proper_type + + # the for_loop_helper_with_index crashes for empty tuples, bail out + if not proper_type.items: + return None + + proper_types = get_proper_types(proper_type.items) + + if all(isinstance(typ, LiteralType) for typ in proper_types): + get_item_ops = [LoadLiteral(typ.value, object_rprimitive) for typ in proper_types] - get_item_ops = [ - ( - LoadLiteral(typ.value, object_rprimitive) - if isinstance(typ, LiteralType) - else TupleGet(sequence, i, line) - ) - for i, typ in enumerate(get_proper_types(proper_type.items)) - ] + else: + sequence = builder.accept(sequence_expr) + get_item_ops = [ + ( + LoadLiteral(typ.value, object_rprimitive) + if isinstance(typ, LiteralType) + else TupleGet(sequence, i, line) + ) + for i, typ in enumerate(proper_types) + ] + items = list(map(builder.add, get_item_ops)) sequence = builder.new_tuple(items, line) + + else: + sequence = builder.accept(sequence_expr) + + length = get_expr_length_value(builder, sequence_expr, sequence, line, use_pyssize_t=True) target_op = empty_op_llbuilder(length, line) From a0470362bcdd04f4127336363e4d3f095f472657 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 6 Dec 2025 20:27:19 +0000 Subject: [PATCH 04/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypyc/irbuild/for_helpers.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index 55bc2440cd55..798ddce1fc17 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -242,13 +242,13 @@ def sequence_from_generator_preallocate_helper( rtype = builder.node_type(sequence_expr) if not (is_sequence_rprimitive(rtype) or isinstance(rtype, RTuple)): return None - + if isinstance(rtype, RTuple): # If input is RTuple, box it to tuple_rprimitive for generic iteration # TODO: this can be optimized a bit better with an unrolled ForRTuple helper proper_type = get_proper_type(builder.types[sequence_expr]) assert isinstance(proper_type, TupleType), proper_type - + # the for_loop_helper_with_index crashes for empty tuples, bail out if not proper_type.items: return None @@ -268,13 +268,13 @@ def sequence_from_generator_preallocate_helper( ) for i, typ in enumerate(proper_types) ] - + items = list(map(builder.add, get_item_ops)) sequence = builder.new_tuple(items, line) - + else: sequence = builder.accept(sequence_expr) - + length = get_expr_length_value(builder, sequence_expr, sequence, line, use_pyssize_t=True) target_op = empty_op_llbuilder(length, line) From 8d45132f3f34b83892f70dbbc5f52ed7d7ce0809 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Sat, 6 Dec 2025 16:32:15 -0400 Subject: [PATCH 05/10] Update for_helpers.py --- mypyc/irbuild/for_helpers.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index 798ddce1fc17..add6426fd926 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -8,7 +8,7 @@ from __future__ import annotations from collections.abc import Callable -from typing import ClassVar +from typing import ClassVar, cast from mypy.nodes import ( ARG_POS, @@ -255,8 +255,9 @@ def sequence_from_generator_preallocate_helper( proper_types = get_proper_types(proper_type.items) + get_item_ops: list[LoadLiteral | TupleGet] if all(isinstance(typ, LiteralType) for typ in proper_types): - get_item_ops = [LoadLiteral(typ.value, object_rprimitive) for typ in proper_types] + get_item_ops = [LoadLiteral(cast(LiteralType, typ.value), object_rprimitive) for typ in proper_types] else: sequence = builder.accept(sequence_expr) From b5dcffc1955f6b23031a87eeb86eed8c23d6758d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 6 Dec 2025 20:33:44 +0000 Subject: [PATCH 06/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypyc/irbuild/for_helpers.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index add6426fd926..d591083e0b15 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -257,7 +257,10 @@ def sequence_from_generator_preallocate_helper( get_item_ops: list[LoadLiteral | TupleGet] if all(isinstance(typ, LiteralType) for typ in proper_types): - get_item_ops = [LoadLiteral(cast(LiteralType, typ.value), object_rprimitive) for typ in proper_types] + get_item_ops = [ + LoadLiteral(cast(LiteralType, typ.value), object_rprimitive) + for typ in proper_types + ] else: sequence = builder.accept(sequence_expr) From 1455270939b795e78e682c7baaf51538b25b058c Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Sat, 6 Dec 2025 16:34:36 -0400 Subject: [PATCH 07/10] Update for_helpers.py --- mypyc/irbuild/for_helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index d591083e0b15..1e845aa061a2 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -258,7 +258,7 @@ def sequence_from_generator_preallocate_helper( get_item_ops: list[LoadLiteral | TupleGet] if all(isinstance(typ, LiteralType) for typ in proper_types): get_item_ops = [ - LoadLiteral(cast(LiteralType, typ.value), object_rprimitive) + LoadLiteral(cast(LiteralType, typ).value, object_rprimitive) for typ in proper_types ] From 1fb67dbcd9264042d61cfe8f4664c5ad6ee98b99 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Sun, 7 Dec 2025 21:08:24 -0400 Subject: [PATCH 08/10] Update run-loops.test --- mypyc/test-data/run-loops.test | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mypyc/test-data/run-loops.test b/mypyc/test-data/run-loops.test index 3cbb07297e6e..7ffeddccbc03 100644 --- a/mypyc/test-data/run-loops.test +++ b/mypyc/test-data/run-loops.test @@ -21,6 +21,9 @@ def list_iter(l: List[int]) -> None: def tuple_iter(l: Tuple[int, ...]) -> None: for i in l: print(i) +def empty_tuple_iter(l: Tuple[()]) -> None: + for i in l: + print(i) def str_iter(l: str) -> None: for i in l: print(i) @@ -39,7 +42,7 @@ def count_down_short() -> None: [file driver.py] from native import ( count, list_iter, list_rev_iter, list_rev_iter_lol, count_between, count_down, count_double, - count_down_short, tuple_iter, str_iter, + count_down_short, tuple_iter, empty_tuple_iter, str_iter, ) count(5) list_iter(list(reversed(range(5)))) @@ -52,6 +55,7 @@ count_down_short() print('==') list_rev_iter_lol(list(reversed(range(5)))) tuple_iter((1, 2, 3)) +empty_tuple_iter(()) str_iter("abc") [out] 0 From c7c72012c6e5ad554fc609f81258378399bca6d0 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Sun, 7 Dec 2025 21:39:24 -0400 Subject: [PATCH 09/10] Update run-loops.test --- mypyc/test-data/run-loops.test | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mypyc/test-data/run-loops.test b/mypyc/test-data/run-loops.test index 7ffeddccbc03..106c2271d326 100644 --- a/mypyc/test-data/run-loops.test +++ b/mypyc/test-data/run-loops.test @@ -1,7 +1,7 @@ # Test cases for "range" objects, "for" and "while" loops (compile and run) [case testFor] -from typing import List, Tuple +from typing import Any, List, Tuple def count(n: int) -> None: for i in range(n): print(i) @@ -22,6 +22,7 @@ def tuple_iter(l: Tuple[int, ...]) -> None: for i in l: print(i) def empty_tuple_iter(l: Tuple[()]) -> None: + i: Any for i in l: print(i) def str_iter(l: str) -> None: From c2e98c5cb296ce0d01b7d5d70efa1b9ec9a223eb Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Mon, 8 Dec 2025 09:18:04 -0400 Subject: [PATCH 10/10] Update run-generators.test --- mypyc/test-data/run-generators.test | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/mypyc/test-data/run-generators.test b/mypyc/test-data/run-generators.test index c8e83173474d..cf1dac7c5733 100644 --- a/mypyc/test-data/run-generators.test +++ b/mypyc/test-data/run-generators.test @@ -936,3 +936,11 @@ def test_generator_override() -> None: assert base1_foo(Base1()) == [1] assert base1_foo(Derived1()) == [2, 3] assert derived1_foo(Derived1()) == [2, 3] + +[case testGeneratorEmptyTuple] +from collections.abc import Generator +from typing import Optional, Union + +def test_compiledGeneratorEmptyTuple() -> None: + jobs: Generator[Optional[str], None, None] = (_ for _ in ()) + assert list(jobs) == []