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.erl153
1 files changed, 82 insertions, 71 deletions
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.