File 1831-compiler-Add-zip-generators-for-comprehensions.patch of Package erlang

From 899322d9f8814900b97b362bab09b2c0e85cd62b Mon Sep 17 00:00:00 2001
From: lucioleKi <isabell@erlang.org>
Date: Fri, 16 Aug 2024 09:10:26 +0200
Subject: [PATCH] compiler: Add zip generators for comprehensions

We introduce zip generators for comprehensions to reduce the need for
users to use lists:zip.

Zip generators have the syntax of generator1 && ... && generatorN,
where each generator can be a list, binary, or map generator.

Zip generators are evaluated as if they are zipped to be a list of
tuples. They can be mixed with all existing generators and filters
freely.

Two examples to show how zip generators is used and evaluated:

    1> [{X,Y} || X <- [a,b,c] && Y <- [1,2,3]].
    [{a,1},{b,2},{c,3}]
    2> << <<(X+Y)/integer>> || X <- [1,2,3] && <<Y>> <= <<1,1,1>>, X < 3 >>.
    <<2,3>>
---
 erts/doc/guides/absform.md                    |   3 +
 lib/compiler/src/sys_coverage.erl             |   6 +
 lib/compiler/src/v3_core.erl                  | 251 +++++++++-
 lib/compiler/test/Makefile                    |   4 +-
 lib/compiler/test/zlc_SUITE.erl               | 452 ++++++++++++++++++
 lib/debugger/src/dbg_ieval.erl                | 285 +++++++++++
 lib/debugger/src/dbg_iload.erl                |  20 +-
 lib/debugger/src/debugger.app.src             |   2 +-
 lib/debugger/test/Makefile                    |   1 +
 lib/debugger/test/erl_eval_SUITE.erl          | 160 ++++++-
 lib/debugger/test/zlc_SUITE.erl               | 410 ++++++++++++++++
 lib/stdlib/examples/erl_id_trans.erl          |   3 +
 lib/stdlib/src/erl_error.erl                  |   2 +
 lib/stdlib/src/erl_eval.erl                   | 337 ++++++++++++-
 lib/stdlib/src/erl_expand_records.erl         |   4 +
 lib/stdlib/src/erl_lint.erl                   |  40 ++
 lib/stdlib/src/erl_parse.yrl                  |  15 +-
 lib/stdlib/src/erl_pp.erl                     |   2 +
 lib/stdlib/src/erl_scan.erl                   |   8 +-
 lib/stdlib/test/erl_eval_SUITE.erl            | 161 ++++++-
 lib/stdlib/test/erl_expand_records_SUITE.erl  |  13 +-
 lib/stdlib/test/erl_lint_SUITE.erl            |  24 +-
 lib/syntax_tools/src/erl_prettypr.erl         |   6 +
 lib/syntax_tools/src/erl_syntax.erl           |  58 ++-
 lib/syntax_tools/src/erl_syntax_lib.erl       |  32 ++
 lib/syntax_tools/test/syntax_tools_SUITE.erl  |   6 +
 .../syntax_tools_SUITE_test_module.erl        |   9 +-
 lib/tools/emacs/erlang.el                     |   4 +-
 28 files changed, 2277 insertions(+), 41 deletions(-)
 create mode 100644 lib/compiler/test/zlc_SUITE.erl
 create mode 100644 lib/debugger/test/zlc_SUITE.erl

diff --git a/erts/doc/guides/absform.md b/erts/doc/guides/absform.md
index 716dd132eb..1f814d8d48 100644
--- a/erts/doc/guides/absform.md
+++ b/erts/doc/guides/absform.md
@@ -292,6 +292,9 @@ An expression E is one of the following:
 A qualifier Q is one of the following:
 
 - If Q is a filter `E`, where `E` is an expression, then Rep(Q) = `Rep(E)`.
+- If Q is a zip generator `Q_1 && ...&& Q_k]`, where each `Q_i` is
+  a non-zip generator, then Rep(E) = `{zip,ANNO,[Rep(Q_1), ..., Rep(Q_k)]}`.
+  For Rep(Q), see below.
 - If Q is a list generator `P <- E`, where `P` is a pattern and `E` is an
   expression, then Rep(Q) = `{generate,ANNO,Rep(P),Rep(E)}`.
 - If Q is a list generator `P <:- E`, where `P` is a pattern and `E` is an
diff --git a/lib/compiler/src/sys_coverage.erl b/lib/compiler/src/sys_coverage.erl
index 9ad9899304..b3515f83e8 100644
--- a/lib/compiler/src/sys_coverage.erl
+++ b/lib/compiler/src/sys_coverage.erl
@@ -573,6 +573,12 @@ munge_qs([{m_generate_strict,Anno,Pattern,Expr}|Qs], Vars0, MQs) ->
     A = element(2, Expr),
     {MExpr, Vars1} = munge_expr(Expr, Vars0),
     munge_qs1(Qs, A, {m_generate_strict,Anno,Pattern,MExpr}, Vars0, Vars1, MQs);
+munge_qs([{zip,Anno,Gs0}|Qs], Vars0, MQs) ->
+    {Gs1, Vars1} = munge_qualifiers(Gs0, Vars0),
+    %% Get rid of dummy filters inserted by munge_qualifiers/2 --
+    %% they are not allowed in the zip construct.
+    Gs = [G || G <- Gs1, element(1, G) =/= block],
+    munge_qs1(Qs, Anno, {zip,Anno,Gs}, Vars0, Vars1, MQs);
 munge_qs([Expr|Qs], Vars0, MQs) ->
     A = element(2, Expr),
     {MungedExpr, Vars1} = munge_expr(Expr, Vars0),
diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl
index 448b321326..1acd6ed9b0 100644
--- a/lib/compiler/src/v3_core.erl
+++ b/lib/compiler/src/v3_core.erl
@@ -82,9 +82,12 @@
 
 -export([module/2,format_error/1]).
 
--import(lists, [any/2,reverse/1,reverse/2,map/2,member/2,foldl/3,foldr/3,mapfoldl/3,
-                splitwith/2,keydelete/3,keyfind/3,keymember/3,sort/1,droplast/1,last/1,
-                duplicate/2]).
+-import(lists, [all/2,any/2,append/1,droplast/1,duplicate/2,
+                foldl/3,foldr/3,
+                keydelete/3,keyfind/3,keymember/3,
+                last/1,map/2,member/2,mapfoldl/3,
+                reverse/1,reverse/2,
+                split/2,splitwith/2,sort/1]).
 -import(ordsets, [add_element/2,del_element/2,is_element/2,
 		  union/1,union/2,intersection/2,subtract/2]).
 -import(cerl, [ann_c_cons/3,ann_c_tuple/2,c_tuple/1,
@@ -124,6 +127,10 @@
                     nomatch_pat,nomatch_mode,
                     tail,tail_pat,arg,
                     refill={nomatch,ignore}}).
