77
88from __future__ import annotations
99
10- from typing import Callable , ClassVar
10+ from typing import Any , Callable , ClassVar , Union
1111
1212from mypy .nodes import (
1313 ARG_POS ,
14+ BytesExpr ,
1415 CallExpr ,
1516 DictionaryComprehension ,
1617 Expression ,
18+ FloatExpr ,
1719 GeneratorExpr ,
20+ IntExpr ,
21+ ListExpr ,
1822 Lvalue ,
1923 MemberExpr ,
2024 NameExpr ,
@@ -385,6 +389,27 @@ def is_range_ref(expr: RefExpr) -> bool:
385389 )
386390
387391
392+ def is_literal_expr (expr : Expression ) -> bool :
393+ # Add other literal types as needed
394+ if isinstance (expr , (IntExpr , StrExpr , FloatExpr , BytesExpr )):
395+ return True
396+ if isinstance (expr , NameExpr ) and expr .fullname in {"builtins.None" , "builtins.True" , "builtins.False" }:
397+ return True
398+ return False
399+
400+
401+ def is_iterable_expr_with_literal_mambers (expr : Expression ) -> bool :
402+ return (
403+ isinstance (expr , (ListExpr , SetExpr , TupleExpr ))
404+ and not isinstance (expr , MemberExpr )
405+ and all (
406+ is_literal_expr (item )
407+ or is_iterable_expr_with_literal_mambers (item )
408+ for item in expr .items
409+ )
410+ )
411+
412+
388413def make_for_loop_generator (
389414 builder : IRBuilder ,
390415 index : Lvalue ,
@@ -413,21 +438,22 @@ def make_for_loop_generator(
413438 rtyp = builder .node_type (expr )
414439
415440 # Special case: tuple literal (unroll the loop)
416- if isinstance (expr , TupleExpr ):
417- return ForUnrolledLiteral (builder , index , body_block , loop_exit , line , expr . items , expr , body_insts )
441+ if is_iterable_expr_with_literal_mambers (expr ):
442+ return ForUnrolledSequenceLiteral (builder , index , body_block , loop_exit , line , expr , body_insts )
418443
419- # Special case: RTuple (known-length tuple, index-based iteration)
444+ # Special case: RTuple (known-length tuple, struct field iteration)
420445 if isinstance (rtyp , RTuple ):
421446 expr_reg = builder .accept (expr )
422- target_type = builder .get_sequence_type (expr )
423- for_tuple = ForSequence (builder , index , body_block , loop_exit , line , nested )
424- for_tuple .init (expr_reg , target_type , reverse = False )
425- return for_tuple
447+ return ForUnrolledRTuple (builder , index , body_block , loop_exit , line , rtyp , expr_reg , expr , body_insts )
426448
427449 # Special case: string literal (unroll the loop)
428450 if isinstance (expr , StrExpr ):
429451 return ForUnrolledStringLiteral (builder , index , body_block , loop_exit , line , expr .value , expr , body_insts )
430452
453+ # Special case: string literal (unroll the loop)
454+ if isinstance (expr , BytesExpr ):
455+ return ForUnrolledBytesLiteral (builder , index , body_block , loop_exit , line , expr .value , expr , body_insts )
456+
431457 if is_sequence_rprimitive (rtyp ):
432458 # Special case "for x in <list>".
433459 expr_reg = builder .accept (expr )
@@ -790,7 +816,28 @@ def gen_step(self) -> None:
790816 pass
791817
792818
793- class ForUnrolledLiteral (ForGenerator ):
819+ class _ForUnrolled (ForGenerator ):
820+ """Generate IR for a for loop over a value known at compile time by unrolling the loop.
821+
822+ This class emits the loop body for each element of the value literal directly,
823+ avoiding any runtime iteration logic and generator handling.
824+ """
825+
826+ def __init__ (self , * args : Any , ** kwargs : Any ):
827+ if type (self ) is _ForUnrolled :
828+ raise NotImplementedError ("This is a base class and should not be initialized directly." )
829+ super ().__init__ (* args , ** kwargs )
830+
831+ def gen_condition (self ) -> None :
832+ # Unrolled: nothing to do here.
833+ pass
834+
835+ def gen_step (self ) -> None :
836+ # Unrolled: nothing to do here.
837+ pass
838+
839+
840+ class ForUnrolledSequenceLiteral (_ForUnrolled ):
794841 """Generate IR for a for loop over a tuple literal by unrolling the loop.
795842
796843 This class emits the loop body for each element of the tuple literal directly,
@@ -805,31 +852,25 @@ def __init__(
805852 body_block : BasicBlock ,
806853 loop_exit : BasicBlock ,
807854 line : int ,
808- items : list [Expression ],
809- expr : Expression ,
855+ expr : Union [ListExpr , SetExpr , TupleExpr ],
810856 body_insts : GenFunc ,
811857 ) -> None :
812858 super ().__init__ (builder , index , body_block , loop_exit , line , nested = False )
813- self .items = items
814859 self .expr = expr
860+ self .items = expr .items
815861 self .body_insts = body_insts
816-
817- def gen_condition (self ) -> None :
818- # Unrolled: nothing to do here.
819- pass
862+ self .item_types = [builder .node_type (item ) for item in self .items ]
820863
821864 def begin_body (self ) -> None :
822865 builder = self .builder
823- for item in self .items :
824- builder .assign (builder .get_assignment_target (self .index ), builder .accept (item ), self .line )
866+ for item , item_type in zip (self .items , self .item_types ):
867+ value = builder .accept (item )
868+ value = builder .coerce (value , item_type , self .line )
869+ builder .assign (builder .get_assignment_target (self .index ), value , self .line )
825870 self .body_insts ()
826871
827- def gen_step (self ) -> None :
828- # Unrolled: nothing to do here.
829- pass
830-
831872
832- class ForUnrolledStringLiteral (ForGenerator ):
873+ class ForUnrolledStringLiteral (_ForUnrolled ):
833874 """Generate IR for a for loop over a string literal by unrolling the loop.
834875
835876 This class emits the loop body for each character of the string literal directly,
@@ -853,10 +894,6 @@ def __init__(
853894 self .expr = expr
854895 self .body_insts = body_insts
855896
856- def gen_condition (self ) -> None :
857- # Unrolled: nothing to do here.
858- pass
859-
860897 def begin_body (self ) -> None :
861898 builder = self .builder
862899 for c in self .value :
@@ -867,9 +904,74 @@ def begin_body(self) -> None:
867904 )
868905 self .body_insts ()
869906
870- def gen_step (self ) -> None :
871- # Unrolled: nothing to do here.
872- pass
907+
908+ class ForUnrolledBytesLiteral (_ForUnrolled ):
909+ """Generate IR for a for loop over a string literal by unrolling the loop.
910+
911+ This class emits the loop body for each character of the string literal directly,
912+ avoiding any runtime iteration logic.
913+ """
914+ handles_body_insts = True
915+
916+ def __init__ (
917+ self ,
918+ builder : IRBuilder ,
919+ index : Lvalue ,
920+ body_block : BasicBlock ,
921+ loop_exit : BasicBlock ,
922+ line : int ,
923+ value : bytes ,
924+ expr : Expression ,
925+ body_insts : GenFunc ,
926+ ) -> None :
927+ super ().__init__ (builder , index , body_block , loop_exit , line , nested = False )
928+ self .value = value
929+ self .expr = expr
930+ self .body_insts = body_insts
931+
932+ def begin_body (self ) -> None :
933+ builder = self .builder
934+ for c in self .value :
935+ builder .assign (
936+ builder .get_assignment_target (self .index ),
937+ builder .accept (IntExpr (c )),
938+ self .line ,
939+ )
940+ self .body_insts ()
941+
942+
943+ class ForUnrolledRTuple (_ForUnrolled ):
944+ """Generate IR for a for loop over an RTuple by directly accessing struct fields."""
945+
946+ handles_body_insts = True
947+
948+ def __init__ (
949+ self ,
950+ builder : IRBuilder ,
951+ index : Lvalue ,
952+ body_block : BasicBlock ,
953+ loop_exit : BasicBlock ,
954+ line : int ,
955+ rtuple_type : RTuple ,
956+ expr_reg : Value ,
957+ expr : Expression ,
958+ body_insts : GenFunc ,
959+ ) -> None :
960+ super ().__init__ (builder , index , body_block , loop_exit , line , nested = False )
961+ self .rtuple_type = rtuple_type
962+ self .expr_reg = expr_reg
963+ self .expr = expr
964+ self .body_insts = body_insts
965+
966+ def begin_body (self ) -> None :
967+ builder = self .builder
968+ line = self .line
969+ for i , item_type in enumerate (self .rtuple_type .types ):
970+ # Directly access the struct field for each RTuple element
971+ value = builder .add (TupleGet (self .expr_reg , i , line ))
972+ value = builder .coerce (value , item_type , line )
973+ builder .assign (builder .get_assignment_target (self .index ), value , line )
974+ self .body_insts ()
873975
874976
875977def unsafe_index (builder : IRBuilder , target : Value , index : Value , line : int ) -> Value :
0 commit comments