File 5609-v3_core-Handle-map-comprehensions.patch of Package erlang

From 0de6bc8226e78326373aabb93ea9654ed44bb837 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= <bjorn@erlang.org>
Date: Tue, 17 Jan 2023 08:58:23 +0100
Subject: [PATCH 09/12] v3_core: Handle map comprehensions

---
 lib/compiler/src/v3_core.erl           | 191 ++++++++++++-----
 lib/compiler/test/Makefile             |   3 +
 lib/compiler/test/bs_bincomp_SUITE.erl |   2 +
 lib/compiler/test/lc_SUITE.erl         |   8 +
 lib/compiler/test/mc_SUITE.erl         | 272 +++++++++++++++++++++++++
 lib/compiler/test/record_SUITE.erl     |   8 +
 6 files changed, 430 insertions(+), 54 deletions(-)
 create mode 100644 lib/compiler/test/mc_SUITE.erl

diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl
index eb337672ff..cedb162eef 100644
--- a/lib/compiler/src/v3_core.erl
+++ b/lib/compiler/src/v3_core.erl
@@ -120,7 +120,8 @@
 -record(itry,      {anno=#a{},args,vars,body,evars,handler}).
 -record(ifilter,   {anno=#a{},arg}).
 -record(igen,      {anno=#a{},acc_pat,acc_guard,
-		    skip_pat,tail,tail_pat,arg}).
+                    skip_pat,tail,tail_pat,arg,
+                    refill={nomatch,ignore}}).
 -record(isimple,   {anno=#a{},term :: cerl:cerl()}).
 
 -type iapply()    :: #iapply{}.
@@ -691,6 +692,9 @@ expr({lc,L,E,Qs0}, St0) ->
     lc_tq(L, E, Qs1, #c_literal{anno=lineno_anno(L, St1),val=[]}, St1);
 expr({bc,L,E,Qs}, St) ->
     bc_tq(L, E, Qs, St);
+expr({mc,L,E,Qs0}, St0) ->
+    {Qs1,St1} = preprocess_quals(L, Qs0, St0),
+    mc_tq(L, E, Qs1, #c_literal{anno=lineno_anno(L, St1),val=[]}, St1);
 expr({tuple,L,Es0}, St0) ->
     {Es1,Eps,St1} = safe_list(Es0, St0),
     A = record_anno(L, St1),
@@ -1594,37 +1598,31 @@ fun_tq(Cs0, L, St0, NameInfo) ->
     {Fun,[],St4}.
 
 %% lc_tq(Line, Exp, [Qualifier], Mc, State) -> {LetRec,[PreExp],State}.
-%%  This TQ from Simon PJ pp 127-138.  
+%%  This TQ from Simon PJ pp 127-138.
 
 lc_tq(Line, E, [#igen{anno=#a{anno=GA}=GAnno,
 		      acc_pat=AccPat,acc_guard=AccGuard,
                       skip_pat=SkipPat,tail=Tail,tail_pat=TailPat,
+                      refill={RefillPat,RefillAction},
                       arg={Pre,Arg}}|Qs], Mc, St0) ->
     {Name,St1} = new_fun_name("lc", St0),
     LA = lineno_anno(Line, St1),
-    LAnno = #a{anno=LA},
     F = #c_var{anno=LA,name={Name,1}},
     Nc = #iapply{anno=GAnno,op=F,args=[Tail]},
     {[FcVar,Var],St2} = new_vars(2, St1),
     Fc = bad_generator([FcVar], FcVar, Arg),
-    SkipClause = #iclause{anno=#a{anno=[skip_clause,compiler_generated|LA]},
-                          pats=[SkipPat],guard=[],body=[Nc]},
-    TailClause = #iclause{anno=LAnno,pats=[TailPat],guard=[],body=[Mc]},
-    {Cs,St4} = case {AccPat,SkipPat} of
-                   {nomatch,nomatch} ->
-                       {[TailClause],St2};
-                   {nomatch,_} ->
-                       {[SkipClause,TailClause],St2};
-                   _ ->
-                       {Lc,Lps,St3} = lc_tq(Line, E, Qs, Nc, St2),
-                       AccClause = #iclause{anno=LAnno,pats=[AccPat],guard=AccGuard,
-                                            body=Lps ++ [Lc]},
-                       {[AccClause,SkipClause,TailClause],St3}
-               end,
+    SkipClause = make_clause([skip_clause,compiler_generated|LA],
+                             SkipPat, [], [], [Nc]),
+    TailClause = make_clause(LA, TailPat, [], [], [Mc]),
+    {Lc,Lps,St3} = lc_tq(Line, E, Qs, Nc, St2),
+    AccClause = make_clause(LA, AccPat, [], AccGuard, Lps ++ [Lc]),
+    RefillClause = make_clause(LA, RefillPat, [], [], [RefillAction,Nc]),
+    Cs0 = [AccClause,SkipClause,TailClause,RefillClause],
+    Cs = [C || C <- Cs0, C =/= nomatch],
     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]}]},
