@@ -2573,6 +2573,11 @@ type_check_comprehension(Env, Compr, Expr, [{BGenerateTag, _P, Pat, Gen} | Quals
25732573 {TyL , VarBinds2 } =
25742574 type_check_comprehension (NewEnv , Compr , Expr , Quals ),
25752575 {TyL , union_var_binds (VarBinds1 , VarBinds2 , Env )};
2576+ type_check_comprehension (Env , Compr , Expr , [{zip , _ , Generators } | Quals ]) ->
2577+ % % Zip generators iterate in lockstep. Each generator's expression is
2578+ % % evaluated in the original Env (variables don't leak between arms).
2579+ NewEnv = type_check_zip_generators (Env , Generators ),
2580+ type_check_comprehension (NewEnv , Compr , Expr , Quals );
25762581type_check_comprehension (Env , Compr , Expr , [{MGenerateTag , _ , {map_field_exact , _ , KeyPat , ValPat }, Gen } | Quals ])
25772582 when MGenerateTag =:= m_generate ; MGenerateTag =:= m_generate_strict ->
25782583 {Ty , _VB1 } = type_check_expr (Env , Gen ),
@@ -2605,6 +2610,78 @@ type_check_comprehension(Env, Compr, Expr, [Guard | Quals]) ->
26052610 {TyL , VarBinds2 } = type_check_comprehension (NewEnv , Compr , Expr , Quals ),
26062611 {TyL , union_var_binds (VarBinds1 , VarBinds2 , Env )}.
26072612
2613+ % % Type check generators in a zip group.
2614+ % % All generator expressions are type-checked in the original Env
2615+ % % (generators are independent - variables don't leak between zip arms).
2616+ % % Pattern variables from all generators are bound in the returned env.
2617+ - spec type_check_zip_generators (env (), [_ ]) -> env ().
2618+ type_check_zip_generators (Env , Generators ) ->
2619+ % % Remove all pattern variables from all generators
2620+ GenEnv = lists :foldl (fun zip_remove_pat_vars /2 , Env , Generators ),
2621+ % % Type-check each generator's expression in the ORIGINAL Env
2622+ % % and bind pattern variables in the accumulated env
2623+ lists :foldl (
2624+ fun (Gen , AccEnv ) ->
2625+ type_check_single_zip_generator (Env , AccEnv , Gen )
2626+ end , GenEnv , Generators ).
2627+
2628+ - spec zip_remove_pat_vars (_ , env ()) -> env ().
2629+ zip_remove_pat_vars ({Tag , _ , Pat , _Gen }, Env )
2630+ when Tag =:= generate ; Tag =:= generate_strict ->
2631+ remove_pat_vars (Pat , Env );
2632+ zip_remove_pat_vars ({Tag , _ , Pat , _Gen }, Env )
2633+ when Tag =:= b_generate ; Tag =:= b_generate_strict ->
2634+ remove_pat_vars (Pat , Env );
2635+ zip_remove_pat_vars ({Tag , _ , {map_field_exact , _ , KeyPat , ValPat }, _Gen }, Env )
2636+ when Tag =:= m_generate ; Tag =:= m_generate_strict ->
2637+ remove_pat_vars (KeyPat , remove_pat_vars (ValPat , Env )).
2638+
2639+ - spec type_check_single_zip_generator (env (), env (), _ ) -> env ().
2640+ type_check_single_zip_generator (OrigEnv , AccEnv , {Tag , _ , Pat , Gen })
2641+ when Tag =:= generate ; Tag =:= generate_strict ->
2642+ {Ty , _ } = type_check_expr (OrigEnv , Gen ),
2643+ case expect_list_type (Ty , allow_nil_type , OrigEnv ) of
2644+ {elem_ty , ElemTy } ->
2645+ {_PatTys , _UBounds , NewEnv } =
2646+ add_types_pats ([Pat ], [ElemTy ], AccEnv , capture_vars ),
2647+ NewEnv ;
2648+ any ->
2649+ add_any_types_pat (Pat , AccEnv );
2650+ {elem_tys , _ElemTys } ->
2651+ add_any_types_pat (Pat , AccEnv );
2652+ {type_error , BadTy } ->
2653+ throw (type_error (Gen , BadTy , type (list )))
2654+ end ;
2655+ type_check_single_zip_generator (OrigEnv , AccEnv , {Tag , _ , Pat , Gen })
2656+ when Tag =:= b_generate ; Tag =:= b_generate_strict ->
2657+ BitStringTy = type (binary , [{integer , erl_anno :new (0 ), 0 },
2658+ {integer , erl_anno :new (0 ), 1 }]),
2659+ _VarBinds = type_check_expr_in (OrigEnv , BitStringTy , Gen ),
2660+ {_PatTys , _UBounds , NewEnv } =
2661+ add_types_pats ([Pat ], [BitStringTy ], AccEnv , capture_vars ),
2662+ NewEnv ;
2663+ type_check_single_zip_generator (OrigEnv , AccEnv , {Tag , _ , {map_field_exact , _ , KeyPat , ValPat }, Gen })
2664+ when Tag =:= m_generate ; Tag =:= m_generate_strict ->
2665+ {Ty , _ } = type_check_expr (OrigEnv , Gen ),
2666+ case expect_map_type (normalize (Ty , OrigEnv ), OrigEnv ) of
2667+ {assoc_tys , AssocTys } when is_list (AssocTys ) ->
2668+ {KeyTys , ValTys } = lists :foldl (
2669+ fun ({type , _ , _AssocTag , [KT , VT ]}, {KAcc , VAcc }) ->
2670+ {[KT | KAcc ], [VT | VAcc ]}
2671+ end , {[], []}, AssocTys ),
2672+ KeyTy = normalize (type (union , KeyTys ), OrigEnv ),
2673+ ValTy = normalize (type (union , ValTys ), OrigEnv ),
2674+ {_PatTys1 , _UBounds1 , Env1 } =
2675+ add_types_pats ([KeyPat ], [KeyTy ], AccEnv , capture_vars ),
2676+ {_PatTys2 , _UBounds2 , NewEnv } =
2677+ add_types_pats ([ValPat ], [ValTy ], Env1 , capture_vars ),
2678+ NewEnv ;
2679+ any ->
2680+ add_any_types_pat (ValPat , add_any_types_pat (KeyPat , AccEnv ));
2681+ {type_error , BadTy } ->
2682+ throw (type_error (Gen , BadTy , type (map )))
2683+ end .
2684+
26082685% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
26092686% % Checking the type of an expression
26102687% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -3315,12 +3392,13 @@ unary_op_arg_type('-', Ty = {type, _, float, []}) ->
33153392 Compr :: lc | bc | mc ,
33163393 Expr :: gradualizer_type :abstract_expr (),
33173394 Position :: erl_anno :anno (),
3318- Qualifiers :: [ListGen | BinGen | MapGen | Filter ]) ->
3395+ Qualifiers :: [ListGen | BinGen | MapGen | ZipGen | Filter ]) ->
33193396 env ()
33203397 when
33213398 ListGen :: {generate | generate_strict , erl_anno :anno (), gradualizer_type :abstract_expr (), gradualizer_type :abstract_expr ()},
33223399 BinGen :: {b_generate | b_generate_strict , erl_anno :anno (), gradualizer_type :abstract_expr (), gradualizer_type :abstract_expr ()},
33233400 MapGen :: {m_generate | m_generate_strict , erl_anno :anno (), gradualizer_type :abstract_expr (), gradualizer_type :abstract_expr ()},
3401+ ZipGen :: {zip , erl_anno :anno (), [ListGen | BinGen | MapGen ]},
33243402 Filter :: gradualizer_type :abstract_expr ().
33253403type_check_comprehension_in (Env , ResTy , OrigExpr , lc , Expr , _P , []) ->
33263404 case expect_list_type (ResTy , allow_nil_type , Env ) of
@@ -3389,6 +3467,12 @@ type_check_comprehension_in(Env, ResTy, OrigExpr, mc,
33893467 {type_error , _ } ->
33903468 throw (type_error (OrigExpr , type (map ), ResTy ))
33913469 end ;
3470+ type_check_comprehension_in (Env , ResTy , OrigExpr , Compr , Expr , P ,
3471+ [{zip , _ , Generators } | Quals ]) ->
3472+ % % Zip generators iterate in lockstep. Each generator's expression is
3473+ % % evaluated in the original Env (variables don't leak between arms).
3474+ NewEnv = type_check_zip_generators (Env , Generators ),
3475+ type_check_comprehension_in (NewEnv , ResTy , OrigExpr , Compr , Expr , P , Quals );
33923476type_check_comprehension_in (Env , ResTy , OrigExpr , Compr , Expr , P ,
33933477 [{MGenerateTag , _ , {map_field_exact , _ , KeyPat , ValPat }, Gen } | Quals ])
33943478 when MGenerateTag =:= m_generate ; MGenerateTag =:= m_generate_strict ->
0 commit comments