aboutsummaryrefslogtreecommitdiffstats
path: root/lib/compiler/src/v3_core.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/compiler/src/v3_core.erl')
-rw-r--r--lib/compiler/src/v3_core.erl199
1 files changed, 146 insertions, 53 deletions
diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl
index a50b46bd7b..a548ba2f7c 100644
--- a/lib/compiler/src/v3_core.erl
+++ b/lib/compiler/src/v3_core.erl
@@ -77,7 +77,8 @@
splitwith/2,keyfind/3,sort/1,foreach/2,droplast/1,last/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_cons_skel/3,ann_c_tuple/2,c_tuple/1]).
+-import(cerl, [ann_c_cons/3,ann_c_cons_skel/3,ann_c_tuple/2,c_tuple/1,
+ ann_c_map/2, ann_c_map/3]).
-include("core_parse.hrl").
@@ -274,51 +275,67 @@ gexpr({op,L,'orelse',E1,E2}, Bools, St0) ->
True = {atom,L,true},
E = make_bool_switch_guard(L, E1, V, True, E2),
gexpr(E, Bools, St);
-gexpr({op,Line,Op,L,R}=Call, Bools0, St0) ->
+gexpr({op,Line,Op,L,R}=E, Bools, St) ->
case erl_internal:bool_op(Op, 2) of
- true ->
- {Le,Lps,Bools1,St1} = gexpr(L, Bools0, St0),
- {Ll,Llps,St2} = force_safe(Le, St1),
- {Re,Rps,Bools,St3} = gexpr(R, Bools1, St2),
- {Rl,Rlps,St4} = force_safe(Re, St3),
- Anno = lineno_anno(Line, St4),
- {#icall{anno=#a{anno=Anno}, %Must have an #a{}
- module=#c_literal{anno=Anno,val=erlang},
- name=#c_literal{anno=Anno,val=Op},
- args=[Ll,Rl]},Lps ++ Llps ++ Rps ++ Rlps,Bools,St4};
- false ->
- gexpr_test(Call, Bools0, St0)
+ true ->
+ gexpr_bool(Op, L, R, Bools, St, Line);
+ false ->
+ gexpr_test(E, Bools, St)
end;
-gexpr({op,Line,Op,A}=Call, Bools0, St0) ->
- case Op of
- 'not' ->
- {Ae0,Aps,Bools,St1} = gexpr(A, Bools0, St0),
- case Ae0 of
- #icall{module=#c_literal{val=erlang},
- name=#c_literal{val='=:='},
- args=[E,#c_literal{val=true}]}=EqCall ->
- %%
- %% Doing the following transformation
- %% not(Expr =:= true) ==> Expr =:= false
- %% will help eliminating redundant is_boolean/1 tests.
- %%
- Ae = EqCall#icall{args=[E,#c_literal{val=false}]},
- {Al,Alps,St2} = force_safe(Ae, St1),
- {Al,Aps ++ Alps,Bools,St2};
- Ae ->
- {Al,Alps,St2} = force_safe(Ae, St1),
- Anno = lineno_anno(Line, St2),
- {#icall{anno=#a{anno=Anno}, %Must have an #a{}
- module=#c_literal{anno=Anno,val=erlang},
- name=#c_literal{anno=Anno,val=Op},
- args=[Al]},Aps ++ Alps,Bools,St2}
- end;
- _ ->
- gexpr_test(Call, Bools0, St0)
+gexpr({call,Line,{remote,_,{atom,_,erlang},{atom,_,Op}},[L,R]}=E, Bools, St) ->
+ case erl_internal:bool_op(Op, 2) of
+ true ->
+ gexpr_bool(Op, L, R, Bools, St, Line);
+ false ->
+ gexpr_test(E, Bools, St)
end;
+gexpr({op,Line,'not',A}, Bools, St) ->
+ gexpr_not(A, Bools, St, Line);
+gexpr({call,Line,{remote,_,{atom,_,erlang},{atom,_,'not'}},[A]}, Bools, St) ->
+ gexpr_not(A, Bools, St, Line);
gexpr(E0, Bools, St0) ->
gexpr_test(E0, Bools, St0).
+%% gexpr_not(L, R, Bools, State) -> {Cexpr,[PreExp],Bools,State}.
+%% Generate a guard for boolean operators
+
+gexpr_bool(Op, L, R, Bools0, St0, Line) ->
+ {Le,Lps,Bools1,St1} = gexpr(L, Bools0, St0),
+ {Ll,Llps,St2} = force_safe(Le, St1),
+ {Re,Rps,Bools,St3} = gexpr(R, Bools1, St2),
+ {Rl,Rlps,St4} = force_safe(Re, St3),
+ Anno = lineno_anno(Line, St4),
+ {#icall{anno=#a{anno=Anno}, %Must have an #a{}
+ module=#c_literal{anno=Anno,val=erlang},
+ name=#c_literal{anno=Anno,val=Op},
+ args=[Ll,Rl]},Lps ++ Llps ++ Rps ++ Rlps,Bools,St4}.
+
+%% gexpr_not(Expr, Bools, State) -> {Cexpr,[PreExp],Bools,State}.
+%% Generate an erlang:'not'/1 guard test.
+
+gexpr_not(A, Bools0, St0, Line) ->
+ {Ae0,Aps,Bools,St1} = gexpr(A, Bools0, St0),
+ case Ae0 of
+ #icall{module=#c_literal{val=erlang},
+ name=#c_literal{val='=:='},
+ args=[E,#c_literal{val=true}]}=EqCall ->
+ %%
+ %% Doing the following transformation
+ %% not(Expr =:= true) ==> Expr =:= false
+ %% will help eliminating redundant is_boolean/1 tests.
+ %%
+ Ae = EqCall#icall{args=[E,#c_literal{val=false}]},
+ {Al,Alps,St2} = force_safe(Ae, St1),
+ {Al,Aps ++ Alps,Bools,St2};
+ Ae ->
+ {Al,Alps,St2} = force_safe(Ae, St1),
+ Anno = lineno_anno(Line, St2),
+ {#icall{anno=#a{anno=Anno}, %Must have an #a{}
+ module=#c_literal{anno=Anno,val=erlang},
+ name=#c_literal{anno=Anno,val='not'},
+ args=[Al]},Aps ++ Alps,Bools,St2}
+ end.
+
%% gexpr_test(Expr, Bools, State) -> {Cexpr,[PreExp],Bools,State}.
%% Generate a guard test. At this stage we must be sure that we have
%% a proper boolean value here so wrap things with an true test if we
@@ -335,7 +352,8 @@ gexpr_test(E0, Bools0, St0) ->
#icall{anno=Anno,module=#c_literal{val=erlang},name=#c_literal{val=N},args=As} ->
Ar = length(As),
case erl_internal:type_test(N, Ar) orelse
- erl_internal:comp_op(N, Ar) of
+ erl_internal:comp_op(N, Ar) orelse
+ erl_internal:bool_op(N, Ar) of
true -> {E1,Eps0,Bools0,St1};
false ->
Lanno = Anno#a.anno,
@@ -496,14 +514,33 @@ expr({tuple,L,Es0}, St0) ->
expr({map,L,Es0}, St0) ->
% erl_lint should make sure only #{ K => V } are allowed
% in map construction.
- {Es1,Eps,St1} = map_pair_list(Es0, St0),
- A = lineno_anno(L, St1),
- {#c_map{anno=A,es=Es1},Eps,St1};
+ try map_pair_list(Es0, St0) of
+ {Es1,Eps,St1} ->
+ A = lineno_anno(L, St1),
+ {ann_c_map(A,Es1),Eps,St1}
+ catch
+ throw:{bad_map,Warning} ->
+ St = add_warning(L, Warning, St0),
+ LineAnno = lineno_anno(L, St),
+ As = [#c_literal{anno=LineAnno,val=badarg}],
+ {#icall{anno=#a{anno=LineAnno}, %Must have an #a{}
+ module=#c_literal{anno=LineAnno,val=erlang},
+ name=#c_literal{anno=LineAnno,val=error},
+ args=As},[],St}
+ end;
expr({map,L,M0,Es0}, St0) ->
- {M1,Mps,St1} = safe(M0, St0),
- {Es1,Eps,St2} = map_pair_list(Es0, St1),
- A = lineno_anno(L, St2),
- {#c_map{anno=A,var=M1,es=Es1},Mps++Eps,St2};
+ try expr_map(M0,Es0,lineno_anno(L, St0),St0) of
+ {_,_,_}=Res -> Res
+ catch
+ throw:{bad_map,Warning} ->
+ St = add_warning(L, Warning, St0),
+ LineAnno = lineno_anno(L, St),
+ As = [#c_literal{anno=LineAnno,val=badarg}],
+ {#icall{anno=#a{anno=LineAnno}, %Must have an #a{}
+ module=#c_literal{anno=LineAnno,val=erlang},
+ name=#c_literal{anno=LineAnno,val=error},
+ args=As},[],St}
+ end;
expr({bin,L,Es0}, St0) ->
try expr_bin(Es0, lineno_anno(L, St0), St0) of
{_,_,_}=Res -> Res
@@ -623,7 +660,7 @@ expr({call,Lc,{atom,Lf,F},As0}, St0) ->
Op = #c_var{anno=lineno_anno(Lf, St1),name={F,length(As1)}},
{#iapply{anno=#a{anno=lineno_anno(Lc, St1)},op=Op,args=As1},Aps,St1};
expr({call,L,FunExp,As0}, St0) ->
- {Fun,Fps,St1} = safe(FunExp, St0),
+ {Fun,Fps,St1} = safe_fun(length(As0), FunExp, St0),
{As1,Aps,St2} = safe_list(As0, St1),
Lanno = lineno_anno(L, St2),
{#iapply{anno=#a{anno=Lanno},op=Fun,args=As1},Fps ++ Aps,St2};
@@ -713,22 +750,58 @@ make_bool_switch_guard(L, E, V, T, F) ->
{clause,NegL,[V],[],[V]}
]}.
+expr_map(M0,Es0,A,St0) ->
+ {M1,Mps,St1} = safe(M0, St0),
+ case is_valid_map_src(M1) of
+ true ->
+ case {M1,Es0} of
+ {#c_var{}, []} ->
+ %% transform M#{} to is_map(M)
+ {Vpat,St2} = new_var(St1),
+ {Fpat,St3} = new_var(St2),
+ Cs = [#iclause{
+ anno=A,
+ pats=[Vpat],
+ guard=[#icall{anno=#a{anno=A},
+ module=#c_literal{anno=A,val=erlang},
+ name=#c_literal{anno=A,val=is_map},
+ args=[Vpat]}],
+ body=[Vpat]}],
+ Fc = fail_clause([Fpat], A, #c_literal{val=badarg}),
+ {#icase{anno=#a{anno=A},args=[M1],clauses=Cs,fc=Fc},Mps,St3};
+ {_,_} ->
+ {Es1,Eps,St2} = map_pair_list(Es0, St1),
+ {ann_c_map(A,M1,Es1),Mps++Eps,St2}
+ end;
+ false -> throw({bad_map,bad_map})
+ end.
+
+is_valid_map_src(#c_literal{val = M}) when is_map(M) -> true;
+is_valid_map_src(#c_map{}) -> true;
+is_valid_map_src(#c_var{}) -> true;
+is_valid_map_src(_) -> false.
+
map_pair_list(Es, St) ->
foldr(fun
({map_field_assoc,L,K0,V0}, {Ces,Esp,St0}) ->
{K,Ep0,St1} = safe(K0, St0),
+ ok = ensure_valid_map_key(K),
{V,Ep1,St2} = safe(V0, St1),
A = lineno_anno(L, St2),
Pair = #c_map_pair{op=#c_literal{val=assoc},anno=A,key=K,val=V},
{[Pair|Ces],Ep0 ++ Ep1 ++ Esp,St2};
({map_field_exact,L,K0,V0}, {Ces,Esp,St0}) ->
{K,Ep0,St1} = safe(K0, St0),
+ ok = ensure_valid_map_key(K),
{V,Ep1,St2} = safe(V0, St1),
A = lineno_anno(L, St2),
Pair = #c_map_pair{op=#c_literal{val=exact},anno=A,key=K,val=V},
{[Pair|Ces],Ep0 ++ Ep1 ++ Esp,St2}
end, {[],[],St}, Es).
+ensure_valid_map_key(#c_literal{}) -> ok;
+ensure_valid_map_key(_) -> throw({bad_map,bad_map_key}).
+
%% try_exception([ExcpClause], St) -> {[ExcpVar],Handler,St}.
try_exception(Ecs0, St0) ->
@@ -1408,6 +1481,15 @@ safe(E0, St0) ->
{Se,Sps,St2} = force_safe(E1, St1),
{Se,Eps ++ Sps,St2}.
+safe_fun(A0, E0, St0) ->
+ case safe(E0, St0) of
+ {#c_var{name={_,A1}}=E1,Eps,St1} when A1 =/= A0 ->
+ {V,St2} = new_var(St1),
+ {V,Eps ++ [#iset{var=V,arg=E1}],St2};
+ Result ->
+ Result
+ end.
+
safe_list(Es, St) ->
foldr(fun (E, {Ces,Esp,St0}) ->
{Ce,Ep,St1} = safe(E, St0),
@@ -1529,7 +1611,9 @@ pattern_map_pair({map_field_exact,L,K,V}, St) ->
{bin,L,Es0} ->
case constant_bin(Es0) of
error ->
- throw(badmatch);
+ %% this will throw a cryptic error message
+ %% but it is better than nothing
+ throw(nomatch);
Bin ->
#c_literal{anno=lineno_anno(L,St),val=Bin}
end;
@@ -1756,13 +1840,16 @@ uexpr(#iletrec{anno=A,defs=Fs0,body=B0}, Ks, St0) ->
{B1,St2} = uexprs(B0, Ks, St1),
Used = used_in_any(map(fun ({_,F}) -> F end, Fs1) ++ B1),
{#iletrec{anno=A#a{us=Used,ns=[]},defs=Fs1,body=B1},St2};
-uexpr(#icase{anno=A,args=As0,clauses=Cs0,fc=Fc0}, Ks, St0) ->
+uexpr(#icase{anno=#a{anno=Anno}=A,args=As0,clauses=Cs0,fc=Fc0}, Ks, St0) ->
%% As0 will never generate new variables.
{As1,St1} = uexpr_list(As0, Ks, St0),
{Cs1,St2} = uclauses(Cs0, Ks, St1),
{Fc1,St3} = uclause(Fc0, Ks, St2),
Used = union(used_in_any(As1), used_in_any(Cs1)),
- New = new_in_all(Cs1),
+ New = case member(list_comprehension, Anno) of
+ true -> [];
+ false -> new_in_all(Cs1)
+ end,
{#icase{anno=A#a{us=Used,ns=New},args=As1,clauses=Cs1,fc=Fc1},St3};
uexpr(#ifun{anno=A0,id=Id,vars=As,clauses=Cs0,fc=Fc0,name=Name}, Ks0, St0) ->
Avs = lit_list_vars(As),
@@ -2137,6 +2224,8 @@ lit_vars(Lit) -> lit_vars(Lit, []).
lit_vars(#c_cons{hd=H,tl=T}, Vs) -> lit_vars(H, lit_vars(T, Vs));
lit_vars(#c_tuple{es=Es}, Vs) -> lit_list_vars(Es, Vs);
+lit_vars(#c_map{arg=V,es=Es}, Vs) -> lit_vars(V, lit_list_vars(Es, Vs));
+lit_vars(#c_map_pair{key=K,val=V}, Vs) -> lit_vars(K, lit_vars(V, Vs));
lit_vars(#c_var{name=V}, Vs) -> add_element(V, Vs);
lit_vars(_, Vs) -> Vs. %These are atomic
@@ -2227,7 +2316,11 @@ is_simple_list(Es) -> lists:all(fun is_simple/1, Es).
format_error(nomatch) ->
"pattern cannot possibly match";
format_error(bad_binary) ->
- "binary construction will fail because of a type mismatch".
+ "binary construction will fail because of a type mismatch";
+format_error(bad_map_key) ->
+ "map construction will fail because of none literal key (large binaries are not literals)";
+format_error(bad_map) ->
+ "map construction will fail because of a type mismatch".
add_warning(Line, Term, #core{ws=Ws,file=[{file,File}]}=St) when Line >= 0 ->
St#core{ws=[{File,[{location(Line),?MODULE,Term}]}|Ws]};