File 3136-erl_eval-Handle-map-comprehensions.patch of Package erlang

From 6cbdf51d1dfab4e0e1f4acc873a6502c795b3e01 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= <bjorn@erlang.org>
Date: Mon, 23 Jan 2023 05:05:13 +0100
Subject: [PATCH 06/12] erl_eval: Handle map comprehensions

---
 lib/stdlib/src/erl_eval.erl        | 109 +++++++++++++++++++++++------
 lib/stdlib/test/erl_eval_SUITE.erl |  33 ++++++++-
 2 files changed, 118 insertions(+), 24 deletions(-)

diff --git a/lib/stdlib/src/erl_eval.erl b/lib/stdlib/src/erl_eval.erl
index 987ba0cf0a..ecb823a246 100644
--- a/lib/stdlib/src/erl_eval.erl
+++ b/lib/stdlib/src/erl_eval.erl
@@ -286,6 +286,8 @@ expr({lc,_,E,Qs}, Bs, Lf, Ef, RBs, FUVs) ->
     eval_lc(E, Qs, Bs, Lf, Ef, RBs, FUVs);
 expr({bc,_,E,Qs}, Bs, Lf, Ef, RBs, FUVs) ->
     eval_bc(E, Qs, Bs, Lf, Ef, RBs, FUVs);
+expr({mc,_,E,Qs}, Bs, Lf, Ef, RBs, FUVs) ->
+    eval_mc(E, Qs, Bs, Lf, Ef, RBs, FUVs);
 expr({tuple,_,Es}, Bs0, Lf, Ef, RBs, FUVs) ->
     {Vs,Bs} = expr_list(Es, Bs0, Lf, Ef, FUVs),
     ret_expr(list_to_tuple(Vs), Bs, RBs);
@@ -760,17 +762,15 @@ 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, [{generate,Anno,P,L0}|Qs], Bs0, Lf, Ef, FUVs, Acc0) ->
-    {value,L1,_Bs1} = expr(L0, Bs0, Lf, Ef, none, FUVs),
-    CompFun = fun(Bs, Acc) -> eval_lc1(E, Qs, Bs, Lf, Ef, FUVs, Acc) end,
-    eval_generate(L1, P, Anno, Bs0, Lf, Ef, CompFun, Acc0);
-eval_lc1(E, [{b_generate,Anno,P,L0}|Qs], Bs0, Lf, Ef, FUVs, Acc0) ->
-    {value,Bin,_Bs1} = expr(L0, Bs0, Lf, Ef, none, FUVs),
-    CompFun = fun(Bs, Acc) -> eval_lc1(E, Qs, Bs, Lf, Ef, FUVs, Acc) end,
-    eval_b_generate(Bin, P, Anno, Bs0, Lf, Ef, CompFun, Acc0);
-eval_lc1(E, [F|Qs], Bs0, Lf, Ef, FUVs, Acc) ->
-    CompFun = fun(Bs) -> eval_lc1(E, Qs, Bs, Lf, Ef, FUVs, Acc) end,
-    eval_filter(F, Bs0, Lf, Ef, CompFun, FUVs, Acc);
+eval_lc1(E, [Q|Qs], Bs0, Lf, Ef, FUVs, Acc0) ->
+    case is_generator(Q) of
+        true ->
+            CF = fun(Bs, Acc) -> eval_lc1(E, Qs, Bs, Lf, Ef, FUVs, Acc) end,
+            eval_generator(Q, Bs0, Lf, Ef, FUVs, Acc0, CF);
+        false ->
+            CF = fun(Bs) -> eval_lc1(E, Qs, Bs, Lf, Ef, FUVs, Acc0) end,
+            eval_filter(Q, Bs0, Lf, Ef, CF, FUVs, Acc0)
+    end;
 eval_lc1(E, [], Bs, Lf, Ef, FUVs, Acc) ->
     {value,V,_} = expr(E, Bs, Lf, Ef, none, FUVs),
     [V|Acc].
