aboutsummaryrefslogtreecommitdiffstats
path: root/lib/hipe/cerl
diff options
context:
space:
mode:
authorHans Bolinder <[email protected]>2015-01-23 14:57:31 +0100
committerHans Bolinder <[email protected]>2015-03-10 10:49:15 +0100
commit2c1ad10d8ab95aa602337b389dbefedddfb1747c (patch)
treed7df64e9feeeebabec97d5831df45035074369ca /lib/hipe/cerl
parent747955f26376486eb7ecbd8f9a6144ba668a21c7 (diff)
downloadotp-2c1ad10d8ab95aa602337b389dbefedddfb1747c.tar.gz
otp-2c1ad10d8ab95aa602337b389dbefedddfb1747c.tar.bz2
otp-2c1ad10d8ab95aa602337b389dbefedddfb1747c.zip
[dialyzer] Correct handling of limited opaque types
Diffstat (limited to 'lib/hipe/cerl')
-rw-r--r--lib/hipe/cerl/erl_types.erl62
1 files changed, 43 insertions, 19 deletions
diff --git a/lib/hipe/cerl/erl_types.erl b/lib/hipe/cerl/erl_types.erl
index d092e3fe40..09dffe1280 100644
--- a/lib/hipe/cerl/erl_types.erl
+++ b/lib/hipe/cerl/erl_types.erl
@@ -749,7 +749,7 @@ t_opaque_from_records(RecDict) ->
end
end, RecDict),
OpaqueTypeDict =
- dict:map(fun({opaque, Name, _Arity}, {Module, _Type, ArgNames}) ->
+ dict:map(fun({opaque, Name, _Arity}, {{Module, _Form, ArgNames}, _Type}) ->
%% Args = args_to_types(ArgNames),
%% List = lists:zip(ArgNames, Args),
%% TmpVarDict = dict:from_list(List),
@@ -3963,7 +3963,8 @@ t_from_form(Form, ExpTypes, Module, RecDict) ->
module(), mod_records(), var_table()) -> erl_type().
t_from_form(Form, ExpTypes, Module, RecDict, VarDict) ->
- t_from_form1(Form, [], ExpTypes, Module, RecDict, VarDict).
+ {T, _} = t_from_form1(Form, [], ExpTypes, Module, RecDict, VarDict),
+ T.
%% Replace external types with with none().
-spec t_from_form_without_remote(parse_form(), type_table()) -> erl_type().
@@ -3972,7 +3973,8 @@ t_from_form_without_remote(Form, TypeTable) ->
Module = mod,
RecDict = dict:from_list([{Module, TypeTable}]),
ExpTypes = replace_by_none,
- t_from_form1(Form, [], ExpTypes, Module, RecDict, dict:new()).
+ {T, _} = t_from_form1(Form, [], ExpTypes, Module, RecDict, dict:new()),
+ T.
%% REC_TYPE_LIMIT is used for limiting the depth of recursive types.
%% EXPAND_LIMIT is used for limiting the size of types by
@@ -3996,7 +3998,7 @@ t_from_form1(Form, TypeNames, ET, M, MR, V, D) ->
D1 = D div 2,
t_from_form1(Form, TypeNames, ET, M, MR, V, D1);
true ->
- T
+ {T, L1}
end.
-spec t_from_form(parse_form(), type_names(),
@@ -4196,7 +4198,7 @@ builtin_type(Name, Type, TypeNames, ET, M, MR, V, D, L) ->
case dict:find(M, MR) of
{ok, R} ->
case lookup_type(Name, 0, R) of
- {_, {_M, _T, _A}} ->
+ {_, {{_M, _F, _A}, _T}} ->
type_from_form(Name, [], TypeNames, ET, M, MR, V, D, L);
error ->
{Type, L}
@@ -4210,28 +4212,29 @@ type_from_form(Name, Args, TypeNames, ET, M, MR, V, D, L) ->
{ArgTypes, L1} = list_from_form(Args, TypeNames, ET, M, MR, V, D, L),
{ok, R} = dict:find(M, MR),
case lookup_type(Name, ArgsLen, R) of
- {type, {Module, Type, ArgNames}} ->
+ {type, {{Module, Form, ArgNames}, _Type}} ->
TypeName = {type, Module, Name, ArgsLen},
case can_unfold_more(TypeName, TypeNames) of
true ->
List = lists:zip(ArgNames, ArgTypes),
TmpV = dict:from_list(List),
- t_from_form(Type, [TypeName|TypeNames], ET, M, MR, TmpV, D, L1);
+ t_from_form(Form, [TypeName|TypeNames], ET, M, MR, TmpV, D, L1);
false ->
{t_any(), L1}
end;
- {opaque, {Module, Type, ArgNames}} ->
- TypeName = {opaque, Module, Name, ArgsLen}, % 'type' would do...
- {Rep, L3} =
+ {opaque, {{Module, Form, ArgNames}, Type}} ->
+ TypeName = {opaque, Module, Name, ArgsLen},
+ {Rep, L2} =
case can_unfold_more(TypeName, TypeNames) of
true ->
List = lists:zip(ArgNames, ArgTypes),
TmpV = dict:from_list(List),
- t_from_form(Type, [TypeName|TypeNames], ET, M, MR, TmpV, D, L1);
+ t_from_form(Form, [TypeName|TypeNames], ET, M, MR, TmpV, D, L1);
false -> {t_any(), L1}
end,
+ Rep1 = choose_opaque_type(Rep, Type),
Args2 = [subst_all_vars_to_any(ArgType) || ArgType <- ArgTypes],
- {skip_opaque_alias(Rep, Module, Name, Args2), L3};
+ {skip_opaque_alias(Rep1, Module, Name, Args2), L2};
error ->
Msg = io_lib:format("Unable to find type ~w/~w\n", [Name, ArgsLen]),
throw({error, Msg})
@@ -4248,7 +4251,6 @@ remote_from_form(RemMod, Name, Args, TypeNames, ET, M, MR, V, D, L) ->
{t_none(), L1};
true ->
ArgsLen = length(Args),
- RemType = {type, RemMod, Name, ArgsLen},
case dict:find(RemMod, MR) of
error ->
self() ! {self(), ext_types, {RemMod, Name, ArgsLen}},
@@ -4258,30 +4260,33 @@ remote_from_form(RemMod, Name, Args, TypeNames, ET, M, MR, V, D, L) ->
case sets:is_element(MFA, ET) of
true ->
case lookup_type(Name, ArgsLen, RemDict) of
- {type, {_Mod, Type, ArgNames}} ->
+ {type, {{_Mod, Form, ArgNames}, _Type}} ->
+ RemType = {type, RemMod, Name, ArgsLen},
case can_unfold_more(RemType, TypeNames) of
true ->
List = lists:zip(ArgNames, ArgTypes),
TmpVarDict = dict:from_list(List),
NewTypeNames = [RemType|TypeNames],
- t_from_form(Type, NewTypeNames, ET,
+ t_from_form(Form, NewTypeNames, ET,
RemMod, MR, TmpVarDict, D, L1);
false ->
{t_any(), L1}
end;
- {opaque, {Mod, Type, ArgNames}} ->
+ {opaque, {{Mod, Form, ArgNames}, Type}} ->
+ RemType = {opaque, RemMod, Name, ArgsLen},
List = lists:zip(ArgNames, ArgTypes),
TmpVarDict = dict:from_list(List),
{NewRep, L2} =
case can_unfold_more(RemType, TypeNames) of
true ->
NewTypeNames = [RemType|TypeNames],
- t_from_form(Type, NewTypeNames, ET,
- RemMod, MR, TmpVarDict, D, L1);
+ t_from_form(Form, NewTypeNames, ET, RemMod, MR,
+ TmpVarDict, D, L1);
false ->
{t_any(), L1}
end,
- {skip_opaque_alias(NewRep, Mod, Name, ArgTypes), L2};
+ NewRep1 = choose_opaque_type(NewRep, Type),
+ {skip_opaque_alias(NewRep1, Mod, Name, ArgTypes), L2};
error ->
Msg = io_lib:format("Unable to find remote type ~w:~w()\n",
[RemMod, Name]),
@@ -4294,6 +4299,25 @@ remote_from_form(RemMod, Name, Args, TypeNames, ET, M, MR, V, D, L) ->
end
end.
+%% Opaque types (both local and remote) are problematic when it comes
+%% to the limits (TypeNames, D, and L). The reason is that if any() is
+%% substituted for a more specialized subtype of an opaque type, the
+%% property stated along with decorate_with_opaque() (the type has to
+%% be a subtype of the declared type) no longer holds.
+%%
+%% The less than perfect remedy: if the opaque type created from a
+%% form is not a subset of the declared type, the declared type is
+%% used instead, effectively bypassing the limits, and potentially
+%% resulting in huge types.
+choose_opaque_type(Type, DeclType) ->
+ case
+ t_is_subtype(subst_all_vars_to_any(Type),
+ subst_all_vars_to_any(DeclType))
+ of
+ true -> Type;
+ false -> DeclType
+ end.
+
record_from_form({atom, _, Name}, ModFields, TypeNames, ET, M, MR, V, D, L) ->
case can_unfold_more({record, Name}, TypeNames) of
true ->