aboutsummaryrefslogtreecommitdiffstats
path: root/lib/hipe/cerl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/hipe/cerl')
-rw-r--r--lib/hipe/cerl/Makefile11
-rw-r--r--lib/hipe/cerl/cerl_closurean.erl8
-rw-r--r--lib/hipe/cerl/cerl_messagean.erl4
-rw-r--r--lib/hipe/cerl/cerl_to_icode.erl38
-rw-r--r--lib/hipe/cerl/cerl_typean.erl4
-rw-r--r--lib/hipe/cerl/erl_bif_types.erl16
-rw-r--r--lib/hipe/cerl/erl_types.erl430
7 files changed, 306 insertions, 205 deletions
diff --git a/lib/hipe/cerl/Makefile b/lib/hipe/cerl/Makefile
index 506e993ff4..d13dfb33c2 100644
--- a/lib/hipe/cerl/Makefile
+++ b/lib/hipe/cerl/Makefile
@@ -42,8 +42,8 @@ RELSYSDIR = $(RELEASE_PATH)/lib/hipe-$(VSN)
# ----------------------------------------------------
# Target Specs
# ----------------------------------------------------
-MODULES = cerl_cconv cerl_closurean cerl_hipeify \
- cerl_lib cerl_messagean cerl_pmatch cerl_prettypr cerl_to_icode \
+MODULES = cerl_cconv cerl_closurean cerl_hipeify cerl_lib \
+ cerl_messagean cerl_pmatch cerl_prettypr cerl_to_icode \
cerl_typean erl_bif_types erl_types
HRL_FILES= cerl_hipe_primops.hrl
@@ -65,7 +65,7 @@ DOC_FILES= $(MODULES:%=$(DOCS)/%.html)
include ../native.mk
-ERL_COMPILE_FLAGS += +inline +warn_exported_vars +warn_unused_import +warn_missing_spec# +warn_untyped_record
+ERL_COMPILE_FLAGS += -Werror +inline +warn_exported_vars +warn_unused_import +warn_missing_spec #+warn_untyped_record
# ----------------------------------------------------
# Targets
@@ -107,7 +107,6 @@ release_spec: opt
release_docs_spec:
-$(EBIN)/cerl_to_icode.beam: cerl_hipe_primops.hrl ../icode/hipe_icode_primops.hrl
+$(EBIN)/cerl_cconv.beam: cerl_hipe_primops.hrl
$(EBIN)/cerl_hipeify.beam: cerl_hipe_primops.hrl
-$(EBIN)/cerl_lambdalift.beam: cerl_hipe_primops.hrl
-$(EBIN)/erl_bif_types.beam: ../icode/hipe_icode_primops.hrl
+$(EBIN)/cerl_to_icode.beam: cerl_hipe_primops.hrl ../icode/hipe_icode_primops.hrl
diff --git a/lib/hipe/cerl/cerl_closurean.erl b/lib/hipe/cerl/cerl_closurean.erl
index 021acd5b35..1b325703ae 100644
--- a/lib/hipe/cerl/cerl_closurean.erl
+++ b/lib/hipe/cerl/cerl_closurean.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -78,7 +78,8 @@
%% function; see `analyze' for details.
-spec annotate(cerl:cerl()) ->
- {cerl:cerl(), outlist(), dict(), escapes(), dict(), dict()}.
+ {cerl:cerl(), outlist(), dict:dict(),
+ escapes(), dict:dict(), dict:dict()}.
annotate(Tree) ->
{Xs, Out, Esc, Deps, Par} = analyze(Tree),
@@ -206,7 +207,8 @@ append_ann(Tag, Val, []) ->
%% variable labeled `escape', which will hold the set of escaped labels.
%% initially it contains `top' and `external'.
--spec analyze(cerl:cerl()) -> {outlist(), dict(), escapes(), dict(), dict()}.
+-spec analyze(cerl:cerl()) ->
+ {outlist(), dict:dict(), escapes(), dict:dict(), dict:dict()}.
analyze(Tree) ->
%% Note that we use different name spaces for variable labels and
diff --git a/lib/hipe/cerl/cerl_messagean.erl b/lib/hipe/cerl/cerl_messagean.erl
index ca812a0f0d..7911b875a9 100644
--- a/lib/hipe/cerl/cerl_messagean.erl
+++ b/lib/hipe/cerl/cerl_messagean.erl
@@ -1,7 +1,7 @@
%% =====================================================================
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -182,7 +182,7 @@
-type label() :: integer() | 'external' | 'top'.
-type ordset(X) :: [X]. % XXX: TAKE ME OUT
--spec annotate(cerl:cerl()) -> {cerl:cerl(), ordset(label()), dict()}.
+-spec annotate(cerl:cerl()) -> {cerl:cerl(), ordset(label()), dict:dict()}.
annotate(Tree) ->
{Esc0, Vars} = analyze(Tree),
diff --git a/lib/hipe/cerl/cerl_to_icode.erl b/lib/hipe/cerl/cerl_to_icode.erl
index 1c1c10d9b0..2645056be1 100644
--- a/lib/hipe/cerl/cerl_to_icode.erl
+++ b/lib/hipe/cerl/cerl_to_icode.erl
@@ -29,9 +29,9 @@
-define(NO_UNUSED, true).
--export([module/2]).
+-export([module/1, module/2]).
-ifndef(NO_UNUSED).
--export([function/3, function/4, module/1]).
+-export([function/3, function/4]).
-endif.
%% Added in an attempt to suppress message by Dialyzer, but I run into
@@ -102,36 +102,32 @@
%% Record definitions
--record(ctxt, {final = false :: boolean(),
- effect = false,
- fail = [], % [] or fail-to label
- class = expr, % expr | guard
- line = 0, % current line number
- 'receive' % undefined | #receive{}
- }).
-
-record('receive', {loop}).
-record(cerl_to_icode__var, {name}).
-record('fun', {label, vars}).
+-record(ctxt, {final = false :: boolean(),
+ effect = false :: boolean(),
+ fail = [], % [] or fail-to label
+ class = expr :: 'expr' | 'guard',
+ line = 0 :: erl_scan:line(), % current line number
+ 'receive' :: 'undefined' | #'receive'{}
+ }).
%% ---------------------------------------------------------------------
%% Code
-
-%% @spec module(Module::cerl()) -> [icode()]
+%% @spec module(Module::cerl()) -> [{mfa(), icode()}]
%% @equiv module(Module, [])
--ifndef(NO_UNUSED).
+-spec module(cerl:c_module()) -> [{mfa(), hipe_icode:icode()}].
+
module(E) ->
module(E, []).
--endif.
-%% @clear
-
-%% @spec module(Module::cerl(), Options::[term()]) -> [icode()]
+%% @spec module(Module::cerl(), Options::[term()]) -> [{mfa(), icode()}]
%%
-%% cerl() = cerl:cerl()
+%% cerl() = cerl:c_module()
%% icode() = hipe_icode:icode()
%%
%% @doc Transforms a Core Erlang module to linear HiPE Icode. The result
@@ -149,7 +145,7 @@ module(E) ->
%% @see function/4
%% @see cerl_hipeify:transform/1
-%% -spec module(cerl:c_module(), [term()]) -> [{mfa(), hipe_icode:icode()}].
+-spec module(cerl:c_module(), [term()]) -> [{mfa(), hipe_icode:icode()}].
module(E, Options) ->
module_1(cerl_hipeify:transform(E, Options), Options).
@@ -163,8 +159,8 @@ module_1(E, Options) ->
throw(error)
end,
S0 = init(M),
- S1 = s__set_pmatch(proplists:get_value(pmatch, Options), S0),
- S2 = s__set_bitlevel_binaries(proplists:get_value(
+ S1 = s__set_pmatch(proplists:get_value(pmatch, Options), S0),
+ S2 = s__set_bitlevel_binaries(proplists:get_value(
bitlevel_binaries, Options), S1),
{Icode, _} = lists:mapfoldl(fun function_definition/2,
S2, cerl:module_defs(E)),
diff --git a/lib/hipe/cerl/cerl_typean.erl b/lib/hipe/cerl/cerl_typean.erl
index ccd8903658..f694c07c82 100644
--- a/lib/hipe/cerl/cerl_typean.erl
+++ b/lib/hipe/cerl/cerl_typean.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -242,7 +242,7 @@ delete_ann(_, []) ->
-type labelset() :: ordset(label()).
-type outlist() :: [labelset()] | 'none'.
--spec analyze(cerl:cerl()) -> {outlist(), dict(), dict()}.
+-spec analyze(cerl:cerl()) -> {outlist(), dict:dict(), dict:dict()}.
analyze(Tree) ->
analyze(Tree, ?DEF_LIMIT).
diff --git a/lib/hipe/cerl/erl_bif_types.erl b/lib/hipe/cerl/erl_bif_types.erl
index 8b610ac893..a460f16272 100644
--- a/lib/hipe/cerl/erl_bif_types.erl
+++ b/lib/hipe/cerl/erl_bif_types.erl
@@ -912,7 +912,8 @@ type(erlang, system_info, 1, Xs, Opaques) ->
t_list(t_pid());
['os_type'] ->
t_tuple([t_sup([t_atom('unix'),
- t_atom('win32')]),
+ t_atom('win32'),
+ t_atom('ose')]),
t_atom()]);
['os_version'] ->
t_sup(t_tuple([t_non_neg_fixnum(),
@@ -1072,14 +1073,14 @@ type(hipe_bifs, fun_to_address, 1, Xs, Opaques) ->
%% type(hipe_bifs, get_emu_address, 1, Xs, Opaques) ->
%% strict(hipe_bifs, get_emu_address, 1, Xs,
%% fun (_) -> t_integer() end, Opaques); % address
+type(hipe_bifs, get_fe, 2, Xs, Opaques) ->
+ strict(hipe_bifs, get_fe, 2, Xs, fun (_) -> t_integer() end, Opaques);
type(hipe_bifs, get_rts_param, 1, Xs, Opaques) ->
strict(hipe_bifs, get_rts_param, 1, Xs,
fun (_) -> t_sup(t_integer(), t_nil()) end, Opaques);
type(hipe_bifs, invalidate_funinfo_native_addresses, 1, Xs, Opaques) ->
strict(hipe_bifs, invalidate_funinfo_native_addresses, 1, Xs,
fun (_) -> t_nil() end, Opaques);
-type(hipe_bifs, make_fe, 3, Xs, Opaques) ->
- strict(hipe_bifs, make_fe, 3, Xs, fun (_) -> t_integer() end, Opaques);
%% type(hipe_bifs, make_native_stub, 2, Xs, Opaques) ->
%% strict(hipe_bifs, make_native_stub, 2, Xs,
%% fun (_) -> t_integer() end, Opaques); % address
@@ -1115,6 +1116,9 @@ type(hipe_bifs, set_funinfo_native_address, 3, Xs, Opaques) ->
type(hipe_bifs, set_native_address, 3, Xs, Opaques) ->
strict(hipe_bifs, set_native_address, 3, Xs,
fun (_) -> t_nil() end, Opaques);
+type(hipe_bifs, set_native_address_in_fe, 2, Xs, Opaques) ->
+ strict(hipe_bifs, set_native_address_in_fe, 2, Xs,
+ fun (_) -> t_atom('true') end, Opaques);
type(hipe_bifs, system_crc, 1, Xs, Opaques) ->
strict(hipe_bifs, system_crc, 1, Xs, fun (_) -> t_crc32() end, Opaques);
type(hipe_bifs, term_to_word, 1, Xs, Opaques) ->
@@ -2450,12 +2454,12 @@ arg_types(hipe_bifs, fun_to_address, 1) ->
[t_mfa()];
%% arg_types(hipe_bifs, get_emu_address, 1) ->
%% [t_mfa()];
+arg_types(hipe_bifs, get_fe, 2) ->
+ [t_atom(), t_tuple([t_integer(), t_integer(), t_integer()])];
arg_types(hipe_bifs, get_rts_param, 1) ->
[t_fixnum()];
arg_types(hipe_bifs, invalidate_funinfo_native_addresses, 1) ->
[t_list(t_mfa())];
-arg_types(hipe_bifs, make_fe, 3) ->
- [t_integer(), t_atom(), t_tuple([t_integer(), t_integer(), t_integer()])];
%% arg_types(hipe_bifs, make_native_stub, 2) ->
%% [t_integer(), t_arity()];
arg_types(hipe_bifs, mark_referred_from, 1) ->
@@ -2484,6 +2488,8 @@ arg_types(hipe_bifs, set_funinfo_native_address, 3) ->
arg_types(hipe_bifs, set_native_address, 3);
arg_types(hipe_bifs, set_native_address, 3) ->
[t_mfa(), t_integer(), t_boolean()];
+arg_types(hipe_bifs, set_native_address_in_fe, 2) ->
+ [t_integer(), t_integer()];
arg_types(hipe_bifs, system_crc, 1) ->
[t_crc32()];
arg_types(hipe_bifs, term_to_word, 1) ->
diff --git a/lib/hipe/cerl/erl_types.erl b/lib/hipe/cerl/erl_types.erl
index af34e355bb..47b8dc766a 100644
--- a/lib/hipe/cerl/erl_types.erl
+++ b/lib/hipe/cerl/erl_types.erl
@@ -126,6 +126,8 @@
t_is_instance/2,
t_is_integer/1, t_is_integer/2,
t_is_list/1,
+ t_is_map/1,
+ t_is_map/2,
t_is_matchstate/1,
t_is_nil/1, t_is_nil/2,
t_is_non_neg_integer/1,
@@ -148,6 +150,8 @@
t_list/1,
t_list_elements/1, t_list_elements/2,
t_list_termination/1,
+ t_map/0,
+ t_map/1,
t_matchstate/0,
t_matchstate/2,
t_matchstate_present/1,
@@ -208,11 +212,7 @@
lift_list_to_pos_empty/1,
is_opaque_type/2,
is_erl_type/1,
- atom_to_string/1,
-
- t_is_map/2,
- t_map/1,
- t_map/0
+ atom_to_string/1
]).
%%-define(DO_ERL_TYPES_TEST, true).
@@ -228,7 +228,7 @@
-export([t_is_identifier/1]).
-endif.
--export_type([erl_type/0]).
+-export_type([erl_type/0, type_table/0, var_table/0]).
%%-define(DEBUG, true).
@@ -363,6 +363,15 @@
-type opaques() :: [erl_type()] | 'universe'.
+-type record_key() :: {'record', atom()}.
+-type type_key() :: {'type' | 'opaque', atom(), arity()}.
+-type record_value() :: orddict:orddict(). % XXX. To be refined
+-type type_value() :: {module(), erl_type(), atom()}.
+-type type_table() :: dict:dict(record_key(), record_value())
+ | dict:dict(type_key(), type_value()).
+
+-type var_table() :: dict:dict(atom(), erl_type()).
+
%%-----------------------------------------------------------------------------
%% Unions
%%
@@ -489,9 +498,9 @@ t_contains_opaque(?int_range(_From, _To), _Opaques) -> false;
t_contains_opaque(?int_set(_Set), _Opaques) -> false;
t_contains_opaque(?list(Type, Tail, _), Opaques) ->
t_contains_opaque(Type, Opaques) orelse t_contains_opaque(Tail, Opaques);
-t_contains_opaque(?map(Pairs), Opaques) ->
- list_contains_opaque([V||{_,V}<-Pairs], Opaques) orelse
- list_contains_opaque([K||{K,_}<-Pairs], Opaques);
+t_contains_opaque(?map(_) = Map, Opaques) ->
+ list_contains_opaque(map_values(Map), Opaques) orelse
+ list_contains_opaque(map_keys(Map), Opaques);
t_contains_opaque(?matchstate(_P, _Slots), _Opaques) -> false;
t_contains_opaque(?nil, _Opaques) -> false;
t_contains_opaque(?number(_Set, _Tag), _Opaques) -> false;
@@ -605,7 +614,7 @@ t_decorate_with_opaque(T1, T2, Opaques) ->
end
end.
-decorate(?none=Type, _, _Opaques) -> Type;
+decorate(Type, ?none, _Opaques) -> Type;
decorate(?function(Domain, Range), ?function(D, R), Opaques) ->
?function(decorate(Domain, D, Opaques), decorate(Range, R, Opaques));
decorate(?list(Types, Tail, Size), ?list(Ts, Tl, _Sz), Opaques) ->
@@ -645,15 +654,16 @@ decorate_with_opaque(Type, ?opaque(Set2), Opaques) ->
end.
decoration([#opaque{struct = S} = Opaque|OpaqueTypes], Type, Opaques,
- NewOpaqueTypes, All) ->
+ NewOpaqueTypes0, All) ->
IsOpaque = is_opaque_type2(Opaque, Opaques),
I = t_inf(Type, S),
- case not IsOpaque orelse t_is_none(I = t_inf(Type, S)) of
- true -> decoration(OpaqueTypes, Type, Opaques, NewOpaqueTypes, All);
+ case not IsOpaque orelse t_is_none(I) of
+ true -> decoration(OpaqueTypes, Type, Opaques, NewOpaqueTypes0, All);
false ->
NewOpaque = Opaque#opaque{struct = decorate(I, S, Opaques)},
NewAll = All orelse t_is_equal(I, Type),
- decoration(OpaqueTypes, Type, Opaques, [NewOpaque|NewOpaqueTypes], NewAll)
+ NewOpaqueTypes = [NewOpaque|NewOpaqueTypes0],
+ decoration(OpaqueTypes, Type, Opaques, NewOpaqueTypes, NewAll)
end;
decoration([], _Type, _Opaques, NewOpaqueTypes, All) ->
{NewOpaqueTypes, All}.
@@ -670,6 +680,7 @@ union_decorate(U1, U2, Opaques) ->
List = [A,B,F,I,L,N,T,M,Map],
DecList = [Dec ||
E <- List,
+ not t_is_none(E),
not t_is_none(Dec = decorate(E, Opaque, Opaques))],
t_sup([Union|DecList]).
@@ -722,7 +733,7 @@ decorate_tuples_in_sets([T1|Tuples], L2, Opaques, Acc) ->
decorate_tuples_in_sets([], _L, _Opaques, Acc) ->
lists:reverse(Acc).
--spec t_opaque_from_records(dict()) -> [erl_type()].
+-spec t_opaque_from_records(type_table()) -> [erl_type()].
t_opaque_from_records(RecDict) ->
OpaqueRecDict =
@@ -733,13 +744,14 @@ t_opaque_from_records(RecDict) ->
end
end, RecDict),
OpaqueTypeDict =
- dict:map(fun({opaque, Name, _Arity}, {Module, Type, ArgNames}) ->
- case ArgNames of
- [] ->
- t_opaque(Module, Name, [], t_from_form(Type, RecDict));
- _ ->
- throw({error,"Polymorphic opaque types not supported yet"})
- end
+ dict:map(fun({opaque, Name, _Arity}, {Module, _Type, ArgNames}) ->
+ %% Args = args_to_types(ArgNames),
+ %% List = lists:zip(ArgNames, Args),
+ %% TmpVarDict = dict:from_list(List),
+ %% Rep = t_from_form(Type, RecDict, TmpVarDict),
+ Rep = t_none(), % not used for anything right now
+ Args = [t_any() || _ <- ArgNames],
+ skip_opaque_alias(Rep, Module, Name, Args)
end, OpaqueRecDict),
[OpaqueType || {_Key, OpaqueType} <- dict:to_list(OpaqueTypeDict)].
@@ -796,7 +808,9 @@ t_is_remote(Type) ->
is_remote(?remote(_)) -> true;
is_remote(_) -> false.
--spec t_solve_remote(erl_type(), set(), dict()) -> erl_type().
+-type mod_records() :: dict:dict(module(), type_table()).
+
+-spec t_solve_remote(erl_type(), sets:set(mfa()), mod_records()) -> erl_type().
t_solve_remote(Type, ExpTypes, Records) ->
{RT, _RR} = t_solve_remote(Type, ExpTypes, Records, []),
@@ -833,8 +847,12 @@ t_solve_remote(?union(List), ET, R, C) ->
{t_sup(RL), RR};
t_solve_remote(T, _ET, _R, _C) -> {T, []}.
-t_solve_remote_type(#remote{mod = RemMod, name = Name, args = Args} = RemType,
+t_solve_remote_type(#remote{mod = RemMod, name = Name, args = Args0} = RemType,
ET, R, C) ->
+ Args = lists:map(fun(A) ->
+ {Arg, _} = t_solve_remote(A, ET, R, C),
+ Arg
+ end, Args0),
ArgsLen = length(Args),
case dict:find(RemMod, R) of
error ->
@@ -880,9 +898,7 @@ t_solve_remote_type(#remote{mod = RemMod, name = Name, args = Args} = RemType,
true -> t_limit(NewRep, ?REC_TYPE_LIMIT);
false -> NewRep
end,
- {t_from_form({opaque, -1, Name, {Mod, Args, RT1}},
- RemDict, TmpVarDict),
- RetRR};
+ {skip_opaque_alias(RT1, Mod, Name, Args), RetRR};
error ->
Msg = io_lib:format("Unable to find remote type ~w:~w()\n",
[RemMod, Name]),
@@ -1713,11 +1729,16 @@ lift_list_to_pos_empty(?list(Content, Termination, _)) ->
t_map() ->
?map([]).
--spec t_map([{erl_type(),erl_type()}]) -> erl_type().
+-spec t_map([{erl_type(), erl_type()}]) -> erl_type().
t_map(_) ->
?map([]).
+-spec t_is_map(erl_type()) -> boolean().
+
+t_is_map(Type) ->
+ t_is_map(Type, 'universe').
+
-spec t_is_map(erl_type(), opaques()) -> boolean().
t_is_map(Type, Opaques) ->
@@ -1726,7 +1747,6 @@ t_is_map(Type, Opaques) ->
is_map1(?map(_)) -> true;
is_map1(_) -> false.
-
%%-----------------------------------------------------------------------------
%% Tuples
%%
@@ -1958,20 +1978,24 @@ t_timeout() ->
-spec t_array() -> erl_type().
t_array() ->
- t_opaque(array, array, [],
+ t_opaque(array, array, [t_any()],
t_tuple([t_atom('array'),
t_sup([t_atom('undefined'), t_non_neg_integer()]),
t_sup([t_atom('undefined'), t_non_neg_integer()]),
- t_any(), t_any()])).
+ t_any(),
+ t_any()])).
-spec t_dict() -> erl_type().
t_dict() ->
- t_opaque(dict, dict, [],
+ t_opaque(dict, dict, [t_any(), t_any()],
t_tuple([t_atom('dict'),
- t_non_neg_integer(), t_non_neg_integer(),
- t_non_neg_integer(), t_non_neg_integer(),
- t_non_neg_integer(), t_non_neg_integer(),
+ t_sup([t_atom('undefined'), t_non_neg_integer()]),
+ t_sup([t_atom('undefined'), t_non_neg_integer()]),
+ t_sup([t_atom('undefined'), t_non_neg_integer()]),
+ t_sup([t_atom('undefined'), t_non_neg_integer()]),
+ t_sup([t_atom('undefined'), t_non_neg_integer()]),
+ t_sup([t_atom('undefined'), t_non_neg_integer()]),
t_sup([t_atom('undefined'), t_tuple()]),
t_sup([t_atom('undefined'), t_tuple()])])).
@@ -2000,12 +2024,12 @@ t_gb_tree() ->
-spec t_queue() -> erl_type().
t_queue() ->
- t_opaque(queue, queue, [], t_tuple([t_list(), t_list()])).
+ t_opaque(queue, queue, [t_any()], t_tuple([t_list(), t_list()])).
-spec t_set() -> erl_type().
t_set() ->
- t_opaque(sets, set, [],
+ t_opaque(sets, set, [t_any()],
t_tuple([t_atom('set'), t_non_neg_integer(), t_non_neg_integer(),
t_pos_integer(), t_non_neg_integer(), t_non_neg_integer(),
t_non_neg_integer(),
@@ -2023,18 +2047,6 @@ all_opaque_builtins() ->
[t_array(), t_dict(), t_digraph(), t_gb_set(),
t_gb_tree(), t_queue(), t_set(), t_tid()].
--spec is_opaque_builtin(atom(), atom()) -> boolean().
-
-is_opaque_builtin(array, array) -> true;
-is_opaque_builtin(dict, dict) -> true;
-is_opaque_builtin(digraph, digraph) -> true;
-is_opaque_builtin(gb_sets, gb_set) -> true;
-is_opaque_builtin(gb_trees, gb_tree) -> true;
-is_opaque_builtin(queue, queue) -> true;
-is_opaque_builtin(sets, set) -> true;
-is_opaque_builtin(ets, tid) -> true;
-is_opaque_builtin(_, _) -> false.
-
%%------------------------------------
%% ?none is allowed in products. A product of size 1 is not a product.
@@ -2082,11 +2094,13 @@ t_has_var(?tuple(Elements, _, _)) ->
t_has_var_list(Elements);
t_has_var(?tuple_set(_) = T) ->
t_has_var_list(t_tuple_subtypes(T));
-%% t_has_var(?opaque(_)=T) ->
-%% %% "Polymorphic opaque types not supported yet"
-%% t_has_var(t_opaque_structure(T));
-%% t_has_var(?union(_) = U) ->
-%% exit(flat_format("Union happens in t_has_var/1 ~p\n",[U]));
+t_has_var(?map(_)= Map) ->
+ t_has_var_list(map_keys(Map)) orelse t_has_var_list(map_values(Map));
+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);
t_has_var(_) -> false.
-spec t_has_var_list([erl_type()]) -> boolean().
@@ -2109,20 +2123,28 @@ t_collect_vars(?function(Domain, Range), Acc) ->
t_collect_vars(?list(Contents, Termination, _), Acc) ->
ordsets:union(t_collect_vars(Contents, Acc), t_collect_vars(Termination, []));
t_collect_vars(?product(Types), Acc) ->
- lists:foldl(fun(T, TmpAcc) -> t_collect_vars(T, TmpAcc) end, Acc, Types);
+ t_collect_vars_list(Types, Acc);
t_collect_vars(?tuple(?any, ?any, ?any), Acc) ->
Acc;
t_collect_vars(?tuple(Types, _, _), Acc) ->
- lists:foldl(fun(T, TmpAcc) -> t_collect_vars(T, TmpAcc) end, Acc, Types);
+ t_collect_vars_list(Types, Acc);
t_collect_vars(?tuple_set(_) = TS, Acc) ->
- lists:foldl(fun(T, TmpAcc) -> t_collect_vars(T, TmpAcc) end, Acc,
- t_tuple_subtypes(TS));
-%% t_collect_vars(?opaque(_)=T, Acc) ->
-%% %% "Polymorphic opaque types not supported yet"
-%% t_collect_vars(t_opaque_structure(T), Acc);
+ t_collect_vars_list(t_tuple_subtypes(TS), Acc);
+t_collect_vars(?map(_) = Map, Acc0) ->
+ Acc = t_collect_vars_list(map_keys(Map), Acc0),
+ t_collect_vars_list(map_values(Map), Acc);
+t_collect_vars(?opaque(Set), Acc) ->
+ %% Assume variables in 'args' are also present i 'struct'
+ t_collect_vars_list([O#opaque.struct || O <- set_to_list(Set)], Acc);
+t_collect_vars(?union(List), Acc) ->
+ t_collect_vars_list(List, Acc);
t_collect_vars(_, Acc) ->
Acc.
+t_collect_vars_list([T|Ts], Acc0) ->
+ Acc = t_collect_vars(T, Acc0),
+ t_collect_vars_list(Ts, Acc);
+t_collect_vars_list([], Acc) -> Acc.
%%=============================================================================
%%
@@ -2145,6 +2167,7 @@ t_from_term(T) when is_function(T) ->
{arity, Arity} = erlang:fun_info(T, arity),
t_fun(Arity, t_any());
t_from_term(T) when is_integer(T) -> t_integer(T);
+t_from_term(T) when is_map(T) -> t_map();
t_from_term(T) when is_pid(T) -> t_pid();
t_from_term(T) when is_port(T) -> t_port();
t_from_term(T) when is_reference(T) -> t_reference();
@@ -2442,8 +2465,8 @@ sup_opaque(List) ->
?opaque(ordsets:from_list(L)).
sup_opaq(L0) ->
- L1 = [{{Mod,Name}, T} ||
- #opaque{mod = Mod, name = Name}=T <- L0],
+ L1 = [{{Mod,Name,Args}, T} ||
+ #opaque{mod = Mod, name = Name, args = Args}=T <- L0],
F = family(L1),
[supl(Ts) || {_, Ts} <- F].
@@ -2547,7 +2570,7 @@ force_union(T = ?function(_, _)) -> ?function_union(T);
force_union(T = ?identifier(_)) -> ?identifier_union(T);
force_union(T = ?list(_, _, _)) -> ?list_union(T);
force_union(T = ?nil) -> ?list_union(T);
-force_union(T = ?number(_,_)) -> ?number_union(T);
+force_union(T = ?number(_, _)) -> ?number_union(T);
force_union(T = ?opaque(_)) -> ?opaque_union(T);
force_union(T = ?remote(_)) -> ?remote_union(T);
force_union(T = ?map(_)) -> ?map_union(T);
@@ -2809,31 +2832,35 @@ inf_collect(_T1, [], _Opaques, OpL) ->
OpL.
combine(S, T1, T2) ->
- #opaque{mod = Mod1, name = Name1} = T1,
- #opaque{mod = Mod2, name = Name2} = T2,
- case {Mod1, Name1} =:= {Mod2, Name2} of
- true -> [comb(Mod1, Name1, S, T1)];
- false -> [comb(Mod1, Name1, S, T1), comb(Mod2, Name2, S, T2)]
+ #opaque{mod = Mod1, name = Name1, args = Args1} = T1,
+ #opaque{mod = Mod2, name = Name2, args = Args2} = T2,
+ case is_same_type_name({Mod1, Name1, Args1}, {Mod2, Name2, Args2}) of
+ true -> [comb(Mod1, Name1, Args1, S, T1)];
+ false -> [comb(Mod1, Name1, Args1, S, T1), comb(Mod2, Name2, Args2, S, T2)]
end.
-comb(Mod, Name, S, T) ->
- case is_same_name(Mod, Name, S) of
+comb(Mod, Name, Args, S, T) ->
+ case is_same_name(Mod, Name, Args, S) of
true -> S;
false -> T#opaque{struct = S}
end.
-is_same_name(Mod, Name, ?opaque([#opaque{mod = Mod, name = Name}])) -> true;
-is_same_name(_Mod, _Name, _Opaque) -> false.
+is_same_name(Mod1, Name1, Args1,
+ ?opaque([#opaque{mod = Mod2, name = Name2, args = Args2}])) ->
+ is_same_type_name({Mod1, Name1, Args1}, {Mod2, Name2, Args2});
+is_same_name(_, _, _, _) -> false.
%% Combining two lists this way can be very time consuming...
+%% Note: two parameterized opaque types are not the same if their
+%% actual parameters differ
inf_opaque(Set1, Set2, Opaques) ->
List1 = inf_look_up(Set1, 1, Opaques),
List2 = inf_look_up(Set2, 2, Opaques),
List0 = [combine(Inf, T1, T2) ||
- {Is1, ModName1, T1} <- List1,
- {Is2, ModName2, T2} <- List2,
- not t_is_none(Inf = inf_opaque_types(Is1, ModName1, T1,
- Is2, ModName2, T2,
+ {Is1, ModNameArgs1, T1} <- List1,
+ {Is2, ModNameArgs2, T2} <- List2,
+ not t_is_none(Inf = inf_opaque_types(Is1, ModNameArgs1, T1,
+ Is2, ModNameArgs2, T2,
Opaques))],
List = lists:sort(lists:append(List0)),
sup_opaque(List).
@@ -2841,18 +2868,22 @@ inf_opaque(Set1, Set2, Opaques) ->
%% Optimization: do just one lookup.
inf_look_up(Set, Pos, Opaques) ->
[{Opaques =:= 'universe' orelse inf_is_opaque_type2(T, Pos, Opaques),
- {M, N}, T} ||
- #opaque{mod = M, name = N} = T <- set_to_list(Set)].
+ {M, N, Args}, T} ||
+ #opaque{mod = M, name = N, args = Args} = T <- set_to_list(Set)].
inf_is_opaque_type2(T, Pos, {match, Opaques}) ->
is_opaque_type2(T, Opaques) orelse throw(Pos);
inf_is_opaque_type2(T, _Pos, Opaques) ->
is_opaque_type2(T, Opaques).
-inf_opaque_types(IsOpaque1, ModName1, T1, IsOpaque2, ModName2, T2, Opaques) ->
+inf_opaque_types(IsOpaque1, ModNameArgs1, T1,
+ IsOpaque2, ModNameArgs2, T2, Opaques) ->
#opaque{struct = S1}=T1,
#opaque{struct = S2}=T2,
- case Opaques =:= 'universe' orelse ModName1 =:= ModName2 of
+ case
+ Opaques =:= 'universe' orelse
+ is_same_type_name(ModNameArgs1, ModNameArgs2)
+ of
true -> t_inf(S1, S2, Opaques);
false ->
case {IsOpaque1, IsOpaque2} of
@@ -3018,13 +3049,13 @@ findfirst(N1, N2, U1, B1, U2, B2) ->
%% to types. Hans Bolinder suggested the use of lists of Key-Value pairs for
%% this data structure and measurements showed a non-trivial speedup when using
%% them for operations within this module (e.g. in t_unify/2). However, there
-%% is code outside erl_types that still passes a dict() in the 2nd argument.
+%% is code outside erl_types that still passes a dict:dict() in the 2nd argument.
%% So, for the time being, this module provides a t_subst/2 function for these
%% external calls and a clone of it (t_subst_kv/2) which is used from all calls
%% from within this module. This code duplication needs to be eliminated at
%% some point.
--spec t_subst(erl_type(), dict()) -> erl_type().
+-spec t_subst(erl_type(), dict:dict(atom(), erl_type())) -> erl_type().
t_subst(T, Dict) ->
case t_has_var(T) of
@@ -3060,11 +3091,16 @@ t_subst_dict(?tuple(Elements, _Arity, _Tag), Dict) ->
t_tuple([t_subst_dict(E, Dict) || E <- Elements]);
t_subst_dict(?tuple_set(_) = TS, Dict) ->
t_sup([t_subst_dict(T, Dict) || T <- t_tuple_subtypes(TS)]);
-%% t_subst_dict(?opaque(Es), Dict) ->
-%% %% "Polymorphic opaque types not supported yet"
-%% List = [Opaque#opaque{struct = t_subst_dict(S, Dict)} ||
-%% Opaque = #opaque{struct = S} <- set_to_list(Es)],
-%% ?opaque(ordsets:from_list(List));
+t_subst_dict(?map(Pairs), Dict) ->
+ ?map([{t_subst_dict(K, Dict), t_subst_dict(V, Dict)} ||
+ {K, V} <- Pairs]);
+t_subst_dict(?opaque(Es), Dict) ->
+ List = [Opaque#opaque{args = [t_subst_dict(Arg, Dict) || Arg <- Args],
+ struct = t_subst_dict(S, Dict)} ||
+ Opaque = #opaque{args = Args, struct = S} <- set_to_list(Es)],
+ ?opaque(ordsets:from_list(List));
+t_subst_dict(?union(List), Dict) ->
+ ?union([t_subst_dict(E, Dict) || E <- List]);
t_subst_dict(T, _Dict) ->
T.
@@ -3107,11 +3143,16 @@ t_subst_aux(?tuple(Elements, _Arity, _Tag), VarMap) ->
t_tuple([t_subst_aux(E, VarMap) || E <- Elements]);
t_subst_aux(?tuple_set(_) = TS, VarMap) ->
t_sup([t_subst_aux(T, VarMap) || T <- t_tuple_subtypes(TS)]);
-%% t_subst_aux(?opaque(Es), VarMap) ->
-%% %% "Polymorphic opaque types not supported yet"
-%% List = [Opaque#opaque{struct = t_subst_aux(S, VarMap)} ||
-%% Opaque = #opaque{struct = S} <- set_to_list(Es)],
-%% ?opaque(ordsets:from_list(List));
+t_subst_aux(?map(Pairs), VarMap) ->
+ ?map([{t_subst_aux(K, VarMap), t_subst_aux(V, VarMap)} ||
+ {K, V} <- Pairs]);
+t_subst_aux(?opaque(Es), VarMap) ->
+ List = [Opaque#opaque{args = [t_subst_aux(Arg, VarMap) || Arg <- Args],
+ struct = t_subst_aux(S, VarMap)} ||
+ Opaque = #opaque{args = Args, struct = S} <- set_to_list(Es)],
+ ?opaque(ordsets:from_list(List));
+t_subst_aux(?union(List), VarMap) ->
+ ?union([t_subst_aux(E, VarMap) || E <- List]);
t_subst_aux(T, _VarMap) ->
T.
@@ -3232,15 +3273,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}, Opaques) ->
+is_opaque_type2(#opaque{mod = Mod1, name = Name1, args = Args1}, Opaques) ->
F1 = fun(?opaque(Es)) ->
- F2 = fun(#opaque{mod = Mod, name = Name}) ->
- Mod1 =:= Mod andalso Name1 =:= Name
+ F2 = fun(#opaque{mod = Mod, name = Name, args = Args}) ->
+ is_type_name(Mod1, Name1, Args1, Mod, Name, Args)
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_same_type_name2(Mod1, Name1, Args1, Mod2, Name2, Args2).
+
%% Two functions since t_unify is not symmetric.
unify_tuple_set_and_tuple1(?tuple_set([{Arity, List}]),
?tuple(Elements2, Arity, _), VarMap) ->
@@ -3535,6 +3581,8 @@ t_subtract(?product(Elements1) = T1, ?product(Elements2)) ->
_ -> T1
end
end;
+t_subtract(?map(_) = T, _) -> % XXX: very crude; will probably need refinement
+ T;
t_subtract(?product(P1), _) ->
?product(P1);
t_subtract(T, ?product(_)) ->
@@ -3675,7 +3723,7 @@ t_unopaque(T) ->
t_unopaque(?opaque(_) = T, Opaques) ->
case Opaques =:= 'universe' orelse is_opaque_type(T, Opaques) of
true -> t_unopaque(t_opaque_structure(T), Opaques);
- false -> T % XXX: needs revision for parametric opaque data types
+ false -> T
end;
t_unopaque(?list(ElemT, Termination, Sz), Opaques) ->
?list(t_unopaque(ElemT, Opaques), t_unopaque(Termination, Opaques), Sz);
@@ -3695,11 +3743,12 @@ t_unopaque(?union([A,B,F,I,L,N,T,M,O,R,Map]), Opaques) ->
UL = t_unopaque(L, Opaques),
UT = t_unopaque(T, Opaques),
UF = t_unopaque(F, Opaques),
+ UMap = t_unopaque(Map, Opaques),
{OF,UO} = case t_unopaque(O, Opaques) of
?opaque(_) = O1 -> {O1, []};
Type -> {?none, [Type]}
end,
- t_sup([?union([A,B,UF,I,UL,N,UT,M,OF,R,Map])|UO]);
+ t_sup([?union([A,B,UF,I,UL,N,UT,M,OF,R,UMap])|UO]);
t_unopaque(T, _) ->
T.
@@ -3759,7 +3808,7 @@ t_limit_k(T, _K) -> T.
%%
%%============================================================================
--spec t_abstract_records(erl_type(), dict()) -> erl_type().
+-spec t_abstract_records(erl_type(), type_table()) -> erl_type().
t_abstract_records(?list(Contents, Termination, Size), RecDict) ->
case t_abstract_records(Contents, RecDict) of
@@ -3839,7 +3888,7 @@ t_map(Fun, T) ->
t_to_string(T) ->
t_to_string(T, dict:new()).
--spec t_to_string(erl_type(), dict()) -> string().
+-spec t_to_string(erl_type(), type_table()) -> string().
t_to_string(?any, _RecDict) ->
"any()";
@@ -3885,8 +3934,8 @@ t_to_string(?identifier(Set), _RecDict) ->
string:join([flat_format("~w()", [T]) || T <- set_to_list(Set)], " | ")
end;
t_to_string(?opaque(Set), RecDict) ->
- string:join([opaque_type(Mod, Name, S, RecDict) ||
- #opaque{mod = Mod, name = Name, struct = S}
+ string:join([opaque_type(Mod, Name, Args, S, RecDict) ||
+ #opaque{mod = Mod, name = Name, struct = S, args = Args}
<- set_to_list(Set)],
" | ");
t_to_string(?matchstate(Pres, Slots), RecDict) ->
@@ -4025,7 +4074,7 @@ record_fields_to_string([F|Fs], [{FName, _DefType}|FDefs], RecDict, Acc) ->
record_fields_to_string([], [], _RecDict, Acc) ->
lists:reverse(Acc).
--spec record_field_diffs_to_string(erl_type(), dict()) -> string().
+-spec record_field_diffs_to_string(erl_type(), type_table()) -> string().
record_field_diffs_to_string(?tuple([_|Fs], Arity, Tag), RecDict) ->
[TagAtom] = atom_vals(Tag),
@@ -4059,11 +4108,14 @@ union_sequence(Types, RecDict) ->
string:join(List, " | ").
-ifdef(DEBUG).
-opaque_type(Mod, Name, S, RecDict) ->
- opaque_name(Mod, Name, t_to_string(S, RecDict)).
+opaque_type(Mod, Name, _Args, S, RecDict) ->
+ ArgsString = comma_sequence(_Args, RecDict),
+ String = t_to_string(S, RecDict),
+ opaque_name(Mod, Name, ArgsString) ++ "[" ++ String ++ "]".
-else.
-opaque_type(Mod, Name, _S, _RecDict) ->
- opaque_name(Mod, Name, "").
+opaque_type(Mod, Name, Args, _S, RecDict) ->
+ ArgsString = comma_sequence(Args, RecDict),
+ opaque_name(Mod, Name, ArgsString).
-endif.
opaque_name(Mod, Name, Extra) ->
@@ -4071,11 +4123,16 @@ opaque_name(Mod, Name, Extra) ->
flat_format("~s(~s)", [S, Extra]).
mod_name(Mod, Name) ->
- case is_opaque_builtin(Mod, Name) of
+ case is_obsolete_opaque_builtin(Mod, Name) of
true -> flat_format("~w", [Name]);
false -> flat_format("~w:~w", [Mod, Name])
end.
+is_obsolete_opaque_builtin(digraph, digraph) -> true;
+is_obsolete_opaque_builtin(gb_sets, gb_set) -> true;
+is_obsolete_opaque_builtin(gb_trees, gb_tree) -> true;
+is_obsolete_opaque_builtin(_, _) -> false.
+
%%=============================================================================
%%
%% Build a type from parse forms.
@@ -4087,19 +4144,20 @@ mod_name(Mod, Name) ->
t_from_form(Form) ->
t_from_form(Form, dict:new()).
--spec t_from_form(parse_form(), dict()) -> erl_type().
+-spec t_from_form(parse_form(), type_table()) -> erl_type().
t_from_form(Form, RecDict) ->
t_from_form(Form, RecDict, dict:new()).
--spec t_from_form(parse_form(), dict(), dict()) -> erl_type().
+-spec t_from_form(parse_form(), type_table(), var_table()) -> erl_type().
t_from_form(Form, RecDict, VarDict) ->
{T, _R} = t_from_form(Form, [], RecDict, VarDict),
T.
--type type_names() :: [{'type' | 'opaque' | 'record', atom()}].
--spec t_from_form(parse_form(), type_names(), dict(), dict()) ->
+-type type_names() :: [type_key() | record_key()].
+
+-spec t_from_form(parse_form(), type_names(), type_table(), var_table()) ->
{erl_type(), type_names()}.
t_from_form({var, _L, '_'}, _TypeNames, _RecDict, _VarDict) ->
@@ -4138,8 +4196,8 @@ t_from_form({type, _L, any, []}, _TypeNames, _RecDict, _VarDict) ->
{t_any(), []};
t_from_form({type, _L, arity, []}, _TypeNames, _RecDict, _VarDict) ->
{t_arity(), []};
-t_from_form({type, _L, array, []}, _TypeNames, _RecDict, _VarDict) ->
- {t_array(), []};
+t_from_form({type, _L, array, []}, TypeNames, RecDict, VarDict) ->
+ builtin_type(array, t_array(), TypeNames, RecDict, VarDict);
t_from_form({type, _L, atom, []}, _TypeNames, _RecDict, _VarDict) ->
{t_atom(), []};
t_from_form({type, _L, binary, []}, _TypeNames, _RecDict, _VarDict) ->
@@ -4161,10 +4219,10 @@ t_from_form({type, _L, byte, []}, _TypeNames, _RecDict, _VarDict) ->
{t_byte(), []};
t_from_form({type, _L, char, []}, _TypeNames, _RecDict, _VarDict) ->
{t_char(), []};
-t_from_form({type, _L, dict, []}, _TypeNames, _RecDict, _VarDict) ->
- {t_dict(), []};
-t_from_form({type, _L, digraph, []}, _TypeNames, _RecDict, _VarDict) ->
- {t_digraph(), []};
+t_from_form({type, _L, dict, []}, TypeNames, RecDict, VarDict) ->
+ builtin_type(dict, t_dict(), TypeNames, RecDict, VarDict);
+t_from_form({type, _L, digraph, []}, TypeNames, RecDict, VarDict) ->
+ builtin_type(digraph, t_digraph(), TypeNames, RecDict, VarDict);
t_from_form({type, _L, float, []}, _TypeNames, _RecDict, _VarDict) ->
{t_float(), []};
t_from_form({type, _L, function, []}, _TypeNames, _RecDict, _VarDict) ->
@@ -4180,10 +4238,10 @@ t_from_form({type, _L, 'fun', [{type, _, product, Domain}, Range]},
{L, R1} = list_from_form(Domain, TypeNames, RecDict, VarDict),
{T, R2} = t_from_form(Range, TypeNames, RecDict, VarDict),
{t_fun(L, T), R1 ++ R2};
-t_from_form({type, _L, gb_set, []}, _TypeNames, _RecDict, _VarDict) ->
- {t_gb_set(), []};
-t_from_form({type, _L, gb_tree, []}, _TypeNames, _RecDict, _VarDict) ->
- {t_gb_tree(), []};
+t_from_form({type, _L, gb_set, []}, TypeNames, RecDict, VarDict) ->
+ builtin_type(gb_set, t_gb_set(), TypeNames, RecDict, VarDict);
+t_from_form({type, _L, gb_tree, []}, TypeNames, RecDict, VarDict) ->
+ builtin_type(gb_tree, t_gb_tree(), TypeNames, RecDict, VarDict);
t_from_form({type, _L, identifier, []}, _TypeNames, _RecDict, _VarDict) ->
{t_identifier(), []};
t_from_form({type, _L, integer, []}, _TypeNames, _RecDict, _VarDict) ->
@@ -4197,8 +4255,8 @@ t_from_form({type, _L, list, []}, _TypeNames, _RecDict, _VarDict) ->
t_from_form({type, _L, list, [Type]}, TypeNames, RecDict, VarDict) ->
{T, R} = t_from_form(Type, TypeNames, RecDict, VarDict),
{t_list(T), R};
-t_from_form({type, _L, map, _}, _TypeNames, _RecDict, _VarDict) ->
- {t_map([]), []};
+t_from_form({type, _L, map, _}, TypeNames, RecDict, VarDict) ->
+ builtin_type(map, t_map([]), TypeNames, RecDict, VarDict);
t_from_form({type, _L, mfa, []}, _TypeNames, _RecDict, _VarDict) ->
{t_mfa(), []};
t_from_form({type, _L, module, []}, _TypeNames, _RecDict, _VarDict) ->
@@ -4256,8 +4314,8 @@ t_from_form({type, _L, maybe_improper_list, [Content, Termination]},
t_from_form({type, _L, product, Elements}, TypeNames, RecDict, VarDict) ->
{L, R} = list_from_form(Elements, TypeNames, RecDict, VarDict),
{t_product(L), R};
-t_from_form({type, _L, queue, []}, _TypeNames, _RecDict, _VarDict) ->
- {t_queue(), []};
+t_from_form({type, _L, queue, []}, TypeNames, RecDict, VarDict) ->
+ builtin_type(queue, t_queue(), TypeNames, RecDict, VarDict);
t_from_form({type, _L, range, [From, To]} = Type,
_TypeNames, _RecDict, _VarDict) ->
case {erl_eval:partial_eval(From), erl_eval:partial_eval(To)} of
@@ -4269,14 +4327,14 @@ t_from_form({type, _L, record, [Name|Fields]}, TypeNames, RecDict, VarDict) ->
record_from_form(Name, Fields, TypeNames, RecDict, VarDict);
t_from_form({type, _L, reference, []}, _TypeNames, _RecDict, _VarDict) ->
{t_reference(), []};
-t_from_form({type, _L, set, []}, _TypeNames, _RecDict, _VarDict) ->
- {t_set(), []};
+t_from_form({type, _L, set, []}, TypeNames, RecDict, VarDict) ->
+ builtin_type(set, t_set(), TypeNames, RecDict, VarDict);
t_from_form({type, _L, string, []}, _TypeNames, _RecDict, _VarDict) ->
{t_string(), []};
t_from_form({type, _L, term, []}, _TypeNames, _RecDict, _VarDict) ->
{t_any(), []};
-t_from_form({type, _L, tid, []}, _TypeNames, _RecDict, _VarDict) ->
- {t_tid(), []};
+t_from_form({type, _L, tid, []}, TypeNames, RecDict, VarDict) ->
+ builtin_type(tid, t_tid(), TypeNames, RecDict, VarDict);
t_from_form({type, _L, timeout, []}, _TypeNames, _RecDict, _VarDict) ->
{t_timeout(), []};
t_from_form({type, _L, tuple, any}, _TypeNames, _RecDict, _VarDict) ->
@@ -4288,61 +4346,67 @@ t_from_form({type, _L, union, Args}, TypeNames, RecDict, VarDict) ->
{L, R} = list_from_form(Args, TypeNames, RecDict, VarDict),
{t_sup(L), R};
t_from_form({type, _L, Name, Args}, TypeNames, RecDict, VarDict) ->
+ type_from_form(Name, Args, TypeNames, RecDict, VarDict);
+t_from_form({opaque, _L, Name, {Mod, Args, Rep}}, _TypeNames,
+ _RecDict, _VarDict) ->
+ {t_opaque(Mod, Name, Args, Rep), []}.
+
+builtin_type(Name, Type, TypeNames, RecDict, VarDict) ->
+ case lookup_type(Name, 0, RecDict) of
+ {_, {_M, _T, _A}} ->
+ type_from_form(Name, [], TypeNames, RecDict, VarDict);
+ error ->
+ {Type, []}
+ end.
+
+type_from_form(Name, Args, TypeNames, RecDict, VarDict) ->
ArgsLen = length(Args),
+ ArgTypes = forms_to_types(Args, TypeNames, RecDict, VarDict),
case lookup_type(Name, ArgsLen, RecDict) of
{type, {_Module, Type, ArgNames}} ->
- case can_unfold_more({type, Name}, TypeNames) of
+ TypeName = {type, Name, ArgsLen},
+ case can_unfold_more(TypeName, TypeNames) of
true ->
- List = lists:zipwith(
- fun(ArgName, ArgType) ->
- {Ttemp, _R} = t_from_form(ArgType, TypeNames,
- RecDict, VarDict),
- {ArgName, Ttemp}
- end,
- ArgNames, Args),
+ List = lists:zip(ArgNames, ArgTypes),
TmpVarDict = dict:from_list(List),
- {T, R} = t_from_form(Type, [{type, Name}|TypeNames],
+ {T, R} = t_from_form(Type, [TypeName|TypeNames],
RecDict, TmpVarDict),
- case lists:member({type, Name}, R) of
+ case lists:member(TypeName, R) of
true -> {t_limit(T, ?REC_TYPE_LIMIT), R};
false -> {T, R}
end;
- false -> {t_any(), [{type, Name}]}
+ false -> {t_any(), [TypeName]}
end;
{opaque, {Module, Type, ArgNames}} ->
+ TypeName = {opaque, Name, ArgsLen},
{Rep, Rret} =
- case can_unfold_more({opaque, Name}, TypeNames) of
+ case can_unfold_more(TypeName, TypeNames) of
true ->
- List = lists:zipwith(
- fun(ArgName, ArgType) ->
- {Ttemp, _R} = t_from_form(ArgType, TypeNames,
- RecDict, VarDict),
- {ArgName, Ttemp}
- end,
- ArgNames, Args),
+ List = lists:zip(ArgNames, ArgTypes),
TmpVarDict = dict:from_list(List),
- {T, R} = t_from_form(Type, [{opaque, Name}|TypeNames],
+ {T, R} = t_from_form(Type, [TypeName|TypeNames],
RecDict, TmpVarDict),
- case lists:member({opaque, Name}, R) of
+ case lists:member(TypeName, R) of
true -> {t_limit(T, ?REC_TYPE_LIMIT), R};
false -> {T, R}
end;
- false -> {t_any(), [{opaque, Name}]}
+ false -> {t_any(), [TypeName]}
end,
- Tret = t_from_form({opaque, -1, Name, {Module, Args, Rep}},
- RecDict, VarDict),
- {Tret, Rret};
+ Args2 = [subst_all_vars_to_any(ArgType) || ArgType <- ArgTypes],
+ {skip_opaque_alias(Rep, Module, Name, Args2), Rret};
error ->
Msg = io_lib:format("Unable to find type ~w/~w\n", [Name, ArgsLen]),
throw({error, Msg})
- end;
-t_from_form({opaque, _L, Name, {Mod, Args, Rep}}, _TypeNames,
- _RecDict, _VarDict) ->
- case Args of
- [] -> {t_opaque(Mod, Name, Args, Rep), []};
- _ -> throw({error, "Polymorphic opaque types not supported yet"})
end.
+forms_to_types(Forms, TypeNames, RecDict, VarDict) ->
+ {Types, _} = list_from_form(Forms, TypeNames, RecDict, VarDict),
+ Types.
+
+skip_opaque_alias(?opaque(_) = T, _Mod, _Name, _Args) -> T;
+skip_opaque_alias(T, Module, Name, Args) ->
+ t_opaque(Module, Name, Args, T).
+
record_from_form({atom, _, Name}, ModFields, TypeNames, RecDict, VarDict) ->
case can_unfold_more({record, Name}, TypeNames) of
true ->
@@ -4403,8 +4467,10 @@ build_field_dict([], _TypeNames, _RecDict, _VarDict, Acc) ->
get_mod_record([{FieldName, DeclType}|Left1],
[{FieldName, ModType}|Left2], Acc) ->
- case t_is_var(ModType) orelse t_is_remote(ModType) orelse
- t_is_subtype(ModType, DeclType) of
+ ModTypeNoVars = subst_all_vars_to_any(ModType),
+ case
+ t_is_remote(ModTypeNoVars) orelse t_is_subtype(ModTypeNoVars, DeclType)
+ of
false -> {error, FieldName};
true -> get_mod_record(Left1, Left2, [{FieldName, ModType}|Acc])
end;
@@ -4561,7 +4627,7 @@ is_erl_type(?unit) -> true;
is_erl_type(#c{}) -> true;
is_erl_type(_) -> false.
--spec lookup_record(atom(), dict()) ->
+-spec lookup_record(atom(), type_table()) ->
'error' | {'ok', [{atom(), parse_form() | erl_type()}]}.
lookup_record(Tag, RecDict) when is_atom(Tag) ->
@@ -4576,7 +4642,8 @@ lookup_record(Tag, RecDict) when is_atom(Tag) ->
error
end.
--spec lookup_record(atom(), arity(), dict()) -> 'error' | {'ok', [{atom(), erl_type()}]}.
+-spec lookup_record(atom(), arity(), type_table()) ->
+ 'error' | {'ok', [{atom(), erl_type()}]}.
lookup_record(Tag, Arity, RecDict) when is_atom(Tag) ->
case dict:find({record, Tag}, RecDict) of
@@ -4595,7 +4662,8 @@ lookup_type(Name, Arity, RecDict) ->
{ok, Found} -> {type, Found}
end.
--spec type_is_defined('type' | 'opaque', atom(), arity(), dict()) -> boolean().
+-spec type_is_defined('type' | 'opaque', atom(), arity(), type_table()) ->
+ boolean().
type_is_defined(TypeOrOpaque, Name, Arity, RecDict) ->
dict:is_key({TypeOrOpaque, Name, Arity}, RecDict).
@@ -4627,6 +4695,36 @@ do_opaque(?union(List) = Type, Opaques, Pred) ->
do_opaque(Type, _Opaques, Pred) ->
Pred(Type).
+is_same_type_name(ModNameArgs, ModNameArgs) -> true;
+is_same_type_name({Mod, Name, Args1}, {Mod, Name, Args2}) ->
+ all_any(Args1) orelse all_any(Args2);
+is_same_type_name({Mod1, Name1, Args1}, {Mod2, Name2, Args2}) ->
+ is_same_type_name2(Mod1, Name1, Args1, Mod2, Name2, Args2).
+
+all_any([]) -> true;
+all_any([T|L]) ->
+ t_is_any(T) andalso all_any(L);
+all_any(_) -> false.
+
+%% Compatibility. In Erlang/OTP 17 the pre-defined opaque types
+%% digraph() and so on can be used, but there are also new types such
+%% as digraph:graph() with the exact same meaning. In Erlang/OTP R18.0
+%% all but the last clause can be removed.
+
+is_same_type_name2(digraph, digraph, [], digraph, graph, []) -> true;
+is_same_type_name2(digraph, graph, [], digraph, digraph, []) -> true;
+is_same_type_name2(gb_sets, gb_set, [], gb_sets, set, [_]) -> true;
+is_same_type_name2(gb_sets, set, [_], gb_sets, gb_set, []) -> true;
+is_same_type_name2(gb_trees, gb_tree, [], gb_trees, tree, [_, _]) -> true;
+is_same_type_name2(gb_trees, tree, [_, _], gb_trees, gb_tree, []) -> true;
+is_same_type_name2(_, _, _, _, _, _) -> false.
+
+map_keys(?map(Pairs)) ->
+ [K || {K, _} <- Pairs].
+
+map_values(?map(Pairs)) ->
+ [V || {_, V} <- Pairs].
+
%% -----------------------------------
%% Set
%%