28
28
TypeAlias ,
29
29
Var ,
30
30
)
31
+ from mypy .types import LiteralType , TupleType , get_proper_type , get_proper_types
31
32
from mypyc .ir .ops import (
32
33
ERR_NEVER ,
33
34
BasicBlock ,
36
37
IntOp ,
37
38
LoadAddress ,
38
39
LoadErrorValue ,
40
+ LoadLiteral ,
39
41
LoadMem ,
40
42
MethodCall ,
41
43
RaiseStandardError ,
@@ -170,7 +172,7 @@ def for_loop_helper_with_index(
170
172
body_insts: a function that generates the body of the loop.
171
173
It needs a index as parameter.
172
174
"""
173
- assert is_sequence_rprimitive (expr_reg .type )
175
+ assert is_sequence_rprimitive (expr_reg .type ), ( expr_reg , expr_reg . type )
174
176
target_type = builder .get_sequence_type (expr )
175
177
176
178
body_block = BasicBlock ()
@@ -217,10 +219,9 @@ def sequence_from_generator_preallocate_helper(
217
219
there is no condition list in the generator and only one original sequence with
218
220
one index is allowed.
219
221
220
- e.g. (1) tuple(f(x) for x in a_list/a_tuple/a_str/a_bytes)
221
- (2) list(f(x) for x in a_list/a_tuple/a_str/a_bytes)
222
- (3) [f(x) for x in a_list/a_tuple/a_str/a_bytes]
223
- 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]
224
225
225
226
Args:
226
227
empty_op_llbuilder: A function that can generate an empty sequence op when
@@ -235,23 +236,41 @@ def sequence_from_generator_preallocate_helper(
235
236
implementation.
236
237
"""
237
238
if len (gen .sequences ) == 1 and len (gen .indices ) == 1 and len (gen .condlists [0 ]) == 0 :
238
- rtype = builder .node_type (gen .sequences [0 ])
239
- if is_sequence_rprimitive (rtype ):
240
- sequence = builder .accept (gen .sequences [0 ])
241
- length = get_expr_length_value (
242
- builder , gen .sequences [0 ], sequence , gen .line , use_pyssize_t = True
243
- )
244
- target_op = empty_op_llbuilder (length , gen .line )
245
-
246
- def set_item (item_index : Value ) -> None :
247
- e = builder .accept (gen .left_expr )
248
- builder .call_c (set_item_op , [target_op , item_index , e ], gen .line )
249
-
250
- for_loop_helper_with_index (
251
- builder , gen .indices [0 ], gen .sequences [0 ], sequence , set_item , gen .line , length
252
- )
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
+ )
253
272
254
- return target_op
273
+ return target_op
255
274
return None
256
275
257
276
@@ -804,7 +823,7 @@ class ForSequence(ForGenerator):
804
823
def init (
805
824
self , expr_reg : Value , target_type : RType , reverse : bool , length : Value | None = None
806
825
) -> None :
807
- assert is_sequence_rprimitive (expr_reg .type ), expr_reg
826
+ assert is_sequence_rprimitive (expr_reg .type ), ( expr_reg , expr_reg . type )
808
827
builder = self .builder
809
828
# Record a Value indicating the length of the sequence, if known at compile time.
810
829
self .length = length
@@ -834,7 +853,6 @@ def init(
834
853
def gen_condition (self ) -> None :
835
854
builder = self .builder
836
855
line = self .line
837
- # TODO: Don't reload the length each time when iterating an immutable sequence?
838
856
if self .reverse :
839
857
# If we are iterating in reverse order, we obviously need
840
858
# to check that the index is still positive. Somewhat less
@@ -1216,7 +1234,7 @@ def get_expr_length_value(
1216
1234
builder : IRBuilder , expr : Expression , expr_reg : Value , line : int , use_pyssize_t : bool
1217
1235
) -> Value :
1218
1236
rtype = builder .node_type (expr )
1219
- assert is_sequence_rprimitive (rtype ), rtype
1237
+ assert is_sequence_rprimitive (rtype ) or isinstance ( rtype , RTuple ) , rtype
1220
1238
length = get_expr_length (expr )
1221
1239
if length is None :
1222
1240
# We cannot compute the length at compile time, so we will fetch it.
0 commit comments