diff options
Diffstat (limited to 'lib/dialyzer/src/erl_types.erl')
-rw-r--r-- | lib/dialyzer/src/erl_types.erl | 720 |
1 files changed, 373 insertions, 347 deletions
diff --git a/lib/dialyzer/src/erl_types.erl b/lib/dialyzer/src/erl_types.erl index c75baee3cb..ed0264cc2f 100644 --- a/lib/dialyzer/src/erl_types.erl +++ b/lib/dialyzer/src/erl_types.erl @@ -55,7 +55,7 @@ t_boolean/0, t_byte/0, t_char/0, - t_collect_vars/1, + t_collect_var_names/1, t_cons/0, t_cons/2, t_cons_hd/1, t_cons_hd/2, @@ -116,6 +116,7 @@ t_is_float/1, t_is_float/2, t_is_fun/1, t_is_fun/2, t_is_identifier/1, + t_is_impossible/1, t_is_instance/2, t_is_integer/1, t_is_integer/2, t_is_list/1, @@ -186,7 +187,6 @@ t_reference/0, t_singleton_to_term/2, t_string/0, - t_struct_from_opaque/2, t_subst/2, t_subtract/2, t_subtract_list/2, @@ -203,7 +203,7 @@ t_tuple_sizes/1, t_tuple_subtypes/1, t_tuple_subtypes/2, - t_unify/2, + t_unify_table_only/2, t_unit/0, t_unopaque/1, t_unopaque/2, t_var/1, @@ -218,7 +218,9 @@ is_erl_type/1, atom_to_string/1, var_table__new/0, - cache__new/0 + cache__new/0, + module_type_deps_of_type_defs/1, + type_form_to_remote_modules/1 ]). -compile({no_auto_import,[min/2,max/2,map_get/2]}). @@ -320,11 +322,9 @@ -record(int_set, {set :: [integer()]}). -record(int_rng, {from :: rng_elem(), to :: rng_elem()}). -%% Note: the definition of #opaque{} was changed to 'mod' and 'name'; -%% it used to be an ordsets of {Mod, Name} pairs. The Dialyzer version -%% was updated to 2.7 due to this change. + -record(opaque, {mod :: module(), name :: atom(), - args = [] :: [erl_type()], struct :: erl_type()}). + arity = 0 :: arity(), struct :: erl_type()}). -define(atom(Set), #c{tag=?atom_tag, elements=Set}). -define(bitstr(Unit, Base), #c{tag=?binary_tag, elements=[Unit,Base]}). @@ -363,17 +363,18 @@ -type file_line() :: {file:name(), erl_anno:line()}. -type record_key() :: {'record', atom()}. --type type_key() :: {'type' | 'opaque', mfa()}. +-type type_key() :: {'type' | 'opaque', {atom(), arity()}}. -type field() :: {atom(), erl_parse:abstract_expr(), erl_type()}. -type record_value() :: {file_line(), [{RecordSize :: non_neg_integer(), [field()]}]}. -type type_value() :: {{module(), file_line(), erl_parse:abstract_type(), ArgNames :: [atom()]}, erl_type()}. --type type_table() :: #{record_key() | type_key() => - record_value() | type_value()}. +-type type_table() :: #{record_key() => record_value()} | + #{type_key() => type_value()}. --opaque var_table() :: #{atom() => erl_type()}. +-type var_name() :: atom() | integer(). +-type var_table() :: #{ var_name() => erl_type() }. %%----------------------------------------------------------------------------- %% Unions @@ -436,7 +437,7 @@ t_is_none(_) -> false. -spec t_opaque(module(), atom(), [_], erl_type()) -> erl_type(). t_opaque(Mod, Name, Args, Struct) -> - O = #opaque{mod = Mod, name = Name, args = Args, struct = Struct}, + O = #opaque{mod = Mod, name = Name, arity = length(Args), struct = Struct}, ?opaque(set_singleton(O)). -spec t_is_opaque(erl_type(), [erl_type()]) -> boolean(). @@ -747,7 +748,7 @@ decorate_tuples_in_sets([?tuple(Elements, Arity, Tag1) = T1|Tuples] = L1, if Tag1 < Tag2 -> decorate_tuples_in_sets(Tuples, L2, Opaques, [T1|Acc]); Tag1 > Tag2 -> decorate_tuples_in_sets(L1, Ts, Opaques, Acc); - Tag1 =:= Tag2 -> + Tag1 == Tag2 -> NewElements = list_decorate(Elements, Es, Opaques), NewAcc = [?tuple(NewElements, Arity, Tag1)|Acc], decorate_tuples_in_sets(Tuples, Ts, Opaques, NewAcc) @@ -780,37 +781,6 @@ t_opaque_from_records(RecMap) -> end, OpaqueRecMap), [OpaqueType || {_Key, OpaqueType} <- maps:to_list(OpaqueTypeMap)]. -%% Decompose opaque instances of type arg2 to structured types, in arg1 -%% XXX: Same as t_unopaque --spec t_struct_from_opaque(erl_type(), [erl_type()]) -> erl_type(). - -t_struct_from_opaque(?function(Domain, Range), Opaques) -> - ?function(t_struct_from_opaque(Domain, Opaques), - t_struct_from_opaque(Range, Opaques)); -t_struct_from_opaque(?list(Types, Term, Size), Opaques) -> - ?list(t_struct_from_opaque(Types, Opaques), - t_struct_from_opaque(Term, Opaques), Size); -t_struct_from_opaque(?opaque(_) = T, Opaques) -> - case is_opaque_type(T, Opaques) of - true -> t_opaque_structure(T); - false -> T - end; -t_struct_from_opaque(?product(Types), Opaques) -> - ?product(list_struct_from_opaque(Types, Opaques)); -t_struct_from_opaque(?tuple(?any, _, _) = T, _Opaques) -> T; -t_struct_from_opaque(?tuple(Types, Arity, Tag), Opaques) -> - ?tuple(list_struct_from_opaque(Types, Opaques), Arity, Tag); -t_struct_from_opaque(?tuple_set(Set), Opaques) -> - NewSet = [{Sz, [t_struct_from_opaque(T, Opaques) || T <- Tuples]} - || {Sz, Tuples} <- Set], - ?tuple_set(NewSet); -t_struct_from_opaque(?union(List), Opaques) -> - t_sup(list_struct_from_opaque(List, Opaques)); -t_struct_from_opaque(Type, _Opaques) -> Type. - -list_struct_from_opaque(Types, Opaques) -> - [t_struct_from_opaque(Type, Opaques) || Type <- Types]. - %%----------------------------------------------------------------------------- %% Unit type. Signals non termination. %% @@ -825,11 +795,15 @@ t_unit() -> t_is_unit(?unit) -> true; t_is_unit(_) -> false. +-spec t_is_impossible(erl_type()) -> boolean(). + +t_is_impossible(?none) -> true; +t_is_impossible(?unit) -> true; +t_is_impossible(_) -> false. + -spec t_is_none_or_unit(erl_type()) -> boolean(). -t_is_none_or_unit(?none) -> true; -t_is_none_or_unit(?unit) -> true; -t_is_none_or_unit(_) -> false. +t_is_none_or_unit(T) -> t_is_impossible(T). %%----------------------------------------------------------------------------- %% Atoms and the derived type boolean @@ -1710,7 +1684,7 @@ t_map(L) -> t_map(Pairs0, DefK0, DefV0) -> DefK1 = lists:foldl(fun({K,_,_},Acc)->t_subtract(Acc,K)end, DefK0, Pairs0), {DefK2, DefV1} = - case t_is_none_or_unit(DefK1) orelse t_is_none_or_unit(DefV0) of + case t_is_impossible(DefK1) orelse t_is_impossible(DefV0) of true -> {?none, ?none}; false -> {DefK1, DefV0} end, @@ -1971,7 +1945,7 @@ t_map_put(KV, Map, Opaques) -> map_put(_, ?none, _) -> ?none; map_put(_, ?unit, _) -> ?none; map_put({Key, Value}, ?map(Pairs,DefK,DefV), Opaques) -> - case t_is_none_or_unit(Key) orelse t_is_none_or_unit(Value) of + case t_is_impossible(Key) orelse t_is_impossible(Value) of true -> ?none; false -> case is_singleton_type(Key) of @@ -2359,7 +2333,6 @@ t_has_var(?map(_, DefK, _)= Map) -> t_has_var_list(map_all_values(Map)) orelse t_has_var(DefK); t_has_var(?opaque(Set)) -> - %% Assume variables in 'args' are also present i 'struct' t_has_var_list([O#opaque.struct || O <- set_to_list(Set)]); t_has_var(?union(List)) -> t_has_var_list(List); @@ -2371,45 +2344,42 @@ t_has_var_list([T|Ts]) -> t_has_var(T) orelse t_has_var_list(Ts); t_has_var_list([]) -> false. --spec t_collect_vars(erl_type()) -> [erl_type()]. - -t_collect_vars(T) -> - Vs = t_collect_vars(T, maps:new()), - [V || {V, _} <- maps:to_list(Vs)]. +-spec t_collect_var_names(erl_type()) -> any(). --type ctab() :: #{erl_type() => 'any'}. +t_collect_var_names(T) -> + t_collect_var_names(T, []). --spec t_collect_vars(erl_type(), ctab()) -> ctab(). +-spec t_collect_var_names(erl_type(), ordsets:ordset(term())) -> + ordsets:ordset(term()). -t_collect_vars(?var(_) = Var, Acc) -> - maps:put(Var, any, Acc); -t_collect_vars(?function(Domain, Range), Acc) -> - Acc1 = t_collect_vars(Domain, Acc), - t_collect_vars(Range, Acc1); -t_collect_vars(?list(Contents, Termination, _), Acc) -> - Acc1 = t_collect_vars(Contents, Acc), - t_collect_vars(Termination, Acc1); -t_collect_vars(?product(Types), Acc) -> +t_collect_var_names(?var(Id), Acc) -> + ordsets:add_element(Id, Acc); +t_collect_var_names(?function(Domain, Range), Acc) -> + Acc1 = t_collect_var_names(Domain, Acc), + t_collect_var_names(Range, Acc1); +t_collect_var_names(?list(Contents, Termination, _), Acc) -> + Acc1 = t_collect_var_names(Contents, Acc), + t_collect_var_names(Termination, Acc1); +t_collect_var_names(?product(Types), Acc) -> t_collect_vars_list(Types, Acc); -t_collect_vars(?tuple(?any, ?any, ?any), Acc) -> +t_collect_var_names(?tuple(?any, ?any, ?any), Acc) -> Acc; -t_collect_vars(?tuple(Types, _, _), Acc) -> +t_collect_var_names(?tuple(Types, _, _), Acc) -> t_collect_vars_list(Types, Acc); -t_collect_vars(?tuple_set(_) = TS, Acc) -> +t_collect_var_names(?tuple_set(_) = TS, Acc) -> t_collect_vars_list(t_tuple_subtypes(TS), Acc); -t_collect_vars(?map(_, DefK, _) = Map, Acc0) -> +t_collect_var_names(?map(_, DefK, _) = Map, Acc0) -> Acc = t_collect_vars_list(map_all_values(Map), Acc0), - t_collect_vars(DefK, Acc); -t_collect_vars(?opaque(Set), Acc) -> - %% Assume variables in 'args' are also present i 'struct' + t_collect_var_names(DefK, Acc); +t_collect_var_names(?opaque(Set), Acc) -> t_collect_vars_list([O#opaque.struct || O <- set_to_list(Set)], Acc); -t_collect_vars(?union(List), Acc) -> +t_collect_var_names(?union(List), Acc) -> t_collect_vars_list(List, Acc); -t_collect_vars(_, Acc) -> +t_collect_var_names(_, Acc) -> Acc. t_collect_vars_list([T|Ts], Acc0) -> - Acc = t_collect_vars(T, Acc0), + Acc = t_collect_var_names(T, Acc0), t_collect_vars_list(Ts, Acc); t_collect_vars_list([], Acc) -> Acc. @@ -2630,7 +2600,7 @@ t_sup(Ts) -> t_sup1([H1, H2|T], L) -> t_sup1(T, [t_sup(H1, H2)|L]); -t_sup1([T], []) -> subst_all_vars_to_any(T); +t_sup1([T], []) -> do_not_subst_all_vars_to_any(T); t_sup1(Ts, L) -> t_sup1(Ts++L, []). @@ -2642,7 +2612,7 @@ t_sup(?none, T) -> T; t_sup(T, ?none) -> T; t_sup(?unit, T) -> T; t_sup(T, ?unit) -> T; -t_sup(T, T) -> subst_all_vars_to_any(T); +t_sup(T, T) -> do_not_subst_all_vars_to_any(T); t_sup(?var(_), _) -> ?any; t_sup(_, ?var(_)) -> ?any; t_sup(?atom(Set1), ?atom(Set2)) -> @@ -2716,9 +2686,9 @@ t_sup(?tuple(?any, ?any, ?any) = T, ?tuple_set(_)) -> T; t_sup(?tuple_set(_), ?tuple(?any, ?any, ?any) = T) -> T; t_sup(?tuple(Elements1, Arity, Tag1) = T1, ?tuple(Elements2, Arity, Tag2) = T2) -> - if Tag1 =:= Tag2 -> t_tuple(t_sup_lists(Elements1, Elements2)); - Tag1 =:= ?any -> t_tuple(t_sup_lists(Elements1, Elements2)); - Tag2 =:= ?any -> t_tuple(t_sup_lists(Elements1, Elements2)); + if Tag1 == Tag2 -> t_tuple(t_sup_lists(Elements1, Elements2)); + Tag1 == ?any -> t_tuple(t_sup_lists(Elements1, Elements2)); + Tag2 == ?any -> t_tuple(t_sup_lists(Elements1, Elements2)); Tag1 < Tag2 -> ?tuple_set([{Arity, [T1, T2]}]); Tag1 > Tag2 -> ?tuple_set([{Arity, [T2, T1]}]) end; @@ -2748,8 +2718,8 @@ sup_opaque(List) -> ?opaque(ordsets:from_list(L)). sup_opaq(L0) -> - L1 = [{{Mod,Name,Args}, T} || - #opaque{mod = Mod, name = Name, args = Args}=T <- L0], + L1 = [{{Mod,Name,Arity}, T} || + #opaque{mod = Mod, name = Name, arity = Arity}=T <- L0], F = dialyzer_utils:family(L1), [supl(Ts) || {_, Ts} <- F]. @@ -2824,9 +2794,10 @@ sup_tuples_in_set([?tuple(Elements1, Arity, Tag1) = T1|Left1] = L1, if Tag1 < Tag2 -> sup_tuples_in_set(Left1, L2, [T1|Acc]); Tag1 > Tag2 -> sup_tuples_in_set(L1, Left2, [T2|Acc]); - Tag2 =:= Tag2 -> NewElements = t_sup_lists(Elements1, Elements2), - NewAcc = [?tuple(NewElements, Arity, Tag1)|Acc], - sup_tuples_in_set(Left1, Left2, NewAcc) + Tag1 == Tag2 -> + NewElements = t_sup_lists(Elements1, Elements2), + NewAcc = [?tuple(NewElements, Arity, Tag1)|Acc], + sup_tuples_in_set(Left1, Left2, NewAcc) end; sup_tuples_in_set([], L2, Acc) -> lists:reverse(Acc, L2); sup_tuples_in_set(L1, [], Acc) -> lists:reverse(Acc, L1). @@ -2944,15 +2915,15 @@ t_inf(T1, T2) -> -spec t_inf(erl_type(), erl_type(), t_inf_opaques()) -> erl_type(). t_inf(?var(_), ?var(_), _Opaques) -> ?any; -t_inf(?var(_), T, _Opaques) -> subst_all_vars_to_any(T); -t_inf(T, ?var(_), _Opaques) -> subst_all_vars_to_any(T); -t_inf(?any, T, _Opaques) -> subst_all_vars_to_any(T); -t_inf(T, ?any, _Opaques) -> subst_all_vars_to_any(T); +t_inf(?var(_), T, _Opaques) -> do_not_subst_all_vars_to_any(T); +t_inf(T, ?var(_), _Opaques) -> do_not_subst_all_vars_to_any(T); +t_inf(?any, T, _Opaques) -> do_not_subst_all_vars_to_any(T); +t_inf(T, ?any, _Opaques) -> do_not_subst_all_vars_to_any(T); t_inf(?none, _, _Opaques) -> ?none; t_inf(_, ?none, _Opaques) -> ?none; t_inf(?unit, _, _Opaques) -> ?unit; % ?unit cases should appear below ?none t_inf(_, ?unit, _Opaques) -> ?unit; -t_inf(T, T, _Opaques) -> subst_all_vars_to_any(T); +t_inf(T, T, _Opaques) -> do_not_subst_all_vars_to_any(T); t_inf(?atom(Set1), ?atom(Set2), _) -> case set_intersection(Set1, Set2) of ?none -> ?none; @@ -2988,7 +2959,7 @@ t_inf(?map(_, ADefK, ADefV) = A, ?map(_, BDefK, BDefV) = B, _Opaques) -> %% result in a none result. Pairs = map_pairwise_merge( - %% For optional keys in both maps, when the infinimum is none, we have + %% For optional keys in both maps, when the infimum is none, we have %% essentially concluded that K must not be a key in the map. fun(K, ?opt, V1, ?opt, V2) -> {K, ?opt, t_inf(V1, V2)}; %% When a key is optional in one map, but mandatory in another, it @@ -3072,13 +3043,13 @@ t_inf(?product(_), _, _Opaques) -> t_inf(_, ?product(_), _Opaques) -> ?none; t_inf(?tuple(?any, ?any, ?any), ?tuple(_, _, _) = T, _Opaques) -> - subst_all_vars_to_any(T); + do_not_subst_all_vars_to_any(T); t_inf(?tuple(_, _, _) = T, ?tuple(?any, ?any, ?any), _Opaques) -> - subst_all_vars_to_any(T); + do_not_subst_all_vars_to_any(T); t_inf(?tuple(?any, ?any, ?any), ?tuple_set(_) = T, _Opaques) -> - subst_all_vars_to_any(T); + do_not_subst_all_vars_to_any(T); t_inf(?tuple_set(_) = T, ?tuple(?any, ?any, ?any), _Opaques) -> - subst_all_vars_to_any(T); + do_not_subst_all_vars_to_any(T); t_inf(?tuple(Elements1, Arity, _Tag1), ?tuple(Elements2, Arity, _Tag2), Opaques) -> case t_inf_lists_strict(Elements1, Elements2, Opaques) of bottom -> ?none; @@ -3196,121 +3167,13 @@ compatible_opaque_types(?opaque(Es1), ?opaque(Es2)) -> [{O1, O2} || O1 <- Es1, O2 <- Es2, is_compat_opaque_names(O1, O2)]. is_compat_opaque_names(Opaque1, Opaque2) -> - #opaque{mod = Mod1, name = Name1, args = Args1} = Opaque1, - #opaque{mod = Mod2, name = Name2, args = Args2} = Opaque2, - case {{Mod1, Name1, Args1}, {Mod2, Name2, Args2}} of - {ModNameArgs, ModNameArgs} -> true; - {{Mod, Name, Args1}, {Mod, Name, Args2}} -> - is_compat_args(Args1, Args2); + #opaque{mod = Mod1, name = Name1, arity = Arity1} = Opaque1, + #opaque{mod = Mod2, name = Name2, arity = Arity2} = Opaque2, + case {{Mod1, Name1, Arity1}, {Mod2, Name2, Arity2}} of + {ModNameArity, ModNameArity} -> true; _ -> false end. -is_compat_args([A1|Args1], [A2|Args2]) -> - is_compat_arg(A1, A2) andalso is_compat_args(Args1, Args2); -is_compat_args([], []) -> true; -is_compat_args(_, _) -> false. - --spec is_compat_arg(erl_type(), erl_type()) -> boolean(). - -%% The intention is that 'true' is to be returned iff one of the -%% arguments is a specialization of the other argument in the sense -%% that every type is a specialization of any(). For example, {_,_} is -%% a specialization of any(), but not of tuple(). Does not handle -%% variables, but any() and unions (sort of). However, the -%% implementation is more relaxed as any() is compatible to anything. - -is_compat_arg(T, T) -> true; -is_compat_arg(_, ?any) -> true; -is_compat_arg(?any, _) -> true; -is_compat_arg(?function(Domain1, Range1), ?function(Domain2, Range2)) -> - (is_compat_arg(Domain1, Domain2) andalso - is_compat_arg(Range1, Range2)); -is_compat_arg(?list(Contents1, Termination1, Size1), - ?list(Contents2, Termination2, Size2)) -> - (Size1 =:= Size2 andalso - is_compat_arg(Contents1, Contents2) andalso - is_compat_arg(Termination1, Termination2)); -is_compat_arg(?product(Types1), ?product(Types2)) -> - is_compat_list(Types1, Types2); -is_compat_arg(?map(Pairs1, DefK1, DefV1), ?map(Pairs2, DefK2, DefV2)) -> - {Ks1, _, Vs1} = lists:unzip3(Pairs1), - {Ks2, _, Vs2} = lists:unzip3(Pairs2), - Key1 = t_sup([DefK1 | Ks1]), - Key2 = t_sup([DefK2 | Ks2]), - case is_compat_arg(Key1, Key2) of - true -> - Value1 = t_sup([DefV1 | Vs1]), - Value2 = t_sup([DefV2 | Vs2]), - is_compat_arg(Value1, Value2); - false -> - false - end; -is_compat_arg(?tuple(?any, ?any, ?any), ?tuple(_, _, _)) -> false; -is_compat_arg(?tuple(_, _, _), ?tuple(?any, ?any, ?any)) -> false; -is_compat_arg(?tuple(Elements1, Arity, _), - ?tuple(Elements2, Arity, _)) when Arity =/= ?any -> - is_compat_list(Elements1, Elements2); -is_compat_arg(?tuple_set([{Arity, List}]), - ?tuple(Elements2, Arity, _)) when Arity =/= ?any -> - is_compat_list(sup_tuple_elements(List), Elements2); -is_compat_arg(?tuple(Elements1, Arity, _), - ?tuple_set([{Arity, List}])) when Arity =/= ?any -> - is_compat_list(Elements1, sup_tuple_elements(List)); -is_compat_arg(?tuple_set(List1), ?tuple_set(List2)) -> - try - is_compat_list_list([sup_tuple_elements(T) || {_Arity, T} <- List1], - [sup_tuple_elements(T) || {_Arity, T} <- List2]) - catch _:_ -> false - end; -is_compat_arg(?opaque(_) = T1, T2) -> - is_compat_arg(t_opaque_structure(T1), T2); -is_compat_arg(T1, ?opaque(_) = T2) -> - is_compat_arg(T1, t_opaque_structure(T2)); -is_compat_arg(?union(List1)=T1, ?union(List2)=T2) -> - case is_compat_union2(T1, T2) of - {yes, Type1, Type2} -> is_compat_arg(Type1, Type2); - no -> is_compat_list(List1, List2) - end; -is_compat_arg(?union(List), T2) -> - case unify_union(List) of - {yes, Type} -> is_compat_arg(Type, T2); - no -> false - end; -is_compat_arg(T1, ?union(List)) -> - case unify_union(List) of - {yes, Type} -> is_compat_arg(T1, Type); - no -> false - end; -is_compat_arg(?var(_), _) -> exit(error); -is_compat_arg(_, ?var(_)) -> exit(error); -is_compat_arg(?none, _) -> false; -is_compat_arg(_, ?none) -> false; -is_compat_arg(?unit, _) -> false; -is_compat_arg(_, ?unit) -> false; -is_compat_arg(#c{}, #c{}) -> false. - -is_compat_list_list(LL1, LL2) -> - length(LL1) =:= length(LL2) andalso is_compat_list_list1(LL1, LL2). - -is_compat_list_list1([], []) -> true; -is_compat_list_list1([L1|LL1], [L2|LL2]) -> - is_compat_list(L1, L2) andalso is_compat_list_list1(LL1, LL2). - -is_compat_list(L1, L2) -> - length(L1) =:= length(L2) andalso is_compat_list1(L1, L2). - -is_compat_list1([], []) -> true; -is_compat_list1([T1|L1], [T2|L2]) -> - is_compat_arg(T1, T2) andalso is_compat_list1(L1, L2). - -is_compat_union2(?union(List1)=T1, ?union(List2)=T2) -> - case {unify_union(List1), unify_union(List2)} of - {{yes, Type1}, {yes, Type2}} -> {yes, Type1, Type2}; - {{yes, Type1}, no} -> {yes, Type1, T2}; - {no, {yes, Type2}} -> {yes, T1, Type2}; - {no, no} -> no - end. - -spec t_inf_lists([erl_type()], [erl_type()]) -> [erl_type()]. t_inf_lists(L1, L2) -> @@ -3459,11 +3322,20 @@ findfirst(N1, N2, U1, B1, U2, B2) -> if Val1 =:= Val2 -> Val1; Val1 > Val2 -> - findfirst(N1, N2+1, U1, B1, U2, B2); + N2_1 = N2 + max((Val1 - Val2) div U2, 1), + findfirst(N1, N2_1, U1, B1, U2, B2); Val1 < Val2 -> - findfirst(N1+1, N2, U1, B1, U2, B2) + N1_1 = N1 + max((Val2 - Val1) div U1, 1), + findfirst(N1_1, N2, U1, B1, U2, B2) end. +%% Optimization. Before Erlang/OTP 25, subst_all_vars_to_any() was +%% called. It turned out that variables are not to be substituted for +%% any() since either there are no variables, or variables are +%% substituted for any() afterwards. +do_not_subst_all_vars_to_any(T) -> + T. + %%----------------------------------------------------------------------------- %% Substitution of variables %% @@ -3516,9 +3388,8 @@ t_subst_aux(?map(Pairs, DefK, DefV), Map) -> t_map([{K, MNess, t_subst_aux(V, Map)} || {K, MNess, V} <- Pairs], t_subst_aux(DefK, Map), t_subst_aux(DefV, Map)); t_subst_aux(?opaque(Es), Map) -> - List = [Opaque#opaque{args = [t_subst_aux(Arg, Map) || Arg <- Args], - struct = t_subst_aux(S, Map)} || - Opaque = #opaque{args = Args, struct = S} <- set_to_list(Es)], + List = [Opaque#opaque{struct = t_subst_aux(S, Map)} || + Opaque = #opaque{struct = S} <- set_to_list(Es)], ?opaque(ordsets:from_list(List)); t_subst_aux(?union(List), Map) -> ?union([t_subst_aux(E, Map) || E <- List]); @@ -3529,106 +3400,116 @@ t_subst_aux(T, _Map) -> %% Unification %% --type t_unify_ret() :: {erl_type(), [{_, erl_type()}]}. - --spec t_unify(erl_type(), erl_type()) -> t_unify_ret(). - -t_unify(T1, T2) -> - {T, VarMap} = t_unify(T1, T2, #{}), - {t_subst(T, VarMap), lists:keysort(1, maps:to_list(VarMap))}. - -t_unify(?var(Id) = T, ?var(Id), VarMap) -> - {T, VarMap}; -t_unify(?var(Id1) = T, ?var(Id2), VarMap) -> - case maps:find(Id1, VarMap) of - error -> - case maps:find(Id2, VarMap) of - error -> {T, VarMap#{Id2 => T}}; - {ok, Type} -> t_unify(T, Type, VarMap) - end; - {ok, Type1} -> - case maps:find(Id2, VarMap) of - error -> {Type1, VarMap#{Id2 => T}}; - {ok, Type2} -> t_unify(Type1, Type2, VarMap) - end +-spec t_unify_table_only(erl_type(), erl_type()) -> var_table(). + +%% A simplified version of t_unify/2 which returns the variable +%% bindings only. It is faster, mostly because t_subst() is not +%% called. + +t_unify_table_only(T1, T2) -> + t_unify_table_only(T1, T2, #{}). + +t_unify_table_only(?var(Id), ?var(Id), VarMap) -> + VarMap; +t_unify_table_only(?var(Id1) = LHS, ?var(Id2) = RHS, VarMap) -> + case VarMap of + #{ Id1 := Type1, Id2 := Type2} -> + t_unify_table_only(Type1, Type2, VarMap); + #{ Id1 := Type } -> + t_unify_table_only(Type, RHS, VarMap); + #{ Id2 := Type } -> + t_unify_table_only(LHS, Type, VarMap); + #{} -> + VarMap#{ Id1 => LHS, Id2 => RHS } end; -t_unify(?var(Id), Type, VarMap) -> +t_unify_table_only(?var(Id), Type, VarMap) -> case maps:find(Id, VarMap) of - error -> {Type, VarMap#{Id => Type}}; - {ok, VarType} -> t_unify(VarType, Type, VarMap) + error -> VarMap#{Id => Type}; + {ok, VarType} -> t_unify_table_only(VarType, Type, VarMap) end; -t_unify(Type, ?var(Id), VarMap) -> +t_unify_table_only(Type, ?var(Id), VarMap) -> case maps:find(Id, VarMap) of - error -> {Type, VarMap#{Id => Type}}; - {ok, VarType} -> t_unify(VarType, Type, VarMap) + error -> VarMap#{Id => Type}; + {ok, VarType} -> t_unify_table_only(VarType, Type, VarMap) end; -t_unify(?function(Domain1, Range1), ?function(Domain2, Range2), VarMap) -> - {Domain, VarMap1} = t_unify(Domain1, Domain2, VarMap), - {Range, VarMap2} = t_unify(Range1, Range2, VarMap1), - {?function(Domain, Range), VarMap2}; -t_unify(?list(Contents1, Termination1, Size), +t_unify_table_only(?function(Domain1, Range1), ?function(Domain2, Range2), VarMap) -> + VarMap1 = t_unify_table_only(Domain1, Domain2, VarMap), + t_unify_table_only(Range1, Range2, VarMap1); +t_unify_table_only(?list(Contents1, Termination1, Size), ?list(Contents2, Termination2, Size), VarMap) -> - {Contents, VarMap1} = t_unify(Contents1, Contents2, VarMap), - {Termination, VarMap2} = t_unify(Termination1, Termination2, VarMap1), - {?list(Contents, Termination, Size), VarMap2}; -t_unify(?product(Types1), ?product(Types2), VarMap) -> - {Types, VarMap1} = unify_lists(Types1, Types2, VarMap), - {?product(Types), VarMap1}; -t_unify(?tuple(?any, ?any, ?any) = T, ?tuple(?any, ?any, ?any), VarMap) -> - {T, VarMap}; -t_unify(?tuple(Elements1, Arity, _), + VarMap1 = t_unify_table_only(Contents1, Contents2, VarMap), + t_unify_table_only(Termination1, Termination2, VarMap1); +t_unify_table_only(?product(Types1), ?product(Types2), VarMap) -> + unify_lists_table_only(Types1, Types2, VarMap); +t_unify_table_only(?tuple(?any, ?any, ?any), ?tuple(?any, ?any, ?any), VarMap) -> + VarMap; +t_unify_table_only(?tuple(Elements1, Arity, _), ?tuple(Elements2, Arity, _), VarMap) when Arity =/= ?any -> - {NewElements, VarMap1} = unify_lists(Elements1, Elements2, VarMap), - {t_tuple(NewElements), VarMap1}; -t_unify(?tuple_set([{Arity, _}]) = T1, + unify_lists_table_only(Elements1, Elements2, VarMap); +t_unify_table_only(?tuple_set([{Arity, _}]) = T1, ?tuple(_, Arity, _) = T2, VarMap) when Arity =/= ?any -> - unify_tuple_set_and_tuple1(T1, T2, VarMap); -t_unify(?tuple(_, Arity, _) = T1, + unify_tuple_set_and_tuple1_table_only(T1, T2, VarMap); +t_unify_table_only(?tuple(_, Arity, _) = T1, ?tuple_set([{Arity, _}]) = T2, VarMap) when Arity =/= ?any -> - unify_tuple_set_and_tuple2(T1, T2, VarMap); -t_unify(?tuple_set(List1) = T1, ?tuple_set(List2) = T2, VarMap) -> + unify_tuple_set_and_tuple2_table_only(T1, T2, VarMap); +t_unify_table_only(?tuple_set(List1) = T1, ?tuple_set(List2) = T2, VarMap) -> try - unify_lists(lists:append([T || {_Arity, T} <- List1]), - lists:append([T || {_Arity, T} <- List2]), VarMap) - of - {Tuples, NewVarMap} -> {t_sup(Tuples), NewVarMap} + unify_lists_table_only(lists:append([T || {_Arity, T} <- List1]), + lists:append([T || {_Arity, T} <- List2]), VarMap) catch _:_ -> throw({mismatch, T1, T2}) end; -t_unify(?map(_, ADefK, ADefV) = A, ?map(_, BDefK, BDefV) = B, VarMap0) -> - {DefK, VarMap1} = t_unify(ADefK, BDefK, VarMap0), - {DefV, VarMap2} = t_unify(ADefV, BDefV, VarMap1), - {Pairs, VarMap} = +t_unify_table_only(?map(_, ADefK, ADefV) = A, ?map(_, BDefK, BDefV) = B, VarMap0) -> + VarMap1 = t_unify_table_only(ADefK, BDefK, VarMap0), + VarMap2 = t_unify_table_only(ADefV, BDefV, VarMap1), + {[], VarMap} = map_pairwise_merge_foldr( - fun(K, MNess, V1, MNess, V2, {Pairs0, VarMap3}) -> + fun(_K, MNess, V1, MNess, V2, {Pairs0, VarMap3}) -> %% We know that the keys unify and do not contain variables, or they %% would not be singletons %% TODO: Should V=?none (known missing keys) be handled special? - {V, VarMap4} = t_unify(V1, V2, VarMap3), - {[{K,MNess,V}|Pairs0], VarMap4}; - (K, _, V1, _, V2, {Pairs0, VarMap3}) -> + VarMap4 = t_unify_table_only(V1, V2, VarMap3), + {Pairs0, VarMap4}; + (_K, _, V1, _, V2, {Pairs0, VarMap3}) -> %% One mandatory and one optional; what should be done in this case? - {V, VarMap4} = t_unify(V1, V2, VarMap3), - {[{K,?mand,V}|Pairs0], VarMap4} + VarMap4 = t_unify_table_only(V1, V2, VarMap3), + {Pairs0, VarMap4} end, {[], VarMap2}, A, B), - {t_map(Pairs, DefK, DefV), VarMap}; -t_unify(?opaque(_) = T1, ?opaque(_) = T2, VarMap) -> - t_unify(t_opaque_structure(T1), t_opaque_structure(T2), VarMap); -t_unify(T1, ?opaque(_) = T2, VarMap) -> - t_unify(T1, t_opaque_structure(T2), VarMap); -t_unify(?opaque(_) = T1, T2, VarMap) -> - t_unify(t_opaque_structure(T1), T2, VarMap); -t_unify(T, T, VarMap) -> - {T, VarMap}; -t_unify(?union(_)=T1, ?union(_)=T2, VarMap) -> + VarMap; +t_unify_table_only(?opaque(_) = T1, ?opaque(_) = T2, VarMap) -> + t_unify_table_only(t_opaque_structure(T1), t_opaque_structure(T2), VarMap); +t_unify_table_only(T1, ?opaque(_) = T2, VarMap) -> + t_unify_table_only(T1, t_opaque_structure(T2), VarMap); +t_unify_table_only(?opaque(_) = T1, T2, VarMap) -> + t_unify_table_only(t_opaque_structure(T1), T2, VarMap); +t_unify_table_only(T, T, VarMap) -> + VarMap; +t_unify_table_only(?union(_)=T1, ?union(_)=T2, VarMap) -> {Type1, Type2} = unify_union2(T1, T2), - t_unify(Type1, Type2, VarMap); -t_unify(?union(_)=T1, T2, VarMap) -> - t_unify(unify_union1(T1, T1, T2), T2, VarMap); -t_unify(T1, ?union(_)=T2, VarMap) -> - t_unify(T1, unify_union1(T2, T1, T2), VarMap); -t_unify(T1, T2, _) -> + t_unify_table_only(Type1, Type2, VarMap); +t_unify_table_only(?union(_)=T1, T2, VarMap) -> + t_unify_table_only(unify_union1(T1, T1, T2), T2, VarMap); +t_unify_table_only(T1, ?union(_)=T2, VarMap) -> + t_unify_table_only(T1, unify_union1(T2, T1, T2), VarMap); +t_unify_table_only(T1, T2, _) -> throw({mismatch, T1, T2}). +%% Two functions since t_unify_table_only is not symmetric. +unify_tuple_set_and_tuple1_table_only(?tuple_set([{Arity, List}]), + ?tuple(Elements2, Arity, _), VarMap) -> + %% Can only work if the single tuple has variables at correct places. + unify_lists_table_only(sup_tuple_elements(List), Elements2, VarMap). + +unify_tuple_set_and_tuple2_table_only(?tuple(Elements2, Arity, _), + ?tuple_set([{Arity, List}]), VarMap) -> + %% Can only work if the single tuple has variables at correct places. + unify_lists_table_only(Elements2, sup_tuple_elements(List), VarMap). + +unify_lists_table_only([T1|Left1], [T2|Left2], VarMap) -> + NewVarMap = t_unify_table_only(T1, T2, VarMap), + unify_lists_table_only(Left1, Left2, NewVarMap); +unify_lists_table_only([], [], VarMap) -> + VarMap. + unify_union2(?union(List1)=T1, ?union(List2)=T2) -> case {unify_union(List1), unify_union(List2)} of {{yes, Type1}, {yes, Type2}} -> {Type1, Type2}; @@ -3659,46 +3540,20 @@ unify_union(List) -> is_opaque_type(?opaque(Elements), Opaques) -> lists:any(fun(Opaque) -> is_opaque_type2(Opaque, Opaques) end, Elements). -is_opaque_type2(#opaque{mod = Mod1, name = Name1, args = Args1}, Opaques) -> +is_opaque_type2(#opaque{mod = Mod1, name = Name1, arity = Arity1}, Opaques) -> F1 = fun(?opaque(Es)) -> - F2 = fun(#opaque{mod = Mod, name = Name, args = Args}) -> - is_type_name(Mod1, Name1, Args1, Mod, Name, Args) + F2 = fun(#opaque{mod = Mod, name = Name, arity = Arity}) -> + is_type_name(Mod1, Name1, Arity1, Mod, Name, Arity) end, lists:any(F2, Es) end, lists:any(F1, Opaques). -is_type_name(Mod, Name, Args1, Mod, Name, Args2) -> - length(Args1) =:= length(Args2); -is_type_name(_Mod1, _Name1, _Args1, _Mod2, _Name2, _Args2) -> +is_type_name(Mod, Name, Arity, Mod, Name, Arity) -> + true; +is_type_name(_Mod1, _Name1, _Arity1, _Mod2, _Name2, _Arity2) -> false. -%% Two functions since t_unify is not symmetric. -unify_tuple_set_and_tuple1(?tuple_set([{Arity, List}]), - ?tuple(Elements2, Arity, _), VarMap) -> - %% Can only work if the single tuple has variables at correct places. - %% Collapse the tuple set. - {NewElements, VarMap1} = - unify_lists(sup_tuple_elements(List), Elements2, VarMap), - {t_tuple(NewElements), VarMap1}. - -unify_tuple_set_and_tuple2(?tuple(Elements2, Arity, _), - ?tuple_set([{Arity, List}]), VarMap) -> - %% Can only work if the single tuple has variables at correct places. - %% Collapse the tuple set. - {NewElements, VarMap1} = - unify_lists(Elements2, sup_tuple_elements(List), VarMap), - {t_tuple(NewElements), VarMap1}. - -unify_lists(L1, L2, VarMap) -> - unify_lists(L1, L2, VarMap, []). - -unify_lists([T1|Left1], [T2|Left2], VarMap, Acc) -> - {NewT, NewVarMap} = t_unify(T1, T2, VarMap), - unify_lists(Left1, Left2, NewVarMap, [NewT|Acc]); -unify_lists([], [], VarMap, Acc) -> - {lists:reverse(Acc), VarMap}. - %%t_assign_variables_to_subtype(T1, T2) -> %% try %% Dict = assign_vars(T1, T2, dict:new()), @@ -3750,7 +3605,7 @@ unify_lists([], [], VarMap, Acc) -> %% assign_vars_lists(Elements1, Elements2, Dict); %%assign_vars(?tuple_set(_) = T, ?tuple_set(List2), Dict) -> %% %% All Rhs tuples must already be subtypes of Lhs, so we can take -%% %% each one separatly. +%% %% each one separately. %% assign_vars_lists([T || _ <- List2], List2, Dict); %%assign_vars(?tuple(?any, ?any, ?any), ?tuple_set(_), Dict) -> %% Dict; @@ -3981,7 +3836,7 @@ t_subtract(?map(APairs, ADefK, ADefV) = A, ?map(_, BDefK, BDefV) = B) -> %% * The arguments constrain A at least as much as B, i.e. that A so far %% is a subtype of B. In that case they return false %% * That for the particular arguments, A being a subtype of B does not - %% hold, but the infinimum of A and B is nonempty, and by narrowing a + %% hold, but the infimum of A and B is nonempty, and by narrowing a %% pair in A, we can create a type that excludes some elements in the %% infinumum. In that case, they will return that pair. %% * That for the particular arguments, A being a subtype of B does not @@ -4144,7 +3999,7 @@ t_is_instance(ConcreteType, Type) -> -spec t_do_overlap(erl_type(), erl_type()) -> boolean(). t_do_overlap(TypeA, TypeB) -> - not (t_is_none_or_unit(t_inf(TypeA, TypeB))). + not (t_is_impossible(t_inf(TypeA, TypeB))). -spec t_unopaque(erl_type()) -> erl_type(). @@ -4200,7 +4055,44 @@ t_unopaque(T, _) -> -spec t_limit(erl_type(), integer()) -> erl_type(). t_limit(Term, K) when is_integer(K) -> - t_limit_k(Term, K). + case is_limited(Term, K) of + true -> Term; + false -> t_limit_k(Term, K) + end. + +is_limited(?any, _) -> true; +is_limited(_, K) when K =< 0 -> false; +is_limited(?tuple(?any, ?any, ?any), _K) -> true; +is_limited(?tuple(Elements, _Arity, _), K) -> + if K =:= 1 -> false; + true -> + K1 = K-1, + lists:all(fun(E) -> is_limited(E, K1) end, Elements) + end; +is_limited(?tuple_set(_) = T, K) -> + lists:all(fun(Tuple) -> is_limited(Tuple, K) end, t_tuple_subtypes(T)); +is_limited(?list(Elements, Termination, _Size), K) -> + if K =:= 1 -> is_limited(Termination, K); + true -> is_limited(Termination, K - 1) + end + andalso is_limited(Elements, K - 1); +is_limited(?function(Domain, Range), K) -> + is_limited(Domain, K) andalso is_limited(Range, K-1); +is_limited(?product(Elements), K) -> + K1 = K-1, + lists:all(fun(X) -> is_limited(X, K1) end, Elements); +is_limited(?union(Elements), K) -> + lists:all(fun(X) -> is_limited(X, K) end, Elements); +is_limited(?opaque(Es), K) -> + lists:all(fun(#opaque{struct = S}) -> is_limited(S, K) end, set_to_list(Es)); +is_limited(?map(Pairs, DefK, DefV), K) -> + %% Use the fact that t_sup() does not increase the depth. + K1 = K - 1, + lists:all(fun({Key, _, Value}) -> + is_limited(Key, K1) andalso is_limited(Value, K1) + end, Pairs) + andalso is_limited(DefK, K1) andalso is_limited(DefV, K1); +is_limited(_, _K) -> true. t_limit_k(_, K) when K =< 0 -> ?any; t_limit_k(?tuple(?any, ?any, ?any) = T, _K) -> T; @@ -4350,8 +4242,8 @@ t_to_string(?identifier(Set), _RecDict) -> flat_join([flat_format("~w()", [T]) || T <- set_to_list(Set)], " | ") end; t_to_string(?opaque(Set), RecDict) -> - flat_join([opaque_type(Mod, Name, Args, S, RecDict) || - #opaque{mod = Mod, name = Name, struct = S, args = Args} + flat_join([opaque_type(Mod, Name, Arity, S, RecDict) || + #opaque{mod = Mod, name = Name, struct = S, arity = Arity} <- set_to_list(Set)], " | "); t_to_string(?matchstate(Pres, Slots), RecDict) -> @@ -4524,19 +4416,18 @@ union_sequence(Types, RecDict) -> flat_join(List, " | "). -ifdef(DEBUG). -opaque_type(Mod, Name, _Args, S, RecDict) -> - ArgsString = comma_sequence(_Args, RecDict), +opaque_type(Mod, Name, Arity, S, RecDict) -> String = t_to_string(S, RecDict), - opaque_name(Mod, Name, ArgsString) ++ "[" ++ String ++ "]". + opaque_name(Mod, Name, Arity) ++ "[" ++ String ++ "]". -else. -opaque_type(Mod, Name, Args, _S, RecDict) -> - ArgsString = comma_sequence(Args, RecDict), - opaque_name(Mod, Name, ArgsString). +opaque_type(Mod, Name, Arity, _S, _RecDict) -> + opaque_name(Mod, Name, Arity). -endif. -opaque_name(Mod, Name, Extra) -> +opaque_name(Mod, Name, Arity) -> S = mod_name(Mod, Name), - flat_format("~ts(~ts)", [S, Extra]). + Args = lists:join($,, lists:duplicate(Arity, $_)), + flat_format("~ts(~ts)", [S, Args]). mod_name(Mod, Name) -> flat_format("~w:~tw", [Mod, Name]). @@ -4559,6 +4450,7 @@ mod_name(Mod, Name) -> [erl_type()], type_names()}. -type mod_type_table() :: ets:tid(). -type mod_records() :: dict:dict(module(), type_table()). +-type exported_type_table() :: ets:tid(). -record(cache, { types = maps:new() :: #{cache_key() => {erl_type(), expand_limit()}}, @@ -4567,7 +4459,7 @@ mod_name(Mod, Name) -> -opaque cache() :: #cache{}. --spec t_from_form(parse_form(), sets:set(mfa()), site(), mod_type_table(), +-spec t_from_form(parse_form(), exported_type_table(), site(), mod_type_table(), var_table(), cache()) -> {erl_type(), cache()}. t_from_form(Form, ExpTypes, Site, RecDict, VarTab, Cache) -> @@ -4592,12 +4484,12 @@ t_from_form_without_remote(Form, Site, TypeTable) -> -type expand_depth() :: integer(). -record(from_form, {site :: site(), - xtypes :: sets:set(mfa()) | 'replace_by_none', + xtypes :: exported_type_table() | 'replace_by_none', mrecs :: 'undefined' | mod_type_table(), vtab :: var_table(), tnames :: type_names()}). --spec t_from_form_check_remote(parse_form(), sets:set(mfa()), site(), +-spec t_from_form_check_remote(parse_form(), exported_type_table(), site(), mod_type_table()) -> 'ok'. t_from_form_check_remote(Form, ExpTypes, Site, RecDict) -> State = #from_form{site = Site, @@ -4618,7 +4510,7 @@ t_from_form_check_remote(Form, ExpTypes, Site, RecDict) -> %% types balanced (unions will otherwise collapse to any()) by limiting %% the depth the same way as t_limit/2 does. --spec t_from_form1(parse_form(), sets:set(mfa()) | 'replace_by_none', +-spec t_from_form1(parse_form(), exported_type_table() | 'replace_by_none', site(), 'undefined' | mod_type_table(), var_table(), cache()) -> {erl_type(), cache()}. @@ -4647,6 +4539,10 @@ initial_typenames({type, MTA, _File}) -> [{type, MTA}]; initial_typenames({spec, _MFA, _File}) -> []; initial_typenames({record, _MRA, _File}) -> []. +%% 4 is the maximal depth used by any Dialyzer module +%% (5 is used internally). +-define(TYPE_LIMIT, 4). + from_form_loop(Form, State, D, Limit, C, T0) -> {T1, L1, C1} = from_form(Form, State, D, Limit, C), Delta = Limit - L1, @@ -4656,6 +4552,9 @@ from_form_loop(Form, State, D, Limit, C, T0) -> Delta * 8 > Limit -> %% Save some time by assuming next depth will exceed the limit. {T1, C1}; + D =:= ?TYPE_LIMIT -> + %% No need to go deeper than necessary. + {T1, C1}; true -> D1 = D + 1, from_form_loop(Form, State, D1, Limit, C1, T1) @@ -4963,7 +4862,7 @@ remote_from_form(Anno, RemMod, Name, Args, S, D, L, C) -> self() ! {self(), ext_types, ext_types_message(MFA, Anno, Site)}, {t_any(), L, C}; {RemDict, C1} -> - case sets:is_element(MFA, ET) of + case ets:member(ET, MFA) of true -> RemType = {type, MFA}, case can_unfold_more(RemType, TypeNames) of @@ -5169,7 +5068,7 @@ separate_key(?union(List)) -> lists:append([separate_key(K) || K <- List, not t_is_none(K)]); separate_key(Key) -> [Key]. -%% Sorts, combines non-singleton pairs, and applies precendence and +%% Sorts, combines non-singleton pairs, and applies precedence and %% mandatoriness rules. map_from_form([], ShdwPs, MKs, Pairs, DefK, DefV) -> verify_possible(MKs, ShdwPs), @@ -5234,7 +5133,7 @@ recur_limit(Fun, D, L, TypeName, TypeNames) -> Fun(D, L) end. --spec t_check_record_fields(parse_form(), sets:set(mfa()), site(), +-spec t_check_record_fields(parse_form(), exported_type_table(), site(), mod_type_table(), var_table(), cache()) -> cache(). t_check_record_fields(Form, ExpTypes, Site, RecDict, VarTable, Cache) -> @@ -5465,7 +5364,7 @@ t_form_to_string({type, _Anno, Name, []} = T) -> V = var_table__new(), C = cache__new(), State = #from_form{site = Site, - xtypes = sets:new(), + xtypes = replace_by_none, mrecs = 'undefined', vtab = V, tnames = []}, @@ -5813,3 +5712,130 @@ handle_base(Unit, Neg) -> var_table__new() -> maps:new(). + +%%============================================================================= +%% +%% Utilities for finding a module's type dependencies +%% +%%============================================================================= + + +-spec module_type_deps_of_type_defs(type_table()) -> [module()]. + +module_type_deps_of_type_defs(TypeTable) -> + ModuleTypeDependencies = + [module_type_deps_of_entry(TypeTableEntry) + || TypeTableEntry <- maps:to_list(TypeTable)], + lists:append(ModuleTypeDependencies). + +-spec module_type_deps_of_entry( + {type_key(), type_value()} + | {record_key(), record_value()}) -> [module()]. + +module_type_deps_of_entry({{'type', _TypeName, _A}, {{_FromM, _FileLine, AbstractType, _ArgNames}, _}}) -> + type_form_to_remote_modules(AbstractType); + +module_type_deps_of_entry({{'opaque', _TypeName, _A}, {{_FromM, _FileLine, AbstractType, _ArgNames}, _}}) -> + type_form_to_remote_modules(AbstractType); + +module_type_deps_of_entry({{'record', _Name}, {_FileLine, SizesAndFields}}) -> + AllFields = lists:append([Fields || {_Size, Fields} <- SizesAndFields]), + FieldTypes = [AbstractType || {_, AbstractType, _} <- AllFields], + type_form_to_remote_modules(FieldTypes). + +%% Whilst this function is depth-limited, it should be limited in precisely +%% the same way as Dialyzer's other analyses - i.e. it should only ignore +%% sub-components of types Diaylzer wouldn't explore anyway +-spec type_form_to_remote_modules(parse_form() | [parse_form()]) -> [module()]. + +type_form_to_remote_modules([]) -> + []; + +type_form_to_remote_modules([_|_] = Forms) -> + D = ?EXPAND_DEPTH, + L = ?EXPAND_LIMIT, + {_, Mods} = list_get_modules_mentioned(Forms, D, L, []), + lists:usort(Mods); + +type_form_to_remote_modules(Form) -> + D = ?EXPAND_DEPTH, + L = ?EXPAND_LIMIT, + {_, Mods} = get_modules_mentioned(Form, D, L, []), + lists:usort(Mods). + +-spec get_modules_mentioned(TypeForm :: parse_form(), expand_depth(), expand_limit(), Acc :: [module()]) -> {expand_depth(), [module()]}. + +get_modules_mentioned(_, D, L, Acc) when D =< 0 ; L =< 0 -> + {L, Acc}; +get_modules_mentioned({var, _L, '_'}, _D, L, Acc) -> + {L, Acc}; +get_modules_mentioned({var, _L, _Name}, _D, L, Acc) -> + {L, Acc}; +get_modules_mentioned({ann_type, _L, [_Var, Type]}, D, L, Acc) -> + get_modules_mentioned(Type, D, L, Acc); +get_modules_mentioned({paren_type, _L, [Type]}, D, L, Acc) -> + get_modules_mentioned(Type, D, L, Acc); +get_modules_mentioned({atom, _L, _Atom}, _D, L, Acc) -> + {L, Acc}; +get_modules_mentioned({integer, _L, _Int}, _D, L, Acc) -> + {L, Acc}; +get_modules_mentioned({char, _L, _Char}, _D, L, Acc) -> + {L, Acc}; +get_modules_mentioned({op, _L, _Op, _Arg}, _D, L, Acc) -> + {L, Acc}; +get_modules_mentioned({op, _L, _Op, _Arg1, _Arg2}, _D, L, Acc) -> + {L, Acc}; +get_modules_mentioned({type, _L, 'fun', [{type, _, any}, Range]}, D, L, Acc) -> + get_modules_mentioned(Range, D - 1, L - 1, Acc); +get_modules_mentioned({type, _L, 'fun', [{type, _, product, Domain}, Range]}, D, L, Acc) -> + {L1, Acc1} = list_get_modules_mentioned(Domain, D, L, Acc), + get_modules_mentioned(Range, D, L1, Acc1); +get_modules_mentioned({type, _L, list, [Type]}, D, L, Acc) -> + get_modules_mentioned(Type, D - 1, L - 1, Acc); +get_modules_mentioned({type, _L, map, any}, _D, L, Acc) -> + {L, Acc}; +get_modules_mentioned({type, _L, map, List}, D0, L, Acc) -> + fun PairsFromForm(_, L1, Acc1) when L1 =< 0 -> Acc1; + PairsFromForm([], L1, Acc1) -> {L1, Acc1}; + PairsFromForm([{type, _, _Oper, [KF, VF]}|T], L1, Acc1) -> + D = D0 - 1, + {L2, Acc2} = get_modules_mentioned(KF, D, L1, Acc1), + {L3, Acc3} = get_modules_mentioned(VF, D, L2, Acc2), + PairsFromForm(T, L3 - 1, Acc3) + end(List, L, Acc); +get_modules_mentioned({type, _L, nonempty_list, [Type]}, D, L, Acc) -> + get_modules_mentioned(Type, D, L - 1, Acc); +get_modules_mentioned({type, _L, nonempty_improper_list, [Cont, Term]}, D, L, Acc) -> + {L1, Acc1} = get_modules_mentioned(Cont, D, L - 1, Acc), + get_modules_mentioned(Term, D, L1, Acc1); +get_modules_mentioned({type, _L, nonempty_maybe_improper_list, [Cont, Term]}, D, L, Acc) -> + {L1, Acc1} = get_modules_mentioned(Cont, D, L - 1, Acc), + get_modules_mentioned(Term, D, L1, Acc1); +get_modules_mentioned({type, _L, maybe_improper_list, [Content, Termination]}, D, L, Acc) -> + {L1, Acc1} = get_modules_mentioned(Content, D, L - 1, Acc), + get_modules_mentioned(Termination, D, L1, Acc1); +get_modules_mentioned({type, _L, product, Elements}, D, L, Acc) -> + list_get_modules_mentioned(Elements, D - 1, L, Acc); +get_modules_mentioned({type, _L, range, [_From, _To]}, _D, L, Acc) -> + {L, Acc}; +get_modules_mentioned({type, _L, tuple, any}, _D, L, Acc) -> + {L, Acc}; +get_modules_mentioned({type, _L, tuple, Args}, D, L, Acc) -> + list_get_modules_mentioned(Args, D - 1, L, Acc); +get_modules_mentioned({type, _L, union, Args}, D, L, Acc) -> + list_get_modules_mentioned(Args, D, L, Acc); +get_modules_mentioned({remote_type, _L, [{atom, _, Module}, {atom, _, _Type}, Args]}, D, L, Acc) -> + Acc1 = [Module|Acc], + list_get_modules_mentioned(Args, D, L, Acc1); +get_modules_mentioned({user_type, _L, _Name, Args}, D, L, Acc) -> + list_get_modules_mentioned(Args, D, L, Acc); +get_modules_mentioned({type, _L, _Name, []}, _D, L, Acc) -> + {L, Acc}; +get_modules_mentioned({type, _L, _Name, Args}, D, L, Acc) -> + list_get_modules_mentioned(Args, D, L, Acc). + +list_get_modules_mentioned([], _D, L, Acc) -> + {L, Acc}; +list_get_modules_mentioned([H|Tail], D, L, Acc) -> + {L1, Acc1} = get_modules_mentioned(H, D, L - 1, Acc), + list_get_modules_mentioned(Tail, D, L1, Acc1). |