-     [],St4};
+     [],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) ->
@@ -1659,6 +1657,7 @@ bc_tq(Line, Exp, Qs0, St0) ->
 bc_tq1(Line, E, [#igen{anno=GAnno,
 		       acc_pat=AccPat,acc_guard=AccGuard,
                        skip_pat=SkipPat,tail=Tail,tail_pat=TailPat,
+                       refill={RefillPat,RefillAction},
                        arg={Pre,Arg}}|Qs], Mc, St0) ->
     {Name,St1} = new_fun_name("lbc", St0),
     LA = lineno_anno(Line, St1),
@@ -1669,30 +1668,23 @@ bc_tq1(Line, E, [#igen{anno=GAnno,
     F = #c_var{anno=LA,name={Name,2}},
     Nc = #iapply{anno=GAnno,op=F,args=[Tail,AccVar]},
     Fc = bad_generator(FcVars, hd(FcVars), Arg),
-    SkipClause = #iclause{anno=#a{anno=[compiler_generated,skip_clause|LA]},
-                          pats=[SkipPat,IgnoreVar],guard=[],body=[Nc]},
-    TailClause = #iclause{anno=LAnno,pats=[TailPat,IgnoreVar],guard=[],
-                          body=[AccVar]},
-    {Cs,St} = case {AccPat,SkipPat} of
-                  {nomatch,nomatch} ->
-                      {[TailClause],St4};
-                  {nomatch,_} ->
-                      {[SkipClause,TailClause],St4};
-                  {_,_} ->
-                      {Bc,Bps,St5} = bc_tq1(Line, E, Qs, AccVar, St4),
-                      Body = Bps ++ [#iset{var=AccVar,arg=Bc},Nc],
-                      AccClause = #iclause{anno=LAnno,pats=[AccPat,IgnoreVar],
-                                           guard=AccGuard,body=Body},
-                      {[AccClause,SkipClause,TailClause],St5}
-              end,
-    Fun = #ifun{anno=LAnno,id=[],vars=Vars,clauses=Cs,fc=Fc},
+    SkipClause = make_clause([compiler_generated,skip_clause|LA],
+                             SkipPat, [IgnoreVar], [], [Nc]),
+    TailClause = make_clause(LA, TailPat, [IgnoreVar], [], [AccVar]),
+    {Bc,Bps,St5} = bc_tq1(Line, E, Qs, AccVar, St4),
+    Body = Bps ++ [#iset{var=AccVar,arg=Bc},Nc],
+    AccClause = make_clause(LA, AccPat, [IgnoreVar], AccGuard, Body),
+    RefillClause = make_clause(LA, RefillPat, [AccVar], [], [RefillAction,Nc]),
+    Cs0 = [AccClause,SkipClause,TailClause,RefillClause],
+    Cs = [C || C <- Cs0, C =/= nomatch],
+    Fun = #ifun{anno=GAnno,id=[],vars=Vars,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,2},Fun}],
               body=Pre ++ [#iapply{anno=LAnno,op=F,args=[Arg,Mc]}]},
-     [],St};
+     [],St5};
 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) ->
@@ -1729,6 +1721,20 @@ 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}.
 
+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),
+    {LcVar,St2} = new_var(St1),
+    Pre = Pre0 ++ [#iset{var=LcVar,arg=Lc}],
+    Call = #icall{module=#c_literal{val=maps},
+                  name=#c_literal{val=from_list},
+                  args=[LcVar]},
+    {Call,Pre,St2}.
+
+make_clause(_Anno, nomatch, _PatExtra, _Guard, _Body) ->
+    nomatch;
+make_clause(Anno, Pat, PatExtra, Guard, Body) ->
+    #iclause{anno=#a{anno=Anno},pats=[Pat|PatExtra],guard=Guard,body=Body}.
 
 %% filter_tq(Line, Expr, Filter, Mc, State, [Qualifier], TqFun) ->
 %%     {Case,[PreExpr],State}.
