aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHans Bolinder <[email protected]>2015-08-25 09:26:11 +0200
committerHans Bolinder <[email protected]>2015-08-25 09:26:11 +0200
commit330628d6fc93a05fdfa2f7a82e16b45d984f0bcf (patch)
tree3f1d9b0390228be634b657493fdacf008003cbbc
parente409bd71d9aec56dec9f111966d1ca99d2537c41 (diff)
parenta9f3424c8a04247a96e8bb5aed19b30276fcbad7 (diff)
downloadotp-330628d6fc93a05fdfa2f7a82e16b45d984f0bcf.tar.gz
otp-330628d6fc93a05fdfa2f7a82e16b45d984f0bcf.tar.bz2
otp-330628d6fc93a05fdfa2f7a82e16b45d984f0bcf.zip
Merge branch 'hb/dialyzer/recursive_opaque_types/OTP-12865' into maint
* hb/dialyzer/recursive_opaque_types/OTP-12865: dialyzer: Correct the timing of the phase called 'remote' dialyzer: Optimize expansion of parameters of opaque types dialyzer: Optimize the expansion of parameterized types somewhat dialyzer: Improve the handling of recursive parameterized opaque types dialyzer: Generalize an argument of erl_types:t_from_form()
-rw-r--r--lib/dialyzer/src/dialyzer_analysis_callgraph.erl7
-rw-r--r--lib/dialyzer/src/dialyzer_contracts.erl102
-rw-r--r--lib/dialyzer/src/dialyzer_utils.erl35
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/big_external_type.erl526
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/big_local_type.erl523
-rw-r--r--lib/hipe/cerl/erl_types.erl484
6 files changed, 1390 insertions, 287 deletions
diff --git a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl
index 76b43b6ff0..c57a22129c 100644
--- a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl
+++ b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl
@@ -167,9 +167,12 @@ analysis_start(Parent, Analysis, LegalWarnings) ->
TmpCServer2 =
dialyzer_codeserver:insert_temp_exported_types(MergedExpTypes,
TmpCServer1),
- TmpCServer3 = dialyzer_utils:process_record_remote_types(TmpCServer2),
?timing(State#analysis_state.timing_server, "remote",
- dialyzer_contracts:process_contract_remote_types(TmpCServer3))
+ begin
+ TmpCServer3 =
+ dialyzer_utils:process_record_remote_types(TmpCServer2),
+ dialyzer_contracts:process_contract_remote_types(TmpCServer3)
+ end)
catch
throw:{error, _ErrorMsg} = Error -> exit(Error)
end,
diff --git a/lib/dialyzer/src/dialyzer_contracts.erl b/lib/dialyzer/src/dialyzer_contracts.erl
index 1079c2e09b..7251de8b10 100644
--- a/lib/dialyzer/src/dialyzer_contracts.erl
+++ b/lib/dialyzer/src/dialyzer_contracts.erl
@@ -395,22 +395,21 @@ insert_constraints([], Dict) -> Dict.
store_tmp_contract(MFA, FileLine, {TypeSpec, Xtra}, SpecDict, RecordsDict) ->
%% io:format("contract from form: ~p\n", [TypeSpec]),
- {Module, _, _} = MFA,
- TmpContract = contract_from_form(TypeSpec, Module, RecordsDict, FileLine),
+ TmpContract = contract_from_form(TypeSpec, MFA, RecordsDict, FileLine),
%% io:format("contract: ~p\n", [TmpContract]),
dict:store(MFA, {FileLine, TmpContract, Xtra}, SpecDict).
-contract_from_form(Forms, Module, RecDict, FileLine) ->
- {CFuns, Forms1} = contract_from_form(Forms, Module, RecDict, FileLine, [], []),
+contract_from_form(Forms, MFA, RecDict, FileLine) ->
+ {CFuns, Forms1} = contract_from_form(Forms, MFA, RecDict, FileLine, [], []),
#tmp_contract{contract_funs = CFuns, forms = Forms1}.
-contract_from_form([{type, _, 'fun', [_, _]} = Form | Left], Module, RecDict,
+contract_from_form([{type, _, 'fun', [_, _]} = Form | Left], MFA, RecDict,
FileLine, TypeAcc, FormAcc) ->
TypeFun =
fun(ExpTypes, AllRecords) ->
NewType =
try
- from_form_with_check(Form, ExpTypes, Module, AllRecords)
+ from_form_with_check(Form, ExpTypes, MFA, AllRecords)
catch
throw:{error, Msg} ->
{File, Line} = FileLine,
@@ -423,55 +422,55 @@ contract_from_form([{type, _, 'fun', [_, _]} = Form | Left], Module, RecDict,
end,
NewTypeAcc = [TypeFun | TypeAcc],
NewFormAcc = [{Form, []} | FormAcc],
- contract_from_form(Left, Module, RecDict, FileLine, NewTypeAcc, NewFormAcc);
+ contract_from_form(Left, MFA, RecDict, FileLine, NewTypeAcc, NewFormAcc);
contract_from_form([{type, _L1, bounded_fun,
[{type, _L2, 'fun', [_, _]} = Form, Constr]}| Left],
- Module, RecDict, FileLine, TypeAcc, FormAcc) ->
+ MFA, RecDict, FileLine, TypeAcc, FormAcc) ->
TypeFun =
fun(ExpTypes, AllRecords) ->
{Constr1, VarDict} =
- process_constraints(Constr, Module, RecDict, ExpTypes, AllRecords),
- NewType = from_form_with_check(Form, ExpTypes, Module, AllRecords,
+ process_constraints(Constr, MFA, RecDict, ExpTypes, AllRecords),
+ NewType = from_form_with_check(Form, ExpTypes, MFA, AllRecords,
VarDict),
NewTypeNoVars = erl_types:subst_all_vars_to_any(NewType),
{NewTypeNoVars, Constr1}
end,
NewTypeAcc = [TypeFun | TypeAcc],
NewFormAcc = [{Form, Constr} | FormAcc],
- contract_from_form(Left, Module, RecDict, FileLine, NewTypeAcc, NewFormAcc);
-contract_from_form([], _Module, _RecDict, _FileLine, TypeAcc, FormAcc) ->
+ contract_from_form(Left, MFA, RecDict, FileLine, NewTypeAcc, NewFormAcc);
+contract_from_form([], _MFA, _RecDict, _FileLine, TypeAcc, FormAcc) ->
{lists:reverse(TypeAcc), lists:reverse(FormAcc)}.
-process_constraints(Constrs, Module, RecDict, ExpTypes, AllRecords) ->
- Init0 = initialize_constraints(Constrs, Module, RecDict, ExpTypes, AllRecords),
+process_constraints(Constrs, MFA, RecDict, ExpTypes, AllRecords) ->
+ Init0 = initialize_constraints(Constrs, MFA, RecDict, ExpTypes, AllRecords),
Init = remove_cycles(Init0),
- constraints_fixpoint(Init, Module, RecDict, ExpTypes, AllRecords).
+ constraints_fixpoint(Init, MFA, RecDict, ExpTypes, AllRecords).
-initialize_constraints(Constrs, Module, RecDict, ExpTypes, AllRecords) ->
- initialize_constraints(Constrs, Module, RecDict, ExpTypes, AllRecords, []).
+initialize_constraints(Constrs, MFA, RecDict, ExpTypes, AllRecords) ->
+ initialize_constraints(Constrs, MFA, RecDict, ExpTypes, AllRecords, []).
-initialize_constraints([], _Module, _RecDict, _ExpTypes, _AllRecords, Acc) ->
+initialize_constraints([], _MFA, _RecDict, _ExpTypes, _AllRecords, Acc) ->
Acc;
-initialize_constraints([Constr|Rest], Module, RecDict, ExpTypes, AllRecords, Acc) ->
+initialize_constraints([Constr|Rest], MFA, RecDict, ExpTypes, AllRecords, Acc) ->
case Constr of
{type, _, constraint, [{atom, _, is_subtype}, [Type1, Type2]]} ->
- T1 = final_form(Type1, ExpTypes, Module, AllRecords, dict:new()),
+ T1 = final_form(Type1, ExpTypes, MFA, AllRecords, dict:new()),
Entry = {T1, Type2},
- initialize_constraints(Rest, Module, RecDict, ExpTypes, AllRecords, [Entry|Acc]);
+ initialize_constraints(Rest, MFA, RecDict, ExpTypes, AllRecords, [Entry|Acc]);
{type, _, constraint, [{atom,_,Name}, List]} ->
N = length(List),
throw({error,
io_lib:format("Unsupported type guard ~w/~w\n", [Name, N])})
end.
-constraints_fixpoint(Constrs, Module, RecDict, ExpTypes, AllRecords) ->
+constraints_fixpoint(Constrs, MFA, RecDict, ExpTypes, AllRecords) ->
VarDict =
- constraints_to_dict(Constrs, Module, RecDict, ExpTypes, AllRecords, dict:new()),
- constraints_fixpoint(VarDict, Module, Constrs, RecDict, ExpTypes, AllRecords).
+ constraints_to_dict(Constrs, MFA, RecDict, ExpTypes, AllRecords, dict:new()),
+ constraints_fixpoint(VarDict, MFA, Constrs, RecDict, ExpTypes, AllRecords).
-constraints_fixpoint(OldVarDict, Module, Constrs, RecDict, ExpTypes, AllRecords) ->
+constraints_fixpoint(OldVarDict, MFA, Constrs, RecDict, ExpTypes, AllRecords) ->
NewVarDict =
- constraints_to_dict(Constrs, Module, RecDict, ExpTypes, AllRecords, OldVarDict),
+ constraints_to_dict(Constrs, MFA, RecDict, ExpTypes, AllRecords, OldVarDict),
case NewVarDict of
OldVarDict ->
DictFold =
@@ -481,33 +480,33 @@ constraints_fixpoint(OldVarDict, Module, Constrs, RecDict, ExpTypes, AllRecords)
FinalConstrs = dict:fold(DictFold, [], NewVarDict),
{FinalConstrs, NewVarDict};
_Other ->
- constraints_fixpoint(NewVarDict, Module, Constrs, RecDict, ExpTypes, AllRecords)
+ constraints_fixpoint(NewVarDict, MFA, Constrs, RecDict, ExpTypes, AllRecords)
end.
-final_form(Form, ExpTypes, Module, AllRecords, VarDict) ->
- from_form_with_check(Form, ExpTypes, Module, AllRecords, VarDict).
+final_form(Form, ExpTypes, MFA, AllRecords, VarDict) ->
+ from_form_with_check(Form, ExpTypes, MFA, AllRecords, VarDict).
-from_form_with_check(Form, ExpTypes, Module, AllRecords) ->
- erl_types:t_check_record_fields(Form, ExpTypes, Module, AllRecords),
- erl_types:t_from_form(Form, ExpTypes, Module, AllRecords).
+from_form_with_check(Form, ExpTypes, MFA, AllRecords) ->
+ from_form_with_check(Form, ExpTypes, MFA, AllRecords, dict:new()).
-from_form_with_check(Form, ExpTypes, Module, AllRecords, VarDict) ->
- erl_types:t_check_record_fields(Form, ExpTypes, Module, AllRecords,
+from_form_with_check(Form, ExpTypes, MFA, AllRecords, VarDict) ->
+ Site = {spec, MFA},
+ erl_types:t_check_record_fields(Form, ExpTypes, Site, AllRecords,
VarDict),
- erl_types:t_from_form(Form, ExpTypes, Module, AllRecords, VarDict).
+ erl_types:t_from_form(Form, ExpTypes, Site, AllRecords, VarDict).
-constraints_to_dict(Constrs, Module, RecDict, ExpTypes, AllRecords, VarDict) ->
+constraints_to_dict(Constrs, MFA, RecDict, ExpTypes, AllRecords, VarDict) ->
Subtypes =
- constraints_to_subs(Constrs, Module, RecDict, ExpTypes, AllRecords, VarDict, []),
+ constraints_to_subs(Constrs, MFA, RecDict, ExpTypes, AllRecords, VarDict, []),
insert_constraints(Subtypes, dict:new()).
-constraints_to_subs([], _Module, _RecDict, _ExpTypes, _AllRecords, _VarDict, Acc) ->
+constraints_to_subs([], _MFA, _RecDict, _ExpTypes, _AllRecords, _VarDict, Acc) ->
Acc;
-constraints_to_subs([C|Rest], Module, RecDict, ExpTypes, AllRecords, VarDict, Acc) ->
+constraints_to_subs([C|Rest], MFA, RecDict, ExpTypes, AllRecords, VarDict, Acc) ->
{T1, Form2} = C,
- T2 = final_form(Form2, ExpTypes, Module, AllRecords, VarDict),
+ T2 = final_form(Form2, ExpTypes, MFA, AllRecords, VarDict),
NewAcc = [{subtype, T1, T2}|Acc],
- constraints_to_subs(Rest, Module, RecDict, ExpTypes, AllRecords, VarDict, NewAcc).
+ constraints_to_subs(Rest, MFA, RecDict, ExpTypes, AllRecords, VarDict, NewAcc).
%% Replaces variables with '_' when necessary to break up cycles among
%% the constraints.
@@ -630,7 +629,7 @@ get_invalid_contract_warnings_funs([{MFA, {FileLine, Contract, _Xtra}}|Left],
{error, {extra_range, ExtraRanges, STRange}} ->
Warn =
case t_from_forms_without_remote(Contract#contract.forms,
- RecDict) of
+ MFA, RecDict) of
{ok, NoRemoteType} ->
CRet = erl_types:t_fun_range(NoRemoteType),
erl_types:t_is_subtype(ExtraRanges, CRet);
@@ -705,7 +704,7 @@ picky_contract_check(CSig0, Sig0, MFA, WarningInfo, Contract, RecDict, Acc) ->
end
end.
-extra_contract_warning({M, F, A}, WarningInfo, Contract, CSig, Sig, RecDict) ->
+extra_contract_warning(MFA, WarningInfo, Contract, CSig, Sig, RecDict) ->
%% We do not want to depend upon erl_types:t_to_string() possibly
%% hiding the contents of opaque types.
SigUnopaque = erl_types:t_unopaque(Sig),
@@ -717,11 +716,12 @@ extra_contract_warning({M, F, A}, WarningInfo, Contract, CSig, Sig, RecDict) ->
%% The only difference is in record fields containing 'undefined' or not.
IsUndefRecordFieldsRelated = SigString0 =:= ContractString0,
{IsRemoteTypesRelated, SubtypeRelation} =
- is_remote_types_related(Contract, CSig, Sig, RecDict),
+ is_remote_types_related(Contract, CSig, Sig, MFA, RecDict),
case IsUndefRecordFieldsRelated orelse IsRemoteTypesRelated of
true ->
no_warning;
false ->
+ {M, F, A} = MFA,
SigString = lists:flatten(dialyzer_utils:format_sig(Sig, RecDict)),
ContractString = contract_to_string(Contract),
{Tag, Msg} =
@@ -739,14 +739,15 @@ extra_contract_warning({M, F, A}, WarningInfo, Contract, CSig, Sig, RecDict) ->
{warning, {Tag, WarningInfo, Msg}}
end.
-is_remote_types_related(Contract, CSig, Sig, RecDict) ->
+is_remote_types_related(Contract, CSig, Sig, MFA, RecDict) ->
case erl_types:t_is_subtype(CSig, Sig) of
true ->
{false, contract_is_subtype};
false ->
case erl_types:t_is_subtype(Sig, CSig) of
true ->
- case t_from_forms_without_remote(Contract#contract.forms, RecDict) of
+ case t_from_forms_without_remote(Contract#contract.forms, MFA,
+ RecDict) of
{ok, NoRemoteTypeSig} ->
case blame_remote(CSig, NoRemoteTypeSig, Sig) of
true ->
@@ -762,13 +763,14 @@ is_remote_types_related(Contract, CSig, Sig, RecDict) ->
end
end.
-t_from_forms_without_remote([{FType, []}], RecDict) ->
- Type1 = erl_types:t_from_form_without_remote(FType, RecDict),
+t_from_forms_without_remote([{FType, []}], MFA, RecDict) ->
+ Site = {spec, MFA},
+ Type1 = erl_types:t_from_form_without_remote(FType, Site, RecDict),
{ok, erl_types:subst_all_vars_to_any(Type1)};
-t_from_forms_without_remote([{_FType, _Constrs}], _RecDict) ->
+t_from_forms_without_remote([{_FType, _Constrs}], _MFA, _RecDict) ->
%% 'When' constraints
unsupported;
-t_from_forms_without_remote(_Forms, _RecDict) ->
+t_from_forms_without_remote(_Forms, _MFA, _RecDict) ->
%% Lots of forms
unsupported.
diff --git a/lib/dialyzer/src/dialyzer_utils.erl b/lib/dialyzer/src/dialyzer_utils.erl
index b585646fde..7fe982a992 100644
--- a/lib/dialyzer/src/dialyzer_utils.erl
+++ b/lib/dialyzer/src/dialyzer_utils.erl
@@ -304,15 +304,16 @@ process_record_remote_types(CServer) ->
RecordFun =
fun(Key, Value) ->
case Key of
- {record, _Name} ->
+ {record, Name} ->
FieldFun =
- fun(_Arity, Fields) ->
- [{Name, Field,
+ fun(Arity, Fields) ->
+ Site = {record, {Module, Name, Arity}},
+ [{FieldName, Field,
erl_types:t_from_form(Field,
TempExpTypes,
- Module,
+ Site,
TempRecords1)}
- || {Name, Field, _} <- Fields]
+ || {FieldName, Field, _} <- Fields]
end,
{FileLine, Fields} = Value,
{FileLine, orddict:map(FieldFun, Fields)};
@@ -340,9 +341,10 @@ process_opaque_types(TempRecords, TempExpTypes) ->
RecordFun =
fun(Key, Value) ->
case Key of
- {opaque, _Name, _NArgs} ->
+ {opaque, Name, NArgs} ->
{{_Module, _FileLine, Form, _ArgNames}=F, _Type} = Value,
- Type = erl_types:t_from_form(Form, TempExpTypes, Module,
+ Site = {type, {Module, Name, NArgs}},
+ Type = erl_types:t_from_form(Form, TempExpTypes, Site,
TempRecords),
{F, Type};
_Other -> Value
@@ -355,25 +357,28 @@ process_opaque_types(TempRecords, TempExpTypes) ->
check_record_fields(Records, TempExpTypes) ->
CheckFun =
fun({Module, Element}) ->
- CheckForm = fun(F) ->
- erl_types:t_check_record_fields(F, TempExpTypes,
- Module, Records)
+ CheckForm = fun(Form, Site) ->
+ erl_types:t_check_record_fields(Form, TempExpTypes,
+ Site, Records)
end,
ElemFun =
fun({Key, Value}) ->
case Key of
- {record, _Name} ->
+ {record, Name} ->
FieldFun =
- fun({_Arity, Fields}) ->
- _ = [ok = CheckForm(Field) || {_, Field, _} <- Fields],
+ fun({Arity, Fields}) ->
+ Site = {record, {Module, Name, Arity}},
+ _ = [ok = CheckForm(Field, Site) ||
+ {_, Field, _} <- Fields],
ok
end,
{FileLine, Fields} = Value,
Fun = fun() -> lists:foreach(FieldFun, Fields) end,
msg_with_position(Fun, FileLine);
- {_OpaqueOrType, _Name, _} ->
+ {_OpaqueOrType, Name, NArgs} ->
+ Site = {type, {Module, Name, NArgs}},
{{_Module, FileLine, Form, _ArgNames}, _Type} = Value,
- Fun = fun() -> ok = CheckForm(Form) end,
+ Fun = fun() -> ok = CheckForm(Form, Site) end,
msg_with_position(Fun, FileLine)
end
end,
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/big_external_type.erl b/lib/dialyzer/test/opaque_SUITE_data/src/big_external_type.erl
new file mode 100644
index 0000000000..d286a378ed
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/big_external_type.erl
@@ -0,0 +1,526 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2001-2015. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%% A copy of small_SUITE_data/src/big_external_type.erl, where
+%%% abstract_expr() is opaque. The transformation of forms to types is
+%%% now much faster than it used to be, for this module.
+
+-module(big_external_type).
+
+-export([parse_form/1,parse_exprs/1,parse_term/1]).
+-export([normalise/1,tokens/1,tokens/2]).
+-export([inop_prec/1,preop_prec/1,func_prec/0,max_prec/0]).
+
+-export_type([abstract_clause/0, abstract_expr/0, abstract_form/0,
+ error_info/0]).
+
+%% Start of Abstract Format
+
+-type line() :: erl_anno:line().
+
+-export_type([af_record_index/0, af_record_field/1, af_record_name/0,
+ af_field_name/0, af_function_decl/0]).
+
+-export_type([af_module/0, af_export/0, af_import/0, af_fa_list/0,
+ af_compile/0, af_file/0, af_record_decl/0,
+ af_field_decl/0, af_wild_attribute/0,
+ af_record_update/1, af_catch/0, af_local_call/0,
+ af_remote_call/0, af_args/0, af_local_function/0,
+ af_remote_function/0, af_list_comprehension/0,
+ af_binary_comprehension/0, af_template/0,
+ af_qualifier_seq/0, af_qualifier/0, af_generator/0,
+ af_filter/0, af_block/0, af_if/0, af_case/0, af_try/0,
+ af_clause_seq/0, af_catch_clause_seq/0, af_receive/0,
+ af_local_fun/0, af_remote_fun/0, af_fun/0, af_query/0,
+ af_query_access/0, af_clause/0,
+ af_catch_clause/0, af_catch_pattern/0, af_catch_class/0,
+ af_body/0, af_guard_seq/0, af_guard/0, af_guard_test/0,
+ af_record_access/1, af_guard_call/0,
+ af_remote_guard_call/0, af_pattern/0, af_literal/0,
+ af_atom/0, af_lit_atom/1, af_integer/0, af_float/0,
+ af_string/0, af_match/1, af_variable/0,
+ af_anon_variable/0, af_tuple/1, af_nil/0, af_cons/1,
+ af_bin/1, af_binelement/1, af_binelement_size/0,
+ af_binary_op/1, af_binop/0, af_unary_op/1, af_unop/0]).
+
+-type abstract_form() :: ?MODULE:af_module()
+ | ?MODULE:af_export()
+ | ?MODULE:af_import()
+ | ?MODULE:af_compile()
+ | ?MODULE:af_file()
+ | ?MODULE:af_record_decl()
+ | ?MODULE:af_wild_attribute()
+ | ?MODULE:af_function_decl().
+
+-type af_module() :: {attribute, line(), module, module()}.
+
+-type af_export() :: {attribute, line(), export, ?MODULE:af_fa_list()}.
+
+-type af_import() :: {attribute, line(), import, ?MODULE:af_fa_list()}.
+
+-type af_fa_list() :: [{function(), arity()}].
+
+-type af_compile() :: {attribute, line(), compile, any()}.
+
+-type af_file() :: {attribute, line(), file, {string(), line()}}.
+
+-type af_record_decl() ::
+ {attribute, line(), record, ?MODULE:af_record_name(), [?MODULE:af_field_decl()]}.
+
+-type af_field_decl() :: {record_field, line(), ?MODULE:af_atom()}
+ | {record_field, line(), ?MODULE:af_atom(), ?MODULE:abstract_expr()}.
+
+%% Types and specs, among other things...
+-type af_wild_attribute() :: {attribute, line(), ?MODULE:af_atom(), any()}.
+
+-type af_function_decl() ::
+ {function, line(), function(), arity(), ?MODULE:af_clause_seq()}.
+
+-opaque abstract_expr() :: ?MODULE:af_literal()
+ | ?MODULE:af_match(?MODULE:abstract_expr())
+ | ?MODULE:af_variable()
+ | ?MODULE:af_tuple(?MODULE:abstract_expr())
+ | ?MODULE:af_nil()
+ | ?MODULE:af_cons(?MODULE:abstract_expr())
+ | ?MODULE:af_bin(?MODULE:abstract_expr())
+ | ?MODULE:af_binary_op(?MODULE:abstract_expr())
+ | ?MODULE:af_unary_op(?MODULE:abstract_expr())
+ | ?MODULE:af_record_access(?MODULE:abstract_expr())
+ | ?MODULE:af_record_update(?MODULE:abstract_expr())
+ | ?MODULE:af_record_index()
+ | ?MODULE:af_record_field(?MODULE:abstract_expr())
+ | ?MODULE:af_catch()
+ | ?MODULE:af_local_call()
+ | ?MODULE:af_remote_call()
+ | ?MODULE:af_list_comprehension()
+ | ?MODULE:af_binary_comprehension()
+ | ?MODULE:af_block()
+ | ?MODULE:af_if()
+ | ?MODULE:af_case()
+ | ?MODULE:af_try()
+ | ?MODULE:af_receive()
+ | ?MODULE:af_local_fun()
+ | ?MODULE:af_remote_fun()
+ | ?MODULE:af_fun()
+ | ?MODULE:af_query()
+ | ?MODULE:af_query_access().
+
+-type af_record_update(T) :: {record,
+ line(),
+ ?MODULE:abstract_expr(),
+ ?MODULE:af_record_name(),
+ [?MODULE:af_record_field(T)]}.
+
+-type af_catch() :: {'catch', line(), ?MODULE:abstract_expr()}.
+
+-type af_local_call() :: {call, line(), ?MODULE:af_local_function(), ?MODULE:af_args()}.
+
+-type af_remote_call() :: {call, line(), ?MODULE:af_remote_function(), ?MODULE:af_args()}.
+
+-type af_args() :: [?MODULE:abstract_expr()].
+
+-type af_local_function() :: ?MODULE:abstract_expr().
+
+-type af_remote_function() ::
+ {remote, line(), ?MODULE:abstract_expr(), ?MODULE:abstract_expr()}.
+
+-type af_list_comprehension() ::
+ {lc, line(), ?MODULE:af_template(), ?MODULE:af_qualifier_seq()}.
+
+-type af_binary_comprehension() ::
+ {bc, line(), ?MODULE:af_template(), ?MODULE:af_qualifier_seq()}.
+
+-type af_template() :: ?MODULE:abstract_expr().
+
+-type af_qualifier_seq() :: [?MODULE:af_qualifier()].
+
+-type af_qualifier() :: ?MODULE:af_generator() | ?MODULE:af_filter().
+
+-type af_generator() :: {generate, line(), ?MODULE:af_pattern(), ?MODULE:abstract_expr()}
+ | {b_generate, line(), ?MODULE:af_pattern(), ?MODULE:abstract_expr()}.
+
+-type af_filter() :: ?MODULE:abstract_expr().
+
+-type af_block() :: {block, line(), ?MODULE:af_body()}.
+
+-type af_if() :: {'if', line(), ?MODULE:af_clause_seq()}.
+
+-type af_case() :: {'case', line(), ?MODULE:abstract_expr(), ?MODULE:af_clause_seq()}.
+
+-type af_try() :: {'try',
+ line(),
+ ?MODULE:af_body(),
+ ?MODULE:af_clause_seq(),
+ ?MODULE:af_catch_clause_seq(),
+ ?MODULE:af_body()}.
+
+-type af_clause_seq() :: [?MODULE:af_clause(), ...].
+
+-type af_catch_clause_seq() :: [?MODULE:af_clause(), ...].
+
+-type af_receive() ::
+ {'receive', line(), ?MODULE:af_clause_seq()}
+ | {'receive', line(), ?MODULE:af_clause_seq(), ?MODULE:abstract_expr(), ?MODULE:af_body()}.
+
+-type af_local_fun() :: {'fun', line(), {function, function(), arity()}}.
+
+-type af_remote_fun() ::
+ {'fun', line(), {function, module(), function(), arity()}}
+ | {'fun', line(), {function, ?MODULE:af_atom(), ?MODULE:af_atom(), ?MODULE:af_integer()}}.
+
+-type af_fun() :: {'fun', line(), {clauses, ?MODULE:af_clause_seq()}}.
+
+-type af_query() :: {'query', line(), ?MODULE:af_list_comprehension()}.
+
+-type af_query_access() ::
+ {record_field, line(), ?MODULE:abstract_expr(), ?MODULE:af_field_name()}.
+
+-type abstract_clause() :: ?MODULE:af_clause() | ?MODULE:af_catch_clause().
+
+-type af_clause() ::
+ {clause, line(), [?MODULE:af_pattern()], ?MODULE:af_guard_seq(), ?MODULE:af_body()}.
+
+-type af_catch_clause() ::
+ {clause, line(), [?MODULE:af_catch_pattern()], ?MODULE:af_guard_seq(), ?MODULE:af_body()}.
+
+-type af_catch_pattern() ::
+ {?MODULE:af_catch_class(), ?MODULE:af_pattern(), ?MODULE:af_anon_variable()}.
+
+-type af_catch_class() ::
+ ?MODULE:af_variable()
+ | ?MODULE:af_lit_atom(throw) | ?MODULE:af_lit_atom(error) | ?MODULE:af_lit_atom(exit).
+
+-type af_body() :: [?MODULE:abstract_expr(), ...].
+
+-type af_guard_seq() :: [?MODULE:af_guard()].
+
+-type af_guard() :: [?MODULE:af_guard_test(), ...].
+
+-type af_guard_test() :: ?MODULE:af_literal()
+ | ?MODULE:af_variable()
+ | ?MODULE:af_tuple(?MODULE:af_guard_test())
+ | ?MODULE:af_nil()
+ | ?MODULE:af_cons(?MODULE:af_guard_test())
+ | ?MODULE:af_bin(?MODULE:af_guard_test())
+ | ?MODULE:af_binary_op(?MODULE:af_guard_test())
+ | ?MODULE:af_unary_op(?MODULE:af_guard_test())
+ | ?MODULE:af_record_access(?MODULE:af_guard_test())
+ | ?MODULE:af_record_index()
+ | ?MODULE:af_record_field(?MODULE:af_guard_test())
+ | ?MODULE:af_guard_call()
+ | ?MODULE:af_remote_guard_call().
+
+-type af_record_access(T) ::
+ {record, line(), ?MODULE:af_record_name(), [?MODULE:af_record_field(T)]}.
+
+-type af_guard_call() :: {call, line(), function(), [?MODULE:af_guard_test()]}.
+
+-type af_remote_guard_call() ::
+ {call, line(), atom(), ?MODULE:af_lit_atom(erlang), [?MODULE:af_guard_test()]}.
+
+-type af_pattern() :: ?MODULE:af_literal()
+ | ?MODULE:af_match(?MODULE:af_pattern())
+ | ?MODULE:af_variable()
+ | ?MODULE:af_anon_variable()
+ | ?MODULE:af_tuple(?MODULE:af_pattern())
+ | ?MODULE:af_nil()
+ | ?MODULE:af_cons(?MODULE:af_pattern())
+ | ?MODULE:af_bin(?MODULE:af_pattern())
+ | ?MODULE:af_binary_op(?MODULE:af_pattern())
+ | ?MODULE:af_unary_op(?MODULE:af_pattern())
+ | ?MODULE:af_record_index()
+ | ?MODULE:af_record_field(?MODULE:af_pattern()).
+
+-type af_literal() :: ?MODULE:af_atom() | ?MODULE:af_integer() | ?MODULE:af_float() | ?MODULE:af_string().
+
+-type af_atom() :: ?MODULE:af_lit_atom(atom()).
+
+-type af_lit_atom(A) :: {atom, line(), A}.
+
+-type af_integer() :: {integer, line(), non_neg_integer()}.
+
+-type af_float() :: {float, line(), float()}.
+
+-type af_string() :: {string, line(), [byte()]}.
+
+-type af_match(T) :: {match, line(), T, T}.
+
+-type af_variable() :: {var, line(), atom()}.
+
+-type af_anon_variable() :: {var, line(), '_'}.
+
+-type af_tuple(T) :: {tuple, line(), [T]}.
+
+-type af_nil() :: {nil, line()}.
+
+-type af_cons(T) :: {cons, line, T, T}.
+
+-type af_bin(T) :: {bin, line(), [?MODULE:af_binelement(T)]}.
+
+-type af_binelement(T) :: {bin_element,
+ line(),
+ T,
+ ?MODULE:af_binelement_size(),
+ type_specifier_list()}.
+
+-type af_binelement_size() :: default | ?MODULE:abstract_expr().
+
+-type af_binary_op(T) :: {op, line(), T, ?MODULE:af_binop(), T}.
+
+-type af_binop() :: '/' | '*' | 'div' | 'rem' | 'band' | 'and' | '+' | '-'
+ | 'bor' | 'bxor' | 'bsl' | 'bsr' | 'or' | 'xor' | '++'
+ | '--' | '==' | '/=' | '=<' | '<' | '>=' | '>' | '=:='
+ | '=/='.
+
+-type af_unary_op(T) :: {op, line(), ?MODULE:af_unop(), T}.
+
+-type af_unop() :: '+' | '*' | 'bnot' | 'not'.
+
+%% See also lib/stdlib/{src/erl_bits.erl,include/erl_bits.hrl}.
+-type type_specifier_list() :: default | [type_specifier(), ...].
+
+-type type_specifier() :: af_type()
+ | af_signedness()
+ | af_endianness()
+ | af_unit().
+
+-type af_type() :: integer
+ | float
+ | binary
+ | bytes
+ | bitstring
+ | bits
+ | utf8
+ | utf16
+ | utf32.
+
+-type af_signedness() :: signed | unsigned.
+
+-type af_endianness() :: big | little | native.
+
+-type af_unit() :: {unit, 1..256}.
+
+-type af_record_index() ::
+ {record_index, line(), af_record_name(), af_field_name()}.
+
+-type af_record_field(T) :: {record_field, line(), af_field_name(), T}.
+
+-type af_record_name() :: atom().
+
+-type af_field_name() :: atom().
+
+%% End of Abstract Format
+
+-type error_description() :: term().
+-type error_info() :: {erl_anno:line(), module(), error_description()}.
+-type token() :: {Tag :: atom(), Line :: erl_anno:line()}.
+
+%% mkop(Op, Arg) -> {op,Line,Op,Arg}.
+%% mkop(Left, Op, Right) -> {op,Line,Op,Left,Right}.
+
+-define(mkop2(L, OpPos, R),
+ begin
+ {Op,Pos} = OpPos,
+ {op,Pos,Op,L,R}
+ end).
+
+-define(mkop1(OpPos, A),
+ begin
+ {Op,Pos} = OpPos,
+ {op,Pos,Op,A}
+ end).
+
+%% keep track of line info in tokens
+-define(line(Tup), element(2, Tup)).
+
+%% Entry points compatible to old erl_parse.
+%% These really suck and are only here until Calle gets multiple
+%% entry points working.
+
+-spec parse_form(Tokens) -> {ok, AbsForm} | {error, ErrorInfo} when
+ Tokens :: [token()],
+ AbsForm :: abstract_form(),
+ ErrorInfo :: error_info().
+parse_form([{'-',L1},{atom,L2,spec}|Tokens]) ->
+ parse([{'-',L1},{'spec',L2}|Tokens]);
+parse_form([{'-',L1},{atom,L2,callback}|Tokens]) ->
+ parse([{'-',L1},{'callback',L2}|Tokens]);
+parse_form(Tokens) ->
+ parse(Tokens).
+
+-spec parse_exprs(Tokens) -> {ok, ExprList} | {error, ErrorInfo} when
+ Tokens :: [token()],
+ ExprList :: [abstract_expr()],
+ ErrorInfo :: error_info().
+parse_exprs(Tokens) ->
+ case parse([{atom,0,f},{'(',0},{')',0},{'->',0}|Tokens]) of
+ {ok,{function,_Lf,f,0,[{clause,_Lc,[],[],Exprs}]}} ->
+ {ok,Exprs};
+ {error,_} = Err -> Err
+ end.
+
+-spec parse_term(Tokens) -> {ok, Term} | {error, ErrorInfo} when
+ Tokens :: [token()],
+ Term :: term(),
+ ErrorInfo :: error_info().
+parse_term(Tokens) ->
+ case parse([{atom,0,f},{'(',0},{')',0},{'->',0}|Tokens]) of
+ {ok,{function,_Lf,f,0,[{clause,_Lc,[],[],[Expr]}]}} ->
+ try normalise(Expr) of
+ Term -> {ok,Term}
+ catch
+ _:_R -> {error,{?line(Expr),?MODULE,"bad term"}}
+ end;
+ {ok,{function,_Lf,f,0,[{clause,_Lc,[],[],[_E1,E2|_Es]}]}} ->
+ {error,{?line(E2),?MODULE,"bad term"}};
+ {error,_} = Err -> Err
+ end.
+
+%% Convert between the abstract form of a term and a term.
+
+-spec normalise(AbsTerm) -> Data when
+ AbsTerm :: abstract_expr(),
+ Data :: term().
+normalise({char,_,C}) -> C;
+normalise({integer,_,I}) -> I;
+normalise({float,_,F}) -> F;
+normalise({atom,_,A}) -> A;
+normalise({string,_,S}) -> S;
+normalise({nil,_}) -> [];
+normalise({bin,_,Fs}) ->
+ {value, B, _} =
+ eval_bits:expr_grp(Fs, [],
+ fun(E, _) ->
+ {value, normalise(E), []}
+ end, [], true),
+ B;
+normalise({cons,_,Head,Tail}) ->
+ [normalise(Head)|normalise(Tail)];
+normalise({tuple,_,Args}) ->
+ list_to_tuple(normalise_list(Args));
+%% Atom dot-notation, as in 'foo.bar.baz'
+%% Special case for unary +/-.
+normalise({op,_,'+',{char,_,I}}) -> I;
+normalise({op,_,'+',{integer,_,I}}) -> I;
+normalise({op,_,'+',{float,_,F}}) -> F;
+normalise({op,_,'-',{char,_,I}}) -> -I; %Weird, but compatible!
+normalise({op,_,'-',{integer,_,I}}) -> -I;
+normalise({op,_,'-',{float,_,F}}) -> -F;
+normalise(X) -> erlang:error({badarg, X}).
+
+normalise_list([H|T]) ->
+ [normalise(H)|normalise_list(T)];
+normalise_list([]) ->
+ [].
+
+%% Generate a list of tokens representing the abstract term.
+
+-spec tokens(AbsTerm) -> Tokens when
+ AbsTerm :: abstract_expr(),
+ Tokens :: [token()].
+tokens(Abs) ->
+ tokens(Abs, []).
+
+-spec tokens(AbsTerm, MoreTokens) -> Tokens when
+ AbsTerm :: abstract_expr(),
+ MoreTokens :: [token()],
+ Tokens :: [token()].
+tokens({char,L,C}, More) -> [{char,L,C}|More];
+tokens({integer,L,N}, More) -> [{integer,L,N}|More];
+tokens({float,L,F}, More) -> [{float,L,F}|More];
+tokens({atom,L,A}, More) -> [{atom,L,A}|More];
+tokens({var,L,V}, More) -> [{var,L,V}|More];
+tokens({string,L,S}, More) -> [{string,L,S}|More];
+tokens({nil,L}, More) -> [{'[',L},{']',L}|More];
+tokens({cons,L,Head,Tail}, More) ->
+ [{'[',L}|tokens(Head, tokens_tail(Tail, More))];
+tokens({tuple,L,[]}, More) ->
+ [{'{',L},{'}',L}|More];
+tokens({tuple,L,[E|Es]}, More) ->
+ [{'{',L}|tokens(E, tokens_tuple(Es, ?line(E), More))].
+
+tokens_tail({cons,L,Head,Tail}, More) ->
+ [{',',L}|tokens(Head, tokens_tail(Tail, More))];
+tokens_tail({nil,L}, More) ->
+ [{']',L}|More];
+tokens_tail(Other, More) ->
+ L = ?line(Other),
+ [{'|',L}|tokens(Other, [{']',L}|More])].
+
+tokens_tuple([E|Es], Line, More) ->
+ [{',',Line}|tokens(E, tokens_tuple(Es, ?line(E), More))];
+tokens_tuple([], Line, More) ->
+ [{'}',Line}|More].
+
+%% Give the relative precedences of operators.
+
+inop_prec('=') -> {150,100,100};
+inop_prec('!') -> {150,100,100};
+inop_prec('orelse') -> {160,150,150};
+inop_prec('andalso') -> {200,160,160};
+inop_prec('==') -> {300,200,300};
+inop_prec('/=') -> {300,200,300};
+inop_prec('=<') -> {300,200,300};
+inop_prec('<') -> {300,200,300};
+inop_prec('>=') -> {300,200,300};
+inop_prec('>') -> {300,200,300};
+inop_prec('=:=') -> {300,200,300};
+inop_prec('=/=') -> {300,200,300};
+inop_prec('++') -> {400,300,300};
+inop_prec('--') -> {400,300,300};
+inop_prec('+') -> {400,400,500};
+inop_prec('-') -> {400,400,500};
+inop_prec('bor') -> {400,400,500};
+inop_prec('bxor') -> {400,400,500};
+inop_prec('bsl') -> {400,400,500};
+inop_prec('bsr') -> {400,400,500};
+inop_prec('or') -> {400,400,500};
+inop_prec('xor') -> {400,400,500};
+inop_prec('*') -> {500,500,600};
+inop_prec('/') -> {500,500,600};
+inop_prec('div') -> {500,500,600};
+inop_prec('rem') -> {500,500,600};
+inop_prec('band') -> {500,500,600};
+inop_prec('and') -> {500,500,600};
+inop_prec('#') -> {800,700,800};
+inop_prec(':') -> {900,800,900};
+inop_prec('.') -> {900,900,1000}.
+
+-type pre_op() :: 'catch' | '+' | '-' | 'bnot' | 'not' | '#'.
+
+-spec preop_prec(pre_op()) -> {0 | 600 | 700, 100 | 700 | 800}.
+
+preop_prec('catch') -> {0,100};
+preop_prec('+') -> {600,700};
+preop_prec('-') -> {600,700};
+preop_prec('bnot') -> {600,700};
+preop_prec('not') -> {600,700};
+preop_prec('#') -> {700,800}.
+
+-spec func_prec() -> {800,700}.
+
+func_prec() -> {800,700}.
+
+-spec max_prec() -> 1000.
+
+max_prec() -> 1000.
+
+parse(T) ->
+ bar:foo(T).
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/big_local_type.erl b/lib/dialyzer/test/opaque_SUITE_data/src/big_local_type.erl
new file mode 100644
index 0000000000..7daceb5260
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/big_local_type.erl
@@ -0,0 +1,523 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2001-2015. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%% A copy of small_SUITE_data/src/big_local_type.erl, where
+%%% abstract_expr() is opaque. The transformation of forms to types is
+%%% now much faster than it used to be, for this module.
+
+-module(big_local_type).
+
+-export([parse_form/1,parse_exprs/1,parse_term/1]).
+-export([normalise/1,tokens/1,tokens/2]).
+-export([inop_prec/1,preop_prec/1,func_prec/0,max_prec/0]).
+
+-export_type([abstract_clause/0, abstract_expr/0, abstract_form/0,
+ error_info/0]).
+
+%% Start of Abstract Format
+
+-type line() :: erl_anno:line().
+
+-export_type([af_module/0, af_export/0, af_import/0, af_fa_list/0,
+ af_compile/0, af_file/0, af_record_decl/0,
+ af_field_decl/0, af_wild_attribute/0,
+ af_record_update/1, af_catch/0, af_local_call/0,
+ af_remote_call/0, af_args/0, af_local_function/0,
+ af_remote_function/0, af_list_comprehension/0,
+ af_binary_comprehension/0, af_template/0,
+ af_qualifier_seq/0, af_qualifier/0, af_generator/0,
+ af_filter/0, af_block/0, af_if/0, af_case/0, af_try/0,
+ af_clause_seq/0, af_catch_clause_seq/0, af_receive/0,
+ af_local_fun/0, af_remote_fun/0, af_fun/0, af_query/0,
+ af_query_access/0, af_clause/0,
+ af_catch_clause/0, af_catch_pattern/0, af_catch_class/0,
+ af_body/0, af_guard_seq/0, af_guard/0, af_guard_test/0,
+ af_record_access/1, af_guard_call/0,
+ af_remote_guard_call/0, af_pattern/0, af_literal/0,
+ af_atom/0, af_lit_atom/1, af_integer/0, af_float/0,
+ af_string/0, af_match/1, af_variable/0,
+ af_anon_variable/0, af_tuple/1, af_nil/0, af_cons/1,
+ af_bin/1, af_binelement/1, af_binelement_size/0,
+ af_binary_op/1, af_binop/0, af_unary_op/1, af_unop/0]).
+
+-type abstract_form() :: af_module()
+ | af_export()
+ | af_import()
+ | af_compile()
+ | af_file()
+ | af_record_decl()
+ | af_wild_attribute()
+ | af_function_decl().
+
+-type af_module() :: {attribute, line(), module, module()}.
+
+-type af_export() :: {attribute, line(), export, af_fa_list()}.
+
+-type af_import() :: {attribute, line(), import, af_fa_list()}.
+
+-type af_fa_list() :: [{function(), arity()}].
+
+-type af_compile() :: {attribute, line(), compile, any()}.
+
+-type af_file() :: {attribute, line(), file, {string(), line()}}.
+
+-type af_record_decl() ::
+ {attribute, line(), record, af_record_name(), [af_field_decl()]}.
+
+-type af_field_decl() :: {record_field, line(), af_atom()}
+ | {record_field, line(), af_atom(), abstract_expr()}.
+
+%% Types and specs, among other things...
+-type af_wild_attribute() :: {attribute, line(), af_atom(), any()}.
+
+-type af_function_decl() ::
+ {function, line(), function(), arity(), af_clause_seq()}.
+
+-opaque abstract_expr() :: af_literal()
+ | af_match(abstract_expr())
+ | af_variable()
+ | af_tuple(abstract_expr())
+ | af_nil()
+ | af_cons(abstract_expr())
+ | af_bin(abstract_expr())
+ | af_binary_op(abstract_expr())
+ | af_unary_op(abstract_expr())
+ | af_record_access(abstract_expr())
+ | af_record_update(abstract_expr())
+ | af_record_index()
+ | af_record_field(abstract_expr())
+ | af_catch()
+ | af_local_call()
+ | af_remote_call()
+ | af_list_comprehension()
+ | af_binary_comprehension()
+ | af_block()
+ | af_if()
+ | af_case()
+ | af_try()
+ | af_receive()
+ | af_local_fun()
+ | af_remote_fun()
+ | af_fun()
+ | af_query()
+ | af_query_access().
+
+-type af_record_update(T) :: {record,
+ line(),
+ abstract_expr(),
+ af_record_name(),
+ [af_record_field(T)]}.
+
+-type af_catch() :: {'catch', line(), abstract_expr()}.
+
+-type af_local_call() :: {call, line(), af_local_function(), af_args()}.
+
+-type af_remote_call() :: {call, line(), af_remote_function(), af_args()}.
+
+-type af_args() :: [abstract_expr()].
+
+-type af_local_function() :: abstract_expr().
+
+-type af_remote_function() ::
+ {remote, line(), abstract_expr(), abstract_expr()}.
+
+-type af_list_comprehension() ::
+ {lc, line(), af_template(), af_qualifier_seq()}.
+
+-type af_binary_comprehension() ::
+ {bc, line(), af_template(), af_qualifier_seq()}.
+
+-type af_template() :: abstract_expr().
+
+-type af_qualifier_seq() :: [af_qualifier()].
+
+-type af_qualifier() :: af_generator() | af_filter().
+
+-type af_generator() :: {generate, line(), af_pattern(), abstract_expr()}
+ | {b_generate, line(), af_pattern(), abstract_expr()}.
+
+-type af_filter() :: abstract_expr().
+
+-type af_block() :: {block, line(), af_body()}.
+
+-type af_if() :: {'if', line(), af_clause_seq()}.
+
+-type af_case() :: {'case', line(), abstract_expr(), af_clause_seq()}.
+
+-type af_try() :: {'try',
+ line(),
+ af_body(),
+ af_clause_seq(),
+ af_catch_clause_seq(),
+ af_body()}.
+
+-type af_clause_seq() :: [af_clause(), ...].
+
+-type af_catch_clause_seq() :: [af_clause(), ...].
+
+-type af_receive() ::
+ {'receive', line(), af_clause_seq()}
+ | {'receive', line(), af_clause_seq(), abstract_expr(), af_body()}.
+
+-type af_local_fun() :: {'fun', line(), {function, function(), arity()}}.
+
+-type af_remote_fun() ::
+ {'fun', line(), {function, module(), function(), arity()}}
+ | {'fun', line(), {function, af_atom(), af_atom(), af_integer()}}.
+
+-type af_fun() :: {'fun', line(), {clauses, af_clause_seq()}}.
+
+-type af_query() :: {'query', line(), af_list_comprehension()}.
+
+-type af_query_access() ::
+ {record_field, line(), abstract_expr(), af_field_name()}.
+
+-type abstract_clause() :: af_clause() | af_catch_clause().
+
+-type af_clause() ::
+ {clause, line(), [af_pattern()], af_guard_seq(), af_body()}.
+
+-type af_catch_clause() ::
+ {clause, line(), [af_catch_pattern()], af_guard_seq(), af_body()}.
+
+-type af_catch_pattern() ::
+ {af_catch_class(), af_pattern(), af_anon_variable()}.
+
+-type af_catch_class() ::
+ af_variable()
+ | af_lit_atom(throw) | af_lit_atom(error) | af_lit_atom(exit).
+
+-type af_body() :: [abstract_expr(), ...].
+
+-type af_guard_seq() :: [af_guard()].
+
+-type af_guard() :: [af_guard_test(), ...].
+
+-type af_guard_test() :: af_literal()
+ | af_variable()
+ | af_tuple(af_guard_test())
+ | af_nil()
+ | af_cons(af_guard_test())
+ | af_bin(af_guard_test())
+ | af_binary_op(af_guard_test())
+ | af_unary_op(af_guard_test())
+ | af_record_access(af_guard_test())
+ | af_record_index()
+ | af_record_field(af_guard_test())
+ | af_guard_call()
+ | af_remote_guard_call().
+
+-type af_record_access(T) ::
+ {record, line(), af_record_name(), [af_record_field(T)]}.
+
+-type af_guard_call() :: {call, line(), function(), [af_guard_test()]}.
+
+-type af_remote_guard_call() ::
+ {call, line(), atom(), af_lit_atom(erlang), [af_guard_test()]}.
+
+-type af_pattern() :: af_literal()
+ | af_match(af_pattern())
+ | af_variable()
+ | af_anon_variable()
+ | af_tuple(af_pattern())
+ | af_nil()
+ | af_cons(af_pattern())
+ | af_bin(af_pattern())
+ | af_binary_op(af_pattern())
+ | af_unary_op(af_pattern())
+ | af_record_index()
+ | af_record_field(af_pattern()).
+
+-type af_literal() :: af_atom() | af_integer() | af_float() | af_string().
+
+-type af_atom() :: af_lit_atom(atom()).
+
+-type af_lit_atom(A) :: {atom, line(), A}.
+
+-type af_integer() :: {integer, line(), non_neg_integer()}.
+
+-type af_float() :: {float, line(), float()}.
+
+-type af_string() :: {string, line(), [byte()]}.
+
+-type af_match(T) :: {match, line(), T, T}.
+
+-type af_variable() :: {var, line(), atom()}.
+
+-type af_anon_variable() :: {var, line(), '_'}.
+
+-type af_tuple(T) :: {tuple, line(), [T]}.
+
+-type af_nil() :: {nil, line()}.
+
+-type af_cons(T) :: {cons, line, T, T}.
+
+-type af_bin(T) :: {bin, line(), [af_binelement(T)]}.
+
+-type af_binelement(T) :: {bin_element,
+ line(),
+ T,
+ af_binelement_size(),
+ type_specifier_list()}.
+
+-type af_binelement_size() :: default | abstract_expr().
+
+-type af_binary_op(T) :: {op, line(), T, af_binop(), T}.
+
+-type af_binop() :: '/' | '*' | 'div' | 'rem' | 'band' | 'and' | '+' | '-'
+ | 'bor' | 'bxor' | 'bsl' | 'bsr' | 'or' | 'xor' | '++'
+ | '--' | '==' | '/=' | '=<' | '<' | '>=' | '>' | '=:='
+ | '=/='.
+
+-type af_unary_op(T) :: {op, line(), af_unop(), T}.
+
+-type af_unop() :: '+' | '*' | 'bnot' | 'not'.
+
+%% See also lib/stdlib/{src/erl_bits.erl,include/erl_bits.hrl}.
+-type type_specifier_list() :: default | [type_specifier(), ...].
+
+-type type_specifier() :: af_type()
+ | af_signedness()
+ | af_endianness()
+ | af_unit().
+
+-type af_type() :: integer
+ | float
+ | binary
+ | bytes
+ | bitstring
+ | bits
+ | utf8
+ | utf16
+ | utf32.
+
+-type af_signedness() :: signed | unsigned.
+
+-type af_endianness() :: big | little | native.
+
+-type af_unit() :: {unit, 1..256}.
+
+-type af_record_index() ::
+ {record_index, line(), af_record_name(), af_field_name()}.
+
+-type af_record_field(T) :: {record_field, line(), af_field_name(), T}.
+
+-type af_record_name() :: atom().
+
+-type af_field_name() :: atom().
+
+%% End of Abstract Format
+
+-type error_description() :: term().
+-type error_info() :: {erl_anno:line(), module(), error_description()}.
+-type token() :: {Tag :: atom(), Line :: erl_anno:line()}.
+
+%% mkop(Op, Arg) -> {op,Line,Op,Arg}.
+%% mkop(Left, Op, Right) -> {op,Line,Op,Left,Right}.
+
+-define(mkop2(L, OpPos, R),
+ begin
+ {Op,Pos} = OpPos,
+ {op,Pos,Op,L,R}
+ end).
+
+-define(mkop1(OpPos, A),
+ begin
+ {Op,Pos} = OpPos,
+ {op,Pos,Op,A}
+ end).
+
+%% keep track of line info in tokens
+-define(line(Tup), element(2, Tup)).
+
+%% Entry points compatible to old erl_parse.
+%% These really suck and are only here until Calle gets multiple
+%% entry points working.
+
+-spec parse_form(Tokens) -> {ok, AbsForm} | {error, ErrorInfo} when
+ Tokens :: [token()],
+ AbsForm :: abstract_form(),
+ ErrorInfo :: error_info().
+parse_form([{'-',L1},{atom,L2,spec}|Tokens]) ->
+ parse([{'-',L1},{'spec',L2}|Tokens]);
+parse_form([{'-',L1},{atom,L2,callback}|Tokens]) ->
+ parse([{'-',L1},{'callback',L2}|Tokens]);
+parse_form(Tokens) ->
+ parse(Tokens).
+
+-spec parse_exprs(Tokens) -> {ok, ExprList} | {error, ErrorInfo} when
+ Tokens :: [token()],
+ ExprList :: [abstract_expr()],
+ ErrorInfo :: error_info().
+parse_exprs(Tokens) ->
+ case parse([{atom,0,f},{'(',0},{')',0},{'->',0}|Tokens]) of
+ {ok,{function,_Lf,f,0,[{clause,_Lc,[],[],Exprs}]}} ->
+ {ok,Exprs};
+ {error,_} = Err -> Err
+ end.
+
+-spec parse_term(Tokens) -> {ok, Term} | {error, ErrorInfo} when
+ Tokens :: [token()],
+ Term :: term(),
+ ErrorInfo :: error_info().
+parse_term(Tokens) ->
+ case parse([{atom,0,f},{'(',0},{')',0},{'->',0}|Tokens]) of
+ {ok,{function,_Lf,f,0,[{clause,_Lc,[],[],[Expr]}]}} ->
+ try normalise(Expr) of
+ Term -> {ok,Term}
+ catch
+ _:_R -> {error,{?line(Expr),?MODULE,"bad term"}}
+ end;
+ {ok,{function,_Lf,f,0,[{clause,_Lc,[],[],[_E1,E2|_Es]}]}} ->
+ {error,{?line(E2),?MODULE,"bad term"}};
+ {error,_} = Err -> Err
+ end.
+
+%% Convert between the abstract form of a term and a term.
+
+-spec normalise(AbsTerm) -> Data when
+ AbsTerm :: abstract_expr(),
+ Data :: term().
+normalise({char,_,C}) -> C;
+normalise({integer,_,I}) -> I;
+normalise({float,_,F}) -> F;
+normalise({atom,_,A}) -> A;
+normalise({string,_,S}) -> S;
+normalise({nil,_}) -> [];
+normalise({bin,_,Fs}) ->
+ {value, B, _} =
+ eval_bits:expr_grp(Fs, [],
+ fun(E, _) ->
+ {value, normalise(E), []}
+ end, [], true),
+ B;
+normalise({cons,_,Head,Tail}) ->
+ [normalise(Head)|normalise(Tail)];
+normalise({tuple,_,Args}) ->
+ list_to_tuple(normalise_list(Args));
+%% Atom dot-notation, as in 'foo.bar.baz'
+%% Special case for unary +/-.
+normalise({op,_,'+',{char,_,I}}) -> I;
+normalise({op,_,'+',{integer,_,I}}) -> I;
+normalise({op,_,'+',{float,_,F}}) -> F;
+normalise({op,_,'-',{char,_,I}}) -> -I; %Weird, but compatible!
+normalise({op,_,'-',{integer,_,I}}) -> -I;
+normalise({op,_,'-',{float,_,F}}) -> -F;
+normalise(X) -> erlang:error({badarg, X}).
+
+normalise_list([H|T]) ->
+ [normalise(H)|normalise_list(T)];
+normalise_list([]) ->
+ [].
+
+%% Generate a list of tokens representing the abstract term.
+
+-spec tokens(AbsTerm) -> Tokens when
+ AbsTerm :: abstract_expr(),
+ Tokens :: [token()].
+tokens(Abs) ->
+ tokens(Abs, []).
+
+-spec tokens(AbsTerm, MoreTokens) -> Tokens when
+ AbsTerm :: abstract_expr(),
+ MoreTokens :: [token()],
+ Tokens :: [token()].
+tokens({char,L,C}, More) -> [{char,L,C}|More];
+tokens({integer,L,N}, More) -> [{integer,L,N}|More];
+tokens({float,L,F}, More) -> [{float,L,F}|More];
+tokens({atom,L,A}, More) -> [{atom,L,A}|More];
+tokens({var,L,V}, More) -> [{var,L,V}|More];
+tokens({string,L,S}, More) -> [{string,L,S}|More];
+tokens({nil,L}, More) -> [{'[',L},{']',L}|More];
+tokens({cons,L,Head,Tail}, More) ->
+ [{'[',L}|tokens(Head, tokens_tail(Tail, More))];
+tokens({tuple,L,[]}, More) ->
+ [{'{',L},{'}',L}|More];
+tokens({tuple,L,[E|Es]}, More) ->
+ [{'{',L}|tokens(E, tokens_tuple(Es, ?line(E), More))].
+
+tokens_tail({cons,L,Head,Tail}, More) ->
+ [{',',L}|tokens(Head, tokens_tail(Tail, More))];
+tokens_tail({nil,L}, More) ->
+ [{']',L}|More];
+tokens_tail(Other, More) ->
+ L = ?line(Other),
+ [{'|',L}|tokens(Other, [{']',L}|More])].
+
+tokens_tuple([E|Es], Line, More) ->
+ [{',',Line}|tokens(E, tokens_tuple(Es, ?line(E), More))];
+tokens_tuple([], Line, More) ->
+ [{'}',Line}|More].
+
+%% Give the relative precedences of operators.
+
+inop_prec('=') -> {150,100,100};
+inop_prec('!') -> {150,100,100};
+inop_prec('orelse') -> {160,150,150};
+inop_prec('andalso') -> {200,160,160};
+inop_prec('==') -> {300,200,300};
+inop_prec('/=') -> {300,200,300};
+inop_prec('=<') -> {300,200,300};
+inop_prec('<') -> {300,200,300};
+inop_prec('>=') -> {300,200,300};
+inop_prec('>') -> {300,200,300};
+inop_prec('=:=') -> {300,200,300};
+inop_prec('=/=') -> {300,200,300};
+inop_prec('++') -> {400,300,300};
+inop_prec('--') -> {400,300,300};
+inop_prec('+') -> {400,400,500};
+inop_prec('-') -> {400,400,500};
+inop_prec('bor') -> {400,400,500};
+inop_prec('bxor') -> {400,400,500};
+inop_prec('bsl') -> {400,400,500};
+inop_prec('bsr') -> {400,400,500};
+inop_prec('or') -> {400,400,500};
+inop_prec('xor') -> {400,400,500};
+inop_prec('*') -> {500,500,600};
+inop_prec('/') -> {500,500,600};
+inop_prec('div') -> {500,500,600};
+inop_prec('rem') -> {500,500,600};
+inop_prec('band') -> {500,500,600};
+inop_prec('and') -> {500,500,600};
+inop_prec('#') -> {800,700,800};
+inop_prec(':') -> {900,800,900};
+inop_prec('.') -> {900,900,1000}.
+
+-type pre_op() :: 'catch' | '+' | '-' | 'bnot' | 'not' | '#'.
+
+-spec preop_prec(pre_op()) -> {0 | 600 | 700, 100 | 700 | 800}.
+
+preop_prec('catch') -> {0,100};
+preop_prec('+') -> {600,700};
+preop_prec('-') -> {600,700};
+preop_prec('bnot') -> {600,700};
+preop_prec('not') -> {600,700};
+preop_prec('#') -> {700,800}.
+
+-spec func_prec() -> {800,700}.
+
+func_prec() -> {800,700}.
+
+-spec max_prec() -> 1000.
+
+max_prec() -> 1000.
+
+parse(T) ->
+ bar:foo(T).
diff --git a/lib/hipe/cerl/erl_types.erl b/lib/hipe/cerl/erl_types.erl
index cc4fee0853..56ec757dbf 100644
--- a/lib/hipe/cerl/erl_types.erl
+++ b/lib/hipe/cerl/erl_types.erl
@@ -82,7 +82,7 @@
t_form_to_string/1,
t_from_form/4,
t_from_form/5,
- t_from_form_without_remote/2,
+ t_from_form_without_remote/3,
t_check_record_fields/4,
t_check_record_fields/5,
t_from_range/2,
@@ -3971,27 +3971,32 @@ mod_name(Mod, Name) ->
-type type_names() :: [type_key() | record_key()].
+-type mta() :: {module(), atom(), arity()}.
+-type mra() :: {module(), atom(), arity()}.
+-type site() :: {'type', mta()} | {'spec', mfa()} | {'record', mra()}.
+
-spec t_from_form(parse_form(), sets:set(mfa()),
- module(), mod_records()) -> erl_type().
+ site(), mod_records()) -> erl_type().
-t_from_form(Form, ExpTypes, Module, RecDict) ->
- t_from_form(Form, ExpTypes, Module, RecDict, dict:new()).
+t_from_form(Form, ExpTypes, Site, RecDict) ->
+ t_from_form(Form, ExpTypes, Site, RecDict, dict:new()).
-spec t_from_form(parse_form(), sets:set(mfa()),
- module(), mod_records(), var_table()) -> erl_type().
+ site(), mod_records(), var_table()) -> erl_type().
-t_from_form(Form, ExpTypes, Module, RecDict, VarDict) ->
- {T, _} = t_from_form1(Form, [], ExpTypes, Module, RecDict, VarDict),
+t_from_form(Form, ExpTypes, Site, RecDict, VarDict) ->
+ {T, _} = t_from_form1(Form, ExpTypes, Site, RecDict, VarDict),
T.
%% Replace external types with with none().
--spec t_from_form_without_remote(parse_form(), type_table()) -> erl_type().
+-spec t_from_form_without_remote(parse_form(), site(), type_table()) ->
+ erl_type().
-t_from_form_without_remote(Form, TypeTable) ->
- Module = mod,
+t_from_form_without_remote(Form, Site, TypeTable) ->
+ Module = site_module(Site),
RecDict = dict:from_list([{Module, TypeTable}]),
ExpTypes = replace_by_none,
- {T, _} = t_from_form1(Form, [], ExpTypes, Module, RecDict, dict:new()),
+ {T, _} = t_from_form1(Form, ExpTypes, Site, RecDict, dict:new()),
T.
%% REC_TYPE_LIMIT is used for limiting the depth of recursive types.
@@ -4005,23 +4010,32 @@ t_from_form_without_remote(Form, TypeTable) ->
-type expand_depth() :: integer().
-t_from_form1(Form, TypeNames, ET, M, MR, V) ->
- t_from_form1(Form, TypeNames, ET, M, MR, V, ?EXPAND_DEPTH).
+-spec t_from_form1(parse_form(), sets:set(mfa()) | 'replace_by_none',
+ site(), mod_records(), var_table()) ->
+ {erl_type(), expand_limit()}.
+
+t_from_form1(Form, ET, Site, MR, V) ->
+ TypeNames = initial_typenames(Site),
+ t_from_form1(Form, TypeNames, ET, Site, MR, V, ?EXPAND_DEPTH).
+
+initial_typenames({type, _MTA}=Site) -> [Site];
+initial_typenames({spec, _MFA}) -> [];
+initial_typenames({record, _MRA}) -> [].
-t_from_form1(Form, TypeNames, ET, M, MR, V, D) ->
+t_from_form1(Form, TypeNames, ET, Site, MR, V, D) ->
L = ?EXPAND_LIMIT,
- {T, L1} = t_from_form(Form, TypeNames, ET, M, MR, V, D, L),
+ {T, L1} = t_from_form(Form, TypeNames, ET, Site, MR, V, D, L),
if
L1 =< 0, D > 1 ->
D1 = D div 2,
- t_from_form1(Form, TypeNames, ET, M, MR, V, D1);
+ t_from_form1(Form, TypeNames, ET, Site, MR, V, D1);
true ->
{T, L1}
end.
-spec t_from_form(parse_form(), type_names(),
sets:set(mfa()) | 'replace_by_none',
- module(), mod_records(), var_table(),
+ site(), mod_records(), var_table(),
expand_depth(), expand_limit())
-> {erl_type(), expand_limit()}.
@@ -4031,193 +4045,194 @@ t_from_form1(Form, TypeNames, ET, M, MR, V, D) ->
%% self() ! {self(), ext_types, {RemMod, Name, ArgsLen}}
%% is called, unless 'replace_by_none' is given.
%%
-%% It is assumed that M can be found in MR.
+%% It is assumed that site_module(S) can be found in MR.
-t_from_form(_, _TypeNames, _ET, _M, _MR, _V, D, L) when D =< 0 ; L =< 0 ->
+t_from_form(_, _TypeNames, _ET, _S, _MR, _V, D, L) when D =< 0 ; L =< 0 ->
{t_any(), L};
-t_from_form({var, _L, '_'}, _TypeNames, _ET, _M, _MR, _V, _D, L) ->
+t_from_form({var, _L, '_'}, _TypeNames, _ET, _S, _MR, _V, _D, L) ->
{t_any(), L};
-t_from_form({var, _L, Name}, _TypeNames, _ET, _M, _MR, V, _D, L) ->
+t_from_form({var, _L, Name}, _TypeNames, _ET, _S, _MR, V, _D, L) ->
case dict:find(Name, V) of
error -> {t_var(Name), L};
{ok, Val} -> {Val, L}
end;
-t_from_form({ann_type, _L, [_Var, Type]}, TypeNames, ET, M, MR, V, D, L) ->
- t_from_form(Type, TypeNames, ET, M, MR, V, D, L);
-t_from_form({paren_type, _L, [Type]}, TypeNames, ET, M, MR, V, D, L) ->
- t_from_form(Type, TypeNames, ET, M, MR, V, D, L);
+t_from_form({ann_type, _L, [_Var, Type]}, TypeNames, ET, S, MR, V, D, L) ->
+ t_from_form(Type, TypeNames, ET, S, MR, V, D, L);
+t_from_form({paren_type, _L, [Type]}, TypeNames, ET, S, MR, V, D, L) ->
+ t_from_form(Type, TypeNames, ET, S, MR, V, D, L);
t_from_form({remote_type, _L, [{atom, _, Module}, {atom, _, Type}, Args]},
- TypeNames, ET, M, MR, V, D, L) ->
- remote_from_form(Module, Type, Args, TypeNames, ET, M, MR, V, D, L);
-t_from_form({atom, _L, Atom}, _TypeNames, _ET, _M, _MR, _V, _D, L) ->
+ TypeNames, ET, S, MR, V, D, L) ->
+ remote_from_form(Module, Type, Args, TypeNames, ET, S, MR, V, D, L);
+t_from_form({atom, _L, Atom}, _TypeNames, _ET, _S, _MR, _V, _D, L) ->
{t_atom(Atom), L};
-t_from_form({integer, _L, Int}, _TypeNames, _ET, _M, _MR, _V, _D, L) ->
+t_from_form({integer, _L, Int}, _TypeNames, _ET, _S, _MR, _V, _D, L) ->
{t_integer(Int), L};
-t_from_form({op, _L, _Op, _Arg} = Op, _TypeNames, _ET, _M, _MR, _V, _D, L) ->
+t_from_form({op, _L, _Op, _Arg} = Op, _TypeNames, _ET, _S, _MR, _V, _D, L) ->
case erl_eval:partial_eval(Op) of
{integer, _, Val} ->
{t_integer(Val), L};
_ -> throw({error, io_lib:format("Unable to evaluate type ~w\n", [Op])})
end;
t_from_form({op, _L, _Op, _Arg1, _Arg2} = Op, _TypeNames,
- _ET, _M, _MR, _V, _D, L) ->
+ _ET, _S, _MR, _V, _D, L) ->
case erl_eval:partial_eval(Op) of
{integer, _, Val} ->
{t_integer(Val), L};
_ -> throw({error, io_lib:format("Unable to evaluate type ~w\n", [Op])})
end;
-t_from_form({type, _L, any, []}, _TypeNames, _ET, _M, _MR, _V, _D, L) ->
+t_from_form({type, _L, any, []}, _TypeNames, _ET, _S, _MR, _V, _D, L) ->
{t_any(), L};
-t_from_form({type, _L, arity, []}, _TypeNames, _ET, _M, _MR, _V, _D, L) ->
+t_from_form({type, _L, arity, []}, _TypeNames, _ET, _S, _MR, _V, _D, L) ->
{t_arity(), L};
-t_from_form({type, _L, atom, []}, _TypeNames, _ET, _M, _MR, _V, _D, L) ->
+t_from_form({type, _L, atom, []}, _TypeNames, _ET, _S, _MR, _V, _D, L) ->
{t_atom(), L};
-t_from_form({type, _L, binary, []}, _TypeNames, _ET, _M, _MR, _V, _D, L) ->
+t_from_form({type, _L, binary, []}, _TypeNames, _ET, _S, _MR, _V, _D, L) ->
{t_binary(), L};
t_from_form({type, _L, binary, [Base, Unit]} = Type,
- _TypeNames, _ET, _M, _MR, _V, _D, L) ->
+ _TypeNames, _ET, _S, _MR, _V, _D, L) ->
case {erl_eval:partial_eval(Base), erl_eval:partial_eval(Unit)} of
{{integer, _, B}, {integer, _, U}} when B >= 0, U >= 0 ->
{t_bitstr(U, B), L};
_ -> throw({error, io_lib:format("Unable to evaluate type ~w\n", [Type])})
end;
-t_from_form({type, _L, bitstring, []}, _TypeNames, _ET, _M, _MR, _V, _D, L) ->
+t_from_form({type, _L, bitstring, []}, _TypeNames, _ET, _S, _MR, _V, _D, L) ->
{t_bitstr(), L};
-t_from_form({type, _L, bool, []}, _TypeNames, _ET, _M, _MR, _V, _D, L) ->
+t_from_form({type, _L, bool, []}, _TypeNames, _ET, _S, _MR, _V, _D, L) ->
{t_boolean(), L}; % XXX: Temporarily
-t_from_form({type, _L, boolean, []}, _TypeNames, _ET, _M, _MR, _V, _D, L) ->
+t_from_form({type, _L, boolean, []}, _TypeNames, _ET, _S, _MR, _V, _D, L) ->
{t_boolean(), L};
-t_from_form({type, _L, byte, []}, _TypeNames, _ET, _M, _MR, _V, _D, L) ->
+t_from_form({type, _L, byte, []}, _TypeNames, _ET, _S, _MR, _V, _D, L) ->
{t_byte(), L};
-t_from_form({type, _L, char, []}, _TypeNames, _ET, _M, _MR, _V, _D, L) ->
+t_from_form({type, _L, char, []}, _TypeNames, _ET, _S, _MR, _V, _D, L) ->
{t_char(), L};
-t_from_form({type, _L, float, []}, _TypeNames, _ET, _M, _MR, _V, _D, L) ->
+t_from_form({type, _L, float, []}, _TypeNames, _ET, _S, _MR, _V, _D, L) ->
{t_float(), L};
-t_from_form({type, _L, function, []}, _TypeNames, _ET, _M, _MR, _V, _D, L) ->
+t_from_form({type, _L, function, []}, _TypeNames, _ET, _S, _MR, _V, _D, L) ->
{t_fun(), L};
-t_from_form({type, _L, 'fun', []}, _TypeNames, _ET, _M, _MR, _V, _D, L) ->
+t_from_form({type, _L, 'fun', []}, _TypeNames, _ET, _S, _MR, _V, _D, L) ->
{t_fun(), L};
t_from_form({type, _L, 'fun', [{type, _, any}, Range]}, TypeNames,
- ET, M, MR, V, D, L) ->
- {T, L1} = t_from_form(Range, TypeNames, ET, M, MR, V, D - 1, L - 1),
+ ET, S, MR, V, D, L) ->
+ {T, L1} = t_from_form(Range, TypeNames, ET, S, MR, V, D - 1, L - 1),
{t_fun(T), L1};
t_from_form({type, _L, 'fun', [{type, _, product, Domain}, Range]},
- TypeNames, ET, M, MR, V, D, L) ->
- {Dom1, L1} = list_from_form(Domain, TypeNames, ET, M, MR, V, D, L),
- {Ran1, L2} = t_from_form(Range, TypeNames, ET, M, MR, V, D - 1, L1),
+ TypeNames, ET, S, MR, V, D, L) ->
+ {Dom1, L1} = list_from_form(Domain, TypeNames, ET, S, MR, V, D, L),
+ {Ran1, L2} = t_from_form(Range, TypeNames, ET, S, MR, V, D - 1, L1),
{t_fun(Dom1, Ran1), L2};
-t_from_form({type, _L, identifier, []}, _TypeNames, _ET, _M, _MR, _V, _D, L) ->
+t_from_form({type, _L, identifier, []}, _TypeNames, _ET, _S, _MR, _V, _D, L) ->
{t_identifier(), L};
-t_from_form({type, _L, integer, []}, _TypeNames, _ET, _M, _MR, _V, _D, L) ->
+t_from_form({type, _L, integer, []}, _TypeNames, _ET, _S, _MR, _V, _D, L) ->
{t_integer(), L};
-t_from_form({type, _L, iodata, []}, _TypeNames, _ET, _M, _MR, _V, _D, L) ->
+t_from_form({type, _L, iodata, []}, _TypeNames, _ET, _S, _MR, _V, _D, L) ->
{t_iodata(), L};
-t_from_form({type, _L, iolist, []}, _TypeNames, _ET, _M, _MR, _V, _D, L) ->
+t_from_form({type, _L, iolist, []}, _TypeNames, _ET, _S, _MR, _V, _D, L) ->
{t_iolist(), L};
-t_from_form({type, _L, list, []}, _TypeNames, _ET, _M, _MR, _V, _D, L) ->
+t_from_form({type, _L, list, []}, _TypeNames, _ET, _S, _MR, _V, _D, L) ->
{t_list(), L};
-t_from_form({type, _L, list, [Type]}, TypeNames, ET, M, MR, V, D, L) ->
- {T, L1} = t_from_form(Type, TypeNames, ET, M, MR, V, D - 1, L - 1),
+t_from_form({type, _L, list, [Type]}, TypeNames, ET, S, MR, V, D, L) ->
+ {T, L1} = t_from_form(Type, TypeNames, ET, S, MR, V, D - 1, L - 1),
{t_list(T), L1};
-t_from_form({type, _L, map, _}, TypeNames, ET, M, MR, V, D, L) ->
- builtin_type(map, t_map([]), TypeNames, ET, M, MR, V, D, L);
-t_from_form({type, _L, mfa, []}, _TypeNames, _ET, _M, _MR, _V, _D, L) ->
+t_from_form({type, _L, map, _}, TypeNames, ET, S, MR, V, D, L) ->
+ builtin_type(map, t_map([]), TypeNames, ET, S, MR, V, D, L);
+t_from_form({type, _L, mfa, []}, _TypeNames, _ET, _S, _MR, _V, _D, L) ->
{t_mfa(), L};
-t_from_form({type, _L, module, []}, _TypeNames, _ET, _M, _MR, _V, _D, L) ->
+t_from_form({type, _L, module, []}, _TypeNames, _ET, _S, _MR, _V, _D, L) ->
{t_module(), L};
-t_from_form({type, _L, nil, []}, _TypeNames, _ET, _M, _MR, _V, _D, L) ->
+t_from_form({type, _L, nil, []}, _TypeNames, _ET, _S, _MR, _V, _D, L) ->
{t_nil(), L};
-t_from_form({type, _L, neg_integer, []}, _TypeNames, _ET, _M, _MR, _V, _D, L) ->
+t_from_form({type, _L, neg_integer, []}, _TypeNames, _ET, _S, _MR, _V, _D, L) ->
{t_neg_integer(), L};
-t_from_form({type, _L, non_neg_integer, []}, _TypeNames, _ET, _M, _MR,
+t_from_form({type, _L, non_neg_integer, []}, _TypeNames, _ET, _S, _MR,
_V, _D, L) ->
{t_non_neg_integer(), L};
-t_from_form({type, _L, no_return, []}, _TypeNames, _ET, _M, _MR, _V, _D, L) ->
+t_from_form({type, _L, no_return, []}, _TypeNames, _ET, _S, _MR, _V, _D, L) ->
{t_unit(), L};
-t_from_form({type, _L, node, []}, _TypeNames, _ET, _M, _MR, _V, _D, L) ->
+t_from_form({type, _L, node, []}, _TypeNames, _ET, _S, _MR, _V, _D, L) ->
{t_node(), L};
-t_from_form({type, _L, none, []}, _TypeNames, _ET, _M, _MR, _V, _D, L) ->
+t_from_form({type, _L, none, []}, _TypeNames, _ET, _S, _MR, _V, _D, L) ->
{t_none(), L};
-t_from_form({type, _L, nonempty_list, []}, _TypeNames, _ET, _M, _MR, _V, _D, L) ->
+t_from_form({type, _L, nonempty_list, []}, _TypeNames, _ET, _S, _MR, _V, _D, L) ->
{t_nonempty_list(), L};
-t_from_form({type, _L, nonempty_list, [Type]}, TypeNames, ET, M, MR, V, D, L) ->
- {T, L1} = t_from_form(Type, TypeNames, ET, M, MR, V, D, L - 1),
+t_from_form({type, _L, nonempty_list, [Type]}, TypeNames, ET, S, MR, V, D, L) ->
+ {T, L1} = t_from_form(Type, TypeNames, ET, S, MR, V, D, L - 1),
{t_nonempty_list(T), L1};
t_from_form({type, _L, nonempty_improper_list, [Cont, Term]}, TypeNames,
- ET, M, MR, V, D, L) ->
- {T1, L1} = t_from_form(Cont, TypeNames, ET, M, MR, V, D, L - 1),
- {T2, L2} = t_from_form(Term, TypeNames, ET, M, MR, V, D, L1),
+ ET, S, MR, V, D, L) ->
+ {T1, L1} = t_from_form(Cont, TypeNames, ET, S, MR, V, D, L - 1),
+ {T2, L2} = t_from_form(Term, TypeNames, ET, S, MR, V, D, L1),
{t_cons(T1, T2), L2};
t_from_form({type, _L, nonempty_maybe_improper_list, []}, _TypeNames,
- _ET, _M, _MR, _V, _D, L) ->
+ _ET, _S, _MR, _V, _D, L) ->
{t_cons(?any, ?any), L};
t_from_form({type, _L, nonempty_maybe_improper_list, [Cont, Term]},
- TypeNames, ET, M, MR, V, D, L) ->
- {T1, L1} = t_from_form(Cont, TypeNames, ET, M, MR, V, D, L - 1),
- {T2, L2} = t_from_form(Term, TypeNames, ET, M, MR, V, D, L1),
+ TypeNames, ET, S, MR, V, D, L) ->
+ {T1, L1} = t_from_form(Cont, TypeNames, ET, S, MR, V, D, L - 1),
+ {T2, L2} = t_from_form(Term, TypeNames, ET, S, MR, V, D, L1),
{t_cons(T1, T2), L2};
-t_from_form({type, _L, nonempty_string, []}, _TypeNames, _ET, _M, _MR, _V, _D, L) ->
+t_from_form({type, _L, nonempty_string, []}, _TypeNames, _ET, _S, _MR, _V, _D, L) ->
{t_nonempty_string(), L};
-t_from_form({type, _L, number, []}, _TypeNames, _ET, _M, _MR, _V, _D, L) ->
+t_from_form({type, _L, number, []}, _TypeNames, _ET, _S, _MR, _V, _D, L) ->
{t_number(), L};
-t_from_form({type, _L, pid, []}, _TypeNames, _ET, _M, _MR, _V, _D, L) ->
+t_from_form({type, _L, pid, []}, _TypeNames, _ET, _S, _MR, _V, _D, L) ->
{t_pid(), L};
-t_from_form({type, _L, port, []}, _TypeNames, _ET, _M, _MR, _V, _D, L) ->
+t_from_form({type, _L, port, []}, _TypeNames, _ET, _S, _MR, _V, _D, L) ->
{t_port(), L};
-t_from_form({type, _L, pos_integer, []}, _TypeNames, _ET, _M, _MR, _V, _D, L) ->
+t_from_form({type, _L, pos_integer, []}, _TypeNames, _ET, _S, _MR, _V, _D, L) ->
{t_pos_integer(), L};
t_from_form({type, _L, maybe_improper_list, []}, _TypeNames,
- _ET, _M, _MR, _V, _D, L) ->
+ _ET, _S, _MR, _V, _D, L) ->
{t_maybe_improper_list(), L};
t_from_form({type, _L, maybe_improper_list, [Content, Termination]},
- TypeNames, ET, M, MR, V, D, L) ->
- {T1, L1} = t_from_form(Content, TypeNames, ET, M, MR, V, D, L - 1),
- {T2, L2} = t_from_form(Termination, TypeNames, ET, M, MR, V, D, L1),
+ TypeNames, ET, S, MR, V, D, L) ->
+ {T1, L1} = t_from_form(Content, TypeNames, ET, S, MR, V, D, L - 1),
+ {T2, L2} = t_from_form(Termination, TypeNames, ET, S, MR, V, D, L1),
{t_maybe_improper_list(T1, T2), L2};
-t_from_form({type, _L, product, Elements}, TypeNames, ET, M, MR, V, D, L) ->
- {Lst, L1} = list_from_form(Elements, TypeNames, ET, M, MR, V, D - 1, L),
+t_from_form({type, _L, product, Elements}, TypeNames, ET, S, MR, V, D, L) ->
+ {Lst, L1} = list_from_form(Elements, TypeNames, ET, S, MR, V, D - 1, L),
{t_product(Lst), L1};
t_from_form({type, _L, range, [From, To]} = Type,
- _TypeNames, _ET, _M, _MR, _V, _D, L) ->
+ _TypeNames, _ET, _S, _MR, _V, _D, L) ->
case {erl_eval:partial_eval(From), erl_eval:partial_eval(To)} of
{{integer, _, FromVal}, {integer, _, ToVal}} ->
{t_from_range(FromVal, ToVal), L};
_ -> throw({error, io_lib:format("Unable to evaluate type ~w\n", [Type])})
end;
-t_from_form({type, _L, record, [Name|Fields]}, TypeNames, ET, M, MR, V, D, L) ->
- record_from_form(Name, Fields, TypeNames, ET, M, MR, V, D, L);
-t_from_form({type, _L, reference, []}, _TypeNames, _ET, _M, _MR, _V, _D, L) ->
+t_from_form({type, _L, record, [Name|Fields]}, TypeNames, ET, S, MR, V, D, L) ->
+ record_from_form(Name, Fields, TypeNames, ET, S, MR, V, D, L);
+t_from_form({type, _L, reference, []}, _TypeNames, _ET, _S, _MR, _V, _D, L) ->
{t_reference(), L};
-t_from_form({type, _L, string, []}, _TypeNames, _ET, _M, _MR, _V, _D, L) ->
+t_from_form({type, _L, string, []}, _TypeNames, _ET, _S, _MR, _V, _D, L) ->
{t_string(), L};
-t_from_form({type, _L, term, []}, _TypeNames, _ET, _M, _MR, _V, _D, L) ->
+t_from_form({type, _L, term, []}, _TypeNames, _ET, _S, _MR, _V, _D, L) ->
{t_any(), L};
-t_from_form({type, _L, timeout, []}, _TypeNames, _ET, _M, _MR, _V, _D, L) ->
+t_from_form({type, _L, timeout, []}, _TypeNames, _ET, _S, _MR, _V, _D, L) ->
{t_timeout(), L};
-t_from_form({type, _L, tuple, any}, _TypeNames, _ET, _M, _MR, _V, _D, L) ->
+t_from_form({type, _L, tuple, any}, _TypeNames, _ET, _S, _MR, _V, _D, L) ->
{t_tuple(), L};
-t_from_form({type, _L, tuple, Args}, TypeNames, ET, M, MR, V, D, L) ->
- {Lst, L1} = list_from_form(Args, TypeNames, ET, M, MR, V, D - 1, L),
+t_from_form({type, _L, tuple, Args}, TypeNames, ET, S, MR, V, D, L) ->
+ {Lst, L1} = list_from_form(Args, TypeNames, ET, S, MR, V, D - 1, L),
{t_tuple(Lst), L1};
-t_from_form({type, _L, union, Args}, TypeNames, ET, M, MR, V, D, L) ->
- {Lst, L1} = list_from_form(Args, TypeNames, ET, M, MR, V, D, L),
+t_from_form({type, _L, union, Args}, TypeNames, ET, S, MR, V, D, L) ->
+ {Lst, L1} = list_from_form(Args, TypeNames, ET, S, MR, V, D, L),
{t_sup(Lst), L1};
-t_from_form({user_type, _L, Name, Args}, TypeNames, ET, M, MR, V, D, L) ->
- type_from_form(Name, Args, TypeNames, ET, M, MR, V, D, L);
-t_from_form({type, _L, Name, Args}, TypeNames, ET, M, MR, V, D, L) ->
+t_from_form({user_type, _L, Name, Args}, TypeNames, ET, S, MR, V, D, L) ->
+ type_from_form(Name, Args, TypeNames, ET, S, MR, V, D, L);
+t_from_form({type, _L, Name, Args}, TypeNames, ET, S, MR, V, D, L) ->
%% Compatibility: modules compiled before Erlang/OTP 18.0.
- type_from_form(Name, Args, TypeNames, ET, M, MR, V, D, L);
+ type_from_form(Name, Args, TypeNames, ET, S, MR, V, D, L);
t_from_form({opaque, _L, Name, {Mod, Args, Rep}}, _TypeNames,
- _ET, _M, _MR, _V, _D, L) ->
+ _ET, _S, _MR, _V, _D, L) ->
%% XXX. To be removed.
{t_opaque(Mod, Name, Args, Rep), L}.
-builtin_type(Name, Type, TypeNames, ET, M, MR, V, D, L) ->
+builtin_type(Name, Type, TypeNames, ET, Site, MR, V, D, L) ->
+ M = site_module(Site),
case dict:find(M, MR) of
{ok, R} ->
case lookup_type(Name, 0, R) of
{_, {{_M, _FL, _F, _A}, _T}} ->
- type_from_form(Name, [], TypeNames, ET, M, MR, V, D, L);
+ type_from_form(Name, [], TypeNames, ET, Site, MR, V, D, L);
error ->
{Type, L}
end;
@@ -4225,93 +4240,107 @@ builtin_type(Name, Type, TypeNames, ET, M, MR, V, D, L) ->
{Type, L}
end.
-type_from_form(Name, Args, TypeNames, ET, M, MR, V, D, L) ->
+type_from_form(Name, Args, TypeNames, ET, Site0, MR, V, D, L) ->
ArgsLen = length(Args),
- {ArgTypes, L1} = list_from_form(Args, TypeNames, ET, M, MR, V, D, L),
- {ok, R} = dict:find(M, MR),
+ Module = site_module(Site0),
+ {ok, R} = dict:find(Module, MR),
+ TypeName = {type, {Module, Name, ArgsLen}},
case lookup_type(Name, ArgsLen, R) of
{type, {{Module, _FileName, Form, ArgNames}, _Type}} ->
- TypeName = {type, Module, Name, ArgsLen},
case can_unfold_more(TypeName, TypeNames) of
true ->
+ NewTypeNames = [TypeName|TypeNames],
+ {ArgTypes, L1} =
+ list_from_form(Args, TypeNames, ET, Site0, MR, V, D, L),
List = lists:zip(ArgNames, ArgTypes),
TmpV = dict:from_list(List),
- t_from_form(Form, [TypeName|TypeNames], ET, M, MR, TmpV, D, L1);
+ Site = TypeName,
+ t_from_form(Form, NewTypeNames, ET, Site, MR, TmpV, D, L1);
false ->
- {t_any(), L1}
+ {t_any(), L}
end;
{opaque, {{Module, _FileName, 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(Form, [TypeName|TypeNames], ET, M, MR, TmpV, D, L1);
- false -> {t_any(), L1}
- end,
- Rep1 = choose_opaque_type(Rep, Type),
- Rep2 = case t_is_none(Rep1) of
- true -> Rep1;
- false ->
- ArgTypes2 = subst_all_vars_to_any_list(ArgTypes),
- t_opaque(Module, Name, ArgTypes2, Rep1)
- end,
- {Rep2, L2};
+ case can_unfold_more(TypeName, TypeNames) of
+ true ->
+ NewTypeNames = [TypeName|TypeNames],
+ {ArgTypes, L1} =
+ list_from_form(Args, NewTypeNames, ET, Site0, MR, V, D, L),
+ List = lists:zip(ArgNames, ArgTypes),
+ TmpV = dict:from_list(List),
+ Site = TypeName,
+ {Rep, L2} =
+ t_from_form(Form, NewTypeNames, ET, Site, MR, TmpV, D, L1),
+ Rep1 = choose_opaque_type(Rep, Type),
+ Rep2 = case cannot_have_opaque(Rep1, TypeName, TypeNames) of
+ true -> Rep1;
+ false ->
+ ArgTypes2 = subst_all_vars_to_any_list(ArgTypes),
+ t_opaque(Module, Name, ArgTypes2, Rep1)
+ end,
+ {Rep2, L2};
+ false -> {t_any(), L}
+ end;
error ->
Msg = io_lib:format("Unable to find type ~w/~w\n", [Name, ArgsLen]),
throw({error, Msg})
end.
-remote_from_form(RemMod, Name, Args, TypeNames, ET, M, MR, V, D, L) ->
- {ArgTypes, L1} = list_from_form(Args, TypeNames, ET, M, MR, V, D, L),
+remote_from_form(RemMod, Name, Args, TypeNames, ET, S, MR, V, D, L) ->
if
ET =:= replace_by_none ->
- {t_none(), L1};
+ {t_none(), L};
true ->
ArgsLen = length(Args),
+ MFA = {RemMod, Name, ArgsLen},
case dict:find(RemMod, MR) of
error ->
- self() ! {self(), ext_types, {RemMod, Name, ArgsLen}},
- {t_any(), L1};
+ self() ! {self(), ext_types, MFA},
+ {t_any(), L};
{ok, RemDict} ->
- MFA = {RemMod, Name, ArgsLen},
+ RemType = {type, MFA},
case sets:is_element(MFA, ET) of
true ->
case lookup_type(Name, ArgsLen, RemDict) of
{type, {{_Mod, _FileLine, Form, ArgNames}, _Type}} ->
- RemType = {type, RemMod, Name, ArgsLen},
case can_unfold_more(RemType, TypeNames) of
true ->
+ NewTypeNames = [RemType|TypeNames],
+ {ArgTypes, L1} = list_from_form(Args, TypeNames,
+ ET, S, MR, V, D, L),
List = lists:zip(ArgNames, ArgTypes),
TmpVarDict = dict:from_list(List),
- NewTypeNames = [RemType|TypeNames],
+ Site = RemType,
t_from_form(Form, NewTypeNames, ET,
- RemMod, MR, TmpVarDict, D, L1);
+ Site, MR, TmpVarDict, D, L1);
false ->
- {t_any(), L1}
+ {t_any(), L}
end;
{opaque, {{Mod, _FileLine, 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(Form, NewTypeNames, ET, RemMod, MR,
- TmpVarDict, D, L1);
- false ->
- {t_any(), L1}
- end,
- NewRep1 = choose_opaque_type(NewRep, Type),
- NewRep2 = case t_is_none(NewRep1) of
- true -> NewRep1;
- false ->
- ArgTypes2 = subst_all_vars_to_any_list(ArgTypes),
- t_opaque(Mod, Name, ArgTypes2, NewRep1)
- end,
- {NewRep2, L2};
+ case can_unfold_more(RemType, TypeNames) of
+ true ->
+ NewTypeNames = [RemType|TypeNames],
+ {ArgTypes, L1} = list_from_form(Args, NewTypeNames,
+ ET, S, MR, V, D, L),
+ List = lists:zip(ArgNames, ArgTypes),
+ TmpVarDict = dict:from_list(List),
+ Site = RemType,
+ {NewRep, L2} =
+ t_from_form(Form, NewTypeNames, ET, Site, MR,
+ TmpVarDict, D, L1),
+ NewRep1 = choose_opaque_type(NewRep, Type),
+ NewRep2 =
+ case
+ cannot_have_opaque(NewRep1, RemType, TypeNames)
+ of
+ true -> NewRep1;
+ false ->
+ ArgTypes2 = subst_all_vars_to_any_list(ArgTypes),
+ t_opaque(Mod, Name, ArgTypes2, NewRep1)
+ end,
+ {NewRep2, L2};
+ false ->
+ {t_any(), L}
+ end;
error ->
Msg = io_lib:format("Unable to find remote type ~w:~w()\n",
[RemMod, Name]),
@@ -4319,7 +4348,7 @@ remote_from_form(RemMod, Name, Args, TypeNames, ET, M, MR, V, D, L) ->
end;
false ->
self() ! {self(), ext_types, {RemMod, Name, ArgsLen}},
- {t_any(), L1}
+ {t_any(), L}
end
end
end.
@@ -4346,22 +4375,24 @@ choose_opaque_type(Type, DeclType) ->
false -> DeclType
end.
-record_from_form({atom, _, Name}, ModFields, TypeNames, ET, M, MR, V, D, L) ->
+record_from_form({atom, _, Name}, ModFields, TypeNames, ET, S, MR, V, D, L) ->
case can_unfold_more({record, Name}, TypeNames) of
true ->
+ M = site_module(S),
{ok, R} = dict:find(M, MR),
case lookup_record(Name, R) of
{ok, DeclFields} ->
NewTypeNames = [{record, Name}|TypeNames],
+ S1 = {record, {M, Name, length(DeclFields)}},
{GetModRec, L1} = get_mod_record(ModFields, DeclFields,
- NewTypeNames, ET, M, MR, V, D, L),
+ NewTypeNames, ET, S1, MR, V, D, L),
case GetModRec of
{error, FieldName} ->
throw({error, io_lib:format("Illegal declaration of #~w{~w}\n",
[Name, FieldName])});
{ok, NewFields} ->
{NewFields1, L2} =
- fields_from_form(NewFields, NewTypeNames, ET, M, MR,
+ fields_from_form(NewFields, NewTypeNames, ET, S1, MR,
dict:new(), D, L1),
Rec = t_tuple(
[t_atom(Name)|[Type
@@ -4375,12 +4406,12 @@ record_from_form({atom, _, Name}, ModFields, TypeNames, ET, M, MR, V, D, L) ->
{t_any(), L}
end.
-get_mod_record([], DeclFields, _TypeNames, _ET, _M, _MR, _V, _D, L) ->
+get_mod_record([], DeclFields, _TypeNames, _ET, _S, _MR, _V, _D, L) ->
{{ok, DeclFields}, L};
-get_mod_record(ModFields, DeclFields, TypeNames, ET, M, MR, V, D, L) ->
+get_mod_record(ModFields, DeclFields, TypeNames, ET, S, MR, V, D, L) ->
DeclFieldsDict = lists:keysort(1, DeclFields),
{ModFieldsDict, L1} =
- build_field_dict(ModFields, TypeNames, ET, M, MR, V, D, L),
+ build_field_dict(ModFields, TypeNames, ET, S, MR, V, D, L),
case get_mod_record_types(DeclFieldsDict, ModFieldsDict, []) of
{error, _FieldName} = Error -> {Error, L1};
{ok, FinalKeyDict} ->
@@ -4389,17 +4420,17 @@ get_mod_record(ModFields, DeclFields, TypeNames, ET, M, MR, V, D, L) ->
{{ok, Fields}, L1}
end.
-build_field_dict(FieldTypes, TypeNames, ET, M, MR, V, D, L) ->
- build_field_dict(FieldTypes, TypeNames, ET, M, MR, V, D, L, []).
+build_field_dict(FieldTypes, TypeNames, ET, S, MR, V, D, L) ->
+ build_field_dict(FieldTypes, TypeNames, ET, S, MR, V, D, L, []).
build_field_dict([{type, _, field_type, [{atom, _, Name}, Type]}|Left],
- TypeNames, ET, M, MR, V, D, L, Acc) ->
- {T, L1} = t_from_form(Type, TypeNames, ET, M, MR, V, D, L - 1),
+ TypeNames, ET, S, MR, V, D, L, Acc) ->
+ {T, L1} = t_from_form(Type, TypeNames, ET, S, MR, V, D, L - 1),
NewAcc = [{Name, Type, T}|Acc],
{Dict, L2} =
- build_field_dict(Left, TypeNames, ET, M, MR, V, D, L1, NewAcc),
+ build_field_dict(Left, TypeNames, ET, S, MR, V, D, L1, NewAcc),
{Dict, L2};
-build_field_dict([], _TypeNames, _ET, _M, _MR, _V, _D, L, Acc) ->
+build_field_dict([], _TypeNames, _ET, _S, _MR, _V, _D, L, Acc) ->
{lists:keysort(1, Acc), L}.
get_mod_record_types([{FieldName, _Abstr, _DeclType}|Left1],
@@ -4418,88 +4449,94 @@ get_mod_record_types(_, [{FieldName2, _FormType, _ModType}|_], _Acc) ->
%% It is important to create a limited version of the record type
%% since nested record types can otherwise easily result in huge
%% terms.
-fields_from_form([], _TypeNames, _ET, _M, _MR, _V, _D, L) ->
+fields_from_form([], _TypeNames, _ET, _S, _MR, _V, _D, L) ->
{[], L};
-fields_from_form([{Name, Abstr, _Type}|Tail], TypeNames, ET, M, MR,
+fields_from_form([{Name, Abstr, _Type}|Tail], TypeNames, ET, S, MR,
V, D, L) ->
- {T, L1} = t_from_form(Abstr, TypeNames, ET, M, MR, V, D, L),
- {F, L2} = fields_from_form(Tail, TypeNames, ET, M, MR, V, D, L1),
+ {T, L1} = t_from_form(Abstr, TypeNames, ET, S, MR, V, D, L),
+ {F, L2} = fields_from_form(Tail, TypeNames, ET, S, MR, V, D, L1),
{[{Name, T}|F], L2}.
-list_from_form([], _TypeNames, _ET, _M, _MR, _V, _D, L) ->
+list_from_form([], _TypeNames, _ET, _S, _MR, _V, _D, L) ->
{[], L};
-list_from_form([H|Tail], TypeNames, ET, M, MR, V, D, L) ->
- {H1, L1} = t_from_form(H, TypeNames, ET, M, MR, V, D, L - 1),
- {T1, L2} = list_from_form(Tail, TypeNames, ET, M, MR, V, D, L1),
+list_from_form([H|Tail], TypeNames, ET, S, MR, V, D, L) ->
+ {H1, L1} = t_from_form(H, TypeNames, ET, S, MR, V, D, L - 1),
+ {T1, L2} = list_from_form(Tail, TypeNames, ET, S, MR, V, D, L1),
{[H1|T1], L2}.
--spec t_check_record_fields(parse_form(), sets:set(mfa()), module(),
+-spec t_check_record_fields(parse_form(), sets:set(mfa()), site(),
mod_records()) -> ok.
-t_check_record_fields(Form, ExpTypes, Module, RecDict) ->
- t_check_record_fields(Form, ExpTypes, Module, RecDict, dict:new()).
+t_check_record_fields(Form, ExpTypes, Site, RecDict) ->
+ t_check_record_fields(Form, ExpTypes, Site, RecDict, dict:new()).
--spec t_check_record_fields(parse_form(), sets:set(mfa()), module(),
+-spec t_check_record_fields(parse_form(), sets:set(mfa()), site(),
mod_records(), var_table()) -> ok.
%% If there is something wrong with parse_form()
%% throw({error, io_lib:chars()} is called.
-t_check_record_fields({var, _L, _}, _ET, _M, _MR, _V) -> ok;
-t_check_record_fields({ann_type, _L, [_Var, Type]}, ET, M, MR, V) ->
- t_check_record_fields(Type, ET, M, MR, V);
-t_check_record_fields({paren_type, _L, [Type]}, ET, M, MR, V) ->
- t_check_record_fields(Type, ET, M, MR, V);
+t_check_record_fields({var, _L, _}, _ET, _S, _MR, _V) -> ok;
+t_check_record_fields({ann_type, _L, [_Var, Type]}, ET, S, MR, V) ->
+ t_check_record_fields(Type, ET, S, MR, V);
+t_check_record_fields({paren_type, _L, [Type]}, ET, S, MR, V) ->
+ t_check_record_fields(Type, ET, S, MR, V);
t_check_record_fields({remote_type, _L, [{atom, _, _}, {atom, _, _}, Args]},
- ET, M, MR, V) ->
- list_check_record_fields(Args, ET, M, MR, V);
-t_check_record_fields({atom, _L, _}, _ET, _M, _MR, _V) -> ok;
-t_check_record_fields({integer, _L, _}, _ET, _M, _MR, _V) -> ok;
-t_check_record_fields({op, _L, _Op, _Arg}, _ET, _M, _MR, _V) -> ok;
-t_check_record_fields({op, _L, _Op, _Arg1, _Arg2}, _ET, _M, _MR, _V) -> ok;
-t_check_record_fields({type, _L, tuple, any}, _ET, _M, _MR, _V) -> ok;
-t_check_record_fields({type, _L, map, any}, _ET, _M, _MR, _V) -> ok;
-t_check_record_fields({type, _L, binary, [_Base, _Unit]}, _ET, _M, _MR, _V) ->
+ ET, S, MR, V) ->
+ list_check_record_fields(Args, ET, S, MR, V);
+t_check_record_fields({atom, _L, _}, _ET, _S, _MR, _V) -> ok;
+t_check_record_fields({integer, _L, _}, _ET, _S, _MR, _V) -> ok;
+t_check_record_fields({op, _L, _Op, _Arg}, _ET, _S, _MR, _V) -> ok;
+t_check_record_fields({op, _L, _Op, _Arg1, _Arg2}, _ET, _S, _MR, _V) -> ok;
+t_check_record_fields({type, _L, tuple, any}, _ET, _S, _MR, _V) -> ok;
+t_check_record_fields({type, _L, map, any}, _ET, _S, _MR, _V) -> ok;
+t_check_record_fields({type, _L, binary, [_Base, _Unit]}, _ET, _S, _MR, _V) ->
ok;
t_check_record_fields({type, _L, 'fun', [{type, _, any}, Range]},
- ET, M, MR, V) ->
- t_check_record_fields(Range, ET, M, MR, V);
-t_check_record_fields({type, _L, range, [_From, _To]}, _ET, _M, _MR, _V) ->
+ ET, S, MR, V) ->
+ t_check_record_fields(Range, ET, S, MR, V);
+t_check_record_fields({type, _L, range, [_From, _To]}, _ET, _S, _MR, _V) ->
ok;
-t_check_record_fields({type, _L, record, [Name|Fields]}, ET, M, MR, V) ->
- check_record(Name, Fields, ET, M, MR, V);
-t_check_record_fields({type, _L, _, Args}, ET, M, MR, V) ->
- list_check_record_fields(Args, ET, M, MR, V);
-t_check_record_fields({user_type, _L, _Name, Args}, ET, M, MR, V) ->
- list_check_record_fields(Args, ET, M, MR, V).
-
-check_record({atom, _, Name}, ModFields, ET, M, MR, V) ->
+t_check_record_fields({type, _L, record, [Name|Fields]}, ET, S, MR, V) ->
+ check_record(Name, Fields, ET, S, MR, V);
+t_check_record_fields({type, _L, _, Args}, ET, S, MR, V) ->
+ list_check_record_fields(Args, ET, S, MR, V);
+t_check_record_fields({user_type, _L, _Name, Args}, ET, S, MR, V) ->
+ list_check_record_fields(Args, ET, S, MR, V).
+
+check_record({atom, _, Name}, ModFields, ET, Site, MR, V) ->
+ M = site_module(Site),
{ok, R} = dict:find(M, MR),
{ok, DeclFields} = lookup_record(Name, R),
- case check_fields(ModFields, DeclFields, ET, M, MR, V) of
+ case check_fields(Name, ModFields, DeclFields, ET, Site, MR, V) of
{error, FieldName} ->
throw({error, io_lib:format("Illegal declaration of #~w{~w}\n",
[Name, FieldName])});
ok -> ok
end.
-check_fields([{type, _, field_type, [{atom, _, Name}, Abstr]}|Left],
- DeclFields, ET, M, MR, V) ->
- Type = t_from_form(Abstr, ET, M, MR, V),
+check_fields(RecName, [{type, _, field_type, [{atom, _, Name}, Abstr]}|Left],
+ DeclFields, ET, Site0, MR, V) ->
+ M = site_module(Site0),
+ Site = {record, {M, RecName, length(DeclFields)}},
+ Type = t_from_form(Abstr, ET, Site, MR, V),
{Name, _, DeclType} = lists:keyfind(Name, 1, DeclFields),
TypeNoVars = subst_all_vars_to_any(Type),
case t_is_subtype(TypeNoVars, DeclType) of
false -> {error, Name};
- true -> check_fields(Left, DeclFields, ET, M, MR, V)
+ true -> check_fields(RecName, Left, DeclFields, ET, Site0, MR, V)
end;
-check_fields([], _Decl, _ET, _M, _MR, _V) ->
+check_fields(_RecName, [], _Decl, _ET, _Site, _MR, _V) ->
ok.
-list_check_record_fields([], _ET, _M, _MR, _V) ->
+list_check_record_fields([], _ET, _S, _MR, _V) ->
ok;
-list_check_record_fields([H|Tail], ET, M, MR, V) ->
- ok = t_check_record_fields(H, ET, M, MR, V),
- list_check_record_fields(Tail, ET, M, MR, V).
+list_check_record_fields([H|Tail], ET, S, MR, V) ->
+ ok = t_check_record_fields(H, ET, S, MR, V),
+ list_check_record_fields(Tail, ET, S, MR, V).
+
+site_module({_, {Module, _, _}}) ->
+ Module.
-spec t_var_names([erl_type()]) -> [atom()].
@@ -4594,8 +4631,9 @@ t_form_to_string({type, _L, Name, []} = T) ->
M = mod,
D0 = dict:new(),
MR = dict:from_list([{M, D0}]),
+ S = {type, {M,Name,0}},
{T1, _} =
- t_from_form(T, [], sets:new(), M, MR, D0, _Deep=1000, _ALot=100000),
+ t_from_form(T, [], sets:new(), S, MR, D0, _Deep=1000, _ALot=100000),
t_to_string(T1)
catch throw:{error, _} -> atom_to_string(Name) ++ "()"
end;
@@ -4687,6 +4725,12 @@ lookup_type(Name, Arity, RecDict) ->
type_is_defined(TypeOrOpaque, Name, Arity, RecDict) ->
dict:is_key({TypeOrOpaque, Name, Arity}, RecDict).
+cannot_have_opaque(Type, TypeName, TypeNames) ->
+ t_is_none(Type) orelse is_recursive(TypeName, TypeNames).
+
+is_recursive(TypeName, TypeNames) ->
+ lists:member(TypeName, TypeNames).
+
can_unfold_more(TypeName, TypeNames) ->
Fun = fun(E, Acc) -> case E of TypeName -> Acc + 1; _ -> Acc end end,
lists:foldl(Fun, 0, TypeNames) < ?REC_TYPE_LIMIT.