aboutsummaryrefslogtreecommitdiffstats
path: root/lib/dialyzer/src/dialyzer_typesig.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dialyzer/src/dialyzer_typesig.erl')
-rw-r--r--lib/dialyzer/src/dialyzer_typesig.erl280
1 files changed, 156 insertions, 124 deletions
diff --git a/lib/dialyzer/src/dialyzer_typesig.erl b/lib/dialyzer/src/dialyzer_typesig.erl
index 4d650592e2..d03326ec97 100644
--- a/lib/dialyzer/src/dialyzer_typesig.erl
+++ b/lib/dialyzer/src/dialyzer_typesig.erl
@@ -1,8 +1,4 @@
%% -*- erlang-indent-level: 2 -*-
-%%-----------------------------------------------------------------------
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2006-2017. 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.
@@ -15,9 +11,6 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
%%%-------------------------------------------------------------------
%%% File : dialyzer_typesig.erl
@@ -48,7 +41,7 @@
t_is_float/1, t_is_fun/1,
t_is_integer/1, t_non_neg_integer/0,
t_is_list/1, t_is_nil/1, t_is_none/1, t_is_number/1,
- t_is_singleton/1,
+ t_is_singleton/1, t_is_none_or_unit/1,
t_limit/2, t_list/0, t_list/1,
t_list_elements/1, t_nonempty_list/1, t_maybe_improper_list/0,
@@ -88,7 +81,7 @@
-record(constraint_list, {type :: 'conj' | 'disj',
list :: [constr()],
deps :: deps(),
- masks = maps:new() :: #{dep() => mask()},
+ masks :: #{dep() => mask()} | 'undefined',
id :: {'list', dep()} | 'undefined'}).
-type constraint_list() :: #constraint_list{}.
@@ -103,10 +96,12 @@
-type typesig_funmap() :: #{type_var() => type_var()}.
--type prop_types() :: dict:dict(label(), erl_types:erl_type()).
+-type prop_types() :: orddict:orddict(label(), erl_types:erl_type()).
+-type dict_prop_types() :: dict:dict(label(), erl_types:erl_type()).
-record(state, {callgraph :: dialyzer_callgraph:callgraph()
| 'undefined',
+ cserver :: dialyzer_codeserver:codeserver(),
cs = [] :: [constr()],
cmap = maps:new() :: #{type_var() => constr()},
fun_map = maps:new() :: typesig_funmap(),
@@ -119,8 +114,8 @@
self_rec :: 'false' | erl_types:erl_type(),
plt :: dialyzer_plt:plt()
| 'undefined',
- prop_types = dict:new() :: prop_types(),
- records = maps:new() :: types(),
+ prop_types = dict:new() :: dict_prop_types(),
+ mod_records = [] :: [{module(), types()}],
scc = [] :: ordsets:ordset(type_var()),
mfas :: [mfa()],
solvers = [] :: [solver()]
@@ -145,9 +140,11 @@
-ifdef(DEBUG).
-define(debug(__String, __Args), io:format(__String, __Args)).
-define(mk_fun_var(Fun, Vars), mk_fun_var(?LINE, Fun, Vars)).
+-define(pp_map(S, M), pp_map(S, M)).
-else.
-define(debug(__String, __Args), ok).
-define(mk_fun_var(Fun, Vars), mk_fun_var(Fun, Vars)).
+-define(pp_map(S, M), ok).
-endif.
%% ============================================================================
@@ -184,16 +181,13 @@ analyze_scc(SCC, NextLabel, CallGraph, CServer, Plt, PropTypes, Solvers0) ->
State1 = new_state(SCC, NextLabel, CallGraph, CServer, Plt, PropTypes,
Solvers),
DefSet = add_def_list(maps:values(State1#state.name_map), sets:new()),
- ModRecs = [{M, dialyzer_codeserver:lookup_mod_records(M, CServer)} ||
- M <- lists:usort([M || {M, _, _} <- SCC])],
- State2 = traverse_scc(SCC, CServer, DefSet, ModRecs, State1),
+ State2 = traverse_scc(SCC, CServer, DefSet, State1),
State3 = state__finalize(State2),
- erlang:garbage_collect(),
Funs = state__scc(State3),
pp_constrs_scc(Funs, State3),
constraints_to_dot_scc(Funs, State3),
T = solve(Funs, State3),
- dict:from_list(maps:to_list(T)).
+ orddict:from_list(maps:to_list(T)).
solvers([]) -> [v2];
solvers(Solvers) -> Solvers.
@@ -204,15 +198,14 @@ solvers(Solvers) -> Solvers.
%%
%% ============================================================================
-traverse_scc([{M,_,_}=MFA|Left], Codeserver, DefSet, ModRecs, AccState) ->
+traverse_scc([{M,_,_}=MFA|Left], Codeserver, DefSet, AccState) ->
+ TmpState1 = state__set_module(AccState, M),
Def = dialyzer_codeserver:lookup_mfa_code(MFA, Codeserver),
- {M, Rec} = lists:keyfind(M, 1, ModRecs),
- TmpState1 = state__set_rec_dict(AccState, Rec),
DummyLetrec = cerl:c_letrec([Def], cerl:c_atom(foo)),
TmpState2 = state__new_constraint_context(TmpState1),
{NewAccState, _} = traverse(DummyLetrec, DefSet, TmpState2),
- traverse_scc(Left, Codeserver, DefSet, ModRecs, NewAccState);
-traverse_scc([], _Codeserver, _DefSet, _ModRecs, AccState) ->
+ traverse_scc(Left, Codeserver, DefSet, NewAccState);
+traverse_scc([], _Codeserver, _DefSet, AccState) ->
AccState.
traverse(Tree, DefinedVars, State) ->
@@ -478,12 +471,11 @@ traverse(Tree, DefinedVars, State) ->
true ->
%% Check if a record is constructed.
Arity = length(Fields),
- 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),
- {State3, TupleType}
+ case lookup_record(State2, cerl:atom_val(Tag), Arity) of
+ {error, State3} -> {State3, TupleType};
+ {ok, RecType, State3} ->
+ State4 = state__store_conj(TupleType, sub, RecType, State3),
+ {State4, TupleType}
end;
false -> {State2, TupleType}
end;
@@ -536,13 +528,14 @@ traverse(Tree, DefinedVars, State) ->
false -> t_any();
true ->
MT = t_inf(lookup_type(MapVar, Map), t_map()),
- case t_is_none(MT) of
+ case t_is_none_or_unit(MT) of
true -> t_none();
false ->
DisjointFromKeyType =
fun(ShadowKey) ->
- t_is_none(t_inf(lookup_type(ShadowKey, Map),
- KeyType))
+ ST = t_inf(lookup_type(ShadowKey, Map),
+ KeyType),
+ t_is_none_or_unit(ST)
end,
case lists:all(DisjointFromKeyType, ShadowKeys) of
true -> t_map_get(KeyType, MT);
@@ -575,7 +568,8 @@ traverse(Tree, DefinedVars, State) ->
case cerl:is_literal(OpTree) andalso
cerl:concrete(OpTree) =:= exact of
true ->
- case t_is_none(t_inf(ShadowedKeys, KeyType)) of
+ ST = t_inf(ShadowedKeys, KeyType),
+ case t_is_none_or_unit(ST) of
true ->
t_map_put({KeyType, t_any()}, AccType);
false ->
@@ -779,7 +773,7 @@ handle_call(Call, DefinedVars, State) ->
{ok, Var} ->
%% This is part of the SCC currently analyzed.
%% Intercept and change this to an apply instead.
- ?debug("Found the call to ~w\n", [MFA]),
+ ?debug("Found the call to ~tw\n", [MFA]),
Label = cerl_trees:get_label(Call),
Apply = cerl:ann_c_apply([{label, Label}], Var, Args),
traverse(Apply, DefinedVars, State)
@@ -1448,7 +1442,6 @@ 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,
ArgFun =
fun(Map) ->
case t_is_any_atom(true, lookup_type(Dst, Map)) of
@@ -1465,10 +1458,10 @@ 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 lookup_record(Records, TagVal, ArityVal - 1) of
- {ok, Type} ->
+ case lookup_record(State, TagVal, ArityVal - 1) of
+ {ok, Type, _NewState} ->
Type;
- error -> GenRecord
+ {error, _NewState} -> GenRecord
end;
_ -> GenRecord
end;
@@ -1797,11 +1790,11 @@ get_bif_test_constr(Dst, Arg, Type, _State) ->
%%=============================================================================
solve([Fun], State) ->
- ?debug("============ Analyzing Fun: ~w ===========\n",
+ ?debug("============ Analyzing Fun: ~tw ===========\n",
[debug_lookup_name(Fun)]),
solve_fun(Fun, map_new(), State);
solve([_|_] = SCC, State) ->
- ?debug("============ Analyzing SCC: ~w ===========\n",
+ ?debug("============ Analyzing SCC: ~tw ===========\n",
[[debug_lookup_name(F) || F <- SCC]]),
Users = comp_users(SCC, State),
solve_scc(SCC, map_new(), State, Users, _ToSolve=SCC, false).
@@ -1837,13 +1830,13 @@ solve_scc(SCC, Map, State, Users, ToSolve, TryingUnit) ->
erase_type(t_var_name(Fun), AccFunMap)
end, Map, ToSolve),
Map1 = enter_type_lists(Vars, RecTypes, CleanMap),
- ?debug("Checking SCC: ~w\n", [[debug_lookup_name(F) || F <- SCC]]),
+ ?debug("Checking SCC: ~tw\n", [[debug_lookup_name(F) || F <- SCC]]),
SolveFun = fun(X, Y) -> scc_fold_fun(X, Y, State) end,
Map2 = lists:foldl(SolveFun, Map1, ToSolve),
Updated = updated_vars_only(Vars, Map, Map2),
case Updated =:= [] of
true ->
- ?debug("SCC ~w reached fixpoint\n", [SCC]),
+ ?debug("SCC ~tw reached fixpoint\n", [SCC]),
NewTypes = unsafe_lookup_type_list(Funs, Map2),
case erl_types:any_none([t_fun_range(T) || T <- NewTypes])
andalso TryingUnit =:= false of
@@ -1859,7 +1852,7 @@ solve_scc(SCC, Map, State, Users, ToSolve, TryingUnit) ->
Map2
end;
false ->
- ?debug("SCC ~w did not reach fixpoint\n", [SCC]),
+ ?debug("SCC ~tw did not reach fixpoint\n", [SCC]),
ToSolve1 = affected(Updated, Users),
solve_scc(SCC, Map2, State, Users, ToSolve1, TryingUnit)
end.
@@ -1883,8 +1876,8 @@ scc_fold_fun(F, FunMap, State) ->
error ->
enter_type(F, NewType, FunMap)
end,
- ?debug("Done solving for function ~w :: ~s\n", [debug_lookup_name(F),
- format_type(NewType)]),
+ ?debug("Done solving for function ~tw :: ~ts\n", [debug_lookup_name(F),
+ format_type(NewType)]),
NewFunMap.
solve(Fun, Cs, FunMap, State) ->
@@ -1903,7 +1896,7 @@ solver(Solver, SolveFun) ->
[Solver, _R, 60]),
throw(error)
catch E:R ->
- io:format("Solver ~w failed: ~w:~p\n ~p\n",
+ io:format("Solver ~w failed: ~w:~p\n ~tp\n",
[Solver, E, R, erlang:get_stacktrace()]),
throw(error)
end.
@@ -1925,8 +1918,8 @@ check_solutions([{S1,Map1,_Time1}|Maps], Fun, S, Map) ->
check_solutions(Maps, Fun, S1, Map1);
false ->
?debug("Constraint solvers do not agree on ~w\n", [Fun]),
- pp_map(atom_to_list(S), Map),
- pp_map(atom_to_list(S1), Map1),
+ ?pp_map(atom_to_list(S), Map),
+ ?pp_map(atom_to_list(S1), Map1),
io:format("A bug was found. Please report it, and use the option "
"`--solver v1' until the bug has been fixed.\n"),
throw(error)
@@ -1940,9 +1933,9 @@ sane_maps(Map1, Map2, Keys, _S1, _S2) ->
true -> true;
false ->
?debug("Constraint solvers do not agree on ~w\n", [Key]),
- ?debug("~w: ~s\n",
+ ?debug("~w: ~ts\n",
[_S1, format_type(unsafe_lookup_type(Key, Map1))]),
- ?debug("~w: ~s\n",
+ ?debug("~w: ~ts\n",
[_S2, format_type(unsafe_lookup_type(Key, Map2))]),
false
end
@@ -1974,8 +1967,8 @@ v2_solve(#constraint_ref{id = Id}, Map, V2State) ->
v2_solve_reference(Id, Map, V2State).
v2_solve_reference(Id, Map, V2State0) ->
- ?debug("Checking ref to fun: ~w\n", [debug_lookup_name(Id)]),
- pp_map("Map", Map),
+ ?debug("Checking ref to fun: ~tw\n", [debug_lookup_name(Id)]),
+ ?pp_map("Map", Map),
pp_constr_data("solve_ref", V2State0),
Map1 = restore_local_map(V2State0, Id, Map),
State = V2State0#v2_state.state,
@@ -1988,7 +1981,7 @@ v2_solve_reference(Id, Map, V2State0) ->
{FunType, V2State} =
case Res of
{error, V2State1} ->
- ?debug("Error solving for function ~p\n", [debug_lookup_name(Id)]),
+ ?debug("Error solving for function ~tp\n", [debug_lookup_name(Id)]),
Arity = state__fun_arity(Id, State),
FunType0 =
case state__prop_domain(t_var_name(Id), State) of
@@ -1997,12 +1990,12 @@ v2_solve_reference(Id, Map, V2State0) ->
end,
{FunType0, V2State1};
{ok, NewMap, V2State1, U} ->
- ?debug("Done solving fun: ~p\n", [debug_lookup_name(Id)]),
+ ?debug("Done solving fun: ~tp\n", [debug_lookup_name(Id)]),
FunType0 = lookup_type(Id, NewMap),
V2State2 = save_local_map(V2State1, Id, U, NewMap),
{FunType0, V2State2}
end,
- ?debug("ref Id=~w Assigned ~s\n", [Id, format_type(FunType)]),
+ ?debug("ref Id=~w Assigned ~ts\n", [Id, format_type(FunType)]),
{NewMap1, U1} = enter_var_type(Id, FunType, Map),
{NewMap2, U2} =
case state__get_rec_var(Id, State) of
@@ -2012,10 +2005,10 @@ v2_solve_reference(Id, Map, V2State0) ->
{ok, NewMap2, V2State, lists:umerge(U1, U2)}.
v2_solve_self_recursive(Cs, Map, Id, RecType0, V2State0) ->
- ?debug("Solving self recursive ~w\n", [debug_lookup_name(Id)]),
+ ?debug("Solving self recursive ~tw\n", [debug_lookup_name(Id)]),
State = V2State0#v2_state.state,
{ok, RecVar} = state__get_rec_var(Id, State),
- ?debug("OldRecType ~s\n", [format_type(RecType0)]),
+ ?debug("OldRecType ~ts\n", [format_type(RecType0)]),
RecType = t_limit(RecType0, ?TYPE_LIMIT),
{Map1, U0} = enter_var_type(RecVar, RecType, Map),
V2State1 = save_updated_vars1(V2State0, Cs, U0), % Probably not necessary
@@ -2031,7 +2024,7 @@ v2_solve_self_recursive(Cs, Map, Id, RecType0, V2State0) ->
Error
end;
{ok, NewMap, V2State, U} ->
- pp_map("recursive finished", NewMap),
+ ?pp_map("recursive finished", NewMap),
NewRecType = unsafe_lookup_type(Id, NewMap),
case is_equal(NewRecType, RecType0) of
true ->
@@ -2049,7 +2042,7 @@ enter_var_type(Var, Type, Map0) ->
v2_solve_disjunct(Disj, Map, V2State0) ->
#constraint_list{type = disj, id = _Id, list = Cs, masks = Masks} = Disj,
?debug("disjunct Id=~w~n", [_Id]),
- pp_map("Map", Map),
+ ?pp_map("Map", Map),
pp_constr_data("disjunct", V2State0),
case get_flags(V2State0, Disj) of
{V2State1, failed_list} -> {error, V2State1}; % cannot happen
@@ -2077,7 +2070,7 @@ v2_solve_disjunct(Disj, Map, V2State0) ->
U1 = [V || V <- U0,
var_occurs_everywhere(V, Masks, NotFailed)],
NewMap = join_maps(U1, MapL, Map),
- pp_map("NewMap", NewMap),
+ ?pp_map("NewMap", NewMap),
U = updated_vars_only(U1, Map, NewMap),
?debug("disjunct finished _Id=~w\n", [_Id]),
{ok, NewMap, V2State, U}
@@ -2100,7 +2093,7 @@ v2_solve_disj([I|Is], [C|Cs], I, Map0, V2State0, UL, MapL, Eval, Uneval,
{ok, Map, V2State1, U} ->
?debug("disj I=~w U=~w~n", [I, U]),
V2State = save_local_map(V2State1, Id, U, Map),
- pp_map("DMap", Map),
+ ?pp_map("DMap", Map),
v2_solve_disj(Is, Cs, I+1, Map0, V2State, [U|UL], [Map|MapL],
[I|Eval], Uneval, Failed0)
end;
@@ -2126,9 +2119,9 @@ save_local_map(#v2_state{constr_data = ConData}=V2State, Id, U, Map) ->
end,
?debug("save local map Id=~w:\n", [Id]),
Part = lists:ukeymerge(1, lists:keysort(1, Part0), Part1),
- pp_map("New Part", maps:from_list(Part0)),
- pp_map("Old Part", maps:from_list(Part1)),
- pp_map(" => Part", maps:from_list(Part)),
+ ?pp_map("New Part", maps:from_list(Part0)),
+ ?pp_map("Old Part", maps:from_list(Part1)),
+ ?pp_map(" => Part", maps:from_list(Part)),
V2State#v2_state{constr_data = maps:put(Id, {Part,[]}, ConData)}.
restore_local_map(#v2_state{constr_data = ConData}, Id, Map0) ->
@@ -2139,10 +2132,10 @@ restore_local_map(#v2_state{constr_data = ConData}, Id, Map0) ->
{ok, {Part0,U}} ->
Part = [KV || {K,_V} = KV <- Part0, not lists:member(K, U)],
?debug("restore local map Id=~w U=~w\n", [Id, U]),
- pp_map("Part", maps:from_list(Part)),
- pp_map("Map0", Map0),
+ ?pp_map("Part", maps:from_list(Part)),
+ ?pp_map("Map0", Map0),
Map = lists:foldl(fun({K,V}, D) -> maps:put(K, V, D) end, Map0, Part),
- pp_map("Map", Map),
+ ?pp_map("Map", Map),
Map
end.
@@ -2298,7 +2291,7 @@ pp_constr_data(_Tag, #v2_state{constr_data = D}) ->
case _PartU of
{_Part, _U} ->
io:format("Id: ~w Vars: ~w\n", [_Id, _U]),
- [pp_map("Part", maps:from_list(_Part)) || _Part =/= []];
+ [?pp_map("Part", maps:from_list(_Part)) || _Part =/= []];
failed ->
io:format("Id: ~w failed list\n", [_Id])
end
@@ -2327,7 +2320,7 @@ solve_ref_or_list(#constraint_ref{id = Id, deps = Deps},
error -> {map_new(), false};
{ok, M} -> {M, true}
end,
- ?debug("Checking ref to fun: ~w\n", [debug_lookup_name(Id)]),
+ ?debug("Checking ref to fun: ~tw\n", [debug_lookup_name(Id)]),
%% Note: mk_constraint_ref() has already removed Id from Deps. The
%% reason for doing it there is that it makes it easy for
%% calculate_masks() to make the corresponding adjustment for
@@ -2349,7 +2342,7 @@ solve_ref_or_list(#constraint_ref{id = Id, deps = Deps},
{NewMapDict, FunType} =
case Res of
{error, NewMapDict0} ->
- ?debug("Error solving for function ~p\n", [debug_lookup_name(Id)]),
+ ?debug("Error solving for function ~tp\n", [debug_lookup_name(Id)]),
Arity = state__fun_arity(Id, State),
FunType0 =
case state__prop_domain(t_var_name(Id), State) of
@@ -2358,11 +2351,11 @@ solve_ref_or_list(#constraint_ref{id = Id, deps = Deps},
end,
{NewMapDict0, FunType0};
{ok, NewMapDict0, NewMap} ->
- ?debug("Done solving fun: ~p\n", [debug_lookup_name(Id)]),
+ ?debug("Done solving fun: ~tp\n", [debug_lookup_name(Id)]),
FunType0 = lookup_type(Id, NewMap),
{NewMapDict0, FunType0}
end,
- ?debug(" Id=~w Assigned ~s\n", [Id, format_type(FunType)]),
+ ?debug(" Id=~w Assigned ~ts\n", [Id, format_type(FunType)]),
NewMap1 = enter_type(Id, FunType, Map),
NewMap2 =
case state__get_rec_var(Id, State) of
@@ -2384,21 +2377,21 @@ solve_ref_or_list(#constraint_list{type=Type, list = Cs, deps = Deps, id = Id},
true ->
case Check andalso maps_are_equal(OldLocalMap, Map, Deps) of
true ->
- ?debug("~w equal ~w\n", [Type, Id]),
+ ?debug("~tw equal ~w\n", [Type, Id]),
{ok, MapDict, Map};
false ->
- ?debug("~w not equal: ~w. Solving\n", [Type, Id]),
+ ?debug("~tw not equal: ~w. Solving\n", [Type, Id]),
solve_clist(Cs, Type, Id, Deps, MapDict, Map, State)
end
end.
solve_self_recursive(Cs, Map, MapDict, Id, RecType0, State) ->
- ?debug("Solving self recursive ~w\n", [debug_lookup_name(Id)]),
+ ?debug("Solving self recursive ~tw\n", [debug_lookup_name(Id)]),
{ok, RecVar} = state__get_rec_var(Id, State),
- ?debug("OldRecType ~s\n", [format_type(RecType0)]),
+ ?debug("OldRecType ~ts\n", [format_type(RecType0)]),
RecType = t_limit(RecType0, ?TYPE_LIMIT),
Map1 = enter_type(RecVar, RecType, erase_type(t_var_name(Id), Map)),
- pp_map("Map1", Map1),
+ ?pp_map("Map1", Map1),
case solve_ref_or_list(Cs, Map1, MapDict, State) of
{error, _} = Error ->
case t_is_none(RecType0) of
@@ -2411,7 +2404,7 @@ solve_self_recursive(Cs, Map, MapDict, Id, RecType0, State) ->
Error
end;
{ok, NewMapDict, NewMap} ->
- pp_map("NewMap", NewMap),
+ ?pp_map("NewMap", NewMap),
NewRecType = unsafe_lookup_type(Id, NewMap),
case is_equal(NewRecType, RecType0) of
true ->
@@ -2477,7 +2470,7 @@ solve_one_c(#constraint{lhs = Lhs, rhs = Rhs, op = Op}, Map) ->
LhsType = lookup_type(Lhs, Map),
RhsType = lookup_type(Rhs, Map),
Inf = t_inf(LhsType, RhsType),
- ?debug("Solving: ~s :: ~s ~w ~s :: ~s\n\tInf: ~s\n",
+ ?debug("Solving: ~ts :: ~ts ~w ~ts :: ~ts\n\tInf: ~ts\n",
[format_type(Lhs), format_type(LhsType), Op,
format_type(Rhs), format_type(RhsType), format_type(Inf)]),
case t_is_none(Inf) of
@@ -2509,14 +2502,14 @@ solve_subtype(Type, Inf, Map) ->
{_, List} -> {ok, enter_type_list(List, Map)}
catch
throw:{mismatch, _T1, _T2} ->
- ?debug("Mismatch between ~s and ~s\n",
+ ?debug("Mismatch between ~ts and ~ts\n",
[format_type(_T1), format_type(_T2)]),
error
end.
%% end.
report_failed_constraint(_C, _Map) ->
- ?debug("+++++++++++\nFailed: ~s :: ~s ~w ~s :: ~s\n+++++++++++\n",
+ ?debug("+++++++++++\nFailed: ~ts :: ~ts ~w ~ts :: ~ts\n+++++++++++\n",
[format_type(_C#constraint.lhs),
format_type(lookup_type(_C#constraint.lhs, _Map)),
_C#constraint.op,
@@ -2574,7 +2567,7 @@ maps_are_equal_1(Map1, Map2, [H|Tail]) ->
case is_equal(T1, T2) of
true -> maps_are_equal_1(Map1, Map2, Tail);
false ->
- ?debug("~w: ~s =/= ~s\n", [H, format_type(T1), format_type(T2)]),
+ ?debug("~w: ~ts =/= ~ts\n", [H, format_type(T1), format_type(T2)]),
false
end;
maps_are_equal_1(_Map1, _Map2, []) ->
@@ -2602,7 +2595,7 @@ prune_keys(Map1, Map2, Deps) ->
end.
enter_type(Key, Val, Map) when is_integer(Key) ->
- ?debug("Entering ~s :: ~s\n", [format_type(t_var(Key)), format_type(Val)]),
+ ?debug("Entering ~ts :: ~ts\n", [format_type(t_var(Key)), format_type(Val)]),
%% Keep any() in the map if it is opaque:
case is_equal(Val, t_any()) of
true ->
@@ -2611,7 +2604,7 @@ enter_type(Key, Val, Map) when is_integer(Key) ->
LimitedVal = t_limit(Val, ?INTERNAL_TYPE_LIMIT),
case is_equal(LimitedVal, Val) of
true -> ok;
- false -> ?debug("LimitedVal ~s\n", [format_type(LimitedVal)])
+ false -> ?debug("LimitedVal ~ts\n", [format_type(LimitedVal)])
end,
case maps:find(Key, Map) of
{ok, Value} ->
@@ -2646,7 +2639,7 @@ enter_type2(Key, Val, Map) ->
{Map1, [Key || not is_same(Key, Map, Map1)]}.
map_store(Key, Val, Map) ->
- ?debug("Storing ~w :: ~s\n", [Key, format_type(Val)]),
+ ?debug("Storing ~tw :: ~ts\n", [Key, format_type(Val)]),
maps:put(Key, Val, Map).
erase_type(Key, Map) ->
@@ -2710,18 +2703,13 @@ is_same(Key, Map1, Map2) ->
is_equal(Type1, Type2) ->
t_is_equal(Type1, Type2).
-pp_map(_S, _Map) ->
- ?debug("\t~s: ~p\n",
- [_S, [{X, lists:flatten(format_type(Y))} ||
- {X, Y} <- lists:keysort(1, maps:to_list(_Map))]]).
-
%% ============================================================================
%%
%% The State.
%%
%% ============================================================================
-new_state(MFAs, NextLabel, CallGraph, CServer, Plt, PropTypes, Solvers) ->
+new_state(MFAs, NextLabel, CallGraph, CServer, Plt, PropTypes0, Solvers) ->
List_SCC =
[begin
{Var, Label} = dialyzer_codeserver:lookup_mfa_var_label(MFA, CServer),
@@ -2739,12 +2727,14 @@ new_state(MFAs, NextLabel, CallGraph, CServer, Plt, PropTypes, Solvers) ->
end;
_Many -> false
end,
+ PropTypes = dict:from_list(PropTypes0),
#state{callgraph = CallGraph, name_map = NameMap, next_label = NextLabel,
prop_types = PropTypes, plt = Plt, scc = ordsets:from_list(SCC),
- mfas = MFAs, self_rec = SelfRec, solvers = Solvers}.
+ mfas = MFAs, self_rec = SelfRec, solvers = Solvers,
+ cserver = CServer}.
-state__set_rec_dict(State, RecDict) ->
- State#state{records = RecDict}.
+state__set_module(State, Module) ->
+ State#state{module = Module}.
state__set_in_match(State, Bool) ->
State#state{in_match = Bool}.
@@ -2854,7 +2844,7 @@ state__add_prop_constrs(Tree, #state{prop_types = PropTypes} = State) ->
case erl_types:any_none(ArgTypes) of
true -> not_called;
false ->
- ?debug("Adding propagated constr: ~s for function ~w\n",
+ ?debug("Adding propagated constr: ~ts for function ~tw\n",
[format_type(FunType), debug_lookup_name(mk_var(Tree))]),
FunVar = mk_var(Tree),
state__store_conj(FunVar, sub, FunType, State)
@@ -2983,6 +2973,11 @@ mk_fun_var(Line, Fun, Types) ->
Deps = [t_var_name(Var) || Var <- t_collect_vars(t_product(Types))],
#fun_var{'fun' = Fun, deps = ordsets:from_list(Deps), origin = Line}.
+pp_map(S, Map) ->
+ ?debug("\t~s: ~p\n",
+ [S, [{X, lists:flatten(format_type(Y))} ||
+ {X, Y} <- lists:keysort(1, maps:to_list(Map))]]).
+
-else.
-spec mk_fun_var(fun((_) -> erl_types:erl_type()), [erl_types:erl_type()]) -> #fun_var{}.
@@ -3215,7 +3210,8 @@ order_fun_constraints(State) ->
order_fun_constraints([#constraint_ref{id = Id}|Tail], State) ->
Cs = state__get_cs(Id, State),
- {[NewCs], State1} = order_fun_constraints([Cs], [], [], State),
+ {[Cs1], State1} = order_fun_constraints([Cs], [], [], State),
+ NewCs = Cs1#constraint_list{deps = Cs#constraint_list.deps},
NewState = state__store_constrs(Id, NewCs, State1),
order_fun_constraints(Tail, NewState);
order_fun_constraints([], State) ->
@@ -3223,23 +3219,31 @@ order_fun_constraints([], State) ->
order_fun_constraints([#constraint_ref{} = C|Tail], Funs, Acc, State) ->
order_fun_constraints(Tail, [C|Funs], Acc, State);
-order_fun_constraints([#constraint_list{list = List, type = Type} = C|Tail],
+order_fun_constraints([#constraint_list{list = List,
+ type = Type,
+ masks = OldMasks} = C|Tail],
Funs, Acc, State) ->
- {NewList, NewState} =
- case Type of
- conj -> order_fun_constraints(List, [], [], State);
- disj ->
- FoldFun = fun(X, AccState) ->
- {[NewX], NewAccState} =
- order_fun_constraints([X], [], [], AccState),
- {NewX, NewAccState}
- end,
- lists:mapfoldl(FoldFun, State, List)
- end,
- C1 = update_constraint_list(C, NewList),
- Masks = calculate_masks(NewList, 1, []),
- NewAcc = [update_masks(C1, Masks)|Acc],
- order_fun_constraints(Tail, Funs, NewAcc, NewState);
+ case OldMasks of
+ undefined ->
+ {NewList, NewState} =
+ case Type of
+ conj -> order_fun_constraints(List, [], [], State);
+ disj ->
+ FoldFun = fun(X, AccState) ->
+ {[NewX], NewAccState} =
+ order_fun_constraints([X], [], [], AccState),
+ {NewX, NewAccState}
+ end,
+ lists:mapfoldl(FoldFun, State, List)
+ end,
+ NewList2 = reset_deps(NewList, State),
+ C1 = update_constraint_list(C, NewList2),
+ Masks = calculate_masks(NewList, 1, []),
+ NewAcc = [update_masks(C1, Masks)|Acc],
+ order_fun_constraints(Tail, Funs, NewAcc, NewState);
+ M when is_map(M) ->
+ order_fun_constraints(Tail, Funs, [C|Acc], State)
+ end;
order_fun_constraints([#constraint{} = C|Tail], Funs, Acc, State) ->
order_fun_constraints(Tail, Funs, [C|Acc], State);
order_fun_constraints([], Funs, Acc, State) ->
@@ -3249,6 +3253,18 @@ order_fun_constraints([], Funs, Acc, State) ->
update_masks(C, Masks) ->
C#constraint_list{masks = Masks}.
+reset_deps(ConstrList, #state{solvers = Solvers}) ->
+ case lists:member(v1, Solvers) of
+ true ->
+ ConstrList;
+ false ->
+ [reset_deps(Constr) || Constr <- ConstrList]
+ end.
+
+reset_deps(#constraint{}=C) -> C#constraint{deps = []};
+reset_deps(#constraint_list{}=C) -> C#constraint_list{deps = []};
+reset_deps(#constraint_ref{}=C) -> C#constraint_ref{deps = []}.
+
calculate_masks([C|Cs], I, L0) ->
calculate_masks(Cs, I+1, [{V, I} || V <- get_deps(C)] ++ L0);
calculate_masks([], _I, L) ->
@@ -3335,15 +3351,25 @@ fold_literal_maybe_match(Tree0, State) ->
true -> dialyzer_utils:refold_pattern(Tree1)
end.
-lookup_record(Records, Tag, Arity) ->
- case erl_types:lookup_record(Tag, Arity, Records) of
+lookup_record(State, Tag, Arity) ->
+ #state{module = M, mod_records = ModRecs, cserver = CServer} = State,
+ {State1, Rec} =
+ case lists:keyfind(M, 1, ModRecs) of
+ {M, Rec0} ->
+ {State, Rec0};
+ false ->
+ Rec0 = dialyzer_codeserver:lookup_mod_records(M, CServer),
+ NewModRecs = [{M, Rec0}|ModRecs],
+ {State#state{mod_records = NewModRecs}, Rec0}
+ end,
+ case erl_types:lookup_record(Tag, Arity, Rec) of
{ok, Fields} ->
RecType =
t_tuple([t_from_term(Tag)|
[FieldType || {_FieldName, _Abstr, FieldType} <- Fields]]),
- {ok, RecType};
+ {ok, RecType, State1};
error ->
- error
+ {error, State1}
end.
is_literal_record(Tree) ->
@@ -3368,10 +3394,10 @@ family(L) ->
-ifdef(DEBUG).
format_type(#fun_var{deps = Deps, origin = Origin}) ->
L = [format_type(t_var(X)) || X <- Deps],
- io_lib:format("Fun@L~p(~s)", [Origin, join_chars(L, ",")]);
+ io_lib:format("Fun@L~p(~ts)", [Origin, join_chars(L, ",")]);
format_type(Type) ->
case cerl:is_literal(Type) of
- true -> io_lib:format("~w", [cerl:concrete(Type)]);
+ true -> io_lib:format("~tw", [cerl:concrete(Type)]);
false -> erl_types:t_to_string(Type)
end.
@@ -3413,7 +3439,7 @@ pp_constrs_scc(SCC, State) ->
[pp_constrs(Fun, state__get_cs(Fun, State), State) || Fun <- SCC].
pp_constrs(Fun, Cs, State) ->
- io:format("Constraints for fun: ~w", [debug_lookup_name(Fun)]),
+ io:format("Constraints for fun: ~tw", [debug_lookup_name(Fun)]),
MaxDepth = pp_constraints(Cs, State),
io:format("Depth: ~w\n", [MaxDepth]).
@@ -3451,7 +3477,7 @@ pp_constraints([#constraint_list{type = Type, list = List, id = Id}|Tail],
pp_op(#constraint{lhs = Lhs, op = Op, rhs = Rhs}, Level) ->
pp_indent(Level),
- io:format("~s ~w ~s", [format_type(Lhs), Op, format_type(Rhs)]).
+ io:format("~ts ~w ~ts", [format_type(Lhs), Op, format_type(Rhs)]).
pp_indent(Level) ->
io:format("\n~*s", [Level*2, ""]).
@@ -3463,19 +3489,25 @@ pp_constrs_scc(_SCC, _State) ->
-ifdef(TO_DOT).
constraints_to_dot_scc(SCC, State) ->
- io:format("SCC: ~p\n", [SCC]),
- Name = lists:flatten([io_lib:format("'~w'", [debug_lookup_name(Fun)])
- || Fun <- SCC]),
+ %% TODO: handle Unicode names.
+ io:format("SCC: ~tp\n", [SCC]),
+ Name = lists:flatten([format_lookup_name(debug_lookup_name(Fun))
+ || Fun <- SCC]),
Cs = [state__get_cs(Fun, State) || Fun <- SCC],
constraints_to_dot(Cs, Name, State).
+format_lookup_name({FunName, Arity}) ->
+ TupleS = io_lib:format("{~ts,~w}", [atom_to_list(FunName), Arity]),
+ io_lib:format("~tw", [list_to_atom(lists:flatten(TupleS))]).
+
constraints_to_dot(Cs0, Name, State) ->
NofCs = length(Cs0),
Cs = lists:zip(lists:seq(1, NofCs), Cs0),
{Graph, Opts, _N} = constraints_to_nodes(Cs, NofCs + 1, 1, [], [], State),
hipe_dot:translate_list(Graph, "/tmp/cs.dot", "foo", Opts),
+ %% "-T ps" works for Latin-1. hipe_dot cannot handle UTF-8 either.
Res = os:cmd("dot -o /tmp/"++ Name ++ ".ps -T ps /tmp/cs.dot"),
- io:format("Res: ~p~n", [Res]),
+ io:format("Res: ~ts~n", [Res]),
ok.
constraints_to_nodes([{Name, #constraint_list{type = Type, list = List, id=Id}}
@@ -3494,7 +3526,7 @@ constraints_to_nodes([{Name, #constraint_list{type = Type, list = List, id=Id}}
constraints_to_nodes(Left, N2, Level, NewGraph, NewOpts, State);
constraints_to_nodes([{Name, #constraint{lhs = Lhs, op = Op, rhs = Rhs}}|Left],
N, Level, Graph, Opts, State) ->
- Label = lists:flatten(io_lib:format("~s ~w ~s",
+ Label = lists:flatten(io_lib:format("~ts ~w ~ts",
[format_type(Lhs), Op,
format_type(Rhs)])),
ThisNode = [{Name, Opt} || Opt <- [{label, Label}, {level, Level}]],