diff options
Diffstat (limited to 'lib')
34 files changed, 1762 insertions, 423 deletions
diff --git a/lib/debugger/src/dbg_wx_trace.erl b/lib/debugger/src/dbg_wx_trace.erl index b246c71284..5fd1519ba0 100644 --- a/lib/debugger/src/dbg_wx_trace.erl +++ b/lib/debugger/src/dbg_wx_trace.erl @@ -73,6 +73,10 @@ start(Pid, TraceWin, BackTrace) -> start(Pid, TraceWin, BackTrace, Strings) -> case whereis(dbg_wx_mon) of + undefined -> + Parent = wx:new(), + Env = wx:get_env(), + start(Pid, Env, Parent, TraceWin, BackTrace, Strings); Monitor when is_pid(Monitor) -> Monitor ! {?MODULE, self(), get_env}, receive 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 0c29d524b5..7251de8b10 100644 --- a/lib/dialyzer/src/dialyzer_contracts.erl +++ b/lib/dialyzer/src/dialyzer_contracts.erl @@ -46,7 +46,7 @@ -type file_contract() :: {file_line(), #contract{}, Extra :: [_]}. --type plt_contracts() :: [{mfa(), #contract{}}]. % actually, an orddict() +-type plt_contracts() :: orddict:orddict(mfa(), #contract{}). %%----------------------------------------------------------------------- %% Internal record for contracts whose components have not been processed @@ -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/opaque_SUITE_data/src/para/myqueue.erl b/lib/dialyzer/test/opaque_SUITE_data/src/para/myqueue.erl new file mode 100644 index 0000000000..0f76680464 --- /dev/null +++ b/lib/dialyzer/test/opaque_SUITE_data/src/para/myqueue.erl @@ -0,0 +1,17 @@ +-module(myqueue). + +-export([new/0, in/2]). + +-record(myqueue, {queue = queue:new() :: queue:queue({integer(), _})}). + +-opaque myqueue(Item) :: #myqueue{queue :: queue:queue({integer(), Item})}. + +-export_type([myqueue/1]). + +-spec new() -> myqueue(_). +new() -> + #myqueue{queue=queue:new()}. + +-spec in(Item, myqueue(Item)) -> myqueue(Item). +in(Item, #myqueue{queue=Q}) -> + #myqueue{queue=queue:in({1, Item}, Q)}. diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/para/myqueue_params.erl b/lib/dialyzer/test/opaque_SUITE_data/src/para/myqueue_params.erl new file mode 100644 index 0000000000..8d766b7804 --- /dev/null +++ b/lib/dialyzer/test/opaque_SUITE_data/src/para/myqueue_params.erl @@ -0,0 +1,15 @@ +-module(myqueue_params). + +-export([new/0, in/2]). + +-record(myqueue_params, {myqueue = myqueue:new() :: myqueue:myqueue(integer())}). + +-type myqueue_params() :: #myqueue_params{myqueue :: + myqueue:myqueue(integer())}. +-spec new() -> myqueue_params(). +new() -> + #myqueue_params{myqueue=myqueue:new()}. + +-spec in(integer(), myqueue_params()) -> myqueue_params(). +in(Item, #myqueue_params{myqueue=Q} = P) when is_integer(Item) -> + P#myqueue_params{myqueue=myqueue:in(Item, Q)}. 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 25cf1a7ae1..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). -t_from_form1(Form, TypeNames, ET, M, MR, V, D) -> +initial_typenames({type, _MTA}=Site) -> [Site]; +initial_typenames({spec, _MFA}) -> []; +initial_typenames({record, _MRA}) -> []. + +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,92 +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 -> - Args2 = [subst_all_vars_to_any(ArgType) || - ArgType <- ArgTypes], - t_opaque(Module, Name, Args2, 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 -> t_opaque(Mod, Name, ArgTypes, 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]), @@ -4308,11 +4348,14 @@ 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. +subst_all_vars_to_any_list(Types) -> + [subst_all_vars_to_any(Type) || Type <- Types]. + %% Opaque types (both local and remote) are problematic when it comes %% to the limits (TypeNames, D, and L). The reason is that if any() is %% substituted for a more specialized subtype of an opaque type, the @@ -4332,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 @@ -4361,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} -> @@ -4375,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], @@ -4404,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()]. @@ -4580,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; @@ -4673,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/kernel/test/standard_error_SUITE.erl b/lib/kernel/test/standard_error_SUITE.erl index e8917bbd47..97ead9b9fd 100644 --- a/lib/kernel/test/standard_error_SUITE.erl +++ b/lib/kernel/test/standard_error_SUITE.erl @@ -21,13 +21,13 @@ -module(standard_error_SUITE). -export([all/0,suite/0]). --export([badarg/1,getopts/1]). +-export([badarg/1,getopts/1,output/1]). suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [badarg,getopts]. + [badarg,getopts,output]. badarg(Config) when is_list(Config) -> {'EXIT',{badarg,_}} = (catch io:put_chars(standard_error, [oops])), @@ -37,3 +37,30 @@ badarg(Config) when is_list(Config) -> getopts(Config) when is_list(Config) -> [{encoding,latin1}] = io:getopts(standard_error), ok. + +%% Test that writing a lot of output to standard_error does not cause the +%% processes handling it to terminate like this: +%% +%% =ERROR REPORT==== 9-Aug-2015::23:19:23 === +%% ** Generic server standard_error_sup terminating +%% ** Last message in was {'EXIT',<0.28.0>,eagain} +%% ** When Server state == {state,standard_error,undefined,<0.28.0>, +%% {local,standard_error_sup}} +%% ** Reason for termination == +%% ** eagain +%% +%% This problem, observed with Erlang 18.0.2, was fixed in fd_driver by +%% properly handling EAGAIN if it arises on file descriptor writes. +%% +output(Config) when is_list(Config) -> + Ref = monitor(process, standard_error_sup), + Chars = [ [["1234567890" || _ <- lists:seq(1,10)], $\s, + integer_to_list(L), $\r, $\n] || L <- lists:seq(1, 100) ], + ok = io:put_chars(standard_error, Chars), + receive + {'DOWN', Ref, process, _, _} -> + error(standard_error_noproc) + after + 500 -> + ok + end. diff --git a/lib/mnesia/examples/bench/bench_generate.erl b/lib/mnesia/examples/bench/bench_generate.erl index 7a701812a7..e838f07fbb 100644 --- a/lib/mnesia/examples/bench/bench_generate.erl +++ b/lib/mnesia/examples/bench/bench_generate.erl @@ -153,9 +153,7 @@ generator_init(Monitor, C) -> process_flag(trap_exit, true), Tables = mnesia:system_info(tables), ok = mnesia:wait_for_tables(Tables, infinity), - {_Mega, Sec, Micro} = erlang:now(), - Uniq = lists:sum(binary_to_list(term_to_binary(make_ref()))), - random:seed(Uniq, Sec, Micro), + rand:seed(exsplus), Counters = reset_counters(C, C#config.statistics_detail), SessionTab = ets:new(bench_sessions, [public, {keypos, 1}]), generator_loop(Monitor, C, SessionTab, Counters). @@ -189,9 +187,9 @@ generator_loop(Monitor, C, SessionTab, Counters) -> after 0 -> {Name, {Nodes, Activity, Wlock}, Fun, CommitSessions} = gen_trans(C, SessionTab), - Before = erlang:now(), + Before = erlang:monotonic_time(), Res = call_worker(Nodes, Activity, Fun, Wlock, mnesia_frag), - After = erlang:now(), + After = erlang:monotonic_time(), Elapsed = elapsed(Before, After), post_eval(Monitor, C, Elapsed, Res, Name, CommitSessions, SessionTab, Counters) end. @@ -253,10 +251,8 @@ worker_loop(Parent) -> end. -elapsed({Before1, Before2, Before3}, {After1, After2, After3}) -> - After = After1 * 1000000000000 + After2 * 1000000 + After3, - Before = Before1 * 1000000000000 + Before2 * 1000000 + Before3, - After - Before. +elapsed(Before, After) -> + erlang:convert_time_unit(After-Before, native, micro_seconds). %% Lookup counters get_counters(_C, {table, Tab}) -> @@ -351,7 +347,7 @@ commit_session(Fun) when is_function(Fun, 0) -> %% Randlomly choose a transaction type according to benchmar spec gen_trans(C, SessionTab) when C#config.generator_profile == random -> - case random:uniform(100) of + case rand:uniform(100) of Rand when Rand > 0, Rand =< 25 -> gen_t1(C, SessionTab); Rand when Rand > 25, Rand =< 50 -> gen_t2(C, SessionTab); Rand when Rand > 50, Rand =< 70 -> gen_t3(C, SessionTab); @@ -369,7 +365,7 @@ gen_trans(C, SessionTab) -> end. gen_t1(C, _SessionTab) -> - SubscrId = random:uniform(C#config.n_subscribers) - 1, + SubscrId = rand:uniform(C#config.n_subscribers) - 1, SubscrKey = bench_trans:number_to_key(SubscrId, C), Location = 4711, ChangedBy = <<4711:(8*25)>>, @@ -381,7 +377,7 @@ gen_t1(C, _SessionTab) -> }. gen_t2(C, _SessionTab) -> - SubscrId = random:uniform(C#config.n_subscribers) - 1, + SubscrId = rand:uniform(C#config.n_subscribers) - 1, SubscrKey = bench_trans:number_to_key(SubscrId, C), {t2, nearest_node(SubscrId, sync_dirty, C), @@ -395,9 +391,9 @@ gen_t3(C, SessionTab) -> '$end_of_table' -> %% This generator does not have any session, %% try reading someone elses session details - SubscrId = random:uniform(C#config.n_subscribers) - 1, + SubscrId = rand:uniform(C#config.n_subscribers) - 1, SubscrKey = bench_trans:number_to_key(SubscrId, C), - ServerId = random:uniform(C#config.n_servers) - 1, + ServerId = rand:uniform(C#config.n_servers) - 1, ServerBit = 1 bsl ServerId, {t3, nearest_node(SubscrId, transaction, C), @@ -419,12 +415,12 @@ gen_t4(C, SessionTab) -> %% This generator may already have sessions, %% create a new session and hope that no other %% generator already has occupied it - SubscrId = random:uniform(C#config.n_subscribers) - 1, + SubscrId = rand:uniform(C#config.n_subscribers) - 1, SubscrKey = bench_trans:number_to_key(SubscrId, C), - ServerId = random:uniform(C#config.n_servers) - 1, + ServerId = rand:uniform(C#config.n_servers) - 1, ServerBit = 1 bsl ServerId, Details = <<4711:(8*2000)>>, - DoRollback = (random:uniform(100) =< 2), + DoRollback = (rand:uniform(100) =< 2), Insert = fun() -> ets:insert(SessionTab, {{SubscrId, SubscrKey, ServerId}, self()}) end, {t4, nearest_node(SubscrId, transaction, C), @@ -437,11 +433,11 @@ gen_t5(C, SessionTab) -> '$end_of_table' -> %% This generator does not have any session, %% try to delete someone elses session details - SubscrId = random:uniform(C#config.n_subscribers) - 1, + SubscrId = rand:uniform(C#config.n_subscribers) - 1, SubscrKey = bench_trans:number_to_key(SubscrId, C), - ServerId = random:uniform(C#config.n_servers) - 1, + ServerId = rand:uniform(C#config.n_servers) - 1, ServerBit = 1 bsl ServerId, - DoRollback = (random:uniform(100) =< 2), + DoRollback = (rand:uniform(100) =< 2), {t5, nearest_node(SubscrId, transaction, C), fun(Wlock) -> bench_trans:delete_session_from_server(Wlock, SubscrKey, ServerBit, ServerId, DoRollback) end, @@ -451,7 +447,7 @@ gen_t5(C, SessionTab) -> %% This generator do have at least one session, %% delete it. ServerBit = 1 bsl ServerId, - DoRollback = (random:uniform(100) =< 2), + DoRollback = (rand:uniform(100) =< 2), Delete = fun() -> ets:delete(SessionTab, {SubscrId, SubscrKey, ServerId}) end, {t5, nearest_node(SubscrId, transaction, C), @@ -461,7 +457,7 @@ gen_t5(C, SessionTab) -> end. gen_ping(C, _SessionTab) -> - SubscrId = random:uniform(C#config.n_subscribers) - 1, + SubscrId = rand:uniform(C#config.n_subscribers) - 1, {ping, nearest_node(SubscrId, transaction, C), fun(_Wlock) -> {do_commit, true, []} end, diff --git a/lib/mnesia/examples/mnesia_tpcb.erl b/lib/mnesia/examples/mnesia_tpcb.erl index fde6cf402e..c6eda1c448 100644 --- a/lib/mnesia/examples/mnesia_tpcb.erl +++ b/lib/mnesia/examples/mnesia_tpcb.erl @@ -164,7 +164,7 @@ -record(history, { history_id = {0, 0}, % {DriverId, DriverLocalHistoryid} - time_stamp = now(), % Time point during active transaction + time_stamp = erlang:system_time(), % Time point during active transaction branch_id = 0, % Branch associated with teller teller_id = 0, % Teller invlolved in transaction account_id = 0, % Account updated by transaction @@ -412,9 +412,8 @@ config(remote_frag2_test, ReplicaType) -> config(conflict_benchmark, ReplicaType) -> Remote = nodes(), Local = node(), - Nodes = [Local | Remote], - [{seed, {1326,448637,337711}}, - {db_nodes, Nodes}, + Nodes = [Local | Remote], + [{db_nodes, Nodes}, {driver_nodes, Nodes}, {replica_nodes, Nodes}, {n_drivers_per_node, 10}, @@ -758,7 +757,7 @@ reporter_init(Starter, RC) -> replica_type = Type }, Drivers = start_drivers(RC, TC), - Now = now_to_micros(erlang:now()), + Now = erlang:monotonic_time(), State = #reporter_state{driver_pids = Drivers, run_config = RC, starter_pid = Starter, @@ -896,7 +895,7 @@ add_time(Acc, New) -> -define(AVOID_DIV_ZERO(_What_), try (_What_) catch _:_ -> 0 end). show_report(State) -> - Now = now_to_micros(erlang:now()), + Now = erlang:timestamp(), Iters = State#reporter_state.n_iters, Cfg = State#reporter_state.run_config, Time = State#reporter_state.curr, @@ -924,14 +923,14 @@ show_report(State) -> case Cfg#run_config.send_bench_report of true -> ct_event:notify( - #event{name = benchmark_data, + #event{name = benchmark_data, data = [{suite,"mnesia_tpcb"}, {value,Tps}]}); _ -> ok end, - State#reporter_state{prev_tps = Tps, prev_micros = Now}. + State#reporter_state{prev_tps = Tps, prev_micros = Now}. signed_diff(Iters, Curr, Prev) -> case Iters > 1 of @@ -941,11 +940,6 @@ signed_diff(Iters, Curr, Prev) -> sign(N) when N > 0 -> {"+", N}; sign(N) -> {"", N}. - -now_to_micros({Mega, Secs, Micros}) -> - DT = calendar:now_to_datetime({Mega, Secs, 0}), - S = calendar:datetime_to_gregorian_seconds(DT), - (S * ?SECOND) + Micros. start_drivers(RC, TC) -> LastHistoryId = table_info(history, size), @@ -998,13 +992,11 @@ alloc_local_branches([], Specs, OrphanBranches) -> {Specs, OrphanBranches}. driver_init(DS, AllBranches) -> - case (DS#driver_state.run_config)#run_config.seed of - undefined -> - Seed = erlang:now(); - Seed -> - Seed + Seed = case (DS#driver_state.run_config)#run_config.seed of + undefined -> rand:seed(exsplus); + ExpSeed -> rand:seed(ExpSeed) end, - + DS2 = if DS#driver_state.n_local_branches =:= 0 -> @@ -1058,14 +1050,7 @@ calc_trans(DS) -> %% Generate teller_id, account_id and delta %% Time the TPC-B transaction time_trans(DS) -> - OldSeed = get(random_seed), % Avoid interference with Mnesia - put(random_seed, DS#driver_state.seed), - Random = random:uniform(), - NewSeed = get(random_seed), - case OldSeed of - undefined -> erase(random_seed); - _ -> put(random_seed, OldSeed) - end, + {Random, NewSeed} = rand:uniform_s(DS#driver_state.seed), TC = DS#driver_state.tab_config, RC = DS#driver_state.run_config, diff --git a/lib/mnesia/test/mnesia_atomicity_test.erl b/lib/mnesia/test/mnesia_atomicity_test.erl index 1d9d9c35bc..e3e0eaaf75 100644 --- a/lib/mnesia/test/mnesia_atomicity_test.erl +++ b/lib/mnesia/test/mnesia_atomicity_test.erl @@ -557,8 +557,8 @@ start_lock_waiter(BlockOpA, BlockOpB, Config) -> ?verify_mnesia([N1], [N2]). mk_tab_name(Prefix) -> - {Mega, Sec, Micro} = erlang:now(), - list_to_atom(lists:concat([Prefix , Mega, '_', Sec, '_', Micro])). + Count = erlang:unique_integer([monotonic,positive]), + list_to_atom(lists:concat([Prefix , '_', Count])). lock_waiter_fun(Op, TabName, Val) -> case Op of diff --git a/lib/mnesia/test/mnesia_config_test.erl b/lib/mnesia/test/mnesia_config_test.erl index c8a6a000c6..089fbc06dc 100644 --- a/lib/mnesia/test/mnesia_config_test.erl +++ b/lib/mnesia/test/mnesia_config_test.erl @@ -1206,7 +1206,7 @@ dynamic_ext(Config) when is_list(Config) -> end, [Check(Test) || Test <- [{tab1, ram_copies},{tab2, disc_copies},{tab3, disc_only_copies}]], - T = now(), + T = erlang:unique_integer(), ?match(ok, mnesia:dirty_write({tab0, 42, T})), ?match(ok, mnesia:dirty_write({tab1, 42, T})), ?match(ok, mnesia:dirty_write({tab2, 42, T})), @@ -1284,7 +1284,7 @@ check_storage(Me, Orig, Other) -> mnesia_test_lib:kill_mnesia([Orig]), mnesia_test_lib:kill_mnesia(Other), - T = now(), + T = erlang:unique_integer(), ?match(ok, rpc:call(Me, mnesia, dirty_write, [{tab2, 42, T}])), ?match(stopped, rpc:call(Me, mnesia, stop, [])), ?match(ok, rpc:call(Me, mnesia, start, [])), diff --git a/lib/mnesia/test/mnesia_cost.erl b/lib/mnesia/test/mnesia_cost.erl index ff0108ced1..714dbaef27 100644 --- a/lib/mnesia/test/mnesia_cost.erl +++ b/lib/mnesia/test/mnesia_cost.erl @@ -108,11 +108,11 @@ run(What, OtherInfo, Ops, F) -> run(t, What, OtherInfo, Ops, F). run(How, What, OtherInfo, Ops, F) -> - T1 = erlang:now(), + T1 = erlang:monotonic_time(), statistics(runtime), do_times(How, ?TIMES, F), {_, RunTime} = statistics(runtime), - T2 = erlang:now(), + T2 = erlang:monotonic_time(), RealTime = subtr(T1, T2), report(How, What, OtherInfo, Ops, RunTime, RealTime). @@ -140,11 +140,7 @@ report(dirty, What, OtherInfo, Ops, RunTime, RealTime) -> subtr(Before, After) -> - E =(element(1,After)*1000000000000 - +element(2,After)*1000000+element(3,After)) - - (element(1,Before)*1000000000000 - +element(2,Before)*1000000+element(3,Before)), - E div 1000. + erlang:convert_time_unit(After-Before, native, milli_seconds). do_times(t, I, F) -> do_trans_times(I, F); diff --git a/lib/mnesia/test/mnesia_dbn_meters.erl b/lib/mnesia/test/mnesia_dbn_meters.erl index f97bd973fc..5c3ea08a1d 100644 --- a/lib/mnesia/test/mnesia_dbn_meters.erl +++ b/lib/mnesia/test/mnesia_dbn_meters.erl @@ -93,7 +93,7 @@ some_meters() -> report_meter(Meter) -> Times = 100, Micros = repeat_meter(Meter,{atomic,{0,ignore}},Times) div Times, - io:format("\t~-30w ~-10w micro seconds (mean of ~p repetitions)~n",[Meter,Micros,Times]). + io:format("\t~-30w ~-10w nano seconds (mean of ~p repetitions)~n",[Meter,Micros,Times]). repeat_meter(_Meter,{atomic,{Micros,_Result}},0) -> Micros; @@ -110,9 +110,9 @@ meter(create) -> Key = 1, mnesia:transaction(fun() -> mnesia:delete({simple,Key}) end), Fun = fun() -> - BeforeT = erlang:now(), + BeforeT = erlang:monotonic_time(), R = mnesia:write(#simple{key=Key}), - AfterT = erlang:now(), + AfterT = erlang:monotonic_time(), elapsed_time(BeforeT,AfterT,R) end, mnesia:transaction(Fun); @@ -121,9 +121,9 @@ meter(open_safe_read) -> Key = 2, mnesia:transaction(fun() -> mnesia:write(#simple{key=Key}) end), Fun = fun() -> - BeforeT = erlang:now(), + BeforeT = erlang:monotonic_time(), R = mnesia:read({simple,Key}), - AfterT = erlang:now(), + AfterT = erlang:monotonic_time(), elapsed_time(BeforeT,AfterT,R) end, mnesia:transaction(Fun); @@ -132,9 +132,9 @@ meter(open_dirty_read) -> Key = 21, mnesia:transaction(fun() -> mnesia:write(#simple{key=Key}) end), Fun = fun() -> - BeforeT = erlang:now(), + BeforeT = erlang:monotonic_time(), R = mnesia:dirty_read({simple,Key}), - AfterT = erlang:now(), + AfterT = erlang:monotonic_time(), elapsed_time(BeforeT,AfterT,R) end, mnesia:transaction(Fun); @@ -144,9 +144,9 @@ meter(get_int) -> mnesia:transaction(fun() -> mnesia:write(#simple{key=Key}) end), Fun = fun() -> [Simple] = mnesia:read({simple,Key}), - BeforeT = erlang:now(), + BeforeT = erlang:monotonic_time(), Int = Simple#simple.val, - AfterT = erlang:now(), + AfterT = erlang:monotonic_time(), elapsed_time(BeforeT,AfterT,Int) end, mnesia:transaction(Fun); @@ -155,9 +155,9 @@ meter(open_update) -> Key = 3, mnesia:transaction(fun() -> mnesia:write(#simple{key=Key}) end), Fun = fun() -> - BeforeT = erlang:now(), + BeforeT = erlang:monotonic_time(), R = mnesia:wread({simple,Key}), - AfterT = erlang:now(), + AfterT = erlang:monotonic_time(), elapsed_time(BeforeT,AfterT,R) end, mnesia:transaction(Fun); @@ -167,9 +167,9 @@ meter(put_int) -> mnesia:transaction(fun() -> mnesia:write(#simple{key=Key}) end), Fun = fun() -> [Simple] = mnesia:wread({simple,Key}), - BeforeT = erlang:now(), + BeforeT = erlang:monotonic_time(), R = Simple#simple{val=7}, - AfterT = erlang:now(), + AfterT = erlang:monotonic_time(), elapsed_time(BeforeT,AfterT,R) end, mnesia:transaction(Fun); @@ -179,10 +179,10 @@ meter(put_int_and_copy) -> mnesia:transaction(fun() -> mnesia:write(#simple{key=Key}) end), Fun = fun() -> [Simple] = mnesia:wread({simple,Key}), - BeforeT = erlang:now(), + BeforeT = erlang:monotonic_time(), Simple2 = Simple#simple{val=17}, R = mnesia:write(Simple2), - AfterT = erlang:now(), + AfterT = erlang:monotonic_time(), elapsed_time(BeforeT,AfterT,R) end, mnesia:transaction(Fun); @@ -191,15 +191,15 @@ meter(dirty_put_int_and_copy) -> Key = 55, mnesia:dirty_write(#simple{key=Key}), [Simple] = mnesia:dirty_read({simple,Key}), - BeforeT = erlang:now(), + BeforeT = erlang:monotonic_time(), Simple2 = Simple#simple{val=17}, R = mnesia:dirty_write(Simple2), - AfterT = erlang:now(), + AfterT = erlang:monotonic_time(), {atomic,elapsed_time(BeforeT,AfterT,R)}; meter(start_trans) -> - BeforeT = erlang:now(), - {atomic,AfterT} = mnesia:transaction(fun() -> erlang:now() end), + BeforeT = erlang:monotonic_time(), + {atomic,AfterT} = mnesia:transaction(fun() -> erlang:monotonic_time() end), {atomic,elapsed_time(BeforeT,AfterT,ok)}; meter(commit_one_update) -> @@ -209,19 +209,19 @@ meter(commit_one_update) -> [Simple] = mnesia:wread({simple,Key}), Simple2 = Simple#simple{val=27}, _R = mnesia:write(Simple2), - erlang:now() + erlang:monotonic_time() end, {atomic,BeforeT} = mnesia:transaction(Fun), - AfterT = erlang:now(), + AfterT = erlang:monotonic_time(), {atomic,elapsed_time(BeforeT,AfterT,ok)}; meter(delete) -> Key = 7, mnesia:transaction(fun() -> mnesia:write(#simple{key=Key}) end), Fun = fun() -> - BeforeT = erlang:now(), + BeforeT = erlang:monotonic_time(), R = mnesia:delete({simple,Key}), - AfterT = erlang:now(), + AfterT = erlang:monotonic_time(), elapsed_time(BeforeT,AfterT,R) end, mnesia:transaction(Fun); @@ -229,15 +229,12 @@ meter(delete) -> meter(dirty_delete) -> Key = 75, mnesia:dirty_write(#simple{key=Key}), - BeforeT = erlang:now(), + BeforeT = erlang:monotonic_time(), R = mnesia:dirty_delete({simple,Key}), - AfterT = erlang:now(), + AfterT = erlang:monotonic_time(), {atomic, elapsed_time(BeforeT,AfterT,R)}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Calculate the elapsed time elapsed_time(BeforeT,AfterT,Result) -> - {(element(1,AfterT)*1000000000000 - +element(2,AfterT)*1000000+element(3,AfterT)) - - (element(1,BeforeT)*1000000000000 - +element(2,BeforeT)*1000000+element(3,BeforeT)),Result}. + {erlang:convert_time_unit(AfterT-BeforeT, native, nano_seconds),Result}. diff --git a/lib/mnesia/test/mnesia_evil_backup.erl b/lib/mnesia/test/mnesia_evil_backup.erl index 5392267f79..89f2861661 100644 --- a/lib/mnesia/test/mnesia_evil_backup.erl +++ b/lib/mnesia/test/mnesia_evil_backup.erl @@ -229,7 +229,7 @@ restore(Config, Op) -> [mnesia:dirty_write({Tab1, N, N+1}) || N <- lists:seq(1, 11)], [mnesia:dirty_write({Tab2, N, N+1}) || N <- lists:seq(1, 11)], [mnesia:dirty_write({Tab3, N, N+1}) || N <- lists:seq(1, 11)], - _Res11 = [{Tab1, N, N+1} || N <- lists:seq(1, 11)], + Res21 = [{Tab2, N, N+1} || N <- lists:seq(1, 11)], Res31 = [[{Tab3, N, N+1}, {Tab3, N, N+44}] || N <- lists:seq(1, 10)], diff --git a/lib/mnesia/test/mnesia_isolation_test.erl b/lib/mnesia/test/mnesia_isolation_test.erl index 6abb1f7cdc..b66da6e390 100644 --- a/lib/mnesia/test/mnesia_isolation_test.erl +++ b/lib/mnesia/test/mnesia_isolation_test.erl @@ -1127,7 +1127,9 @@ update_shared(Tab, Me, Acc) -> 0 -> case mnesia:transaction(Update) of {atomic, {ok,Term,W2}} -> - io:format("~p:~p:(~p,~p) ~w@~w~n", [erlang:now(),node(),Me,Acc,Term,W2]), + io:format("~p:~p:(~p,~p) ~w@~w~n", + [erlang:unique_integer([monotonic,positive]), + node(),Me,Acc,Term,W2]), update_shared(Tab, Me, Acc+1); Else -> ?error("Trans failed on ~p with ~p~n" diff --git a/lib/mnesia/test/mnesia_test_lib.erl b/lib/mnesia/test/mnesia_test_lib.erl index 035d6bde87..9d3b277e07 100644 --- a/lib/mnesia/test/mnesia_test_lib.erl +++ b/lib/mnesia/test/mnesia_test_lib.erl @@ -238,8 +238,8 @@ slave_start_link() -> slave_start_link(Node) -> [Local, Host] = node_to_name_and_host(Node), - {Mega, Sec, Micro} = erlang:now(), - List = [Local, "_", Mega, "_", Sec, "_", Micro], + Count = erlang:unique_integer([positive]), + List = [Local, "_", Count], Name = list_to_atom(lists:concat(List)), slave_start_link(list_to_atom(Host), Name). diff --git a/lib/mnesia/test/mnesia_tpcb.erl b/lib/mnesia/test/mnesia_tpcb.erl index 3f936591b0..c6eda1c448 100644 --- a/lib/mnesia/test/mnesia_tpcb.erl +++ b/lib/mnesia/test/mnesia_tpcb.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2013. 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. @@ -100,9 +100,13 @@ replica_test/1, sticky_replica_test/1, remote_test/1, - remote_frag2_test/1 + remote_frag2_test/1, + + conflict_benchmark/1 ]). +-include_lib("common_test/include/ct_event.hrl"). + -define(SECOND, 1000000). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -160,7 +164,7 @@ -record(history, { history_id = {0, 0}, % {DriverId, DriverLocalHistoryid} - time_stamp = now(), % Time point during active transaction + time_stamp = erlang:system_time(), % Time point during active transaction branch_id = 0, % Branch associated with teller teller_id = 0, % Teller invlolved in transaction account_id = 0, % Account updated by transaction @@ -192,8 +196,10 @@ driver_nodes = [node()], n_drivers_per_node = 1, use_running_mnesia = false, + seed, stop_after = timer:minutes(15), % Minimum 15 min report_interval = timer:minutes(1), + send_bench_report = false, use_sticky_locks = false, spawn_near_branch = false, activity_type = transaction, @@ -398,8 +404,29 @@ config(remote_frag2_test, ReplicaType) -> {stop_after, timer:minutes(1)}, {report_interval, timer:seconds(10)}, {reuse_history_id, true} + ]; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Ten drivers per node, tables replicated to all nodes, single branch + +config(conflict_benchmark, ReplicaType) -> + Remote = nodes(), + Local = node(), + Nodes = [Local | Remote], + [{db_nodes, Nodes}, + {driver_nodes, Nodes}, + {replica_nodes, Nodes}, + {n_drivers_per_node, 10}, + {n_branches, 1}, + {n_accounts_per_branch, 10}, + {replica_type, ReplicaType}, + {stop_after, timer:minutes(1)}, + {report_interval, timer:seconds(10)}, + {send_bench_report, true}, + {reuse_history_id, true} ]. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% start(What, ReplicaType) -> @@ -423,6 +450,9 @@ remote_test(ReplicaType) -> remote_frag2_test(ReplicaType) -> start(remote_frag2_test, ReplicaType). +conflict_benchmark(ReplicaType) -> + start(config(conflict_benchmark, ReplicaType)). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Args is a list of {Key, Val} tuples where Key is a field name %% in either the record tab_config or run_config. Unknown keys are ignored. @@ -727,7 +757,7 @@ reporter_init(Starter, RC) -> replica_type = Type }, Drivers = start_drivers(RC, TC), - Now = now_to_micros(erlang:now()), + Now = erlang:monotonic_time(), State = #reporter_state{driver_pids = Drivers, run_config = RC, starter_pid = Starter, @@ -865,8 +895,9 @@ add_time(Acc, New) -> -define(AVOID_DIV_ZERO(_What_), try (_What_) catch _:_ -> 0 end). show_report(State) -> - Now = now_to_micros(erlang:now()), + Now = erlang:timestamp(), Iters = State#reporter_state.n_iters, + Cfg = State#reporter_state.run_config, Time = State#reporter_state.curr, Max = Time#time.max_time, N = Time#time.n_trans, @@ -889,6 +920,16 @@ show_report(State) -> "duration of longest transaction was ~p milliseconds~n", [Tps, BruttoTps, Max div 1000]) end, + case Cfg#run_config.send_bench_report of + true -> + ct_event:notify( + #event{name = benchmark_data, + data = [{suite,"mnesia_tpcb"}, + {value,Tps}]}); + _ -> + ok + end, + State#reporter_state{prev_tps = Tps, prev_micros = Now}. signed_diff(Iters, Curr, Prev) -> @@ -899,11 +940,6 @@ signed_diff(Iters, Curr, Prev) -> sign(N) when N > 0 -> {"+", N}; sign(N) -> {"", N}. - -now_to_micros({Mega, Secs, Micros}) -> - DT = calendar:now_to_datetime({Mega, Secs, 0}), - S = calendar:datetime_to_gregorian_seconds(DT), - (S * ?SECOND) + Micros. start_drivers(RC, TC) -> LastHistoryId = table_info(history, size), @@ -956,7 +992,11 @@ alloc_local_branches([], Specs, OrphanBranches) -> {Specs, OrphanBranches}. driver_init(DS, AllBranches) -> - Seed = erlang:now(), + Seed = case (DS#driver_state.run_config)#run_config.seed of + undefined -> rand:seed(exsplus); + ExpSeed -> rand:seed(ExpSeed) + end, + DS2 = if DS#driver_state.n_local_branches =:= 0 -> @@ -1010,14 +1050,7 @@ calc_trans(DS) -> %% Generate teller_id, account_id and delta %% Time the TPC-B transaction time_trans(DS) -> - OldSeed = get(random_seed), % Avoid interference with Mnesia - put(random_seed, DS#driver_state.seed), - Random = random:uniform(), - NewSeed = get(random_seed), - case OldSeed of - undefined -> erase(random_seed); - _ -> put(random_seed, OldSeed) - end, + {Random, NewSeed} = rand:uniform_s(DS#driver_state.seed), TC = DS#driver_state.tab_config, RC = DS#driver_state.run_config, diff --git a/lib/ssh/src/Makefile b/lib/ssh/src/Makefile index 98fb90d7c4..b44c8eef35 100644 --- a/lib/ssh/src/Makefile +++ b/lib/ssh/src/Makefile @@ -144,3 +144,78 @@ release_spec: opt release_docs_spec: + +deps: + erlc -M $(ERL_FILES) \ + | sed 's@$(ERL_TOP)/lib@../..@g' \ + | sed 's/\.$(EMULATOR)/\.$$\(EMULATOR\)/' \ + | sed 's@^ssh_@$$(EBIN)/ssh_@' + +ssh.$(EMULATOR): ssh.erl ssh.hrl ssh_connect.hrl \ + ../../public_key/include/public_key.hrl \ + ../../public_key/include/OTP-PUB-KEY.hrl \ + ../../public_key/include/PKCS-FRAME.hrl \ + ../../kernel/include/file.hrl +$(EBIN)/ssh_sup.$(EMULATOR): ssh_sup.erl +sshc_sup.$(EMULATOR): sshc_sup.erl +sshd_sup.$(EMULATOR): sshd_sup.erl ssh.hrl +$(EBIN)/ssh_connection_sup.$(EMULATOR): ssh_connection_sup.erl +$(EBIN)/ssh_connection.$(EMULATOR): ssh_connection.erl ssh.hrl ssh_connect.hrl \ + ssh_transport.hrl +$(EBIN)/ssh_connection_handler.$(EMULATOR): ssh_connection_handler.erl ssh.hrl \ + ssh_transport.hrl ssh_auth.hrl ssh_connect.hrl +$(EBIN)/ssh_shell.$(EMULATOR): ssh_shell.erl ssh_connect.hrl +$(EBIN)/ssh_system_sup.$(EMULATOR): ssh_system_sup.erl ssh.hrl +$(EBIN)/ssh_subsystem_sup.$(EMULATOR): ssh_subsystem_sup.erl +$(EBIN)/ssh_channel_sup.$(EMULATOR): ssh_channel_sup.erl +$(EBIN)/ssh_acceptor_sup.$(EMULATOR): ssh_acceptor_sup.erl ssh.hrl +$(EBIN)/ssh_acceptor.$(EMULATOR): ssh_acceptor.erl ssh.hrl +$(EBIN)/ssh_app.$(EMULATOR): ssh_app.erl +$(EBIN)/ssh_auth.$(EMULATOR): ssh_auth.erl \ + ../../public_key/include/public_key.hrl \ + ../../public_key/include/OTP-PUB-KEY.hrl \ + ../../public_key/include/PKCS-FRAME.hrl \ + ssh.hrl ssh_auth.hrl ssh_transport.hrl +$(EBIN)/ssh_bits.$(EMULATOR): ssh_bits.erl ssh.hrl +$(EBIN)/ssh_cli.$(EMULATOR): ssh_cli.erl ssh.hrl ssh_connect.hrl +$(EBIN)/ssh_file.$(EMULATOR): ssh_file.erl \ + ../../public_key/include/public_key.hrl \ + ../../public_key/include/OTP-PUB-KEY.hrl \ + ../../public_key/include/PKCS-FRAME.hrl \ + ../../kernel/include/file.hrl ssh.hrl +$(EBIN)/ssh_io.$(EMULATOR): ssh_io.erl ssh.hrl +$(EBIN)/ssh_info.$(EMULATOR): ssh_info.erl +$(EBIN)/ssh_message.$(EMULATOR): ssh_message.erl \ + ../../public_key/include/public_key.hrl \ + ../../public_key/include/OTP-PUB-KEY.hrl \ + ../../public_key/include/PKCS-FRAME.hrl \ + ssh.hrl ssh_connect.hrl ssh_auth.hrl ssh_transport.hrl +$(EBIN)/ssh_no_io.$(EMULATOR): ssh_no_io.erl ssh_transport.hrl +$(EBIN)/ssh_sftp.$(EMULATOR): ssh_sftp.erl \ + ../../kernel/include/file.hrl ssh.hrl \ + ssh_xfer.hrl +$(EBIN)/ssh_sftpd.$(EMULATOR): ssh_sftpd.erl \ + ../../kernel/include/file.hrl ssh.hrl \ + ssh_xfer.hrl +$(EBIN)/ssh_sftpd_file.$(EMULATOR): ssh_sftpd_file.erl +$(EBIN)/ssh_transport.$(EMULATOR): ssh_transport.erl \ + ../../public_key/include/public_key.hrl \ + ../../public_key/include/OTP-PUB-KEY.hrl \ + ../../public_key/include/PKCS-FRAME.hrl \ + ../../kernel/include/inet.hrl \ + ssh_transport.hrl ssh.hrl +$(EBIN)/ssh_xfer.$(EMULATOR): ssh_xfer.erl ssh.hrl ssh_xfer.hrl +$(EBIN)/ssh_sftpd_file_api.$(EMULATOR): ssh_sftpd_file_api.erl +$(EBIN)/ssh_channel.$(EMULATOR): ssh_channel.erl ssh_connect.hrl +$(EBIN)/ssh_daemon_channel.$(EMULATOR): ssh_daemon_channel.erl +$(EBIN)/ssh_client_key_api.$(EMULATOR): ssh_client_key_api.erl \ + ../../public_key/include/public_key.hrl \ + ../../public_key/include/OTP-PUB-KEY.hrl \ + ../../public_key/include/PKCS-FRAME.hrl \ + ssh.hrl +$(EBIN)/ssh_server_key_api.$(EMULATOR): ssh_server_key_api.erl \ + ../../public_key/include/public_key.hrl \ + ../../public_key/include/OTP-PUB-KEY.hrl \ + ../../public_key/include/PKCS-FRAME.hrl \ + ssh.hrl + diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl index 5b2e0a988c..132de71aed 100644 --- a/lib/ssh/src/ssh.erl +++ b/lib/ssh/src/ssh.erl @@ -397,6 +397,8 @@ handle_option([{id_string, _ID} = Opt|Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); handle_option([{profile, _ID} = Opt|Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); +handle_option([{max_random_length_padding, _Bool} = Opt|Rest], SocketOptions, SshOptions) -> + handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); handle_option([Opt | Rest], SocketOptions, SshOptions) -> handle_option(Rest, [handle_inet_option(Opt) | SocketOptions], SshOptions). @@ -515,6 +517,9 @@ handle_ssh_option({id_string, random}) -> {id_string, {random,2,5}}; %% 2 - 5 random characters handle_ssh_option({id_string, ID} = Opt) when is_list(ID) -> Opt; +handle_ssh_option({max_random_length_padding, Value} = Opt) when is_integer(Value), + Value =< 255 -> + Opt; handle_ssh_option({profile, Value} = Opt) when is_atom(Value) -> Opt; handle_ssh_option(Opt) -> diff --git a/lib/ssh/src/ssh.hrl b/lib/ssh/src/ssh.hrl index 8df5ee820c..462c98f503 100644 --- a/lib/ssh/src/ssh.hrl +++ b/lib/ssh/src/ssh.hrl @@ -124,6 +124,7 @@ recv_sequence = 0, keyex_key, keyex_info, + random_length_padding = 255, % From RFC 4253 section 6. %% User auth user, diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl index c059834b27..180698d741 100644 --- a/lib/ssh/src/ssh_connection_handler.erl +++ b/lib/ssh/src/ssh_connection_handler.erl @@ -1187,7 +1187,10 @@ init_ssh(client = Role, Vsn, Version, Options, Socket) -> opts = Options, userauth_supported_methods = AuthMethods, peer = {PeerName, PeerAddr}, - available_host_keys = supported_host_keys(Role, KeyCb, Options) + available_host_keys = supported_host_keys(Role, KeyCb, Options), + random_length_padding = proplists:get_value(max_random_length_padding, + Options, + (#ssh{})#ssh.random_length_padding) }; init_ssh(server = Role, Vsn, Version, Options, Socket) -> @@ -1207,7 +1210,10 @@ init_ssh(server = Role, Vsn, Version, Options, Socket) -> userauth_methods = AuthMethodsAsList, kb_tries_left = 3, peer = {undefined, PeerAddr}, - available_host_keys = supported_host_keys(Role, KeyCb, Options) + available_host_keys = supported_host_keys(Role, KeyCb, Options), + random_length_padding = proplists:get_value(max_random_length_padding, + Options, + (#ssh{})#ssh.random_length_padding) }. supported_host_keys(client, _, Options) -> diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl index 38a0b7ec7c..9ed6c85ff7 100644 --- a/lib/ssh/src/ssh_transport.erl +++ b/lib/ssh/src/ssh_transport.erl @@ -745,13 +745,20 @@ ssh_packet(Msg, Ssh) -> pack(Data0, #ssh{encrypt_block_size = BlockSize, send_sequence = SeqNum, send_mac = MacAlg, - send_mac_key = MacKey} + send_mac_key = MacKey, + random_length_padding = RandomLengthPadding} = Ssh0) when is_binary(Data0) -> {Ssh1, Data} = compress(Ssh0, Data0), PL = (BlockSize - ((4 + 1 + size(Data)) rem BlockSize)) rem BlockSize, - PaddingLen = if PL < 4 -> PL + BlockSize; - true -> PL - end, + MinPaddingLen = if PL < 4 -> PL + BlockSize; + true -> PL + end, + PadBlockSize = max(BlockSize,4), + MaxExtraBlocks = (max(RandomLengthPadding,MinPaddingLen) - MinPaddingLen) div PadBlockSize, + ExtraPaddingLen = try crypto:rand_uniform(0,MaxExtraBlocks)*PadBlockSize + catch _:_ -> 0 + end, + PaddingLen = MinPaddingLen + ExtraPaddingLen, Padding = ssh_bits:random(PaddingLen), PacketLen = 1 + PaddingLen + size(Data), PacketData = <<?UINT32(PacketLen),?BYTE(PaddingLen), diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl index f30e86f193..6dfff945ac 100644 --- a/lib/ssh/test/ssh_basic_SUITE.erl +++ b/lib/ssh/test/ssh_basic_SUITE.erl @@ -466,6 +466,7 @@ rekey_limit(Config) -> DataFile = filename:join(UserDir, "rekey.data"), {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, + {max_random_length_padding, 0}, {user_dir, UserDir}, {user_passwords, [{"simon", "says"}]}]), @@ -475,6 +476,7 @@ rekey_limit(Config) -> {user, "simon"}, {password, "says"}, {rekey_limit, 2500}, + {max_random_length_padding, 0}, {user_interaction, false}, {silently_accept_hosts, true}]), 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), diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml index 52d68c1b4a..6c977bdb74 100644 --- a/lib/ssl/doc/src/ssl.xml +++ b/lib/ssl/doc/src/ssl.xml @@ -664,11 +664,6 @@ fun(srp, Username :: string(), UserState :: term()) -> cipher suite can encipher. </item> - <tag><c>{psk_identity, string()}</c></tag> - <item>Specifies the server identity hint the server presents to the client. - </item> - <tag><c>{log_alert, boolean()}</c></tag> - <item>If false, error reports will not be displayed.</item> <tag><c>{honor_cipher_order, boolean()}</c></tag> <item>If true, use the server's preference for cipher selection. If false (the default), use the client's preference. diff --git a/lib/stdlib/src/qlc_pt.erl b/lib/stdlib/src/qlc_pt.erl index 4e81e2c2dd..9577d17a85 100644 --- a/lib/stdlib/src/qlc_pt.erl +++ b/lib/stdlib/src/qlc_pt.erl @@ -428,7 +428,13 @@ compile_errors(FormsNoShadows) -> end. compile_forms(Forms0, Options) -> - Forms = [F || F <- Forms0, element(1, F) =/= eof] ++ [{eof,anno0()}], + Exclude = fun(eof) -> true; + (warning) -> true; + (error) -> true; + (_) -> false + end, + Forms = ([F || F <- Forms0, not Exclude(element(1, F))] + ++ [{eof,anno0()}]), try case compile:noenv_forms(Forms, compile_options(Options)) of {ok, _ModName, Ws0} -> diff --git a/lib/stdlib/test/qlc_SUITE.erl b/lib/stdlib/test/qlc_SUITE.erl index 72216bfa0d..52fdb69b73 100644 --- a/lib/stdlib/test/qlc_SUITE.erl +++ b/lib/stdlib/test/qlc_SUITE.erl @@ -74,6 +74,7 @@ otp_5644/1, otp_5195/1, otp_6038_bug/1, otp_6359/1, otp_6562/1, otp_6590/1, otp_6673/1, otp_6964/1, otp_7114/1, otp_7238/1, otp_7232/1, otp_7552/1, otp_6674/1, otp_7714/1, otp_11758/1, + otp_12946/1, manpage/1, @@ -143,7 +144,7 @@ groups() -> {tickets, [], [otp_5644, otp_5195, otp_6038_bug, otp_6359, otp_6562, otp_6590, otp_6673, otp_6964, otp_7114, otp_7232, - otp_7238, otp_7552, otp_6674, otp_7714, otp_11758]}, + otp_7238, otp_7552, otp_6674, otp_7714, otp_11758, otp_12946]}, {compat, [], [backward, forward]}]. init_per_suite(Config) -> @@ -7154,6 +7155,18 @@ otp_6674(Config) when is_list(Config) -> ?line run(Config, Ts). +otp_12946(doc) -> + ["Syntax error."]; +otp_12946(suite) -> []; +otp_12946(Config) when is_list(Config) -> + Text = + <<"-export([init/0]). + init() -> + ok. + y">>, + {errors,[{4,erl_parse,_}],[]} = compile_file(Config, Text, []), + ok. + manpage(doc) -> "Examples from qlc(3)."; manpage(suite) -> []; diff --git a/lib/wx/test/wx_class_SUITE.erl b/lib/wx/test/wx_class_SUITE.erl index 465299a649..93a4c24a84 100644 --- a/lib/wx/test/wx_class_SUITE.erl +++ b/lib/wx/test/wx_class_SUITE.erl @@ -387,9 +387,18 @@ listCtrlSort(Config) -> Item = wxListItem:new(), - %% Force an assert on (and debug compiled) which 3.0 is by default + %% Test that wx-asserts are sent to error logger + %% Force an assert on 3.0 (when debug compiled which it is by default) wxListItem:setId(Item, 200), - io:format("Got ~p ~n", [wxListCtrl:getItem(LC, Item)]), + case os:type() of + {win32, _} -> + wxListItem:setColumn(Item, 3), + io:format("Got ~p ~n", [wxListCtrl:insertItem(LC, Item)]), + wxListItem:setColumn(Item, 0); + _ -> %% Uses generic listctrl + %% we can't use the code above on linux with wx-2.8.8 because it segfaults. + io:format("Got ~p ~n", [wxListCtrl:getItem(LC, Item)]) + end, wxListItem:setMask(Item, ?wxLIST_MASK_TEXT), _List = wx:map(fun(Int) -> |