@@ -1805,6 +1811,7 @@ preprocess_quals(_, [], St, Acc) ->
 
 is_generator({generate,_,_,_}) -> true;
 is_generator({b_generate,_,_,_}) -> true;
+is_generator({m_generate,_,_,_}) -> true;
 is_generator(_) -> false.
 
 %% Retrieve the annotation from an Erlang AST form.
@@ -1813,7 +1820,7 @@ is_generator(_) -> false.
 get_qual_anno(Abstract) -> element(2, Abstract).
 
 %%
-%% Generators are abstracted as sextuplets:
+%% Generators are abstracted as a record #igen{}:
 %%  - acc_pat is the accumulator pattern, e.g. [Pat|Tail] for Pat <- Expr.
 %%  - acc_guard is the list of guards immediately following the current
 %%    generator in the qualifier list input.
@@ -1823,6 +1830,8 @@ get_qual_anno(Abstract) -> element(2, Abstract).
 %%    generator input.
 %%  - tail_pat is the tail pattern, respectively [] and <<_/bitstring>> for list
 %%    and bit string generators.
+%%  - refill is a pair {RefillPat,RefillAction}, used to refill the iterator
+%%    argument (used by map generators).
 %%  - arg is a pair {Pre,Arg} where Pre is the list of expressions to be
 %%    inserted before the comprehension function and Arg is the expression
 %%    that it should be passed.
@@ -1837,22 +1846,13 @@ generator(Line, {generate,Lg,P0,E}, Gs, St0) ->
     {Head,St1} = list_gen_pattern(P0, Line, St0),
     {[Tail,Skip],St2} = new_vars(2, St1),
     {Cg,St3} = lc_guard_tests(Gs, St2),
-    {AccPat,SkipPat} = case Head of
-                           #c_var{} ->
-                               %% If the generator pattern is a variable, the
-                               %% pattern from the accumulator clause can be
-                               %% reused in the skip one. lc_tq and bc_tq1 takes
-                               %% care of dismissing the latter in that case.
-                               Cons = ann_c_cons(LA, Head, Tail),
-                               {Cons,Cons};
-                           nomatch ->
-                               %% If it never matches, there is no need for
-                               %% an accumulator clause.
-                               {nomatch,ann_c_cons(LA, Skip, Tail)};
-                           _ ->
-                               {ann_c_cons(LA, Head, Tail),
-                                ann_c_cons(LA, Skip, Tail)}
-                       end,
+    AccPat = case Head of
+                 nomatch ->
+                     nomatch;
+                 _ ->
+                     ann_c_cons(LA, Head, Tail)
+             end,
+    SkipPat = ann_c_cons(LA, Skip, Tail),
     {Ce,Pre,St4} = safe(E, St3),
     Gen = #igen{anno=#a{anno=GA},
 		acc_pat=AccPat,acc_guard=Cg,skip_pat=SkipPat,
@@ -1885,7 +1885,90 @@ generator(Line, {b_generate,Lg,P,E}, Gs, St0) ->
                         tail_pat=#c_var{name='_'},
                         arg={Pre,Ce}},
             {Gen,St1}