+-record(izip,      {anno=#a{},acc_pats=[],acc_guard,
+                    nomatch_pats=[],nomatch_total=[],skip_pats=[],
+                    tails=[],tail_pats=[],pres=[],args=[],
+                    refill_pats=[],refill_as=[]}).
 -record(isimple,   {anno=#a{},term :: cerl:cerl()}).
 
 -type iapply()    :: #iapply{}.
@@ -144,13 +151,14 @@
 -type itry()      :: #itry{}.
 -type ifilter()   :: #ifilter{}.
 -type igen()      :: #igen{}.
+-type izip()      :: #izip{}.
 -type isimple()   :: #isimple{}.
 
 -type i() :: iapply()    | ibinary()   | icall()     | icase()  | icatch()
            | iclause()   | ifun()      | iletrec()   | imatch() | imap()
            | iprimop()   | iprotect()  | ireceive1() | ireceive2()
            | iset()      | itry()      | ifilter()
-           | igen()      | isimple().
+           | igen()      | izip()      | isimple().
 
 -type warning() :: {file:filename(), [{integer(), module(), term()}]}.
 
@@ -1602,7 +1610,20 @@ fun_tq(Cs0, L, St0, NameInfo) ->
 %% lc_tq(Line, Exp, [Qualifier], Mc, State) -> {LetRec,[PreExp],State}.
 %%  This TQ from Simon PJ pp 127-138.
 
-lc_tq(Line, E, [#igen{anno=#a{anno=GA}=GAnno,
+lc_tq(Line, E, [#igen{}|_T] = Qs, Mc, St) ->
+    lc_tq1(Line, E, Qs, Mc, St);
+lc_tq(Line, E, [#izip{}=Zip|Qs], Mc, St) ->
+    zip_tq(Line, E, Zip, Mc, St, Qs);
+lc_tq(Line, E, [#ifilter{}=Filter|Qs], Mc, St) ->
+    filter_tq(Line, E, Filter, Mc, St, Qs, fun lc_tq/5);
+lc_tq(Line, E0, [], Mc0, St0) ->
+    {H1,Hps,St1} = safe(E0, St0),
+    {T1,Tps,St} = force_safe(Mc0, St1),
+    Anno = lineno_anno(Line, St),
+    E = ann_c_cons(Anno, H1, T1),
+    {set_anno(E, [compiler_generated|Anno]),Hps ++ Tps,St}.
+
+lc_tq1(Line, E, [#igen{anno=#a{anno=GA}=GAnno,
 		      acc_pat=AccPat,acc_guard=AccGuard,
                       nomatch_pat=NomatchPat,
                       nomatch_mode=NomatchMode,
@@ -1645,15 +1666,61 @@ lc_tq(Line, E, [#igen{anno=#a{anno=GA}=GAnno,
     Fun = #ifun{anno=GAnno,id=[],vars=[Var],clauses=Cs,fc=Fc},
     {#iletrec{anno=GAnno#a{anno=[list_comprehension|GA]},defs=[{{Name,1},Fun}],
               body=Pre ++ [#iapply{anno=GAnno,op=F,args=[Arg]}]},
-     [],St3};
-lc_tq(Line, E, [#ifilter{}=Filter|Qs], Mc, St) ->
-    filter_tq(Line, E, Filter, Mc, St, Qs, fun lc_tq/5);
-lc_tq(Line, E0, [], Mc0, St0) ->
-    {H1,Hps,St1} = safe(E0, St0),
-    {T1,Tps,St} = force_safe(Mc0, St1),
-    Anno = lineno_anno(Line, St),
-    E = ann_c_cons(Anno, H1, T1),
-    {set_anno(E, [compiler_generated|Anno]),Hps ++ Tps,St}.
+     [],St3}.
+
+%% zip_tq(Line, Exp, [Qualifier], Mc, State, TqFun) -> {LetRec,[PreExp],State}.
+zip_tq(Line, E, #izip{anno=#a{anno=GA}=GAnno,
+                      acc_pats=AccPats,acc_guard=AccGuard,
+                      nomatch_total=NomatchTotal,
+                      skip_pats=SkipPats,
+                      tails=TailVars,tail_pats=TailPats,
+                      refill_pats=RefillPats0,
+                      refill_as=RefillAs,pres=Pres,args=Args}, Mc, St0, Qs) ->
+    {Name,St1} = new_fun_name("zlc", St0),
+    LA = lineno_anno(Line, St1),
+    NumGenerators = length(AccPats),
+
+    %% Generate new vars for each generator, 1 for the regular call, and 1 for
+    %% the bad generator case.
+    {CallVars,St2} = new_vars(NumGenerators, St1),
+    {FcVars, St3} = new_vars(NumGenerators, St2),
+
+    %% Generate the name for the letrec.
+    F = #c_var{anno=LA,name={Name,NumGenerators}},
+
+    %% Generate the clauses for the letrec. First, the accumulating
+    %% clause.
+    Sc = #iapply{anno=GAnno,op=F,args=TailVars},
+    {Lc,Lps,St4} = lc_tq(Line, E, Qs, Sc, St3),
+    AccClause = make_clause(LA, AccPats, AccGuard, Lps++[Lc]),
+
+    %% Generate the skip clause unless all generators are strict, in
+    %% which case no skipping is possible.
+    AccClauseNoGuards =
+        case NomatchTotal of
+            strict ->
+                nomatch;
+            _ ->
+                make_clause([skip_clause,compiler_generated|LA],
+                            SkipPats, [], [Sc])
+        end,
+
+    %% Generate the clause testing for empty generators.
+    TailClause = make_clause(LA, TailPats, [], [Mc]),
+
+    %% Generate refill clauses for map generators.
+    RefillClauses = make_refill(RefillPats0, 0, RefillAs, {TailVars, LA, [], Sc}),
+
+    %% Gather clauses.
+    Cs0 = [AccClause, AccClauseNoGuards, TailClause | RefillClauses],
+    Cs = [C || C <- Cs0, C =/= nomatch],
+
+    Fc = bad_generators(FcVars, hd(Args), lc, bad_generators),
+    Fun = #ifun{anno=GAnno,id=[],vars=CallVars,clauses=Cs,fc=Fc},
+    {#iletrec{anno=GAnno#a{anno=[list_comprehension|GA]},
+              defs=[{{Name,NumGenerators},Fun}],
+              body=append(Pres) ++
+                  [#iapply{anno=GAnno,op=F,args=Args}]},[],St4}.
 
 %% bc_tq(Line, Exp, [Qualifier], More, State) -> {LetRec,[PreExp],State}.
 %%  This TQ from Gustafsson ERLANG'05.
@@ -1729,6 +1796,8 @@ bc_tq1(Line, E, [#igen{anno=#a{anno=GA}=GAnno,
               defs=[{{Name,2},Fun}],
               body=Pre ++ [#iapply{anno=LAnno,op=F,args=[Arg,Mc]}]},
      [],St5};
+bc_tq1(Line, E, [#izip{}=Zip|Qs], Mc, St) ->
+    bzip_tq1(Line, E, Zip, Mc, St, Qs);
 bc_tq1(Line, E, [#ifilter{}=Filter|Qs], Mc, St) ->
     filter_tq(Line, E, Filter, Mc, St, Qs, fun bc_tq1/5);
 bc_tq1(_, {bin,Bl,Elements}, [], AccVar, St0) ->
@@ -1765,6 +1834,66 @@ bc_tq_build(Line, Pre0, #c_var{name=AccVar}, Elements0, St0) ->
     Anno = Anno0#a{anno=[compiler_generated,single_use|A]},
     {set_anno(E, Anno),Pre0++Pre,St}.
 
+bzip_tq1(Line, E, #izip{anno=#a{anno=_GA}=GAnno,
+                        acc_pats=AccPats,acc_guard=AccGuard,
+                        nomatch_total=NomatchTotal,
+                        skip_pats=SkipPats,
+                        tails=TailVars,tail_pats=TailPats,
+                        refill_pats=RefillPats0,
+                        refill_as=RefillAs,pres=Pres,args=Args}, Mc, St0, Qs) ->
+    {Name,St1} = new_fun_name("bzip", St0),
+    LA = lineno_anno(Line, St1),
+    LAnno = #a{anno=LA},
+    Arity = length(AccPats) + 1,
+
+    %% Generate new vars for each generator, 1 for the regular call, and 1 for
+    %% the bad generator case. last(CallVars) is used as the accumulator var
+    %% when constructing the new binary.
+    {CallVars, St2} = new_vars(LA, Arity, St1),
+    {FcVars, St3} = new_vars(LA, Arity, St2),
+
+    %% Generate the name for the letrec.
+    F = #c_var{anno=LA,name={Name,Arity}},
+
+    %% Generate the clauses for the letrec. First, the accumulating
+    %% clause.
+    BinAccVar = last(CallVars),
+    Sc = #iapply{anno=GAnno,op=F,args=TailVars++[BinAccVar]},
+    {Bc,Bps,St4} = bc_tq1(Line, E, Qs, BinAccVar, St3),
+    Body = Bps++[#iset{var=hd(CallVars), arg=Bc}, Sc],
+    AccClause = make_clause(LA, AccPats++[Mc], AccGuard, Body),
+
+    %% Generate the skip clause unless all generators are strict, in
+    %% which case no skipping is possible.
+    AccClauseNoGuards =
+        case NomatchTotal of
+            strict ->
+                nomatch;
+            _ ->
+                make_clause([skip_clause,compiler_generated|LA],
+                            SkipPats++[Mc], [], [Sc])
+        end,
+
+    %% Generate the clause testing for empty generators.
+    TailClause = make_clause(LA, TailPats++[Mc], [], [Mc]),
+
+    %% Generate refill clauses for map generators.
+    RefillClauses = make_refill(RefillPats0, 0, RefillAs, {TailVars, LA, [Mc], Sc}),
+
+    %% Gather clauses.
+    Cs0 = [AccClause, AccClauseNoGuards, TailClause | RefillClauses],
+    Cs = [C || C <- Cs0, C =/= nomatch],
+
+    Fc = bad_generators(FcVars, hd(Args), bc, bad_generators),
+    Fun = #ifun{anno=GAnno,id=[],vars=CallVars,clauses=Cs,fc=Fc},
+
+    %% Inlining would disable the size calculation optimization for
+    %% bs_init_writable.
+    {#iletrec{anno=LAnno#a{anno=[list_comprehension,no_inline|LA]},
+              defs=[{{Name,Arity},Fun}],
+              body=append(Pres) ++
+                  [#iapply{anno=LAnno,op=F,args=Args++[Mc]}]},[],St4}.
+
 mc_tq(Line, {map_field_assoc,Lf,K,V}, Qs, Mc, St0) ->
     E = {tuple,Lf,[K,V]},
     {Lc,Pre0,St1} = lc_tq(Line, E, Qs, Mc, St0),
@@ -1775,6 +1904,19 @@ mc_tq(Line, {map_field_assoc,Lf,K,V}, Qs, Mc, St0) ->
                   args=[LcVar]},
     {Call,Pre,St2}.
 
+make_refill([nomatch|RefillPats], Index, [_|Bodies], Args) ->
+    make_refill(RefillPats, Index + 1, Bodies, Args);
+make_refill([RefillPat0|RefillPats], Index, [RefillBody|Bodies], {TailVars, LA, Mc, Sc}=Args) ->
+    {H, [_|T]} = split(Index, TailVars),
+    RefillPat1 = H ++ [RefillPat0|T] ++ Mc,
+    RefillClause = make_clause(LA, RefillPat1, [], [RefillBody,Sc]),
+    [RefillClause|make_refill(RefillPats, Index + 1, Bodies, Args)];
+make_refill([], _Index, [], _Args) ->
+    [].
+
+make_clause(Anno, [Pat|PatExtra], Guard, Body) ->
+    make_clause(Anno, Pat, PatExtra, Guard, Body).
+
 make_clause(_Anno, nomatch, _PatExtra, _Guard, _Body) ->
     nomatch;
 make_clause(Anno, Pat, PatExtra, Guard, Body) ->
@@ -1825,6 +1967,31 @@ filter_tq(Line, E, #ifilter{anno=#a{anno=LA}=LAnno,arg=Guard},
 preprocess_quals(Line, Qs, St) ->
     preprocess_quals(Line, Qs, St, []).
 
+preprocess_quals(Line, [{zip,Anno,Gens}|Qs], St, Acc) ->
+    LAnno = #a{anno=lineno_anno(Anno, St)},
+    {Gens1, St1} = preprocess_quals(Line, Gens, St, []),
+    [#igen{acc_guard=AccGuard}|_] = Gens1,
+    Zip0 = #izip{anno=LAnno,
+                 acc_guard=AccGuard},
+    Zip1 = preprocess_zip_generators(Gens1, Zip0),
+    Zip2 = Zip1#izip{skip_pats=[case NomatchMode of
+                                    skip -> NomatchPat;
+                                    _ -> AccPat
+                                end ||
+                                   {NomatchMode, NomatchPat, AccPat} <:-
+                                       lists:zip3(Zip1#izip.nomatch_total,
+                                                  Zip1#izip.nomatch_pats,
+                                                  Zip1#izip.acc_pats)],
+                     tail_pats=[case {NomatchMode,AccPat} of
+                                    {skip,_} -> AccPat;
+                                    {_,#ibinary{}} -> AccPat#ibinary{segments=[]};
+                                    {_,_} -> AccPat
+                                end ||
+                                   {NomatchMode, AccPat} <:-
+                                       lists:zip(Zip1#izip.nomatch_total,
+                                                 Zip1#izip.tail_pats)],
+                     nomatch_total=get_nomatch_total(Zip1#izip.nomatch_total)},
+    preprocess_quals(Line, Qs, St1, [Zip2|Acc]);
 preprocess_quals(Line, [Q|Qs0], St0, Acc) ->
     case is_generator(Q) of
         true ->
@@ -1853,6 +2020,39 @@ preprocess_quals(Line, [Q|Qs0], St0, Acc) ->
 preprocess_quals(_, [], St, Acc) ->
     {reverse(Acc),St}.
 
+preprocess_zip_generators([#igen{}=Igen | Rest], #izip{}=Zip0) ->
+    Zip = preprocess_zip_generators(Rest, Zip0),
+
+    #igen{arg={Pre,Arg},
+          tail=Tail,
+          acc_pat=AccPat,
+          tail_pat=TailPat,
+          nomatch_mode=NomatchMode,
+          nomatch_pat=NomatchPat,
+          refill={RefillPat, RefillArg}} = Igen,
+
+    Zip#izip{acc_pats=[AccPat | Zip#izip.acc_pats],
+             tails=[Tail | Zip#izip.tails],
+             tail_pats=[TailPat | Zip#izip.tail_pats],
+             nomatch_total = [NomatchMode | Zip#izip.nomatch_total],
+             nomatch_pats = [NomatchPat | Zip#izip.nomatch_pats],
+             refill_pats=[RefillPat | Zip#izip.refill_pats],
+             refill_as=[RefillArg | Zip#izip.refill_as],
+             pres=[Pre | Zip#izip.pres],
+             args=[Arg | Zip#izip.args]};
+preprocess_zip_generators([], Zip) ->
+    Zip.
+
+get_nomatch_total(NomatchModes) ->
+    case all(fun(X) -> X =:= skip end, NomatchModes) of
+        true -> skip;
+        false ->
+            case any(fun(X) -> X =:= skip end, NomatchModes) of
+                true -> mixed;
+                false -> strict
+            end
+        end.
+
 is_generator({generate,_,_,_}) -> true;
 is_generator({generate_strict,_,_,_}) -> true;
 is_generator({b_generate,_,_,_}) -> true;
@@ -2023,7 +2223,7 @@ generator(Line, {Generate,Lg,{map_field_exact,_,K0,V0},E}, Gs, St0) when
     %%                   call 'erlang':'error'({'badmatch',{K,V}})
     %%               <'none'> when 'true' ->
     %%                   []
-    %%               <Iter> when 'true' ->
+    %%               <Iter=[_|_]> when 'true' ->
     %%                   let NextIter =
     %%                       call 'erts_internal':'mc_refill'(Iter)
     %%                   in apply 'lc$^0'/1(NextIter)
@@ -2062,12 +2262,12 @@ generator(Line, {Generate,Lg,{map_field_exact,_,K0,V0},E}, Gs, St0) when
                       m_generate_strict ->
                           #c_tuple{es=[NomatchK,NomatchV]}
                   end,
-    Refill = {NomatchK,
+    Refill = {ann_c_cons(GA,NomatchK,NomatchV),
               #iset{var=IterVar,
                     arg=#icall{anno=#a{anno=GA},
                                module=#c_literal{val=erts_internal},
                                name=#c_literal{val=mc_refill},
-                               args=[NomatchK]}}},
+                               args=[ann_c_cons(GA,NomatchK,NomatchV)]}}},
 
     InitIter = #icall{anno=#a{anno=GA},
                       module=#c_literal{val=erts_internal},
@@ -2537,8 +2737,21 @@ new_vars_1(N, Anno, St0, Vs) when N > 0 ->
 new_vars_1(0, _, St, Vs) -> {Vs,St}.
 
 bad_generator(Ps, Generator, Arg) ->
+    L = [#c_literal{val=bad_generator}, Generator],
+    bad_generator_common(L, Ps, Arg).
+
+bad_generators(Ps, Arg, bc, ErrorType) ->
+    T1 = #c_tuple{es=droplast(Ps)},
+    L = [#c_literal{val=ErrorType}, T1],
+    bad_generator_common(L, Ps, Arg);
+bad_generators(Ps, Arg, lc, ErrorType) ->
+    T = #c_tuple{es=Ps},
+    L = [#c_literal{val=ErrorType}, T],
+    bad_generator_common(L, Ps, Arg).
+
+bad_generator_common(L, Ps, Arg) ->
     Anno = get_anno(Arg),
-    Tuple = ann_c_tuple(Anno, [#c_literal{val=bad_generator},Generator]),
+    Tuple = ann_c_tuple(Anno, L),
     Call = #icall{anno=#a{anno=Anno},           %Must have an #a{}
                   module=#c_literal{anno=Anno,val=erlang},
                   name=#c_literal{anno=Anno,val=error},
@@ -4224,7 +4437,7 @@ is_simple(_) -> false.
 
 -spec is_simple_list([cerl:cerl()]) -> boolean().
 
-is_simple_list(Es) -> lists:all(fun is_simple/1, Es).
+is_simple_list(Es) -> all(fun is_simple/1, Es).
 
 insert_nif_start([VF={V,F=#c_fun{body=Body}}|Funs]) ->
     case Body of
diff --git a/lib/compiler/test/Makefile b/lib/compiler/test/Makefile
index 9ad903f0a2..dc3cbe128b 100644
--- a/lib/compiler/test/Makefile
+++ b/lib/compiler/test/Makefile
@@ -52,6 +52,7 @@ MODULES= \
 	trycatch_SUITE \
 	warnings_SUITE \
 	z_SUITE \
+	zlc_SUITE \
 	test_lib
 
 NO_OPT= \
@@ -87,7 +88,8 @@ NO_OPT= \
 	overridden_bif \
 	receive \
 	record \
-	trycatch
+	trycatch \
+	zlc
 
 INLINE= \
 	andor \
diff --git a/lib/compiler/test/zlc_SUITE.erl b/lib/compiler/test/zlc_SUITE.erl
new file mode 100644
index 0000000000..eb043d79a5
--- /dev/null
+++ b/lib/compiler/test/zlc_SUITE.erl
@@ -0,0 +1,452 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2001-2024. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%%     http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(zlc_SUITE).
+
+-export([all/0, suite/0, groups/0, init_per_suite/1, end_per_suite/1,
+         init_per_group/2,end_per_group/2,
+         init_per_testcase/2,end_per_testcase/2,
+         basic/1,mixed_zlc/1,zmc/1,filter_guard/1,
+         filter_pattern/1,cartesian/1,nomatch/1,bad_generators/1,
+         strict_list/1,strict_binary/1]).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("stdlib/include/assert.hrl").
+
+suite() ->
+    [{ct_hooks,[ts_install_cth]},
+     {timetrap,{minutes,1}}].
+
+all() ->
+    [{group,p}].
+
+groups() ->
+    [{p,test_lib:parallel(),
+      [basic,
+       mixed_zlc,
+       zmc,
+       filter_guard,
+       filter_pattern,
+       cartesian,
+       nomatch,
+       bad_generators,
+       strict_list,
+       strict_binary
+      ]}].
+
+init_per_suite(Config) ->
+    test_lib:recompile(?MODULE),
+    Config.
+
+end_per_suite(_Config) ->
+    ok.
+
+init_per_group(_GroupName, Config) ->
+    Config.
+
+end_per_group(_GroupName, Config) ->
+    Config.
+
+
+init_per_testcase(Case, Config) when is_atom(Case), is_list(Config) ->
+    Config.
+
+end_per_testcase(Case, Config) when is_atom(Case), is_list(Config) ->
+    ok.
+
+basic(Config) when is_list(Config) ->
+    [6, 7, 8] = [X + Y + Z || X <- [1, 2, 3] && Y <- [2, 2, 2] && Z <- [3,3,3]],
+    [{1, 2, 3}, {2, 2, 3}, {3, 2, 3}] =
+        [{X, Y, Z} || X <- [1, 2, 3] && Y <- [2, 2, 2] && Z <- [3,3,3]],
+    [6, 24] = zipwith4(fun(A, B, C, D) -> (A + B + C) * D end,
+                       [1, 5], [2, 2], [0, 1], [2, 3]),
+    96 = dot([1, 2, 3, 4], [24, 12, 8, 6]),
+
+    [1, 4, 5] = ifelse([true, false, true], [1, 3, 5], [2, 4, 6]),
+
+    [8, 14, 20] = [X + Y + Z || <<X>> <= <<5, 10, 15>> && <<Y>> <= <<1, 2, 3>>
+                                    && <<Z>> <= <<2, 2, 2>>].
+
+
+zipwith4(F, As, Bs, Cs, Ds) ->
+    [F(A,B,C,D) || A <- As && B <- Bs && C <- Cs && D <- Ds].
+
+dot(Xs, Ys) ->
+    lists:sum([X*Y || X <- Xs && Y <- Ys]).
+
+ifelse(Tests, Xs, Ys) ->
+    %% Simulate R's ifelse(,,)
+    [case T of
+         true -> X;
+         false -> Y
+     end || T <- Tests && X <- Xs && Y <- Ys
+    ].
+
+mixed_zlc(Config) when is_list(Config) ->
+    [{a, 2}, {b, 4}, {c, 6}] = [{X,Y} || X <- [a,b,c] && <<Y>> <= <<2,4,6>>],
+    [{a, 2}, {b, 4}, {c, 6}] = [{X,Y} || <<Y>> <= <<2,4,6>> && X <- [a,b,c]],
+    [{a,c,1,3}, {b,d,2,4}] = [{K1,K2,V1,V2}||
+                                 K1 := V1 <- maps:iterator(#{a=>1, b=>2}, ordered) &&
+                                     K2 := V2 <- maps:iterator(#{c=>3, d=>4}, ordered)],
+    [{a,1,2}, {b,2,4}] =
+        [{K1,V1,Y} || K1 := V1 <- maps:iterator(#{a=>1, b=>2}, ordered) &&
+                          <<Y>> <= <<2,4>>],
+    [{a,1,2}, {b,2,4}] = [{K1,V1,Y} ||
+                             K1 := V1 <- maps:iterator(#{a=>1, b=>2}, ordered) &&
+                                 <<Y>> <= <<2,4>>],
+    <<3,4,5>> = << <<(X+Y)/integer>> || X <- [1,2,3] && Y <- [2,2,2]>>,
+    <<3,4,5>> = << <<(X+V1)/integer>> ||
+                    X <- [1,2,3] &&
+                        _K1 := V1 <- maps:iterator(#{a=>2, b=>2, c=>2}, ordered)>>,
+    <<3,4,5>> = << <<(X+V1)/integer>> ||
+                    <<X>> <= <<1,2,3>> &&
+                        _K1 := V1 <- maps:iterator(#{a=>2, b=>2, c=>2}, ordered)>>,
+    <<3,4,5>> = << <<(V1+V2)/integer>> ||
+                    _K1 := V1 <- maps:iterator(#{a=>1, b=>2, c=>3}, ordered) &&
+                        _K2 := V2 <- maps:iterator(#{a=>2, b=>2, c=>2}, ordered)>>,
+    #{c := 3,b := 2,a := 1} = #{X => Y || X <- [a,b,c] && Y <- [1,2,3]},
+    #{c := 3,b := 2,a := 1} = #{X => Y || X <- [a,b,c] && <<Y>> <= <<1,2,3>>},
+    ok.
+
+zmc(Config) when is_list(Config) ->
+    [{a,b,1,3}] = [{K1, K2, V1, V2} || K1 := V1 <- #{a=>1} && K2 := V2 <- #{b=>3}],
+    Seq = lists:seq(1, 50),
+    M1 = maps:iterator(#{X=>X || X <- Seq}, ordered),
+    M2 = maps:iterator(#{X=>X || X <- lists:seq(1,50)}, ordered),
+    true = [A * 4 || A <- Seq] =:=
+        [X+Y+Z+W || X := Y <- M1 && Z := W <- M2],
+    true = << <<(A * 4):64>> || A <- Seq>> =:=
+        << <<(X+Y+Z+W):64>> || X := Y <- M1 && Z := W <- M2>>,
+
+    M3 = maps:iterator(#{X=>X*3 || X <- Seq}, ordered),
+    M4 = maps:iterator(#{X*2=>X*4 || X <- Seq}, ordered),
+    true = [{A, A*3, A*2, A*4} || A <- Seq] =:=
+        [{X, Y, Z, W} || X := Y <- M3 && Z := W <- M4],
+    true = [A * 3 || A <- Seq] =:= [X+Y+Z || X := Y <- M1 && Z <- Seq],
+    true = << <<A:64, (A*3):64, (A*2):64, (A*4):64>> || A <- Seq>> =:=
+        << <<X:64, Y:64, Z:64, W:64>> || X := Y <- M3 && Z := W <- M4>>,
+    true = << <<(A*3):64>> || A <- Seq>> =:=
+        << <<(X+Y+Z):64>> || X := Y <- M1 && Z <- Seq>>,
+
+    M5 = maps:iterator(#{X =>
+                             case X rem 2 of
+                                 0 -> {ok,X};
+                                 1 -> {error,X}
+                             end || X <- Seq}, ordered),
+    M6 = maps:iterator(#{X*2 => X*4 || X <- Seq}, ordered),
+    [] = [X || {{X,{ok,X}}, {_,X}} <- lists:zip(maps:to_list(M5), maps:to_list(M6))],
+    [] = [X || X := {ok,X} <- M5 && _ := X <- M6],
+    [] = [X || X := {e,X} <- M5 && X := {ok,X} <- M5],
+    ok.
+
+filter_guard(Config) when is_list(Config) ->
+    [[1,2,1]] = [X++Y || X <- [[1,2], [2,-3]] && Y <- [[1], [2]], lists:sum(X)>0],
+    [{a,2}, {b,4}, {c,6}] = [{X,Y} || X <- [a,b,c] && <<Y>> <= <<2,4,6>>,
+                                      Y rem 2 == 0],
+    [{b,4}, {c,6}] = [{X, Y} || X <- [a,b,c] && <<Y>> <= <<2,4,6>>, Y =/= 2],
+    [] = [{X,Y} || X <- [a, b, c] && <<Y>> <= <<2,4,6>>, Y rem 2 == 1],
+    [{b,4}] = [{X,Y} || <<Y>> <= <<2,4,6>> && X <- [a,b,c], X>a, X<c],
+    [{b,d,2}] = [{K1,K2,V1} || K1 := V1 <- maps:iterator(#{a=>1, b=>2}, ordered) &&
+                                   K2 := V2 <- maps:iterator(#{c=>3, d=>4}, ordered),
+                               V2 rem 2 == 0],
+    <<5>> = << <<(X+Y)/integer>> || X <- [1,2,3] && Y <- [2,2,2], X rem 2 == 1, X+Y>4>>,
+    #{c := 3,a := 1} = #{X => Y || X <- [a,b,c] && Y <- [1,2,3], Y rem 2 == 1},
+    #{c := 3} = #{X => Y || X <- [a,b,c] && Y <- [1,2,3], Y rem 2 == 1, Y > 1},
+    #{c := 3,a := 1} = #{X => Y || X <- [a,b,c] && <<Y>> <= <<1,2,3>>, Y rem 2 == 1}.
+
+filter_pattern(Config) when is_list(Config) ->
+    [] = do_filter_pat_1([], []),
+    [] = do_filter_pat_1([a], [a]),
+    [] = do_filter_pat_1([{ok,a}], [{error,e}]),
+
+    [] = do_filter_pat_2([], []),
+    [] = do_filter_pat_2([a], [b]),
+    [] = do_filter_pat_2([{a,1}], [{b,1}]),
+    [{1,7}] = do_filter_pat_2([{a,1}], [{a,7}]),
+    [{1,7},{10,20}] = do_filter_pat_2([{a,1},{b,9},{x,10}],
+                                      [{a,7},{wrong,8},{x,20}]),
+
+    ok.
+
+do_filter_pat_1(L1, L2) ->
+    Res = [{A,B} || {ok,A} <- L1 && {ok,B} <- L2],
+    Res = [{A,B} || {{ok,A},{ok,B}} <- lists:zip(L1,L2)],
+    Res.
+
+do_filter_pat_2(L1, L2) ->
+    Res = [{A,B} || {Same,A} <- L1 && {Same,B} <- L2],
+    Res = [{A,B} || {{Same,A},{Same,B}} <- lists:zip(L1,L2)],
+    Res.
+
+cartesian(Config) when is_list(Config) ->
+    [{a,3}, {b,5}, {c,7}, {a,4}, {b,6}, {c,8}] =
+        [{X, W+Y} || W <- [1,2],
+                     X <- [a,b,c] && <<Y>> <= <<2,4,6>>],
+    [{a,3}, {a,4}, {b,5}, {b,6}, {c,7}, {c,8}] =
+        [{X, W+Y} || X <- [a,b,c] &&
+                         <<Y>> <= <<2,4,6>>, W <- [1,2]],
+    [{a,4}, {b,6}, {c,8}] =
+        [{X, W+Y} || X <- [a,b,c] &&
+                         <<Y>> <= <<2,4,6>>, W <- [1,2], (W + Y) rem 2 == 0],
+    <<4,2,5,3,6,4>> = << <<(X+V1+Y)/integer>> ||
+                          X <- [1,2,3] &&
+                              _K1 := V1 <- maps:iterator(#{a=>2, b=>2, c=>2}, ordered),
+                          <<Y>> <= <<1,-1>> >>,
+    ok.
+
+strict_list(Config) when is_list(Config) ->
+    Seq100 = lists:seq(1, 100),
+
+    [2,3,4] = [X+Y || X <:- [1,2,3] && Y <- [1,1,1]],
+    [3,4] = [X+Y || X <:- [1,2,3] && Y <:- [1,1,1], X > 1],
+
+    [] = strict_list_mixed_1([], []),
+    [11,22] = strict_list_mixed_1([{i,1},{i,2}], [{i,10},{i,20}]),
+    [13,25] = strict_list_mixed_1([{i,3},{i,4},{i,5}], [{i,10},bad,{i,20}]),
+    {'EXIT',{{bad_generators,{[bad,{i,5}],[{i,15},{i,20}]}},_}} =
+        catch strict_list_mixed_1([{i,3},bad,{i,5}], [{i,10},{i,15},{i,20}]),
+    {'EXIT',{{bad_generators,{[{i,5}],[]}},_}} =
+        catch strict_list_mixed_1([{i,3},{i,5}], [bad]),
+
+    [] = strict_list_mixed_2([], #{}),
+    [15] = strict_list_mixed_2([{i,3}], #{{k,4} => {v,3}}),
+    [15] = strict_list_mixed_2([{i,0},{i,3}], #{{a,0} => {a,0},
+                                                {k,4} => {v,3}}),
+
+    ?assertEqual([I * 3*I + 7*I || I <- Seq100],
+                 strict_list_mixed_2([{i,I} || I <- Seq100],
+                                     #{{k,3*I} => {v,7*I} || I <- Seq100})),
+    SimpleMap = #{{k,1} => {v,2}},
+    {'EXIT',{{bad_generators,{[{a,3}],{{k,1},{v,2},none}}},_}} =
+        catch strict_list_mixed_2([{a,3}], SimpleMap),
+    {'EXIT',{{bad_generators,{[],{{k,1},{v,2},none}}},_}} =
+        catch strict_list_mixed_2([], SimpleMap),
+
+    [] = strict_list_strict_1([], []),
+    [11,22] = strict_list_strict_1([{i,1},{i,2}], [{i,10},{i,20}]),
+    {'EXIT',{{bad_generators,{[bad,{i,5}],[{i,15},{i,20}]}},_}} =
+        catch strict_list_strict_1([{i,3},bad,{i,5}], [{i,10},{i,15},{i,20}]),
+    {'EXIT',{{bad_generators,{[{i,4},{i,5}],[{wrong_tag,7},{i,20}]}},_}} =
+        catch strict_list_strict_1([{i,3},{i,4},{i,5}], [{i,10},{wrong_tag,7},{i,20}]),
+    {'EXIT',{{bad_generators,{[{a,b,c},{i,5}],[{wrong_tag,7},{i,20}]}},_}} =
+        catch strict_list_strict_1([{i,3},{a,b,c},{i,5}], [{i,10},{wrong_tag,7},{i,20}]),
+    {'EXIT',{{bad_generators,{[{i,5}],[]}},_}} =
+        catch strict_list_strict_1([{i,3},{i,5}], [{i,7}]),
+
+    [] = strict_list_strict_2([], [], <<>>),
+    [5,23] = strict_list_strict_2([{i,1},{i,2}], [{i,2},{i,7}], <<3,9>>),
+    ?assertEqual([2*I * 3*I + I || I <- Seq100],
+                 strict_list_strict_2([{i,2*I} || I <- Seq100],
+                                      [{i,3*I} || I <- Seq100],
+                                      list_to_binary(Seq100))),
+    {'EXIT',{{bad_generators,{[{i,2}],[{i,7}],<<9:7>>}},_}} =
+        catch strict_list_strict_2([{i,1},{i,2}], [{i,2},{i,7}], <<3,9:7>>),
+    {'EXIT',{{bad_generators,{[],[],[]}},_}} =
+        catch strict_list_strict_2([], [], []),
+    {'EXIT',{{bad_generators,{[{i,0}],[],<<>>}},_}} =
+        catch strict_list_strict_2([{i,0}], [], <<>>),
+    {'EXIT',{{bad_generators,{[{i,0}],[{bad,5}],<<99>>}},_}} =
+        catch strict_list_strict_2([{i,0}], [{bad,5}], <<99>>),
+    {'EXIT',{{bad_generators,{[{i,20}],[{i,21}],<<42:7>>}},_}} =
+        catch strict_list_strict_2([{i,20}], [{i,21}], <<42:7>>),
+
+    [] = strict_list_strict_3([], <<>>),
+    [45] = strict_list_strict_3([{i,42}], <<3>>),
+    {'EXIT',{{bad_generators,{[],<<2>>}},_}} =
+        catch strict_list_strict_3([{i,1}], <<1,2>>),
+    {'EXIT',{{bad_generators,{[],<<0:7>>}},_}} =
+        catch strict_list_strict_3([], <<0:7>>),
+    {'EXIT',{{bad_generators,{[{i,1}],<<0:7>>}},_}} =
+        catch strict_list_strict_3([{i,1}], <<0:7>>),
+
+    [] = strict_list_strict_4([], <<>>),
+    [100] = strict_list_strict_4([{i,100}], <<42>>),
+    {'EXIT',{{bad_generators,{[{i,100}],<<0>>}},_}} =
+        catch strict_list_strict_4([{i,100}], <<0>>),
+    {'EXIT',{{bad_generators,{[{i,100}],<<>>}},_}} =
+        catch strict_list_strict_4([{i,100}], <<>>),
+    {'EXIT',{{bad_generators,{[{i,100}],<<0:8,1:1>>}},_}} =
+        catch strict_list_strict_4([{i,100}], <<0:8,1:1>>),
+
+    NaN = <<-1:64>>,
+    [] = strict_list_5(<<>>, <<>>),
+    [3.14] = strict_list_5(<<0:1,1:1>>, <<32,0.0:32/float, 64,3.14:64/float>>),
+    [0.0,3.14] = strict_list_5(<<1:1,1:1>>, <<32,0.0:32/float, 64,3.14:64/float>>),
+    {'EXIT',{{bad_generators,{<<>>,<<64,42.0/float>>}},_}} =
+        catch strict_list_5(<<>>, <<64,42.0/float>>),
+    {'EXIT',{{bad_generators,{<<0:1,1:1>>,
+                              <<117,-1:117/signed,32,17.0:32/float>>}},_}} =
+        catch strict_list_5(<<0:1,1:1>>, <<117,-1:117, 32,17.0:32/float>>),
+    {'EXIT',{{bad_generators,{<<0:1>>,<<64,NaN/binary>>}},_}} =
+        catch strict_list_5(<<1:1,0:1>>, <<32,42.0:32/float, 64,NaN/binary>>),
+    {'EXIT',{{bad_generators,{<<1:1>>,<<64,NaN/binary>>}},_}} =
+        catch strict_list_5(<<1:1,1:1>>, <<32,42.0:32/float, 64,NaN/binary>>),
+
+    ok.
+
+strict_list_mixed_1(X, Y) ->
+    [A + B || {i,A} <:- X && {i,B} <- Y].
+
+strict_list_mixed_2(L, Map0) ->
+    Map = maps:iterator(Map0, ordered),
+    [A * B + C || {i,A} <:- L && {k,B} := {v,C} <- Map].
+
+strict_list_strict_1(X, Y) ->
+    [A + B || {i,A} <:- X && {i,B} <:- Y].
+
+strict_list_strict_2(X, Y, Z) ->
+    [A * B + C || {i,A} <:- X && {i,B} <:- Y && <<C:8>> <:= Z].
+
+strict_list_strict_3(List, Bin) ->
+    [A + B || {i,A} <:- List && <<B:8>> <:= Bin].
+
+strict_list_strict_4(List, Bin) ->
+    [A || {i,A} <:- List && <<42:8>> <:= Bin].
+
+strict_list_5(Wanted, Floats) ->
+    Res = [F || <<W:1>> <:= Wanted && <<Size:8,F:Size/float>> <:= Floats, W =:= 1],
+    Res = [F || <<1:1>> <= Wanted && <<Size:8,F:Size/float>> <:= Floats],
+    Res.
+
+strict_binary(Config) when is_list(Config) ->
+    Seq100 = lists:seq(1, 100),
+
+    <<2,4,6>> = << <<(X+Y)>> || X <:- [1,2,3] && <<Y>> <= <<1,2,3>>>>,
+    <<2,4>> = << <<(X+Y)>> || <<X>> <:= <<1,2,3>> && {X, Y} <- [{1,1},{2,2},{2,3}]>>,
+    <<2,24>> = << <<(X*Y*Z)>> || X := Y <:- #{1 => 2, 3 => 4} && <<Z>> <:= <<1,2>> >>,
+
+    <<>> = strict_binary_1(#{}, <<>>),
+    <<24:64>> = strict_binary_1(#{2 => {val,3}}, <<4:8>>),
+    ?assertEqual(<< <<(5*I * 3*I * I):64>> || I <- Seq100 >>,
+                 strict_binary_1(maps:iterator(#{5*I => {val,3*I} || I <- Seq100}, ordered),
+                                 list_to_binary(Seq100))),
+    {'EXIT',{{bad_generators,{none,<<42:8>>}},_}} = catch strict_binary_1(#{}, <<42:8>>),
+    {'EXIT',{{bad_generators,{none,<<42:7>>}},_}} = catch strict_binary_1(#{}, <<42:7>>),
+    {'EXIT',{{bad_generators,{none,<<0:4>>}},_}} = catch strict_binary_1(#{2 => {val,3}}, <<0,0:4>>),
+
+    <<>> = strict_binary_mixed_1(<<>>, #{}, #{}),
+    <<>> = strict_binary_mixed_1(<<1:2>>, #{}, #{}),
+    <<999:64>> = strict_binary_mixed_1(<<1:1>>, #{0 => {v,0}}, #{1 => {v,999}}),
+    ?assertEqual(<< <<I:64>> || I <- Seq100>>,
+                 strict_binary_mixed_1(<<0:100>>,
+                                       #{I => {v,I} || I <- Seq100},
+                                       #{I => {v,-I} || I <- Seq100})),
+    ?assertEqual(<< <<-I:64>> || I <- Seq100>>,
+                 strict_binary_mixed_1(<<-1:100>>,
+                                       #{I => {v,I} || I <- Seq100},
+                                       #{I => {v,-I} || I <- Seq100})),
+    {'EXIT',{{bad_generators,{<<0:1>>,{0,0,none},{0,{v,7},none}}},_}} =
+        catch strict_binary_mixed_1(<<0:1>>, #{0 => 0}, #{0 => {v,7}}),
+
+    Island = ~"skärgårdsö",
+    IslandSeq = lists:seq(1, length([C || <<C/utf8>> <= Island])),
+    ?assertEqual(<< <<I:8,C:32>> ||
+                     {I,C} <:- lists:zip(IslandSeq, [C || <<C/utf8>> <= Island]) >>,
+                 strict_binary_utf8(IslandSeq, Island)),
+    {'EXIT',{{bad_generators,{[4,5,6,7,8],<<16#ff,16#ff,"def">>}},_}} =
+        catch strict_binary_utf8(lists:seq(1, 8), <<"abc",16#ff,16#ff,"def">>),
+
+    ok.
+
+strict_binary_1(Map, Bin) ->
+    << <<(X*Y*Z):64>> || X := {val,Y} <:- Map && <<Z:8>> <:= Bin >>.
+
+strict_binary_utf8(List, Bin) ->
+    << <<I:8,C:32>> || I <:- List && <<C/utf8>> <:= Bin >>.
+
+strict_binary_mixed_1(Bin, MapA0, MapB0) ->
+    MapA = maps:iterator(MapA0, ordered),
+    MapB = maps:iterator(MapB0, ordered),
+    <<case N of
+          0 -> <<V1:64>>;
+          1 -> <<V2:64>>
+      end || <<N:1>> <= Bin && _ := {v,V1} <:- MapA && _ := {v,V2} <- MapB>>.
+
+nomatch(Config) when is_list(Config) ->
+    [] = do_nomatch_1([], []),
+    [] = do_nomatch_1([1], [a]),
+    [] = do_nomatch_1([1,2], [a,b]),
+    {'EXIT',{{bad_generators,{[1,2,3],[]}},_}} = do_nomatch_1([1,2,3], []),
+    {'EXIT',{{bad_generators,{[3],[]}},_}} = do_nomatch_1([1,2,3], [a,b]),
+
+    <<>> = do_nomatch_2([], <<>>),
+    <<>> = do_nomatch_2([a], <<1>>),
+    {'EXIT',{{bad_generators,{[2],<<>>}},_}} = do_nomatch_2([1,2], <<3>>),
+    ok.
+
+do_nomatch_1(L1, L2) ->
+    catch [{X, Y} || a=b=X <- L1 && Y <- L2].
+
+do_nomatch_2(L, Bin) ->
+    catch << <<(X+Y)/integer>> || a=b=X <- L && <<Y>> <= Bin >>.
+
+bad_generators(Config) when is_list(Config) ->
+    {'EXIT',{{bad_generators,{x,[1,2]}},_}} =
+        catch [{X,Y} || X <- x && Y <- [1,2]],
+    {'EXIT',{{bad_generators,{[],[4]}},_}} =
+        catch [{X,Y} || X <- [1,2,3] && Y <- [1,2,3,4]],
+    {'EXIT',{{bad_generators,{[3,4],[]}},_}} =
+        catch [{X,Y} || X <- [1,2,3,4] && Y <- [1,2], X < 3],
+    {'EXIT',{{bad_generators,{[3,4],[]}},_}} =
+        catch << <<(X+Y)/integer>> || X <- [1,2,3,4] && Y <- [1,2], X < 3>>,
+    {'EXIT',{{bad_generators,{<<1,2>>,a}},_}} =
+        catch << <<X:16>> || <<X:16>> <= <<1:8,2:8>> && <<X:8>> <= a>>,
+    {'EXIT',{{bad_generator,a},_}} = catch [X || X := X <- a && _Y <- [1]],
+    {'EXIT',{{bad_generators,{[d],[]}},_}} =
+        catch #{X => Y || X <- [a,b,c,d] && Y <- [1,2,3], Y > 1},
+
+    %% Make sure that line numbers point out the generator.
+    case ?MODULE of
+        zlc_inline_SUITE ->
+            %% No inline suite for now. Just a guard in case we add it later.
+            ok;
+        _ ->
+            {'EXIT',{{bad_generators,{[],[4]}},
+                     [{?MODULE,_,_,
+                       [{file,"bad_zlc.erl"},{line,4}]}|_]}} =
+                catch bad_generators([1,2,3],[1,2,3,4]),
+
+            {'EXIT',{{bad_generators,{a,[2,3]}},
+                     [{?MODULE,_,_,
+                       [{file,"bad_zlc.erl"},{line,7}]}|_]}} =
+                catch bad_generators_bc(a,[2,3]),
+
+            {'EXIT',{{bad_generators,{[2],[]}},
+                     [{?MODULE,_,_,
+                       [{file,"bad_zlc.erl"},{line,10}]}|_]}} =
+                catch bad_generators_mc([1,2],[1]),
+
+            %% List comprehensions with improper lists.
+            {'EXIT',{{bad_generators,{d,[d]}},
+                     [{?MODULE,_,_,
+                       [{file,"bad_zlc.erl"},{line,4}]}|_]}} =
+                catch bad_generators([a,b,c|d],[a,b,c,d])
+    end,
+    ok.
+
+-file("bad_zlc.erl", 1).
+bad_generators(L1,L2) ->                        %Line 2
+    [{I1, I2} ||                                %Line 3
+        I1 <- L1 && I2 <- L2].                  %Line 4
+bad_generators_bc(L1,L2) ->                     %Line 5
+    << <<I1:4,I2:4>> ||                         %Line 6
+        I1 <- L1 && I2 <- L2>>.                 %Line 7
+bad_generators_mc(L1,L2) ->                     %Line 8
+    #{I1 => I2 ||                               %Line 9
+        I1 <- L1 && I2 <- L2}.                  %Line 10
diff --git a/lib/debugger/src/dbg_ieval.erl b/lib/debugger/src/dbg_ieval.erl
index 6011dd0c97..3681edcc66 100644
--- a/lib/debugger/src/dbg_ieval.erl
+++ b/lib/debugger/src/dbg_ieval.erl
@@ -1112,6 +1112,9 @@ eval_named_fun(As, RF, {Info,Bs,Cs,FName}) ->
 eval_lc(E, Qs, Bs, Ieval) ->
     {value,eval_lc1(E, Qs, Bs, Ieval),Bs}.
 
+eval_lc1(E, [{zip, Anno, Gens}|Qs], Bs0, Ieval) ->
+    {VarList, Bs1} = convert_gen_values(Gens, [], Bs0, Ieval),
+    eval_zip(E, [{zip, Anno, VarList}|Qs], Bs1, fun eval_lc1/4, Ieval);
 eval_lc1(E, [{generator,G}|Qs], Bs, Ieval) ->
     CompFun = fun(NewBs) -> eval_lc1(E, Qs, NewBs, Ieval) end,
     eval_generator(G, Bs, CompFun, Ieval);
@@ -1130,6 +1133,238 @@ eval_lc1(E, [], Bs, Ieval) ->
     {value,V,_} = expr(E, Bs, Ieval#ieval{top=false}),
     [V].
 
+%% convert values for generator vars from abstract form to flattened lists
+convert_gen_values([{generator,{Generate, Line, P, L0}}|Qs], Acc, Bs0, Ieval0)
+  when Generate =:= generate;
+       Generate =:= generate_strict ->
+    Ieval = Ieval0#ieval{line=Line},
+    {value,L1,_Bs1} = expr(L0, Bs0, Ieval#ieval{top=false}),
+    convert_gen_values(Qs, [{Generate, Line, P, L1}|Acc], Bs0, Ieval);
+convert_gen_values([{generator,{Generate, Line, P, L0}}|Qs], Acc, Bs0, Ieval0)
+  when Generate =:= b_generate;
+       Generate =:= b_generate_strict ->
+    Ieval = Ieval0#ieval{line=Line},
+    {value,L1,_Bs1} = expr(L0, Bs0, Ieval#ieval{top=false}),
+    convert_gen_values(Qs, [{Generate, Line, P, L1}|Acc], Bs0, Ieval);
+convert_gen_values([{generator,{Generate, Line, P, Map0}}|Qs], Acc, Bs0, Ieval0)
+    when Generate =:= m_generate;
+       Generate =:= m_generate_strict ->
+    Ieval = Ieval0#ieval{line=Line},
+    {map_field_exact,_,K,V} = P,
+    {value,Map,_Bs1} = expr(Map0, Bs0, Ieval#ieval{top=false}),
+    Iter = case is_map(Map) of
+               true ->
+                   maps:iterator(Map);
+               false ->
+                   %% Validate iterator.
+                   try maps:foreach(fun(_, _) -> ok end, Map) of
+                       _ ->
+                           Map
+                   catch
+                       _:_ ->
+                           exception(error,{bad_generator,Map}, Bs0, Ieval)
+                   end
+           end,
+    convert_gen_values(Qs, [{Generate, Line, {tuple, Line, [K, V]}, Iter}|Acc], Bs0, Ieval);
+convert_gen_values([], Acc, Bs0, _Ieval) ->
+    {lists:reverse(Acc), Bs0}.
+
+bind_all_generators(Gens, Bs0, Ieval) ->
+    bind_all_generators1(Gens, [], erl_eval:new_bindings(Bs0), Ieval, continue).
+
+bind_all_generators1([{Generate, Anno, P, <<_/bitstring>>=Bin}|Qs],
+                     Acc, Bs0, Ieval, continue)
+  when Generate =:= b_generate;
+       Generate =:= b_generate_strict ->
+    Mfun = match_fun(Bs0),
+    Efun = fun(Exp, Bs) -> expr(Exp, Bs, #ieval{}) end,
+    case eval_bits:bin_gen(P, Bin, erl_eval:new_bindings(Bs0), Bs0, Mfun, Efun) of
+        {match, Rest, Bs1} ->
+            Bs2 = zip_add_bindings(Bs1, Bs0),
+            case Bs2 of
+                nomatch when Generate =:= b_generate ->
+                    bind_all_generators1(Qs, [{b_generate, Anno, P, Rest}|Acc],
+                                         Bs0, Ieval, skip);
+                nomatch -> {Acc, error};
+                _ ->
+                    bind_all_generators1(Qs, [{Generate, Anno, P, Rest}|Acc],
+                                         Bs2, Ieval, continue)
+            end;
+        {nomatch, Rest} when Generate =:= b_generate ->
+            bind_all_generators1(Qs, [{b_generate, Anno, P, Rest}|Acc], Bs0, Ieval, skip);
+        {nomatch, _Rest} ->
+            {Acc, error};
+        done when Generate =:= b_generate_strict, Bin =/= <<>> ->
+            {Acc, error};
+        done ->
+            {[], done}
+    end;
+bind_all_generators1([{Generate, Anno, P, <<_/bitstring>>=Bin}|Qs], Acc, Bs0, Ieval, skip)
+  when Generate =:= b_generate;
+       Generate =:= b_generate_strict ->
+    Mfun = match_fun(Bs0),
+    Efun = fun(Exp, Bs) -> expr(Exp, Bs, #ieval{}) end,
+    case eval_bits:bin_gen(P, Bin, erl_eval:new_bindings(Bs0), Bs0, Mfun, Efun) of
+        {match, Rest, _} ->
+            bind_all_generators1(Qs, [{Generate, Anno, P, Rest}|Acc], Bs0, Ieval, skip);
+        {nomatch, Rest} when Generate =:= b_generate ->
+            bind_all_generators1(Qs, [{b_generate, Anno, P, Rest}|Acc], Bs0, Ieval, skip);
+        {nomatch, _Rest} ->
+            {Acc, error};
+        done when Generate =:= b_generate_strict, Bin =/= <<>> ->
+            {Acc, error};
+        done ->
+            {[], skip}
+    end;
+bind_all_generators1([{Generate, Anno, P, [H|T]}|Qs], Acc, Bs0, Ieval, continue)
+  when Generate =:= generate;
+       Generate =:= generate_strict ->
+    case catch match1(P, H, erl_eval:new_bindings(Bs0), Bs0) of
+        {match,Bsn} ->
+            Bs2 = zip_add_bindings(Bsn, Bs0),
+            case Bs2 of
+                nomatch when Generate =:= generate ->
+                    bind_all_generators1(Qs,[{generate, Anno, P, T}|Acc], Bs0, Ieval, skip);
+                nomatch -> {Acc, error};
+                _ ->
+                    bind_all_generators1(Qs,[{Generate, Anno, P, T}|Acc], Bs2, Ieval, continue)
+            end;
+        nomatch when Generate =:= generate ->
+            %% match/6 returns nomatch. Skip this value
+            bind_all_generators1(Qs,[{generate, Anno, P, T}|Acc], Bs0, Ieval, skip);
+        nomatch ->
+            {Acc, error}
+    end;
+bind_all_generators1([{generate, Anno, P, [_H|T]}|Qs], Acc, Bs0, Ieval, skip) ->
+    bind_all_generators1(Qs,[{generate, Anno, P, T}|Acc], Bs0, Ieval, skip);
+bind_all_generators1([{generate_strict, Anno, P, [H|T]}|Qs], Acc, Bs0, Ieval, continue) ->
+    case catch match1(P, H, erl_eval:new_bindings(Bs0), Bs0) of
+        {match,Bsn} ->
+            Bs2 = zip_add_bindings(Bsn, Bs0),
+            case Bs2 of
+                nomatch -> {Acc, error};
+                _ ->
+                    bind_all_generators1(Qs,[{generate_strict, Anno, P, T}|Acc], Bs2, Ieval, continue)
+            end;
+        nomatch ->
+            {Acc, error}
+    end;
+bind_all_generators1([{Generate, Anno, P, Iter0}|Qs], Acc, Bs0, Ieval, continue)
+  when Generate =:= m_generate;
+       Generate =:= m_generate_strict ->
+    case maps:next(Iter0) of
+        {K,V,Iter} ->
+            case catch match1(P, {K,V}, erl_eval:new_bindings(Bs0), Bs0) of
+                {match,Bsn} ->
+                    Bs2 = zip_add_bindings(Bsn, Bs0),
+                    case Bs2 of
+                        nomatch when Generate =:= m_generate ->
+                            bind_all_generators1(Qs,[{m_generate, Anno, P, Iter}|Acc],
+                                                 Bs0, Ieval, skip);
+                        nomatch ->
+                            {Acc, error};
+                        _ ->
+                            bind_all_generators1(Qs,[{Generate, Anno, P, Iter}|Acc],
+                                                 Bs2, Ieval, continue)
+                    end;
+                nomatch when Generate =:= m_generate ->
+                    bind_all_generators1(Qs, [{m_generate, Anno, P, Iter}|Acc],
+                                         Bs0, Ieval, skip);
+                nomatch ->
+                    {Acc, error}
+            end;
+        none ->
+            {[], done}
+    end;
+bind_all_generators1([{m_generate, Anno, P, Iter0}|Qs], Acc, Bs0, Ieval, skip) ->
+    case maps:next(Iter0) of
+        {_K,_V,Iter} ->
+            bind_all_generators1(Qs, [{m_generate, Anno, P, Iter}|Acc],
+                                 Bs0, Ieval, skip);
+        none ->
+            {[], skip}
+    end;
+bind_all_generators1([{m_generate_strict, Anno, P, Iter0}|Qs], Acc, Bs0, Ieval, continue) ->
+    case maps:next(Iter0) of
+        {K,V,Iter} ->
+            case catch match1(P, {K,V}, erl_eval:new_bindings(Bs0), Bs0) of
+                {match,Bsn} ->
+                    Bs2 = zip_add_bindings(Bsn, Bs0),
+                    case Bs2 of
+                        nomatch ->
+                            {Acc, error};
+                        _ ->
+                            bind_all_generators1(Qs,[{m_generate_strict, Anno, P, Iter}|Acc],
+                                                 Bs2, Ieval, continue)
+                    end;
+                nomatch -> {Acc, error}
+            end;
+        none ->
+            {[], done}
+    end;
+bind_all_generators1([{generate,_,_,[]}|_], _, _, _, _) ->
+    %% no more values left for a var, time to return
+    {[],done};
+bind_all_generators1([{generate_strict,_,_,[]}|_], _, _, _, _) ->
+    %% no more values left for a var, time to return
+    {[],done};
+bind_all_generators1([{Generate, _Anno, _P, _Term}|_Qs], Acc, _Bs0, _Ieval,_)
+  when Generate =:= generate;
+       Generate =:= generate_strict;
+       Generate =:= b_generate;
+       Generate =:= b_generate_strict ->
+    {Acc, error};
+bind_all_generators1([], [_H|_T] = Acc, Bs0, _Ieval, continue) ->
+    %% all vars are bind for this round
+    {Acc, Bs0};
+bind_all_generators1([], [_H|_T] = Acc, _Bs0, _Ieval, skip) ->
+    {Acc, skip}.
+
+check_bad_generators([{Generate,_,_,V}|T], Env, Acc)
+  when Generate =:= generate;
+       Generate =:= generate_strict ->
+    check_bad_generators(T, Env, [V|Acc]);
+check_bad_generators([{Generate,_,_,Iter0}|T], Env, Acc)
+  when Generate =:= m_generate;
+       Generate =:= m_generate_strict ->
+    case maps:next(Iter0) of
+        none -> check_bad_generators(T, Env, [#{}|Acc]);
+        _ -> check_bad_generators(T, Env, [#{K => V || K := V <- Iter0}|Acc])
+    end;
+check_bad_generators([{Generate,_,P,<<_/bitstring>>=Bin}|T], Bs0, Acc)
+  when Generate =:= b_generate;
+       Generate =:= b_generate_strict ->
+    Mfun = match_fun(Bs0),
+    Efun = fun(Exp, Bs) -> expr(Exp, Bs, #ieval{}) end,
+    case eval_bits:bin_gen(P, Bin, erl_eval:new_bindings(Bs0), Bs0, Mfun, Efun) of
+        done ->
+            check_bad_generators(T, Bs0, [<<>>|Acc]);
+        _ ->
+            check_bad_generators(T, Bs0, [Bin|Acc])
+    end;
+check_bad_generators([{b_generate,_,_,Term}|T], Env, Acc) ->
+    check_bad_generators(T, Env, [Term|Acc]);
+check_bad_generators([{b_generate_strict,_,_,Term}|T], Env, Acc) ->
+    check_bad_generators(T, Env, [Term|Acc]);
+check_bad_generators([], _, Acc)->
+    case lists:any(fun is_generator_end/1, Acc) of
+        false ->
+            %% None of the generators has reached its end.
+            {ok, list_to_tuple(lists:reverse(Acc))};
+        true ->
+            case lists:all(fun(V) -> is_generator_end(V) end, Acc) of
+                true ->
+                    %% All generators have reached their end.
+                    {ok, list_to_tuple(lists:reverse(Acc))};
+                false ->
+                    {error, {bad_generators,list_to_tuple(lists:reverse(Acc))}}
+            end
+    end.
+
+is_generator_end([]) -> true;
+is_generator_end(<<>>) -> true;
+is_generator_end(Other) -> Other =:= #{}.
+
 %% eval_bc(Expr,[Qualifier],Bindings,IevalState) ->
 %%	{value,Value,Bindings}.
 %% This is evaluating list comprehensions "straight out of the book".
@@ -1138,6 +1373,9 @@ eval_bc(E, Qs, Bs, Ieval) ->
     Val = erlang:list_to_bitstring(eval_bc1(E, Qs, Bs, Ieval)),
     {value,Val,Bs}.
 
+eval_bc1(E, [{zip, Anno, Gens}|Qs], Bs0, Ieval) ->
+    {VarList, Bs1} = convert_gen_values(Gens, [], Bs0, Ieval),
+    eval_zip(E, [{zip, Anno, VarList}|Qs], Bs1, fun eval_bc1/4, Ieval);
 eval_bc1(E, [{generator,G}|Qs], Bs, Ieval) ->
     CompFun = fun(NewBs) -> eval_bc1(E, Qs, NewBs, Ieval) end,
     eval_generator(G, Bs, CompFun, Ieval);
@@ -1160,6 +1398,9 @@ eval_mc(E, Qs, Bs, Ieval) ->
     Map = eval_mc1(E, Qs, Bs, Ieval),
     {value,maps:from_list(Map),Bs}.
 
+eval_mc1(E, [{zip, Anno, Gens}|Qs], Bs0, Ieval) ->
+    {VarList, Bs1} = convert_gen_values(Gens, [], Bs0, Ieval),
+    eval_zip(E, [{zip, Anno, VarList}|Qs], Bs1, fun eval_mc1/4, Ieval);
 eval_mc1(E, [{generator,G}|Qs], Bs, Ieval) ->
     CompFun = fun(NewBs) -> eval_mc1(E, Qs, NewBs, Ieval) end,
     eval_generator(G, Bs, CompFun, Ieval);
@@ -1179,6 +1420,25 @@ eval_mc1({map_field_assoc,_,K0,V0}, [], Bs, Ieval) ->
     {value,V,_} = expr(V0, Bs, Ieval#ieval{top=false}),
     [{K,V}].
 
+eval_zip(E, [{zip, Anno, VarList}|Qs], Bs0, Fun, Ieval) ->
+    Gens = case check_bad_generators(VarList, Bs0, []) of
+               {ok, Acc} -> Acc;
+               {error, Reason} ->
+                   exception(error, Reason, Bs0, Ieval)
+           end,
+    {Rest, Bs1} = bind_all_generators(VarList, Bs0, Ieval),
+    case {Rest, Qs, Bs1} of
+        {_, _, error} -> exception(error,{bad_generators,Gens}, Bs0, Ieval);
+        {[], [], _} -> [];
+        {[], _, _} -> [];
+        {_,_,done} -> [];
+        {_, _, skip} ->
+            eval_zip(E, [{zip, Anno, lists:reverse(Rest)}|Qs], Bs0, Fun, Ieval);
+        {_, _, _} ->
+            Fun(E, Qs, add_bindings(Bs1, Bs0), Ieval) ++
+                eval_zip(E, [{zip, Anno, lists:reverse(Rest)}|Qs], Bs0, Fun, Ieval)
+    end.
+
 eval_generator({Generate,Line,P,L0}, Bs0, CompFun, Ieval0) when Generate =:= generate;
                                                                 Generate =:= generate_strict ->
     Ieval = Ieval0#ieval{line=Line},
@@ -1818,6 +2078,31 @@ merge_bindings([{Name,V}|B1s], B2s, Ieval) ->
 merge_bindings([], B2s, _Ieval) ->
     B2s.
 
+zip_add_bindings(Bs1, Bs2) when is_map(Bs1), is_map(Bs2) ->
+    zip_add_bindings_map(maps:keys(Bs1), Bs1, Bs2);
+zip_add_bindings(Bs1, Bs2) when is_list(Bs1), is_list(Bs2) ->
+    zip_add_bindings1(orddict:to_list(Bs1), Bs2).
+
+zip_add_bindings_map([Key | Keys], Bs1, Bs2) ->
+    case {Bs1, Bs2} of
+        {#{Key := Same}, #{Key := Same}} -> zip_add_bindings_map(Keys, Bs1, Bs2);
+        {_, #{Key := _}} -> nomatch;
+        {#{Key := Value},_} -> zip_add_bindings_map(Keys, Bs1, Bs2#{Key => Value})
+    end;
+zip_add_bindings_map([], _, Bs2) ->
+    Bs2.
+
+zip_add_bindings1([{Name,Val}|Bs1], Bs2) ->
+    case orddict:find(Name, Bs2) of
+        {ok, Val} ->
+            zip_add_bindings1(Bs1, Bs2);
+        {ok, _Value} -> nomatch;
+        error ->
+            zip_add_bindings1(Bs1, orddict:store(Name, Val, Bs2))
+    end;
+zip_add_bindings1([], Bs2) ->
+    Bs2.
+
 %% add_bindings(Bindings1,Bindings2)
 %% Add Bindings1 to Bindings2. Bindings in
 %% Bindings1 hides bindings in Bindings2.
diff --git a/lib/debugger/src/dbg_iload.erl b/lib/debugger/src/dbg_iload.erl
index 597ec4e94c..accb744bf7 100644
--- a/lib/debugger/src/dbg_iload.erl
+++ b/lib/debugger/src/dbg_iload.erl
@@ -704,13 +704,31 @@ expr_comprehension({Tag,Anno,E0,Gs0}, St) ->
                   {generator,{m_generate,L,mc_pattern(P0, St),expr(Qs, false, St)}};
               ({m_generate_strict,L,P0,Qs}) -> %OTP 26
                   {generator,{m_generate_strict,L,mc_pattern(P0, St),expr(Qs, false, St)}};
+              ({zip,L,Gens}) ->
+                  expr_comprehension({zip,L,Gens}, St);
               (Expr) ->
                   case is_guard_test(Expr, St) of
                       true -> {guard,guard([[Expr]], St)};
                       false -> expr(Expr, false, St)
                   end
           end || G <- Gs0],
-    {Tag,ln(Anno),expr(E0, false, St),Gs}.
+    {Tag,ln(Anno),expr(E0, false, St),Gs};
+expr_comprehension({zip,Anno,Gens}, St) ->
+    Gs = [case G of
+              ({generate,L,P0,Qs}) ->
+                  {generator,{generate,L,pattern(P0, St),expr(Qs, false, St)}};
+              ({generate_strict,L,P0,Qs}) ->
+                  {generator,{generate_strict,L,pattern(P0, St),expr(Qs, false, St)}};
+              ({b_generate,L,P0,Qs}) -> %R12.
+                  {generator,{b_generate,L,pattern(P0, St),expr(Qs, false, St)}};
+              ({b_generate_strict,L,P0,Qs}) -> %R12.
+                  {generator,{b_generate_strict,L,pattern(P0, St),expr(Qs, false, St)}};
+              ({m_generate,L,P0,Qs}) -> %OTP 26
+                  {generator,{m_generate,L,mc_pattern(P0, St),expr(Qs, false, St)}};
+              ({m_generate_strict,L,P0,Qs}) -> %OTP 26
+                  {generator,{m_generate_strict,L,mc_pattern(P0, St),expr(Qs, false, St)}}
+          end || G <- Gens],
+    {zip,ln(Anno),Gs}.
 
 mc_pattern({map_field_exact,L,KeyP0,ValP0}, St) ->
     KeyP1 = pattern(KeyP0, St),
diff --git a/lib/debugger/src/debugger.app.src b/lib/debugger/src/debugger.app.src
index 981e91f73b..56b80b6c84 100644
--- a/lib/debugger/src/debugger.app.src
+++ b/lib/debugger/src/debugger.app.src
@@ -49,5 +49,5 @@
   {registered, [dbg_iserver, dbg_wx_mon, dbg_wx_winman]},
   {applications, [kernel, stdlib, compiler]},
   {optional_applications, [compiler, wx]},
-  {runtime_dependencies, ["wx-2.0","stdlib-3.15","kernel-10.0","erts-15.0",
+  {runtime_dependencies, ["wx-2.0","stdlib-6.2","kernel-10.0","erts-15.0",
 			  "compiler-8.0"]}]}.
diff --git a/lib/debugger/test/Makefile b/lib/debugger/test/Makefile
index bf464f8d76..4ecb3652ed 100644
--- a/lib/debugger/test/Makefile
+++ b/lib/debugger/test/Makefile
@@ -53,6 +53,7 @@ MODULES= \
 	record_SUITE \
 	trycatch_SUITE \
 	test_lib \
+	zlc_SUITE \
 	cleanup
 
 ERL_FILES= $(MODULES:%=%.erl)
diff --git a/lib/debugger/test/erl_eval_SUITE.erl b/lib/debugger/test/erl_eval_SUITE.erl
index 4977b33ee7..bfff31ef01 100644
--- a/lib/debugger/test/erl_eval_SUITE.erl
+++ b/lib/debugger/test/erl_eval_SUITE.erl
@@ -29,6 +29,9 @@
 	 pattern_expr/1,
          guard_3/1, guard_4/1,
          lc/1,
+         zlc/1,
+         zbc/1,
+         zmc/1,
          simple_cases/1,
          unary_plus/1,
          apply_atom/1,
@@ -64,7 +67,8 @@ suite() ->
 
 all() -> 
     [guard_1, guard_2, match_pattern, string_plusplus,
-     pattern_expr, match_bin, guard_3, guard_4, lc,
+     pattern_expr, match_bin, guard_3, guard_4,
+     lc, zlc, zbc, zmc,
      simple_cases, unary_plus, apply_atom, otp_5269,
      otp_6539, otp_6543, otp_6787, otp_6977, otp_7550,
      otp_8133, funs, try_catch, eval_expr_5, eep37].
@@ -238,6 +242,160 @@ lc(Config) when is_list(Config) ->
 	  "[X || X <- [true,false], X].", [true]),
     ok.
 
+zlc(Config) when is_list(Config) ->
+    check(fun() ->
+                  X = 32, Y = 32, [{X, Y} || X <- [1,2,3] && Y <- [4,5,6]]
+          end,
+          "begin X = 32, Y = 32, [{X, Y} || X <- [1,2,3] && Y <- [4,5,6]] end.",
+          [{1,4},{2,5},{3,6}]),
+    check(fun() ->
+                  S1 = [x, y, z], S2 = [5, 10, 15], X = 32, Y = 32,
+                  [{X, Y} || X <- S1 && Y <- S2]
+          end,
+          "begin
+        S1 = [x, y, z], S2 = [5, 10, 15], X = 32, Y = 32,
+          [{X, Y} || X <- S1 && Y <- S2]
+          end.",
+    [{x,5}, {y,10}, {z,15}]),
+    check(fun() ->
+                  [{X, Y, K} || X <- [1,2,3] &&  Y:=K <- #{1=>a, 2=>b, 3=>c}]
+          end,
+          "begin [{X, Y, K} || X <- [1,2,3] &&  Y:=K <- #{1=>a, 2=>b, 3=>c}] end.",
+          [{1,1,a},{2,2,b},{3,3,c}]),
+    check(fun() ->
+                [{X, W+Y} || X <- [a, b, c] && <<Y>> <= <<2, 4, 6>>, W <- [1,2]]
+        end,
+        "begin [{X, W+Y} || X <- [a, b, c] && <<Y>> <= <<2, 4, 6>>, W <- [1,2]] end.",
+        [{a,3}, {a,4}, {b,5}, {b,6}, {c,7}, {c,8}]),
+    check(fun() ->
+            [{X, W+Y} || W <- [0], X <- [a, b, c] && <<Y>> <= <<2, 4, 6>>, Y<4]
+    end,
+    "begin [{X, W+Y} || W <- [0], X <- [a, b, c] && <<Y>> <= <<2, 4, 6>>, Y<4] end.",
+    [{a,2}]),
+    check(fun() ->
+            [{X,Y}|| a=b=X <- [1,2] && Y <-[1,2]] end,
+        "begin [{X,Y}|| a=b=X <- [1,2] && Y <-[1,2]] end.",
+        []),
+    check(fun() ->
+        [{A,B,W} || {Same,W} <- [{a,1}],
+            {Same,A} <- [{a,1},{b,9},{x,10}] && {Same,B} <- [{a,7},{wrong,8},{x,20}]]
+        end,
+        "begin [{A,B,W} || {Same,W} <- [{a,1}],
+            {Same,A} <- [{a,1},{b,9},{x,10}] && {Same,B} <- [{a,7},{wrong,8},{x,20}]]
+        end.",
+        [{1,7,1},{10,20,1}]),
+    error_check("[X || X <- a && Y <- [1]].",{bad_generators,{a,[1]}}),
+    error_check("[{X,Y} || X <- a && <<Y>> <= <<1,2>>].",{bad_generators,{a,<<1,2>>}}),
+    error_check("[{X,V} || X <- a && _K := V <- #{b=>3}].",{bad_generators,{a,#{b=>3}}}),
+    error_check("begin
+        X = 32, Y = 32, [{X, Y} || X <- [1,2,3] && Y <- [4]] end.",
+    {bad_generators,{[2,3],[]}}),
+    error_check("begin
+        X = 32, Y = 32, [{X, Y} || X <- [1,2,3] && Y:=_V <- #{1=>1}] end.",
+    {bad_generators,{[2,3],#{}}}),
+    ok.
+
+zbc(Config) when is_list(Config) ->
+    check(fun() ->
+                  <<3, 4, 5>>
+          end,
+          "begin
+        X = 32, Y = 32,
+          << <<(X+Y)/integer>> || <<X>> <= <<1,2,3>> && <<Y>> <= <<2,2,2>> >>
+              end.",
+        <<3, 4, 5>>),
+    check(fun() ->
+                  <<4,5,6,5,6,7,6,7,8>>
+          end,
+          "begin
+        X = 32, Y = 32, Z = 32,
+          << <<(X+Y+Z)/integer>> || <<X>> <= <<1,2,3>> && <<Y>> <= <<2,2,2>>, Z<-[1,2,3] >>
+              end.",
+        <<4,5,6,5,6,7,6,7,8>>),
+    check(fun() ->
+                  <<4, 5, 6>>
+          end,
+          "begin
+        L1 = <<1, 2, 3>>, L2 = <<1, 1, 1>>, L3 = <<2, 2, 2>>,
+          << <<(X+Y+Z)/integer>> || <<X>> <= L1 && <<Y>> <= L2 && <<Z>> <= L3 >>
+              end.",
+    <<4, 5, 6>>),
+    check(fun() ->
+         << <<(X+Y):64>>|| a=b=X <- [1,2] && Y <- [1,2] >> end,
+        "begin << <<(X+Y):64>>|| a=b=X <- [1,2] && Y <- [1,2] >> end.",
+        <<>>),
+    check(fun() ->
+         << <<(X+Y):64>>|| a=b=X <- [1,2] && <<Y>> <= <<1,2>> >> end,
+        "begin << <<(X+Y):64>>|| a=b=X <- [1,2] && <<Y>> <= <<1,2>> >> end.",
+        <<>>),
+    check(fun() ->
+         << <<(X+V):64>>|| a=b=X <- [1,2] && _K:=V <- #{a=>1,b=>2}>> end,
+        "begin << <<(X+V):64>>|| a=b=X <- [1,2] && _K:=V <- #{a=>1,b=>2}>> end.",
+        <<>>),
+    error_check("begin << <<(X+Y):8>> || <<X:b>> <= <<1,2>> && <<Y>> <= <<1,2>> >> end.",
+        {bad_generators,{<<>>,<<1,2>>}}),
+    error_check("begin << <<X>> || <<X>> <= a && Y <- [1]>> end.",{bad_generators,{a,[1]}}),
+    error_check("begin
+        X = 32, Y = 32,
+                << <<(X+Y)/integer>> || X <- [1,2] && Y <- [1,2,3,4]>>
+                    end.",
+    {bad_generators,{[],[3,4]}}),
+    error_check("begin << <<X:8>> || X <- [1] && Y <- a && <<Z>> <= <<2>> >> end.",
+        {bad_generators,{[1], a, <<2>>}}),
+    ok.
+
+zmc(Config) when is_list(Config) ->
+    check(fun() ->
+                  [{a,b,1,3}]
+          end,
+          "begin
+        M1 = #{a=>1}, M2 = #{b=>3},
+          [{K1, K2, V1, V2} || K1 := V1 <- M1 && K2 := V2 <- M2]
+          end.",
+    [{a,b,1,3}]),
+    check(fun() ->
+                  [A * 4 || A <- lists:seq(1, 50)]
+          end,
+          "begin
+        Seq = lists:seq(1, 50),
+          M1 = maps:iterator(#{X=>X || X <- Seq}, ordered),
+          M2 = maps:iterator(#{X=>X || X <- lists:seq(1,50)}, ordered),
+          [X+Y+Z+W || X := Y <- M1 && Z := W <- M2]
+          end.",
+    [A * 4 || A <- lists:seq(1, 50)]),
+    check(fun() ->
+                  [{A, A*3, A*2, A*4} || A <- lists:seq(1, 50)]
+          end,
+          "begin
+        Seq = lists:seq(1, 50),
+          M3 = maps:iterator(#{X=>X*3 || X <- Seq}, ordered),
+          M4 = maps:iterator(#{X*2=>X*4 || X <- Seq}, ordered),
+          [{X, Y, Z, W} || X := Y <- M3 && Z := W <- M4]
+          end.",
+    [{A, A*3, A*2, A*4} || A <- lists:seq(1, 50)]),
+    check(fun() ->
+                  #{K1 => V1+V2 || K1:=V1 <- #{a=>1} && _K2:=V2 <- #{b=>3}}
+          end,
+          "begin
+        #{K1 => V1+V2 || K1:=V1 <- #{a=>1} && _K2:=V2 <- #{b=>3}}
+          end.",
+    #{a=>4}),
+    check(fun() ->
+                  #{K=>V || a := b <- #{x => y} && K := V <- #{x => y}}
+          end,
+          "begin
+        #{K=>V || a := b <- #{x => y} && K := V <- #{x => y}}
+          end.",
+    #{}),
+    error_check("begin
+        #{K1 => V1+V2 || K1:=V1 <- #{a=>1} &&
+                             _K2:=V2 <- maps:iterator(#{b=>3,c=>4}, ordered)}
+                end.",
+    {bad_generators,{#{},#{c=>4}}}),
+    error_check("begin #{X=>Y || X <- [1] && Y <- a && K1:=V1 <- #{b=>3}} end.",
+        {bad_generators,{[1], a, #{b=>3}}}),
+    ok.
+
 %% Simple cases, just to cover some code.
 simple_cases(Config) when is_list(Config) ->
     check(fun() -> A = $C end, "A = $C.", $C),
diff --git a/lib/debugger/test/zlc_SUITE.erl b/lib/debugger/test/zlc_SUITE.erl
new file mode 100644
index 0000000000..1fd30d520c
--- /dev/null
+++ b/lib/debugger/test/zlc_SUITE.erl
@@ -0,0 +1,410 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2001-2024. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%%     http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(zlc_SUITE).
+
+-export([all/0, suite/0, groups/0, init_per_suite/1, end_per_suite/1,
+         init_per_group/2,end_per_group/2,
+         init_per_testcase/2,end_per_testcase/2,
+         basic/1,mixed_zlc/1,zmc/1,filter_guard/1,
+         filter_pattern/1,cartesian/1,nomatch/1,bad_generators/1]).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("stdlib/include/assert.hrl").
+
+suite() ->
+    [{ct_hooks,[ts_install_cth]},
+     {timetrap,{minutes,1}}].
+
+all() ->
+    [basic,
+     mixed_zlc,
+     zmc,
+     filter_guard,
+     filter_pattern,
+     cartesian,
+     nomatch,
+     bad_generators].
+
+groups() ->
+    [].
+
+init_per_suite(Config) when is_list(Config) ->
+    test_lib:interpret(?MODULE),
+    true = lists:member(?MODULE, int:interpreted()),
+    Config.
+
+end_per_suite(_Config) ->
+    ok.
+
+init_per_group(_GroupName, Config) ->
+    Config.
+
+end_per_group(_GroupName, Config) ->
+    Config.
+
+
+init_per_testcase(Case, Config) when is_atom(Case), is_list(Config) ->
+    Config.
+
+end_per_testcase(Case, Config) when is_atom(Case), is_list(Config) ->
+    ok.
+
+basic(Config) when is_list(Config) ->
+    [6, 7, 8] = [X + Y + Z || X <- [1, 2, 3] && Y <- [2, 2, 2] && Z <- [3,3,3]],
+    [{1, 2, 3}, {2, 2, 3}, {3, 2, 3}] =
+        [{X, Y, Z} || X <- [1, 2, 3] && Y <- [2, 2, 2] && Z <- [3,3,3]],
+    [6, 24] = zipwith4(fun(A, B, C, D) -> (A + B + C) * D end,
+                       [1, 5], [2, 2], [0, 1], [2, 3]),
+    96 = dot([1, 2, 3, 4], [24, 12, 8, 6]),
+
+    [1, 4, 5] = ifelse([true, false, true], [1, 3, 5], [2, 4, 6]),
+
+    [8, 14, 20] = [X + Y + Z || <<X>> <= <<5, 10, 15>> && <<Y>> <= <<1, 2, 3>>
+                                    && <<Z>> <= <<2, 2, 2>>].
+
+
+zipwith4(F, As, Bs, Cs, Ds) ->
+    [F(A,B,C,D) || A <- As && B <- Bs && C <- Cs && D <- Ds].
+
+dot(Xs, Ys) ->
+    lists:sum([X*Y || X <- Xs && Y <- Ys]).
+
+ifelse(Tests, Xs, Ys) ->
+    %% Simulate R's ifelse(,,)
+    [case T of
+         true -> X;
+         false -> Y
+     end || T <- Tests && X <- Xs && Y <- Ys
+    ].
+
+mixed_zlc(Config) when is_list(Config) ->
+    [{a, 2}, {b, 4}, {c, 6}] = [{X,Y} || X <- [a,b,c] && <<Y>> <= <<2,4,6>>],
+    [{a, 2}, {b, 4}, {c, 6}] = [{X,Y} || <<Y>> <= <<2,4,6>> && X <- [a,b,c]],
+    [{a,c,1,3}, {b,d,2,4}] = [{K1,K2,V1,V2}||
+                                 K1 := V1 <- maps:iterator(#{a=>1, b=>2}, ordered) &&
+                                     K2 := V2 <- maps:iterator(#{c=>3, d=>4}, ordered)],
+    [{a,1,2}, {b,2,4}] =
+        [{K1,V1,Y} || K1 := V1 <- maps:iterator(#{a=>1, b=>2}, ordered) &&
+                          <<Y>> <= <<2,4>>],
+    [{a,1,2}, {b,2,4}] = [{K1,V1,Y} ||
+                             K1 := V1 <- maps:iterator(#{a=>1, b=>2}, ordered) &&
+                                 <<Y>> <= <<2,4>>],
+    <<3,4,5>> = << <<(X+Y)/integer>> || X <- [1,2,3] && Y <- [2,2,2]>>,
+    <<3,4,5>> = << <<(X+V1)/integer>> ||
+                    X <- [1,2,3] &&
+                        _K1 := V1 <- maps:iterator(#{a=>2, b=>2, c=>2}, ordered)>>,
+    <<3,4,5>> = << <<(X+V1)/integer>> ||
+                    <<X>> <= <<1,2,3>> &&
+                        _K1 := V1 <- maps:iterator(#{a=>2, b=>2, c=>2}, ordered)>>,
+    <<3,4,5>> = << <<(V1+V2)/integer>> ||
+                    _K1 := V1 <- maps:iterator(#{a=>1, b=>2, c=>3}, ordered) &&
+                        _K2 := V2 <- maps:iterator(#{a=>2, b=>2, c=>2}, ordered)>>,
+    #{c := 3,b := 2,a := 1} = #{X => Y || X <- [a,b,c] && Y <- [1,2,3]},
+    #{c := 3,b := 2,a := 1} = #{X => Y || X <- [a,b,c] && <<Y>> <= <<1,2,3>>},
+    ok.
+
+zmc(Config) when is_list(Config) ->
+    [{a,b,1,3}] = [{K1, K2, V1, V2} || K1 := V1 <- #{a=>1} && K2 := V2 <- #{b=>3}],
+    Seq = lists:seq(1, 50),
+    M1 = maps:iterator(#{X=>X || X <- Seq}, ordered),
+    M2 = maps:iterator(#{X=>X || X <- lists:seq(1,50)}, ordered),
+    true = [A * 4 || A <- Seq] =:=
+        [X+Y+Z+W || X := Y <- M1 && Z := W <- M2],
+    true = << <<(A * 4):64>> || A <- Seq>> =:=
+        << <<(X+Y+Z+W):64>> || X := Y <- M1 && Z := W <- M2>>,
+
+    M3 = maps:iterator(#{X=>X*3 || X <- Seq}, ordered),
+    M4 = maps:iterator(#{X*2=>X*4 || X <- Seq}, ordered),
+    true = [{A, A*3, A*2, A*4} || A <- Seq] =:=
+        [{X, Y, Z, W} || X := Y <- M3 && Z := W <- M4],
+    true = [A * 3 || A <- Seq] =:= [X+Y+Z || X := Y <- M1 && Z <- Seq],
+    true = << <<A:64, (A*3):64, (A*2):64, (A*4):64>> || A <- Seq>> =:=
+        << <<X:64, Y:64, Z:64, W:64>> || X := Y <- M3 && Z := W <- M4>>,
+    true = << <<(A*3):64>> || A <- Seq>> =:=
+        << <<(X+Y+Z):64>> || X := Y <- M1 && Z <- Seq>>,
+
+    M5 = maps:iterator(#{X =>
+                             case X rem 2 of
+                                 0 -> {ok,X};
+                                 1 -> {error,X}
+                             end || X <- Seq}, ordered),
+    M6 = maps:iterator(#{X*2 => X*4 || X <- Seq}, ordered),
+    [] = [X || {{X,{ok,X}}, {_,X}} <- lists:zip(maps:to_list(M5), maps:to_list(M6))],
+    [] = [X || X := {ok,X} <- M5 && _ := X <- M6],
+    [] = [X || X := {e,X} <- M5 && X := {ok,X} <- M5],
+    ok.
+
+filter_guard(Config) when is_list(Config) ->
+    [[1,2,1]] = [X++Y || X <- [[1,2], [2,-3]] && Y <- [[1], [2]], lists:sum(X)>0],
+    [{a,2}, {b,4}, {c,6}] = [{X,Y} || X <- [a,b,c] && <<Y>> <= <<2,4,6>>,
+                                      Y rem 2 == 0],
+    [{b,4}, {c,6}] = [{X, Y} || X <- [a,b,c] && <<Y>> <= <<2,4,6>>, Y =/= 2],
+    [] = [{X,Y} || X <- [a, b, c] && <<Y>> <= <<2,4,6>>, Y rem 2 == 1],
+    [{b,4}] = [{X,Y} || <<Y>> <= <<2,4,6>> && X <- [a,b,c], X>a, X<c],
+    [{b,d,2}] = [{K1,K2,V1} || K1 := V1 <- maps:iterator(#{a=>1, b=>2}, ordered) &&
+                                   K2 := V2 <- maps:iterator(#{c=>3, d=>4}, ordered),
+                               V2 rem 2 == 0],
+    <<5>> = << <<(X+Y)/integer>> || X <- [1,2,3] && Y <- [2,2,2], X rem 2 == 1, X+Y>4>>,
+    #{c := 3,a := 1} = #{X => Y || X <- [a,b,c] && Y <- [1,2,3], Y rem 2 == 1},
+    #{c := 3} = #{X => Y || X <- [a,b,c] && Y <- [1,2,3], Y rem 2 == 1, Y > 1},
+    #{c := 3,a := 1} = #{X => Y || X <- [a,b,c] && <<Y>> <= <<1,2,3>>, Y rem 2 == 1}.
+
+filter_pattern(Config) when is_list(Config) ->
+    [] = do_filter_pat_1([], []),
+    [] = do_filter_pat_1([a], [a]),
+    [] = do_filter_pat_1([{ok,a}], [{error,e}]),
+
+    [] = do_filter_pat_2([], []),
+    [] = do_filter_pat_2([a], [b]),
+    [] = do_filter_pat_2([{a,1}], [{b,1}]),
+    [{1,7}] = do_filter_pat_2([{a,1}], [{a,7}]),
+    [{1,7},{10,20}] = do_filter_pat_2([{a,1},{b,9},{x,10}],
+                                      [{a,7},{wrong,8},{x,20}]),
+
+    ok.
+
+do_filter_pat_1(L1, L2) ->
+    Res = [{A,B} || {ok,A} <- L1 && {ok,B} <- L2],
+    Res = [{A,B} || {{ok,A},{ok,B}} <- lists:zip(L1,L2)],
+    Res.
+
+do_filter_pat_2(L1, L2) ->
+    Res = [{A,B} || {Same,A} <- L1 && {Same,B} <- L2],
+    Res = [{A,B} || {{Same,A},{Same,B}} <- lists:zip(L1,L2)],
+    Res.
+
+cartesian(Config) when is_list(Config) ->
+    [{a,3}, {b,5}, {c,7}, {a,4}, {b,6}, {c,8}] =
+        [{X, W+Y} || W <- [1,2],
+                     X <- [a,b,c] && <<Y>> <= <<2,4,6>>],
+    [{a,3}, {a,4}, {b,5}, {b,6}, {c,7}, {c,8}] =
+        [{X, W+Y} || X <- [a,b,c] &&
+                         <<Y>> <= <<2,4,6>>, W <- [1,2]],
+    [{a,4}, {b,6}, {c,8}] =
+        [{X, W+Y} || X <- [a,b,c] &&
+                         <<Y>> <= <<2,4,6>>, W <- [1,2], (W + Y) rem 2 == 0],
+    <<4,2,5,3,6,4>> = << <<(X+V1+Y)/integer>> ||
+                          X <- [1,2,3] &&
+                              _K1 := V1 <- maps:iterator(#{a=>2, b=>2, c=>2}, ordered),
+                          <<Y>> <= <<1,-1>> >>,
+    ok.
+
+strict_list(Config) when is_list(Config) ->
+    Seq100 = lists:seq(1, 100),
+
+    [2,3,4] = [X+Y || X <:- [1,2,3] && Y <- [1,1,1]],
+    [3,4] = [X+Y || X <:- [1,2,3] && Y <:- [1,1,1], X > 1],
+
+    [] = strict_list_mixed_1([], []),
+    [11,22] = strict_list_mixed_1([{i,1},{i,2}], [{i,10},{i,20}]),
+    [13,25] = strict_list_mixed_1([{i,3},{i,4},{i,5}], [{i,10},bad,{i,20}]),
+    {'EXIT',{{bad_generators,{[bad,{i,5}],[{i,15},{i,20}]}},_}} =
+        catch strict_list_mixed_1([{i,3},bad,{i,5}], [{i,10},{i,15},{i,20}]),
+    {'EXIT',{{bad_generators,{[{i,5}],[]}},_}} =
+        catch strict_list_mixed_1([{i,3},{i,5}], [bad]),
+
+    [] = strict_list_mixed_2([], #{}),
+    [15] = strict_list_mixed_2([{i,3}], #{{k,4} => {v,3}}),
+    [15] = strict_list_mixed_2([{i,0},{i,3}], #{{a,0} => {a,0},
+                                                {k,4} => {v,3}}),
+
+    ?assertEqual([I * 3*I + 7*I || I <- Seq100],
+                 strict_list_mixed_2([{i,I} || I <- Seq100],
+                                     #{{k,3*I} => {v,7*I} || I <- Seq100})),
+    SimpleMap = #{{k,1} => {v,2}},
+    {'EXIT',{{bad_generators,{[{a,3}],{{k,1},{v,2},none}}},_}} =
+        catch strict_list_mixed_2([{a,3}], SimpleMap),
+    {'EXIT',{{bad_generators,{[],{{k,1},{v,2},none}}},_}} =
+        catch strict_list_mixed_2([], SimpleMap),
+
+    [] = strict_list_strict_1([], []),
+    [11,22] = strict_list_strict_1([{i,1},{i,2}], [{i,10},{i,20}]),
+    {'EXIT',{{bad_generators,{[bad,{i,5}],[{i,15},{i,20}]}},_}} =
+        catch strict_list_strict_1([{i,3},bad,{i,5}], [{i,10},{i,15},{i,20}]),
+    {'EXIT',{{bad_generators,{[{i,4},{i,5}],[{wrong_tag,7},{i,20}]}},_}} =
+        catch strict_list_strict_1([{i,3},{i,4},{i,5}], [{i,10},{wrong_tag,7},{i,20}]),
+    {'EXIT',{{bad_generators,{[{a,b,c},{i,5}],[{wrong_tag,7},{i,20}]}},_}} =
+        catch strict_list_strict_1([{i,3},{a,b,c},{i,5}], [{i,10},{wrong_tag,7},{i,20}]),
+    {'EXIT',{{bad_generators,{[{i,5}],[]}},_}} =
+        catch strict_list_strict_1([{i,3},{i,5}], [{i,7}]),
+
+    [] = strict_list_strict_2([], [], <<>>),
+    [5,23] = strict_list_strict_2([{i,1},{i,2}], [{i,2},{i,7}], <<3,9>>),
+    ?assertEqual([2*I * 3*I + I || I <- Seq100],
+                 strict_list_strict_2([{i,2*I} || I <- Seq100],
+                                      [{i,3*I} || I <- Seq100],
+                                      list_to_binary(Seq100))),
+    {'EXIT',{{bad_generators,{[{i,2}],[{i,7}],<<9:7>>}},_}} =
+        catch strict_list_strict_2([{i,1},{i,2}], [{i,2},{i,7}], <<3,9:7>>),
+    {'EXIT',{{bad_generators,{[],[],[]}},_}} =
+        catch strict_list_strict_2([], [], []),
+    {'EXIT',{{bad_generators,{[{i,0}],[],<<>>}},_}} =
+        catch strict_list_strict_2([{i,0}], [], <<>>),
+    {'EXIT',{{bad_generators,{[{i,0}],[{bad,5}],<<99>>}},_}} =
+        catch strict_list_strict_2([{i,0}], [{bad,5}], <<99>>),
+    {'EXIT',{{bad_generators,{[{i,20}],[{i,21}],<<42:7>>}},_}} =
+        catch strict_list_strict_2([{i,20}], [{i,21}], <<42:7>>),
+
+    [] = strict_list_strict_3([], <<>>),
+    [45] = strict_list_strict_3([{i,42}], <<3>>),
+    {'EXIT',{{bad_generators,{[],<<2>>}},_}} =
+        catch strict_list_strict_3([{i,1}], <<1,2>>),
+    {'EXIT',{{bad_generators,{[],<<0:7>>}},_}} =
+        catch strict_list_strict_3([], <<0:7>>),
+    {'EXIT',{{bad_generators,{[{i,1}],<<0:7>>}},_}} =
+        catch strict_list_strict_3([{i,1}], <<0:7>>),
+
+    [] = strict_list_strict_4([], <<>>),
+    [100] = strict_list_strict_4([{i,100}], <<42>>),
+    {'EXIT',{{bad_generators,{[{i,100}],<<0>>}},_}} =
+        catch strict_list_strict_4([{i,100}], <<0>>),
+    {'EXIT',{{bad_generators,{[{i,100}],<<>>}},_}} =
+        catch strict_list_strict_4([{i,100}], <<>>),
+    {'EXIT',{{bad_generators,{[{i,100}],<<0:8,1:1>>}},_}} =
+        catch strict_list_strict_4([{i,100}], <<0:8,1:1>>),
+
+    NaN = <<-1:64>>,
+    [] = strict_list_5(<<>>, <<>>),
+    [3.14] = strict_list_5(<<0:1,1:1>>, <<32,0.0:32/float, 64,3.14:64/float>>),
+    [0.0,3.14] = strict_list_5(<<1:1,1:1>>, <<32,0.0:32/float, 64,3.14:64/float>>),
+    {'EXIT',{{bad_generators,{<<>>,<<64,42.0/float>>}},_}} =
+        catch strict_list_5(<<>>, <<64,42.0/float>>),
+    {'EXIT',{{bad_generators,{<<0:1,1:1>>,
+                              <<117,-1:117/signed,32,17.0:32/float>>}},_}} =
+        catch strict_list_5(<<0:1,1:1>>, <<117,-1:117, 32,17.0:32/float>>),
+    {'EXIT',{{bad_generators,{<<0:1>>,<<64,NaN/binary>>}},_}} =
+        catch strict_list_5(<<1:1,0:1>>, <<32,42.0:32/float, 64,NaN/binary>>),
+    {'EXIT',{{bad_generators,{<<1:1>>,<<64,NaN/binary>>}},_}} =
+        catch strict_list_5(<<1:1,1:1>>, <<32,42.0:32/float, 64,NaN/binary>>),
+
+    ok.
+
+strict_list_mixed_1(X, Y) ->
+    [A + B || {i,A} <:- X && {i,B} <- Y].
+
+strict_list_mixed_2(L, Map0) ->
+    Map = maps:iterator(Map0, ordered),
+    [A * B + C || {i,A} <:- L && {k,B} := {v,C} <- Map].
+
+strict_list_strict_1(X, Y) ->
+    [A + B || {i,A} <:- X && {i,B} <:- Y].
+
+strict_list_strict_2(X, Y, Z) ->
+    [A * B + C || {i,A} <:- X && {i,B} <:- Y && <<C:8>> <:= Z].
+
+strict_list_strict_3(List, Bin) ->
+    [A + B || {i,A} <:- List && <<B:8>> <:= Bin].
+
+strict_list_strict_4(List, Bin) ->
+    [A || {i,A} <:- List && <<42:8>> <:= Bin].
+
+strict_list_5(Wanted, Floats) ->
+    Res = [F || <<W:1>> <:= Wanted && <<Size:8,F:Size/float>> <:= Floats, W =:= 1],
+    Res = [F || <<1:1>> <= Wanted && <<Size:8,F:Size/float>> <:= Floats],
+    Res.
+
+strict_binary(Config) when is_list(Config) ->
+    Seq100 = lists:seq(1, 100),
+
+    <<2,4,6>> = << <<(X+Y)>> || X <:- [1,2,3] && <<Y>> <= <<1,2,3>>>>,
+    <<2,4>> = << <<(X+Y)>> || <<X>> <:= <<1,2,3>> && {X, Y} <- [{1,1},{2,2},{2,3}]>>,
+    <<2,24>> = << <<(X*Y*Z)>> || X := Y <:- #{1 => 2, 3 => 4} && <<Z>> <:= <<1,2>> >>,
+
+    <<>> = strict_binary_1(#{}, <<>>),
+    <<24:64>> = strict_binary_1(#{2 => {val,3}}, <<4:8>>),
+    ?assertEqual(<< <<(5*I * 3*I * I):64>> || I <- Seq100 >>,
+                 strict_binary_1(maps:iterator(#{5*I => {val,3*I} || I <- Seq100}, ordered),
+                                 list_to_binary(Seq100))),
+    {'EXIT',{{bad_generators,{none,<<42:8>>}},_}} = catch strict_binary_1(#{}, <<42:8>>),
+    {'EXIT',{{bad_generators,{none,<<42:7>>}},_}} = catch strict_binary_1(#{}, <<42:7>>),
+    {'EXIT',{{bad_generators,{none,<<0:4>>}},_}} = catch strict_binary_1(#{2 => {val,3}}, <<0,0:4>>),
+
+    <<>> = strict_binary_mixed_1(<<>>, #{}, #{}),
+    <<>> = strict_binary_mixed_1(<<1:2>>, #{}, #{}),
+    <<999:64>> = strict_binary_mixed_1(<<1:1>>, #{0 => {v,0}}, #{1 => {v,999}}),
+    ?assertEqual(<< <<I:64>> || I <- Seq100>>,
+                 strict_binary_mixed_1(<<0:100>>,
+                                       #{I => {v,I} || I <- Seq100},
+                                       #{I => {v,-I} || I <- Seq100})),
+    ?assertEqual(<< <<-I:64>> || I <- Seq100>>,
+                 strict_binary_mixed_1(<<-1:100>>,
+                                       #{I => {v,I} || I <- Seq100},
+                                       #{I => {v,-I} || I <- Seq100})),
+    {'EXIT',{{bad_generators,{<<0:1>>,{0,0,none},{0,{v,7},none}}},_}} =
+        catch strict_binary_mixed_1(<<0:1>>, #{0 => 0}, #{0 => {v,7}}),
+
+    Island = ~"skärgårdsö",
+    IslandSeq = lists:seq(1, length([C || <<C/utf8>> <= Island])),
+    ?assertEqual(<< <<I:8,C:32>> ||
+                     {I,C} <:- lists:zip(IslandSeq, [C || <<C/utf8>> <= Island]) >>,
+                 strict_binary_utf8(IslandSeq, Island)),
+    {'EXIT',{{bad_generators,{[4,5,6,7,8],<<16#ff,16#ff,"def">>}},_}} =
+        catch strict_binary_utf8(lists:seq(1, 8), <<"abc",16#ff,16#ff,"def">>),
+
+    ok.
+
+strict_binary_1(Map, Bin) ->
+    << <<(X*Y*Z):64>> || X := {val,Y} <:- Map && <<Z:8>> <:= Bin >>.
+
+strict_binary_utf8(List, Bin) ->
+    << <<I:8,C:32>> || I <:- List && <<C/utf8>> <:= Bin >>.
+
+strict_binary_mixed_1(Bin, MapA0, MapB0) ->
+    MapA = maps:iterator(MapA0, ordered),
+    MapB = maps:iterator(MapB0, ordered),
+    <<case N of
+          0 -> <<V1:64>>;
+          1 -> <<V2:64>>
+      end || <<N:1>> <= Bin && _ := {v,V1} <:- MapA && _ := {v,V2} <- MapB>>.
+
+nomatch(Config) when is_list(Config) ->
+    [] = do_nomatch_1([], []),
+    [] = do_nomatch_1([1], [a]),
+    [] = do_nomatch_1([1,2], [a,b]),
+    {'EXIT',{{bad_generators,{[1,2,3],[]}},_}} = do_nomatch_1([1,2,3], []),
+    {'EXIT',{{bad_generators,{[3],[]}},_}} = do_nomatch_1([1,2,3], [a,b]),
+
+    <<>> = do_nomatch_2([], <<>>),
+    <<>> = do_nomatch_2([a], <<1>>),
+    {'EXIT',{{bad_generators,{[2],<<>>}},_}} = do_nomatch_2([1,2], <<3>>),
+    ok.
+
+do_nomatch_1(L1, L2) ->
+    catch [{X, Y} || a=b=X <- L1 && Y <- L2].
+
+do_nomatch_2(L, Bin) ->
+    catch << <<(X+Y)/integer>> || a=b=X <- L && <<Y>> <= Bin >>.
+
+bad_generators(Config) when is_list(Config) ->
+    {'EXIT',{{bad_generators,{x,[1,2]}},_}} =
+        catch [{X,Y} || X <- x && Y <- [1,2]],
+    {'EXIT',{{bad_generators,{[],[4]}},_}} =
+        catch [{X,Y} || X <- [1,2,3] && Y <- [1,2,3,4]],
+    {'EXIT',{{bad_generators,{[3,4],[]}},_}} =
+        catch [{X,Y} || X <- [1,2,3,4] && Y <- [1,2], X < 3],
+    {'EXIT',{{bad_generators,{[3,4],[]}},_}} =
+        catch << <<(X+Y)/integer>> || X <- [1,2,3,4] && Y <- [1,2], X < 3>>,
+    {'EXIT',{{bad_generators,{<<1,2>>,a}},_}} =
+        catch << <<X:16>> || <<X:16>> <= <<1:8,2:8>> && <<X:8>> <= a>>,
+    {'EXIT',{{bad_generator,a},_}} = catch [X || X := X <- a && _Y <- [1]],
+    {'EXIT',{{bad_generators,{[d],[]}},_}} =
+        catch #{X => Y || X <- [a,b,c,d] && Y <- [1,2,3], Y > 1},
+
+    ok.
diff --git a/lib/stdlib/examples/erl_id_trans.erl b/lib/stdlib/examples/erl_id_trans.erl
index 0d54643659..c336af17ca 100644
--- a/lib/stdlib/examples/erl_id_trans.erl
+++ b/lib/stdlib/examples/erl_id_trans.erl
@@ -643,6 +643,9 @@ comprehension_quals([{m_generate_strict,Anno,P0,E0}|Qs]) ->
     E1 = expr(E0),
     P1 = pattern(P0),
     [{m_generate_strict,Anno,P1,E1}|comprehension_quals(Qs)];
+comprehension_quals([{zip,Anno,Gens0}|Qs]) ->
+    Gens1 = comprehension_quals(Gens0),
+    [{zip,Anno,Gens1}|comprehension_quals(Qs)];
 comprehension_quals([E0|Qs]) ->
     E1 = expr(E0),
     [E1|comprehension_quals(Qs)];
diff --git a/lib/stdlib/src/erl_error.erl b/lib/stdlib/src/erl_error.erl
index d5f1e550a3..c5ae309ce1 100644
--- a/lib/stdlib/src/erl_error.erl
+++ b/lib/stdlib/src/erl_error.erl
@@ -397,6 +397,8 @@ explain_reason({bad_filter,V}, error=Cl, [], PF, S, _Enc, CL) ->
     format_value(V, <<"bad filter ">>, Cl, PF, S, CL);
 explain_reason({bad_generator,V}, error=Cl, [], PF, S, _Enc, CL) ->
     format_value(V, <<"bad generator ">>, Cl, PF, S, CL);
+explain_reason({bad_generators,V}, error=Cl, [], PF, S, _Enc, CL) ->
+    format_value(V, <<"bad generators: ">>, Cl, PF, S, CL);
 explain_reason({unbound,V}, error, [], _PF, _S, _Enc, _CL) ->
     io_lib:fwrite(<<"variable ~w is unbound">>, [V]);
 %% Exit codes local to the shell module (restricted shell):
diff --git a/lib/stdlib/src/erl_eval.erl b/lib/stdlib/src/erl_eval.erl
index e825efe49d..9e362190ef 100644
--- a/lib/stdlib/src/erl_eval.erl
+++ b/lib/stdlib/src/erl_eval.erl
@@ -129,7 +129,8 @@ to be called.
 
 -export([exprs/2,exprs/3,exprs/4,expr/2,expr/3,expr/4,expr/5,
          expr_list/2,expr_list/3,expr_list/4]).
--export([new_bindings/0,bindings/1,binding/2,add_binding/3,del_binding/2]).
+-export([new_bindings/0,new_bindings/1,bindings/1,binding/2,
+         add_binding/3,del_binding/2]).
 -export([extended_parse_exprs/1, extended_parse_term/1]).
 -export([is_constant_expr/1, partial_eval/1, eval_str/1]).
 
@@ -139,7 +140,7 @@ to be called.
 
 -export([check_command/2, fun_data/1]).
 
--import(lists, [reverse/1,foldl/3,member/2]).
+-import(lists, [all/2,any/2,foldl/3,member/2,reverse/1]).
 
 -export_type([binding_struct/0]).
 
@@ -931,6 +932,9 @@ do_apply(F, _Anno, FunOrModFun, Args) when is_function(F, 2) ->
 eval_lc(E, Qs, Bs, Lf, Ef, RBs, FUVs) ->
     ret_expr(lists:reverse(eval_lc1(E, Qs, Bs, Lf, Ef, FUVs, [])), Bs, RBs).
 
+eval_lc1(E, [{zip, Anno, Gens}|Qs], Bs0, Lf, Ef, FUVs, Acc0) ->
+    {VarList, Bs1} = convert_gen_values(Gens, [], Bs0, Lf, Ef, FUVs),
+    eval_zip(E, [{zip, Anno, VarList}|Qs], Bs1, Lf, Ef, FUVs, Acc0, fun eval_lc1/7);
 eval_lc1(E, [Q|Qs], Bs0, Lf, Ef, FUVs, Acc0) ->
     case is_generator(Q) of
         true ->
@@ -944,6 +948,284 @@ eval_lc1(E, [], Bs, Lf, Ef, FUVs, Acc) ->
     {value,V,_} = expr(E, Bs, Lf, Ef, none, FUVs),
     [V|Acc].
 
+%% convert values for generator vars from abstract form to flattened lists
+convert_gen_values([{Generate, Anno, P, L0}|Qs], Acc, Bs0, Lf, Ef,FUVs)
+    when Generate =:= generate;
+         Generate =:= generate_strict;
+         Generate =:= b_generate;
+         Generate =:= b_generate_strict ->
+    {value,L1,_Bs1} = expr(L0, Bs0, Lf, Ef, none, FUVs),
+    convert_gen_values(Qs, [{Generate, Anno, P, L1}|Acc], Bs0, Lf, Ef, FUVs);
+convert_gen_values([{Generate, Anno, P, Map0}|Qs], Acc, Bs0, Lf, Ef,FUVs)
+    when Generate =:= m_generate;
+         Generate =:= m_generate_strict ->
+    {map_field_exact,_,K,V} = P,
+    {value,Map,_Bs1} = expr(Map0, Bs0, Lf, Ef, none, FUVs),
+    Iter = case is_map(Map) of
+               true ->
+                   maps:iterator(Map);
+               false ->
+                   %% Validate iterator.
+                   try maps:foreach(fun(_, _) -> ok end, Map) of
+                       _ ->
+                           Map
+                   catch
+                       _:_ ->
+                           apply_error({bad_generator,Map}, ?STACKTRACE,
+                                       Anno, Bs0, Ef, none)
+                   end
+           end,
+    convert_gen_values(Qs, [{Generate, Anno, {tuple, Anno, [K, V]}, Iter}|Acc],
+                       Bs0, Lf, Ef, FUVs);
+convert_gen_values([], Acc, Bs0, _Lf, _Ef, _FUVs) ->
+    {reverse(Acc), Bs0}.
+
+bind_all_generators(Gens, Bs0, Lf, Ef, FUVs) ->
+    bind_all_generators1(Gens, [], new_bindings(Bs0), Lf, Ef, FUVs, continue).
+
+bind_all_generators1([{b_generate, Anno, P, <<_/bitstring>>=Bin}|Qs],
+                     Acc, Bs0, Lf, Ef, FUVs, continue) ->
+    Mfun = match_fun(Bs0, Ef),
+    Efun = fun(Exp, Bs) -> expr(Exp, Bs, Lf, Ef, none) end,
+    ErrorFun = fun(A, R, S) -> apply_error(R, S, A, Bs0, Ef, none) end,
+    case eval_bits:bin_gen(P, Bin, new_bindings(Bs0), Bs0, Mfun, Efun, ErrorFun) of
+        {match, Rest, Bs1} ->
+            Bs2 = zip_add_bindings(Bs1, Bs0),
+            case Bs2 of
+                nomatch ->
+                    bind_all_generators1(Qs,[{b_generate, Anno, P, Rest}|Acc],
+                                         Bs0, Lf, Ef, FUVs, skip);
+                _ ->
+                    bind_all_generators1(Qs,[{b_generate, Anno, P, Rest}|Acc],
+                                         Bs2, Lf, Ef, FUVs, continue)
+            end;
+        {nomatch, Rest} ->
+            bind_all_generators1(Qs, [{b_generate, Anno, P, Rest}|Acc],
+                                 Bs0, Lf, Ef, FUVs, skip);
+        done ->
+            {[], done}
+    end;
+bind_all_generators1([{b_generate_strict, Anno, P, <<_/bitstring>>=Bin}|Qs],
+                     Acc, Bs0, Lf, Ef, FUVs, continue) ->
+    Mfun = match_fun(Bs0, Ef),
+    Efun = fun(Exp, Bs) -> expr(Exp, Bs, Lf, Ef, none) end,
+    ErrorFun = fun(A, R, S) -> apply_error(R, S, A, Bs0, Ef, none) end,
+    case eval_bits:bin_gen(P, Bin, new_bindings(Bs0), Bs0, Mfun, Efun, ErrorFun) of
+        {match, Rest, Bs1} ->
+            Bs2 = zip_add_bindings(Bs1, Bs0),
+            case Bs2 of
+                nomatch -> {Acc, error};
+                _ ->
+                    bind_all_generators1(Qs,[{b_generate_strict, Anno, P, Rest}|Acc],
+                                         Bs2, Lf, Ef, FUVs, continue)
+            end;
+        {nomatch, _Rest} ->
+            {Acc, error};
+        done when Bin =/= <<>> ->
+            {Acc, error};
+        done ->
+            {[], done}
+    end;
+bind_all_generators1([{b_generate, Anno, P, <<_/bitstring>>=Bin}|Qs],
+                     Acc, Bs0, Lf, Ef, FUVs, skip) ->
+    Mfun = match_fun(Bs0, Ef),
+    Efun = fun(Exp, Bs) -> expr(Exp, Bs, Lf, Ef, none) end,
+    ErrorFun = fun(A, R, S) -> apply_error(R, S, A, Bs0, Ef, none) end,
+    case eval_bits:bin_gen(P, Bin, new_bindings(Bs0), Bs0, Mfun, Efun, ErrorFun) of
+        {match, Rest, _} ->
+            bind_all_generators1(Qs, [{b_generate, Anno, P, Rest}|Acc],
+                                 Bs0, Lf, Ef, FUVs, skip);
+        {nomatch, Rest} ->
+            bind_all_generators1(Qs, [{b_generate, Anno, P, Rest}|Acc],
+                                 Bs0, Lf, Ef, FUVs, skip);
+        done ->
+            {[], skip}
+    end;
+bind_all_generators1([{b_generate_strict, Anno, P, <<_/bitstring>>=Bin}|Qs],
+                     Acc, Bs0, Lf, Ef, FUVs, skip) ->
+    Mfun = match_fun(Bs0, Ef),
+    Efun = fun(Exp, Bs) -> expr(Exp, Bs, Lf, Ef, none) end,
+    ErrorFun = fun(A, R, S) -> apply_error(R, S, A, Bs0, Ef, none) end,
+    case eval_bits:bin_gen(P, Bin, new_bindings(Bs0), Bs0, Mfun, Efun, ErrorFun) of
+        {match, Rest, _} ->
+            bind_all_generators1(Qs, [{b_generate_strict, Anno, P, Rest}|Acc],
+                                 Bs0, Lf, Ef, FUVs, skip);
+        {nomatch, _Rest} ->
+            {Acc, error};
+        done when Bin =/= <<>> ->
+            {Acc, error};
+        done ->
+            {[], skip}
+    end;
+bind_all_generators1([{generate, Anno, P, [H|T]}|Qs], Acc, Bs0, Lf, Ef, FUVs, continue) ->
+    case match(P, H, Anno, new_bindings(Bs0), Bs0, Ef) of
+        {match,Bsn} ->
+            Bs2 = zip_add_bindings(Bsn, Bs0),
+            case Bs2 of
+                nomatch ->
+                    bind_all_generators1(Qs,[{generate, Anno, P, T}|Acc],
+                                         Bs0, Lf, Ef, FUVs, skip);
+                _ -> bind_all_generators1(Qs,[{generate, Anno, P, T}|Acc],
+                                          Bs2, Lf, Ef, FUVs, continue)
+            end;
+        nomatch ->
+            %% match/6 returns nomatch. Skip this value
+            bind_all_generators1(Qs,[{generate, Anno, P, T}|Acc], Bs0, Lf, Ef, FUVs, skip)
+    end;
+bind_all_generators1([{generate_strict, Anno, P, [H|T]}|Qs], Acc, Bs0, Lf, Ef, FUVs, continue) ->
+    case match(P, H, Anno, new_bindings(Bs0), Bs0, Ef) of
+        {match,Bsn} ->
+            Bs2 = zip_add_bindings(Bsn, Bs0),
+            case Bs2 of
+                nomatch -> {Acc, error};
+                _ -> bind_all_generators1(Qs,[{generate_strict, Anno, P, T}|Acc],
+                                          Bs2, Lf, Ef, FUVs, continue)
+            end;
+        nomatch ->
+            {Acc, error}
+    end;
+bind_all_generators1([{generate, Anno, P, [_H|T]}|Qs], Acc, Bs0, Lf, Ef, FUVs, skip) ->
+    bind_all_generators1(Qs,[{generate, Anno, P, T}|Acc], Bs0, Lf, Ef, FUVs, skip);
+bind_all_generators1([{generate_strict, Anno, P, [H|T]}|Qs], Acc, Bs0, Lf, Ef, FUVs, skip) ->
+    case match(P, H, Anno, new_bindings(Bs0), Bs0, Ef) of
+        {match,Bsn} ->
+            Bs2 = zip_add_bindings(Bsn, Bs0),
+            case Bs2 of
+                nomatch ->
+                    {Acc, error};
+                _ -> bind_all_generators1(Qs,[{generate_strict, Anno, P, T}|Acc],
+                                          Bs2, Lf, Ef, FUVs, skip)
+            end;
+        nomatch ->
+            {Acc, error}
+    end;
+bind_all_generators1([{m_generate, Anno, P, Iter0}|Qs], Acc, Bs0, Lf, Ef, FUVs, continue) ->
+    case maps:next(Iter0) of
+        {K,V,Iter} ->
+            case match(P, {K,V}, Anno, new_bindings(Bs0), Bs0, Ef) of
+                {match,Bsn} ->
+                    Bs2 = zip_add_bindings(Bsn, Bs0),
+                    case Bs2 of
+                        nomatch->
+                            bind_all_generators1(Qs,[{m_generate, Anno, P, Iter}|Acc],
+                                                 Bs0, Lf, Ef, FUVs, skip);
+                        _ -> bind_all_generators1(Qs,[{m_generate, Anno, P, Iter}|Acc],
+                                                  Bs2, Lf, Ef, FUVs, continue)
+                    end;
+                nomatch ->
+                    bind_all_generators1(Qs, [{m_generate, Anno, P, Iter}|Acc],
+                                         Bs0, Lf, Ef, FUVs, skip)
+            end;
+        none ->
+            {[], done}
+    end;
+bind_all_generators1([{m_generate_strict, Anno, P, Iter0}|Qs], Acc, Bs0, Lf, Ef, FUVs, continue) ->
+    case maps:next(Iter0) of
+        {K,V,Iter} ->
+            case match(P, {K,V}, Anno, new_bindings(Bs0), Bs0, Ef) of
+                {match,Bsn} ->
+                    Bs2 = zip_add_bindings(Bsn, Bs0),
+                    case Bs2 of
+                        nomatch -> {Acc, error};
+                        _ -> bind_all_generators1(Qs,[{m_generate_strict, Anno, P, Iter}|Acc],
+                                                  Bs2, Lf, Ef, FUVs, continue)
+                    end;
+                nomatch ->
+                    {Acc, error}
+            end;
+        none ->
+            {[], done}
+    end;
+bind_all_generators1([{m_generate, Anno, P, Iter0}|Qs], Acc, Bs0, Lf, Ef, FUVs, skip) ->
+    case maps:next(Iter0) of
+        {_K,_V,Iter} ->
+            bind_all_generators1(Qs, [{m_generate, Anno, P, Iter}|Acc],
+                                 Bs0, Lf, Ef, FUVs, skip);
+        none ->
+            {[], skip}
+    end;
+bind_all_generators1([{m_generate_strict, Anno, P, Iter0}|Qs], Acc, Bs0, Lf, Ef, FUVs, skip) ->
+    case maps:next(Iter0) of
+        {K,V,Iter} ->
+            case match(P, {K,V}, Anno, new_bindings(Bs0), Bs0, Ef) of
+                {match,Bsn} ->
+                    Bs2 = zip_add_bindings(Bsn, Bs0),
+                    case Bs2 of
+                        nomatch -> {Acc, error};
+                        _ -> bind_all_generators1(Qs,[{m_generate_strict, Anno, P, Iter}|Acc],
+                                                  Bs2, Lf, Ef, FUVs, continue)
+                    end;
+                nomatch -> {Acc, error}
+            end;
+        none ->
+            {[], done}
+    end;
+bind_all_generators1([{generate,_,_,[]}|_], _, _, _, _, _,_) ->
+    %% no more values left for a var, time to return
+    {[],done};
+bind_all_generators1([{generate_strict,_,_,[]}|_], _, _, _, _, _,_) ->
+    %% no more values left for a var, time to return
+    {[],done};
+bind_all_generators1([{generate, _Anno, _P, _Term}|_Qs], Acc, _Bs0,_Lf, _Ef, _FUVs,_) ->
+    {Acc, error};
+bind_all_generators1([{generate_strict, _Anno, _P, _Term}|_Qs], Acc, _Bs0,_Lf, _Ef, _FUVs,_) ->
+    {Acc, error};
+bind_all_generators1([{b_generate, _Anno, _P, _Term}|_Qs], Acc, _Bs0,_Lf, _Ef, _FUVs,_) ->
+    {Acc, error};
+bind_all_generators1([{b_generate_strict, _Anno, _P, _Term}|_Qs], Acc, _Bs0,_Lf, _Ef, _FUVs,_) ->
+    {Acc, error};
+bind_all_generators1([], [_H|_T] = Acc, Bs0, _Lf, _Ef, _FUVs, continue) ->
+    %% all vars are bind for this round
+    {Acc, Bs0};
+bind_all_generators1([], [_H|_T] = Acc, _Bs0,_Lf, _Ef, _FUVs, skip) ->
+    {Acc, skip}.
+
+check_bad_generators([{Generate,_,_,V}|T], Env, Acc)
+    when Generate =:= generate;
+         Generate =:= generate_strict ->
+    check_bad_generators(T, Env, [V|Acc]);
+check_bad_generators([{Generate,_,_,Iter0}|T], Env, Acc)
+    when Generate =:= m_generate;
+         Generate =:= m_generate_strict ->
+    case maps:next(Iter0) of
+        none -> check_bad_generators(T, Env, [#{}|Acc]);
+        _ -> check_bad_generators(T, Env, [#{K => V || K := V <- Iter0}|Acc])
+    end;
+check_bad_generators([{Generate,_,P,<<_/bitstring>>=Bin}|T], {Bs0, Lf, Ef}, Acc)
+    when Generate =:= b_generate;
+         Generate =:= b_generate_strict ->
+    Mfun = match_fun(Bs0, Ef),
+    Efun = fun(Exp, Bs) -> expr(Exp, Bs, Lf, Ef, none) end,
+    ErrorFun = fun(A, R, S) -> apply_error(R, S, A, Bs0, Ef, none) end,
+    case eval_bits:bin_gen(P, Bin, new_bindings(Bs0), Bs0, Mfun, Efun, ErrorFun) of
+        done ->
+            check_bad_generators(T, {Bs0, Lf, Ef}, [<<>>|Acc]);
+        _ ->
+            check_bad_generators(T, {Bs0, Lf, Ef}, [Bin|Acc])
+    end;
+check_bad_generators([{b_generate,_,_,Term}|T], Env, Acc) ->
+    check_bad_generators(T, Env, [Term|Acc]);
+check_bad_generators([{b_generate_strict,_,_,Term}|T], Env, Acc) ->
+    check_bad_generators(T, Env, [Term|Acc]);
+check_bad_generators([], _, Acc)->
+    case any(fun is_generator_end/1, Acc) of
+        false ->
+            %% None of the generators has reached its end.
+            {ok, list_to_tuple(reverse(Acc))};
+        true ->
+            case all(fun(V) -> is_generator_end(V) end, Acc) of
+                true ->
+                    %% All generators have reached their end.
+                    {ok, list_to_tuple(reverse(Acc))};
+                false ->
+                    {error, {bad_generators,list_to_tuple(reverse(Acc))}}
+            end
+    end.
+
+is_generator_end([]) -> true;
+is_generator_end(<<>>) -> true;
+is_generator_end(Other) -> Other =:= #{}.
+
 %% eval_bc(Expr, [Qualifier], Bindings, LocalFunctionHandler,
 %%         ExternalFuncHandler, RetBindings) ->
 %%	{value,Value,Bindings} | Value
@@ -951,6 +1233,9 @@ eval_lc1(E, [], Bs, Lf, Ef, FUVs, Acc) ->
 eval_bc(E, Qs, Bs, Lf, Ef, RBs, FUVs) ->
     ret_expr(eval_bc1(E, Qs, Bs, Lf, Ef, FUVs, <<>>), Bs, RBs).
 
+eval_bc1(E, [{zip, Anno, Gens}|Qs], Bs0, Lf, Ef, FUVs, Acc0) ->
+    {VarList, Bs1} = convert_gen_values(Gens, [], Bs0, Lf, Ef, FUVs),
+    eval_zip(E, [{zip, Anno, VarList}|Qs], Bs1, Lf, Ef, FUVs, Acc0, fun eval_bc1/7);
 eval_bc1(E, [Q|Qs], Bs0, Lf, Ef, FUVs, Acc0) ->
     case is_generator(Q) of
         true ->
@@ -973,6 +1258,9 @@ eval_mc(E, Qs, Bs, Lf, Ef, RBs, FUVs) ->
     Map = maps:from_list(L),
     ret_expr(Map, Bs, RBs).
 
+eval_mc1(E, [{zip, Anno, Gens}|Qs], Bs0, Lf, Ef, FUVs, Acc0) ->
+    {VarList, Bs1} = convert_gen_values(Gens, [], Bs0, Lf, Ef, FUVs),
+    eval_zip(E, [{zip, Anno, VarList}|Qs], Bs1, Lf, Ef, FUVs, Acc0, fun eval_mc1/7);
 eval_mc1(E, [Q|Qs], Bs0, Lf, Ef, FUVs, Acc0) ->
     case is_generator(Q) of
         true ->
@@ -986,6 +1274,25 @@ eval_mc1({map_field_assoc,Lfa,K0,V0}, [], Bs, Lf, Ef, FUVs, Acc) ->
     {value,KV,_} = expr({tuple,Lfa,[K0,V0]}, Bs, Lf, Ef, none, FUVs),
     [KV|Acc].
 
+eval_zip(E, [{zip, Anno, VarList}|Qs], Bs0, Lf, Ef, FUVs, Acc0, Fun) ->
+    Gens = case check_bad_generators(VarList, {Bs0, Lf, Ef}, []) of
+               {ok, Acc} -> Acc;
+               {error, Reason} ->
+                   apply_error(Reason, ?STACKTRACE, Anno, Bs0, Ef, none)
+           end,
+    {Rest, Bs1} = bind_all_generators(VarList, Bs0, Lf, Ef, FUVs),
+    case {Rest, Qs, Bs1} of
+        {_, _, error} -> apply_error({bad_generators,Gens}, ?STACKTRACE, Anno, Bs0, Ef, none);
+        {[], [], _} -> Acc0;
+        {[], _, _} -> Acc0;
+        {_,_,done} -> Acc0;
+        {_, _, skip} ->
+            eval_zip(E, [{zip, Anno, reverse(Rest)}|Qs], Bs0, Lf, Ef, FUVs, Acc0, Fun);
+        {_, _, _} ->
+            Acc1 = Fun(E, Qs, add_bindings(Bs1, Bs0), Lf, Ef, FUVs, Acc0),
+            eval_zip(E, [{zip, Anno, reverse(Rest)}|Qs], Bs0, Lf, Ef, FUVs, Acc1, Fun)
+    end.
+
 eval_generator({Generate,Anno,P,L0}, Bs0, Lf, Ef, FUVs, Acc0, CompFun)
   when Generate =:= generate;
        Generate =:= generate_strict ->
@@ -1630,6 +1937,31 @@ structure.
 del_binding(Name, Bs) when is_map(Bs) -> maps:remove(Name, Bs);
 del_binding(Name, Bs) when is_list(Bs) -> orddict:erase(Name, Bs).
 
+zip_add_bindings(Bs1, Bs2) when is_map(Bs1), is_map(Bs2) ->
+    zip_add_bindings_map(maps:keys(Bs1), Bs1, Bs2);
+zip_add_bindings(Bs1, Bs2) when is_list(Bs1), is_list(Bs2) ->
+    zip_add_bindings1(orddict:to_list(Bs1), Bs2).
+
+zip_add_bindings_map([Key | Keys], Bs1, Bs2) ->
+    case {Bs1, Bs2} of
+        {#{Key := Same}, #{Key := Same}} -> zip_add_bindings_map(Keys, Bs1, Bs2);
+        {_, #{Key := _}} -> nomatch;
+        {#{Key := Value},_} -> zip_add_bindings_map(Keys, Bs1, Bs2#{Key => Value})
+    end;
+zip_add_bindings_map([], _, Bs2) ->
+    Bs2.
+
+zip_add_bindings1([{Name,Val}|Bs1], Bs2) ->
+    case orddict:find(Name, Bs2) of
+        {ok, Val} ->
+            zip_add_bindings1(Bs1, Bs2);
+        {ok, _Value} -> nomatch;
+        error ->
+            zip_add_bindings1(Bs1, orddict:store(Name, Val, Bs2))
+    end;
+zip_add_bindings1([], Bs2) ->
+    Bs2.
+
 add_bindings(Bs1, Bs2) when is_map(Bs1), is_map(Bs2) ->
     maps:merge(Bs2, Bs1);
 add_bindings(Bs1, Bs2) ->
@@ -1651,6 +1983,7 @@ merge_bindings(Bs1, Bs2, Anno, Ef) ->
 		  end end,
 	  Bs2, orddict:to_list(Bs1)).
 
+-spec(new_bindings(binding_struct()) -> binding_struct()).
 new_bindings(Bs) when is_map(Bs) -> maps:new();
 new_bindings(Bs) when is_list(Bs) -> orddict:new().
 
diff --git a/lib/stdlib/src/erl_expand_records.erl b/lib/stdlib/src/erl_expand_records.erl
index 7d314632de..6b093e4c4b 100644
--- a/lib/stdlib/src/erl_expand_records.erl
+++ b/lib/stdlib/src/erl_expand_records.erl
@@ -576,6 +576,10 @@ lc_tq(Anno, [{m_generate_strict,AnnoG,P0,G0} | Qs0], St0) ->
     {Qs1,St4} = lc_tq(Anno, Qs0, St3),
     P1 = {map_field_exact,AnnoMFE,KeyP1,ValP1},
     {[{m_generate_strict,AnnoG,P1,G1} | Qs1],St4};
+lc_tq(Anno, [{zip,AnnoG,G0} | Qs0], St0) ->
+    {G1,St1} = lc_tq(Anno, G0, St0),
+    {Qs1,St2} = lc_tq(Anno, Qs0, St1),
+    {[{zip,AnnoG,G1}|Qs1],St2};
 lc_tq(Anno, [F0 | Qs0], #exprec{calltype=Calltype,raw_records=Records}=St0) ->
     %% Allow record/2 and expand out as guard test.
     IsOverriden = fun(FA) ->
diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl
index 850191ca22..5ba47662c9 100644
--- a/lib/stdlib/src/erl_lint.erl
+++ b/lib/stdlib/src/erl_lint.erl
@@ -403,6 +403,8 @@ format_error_1({too_many_arguments,Arity}) ->
     {~"too many arguments (~w) -- maximum allowed is ~w", [Arity,?MAX_ARGUMENTS]};
 format_error_1(update_literal) ->
     ~"expression updates a literal";
+format_error_1(illegal_zip_generator) ->
+    ~"only generators are allowed in a zip generator.";
 %% --- patterns and guards ---
 format_error_1(illegal_map_assoc_in_pattern) -> ~"illegal pattern, did you mean to use `:=`?";
 format_error_1(illegal_pattern) -> ~"illegal pattern";
@@ -3979,6 +3981,10 @@ lc_quals(Qs, Vt0, St0) ->
     {Vt,Uvt,St} = lc_quals(Qs, Vt0, [], St0#lint{recdef_top = false}),
     {Vt,Uvt,St#lint{recdef_top = OldRecDef}}.
 
+lc_quals([{zip,_Anno,Gens} | Qs], Vt0, Uvt0, St0) ->
+    St1 = are_all_generators(Gens,St0),
+    {Vt,Uvt,St} = handle_generators(Gens,Vt0,Uvt0,St1),
+    lc_quals(Qs, Vt, Uvt, St);
 lc_quals([{generate,_Anno,P,E} | Qs], Vt0, Uvt0, St0) ->
     {Vt,Uvt,St} = handle_generator(P,E,Vt0,Uvt0,St0),
     lc_quals(Qs, Vt, Uvt, St);
@@ -4015,6 +4021,36 @@ is_guard_test2_info(#lint{records=RDs,locals=Locals,imports=Imports}) ->
 		     is_imported_function(Imports, FA)
 	 end}.
 
+are_all_generators([{generate,_,_,_}|Qs],St) -> are_all_generators(Qs,St);
+are_all_generators([{generate_strict,_,_,_}|Qs],St) -> are_all_generators(Qs,St);
+are_all_generators([{b_generate,_,_,_}|Qs],St) -> are_all_generators(Qs,St);
+are_all_generators([{b_generate_strict,_,_,_}|Qs],St) -> are_all_generators(Qs,St);
+are_all_generators([{m_generate,_,_,_}|Qs],St) -> are_all_generators(Qs,St);
+are_all_generators([{m_generate_strict,_,_,_}|Qs],St) -> are_all_generators(Qs,St);
+are_all_generators([Q|_Qs],St) ->
+    Anno1 = element(2,Q),
+    add_error(Anno1, illegal_zip_generator, St);
+are_all_generators([],St) -> St.
+
+handle_generators(Gens,Vt,Uvt,St0) ->
+    Ps = [P || {_,_,P,_} <- Gens],
+    Es = [E || {_,_,_,E} <- Gens],
+    {Evt,St1} = exprs(Es, Vt, St0),
+    %% Forget variables local to E immediately.
+    Vt1 = vtupdate(vtold(Evt, Vt), Vt),
+    {_, St2} = check_unused_vars(Evt, Vt, St1),
+    {Pvt,Pnew,St3} = comprehension_pattern(Ps, Vt1, St2),
+    %% Have to keep fresh variables separated from used variables somehow
+    %% in order to handle for example X = foo(), [X || <<X:X>> <- bar()].
+    %%                                1           2      2 1
+    Vt2 = vtupdate(Pvt, Vt1),
+    St4 = shadow_vars(Pnew, Vt1, generate, St3),
+    Svt = vtold(Vt2, Pnew),
+    {_, St5} = check_old_unused_vars(Svt, Uvt, St4),
+    NUvt = vtupdate(vtnew(Svt, Uvt), Uvt),
+    Vt3 = vtupdate(vtsubtract(Vt2, Pnew), Pnew),
+    {Vt3,NUvt,St5}.
+
 handle_generator(P,E,Vt,Uvt,St0) ->
     {Evt,St1} = expr(E, Vt, St0),
     %% Forget variables local to E immediately.
@@ -4032,6 +4068,10 @@ handle_generator(P,E,Vt,Uvt,St0) ->
     Vt3 = vtupdate(vtsubtract(Vt2, Pnew), Pnew),
     {Vt3,NUvt,St5}.
 
+comprehension_pattern([_|_]=Ps, Vt, St) ->
+    Mps = [K || {map_field_exact,_,K,_} <- Ps] ++ [V || {map_field_exact,_,_,V} <- Ps],
+    Ps1 = [P || P <- Ps, element(1,P)=/=map_field_exact],
+    pattern_list(Ps1++Mps, Vt, [], St);
 comprehension_pattern({map_field_exact,_,K,V}, Vt, St) ->
     pattern_list([K,V], Vt, [], St);
 comprehension_pattern(P, Vt, St) ->
diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl
index 18b43dd98c..7babc9c7bc 100644
--- a/lib/stdlib/src/erl_parse.yrl
+++ b/lib/stdlib/src/erl_parse.yrl
@@ -31,6 +31,7 @@ pat_expr pat_expr_max map_pat_expr record_pat_expr
 pat_argument_list pat_exprs
 list tail
 list_comprehension lc_expr lc_exprs
+zc_exprs
 map_comprehension
 binary_comprehension
 tuple
@@ -79,7 +80,7 @@ ssa_check_when_clauses.
 Terminals
 char integer float atom sigil_prefix string sigil_suffix var
 
-'(' ')' ',' '->' '{' '}' '[' ']' '|' '||' '<-' '<:-' ';' ':' '#' '.'
+'(' ')' ',' '->' '{' '}' '[' ']' '|' '||' '<-' '<:-' ';' ':' '#' '.' '&&'
 'after' 'begin' 'case' 'try' 'catch' 'end' 'fun' 'if' 'of' 'receive' 'when'
 'maybe' 'else'
 'andalso' 'orelse'
@@ -362,6 +363,11 @@ binary_comprehension -> '<<' expr_max '||' lc_exprs '>>' :
 	{bc,?anno('$1'),'$2','$4'}.
 lc_exprs -> lc_expr : ['$1'].
 lc_exprs -> lc_expr ',' lc_exprs : ['$1'|'$3'].
+lc_exprs -> zc_exprs : [{zip, ?anno(hd('$1')), '$1'}].
+lc_exprs -> zc_exprs ',' lc_exprs : [{zip, ?anno('$2'), '$1'}|'$3'].
+
+zc_exprs -> lc_expr '&&' lc_expr : ['$1','$3'].
+zc_exprs -> lc_expr '&&' zc_exprs : ['$1'|'$3'].
 
 lc_expr -> expr : '$1'.
 lc_expr -> map_field_exact '<-' expr : {m_generate,?anno('$2'),'$1','$3'}.
@@ -785,7 +791,7 @@ processed (see section [Error Information](#module-error-information)).
 -export_type([abstract_clause/0, abstract_expr/0, abstract_form/0,
               abstract_type/0, form_info/0, error_info/0]).
 %% The following types are exported because they are used by syntax_tools
--export_type([af_binelement/1, af_generator/0, af_remote_function/0]).
+-export_type([af_binelement/1, af_generator/0, af_zip_generator/0, af_remote_function/0]).
 %% The following type is used by PropEr
 -export_type([af_field_decl/0]).
 
@@ -942,7 +948,10 @@ processed (see section [Error Information](#module-error-information)).
                       | {'m_generate', anno(), af_assoc_exact(af_pattern()), abstract_expr()}
                       | {'m_generate_strict', anno(), af_assoc_exact(af_pattern()), abstract_expr()}
                       | {'b_generate', anno(), af_pattern(), abstract_expr()}
-                      | {'b_generate_strict', anno(), af_pattern(), abstract_expr()}.
+                      | {'b_generate_strict', anno(), af_pattern(), abstract_expr()}
+                      | af_zip_generator().
+
+-type af_zip_generator() :: [af_generator(), ...].
 
 -type af_filter() :: abstract_expr().
 
diff --git a/lib/stdlib/src/erl_pp.erl b/lib/stdlib/src/erl_pp.erl
index 401cd11007..bff5a92318 100644
--- a/lib/stdlib/src/erl_pp.erl
+++ b/lib/stdlib/src/erl_pp.erl
@@ -1047,6 +1047,8 @@ clauses(Type, Opts, Cs) ->
 lc_quals(Qs, Opts) ->
     {prefer_nl,[$,],lexprs(Qs, fun lc_qual/2, Opts)}.
 
+lc_qual({zip,_,Qs}, Opts) ->
+    {prefer_nl,["&&"],lexprs(Qs, fun lc_qual/2, Opts)};
 lc_qual({m_generate,_,Pat,E}, Opts) ->
     Pl = map_field(Pat, Opts),
     {list,[{step,[Pl,leaf(" <-")],lexpr(E, 0, Opts)}]};
diff --git a/lib/stdlib/src/erl_scan.erl b/lib/stdlib/src/erl_scan.erl
index 5b4893b431..117acf3b01 100644
--- a/lib/stdlib/src/erl_scan.erl
+++ b/lib/stdlib/src/erl_scan.erl
@@ -604,6 +604,12 @@ scan1(".."++Cs, St, Line, Col, Toks) ->
     tok2(Cs, St, Line, Col, Toks, "..", '..', 2);
 scan1("."=Cs, St, Line, Col, Toks) ->
     {more,{Cs,St,Col,Toks,Line,[],fun scan/6}};
+scan1("&&"++Cs, St, Line, Col, Toks) ->
+    tok2(Cs, St, Line, Col, Toks, "&&", '&&', 2);
+scan1("&"=Cs, St, Line, Col, Toks) ->
+    {more,{Cs,St,Col,Toks,Line,[],fun scan/6}};
+scan1("&"++Cs, St, Line, Col, Toks) ->
+    tok2(Cs, St, Line, Col, Toks, "&", '&', 1);
 scan1([$.=C|Cs], St, Line, Col, Toks) ->
     scan_dot(Cs, St, Line, Col, Toks, [C]);
 scan1([$'|Cs], St, Line, Col, Toks) -> %' Emacs
@@ -741,8 +747,6 @@ scan1([$`|Cs], St, Line, Col, Toks) ->
     tok2(Cs, St, Line, Col, Toks, "`", '`', 1);
 scan1([$~|Cs], St, Line, Col, Toks) ->
     tok2(Cs, St, Line, Col, Toks, "~", '~', 1);
-scan1([$&|Cs], St, Line, Col, Toks) ->
-    tok2(Cs, St, Line, Col, Toks, "&", '&', 1);
 %% End of optimization.
 scan1([C|Cs], St, Line, Col, Toks) when ?UNI255(C) ->
     Str = [C],
diff --git a/lib/stdlib/test/erl_eval_SUITE.erl b/lib/stdlib/test/erl_eval_SUITE.erl
index 6b8ad51f9c..674204ff7c 100644
--- a/lib/stdlib/test/erl_eval_SUITE.erl
+++ b/lib/stdlib/test/erl_eval_SUITE.erl
@@ -29,6 +29,9 @@
 	 pattern_expr/1,
          guard_3/1, guard_4/1, guard_5/1,
          lc/1,
+         zlc/1,
+         zbc/1,
+         zmc/1,
          simple_cases/1,
          unary_plus/1,
          apply_atom/1,
@@ -100,7 +103,8 @@ all() ->
      otp_8133, otp_10622, otp_13228, otp_14826,
      funs, custom_stacktrace, try_catch, eval_expr_5, zero_width,
      eep37, eep43, otp_15035, otp_16439, otp_14708, otp_16545, otp_16865,
-     eep49, binary_and_map_aliases, eep58, strict_generators, binary_skip].
+     eep49, binary_and_map_aliases, eep58, strict_generators, binary_skip,
+     zlc, zbc, zmc].
 
 groups() ->
     [].
@@ -281,6 +285,161 @@ lc(Config) when is_list(Config) ->
 	  "[X || X <- [true,false], X].", [true]),
     ok.
 
+%% EEP-73 zip generator.
+zlc(Config) when is_list(Config) ->
+    check(fun() ->
+                  X = 32, Y = 32, [{X, Y} || X <- [1,2,3] && Y <- [4,5,6]]
+          end,
+          "begin X = 32, Y = 32, [{X, Y} || X <- [1,2,3] && Y <- [4,5,6]] end.",
+          [{1,4},{2,5},{3,6}]),
+    check(fun() ->
+                  S1 = [x, y, z], S2 = [5, 10, 15], X = 32, Y = 32,
+                  [{X, Y} || X <- S1 && Y <- S2]
+          end,
+          "begin
+        S1 = [x, y, z], S2 = [5, 10, 15], X = 32, Y = 32,
+          [{X, Y} || X <- S1 && Y <- S2]
+          end.",
+    [{x,5}, {y,10}, {z,15}]),
+    check(fun() ->
+                  [{X, Y, K} || X <:- [1,2,3] &&  Y:=K <- #{1=>a, 2=>b, 3=>c}]
+          end,
+          "begin [{X, Y, K} || X <:- [1,2,3] &&  Y:=K <- #{1=>a, 2=>b, 3=>c}] end.",
+          [{1,1,a},{2,2,b},{3,3,c}]),
+    check(fun() ->
+                [{X, W+Y} || X <- [a, b, c] && <<Y>> <= <<2, 4, 6>>, W <- [1,2]]
+        end,
+        "begin [{X, W+Y} || X <- [a, b, c] && <<Y>> <= <<2, 4, 6>>, W <- [1,2]] end.",
+        [{a,3}, {a,4}, {b,5}, {b,6}, {c,7}, {c,8}]),
+    check(fun() ->
+            [{X, W+Y} || W <- [0], X <- [a, b, c] && <<Y>> <:= <<2, 4, 6>>, Y<4]
+    end,
+    "begin [{X, W+Y} || W <- [0], X <- [a, b, c] && <<Y>> <:= <<2, 4, 6>>, Y<4] end.",
+    [{a,2}]),
+    check(fun() ->
+            [{X,Y}|| a=b=X <- [1,2] && Y <-[1,2]] end,
+        "begin [{X,Y}|| a=b=X <- [1,2] && Y <-[1,2]] end.",
+        []),
+    check(fun() ->
+        [{A,B,W} || {Same,W} <- [{a,1}],
+            {Same,A} <- [{a,1},{b,9},{x,10}] && {Same,B} <- [{a,7},{wrong,8},{x,20}]]
+        end,
+        "begin [{A,B,W} || {Same,W} <- [{a,1}],
+            {Same,A} <- [{a,1},{b,9},{x,10}] && {Same,B} <- [{a,7},{wrong,8},{x,20}]]
+        end.",
+        [{1,7,1},{10,20,1}]),
+    error_check("[X || X <- a && Y <- [1]].",{bad_generators,{a,[1]}}),
+    error_check("[{X,Y} || X <- a && <<Y>> <= <<1,2>>].",{bad_generators,{a,<<1,2>>}}),
+    error_check("[{X,V} || X <- a && _K := V <- #{b=>3}].",{bad_generators,{a,#{b=>3}}}),
+    error_check("begin
+        X = 32, Y = 32, [{X, Y} || X <- [1,2,3] && Y <- [4]] end.",
+    {bad_generators,{[2,3],[]}}),
+    error_check("begin
+        X = 32, Y = 32, [{X, Y} || X <- [1,2,3] && Y:=_V <- #{1=>1}] end.",
+    {bad_generators,{[2,3],#{}}}),
+    ok.
+
+zbc(Config) when is_list(Config) ->
+    check(fun() ->
+                  <<3, 4, 5>>
+          end,
+          "begin
+        X = 32, Y = 32,
+          << <<(X+Y)/integer>> || <<X>> <= <<1,2,3>> && <<Y>> <= <<2,2,2>> >>
+              end.",
+        <<3, 4, 5>>),
+    check(fun() ->
+                  <<4,5,6,5,6,7,6,7,8>>
+          end,
+          "begin
+        X = 32, Y = 32, Z = 32,
+          << <<(X+Y+Z)/integer>> || <<X>> <= <<1,2,3>> && <<Y>> <= <<2,2,2>>, Z<-[1,2,3] >>
+              end.",
+        <<4,5,6,5,6,7,6,7,8>>),
+    check(fun() ->
+                  <<4, 5, 6>>
+          end,
+          "begin
+        L1 = <<1, 2, 3>>, L2 = <<1, 1, 1>>, L3 = <<2, 2, 2>>,
+          << <<(X+Y+Z)/integer>> || <<X>> <= L1 && <<Y>> <= L2 && <<Z>> <= L3 >>
+              end.",
+    <<4, 5, 6>>),
+    check(fun() ->
+         << <<(X+Y):64>>|| a=b=X <- [1,2] && Y <- [1,2] >> end,
+        "begin << <<(X+Y):64>>|| a=b=X <- [1,2] && Y <- [1,2] >> end.",
+        <<>>),
+    check(fun() ->
+         << <<(X+Y):64>>|| a=b=X <- [1,2] && <<Y>> <= <<1,2>> >> end,
+        "begin << <<(X+Y):64>>|| a=b=X <- [1,2] && <<Y>> <= <<1,2>> >> end.",
+        <<>>),
+    check(fun() ->
+         << <<(X+V):64>>|| a=b=X <- [1,2] && _K:=V <- #{a=>1,b=>2}>> end,
+        "begin << <<(X+V):64>>|| a=b=X <- [1,2] && _K:=V <- #{a=>1,b=>2}>> end.",
+        <<>>),
+    error_check("begin << <<(X+Y):8>> || <<X:b>> <= <<1,2>> && <<Y>> <= <<1,2>> >> end.",
+        {bad_generators,{<<>>,<<1,2>>}}),
+    error_check("begin << <<X>> || <<X>> <= a && Y <- [1]>> end.",{bad_generators,{a,[1]}}),
+    error_check("begin
+        X = 32, Y = 32,
+                << <<(X+Y)/integer>> || X <- [1,2] && Y <- [1,2,3,4]>>
+                    end.",
+    {bad_generators,{[],[3,4]}}),
+    error_check("begin << <<X:8>> || X <- [1] && Y <- a && <<Z>> <= <<2>> >> end.",
+        {bad_generators,{[1], a, <<2>>}}),
+    ok.
+
+zmc(Config) when is_list(Config) ->
+    check(fun() ->
+                  [{a,b,1,3}]
+          end,
+          "begin
+        M1 = #{a=>1}, M2 = #{b=>3},
+          [{K1, K2, V1, V2} || K1 := V1 <- M1 && K2 := V2 <- M2]
+          end.",
+    [{a,b,1,3}]),
+    check(fun() ->
+                  [A * 4 || A <- lists:seq(1, 50)]
+          end,
+          "begin
+        Seq = lists:seq(1, 50),
+          M1 = maps:iterator(#{X=>X || X <- Seq}, ordered),
+          M2 = maps:iterator(#{X=>X || X <- lists:seq(1,50)}, ordered),
+          [X+Y+Z+W || X := Y <- M1 && Z := W <- M2]
+          end.",
+    [A * 4 || A <- lists:seq(1, 50)]),
+    check(fun() ->
+                  [{A, A*3, A*2, A*4} || A <- lists:seq(1, 50)]
+          end,
+          "begin
+        Seq = lists:seq(1, 50),
+          M3 = maps:iterator(#{X=>X*3 || X <- Seq}, ordered),
+          M4 = maps:iterator(#{X*2=>X*4 || X <- Seq}, ordered),
+          [{X, Y, Z, W} || X := Y <- M3 && Z := W <- M4]
+          end.",
+    [{A, A*3, A*2, A*4} || A <- lists:seq(1, 50)]),
+    check(fun() ->
+                  #{K1 => V1+V2 || K1:=V1 <:- #{a=>1} && _K2:=V2 <- #{b=>3}}
+          end,
+          "begin
+        #{K1 => V1+V2 || K1:=V1 <- #{a=>1} && _K2:=V2 <- #{b=>3}}
+          end.",
+    #{a=>4}),
+    check(fun() ->
+                  #{K=>V || a := b <- #{x => y} && K := V <- #{x => y}}
+          end,
+          "begin
+        #{K=>V || a := b <- #{x => y} && K := V <- #{x => y}}
+          end.",
+    #{}),
+    error_check("begin
+        #{K1 => V1+V2 || K1:=V1 <- #{a=>1} &&
+                             _K2:=V2 <- maps:iterator(#{b=>3,c=>4}, ordered)}
+                end.",
+    {bad_generators,{#{},#{c=>4}}}),
+    error_check("begin #{X=>Y || X <- [1] && Y <- a && K1:=V1 <- #{b=>3}} end.",
+        {bad_generators,{[1], a, #{b=>3}}}),
+    ok.
+
 %% Simple cases, just to cover some code.
 simple_cases(Config) when is_list(Config) ->
     check(fun() -> A = $C end, "A = $C.", $C),
diff --git a/lib/stdlib/test/erl_expand_records_SUITE.erl b/lib/stdlib/test/erl_expand_records_SUITE.erl
index fe055e03f7..e51044febf 100644
--- a/lib/stdlib/test/erl_expand_records_SUITE.erl
+++ b/lib/stdlib/test/erl_expand_records_SUITE.erl
@@ -39,7 +39,7 @@
 -export([attributes/1, expr/1, guard/1,
          init/1, pattern/1, strict/1, update/1,
 	 otp_5915/1, otp_7931/1, otp_5990/1,
-	 otp_7078/1, maps/1,
+	 otp_7078/1, maps/1, zlc/1,
          side_effects/1]).
 
 init_per_testcase(_Case, Config) ->
@@ -55,7 +55,7 @@ suite() ->
 all() -> 
     [attributes, expr, guard, init,
      pattern, strict, update, maps,
-     side_effects, {group, tickets}].
+     side_effects, zlc, {group, tickets}].
 
 groups() -> 
     [{tickets, [],
@@ -459,6 +459,15 @@ maps(Config) when is_list(Config) ->
     run(Config, Ts, [strict_record_tests]),
     ok.
 
+zlc(Config) when is_list(Config) ->
+    Ts = [<<"-record(rr, {a,b,c}).
+             t() -> R0 = id(#rr{a=[{X,Y}||X <- [1,2] && Y <- [a,b]]}),
+             ok.
+             id(X) -> X.
+            ">>],
+    run(Config, Ts, [strict_record_tests]),
+    ok.
+
 %% Strict record tests in guards.
 otp_5915(Config) when is_list(Config) ->
     %% These tests are also run by the compiler's record_SUITE.
diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl
index b515191639..f6b143a838 100644
--- a/lib/stdlib/test/erl_lint_SUITE.erl
+++ b/lib/stdlib/test/erl_lint_SUITE.erl
@@ -87,7 +87,8 @@
          match_float_zero/1,
          undefined_module/1,
          update_literal/1,
-         messages_with_jaro_suggestions/1]).
+         messages_with_jaro_suggestions/1,
+         illegal_zip_generator/1]).
 
 suite() ->
     [{ct_hooks,[ts_install_cth]},
@@ -123,7 +124,8 @@ all() ->
      match_float_zero,
      undefined_module,
      update_literal,
-     messages_with_jaro_suggestions].
+     messages_with_jaro_suggestions,
+     illegal_zip_generator].
 
 groups() -> 
     [{unused_vars_warn, [],
@@ -5451,6 +5453,24 @@ messages_with_jaro_suggestions(Config) ->
 
     ok.
 
+illegal_zip_generator(Config) ->
+    Ts = [{not_generator,
+           <<"-compile({nowarn_unused_function,[{foo,0}]}).
+            foo() -> [X + Y || X <- [1,2,3,4] && Y <- [5,6,7] && X > 1].
+            ">>,
+           {[]},
+           {errors,[{{2,68},erl_lint,illegal_zip_generator}],[]}},
+           {not_generator,
+           <<"-compile({nowarn_unused_function,[{bar,0}]}).
+            bar() -> [X + Y || X <- [[1,2],[3,4]] && lists:sum(X) > 0 && Y <- [5,6,7]].
+            ">>,
+           {[]},
+           {errors,[{{2,67},erl_lint,illegal_zip_generator}],[]}}
+           ],
+    [] = run(Config,Ts),
+
+    ok.
+
 %%%
 %%% Common utilities.
 %%%
diff --git a/lib/syntax_tools/src/erl_prettypr.erl b/lib/syntax_tools/src/erl_prettypr.erl
index bcb9982f31..1d7bd31883 100644
--- a/lib/syntax_tools/src/erl_prettypr.erl
+++ b/lib/syntax_tools/src/erl_prettypr.erl
@@ -856,6 +856,12 @@ lay_2(Node, Ctxt) ->
 	    D2 = lay(erl_syntax:strict_map_generator_body(Node), Ctxt1),
 	    par([D1, beside(text("<:- "), D2)], Ctxt1#ctxt.break_indent);
 
+	zip_generator ->
+	    Ctxt1 = reset_prec(Ctxt),
+	    par(seq(erl_syntax:zip_generator_body(Node),
+			 floating(text("&&")), Ctxt1,
+			 fun lay/2));
+
 	implicit_fun ->
 	    D = lay(erl_syntax:implicit_fun_name(Node),
 		    reset_prec(Ctxt)),
diff --git a/lib/syntax_tools/src/erl_syntax.erl b/lib/syntax_tools/src/erl_syntax.erl
index 82ca6a8e9e..e1ec49ab82 100644
--- a/lib/syntax_tools/src/erl_syntax.erl
+++ b/lib/syntax_tools/src/erl_syntax.erl
@@ -360,6 +360,8 @@ trees.
 	 variable_literal/1,
 	 warning_marker/1,
 	 warning_marker_info/1,
+	 zip_generator/1,
+	 zip_generator_body/1,
 
 	 tree/1,
 	 tree/2,
@@ -467,6 +469,7 @@ trees.
                    | erl_parse:form_info()
                    | erl_parse:af_binelement(term())
                    | erl_parse:af_generator()
+                   | erl_parse:af_zip_generator()
                    | erl_parse:af_remote_function().
 
 %% The representation built by the Erlang standard library parser
@@ -558,6 +561,7 @@ reason `badarg`. Node types currently defined by this module are:
 * `user_type_application`
 * `variable`
 * `warning_marker`
+* `zip_generator`
 
 The user may (for special purposes) create additional nodes with other type
 tags, using the `tree/2` function.
@@ -582,7 +586,7 @@ _See also: _`annotated_type/2`, `application/3`, `arity_qualifier/2`, `atom/1`,
 `size_qualifier/2`, `string/1`, `text/1`, `tree/2`, `try_expr/3`, `tuple/1`,
 `tuple_type/0`, `tuple_type/1`, `type_application/2`, `type_union/1`,
 `typed_record_field/2`, `underscore/0`, `user_type_application/2`, `variable/1`,
-`warning_marker/1`.
+`warning_marker/1`,`zip_generator/1`.
 """.
 -spec type(syntaxTree()) -> atom().
 
@@ -635,6 +639,7 @@ type(Node) ->
 	{generate_strict, _, _, _} -> strict_generator;
 	{m_generate, _, _, _} -> map_generator;
 	{m_generate_strict, _, _, _} -> strict_map_generator;
+	{zip,_,_} -> zip_generator;
 	{lc, _, _, _} -> list_comp;
 	{bc, _, _, _} -> binary_comp;
 	{mc, _, _, _} -> map_comp;
@@ -6158,6 +6163,48 @@ strict_map_generator_body(Node) ->
     end.
 
 
+-record(zip_generator, {body :: [syntaxTree()]}).
+
+-doc """
+Creates an abstract zip_generator.
+
+The result represents `G1 && ... Gn`, where each `G` is a generator.
+
+_See also: _`binary_comp/2`, `list_comp/2`, `map_comp/2`, `map_generator_body/1`,
+`map_generator_pattern/1`.
+""".
+-spec zip_generator([syntaxTree()]) -> syntaxTree().
+
+%% `erl_parse' representation:
+%%
+%% {zip, Pos, Body}
+%%
+%%	Body = erl_parse()
+
+zip_generator(Body) ->
+    tree(zip_generator, #zip_generator{body = Body}).
+
+revert_zip_generator(Node) ->
+    Pos = get_pos(Node),
+    Body = zip_generator_body(Node),
+    {zip, Pos, Body}.
+
+
+-doc """
+Returns the body subtree of a `zip_generator` node.
+
+_See also: _`zip_generator/1`.
+""".
+-spec zip_generator_body(syntaxTree()) -> syntaxTree().
+
+zip_generator_body(Node) ->
+    case unwrap(Node) of
+	{zip, _, Body} ->
+	    Body;
+	Node1 ->
+	    (data(Node1))#zip_generator.body
+    end.
+
 %% =====================================================================
 
 -doc """
@@ -7577,6 +7624,8 @@ revert_root(Node) ->
 	    revert_variable(Node);
 	warning_marker ->
 	    revert_warning_marker(Node);
+	zip_generator ->
+	    revert_zip_generator(Node);
 	_ ->
 	    %% Non-revertible new-form node
 	    Node
@@ -7934,7 +7983,9 @@ subtrees(T) ->
                      [typed_record_field_type(T)]];
                 user_type_application ->
                     [[user_type_application_name(T)],
-                     user_type_application_arguments(T)]
+                     user_type_application_arguments(T)];
+		zip_generator ->
+		    [zip_generator_body(T)]
 	    end
     end.
 
@@ -8053,7 +8104,8 @@ make_tree(tuple_type, [Es]) -> tuple_type(Es);
 make_tree(type_application, [[N], Ts]) -> type_application(N, Ts);
 make_tree(type_union, [Es]) -> type_union(Es);
 make_tree(typed_record_field, [[F],[T]]) -> typed_record_field(F, T);
-make_tree(user_type_application, [[N], Ts]) -> user_type_application(N, Ts).
+make_tree(user_type_application, [[N], Ts]) -> user_type_application(N, Ts);
+make_tree(zip_generator, [Ts]) -> zip_generator(Ts).
 
 
 -doc """
diff --git a/lib/syntax_tools/src/erl_syntax_lib.erl b/lib/syntax_tools/src/erl_syntax_lib.erl
index 670b40961a..ea0e67ec7a 100644
--- a/lib/syntax_tools/src/erl_syntax_lib.erl
+++ b/lib/syntax_tools/src/erl_syntax_lib.erl
@@ -502,6 +502,8 @@ vann(Tree, Env) ->
             vann_map_generator(Tree, Env);
         strict_map_generator ->
             vann_strict_map_generator(Tree, Env);
+        zip_generator ->
+            vann_zip_generator(Tree, Env);
         block_expr ->
             vann_block_expr(Tree, Env);
         macro ->
@@ -646,6 +648,8 @@ vann_list_comp_body_join() ->
                                           vann_map_generator(T,Env);
                                       strict_map_generator ->
                                           vann_strict_map_generator(T,Env);
+                                      zip_generator ->
+                                          vann_zip_generator(T,Env);
                                       _ ->
                                           %% Bindings in filters are not
                                           %% exported to the rest of the
@@ -690,6 +694,8 @@ vann_binary_comp_body_join() ->
                                           vann_map_generator(T,Env);
                                       strict_map_generator ->
                                           vann_strict_map_generator(T,Env);
+                                      zip_generator ->
+                                          vann_zip_generator(T,Env);
                                       _ ->
                                           %% Bindings in filters are not
                                           %% exported to the rest of the
@@ -708,6 +714,25 @@ vann_binary_comp_body(Ts, Env) ->
     {Ts1, {_, Bound, Free}} = lists:mapfoldl(F, {Env, [], []}, Ts),
     {Ts1, {Bound, Free}}.
 
+vann_zip_generator_body_join() ->
+    fun (T, {Env, Bound, Free}) ->
+            {T1, Bound1, Free1} = case erl_syntax:type(T) of
+                                      binary_generator ->
+                                          vann_binary_generator(T, Env);
+                                      generator ->
+                                          vann_generator(T, Env)
+                                  end,
+            Env1 = ordsets:union(Env, Bound1),
+            {T1, {Env1, ordsets:union(Bound, Bound1),
+                  ordsets:union(Free,
+                                ordsets:subtract(Free1, Bound))}}
+    end.
+
+vann_zip_generator_body(Ts, Env) ->
+    F = vann_zip_generator_body_join(),
+    {Ts1, {_, Bound, Free}} = lists:mapfoldl(F, {Env, [], []}, Ts),
+    {Ts1, {Bound, Free}}.
+
 %% In list comprehension generators, the pattern variables are always
 %% viewed as new occurrences, shadowing whatever is in the input
 %% environment (thus, the pattern contains no variable uses, only
@@ -761,6 +786,13 @@ vann_strict_map_generator(Tree, Env) ->
     Tree1 = rewrite(Tree, erl_syntax:strict_map_generator(P1, E1)),
     {ann_bindings(Tree1, Env, Bound, Free), Bound, Free}.
 
+vann_zip_generator(Tree, Env) ->
+    Es = erl_syntax:zip_generator_body(Tree),
+    {Es1, {Bound, Free}} = vann_zip_generator_body(Es, Env),
+    Env1 = ordsets:union(Env, Bound),
+    Tree1 = rewrite(Tree, erl_syntax:zip_generator(Es1)),
+    {ann_bindings(Tree1, Env1, Bound, Free), Bound, Free}.
+
 vann_block_expr(Tree, Env) ->
     Es = erl_syntax:block_expr_body(Tree),
     {Es1, {Bound, Free}} = vann_body(Es, Env),
diff --git a/lib/syntax_tools/test/syntax_tools_SUITE.erl b/lib/syntax_tools/test/syntax_tools_SUITE.erl
index 87aa5cc810..0ddbbb2ebd 100644
--- a/lib/syntax_tools/test/syntax_tools_SUITE.erl
+++ b/lib/syntax_tools/test/syntax_tools_SUITE.erl
@@ -340,6 +340,12 @@ t_erl_parse_type(Config) when is_list(Config) ->
 		     {"#{K => V || {K,V} <- KVs}", map_comp,false},
 		     {"#{K => V || {K,V} <:- KVs}", map_comp,false},
 		     {"#{K => (catch V) || {K,V} <- KVs}", map_comp,false},
+                     {"[V+W||V <- Vs && W <- Ws]", list_comp,false},
+                     {"[catch V+W||V <- Vs && W <- Ws]", list_comp,false},
+                     {"<< <<B>> || <<B>> <= Bs>>", binary_comp,false},
+                     {"<< (catch <<B>>) || <<B>> <= Bs>>", binary_comp,false},
+                     {"<< <<B:8,C:8>> || <<B>> <= Bs && <<C>> <= Cs>>", binary_comp,false},
+		     {"<< (catch <<B:8,C:8>>) || <<B>> <= Bs && <<C>> <= Cs>>", binary_comp,false},
 		     {"#state{ a = A, b = B}", record_expr,false},
 		     {"#state{}", record_expr,false},
 		     {"#s{ a = #def{ a=A }, b = B}", record_expr,false},
diff --git a/lib/syntax_tools/test/syntax_tools_SUITE_data/syntax_tools_SUITE_test_module.erl b/lib/syntax_tools/test/syntax_tools_SUITE_data/syntax_tools_SUITE_test_module.erl
index 9551e2641e..ddf06a7597 100644
--- a/lib/syntax_tools/test/syntax_tools_SUITE_data/syntax_tools_SUITE_test_module.erl
+++ b/lib/syntax_tools/test/syntax_tools_SUITE_data/syntax_tools_SUITE_test_module.erl
@@ -597,4 +597,11 @@ strict_generators() ->
     [X+1 || <<X>> <:= <<1,2,3>>],
     [X*Y || X := Y <:- #{1 => 2, 3 => 4}],
 
-    ok.
\ No newline at end of file
+    ok.
+
+%% EEP-73: Zip generators.
+eep73() ->
+    [{X,Y}||X <- [1,2,3] && Y <- [2,2,2]],
+    [{X,Y}||X <- [1,2,3] && <<Y>> <= <<2,2,2>>],
+    [{K1,K2,V1,V2}|| K1 := V1 <- #{a=>1} && K2 := V2 <- #{b=>3}],
+    ok.
diff --git a/lib/tools/emacs/erlang.el b/lib/tools/emacs/erlang.el
index c1f92b9bd2..f582c198ad 100644
--- a/lib/tools/emacs/erlang.el
+++ b/lib/tools/emacs/erlang.el
@@ -1186,7 +1186,7 @@ behaviour.")
 
 (defvar erlang-font-lock-keywords-lc
   (list
-   (list "\\(<-\\|<:-\\|<=\\|<:=\\|||\\)\\(\\s \\|$\\)" 1 'font-lock-keyword-face))
+   (list "\\(<-\\|<:-\\|<=\\|<:=\\|||\\|&&\\)\\(\\s \\|$\\)" 1 'font-lock-keyword-face))
   "Font lock keyword highlighting list comprehension operators.")
 
 (defvar erlang-font-lock-keywords-keywords
@@ -1513,7 +1513,7 @@ Other commands:
          ))
     (add-to-list 'align-rules-list
       `(erlang-generator-arrows
-         (regexp   . ,(concat space-group "\\(<-\\|<:-\\|<=\\|<:=\\)" space-group))
+         (regexp   . ,(concat space-group "\\(<-\\|<:-\\|<=\\|<:=\\|&&\\)" space-group))
          (group    . (1 3))
          (separate . ,(concat "\\(||\\|" erl-sep-forms "\\|" erl-sep-symbols "\\)"))
          (repeat   . t)
-- 
2.43.0

openSUSE Build Service is sponsored by