diff options
| -rw-r--r-- | lib/dialyzer/src/dialyzer_analysis_callgraph.erl | 7 | ||||
| -rw-r--r-- | lib/dialyzer/src/dialyzer_contracts.erl | 102 | ||||
| -rw-r--r-- | lib/dialyzer/src/dialyzer_utils.erl | 35 | ||||
| -rw-r--r-- | lib/dialyzer/test/opaque_SUITE_data/src/big_external_type.erl | 526 | ||||
| -rw-r--r-- | lib/dialyzer/test/opaque_SUITE_data/src/big_local_type.erl | 523 | ||||
| -rw-r--r-- | lib/dialyzer/test/small_SUITE_data/src/keydel.erl | 29 | ||||
| -rw-r--r-- | lib/eunit/src/eunit_tty.erl | 2 | ||||
| -rw-r--r-- | lib/hipe/cerl/erl_bif_types.erl | 8 | ||||
| -rw-r--r-- | lib/hipe/cerl/erl_types.erl | 498 | ||||
| -rw-r--r-- | lib/ssh/test/ssh_sftp_SUITE.erl | 2 | 
10 files changed, 1438 insertions, 294 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/dialyzer/test/small_SUITE_data/src/keydel.erl b/lib/dialyzer/test/small_SUITE_data/src/keydel.erl new file mode 100644 index 0000000000..18a5c0670c --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/keydel.erl @@ -0,0 +1,29 @@ +-module(keydel). + +-export([store/3]). + +-record(att, {f}). + +-type attachment() :: list(). + +-opaque att() :: #att{} | attachment(). + +-spec store(atom(), any(), att()) -> att(). +store(Field, undefined, Att) when is_list(Att) -> +    lists:keydelete(Field, 1, Att); +store(Field, Value, Att) when is_list(Att) -> +    lists:keystore(Field, 1, Att, {Field, Value}); +store(Field, Value, Att) -> +    store(Field, Value, upgrade(Att)). + + +-spec upgrade(#att{}) -> attachment(). +upgrade(#att{} = Att) -> +    Map = lists:zip( +            record_info(fields, att), +            lists:seq(2, record_info(size, att)) +           ), +    %% Don't store undefined elements since that is default +    [{F, element(I, Att)} || {F, I} <- Map, element(I, Att) /= undefined]; +upgrade(Att) -> +    Att. diff --git a/lib/eunit/src/eunit_tty.erl b/lib/eunit/src/eunit_tty.erl index 699d2adaca..f604ca5ba3 100644 --- a/lib/eunit/src/eunit_tty.erl +++ b/lib/eunit/src/eunit_tty.erl @@ -67,6 +67,8 @@ terminate({ok, Data}, St) ->  		    end,  		    if Pass =:= 1 ->  			    fwrite("  Test passed.\n"); +		       Pass =:= 2 -> +			    fwrite("  2 tests passed.\n");  		       true ->  			    fwrite("  All ~w tests passed.\n", [Pass])  		    end diff --git a/lib/hipe/cerl/erl_bif_types.erl b/lib/hipe/cerl/erl_bif_types.erl index 41a6c731c9..f49089d41c 100644 --- a/lib/hipe/cerl/erl_bif_types.erl +++ b/lib/hipe/cerl/erl_bif_types.erl @@ -2,7 +2,7 @@  %%  %% %CopyrightBegin%  %% -%% Copyright Ericsson AB 2003-2014. All Rights Reserved. +%% Copyright Ericsson AB 2003-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. @@ -93,7 +93,7 @@  		    t_list/0,  		    t_list/1,  		    t_list_elements/2, -		    t_list_termination/1, +		    t_list_termination/2,  		    t_mfa/0,  		    t_module/0,  		    t_nil/0, @@ -1336,8 +1336,8 @@ type(lists, foldr, 3, Xs, _Opaques) -> type(lists, foldl, 3, Xs);  % same  type(lists, keydelete, 3, Xs, Opaques) ->    strict(lists, keydelete, 3, Xs,  	 fun ([_, _, L]) -> -	     Term = t_list_termination(L), -	     t_sup(Term, erl_types:lift_list_to_pos_empty(L)) +	     Term = t_list_termination(L, Opaques), +	     t_sup(Term, erl_types:lift_list_to_pos_empty(L, Opaques))  	 end, Opaques);  type(lists, keyfind, 3, Xs, Opaques) ->    strict(lists, keyfind, 3, Xs, diff --git a/lib/hipe/cerl/erl_types.erl b/lib/hipe/cerl/erl_types.erl index a28dfb9e05..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, @@ -150,7 +150,7 @@  	 t_list/0,  	 t_list/1,  	 t_list_elements/1, t_list_elements/2, -	 t_list_termination/1, +	 t_list_termination/1, t_list_termination/2,  	 t_map/0,  	 t_map/1,  	 t_matchstate/0, @@ -209,7 +209,7 @@  	 record_field_diffs_to_string/2,  	 subst_all_vars_to_any/1,           subst_all_remote/2, -	 lift_list_to_pos_empty/1, +         lift_list_to_pos_empty/1, lift_list_to_pos_empty/2,           is_opaque_type/2,  	 is_erl_type/1,  	 atom_to_string/1 @@ -1510,6 +1510,11 @@ t_list_elements(Type, Opaques) ->  list_elements(?list(Contents, _, _)) -> Contents;  list_elements(?nil) -> ?none. +-spec t_list_termination(erl_type(), opaques()) -> erl_type(). + +t_list_termination(Type, Opaques) -> +  do_opaque(Type, Opaques, fun t_list_termination/1). +  -spec t_list_termination(erl_type()) -> erl_type().  t_list_termination(?nil) -> ?nil; @@ -1585,6 +1590,11 @@ is_maybe_improper_list(_) -> false.  %%   %% false = t_is_subtype(t_nil(), Termination),  %%   ?list(Content, Termination, ?any).   +-spec lift_list_to_pos_empty(erl_type(), opaques()) -> erl_type(). + +lift_list_to_pos_empty(Type, Opaques) -> +  do_opaque(Type, Opaques, fun lift_list_to_pos_empty/1). +  -spec lift_list_to_pos_empty(erl_type()) -> erl_type().  lift_list_to_pos_empty(?nil) -> ?nil; @@ -3961,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. @@ -3995,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()}. @@ -4021,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; @@ -4215,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]), @@ -4309,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. @@ -4336,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 @@ -4365,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} -> @@ -4379,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], @@ -4408,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()]. @@ -4584,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; @@ -4677,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. diff --git a/lib/ssh/test/ssh_sftp_SUITE.erl b/lib/ssh/test/ssh_sftp_SUITE.erl index bab5bf9fe9..8d0b887d83 100644 --- a/lib/ssh/test/ssh_sftp_SUITE.erl +++ b/lib/ssh/test/ssh_sftp_SUITE.erl @@ -291,7 +291,7 @@ init_per_testcase(Case, Config0) ->      prep(Config0),      Config1 = lists:keydelete(watchdog, 1, Config0),      Config2 = lists:keydelete(sftp, 1, Config1), -    Dog = ct:timetrap(?default_timeout), +    Dog = ct:timetrap(2 * ?default_timeout),      User = ?config(user, Config0),      Passwd = ?config(passwd, Config0),  | 