-    end.
+    end;
+generator(Line, {m_generate,Lg,{map_field_exact,_,K0,V0},E}, Gs, St0) ->
+    %% Consider this example:
+    %%
+    %%   [{K,V} || K := V <- L].
+    %%
+    %% The following Core Erlang code will be generated:
+    %%
+    %% letrec
+    %%     'lc$^0'/1 =
+    %%         fun (Iter0) ->
+    %%             case Iter0 of
+    %%               <{K,V,NextIter}> when 'true' ->
+    %%                   let <Tail> =
+    %%                       apply 'lc$^0'/1(NextIter)
+    %%                   in [{K,V}|Tail]
+    %%               <{_K,_V,NextIter}> when 'true' ->
+    %%                   %% Skip clause; will be optimized away later
+    %%                   %% since there are no filters.
+    %%                   apply 'lc$^0'/1(NextIter)
+    %%               <'none'> when 'true' ->
+    %%                   []
+    %%               <Iter> when 'true' ->
+    %%                   let NextIter =
+    %%                       call 'erts_internal':'mc_refill'(Iter)
+    %%                   in apply 'lc$^0'/1(NextIter)
+    %%               <Bad> when 'true' ->
+    %%                     %% Generated by lc_tq/5. Never reached;
+    %%                     %% will be optimized away.
+    %%                     call 'erlang':'error'({'bad_generator',Bad})
+    %%             end
+    %% in let <Iter> =
+    %%         case call 'erts_internal':'mc_iterator'(L) of
+    %%           <[]> when 'true' ->
+    %%                 call 'erlang':'error'
+    %%                     ({'bad_generator',L})
+    %%           <Iter0> when 'true' ->
+    %%               Iter0
+    %%         end
+    %%     in apply 'lc$^0'/1(Iter0)
+    LA = lineno_anno(Line, St0),
+    GA = lineno_anno(Lg, St0),
+    {Pat,St1} = list_gen_pattern({cons,Lg,K0,V0}, Line, St0),
+    {[SkipK,SkipV,IterVar,OuterIterVar,_BadGenVar],St2} = new_vars(5, St1),
+    {Cg,St3} = lc_guard_tests(Gs, St2),
+    {Ce,Pre0,St4} = safe(E, St3),
+    AccPat = case Pat of
+                 #c_cons{hd=K,tl=V} ->
+                     #c_tuple{es=[K,V,IterVar]};
+                 nomatch ->
+                     nomatch
+             end,
+    SkipPat = #c_tuple{es=[SkipK,SkipV,IterVar]},
+
+    Refill = {SkipK,
+              #iset{var=IterVar,
+                    arg=#icall{anno=#a{anno=GA},
+                               module=#c_literal{val=erts_internal},
+                               name=#c_literal{val=mc_refill},
+                               args=[SkipK]}}},
+
+    InitIter = #icall{anno=#a{anno=GA},
+                      module=#c_literal{val=erts_internal},
+                      name=#c_literal{val=mc_iterator},
+                      args=[Ce]},
+
+    BadGenerator = bad_generator([#c_literal{val=[]}], Ce,
+                                 #c_literal{val=[],anno=GA}),
+    BeforeFc = #iclause{anno=#a{anno=GA},
+                        pats=[IterVar],
+                        guard=[],
+                        body=[IterVar]},
+    Before = #iset{var=OuterIterVar,
+                   arg=#icase{args=[InitIter],
+                              clauses=[BadGenerator],
+                              fc=BeforeFc}},
+
+    Pre = Pre0 ++ [Before],
+    Gen = #igen{anno=#a{anno=GA},
+		acc_pat=AccPat,acc_guard=Cg,skip_pat=SkipPat,
+                tail=IterVar,tail_pat=#c_literal{anno=LA,val=none},
+                refill=Refill,
+                arg={Pre,OuterIterVar}},
+    {Gen,St4}.
 
 append_tail_segment(Segs, St0) ->
     {Var,St} = new_var(St0),
diff --git a/lib/compiler/test/Makefile b/lib/compiler/test/Makefile
index 3a585fc10e..19720abe50 100644
--- a/lib/compiler/test/Makefile
+++ b/lib/compiler/test/Makefile
@@ -41,6 +41,7 @@ MODULES= \
 	map_SUITE \
 	match_SUITE \
 	maybe_SUITE \
+	mc_SUITE \
 	misc_SUITE \
 	overridden_bif_SUITE \
 	random_code_SUITE \
@@ -76,6 +77,7 @@ NO_OPT= \
 	map \
 	match \
 	maybe \
+        mc \
 	misc \
 	overridden_bif \
 	receive \
@@ -103,6 +105,7 @@ INLINE= \
 	map \
 	match \
 	maybe \
+	mc \
 	misc \
 	overridden_bif \
 	receive \
diff --git a/lib/compiler/test/bs_bincomp_SUITE.erl b/lib/compiler/test/bs_bincomp_SUITE.erl
index d6718c6e9c..b560203514 100644
--- a/lib/compiler/test/bs_bincomp_SUITE.erl
+++ b/lib/compiler/test/bs_bincomp_SUITE.erl
@@ -626,6 +626,8 @@ cs(Bin) ->
             ok;
         bs_bincomp_no_ssa_opt_SUITE ->
             ok;
