2828 TypeAlias ,
2929 Var ,
3030)
31- from mypy .types import TupleType , get_proper_type
31+ from mypy .types import LiteralType , TupleType , get_proper_type , get_proper_types
3232from mypyc .ir .ops import (
3333 ERR_NEVER ,
3434 BasicBlock ,
3737 IntOp ,
3838 LoadAddress ,
3939 LoadErrorValue ,
40+ LoadLiteral ,
4041 LoadMem ,
4142 MethodCall ,
4243 RaiseStandardError ,
@@ -171,7 +172,7 @@ def for_loop_helper_with_index(
171172 body_insts: a function that generates the body of the loop.
172173 It needs a index as parameter.
173174 """
174- assert is_sequence_rprimitive (expr_reg .type )
175+ assert is_sequence_rprimitive (expr_reg .type ), ( expr_reg , expr_reg . type )
175176 target_type = builder .get_sequence_type (expr )
176177
177178 body_block = BasicBlock ()
@@ -218,10 +219,9 @@ def sequence_from_generator_preallocate_helper(
218219 there is no condition list in the generator and only one original sequence with
219220 one index is allowed.
220221
221- e.g. (1) tuple(f(x) for x in a_list/a_tuple/a_str/a_bytes)
222- (2) list(f(x) for x in a_list/a_tuple/a_str/a_bytes)
223- (3) [f(x) for x in a_list/a_tuple/a_str/a_bytes]
224- RTuple as an original sequence is not supported yet.
222+ e.g. (1) tuple(f(x) for x in a_list/a_tuple/a_str/a_bytes/an_rtuple)
223+ (2) list(f(x) for x in a_list/a_tuple/a_str/a_bytes/an_rtuple)
224+ (3) [f(x) for x in a_list/a_tuple/a_str/a_bytes/an_rtuple]
225225
226226 Args:
227227 empty_op_llbuilder: A function that can generate an empty sequence op when
@@ -236,23 +236,41 @@ def sequence_from_generator_preallocate_helper(
236236 implementation.
237237 """
238238 if len (gen .sequences ) == 1 and len (gen .indices ) == 1 and len (gen .condlists [0 ]) == 0 :
239- rtype = builder .node_type (gen .sequences [0 ])
240- if is_sequence_rprimitive (rtype ):
241- sequence = builder .accept (gen .sequences [0 ])
242- length = get_expr_length_value (
243- builder , gen .sequences [0 ], sequence , gen .line , use_pyssize_t = True
244- )
245- target_op = empty_op_llbuilder (length , gen .line )
246-
247- def set_item (item_index : Value ) -> None :
248- e = builder .accept (gen .left_expr )
249- builder .call_c (set_item_op , [target_op , item_index , e ], gen .line )
250-
251- for_loop_helper_with_index (
252- builder , gen .indices [0 ], gen .sequences [0 ], sequence , set_item , gen .line , length
253- )
239+ line = gen .line
240+ sequence_expr = gen .sequences [0 ]
241+ rtype = builder .node_type (sequence_expr )
242+ if not (is_sequence_rprimitive (rtype ) or isinstance (rtype , RTuple )):
243+ return None
244+ sequence = builder .accept (sequence_expr )
245+ length = get_expr_length_value (builder , sequence_expr , sequence , line , use_pyssize_t = True )
246+ if isinstance (rtype , RTuple ):
247+ # If input is RTuple, box it to tuple_rprimitive for generic iteration
248+ # TODO: this can be optimized a bit better with an unrolled ForRTuple helper
249+ proper_type = get_proper_type (builder .types [sequence_expr ])
250+ assert isinstance (proper_type , TupleType ), proper_type
251+
252+ get_item_ops = [
253+ (
254+ LoadLiteral (typ .value , object_rprimitive )
255+ if isinstance (typ , LiteralType )
256+ else TupleGet (sequence , i , line )
257+ )
258+ for i , typ in enumerate (get_proper_types (proper_type .items ))
259+ ]
260+ items = list (map (builder .add , get_item_ops ))
261+ sequence = builder .new_tuple (items , line )
262+
263+ target_op = empty_op_llbuilder (length , line )
264+
265+ def set_item (item_index : Value ) -> None :
266+ e = builder .accept (gen .left_expr )
267+ builder .call_c (set_item_op , [target_op , item_index , e ], line )
268+
269+ for_loop_helper_with_index (
270+ builder , gen .indices [0 ], sequence_expr , sequence , set_item , line , length
271+ )
254272
255- return target_op
273+ return target_op
256274 return None
257275
258276
@@ -805,7 +823,7 @@ class ForSequence(ForGenerator):
805823 def init (
806824 self , expr_reg : Value , target_type : RType , reverse : bool , length : Value | None = None
807825 ) -> None :
808- assert is_sequence_rprimitive (expr_reg .type ), expr_reg
826+ assert is_sequence_rprimitive (expr_reg .type ), ( expr_reg , expr_reg . type )
809827 builder = self .builder
810828 # Record a Value indicating the length of the sequence, if known at compile time.
811829 self .length = length
@@ -835,7 +853,6 @@ def init(
835853 def gen_condition (self ) -> None :
836854 builder = self .builder
837855 line = self .line
838- # TODO: Don't reload the length each time when iterating an immutable sequence?
839856 if self .reverse :
840857 # If we are iterating in reverse order, we obviously need
841858 # to check that the index is still positive. Somewhat less
@@ -1225,7 +1242,7 @@ def get_expr_length_value(
12251242 builder : IRBuilder , expr : Expression , expr_reg : Value , line : int , use_pyssize_t : bool
12261243) -> Value :
12271244 rtype = builder .node_type (expr )
1228- assert is_sequence_rprimitive (rtype ), rtype
1245+ assert is_sequence_rprimitive (rtype ) or isinstance ( rtype , RTuple ) , rtype
12291246 length = get_expr_length (builder , expr )
12301247 if length is None :
12311248 # We cannot compute the length at compile time, so we will fetch it.
0 commit comments