aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--HOWTO/INSTALL.md25
-rw-r--r--erts/emulator/beam/erl_bif_port.c6
-rw-r--r--erts/emulator/test/port_SUITE.erl22
-rw-r--r--lib/asn1/src/asn1ct_check.erl2
-rw-r--r--lib/asn1/test/asn1_SUITE.erl16
-rw-r--r--lib/asn1/test/asn1_SUITE_data/TAGDEFAULT-AUTOMATIC.asn25
-rw-r--r--lib/kernel/doc/src/file.xml6
-rw-r--r--lib/kernel/src/file.erl62
-rw-r--r--lib/kernel/test/file_SUITE.erl61
-rw-r--r--lib/tools/src/cover.erl2
-rw-r--r--lib/tools/test/cover_SUITE_data/b.erl8
-rw-r--r--system/doc/reference_manual/typespec.xml3
12 files changed, 187 insertions, 51 deletions
diff --git a/HOWTO/INSTALL.md b/HOWTO/INSTALL.md
index 7a7e63164c..53b1b8cd8a 100644
--- a/HOWTO/INSTALL.md
+++ b/HOWTO/INSTALL.md
@@ -409,6 +409,30 @@ Some of the available `configure` options are:
If you or your system has special requirements please read the `Makefile` for
additional configuration information.
+#### Atomic Memory Operations and the VM ####
+
+The VM with SMP support makes quite a heavy use of atomic memory operations.
+An implementation providing native atomic memory operations is therefore very
+important when building Erlang/OTP. By default the VM will refuse to build
+if native atomic memory operations are not available.
+
+Erlang/OTP itself provides implementations of native atomic memory operations
+that can be used when compiling with a `gcc` compatible compiler on 32-bit
+and 64-bit x86, 32-bit and 64-bit SPARC V9, and 32-bit PowerPC. When compiling
+with a `gcc` compatible compiler on other architectures, the VM may be able to
+make use of native atomic operations using the `__sync_*` primitives, but this
+should only be used as a last resort since this wont give you optimal
+performance. When compiling on Windows using a MicroSoft Visual C++ compiler
+native atomic memory operations are provided by Windows APIs.
+
+You are recommended to use the native atomic implementation provided by
+Erlang/OTP, or the API provided by Windows. If these do not provide native
+atomics on your platform, you are recommended to build and install
+[libatomic_ops][] before building Erlang/OTP. The `libatomic_ops` library
+provides native atomic memory operations for a variety of platforms and
+compilers. When building Erlang/OTP you need to inform the build system of
+where the `libatomic_ops` library is installed using the
+`--with-libatomic_ops=PATH` configure switch.
### Building ###
@@ -862,3 +886,4 @@ under the License.
[Optional Utilities]: #Optional-Utilities
[Building on a Mac]: #Advanced-configuration-and-build-of-ErlangOTP_Building_OS-X-Darwin
[Building with wxErlang]: #Advanced-configuration-and-build-of-ErlangOTP_Building_Building-with-wxErlang
+ [libatomic_ops]: https://github.com/ivmai/libatomic_ops/
diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c
index 64bd598ba6..7ce950e090 100644
--- a/erts/emulator/beam/erl_bif_port.c
+++ b/erts/emulator/beam/erl_bif_port.c
@@ -472,7 +472,7 @@ cleanup_old_port_data(erts_aint_t data)
ErtsPortDataHeap *pdhp = (ErtsPortDataHeap *) data;
size_t size;
ERTS_SMP_DATA_DEPENDENCY_READ_MEMORY_BARRIER;
- size = sizeof(ErtsPortDataHeap) + pdhp->hsize*(sizeof(Eterm) - 1);
+ size = sizeof(ErtsPortDataHeap) + (pdhp->hsize-1)*sizeof(Eterm);
erts_schedule_thr_prgr_later_cleanup_op(free_port_data_heap,
(void *) pdhp,
&pdhp->later_op,
@@ -508,7 +508,7 @@ erts_port_data_size(Port *prt)
}
else {
ErtsPortDataHeap *pdhp = (ErtsPortDataHeap *) data;
- return (Uint) sizeof(ErtsPortDataHeap) + pdhp->hsize*(sizeof(Eterm)-1);
+ return (Uint) sizeof(ErtsPortDataHeap) + (pdhp->hsize-1)*sizeof(Eterm);
}
}
@@ -550,7 +550,7 @@ BIF_RETTYPE port_set_data_2(BIF_ALIST_2)
hsize = size_object(BIF_ARG_2);
pdhp = erts_alloc(ERTS_ALC_T_PORT_DATA_HEAP,
- sizeof(ErtsPortDataHeap) + hsize*(sizeof(Eterm)-1));
+ sizeof(ErtsPortDataHeap) + (hsize-1)*sizeof(Eterm));
hp = &pdhp->heap[0];
pdhp->off_heap.first = NULL;
pdhp->off_heap.overhead = 0;
diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl
index 8792f25d76..9083545060 100644
--- a/erts/emulator/test/port_SUITE.erl
+++ b/erts/emulator/test/port_SUITE.erl
@@ -2343,8 +2343,10 @@ port_setget_data(Config) when is_list(Config) ->
Port = erlang:open_port({spawn_driver, "echo_drv"}, []),
NSched = erlang:system_info(schedulers_online),
+ HeapData = {1,2,3,<<"A heap binary">>,fun()->"This is fun"end,
+ list_to_binary(lists:seq(1,100))},
PRs = lists:map(fun(I) ->
- spawn_opt(fun() -> port_setget_data_hammer(Port,1) end,
+ spawn_opt(fun() -> port_setget_data_hammer(Port,HeapData,false,1) end,
[monitor, {scheduler, I rem NSched}])
end,
lists:seq(1,10)),
@@ -2362,13 +2364,17 @@ port_setget_data(Config) when is_list(Config) ->
PRs),
ok.
-port_setget_data_hammer(Port, N) ->
+port_setget_data_hammer(Port, HeapData, IsSet0, N) ->
Rand = random:uniform(3),
- try case Rand of
- 1 -> true = erlang:port_set_data(Port, atom);
- 2 -> true = erlang:port_set_data(Port, {1,2,3});
- 3 -> erlang:port_get_data(Port)
- end
+ IsSet1 = try case Rand of
+ 1 -> true = erlang:port_set_data(Port, atom), true;
+ 2 -> true = erlang:port_set_data(Port, HeapData), true;
+ 3 -> case erlang:port_get_data(Port) of
+ atom -> true;
+ HeapData -> true;
+ undefined -> false=IsSet0
+ end
+ end
catch
error:badarg ->
true = get(prepare_for_close),
@@ -2381,7 +2387,7 @@ port_setget_data_hammer(Port, N) ->
after 0 ->
ok
end,
- port_setget_data_hammer(Port, N+1).
+ port_setget_data_hammer(Port, HeapData, IsSet1, N+1).
wait_until(Fun) ->
diff --git a/lib/asn1/src/asn1ct_check.erl b/lib/asn1/src/asn1ct_check.erl
index 7b33d1e5e7..240f1cbb16 100644
--- a/lib/asn1/src/asn1ct_check.erl
+++ b/lib/asn1/src/asn1ct_check.erl
@@ -6583,6 +6583,8 @@ merge_tags2([T1= #tag{type='IMPLICIT'}, T2 |Rest], Acc) ->
merge_tags2([T1#tag{type=T2#tag.type, form=T2#tag.form}|Rest],Acc);
merge_tags2([T1= #tag{type={default,'IMPLICIT'}}, T2 |Rest], Acc) ->
merge_tags2([T1#tag{type=T2#tag.type, form=T2#tag.form}|Rest],Acc);
+merge_tags2([T1= #tag{type={default,'AUTOMATIC'}}, T2 |Rest], Acc) ->
+ merge_tags2([T1#tag{type=T2#tag.type, form=T2#tag.form}|Rest],Acc);
merge_tags2([H|T],Acc) ->
merge_tags2(T, [H|Acc]);
merge_tags2([], Acc) ->
diff --git a/lib/asn1/test/asn1_SUITE.erl b/lib/asn1/test/asn1_SUITE.erl
index 888339a4d2..432197eec0 100644
--- a/lib/asn1/test/asn1_SUITE.erl
+++ b/lib/asn1/test/asn1_SUITE.erl
@@ -60,7 +60,8 @@ groups() ->
{ber, Parallel,
[ber_choiceinseq,
% Uses 'SOpttest'
- ber_optional]},
+ ber_optional,
+ tagdefault_automatic]},
{app_test, [], [{asn1_app_test, all}]},
@@ -659,6 +660,19 @@ ber_optional(Config, Rule, Opts) ->
{'C', asn1_NOVALUE, 111, asn1_NOVALUE}},
asn1_test_lib:roundtrip('SOpttest', 'S', V).
+tagdefault_automatic(Config) ->
+ test(Config, fun tagdefault_automatic/3, [ber]).
+tagdefault_automatic(Config, Rule, Opts) ->
+ asn1_test_lib:compile("TAGDEFAULT-AUTOMATIC", Config, [Rule|Opts]),
+ << 48,8,128,2,100,101,129,2,110,111 >> =
+ asn1_test_lib:roundtrip_enc('TAGDEFAULT-AUTOMATIC', 'Tagged', {'Tagged', << 100,101 >>, << 110,111 >>}),
+ << 48,8,128,2,100,101,129,2,110,111 >> =
+ asn1_test_lib:roundtrip_enc('TAGDEFAULT-AUTOMATIC', 'Untagged', {'Untagged', << 100,101 >>, << 110,111 >>}),
+ << 48,8,4,2,100,101,130,2,110,111 >> =
+ asn1_test_lib:roundtrip_enc('TAGDEFAULT-AUTOMATIC', 'Mixed', {'Mixed', << 100,101 >>, << 110,111 >>}),
+
+ ok.
+
%% records used by test-case default
-record('Def1', {bool0,
bool1 = asn1_DEFAULT,
diff --git a/lib/asn1/test/asn1_SUITE_data/TAGDEFAULT-AUTOMATIC.asn b/lib/asn1/test/asn1_SUITE_data/TAGDEFAULT-AUTOMATIC.asn
new file mode 100644
index 0000000000..2fcba1f71e
--- /dev/null
+++ b/lib/asn1/test/asn1_SUITE_data/TAGDEFAULT-AUTOMATIC.asn
@@ -0,0 +1,25 @@
+TAGDEFAULT-AUTOMATIC DEFINITIONS AUTOMATIC TAGS ::=
+BEGIN
+
+EXPORTS
+ Tagged,
+ Untagged,
+ Mixed
+;
+
+Tagged ::= SEQUENCE {
+ o0 [0] OCTET STRING,
+ o1 [1] OCTET STRING
+}
+
+Untagged ::= SEQUENCE {
+ o0 OCTET STRING,
+ o1 OCTET STRING
+}
+
+Mixed ::= SEQUENCE {
+ o0 OCTET STRING,
+ o2 [2] OCTET STRING
+}
+
+END
diff --git a/lib/kernel/doc/src/file.xml b/lib/kernel/doc/src/file.xml
index 1c03efe7fd..dcb9640dcf 100644
--- a/lib/kernel/doc/src/file.xml
+++ b/lib/kernel/doc/src/file.xml
@@ -1283,6 +1283,8 @@
or before unix time epoch which is 1970-01-01 00:00 UTC.
Default is <c>{time, local}</c>.
</p>
+ <p>If the <c>raw</c> option is set, the file server will not be called
+ and only informations about local files will be returned.</p>
<note>
<p>
Since file times is stored in posix time on most OS it is
@@ -1509,6 +1511,8 @@
the link will be returned in the <c>file_info</c> record and
the <c>type</c> field of the record will be set to
<c>symlink</c>.</p>
+ <p>If the <c>raw</c> option is set, the file server will not be called
+ and only informations about local files will be returned.</p>
<p>If <c><anno>Name</anno></c> is not a symbolic link, this function returns
exactly the same result as <c>read_file_info/1</c>.
On platforms that do not support symbolic links, this function
@@ -1847,6 +1851,8 @@
interpret it as universal time and <c>posix</c> must be seconds since
or before unix time epoch which is 1970-01-01 00:00 UTC.
Default is <c>{time, local}</c>.
+ <p>If the <c>raw</c> option is set, the file server will not be called
+ and only informations about local files will be returned.</p>
</p>
<p>The following fields are used from the record, if they are
given.</p>
diff --git a/lib/kernel/src/file.erl b/lib/kernel/src/file.erl
index ee2fb85de2..3d6665a36a 100644
--- a/lib/kernel/src/file.erl
+++ b/lib/kernel/src/file.erl
@@ -114,7 +114,7 @@
-type sendfile_option() :: {chunk_size, non_neg_integer()}
| {use_threads, boolean()}.
-type file_info_option() :: {'time', 'local'} | {'time', 'universal'}
- | {'time', 'posix'}.
+ | {'time', 'posix'} | raw.
%%% BIFs
-export([native_name_encoding/0]).
@@ -242,7 +242,19 @@ read_file_info(Name) ->
Reason :: posix() | badarg.
read_file_info(Name, Opts) when is_list(Opts) ->
- check_and_call(read_file_info, [file_name(Name), Opts]).
+ Args = [file_name(Name), Opts],
+ case check_args(Args) of
+ ok ->
+ case lists:member(raw, Opts) of
+ true ->
+ [FileName|_] = Args,
+ ?PRIM_FILE:read_file_info(FileName, Opts);
+ false ->
+ call(read_file_info, Args)
+ end;
+ Error ->
+ Error
+ end.
-spec altname(Name :: name_all()) -> any().
@@ -264,7 +276,19 @@ read_link_info(Name) ->
Reason :: posix() | badarg.
read_link_info(Name, Opts) when is_list(Opts) ->
- check_and_call(read_link_info, [file_name(Name),Opts]).
+ Args = [file_name(Name), Opts],
+ case check_args(Args) of
+ ok ->
+ case lists:member(raw, Opts) of
+ true ->
+ [FileName|_] = Args,
+ ?PRIM_FILE:read_link_info(FileName, Opts);
+ false ->
+ call(read_link_info, Args)
+ end;
+ Error ->
+ Error
+ end.
-spec read_link(Name) -> {ok, Filename} | {error, Reason} when
@@ -298,7 +322,19 @@ write_file_info(Name, Info = #file_info{}) ->
Reason :: posix() | badarg.
write_file_info(Name, Info = #file_info{}, Opts) when is_list(Opts) ->
- check_and_call(write_file_info, [file_name(Name), Info, Opts]).
+ Args = [file_name(Name), Info, Opts],
+ case check_args(Args) of
+ ok ->
+ case lists:member(raw, Opts) of
+ true ->
+ [FileName|_] = Args,
+ ?PRIM_FILE:write_file_info(FileName, Info, Opts);
+ false ->
+ call(write_file_info, Args)
+ end;
+ Error ->
+ Error
+ end.
-spec list_dir(Dir) -> {ok, Filenames} | {error, Reason} when
Dir :: name_all(),
@@ -384,26 +420,12 @@ write_file(Name, Bin, ModeList) when is_list(ModeList) ->
%% Obsolete, undocumented, local node only, don't use!.
%% XXX to be removed.
raw_read_file_info(Name) ->
- Args = [file_name(Name)],
- case check_args(Args) of
- ok ->
- [FileName] = Args,
- ?PRIM_FILE:read_file_info(FileName);
- Error ->
- Error
- end.
+ read_file_info(Name, [raw]).
%% Obsolete, undocumented, local node only, don't use!.
%% XXX to be removed.
raw_write_file_info(Name, #file_info{} = Info) ->
- Args = [file_name(Name)],
- case check_args(Args) of
- ok ->
- [FileName] = Args,
- ?PRIM_FILE:write_file_info(FileName, Info);
- Error ->
- Error
- end.
+ write_file_info(Name, Info, [raw]).
%%%-----------------------------------------------------------------
%%% File io server functions.
diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl
index f6d6cd94ab..56c35678b6 100644
--- a/lib/kernel/test/file_SUITE.erl
+++ b/lib/kernel/test/file_SUITE.erl
@@ -1236,9 +1236,10 @@ file_info_basic_file(Config) when is_list(Config) ->
%% Test that the file has the expected attributes.
%% The times are tricky, so we will save them to a separate test case.
- ?line {ok,#file_info{size=Size,type=Type,access=Access,
- atime=AccessTime,mtime=ModifyTime}} =
- ?FILE_MODULE:read_file_info(Name),
+ {ok,FileInfo} = ?FILE_MODULE:read_file_info(Name),
+ {ok,FileInfo} = ?FILE_MODULE:read_file_info(Name, [raw]),
+ #file_info{size=Size,type=Type,access=Access,
+ atime=AccessTime,mtime=ModifyTime} = FileInfo,
?line io:format("Access ~p, Modify ~p", [AccessTime, ModifyTime]),
?line Size = 7,
?line Type = regular,
@@ -1280,9 +1281,10 @@ file_info_basic_directory(Config) when is_list(Config) ->
test_server:timetrap_cancel(Dog).
test_directory(Name, ExpectedAccess) ->
- ?line {ok,#file_info{size=Size,type=Type,access=Access,
- atime=AccessTime,mtime=ModifyTime}} =
- ?FILE_MODULE:read_file_info(Name),
+ {ok,FileInfo} = ?FILE_MODULE:read_file_info(Name),
+ {ok,FileInfo} = ?FILE_MODULE:read_file_info(Name, [raw]),
+ #file_info{size=Size,type=Type,access=Access,
+ atime=AccessTime,mtime=ModifyTime} = FileInfo,
?line io:format("Testing directory ~s", [Name]),
?line io:format("Directory size is ~p", [Size]),
?line io:format("Access ~p", [Access]),
@@ -1307,11 +1309,11 @@ file_info_bad(doc) -> [];
file_info_bad(Config) when is_list(Config) ->
?line Dog = test_server:timetrap(test_server:seconds(5)),
?line RootDir = filename:join([?config(priv_dir, Config)]),
- ?line {error, enoent} =
- ?FILE_MODULE:read_file_info(
- filename:join(RootDir,
- atom_to_list(?MODULE)++ "_nonexistent")),
+ FileName = filename:join(RootDir, atom_to_list(?MODULE) ++ "_nonexistent"),
+ {error,enoent} = ?FILE_MODULE:read_file_info(FileName),
+ {error,enoent} = ?FILE_MODULE:read_file_info(FileName, [raw]),
?line {error, enoent} = ?FILE_MODULE:read_file_info(""),
+ {error, enoent} = ?FILE_MODULE:read_file_info("", [raw]),
?line [] = flush(),
?line test_server:timetrap_cancel(Dog),
ok.
@@ -1346,8 +1348,16 @@ file_info_int(Config) ->
?line io:put_chars(Fd1,"foo"),
%% check that the file got a modify date max a few seconds away from now
- ?line {ok,#file_info{type=regular,atime=AccTime1,mtime=ModTime1}} =
- ?FILE_MODULE:read_file_info(Name),
+ {ok,FileInfo1} = ?FILE_MODULE:read_file_info(Name),
+ {ok,FileInfo1Raw} = ?FILE_MODULE:read_file_info(Name, [raw]),
+
+ %% We assert that everything but the size is the same, on some OSs the
+ %% size may not have been flushed to disc and we do not want to do a
+ %% sync to force it.
+ FileInfo1Raw = FileInfo1#file_info{ size = FileInfo1Raw#file_info.size },
+
+ #file_info{type=regular,atime=AccTime1,mtime=ModTime1} = FileInfo1,
+
?line Now = erlang:localtime(), %???
?line io:format("Now ~p",[Now]),
?line io:format("Open file Acc ~p Mod ~p",[AccTime1,ModTime1]),
@@ -1363,9 +1373,10 @@ file_info_int(Config) ->
%% close the file, and watch the modify date change
?line ok = ?FILE_MODULE:close(Fd1),
- ?line {ok,#file_info{size=Size,type=regular,access=Access,
- atime=AccTime2,mtime=ModTime2}} =
- ?FILE_MODULE:read_file_info(Name),
+ {ok,FileInfo2} = ?FILE_MODULE:read_file_info(Name),
+ {ok,FileInfo2} = ?FILE_MODULE:read_file_info(Name, [raw]),
+ #file_info{size=Size,type=regular,access=Access,
+ atime=AccTime2,mtime=ModTime2} = FileInfo2,
?line io:format("Closed file Acc ~p Mod ~p",[AccTime2,ModTime2]),
?line true = time_dist(ModTime1,ModTime2) >= 0,
@@ -1374,9 +1385,10 @@ file_info_int(Config) ->
?line Access = read_write,
%% Do some directory checking
- ?line {ok,#file_info{size=DSize,type=directory,access=DAccess,
- atime=AccTime3,mtime=ModTime3}} =
- ?FILE_MODULE:read_file_info(RootDir),
+ {ok,FileInfo3} = ?FILE_MODULE:read_file_info(RootDir),
+ {ok,FileInfo3} = ?FILE_MODULE:read_file_info(RootDir, [raw]),
+ #file_info{size=DSize,type=directory,access=DAccess,
+ atime=AccTime3,mtime=ModTime3} = FileInfo3,
%% this dir was modified only a few secs ago
?line io:format("Dir Acc ~p; Mod ~p; Now ~p", [AccTime3, ModTime3, Now]),
?line true = abs(time_dist(Now,ModTime3)) < 5,
@@ -1449,6 +1461,12 @@ file_write_file_info(Config) when is_list(Config) ->
?line ?FILE_MODULE:write_file_info(Name1, #file_info{mode=8#400}),
?line {error, eacces} = ?FILE_MODULE:write_file(Name1, "hello again"),
+ %% Same with raw.
+ ?FILE_MODULE:write_file_info(Name1, #file_info{mode=8#600}, [raw]),
+ ok = ?FILE_MODULE:write_file(Name1, "hello again"),
+ ?FILE_MODULE:write_file_info(Name1, #file_info{mode=8#400}, [raw]),
+ {error,eacces} = ?FILE_MODULE:write_file(Name1, "hello again"),
+
%% Write the times again.
%% Note: Seconds must be even; see note in file_info_times/1.
@@ -2650,7 +2668,9 @@ make_link(Config) when is_list(Config) ->
%% since they are not used on symbolic links.
?line {ok, Info} = ?FILE_MODULE:read_link_info(Name),
+ {ok,Info} = ?FILE_MODULE:read_link_info(Name, [raw]),
?line {ok, Info} = ?FILE_MODULE:read_link_info(Alias),
+ {ok,Info} = ?FILE_MODULE:read_link_info(Alias, [raw]),
?line #file_info{links = 2, type = regular} = Info,
?line {error, eexist} =
?FILE_MODULE:make_link(Name, Alias),
@@ -2670,6 +2690,7 @@ read_link_info_for_non_link(Config) when is_list(Config) ->
?line {ok, #file_info{type=directory}} =
?FILE_MODULE:read_link_info("."),
+ {ok, #file_info{type=directory}} = ?FILE_MODULE:read_link_info(".", [raw]),
?line [] = flush(),
?line test_server:timetrap_cancel(Dog),
@@ -2700,11 +2721,15 @@ symlinks(Config) when is_list(Config) ->
{skipped, "Windows user not privileged to create symlinks"};
ok ->
?line {ok, Info1} = ?FILE_MODULE:read_file_info(Name),
+ {ok,Info1} = ?FILE_MODULE:read_file_info(Name, [raw]),
?line {ok, Info1} = ?FILE_MODULE:read_file_info(Alias),
+ {ok,Info1} = ?FILE_MODULE:read_file_info(Alias, [raw]),
?line {ok, Info1} = ?FILE_MODULE:read_link_info(Name),
+ {ok,Info1} = ?FILE_MODULE:read_link_info(Name, [raw]),
?line #file_info{links = 1, type = regular} = Info1,
?line {ok, Info2} = ?FILE_MODULE:read_link_info(Alias),
+ {ok,Info2} = ?FILE_MODULE:read_link_info(Alias, [raw]),
?line #file_info{links=1, type=symlink} = Info2,
?line {ok, Name} = ?FILE_MODULE:read_link(Alias),
{ok, Name} = ?FILE_MODULE:read_link_all(Alias),
diff --git a/lib/tools/src/cover.erl b/lib/tools/src/cover.erl
index 113fa24bd5..31754015f7 100644
--- a/lib/tools/src/cover.erl
+++ b/lib/tools/src/cover.erl
@@ -1696,6 +1696,8 @@ fix_expr(T, Line, Bump) when is_tuple(T) ->
fix_expr(E, _Line, _Bump) ->
E.
+fix_clauses([], _Line, _Bump) ->
+ [];
fix_clauses(Cs, Line, Bump) ->
case bumps_line(lists:last(Cs), Line) of
true ->
diff --git a/lib/tools/test/cover_SUITE_data/b.erl b/lib/tools/test/cover_SUITE_data/b.erl
index 13f39b8cb9..0a418a58d8 100644
--- a/lib/tools/test/cover_SUITE_data/b.erl
+++ b/lib/tools/test/cover_SUITE_data/b.erl
@@ -1,5 +1,5 @@
-module(b).
--export([start/0, loop/0]).
+-export([start/0, loop/0, wait/0]).
start() ->
spawn(?MODULE, loop, []).
@@ -12,3 +12,9 @@ loop() ->
stop ->
done
end.
+
+%% This checks for a bug in expressions which have no
+%% "main" clauses (only after and friends) followed by
+%% a return value in the same line.
+wait() ->
+ receive after 1000 -> done end, ok.
diff --git a/system/doc/reference_manual/typespec.xml b/system/doc/reference_manual/typespec.xml
index 321f0a24d5..e4aa2ceda6 100644
--- a/system/doc/reference_manual/typespec.xml
+++ b/system/doc/reference_manual/typespec.xml
@@ -217,6 +217,9 @@
<cell><c>iolist()</c></cell><cell><c>maybe_improper_list(byte() | binary() | iolist(), binary() | [])</c></cell>
</row>
<row>
+ <cell><c>function()</c></cell><cell><c>fun()</c></cell>
+ </row>
+ <row>
<cell><c>module()</c></cell><cell><c>atom()</c></cell>
</row>
<row>