29
29
TypeAlias ,
30
30
Var ,
31
31
)
32
- from mypy .types import Type
32
+ from mypy .types import LiteralType , TupleType , Type , get_proper_type , get_proper_types
33
33
from mypyc .ir .ops import (
34
34
ERR_NEVER ,
35
35
BasicBlock ,
38
38
IntOp ,
39
39
LoadAddress ,
40
40
LoadErrorValue ,
41
+ LoadLiteral ,
41
42
LoadMem ,
42
43
MethodCall ,
43
44
RaiseStandardError ,
@@ -172,7 +173,7 @@ def for_loop_helper_with_index(
172
173
body_insts: a function that generates the body of the loop.
173
174
It needs a index as parameter.
174
175
"""
175
- assert is_sequence_rprimitive (expr_reg .type )
176
+ assert is_sequence_rprimitive (expr_reg .type ), ( expr_reg , expr_reg . type )
176
177
target_type = builder .get_sequence_type (expr )
177
178
178
179
body_block = BasicBlock ()
@@ -185,7 +186,12 @@ def for_loop_helper_with_index(
185
186
186
187
builder .push_loop_stack (step_block , exit_block )
187
188
188
- builder .goto_and_activate (condition_block )
189
+ if isinstance (length , Integer ) and length .value > 0 :
190
+ builder .goto (body_block )
191
+ builder .activate_block (condition_block )
192
+ else :
193
+ builder .goto_and_activate (condition_block )
194
+
189
195
for_gen .gen_condition ()
190
196
191
197
builder .activate_block (body_block )
@@ -214,10 +220,9 @@ def sequence_from_generator_preallocate_helper(
214
220
there is no condition list in the generator and only one original sequence with
215
221
one index is allowed.
216
222
217
- e.g. (1) tuple(f(x) for x in a_list/a_tuple/a_str/a_bytes)
218
- (2) list(f(x) for x in a_list/a_tuple/a_str/a_bytes)
219
- (3) [f(x) for x in a_list/a_tuple/a_str/a_bytes]
220
- RTuple as an original sequence is not supported yet.
223
+ e.g. (1) tuple(f(x) for x in a_list/a_tuple/a_str/a_bytes/an_rtuple)
224
+ (2) list(f(x) for x in a_list/a_tuple/a_str/a_bytes/an_rtuple)
225
+ (3) [f(x) for x in a_list/a_tuple/a_str/a_bytes/an_rtuple]
221
226
222
227
Args:
223
228
empty_op_llbuilder: A function that can generate an empty sequence op when
@@ -232,23 +237,41 @@ def sequence_from_generator_preallocate_helper(
232
237
implementation.
233
238
"""
234
239
if len (gen .sequences ) == 1 and len (gen .indices ) == 1 and len (gen .condlists [0 ]) == 0 :
235
- rtype = builder .node_type (gen .sequences [0 ])
236
- if is_sequence_rprimitive (rtype ):
237
- sequence = builder .accept (gen .sequences [0 ])
238
- length = get_expr_length_value (
239
- builder , gen .sequences [0 ], sequence , gen .line , use_pyssize_t = True
240
- )
241
- target_op = empty_op_llbuilder (length , gen .line )
242
-
243
- def set_item (item_index : Value ) -> None :
244
- e = builder .accept (gen .left_expr )
245
- builder .call_c (set_item_op , [target_op , item_index , e ], gen .line )
246
-
247
- for_loop_helper_with_index (
248
- builder , gen .indices [0 ], gen .sequences [0 ], sequence , set_item , gen .line , length
249
- )
240
+ line = gen .line
241
+ sequence_expr = gen .sequences [0 ]
242
+ rtype = builder .node_type (sequence_expr )
243
+ if not (is_sequence_rprimitive (rtype ) or isinstance (rtype , RTuple )):
244
+ return None
245
+ sequence = builder .accept (sequence_expr )
246
+ length = get_expr_length_value (builder , sequence_expr , sequence , line , use_pyssize_t = True )
247
+ if isinstance (rtype , RTuple ):
248
+ # If input is RTuple, box it to tuple_rprimitive for generic iteration
249
+ # TODO: this can be optimized a bit better with an unrolled ForRTuple helper
250
+ proper_type = get_proper_type (builder .types [sequence_expr ])
251
+ assert isinstance (proper_type , TupleType ), proper_type
252
+
253
+ get_item_ops = [
254
+ (
255
+ LoadLiteral (typ .value , object_rprimitive )
256
+ if isinstance (typ , LiteralType )
257
+ else TupleGet (sequence , i , line )
258
+ )
259
+ for i , typ in enumerate (get_proper_types (proper_type .items ))
260
+ ]
261
+ items = list (map (builder .add , get_item_ops ))
262
+ sequence = builder .new_tuple (items , line )
263
+
264
+ target_op = empty_op_llbuilder (length , line )
265
+
266
+ def set_item (item_index : Value ) -> None :
267
+ e = builder .accept (gen .left_expr )
268
+ builder .call_c (set_item_op , [target_op , item_index , e ], line )
269
+
270
+ for_loop_helper_with_index (
271
+ builder , gen .indices [0 ], sequence_expr , sequence , set_item , line , length
272
+ )
250
273
251
- return target_op
274
+ return target_op
252
275
return None
253
276
254
277
@@ -801,7 +824,7 @@ class ForSequence(ForGenerator):
801
824
def init (
802
825
self , expr_reg : Value , target_type : RType , reverse : bool , length : Value | None = None
803
826
) -> None :
804
- assert is_sequence_rprimitive (expr_reg .type ), expr_reg
827
+ assert is_sequence_rprimitive (expr_reg .type ), ( expr_reg , expr_reg . type )
805
828
builder = self .builder
806
829
# Record a Value indicating the length of the sequence, if known at compile time.
807
830
self .length = length
@@ -831,7 +854,6 @@ def init(
831
854
def gen_condition (self ) -> None :
832
855
builder = self .builder
833
856
line = self .line
834
- # TODO: Don't reload the length each time when iterating an immutable sequence?
835
857
if self .reverse :
836
858
# If we are iterating in reverse order, we obviously need
837
859
# to check that the index is still positive. Somewhat less
@@ -1213,7 +1235,7 @@ def get_expr_length_value(
1213
1235
builder : IRBuilder , expr : Expression , expr_reg : Value , line : int , use_pyssize_t : bool
1214
1236
) -> Value :
1215
1237
rtype = builder .node_type (expr )
1216
- assert is_sequence_rprimitive (rtype ), rtype
1238
+ assert is_sequence_rprimitive (rtype ) or isinstance ( rtype , RTuple ) , rtype
1217
1239
length = get_expr_length (expr )
1218
1240
if length is None :
1219
1241
# We cannot compute the length at compile time, so we will fetch it.
0 commit comments