@@ -782,21 +782,66 @@ 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, [{b_generate,Anno,P,L0}|Qs], Bs0, Lf, Ef, FUVs, Acc0) ->
-    {value,Bin,_Bs1} = expr(L0, Bs0, Lf, Ef, none, FUVs),
-    CompFun = fun(Bs, Acc) -> eval_bc1(E, Qs, Bs, Lf, Ef, FUVs, Acc) end,
-    eval_b_generate(Bin, P, Anno, Bs0, Lf, Ef, CompFun, Acc0);
-eval_bc1(E, [{generate,Anno,P,L0}|Qs], Bs0, Lf, Ef, FUVs, Acc0) ->
-    {value,List,_Bs1} = expr(L0, Bs0, Lf, Ef, none, FUVs),
-    CompFun = fun(Bs, Acc) -> eval_bc1(E, Qs, Bs, Lf, Ef, FUVs, Acc) end,
-    eval_generate(List, P, Anno, Bs0, Lf, Ef, CompFun, Acc0);
-eval_bc1(E, [F|Qs], Bs0, Lf, Ef, FUVs, Acc) ->
-    CompFun = fun(Bs) -> eval_bc1(E, Qs, Bs, Lf, Ef, FUVs, Acc) end,
-    eval_filter(F, Bs0, Lf, Ef, CompFun, FUVs, Acc);
+eval_bc1(E, [Q|Qs], Bs0, Lf, Ef, FUVs, Acc0) ->
+    case is_generator(Q) of
+        true ->
+            CF = fun(Bs, Acc) -> eval_bc1(E, Qs, Bs, Lf, Ef, FUVs, Acc) end,
+            eval_generator(Q, Bs0, Lf, Ef, FUVs, Acc0, CF);
+        false ->
+            CF = fun(Bs) -> eval_bc1(E, Qs, Bs, Lf, Ef, FUVs, Acc0) end,
+            eval_filter(Q, Bs0, Lf, Ef, CF, FUVs, Acc0)
+    end;
 eval_bc1(E, [], Bs, Lf, Ef, FUVs, Acc) ->
     {value,V,_} = expr(E, Bs, Lf, Ef, none, FUVs),
     <<Acc/bitstring,V/bitstring>>.
 
+%% eval_mc(Expr, [Qualifier], Bindings, LocalFunctionHandler,
+%%         ExternalFuncHandler, RetBindings) ->
+%%	{value,Value,Bindings} | Value
+
+eval_mc(E, Qs, Bs, Lf, Ef, RBs, FUVs) ->
+    L = eval_mc1(E, Qs, Bs, Lf, Ef, FUVs, []),
+    Map = maps:from_list(L),
+    ret_expr(Map, Bs, RBs).
+
+eval_mc1(E, [Q|Qs], Bs0, Lf, Ef, FUVs, Acc0) ->
+    case is_generator(Q) of
+        true ->
+            CF = fun(Bs, Acc) -> eval_mc1(E, Qs, Bs, Lf, Ef, FUVs, Acc) end,
+            eval_generator(Q, Bs0, Lf, Ef, FUVs, Acc0, CF);
+        false ->
+            CF = fun(Bs) -> eval_mc1(E, Qs, Bs, Lf, Ef, FUVs, Acc0) end,
+            eval_filter(Q, Bs0, Lf, Ef, CF, FUVs, Acc0)
+    end;
+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_generator({generate,Anno,P,L0}, Bs0, Lf, Ef, FUVs, Acc0, CompFun) ->
+    {value,L1,_Bs1} = expr(L0, Bs0, Lf, Ef, none, FUVs),
+    eval_generate(L1, P, Anno, Bs0, Lf, Ef, CompFun, Acc0);
+eval_generator({b_generate,Anno,P,Bin0}, Bs0, Lf, Ef, FUVs, Acc0, CompFun) ->
+    {value,Bin,_Bs1} = expr(Bin0, Bs0, Lf, Ef, none, FUVs),
+    eval_b_generate(Bin, P, Anno, Bs0, Lf, Ef, CompFun, Acc0);
+eval_generator({m_generate,Anno,P,Map0}, Bs0, Lf, Ef, FUVs, Acc0, CompFun) ->
+    {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,
+    eval_m_generate(Iter, {tuple,Anno,[K,V]}, Anno, Bs0, Lf, Ef, CompFun, Acc0).
+
 eval_generate([V|Rest], P, Anno, Bs0, Lf, Ef, CompFun, Acc) ->
     case match(P, V, Anno, new_bindings(Bs0), Bs0, Ef) of
 	{match,Bsn} ->
@@ -828,6 +873,21 @@ eval_b_generate(<<_/bitstring>>=Bin, P, Anno, Bs0, Lf, Ef, CompFun, Acc) ->
 eval_b_generate(Term, _P, Anno, Bs0, _Lf, Ef, _CompFun, _Acc) ->
     apply_error({bad_generator,Term}, ?STACKTRACE, Anno, Bs0, Ef, none).
 
+eval_m_generate(Iter0, P, Anno, Bs0, Lf, Ef, CompFun, Acc0) ->
+    case maps:next(Iter0) of
+        {K,V,Iter} ->
+            case match(P, {K,V}, Anno, new_bindings(Bs0), Bs0, Ef) of
+                {match,Bsn} ->
+                    Bs2 = add_bindings(Bsn, Bs0),
+                    Acc = CompFun(Bs2, Acc0),
+                    eval_m_generate(Iter, P, Anno, Bs0, Lf, Ef, CompFun, Acc);
+                nomatch ->
+                    eval_m_generate(Iter, P, Anno, Bs0, Lf, Ef, CompFun, Acc0)
+            end;
+        none ->
+            Acc0
+    end.
+
 eval_filter(F, Bs0, Lf, Ef, CompFun, FUVs, Acc) ->
     case erl_lint:is_guard_test(F) of
 	true ->
@@ -844,6 +904,11 @@ eval_filter(F, Bs0, Lf, Ef, CompFun, FUVs, Acc) ->
 	    end
     end.
 
+is_generator({generate,_,_,_}) -> true;
+is_generator({b_generate,_,_,_}) -> true;
+is_generator({m_generate,_,_,_}) -> true;
+is_generator(_) -> false.
+
 %% eval_map_fields([Field], Bindings, LocalFunctionHandler,
 %%                 ExternalFuncHandler) ->
 %%  {[{map_assoc | map_exact,Key,Value}],Bindings}
diff --git a/lib/stdlib/test/erl_eval_SUITE.erl b/lib/stdlib/test/erl_eval_SUITE.erl
index bd76fc5a89..d3f6f951e2 100644
--- a/lib/stdlib/test/erl_eval_SUITE.erl
+++ b/lib/stdlib/test/erl_eval_SUITE.erl
@@ -56,6 +56,7 @@
          otp_16545/1,
          otp_16865/1,
          eep49/1,
+         eep58/1,
          binary_skip/1]).
 
 %%
