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