diff options
Diffstat (limited to 'lib/stdlib')
25 files changed, 348 insertions, 205 deletions
diff --git a/lib/stdlib/doc/src/filename.xml b/lib/stdlib/doc/src/filename.xml index ce19f70df0..36254c2d00 100644 --- a/lib/stdlib/doc/src/filename.xml +++ b/lib/stdlib/doc/src/filename.xml @@ -84,11 +84,6 @@ reject such filenames. </p></warning> </description> - <datatypes> - <datatype> - <name name="basedir_type"/> - </datatype> - </datatypes> <funcs> <func> @@ -149,18 +144,37 @@ </func> <func> - <name name="basedir" arity="2"/> - <fsummary>Equivalent to <c>basedir(<anno>Type</anno>,<anno>Application</anno>,#{})</c>.</fsummary> + <name name="basedir" arity="2" clause_i="1"/> + <name name="basedir" arity="2" clause_i="2"/> + <fsummary>Equivalent to <c>basedir(<anno>PathType</anno>, + <anno>Application</anno>,#{})</c> or + <c>basedir(<anno>PathsType</anno>, <anno>Application</anno>,#{})</c>. + </fsummary> + <type variable="PathType" name_i="1"/> + <type name="basedir_path_type"/> + <type variable="PathsType" name_i="2"/> + <type name="basedir_paths_type"/> + <type variable="Application"/> <desc> <p> - Equivalent to <seealso marker="#basedir-3"> - basedir(<anno>Type</anno>, <anno>Application</anno>, #{})</seealso>. + Equivalent to <seealso marker="#basedir_3_1"> + basedir(<anno>PathType</anno>, <anno>Application</anno>, #{})</seealso> + or <seealso marker="#basedir_3_2"> +basedir(<anno>PathsType</anno>, <anno>Application</anno>, #{})</seealso>. </p> </desc> </func> <func> - <name name="basedir" arity="3"/> + <name name="basedir" arity="3" clause_i="1" anchor="basedir_3_1"/> + <name name="basedir" arity="3" clause_i="2" anchor="basedir_3_2"/> <fsummary></fsummary> + <type variable="PathType" name_i="1"/> + <type name="basedir_path_type"/> + <type variable="PathsType" name_i="2"/> + <type name="basedir_paths_type"/> + <type variable="Application"/> + <type variable="Opts"/> + <type name="basedir_opts"/> <desc><marker id="basedir-3"/> <p> Returns a suitable path, or paths, for a given type. If diff --git a/lib/stdlib/doc/src/gen_statem.xml b/lib/stdlib/doc/src/gen_statem.xml index a808d3af55..eb0f7d24f0 100644 --- a/lib/stdlib/doc/src/gen_statem.xml +++ b/lib/stdlib/doc/src/gen_statem.xml @@ -722,7 +722,7 @@ handle_event(_, _, State, Data) -> right before entering the initial state even though this formally is not a state change. In this case <c>OldState</c> will be the same as <c>State</c>, - which can not happen for a subsequent state change, + which cannot happen for a subsequent state change, but will happen when repeating the state enter call. </p> </desc> @@ -1256,7 +1256,7 @@ handle_event(_, _, State, Data) -> <desc> <p> <c><anno>State</anno></c> is the current state - and it can not be changed since the state callback + and it cannot be changed since the state callback was called with a <seealso marker="#type-state_enter"><em>state enter call</em></seealso>. </p> @@ -1922,7 +1922,7 @@ handle_event(_, _, State, Data) -> <p> If the function returns a failure <c>Reason</c>, the ongoing upgrade fails and rolls back to the old release. - Note that <c>Reason</c> can not be an <c>{ok,_,_}</c> tuple + Note that <c>Reason</c> cannot be an <c>{ok,_,_}</c> tuple since that will be regarded as a <c>{ok,NewState,NewData}</c> tuple, and that a tuple matching <c>{ok,_}</c> @@ -2208,7 +2208,7 @@ init(Args) -> erlang:error(not_implemented, [Args]).</pre> <seealso marker="erts:erlang#throw/1"><c>throw</c></seealso> to return the result, which can be useful. For example to bail out with <c>throw(keep_state_and_data)</c> - from deep within complex code that can not + from deep within complex code that cannot return <c>{next_state,State,Data}</c> because <c>State</c> or <c>Data</c> is no longer in scope. </p> diff --git a/lib/stdlib/doc/src/maps.xml b/lib/stdlib/doc/src/maps.xml index a225dea3b5..4c5199ca2b 100644 --- a/lib/stdlib/doc/src/maps.xml +++ b/lib/stdlib/doc/src/maps.xml @@ -35,9 +35,10 @@ <datatypes> <datatype> - <name name="iterator"/> + <name name="iterator" n_vars="2"/> <desc> - <p>An iterator representing the key value associations in a map.</p> + <p>An iterator representing the associations in a map with keys of type + <c><anno>Key</anno></c> and values of type <c><anno>Value</anno></c>.</p> <p>Created using <seealso marker="#iterator-1"><c>maps:iterator/1</c></seealso>.</p> <p>Consumed by <seealso marker="#next-1"><c>maps:next/1</c></seealso>, <seealso marker="#filter-2"><c>maps:filter/2</c></seealso>, @@ -45,6 +46,10 @@ <seealso marker="#map-2"><c>maps:map/2</c></seealso>.</p> </desc> </datatype> + + <datatype> + <name name="iterator" n_vars="0"/> + </datatype> </datatypes> <funcs> @@ -90,13 +95,13 @@ <name name="fold" arity="3"/> <fsummary></fsummary> <desc> - <p>Calls <c>F(K, V, AccIn)</c> for every <c><anno>K</anno></c> to value - <c><anno>V</anno></c> association in <c><anno>MapOrIter</anno></c> in - any order. Function <c>fun F/3</c> must return a new - accumulator, which is passed to the next successive call. - This function returns the final value of the accumulator. The initial - accumulator value <c><anno>Init</anno></c> is returned if the map is - empty.</p> + <p>Calls <c>F(Key, Value, AccIn)</c> for every <c><anno>Key</anno></c> + to value <c><anno>Value</anno></c> association in + <c><anno>MapOrIter</anno></c> in any order. Function <c>fun F/3</c> + must return a new accumulator, which is passed to the next successive + call. This function returns the final value of the accumulator. + The initial accumulator value <c><anno>Init</anno></c> is returned + if the map is empty.</p> <p>The call fails with a <c>{badmap,Map}</c> exception if <c><anno>MapOrIter</anno></c> is not a map or valid iterator, or with <c>badarg</c> if <c><anno>Fun</anno></c> is not a @@ -234,11 +239,12 @@ none</code> <fsummary></fsummary> <desc> <p>Produces a new map <c><anno>Map</anno></c> by calling function - <c>fun F(K, V1)</c> for every <c><anno>K</anno></c> to value - <c><anno>V1</anno></c> association in <c><anno>MapOrIter</anno></c> in - any order. Function <c>fun F/2</c> must return value - <c><anno>V2</anno></c> to be associated with key <c><anno>K</anno></c> - for the new map <c><anno>Map</anno></c>.</p> + <c>fun F(Key, Value1)</c> for every <c><anno>Key</anno></c> to value + <c><anno>Value1</anno></c> association in + <c><anno>MapOrIter</anno></c> in any order. Function <c>fun Fun/2</c> + must return value <c><anno>Value2</anno></c> to be associated with + key <c><anno>Key</anno></c> for the new map + <c><anno>Map</anno></c>.</p> <p>The call fails with a <c>{badmap,Map}</c> exception if <c><anno>MapOrIter</anno></c> is not a map or valid iterator, or with <c>badarg</c> if <c><anno>Fun</anno></c> is not a diff --git a/lib/stdlib/doc/src/notes.xml b/lib/stdlib/doc/src/notes.xml index 712a474b0b..8b6de03f5f 100644 --- a/lib/stdlib/doc/src/notes.xml +++ b/lib/stdlib/doc/src/notes.xml @@ -3659,7 +3659,7 @@ you use erlang:halt/2 with an integer first argument and an option list containing {flush,false} as the second argument. Note that now is flushing not dependant of the - exit code, and you can not only flush async threads + exit code, and you cannot only flush async threads operations which we deemed as a strange behaviour anyway. </p> <p>Also, erlang:halt/1,2 has gotten a new feature: If the @@ -4173,9 +4173,9 @@ Supervisors should not save child-specs for temporary processes when they terminate as they should not be restarted. Saving the temporary child spec will result in - that you can not start a new temporary process with the + that you cannot start a new temporary process with the same child spec as an already terminated temporary - process. Since R14B02 you can not restart a temporary + process. Since R14B02 you cannot restart a temporary temporary process as arguments are no longer saved, it has however always been semantically incorrect to restart a temporary process. Thanks to Filipe David Manana for diff --git a/lib/stdlib/doc/src/supervisor.xml b/lib/stdlib/doc/src/supervisor.xml index 6d5065ca02..0e8075a578 100644 --- a/lib/stdlib/doc/src/supervisor.xml +++ b/lib/stdlib/doc/src/supervisor.xml @@ -208,8 +208,16 @@ child_spec() = #{id => child_id(), % mandatory the child process is unconditionally terminated using <c>exit(Child,kill)</c>.</p> <p>If the child process is another supervisor, the shutdown time - is to be set to <c>infinity</c> to give the subtree ample - time to shut down. It is also allowed to set it to <c>infinity</c>, + must be set to <c>infinity</c> to give the subtree ample + time to shut down.</p> + <warning> + <p>Setting the shutdown time to anything other + than <c>infinity</c> for a child of type <c>supervisor</c> + can cause a race condition where the child in question + unlinks its own children, but fails to terminate them + before it is killed.</p> + </warning> + <p>It is also allowed to set it to <c>infinity</c>, if the child process is a worker.</p> <warning> <p>Be careful when setting the shutdown time to diff --git a/lib/stdlib/include/assert.hrl b/lib/stdlib/include/assert.hrl index 2ec89e7d8a..28d25c6589 100644 --- a/lib/stdlib/include/assert.hrl +++ b/lib/stdlib/include/assert.hrl @@ -140,7 +140,7 @@ -endif. %% This is mostly a convenience which gives more detailed reports. -%% Note: Guard is a guarded pattern, and can not be used for value. +%% Note: Guard is a guarded pattern, and cannot be used for value. -ifdef(NOASSERT). -define(assertMatch(Guard, Expr), ok). -define(assertMatch(Guard, Expr, Comment), ok). @@ -289,7 +289,7 @@ end). -endif. -%% Note: Class and Term are patterns, and can not be used for value. +%% Note: Class and Term are patterns, and cannot be used for value. %% Term can be a guarded pattern, but Class cannot. -ifdef(NOASSERT). -define(assertException(Class, Term, Expr), ok). @@ -364,7 +364,7 @@ ?assertException(throw, Term, Expr, Comment)). %% This is the inverse case of assertException, for convenience. -%% Note: Class and Term are patterns, and can not be used for value. +%% Note: Class and Term are patterns, and cannot be used for value. %% Both Class and Term can be guarded patterns. -ifdef(NOASSERT). -define(assertNotException(Class, Term, Expr), ok). diff --git a/lib/stdlib/src/dets.erl b/lib/stdlib/src/dets.erl index e016d5a80e..0488c2bef2 100644 --- a/lib/stdlib/src/dets.erl +++ b/lib/stdlib/src/dets.erl @@ -616,12 +616,18 @@ next(Tab, Key) -> %% Assuming that a file already exists, open it with the %% parameters as already specified in the file itself. %% Return a ref leading to the file. -open_file(File) -> - case dets_server:open_file(to_list(File)) of - badarg -> % Should not happen. - erlang:error(dets_process_died, [File]); - Reply -> - einval(Reply, [File]) +open_file(File0) -> + File = to_list(File0), + case is_list(File) of + true -> + case dets_server:open_file(File) of + badarg -> % Should not happen. + erlang:error(dets_process_died, [File]); + Reply -> + einval(Reply, [File]) + end; + false -> + erlang:error(badarg, [File0]) end. -spec open_file(Name, Args) -> {'ok', Name} | {'error', Reason} when @@ -1088,6 +1094,7 @@ defaults(Tab, Args) -> debug = false}, Fun = fun repl/2, Defaults = lists:foldl(Fun, Defaults0, Args), + true = is_list(Defaults#open_args.file), is_comp_min_max(Defaults). to_list(T) when is_atom(T) -> atom_to_list(T); @@ -1112,9 +1119,7 @@ repl({delayed_write, {Delay,Size} = C}, Defs) Defs#open_args{delayed_write = C}; repl({estimated_no_objects, I}, Defs) -> repl({min_no_slots, I}, Defs); -repl({file, File}, Defs) when is_list(File) -> - Defs#open_args{file = File}; -repl({file, File}, Defs) when is_atom(File) -> +repl({file, File}, Defs) -> Defs#open_args{file = to_list(File)}; repl({keypos, P}, Defs) when is_integer(P), P > 0 -> Defs#open_args{keypos =P}; diff --git a/lib/stdlib/src/erl_internal.erl b/lib/stdlib/src/erl_internal.erl index b311a843c2..939abaff00 100644 --- a/lib/stdlib/src/erl_internal.erl +++ b/lib/stdlib/src/erl_internal.erl @@ -74,6 +74,7 @@ guard_bif(element, 2) -> true; guard_bif(float, 1) -> true; guard_bif(floor, 1) -> true; guard_bif(hd, 1) -> true; +guard_bif(is_map_key, 2) -> true; guard_bif(length, 1) -> true; guard_bif(map_size, 1) -> true; guard_bif(map_get, 2) -> true; @@ -109,7 +110,6 @@ new_type_test(is_function, 2) -> true; new_type_test(is_integer, 1) -> true; new_type_test(is_list, 1) -> true; new_type_test(is_map, 1) -> true; -new_type_test(is_map_key, 2) -> true; new_type_test(is_number, 1) -> true; new_type_test(is_pid, 1) -> true; new_type_test(is_port, 1) -> true; diff --git a/lib/stdlib/src/erl_posix_msg.erl b/lib/stdlib/src/erl_posix_msg.erl index 8959fea498..b9ed4a3a9d 100644 --- a/lib/stdlib/src/erl_posix_msg.erl +++ b/lib/stdlib/src/erl_posix_msg.erl @@ -81,9 +81,9 @@ message_1(el2hlt) -> <<"level 2 halted">>; message_1(el2nsync) -> <<"level 2 not synchronized">>; message_1(el3hlt) -> <<"level 3 halted">>; message_1(el3rst) -> <<"level 3 reset">>; -message_1(elibacc) -> <<"can not access a needed shared library">>; +message_1(elibacc) -> <<"cannot access a needed shared library">>; message_1(elibbad) -> <<"accessing a corrupted shared library">>; -message_1(elibexec) -> <<"can not exec a shared library directly">>; +message_1(elibexec) -> <<"cannot exec a shared library directly">>; message_1(elibmax) -> <<"attempting to link in more shared libraries than system limit">>; message_1(elibscn) -> <<".lib section in a.out corrupted">>; diff --git a/lib/stdlib/src/filename.erl b/lib/stdlib/src/filename.erl index a322bd002d..b7b7b562ab 100644 --- a/lib/stdlib/src/filename.erl +++ b/lib/stdlib/src/filename.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2017. All Rights Reserved. +%% Copyright Ericsson AB 1997-2018. 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. @@ -1012,24 +1012,33 @@ filename_string_to_binary(List) -> %% basedir %% http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html --type basedir_type() :: 'user_cache' | 'user_config' | 'user_data' - | 'user_log' - | 'site_config' | 'site_data'. +-type basedir_path_type() :: 'user_cache' | 'user_config' | 'user_data' + | 'user_log'. +-type basedir_paths_type() :: 'site_config' | 'site_data'. --spec basedir(Type,Application) -> file:filename_all() when - Type :: basedir_type(), +-type basedir_opts() :: #{author => string() | binary(), + os => 'windows' | 'darwin' | 'linux', + version => string() | binary()}. + +-spec basedir(PathType,Application) -> file:filename_all() when + PathType :: basedir_path_type(), + Application :: string() | binary(); + (PathsType,Application) -> [file:filename_all()] when + PathsType :: basedir_paths_type(), Application :: string() | binary(). basedir(Type,Application) when is_atom(Type), is_list(Application) orelse is_binary(Application) -> basedir(Type, Application, #{}). --spec basedir(Type,Application,Opts) -> file:filename_all() when - Type :: basedir_type(), +-spec basedir(PathType,Application,Opts) -> file:filename_all() when + PathType :: basedir_path_type(), + Application :: string() | binary(), + Opts :: basedir_opts(); + (PathsType,Application,Opts) -> [file:filename_all()] when + PathsType :: basedir_paths_type(), Application :: string() | binary(), - Opts :: #{author => string() | binary(), - os => 'windows' | 'darwin' | 'linux', - version => string() | binary()}. + Opts :: basedir_opts(). basedir(Type,Application,Opts) when is_atom(Type), is_map(Opts), is_list(Application) orelse diff --git a/lib/stdlib/src/io_lib.erl b/lib/stdlib/src/io_lib.erl index 3a5aba60b4..8223a52873 100644 --- a/lib/stdlib/src/io_lib.erl +++ b/lib/stdlib/src/io_lib.erl @@ -178,11 +178,11 @@ fread(Cont, Chars, Format) -> Data :: [term()]. format(Format, Args) -> - case catch io_lib_format:fwrite(Format, Args) of - {'EXIT',_} -> - erlang:error(badarg, [Format, Args]); - Other -> - Other + try io_lib_format:fwrite(Format, Args) + catch + C:R:S -> + test_modules_loaded(C, R, S), + erlang:error(badarg, [Format, Args]) end. -spec format(Format, Data, Options) -> chars() when @@ -193,11 +193,11 @@ format(Format, Args) -> CharsLimit :: chars_limit(). format(Format, Args, Options) -> - case catch io_lib_format:fwrite(Format, Args, Options) of - {'EXIT',_} -> - erlang:error(badarg, [Format, Args, Options]); - Other -> - Other + try io_lib_format:fwrite(Format, Args, Options) + catch + C:R:S -> + test_modules_loaded(C, R, S), + erlang:error(badarg, [Format, Args]) end. -spec scan_format(Format, Data) -> FormatList when @@ -208,7 +208,9 @@ format(Format, Args, Options) -> scan_format(Format, Args) -> try io_lib_format:scan(Format, Args) catch - _:_ -> erlang:error(badarg, [Format, Args]) + C:R:S -> + test_modules_loaded(C, R, S), + erlang:error(badarg, [Format, Args]) end. -spec unscan_format(FormatList) -> {Format, Data} when @@ -223,7 +225,12 @@ unscan_format(FormatList) -> FormatList :: [char() | format_spec()]. build_text(FormatList) -> - io_lib_format:build(FormatList). + try io_lib_format:build(FormatList) + catch + C:R:S -> + test_modules_loaded(C, R, S), + erlang:error(badarg, [FormatList]) + end. -spec build_text(FormatList, Options) -> chars() when FormatList :: [char() | format_spec()], @@ -232,7 +239,23 @@ build_text(FormatList) -> CharsLimit :: chars_limit(). build_text(FormatList, Options) -> - io_lib_format:build(FormatList, Options). + try io_lib_format:build(FormatList, Options) + catch + C:R:S -> + test_modules_loaded(C, R, S), + erlang:error(badarg, [FormatList, Options]) + end. + +%% Failure to load a module must not be labeled as badarg. +%% C, R, and S are included so that the original error, which could be +%% a bug in io_lib_format, can be found by tracing on +%% test_modules_loaded/3. +test_modules_loaded(_C, _R, _S) -> + Modules = [io_lib_format, io_lib_pretty, string, unicode], + case code:ensure_modules_loaded(Modules) of + ok -> ok; + Error -> erlang:error(Error) + end. -spec print(Term) -> chars() when Term :: term(). diff --git a/lib/stdlib/src/io_lib_format.erl b/lib/stdlib/src/io_lib_format.erl index c814ab50d4..e247b00a04 100644 --- a/lib/stdlib/src/io_lib_format.erl +++ b/lib/stdlib/src/io_lib_format.erl @@ -38,18 +38,16 @@ %% and it also splits the handling of the control characters into two %% parts. --spec fwrite(Format, Data) -> FormatList when +-spec fwrite(Format, Data) -> io_lib:chars() when Format :: io:format(), - Data :: [term()], - FormatList :: [char() | io_lib:format_spec()]. + Data :: [term()]. fwrite(Format, Args) -> build(scan(Format, Args)). --spec fwrite(Format, Data, Options) -> FormatList when +-spec fwrite(Format, Data, Options) -> io_lib:chars() when Format :: io:format(), Data :: [term()], - FormatList :: [char() | io_lib:format_spec()], Options :: [Option], Option :: {'chars_limit', CharsLimit}, CharsLimit :: io_lib:chars_limit(). diff --git a/lib/stdlib/src/maps.erl b/lib/stdlib/src/maps.erl index 60463feec2..51965ddb57 100644 --- a/lib/stdlib/src/maps.erl +++ b/lib/stdlib/src/maps.erl @@ -21,7 +21,7 @@ -module(maps). -export([get/3, filter/2,fold/3, - map/2, size/1, + map/2, size/1, new/0, update_with/3, update_with/4, without/2, with/2, iterator/1, next/1]). @@ -29,13 +29,15 @@ %% BIFs -export([get/2, find/2, from_list/1, is_key/2, keys/1, merge/2, - new/0, put/3, remove/2, take/2, + put/3, remove/2, take/2, to_list/1, update/3, values/1]). --opaque iterator() :: {term(), term(), iterator()} - | none | nonempty_improper_list(integer(),map()). +-opaque iterator(Key, Value) :: {Key, Value, iterator(Key, Value)} | none + | nonempty_improper_list(integer(), #{Key => Value}). --export_type([iterator/0]). +-type iterator() :: iterator(term(), term()). + +-export_type([iterator/2, iterator/0]). -dialyzer({no_improper_lists, iterator/1}). @@ -50,9 +52,7 @@ get(_,_) -> erlang:nif_error(undef). -spec find(Key,Map) -> {ok, Value} | error when - Key :: term(), - Map :: map(), - Value :: term(). + Map :: #{Key => Value, _ => _}. find(_,_) -> erlang:nif_error(undef). @@ -75,9 +75,8 @@ is_key(_,_) -> erlang:nif_error(undef). -spec keys(Map) -> Keys when - Map :: map(), - Keys :: [Key], - Key :: term(). + Map :: #{Key => _}, + Keys :: [Key]. keys(_) -> erlang:nif_error(undef). @@ -91,13 +90,6 @@ keys(_) -> erlang:nif_error(undef). merge(_,_) -> erlang:nif_error(undef). - --spec new() -> Map when - Map :: map(). - -new() -> erlang:nif_error(undef). - - %% Shadowed by erl_bif_types: maps:put/3 -spec put(Key,Value,Map1) -> Map2 when Key :: term(), @@ -116,17 +108,13 @@ put(_,_,_) -> erlang:nif_error(undef). remove(_,_) -> erlang:nif_error(undef). -spec take(Key,Map1) -> {Value,Map2} | error when - Key :: term(), - Map1 :: map(), - Value :: term(), - Map2 :: map(). + Map1 :: #{Key => Value, _ => _}, + Map2 :: #{_ => _}. take(_,_) -> erlang:nif_error(undef). -spec to_list(Map) -> [{Key,Value}] when - Map :: map(), - Key :: term(), - Value :: term(). + Map :: #{Key => Value}. to_list(Map) when is_map(Map) -> to_list_internal(erts_internal:map_next(0, Map, [])); @@ -140,79 +128,69 @@ to_list_internal(Acc) -> %% Shadowed by erl_bif_types: maps:update/3 -spec update(Key,Value,Map1) -> Map2 when - Key :: term(), - Value :: term(), - Map1 :: map(), - Map2 :: map(). + Map1 :: #{Key := _, _ => _}, + Map2 :: #{Key := Value, _ => _}. update(_,_,_) -> erlang:nif_error(undef). -spec values(Map) -> Values when - Map :: map(), - Values :: [Value], - Value :: term(). + Map :: #{_ => Value}, + Values :: [Value]. values(_) -> erlang:nif_error(undef). %% End of BIFs +-spec new() -> Map when + Map :: #{}. + +new() -> #{}. + -spec update_with(Key,Fun,Map1) -> Map2 when - Key :: term(), - Map1 :: map(), - Map2 :: map(), - Fun :: fun((Value1 :: term()) -> Value2 :: term()). + Map1 :: #{Key := Value1, _ => _}, + Map2 :: #{Key := Value2, _ => _}, + Fun :: fun((Value1) -> Value2). update_with(Key,Fun,Map) when is_function(Fun,1), is_map(Map) -> - try maps:get(Key,Map) of - Val -> maps:update(Key,Fun(Val),Map) - catch - error:{badkey,_} -> - erlang:error({badkey,Key},[Key,Fun,Map]) + case Map of + #{Key := Value} -> Map#{Key := Fun(Value)}; + #{} -> erlang:error({badkey,Key},[Key,Fun,Map]) end; update_with(Key,Fun,Map) -> erlang:error(error_type(Map),[Key,Fun,Map]). -spec update_with(Key,Fun,Init,Map1) -> Map2 when - Key :: term(), - Map1 :: Map1, - Map2 :: Map2, - Fun :: fun((Value1 :: term()) -> Value2 :: term()), - Init :: term(). + Map1 :: #{Key => Value1, _ => _}, + Map2 :: #{Key := Value2 | Init, _ => _}, + Fun :: fun((Value1) -> Value2). update_with(Key,Fun,Init,Map) when is_function(Fun,1), is_map(Map) -> - case maps:find(Key,Map) of - {ok,Val} -> maps:update(Key,Fun(Val),Map); - error -> maps:put(Key,Init,Map) + case Map of + #{Key := Value} -> Map#{Key := Fun(Value)}; + #{} -> Map#{Key => Init} end; update_with(Key,Fun,Init,Map) -> erlang:error(error_type(Map),[Key,Fun,Init,Map]). -spec get(Key, Map, Default) -> Value | Default when - Key :: term(), - Map :: map(), - Value :: term(), - Default :: term(). + Map :: #{Key => Value, _ => _}. get(Key,Map,Default) when is_map(Map) -> - case maps:find(Key, Map) of - {ok, Value} -> - Value; - error -> - Default + case Map of + #{Key := Value} -> Value; + #{} -> Default end; get(Key,Map,Default) -> erlang:error({badmap,Map},[Key,Map,Default]). --spec filter(Pred,MapOrIter) -> Map when +-spec filter(Pred, MapOrIter) -> Map when Pred :: fun((Key, Value) -> boolean()), - Key :: term(), - Value :: term(), - MapOrIter :: map() | iterator(), - Map :: map(). + MapOrIter :: #{Key => Value} | iterator(Key, Value), + Map :: #{Key => Value}. filter(Pred,Map) when is_function(Pred,2), is_map(Map) -> maps:from_list(filter_1(Pred, iterator(Map))); @@ -235,14 +213,11 @@ filter_1(Pred, Iter) -> end. -spec fold(Fun,Init,MapOrIter) -> Acc when - Fun :: fun((K, V, AccIn) -> AccOut), + Fun :: fun((Key, Value, AccIn) -> AccOut), Init :: term(), - Acc :: term(), - AccIn :: term(), - AccOut :: term(), - MapOrIter :: map() | iterator(), - K :: term(), - V :: term(). + Acc :: AccOut, + AccIn :: Init | AccOut, + MapOrIter :: #{Key => Value} | iterator(Key, Value). fold(Fun,Init,Map) when is_function(Fun,3), is_map(Map) -> fold_1(Fun,Init,iterator(Map)); @@ -260,12 +235,9 @@ fold_1(Fun, Acc, Iter) -> end. -spec map(Fun,MapOrIter) -> Map when - Fun :: fun((K, V1) -> V2), - MapOrIter :: map() | iterator(), - Map :: map(), - K :: term(), - V1 :: term(), - V2 :: term(). + Fun :: fun((Key, Value1) -> Value2), + MapOrIter :: #{Key => Value1} | iterator(Key, Value1), + Map :: #{Key => Value2}. map(Fun,Map) when is_function(Fun, 2), is_map(Map) -> maps:from_list(map_1(Fun, iterator(Map))); @@ -291,17 +263,15 @@ size(Val) -> erlang:error({badmap,Val},[Val]). -spec iterator(Map) -> Iterator when - Map :: map(), - Iterator :: iterator(). + Map :: #{Key => Value}, + Iterator :: iterator(Key, Value). iterator(M) when is_map(M) -> [0 | M]; iterator(M) -> erlang:error({badmap, M}, [M]). -spec next(Iterator) -> {Key, Value, NextIterator} | 'none' when - Iterator :: iterator(), - Key :: term(), - Value :: term(), - NextIterator :: iterator(). + Iterator :: iterator(Key, Value), + NextIterator :: iterator(Key, Value). next({K, V, I}) -> {K, V, I}; next([Path | Map]) when is_integer(Path), is_map(Map) -> @@ -318,29 +288,26 @@ next(Iter) -> K :: term(). without(Ks,M) when is_list(Ks), is_map(M) -> - lists:foldl(fun(K, M1) -> maps:remove(K, M1) end, M, Ks); + lists:foldl(fun maps:remove/2, M, Ks); without(Ks,M) -> erlang:error(error_type(M),[Ks,M]). -spec with(Ks, Map1) -> Map2 when Ks :: [K], - Map1 :: map(), - Map2 :: map(), - K :: term(). + Map1 :: #{K => V, _ => _}, + Map2 :: #{K => V}. with(Ks,Map1) when is_list(Ks), is_map(Map1) -> - Fun = fun(K, List) -> - case maps:find(K, Map1) of - {ok, V} -> - [{K, V} | List]; - error -> - List - end - end, - maps:from_list(lists:foldl(Fun, [], Ks)); + maps:from_list(with_1(Ks, Map1)); with(Ks,M) -> erlang:error(error_type(M),[Ks,M]). +with_1([K|Ks], Map) -> + case Map of + #{K := V} -> [{K,V}|with_1(Ks, Map)]; + #{} -> with_1(Ks, Map) + end; +with_1([], _Map) -> []. error_type(M) when is_map(M) -> badarg; error_type(V) -> {badmap, V}. diff --git a/lib/stdlib/src/ms_transform.erl b/lib/stdlib/src/ms_transform.erl index d117481d2e..3845e35e9b 100644 --- a/lib/stdlib/src/ms_transform.erl +++ b/lib/stdlib/src/ms_transform.erl @@ -224,10 +224,12 @@ transform_from_shell(Dialect, Clauses, BoundEnvironment) -> %% Called when translating during compiling %% --spec parse_transform(Forms, Options) -> Forms2 when +-spec parse_transform(Forms, Options) -> Forms2 | Errors | Warnings when Forms :: [erl_parse:abstract_form() | erl_parse:form_info()], Forms2 :: [erl_parse:abstract_form() | erl_parse:form_info()], - Options :: term(). + Options :: term(), + Errors :: {error, ErrInfo :: [tuple()], WarnInfo :: []}, + Warnings :: {warning, Forms2, WarnInfo :: [tuple()]}. parse_transform(Forms, _Options) -> SaveFilename = setup_filename(), diff --git a/lib/stdlib/src/rand.erl b/lib/stdlib/src/rand.erl index 362e98006e..4951dc727b 100644 --- a/lib/stdlib/src/rand.erl +++ b/lib/stdlib/src/rand.erl @@ -486,7 +486,7 @@ uniform_real_s(Alg, Next, M0, BitNo, R1, V1, Bits) -> {M1 * math:pow(2.0, BitNo - 56), {Alg, R1}}; BitNo =:= -1008 -> %% Endgame - %% For the last round we can not have 14 zeros or more + %% For the last round we cannot have 14 zeros or more %% at the top of M1 because then we will underflow, %% so we need at least 43 bits if diff --git a/lib/stdlib/test/dets_SUITE.erl b/lib/stdlib/test/dets_SUITE.erl index fe324391af..65977a764a 100644 --- a/lib/stdlib/test/dets_SUITE.erl +++ b/lib/stdlib/test/dets_SUITE.erl @@ -3417,6 +3417,7 @@ otp_11709(Config) when is_list(Config) -> ok. %% OTP-13229. open_file() exits with badarg when given binary file name. +%% Also OTP-15253. otp_13229(_Config) -> F = <<"binfile.tab">>, try dets:open_file(name, [{file, F}]) of @@ -3425,6 +3426,20 @@ otp_13229(_Config) -> catch error:badarg -> ok + end, + try dets:open_file(F, []) of % OTP-15253 + R2 -> + exit({open_succeeded, R2}) + catch + error:badarg -> + ok + end, + try dets:open_file(F) of + R3 -> + exit({open_succeeded, R3}) + catch + error:badarg -> + ok end. %% OTP-13260. Race when opening a table. diff --git a/lib/stdlib/test/epp_SUITE.erl b/lib/stdlib/test/epp_SUITE.erl index a3e294ffea..10e1b75e0f 100644 --- a/lib/stdlib/test/epp_SUITE.erl +++ b/lib/stdlib/test/epp_SUITE.erl @@ -1372,7 +1372,7 @@ otp_8562(Config) when is_list(Config) -> otp_8911(Config) when is_list(Config) -> case test_server:is_cover() of true -> - {skip, "Testing cover, so can not run when cover is already running"}; + {skip, "Testing cover, so cannot run when cover is already running"}; false -> do_otp_8911(Config) end. diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl index f9ab83a120..c1613a7273 100644 --- a/lib/stdlib/test/erl_lint_SUITE.erl +++ b/lib/stdlib/test/erl_lint_SUITE.erl @@ -2730,7 +2730,7 @@ bif_clash(Config) when is_list(Config) -> [], {errors,[{2,erl_lint,{call_to_redefined_old_bif,{size,1}}}],[]}}, - %% Verify that warnings can not be turned off in the old way. + %% Verify that warnings cannot be turned off in the old way. {clash2, <<"-export([t/1,size/1]). t(X) -> diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl index fee8b204f4..29677ac67e 100644 --- a/lib/stdlib/test/ets_SUITE.erl +++ b/lib/stdlib/test/ets_SUITE.erl @@ -66,7 +66,7 @@ meta_lookup_named_read/1, meta_lookup_named_write/1, meta_newdel_unnamed/1, meta_newdel_named/1]). -export([smp_insert/1, smp_fixed_delete/1, smp_unfix_fix/1, smp_select_delete/1, - smp_select_replace/1, otp_8166/1, otp_8732/1]). + smp_select_replace/1, otp_8166/1, otp_8732/1, delete_unfix_race/1]). -export([exit_large_table_owner/1, exit_many_large_table_owner/1, exit_many_tables_owner/1, @@ -142,7 +142,8 @@ all() -> ets_all, massive_ets_all, take, - whereis_table]. + whereis_table, + delete_unfix_race]. groups() -> [{new, [], @@ -5489,6 +5490,46 @@ smp_fixed_delete_do() -> %%verify_table_load(T), ets:delete(T). +%% ERL-720 +%% Provoke race between ets:delete and table unfix (by select_count) +%% that caused ets_misc memory counter to indicate false leak. +delete_unfix_race(Config) when is_list(Config) -> + EtsMem = etsmem(), + Table = ets:new(t,[set,public,{write_concurrency,true}]), + InsertOp = + fun() -> + receive stop -> + false + after 0 -> + ets:insert(Table, {rand:uniform(10)}), + true + end + end, + DeleteOp = + fun() -> + receive stop -> + false + after 0 -> + ets:delete(Table, rand:uniform(10)), + true + end + end, + SelectOp = + fun() -> + ets:select_count(Table, ets:fun2ms(fun(X) -> true end)) + end, + Main = self(), + Ins = spawn(fun()-> repeat_while(InsertOp), Main ! self() end), + Del = spawn(fun()-> repeat_while(DeleteOp), Main ! self() end), + spawn(fun()-> + repeat(SelectOp, 10000), + Del ! stop, + Ins ! stop + end), + [receive Pid -> ok end || Pid <- [Ins,Del]], + ets:delete(Table), + verify_etsmem(EtsMem). + num_of_buckets(T) -> element(1,ets:info(T,stats)). diff --git a/lib/stdlib/test/io_SUITE.erl b/lib/stdlib/test/io_SUITE.erl index 79cee54335..f097552e8c 100644 --- a/lib/stdlib/test/io_SUITE.erl +++ b/lib/stdlib/test/io_SUITE.erl @@ -31,7 +31,7 @@ otp_10836/1, io_lib_width_too_small/1, io_with_huge_message_queue/1, format_string/1, maps/1, coverage/1, otp_14178_unicode_atoms/1, otp_14175/1, - otp_14285/1, limit_term/1, otp_14983/1, otp_15103/1, + otp_14285/1, limit_term/1, otp_14983/1, otp_15103/1, otp_15076/1, otp_15159/1]). -export([pretty/2, trf/3]). @@ -64,7 +64,7 @@ all() -> io_lib_print_binary_depth_one, otp_10302, otp_10755, otp_10836, io_lib_width_too_small, io_with_huge_message_queue, format_string, maps, coverage, otp_14178_unicode_atoms, otp_14175, - otp_14285, limit_term, otp_14983, otp_15103, otp_15159]. + otp_14285, limit_term, otp_14983, otp_15103, otp_15076, otp_15159]. %% Error cases for output. error_1(Config) when is_list(Config) -> @@ -2639,3 +2639,11 @@ otp_15159(_Config) -> "[atom]" = lists:flatten(io_lib:format("~p", [[atom]], [{chars_limit,5}])), ok. + +otp_15076(_Config) -> + {'EXIT', {badarg, _}} = (catch io_lib:format("~c", [a])), + L = io_lib:scan_format("~c", [a]), + {"~c", [a]} = io_lib:unscan_format(L), + {'EXIT', {badarg, _}} = (catch io_lib:build_text(L)), + {'EXIT', {badarg, _}} = (catch io_lib:build_text(L, [])), + ok. diff --git a/lib/stdlib/test/lists_SUITE.erl b/lib/stdlib/test/lists_SUITE.erl index 837ab4e97e..af94fc79bc 100644 --- a/lib/stdlib/test/lists_SUITE.erl +++ b/lib/stdlib/test/lists_SUITE.erl @@ -1679,7 +1679,7 @@ make_fun() -> receive {Pid, Fun} -> Fun end. make_fun(Pid) -> - Pid ! {self(), fun make_fun/1}. + Pid ! {self(), fun (X) -> {X, Pid} end}. fun_pid(Fun) -> erlang:fun_info(Fun, pid). diff --git a/lib/stdlib/test/re_SUITE_data/testoutput2 b/lib/stdlib/test/re_SUITE_data/testoutput2 index 811bbefc84..61ed8d9d4e 100644 --- a/lib/stdlib/test/re_SUITE_data/testoutput2 +++ b/lib/stdlib/test/re_SUITE_data/testoutput2 @@ -14705,4 +14705,20 @@ No options No first char No need char +"(?<=(a))\1?b" + ab + 0: b + 1: a + aaab + 0: ab + 1: a + +"(?=(a))\1?b" + ab + 0: ab + 1: a + aaab + 0: ab + 1: a + /-- End of testinput2 --/ diff --git a/lib/stdlib/test/re_SUITE_data/testoutput5 b/lib/stdlib/test/re_SUITE_data/testoutput5 index bab989ca7e..090e1e1c85 100644 --- a/lib/stdlib/test/re_SUITE_data/testoutput5 +++ b/lib/stdlib/test/re_SUITE_data/testoutput5 @@ -1942,4 +1942,12 @@ Need char = 'z' 0: \x{17f} 0+ +/\C[^\v]+\x80/8 + [AΏBŀC] +No match + +/\C[^\d]+\x80/8 + [AΏBŀC] +No match + /-- End of testinput5 --/ diff --git a/lib/stdlib/test/stdlib.spec b/lib/stdlib/test/stdlib.spec index 9c625091a8..4de7c1a0eb 100644 --- a/lib/stdlib/test/stdlib.spec +++ b/lib/stdlib/test/stdlib.spec @@ -1,4 +1,4 @@ {suites,"../stdlib_test",all}. {skip_groups,"../stdlib_test",stdlib_bench_SUITE, - [base64,gen_server,gen_statem,unicode], + [binary,base64,gen_server,gen_statem,unicode], "Benchmark only"}. diff --git a/lib/stdlib/test/stdlib_bench_SUITE.erl b/lib/stdlib/test/stdlib_bench_SUITE.erl index 2364e8376f..b937eeb06a 100644 --- a/lib/stdlib/test/stdlib_bench_SUITE.erl +++ b/lib/stdlib/test/stdlib_bench_SUITE.erl @@ -29,7 +29,7 @@ suite() -> [{ct_hooks,[{ts_install_cth,[{nodenames,2}]}]}]. all() -> - [{group,unicode},{group,base64}, + [{group,unicode},{group,base64},{group,binary}, {group,gen_server},{group,gen_statem}, {group,gen_server_comparison},{group,gen_statem_comparison}]. @@ -38,6 +38,11 @@ groups() -> [norm_nfc_list, norm_nfc_deep_l, norm_nfc_binary, string_lexemes_list, string_lexemes_binary ]}, + {binary, [{repeat, 5}], + [match_single_pattern_no_match, + matches_single_pattern_no_match, + matches_single_pattern_eventual_match, + matches_single_pattern_frequent_match]}, {base64,[{repeat,5}], [decode_binary, decode_binary_to_string, decode_list, decode_list_to_string, @@ -157,41 +162,59 @@ norm_data(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +match_single_pattern_no_match(_Config) -> + Binary = binary:copy(<<"ugbcfuysabfuqyfikgfsdalpaskfhgjsdgfjwsalp">>, 1000000), + comment(test(binary, match, [Binary, <<"o">>])). + +matches_single_pattern_no_match(_Config) -> + Binary = binary:copy(<<"ugbcfuysabfuqyfikgfsdalpaskfhgjsdgfjwsalp">>, 1000000), + comment(test(binary, matches, [Binary, <<"o">>])). + +matches_single_pattern_eventual_match(_Config) -> + Binary = binary:copy(<<"ugbcfuysabfuqyfikgfsdalpaskfhgjsdgfjwsal\n">>, 1000000), + comment(test(binary, matches, [Binary, <<"\n">>])). + +matches_single_pattern_frequent_match(_Config) -> + Binary = binary:copy(<<"abc\n">>, 1000000), + comment(test(binary, matches, [Binary, <<"abc">>])). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + decode_binary(_Config) -> - comment(test(decode, encoded_binary())). + comment(test(base64, decode, [encoded_binary()])). decode_binary_to_string(_Config) -> - comment(test(decode_to_string, encoded_binary())). + comment(test(base64, decode_to_string, [encoded_binary()])). decode_list(_Config) -> - comment(test(decode, encoded_list())). + comment(test(base64, decode, [encoded_list()])). decode_list_to_string(_Config) -> - comment(test(decode_to_string, encoded_list())). + comment(test(base64, decode_to_string, [encoded_list()])). encode_binary(_Config) -> - comment(test(encode, binary())). + comment(test(base64, encode, [binary()])). encode_binary_to_string(_Config) -> - comment(test(encode_to_string, binary())). + comment(test(base64, encode_to_string, [binary()])). encode_list(_Config) -> - comment(test(encode, list())). + comment(test(base64, encode, [list()])). encode_list_to_string(_Config) -> - comment(test(encode_to_string, list())). + comment(test(base64, encode_to_string, [list()])). mime_binary_decode(_Config) -> - comment(test(mime_decode, encoded_binary())). + comment(test(base64, mime_decode, [encoded_binary()])). mime_binary_decode_to_string(_Config) -> - comment(test(mime_decode_to_string, encoded_binary())). + comment(test(base64, mime_decode_to_string, [encoded_binary()])). mime_list_decode(_Config) -> - comment(test(mime_decode, encoded_list())). + comment(test(base64, mime_decode, [encoded_list()])). mime_list_decode_to_string(_Config) -> - comment(test(mime_decode_to_string, encoded_list())). + comment(test(base64, mime_decode_to_string, [encoded_list()])). -define(SIZE, 10000). -define(N, 1000). @@ -209,15 +232,15 @@ binary() -> list() -> random_byte_list(?SIZE). -test(Func, Data) -> - F = fun() -> loop(?N, Func, Data) end, +test(Mod, Fun, Args) -> + F = fun() -> loop(?N, Mod, Fun, Args) end, {Time, ok} = timer:tc(fun() -> lspawn(F) end), - report_base64(Time). + report_mfa(Time, Mod). -loop(0, _F, _D) -> garbage_collect(), ok; -loop(N, F, D) -> - _ = base64:F(D), - loop(N - 1, F, D). +loop(0, _M, _F, _A) -> garbage_collect(), ok; +loop(N, M, F, A) -> + _ = apply(M, F, A), + loop(N - 1, M, F, A). lspawn(Fun) -> {Pid, Ref} = spawn_monitor(fun() -> exit(Fun()) end), @@ -225,10 +248,10 @@ lspawn(Fun) -> {'DOWN', Ref, process, Pid, Rep} -> Rep end. -report_base64(Time) -> +report_mfa(Time, Mod) -> Tps = round((?N*1000000)/Time), ct_event:notify(#event{name = benchmark_data, - data = [{suite, "stdlib_base64"}, + data = [{suite, "stdlib_" ++ atom_to_list(Mod)}, {value, Tps}]}), Tps. |