aboutsummaryrefslogtreecommitdiffstats
path: root/lib/stdlib/src
diff options
context:
space:
mode:
Diffstat (limited to 'lib/stdlib/src')
-rw-r--r--lib/stdlib/src/Makefile27
-rw-r--r--lib/stdlib/src/array.erl25
-rw-r--r--lib/stdlib/src/base64.erl23
-rw-r--r--lib/stdlib/src/beam_lib.erl78
-rw-r--r--lib/stdlib/src/binary.erl93
-rw-r--r--lib/stdlib/src/c.erl44
-rw-r--r--lib/stdlib/src/calendar.erl25
-rw-r--r--lib/stdlib/src/dets.erl81
-rw-r--r--lib/stdlib/src/dets.hrl23
-rw-r--r--lib/stdlib/src/dets_server.erl23
-rw-r--r--lib/stdlib/src/dets_sup.erl21
-rw-r--r--lib/stdlib/src/dets_utils.erl27
-rw-r--r--lib/stdlib/src/dets_v8.erl23
-rw-r--r--lib/stdlib/src/dets_v9.erl25
-rw-r--r--lib/stdlib/src/dict.erl33
-rw-r--r--lib/stdlib/src/digraph.erl27
-rw-r--r--lib/stdlib/src/digraph_utils.erl23
-rw-r--r--lib/stdlib/src/edlin.erl42
-rw-r--r--lib/stdlib/src/edlin_expand.erl21
-rw-r--r--lib/stdlib/src/epp.erl718
-rw-r--r--lib/stdlib/src/erl_anno.erl405
-rw-r--r--lib/stdlib/src/erl_bits.erl23
-rw-r--r--lib/stdlib/src/erl_compile.erl21
-rw-r--r--lib/stdlib/src/erl_eval.erl107
-rw-r--r--lib/stdlib/src/erl_expand_records.erl120
-rw-r--r--lib/stdlib/src/erl_internal.erl74
-rw-r--r--lib/stdlib/src/erl_lint.erl1068
-rw-r--r--lib/stdlib/src/erl_parse.yrl1313
-rw-r--r--lib/stdlib/src/erl_posix_msg.erl23
-rw-r--r--lib/stdlib/src/erl_pp.erl260
-rw-r--r--lib/stdlib/src/erl_scan.erl402
-rw-r--r--lib/stdlib/src/erl_tar.erl25
-rw-r--r--lib/stdlib/src/error_logger_file_h.erl251
-rw-r--r--lib/stdlib/src/error_logger_tty_h.erl260
-rw-r--r--lib/stdlib/src/escript.erl63
-rw-r--r--lib/stdlib/src/ets.erl142
-rw-r--r--lib/stdlib/src/eval_bits.erl23
-rw-r--r--lib/stdlib/src/file_sorter.erl49
-rw-r--r--lib/stdlib/src/filelib.erl21
-rw-r--r--lib/stdlib/src/filename.erl206
-rw-r--r--lib/stdlib/src/gb_sets.erl62
-rw-r--r--lib/stdlib/src/gb_trees.erl57
-rw-r--r--lib/stdlib/src/gen.erl253
-rw-r--r--lib/stdlib/src/gen_event.erl48
-rw-r--r--lib/stdlib/src/gen_fsm.erl118
-rw-r--r--lib/stdlib/src/gen_server.erl164
-rw-r--r--lib/stdlib/src/gen_statem.erl1308
-rw-r--r--lib/stdlib/src/io.erl68
-rw-r--r--lib/stdlib/src/io_lib.erl67
-rw-r--r--lib/stdlib/src/io_lib_format.erl133
-rw-r--r--lib/stdlib/src/io_lib_fread.erl21
-rw-r--r--lib/stdlib/src/io_lib_pretty.erl23
-rw-r--r--lib/stdlib/src/lib.erl23
-rw-r--r--lib/stdlib/src/lists.erl38
-rw-r--r--lib/stdlib/src/log_mf_h.erl23
-rw-r--r--lib/stdlib/src/maps.erl167
-rw-r--r--lib/stdlib/src/math.erl30
-rw-r--r--lib/stdlib/src/ms_transform.erl45
-rw-r--r--lib/stdlib/src/orddict.erl151
-rw-r--r--lib/stdlib/src/ordsets.erl24
-rw-r--r--lib/stdlib/src/otp_internal.erl246
-rw-r--r--lib/stdlib/src/pg.erl187
-rw-r--r--lib/stdlib/src/pool.erl23
-rw-r--r--lib/stdlib/src/proc_lib.erl154
-rw-r--r--lib/stdlib/src/proplists.erl25
-rw-r--r--lib/stdlib/src/qlc.erl135
-rw-r--r--lib/stdlib/src/qlc_pt.erl699
-rw-r--r--lib/stdlib/src/queue.erl25
-rw-r--r--lib/stdlib/src/rand.erl596
-rw-r--r--lib/stdlib/src/random.erl32
-rw-r--r--lib/stdlib/src/re.erl122
-rw-r--r--lib/stdlib/src/sets.erl25
-rw-r--r--lib/stdlib/src/shell.erl79
-rw-r--r--lib/stdlib/src/shell_default.erl24
-rw-r--r--lib/stdlib/src/slave.erl68
-rw-r--r--lib/stdlib/src/sofs.erl21
-rw-r--r--lib/stdlib/src/stdlib.app.src27
-rw-r--r--lib/stdlib/src/stdlib.appup.src31
-rw-r--r--lib/stdlib/src/string.erl69
-rw-r--r--lib/stdlib/src/supervisor.erl386
-rw-r--r--lib/stdlib/src/supervisor_bridge.erl23
-rw-r--r--lib/stdlib/src/sys.erl48
-rw-r--r--lib/stdlib/src/timer.erl48
-rw-r--r--lib/stdlib/src/unicode.erl25
-rw-r--r--lib/stdlib/src/win32reg.erl30
-rw-r--r--lib/stdlib/src/zip.erl137
86 files changed, 8026 insertions, 4340 deletions
diff --git a/lib/stdlib/src/Makefile b/lib/stdlib/src/Makefile
index 9ab2cd4134..302834f9d0 100644
--- a/lib/stdlib/src/Makefile
+++ b/lib/stdlib/src/Makefile
@@ -1,18 +1,19 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2013. All Rights Reserved.
+# Copyright Ericsson AB 1996-2016. All Rights Reserved.
#
-# The contents of this file are subject to the Erlang Public License,
-# Version 1.1, (the "License"); you may not use this file except in
-# compliance with the License. You should have received a copy of the
-# Erlang Public License along with this software. If not, it can be
-# retrieved online at http://www.erlang.org/.
+# 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
#
-# Software distributed under the License is distributed on an "AS IS"
-# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-# the License for the specific language governing rights and limitations
-# under the License.
+# 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%
#
@@ -58,6 +59,7 @@ MODULES= \
edlin \
edlin_expand \
epp \
+ erl_anno \
erl_bits \
erl_compile \
erl_eval \
@@ -83,6 +85,7 @@ MODULES= \
gen_event \
gen_fsm \
gen_server \
+ gen_statem \
io \
io_lib \
io_lib_format \
@@ -97,7 +100,6 @@ MODULES= \
otp_internal \
orddict \
ordsets \
- pg \
re \
pool \
proc_lib \
@@ -105,6 +107,7 @@ MODULES= \
qlc \
qlc_pt \
queue \
+ rand \
random \
sets \
shell \
@@ -121,6 +124,7 @@ MODULES= \
zip
HRL_FILES= \
+ ../include/assert.hrl \
../include/erl_compile.hrl \
../include/erl_bits.hrl \
../include/ms_transform.hrl \
@@ -169,6 +173,7 @@ docs:
# specifications.
primary_bootstrap_compiler: \
$(BOOTSTRAP_COMPILER)/ebin/epp.beam \
+ $(BOOTSTRAP_COMPILER)/ebin/erl_anno.beam \
$(BOOTSTRAP_COMPILER)/ebin/erl_scan.beam \
$(BOOTSTRAP_COMPILER)/ebin/erl_parse.beam \
$(BOOTSTRAP_COMPILER)/ebin/erl_lint.beam \
diff --git a/lib/stdlib/src/array.erl b/lib/stdlib/src/array.erl
index 10d2ccea45..d5757dda5b 100644
--- a/lib/stdlib/src/array.erl
+++ b/lib/stdlib/src/array.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2014. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -164,7 +165,7 @@
elements :: elements(_) %% the tuple tree
}).
--opaque array() :: array(term()).
+-type array() :: array(term()).
-opaque array(Type) ::
#array{default :: Type, elements :: elements(Type)}.
diff --git a/lib/stdlib/src/base64.erl b/lib/stdlib/src/base64.erl
index 7bf281bd8a..bf259e6691 100644
--- a/lib/stdlib/src/base64.erl
+++ b/lib/stdlib/src/base64.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
diff --git a/lib/stdlib/src/beam_lib.erl b/lib/stdlib/src/beam_lib.erl
index 1a7b7d5a5e..7a17226e46 100644
--- a/lib/stdlib/src/beam_lib.erl
+++ b/lib/stdlib/src/beam_lib.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -307,6 +308,17 @@ make_crypto_key(des3_cbc=Type, String) ->
<<K3:8/binary,IVec:8/binary>> = erlang:md5([First|reverse(String)]),
{Type,[K1,K2,K3],IVec,8}.
+-spec build_module(Chunks) -> {'ok', Binary} when
+ Chunks :: [{chunkid(), dataB()}],
+ Binary :: binary().
+
+build_module(Chunks0) ->
+ Chunks = list_to_binary(build_chunks(Chunks0)),
+ Size = byte_size(Chunks),
+ 0 = Size rem 4, % Assertion: correct padding?
+ {ok, <<"FOR1", (Size+4):32, "BEAM", Chunks/binary>>}.
+
+
%%
%% Local functions
%%
@@ -418,12 +430,6 @@ strip_file(File) ->
end
end.
-build_module(Chunks0) ->
- Chunks = list_to_binary(build_chunks(Chunks0)),
- Size = byte_size(Chunks),
- 0 = Size rem 4, % Assertion: correct padding?
- {ok, <<"FOR1", (Size+4):32, "BEAM", Chunks/binary>>}.
-
build_chunks([{Id, Data} | Chunks]) ->
BId = list_to_binary(Id),
Size = byte_size(Data),
@@ -652,7 +658,13 @@ chunk_to_data(abstract_code=Id, Chunk, File, _Cs, AtomTable, Mod) ->
{'EXIT', _} ->
error({invalid_chunk, File, chunk_name_to_id(Id, File)});
Term ->
- {AtomTable, {Id, Term}}
+ try
+ {AtomTable, {Id, anno_from_term(Term)}}
+ catch
+ _:_ ->
+ error({invalid_chunk, File,
+ chunk_name_to_id(Id, File)})
+ end
end
end;
chunk_to_data(atoms=Id, _Chunk, _File, Cs, AtomTable0, _Mod) ->
@@ -860,7 +872,7 @@ mandatory_chunks() ->
%%% can use it.
%%% ====================================================================
--record(state, {crypto_key_f :: crypto_fun()}).
+-record(state, {crypto_key_f :: crypto_fun() | 'undefined'}).
-define(CRYPTO_KEY_SERVER, beam_lib__crypto_key_server).
@@ -878,7 +890,26 @@ decrypt_abst(Type, Module, File, Id, AtomTable, Bin) ->
decrypt_abst_1({Type,Key,IVec,_BlockSize}, Bin) ->
ok = start_crypto(),
NewBin = crypto:block_decrypt(Type, Key, IVec, Bin),
- binary_to_term(NewBin).
+ Term = binary_to_term(NewBin),
+ anno_from_term(Term).
+
+anno_from_term({raw_abstract_v1, Forms}) ->
+ {raw_abstract_v1, anno_from_forms(Forms)};
+anno_from_term({Tag, Forms}) when Tag =:= abstract_v1; Tag =:= abstract_v2 ->
+ try {Tag, anno_from_forms(Forms)}
+ catch
+ _:_ ->
+ {Tag, Forms}
+ end;
+anno_from_term(T) ->
+ T.
+
+anno_from_forms(Forms0) ->
+ %% Forms with record field types created before OTP 19.0 are
+ %% replaced by well-formed record forms holding the type
+ %% information.
+ Forms = epp:restore_typed_record_fields(Forms0),
+ [erl_parse:anno_from_term(Form) || Form <- Forms].
start_crypto() ->
case crypto:start() of
@@ -904,7 +935,10 @@ call_crypto_server(Req) ->
end.
call_crypto_server_1(Req) ->
- {ok, _} = gen_server:start({local,?CRYPTO_KEY_SERVER}, ?MODULE, [], []),
+ case gen_server:start({local,?CRYPTO_KEY_SERVER}, ?MODULE, [], []) of
+ {ok, _} -> ok;
+ {error, {already_started, _}} -> ok
+ end,
erlang:yield(),
call_crypto_server(Req).
@@ -945,9 +979,7 @@ handle_call({get_crypto_key, What}, From, #state{crypto_key_f=F}=S) ->
handle_call({crypto_key_fun, F}, {_,_} = From, S) ->
case S#state.crypto_key_f of
undefined ->
- %% Don't allow tuple funs here. (They weren't allowed before,
- %% so there is no reason to allow them now.)
- if is_function(F), is_function(F, 1) ->
+ if is_function(F, 1) ->
{Result, Fun, Reply} =
case catch F(init) of
ok ->
diff --git a/lib/stdlib/src/binary.erl b/lib/stdlib/src/binary.erl
index 8d07a356dd..ccc827ca2d 100644
--- a/lib/stdlib/src/binary.erl
+++ b/lib/stdlib/src/binary.erl
@@ -1,25 +1,26 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
-module(binary).
%%
%% Implemented in this module:
--export([split/2,split/3,replace/3,replace/4]).
+-export([replace/3,replace/4]).
-export_type([cp/0]).
@@ -33,7 +34,8 @@
decode_unsigned/2, encode_unsigned/1, encode_unsigned/2,
first/1, last/1, list_to_bin/1, longest_common_prefix/1,
longest_common_suffix/1, match/2, match/3, matches/2,
- matches/3, part/2, part/3, referenced_byte_size/1]).
+ matches/3, part/2, part/3, referenced_byte_size/1,
+ split/2, split/3]).
-spec at(Subject, Pos) -> byte() when
Subject :: binary(),
@@ -197,69 +199,25 @@ part(_, _, _) ->
referenced_byte_size(_) ->
erlang:nif_error(undef).
-%%% End of BIFs.
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% split
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-spec split(Subject, Pattern) -> Parts when
Subject :: binary(),
Pattern :: binary() | [binary()] | cp(),
Parts :: [binary()].
-split(H,N) ->
- split(H,N,[]).
+split(_, _) ->
+ erlang:nif_error(undef).
-spec split(Subject, Pattern, Options) -> Parts when
Subject :: binary(),
Pattern :: binary() | [binary()] | cp(),
Options :: [Option],
- Option :: {scope, part()} | trim | global,
+ Option :: {scope, part()} | trim | global | trim_all,
Parts :: [binary()].
-split(Haystack,Needles,Options) ->
- try
- {Part,Global,Trim} = get_opts_split(Options,{no,false,false}),
- Moptlist = case Part of
- no ->
- [];
- {A,B} ->
- [{scope,{A,B}}]
- end,
- MList = if
- Global ->
- binary:matches(Haystack,Needles,Moptlist);
- true ->
- case binary:match(Haystack,Needles,Moptlist) of
- nomatch -> [];
- Match -> [Match]
- end
- end,
- do_split(Haystack,MList,0,Trim)
- catch
- _:_ ->
- erlang:error(badarg)
- end.
-
-do_split(H,[],N,true) when N >= byte_size(H) ->
- [];
-do_split(H,[],N,_) ->
- [binary:part(H,{N,byte_size(H)-N})];
-do_split(H,[{A,B}|T],N,Trim) ->
- case binary:part(H,{N,A-N}) of
- <<>> ->
- Rest = do_split(H,T,A+B,Trim),
- case {Trim, Rest} of
- {true,[]} ->
- [];
- _ ->
- [<<>> | Rest]
- end;
- Oth ->
- [Oth | do_split(H,T,A+B,Trim)]
- end.
+split(_, _, _) ->
+ erlang:nif_error(undef).
+%%% End of BIFs.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% replace
@@ -346,17 +304,6 @@ splitat(H,N,[I|T]) ->
%% Simple helper functions
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-get_opts_split([],{Part,Global,Trim}) ->
- {Part,Global,Trim};
-get_opts_split([{scope,{A,B}} | T],{_Part,Global,Trim}) ->
- get_opts_split(T,{{A,B},Global,Trim});
-get_opts_split([global | T],{Part,_Global,Trim}) ->
- get_opts_split(T,{Part,true,Trim});
-get_opts_split([trim | T],{Part,Global,_Trim}) ->
- get_opts_split(T,{Part,Global,true});
-get_opts_split(_,_) ->
- throw(badopt).
-
get_opts_replace([],{Part,Global,Insert}) ->
{Part,Global,Insert};
get_opts_replace([{scope,{A,B}} | T],{_Part,Global,Insert}) ->
diff --git a/lib/stdlib/src/c.erl b/lib/stdlib/src/c.erl
index 9860adf04d..ad4915eabe 100644
--- a/lib/stdlib/src/c.erl
+++ b/lib/stdlib/src/c.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -27,7 +28,7 @@
lc_batch/0, lc_batch/1,
i/3,pid/3,m/0,m/1,
bt/1, q/0,
- erlangrc/0,erlangrc/1,bi/1, flush/0, regs/0,
+ erlangrc/0,erlangrc/1,bi/1, flush/0, regs/0, uptime/0,
nregs/0,pwd/0,ls/0,ls/1,cd/1,memory/1,memory/0, xm/1]).
-export([display_info/1]).
@@ -65,6 +66,7 @@ help() ->
"q() -- quit - shorthand for init:stop()\n"
"regs() -- information about registered processes\n"
"nregs() -- information about all registered processes\n"
+ "uptime() -- print node uptime\n"
"xm(M) -- cross reference check a module\n"
"y(File) -- generate a Yecc parser\n">>).
@@ -774,6 +776,26 @@ memory() -> erlang:memory().
memory(TypeSpec) -> erlang:memory(TypeSpec).
%%
+%% uptime/0
+%%
+
+-spec uptime() -> 'ok'.
+
+uptime() ->
+ io:format("~s~n", [uptime(get_uptime())]).
+
+uptime({D, {H, M, S}}) ->
+ lists:flatten(
+ [[ io_lib:format("~p days, ", [D]) || D > 0 ],
+ [ io_lib:format("~p hours, ", [H]) || D+H > 0 ],
+ [ io_lib:format("~p minutes and ", [M]) || D+H+M > 0 ],
+ io_lib:format("~p seconds", [S])]).
+
+get_uptime() ->
+ {UpTime, _} = erlang:statistics(wall_clock),
+ calendar:seconds_to_daystime(UpTime div 1000).
+
+%%
%% Cross Reference Check
%%
%%-spec xm(module() | file:filename()) -> xref:m/1 return
diff --git a/lib/stdlib/src/calendar.erl b/lib/stdlib/src/calendar.erl
index 0320e0cd0e..55a0cfc9a1 100644
--- a/lib/stdlib/src/calendar.erl
+++ b/lib/stdlib/src/calendar.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -299,7 +300,7 @@ local_time_to_universal_time_dst(DateTime) ->
%% now_to_universal_time(Now)
%% now_to_datetime(Now)
%%
-%% Convert from now() to UTC.
+%% Convert from erlang:timestamp() to UTC.
%%
%% Args: Now = now(); now() = {MegaSec, Sec, MilliSec}, MegaSec = Sec
%% = MilliSec = integer()
diff --git a/lib/stdlib/src/dets.erl b/lib/stdlib/src/dets.erl
index a4bd45ea19..bf22949870 100644
--- a/lib/stdlib/src/dets.erl
+++ b/lib/stdlib/src/dets.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -371,7 +372,7 @@ info(Tab) ->
Item :: 'access' | 'auto_save' | 'bchunk_format'
| 'hash' | 'file_size' | 'filename' | 'keypos' | 'memory'
| 'no_keys' | 'no_objects' | 'no_slots' | 'owner' | 'ram_file'
- | 'safe_fixed' | 'size' | 'type' | 'version',
+ | 'safe_fixed' | 'safe_fixed_monotonic_time' | 'size' | 'type' | 'version',
Value :: term().
info(Tab, owner) ->
@@ -1123,7 +1124,9 @@ 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) ->
+repl({file, File}, Defs) when is_list(File) ->
+ Defs#open_args{file = File};
+repl({file, File}, Defs) when is_atom(File) ->
Defs#open_args{file = to_list(File)};
repl({keypos, P}, Defs) when is_integer(P), P > 0 ->
Defs#open_args{keypos =P};
@@ -1288,7 +1291,15 @@ init(Parent, Server) ->
open_file_loop(#head{parent = Parent, server = Server}).
open_file_loop(Head) ->
- open_file_loop(Head, 0).
+ %% The Dets server pretends the file is open before
+ %% internal_open() has been called, which means that unless the
+ %% internal_open message is applied first, other processes can
+ %% find the pid by calling dets_server:get_pid() and do things
+ %% before Head has been initialized properly.
+ receive
+ ?DETS_CALL(From, {internal_open, _Ref, _Args}=Op) ->
+ do_apply_op(Op, From, Head, 0)
+ end.
open_file_loop(Head, N) when element(1, Head#head.update_mode) =:= error ->
open_file_loop2(Head, N);
@@ -1963,7 +1974,9 @@ do_safe_fixtable(Head, Pid, true) ->
case Head#head.fixed of
false ->
link(Pid),
- Fixed = {erlang:now(), [{Pid, 1}]},
+ MonTime = erlang:monotonic_time(),
+ TimeOffset = erlang:time_offset(),
+ Fixed = {{MonTime, TimeOffset}, [{Pid, 1}]},
Ftab = dets_utils:get_freelists(Head),
Head#head{fixed = Fixed, freelists = {Ftab, Ftab}};
{TimeStamp, Counters} ->
@@ -2090,7 +2103,22 @@ finfo(H, no_keys) ->
finfo(H, no_slots) -> {H, (H#head.mod):no_slots(H)};
finfo(H, pid) -> {H, self()};
finfo(H, ram_file) -> {H, H#head.ram_file};
-finfo(H, safe_fixed) -> {H, H#head.fixed};
+finfo(H, safe_fixed) ->
+ {H,
+ case H#head.fixed of
+ false ->
+ false;
+ {{FixMonTime, TimeOffset}, RefList} ->
+ {make_timestamp(FixMonTime, TimeOffset), RefList}
+ end};
+finfo(H, safe_fixed_monotonic_time) ->
+ {H,
+ case H#head.fixed of
+ false ->
+ false;
+ {{FixMonTime, _TimeOffset}, RefList} ->
+ {FixMonTime, RefList}
+ end};
finfo(H, size) ->
case catch write_cache(H) of
{H2, []} ->
@@ -3088,14 +3116,14 @@ update_cache(Head, ToAdd) ->
{Head1, Found, []};
Cache#cache.wrtime =:= undefined ->
%% Empty cache. Schedule a delayed write.
- Now = now(), Me = self(),
+ Now = time_now(), Me = self(),
Call = ?DETS_CALL(Me, {delayed_write, Now}),
erlang:send_after(Cache#cache.delay, Me, Call),
{Head1#head{cache = NewCache#cache{wrtime = Now}}, Found, []};
Size0 =:= 0 ->
%% Empty cache that has been written after the
%% currently scheduled delayed write.
- {Head1#head{cache = NewCache#cache{wrtime = now()}}, Found, []};
+ {Head1#head{cache = NewCache#cache{wrtime = time_now()}}, Found, []};
true ->
%% Cache is not empty, delayed write has been scheduled.
{Head1, Found, []}
@@ -3158,11 +3186,7 @@ delayed_write(Head, WrTime) ->
Head#head{cache = NewCache};
true ->
%% Yes, schedule a new delayed write.
- {MS1,S1,M1} = WrTime,
- {MS2,S2,M2} = LastWrTime,
- WrT = M1+1000000*(S1+1000000*MS1),
- LastWrT = M2+1000000*(S2+1000000*MS2),
- When = round((LastWrT - WrT)/1000), Me = self(),
+ When = round((LastWrTime - WrTime)/1000), Me = self(),
Call = ?DETS_CALL(Me, {delayed_write, LastWrTime}),
erlang:send_after(When, Me, Call),
Head
@@ -3274,6 +3298,19 @@ err(Error) ->
Error
end.
+-compile({inline, [time_now/0]}).
+time_now() ->
+ erlang:monotonic_time(1000000).
+
+make_timestamp(MonTime, TimeOffset) ->
+ ErlangSystemTime = erlang:convert_time_unit(MonTime+TimeOffset,
+ native,
+ micro_seconds),
+ MegaSecs = ErlangSystemTime div 1000000000000,
+ Secs = ErlangSystemTime div 1000000 - MegaSecs*1000000,
+ MicroSecs = ErlangSystemTime rem 1000000,
+ {MegaSecs, Secs, MicroSecs}.
+
%%%%%%%%%%%%%%%%% DEBUG functions %%%%%%%%%%%%%%%%
file_info(FileName) ->
diff --git a/lib/stdlib/src/dets.hrl b/lib/stdlib/src/dets.hrl
index a3f99357a2..6ebeb96156 100644
--- a/lib/stdlib/src/dets.hrl
+++ b/lib/stdlib/src/dets.hrl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
diff --git a/lib/stdlib/src/dets_server.erl b/lib/stdlib/src/dets_server.erl
index 3164d40f35..b02d6ae159 100644
--- a/lib/stdlib/src/dets_server.erl
+++ b/lib/stdlib/src/dets_server.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2014. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
diff --git a/lib/stdlib/src/dets_sup.erl b/lib/stdlib/src/dets_sup.erl
index 8ea2ba9b3f..43609cb8a1 100644
--- a/lib/stdlib/src/dets_sup.erl
+++ b/lib/stdlib/src/dets_sup.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
diff --git a/lib/stdlib/src/dets_utils.erl b/lib/stdlib/src/dets_utils.erl
index 6c176ad513..34a8ddddaa 100644
--- a/lib/stdlib/src/dets_utils.erl
+++ b/lib/stdlib/src/dets_utils.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -447,7 +448,7 @@ reset_cache(C) ->
WrTime =:= undefined ->
WrTime;
true ->
- now()
+ erlang:monotonic_time(1000000)
end,
PK = family(C#cache.cache),
NewC = C#cache{cache = [], csize = 0, inserts = 0, wrtime = NewWrTime},
@@ -746,6 +747,8 @@ all_allocated([{X,Y} | L], _X0, Y0, A) when Y0 < X ->
all_allocated_as_list(Head) ->
all_allocated_as_list(all(get_freelists(Head)), 0, Head#head.base, []).
+-dialyzer({no_improper_lists, all_allocated_as_list/4}).
+
all_allocated_as_list([], _X0, _Y0, []) ->
[];
all_allocated_as_list([], _X0, _Y0, A) ->
diff --git a/lib/stdlib/src/dets_v8.erl b/lib/stdlib/src/dets_v8.erl
index f188502017..1bf53d91b1 100644
--- a/lib/stdlib/src/dets_v8.erl
+++ b/lib/stdlib/src/dets_v8.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -35,6 +36,8 @@
%% For backward compatibility.
-export([sz2pos/1]).
+-dialyzer(no_improper_lists).
+
-compile({inline, [{sz2pos,1},{scan_skip,7}]}).
-compile({inline, [{skip_bytes,5}, {get_segp,1}]}).
-compile({inline, [{wl_lookup,5}]}).
diff --git a/lib/stdlib/src/dets_v9.erl b/lib/stdlib/src/dets_v9.erl
index 2af93ec800..6c406fc03a 100644
--- a/lib/stdlib/src/dets_v9.erl
+++ b/lib/stdlib/src/dets_v9.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -33,6 +34,8 @@
-export([cache_segps/3]).
+-dialyzer(no_improper_lists).
+
-compile({inline, [{max_objsize,1},{maxobjsize,1}]}).
-compile({inline, [{write_segment_file,6}]}).
-compile({inline, [{sz2pos,1},{adjsz,1}]}).
diff --git a/lib/stdlib/src/dict.erl b/lib/stdlib/src/dict.erl
index cf8fb3114a..f921e28ef6 100644
--- a/lib/stdlib/src/dict.erl
+++ b/lib/stdlib/src/dict.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2014. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -70,7 +71,7 @@
}).
--opaque dict() :: dict(_, _).
+-type dict() :: dict(_, _).
-opaque dict(Key, Value) :: #dict{segs :: segs(Key, Value)}.
@@ -332,6 +333,8 @@ update_counter(Key, Incr, D0) when is_number(Incr) ->
D0, Slot),
maybe_expand(D1, Ic).
+-dialyzer({no_improper_lists, counter_bkt/3}).
+
counter_bkt(Key, I, [?kv(Key,Val)|Bkt]) ->
{[?kv(Key,Val+I)|Bkt],0};
counter_bkt(Key, I, [Other|Bkt0]) ->
@@ -417,6 +420,8 @@ on_bucket(F, T, Slot) ->
%% could have implemented map and filter using fold but these are
%% faster. We hope!
+fold_dict(F, Acc, #dict{size=0}) when is_function(F, 3) ->
+ Acc;
fold_dict(F, Acc, D) ->
Segs = D#dict.segs,
fold_segs(F, Acc, Segs, tuple_size(Segs)).
@@ -434,6 +439,8 @@ fold_bucket(F, Acc, [?kv(Key,Val)|Bkt]) ->
fold_bucket(F, F(Key, Val, Acc), Bkt);
fold_bucket(F, Acc, []) when is_function(F, 3) -> Acc.
+map_dict(F, #dict{size=0} = Dict) when is_function(F, 2) ->
+ Dict;
map_dict(F, D) ->
Segs0 = tuple_to_list(D#dict.segs),
Segs1 = map_seg_list(F, Segs0),
@@ -453,6 +460,8 @@ map_bucket(F, [?kv(Key,Val)|Bkt]) ->
[?kv(Key,F(Key, Val))|map_bucket(F, Bkt)];
map_bucket(F, []) when is_function(F, 2) -> [].
+filter_dict(F, #dict{size=0} = Dict) when is_function(F, 2) ->
+ Dict;
filter_dict(F, D) ->
Segs0 = tuple_to_list(D#dict.segs),
{Segs1,Fc} = filter_seg_list(F, Segs0, [], 0),
diff --git a/lib/stdlib/src/digraph.erl b/lib/stdlib/src/digraph.erl
index 0c21271529..8a4df95027 100644
--- a/lib/stdlib/src/digraph.erl
+++ b/lib/stdlib/src/digraph.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -36,7 +37,7 @@
-export([get_short_path/3, get_short_cycle/2]).
--export_type([graph/0, d_type/0, vertex/0, edge/0]).
+-export_type([graph/0, d_type/0, vertex/0, edge/0, label/0]).
-record(digraph, {vtab = notable :: ets:tab(),
etab = notable :: ets:tab(),
@@ -337,6 +338,8 @@ edge(G, E) ->
%%
-spec new_edge_id(graph()) -> edge().
+-dialyzer({no_improper_lists, new_edge_id/1}).
+
new_edge_id(G) ->
NT = G#digraph.ntab,
[{'$eid', K}] = ets:lookup(NT, '$eid'),
@@ -349,6 +352,8 @@ new_edge_id(G) ->
%%
-spec new_vertex_id(graph()) -> vertex().
+-dialyzer({no_improper_lists, new_vertex_id/1}).
+
new_vertex_id(G) ->
NT = G#digraph.ntab,
[{'$vid', K}] = ets:lookup(NT, '$vid'),
diff --git a/lib/stdlib/src/digraph_utils.erl b/lib/stdlib/src/digraph_utils.erl
index 011bcd0260..4aa9ae810d 100644
--- a/lib/stdlib/src/digraph_utils.erl
+++ b/lib/stdlib/src/digraph_utils.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2014. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
diff --git a/lib/stdlib/src/edlin.erl b/lib/stdlib/src/edlin.erl
index b3bc5f6d92..71e8471c45 100644
--- a/lib/stdlib/src/edlin.erl
+++ b/lib/stdlib/src/edlin.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -21,7 +22,7 @@
%% A simple Emacs-like line editor.
%% About Latin-1 characters: see the beginning of erl_scan.erl.
--export([init/0,start/1,start/2,edit_line/2,prefix_arg/1]).
+-export([init/0,init/1,start/1,start/2,edit_line/2,prefix_arg/1]).
-export([erase_line/1,erase_inp/1,redraw_line/1]).
-export([length_before/1,length_after/1,prompt/1]).
-export([current_line/1, current_chars/1]).
@@ -44,6 +45,20 @@
init() ->
put(kill_buffer, []).
+init(Pid) ->
+ %% copy the kill_buffer from the process Pid
+ CopiedKillBuf =
+ case erlang:process_info(Pid, dictionary) of
+ {dictionary,Dict} ->
+ case proplists:get_value(kill_buffer, Dict) of
+ undefined -> [];
+ Buf -> Buf
+ end;
+ undefined ->
+ []
+ end,
+ put(kill_buffer, CopiedKillBuf).
+
%% start(Prompt)
%% edit(Characters, Continuation)
%% Return
@@ -212,6 +227,8 @@ key_map($F, meta_o) -> end_of_line;
key_map($\177, none) -> backward_delete_char;
key_map($\177, meta) -> backward_kill_word;
key_map($[, meta) -> meta_left_sq_bracket;
+key_map($H, meta_left_sq_bracket) -> beginning_of_line;
+key_map($F, meta_left_sq_bracket) -> end_of_line;
key_map($D, meta_left_sq_bracket) -> backward_char;
key_map($C, meta_left_sq_bracket) -> forward_char;
% support a few <CTRL>+<CURSOR LEFT|RIGHT> combinations...
@@ -222,8 +239,10 @@ key_map($[, meta_meta) -> meta_csi;
key_map($C, meta_csi) -> forward_word;
key_map($D, meta_csi) -> backward_word;
key_map($1, meta_left_sq_bracket) -> {csi, "1"};
+key_map($3, meta_left_sq_bracket) -> {csi, "3"};
key_map($5, meta_left_sq_bracket) -> {csi, "5"};
key_map($5, {csi, "1;"}) -> {csi, "1;5"};
+key_map($~, {csi, "3"}) -> forward_delete_char;
key_map($C, {csi, "5"}) -> forward_word;
key_map($C, {csi, "1;5"}) -> forward_word;
key_map($D, {csi, "5"}) -> backward_word;
@@ -446,7 +465,6 @@ word_char(C) when C >= $a, C =< $z -> true;
word_char(C) when C >= $ß, C =< $ÿ, C =/= $÷ -> true;
word_char(C) when C >= $0, C =< $9 -> true;
word_char(C) when C =:= $_ -> true;
-word_char(C) when C =:= $. -> true; % accept dot-separated names
word_char(_) -> false.
%% over_white(Chars, InitialStack, InitialCount) ->
diff --git a/lib/stdlib/src/edlin_expand.erl b/lib/stdlib/src/edlin_expand.erl
index a2b4663219..ec64470461 100644
--- a/lib/stdlib/src/edlin_expand.erl
+++ b/lib/stdlib/src/edlin_expand.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
diff --git a/lib/stdlib/src/epp.erl b/lib/stdlib/src/epp.erl
index 5f8637c118..73934e0e3c 100644
--- a/lib/stdlib/src/epp.erl
+++ b/lib/stdlib/src/epp.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
@@ -39,16 +40,26 @@
-type ifdef() :: 'ifdef' | 'ifndef' | 'else'.
--type name() :: {'atom', atom()}.
+-type name() :: atom().
-type argspec() :: 'none' %No arguments
| non_neg_integer(). %Number of arguments
+-type argnames() :: [atom()].
-type tokens() :: [erl_scan:token()].
+-type predef() :: 'undefined' | {'none', tokens()}.
+-type userdef() :: {argspec(), {argnames(), tokens()}}.
-type used() :: {name(), argspec()}.
+-type function_name_type() :: 'undefined'
+ | {atom(),non_neg_integer()}
+ | tokens().
+
+-type warning_info() :: {erl_anno:location(), module(), term()}.
+
-define(DEFAULT_ENCODING, utf8).
%% Epp state record.
--record(epp, {file :: file:io_device(), %Current file
+-record(epp, {file :: file:io_device()
+ | 'undefined', %Current file
location=1, %Current location
delta=0 :: non_neg_integer(), %Offset from Location (-file)
name="" :: file:name(), %Current file name
@@ -56,21 +67,15 @@
istk=[] :: [ifdef()], %Ifdef stack
sstk=[] :: [#epp{}], %State stack
path=[] :: [file:name()], %Include-path
- macs = dict:new() %Macros (don't care locations)
- :: dict:dict(name(), {argspec(), tokens()}),
- uses = dict:new() %Macro use structure
- :: dict:dict(name(), [{argspec(), [used()]}]),
+ macs = #{} %Macros (don't care locations)
+ :: #{name() => predef() | [userdef()]},
+ uses = #{} %Macro use structure
+ :: #{name() => [{argspec(), [used()]}]},
default_encoding = ?DEFAULT_ENCODING :: source_encoding(),
- pre_opened = false :: boolean()
+ pre_opened = false :: boolean(),
+ fname = [] :: function_name_type()
}).
-%%% Note on representation: as tokens, both {var, Location, Name} and
-%%% {atom, Location, Name} can occur as macro identifiers. However, keeping
-%%% this distinction here is done for historical reasons only: previously,
-%%% ?FOO and ?'FOO' were not the same, but now they are. Removing the
-%%% distinction in the internal representation would simplify the code
-%%% a little.
-
%% open(Options)
%% open(FileName, IncludePath)
%% open(FileName, IncludePath, PreDefMacros)
@@ -155,11 +160,13 @@ scan_erl_form(Epp) ->
epp_request(Epp, scan_erl_form).
-spec parse_erl_form(Epp) ->
- {'ok', AbsForm} | {'eof', Line} | {error, ErrorInfo} when
+ {'ok', AbsForm} | {error, ErrorInfo} |
+ {'warning',WarningInfo} | {'eof',Line} when
Epp :: epp_handle(),
AbsForm :: erl_parse:abstract_form(),
- Line :: erl_scan:line(),
- ErrorInfo :: erl_scan:error_info() | erl_parse:error_info().
+ Line :: erl_anno:line(),
+ ErrorInfo :: erl_scan:error_info() | erl_parse:error_info(),
+ WarningInfo :: warning_info().
parse_erl_form(Epp) ->
case epp_request(Epp, scan_erl_form) of
@@ -210,8 +217,16 @@ format_error({include,W,F}) ->
io_lib:format("can't find include ~s \"~s\"", [W,F]);
format_error({illegal,How,What}) ->
io_lib:format("~s '-~s'", [How,What]);
+format_error({illegal_function,Macro}) ->
+ io_lib:format("?~s can only be used within a function", [Macro]);
+format_error({illegal_function_usage,Macro}) ->
+ io_lib:format("?~s must not begin a form", [Macro]);
format_error({'NYI',What}) ->
io_lib:format("not yet implemented '~s'", [What]);
+format_error({error,Term}) ->
+ io_lib:format("-error(~p).", [Term]);
+format_error({warning,Term}) ->
+ io_lib:format("-warning(~p).", [Term]);
format_error(E) -> file:format_error(E).
-spec parse_file(FileName, IncludePath, PredefMacros) ->
@@ -220,7 +235,7 @@ format_error(E) -> file:format_error(E).
IncludePath :: [DirectoryName :: file:name()],
Form :: erl_parse:abstract_form() | {'error', ErrorInfo} | {'eof',Line},
PredefMacros :: macros(),
- Line :: erl_scan:line(),
+ Line :: erl_anno:line(),
ErrorInfo :: erl_scan:error_info() | erl_parse:error_info(),
OpenError :: file:posix() | badarg | system_limit.
@@ -235,7 +250,7 @@ parse_file(Ifile, Path, Predefs) ->
{'default_encoding', DefEncoding :: source_encoding()} |
'extra'],
Form :: erl_parse:abstract_form() | {'error', ErrorInfo} | {'eof',Line},
- Line :: erl_scan:line(),
+ Line :: erl_anno:line(),
ErrorInfo :: erl_scan:error_info() | erl_parse:error_info(),
Extra :: [{'encoding', source_encoding() | 'none'}],
OpenError :: file:posix() | badarg | system_limit.
@@ -256,31 +271,22 @@ parse_file(Ifile, Options) ->
-spec parse_file(Epp) -> [Form] when
Epp :: epp_handle(),
- Form :: erl_parse:abstract_form() | {'error', ErrorInfo} | {'eof',Line},
- Line :: erl_scan:line(),
- ErrorInfo :: erl_scan:error_info() | erl_parse:error_info().
+ Form :: erl_parse:abstract_form() | {'error', ErrorInfo} |
+ {'warning',WarningInfo} | {'eof',Line},
+ Line :: erl_anno:line(),
+ ErrorInfo :: erl_scan:error_info() | erl_parse:error_info(),
+ WarningInfo :: warning_info().
parse_file(Epp) ->
case parse_erl_form(Epp) of
{ok,Form} ->
- case Form of
- {attribute,La,record,{Record, Fields}} ->
- case normalize_typed_record_fields(Fields) of
- {typed, NewFields} ->
- [{attribute, La, record, {Record, NewFields}},
- {attribute, La, type,
- {{record, Record}, Fields, []}}
- |parse_file(Epp)];
- not_typed ->
- [Form|parse_file(Epp)]
- end;
- _ ->
- [Form|parse_file(Epp)]
- end;
+ [Form|parse_file(Epp)];
{error,E} ->
[{error,E}|parse_file(Epp)];
+ {warning,W} ->
+ [{warning,W}|parse_file(Epp)];
{eof,Location} ->
- [{eof,Location}]
+ [{eof,erl_anno:new(Location)}]
end.
-spec default_encoding() -> source_encoding().
@@ -547,7 +553,9 @@ init_server(Pid, Name, Options, St0) ->
path=Path, macs=Ms1,
default_encoding=DefEncoding},
From = wait_request(St),
- enter_file_reply(From, Name, AtLocation, AtLocation),
+ Anno = erl_anno:new(AtLocation),
+ enter_file_reply(From, file_name(Name), Anno,
+ AtLocation, code),
wait_req_scan(St);
{error,E} ->
epp_reply(Pid, {error,E})
@@ -558,17 +566,20 @@ init_server(Pid, Name, Options, St0) ->
%% FILE, LINE, MODULE as undefined, MACHINE and MACHINE value.
predef_macros(File) ->
- Machine = list_to_atom(erlang:system_info(machine)),
- dict:from_list([
- {{atom,'FILE'}, {none,[{string,1,File}]}},
- {{atom,'LINE'}, {none,[{integer,1,1}]}},
- {{atom,'MODULE'}, undefined},
- {{atom,'MODULE_STRING'}, undefined},
- {{atom,'BASE_MODULE'}, undefined},
- {{atom,'BASE_MODULE_STRING'}, undefined},
- {{atom,'MACHINE'}, {none,[{atom,1,Machine}]}},
- {{atom,Machine}, {none,[{atom,1,true}]}}
- ]).
+ Machine = list_to_atom(erlang:system_info(machine)),
+ Anno = line1(),
+ Defs = [{'FILE', {none,[{string,Anno,File}]}},
+ {'FUNCTION_NAME', undefined},
+ {'FUNCTION_ARITY', undefined},
+ {'LINE', {none,[{integer,Anno,1}]}},
+ {'MODULE', undefined},
+ {'MODULE_STRING', undefined},
+ {'BASE_MODULE', undefined},
+ {'BASE_MODULE_STRING', undefined},
+ {'MACHINE', {none,[{atom,Anno,Machine}]}},
+ {Machine, {none,[{atom,Anno,true}]}}
+ ],
+ maps:from_list(Defs).
%% user_predef(PreDefMacros, Macros) ->
%% {ok,MacroDict} | {error,E}
@@ -577,27 +588,21 @@ predef_macros(File) ->
user_predef([{M,Val,redefine}|Pdm], Ms) when is_atom(M) ->
Exp = erl_parse:tokens(erl_parse:abstract(Val)),
- user_predef(Pdm, dict:store({atom,M}, {none,Exp}, Ms));
+ user_predef(Pdm, Ms#{M=>{none,Exp}});
user_predef([{M,Val}|Pdm], Ms) when is_atom(M) ->
- case dict:find({atom,M}, Ms) of
- {ok,_Defs} when is_list(_Defs) -> %% User defined macros
+ case Ms of
+ #{M:=Defs} when is_list(Defs) ->
+ %% User defined macros.
{error,{redefine,M}};
- {ok,_Def} -> %% Predefined macros
+ #{M:=_Defs} ->
+ %% Predefined macros.
{error,{redefine_predef,M}};
- error ->
+ _ ->
Exp = erl_parse:tokens(erl_parse:abstract(Val)),
- user_predef(Pdm, dict:store({atom,M}, [{none, {none,Exp}}], Ms))
+ user_predef(Pdm, Ms#{M=>[{none,{none,Exp}}]})
end;
user_predef([M|Pdm], Ms) when is_atom(M) ->
- case dict:find({atom,M}, Ms) of
- {ok,_Defs} when is_list(_Defs) -> %% User defined macros
- {error,{redefine,M}};
- {ok,_Def} -> %% Predefined macros
- {error,{redefine_predef,M}};
- error ->
- user_predef(Pdm,
- dict:store({atom,M}, [{none, {none,[{atom,1,true}]}}], Ms))
- end;
+ user_predef([{M,true}|Pdm], Ms);
user_predef([Md|_Pdm], _Ms) -> {error,{bad,Md}};
user_predef([], Ms) -> {ok,Ms}.
@@ -611,7 +616,9 @@ wait_request(St) ->
receive
{epp_request,From,scan_erl_form} -> From;
{epp_request,From,macro_defs} ->
- epp_reply(From, dict:to_list(St#epp.macs)),
+ %% Return the old format to avoid any incompability issues.
+ Defs = [{{atom,K},V} || {K,V} <- maps:to_list(St#epp.macs)],
+ epp_reply(From, Defs),
wait_request(St);
{epp_request,From,close} ->
close_file(St),
@@ -645,7 +652,7 @@ wait_req_skip(St, Sis) ->
enter_file(_NewName, Inc, From, St)
when length(St#epp.sstk) >= 8 ->
- epp_reply(From, {error,{abs_loc(Inc),epp,{depth,"include"}}}),
+ epp_reply(From, {error,{loc(Inc),epp,{depth,"include"}}}),
wait_req_scan(St);
enter_file(NewName, Inc, From, St) ->
case file:path_open(St#epp.path, NewName, [read]) of
@@ -653,7 +660,7 @@ enter_file(NewName, Inc, From, St) ->
Loc = start_loc(St#epp.location),
wait_req_scan(enter_file2(NewF, Pname, From, St, Loc));
{error,_E} ->
- epp_reply(From, {error,{abs_loc(Inc),epp,{include,file,NewName}}}),
+ epp_reply(From, {error,{loc(Inc),epp,{include,file,NewName}}}),
wait_req_scan(St)
end.
@@ -661,9 +668,10 @@ enter_file(NewName, Inc, From, St) ->
%% Set epp to use this file and "enter" it.
enter_file2(NewF, Pname, From, St0, AtLocation) ->
- Loc = start_loc(AtLocation),
- enter_file_reply(From, Pname, Loc, AtLocation),
- Ms = dict:store({atom,'FILE'}, {none,[{string,Loc,Pname}]}, St0#epp.macs),
+ Anno = erl_anno:new(AtLocation),
+ enter_file_reply(From, Pname, Anno, AtLocation, code),
+ Ms0 = St0#epp.macs,
+ Ms = Ms0#{'FILE':={none,[{string,Anno,Pname}]}},
%% update the head of the include path to be the directory of the new
%% source file, so that an included file can always include other files
%% relative to its current location (this is also how C does it); note
@@ -673,16 +681,20 @@ enter_file2(NewF, Pname, From, St0, AtLocation) ->
Path = [filename:dirname(Pname) | tl(St0#epp.path)],
DefEncoding = St0#epp.default_encoding,
_ = set_encoding(NewF, DefEncoding),
- #epp{file=NewF,location=Loc,name=Pname,name2=Pname,delta=0,
+ #epp{file=NewF,location=AtLocation,name=Pname,name2=Pname,delta=0,
sstk=[St0|St0#epp.sstk],path=Path,macs=Ms,
default_encoding=DefEncoding}.
-enter_file_reply(From, Name, Location, AtLocation) ->
- Attr = loc_attr(AtLocation),
- Rep = {ok, [{'-',Attr},{atom,Attr,file},{'(',Attr},
- {string,Attr,file_name(Name)},{',',Attr},
- {integer,Attr,get_line(Location)},{')',Location},
- {dot,Attr}]},
+enter_file_reply(From, Name, LocationAnno, AtLocation, Where) ->
+ Anno0 = loc_anno(AtLocation),
+ Anno = case Where of
+ code -> Anno0;
+ generated -> erl_anno:set_generated(true, Anno0)
+ end,
+ Rep = {ok, [{'-',Anno},{atom,Anno,file},{'(',Anno},
+ {string,Anno,Name},{',',Anno},
+ {integer,Anno,get_line(LocationAnno)},{')',LocationAnno},
+ {dot,Anno}]},
epp_reply(From, Rep).
%% Flatten filename to a string. Must be a valid filename.
@@ -710,18 +722,19 @@ leave_file(From, St) ->
#epp{location=OldLoc, delta=Delta, name=OldName,
name2=OldName2} = OldSt,
CurrLoc = add_line(OldLoc, Delta),
- Ms = dict:store({atom,'FILE'},
- {none,[{string,CurrLoc,OldName2}]},
- St#epp.macs),
+ Anno = erl_anno:new(CurrLoc),
+ Ms0 = St#epp.macs,
+ Ms = Ms0#{'FILE':={none,[{string,Anno,OldName2}]}},
NextSt = OldSt#epp{sstk=Sts,macs=Ms,uses=St#epp.uses},
- enter_file_reply(From, OldName, CurrLoc, CurrLoc),
+ enter_file_reply(From, OldName, Anno, CurrLoc, code),
case OldName2 =:= OldName of
true ->
ok;
false ->
NFrom = wait_request(NextSt),
- enter_file_reply(NFrom, OldName2, OldLoc,
- neg_line(CurrLoc))
+ OldAnno = erl_anno:new(OldLoc),
+ enter_file_reply(NFrom, OldName2, OldAnno,
+ CurrLoc, generated)
end,
wait_req_scan(NextSt);
[] ->
@@ -751,6 +764,10 @@ scan_toks([{'-',_Lh},{atom,_Ld,define}=Define|Toks], From, St) ->
scan_define(Toks, Define, From, St);
scan_toks([{'-',_Lh},{atom,_Ld,undef}=Undef|Toks], From, St) ->
scan_undef(Toks, Undef, From, St);
+scan_toks([{'-',_Lh},{atom,_Ld,error}=Error|Toks], From, St) ->
+ scan_err_warn(Toks, Error, From, St);
+scan_toks([{'-',_Lh},{atom,_Ld,warning}=Warn|Toks], From, St) ->
+ scan_err_warn(Toks, Warn, From, St);
scan_toks([{'-',_Lh},{atom,_Li,include}=Inc|Toks], From, St) ->
scan_include(Toks, Inc, From, St);
scan_toks([{'-',_Lh},{atom,_Li,include_lib}=IncLib|Toks], From, St) ->
@@ -768,7 +785,7 @@ scan_toks([{'-',_Lh},{atom,_Le,elif}=Elif|Toks], From, St) ->
scan_toks([{'-',_Lh},{atom,_Le,endif}=Endif|Toks], From, St) ->
scan_endif(Toks, Endif, From, St);
scan_toks([{'-',_Lh},{atom,_Lf,file}=FileToken|Toks0], From, St) ->
- case catch expand_macros(Toks0, {St#epp.macs, St#epp.uses}) of
+ case catch expand_macros(Toks0, St) of
Toks1 when is_list(Toks1) ->
scan_file(Toks1, FileToken, From, St);
{error,ErrL,What} ->
@@ -776,7 +793,7 @@ scan_toks([{'-',_Lh},{atom,_Lf,file}=FileToken|Toks0], From, St) ->
wait_req_scan(St)
end;
scan_toks(Toks0, From, St) ->
- case catch expand_macros(Toks0, {St#epp.macs, St#epp.uses}) of
+ case catch expand_macros(Toks0, St#epp{fname=Toks0}) of
Toks1 when is_list(Toks1) ->
epp_reply(From, {ok,Toks1}),
wait_req_scan(St#epp{macs=scan_module(Toks1, St#epp.macs)});
@@ -786,91 +803,66 @@ scan_toks(Toks0, From, St) ->
end.
scan_module([{'-',_Lh},{atom,_Lm,module},{'(',_Ll}|Ts], Ms) ->
- scan_module_1(Ts, [], Ms);
+ scan_module_1(Ts, Ms);
scan_module([{'-',_Lh},{atom,_Lm,extends},{'(',_Ll}|Ts], Ms) ->
- scan_extends(Ts, [], Ms);
+ scan_extends(Ts, Ms);
scan_module(_Ts, Ms) -> Ms.
-scan_module_1([{atom,_,_}=A,{',',L}|Ts], As, Ms) ->
+scan_module_1([{atom,_,_}=A,{',',L}|Ts], Ms) ->
%% Parameterized modules.
- scan_module_1([A,{')',L}|Ts], As, Ms);
-scan_module_1([{atom,Ln,A},{')',_Lr}|_Ts], As, Ms0) ->
- Mod = lists:concat(lists:reverse([A|As])),
- Ms = dict:store({atom,'MODULE'},
- {none,[{atom,Ln,list_to_atom(Mod)}]}, Ms0),
- dict:store({atom,'MODULE_STRING'}, {none,[{string,Ln,Mod}]}, Ms);
-scan_module_1([{atom,_Ln,A},{'.',_Lr}|Ts], As, Ms) ->
- scan_module_1(Ts, [".",A|As], Ms);
-scan_module_1([{'.',_Lr}|Ts], As, Ms) ->
- scan_module_1(Ts, As, Ms);
-scan_module_1(_Ts, _As, Ms) -> Ms.
-
-scan_extends([{atom,Ln,A},{')',_Lr}|_Ts], As, Ms0) ->
- Mod = lists:concat(lists:reverse([A|As])),
- Ms = dict:store({atom,'BASE_MODULE'},
- {none,[{atom,Ln,list_to_atom(Mod)}]}, Ms0),
- dict:store({atom,'BASE_MODULE_STRING'}, {none,[{string,Ln,Mod}]}, Ms);
-scan_extends([{atom,_Ln,A},{'.',_Lr}|Ts], As, Ms) ->
- scan_extends(Ts, [".",A|As], Ms);
-scan_extends([{'.',_Lr}|Ts], As, Ms) ->
- scan_extends(Ts, As, Ms);
-scan_extends(_Ts, _As, Ms) -> Ms.
+ scan_module_1([A,{')',L}|Ts], Ms);
+scan_module_1([{atom,Ln,A}=ModAtom,{')',_Lr}|_Ts], Ms0) ->
+ ModString = atom_to_list(A),
+ Ms = Ms0#{'MODULE':={none,[ModAtom]}},
+ Ms#{'MODULE_STRING':={none,[{string,Ln,ModString}]}};
+scan_module_1(_Ts, Ms) -> Ms.
+
+scan_extends([{atom,Ln,A}=ModAtom,{')',_Lr}|_Ts], Ms0) ->
+ ModString = atom_to_list(A),
+ Ms = Ms0#{'BASE_MODULE':={none,[ModAtom]}},
+ Ms#{'BASE_MODULE_STRING':={none,[{string,Ln,ModString}]}};
+scan_extends(_Ts, Ms) -> Ms.
+
+scan_err_warn([{'(',_}|_]=Toks0, {atom,_,Tag}=Token, From, St) ->
+ try expand_macros(Toks0, St) of
+ Toks when is_list(Toks) ->
+ case erl_parse:parse_term(Toks) of
+ {ok,Term} ->
+ epp_reply(From, {Tag,{loc(Token),epp,{Tag,Term}}});
+ {error,_} ->
+ epp_reply(From, {error,{loc(Token),epp,{bad,Tag}}})
+ end
+ catch
+ _:_ ->
+ epp_reply(From, {error,{loc(Token),epp,{bad,Tag}}})
+ end,
+ wait_req_scan(St);
+scan_err_warn(_Toks, {atom,_,Tag}=Token, From, St) ->
+ epp_reply(From, {error,{loc(Token),epp,{bad,Tag}}}),
+ wait_req_scan(St).
%% scan_define(Tokens, DefineToken, From, EppState)
-scan_define([{'(',_Lp},{Type,_Lm,M}=Mac,{',',Lc}|Toks], _Def, From, St)
+scan_define([{'(',_Lp},{Type,_Lm,_}=Mac|Toks], Def, From, St)
when Type =:= atom; Type =:= var ->
- case catch macro_expansion(Toks, Lc) of
+ scan_define_1(Toks, Mac, Def, From, St);
+scan_define(_Toks, Def, From, St) ->
+ epp_reply(From, {error,{loc(Def),epp,{bad,define}}}),
+ wait_req_scan(St).
+
+scan_define_1([{',',_}=Comma|Toks], Mac,_Def, From, St) ->
+ case catch macro_expansion(Toks, Comma) of
Expansion when is_list(Expansion) ->
- case dict:find({atom,M}, St#epp.macs) of
- {ok, Defs} when is_list(Defs) ->
- %% User defined macros: can be overloaded
- case proplists:is_defined(none, Defs) of
- true ->
- epp_reply(From, {error,{loc(Mac),epp,{redefine,M}}}),
- wait_req_scan(St);
- false ->
- scan_define_cont(From, St,
- {atom, M},
- {none, {none,Expansion}})
- end;
- {ok, _PreDef} ->
- %% Predefined macros: cannot be overloaded
- epp_reply(From, {error,{loc(Mac),epp,{redefine_predef,M}}}),
- wait_req_scan(St);
- error ->
- scan_define_cont(From, St,
- {atom, M},
- {none, {none,Expansion}})
- end;
+ scan_define_2(none, {none,Expansion}, Mac, From, St);
{error,ErrL,What} ->
epp_reply(From, {error,{ErrL,epp,What}}),
wait_req_scan(St)
end;
-scan_define([{'(',_Lp},{Type,_Lm,M}=Mac,{'(',_Lc}|Toks], Def, From, St)
- when Type =:= atom; Type =:= var ->
+scan_define_1([{'(',_Lc}|Toks], Mac, Def, From, St) ->
case catch macro_pars(Toks, []) of
- {ok, {As,Me}} ->
+ {ok,{As,_}=MacroDef} ->
Len = length(As),
- case dict:find({atom,M}, St#epp.macs) of
- {ok, Defs} when is_list(Defs) ->
- %% User defined macros: can be overloaded
- case proplists:is_defined(Len, Defs) of
- true ->
- epp_reply(From,{error,{loc(Mac),epp,{redefine,M}}}),
- wait_req_scan(St);
- false ->
- scan_define_cont(From, St, {atom, M},
- {Len, {As, Me}})
- end;
- {ok, _PreDef} ->
- %% Predefined macros: cannot be overloaded
- %% (There are currently no predefined F(...) macros.)
- epp_reply(From, {error,{loc(Mac),epp,{redefine_predef,M}}}),
- wait_req_scan(St);
- error ->
- scan_define_cont(From, St, {atom, M}, {Len, {As, Me}})
- end;
+ scan_define_2(Len, MacroDef, Mac, From, St);
{error,ErrL,What} ->
epp_reply(From, {error,{ErrL,epp,What}}),
wait_req_scan(St);
@@ -878,10 +870,29 @@ scan_define([{'(',_Lp},{Type,_Lm,M}=Mac,{'(',_Lc}|Toks], Def, From, St)
epp_reply(From, {error,{loc(Def),epp,{bad,define}}}),
wait_req_scan(St)
end;
-scan_define(_Toks, Def, From, St) ->
+scan_define_1(_Toks, _Mac, Def, From, St) ->
epp_reply(From, {error,{loc(Def),epp,{bad,define}}}),
wait_req_scan(St).
+scan_define_2(Arity, Def, {_,_,Key}=Mac, From, #epp{macs=Ms}=St) ->
+ case Ms of
+ #{Key:=Defs} when is_list(Defs) ->
+ %% User defined macros: can be overloaded
+ case proplists:is_defined(Arity, Defs) of
+ true ->
+ epp_reply(From, {error,{loc(Mac),epp,{redefine,Key}}}),
+ wait_req_scan(St);
+ false ->
+ scan_define_cont(From, St, Key, Defs, Arity, Def)
+ end;
+ #{Key:=_} ->
+ %% Predefined macros: cannot be overloaded
+ epp_reply(From, {error,{loc(Mac),epp,{redefine_predef,Key}}}),
+ wait_req_scan(St);
+ _ ->
+ scan_define_cont(From, St, Key, [], Arity, Def)
+ end.
+
%%% Detection of circular macro expansions (which would either keep
%%% the compiler looping forever, or run out of memory):
%%% When a macro is defined, we store the names of other macros it
@@ -891,11 +902,17 @@ scan_define(_Toks, Def, From, St) ->
%%% the information from St#epp.uses is traversed, and if a circularity
%%% is detected, an error message is thrown.
-scan_define_cont(F, St, M, {Arity, Def}) ->
- Ms = dict:append_list(M, [{Arity, Def}], St#epp.macs),
- try dict:append_list(M, [{Arity, macro_uses(Def)}], St#epp.uses) of
+scan_define_cont(F, #epp{macs=Ms0}=St, M, Defs, Arity, Def) ->
+ Ms = Ms0#{M=>[{Arity,Def}|Defs]},
+ try macro_uses(Def) of
U ->
- scan_toks(F, St#epp{uses=U, macs=Ms})
+ Uses0 = St#epp.uses,
+ Val = [{Arity,U}|case Uses0 of
+ #{M:=UseList} -> UseList;
+ _ -> []
+ end],
+ Uses = Uses0#{M=>Val},
+ scan_toks(F, St#epp{uses=Uses,macs=Ms})
catch
{error, Line, Reason} ->
epp_reply(F, {error,{Line,epp,Reason}}),
@@ -910,24 +927,26 @@ macro_ref([]) ->
[];
macro_ref([{'?', _}, {'?', _} | Rest]) ->
macro_ref(Rest);
-macro_ref([{'?', _}, {atom, Lm, A} | Rest]) ->
+macro_ref([{'?', _}, {atom, _, A}=Atom | Rest]) ->
+ Lm = loc(Atom),
Arity = count_args(Rest, Lm, A),
- [{{atom, A}, Arity} | macro_ref(Rest)];
-macro_ref([{'?', _}, {var, Lm, A} | Rest]) ->
+ [{A,Arity} | macro_ref(Rest)];
+macro_ref([{'?', _}, {var, _, A}=Var | Rest]) ->
+ Lm = loc(Var),
Arity = count_args(Rest, Lm, A),
- [{{atom, A}, Arity} | macro_ref(Rest)];
+ [{A,Arity} | macro_ref(Rest)];
macro_ref([_Token | Rest]) ->
macro_ref(Rest).
%% scan_undef(Tokens, UndefToken, From, EppState)
scan_undef([{'(',_Llp},{atom,_Lm,M},{')',_Lrp},{dot,_Ld}], _Undef, From, St) ->
- Macs = dict:erase({atom,M}, St#epp.macs),
- Uses = dict:erase({atom,M}, St#epp.uses),
+ Macs = maps:remove(M, St#epp.macs),
+ Uses = maps:remove(M, St#epp.uses),
scan_toks(From, St#epp{macs=Macs, uses=Uses});
scan_undef([{'(',_Llp},{var,_Lm,M},{')',_Lrp},{dot,_Ld}], _Undef, From,St) ->
- Macs = dict:erase({atom,M}, St#epp.macs),
- Uses = dict:erase({atom,M}, St#epp.uses),
+ Macs = maps:remove(M, St#epp.macs),
+ Uses = maps:remove(M, St#epp.uses),
scan_toks(From, St#epp{macs=Macs, uses=Uses});
scan_undef(_Toks, Undef, From, St) ->
epp_reply(From, {error,{loc(Undef),epp,{bad,undef}}}),
@@ -940,7 +959,7 @@ scan_include([{'(',_Llp},{string,_Lf,NewName0},{')',_Lrp},{dot,_Ld}], Inc,
NewName = expand_var(NewName0),
enter_file(NewName, Inc, From, St);
scan_include(_Toks, Inc, From, St) ->
- epp_reply(From, {error,{abs_loc(Inc),epp,{bad,include}}}),
+ epp_reply(From, {error,{loc(Inc),epp,{bad,include}}}),
wait_req_scan(St).
%% scan_include_lib(Tokens, IncludeToken, From, EppState)
@@ -948,14 +967,20 @@ scan_include(_Toks, Inc, From, St) ->
%% normal search path, if not we assume that the first directory name
%% is a library name, find its true directory and try with that.
-find_lib_dir(NewName) ->
- [Lib | Rest] = filename:split(NewName),
- {code:lib_dir(list_to_atom(Lib)), Rest}.
+expand_lib_dir(Name) ->
+ try
+ [App|Path] = filename:split(Name),
+ LibDir = code:lib_dir(list_to_atom(App)),
+ {ok,fname_join([LibDir|Path])}
+ catch
+ _:_ ->
+ error
+ end.
scan_include_lib([{'(',_Llp},{string,_Lf,_NewName0},{')',_Lrp},{dot,_Ld}],
Inc, From, St)
when length(St#epp.sstk) >= 8 ->
- epp_reply(From, {error,{abs_loc(Inc),epp,{depth,"include_lib"}}}),
+ epp_reply(From, {error,{loc(Inc),epp,{depth,"include_lib"}}}),
wait_req_scan(St);
scan_include_lib([{'(',_Llp},{string,_Lf,NewName0},{')',_Lrp},{dot,_Ld}],
Inc, From, St) ->
@@ -965,27 +990,26 @@ scan_include_lib([{'(',_Llp},{string,_Lf,NewName0},{')',_Lrp},{dot,_Ld}],
{ok,NewF,Pname} ->
wait_req_scan(enter_file2(NewF, Pname, From, St, Loc));
{error,_E1} ->
- case catch find_lib_dir(NewName) of
- {LibDir, Rest} when is_list(LibDir) ->
- LibName = fname_join([LibDir | Rest]),
- case file:open(LibName, [read]) of
+ case expand_lib_dir(NewName) of
+ {ok,Header} ->
+ case file:open(Header, [read]) of
{ok,NewF} ->
- wait_req_scan(enter_file2(NewF, LibName, From,
+ wait_req_scan(enter_file2(NewF, Header, From,
St, Loc));
{error,_E2} ->
epp_reply(From,
- {error,{abs_loc(Inc),epp,
+ {error,{loc(Inc),epp,
{include,lib,NewName}}}),
wait_req_scan(St)
end;
- _Error ->
- epp_reply(From, {error,{abs_loc(Inc),epp,
+ error ->
+ epp_reply(From, {error,{loc(Inc),epp,
{include,lib,NewName}}}),
wait_req_scan(St)
end
end;
scan_include_lib(_Toks, Inc, From, St) ->
- epp_reply(From, {error,{abs_loc(Inc),epp,{bad,include_lib}}}),
+ epp_reply(From, {error,{loc(Inc),epp,{bad,include_lib}}}),
wait_req_scan(St).
%% scan_ifdef(Tokens, IfdefToken, From, EppState)
@@ -994,17 +1018,17 @@ scan_include_lib(_Toks, Inc, From, St) ->
%% Report a badly formed if[n]def test and then treat as undefined macro.
scan_ifdef([{'(',_Llp},{atom,_Lm,M},{')',_Lrp},{dot,_Ld}], _IfD, From, St) ->
- case dict:find({atom,M}, St#epp.macs) of
- {ok,_Def} ->
+ case St#epp.macs of
+ #{M:=_Def} ->
scan_toks(From, St#epp{istk=[ifdef|St#epp.istk]});
- error ->
+ _ ->
skip_toks(From, St, [ifdef])
end;
scan_ifdef([{'(',_Llp},{var,_Lm,M},{')',_Lrp},{dot,_Ld}], _IfD, From, St) ->
- case dict:find({atom,M}, St#epp.macs) of
- {ok,_Def} ->
+ case St#epp.macs of
+ #{M:=_Def} ->
scan_toks(From, St#epp{istk=[ifdef|St#epp.istk]});
- error ->
+ _ ->
skip_toks(From, St, [ifdef])
end;
scan_ifdef(_Toks, IfDef, From, St) ->
@@ -1012,17 +1036,17 @@ scan_ifdef(_Toks, IfDef, From, St) ->
wait_req_skip(St, [ifdef]).
scan_ifndef([{'(',_Llp},{atom,_Lm,M},{')',_Lrp},{dot,_Ld}], _IfnD, From, St) ->
- case dict:find({atom,M}, St#epp.macs) of
- {ok,_Def} ->
+ case St#epp.macs of
+ #{M:=_Def} ->
skip_toks(From, St, [ifndef]);
- error ->
+ _ ->
scan_toks(From, St#epp{istk=[ifndef|St#epp.istk]})
end;
scan_ifndef([{'(',_Llp},{var,_Lm,M},{')',_Lrp},{dot,_Ld}], _IfnD, From, St) ->
- case dict:find({atom,M}, St#epp.macs) of
- {ok,_Def} ->
+ case St#epp.macs of
+ #{M:=_Def} ->
skip_toks(From, St, [ifndef]);
- error ->
+ _ ->
scan_toks(From, St#epp{istk=[ifndef|St#epp.istk]})
end;
scan_ifndef(_Toks, IfnDef, From, St) ->
@@ -1088,11 +1112,13 @@ scan_endif(_Toks, Endif, From, St) ->
scan_file([{'(',_Llp},{string,_Ls,Name},{',',_Lc},{integer,_Li,Ln},{')',_Lrp},
{dot,_Ld}], Tf, From, St) ->
- enter_file_reply(From, Name, Ln, neg_line(abs_loc(Tf))),
- Ms = dict:store({atom,'FILE'}, {none,[{string,1,Name}]}, St#epp.macs),
+ Anno = erl_anno:new(Ln),
+ enter_file_reply(From, Name, Anno, loc(Tf), generated),
+ Ms0 = St#epp.macs,
+ Ms = Ms0#{'FILE':={none,[{string,line1(),Name}]}},
Locf = loc(Tf),
NewLoc = new_location(Ln, St#epp.location, Locf),
- Delta = abs(get_line(element(2, Tf)))-Ln + St#epp.delta,
+ Delta = get_line(element(2, Tf))-Ln + St#epp.delta,
wait_req_scan(St#epp{name2=Name,location=NewLoc,delta=Delta,macs=Ms});
scan_file(_Toks, Tf, From, St) ->
epp_reply(From, {error,{loc(Tf),epp,{bad,file}}}),
@@ -1153,7 +1179,7 @@ skip_else(_Else, From, St, Sis) ->
skip_toks(From, St, Sis).
%% macro_pars(Tokens, ArgStack)
-%% macro_expansion(Tokens, Line)
+%% macro_expansion(Tokens, Anno)
%% Extract the macro parameters and the expansion from a macro definition.
macro_pars([{')',_Lp}, {',',Ld}|Ex], Args) ->
@@ -1165,51 +1191,54 @@ macro_pars([{var,_L,Name}, {',',_}|Ts], Args) ->
false = lists:member(Name, Args),
macro_pars(Ts, [Name|Args]).
-macro_expansion([{')',_Lp},{dot,_Ld}], _L0) -> [];
-macro_expansion([{dot,Ld}], _L0) -> throw({error,Ld,missing_parenthesis});
-macro_expansion([T|Ts], _L0) ->
- [T|macro_expansion(Ts, element(2, T))];
-macro_expansion([], L0) -> throw({error,L0,premature_end}).
+macro_expansion([{')',_Lp},{dot,_Ld}], _Anno0) -> [];
+macro_expansion([{dot,_}=Dot], _Anno0) ->
+ throw({error,loc(Dot),missing_parenthesis});
+macro_expansion([T|Ts], _Anno0) ->
+ [T|macro_expansion(Ts, T)];
+macro_expansion([], Anno0) -> throw({error,loc(Anno0),premature_end}).
-%% expand_macros(Tokens, Macros)
+%% expand_macros(Tokens, St)
%% expand_macro(Tokens, MacroToken, RestTokens)
%% Expand the macros in a list of tokens, making sure that an expansion
%% gets the same location as the macro call.
-expand_macros(Type, MacT, M, Toks, Ms0) ->
- %% (Type will always be 'atom')
- {Ms, U} = Ms0,
+expand_macros(MacT, M, Toks, St) ->
+ #epp{macs=Ms,uses=U} = St,
Lm = loc(MacT),
Tinfo = element(2, MacT),
- case expand_macro1(Type, Lm, M, Toks, Ms) of
+ case expand_macro1(Lm, M, Toks, Ms) of
{ok,{none,Exp}} ->
- check_uses([{{Type,M}, none}], [], U, Lm),
- Toks1 = expand_macros(expand_macro(Exp, Tinfo, [], dict:new()), Ms0),
- expand_macros(Toks1++Toks, Ms0);
+ check_uses([{M,none}], [], U, Lm),
+ Toks1 = expand_macros(expand_macro(Exp, Tinfo, [], #{}), St),
+ expand_macros(Toks1++Toks, St);
{ok,{As,Exp}} ->
- check_uses([{{Type,M}, length(As)}], [], U, Lm),
- {Bs,Toks1} = bind_args(Toks, Lm, M, As, dict:new()),
- expand_macros(expand_macro(Exp, Tinfo, Toks1, Bs), Ms0)
+ check_uses([{M,length(As)}], [], U, Lm),
+ {Bs,Toks1} = bind_args(Toks, Lm, M, As, #{}),
+ expand_macros(expand_macro(Exp, Tinfo, Toks1, Bs), St)
end.
-expand_macro1(Type, Lm, M, Toks, Ms) ->
+expand_macro1(Lm, M, Toks, Ms) ->
Arity = count_args(Toks, Lm, M),
- case dict:find({Type,M}, Ms) of
- error -> %% macro not found
+ case Ms of
+ #{M:=undefined} ->
+ %% Predefined macro without definition.
throw({error,Lm,{undefined,M,Arity}});
- {ok, undefined} -> %% Predefined macro without definition
- throw({error,Lm,{undefined,M,Arity}});
- {ok, [{none, Def}]} ->
- {ok, Def};
- {ok, Defs} when is_list(Defs) ->
- case proplists:get_value(Arity, Defs) of
+ #{M:=[{none,Def}]} ->
+ {ok,Def};
+ #{M:=Defs} when is_list(Defs) ->
+ case proplists:get_value(Arity, Defs) of
undefined ->
throw({error,Lm,{mismatch,M}});
Def ->
- {ok, Def}
+ {ok,Def}
end;
- {ok, PreDef} -> %% Predefined macro
- {ok, PreDef}
+ #{M:=PreDef} ->
+ %% Predefined macro.
+ {ok,PreDef};
+ _ ->
+ %% Macro not found.
+ throw({error,Lm,{undefined,M,Arity}})
end.
check_uses([], _Anc, _U, _Lm) ->
@@ -1217,7 +1246,7 @@ check_uses([], _Anc, _U, _Lm) ->
check_uses([M|Rest], Anc, U, Lm) ->
case lists:member(M, Anc) of
true ->
- {{_, Name},Arity} = M,
+ {Name,Arity} = M,
throw({error,Lm,{circular,Name,Arity}});
false ->
L = get_macro_uses(M, U),
@@ -1226,36 +1255,52 @@ check_uses([M|Rest], Anc, U, Lm) ->
end.
get_macro_uses({M,Arity}, U) ->
- case dict:find(M, U) of
- error ->
- [];
- {ok, L} ->
- proplists:get_value(Arity, L, proplists:get_value(none, L, []))
+ case U of
+ #{M:=L} ->
+ proplists:get_value(Arity, L, proplists:get_value(none, L, []));
+ _ ->
+ []
end.
%% Macro expansion
%% Note: io:scan_erl_form() does not return comments or white spaces.
-expand_macros([{'?',_Lq},{atom,_Lm,M}=MacT|Toks], Ms) ->
- expand_macros(atom, MacT, M, Toks, Ms);
+expand_macros([{'?',_Lq},{atom,_Lm,M}=MacT|Toks], St) ->
+ expand_macros(MacT, M, Toks, St);
%% Special macros
-expand_macros([{'?',_Lq},{var,Lm,'LINE'}=Tok|Toks], Ms) ->
- {line,Line} = erl_scan:token_info(Tok, line),
- [{integer,Lm,Line}|expand_macros(Toks, Ms)];
-expand_macros([{'?',_Lq},{var,_Lm,M}=MacT|Toks], Ms) ->
- expand_macros(atom, MacT, M, Toks, Ms);
+expand_macros([{'?',_Lq},{var,Lm,'FUNCTION_NAME'}=Token|Toks], St0) ->
+ St = update_fun_name(Token, St0),
+ case St#epp.fname of
+ undefined ->
+ [{'?',_Lq},Token];
+ {Name,_} ->
+ [{atom,Lm,Name}]
+ end ++ expand_macros(Toks, St);
+expand_macros([{'?',_Lq},{var,Lm,'FUNCTION_ARITY'}=Token|Toks], St0) ->
+ St = update_fun_name(Token, St0),
+ case St#epp.fname of
+ undefined ->
+ [{'?',_Lq},Token];
+ {_,Arity} ->
+ [{integer,Lm,Arity}]
+ end ++ expand_macros(Toks, St);
+expand_macros([{'?',_Lq},{var,Lm,'LINE'}=Tok|Toks], St) ->
+ Line = erl_scan:line(Tok),
+ [{integer,Lm,Line}|expand_macros(Toks, St)];
+expand_macros([{'?',_Lq},{var,_Lm,M}=MacT|Toks], St) ->
+ expand_macros(MacT, M, Toks, St);
%% Illegal macros
-expand_macros([{'?',_Lq},Token|_Toks], _Ms) ->
- T = case erl_scan:token_info(Token, text) of
- {text,Text} ->
+expand_macros([{'?',_Lq},Token|_Toks], _St) ->
+ T = case erl_scan:text(Token) of
+ Text when is_list(Text) ->
Text;
undefined ->
- {symbol,Symbol} = erl_scan:token_info(Token, symbol),
+ Symbol = erl_scan:symbol(Token),
io_lib:write(Symbol)
end,
throw({error,loc(Token),{call,[$?|T]}});
-expand_macros([T|Ts], Ms) ->
- [T|expand_macros(Ts, Ms)];
-expand_macros([], _Ms) -> [].
+expand_macros([T|Ts], St) ->
+ [T|expand_macros(Ts, St)];
+expand_macros([], _St) -> [].
%% bind_args(Tokens, MacroLocation, MacroName, ArgumentVars, Bindings)
%% Collect the arguments to a macro call.
@@ -1281,7 +1326,7 @@ macro_args(_Toks, Lm, M, _As, _Bs) ->
store_arg(L, M, _A, [], _Bs) ->
throw({error,L,{mismatch,M}});
store_arg(_L, _M, A, Arg, Bs) ->
- dict:store(A, Arg, Bs).
+ Bs#{A=>Arg}.
%% count_args(Tokens, MacroLine, MacroName)
%% Count the number of arguments in a macro call.
@@ -1354,19 +1399,17 @@ macro_arg([], _E, Arg) ->
%% and then the macro arguments, i.e. simulate textual expansion.
expand_macro([{var,_Lv,V}|Ts], L, Rest, Bs) ->
- case dict:find(V, Bs) of
- {ok,Val} ->
- %% lists:append(Val, expand_macro(Ts, L, Rest, Bs));
+ case Bs of
+ #{V:=Val} ->
expand_arg(Val, Ts, L, Rest, Bs);
- error ->
+ _ ->
[{var,L,V}|expand_macro(Ts, L, Rest, Bs)]
end;
expand_macro([{'?', _}, {'?', _}, {var,_Lv,V}|Ts], L, Rest, Bs) ->
- case dict:find(V, Bs) of
- {ok,Val} ->
- %% lists:append(Val, expand_macro(Ts, L, Rest, Bs));
+ case Bs of
+ #{V:=Val} ->
expand_arg(stringify(Val, L), Ts, L, Rest, Bs);
- error ->
+ _ ->
[{var,L,V}|expand_macro(Ts, L, Rest, Bs)]
end;
expand_macro([T|Ts], L, Rest, Bs) ->
@@ -1380,10 +1423,97 @@ expand_arg([A|As], Ts, _L, Rest, Bs) ->
expand_arg([], Ts, L, Rest, Bs) ->
expand_macro(Ts, L, Rest, Bs).
+%%%
+%%% Here follows support for the ?FUNCTION_NAME and ?FUNCTION_ARITY
+%%% macros. Since the parser has not been run yet, we don't know the
+%%% name and arity of the current function. Therefore, we will need to
+%%% scan the beginning of the current form to extract the name and
+%%% arity of the function.
+%%%
+
+update_fun_name(Token, #epp{fname=Toks0}=St) when is_list(Toks0) ->
+ %% ?FUNCTION_NAME or ?FUNCTION_ARITY is used for the first time in
+ %% a function. First expand macros (except ?FUNCTION_NAME and
+ %% ?FUNCTION_ARITY) in the form.
+
+ Toks1 = (catch expand_macros(Toks0, St#epp{fname=undefined})),
+
+ %% Now extract the name and arity from the stream of tokens, and store
+ %% the result in the #epp{} record so we don't have to do it
+ %% again.
+
+ case Toks1 of
+ [{atom,_,Name},{'(',_}|Toks] ->
+ %% This is the beginning of a function definition.
+ %% Scan the token stream up to the matching right
+ %% parenthesis and count the number of arguments.
+ FA = update_fun_name_1(Toks, 1, {Name,0}, St),
+ St#epp{fname=FA};
+ [{'?',_}|_] ->
+ %% ?FUNCTION_NAME/?FUNCTION_ARITY used at the beginning
+ %% of a form. Does not make sense.
+ {var,_,Macro} = Token,
+ throw({error,loc(Token),{illegal_function_usage,Macro}});
+ _ when is_list(Toks1) ->
+ %% Not the beginning of a function (an attribute or a
+ %% syntax error).
+ {var,_,Macro} = Token,
+ throw({error,loc(Token),{illegal_function,Macro}});
+ _ ->
+ %% A macro expansion error. Return a dummy value and
+ %% let the caller notice and handle the error.
+ St#epp{fname={'_',0}}
+ end;
+update_fun_name(_Token, St) ->
+ St.
+
+update_fun_name_1([Tok|Toks], L, FA, St) ->
+ case classify_token(Tok) of
+ comma ->
+ if
+ L =:= 1 ->
+ {Name,Arity} = FA,
+ update_fun_name_1(Toks, L, {Name,Arity+1}, St);
+ true ->
+ update_fun_name_1(Toks, L, FA, St)
+ end;
+ left ->
+ update_fun_name_1(Toks, L+1, FA, St);
+ right when L =:= 1 ->
+ FA;
+ right ->
+ update_fun_name_1(Toks, L-1, FA, St);
+ other ->
+ case FA of
+ {Name,0} ->
+ update_fun_name_1(Toks, L, {Name,1}, St);
+ {_,_} ->
+ update_fun_name_1(Toks, L, FA, St)
+ end
+ end;
+update_fun_name_1([], _, FA, _) ->
+ %% Syntax error, but never mind.
+ FA.
+
+classify_token({C,_}) -> classify_token_1(C);
+classify_token(_) -> other.
+
+classify_token_1(',') -> comma;
+classify_token_1('(') -> left;
+classify_token_1('{') -> left;
+classify_token_1('[') -> left;
+classify_token_1('<<') -> left;
+classify_token_1(')') -> right;
+classify_token_1('}') -> right;
+classify_token_1(']') -> right;
+classify_token_1('>>') -> right;
+classify_token_1(_) -> other.
+
+
%%% stringify(Ts, L) returns a list of one token: a string which when
%%% tokenized would yield the token list Ts.
-%% erl_scan:token_info(T, text) is not backward compatible with this.
+%% erl_scan:text(T) is not backward compatible with this.
%% Note that escaped characters will be replaced by themselves.
token_src({dot, _}) ->
".";
@@ -1456,36 +1586,29 @@ fname_join(Components) ->
filename:join(Components).
%% The line only. (Other tokens may have the column and text as well...)
-loc_attr(Line) when is_integer(Line) ->
- Line;
-loc_attr({Line,_Column}) ->
- Line.
+loc_anno(Line) when is_integer(Line) ->
+ erl_anno:new(Line);
+loc_anno({Line,_Column}) ->
+ erl_anno:new(Line).
loc(Token) ->
- {location,Location} = erl_scan:token_info(Token, location),
- Location.
-
-abs_loc(Token) ->
- loc(setelement(2, Token, abs_line(element(2, Token)))).
-
-neg_line(L) ->
- erl_scan:set_attribute(line, L, fun(Line) -> -abs(Line) end).
+ erl_scan:location(Token).
-abs_line(L) ->
- erl_scan:set_attribute(line, L, fun(Line) -> abs(Line) end).
-
-add_line(L, Offset) ->
- erl_scan:set_attribute(line, L, fun(Line) -> Line+Offset end).
+add_line(Line, Offset) when is_integer(Line) ->
+ Line+Offset;
+add_line({Line, Column}, Offset) ->
+ {Line+Offset, Column}.
start_loc(Line) when is_integer(Line) ->
1;
start_loc({_Line, _Column}) ->
- {1,1}.
+ {1, 1}.
+
+line1() ->
+ erl_anno:new(1).
-get_line(Line) when is_integer(Line) ->
- Line;
-get_line({Line,_Column}) ->
- Line.
+get_line(Anno) ->
+ erl_anno:line(Anno).
%% epp has always output -file attributes when entering and leaving
%% included files (-include, -include_lib). Starting with R11B the
@@ -1525,14 +1648,15 @@ get_line({Line,_Column}) ->
interpret_file_attribute(Forms) ->
interpret_file_attr(Forms, 0, []).
-interpret_file_attr([{attribute,Loc,file,{File,Line}}=Form | Forms],
+interpret_file_attr([{attribute,Anno,file,{File,Line}}=Form | Forms],
Delta, Fs) ->
- {line, L} = erl_scan:attributes_info(Loc, line),
+ L = get_line(Anno),
+ Generated = erl_anno:generated(Anno),
if
- L < 0 ->
+ Generated ->
%% -file attribute
- interpret_file_attr(Forms, (abs(L) + Delta) - Line, Fs);
- true ->
+ interpret_file_attr(Forms, (L + Delta) - Line, Fs);
+ not Generated ->
%% -include or -include_lib
% true = L =:= Line,
case Fs of
@@ -1543,11 +1667,11 @@ interpret_file_attr([{attribute,Loc,file,{File,Line}}=Form | Forms],
end
end;
interpret_file_attr([Form0 | Forms], Delta, Fs) ->
- F = fun(Attrs) ->
- F2 = fun(L) -> abs(L) + Delta end,
- erl_scan:set_attribute(line, Attrs, F2)
+ F = fun(Anno) ->
+ Line = erl_anno:line(Anno),
+ erl_anno:set_line(Line + Delta, Anno)
end,
- Form = erl_lint:modify_line(Form0, F),
+ Form = erl_parse:map_anno(F, Form0),
[Form | interpret_file_attr(Forms, Delta, Fs)];
interpret_file_attr([], _Delta, _Fs) ->
[].
diff --git a/lib/stdlib/src/erl_anno.erl b/lib/stdlib/src/erl_anno.erl
new file mode 100644
index 0000000000..d32c34dabd
--- /dev/null
+++ b/lib/stdlib/src/erl_anno.erl
@@ -0,0 +1,405 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-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%
+%%
+
+-module(erl_anno).
+
+-export([new/1, is_anno/1]).
+-export([column/1, end_location/1, file/1, generated/1,
+ line/1, location/1, record/1, text/1]).
+-export([set_file/2, set_generated/2, set_line/2, set_location/2,
+ set_record/2, set_text/2]).
+
+%% To be used when necessary to avoid Dialyzer warnings.
+-export([to_term/1, from_term/1]).
+
+-export_type([anno/0, line/0, column/0, location/0, text/0]).
+
+-export_type([anno_term/0]).
+
+-define(LN(L), is_integer(L), L >= 0).
+-define(COL(C), (is_integer(C) andalso C >= 1)).
+
+%% Location.
+-define(LCOLUMN(C), ?COL(C)).
+-define(LLINE(L), ?LN(L)).
+
+%% Debug: define DEBUG to make sure that annotations are handled as an
+%% opaque type. Note that all abstract code need to be compiled with
+%% DEBUG=true. See also ./erl_pp.erl.
+
+%-define(DEBUG, true).
+
+-type annotation() :: {'file', filename()}
+ | {'generated', generated()}
+ | {'location', location()}
+ | {'record', record()}
+ | {'text', string()}.
+
+-opaque anno() :: location() | [annotation(), ...].
+-type anno_term() :: term().
+
+-type column() :: pos_integer().
+-type generated() :: boolean().
+-type filename() :: file:filename_all().
+-type line() :: non_neg_integer().
+-type location() :: line() | {line(), column()}.
+-type record() :: boolean().
+-type text() :: string().
+
+-ifdef(DEBUG).
+%% Anything 'false' accepted by the compiler.
+-define(ALINE(A), is_reference(A)).
+-define(ACOLUMN(A), is_reference(A)).
+-else.
+-define(ALINE(L), ?LN(L)).
+-define(ACOLUMN(C), ?COL(C)).
+-endif.
+
+-spec to_term(Anno) -> anno_term() when
+ Anno :: anno().
+
+-ifdef(DEBUG).
+to_term(Anno) ->
+ simplify(Anno).
+-else.
+to_term(Anno) ->
+ Anno.
+-endif.
+
+-spec from_term(Term) -> Anno when
+ Term :: anno_term(),
+ Anno :: anno().
+
+-ifdef(DEBUG).
+from_term(Term) when is_list(Term) ->
+ Term;
+from_term(Line) when is_integer(Line), Line < 0 -> % Before OTP 19
+ set_generated(true, new(-Line));
+from_term(Term) ->
+ [{location, Term}].
+-else.
+from_term(Line) when is_integer(Line), Line < 0 -> % Before OTP 19
+ set_generated(true, new(-Line));
+from_term(Term) ->
+ Term.
+-endif.
+
+-spec new(Location) -> anno() when
+ Location :: location().
+
+new(Line) when ?LLINE(Line) ->
+ new_location(Line);
+new({Line, Column}=Loc) when ?LLINE(Line), ?LCOLUMN(Column) ->
+ new_location(Loc);
+new(Term) ->
+ erlang:error(badarg, [Term]).
+
+-ifdef(DEBUG).
+new_location(Location) ->
+ [{location, Location}].
+-else.
+new_location(Location) ->
+ Location.
+-endif.
+
+-spec is_anno(Term) -> boolean() when
+ Term :: any().
+
+is_anno(Line) when ?ALINE(Line) ->
+ true;
+is_anno({Line, Column}) when ?ALINE(Line), ?ACOLUMN(Column) ->
+ true;
+is_anno(Anno) ->
+ (Anno =/= [] andalso
+ is_anno1(Anno) andalso
+ lists:keymember(location, 1, Anno)).
+
+is_anno1([{Item, Value}|Anno]) ->
+ is_anno2(Item, Value) andalso is_anno1(Anno);
+is_anno1(A) ->
+ A =:= [].
+
+is_anno2(location, Line) when ?LN(Line) ->
+ true;
+is_anno2(location, {Line, Column}) when ?LN(Line), ?COL(Column) ->
+ true;
+is_anno2(generated, true) ->
+ true;
+is_anno2(file, Filename) ->
+ is_filename(Filename);
+is_anno2(record, true) ->
+ true;
+is_anno2(text, Text) ->
+ is_string(Text);
+is_anno2(_, _) ->
+ false.
+
+is_filename(T) ->
+ is_list(T) orelse is_binary(T).
+
+is_string(T) ->
+ is_list(T).
+
+-spec column(Anno) -> column() | 'undefined' when
+ Anno :: anno().
+
+column({Line, Column}) when ?ALINE(Line), ?ACOLUMN(Column) ->
+ Column;
+column(Line) when ?ALINE(Line) ->
+ undefined;
+column(Anno) ->
+ case location(Anno) of
+ {_Line, Column} ->
+ Column;
+ _Line ->
+ undefined
+ end.
+
+-spec end_location(Anno) -> location() | 'undefined' when
+ Anno :: anno().
+
+end_location(Anno) ->
+ case text(Anno) of
+ undefined ->
+ undefined;
+ Text ->
+ case location(Anno) of
+ {Line, Column} ->
+ end_location(Text, Line, Column);
+ Line ->
+ end_location(Text, Line)
+ end
+ end.
+
+-spec file(Anno) -> filename() | 'undefined' when
+ Anno :: anno().
+
+file(Line) when ?ALINE(Line) ->
+ undefined;
+file({Line, Column}) when ?ALINE(Line), ?ACOLUMN(Column) ->
+ undefined;
+file(Anno) ->
+ anno_info(Anno, file).
+
+-spec generated(Anno) -> generated() when
+ Anno :: anno().
+
+generated(Line) when ?ALINE(Line) ->
+ false;
+generated({Line, Column}) when ?ALINE(Line), ?ACOLUMN(Column) ->
+ false;
+generated(Anno) ->
+ anno_info(Anno, generated, false).
+
+-spec line(Anno) -> line() when
+ Anno :: anno().
+
+line(Anno) ->
+ case location(Anno) of
+ {Line, _Column} ->
+ Line;
+ Line ->
+ Line
+ end.
+
+-spec location(Anno) -> location() when
+ Anno :: anno().
+
+location(Line) when ?ALINE(Line) ->
+ Line;
+location({Line, Column}=Location) when ?ALINE(Line), ?ACOLUMN(Column) ->
+ Location;
+location(Anno) ->
+ anno_info(Anno, location).
+
+-spec record(Anno) -> record() when
+ Anno :: anno().
+
+record(Line) when ?ALINE(Line) ->
+ false;
+record({Line, Column}) when ?ALINE(Line), ?ACOLUMN(Column) ->
+ false;
+record(Anno) ->
+ anno_info(Anno, record, false).
+
+-spec text(Anno) -> text() | 'undefined' when
+ Anno :: anno().
+
+text(Line) when ?ALINE(Line) ->
+ undefined;
+text({Line, Column}) when ?ALINE(Line), ?ACOLUMN(Column) ->
+ undefined;
+text(Anno) ->
+ anno_info(Anno, text).
+
+-spec set_file(File, Anno) -> Anno when
+ File :: filename(),
+ Anno :: anno().
+
+set_file(File, Anno) ->
+ set(file, File, Anno).
+
+-spec set_generated(Generated, Anno) -> Anno when
+ Generated :: generated(),
+ Anno :: anno().
+
+set_generated(Generated, Anno) ->
+ set(generated, Generated, Anno).
+
+-spec set_line(Line, Anno) -> Anno when
+ Line :: line(),
+ Anno :: anno().
+
+set_line(Line, Anno) ->
+ case location(Anno) of
+ {_Line, Column} ->
+ set_location({Line, Column}, Anno);
+ _Line ->
+ set_location(Line, Anno)
+ end.
+
+-spec set_location(Location, Anno) -> Anno when
+ Location :: location(),
+ Anno :: anno().
+
+set_location(Line, L) when ?ALINE(L), ?LLINE(Line) ->
+ new_location(Line);
+set_location(Line, {L, Column}) when ?ALINE(L), ?ACOLUMN(Column),
+ ?LLINE(Line) ->
+ new_location(Line);
+set_location({L, C}=Loc, Line) when ?ALINE(Line), ?LLINE(L), ?LCOLUMN(C) ->
+ new_location(Loc);
+set_location({L, C}=Loc, {Line, Column}) when ?ALINE(Line), ?ACOLUMN(Column),
+ ?LLINE(L), ?LCOLUMN(C) ->
+ new_location(Loc);
+set_location(Location, Anno) ->
+ set(location, Location, Anno).
+
+-spec set_record(Record, Anno) -> Anno when
+ Record :: record(),
+ Anno :: anno().
+
+set_record(Record, Anno) ->
+ set(record, Record, Anno).
+
+-spec set_text(Text, Anno) -> Anno when
+ Text :: text(),
+ Anno :: anno().
+
+set_text(Text, Anno) ->
+ set(text, Text, Anno).
+
+set(Item, Value, Anno) ->
+ case {is_settable(Item, Value), Anno} of
+ {true, Line} when ?ALINE(Line) ->
+ set_anno(Item, Value, [{location, Line}]);
+ {true, {L, C}=Location} when ?ALINE(L), ?ACOLUMN(C) ->
+ set_anno(Item, Value, [{location, Location}]);
+ {true, A} when is_list(A), A =/= [] ->
+ set_anno(Item, Value, Anno);
+ _ ->
+ erlang:error(badarg, [Item, Value, Anno])
+ end.
+
+set_anno(Item, Value, Anno) ->
+ case default(Item, Value) of
+ true ->
+ reset(Anno, Item);
+ false ->
+ R = case anno_info(Anno, Item) of
+ undefined ->
+ [{Item, Value}|Anno];
+ _ ->
+ lists:keyreplace(Item, 1, Anno, {Item, Value})
+ end,
+ reset_simplify(R)
+ end.
+
+reset(Anno, Item) ->
+ A = lists:keydelete(Item, 1, Anno),
+ reset_simplify(A).
+
+-ifdef(DEBUG).
+reset_simplify(A) ->
+ A.
+-else.
+reset_simplify(A) ->
+ simplify(A).
+-endif.
+
+simplify([{location, Location}]) ->
+ Location;
+simplify(Anno) ->
+ Anno.
+
+anno_info(Anno, Item, Default) ->
+ try lists:keyfind(Item, 1, Anno) of
+ false ->
+ Default;
+ {Item, Value} ->
+ Value
+ catch
+ _:_ ->
+ erlang:error(badarg, [Anno])
+ end.
+
+anno_info(Anno, Item) ->
+ try lists:keyfind(Item, 1, Anno) of
+ {Item, Value} ->
+ Value;
+ false ->
+ undefined
+ catch
+ _:_ ->
+ erlang:error(badarg, [Anno])
+ end.
+
+end_location("", Line, Column) ->
+ {Line, Column};
+end_location([$\n|String], Line, _Column) ->
+ end_location(String, Line+1, 1);
+end_location([_|String], Line, Column) ->
+ end_location(String, Line, Column+1).
+
+end_location("", Line) ->
+ Line;
+end_location([$\n|String], Line) ->
+ end_location(String, Line+1);
+end_location([_|String], Line) ->
+ end_location(String, Line).
+
+is_settable(file, File) ->
+ is_filename(File);
+is_settable(generated, Boolean) when Boolean; not Boolean ->
+ true;
+is_settable(location, Line) when ?LLINE(Line) ->
+ true;
+is_settable(location, {Line, Column}) when ?LLINE(Line), ?LCOLUMN(Column) ->
+ true;
+is_settable(record, Boolean) when Boolean; not Boolean ->
+ true;
+is_settable(text, Text) ->
+ is_string(Text);
+is_settable(_, _) ->
+ false.
+
+default(generated, false) -> true;
+default(record, false) -> true;
+default(_, _) -> false.
diff --git a/lib/stdlib/src/erl_bits.erl b/lib/stdlib/src/erl_bits.erl
index 62f6d00fae..5851401026 100644
--- a/lib/stdlib/src/erl_bits.erl
+++ b/lib/stdlib/src/erl_bits.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
diff --git a/lib/stdlib/src/erl_compile.erl b/lib/stdlib/src/erl_compile.erl
index caed4d41d6..ef54076ee3 100644
--- a/lib/stdlib/src/erl_compile.erl
+++ b/lib/stdlib/src/erl_compile.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2014. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
diff --git a/lib/stdlib/src/erl_eval.erl b/lib/stdlib/src/erl_eval.erl
index 639ddfc214..40a34aa30f 100644
--- a/lib/stdlib/src/erl_eval.erl
+++ b/lib/stdlib/src/erl_eval.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -246,18 +247,14 @@ expr({record,_,_,Name,_}, _Bs, _Lf, _Ef, _RBs) ->
%% map
expr({map,_,Binding,Es}, Bs0, Lf, Ef, RBs) ->
{value, Map0, Bs1} = expr(Binding, Bs0, Lf, Ef, none),
- case Map0 of
- #{} ->
- {Vs,Bs2} = eval_map_fields(Es, Bs0, Lf, Ef),
- Map1 = lists:foldl(fun ({map_assoc,K,V}, Mi) ->
- maps:put(K, V, Mi);
- ({map_exact,K,V}, Mi) ->
- maps:update(K, V, Mi)
- end, Map0, Vs),
- ret_expr(Map1, merge_bindings(Bs2, Bs1), RBs);
- _ ->
- erlang:raise(error, {badarg,Map0}, stacktrace())
- end;
+ {Vs,Bs2} = eval_map_fields(Es, Bs0, Lf, Ef),
+ _ = maps:put(k, v, Map0), %Validate map.
+ Map1 = lists:foldl(fun ({map_assoc,K,V}, Mi) ->
+ maps:put(K, V, Mi);
+ ({map_exact,K,V}, Mi) ->
+ maps:update(K, V, Mi)
+ end, Map0, Vs),
+ ret_expr(Map1, merge_bindings(Bs2, Bs1), RBs);
expr({map,_,Es}, Bs0, Lf, Ef, RBs) ->
{Vs,Bs} = eval_map_fields(Es, Bs0, Lf, Ef),
ret_expr(lists:foldl(fun
@@ -418,7 +415,7 @@ expr({call,_,{atom,_,Func},As0}, Bs0, Lf, Ef, RBs) ->
{As,Bs} = expr_list(As0, Bs0, Lf, Ef),
bif(Func, As, Bs, Ef, RBs);
false ->
- local_func(Func, As0, Bs0, Lf, RBs)
+ local_func(Func, As0, Bs0, Lf, Ef, RBs)
end;
expr({call,_,Func0,As0}, Bs0, Lf, Ef, RBs) -> % function or {Mod,Fun}
{value,Func,Bs1} = expr(Func0, Bs0, Lf, Ef, none),
@@ -483,12 +480,13 @@ expr({value,_,Val}, Bs, _Lf, _Ef, RBs) -> % Special case straight values.
find_maxline(LC) ->
put('$erl_eval_max_line', 0),
- F = fun(L) ->
+ F = fun(A) ->
+ L = erl_anno:line(A),
case is_integer(L) and (L > get('$erl_eval_max_line')) of
true -> put('$erl_eval_max_line', L);
false -> ok
end end,
- _ = erl_lint:modify_line(LC, F),
+ _ = erl_parse:map_anno(F, LC),
erase('$erl_eval_max_line').
hide_calls(LC, MaxLine) ->
@@ -498,14 +496,16 @@ hide_calls(LC, MaxLine) ->
%% v/1 and local calls are hidden.
hide({value,L,V}, Id, D) ->
- {{atom,Id,ok}, Id+1, dict:store(Id, {value,L,V}, D)};
+ A = erl_anno:new(Id),
+ {{atom,A,ok}, Id+1, dict:store(Id, {value,L,V}, D)};
hide({call,L,{atom,_,N}=Atom,Args}, Id0, D0) ->
{NArgs, Id, D} = hide(Args, Id0, D0),
C = case erl_internal:bif(N, length(Args)) of
true ->
{call,L,Atom,NArgs};
false ->
- {call,Id,{remote,L,{atom,L,m},{atom,L,f}},NArgs}
+ A = erl_anno:new(Id),
+ {call,A,{remote,L,{atom,L,m},{atom,L,f}},NArgs}
end,
{C, Id+1, dict:store(Id, {call,Atom}, D)};
hide(T0, Id0, D0) when is_tuple(T0) ->
@@ -518,11 +518,23 @@ hide([E0 | Es0], Id0, D0) ->
hide(E, Id, D) ->
{E, Id, D}.
-unhide_calls({atom,Id,ok}, MaxLine, D) when Id > MaxLine ->
- dict:fetch(Id, D);
-unhide_calls({call,Id,{remote,L,_M,_F},Args}, MaxLine, D) when Id > MaxLine ->
- {call,Atom} = dict:fetch(Id, D),
- {call,L,Atom,unhide_calls(Args, MaxLine, D)};
+unhide_calls({atom,A,ok}=E, MaxLine, D) ->
+ L = erl_anno:line(A),
+ if
+ L > MaxLine ->
+ dict:fetch(L, D);
+ true ->
+ E
+ end;
+unhide_calls({call,A,{remote,L,{atom,L,m},{atom,L,f}}=F,Args}, MaxLine, D) ->
+ Line = erl_anno:line(A),
+ if
+ Line > MaxLine ->
+ {call,Atom} = dict:fetch(Line, D),
+ {call,L,Atom,unhide_calls(Args, MaxLine, D)};
+ true ->
+ {call,A,F,unhide_calls(Args, MaxLine, D)}
+ end;
unhide_calls(T, MaxLine, D) when is_tuple(T) ->
list_to_tuple(unhide_calls(tuple_to_list(T), MaxLine, D));
unhide_calls([E | Es], MaxLine, D) ->
@@ -530,33 +542,34 @@ unhide_calls([E | Es], MaxLine, D) ->
unhide_calls(E, _MaxLine, _D) ->
E.
-%% local_func(Function, Arguments, Bindings, LocalFuncHandler, RBs) ->
+%% local_func(Function, Arguments, Bindings, LocalFuncHandler,
+%% ExternalFuncHandler, RBs) ->
%% {value,Value,Bindings} | Value when
%% LocalFuncHandler = {value,F} | {value,F,Eas} |
%% {eval,F} | {eval,F,Eas} | none.
-local_func(Func, As0, Bs0, {value,F}, value) ->
- {As1,_Bs1} = expr_list(As0, Bs0, {value,F}),
+local_func(Func, As0, Bs0, {value,F}, Ef, value) ->
+ {As1,_Bs1} = expr_list(As0, Bs0, {value,F}, Ef),
%% Make tail recursive calls when possible.
F(Func, As1);
-local_func(Func, As0, Bs0, {value,F}, RBs) ->
- {As1,Bs1} = expr_list(As0, Bs0, {value,F}),
+local_func(Func, As0, Bs0, {value,F}, Ef, RBs) ->
+ {As1,Bs1} = expr_list(As0, Bs0, {value,F}, Ef),
ret_expr(F(Func, As1), Bs1, RBs);
-local_func(Func, As0, Bs0, {value,F,Eas}, RBs) ->
+local_func(Func, As0, Bs0, {value,F,Eas}, Ef, RBs) ->
Fun = fun(Name, Args) -> apply(F, [Name,Args|Eas]) end,
- local_func(Func, As0, Bs0, {value, Fun}, RBs);
-local_func(Func, As, Bs, {eval,F}, RBs) ->
+ local_func(Func, As0, Bs0, {value, Fun}, Ef, RBs);
+local_func(Func, As, Bs, {eval,F}, _Ef, RBs) ->
local_func2(F(Func, As, Bs), RBs);
-local_func(Func, As, Bs, {eval,F,Eas}, RBs) ->
+local_func(Func, As, Bs, {eval,F,Eas}, _Ef, RBs) ->
local_func2(apply(F, [Func,As,Bs|Eas]), RBs);
%% These two clauses are for backwards compatibility.
-local_func(Func, As0, Bs0, {M,F}, RBs) ->
- {As1,Bs1} = expr_list(As0, Bs0, {M,F}),
+local_func(Func, As0, Bs0, {M,F}, Ef, RBs) ->
+ {As1,Bs1} = expr_list(As0, Bs0, {M,F}, Ef),
ret_expr(M:F(Func,As1), Bs1, RBs);
-local_func(Func, As, _Bs, {M,F,Eas}, RBs) ->
+local_func(Func, As, _Bs, {M,F,Eas}, _Ef, RBs) ->
local_func2(apply(M, F, [Func,As|Eas]), RBs);
%% Default unknown function handler to undefined function.
-local_func(Func, As0, _Bs0, none, _RBs) ->
+local_func(Func, As0, _Bs0, none, _Ef, _RBs) ->
erlang:raise(error, undef, [{erl_eval,Func,length(As0)}|stacktrace()]).
local_func2({value,V,Bs}, RBs) ->
@@ -1172,7 +1185,7 @@ match_tuple([], _, _, Bs, _BBs) ->
match_map([{map_field_exact, _, K, V}|Fs], Map, Bs0, BBs) ->
Vm = try
- {value, Ke, _} = expr(K, new_bindings()),
+ {value, Ke, _} = expr(K, BBs),
maps:get(Ke,Map)
catch error:_ ->
throw(nomatch)
diff --git a/lib/stdlib/src/erl_expand_records.erl b/lib/stdlib/src/erl_expand_records.erl
index 64a00acd88..9c0a7fb7d5 100644
--- a/lib/stdlib/src/erl_expand_records.erl
+++ b/lib/stdlib/src/erl_expand_records.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2014. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2015. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -32,14 +33,10 @@
vcount=0, % Variable counter
imports=[], % Imports
records=dict:new(), % Record definitions
- trecords=sets:new(), % Typed records
- uses_types=false, % Are there -spec or -type in the module
strict_ra=[], % strict record accesses
checked_ra=[] % successfully accessed records
}).
--define(REC_OFFSET, 100000000). % A hundred millions. Also in v3_core.
-
-spec(module(AbsForms, CompileOptions) -> AbsForms when
AbsForms :: [erl_parse:abstract_form()],
CompileOptions :: [compile:option()]).
@@ -48,45 +45,18 @@
%% erl_lint without errors.
module(Fs0, Opts0) ->
Opts = compiler_options(Fs0) ++ Opts0,
- TRecs = typed_records(Fs0),
- UsesTypes = uses_types(Fs0),
- St0 = #exprec{compile = Opts, trecords = TRecs, uses_types = UsesTypes},
+ St0 = #exprec{compile = Opts},
{Fs,_St} = forms(Fs0, St0),
Fs.
compiler_options(Forms) ->
lists:flatten([C || {attribute,_,compile,C} <- Forms]).
-typed_records(Fs) ->
- typed_records(Fs, sets:new()).
-
-typed_records([{attribute,_L,type,{{record, Name},_Defs,[]}} | Fs], Trecs) ->
- typed_records(Fs, sets:add_element(Name, Trecs));
-typed_records([_|Fs], Trecs) ->
- typed_records(Fs, Trecs);
-typed_records([], Trecs) ->
- Trecs.
-
-uses_types([{attribute,_L,spec,_}|_]) -> true;
-uses_types([{attribute,_L,type,_}|_]) -> true;
-uses_types([{attribute,_L,opaque,_}|_]) -> true;
-uses_types([_|Fs]) -> uses_types(Fs);
-uses_types([]) -> false.
-
-forms([{attribute,L,record,{Name,Defs}} | Fs], St0) ->
+forms([{attribute,_,record,{Name,Defs}}=Attr | Fs], St0) ->
NDefs = normalise_fields(Defs),
St = St0#exprec{records=dict:store(Name, NDefs, St0#exprec.records)},
{Fs1, St1} = forms(Fs, St),
- %% Check if we need to keep the record information for usage in types.
- case St#exprec.uses_types of
- true ->
- case sets:is_element(Name, St#exprec.trecords) of
- true -> {Fs1, St1};
- false -> {[{attribute,L,type,{{record,Name},Defs,[]}}|Fs1], St1}
- end;
- false ->
- {Fs1, St1}
- end;
+ {[Attr | Fs1], St1};
forms([{attribute,L,import,Is} | Fs0], St0) ->
St1 = import(Is, St0),
{Fs,St2} = forms(Fs0, St1),
@@ -149,7 +119,7 @@ pattern({record_index,Line,Name,Field}, St) ->
pattern({record,Line0,Name,Pfs}, St0) ->
Fs = record_fields(Name, St0),
{TMs,St1} = pattern_list(pattern_fields(Fs, Pfs), St0),
- Line = record_offset(Line0, St1),
+ Line = mark_record(Line0, St1),
{{tuple,Line,[{atom,Line0,Name} | TMs]},St1};
pattern({bin,Line,Es0}, St0) ->
{Es1,St1} = pattern_bin(Es0, St0),
@@ -243,7 +213,7 @@ record_test_in_guard(Line, Term, Name, St) ->
expr({atom,Line,false}, St);
false ->
Fs = record_fields(Name, St),
- NLine = neg_line(Line),
+ NLine = no_compiler_warning(Line),
expr({call,NLine,{remote,NLine,{atom,NLine,erlang},{atom,NLine,is_record}},
[Term,{atom,Line,Name},{integer,Line,length(Fs)+1}]},
St)
@@ -269,7 +239,7 @@ record_test_in_body(Line, Expr, Name, St0) ->
%% evaluate to a tuple properly.
Fs = record_fields(Name, St0),
{Var,St} = new_var(Line, St0),
- NLine = neg_line(Line),
+ NLine = no_compiler_warning(Line),
expr({block,Line,
[{match,Line,Var,Expr},
{call,NLine,{remote,NLine,{atom,NLine,erlang},
@@ -333,7 +303,7 @@ expr({record_index,Line,Name,F}, St) ->
I = index_expr(Line, F, Name, record_fields(Name, St)),
expr(I, St);
expr({record,Line0,Name,Is}, St) ->
- Line = record_offset(Line0, St),
+ Line = mark_record(Line0, St),
expr({tuple,Line,[{atom,Line0,Name} |
record_inits(record_fields(Name, St), Is)]},
St);
@@ -384,21 +354,11 @@ expr({call,Line,{tuple,_,[{atom,_,erlang},{atom,_,is_record}]},
expr({call,Line,{atom,_La,N}=Atom,As0}, St0) ->
{As,St1} = expr_list(As0, St0),
Ar = length(As),
- case erl_internal:bif(N, Ar) of
- true ->
- {{call,Line,Atom,As},St1};
- false ->
- case imported(N, Ar, St1) of
- {yes,_Mod} ->
- {{call,Line,Atom,As},St1};
- no ->
- case {N,Ar} of
- {record_info,2} ->
- record_info_call(Line, As, St1);
- _ ->
- {{call,Line,Atom,As},St1}
- end
- end
+ case {N,Ar} =:= {record_info,2} andalso not imported(N, Ar, St1) of
+ true ->
+ record_info_call(Line, As, St1);
+ false ->
+ {{call,Line,Atom,As},St1}
end;
expr({call,Line,{remote,Lr,M,F},As0}, St0) ->
{[M1,F1 | As1],St1} = expr_list([M,F | As0], St0),
@@ -469,7 +429,7 @@ strict_record_access(E0, St0) ->
conj([], _E) ->
empty;
conj([{{Name,_Rp},L,R,Sz} | AL], E) ->
- NL = neg_line(L),
+ NL = no_compiler_warning(L),
T1 = {op,NL,'orelse',
{call,NL,
{remote,NL,{atom,NL,erlang},{atom,NL,is_record}},
@@ -524,7 +484,6 @@ lc_tq(Line, [F0 | Qs0], St0) ->
lc_tq(_Line, [], St0) ->
{[],St0#exprec{checked_ra = []}}.
-
%% normalise_fields([RecDef]) -> [Field].
%% Normalise the field definitions to always have a default value. If
%% none has been given then use 'undefined'.
@@ -585,8 +544,8 @@ strict_get_record_field(Line, R, {atom,_,F}=Index, Name, St0) ->
Fs = record_fields(Name, St),
I = index_expr(F, Fs, 2),
P = record_pattern(2, I, Var, length(Fs)+1, Line, [{atom,Line,Name}]),
- NLine = neg_line(Line),
- RLine = record_offset(NLine, St),
+ NLine = no_compiler_warning(Line),
+ RLine = mark_record(NLine, St),
E = {'case',NLine,R,
[{clause,NLine,[{tuple,RLine,P}],[],[Var]},
{clause,NLine,[{var,NLine,'_'}],[],
@@ -600,7 +559,8 @@ strict_get_record_field(Line, R, {atom,_,F}=Index, Name, St0) ->
I = index_expr(Line, Index, Name, Fs),
{ExpR,St1} = expr(R, St0),
%% Just to make comparison simple:
- ExpRp = erl_lint:modify_line(ExpR, fun(_L) -> 0 end),
+ A0 = erl_anno:new(0),
+ ExpRp = erl_parse:map_anno(fun(_A) -> A0 end, ExpR),
RA = {{Name,ExpRp},Line,ExpR,length(Fs)+1},
St2 = St1#exprec{strict_ra = [RA | St1#exprec.strict_ra]},
{{call,Line,
@@ -701,8 +661,8 @@ record_update(R, Name, Fs, Us0, St0) ->
record_match(R, Name, Lr, Fs, Us, St0) ->
{Ps,News,St1} = record_upd_fs(Fs, Us, St0),
- NLr = neg_line(Lr),
- RLine = record_offset(Lr, St1),
+ NLr = no_compiler_warning(Lr),
+ RLine = mark_record(Lr, St1),
{{'case',Lr,R,
[{clause,Lr,[{tuple,RLine,[{atom,Lr,Name} | Ps]}],[],
[{tuple,RLine,[{atom,Lr,Name} | News]}]},
@@ -733,8 +693,8 @@ record_setel(R, Name, Fs, Us0) ->
Us = [T || {_,T} <- Us2],
Lr = element(2, hd(Us)),
Wildcards = duplicate(length(Fs), {var,Lr,'_'}),
- NLr = neg_line(Lr),
- %% Note: calling record_offset() here is not necessary since it is
+ NLr = no_compiler_warning(Lr),
+ %% Note: calling mark_record() here is not necessary since it is
%% targeted at Dialyzer which always calls the compiler with
%% 'strict_record_updates' meaning that record_setel() will never
%% be called.
@@ -832,10 +792,7 @@ add_imports(Mod, [F | Fs], Is) ->
add_imports(_, [], Is) -> Is.
imported(F, A, St) ->
- case orddict:find({F,A}, St#exprec.imports) of
- {ok,Mod} -> {yes,Mod};
- error -> no
- end.
+ orddict:is_key({F,A}, St#exprec.imports).
%%%
%%% Replace is_record/3 in guards with matching if possible.
@@ -969,12 +926,11 @@ opt_remove_2({call,Line,{atom,_,is_record},
end;
opt_remove_2(A, _) -> A.
-neg_line(L) ->
- erl_parse:set_line(L, fun(Line) -> -abs(Line) end).
+no_compiler_warning(Anno) ->
+ erl_anno:set_generated(true, Anno).
-record_offset(L, St) ->
+mark_record(Anno, St) ->
case lists:member(dialyzer, St#exprec.compile) of
- true when L >= 0 -> L+?REC_OFFSET;
- true when L < 0 -> L-?REC_OFFSET;
- false -> L
+ true -> erl_anno:set_record(true, Anno);
+ false -> Anno
end.
diff --git a/lib/stdlib/src/erl_internal.erl b/lib/stdlib/src/erl_internal.erl
index edfb097de0..c08328b4b7 100644
--- a/lib/stdlib/src/erl_internal.erl
+++ b/lib/stdlib/src/erl_internal.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -51,6 +52,8 @@
type_test/2,new_type_test/2,old_type_test/2,old_bif/2]).
-export([arith_op/2,bool_op/2,comp_op/2,list_op/2,send_op/2,op_type/2]).
+-export([is_type/2]).
+
%%---------------------------------------------------------------------------
%% Erlang builtin functions allowed in guards.
@@ -293,6 +296,7 @@ bif(garbage_collect, 1) -> true;
bif(garbage_collect, 2) -> true;
bif(get, 0) -> true;
bif(get, 1) -> true;
+bif(get_keys, 0) -> true;
bif(get_keys, 1) -> true;
bif(group_leader, 0) -> true;
bif(group_leader, 2) -> true;
@@ -530,3 +534,53 @@ old_bif(unlink, 1) -> true;
old_bif(unregister, 1) -> true;
old_bif(whereis, 1) -> true;
old_bif(Name, A) when is_atom(Name), is_integer(A) -> false.
+
+-spec is_type(Name, NumberOfTypeVariables) -> boolean() when
+ Name :: atom(),
+ NumberOfTypeVariables :: non_neg_integer().
+%% Returns true if Name/NumberOfTypeVariables is a predefined type.
+
+is_type(any, 0) -> true;
+is_type(arity, 0) -> true;
+is_type(atom, 0) -> true;
+is_type(binary, 0) -> true;
+is_type(bitstring, 0) -> true;
+is_type(bool, 0) -> true;
+is_type(boolean, 0) -> true;
+is_type(byte, 0) -> true;
+is_type(char, 0) -> true;
+is_type(float, 0) -> true;
+is_type(function, 0) -> true;
+is_type(identifier, 0) -> true;
+is_type(integer, 0) -> true;
+is_type(iodata, 0) -> true;
+is_type(iolist, 0) -> true;
+is_type(list, 0) -> true;
+is_type(list, 1) -> true;
+is_type(map, 0) -> true;
+is_type(maybe_improper_list, 0) -> true;
+is_type(maybe_improper_list, 2) -> true;
+is_type(mfa, 0) -> true;
+is_type(module, 0) -> true;
+is_type(neg_integer, 0) -> true;
+is_type(nil, 0) -> true;
+is_type(no_return, 0) -> true;
+is_type(node, 0) -> true;
+is_type(non_neg_integer, 0) -> true;
+is_type(none, 0) -> true;
+is_type(nonempty_improper_list, 2) -> true;
+is_type(nonempty_list, 0) -> true;
+is_type(nonempty_list, 1) -> true;
+is_type(nonempty_maybe_improper_list, 0) -> true;
+is_type(nonempty_maybe_improper_list, 2) -> true;
+is_type(nonempty_string, 0) -> true;
+is_type(number, 0) -> true;
+is_type(pid, 0) -> true;
+is_type(port, 0) -> true;
+is_type(pos_integer, 0) -> true;
+is_type(reference, 0) -> true;
+is_type(string, 0) -> true;
+is_type(term, 0) -> true;
+is_type(timeout, 0) -> true;
+is_type(tuple, 0) -> true;
+is_type(_, _) -> false.
diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl
index 39cc03cf7a..2508f96b91 100644
--- a/lib/stdlib/src/erl_lint.erl
+++ b/lib/stdlib/src/erl_lint.erl
@@ -2,18 +2,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -30,8 +31,6 @@
-export([is_guard_expr/1]).
-export([bool_option/4,value_option/3,value_option/7]).
--export([modify_line/2]).
-
-import(lists, [member/2,map/2,foldl/3,foldr/3,mapfoldl/3,all/2,reverse/1]).
%% bool_option(OnOpt, OffOpt, Default, Options) -> boolean().
@@ -76,7 +75,7 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) ->
%%-define(DEBUGF(X,Y), io:format(X, Y)).
-define(DEBUGF(X,Y), void).
--type line() :: erl_scan:line(). % a convenient alias
+-type line() :: erl_anno:anno(). % a convenient alias
-type fa() :: {atom(), arity()}. % function+arity
-type ta() :: {atom(), arity()}. % type+arity
@@ -97,7 +96,7 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) ->
%% 'called' and 'exports' contain {Line, {Function, Arity}},
%% the other function collections contain {Function, Arity}.
-record(lint, {state=start :: 'start' | 'attribute' | 'function',
- module=[], %Module
+ module='', %Module
behaviour=[], %Behaviour
exports=gb_sets:empty() :: gb_sets:set(fa()),%Exports
imports=[] :: [fa()], %Imports, an orddict()
@@ -111,25 +110,28 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) ->
defined=gb_sets:empty() %Defined fuctions
:: gb_sets:set(fa()),
on_load=[] :: [fa()], %On-load function
- on_load_line=0 :: line(), %Line for on_load
+ on_load_line=erl_anno:new(0) %Line for on_load
+ :: erl_anno:anno(),
clashes=[], %Exported functions named as BIFs
not_deprecated=[], %Not considered deprecated
func=[], %Current function
warn_format=0, %Warn format calls
enabled_warnings=[], %All enabled warnings (ordset).
+ nowarn_bif_clash=[], %All no warn bif clashes (ordset).
errors=[], %Current errors
warnings=[], %Current warnings
file = "" :: string(), %From last file attribute
recdef_top=false :: boolean(), %true in record initialisation
%outside any fun or lc
xqlc= false :: boolean(), %true if qlc.hrl included
- new = false :: boolean(), %Has user-defined 'new/N'
called= [] :: [{fa(),line()}], %Called functions
usage = #usage{} :: #usage{},
specs = dict:new() %Type specifications
:: dict:dict(mfa(), line()),
callbacks = dict:new() %Callback types
:: dict:dict(mfa(), line()),
+ optional_callbacks = dict:new() %Optional callbacks
+ :: dict:dict(mfa(), line()),
types = dict:new() %Type definitions
:: dict:dict(ta(), #typeinfo{}),
exp_types=gb_sets:empty() %Exported types
@@ -138,7 +140,7 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) ->
-type lint_state() :: #lint{}.
-type error_description() :: term().
--type error_info() :: {erl_scan:line(), module(), error_description()}.
+-type error_info() :: {erl_anno:line(), module(), error_description()}.
%% format_error(Error)
%% Return a string describing the error.
@@ -225,11 +227,16 @@ format_error({deprecated, MFA, ReplacementMFA, Rel}) ->
[format_mfa(MFA), Rel, format_mfa(ReplacementMFA)]);
format_error({deprecated, {M1, F1, A1}, String}) when is_list(String) ->
io_lib:format("~p:~p/~p: ~s", [M1, F1, A1, String]);
+format_error({deprecated_type, {M1, F1, A1}, String}) when is_list(String) ->
+ io_lib:format("~p:~p~s: ~s", [M1, F1, gen_type_paren(A1), String]);
format_error({removed, MFA, ReplacementMFA, Rel}) ->
io_lib:format("call to ~s will fail, since it was removed in ~s; "
"use ~s", [format_mfa(MFA), Rel, format_mfa(ReplacementMFA)]);
format_error({removed, MFA, String}) when is_list(String) ->
io_lib:format("~s: ~s", [format_mfa(MFA), String]);
+format_error({removed_type, MNA, ReplacementMNA, Rel}) ->
+ io_lib:format("the type ~s was removed in ~s; use ~s instead",
+ [format_mna(MNA), Rel, format_mna(ReplacementMNA)]);
format_error({obsolete_guard, {F, A}}) ->
io_lib:format("~p/~p obsolete", [F, A]);
format_error({too_many_arguments,Arity}) ->
@@ -237,10 +244,7 @@ format_error({too_many_arguments,Arity}) ->
"maximum allowed is ~w", [Arity,?MAX_ARGUMENTS]);
%% --- patterns and guards ---
format_error(illegal_pattern) -> "illegal pattern";
-format_error(illegal_map_key) ->
- "illegal map key";
-format_error({illegal_map_key_variable,K}) ->
- io_lib:format("illegal use of variable ~w in map",[K]);
+format_error(illegal_map_key) -> "illegal map key in pattern";
format_error(illegal_bin_pattern) ->
"binary patterns cannot be matched in parallel using '='";
format_error(illegal_expr) -> "illegal expression";
@@ -288,6 +292,9 @@ format_error({variable_in_record_def,V}) ->
%% --- binaries ---
format_error({undefined_bittype,Type}) ->
io_lib:format("bit type ~w undefined", [Type]);
+format_error({bittype_mismatch,Val1,Val2,What}) ->
+ io_lib:format("conflict in ~s specification for bit field: '~p' and '~p'",
+ [What,Val1,Val2]);
format_error(bittype_unit) ->
"a bit unit size must not be specified unless a size is specified too";
format_error(illegal_bitsize) ->
@@ -313,13 +320,20 @@ format_error({undefined_behaviour,Behaviour}) ->
io_lib:format("behaviour ~w undefined", [Behaviour]);
format_error({undefined_behaviour_callbacks,Behaviour}) ->
io_lib:format("behaviour ~w callback functions are undefined",
- [Behaviour]);
+ [Behaviour]);
format_error({ill_defined_behaviour_callbacks,Behaviour}) ->
io_lib:format("behaviour ~w callback functions erroneously defined",
[Behaviour]);
+format_error({ill_defined_optional_callbacks,Behaviour}) ->
+ io_lib:format("behaviour ~w optional callback functions erroneously defined",
+ [Behaviour]);
format_error({behaviour_info, {_M,F,A}}) ->
io_lib:format("cannot define callback attibute for ~w/~w when "
"behaviour_info is defined",[F,A]);
+format_error({redefine_optional_callback, {F, A}}) ->
+ io_lib:format("optional callback ~w/~w duplicated", [F, A]);
+format_error({undefined_callback, {_M, F, A}}) ->
+ io_lib:format("callback ~w/~w is undefined", [F, A]);
%% --- types and specs ---
format_error({singleton_typevar, Name}) ->
io_lib:format("type variable ~w is only used once (is unbound)", [Name]);
@@ -331,14 +345,10 @@ format_error({undefined_type, {TypeName, Arity}}) ->
io_lib:format("type ~w~s undefined", [TypeName, gen_type_paren(Arity)]);
format_error({unused_type, {TypeName, Arity}}) ->
io_lib:format("type ~w~s is unused", [TypeName, gen_type_paren(Arity)]);
-%% format_error({new_builtin_type, {TypeName, Arity}}) ->
-%% io_lib:format("type ~w~s is a new builtin type; "
-%% "its (re)definition is allowed only until the next release",
-%% [TypeName, gen_type_paren(Arity)]);
-format_error({new_var_arity_type, TypeName}) ->
- io_lib:format("type ~w is a new builtin type; "
+format_error({new_builtin_type, {TypeName, Arity}}) ->
+ io_lib:format("type ~w~s is a new builtin type; "
"its (re)definition is allowed only until the next release",
- [TypeName]);
+ [TypeName, gen_type_paren(Arity)]);
format_error({builtin_type, {TypeName, Arity}}) ->
io_lib:format("type ~w~s is a builtin type; it cannot be redefined",
[TypeName, gen_type_paren(Arity)]);
@@ -350,18 +360,25 @@ format_error({redefine_type, {TypeName, Arity}}) ->
[TypeName, gen_type_paren(Arity)]);
format_error({type_syntax, Constr}) ->
io_lib:format("bad ~w type", [Constr]);
+format_error(old_abstract_code) ->
+ io_lib:format("abstract code generated before Erlang/OTP 19.0 and "
+ "having typed record fields cannot be compiled", []);
format_error({redefine_spec, {M, F, A}}) ->
io_lib:format("spec for ~w:~w/~w already defined", [M, F, A]);
-format_error({redefine_callback, {M, F, A}}) ->
- io_lib:format("callback ~w:~w/~w already defined", [M, F, A]);
-format_error({spec_fun_undefined, {M, F, A}}) ->
- io_lib:format("spec for undefined function ~w:~w/~w", [M, F, A]);
+format_error({redefine_spec, {F, A}}) ->
+ io_lib:format("spec for ~w/~w already defined", [F, A]);
+format_error({redefine_callback, {F, A}}) ->
+ io_lib:format("callback ~w/~w already defined", [F, A]);
+format_error({bad_callback, {M, F, A}}) ->
+ io_lib:format("explicit module not allowed for callback ~w:~w/~w ", [M, F, A]);
+format_error({spec_fun_undefined, {F, A}}) ->
+ io_lib:format("spec for undefined function ~w/~w", [F, A]);
format_error({missing_spec, {F,A}}) ->
io_lib:format("missing specification for function ~w/~w", [F, A]);
format_error(spec_wrong_arity) ->
- "spec has the wrong arity";
+ "spec has wrong arity";
format_error(callback_wrong_arity) ->
- "callback has the wrong arity";
+ "callback has wrong arity";
format_error({deprecated_builtin_type, {Name, Arity},
Replacement, Rel}) ->
UseS = case Replacement of
@@ -383,9 +400,7 @@ format_error({underspecified_opaque, {TypeName, Arity}}) ->
[TypeName, gen_type_paren(Arity)]);
%% --- obsolete? unused? ---
format_error({format_error, {Fmt, Args}}) ->
- io_lib:format(Fmt, Args);
-format_error({mnemosyne, What}) ->
- "mnemosyne " ++ What ++ ", missing transformation".
+ io_lib:format(Fmt, Args).
gen_type_paren(Arity) when is_integer(Arity), Arity >= 0 ->
gen_type_paren_1(Arity, ")").
@@ -403,6 +418,9 @@ format_mfa({M, F, A}) when is_integer(A) ->
format_mf(M, F, ArityString) when is_atom(M), is_atom(F) ->
atom_to_list(M) ++ ":" ++ atom_to_list(F) ++ "/" ++ ArityString.
+format_mna({M, N, A}) when is_integer(A) ->
+ atom_to_list(M) ++ ":" ++ atom_to_list(N) ++ gen_type_paren(A).
+
format_where(L) when is_integer(L) ->
io_lib:format("(line ~p)", [L]);
format_where({L,C}) when is_integer(L), is_integer(C) ->
@@ -421,13 +439,13 @@ exprs(Exprs, BindingsList) ->
exprs_opt(Exprs, BindingsList, Opts) ->
{St0,Vs} = foldl(fun({{record,_SequenceNumber,_Name},Attr0}, {St1,Vs1}) ->
- Attr = zip_file_and_line(Attr0, "none"),
+ Attr = set_file(Attr0, "none"),
{attribute_state(Attr, St1),Vs1};
({V,_}, {St1,Vs1}) ->
{St1,[{V,{bound,unused,[]}} | Vs1]}
end, {start("nofile",Opts),[]}, BindingsList),
Vt = orddict:from_list(Vs),
- {_Evt,St} = exprs(zip_file_and_line(Exprs, "nofile"), Vt, St0),
+ {_Evt,St} = exprs(set_file(Exprs, "nofile"), Vt, St0),
return_status(St).
used_vars(Exprs, BindingsList) ->
@@ -435,7 +453,7 @@ used_vars(Exprs, BindingsList) ->
({V,_Val}, Vs0) -> [{V,{bound,unused,[]}} | Vs0]
end, [], BindingsList),
Vt = orddict:from_list(Vs),
- {Evt,_St} = exprs(zip_file_and_line(Exprs, "nofile"), Vt, start()),
+ {Evt,_St} = exprs(set_file(Exprs, "nofile"), Vt, start()),
{ok, foldl(fun({V,{_,used,_}}, L) -> [V | L];
(_, L) -> L
end, [], Evt)}.
@@ -560,6 +578,7 @@ start(File, Opts) ->
warn_format = value_option(warn_format, 1, warn_format, 1,
nowarn_format, 0, Opts),
enabled_warnings = Enabled,
+ nowarn_bif_clash = nowarn_function(nowarn_bif_clash, Opts),
file = File
}.
@@ -599,34 +618,39 @@ pack_warnings(Ws) ->
%% add_warning(ErrorDescriptor, State) -> State'
%% add_warning(Line, Error, State) -> State'
-add_error(E, St) -> St#lint{errors=[{St#lint.file,E}|St#lint.errors]}.
+add_error(E, St) -> add_lint_error(E, St#lint.file, St).
+
+add_error(Anno, E, St) ->
+ {File,Location} = loc(Anno, St),
+ add_lint_error({Location,erl_lint,E}, File, St).
-add_error(FileLine, E, St) ->
- {File,Location} = loc(FileLine),
- add_error({Location,erl_lint,E}, St#lint{file = File}).
+add_lint_error(E, File, St) ->
+ St#lint{errors=[{File,E}|St#lint.errors]}.
-add_warning(W, St) -> St#lint{warnings=[{St#lint.file,W}|St#lint.warnings]}.
+add_warning(W, St) -> add_lint_warning(W, St#lint.file, St).
add_warning(FileLine, W, St) ->
- {File,Location} = loc(FileLine),
- add_warning({Location,erl_lint,W}, St#lint{file = File}).
-
-loc(L) ->
- case erl_parse:get_attribute(L, location) of
- {location,{{File,Line},Column}} ->
- {File,{Line,Column}};
- {location,{File,Line}} ->
- {File,Line}
+ {File,Location} = loc(FileLine, St),
+ add_lint_warning({Location,erl_lint,W}, File, St).
+
+add_lint_warning(W, File, St) ->
+ St#lint{warnings=[{File,W}|St#lint.warnings]}.
+
+loc(Anno, St) ->
+ Location = erl_anno:location(Anno),
+ case erl_anno:file(Anno) of
+ undefined -> {St#lint.file,Location};
+ File -> {File,Location}
end.
%% forms([Form], State) -> State'
forms(Forms0, St0) ->
Forms = eval_file_attribute(Forms0, St0),
+ %% Annotations from now on include the 'file' item.
Locals = local_functions(Forms),
AutoImportSuppressed = auto_import_suppressed(St0#lint.compile),
StDeprecated = disallowed_compile_flags(Forms,St0),
- %% Line numbers are from now on pairs {File,Line}.
St1 = includes_qlc_hrl(Forms, StDeprecated#lint{locals = Locals,
no_auto = AutoImportSuppressed}),
St2 = bif_clashes(Forms, St1),
@@ -634,8 +658,6 @@ forms(Forms0, St0) ->
St4 = foldl(fun form/2, pre_scan(Forms, St3), Forms),
post_traversal_check(Forms, St4).
-pre_scan([{function,_L,new,_A,_Cs} | Fs], St) ->
- pre_scan(Fs, St#lint{new=true});
pre_scan([{attribute,L,compile,C} | Fs], St) ->
case is_warn_enabled(export_all, St) andalso
member(export_all, lists:flatten([C])) of
@@ -662,15 +684,29 @@ eval_file_attribute(Forms, St) ->
eval_file_attr([{attribute,_L,file,{File,_Line}}=Form | Forms], _File) ->
[Form | eval_file_attr(Forms, File)];
eval_file_attr([Form0 | Forms], File) ->
- Form = zip_file_and_line(Form0, File),
+ Form = set_form_file(Form0, File),
[Form | eval_file_attr(Forms, File)];
eval_file_attr([], _File) ->
[].
-zip_file_and_line(T, File) ->
- F0 = fun(Line) -> {File,Line} end,
- F = fun(L) -> erl_parse:set_line(L, F0) end,
- modify_line(T, F).
+%% Sets the file only on the form. This is used on post-traversal.
+%% For the remaining of the AST we rely on #lint.file.
+
+set_form_file({attribute,L,K,V}, File) ->
+ {attribute,erl_anno:set_file(File, L),K,V};
+set_form_file({function,L,N,A,C}, File) ->
+ {function,erl_anno:set_file(File, L),N,A,C};
+set_form_file(Form, _File) ->
+ Form.
+
+set_file(Ts, File) when is_list(Ts) ->
+ [anno_set_file(T, File) || T <- Ts];
+set_file(T, File) ->
+ anno_set_file(T, File).
+
+anno_set_file(T, File) ->
+ F = fun(Anno) -> erl_anno:set_file(File, Anno) end,
+ erl_parse:map_anno(F, T).
%% form(Form, State) -> State'
%% Check a form returning the updated State. Handle generic cases here.
@@ -703,7 +739,7 @@ start_state(Form, St) ->
%% attribute_state(Form, State) ->
%% State'
-attribute_state({attribute,_L,module,_M}, #lint{module=[]}=St) ->
+attribute_state({attribute,_L,module,_M}, #lint{module=''}=St) ->
St;
attribute_state({attribute,L,module,_M}, St) ->
add_error(L, redefine_module, St);
@@ -727,6 +763,8 @@ attribute_state({attribute,L,spec,{Fun,Types}}, St) ->
spec_decl(L, Fun, Types, St);
attribute_state({attribute,L,callback,{Fun,Types}}, St) ->
callback_decl(L, Fun, Types, St);
+attribute_state({attribute,L,optional_callbacks,Es}, St) ->
+ optional_callbacks(L, Es, St);
attribute_state({attribute,L,on_load,Val}, St) ->
on_load(L, Val, St);
attribute_state({attribute,_L,_Other,_Val}, St) -> % Ignore others
@@ -738,6 +776,8 @@ attribute_state(Form, St) ->
%% State'
%% Allow for record, type and opaque type definitions and spec
%% declarations to be intersperced within function definitions.
+%% Dialyzer attributes are also allowed everywhere, but are not
+%% checked at all.
function_state({attribute,L,record,{Name,Fields}}, St) ->
record_def(L, Name, Fields, St);
@@ -747,12 +787,12 @@ function_state({attribute,L,opaque,{TypeName,TypeDef,Args}}, St) ->
type_def(opaque, L, TypeName, TypeDef, Args, St);
function_state({attribute,L,spec,{Fun,Types}}, St) ->
spec_decl(L, Fun, Types, St);
+function_state({attribute,_L,dialyzer,_Val}, St) ->
+ St;
function_state({attribute,La,Attr,_Val}, St) ->
add_error(La, {attribute,Attr}, St);
function_state({function,L,N,A,Cs}, St) ->
function(L, N, A, Cs, St);
-function_state({rule,L,_N,_A,_Cs}, St) ->
- add_error(L, {mnemosyne,"rule"}, St);
function_state({eof,L}, St) -> eof(L, St).
%% eof(LastLine, State) ->
@@ -763,8 +803,7 @@ eof(_Line, St0) ->
%% bif_clashes(Forms, State0) -> State.
-bif_clashes(Forms, St) ->
- Nowarn = nowarn_function(nowarn_bif_clash, St#lint.compile),
+bif_clashes(Forms, #lint{nowarn_bif_clash=Nowarn} = St) ->
Clashes0 = [{Name,Arity} || {function,_L,Name,Arity,_Cs} <- Forms,
erl_internal:bif(Name, Arity)],
Clashes = ordsets:subtract(ordsets:from_list(Clashes0), Nowarn),
@@ -788,9 +827,11 @@ not_deprecated(Forms, St0) ->
disallowed_compile_flags(Forms, St0) ->
%% There are (still) no line numbers in St0#lint.compile.
Errors0 = [ {St0#lint.file,{L,erl_lint,disallowed_nowarn_bif_clash}} ||
- {attribute,[{line,{_,L}}],compile,nowarn_bif_clash} <- Forms ],
+ {attribute,A,compile,nowarn_bif_clash} <- Forms,
+ {_,L} <- [loc(A, St0)] ],
Errors1 = [ {St0#lint.file,{L,erl_lint,disallowed_nowarn_bif_clash}} ||
- {attribute,[{line,{_,L}}],compile,{nowarn_bif_clash, {_,_}}} <- Forms ],
+ {attribute,A,compile,{nowarn_bif_clash, {_,_}}} <- Forms,
+ {_,L} <- [loc(A, St0)] ],
Disabled = (not is_warn_enabled(bif_clash, St0)),
Errors = if
Disabled andalso Errors0 =:= [] ->
@@ -834,57 +875,73 @@ check_behaviour(St0) ->
%% Check behaviours for existence and defined functions.
behaviour_check(Bs, St0) ->
- {AllBfs,St1} = all_behaviour_callbacks(Bs, [], St0),
- St = behaviour_missing_callbacks(AllBfs, St1),
+ {AllBfs0, St1} = all_behaviour_callbacks(Bs, [], St0),
+ St = behaviour_missing_callbacks(AllBfs0, St1),
+ Exports = exports(St0),
+ F = fun(Bfs, OBfs) ->
+ [B || B <- Bfs,
+ not lists:member(B, OBfs)
+ orelse gb_sets:is_member(B, Exports)]
+ end,
+ %% After fixing missing callbacks new warnings may be emitted.
+ AllBfs = [{Item,F(Bfs0, OBfs0)} || {Item,Bfs0,OBfs0} <- AllBfs0],
behaviour_conflicting(AllBfs, St).
all_behaviour_callbacks([{Line,B}|Bs], Acc, St0) ->
- {Bfs0,St} = behaviour_callbacks(Line, B, St0),
- all_behaviour_callbacks(Bs, [{{Line,B},Bfs0}|Acc], St);
+ {Bfs0,OBfs0,St} = behaviour_callbacks(Line, B, St0),
+ all_behaviour_callbacks(Bs, [{{Line,B},Bfs0,OBfs0}|Acc], St);
all_behaviour_callbacks([], Acc, St) -> {reverse(Acc),St}.
behaviour_callbacks(Line, B, St0) ->
try B:behaviour_info(callbacks) of
- Funcs when is_list(Funcs) ->
- All = all(fun({FuncName, Arity}) ->
- is_atom(FuncName) andalso is_integer(Arity);
- ({FuncName, Arity, Spec}) ->
- is_atom(FuncName) andalso is_integer(Arity)
- andalso is_list(Spec);
- (_Other) ->
- false
- end,
- Funcs),
- MaybeRemoveSpec = fun({_F,_A}=FA) -> FA;
- ({F,A,_S}) -> {F,A};
- (Other) -> Other
- end,
- if
- All =:= true ->
- {[MaybeRemoveSpec(F) || F <- Funcs], St0};
+ undefined ->
+ St1 = add_warning(Line, {undefined_behaviour_callbacks, B}, St0),
+ {[], [], St1};
+ Funcs ->
+ case is_fa_list(Funcs) of
true ->
+ try B:behaviour_info(optional_callbacks) of
+ undefined ->
+ {Funcs, [], St0};
+ OptFuncs ->
+ %% OptFuncs should always be OK thanks to
+ %% sys_pre_expand.
+ case is_fa_list(OptFuncs) of
+ true ->
+ {Funcs, OptFuncs, St0};
+ false ->
+ W = {ill_defined_optional_callbacks, B},
+ St1 = add_warning(Line, W, St0),
+ {Funcs, [], St1}
+ end
+ catch
+ _:_ ->
+ {Funcs, [], St0}
+ end;
+ false ->
St1 = add_warning(Line,
- {ill_defined_behaviour_callbacks,B},
+ {ill_defined_behaviour_callbacks, B},
St0),
- {[], St1}
- end;
- undefined ->
- St1 = add_warning(Line, {undefined_behaviour_callbacks,B}, St0),
- {[], St1};
- _Other ->
- St1 = add_warning(Line, {ill_defined_behaviour_callbacks,B}, St0),
- {[], St1}
+ {[], [], St1}
+ end
catch
_:_ ->
- St1 = add_warning(Line, {undefined_behaviour,B}, St0),
- {[], St1}
+ St1 = add_warning(Line, {undefined_behaviour, B}, St0),
+ {[], [], St1}
end.
-behaviour_missing_callbacks([{{Line,B},Bfs}|T], St0) ->
+behaviour_missing_callbacks([{{Line,B},Bfs0,OBfs}|T], St0) ->
+ Bfs = ordsets:subtract(ordsets:from_list(Bfs0), ordsets:from_list(OBfs)),
Exports = gb_sets:to_list(exports(St0)),
- Missing = ordsets:subtract(ordsets:from_list(Bfs), Exports),
+ Missing = ordsets:subtract(Bfs, Exports),
St = foldl(fun (F, S0) ->
- add_warning(Line, {undefined_behaviour_func,F,B}, S0)
+ case is_fa(F) of
+ true ->
+ M = {undefined_behaviour_func,F,B},
+ add_warning(Line, M, S0);
+ false ->
+ S0 % ill_defined_behaviour_callbacks
+ end
end, St0, Missing),
behaviour_missing_callbacks(T, St);
behaviour_missing_callbacks([], St) -> St.
@@ -899,7 +956,7 @@ behaviour_conflicting(AllBfs, St) ->
behaviour_add_conflicts(R, St).
behaviour_add_conflicts([{Cb,[{FirstLoc,FirstB}|Cs]}|T], St0) ->
- FirstL = element(2, loc(FirstLoc)),
+ FirstL = element(2, loc(FirstLoc, St0)),
St = behaviour_add_conflict(Cs, Cb, FirstL, FirstB, St0),
behaviour_add_conflicts(T, St);
behaviour_add_conflicts([], St) -> St.
@@ -1046,10 +1103,9 @@ check_undefined_types(#lint{usage=Usage,types=Def}=St0) ->
Used = Usage#usage.used_types,
UTAs = dict:fetch_keys(Used),
Undef = [{TA,dict:fetch(TA, Used)} ||
- {T,_}=TA <- UTAs,
+ TA <- UTAs,
not dict:is_key(TA, Def),
- not is_default_type(TA),
- not is_newly_introduced_var_arity_type(T)],
+ not is_default_type(TA)],
foldl(fun ({TA,L}, St) ->
add_error(L, {undefined_type,TA}, St)
end, St0, Undef).
@@ -1090,7 +1146,7 @@ check_untyped_records(Forms, St0) ->
RecNames = dict:fetch_keys(St0#lint.records),
%% these are the records with field(s) containing type info
TRecNames = [Name ||
- {attribute,_,type,{{record,Name},Fields,_}} <- Forms,
+ {attribute,_,record,{Name,Fields}} <- Forms,
lists:all(fun ({typed_record_field,_,_}) -> true;
(_) -> false
end, Fields)],
@@ -1100,7 +1156,8 @@ check_untyped_records(Forms, St0) ->
[] -> St; % exclude records with no fields
[_|_] -> add_warning(L, {untyped_record, N}, St)
end
- end, St0, RecNames -- TRecNames);
+ end, St0, ordsets:subtract(ordsets:from_list(RecNames),
+ ordsets:from_list(TRecNames)));
false ->
St0
end.
@@ -1118,7 +1175,7 @@ check_unused_records(Forms, St0) ->
end, St0#lint.records, UsedRecords),
Unused = [{Name,FileLine} ||
{Name,{FileLine,_Fields}} <- dict:to_list(URecs),
- element(1, loc(FileLine)) =:= FirstFile],
+ element(1, loc(FileLine, St0)) =:= FirstFile],
foldl(fun ({N,L}, St) ->
add_warning(L, {unused_record, N}, St)
end, St0, Unused);
@@ -1127,19 +1184,29 @@ check_unused_records(Forms, St0) ->
end.
check_callback_information(#lint{callbacks = Callbacks,
- defined = Defined} = State) ->
- case gb_sets:is_member({behaviour_info,1}, Defined) of
- false -> State;
+ optional_callbacks = OptionalCbs,
+ defined = Defined} = St0) ->
+ OptFun = fun({MFA, Line}, St) ->
+ case dict:is_key(MFA, Callbacks) of
+ true ->
+ St;
+ false ->
+ add_error(Line, {undefined_callback, MFA}, St)
+ end
+ end,
+ St1 = lists:foldl(OptFun, St0, dict:to_list(OptionalCbs)),
+ case gb_sets:is_member({behaviour_info, 1}, Defined) of
+ false -> St1;
true ->
case dict:size(Callbacks) of
- 0 -> State;
+ 0 -> St1;
_ ->
CallbacksList = dict:to_list(Callbacks),
FoldL =
- fun({Fa,Line},St) ->
+ fun({Fa, Line}, St) ->
add_error(Line, {behaviour_info, Fa}, St)
end,
- lists:foldl(FoldL, State, CallbacksList)
+ lists:foldl(FoldL, St1, CallbacksList)
end
end.
@@ -1266,7 +1333,7 @@ imported(F, A, St) ->
error -> no
end.
--spec on_load(line(), fa(), lint_state()) -> lint_state().
+-spec on_load(erl_anno:anno(), fa(), lint_state()) -> lint_state().
%% Check an on_load directive and remember it.
on_load(Line, {Name,Arity}=Fa, #lint{on_load=OnLoad0}=St0)
@@ -1301,14 +1368,15 @@ check_on_load(St) -> St.
-spec call_function(line(), atom(), arity(), lint_state()) -> lint_state().
%% Add to both called and calls.
-call_function(Line, F, A, #lint{usage=Usage0,called=Cd,func=Func}=St) ->
+call_function(Line, F, A, #lint{usage=Usage0,called=Cd,func=Func,file=File}=St) ->
#usage{calls = Cs} = Usage0,
NA = {F,A},
Usage = case Cs of
undefined -> Usage0;
_ -> Usage0#usage{calls=dict:append(Func, NA, Cs)}
end,
- St#lint{called=[{NA,Line}|Cd], usage=Usage}.
+ Anno = erl_anno:set_file(File, Line),
+ St#lint{called=[{NA,Anno}|Cd], usage=Usage}.
%% function(Line, Name, Arity, Clauses, State) -> State.
@@ -1404,20 +1472,7 @@ pattern({cons,_Line,H,T}, Vt, Old, Bvt, St0) ->
pattern({tuple,_Line,Ps}, Vt, Old, Bvt, St) ->
pattern_list(Ps, Vt, Old, Bvt, St);
pattern({map,_Line,Ps}, Vt, Old, Bvt, St) ->
- foldl(fun
- ({map_field_assoc,L,_,_}, {Psvt,Bvt0,St0}) ->
- {Psvt,Bvt0,add_error(L, illegal_pattern, St0)};
- ({map_field_exact,L,KP,VP}, {Psvt,Bvt0,St0}) ->
- case is_valid_map_key(KP, pattern, St0) of
- true ->
- {Pvt,Bvt1,St1} = pattern(VP, Vt, Old, Bvt, St0),
- {vtmerge_pat(Pvt, Psvt),vtmerge_pat(Bvt0, Bvt1), St1};
- false ->
- {Psvt,Bvt0,add_error(L, illegal_map_key, St0)};
- {false,variable,Var} ->
- {Psvt,Bvt0,add_error(L, {illegal_map_key_variable,Var}, St0)}
- end
- end, {[],[],St}, Ps);
+ pattern_map(Ps, Vt, Old, Bvt, St);
%%pattern({struct,_Line,_Tag,Ps}, Vt, Old, Bvt, St) ->
%% pattern_list(Ps, Vt, Old, Bvt, St);
pattern({record_index,Line,Name,Field}, _Vt, _Old, _Bvt, St) ->
@@ -1447,7 +1502,7 @@ pattern({op,_Line,'++',{string,_Li,_S},R}, Vt, Old, Bvt, St) ->
pattern({match,_Line,Pat1,Pat2}, Vt, Old, Bvt, St0) ->
{Lvt,Bvt1,St1} = pattern(Pat1, Vt, Old, Bvt, St0),
{Rvt,Bvt2,St2} = pattern(Pat2, Vt, Old, Bvt, St1),
- St3 = reject_bin_alias(Pat1, Pat2, St2),
+ St3 = reject_invalid_alias(Pat1, Pat2, Vt, St2),
{vtmerge_pat(Lvt, Rvt),vtmerge_pat(Bvt1,Bvt2),St3};
%% Catch legal constant expressions, including unary +,-.
pattern(Pat, _Vt, _Old, _Bvt, St) ->
@@ -1462,56 +1517,77 @@ pattern_list(Ps, Vt, Old, Bvt0, St) ->
{vtmerge_pat(Pvt, Psvt),vtmerge_pat(Bvt,Bvt1),St1}
end, {[],[],St}, Ps).
-%% reject_bin_alias(Pat, Expr, St) -> St'
+
+
+%% reject_invalid_alias(Pat, Expr, Vt, St) -> St'
%% Reject aliases for binary patterns at the top level.
+%% Reject aliases for maps patterns at the top level.
+%% The variables table (Vt) are for maps checkking.
+
+reject_invalid_alias_expr({bin,_,_}=P, {match,_,P0,E}, Vt, St0) ->
+ St = reject_invalid_alias(P, P0, Vt, St0),
+ reject_invalid_alias_expr(P, E, Vt, St);
+reject_invalid_alias_expr({map,_,_}=P, {match,_,P0,E}, Vt, St0) ->
+ St = reject_invalid_alias(P, P0, Vt, St0),
+ reject_invalid_alias_expr(P, E, Vt, St);
+reject_invalid_alias_expr({match,_,_,_}=P, {match,_,P0,E}, Vt, St0) ->
+ St = reject_invalid_alias(P, P0, Vt, St0),
+ reject_invalid_alias_expr(P, E, Vt, St);
+reject_invalid_alias_expr(_, _, _, St) -> St.
-reject_bin_alias_expr({bin,_,_}=P, {match,_,P0,E}, St0) ->
- St = reject_bin_alias(P, P0, St0),
- reject_bin_alias_expr(P, E, St);
-reject_bin_alias_expr({match,_,_,_}=P, {match,_,P0,E}, St0) ->
- St = reject_bin_alias(P, P0, St0),
- reject_bin_alias_expr(P, E, St);
-reject_bin_alias_expr(_, _, St) -> St.
-%% reject_bin_alias(Pat1, Pat2, St) -> St'
+%% reject_invalid_alias(Pat1, Pat2, St) -> St'
%% Aliases of binary patterns, such as <<A:8>> = <<B:4,C:4>> or even
%% <<A:8>> = <<A:8>>, are not allowed. Traverse the patterns in parallel
%% and generate an error if any binary aliases are found.
%% We generate an error even if is obvious that the overall pattern can't
%% possibly match, for instance, {a,<<A:8>>,c}={x,<<A:8>>} WILL generate an
%% error.
+%% Maps should reject unbound variables here.
-reject_bin_alias({bin,Line,_}, {bin,_,_}, St) ->
+reject_invalid_alias({bin,Line,_}, {bin,_,_}, _, St) ->
add_error(Line, illegal_bin_pattern, St);
-reject_bin_alias({cons,_,H1,T1}, {cons,_,H2,T2}, St0) ->
- St = reject_bin_alias(H1, H2, St0),
- reject_bin_alias(T1, T2, St);
-reject_bin_alias({tuple,_,Es1}, {tuple,_,Es2}, St) ->
- reject_bin_alias_list(Es1, Es2, St);
-reject_bin_alias({record,_,Name1,Pfs1}, {record,_,Name2,Pfs2},
+reject_invalid_alias({map,_Line,Ps1}, {map,_,Ps2}, Vt, St0) ->
+ Fun = fun ({map_field_exact,L,{var,_,K},_V}, Sti) ->
+ case is_var_bound(K,Vt) of
+ true ->
+ Sti;
+ false ->
+ add_error(L, {unbound_var,K}, Sti)
+ end;
+ ({map_field_exact,_L,_K,_V}, Sti) ->
+ Sti
+ end,
+ foldl(Fun, foldl(Fun, St0, Ps1), Ps2);
+reject_invalid_alias({cons,_,H1,T1}, {cons,_,H2,T2}, Vt, St0) ->
+ St = reject_invalid_alias(H1, H2, Vt, St0),
+ reject_invalid_alias(T1, T2, Vt, St);
+reject_invalid_alias({tuple,_,Es1}, {tuple,_,Es2}, Vt, St) ->
+ reject_invalid_alias_list(Es1, Es2, Vt, St);
+reject_invalid_alias({record,_,Name1,Pfs1}, {record,_,Name2,Pfs2}, Vt,
#lint{records=Recs}=St) ->
case {dict:find(Name1, Recs),dict:find(Name2, Recs)} of
{{ok,{_Line1,Fields1}},{ok,{_Line2,Fields2}}} ->
- reject_bin_alias_rec(Pfs1, Pfs2, Fields1, Fields2, St);
+ reject_invalid_alias_rec(Pfs1, Pfs2, Fields1, Fields2, Vt, St);
{_,_} ->
%% One or more non-existing records. (An error messages has
%% already been generated, so we are done here.)
St
end;
-reject_bin_alias({match,_,P1,P2}, P, St0) ->
- St = reject_bin_alias(P1, P, St0),
- reject_bin_alias(P2, P, St);
-reject_bin_alias(P, {match,_,_,_}=M, St) ->
- reject_bin_alias(M, P, St);
-reject_bin_alias(_P1, _P2, St) -> St.
-
-reject_bin_alias_list([E1|Es1], [E2|Es2], St0) ->
- St = reject_bin_alias(E1, E2, St0),
- reject_bin_alias_list(Es1, Es2, St);
-reject_bin_alias_list(_, _, St) -> St.
-
-reject_bin_alias_rec(PfsA0, PfsB0, FieldsA0, FieldsB0, St) ->
+reject_invalid_alias({match,_,P1,P2}, P, Vt, St0) ->
+ St = reject_invalid_alias(P1, P, Vt, St0),
+ reject_invalid_alias(P2, P, Vt, St);
+reject_invalid_alias(P, {match,_,_,_}=M, Vt, St) ->
+ reject_invalid_alias(M, P, Vt, St);
+reject_invalid_alias(_P1, _P2, _Vt, St) -> St.
+
+reject_invalid_alias_list([E1|Es1], [E2|Es2], Vt, St0) ->
+ St = reject_invalid_alias(E1, E2, Vt, St0),
+ reject_invalid_alias_list(Es1, Es2, Vt, St);
+reject_invalid_alias_list(_, _, _, St) -> St.
+
+reject_invalid_alias_rec(PfsA0, PfsB0, FieldsA0, FieldsB0, Vt, St) ->
%% We treat records as if they have been converted to tuples.
PfsA1 = rbia_field_vars(PfsA0),
PfsB1 = rbia_field_vars(PfsB0),
@@ -1527,7 +1603,7 @@ reject_bin_alias_rec(PfsA0, PfsB0, FieldsA0, FieldsB0, St) ->
D = sofs:projection({external,fun({_,_,P1,_,P2}) -> {P1,P2} end}, C),
E = sofs:to_external(D),
{Ps1,Ps2} = lists:unzip(E),
- reject_bin_alias_list(Ps1, Ps2, St).
+ reject_invalid_alias_list(Ps1, Ps2, Vt, St).
rbia_field_vars(Fs) ->
[{Name,Pat} || {record_field,_,{atom,_,Name},Pat} <- Fs].
@@ -1571,6 +1647,21 @@ is_pattern_expr_1({op,_Line,Op,A1,A2}) ->
erl_internal:arith_op(Op, 2) andalso all(fun is_pattern_expr/1, [A1,A2]);
is_pattern_expr_1(_Other) -> false.
+pattern_map(Ps, Vt, Old, Bvt, St) ->
+ foldl(fun
+ ({map_field_assoc,L,_,_}, {Psvt,Bvt0,St0}) ->
+ {Psvt,Bvt0,add_error(L, illegal_pattern, St0)};
+ ({map_field_exact,L,K,V}, {Psvt,Bvt0,St0}) ->
+ case is_valid_map_key(K) of
+ true ->
+ {Kvt,St1} = expr(K, Vt, St0),
+ {Vvt,Bvt2,St2} = pattern(V, Vt, Old, Bvt, St1),
+ {vtmerge_pat(vtmerge_pat(Kvt, Vvt), Psvt), vtmerge_pat(Bvt0, Bvt2), St2};
+ false ->
+ {Psvt,Bvt0,add_error(L, illegal_map_key, St0)}
+ end
+ end, {[],[],St}, Ps).
+
%% pattern_bin([Element], VarTable, Old, BinVarTable, State) ->
%% {UpdVarTable,UpdBinVarTable,State}.
%% Check a pattern group. BinVarTable are used binsize variables.
@@ -1919,10 +2010,10 @@ is_guard_test(E) ->
is_guard_test(Expression, Forms) ->
RecordAttributes = [A || A = {attribute, _, record, _D} <- Forms],
St0 = foldl(fun(Attr0, St1) ->
- Attr = zip_file_and_line(Attr0, "none"),
+ Attr = set_file(Attr0, "none"),
attribute_state(Attr, St1)
end, start(), RecordAttributes),
- is_guard_test2(zip_file_and_line(Expression, "nofile"), St0#lint.records).
+ is_guard_test2(set_file(Expression, "nofile"), St0#lint.records).
%% is_guard_test2(Expression, RecordDefs :: dict:dict()) -> boolean().
is_guard_test2({call,Line,{atom,Lr,record},[E,A]}, RDs) ->
@@ -2085,8 +2176,8 @@ expr({'receive',Line,Cs,To,ToEs}, Vt, St0) ->
{Cvt,St3} = icrt_clauses(Cs, Vt, St2),
%% Csvts = [vtnew(Tevt, Vt)|Cvt], %This is just NEW variables!
Csvts = [Tevt|Cvt],
- {Rvt,St4} = icrt_export(Csvts, Vt, {'receive',Line}, St3),
- {vtmerge([Tvt,Tevt,Rvt]),St4};
+ Rvt = icrt_export(Csvts, Vt, {'receive',Line}, St3),
+ {vtmerge([Tvt,Tevt,Rvt]),St3};
expr({'fun',Line,Body}, Vt, St) ->
%%No one can think funs export!
case Body of
@@ -2197,25 +2288,24 @@ expr({'try',Line,Es,Scs,Ccs,As}, Vt, St0) ->
%% passes cannot handle exports in combination with 'after'.
{Evt0,St1} = exprs(Es, Vt, St0),
TryLine = {'try',Line},
- Uvt = vtunsafe(vtnames(vtnew(Evt0, Vt)), TryLine, []),
- Evt1 = vtupdate(Uvt, vtsubtract(Evt0, Uvt)),
+ Uvt = vtunsafe(TryLine, Evt0, Vt),
+ Evt1 = vtupdate(Uvt, Evt0),
{Sccs,St2} = icrt_clauses(Scs++Ccs, TryLine, vtupdate(Evt1, Vt), St1),
Rvt0 = Sccs,
- Rvt1 = vtupdate(vtunsafe(vtnames(vtnew(Rvt0, Vt)), TryLine, []), Rvt0),
+ Rvt1 = vtupdate(vtunsafe(TryLine, Rvt0, Vt), Rvt0),
Evt2 = vtmerge(Evt1, Rvt1),
{Avt0,St} = exprs(As, vtupdate(Evt2, Vt), St2),
- Avt1 = vtupdate(vtunsafe(vtnames(vtnew(Avt0, Vt)), TryLine, []), Avt0),
+ Avt1 = vtupdate(vtunsafe(TryLine, Avt0, Vt), Avt0),
Avt = vtmerge(Evt2, Avt1),
{Avt,St};
expr({'catch',Line,E}, Vt, St0) ->
%% No new variables added, flag new variables as unsafe.
- {Evt,St1} = expr(E, Vt, St0),
- Uvt = vtunsafe(vtnames(vtnew(Evt, Vt)), {'catch',Line}, []),
- {vtupdate(Uvt,vtupdate(Evt, Vt)),St1};
+ {Evt,St} = expr(E, Vt, St0),
+ {vtupdate(vtunsafe({'catch',Line}, Evt, Vt), Evt),St};
expr({match,_Line,P,E}, Vt, St0) ->
{Evt,St1} = expr(E, Vt, St0),
{Pvt,Bvt,St2} = pattern(P, vtupdate(Evt, Vt), St1),
- St = reject_bin_alias_expr(P, E, St2),
+ St = reject_invalid_alias_expr(P, E, Vt, St2),
{vtupdate(Bvt, vtmerge(Evt, Pvt)),St};
%% No comparison or boolean operators yet.
expr({op,_Line,_Op,A}, Vt, St) ->
@@ -2224,9 +2314,8 @@ expr({op,Line,Op,L,R}, Vt, St0) when Op =:= 'orelse'; Op =:= 'andalso' ->
{Evt1,St1} = expr(L, Vt, St0),
Vt1 = vtupdate(Evt1, Vt),
{Evt2,St2} = expr(R, Vt1, St1),
- Vt2 = vtmerge(Evt2, Vt1),
- {Vt3,St3} = icrt_export([Vt1,Vt2], Vt1, {Op,Line}, St2),
- {vtmerge(Evt1, Vt3),St3};
+ Evt3 = vtupdate(vtunsafe({Op,Line}, Evt2, Vt1), Evt2),
+ {vtmerge(Evt1, Evt3),St2};
expr({op,_Line,_Op,L,R}, Vt, St) ->
expr_list([L,R], Vt, St); %They see the same variables
%% The following are not allowed to occur anywhere!
@@ -2237,11 +2326,10 @@ expr({remote,Line,_M,_F}, _Vt, St) ->
%% {UsedVarTable,State}
expr_list(Es, Vt, St) ->
- {Vt1,St1} = foldl(fun (E, {Esvt,St0}) ->
- {Evt,St1} = expr(E, Vt, St0),
- {vtmerge_pat(Evt, Esvt),St1}
- end, {[],St}, Es),
- {vtmerge(vtnew(Vt1, Vt), vtold(Vt1, Vt)),St1}.
+ foldl(fun (E, {Esvt,St0}) ->
+ {Evt,St1} = expr(E, Vt, St0),
+ {vtmerge_pat(Evt, Esvt),St1}
+ end, {[],St}, Es).
record_expr(Line, Rec, Vt, St0) ->
St1 = warn_invalid_record(Line, Rec, St0),
@@ -2254,18 +2342,13 @@ check_assoc_fields([{map_field_assoc,_,_,_}|Fs], St) ->
check_assoc_fields([], St) ->
St.
-map_fields([{Tag,Line,K,V}|Fs], Vt, St, F) when Tag =:= map_field_assoc;
- Tag =:= map_field_exact ->
- St1 = case is_valid_map_key(K, St) of
- true -> St;
- false -> add_error(Line, illegal_map_key, St);
- {false,variable,Var} -> add_error(Line, {illegal_map_key_variable,Var}, St)
- end,
- {Pvt,St2} = F([K,V], Vt, St1),
+map_fields([{Tag,_,K,V}|Fs], Vt, St, F) when Tag =:= map_field_assoc;
+ Tag =:= map_field_exact ->
+ {Pvt,St2} = F([K,V], Vt, St),
{Vts,St3} = map_fields(Fs, Vt, St2, F),
{vtupdate(Pvt, Vts),St3};
-map_fields([], Vt, St, _) ->
- {Vt,St}.
+map_fields([], _, St, _) ->
+ {[],St}.
%% warn_invalid_record(Line, Record, State0) -> State
%% Adds warning if the record is invalid.
@@ -2319,21 +2402,14 @@ is_valid_call(Call) ->
_ -> true
end.
-%% is_valid_map_key(K,St) -> true | false | {false, Var::atom()}
-%% check for value expression without variables
-
-is_valid_map_key(K,St) ->
- is_valid_map_key(K,expr,St).
-is_valid_map_key(K,Ctx,St) ->
- case expr(K,[],St) of
- {[],_} ->
- is_valid_map_key_value(K,Ctx);
- {[Var|_],_} ->
- {false,variable,element(1,Var)}
- end.
+%% is_valid_map_key(K) -> true | false
+%% variables are allowed for patterns only at the top of the tree
-is_valid_map_key_value(K,Ctx) ->
+is_valid_map_key({var,_,_}) -> true;
+is_valid_map_key(K) -> is_valid_map_key_value(K).
+is_valid_map_key_value(K) ->
case K of
+ {var,_,_} -> false;
{char,_,_} -> true;
{integer,_,_} -> true;
{float,_,_} -> true;
@@ -2341,36 +2417,36 @@ is_valid_map_key_value(K,Ctx) ->
{nil,_} -> true;
{atom,_,_} -> true;
{cons,_,H,T} ->
- is_valid_map_key_value(H,Ctx) andalso
- is_valid_map_key_value(T,Ctx);
+ is_valid_map_key_value(H) andalso
+ is_valid_map_key_value(T);
{tuple,_,Es} ->
foldl(fun(E,B) ->
- B andalso is_valid_map_key_value(E,Ctx)
+ B andalso is_valid_map_key_value(E)
end,true,Es);
{map,_,Arg,Ps} ->
% only check for value expressions to be valid
% invalid map expressions are later checked in
% core and kernel
- is_valid_map_key_value(Arg,Ctx) andalso foldl(fun
+ is_valid_map_key_value(Arg) andalso foldl(fun
({Tag,_,Ke,Ve},B) when Tag =:= map_field_assoc;
- Tag =:= map_field_exact, Ctx =:= expr ->
- B andalso is_valid_map_key_value(Ke,Ctx)
- andalso is_valid_map_key_value(Ve,Ctx);
+ Tag =:= map_field_exact ->
+ B andalso is_valid_map_key_value(Ke)
+ andalso is_valid_map_key_value(Ve);
(_,_) -> false
end,true,Ps);
{map,_,Ps} ->
foldl(fun
({Tag,_,Ke,Ve},B) when Tag =:= map_field_assoc;
- Tag =:= map_field_exact, Ctx =:= expr ->
- B andalso is_valid_map_key_value(Ke,Ctx)
- andalso is_valid_map_key_value(Ve,Ctx);
+ Tag =:= map_field_exact ->
+ B andalso is_valid_map_key_value(Ke)
+ andalso is_valid_map_key_value(Ve);
(_,_) -> false
end, true, Ps);
{record,_,_,Fs} ->
foldl(fun
({record_field,_,Ke,Ve},B) ->
- B andalso is_valid_map_key_value(Ke,Ctx)
- andalso is_valid_map_key_value(Ve,Ctx)
+ B andalso is_valid_map_key_value(Ke)
+ andalso is_valid_map_key_value(Ve)
end,true,Fs);
{bin,_,Es} ->
% only check for value expressions to be valid
@@ -2378,9 +2454,9 @@ is_valid_map_key_value(K,Ctx) ->
% core and kernel
foldl(fun
({bin_element,_,E,_,_},B) ->
- B andalso is_valid_map_key_value(E,Ctx)
+ B andalso is_valid_map_key_value(E)
end,true,Es);
- _ -> false
+ Val -> is_pattern_expr(Val)
end.
%% record_def(Line, RecordName, [RecField], State) -> State.
@@ -2392,7 +2468,10 @@ record_def(Line, Name, Fs0, St0) ->
true -> add_error(Line, {redefine_record,Name}, St0);
false ->
{Fs1,St1} = def_fields(normalise_fields(Fs0), Name, St0),
- St1#lint{records=dict:store(Name, {Line,Fs1}, St1#lint.records)}
+ St2 = St1#lint{records=dict:store(Name, {Line,Fs1},
+ St1#lint.records)},
+ Types = [T || {typed_record_field, _, T} <- Fs0],
+ check_type({type, nowarn(), product, Types}, St2)
end.
%% def_fields([RecDef], RecordName, State) -> {[DefField],State}.
@@ -2595,11 +2674,8 @@ find_field(_F, []) -> error.
%% Attr :: 'type' | 'opaque'
%% Checks that a type definition is valid.
-type_def(_Attr, _Line, {record, _RecName}, Fields, [], St0) ->
- %% The record field names and such are checked in the record format.
- %% We only need to check the types.
- Types = [T || {typed_record_field, _, T} <- Fields],
- check_type({type, -1, product, Types}, St0);
+-dialyzer({no_match, type_def/6}).
+
type_def(Attr, Line, TypeName, ProtoType, Args, St0) ->
TypeDefs = St0#lint.types,
Arity = length(Args),
@@ -2608,37 +2684,28 @@ type_def(Attr, Line, TypeName, ProtoType, Args, St0) ->
StoreType =
fun(St) ->
NewDefs = dict:store(TypePair, Info, TypeDefs),
- CheckType = {type, -1, product, [ProtoType|Args]},
+ CheckType = {type, nowarn(), product, [ProtoType|Args]},
check_type(CheckType, St#lint{types=NewDefs})
end,
case is_default_type(TypePair) of
true ->
case is_obsolete_builtin_type(TypePair) of
true -> StoreType(St0);
- false -> add_error(Line, {builtin_type, TypePair}, St0)
-%% case is_newly_introduced_builtin_type(TypePair) of
-%% %% allow some types just for bootstrapping
-%% true ->
-%% Warn = {new_builtin_type, TypePair},
-%% St1 = add_warning(Line, Warn, St0),
-%% StoreType(St1);
-%% false ->
-%% add_error(Line, {builtin_type, TypePair}, St0)
-%% end
+ false ->
+ case is_newly_introduced_builtin_type(TypePair) of
+ %% allow some types just for bootstrapping
+ true ->
+ Warn = {new_builtin_type, TypePair},
+ St1 = add_warning(Line, Warn, St0),
+ StoreType(St1);
+ false ->
+ add_error(Line, {builtin_type, TypePair}, St0)
+ end
end;
false ->
- case
- dict:is_key(TypePair, TypeDefs) orelse
- is_var_arity_type(TypeName)
- of
+ case dict:is_key(TypePair, TypeDefs) of
true ->
- case is_newly_introduced_var_arity_type(TypeName) of
- true ->
- Warn = {new_var_arity_type, TypeName},
- add_warning(Line, Warn, St0);
- false ->
- add_error(Line, {redefine_type, TypePair}, St0)
- end;
+ add_error(Line, {redefine_type, TypePair}, St0);
false ->
St1 = case
Attr =:= opaque andalso
@@ -2670,12 +2737,12 @@ check_type(Types, St) ->
check_type({ann_type, _L, [_Var, Type]}, SeenVars, St) ->
check_type(Type, SeenVars, St);
-check_type({paren_type, _L, [Type]}, SeenVars, St) ->
- check_type(Type, SeenVars, St);
check_type({remote_type, L, [{atom, _, Mod}, {atom, _, Name}, Args]},
- SeenVars, #lint{module=CurrentMod} = St) ->
+ SeenVars, St0) ->
+ St = deprecated_type(L, Mod, Name, Args, St0),
+ CurrentMod = St#lint.module,
case Mod =:= CurrentMod of
- true -> check_type({type, L, Name, Args}, SeenVars, St);
+ true -> check_type({user_type, L, Name, Args}, SeenVars, St);
false ->
lists:foldl(fun(T, {AccSeenVars, AccSt}) ->
check_type(T, AccSeenVars, AccSt)
@@ -2701,7 +2768,7 @@ check_type({type, L, 'fun', [Dom, Range]}, SeenVars, St) ->
{type, _, any} -> St;
_ -> add_error(L, {type_syntax, 'fun'}, St)
end,
- check_type({type, -1, product, [Dom, Range]}, SeenVars, St1);
+ check_type({type, nowarn(), product, [Dom, Range]}, SeenVars, St1);
check_type({type, L, range, [From, To]}, SeenVars, St) ->
St1 =
case {erl_eval:partial_eval(From), erl_eval:partial_eval(To)} of
@@ -2709,13 +2776,14 @@ check_type({type, L, range, [From, To]}, SeenVars, St) ->
_ -> add_error(L, {type_syntax, range}, St)
end,
{SeenVars, St1};
-check_type({type, _L, map, any}, SeenVars, St) -> {SeenVars, St};
+check_type({type, _L, map, any}, SeenVars, St) ->
+ {SeenVars, St};
check_type({type, _L, map, Pairs}, SeenVars, St) ->
lists:foldl(fun(Pair, {AccSeenVars, AccSt}) ->
check_type(Pair, AccSeenVars, AccSt)
end, {SeenVars, St}, Pairs);
-check_type({type, _L, map_field_assoc, Dom, Range}, SeenVars, St) ->
- check_type({type, -1, product, [Dom, Range]}, SeenVars, St);
+check_type({type, _L, map_field_assoc, [Dom, Range]}, SeenVars, St) ->
+ check_type({type, nowarn(), product, [Dom, Range]}, SeenVars, St);
check_type({type, _L, tuple, any}, SeenVars, St) -> {SeenVars, St};
check_type({type, _L, any}, SeenVars, St) -> {SeenVars, St};
check_type({type, L, binary, [Base, Unit]}, SeenVars, St) ->
@@ -2733,41 +2801,41 @@ check_type({type, L, record, [Name|Fields]}, SeenVars, St) ->
check_record_types(L, Atom, Fields, SeenVars, St1);
_ -> {SeenVars, add_error(L, {type_syntax, record}, St)}
end;
-check_type({type, _L, product, Args}, SeenVars, St) ->
+check_type({type, _L, Tag, Args}, SeenVars, St) when Tag =:= product;
+ Tag =:= union;
+ Tag =:= tuple ->
lists:foldl(fun(T, {AccSeenVars, AccSt}) ->
check_type(T, AccSeenVars, AccSt)
end, {SeenVars, St}, Args);
check_type({type, La, TypeName, Args}, SeenVars, St) ->
- #lint{usage=Usage, module = Module, types=Types} = St,
+ #lint{module = Module, types=Types} = St,
Arity = length(Args),
TypePair = {TypeName, Arity},
- St1 = case is_var_arity_type(TypeName) of
- true -> St;
- false ->
- Obsolete = (is_warn_enabled(deprecated_type, St)
- andalso obsolete_builtin_type(TypePair)),
- IsObsolete =
- case Obsolete of
- {deprecated, Repl, _} when element(1, Repl) =/= Module ->
- case dict:find(TypePair, Types) of
- {ok, _} -> false;
- error -> true
- end;
- _ -> false
- end,
- case IsObsolete of
- true ->
+ Obsolete = (is_warn_enabled(deprecated_type, St)
+ andalso obsolete_builtin_type(TypePair)),
+ St1 = case Obsolete of
+ {deprecated, Repl, _} when element(1, Repl) =/= Module ->
+ case dict:find(TypePair, Types) of
+ {ok, _} ->
+ used_type(TypePair, La, St);
+ error ->
{deprecated, Replacement, Rel} = Obsolete,
Tag = deprecated_builtin_type,
W = {Tag, TypePair, Replacement, Rel},
- add_warning(La, W, St);
- false ->
- OldUsed = Usage#usage.used_types,
- UsedTypes = dict:store(TypePair, La, OldUsed),
- St#lint{usage=Usage#usage{used_types=UsedTypes}}
- end
- end,
- check_type({type, -1, product, Args}, SeenVars, St1);
+ add_warning(La, W, St)
+ end;
+ _ -> St
+ end,
+ check_type({type, nowarn(), product, Args}, SeenVars, St1);
+check_type({user_type, L, TypeName, Args}, SeenVars, St) ->
+ Arity = length(Args),
+ TypePair = {TypeName, Arity},
+ St1 = used_type(TypePair, L, St),
+ lists:foldl(fun(T, {AccSeenVars, AccSt}) ->
+ check_type(T, AccSeenVars, AccSt)
+ end, {SeenVars, St1}, Args);
+check_type([{typed_record_field,Field,_T}|_], SeenVars, St) ->
+ {SeenVars, add_error(element(2, Field), old_abstract_code, St)};
check_type(I, SeenVars, St) ->
case erl_eval:partial_eval(I) of
{integer,_ILn,_Integer} -> {SeenVars, St};
@@ -2809,95 +2877,22 @@ check_record_types([{type, _, field_type, [{atom, AL, FName}, Type]}|Left],
check_record_types([], _Name, _DefFields, SeenVars, St, _SeenFields) ->
{SeenVars, St}.
-is_var_arity_type(tuple) -> true;
-is_var_arity_type(map) -> true;
-is_var_arity_type(product) -> true;
-is_var_arity_type(union) -> true;
-is_var_arity_type(record) -> true;
-is_var_arity_type(_) -> false.
-
-is_default_type({any, 0}) -> true;
-is_default_type({arity, 0}) -> true;
-is_default_type({array, 0}) -> true;
-is_default_type({atom, 0}) -> true;
-is_default_type({atom, 1}) -> true;
-is_default_type({binary, 0}) -> true;
-is_default_type({binary, 2}) -> true;
-is_default_type({bitstring, 0}) -> true;
-is_default_type({bool, 0}) -> true;
-is_default_type({boolean, 0}) -> true;
-is_default_type({byte, 0}) -> true;
-is_default_type({char, 0}) -> true;
-is_default_type({dict, 0}) -> true;
-is_default_type({digraph, 0}) -> true;
-is_default_type({float, 0}) -> true;
-is_default_type({'fun', 0}) -> true;
-is_default_type({'fun', 2}) -> true;
-is_default_type({function, 0}) -> true;
-is_default_type({gb_set, 0}) -> true;
-is_default_type({gb_tree, 0}) -> true;
-is_default_type({identifier, 0}) -> true;
-is_default_type({integer, 0}) -> true;
-is_default_type({integer, 1}) -> true;
-is_default_type({iodata, 0}) -> true;
-is_default_type({iolist, 0}) -> true;
-is_default_type({list, 0}) -> true;
-is_default_type({list, 1}) -> true;
-is_default_type({maybe_improper_list, 0}) -> true;
-is_default_type({maybe_improper_list, 2}) -> true;
-is_default_type({mfa, 0}) -> true;
-is_default_type({module, 0}) -> true;
-is_default_type({neg_integer, 0}) -> true;
-is_default_type({nil, 0}) -> true;
-is_default_type({no_return, 0}) -> true;
-is_default_type({node, 0}) -> true;
-is_default_type({non_neg_integer, 0}) -> true;
-is_default_type({none, 0}) -> true;
-is_default_type({nonempty_list, 0}) -> true;
-is_default_type({nonempty_list, 1}) -> true;
-is_default_type({nonempty_improper_list, 2}) -> true;
-is_default_type({nonempty_maybe_improper_list, 0}) -> true;
-is_default_type({nonempty_maybe_improper_list, 2}) -> true;
-is_default_type({nonempty_string, 0}) -> true;
-is_default_type({number, 0}) -> true;
-is_default_type({pid, 0}) -> true;
-is_default_type({port, 0}) -> true;
-is_default_type({pos_integer, 0}) -> true;
-is_default_type({queue, 0}) -> true;
-is_default_type({range, 2}) -> true;
-is_default_type({reference, 0}) -> true;
-is_default_type({set, 0}) -> true;
-is_default_type({string, 0}) -> true;
-is_default_type({term, 0}) -> true;
-is_default_type({timeout, 0}) -> true;
-is_default_type({var, 1}) -> true;
-is_default_type(_) -> false.
-
-is_newly_introduced_var_arity_type(map) -> true;
-is_newly_introduced_var_arity_type(_) -> false.
-
-%% is_newly_introduced_builtin_type({Name, _}) when is_atom(Name) -> false.
+used_type(TypePair, L, #lint{usage = Usage, file = File} = St) ->
+ OldUsed = Usage#usage.used_types,
+ UsedTypes = dict:store(TypePair, erl_anno:set_file(File, L), OldUsed),
+ St#lint{usage=Usage#usage{used_types=UsedTypes}}.
+
+is_default_type({Name, NumberOfTypeVariables}) ->
+ erl_internal:is_type(Name, NumberOfTypeVariables).
+
+is_newly_introduced_builtin_type({Name, _}) when is_atom(Name) -> false.
is_obsolete_builtin_type(TypePair) ->
obsolete_builtin_type(TypePair) =/= no.
-%% Obsolete in OTP 17.0.
-obsolete_builtin_type({array, 0}) ->
- {deprecated, {array, array, 1}, "OTP 18.0"};
-obsolete_builtin_type({dict, 0}) ->
- {deprecated, {dict, dict, 2}, "OTP 18.0"};
-obsolete_builtin_type({digraph, 0}) ->
- {deprecated, {digraph, graph}, "OTP 18.0"};
-obsolete_builtin_type({gb_set, 0}) ->
- {deprecated, {gb_sets, set, 1}, "OTP 18.0"};
-obsolete_builtin_type({gb_tree, 0}) ->
- {deprecated, {gb_trees, tree, 2}, "OTP 18.0"};
-obsolete_builtin_type({queue, 0}) ->
- {deprecated, {queue, queue, 1}, "OTP 18.0"};
-obsolete_builtin_type({set, 0}) ->
- {deprecated, {sets, set, 1}, "OTP 18.0"};
-obsolete_builtin_type({tid, 0}) ->
- {deprecated, {ets, tid}, "OTP 18.0"};
+%% To keep Dialyzer silent...
+obsolete_builtin_type({1, 255}) ->
+ {deprecated, {2, 255}, ""};
obsolete_builtin_type({Name, A}) when is_atom(Name), is_integer(A) -> no.
%% spec_decl(Line, Fun, Types, State) -> State.
@@ -2909,25 +2904,60 @@ spec_decl(Line, MFA0, TypeSpecs, St0 = #lint{specs = Specs, module = Mod}) ->
end,
St1 = St0#lint{specs = dict:store(MFA, Line, Specs)},
case dict:is_key(MFA, Specs) of
- true -> add_error(Line, {redefine_spec, MFA}, St1);
- false -> check_specs(TypeSpecs, Arity, St1)
+ true -> add_error(Line, {redefine_spec, MFA0}, St1);
+ false -> check_specs(TypeSpecs, spec_wrong_arity, Arity, St1)
end.
%% callback_decl(Line, Fun, Types, State) -> State.
callback_decl(Line, MFA0, TypeSpecs,
St0 = #lint{callbacks = Callbacks, module = Mod}) ->
- MFA = case MFA0 of
- {F, Arity} -> {Mod, F, Arity};
- {_M, _F, Arity} -> MFA0
- end,
- St1 = St0#lint{callbacks = dict:store(MFA, Line, Callbacks)},
- case dict:is_key(MFA, Callbacks) of
- true -> add_error(Line, {redefine_callback, MFA}, St1);
- false -> check_specs(TypeSpecs, Arity, St1)
+ case MFA0 of
+ {_M, _F, _A} -> add_error(Line, {bad_callback, MFA0}, St0);
+ {F, Arity} ->
+ MFA = {Mod, F, Arity},
+ St1 = St0#lint{callbacks = dict:store(MFA, Line, Callbacks)},
+ case dict:is_key(MFA, Callbacks) of
+ true -> add_error(Line, {redefine_callback, MFA0}, St1);
+ false -> check_specs(TypeSpecs, callback_wrong_arity,
+ Arity, St1)
+ end
end.
-check_specs([FunType|Left], Arity, St0) ->
+%% optional_callbacks(Line, FAs, State) -> State.
+
+optional_callbacks(Line, Term, St0) ->
+ try true = is_fa_list(Term), Term of
+ FAs ->
+ optional_cbs(Line, FAs, St0)
+ catch
+ _:_ ->
+ St0 % ignore others
+ end.
+
+optional_cbs(_Line, [], St) ->
+ St;
+optional_cbs(Line, [{F,A}|FAs], St0) ->
+ #lint{optional_callbacks = OptionalCbs, module = Mod} = St0,
+ MFA = {Mod, F, A},
+ St1 = St0#lint{optional_callbacks = dict:store(MFA, Line, OptionalCbs)},
+ St2 = case dict:is_key(MFA, OptionalCbs) of
+ true ->
+ add_error(Line, {redefine_optional_callback, {F,A}}, St1);
+ false ->
+ St1
+ end,
+ optional_cbs(Line, FAs, St2).
+
+is_fa_list([E|L]) -> is_fa(E) andalso is_fa_list(L);
+is_fa_list([]) -> true;
+is_fa_list(_) -> false.
+
+is_fa({FuncName, Arity})
+ when is_atom(FuncName), is_integer(Arity), Arity >= 0 -> true;
+is_fa(_) -> false.
+
+check_specs([FunType|Left], ETag, Arity, St0) ->
{FunType1, CTypes} =
case FunType of
{type, _, bounded_fun, [FT = {type, _, 'fun', _}, Cs]} ->
@@ -2935,25 +2965,29 @@ check_specs([FunType|Left], Arity, St0) ->
{FT, lists:append(Types0)};
{type, _, 'fun', _} = FT -> {FT, []}
end,
- SpecArity =
- case FunType1 of
- {type, L, 'fun', [any, _]} -> any;
- {type, L, 'fun', [{type, _, product, D}, _]} -> length(D)
- end,
+ {type, L, 'fun', [{type, _, product, D}, _]} = FunType1,
+ SpecArity = length(D),
St1 = case Arity =:= SpecArity of
true -> St0;
- false -> add_error(L, spec_wrong_arity, St0)
+ false -> %% Cannot happen if called from the compiler.
+ add_error(L, ETag, St0)
end,
- St2 = check_type({type, -1, product, [FunType1|CTypes]}, St1),
- check_specs(Left, Arity, St2);
-check_specs([], _Arity, St) ->
+ St2 = check_type({type, nowarn(), product, [FunType1|CTypes]}, St1),
+ check_specs(Left, ETag, Arity, St2);
+check_specs([], _ETag, _Arity, St) ->
St.
+nowarn() ->
+ A0 = erl_anno:new(0),
+ A1 = erl_anno:set_generated(true, A0),
+ erl_anno:set_file("", A1).
+
check_specs_without_function(#lint{module=Mod,defined=Funcs,specs=Specs}=St) ->
- Fun = fun({M, F, A} = MFA, Line, AccSt) when M =:= Mod ->
- case gb_sets:is_element({F, A}, Funcs) of
+ Fun = fun({M, F, A}, Line, AccSt) when M =:= Mod ->
+ FA = {F, A},
+ case gb_sets:is_element(FA, Funcs) of
true -> AccSt;
- false -> add_error(Line, {spec_fun_undefined, MFA}, AccSt)
+ false -> add_error(Line, {spec_fun_undefined, FA}, AccSt)
end;
({_M, _F, _A}, _Line, AccSt) -> AccSt
end,
@@ -2982,9 +3016,10 @@ add_missing_spec_warnings(Forms, St0, Type) ->
[{FA,L} || {function,L,F,A,_} <- Forms,
not lists:member(FA = {F,A}, Specs)];
exported ->
- Exps = gb_sets:to_list(St0#lint.exports) -- pseudolocals(),
+ Exps0 = gb_sets:to_list(St0#lint.exports) -- pseudolocals(),
+ Exps = Exps0 -- Specs,
[{FA,L} || {function,L,F,A,_} <- Forms,
- member(FA = {F,A}, Exps -- Specs)]
+ member(FA = {F,A}, Exps)]
end,
foldl(fun ({FA,L}, St) ->
add_warning(L, {missing_spec,FA}, St)
@@ -2997,8 +3032,10 @@ check_unused_types(Forms, #lint{usage=Usage, types=Ts, exp_types=ExpTs}=St) ->
L = gb_sets:to_list(ExpTs) ++ dict:fetch_keys(D),
UsedTypes = gb_sets:from_list(L),
FoldFun =
- fun(Type, #typeinfo{line = FileLine}, AccSt) ->
- case loc(FileLine) of
+ fun({{record, _}=_Type, 0}, _, AccSt) ->
+ AccSt; % Before Erlang/OTP 19.0
+ (Type, #typeinfo{line = FileLine}, AccSt) ->
+ case loc(FileLine, AccSt) of
{FirstFile, _} ->
case gb_sets:is_member(Type, UsedTypes) of
true -> AccSt;
@@ -3032,11 +3069,12 @@ check_local_opaque_types(St) ->
dict:fold(FoldFun, St, Ts).
%% icrt_clauses(Clauses, In, ImportVarTable, State) ->
-%% {NewVts,State}.
+%% {UpdVt,State}.
icrt_clauses(Cs, In, Vt, St0) ->
{Csvt,St1} = icrt_clauses(Cs, Vt, St0),
- icrt_export(Csvt, Vt, In, St1).
+ UpdVt = icrt_export(Csvt, Vt, In, St1),
+ {UpdVt,St1}.
%% icrt_clauses(Clauses, ImportVarTable, State) ->
%% {NewVts,State}.
@@ -3046,26 +3084,73 @@ icrt_clauses(Cs, Vt, St) ->
icrt_clause({clause,_Line,H,G,B}, Vt0, St0) ->
{Hvt,Binvt,St1} = head(H, Vt0, St0),
- Vt1 = vtupdate(Hvt, vtupdate(Binvt, Vt0)),
- {Gvt,St2} = guard(G, Vt1, St1),
+ Vt1 = vtupdate(Hvt, Binvt),
+ {Gvt,St2} = guard(G, vtupdate(Vt1, Vt0), St1),
Vt2 = vtupdate(Gvt, Vt1),
- {Bvt,St3} = exprs(B, Vt2, St2),
+ {Bvt,St3} = exprs(B, vtupdate(Vt2, Vt0), St2),
{vtupdate(Bvt, Vt2),St3}.
-icrt_export(Csvt, Vt, In, St) ->
- Vt1 = vtmerge(Csvt),
- All = ordsets:subtract(vintersection(Csvt), vtnames(Vt)),
- Some = ordsets:subtract(vtnames(Vt1), vtnames(Vt)),
- Xvt = vtexport(All, In, []),
- Evt = vtunsafe(ordsets:subtract(Some, All), In, Xvt),
- Unused = vtmerge([unused_vars(Vt0, Vt, St) || Vt0 <- Csvt]),
- %% Exported and unsafe variables may be unused:
- Uvt = vtmerge(Evt, Unused),
- %% Make exported and unsafe unused variables unused in subsequent code:
- Vt2 = vtmerge(Uvt, vtsubtract(Vt1, Uvt)),
- %% Forget about old variables which were not used:
- Vt3 = vtmerge(vtnew(Vt2, Vt), vt_no_unused(vtold(Vt2, Vt))),
- {Vt3,St}.
+icrt_export(Vts, Vt, {Tag,Attrs}, St) ->
+ {_File,Loc} = loc(Attrs, St),
+ icrt_export(lists:merge(Vts), Vt, {Tag,Loc}, length(Vts), []).
+
+icrt_export([{V,{{export,_},_,_}}|Vs0], [{V,{{export,_}=S0,_,Ls}}|Vt],
+ In, I, Acc) ->
+ %% V was an exported variable and has been used in an expression in at least
+ %% one clause. Its state needs to be merged from all clauses to silence any
+ %% exported var warning already emitted.
+ {VVs,Vs} = lists:partition(fun ({K,_}) -> K =:= V end, Vs0),
+ S = foldl(fun ({_,{S1,_,_}}, AccS) -> merge_state(AccS, S1) end, S0, VVs),
+ icrt_export(Vs, Vt, In, I, [{V,{S,used,Ls}}|Acc]);
+icrt_export([{V,_}|Vs0], [{V,{_,_,Ls}}|Vt], In, I, Acc) ->
+ %% V was either unsafe or bound and has now been reused. It may also have
+ %% been an export but as it was not matched by the previous clause, it means
+ %% it has been changed to 'bound' in at least one clause because it was used
+ %% in a pattern.
+ Vs = lists:dropwhile(fun ({K,_}) -> K =:= V end, Vs0),
+ icrt_export(Vs, Vt, In, I, [{V,{bound,used,Ls}}|Acc]);
+icrt_export([{V1,_}|_]=Vs, [{V2,_}|Vt], In, I, Acc) when V1 > V2 ->
+ %% V2 was already in scope and has not been reused in any clause.
+ icrt_export(Vs, Vt, In, I, Acc);
+icrt_export([{V,_}|_]=Vs0, Vt, In, I, Acc) ->
+ %% V is a new variable.
+ {VVs,Vs} = lists:partition(fun ({K,_}) -> K =:= V end, Vs0),
+ F = fun ({_,{S,U,Ls}}, {AccI,AccS0,AccLs0}) ->
+ AccS = case {S,AccS0} of
+ {{unsafe,_},{unsafe,_}} ->
+ %% V was found unsafe in a previous clause, mark
+ %% it as unsafe for the whole parent expression.
+ {unsafe,In};
+ {{unsafe,_},_} ->
+ %% V was unsafe in a clause, keep that state and
+ %% generalize it to the whole expression if it
+ %% is found unsafe in another one.
+ S;
+ _ ->
+ %% V is either bound or exported, keep original
+ %% state.
+ AccS0
+ end,
+ AccLs = case U of
+ used -> AccLs0;
+ unused -> merge_lines(AccLs0, Ls)
+ end,
+ {AccI + 1,AccS,AccLs}
+ end,
+ %% Initial state is exported from the current expression.
+ {Count,S1,Ls} = foldl(F, {0,{export,In},[]}, VVs),
+ S = case Count of
+ I ->
+ %% V was found in all clauses, keep computed state.
+ S1;
+ _ ->
+ %% V was not bound in some clauses, mark as unsafe.
+ {unsafe,In}
+ end,
+ U = case Ls of [] -> used; _ -> unused end,
+ icrt_export(Vs, Vt, In, I, [{V,{S,U,Ls}}|Acc]);
+icrt_export([], _, _, _, Acc) ->
+ reverse(Acc).
handle_comprehension(E, Qs, Vt0, St0) ->
{Vt1, Uvt, St1} = lc_quals(Qs, Vt0, St0),
@@ -3136,8 +3221,8 @@ handle_generator(P,E,Vt,Uvt,St0) ->
handle_bitstring_gen_pat({bin,_,Segments=[_|_]},St) ->
case lists:last(Segments) of
{bin_element,Line,{var,_,_},default,Flags} when is_list(Flags) ->
- case member(binary, Flags) orelse member(bits, Flags)
- orelse member(bitstring, Flags) of
+ case member(binary, Flags) orelse member(bytes, Flags)
+ orelse member(bits, Flags) orelse member(bitstring, Flags) of
true ->
add_error(Line, unsized_binary_in_bin_gen_pattern, St);
false ->
@@ -3163,7 +3248,8 @@ fun_clauses(Cs, Vt, St) ->
{Cvt,St1} = fun_clause(C, Vt, St0),
{vtmerge(Cvt, Bvt0),St1}
end, {[],St#lint{recdef_top = false}}, Cs),
- {vt_no_unused(vtold(Bvt, Vt)),St2#lint{recdef_top = OldRecDef}}.
+ Uvt = vt_no_unsafe(vt_no_unused(vtold(Bvt, Vt))),
+ {Uvt,St2#lint{recdef_top = OldRecDef}}.
fun_clause({clause,_Line,H,G,B}, Vt0, St0) ->
{Hvt,Binvt,St1} = head(H, Vt0, [], St0), % No imported pattern variables
@@ -3277,19 +3363,24 @@ pat_binsize_var(V, Line, Vt, Bvt, St) ->
%% exported vars are probably safe, warn only if warn_export_vars is
%% set.
-expr_var(V, Line, Vt, St0) ->
+expr_var(V, Line, Vt, St) ->
case orddict:find(V, Vt) of
{ok,{bound,_Usage,Ls}} ->
- {[{V,{bound,used,Ls}}],St0};
+ {[{V,{bound,used,Ls}}],St};
{ok,{{unsafe,In},_Usage,Ls}} ->
{[{V,{bound,used,Ls}}],
- add_error(Line, {unsafe_var,V,In}, St0)};
+ add_error(Line, {unsafe_var,V,In}, St)};
{ok,{{export,From},_Usage,Ls}} ->
- {[{V,{bound,used,Ls}}],
- exported_var(Line, V, From, St0)};
+ case is_warn_enabled(export_vars, St) of
+ true ->
+ {[{V,{bound,used,Ls}}],
+ add_warning(Line, {exported_var,V,From}, St)};
+ false ->
+ {[{V,{{export,From},used,Ls}}],St}
+ end;
error ->
{[{V,{bound,used,[Line]}}],
- add_error(Line, {unbound_var,V}, St0)}
+ add_error(Line, {unbound_var,V}, St)}
end.
exported_var(Line, V, From, St) ->
@@ -3343,6 +3434,14 @@ warn_unused_vars(U, Vt, St0) ->
UVt = map(fun ({V,{State,_,Ls}}) -> {V,{State,used,Ls}} end, U),
{vtmerge(Vt, UVt), St1}.
+
+is_var_bound(V, Vt) ->
+ case orddict:find(V, Vt) of
+ {ok,{bound,_Usage,_}} -> true;
+ _ -> false
+ end.
+
+
%% vtupdate(UpdVarTable, VarTable) -> VarTable.
%% Add the variables in the updated vartable to VarTable. The variables
%% will be updated with their property in UpdVarTable. The state of
@@ -3353,17 +3452,12 @@ vtupdate(Uvt, Vt0) ->
{S, merge_used(U1, U2), merge_lines(L1, L2)}
end, Uvt, Vt0).
-%% vtexport([Variable], From, VarTable) -> VarTable.
-%% vtunsafe([Variable], From, VarTable) -> VarTable.
-%% Add the variables to VarTable either as exported from From or as unsafe.
-
-vtexport(Vs, {InTag,FileLine}, Vt0) ->
- {_File,Line} = loc(FileLine),
- vtupdate([{V,{{export,{InTag,Line}},unused,[]}} || V <- Vs], Vt0).
+%% vtunsafe(From, UpdVarTable, VarTable) -> UnsafeVarTable.
+%% Return all new variables in UpdVarTable as unsafe.
-vtunsafe(Vs, {InTag,FileLine}, Vt0) ->
- {_File,Line} = loc(FileLine),
- vtupdate([{V,{{unsafe,{InTag,Line}},unused,[]}} || V <- Vs], Vt0).
+vtunsafe({Tag,FileLine}, Uvt, Vt) ->
+ Line = erl_anno:location(FileLine),
+ [{V,{{unsafe,{Tag,Line}},U,Ls}} || {V,{_,U,Ls}} <- vtnew(Uvt, Vt)].
%% vtmerge(VarTable, VarTable) -> VarTable.
%% Merge two variables tables generating a new vartable. Give priority to
@@ -3416,8 +3510,6 @@ vtsubtract(New, Old) ->
vtold(New, Old) ->
orddict:filter(fun (V, _How) -> orddict:is_key(V, Old) end, New).
-vtnames(Vt) -> [ V || {V,_How} <- Vt ].
-
vt_no_unsafe(Vt) -> [V || {_,{S,_U,_L}}=V <- Vt,
case S of
{unsafe,_} -> false;
@@ -3426,84 +3518,11 @@ vt_no_unsafe(Vt) -> [V || {_,{S,_U,_L}}=V <- Vt,
vt_no_unused(Vt) -> [V || {_,{_,U,_L}}=V <- Vt, U =/= unused].
-%% vunion(VarTable1, VarTable2) -> [VarName].
-%% vunion([VarTable]) -> [VarName].
-%% vintersection(VarTable1, VarTable2) -> [VarName].
-%% vintersection([VarTable]) -> [VarName].
-%% Union/intersection of names of vars in VarTable.
-
--ifdef(NOTUSED).
-vunion(Vs1, Vs2) -> ordsets:union(vtnames(Vs1), vtnames(Vs2)).
-
-vunion(Vss) -> foldl(fun (Vs, Uvs) ->
- ordsets:union(vtnames(Vs), Uvs)
- end, [], Vss).
-
-vintersection(Vs1, Vs2) -> ordsets:intersection(vtnames(Vs1), vtnames(Vs2)).
--endif.
-
-vintersection([Vs]) ->
- vtnames(Vs); %Boundary conditions!!!
-vintersection([Vs|Vss]) ->
- ordsets:intersection(vtnames(Vs), vintersection(Vss));
-vintersection([]) ->
- [].
-
%% copy_expr(Expr, Line) -> Expr.
%% Make a copy of Expr converting all line numbers to Line.
-copy_expr(Expr, Line) ->
- modify_line(Expr, fun(_L) -> Line end).
-
-%% modify_line(Form, Fun) -> Form
-%% modify_line(Expression, Fun) -> Expression
-%% Applies Fun to each line number occurrence.
-
-modify_line(T, F0) ->
- modify_line1(T, F0).
-
-%% Forms.
-modify_line1({function,F,A}, _Mf) -> {function,F,A};
-modify_line1({function,M,F,A}, Mf) ->
- {function,modify_line1(M, Mf),modify_line1(F, Mf),modify_line1(A, Mf)};
-modify_line1({attribute,L,record,{Name,Fields}}, Mf) ->
- {attribute,Mf(L),record,{Name,modify_line1(Fields, Mf)}};
-modify_line1({attribute,L,spec,{Fun,Types}}, Mf) ->
- {attribute,Mf(L),spec,{Fun,modify_line1(Types, Mf)}};
-modify_line1({attribute,L,callback,{Fun,Types}}, Mf) ->
- {attribute,Mf(L),callback,{Fun,modify_line1(Types, Mf)}};
-modify_line1({attribute,L,type,{TypeName,TypeDef,Args}}, Mf) ->
- {attribute,Mf(L),type,{TypeName,modify_line1(TypeDef, Mf),
- modify_line1(Args, Mf)}};
-modify_line1({attribute,L,opaque,{TypeName,TypeDef,Args}}, Mf) ->
- {attribute,Mf(L),opaque,{TypeName,modify_line1(TypeDef, Mf),
- modify_line1(Args, Mf)}};
-modify_line1({attribute,L,Attr,Val}, Mf) -> {attribute,Mf(L),Attr,Val};
-modify_line1({warning,W}, _Mf) -> {warning,W};
-modify_line1({error,W}, _Mf) -> {error,W};
-%% Expressions.
-modify_line1({clauses,Cs}, Mf) -> {clauses,modify_line1(Cs, Mf)};
-modify_line1({typed_record_field,Field,Type}, Mf) ->
- {typed_record_field,modify_line1(Field, Mf),modify_line1(Type, Mf)};
-modify_line1({Tag,L}, Mf) -> {Tag,Mf(L)};
-modify_line1({Tag,L,E1}, Mf) ->
- {Tag,Mf(L),modify_line1(E1, Mf)};
-modify_line1({Tag,L,E1,E2}, Mf) ->
- {Tag,Mf(L),modify_line1(E1, Mf),modify_line1(E2, Mf)};
-modify_line1({bin_element,L,E1,E2,TSL}, Mf) ->
- {bin_element,Mf(L),modify_line1(E1, Mf),modify_line1(E2, Mf), TSL};
-modify_line1({Tag,L,E1,E2,E3}, Mf) ->
- {Tag,Mf(L),modify_line1(E1, Mf),modify_line1(E2, Mf),modify_line1(E3, Mf)};
-modify_line1({Tag,L,E1,E2,E3,E4}, Mf) ->
- {Tag,Mf(L),
- modify_line1(E1, Mf),
- modify_line1(E2, Mf),
- modify_line1(E3, Mf),
- modify_line1(E4, Mf)};
-modify_line1([H|T], Mf) ->
- [modify_line1(H, Mf)|modify_line1(T, Mf)];
-modify_line1([], _Mf) -> [];
-modify_line1(E, _Mf) when not is_tuple(E), not is_list(E) -> E.
+copy_expr(Expr, Anno) ->
+ erl_parse:map_anno(fun(_A) -> Anno end, Expr).
%% Check a record_info call. We have already checked that it is not
%% shadowed by an import.
@@ -3545,6 +3564,8 @@ check_qlc_hrl(Line, M, F, As, St) ->
%% deprecated_function(Line, ModName, FuncName, [Arg], State) -> State.
%% Add warning for calls to deprecated functions.
+-dialyzer({no_match, deprecated_function/5}).
+
deprecated_function(Line, M, F, As, St) ->
Arity = length(As),
MFA = {M, F, Arity},
@@ -3573,6 +3594,24 @@ deprecated_function(Line, M, F, As, St) ->
St
end.
+-dialyzer({no_match, deprecated_type/5}).
+
+deprecated_type(L, M, N, As, St) ->
+ NAs = length(As),
+ case otp_internal:obsolete_type(M, N, NAs) of
+ {deprecated, String} when is_list(String) ->
+ case is_warn_enabled(deprecated_type, St) of
+ true ->
+ add_warning(L, {deprecated_type, {M,N,NAs}, String}, St);
+ false ->
+ St
+ end;
+ {removed, Replacement, Rel} ->
+ add_warning(L, {removed_type, {M,N,NAs}, Replacement, Rel}, St);
+ no ->
+ St
+ end.
+
obsolete_guard({call,Line,{atom,Lr,F},As}, St0) ->
Arity = length(As),
case erl_internal:old_type_test(F, Arity) of
@@ -3800,8 +3839,7 @@ is_autoimport_suppressed(NoAutoSet,{Func,Arity}) ->
gb_sets:is_element({Func,Arity},NoAutoSet).
%% Predicate to find out if a function specific bif-clash suppression (old deprecated) is present
bif_clash_specifically_disabled(St,{F,A}) ->
- Nowarn = nowarn_function(nowarn_bif_clash, St#lint.compile),
- lists:member({F,A},Nowarn).
+ lists:member({F,A},St#lint.nowarn_bif_clash).
%% Predicate to find out if an autoimported guard_bif is not overriden in some way
%% Guard Bif without module name is disallowed if
diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl
index 1d4a2a1fef..a896de4f1c 100644
--- a/lib/stdlib/src/erl_parse.yrl
+++ b/lib/stdlib/src/erl_parse.yrl
@@ -2,18 +2,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -42,7 +43,6 @@ function_call argument_list
exprs guard
atomic strings
prefix_op mult_op add_op list_op comp_op
-rule rule_clauses rule_clause rule_body
binary bin_elements bin_element bit_expr
opt_bit_size_expr bit_size_expr opt_bit_type_list bit_type_list bit_type
top_type top_type_100 top_types type typed_expr typed_attr_val
@@ -54,7 +54,7 @@ bin_base_type bin_unit_type type_200 type_300 type_400 type_500.
Terminals
char integer float atom string var
-'(' ')' ',' '->' ':-' '{' '}' '[' ']' '|' '||' '<-' ';' ':' '#' '.'
+'(' ')' ',' '->' '{' '}' '[' ']' '|' '||' '<-' ';' ':' '#' '.'
'after' 'begin' 'case' 'try' 'catch' 'end' 'fun' 'if' 'of' 'receive' 'when'
'andalso' 'orelse'
'bnot' 'not'
@@ -73,7 +73,6 @@ Rootsymbol form.
form -> attribute dot : '$1'.
form -> function dot : '$1'.
-form -> rule dot : '$1'.
attribute -> '-' atom attr_val : build_attribute('$2', '$3').
attribute -> '-' atom typed_attr_val : build_typed_attribute('$2','$3').
@@ -86,15 +85,11 @@ type_spec -> '(' spec_fun type_sigs ')' : {'$2', '$3'}.
spec_fun -> atom : '$1'.
spec_fun -> atom ':' atom : {'$1', '$3'}.
-%% The following two are retained only for backwards compatibility;
-%% they are not part of the EEP syntax and should be removed.
-spec_fun -> atom '/' integer '::' : {'$1', '$3'}.
-spec_fun -> atom ':' atom '/' integer '::' : {'$1', '$3', '$5'}.
typed_attr_val -> expr ',' typed_record_fields : {typed_record, '$1', '$3'}.
typed_attr_val -> expr '::' top_type : {type_def, '$1', '$3'}.
-typed_record_fields -> '{' typed_exprs '}' : {tuple, ?line('$1'), '$2'}.
+typed_record_fields -> '{' typed_exprs '}' : {tuple, ?anno('$1'), '$2'}.
typed_exprs -> typed_expr : ['$1'].
typed_exprs -> typed_expr ',' typed_exprs : ['$1'|'$3'].
@@ -107,97 +102,100 @@ type_sigs -> type_sig : ['$1'].
type_sigs -> type_sig ';' type_sigs : ['$1'|'$3'].
type_sig -> fun_type : '$1'.
-type_sig -> fun_type 'when' type_guards : {type, ?line('$1'), bounded_fun,
+type_sig -> fun_type 'when' type_guards : {type, ?anno('$1'), bounded_fun,
['$1','$3']}.
type_guards -> type_guard : ['$1'].
type_guards -> type_guard ',' type_guards : ['$1'|'$3'].
-type_guard -> atom '(' top_types ')' : {type, ?line('$1'), constraint,
+type_guard -> atom '(' top_types ')' : {type, ?anno('$1'), constraint,
['$1', '$3']}.
type_guard -> var '::' top_type : build_def('$1', '$3').
top_types -> top_type : ['$1'].
top_types -> top_type ',' top_types : ['$1'|'$3'].
-top_type -> var '::' top_type_100 : {ann_type, ?line('$1'), ['$1','$3']}.
+top_type -> var '::' top_type_100 : {ann_type, ?anno('$1'), ['$1','$3']}.
top_type -> top_type_100 : '$1'.
top_type_100 -> type_200 : '$1'.
top_type_100 -> type_200 '|' top_type_100 : lift_unions('$1','$3').
-type_200 -> type_300 '..' type_300 : {type, ?line('$1'), range,
- [skip_paren('$1'),
- skip_paren('$3')]}.
+type_200 -> type_300 '..' type_300 : {type, ?anno('$1'), range,
+ ['$1', '$3']}.
type_200 -> type_300 : '$1'.
-type_300 -> type_300 add_op type_400 : ?mkop2(skip_paren('$1'),
- '$2', skip_paren('$3')).
+type_300 -> type_300 add_op type_400 : ?mkop2('$1', '$2', '$3').
type_300 -> type_400 : '$1'.
-type_400 -> type_400 mult_op type_500 : ?mkop2(skip_paren('$1'),
- '$2', skip_paren('$3')).
+type_400 -> type_400 mult_op type_500 : ?mkop2('$1', '$2', '$3').
type_400 -> type_500 : '$1'.
-type_500 -> prefix_op type : ?mkop1('$1', skip_paren('$2')).
+type_500 -> prefix_op type : ?mkop1('$1', '$2').
type_500 -> type : '$1'.
-type -> '(' top_type ')' : {paren_type, ?line('$2'), ['$2']}.
+type -> '(' top_type ')' : '$2'.
type -> var : '$1'.
type -> atom : '$1'.
type -> atom '(' ')' : build_gen_type('$1').
-type -> atom '(' top_types ')' : {type, ?line('$1'),
- normalise('$1'), '$3'}.
-type -> atom ':' atom '(' ')' : {remote_type, ?line('$1'),
+type -> atom '(' top_types ')' : build_type('$1', '$3').
+type -> atom ':' atom '(' ')' : {remote_type, ?anno('$1'),
['$1', '$3', []]}.
-type -> atom ':' atom '(' top_types ')' : {remote_type, ?line('$1'),
+type -> atom ':' atom '(' top_types ')' : {remote_type, ?anno('$1'),
['$1', '$3', '$5']}.
-type -> '[' ']' : {type, ?line('$1'), nil, []}.
-type -> '[' top_type ']' : {type, ?line('$1'), list, ['$2']}.
-type -> '[' top_type ',' '...' ']' : {type, ?line('$1'),
+type -> '[' ']' : {type, ?anno('$1'), nil, []}.
+type -> '[' top_type ']' : {type, ?anno('$1'), list, ['$2']}.
+type -> '[' top_type ',' '...' ']' : {type, ?anno('$1'),
nonempty_list, ['$2']}.
-type -> '#' '{' '}' : {type, ?line('$1'), map, []}.
-type -> '#' '{' map_pair_types '}' : {type, ?line('$1'), map, '$3'}.
-type -> '{' '}' : {type, ?line('$1'), tuple, []}.
-type -> '{' top_types '}' : {type, ?line('$1'), tuple, '$2'}.
-type -> '#' atom '{' '}' : {type, ?line('$1'), record, ['$2']}.
-type -> '#' atom '{' field_types '}' : {type, ?line('$1'),
+type -> '#' '{' '}' : {type, ?anno('$1'), map, []}.
+type -> '#' '{' map_pair_types '}' : {type, ?anno('$1'), map, '$3'}.
+type -> '{' '}' : {type, ?anno('$1'), tuple, []}.
+type -> '{' top_types '}' : {type, ?anno('$1'), tuple, '$2'}.
+type -> '#' atom '{' '}' : {type, ?anno('$1'), record, ['$2']}.
+type -> '#' atom '{' field_types '}' : {type, ?anno('$1'),
record, ['$2'|'$4']}.
type -> binary_type : '$1'.
type -> integer : '$1'.
-type -> 'fun' '(' ')' : {type, ?line('$1'), 'fun', []}.
+type -> 'fun' '(' ')' : {type, ?anno('$1'), 'fun', []}.
type -> 'fun' '(' fun_type_100 ')' : '$3'.
fun_type_100 -> '(' '...' ')' '->' top_type
- : {type, ?line('$1'), 'fun',
- [{type, ?line('$1'), any}, '$5']}.
+ : {type, ?anno('$1'), 'fun',
+ [{type, ?anno('$1'), any}, '$5']}.
fun_type_100 -> fun_type : '$1'.
-fun_type -> '(' ')' '->' top_type : {type, ?line('$1'), 'fun',
- [{type, ?line('$1'), product, []}, '$4']}.
+fun_type -> '(' ')' '->' top_type : {type, ?anno('$1'), 'fun',
+ [{type, ?anno('$1'), product, []}, '$4']}.
fun_type -> '(' top_types ')' '->' top_type
- : {type, ?line('$1'), 'fun',
- [{type, ?line('$1'), product, '$2'},'$5']}.
+ : {type, ?anno('$1'), 'fun',
+ [{type, ?anno('$1'), product, '$2'},'$5']}.
+map_pair_types -> '...' : [{type, ?anno('$1'), map_field_assoc,
+ [{type, ?anno('$1'), any, []},
+ {type, ?anno('$1'), any, []}]}].
map_pair_types -> map_pair_type : ['$1'].
map_pair_types -> map_pair_type ',' map_pair_types : ['$1'|'$3'].
-map_pair_type -> top_type '=>' top_type : {type, ?line('$2'), map_field_assoc,'$1','$3'}.
+
+map_pair_type -> top_type '=>' top_type : {type, ?anno('$2'),
+ map_field_assoc,['$1','$3']}.
+map_pair_type -> top_type ':=' top_type : {type, ?anno('$2'),
+ map_field_exact,['$1','$3']}.
field_types -> field_type : ['$1'].
field_types -> field_type ',' field_types : ['$1'|'$3'].
-field_type -> atom '::' top_type : {type, ?line('$1'), field_type,
+field_type -> atom '::' top_type : {type, ?anno('$1'), field_type,
['$1', '$3']}.
-binary_type -> '<<' '>>' : {type, ?line('$1'),binary,
- [abstract(0, ?line('$1')),
- abstract(0, ?line('$1'))]}.
-binary_type -> '<<' bin_base_type '>>' : {type, ?line('$1'),binary,
- ['$2', abstract(0, ?line('$1'))]}.
-binary_type -> '<<' bin_unit_type '>>' : {type, ?line('$1'),binary,
- [abstract(0, ?line('$1')), '$2']}.
+binary_type -> '<<' '>>' : {type, ?anno('$1'),binary,
+ [abstract2(0, ?anno('$1')),
+ abstract2(0, ?anno('$1'))]}.
+binary_type -> '<<' bin_base_type '>>' : {type, ?anno('$1'),binary,
+ ['$2', abstract2(0, ?anno('$1'))]}.
+binary_type -> '<<' bin_unit_type '>>' : {type, ?anno('$1'),binary,
+ [abstract2(0, ?anno('$1')), '$2']}.
binary_type -> '<<' bin_base_type ',' bin_unit_type '>>'
- : {type, ?line('$1'), binary, ['$2', '$4']}.
+ : {type, ?anno('$1'), binary, ['$2', '$4']}.
bin_base_type -> var ':' type : build_bin_type(['$1'], '$3').
@@ -213,7 +211,7 @@ function_clauses -> function_clause : ['$1'].
function_clauses -> function_clause ';' function_clauses : ['$1'|'$3'].
function_clause -> atom clause_args clause_guard clause_body :
- {clause,?line('$1'),element(3, '$1'),'$2','$3','$4'}.
+ {clause,?anno('$1'),element(3, '$1'),'$2','$3','$4'}.
clause_args -> argument_list : element(1, '$1').
@@ -224,10 +222,10 @@ clause_guard -> '$empty' : [].
clause_body -> '->' exprs: '$2'.
-expr -> 'catch' expr : {'catch',?line('$1'),'$2'}.
+expr -> 'catch' expr : {'catch',?anno('$1'),'$2'}.
expr -> expr_100 : '$1'.
-expr_100 -> expr_150 '=' expr_100 : {match,?line('$2'),'$1','$3'}.
+expr_100 -> expr_150 '=' expr_100 : {match,?anno('$2'),'$1','$3'}.
expr_100 -> expr_150 '!' expr_100 : ?mkop2('$1', '$2', '$3').
expr_100 -> expr_150 : '$1'.
@@ -263,7 +261,7 @@ expr_700 -> record_expr : '$1'.
expr_700 -> expr_800 : '$1'.
expr_800 -> expr_max ':' expr_max :
- {remote,?line('$2'),'$1','$3'}.
+ {remote,?anno('$2'),'$1','$3'}.
expr_800 -> expr_max : '$1'.
expr_max -> var : '$1'.
@@ -275,7 +273,7 @@ expr_max -> binary_comprehension : '$1'.
expr_max -> tuple : '$1'.
%%expr_max -> struct : '$1'.
expr_max -> '(' expr ')' : '$2'.
-expr_max -> 'begin' exprs 'end' : {block,?line('$1'),'$2'}.
+expr_max -> 'begin' exprs 'end' : {block,?anno('$1'),'$2'}.
expr_max -> if_expr : '$1'.
expr_max -> case_expr : '$1'.
expr_max -> receive_expr : '$1'.
@@ -283,22 +281,22 @@ expr_max -> fun_expr : '$1'.
expr_max -> try_expr : '$1'.
-list -> '[' ']' : {nil,?line('$1')}.
-list -> '[' expr tail : {cons,?line('$1'),'$2','$3'}.
+list -> '[' ']' : {nil,?anno('$1')}.
+list -> '[' expr tail : {cons,?anno('$1'),'$2','$3'}.
-tail -> ']' : {nil,?line('$1')}.
+tail -> ']' : {nil,?anno('$1')}.
tail -> '|' expr ']' : '$2'.
-tail -> ',' expr tail : {cons,?line('$2'),'$2','$3'}.
+tail -> ',' expr tail : {cons,?anno('$2'),'$2','$3'}.
-binary -> '<<' '>>' : {bin,?line('$1'),[]}.
-binary -> '<<' bin_elements '>>' : {bin,?line('$1'),'$2'}.
+binary -> '<<' '>>' : {bin,?anno('$1'),[]}.
+binary -> '<<' bin_elements '>>' : {bin,?anno('$1'),'$2'}.
bin_elements -> bin_element : ['$1'].
bin_elements -> bin_element ',' bin_elements : ['$1'|'$3'].
bin_element -> bit_expr opt_bit_size_expr opt_bit_type_list :
- {bin_element,?line('$1'),'$1','$2','$3'}.
+ {bin_element,?anno('$1'),'$1','$2','$3'}.
bit_expr -> prefix_op expr_max : ?mkop1('$1', '$2').
bit_expr -> expr_max : '$1'.
@@ -319,29 +317,29 @@ bit_size_expr -> expr_max : '$1'.
list_comprehension -> '[' expr '||' lc_exprs ']' :
- {lc,?line('$1'),'$2','$4'}.
-binary_comprehension -> '<<' binary '||' lc_exprs '>>' :
- {bc,?line('$1'),'$2','$4'}.
+ {lc,?anno('$1'),'$2','$4'}.
+binary_comprehension -> '<<' expr_max '||' lc_exprs '>>' :
+ {bc,?anno('$1'),'$2','$4'}.
lc_exprs -> lc_expr : ['$1'].
lc_exprs -> lc_expr ',' lc_exprs : ['$1'|'$3'].
lc_expr -> expr : '$1'.
-lc_expr -> expr '<-' expr : {generate,?line('$2'),'$1','$3'}.
-lc_expr -> binary '<=' expr : {b_generate,?line('$2'),'$1','$3'}.
+lc_expr -> expr '<-' expr : {generate,?anno('$2'),'$1','$3'}.
+lc_expr -> binary '<=' expr : {b_generate,?anno('$2'),'$1','$3'}.
-tuple -> '{' '}' : {tuple,?line('$1'),[]}.
-tuple -> '{' exprs '}' : {tuple,?line('$1'),'$2'}.
+tuple -> '{' '}' : {tuple,?anno('$1'),[]}.
+tuple -> '{' exprs '}' : {tuple,?anno('$1'),'$2'}.
%%struct -> atom tuple :
-%% {struct,?line('$1'),element(3, '$1'),element(3, '$2')}.
+%% {struct,?anno('$1'),element(3, '$1'),element(3, '$2')}.
map_expr -> '#' map_tuple :
- {map, ?line('$1'),'$2'}.
+ {map, ?anno('$1'),'$2'}.
map_expr -> expr_max '#' map_tuple :
- {map, ?line('$2'),'$1','$3'}.
+ {map, ?anno('$2'),'$1','$3'}.
map_expr -> map_expr '#' map_tuple :
- {map, ?line('$2'),'$1','$3'}.
+ {map, ?anno('$2'),'$1','$3'}.
map_tuple -> '{' '}' : [].
map_tuple -> '{' map_fields '}' : '$2'.
@@ -353,10 +351,10 @@ map_field -> map_field_assoc : '$1'.
map_field -> map_field_exact : '$1'.
map_field_assoc -> map_key '=>' expr :
- {map_field_assoc,?line('$1'),'$1','$3'}.
+ {map_field_assoc,?anno('$1'),'$1','$3'}.
map_field_exact -> map_key ':=' expr :
- {map_field_exact,?line('$1'),'$1','$3'}.
+ {map_field_exact,?anno('$1'),'$1','$3'}.
map_key -> expr : '$1'.
@@ -366,17 +364,17 @@ map_key -> expr : '$1'.
%% always atoms for the moment, this might change in the future.
record_expr -> '#' atom '.' atom :
- {record_index,?line('$1'),element(3, '$2'),'$4'}.
+ {record_index,?anno('$1'),element(3, '$2'),'$4'}.
record_expr -> '#' atom record_tuple :
- {record,?line('$1'),element(3, '$2'),'$3'}.
+ {record,?anno('$1'),element(3, '$2'),'$3'}.
record_expr -> expr_max '#' atom '.' atom :
- {record_field,?line('$2'),'$1',element(3, '$3'),'$5'}.
+ {record_field,?anno('$2'),'$1',element(3, '$3'),'$5'}.
record_expr -> expr_max '#' atom record_tuple :
- {record,?line('$2'),'$1',element(3, '$3'),'$4'}.
+ {record,?anno('$2'),'$1',element(3, '$3'),'$4'}.
record_expr -> record_expr '#' atom '.' atom :
- {record_field,?line('$2'),'$1',element(3, '$3'),'$5'}.
+ {record_field,?anno('$2'),'$1',element(3, '$3'),'$5'}.
record_expr -> record_expr '#' atom record_tuple :
- {record,?line('$2'),'$1',element(3, '$3'),'$4'}.
+ {record,?anno('$2'),'$1',element(3, '$3'),'$4'}.
record_tuple -> '{' '}' : [].
record_tuple -> '{' record_fields '}' : '$2'.
@@ -384,47 +382,47 @@ record_tuple -> '{' record_fields '}' : '$2'.
record_fields -> record_field : ['$1'].
record_fields -> record_field ',' record_fields : ['$1' | '$3'].
-record_field -> var '=' expr : {record_field,?line('$1'),'$1','$3'}.
-record_field -> atom '=' expr : {record_field,?line('$1'),'$1','$3'}.
+record_field -> var '=' expr : {record_field,?anno('$1'),'$1','$3'}.
+record_field -> atom '=' expr : {record_field,?anno('$1'),'$1','$3'}.
%% N.B. This is called from expr_700.
function_call -> expr_800 argument_list :
- {call,?line('$1'),'$1',element(1, '$2')}.
+ {call,?anno('$1'),'$1',element(1, '$2')}.
-if_expr -> 'if' if_clauses 'end' : {'if',?line('$1'),'$2'}.
+if_expr -> 'if' if_clauses 'end' : {'if',?anno('$1'),'$2'}.
if_clauses -> if_clause : ['$1'].
if_clauses -> if_clause ';' if_clauses : ['$1' | '$3'].
if_clause -> guard clause_body :
- {clause,?line(hd(hd('$1'))),[],'$1','$2'}.
+ {clause,?anno(hd(hd('$1'))),[],'$1','$2'}.
case_expr -> 'case' expr 'of' cr_clauses 'end' :
- {'case',?line('$1'),'$2','$4'}.
+ {'case',?anno('$1'),'$2','$4'}.
cr_clauses -> cr_clause : ['$1'].
cr_clauses -> cr_clause ';' cr_clauses : ['$1' | '$3'].
cr_clause -> expr clause_guard clause_body :
- {clause,?line('$1'),['$1'],'$2','$3'}.
+ {clause,?anno('$1'),['$1'],'$2','$3'}.
receive_expr -> 'receive' cr_clauses 'end' :
- {'receive',?line('$1'),'$2'}.
+ {'receive',?anno('$1'),'$2'}.
receive_expr -> 'receive' 'after' expr clause_body 'end' :
- {'receive',?line('$1'),[],'$3','$4'}.
+ {'receive',?anno('$1'),[],'$3','$4'}.
receive_expr -> 'receive' cr_clauses 'after' expr clause_body 'end' :
- {'receive',?line('$1'),'$2','$4','$5'}.
+ {'receive',?anno('$1'),'$2','$4','$5'}.
fun_expr -> 'fun' atom '/' integer :
- {'fun',?line('$1'),{function,element(3, '$2'),element(3, '$4')}}.
+ {'fun',?anno('$1'),{function,element(3, '$2'),element(3, '$4')}}.
fun_expr -> 'fun' atom_or_var ':' atom_or_var '/' integer_or_var :
- {'fun',?line('$1'),{function,'$2','$4','$6'}}.
+ {'fun',?anno('$1'),{function,'$2','$4','$6'}}.
fun_expr -> 'fun' fun_clauses 'end' :
- build_fun(?line('$1'), '$2').
+ build_fun(?anno('$1'), '$2').
atom_or_var -> atom : '$1'.
atom_or_var -> var : '$1'.
@@ -436,16 +434,16 @@ fun_clauses -> fun_clause : ['$1'].
fun_clauses -> fun_clause ';' fun_clauses : ['$1' | '$3'].
fun_clause -> argument_list clause_guard clause_body :
- {Args,Pos} = '$1',
- {clause,Pos,'fun',Args,'$2','$3'}.
+ {Args,Anno} = '$1',
+ {clause,Anno,'fun',Args,'$2','$3'}.
fun_clause -> var argument_list clause_guard clause_body :
{clause,element(2, '$1'),element(3, '$1'),element(1, '$2'),'$3','$4'}.
try_expr -> 'try' exprs 'of' cr_clauses try_catch :
- build_try(?line('$1'),'$2','$4','$5').
+ build_try(?anno('$1'),'$2','$4','$5').
try_expr -> 'try' exprs try_catch :
- build_try(?line('$1'),'$2',[],'$3').
+ build_try(?anno('$1'),'$2',[],'$3').
try_catch -> 'catch' try_clauses 'end' :
{'$2',[]}.
@@ -458,18 +456,18 @@ try_clauses -> try_clause : ['$1'].
try_clauses -> try_clause ';' try_clauses : ['$1' | '$3'].
try_clause -> expr clause_guard clause_body :
- L = ?line('$1'),
- {clause,L,[{tuple,L,[{atom,L,throw},'$1',{var,L,'_'}]}],'$2','$3'}.
+ A = ?anno('$1'),
+ {clause,A,[{tuple,A,[{atom,A,throw},'$1',{var,A,'_'}]}],'$2','$3'}.
try_clause -> atom ':' expr clause_guard clause_body :
- L = ?line('$1'),
- {clause,L,[{tuple,L,['$1','$3',{var,L,'_'}]}],'$4','$5'}.
+ A = ?anno('$1'),
+ {clause,A,[{tuple,A,['$1','$3',{var,A,'_'}]}],'$4','$5'}.
try_clause -> var ':' expr clause_guard clause_body :
- L = ?line('$1'),
- {clause,L,[{tuple,L,['$1','$3',{var,L,'_'}]}],'$4','$5'}.
+ A = ?anno('$1'),
+ {clause,A,[{tuple,A,['$1','$3',{var,A,'_'}]}],'$4','$5'}.
-argument_list -> '(' ')' : {[],?line('$1')}.
-argument_list -> '(' exprs ')' : {'$2',?line('$1')}.
+argument_list -> '(' ')' : {[],?anno('$1')}.
+argument_list -> '(' exprs ')' : {'$2',?anno('$1')}.
exprs -> expr : ['$1'].
@@ -486,7 +484,7 @@ atomic -> strings : '$1'.
strings -> string : '$1'.
strings -> string strings :
- {string,?line('$1'),element(3, '$1') ++ element(3, '$2')}.
+ {string,?anno('$1'),element(3, '$1') ++ element(3, '$2')}.
prefix_op -> '+' : '$1'.
prefix_op -> '-' : '$1'.
@@ -521,56 +519,455 @@ comp_op -> '>' : '$1'.
comp_op -> '=:=' : '$1'.
comp_op -> '=/=' : '$1'.
-rule -> rule_clauses : build_rule('$1').
-
-rule_clauses -> rule_clause : ['$1'].
-rule_clauses -> rule_clause ';' rule_clauses : ['$1'|'$3'].
-
-rule_clause -> atom clause_args clause_guard rule_body :
- {clause,?line('$1'),element(3, '$1'),'$2','$3','$4'}.
-
-rule_body -> ':-' lc_exprs: '$2'.
-
-
Erlang code.
-export([parse_form/1,parse_exprs/1,parse_term/1]).
-export([normalise/1,abstract/1,tokens/1,tokens/2]).
-export([abstract/2]).
-export([inop_prec/1,preop_prec/1,func_prec/0,max_prec/0]).
--export([set_line/2,get_attribute/2,get_attributes/1]).
+-export([type_inop_prec/1,type_preop_prec/1]).
+-export([map_anno/2, fold_anno/3, mapfold_anno/3,
+ new_anno/1, anno_to_term/1, anno_from_term/1]).
%% The following directive is needed for (significantly) faster compilation
%% of the generated .erl file by the HiPE compiler. Please do not remove.
-compile([{hipe,[{regalloc,linear_scan}]}]).
-export_type([abstract_clause/0, abstract_expr/0, abstract_form/0,
- error_info/0]).
+ abstract_type/0, error_info/0]).
+
+%% Start of Abstract Format
+
+-type anno() :: erl_anno:anno().
+
+-type abstract_form() :: af_module()
+ | af_behavior()
+ | af_behaviour()
+ | af_export()
+ | af_import()
+ | af_export_type()
+ | af_optional_callbacks()
+ | af_compile()
+ | af_file()
+ | af_record_decl()
+ | af_type_decl()
+ | af_function_spec()
+ | af_wild_attribute()
+ | af_function_decl().
+
+-type af_module() :: {'attribute', anno(), 'module', module()}.
+
+-type af_behavior() :: {'attribute', anno(), 'behavior', behaviour()}.
+
+-type af_behaviour() :: {'attribute', anno(), 'behaviour', behaviour()}.
+
+-type behaviour() :: atom().
+
+-type af_export() :: {'attribute', anno(), 'export', af_fa_list()}.
+
+-type af_import() :: {'attribute', anno(), 'import', af_fa_list()}.
+
+-type af_fa_list() :: [{function_name(), arity()}].
+
+-type af_export_type() :: {'attribute', anno(), 'export_type', af_ta_list()}.
+
+-type af_ta_list() :: [{type_name(), arity()}].
+
+-type af_optional_callbacks() ::
+ {'attribute', anno(), 'optional_callbacks', af_fa_list()}.
+
+-type af_compile() :: {'attribute', anno(), 'compile', any()}.
+
+-type af_file() :: {'attribute', anno(), 'file', {string(), anno()}}.
+
+-type af_record_decl() ::
+ {'attribute', anno(), 'record', {record_name(), [af_field_decl()]}}.
+
+-type af_field_decl() :: af_typed_field() | af_field().
+
+-type af_typed_field() ::
+ {'typed_record_field', af_field(), abstract_type()}.
+
+-type af_field() :: {'record_field', anno(), af_field_name()}
+ | {'record_field', anno(), af_field_name(), abstract_expr()}.
+
+-type af_type_decl() :: {'attribute', anno(), type_attr(),
+ {type_name(), abstract_type(), [af_variable()]}}.
+
+-type type_attr() :: 'opaque' | 'type'.
+
+-type af_function_spec() :: {'attribute', anno(), spec_attr(),
+ {{function_name(), arity()},
+ af_function_type_list()}}
+ | {'attribute', anno(), 'spec',
+ {{module(), function_name(), arity()},
+ af_function_type_list()}}.
+
+-type spec_attr() :: 'callback' | 'spec'.
+
+-type af_wild_attribute() :: {'attribute', anno(), atom(), any()}.
+
+-type af_function_decl() ::
+ {'function', anno(), function_name(), arity(), af_clause_seq()}.
+
+-type 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_access(abstract_expr())
+ | af_map_access(abstract_expr())
+ | af_map_update(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_named_fun().
+
+-type af_record_update(T) :: {'record',
+ anno(),
+ abstract_expr(),
+ record_name(),
+ [af_record_field(T)]}.
+
+-type af_catch() :: {'catch', anno(), abstract_expr()}.
+
+-type af_local_call() :: {'call', anno(), af_local_function(), af_args()}.
+
+-type af_remote_call() :: {'call', anno(), af_remote_function(), af_args()}.
+
+-type af_args() :: [abstract_expr()].
+
+-type af_local_function() :: abstract_expr().
+
+-type af_remote_function() ::
+ {'remote', anno(), abstract_expr(), abstract_expr()}.
+
+-type af_list_comprehension() ::
+ {'lc', anno(), af_template(), af_qualifier_seq()}.
+
+-type af_binary_comprehension() ::
+ {'bc', anno(), 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', anno(), af_pattern(), abstract_expr()}
+ | {'b_generate', anno(), af_pattern(), abstract_expr()}.
+
+-type af_filter() :: abstract_expr().
+
+-type af_block() :: {'block', anno(), af_body()}.
+
+-type af_if() :: {'if', anno(), af_clause_seq()}.
+
+-type af_case() :: {'case', anno(), abstract_expr(), af_clause_seq()}.
+
+-type af_try() :: {'try',
+ anno(),
+ af_body() | [],
+ af_clause_seq() | [],
+ af_clause_seq() | [],
+ af_body() | []}.
+
+-type af_clause_seq() :: [af_clause(), ...].
+
+-type af_receive() ::
+ {'receive', anno(), af_clause_seq()}
+ | {'receive', anno(), af_clause_seq(), abstract_expr(), af_body()}.
+
+-type af_local_fun() ::
+ {'fun', anno(), {'function', function_name(), arity()}}.
+
+-type af_remote_fun() ::
+ {'fun', anno(), {'function', module(), function_name(), arity()}}
+ | {'fun', anno(), {'function', af_atom(), af_atom(), af_integer()}}.
+
+-type af_fun() :: {'fun', anno(), {'clauses', af_clause_seq()}}.
+
+-type af_named_fun() :: {'named_fun', anno(), fun_name(), af_clause_seq()}.
+
+-type fun_name() :: atom().
+
+-type abstract_clause() :: af_clause().
+
+-type af_clause() ::
+ {'clause', anno(), [af_pattern()], af_guard_seq(), af_body()}.
+
+-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_access(af_guard_test())
+ | af_map_access(abstract_expr()) % FIXME
+ | af_map_update(abstract_expr()) % FIXME
+ | af_guard_call()
+ | af_remote_guard_call().
+
+-type af_record_field_access(T) ::
+ {'record_field', anno(), T, record_name(), af_field_name()}.
+
+-type af_map_access(T) :: {'map', anno(), [af_map_field(T)]}.
+
+-type af_map_update(T) :: {'map', anno(), T, [af_map_field(T)]}.
+
+-type af_map_field(T) :: af_map_field_assoc(T) | af_map_field_exact(T).
+
+-type af_map_field_assoc(T) :: {'map_field_assoc', anno(), T, T}.
+
+-type af_map_field_exact(T) :: {'map_field_exact', anno(), T, T}.
+
+-type af_guard_call() :: {'call', anno(), function_name(), [af_guard_test()]}.
--type abstract_clause() :: term().
--type abstract_expr() :: term().
--type abstract_form() :: term().
+-type af_remote_guard_call() ::
+ {'call', anno(),
+ {'remote', anno(), af_lit_atom('erlang'), af_atom()},
+ [af_guard_test()]}.
+
+-type af_pattern() :: af_literal()
+ | af_match(af_pattern())
+ | af_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_access(af_pattern())
+ | af_record_index()
+ | af_map_pattern().
+
+-type af_record_index() ::
+ {'record_index', anno(), record_name(), af_field_name()}.
+
+-type af_record_access(T) ::
+ {'record', anno(), record_name(), [af_record_field(T)]}.
+
+-type af_record_field(T) :: {'record_field', anno(), af_field_name(), T}.
+
+-type af_map_pattern() ::
+ {'map', anno(), [af_map_field_exact(abstract_expr)]}. % FIXME?
+
+-type abstract_type() :: af_annotated_type()
+ | af_atom()
+ | af_bitstring_type()
+ | af_empty_list_type()
+ | af_fun_type()
+ | af_integer_range_type()
+ | af_map_type()
+ | af_predefined_type()
+ | af_record_type()
+ | af_remote_type()
+ | af_singleton_integer_type()
+ | af_tuple_type()
+ | af_type_union()
+ | af_type_variable()
+ | af_user_defined_type().
+
+-type af_annotated_type() ::
+ {'ann_type', anno(), [af_anno() | abstract_type()]}. % [Var, Type]
+
+-type af_anno() :: af_variable().
+
+-type af_bitstring_type() ::
+ {'type', anno(), 'binary', [af_singleton_integer_type()]}.
+
+-type af_empty_list_type() :: {'type', anno(), 'nil', []}.
+
+-type af_fun_type() :: {'type', anno(), 'fun', []}
+ | {'type', anno(), 'fun', [{'type', anno(), 'any'} |
+ abstract_type()]}
+ | {'type', anno(), 'fun', af_function_type()}.
+
+-type af_integer_range_type() ::
+ {'type', anno(), 'range', [af_singleton_integer_type()]}.
+
+-type af_map_type() :: {'type', anno(), 'map', 'any'}
+ | {'type', anno(), 'map', [af_map_pair_type()]}.
+
+-type af_map_pair_type() ::
+ {'type', anno(), 'map_field_assoc', [abstract_type()]}
+ | {'type', anno(), 'map_field_exact', [abstract_type()]}.
+
+-type af_predefined_type() ::
+ {'type', anno(), type_name(), [abstract_type()]}.
+
+-type af_record_type() ::
+ {'type', anno(), 'record', [(Name :: af_atom()) % [Name, T1, ... Tk]
+ | af_record_field_type()]}.
+
+-type af_record_field_type() ::
+ {'type', anno(), 'field_type', [(Name :: af_atom()) |
+ abstract_type()]}. % [Name, Type]
+
+-type af_remote_type() ::
+ {'remote_type', anno(), [(Module :: af_atom()) |
+ (TypeName :: af_atom()) |
+ [abstract_type()]]}. % [Module, Name, [T]]
+
+-type af_tuple_type() :: {'type', anno(), 'tuple', 'any'}
+ | {'type', anno(), 'tuple', [abstract_type()]}.
+
+-type af_type_union() :: {'type', anno(), 'union', [abstract_type()]}.
+
+-type af_type_variable() :: {'var', anno(), atom()}. % except '_'
+
+-type af_user_defined_type() ::
+ {'user_type', anno(), type_name(), [abstract_type()]}.
+
+-type af_function_type_list() :: [af_constrained_function_type() |
+ af_function_type()].
+
+-type af_constrained_function_type() ::
+ {'type', anno(), 'bounded_fun', [af_function_type() | % [Ft, Fc]
+ af_function_constraint()]}.
+
+-type af_function_type() ::
+ {'type', anno(), 'fun',
+ [{'type', anno(), 'product', [abstract_type()]} | abstract_type()]}.
+
+-type af_function_constraint() :: [af_constraint()].
+
+-type af_constraint() :: {'type', anno(), 'constraint',
+ af_lit_atom('is_subtype'),
+ [af_type_variable() | abstract_type()]}. % [V, T]
+
+-type af_singleton_integer_type() :: af_integer()
+ | af_unary_op(af_singleton_integer_type())
+ | af_binary_op(af_singleton_integer_type()).
+
+-type af_literal() :: af_atom() | af_integer() | af_float() | af_string().
+
+-type af_atom() :: af_lit_atom(atom()).
+
+-type af_lit_atom(A) :: {'atom', anno(), A}.
+
+-type af_integer() :: {'integer', anno(), non_neg_integer()}.
+
+-type af_float() :: {'float', anno(), float()}.
+
+-type af_string() :: {'string', anno(), string()}.
+
+-type af_match(T) :: {'match', anno(), af_pattern(), T}.
+
+-type af_variable() :: {'var', anno(), atom()}. % | af_anon_variable()
+
+%-type af_anon_variable() :: {'var', anno(), '_'}.
+
+-type af_tuple(T) :: {'tuple', anno(), [T]}.
+
+-type af_nil() :: {'nil', anno()}.
+
+-type af_cons(T) :: {'cons', anno(), T, T}.
+
+-type af_bin(T) :: {'bin', anno(), [af_binelement(T)]}.
+
+-type af_binelement(T) :: {'bin_element',
+ anno(),
+ T,
+ af_binelement_size(),
+ type_specifier_list()}.
+
+-type af_binelement_size() :: 'default' | abstract_expr().
+
+-type af_binary_op(T) :: {'op', anno(), binary_op(), T, T}.
+
+-type binary_op() :: '/' | '*' | 'div' | 'rem' | 'band' | 'and' | '+' | '-'
+ | 'bor' | 'bxor' | 'bsl' | 'bsr' | 'or' | 'xor' | '++'
+ | '--' | '==' | '/=' | '=<' | '<' | '>=' | '>' | '=:='
+ | '=/='.
+
+-type af_unary_op(T) :: {'op', anno(), unary_op(), T}.
+
+-type unary_op() :: '+' | '*' | 'bnot' | 'not'.
+
+%% See also lib/stdlib/{src/erl_bits.erl,include/erl_bits.hrl}.
+-type type_specifier_list() :: 'default' | [type_specifier(), ...].
+
+-type type_specifier() :: type()
+ | signedness()
+ | endianness()
+ | unit().
+
+-type type() :: 'integer'
+ | 'float'
+ | 'binary'
+ | 'bytes'
+ | 'bitstring'
+ | 'bits'
+ | 'utf8'
+ | 'utf16'
+ | 'utf32'.
+
+-type signedness() :: 'signed' | 'unsigned'.
+
+-type endianness() :: 'big' | 'little' | 'native'.
+
+-type unit() :: {'unit', 1..256}.
+
+-type record_name() :: atom().
+
+-type af_field_name() :: af_atom().
+
+-type function_name() :: atom().
+
+-type type_name() :: atom().
+
+%% End of Abstract Format
+
+%% XXX. To be refined.
-type error_description() :: term().
--type error_info() :: {erl_scan:line(), module(), error_description()}.
+-type error_info() :: {erl_anno:line(), module(), error_description()}.
-type token() :: erl_scan:token().
-%% mkop(Op, Arg) -> {op,Line,Op,Arg}.
-%% mkop(Left, Op, Right) -> {op,Line,Op,Left,Right}.
+%% mkop(Op, Arg) -> {op,Anno,Op,Arg}.
+%% mkop(Left, Op, Right) -> {op,Anno,Op,Left,Right}.
--define(mkop2(L, OpPos, R),
+-define(mkop2(L, OpAnno, R),
begin
- {Op,Pos} = OpPos,
- {op,Pos,Op,L,R}
+ {Op,Anno} = OpAnno,
+ {op,Anno,Op,L,R}
end).
--define(mkop1(OpPos, A),
+-define(mkop1(OpAnno, A),
begin
- {Op,Pos} = OpPos,
- {op,Pos,Op,A}
+ {Op,Anno} = OpAnno,
+ {op,Anno,Op,A}
end).
-%% keep track of line info in tokens
--define(line(Tup), element(2, Tup)).
+%% keep track of annotation info in tokens
+-define(anno(Tup), element(2, Tup)).
%% Entry points compatible to old erl_parse.
%% These really suck and are only here until Calle gets multiple
@@ -580,10 +977,10 @@ Erlang code.
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([{'-',A1},{atom,A2,spec}|Tokens]) ->
+ parse([{'-',A1},{'spec',A2}|Tokens]);
+parse_form([{'-',A1},{atom,A2,callback}|Tokens]) ->
+ parse([{'-',A1},{'callback',A2}|Tokens]);
parse_form(Tokens) ->
parse(Tokens).
@@ -592,7 +989,8 @@ parse_form(Tokens) ->
ExprList :: [abstract_expr()],
ErrorInfo :: error_info().
parse_exprs(Tokens) ->
- case parse([{atom,0,f},{'(',0},{')',0},{'->',0}|Tokens]) of
+ A = erl_anno:new(0),
+ case parse([{atom,A,f},{'(',A},{')',A},{'->',A}|Tokens]) of
{ok,{function,_Lf,f,0,[{clause,_Lc,[],[],Exprs}]}} ->
{ok,Exprs};
{error,_} = Err -> Err
@@ -603,57 +1001,52 @@ parse_exprs(Tokens) ->
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]}]}} ->
+ A = erl_anno:new(0),
+ case parse([{atom,A,f},{'(',A},{')',A},{'->',A}|Tokens]) of
+ {ok,{function,_Af,f,0,[{clause,_Ac,[],[],[Expr]}]}} ->
try normalise(Expr) of
Term -> {ok,Term}
catch
- _:_R -> {error,{?line(Expr),?MODULE,"bad term"}}
+ _:_R -> {error,{location(?anno(Expr)),?MODULE,"bad term"}}
end;
- {ok,{function,_Lf,f,0,[{clause,_Lc,[],[],[_E1,E2|_Es]}]}} ->
- {error,{?line(E2),?MODULE,"bad term"}};
+ {ok,{function,_Af,f,A,[{clause,_Ac,[],[],[_E1,E2|_Es]}]}} ->
+ {error,{location(?anno(E2)),?MODULE,"bad term"}};
{error,_} = Err -> Err
end.
-type attributes() :: 'export' | 'file' | 'import' | 'module'
| 'opaque' | 'record' | 'type'.
-build_typed_attribute({atom,La,record},
- {typed_record, {atom,_Ln,RecordName}, RecTuple}) ->
- {attribute,La,record,{RecordName,record_tuple(RecTuple)}};
-build_typed_attribute({atom,La,Attr},
+build_typed_attribute({atom,Aa,record},
+ {typed_record, {atom,_An,RecordName}, RecTuple}) ->
+ {attribute,Aa,record,{RecordName,record_tuple(RecTuple)}};
+build_typed_attribute({atom,Aa,Attr},
{type_def, {call,_,{atom,_,TypeName},Args}, Type})
when Attr =:= 'type' ; Attr =:= 'opaque' ->
case lists:all(fun({var, _, _}) -> true;
(_) -> false
end, Args) of
- true -> {attribute,La,Attr,{TypeName,Type,Args}};
- false -> error_bad_decl(La, Attr)
+ true -> {attribute,Aa,Attr,{TypeName,Type,Args}};
+ false -> error_bad_decl(Aa, Attr)
end;
-build_typed_attribute({atom,La,Attr},_) ->
+build_typed_attribute({atom,Aa,Attr},_) ->
case Attr of
- record -> error_bad_decl(La, record);
- type -> error_bad_decl(La, type);
- opaque -> error_bad_decl(La, opaque);
- _ -> ret_err(La, "bad attribute")
+ record -> error_bad_decl(Aa, record);
+ type -> error_bad_decl(Aa, type);
+ opaque -> error_bad_decl(Aa, opaque);
+ _ -> ret_err(Aa, "bad attribute")
end.
-build_type_spec({Kind,La}, {SpecFun, TypeSpecs})
+build_type_spec({Kind,Aa}, {SpecFun, TypeSpecs})
when (Kind =:= spec) or (Kind =:= callback) ->
NewSpecFun =
case SpecFun of
{atom, _, Fun} ->
{Fun, find_arity_from_specs(TypeSpecs)};
{{atom,_, Mod}, {atom,_, Fun}} ->
- {Mod,Fun,find_arity_from_specs(TypeSpecs)};
- {{atom, _, Fun}, {integer, _, Arity}} ->
- %% Old style spec. Allow this for now.
- {Fun,Arity};
- {{atom,_, Mod}, {atom, _, Fun}, {integer, _, Arity}} ->
- %% Old style spec. Allow this for now.
- {Mod,Fun,Arity}
- end,
- {attribute,La,Kind,{NewSpecFun, TypeSpecs}}.
+ {Mod,Fun,find_arity_from_specs(TypeSpecs)}
+ end,
+ {attribute,Aa,Kind,{NewSpecFun, TypeSpecs}}.
find_arity_from_specs([Spec|_]) ->
%% Use the first spec to find the arity. If all are not the same,
@@ -665,99 +1058,111 @@ find_arity_from_specs([Spec|_]) ->
{type, _, 'fun', [{type, _, product, Args},_]} = Fun,
length(Args).
+build_def({var, A, '_'}, _Types) ->
+ ret_err(A, "bad type variable");
build_def(LHS, Types) ->
- IsSubType = {atom, ?line(LHS), is_subtype},
- {type, ?line(LHS), constraint, [IsSubType, [LHS, Types]]}.
+ IsSubType = {atom, ?anno(LHS), is_subtype},
+ {type, ?anno(LHS), constraint, [IsSubType, [LHS, Types]]}.
-lift_unions(T1, {type, _La, union, List}) ->
- {type, ?line(T1), union, [T1|List]};
+lift_unions(T1, {type, _Aa, union, List}) ->
+ {type, ?anno(T1), union, [T1|List]};
lift_unions(T1, T2) ->
- {type, ?line(T1), union, [T1, T2]}.
+ {type, ?anno(T1), union, [T1, T2]}.
-skip_paren({paren_type,_L,[Type]}) ->
- skip_paren(Type);
-skip_paren(Type) ->
- Type.
-
-build_gen_type({atom, La, tuple}) ->
- {type, La, tuple, any};
-build_gen_type({atom, La, map}) ->
- {type, La, map, any};
-build_gen_type({atom, La, Name}) ->
- {type, La, Name, []}.
+build_gen_type({atom, Aa, tuple}) ->
+ {type, Aa, tuple, any};
+build_gen_type({atom, Aa, map}) ->
+ {type, Aa, map, any};
+build_gen_type({atom, Aa, Name}) ->
+ Tag = type_tag(Name, 0),
+ {Tag, Aa, Name, []}.
build_bin_type([{var, _, '_'}|Left], Int) ->
build_bin_type(Left, Int);
build_bin_type([], Int) ->
- skip_paren(Int);
-build_bin_type([{var, La, _}|_], _) ->
- ret_err(La, "Bad binary type").
+ Int;
+build_bin_type([{var, Aa, _}|_], _) ->
+ ret_err(Aa, "Bad binary type").
+
+build_type({atom, A, Name}, Types) ->
+ Tag = type_tag(Name, length(Types)),
+ {Tag, A, Name, Types}.
+
+type_tag(TypeName, NumberOfTypeVariables) ->
+ case erl_internal:is_type(TypeName, NumberOfTypeVariables) of
+ true -> type;
+ false -> user_type
+ end.
+
+abstract2(Term, Anno) ->
+ Line = erl_anno:line(Anno),
+ abstract(Term, Line).
%% build_attribute(AttrName, AttrValue) ->
-%% {attribute,Line,module,Module}
-%% {attribute,Line,export,Exports}
-%% {attribute,Line,import,Imports}
-%% {attribute,Line,record,{Name,Inits}}
-%% {attribute,Line,file,{Name,Line}}
-%% {attribute,Line,Name,Val}
-
-build_attribute({atom,La,module}, Val) ->
+%% {attribute,Anno,module,Module}
+%% {attribute,Anno,export,Exports}
+%% {attribute,Anno,import,Imports}
+%% {attribute,Anno,record,{Name,Inits}}
+%% {attribute,Anno,file,{Name,Line}}
+%% {attribute,Anno,Name,Val}
+
+build_attribute({atom,Aa,module}, Val) ->
case Val of
- [{atom,_Lm,Module}] ->
- {attribute,La,module,Module};
- [{atom,_Lm,Module},ExpList] ->
- {attribute,La,module,{Module,var_list(ExpList)}};
+ [{atom,_Am,Module}] ->
+ {attribute,Aa,module,Module};
+ [{atom,_Am,Module},ExpList] ->
+ {attribute,Aa,module,{Module,var_list(ExpList)}};
_Other ->
- error_bad_decl(La, module)
+ error_bad_decl(Aa, module)
end;
-build_attribute({atom,La,export}, Val) ->
+build_attribute({atom,Aa,export}, Val) ->
case Val of
[ExpList] ->
- {attribute,La,export,farity_list(ExpList)};
- _Other -> error_bad_decl(La, export)
+ {attribute,Aa,export,farity_list(ExpList)};
+ _Other -> error_bad_decl(Aa, export)
end;
-build_attribute({atom,La,import}, Val) ->
+build_attribute({atom,Aa,import}, Val) ->
case Val of
- [{atom,_Lm,Mod},ImpList] ->
- {attribute,La,import,{Mod,farity_list(ImpList)}};
- _Other -> error_bad_decl(La, import)
+ [{atom,_Am,Mod},ImpList] ->
+ {attribute,Aa,import,{Mod,farity_list(ImpList)}};
+ _Other -> error_bad_decl(Aa, import)
end;
-build_attribute({atom,La,record}, Val) ->
+build_attribute({atom,Aa,record}, Val) ->
case Val of
- [{atom,_Ln,Record},RecTuple] ->
- {attribute,La,record,{Record,record_tuple(RecTuple)}};
- _Other -> error_bad_decl(La, record)
+ [{atom,_An,Record},RecTuple] ->
+ {attribute,Aa,record,{Record,record_tuple(RecTuple)}};
+ _Other -> error_bad_decl(Aa, record)
end;
-build_attribute({atom,La,file}, Val) ->
+build_attribute({atom,Aa,file}, Val) ->
case Val of
- [{string,_Ln,Name},{integer,_Ll,Line}] ->
- {attribute,La,file,{Name,Line}};
- _Other -> error_bad_decl(La, file)
+ [{string,_An,Name},{integer,_Al,Line}] ->
+ {attribute,Aa,file,{Name,Line}};
+ _Other -> error_bad_decl(Aa, file)
end;
-build_attribute({atom,La,Attr}, Val) ->
+build_attribute({atom,Aa,Attr}, Val) ->
case Val of
[Expr0] ->
Expr = attribute_farity(Expr0),
- {attribute,La,Attr,term(Expr)};
- _Other -> ret_err(La, "bad attribute")
+ {attribute,Aa,Attr,term(Expr)};
+ _Other -> ret_err(Aa, "bad attribute")
end.
-var_list({cons,_Lc,{var,_,V},Tail}) ->
+var_list({cons,_Ac,{var,_,V},Tail}) ->
[V|var_list(Tail)];
-var_list({nil,_Ln}) -> [];
+var_list({nil,_An}) -> [];
var_list(Other) ->
- ret_err(?line(Other), "bad variable list").
+ ret_err(?anno(Other), "bad variable list").
-attribute_farity({cons,L,H,T}) ->
- {cons,L,attribute_farity(H),attribute_farity(T)};
-attribute_farity({tuple,L,Args0}) ->
+attribute_farity({cons,A,H,T}) ->
+ {cons,A,attribute_farity(H),attribute_farity(T)};
+attribute_farity({tuple,A,Args0}) ->
Args = attribute_farity_list(Args0),
- {tuple,L,Args};
-attribute_farity({map,L,Args0}) ->
+ {tuple,A,Args};
+attribute_farity({map,A,Args0}) ->
Args = attribute_farity_map(Args0),
- {map,L,Args};
-attribute_farity({op,L,'/',{atom,_,_}=Name,{integer,_,_}=Arity}) ->
- {tuple,L,[Name,Arity]};
+ {map,A,Args};
+attribute_farity({op,A,'/',{atom,_,_}=Name,{integer,_,_}=Arity}) ->
+ {tuple,A,[Name,Arity]};
attribute_farity(Other) -> Other.
attribute_farity_list(Args) ->
@@ -765,111 +1170,77 @@ attribute_farity_list(Args) ->
%% It is not meaningful to have farity keys.
attribute_farity_map(Args) ->
- [{Op,L,K,attribute_farity(V)} || {Op,L,K,V} <- Args].
+ [{Op,A,K,attribute_farity(V)} || {Op,A,K,V} <- Args].
--spec error_bad_decl(integer(), attributes()) -> no_return().
+-spec error_bad_decl(erl_anno:anno(), attributes()) -> no_return().
-error_bad_decl(L, S) ->
- ret_err(L, io_lib:format("bad ~w declaration", [S])).
+error_bad_decl(Anno, S) ->
+ ret_err(Anno, io_lib:format("bad ~w declaration", [S])).
-farity_list({cons,_Lc,{op,_Lo,'/',{atom,_La,A},{integer,_Li,I}},Tail}) ->
+farity_list({cons,_Ac,{op,_Ao,'/',{atom,_Aa,A},{integer,_Ai,I}},Tail}) ->
[{A,I}|farity_list(Tail)];
-farity_list({nil,_Ln}) -> [];
+farity_list({nil,_An}) -> [];
farity_list(Other) ->
- ret_err(?line(Other), "bad function arity").
+ ret_err(?anno(Other), "bad function arity").
-record_tuple({tuple,_Lt,Fields}) ->
+record_tuple({tuple,_At,Fields}) ->
record_fields(Fields);
record_tuple(Other) ->
- ret_err(?line(Other), "bad record declaration").
+ ret_err(?anno(Other), "bad record declaration").
-record_fields([{atom,La,A}|Fields]) ->
- [{record_field,La,{atom,La,A}}|record_fields(Fields)];
-record_fields([{match,_Lm,{atom,La,A},Expr}|Fields]) ->
- [{record_field,La,{atom,La,A},Expr}|record_fields(Fields)];
+record_fields([{atom,Aa,A}|Fields]) ->
+ [{record_field,Aa,{atom,Aa,A}}|record_fields(Fields)];
+record_fields([{match,_Am,{atom,Aa,A},Expr}|Fields]) ->
+ [{record_field,Aa,{atom,Aa,A},Expr}|record_fields(Fields)];
record_fields([{typed,Expr,TypeInfo}|Fields]) ->
[Field] = record_fields([Expr]),
- TypeInfo1 =
- case Expr of
- {match, _, _, _} -> TypeInfo; %% If we have an initializer.
- {atom, La, _} ->
- case has_undefined(TypeInfo) of
- false ->
- TypeInfo2 = maybe_add_paren(TypeInfo),
- lift_unions(abstract(undefined, La), TypeInfo2);
- true ->
- TypeInfo
- end
- end,
- [{typed_record_field,Field,TypeInfo1}|record_fields(Fields)];
+ [{typed_record_field,Field,TypeInfo}|record_fields(Fields)];
record_fields([Other|_Fields]) ->
- ret_err(?line(Other), "bad record field");
+ ret_err(?anno(Other), "bad record field");
record_fields([]) -> [].
-has_undefined({atom,_,undefined}) ->
- true;
-has_undefined({ann_type,_,[_,T]}) ->
- has_undefined(T);
-has_undefined({paren_type,_,[T]}) ->
- has_undefined(T);
-has_undefined({type,_,union,Ts}) ->
- lists:any(fun has_undefined/1, Ts);
-has_undefined(_) ->
- false.
-
-maybe_add_paren({ann_type,L,T}) ->
- {paren_type,L,[{ann_type,L,T}]};
-maybe_add_paren(T) ->
- T.
-
term(Expr) ->
try normalise(Expr)
- catch _:_R -> ret_err(?line(Expr), "bad attribute")
+ catch _:_R -> ret_err(?anno(Expr), "bad attribute")
end.
-%% build_function([Clause]) -> {function,Line,Name,Arity,[Clause]}
+%% build_function([Clause]) -> {function,Anno,Name,Arity,[Clause]}
build_function(Cs) ->
Name = element(3, hd(Cs)),
Arity = length(element(4, hd(Cs))),
- {function,?line(hd(Cs)),Name,Arity,check_clauses(Cs, Name, Arity)}.
+ {function,?anno(hd(Cs)),Name,Arity,check_clauses(Cs, Name, Arity)}.
-%% build_rule([Clause]) -> {rule,Line,Name,Arity,[Clause]'}
+%% build_fun(Anno, [Clause]) -> {'fun',Anno,{clauses,[Clause]}}.
-build_rule(Cs) ->
- Name = element(3, hd(Cs)),
- Arity = length(element(4, hd(Cs))),
- {rule,?line(hd(Cs)),Name,Arity,check_clauses(Cs, Name, Arity)}.
-
-%% build_fun(Line, [Clause]) -> {'fun',Line,{clauses,[Clause]}}.
-
-build_fun(Line, Cs) ->
+build_fun(Anno, Cs) ->
Name = element(3, hd(Cs)),
Arity = length(element(4, hd(Cs))),
CheckedCs = check_clauses(Cs, Name, Arity),
case Name of
'fun' ->
- {'fun',Line,{clauses,CheckedCs}};
+ {'fun',Anno,{clauses,CheckedCs}};
Name ->
- {named_fun,Line,Name,CheckedCs}
+ {named_fun,Anno,Name,CheckedCs}
end.
check_clauses(Cs, Name, Arity) ->
[case C of
- {clause,L,N,As,G,B} when N =:= Name, length(As) =:= Arity ->
- {clause,L,As,G,B};
- {clause,L,_N,_As,_G,_B} ->
- ret_err(L, "head mismatch")
+ {clause,A,N,As,G,B} when N =:= Name, length(As) =:= Arity ->
+ {clause,A,As,G,B};
+ {clause,A,_N,_As,_G,_B} ->
+ ret_err(A, "head mismatch")
end || C <- Cs].
-build_try(L,Es,Scs,{Ccs,As}) ->
- {'try',L,Es,Scs,Ccs,As}.
+build_try(A,Es,Scs,{Ccs,As}) ->
+ {'try',A,Es,Scs,Ccs,As}.
-spec ret_err(_, _) -> no_return().
-ret_err(L, S) ->
- {location,Location} = get_attribute(L, location),
- return_error(Location, S).
+ret_err(Anno, S) ->
+ return_error(location(Anno), S).
+location(Anno) ->
+ erl_anno:location(Anno).
%% Convert between the abstract form of a term and a term.
@@ -917,7 +1288,8 @@ normalise_list([]) ->
Data :: term(),
AbsTerm :: abstract_expr().
abstract(T) ->
- abstract(T, 0, enc_func(epp:default_encoding())).
+ Anno = erl_anno:new(0),
+ abstract(T, Anno, enc_func(epp:default_encoding())).
-type encoding_func() :: fun((non_neg_integer()) -> boolean()).
@@ -927,16 +1299,18 @@ abstract(T) ->
Options :: Line | [Option],
Option :: {line, Line} | {encoding, Encoding},
Encoding :: 'latin1' | 'unicode' | 'utf8' | 'none' | encoding_func(),
- Line :: erl_scan:line(),
+ Line :: erl_anno:line(),
AbsTerm :: abstract_expr().
abstract(T, Line) when is_integer(Line) ->
- abstract(T, Line, enc_func(epp:default_encoding()));
+ Anno = erl_anno:new(Line),
+ abstract(T, Anno, enc_func(epp:default_encoding()));
abstract(T, Options) when is_list(Options) ->
Line = proplists:get_value(line, Options, 0),
Encoding = proplists:get_value(encoding, Options,epp:default_encoding()),
EncFunc = enc_func(Encoding),
- abstract(T, Line, EncFunc).
+ Anno = erl_anno:new(Line),
+ abstract(T, Anno, EncFunc).
-define(UNICODE(C),
(C < 16#D800 orelse
@@ -950,53 +1324,53 @@ enc_func(none) -> none;
enc_func(Fun) when is_function(Fun, 1) -> Fun;
enc_func(Term) -> erlang:error({badarg, Term}).
-abstract(T, L, _E) when is_integer(T) -> {integer,L,T};
-abstract(T, L, _E) when is_float(T) -> {float,L,T};
-abstract(T, L, _E) when is_atom(T) -> {atom,L,T};
-abstract([], L, _E) -> {nil,L};
-abstract(B, L, _E) when is_bitstring(B) ->
- {bin, L, [abstract_byte(Byte, L) || Byte <- bitstring_to_list(B)]};
-abstract([H|T], L, none=E) ->
- {cons,L,abstract(H, L, E),abstract(T, L, E)};
-abstract(List, L, E) when is_list(List) ->
- abstract_list(List, [], L, E);
-abstract(Tuple, L, E) when is_tuple(Tuple) ->
- {tuple,L,abstract_tuple_list(tuple_to_list(Tuple), L, E)};
-abstract(Map, L, E) when is_map(Map) ->
- {map,L,abstract_map_fields(maps:to_list(Map),L,E)}.
-
-abstract_list([H|T], String, L, E) ->
+abstract(T, A, _E) when is_integer(T) -> {integer,A,T};
+abstract(T, A, _E) when is_float(T) -> {float,A,T};
+abstract(T, A, _E) when is_atom(T) -> {atom,A,T};
+abstract([], A, _E) -> {nil,A};
+abstract(B, A, _E) when is_bitstring(B) ->
+ {bin, A, [abstract_byte(Byte, A) || Byte <- bitstring_to_list(B)]};
+abstract([H|T], A, none=E) ->
+ {cons,A,abstract(H, A, E),abstract(T, A, E)};
+abstract(List, A, E) when is_list(List) ->
+ abstract_list(List, [], A, E);
+abstract(Tuple, A, E) when is_tuple(Tuple) ->
+ {tuple,A,abstract_tuple_list(tuple_to_list(Tuple), A, E)};
+abstract(Map, A, E) when is_map(Map) ->
+ {map,A,abstract_map_fields(maps:to_list(Map),A,E)}.
+
+abstract_list([H|T], String, A, E) ->
case is_integer(H) andalso H >= 0 andalso E(H) of
true ->
- abstract_list(T, [H|String], L, E);
+ abstract_list(T, [H|String], A, E);
false ->
- AbstrList = {cons,L,abstract(H, L, E),abstract(T, L, E)},
- not_string(String, AbstrList, L, E)
+ AbstrList = {cons,A,abstract(H, A, E),abstract(T, A, E)},
+ not_string(String, AbstrList, A, E)
end;
-abstract_list([], String, L, _E) ->
- {string, L, lists:reverse(String)};
-abstract_list(T, String, L, E) ->
- not_string(String, abstract(T, L, E), L, E).
-
-not_string([C|T], Result, L, E) ->
- not_string(T, {cons, L, {integer, L, C}, Result}, L, E);
-not_string([], Result, _L, _E) ->
+abstract_list([], String, A, _E) ->
+ {string, A, lists:reverse(String)};
+abstract_list(T, String, A, E) ->
+ not_string(String, abstract(T, A, E), A, E).
+
+not_string([C|T], Result, A, E) ->
+ not_string(T, {cons, A, {integer, A, C}, Result}, A, E);
+not_string([], Result, _A, _E) ->
Result.
-abstract_tuple_list([H|T], L, E) ->
- [abstract(H, L, E)|abstract_tuple_list(T, L, E)];
-abstract_tuple_list([], _L, _E) ->
+abstract_tuple_list([H|T], A, E) ->
+ [abstract(H, A, E)|abstract_tuple_list(T, A, E)];
+abstract_tuple_list([], _A, _E) ->
[].
-abstract_map_fields(Fs,L,E) ->
- [{map_field_assoc,L,abstract(K,L,E),abstract(V,L,E)}||{K,V}<-Fs].
+abstract_map_fields(Fs,A,E) ->
+ [{map_field_assoc,A,abstract(K,A,E),abstract(V,A,E)}||{K,V}<-Fs].
-abstract_byte(Byte, L) when is_integer(Byte) ->
- {bin_element, L, {integer, L, Byte}, default, default};
-abstract_byte(Bits, L) ->
+abstract_byte(Byte, A) when is_integer(Byte) ->
+ {bin_element, A, {integer, A, Byte}, default, default};
+abstract_byte(Bits, A) ->
Sz = bit_size(Bits),
<<Val:Sz>> = Bits,
- {bin_element, L, {integer, L, Val}, {integer, L, Sz}, default}.
+ {bin_element, A, {integer, A, Val}, {integer, A, Sz}, default}.
%% Generate a list of tokens representing the abstract term.
@@ -1010,32 +1384,32 @@ tokens(Abs) ->
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({char,A,C}, More) -> [{char,A,C}|More];
+tokens({integer,A,N}, More) -> [{integer,A,N}|More];
+tokens({float,A,F}, More) -> [{float,A,F}|More];
+tokens({atom,Aa,A}, More) -> [{atom,Aa,A}|More];
+tokens({var,A,V}, More) -> [{var,A,V}|More];
+tokens({string,A,S}, More) -> [{string,A,S}|More];
+tokens({nil,A}, More) -> [{'[',A},{']',A}|More];
+tokens({cons,A,Head,Tail}, More) ->
+ [{'[',A}|tokens(Head, tokens_tail(Tail, More))];
+tokens({tuple,A,[]}, More) ->
+ [{'{',A},{'}',A}|More];
+tokens({tuple,A,[E|Es]}, More) ->
+ [{'{',A}|tokens(E, tokens_tuple(Es, ?anno(E), More))].
+
+tokens_tail({cons,A,Head,Tail}, More) ->
+ [{',',A}|tokens(Head, tokens_tail(Tail, More))];
+tokens_tail({nil,A}, More) ->
+ [{']',A}|More];
tokens_tail(Other, More) ->
- L = ?line(Other),
- [{'|',L}|tokens(Other, [{']',L}|More])].
+ A = ?anno(Other),
+ [{'|',A}|tokens(Other, [{']',A}|More])].
-tokens_tuple([E|Es], Line, More) ->
- [{',',Line}|tokens(E, tokens_tuple(Es, ?line(E), More))];
-tokens_tuple([], Line, More) ->
- [{'}',Line}|More].
+tokens_tuple([E|Es], Anno, More) ->
+ [{',',Anno}|tokens(E, tokens_tuple(Es, ?anno(E), More))];
+tokens_tuple([], Anno, More) ->
+ [{'}',Anno}|More].
%% Give the relative precedences of operators.
@@ -1090,23 +1464,194 @@ func_prec() -> {800,700}.
max_prec() -> 900.
-%%% [Experimental]. The parser just copies the attributes of the
-%%% scanner tokens to the abstract format. This design decision has
-%%% been hidden to some extent: use set_line() and get_attribute() to
-%%% access the second element of (almost all) of the abstract format
-%%% tuples. A typical use is to negate line numbers to prevent the
-%%% compiler from emitting warnings and errors. The second element can
-%%% (of course) be set to any value, but then these functions no
-%%% longer apply. To get all present attributes as a property list
-%%% get_attributes() should be used.
-
-set_line(L, F) ->
- erl_scan:set_attribute(line, L, F).
-
-get_attribute(L, Name) ->
- erl_scan:attributes_info(L, Name).
-
-get_attributes(L) ->
- erl_scan:attributes_info(L).
+-type prec() :: non_neg_integer().
+
+-type type_inop() :: '::' | '|' | '..' | '+' | '-' | 'bor' | 'bxor'
+ | 'bsl' | 'bsr' | '*' | '/' | 'div' | 'rem' | 'band'.
+
+-type type_preop() :: '+' | '-' | 'bnot' | '#'.
+
+-spec type_inop_prec(type_inop()) -> {prec(), prec(), prec()}.
+
+type_inop_prec('=') -> {150,100,100};
+type_inop_prec('::') -> {160,150,150};
+type_inop_prec('|') -> {180,170,170};
+type_inop_prec('..') -> {300,200,300};
+type_inop_prec('+') -> {400,400,500};
+type_inop_prec('-') -> {400,400,500};
+type_inop_prec('bor') -> {400,400,500};
+type_inop_prec('bxor') -> {400,400,500};
+type_inop_prec('bsl') -> {400,400,500};
+type_inop_prec('bsr') -> {400,400,500};
+type_inop_prec('*') -> {500,500,600};
+type_inop_prec('/') -> {500,500,600};
+type_inop_prec('div') -> {500,500,600};
+type_inop_prec('rem') -> {500,500,600};
+type_inop_prec('band') -> {500,500,600};
+type_inop_prec('#') -> {800,700,800}.
+
+-spec type_preop_prec(type_preop()) -> {prec(), prec()}.
+
+type_preop_prec('+') -> {600,700};
+type_preop_prec('-') -> {600,700};
+type_preop_prec('bnot') -> {600,700};
+type_preop_prec('#') -> {700,800}.
+
+-type erl_parse_tree() :: abstract_clause()
+ | abstract_expr()
+ | abstract_form()
+ | abstract_type().
+
+-spec map_anno(Fun, Abstr) -> NewAbstr when
+ Fun :: fun((Anno) -> Anno),
+ Anno :: erl_anno:anno(),
+ Abstr :: erl_parse_tree(),
+ NewAbstr :: erl_parse_tree().
+
+map_anno(F0, Abstr) ->
+ F = fun(A, Acc) -> {F0(A), Acc} end,
+ {NewAbstr, []} = modify_anno1(Abstr, [], F),
+ NewAbstr.
+
+-spec fold_anno(Fun, Acc0, Abstr) -> NewAbstr when
+ Fun :: fun((Anno, AccIn) -> AccOut),
+ Anno :: erl_anno:anno(),
+ Acc0 :: term(),
+ AccIn :: term(),
+ AccOut :: term(),
+ Abstr :: erl_parse_tree(),
+ NewAbstr :: erl_parse_tree().
+
+fold_anno(F0, Acc0, Abstr) ->
+ F = fun(A, Acc) -> {A, F0(A, Acc)} end,
+ {_, NewAcc} = modify_anno1(Abstr, Acc0, F),
+ NewAcc.
+
+-spec mapfold_anno(Fun, Acc0, Abstr) -> {NewAbstr, Acc1} when
+ Fun :: fun((Anno, AccIn) -> {Anno, AccOut}),
+ Anno :: erl_anno:anno(),
+ Acc0 :: term(),
+ Acc1 :: term(),
+ AccIn :: term(),
+ AccOut :: term(),
+ Abstr :: erl_parse_tree(),
+ NewAbstr :: erl_parse_tree().
+
+mapfold_anno(F, Acc0, Abstr) ->
+ modify_anno1(Abstr, Acc0, F).
+
+-spec new_anno(Term) -> Abstr when
+ Term :: term(),
+ Abstr :: erl_parse_tree().
+
+new_anno(Term) ->
+ map_anno(fun erl_anno:new/1, Term).
+
+-spec anno_to_term(Abstr) -> term() when
+ Abstr :: erl_parse_tree().
+
+anno_to_term(Abstract) ->
+ map_anno(fun erl_anno:to_term/1, Abstract).
+
+-spec anno_from_term(Term) -> erl_parse_tree() when
+ Term :: term().
+
+anno_from_term(Term) ->
+ map_anno(fun erl_anno:from_term/1, Term).
+
+%% Forms.
+%% Recognize what sys_pre_expand does:
+modify_anno1({'fun',A,F,{_,_,_}=Id}, Ac, Mf) ->
+ {A1,Ac1} = Mf(A, Ac),
+ {F1,Ac2} = modify_anno1(F, Ac1, Mf),
+ {{'fun',A1,F1,Id},Ac2};
+modify_anno1({named_fun,A,N,F,{_,_,_}=Id}, Ac, Mf) ->
+ {A1,Ac1} = Mf(A, Ac),
+ {F1,Ac2} = modify_anno1(F, Ac1, Mf),
+ {{named_fun,A1,N,F1,Id},Ac2};
+modify_anno1({attribute,A,N,[V]}, Ac, Mf) ->
+ {{attribute,A1,N1,V1},Ac1} = modify_anno1({attribute,A,N,V}, Ac, Mf),
+ {{attribute,A1,N1,[V1]},Ac1};
+%% End of sys_pre_expand special forms.
+modify_anno1({function,F,A}, Ac, _Mf) ->
+ {{function,F,A},Ac};
+modify_anno1({function,M,F,A}, Ac, Mf) ->
+ {M1,Ac1} = modify_anno1(M, Ac, Mf),
+ {F1,Ac2} = modify_anno1(F, Ac1, Mf),
+ {A1,Ac3} = modify_anno1(A, Ac2, Mf),
+ {{function,M1,F1,A1},Ac3};
+modify_anno1({attribute,A,record,{Name,Fields}}, Ac, Mf) ->
+ {A1,Ac1} = Mf(A, Ac),
+ {Fields1,Ac2} = modify_anno1(Fields, Ac1, Mf),
+ {{attribute,A1,record,{Name,Fields1}},Ac2};
+modify_anno1({attribute,A,spec,{Fun,Types}}, Ac, Mf) ->
+ {A1,Ac1} = Mf(A, Ac),
+ {Types1,Ac2} = modify_anno1(Types, Ac1, Mf),
+ {{attribute,A1,spec,{Fun,Types1}},Ac2};
+modify_anno1({attribute,A,callback,{Fun,Types}}, Ac, Mf) ->
+ {A1,Ac1} = Mf(A, Ac),
+ {Types1,Ac2} = modify_anno1(Types, Ac1, Mf),
+ {{attribute,A1,callback,{Fun,Types1}},Ac2};
+modify_anno1({attribute,A,type,{TypeName,TypeDef,Args}}, Ac, Mf) ->
+ {A1,Ac1} = Mf(A, Ac),
+ {TypeDef1,Ac2} = modify_anno1(TypeDef, Ac1, Mf),
+ {Args1,Ac3} = modify_anno1(Args, Ac2, Mf),
+ {{attribute,A1,type,{TypeName,TypeDef1,Args1}},Ac3};
+modify_anno1({attribute,A,opaque,{TypeName,TypeDef,Args}}, Ac, Mf) ->
+ {A1,Ac1} = Mf(A, Ac),
+ {TypeDef1,Ac2} = modify_anno1(TypeDef, Ac1, Mf),
+ {Args1,Ac3} = modify_anno1(Args, Ac2, Mf),
+ {{attribute,A1,opaque,{TypeName,TypeDef1,Args1}},Ac3};
+modify_anno1({attribute,A,Attr,Val}, Ac, Mf) ->
+ {A1,Ac1} = Mf(A, Ac),
+ {{attribute,A1,Attr,Val},Ac1};
+modify_anno1({warning,W}, Ac, _Mf) ->
+ {{warning,W},Ac};
+modify_anno1({error,W}, Ac, _Mf) ->
+ {{error,W},Ac};
+%% Expressions.
+modify_anno1({clauses,Cs}, Ac, Mf) ->
+ {Cs1,Ac1} = modify_anno1(Cs, Ac, Mf),
+ {{clauses,Cs1},Ac1};
+modify_anno1({typed_record_field,Field,Type}, Ac, Mf) ->
+ {Field1,Ac1} = modify_anno1(Field, Ac, Mf),
+ {Type1,Ac2} = modify_anno1(Type, Ac1, Mf),
+ {{typed_record_field,Field1,Type1},Ac2};
+modify_anno1({Tag,A}, Ac, Mf) ->
+ {A1,Ac1} = Mf(A, Ac),
+ {{Tag,A1},Ac1};
+modify_anno1({Tag,A,E1}, Ac, Mf) ->
+ {A1,Ac1} = Mf(A, Ac),
+ {E11,Ac2} = modify_anno1(E1, Ac1, Mf),
+ {{Tag,A1,E11},Ac2};
+modify_anno1({Tag,A,E1,E2}, Ac, Mf) ->
+ {A1,Ac1} = Mf(A, Ac),
+ {E11,Ac2} = modify_anno1(E1, Ac1, Mf),
+ {E21,Ac3} = modify_anno1(E2, Ac2, Mf),
+ {{Tag,A1,E11,E21},Ac3};
+modify_anno1({bin_element,A,E1,E2,TSL}, Ac, Mf) ->
+ {A1,Ac1} = Mf(A, Ac),
+ {E11,Ac2} = modify_anno1(E1, Ac1, Mf),
+ {E21,Ac3} = modify_anno1(E2, Ac2, Mf),
+ {{bin_element,A1,E11,E21, TSL},Ac3};
+modify_anno1({Tag,A,E1,E2,E3}, Ac, Mf) ->
+ {A1,Ac1} = Mf(A, Ac),
+ {E11,Ac2} = modify_anno1(E1, Ac1, Mf),
+ {E21,Ac3} = modify_anno1(E2, Ac2, Mf),
+ {E31,Ac4} = modify_anno1(E3, Ac3, Mf),
+ {{Tag,A1,E11,E21,E31},Ac4};
+modify_anno1({Tag,A,E1,E2,E3,E4}, Ac, Mf) ->
+ {A1,Ac1} = Mf(A, Ac),
+ {E11,Ac2} = modify_anno1(E1, Ac1, Mf),
+ {E21,Ac3} = modify_anno1(E2, Ac2, Mf),
+ {E31,Ac4} = modify_anno1(E3, Ac3, Mf),
+ {E41,Ac5} = modify_anno1(E4, Ac4, Mf),
+ {{Tag,A1,E11,E21,E31,E41},Ac5};
+modify_anno1([H|T], Ac, Mf) ->
+ {H1,Ac1} = modify_anno1(H, Ac, Mf),
+ {T1,Ac2} = modify_anno1(T, Ac1, Mf),
+ {[H1|T1],Ac2};
+modify_anno1([], Ac, _Mf) -> {[],Ac};
+modify_anno1(E, Ac, _Mf) when not is_tuple(E), not is_list(E) -> {E,Ac}.
%% vim: ft=erlang
diff --git a/lib/stdlib/src/erl_posix_msg.erl b/lib/stdlib/src/erl_posix_msg.erl
index 909cc1d102..bfafca1ff7 100644
--- a/lib/stdlib/src/erl_posix_msg.erl
+++ b/lib/stdlib/src/erl_posix_msg.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
diff --git a/lib/stdlib/src/erl_pp.erl b/lib/stdlib/src/erl_pp.erl
index 1fd6d2a8df..ca764675fc 100644
--- a/lib/stdlib/src/erl_pp.erl
+++ b/lib/stdlib/src/erl_pp.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -22,12 +23,13 @@
%%% the parser. It does not always produce pretty code.
-export([form/1,form/2,
- attribute/1,attribute/2,function/1,function/2,rule/1,rule/2,
+ attribute/1,attribute/2,function/1,function/2,
guard/1,guard/2,exprs/1,exprs/2,exprs/3,expr/1,expr/2,expr/3,expr/4]).
-import(lists, [append/1,foldr/3,mapfoldl/3,reverse/1,reverse/2]).
-import(io_lib, [write/1,format/2]).
--import(erl_parse, [inop_prec/1,preop_prec/1,func_prec/0,max_prec/0]).
+-import(erl_parse, [inop_prec/1,preop_prec/1,func_prec/0,max_prec/0,
+ type_inop_prec/1, type_preop_prec/1]).
-define(MAXLINE, 72).
@@ -46,6 +48,23 @@
-record(options, {hook, encoding, opts}).
+%-define(DEBUG, true).
+
+-ifdef(DEBUG).
+-define(TEST(T),
+ %% Assumes that erl_anno has been compiled with DEBUG=true.
+ %% erl_pp does not use the annoations, but test it anyway.
+ %% Note: hooks are not handled.
+ _ = try
+ erl_parse:map_anno(fun(A) when is_list(A) -> A end, T)
+ catch
+ _:_ ->
+ erlang:error(badarg, [T])
+ end).
+-else.
+-define(TEST(T), ok).
+-endif.
+
%%%
%%% Exported functions
%%%
@@ -61,6 +80,7 @@ form(Thing) ->
Options :: options()).
form(Thing, Options) ->
+ ?TEST(Thing),
State = state(Options),
frmt(lform(Thing, options(Options), State), State).
@@ -75,6 +95,7 @@ attribute(Thing) ->
Options :: options()).
attribute(Thing, Options) ->
+ ?TEST(Thing),
State = state(Options),
frmt(lattribute(Thing, options(Options), State), State).
@@ -89,14 +110,9 @@ function(F) ->
Options :: options()).
function(F, Options) ->
+ ?TEST(F),
frmt(lfunction(F, options(Options)), state(Options)).
-rule(R) ->
- rule(R, none).
-
-rule(R, Options) ->
- frmt(lrule(R, options(Options)), state(Options)).
-
-spec(guard(Guard) -> io_lib:chars() when
Guard :: [erl_parse:abstract_expr()]).
@@ -108,6 +124,7 @@ guard(Gs) ->
Options :: options()).
guard(Gs, Options) ->
+ ?TEST(Gs),
frmt(lguard(Gs, options(Options)), state(Options)).
-spec(exprs(Expressions) -> io_lib:chars() when
@@ -129,12 +146,14 @@ exprs(Es, Options) ->
Options :: options()).
exprs(Es, I, Options) ->
+ ?TEST(Es),
frmt({seq,[],[],[$,],lexprs(Es, options(Options))}, I, state(Options)).
-spec(expr(Expression) -> io_lib:chars() when
Expression :: erl_parse:abstract_expr()).
expr(E) ->
+ ?TEST(E),
frmt(lexpr(E, 0, options(none)), state(none)).
-spec(expr(Expression, Options) -> io_lib:chars() when
@@ -142,6 +161,7 @@ expr(E) ->
Options :: options()).
expr(E, Options) ->
+ ?TEST(E),
frmt(lexpr(E, 0, options(Options)), state(Options)).
-spec(expr(Expression, Indent, Options) -> io_lib:chars() when
@@ -150,6 +170,7 @@ expr(E, Options) ->
Options :: options()).
expr(E, I, Options) ->
+ ?TEST(E),
frmt(lexpr(E, 0, options(Options)), I, state(Options)).
-spec(expr(Expression, Indent, Precedence, Options) -> io_lib:chars() when
@@ -159,6 +180,7 @@ expr(E, I, Options) ->
Options :: options()).
expr(E, I, P, Options) ->
+ ?TEST(E),
frmt(lexpr(E, P, options(Options)), I, state(Options)).
%%%
@@ -199,8 +221,6 @@ lform({attribute,Line,Name,Arg}, Opts, State) ->
lattribute({attribute,Line,Name,Arg}, Opts, State);
lform({function,Line,Name,Arity,Clauses}, Opts, _State) ->
lfunction({function,Line,Name,Arity,Clauses}, Opts);
-lform({rule,Line,Name,Arity,Clauses}, Opts, _State) ->
- lrule({rule,Line,Name,Arity,Clauses}, Opts);
%% These are specials to make it easier for the compiler.
lform({error,E}, _Opts, _State) ->
leaf(format("~p\n", [{error,E}]));
@@ -221,69 +241,97 @@ lattribute({attribute,_Line,Name,Arg}, Opts, State) ->
[lattribute(Name, Arg, Opts, State),leaf(".\n")].
lattribute(module, {M,Vs}, _Opts, _State) ->
- attr("module",[{var,0,pname(M)},
- foldr(fun(V, C) -> {cons,0,{var,0,V},C}
- end, {nil,0}, Vs)]);
+ A = a0(),
+ attr("module",[{var,A,pname(M)},
+ foldr(fun(V, C) -> {cons,A,{var,A,V},C}
+ end, {nil,A}, Vs)]);
lattribute(module, M, _Opts, _State) ->
- attr("module", [{var,0,pname(M)}]);
+ attr("module", [{var,a0(),pname(M)}]);
lattribute(export, Falist, _Opts, _State) ->
- call({var,0,"-export"}, [falist(Falist)], 0, options(none));
+ call({var,a0(),"-export"}, [falist(Falist)], 0, options(none));
lattribute(import, Name, _Opts, _State) when is_list(Name) ->
- attr("import", [{var,0,pname(Name)}]);
+ attr("import", [{var,a0(),pname(Name)}]);
lattribute(import, {From,Falist}, _Opts, _State) ->
- attr("import",[{var,0,pname(From)},falist(Falist)]);
+ attr("import",[{var,a0(),pname(From)},falist(Falist)]);
+lattribute(export_type, Talist, _Opts, _State) ->
+ call({var,a0(),"-export_type"}, [falist(Talist)], 0, options(none));
+lattribute(optional_callbacks, Falist, Opts, _State) ->
+ ArgL = try falist(Falist)
+ catch _:_ -> abstract(Falist, Opts)
+ end,
+ call({var,a0(),"-optional_callbacks"}, [ArgL], 0, options(none));
lattribute(file, {Name,Line}, _Opts, State) ->
- attr("file", [{var,0,(State#pp.string_fun)(Name)},{integer,0,Line}]);
+ attr("file", [{var,a0(),(State#pp.string_fun)(Name)},{integer,a0(),Line}]);
lattribute(record, {Name,Is}, Opts, _State) ->
Nl = leaf(format("-record(~w,", [Name])),
[{first,Nl,record_fields(Is, Opts)},$)];
-lattribute(Name, Arg, #options{encoding = Encoding}, _State) ->
- attr(write(Name), [erl_parse:abstract(Arg, [{encoding,Encoding}])]).
+lattribute(Name, Arg, Options, _State) ->
+ attr(write(Name), [abstract(Arg, Options)]).
+
+abstract(Arg, #options{encoding = Encoding}) ->
+ erl_parse:abstract(Arg, [{encoding,Encoding}]).
typeattr(Tag, {TypeName,Type,Args}, _Opts) ->
{first,leaf("-"++atom_to_list(Tag)++" "),
- typed(call({atom,0,TypeName}, Args, 0, options(none)), Type)}.
-
-ltype({ann_type,_Line,[V,T]}) ->
- typed(lexpr(V, options(none)), T);
-ltype({paren_type,_Line,[T]}) ->
- [$(,ltype(T),$)];
-ltype({type,_Line,union,Ts}) ->
- {seq,[],[],[' |'],ltypes(Ts)};
-ltype({type,_Line,list,[T]}) ->
+ typed(call({atom,a0(),TypeName}, Args, 0, options(none)), Type)}.
+
+ltype(T) ->
+ ltype(T, 0).
+
+ltype({ann_type,_Line,[V,T]}, Prec) ->
+ {_L,P,_R} = type_inop_prec('::'),
+ E = typed(lexpr(V, options(none)), T),
+ maybe_paren(P, Prec, E);
+ltype({paren_type,_Line,[T]}, P) ->
+ %% Generated before Erlang/OTP 18.
+ ltype(T, P);
+ltype({type,_Line,union,Ts}, Prec) ->
+ {_L,P,R} = type_inop_prec('|'),
+ E = {seq,[],[],[' |'],ltypes(Ts, R)},
+ maybe_paren(P, Prec, E);
+ltype({type,_Line,list,[T]}, _) ->
{seq,$[,$],$,,[ltype(T)]};
-ltype({type,_Line,nonempty_list,[T]}) ->
+ltype({type,_Line,nonempty_list,[T]}, _) ->
{seq,$[,$],[$,],[ltype(T),leaf("...")]};
-ltype({type,Line,nil,[]}) ->
- lexpr({nil,Line}, 0, options(none));
-ltype({type,Line,map,any}) ->
+ltype({type,Line,nil,[]}, _) ->
+ lexpr({nil,Line}, options(none));
+ltype({type,Line,map,any}, _) ->
simple_type({atom,Line,map}, []);
-ltype({type,_Line,map,Pairs}) ->
- map_type(Pairs);
-ltype({type,Line,tuple,any}) ->
+ltype({type,_Line,map,Pairs}, Prec) ->
+ {P,_R} = type_preop_prec('#'),
+ E = map_type(Pairs),
+ maybe_paren(P, Prec, E);
+ltype({type,Line,tuple,any}, _) ->
simple_type({atom,Line,tuple}, []);
-ltype({type,_Line,tuple,Ts}) ->
- tuple_type(Ts, fun ltype/1);
-ltype({type,_Line,record,[{atom,_,N}|Fs]}) ->
- record_type(N, Fs);
-ltype({type,_Line,range,[_I1,_I2]=Es}) ->
- expr_list(Es, '..', fun lexpr/2, options(none));
-ltype({type,_Line,binary,[I1,I2]}) ->
+ltype({type,_Line,tuple,Ts}, _) ->
+ tuple_type(Ts, fun ltype/2);
+ltype({type,_Line,record,[{atom,_,N}|Fs]}, Prec) ->
+ {P,_R} = type_preop_prec('#'),
+ E = record_type(N, Fs),
+ maybe_paren(P, Prec, E);
+ltype({type,_Line,range,[_I1,_I2]=Es}, Prec) ->
+ {_L,P,R} = type_inop_prec('..'),
+ F = fun(E, Opts) -> lexpr(E, R, Opts) end,
+ E = expr_list(Es, '..', F, options(none)),
+ maybe_paren(P, Prec, E);
+ltype({type,_Line,binary,[I1,I2]}, _) ->
binary_type(I1, I2); % except binary()
-ltype({type,_Line,'fun',[]}) ->
+ltype({type,_Line,'fun',[]}, _) ->
leaf("fun()");
-ltype({type,_,'fun',[{type,_,any},_]}=FunType) ->
+ltype({type,_,'fun',[{type,_,any},_]}=FunType, _) ->
[fun_type(['fun',$(], FunType),$)];
-ltype({type,_Line,'fun',[{type,_,product,_},_]}=FunType) ->
+ltype({type,_Line,'fun',[{type,_,product,_},_]}=FunType, _) ->
[fun_type(['fun',$(], FunType),$)];
-ltype({type,Line,T,Ts}) ->
+ltype({type,Line,T,Ts}, _) ->
+ simple_type({atom,Line,T}, Ts);
+ltype({user_type,Line,T,Ts}, _) ->
simple_type({atom,Line,T}, Ts);
-ltype({remote_type,Line,[M,F,Ts]}) ->
+ltype({remote_type,Line,[M,F,Ts]}, _) ->
simple_type({remote,Line,M,F}, Ts);
-ltype({atom,_,T}) ->
+ltype({atom,_,T}, _) ->
leaf(write(T));
-ltype(E) ->
- lexpr(E, 0, options(none)).
+ltype(E, P) ->
+ lexpr(E, P, options(none)).
binary_type(I1, I2) ->
B = [[] || {integer,_,0} <- [I1]] =:= [],
@@ -296,43 +344,47 @@ binary_type(I1, I2) ->
map_type(Fs) ->
{first,[$#],map_pair_types(Fs)}.
-map_pair_types(Fs) ->
- tuple_type(Fs, fun map_pair_type/1).
-
-map_pair_type({type,_Line,map_field_assoc,Ktype,Vtype}) ->
- map_assoc_typed(ltype(Ktype), Vtype).
+map_pair_types(Fs0) ->
+ Fs = replace_any_map(Fs0),
+ tuple_type(Fs, fun map_pair_type/2).
-map_assoc_typed(B, {type,_,union,Ts}) ->
- {first,[B,$\s],{seq,[],[],[],map_assoc_union_type(Ts)}};
-map_assoc_typed(B, Type) ->
- {list,[{cstep,[B," =>"],ltype(Type)}]}.
+replace_any_map([{type,Line,map_field_assoc,[KType,VType]}]=Fs) ->
+ IsAny = fun({type,_,any,[]}) -> true;
+ %% ({var,_,'_'}) -> true;
+ (_) -> false
+ end,
+ case IsAny(KType) andalso IsAny(VType) of
+ true ->
+ [{type,Line,map_field_assoc,any}];
+ false ->
+ Fs
+ end;
+replace_any_map([F|Fs]) ->
+ [F|replace_any_map(Fs)];
+replace_any_map([]) -> [].
-map_assoc_union_type([T|Ts]) ->
- [[leaf("=> "),ltype(T)] | ltypes(Ts, fun union_elem/1)].
+map_pair_type({type,_Line,map_field_assoc,any}, _Prec) ->
+ leaf("...");
+map_pair_type({type,_Line,map_field_assoc,[KType,VType]}, Prec) ->
+ {list,[{cstep,[ltype(KType, Prec),leaf(" =>")],ltype(VType, Prec)}]};
+map_pair_type({type,_Line,map_field_exact,[KType,VType]}, Prec) ->
+ {list,[{cstep,[ltype(KType, Prec),leaf(" :=")],ltype(VType, Prec)}]}.
record_type(Name, Fields) ->
{first,[record_name(Name)],field_types(Fields)}.
field_types(Fs) ->
- tuple_type(Fs, fun field_type/1).
+ tuple_type(Fs, fun field_type/2).
-field_type({type,_Line,field_type,[Name,Type]}) ->
+field_type({type,_Line,field_type,[Name,Type]}, _Prec) ->
typed(lexpr(Name, options(none)), Type).
-typed(B, {type,_,union,Ts}) ->
- %% Special layout for :: followed by union.
- {first,[B,$\s],{seq,[],[],[],union_type(Ts)}};
typed(B, Type) ->
- {list,[{cstep,[B,' ::'],ltype(Type)}]}.
-
-union_type([T|Ts]) ->
- [[leaf(":: "),ltype(T)] | ltypes(Ts, fun union_elem/1)].
-
-union_elem(T) ->
- [leaf(" | "),ltype(T)].
+ {_L,_P,R} = type_inop_prec('::'),
+ {list,[{cstep,[B,' ::'],ltype(Type, R)}]}.
tuple_type(Ts, F) ->
- {seq,${,$},[$,],ltypes(Ts, F)}.
+ {seq,${,$},[$,],ltypes(Ts, F, 0)}.
specattr(SpecKind, {FuncSpec,TypeSpecs}) ->
Func = case FuncSpec of
@@ -357,6 +409,9 @@ guard_type(Before, Gs) ->
Gl = {list,[{step,'when',expr_list(Gs, [$,], fun constraint/2, Opts)}]},
{list,[{step,Before,Gl}]}.
+constraint({type,_Line,constraint,[{atom,_,is_subtype},[{var,_,_}=V,Type]]},
+ _Opts) ->
+ typed(lexpr(V, options(none)), Type);
constraint({type,_Line,constraint,[Tag,As]}, _Opts) ->
simple_type(Tag, As).
@@ -369,19 +424,19 @@ type_args({type,_line,product,Ts}) ->
targs(Ts).
simple_type(Tag, Types) ->
- {first,lexpr(Tag, 0, options(none)),targs(Types)}.
+ {first,lexpr(Tag, options(none)),targs(Types)}.
targs(Ts) ->
- {seq,$(,$),[$,],ltypes(Ts)}.
+ {seq,$(,$),[$,],ltypes(Ts, 0)}.
-ltypes(Ts) ->
- ltypes(Ts, fun ltype/1).
+ltypes(Ts, Prec) ->
+ ltypes(Ts, fun ltype/2, Prec).
-ltypes(Ts, F) ->
- [F(T) || T <- Ts].
+ltypes(Ts, F, Prec) ->
+ [F(T, Prec) || T <- Ts].
attr(Name, Args) ->
- call({var,0,format("-~s", [Name])}, Args, 0, options(none)).
+ call({var,a0(),format("-~s", [Name])}, Args, 0, options(none)).
pname(['' | As]) ->
[$. | pname(As)];
@@ -393,9 +448,10 @@ pname(A) when is_atom(A) ->
write(A).
falist([]) ->
- {nil,0};
+ {nil,a0()};
falist([{Name,Arity}|Falist]) ->
- {cons,0,{var,0,format("~w/~w", [Name,Arity])},falist(Falist)}.
+ A = a0(),
+ {cons,A,{var,A,format("~w/~w", [Name,Arity])},falist(Falist)}.
lfunction({function,_Line,Name,_Arity,Cs}, Opts) ->
Cll = nl_clauses(fun (C, H) -> func_clause(Name, C, H) end, $;, Opts, Cs),
@@ -407,19 +463,6 @@ func_clause(Name, {clause,Line,Head,Guard,Body}, Opts) ->
Bl = body(Body, Opts),
{step,Gl,Bl}.
-lrule({rule,_Line,Name,_Arity,Cs}, Opts) ->
- Cll = nl_clauses(fun (C, H) -> rule_clause(Name, C, H) end, $;, Opts, Cs),
- [Cll,leaf(".\n")].
-
-rule_clause(Name, {clause,Line,Head,Guard,Body}, Opts) ->
- Hl = call({atom,Line,Name}, Head, 0, Opts),
- Gl = guard_when(Hl, Guard, Opts, leaf(" :-")),
- Bl = rule_body(Body, Opts),
- {step,Gl,Bl}.
-
-rule_body(Es, Opts) ->
- lc_quals(Es, Opts).
-
guard_when(Before, Guard, Opts) ->
guard_when(Before, Guard, Opts, ' ->').
@@ -1121,6 +1164,9 @@ write_char(C, PP) ->
%% Utilities
%%
+a0() ->
+ erl_anno:new(0).
+
chars_size([C | Es]) when is_integer(C) ->
1 + chars_size(Es);
chars_size([E | Es]) ->
diff --git a/lib/stdlib/src/erl_scan.erl b/lib/stdlib/src/erl_scan.erl
index 6fd6bb888b..47223b129c 100644
--- a/lib/stdlib/src/erl_scan.erl
+++ b/lib/stdlib/src/erl_scan.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2015. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -51,16 +52,15 @@
%%% External exports
-export([string/1,string/2,string/3,tokens/3,tokens/4,
- format_error/1,reserved_word/1,
- token_info/1,token_info/2,
- attributes_info/1,attributes_info/2,set_attribute/3]).
+ format_error/1,reserved_word/1]).
+
+-export([column/1,end_location/1,line/1,location/1,text/1,
+ category/1,symbol/1]).
%%% Private
-export([continuation_location/1]).
-export_type([error_info/0,
- line/0,
- location/0,
options/0,
return_cont/0,
token/0,
@@ -75,29 +75,18 @@
-define(ALINE(L), is_integer(L)).
-define(STRING(S), is_list(S)).
-define(RESWORDFUN(F), is_function(F, 1)).
--define(SETATTRFUN(F), is_function(F, 1)).
-type category() :: atom().
--type column() :: pos_integer().
--type line() :: integer().
--type location() :: line() | {line(),column()}.
-type resword_fun() :: fun((atom()) -> boolean()).
-type option() :: 'return' | 'return_white_spaces' | 'return_comments'
| 'text' | {'reserved_word_fun', resword_fun()}.
-type options() :: option() | [option()].
-type symbol() :: atom() | float() | integer() | string().
--type info_line() :: integer() | term().
--type attributes_data()
- :: [{'column', column()} | {'line', info_line()} | {'text', string()}]
- | {line(), column()}.
-%% The fact that {line(),column()} is a possible attributes() type
-%% is hidden.
--type attributes() :: line() | attributes_data().
--type token() :: {category(), attributes(), symbol()}
- | {category(), attributes()}.
+-type token() :: {category(), Anno :: erl_anno:anno(), symbol()}
+ | {category(), Anno :: erl_anno:anno()}.
-type tokens() :: [token()].
-type error_description() :: term().
--type error_info() :: {location(), module(), error_description()}.
+-type error_info() :: {erl_anno:location(), module(), error_description()}.
%%% Local record.
-record(erl_scan,
@@ -126,8 +115,8 @@ format_error(Other) ->
String :: string(),
Return :: {'ok', Tokens :: tokens(), EndLocation}
| {'error', ErrorInfo :: error_info(), ErrorLocation},
- EndLocation :: location(),
- ErrorLocation :: location().
+ EndLocation :: erl_anno:location(),
+ ErrorLocation :: erl_anno:location().
string(String) ->
string(String, 1, []).
@@ -135,9 +124,9 @@ string(String) ->
String :: string(),
Return :: {'ok', Tokens :: tokens(), EndLocation}
| {'error', ErrorInfo :: error_info(), ErrorLocation},
- StartLocation :: location(),
- EndLocation :: location(),
- ErrorLocation :: location().
+ StartLocation :: erl_anno:location(),
+ EndLocation :: erl_anno:location(),
+ ErrorLocation :: erl_anno:location().
string(String, StartLocation) ->
string(String, StartLocation, []).
@@ -146,9 +135,9 @@ string(String, StartLocation) ->
Options :: options(),
Return :: {'ok', Tokens :: tokens(), EndLocation}
| {'error', ErrorInfo :: error_info(), ErrorLocation},
- StartLocation :: location(),
- EndLocation :: location(),
- ErrorLocation :: location().
+ StartLocation :: erl_anno:location(),
+ EndLocation :: erl_anno:location(),
+ ErrorLocation :: erl_anno:location().
string(String, Line, Options) when ?STRING(String), ?ALINE(Line) ->
string1(String, options(Options), Line, no_col, []);
string(String, {Line,Column}, Options) when ?STRING(String),
@@ -157,20 +146,23 @@ string(String, {Line,Column}, Options) when ?STRING(String),
string1(String, options(Options), Line, Column, []).
-type char_spec() :: string() | 'eof'.
--type cont_fun() :: fun((char_spec(), #erl_scan{}, line(), column(),
+-type cont_fun() :: fun((char_spec(), #erl_scan{},
+ erl_anno:line(), erl_anno:column(),
tokens(), any()) -> any()).
-opaque return_cont() :: {erl_scan_continuation,
- string(), column(), tokens(), line(),
+ string(), erl_anno:column(), tokens(),
+ erl_anno:line(),
#erl_scan{}, any(), cont_fun()}.
--type tokens_result() :: {'ok', Tokens :: tokens(), EndLocation :: location()}
- | {'eof', EndLocation :: location()}
+-type tokens_result() :: {'ok', Tokens :: tokens(),
+ EndLocation :: erl_anno:location()}
+ | {'eof', EndLocation :: erl_anno:location()}
| {'error', ErrorInfo :: error_info(),
- EndLocation :: location()}.
+ EndLocation :: erl_anno:location()}.
-spec tokens(Continuation, CharSpec, StartLocation) -> Return when
Continuation :: return_cont() | [],
CharSpec :: char_spec(),
- StartLocation :: location(),
+ StartLocation :: erl_anno:location(),
Return :: {'done',Result :: tokens_result(),LeftOverChars :: char_spec()}
| {'more', Continuation1 :: return_cont()}.
tokens(Cont, CharSpec, StartLocation) ->
@@ -179,7 +171,7 @@ tokens(Cont, CharSpec, StartLocation) ->
-spec tokens(Continuation, CharSpec, StartLocation, Options) -> Return when
Continuation :: return_cont() | [],
CharSpec :: char_spec(),
- StartLocation :: location(),
+ StartLocation :: erl_anno:location(),
Options :: options(),
Return :: {'done',Result :: tokens_result(),LeftOverChars :: char_spec()}
| {'more', Continuation1 :: return_cont()}.
@@ -197,132 +189,55 @@ continuation_location({erl_scan_continuation,_,no_col,_,Line,_,_,_}) ->
continuation_location({erl_scan_continuation,_,Col,_,Line,_,_,_}) ->
{Line,Col}.
--type attribute_item() :: 'column' | 'length' | 'line'
- | 'location' | 'text'.
--type info_location() :: location() | term().
--type attribute_info() :: {'column', column()}| {'length', pos_integer()}
- | {'line', info_line()}
- | {'location', info_location()}
- | {'text', string()}.
--type token_item() :: 'category' | 'symbol' | attribute_item().
--type token_info() :: {'category', category()} | {'symbol', symbol()}
- | attribute_info().
-
--spec token_info(Token) -> TokenInfo when
- Token :: token(),
- TokenInfo :: [TokenInfoTuple :: token_info()].
-token_info(Token) ->
- Items = [category,column,length,line,symbol,text], % undefined order
- token_info(Token, Items).
-
--spec token_info(Token, TokenItem) -> TokenInfoTuple | 'undefined' when
- Token :: token(),
- TokenItem :: token_item(),
- TokenInfoTuple :: token_info();
- (Token, TokenItems) -> TokenInfo when
- Token :: token(),
- TokenItems :: [TokenItem :: token_item()],
- TokenInfo :: [TokenInfoTuple :: token_info()].
-token_info(_Token, []) ->
- [];
-token_info(Token, [Item|Items]) when is_atom(Item) ->
- case token_info(Token, Item) of
- undefined ->
- token_info(Token, Items);
- TokenInfo when is_tuple(TokenInfo) ->
- [TokenInfo|token_info(Token, Items)]
- end;
-token_info({Category,_Attrs}, category=Item) ->
- {Item,Category};
-token_info({Category,_Attrs,_Symbol}, category=Item) ->
- {Item,Category};
-token_info({Category,_Attrs}, symbol=Item) ->
- {Item,Category};
-token_info({_Category,_Attrs,Symbol}, symbol=Item) ->
- {Item,Symbol};
-token_info({_Category,Attrs}, Item) ->
- attributes_info(Attrs, Item);
-token_info({_Category,Attrs,_Symbol}, Item) ->
- attributes_info(Attrs, Item).
-
--spec attributes_info(Attributes) -> AttributesInfo when
- Attributes :: attributes(),
- AttributesInfo :: [AttributeInfoTuple :: attribute_info()].
-attributes_info(Attributes) ->
- Items = [column,length,line,text], % undefined order
- attributes_info(Attributes, Items).
-
--spec attributes_info
- (Attributes, AttributeItem) -> AttributeInfoTuple | 'undefined' when
- Attributes :: attributes(),
- AttributeItem :: attribute_item(),
- AttributeInfoTuple :: attribute_info();
- (Attributes, AttributeItems) -> AttributeInfo when
- Attributes :: attributes(),
- AttributeItems :: [AttributeItem :: attribute_item()],
- AttributeInfo :: [AttributeInfoTuple :: attribute_info()].
-attributes_info(_Attrs, []) ->
- [];
-attributes_info(Attrs, [A|As]) when is_atom(A) ->
- case attributes_info(Attrs, A) of
- undefined ->
- attributes_info(Attrs, As);
- AttributeInfo when is_tuple(AttributeInfo) ->
- [AttributeInfo|attributes_info(Attrs, As)]
- end;
-attributes_info({Line,Column}, column=Item) when ?ALINE(Line),
- ?COLUMN(Column) ->
- {Item,Column};
-attributes_info(Line, column) when ?ALINE(Line) ->
- undefined;
-attributes_info(Attrs, column=Item) ->
- attr_info(Attrs, Item);
-attributes_info(Attrs, length=Item) ->
- case attributes_info(Attrs, text) of
- undefined ->
- undefined;
- {text,Text} ->
- {Item,length(Text)}
- end;
-attributes_info(Line, line=Item) when ?ALINE(Line) ->
- {Item,Line};
-attributes_info({Line,Column}, line=Item) when ?ALINE(Line),
- ?COLUMN(Column) ->
- {Item,Line};
-attributes_info(Attrs, line=Item) ->
- attr_info(Attrs, Item);
-attributes_info({Line,Column}=Location, location=Item) when ?ALINE(Line),
- ?COLUMN(Column) ->
- {Item,Location};
-attributes_info(Line, location=Item) when ?ALINE(Line) ->
- {Item,Line};
-attributes_info(Attrs, location=Item) ->
- {line,Line} = attributes_info(Attrs, line), % assume line is present
- case attributes_info(Attrs, column) of
- undefined ->
- %% If set_attribute() has assigned a term such as {17,42}
- %% to 'line', then Line will look like {Line,Column}. One
- %% should not use 'location' but 'line' and 'column' in
- %% such special cases.
- {Item,Line};
- {column,Column} ->
- {Item,{Line,Column}}
- end;
-attributes_info({Line,Column}, text) when ?ALINE(Line), ?COLUMN(Column) ->
- undefined;
-attributes_info(Line, text) when ?ALINE(Line) ->
- undefined;
-attributes_info(Attrs, text=Item) ->
- attr_info(Attrs, Item);
-attributes_info(T1, T2) ->
- erlang:error(badarg, [T1,T2]).
-
--spec set_attribute(AttributeItem, Attributes, SetAttributeFun) -> Attributes when
- AttributeItem :: 'line',
- Attributes :: attributes(),
- SetAttributeFun :: fun((info_line()) -> info_line()).
-set_attribute(Tag, Attributes, Fun) when ?SETATTRFUN(Fun) ->
- set_attr(Tag, Attributes, Fun).
+-spec column(Token) -> erl_anno:column() | 'undefined' when
+ Token :: token().
+
+column(Token) ->
+ erl_anno:column(element(2, Token)).
+
+-spec end_location(Token) -> erl_anno:location() | 'undefined' when
+ Token :: token().
+
+end_location(Token) ->
+ erl_anno:end_location(element(2, Token)).
+
+-spec line(Token) -> erl_anno:line() when
+ Token :: token().
+
+line(Token) ->
+ erl_anno:line(element(2, Token)).
+
+-spec location(Token) -> erl_anno:location() when
+ Token :: token().
+
+location(Token) ->
+ erl_anno:location(element(2, Token)).
+
+-spec text(Token) -> erl_anno:text() | 'undefined' when
+ Token :: token().
+
+text(Token) ->
+ erl_anno:text(element(2, Token)).
+
+-spec category(Token) -> category() when
+ Token :: token().
+
+category({Category,_Anno}) ->
+ Category;
+category({Category,_Anno,_Symbol}) ->
+ Category;
+category(T) ->
+ erlang:error(badarg, [T]).
+
+-spec symbol(Token) -> symbol() when
+ Token :: token().
+
+symbol({Category,_Anno}) ->
+ Category;
+symbol({_Category,_Anno,Symbol}) ->
+ Symbol;
+symbol(T) ->
+ erlang:error(badarg, [T]).
%%%
%%% Local functions
@@ -389,46 +304,6 @@ expand_opt(return, Os) ->
expand_opt(O, Os) ->
[O|Os].
-attr_info(Attrs, Item) ->
- try lists:keyfind(Item, 1, Attrs) of
- {_Item, _Value} = T ->
- T;
- false ->
- undefined
- catch
- _:_ ->
- erlang:error(badarg, [Attrs, Item])
- end.
-
--spec set_attr('line', attributes(), fun((line()) -> line())) -> attributes().
-
-set_attr(line, Line, Fun) when ?ALINE(Line) ->
- Ln = Fun(Line),
- if
- ?ALINE(Ln) ->
- Ln;
- true ->
- [{line,Ln}]
- end;
-set_attr(line, {Line,Column}, Fun) when ?ALINE(Line), ?COLUMN(Column) ->
- Ln = Fun(Line),
- if
- ?ALINE(Ln) ->
- {Ln,Column};
- true ->
- [{line,Ln},{column,Column}]
- end;
-set_attr(line=Tag, Attrs, Fun) when is_list(Attrs) ->
- {line,Line} = lists:keyfind(Tag, 1, Attrs),
- case lists:keyreplace(Tag, 1, Attrs, {line,Fun(Line)}) of
- [{line,Ln}] when ?ALINE(Ln) ->
- Ln;
- As ->
- As
- end;
-set_attr(T1, T2, T3) ->
- erlang:error(badarg, [T1,T2,T3]).
-
tokens1(Cs, St, Line, Col, Toks, Fun, Any) when ?STRING(Cs); Cs =:= eof ->
case Fun(Cs, St, Line, Col, Toks, Any) of
{more,{Cs0,Ncol,Ntoks,Nline,Nany,Nfun}} ->
@@ -599,9 +474,6 @@ scan1("|"=Cs, _St, Line, Col, Toks) ->
%% :=
scan1(":="++Cs, St, Line, Col, Toks) ->
tok2(Cs, St, Line, Col, Toks, ":=", ':=', 2);
-%% :-
-scan1(":-"++Cs, St, Line, Col, Toks) ->
- tok2(Cs, St, Line, Col, Toks, ":-", ':-', 2);
%% :: for typed records
scan1("::"++Cs, St, Line, Col, Toks) ->
tok2(Cs, St, Line, Col, Toks, "::", '::', 2);
@@ -711,17 +583,17 @@ scan_name(Cs, Ncs) ->
-define(STR(St, S), if St#erl_scan.text -> S; true -> [] end).
scan_dot([$%|_]=Cs, St, Line, Col, Toks, Ncs) ->
- Attrs = attributes(Line, Col, St, Ncs),
- {ok,[{dot,Attrs}|Toks],Cs,Line,incr_column(Col, 1)};
+ Anno = anno(Line, Col, St, Ncs),
+ {ok,[{dot,Anno}|Toks],Cs,Line,incr_column(Col, 1)};
scan_dot([$\n=C|Cs], St, Line, Col, Toks, Ncs) ->
- Attrs = attributes(Line, Col, St, ?STR(St, Ncs++[C])),
- {ok,[{dot,Attrs}|Toks],Cs,Line+1,new_column(Col, 1)};
+ Anno = anno(Line, Col, St, ?STR(St, Ncs++[C])),
+ {ok,[{dot,Anno}|Toks],Cs,Line+1,new_column(Col, 1)};
scan_dot([C|Cs], St, Line, Col, Toks, Ncs) when ?WHITE_SPACE(C) ->
- Attrs = attributes(Line, Col, St, ?STR(St, Ncs++[C])),
- {ok,[{dot,Attrs}|Toks],Cs,Line,incr_column(Col, 2)};
+ Anno = anno(Line, Col, St, ?STR(St, Ncs++[C])),
+ {ok,[{dot,Anno}|Toks],Cs,Line,incr_column(Col, 2)};
scan_dot(eof=Cs, St, Line, Col, Toks, Ncs) ->
- Attrs = attributes(Line, Col, St, Ncs),
- {ok,[{dot,Attrs}|Toks],Cs,Line,incr_column(Col, 1)};
+ Anno = anno(Line, Col, St, Ncs),
+ {ok,[{dot,Anno}|Toks],Cs,Line,incr_column(Col, 1)};
scan_dot(Cs, St, Line, Col, Toks, Ncs) ->
tok2(Cs, St, Line, Col, Toks, Ncs, '.', 1).
@@ -776,12 +648,12 @@ scan_nl_tabs(Cs, St, Line, Col, Toks, N) ->
%% stop anyway, nothing is gained by not collecting all white spaces.
scan_nl_white_space([$\n|Cs], #erl_scan{text = false}=St, Line, no_col=Col,
Toks0, Ncs) ->
- Toks = [{white_space,Line,lists:reverse(Ncs)}|Toks0],
+ Toks = [{white_space,anno(Line),lists:reverse(Ncs)}|Toks0],
scan_newline(Cs, St, Line+1, Col, Toks);
scan_nl_white_space([$\n|Cs], St, Line, Col, Toks, Ncs0) ->
Ncs = lists:reverse(Ncs0),
- Attrs = attributes(Line, Col, St, Ncs),
- Token = {white_space,Attrs,Ncs},
+ Anno = anno(Line, Col, St, Ncs),
+ Token = {white_space,Anno,Ncs},
scan_newline(Cs, St, Line+1, new_column(Col, length(Ncs)), [Token|Toks]);
scan_nl_white_space([C|Cs], St, Line, Col, Toks, Ncs) when ?WHITE_SPACE(C) ->
scan_nl_white_space(Cs, St, Line, Col, Toks, [C|Ncs]);
@@ -789,19 +661,20 @@ scan_nl_white_space([]=Cs, _St, Line, Col, Toks, Ncs) ->
{more,{Cs,Col,Toks,Line,Ncs,fun scan_nl_white_space/6}};
scan_nl_white_space(Cs, #erl_scan{text = false}=St, Line, no_col=Col,
Toks, Ncs) ->
- scan1(Cs, St, Line+1, Col, [{white_space,Line,lists:reverse(Ncs)}|Toks]);
+ Anno = anno(Line),
+ scan1(Cs, St, Line+1, Col, [{white_space,Anno,lists:reverse(Ncs)}|Toks]);
scan_nl_white_space(Cs, St, Line, Col, Toks, Ncs0) ->
Ncs = lists:reverse(Ncs0),
- Attrs = attributes(Line, Col, St, Ncs),
- Token = {white_space,Attrs,Ncs},
+ Anno = anno(Line, Col, St, Ncs),
+ Token = {white_space,Anno,Ncs},
scan1(Cs, St, Line+1, new_column(Col, length(Ncs)), [Token|Toks]).
newline_end(Cs, #erl_scan{text = false}=St, Line, no_col=Col,
Toks, _N, Ncs) ->
- scan1(Cs, St, Line+1, Col, [{white_space,Line,Ncs}|Toks]);
+ scan1(Cs, St, Line+1, Col, [{white_space,anno(Line),Ncs}|Toks]);
newline_end(Cs, St, Line, Col, Toks, N, Ncs) ->
- Attrs = attributes(Line, Col, St, Ncs),
- scan1(Cs, St, Line+1, new_column(Col, N), [{white_space,Attrs,Ncs}|Toks]).
+ Anno = anno(Line, Col, St, Ncs),
+ scan1(Cs, St, Line+1, new_column(Col, N), [{white_space,Anno,Ncs}|Toks]).
scan_spcs([$\s|Cs], St, Line, Col, Toks, N) when N < 16 ->
scan_spcs(Cs, St, Line, Col, Toks, N+1);
@@ -850,20 +723,20 @@ scan_char([$\\|Cs]=Cs0, St, Line, Col, Toks) ->
{eof,Ncol} ->
scan_error(char, Line, Col, Line, Ncol, eof);
{nl,Val,Str,Ncs,Ncol} ->
- Attrs = attributes(Line, Col, St, ?STR(St, "$\\"++Str)), %"
- Ntoks = [{char,Attrs,Val}|Toks],
+ Anno = anno(Line, Col, St, ?STR(St, "$\\"++Str)), %"
+ Ntoks = [{char,Anno,Val}|Toks],
scan1(Ncs, St, Line+1, Ncol, Ntoks);
{Val,Str,Ncs,Ncol} ->
- Attrs = attributes(Line, Col, St, ?STR(St, "$\\"++Str)), %"
- Ntoks = [{char,Attrs,Val}|Toks],
+ Anno = anno(Line, Col, St, ?STR(St, "$\\"++Str)), %"
+ Ntoks = [{char,Anno,Val}|Toks],
scan1(Ncs, St, Line, Ncol, Ntoks)
end;
scan_char([$\n=C|Cs], St, Line, Col, Toks) ->
- Attrs = attributes(Line, Col, St, ?STR(St, [$$,C])),
- scan1(Cs, St, Line+1, new_column(Col, 1), [{char,Attrs,C}|Toks]);
+ Anno = anno(Line, Col, St, ?STR(St, [$$,C])),
+ scan1(Cs, St, Line+1, new_column(Col, 1), [{char,Anno,C}|Toks]);
scan_char([C|Cs], St, Line, Col, Toks) when ?UNICODE(C) ->
- Attrs = attributes(Line, Col, St, ?STR(St, [$$,C])),
- scan1(Cs, St, Line, incr_column(Col, 2), [{char,Attrs,C}|Toks]);
+ Anno = anno(Line, Col, St, ?STR(St, [$$,C])),
+ scan1(Cs, St, Line, incr_column(Col, 2), [{char,Anno,C}|Toks]);
scan_char([C|_Cs], _St, Line, Col, _Toks) when ?CHAR(C) ->
scan_error({illegal,character}, Line, Col, Line, incr_column(Col, 1), eof);
scan_char([], _St, Line, Col, Toks) ->
@@ -882,8 +755,8 @@ scan_string(Cs, St, Line, Col, Toks, {Wcs,Str,Line0,Col0}) ->
Estr = string:substr(Nwcs, 1, 16), % Expanded escape chars.
scan_error({string,$\",Estr}, Line0, Col0, Nline, Ncol, Ncs); %"
{Ncs,Nline,Ncol,Nstr,Nwcs} ->
- Attrs = attributes(Line0, Col0, St, Nstr),
- scan1(Ncs, St, Nline, Ncol, [{string,Attrs,Nwcs}|Toks])
+ Anno = anno(Line0, Col0, St, Nstr),
+ scan1(Ncs, St, Nline, Ncol, [{string,Anno,Nwcs}|Toks])
end.
scan_qatom(Cs, St, Line, Col, Toks, {Wcs,Str,Line0,Col0}) ->
@@ -899,8 +772,8 @@ scan_qatom(Cs, St, Line, Col, Toks, {Wcs,Str,Line0,Col0}) ->
{Ncs,Nline,Ncol,Nstr,Nwcs} ->
case catch list_to_atom(Nwcs) of
A when is_atom(A) ->
- Attrs = attributes(Line0, Col0, St, Nstr),
- scan1(Ncs, St, Nline, Ncol, [{atom,Attrs,A}|Toks]);
+ Anno = anno(Line0, Col0, St, Nstr),
+ scan1(Ncs, St, Nline, Ncol, [{atom,Anno,A}|Toks]);
_ ->
scan_error({illegal,atom}, Line0, Col0, Nline, Ncol, Ncs)
end
@@ -1176,28 +1049,28 @@ scan_comment(Cs, St, Line, Col, Toks, Ncs0) ->
tok3(Cs, St, Line, Col, Toks, comment, Ncs, Ncs).
tok2(Cs, #erl_scan{text = false}=St, Line, no_col=Col, Toks, _Wcs, P) ->
- scan1(Cs, St, Line, Col, [{P,Line}|Toks]);
+ scan1(Cs, St, Line, Col, [{P,anno(Line)}|Toks]);
tok2(Cs, St, Line, Col, Toks, Wcs, P) ->
- Attrs = attributes(Line, Col, St, Wcs),
- scan1(Cs, St, Line, incr_column(Col, length(Wcs)), [{P,Attrs}|Toks]).
+ Anno = anno(Line, Col, St, Wcs),
+ scan1(Cs, St, Line, incr_column(Col, length(Wcs)), [{P,Anno}|Toks]).
tok2(Cs, #erl_scan{text = false}=St, Line, no_col=Col, Toks, _Wcs, P, _N) ->
- scan1(Cs, St, Line, Col, [{P,Line}|Toks]);
+ scan1(Cs, St, Line, Col, [{P,anno(Line)}|Toks]);
tok2(Cs, St, Line, Col, Toks, Wcs, P, N) ->
- Attrs = attributes(Line, Col, St, Wcs),
- scan1(Cs, St, Line, incr_column(Col, N), [{P,Attrs}|Toks]).
+ Anno = anno(Line, Col, St, Wcs),
+ scan1(Cs, St, Line, incr_column(Col, N), [{P,Anno}|Toks]).
tok3(Cs, #erl_scan{text = false}=St, Line, no_col=Col, Toks, Item, _S, Sym) ->
- scan1(Cs, St, Line, Col, [{Item,Line,Sym}|Toks]);
+ scan1(Cs, St, Line, Col, [{Item,anno(Line),Sym}|Toks]);
tok3(Cs, St, Line, Col, Toks, Item, String, Sym) ->
- Token = {Item,attributes(Line, Col, St, String),Sym},
+ Token = {Item,anno(Line, Col, St, String),Sym},
scan1(Cs, St, Line, incr_column(Col, length(String)), [Token|Toks]).
tok3(Cs, #erl_scan{text = false}=St, Line, no_col=Col, Toks, Item,
_String, Sym, _Length) ->
- scan1(Cs, St, Line, Col, [{Item,Line,Sym}|Toks]);
+ scan1(Cs, St, Line, Col, [{Item,anno(Line),Sym}|Toks]);
tok3(Cs, St, Line, Col, Toks, Item, String, Sym, Length) ->
- Token = {Item,attributes(Line, Col, St, String),Sym},
+ Token = {Item,anno(Line, Col, St, String),Sym},
scan1(Cs, St, Line, incr_column(Col, Length), [Token|Toks]).
scan_error(Error, Line, Col, EndLine, EndCol, Rest) ->
@@ -1208,23 +1081,28 @@ scan_error(Error, Line, Col, EndLine, EndCol, Rest) ->
scan_error(Error, ErrorLoc, EndLoc, Rest) ->
{{error,{ErrorLoc,?MODULE,Error},EndLoc},Rest}.
--compile({inline,[attributes/4]}).
+-compile({inline,[anno/4]}).
-attributes(Line, no_col, #erl_scan{text = false}, _String) ->
- Line;
-attributes(Line, no_col, #erl_scan{text = true}, String) ->
- [{line,Line},{text,String}];
-attributes(Line, Col, #erl_scan{text = false}, _String) ->
- {Line,Col};
-attributes(Line, Col, #erl_scan{text = true}, String) ->
- [{line,Line},{column,Col},{text,String}].
+anno(Line, no_col, #erl_scan{text = false}, _String) ->
+ anno(Line);
+anno(Line, no_col, #erl_scan{text = true}, String) ->
+ Anno = anno(Line),
+ erl_anno:set_text(String, Anno);
+anno(Line, Col, #erl_scan{text = false}, _String) ->
+ anno({Line, Col});
+anno(Line, Col, #erl_scan{text = true}, String) ->
+ Anno = anno({Line, Col}),
+ erl_anno:set_text(String, Anno).
location(Line, no_col) ->
Line;
location(Line, Col) when is_integer(Col) ->
{Line,Col}.
--compile({inline,[incr_column/2,new_column/2]}).
+-compile({inline,[anno/1,incr_column/2,new_column/2]}).
+
+anno(Location) ->
+ erl_anno:new(Location).
incr_column(no_col=Col, _N) ->
Col;
diff --git a/lib/stdlib/src/erl_tar.erl b/lib/stdlib/src/erl_tar.erl
index caa3276d09..a383a0fc67 100644
--- a/lib/stdlib/src/erl_tar.erl
+++ b/lib/stdlib/src/erl_tar.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -300,7 +301,7 @@ format_error(Term) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
add1(TarFile, Bin, NameInArchive, Opts) when is_binary(Bin) ->
- Now = calendar:now_to_local_time(now()),
+ Now = calendar:now_to_local_time(erlang:timestamp()),
Info = #file_info{size = byte_size(Bin),
type = regular,
access = read_write,
diff --git a/lib/stdlib/src/error_logger_file_h.erl b/lib/stdlib/src/error_logger_file_h.erl
index e9364ed787..665685d3ee 100644
--- a/lib/stdlib/src/error_logger_file_h.erl
+++ b/lib/stdlib/src/error_logger_file_h.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -23,24 +24,28 @@
%%%
%%% A handler that can be connected to the error_logger
-%%% event handler.
-%%% Writes all events formatted to file.
-%%% Handles events tagged error, emulator and info.
+%%% event handler. Writes all events formatted to file.
%%%
%%% It can only be started from error_logger:swap_handler({logfile, File})
-%%% or error_logger:logfile(File)
+%%% or error_logger:logfile(File).
%%%
-export([init/1,
handle_event/2, handle_call/2, handle_info/2,
terminate/2, code_change/3]).
+-record(st,
+ {fd,
+ filename,
+ prev_handler,
+ depth=unlimited :: 'unlimited' | non_neg_integer()}).
+
%% This one is used when we takeover from the simple error_logger.
init({File, {error_logger, Buf}}) ->
case init(File, error_logger) of
- {ok, {Fd, File, PrevHandler}} ->
- write_events(Fd, Buf),
- {ok, {Fd, File, PrevHandler}};
+ {ok, State} ->
+ write_events(State, Buf),
+ {ok, State};
Error ->
Error
end;
@@ -52,49 +57,45 @@ init(File, PrevHandler) ->
process_flag(trap_exit, true),
case file:open(File, [write]) of
{ok,Fd} ->
- {ok, {Fd, File, PrevHandler}};
+ Depth = get_depth(),
+ State = #st{fd=Fd,filename=File,prev_handler=PrevHandler,
+ depth=Depth},
+ {ok, State};
Error ->
Error
end.
-
+
+get_depth() ->
+ case application:get_env(kernel, error_logger_format_depth) of
+ {ok, Depth} when is_integer(Depth) ->
+ max(10, Depth);
+ undefined ->
+ unlimited
+ end.
+
handle_event({_Type, GL, _Msg}, State) when node(GL) =/= node() ->
{ok, State};
-handle_event(Event, {Fd, File, PrevHandler}) ->
- write_event(Fd, tag_event(Event)),
- {ok, {Fd, File, PrevHandler}};
-handle_event(_, State) ->
+handle_event(Event, State) ->
+ write_event(State, Event),
{ok, State}.
-handle_info({'EXIT', Fd, _Reason}, {Fd, _File, PrevHandler}) ->
+handle_info({'EXIT', Fd, _Reason}, #st{fd=Fd,prev_handler=PrevHandler}) ->
case PrevHandler of
[] ->
remove_handler;
_ ->
{swap_handler, install_prev, [], PrevHandler, go_back}
end;
-handle_info({emulator, GL, Chars}, {Fd, File, PrevHandler})
- when node(GL) == node() ->
- write_event(Fd, tag_event({emulator, GL, Chars})),
- {ok, {Fd, File, PrevHandler}};
-handle_info({emulator, noproc, Chars}, {Fd, File, PrevHandler}) ->
- write_event(Fd, tag_event({emulator, noproc, Chars})),
- {ok, {Fd, File, PrevHandler}};
handle_info(_, State) ->
{ok, State}.
-handle_call(filename, {Fd, File, Prev}) ->
- {ok, File, {Fd, File, Prev}};
+handle_call(filename, #st{filename=File}=State) ->
+ {ok, File, State};
handle_call(_Query, State) ->
{ok, {error, bad_query}, State}.
-terminate(_Reason, State) ->
- case State of
- {Fd, _File, _Prev} ->
- ok = file:close(Fd);
- _ ->
- ok
- end,
- [].
+terminate(_Reason, #st{fd=Fd}) ->
+ file:close(Fd).
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
@@ -103,69 +104,73 @@ code_change(_OldVsn, State, _Extra) ->
%%% Misc. functions.
%%% ------------------------------------------------------
-tag_event(Event) ->
- {erlang:universaltime(), Event}.
+write_events(State, [Ev|Es]) ->
+ %% Write the events in reversed order.
+ write_events(State, Es),
+ write_event(State, Ev);
+write_events(_State, []) ->
+ ok.
-write_events(Fd, Events) -> write_events1(Fd, lists:reverse(Events)).
+write_event(#st{fd=Fd}=State, Event) ->
+ case parse_event(Event) of
+ ignore ->
+ ok;
+ {Head,Pid,FormatList} ->
+ Time = maybe_utc(erlang:universaltime()),
+ Header = write_time(Time, Head),
+ Body = format_body(State, FormatList),
+ AtNode = if
+ node(Pid) =/= node() ->
+ ["** at node ",atom_to_list(node(Pid))," **\n"];
+ true ->
+ []
+ end,
+ io:put_chars(Fd, [Header,Body,AtNode])
+ end.
-write_events1(Fd, [Event|Es]) ->
- write_event(Fd, Event),
- write_events1(Fd, Es);
-write_events1(_, []) ->
- ok.
+format_body(State, [{Format,Args}|T]) ->
+ S = try format(State, Format, Args) of
+ S0 ->
+ S0
+ catch
+ _:_ ->
+ format(State, "ERROR: ~p - ~p\n", [Format,Args])
+ end,
+ [S|format_body(State, T)];
+format_body(_State, []) ->
+ [].
-write_event(Fd, {Time, {error, _GL, {Pid, Format, Args}}}) ->
- T = write_time(maybe_utc(Time)),
- case catch io_lib:format(add_node(Format,Pid), Args) of
- S when is_list(S) ->
- io:format(Fd, T ++ S, []);
- _ ->
- F = add_node("ERROR: ~p - ~p~n", Pid),
- io:format(Fd, T ++ F, [Format,Args])
- end;
-write_event(Fd, {Time, {emulator, _GL, Chars}}) ->
- T = write_time(maybe_utc(Time)),
- case catch io_lib:format(Chars, []) of
- S when is_list(S) ->
- io:format(Fd, T ++ S, []);
- _ ->
- io:format(Fd, T ++ "ERROR: ~p ~n", [Chars])
- end;
-write_event(Fd, {Time, {info, _GL, {Pid, Info, _}}}) ->
- T = write_time(maybe_utc(Time)),
- io:format(Fd, T ++ add_node("~p~n",Pid),[Info]);
-write_event(Fd, {Time, {error_report, _GL, {Pid, std_error, Rep}}}) ->
- T = write_time(maybe_utc(Time)),
- S = format_report(Rep),
- io:format(Fd, T ++ S ++ add_node("", Pid), []);
-write_event(Fd, {Time, {info_report, _GL, {Pid, std_info, Rep}}}) ->
- T = write_time(maybe_utc(Time), "INFO REPORT"),
- S = format_report(Rep),
- io:format(Fd, T ++ S ++ add_node("", Pid), []);
-write_event(Fd, {Time, {info_msg, _GL, {Pid, Format, Args}}}) ->
- T = write_time(maybe_utc(Time), "INFO REPORT"),
- case catch io_lib:format(add_node(Format,Pid), Args) of
- S when is_list(S) ->
- io:format(Fd, T ++ S, []);
- _ ->
- F = add_node("ERROR: ~p - ~p~n", Pid),
- io:format(Fd, T ++ F, [Format,Args])
- end;
-write_event(Fd, {Time, {warning_report, _GL, {Pid, std_warning, Rep}}}) ->
- T = write_time(maybe_utc(Time), "WARNING REPORT"),
- S = format_report(Rep),
- io:format(Fd, T ++ S ++ add_node("", Pid), []);
-write_event(Fd, {Time, {warning_msg, _GL, {Pid, Format, Args}}}) ->
- T = write_time(maybe_utc(Time), "WARNING REPORT"),
- case catch io_lib:format(add_node(Format,Pid), Args) of
- S when is_list(S) ->
- io:format(Fd, T ++ S, []);
- _ ->
- F = add_node("ERROR: ~p - ~p~n", Pid),
- io:format(Fd, T ++ F, [Format,Args])
- end;
-write_event(_, _) ->
- ok.
+format(#st{depth=unlimited}, Format, Args) ->
+ io_lib:format(Format, Args);
+format(#st{depth=Depth}, Format0, Args) ->
+ Format1 = io_lib:scan_format(Format0, Args),
+ Format = limit_format(Format1, Depth),
+ io_lib:build_text(Format).
+
+limit_format([#{control_char:=C0}=M0|T], Depth) when C0 =:= $p;
+ C0 =:= $w ->
+ C = C0 - ($a - $A), %To uppercase.
+ #{args:=Args} = M0,
+ M = M0#{control_char:=C,args:=Args++[Depth]},
+ [M|limit_format(T, Depth)];
+limit_format([H|T], Depth) ->
+ [H|limit_format(T, Depth)];
+limit_format([], _) ->
+ [].
+
+parse_event({error, _GL, {Pid, Format, Args}}) ->
+ {"ERROR REPORT",Pid,[{Format,Args}]};
+parse_event({info_msg, _GL, {Pid, Format, Args}}) ->
+ {"INFO REPORT",Pid,[{Format, Args}]};
+parse_event({warning_msg, _GL, {Pid, Format, Args}}) ->
+ {"WARNING REPORT",Pid,[{Format,Args}]};
+parse_event({error_report, _GL, {Pid, std_error, Args}}) ->
+ {"ERROR REPORT",Pid,format_term(Args)};
+parse_event({info_report, _GL, {Pid, std_info, Args}}) ->
+ {"INFO REPORT",Pid,format_term(Args)};
+parse_event({warning_report, _GL, {Pid, std_warning, Args}}) ->
+ {"WARNING REPORT",Pid,format_term(Args)};
+parse_event(_) -> ignore.
maybe_utc(Time) ->
UTC = case application:get_env(sasl, utc_log) of
@@ -182,30 +187,27 @@ maybe_utc(Time) ->
maybe_utc(Time, true) -> {utc, Time};
maybe_utc(Time, _) -> {local, calendar:universal_time_to_local_time(Time)}.
-format_report(Rep) when is_list(Rep) ->
- case string_p(Rep) of
+format_term(Term) when is_list(Term) ->
+ case string_p(Term) of
true ->
- io_lib:format("~s~n",[Rep]);
- _ ->
- format_rep(Rep)
+ [{"~s\n",[Term]}];
+ false ->
+ format_term_list(Term)
end;
-format_report(Rep) ->
- io_lib:format("~p~n",[Rep]).
+format_term(Term) ->
+ [{"~p\n",[Term]}].
-format_rep([{Tag,Data}|Rep]) ->
- io_lib:format(" ~p: ~p~n",[Tag,Data]) ++ format_rep(Rep);
-format_rep([Other|Rep]) ->
- io_lib:format(" ~p~n",[Other]) ++ format_rep(Rep);
-format_rep(_) ->
+format_term_list([{Tag,Data}|T]) ->
+ [{" ~p: ~p\n",[Tag,Data]}|format_term_list(T)];
+format_term_list([Data|T]) ->
+ [{" ~p\n",[Data]}|format_term_list(T)];
+format_term_list([]) ->
+ [];
+format_term_list(_) ->
+ %% Continue to allow non-proper lists for now.
+ %% FIXME: Remove this clause in OTP 19.
[].
-add_node(X, Pid) when is_atom(X) ->
- add_node(atom_to_list(X), Pid);
-add_node(X, Pid) when node(Pid) =/= node() ->
- lists:concat([X,"** at node ",node(Pid)," **~n"]);
-add_node(X, _) ->
- X.
-
string_p([]) ->
false;
string_p(Term) ->
@@ -221,15 +223,10 @@ string_p1([$\b|T]) -> string_p1(T);
string_p1([$\f|T]) -> string_p1(T);
string_p1([$\e|T]) -> string_p1(T);
string_p1([H|T]) when is_list(H) ->
- case string_p1(H) of
- true -> string_p1(T);
- _ -> false
- end;
+ string_p1(H) andalso string_p1(T);
string_p1([]) -> true;
string_p1(_) -> false.
-write_time(Time) -> write_time(Time, "ERROR REPORT").
-
write_time({utc,{{Y,Mo,D},{H,Mi,S}}}, Type) ->
io_lib:format("~n=~s==== ~p-~s-~p::~s:~s:~s UTC ===~n",
[Type,D,month(Mo),Y,t(H),t(Mi),t(S)]);
diff --git a/lib/stdlib/src/error_logger_tty_h.erl b/lib/stdlib/src/error_logger_tty_h.erl
index e92142d154..cb22a8c0b6 100644
--- a/lib/stdlib/src/error_logger_tty_h.erl
+++ b/lib/stdlib/src/error_logger_tty_h.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -22,145 +23,180 @@
%%%
%%% A handler that can be connected to the error_logger
-%%% event handler.
-%%% Writes all events formatted to stdout.
-%%% Handles events tagged error, emulator and info.
+%%% event handler. Writes all events formatted to stdout.
%%%
%%% It can only be started from error_logger:swap_handler(tty)
-%%% or error_logger:tty(true)
+%%% or error_logger:tty(true).
%%%
-export([init/1,
handle_event/2, handle_call/2, handle_info/2,
terminate/2, code_change/3]).
--export([write_event/2]).
+-export([write_event/2,write_event/3]).
+
+-record(st,
+ {user,
+ prev_handler,
+ io_mod=io,
+ depth=unlimited}).
%% This one is used when we takeover from the simple error_logger.
init({[], {error_logger, Buf}}) ->
User = set_group_leader(),
- write_events(Buf,io),
- {ok, {User, error_logger}};
+ Depth = get_depth(),
+ State = #st{user=User,prev_handler=error_logger,depth=Depth},
+ write_events(State, Buf),
+ {ok, State};
%% This one is used if someone took over from us, and now wants to
%% go back.
init({[], {error_logger_tty_h, PrevHandler}}) ->
User = set_group_leader(),
- {ok, {User, PrevHandler}};
+ {ok, #st{user=User,prev_handler=PrevHandler}};
%% This one is used when we are started directly.
init([]) ->
User = set_group_leader(),
- {ok, {User, []}}.
+ Depth = get_depth(),
+ {ok, #st{user=User,prev_handler=[],depth=Depth}}.
+
+get_depth() ->
+ case application:get_env(kernel, error_logger_format_depth) of
+ {ok, Depth} when is_integer(Depth) ->
+ max(10, Depth);
+ undefined ->
+ unlimited
+ end.
handle_event({_Type, GL, _Msg}, State) when node(GL) =/= node() ->
{ok, State};
handle_event(Event, State) ->
- ok = write_event(tag_event(Event),io),
+ ok = do_write_event(State, tag_event(Event)),
{ok, State}.
-handle_info({'EXIT', User, _Reason}, {User, PrevHandler}) ->
+handle_info({'EXIT', User, _Reason},
+ #st{user=User,prev_handler=PrevHandler}=State) ->
case PrevHandler of
[] ->
remove_handler;
_ ->
- {swap_handler, install_prev, {User, PrevHandler},
+ {swap_handler, install_prev, State,
PrevHandler, go_back}
end;
-handle_info({emulator, GL, Chars}, State) when node(GL) == node() ->
- ok = write_event(tag_event({emulator, GL, Chars}),io),
- {ok, State};
-handle_info({emulator, noproc, Chars}, State) ->
- ok = write_event(tag_event({emulator, noproc, Chars}),io),
- {ok, State};
handle_info(_, State) ->
{ok, State}.
handle_call(_Query, State) -> {ok, {error, bad_query}, State}.
-% unfortunately, we can't unlink from User - links are not counted!
-% if pid(User) -> unlink(User); true -> ok end,
terminate(install_prev, _State) ->
[];
-terminate(_Reason, {_User, PrevHandler}) ->
+terminate(_Reason, #st{prev_handler=PrevHandler}) ->
{error_logger_tty_h, PrevHandler}.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
+%% Exported (but unoffical) API.
+write_event(Event, IoMod) ->
+ do_write_event(#st{io_mod=IoMod}, Event).
+
+write_event(Event, IoMod, Depth) ->
+ do_write_event(#st{io_mod=IoMod,depth=Depth}, Event).
+
+
%%% ------------------------------------------------------
%%% Misc. functions.
%%% ------------------------------------------------------
set_group_leader() ->
case whereis(user) of
- User when is_pid(User) -> link(User), group_leader(User,self()), User;
- _ -> false
+ User when is_pid(User) ->
+ link(User),
+ group_leader(User,self()),
+ User;
+ _ ->
+ false
end.
tag_event(Event) ->
{erlang:universaltime(), Event}.
-%% IOMOd is always 'io'
-write_events(Events,IOMod) -> write_events1(lists:reverse(Events),IOMod).
-
-write_events1([Event|Es],IOMod) ->
- ok = write_event(Event,IOMod),
- write_events1(Es,IOMod);
-write_events1([],_IOMod) ->
+write_events(State, [Ev|Es]) ->
+ %% Write the events in reverse order.
+ _ = write_events(State, Es),
+ _ = do_write_event(State, Ev),
+ ok;
+write_events(_State, []) ->
ok.
-write_event({Time, {error, _GL, {Pid, Format, Args}}},IOMod) ->
- T = write_time(maybe_utc(Time)),
- case catch io_lib:format(add_node(Format,Pid), Args) of
- S when is_list(S) ->
- format(IOMod, T ++ S);
- _ ->
- F = add_node("ERROR: ~p - ~p~n", Pid),
- format(IOMod, T ++ F, [Format,Args])
- end;
-write_event({Time, {emulator, _GL, Chars}},IOMod) ->
- T = write_time(maybe_utc(Time)),
- case catch io_lib:format(Chars, []) of
- S when is_list(S) ->
- format(IOMod, T ++ S);
- _ ->
- format(IOMod, T ++ "ERROR: ~p ~n", [Chars])
- end;
-write_event({Time, {info, _GL, {Pid, Info, _}}},IOMod) ->
- T = write_time(maybe_utc(Time)),
- format(IOMod, T ++ add_node("~p~n",Pid),[Info]);
-write_event({Time, {error_report, _GL, {Pid, std_error, Rep}}},IOMod) ->
- T = write_time(maybe_utc(Time)),
- S = format_report(Rep),
- format(IOMod, T ++ S ++ add_node("", Pid));
-write_event({Time, {info_report, _GL, {Pid, std_info, Rep}}},IOMod) ->
- T = write_time(maybe_utc(Time), "INFO REPORT"),
- S = format_report(Rep),
- format(IOMod, T ++ S ++ add_node("", Pid));
-write_event({Time, {info_msg, _GL, {Pid, Format, Args}}},IOMod) ->
- T = write_time(maybe_utc(Time), "INFO REPORT"),
- case catch io_lib:format(add_node(Format,Pid), Args) of
- S when is_list(S) ->
- format(IOMod, T ++ S);
- _ ->
- F = add_node("ERROR: ~p - ~p~n", Pid),
- format(IOMod, T ++ F, [Format,Args])
- end;
-write_event({Time, {warning_report, _GL, {Pid, std_warning, Rep}}},IOMod) ->
- T = write_time(maybe_utc(Time), "WARNING REPORT"),
- S = format_report(Rep),
- format(IOMod, T ++ S ++ add_node("", Pid));
-write_event({Time, {warning_msg, _GL, {Pid, Format, Args}}},IOMod) ->
- T = write_time(maybe_utc(Time), "WARNING REPORT"),
- case catch io_lib:format(add_node(Format,Pid), Args) of
- S when is_list(S) ->
- format(IOMod, T ++ S);
- _ ->
- F = add_node("ERROR: ~p - ~p~n", Pid),
- format(IOMod, T ++ F, [Format,Args])
+do_write_event(State, {Time0, Event}) ->
+ case parse_event(Event) of
+ ignore ->
+ ok;
+ {Head,Pid,FormatList} ->
+ Time = maybe_utc(Time0),
+ Header = write_time(Time, Head),
+ Body = format_body(State, FormatList),
+ AtNode = if
+ node(Pid) =/= node() ->
+ ["** at node ",atom_to_list(node(Pid))," **\n"];
+ true ->
+ []
+ end,
+ Str = [Header,Body,AtNode],
+ case State#st.io_mod of
+ io_lib ->
+ Str;
+ io ->
+ io:put_chars(user, Str)
+ end
end;
-write_event({_Time, _Error},_IOMod) ->
+do_write_event(_, _) ->
ok.
+format_body(State, [{Format,Args}|T]) ->
+ S = try format(State, Format, Args) of
+ S0 ->
+ S0
+ catch
+ _:_ ->
+ format(State, "ERROR: ~p - ~p\n", [Format,Args])
+ end,
+ [S|format_body(State, T)];
+format_body(_State, []) ->
+ [].
+
+format(#st{depth=unlimited}, Format, Args) ->
+ io_lib:format(Format, Args);
+format(#st{depth=Depth}, Format0, Args) ->
+ Format1 = io_lib:scan_format(Format0, Args),
+ Format = limit_format(Format1, Depth),
+ io_lib:build_text(Format).
+
+limit_format([#{control_char:=C0}=M0|T], Depth) when C0 =:= $p;
+ C0 =:= $w ->
+ C = C0 - ($a - $A), %To uppercase.
+ #{args:=Args} = M0,
+ M = M0#{control_char:=C,args:=Args++[Depth]},
+ [M|limit_format(T, Depth)];
+limit_format([H|T], Depth) ->
+ [H|limit_format(T, Depth)];
+limit_format([], _) ->
+ [].
+
+parse_event({error, _GL, {Pid, Format, Args}}) ->
+ {"ERROR REPORT",Pid,[{Format,Args}]};
+parse_event({info_msg, _GL, {Pid, Format, Args}}) ->
+ {"INFO REPORT",Pid,[{Format, Args}]};
+parse_event({warning_msg, _GL, {Pid, Format, Args}}) ->
+ {"WARNING REPORT",Pid,[{Format,Args}]};
+parse_event({error_report, _GL, {Pid, std_error, Args}}) ->
+ {"ERROR REPORT",Pid,format_term(Args)};
+parse_event({info_report, _GL, {Pid, std_info, Args}}) ->
+ {"INFO REPORT",Pid,format_term(Args)};
+parse_event({warning_report, _GL, {Pid, std_warning, Args}}) ->
+ {"WARNING REPORT",Pid,format_term(Args)};
+parse_event(_) -> ignore.
+
maybe_utc(Time) ->
UTC = case application:get_env(sasl, utc_log) of
{ok, Val} -> Val;
@@ -176,33 +212,26 @@ maybe_utc(Time) ->
maybe_utc(Time, true) -> {utc, Time};
maybe_utc(Time, _) -> {local, calendar:universal_time_to_local_time(Time)}.
-format(IOMod, String) -> format(IOMod, String, []).
-format(io_lib, String, Args) -> io_lib:format(String, Args);
-format(io, String, Args) -> io:format(user, String, Args).
-
-format_report(Rep) when is_list(Rep) ->
- case string_p(Rep) of
+format_term(Term) when is_list(Term) ->
+ case string_p(Term) of
true ->
- io_lib:format("~s~n",[Rep]);
- _ ->
- format_rep(Rep)
+ [{"~s\n",[Term]}];
+ false ->
+ format_term_list(Term)
end;
-format_report(Rep) ->
- io_lib:format("~p~n",[Rep]).
-
-format_rep([{Tag,Data}|Rep]) ->
- io_lib:format(" ~p: ~p~n",[Tag,Data]) ++ format_rep(Rep);
-format_rep([Other|Rep]) ->
- io_lib:format(" ~p~n",[Other]) ++ format_rep(Rep);
-format_rep(_) ->
- [].
+format_term(Term) ->
+ [{"~p\n",[Term]}].
-add_node(X, Pid) when is_atom(X) ->
- add_node(atom_to_list(X), Pid);
-add_node(X, Pid) when node(Pid) =/= node() ->
- lists:concat([X,"** at node ",node(Pid)," **~n"]);
-add_node(X, _) ->
- X.
+format_term_list([{Tag,Data}|T]) ->
+ [{" ~p: ~p\n",[Tag,Data]}|format_term_list(T)];
+format_term_list([Data|T]) ->
+ [{" ~p\n",[Data]}|format_term_list(T)];
+format_term_list([]) ->
+ [];
+format_term_list(_) ->
+ %% Continue to allow non-proper lists for now.
+ %% FIXME: Remove this clause in OTP 19.
+ [].
string_p([]) ->
false;
@@ -226,7 +255,6 @@ string_p1([H|T]) when is_list(H) ->
string_p1([]) -> true;
string_p1(_) -> false.
-write_time(Time) -> write_time(Time, "ERROR REPORT").
write_time({utc,{{Y,Mo,D},{H,Mi,S}}},Type) ->
io_lib:format("~n=~s==== ~p-~s-~p::~s:~s:~s UTC ===~n",
[Type,D,month(Mo),Y,t(H),t(Mi),t(S)]);
diff --git a/lib/stdlib/src/escript.erl b/lib/stdlib/src/escript.erl
index 6bd0eb8a22..b8ce311c35 100644
--- a/lib/stdlib/src/escript.erl
+++ b/lib/stdlib/src/escript.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2014. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2015. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
@@ -37,7 +38,7 @@
-record(state, {file :: file:filename(),
module :: module(),
forms_or_bin,
- source :: source(),
+ source :: source() | 'undefined',
n_errors :: non_neg_integer(),
mode :: mode(),
exports_main :: boolean(),
@@ -48,9 +49,9 @@
-type emu_args() :: string().
-record(sections, {type,
- shebang :: shebang(),
- comment :: comment(),
- emu_args :: emu_args(),
+ shebang :: shebang() | 'undefined',
+ comment :: comment() | 'undefined',
+ emu_args :: emu_args() | 'undefined',
body}).
-record(extract_options, {compile_source}).
@@ -620,12 +621,13 @@ parse_source(S, File, Fd, StartLine, HeaderSz, CheckOnly) ->
{ok, {attribute,_, module, M} = Form} ->
epp_parse_file(Epp, S2#state{module = M}, [Form, FileForm]);
{ok, _} ->
- ModForm = {attribute,1,module, Module},
+ ModForm = {attribute,a1(),module, Module},
epp_parse_file2(Epp, S2, [ModForm, FileForm], OptModRes);
{error, _} ->
epp_parse_file2(Epp, S2, [FileForm], OptModRes);
- {eof, _LastLine} = Eof ->
- S#state{forms_or_bin = [FileForm, Eof]}
+ {eof, LastLine} ->
+ Anno = anno(LastLine),
+ S#state{forms_or_bin = [FileForm, {eof, Anno}]}
end,
ok = epp:close(Epp),
ok = file:close(Fd),
@@ -644,7 +646,7 @@ check_source(S, CheckOnly) ->
%% Optionally add export of main/1
Forms2 =
case ExpMain of
- false -> [{attribute,0,export, [{main,1}]} | Forms];
+ false -> [{attribute, a0(), export, [{main,1}]} | Forms];
true -> Forms
end,
Forms3 = [FileForm2, ModForm2 | Forms2],
@@ -663,7 +665,8 @@ check_source(S, CheckOnly) ->
end.
pre_def_macros(File) ->
- {MegaSecs, Secs, MicroSecs} = erlang:now(),
+ {MegaSecs, Secs, MicroSecs} = erlang:timestamp(),
+ Unique = erlang:unique_integer([positive]),
Replace = fun(Char) ->
case Char of
$\. -> $\_;
@@ -675,8 +678,9 @@ pre_def_macros(File) ->
CleanBase ++ "__" ++
"escript__" ++
integer_to_list(MegaSecs) ++ "__" ++
- integer_to_list(Secs) ++ "__" ++
- integer_to_list(MicroSecs),
+ integer_to_list(Secs) ++ "__" ++
+ integer_to_list(MicroSecs) ++ "__" ++
+ integer_to_list(Unique),
Module = list_to_atom(ModuleStr),
PreDefMacros = [{'MODULE', Module, redefine},
{'MODULE_STRING', ModuleStr, redefine}],
@@ -720,8 +724,9 @@ epp_parse_file2(Epp, S, Forms, Parsed) ->
io:format("~ts:~w: ~ts\n",
[S#state.file,Ln,Mod:format_error(Args)]),
epp_parse_file(Epp, S#state{n_errors = S#state.n_errors + 1}, [Form | Forms]);
- {eof, _LastLine} = Eof ->
- S#state{forms_or_bin = lists:reverse([Eof | Forms])}
+ {eof, LastLine} ->
+ Anno = anno(LastLine),
+ S#state{forms_or_bin = lists:reverse([{eof, Anno} | Forms])}
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -776,7 +781,8 @@ interpret(Forms, HasRecs, File, Args) ->
end,
Dict = parse_to_dict(Forms2),
ArgsA = erl_parse:abstract(Args, 0),
- Call = {call,0,{atom,0,main},[ArgsA]},
+ Anno = a0(),
+ Call = {call,Anno,{atom,Anno,main},[ArgsA]},
try
_ = erl_eval:expr(Call,
erl_eval:new_bindings(),
@@ -888,6 +894,15 @@ enc() ->
Enc -> [Enc]
end.
+a0() ->
+ anno(0).
+
+a1() ->
+ anno(1).
+
+anno(L) ->
+ erl_anno:new(L).
+
fatal(Str) ->
throw(Str).
diff --git a/lib/stdlib/src/ets.erl b/lib/stdlib/src/ets.erl
index 93c4f59896..3f74e01692 100644
--- a/lib/stdlib/src/ets.erl
+++ b/lib/stdlib/src/ets.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -71,7 +72,8 @@
rename/2, safe_fixtable/2, select/1, select/2, select/3,
select_count/2, select_delete/2, select_reverse/1,
select_reverse/2, select_reverse/3, setopts/2, slot/2,
- update_counter/3, update_element/3]).
+ take/2,
+ update_counter/3, update_counter/4, update_element/3]).
-spec all() -> [Tab] when
Tab :: tab().
@@ -133,7 +135,9 @@ give_away(_, _, _) ->
| {owner, pid()}
| {protection, access()}
| {size, non_neg_integer()}
- | {type, type()}.
+ | {type, type()}
+ | {write_concurrency, boolean()}
+ | {read_concurrency, boolean()}.
info(_) ->
erlang:nif_error(undef).
@@ -142,7 +146,8 @@ info(_) ->
Tab :: tab(),
Item :: compressed | fixed | heir | keypos | memory
| name | named_table | node | owner | protection
- | safe_fixed | size | stats | type,
+ | safe_fixed | safe_fixed_monotonic_time | size | stats | type
+ | write_concurrency | read_concurrency,
Value :: term().
info(_, _) ->
@@ -400,6 +405,14 @@ setopts(_, _) ->
slot(_, _) ->
erlang:nif_error(undef).
+-spec take(Tab, Key) -> [Object] when
+ Tab :: tab(),
+ Key :: term(),
+ Object :: tuple().
+
+take(_, _) ->
+ erlang:nif_error(undef).
+
-spec update_counter(Tab, Key, UpdateOp) -> Result when
Tab :: tab(),
Key :: term(),
@@ -427,6 +440,38 @@ slot(_, _) ->
update_counter(_, _, _) ->
erlang:nif_error(undef).
+-spec update_counter(Tab, Key, UpdateOp, Default) -> Result when
+ Tab :: tab(),
+ Key :: term(),
+ UpdateOp :: {Pos, Incr}
+ | {Pos, Incr, Threshold, SetValue},
+ Pos :: integer(),
+ Incr :: integer(),
+ Threshold :: integer(),
+ SetValue :: integer(),
+ Result :: integer(),
+ Default :: tuple();
+ (Tab, Key, [UpdateOp], Default) -> [Result] when
+ Tab :: tab(),
+ Key :: term(),
+ UpdateOp :: {Pos, Incr}
+ | {Pos, Incr, Threshold, SetValue},
+ Pos :: integer(),
+ Incr :: integer(),
+ Threshold :: integer(),
+ SetValue :: integer(),
+ Result :: integer(),
+ Default :: tuple();
+ (Tab, Key, Incr, Default) -> Result when
+ Tab :: tab(),
+ Key :: term(),
+ Incr :: integer(),
+ Result :: integer(),
+ Default :: tuple().
+
+update_counter(_, _, _, _) ->
+ erlang:nif_error(undef).
+
-spec update_element(Tab, Key, ElementSpec :: {Pos, Value}) -> boolean() when
Tab :: tab(),
Key :: term(),
@@ -695,7 +740,8 @@ do_filter(Tab, Key, F, A, Ack) ->
-record(filetab_options,
{
object_count = false :: boolean(),
- md5sum = false :: boolean()
+ md5sum = false :: boolean(),
+ sync = false :: boolean()
}).
-spec tab2file(Tab, Filename) -> 'ok' | {'error', Reason} when
@@ -710,7 +756,7 @@ tab2file(Tab, File) ->
Tab :: tab(),
Filename :: file:name(),
Options :: [Option],
- Option :: {'extended_info', [ExtInfo]},
+ Option :: {'extended_info', [ExtInfo]} | {'sync', boolean()},
ExtInfo :: 'md5sum' | 'object_count',
Reason :: term().
@@ -791,6 +837,15 @@ tab2file(Tab, File, Options) ->
List ->
LogFun(NewState1,[['$end_of_table',List]])
end,
+ case FtOptions#filetab_options.sync of
+ true ->
+ case disk_log:sync(Name) of
+ ok -> ok;
+ {error, Reason2} -> throw(Reason2)
+ end;
+ false ->
+ ok
+ end,
disk_log:close(Name)
catch
throw:TReason ->
@@ -843,23 +898,24 @@ md5terms(State, [H|T]) ->
{FinState, [B|TL]}.
parse_ft_options(Options) when is_list(Options) ->
- {Opt,Rest} = case (catch lists:keytake(extended_info,1,Options)) of
- false ->
- {[],Options};
- {value,{extended_info,L},R} when is_list(L) ->
- {L,R}
- end,
- case Rest of
- [] ->
- parse_ft_info_options(#filetab_options{}, Opt);
- Other ->
- throw({unknown_option, Other})
- end;
-parse_ft_options(Malformed) ->
+ {ok, parse_ft_options(Options, #filetab_options{}, false)}.
+
+parse_ft_options([], FtOpt, _) ->
+ FtOpt;
+parse_ft_options([{sync,true} | Rest], FtOpt, EI) ->
+ parse_ft_options(Rest, FtOpt#filetab_options{sync = true}, EI);
+parse_ft_options([{sync,false} | Rest], FtOpt, EI) ->
+ parse_ft_options(Rest, FtOpt, EI);
+parse_ft_options([{extended_info,L} | Rest], FtOpt0, false) ->
+ FtOpt1 = parse_ft_info_options(FtOpt0, L),
+ parse_ft_options(Rest, FtOpt1, true);
+parse_ft_options([Other | _], _, _) ->
+ throw({unknown_option, Other});
+parse_ft_options(Malformed, _, _) ->
throw({malformed_option, Malformed}).
parse_ft_info_options(FtOpt,[]) ->
- {ok,FtOpt};
+ FtOpt;
parse_ft_info_options(FtOpt,[object_count | T]) ->
parse_ft_info_options(FtOpt#filetab_options{object_count = true}, T);
parse_ft_info_options(FtOpt,[md5sum | T]) ->
@@ -1253,18 +1309,30 @@ create_tab(I, TabArg) ->
{name, Name} = lists:keyfind(name, 1, I),
{type, Type} = lists:keyfind(type, 1, I),
{protection, P} = lists:keyfind(protection, 1, I),
- {named_table, Val} = lists:keyfind(named_table, 1, I),
{keypos, _Kp} = Keypos = lists:keyfind(keypos, 1, I),
{size, Sz} = lists:keyfind(size, 1, I),
- Comp = case lists:keyfind(compressed, 1, I) of
- {compressed, true} -> [compressed];
- {compressed, false} -> [];
- false -> []
- end,
+ L1 = [Type, P, Keypos],
+ L2 = case lists:keyfind(named_table, 1, I) of
+ {named_table, true} -> [named_table | L1];
+ {named_table, false} -> L1
+ end,
+ L3 = case lists:keyfind(compressed, 1, I) of
+ {compressed, true} -> [compressed | L2];
+ {compressed, false} -> L2;
+ false -> L2
+ end,
+ L4 = case lists:keyfind(write_concurrency, 1, I) of
+ {write_concurrency, _}=Wcc -> [Wcc | L3];
+ _ -> L3
+ end,
+ L5 = case lists:keyfind(read_concurrency, 1, I) of
+ {read_concurrency, _}=Rcc -> [Rcc | L4];
+ false -> L4
+ end,
case TabArg of
[] ->
try
- Tab = ets:new(Name, [Type, P, Keypos] ++ named_table(Val) ++ Comp),
+ Tab = ets:new(Name, L5),
{ok, Tab, Sz}
catch _:_ ->
throw(cannot_create_table)
@@ -1273,8 +1341,6 @@ create_tab(I, TabArg) ->
{ok, TabArg, Sz}
end.
-named_table(true) -> [named_table];
-named_table(false) -> [].
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% tabfile_info/1 reads the head information in an ets table dumped to
diff --git a/lib/stdlib/src/eval_bits.erl b/lib/stdlib/src/eval_bits.erl
index 75fe2c00c7..80667023fb 100644
--- a/lib/stdlib/src/eval_bits.erl
+++ b/lib/stdlib/src/eval_bits.erl
@@ -2,18 +2,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
diff --git a/lib/stdlib/src/file_sorter.erl b/lib/stdlib/src/file_sorter.erl
index 687d72b4bd..3aeaff8dc4 100644
--- a/lib/stdlib/src/file_sorter.erl
+++ b/lib/stdlib/src/file_sorter.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -27,6 +28,8 @@
check/1, check/2,
keycheck/2, keycheck/3]).
+-dialyzer(no_improper_lists).
+
-include_lib("kernel/include/file.hrl").
-define(CHUNKSIZE, 16384).
@@ -304,7 +307,6 @@ options(Option) ->
options([{format, Format} | L], Opts) when Format =:= binary;
Format =:= term;
- is_function(Format),
is_function(Format, 1) ->
options(L, Opts#opts{format = Format});
options([{format, binary_term} | L], Opts) ->
@@ -323,7 +325,7 @@ options([{tmpdir, Dir} | L], Opts) ->
FileName ->
options(L, Opts#opts{tmpdir = {dir, FileName}})
end;
-options([{order, Fun} | L], Opts) when is_function(Fun), is_function(Fun, 2) ->
+options([{order, Fun} | L], Opts) when is_function(Fun, 2) ->
options(L, Opts#opts{order = Fun});
options([{order, Order} | L], Opts) when Order =:= ascending;
Order =:= descending ->
@@ -408,7 +410,7 @@ merge_terms_fun(RFun) ->
case RFun(read) of
end_of_input ->
eof;
- {Objs, NRFun} when is_function(NRFun), is_function(NRFun, 1) ->
+ {Objs, NRFun} when is_function(NRFun, 1) ->
{_, [], Ts, _} = fun_objs(Objs, [], 0, ?MAXSIZE, I, W),
{{I, Ts, ?CHUNKSIZE}, merge_terms_fun(NRFun)};
Error ->
@@ -424,13 +426,12 @@ merge_bins_fun(FileName) ->
Fun(A)
end.
-wrap_output_terms(term, OutFun, _Z) when is_function(OutFun),
- is_function(OutFun, 1) ->
+wrap_output_terms(term, OutFun, _Z) when is_function(OutFun, 1) ->
{fun_wterms(OutFun), true};
wrap_output_terms(term, File, Z) when File =/= undefined ->
{file_wterms(name, File, Z++[write]), false};
wrap_output_terms(_Format, Output, _Z) ->
- {Output, is_function(Output) and is_function(Output, 1)}.
+ {Output, is_function(Output, 1)}.
binary_term_fun() ->
fun binary_to_term/1.
@@ -1308,8 +1309,7 @@ infun(W) ->
{end_of_input, W1};
{end_of_input, Value} ->
{end_of_input, W1#w{inout_value = {value, Value}}};
- {Objs, NFun} when is_function(NFun),
- is_function(NFun, 1),
+ {Objs, NFun} when is_function(NFun, 1),
is_list(Objs) ->
{cont, W#w{in = NFun}, Objs};
Error ->
@@ -1332,7 +1332,7 @@ outfun(A, W) ->
try (W#w.out)(A) of
Reply when A =:= close ->
Reply;
- NF when is_function(NF), is_function(NF, 1) ->
+ NF when is_function(NF, 1) ->
W#w{out = NF};
Error ->
error(Error, W1)
@@ -1357,7 +1357,7 @@ is_keyposs([Bad | _]) ->
is_keyposs(Bad) ->
{badarg, Bad}.
-is_input(Fun) when is_function(Fun), is_function(Fun, 1) ->
+is_input(Fun) when is_function(Fun, 1) ->
{true, Fun};
is_input(Files) ->
is_files(Files).
@@ -1377,7 +1377,7 @@ is_files([], L) ->
is_files(Bad, _L) ->
{badarg, Bad}.
-maybe_output(Fun) when is_function(Fun), is_function(Fun, 1) ->
+maybe_output(Fun) when is_function(Fun, 1) ->
{true, Fun};
maybe_output(File) ->
case read_file_info(File) of
@@ -1425,8 +1425,8 @@ tmp_prefix1(Dir, TmpDirOpt) ->
U = "_",
Node = node(),
Pid = os:getpid(),
- {MSecs,Secs,MySecs} = now(),
- F = lists:concat(["fs_",Node,U,Pid,U,MSecs,U,Secs,U,MySecs,"."]),
+ Unique = erlang:unique_integer([positive]),
+ F = lists:concat(["fs_",Node,U,Pid,U,Unique,"."]),
TmpDir = case TmpDirOpt of
default ->
Dir;
@@ -1586,7 +1586,6 @@ fun_rterms(InFun) ->
(read) ->
case InFun(read) of
{Ts, NInFun} when is_list(Ts),
- is_function(NInFun),
is_function(NInFun, 1) ->
{to_bin(Ts, []), fun_rterms(NInFun)};
Else ->
@@ -1599,7 +1598,7 @@ fun_wterms(OutFun) ->
OutFun(close);
(L) ->
case OutFun(wterms_arg(L)) of
- NOutFun when is_function(NOutFun), is_function(NOutFun, 1) ->
+ NOutFun when is_function(NOutFun, 1) ->
fun_wterms(NOutFun);
Else ->
Else
diff --git a/lib/stdlib/src/filelib.erl b/lib/stdlib/src/filelib.erl
index daae1fd2d2..7029389e2f 100644
--- a/lib/stdlib/src/filelib.erl
+++ b/lib/stdlib/src/filelib.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
diff --git a/lib/stdlib/src/filename.erl b/lib/stdlib/src/filename.erl
index e6bde5673c..c4586171ca 100644
--- a/lib/stdlib/src/filename.erl
+++ b/lib/stdlib/src/filename.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -35,6 +36,7 @@
extension/1, join/1, join/2, pathtype/1,
rootname/1, rootname/2, split/1, nativename/1]).
-export([find_src/1, find_src/2, flatten/1]).
+-export([basedir/2, basedir/3]).
%% Undocumented and unsupported exports.
-export([append/2]).
@@ -138,6 +140,7 @@ absname_join(AbsBase, Name) ->
-spec basename(Filename) -> file:filename_all() when
Filename :: file:name_all().
+
basename(Name) when is_binary(Name) ->
case os:type() of
{win32,_} ->
@@ -444,6 +447,8 @@ join1([], RelativeName, [$:|Rest], win32) ->
join1(RelativeName, [], [$:|Rest], win32);
join1([], RelativeName, [$/|Result], OsType) ->
join1(RelativeName, [], [$/|Result], OsType);
+join1([], RelativeName, [$., $/|Result], OsType) ->
+ join1(RelativeName, [], [$/|Result], OsType);
join1([], RelativeName, Result, OsType) ->
join1(RelativeName, [], [$/|Result], OsType);
join1([[_|_]=List|Rest], RelativeName, Result, OsType) ->
@@ -470,6 +475,8 @@ join1b(<<>>, RelativeName, [$:|Rest], win32) ->
join1b(RelativeName, <<>>, [$:|Rest], win32);
join1b(<<>>, RelativeName, [$/|Result], OsType) ->
join1b(RelativeName, <<>>, [$/|Result], OsType);
+join1b(<<>>, RelativeName, [$., $/|Result], OsType) ->
+ join1b(RelativeName, <<>>, [$/|Result], OsType);
join1b(<<>>, RelativeName, Result, OsType) ->
join1b(RelativeName, <<>>, [$/|Result], OsType);
join1b(<<Char,Rest/binary>>, RelativeName, Result, OsType) when is_integer(Char) ->
@@ -644,7 +651,7 @@ split(Name0) ->
unix_splitb(Name) ->
L = binary:split(Name,[<<"/">>],[global]),
LL = case L of
- [<<>>|Rest] ->
+ [<<>>|Rest] when Rest =/= [] ->
[<<"/">>|Rest];
_ ->
L
@@ -949,3 +956,180 @@ filename_string_to_binary(List) ->
Bin
end.
+%% Application Base Directories
+%% 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'.
+
+-spec basedir(Type,Application) -> file:filename_all() when
+ Type :: basedir_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(),
+ Application :: string() | binary(),
+ Opts :: #{author => string() | binary(),
+ os => 'windows' | 'darwin' | 'linux',
+ version => string() | binary()}.
+
+basedir(Type,Application,Opts) when is_atom(Type), is_map(Opts),
+ is_list(Application) orelse
+ is_binary(Application) ->
+ Os = basedir_os_from_opts(Opts),
+ Name = basedir_name_from_opts(Os,Application,Opts),
+ Base = basedir_from_os(Type,Os),
+ case {Type,Os} of
+ {user_log,linux} ->
+ filename:join([Base,Name,"log"]);
+ {user_log,windows} ->
+ filename:join([Base,Name,"Logs"]);
+ {user_cache,windows} ->
+ filename:join([Base,Name,"Cache"]);
+ {Type,_} when Type =:= site_config orelse Type =:= site_data ->
+ [filename:join([B,Name]) || B <- Base];
+ _ ->
+ filename:join([Base,Name])
+ end.
+
+basedir_os_from_opts(#{os := linux}) -> linux;
+basedir_os_from_opts(#{os := windows}) -> windows;
+basedir_os_from_opts(#{os := darwin}) -> darwin;
+basedir_os_from_opts(#{}) -> basedir_os_type().
+
+basedir_name_from_opts(windows,App,#{author:=Author,version:=Vsn}) ->
+ filename:join([Author,App,Vsn]);
+basedir_name_from_opts(windows,App,#{author:=Author}) ->
+ filename:join([Author,App]);
+basedir_name_from_opts(_,App,#{version:=Vsn}) ->
+ filename:join([App,Vsn]);
+basedir_name_from_opts(_,App,_) ->
+ App.
+
+basedir_from_os(Type,Os) ->
+ case Os of
+ linux -> basedir_linux(Type);
+ darwin -> basedir_darwin(Type);
+ windows -> basedir_windows(Type)
+ end.
+
+-define(basedir_linux_user_data, ".local/share").
+-define(basedir_linux_user_config, ".config").
+-define(basedir_linux_user_cache, ".cache").
+-define(basedir_linux_user_log, ".cache"). %% .cache/App/log
+-define(basedir_linux_site_data, "/usr/local/share/:/usr/share/").
+-define(basedir_linux_site_config, "/etc/xdg").
+
+basedir_linux(Type) ->
+ case Type of
+ user_data -> getenv("XDG_DATA_HOME", ?basedir_linux_user_data, true);
+ user_config -> getenv("XDG_CONFIG_HOME",?basedir_linux_user_config,true);
+ user_cache -> getenv("XDG_CACHE_HOME", ?basedir_linux_user_cache, true);
+ user_log -> getenv("XDG_CACHE_HOME", ?basedir_linux_user_log, true);
+ site_data ->
+ Base = getenv("XDG_DATA_DIRS",?basedir_linux_site_data,false),
+ string:tokens(Base,":");
+ site_config ->
+ Base = getenv("XDG_CONFIG_DIRS",?basedir_linux_site_config,false),
+ string:tokens(Base,":")
+ end.
+
+-define(basedir_darwin_user_data, "Library/Application Support").
+-define(basedir_darwin_user_config, "Library/Application Support").
+-define(basedir_darwin_user_cache, "Library/Caches").
+-define(basedir_darwin_user_log, "Library/Logs").
+-define(basedir_darwin_site_data, "/Library/Application Support").
+-define(basedir_darwin_site_config, "/Library/Application Support").
+
+basedir_darwin(Type) ->
+ case Type of
+ user_data -> basedir_join_home(?basedir_darwin_user_data);
+ user_config -> basedir_join_home(?basedir_darwin_user_config);
+ user_cache -> basedir_join_home(?basedir_darwin_user_cache);
+ user_log -> basedir_join_home(?basedir_darwin_user_log);
+ site_data -> [?basedir_darwin_site_data];
+ site_config -> [?basedir_darwin_site_config]
+ end.
+
+%% On Windows:
+%% ex. C:\Users\egil\AppData\Local\Ericsson\Erlang
+%% %LOCALAPPDATA% is defined on Windows 7 and onwards
+%% %APPDATA% is used instead of %LOCALAPPDATA% if it's not defined.
+%% %APPDATA% is used for roaming, i.e. for user_config on Windows 7 and beyond.
+%%
+%% user_data %LOCALAPPDATA%[/$author]/$appname[/$version]
+%% user_config %APPDATA%[/$author]/$appname[/$version]
+%% user_cache %LOCALAPPDATA%[/$author]/$appname[/$version]/Cache
+%% user_log %LOCALAPPDATA%[/$author]/$appname[/$version]/Logs
+
+-define(basedir_windows_user_data, "Local").
+-define(basedir_windows_user_config, "Roaming").
+-define(basedir_windows_user_cache, "Local"). %% Cache is added later
+-define(basedir_windows_user_log, "Local"). %% Logs is added later
+
+basedir_windows(Type) ->
+ %% If LOCALAPPDATA is not defined we are likely on an
+ %% XP machine. Use APPDATA instead.
+ case basedir_windows_appdata() of
+ noappdata ->
+ %% No AppData is set
+ %% Probably running MSYS
+ case Type of
+ user_data -> basedir_join_home(?basedir_windows_user_data);
+ user_config -> basedir_join_home(?basedir_windows_user_config);
+ user_cache -> basedir_join_home(?basedir_windows_user_cache);
+ user_log -> basedir_join_home(?basedir_windows_user_log);
+ site_data -> [];
+ site_config -> []
+ end;
+ {ok, AppData} ->
+ case Type of
+ user_data -> getenv("LOCALAPPDATA", AppData);
+ user_config -> AppData;
+ user_cache -> getenv("LOCALAPPDATA", AppData);
+ user_log -> getenv("LOCALAPPDATA", AppData);
+ site_data -> [];
+ site_config -> []
+ end
+ end.
+
+basedir_windows_appdata() ->
+ case os:getenv("APPDATA") of
+ Invalid when Invalid =:= false orelse Invalid =:= [] ->
+ noappdata;
+ Val ->
+ {ok, Val}
+ end.
+
+%% basedir aux
+
+getenv(K,Def,false) -> getenv(K,Def);
+getenv(K,Def,true) -> getenv(K,basedir_join_home(Def)).
+
+getenv(K,Def) ->
+ case os:getenv(K) of
+ [] -> Def;
+ false -> Def;
+ Val -> Val
+ end.
+
+basedir_join_home(Dir) ->
+ case os:getenv("HOME") of
+ false ->
+ {ok,[[Home]]} = init:get_argument(home),
+ filename:join(Home,Dir);
+ Home -> filename:join(Home,Dir)
+ end.
+
+basedir_os_type() ->
+ case os:type() of
+ {unix,darwin} -> darwin;
+ {win32,_} -> windows;
+ _ -> linux
+ end.
diff --git a/lib/stdlib/src/gb_sets.erl b/lib/stdlib/src/gb_sets.erl
index 0a26d0182d..47a8fa6db0 100644
--- a/lib/stdlib/src/gb_sets.erl
+++ b/lib/stdlib/src/gb_sets.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2014. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2015. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -137,6 +138,10 @@
%% approach is that it does not require the complete list of all
%% elements to be built in memory at one time.
%%
+%% - iterator_from(X, S): returns an iterator that can be used for
+%% traversing the elements of set S greater than or equal to X;
+%% see `next'.
+%%
%% - next(T): returns {X, T1} where X is the smallest element referred
%% to by the iterator T, and T1 is the new iterator to be used for
%% traversing the remaining elements, or the atom `none' if no
@@ -157,8 +162,8 @@
insert/2, add/2, delete/2, delete_any/2, balance/1, union/2,
union/1, intersection/2, intersection/1, is_disjoint/2, difference/2,
is_subset/2, to_list/1, from_list/1, from_ordset/1, smallest/1,
- largest/1, take_smallest/1, take_largest/1, iterator/1, next/1,
- filter/2, fold/3, is_set/1]).
+ largest/1, take_smallest/1, take_largest/1, iterator/1,
+ iterator_from/2, next/1, filter/2, fold/3, is_set/1]).
%% `sets' compatibility aliases:
@@ -199,29 +204,26 @@
-export_type([set/0, set/1, iter/0, iter/1]).
-type gb_set_node(Element) :: 'nil' | {Element, _, _}.
--type gb_set_node() :: gb_set_node(_).
-opaque set(Element) :: {non_neg_integer(), gb_set_node(Element)}.
--opaque set() :: set(_).
+-type set() :: set(_).
-opaque iter(Element) :: [gb_set_node(Element)].
--opaque iter() :: [gb_set_node()].
+-type iter() :: iter(_).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%%% gb_sets:set() in OTP 17 only.
-
-spec empty() -> Set when
- Set :: gb_sets:set().
+ Set :: set().
empty() ->
{0, nil}.
-spec new() -> Set when
- Set :: gb_sets:set().
+ Set :: set().
new() -> empty().
-spec is_empty(Set) -> boolean() when
- Set :: gb_sets:set().
+ Set :: set().
is_empty({0, nil}) ->
true;
@@ -229,7 +231,7 @@ is_empty(_) ->
false.
-spec size(Set) -> non_neg_integer() when
- Set :: gb_sets:set().
+ Set :: set().
size({Size, _}) ->
Size.
@@ -502,6 +504,22 @@ iterator({_, L, _} = T, As) ->
iterator(nil, As) ->
As.
+-spec iterator_from(Element, Set) -> Iter when
+ Set :: set(Element),
+ Iter :: iter(Element).
+
+iterator_from(S, {_, T}) ->
+ iterator_from(S, T, []).
+
+iterator_from(S, {K, _, T}, As) when K < S ->
+ iterator_from(S, T, As);
+iterator_from(_, {_, nil, _} = T, As) ->
+ [T | As];
+iterator_from(S, {_, L, _} = T, As) ->
+ iterator_from(S, L, [T | As]);
+iterator_from(_, nil, As) ->
+ As.
+
-spec next(Iter1) -> {Element, Iter2} | 'none' when
Iter1 :: iter(Element),
Iter2 :: iter(Element).
diff --git a/lib/stdlib/src/gb_trees.erl b/lib/stdlib/src/gb_trees.erl
index 7069b61873..c4a20d92a7 100644
--- a/lib/stdlib/src/gb_trees.erl
+++ b/lib/stdlib/src/gb_trees.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2014. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2015. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -102,6 +103,10 @@
%% approach is that it does not require the complete list of all
%% elements to be built in memory at one time.
%%
+%% - iterator_from(K, T): returns an iterator that can be used for
+%% traversing the entries of tree T with key greater than or
+%% equal to K; see `next'.
+%%
%% - next(S): returns {X, V, S1} where X is the smallest key referred to
%% by the iterator S, and S1 is the new iterator to be used for
%% traversing the remaining entries, or the atom `none' if no entries
@@ -117,7 +122,7 @@
update/3, enter/3, delete/2, delete_any/2, balance/1,
is_defined/2, keys/1, values/1, to_list/1, from_orddict/1,
smallest/1, largest/1, take_smallest/1, take_largest/1,
- iterator/1, next/1, map/2]).
+ iterator/1, iterator_from/2, next/1, map/2]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -156,11 +161,10 @@
-type gb_tree_node(K, V) :: 'nil'
| {K, V, gb_tree_node(K, V), gb_tree_node(K, V)}.
--type gb_tree_node() :: gb_tree_node(_, _).
-opaque tree(Key, Value) :: {non_neg_integer(), gb_tree_node(Key, Value)}.
--opaque tree() :: tree(_, _).
+-type tree() :: tree(_, _).
-opaque iter(Key, Value) :: [gb_tree_node(Key, Value)].
--opaque iter() :: [gb_tree_node()].
+-type iter() :: iter(_, _).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -529,6 +533,29 @@ iterator({_, _, L, _} = T, As) ->
iterator(nil, As) ->
As.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-spec iterator_from(Key, Tree) -> Iter when
+ Tree :: tree(Key, Value),
+ Iter :: iter(Key, Value).
+
+iterator_from(S, {_, T}) ->
+ iterator_1_from(S, T).
+
+iterator_1_from(S, T) ->
+ iterator_from(S, T, []).
+
+iterator_from(S, {K, _, _, T}, As) when K < S ->
+ iterator_from(S, T, As);
+iterator_from(_, {_, _, nil, _} = T, As) ->
+ [T | As];
+iterator_from(S, {_, _, L, _} = T, As) ->
+ iterator_from(S, L, [T | As]);
+iterator_from(_, nil, As) ->
+ As.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
-spec next(Iter1) -> 'none' | {Key, Value, Iter2} when
Iter1 :: iter(Key, Value),
Iter2 :: iter(Key, Value).
diff --git a/lib/stdlib/src/gen.erl b/lib/stdlib/src/gen.erl
index 63116fa16e..597830cf9a 100644
--- a/lib/stdlib/src/gen.erl
+++ b/lib/stdlib/src/gen.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -25,8 +26,9 @@
%%%
%%% The standard behaviour should export init_it/6.
%%%-----------------------------------------------------------------
--export([start/5, start/6, debug_options/1,
- call/3, call/4, reply/2]).
+-export([start/5, start/6, debug_options/2,
+ name/1, unregister_name/1, get_proc_name/1, get_parent/0,
+ call/3, call/4, reply/2, stop/1, stop/3]).
-export([init_it/6, init_it/7]).
@@ -123,7 +125,7 @@ init_it(GenMod, Starter, Parent, Mod, Args, Options) ->
init_it2(GenMod, Starter, Parent, self(), Mod, Args, Options).
init_it(GenMod, Starter, Parent, Name, Mod, Args, Options) ->
- case name_register(Name) of
+ case register_name(Name) of
true ->
init_it2(GenMod, Starter, Parent, Name, Mod, Args, Options);
{false, Pid} ->
@@ -145,56 +147,10 @@ init_it2(GenMod, Starter, Parent, Name, Mod, Args, Options) ->
call(Process, Label, Request) ->
call(Process, Label, Request, ?default_timeout).
-%% Local or remote by pid
-call(Pid, Label, Request, Timeout)
- when is_pid(Pid), Timeout =:= infinity;
- is_pid(Pid), is_integer(Timeout), Timeout >= 0 ->
- do_call(Pid, Label, Request, Timeout);
-%% Local by name
-call(Name, Label, Request, Timeout)
- when is_atom(Name), Timeout =:= infinity;
- is_atom(Name), is_integer(Timeout), Timeout >= 0 ->
- case whereis(Name) of
- Pid when is_pid(Pid) ->
- do_call(Pid, Label, Request, Timeout);
- undefined ->
- exit(noproc)
- end;
-%% Global by name
call(Process, Label, Request, Timeout)
- when ((tuple_size(Process) == 2 andalso element(1, Process) == global)
- orelse
- (tuple_size(Process) == 3 andalso element(1, Process) == via))
- andalso
- (Timeout =:= infinity orelse (is_integer(Timeout) andalso Timeout >= 0)) ->
- case where(Process) of
- Pid when is_pid(Pid) ->
- Node = node(Pid),
- try do_call(Pid, Label, Request, Timeout)
- catch
- exit:{nodedown, Node} ->
- %% A nodedown not yet detected by global,
- %% pretend that it was.
- exit(noproc)
- end;
- undefined ->
- exit(noproc)
- end;
-%% Local by name in disguise
-call({Name, Node}, Label, Request, Timeout)
- when Node =:= node(), Timeout =:= infinity;
- Node =:= node(), is_integer(Timeout), Timeout >= 0 ->
- call(Name, Label, Request, Timeout);
-%% Remote by name
-call({_Name, Node}=Process, Label, Request, Timeout)
- when is_atom(Node), Timeout =:= infinity;
- is_atom(Node), is_integer(Timeout), Timeout >= 0 ->
- if
- node() =:= nonode@nohost ->
- exit({nodedown, Node});
- true ->
- do_call(Process, Label, Request, Timeout)
- end.
+ when Timeout =:= infinity; is_integer(Timeout), Timeout >= 0 ->
+ Fun = fun(Pid) -> do_call(Pid, Label, Request, Timeout) end,
+ do_for_proc(Process, Fun).
do_call(Process, Label, Request, Timeout) ->
try erlang:monitor(process, Process) of
@@ -276,6 +232,65 @@ reply({To, Tag}, Reply) ->
Msg = {Tag, Reply},
try To ! Msg catch _:_ -> Msg end.
+%%-----------------------------------------------------------------
+%% Syncronously stop a generic process
+%%-----------------------------------------------------------------
+stop(Process) ->
+ stop(Process, normal, infinity).
+
+stop(Process, Reason, Timeout)
+ when Timeout =:= infinity; is_integer(Timeout), Timeout >= 0 ->
+ Fun = fun(Pid) -> proc_lib:stop(Pid, Reason, Timeout) end,
+ do_for_proc(Process, Fun).
+
+%%-----------------------------------------------------------------
+%% Map different specifications of a process to either Pid or
+%% {Name,Node}. Execute the given Fun with the process as only
+%% argument.
+%% -----------------------------------------------------------------
+
+%% Local or remote by pid
+do_for_proc(Pid, Fun) when is_pid(Pid) ->
+ Fun(Pid);
+%% Local by name
+do_for_proc(Name, Fun) when is_atom(Name) ->
+ case whereis(Name) of
+ Pid when is_pid(Pid) ->
+ Fun(Pid);
+ undefined ->
+ exit(noproc)
+ end;
+%% Global by name
+do_for_proc(Process, Fun)
+ when ((tuple_size(Process) == 2 andalso element(1, Process) == global)
+ orelse
+ (tuple_size(Process) == 3 andalso element(1, Process) == via)) ->
+ case where(Process) of
+ Pid when is_pid(Pid) ->
+ Node = node(Pid),
+ try Fun(Pid)
+ catch
+ exit:{nodedown, Node} ->
+ %% A nodedown not yet detected by global,
+ %% pretend that it was.
+ exit(noproc)
+ end;
+ undefined ->
+ exit(noproc)
+ end;
+%% Local by name in disguise
+do_for_proc({Name, Node}, Fun) when Node =:= node() ->
+ do_for_proc(Name, Fun);
+%% Remote by name
+do_for_proc({_Name, Node} = Process, Fun) when is_atom(Node) ->
+ if
+ node() =:= nonode@nohost ->
+ exit({nodedown, Node});
+ true ->
+ Fun(Process)
+ end.
+
+
%%%-----------------------------------------------------------------
%%% Misc. functions.
%%%-----------------------------------------------------------------
@@ -283,19 +298,19 @@ where({global, Name}) -> global:whereis_name(Name);
where({via, Module, Name}) -> Module:whereis_name(Name);
where({local, Name}) -> whereis(Name).
-name_register({local, Name} = LN) ->
+register_name({local, Name} = LN) ->
try register(Name, self()) of
true -> true
catch
error:_ ->
{false, where(LN)}
end;
-name_register({global, Name} = GN) ->
+register_name({global, Name} = GN) ->
case global:register_name(Name, self()) of
yes -> true;
no -> {false, where(GN)}
end;
-name_register({via, Module, Name} = GN) ->
+register_name({via, Module, Name} = GN) ->
case Module:register_name(Name, self()) of
yes ->
true;
@@ -303,34 +318,108 @@ name_register({via, Module, Name} = GN) ->
{false, where(GN)}
end.
+name({local,Name}) -> Name;
+name({global,Name}) -> Name;
+name({via,_, Name}) -> Name;
+name(Pid) when is_pid(Pid) -> Pid.
+
+unregister_name({local,Name}) ->
+ try unregister(Name) of
+ _ -> ok
+ catch
+ _:_ -> ok
+ end;
+unregister_name({global,Name}) ->
+ _ = global:unregister_name(Name),
+ ok;
+unregister_name({via, Mod, Name}) ->
+ _ = Mod:unregister_name(Name),
+ ok;
+unregister_name(Pid) when is_pid(Pid) ->
+ ok.
+
+get_proc_name(Pid) when is_pid(Pid) ->
+ Pid;
+get_proc_name({local, Name}) ->
+ case process_info(self(), registered_name) of
+ {registered_name, Name} ->
+ Name;
+ {registered_name, _Name} ->
+ exit(process_not_registered);
+ [] ->
+ exit(process_not_registered)
+ end;
+get_proc_name({global, Name}) ->
+ case global:whereis_name(Name) of
+ undefined ->
+ exit(process_not_registered_globally);
+ Pid when Pid =:= self() ->
+ Name;
+ _Pid ->
+ exit(process_not_registered_globally)
+ end;
+get_proc_name({via, Mod, Name}) ->
+ case Mod:whereis_name(Name) of
+ undefined ->
+ exit({process_not_registered_via, Mod});
+ Pid when Pid =:= self() ->
+ Name;
+ _Pid ->
+ exit({process_not_registered_via, Mod})
+ end.
+
+get_parent() ->
+ case get('$ancestors') of
+ [Parent | _] when is_pid(Parent) ->
+ Parent;
+ [Parent | _] when is_atom(Parent) ->
+ name_to_pid(Parent);
+ _ ->
+ exit(process_was_not_started_by_proc_lib)
+ end.
+
+name_to_pid(Name) ->
+ case whereis(Name) of
+ undefined ->
+ case global:whereis_name(Name) of
+ undefined ->
+ exit(could_not_find_registered_name);
+ Pid ->
+ Pid
+ end;
+ Pid ->
+ Pid
+ end.
+
timeout(Options) ->
- case opt(timeout, Options) of
- {ok, Time} ->
+ case lists:keyfind(timeout, 1, Options) of
+ {_,Time} ->
Time;
- _ ->
+ false ->
infinity
end.
spawn_opts(Options) ->
- case opt(spawn_opt, Options) of
- {ok, Opts} ->
+ case lists:keyfind(spawn_opt, 1, Options) of
+ {_,Opts} ->
Opts;
- _ ->
+ false ->
[]
end.
-opt(Op, [{Op, Value}|_]) ->
- {ok, Value};
-opt(Op, [_|Options]) ->
- opt(Op, Options);
-opt(_, []) ->
- false.
-
-debug_options(Opts) ->
- case opt(debug, Opts) of
- {ok, Options} -> sys:debug_options(Options);
- _ -> []
+debug_options(Name, Opts) ->
+ case lists:keyfind(debug, 1, Opts) of
+ {_,Options} ->
+ try sys:debug_options(Options)
+ catch _:_ ->
+ error_logger:format(
+ "~p: ignoring erroneous debug options - ~p~n",
+ [Name,Options]),
+ []
+ end;
+ false ->
+ []
end.
format_status_header(TagLine, Pid) when is_pid(Pid) ->
diff --git a/lib/stdlib/src/gen_event.erl b/lib/stdlib/src/gen_event.erl
index 469acdc37c..ccacf658e9 100644
--- a/lib/stdlib/src/gen_event.erl
+++ b/lib/stdlib/src/gen_event.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -31,8 +32,8 @@
%%% Modified by Martin - uses proc_lib, sys and gen!
--export([start/0, start/1, start_link/0, start_link/1, stop/1, notify/2,
- sync_notify/2,
+-export([start/0, start/1, start_link/0, start_link/1, stop/1, stop/3,
+ notify/2, sync_notify/2,
add_handler/3, add_sup_handler/3, delete_handler/3, swap_handler/3,
swap_sup_handler/3, which_handlers/1, call/3, call/4, wake_hib/4]).
@@ -99,6 +100,14 @@
-callback code_change(OldVsn :: (term() | {down, term()}),
State :: term(), Extra :: term()) ->
{ok, NewState :: term()}.
+-callback format_status(Opt, StatusData) -> Status when
+ Opt :: 'normal' | 'terminate',
+ StatusData :: [PDict | State],
+ PDict :: [{Key :: term(), Value :: term()}],
+ State :: term(),
+ Status :: term().
+
+-optional_callbacks([format_status/2]).
%%---------------------------------------------------------------------------
@@ -138,16 +147,11 @@ init_it(Starter, self, Name, Mod, Args, Options) ->
init_it(Starter, self(), Name, Mod, Args, Options);
init_it(Starter, Parent, Name0, _, _, Options) ->
process_flag(trap_exit, true),
- Debug = gen:debug_options(Options),
+ Name = gen:name(Name0),
+ Debug = gen:debug_options(Name, Options),
proc_lib:init_ack(Starter, {ok, self()}),
- Name = name(Name0),
loop(Parent, Name, [], Debug, false).
-name({local,Name}) -> Name;
-name({global,Name}) -> Name;
-name({via,_, Name}) -> Name;
-name(Pid) when is_pid(Pid) -> Pid.
-
-spec add_handler(emgr_ref(), handler(), term()) -> term().
add_handler(M, Handler, Args) -> rpc(M, {add_handler, Handler, Args}).
@@ -183,7 +187,11 @@ swap_sup_handler(M, {H1, A1}, {H2, A2}) ->
which_handlers(M) -> rpc(M, which_handlers).
-spec stop(emgr_ref()) -> 'ok'.
-stop(M) -> rpc(M, stop).
+stop(M) ->
+ gen:stop(M).
+
+stop(M, Reason, Timeout) ->
+ gen:stop(M, Reason, Timeout).
rpc(M, Cmd) ->
{ok, Reply} = gen:call(M, self(), Cmd, infinity),
diff --git a/lib/stdlib/src/gen_fsm.erl b/lib/stdlib/src/gen_fsm.erl
index 5afe3e8b09..6e7528fd98 100644
--- a/lib/stdlib/src/gen_fsm.erl
+++ b/lib/stdlib/src/gen_fsm.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -106,6 +107,7 @@
-export([start/3, start/4,
start_link/3, start_link/4,
+ stop/1, stop/3,
send_event/2, sync_send_event/2, sync_send_event/3,
send_all_state_event/2,
sync_send_all_state_event/2, sync_send_all_state_event/3,
@@ -160,6 +162,14 @@
-callback code_change(OldVsn :: term() | {down, term()}, StateName :: atom(),
StateData :: term(), Extra :: term()) ->
{ok, NextStateName :: atom(), NewStateData :: term()}.
+-callback format_status(Opt, StatusData) -> Status when
+ Opt :: 'normal' | 'terminate',
+ StatusData :: [PDict | State],
+ PDict :: [{Key :: term(), Value :: term()}],
+ State :: term(),
+ Status :: term().
+
+-optional_callbacks([format_status/2]).
%%% ---------------------------------------------------
%%% Starts a generic state machine.
@@ -189,6 +199,11 @@ start_link(Mod, Args, Options) ->
start_link(Name, Mod, Args, Options) ->
gen:start(?MODULE, link, Name, Mod, Args, Options).
+stop(Name) ->
+ gen:stop(Name).
+
+stop(Name, Reason, Timeout) ->
+ gen:stop(Name, Reason, Timeout).
send_event({global, Name}, Event) ->
catch global:send(Name, {'$gen_event', Event}),
@@ -290,64 +305,11 @@ enter_loop(Mod, Options, StateName, StateData, Timeout) ->
enter_loop(Mod, Options, StateName, StateData, self(), Timeout).
enter_loop(Mod, Options, StateName, StateData, ServerName, Timeout) ->
- Name = get_proc_name(ServerName),
- Parent = get_parent(),
- Debug = gen:debug_options(Options),
+ Name = gen:get_proc_name(ServerName),
+ Parent = gen:get_parent(),
+ Debug = gen:debug_options(Name, Options),
loop(Parent, Name, StateName, StateData, Mod, Timeout, Debug).
-get_proc_name(Pid) when is_pid(Pid) ->
- Pid;
-get_proc_name({local, Name}) ->
- case process_info(self(), registered_name) of
- {registered_name, Name} ->
- Name;
- {registered_name, _Name} ->
- exit(process_not_registered);
- [] ->
- exit(process_not_registered)
- end;
-get_proc_name({global, Name}) ->
- case global:whereis_name(Name) of
- undefined ->
- exit(process_not_registered_globally);
- Pid when Pid =:= self() ->
- Name;
- _Pid ->
- exit(process_not_registered_globally)
- end;
-get_proc_name({via, Mod, Name}) ->
- case Mod:whereis_name(Name) of
- undefined ->
- exit({process_not_registered_via, Mod});
- Pid when Pid =:= self() ->
- Name;
- _Pid ->
- exit({process_not_registered_via, Mod})
- end.
-
-get_parent() ->
- case get('$ancestors') of
- [Parent | _] when is_pid(Parent) ->
- Parent;
- [Parent | _] when is_atom(Parent) ->
- name_to_pid(Parent);
- _ ->
- exit(process_was_not_started_by_proc_lib)
- end.
-
-name_to_pid(Name) ->
- case whereis(Name) of
- undefined ->
- case global:whereis_name(Name) of
- undefined ->
- exit(could_not_find_registered_name);
- Pid ->
- Pid
- end;
- Pid ->
- Pid
- end.
-
%%% ---------------------------------------------------
%%% Initiate the new process.
%%% Register the name using the Rfunc function
@@ -358,8 +320,8 @@ name_to_pid(Name) ->
init_it(Starter, self, Name, Mod, Args, Options) ->
init_it(Starter, self(), Name, Mod, Args, Options);
init_it(Starter, Parent, Name0, Mod, Args, Options) ->
- Name = name(Name0),
- Debug = gen:debug_options(Options),
+ Name = gen:name(Name0),
+ Debug = gen:debug_options(Name, Options),
case catch Mod:init(Args) of
{ok, StateName, StateData} ->
proc_lib:init_ack(Starter, {ok, self()}),
@@ -368,15 +330,15 @@ init_it(Starter, Parent, Name0, Mod, Args, Options) ->
proc_lib:init_ack(Starter, {ok, self()}),
loop(Parent, Name, StateName, StateData, Mod, Timeout, Debug);
{stop, Reason} ->
- unregister_name(Name0),
+ gen:unregister_name(Name0),
proc_lib:init_ack(Starter, {error, Reason}),
exit(Reason);
ignore ->
- unregister_name(Name0),
+ gen:unregister_name(Name0),
proc_lib:init_ack(Starter, ignore),
exit(normal);
{'EXIT', Reason} ->
- unregister_name(Name0),
+ gen:unregister_name(Name0),
proc_lib:init_ack(Starter, {error, Reason}),
exit(Reason);
Else ->
@@ -385,20 +347,6 @@ init_it(Starter, Parent, Name0, Mod, Args, Options) ->
exit(Error)
end.
-name({local,Name}) -> Name;
-name({global,Name}) -> Name;
-name({via,_, Name}) -> Name;
-name(Pid) when is_pid(Pid) -> Pid.
-
-unregister_name({local,Name}) ->
- _ = (catch unregister(Name));
-unregister_name({global,Name}) ->
- _ = global:unregister_name(Name);
-unregister_name({via, Mod, Name}) ->
- _ = Mod:unregister_name(Name);
-unregister_name(Pid) when is_pid(Pid) ->
- Pid.
-
%%-----------------------------------------------------------------
%% The MAIN loop
%%-----------------------------------------------------------------
diff --git a/lib/stdlib/src/gen_server.erl b/lib/stdlib/src/gen_server.erl
index 18ef4a2507..5800aca66f 100644
--- a/lib/stdlib/src/gen_server.erl
+++ b/lib/stdlib/src/gen_server.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -88,6 +89,7 @@
%% API
-export([start/3, start/4,
start_link/3, start_link/4,
+ stop/1, stop/3,
call/2, call/3,
cast/2, reply/2,
abcast/2, abcast/3,
@@ -137,6 +139,15 @@
-callback code_change(OldVsn :: (term() | {down, term()}), State :: term(),
Extra :: term()) ->
{ok, NewState :: term()} | {error, Reason :: term()}.
+-callback format_status(Opt, StatusData) -> Status when
+ Opt :: 'normal' | 'terminate',
+ StatusData :: [PDict | State],
+ PDict :: [{Key :: term(), Value :: term()}],
+ State :: term(),
+ Status :: term().
+
+-optional_callbacks([format_status/2]).
+
%%% -----------------------------------------------------------------
%%% Starts a generic server.
@@ -168,6 +179,17 @@ start_link(Name, Mod, Args, Options) ->
%% -----------------------------------------------------------------
+%% Stop a generic server and wait for it to terminate.
+%% If the server is located at another node, that node will
+%% be monitored.
+%% -----------------------------------------------------------------
+stop(Name) ->
+ gen:stop(Name).
+
+stop(Name, Reason, Timeout) ->
+ gen:stop(Name, Reason, Timeout).
+
+%% -----------------------------------------------------------------
%% Make a call to a generic server.
%% If the server is located at another node, that node will
%% be monitored.
@@ -282,9 +304,9 @@ enter_loop(Mod, Options, State, Timeout) ->
enter_loop(Mod, Options, State, self(), Timeout).
enter_loop(Mod, Options, State, ServerName, Timeout) ->
- Name = get_proc_name(ServerName),
- Parent = get_parent(),
- Debug = debug_options(Name, Options),
+ Name = gen:get_proc_name(ServerName),
+ Parent = gen:get_parent(),
+ Debug = gen:debug_options(Name, Options),
loop(Parent, Name, State, Mod, Timeout, Debug).
%%%========================================================================
@@ -301,8 +323,8 @@ enter_loop(Mod, Options, State, ServerName, Timeout) ->
init_it(Starter, self, Name, Mod, Args, Options) ->
init_it(Starter, self(), Name, Mod, Args, Options);
init_it(Starter, Parent, Name0, Mod, Args, Options) ->
- Name = name(Name0),
- Debug = debug_options(Name, Options),
+ Name = gen:name(Name0),
+ Debug = gen:debug_options(Name, Options),
case catch Mod:init(Args) of
{ok, State} ->
proc_lib:init_ack(Starter, {ok, self()}),
@@ -317,15 +339,15 @@ init_it(Starter, Parent, Name0, Mod, Args, Options) ->
%% (Otherwise, the parent process could get
%% an 'already_started' error if it immediately
%% tried starting the process again.)
- unregister_name(Name0),
+ gen:unregister_name(Name0),
proc_lib:init_ack(Starter, {error, Reason}),
exit(Reason);
ignore ->
- unregister_name(Name0),
+ gen:unregister_name(Name0),
proc_lib:init_ack(Starter, ignore),
exit(normal);
{'EXIT', Reason} ->
- unregister_name(Name0),
+ gen:unregister_name(Name0),
proc_lib:init_ack(Starter, {error, Reason}),
exit(Reason);
Else ->
@@ -334,20 +356,6 @@ init_it(Starter, Parent, Name0, Mod, Args, Options) ->
exit(Error)
end.
-name({local,Name}) -> Name;
-name({global,Name}) -> Name;
-name({via,_, Name}) -> Name;
-name(Pid) when is_pid(Pid) -> Pid.
-
-unregister_name({local,Name}) ->
- _ = (catch unregister(Name));
-unregister_name({global,Name}) ->
- _ = global:unregister_name(Name);
-unregister_name({via, Mod, Name}) ->
- _ = Mod:unregister_name(Name);
-unregister_name(Pid) when is_pid(Pid) ->
- Pid.
-
%%%========================================================================
%%% Internal functions
%%%========================================================================
@@ -836,98 +844,6 @@ error_info(Reason, Name, Msg, State, Debug) ->
sys:print_log(Debug),
ok.
-%%% ---------------------------------------------------
-%%% Misc. functions.
-%%% ---------------------------------------------------
-
-opt(Op, [{Op, Value}|_]) ->
- {ok, Value};
-opt(Op, [_|Options]) ->
- opt(Op, Options);
-opt(_, []) ->
- false.
-
-debug_options(Name, Opts) ->
- case opt(debug, Opts) of
- {ok, Options} -> dbg_options(Name, Options);
- _ -> dbg_options(Name, [])
- end.
-
-dbg_options(Name, []) ->
- Opts =
- case init:get_argument(generic_debug) of
- error ->
- [];
- _ ->
- [log, statistics]
- end,
- dbg_opts(Name, Opts);
-dbg_options(Name, Opts) ->
- dbg_opts(Name, Opts).
-
-dbg_opts(Name, Opts) ->
- case catch sys:debug_options(Opts) of
- {'EXIT',_} ->
- format("~p: ignoring erroneous debug options - ~p~n",
- [Name, Opts]),
- [];
- Dbg ->
- Dbg
- end.
-
-get_proc_name(Pid) when is_pid(Pid) ->
- Pid;
-get_proc_name({local, Name}) ->
- case process_info(self(), registered_name) of
- {registered_name, Name} ->
- Name;
- {registered_name, _Name} ->
- exit(process_not_registered);
- [] ->
- exit(process_not_registered)
- end;
-get_proc_name({global, Name}) ->
- case global:whereis_name(Name) of
- undefined ->
- exit(process_not_registered_globally);
- Pid when Pid =:= self() ->
- Name;
- _Pid ->
- exit(process_not_registered_globally)
- end;
-get_proc_name({via, Mod, Name}) ->
- case Mod:whereis_name(Name) of
- undefined ->
- exit({process_not_registered_via, Mod});
- Pid when Pid =:= self() ->
- Name;
- _Pid ->
- exit({process_not_registered_via, Mod})
- end.
-
-get_parent() ->
- case get('$ancestors') of
- [Parent | _] when is_pid(Parent)->
- Parent;
- [Parent | _] when is_atom(Parent)->
- name_to_pid(Parent);
- _ ->
- exit(process_was_not_started_by_proc_lib)
- end.
-
-name_to_pid(Name) ->
- case whereis(Name) of
- undefined ->
- case global:whereis_name(Name) of
- undefined ->
- exit(could_not_find_registered_name);
- Pid ->
- Pid
- end;
- Pid ->
- Pid
- end.
-
%%-----------------------------------------------------------------
%% Status information
%%-----------------------------------------------------------------
diff --git a/lib/stdlib/src/gen_statem.erl b/lib/stdlib/src/gen_statem.erl
new file mode 100644
index 0000000000..23bddafeed
--- /dev/null
+++ b/lib/stdlib/src/gen_statem.erl
@@ -0,0 +1,1308 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2016. 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%
+%%
+-module(gen_statem).
+
+%% API
+-export(
+ [start/3,start/4,start_link/3,start_link/4,
+ stop/1,stop/3,
+ cast/2,call/2,call/3,
+ enter_loop/5,enter_loop/6,enter_loop/7,
+ reply/1,reply/2]).
+
+%% gen callbacks
+-export(
+ [init_it/6]).
+
+%% sys callbacks
+-export(
+ [system_continue/3,
+ system_terminate/4,
+ system_code_change/4,
+ system_get_state/1,
+ system_replace_state/2,
+ format_status/2]).
+
+%% Internal callbacks
+-export(
+ [wakeup_from_hibernate/3]).
+
+%% Type exports for templates
+-export_type(
+ [event_type/0,
+ callback_mode/0,
+ state_function_result/0,
+ handle_event_result/0,
+ action/0]).
+
+%% Fix problem for doc build
+-export_type([transition_option/0]).
+
+%%%==========================================================================
+%%% Interface functions.
+%%%==========================================================================
+
+-type from() ::
+ {To :: pid(), Tag :: term()}. % Reply-to specifier for call
+
+-type state() ::
+ state_name() | % For state callback function StateName/5
+ term(). % For state callback function handle_event/5
+
+-type state_name() :: atom().
+
+-type data() :: term().
+
+-type event_type() ::
+ {'call',From :: from()} | 'cast' |
+ 'info' | 'timeout' | 'internal'.
+
+-type callback_mode() :: 'state_functions' | 'handle_event_function'.
+
+-type transition_option() ::
+ postpone() | hibernate() | event_timeout().
+-type postpone() ::
+ %% If 'true' postpone the current event
+ %% and retry it when the state changes (=/=)
+ boolean().
+-type hibernate() ::
+ %% If 'true' hibernate the server instead of going into receive
+ boolean().
+-type event_timeout() ::
+ %% Generate a ('timeout', EventContent, ...) event after Time
+ %% unless some other event is delivered
+ Time :: timeout().
+
+-type action() ::
+ %% During a state change:
+ %% * NextState and NewData are set.
+ %% * All action()s are executed in order of apperance.
+ %% * Postponing the current event is performed
+ %% iff 'postpone' is 'true'.
+ %% * A state timer is started iff 'timeout' is set.
+ %% * Pending events are handled or if there are
+ %% no pending events the server goes into receive
+ %% or hibernate (iff 'hibernate' is 'true')
+ %%
+ %% These action()s are executed in order of appearence
+ %% in the containing list. The ones that set options
+ %% will override any previous so the last of each kind wins.
+ %%
+ 'postpone' | % Set the postpone option
+ {'postpone', Postpone :: postpone()} |
+ %%
+ 'hibernate' | % Set the hibernate option
+ {'hibernate', Hibernate :: hibernate()} |
+ %%
+ (Timeout :: event_timeout()) | % {timeout,Timeout}
+ {'timeout', % Set the event timeout option
+ Time :: event_timeout(), EventContent :: term()} |
+ %%
+ reply_action() |
+ %%
+ %% All 'next_event' events are kept in a list and then
+ %% inserted at state changes so the first in the
+ %% action() list is the first to be delivered.
+ {'next_event', % Insert event as the next to handle
+ EventType :: event_type(),
+ EventContent :: term()}.
+-type reply_action() ::
+ {'reply', % Reply to a caller
+ From :: from(), Reply :: term()}.
+
+-type state_function_result() ::
+ {'next_state', % {next_state,NextStateName,NewData,[]}
+ NextStateName :: state_name(),
+ NewData :: data()} |
+ {'next_state', % State transition, maybe to the same state
+ NextStateName :: state_name(),
+ NewData :: data(),
+ Actions :: [action()] | action()} |
+ common_state_callback_result().
+-type handle_event_result() ::
+ {'next_state', % {next_state,NextState,NewData,[]}
+ NextState :: state(),
+ NewData :: data()} |
+ {'next_state', % State transition, maybe to the same state
+ NextState :: state(),
+ NewData :: data(),
+ Actions :: [action()] | action()} |
+ common_state_callback_result().
+-type common_state_callback_result() ::
+ 'stop' | % {stop,normal}
+ {'stop', % Stop the server
+ Reason :: term()} |
+ {'stop', % Stop the server
+ Reason :: term(),
+ NewData :: data()} |
+ {'stop_and_reply', % Reply then stop the server
+ Reason :: term(),
+ Replies :: [reply_action()] | reply_action()} |
+ {'stop_and_reply', % Reply then stop the server
+ Reason :: term(),
+ Replies :: [reply_action()] | reply_action(),
+ NewData :: data()} |
+ {'keep_state', % {keep_state,NewData,[]}
+ NewData :: data()} |
+ {'keep_state', % Keep state, change data
+ NewData :: data(),
+ Actions :: [action()] | action()} |
+ 'keep_state_and_data' | % {keep_state_and_data,[]}
+ {'keep_state_and_data', % Keep state and data -> only actions
+ Actions :: [action()] | action()}.
+
+
+%% The state machine init function. It is called only once and
+%% the server is not running until this function has returned
+%% an {ok, ...} tuple. Thereafter the state callbacks are called
+%% for all events to this server.
+-callback init(Args :: term()) ->
+ {callback_mode(), state(), data()} |
+ {callback_mode(), state(), data(), [action()] | action()} |
+ 'ignore' |
+ {'stop', Reason :: term()}.
+
+%% Example state callback for callback_mode() =:= state_functions
+%% state name 'state_name'.
+%%
+%% In this mode all states has to be type state_name() i.e atom().
+%%
+%% Note that state callbacks and only state callbacks have arity 5
+%% and that is intended.
+-callback state_name(
+ event_type(),
+ EventContent :: term(),
+ Data :: data()) ->
+ state_function_result().
+%%
+%% State callback for callback_mode() =:= handle_event_function.
+%%
+%% Note that state callbacks and only state callbacks have arity 5
+%% and that is intended.
+-callback handle_event(
+ event_type(),
+ EventContent :: term(),
+ State :: state(), % Current state
+ Data :: data()) ->
+ handle_event_result().
+
+%% Clean up before the server terminates.
+-callback terminate(
+ Reason :: 'normal' | 'shutdown' | {'shutdown', term()}
+ | term(),
+ State :: state(),
+ Data :: data()) ->
+ any().
+
+%% Note that the new code can expect to get an OldState from
+%% the old code version not only in code_change/4 but in the first
+%% state callback function called thereafter
+-callback code_change(
+ OldVsn :: term() | {'down', term()},
+ OldState :: state(),
+ OldData :: data(),
+ Extra :: term()) ->
+ {NewCallbackMode :: callback_mode(),
+ NewState :: state(),
+ NewData :: data()}.
+
+%% Format the callback module state in some sensible that is
+%% often condensed way. For StatusOption =:= 'normal' the perferred
+%% return term is [{data,[{"State",FormattedState}]}], and for
+%% StatusOption =:= 'terminate' it is just FormattedState.
+-callback format_status(
+ StatusOption,
+ [ [{Key :: term(), Value :: term()}] |
+ state() |
+ data()]) ->
+ Status :: term() when
+ StatusOption :: 'normal' | 'terminate'.
+
+-optional_callbacks(
+ [init/1, % One may use enter_loop/5,6,7 instead
+ format_status/2, % Has got a default implementation
+ %%
+ state_name/3, % Example for callback_mode =:= state_functions:
+ %% there has to be a StateName/5 callback function for every StateName.
+ %%
+ handle_event/4]). % For callback_mode =:= handle_event_function
+
+%% Type validation functions
+callback_mode(CallbackMode) ->
+ case CallbackMode of
+ state_functions ->
+ true;
+ handle_event_function ->
+ true;
+ _ ->
+ false
+ end.
+%%
+from({Pid,_}) when is_pid(Pid) ->
+ true;
+from(_) ->
+ false.
+%%
+event_type({call,From}) ->
+ from(From);
+event_type(Type) ->
+ case Type of
+ cast ->
+ true;
+ info ->
+ true;
+ timeout ->
+ true;
+ internal ->
+ true;
+ _ ->
+ false
+ end.
+
+
+
+-define(
+ STACKTRACE(),
+ try throw(ok) catch _ -> erlang:get_stacktrace() end).
+
+%%%==========================================================================
+%%% API
+
+-type server_name() ::
+ {'global', GlobalName :: term()}
+ | {'via', RegMod :: module(), Name :: term()}
+ | {'local', atom()}.
+-type server_ref() ::
+ pid()
+ | (LocalName :: atom())
+ | {Name :: atom(), Node :: atom()}
+ | {'global', GlobalName :: term()}
+ | {'via', RegMod :: module(), ViaName :: term()}.
+-type debug_opt() ::
+ {'debug',
+ Dbgs ::
+ ['trace' | 'log' | 'statistics' | 'debug'
+ | {'logfile', string()}]}.
+-type start_opt() ::
+ debug_opt()
+ | {'timeout', Time :: timeout()}
+ | {'spawn_opt', [proc_lib:spawn_option()]}.
+-type start_ret() :: {'ok', pid()} | 'ignore' | {'error', term()}.
+
+
+
+%% Start a state machine
+-spec start(
+ Module :: module(), Args :: term(), Opts :: [start_opt()]) ->
+ start_ret().
+start(Module, Args, Opts) ->
+ gen:start(?MODULE, nolink, Module, Args, Opts).
+%%
+-spec start(
+ ServerName :: server_name(),
+ Module :: module(), Args :: term(), Opts :: [start_opt()]) ->
+ start_ret().
+start(ServerName, Module, Args, Opts) ->
+ gen:start(?MODULE, nolink, ServerName, Module, Args, Opts).
+
+%% Start and link to a state machine
+-spec start_link(
+ Module :: module(), Args :: term(), Opts :: [start_opt()]) ->
+ start_ret().
+start_link(Module, Args, Opts) ->
+ gen:start(?MODULE, link, Module, Args, Opts).
+%%
+-spec start_link(
+ ServerName :: server_name(),
+ Module :: module(), Args :: term(), Opts :: [start_opt()]) ->
+ start_ret().
+start_link(ServerName, Module, Args, Opts) ->
+ gen:start(?MODULE, link, ServerName, Module, Args, Opts).
+
+%% Stop a state machine
+-spec stop(ServerRef :: server_ref()) -> ok.
+stop(ServerRef) ->
+ gen:stop(ServerRef).
+%%
+-spec stop(
+ ServerRef :: server_ref(),
+ Reason :: term(),
+ Timeout :: timeout()) -> ok.
+stop(ServerRef, Reason, Timeout) ->
+ gen:stop(ServerRef, Reason, Timeout).
+
+%% Send an event to a state machine that arrives with type 'event'
+-spec cast(ServerRef :: server_ref(), Msg :: term()) -> ok.
+cast({global,Name}, Msg) ->
+ try global:send(Name, wrap_cast(Msg)) of
+ _ -> ok
+ catch
+ _:_ -> ok
+ end;
+cast({via,RegMod,Name}, Msg) ->
+ try RegMod:send(Name, wrap_cast(Msg)) of
+ _ -> ok
+ catch
+ _:_ -> ok
+ end;
+cast({Name,Node} = ServerRef, Msg) when is_atom(Name), is_atom(Node) ->
+ send(ServerRef, wrap_cast(Msg));
+cast(ServerRef, Msg) when is_atom(ServerRef) ->
+ send(ServerRef, wrap_cast(Msg));
+cast(ServerRef, Msg) when is_pid(ServerRef) ->
+ send(ServerRef, wrap_cast(Msg)).
+
+%% Call a state machine (synchronous; a reply is expected) that
+%% arrives with type {call,From}
+-spec call(ServerRef :: server_ref(), Request :: term()) -> Reply :: term().
+call(ServerRef, Request) ->
+ call(ServerRef, Request, infinity).
+%%
+-spec call(
+ ServerRef :: server_ref(),
+ Request :: term(),
+ Timeout :: timeout()) ->
+ Reply :: term().
+call(ServerRef, Request, infinity) ->
+ try gen:call(ServerRef, '$gen_call', Request, infinity) of
+ {ok,Reply} ->
+ Reply
+ catch
+ Class:Reason ->
+ erlang:raise(
+ Class,
+ {Reason,{?MODULE,call,[ServerRef,Request,infinity]}},
+ erlang:get_stacktrace())
+ end;
+call(ServerRef, Request, Timeout) ->
+ %% Call server through proxy process to dodge any late reply
+ Ref = make_ref(),
+ Self = self(),
+ Pid = spawn(
+ fun () ->
+ Self !
+ try gen:call(
+ ServerRef, '$gen_call', Request, Timeout) of
+ Result ->
+ {Ref,Result}
+ catch Class:Reason ->
+ {Ref,Class,Reason,erlang:get_stacktrace()}
+ end
+ end),
+ Mref = monitor(process, Pid),
+ receive
+ {Ref,Result} ->
+ demonitor(Mref, [flush]),
+ case Result of
+ {ok,Reply} ->
+ Reply
+ end;
+ {Ref,Class,Reason,Stacktrace} ->
+ demonitor(Mref, [flush]),
+ erlang:raise(
+ Class,
+ {Reason,{?MODULE,call,[ServerRef,Request,Timeout]}},
+ Stacktrace);
+ {'DOWN',Mref,_,_,Reason} ->
+ %% There is a theoretical possibility that the
+ %% proxy process gets killed between try--of and !
+ %% so this clause is in case of that
+ exit(Reason)
+ end.
+
+%% Reply from a state machine callback to whom awaits in call/2
+-spec reply([reply_action()] | reply_action()) -> ok.
+reply({reply,From,Reply}) ->
+ reply(From, Reply);
+reply(Replies) when is_list(Replies) ->
+ replies(Replies).
+%%
+-spec reply(From :: from(), Reply :: term()) -> ok.
+reply({To,Tag}, Reply) when is_pid(To) ->
+ Msg = {Tag,Reply},
+ try To ! Msg of
+ _ ->
+ ok
+ catch
+ _:_ -> ok
+ end.
+
+%% Instead of starting the state machine through start/3,4
+%% or start_link/3,4 turn the current process presumably
+%% started by proc_lib into a state machine using
+%% the same arguments as you would have returned from init/1
+-spec enter_loop(
+ Module :: module(), Opts :: [debug_opt()],
+ CallbackMode :: callback_mode(),
+ State :: state(), Data :: data()) ->
+ no_return().
+enter_loop(Module, Opts, CallbackMode, State, Data) ->
+ enter_loop(Module, Opts, CallbackMode, State, Data, self()).
+%%
+-spec enter_loop(
+ Module :: module(), Opts :: [debug_opt()],
+ CallbackMode :: callback_mode(),
+ State :: state(), Data :: data(),
+ Server_or_Actions ::
+ server_name() | pid() | [action()]) ->
+ no_return().
+enter_loop(Module, Opts, CallbackMode, State, Data, Server_or_Actions) ->
+ if
+ is_list(Server_or_Actions) ->
+ enter_loop(
+ Module, Opts, CallbackMode, State, Data,
+ self(), Server_or_Actions);
+ true ->
+ enter_loop(
+ Module, Opts, CallbackMode, State, Data,
+ Server_or_Actions, [])
+ end.
+%%
+-spec enter_loop(
+ Module :: module(), Opts :: [debug_opt()],
+ CallbackMode :: callback_mode(),
+ State :: state(), Data :: data(),
+ Server :: server_name() | pid(),
+ Actions :: [action()] | action()) ->
+ no_return().
+enter_loop(Module, Opts, CallbackMode, State, Data, Server, Actions) ->
+ is_atom(Module) orelse error({atom,Module}),
+ callback_mode(CallbackMode) orelse error({callback_mode,CallbackMode}),
+ Parent = gen:get_parent(),
+ enter(Module, Opts, CallbackMode, State, Data, Server, Actions, Parent).
+
+%%---------------------------------------------------------------------------
+%% API helpers
+
+wrap_cast(Event) ->
+ {'$gen_cast',Event}.
+
+replies([{reply,From,Reply}|Replies]) ->
+ reply(From, Reply),
+ replies(Replies);
+replies([]) ->
+ ok.
+
+%% Might actually not send the message in case of caught exception
+send(Proc, Msg) ->
+ try erlang:send(Proc, Msg, [noconnect]) of
+ noconnect ->
+ _ = spawn(erlang, send, [Proc,Msg]),
+ ok;
+ ok ->
+ ok
+ catch
+ _:_ ->
+ ok
+ end.
+
+%% Here the init_it/6 and enter_loop/5,6,7 functions converge
+enter(Module, Opts, CallbackMode, State, Data, Server, Actions, Parent) ->
+ %% The values should already have been type checked
+ Name = gen:get_proc_name(Server),
+ Debug = gen:debug_options(Name, Opts),
+ P = Events = [],
+ Event = {internal,initial_state},
+ %% We enforce {postpone,false} to ensure that
+ %% our fake Event gets discarded, thought it might get logged
+ NewActions =
+ if
+ is_list(Actions) ->
+ Actions ++ [{postpone,false}];
+ true ->
+ [Actions,{postpone,false}]
+ end,
+ S = #{
+ callback_mode => CallbackMode,
+ module => Module,
+ name => Name,
+ %% All fields below will be replaced according to the arguments to
+ %% loop_event_actions/10 when it finally loops back to loop/3
+ state => State,
+ data => Data,
+ postponed => P,
+ hibernate => false,
+ timer => undefined},
+ NewDebug = sys_debug(Debug, S, State, {enter,Event,State}),
+ loop_event_actions(
+ Parent, NewDebug, S, Events,
+ State, Data, P, Event, State, NewActions).
+
+%%%==========================================================================
+%%% gen callbacks
+
+init_it(Starter, self, ServerRef, Module, Args, Opts) ->
+ init_it(Starter, self(), ServerRef, Module, Args, Opts);
+init_it(Starter, Parent, ServerRef, Module, Args, Opts) ->
+ try Module:init(Args) of
+ Result ->
+ init_result(Starter, Parent, ServerRef, Module, Result, Opts)
+ catch
+ Result ->
+ init_result(Starter, Parent, ServerRef, Module, Result, Opts);
+ Class:Reason ->
+ gen:unregister_name(ServerRef),
+ proc_lib:init_ack(Starter, {error,Reason}),
+ erlang:raise(Class, Reason, erlang:get_stacktrace())
+ end.
+
+%%---------------------------------------------------------------------------
+%% gen callbacks helpers
+
+init_result(Starter, Parent, ServerRef, Module, Result, Opts) ->
+ case Result of
+ {CallbackMode,State,Data} ->
+ case callback_mode(CallbackMode) of
+ true ->
+ proc_lib:init_ack(Starter, {ok,self()}),
+ enter(
+ Module, Opts, CallbackMode, State, Data,
+ ServerRef, [], Parent);
+ false ->
+ Error = {callback_mode,CallbackMode},
+ proc_lib:init_ack(Starter, {error,Error}),
+ exit(Error)
+ end;
+ {CallbackMode,State,Data,Actions} ->
+ case callback_mode(CallbackMode) of
+ true ->
+ proc_lib:init_ack(Starter, {ok,self()}),
+ enter(
+ Module, Opts, CallbackMode, State, Data,
+ ServerRef, Actions, Parent);
+ false ->
+ Error = {callback_mode,CallbackMode},
+ proc_lib:init_ack(Starter, {error,Error}),
+ exit(Error)
+ end;
+ {stop,Reason} ->
+ gen:unregister_name(ServerRef),
+ proc_lib:init_ack(Starter, {error,Reason}),
+ exit(Reason);
+ ignore ->
+ gen:unregister_name(ServerRef),
+ proc_lib:init_ack(Starter, ignore),
+ exit(normal);
+ _ ->
+ Error = {bad_return_value,Result},
+ proc_lib:init_ack(Starter, {error,Error}),
+ exit(Error)
+ end.
+
+%%%==========================================================================
+%%% sys callbacks
+
+system_continue(Parent, Debug, S) ->
+ loop(Parent, Debug, S).
+
+system_terminate(
+ Reason, _Parent, Debug,
+ #{state := State, data := Data, postponed := P} = S) ->
+ terminate(
+ exit, Reason, ?STACKTRACE(),
+ Debug, S, [], State, Data, P).
+
+system_code_change(
+ #{module := Module,
+ state := State,
+ data := Data} = S,
+ _Mod, OldVsn, Extra) ->
+ case
+ try Module:code_change(OldVsn, State, Data, Extra)
+ catch
+ Result -> Result
+ end
+ of
+ {NewCallbackMode,NewState,NewData} ->
+ callback_mode(NewCallbackMode) orelse
+ error({callback_mode,NewCallbackMode}),
+ {ok,
+ S#{callback_mode := NewCallbackMode,
+ state := NewState,
+ data := NewData}};
+ {ok,_} = Error ->
+ error({case_clause,Error});
+ Error ->
+ Error
+ end.
+
+system_get_state(#{state := State, data := Data}) ->
+ {ok,{State,Data}}.
+
+system_replace_state(
+ StateFun,
+ #{state := State,
+ data := Data} = S) ->
+ {NewState,NewData} = Result = StateFun({State,Data}),
+ {ok,Result,S#{state := NewState, data := NewData}}.
+
+format_status(
+ Opt,
+ [PDict,SysState,Parent,Debug,
+ #{name := Name, postponed := P, state := State, data := Data} = S]) ->
+ Header = gen:format_status_header("Status for state machine", Name),
+ Log = sys:get_debug(log, Debug, []),
+ [{header,Header},
+ {data,
+ [{"Status",SysState},
+ {"Parent",Parent},
+ {"Logged Events",Log},
+ {"Postponed",P}]} |
+ case format_status(Opt, PDict, S, State, Data) of
+ L when is_list(L) -> L;
+ T -> [T]
+ end].
+
+%%---------------------------------------------------------------------------
+%% Format debug messages. Print them as the call-back module sees
+%% them, not as the real erlang messages. Use trace for that.
+%%---------------------------------------------------------------------------
+
+print_event(Dev, {in,Event}, {Name,_}) ->
+ io:format(
+ Dev, "*DBG* ~p received ~s~n",
+ [Name,event_string(Event)]);
+print_event(Dev, {out,Reply,{To,_Tag}}, {Name,_}) ->
+ io:format(
+ Dev, "*DBG* ~p sent ~p to ~p~n",
+ [Name,Reply,To]);
+print_event(Dev, {Tag,Event,NextState}, {Name,State}) ->
+ StateString =
+ case NextState of
+ State ->
+ io_lib:format("~p", [State]);
+ _ ->
+ io_lib:format("~p => ~p", [State,NextState])
+ end,
+ io:format(
+ Dev, "*DBG* ~p ~w ~s in state ~s~n",
+ [Name,Tag,event_string(Event),StateString]).
+
+event_string(Event) ->
+ case Event of
+ {{call,{Pid,_Tag}},Request} ->
+ io_lib:format("call ~p from ~w", [Request,Pid]);
+ {EventType,EventContent} ->
+ io_lib:format("~w ~p", [EventType,EventContent])
+ end.
+
+sys_debug(Debug, #{name := Name}, State, Entry) ->
+ case Debug of
+ [] ->
+ Debug;
+ _ ->
+ sys:handle_debug(
+ Debug, fun print_event/3, {Name,State}, Entry)
+ end.
+
+%%%==========================================================================
+%%% Internal callbacks
+
+wakeup_from_hibernate(Parent, Debug, S) ->
+ %% It is a new message that woke us up so we have to receive it now
+ loop_receive(Parent, Debug, S).
+
+%%%==========================================================================
+%%% State Machine engine implementation of proc_lib/gen server
+
+%% Server loop, consists of all loop* functions
+%% and detours through sys:handle_system_message/7 and proc_lib:hibernate/3
+
+%% Entry point for system_continue/3
+loop(Parent, Debug, #{hibernate := Hibernate} = S) ->
+ case Hibernate of
+ true ->
+ %% Does not return but restarts process at
+ %% wakeup_from_hibernate/3 that jumps to loop_receive/3
+ proc_lib:hibernate(
+ ?MODULE, wakeup_from_hibernate, [Parent,Debug,S]),
+ error(
+ {should_not_have_arrived_here_but_instead_in,
+ {wakeup_from_hibernate,3}});
+ false ->
+ loop_receive(Parent, Debug, S)
+ end.
+
+%% Entry point for wakeup_from_hibernate/3
+loop_receive(Parent, Debug, #{timer := Timer} = S) ->
+ receive
+ Msg ->
+ case Msg of
+ {system,Pid,Req} ->
+ #{hibernate := Hibernate} = S,
+ %% Does not return but tail recursively calls
+ %% system_continue/3 that jumps to loop/3
+ sys:handle_system_msg(
+ Req, Pid, Parent, ?MODULE, Debug, S, Hibernate);
+ {'EXIT',Parent,Reason} = EXIT ->
+ #{state := State, data := Data, postponed := P} = S,
+ %% EXIT is not a 2-tuple and therefore
+ %% not an event and has no event_type(),
+ %% but this will stand out in the crash report...
+ terminate(
+ exit, Reason, ?STACKTRACE(),
+ Debug, S, [EXIT], State, Data, P);
+ {timeout,Timer,Content} when Timer =/= undefined ->
+ loop_receive_result(
+ Parent, Debug, S, {timeout,Content});
+ _ ->
+ %% Cancel Timer if running
+ case Timer of
+ undefined ->
+ ok;
+ _ ->
+ case erlang:cancel_timer(Timer) of
+ TimeLeft when is_integer(TimeLeft) ->
+ ok;
+ false ->
+ receive
+ {timeout,Timer,_} ->
+ ok
+ after 0 ->
+ ok
+ end
+ end
+ end,
+ Event =
+ case Msg of
+ {'$gen_call',From,Request} ->
+ {{call,From},Request};
+ {'$gen_cast',E} ->
+ {cast,E};
+ _ ->
+ {info,Msg}
+ end,
+ loop_receive_result(Parent, Debug, S, Event)
+ end
+ end.
+
+loop_receive_result(
+ Parent, Debug,
+ #{state := State,
+ data := Data,
+ postponed := P} = S,
+ Event) ->
+ %% The engine state map S is now dismantled
+ %% and will not be restored until we return to loop/3.
+ %%
+ %% The fields 'callback_mode', 'module', and 'name' are still valid.
+ %% The fields 'state', 'data', and 'postponed' are held in arguments.
+ %% The fields 'timer' and 'hibernate' will be recalculated.
+ %%
+ NewDebug = sys_debug(Debug, S, State, {in,Event}),
+ %% Here the queue of not yet handled events is created
+ Events = [],
+ Hibernate = false,
+ loop_event(
+ Parent, NewDebug, S, Events, State, Data, P, Event, Hibernate).
+
+%% Process the event queue, or if it is empty
+%% loop back to loop/3 to receive a new event
+loop_events(
+ Parent, Debug, S, [Event|Events],
+ State, Data, P, Hibernate, _Timeout) ->
+ %%
+ %% If there was a state timer requested we just ignore that
+ %% since we have events to handle which cancels the timer
+ loop_event(
+ Parent, Debug, S, Events, State, Data, P, Event, Hibernate);
+loop_events(
+ Parent, Debug, S, [],
+ State, Data, P, Hibernate, Timeout) ->
+ case Timeout of
+ {timeout,0,EventContent} ->
+ %% Immediate timeout - simulate it
+ %% so we do not get the timeout message
+ %% after any received event
+ loop_event(
+ Parent, Debug, S, [],
+ State, Data, P, {timeout,EventContent}, Hibernate);
+ {timeout,Time,EventContent} ->
+ %% Actually start a timer
+ Timer = erlang:start_timer(Time, self(), EventContent),
+ loop_events_done(
+ Parent, Debug, S, Timer, State, Data, P, Hibernate);
+ undefined ->
+ %% No state timeout has been requested
+ Timer = undefined,
+ loop_events_done(
+ Parent, Debug, S, Timer, State, Data, P, Hibernate)
+ end.
+%%
+loop_events_done(Parent, Debug, S, Timer, State, Data, P, Hibernate) ->
+ NewS =
+ S#{
+ state := State,
+ data := Data,
+ postponed := P,
+ hibernate := Hibernate,
+ timer := Timer},
+ loop(Parent, Debug, NewS).
+
+loop_event(
+ Parent, Debug,
+ #{callback_mode := CallbackMode,
+ module := Module} = S,
+ Events,
+ State, Data, P, {Type,Content} = Event, Hibernate) ->
+ %%
+ %% If Hibernate is true here it can only be
+ %% because it was set from an event action
+ %% and we did not go into hibernation since there
+ %% were events in queue, so we do what the user
+ %% might depend on i.e collect garbage which
+ %% would have happened if we actually hibernated
+ %% and immediately was awakened
+ Hibernate andalso garbage_collect(),
+ %%
+ try
+ case CallbackMode of
+ state_functions ->
+ Module:State(Type, Content, Data);
+ handle_event_function ->
+ Module:handle_event(Type, Content, State, Data)
+ end of
+ Result ->
+ loop_event_result(
+ Parent, Debug, S, Events, State, Data, P, Event, Result)
+ catch
+ Result ->
+ loop_event_result(
+ Parent, Debug, S, Events, State, Data, P, Event, Result);
+ error:badarg when CallbackMode =:= state_functions ->
+ case erlang:get_stacktrace() of
+ [{erlang,apply,[Module,State,_],_}|Stacktrace] ->
+ Args = [Type,Content,Data],
+ terminate(
+ error,
+ {undef_state_function,{Module,State,Args}},
+ Stacktrace,
+ Debug, S, [Event|Events], State, Data, P);
+ Stacktrace ->
+ terminate(
+ error, badarg, Stacktrace,
+ Debug, S, [Event|Events], State, Data, P)
+ end;
+ error:undef ->
+ %% Process an undef to check for the simple mistake
+ %% of calling a nonexistent state function
+ case erlang:get_stacktrace() of
+ [{Module,State,
+ [Type,Content,Data]=Args,
+ _}
+ |Stacktrace]
+ when CallbackMode =:= state_functions ->
+ terminate(
+ error,
+ {undef_state_function,{Module,State,Args}},
+ Stacktrace,
+ Debug, S, [Event|Events], State, Data, P);
+ [{Module,handle_event,
+ [Type,Content,State,Data]=Args,
+ _}
+ |Stacktrace]
+ when CallbackMode =:= handle_event_function ->
+ terminate(
+ error,
+ {undef_state_function,{Module,handle_event,Args}},
+ Stacktrace,
+ Debug, S, [Event|Events], State, Data, P);
+ Stacktrace ->
+ terminate(
+ error, undef, Stacktrace,
+ Debug, S, [Event|Events], State, Data, P)
+ end;
+ Class:Reason ->
+ Stacktrace = erlang:get_stacktrace(),
+ terminate(
+ Class, Reason, Stacktrace,
+ Debug, S, [Event|Events], State, Data, P)
+ end.
+
+%% Interpret all callback return variants
+loop_event_result(
+ Parent, Debug, S, Events, State, Data, P, Event, Result) ->
+ case Result of
+ stop ->
+ terminate(
+ exit, normal, ?STACKTRACE(),
+ Debug, S, [Event|Events], State, Data, P);
+ {stop,Reason} ->
+ terminate(
+ exit, Reason, ?STACKTRACE(),
+ Debug, S, [Event|Events], State, Data, P);
+ {stop,Reason,NewData} ->
+ terminate(
+ exit, Reason, ?STACKTRACE(),
+ Debug, S, [Event|Events], State, NewData, P);
+ {stop_and_reply,Reason,Replies} ->
+ Q = [Event|Events],
+ reply_then_terminate(
+ exit, Reason, ?STACKTRACE(),
+ Debug, S, Q, State, Data, P, Replies);
+ {stop_and_reply,Reason,Replies,NewData} ->
+ Q = [Event|Events],
+ reply_then_terminate(
+ exit, Reason, ?STACKTRACE(),
+ Debug, S, Q, State, NewData, P, Replies);
+ {next_state,NextState,NewData} ->
+ loop_event_actions(
+ Parent, Debug, S, Events,
+ State, NewData, P, Event, NextState, []);
+ {next_state,NextState,NewData,Actions} ->
+ loop_event_actions(
+ Parent, Debug, S, Events,
+ State, NewData, P, Event, NextState, Actions);
+ {keep_state,NewData} ->
+ loop_event_actions(
+ Parent, Debug, S, Events,
+ State, NewData, P, Event, State, []);
+ {keep_state,NewData,Actions} ->
+ loop_event_actions(
+ Parent, Debug, S, Events,
+ State, NewData, P, Event, State, Actions);
+ keep_state_and_data ->
+ loop_event_actions(
+ Parent, Debug, S, Events,
+ State, Data, P, Event, State, []);
+ {keep_state_and_data,Actions} ->
+ loop_event_actions(
+ Parent, Debug, S, Events,
+ State, Data, P, Event, State, Actions);
+ _ ->
+ terminate(
+ error, {bad_return_value,Result}, ?STACKTRACE(),
+ Debug, S, [Event|Events], State, Data, P)
+ end.
+
+loop_event_actions(
+ Parent, Debug, S, Events, State, NewData, P, Event, NextState, Actions) ->
+ Postpone = false, % Shall we postpone this event; boolean()
+ Hibernate = false,
+ Timeout = undefined,
+ NextEvents = [],
+ loop_event_actions(
+ Parent, Debug, S, Events, State, NewData, P, Event, NextState,
+ if
+ is_list(Actions) ->
+ Actions;
+ true ->
+ [Actions]
+ end,
+ Postpone, Hibernate, Timeout, NextEvents).
+%%
+%% Process all actions
+loop_event_actions(
+ Parent, Debug, S, Events,
+ State, NewData, P, Event, NextState, [Action|Actions],
+ Postpone, Hibernate, Timeout, NextEvents) ->
+ case Action of
+ %% Actual actions
+ {reply,From,Reply} ->
+ case from(From) of
+ true ->
+ NewDebug = do_reply(Debug, S, State, From, Reply),
+ loop_event_actions(
+ Parent, NewDebug, S, Events,
+ State, NewData, P, Event, NextState, Actions,
+ Postpone, Hibernate, Timeout, NextEvents);
+ false ->
+ terminate(
+ error, {bad_action,Action}, ?STACKTRACE(),
+ Debug, S, [Event|Events], State, NewData, P)
+ end;
+ {next_event,Type,Content} ->
+ case event_type(Type) of
+ true ->
+ loop_event_actions(
+ Parent, Debug, S, Events,
+ State, NewData, P, Event, NextState, Actions,
+ Postpone, Hibernate, Timeout,
+ [{Type,Content}|NextEvents]);
+ false ->
+ terminate(
+ error, {bad_action,Action}, ?STACKTRACE(),
+ Debug, S, [Event|Events], State, NewData, P)
+ end;
+ %% Actions that set options
+ {postpone,NewPostpone} when is_boolean(NewPostpone) ->
+ loop_event_actions(
+ Parent, Debug, S, Events,
+ State, NewData, P, Event, NextState, Actions,
+ NewPostpone, Hibernate, Timeout, NextEvents);
+ {postpone,_} ->
+ terminate(
+ error, {bad_action,Action}, ?STACKTRACE(),
+ Debug, S, [Event|Events], State, NewData, P);
+ postpone ->
+ loop_event_actions(
+ Parent, Debug, S, Events,
+ State, NewData, P, Event, NextState, Actions,
+ true, Hibernate, Timeout, NextEvents);
+ {hibernate,NewHibernate} when is_boolean(NewHibernate) ->
+ loop_event_actions(
+ Parent, Debug, S, Events,
+ State, NewData, P, Event, NextState, Actions,
+ Postpone, NewHibernate, Timeout, NextEvents);
+ {hibernate,_} ->
+ terminate(
+ error, {bad_action,Action}, ?STACKTRACE(),
+ Debug, S, [Event|Events], State, NewData, P);
+ hibernate ->
+ loop_event_actions(
+ Parent, Debug, S, Events,
+ State, NewData, P, Event, NextState, Actions,
+ Postpone, true, Timeout, NextEvents);
+ {timeout,infinity,_} -> % Clear timer - it will never trigger
+ loop_event_actions(
+ Parent, Debug, S, Events,
+ State, NewData, P, Event, NextState, Actions,
+ Postpone, Hibernate, undefined, NextEvents);
+ {timeout,Time,_} = NewTimeout when is_integer(Time), Time >= 0 ->
+ loop_event_actions(
+ Parent, Debug, S, Events,
+ State, NewData, P, Event, NextState, Actions,
+ Postpone, Hibernate, NewTimeout, NextEvents);
+ {timeout,_,_} ->
+ terminate(
+ error, {bad_action,Action}, ?STACKTRACE(),
+ Debug, S, [Event|Events], State, NewData, P);
+ infinity -> % Clear timer - it will never trigger
+ loop_event_actions(
+ Parent, Debug, S, Events,
+ State, NewData, P, Event, NextState, Actions,
+ Postpone, Hibernate, undefined, NextEvents);
+ Time when is_integer(Time), Time >= 0 ->
+ NewTimeout = {timeout,Time,Time},
+ loop_event_actions(
+ Parent, Debug, S, Events,
+ State, NewData, P, Event, NextState, Actions,
+ Postpone, Hibernate, NewTimeout, NextEvents);
+ _ ->
+ terminate(
+ error, {bad_action,Action}, ?STACKTRACE(),
+ Debug, S, [Event|Events], State, NewData, P)
+ end;
+%%
+%% End of actions list
+loop_event_actions(
+ Parent, Debug, S, Events,
+ State, NewData, P0, Event, NextState, [],
+ Postpone, Hibernate, Timeout, NextEvents) ->
+ %%
+ %% All options have been collected and next_events are buffered.
+ %% Do the actual state transition.
+ %%
+ P1 = % Move current event to postponed if Postpone
+ case Postpone of
+ true ->
+ [Event|P0];
+ false ->
+ P0
+ end,
+ {Q2,P} = % Move all postponed events to queue if state change
+ if
+ NextState =:= State ->
+ {Events,P1};
+ true ->
+ {lists:reverse(P1, Events),[]}
+ end,
+ %% Place next events first in queue
+ Q = lists:reverse(NextEvents, Q2),
+ %%
+ NewDebug =
+ sys_debug(
+ Debug, S, State,
+ case Postpone of
+ true ->
+ {postpone,Event,NextState};
+ false ->
+ {consume,Event,NextState}
+ end),
+ loop_events(
+ Parent, NewDebug, S, Q, NextState, NewData, P, Hibernate, Timeout).
+
+%%---------------------------------------------------------------------------
+%% Server helpers
+
+reply_then_terminate(
+ Class, Reason, Stacktrace,
+ Debug, S, Q, State, Data, P, Replies) ->
+ if
+ is_list(Replies) ->
+ do_reply_then_terminate(
+ Class, Reason, Stacktrace,
+ Debug, S, Q, State, Data, P, Replies);
+ true ->
+ do_reply_then_terminate(
+ Class, Reason, Stacktrace,
+ Debug, S, Q, State, Data, P, [Replies])
+ end.
+%%
+do_reply_then_terminate(
+ Class, Reason, Stacktrace, Debug, S, Q, State, Data, P, []) ->
+ terminate(Class, Reason, Stacktrace, Debug, S, Q, State, Data, P);
+do_reply_then_terminate(
+ Class, Reason, Stacktrace, Debug, S, Q, State, Data, P, [R|Rs]) ->
+ case R of
+ {reply,{_To,_Tag}=From,Reply} ->
+ NewDebug = do_reply(Debug, S, State, From, Reply),
+ do_reply_then_terminate(
+ Class, Reason, Stacktrace,
+ NewDebug, S, Q, State, Data, P, Rs);
+ _ ->
+ terminate(
+ error, {bad_action,R}, ?STACKTRACE(),
+ Debug, S, Q, State, Data, P)
+ end.
+
+do_reply(Debug, S, State, From, Reply) ->
+ reply(From, Reply),
+ sys_debug(Debug, S, State, {out,Reply,From}).
+
+
+terminate(
+ Class, Reason, Stacktrace,
+ Debug, #{module := Module} = S, Q, State, Data, P) ->
+ try Module:terminate(Reason, State, Data) of
+ _ -> ok
+ catch
+ _ -> ok;
+ C:R ->
+ ST = erlang:get_stacktrace(),
+ error_info(
+ C, R, ST, Debug, S, Q, P,
+ format_status(terminate, get(), S, State, Data)),
+ erlang:raise(C, R, ST)
+ end,
+ case Reason of
+ normal -> ok;
+ shutdown -> ok;
+ {shutdown,_} -> ok;
+ _ ->
+ error_info(
+ Class, Reason, Stacktrace, Debug, S, Q, P,
+ format_status(terminate, get(), S, State, Data))
+ end,
+ case Stacktrace of
+ [] ->
+ erlang:Class(Reason);
+ _ ->
+ erlang:raise(Class, Reason, Stacktrace)
+ end.
+
+error_info(
+ Class, Reason, Stacktrace, Debug,
+ #{name := Name, callback_mode := CallbackMode},
+ Q, P, FmtData) ->
+ {FixedReason,FixedStacktrace} =
+ case Stacktrace of
+ [{M,F,Args,_}|ST]
+ when Class =:= error, Reason =:= undef ->
+ case code:is_loaded(M) of
+ false ->
+ {{'module could not be loaded',M},ST};
+ _ ->
+ Arity =
+ if
+ is_list(Args) ->
+ length(Args);
+ is_integer(Args) ->
+ Args
+ end,
+ case erlang:function_exported(M, F, Arity) of
+ true ->
+ {Reason,Stacktrace};
+ false ->
+ {{'function not exported',{M,F,Arity}},
+ ST}
+ end
+ end;
+ _ -> {Reason,Stacktrace}
+ end,
+ error_logger:format(
+ "** State machine ~p terminating~n" ++
+ case Q of
+ [] -> "";
+ _ -> "** Last event = ~p~n"
+ end ++
+ "** When server state = ~p~n" ++
+ "** Reason for termination = ~w:~p~n" ++
+ "** Callback mode = ~p~n" ++
+ case Q of
+ [_,_|_] -> "** Queued = ~p~n";
+ _ -> ""
+ end ++
+ case P of
+ [] -> "";
+ _ -> "** Postponed = ~p~n"
+ end ++
+ case FixedStacktrace of
+ [] -> "";
+ _ -> "** Stacktrace =~n** ~p~n"
+ end,
+ [Name |
+ case Q of
+ [] -> [];
+ [Event|_] -> [Event]
+ end] ++
+ [FmtData,Class,FixedReason,
+ CallbackMode] ++
+ case Q of
+ [_|[_|_] = Events] -> [Events];
+ _ -> []
+ end ++
+ case P of
+ [] -> [];
+ _ -> [P]
+ end ++
+ case FixedStacktrace of
+ [] -> [];
+ _ -> [FixedStacktrace]
+ end),
+ sys:print_log(Debug),
+ ok.
+
+
+%% Call Module:format_status/2 or return a default value
+format_status(Opt, PDict, #{module := Module}, State, Data) ->
+ case erlang:function_exported(Module, format_status, 2) of
+ true ->
+ try Module:format_status(Opt, [PDict,State,Data])
+ catch
+ Result -> Result;
+ _:_ ->
+ format_status_default(
+ Opt, State,
+ "Module:format_status/2 crashed")
+ end;
+ false ->
+ format_status_default(Opt, State, Data)
+ end.
+
+%% The default Module:format_status/2
+format_status_default(Opt, State, Data) ->
+ SSD = {State,Data},
+ case Opt of
+ terminate ->
+ SSD;
+ _ ->
+ [{data,[{"State",SSD}]}]
+ end.
diff --git a/lib/stdlib/src/io.erl b/lib/stdlib/src/io.erl
index b9ace2f442..f510f61e9f 100644
--- a/lib/stdlib/src/io.erl
+++ b/lib/stdlib/src/io.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2015. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -45,7 +46,7 @@
%% ErrorDescription is whatever the I/O-server sends.
-type server_no_data() :: {'error', ErrorDescription :: term()} | 'eof'.
--type location() :: erl_scan:location().
+-type location() :: erl_anno:location().
%%-------------------------------------------------------------------------
@@ -443,7 +444,7 @@ scan_erl_form(Io, Prompt, Pos0, Options) ->
%% Parsing Erlang code.
-type parse_ret() :: {'ok',
- ExprList :: erl_parse:abstract_expr(),
+ ExprList :: [erl_parse:abstract_expr()],
EndLocation :: location()}
| {'eof', EndLocation :: location()}
| {'error',
@@ -630,41 +631,20 @@ io_requests(Pid, [], [Rs|Cont], Tail) ->
io_requests(_Pid, [], [], _Tail) ->
{false,[]}.
-
-bc_req(Pid,{Op,Enc,Param},MaybeConvert) ->
- case net_kernel:dflag_unicode_io(Pid) of
- true ->
- {false,{Op,Enc,Param}};
- false ->
- {MaybeConvert,{Op,Param}}
- end;
-bc_req(Pid,{Op,Enc,P,F},MaybeConvert) ->
- case net_kernel:dflag_unicode_io(Pid) of
- true ->
- {false,{Op,Enc,P,F}};
- false ->
- {MaybeConvert,{Op,P,F}}
- end;
-bc_req(Pid, {Op,Enc,M,F,A},MaybeConvert) ->
+bc_req(Pid, Req0, MaybeConvert) ->
case net_kernel:dflag_unicode_io(Pid) of
true ->
- {false,{Op,Enc,M,F,A}};
+ %% The most common case. A modern i/o server.
+ {false,Req0};
false ->
- {MaybeConvert,{Op,M,F,A}}
- end;
-bc_req(Pid, {Op,Enc,P,M,F,A},MaybeConvert) ->
- case net_kernel:dflag_unicode_io(Pid) of
- true ->
- {false,{Op,Enc,P,M,F,A}};
- false ->
- {MaybeConvert,{Op,P,M,F,A}}
- end;
-bc_req(Pid,{Op,Enc},MaybeConvert) ->
- case net_kernel:dflag_unicode_io(Pid) of
- true ->
- {false,{Op, Enc}};
- false ->
- {MaybeConvert,Op}
+ %% Backward compatibility only. Unlikely to ever happen.
+ case tuple_to_list(Req0) of
+ [Op,_Enc] ->
+ {MaybeConvert,Op};
+ [Op,_Enc|T] ->
+ Req = list_to_tuple([Op|T]),
+ {MaybeConvert,Req}
+ end
end.
io_request(Pid, {write,Term}) ->
diff --git a/lib/stdlib/src/io_lib.erl b/lib/stdlib/src/io_lib.erl
index adc9a0cf5f..ad98bc0420 100644
--- a/lib/stdlib/src/io_lib.erl
+++ b/lib/stdlib/src/io_lib.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -60,6 +61,7 @@
-module(io_lib).
-export([fwrite/2,fread/2,fread/3,format/2]).
+-export([scan_format/2,unscan_format/1,build_text/1]).
-export([print/1,print/4,indentation/2]).
-export([write/1,write/2,write/3,nl/0,format_prompt/1,format_prompt/2]).
@@ -83,7 +85,7 @@
deep_unicode_char_list/1]).
-export_type([chars/0, latin1_string/0, continuation/0,
- fread_error/0, fread_item/0]).
+ fread_error/0, fread_item/0, format_spec/0]).
%%----------------------------------------------------------------------
@@ -108,6 +110,18 @@
-type fread_item() :: string() | atom() | integer() | float().
+-type format_spec() ::
+ #{
+ control_char := char(),
+ args := [any()],
+ width := 'none' | integer(),
+ adjust := 'left' | 'right',
+ precision := 'none' | integer(),
+ pad_char := char(),
+ encoding := 'unicode' | 'latin1',
+ strings := boolean()
+ }.
+
%%----------------------------------------------------------------------
%% Interface calls to sub-modules.
@@ -156,6 +170,31 @@ format(Format, Args) ->
Other
end.
+-spec scan_format(Format, Data) -> FormatList when
+ Format :: io:format(),
+ Data :: [term()],
+ FormatList :: [char() | format_spec()].
+
+scan_format(Format, Args) ->
+ try io_lib_format:scan(Format, Args)
+ catch
+ _:_ -> erlang:error(badarg, [Format, Args])
+ end.
+
+-spec unscan_format(FormatList) -> {Format, Data} when
+ FormatList :: [char() | format_spec()],
+ Format :: io:format(),
+ Data :: [term()].
+
+unscan_format(FormatList) ->
+ io_lib_format:unscan(FormatList).
+
+-spec build_text(FormatList) -> chars() when
+ FormatList :: [char() | format_spec()].
+
+build_text(FormatList) ->
+ io_lib_format:build(FormatList).
+
-spec print(Term) -> chars() when
Term :: term().
@@ -249,6 +288,8 @@ write([H|T], D) ->
end;
write(F, _D) when is_function(F) ->
erlang:fun_to_list(F);
+write(Term, D) when is_map(Term) ->
+ write_map(Term, D);
write(T, D) when is_tuple(T) ->
if
D =:= 1 -> "{...}";
@@ -257,9 +298,7 @@ write(T, D) when is_tuple(T) ->
[write(element(1, T), D-1)|
write_tail(tl(tuple_to_list(T)), D-1, $,)],
$}]
- end;
-%write(Term, D) when is_map(Term) -> write_map(Term, D);
-write(Term, D) -> write_map(Term, D).
+ end.
%% write_tail(List, Depth, CharacterBeforeDots)
%% Test the terminating case first as this looks better with depth.
diff --git a/lib/stdlib/src/io_lib_format.erl b/lib/stdlib/src/io_lib_format.erl
index 89ae6fb187..1da866dc88 100644
--- a/lib/stdlib/src/io_lib_format.erl
+++ b/lib/stdlib/src/io_lib_format.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -20,10 +21,9 @@
%% Formatting functions of io library.
--export([fwrite/2,fwrite_g/1,indentation/2]).
+-export([fwrite/2,fwrite_g/1,indentation/2,scan/2,unscan/1,build/1]).
-%% fwrite(Format, ArgList) -> string().
-%% Format the arguments in ArgList after string Format. Just generate
+%% Format the arguments in Args after string Format. Just generate
%% an error if there is an error in the arguments.
%%
%% To do the printing command correctly we need to calculate the
@@ -37,15 +37,84 @@
%% and it also splits the handling of the control characters into two
%% parts.
-fwrite(Format, Args) when is_atom(Format) ->
- fwrite(atom_to_list(Format), Args);
-fwrite(Format, Args) when is_binary(Format) ->
- fwrite(binary_to_list(Format), Args);
+-spec fwrite(Format, Data) -> FormatList when
+ Format :: io:format(),
+ Data :: [term()],
+ FormatList :: [char() | io_lib:format_spec()].
+
fwrite(Format, Args) ->
- Cs = collect(Format, Args),
+ build(scan(Format, Args)).
+
+%% Build the output text for a pre-parsed format list.
+
+-spec build(FormatList) -> io_lib:chars() when
+ FormatList :: [char() | io_lib:format_spec()].
+
+build(Cs) ->
Pc = pcount(Cs),
build(Cs, Pc, 0).
+%% Parse all control sequences in the format string.
+
+-spec scan(Format, Data) -> FormatList when
+ Format :: io:format(),
+ Data :: [term()],
+ FormatList :: [char() | io_lib:format_spec()].
+
+scan(Format, Args) when is_atom(Format) ->
+ scan(atom_to_list(Format), Args);
+scan(Format, Args) when is_binary(Format) ->
+ scan(binary_to_list(Format), Args);
+scan(Format, Args) ->
+ collect(Format, Args).
+
+%% Revert a pre-parsed format list to a plain character list and a
+%% list of arguments.
+
+-spec unscan(FormatList) -> {Format, Data} when
+ FormatList :: [char() | io_lib:format_spec()],
+ Format :: io:format(),
+ Data :: [term()].
+
+unscan(Cs) ->
+ {print(Cs), args(Cs)}.
+
+args([#{args := As} | Cs]) ->
+ As ++ args(Cs);
+args([_C | Cs]) ->
+ args(Cs);
+args([]) ->
+ [].
+
+print([#{control_char := C, width := F, adjust := Ad, precision := P,
+ pad_char := Pad, encoding := Encoding, strings := Strings} | Cs]) ->
+ print(C, F, Ad, P, Pad, Encoding, Strings) ++ print(Cs);
+print([C | Cs]) ->
+ [C | print(Cs)];
+print([]) ->
+ [].
+
+print(C, F, Ad, P, Pad, Encoding, Strings) ->
+ [$~] ++ print_field_width(F, Ad) ++ print_precision(P) ++
+ print_pad_char(Pad) ++ print_encoding(Encoding) ++
+ print_strings(Strings) ++ [C].
+
+print_field_width(none, _Ad) -> "";
+print_field_width(F, left) -> integer_to_list(-F);
+print_field_width(F, right) -> integer_to_list(F).
+
+print_precision(none) -> "";
+print_precision(P) -> [$. | integer_to_list(P)].
+
+print_pad_char($\s) -> ""; % default, no need to make explicit
+print_pad_char(Pad) -> [$., Pad].
+
+print_encoding(unicode) -> "t";
+print_encoding(latin1) -> "".
+
+print_strings(false) -> "l";
+print_strings(true) -> "".
+
collect([$~|Fmt0], Args0) ->
{C,Fmt1,Args1} = collect_cseq(Fmt0, Args0),
[C|collect(Fmt1, Args1)];
@@ -60,7 +129,10 @@ collect_cseq(Fmt0, Args0) ->
{Encoding,Fmt4,Args4} = encoding(Fmt3, Args3),
{Strings,Fmt5,Args5} = strings(Fmt4, Args4),
{C,As,Fmt6,Args6} = collect_cc(Fmt5, Args5),
- {{C,As,F,Ad,P,Pad,Encoding,Strings},Fmt6,Args6}.
+ FormatSpec = #{control_char => C, args => As, width => F, adjust => Ad,
+ precision => P, pad_char => Pad, encoding => Encoding,
+ strings => Strings},
+ {FormatSpec,Fmt6,Args6}.
encoding([$t|Fmt],Args) ->
true = hd(Fmt) =/= $l,
@@ -136,17 +208,19 @@ collect_cc([$i|Fmt], [A|Args]) -> {$i,[A],Fmt,Args}.
pcount(Cs) -> pcount(Cs, 0).
-pcount([{$p,_As,_F,_Ad,_P,_Pad,_Enc,_Str}|Cs], Acc) -> pcount(Cs, Acc+1);
-pcount([{$P,_As,_F,_Ad,_P,_Pad,_Enc,_Str}|Cs], Acc) -> pcount(Cs, Acc+1);
+pcount([#{control_char := $p}|Cs], Acc) -> pcount(Cs, Acc+1);
+pcount([#{control_char := $P}|Cs], Acc) -> pcount(Cs, Acc+1);
pcount([_|Cs], Acc) -> pcount(Cs, Acc);
pcount([], Acc) -> Acc.
-%% build([Control], Pc, Indentation) -> string().
+%% build([Control], Pc, Indentation) -> io_lib:chars().
%% Interpret the control structures. Count the number of print
%% remaining and only calculate indentation when necessary. Must also
%% be smart when calculating indentation for characters in format.
-build([{C,As,F,Ad,P,Pad,Enc,Str}|Cs], Pc0, I) ->
+build([#{control_char := C, args := As, width := F, adjust := Ad,
+ precision := P, pad_char := Pad, encoding := Enc,
+ strings := Str} | Cs], Pc0, I) ->
S = control(C, As, F, Ad, P, Pad, Enc, Str, I),
Pc1 = decr_pc(C, Pc0),
if
@@ -162,10 +236,14 @@ decr_pc($p, Pc) -> Pc - 1;
decr_pc($P, Pc) -> Pc - 1;
decr_pc(_, Pc) -> Pc.
-%% indentation(String, Indentation) -> Indentation.
+
%% Calculate the indentation of the end of a string given its start
%% indentation. We assume tabs at 8 cols.
+-spec indentation(String, StartIndent) -> integer() when
+ String :: io_lib:chars(),
+ StartIndent :: integer().
+
indentation([$\n|Cs], _I) -> indentation(Cs, 0);
indentation([$\t|Cs], I) -> indentation(Cs, ((I + 8) div 8) * 8);
indentation([C|Cs], I) when is_integer(C) ->
@@ -366,7 +444,6 @@ float_data([D|Cs], Ds) when D >= $0, D =< $9 ->
float_data([_|Cs], Ds) ->
float_data(Cs, Ds).
-%% fwrite_g(Float)
%% Writes the shortest, correctly rounded string that converts
%% to Float when read back with list_to_float/1.
%%
@@ -374,6 +451,8 @@ float_data([_|Cs], Ds) ->
%% in Proceedings of the SIGPLAN '96 Conference on Programming
%% Language Design and Implementation.
+-spec fwrite_g(float()) -> string().
+
fwrite_g(0.0) ->
"0.0";
fwrite_g(Float) when is_float(Float) ->
@@ -642,7 +721,7 @@ prefixed_integer(Int, F, Adj, Base, Pad, Prefix, Lowercase)
term([Prefix|S], F, Adj, none, Pad)
end.
-%% char(Char, Field, Adjust, Precision, PadChar) -> string().
+%% char(Char, Field, Adjust, Precision, PadChar) -> chars().
char(C, none, _Adj, none, _Pad) -> [C];
char(C, F, _Adj, none, _Pad) -> chars(C, F);
diff --git a/lib/stdlib/src/io_lib_fread.erl b/lib/stdlib/src/io_lib_fread.erl
index 491e1f40d7..6a8f8f728e 100644
--- a/lib/stdlib/src/io_lib_fread.erl
+++ b/lib/stdlib/src/io_lib_fread.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
diff --git a/lib/stdlib/src/io_lib_pretty.erl b/lib/stdlib/src/io_lib_pretty.erl
index aece06afa6..16ca2f41dc 100644
--- a/lib/stdlib/src/io_lib_pretty.erl
+++ b/lib/stdlib/src/io_lib_pretty.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
diff --git a/lib/stdlib/src/lib.erl b/lib/stdlib/src/lib.erl
index 8351376691..6fba63a895 100644
--- a/lib/stdlib/src/lib.erl
+++ b/lib/stdlib/src/lib.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
diff --git a/lib/stdlib/src/lists.erl b/lib/stdlib/src/lists.erl
index 6303465d3d..af9d63ddd6 100644
--- a/lib/stdlib/src/lists.erl
+++ b/lib/stdlib/src/lists.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -38,7 +39,8 @@
-export([all/2,any/2,map/2,flatmap/2,foldl/3,foldr/3,filter/2,
partition/2,zf/2,filtermap/2,
mapfoldl/3,mapfoldr/3,foreach/2,takewhile/2,dropwhile/2,splitwith/2,
- split/2]).
+ split/2,
+ join/2]).
%%% BIFs
-export([keyfind/3, keymember/3, keysearch/3, member/2, reverse/2]).
@@ -1438,6 +1440,18 @@ split(N, [H|T], R) ->
split(_, [], _) ->
badarg.
+-spec join(Sep, List1) -> List2 when
+ Sep :: T,
+ List1 :: [T],
+ List2 :: [T],
+ T :: term().
+
+join(_Sep, []) -> [];
+join(Sep, [H|T]) -> [H|join_prepend(Sep, T)].
+
+join_prepend(_Sep, []) -> [];
+join_prepend(Sep, [H|T]) -> [Sep,H|join_prepend(Sep,T)].
+
%%% =================================================================
%%% Here follows the implementation of the sort functions.
%%%
@@ -2266,6 +2280,8 @@ ukeysplit_2(I, Y, EY, [Z | L], R) ->
ukeysplit_2(_I, Y, _EY, [], R) ->
[Y | R].
+-dialyzer({no_improper_lists, ukeymergel/3}).
+
ukeymergel(I, [T1, [H2 | T2], [H3 | T3] | L], Acc) ->
%% The fourth argument, [H2 | H3] (=HdM), may confuse type
%% checkers. Its purpose is to ensure that the tests H2 == HdM
diff --git a/lib/stdlib/src/log_mf_h.erl b/lib/stdlib/src/log_mf_h.erl
index 6b42363979..393da9ab27 100644
--- a/lib/stdlib/src/log_mf_h.erl
+++ b/lib/stdlib/src/log_mf_h.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
diff --git a/lib/stdlib/src/maps.erl b/lib/stdlib/src/maps.erl
index ba4d6a5c87..5dafdb282a 100644
--- a/lib/stdlib/src/maps.erl
+++ b/lib/stdlib/src/maps.erl
@@ -1,50 +1,37 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
-module(maps).
--export([
- fold/3,
- map/2,
- size/1,
- without/2,
- with/2,
- get/3
- ]).
-
-
-%%% BIFs
--export([
- get/2,
- find/2,
- from_list/1,
- is_key/2,
- keys/1,
- merge/2,
- new/0,
- put/3,
- remove/2,
- to_list/1,
- update/3,
- values/1
- ]).
+-export([get/3, filter/2,fold/3,
+ map/2, size/1,
+ update_with/3, update_with/4,
+ without/2, with/2]).
+
+%% BIFs
+-export([get/2, find/2, from_list/1,
+ is_key/2, keys/1, merge/2,
+ new/0, put/3, remove/2, take/2,
+ to_list/1, update/3, values/1]).
+%% Shadowed by erl_bif_types: maps:get/2
-spec get(Key,Map) -> Value when
Key :: term(),
Map :: map(),
@@ -60,7 +47,7 @@ get(_,_) -> erlang:nif_error(undef).
find(_,_) -> erlang:nif_error(undef).
-
+%% Shadowed by erl_bif_types: maps:from_list/1
-spec from_list(List) -> Map when
List :: [{Key,Value}],
Key :: term(),
@@ -70,6 +57,7 @@ find(_,_) -> erlang:nif_error(undef).
from_list(_) -> erlang:nif_error(undef).
+%% Shadowed by erl_bif_types: maps:is_key/2
-spec is_key(Key,Map) -> boolean() when
Key :: term(),
Map :: map().
@@ -85,6 +73,7 @@ is_key(_,_) -> erlang:nif_error(undef).
keys(_) -> erlang:nif_error(undef).
+%% Shadowed by erl_bif_types: maps:merge/2
-spec merge(Map1,Map2) -> Map3 when
Map1 :: map(),
Map2 :: map(),
@@ -100,6 +89,7 @@ merge(_,_) -> erlang:nif_error(undef).
new() -> erlang:nif_error(undef).
+%% Shadowed by erl_bif_types: maps:put/3
-spec put(Key,Value,Map1) -> Map2 when
Key :: term(),
Value :: term(),
@@ -116,7 +106,15 @@ 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().
+
+take(_,_) -> erlang:nif_error(undef).
+%% Shadowed by erl_bif_types: maps:to_list/1
-spec to_list(Map) -> [{Key,Value}] when
Map :: map(),
Key :: term(),
@@ -125,6 +123,7 @@ remove(_,_) -> erlang:nif_error(undef).
to_list(_) -> erlang:nif_error(undef).
+%% Shadowed by erl_bif_types: maps:update/3
-spec update(Key,Value,Map1) -> Map2 when
Key :: term(),
Value :: term(),
@@ -141,8 +140,40 @@ update(_,_,_) -> erlang:nif_error(undef).
values(_) -> erlang:nif_error(undef).
+%% End of BIFs
+
+-spec update_with(Key,Fun,Map1) -> Map2 when
+ Key :: term(),
+ Map1 :: map(),
+ Map2 :: map(),
+ Fun :: fun((Value1 :: term()) -> Value2 :: term()).
+
+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])
+ 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().
+
+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)
+ end;
+update_with(Key,Fun,Init,Map) ->
+ erlang:error(error_type(Map),[Key,Fun,Init,Map]).
-%%% End of BIFs
-spec get(Key, Map, Default) -> Value | Default when
Key :: term(),
@@ -150,13 +181,28 @@ values(_) -> erlang:nif_error(undef).
Value :: term(),
Default :: term().
-get(Key, Map, Default) ->
+get(Key,Map,Default) when is_map(Map) ->
case maps:find(Key, Map) of
{ok, Value} ->
Value;
error ->
Default
- end.
+ end;
+get(Key,Map,Default) ->
+ erlang:error({badmap,Map},[Key,Map,Default]).
+
+
+-spec filter(Pred,Map1) -> Map2 when
+ Pred :: fun((Key, Value) -> boolean()),
+ Key :: term(),
+ Value :: term(),
+ Map1 :: map(),
+ Map2 :: map().
+
+filter(Pred,Map) when is_function(Pred,2), is_map(Map) ->
+ maps:from_list([{K,V}||{K,V}<-maps:to_list(Map),Pred(K,V)]);
+filter(Pred,Map) ->
+ erlang:error(error_type(Map),[Pred,Map]).
-spec fold(Fun,Init,Map) -> Acc when
@@ -169,8 +215,10 @@ get(Key, Map, Default) ->
K :: term(),
V :: term().
-fold(Fun, Init, Map) when is_function(Fun,3), is_map(Map) ->
- lists:foldl(fun({K,V},A) -> Fun(K,V,A) end,Init,maps:to_list(Map)).
+fold(Fun,Init,Map) when is_function(Fun,3), is_map(Map) ->
+ lists:foldl(fun({K,V},A) -> Fun(K,V,A) end,Init,maps:to_list(Map));
+fold(Fun,Init,Map) ->
+ erlang:error(error_type(Map),[Fun,Init,Map]).
-spec map(Fun,Map1) -> Map2 when
Fun :: fun((K, V1) -> V2),
@@ -180,18 +228,19 @@ fold(Fun, Init, Map) when is_function(Fun,3), is_map(Map) ->
V1 :: term(),
V2 :: term().
-map(Fun, Map) when is_function(Fun, 2), is_map(Map) ->
- maps:from_list(lists:map(fun
- ({K,V}) ->
- {K,Fun(K,V)}
- end,maps:to_list(Map))).
+map(Fun,Map) when is_function(Fun, 2), is_map(Map) ->
+ maps:from_list([{K,Fun(K,V)}||{K,V}<-maps:to_list(Map)]);
+map(Fun,Map) ->
+ erlang:error(error_type(Map),[Fun,Map]).
-spec size(Map) -> non_neg_integer() when
Map :: map().
size(Map) when is_map(Map) ->
- erlang:map_size(Map).
+ erlang:map_size(Map);
+size(Val) ->
+ erlang:error({badmap,Val},[Val]).
-spec without(Ks,Map1) -> Map2 when
@@ -200,8 +249,10 @@ size(Map) when is_map(Map) ->
Map2 :: map(),
K :: term().
-without(Ks, M) when is_list(Ks), is_map(M) ->
- maps:from_list([{K,V}||{K,V} <- maps:to_list(M), not lists:member(K, Ks)]).
+without(Ks,M) when is_list(Ks), is_map(M) ->
+ lists:foldl(fun(K, M1) -> ?MODULE:remove(K, M1) end, M, Ks);
+without(Ks,M) ->
+ erlang:error(error_type(M),[Ks,M]).
-spec with(Ks, Map1) -> Map2 when
@@ -210,5 +261,19 @@ without(Ks, M) when is_list(Ks), is_map(M) ->
Map2 :: map(),
K :: term().
-with(Ks, M) when is_list(Ks), is_map(M) ->
- maps:from_list([{K,V}||{K,V} <- maps:to_list(M), lists:member(K, Ks)]).
+with(Ks,Map1) when is_list(Ks), is_map(Map1) ->
+ Fun = fun(K, List) ->
+ case ?MODULE:find(K, Map1) of
+ {ok, V} ->
+ [{K, V} | List];
+ error ->
+ List
+ end
+ end,
+ ?MODULE:from_list(lists:foldl(Fun, [], Ks));
+with(Ks,M) ->
+ erlang:error(error_type(M),[Ks,M]).
+
+
+error_type(M) when is_map(M) -> badarg;
+error_type(V) -> {badmap, V}.
diff --git a/lib/stdlib/src/math.erl b/lib/stdlib/src/math.erl
index 98a70b1644..97c965e27a 100644
--- a/lib/stdlib/src/math.erl
+++ b/lib/stdlib/src/math.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -24,7 +25,7 @@
-export([sin/1, cos/1, tan/1, asin/1, acos/1, atan/1, atan2/2, sinh/1,
cosh/1, tanh/1, asinh/1, acosh/1, atanh/1, exp/1, log/1,
- log10/1, pow/2, sqrt/1, erf/1, erfc/1]).
+ log2/1, log10/1, pow/2, sqrt/1, erf/1, erfc/1]).
-spec acos(X) -> float() when
X :: number().
@@ -92,6 +93,11 @@ exp(_) ->
log(_) ->
erlang:nif_error(undef).
+-spec log2(X) -> float() when
+ X :: number().
+log2(_) ->
+ erlang:nif_error(undef).
+
-spec log10(X) -> float() when
X :: number().
log10(_) ->
diff --git a/lib/stdlib/src/ms_transform.erl b/lib/stdlib/src/ms_transform.erl
index 97564e2e44..24b5fde1db 100644
--- a/lib/stdlib/src/ms_transform.erl
+++ b/lib/stdlib/src/ms_transform.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2015. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -306,15 +307,18 @@ cleanup_filename({Old,OldRec,OldWarnings}) ->
add_record_definition({Name,FieldList}) ->
{KeyList,_} = lists:foldl(
- fun({record_field,_,{atom,Line0,FieldName}},{L,C}) ->
- {[{FieldName,C,{atom,Line0,undefined}}|L],C+1};
- ({record_field,_,{atom,_,FieldName},Def},{L,C}) ->
- {[{FieldName,C,Def}|L],C+1}
- end,
+ fun(F, {L,C}) -> {[record_field(F, C)|L],C+1} end,
{[],2},
FieldList),
put_records([{Name,KeyList}|get_records()]).
+record_field({record_field,_,{atom,Line0,FieldName}}, C) ->
+ {FieldName,C,{atom,Line0,undefined}};
+record_field({record_field,_,{atom,_,FieldName},Def}, C) ->
+ {FieldName,C,Def};
+record_field({typed_record_field,Field,_Type}, C) ->
+ record_field(Field, C).
+
forms([F0|Fs0]) ->
F1 = form(F0),
Fs1 = forms(Fs0),
@@ -822,9 +826,10 @@ th(T,B,OB) when is_tuple(T) ->
th(Nonstruct,B,_OB) ->
{Nonstruct,B}.
-warn_var_clash(Line,Name,OuterBound) ->
+warn_var_clash(Anno,Name,OuterBound) ->
case gb_sets:is_member(Name,OuterBound) of
true ->
+ Line = erl_anno:line(Anno),
add_warning(Line,{?WARN_SHADOW_VAR,Name});
_ ->
ok
@@ -1079,6 +1084,12 @@ normalise({cons,_,Head,Tail}) ->
[normalise(Head)|normalise(Tail)];
normalise({tuple,_,Args}) ->
list_to_tuple(normalise_list(Args));
+normalise({map,_,Pairs0}) ->
+ Pairs1 = lists:map(fun ({map_field_exact,_,K,V}) ->
+ {normalise(K),normalise(V)}
+ end,
+ Pairs0),
+ maps:from_list(Pairs1);
%% Special case for unary +/-.
normalise({op,_,'+',{char,_,I}}) -> I;
normalise({op,_,'+',{integer,_,I}}) -> I;
diff --git a/lib/stdlib/src/orddict.erl b/lib/stdlib/src/orddict.erl
index c98d78b34d..37cf0084f0 100644
--- a/lib/stdlib/src/orddict.erl
+++ b/lib/stdlib/src/orddict.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2015. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -25,11 +26,13 @@
-export([store/3,append/3,append_list/3,update/3,update/4,update_counter/3]).
-export([fold/3,map/2,filter/2,merge/3]).
--export_type([orddict/0]).
+-export_type([orddict/0, orddict/2]).
%%---------------------------------------------------------------------------
--type orddict() :: [{Key :: term(), Value :: term()}].
+-type orddict() :: orddict(_, _).
+
+-type orddict(Key, Value) :: [{Key, Value}].
%%---------------------------------------------------------------------------
@@ -38,8 +41,7 @@
new() -> [].
-spec is_key(Key, Orddict) -> boolean() when
- Key :: term(),
- Orddict :: orddict().
+ Orddict :: orddict(Key, Value :: term()).
is_key(Key, [{K,_}|_]) when Key < K -> false;
is_key(Key, [{K,_}|Dict]) when Key > K -> is_key(Key, Dict);
@@ -47,14 +49,14 @@ is_key(_Key, [{_K,_Val}|_]) -> true; %Key == K
is_key(_, []) -> false.
-spec to_list(Orddict) -> List when
- Orddict :: orddict(),
- List :: [{Key :: term(), Value :: term()}].
+ Orddict :: orddict(Key, Value),
+ List :: [{Key, Value}].
to_list(Dict) -> Dict.
-spec from_list(List) -> Orddict when
- List :: [{Key :: term(), Value :: term()}],
- Orddict :: orddict().
+ List :: [{Key, Value}],
+ Orddict :: orddict(Key, Value).
from_list([]) -> [];
from_list([{_,_}]=Pair) -> Pair;
@@ -73,17 +75,13 @@ is_empty([]) -> true;
is_empty([_|_]) -> false.
-spec fetch(Key, Orddict) -> Value when
- Key :: term(),
- Value :: term(),
- Orddict :: orddict().
+ Orddict :: orddict(Key, Value).
fetch(Key, [{K,_}|D]) when Key > K -> fetch(Key, D);
fetch(Key, [{K,Value}|_]) when Key == K -> Value.
-spec find(Key, Orddict) -> {'ok', Value} | 'error' when
- Key :: term(),
- Orddict :: orddict(),
- Value :: term().
+ Orddict :: orddict(Key, Value).
find(Key, [{K,_}|_]) when Key < K -> error;
find(Key, [{K,_}|D]) when Key > K -> find(Key, D);
@@ -91,17 +89,16 @@ find(_Key, [{_K,Value}|_]) -> {ok,Value}; %Key == K
find(_, []) -> error.
-spec fetch_keys(Orddict) -> Keys when
- Orddict :: orddict(),
- Keys :: [term()].
+ Orddict :: orddict(Key, Value :: term()),
+ Keys :: [Key].
fetch_keys([{Key,_}|Dict]) ->
[Key|fetch_keys(Dict)];
fetch_keys([]) -> [].
-spec erase(Key, Orddict1) -> Orddict2 when
- Key :: term(),
- Orddict1 :: orddict(),
- Orddict2 :: orddict().
+ Orddict1 :: orddict(Key, Value),
+ Orddict2 :: orddict(Key, Value).
erase(Key, [{K,_}=E|Dict]) when Key < K -> [E|Dict];
erase(Key, [{K,_}=E|Dict]) when Key > K ->
@@ -110,13 +107,11 @@ erase(_Key, [{_K,_Val}|Dict]) -> Dict; %Key == K
erase(_, []) -> [].
-spec store(Key, Value, Orddict1) -> Orddict2 when
- Key :: term(),
- Value :: term(),
- Orddict1 :: orddict(),
- Orddict2 :: orddict().
+ Orddict1 :: orddict(Key, Value),
+ Orddict2 :: orddict(Key, Value).
-store(Key, New, [{K,_}=E|Dict]) when Key < K ->
- [{Key,New},E|Dict];
+store(Key, New, [{K,_}|_]=Dict) when Key < K ->
+ [{Key,New}|Dict];
store(Key, New, [{K,_}=E|Dict]) when Key > K ->
[E|store(Key, New, Dict)];
store(Key, New, [{_K,_Old}|Dict]) -> %Key == K
@@ -124,13 +119,11 @@ store(Key, New, [{_K,_Old}|Dict]) -> %Key == K
store(Key, New, []) -> [{Key,New}].
-spec append(Key, Value, Orddict1) -> Orddict2 when
- Key :: term(),
- Value :: term(),
- Orddict1 :: orddict(),
- Orddict2 :: orddict().
+ Orddict1 :: orddict(Key, Value),
+ Orddict2 :: orddict(Key, Value).
-append(Key, New, [{K,_}=E|Dict]) when Key < K ->
- [{Key,[New]},E|Dict];
+append(Key, New, [{K,_}|_]=Dict) when Key < K ->
+ [{Key,[New]}|Dict];
append(Key, New, [{K,_}=E|Dict]) when Key > K ->
[E|append(Key, New, Dict)];
append(Key, New, [{_K,Old}|Dict]) -> %Key == K
@@ -138,13 +131,12 @@ append(Key, New, [{_K,Old}|Dict]) -> %Key == K
append(Key, New, []) -> [{Key,[New]}].
-spec append_list(Key, ValList, Orddict1) -> Orddict2 when
- Key :: term(),
- ValList :: [Value :: term()],
- Orddict1 :: orddict(),
- Orddict2 :: orddict().
+ ValList :: [Value],
+ Orddict1 :: orddict(Key, Value),
+ Orddict2 :: orddict(Key, Value).
-append_list(Key, NewList, [{K,_}=E|Dict]) when Key < K ->
- [{Key,NewList},E|Dict];
+append_list(Key, NewList, [{K,_}|_]=Dict) when Key < K ->
+ [{Key,NewList}|Dict];
append_list(Key, NewList, [{K,_}=E|Dict]) when Key > K ->
[E|append_list(Key, NewList, Dict)];
append_list(Key, NewList, [{_K,Old}|Dict]) -> %Key == K
@@ -153,10 +145,9 @@ append_list(Key, NewList, []) ->
[{Key,NewList}].
-spec update(Key, Fun, Orddict1) -> Orddict2 when
- Key :: term(),
- Fun :: fun((Value1 :: term()) -> Value2 :: term()),
- Orddict1 :: orddict(),
- Orddict2 :: orddict().
+ Fun :: fun((Value1 :: Value) -> Value2 :: Value),
+ Orddict1 :: orddict(Key, Value),
+ Orddict2 :: orddict(Key, Value).
update(Key, Fun, [{K,_}=E|Dict]) when Key > K ->
[E|update(Key, Fun, Dict)];
@@ -164,14 +155,13 @@ update(Key, Fun, [{K,Val}|Dict]) when Key == K ->
[{Key,Fun(Val)}|Dict].
-spec update(Key, Fun, Initial, Orddict1) -> Orddict2 when
- Key :: term(),
- Initial :: term(),
- Fun :: fun((Value1 :: term()) -> Value2 :: term()),
- Orddict1 :: orddict(),
- Orddict2 :: orddict().
-
-update(Key, _, Init, [{K,_}=E|Dict]) when Key < K ->
- [{Key,Init},E|Dict];
+ Initial :: Value,
+ Fun :: fun((Value1 :: Value) -> Value2 :: Value),
+ Orddict1 :: orddict(Key, Value),
+ Orddict2 :: orddict(Key, Value).
+
+update(Key, _, Init, [{K,_}|_]=Dict) when Key < K ->
+ [{Key,Init}|Dict];
update(Key, Fun, Init, [{K,_}=E|Dict]) when Key > K ->
[E|update(Key, Fun, Init, Dict)];
update(Key, Fun, _Init, [{_K,Val}|Dict]) -> %Key == K
@@ -179,13 +169,12 @@ update(Key, Fun, _Init, [{_K,Val}|Dict]) -> %Key == K
update(Key, _, Init, []) -> [{Key,Init}].
-spec update_counter(Key, Increment, Orddict1) -> Orddict2 when
- Key :: term(),
- Increment :: number(),
- Orddict1 :: orddict(),
- Orddict2 :: orddict().
+ Orddict1 :: orddict(Key, Value),
+ Orddict2 :: orddict(Key, Value),
+ Increment :: number().
-update_counter(Key, Incr, [{K,_}=E|Dict]) when Key < K ->
- [{Key,Incr},E|Dict];
+update_counter(Key, Incr, [{K,_}|_]=Dict) when Key < K ->
+ [{Key,Incr}|Dict];
update_counter(Key, Incr, [{K,_}=E|Dict]) when Key > K ->
[E|update_counter(Key, Incr, Dict)];
update_counter(Key, Incr, [{_K,Val}|Dict]) -> %Key == K
@@ -193,28 +182,30 @@ update_counter(Key, Incr, [{_K,Val}|Dict]) -> %Key == K
update_counter(Key, Incr, []) -> [{Key,Incr}].
-spec fold(Fun, Acc0, Orddict) -> Acc1 when
- Fun :: fun((Key :: term(), Value :: term(), AccIn :: term()) -> AccOut :: term()),
- Acc0 :: term(),
- Acc1 :: term(),
- Orddict :: orddict().
+ Fun :: fun((Key, Value, AccIn) -> AccOut),
+ Orddict :: orddict(Key, Value),
+ Acc0 :: Acc,
+ Acc1 :: Acc,
+ AccIn :: Acc,
+ AccOut :: Acc.
fold(F, Acc, [{Key,Val}|D]) ->
fold(F, F(Key, Val, Acc), D);
fold(F, Acc, []) when is_function(F, 3) -> Acc.
-spec map(Fun, Orddict1) -> Orddict2 when
- Fun :: fun((Key :: term(), Value1 :: term()) -> Value2 :: term()),
- Orddict1 :: orddict(),
- Orddict2 :: orddict().
+ Fun :: fun((Key, Value1) -> Value2),
+ Orddict1 :: orddict(Key, Value1),
+ Orddict2 :: orddict(Key, Value2).
map(F, [{Key,Val}|D]) ->
[{Key,F(Key, Val)}|map(F, D)];
map(F, []) when is_function(F, 2) -> [].
-spec filter(Pred, Orddict1) -> Orddict2 when
- Pred :: fun((Key :: term(), Value :: term()) -> boolean()),
- Orddict1 :: orddict(),
- Orddict2 :: orddict().
+ Pred :: fun((Key, Value) -> boolean()),
+ Orddict1 :: orddict(Key, Value),
+ Orddict2 :: orddict(Key, Value).
filter(F, [{Key,Val}=E|D]) ->
case F(Key, Val) of
@@ -224,10 +215,10 @@ filter(F, [{Key,Val}=E|D]) ->
filter(F, []) when is_function(F, 2) -> [].
-spec merge(Fun, Orddict1, Orddict2) -> Orddict3 when
- Fun :: fun((Key :: term(), Value1 :: term(), Value2 :: term()) -> Value :: term()),
- Orddict1 :: orddict(),
- Orddict2 :: orddict(),
- Orddict3 :: orddict().
+ Fun :: fun((Key, Value1, Value2) -> Value),
+ Orddict1 :: orddict(Key, Value1),
+ Orddict2 :: orddict(Key, Value2),
+ Orddict3 :: orddict(Key, Value).
merge(F, [{K1,_}=E1|D1], [{K2,_}=E2|D2]) when K1 < K2 ->
[E1|merge(F, D1, [E2|D2])];
diff --git a/lib/stdlib/src/ordsets.erl b/lib/stdlib/src/ordsets.erl
index 4a8b1275b2..569407f5ef 100644
--- a/lib/stdlib/src/ordsets.erl
+++ b/lib/stdlib/src/ordsets.erl
@@ -1,21 +1,21 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
-%%
-module(ordsets).
diff --git a/lib/stdlib/src/otp_internal.erl b/lib/stdlib/src/otp_internal.erl
index 6c25beabe9..7a59523f06 100644
--- a/lib/stdlib/src/otp_internal.erl
+++ b/lib/stdlib/src/otp_internal.erl
@@ -1,32 +1,35 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2014. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
-module(otp_internal).
--export([obsolete/3]).
+-export([obsolete/3, obsolete_type/3]).
%%----------------------------------------------------------------------
+-dialyzer({no_match, obsolete/3}).
+
-type tag() :: 'deprecated' | 'removed'. %% | 'experimental'.
-type mfas() :: mfa() | {atom(), atom(), [byte()]}.
-type release() :: string().
--spec obsolete(atom(), atom(), byte()) ->
+-spec obsolete(module(), atom(), arity()) ->
'no' | {tag(), string()} | {tag(), mfas(), release()}.
obsolete(Module, Name, Arity) ->
@@ -44,29 +47,23 @@ obsolete(Module, Name, Arity) ->
obsolete_1(net, _, _) ->
{deprecated, "module 'net' obsolete; use 'net_adm'"};
-obsolete_1(erl_internal, builtins, 0) ->
- {deprecated, {erl_internal, bif, 2}};
-
-obsolete_1(erl_eval, seq, 2) ->
- {deprecated, {erl_eval, exprs, 2}};
-obsolete_1(erl_eval, seq, 3) ->
- {deprecated, {erl_eval, exprs, 3}};
-obsolete_1(erl_eval, arg_list, 2) ->
- {deprecated, {erl_eval, expr_list, 2}};
-obsolete_1(erl_eval, arg_list, 3) ->
- {deprecated, {erl_eval, expr_list, 3}};
-
obsolete_1(erlang, hash, 2) ->
{deprecated, {erlang, phash2, 2}};
+obsolete_1(erlang, now, 0) ->
+ {deprecated,
+ "Deprecated BIF. See the \"Time and Time Correction in Erlang\" "
+ "chapter of the ERTS User's Guide for more information."};
+
obsolete_1(calendar, local_time_to_universal_time, 1) ->
{deprecated, {calendar, local_time_to_universal_time_dst, 1}};
-obsolete_1(rpc, safe_multi_server_call, A) when A =:= 2; A =:= 3 ->
- {deprecated, {rpc, multi_server_call, A}};
+%% *** CRYPTO added in OTP 19 ***
+obsolete_1(crypto, rand_bytes, 1) ->
+ {deprecated, {crypto, strong_rand_bytes, 1}};
-%% *** CRYPTO add in R16B01 ***
+%% *** CRYPTO added in R16B01 ***
obsolete_1(crypto, md4, 1) ->
{deprecated, {crypto, hash, 2}};
@@ -383,106 +380,10 @@ obsolete_1(http, cookie_header, 2) -> {removed,{httpc,cookie_header,2},"R15B"
obsolete_1(http, stream_next, 1) -> {removed,{httpc,stream_next,1},"R15B"};
obsolete_1(http, default_profile, 0) -> {removed,{httpc,default_profile,0},"R15B"};
-obsolete_1(httpd, start, 0) -> {removed,{inets,start,[2,3]},"R14B"};
-obsolete_1(httpd, start, 1) -> {removed,{inets,start,[2,3]},"R14B"};
-obsolete_1(httpd, start_link, 0) -> {removed,{inets,start,[2,3]},"R14B"};
-obsolete_1(httpd, start_link, 1) -> {removed,{inets,start,[2,3]},"R14B"};
-obsolete_1(httpd, start_child, 0) -> {removed,{inets,start,[2,3]},"R14B"};
-obsolete_1(httpd, start_child, 1) -> {removed,{inets,start,[2,3]},"R14B"};
-obsolete_1(httpd, stop, 0) -> {removed,{inets,stop,2},"R14B"};
-obsolete_1(httpd, stop, 1) -> {removed,{inets,stop,2},"R14B"};
-obsolete_1(httpd, stop, 2) -> {removed,{inets,stop,2},"R14B"};
-obsolete_1(httpd, stop_child, 0) -> {removed,{inets,stop,2},"R14B"};
-obsolete_1(httpd, stop_child, 1) -> {removed,{inets,stop,2},"R14B"};
-obsolete_1(httpd, stop_child, 2) -> {removed,{inets,stop,2},"R14B"};
-obsolete_1(httpd, restart, 0) -> {removed,{httpd,reload_config,2},"R14B"};
-obsolete_1(httpd, restart, 1) -> {removed,{httpd,reload_config,2},"R14B"};
-obsolete_1(httpd, restart, 2) -> {removed,{httpd,reload_config,2},"R14B"};
-obsolete_1(httpd, block, 0) -> {removed,{httpd,reload_config,2},"R14B"};
-obsolete_1(httpd, block, 1) -> {removed,{httpd,reload_config,2},"R14B"};
-obsolete_1(httpd, block, 2) -> {removed,{httpd,reload_config,2},"R14B"};
-obsolete_1(httpd, block, 3) -> {removed,{httpd,reload_config,2},"R14B"};
-obsolete_1(httpd, block, 4) -> {removed,{httpd,reload_config,2},"R14B"};
-obsolete_1(httpd, unblock, 0) -> {removed,{httpd,reload_config,2},"R14B"};
-obsolete_1(httpd, unblock, 1) -> {removed,{httpd,reload_config,2},"R14B"};
-obsolete_1(httpd, unblock, 2) -> {removed,{httpd,reload_config,2},"R14B"};
-obsolete_1(httpd_util, key1search, 2) -> {removed,{proplists,get_value,2},"R13B"};
-obsolete_1(httpd_util, key1search, 3) -> {removed,{proplists,get_value,3},"R13B"};
-obsolete_1(ftp, open, 3) -> {removed,{inets,start,[2,3]},"R14B"};
-obsolete_1(ftp, force_active, 1) -> {removed,{inets,start,[2,3]},"R14B"};
-
-%% Added in R12B-4.
-obsolete_1(ssh_cm, connect, A) when 1 =< A, A =< 3 ->
- {removed,{ssh,connect,A},"R14B"};
-obsolete_1(ssh_cm, listen, A) when 2 =< A, A =< 4 ->
- {removed,{ssh,daemon,A},"R14B"};
-obsolete_1(ssh_cm, stop_listener, 1) ->
- {removed,{ssh,stop_listener,[1,2]},"R14B"};
-obsolete_1(ssh_cm, session_open, A) when A =:= 2; A =:= 4 ->
- {removed,{ssh_connection,session_channel,A},"R14B"};
-obsolete_1(ssh_cm, direct_tcpip, A) when A =:= 6; A =:= 8 ->
- {removed,{ssh_connection,direct_tcpip,A},"R14B"};
-obsolete_1(ssh_cm, tcpip_forward, 3) ->
- {removed,{ssh_connection,tcpip_forward,3},"R14B"};
-obsolete_1(ssh_cm, cancel_tcpip_forward, 3) ->
- {removed,{ssh_connection,cancel_tcpip_forward,3},"R14B"};
-obsolete_1(ssh_cm, open_pty, A) when A =:= 3; A =:= 7; A =:= 9 ->
- {removed,{ssh_connection,open_pty,A},"R14B"};
-obsolete_1(ssh_cm, setenv, 5) ->
- {removed,{ssh_connection,setenv,5},"R14B"};
-obsolete_1(ssh_cm, shell, 2) ->
- {removed,{ssh_connection,shell,2},"R14B"};
-obsolete_1(ssh_cm, exec, 4) ->
- {removed,{ssh_connection,exec,4},"R14B"};
-obsolete_1(ssh_cm, subsystem, 4) ->
- {removed,{ssh_connection,subsystem,4},"R14B"};
-obsolete_1(ssh_cm, winch, A) when A =:= 4; A =:= 6 ->
- {removed,{ssh_connection,window_change,A},"R14B"};
-obsolete_1(ssh_cm, signal, 3) ->
- {removed,{ssh_connection,signal,3},"R14B"};
-obsolete_1(ssh_cm, attach, A) when A =:= 2; A =:= 3 ->
- {removed,"no longer useful; removed in R14B"};
-obsolete_1(ssh_cm, detach, 2) ->
- {removed,"no longer useful; removed in R14B"};
-obsolete_1(ssh_cm, set_user_ack, 4) ->
- {removed,"no longer useful; removed in R14B"};
-obsolete_1(ssh_cm, adjust_window, 3) ->
- {removed,{ssh_connection,adjust_window,3},"R14B"};
-obsolete_1(ssh_cm, close, 2) ->
- {removed,{ssh_connection,close,2},"R14B"};
-obsolete_1(ssh_cm, stop, 1) ->
- {removed,{ssh,close,1},"R14B"};
-obsolete_1(ssh_cm, send_eof, 2) ->
- {removed,{ssh_connection,send_eof,2},"R14B"};
-obsolete_1(ssh_cm, send, A) when A =:= 3; A =:= 4 ->
- {removed,{ssh_connection,send,A},"R14B"};
-obsolete_1(ssh_cm, send_ack, A) when 3 =< A, A =< 5 ->
- {removed,{ssh_connection,send,[3,4]},"R14B"};
-obsolete_1(ssh_ssh, connect, A) when 1 =< A, A =< 3 ->
- {removed,{ssh,shell,A},"R14B"};
-obsolete_1(ssh_sshd, listen, A) when 0 =< A, A =< 3 ->
- {removed,{ssh,daemon,[1,2,3]},"R14B"};
-obsolete_1(ssh_sshd, stop, 1) ->
- {removed,{ssh,stop_listener,1},"R14B"};
-
%% Added in R13A.
obsolete_1(regexp, _, _) ->
{removed, "removed in R15; use the re module instead"};
-obsolete_1(lists, flat_length, 1) ->
- {removed,{lists,flatlength,1},"R14"};
-
-obsolete_1(ssh_sftp, connect, A) when 1 =< A, A =< 3 ->
- {removed,{ssh_sftp,start_channel,A},"R14B"};
-obsolete_1(ssh_sftp, stop, 1) ->
- {removed,{ssh_sftp,stop_channel,1},"R14B"};
-
-%% Added in R13B01.
-obsolete_1(ssl_pkix, decode_cert_file, A) when A =:= 1; A =:= 2 ->
- {removed,"removed in R14A; use public_key:pem_to_der/1 and public_key:pkix_decode_cert/2 instead"};
-obsolete_1(ssl_pkix, decode_cert, A) when A =:= 1; A =:= 2 ->
- {removed,{public_key,pkix_decode_cert,2},"R14A"};
-
%% Added in R13B04.
obsolete_1(erlang, concat_binary, 1) ->
{removed,{erlang,list_to_binary,1},"R15B"};
@@ -577,8 +478,76 @@ obsolete_1(asn1rt, utf8_binary_to_list, 1) ->
{deprecated,{unicode,characters_to_list,1}};
obsolete_1(asn1rt, utf8_list_to_binary, 1) ->
{deprecated,{unicode,characters_to_binary,1}};
-obsolete_1(pg, _, _) ->
- {deprecated,"deprecated; will be removed in OTP 18"};
+
+%% Added in OTP 18.
+obsolete_1(core_lib, get_anno, 1) ->
+ {removed,{cerl,get_ann,1},"19"};
+obsolete_1(core_lib, set_anno, 2) ->
+ {removed,{cerl,set_ann,2},"19"};
+obsolete_1(core_lib, is_literal, 1) ->
+ {removed,{cerl,is_literal,1},"19"};
+obsolete_1(core_lib, is_literal_list, 1) ->
+ {removed,"removed; use lists:all(fun cerl:is_literal/1, L)"
+ " instead"};
+obsolete_1(core_lib, literal_value, 1) ->
+ {removed,{core_lib,concrete,1},"19"};
+obsolete_1(erl_scan, set_attribute, 3) ->
+ {removed,{erl_anno,set_line,2},"19.0"};
+obsolete_1(erl_scan, attributes_info, 1) ->
+ {removed,"removed in 19.0; use "
+ "erl_anno:{column,line,location,text}/1 instead"};
+obsolete_1(erl_scan, attributes_info, 2) ->
+ {removed,"removed in 19.0; use "
+ "erl_anno:{column,line,location,text}/1 instead"};
+obsolete_1(erl_scan, token_info, 1) ->
+ {removed,"removed in 19.0; use "
+ "erl_scan:{category,column,line,location,symbol,text}/1 instead"};
+obsolete_1(erl_scan, token_info, 2) ->
+ {removed,"removed in 19.0; use "
+ "erl_scan:{category,column,line,location,symbol,text}/1 instead"};
+obsolete_1(erl_parse, set_line, 2) ->
+ {removed,{erl_anno,set_line,2},"19.0"};
+obsolete_1(erl_parse, get_attributes, 1) ->
+ {removed,"removed in 19.0; use "
+ "erl_anno:{column,line,location,text}/1 instead"};
+obsolete_1(erl_parse, get_attribute, 2) ->
+ {removed,"removed in 19.0; use "
+ "erl_anno:{column,line,location,text}/1 instead"};
+obsolete_1(erl_lint, modify_line, 2) ->
+ {removed,{erl_parse,map_anno,2},"19.0"};
+obsolete_1(ssl, negotiated_next_protocol, 1) ->
+ {deprecated,{ssl,negotiated_protocol,1}};
+
+obsolete_1(ssl, connection_info, 1) ->
+ {deprecated, "deprecated; use connection_information/[1,2] instead"};
+
+obsolete_1(httpd_conf, check_enum, 2) ->
+ {deprecated, "deprecated; use lists:member/2 instead"};
+obsolete_1(httpd_conf, clean, 1) ->
+ {deprecated, "deprecated; use sting:strip/1 instead or possible the re module"};
+obsolete_1(httpd_conf, custom_clean, 3) ->
+ {deprecated, "deprecated; use sting:strip/3 instead or possible the re module"};
+obsolete_1(httpd_conf, is_directory, 1) ->
+ {deprecated, "deprecated; use filelib:is_dir/1 instead"};
+obsolete_1(httpd_conf, is_file, 1) ->
+ {deprecated, "deprecated; use filelib:is_file/1 instead"};
+obsolete_1(httpd_conf, make_integer, 1) ->
+ {deprecated, "deprecated; use erlang:list_to_integer/1 instead"};
+
+%% Added in OTP 19.
+
+obsolete_1(random, _, _) ->
+ {deprecated, "the 'random' module is deprecated; "
+ "use the 'rand' module instead"};
+obsolete_1(code, rehash, 0) ->
+ {deprecated, "deprecated because the code path cache feature has been removed"};
+
+%% Removed in OTP 19.
+
+obsolete_1(overload, _, _) ->
+ {removed, "removed in OTP 19"};
+obsolete_1(rpc, safe_multi_server_call, A) when A =:= 2; A =:= 3 ->
+ {removed, {rpc, multi_server_call, A}};
obsolete_1(_, _, _) ->
no.
@@ -626,3 +595,30 @@ is_snmp_agent_function(add_agent_caps, 2) -> true;
is_snmp_agent_function(del_agent_caps, 1) -> true;
is_snmp_agent_function(get_agent_caps, 0) -> true;
is_snmp_agent_function(_, _) -> false.
+
+-dialyzer({no_match, obsolete_type/3}).
+
+-spec obsolete_type(module(), atom(), arity()) ->
+ 'no' | {tag(), string()} | {tag(), mfas(), release()}.
+
+-dialyzer({no_match, obsolete_type/3}).
+obsolete_type(Module, Name, NumberOfVariables) ->
+ case obsolete_type_1(Module, Name, NumberOfVariables) of
+ {deprecated=Tag,{_,_,_}=Replacement} ->
+ {Tag,Replacement,"in a future release"};
+ {_,String}=Ret when is_list(String) ->
+ Ret;
+ {_,_,_}=Ret ->
+ Ret;
+ no ->
+ no
+ end.
+
+obsolete_type_1(erl_scan,column,0) ->
+ {removed,{erl_anno,column,0},"19.0"};
+obsolete_type_1(erl_scan,line,0) ->
+ {removed,{erl_anno,line,0},"19.0"};
+obsolete_type_1(erl_scan,location,0) ->
+ {removed,{erl_anno,location,0},"19.0"};
+obsolete_type_1(_,_,_) ->
+ no.
diff --git a/lib/stdlib/src/pg.erl b/lib/stdlib/src/pg.erl
deleted file mode 100644
index a41fd329c2..0000000000
--- a/lib/stdlib/src/pg.erl
+++ /dev/null
@@ -1,187 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
--module(pg).
--deprecated(module).
-
-%% pg provides a process group facility. Messages
-%% can be multicasted to all members in the group
-
--export([create/1,
- create/2,
- standby/2,
- join/2,
- send/2,
- esend/2,
- members/1,
- name_to_pid/1,
- master/1]).
-
-
-%% Create a brand new empty process group with the master residing
-%% at the local node
-
--spec create(PgName) -> 'ok' | {'error', Reason} when
- PgName :: term(),
- Reason :: 'already_created' | term().
-
-create(PgName) ->
- catch begin check(PgName),
- Pid = spawn(pg,master,[PgName]),
- global:register_name(PgName,Pid),
- ok end.
-
-%% Create a brand new empty process group with the master
-%% residing at Node
-
--spec create(PgName, Node) -> 'ok' | {'error', Reason} when
- PgName :: term(),
- Node :: node(),
- Reason :: 'already_created' | term().
-
-create(PgName, Node) ->
- catch begin check(PgName),
- Pid = spawn(Node,pg,master,[PgName]),
- global:register_name(PgName,Pid),
- ok end.
-
-%% Have a process on Node that will act as a standby for the process
-%% group manager. So if the node where the manager runs fails, the
-%% process group will continue to function.
-
--spec standby(term(), node()) -> 'ok'.
-
-standby(_PgName, _Node) ->
- ok.
-
-%% Tell process group PgName that Pid is a new member of the group
-%% synchronously return a list of all old members in the group
-
--spec join(PgName, Pid) -> Members when
- PgName :: term(),
- Pid :: pid(),
- Members :: [pid()].
-
-join(PgName, Pid) when is_atom(PgName) ->
- global:send(PgName, {join,self(),Pid}),
- receive
- {_P,{members,Members}} ->
- Members
- end.
-
-%% Multi cast Mess to all members in the group
-
--spec send(PgName, Msg) -> 'ok' when
- PgName :: term(),
- Msg :: term().
-
-send(PgName, Mess) when is_atom(PgName) ->
- global:send(PgName, {send, self(), Mess}),
- ok;
-send(Pg, Mess) when is_pid(Pg) ->
- Pg ! {send,self(),Mess},
- ok.
-
-%% multi cast a message to all members in the group but ourselves
-%% If we are a member
-
--spec esend(PgName, Msg) -> 'ok' when
- PgName :: term(),
- Msg :: term().
-
-esend(PgName, Mess) when is_atom(PgName) ->
- global:send(PgName, {esend,self(),Mess}),
- ok;
-esend(Pg, Mess) when is_pid(Pg) ->
- Pg ! {esend,self(),Mess},
- ok.
-
-%% Return the members of the group
-
--spec members(PgName) -> Members when
- PgName :: term(),
- Members :: [pid()].
-
-members(PgName) when is_atom(PgName) ->
- global:send(PgName, {self() ,members}),
- receive
- {_P,{members,Members}} ->
- Members
- end;
-members(Pg) when is_pid(Pg) ->
- Pg ! {self,members},
- receive
- {_P,{members,Members}} ->
- Members
- end.
-
--spec name_to_pid(atom()) -> pid() | 'undefined'.
-
-name_to_pid(PgName) when is_atom(PgName) ->
- global:whereis_name(PgName).
-
--spec master(term()) -> no_return().
-
-master(PgName) ->
- process_flag(trap_exit, true),
- master_loop(PgName, []).
-
-master_loop(PgName,Members) ->
- receive
- {send,From,Message} ->
- send_all(Members,{pg_message,From,PgName,Message}),
- master_loop(PgName,Members);
- {esend,From,Message} ->
- send_all(lists:delete(From,Members),
- {pg_message,From,PgName,Message}),
- master_loop(PgName,Members);
- {join,From,Pid} ->
- link(Pid),
- send_all(Members,{new_member,PgName,Pid}),
- From ! {self(),{members,Members}},
- master_loop(PgName,[Pid|Members]);
- {From,members} ->
- From ! {self(),{members,Members}},
- master_loop(PgName,Members);
- {'EXIT',From,_} ->
- L =
- case lists:member(From,Members) of
- true ->
- NewMembers = lists:delete(From,Members),
- send_all(NewMembers, {crashed_member,PgName,From}),
- NewMembers;
- false ->
- Members
- end,
- master_loop(PgName,L)
- end.
-
-send_all([], _) -> ok;
-send_all([P|Ps], M) ->
- P ! M,
- send_all(Ps, M).
-
-%% Check if the process group already exists
-
-check(PgName) ->
- case global:whereis_name(PgName) of
- Pid when is_pid(Pid) ->
- throw({error,already_created});
- undefined ->
- ok
- end.
diff --git a/lib/stdlib/src/pool.erl b/lib/stdlib/src/pool.erl
index dfe6318dea..05950a1d7c 100644
--- a/lib/stdlib/src/pool.erl
+++ b/lib/stdlib/src/pool.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
diff --git a/lib/stdlib/src/proc_lib.erl b/lib/stdlib/src/proc_lib.erl
index bf2a4e7ac5..fdc2ae4070 100644
--- a/lib/stdlib/src/proc_lib.erl
+++ b/lib/stdlib/src/proc_lib.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -29,8 +30,10 @@
start/3, start/4, start/5, start_link/3, start_link/4, start_link/5,
hibernate/3,
init_ack/1, init_ack/2,
- init_p/3,init_p/5,format/1,format/2,initial_call/1,
- translate_initial_call/1]).
+ init_p/3,init_p/5,format/1,format/2,format/3,
+ initial_call/1,
+ translate_initial_call/1,
+ stop/1, stop/3]).
%% Internal exports.
-export([wake_up/3]).
@@ -45,7 +48,9 @@
| {'priority', priority_level()}
| {'min_heap_size', non_neg_integer()}
| {'min_bin_vheap_size', non_neg_integer()}
- | {'fullsweep_after', non_neg_integer()}.
+ | {'fullsweep_after', non_neg_integer()}
+ | {'message_queue_data',
+ 'off_heap' | 'on_heap' | 'mixed' }.
-type dict_or_pid() :: pid()
| (ProcInfo :: [_])
@@ -469,13 +474,15 @@ trans_init(gen,init_it,[gen_server,_,_,supervisor_bridge,[Module|_],_]) ->
{supervisor_bridge,Module,1};
trans_init(gen,init_it,[gen_server,_,_,_,supervisor_bridge,[Module|_],_]) ->
{supervisor_bridge,Module,1};
-trans_init(gen,init_it,[gen_server,_,_,Module,_,_]) ->
- {Module,init,1};
-trans_init(gen,init_it,[gen_server,_,_,_,Module|_]) ->
- {Module,init,1};
-trans_init(gen,init_it,[gen_fsm,_,_,Module,_,_]) ->
+trans_init(gen,init_it,[GenMod,_,_,Module,_,_])
+ when GenMod =:= gen_server;
+ GenMod =:= gen_statem;
+ GenMod =:= gen_fsm ->
{Module,init,1};
-trans_init(gen,init_it,[gen_fsm,_,_,_,Module|_]) ->
+trans_init(gen,init_it,[GenMod,_,_,_,Module|_])
+ when GenMod =:= gen_server;
+ GenMod =:= gen_statem;
+ GenMod =:= gen_fsm ->
{Module,init,1};
trans_init(gen,init_it,[gen_event|_]) ->
{gen_event,init_it,6};
@@ -698,53 +705,118 @@ format(CrashReport) ->
CrashReport :: [term()],
Encoding :: latin1 | unicode | utf8.
-format([OwnReport,LinkReport], Encoding) ->
- OwnFormat = format_report(OwnReport, Encoding),
- LinkFormat = format_report(LinkReport, Encoding),
+format(CrashReport, Encoding) ->
+ format(CrashReport, Encoding, unlimited).
+
+-spec format(CrashReport, Encoding, Depth) -> string() when
+ CrashReport :: [term()],
+ Encoding :: latin1 | unicode | utf8,
+ Depth :: unlimited | pos_integer().
+
+format([OwnReport,LinkReport], Encoding, Depth) ->
+ Extra = {Encoding,Depth},
+ OwnFormat = format_report(OwnReport, Extra),
+ LinkFormat = format_report(LinkReport, Extra),
Str = io_lib:format(" crasher:~n~ts neighbours:~n~ts",
[OwnFormat, LinkFormat]),
lists:flatten(Str).
-format_report(Rep, Enc) when is_list(Rep) ->
- format_rep(Rep,Enc);
-format_report(Rep, Enc) ->
+format_report(Rep, Extra) when is_list(Rep) ->
+ format_rep(Rep, Extra);
+format_report(Rep, {Enc,_}) ->
io_lib:format("~"++modifier(Enc)++"p~n", [Rep]).
-format_rep([{initial_call,InitialCall}|Rep], Enc) ->
- [format_mfa(InitialCall)|format_rep(Rep, Enc)];
-format_rep([{error_info,{Class,Reason,StackTrace}}|Rep], Enc) ->
- [format_exception(Class, Reason, StackTrace, Enc)|format_rep(Rep, Enc)];
-format_rep([{Tag,Data}|Rep], Enc) ->
- [format_tag(Tag, Data)|format_rep(Rep, Enc)];
-format_rep(_, _Enc) ->
+format_rep([{initial_call,InitialCall}|Rep], {_Enc,Depth}=Extra) ->
+ [format_mfa(InitialCall, Depth)|format_rep(Rep, Extra)];
+format_rep([{error_info,{Class,Reason,StackTrace}}|Rep], Extra) ->
+ [format_exception(Class, Reason, StackTrace, Extra)|format_rep(Rep, Extra)];
+format_rep([{Tag,Data}|Rep], Extra) ->
+ [format_tag(Tag, Data, Extra)|format_rep(Rep, Extra)];
+format_rep(_, _Extra) ->
[].
-format_exception(Class, Reason, StackTrace, Enc) ->
- PF = pp_fun(Enc),
+format_exception(Class, Reason, StackTrace, {Enc,_}=Extra) ->
+ PF = pp_fun(Extra),
StackFun = fun(M, _F, _A) -> (M =:= erl_eval) or (M =:= ?MODULE) end,
%% EI = " exception: ",
EI = " ",
[EI, lib:format_exception(1+length(EI), Class, Reason,
StackTrace, StackFun, PF, Enc), "\n"].
-format_mfa({M,F,Args}=StartF) ->
+format_mfa({M,F,Args}=StartF, Depth) ->
try
A = length(Args),
[" initial call: ",atom_to_list(M),$:,atom_to_list(F),$/,
integer_to_list(A),"\n"]
catch
error:_ ->
- format_tag(initial_call, StartF)
+ format_tag(initial_call, StartF, Depth)
end.
-pp_fun(Enc) ->
- P = modifier(Enc) ++ "p",
+pp_fun({Enc,Depth}) ->
+ {Letter,Tl} = case Depth of
+ unlimited -> {"p",[]};
+ _ -> {"P",[Depth]}
+ end,
+ P = modifier(Enc) ++ Letter,
fun(Term, I) ->
- io_lib:format("~." ++ integer_to_list(I) ++ P, [Term])
+ io_lib:format("~." ++ integer_to_list(I) ++ P, [Term|Tl])
end.
-format_tag(Tag, Data) ->
- io_lib:format(" ~p: ~80.18p~n", [Tag, Data]).
+format_tag(Tag, Data, {_Enc,Depth}) ->
+ case Depth of
+ unlimited ->
+ io_lib:format(" ~p: ~80.18p~n", [Tag, Data]);
+ _ ->
+ io_lib:format(" ~p: ~80.18P~n", [Tag, Data, Depth])
+ end.
modifier(latin1) -> "";
modifier(_) -> "t".
+
+
+%%% -----------------------------------------------------------
+%%% Stop a process and wait for it to terminate
+%%% -----------------------------------------------------------
+-spec stop(Process) -> 'ok' when
+ Process :: pid() | RegName | {RegName,node()},
+ RegName :: atom().
+stop(Process) ->
+ stop(Process, normal, infinity).
+
+-spec stop(Process, Reason, Timeout) -> 'ok' when
+ Process :: pid() | RegName | {RegName,node()},
+ RegName :: atom(),
+ Reason :: term(),
+ Timeout :: timeout().
+stop(Process, Reason, Timeout) ->
+ {Pid, Mref} = erlang:spawn_monitor(do_stop(Process, Reason)),
+ receive
+ {'DOWN', Mref, _, _, Reason} ->
+ ok;
+ {'DOWN', Mref, _, _, {noproc,{sys,terminate,_}}} ->
+ exit(noproc);
+ {'DOWN', Mref, _, _, CrashReason} ->
+ exit(CrashReason)
+ after Timeout ->
+ exit(Pid, kill),
+ receive
+ {'DOWN', Mref, _, _, _} ->
+ exit(timeout)
+ end
+ end.
+
+-spec do_stop(Process, Reason) -> Fun when
+ Process :: pid() | RegName | {RegName,node()},
+ RegName :: atom(),
+ Reason :: term(),
+ Fun :: fun(() -> no_return()).
+do_stop(Process, Reason) ->
+ fun() ->
+ Mref = erlang:monitor(process, Process),
+ ok = sys:terminate(Process, Reason, infinity),
+ receive
+ {'DOWN', Mref, _, _, ExitReason} ->
+ exit(ExitReason)
+ end
+ end.
diff --git a/lib/stdlib/src/proplists.erl b/lib/stdlib/src/proplists.erl
index 634724019f..8e99ec0ed9 100644
--- a/lib/stdlib/src/proplists.erl
+++ b/lib/stdlib/src/proplists.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2013. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Copyright Ericsson AB 2001-2016. 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%
%%
diff --git a/lib/stdlib/src/qlc.erl b/lib/stdlib/src/qlc.erl
index 002032d48d..b396ba7057 100644
--- a/lib/stdlib/src/qlc.erl
+++ b/lib/stdlib/src/qlc.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -50,6 +51,8 @@
-export([template_state/0, aux_name/3, name_suffix/2, vars/1,
var_ufold/2, var_fold/3, all_selections/1]).
+-dialyzer(no_improper_lists).
+
%% When cache=list lists bigger than ?MAX_LIST_SIZE bytes are put on
%% file. Also used when merge join finds big equivalence classes.
-define(MAX_LIST_SIZE, 512*1024).
@@ -807,21 +810,21 @@ options(Options0, [Key | Keys], L) when is_list(Options0) ->
{ok, U};
{pre_fun, U=undefined} ->
{ok, U};
- {info_fun, Fun} when is_function(Fun), is_function(Fun, 1) ->
+ {info_fun, Fun} when is_function(Fun, 1) ->
{ok, Fun};
- {pre_fun, Fun} when is_function(Fun), is_function(Fun, 1) ->
+ {pre_fun, Fun} when is_function(Fun, 1) ->
{ok, Fun};
- {post_fun, Fun} when is_function(Fun), is_function(Fun, 0) ->
+ {post_fun, Fun} when is_function(Fun, 0) ->
{ok, Fun};
- {lookup_fun, Fun} when is_function(Fun), is_function(Fun, 2) ->
+ {lookup_fun, Fun} when is_function(Fun, 2) ->
{ok, Fun};
{max_lookup, Max} when is_integer(Max), Max >= 0 ->
{ok, Max};
{max_lookup, infinity} ->
{ok, -1};
- {format_fun, Fun} when is_function(Fun), is_function(Fun, 1) ->
+ {format_fun, Fun} when is_function(Fun, 1) ->
{ok, Fun};
- {parent_fun, Fun} when is_function(Fun), is_function(Fun, 0) ->
+ {parent_fun, Fun} when is_function(Fun, 0) ->
{ok, Fun};
{key_equality, KE='=='} ->
{ok, KE};
@@ -884,7 +887,7 @@ options(Options0, [Key | Keys], L) when is_list(Options0) ->
{depth, Depth} when Depth =:= infinity;
is_integer(Depth), Depth >= 0 ->
{ok, Depth};
- {order, Order} when is_function(Order), is_function(Order, 2);
+ {order, Order} when is_function(Order, 2);
(Order =:= ascending);
(Order =:= descending) ->
{ok, Order};
@@ -1006,7 +1009,7 @@ listify(T) ->
-record(simple_qlc,
{p, % atom(), pattern variable
le,
- line,
+ line :: erl_anno:anno(),
init_value,
optz % #optz
}).
@@ -1148,15 +1151,18 @@ abstract(Info, true=_Flat, NElements, Depth) ->
[{match,_,Expr,Q}] ->
Q;
[{match,_,Expr,Q} | Body] ->
- {block, 0, lists:reverse(Body, [Q])};
+ {block, anno0(), lists:reverse(Body, [Q])};
_ ->
- {block, 0, lists:reverse(Body0, [Expr])}
+ {block, anno0(), lists:reverse(Body0, [Expr])}
end.
-abstract({qlc, E0, Qs0, Opt}, NElements, Depth) ->
+abstract(Info, NElements, Depth) ->
+ abstract1(Info, NElements, Depth, anno1()).
+
+abstract1({qlc, E0, Qs0, Opt}, NElements, Depth, A) ->
Qs = lists:map(fun({generate, P, LE}) ->
- {generate, 1, binary_to_term(P),
- abstract(LE, NElements, Depth)};
+ {generate, A, binary_to_term(P),
+ abstract1(LE, NElements, Depth, A)};
(F) ->
binary_to_term(F)
end, Qs0),
@@ -1165,12 +1171,12 @@ abstract({qlc, E0, Qs0, Opt}, NElements, Depth) ->
[] -> [];
_ -> [abstract_term(Opt, 1)]
end,
- ?QLC_Q(1, 1, 1, 1, {lc,1,E,Qs}, Os);
-abstract({table, {M, F, As0}}, _NElements, _Depth)
+ ?QLC_Q(A, A, A, A, {lc,A,E,Qs}, Os);
+abstract1({table, {M, F, As0}}, _NElements, _Depth, Anno)
when is_atom(M), is_atom(F), is_list(As0) ->
As = [abstract_term(A, 1) || A <- As0],
- {call, 1, {remote, 1, {atom, 1, M}, {atom, 1, F}}, As};
-abstract({table, TableDesc}, _NElements, _Depth) ->
+ {call, Anno, {remote, Anno, {atom, Anno, M}, {atom, Anno, F}}, As};
+abstract1({table, TableDesc}, _NElements, _Depth, _A) ->
case io_lib:deep_char_list(TableDesc) of
true ->
{ok, Tokens, _} = erl_scan:string(lists:flatten(TableDesc++".")),
@@ -1179,27 +1185,28 @@ abstract({table, TableDesc}, _NElements, _Depth) ->
false -> % abstract expression
TableDesc
end;
-abstract({append, Infos}, NElements, Depth) ->
+abstract1({append, Infos}, NElements, Depth, A) ->
As = lists:foldr(fun(Info, As0) ->
- {cons,1,abstract(Info, NElements, Depth),As0}
- end, {nil, 1}, Infos),
- {call, 1, {remote, 1, {atom, 1, ?MODULE}, {atom, 1, append}}, [As]};
-abstract({sort, Info, SortOptions}, NElements, Depth) ->
- {call, 1, {remote, 1, {atom, 1, ?MODULE}, {atom, 1, sort}},
- [abstract(Info, NElements, Depth), abstract_term(SortOptions, 1)]};
-abstract({keysort, Info, Kp, SortOptions}, NElements, Depth) ->
- {call, 1, {remote, 1, {atom, 1, ?MODULE}, {atom, 1, keysort}},
- [abstract_term(Kp, 1), abstract(Info, NElements, Depth),
+ {cons,A,abstract1(Info, NElements, Depth, A),
+ As0}
+ end, {nil, A}, Infos),
+ {call, A, {remote, A, {atom, A, ?MODULE}, {atom, A, append}}, [As]};
+abstract1({sort, Info, SortOptions}, NElements, Depth, A) ->
+ {call, A, {remote, A, {atom, A, ?MODULE}, {atom, A, sort}},
+ [abstract1(Info, NElements, Depth, A), abstract_term(SortOptions, 1)]};
+abstract1({keysort, Info, Kp, SortOptions}, NElements, Depth, A) ->
+ {call, A, {remote, A, {atom, A, ?MODULE}, {atom, A, keysort}},
+ [abstract_term(Kp, 1), abstract1(Info, NElements, Depth, A),
abstract_term(SortOptions, 1)]};
-abstract({list,L,MS}, NElements, Depth) ->
- {call, 1, {remote, 1, {atom, 1, ets}, {atom, 1, match_spec_run}},
- [abstract(L, NElements, Depth),
- {call, 1, {remote, 1, {atom, 1, ets}, {atom, 1, match_spec_compile}},
+abstract1({list,L,MS}, NElements, Depth, A) ->
+ {call, A, {remote, A, {atom, A, ets}, {atom, A, match_spec_run}},
+ [abstract1(L, NElements, Depth, A),
+ {call, A, {remote, A, {atom, A, ets}, {atom, A, match_spec_compile}},
[abstract_term(depth(MS, Depth), 1)]}]};
-abstract({list, L}, NElements, Depth) when NElements =:= infinity;
- NElements >= length(L) ->
+abstract1({list, L}, NElements, Depth, _A) when NElements =:= infinity;
+ NElements >= length(L) ->
abstract_term(depth(L, Depth), 1);
-abstract({list, L}, NElements, Depth) ->
+abstract1({list, L}, NElements, Depth, _A) ->
abstract_term(depth(lists:sublist(L, NElements), Depth) ++ '...', 1).
depth(List, infinity) ->
@@ -1251,14 +1258,14 @@ abstract_term(Term) ->
abstract_term(Term, 0).
abstract_term(Term, Line) ->
- abstr_term(Term, Line).
+ abstr_term(Term, anno(Line)).
abstr_term(Tuple, Line) when is_tuple(Tuple) ->
{tuple,Line,[abstr_term(E, Line) || E <- tuple_to_list(Tuple)]};
abstr_term([_ | _]=L, Line) ->
case io_lib:char_list(L) of
true ->
- erl_parse:abstract(L, Line);
+ erl_parse:abstract(L, erl_anno:line(Line));
false ->
abstr_list(L, Line)
end;
@@ -1285,7 +1292,7 @@ abstr_term(Fun, Line) when is_function(Fun) ->
abstr_term(PPR, Line) when is_pid(PPR); is_port(PPR); is_reference(PPR) ->
{special, Line, lists:flatten(io_lib:write(PPR))};
abstr_term(Simple, Line) ->
- erl_parse:abstract(Simple, Line).
+ erl_parse:abstract(Simple, erl_anno:line(Line)).
abstr_list([H | T], Line) ->
{cons, Line, abstr_term(H, Line), abstr_list(T, Line)};
@@ -1519,7 +1526,7 @@ join_info(Join, QInfo, Qdata, Code) ->
%% Only compared constants (==).
[Cs1_0, Cs2_0]
end,
- L = 0,
+ L = anno0(),
G1_0 = {var,L,'G1'}, G2_0 = {var,L,'G2'},
JP = element(JQNum + 1, Code),
%% Create code for wh1 and wh2 in #join{}:
@@ -1571,7 +1578,7 @@ join_merge_info(QNum, QInfo, Code, G, ExtraConstants) ->
{P, P};
_ ->
{PV, _} = aux_name1('P', 0, abstract_vars(P)),
- L = 0,
+ L = erl_anno:new(0),
V = {var, L, PV},
{V, {match, L, V, P}}
end,
@@ -1579,19 +1586,20 @@ join_merge_info(QNum, QInfo, Code, G, ExtraConstants) ->
LEI = {generate, term_to_binary(M), LEInfo},
TP = term_to_binary(G),
CFs = [begin
- Call = {call,0,{atom,0,element},[{integer,0,Col},EPV]},
- F = list2op([{op,0,Op,abstract_term(Con),Call}
- || {Con,Op} <- ConstOps], 'or'),
+ A = anno0(),
+ Call = {call,A,{atom,A,element},[{integer,A,Col},EPV]},
+ F = list2op([{op,A,Op,abstract_term(Con),Call}
+ || {Con,Op} <- ConstOps], 'or', A),
term_to_binary(F)
end ||
{Col,ConstOps} <- ExtraConstants],
{{I,G}, [{generate, TP, {qlc, DQP, [LEI | CFs], []}}]}
end.
-list2op([E], _Op) ->
+list2op([E], _Op, _Anno) ->
E;
-list2op([E | Es], Op) ->
- {op,0,Op,E,list2op(Es, Op)}.
+list2op([E | Es], Op, Anno) ->
+ {op,Anno,Op,E,list2op(Es, Op, Anno)}.
join_lookup_info(QNum, QInfo, G) ->
{generate, _, LEInfo}=I = lists:nth(QNum, QInfo),
@@ -1704,7 +1712,7 @@ eval_le(LE_fun, GOpt) ->
prep_qlc_lc({simple_v1, PVar, LE_fun, L}, Opt, GOpt, _H) ->
check_lookup_option(Opt, false),
- prep_simple_qlc(PVar, L, eval_le(LE_fun, GOpt), Opt);
+ prep_simple_qlc(PVar, anno(L), eval_le(LE_fun, GOpt), Opt);
prep_qlc_lc({qlc_v1, QFun, CodeF, Qdata0, QOpt}, Opt, GOpt, _H) ->
F = fun(?qual_data(_QNum, _GoI, _SI, fil)=QualData, ModGens) ->
{QualData, ModGens};
@@ -1821,7 +1829,7 @@ may_create_simple(#qlc_opt{unique = Unique, cache = Cache} = Opt,
if
Unique and not IsUnique;
(Cache =/= false) and not IsCached ->
- prep_simple_qlc(?SIMPLE_QVAR, 1, Prep, Opt);
+ prep_simple_qlc(?SIMPLE_QVAR, anno(1), Prep, Opt);
true ->
Prep
end.
@@ -2764,8 +2772,8 @@ tmp_filename(TmpDirOpt) ->
U = "_",
Node = node(),
Pid = os:getpid(),
- {MSecs,Secs,MySecs} = erlang:now(),
- F = lists:concat([?MODULE,U,Node,U,Pid,U,MSecs,U,Secs,U,MySecs]),
+ Unique = erlang:unique_integer(),
+ F = lists:concat([?MODULE,U,Node,U,Pid,U,Unique]),
TmpDir = case TmpDirOpt of
"" ->
{ok, CurDir} = file:get_cwd(),
@@ -3772,6 +3780,15 @@ grd(Fun, Arg) ->
false
end.
+anno0() ->
+ anno(0).
+
+anno1() ->
+ anno(1).
+
+anno(L) ->
+ erl_anno:new(L).
+
family(L) ->
sofs:to_external(sofs:relation_to_family(sofs:relation(L))).
diff --git a/lib/stdlib/src/qlc_pt.erl b/lib/stdlib/src/qlc_pt.erl
index b6bb758dfb..e4b9768b12 100644
--- a/lib/stdlib/src/qlc_pt.erl
+++ b/lib/stdlib/src/qlc_pt.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2014. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -39,7 +40,12 @@
opt % #qlc_opt
}).
--record(state, {imp, maxargs, records, xwarnings = []}).
+-record(state, {imp,
+ maxargs,
+ records,
+ xwarnings = [],
+ intro_vars,
+ node_info}).
%-define(debug, true).
@@ -66,37 +72,49 @@
Options :: [Option],
Option :: type_checker | compile:option()).
-parse_transform(Forms, Options) ->
+parse_transform(Forms0, Options) ->
?DEBUG("qlc Parse Transform~n", []),
- State = #state{imp = is_qlc_q_imported(Forms),
- maxargs = ?COMPILE_MAX_NUM_OF_ARGS,
- records = record_attributes(Forms)},
- case called_from_type_checker(Options) of
- true ->
- %% The returned value should conform to the types, but
- %% need not evaluate to anything meaningful.
- L = 0,
- {tuple,_,Fs0} = abstr(#qlc_lc{}, L),
- F = fun(_Id, LC, A) ->
- Init = simple(L, 'V', LC, L),
- {{tuple,L,set_field(#qlc_lc.lc, Fs0, Init)}, A}
- end,
- {Forms1,ok} = qlc_mapfold(F, ok, Forms, State),
- Forms1;
- false ->
- FormsNoShadows = no_shadows(Forms, State),
- case compile_messages(Forms, FormsNoShadows, Options, State) of
- {[],[],Warnings} ->
- {NewForms, State1} = transform(FormsNoShadows, State),
- ExtraWs = State1#state.xwarnings,
- {[],WForms} = no_duplicates(NewForms, [], Warnings,
- ExtraWs, Options),
- WForms ++ NewForms;
- {E0,Errors,Warnings} ->
- {EForms,WForms} = no_duplicates(Forms, E0++Errors,
- Warnings, [], Options),
- EForms ++ WForms ++ Forms
- end
+ Imported = is_qlc_q_imported(Forms0),
+ {Forms, FormsNoShadows, State} = initiate(Forms0, Imported),
+ NodeInfo = State#state.node_info,
+ try
+ case called_from_type_checker(Options) of
+ true ->
+ %% The returned value should conform to the types, but
+ %% need not evaluate to anything meaningful.
+ L = anno0(),
+ {tuple,_,Fs0} = abstr(#qlc_lc{}, L),
+ F = fun(_Id, LC, A) ->
+ Init = simple(L, 'V', LC, L),
+ {{tuple,L,set_field(#qlc_lc.lc, Fs0, Init)}, A}
+ end,
+ {Forms1,ok} = qlc_mapfold(F, ok, Forms, State),
+ Forms1;
+ false ->
+ case
+ compile_messages(Forms, FormsNoShadows, Options, State)
+ of
+ {[],Warnings} ->
+ ?DEBUG("node info1 ~p~n",
+ [lists:sort(ets:tab2list(NodeInfo))]),
+ {NewForms, State1} =
+ transform(FormsNoShadows, State),
+ ExtraWs = State1#state.xwarnings,
+ {[],WForms} = no_duplicates(NewForms, [], Warnings,
+ ExtraWs, Options),
+ (restore_locations(WForms, State) ++
+ restore_anno(NewForms, NodeInfo));
+ {Errors,Warnings} ->
+ ?DEBUG("node info2 ~p~n",
+ [lists:sort(ets:tab2list(NodeInfo))]),
+ {EForms,WForms} = no_duplicates(FormsNoShadows, Errors,
+ Warnings, [],
+ Options),
+ restore_locations(EForms ++ WForms, State) ++ Forms0
+ end
+ end
+ after
+ true = ets:delete(NodeInfo)
end.
-spec(transform_from_evaluator(LC, Bs) -> Expr when
@@ -124,30 +142,78 @@ called_from_type_checker(Options) ->
lists:member(type_checker, Options).
transform_expression(LC, Bs0, WithLintErrors) ->
- L = 1,
+ L = anno1(),
As = [{var,L,V} || {V,_Val} <- Bs0],
Ar = length(As),
F = {function,L,bar,Ar,[{clause,L,As,[],[?QLC_Q(L, L, L, L, LC, [])]}]},
- Forms = [{attribute,L,file,{"foo",L}},
- {attribute,L,module,foo}, F],
- State = #state{imp = false,
- maxargs = ?EVAL_MAX_NUM_OF_ARGS,
- records = record_attributes(Forms)},
+ Forms0 = [{attribute,L,file,{"foo",L}},
+ {attribute,L,module,foo}, F],
+ {Forms, FormsNoShadows, State} = initiate(Forms0, false),
+ NodeInfo = State#state.node_info,
Options = [],
- FormsNoShadows = no_shadows(Forms, State),
- case compile_messages(Forms, FormsNoShadows, Options, State) of
- {[],[],_Warnings} ->
- {NewForms,_State1} = transform(FormsNoShadows, State),
- {function,L,bar,Ar,[{clause,L,As,[],[NF]}]} =
- lists:last(NewForms),
- {ok,NF};
- {E0,Errors,_Warnings} when WithLintErrors ->
- {not_ok,mforms(error, E0 ++ Errors)};
- {E0,Errors0,_Warnings} ->
- [{error,Reason} | _] = mforms(error, E0++Errors0),
- {not_ok, {error, ?APIMOD, Reason}}
+ try compile_messages(Forms, FormsNoShadows, Options, State) of
+ {Errors0,_Warnings} ->
+ case restore_locations(Errors0, State) of
+ [] ->
+ {NewForms,_State1} = transform(FormsNoShadows, State),
+ NewForms1 = restore_anno(NewForms, NodeInfo),
+ {function,L,bar,Ar,[{clause,L,As,[],[NF]}]} =
+ lists:last(NewForms1),
+ {ok,NF};
+ Errors when WithLintErrors ->
+ {not_ok,mforms(error, Errors)};
+ Errors ->
+ [{error,Reason} | _] = mforms(error, Errors),
+ {not_ok, {error, ?APIMOD, Reason}}
+ end
+ after
+ true = ets:delete(NodeInfo)
end.
+-ifdef(DEBUG).
+-define(ILIM, 0).
+-else.
+-define(ILIM, 255).
+-endif.
+
+initiate(Forms0, Imported) ->
+ NodeInfo = ets:new(?APIMOD, []),
+ true = ets:insert(NodeInfo, {var_n, ?ILIM}),
+ exclude_integers_from_unique_line_numbers(Forms0, NodeInfo),
+ ?DEBUG("node info0 ~p~n",
+ [lists:sort(ets:tab2list(NodeInfo))]),
+ State0 = #state{imp = Imported,
+ maxargs = ?EVAL_MAX_NUM_OF_ARGS,
+ records = record_attributes(Forms0),
+ node_info = NodeInfo},
+ Forms = save_anno(Forms0, NodeInfo),
+ FormsNoShadows = no_shadows(Forms, State0),
+ IntroVars = intro_variables(FormsNoShadows, State0),
+ State = State0#state{intro_vars = IntroVars},
+ {Forms, FormsNoShadows, State}.
+
+%% Make sure restore_locations() does not confuse integers with (the
+%% unique) line numbers.
+exclude_integers_from_unique_line_numbers(Forms, NodeInfo) ->
+ Integers = find_integers(Forms),
+ lists:foreach(fun(I) -> ets:insert(NodeInfo, {I}) end, Integers).
+
+find_integers(Forms) ->
+ F = fun(A) ->
+ Fs1 = map_anno(fun(_) -> A end, Forms),
+ ordsets:from_list(integers(Fs1, []))
+ end,
+ ordsets:to_list(ordsets:intersection(F(anno0()), F(anno1()))).
+
+integers([E | Es], L) ->
+ integers(Es, integers(E, L));
+integers(T, L) when is_tuple(T) ->
+ integers(tuple_to_list(T), L);
+integers(I, L) when is_integer(I), I > ?ILIM ->
+ [I | L];
+integers(_, L) ->
+ L.
+
-define(I(I), {integer, L, I}).
-define(A(A), {atom, L, A}).
-define(V(V), {var, L, V}).
@@ -164,9 +230,15 @@ mforms(Tag, L) ->
%% Avoid duplicated lint warnings and lint errors. Care has been taken
%% not to introduce unused variables in the transformed code.
%%
-no_duplicates(Forms, Errors, Warnings0, ExtraWarnings, Options) ->
+no_duplicates(Forms, Errors, Warnings0, ExtraWarnings0, Options) ->
%% Some mistakes such as "{X} =:= {}" are found by strong
%% validation as well as by qlc. Prefer the warnings from qlc:
+ %% The Compiler and qlc do not agree on the location of errors.
+ %% For now, qlc's messages about failing patterns and filters
+ %% are ignored.
+ ExtraWarnings = [W || W={_File,[{_,qlc,Tag}]} <-
+ ExtraWarnings0,
+ not lists:member(Tag, [nomatch_pattern,nomatch_filter])],
Warnings1 = mforms(Warnings0) --
([{File,[{L,v3_core,nomatch}]} ||
{File,[{L,qlc,M}]} <- mforms(ExtraWarnings),
@@ -185,13 +257,22 @@ mforms(L) ->
lists:sort([{File,[M]} || {File,Ms} <- L, M <- Ms]).
mforms2(Tag, L) ->
- Line = 0,
+ Line = anno0(),
ML = lists:flatmap(fun({File,Ms}) ->
- [[{attribute,Line,file,{File,Line}}, {Tag,M}] ||
+ [[{attribute,Line,file,{File,0}}, {Tag,M}] ||
M <- Ms]
end, lists:sort(L)),
lists:flatten(lists:sort(ML)).
+restore_locations([T | Ts], State) ->
+ [restore_locations(T, State) | restore_locations(Ts, State)];
+restore_locations(T, State) when is_tuple(T) ->
+ list_to_tuple(restore_locations(tuple_to_list(T), State));
+restore_locations(I, State) when I > ?ILIM ->
+ restore_loc(I, State);
+restore_locations(T, _State) ->
+ T.
+
is_qlc_q_imported(Forms) ->
[[] || {attribute,_,import,{?APIMOD,FAs}} <- Forms, {?Q,1} <- FAs] =/= [].
@@ -212,13 +293,20 @@ compile_messages(Forms, FormsNoShadows, Options, State) ->
(_QId, Q, GA, A) ->
{Q,GA,A}
end,
- {_,BGens} = qual_fold(BGenF, [], [], FormsNoShadows, State),
+ {_,BGens} = qual_fold(BGenF, [], [], Forms, State),
GenForm = used_genvar_check(FormsNoShadows, State),
?DEBUG("GenForm = ~ts~n", [catch erl_pp:form(GenForm)]),
- WarnFun = fun(Id, LC, A) -> {tag_lines(LC, get_lcid_no(Id)), A} end,
+ {GEs,_} = compile_forms([GenForm], Options),
+ UsedGenVarMsgs = used_genvar_messages(GEs, State),
+ NodeInfo = State#state.node_info,
+ WarnFun = fun(_Id, LC, A) -> {lc_nodes(LC, NodeInfo), A} end,
{WForms,ok} = qlc_mapfold(WarnFun, ok, Forms, State),
- {Es,Ws} = compile_forms(WForms ++ [GenForm], Options),
- {badarg(Forms, State),tagged_messages(Es)++BGens,tagged_messages(Ws)}.
+ {Es,Ws} = compile_forms(WForms, Options),
+ LcEs = lc_messages(Es, NodeInfo),
+ LcWs = lc_messages(Ws, NodeInfo),
+ Errors = badarg(Forms, State) ++ UsedGenVarMsgs++LcEs++BGens,
+ Warnings = LcWs,
+ {Errors,Warnings}.
badarg(Forms, State) ->
F = fun(_Id, {lc,_L,_E,_Qs}=LC, Es) ->
@@ -230,54 +318,39 @@ badarg(Forms, State) ->
{_,E0} = qlc_mapfold(F, [], Forms, State),
E0.
-tag_lines(E, No) ->
- map_lines(fun(Id) ->
- case is_lcid(Id) of
- true -> Id;
- false -> make_lcid(Id, No)
- end
- end, E).
-
-map_lines(F, E) ->
- erl_lint:modify_line(E, F).
-
-tagged_messages(MsL) ->
- [{File,
- [{Loc,Mod,untag(T)} || {Loc0,Mod,T} <- Ms,
- {true,Loc} <- [tloc(Loc0)]]}
- || {File,Ms} <- MsL]
- ++
+lc_nodes(E, NodeInfo) ->
+ map_anno(fun(Anno) ->
+ N = erl_anno:line(Anno),
+ [{N, Data}] = ets:lookup(NodeInfo, N),
+ NData = Data#{inside_lc => true},
+ true = ets:insert(NodeInfo, {N, NData}),
+ Anno
+ end, E).
+
+used_genvar_messages(MsL, S) ->
[{File,[{Loc,?APIMOD,{used_generator_variable,V}}]}
- || {_, Ms} <- MsL,
+ || {_, Ms} <- MsL,
{XLoc,erl_lint,{unbound_var,_}} <- Ms,
- {Loc,File,V} <- [extra(XLoc)]].
-
-tloc({Id,Column}) ->
- {IsLcid,T} = tloc(Id),
- {IsLcid,{T,Column}};
-tloc(Id) ->
- IsLcid = is_lcid(Id),
- {IsLcid,case IsLcid of
- true -> get_lcid_line(Id);
- false -> any
- end}.
-
-extra({extra,Line,File,V}) ->
- {Line,File,V};
-extra({Line,Column}) ->
- case extra(Line) of
- {L,File,V} -> {{L,Column},File,V};
- Else -> Else
- end;
-extra(Else) ->
- Else.
-
-untag([E | Es]) -> [untag(E) | untag(Es)];
-untag(T) when is_tuple(T) -> list_to_tuple(untag(tuple_to_list(T)));
-untag(E) ->
- case is_lcid(E) of
- true -> get_lcid_line(E);
- false -> E
+ {Loc,File,V} <- [genvar_pos(XLoc, S)]].
+
+lc_messages(MsL, NodeInfo) ->
+ [{File,[{Loc,Mod,T} || {Loc,Mod,T} <- Ms, lc_loc(Loc, NodeInfo)]} ||
+ {File,Ms} <- MsL].
+
+lc_loc(N, NodeInfo) ->
+ case ets:lookup(NodeInfo, N) of
+ [{N, #{inside_lc := true}}] ->
+ true;
+ [{N, _}] ->
+ false
+ end.
+
+genvar_pos(Location, S) ->
+ case ets:lookup(S#state.node_info, Location) of
+ [{Location, #{genvar_pos := Pos}}] ->
+ Pos;
+ [] ->
+ Location
end.
%% -> [{Qid,[variable()]}].
@@ -293,6 +366,7 @@ untag(E) ->
%% variables (unless they are unsafe).
%%
intro_variables(FormsNoShadows, State) ->
+ NodeInfo = State#state.node_info,
Fun = fun(QId, {T,_L,P0,_E0}=Q, {GVs,QIds}, Foo) when T =:= b_generate;
T =:= generate ->
PVs = qlc:var_ufold(fun({var,_,V}) -> {QId,V} end, P0),
@@ -302,10 +376,11 @@ intro_variables(FormsNoShadows, State) ->
%% where E is an LC expression consisting of a
%% template mentioning all variables occurring in F.
Vs = ordsets:to_list(qlc:vars(Filter0)),
- Id = QId#qid.lcid,
- LC1 = embed_vars(intro_set_line({QId,f1}, Vs), Id),
- LC2 = embed_vars(intro_set_line({QId,f2}, Vs), Id),
- AnyLine = -1,
+ AnyLine = anno0(),
+ Vars = [{var,AnyLine,V} || V <- Vs],
+ LC = embed_vars(Vars, AnyLine),
+ LC1 = intro_anno(LC, before, QId, NodeInfo),
+ LC2 = intro_anno(LC, 'after', QId, NodeInfo),
Filter = {block,AnyLine,[LC1,Filter0,LC2]},
{Filter,{GVs,[{QId,[]} | QIds]},Foo}
end,
@@ -317,9 +392,15 @@ intro_variables(FormsNoShadows, State) ->
Es0 = compile_errors(FForms),
%% A variable is bound inside the filter if it is not bound before
%% the filter, but it is bound after the filter (obviously).
- Before = [{QId,V} || {{QId,f1},erl_lint,{unbound_var,V}} <- Es0],
- After = [{QId,V} || {{QId,f2},erl_lint,{unbound_var,V}} <- Es0],
- Unsafe = [{QId,V} || {{QId,f2},erl_lint,{unsafe_var,V,_Where}} <- Es0],
+ Before = [{QId,V} ||
+ {L,erl_lint,{unbound_var,V}} <- Es0,
+ {_L,{QId,before}} <- ets:lookup(NodeInfo, L)],
+ After = [{QId,V} ||
+ {L,erl_lint,{unbound_var,V}} <- Es0,
+ {_L,{QId,'after'}} <- ets:lookup(NodeInfo, L)],
+ Unsafe = [{QId,V} ||
+ {L,erl_lint,{unsafe_var,V,_Where}} <- Es0,
+ {_L,{QId,'after'}} <- ets:lookup(NodeInfo, L)],
?DEBUG("Before = ~p~n", [Before]),
?DEBUG("After = ~p~n", [After]),
?DEBUG("Unsafe = ~p~n", [Unsafe]),
@@ -328,9 +409,14 @@ intro_variables(FormsNoShadows, State) ->
I1 = family(IV ++ GenVars),
sofs:to_external(sofs:family_union(sofs:family(QIds), I1)).
-intro_set_line(Tag, Vars) ->
- L = erl_parse:set_line(1, fun(_) -> Tag end),
- [{var,L,V} || V <- Vars].
+intro_anno(LC, Where, QId, NodeInfo) ->
+ Data = {QId,Where},
+ Fun = fun(Anno) ->
+ Location = erl_anno:location(Anno),
+ true = ets:insert(NodeInfo, {Location,Data}),
+ Anno
+ end,
+ map_anno(Fun, save_anno(LC, NodeInfo)).
compile_errors(FormsNoShadows) ->
case compile_forms(FormsNoShadows, []) of
@@ -341,11 +427,14 @@ compile_errors(FormsNoShadows) ->
lists:flatmap(fun({_File,Es}) -> Es end, Errors)
end.
--define(MAX_NUM_OF_LINES, 23). % assume max 1^23 lines (> 8 millions)
-
compile_forms(Forms0, Options) ->
- Forms = [F || F <- Forms0, element(1, F) =/= eof] ++
- [{eof,1 bsl ?MAX_NUM_OF_LINES}],
+ 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} ->
@@ -384,20 +473,23 @@ bitstr_options() ->
%% for each ListExpr. The expression mentions all introduced variables
%% occurring in ListExpr. Running the function through the compiler
%% yields error messages for erroneous use of introduced variables.
-%% The messages have the form
-%% {{extra,LineNo,File,Var},Module,{unbound_var,V}}, where Var is the
-%% original variable name and V is the name invented by no_shadows/2.
%%
used_genvar_check(FormsNoShadows, State) ->
- F = fun(QId, {T, Ln, _P, LE}=Q, {QsIVs0, Exprs0}, IVsSoFar0)
+ NodeInfo = State#state.node_info,
+ F = fun(QId, {T, Ln, _P, LE}=Q, {QsIVs0, Exprs0}, IVsSoFar0)
when T =:= b_generate; T =:= generate ->
- F = fun({var, _, V}=Var) ->
- {var, L, OrigVar} = undo_no_shadows(Var),
- AF = fun(Line) ->
- {extra, Line, get(?QLC_FILE), OrigVar}
- end,
- L2 = erl_parse:set_line(L, AF),
- {var, L2, V}
+ F = fun(Var) ->
+ {var, Anno0, OrigVar} =
+ undo_no_shadows(Var, State),
+ {var, Anno, _} = NewVar = save_anno(Var, NodeInfo),
+ Location0 = erl_anno:location(Anno0),
+ Location = erl_anno:location(Anno),
+ [{Location, Data}] =
+ ets:lookup(NodeInfo, Location),
+ Pos = {Location0,get(?QLC_FILE),OrigVar},
+ NData = Data#{genvar_pos => Pos},
+ true = ets:insert(NodeInfo, {Location, NData}),
+ NewVar
end,
Vs = [Var || {var, _, V}=Var <- qlc:var_fold(F, [], LE),
lists:member(V, IVsSoFar0)],
@@ -411,12 +503,12 @@ used_genvar_check(FormsNoShadows, State) ->
{QsIVs, IVsSoFar} = q_intro_vars(QId, QsIVs0, IVsSoFar0),
{Filter, {QsIVs, Exprs}, IVsSoFar}
end,
- IntroVars = intro_variables(FormsNoShadows, State),
- Acc0 = {IntroVars, [{atom, 0, true}]},
+ Acc0 = {State#state.intro_vars, [{atom, anno0(), true}]},
{_, {[], Exprs}} = qual_fold(F, Acc0, [], FormsNoShadows, State),
FunctionNames = [Name || {function, _, Name, _, _} <- FormsNoShadows],
UniqueFName = qlc:aux_name(used_genvar, 1, sets:from_list(FunctionNames)),
- {function,0,UniqueFName,0,[{clause,0,[],[],lists:reverse(Exprs)}]}.
+ A = anno0(),
+ {function,A,UniqueFName,0,[{clause,A,[],[],lists:reverse(Exprs)}]}.
q_intro_vars(QId, [{QId, IVs} | QsIVs], IVsSoFar) -> {QsIVs, IVs ++ IVsSoFar}.
@@ -514,7 +606,8 @@ q_intro_vars(QId, [{QId, IVs} | QsIVs], IVsSoFar) -> {QsIVs, IVs ++ IVsSoFar}.
%% (calling LEf returns the objects generated by LE).
transform(FormsNoShadows, State) ->
- IntroVars = intro_variables(FormsNoShadows, State),
+ _ = erlang:system_flag(backtrace_depth, 500),
+ IntroVars = State#state.intro_vars,
AllVars = sets:from_list(ordsets:to_list(qlc:vars(FormsNoShadows))),
?DEBUG("AllVars = ~p~n", [sets:to_list(AllVars)]),
F1 = fun(QId, {generate,_,P,LE}, Foo, {GoI,SI}) ->
@@ -588,8 +681,8 @@ transform(FormsNoShadows, State) ->
[{match,L,{var,L,Fun},FunC},
{call,L,{var,L,Fun},As0}]}]}},
{ok, OrigE0} = dict:find(Id, Source),
- OrigE = undo_no_shadows(OrigE0),
- QCode = qcode(OrigE, XQCs, Source, L),
+ OrigE = undo_no_shadows(OrigE0, State),
+ QCode = qcode(OrigE, XQCs, Source, L, State),
Qdata = qdata(XQCs, L),
TemplateInfo =
template_columns(Qs, E, AllIVs, Dependencies, State),
@@ -598,7 +691,7 @@ transform(FormsNoShadows, State) ->
Opt = opt_info(TemplateInfo, SizeInfo, JoinInfo, MSQs, L,
EqColumnConstants, EqualColumnConstants),
LCTuple =
- case qlc_kind(OrigE, Qs) of
+ case qlc_kind(OrigE, Qs, State) of
qlc ->
{tuple,L,[?A(qlc_v1),FunW,QCode,Qdata,Opt]};
{simple, PL, LE, V} ->
@@ -612,7 +705,7 @@ transform(FormsNoShadows, State) ->
end,
{NForms,{[],XW}} = qlc_mapfold(F2, {IntroVars,[]}, ModifiedForms1, State),
display_forms(NForms),
- {restore_line_numbers(NForms), State#state{xwarnings = XW}}.
+ {NForms, State#state{xwarnings = XW}}.
join_kind(Qs, LcL, AllIVs, Dependencies, State) ->
{EqualCols2, EqualColsN} = equal_columns(Qs, AllIVs, Dependencies, State),
@@ -623,20 +716,21 @@ join_kind(Qs, LcL, AllIVs, Dependencies, State) ->
if
EqualColsN =/= []; MatchColsN =/= [] ->
{[],
- [{get(?QLC_FILE),[{abs(LcL),?APIMOD,too_complex_join}]}]};
+ [{get(?QLC_FILE),[{LcL,?APIMOD,too_complex_join}]}]};
EqualCols2 =:= [], MatchCols2 =:= [] ->
{[], []};
length(Tables) > 2 ->
{[],
- [{get(?QLC_FILE),[{abs(LcL),?APIMOD,too_many_joins}]}]};
+ [{get(?QLC_FILE),[{LcL,?APIMOD,too_many_joins}]}]};
EqualCols2 =:= MatchCols2 ->
{EqualCols2, []};
true ->
{{EqualCols2, MatchCols2}, []}
end.
-qlc_kind(OrigE, Qs) ->
- {OrigFilterData, OrigGeneratorData} = qual_data(undo_no_shadows(Qs)),
+qlc_kind(OrigE, Qs, State) ->
+ {OrigFilterData, OrigGeneratorData} =
+ qual_data(undo_no_shadows(Qs, State)),
OrigAllFilters = filters_as_one(OrigFilterData),
{_FilterData, GeneratorData} = qual_data(Qs),
case {OrigE, OrigAllFilters, OrigGeneratorData} of
@@ -663,12 +757,12 @@ warn_failing_qualifiers(Qualifiers, AllIVs, Dependencies, State) ->
lists:foldl(fun({_QId,{fil,_Filter}}, {[]=Frames,Warnings}) ->
{Frames,Warnings};
({_QId,{fil,Filter}}, {Frames,Warnings}) ->
- case filter(set_line(Filter, 0), Frames, BindFun,
+ case filter(reset_anno(Filter), Frames, BindFun,
State, Imported) of
[] ->
{[],
[{get(?QLC_FILE),
- [{abs_loc(element(2, Filter)),?APIMOD,
+ [{loc(element(2, Filter)),?APIMOD,
nomatch_filter}]} | Warnings]};
Frames1 ->
{Frames1,Warnings}
@@ -678,7 +772,7 @@ warn_failing_qualifiers(Qualifiers, AllIVs, Dependencies, State) ->
{failed, _, _} ->
{Frames,
[{get(?QLC_FILE),
- [{abs_loc(element(2, Pattern)),?APIMOD,
+ [{loc(element(2, Pattern)),?APIMOD,
nomatch_pattern}]} | Warnings]};
_ ->
{Frames,Warnings}
@@ -751,8 +845,8 @@ opt_constants(L, ColumnConstants) ->
|| IdNo <- Ns]
++ [{clause,L,[?V('_')],[],[?A(no_column_fun)]}].
-abstr(Term, Line) ->
- erl_parse:abstract(Term, Line).
+abstr(Term, Anno) ->
+ erl_parse:abstract(Term, loc(Anno)).
%% Extra generators are introduced for join.
join_quals(JoinInfo, QCs, L, LcNo, ExtraConstants, AllVars) ->
@@ -837,9 +931,10 @@ join_handle(AP, L, [F, H, O, C], Constants) ->
{{var, _, _}, []} ->
{'fun',L,{clauses,[{clause,L,[H],[],[H]}]}};
_ ->
+ A = anno0(),
G0 = [begin
- Call = {call,0,{atom,0,element},[{integer,0,Col},O]},
- list2op([{op,0,Op,Con,Call} || {Con,Op} <- Cs], 'or')
+ Call = {call,A,{atom,A,element},[{integer,A,Col},O]},
+ list2op([{op,A,Op,Con,Call} || {Con,Op} <- Cs], 'or')
end || {Col,Cs} <- Constants],
G = if G0 =:= [] -> G0; true -> [G0] end,
CC1 = {clause,L,[AP],G,[{cons,L,O,closure({call,L,F,[F,C]},L)}]},
@@ -876,14 +971,15 @@ join_handle_constants(QId, ExtraConstants) ->
%% order the traverse fun would return them.
column_fun(Columns, QualifierNumber, LcL) ->
+ A = anno0(),
ColCls0 =
[begin
true = Vs0 =/= [], % at least one value to look up
Vs1 = list2cons(Vs0),
- Fils1 = {tuple,0,[{atom,0,FTag},
+ Fils1 = {tuple,A,[{atom,A,FTag},
lists:foldr
- (fun(F, A) -> {cons,0,{integer,0,F},A}
- end, {nil,0}, Fils)]},
+ (fun(F, Ac) -> {cons,A,{integer,A,F},Ac}
+ end, {nil,A}, Fils)]},
Tag = case ordsets:to_list(qlc:vars(Vs1)) of
Imp when length(Imp) > 0, % imported vars
length(Vs0) > 1 ->
@@ -891,13 +987,13 @@ column_fun(Columns, QualifierNumber, LcL) ->
_ ->
values
end,
- Vs = {tuple,0,[{atom,0,Tag},Vs1,Fils1]},
- {clause,0,[erl_parse:abstract(Col)],[],[Vs]}
+ Vs = {tuple,A,[{atom,A,Tag},Vs1,Fils1]},
+ {clause,A,[erl_parse:abstract(Col)],[],[Vs]}
end ||
{{CIdNo,Col}, Vs0, {FTag,Fils}} <- Columns,
CIdNo =:= QualifierNumber]
- ++ [{clause,0,[{var,0,'_'}],[],[{atom,0,false}]}],
- ColCls = set_line(ColCls0, LcL),
+ ++ [{clause,A,[{var,A,'_'}],[],[{atom,A,false}]}],
+ ColCls = set_anno(ColCls0, LcL),
{'fun', LcL, {clauses, ColCls}}.
%% Tries to find columns of the template that (1) are equal to (or
@@ -920,7 +1016,7 @@ template_columns(Qs0, E0, AllIVs, Dependencies, State) ->
MatchColumns = eq_columns2(Qs, AllIVs, Dependencies, State),
Equal = template_cols(EqualColumns),
Match = template_cols(MatchColumns),
- L = 0,
+ L = anno0(),
if
Match =:= Equal ->
[{?V('_'), Match}];
@@ -947,7 +1043,7 @@ template_cols(ColumnClasses) ->
template_as_pattern(E) ->
P = simple_template(E),
- {?TID,foo,foo,{gen,P,{nil,0}}}.
+ {?TID,foo,foo,{gen,P,{nil,anno0()}}}.
simple_template({call,L,{remote,_,{atom,_,erlang},{atom,_,element}}=Call,
[{integer,_,I}=A1,A2]}) when I > 0 ->
@@ -1004,10 +1100,10 @@ match_spec_quals(Template, Dependencies, Qualifiers, State) ->
GQId =:= QId2,
{FQId,{fil,F}}=Filter <- Filters, % guard filters only
FQId =:= QId]
- ++ [{GId#qid.no,Pattern,[],{atom,0,true}} ||
+ ++ [{GId#qid.no,Pattern,[],{atom,anno0(),true}} ||
{GId,{gen,Pattern,_}} <- GeneratorData,
lists:member(GId, NoFilterGIds)],
- E = {nil, 0},
+ E = {nil, anno0()},
GF = [{{GNum,Pattern},Filter} ||
{GNum,Pattern,Filter,F} <- Candidates,
no =/= try_ms(E, Pattern, F, State)],
@@ -1024,7 +1120,7 @@ match_spec_quals(Template, Dependencies, Qualifiers, State) ->
%% expressione can be replaced by a match specification.
[{GNum, AbstrMS, all}]
catch _:_ ->
- {TemplVar, _} = anon_var({var,0,'_'}, 0),
+ {TemplVar, _} = anon_var({var,anno0(),'_'}, 0),
[one_gen_match_spec(GNum, Pattern, GFilterData, State, TemplVar) ||
{{GNum,Pattern},GFilterData} <- GFFL]
end.
@@ -1038,7 +1134,7 @@ gen_ms(E, Pattern, GFilterData, State) ->
{ok, MS, AMS} = try_ms(E, Pattern, filters_as_one(GFilterData), State),
case MS of
[{'$1',[true],['$1']}] ->
- {atom, 0, no_match_spec};
+ {atom, anno0(), no_match_spec};
_ ->
AMS
end.
@@ -1060,7 +1156,7 @@ pattern_as_template({match,_,_E,{var,_,_}=V}=P, _TemplVar) ->
pattern_as_template({match,_,{var,_,_}=V,_E}=P, _TemplVar) ->
{V, P};
pattern_as_template(E, TemplVar) ->
- L = 0,
+ L = anno0(),
{TemplVar, {match, L, E, TemplVar}}.
%% Tries to find columns which are compared or matched against
@@ -1203,7 +1299,7 @@ lu_skip(ColConstants, FilterData, PatternFrame, PatternVars,
ColFil = [{Column, FId#qid.no} ||
{FId,{fil,Fil}} <-
filter_list(FilterData, Dependencies, State),
- [] =/= (SFs = safe_filter(set_line(Fil, 0), PatternFrames,
+ [] =/= (SFs = safe_filter(reset_anno(Fil), PatternFrames,
BindFun, State, Imported)),
{GId,PV} <- PatternVars,
[] =/=
@@ -1392,7 +1488,7 @@ join_skip(JoinClasses, FilterData, PatternFrame, PatternVars, Dependencies,
JF = unify(JoinOp, V1, V2, JF2, BindFun, Imported),
%% "Run" the filter:
- SFs = safe_filter(set_line(Fil, 0), PatternFrames,
+ SFs = safe_filter(reset_anno(Fil), PatternFrames,
BindFun, State, Imported),
JImp = qlc:vars([SFs, JF]), % kludge
lists:all(fun(Frame) ->
@@ -1403,7 +1499,7 @@ join_skip(JoinClasses, FilterData, PatternFrame, PatternVars, Dependencies,
filter_info(FilterData, AllIVs, Dependencies, State) ->
FilterList = filter_list(FilterData, Dependencies, State),
- Filter0 = set_line(filters_as_one(FilterList), 0),
+ Filter0 = reset_anno(filters_as_one(FilterList)),
Anon0 = 0,
{Filter, Anon1} = anon_var(Filter0, Anon0),
Imported = ordsets:subtract(qlc:vars(Filter), % anonymous too
@@ -1510,7 +1606,7 @@ pattern(P0, AnonI, Frame0, BindFun, State) ->
catch _:_ -> P0 % template, records already expanded
end,
%% Makes test for equality simple:
- P2 = set_line(P1, 0),
+ P2 = reset_anno(P1),
{P3, AnonN} = anon_var(P2, AnonI),
{P4, F1} = match_in_pattern(tuple2cons(P3), Frame0, BindFun),
{P, F2} = element_calls(P4, F1, BindFun, _Imp=[]), % kludge for templates
@@ -1550,8 +1646,11 @@ anon_var(E, AnonI) ->
(Var, N) -> {Var, N}
end, AnonI, E).
-set_line(T, L) ->
- map_lines(fun(_L) -> L end, T).
+reset_anno(T) ->
+ set_anno(T, anno0()).
+
+set_anno(T, A) ->
+ map_anno(fun(_L) -> A end, T).
-record(fstate, {state, bind_fun, imported}).
@@ -1673,7 +1772,7 @@ frames_to_columns(Fs, PatternVars, DerefFun, SelectorFun, Imp, CompOp) ->
%% same variables have to be the representatives in every frame.)
SizesVarsL =
[begin
- PatVar = {var,0,PV},
+ PatVar = {var,anno0(),PV},
PatternSizes = [pattern_size([F], PatVar, false) ||
F <- Fs],
MaxPZ = lists:max([0 | PatternSizes -- [undefined]]),
@@ -1692,8 +1791,8 @@ frames_to_columns(Fs, PatternVars, DerefFun, SelectorFun, Imp, CompOp) ->
frames2cols(Fs, PatN, PatSizes, Vars, DerefFun, SelectorFun, CompOp) ->
Rs = [ begin
RL = [{{PatN,Col},cons2tuple(element(2, Const))} ||
- {V, Col} <- lists:zip(sublist(Vars, PatSz),
- seq(1, PatSz)),
+ {V, Col} <- lists:zip(lists:sublist(Vars, PatSz),
+ lists:seq(1, PatSz)),
%% Do not handle the case where several
%% values compare equal, e.g. "X =:= 1
%% andalso X == 1.0". Looking up both
@@ -1722,11 +1821,11 @@ frames2cols(Fs, PatN, PatSizes, Vars, DerefFun, SelectorFun, CompOp) ->
[C || {_,Vs}=C <- sofs:to_external(Cs), not col_ignore(Vs, CompOp)].
pat_vars(N) ->
- [unique_var() || _ <- seq(1, N)].
+ [unique_var() || _ <- lists:seq(1, N)].
pat_tuple(Sz, Vars) when is_integer(Sz), Sz > 0 ->
TupleTail = unique_var(),
- {cons_tuple, list2cons(sublist(Vars, Sz) ++ TupleTail)};
+ {cons_tuple, list2cons(lists:sublist(Vars, Sz) ++ TupleTail)};
pat_tuple(_, _Vars) ->
unique_var().
@@ -1740,7 +1839,7 @@ col_ignore(Vs, '==') ->
pattern_sizes(PatternVars, Fs) ->
[{QId#qid.no, Size} ||
{QId,PV} <- PatternVars,
- undefined =/= (Size = pattern_size(Fs, {var,0,PV}, true))].
+ undefined =/= (Size = pattern_size(Fs, {var,anno0(),PV}, true))].
pattern_size(Fs, PatternVar, Exact) ->
Fun = fun(F) -> (deref_pattern(_Imported = []))(PatternVar, F) end,
@@ -1768,7 +1867,8 @@ prep_expr(E, F, S, BF, Imported) ->
element_calls(tuple2cons(expand_expr_records(E, S)), F, BF, Imported).
unify_column(Frame, Var, Col, BindFun, Imported) ->
- Call = {call,0,{atom,0,element},[{integer,0,Col}, {var,0,Var}]},
+ A = anno0(),
+ Call = {call,A,{atom,A,element},[{integer,A,Col}, {var,A,Var}]},
element_calls(Call, Frame, BindFun, Imported).
%% cons_tuple is used for representing {V1, ..., Vi | TupleTail}.
@@ -1800,21 +1900,23 @@ element_calls(E, F, _BF, _Imported) ->
{E, F}.
unique_var() ->
- {var, 0, make_ref()}.
+ {var, anno0(), make_ref()}.
is_unique_var({var, _L, V}) ->
is_reference(V).
expand_pattern_records(P, State) ->
- E = {'case',0,{atom,0,true},[{clause,0,[P],[],[{atom,0,true}]}]},
- {'case',_,_,[{clause,0,[NP],_,_}]} = expand_expr_records(E, State),
+ A = anno0(),
+ E = {'case',A,{atom,A,true},[{clause,A,[P],[],[{atom,A,true}]}]},
+ {'case',_,_,[{clause,A,[NP],_,_}]} = expand_expr_records(E, State),
NP.
expand_expr_records(E, State) ->
RecordDefs = State#state.records,
- Forms = RecordDefs ++ [{function,1,foo,0,[{clause,1,[],[],[pe(E)]}]}],
- [{function,_,foo,0,[{clause,_,[],[],[NE]}]}] =
- erl_expand_records:module(Forms, [no_strict_record_tests]),
+ A = anno1(),
+ Forms0 = RecordDefs ++ [{function,A,foo,0,[{clause,A,[],[],[pe(E)]}]}],
+ Forms = erl_expand_records:module(Forms0, [no_strict_record_tests]),
+ {function,_,foo,0,[{clause,_,[],[],[NE]}]} = lists:last(Forms),
NE.
%% Partial evaluation.
@@ -2126,15 +2228,15 @@ tuple2cons(E) ->
E.
list2cons([E | Es]) ->
- {cons, 0, E, list2cons(Es)};
+ {cons, anno0(), E, list2cons(Es)};
list2cons([]) ->
- {nil, 0};
+ {nil, anno0()};
list2cons(E) ->
E.
%% Returns {..., Variable} if Variable is a tuple tail.
cons2tuple({cons_tuple, Es}) ->
- {tuple, 0, cons2list(Es)};
+ {tuple, anno0(), cons2list(Es)};
cons2tuple(T) when is_tuple(T) ->
list_to_tuple(cons2tuple(tuple_to_list(T)));
cons2tuple([E | Es]) ->
@@ -2173,11 +2275,10 @@ bindings_subset(F1, F2, Imp) ->
%% not to have guard semantics, affected filters will have to be
%% recognized and excluded here as well.
try_ms(E, P, Fltr, State) ->
- L = 1,
+ L = anno1(),
Fun = {'fun',L,{clauses,[{clause,L,[P],[[Fltr]],[E]}]}},
Expr = {call,L,{remote,L,{atom,L,ets},{atom,L,fun2ms}},[Fun]},
- Form0 = {function,L,foo,0,[{clause,L,[],[],[Expr]}]},
- Form = restore_line_numbers(Form0),
+ Form = {function,L,foo,0,[{clause,L,[],[],[Expr]}]},
X = ms_transform:parse_transform(State#state.records ++ [Form], []),
case catch
begin
@@ -2194,11 +2295,11 @@ try_ms(E, P, Fltr, State) ->
end.
filters_as_one([]) ->
- {atom, 0, true};
+ {atom, anno0(), true};
filters_as_one(FilterData) ->
[{_,{fil,Filter1}} | Filters] = lists:reverse(FilterData),
lists:foldr(fun({_QId,{fil,Filter}}, AbstF) ->
- {op,0,'andalso',Filter,AbstF}
+ {op,anno0(),'andalso',Filter,AbstF}
end, Filter1, Filters).
qual_data(Qualifiers) ->
@@ -2233,38 +2334,40 @@ qdata([], L) ->
{nil,L}.
qcon(Cs) ->
- list2cons([{tuple,0,[{integer,0,Col},list2cons(qcon1(ConstOps))]} ||
+ A = anno0(),
+ list2cons([{tuple,A,[{integer,A,Col},list2cons(qcon1(ConstOps))]} ||
{Col,ConstOps} <- Cs]).
qcon1(ConstOps) ->
- [{tuple,0,[Const,abstr(Op, 0)]} || {Const,Op} <- ConstOps].
+ A = anno0(),
+ [{tuple,A,[Const,abstr(Op, A)]} || {Const,Op} <- ConstOps].
%% The original code (in Source) is used for filters and the template
%% since the translated code can have QLCs and we don't want them to
%% be visible.
-qcode(E, QCs, Source, L) ->
+qcode(E, QCs, Source, L, State) ->
CL = [begin
Bin = term_to_binary(C, [compressed]),
{bin, L, [{bin_element, L,
{string, L, binary_to_list(Bin)},
default, default}]}
end || {_,C} <- lists:keysort(1, [{qlc:template_state(),E} |
- qcode(QCs, Source)])],
+ qcode(QCs, Source, State)])],
{'fun', L, {clauses, [{clause, L, [], [], [{tuple, L, CL}]}]}}.
-qcode([{_QId, {_QIvs, {{gen,P,_LE,_GV}, GoI, _SI}}} | QCs], Source) ->
- [{GoI,undo_no_shadows(P)} | qcode(QCs, Source)];
-qcode([{QId, {_QIVs, {{fil,_F}, GoI, _SI}}} | QCs], Source) ->
+qcode([{_QId, {_QIvs, {{gen,P,_LE,_GV}, GoI, _SI}}} | QCs], Source, State) ->
+ [{GoI,undo_no_shadows(P, State)} | qcode(QCs, Source, State)];
+qcode([{QId, {_QIVs, {{fil,_F}, GoI, _SI}}} | QCs], Source, State) ->
{ok,OrigF} = dict:find(QId, Source),
- [{GoI,undo_no_shadows(OrigF)} | qcode(QCs, Source)];
-qcode([], _Source) ->
+ [{GoI,undo_no_shadows(OrigF, State)} | qcode(QCs, Source, State)];
+qcode([], _Source, _State) ->
[].
closure(Code, L) ->
{'fun',L,{clauses,[{clause,L,[],[],[Code]}]}}.
-simple(L, Var, Init, Line) ->
- {tuple,L,[?A(simple_v1),?A(Var),Init,?I(Line)]}.
+simple(L, Var, Init, Anno) ->
+ {tuple,L,[?A(simple_v1),?A(Var),Init,abstr(loc(Anno), Anno)]}.
clauses([{QId,{QIVs,{QualData,GoI,S}}} | QCs], RL, Fun, Go, NGV, E, IVs,St) ->
?DEBUG("QIVs = ~p~n", [QIVs]),
@@ -2426,19 +2529,22 @@ aux_var(Name, LcN, QN, N, AllVars) ->
qlc:aux_name(lists:concat([Name, LcN, '_', QN, '_']), N, AllVars).
no_compiler_warning(L) ->
- erl_parse:set_line(L, fun(Line) -> -abs(Line) end).
-
-abs_loc(L) ->
- loc(erl_parse:set_line(L, fun(Line) -> abs(Line) end)).
+ Anno = erl_anno:new(L),
+ erl_anno:set_generated(true, Anno).
-loc(L) ->
- {location,Location} = erl_parse:get_attribute(L, location),
- Location.
+loc(A) ->
+ erl_anno:location(A).
list2op([E], _Op) ->
E;
list2op([E | Es], Op) ->
- {op,0,Op,E,list2op(Es, Op)}.
+ {op,anno0(),Op,E,list2op(Es, Op)}.
+
+anno0() ->
+ erl_anno:new(0).
+
+anno1() ->
+ erl_anno:new(1).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -2491,13 +2597,61 @@ qlcmf(T, _F, _Imp, A, No) ->
occ_vars(E) ->
qlc:var_fold(fun({var,_L,V}) -> V end, [], E).
+%% Every Anno is replaced by a unique number. The number is used in a
+%% table that holds data about the abstract node where Anno resides.
+%% In particular, the original location is kept there, so that the
+%% original abstract code can be re-created.
+save_anno(Abstr, NodeInfo) ->
+ F = fun(Anno) ->
+ N = next_slot(NodeInfo),
+ Location = erl_anno:location(Anno),
+ Data = {N, #{location => Location}},
+ true = ets:insert(NodeInfo, Data),
+ erl_anno:new(N)
+ end,
+ map_anno(F, Abstr).
+
+next_slot(T) ->
+ I = ets:update_counter(T, var_n, 1),
+ case ets:lookup(T, I) of
+ [] ->
+ I;
+ _ ->
+ next_slot(T)
+ end.
+
+restore_anno(Abstr, NodeInfo) ->
+ F = fun(Anno) ->
+ Location = erl_anno:location(Anno),
+ case ets:lookup(NodeInfo, Location) of
+ [{Location, Data}] ->
+ OrigLocation = maps:get(location, Data),
+ erl_anno:set_location(OrigLocation, Anno);
+ [{Location}] -> % generated code
+ Anno;
+ [] ->
+ Anno
+ end
+ end,
+ map_anno(F, Abstr).
+
+restore_loc(Location, #state{node_info = NodeInfo}) ->
+ case ets:lookup(NodeInfo, Location) of
+ [{Location, #{location := OrigLocation}}] ->
+ OrigLocation;
+ [{Location}] ->
+ Location;
+ [] ->
+ Location
+ end.
+
no_shadows(Forms0, State) ->
%% Variables that may shadow other variables are introduced in
%% LCs and Funs. Such variables (call them SV, Shadowing
%% Variables) are now renamed. Each (new) occurrence in a pattern
%% is assigned an index (integer), unique in the file.
%%
- %% The state {LastIndex,ActiveVars,UsedVars,AllVars,Singletons}
+ %% The state {LastIndex,ActiveVars,UsedVars,AllVars,Singletons,State}
%% holds the last index used for each SV (LastIndex), the SVs in
%% the current scope (ActiveVars), used SVs (UsedVars, the indexed
%% name is the key), all variables occurring in the file
@@ -2507,16 +2661,15 @@ no_shadows(Forms0, State) ->
%% the indexed name of an SV occurs in the file, next index is
%% tried (to avoid mixing up introduced names with existing ones).
%%
- %% The original names of variables are kept in the line number
- %% position of the abstract code: {var, {nos, OriginalName, L},
- %% NewName}. undo_no_shadows/1 re-creates the original code.
+ %% The original names of variables are kept in a table in State.
+ %% undo_no_shadows/2 re-creates the original code.
AllVars = sets:from_list(ordsets:to_list(qlc:vars(Forms0))),
?DEBUG("nos AllVars = ~p~n", [sets:to_list(AllVars)]),
VFun = fun(_Id, LC, Vs) -> nos(LC, Vs) end,
LI = ets:new(?APIMOD,[]),
UV = ets:new(?APIMOD,[]),
D0 = dict:new(),
- S1 = {LI, D0, UV, AllVars, []},
+ S1 = {LI, D0, UV, AllVars, [], State},
_ = qlc_mapfold(VFun, S1, Forms0, State),
?DEBUG("UsedIntroVars = ~p~n", [ets:match_object(UV, '_')]),
Singletons = ets:select(UV, ets:fun2ms(fun({K,0}) -> K end)),
@@ -2524,7 +2677,7 @@ no_shadows(Forms0, State) ->
true = ets:delete_all_objects(LI),
true = ets:delete_all_objects(UV),
%% Do it again, this time we know which variables are singletons.
- S2 = {LI, D0, UV, AllVars, Singletons},
+ S2 = {LI, D0, UV, AllVars, Singletons, State},
{Forms,_} = qlc_mapfold(VFun, S2, Forms0, State),
true = ets:delete(LI),
true = ets:delete(UV),
@@ -2568,11 +2721,11 @@ nos({lc,L,E0,Qs0}, S) ->
{Qs, S1} = lists:mapfoldl(F, S, Qs0),
{E, _} = nos(E0, S1),
{{lc,L,E,Qs}, S};
-nos({var,L,V}=Var, {_LI,Vs,UV,_A,_Sg}=S) when V =/= '_' ->
+nos({var,L,V}=Var, {_LI,Vs,UV,_A,_Sg,State}=S) when V =/= '_' ->
case used_var(V, Vs, UV) of
{true, VN} ->
- NL = nos_var(L, V),
- {{var,NL,VN}, S};
+ nos_var(L, V, State),
+ {{var,L,VN}, S};
false ->
{Var, S}
end;
@@ -2590,7 +2743,7 @@ nos_pattern([P0 | Ps0], S0, PVs0) ->
{P, S1, PVs1} = nos_pattern(P0, S0, PVs0),
{Ps, S, PVs} = nos_pattern(Ps0, S1, PVs1),
{[P | Ps], S, PVs};
-nos_pattern({var,L,V}, {LI,Vs0,UV,A,Sg}, PVs0) when V =/= '_' ->
+nos_pattern({var,L,V}, {LI,Vs0,UV,A,Sg,State}, PVs0) when V =/= '_' ->
{Name, Vs, PVs} =
case lists:keyfind(V, 1, PVs0) of
{V, VN} ->
@@ -2604,16 +2757,25 @@ nos_pattern({var,L,V}, {LI,Vs0,UV,A,Sg}, PVs0) when V =/= '_' ->
end,
{N, Vs1, [{V,VN} | PVs0]}
end,
- NL = nos_var(L, V),
- {{var,NL,Name}, {LI,Vs,UV,A,Sg}, PVs};
+ nos_var(L, V, State),
+ {{var,L,Name}, {LI,Vs,UV,A,Sg,State}, PVs};
nos_pattern(T, S0, PVs0) when is_tuple(T) ->
{TL, S, PVs} = nos_pattern(tuple_to_list(T), S0, PVs0),
{list_to_tuple(TL), S, PVs};
nos_pattern(T, S, PVs) ->
{T, S, PVs}.
-nos_var(L, Name) ->
- erl_parse:set_line(L, fun(Line) -> {nos,Name,Line} end).
+nos_var(Anno, Name, State) ->
+ NodeInfo = State#state.node_info,
+ Location = erl_anno:location(Anno),
+ case ets:lookup(NodeInfo, Location) of
+ [{Location, #{name := _}}] ->
+ true;
+ [{Location, Data}] ->
+ true = ets:insert(NodeInfo, {Location, Data#{name => Name}});
+ [] -> % cannot happen
+ true
+ end.
used_var(V, Vs, UV) ->
case dict:find(V, Vs) of
@@ -2638,69 +2800,30 @@ next_var(V, Vs, AllVars, LI, UV) ->
{VN, NVs}
end.
-undo_no_shadows(E) ->
- var_map(fun undo_no_shadows1/1, E).
-
-undo_no_shadows1({var, L, _}=Var) ->
- case erl_parse:get_attribute(L, line) of
- {line,{nos,V,_VL}} ->
- NL = erl_parse:set_line(L, fun({nos,_V,VL}) -> VL end),
- undo_no_shadows1({var, NL, V});
- _Else ->
- Var
- end.
+undo_no_shadows(E, State) ->
+ var_map(fun(Anno) -> undo_no_shadows1(Anno, State) end, E).
-restore_line_numbers(E) ->
- var_map(fun restore_line_numbers1/1, E).
-
-restore_line_numbers1({var, L, V}=Var) ->
- case erl_parse:get_attribute(L, line) of
- {line,{nos,_,_}} ->
- NL = erl_parse:set_line(L, fun({nos,_V,VL}) -> VL end),
- restore_line_numbers1({var, NL, V});
- _Else ->
+undo_no_shadows1({var, Anno, _}=Var, State) ->
+ Location = erl_anno:location(Anno),
+ NodeInfo = State#state.node_info,
+ case ets:lookup(NodeInfo, Location) of
+ [{Location, #{name := Name}}] ->
+ {var, Anno, Name};
+ _ ->
Var
end.
%% QLC identifier.
%% The first one encountered in the file has No=1.
-make_lcid(Attrs, No) when is_integer(No), No > 0 ->
- F = fun(Line) when is_integer(Line), Line < (1 bsl ?MAX_NUM_OF_LINES) ->
- sgn(Line) * ((No bsl ?MAX_NUM_OF_LINES) + sgn(Line) * Line)
- end,
- erl_parse:set_line(Attrs, F).
-
-is_lcid(Attrs) ->
- try
- {line,Id} = erl_parse:get_attribute(Attrs, line),
- is_integer(Id) andalso (abs(Id) > (1 bsl ?MAX_NUM_OF_LINES))
- catch _:_ ->
- false
- end.
-
-get_lcid_no(IdAttrs) ->
- {line,Id} = erl_parse:get_attribute(IdAttrs, line),
- abs(Id) bsr ?MAX_NUM_OF_LINES.
-
-get_lcid_line(IdAttrs) ->
- {line,Id} = erl_parse:get_attribute(IdAttrs, line),
- sgn(Id) * (abs(Id) band ((1 bsl ?MAX_NUM_OF_LINES) - 1)).
+make_lcid(Anno, No) when is_integer(No), No > 0 ->
+ {No, erl_anno:line(Anno)}.
-sgn(X) when X >= 0 ->
- 1;
-sgn(X) when X < 0 ->
- -1.
+get_lcid_no({No, _Line}) ->
+ No.
-seq(S, E) when S - E =:= 1 ->
- [];
-seq(S, E) ->
- lists:seq(S, E).
-
-sublist(_, 0) ->
- [];
-sublist(L, N) ->
- lists:sublist(L, N).
+get_lcid_line({_No, Line}) ->
+ Line.
qid(LCId, No) ->
#qid{no = No, lcid = LCId}.
@@ -2749,6 +2872,14 @@ var_mapfold(F, A0, [E0 | Es0]) ->
var_mapfold(_F, A, E) ->
{E, A}.
+map_anno(F, AbstrList) when is_list(AbstrList) ->
+ [map_anno1(F, Abstr) || Abstr <- AbstrList];
+map_anno(F, Abstr) ->
+ map_anno1(F, Abstr).
+
+map_anno1(F, Abstr) ->
+ erl_parse:map_anno(F, Abstr).
+
family_list(L) ->
sofs:to_external(family(L)).
diff --git a/lib/stdlib/src/queue.erl b/lib/stdlib/src/queue.erl
index 472d503b99..d4d1904886 100644
--- a/lib/stdlib/src/queue.erl
+++ b/lib/stdlib/src/queue.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -48,7 +49,7 @@
-opaque queue(Item) :: {list(Item), list(Item)}.
--opaque queue() :: queue(_).
+-type queue() :: queue(_).
%% Creation, inspection and conversion
diff --git a/lib/stdlib/src/rand.erl b/lib/stdlib/src/rand.erl
new file mode 100644
index 0000000000..93409d95df
--- /dev/null
+++ b/lib/stdlib/src/rand.erl
@@ -0,0 +1,596 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2015-2016. 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%
+%%
+%% =====================================================================
+%% Multiple PRNG module for Erlang/OTP
+%% Copyright (c) 2015 Kenji Rikitake
+%% =====================================================================
+
+-module(rand).
+
+-export([seed_s/1, seed_s/2, seed/1, seed/2,
+ export_seed/0, export_seed_s/1,
+ uniform/0, uniform/1, uniform_s/1, uniform_s/2,
+ normal/0, normal_s/1
+ ]).
+
+-compile({inline, [exs64_next/1, exsplus_next/1,
+ exs1024_next/1, exs1024_calc/2,
+ get_52/1, normal_kiwi/1]}).
+
+-define(DEFAULT_ALG_HANDLER, exsplus).
+-define(SEED_DICT, rand_seed).
+
+%% =====================================================================
+%% Types
+%% =====================================================================
+
+%% This depends on the algorithm handler function
+-type alg_seed() :: exs64_state() | exsplus_state() | exs1024_state().
+%% This is the algorithm handler function within this module
+-type alg_handler() :: #{type := alg(),
+ max := integer(),
+ next := fun(),
+ uniform := fun(),
+ uniform_n := fun()}.
+
+%% Internal state
+-opaque state() :: {alg_handler(), alg_seed()}.
+-type alg() :: exs64 | exsplus | exs1024.
+-opaque export_state() :: {alg(), alg_seed()}.
+-export_type([alg/0, state/0, export_state/0]).
+
+%% =====================================================================
+%% API
+%% =====================================================================
+
+%% Return algorithm and seed so that RNG state can be recreated with seed/1
+-spec export_seed() -> undefined | export_state().
+export_seed() ->
+ case get(?SEED_DICT) of
+ {#{type:=Alg}, Seed} -> {Alg, Seed};
+ _ -> undefined
+ end.
+
+-spec export_seed_s(state()) -> export_state().
+export_seed_s({#{type:=Alg}, Seed}) -> {Alg, Seed}.
+
+%% seed(Alg) seeds RNG with runtime dependent values
+%% and return the NEW state
+
+%% seed({Alg,Seed}) setup RNG with a previously exported seed
+%% and return the NEW state
+
+-spec seed(AlgOrExpState::alg() | export_state()) -> state().
+seed(Alg) ->
+ R = seed_s(Alg),
+ _ = seed_put(R),
+ R.
+
+-spec seed_s(AlgOrExpState::alg() | export_state()) -> state().
+seed_s(Alg) when is_atom(Alg) ->
+ seed_s(Alg, {erlang:phash2([{node(),self()}]),
+ erlang:system_time(),
+ erlang:unique_integer()});
+seed_s({Alg0, Seed}) ->
+ {Alg,_SeedFun} = mk_alg(Alg0),
+ {Alg, Seed}.
+
+%% seed/2: seeds RNG with the algorithm and given values
+%% and returns the NEW state.
+
+-spec seed(Alg :: alg(), {integer(), integer(), integer()}) -> state().
+seed(Alg0, S0) ->
+ State = seed_s(Alg0, S0),
+ _ = seed_put(State),
+ State.
+
+-spec seed_s(Alg :: alg(), {integer(), integer(), integer()}) -> state().
+seed_s(Alg0, S0 = {_, _, _}) ->
+ {Alg, Seed} = mk_alg(Alg0),
+ AS = Seed(S0),
+ {Alg, AS}.
+
+%%% uniform/0, uniform/1, uniform_s/1, uniform_s/2 are all
+%%% uniformly distributed random numbers.
+
+%% uniform/0: returns a random float X where 0.0 < X < 1.0,
+%% updating the state in the process dictionary.
+
+-spec uniform() -> X::float().
+uniform() ->
+ {X, Seed} = uniform_s(seed_get()),
+ _ = seed_put(Seed),
+ X.
+
+%% uniform/1: given an integer N >= 1,
+%% uniform/1 returns a random integer X where 1 =< X =< N,
+%% updating the state in the process dictionary.
+
+-spec uniform(N :: pos_integer()) -> X::pos_integer().
+uniform(N) ->
+ {X, Seed} = uniform_s(N, seed_get()),
+ _ = seed_put(Seed),
+ X.
+
+%% uniform_s/1: given a state, uniform_s/1
+%% returns a random float X where 0.0 < X < 1.0,
+%% and a new state.
+
+-spec uniform_s(state()) -> {X::float(), NewS :: state()}.
+uniform_s(State = {#{uniform:=Uniform}, _}) ->
+ Uniform(State).
+
+%% uniform_s/2: given an integer N >= 1 and a state, uniform_s/2
+%% uniform_s/2 returns a random integer X where 1 =< X =< N,
+%% and a new state.
+
+-spec uniform_s(N::pos_integer(), state()) -> {X::pos_integer(), NewS::state()}.
+uniform_s(N, State = {#{uniform_n:=Uniform, max:=Max}, _})
+ when 0 < N, N =< Max ->
+ Uniform(N, State);
+uniform_s(N, State0 = {#{uniform:=Uniform}, _})
+ when is_integer(N), 0 < N ->
+ {F, State} = Uniform(State0),
+ {trunc(F * N) + 1, State}.
+
+%% normal/0: returns a random float with standard normal distribution
+%% updating the state in the process dictionary.
+
+-spec normal() -> float().
+normal() ->
+ {X, Seed} = normal_s(seed_get()),
+ _ = seed_put(Seed),
+ X.
+
+%% normal_s/1: returns a random float with standard normal distribution
+%% The Ziggurat Method for generating random variables - Marsaglia and Tsang
+%% Paper and reference code: http://www.jstatsoft.org/v05/i08/
+
+-spec normal_s(state()) -> {float(), NewS :: state()}.
+normal_s(State0) ->
+ {Sign, R, State} = get_52(State0),
+ Idx = R band 16#FF,
+ Idx1 = Idx+1,
+ {Ki, Wi} = normal_kiwi(Idx1),
+ X = R * Wi,
+ case R < Ki of
+ %% Fast path 95% of the time
+ true when Sign =:= 0 -> {X, State};
+ true -> {-X, State};
+ %% Slow path
+ false when Sign =:= 0 -> normal_s(Idx, Sign, X, State);
+ false -> normal_s(Idx, Sign, -X, State)
+ end.
+
+%% =====================================================================
+%% Internal functions
+
+-define(UINT21MASK, 16#00000000001fffff).
+-define(UINT32MASK, 16#00000000ffffffff).
+-define(UINT33MASK, 16#00000001ffffffff).
+-define(UINT39MASK, 16#0000007fffffffff).
+-define(UINT58MASK, 16#03ffffffffffffff).
+-define(UINT64MASK, 16#ffffffffffffffff).
+
+-type uint64() :: 0..16#ffffffffffffffff.
+-type uint58() :: 0..16#03ffffffffffffff.
+
+-spec seed_put(state()) -> undefined | state().
+seed_put(Seed) ->
+ put(?SEED_DICT, Seed).
+
+seed_get() ->
+ case get(?SEED_DICT) of
+ undefined -> seed(?DEFAULT_ALG_HANDLER);
+ Old -> Old % no type checking here
+ end.
+
+%% Setup alg record
+mk_alg(exs64) ->
+ {#{type=>exs64, max=>?UINT64MASK, next=>fun exs64_next/1,
+ uniform=>fun exs64_uniform/1, uniform_n=>fun exs64_uniform/2},
+ fun exs64_seed/1};
+mk_alg(exsplus) ->
+ {#{type=>exsplus, max=>?UINT58MASK, next=>fun exsplus_next/1,
+ uniform=>fun exsplus_uniform/1, uniform_n=>fun exsplus_uniform/2},
+ fun exsplus_seed/1};
+mk_alg(exs1024) ->
+ {#{type=>exs1024, max=>?UINT64MASK, next=>fun exs1024_next/1,
+ uniform=>fun exs1024_uniform/1, uniform_n=>fun exs1024_uniform/2},
+ fun exs1024_seed/1}.
+
+%% =====================================================================
+%% exs64 PRNG: Xorshift64*
+%% Algorithm by Sebastiano Vigna
+%% Reference URL: http://xorshift.di.unimi.it/
+%% =====================================================================
+
+-type exs64_state() :: uint64().
+
+exs64_seed({A1, A2, A3}) ->
+ {V1, _} = exs64_next(((A1 band ?UINT32MASK) * 4294967197 + 1)),
+ {V2, _} = exs64_next(((A2 band ?UINT32MASK) * 4294967231 + 1)),
+ {V3, _} = exs64_next(((A3 band ?UINT32MASK) * 4294967279 + 1)),
+ ((V1 * V2 * V3) rem (?UINT64MASK - 1)) + 1.
+
+%% Advance xorshift64* state for one step and generate 64bit unsigned integer
+-spec exs64_next(exs64_state()) -> {uint64(), exs64_state()}.
+exs64_next(R) ->
+ R1 = R bxor (R bsr 12),
+ R2 = R1 bxor ((R1 band ?UINT39MASK) bsl 25),
+ R3 = R2 bxor (R2 bsr 27),
+ {(R3 * 2685821657736338717) band ?UINT64MASK, R3}.
+
+exs64_uniform({Alg, R0}) ->
+ {V, R1} = exs64_next(R0),
+ {V / 18446744073709551616, {Alg, R1}}.
+
+exs64_uniform(Max, {Alg, R}) ->
+ {V, R1} = exs64_next(R),
+ {(V rem Max) + 1, {Alg, R1}}.
+
+%% =====================================================================
+%% exsplus PRNG: Xorshift116+
+%% Algorithm by Sebastiano Vigna
+%% Reference URL: http://xorshift.di.unimi.it/
+%% 58 bits fits into an immediate on 64bits erlang and is thus much faster.
+%% Modification of the original Xorshift128+ algorithm to 116
+%% by Sebastiano Vigna, a lot of thanks for his help and work.
+%% =====================================================================
+-type exsplus_state() :: nonempty_improper_list(uint58(), uint58()).
+
+-dialyzer({no_improper_lists, exsplus_seed/1}).
+
+exsplus_seed({A1, A2, A3}) ->
+ {_, R1} = exsplus_next([(((A1 * 4294967197) + 1) band ?UINT58MASK)|
+ (((A2 * 4294967231) + 1) band ?UINT58MASK)]),
+ {_, R2} = exsplus_next([(((A3 * 4294967279) + 1) band ?UINT58MASK)|
+ tl(R1)]),
+ R2.
+
+-dialyzer({no_improper_lists, exsplus_next/1}).
+
+%% Advance xorshift116+ state for one step and generate 58bit unsigned integer
+-spec exsplus_next(exsplus_state()) -> {uint58(), exsplus_state()}.
+exsplus_next([S1|S0]) ->
+ %% Note: members s0 and s1 are swapped here
+ S11 = (S1 bxor (S1 bsl 24)) band ?UINT58MASK,
+ S12 = S11 bxor S0 bxor (S11 bsr 11) bxor (S0 bsr 41),
+ {(S0 + S12) band ?UINT58MASK, [S0|S12]}.
+
+exsplus_uniform({Alg, R0}) ->
+ {I, R1} = exsplus_next(R0),
+ {I / (?UINT58MASK+1), {Alg, R1}}.
+
+exsplus_uniform(Max, {Alg, R}) ->
+ {V, R1} = exsplus_next(R),
+ {(V rem Max) + 1, {Alg, R1}}.
+
+%% =====================================================================
+%% exs1024 PRNG: Xorshift1024*
+%% Algorithm by Sebastiano Vigna
+%% Reference URL: http://xorshift.di.unimi.it/
+%% =====================================================================
+
+-type exs1024_state() :: {list(uint64()), list(uint64())}.
+
+exs1024_seed({A1, A2, A3}) ->
+ B1 = (((A1 band ?UINT21MASK) + 1) * 2097131) band ?UINT21MASK,
+ B2 = (((A2 band ?UINT21MASK) + 1) * 2097133) band ?UINT21MASK,
+ B3 = (((A3 band ?UINT21MASK) + 1) * 2097143) band ?UINT21MASK,
+ {exs1024_gen1024((B1 bsl 43) bor (B2 bsl 22) bor (B3 bsl 1) bor 1),
+ []}.
+
+%% Generate a list of 16 64-bit element list
+%% of the xorshift64* random sequence
+%% from a given 64-bit seed.
+%% Note: dependent on exs64_next/1
+-spec exs1024_gen1024(uint64()) -> list(uint64()).
+exs1024_gen1024(R) ->
+ exs1024_gen1024(16, R, []).
+
+exs1024_gen1024(0, _, L) ->
+ L;
+exs1024_gen1024(N, R, L) ->
+ {X, R2} = exs64_next(R),
+ exs1024_gen1024(N - 1, R2, [X|L]).
+
+%% Calculation of xorshift1024*.
+%% exs1024_calc(S0, S1) -> {X, NS1}.
+%% X: random number output
+-spec exs1024_calc(uint64(), uint64()) -> {uint64(), uint64()}.
+exs1024_calc(S0, S1) ->
+ S11 = S1 bxor ((S1 band ?UINT33MASK) bsl 31),
+ S12 = S11 bxor (S11 bsr 11),
+ S01 = S0 bxor (S0 bsr 30),
+ NS1 = S01 bxor S12,
+ {(NS1 * 1181783497276652981) band ?UINT64MASK, NS1}.
+
+%% Advance xorshift1024* state for one step and generate 64bit unsigned integer
+-spec exs1024_next(exs1024_state()) -> {uint64(), exs1024_state()}.
+exs1024_next({[S0,S1|L3], RL}) ->
+ {X, NS1} = exs1024_calc(S0, S1),
+ {X, {[NS1|L3], [S0|RL]}};
+exs1024_next({[H], RL}) ->
+ NL = [H|lists:reverse(RL)],
+ exs1024_next({NL, []}).
+
+exs1024_uniform({Alg, R0}) ->
+ {V, R1} = exs1024_next(R0),
+ {V / 18446744073709551616, {Alg, R1}}.
+
+exs1024_uniform(Max, {Alg, R}) ->
+ {V, R1} = exs1024_next(R),
+ {(V rem Max) + 1, {Alg, R1}}.
+
+%% =====================================================================
+%% Ziggurat cont
+%% =====================================================================
+-define(NOR_R, 3.6541528853610087963519472518).
+-define(NOR_INV_R, 1/?NOR_R).
+
+%% return a {sign, Random51bits, State}
+get_52({Alg=#{next:=Next}, S0}) ->
+ {Int,S1} = Next(S0),
+ {((1 bsl 51) band Int), Int band ((1 bsl 51)-1), {Alg, S1}}.
+
+%% Slow path
+normal_s(0, Sign, X0, State0) ->
+ {U0, S1} = uniform_s(State0),
+ X = -?NOR_INV_R*math:log(U0),
+ {U1, S2} = uniform_s(S1),
+ Y = -math:log(U1),
+ case Y+Y > X*X of
+ false ->
+ normal_s(0, Sign, X0, S2);
+ true when Sign =:= 0 ->
+ {?NOR_R + X, S2};
+ true ->
+ {-?NOR_R - X, S2}
+ end;
+normal_s(Idx, _Sign, X, State0) ->
+ Fi2 = normal_fi(Idx+1),
+ {U0, S1} = uniform_s(State0),
+ case ((normal_fi(Idx) - Fi2)*U0 + Fi2) < math:exp(-0.5*X*X) of
+ true -> {X, S1};
+ false -> normal_s(S1)
+ end.
+
+%% Tables for generating normal_s
+%% ki is zipped with wi (slightly faster)
+normal_kiwi(Indx) ->
+ element(Indx,
+ {{2104047571236786,1.736725412160263e-15}, {0,9.558660351455634e-17},
+ {1693657211986787,1.2708704834810623e-16},{1919380038271141,1.4909740962495474e-16},
+ {2015384402196343,1.6658733631586268e-16},{2068365869448128,1.8136120810119029e-16},
+ {2101878624052573,1.9429720153135588e-16},{2124958784102998,2.0589500628482093e-16},
+ {2141808670795147,2.1646860576895422e-16},{2154644611568301,2.2622940392218116e-16},
+ {2164744887587275,2.353271891404589e-16},{2172897953696594,2.438723455742877e-16},
+ {2179616279372365,2.5194879829274225e-16},{2185247251868649,2.5962199772528103e-16},
+ {2190034623107822,2.6694407473648285e-16},{2194154434521197,2.7395729685142446e-16},
+ {2197736978774660,2.8069646002484804e-16},{2200880740891961,2.871905890411393e-16},
+ {2203661538010620,2.9346417484728883e-16},{2206138681109102,2.9953809336782113e-16},
+ {2208359231806599,3.054303000719244e-16},{2210361007258210,3.111563633892157e-16},
+ {2212174742388539,3.1672988018581815e-16},{2213825672704646,3.2216280350549905e-16},
+ {2215334711002614,3.274657040793975e-16},{2216719334487595,3.326479811684171e-16},
+ {2217994262139172,3.377180341735323e-16},{2219171977965032,3.4268340353119356e-16},
+ {2220263139538712,3.475508873172976e-16},{2221276900117330,3.523266384600203e-16},
+ {2222221164932930,3.5701624633953494e-16},{2223102796829069,3.616248057159834e-16},
+ {2223927782546658,3.661569752965354e-16},{2224701368170060,3.7061702777236077e-16},
+ {2225428170204312,3.75008892787478e-16},{2226112267248242,3.7933619401549554e-16},
+ {2226757276105256,3.836022812967728e-16},{2227366415328399,3.8781025861250247e-16},
+ {2227942558554684,3.919630085325768e-16},{2228488279492521,3.9606321366256378e-16},
+ {2229005890047222,4.001133755254669e-16},{2229497472775193,4.041158312414333e-16},
+ {2229964908627060,4.080727683096045e-16},{2230409900758597,4.119862377480744e-16},
+ {2230833995044585,4.1585816580828064e-16},{2231238597816133,4.1969036444740733e-16},
+ {2231624991250191,4.234845407152071e-16},{2231994346765928,4.272423051889976e-16},
+ {2232347736722750,4.309651795716294e-16},{2232686144665934,4.346546035512876e-16},
+ {2233010474325959,4.383119410085457e-16},{2233321557544881,4.4193848564470665e-16},
+ {2233620161276071,4.455354660957914e-16},{2233906993781271,4.491040505882875e-16},
+ {2234182710130335,4.52645351185714e-16},{2234447917093496,4.561604276690038e-16},
+ {2234703177503020,4.596502910884941e-16},{2234949014150181,4.631159070208165e-16},
+ {2235185913274316,4.665581985600875e-16},{2235414327692884,4.699780490694195e-16},
+ {2235634679614920,4.733763047158324e-16},{2235847363174595,4.767537768090853e-16},
+ {2236052746716837,4.8011124396270155e-16},{2236251174862869,4.834494540935008e-16},
+ {2236442970379967,4.867691262742209e-16},{2236628435876762,4.900709524522994e-16},
+ {2236807855342765,4.933555990465414e-16},{2236981495548562,4.966237084322178e-16},
+ {2237149607321147,4.998759003240909e-16},{2237312426707209,5.031127730659319e-16},
+ {2237470176035652,5.0633490483427195e-16},{2237623064889403,5.095428547633892e-16},
+ {2237771290995388,5.127371639978797e-16},{2237915041040597,5.159183566785736e-16},
+ {2238054491421305,5.190869408670343e-16},{2238189808931712,5.222434094134042e-16},
+ {2238321151397660,5.253882407719454e-16},{2238448668260432,5.285218997682382e-16},
+ {2238572501115169,5.316448383216618e-16},{2238692784207942,5.34757496126473e-16},
+ {2238809644895133,5.378603012945235e-16},{2238923204068402,5.409536709623993e-16},
+ {2239033576548190,5.440380118655467e-16},{2239140871448443,5.471137208817361e-16},
+ {2239245192514958,5.501811855460336e-16},{2239346638439541,5.532407845392784e-16},
+ {2239445303151952,5.56292888151909e-16},{2239541276091442,5.593378587248462e-16},
+ {2239634642459498,5.623760510690043e-16},{2239725483455293,5.65407812864896e-16},
+ {2239813876495186,5.684334850436814e-16},{2239899895417494,5.714534021509204e-16},
+ {2239983610673676,5.744678926941961e-16},{2240065089506935,5.774772794756965e-16},
+ {2240144396119183,5.804818799107686e-16},{2240221591827230,5.834820063333892e-16},
+ {2240296735208969,5.864779662894365e-16},{2240369882240293,5.894700628185872e-16},
+ {2240441086423386,5.924585947256134e-16},{2240510398907004,5.95443856841806e-16},
+ {2240577868599305,5.984261402772028e-16},{2240643542273726,6.014057326642664e-16},
+ {2240707464668391,6.043829183936125e-16},{2240769678579486,6.073579788423606e-16},
+ {2240830224948980,6.103311925956439e-16},{2240889142947082,6.133028356617911e-16},
+ {2240946470049769,6.162731816816596e-16},{2241002242111691,6.192425021325847e-16},
+ {2241056493434746,6.222110665273788e-16},{2241109256832602,6.251791426088e-16},
+ {2241160563691400,6.281469965398895e-16},{2241210444026879,6.311148930905604e-16},
+ {2241258926538122,6.34083095820806e-16},{2241306038658137,6.370518672608815e-16},
+ {2241351806601435,6.400214690888025e-16},{2241396255408788,6.429921623054896e-16},
+ {2241439408989313,6.459642074078832e-16},{2241481290160038,6.489378645603397e-16},
+ {2241521920683062,6.519133937646159e-16},{2241561321300462,6.548910550287415e-16},
+ {2241599511767028,6.578711085350741e-16},{2241636510880960,6.608538148078259e-16},
+ {2241672336512612,6.638394348803506e-16},{2241707005631362,6.668282304624746e-16},
+ {2241740534330713,6.698204641081558e-16},{2241772937851689,6.728163993837531e-16},
+ {2241804230604585,6.758163010371901e-16},{2241834426189161,6.78820435168298e-16},
+ {2241863537413311,6.818290694006254e-16},{2241891576310281,6.848424730550038e-16},
+ {2241918554154466,6.878609173251664e-16},{2241944481475843,6.908846754557169e-16},
+ {2241969368073071,6.939140229227569e-16},{2241993223025298,6.969492376174829e-16},
+ {2242016054702685,6.999906000330764e-16},{2242037870775710,7.030383934552151e-16},
+ {2242058678223225,7.060929041565482e-16},{2242078483339331,7.091544215954873e-16},
+ {2242097291739040,7.122232386196779e-16},{2242115108362774,7.152996516745303e-16},
+ {2242131937479672,7.183839610172063e-16},{2242147782689725,7.214764709364707e-16},
+ {2242162646924736,7.245774899788387e-16},{2242176532448092,7.276873311814693e-16},
+ {2242189440853337,7.308063123122743e-16},{2242201373061537,7.339347561177405e-16},
+ {2242212329317416,7.370729905789831e-16},{2242222309184237,7.4022134917658e-16},
+ {2242231311537397,7.433801711647648e-16},{2242239334556717,7.465498018555889e-16},
+ {2242246375717369,7.497305929136979e-16},{2242252431779415,7.529229026624058e-16},
+ {2242257498775893,7.561270964017922e-16},{2242261571999416,7.5934354673958895e-16},
+ {2242264645987196,7.625726339356756e-16},{2242266714504453,7.658147462610487e-16},
+ {2242267770526109,7.690702803721919e-16},{2242267806216711,7.723396417018299e-16},
+ {2242266812908462,7.756232448671174e-16},{2242264781077289,7.789215140963852e-16},
+ {2242261700316818,7.822348836756411e-16},{2242257559310145,7.855637984161084e-16},
+ {2242252345799276,7.889087141441755e-16},{2242246046552082,7.922700982152271e-16},
+ {2242238647326615,7.956484300529366e-16},{2242230132832625,7.99044201715713e-16},
+ {2242220486690076,8.024579184921259e-16},{2242209691384458,8.058900995272657e-16},
+ {2242197728218684,8.093412784821501e-16},{2242184577261310,8.128120042284501e-16},
+ {2242170217290819,8.163028415809877e-16},{2242154625735679,8.198143720706533e-16},
+ {2242137778609839,8.23347194760605e-16},{2242119650443327,8.26901927108847e-16},
+ {2242100214207556,8.304792058805374e-16},{2242079441234906,8.340796881136629e-16},
+ {2242057301132135,8.377040521420222e-16},{2242033761687079,8.413529986798028e-16},
+ {2242008788768107,8.450272519724097e-16},{2241982346215682,8.487275610186155e-16},
+ {2241954395725356,8.524547008695596e-16},{2241924896721443,8.562094740106233e-16},
+ {2241893806220517,8.599927118327665e-16},{2241861078683830,8.638052762005259e-16},
+ {2241826665857598,8.676480611245582e-16},{2241790516600041,8.715219945473698e-16},
+ {2241752576693881,8.754280402517175e-16},{2241712788642916,8.793671999021043e-16},
+ {2241671091451078,8.833405152308408e-16},{2241627420382235,8.873490703813135e-16},
+ {2241581706698773,8.913939944224086e-16},{2241533877376767,8.954764640495068e-16},
+ {2241483854795281,8.9959770648911e-16},{2241431556397035,9.037590026260118e-16},
+ {2241376894317345,9.079616903740068e-16},{2241319774977817,9.122071683134846e-16},
+ {2241260098640860,9.164968996219135e-16},{2241197758920538,9.208324163262308e-16},
+ {2241132642244704,9.252153239095693e-16},{2241064627262652,9.296473063086417e-16},
+ {2240993584191742,9.341301313425265e-16},{2240919374095536,9.38665656618666e-16},
+ {2240841848084890,9.432558359676707e-16},{2240760846432232,9.479027264651738e-16},
+ {2240676197587784,9.526084961066279e-16},{2240587717084782,9.57375432209745e-16},
+ {2240495206318753,9.622059506294838e-16},{2240398451183567,9.671026058823054e-16},
+ {2240297220544165,9.720681022901626e-16},{2240191264522612,9.771053062707209e-16},
+ {2240080312570155,9.822172599190541e-16},{2239964071293331,9.874071960480671e-16},
+ {2239842221996530,9.926785548807976e-16},{2239714417896699,9.980350026183645e-16},
+ {2239580280957725,1.003480452143618e-15},{2239439398282193,1.0090190861637457e-15},
+ {2239291317986196,1.0146553831467086e-15},{2239135544468203,1.0203941464683124e-15},
+ {2238971532964979,1.0262405372613567e-15},{2238798683265269,1.0322001115486456e-15},
+ {2238616332424351,1.03827886235154e-15},{2238423746288095,1.044483267600047e-15},
+ {2238220109591890,1.0508203448355195e-15},{2238004514345216,1.057297713900989e-15},
+ {2237775946143212,1.06392366906768e-15},{2237533267957822,1.0707072623632994e-15},
+ {2237275200846753,1.0776584002668106e-15},{2237000300869952,1.0847879564403425e-15},
+ {2236706931309099,1.0921079038149563e-15},{2236393229029147,1.0996314701785628e-15},
+ {2236057063479501,1.1073733224935752e-15},{2235695986373246,1.1153497865853155e-15},
+ {2235307169458859,1.1235791107110833e-15},{2234887326941578,1.1320817840164846e-15},
+ {2234432617919447,1.140880924258278e-15},{2233938522519765,1.1500027537839792e-15},
+ {2233399683022677,1.159477189144919e-15},{2232809697779198,1.169338578691096e-15},
+ {2232160850599817,1.17962663529558e-15},{2231443750584641,1.190387629928289e-15},
+ {2230646845562170,1.2016759392543819e-15},{2229755753817986,1.2135560818666897e-15},
+ {2228752329126533,1.2261054417450561e-15},{2227613325162504,1.2394179789163251e-15},
+ {2226308442121174,1.2536093926602567e-15},{2224797391720399,1.268824481425501e-15},
+ {2223025347823832,1.2852479319096109e-15},{2220915633329809,1.3031206634689985e-15},
+ {2218357446087030,1.3227655770195326e-15},{2215184158448668,1.3446300925011171e-15},
+ {2211132412537369,1.3693606835128518e-15},{2205758503851065,1.397943667277524e-15},
+ {2198248265654987,1.4319989869661328e-15},{2186916352102141,1.4744848603597596e-15},
+ {2167562552481814,1.5317872741611144e-15},{2125549880839716,1.6227698675312968e-15}}).
+
+normal_fi(Indx) ->
+ element(Indx,
+ {1.0000000000000000e+00,9.7710170126767082e-01,9.5987909180010600e-01,
+ 9.4519895344229909e-01,9.3206007595922991e-01,9.1999150503934646e-01,
+ 9.0872644005213032e-01,8.9809592189834297e-01,8.8798466075583282e-01,
+ 8.7830965580891684e-01,8.6900868803685649e-01,8.6003362119633109e-01,
+ 8.5134625845867751e-01,8.4291565311220373e-01,8.3471629298688299e-01,
+ 8.2672683394622093e-01,8.1892919160370192e-01,8.1130787431265572e-01,
+ 8.0384948317096383e-01,7.9654233042295841e-01,7.8937614356602404e-01,
+ 7.8234183265480195e-01,7.7543130498118662e-01,7.6863731579848571e-01,
+ 7.6195334683679483e-01,7.5537350650709567e-01,7.4889244721915638e-01,
+ 7.4250529634015061e-01,7.3620759812686210e-01,7.2999526456147568e-01,
+ 7.2386453346862967e-01,7.1781193263072152e-01,7.1183424887824798e-01,
+ 7.0592850133275376e-01,7.0009191813651117e-01,6.9432191612611627e-01,
+ 6.8861608300467136e-01,6.8297216164499430e-01,6.7738803621877308e-01,
+ 6.7186171989708166e-01,6.6639134390874977e-01,6.6097514777666277e-01,
+ 6.5561147057969693e-01,6.5029874311081637e-01,6.4503548082082196e-01,
+ 6.3982027745305614e-01,6.3465179928762327e-01,6.2952877992483625e-01,
+ 6.2445001554702606e-01,6.1941436060583399e-01,6.1442072388891344e-01,
+ 6.0946806492577310e-01,6.0455539069746733e-01,5.9968175261912482e-01,
+ 5.9484624376798689e-01,5.9004799633282545e-01,5.8528617926337090e-01,
+ 5.8055999610079034e-01,5.7586868297235316e-01,5.7121150673525267e-01,
+ 5.6658776325616389e-01,5.6199677581452390e-01,5.5743789361876550e-01,
+ 5.5291049042583185e-01,5.4841396325526537e-01,5.4394773119002582e-01,
+ 5.3951123425695158e-01,5.3510393238045717e-01,5.3072530440366150e-01,
+ 5.2637484717168403e-01,5.2205207467232140e-01,5.1775651722975591e-01,
+ 5.1348772074732651e-01,5.0924524599574761e-01,5.0502866794346790e-01,
+ 5.0083757512614835e-01,4.9667156905248933e-01,4.9253026364386815e-01,
+ 4.8841328470545758e-01,4.8432026942668288e-01,4.8025086590904642e-01,
+ 4.7620473271950547e-01,4.7218153846772976e-01,4.6818096140569321e-01,
+ 4.6420268904817391e-01,4.6024641781284248e-01,4.5631185267871610e-01,
+ 4.5239870686184824e-01,4.4850670150720273e-01,4.4463556539573912e-01,
+ 4.4078503466580377e-01,4.3695485254798533e-01,4.3314476911265209e-01,
+ 4.2935454102944126e-01,4.2558393133802180e-01,4.2183270922949573e-01,
+ 4.1810064983784795e-01,4.1438753404089090e-01,4.1069314827018799e-01,
+ 4.0701728432947315e-01,4.0335973922111429e-01,3.9972031498019700e-01,
+ 3.9609881851583223e-01,3.9249506145931540e-01,3.8890886001878855e-01,
+ 3.8534003484007706e-01,3.8178841087339344e-01,3.7825381724561896e-01,
+ 3.7473608713789086e-01,3.7123505766823922e-01,3.6775056977903225e-01,
+ 3.6428246812900372e-01,3.6083060098964775e-01,3.5739482014578022e-01,
+ 3.5397498080007656e-01,3.5057094148140588e-01,3.4718256395679348e-01,
+ 3.4380971314685055e-01,3.4045225704452164e-01,3.3711006663700588e-01,
+ 3.3378301583071823e-01,3.3047098137916342e-01,3.2717384281360129e-01,
+ 3.2389148237639104e-01,3.2062378495690530e-01,3.1737063802991350e-01,
+ 3.1413193159633707e-01,3.1090755812628634e-01,3.0769741250429189e-01,
+ 3.0450139197664983e-01,3.0131939610080288e-01,2.9815132669668531e-01,
+ 2.9499708779996164e-01,2.9185658561709499e-01,2.8872972848218270e-01,
+ 2.8561642681550159e-01,2.8251659308370741e-01,2.7943014176163772e-01,
+ 2.7635698929566810e-01,2.7329705406857691e-01,2.7025025636587519e-01,
+ 2.6721651834356114e-01,2.6419576399726080e-01,2.6118791913272082e-01,
+ 2.5819291133761890e-01,2.5521066995466168e-01,2.5224112605594190e-01,
+ 2.4928421241852824e-01,2.4633986350126363e-01,2.4340801542275012e-01,
+ 2.4048860594050039e-01,2.3758157443123795e-01,2.3468686187232990e-01,
+ 2.3180441082433859e-01,2.2893416541468023e-01,2.2607607132238020e-01,
+ 2.2323007576391746e-01,2.2039612748015194e-01,2.1757417672433113e-01,
+ 2.1476417525117358e-01,2.1196607630703015e-01,2.0917983462112499e-01,
+ 2.0640540639788071e-01,2.0364274931033485e-01,2.0089182249465656e-01,
+ 1.9815258654577511e-01,1.9542500351413428e-01,1.9270903690358912e-01,
+ 1.9000465167046496e-01,1.8731181422380025e-01,1.8463049242679927e-01,
+ 1.8196065559952254e-01,1.7930227452284767e-01,1.7665532144373500e-01,
+ 1.7401977008183875e-01,1.7139559563750595e-01,1.6878277480121151e-01,
+ 1.6618128576448205e-01,1.6359110823236570e-01,1.6101222343751107e-01,
+ 1.5844461415592431e-01,1.5588826472447920e-01,1.5334316106026283e-01,
+ 1.5080929068184568e-01,1.4828664273257453e-01,1.4577520800599403e-01,
+ 1.4327497897351341e-01,1.4078594981444470e-01,1.3830811644855071e-01,
+ 1.3584147657125373e-01,1.3338602969166913e-01,1.3094177717364430e-01,
+ 1.2850872227999952e-01,1.2608687022018586e-01,1.2367622820159654e-01,
+ 1.2127680548479021e-01,1.1888861344290998e-01,1.1651166562561080e-01,
+ 1.1414597782783835e-01,1.1179156816383801e-01,1.0944845714681163e-01,
+ 1.0711666777468364e-01,1.0479622562248690e-01,1.0248715894193508e-01,
+ 1.0018949876880981e-01,9.7903279038862284e-02,9.5628536713008819e-02,
+ 9.3365311912690860e-02,9.1113648066373634e-02,8.8873592068275789e-02,
+ 8.6645194450557961e-02,8.4428509570353374e-02,8.2223595813202863e-02,
+ 8.0030515814663056e-02,7.7849336702096039e-02,7.5680130358927067e-02,
+ 7.3522973713981268e-02,7.1377949058890375e-02,6.9245144397006769e-02,
+ 6.7124653827788497e-02,6.5016577971242842e-02,6.2921024437758113e-02,
+ 6.0838108349539864e-02,5.8767952920933758e-02,5.6710690106202902e-02,
+ 5.4666461324888914e-02,5.2635418276792176e-02,5.0617723860947761e-02,
+ 4.8613553215868521e-02,4.6623094901930368e-02,4.4646552251294443e-02,
+ 4.2684144916474431e-02,4.0736110655940933e-02,3.8802707404526113e-02,
+ 3.6884215688567284e-02,3.4980941461716084e-02,3.3093219458578522e-02,
+ 3.1221417191920245e-02,2.9365939758133314e-02,2.7527235669603082e-02,
+ 2.5705804008548896e-02,2.3902203305795882e-02,2.2117062707308864e-02,
+ 2.0351096230044517e-02,1.8605121275724643e-02,1.6880083152543166e-02,
+ 1.5177088307935325e-02,1.3497450601739880e-02,1.1842757857907888e-02,
+ 1.0214971439701471e-02,8.6165827693987316e-03,7.0508754713732268e-03,
+ 5.5224032992509968e-03,4.0379725933630305e-03,2.6090727461021627e-03,
+ 1.2602859304985975e-03}).
diff --git a/lib/stdlib/src/random.erl b/lib/stdlib/src/random.erl
index d7b51a151c..46dabb4323 100644
--- a/lib/stdlib/src/random.erl
+++ b/lib/stdlib/src/random.erl
@@ -1,22 +1,24 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
-module(random).
+-deprecated(module).
%% Reasonable random number generator.
%% The method is attributed to B. A. Wichmann and I. D. Hill
@@ -57,11 +59,17 @@ seed() ->
%% seed({A1, A2, A3})
%% Seed random number generation
--spec seed({A1, A2, A3}) -> 'undefined' | ran() when
+-spec seed(SValue) -> 'undefined' | ran() when
+ SValue :: {A1, A2, A3} | integer(),
A1 :: integer(),
A2 :: integer(),
A3 :: integer().
+seed(Int) when is_integer(Int) ->
+ A1 = (Int bsr 16) band 16#fffffff,
+ A2 = Int band 16#ffffff,
+ A3 = (Int bsr 36) bor (A2 bsr 16),
+ seed(A1, A2, A3);
seed({A1, A2, A3}) ->
seed(A1, A2, A3).
diff --git a/lib/stdlib/src/re.erl b/lib/stdlib/src/re.erl
index 7f3cd8f592..52d3c35608 100644
--- a/lib/stdlib/src/re.erl
+++ b/lib/stdlib/src/re.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2014. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -131,8 +132,9 @@ split(Subject,RE) ->
split(Subject,RE,Options) ->
try
- {NewOpt,Convert,Unicode,Limit,Strip,Group} =
- process_split_params(Options,iodata,false,-1,false,false),
+ {NewOpt,Convert,Limit,Strip,Group} =
+ process_split_params(Options,iodata,-1,false,false),
+ Unicode = check_for_unicode(RE, Options),
FlatSubject = to_binary(Subject, Unicode),
case compile_split(RE,NewOpt) of
{error,_Err} ->
@@ -323,8 +325,8 @@ replace(Subject,RE,Replacement) ->
replace(Subject,RE,Replacement,Options) ->
try
- {NewOpt,Convert,Unicode} =
- process_repl_params(Options,iodata,false),
+ {NewOpt,Convert} = process_repl_params(Options,iodata),
+ Unicode = check_for_unicode(RE, Options),
FlatSubject = to_binary(Subject, Unicode),
FlatReplacement = to_binary(Replacement, Unicode),
IoList = do_replace(FlatSubject,Subject,RE,FlatReplacement,NewOpt),
@@ -366,65 +368,59 @@ do_replace(FlatSubject,Subject,RE,Replacement,Options) ->
apply_mlist(FlatSubject,Replacement,[Slist])
end.
-process_repl_params([],Convert,Unicode) ->
- {[],Convert,Unicode};
-process_repl_params([unicode|T],C,_U) ->
- {NT,NC,NU} = process_repl_params(T,C,true),
- {[unicode|NT],NC,NU};
-process_repl_params([report_errors|_],_,_) ->
+process_repl_params([],Convert) ->
+ {[],Convert};
+process_repl_params([report_errors|_],_) ->
throw(badopt);
-process_repl_params([{capture,_,_}|_],_,_) ->
+process_repl_params([{capture,_,_}|_],_) ->
throw(badopt);
-process_repl_params([{capture,_}|_],_,_) ->
+process_repl_params([{capture,_}|_],_) ->
throw(badopt);
-process_repl_params([{return,iodata}|T],_C,U) ->
- process_repl_params(T,iodata,U);
-process_repl_params([{return,list}|T],_C,U) ->
- process_repl_params(T,list,U);
-process_repl_params([{return,binary}|T],_C,U) ->
- process_repl_params(T,binary,U);
-process_repl_params([{return,_}|_],_,_) ->
+process_repl_params([{return,iodata}|T],_C) ->
+ process_repl_params(T,iodata);
+process_repl_params([{return,list}|T],_C) ->
+ process_repl_params(T,list);
+process_repl_params([{return,binary}|T],_C) ->
+ process_repl_params(T,binary);
+process_repl_params([{return,_}|_],_) ->
throw(badopt);
-process_repl_params([H|T],C,U) ->
- {NT,NC,NU} = process_repl_params(T,C,U),
- {[H|NT],NC,NU}.
-
-process_split_params([],Convert,Unicode,Limit,Strip,Group) ->
- {[],Convert,Unicode,Limit,Strip,Group};
-process_split_params([unicode|T],C,_U,L,S,G) ->
- {NT,NC,NU,NL,NS,NG} = process_split_params(T,C,true,L,S,G),
- {[unicode|NT],NC,NU,NL,NS,NG};
-process_split_params([trim|T],C,U,_L,_S,G) ->
- process_split_params(T,C,U,-1,true,G);
-process_split_params([{parts,0}|T],C,U,_L,_S,G) ->
- process_split_params(T,C,U,-1,true,G);
-process_split_params([{parts,N}|T],C,U,_L,_S,G) when is_integer(N), N >= 1 ->
- process_split_params(T,C,U,N-1,false,G);
-process_split_params([{parts,infinity}|T],C,U,_L,_S,G) ->
- process_split_params(T,C,U,-1,false,G);
-process_split_params([{parts,_}|_],_,_,_,_,_) ->
+process_repl_params([H|T],C) ->
+ {NT,NC} = process_repl_params(T,C),
+ {[H|NT],NC}.
+
+process_split_params([],Convert,Limit,Strip,Group) ->
+ {[],Convert,Limit,Strip,Group};
+process_split_params([trim|T],C,_L,_S,G) ->
+ process_split_params(T,C,-1,true,G);
+process_split_params([{parts,0}|T],C,_L,_S,G) ->
+ process_split_params(T,C,-1,true,G);
+process_split_params([{parts,N}|T],C,_L,_S,G) when is_integer(N), N >= 1 ->
+ process_split_params(T,C,N-1,false,G);
+process_split_params([{parts,infinity}|T],C,_L,_S,G) ->
+ process_split_params(T,C,-1,false,G);
+process_split_params([{parts,_}|_],_,_,_,_) ->
throw(badopt);
-process_split_params([group|T],C,U,L,S,_G) ->
- process_split_params(T,C,U,L,S,true);
-process_split_params([global|_],_,_,_,_,_) ->
+process_split_params([group|T],C,L,S,_G) ->
+ process_split_params(T,C,L,S,true);
+process_split_params([global|_],_,_,_,_) ->
throw(badopt);
-process_split_params([report_errors|_],_,_,_,_,_) ->
+process_split_params([report_errors|_],_,_,_,_) ->
throw(badopt);
-process_split_params([{capture,_,_}|_],_,_,_,_,_) ->
+process_split_params([{capture,_,_}|_],_,_,_,_) ->
throw(badopt);
-process_split_params([{capture,_}|_],_,_,_,_,_) ->
+process_split_params([{capture,_}|_],_,_,_,_) ->
throw(badopt);
-process_split_params([{return,iodata}|T],_C,U,L,S,G) ->
- process_split_params(T,iodata,U,L,S,G);
-process_split_params([{return,list}|T],_C,U,L,S,G) ->
- process_split_params(T,list,U,L,S,G);
-process_split_params([{return,binary}|T],_C,U,L,S,G) ->
- process_split_params(T,binary,U,L,S,G);
-process_split_params([{return,_}|_],_,_,_,_,_) ->
+process_split_params([{return,iodata}|T],_C,L,S,G) ->
+ process_split_params(T,iodata,L,S,G);
+process_split_params([{return,list}|T],_C,L,S,G) ->
+ process_split_params(T,list,L,S,G);
+process_split_params([{return,binary}|T],_C,L,S,G) ->
+ process_split_params(T,binary,L,S,G);
+process_split_params([{return,_}|_],_,_,_,_) ->
throw(badopt);
-process_split_params([H|T],C,U,L,S,G) ->
- {NT,NC,NU,NL,NS,NG} = process_split_params(T,C,U,L,S,G),
- {[H|NT],NC,NU,NL,NS,NG}.
+process_split_params([H|T],C,L,S,G) ->
+ {NT,NC,NL,NS,NG} = process_split_params(T,C,L,S,G),
+ {[H|NT],NC,NL,NS,NG}.
apply_mlist(Subject,Replacement,Mlist) ->
do_mlist(Subject,Subject,0,precomp_repl(Replacement), Mlist).
diff --git a/lib/stdlib/src/sets.erl b/lib/stdlib/src/sets.erl
index 167a676281..3e70450320 100644
--- a/lib/stdlib/src/sets.erl
+++ b/lib/stdlib/src/sets.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2014. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -70,7 +71,7 @@
segs :: segs(_) % Segments
}).
--opaque set() :: set(_).
+-type set() :: set(_).
-opaque set(Element) :: #set{segs :: segs(Element)}.
diff --git a/lib/stdlib/src/shell.erl b/lib/stdlib/src/shell.erl
index 679c13f0cf..82a3a2be4f 100644
--- a/lib/stdlib/src/shell.erl
+++ b/lib/stdlib/src/shell.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2015. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -22,7 +23,7 @@
-export([whereis_evaluator/0, whereis_evaluator/1]).
-export([start_restricted/1, stop_restricted/0]).
-export([local_allowed/3, non_local_allowed/3]).
--export([prompt_func/1, strings/1]).
+-export([catch_exception/1, prompt_func/1, strings/1]).
-define(LINEMAX, 30).
-define(CHAR_MAX, 60).
@@ -314,7 +315,8 @@ prompt(N, Eval0, Bs0, RT, Ds0) ->
case get_prompt_func() of
{M,F} ->
L = [{history,N}],
- C = {call,1,{remote,1,{atom,1,M},{atom,1,F}},[{value,1,L}]},
+ A = erl_anno:new(1),
+ C = {call,A,{remote,A,{atom,A,M},{atom,A,F}},[{value,A,L}]},
{V,Eval,Bs,Ds} = shell_cmd([C], Eval0, Bs0, RT, Ds0, pmt),
{Eval,Bs,Ds,case V of
{pmt,Val} ->
@@ -416,7 +418,7 @@ expand_expr({call,_L,{atom,_,v},[N]}, C) ->
{_,undefined,_} ->
no_command(N);
{Ces,V,CommandN} when is_list(Ces) ->
- {value,CommandN,V}
+ {value,erl_anno:new(CommandN),V}
end;
expand_expr({call,L,F,Args}, C) ->
{call,L,expand_expr(F, C),expand_exprs(Args, C)};
@@ -901,7 +903,7 @@ prep_check({call,Line,{atom,_,f},[{var,_,_Name}]}) ->
{atom,Line,ok};
prep_check({value,_CommandN,_Val}) ->
%% erl_lint cannot handle the history expansion {value,_,_}.
- {atom,0,ok};
+ {atom,a0(),ok};
prep_check(T) when is_tuple(T) ->
list_to_tuple(prep_check(tuple_to_list(T)));
prep_check([E | Es]) ->
@@ -913,11 +915,11 @@ expand_records([], E0) ->
E0;
expand_records(UsedRecords, E0) ->
RecordDefs = [Def || {_Name,Def} <- UsedRecords],
- L = 1,
+ L = erl_anno:new(1),
E = prep_rec(E0),
- Forms = RecordDefs ++ [{function,L,foo,0,[{clause,L,[],[],[E]}]}],
- [{function,L,foo,0,[{clause,L,[],[],[NE]}]}] =
- erl_expand_records:module(Forms, [strict_record_tests]),
+ Forms0 = RecordDefs ++ [{function,L,foo,0,[{clause,L,[],[],[E]}]}],
+ Forms = erl_expand_records:module(Forms0, [strict_record_tests]),
+ {function,L,foo,0,[{clause,L,[],[],[NE]}]} = lists:last(Forms),
prep_rec(NE).
prep_rec({value,_CommandN,_V}=Value) ->
@@ -997,12 +999,7 @@ local_func(rl, [A], Bs0, _Shell, RT, Lf, Ef) ->
{value,list_records(record_defs(RT, listify(Recs))),Bs};
local_func(rp, [A], Bs0, _Shell, RT, Lf, Ef) ->
{[V],Bs} = expr_list([A], Bs0, Lf, Ef),
- Cs = io_lib_pretty:print(V, ([{column, 1},
- {line_length, columns()},
- {depth, -1},
- {max_chars, ?CHAR_MAX},
- {record_print_fun, record_print_fun(RT)}]
- ++ enc())),
+ Cs = pp(V, _Column=1, _Depth=-1, RT),
io:requests([{put_chars, unicode, Cs}, nl]),
{value,ok,Bs};
local_func(rr, [A], Bs0, _Shell, RT, Lf, Ef) ->
@@ -1084,6 +1081,8 @@ record_fields([{record_field,_,{atom,_,Field}} | Fs]) ->
[Field | record_fields(Fs)];
record_fields([{record_field,_,{atom,_,Field},_} | Fs]) ->
[Field | record_fields(Fs)];
+record_fields([{typed_record_field,Field,_Type} | Fs]) ->
+ record_fields([Field | Fs]);
record_fields([]) ->
[].
@@ -1320,13 +1319,15 @@ list_bindings([{Name,Val}|Bs], RT) ->
case erl_eval:fun_data(Val) of
{fun_data,_FBs,FCs0} ->
FCs = expand_value(FCs0), % looks nicer
- F = {'fun',0,{clauses,FCs}},
- M = {match,0,{var,0,Name},F},
+ A = a0(),
+ F = {'fun',A,{clauses,FCs}},
+ M = {match,A,{var,A,Name},F},
io:fwrite(<<"~ts\n">>, [erl_pp:expr(M, enc())]);
{named_fun_data,_FBs,FName,FCs0} ->
FCs = expand_value(FCs0), % looks nicer
- F = {named_fun,0,FName,FCs},
- M = {match,0,{var,0,Name},F},
+ A = a0(),
+ F = {named_fun,A,FName,FCs},
+ M = {match,A,{var,A,Name},F},
io:fwrite(<<"~ts\n">>, [erl_pp:expr(M, enc())]);
false ->
Namel = io_lib:fwrite(<<"~s = ">>, [Name]),
@@ -1356,13 +1357,18 @@ expand_value(E) ->
%% There is no abstract representation of funs.
try_abstract(V, CommandN) ->
try erl_parse:abstract(V)
- catch _:_ -> {call,0,{atom,0,v},[{integer,0,CommandN}]}
+ catch
+ _:_ ->
+ A = a0(),
+ {call,A,{atom,A,v},[{integer,A,CommandN}]}
end.
%% Rather than listing possibly huge results the calls to v/1 are shown.
prep_list_commands(E) ->
- substitute_v1(fun({value,CommandN,_V}) ->
- {call,0,{atom,0,v},[{integer,0,CommandN}]}
+ A = a0(),
+ substitute_v1(fun({value,Anno,_V}) ->
+ CommandN = erl_anno:line(Anno),
+ {call,A,{atom,A,v},[{integer,A,CommandN}]}
end, E).
substitute_v1(F, {value,_,_}=Value) ->
@@ -1374,6 +1380,9 @@ substitute_v1(F, [E | Es]) ->
substitute_v1(_F, E) ->
E.
+a0() ->
+ erl_anno:new(0).
+
check_and_get_history_and_results() ->
check_env(shell_history_length),
check_env(shell_saved_results),
@@ -1385,9 +1394,9 @@ get_history_and_results() ->
{History, erlang:min(Results, History)}.
pp(V, I, RT) ->
- pp(V, I, RT, enc()).
+ pp(V, I, _Depth=?LINEMAX, RT).
-pp(V, I, RT, Enc) ->
+pp(V, I, D, RT) ->
Strings =
case application:get_env(stdlib, shell_strings) of
{ok, false} ->
@@ -1396,10 +1405,10 @@ pp(V, I, RT, Enc) ->
true
end,
io_lib_pretty:print(V, ([{column, I}, {line_length, columns()},
- {depth, ?LINEMAX}, {max_chars, ?CHAR_MAX},
+ {depth, D}, {max_chars, ?CHAR_MAX},
{strings, Strings},
{record_print_fun, record_print_fun(RT)}]
- ++ Enc)).
+ ++ enc())).
columns() ->
case io:columns() of
diff --git a/lib/stdlib/src/shell_default.erl b/lib/stdlib/src/shell_default.erl
index 3fe359af0e..6947cf181b 100644
--- a/lib/stdlib/src/shell_default.erl
+++ b/lib/stdlib/src/shell_default.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -23,7 +24,7 @@
-module(shell_default).
-export([help/0,lc/1,c/1,c/2,nc/1,nl/1,l/1,i/0,pid/3,i/3,m/0,m/1,
- memory/0,memory/1,
+ memory/0,memory/1,uptime/0,
erlangrc/1,bi/1, regs/0, flush/0,pwd/0,ls/0,ls/1,cd/1,
y/1, y/2,
xm/1, bt/1, q/0,
@@ -92,6 +93,7 @@ pid(X,Y,Z) -> c:pid(X,Y,Z).
pwd() -> c:pwd().
q() -> c:q().
regs() -> c:regs().
+uptime() -> c:uptime().
xm(Mod) -> c:xm(Mod).
y(File) -> c:y(File).
y(File, Opts) -> c:y(File, Opts).
diff --git a/lib/stdlib/src/slave.erl b/lib/stdlib/src/slave.erl
index 1898dc8aba..5b5c328c0c 100644
--- a/lib/stdlib/src/slave.erl
+++ b/lib/stdlib/src/slave.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -128,7 +129,7 @@ relay1(Pid) ->
%% {error, {already_running, Name@Host}}
-spec start(Host) -> {ok, Node} | {error, Reason} when
- Host :: atom(),
+ Host :: inet:hostname(),
Node :: node(),
Reason :: timeout | no_rsh | {already_running, Node}.
@@ -138,8 +139,8 @@ start(Host) ->
start(Host, Name, [], no_link).
-spec start(Host, Name) -> {ok, Node} | {error, Reason} when
- Host :: atom(),
- Name :: atom(),
+ Host :: inet:hostname(),
+ Name :: atom() | string(),
Node :: node(),
Reason :: timeout | no_rsh | {already_running, Node}.
@@ -147,8 +148,8 @@ start(Host, Name) ->
start(Host, Name, []).
-spec start(Host, Name, Args) -> {ok, Node} | {error, Reason} when
- Host :: atom(),
- Name :: atom(),
+ Host :: inet:hostname(),
+ Name :: atom() | string(),
Args :: string(),
Node :: node(),
Reason :: timeout | no_rsh | {already_running, Node}.
@@ -157,7 +158,7 @@ start(Host, Name, Args) ->
start(Host, Name, Args, no_link).
-spec start_link(Host) -> {ok, Node} | {error, Reason} when
- Host :: atom(),
+ Host :: inet:hostname(),
Node :: node(),
Reason :: timeout | no_rsh | {already_running, Node}.
@@ -167,8 +168,8 @@ start_link(Host) ->
start(Host, Name, [], self()).
-spec start_link(Host, Name) -> {ok, Node} | {error, Reason} when
- Host :: atom(),
- Name :: atom(),
+ Host :: inet:hostname(),
+ Name :: atom() | string(),
Node :: node(),
Reason :: timeout | no_rsh | {already_running, Node}.
@@ -176,8 +177,8 @@ start_link(Host, Name) ->
start_link(Host, Name, []).
-spec start_link(Host, Name, Args) -> {ok, Node} | {error, Reason} when
- Host :: atom(),
- Name :: atom(),
+ Host :: inet:hostname(),
+ Name :: atom() | string(),
Args :: string(),
Node :: node(),
Reason :: timeout | no_rsh | {already_running, Node}.
@@ -210,7 +211,6 @@ start(Host0, Name, Args, LinkTo, Prog) ->
Node :: node().
stop(Node) ->
-% io:format("stop(~p)~n", [Node]),
rpc:call(Node, erlang, halt, []),
ok.
@@ -229,7 +229,6 @@ wait_for_slave(Parent, Host, Name, Node, Args, LinkTo, Prog) ->
Waiter = register_unique_name(0),
case mk_cmd(Host, Name, Args, Waiter, Prog) of
{ok, Cmd} ->
-%% io:format("Command: ~ts~n", [Cmd]),
open_port({spawn, Cmd}, [stream]),
receive
{SlavePid, slave_started} ->
@@ -290,10 +289,7 @@ register_unique_name(Number) ->
%% no need to use rsh.
mk_cmd(Host, Name, Args, Waiter, Prog0) ->
- Prog = case os:type() of
- {ose,_} -> mk_ose_prog(Prog0);
- _ -> quote_progname(Prog0)
- end,
+ Prog = quote_progname(Prog0),
BasicCmd = lists:concat([Prog,
" -detached -noinput -master ", node(),
" ", long_or_short(), Name, "@", Host,
@@ -313,24 +309,6 @@ mk_cmd(Host, Name, Args, Waiter, Prog0) ->
end
end.
-%% On OSE we have to pass the beam arguments directory to the slave
-%% process. To find out what arguments that should be passed on we
-%% make an assumption. All arguments after the last "--" should be
-%% skipped. So given these arguments:
-%% -Muycs256 -A 1 -- -root /mst/ -progname beam.debug.smp -- -home /mst/ -- -kernel inetrc '"/mst/inetrc.conf"' -- -name test@localhost
-%% we send
-%% -Muycs256 -A 1 -- -root /mst/ -progname beam.debug.smp -- -home /mst/ -- -kernel inetrc '"/mst/inetrc.conf"' --
-%% to the slave with whatever other args that are added in mk_cmd.
-mk_ose_prog(Prog) ->
- SkipTail = fun("--",[]) ->
- ["--"];
- (_,[]) ->
- [];
- (Arg,Args) ->
- [Arg," "|Args]
- end,
- [Prog,tl(lists:foldr(SkipTail,[],erlang:system_info(emu_args)))].
-
%% This is an attempt to distinguish between spaces in the program
%% path and spaces that separate arguments. The program is quoted to
%% allow spaces in the path.
diff --git a/lib/stdlib/src/sofs.erl b/lib/stdlib/src/sofs.erl
index 0bd67db100..b18df2ad09 100644
--- a/lib/stdlib/src/sofs.erl
+++ b/lib/stdlib/src/sofs.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2014. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
diff --git a/lib/stdlib/src/stdlib.app.src b/lib/stdlib/src/stdlib.app.src
index aa9899da3b..32bcdc4f2a 100644
--- a/lib/stdlib/src/stdlib.app.src
+++ b/lib/stdlib/src/stdlib.app.src
@@ -2,18 +2,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -39,6 +40,7 @@
edlin_expand,
epp,
eval_bits,
+ erl_anno,
erl_bits,
erl_compile,
erl_eval,
@@ -63,6 +65,7 @@
gen_event,
gen_fsm,
gen_server,
+ gen_statem,
io,
io_lib,
io_lib_format,
@@ -77,13 +80,13 @@
orddict,
ordsets,
otp_internal,
- pg,
pool,
proc_lib,
proplists,
qlc,
qlc_pt,
queue,
+ rand,
random,
re,
sets,
@@ -103,7 +106,7 @@
dets]},
{applications, [kernel]},
{env, []},
- {runtime_dependencies, ["sasl-2.4","kernel-3.0.2","erts-6.2","crypto-3.3",
+ {runtime_dependencies, ["sasl-2.6","kernel-4.1","erts-7.3","crypto-3.3",
"compiler-5.0"]}
]}.
diff --git a/lib/stdlib/src/stdlib.appup.src b/lib/stdlib/src/stdlib.appup.src
index 951dbca68e..9877662743 100644
--- a/lib/stdlib/src/stdlib.appup.src
+++ b/lib/stdlib/src/stdlib.appup.src
@@ -1,27 +1,26 @@
%% -*- erlang -*-
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2014. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
{"%VSN%",
%% Up from - max one major revision back
- [{<<"2\\.[1-3](\\.[0-9]+)*">>,[restart_new_emulator]}, %% 17.1-17.3
- {<<"2\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, %% 17.0
- {<<"1\\.19(\\.[0-9]+)*">>,[restart_new_emulator]}],%% R16
+ [{<<"3\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.*
+ {<<"2\\.[5-8](\\.[0-9]+)*">>,[restart_new_emulator]}], % OTP-18.*
%% Down to - max one major revision back
- [{<<"2\\.[1-3](\\.[0-9]+)*">>,[restart_new_emulator]}, %% 17.1-17.3
- {<<"2\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, %% 17.0
- {<<"1\\.19(\\.[0-9]+)*">>,[restart_new_emulator]}] %% R16
+ [{<<"3\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.*
+ {<<"2\\.[5-8](\\.[0-9]+)*">>,[restart_new_emulator]}] % OTP-18.*
}.
diff --git a/lib/stdlib/src/string.erl b/lib/stdlib/src/string.erl
index f9b083a56d..c659db78bd 100644
--- a/lib/stdlib/src/string.erl
+++ b/lib/stdlib/src/string.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -221,23 +222,47 @@ substr2([_|String], S) -> substr2(String, S-1).
Tokens :: [Token :: nonempty_string()].
tokens(S, Seps) ->
- tokens1(S, Seps, []).
+ case Seps of
+ [] ->
+ case S of
+ [] -> [];
+ [_|_] -> [S]
+ end;
+ [C] ->
+ tokens_single_1(reverse(S), C, []);
+ [_|_] ->
+ tokens_multiple_1(reverse(S), Seps, [])
+ end.
-tokens1([C|S], Seps, Toks) ->
+tokens_single_1([Sep|S], Sep, Toks) ->
+ tokens_single_1(S, Sep, Toks);
+tokens_single_1([C|S], Sep, Toks) ->
+ tokens_single_2(S, Sep, Toks, [C]);
+tokens_single_1([], _, Toks) ->
+ Toks.
+
+tokens_single_2([Sep|S], Sep, Toks, Tok) ->
+ tokens_single_1(S, Sep, [Tok|Toks]);
+tokens_single_2([C|S], Sep, Toks, Tok) ->
+ tokens_single_2(S, Sep, Toks, [C|Tok]);
+tokens_single_2([], _Sep, Toks, Tok) ->
+ [Tok|Toks].
+
+tokens_multiple_1([C|S], Seps, Toks) ->
case member(C, Seps) of
- true -> tokens1(S, Seps, Toks);
- false -> tokens2(S, Seps, Toks, [C])
+ true -> tokens_multiple_1(S, Seps, Toks);
+ false -> tokens_multiple_2(S, Seps, Toks, [C])
end;
-tokens1([], _Seps, Toks) ->
- reverse(Toks).
+tokens_multiple_1([], _Seps, Toks) ->
+ Toks.
-tokens2([C|S], Seps, Toks, Cs) ->
+tokens_multiple_2([C|S], Seps, Toks, Tok) ->
case member(C, Seps) of
- true -> tokens1(S, Seps, [reverse(Cs)|Toks]);
- false -> tokens2(S, Seps, Toks, [C|Cs])
+ true -> tokens_multiple_1(S, Seps, [Tok|Toks]);
+ false -> tokens_multiple_2(S, Seps, Toks, [C|Tok])
end;
-tokens2([], _Seps, Toks, Cs) ->
- reverse([reverse(Cs)|Toks]).
+tokens_multiple_2([], _Seps, Toks, Tok) ->
+ [Tok|Toks].
-spec chars(Character, Number) -> String when
Character :: char(),
diff --git a/lib/stdlib/src/supervisor.erl b/lib/stdlib/src/supervisor.erl
index ede2742875..38b764541a 100644
--- a/lib/stdlib/src/supervisor.erl
+++ b/lib/stdlib/src/supervisor.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -25,16 +26,19 @@
start_child/2, restart_child/2,
delete_child/2, terminate_child/2,
which_children/1, count_children/1,
- check_childspecs/1]).
+ check_childspecs/1, get_childspec/2]).
%% Internal exports
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-export([try_again_restart/2]).
+%% For release_handler only
+-export([get_callback_module/1]).
+
%%--------------------------------------------------------------------------
--export_type([child_spec/0, startchild_ret/0, strategy/0]).
+-export_type([sup_flags/0, child_spec/0, startchild_ret/0, strategy/0]).
%%--------------------------------------------------------------------------
@@ -53,7 +57,13 @@
| {'global', Name :: atom()}
| {'via', Module :: module(), Name :: any()}
| pid().
--type child_spec() :: {Id :: child_id(),
+-type child_spec() :: #{id := child_id(), % mandatory
+ start := mfargs(), % mandatory
+ restart => restart(), % optional
+ shutdown => shutdown(), % optional
+ type => worker(), % optional
+ modules => modules()} % optional
+ | {Id :: child_id(),
StartFunc :: mfargs(),
Restart :: restart(),
Shutdown :: shutdown(),
@@ -63,6 +73,23 @@
-type strategy() :: 'one_for_all' | 'one_for_one'
| 'rest_for_one' | 'simple_one_for_one'.
+-type sup_flags() :: #{strategy => strategy(), % optional
+ intensity => non_neg_integer(), % optional
+ period => pos_integer()} % optional
+ | {RestartStrategy :: strategy(),
+ Intensity :: non_neg_integer(),
+ Period :: pos_integer()}.
+
+%%--------------------------------------------------------------------------
+%% Defaults
+-define(default_flags, #{strategy => one_for_one,
+ intensity => 1,
+ period => 5}).
+-define(default_child_spec, #{restart => permanent,
+ type => worker}).
+%% Default 'shutdown' is 5000 for workers and infinity for supervisors.
+%% Default 'modules' is [M], where M comes from the child's start {M,F,A}.
+
%%--------------------------------------------------------------------------
-record(child, {% pid is undefined when child is not running
@@ -83,12 +110,15 @@
-define(SET, sets:set).
-record(state, {name,
- strategy :: strategy(),
+ strategy :: strategy() | 'undefined',
children = [] :: [child_rec()],
- dynamics :: ?DICT(pid(), list()) | ?SET(pid()),
- intensity :: non_neg_integer(),
- period :: pos_integer(),
+ dynamics :: {'dict', ?DICT(pid(), list())}
+ | {'set', ?SET(pid())}
+ | 'undefined',
+ intensity :: non_neg_integer() | 'undefined',
+ period :: pos_integer() | 'undefined',
restarts = [],
+ dynamic_restarts = 0 :: non_neg_integer(),
module,
args}).
-type state() :: #state{}.
@@ -96,10 +126,7 @@
-define(is_simple(State), State#state.strategy =:= simple_one_for_one).
-callback init(Args :: term()) ->
- {ok, {{RestartStrategy :: strategy(),
- MaxR :: non_neg_integer(),
- MaxT :: non_neg_integer()},
- [ChildSpec :: child_spec()]}}
+ {ok, {SupFlags :: sup_flags(), [ChildSpec :: child_spec()]}}
| ignore.
-define(restarting(_Pid_), {restarting,_Pid_}).
@@ -178,6 +205,14 @@ delete_child(Supervisor, Name) ->
terminate_child(Supervisor, Name) ->
call(Supervisor, {terminate_child, Name}).
+-spec get_childspec(SupRef, Id) -> Result when
+ SupRef :: sup_ref(),
+ Id :: pid() | child_id(),
+ Result :: {'ok', child_spec()} | {'error', Error},
+ Error :: 'not_found'.
+get_childspec(Supervisor, Name) ->
+ call(Supervisor, {get_childspec, Name}).
+
-spec which_children(SupRef) -> [{Id,Child,Type,Modules}] when
SupRef :: sup_ref(),
Id :: child_id() | undefined,
@@ -221,6 +256,17 @@ try_again_restart(Supervisor, Child) ->
cast(Supervisor, Req) ->
gen_server:cast(Supervisor, Req).
+%%%-----------------------------------------------------------------
+%%% Called by release_handler during upgrade
+-spec get_callback_module(Pid) -> Module when
+ Pid :: pid(),
+ Module :: atom().
+get_callback_module(Pid) ->
+ {status, _Pid, {module, _Mod},
+ [_PDict, _SysState, _Parent, _Dbg, Misc]} = sys:get_status(Pid),
+ [_Header, _Data, {data, [{"State", State}]}] = Misc,
+ State#state.module.
+
%%% ---------------------------------------------------
%%%
%%% Initialize the supervisor.
@@ -353,7 +399,7 @@ handle_call({start_child, EArgs}, _From, State) when ?is_simple(State) ->
#child{mfargs = {M, F, A}} = Child,
Args = A ++ EArgs,
case do_start_child_i(M, F, Args) of
- {ok, undefined} when Child#child.restart_type =:= temporary ->
+ {ok, undefined} ->
{reply, {ok, undefined}, State};
{ok, Pid} ->
NState = save_dynamic_child(Child#child.restart_type, Pid, Args, State),
@@ -365,6 +411,15 @@ handle_call({start_child, EArgs}, _From, State) when ?is_simple(State) ->
{reply, What, State}
end;
+handle_call({start_child, ChildSpec}, _From, State) ->
+ case check_childspec(ChildSpec) of
+ {ok, Child} ->
+ {Resp, NState} = handle_start_child(Child, State),
+ {reply, Resp, NState};
+ What ->
+ {reply, {error, What}, State}
+ end;
+
%% terminate_child for simple_one_for_one can only be done with pid
handle_call({terminate_child, Name}, _From, State) when not is_pid(Name),
?is_simple(State) ->
@@ -383,20 +438,10 @@ handle_call({terminate_child, Name}, _From, State) ->
{reply, {error, not_found}, State}
end;
-%%% The requests delete_child and restart_child are invalid for
-%%% simple_one_for_one supervisors.
-handle_call({_Req, _Data}, _From, State) when ?is_simple(State) ->
+%% restart_child request is invalid for simple_one_for_one supervisors
+handle_call({restart_child, _Name}, _From, State) when ?is_simple(State) ->
{reply, {error, simple_one_for_one}, State};
-handle_call({start_child, ChildSpec}, _From, State) ->
- case check_childspec(ChildSpec) of
- {ok, Child} ->
- {Resp, NState} = handle_start_child(Child, State),
- {reply, Resp, NState};
- What ->
- {reply, {error, What}, State}
- end;
-
handle_call({restart_child, Name}, _From, State) ->
case get_child(Name, State) of
{value, Child} when Child#child.pid =:= undefined ->
@@ -418,6 +463,10 @@ handle_call({restart_child, Name}, _From, State) ->
{reply, {error, not_found}, State}
end;
+%% delete_child request is invalid for simple_one_for_one supervisors
+handle_call({delete_child, _Name}, _From, State) when ?is_simple(State) ->
+ {reply, {error, simple_one_for_one}, State};
+
handle_call({delete_child, Name}, _From, State) ->
case get_child(Name, State) of
{value, Child} when Child#child.pid =:= undefined ->
@@ -431,6 +480,14 @@ handle_call({delete_child, Name}, _From, State) ->
{reply, {error, not_found}, State}
end;
+handle_call({get_childspec, Name}, _From, State) ->
+ case get_child(Name, State, ?is_simple(State)) of
+ {value, Child} ->
+ {reply, {ok, child_to_spec(Child)}, State};
+ false ->
+ {reply, {error, not_found}, State}
+ end;
+
handle_call(which_children, _From, #state{children = [#child{restart_type = temporary,
child_type = CT,
modules = Mods}]} =
@@ -464,39 +521,26 @@ handle_call(which_children, _From, State) ->
handle_call(count_children, _From, #state{children = [#child{restart_type = temporary,
child_type = CT}]} = State)
when ?is_simple(State) ->
- {Active, Count} =
- ?SETS:fold(fun(Pid, {Alive, Tot}) ->
- case is_pid(Pid) andalso is_process_alive(Pid) of
- true ->{Alive+1, Tot +1};
- false ->
- {Alive, Tot + 1}
- end
- end, {0, 0}, dynamics_db(temporary, State#state.dynamics)),
+ Sz = ?SETS:size(dynamics_db(temporary, State#state.dynamics)),
Reply = case CT of
- supervisor -> [{specs, 1}, {active, Active},
- {supervisors, Count}, {workers, 0}];
- worker -> [{specs, 1}, {active, Active},
- {supervisors, 0}, {workers, Count}]
+ supervisor -> [{specs, 1}, {active, Sz},
+ {supervisors, Sz}, {workers, 0}];
+ worker -> [{specs, 1}, {active, Sz},
+ {supervisors, 0}, {workers, Sz}]
end,
{reply, Reply, State};
-handle_call(count_children, _From, #state{children = [#child{restart_type = RType,
+handle_call(count_children, _From, #state{dynamic_restarts = Restarts,
+ children = [#child{restart_type = RType,
child_type = CT}]} = State)
when ?is_simple(State) ->
- {Active, Count} =
- ?DICTS:fold(fun(Pid, _Val, {Alive, Tot}) ->
- case is_pid(Pid) andalso is_process_alive(Pid) of
- true ->
- {Alive+1, Tot +1};
- false ->
- {Alive, Tot + 1}
- end
- end, {0, 0}, dynamics_db(RType, State#state.dynamics)),
+ Sz = ?DICTS:size(dynamics_db(RType, State#state.dynamics)),
+ Active = Sz - Restarts,
Reply = case CT of
supervisor -> [{specs, 1}, {active, Active},
- {supervisors, Count}, {workers, 0}];
+ {supervisors, Sz}, {workers, 0}];
worker -> [{specs, 1}, {active, Active},
- {supervisors, 0}, {workers, Count}]
+ {supervisors, 0}, {workers, Sz}]
end,
{reply, Reply, State};
@@ -537,7 +581,7 @@ handle_cast({try_again_restart,Pid}, #state{children=[Child]}=State)
when ?is_simple(State) ->
RT = Child#child.restart_type,
RPid = restarting(Pid),
- case dynamic_child_args(RPid, dynamics_db(RT, State#state.dynamics)) of
+ case dynamic_child_args(RPid, RT, State#state.dynamics) of
{ok, Args} ->
{M, F, _} = Child#child.mfargs,
NChild = Child#child{pid = RPid, mfargs = {M, F, Args}},
@@ -610,13 +654,11 @@ terminate(_Reason, State) ->
code_change(_, State, _) ->
case (State#state.module):init(State#state.args) of
{ok, {SupFlags, StartSpec}} ->
- case catch check_flags(SupFlags) of
- ok ->
- {Strategy, MaxIntensity, Period} = SupFlags,
- update_childspec(State#state{strategy = Strategy,
- intensity = MaxIntensity,
- period = Period},
- StartSpec);
+ case set_flags(SupFlags, State) of
+ {ok, State1} ->
+ update_childspec(State1, StartSpec);
+ {invalid_type, SupFlags} ->
+ {error, {bad_flags, SupFlags}}; % backwards compatibility
Error ->
{error, Error}
end;
@@ -626,14 +668,6 @@ code_change(_, State, _) ->
Error
end.
-check_flags({Strategy, MaxIntensity, Period}) ->
- validStrategy(Strategy),
- validIntensity(MaxIntensity),
- validPeriod(Period),
- ok;
-check_flags(What) ->
- {bad_flags, What}.
-
update_childspec(State, StartSpec) when ?is_simple(State) ->
case check_startspec(StartSpec) of
{ok, [Child]} ->
@@ -705,7 +739,7 @@ handle_start_child(Child, State) ->
restart_child(Pid, Reason, #state{children = [Child]} = State) when ?is_simple(State) ->
RestartType = Child#child.restart_type,
- case dynamic_child_args(Pid, dynamics_db(RestartType, State#state.dynamics)) of
+ case dynamic_child_args(Pid, RestartType, State#state.dynamics) of
{ok, Args} ->
{M, F, _} = Child#child.mfargs,
NChild = Child#child{pid = Pid, mfargs = {M, F, Args}},
@@ -776,20 +810,31 @@ restart(Child, State) ->
{shutdown, remove_child(Child, NState)}
end.
-restart(simple_one_for_one, Child, State) ->
+restart(simple_one_for_one, Child, State0) ->
#child{pid = OldPid, mfargs = {M, F, A}} = Child,
+ State = case OldPid of
+ ?restarting(_) ->
+ NRes = State0#state.dynamic_restarts - 1,
+ State0#state{dynamic_restarts = NRes};
+ _ ->
+ State0
+ end,
Dynamics = ?DICTS:erase(OldPid, dynamics_db(Child#child.restart_type,
State#state.dynamics)),
case do_start_child_i(M, F, A) of
{ok, Pid} ->
- NState = State#state{dynamics = ?DICTS:store(Pid, A, Dynamics)},
+ DynamicsDb = {dict, ?DICTS:store(Pid, A, Dynamics)},
+ NState = State#state{dynamics = DynamicsDb},
{ok, NState};
{ok, Pid, _Extra} ->
- NState = State#state{dynamics = ?DICTS:store(Pid, A, Dynamics)},
+ DynamicsDb = {dict, ?DICTS:store(Pid, A, Dynamics)},
+ NState = State#state{dynamics = DynamicsDb},
{ok, NState};
{error, Error} ->
- NState = State#state{dynamics = ?DICTS:store(restarting(OldPid), A,
- Dynamics)},
+ NRestarts = State#state.dynamic_restarts + 1,
+ DynamicsDb = {dict, ?DICTS:store(restarting(OldPid), A, Dynamics)},
+ NState = State#state{dynamic_restarts = NRestarts,
+ dynamics = DynamicsDb},
report_error(start_error, Error, Child, State#state.name),
{try_again, NState}
end;
@@ -1053,7 +1098,7 @@ wait_dynamic_children(#child{restart_type=RType} = Child, Pids, Sz,
{timeout, TRef, kill} ->
?SETS:fold(fun(P, _) -> exit(P, kill) end, ok, Pids),
- wait_dynamic_children(Child, Pids, Sz-1, undefined, EStack)
+ wait_dynamic_children(Child, Pids, Sz, undefined, EStack)
end.
%%-----------------------------------------------------------------
@@ -1072,31 +1117,32 @@ save_child(Child, #state{children = Children} = State) ->
State#state{children = [Child |Children]}.
save_dynamic_child(temporary, Pid, _, #state{dynamics = Dynamics} = State) ->
- State#state{dynamics = ?SETS:add_element(Pid, dynamics_db(temporary, Dynamics))};
+ DynamicsDb = dynamics_db(temporary, Dynamics),
+ State#state{dynamics = {set, ?SETS:add_element(Pid, DynamicsDb)}};
save_dynamic_child(RestartType, Pid, Args, #state{dynamics = Dynamics} = State) ->
- State#state{dynamics = ?DICTS:store(Pid, Args, dynamics_db(RestartType, Dynamics))}.
+ DynamicsDb = dynamics_db(RestartType, Dynamics),
+ State#state{dynamics = {dict, ?DICTS:store(Pid, Args, DynamicsDb)}}.
dynamics_db(temporary, undefined) ->
?SETS:new();
dynamics_db(_, undefined) ->
?DICTS:new();
-dynamics_db(_,Dynamics) ->
- Dynamics.
-
-dynamic_child_args(Pid, Dynamics) ->
- case ?SETS:is_set(Dynamics) of
- true ->
- {ok, undefined};
- false ->
- ?DICTS:find(Pid, Dynamics)
- end.
+dynamics_db(_, {_Tag, DynamicsDb}) ->
+ DynamicsDb.
+
+dynamic_child_args(_Pid, temporary, _DynamicsDb) ->
+ {ok, undefined};
+dynamic_child_args(Pid, _RT, {dict, DynamicsDb}) ->
+ ?DICTS:find(Pid, DynamicsDb);
+dynamic_child_args(_Pid, _RT, undefined) ->
+ error.
state_del_child(#child{pid = Pid, restart_type = temporary}, State) when ?is_simple(State) ->
NDynamics = ?SETS:del_element(Pid, dynamics_db(temporary, State#state.dynamics)),
- State#state{dynamics = NDynamics};
+ State#state{dynamics = {set, NDynamics}};
state_del_child(#child{pid = Pid, restart_type = RType}, State) when ?is_simple(State) ->
NDynamics = ?DICTS:erase(Pid, dynamics_db(RType, State#state.dynamics)),
- State#state{dynamics = NDynamics};
+ State#state{dynamics = {dict, NDynamics}};
state_del_child(Child, State) ->
NChildren = del_child(Child#child.name, State#state.children),
State#state{children = NChildren}.
@@ -1130,19 +1176,19 @@ split_child(_, [], After) ->
get_child(Name, State) ->
get_child(Name, State, false).
+
get_child(Pid, State, AllowPid) when AllowPid, is_pid(Pid) ->
get_dynamic_child(Pid, State);
get_child(Name, State, _) ->
lists:keysearch(Name, #child.name, State#state.children).
get_dynamic_child(Pid, #state{children=[Child], dynamics=Dynamics}) ->
- DynamicsDb = dynamics_db(Child#child.restart_type, Dynamics),
- case is_dynamic_pid(Pid, DynamicsDb) of
+ case is_dynamic_pid(Pid, Dynamics) of
true ->
{value, Child#child{pid=Pid}};
false ->
RPid = restarting(Pid),
- case is_dynamic_pid(RPid, DynamicsDb) of
+ case is_dynamic_pid(RPid, Dynamics) of
true ->
{value, Child#child{pid=RPid}};
false ->
@@ -1153,13 +1199,12 @@ get_dynamic_child(Pid, #state{children=[Child], dynamics=Dynamics}) ->
end
end.
-is_dynamic_pid(Pid, Dynamics) ->
- case ?SETS:is_set(Dynamics) of
- true ->
- ?SETS:is_element(Pid, Dynamics);
- false ->
- ?DICTS:is_key(Pid, Dynamics)
- end.
+is_dynamic_pid(Pid, {dict, Dynamics}) ->
+ ?DICTS:is_key(Pid, Dynamics);
+is_dynamic_pid(Pid, {set, Dynamics}) ->
+ ?SETS:is_element(Pid, Dynamics);
+is_dynamic_pid(_Pid, undefined) ->
+ false.
replace_child(Child, State) ->
Chs = do_replace_child(Child, State#state.children),
@@ -1188,25 +1233,36 @@ remove_child(Child, State) ->
%% Returns: {ok, state()} | Error
%%-----------------------------------------------------------------
init_state(SupName, Type, Mod, Args) ->
- case catch init_state1(SupName, Type, Mod, Args) of
- {ok, State} ->
- {ok, State};
- Error ->
- Error
+ set_flags(Type, #state{name = supname(SupName,Mod),
+ module = Mod,
+ args = Args}).
+
+set_flags(Flags, State) ->
+ try check_flags(Flags) of
+ #{strategy := Strategy, intensity := MaxIntensity, period := Period} ->
+ {ok, State#state{strategy = Strategy,
+ intensity = MaxIntensity,
+ period = Period}}
+ catch
+ Thrown -> Thrown
end.
-init_state1(SupName, {Strategy, MaxIntensity, Period}, Mod, Args) ->
+check_flags(SupFlags) when is_map(SupFlags) ->
+ do_check_flags(maps:merge(?default_flags,SupFlags));
+check_flags({Strategy, MaxIntensity, Period}) ->
+ check_flags(#{strategy => Strategy,
+ intensity => MaxIntensity,
+ period => Period});
+check_flags(What) ->
+ throw({invalid_type, What}).
+
+do_check_flags(#{strategy := Strategy,
+ intensity := MaxIntensity,
+ period := Period} = Flags) ->
validStrategy(Strategy),
validIntensity(MaxIntensity),
validPeriod(Period),
- {ok, #state{name = supname(SupName,Mod),
- strategy = Strategy,
- intensity = MaxIntensity,
- period = Period,
- module = Mod,
- args = Args}};
-init_state1(_SupName, Type, _, _) ->
- {invalid_type, Type}.
+ Flags.
validStrategy(simple_one_for_one) -> true;
validStrategy(one_for_one) -> true;
@@ -1227,14 +1283,7 @@ supname(N, _) -> N.
%%% ------------------------------------------------------
%%% Check that the children start specification is valid.
-%%% Shall be a six (6) tuple
-%%% {Name, Func, RestartType, Shutdown, ChildType, Modules}
-%%% where Name is an atom
-%%% Func is {Mod, Fun, Args} == {atom(), atom(), list()}
-%%% RestartType is permanent | temporary | transient
-%%% Shutdown = integer() > 0 | infinity | brutal_kill
-%%% ChildType = supervisor | worker
-%%% Modules = [atom()] | dynamic
+%%% Input: [child_spec()]
%%% Returns: {ok, [child_rec()]} | Error
%%% ------------------------------------------------------
@@ -1244,6 +1293,9 @@ check_startspec([ChildSpec|T], Res) ->
case check_childspec(ChildSpec) of
{ok, Child} ->
case lists:keymember(Child#child.name, #child.name, Res) of
+ %% The error message duplicate_child_name is kept for
+ %% backwards compatibility, although
+ %% duplicate_child_id would be more correct.
true -> {duplicate_child_name, Child#child.name};
false -> check_startspec(T, [Child | Res])
end;
@@ -1252,16 +1304,41 @@ check_startspec([ChildSpec|T], Res) ->
check_startspec([], Res) ->
{ok, lists:reverse(Res)}.
+check_childspec(ChildSpec) when is_map(ChildSpec) ->
+ catch do_check_childspec(maps:merge(?default_child_spec,ChildSpec));
check_childspec({Name, Func, RestartType, Shutdown, ChildType, Mods}) ->
- catch check_childspec(Name, Func, RestartType, Shutdown, ChildType, Mods);
+ check_childspec(#{id => Name,
+ start => Func,
+ restart => RestartType,
+ shutdown => Shutdown,
+ type => ChildType,
+ modules => Mods});
check_childspec(X) -> {invalid_child_spec, X}.
-check_childspec(Name, Func, RestartType, Shutdown, ChildType, Mods) ->
+do_check_childspec(#{restart := RestartType,
+ type := ChildType} = ChildSpec)->
+ Name = case ChildSpec of
+ #{id := N} -> N;
+ _ -> throw(missing_id)
+ end,
+ Func = case ChildSpec of
+ #{start := F} -> F;
+ _ -> throw(missing_start)
+ end,
validName(Name),
validFunc(Func),
validRestartType(RestartType),
validChildType(ChildType),
- validShutdown(Shutdown, ChildType),
+ Shutdown = case ChildSpec of
+ #{shutdown := S} -> S;
+ #{type := worker} -> 5000;
+ #{type := supervisor} -> infinity
+ end,
+ validShutdown(Shutdown),
+ Mods = case ChildSpec of
+ #{modules := Ms} -> Ms;
+ _ -> {M,_,_} = Func, [M]
+ end,
validMods(Mods),
{ok, #child{name = Name, mfargs = Func, restart_type = RestartType,
shutdown = Shutdown, child_type = ChildType, modules = Mods}}.
@@ -1282,11 +1359,11 @@ validRestartType(temporary) -> true;
validRestartType(transient) -> true;
validRestartType(RestartType) -> throw({invalid_restart_type, RestartType}).
-validShutdown(Shutdown, _)
+validShutdown(Shutdown)
when is_integer(Shutdown), Shutdown > 0 -> true;
-validShutdown(infinity, _) -> true;
-validShutdown(brutal_kill, _) -> true;
-validShutdown(Shutdown, _) -> throw({invalid_shutdown, Shutdown}).
+validShutdown(infinity) -> true;
+validShutdown(brutal_kill) -> true;
+validShutdown(Shutdown) -> throw({invalid_shutdown, Shutdown}).
validMods(dynamic) -> true;
validMods(Mods) when is_list(Mods) ->
@@ -1299,6 +1376,19 @@ validMods(Mods) when is_list(Mods) ->
Mods);
validMods(Mods) -> throw({invalid_modules, Mods}).
+child_to_spec(#child{name = Name,
+ mfargs = Func,
+ restart_type = RestartType,
+ shutdown = Shutdown,
+ child_type = ChildType,
+ modules = Mods}) ->
+ #{id => Name,
+ start => Func,
+ restart => RestartType,
+ shutdown => Shutdown,
+ type => ChildType,
+ modules => Mods}.
+
%%% ------------------------------------------------------
%%% Add a new restart and calculate if the max restart
%%% intensity has been reached (in that case the supervisor
@@ -1312,7 +1402,7 @@ add_restart(State) ->
I = State#state.intensity,
P = State#state.period,
R = State#state.restarts,
- Now = erlang:now(),
+ Now = erlang:monotonic_time(1),
R1 = add_restart([Now|R], Now, P),
State1 = State#state{restarts = R1},
case length(R1) of
@@ -1332,26 +1422,8 @@ add_restart([R|Restarts], Now, Period) ->
add_restart([], _, _) ->
[].
-inPeriod(Time, Now, Period) ->
- case difference(Time, Now) of
- T when T > Period ->
- false;
- _ ->
- true
- end.
-
-%%
-%% Time = {MegaSecs, Secs, MicroSecs} (NOTE: MicroSecs is ignored)
-%% Calculate the time elapsed in seconds between two timestamps.
-%% If MegaSecs is equal just subtract Secs.
-%% Else calculate the Mega difference and add the Secs difference,
-%% note that Secs difference can be negative, e.g.
-%% {827, 999999, 676} diff {828, 1, 653753} == > 2 secs.
-%%
-difference({TimeM, TimeS, _}, {CurM, CurS, _}) when CurM > TimeM ->
- ((CurM - TimeM) * 1000000) + (CurS - TimeS);
-difference({_, TimeS, _}, {_, CurS, _}) ->
- CurS - TimeS.
+inPeriod(Then, Now, Period) ->
+ Now =< Then + Period.
%%% ------------------------------------------------------
%%% Error and progress reporting.
@@ -1367,14 +1439,14 @@ report_error(Error, Reason, Child, SupName) ->
extract_child(Child) when is_list(Child#child.pid) ->
[{nb_children, length(Child#child.pid)},
- {name, Child#child.name},
+ {id, Child#child.name},
{mfargs, Child#child.mfargs},
{restart_type, Child#child.restart_type},
{shutdown, Child#child.shutdown},
{child_type, Child#child.child_type}];
extract_child(Child) ->
[{pid, Child#child.pid},
- {name, Child#child.name},
+ {id, Child#child.name},
{mfargs, Child#child.mfargs},
{restart_type, Child#child.restart_type},
{shutdown, Child#child.shutdown},
diff --git a/lib/stdlib/src/supervisor_bridge.erl b/lib/stdlib/src/supervisor_bridge.erl
index ff4502f0b9..af1e046d30 100644
--- a/lib/stdlib/src/supervisor_bridge.erl
+++ b/lib/stdlib/src/supervisor_bridge.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
diff --git a/lib/stdlib/src/sys.erl b/lib/stdlib/src/sys.erl
index d3ba09ce82..a6ecf03716 100644
--- a/lib/stdlib/src/sys.erl
+++ b/lib/stdlib/src/sys.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -24,6 +25,7 @@
get_state/1, get_state/2,
replace_state/2, replace_state/3,
change_code/4, change_code/5,
+ terminate/2, terminate/3,
log/2, log/3, trace/2, trace/3, statistics/2, statistics/3,
log_to_file/2, log_to_file/3, no_debug/1, no_debug/2,
install/2, install/3, remove/2, remove/3]).
@@ -163,6 +165,19 @@ change_code(Name, Mod, Vsn, Extra) ->
change_code(Name, Mod, Vsn, Extra, Timeout) ->
send_system_msg(Name, {change_code, Mod, Vsn, Extra}, Timeout).
+-spec terminate(Name, Reason) -> 'ok' when
+ Name :: name(),
+ Reason :: term().
+terminate(Name, Reason) ->
+ send_system_msg(Name, {terminate, Reason}).
+
+-spec terminate(Name, Reason, Timeout) -> 'ok' when
+ Name :: name(),
+ Reason :: term(),
+ Timeout :: timeout().
+terminate(Name, Reason, Timeout) ->
+ send_system_msg(Name, {terminate, Reason}, Timeout).
+
%%-----------------------------------------------------------------
%% Debug commands
%%-----------------------------------------------------------------
@@ -298,6 +313,8 @@ mfa(Name, {debug, {Func, Arg2}}) ->
{sys, Func, [Name, Arg2]};
mfa(Name, {change_code, Mod, Vsn, Extra}) ->
{sys, change_code, [Name, Mod, Vsn, Extra]};
+mfa(Name, {terminate, Reason}) ->
+ {sys, terminate, [Name, Reason]};
mfa(Name, Atom) ->
{sys, Atom, [Name]}.
@@ -313,7 +330,7 @@ mfa(Name, Req, Timeout) ->
%% Returns: This function *never* returns! It calls the function
%% Module:system_continue(Parent, NDebug, Misc)
%% there the process continues the execution or
-%% Module:system_terminate(Raeson, Parent, Debug, Misc) if
+%% Module:system_terminate(Reason, Parent, Debug, Misc) if
%% the process should terminate.
%% The Module must export system_continue/3, system_terminate/4
%% and format_status/2 for status information.
@@ -339,7 +356,10 @@ handle_system_msg(SysState, Msg, From, Parent, Mod, Debug, Misc, Hib) ->
suspend_loop(suspended, Parent, Mod, NDebug, NMisc, Hib);
{running, Reply, NDebug, NMisc} ->
_ = gen:reply(From, Reply),
- Mod:system_continue(Parent, NDebug, NMisc)
+ Mod:system_continue(Parent, NDebug, NMisc);
+ {{terminating, Reason}, Reply, NDebug, NMisc} ->
+ _ = gen:reply(From, Reply),
+ Mod:system_terminate(Reason, Parent, NDebug, NMisc)
end.
%%-----------------------------------------------------------------
@@ -419,6 +439,8 @@ do_cmd(SysState, get_status, Parent, Mod, Debug, Misc) ->
do_cmd(SysState, {debug, What}, _Parent, _Mod, Debug, Misc) ->
{Res, NDebug} = debug_cmd(What, Debug),
{SysState, Res, NDebug, Misc};
+do_cmd(_, {terminate, Reason}, _Parent, _Mod, Debug, Misc) ->
+ {{terminating, Reason}, ok, Debug, Misc};
do_cmd(suspended, {change_code, Module, Vsn, Extra}, _Parent,
Mod, Debug, Misc) ->
{Res, NMisc} = do_change_code(Mod, Module, Vsn, Extra, Misc),
diff --git a/lib/stdlib/src/timer.erl b/lib/stdlib/src/timer.erl
index 72a2dd9616..ca868627a9 100644
--- a/lib/stdlib/src/timer.erl
+++ b/lib/stdlib/src/timer.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -161,10 +162,11 @@ sleep(T) ->
Time :: integer(),
Value :: term().
tc(F) ->
- Before = os:timestamp(),
+ T1 = erlang:monotonic_time(),
Val = F(),
- After = os:timestamp(),
- {now_diff(After, Before), Val}.
+ T2 = erlang:monotonic_time(),
+ Time = erlang:convert_time_unit(T2 - T1, native, micro_seconds),
+ {Time, Val}.
%%
%% Measure the execution time (in microseconds) for Fun(Args).
@@ -175,10 +177,11 @@ tc(F) ->
Time :: integer(),
Value :: term().
tc(F, A) ->
- Before = os:timestamp(),
+ T1 = erlang:monotonic_time(),
Val = apply(F, A),
- After = os:timestamp(),
- {now_diff(After, Before), Val}.
+ T2 = erlang:monotonic_time(),
+ Time = erlang:convert_time_unit(T2 - T1, native, micro_seconds),
+ {Time, Val}.
%%
%% Measure the execution time (in microseconds) for an MFA.
@@ -190,10 +193,11 @@ tc(F, A) ->
Time :: integer(),
Value :: term().
tc(M, F, A) ->
- Before = os:timestamp(),
+ T1 = erlang:monotonic_time(),
Val = apply(M, F, A),
- After = os:timestamp(),
- {now_diff(After, Before), Val}.
+ T2 = erlang:monotonic_time(),
+ Time = erlang:convert_time_unit(T2 - T1, native, micro_seconds),
+ {Time, Val}.
%%
%% Calculate the time difference (in microseconds) of two
@@ -437,10 +441,8 @@ positive(X) ->
%%
%% system_time() -> time in microseconds
%%
-system_time() ->
- {M,S,U} = erlang:now(),
- 1000000 * (M*1000000 + S) + U.
-
+system_time() ->
+ erlang:monotonic_time(1000000).
send([Pid, Msg]) ->
Pid ! Msg.
diff --git a/lib/stdlib/src/unicode.erl b/lib/stdlib/src/unicode.erl
index 49529cffd4..617da11ba8 100644
--- a/lib/stdlib/src/unicode.erl
+++ b/lib/stdlib/src/unicode.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -560,6 +561,8 @@ do_o_binary(F,L) ->
erlang:iolist_to_binary(List)
end.
+-dialyzer({no_improper_lists, do_o_binary2/2}).
+
do_o_binary2(_F,[]) ->
<<>>;
do_o_binary2(F,[H|T]) ->
diff --git a/lib/stdlib/src/win32reg.erl b/lib/stdlib/src/win32reg.erl
index 48a7e262be..8e82a79cbf 100644
--- a/lib/stdlib/src/win32reg.erl
+++ b/lib/stdlib/src/win32reg.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -218,12 +219,7 @@ expand([C|Rest], [], Result) ->
expand(Rest, [], [C|Result]);
expand([$%|Rest], Env0, Result) ->
Env = lists:reverse(Env0),
- case os:getenv(Env) of
- false ->
- expand(Rest, [], Result);
- Value ->
- expand(Rest, [], lists:reverse(Value)++Result)
- end;
+ expand(Rest, [], lists:reverse(os:getenv(Env, ""))++Result);
expand([C|Rest], Env, Result) ->
expand(Rest, [C|Env], Result);
expand([], [], Result) ->
diff --git a/lib/stdlib/src/zip.erl b/lib/stdlib/src/zip.erl
index b768c6d0b9..f8ba6f18e9 100644
--- a/lib/stdlib/src/zip.erl
+++ b/lib/stdlib/src/zip.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -24,7 +25,7 @@
list_dir/1, list_dir/2, table/1, table/2,
t/1, tt/1]).
-%% unzipping peicemeal
+%% unzipping piecemeal
-export([openzip_open/1, openzip_open/2,
openzip_get/1, openzip_get/2,
openzip_t/1, openzip_tt/1,
@@ -214,7 +215,9 @@
-type zip_comment() :: #zip_comment{}.
-type zip_file() :: #zip_file{}.
--export_type([create_option/0, filename/0]).
+-opaque handle() :: pid().
+
+-export_type([create_option/0, filename/0, handle/0]).
%% Open a zip archive with options
%%
@@ -500,7 +503,7 @@ do_list_dir(F, Options) ->
-spec(t(Archive) -> ok when
Archive :: file:name() | binary() | ZipHandle,
- ZipHandle :: pid()).
+ ZipHandle :: handle()).
t(F) when is_pid(F) -> zip_t(F);
t(F) when is_record(F, openzip) -> openzip_t(F);
@@ -524,7 +527,7 @@ do_t(F, RawPrint) ->
-spec(tt(Archive) -> ok when
Archive :: file:name() | binary() | ZipHandle,
- ZipHandle :: pid()).
+ ZipHandle :: handle()).
tt(F) when is_pid(F) -> zip_tt(F);
tt(F) when is_record(F, openzip) -> openzip_tt(F);
@@ -1114,15 +1117,19 @@ local_file_header_from_info_method_name(#file_info{mtime = MTime},
file_name_length = length(Name),
extra_field_length = 0}.
+server_init(Parent) ->
+ %% we want to know if our parent dies
+ process_flag(trap_exit, true),
+ server_loop(Parent, not_open).
%% small, simple, stupid zip-archive server
-server_loop(OpenZip) ->
+server_loop(Parent, OpenZip) ->
receive
{From, {open, Archive, Options}} ->
case openzip_open(Archive, Options) of
{ok, NewOpenZip} ->
From ! {self(), {ok, self()}},
- server_loop(NewOpenZip);
+ server_loop(Parent, NewOpenZip);
Error ->
From ! {self(), Error}
end;
@@ -1130,43 +1137,47 @@ server_loop(OpenZip) ->
From ! {self(), openzip_close(OpenZip)};
{From, get} ->
From ! {self(), openzip_get(OpenZip)},
- server_loop(OpenZip);
+ server_loop(Parent, OpenZip);
{From, {get, FileName}} ->
From ! {self(), openzip_get(FileName, OpenZip)},
- server_loop(OpenZip);
+ server_loop(Parent, OpenZip);
{From, list_dir} ->
From ! {self(), openzip_list_dir(OpenZip)},
- server_loop(OpenZip);
+ server_loop(Parent, OpenZip);
{From, {list_dir, Opts}} ->
From ! {self(), openzip_list_dir(OpenZip, Opts)},
- server_loop(OpenZip);
+ server_loop(Parent, OpenZip);
{From, get_state} ->
From ! {self(), OpenZip},
- server_loop(OpenZip);
+ server_loop(Parent, OpenZip);
+ {'EXIT', Parent, Reason} ->
+ _ = openzip_close(OpenZip),
+ exit({parent_died, Reason});
_ ->
{error, bad_msg}
end.
-spec(zip_open(Archive) -> {ok, ZipHandle} | {error, Reason} when
Archive :: file:name() | binary(),
- ZipHandle :: pid(),
+ ZipHandle :: handle(),
Reason :: term()).
zip_open(Archive) -> zip_open(Archive, []).
-spec(zip_open(Archive, Options) -> {ok, ZipHandle} | {error, Reason} when
Archive :: file:name() | binary(),
- ZipHandle :: pid(),
+ ZipHandle :: handle(),
Options :: [Option],
Option :: cooked | memory | {cwd, CWD :: file:filename()},
Reason :: term()).
zip_open(Archive, Options) ->
- Pid = spawn(fun() -> server_loop(not_open) end),
- request(self(), Pid, {open, Archive, Options}).
+ Self = self(),
+ Pid = spawn_link(fun() -> server_init(Self) end),
+ request(Self, Pid, {open, Archive, Options}).
-spec(zip_get(ZipHandle) -> {ok, [Result]} | {error, Reason} when
- ZipHandle :: pid(),
+ ZipHandle :: handle(),
Result :: file:name() | {file:name(), binary()},
Reason :: term()).
@@ -1174,14 +1185,14 @@ zip_get(Pid) when is_pid(Pid) ->
request(self(), Pid, get).
-spec(zip_close(ZipHandle) -> ok | {error, einval} when
- ZipHandle :: pid()).
+ ZipHandle :: handle()).
zip_close(Pid) when is_pid(Pid) ->
request(self(), Pid, close).
-spec(zip_get(FileName, ZipHandle) -> {ok, Result} | {error, Reason} when
FileName :: file:name(),
- ZipHandle :: pid(),
+ ZipHandle :: handle(),
Result :: file:name() | {file:name(), binary()},
Reason :: term()).
@@ -1190,7 +1201,7 @@ zip_get(FileName, Pid) when is_pid(Pid) ->
-spec(zip_list_dir(ZipHandle) -> {ok, Result} | {error, Reason} when
Result :: [zip_comment() | zip_file()],
- ZipHandle :: pid(),
+ ZipHandle :: handle(),
Reason :: term()).
zip_list_dir(Pid) when is_pid(Pid) ->
@@ -1539,57 +1550,35 @@ unix_extra_field_and_var_from_bin(_) ->
%% A pwrite-like function for iolists (used by memory-option)
-split_iolist(B, Pos) when is_binary(B) ->
- split_binary(B, Pos);
-split_iolist(L, Pos) when is_list(L) ->
- splitter([], L, Pos).
+pwrite_binary(B, Pos, Bin) when byte_size(B) =:= Pos ->
+ append_bins(Bin, B);
+pwrite_binary(B, Pos, Bin) ->
+ erlang:iolist_to_binary(pwrite_iolist(B, Pos, Bin)).
-splitter(Left, Right, 0) ->
- {Left, Right};
-splitter(Left, [A | Right], RelPos) when is_list(A) or is_binary(A) ->
- Sz = erlang:iolist_size(A),
- case Sz > RelPos of
- true ->
- {Leftx, Rightx} = split_iolist(A, RelPos),
- {[Left | Leftx], [Rightx, Right]};
- _ ->
- splitter([Left | A], Right, RelPos - Sz)
- end;
-splitter(Left, [A | Right], RelPos) when is_integer(A) ->
- splitter([Left, A], Right, RelPos - 1);
-splitter(Left, Right, RelPos) when is_binary(Right) ->
- splitter(Left, [Right], RelPos).
+append_bins([Bin|Bins], B) when is_binary(Bin) ->
+ append_bins(Bins, <<B/binary, Bin/binary>>);
+append_bins([List|Bins], B) when is_list(List) ->
+ append_bins(Bins, append_bins(List, B));
+append_bins(Bin, B) when is_binary(Bin) ->
+ <<B/binary, Bin/binary>>;
+append_bins([_|_]=List, B) ->
+ <<B/binary, (iolist_to_binary(List))/binary>>;
+append_bins([], B) ->
+ B.
-skip_iolist(B, Pos) when is_binary(B) ->
- case B of
- <<_:Pos/binary, Bin/binary>> -> Bin;
- _ -> <<>>
- end;
-skip_iolist(L, Pos) when is_list(L) ->
- skipper(L, Pos).
-
-skipper(Right, 0) ->
- Right;
-skipper([A | Right], RelPos) when is_list(A) or is_binary(A) ->
- Sz = erlang:iolist_size(A),
- case Sz > RelPos of
- true ->
- Rightx = skip_iolist(A, RelPos),
- [Rightx, Right];
- _ ->
- skip_iolist(Right, RelPos - Sz)
- end;
-skipper([A | Right], RelPos) when is_integer(A) ->
- skip_iolist(Right, RelPos - 1).
+-dialyzer({no_improper_lists, pwrite_iolist/3}).
-pwrite_iolist(Iolist, Pos, Bin) ->
- {Left, Right} = split_iolist(Iolist, Pos),
+pwrite_iolist(B, Pos, Bin) ->
+ {Left, Right} = split_binary(B, Pos),
Sz = erlang:iolist_size(Bin),
- R = skip_iolist(Right, Sz),
+ R = skip_bin(Right, Sz),
[Left, Bin | R].
-pwrite_binary(B, Pos, Bin) ->
- erlang:iolist_to_binary(pwrite_iolist(B, Pos, Bin)).
+skip_bin(B, Pos) when is_binary(B) ->
+ case B of
+ <<_:Pos/binary, Bin/binary>> -> Bin;
+ _ -> <<>>
+ end.
%% ZIP header manipulations