@@ -98,7 +99,7 @@ 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_skip].
+     eep49, eep58, binary_skip].
 
 groups() -> 
     [].
@@ -1998,6 +1999,34 @@ eep49(Config) when is_list(Config) ->
                 {else_clause,simply_wrong}),
     ok.
 
+%% EEP 58: Map comprehensions.
+eep58(Config) when is_list(Config) ->
+    check(fun() -> X = 32, #{X => X*X || X <- [1,2,3]} end,
+	  "begin X = 32, #{X => X*X || X <- [1,2,3]} end.",
+	  #{1 => 1, 2 => 4, 3 => 9}),
+    check(fun() ->
+                  K = V = none,
+                  #{K => V*V || K := V <- #{1 => 1, 2 => 2, 3 => 3}}
+          end,
+          "begin K = V = none, #{K => V*V || K := V <- #{1 => 1, 2 => 2, 3 => 3}} end.",
+	  #{1 => 1, 2 => 4, 3 => 9}),
+    check(fun() ->
+                  #{K => V*V || K := V <- maps:iterator(#{1 => 1, 2 => 2, 3 => 3})}
+          end,
+          "#{K => V*V || K := V <- maps:iterator(#{1 => 1, 2 => 2, 3 => 3})}.",
+	  #{1 => 1, 2 => 4, 3 => 9}),
+    check(fun() -> << <<K:8,V:24>> || K := V <- #{42 => 7777} >> end,
+          "<< <<K:8,V:24>> || K := V <- #{42 => 7777} >>.",
+	  <<42:8,7777:24>>),
+    check(fun() -> [X || X := X <- #{a => 1, b => b}] end,
+          "[X || X := X <- #{a => 1, b => b}].",
+	  [b]),
+
+    error_check("[K+V || K := V <- a].", {bad_generator,a}),
+    error_check("[K+V || K := V <- [-1|#{}]].", {bad_generator,[-1|#{}]}),
+
+    ok.
+
 binary_skip(Config) when is_list(Config) ->
     check(fun() -> X = 32, [X || <<X:64/float>> <= <<-1:64, 0:64, 0:64, 0:64>>] end,
 	  "begin X = 32, [X || <<X:64/float>> <= <<-1:64, 0:64, 0:64, 0:64>>] end.",
-- 
2.35.3

openSUSE Build Service is sponsored by