aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/common_test/src/ct_repeat.erl2
-rw-r--r--lib/common_test/src/ct_run.erl80
-rw-r--r--lib/compiler/src/sys_core_fold.erl124
-rw-r--r--lib/compiler/src/v3_codegen.erl17
-rw-r--r--lib/compiler/src/v3_core.erl7
-rw-r--r--lib/compiler/src/v3_kernel.erl254
-rw-r--r--lib/compiler/src/v3_kernel.hrl1
-rw-r--r--lib/compiler/src/v3_life.erl97
-rw-r--r--lib/compiler/test/core_SUITE.erl5
-rw-r--r--lib/compiler/test/core_SUITE_data/seq_in_guard.core66
-rw-r--r--lib/compiler/test/core_fold_SUITE.erl21
-rw-r--r--lib/dialyzer/src/dialyzer_analysis_callgraph.erl59
-rw-r--r--lib/dialyzer/src/dialyzer_behaviours.erl12
-rw-r--r--lib/dialyzer/src/dialyzer_callgraph.erl44
-rw-r--r--lib/dialyzer/src/dialyzer_codeserver.erl17
-rw-r--r--lib/dialyzer/src/dialyzer_dataflow.erl66
-rw-r--r--lib/dialyzer/src/dialyzer_gui.erl10
-rw-r--r--lib/dialyzer/src/dialyzer_gui_wx.erl10
-rw-r--r--lib/dialyzer/src/dialyzer_plt.erl37
-rw-r--r--lib/dialyzer/src/dialyzer_races.erl21
-rw-r--r--lib/dialyzer/src/dialyzer_succ_typings.erl34
-rw-r--r--lib/dialyzer/src/dialyzer_typesig.erl153
-rw-r--r--lib/dialyzer/test/Makefile2
-rw-r--r--lib/dialyzer/test/behaviour_SUITE_data/results/sample_behaviour16
-rw-r--r--lib/dialyzer/test/behaviour_SUITE_data/src/custom_sup.erl37
-rw-r--r--lib/dialyzer/test/behaviour_SUITE_data/src/sample_behaviour/sample_callback_wrong.erl3
-rw-r--r--lib/dialyzer/test/dialyzer_common.erl11
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/results/multiple_wrong_opaques2
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/multiple_wrong_opaques.erl8
-rw-r--r--lib/dialyzer/test/options1_SUITE_data/results/compiler1
-rw-r--r--lib/dialyzer/test/r9c_SUITE_data/results/inets1
-rw-r--r--lib/dialyzer/test/small_SUITE_data/results/cerl_hipeify2
-rw-r--r--lib/dialyzer/test/small_SUITE_data/results/inf_loop22
-rw-r--r--lib/dialyzer/test/small_SUITE_data/results/no_local_return3
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/no_local_return.erl12
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/on_load.erl11
-rw-r--r--lib/hipe/cerl/erl_types.erl11
37 files changed, 655 insertions, 604 deletions
diff --git a/lib/common_test/src/ct_repeat.erl b/lib/common_test/src/ct_repeat.erl
index be3c485b75..e6eb135ae8 100644
--- a/lib/common_test/src/ct_repeat.erl
+++ b/lib/common_test/src/ct_repeat.erl
@@ -116,7 +116,7 @@ spawn_tester(script,Ctrl,Args) ->
spawn_tester(func,Ctrl,Opts) ->
Tester = fun() ->
- case catch ct_run:run_test1(Opts) of
+ case catch ct_run:run_test2(Opts) of
{'EXIT',Reason} ->
exit(Reason);
Result ->
diff --git a/lib/common_test/src/ct_run.erl b/lib/common_test/src/ct_run.erl
index ef3e1b6a32..666eb3c988 100644
--- a/lib/common_test/src/ct_run.erl
+++ b/lib/common_test/src/ct_run.erl
@@ -37,7 +37,7 @@
%% Misc internal functions
--export([variables_file_name/1,script_start1/2,run_test1/1]).
+-export([variables_file_name/1,script_start1/2,run_test2/1]).
-include("ct_event.hrl").
-include("ct_util.hrl").
@@ -1775,25 +1775,31 @@ set_group_leader_same_as_shell() ->
false
end.
-check_and_add([{TestDir0,M,_} | Tests], Added) ->
+check_and_add([{TestDir0,M,_} | Tests], Added, PA) ->
case locate_test_dir(TestDir0, M) of
{ok,TestDir} ->
case lists:member(TestDir, Added) of
true ->
- check_and_add(Tests, Added);
+ check_and_add(Tests, Added, PA);
false ->
- true = code:add_patha(TestDir),
- check_and_add(Tests, [TestDir|Added])
+ case lists:member(rm_trailing_slash(TestDir),
+ code:get_path()) of
+ false ->
+ true = code:add_patha(TestDir),
+ check_and_add(Tests, [TestDir|Added], [TestDir|PA]);
+ true ->
+ check_and_add(Tests, [TestDir|Added], PA)
+ end
end;
{error,_} ->
{error,{invalid_directory,TestDir0}}
end;
-check_and_add([], _) ->
- ok.
+check_and_add([], _, PA) ->
+ {ok,PA}.
do_run_test(Tests, Skip, Opts) ->
- case check_and_add(Tests, []) of
- ok ->
+ case check_and_add(Tests, [], []) of
+ {ok,AddedToPath} ->
ct_util:set_testdata({stats,{0,0,{0,0}}}),
ct_util:set_testdata({cover,undefined}),
test_server_ctrl:start_link(local),
@@ -1889,7 +1895,9 @@ do_run_test(Tests, Skip, Opts) ->
end,
lists:foreach(fun(Suite) ->
maybe_cleanup_interpret(Suite, Opts#opts.step)
- end, CleanUp);
+ end, CleanUp),
+ [code:del_path(Dir) || Dir <- AddedToPath],
+ ok;
Error ->
Error
end.
@@ -2378,31 +2386,38 @@ event_handler_init_args2opts([]) ->
%% relative dirs "post run_test erl_args" is not kept!
rel_to_abs(CtArgs) ->
{PA,PZ} = get_pa_pz(CtArgs, [], []),
- io:format(user, "~n", []),
[begin
- code:del_path(filename:basename(D)),
- Abs = filename:absname(D),
- code:add_pathz(Abs),
- if D /= Abs ->
+ Dir = rm_trailing_slash(D),
+ Abs = make_abs(Dir),
+ if Dir /= Abs ->
+ code:del_path(Dir),
+ code:del_path(Abs),
io:format(user, "Converting ~p to ~p and re-inserting "
"with add_pathz/1~n",
- [D, Abs]);
+ [Dir, Abs]);
true ->
- ok
- end
+ code:del_path(Dir)
+ end,
+ code:add_pathz(Abs)
end || D <- PZ],
[begin
- code:del_path(filename:basename(D)),
- Abs = filename:absname(D),
- code:add_patha(Abs),
- if D /= Abs ->
+ Dir = rm_trailing_slash(D),
+ Abs = make_abs(Dir),
+ if Dir /= Abs ->
+ code:del_path(Dir),
+ code:del_path(Abs),
io:format(user, "Converting ~p to ~p and re-inserting "
"with add_patha/1~n",
- [D, Abs]);
- true ->ok
- end
+ [Dir, Abs]);
+ true ->
+ code:del_path(Dir)
+ end,
+ code:add_patha(Abs)
end || D <- PA],
- io:format(user, "~n", []).
+ io:format(user, "~n", []).
+
+rm_trailing_slash(Dir) ->
+ filename:join(filename:split(Dir)).
get_pa_pz([{pa,Dirs} | Args], PA, PZ) ->
get_pa_pz(Args, PA ++ Dirs, PZ);
@@ -2413,6 +2428,19 @@ get_pa_pz([_ | Args], PA, PZ) ->
get_pa_pz([], PA, PZ) ->
{PA,PZ}.
+make_abs(RelDir) ->
+ Tokens = filename:split(filename:absname(RelDir)),
+ filename:join(lists:reverse(make_abs1(Tokens, []))).
+
+make_abs1([".."|Dirs], [_Dir|Path]) ->
+ make_abs1(Dirs, Path);
+make_abs1(["."|Dirs], Path) ->
+ make_abs1(Dirs, Path);
+make_abs1([Dir|Dirs], Path) ->
+ make_abs1(Dirs, [Dir|Path]);
+make_abs1([], Path) ->
+ Path.
+
%% This function translates ct:run_test/1 start options
%% to ct_run start arguments (on the init arguments format) -
%% this is useful mainly for testing the ct_run start functions.
diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl
index 5b155398dc..4e67639805 100644
--- a/lib/compiler/src/sys_core_fold.erl
+++ b/lib/compiler/src/sys_core_fold.erl
@@ -150,14 +150,26 @@ guard(Expr, Sub) ->
opt_guard_try(#c_seq{arg=Arg,body=Body0}=Seq) ->
Body = opt_guard_try(Body0),
case {Arg,Body} of
- {#c_call{},#c_literal{val=false}} ->
- %% We have sequence consisting of a call (evaluted
- %% for a possible exception only), followed by 'false'.
- %% Since the sequence is inside a try block that will
+ {#c_call{module=#c_literal{val=Mod},
+ name=#c_literal{val=Name},
+ args=Args},#c_literal{val=false}} ->
+ %% We have sequence consisting of a call (evaluated
+ %% for a possible exception and/or side effect only),
+ %% followed by 'false'.
+ %% Since the sequence is inside a try block that will
%% default to 'false' if any exception occurs, not
%% evalutating the call will not change the behaviour
- %% of the guard.
- Body;
+ %% provided that the call has no side effects.
+ case erl_bifs:is_pure(Mod, Name, length(Args)) of
+ false ->
+ %% Not a pure BIF (meaning that this is not
+ %% a guard and that we must keep the call).
+ Seq#c_seq{body=Body};
+ true ->
+ %% The BIF has no side effects, so it can
+ %% be safely removed.
+ Body
+ end;
{_,_} ->
Seq#c_seq{body=Body}
end;
@@ -1747,36 +1759,26 @@ opt_bool_clauses([_|_], _, _) ->
%% end. NewVar ->
%% erlang:error(badarg)
%% end.
-%%
-%% We add the extra match-all clause at the end only if Expr is
-%% not guaranteed to evaluate to a boolean.
opt_bool_not(#c_case{arg=Arg,clauses=Cs0}=Case0) ->
case Arg of
#c_call{anno=Anno,module=#c_literal{val=erlang},
name=#c_literal{val='not'},
args=[Expr]} ->
- Cs = opt_bool_not(Anno, Expr, Cs0),
+ Cs = [opt_bool_not_invert(C) || C <- Cs0] ++
+ [#c_clause{anno=[compiler_generated],
+ pats=[#c_var{name=cor_variable}],
+ guard=#c_literal{val=true},
+ body=#c_call{anno=Anno,
+ module=#c_literal{val=erlang},
+ name=#c_literal{val=error},
+ args=[#c_literal{val=badarg}]}}],
Case = Case0#c_case{arg=Expr,clauses=Cs},
opt_bool_not(Case);
_ ->
opt_bool_case_redundant(Case0)
end.
-opt_bool_not(Anno, Expr, Cs) ->
- Tail = case is_bool_expr(Expr) of
- false ->
- [#c_clause{anno=[compiler_generated],
- pats=[#c_var{name=cor_variable}],
- guard=#c_literal{val=true},
- body=#c_call{anno=Anno,
- module=#c_literal{val=erlang},
- name=#c_literal{val=error},
- args=[#c_literal{val=badarg}]}}];
- true -> []
- end,
- [opt_bool_not_invert(C) || C <- Cs] ++ Tail.
-
opt_bool_not_invert(#c_clause{pats=[#c_literal{val=Bool}]}=C) ->
C#c_clause{pats=[#c_literal{val=not Bool}]}.
@@ -2065,32 +2067,7 @@ opt_case_in_let_2(V, Arg0,
(_) -> false end, Es), %Only variables in tuple
false = core_lib:is_var_used(V, B), %Built tuple must not be used.
Arg1 = tuple_to_values(Arg0, length(Es)), %Might fail.
- #c_let{vars=Es,arg=Arg1,body=B};
-opt_case_in_let_2(_, Arg, Cs) ->
- %% simplify_bool_case(Case0) -> Case
- %% Remove unecessary cases like
- %%
- %% case BoolExpr of
- %% true -> true;
- %% false -> false;
- %% ....
- %% end
- %%
- %% where BoolExpr is an expression that can only return true
- %% or false (or throw an exception).
-
- true = is_bool_case(Cs) andalso is_bool_expr(Arg),
- Arg.
-
-is_bool_case([A,B|_]) ->
- (is_bool_clause(true, A) andalso is_bool_clause(false, B))
- orelse (is_bool_clause(false, A) andalso is_bool_clause(true, B)).
-
-is_bool_clause(Bool, #c_clause{pats=[#c_literal{val=Bool}],
- guard=#c_literal{val=true},
- body=#c_literal{val=Bool}}) ->
- true;
-is_bool_clause(_, _) -> false.
+ #c_let{vars=Es,arg=Arg1,body=B}.
%% is_simple_case_arg(Expr) -> true|false
%% Determine whether the Expr is simple enough to be worth
@@ -2612,14 +2589,14 @@ bsm_maybe_ctx_to_binary(V, B) ->
body=B}
end.
-previous_ctx_to_binary(V, #c_seq{arg=#c_primop{name=Name,args=As}}) ->
- case {Name,As} of
- {#c_literal{val=bs_context_to_binary},[#c_var{name=V}]} ->
+previous_ctx_to_binary(V, Core) ->
+ case Core of
+ #c_seq{arg=#c_primop{name=#c_literal{val=bs_context_to_binary},
+ args=[#c_var{name=V}]}} ->
true;
- {_,_} ->
+ _ ->
false
- end;
-previous_ctx_to_binary(_, _) -> false.
+ end.
%% bsm_leftmost(Cs) -> none | ArgumentNumber
%% Find the leftmost argument that does binary matching. Return
@@ -2764,22 +2741,20 @@ add_bin_opt_info(Core, Term) ->
end.
add_warning(Core, Term) ->
- Anno = core_lib:get_anno(Core),
- case lists:member(compiler_generated, Anno) of
- true -> ok;
+ case is_compiler_generated(Core) of
+ true ->
+ ok;
false ->
- case get_line(Anno) of
- Line when Line >= 0 -> %Must be positive.
- File = get_file(Anno),
- Key = {?MODULE,warnings},
- case get(Key) of
- [{File,[{Line,?MODULE,Term}]}|_] ->
- ok; %We already have
+ Anno = core_lib:get_anno(Core),
+ Line = get_line(Anno),
+ File = get_file(Anno),
+ Key = {?MODULE,warnings},
+ case get(Key) of
+ [{File,[{Line,?MODULE,Term}]}|_] ->
+ ok; %We already have
%an identical warning.
- Ws ->
- put(Key, [{File,[{Line,?MODULE,Term}]}|Ws])
- end;
- _ -> ok %Compiler-generated code.
+ Ws ->
+ put(Key, [{File,[{Line,?MODULE,Term}]}|Ws])
end
end.
@@ -2793,14 +2768,7 @@ get_file([]) -> "no_file". % should not happen
is_compiler_generated(Core) ->
Anno = core_lib:get_anno(Core),
- case lists:member(compiler_generated, Anno) of
- true -> true;
- false ->
- case get_line(Anno) of
- Line when Line >= 0 -> false;
- _ -> true
- end
- end.
+ member(compiler_generated, Anno).
get_warnings() ->
ordsets:from_list((erase({?MODULE,warnings}))).
diff --git a/lib/compiler/src/v3_codegen.erl b/lib/compiler/src/v3_codegen.erl
index 6623485609..36d35a8122 100644
--- a/lib/compiler/src/v3_codegen.erl
+++ b/lib/compiler/src/v3_codegen.erl
@@ -1423,20 +1423,7 @@ set_cg([{var,R}], Con, Le, Vdb, Bef, St) ->
Other ->
[{move,Other,Ret}]
end,
- {Ais,clear_dead(Int, Le#l.i, Vdb),St};
-set_cg([], {binary,Segs}, Le, Vdb, Bef, St) ->
- Fail = {f,St#cg.bfail},
- Target = find_scratch_reg(Bef#sr.reg),
- Temp = find_scratch_reg(put_reg(Target, Bef#sr.reg)),
- PutCode = cg_bin_put(Segs, Fail, Bef),
- MaxRegs = max_reg(Bef#sr.reg),
- Code = cg_binary(PutCode, Target, Temp, Fail, MaxRegs, Le#l.a),
- Aft = clear_dead(Bef, Le#l.i, Vdb),
- {Code,Aft,St};
-set_cg([], _, Le, Vdb, Bef, St) ->
- %% This should have been stripped by compiler, just cleanup.
- {[],clear_dead(Bef, Le#l.i, Vdb), St}.
-
+ {Ais,clear_dead(Int, Le#l.i, Vdb),St}.
%%%
%%% Code generation for constructing binaries.
@@ -2067,7 +2054,7 @@ line_1(_, 0) ->
%% Missing line number or line number 0.
{line,[]};
line_1(Name, Line) ->
- {line,[{location,Name,abs(Line)}]}.
+ {line,[{location,Name,Line}]}.
find_loc([Line|T], File, _) when is_integer(Line) ->
find_loc(T, File, Line);
diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl
index 6885405ae0..ad0a8a7654 100644
--- a/lib/compiler/src/v3_core.erl
+++ b/lib/compiler/src/v3_core.erl
@@ -2085,7 +2085,12 @@ bitstr_vars(Segs, Vs) ->
lineno_anno(L, St) ->
{line, Line} = erl_parse:get_attribute(L, line),
- [Line] ++ St#core.file.
+ if
+ Line < 0 ->
+ [-Line] ++ St#core.file ++ [compiler_generated];
+ true ->
+ [Line] ++ St#core.file
+ end.
get_ianno(Ce) ->
case get_anno(Ce) of
diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl
index f2eaa37617..abe94ad991 100644
--- a/lib/compiler/src/v3_kernel.erl
+++ b/lib/compiler/src/v3_kernel.erl
@@ -88,8 +88,6 @@
-include("core_parse.hrl").
-include("v3_kernel.hrl").
--define(EXPENSIVE_BINARY_LIMIT, 256).
-
%% These are not defined in v3_kernel.hrl.
get_kanno(Kthing) -> element(2, Kthing).
set_kanno(Kthing, Anno) -> setelement(2, Kthing, Anno).
@@ -120,7 +118,6 @@ copy_anno(Kdst, Ksrc) ->
funs=[], %Fun functions
free=[], %Free variables
ws=[] :: [warning()], %Warnings.
- lit, %Constant pool for literals.
guard_refc=0}). %> 0 means in guard
-spec module(cerl:c_module(), [compile:option()]) ->
@@ -129,7 +126,7 @@ copy_anno(Kdst, Ksrc) ->
module(#c_module{anno=A,name=M,exports=Es,attrs=As,defs=Fs}, _Options) ->
Kas = attributes(As),
Kes = map(fun (#c_var{name={_,_}=Fname}) -> Fname end, Es),
- St0 = #kern{lit=dict:new()},
+ St0 = #kern{},
{Kfs,St} = mapfoldl(fun function/2, St0, Fs),
{ok,#k_mdef{anno=A,name=M#c_literal.val,exports=Kes,attributes=Kas,
body=Kfs ++ St#kern.funs},lists:sort(St#kern.ws)}.
@@ -250,26 +247,20 @@ expr(#c_var{anno=A,name={_Name,Arity}}=Fname, Sub, St) ->
expr(Fun, Sub, St);
expr(#c_var{anno=A,name=V}, Sub, St) ->
{#k_var{anno=A,name=get_vsub(V, Sub)},[],St};
-expr(#c_literal{}=Lit, Sub, St) ->
- Core = handle_literal(Lit),
- expr(Core, Sub, St);
-expr(#k_literal{val=Val0}=Klit, _Sub, #kern{lit=Literals0}=St) ->
- %% Share identical literals to save some space and time during compilation.
- case dict:find(Val0, Literals0) of
- {ok,Val} ->
- {Klit#k_literal{val=Val},[],St};
- error ->
- Literals = dict:store(Val0, Val0, Literals0),
- {Klit,[],St#kern{lit=Literals}}
- end;
-expr(#k_nil{}=V, _Sub, St) ->
- {V,[],St};
-expr(#k_int{}=V, _Sub, St) ->
- {V,[],St};
-expr(#k_float{}=V, _Sub, St) ->
- {V,[],St};
-expr(#k_atom{}=V, _Sub, St) ->
- {V,[],St};
+expr(#c_literal{anno=A,val=V}, _Sub, St) ->
+ Klit = case V of
+ [] ->
+ #k_nil{anno=A};
+ V when is_integer(V) ->
+ #k_int{anno=A,val=V};
+ V when is_float(V) ->
+ #k_float{anno=A,val=V};
+ V when is_atom(V) ->
+ #k_atom{anno=A,val=V};
+ _ ->
+ #k_literal{anno=A,val=V}
+ end,
+ {Klit,[],St};
expr(#c_cons{anno=A,hd=Ch,tl=Ct}, Sub, St0) ->
%% Do cons in two steps, first the expressions left to right, then
%% any remaining literals right to left.
@@ -610,7 +601,6 @@ is_atomic(#k_int{}) -> true;
is_atomic(#k_float{}) -> true;
is_atomic(#k_atom{}) -> true;
%%is_atomic(#k_char{}) -> true; %No characters
-%%is_atomic(#k_string{}) -> true;
is_atomic(#k_nil{}) -> true;
is_atomic(#k_var{}) -> true;
is_atomic(_) -> false.
@@ -919,9 +909,8 @@ match_guard_1([#iclause{anno=A,osub=Osub,guard=G,body=B}|Cs0], Def0, St0) ->
true ->
%% The true clause body becomes the default.
{Kb,Pb,St1} = body(B, Osub, St0),
- Line = get_line(A),
- St2 = maybe_add_warning(Cs0, Line, St1),
- St = maybe_add_warning(Def0, Line, St2),
+ St2 = maybe_add_warning(Cs0, A, St1),
+ St = maybe_add_warning(Def0, A, St2),
{[],pre_seq(Pb, Kb),St};
false ->
{Kg,St1} = guard(G, Osub, St0),
@@ -932,15 +921,18 @@ match_guard_1([#iclause{anno=A,osub=Osub,guard=G,body=B}|Cs0], Def0, St0) ->
end;
match_guard_1([], Def, St) -> {[],Def,St}.
-maybe_add_warning([C|_], Line, St) ->
- maybe_add_warning(C, Line, St);
-maybe_add_warning([], _Line, St) -> St;
-maybe_add_warning(fail, _Line, St) -> St;
-maybe_add_warning(Ke, MatchLine, St) ->
- case get_kanno(Ke) of
- [compiler_generated|_] -> St;
- Anno ->
+maybe_add_warning([C|_], MatchAnno, St) ->
+ maybe_add_warning(C, MatchAnno, St);
+maybe_add_warning([], _MatchAnno, St) -> St;
+maybe_add_warning(fail, _MatchAnno, St) -> St;
+maybe_add_warning(Ke, MatchAnno, St) ->
+ case is_compiler_generated(Ke) of
+ true ->
+ St;
+ false ->
+ Anno = get_kanno(Ke),
Line = get_line(Anno),
+ MatchLine = get_line(MatchAnno),
Warn = case MatchLine of
none -> nomatch_shadow;
_ -> {nomatch_shadow,MatchLine}
@@ -1122,7 +1114,6 @@ select_bin_int([#iclause{pats=[#k_bin_seg{anno=A,type=integer,
end,
select_assert_match_possible(Bits, Val, Fl),
P = #k_bin_int{anno=A,size=Sz,unit=U,flags=Fl,val=Val,next=N},
- select_assert_match_possible(Bits, Val, Fl),
case member(native, Fl) of
true -> throw(not_possible);
false -> ok
@@ -1264,8 +1255,6 @@ match_clause([U|Us], [C|_]=Cs0, Def, St0) ->
sub_size_var(#k_bin_seg{size=#k_var{name=Name}=Kvar}=BinSeg, [#iclause{isub=Sub}|_]) ->
BinSeg#k_bin_seg{size=Kvar#k_var{name=get_vsub(Name, Sub)}};
-sub_size_var(#k_bin_int{size=#k_var{name=Name}=Kvar}=BinSeg, [#iclause{isub=Sub}|_]) ->
- BinSeg#k_bin_int{size=Kvar#k_var{name=get_vsub(Name, Sub)}};
sub_size_var(K, _) -> K.
get_con([C|_]) -> arg_arg(clause_arg(C)). %Get the constructor
@@ -1383,7 +1372,6 @@ arg_con(Arg) ->
#k_tuple{} -> k_tuple;
#k_binary{} -> k_binary;
#k_bin_end{} -> k_bin_end;
- #k_bin_int{} -> k_bin_int;
#k_bin_seg{} -> k_bin_seg;
#k_var{} -> k_var
end.
@@ -1394,15 +1382,9 @@ arg_val(Arg) ->
#k_int{val=I} -> I;
#k_float{val=F} -> F;
#k_atom{val=A} -> A;
- #k_nil{} -> 0;
- #k_cons{} -> 2;
#k_tuple{es=Es} -> length(Es);
#k_bin_seg{size=S,unit=U,type=T,flags=Fs} ->
- {set_kanno(S, []),U,T,Fs};
- #k_bin_int{} ->
- 0;
- #k_bin_end{} -> 0;
- #k_binary{} -> 0
+ {set_kanno(S, []),U,T,Fs}
end.
%% ubody_used_vars(Expr, State) -> [UsedVar]
@@ -1432,14 +1414,12 @@ ubody(#ivalues{anno=A,args=As}, return, St) ->
{#k_return{anno=#k{us=Au,ns=[],a=A},args=As},Au,St};
ubody(#ivalues{anno=A,args=As}, {break,_Vbs}, St) ->
Au = lit_list_vars(As),
- if St#kern.guard_refc > 0 ->
+ case is_in_guard(St) of
+ true ->
{#k_guard_break{anno=#k{us=Au,ns=[],a=A},args=As},Au,St};
- true ->
+ false ->
{#k_break{anno=#k{us=Au,ns=[],a=A},args=As},Au,St}
end;
-ubody(#ivalues{anno=A,args=As}, {guard_break,_Vbs}, St) ->
- Au = lit_list_vars(As),
- {#k_guard_break{anno=#k{us=Au,ns=[],a=A},args=As},Au,St};
ubody(E, return, St0) ->
%% Enterable expressions need no trailing return.
case is_enter_expr(E) of
@@ -1456,12 +1436,7 @@ ubody(E, {break,_Rs} = Break, St0) ->
false ->
{Ea,Pa,St1} = force_atomic(E, St0),
ubody(pre_seq(Pa, #ivalues{args=[Ea]}), Break, St1)
- end;
-ubody(E, {guard_break,_Rs} = GuardBreak, St0) ->
- %%ok = io:fwrite("ubody ~w:~p~n", [?LINE,{E,Br}]),
- %% Exiting expressions need no trailing break.
- {Ea,Pa,St1} = force_atomic(E, St0),
- ubody(pre_seq(Pa, #ivalues{args=[Ea]}), GuardBreak, St1).
+ end.
iletrec_funs(#iletrec{defs=Fs}, St0) ->
%% Use union of all free variables.
@@ -1513,64 +1488,21 @@ is_enter_expr(#k_receive{}) -> true;
is_enter_expr(#k_receive_next{}) -> true;
is_enter_expr(_) -> false.
-%% uguard(Expr, State) -> {Expr,[UsedVar],State}.
-%% Tag the guard sequence with its used variables.
-
-uguard(#k_try{anno=A,arg=B0,vars=[#k_var{name=X}],body=#k_var{name=X},
- handler=#k_atom{val=false}}=Try, St0) ->
- {B1,Bu,St1} = uguard(B0, St0),
- {Try#k_try{anno=#k{us=Bu,ns=[],a=A},arg=B1},Bu,St1};
-uguard(T, St) ->
- %%ok = io:fwrite("~w: ~p~n", [?LINE,T]),
- uguard_test(T, St).
-
-%% uguard_test(Expr, State) -> {Test,[UsedVar],State}.
-%% At this stage tests are just expressions which don't return any
-%% values.
-
-uguard_test(T, St) -> uguard_expr(T, [], St).
+%% uexpr(Expr, Break, State) -> {Expr,[UsedVar],State}.
+%% Tag an expression with its used variables.
+%% Break = return | {break,[RetVar]}.
-uguard_expr(#iset{anno=A,vars=Vs,arg=E0,body=B0}, Rs, St0) ->
- Ns = lit_list_vars(Vs),
- {E1,Eu,St1} = uguard_expr(E0, Vs, St0),
- {B1,Bu,St2} = uguard_expr(B0, Rs, St1),
- Used = union(Eu, subtract(Bu, Ns)),
- {#k_seq{anno=#k{us=Used,ns=Ns,a=A},arg=E1,body=B1},Used,St2};
-uguard_expr(#k_try{anno=A,arg=B0,vars=[#k_var{name=X}],body=#k_var{name=X},
- handler=#k_atom{val=false}}=Try, Rs, St0) ->
- {B1,Bu,St1} = uguard_expr(B0, Rs, St0),
- {Try#k_try{anno=#k{us=Bu,ns=lit_list_vars(Rs),a=A},arg=B1,ret=Rs},
- Bu,St1};
-uguard_expr(#k_test{anno=A,op=Op,args=As}=Test, Rs, St) ->
+uexpr(#k_test{anno=A,op=Op,args=As}=Test, {break,Rs}, St) ->
[] = Rs, %Sanity check
Used = union(op_vars(Op), lit_list_vars(As)),
{Test#k_test{anno=#k{us=Used,ns=lit_list_vars(Rs),a=A}},
Used,St};
-uguard_expr(#k_bif{anno=A,op=Op,args=As}=Bif, Rs, St) ->
- Used = union(op_vars(Op), lit_list_vars(As)),
- {Bif#k_bif{anno=#k{us=Used,ns=lit_list_vars(Rs),a=A},ret=Rs},
- Used,St};
-uguard_expr(#ivalues{anno=A,args=As}, Rs, St) ->
- Sets = foldr2(fun (V, Arg, Rhs) ->
- #iset{anno=A,vars=[V],arg=Arg,body=Rhs}
- end, #k_atom{val=true}, Rs, As),
- uguard_expr(Sets, [], St);
-uguard_expr(#k_match{anno=A,vars=Vs,body=B0}, Rs, St0) ->
- %% Experimental support for andalso/orelse in guards.
- Br = {guard_break,Rs},
- {B1,Bu,St1} = umatch(B0, Br, St0),
- {#k_guard_match{anno=#k{us=Bu,ns=lit_list_vars(Rs),a=A},
- vars=Vs,body=B1,ret=Rs},Bu,St1};
-uguard_expr(Lit, Rs, St) ->
- %% Transform literals to puts here.
- Used = lit_vars(Lit),
- {#k_put{anno=#k{us=Used,ns=lit_list_vars(Rs),a=get_kanno(Lit)},
- arg=Lit,ret=Rs},Used,St}.
-
-%% uexpr(Expr, Break, State) -> {Expr,[UsedVar],State}.
-%% Tag an expression with its used variables.
-%% Break = return | {break,[RetVar]}.
-
+uexpr(#iset{anno=A,vars=Vs,arg=E0,body=B0}, {break,_}=Br, St0) ->
+ Ns = lit_list_vars(Vs),
+ {E1,Eu,St1} = uexpr(E0, {break,Vs}, St0),
+ {B1,Bu,St2} = uexpr(B0, Br, St1),
+ Used = union(Eu, subtract(Bu, Ns)),
+ {#k_seq{anno=#k{us=Used,ns=Ns,a=A},arg=E1,body=B1},Used,St2};
uexpr(#k_call{anno=A,op=#k_local{name=F,arity=Ar}=Op,args=As0}=Call, Br, St) ->
Free = get_free(F, Ar, St),
As1 = As0 ++ Free, %Add free variables LAST!
@@ -1602,10 +1534,11 @@ uexpr(#k_match{anno=A,vars=Vs0,body=B0}, Br, St0) ->
Vs = handle_reuse_annos(Vs0, St0),
Rs = break_rets(Br),
{B1,Bu,St1} = umatch(B0, Br, St0),
- if St0#kern.guard_refc > 0 ->
+ case is_in_guard(St1) of
+ true ->
{#k_guard_match{anno=#k{us=Bu,ns=lit_list_vars(Rs),a=A},
vars=Vs,body=B1,ret=Rs},Bu,St1};
- true ->
+ false ->
{#k_match{anno=#k{us=Bu,ns=lit_list_vars(Rs),a=A},
vars=Vs,body=B1,ret=Rs},Bu,St1}
end;
@@ -1622,24 +1555,27 @@ uexpr(#k_receive_accept{anno=A}, _, St) ->
{#k_receive_accept{anno=#k{us=[],ns=[],a=A}},[],St};
uexpr(#k_receive_next{anno=A}, _, St) ->
{#k_receive_next{anno=#k{us=[],ns=[],a=A}},[],St};
-uexpr(#k_try{anno=A,arg=A0,vars=Vs,body=B0,evars=Evs,handler=H0},
- {break,Rs0}, St0) ->
- {Avs,St1} = new_vars(length(Vs), St0), %Need dummy names here
- {A1,Au,St2} = ubody(A0, {break,Avs}, St1), %Must break to clean up here!
- {B1,Bu,St3} = ubody(B0, {break,Rs0}, St2),
- {H1,Hu,St4} = ubody(H0, {break,Rs0}, St3),
- %% Guarantee ONE return variable.
- NumNew = if
- Rs0 =:= [] -> 1;
- true -> 0
- end,
- {Ns,St5} = new_vars(NumNew, St4),
- Rs1 = Rs0 ++ Ns,
- Used = union([Au,subtract(Bu, lit_list_vars(Vs)),
- subtract(Hu, lit_list_vars(Evs))]),
- {#k_try{anno=#k{us=Used,ns=lit_list_vars(Rs1),a=A},
- arg=A1,vars=Vs,body=B1,evars=Evs,handler=H1,ret=Rs1},
- Used,St5};
+uexpr(#k_try{anno=A,arg=A0,vars=Vs,body=B0,evars=Evs,handler=H0}=Try,
+ {break,Rs0}=Br, St0) ->
+ case is_in_guard(St0) of
+ true ->
+ {[#k_var{name=X}],#k_var{name=X}} = {Vs,B0}, %Assertion.
+ #k_atom{val=false} = H0, %Assertion.
+ {A1,Bu,St1} = uexpr(A0, Br, St0),
+ {Try#k_try{anno=#k{us=Bu,ns=lit_list_vars(Rs0),a=A},
+ arg=A1,ret=Rs0},Bu,St1};
+ false ->
+ {Avs,St1} = new_vars(length(Vs), St0),
+ {A1,Au,St2} = ubody(A0, {break,Avs}, St1),
+ {B1,Bu,St3} = ubody(B0, Br, St2),
+ {H1,Hu,St4} = ubody(H0, Br, St3),
+ {Rs1,St5} = ensure_return_vars(Rs0, St4),
+ Used = union([Au,subtract(Bu, lit_list_vars(Vs)),
+ subtract(Hu, lit_list_vars(Evs))]),
+ {#k_try{anno=#k{us=Used,ns=lit_list_vars(Rs1),a=A},
+ arg=A1,vars=Vs,body=B1,evars=Evs,handler=H1,ret=Rs1},
+ Used,St5}
+ end;
uexpr(#k_try{anno=A,arg=A0,vars=Vs,body=B0,evars=Evs,handler=H0},
return, St0) ->
{Avs,St1} = new_vars(length(Vs), St0), %Need dummy names here
@@ -1685,12 +1621,13 @@ uexpr(#ifun{anno=A,vars=Vs,body=B0}, {break,Rs}, St0) ->
#k_int{val=Index},#k_int{val=Uniq}|Fvs],
ret=Rs},
Free,add_local_function(Fun, St)};
-uexpr(Lit, {break,Rs}, St) ->
+uexpr(Lit, {break,Rs0}, St0) ->
%% Transform literals to puts here.
%%ok = io:fwrite("uexpr ~w:~p~n", [?LINE,Lit]),
Used = lit_vars(Lit),
+ {Rs,St1} = ensure_return_vars(Rs0, St0),
{#k_put{anno=#k{us=Used,ns=lit_list_vars(Rs),a=get_kanno(Lit)},
- arg=Lit,ret=Rs},Used,St}.
+ arg=Lit,ret=Rs},Used,St1}.
add_local_function(_, #kern{funs=ignore}=St) -> St;
add_local_function(F, #kern{funs=Funs}=St) -> St#kern{funs=[F|Funs]}.
@@ -1747,6 +1684,11 @@ bif_returns(#k_internal{name=N,arity=Ar}, Rs, St0) ->
{Ns,St1} = new_vars(bif_vals(N, Ar) - length(Rs), St0),
{Rs ++ Ns,St1}.
+%% ensure_return_vars([Ret], State) -> {[Ret],State}.
+
+ensure_return_vars([], St) -> new_vars(1, St);
+ensure_return_vars([_]=Rs, St) -> {Rs,St}.
+
%% umatch(Match, Break, State) -> {Match,[UsedVar],State}.
%% Tag a match expression with its used variables.
@@ -1779,7 +1721,8 @@ umatch(#k_guard{anno=A,clauses=Gs0}, Br, St0) ->
{#k_guard{anno=#k{us=Gus,ns=[],a=A},clauses=Gs1},Gus,St1};
umatch(#k_guard_clause{anno=A,guard=G0,body=B0}, Br, St0) ->
%%ok = io:fwrite("~w: ~p~n", [?LINE,G0]),
- {G1,Gu,St1} = uguard(G0, St0#kern{guard_refc=St0#kern.guard_refc+1}),
+ {G1,Gu,St1} = uexpr(G0, {break,[]},
+ St0#kern{guard_refc=St0#kern.guard_refc+1}),
%%ok = io:fwrite("~w: ~p~n", [?LINE,G1]),
{B1,Bu,St2} = umatch(B0, Br, St1#kern{guard_refc=St1#kern.guard_refc-1}),
Used = union(Gu, Bu),
@@ -1827,7 +1770,6 @@ lit_list_vars(Ps) ->
pat_vars(#k_var{name=N}) -> {[],[N]};
%%pat_vars(#k_char{}) -> {[],[]};
-%%pat_vars(#k_string{}) -> {[],[]};
pat_vars(#k_literal{}) -> {[],[]};
pat_vars(#k_int{}) -> {[],[]};
pat_vars(#k_float{}) -> {[],[]};
@@ -1854,34 +1796,6 @@ pat_list_vars(Ps) ->
{union(Used0, Used),union(New0, New)} end,
{[],[]}, Ps).
-%% handle_literal(Literal, Anno) -> Kernel
-%% Examine the literal. Complex (heap-based) literals such as lists,
-%% tuples, and binaries should be kept as literals and put into the constant pool.
-%%
-%% (If necessary, this function could be extended to go through the literal
-%% and convert huge binary literals to bit syntax expressions. We don't do that
-%% because v3_core does not produce huge binary literals, and the optimizations in
-%% sys_core_fold don't do much optimizations of binaries. IF THAT CHANGE IS MADE,
-%% ALSO CHANGE sys_core_dsetel.)
-
-handle_literal(#c_literal{anno=A,val=V}) ->
- case V of
- [_|_] ->
- #k_literal{anno=A,val=V};
- [] ->
- #k_nil{anno=A};
- V when is_tuple(V) ->
- #k_literal{anno=A,val=V};
- V when is_bitstring(V) ->
- #k_literal{anno=A,val=V};
- V when is_integer(V) ->
- #k_int{anno=A,val=V};
- V when is_float(V) ->
- #k_float{anno=A,val=V};
- V when is_atom(V) ->
- #k_atom{anno=A,val=V}
- end.
-
make_list(Es) ->
foldr(fun(E, Acc) ->
#c_cons{hd=E,tl=Acc}
@@ -1893,6 +1807,11 @@ integers(N, M) when N =< M ->
[N|integers(N + 1, M)];
integers(_, _) -> [].
+%% is_in_guard(State) -> true|false.
+
+is_in_guard(#kern{guard_refc=Refc}) ->
+ Refc > 0.
+
%%%
%%% Handling of errors and warnings.
%%%
@@ -1913,7 +1832,10 @@ format_error(bad_call) ->
add_warning(none, Term, Anno, #kern{ws=Ws}=St) ->
File = get_file(Anno),
St#kern{ws=[{File,[{?MODULE,Term}]}|Ws]};
-add_warning(Line, Term, Anno, #kern{ws=Ws}=St) when Line >= 0 ->
+add_warning(Line, Term, Anno, #kern{ws=Ws}=St) ->
File = get_file(Anno),
- St#kern{ws=[{File,[{Line,?MODULE,Term}]}|Ws]};
-add_warning(_, _, _, St) -> St.
+ St#kern{ws=[{File,[{Line,?MODULE,Term}]}|Ws]}.
+
+is_compiler_generated(Ke) ->
+ Anno = get_kanno(Ke),
+ member(compiler_generated, Anno).
diff --git a/lib/compiler/src/v3_kernel.hrl b/lib/compiler/src/v3_kernel.hrl
index 37f2fdcf7e..17904c37da 100644
--- a/lib/compiler/src/v3_kernel.hrl
+++ b/lib/compiler/src/v3_kernel.hrl
@@ -35,7 +35,6 @@
-record(k_int, {anno=[],val}).
-record(k_float, {anno=[],val}).
-record(k_atom, {anno=[],val}).
--record(k_string, {anno=[],val}).
-record(k_nil, {anno=[]}).
-record(k_tuple, {anno=[],es}).
diff --git a/lib/compiler/src/v3_life.erl b/lib/compiler/src/v3_life.erl
index 93f8034230..22f3e3c2d2 100644
--- a/lib/compiler/src/v3_life.erl
+++ b/lib/compiler/src/v3_life.erl
@@ -112,53 +112,14 @@ guard(#k_try{anno=A,arg=Ts,vars=[#k_var{name=X}],body=#k_var{name=X},
%% Lock variables that are alive before try and used afterwards.
%% Don't lock variables that are only used inside the try expression.
Pdb0 = vdb_sub(I, I+1, Vdb),
- {T,MaxI,Pdb1} = guard_body(Ts, I+1, Pdb0),
+ {T,MaxI,Pdb1} = body(Ts, I+1, Pdb0),
Pdb2 = use_vars(A#k.ns, MaxI+1, Pdb1), %Save "return" values
- #l{ke={protected,T,var_list(Rs)},i=I,a=A#k.a,vdb=Pdb2};
-guard(#k_seq{}=G, I, Vdb0) ->
- {Es,_,Vdb1} = guard_body(G, I, Vdb0),
- #l{ke={block,Es},i=I,vdb=Vdb1,a=[]};
-guard(G, I, Vdb) -> guard_expr(G, I, Vdb).
-
-%% guard_body(Kbody, I, Vdb) -> {[Expr],MaxI,Vdb}.
-
-guard_body(#k_seq{arg=Ke,body=Kb}, I, Vdb0) ->
- A = get_kanno(Ke),
- Vdb1 = use_vars(A#k.us, I, new_vars(A#k.ns, I, Vdb0)),
- {Es,MaxI,Vdb2} = guard_body(Kb, I+1, Vdb1),
- E = guard_expr(Ke, I, Vdb2),
- {[E|Es],MaxI,Vdb2};
-guard_body(Ke, I, Vdb0) ->
- A = get_kanno(Ke),
- Vdb1 = use_vars(A#k.us, I, new_vars(A#k.ns, I, Vdb0)),
- E = guard_expr(Ke, I, Vdb1),
- {[E],I,Vdb1}.
-
-%% guard_expr(Call, I, Vdb) -> Expr
-
-guard_expr(#k_test{anno=A,op=Op,args=As}, I, _Vdb) ->
- #l{ke={test,test_op(Op),atomic_list(As)},i=I,a=A#k.a};
-guard_expr(#k_bif{anno=A,op=Op,args=As,ret=Rs}, I, _Vdb) ->
- Name = bif_op(Op),
- Ar = length(As),
- case is_gc_bif(Name, Ar) of
- false ->
- #l{ke={bif,Name,atomic_list(As),var_list(Rs)},i=I,a=A#k.a};
- true ->
- #l{ke={gc_bif,Name,atomic_list(As),var_list(Rs)},i=I,a=A#k.a}
- end;
-guard_expr(#k_put{anno=A,arg=Arg,ret=Rs}, I, _Vdb) ->
- #l{ke={set,var_list(Rs),literal(Arg, [])},i=I,a=A#k.a};
-guard_expr(#k_guard_match{anno=A,body=Kb,ret=Rs}, I, Vdb) ->
- %% Support for andalso/orelse in guards.
- %% Work out imported variables which need to be locked.
- Mdb = vdb_sub(I, I+1, Vdb),
- M = match(Kb, A#k.us, I+1, [], Mdb),
- #l{ke={guard_match,M,var_list(Rs)},i=I,vdb=use_vars(A#k.us, I+1, Mdb),a=A#k.a};
-guard_expr(G, I, Vdb) -> guard(G, I, Vdb).
+ #l{ke={protected,T,var_list(Rs)},i=I,a=A#k.a,vdb=Pdb2}.
%% expr(Kexpr, I, Vdb) -> Expr.
+expr(#k_test{anno=A,op=Op,args=As}, I, _Vdb) ->
+ #l{ke={test,test_op(Op),atomic_list(As)},i=I,a=A#k.a};
expr(#k_call{anno=A,op=Op,args=As,ret=Rs}, I, _Vdb) ->
#l{ke={call,call_op(Op),atomic_list(As),var_list(Rs)},i=I,a=A#k.a};
expr(#k_enter{anno=A,op=Op,args=As}, I, _Vdb) ->
@@ -176,25 +137,11 @@ expr(#k_guard_match{anno=A,body=Kb,ret=Rs}, I, Vdb) ->
Mdb = vdb_sub(I, I+1, Vdb),
M = match(Kb, A#k.us, I+1, [], Mdb),
#l{ke={guard_match,M,var_list(Rs)},i=I,vdb=use_vars(A#k.us, I+1, Mdb),a=A#k.a};
-expr(#k_try{anno=A,arg=Ka,vars=Vs,body=Kb,evars=Evs,handler=Kh,ret=Rs}, I, Vdb) ->
- %% Lock variables that are alive before the catch and used afterwards.
- %% Don't lock variables that are only used inside the try.
- Tdb0 = vdb_sub(I, I+1, Vdb),
- %% This is the tricky bit. Lock variables in Arg that are used in
- %% the body and handler. Add try tag 'variable'.
- Ab = get_kanno(Kb),
- Ah = get_kanno(Kh),
- Tdb1 = use_vars(Ab#k.us, I+3, use_vars(Ah#k.us, I+3, Tdb0)),
- Tdb2 = vdb_sub(I, I+2, Tdb1),
- Vnames = fun (Kvar) -> Kvar#k_var.name end, %Get the variable names
- {Aes,_,Adb} = body(Ka, I+2, add_var({catch_tag,I+1}, I+1, locked, Tdb2)),
- {Bes,_,Bdb} = body(Kb, I+4, new_vars(map(Vnames, Vs), I+3, Tdb2)),
- {Hes,_,Hdb} = body(Kh, I+4, new_vars(map(Vnames, Evs), I+3, Tdb2)),
- #l{ke={'try',#l{ke={block,Aes},i=I+1,vdb=Adb,a=[]},
- var_list(Vs),#l{ke={block,Bes},i=I+3,vdb=Bdb,a=[]},
- var_list(Evs),#l{ke={block,Hes},i=I+3,vdb=Hdb,a=[]},
- var_list(Rs)},
- i=I,vdb=Tdb1,a=A#k.a};
+expr(#k_try{}=Try, I, Vdb) ->
+ case is_in_guard() of
+ false -> body_try(Try, I, Vdb);
+ true -> guard(Try, I, Vdb)
+ end;
expr(#k_try_enter{anno=A,arg=Ka,vars=Vs,body=Kb,evars=Evs,handler=Kh}, I, Vdb) ->
%% Lock variables that are alive before the catch and used afterwards.
%% Don't lock variables that are only used inside the try.
@@ -243,6 +190,27 @@ expr(#k_guard_break{anno=A,args=As}, I, Vdb) ->
expr(#k_return{anno=A,args=As}, I, _Vdb) ->
#l{ke={return,atomic_list(As)},i=I,a=A#k.a}.
+body_try(#k_try{anno=A,arg=Ka,vars=Vs,body=Kb,evars=Evs,handler=Kh,ret=Rs},
+ I, Vdb) ->
+ %% Lock variables that are alive before the catch and used afterwards.
+ %% Don't lock variables that are only used inside the try.
+ Tdb0 = vdb_sub(I, I+1, Vdb),
+ %% This is the tricky bit. Lock variables in Arg that are used in
+ %% the body and handler. Add try tag 'variable'.
+ Ab = get_kanno(Kb),
+ Ah = get_kanno(Kh),
+ Tdb1 = use_vars(Ab#k.us, I+3, use_vars(Ah#k.us, I+3, Tdb0)),
+ Tdb2 = vdb_sub(I, I+2, Tdb1),
+ Vnames = fun (Kvar) -> Kvar#k_var.name end, %Get the variable names
+ {Aes,_,Adb} = body(Ka, I+2, add_var({catch_tag,I+1}, I+1, locked, Tdb2)),
+ {Bes,_,Bdb} = body(Kb, I+4, new_vars(map(Vnames, Vs), I+3, Tdb2)),
+ {Hes,_,Hdb} = body(Kh, I+4, new_vars(map(Vnames, Evs), I+3, Tdb2)),
+ #l{ke={'try',#l{ke={block,Aes},i=I+1,vdb=Adb,a=[]},
+ var_list(Vs),#l{ke={block,Bes},i=I+3,vdb=Bdb,a=[]},
+ var_list(Evs),#l{ke={block,Hes},i=I+3,vdb=Hdb,a=[]},
+ var_list(Rs)},
+ i=I,vdb=Tdb1,a=A#k.a}.
+
%% call_op(Op) -> Op.
%% bif_op(Op) -> Op.
%% test_op(Op) -> Op.
@@ -373,7 +341,6 @@ atomic(#k_int{val=I}) -> {integer,I};
atomic(#k_float{val=F}) -> {float,F};
atomic(#k_atom{val=N}) -> {atom,N};
%%atomic(#k_char{val=C}) -> {char,C};
-%%atomic(#k_string{val=S}) -> {string,S};
atomic(#k_nil{}) -> nil.
atomic_list(Ks) -> [atomic(K) || K <- Ks].
@@ -535,3 +502,7 @@ vdb_sub(Min, Max, Vdb) ->
true -> Vd
end || {V,F,L}=Vd <- Vdb, F < Min, L >= Min ].
+%% is_in_guard() -> true|false.
+
+is_in_guard() ->
+ get(guard_refc) > 0.
diff --git a/lib/compiler/test/core_SUITE.erl b/lib/compiler/test/core_SUITE.erl
index 874e02803d..fd1539e7fd 100644
--- a/lib/compiler/test/core_SUITE.erl
+++ b/lib/compiler/test/core_SUITE.erl
@@ -22,7 +22,7 @@
init_per_group/2,end_per_group/2,
init_per_testcase/2,end_per_testcase/2,
dehydrated_itracer/1,nested_tries/1,
- make_effect_seq/1,eval_is_boolean/1,
+ seq_in_guard/1,make_effect_seq/1,eval_is_boolean/1,
unsafe_case/1,nomatch_shadow/1,reversed_annos/1]).
-include_lib("test_server/include/test_server.hrl").
@@ -43,7 +43,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
test_lib:recompile(?MODULE),
- [dehydrated_itracer,nested_tries,make_effect_seq,
+ [dehydrated_itracer,nested_tries,seq_in_guard,make_effect_seq,
eval_is_boolean,unsafe_case,nomatch_shadow,reversed_annos].
groups() ->
@@ -64,6 +64,7 @@ end_per_group(_GroupName, Config) ->
?comp(dehydrated_itracer).
?comp(nested_tries).
+?comp(seq_in_guard).
?comp(make_effect_seq).
?comp(eval_is_boolean).
?comp(unsafe_case).
diff --git a/lib/compiler/test/core_SUITE_data/seq_in_guard.core b/lib/compiler/test/core_SUITE_data/seq_in_guard.core
new file mode 100644
index 0000000000..44686a7187
--- /dev/null
+++ b/lib/compiler/test/core_SUITE_data/seq_in_guard.core
@@ -0,0 +1,66 @@
+module 'seq_in_guard' ['seq_in_guard'/0,
+ 't'/1]
+ attributes []
+'seq_in_guard'/0 =
+ %% Line 4
+ fun () ->
+ case <> of
+ <> when 'true' ->
+ let <_cor0> =
+ catch
+ %% Line 5
+ apply 't'/1
+ ({})
+ in %% Line 5
+ case _cor0 of
+ <{'EXIT',{'function_clause',_cor4}}> when 'true' ->
+ let <_cor2> =
+ catch
+ %% Line 6
+ apply 't'/1
+ ('atom')
+ in %% Line 6
+ case _cor2 of
+ <{'EXIT',{'function_clause',_cor5}}> when 'true' ->
+ %% Line 7
+ apply 't'/1
+ ({'a','b'})
+ ( <_cor3> when 'true' ->
+ primop 'match_fail'
+ ({'badmatch',_cor3})
+ -| ['compiler_generated'] )
+ end
+ ( <_cor1> when 'true' ->
+ primop 'match_fail'
+ ({'badmatch',_cor1})
+ -| ['compiler_generated'] )
+ end
+ ( <> when 'true' ->
+ ( primop 'match_fail'
+ ({'function_clause'})
+ -| [{'function_name',{'seq_in_guard',0}}] )
+ -| ['compiler_generated'] )
+ end
+'t'/1 =
+ %% Line 9
+ fun (_cor0) ->
+ case _cor0 of
+ <X>
+ when try
+ do
+ call 'erlang':'element'
+ (2, X)
+ 'true'
+ of <Try> ->
+ Try
+ catch <T,R> ->
+ 'false' ->
+ %% Line 10
+ 'ok'
+ ( <_cor3> when 'true' ->
+ ( primop 'match_fail'
+ ({'function_clause',_cor3})
+ -| [{'function_name',{'t',1}}] )
+ -| ['compiler_generated'] )
+ end
+end
diff --git a/lib/compiler/test/core_fold_SUITE.erl b/lib/compiler/test/core_fold_SUITE.erl
index fb5ec88c9f..54bd52947e 100644
--- a/lib/compiler/test/core_fold_SUITE.erl
+++ b/lib/compiler/test/core_fold_SUITE.erl
@@ -21,7 +21,7 @@
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2,
t_element/1,setelement/1,t_length/1,append/1,t_apply/1,bifs/1,
- eq/1,nested_call_in_case/1,coverage/1]).
+ eq/1,nested_call_in_case/1,guard_try_catch/1,coverage/1]).
-export([foo/0,foo/1,foo/2,foo/3]).
@@ -32,7 +32,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
test_lib:recompile(?MODULE),
[t_element, setelement, t_length, append, t_apply, bifs,
- eq, nested_call_in_case, coverage].
+ eq, nested_call_in_case, guard_try_catch, coverage].
groups() ->
[].
@@ -207,6 +207,23 @@ nested_call_in_case(Config) when is_list(Config) ->
?line {'EXIT',_} = (catch Mod:a(not_a_list, 42)),
ok.
+guard_try_catch(_Config) ->
+ false = do_guard_try_catch(key, value),
+ value = get(key),
+ ok.
+
+do_guard_try_catch(K, V) ->
+ %% This try...catch block looks like a guard.
+ %% Make sure that it is not optimized like a guard
+ %% (the put/2 call must not be optimized away).
+ try
+ put(K, V),
+ false
+ catch
+ _:_ ->
+ false
+ end.
+
coverage(Config) when is_list(Config) ->
?line {'EXIT',{{case_clause,{a,b,c}},_}} =
(catch cover_will_match_list_type({a,b,c})),
diff --git a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl
index 458f3a4c81..5be870d78f 100644
--- a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl
+++ b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl
@@ -170,33 +170,32 @@ analysis_start(Parent, Analysis) ->
rcv_and_send_ext_types(Parent),
NonExports = sets:subtract(sets:from_list(AllNodes), Exports),
NonExportsList = sets:to_list(NonExports),
- Plt3 = dialyzer_plt:delete_list(State3#analysis_state.plt, NonExportsList),
- Plt4 = dialyzer_plt:delete_contract_list(Plt3, NonExportsList),
+ Plt2 = dialyzer_plt:delete_list(State3#analysis_state.plt, NonExportsList),
send_codeserver_plt(Parent, CServer, State3#analysis_state.plt),
- send_analysis_done(Parent, Plt4, State3#analysis_state.doc_plt).
+ send_analysis_done(Parent, Plt2, State3#analysis_state.doc_plt).
analyze_callgraph(Callgraph, State) ->
Codeserver = State#analysis_state.codeserver,
- Plt = dialyzer_plt:insert_callbacks(State#analysis_state.plt, Codeserver),
Parent = State#analysis_state.parent,
- case State#analysis_state.analysis_type of
- plt_build ->
- Callgraph1 = dialyzer_callgraph:finalize(Callgraph),
- NewPlt = dialyzer_succ_typings:analyze_callgraph(Callgraph1, Plt,
- Codeserver, Parent),
- dialyzer_callgraph:delete(Callgraph1),
- State#analysis_state{plt = NewPlt};
- succ_typings ->
- NoWarn = State#analysis_state.no_warn_unused,
- DocPlt = State#analysis_state.doc_plt,
- Callgraph1 = dialyzer_callgraph:finalize(Callgraph),
- {Warnings, NewPlt, NewDocPlt} =
- dialyzer_succ_typings:get_warnings(Callgraph1, Plt, DocPlt,
- Codeserver, NoWarn, Parent),
- dialyzer_callgraph:delete(Callgraph1),
- send_warnings(State#analysis_state.parent, Warnings),
- State#analysis_state{plt = NewPlt, doc_plt = NewDocPlt}
- end.
+ DocPlt = State#analysis_state.doc_plt,
+ Plt = dialyzer_plt:insert_callbacks(State#analysis_state.plt, Codeserver),
+ Callgraph1 = dialyzer_callgraph:finalize(Callgraph),
+ {NewPlt, NewDocPlt} =
+ case State#analysis_state.analysis_type of
+ plt_build ->
+ {dialyzer_succ_typings:analyze_callgraph(Callgraph1, Plt,
+ Codeserver, Parent),
+ DocPlt};
+ succ_typings ->
+ NoWarn = State#analysis_state.no_warn_unused,
+ {Warnings, NewPlt0, NewDocPlt0} =
+ dialyzer_succ_typings:get_warnings(Callgraph1, Plt, DocPlt,
+ Codeserver, NoWarn, Parent),
+ send_warnings(State#analysis_state.parent, Warnings),
+ {NewPlt0, NewDocPlt0}
+ end,
+ dialyzer_callgraph:delete(Callgraph1),
+ State#analysis_state{plt = NewPlt, doc_plt = NewDocPlt}.
%%--------------------------------------------------------------------
%% Build the callgraph and fill the codeserver.
@@ -282,10 +281,10 @@ cleanup_callgraph(#analysis_state{plt = InitPlt, parent = Parent,
if ExtCalls1 =:= [] -> {[], []};
true ->
ModuleSet = sets:from_list(Modules),
- lists:partition(fun({_From, {M, _F, _A}}) ->
- sets:is_element(M, ModuleSet) orelse
- dialyzer_plt:contains_module(InitPlt, M)
- end, ExtCalls1)
+ PltModuleSet = dialyzer_plt:all_modules(InitPlt),
+ AllModules = sets:union(ModuleSet, PltModuleSet),
+ Pred = fun({_From, {M, _F, _A}}) -> sets:is_element(M, AllModules) end,
+ lists:partition(Pred, ExtCalls1)
end,
NonLocalCalls = dialyzer_callgraph:non_local_calls(Callgraph1),
BadCalls2 = [Call || Call = {_From, To} <- NonLocalCalls,
@@ -366,9 +365,11 @@ abs_get_nowarn(Abs, M) ->
false ->
[{M, F, A} || {function, _, F, A, _} <- Abs]; % all functions
true ->
- [{M, F, A} ||
- {nowarn_unused_function, FAs} <- Opts,
- {F, A} <- lists:flatten([FAs])]
+ OnLoad =
+ lists:flatten([{M, F, A} || {attribute, _, on_load, {F, A}} <- Abs]),
+ OnLoad ++ [{M, F, A} ||
+ {nowarn_unused_function, FAs} <- Opts,
+ {F, A} <- lists:flatten([FAs])]
end.
get_exported_types_from_core(Core) ->
diff --git a/lib/dialyzer/src/dialyzer_behaviours.erl b/lib/dialyzer/src/dialyzer_behaviours.erl
index e89c08df7d..fdaa8b663c 100644
--- a/lib/dialyzer/src/dialyzer_behaviours.erl
+++ b/lib/dialyzer/src/dialyzer_behaviours.erl
@@ -72,9 +72,11 @@ check_callbacks(Module, Attrs, Plt, Codeserver) ->
%%--------------------------------------------------------------------
get_behaviours(Attrs) ->
- BehaviourListsAndLine = [{cerl:concrete(L2), hd(cerl:get_ann(L2))} ||
- {L1, L2} <- Attrs, cerl:is_literal(L1),
- cerl:is_literal(L2), cerl:concrete(L1) =:= 'behaviour'],
+ BehaviourListsAndLine =
+ [{cerl:concrete(L2), hd(cerl:get_ann(L2))} ||
+ {L1, L2} <- Attrs, cerl:is_literal(L1),
+ cerl:is_literal(L2), cerl:concrete(L1) =:= 'behaviour' orelse
+ cerl:concrete(L1) =:= 'behavior'],
Behaviours = lists:append([Behs || {Behs,_} <- BehaviourListsAndLine]),
BehLines = [{B,L} || {L1,L} <- BehaviourListsAndLine, B <- L1],
{Behaviours, BehLines}.
@@ -239,12 +241,12 @@ translatable_behaviours(Tree) ->
get_behaviour_apis(Behaviours) ->
get_behaviour_apis(Behaviours, []).
--spec translate_behaviour_api_call(dialyzer_races:mfa_or_funlbl(),
+-spec translate_behaviour_api_call(dialyzer_callgraph:mfa_or_funlbl(),
[erl_types:erl_type()],
[dialyzer_races:core_vars()],
module(),
behaviour_api_dict()) ->
- {dialyzer_races:mfa_or_funlbl(),
+ {dialyzer_callgraph:mfa_or_funlbl(),
[erl_types:erl_type()],
[dialyzer_races:core_vars()]}
| 'plain_call'.
diff --git a/lib/dialyzer/src/dialyzer_callgraph.erl b/lib/dialyzer/src/dialyzer_callgraph.erl
index d3de5aaf45..4767c02f77 100644
--- a/lib/dialyzer/src/dialyzer_callgraph.erl
+++ b/lib/dialyzer/src/dialyzer_callgraph.erl
@@ -59,7 +59,7 @@
put_named_tables/2, put_public_tables/2, put_behaviour_api_calls/2,
get_behaviour_api_calls/1]).
--export_type([callgraph/0]).
+-export_type([callgraph/0, mfa_or_funlbl/0]).
-include("dialyzer.hrl").
@@ -177,14 +177,14 @@ is_escaping(Label, #callgraph{esc = Esc}) when is_integer(Label) ->
add_edges([], CG) ->
CG;
-add_edges(Edges, #callgraph{digraph = Callgraph} = CG) ->
- CG#callgraph{digraph = digraph_add_edges(Edges, Callgraph)}.
+add_edges(Edges, #callgraph{digraph = Digraph} = CG) ->
+ CG#callgraph{digraph = digraph_add_edges(Edges, Digraph)}.
-spec add_edges([callgraph_edge()], [mfa_or_funlbl()], callgraph()) -> callgraph().
add_edges(Edges, MFAs, #callgraph{digraph = DG} = CG) ->
- DG1 = digraph_confirm_vertices(MFAs, DG),
- add_edges(Edges, CG#callgraph{digraph = DG1}).
+ DG = digraph_confirm_vertices(MFAs, DG),
+ add_edges(Edges, CG).
-spec take_scc(callgraph()) -> 'none' | {'ok', scc(), callgraph()}.
@@ -196,8 +196,8 @@ take_scc(#callgraph{postorder = []}) ->
-spec remove_external(callgraph()) -> {callgraph(), [tuple()]}.
remove_external(#callgraph{digraph = DG} = CG) ->
- {NewDG, External} = digraph_remove_external(DG),
- {CG#callgraph{digraph = NewDG}, External}.
+ {DG, External} = digraph_remove_external(DG),
+ {CG, External}.
-spec non_local_calls(callgraph()) -> mfa_calls().
@@ -241,30 +241,30 @@ modules(#callgraph{digraph = DG}) ->
-spec module_postorder(callgraph()) -> [[module()]].
module_postorder(#callgraph{digraph = DG}) ->
+ {MDG, _Nodes} = get_module_digraph_and_nodes(DG),
+ MDG1 = digraph_utils:condensation(MDG),
+ PostOrder = digraph_utils:postorder(MDG1),
+ PostOrder1 = sort_sccs_internally(PostOrder, MDG),
+ digraph:delete(MDG1),
+ digraph_delete(MDG),
+ PostOrder1.
+
+get_module_digraph_and_nodes(DG) ->
Edges = digraph_edges(DG),
Nodes = ordsets:from_list([M || {M,_F,_A} <- digraph_vertices(DG)]),
MDG = digraph:new(),
- MDG1 = digraph_confirm_vertices(Nodes, MDG),
- MDG2 = create_module_digraph(Edges, MDG1),
- MDG3 = digraph_utils:condensation(MDG2),
- PostOrder = digraph_utils:postorder(MDG3),
- PostOrder1 = sort_sccs_internally(PostOrder, MDG2),
- digraph:delete(MDG2),
- digraph_delete(MDG3),
- PostOrder1.
+ MDG = digraph_confirm_vertices(Nodes, MDG),
+ MDG = create_module_digraph(Edges, MDG),
+ {MDG, Nodes}.
%% The module deps of a module are modules that depend on the module
-spec module_deps(callgraph()) -> dict().
module_deps(#callgraph{digraph = DG}) ->
- Edges = digraph_edges(DG),
- Nodes = ordsets:from_list([M || {M,_F,_A} <- digraph_vertices(DG)]),
- MDG = digraph:new(),
- MDG1 = digraph_confirm_vertices(Nodes, MDG),
- MDG2 = create_module_digraph(Edges, MDG1),
- Deps = [{N, ordsets:from_list(digraph:in_neighbours(MDG2, N))}
+ {MDG, Nodes} = get_module_digraph_and_nodes(DG),
+ Deps = [{N, ordsets:from_list(digraph:in_neighbours(MDG, N))}
|| N <- Nodes],
- digraph_delete(MDG2),
+ digraph_delete(MDG),
dict:from_list(Deps).
-spec strip_module_deps(dict(), set()) -> dict().
diff --git a/lib/dialyzer/src/dialyzer_codeserver.erl b/lib/dialyzer/src/dialyzer_codeserver.erl
index 13ca65e4dd..f1e87affbd 100644
--- a/lib/dialyzer/src/dialyzer_codeserver.erl
+++ b/lib/dialyzer/src/dialyzer_codeserver.erl
@@ -292,15 +292,11 @@ table__loop(Cached, Map) ->
{NewCached, Ans} =
case Cached of
{M, Tree} ->
- [Val] = [VarFun || {Var, _Fun} = VarFun <- cerl:module_defs(Tree),
- cerl:fname_id(Var) =:= F,
- cerl:fname_arity(Var) =:= A],
+ Val = find_fun(F, A, Tree),
{Cached, Val};
_ ->
Tree = fetch_and_expand(M, Map),
- [Val] = [VarFun || {Var, _Fun} = VarFun <- cerl:module_defs(Tree),
- cerl:fname_id(Var) =:= F,
- cerl:fname_arity(Var) =:= A],
+ Val = find_fun(F, A, Tree),
{{M, Tree}, Val}
end,
Pid ! {self(), MFA, Ans},
@@ -329,3 +325,12 @@ fetch_and_expand(Mod, Map) ->
Msg = "found no module named '" ++ S ++ "' in the analyzed files",
exit({error, Msg})
end.
+
+find_fun(F, A, Tree) ->
+ Pred =
+ fun({Var, _Fun}) ->
+ (cerl:fname_id(Var) =/= F) orelse
+ (cerl:fname_arity(Var) =/= A)
+ end,
+ [Val|_] = lists:dropwhile(Pred, cerl:module_defs(Tree)),
+ Val.
diff --git a/lib/dialyzer/src/dialyzer_dataflow.erl b/lib/dialyzer/src/dialyzer_dataflow.erl
index 6008dba080..aba13278ff 100644
--- a/lib/dialyzer/src/dialyzer_dataflow.erl
+++ b/lib/dialyzer/src/dialyzer_dataflow.erl
@@ -67,7 +67,6 @@
%%-define(DEBUG, true).
%%-define(DEBUG_PP, true).
-%%-define(DEBUG_TIME, true).
%%-define(DOT, true).
-ifdef(DEBUG).
@@ -77,9 +76,6 @@
-define(debug(S_, L_), ok).
-endif.
-%%-define(debug1(S_, L_), io:format(S_, L_)).
-%%-define(debug1(S_, L_), ok).
-
%%--------------------------------------------------------------------
-define(no_arg, no_arg).
@@ -276,6 +272,7 @@ analyze_module(Tree, Plt, Callgraph, Records, GetWarnings) ->
debug_pp(Tree, false),
Module = cerl:atom_val(cerl:module_name(Tree)),
RaceDetection = dialyzer_callgraph:get_race_detection(Callgraph),
+ RaceCode = dialyzer_callgraph:get_race_code(Callgraph),
BehaviourTranslations =
case RaceDetection of
true -> dialyzer_behaviours:translatable_behaviours(Tree);
@@ -286,9 +283,6 @@ analyze_module(Tree, Plt, Callgraph, Records, GetWarnings) ->
TopFun, Plt, Module, Records, BehaviourTranslations),
State1 = state__race_analysis(not GetWarnings, State),
State2 = analyze_loop(State1),
- RaceCode = dialyzer_callgraph:get_race_code(Callgraph),
- Callgraph1 = State2#state.callgraph,
- RaceCode1 = dialyzer_callgraph:get_race_code(Callgraph1),
case GetWarnings of
true ->
State3 = state__set_warning_mode(State2),
@@ -313,6 +307,8 @@ analyze_module(Tree, Plt, Callgraph, Records, GetWarnings) ->
St#state{callgraph = Callgraph3}
end;
false ->
+ Callgraph1 = State2#state.callgraph,
+ RaceCode1 = dialyzer_callgraph:get_race_code(Callgraph1),
state__restore_race_code(
dict:merge(fun (_K, V1, _V2) -> V1 end,
RaceCode, RaceCode1), State2)
@@ -320,27 +316,26 @@ analyze_module(Tree, Plt, Callgraph, Records, GetWarnings) ->
analyze_loop(#state{callgraph = Callgraph, races = Races} = State) ->
case state__get_work(State) of
- none -> state__clean_not_called(State);
- {Fun, NewState} ->
- ArgTypes = state__get_args(Fun, NewState),
- case any_none(ArgTypes) of
+ none -> State;
+ {Fun, NewState1} ->
+ {ArgTypes, IsCalled} = state__get_args_and_status(Fun, NewState1),
+ case not IsCalled of
true ->
- ?debug("Not handling1 ~w: ~s\n",
+ ?debug("Not handling (not called) ~w: ~s\n",
[state__lookup_name(get_label(Fun), State),
t_to_string(t_product(ArgTypes))]),
- analyze_loop(NewState);
+ analyze_loop(NewState1);
false ->
- case state__fun_env(Fun, NewState) of
+ case state__fun_env(Fun, NewState1) of
none ->
- ?debug("Not handling2 ~w: ~s\n",
+ ?debug("Not handling (no env) ~w: ~s\n",
[state__lookup_name(get_label(Fun), State),
t_to_string(t_product(ArgTypes))]),
- analyze_loop(NewState);
+ analyze_loop(NewState1);
Map ->
?debug("Handling fun ~p: ~s\n",
[state__lookup_name(get_label(Fun), State),
- t_to_string(state__fun_type(Fun, NewState))]),
- NewState1 = state__mark_fun_as_handled(NewState, Fun),
+ t_to_string(state__fun_type(Fun, NewState1))]),
Vars = cerl:fun_vars(Fun),
Map1 = enter_type_lists(Vars, ArgTypes, Map),
Body = cerl:fun_body(Fun),
@@ -2892,28 +2887,22 @@ state__new(Callgraph, Tree, Plt, Module, Records, BehaviourTranslations) ->
TreeMap = build_tree_map(Tree),
Funs = dict:fetch_keys(TreeMap),
FunTab = init_fun_tab(Funs, dict:new(), TreeMap, Callgraph, Plt, Opaques),
- Work = init_work([get_label(Tree)]),
- Env = dict:store(top, map__new(), dict:new()),
+ ExportedFuns =
+ [Fun || Fun <- Funs--[top], dialyzer_callgraph:is_escaping(Fun, Callgraph)],
+ Work = init_work(ExportedFuns),
+ Env = lists:foldl(fun(Fun, Env) -> dict:store(Fun, map__new(), Env) end,
+ dict:new(), Funs),
#state{callgraph = Callgraph, envs = Env, fun_tab = FunTab, opaques = Opaques,
plt = Plt, races = dialyzer_races:new(), records = Records,
warning_mode = false, warnings = [], work = Work, tree_map = TreeMap,
module = Module, behaviour_api_dict = BehaviourTranslations}.
-state__mark_fun_as_handled(#state{fun_tab = FunTab} = State, Fun0) ->
- Fun = get_label(Fun0),
- case dict:find(Fun, FunTab) of
- {ok, {not_handled, Entry}} ->
- State#state{fun_tab = dict:store(Fun, Entry, FunTab)};
- {ok, {_, _}} ->
- State
- end.
-
state__warning_mode(#state{warning_mode = WM}) ->
WM.
state__set_warning_mode(#state{tree_map = TreeMap, fun_tab = FunTab,
races = Races} = State) ->
- ?debug("Starting warning pass\n", []),
+ ?debug("==========\nStarting warning pass\n==========\n", []),
Funs = dict:fetch_keys(TreeMap),
State#state{work = init_work([top|Funs--[top]]),
fun_tab = FunTab, warning_mode = true,
@@ -2985,7 +2974,7 @@ state__get_warnings(#state{tree_map = TreeMap, fun_tab = FunTab,
{NotCalled, Ret} =
case dict:fetch(get_label(Fun), FunTab) of
{not_handled, {_Args0, Ret0}} -> {true, Ret0};
- {Args0, Ret0} -> {any_none(Args0), Ret0}
+ {_Args0, Ret0} -> {false, Ret0}
end,
case NotCalled of
true ->
@@ -3079,11 +3068,11 @@ state__lookup_record(Tag, Arity, #state{records = Records}) ->
error
end.
-state__get_args(Tree, #state{fun_tab = FunTab}) ->
+state__get_args_and_status(Tree, #state{fun_tab = FunTab}) ->
Fun = get_label(Tree),
case dict:find(Fun, FunTab) of
- {ok, {not_handled, {ArgTypes, _}}} -> ArgTypes;
- {ok, {ArgTypes, _}} -> ArgTypes
+ {ok, {not_handled, {ArgTypes, _}}} -> {ArgTypes, false};
+ {ok, {ArgTypes, _}} -> {ArgTypes, true}
end.
build_tree_map(Tree) ->
@@ -3099,7 +3088,7 @@ build_tree_map(Tree) ->
cerl_trees:fold(Fun, dict:new(), Tree).
init_fun_tab([top|Left], Dict, TreeMap, Callgraph, Plt, Opaques) ->
- NewDict = dict:store(top, {not_handled, {[], t_none()}}, Dict),
+ NewDict = dict:store(top, {[], t_none()}, Dict),
init_fun_tab(Left, NewDict, TreeMap, Callgraph, Plt, Opaques);
init_fun_tab([Fun|Left], Dict, TreeMap, Callgraph, Plt, Opaques) ->
Arity = cerl:fun_arity(dict:fetch(Fun, TreeMap)),
@@ -3115,9 +3104,9 @@ init_fun_tab([Fun|Left], Dict, TreeMap, Callgraph, Plt, Opaques) ->
false -> {Args, t_unit()}
end
end;
- false -> {lists:duplicate(Arity, t_none()), t_unit()}
+ false -> {not_handled, {lists:duplicate(Arity, t_none()), t_unit()}}
end,
- NewDict = dict:store(Fun, {not_handled, FunEntry}, Dict),
+ NewDict = dict:store(Fun, FunEntry, Dict),
init_fun_tab(Left, NewDict, TreeMap, Callgraph, Plt, Opaques);
init_fun_tab([], Dict, _TreeMap, _Callgraph, _Plt, _Opaques) ->
Dict.
@@ -3141,7 +3130,8 @@ state__clean_not_called(#state{fun_tab = FunTab} = State) ->
end, FunTab),
State#state{fun_tab = NewFunTab}.
-state__all_fun_types(#state{fun_tab = FunTab}) ->
+state__all_fun_types(State) ->
+ #state{fun_tab = FunTab} = state__clean_not_called(State),
Tab1 = dict:erase(top, FunTab),
dict:map(fun(_Fun, {Args, Ret}) -> t_fun(Args, Ret)end, Tab1).
diff --git a/lib/dialyzer/src/dialyzer_gui.erl b/lib/dialyzer/src/dialyzer_gui.erl
index f60194e01f..bac659548f 100644
--- a/lib/dialyzer/src/dialyzer_gui.erl
+++ b/lib/dialyzer/src/dialyzer_gui.erl
@@ -511,6 +511,16 @@ gui_loop(#gui_state{add_all = AddAll, add_file = AddFile, add_rec = AddRec,
[ExtCalls]),
free_editor(State, "Analysis done", Msg),
gui_loop(State);
+ {BackendPid, ext_types, ExtTypes} ->
+ Map = fun({M,F,A}) -> io_lib:format("~p:~p/~p",[M,F,A]) end,
+ ExtTypeString = string:join(lists:map(Map, ExtTypes), "\n"),
+ Msg = io_lib:format("The following remote types are being used "
+ "but information about them is not available.\n"
+ "The analysis might get more precise by including "
+ "the modules containing these types and making sure "
+ "that they are exported:\n~s\n", [ExtTypeString]),
+ free_editor(State, "Analysis done", Msg),
+ gui_loop(State);
{BackendPid, log, LogMsg} ->
update_editor(Log, LogMsg),
gui_loop(State);
diff --git a/lib/dialyzer/src/dialyzer_gui_wx.erl b/lib/dialyzer/src/dialyzer_gui_wx.erl
index e711c15ea7..9ff32bd8b1 100644
--- a/lib/dialyzer/src/dialyzer_gui_wx.erl
+++ b/lib/dialyzer/src/dialyzer_gui_wx.erl
@@ -503,6 +503,16 @@ gui_loop(#gui_state{backend_pid = BackendPid, doc_plt = DocPlt,
[ExtCalls]),
free_editor(State,"Analysis Done", Msg),
gui_loop(State);
+ {BackendPid, ext_types, ExtTypes} ->
+ Map = fun({M,F,A}) -> io_lib:format("~p:~p/~p",[M,F,A]) end,
+ ExtTypeString = string:join(lists:map(Map, ExtTypes), "\n"),
+ Msg = io_lib:format("The following remote types are being used "
+ "but information about them is not available.\n"
+ "The analysis might get more precise by including "
+ "the modules containing these types and making sure "
+ "that they are exported:\n~s\n", [ExtTypeString]),
+ free_editor(State, "Analysis done", Msg),
+ gui_loop(State);
{BackendPid, log, LogMsg} ->
update_editor(Log, LogMsg),
gui_loop(State);
diff --git a/lib/dialyzer/src/dialyzer_plt.erl b/lib/dialyzer/src/dialyzer_plt.erl
index 206c43e4e2..74892d1668 100644
--- a/lib/dialyzer/src/dialyzer_plt.erl
+++ b/lib/dialyzer/src/dialyzer_plt.erl
@@ -31,8 +31,7 @@
-export([check_plt/3,
compute_md5_from_files/1,
contains_mfa/2,
- contains_module/2,
- delete_contract_list/2,
+ all_modules/1,
delete_list/2,
delete_module/2,
included_files/1,
@@ -153,10 +152,8 @@ lookup_contract(#plt{contracts = Contracts},
{M, F, _} = MFA) when is_atom(M), is_atom(F) ->
table_lookup(Contracts, MFA).
--spec lookup_callbacks(plt(), module()) -> [{mfa(),
- {{Filename::string(),
- Line::pos_integer()},
- #contract{}}}].
+-spec lookup_callbacks(plt(), module()) ->
+ [{mfa(), {{Filename::string(), Line::pos_integer()}, #contract{}}}].
lookup_callbacks(#plt{callbacks = Callbacks}, Mod) when is_atom(Mod) ->
FunModFilter =
@@ -166,18 +163,6 @@ lookup_callbacks(#plt{callbacks = Callbacks}, Mod) when is_atom(Mod) ->
ModCallbacks = dict:filter(FunModFilter, Callbacks),
dict:to_list(ModCallbacks).
--spec delete_contract_list(plt(), [mfa()]) -> plt().
-
-delete_contract_list(#plt{contracts = Contracts,
- callbacks = Callbacks} = PLT, List) ->
- PLT#plt{contracts = table_delete_list(Contracts, List),
- callbacks = table_delete_list(Callbacks, List)}.
-
-%% -spec insert(plt(), mfa() | integer(), {_, _}) -> plt().
-%%
-%% insert(#plt{info = Info} = PLT, Id, Types) ->
-%% PLT#plt{info = table_insert(Info, Id, Types)}.
-
-type ret_args_types() :: {erl_types:erl_type(), [erl_types:erl_type()]}.
-spec insert_list(plt(), [{mfa() | integer(), ret_args_types()}]) -> plt().
@@ -220,10 +205,10 @@ get_exported_types(#plt{exported_types = ExpTypes}) ->
lookup_module(#plt{info = Info}, M) when is_atom(M) ->
table_lookup_module(Info, M).
--spec contains_module(plt(), atom()) -> boolean().
+-spec all_modules(plt()) -> set().
-contains_module(#plt{info = Info, contracts = Cs}, M) when is_atom(M) ->
- table_contains_module(Info, M) orelse table_contains_module(Cs, M).
+all_modules(#plt{info = Info, contracts = Cs}) ->
+ sets:union(table_all_modules(Info), table_all_modules(Cs)).
-spec contains_mfa(plt(), mfa()) -> boolean().
@@ -623,10 +608,12 @@ table_lookup_module(Plt, Mod) ->
false -> {value, List}
end.
-table_contains_module(Plt, Mod) ->
- dict:fold(fun({M, _F, _A}, _Val, _Acc) when M =:= Mod -> true;
- (_, _, Acc) -> Acc
- end, false, Plt).
+table_all_modules(Plt) ->
+ Fold =
+ fun({M, _F, _A}, _Val, Acc) -> sets:add_element(M, Acc);
+ (_, _, Acc) -> Acc
+ end,
+ dict:fold(Fold, sets:new(), Plt).
table_merge([H|T]) ->
table_merge(T, H).
diff --git a/lib/dialyzer/src/dialyzer_races.erl b/lib/dialyzer/src/dialyzer_races.erl
index ee9d5e88a3..b9594f5301 100644
--- a/lib/dialyzer/src/dialyzer_races.erl
+++ b/lib/dialyzer/src/dialyzer_races.erl
@@ -39,7 +39,7 @@
let_tag_new/2, new/0, put_curr_fun/3, put_fun_args/2,
put_race_analysis/2, put_race_list/3]).
--export_type([races/0, mfa_or_funlbl/0, core_vars/0]).
+-export_type([races/0, core_vars/0]).
-include("dialyzer.hrl").
@@ -66,8 +66,6 @@
%%%
%%% ===========================================================================
--type mfa_or_funlbl() :: label() | mfa().
-
-type label_type() :: label() | [label()] | {label()} | ?no_label.
-type args() :: [label_type() | [string()]].
-type core_vars() :: cerl:cerl() | ?no_arg | ?bypassed.
@@ -94,7 +92,7 @@
guard :: cerl:cerl()}).
-record(end_case, {clauses :: [#end_clause{}]}).
-record(curr_fun, {status :: 'in' | 'out',
- mfa :: mfa_or_funlbl(),
+ mfa :: dialyzer_callgraph:mfa_or_funlbl(),
label :: label(),
def_vars :: [core_vars()],
arg_types :: [erl_types:erl_type()],
@@ -107,8 +105,8 @@
state :: _, %% XXX: recursive
file_line :: file_line(),
var_map :: dict()}).
--record(fun_call, {caller :: mfa_or_funlbl(),
- callee :: mfa_or_funlbl(),
+-record(fun_call, {caller :: dialyzer_callgraph:mfa_or_funlbl(),
+ callee :: dialyzer_callgraph:mfa_or_funlbl(),
arg_types :: [erl_types:erl_type()],
vars :: [core_vars()]}).
-record(let_tag, {var :: var_to_map1(),
@@ -130,10 +128,10 @@
vars :: [core_vars()],
file_line :: file_line(),
index :: non_neg_integer(),
- fun_mfa :: mfa_or_funlbl(),
+ fun_mfa :: dialyzer_callgraph:mfa_or_funlbl(),
fun_label :: label()}).
--record(races, {curr_fun :: mfa_or_funlbl(),
+-record(races, {curr_fun :: dialyzer_callgraph:mfa_or_funlbl(),
curr_fun_label :: label(),
curr_fun_args = 'empty' :: core_args(),
new_table = 'no_t' :: table(),
@@ -158,7 +156,8 @@
%%%
%%% ===========================================================================
--spec store_race_call(mfa_or_funlbl(), [erl_types:erl_type()], [core_vars()],
+-spec store_race_call(dialyzer_callgraph:mfa_or_funlbl(),
+ [erl_types:erl_type()], [core_vars()],
file_line(), dialyzer_dataflow:state()) ->
dialyzer_dataflow:state().
@@ -2405,7 +2404,7 @@ end_case_new(Clauses) ->
end_clause_new(Arg, Pats, Guard) ->
#end_clause{arg = Arg, pats = Pats, guard = Guard}.
--spec get_curr_fun(races()) -> mfa_or_funlbl().
+-spec get_curr_fun(races()) -> dialyzer_callgraph:mfa_or_funlbl().
get_curr_fun(#races{curr_fun = CurrFun}) ->
CurrFun.
@@ -2444,7 +2443,7 @@ let_tag_new(Var, Arg) ->
new() -> #races{}.
--spec put_curr_fun(mfa_or_funlbl(), label(), races()) ->
+-spec put_curr_fun(dialyzer_callgraph:mfa_or_funlbl(), label(), races()) ->
races().
put_curr_fun(CurrFun, CurrFunLabel, Races) ->
diff --git a/lib/dialyzer/src/dialyzer_succ_typings.erl b/lib/dialyzer/src/dialyzer_succ_typings.erl
index 4d86bb34a7..de4c8a32a3 100644
--- a/lib/dialyzer/src/dialyzer_succ_typings.erl
+++ b/lib/dialyzer/src/dialyzer_succ_typings.erl
@@ -204,7 +204,7 @@ refine_one_module(M, State) ->
#st{callgraph = Callgraph, codeserver = CodeServer, plt = PLT} = State,
ModCode = dialyzer_codeserver:lookup_mod_code(M, CodeServer),
AllFuns = collect_fun_info([ModCode]),
- FunTypes = get_fun_types_from_plt(AllFuns, State),
+ FunTypes = get_fun_types_from_plt(AllFuns, Callgraph, PLT),
Records = dialyzer_codeserver:lookup_mod_records(M, CodeServer),
{NewFunTypes, RaceCode, PublicTables, NamedTables} =
dialyzer_dataflow:get_fun_types(ModCode, PLT, Callgraph, Records),
@@ -321,7 +321,9 @@ find_succ_typings(#st{callgraph = Callgraph, parent = Parent} = State,
end
end.
-analyze_scc(SCC, #st{codeserver = Codeserver} = State) ->
+analyze_scc(SCC, #st{codeserver = Codeserver,
+ callgraph = Callgraph,
+ plt = Plt} = State) ->
SCC_Info = [{MFA,
dialyzer_codeserver:lookup_mfa_code(MFA, Codeserver),
dialyzer_codeserver:lookup_mod_records(M, Codeserver)}
@@ -330,23 +332,19 @@ analyze_scc(SCC, #st{codeserver = Codeserver} = State) ->
|| {_, _, _} = MFA <- SCC],
Contracts2 = [{MFA, Contract} || {MFA, {ok, Contract}} <- Contracts1],
Contracts3 = orddict:from_list(Contracts2),
+ NextLabel = dialyzer_codeserver:get_next_core_label(Codeserver),
{SuccTypes, PltContracts, NotFixpoint} =
- find_succ_types_for_scc(SCC_Info, Contracts3, State),
+ find_succ_types_for_scc(SCC_Info, Contracts3, NextLabel, Callgraph, Plt),
State1 = insert_into_plt(SuccTypes, State),
ContrPlt = dialyzer_plt:insert_contract_list(State1#st.plt, PltContracts),
{State1#st{plt = ContrPlt}, NotFixpoint}.
-find_succ_types_for_scc(SCC_Info, Contracts,
- #st{codeserver = Codeserver,
- callgraph = Callgraph, plt = Plt} = State) ->
+find_succ_types_for_scc(SCC_Info, Contracts, NextLabel, Callgraph, Plt) ->
%% Assume that the PLT contains the current propagated types
AllFuns = collect_fun_info([Fun || {_MFA, {_Var, Fun}, _Rec} <- SCC_Info]),
- PropTypes = get_fun_types_from_plt(AllFuns, State),
- MFAs = [MFA || {MFA, {_Var, _Fun}, _Rec} <- SCC_Info],
- NextLabel = dialyzer_codeserver:get_next_core_label(Codeserver),
- Plt1 = dialyzer_plt:delete_contract_list(Plt, MFAs),
+ PropTypes = get_fun_types_from_plt(AllFuns, Callgraph, Plt),
FunTypes = dialyzer_typesig:analyze_scc(SCC_Info, NextLabel,
- Callgraph, Plt1, PropTypes),
+ Callgraph, Plt, PropTypes),
AllFunSet = sets:from_list([X || {X, _} <- AllFuns]),
FilteredFunTypes = dict:filter(fun(X, _) ->
sets:is_element(X, AllFunSet)
@@ -372,13 +370,13 @@ find_succ_types_for_scc(SCC_Info, Contracts,
ordsets:from_list([Fun || {Fun, _Arity} <- AllFuns])}
end.
-get_fun_types_from_plt(FunList, State) ->
- get_fun_types_from_plt(FunList, State, dict:new()).
+get_fun_types_from_plt(FunList, Callgraph, Plt) ->
+ get_fun_types_from_plt(FunList, Callgraph, Plt, dict:new()).
-get_fun_types_from_plt([{FunLabel, Arity}|Left], State, Map) ->
- Type = lookup_fun_type(FunLabel, Arity, State),
- get_fun_types_from_plt(Left, State, dict:store(FunLabel, Type, Map));
-get_fun_types_from_plt([], _State, Map) ->
+get_fun_types_from_plt([{FunLabel, Arity}|Left], Callgraph, Plt, Map) ->
+ Type = lookup_fun_type(FunLabel, Arity, Callgraph, Plt),
+ get_fun_types_from_plt(Left, Callgraph, Plt, dict:store(FunLabel, Type, Map));
+get_fun_types_from_plt([], _Callgraph, _Plt, Map) ->
Map.
collect_fun_info(Trees) ->
@@ -396,7 +394,7 @@ collect_fun_info([Tree|Trees], List) ->
collect_fun_info([], List) ->
List.
-lookup_fun_type(Label, Arity, #st{callgraph = Callgraph, plt = Plt}) ->
+lookup_fun_type(Label, Arity, Callgraph, Plt) ->
ID = lookup_name(Label, Callgraph),
case dialyzer_plt:lookup(Plt, ID) of
none -> erl_types:t_fun(Arity, erl_types:t_any());
diff --git a/lib/dialyzer/src/dialyzer_typesig.erl b/lib/dialyzer/src/dialyzer_typesig.erl
index 4268814859..04ff8e4941 100644
--- a/lib/dialyzer/src/dialyzer_typesig.erl
+++ b/lib/dialyzer/src/dialyzer_typesig.erl
@@ -91,22 +91,24 @@
-type typesig_scc() :: [{mfa(), {cerl:c_var(), cerl:c_fun()}, dict()}].
-type typesig_funmap() :: [{type_var(), type_var()}]. %% Orddict
--record(state, {callgraph :: dialyzer_callgraph:callgraph(),
- cs = [] :: [constr()],
- cmap = dict:new() :: dict(),
- fun_map = [] :: typesig_funmap(),
- fun_arities = dict:new() :: dict(),
- in_match = false :: boolean(),
- in_guard = false :: boolean(),
- module :: module(),
- name_map = dict:new() :: dict(),
- next_label :: label(),
- non_self_recs = [] :: [label()],
- plt :: dialyzer_plt:plt(),
- prop_types = dict:new() :: dict(),
- records = dict:new() :: dict(),
- opaques = [] :: [erl_types:erl_type()],
- scc = [] :: [type_var()]}).
+-record(state, {callgraph :: dialyzer_callgraph:callgraph(),
+ cs = [] :: [constr()],
+ cmap = dict:new() :: dict(),
+ fun_map = [] :: typesig_funmap(),
+ fun_arities = dict:new() :: dict(),
+ in_match = false :: boolean(),
+ in_guard = false :: boolean(),
+ module :: module(),
+ name_map = dict:new() :: dict(),
+ next_label :: label(),
+ self_recs :: [label()],
+ plt :: dialyzer_plt:plt(),
+ prop_types = dict:new() :: dict(),
+ records = dict:new() :: dict(),
+ opaques = [] :: [erl_types:erl_type()],
+ scc = [] :: [type_var()],
+ mfas = [] :: [dialyzer_callgraph:mfa_or_funlbl()]
+ }).
%%-----------------------------------------------------------------------------
@@ -448,7 +450,8 @@ traverse(Tree, DefinedVars, State) ->
%% Check if a record is constructed.
_ ->
Arity = length(Fields),
- case state__lookup_record(State2, cerl:atom_val(Tag), Arity) of
+ Records = State2#state.records,
+ case lookup_record(Records, cerl:atom_val(Tag), Arity) of
error -> {State2, TupleType};
{ok, RecType} ->
State3 = state__store_conj(TupleType, sub, RecType, State2),
@@ -646,8 +649,14 @@ get_plt_constr(MFA, Dst, ArgVars, State) ->
PltRes = dialyzer_plt:lookup(Plt, MFA),
Opaques = State#state.opaques,
Module = State#state.module,
+ SCCMFAs = State#state.mfas,
{FunModule, _, _} = MFA,
- case dialyzer_plt:lookup_contract(Plt, MFA) of
+ Contract =
+ case lists:member(MFA, SCCMFAs) of
+ true -> none;
+ false -> dialyzer_plt:lookup_contract(Plt, MFA)
+ end,
+ case Contract of
none ->
case PltRes of
none -> State;
@@ -1246,6 +1255,8 @@ get_bif_constr({erlang, is_record, 2}, Dst, [Var, Tag] = Args, _State) ->
mk_constraint(Var, sub, ArgV)]);
get_bif_constr({erlang, is_record, 3}, Dst, [Var, Tag, Arity] = Args, State) ->
%% TODO: Revise this to make it precise for Tag and Arity.
+ Records = State#state.records,
+ AllOpaques = State#state.opaques,
ArgFun =
fun(Map) ->
case t_is_atom(true, lookup_type(Dst, Map)) of
@@ -1262,10 +1273,8 @@ get_bif_constr({erlang, is_record, 3}, Dst, [Var, Tag, Arity] = Args, State) ->
GenRecord = t_tuple([TagType|AnyElems]),
case t_atom_vals(TagType) of
[TagVal] ->
- case state__lookup_record(State, TagVal,
- ArityVal - 1) of
+ case lookup_record(Records, TagVal, ArityVal - 1) of
{ok, Type} ->
- AllOpaques = State#state.opaques,
case t_opaque_match_record(Type, AllOpaques) of
[Opaque] -> Opaque;
_ -> Type
@@ -1287,7 +1296,7 @@ get_bif_constr({erlang, is_record, 3}, Dst, [Var, Tag, Arity] = Args, State) ->
DstFun = fun(Map) ->
[TmpVar, TmpTag, TmpArity] = TmpArgTypes = lookup_type_list(Args, Map),
TmpArgTypes2 =
- case lists:member(TmpVar, State#state.opaques) of
+ case lists:member(TmpVar, AllOpaques) of
true ->
case t_is_integer(TmpArity) of
true ->
@@ -1297,7 +1306,8 @@ get_bif_constr({erlang, is_record, 3}, Dst, [Var, Tag, Arity] = Args, State) ->
true ->
case t_atom_vals(TmpTag) of
[TmpTagVal] ->
- case state__lookup_record(State, TmpTagVal, TmpArityVal - 1) of
+ case lookup_record(Records, TmpTagVal,
+ TmpArityVal - 1) of
{ok, TmpType} ->
case t_is_none(t_inf(TmpType, TmpVar, opaque)) of
true -> TmpArgTypes;
@@ -1526,9 +1536,10 @@ get_bif_constr({erlang, element, 2} = _BIF, Dst, Args,
case t_is_none(GenType) of
true -> ?debug("Bif: ~w failed\n", [_BIF]), throw(error);
false ->
+ Opaques = State#state.opaques,
Fun = fun(Map) ->
[I, T] = ATs = lookup_type_list(Args, Map),
- ATs2 = case lists:member(T, State#state.opaques) of
+ ATs2 = case lists:member(T, Opaques) of
true -> [I, erl_types:t_opaque_structure(T)];
false -> ATs
end,
@@ -1546,7 +1557,7 @@ get_bif_constr({erlang, element, 2} = _BIF, Dst, Args,
end;
get_bif_constr({M, F, A} = _BIF, Dst, Args, State) ->
GenType = erl_bif_types:type(M, F, A),
- Opaques = State#state.opaques,
+ Opaques = State#state.opaques,
case t_is_none(GenType) of
true -> ?debug("Bif: ~w failed\n", [_BIF]), throw(error);
false ->
@@ -1612,11 +1623,12 @@ get_bif_test_constr(Dst, Arg, Type, State) ->
end
end,
ArgV = ?mk_fun_var(ArgFun, [Dst]),
+ Opaques = State#state.opaques,
DstFun = fun(Map) ->
ArgType = lookup_type(Arg, Map),
case t_is_none(t_inf(ArgType, Type)) of
true ->
- case lists:member(ArgType, State#state.opaques) of
+ case lists:member(ArgType, Opaques) of
true ->
OpaqueStruct = erl_types:t_opaque_structure(ArgType),
case t_is_none(t_inf(OpaqueStruct, Type)) of
@@ -1743,33 +1755,29 @@ solve_ref_or_list(#constraint_ref{id = Id, deps = Deps},
true -> solve_self_recursive(Cs, Map, MapDict, Id, t_none(), State);
false -> solve_ref_or_list(Cs, Map, MapDict, State)
end,
- case Res of
- {error, NewMapDict} ->
- ?debug("Error solving for function ~p\n", [debug_lookup_name(Id)]),
- Arity = state__fun_arity(Id, State),
- FunType =
- case state__prop_domain(t_var_name(Id), State) of
- error -> t_fun(Arity, t_none());
- {ok, Dom} -> t_fun(Dom, t_none())
- end,
- NewMap1 = enter_type(Id, FunType, Map),
- NewMap2 =
- case state__get_rec_var(Id, State) of
- {ok, Var} -> enter_type(Var, FunType, NewMap1);
- error -> NewMap1
- end,
- {ok, dict:store(Id, NewMap2, NewMapDict), NewMap2};
- {ok, NewMapDict, NewMap} ->
- ?debug("Done solving fun: ~p\n", [debug_lookup_name(Id)]),
- FunType = lookup_type(Id, NewMap),
- NewMap1 = enter_type(Id, FunType, Map),
- NewMap2 =
- case state__get_rec_var(Id, State) of
- {ok, Var} -> enter_type(Var, FunType, NewMap1);
- error -> NewMap1
- end,
- {ok, dict:store(Id, NewMap2, NewMapDict), NewMap2}
- end
+ {NewMapDict, FunType} =
+ case Res of
+ {error, NewMapDict0} ->
+ ?debug("Error solving for function ~p\n", [debug_lookup_name(Id)]),
+ Arity = state__fun_arity(Id, State),
+ FunType0 =
+ case state__prop_domain(t_var_name(Id), State) of
+ error -> t_fun(Arity, t_none());
+ {ok, Dom} -> t_fun(Dom, t_none())
+ end,
+ {NewMapDict0, FunType0};
+ {ok, NewMapDict0, NewMap} ->
+ ?debug("Done solving fun: ~p\n", [debug_lookup_name(Id)]),
+ FunType0 = lookup_type(Id, NewMap),
+ {NewMapDict0, FunType0}
+ end,
+ NewMap1 = enter_type(Id, FunType, Map),
+ NewMap2 =
+ case state__get_rec_var(Id, State) of
+ {ok, Var} -> enter_type(Var, FunType, NewMap1);
+ error -> NewMap1
+ end,
+ {ok, dict:store(Id, NewMap2, NewMapDict), NewMap2}
end;
solve_ref_or_list(#constraint_list{type=Type, list = Cs, deps = Deps, id = Id},
Map, MapDict, State) ->
@@ -2078,10 +2086,15 @@ mk_var_no_lit_list(List) ->
%% ============================================================================
new_state(SCC0, NextLabel, CallGraph, Plt, PropTypes) ->
- NameMap = dict:from_list([{MFA, Var} || {MFA, {Var, _Fun}, _Rec} <- SCC0]),
+ List = [{MFA, Var} || {MFA, {Var, _Fun}, _Rec} <- SCC0],
+ NameMap = dict:from_list(List),
+ MFAs = [MFA || {MFA, _Var} <- List],
SCC = [mk_var(Fun) || {_MFA, {_Var, Fun}, _Rec} <- SCC0],
+ SelfRecs = [F || F <- SCC,
+ dialyzer_callgraph:is_self_rec(t_var_name(F), CallGraph)],
#state{callgraph = CallGraph, name_map = NameMap, next_label = NextLabel,
- prop_types = PropTypes, plt = Plt, scc = ordsets:from_list(SCC)}.
+ prop_types = PropTypes, plt = Plt, scc = ordsets:from_list(SCC),
+ mfas = MFAs, self_recs = ordsets:from_list(SelfRecs)}.
state__set_rec_dict(State, RecDict) ->
State#state{records = RecDict}.
@@ -2091,15 +2104,6 @@ state__set_opaques(#state{records = RecDict} = State, {M, _F, _A}) ->
erl_types:module_builtin_opaques(M) ++ t_opaque_from_records(RecDict),
State#state{opaques = Opaques, module = M}.
-state__lookup_record(#state{records = Records}, Tag, Arity) ->
- case erl_types:lookup_record(Tag, Arity, Records) of
- {ok, Fields} ->
- {ok, t_tuple([t_from_term(Tag)|
- [FieldType || {_FieldName, FieldType} <- Fields]])};
- error ->
- error
- end.
-
state__set_in_match(State, Bool) ->
State#state{in_match = Bool}.
@@ -2268,14 +2272,12 @@ state__get_cs(Var, #state{cmap = Dict}) ->
%% The functions here will not be treated as self recursive.
%% These functions will need to be handled as such manually.
-state__mark_as_non_self_rec(SCC, #state{non_self_recs = NS} = State) ->
- State#state{non_self_recs = ordsets:union(NS, ordsets:from_list(SCC))}.
+state__mark_as_non_self_rec(SCC, #state{self_recs = SelfRecs} = State) ->
+ %% TODO: Check if the result is always empty and just set it to [] if so.
+ State#state{self_recs = ordsets:subtract(SelfRecs, ordsets:from_list(SCC))}.
-state__is_self_rec(Fun, #state{callgraph = CallGraph, non_self_recs = NS}) ->
- case ordsets:is_element(Fun, NS) of
- true -> false;
- false -> dialyzer_callgraph:is_self_rec(t_var_name(Fun), CallGraph)
- end.
+state__is_self_rec(Fun, #state{self_recs = SelfRecs}) ->
+ ordsets:is_element(Fun, SelfRecs).
state__store_funs(Vars0, Funs0, #state{fun_map = Map} = State) ->
debug_make_name_map(Vars0, Funs0),
@@ -2689,6 +2691,15 @@ find_constraint(Tuple, [#constraint_list{list = List}|Cs]) ->
find_constraint(Tuple, [_|Cs]) ->
find_constraint(Tuple, Cs).
+lookup_record(Records, Tag, Arity) ->
+ case erl_types:lookup_record(Tag, Arity, Records) of
+ {ok, Fields} ->
+ {ok, t_tuple([t_from_term(Tag)|
+ [FieldType || {_FieldName, FieldType} <- Fields]])};
+ error ->
+ error
+ end.
+
%% ============================================================================
%%
%% Pretty printer and debug facilities.
diff --git a/lib/dialyzer/test/Makefile b/lib/dialyzer/test/Makefile
index 47deb17f1d..6a1abce943 100644
--- a/lib/dialyzer/test/Makefile
+++ b/lib/dialyzer/test/Makefile
@@ -30,5 +30,5 @@ release_tests_spec:
$(INSTALL_DATA) $(AUXILIARY_FILES) $(RELSYSDIR)
@tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -)
cd $(RELSYSDIR);\
- erl -make;\
+ erlc dialyzer_common.erl file_utils.erl;\
erl -noshell -run dialyzer_common create_all_suites -s erlang halt
diff --git a/lib/dialyzer/test/behaviour_SUITE_data/results/sample_behaviour b/lib/dialyzer/test/behaviour_SUITE_data/results/sample_behaviour
index a38e662ccf..8cecabccaa 100644
--- a/lib/dialyzer/test/behaviour_SUITE_data/results/sample_behaviour
+++ b/lib/dialyzer/test/behaviour_SUITE_data/results/sample_behaviour
@@ -1,9 +1,9 @@
-sample_callback_wrong.erl:15: The inferred return type of sample_callback_2/0 (42) has nothing in common with atom(), which is the expected return type for the callback of sample_behaviour behaviour
-sample_callback_wrong.erl:16: The inferred return type of sample_callback_3/0 ('fair') has nothing in common with 'fail' | {'ok',1..255}, which is the expected return type for the callback of sample_behaviour behaviour
-sample_callback_wrong.erl:17: The inferred return type of sample_callback_4/1 ('fail') has nothing in common with 'ok', which is the expected return type for the callback of sample_behaviour behaviour
-sample_callback_wrong.erl:19: The inferred return type of sample_callback_5/1 (string()) has nothing in common with 'fail' | 'ok', which is the expected return type for the callback of sample_behaviour behaviour
-sample_callback_wrong.erl:19: The inferred type for the 1st argument of sample_callback_5/1 (atom()) is not a supertype of 1..255, which is expected type for this argument in the callback of the sample_behaviour behaviour
-sample_callback_wrong.erl:21: The inferred return type of sample_callback_6/3 ({'okk',number()}) has nothing in common with 'fail' | {'ok',1..255}, which is the expected return type for the callback of sample_behaviour behaviour
-sample_callback_wrong.erl:21: The inferred type for the 3rd argument of sample_callback_6/3 (atom()) is not a supertype of string(), which is expected type for this argument in the callback of the sample_behaviour behaviour
-sample_callback_wrong.erl:3: Undefined callback function sample_callback_1/0 (behaviour 'sample_behaviour')
+sample_callback_wrong.erl:16: The inferred return type of sample_callback_2/0 (42) has nothing in common with atom(), which is the expected return type for the callback of sample_behaviour behaviour
+sample_callback_wrong.erl:17: The inferred return type of sample_callback_3/0 ('fair') has nothing in common with 'fail' | {'ok',1..255}, which is the expected return type for the callback of sample_behaviour behaviour
+sample_callback_wrong.erl:18: The inferred return type of sample_callback_4/1 ('fail') has nothing in common with 'ok', which is the expected return type for the callback of sample_behaviour behaviour
+sample_callback_wrong.erl:20: The inferred return type of sample_callback_5/1 (string()) has nothing in common with 'fail' | 'ok', which is the expected return type for the callback of sample_behaviour behaviour
+sample_callback_wrong.erl:20: The inferred type for the 1st argument of sample_callback_5/1 (atom()) is not a supertype of 1..255, which is expected type for this argument in the callback of the sample_behaviour behaviour
+sample_callback_wrong.erl:22: The inferred return type of sample_callback_6/3 ({'okk',number()}) has nothing in common with 'fail' | {'ok',1..255}, which is the expected return type for the callback of sample_behaviour behaviour
+sample_callback_wrong.erl:22: The inferred type for the 3rd argument of sample_callback_6/3 (atom()) is not a supertype of string(), which is expected type for this argument in the callback of the sample_behaviour behaviour
+sample_callback_wrong.erl:4: Undefined callback function sample_callback_1/0 (behaviour 'sample_behaviour')
diff --git a/lib/dialyzer/test/behaviour_SUITE_data/src/custom_sup.erl b/lib/dialyzer/test/behaviour_SUITE_data/src/custom_sup.erl
new file mode 100644
index 0000000000..8ec84d798f
--- /dev/null
+++ b/lib/dialyzer/test/behaviour_SUITE_data/src/custom_sup.erl
@@ -0,0 +1,37 @@
+%%% Dialyzer was giving a warning with this input because of a bug in the
+%%% substitution of remote types in specs. Remote types in the first element of
+%%% a tuple would not update the tuple's tag set and we could end up with a
+%%% non-normalized representation.
+%%%
+%%% Reported by Damian DobroczyƄski on 29/02/2012
+
+-module(custom_sup).
+
+-behavior(supervisor).
+
+-export([init/1]).
+
+-spec init(atom()) ->
+ {ok, {{supervisor:strategy(), non_neg_integer(), non_neg_integer()},
+ [supervisor:child_spec()]}} | ignore.
+
+init(StorageName) ->
+ Strategy = {one_for_all, 100, 1},
+ %% get application-wide storage parameters
+ case application:get_env(storage) of
+ undefined ->
+ ignore;
+ {ok, Storage} ->
+ BackendId = proplists:get_value(backend, Storage),
+ BackendArgs = proplists:get_value(args, Storage),
+ if
+ (BackendId =:= undefined) orelse (BackendArgs =:= undefined) ->
+ ignore;
+ true ->
+ {ok, {Strategy,
+ [{id1, {a_module, start_link, []},
+ permanent, 5000, worker, [a_module]},
+ {id2, {another_module, start_link, []},
+ permanent, 5000, worker, [another_module]}]}}
+ end
+ end.
diff --git a/lib/dialyzer/test/behaviour_SUITE_data/src/sample_behaviour/sample_callback_wrong.erl b/lib/dialyzer/test/behaviour_SUITE_data/src/sample_behaviour/sample_callback_wrong.erl
index 02a063fab7..430494c48c 100644
--- a/lib/dialyzer/test/behaviour_SUITE_data/src/sample_behaviour/sample_callback_wrong.erl
+++ b/lib/dialyzer/test/behaviour_SUITE_data/src/sample_behaviour/sample_callback_wrong.erl
@@ -1,6 +1,7 @@
-module(sample_callback_wrong).
--behaviour(sample_behaviour).
+%% This attribute uses the american spelling of 'behaviour'.
+-behavior(sample_behaviour).
-export([
% sample_callback_1/0,
diff --git a/lib/dialyzer/test/dialyzer_common.erl b/lib/dialyzer/test/dialyzer_common.erl
index 51766a4604..d2b1026c06 100644
--- a/lib/dialyzer/test/dialyzer_common.erl
+++ b/lib/dialyzer/test/dialyzer_common.erl
@@ -216,10 +216,13 @@ get_suites(Dir) ->
end.
suffix(String, Suffix) ->
- Index = string:rstr(String, Suffix),
- case string:substr(String, Index) =:= Suffix of
- true -> {yes, string:sub_string(String,1,Index-1)};
- false -> no
+ case string:rstr(String, Suffix) of
+ 0 -> no;
+ Index ->
+ case string:substr(String, Index) =:= Suffix of
+ true -> {yes, string:sub_string(String,1,Index-1)};
+ false -> no
+ end
end.
-spec create_suite(string()) -> 'ok'.
diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/multiple_wrong_opaques b/lib/dialyzer/test/opaque_SUITE_data/results/multiple_wrong_opaques
new file mode 100644
index 0000000000..18ece8820c
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/results/multiple_wrong_opaques
@@ -0,0 +1,2 @@
+
+multiple_wrong_opaques.erl:5: Invalid type specification for function multiple_wrong_opaques:weird/1. The success typing is ('gazonk') -> 42
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/multiple_wrong_opaques.erl b/lib/dialyzer/test/opaque_SUITE_data/src/multiple_wrong_opaques.erl
new file mode 100644
index 0000000000..9e695cec1d
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/multiple_wrong_opaques.erl
@@ -0,0 +1,8 @@
+-module(multiple_wrong_opaques).
+
+-export([weird/1]).
+
+-spec weird(dict() | gb_tree()) -> 42.
+
+weird(gazonk) -> 42.
+
diff --git a/lib/dialyzer/test/options1_SUITE_data/results/compiler b/lib/dialyzer/test/options1_SUITE_data/results/compiler
index e82087ae86..6399e3e36b 100644
--- a/lib/dialyzer/test/options1_SUITE_data/results/compiler
+++ b/lib/dialyzer/test/options1_SUITE_data/results/compiler
@@ -20,6 +20,7 @@ cerl_inline.erl:2333: The pattern 'true' can never match the type 'false'
cerl_inline.erl:2355: The pattern 'true' can never match the type 'false'
cerl_inline.erl:238: The pattern 'true' can never match the type 'false'
cerl_inline.erl:2436: Function filename/1 will never be called
+cerl_inline.erl:244: Function counter_stats/0 will never be called
cerl_inline.erl:2700: The pattern 'true' can never match the type 'false'
cerl_inline.erl:2730: The pattern <{F, L, D}, Vs> can never match the type <[1..255,...],[any()]>
cerl_inline.erl:2738: The pattern <{F, L, D}, Vs> can never match the type <[1..255,...],[any()]>
diff --git a/lib/dialyzer/test/r9c_SUITE_data/results/inets b/lib/dialyzer/test/r9c_SUITE_data/results/inets
index 24cb39e52b..773525eb7f 100644
--- a/lib/dialyzer/test/r9c_SUITE_data/results/inets
+++ b/lib/dialyzer/test/r9c_SUITE_data/results/inets
@@ -8,6 +8,7 @@ http_lib.erl:424: The variable _ can never match since previous clauses complete
http_lib.erl:438: The variable _ can never match since previous clauses completely covered the type any()
http_lib.erl:99: Function getHeaderValue/2 will never be called
httpc_handler.erl:660: Function exit_session_ok/2 has no local return
+httpc_handler.erl:676: Function format_time/0 will never be called
httpc_manager.erl:145: The pattern {ErrorReply, State2} can never match the type {{'ok',number()},number(),#state{reqid::number()}}
httpc_manager.erl:160: The pattern {ErrorReply, State2} can never match the type {{'ok',number()},number(),#state{reqid::number()}}
httpc_manager.erl:478: The pattern {'error', Reason} can never match the type 'ok' | {number(),#session{clientclose::boolean(),pipeline::[],quelength::1}}
diff --git a/lib/dialyzer/test/small_SUITE_data/results/cerl_hipeify b/lib/dialyzer/test/small_SUITE_data/results/cerl_hipeify
index 87bf6f309f..06dc0d63ee 100644
--- a/lib/dialyzer/test/small_SUITE_data/results/cerl_hipeify
+++ b/lib/dialyzer/test/small_SUITE_data/results/cerl_hipeify
@@ -1,4 +1,4 @@
cerl_hipeify.erl:370: Function will never be called
-cerl_hipeify.erl:370: Guard test fun((none()) -> none()) =:= F::{_,_,_} | {_,_,_,_} | {_,_,_,_,_} | {_,_,_,_,_,_} | {_,_,_,_,_,_,_} can never succeed
+cerl_hipeify.erl:370: Guard test fun((none()) -> no_return()) =:= F::{_,_,_} | {_,_,_,_} | {_,_,_,_,_} | {_,_,_,_,_,_} | {_,_,_,_,_,_,_} can never succeed
cerl_hipeify.erl:641: Function env__new_function_name/2 will never be called
diff --git a/lib/dialyzer/test/small_SUITE_data/results/inf_loop2 b/lib/dialyzer/test/small_SUITE_data/results/inf_loop2
index 7e9972ad98..142e4b2c37 100644
--- a/lib/dialyzer/test/small_SUITE_data/results/inf_loop2
+++ b/lib/dialyzer/test/small_SUITE_data/results/inf_loop2
@@ -1,4 +1,4 @@
inf_loop2.erl:18: Function test/0 has no local return
inf_loop2.erl:19: The call lists:reverse('gazonk') will never return since it differs in the 1st argument from the success typing arguments: ([any()])
-inf_loop2.erl:22: Function loop/0 has no local return
+inf_loop2.erl:22: Function loop/0 will never be called
diff --git a/lib/dialyzer/test/small_SUITE_data/results/no_local_return b/lib/dialyzer/test/small_SUITE_data/results/no_local_return
new file mode 100644
index 0000000000..6ca1ed51d8
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/results/no_local_return
@@ -0,0 +1,3 @@
+
+no_local_return.erl:11: Function bar/1 will never be called
+no_local_return.erl:8: Function foo/0 will never be called
diff --git a/lib/dialyzer/test/small_SUITE_data/src/no_local_return.erl b/lib/dialyzer/test/small_SUITE_data/src/no_local_return.erl
new file mode 100644
index 0000000000..4e1a0b015a
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/no_local_return.erl
@@ -0,0 +1,12 @@
+-module(no_local_return).
+
+%% NOTE: No function is exported. Dialyzer produced a bogus
+%% 'Function foo/0 has no local return' warning
+%% when in fact typer was finding correct return values for both
+%% these functions.
+
+foo() ->
+ bar(42).
+
+bar(X) ->
+ lists:duplicate(X, gazonk).
diff --git a/lib/dialyzer/test/small_SUITE_data/src/on_load.erl b/lib/dialyzer/test/small_SUITE_data/src/on_load.erl
new file mode 100644
index 0000000000..16533a9caa
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/on_load.erl
@@ -0,0 +1,11 @@
+%%% This is to ensure that "on_load" functions are never reported as unused.
+
+-module(on_load).
+
+-export([foo/0]).
+
+-on_load(bar/0).
+
+foo() -> ok.
+
+bar() -> ok.
diff --git a/lib/hipe/cerl/erl_types.erl b/lib/hipe/cerl/erl_types.erl
index 620fed365e..65b9a057de 100644
--- a/lib/hipe/cerl/erl_types.erl
+++ b/lib/hipe/cerl/erl_types.erl
@@ -424,12 +424,7 @@ t_has_opaque_subtype(T) ->
-spec t_opaque_structure(erl_type()) -> erl_type().
t_opaque_structure(?opaque(Elements)) ->
- case ordsets:size(Elements) of
- 1 ->
- [#opaque{struct = Struct}] = ordsets:to_list(Elements),
- Struct;
- _ -> throw({error, "Unexpected multiple opaque types"})
- end.
+ t_sup([Struct || #opaque{struct = Struct} <- ordsets:to_list(Elements)]).
-spec t_opaque_module(erl_type()) -> module().
@@ -688,9 +683,9 @@ t_solve_remote(?opaque(Set), ET, R, C) ->
{NewList, RR} = opaques_solve_remote(List, ET, R, C),
{?opaque(ordsets:from_list(NewList)), RR};
t_solve_remote(?tuple(?any, _, _) = T, _ET, _R, _C) -> {T, []};
-t_solve_remote(?tuple(Types, Arity, Tag), ET, R, C) ->
+t_solve_remote(?tuple(Types, _Arity, _Tag), ET, R, C) ->
{RL, RR} = list_solve_remote(Types, ET, R, C),
- {?tuple(RL, Arity, Tag), RR};
+ {t_tuple(RL), RR};
t_solve_remote(?tuple_set(Set), ET, R, C) ->
{NewSet, RR} = tuples_solve_remote(Set, ET, R, C),
{?tuple_set(NewSet), RR};