+        bs_bincomp_no_copt_SUITE ->
+            ok;
         bs_bincomp_post_opt_SUITE ->
             ok;
         _ ->
diff --git a/lib/compiler/test/lc_SUITE.erl b/lib/compiler/test/lc_SUITE.erl
index 7d30f1c74f..ab683f4de1 100644
--- a/lib/compiler/test/lc_SUITE.erl
+++ b/lib/compiler/test/lc_SUITE.erl
@@ -124,6 +124,11 @@ basic(Config) when is_list(Config) ->
                        [{file,"bad_lc.erl"},{line,7}]}|_]}} =
                 (catch id(bad_generator_bc(a))),
 
+            {'EXIT',{{bad_generator,a},
+                     [{?MODULE,_,_,
+                       [{file,"bad_lc.erl"},{line,10}]}|_]}} =
+                (catch id(bad_generator_mc(a))),
+
             %% List comprehensions with improper lists.
             {'EXIT',{{bad_generator,d},
                      [{?MODULE,_,_,
@@ -273,3 +278,6 @@ bad_generator(List) ->                          %Line 2
 bad_generator_bc(List) ->                       %Line 5
     << <<I:4>> ||                               %Line 6
         I <- List>>.                            %Line 7
+bad_generator_mc(List) ->                       %Line 8
+    #{I => ok ||                                %Line 9
+        I <- List}.                             %Line 10
diff --git a/lib/compiler/test/mc_SUITE.erl b/lib/compiler/test/mc_SUITE.erl
new file mode 100644
index 0000000000..21aa2c017e
--- /dev/null
+++ b/lib/compiler/test/mc_SUITE.erl
@@ -0,0 +1,272 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2023. 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%
+%%
+%% Originally based on Per Gustafsson's test suite.
+%%
+
+-module(mc_SUITE).
+
+-export([all/0,suite/0,groups/0,init_per_suite/1,end_per_suite/1,
+	 init_per_group/2,end_per_group/2,
+         basic/1,duplicate_keys/1,mixed/1,
+         shadow/1,bad_generators/1]).
+
+-include_lib("common_test/include/ct.hrl").
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+    [{group,p}].
+
+groups() ->
+    [{p,test_lib:parallel(),
+      [basic,
+       duplicate_keys,
+       mixed,
+       shadow,
+       bad_generators]}].
+
+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.
+
+basic(_Config) ->
+    mc_double(0),
+    mc_double(1),
+    mc_double(2),
+    mc_double(3),
+    mc_double(4),
+    mc_double(5),
+
+    mc_double(17),
+    mc_double(18),
+    mc_double(19),
+
+    mc_double(30),
+    mc_double(31),
+    mc_double(32),
+    mc_double(33),
+    mc_double(34),
+
+    mc_double(63),
+    mc_double(64),
+    mc_double(65),
+
+    mc_double(77),
+    mc_double(127),
+    mc_double(128),
+    mc_double(255),
+    mc_double(333),
+    mc_double(444),
+    mc_double(7777),
+    mc_double(8765),
+
+    %% Patterns that cannot possibly match.
+    #{} = #{K => V || K := [V]={V} <- id(#{a => [b]})},
+    #{} = #{K => V || [K] = 42 := V <- id(#{42 => whatever})},
+
+    %% Filtering.
+    Map = #{{a,1} => {a,b,c}, {b,42} => [1,2,3], c => [4,5,6], d => {x,y}},
+    [c, d] = lists:sort([K || K := _ <- Map, is_atom(K)]),
+    [{a,b,c}, [1,2,3]] = lists:sort([V || {_,_} := V <- Map]),
+    [1] = [H || {_,_} := [H|_] <- Map],
+    [c, {b,42}] = lists:sort([K || K := [_|_] <- Map]),
+
+    ok.
+
+mc_double(Size) ->
+    Seq = lists:seq(1, Size),
+    Map = #{{key,I} => I || I <- Seq},
+
+    MapDouble = #{K => 2 * V || K := V <- id(Map)},
+    MapDouble = maps:from_list([{{key,I}, 2 * I} || I <- Seq]),
+
+    OddKeys = lists:seq(1, Size, 2),
+    OddKeys = lists:sort([I || {key,I} := I <- Map,
+                               I rem 2 =/= 0]),
+
+    OddMap = #{I => [] || {key,I} := I <- Map,
+                          I rem 2 =/= 0},
+    OddMap = #{I => [] || {key,I} := I <- Map,
+                          id(I) rem 2 =/= 0},
+    OddKeys = lists:sort(maps:keys(OddMap)),
+
+    %% Test that map comprehensions works on iterators.
+    test_iterator(Map, 0),
+    test_iterator(Map, map_size(Map) div 3),
+    test_iterator(Map, map_size(Map) div 2),
+    test_iterator(Map, map_size(Map)),
+
+    ok.
+
+test_iterator(Map, N) ->
+    Iter0 = maps:iterator(Map),
+    {First,Iter} = grab(N, Iter0, []),
+    All = [{K,V} || K := V <- Iter] ++ First,
+    Map = maps:from_list(All),
+    ok.
+
+grab(0, Iter, Acc) ->
+    {Acc,Iter};
+grab(N, Iter0, Acc) ->
+    case maps:next(Iter0) of
+        none ->
+            {Acc,Iter0};
+        {K,V,Iter} ->
+            grab(N - 1, Iter, [{K,V}|Acc])
+    end.
+
+duplicate_keys(_Config) ->
+    #{x := b} = #{V => K || {K,V} <- [{a, x}, {b, x}]},
+
+    #{a := 4, b := 4} =
+        #{K => V || K <- [a,b],
+                    <<V>> <= <<1,2,3,4>>},
+    ok.
+
+mixed(_Config) ->
+    Map = id(#{1 => 10, 2 => 5, 3 => 88, 4 => 99, 5 => 36}),
+    Bin = << <<K:8,V:24>> || K := V <- Map >>,
+    Map = maps:from_list([{K,V} || <<K:8,V:24>> <= Bin]),
+
+    Atoms = [list_to_atom([C]) || C <- lists:seq($a, $z)],
+    Integers = lists:seq(1, 64),
+
+    mixed_1(Atoms, Integers),
+    mixed_2(Atoms, Integers),
+    mixed_3(Atoms, Integers),
+
+    sum_of_triangular_numbers(7),
+    sum_of_triangular_numbers(10),
+
+    ok.
+
+mixed_1(Atoms, Integers) ->
+    IntegerMap = #{N => [] || N <- Integers},
+    IntegerKeys = [N || N := [] <- IntegerMap],
+    Integers = lists:sort(IntegerKeys),
+    Combined = [{C,N} || C <- Atoms, N := [] <- IntegerMap],
+    Combined = [{C,N} || C <- Atoms, N := [] <- maps:iterator(IntegerMap)],
+    Combined = [{C,N} || C <- Atoms, N <- IntegerKeys],
+
+    ok.
+
+mixed_2(Atoms, Integers) ->
+    IntegerMap = #{N => [] || N <- Integers},
+    IntegerKeys = [N || N := [] <- IntegerMap],
+    Bin = << <<N:16>> || N := [] <- IntegerMap >>,
+    Integers = lists:sort(IntegerKeys),
+
+    Combined = [{C,N} || N := [] <- IntegerMap, C <- Atoms],
+    Combined = [{C,N} || N := [] <- maps:iterator(IntegerMap), C <- Atoms],
+    Combined = [{C,N} || <<N:16>> <= Bin, C <- Atoms],
+    Combined = [{C,N} || N <- IntegerKeys, C <- Atoms],
+
+    ok.
+
+mixed_3(Atoms, Integers) ->
+    Map = #{K => V || {K,V} <- lists:zip(Atoms, Integers, trim)},
+    Bin = << <<N:16>> || _ := N <- Map >>,
+    {TrimmedAtoms,TrimmedIntegers} = lists:unzip([{K,V} || K := V <- Map]),
+
+    Combined = lists:sort([{K,V} || K := _ <- Map, _ := V <- Map]),
+    Combined = lists:sort([{K,V} || K <- TrimmedAtoms, <<V:16>> <= Bin]),
+    Combined = lists:sort([{K,V} || K <- TrimmedAtoms, V <- TrimmedIntegers]),
+
+    ok.
+
+sum_of_triangular_numbers(N) ->
+    Sum = N * (N + 1) * (N + 2) div 6,
+    Maps = [#{I => I || I <- lists:seq(0, I)} || I <- lists:seq(0, N)],
+    Numbers = [I || M <- Maps, I := I <- M],
+    Numbers = lists:flatten([[I || I := I <- M] || M <- Maps]),
+    Sum = lists:sum([lists:sum([I || I := I <- M]) || M <- Maps]),
+    Sum = lists:sum(Numbers),
+    ok.
+
+shadow(_Config)->
+    Shadowed = nomatch,
+    _ = id(Shadowed),				%Eliminate warning.
+    Map = #{Shadowed => Shadowed+1 || Shadowed <- lists:seq(7, 9)},
+    #{7 := 8, 8 := 9, 9 := 10} = id(Map),
+    [8,9] = lists:sort([Shadowed || _ := Shadowed <- id(Map),
+                                    Shadowed < 10]),
+    ok.
+
+bad_generators(_Config) ->
+    %% Make sure that line numbers point out the generator.
+    case ?MODULE of
+        mc_inline_SUITE ->
+            ok;
+        _ ->
+            {'EXIT',{{bad_generator,a},
+                     [{?MODULE,_,_,
+                       [{file,"bad_mc.erl"},{line,4}]}|_]}} =
+                catch id(bad_generator(a)),
+
+            {'EXIT',{{bad_generator,a},
+                     [{?MODULE,_,_,
+                       [{file,"bad_mc.erl"},{line,7}]}|_]}} =
+                catch id(bad_generator_bc(a)),
+
+            {'EXIT',{{bad_generator,a},
+                     [{?MODULE,_,_,
+                       [{file,"bad_mc.erl"},{line,10}]}|_]}} =
+                catch id(bad_generator_mc(a)),
+
+            BadIterator = [16#ffff|#{}],
+
+            {'EXIT',{{bad_generator,BadIterator},
+                     [{?MODULE,_,_,
+                       [{file,"bad_mc.erl"},{line,4}]}|_]}} =
+                catch id(bad_generator(BadIterator)),
+
+            {'EXIT',{{bad_generator,BadIterator},
+                     [{?MODULE,_,_,
+                       [{file,"bad_mc.erl"},{line,7}]}|_]}} =
+                catch id(bad_generator_bc(BadIterator)),
+
+            {'EXIT',{{bad_generator,BadIterator},
+                     [{?MODULE,_,_,
+                       [{file,"bad_mc.erl"},{line,10}]}|_]}} =
+                catch id(bad_generator_mc(BadIterator))
+    end,
+    ok.
+
+id(I) -> I.
+
+-file("bad_mc.erl", 1).
+bad_generator(Map) ->                           %Line 2
+    [{K,V} ||                                   %Line 3
+        K := V <- Map].                         %Line 4
+bad_generator_bc(Map) ->                        %Line 5
+    << <<K:8,V:24>> ||                          %Line 6
+        K := V <- Map>>.                        %Line 7
+bad_generator_mc(Map) ->                        %Line 8
+    #{V => K ||                                 %Line 9
+        K := V <- Map}.                         %Line 10
diff --git a/lib/compiler/test/record_SUITE.erl b/lib/compiler/test/record_SUITE.erl
index f6c6c27965..34708a4d6d 100644
--- a/lib/compiler/test/record_SUITE.erl
+++ b/lib/compiler/test/record_SUITE.erl
@@ -238,6 +238,14 @@ record_test_2(Config) when is_list(Config) ->
 			 begin not is_record(X, foo) or
 				   is_reference(X) end],
 
+    Map = id(#{a => 1, b => #foo{a=2}, c => 3, d => #bar{d=4},
+               e => 5, f => #foo{a=6}, h => 7}),
+    [#foo{a=2},#foo{a=6}] = lists:sort([X || _ := X <- Map, is_record(X, foo)]),
+    [#bar{d=4}] = [X || _ := X <- Map, is_record(X, bar)],
+    [1,3,5,7,#foo{a=2},#foo{a=6}] =
+	lists:sort([X || _ := X <- Map, not is_record(X, bar)]),
+    [2,6] = lists:sort([A || _ := #foo{a=A} <- Map]),
+
     %% Call is_record/2 with illegal arguments.
     [] = [X || X <- [], is_record(t, id(X))],
     {'EXIT',{badarg,_}} = (catch [X || X <- [1], is_record(t, id(X))]),
-- 
2.35.3

openSUSE Build Service is sponsored by