summaryrefslogtreecommitdiff
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.erl769
1 files changed, 595 insertions, 174 deletions
diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl
index 2fd31656ca..ff7a48e002 100644
--- a/lib/compiler/src/v3_core.erl
+++ b/lib/compiler/src/v3_core.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2022. All Rights Reserved.
+%% Copyright Ericsson AB 1999-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.
@@ -109,6 +109,7 @@
-record(ifun, {anno=#a{},id,vars,clauses,fc,name=unnamed}).
-record(iletrec, {anno=#a{},defs,body}).
-record(imatch, {anno=#a{},pat,guard=[],arg,fc}).
+-record(iexprs, {anno=#a{},bodies=[]}).
-record(imap, {anno=#a{},arg=#c_literal{val=#{}},es,is_pat=false}).
-record(imappair, {anno=#a{},op,key,val}).
-record(iprimop, {anno=#a{},name,args}).
@@ -170,6 +171,7 @@
-record(imodule, {name = [],
exports = ordsets:new(),
+ nifs = sets:new([{version, 2}]),
attrs = [],
defs = [],
file = [],
@@ -194,9 +196,8 @@ module(Forms0, Opts) ->
Kfs = reverse(Kfs0),
{ok,#c_module{name=#c_literal{val=Mod},exports=Cexp,attrs=As,defs=Kfs},Ws}.
-form({function,_,_,_,_}=F0, Module, Opts) ->
- #imodule{file=File,defs=Defs,ws=Ws0} = Module,
- {F,Ws} = function(F0, Ws0, File, Opts),
+form({function,_,_,_,_}=F0, #imodule{defs=Defs}=Module, Opts) ->
+ {F,Ws} = function(F0, Module, Opts),
Module#imodule{defs=[F|Defs],ws=Ws};
form({attribute,_,module,Mod}, Module, _Opts) ->
true = is_atom(Mod),
@@ -204,11 +205,14 @@ form({attribute,_,module,Mod}, Module, _Opts) ->
form({attribute,_,file,{File,_Line}}=F, #imodule{attrs=As}=Module, _Opts) ->
Module#imodule{file=File, attrs=[attribute(F)|As]};
form({attribute,_,import,_}, Module, _Opts) ->
- %% Ignore. We have no futher use for imports.
+ %% Ignore. We have no further use for imports.
Module;
form({attribute,_,export,Es}, #imodule{exports=Exp0}=Module, _Opts) ->
Exp = ordsets:union(ordsets:from_list(Es), Exp0),
Module#imodule{exports=Exp};
+form({attribute,_,nifs,Ns}, #imodule{nifs=Nifs0}=Module, _Opts) ->
+ Nifs = sets:union(sets:from_list(Ns, [{version,2}]), Nifs0),
+ Module#imodule{nifs=Nifs};
form({attribute,_,_,_}=F, #imodule{attrs=As}=Module, _Opts) ->
Module#imodule{attrs=[attribute(F)|As]};
form(_, Module, _Opts) ->
@@ -232,7 +236,8 @@ defined_functions(Forms) ->
%% io:format("~w/~w " ++ Format,[Name,Arity]++Terms),
%% ok.
-function({function,_,Name,Arity,Cs0}, Ws0, File, Opts) ->
+function({function,_,Name,Arity,Cs0}, Module, Opts) ->
+ #imodule{file=File, ws=Ws0, nifs=Nifs} = Module,
try
St0 = #core{vcount=0,function={Name,Arity},opts=Opts,
dialyzer=member(dialyzer, Opts),
@@ -241,7 +246,7 @@ function({function,_,Name,Arity,Cs0}, Ws0, File, Opts) ->
%% ok = function_dump(Name, Arity, "body:~n~p~n",[B0]),
{B1,St2} = ubody(B0, St1),
%% ok = function_dump(Name, Arity, "ubody:~n~p~n",[B1]),
- {B2,St3} = cbody(B1, St2),
+ {B2,St3} = cbody(B1, Nifs, St2),
%% ok = function_dump(Name, Arity, "cbody:~n~p~n",[B2]),
{B3,#core{ws=Ws}} = lbody(B2, St3),
%% ok = function_dump(Name, Arity, "lbody:~n~p~n",[B3]),
@@ -259,7 +264,7 @@ body(Cs0, Name, Arity, St0) ->
Args = reverse(Args0), %Nicer order
{Cs1,St2} = clauses(Cs0, St1),
{Ps,St3} = new_vars(Arity, St2), %Need new variables here
- Fc = function_clause(Ps, Anno),
+ Fc = function_clause(Ps, FunAnno),
{#ifun{anno=#a{anno=FunAnno},id=[],vars=Args,clauses=Cs1,fc=Fc},St3}.
%% clause(Clause, State) -> {Cclause,State}.
@@ -282,7 +287,7 @@ clause({clause,Lc,H0,G0,B0}, St0) ->
catch
throw:nomatch ->
%% This pattern can't possibly match. If we simply remove
- %% the clause, varibles that are used later might not be
+ %% the clause, variables that are used later might not be
%% bound. Therefore, we must keep the clause, but rewrite
%% the pattern to a pattern that will bind the same
%% variables and ensure that the clause can't be executed
@@ -303,7 +308,7 @@ head(Ps, St) ->
pattern_list(Ps, St).
%% guard([Expr], State) -> {[Cexpr],State}.
-%% Build an explict and/or tree of guard alternatives, then traverse
+%% Build an explicit and/or tree of guard alternatives, then traverse
%% top-level and/or tree and "protect" inner tests.
guard([], St) -> {[],St};
@@ -422,7 +427,7 @@ gexpr_not(A, Bools0, St0, Line) ->
%% which will produce the same result, but may eliminate
%% redundant is_boolean/1 tests (see unforce/3).
%%
- %% Note that this tranformation would not be safe if the
+ %% Note that this transformation would not be safe if the
%% original expression had been:
%%
%% not(Expr =:= true)
@@ -565,6 +570,8 @@ unforce(E, Eps, Vs) ->
Tree = unforce_tree(Eps++[E], gb_trees:empty()),
unforce(Tree, Vs).
+unforce_tree([#iexprs{bodies=Exprs}|Es], D0) ->
+ unforce_tree(lists:append(Exprs) ++ Es, D0);
unforce_tree([#iset{var=#c_var{name=V},arg=Arg0}|Es], D0) ->
Arg = unforce_tree_subst(Arg0, D0),
D = gb_trees:insert(V, Arg, D0),
@@ -616,6 +623,34 @@ exprs([E0|Es0], St0) ->
{Eps ++ [E1] ++ Es1,St2};
exprs([], St) -> {[],St}.
+%% exprs([Expr], State) -> {[Cexpr],State}.
+%% Flatten top-level exprs while handling maybe_match operators.
+
+maybe_match_exprs([{maybe_match,L,P0,E0}|Es0], Fail, St0) ->
+ {Es1,St1} = maybe_match_exprs(Es0, Fail, St0),
+ {C,St2} =
+ case Es1 of
+ [] ->
+ {AllName,StInt} = new_var_name(St1),
+ All = {var,L,AllName},
+ clause({clause,L,[{match,L,P0,All}],[],[All]}, StInt);
+ [_|_] ->
+ {C0,StInt} = clause({clause,L,[P0],[],[{nil,0}]}, St1),
+ {C0#iclause{body=Es1},StInt}
+ end,
+ {E1,Eps,St3} = novars(E0, St2),
+ {Fpat,St4} = new_var(St3),
+ Lanno = lineno_anno(L, St4),
+ Fc = #iclause{anno=#a{anno=[dialyzer_ignore,compiler_generated|Lanno]},pats=[Fpat],guard=[],
+ body=[#iapply{op=Fail,args=[Fpat]}]},
+ {Eps ++ [#icase{anno=#a{anno=Lanno},args=[E1],clauses=[C],fc=Fc}],St4};
+maybe_match_exprs([E0|Es0], Fail, St0) ->
+ {E1,Eps,St1} = expr(E0, St0),
+ {Es1,St2} = maybe_match_exprs(Es0, Fail, St1),
+ {Eps ++ [E1|Es1],St2};
+maybe_match_exprs([], _Fail, St) ->
+ {[],St}.
+
%% expr(Expr, State) -> {Cexpr,[PreExp],State}.
%% Generate an internal core expression.
@@ -627,10 +662,9 @@ expr({atom,L,A}, St) -> {#c_literal{anno=full_anno(L, St),val=A},[],St};
expr({nil,L}, St) -> {#c_literal{anno=full_anno(L, St),val=[]},[],St};
expr({string,L,S}, St) -> {#c_literal{anno=full_anno(L, St),val=S},[],St};
expr({cons,L,H0,T0}, St0) ->
- {H1,Hps,St1} = safe(H0, St0),
- {T1,Tps,St2} = safe(T0, St1),
- A = full_anno(L, St2),
- {annotate_cons(A, H1, T1, St2),Hps ++ Tps,St2};
+ {[H1,T1],Eps,St1} = safe_list([H0,T0], St0),
+ A = full_anno(L, St1),
+ {annotate_cons(A, H1, T1, St1),Eps,St1};
expr({lc,L,E,Qs0}, St0) ->
{Qs1,St1} = preprocess_quals(L, Qs0, St0),
lc_tq(L, E, Qs1, #c_literal{anno=lineno_anno(L, St1),val=[]}, St1);
@@ -662,6 +696,84 @@ expr({block,_,Es0}, St0) ->
{Es1,St1} = exprs(droplast(Es0), St0),
{E1,Eps,St2} = expr(last(Es0), St1),
{E1,Es1 ++ Eps,St2};
+expr({'maybe',L,Es}, St0) ->
+ {V,St1} = new_var_name(St0),
+ Var = {var,L,V},
+ Cs = [{clause,L,[Var],[],[Var]}],
+ expr({'maybe',L,Es,{'else',L,Cs}}, St1);
+expr({'maybe',L,Es0,{'else',_,Cs0}}, St0) ->
+ %% Translate the maybe ... else ... end construct.
+ %%
+ %% As an example, the following Erlang code:
+ %%
+ %% foo(A) ->
+ %% maybe
+ %% {ok, V} ?= A,
+ %% V
+ %% else
+ %% Other ->
+ %% {error, Other}
+ %% end.
+ %%
+ %% is translated into Core Erlang like this:
+ %%
+ %% 'foo'/1 =
+ %% fun (_0) ->
+ %% case _0 of
+ %% <A> when 'true' ->
+ %% ( letrec
+ %% 'maybe_else_fail'/1 =
+ %% fun (_3) ->
+ %% case _3 of
+ %% <_2> when 'true' ->
+ %% case _2 of
+ %% <Other> when 'true' ->
+ %% {'error',Other}
+ %% ( <_1> when 'true' ->
+ %% primop 'match_fail'({'else_clause',_1})
+ %% -| ['compiler_generated'] )
+ %% end
+ %% ( <_1> when 'true' ->
+ %% primop 'match_fail'('never_fails')
+ %% -| ['compiler_generated'] )
+ %% end
+ %% in
+ %% case A of
+ %% <{'ok',V}> when 'true' ->
+ %% V
+ %% ( <_4> when 'true' ->
+ %% apply 'maybe_else_fail'/1(_4)
+ %% -| ['dialyzer_ignore','compiler_generated'] )
+ %% end
+ %% -| ['letrec_goto','no_inline'] )
+ %% ( <_5> when 'true' ->
+ %% primop 'match_fail'({'function_clause',_5})
+ %% -| ['compiler_generated'] )
+ %% end
+ {[V1,V2,FailVar],St1} = new_vars(3, St0),
+
+ %% Translate the body of the letrec.
+ Fail = {maybe_else_fail,1},
+ Lanno = lineno_anno(L, St1),
+ {Es1,St2} = maybe_match_exprs(Es0, #c_var{name=Fail}, St1),
+
+ %% Translate the 'else' clauses. Note that we must not put the clauses
+ %% as the top-level clauses in the fun, because all shawdowing variables
+ %% in a fun head will be renamed.
+ {Cs1,St3} = clauses(Cs0, St2),
+ Fc1 = fail_clause([FailVar], Lanno, c_tuple([#c_literal{val=else_clause},FailVar])),
+ FailCase = #icase{args=[V2],clauses=Cs1,fc=Fc1},
+ FailFunCs = [#iclause{pats=[V2],guard=[#c_literal{val=true}],
+ body=[FailCase]}],
+ Anno = #a{anno=[letrec_goto,no_inline|Lanno]},
+ Fc2 = fail_clause([FailVar], Lanno, #c_literal{val=never_fails}),
+ FailFun = #ifun{id=[],vars=[V1],
+ clauses=FailFunCs,
+ fc=Fc2},
+
+ %% Construct the letrec.
+ Letrec = #iletrec{anno=Anno,defs=[{Fail,FailFun}],body=Es1},
+ {Letrec,[],St3};
expr({'if',L,Cs0}, St0) ->
{Cs1,St1} = clauses(Cs0, St0),
Lanno = lineno_anno(L, St1),
@@ -717,6 +829,8 @@ expr({'catch',L,E0}, St0) ->
Lanno = lineno_anno(L, St1),
{#icatch{anno=#a{anno=Lanno},body=Eps ++ [E1]},[],St1};
expr({'fun',L,{function,F,A}}, St0) ->
+ %% Generate a new name for eta conversion of local funs (`fun local/123`)
+ %% in case `no_shared_fun_wrappers` is given.
{Fname,St1} = new_fun_name(St0),
Lanno = full_anno(L, St1),
Id = {0,0,Fname},
@@ -734,10 +848,20 @@ expr({named_fun,L,'_',Cs}, St) ->
fun_tq(Cs, L, St, unnamed);
expr({named_fun,L,Name,Cs}, St) ->
fun_tq(Cs, L, St, {named,Name});
-expr({call,L,{remote,_,M,F},As0}, St0) ->
- {[M1,F1|As1],Aps,St1} = safe_list([M,F|As0], St0),
+expr({call,L,{remote,_,M0,F0},As0}, St0) ->
+ {[M1,F1|As1],Aps,St1} = safe_list([M0,F0|As0], St0),
Anno = full_anno(L, St1),
- {#icall{anno=#a{anno=Anno},module=M1,name=F1,args=As1},Aps,St1};
+ case {M1,F1,As1} of
+ {#c_literal{val=erlang},
+ #c_literal{val=error},
+ [#c_tuple{es=[#c_literal{val=badrecord},_]}=Tuple]} ->
+ Fail = #iprimop{anno=#a{anno=Anno},
+ name=#c_literal{val=match_fail},
+ args=[Tuple]},
+ {Fail,Aps,St1};
+ {_,_,_} ->
+ {#icall{anno=#a{anno=Anno},module=M1,name=F1,args=As1},Aps,St1}
+ end;
expr({call,Lc,{atom,Lf,F},As0}, St0) ->
{As1,Aps,St1} = safe_list(As0, St0),
Op = #c_var{anno=lineno_anno(Lf, St1),name={F,length(As1)}},
@@ -748,85 +872,47 @@ expr({call,L,FunExp,As0}, St0) ->
Lanno = lineno_anno(L, St2),
{#iapply{anno=#a{anno=Lanno},op=Fun,args=As1},Fps ++ Aps,St2};
expr({match,L,P0,E0}, St0) ->
- %% First fold matches together to create aliases.
- {P1,E1} = fold_match(E0, P0),
- St1 = set_wanted(P1, St0),
- {E2,Eps1,St2} = novars(E1, St1),
- St3 = St2#core{wanted=St0#core.wanted},
- {P2,St4} = try
- pattern(P1, St3)
- catch
- throw:Thrown ->
- {Thrown,St3}
- end,
- {Fpat,St5} = new_var(St4),
- Lanno = lineno_anno(L, St5),
- Fc = fail_clause([Fpat], Lanno, c_tuple([#c_literal{val=badmatch},Fpat])),
- case P2 of
- nomatch ->
- %% The pattern will not match. We must take care here to
- %% bind all variables that the pattern would have bound
- %% so that subsequent expressions do not refer to unbound
- %% variables.
- %%
- %% As an example, this code:
- %%
- %% [X] = {Y} = E,
- %% X + Y.
- %%
- %% will be rewritten to:
- %%
- %% error({badmatch,E}),
- %% case E of
- %% {[X],{Y}} ->
- %% X + Y;
- %% Other ->
- %% error({badmatch,Other})
- %% end.
- %%
- St6 = add_warning(L, {nomatch,pattern}, St5),
- {Expr,Eps3,St7} = safe(E1, St6),
- SanPat0 = sanitize(P1),
- {SanPat,St} = pattern(SanPat0, St7),
- Badmatch = c_tuple([#c_literal{val=badmatch},Expr]),
- Fail = #iprimop{anno=#a{anno=Lanno},
- name=#c_literal{val=match_fail},
- args=[Badmatch]},
- Eps = Eps3 ++ [Fail],
- {#imatch{anno=#a{anno=Lanno},pat=SanPat,arg=Expr,fc=Fc},Eps,St};
- Other when not is_atom(Other) ->
- %% We must rewrite top-level aliases to lets to avoid unbound
- %% variables in code such as:
- %%
- %% <<42:Sz>> = Sz = B
- %%
- %% If we would keep the top-level aliases the example would
- %% be translated like this:
- %%
- %% case B of
- %% <Sz = #{#<42>(Sz,1,'integer',['unsigned'|['big']])}#>
- %% when 'true' ->
- %% .
- %% .
- %% .
- %%
- %% Here the variable Sz would be unbound in the binary pattern.
+ St1 = set_wanted(P0, St0),
+ case fold_match(E0, P0) of
+ {{sequential_match,_,_,_}=P1,E1} ->
+ %% Matching of an expression to more than one pattern. Example:
%%
- %% Instead we bind Sz in a let to ensure it is bound when
- %% used in the binary pattern:
+ %% #rec{f=Val} = A = Expr
+ {E2,Eps1,St2} = safe(E1, St1),
+ St3 = St2#core{wanted=St0#core.wanted},
+
+ %% If necessary, bind the expression to a variable to ensure it is
+ %% only evaluted once.
+ {Var,Eps2,St4} =
+ case E2 of
+ #c_var{} ->
+ {E2,[],St3};
+ _ ->
+ {Var0,StInt} = new_var(St3),
+ {Var0,[#iset{var=Var0,arg=E2}],StInt}
+ end,
+
+ %% Rewrite to a begin/end block matching one pattern at the time
+ %% (using the `single_match` operator). Example:
%%
- %% let <Sz> = B
- %% in case Sz of
- %% <#{#<42>(Sz,1,'integer',['unsigned'|['big']])}#>
- %% when 'true' ->
- %% .
- %% .
- %% .
- %%
- {P3,E3,Eps2} = letify_aliases(P2, E2),
- Eps = Eps1 ++ Eps2,
- {#imatch{anno=#a{anno=Lanno},pat=P3,arg=E3,fc=Fc},Eps,St5}
+ %% begin
+ %% V = Expr,
+ %% A = V,
+ %% #rec{f=Val} = V
+ %% end
+ Block = blockify(L, P1, Var),
+ {E3,Eps3,St5} = expr({block,L,Block}, St4),
+ {E3,Eps1 ++ Eps2 ++ Eps3,St5};
+ {P0,E1} ->
+ %% Matching of an expression to a single pattern. Example:
+ %% {A,B} = Expr
+ {E2,Eps1,St2} = novars(E1, St1),
+ St3 = St2#core{wanted=St0#core.wanted},
+ {E3,Eps2,St4} = single_match(L, P0, E2, St3),
+ {E3,Eps1 ++ Eps2,St4}
end;
+expr({single_match,L,P,#c_var{}=E}, St0) ->
+ single_match(L, P, E, St0);
expr({op,_,'++',{lc,Llc,E,Qs0},More}, St0) ->
%% Optimise '++' here because of the list comprehension algorithm.
%%
@@ -867,6 +953,56 @@ expr({op,L,Op,L0,R0}, St0) ->
module=#c_literal{anno=LineAnno,val=erlang},
name=#c_literal{anno=LineAnno,val=Op},args=As},Aps,St1}.
+blockify(L0, {sequential_match,_L1,First,Then}, E) ->
+ [{single_match,L0,First,E}|blockify(L0, Then, E)];
+blockify(L, P, E) ->
+ [{single_match,L,P,E}].
+
+%% single_match(Line, AbstractPattern, CoreExpr, State0) -> {Expr,Pre,State}.
+%% Generate the code for matching an expression against a single pattern.
+single_match(L, P0, E, St0) ->
+ {Fpat,St1} = new_var(St0),
+ Lanno = lineno_anno(L, St1),
+ Fc = fail_clause([Fpat], Lanno, c_tuple([#c_literal{val=badmatch},Fpat])),
+ try pattern(P0, St1) of
+ {P1,St2} ->
+ St3 = set_wanted(P0, St2),
+ St4 = St3#core{wanted=St0#core.wanted},
+ {#imatch{anno=#a{anno=Lanno},pat=P1,arg=E,fc=Fc},[],St4}
+ catch
+ throw:nomatch ->
+ %% The pattern will not match. We must take care here to
+ %% bind all variables that the pattern would have bound
+ %% so that subsequent expressions do not refer to unbound
+ %% variables.
+ %%
+ %% As an example, this code:
+ %%
+ %% ([X] = {Y}) = E,
+ %% X + Y.
+ %%
+ %% will be rewritten to:
+ %%
+ %% error({badmatch,E}),
+ %% case E of
+ %% {[X],{Y}} ->
+ %% X + Y;
+ %% Other ->
+ %% error({badmatch,Other})
+ %% end.
+ %%
+ St2 = add_warning(L, {nomatch,pattern}, St1),
+ {Expr,Eps0,St3} = force_safe(E, St2),
+ SanPat0 = sanitize(P0),
+ {SanPat,St} = pattern(SanPat0, St3),
+ Badmatch = c_tuple([#c_literal{val=badmatch},Expr]),
+ Fail = #iprimop{anno=#a{anno=Lanno},
+ name=#c_literal{val=match_fail},
+ args=[Badmatch]},
+ Eps = Eps0 ++ [Fail],
+ {#imatch{anno=#a{anno=Lanno},pat=SanPat,arg=Expr,fc=Fc},Eps,St}
+ end.
+
%% set_wanted(Pattern, St) -> St'.
%% Suppress warnings for expressions that are bound to the '_'
%% variable and variables that begin with '_'.
@@ -881,12 +1017,6 @@ set_wanted({var,_,Var}, St) ->
end;
set_wanted(_, St) -> St.
-letify_aliases(#c_alias{var=V,pat=P0}, E0) ->
- {P1,E1,Eps0} = letify_aliases(P0, V),
- {P1,E1,[#iset{var=V,arg=E0}|Eps0]};
-letify_aliases(P, E) ->
- {P,E,[]}.
-
%% sanitize(Pat) -> SanitizedPattern
%% Rewrite Pat so that it will be accepted by pattern/2 and will
%% bind the same variables as the original pattern.
@@ -1162,7 +1292,7 @@ is_iexprs_small_2(_, Threshold) ->
%% record whereas c_literal should not have a wrapped annotation
expr_bin(Es0, Anno, St0) ->
- Es1 = [bin_element(E) || E <- Es0],
+ Es1 = bin_elements(Es0, 1),
case constant_bin(Es1) of
error ->
case expr_bin_1(Es1, St0) of
@@ -1202,12 +1332,12 @@ bitstrs([E0|Es0], St0) ->
bitstrs([], St) ->
{[],[],St}.
-bitstr({bin_element,Line,{string,_,S},{integer,_,8},_}, St) ->
- bitstrs(bin_expand_string(S, Line, 0, 0, []), St);
-bitstr({bin_element,Line,{string,_,[]},Sz0,Ts}, St0) ->
+bitstr({bin_element,{sl,_,Line},{string,_,S},{integer,_,8},_}, St) ->
+ bitstrs(bin_expand_string(S, {sl,0,Line}, 0, 0, []), St);
+bitstr({bin_element,{sl,_,Line},{string,_,[]},Sz0,Ts}, St0) ->
%% Empty string. We must make sure that the type is correct.
{[#c_bitstr{size=Sz}],Eps0,St1} =
- bitstr({bin_element,Line,{char,Line,0},Sz0,Ts}, St0),
+ bitstr({bin_element,{sl,0,Line},{char,Line,0},Sz0,Ts}, St0),
%% At this point, the type is either a correct literal or
%% an expression.
@@ -1234,12 +1364,12 @@ bitstr({bin_element,Line,{string,_,[]},Sz0,Ts}, St0) ->
Eps = Eps0 ++ Eps1,
{[],Eps,St2}
end;
-bitstr({bin_element,Line,{string,_,S},Sz0,Ts}, St0) ->
- {[Bitstr],Eps,St1} = bitstr({bin_element,Line,{char,Line,0},Sz0,Ts}, St0),
+bitstr({bin_element,{sl,_,Line},{string,_,S},Sz0,Ts}, St0) ->
+ {[Bitstr],Eps,St1} = bitstr({bin_element,{sl,0,Line},{char,Line,0},Sz0,Ts}, St0),
Es = [Bitstr#c_bitstr{val=#c_literal{anno=full_anno(Line, St1),val=C}} ||
C <- S],
{Es,Eps,St1};
-bitstr({bin_element,Line,E0,Size0,[Type,{unit,Unit}|Flags]}, St0) ->
+bitstr({bin_element,{sl,Seg,Line},E0,Size0,[Type,{unit,Unit}|Flags]}, St0) ->
{E1,Eps0,St1} = safe(E0, St0),
{Size1,Eps1,St2} = safe(Size0, St1),
Eps = Eps0 ++ Eps1,
@@ -1263,36 +1393,56 @@ bitstr({bin_element,Line,E0,Size0,[Type,{unit,Unit}|Flags]}, St0) ->
#c_literal{val=all} -> ok;
_ -> throw({bad_binary,Eps,St2})
end,
- {[#c_bitstr{anno=lineno_anno(Line, St2),
+ Anno0 = lineno_anno(Line, St2),
+
+ %% We will add a 'segment' annotation to segments that could
+ %% fail. There is no need to add it to literal segments of fixed
+ %% sized. The annotation will be used by the runtime system to
+ %% provide extended error information if construction of the
+ %% binary fails.
+ Anno = if Seg =:= 0 ->
+ Anno0;
+ true ->
+ [{segment,Seg}|Anno0]
+ end,
+
+ {[#c_bitstr{anno=Anno,
val=E1,size=Size1,
unit=#c_literal{val=Unit},
type=#c_literal{val=Type},
flags=#c_literal{val=Flags}}],
Eps,St2}.
-bin_element({bin_element,Line,Expr,Size0,Type0}) ->
- {Size,Type} = make_bit_type(Line, Size0, Type0),
- {bin_element,Line,Expr,Size,Type}.
+bin_elements([{bin_element,Line,Expr,Size0,Type0}|Es], Seg) ->
+ {Size,Type} = make_bit_type(Line, Size0, Type0, construction),
+ [{bin_element,{sl,Seg,Line},Expr,Size,Type}|bin_elements(Es, Seg+1)];
+bin_elements([], _) -> [].
-make_bit_type(Line, default, Type0) ->
+make_bit_type(Line, default, Type0, _Context) ->
case erl_bits:set_bit_type(default, Type0) of
{ok,all,Bt} -> {make_all_size(Line),erl_bits:as_list(Bt)};
{ok,undefined,Bt} -> {{atom,Line,undefined},erl_bits:as_list(Bt)};
{ok,Size,Bt} -> {{integer,Line,Size},erl_bits:as_list(Bt)}
end;
-make_bit_type(_Line, {atom,Anno,all}=Size, Type0) ->
+make_bit_type(_Line, {atom,Anno,all}=Size, Type0, Context) ->
+ {ok,Size,Bt} = erl_bits:set_bit_type(Size, Type0),
+ Type = erl_bits:as_list(Bt),
case erl_anno:generated(Anno) of
true ->
%% This `all` was created by the compiler from a binary
%% segment without a size.
- {ok,Size,Bt} = erl_bits:set_bit_type(Size, Type0),
- {Size,erl_bits:as_list(Bt)};
+ {Size,Type};
false ->
%% This `all` was present in the source code. It is not
%% a valid size.
- throw(nomatch)
+ case Context of
+ matching ->
+ throw(nomatch);
+ construction ->
+ {{atom,Anno,bad_size},Type}
+ end
end;
-make_bit_type(_Line, Size0, Type0) -> %Integer or 'all'
+make_bit_type(_Line, Size0, Type0, _Context) ->
{ok,Size1,Bt} = erl_bits:set_bit_type(Size0, Type0),
Size = case Size1 of
{char,Anno,CharVal} -> {integer,Anno,CharVal};
@@ -1392,8 +1542,12 @@ bin_expand_string([H|T], Line, Val, Size, Last) ->
bin_expand_string([], Line, Val, Size, Last) ->
[make_combined(Line, Val, Size) | Last].
-make_combined(Line, Val, Size) ->
- {bin_element,Line,{integer,Line,Val},
+make_combined(SegLine, Val, Size) ->
+ Line = case SegLine of
+ {sl,_,Line0} -> Line0;
+ _ -> SegLine
+ end,
+ {bin_element,SegLine,{integer,Line,Val},
{integer,Line,Size},
[integer,{unit,1},unsigned,big]}.
@@ -1777,6 +1931,33 @@ force_novars(#c_map{}=Bin, St) -> {Bin,[],St};
force_novars(Ce, St) ->
force_safe(Ce, St).
+
+%% safe_list(Expr, State) -> {Safe,[PreExpr],State}.
+%% Generate an internal safe expression for a list of
+%% expressions.
+
+safe_list(Es, St0) ->
+ {Vs,Eps0,St} =
+ foldr(fun (E, {Ces,Eps,Sti0}) ->
+ {Ce,Ep,Sti1} = safe(E, Sti0),
+ case Eps of
+ [[#iexprs{bodies=Bs}]|T] ->
+ %% A cons within a cons.
+ {[Ce|Ces],[Ep|Bs]++T,Sti1};
+ _ ->
+ {[Ce|Ces],[Ep|Eps],Sti1}
+ end
+ end, {[],[],St0}, Es),
+ case [Ep || [_|_]=Ep <- Eps0] of
+ [] ->
+ {Vs,[],St};
+ [Ep] ->
+ {Vs,Ep,St};
+ [_|_]=Eps ->
+ %% Two or more bodies. They see the same variables.
+ {Vs,[#iexprs{bodies=Eps}],St}
+ end.
+
%% safe(Expr, State) -> {Safe,[PreExpr],State}.
%% Generate an internal safe expression. These are simples without
%% binaries which can fail. At this level we do not need to do a
@@ -1787,12 +1968,6 @@ safe(E0, St0) ->
{Se,Sps,St2} = force_safe(E1, St1),
{Se,Eps ++ Sps,St2}.
-safe_list(Es, St) ->
- foldr(fun (E, {Ces,Esp,St0}) ->
- {Ce,Ep,St1} = safe(E, St0),
- {[Ce|Ces],Ep ++ Esp,St1}
- end, {[],[],St}, Es).
-
force_safe(#imatch{pat=P,arg=E}=Imatch, St0) ->
{Le,Lps0,St1} = force_safe(E, St0),
Lps = Lps0 ++ [Imatch#imatch{arg=Le}],
@@ -1838,10 +2013,10 @@ is_safe(_) -> false.
%% fold_match(MatchExpr, Pat) -> {MatchPat,Expr}.
%% Fold nested matches into one match with aliased patterns.
-fold_match({match,L,P0,E0}, P) ->
- {P1,E1} = fold_match(E0, P),
- {{match,L,P0,P1},E1};
-fold_match(E, P) -> {P,E}.
+fold_match({match, L, P, E}, E0) ->
+ fold_match(E, {sequential_match, L, P, E0});
+fold_match(E, E0) ->
+ {E0, E}.
%% pattern(Pattern, State) -> {CorePat,[PreExp],State}.
%% Transform a pattern by removing line numbers. We also normalise
@@ -1955,7 +2130,7 @@ pat_segments([P0|Ps0], St0) ->
pat_segments([], St) -> {[],St}.
pat_segment({bin_element,L,Val,Size0,Type0}, St) ->
- {Size1,Type1} = make_bit_type(L, Size0, Type0),
+ {Size1,Type1} = make_bit_type(L, Size0, Type0, matching),
[Type,{unit,Unit}|Flags] = Type1,
Anno = lineno_anno(L, St),
{Pval0,St1} = pattern(Val, St),
@@ -2092,7 +2267,12 @@ new_vars_1(0, _, St, Vs) -> {Vs,St}.
bad_generator(Ps, Generator, Arg) ->
Anno = get_anno(Arg),
Tuple = ann_c_tuple(Anno, [#c_literal{val=bad_generator},Generator]),
- fail_clause(Ps, Anno, Tuple).
+ Call = #icall{anno=#a{anno=Anno}, %Must have an #a{}
+ module=#c_literal{anno=Anno,val=erlang},
+ name=#c_literal{anno=Anno,val=error},
+ args=[Tuple]},
+ #iclause{anno=#a{anno=[compiler_generated]},
+ pats=Ps,guard=[],body=[Call]}.
function_clause(Ps, LineAnno) ->
fail_clause(Ps, LineAnno,
@@ -2129,7 +2309,155 @@ annotate_cons(A, H, T, #core{dialyzer=Dialyzer}) ->
ann_c_cons(A, H, T)
end.
-ubody(B, St) -> uexpr(B, [], St).
+%%%
+%%% Here follows an abstract data structure to help us handle Erlang's
+%%% implicit matching that occurs when a variable is bound more than
+%%% once:
+%%%
+%%% X = Expr1(),
+%%% X = Expr2()
+%%%
+%%% What is implicit in Erlang, must be explicit in Core Erlang; that
+%%% is, repeated variables must be eliminated and explicit matching
+%%% must be added. For simplicity, examples that follow will be given
+%%% in Erlang and not in Core Erlang. Here is how the example can be
+%%% rewritten in Erlang to eliminate the repeated variable:
+%%%
+%%% X = Expr1(),
+%%% X1 = Expr2(),
+%%% if
+%%% X1 =:= X -> X;
+%%% true -> error({badmatch,X1})
+%%% end
+%%%
+%%% To implement the renaming, keeping a set of the variables that
+%%% have been bound so far is **almost** sufficient. When a variable
+%%% in the set is bound a again, it will be renamed and a `case` with
+%%% guard test will be added.
+%%%
+%%% Here is another example:
+%%%
+%%% (X=A) + (X=B)
+%%%
+%%% Note that the operands for a binary operands are allowed to be
+%%% evaluated in any order. Therefore, variables bound on the left
+%%% hand side must not referenced on the right hand side, and vice
+%%% versa. If a variable is bound on both sides, it must be bound
+%%% to the same value.
+%%%
+%%% Using the simple scheme of keeping track of known variables,
+%%% the example can be rewritten like this:
+%%%
+%%% X = A,
+%%% X1 = B,
+%%% if
+%%% X1 =:= X -> ok;
+%%% true -> error({badmatch,X1})
+%%% end,
+%%% X + X1
+%%%
+%%% However, this simple scheme of keeping all previously bound variables in
+%%% a set breaks down for this example:
+%%%
+%%% (X=A) + fun() -> X = B end()
+%%%
+%%% The rewritten code would be:
+%%%
+%%% X = A,
+%%% Tmp = fun() ->
+%%% X1 = B,
+%%% if
+%%% X1 =:= X -> ok;
+%%% true -> error({badmatch,X1})
+%%% end
+%%% end(),
+%%% X + Tmp
+%%%
+%%% That is wrong, because the binding of `X` created on the left hand
+%%% side of `+` must not be seen inside the fun. The correct rewrite
+%%% would be like this:
+%%%
+%%% X = A,
+%%% Tmp = fun() ->
+%%% X1 = B
+%%% end(),
+%%% X + Tmp
+%%%
+%%% To correctly rewrite fun bodies, we will need to keep addtional
+%%% information in a record so that we can remove `X` from the known
+%%% variables when rewriting the body of the fun.
+%%%
+
+-record(known, {base=[],ks=[],prev_ks=[]}).
+
+known_init() ->
+ #known{}.
+
+%% known_get(#known{}) -> [KnownVar].
+%% Get the currently known variables.
+
+known_get(#known{ks=Ks}) ->
+ Ks.
+
+%% known_start_group(#known{}) -> #known{}.
+
+known_start_group(#known{base=OldBase,ks=Ks,prev_ks=PrevKs}=K) ->
+ K#known{base=[Ks|OldBase],prev_ks=[[]|PrevKs]}.
+
+%% known_end_body(#known{}) -> #known{}.
+
+known_end_body(#known{ks=Ks,prev_ks=[_|OldPrevKs]}=K) ->
+ K#known{prev_ks=[Ks|OldPrevKs]}.
+
+%% known_end_group(#known{}) -> #known{}.
+%% Consolidate the known variables after having processed the
+%% last body in a group of bodies that see the same bindings.
+
+known_end_group(#known{base=[_|OldBase],prev_ks=[_|OldPrevKs]}=K) ->
+ K#known{base=OldBase,prev_ks=OldPrevKs}.
+
+%% known_union(#known{}, KnownVarsSet) -> #known{}.
+%% Update the known variables to be the union of the previous
+%% known variables and the set KnownVarsSet.
+
+known_union(#known{ks=Ks}=K, Set) ->
+ K#known{ks=union(Ks, Set)}.
+
+%% known_bind(#known{}, BoundVarsSet) -> #known{}.
+%% Add variables that are known to be bound in the current
+%% body.
+
+known_bind(#known{prev_ks=[PrevKs0|OldPrevKs]}=K, BoundVs) ->
+ PrevKs = subtract(PrevKs0, BoundVs),
+ K#known{prev_ks=[PrevKs|OldPrevKs]};
+known_bind(#known{}=K, _) -> K.
+
+%% known_in_fun(#known{}) -> #known{}.
+%% Update the known variables to only the set of variables that
+%% should be known when entering the fun.
+
+known_in_fun(#known{base=[BaseKs|_],ks=Ks0,prev_ks=[PrevKs|_]}=K) ->
+ %% Within a group of bodies that see the same bindings, calculate
+ %% the known variables for a fun. Example:
+ %%
+ %% A = 1,
+ %% {X = 2, fun() -> X = 99, A = 1 end()}.
+ %%
+ %% In this example:
+ %%
+ %% BaseKs = ['A'], Ks0 = ['A','X'], PrevKs = ['A','X']
+ %%
+ %% Thus, only `A` is known when entering the fun.
+
+ Ks = union(BaseKs, subtract(Ks0, PrevKs)),
+ K#known{base=[],ks=Ks,prev_ks=[]};
+known_in_fun(#known{}=K) -> K.
+
+%%%
+%%% End of abstract data type for known variables.
+%%%
+
+ubody(B, St) -> uexpr(B, known_init(), St).
%% ufun_clauses([Lclause], [KnownVar], State) -> {[Lclause],State}.
@@ -2189,15 +2517,41 @@ do_uclause(#iclause{anno=A0,pats=Ps0,guard=G0,body=B0}, Ks0, St0) ->
false ->
{Pg0,A0}
end,
- Pu = union(Pus, intersection(Pvs, Ks0)),
+ Pu = union(Pus, intersection(Pvs, known_get(Ks0))),
Pn = subtract(Pvs, Pu),
- Ks1 = union(Pn, Ks0),
+ Ks1 = known_union(Ks0, Pn),
{G1,St2} = uguard(Pg, G0, Ks1, St1),
Gu = used_in_any(G1),
Gn = new_in_any(G1),
- Ks2 = union(Gn, Ks1),
- {B1,St3} = uexprs(B0, Ks2, St2),
- Used = intersection(union([Pu,Gu,used_in_any(B1)]), Ks0),
+ Ks2 = known_union(Ks1, Gn),
+
+ %% Consider this example:
+ %%
+ %% {X = A,
+ %% begin X = B, fun() -> X = C end() end}.
+ %%
+ %% At this point it has been rewritten to something similar
+ %% like this (the fun body has not been rewritten yet):
+ %%
+ %% {X = A,
+ %% begin
+ %% X1 = B,
+ %% if
+ %% X1 =:= X -> ok;
+ %% true -> error({badmatch,X1})
+ %% end,
+ %% fun() -> ... end() end
+ %% end}.
+ %%
+ %% In this example, the variable `X` is a known variable that must
+ %% be passed into the fun body (because of `X = B` above). To ensure
+ %% that it is, we must call known_bind/2 with the variables used
+ %% in the guard (`X1` and `X`; any variables used must surely be
+ %% bound).
+
+ Ks3 = known_bind(Ks2, Gu),
+ {B1,_,St3} = uexprs(B0, Ks3, St2),
+ Used = intersection(union([Pu,Gu,used_in_any(B1)]), known_get(Ks0)),
New = union([Pn,Gn,new_in_any(B1)]),
{#iclause{anno=A,pats=Ps1,guard=G1,body=B1},Pvs,Used,New,St3}.
@@ -2222,10 +2576,31 @@ uguard(Pg, Gs0, Ks, St0) ->
St3}
end, {Gs0,St0}, Pg),
%%ok = io:fwrite("core ~w: ~p~n", [?LINE,Gs3]),
- uexprs(Gs3, Ks, St5).
+ {Gs4,_,St6} = uexprs(Gs3, Ks, St5),
+ {Gs4,St6}.
+
+%% ulinearize_exprs([[Kexpr]], [Kexpr]) -> [Kexpr].
+%% Linearize a group of bodies to a linear list of Kernel expressions
+%% while inserting markers for the end of each body and the group
+%% itself.
+
+ulinearize_exprs([Bs|Bss], Les) ->
+ [known_end_body|Bs] ++ ulinearize_exprs(Bss, Les);
+ulinearize_exprs([], Les) ->
+ [known_end_group|Les].
%% uexprs([Kexpr], [KnownVar], State) -> {[Kexpr],State}.
+uexprs([known_end_body|Les], Ks0, St0) ->
+ Ks1 = known_end_body(Ks0),
+ uexprs(Les, Ks1, St0);
+uexprs([known_end_group|Les], Ks0, St0) ->
+ Ks1 = known_end_group(Ks0),
+ uexprs(Les, Ks1, St0);
+uexprs([#iexprs{bodies=Es0}|Les], Ks0, St0) ->
+ Es = ulinearize_exprs(Es0, Les),
+ Ks1 = known_start_group(Ks0),
+ uexprs(Es, Ks1, St0);
uexprs([#imatch{anno=A,pat=P0,arg=Arg,fc=Fc}|Les], Ks, St0) ->
case upat_is_new_var(P0, Ks) of
true ->
@@ -2244,18 +2619,54 @@ uexprs([#imatch{anno=A,pat=P0,arg=Arg,fc=Fc}|Les], Ks, St0) ->
uexprs([#icase{anno=A,args=[Arg],
clauses=[Mc],fc=Fc}], Ks, St0)
end;
-uexprs([Le0|Les0], Ks, St0) ->
- {Le1,St1} = uexpr(Le0, Ks, St0),
- {Les1,St2} = uexprs(Les0, union((get_anno(Le1))#a.ns, Ks), St1),
- {[Le1|Les1],St2};
-uexprs([], _, St) -> {[],St}.
+uexprs([#iset{}|_]=Les0, Ks0, St0) ->
+ uexprs_iset(Les0, [], Ks0, St0);
+uexprs([Le0|Les0], Ks0, St0) ->
+ {Le1,St1} = uexpr(Le0, Ks0, St0),
+ {Les1,Ks,St2} = uexprs(Les0, known_union(Ks0, (get_anno(Le1))#a.ns), St1),
+ {[Le1|Les1],Ks,St2};
+uexprs([], Ks, St) -> {[],Ks,St}.
+
+%% Since the set of known variables can grow quite large, try minimize
+%% the number of union operations on it.
+uexprs_iset([#iset{anno=A0,var=V,arg=Arg0}=Le0|Les0], New0, Ks0, St0) ->
+ case uexpr_need_known(Arg0) of
+ true ->
+ Ks1 = known_union(Ks0, New0),
+ {Le1,St1} = uexpr(Le0, Ks1, St0),
+ New = (get_anno(Le1))#a.ns,
+ {Les1,Ks,St2} = uexprs_iset(Les0, New, Ks1, St1),
+ {[Le1|Les1],Ks,St2};
+ false ->
+ %% We don't need the set of known variables when processing
+ %% Arg0, so we can postpone the call to known_union/2. This
+ %% will save time for functions with a huge number of variables.
+ {Arg,St1} = uexpr(Arg0, none, St0),
+ #a{us=Us,ns=Ns} = get_anno(Arg),
+ A = A0#a{us=del_element(V#c_var.name, Us),
+ ns=add_element(V#c_var.name, Ns)},
+ Le1 = Le0#iset{anno=A,arg=Arg},
+ New = union(New0, A#a.ns),
+ {Les1,Ks,St2} = uexprs_iset(Les0, New, Ks0, St1),
+ {[Le1|Les1],Ks,St2}
+ end;
+uexprs_iset(Les, New, Ks0, St) ->
+ Ks = known_union(Ks0, New),
+ uexprs(Les, Ks, St).
+
+uexpr_need_known(#icall{}) -> false;
+uexpr_need_known(#iapply{}) -> false;
+uexpr_need_known(#ibinary{}) -> false;
+uexpr_need_known(#iprimop{}) -> false;
+uexpr_need_known(#c_literal{}) -> false;
+uexpr_need_known(Core) -> not is_simple(Core).
%% upat_is_new_var(Pattern, [KnownVar]) -> true|false.
%% Test whether the pattern is a single, previously unknown
%% variable.
upat_is_new_var(#c_var{name=V}, Ks) ->
- not is_element(V, Ks);
+ not is_element(V, known_get(Ks));
upat_is_new_var(_, _) ->
false.
@@ -2283,7 +2694,7 @@ uexpr(#iletrec{anno=A,defs=Fs0,body=B0}, Ks, St0) ->
{F1,S1} = uexpr(F0, Ks, S0),
{{Name,F1},S1}
end, St0, Fs0),
- {B1,St2} = uexprs(B0, Ks, St1),
+ {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{anno=Anno}=A,args=As0,clauses=Cs0,fc=Fc0}, Ks, St0) ->
@@ -2298,7 +2709,7 @@ uexpr(#icase{anno=#a{anno=Anno}=A,args=As0,clauses=Cs0,fc=Fc0}, Ks, St0) ->
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}=Fun0, Ks0, St0) ->
- {Fun1,St2} = case Ks0 of
+ {Fun1,St2} = case known_get(Ks0) of
[] ->
{Fun0,St0};
[_|_] ->
@@ -2309,12 +2720,13 @@ uexpr(#ifun{anno=A0,id=Id,vars=As,clauses=Cs0,fc=Fc0,name=Name}=Fun0, Ks0, St0)
Avs = lit_list_vars(As),
Ks1 = case Name of
unnamed -> Ks0;
- {named,FName} -> union(subtract([FName], Avs), Ks0)
+ {named,FName} -> known_union(Ks0, subtract([FName], Avs))
end,
- Ks2 = union(Avs, Ks1),
- {Cs3,St3} = ufun_clauses(Cs2, Ks2, St2),
- {Fc1,St4} = ufun_clause(Fc0, Ks2, St3),
- Used = subtract(intersection(used_in_any(Cs3), Ks1), Avs),
+ Ks2 = known_union(Ks1, Avs),
+ KnownInFun = known_in_fun(Ks2),
+ {Cs3,St3} = ufun_clauses(Cs2, KnownInFun, St2),
+ {Fc1,St4} = ufun_clause(Fc0, KnownInFun, St3),
+ Used = subtract(intersection(used_in_any(Cs3), known_get(Ks1)), Avs),
A1 = A0#a{us=Used,ns=[]},
{#ifun{anno=A1,id=Id,vars=As,clauses=Cs3,fc=Fc1,name=Name},St4};
uexpr(#iapply{anno=A,op=Op,args=As}, _, St) ->
@@ -2331,15 +2743,15 @@ uexpr(#itry{anno=A,args=As0,vars=Vs,body=Bs0,evars=Evs,handler=Hs0}, Ks, St0) ->
%% variables bound in the argument (the code between the 'try' and
%% the 'of' keywords) are exported to the body (the code following
%% the 'of' keyword).
- {As1,St1} = uexprs(As0, Ks, St0),
- ArgKs = union(Ks, new_in_any(As1)),
- {Bs1,St2} = uexprs(Bs0, ArgKs, St1),
- {Hs1,St3} = uexprs(Hs0, Ks, St2),
- Used = intersection(used_in_any(Bs1++Hs1++As1), Ks),
+ {As1,_,St1} = uexprs(As0, Ks, St0),
+ ArgKs = known_union(Ks, new_in_any(As1)),
+ {Bs1,_,St2} = uexprs(Bs0, ArgKs, St1),
+ {Hs1,_,St3} = uexprs(Hs0, Ks, St2),
+ Used = intersection(used_in_any(Bs1++Hs1++As1), known_get(Ks)),
{#itry{anno=A#a{us=Used,ns=[]},
args=As1,vars=Vs,body=Bs1,evars=Evs,handler=Hs1},St3};
uexpr(#icatch{anno=A,body=Es0}, Ks, St0) ->
- {Es1,St1} = uexprs(Es0, Ks, St0),
+ {Es1,_,St1} = uexprs(Es0, Ks, St0),
{#icatch{anno=A#a{us=used_in_any(Es1)},body=Es1},St1};
uexpr(#ireceive1{anno=A,clauses=Cs0}, Ks, St0) ->
{Cs1,St1} = uclauses(Cs0, Ks, St0),
@@ -2349,7 +2761,7 @@ uexpr(#ireceive2{anno=A,clauses=Cs0,timeout=Te0,action=Tes0}, Ks, St0) ->
%% Te0 will never generate new variables.
{Te1,St1} = uexpr(Te0, Ks, St0),
{Cs1,St2} = uclauses(Cs0, Ks, St1),
- {Tes1,St3} = uexprs(Tes0, Ks, St2),
+ {Tes1,_,St3} = uexprs(Tes0, Ks, St2),
Used = union([used_in_any(Cs1),used_in_any(Tes1),(get_anno(Te1))#a.us]),
New = case Cs1 of
[] -> new_in_any(Tes1);
@@ -2358,7 +2770,7 @@ uexpr(#ireceive2{anno=A,clauses=Cs0,timeout=Te0,action=Tes0}, Ks, St0) ->
{#ireceive2{anno=A#a{us=Used,ns=New},
clauses=Cs1,timeout=Te1,action=Tes1},St3};
uexpr(#iprotect{anno=A,body=Es0}, Ks, St0) ->
- {Es1,St1} = uexprs(Es0, Ks, St0),
+ {Es1,_,St1} = uexprs(Es0, Ks, St0),
Used = used_in_any(Es1),
{#iprotect{anno=A#a{us=Used},body=Es1},St1}; %No new variables escape!
uexpr(#ibinary{anno=A,segments=Ss}, _, St) ->
@@ -2383,7 +2795,7 @@ upattern(#c_var{name='_'}, _, St0) ->
{New,St1} = new_var_name(St0),
{#c_var{name=New},[],[New],[],St1};
upattern(#c_var{name=V}=Var, Ks, St0) ->
- case is_element(V, Ks) of
+ case is_element(V, known_get(Ks)) of
true ->
{N,St1} = new_var_name(St0),
New = #c_var{name=N},
@@ -2398,7 +2810,7 @@ upattern(#c_var{name=V}=Var, Ks, St0) ->
end;
upattern(#c_cons{hd=H0,tl=T0}=Cons, Ks, St0) ->
{H1,Hg,Hv,Hu,St1} = upattern(H0, Ks, St0),
- {T1,Tg,Tv,Tu,St2} = upattern(T0, union(Hv, Ks), St1),
+ {T1,Tg,Tv,Tu,St2} = upattern(T0, known_union(Ks, Hv), St1),
{Cons#c_cons{hd=H1,tl=T1},Hg ++ Tg,union(Hv, Tv),union(Hu, Tu),St2};
upattern(#c_tuple{es=Es0}=Tuple, Ks, St0) ->
{Es1,Esg,Esv,Eus,St1} = upattern_list(Es0, Ks, St0),
@@ -2408,7 +2820,7 @@ upattern(#imap{es=Es0}=Map, Ks, St0) ->
{Map#imap{es=Es1},Esg,Esv,Eus,St1};
upattern(#imappair{op=#c_literal{val=exact},key=K0,val=V0}=Pair,Ks,St0) ->
{V,Vg,Vn,Vu,St1} = upattern(V0, Ks, St0),
- {K,St2} = uexprs(K0, Ks, St1),
+ {K,_,St2} = uexprs(K0, Ks, St1),
Ku = used_in_expr(K),
{Pair#imappair{key=K,val=V},Vg,Vn,union(Ku, Vu),St2};
upattern(#ibinary{segments=Es0}=Bin, Ks, St0) ->
@@ -2416,7 +2828,7 @@ upattern(#ibinary{segments=Es0}=Bin, Ks, St0) ->
{Bin#ibinary{segments=Es1},Esg,Esv,Eus,St1};
upattern(#c_alias{var=V0,pat=P0}=Alias, Ks, St0) ->
{V1,Vg,Vv,Vu,St1} = upattern(V0, Ks, St0),
- {P1,Pg,Pv,Pu,St2} = upattern(P0, union(Vv, Ks), St1),
+ {P1,Pg,Pv,Pu,St2} = upattern(P0, known_union(Ks, Vv), St1),
{Alias#c_alias{var=V1,pat=P1},Vg ++ Pg,union(Vv, Pv),union(Vu, Pu),St2};
upattern(Other, _, St) -> {Other,[],[],[],St}. %Constants
@@ -2425,7 +2837,7 @@ upattern(Other, _, St) -> {Other,[],[],[],St}. %Constants
upattern_list([P0|Ps0], Ks, St0) ->
{P1,Pg,Pv,Pu,St1} = upattern(P0, Ks, St0),
- {Ps1,Psg,Psv,Psu,St2} = upattern_list(Ps0, union(Pv, Ks), St1),
+ {Ps1,Psg,Psv,Psu,St2} = upattern_list(Ps0, known_union(Ks, Pv), St1),
{[P1|Ps1],Pg ++ Psg,union(Pv, Psv),union(Pu, Psu),St2};
upattern_list([], _, St) -> {[],[],[],[],St}.
@@ -2452,7 +2864,7 @@ upat_bin(Es0, Ks, St0) ->
%% {[Pat],[GuardTest],[NewVar],[UsedVar],State}.
upat_bin([P0|Ps0], Ks, Bs, St0) ->
{P1,Pg,Pv,Pu,Bs1,St1} = upat_element(P0, Ks, Bs, St0),
- {Ps1,Psg,Psv,Psu,St2} = upat_bin(Ps0, union(Pv, Ks), Bs1, St1),
+ {Ps1,Psg,Psv,Psu,St2} = upat_bin(Ps0, known_union(Ks, Pv), Bs1, St1),
{[P1|Ps1],Pg ++ Psg,union(Pv, Psv),union(Pu, Psu),St2};
upat_bin([], _, _, St) -> {[],[],[],[],St}.
@@ -2475,16 +2887,16 @@ upat_element(#ibitstr{val=H0,size=Sz0}=Seg, Ks, Bs0, St0) ->
case Sz0 of
[#c_var{name=Vname}] ->
{Sz1,Us} = rename_bitstr_size(Vname, Bs0),
- {Sz2,St2} = uexprs([Sz1], Ks, St1),
+ {Sz2,_,St2} = uexprs([Sz1], Ks, St1),
{Seg#ibitstr{val=H1,size=Sz2},Hg,Hv,Us,Bs1,St2};
[#c_literal{}] ->
- {Sz1,St2} = uexprs(Sz0, Ks, St1),
+ {Sz1,_,St2} = uexprs(Sz0, Ks, St1),
Us = [],
{Seg#ibitstr{val=H1,size=Sz1},Hg,Hv,Us,Bs1,St2};
Expr when is_list(Expr) ->
Sz1 = [#iset{var=#c_var{name=Old},arg=#c_var{name=New}} ||
{Old,New} <- Bs0] ++ Expr,
- {Sz2,St2} = uexprs(Sz1, Ks, St1),
+ {Sz2,_,St2} = uexprs(Sz1, Ks, St1),
Us = used_in_expr(Sz2),
{Seg#ibitstr{val=H1,size=Sz2},Hg,Hv,Us,Bs1,St2}
end.
@@ -2558,7 +2970,7 @@ ren_pats([], _Ks, {_,_}=Subs, St) ->
ren_pat(#c_var{name='_'}=P, _Ks, Subs, St) ->
{P,Subs,St};
ren_pat(#c_var{name=V}=Old, Ks, {Isub0,Osub0}=Subs, St0) ->
- case member(V, Ks) of
+ case member(V, known_get(Ks)) of
true ->
case ren_is_subst(V, Osub0) of
{yes,New} ->
@@ -2630,9 +3042,19 @@ ren_is_subst(_V, []) -> no.
%% from case/receive. In subblocks/clauses the AfterVars of the block
%% are just the exported variables.
-cbody(B0, St0) ->
+cbody(B0, Nifs, St0) ->
{B1,_,_,St1} = cexpr(B0, [], St0),
- {B1,St1}.
+ B2 = case sets:is_element(St1#core.function,Nifs) of
+ true ->
+ #c_fun{body=Body0} = B1,
+ Body1 = #c_seq{arg=#c_primop{name=#c_literal{val=nif_start},
+ args=[]},
+ body=Body0},
+ B1#c_fun{body=Body1};
+ false ->
+ B1
+ end,
+ {B2,St1}.
%% cclause(Lclause, [AfterVar], State) -> {Cclause,State}.
%% The AfterVars are the exported variables.
@@ -2916,8 +3338,7 @@ c_add_dummy_export(C, [], St) ->
%%% primop 'recv_wait_timeout'(Timeout)
%%% in case TimedOut of
%%% <'true'> when 'true' ->
-%%% do primop 'timeout'()
-%%% 'no_message'
+%%% 'no_message'
%%% <'false'> when 'true' ->
%%% apply 'recv$^0'/0()
%%% end