aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--erts/preloaded/src/erts_code_purger.erl4
-rw-r--r--erts/preloaded/src/zlib.erl50
-rw-r--r--lib/compiler/src/beam_asm.erl4
-rw-r--r--lib/dialyzer/src/dialyzer.erl4
-rw-r--r--lib/dialyzer/src/dialyzer_contracts.erl99
-rw-r--r--lib/dialyzer/src/typer.erl2
-rw-r--r--lib/dialyzer/test/overspecs_SUITE_data/dialyzer_options1
-rw-r--r--lib/dialyzer/test/overspecs_SUITE_data/results/iodata2
-rw-r--r--lib/dialyzer/test/overspecs_SUITE_data/results/iolist2
-rw-r--r--lib/dialyzer/test/overspecs_SUITE_data/src/iodata.erl41
-rw-r--r--lib/dialyzer/test/overspecs_SUITE_data/src/iolist.erl41
-rw-r--r--lib/dialyzer/test/specdiffs_SUITE_data/dialyzer_options1
-rw-r--r--lib/dialyzer/test/specdiffs_SUITE_data/results/iodata3
-rw-r--r--lib/dialyzer/test/specdiffs_SUITE_data/results/iolist2
-rw-r--r--lib/dialyzer/test/specdiffs_SUITE_data/src/iodata.erl41
-rw-r--r--lib/dialyzer/test/specdiffs_SUITE_data/src/iolist.erl41
-rw-r--r--lib/kernel/src/code.erl4
-rw-r--r--lib/kernel/src/file_server.erl6
-rw-r--r--lib/kernel/src/kernel_refc.erl4
-rw-r--r--lib/stdlib/src/array.erl2
20 files changed, 292 insertions, 62 deletions
diff --git a/erts/preloaded/src/erts_code_purger.erl b/erts/preloaded/src/erts_code_purger.erl
index fd214228c7..c41532ed87 100644
--- a/erts/preloaded/src/erts_code_purger.erl
+++ b/erts/preloaded/src/erts_code_purger.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2016. All Rights Reserved.
+%% Copyright Ericsson AB 2016-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -25,7 +25,7 @@
-export([start/0, purge/1, soft_purge/1, pending_purge_lambda/3,
finish_after_on_load/2]).
--spec start() -> term().
+-spec start() -> no_return().
start() ->
register(erts_code_purger, self()),
process_flag(trap_exit, true),
diff --git a/erts/preloaded/src/zlib.erl b/erts/preloaded/src/zlib.erl
index a4ef42204d..6f53e67901 100644
--- a/erts/preloaded/src/zlib.erl
+++ b/erts/preloaded/src/zlib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2017. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -87,7 +87,7 @@
%%------------------------------------------------------------------------
%% Public data types.
--type zstream() :: term().
+-type zstream() :: reference().
-type zflush() :: 'none' | 'sync' | 'full' | 'finish'.
-type zlevel() ::
@@ -102,11 +102,11 @@
-type zmethod() :: 'deflated'.
-record(zlib_opts, {
- stream :: zstream(),
- method :: term(),
- input_chunk_size :: integer(),
- output_chunk_size :: integer(),
- flush :: integer()
+ stream :: zstream() | 'undefined',
+ method :: function(),
+ input_chunk_size :: pos_integer(),
+ output_chunk_size :: pos_integer(),
+ flush :: non_neg_integer()
}).
%%------------------------------------------------------------------------
@@ -168,7 +168,7 @@ deflateInit_nif(_Z, _Level, _Method, _WindowBits, _MemLevel, _Strategy) ->
-spec deflateSetDictionary(Z, Dictionary) -> Adler32 when
Z :: zstream(),
Dictionary :: iodata(),
- Adler32 :: integer().
+ Adler32 :: non_neg_integer().
deflateSetDictionary(Z, Dictionary) ->
deflateSetDictionary_nif(Z, Dictionary).
deflateSetDictionary_nif(_Z, _Dictionary) ->
@@ -295,7 +295,7 @@ inflate(Z, Data) ->
Options :: list({exception_on_need_dict, boolean()}),
Decompressed :: iolist() |
{need_dictionary,
- Adler32 :: integer(),
+ Adler32 :: non_neg_integer(),
Output :: iolist()}.
inflate(Z, Data, Options) ->
enqueue_input(Z, Data),
@@ -357,7 +357,7 @@ exception_on_need_dict(Z, Output) when is_list(Output); is_binary(Output) ->
Result :: {continue, Output :: iolist()} |
{finished, Output :: iolist()} |
{need_dictionary,
- Adler32 :: integer(),
+ Adler32 :: non_neg_integer(),
Output :: iolist()}.
safeInflate(Z, Data) ->
enqueue_input(Z, Data),
@@ -389,7 +389,7 @@ getBufSize_nif(_Z) ->
-spec crc32(Z) -> CRC when
Z :: zstream(),
- CRC :: integer().
+ CRC :: non_neg_integer().
crc32(Z) ->
crc32_nif(Z).
crc32_nif(_Z) ->
@@ -398,7 +398,7 @@ crc32_nif(_Z) ->
-spec crc32(Z, Data) -> CRC when
Z :: zstream(),
Data :: iodata(),
- CRC :: integer().
+ CRC :: non_neg_integer().
crc32(Z, Data) when is_reference(Z) ->
erlang:crc32(Data);
crc32(_Z, _Data) ->
@@ -406,9 +406,9 @@ crc32(_Z, _Data) ->
-spec crc32(Z, PrevCRC, Data) -> CRC when
Z :: zstream(),
- PrevCRC :: integer(),
+ PrevCRC :: non_neg_integer(),
Data :: iodata(),
- CRC :: integer().
+ CRC :: non_neg_integer().
crc32(Z, CRC, Data) when is_reference(Z) ->
erlang:crc32(CRC, Data);
crc32(_Z, _CRC, _Data) ->
@@ -416,10 +416,10 @@ crc32(_Z, _CRC, _Data) ->
-spec crc32_combine(Z, CRC1, CRC2, Size2) -> CRC when
Z :: zstream(),
- CRC :: integer(),
- CRC1 :: integer(),
- CRC2 :: integer(),
- Size2 :: integer().
+ CRC :: non_neg_integer(),
+ CRC1 :: non_neg_integer(),
+ CRC2 :: non_neg_integer(),
+ Size2 :: non_neg_integer().
crc32_combine(Z, CRC1, CRC2, Size2) when is_reference(Z) ->
erlang:crc32_combine(CRC1, CRC2, Size2);
crc32_combine(_Z, _CRC1, _CRC2, _Size2) ->
@@ -428,7 +428,7 @@ crc32_combine(_Z, _CRC1, _CRC2, _Size2) ->
-spec adler32(Z, Data) -> CheckSum when
Z :: zstream(),
Data :: iodata(),
- CheckSum :: integer().
+ CheckSum :: non_neg_integer().
adler32(Z, Data) when is_reference(Z) ->
erlang:adler32(Data);
adler32(_Z, _Data) ->
@@ -436,9 +436,9 @@ adler32(_Z, _Data) ->
-spec adler32(Z, PrevAdler, Data) -> CheckSum when
Z :: zstream(),
- PrevAdler :: integer(),
+ PrevAdler :: non_neg_integer(),
Data :: iodata(),
- CheckSum :: integer().
+ CheckSum :: non_neg_integer().
adler32(Z, Adler, Data) when is_reference(Z) ->
erlang:adler32(Adler, Data);
adler32(_Z, _Adler, _Data) ->
@@ -446,10 +446,10 @@ adler32(_Z, _Adler, _Data) ->
-spec adler32_combine(Z, Adler1, Adler2, Size2) -> Adler when
Z :: zstream(),
- Adler :: integer(),
- Adler1 :: integer(),
- Adler2 :: integer(),
- Size2 :: integer().
+ Adler :: non_neg_integer(),
+ Adler1 :: non_neg_integer(),
+ Adler2 :: non_neg_integer(),
+ Size2 :: non_neg_integer().
adler32_combine(Z, Adler1, Adler2, Size2) when is_reference(Z) ->
erlang:adler32_combine(Adler1, Adler2, Size2);
adler32_combine(_Z, _Adler1, _Adler2, _Size2) ->
diff --git a/lib/compiler/src/beam_asm.erl b/lib/compiler/src/beam_asm.erl
index 5ef340c831..df0321e85a 100644
--- a/lib/compiler/src/beam_asm.erl
+++ b/lib/compiler/src/beam_asm.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -475,7 +475,7 @@ encode_alloc_list_1([{floats,Floats}|T], Dict, Acc0) ->
encode_alloc_list_1([], Dict, Acc) ->
{iolist_to_binary(Acc),Dict}.
--spec encode(non_neg_integer(), integer()) -> iodata().
+-spec encode(non_neg_integer(), integer()) -> iolist() | integer().
encode(Tag, N) when N < 0 ->
encode1(Tag, negative_to_bytes(N));
diff --git a/lib/dialyzer/src/dialyzer.erl b/lib/dialyzer/src/dialyzer.erl
index 1538174d4a..185c8c9ae6 100644
--- a/lib/dialyzer/src/dialyzer.erl
+++ b/lib/dialyzer/src/dialyzer.erl
@@ -409,6 +409,10 @@ message_to_string({extra_range, [M, F, A, ExtraRanges, SigRange]}) ->
io_lib:format("The specification for ~w:~tw/~w states that the function"
" might also return ~ts but the inferred return is ~ts\n",
[M, F, A, ExtraRanges, SigRange]);
+message_to_string({missing_range, [M, F, A, ExtraRanges, ContrRange]}) ->
+ io_lib:format("The success typing for ~w:~tw/~w implies that the function"
+ " might also return ~ts but the specification return is ~ts\n",
+ [M, F, A, ExtraRanges, ContrRange]);
message_to_string({overlapping_contract, [M, F, A]}) ->
io_lib:format("Overloaded contract for ~w:~tw/~w has overlapping domains;"
" such contracts are currently unsupported and are simply ignored\n",
diff --git a/lib/dialyzer/src/dialyzer_contracts.erl b/lib/dialyzer/src/dialyzer_contracts.erl
index 0df15e55f9..af7f4385ad 100644
--- a/lib/dialyzer/src/dialyzer_contracts.erl
+++ b/lib/dialyzer/src/dialyzer_contracts.erl
@@ -197,9 +197,11 @@ check_contracts(Contracts, Callgraph, FunTypes, ModOpaques) ->
false ->
[{MFA, Contract}|NewContracts]
end;
- {error, {extra_range, _, _}} ->
- %% do not treat extra range as an error in this check
- %% since that prevents discovering other actual errors
+ {range_warnings, _} ->
+ %% do not treat extra range, either in contract or
+ %% in success typing, as an error in this check
+ %% since that prevents discovering other actual
+ %% errors
[{MFA, Contract}|NewContracts];
{error, _Error} -> NewContracts
end;
@@ -210,14 +212,26 @@ check_contracts(Contracts, Callgraph, FunTypes, ModOpaques) ->
end,
orddict:from_list(lists:foldl(FoldFun, [], orddict:to_list(FunTypes))).
+-type check_contract_return() ::
+ 'ok'
+ | {'error',
+ 'invalid_contract'
+ | {'opaque_mismatch', erl_types:erl_type()}
+ | {'overlapping_contract', [module() | atom() | byte()]}
+ | string()}
+ | {'range_warnings',
+ [{'error', {'extra_range' | 'missing_range',
+ erl_types:erl_type(),
+ erl_types:erl_type()}}]}.
+
%% Checks all components of a contract
--spec check_contract(#contract{}, erl_types:erl_type()) -> 'ok' | {'error', term()}.
+-spec check_contract(#contract{}, erl_types:erl_type()) -> check_contract_return().
check_contract(Contract, SuccType) ->
check_contract(Contract, SuccType, 'universe').
-spec check_contract(#contract{}, erl_types:erl_type(), erl_types:opaques()) ->
- 'ok' | {'error', term()}.
+ check_contract_return().
check_contract(#contract{contracts = Contracts}, SuccType, Opaques) ->
try
@@ -290,15 +304,23 @@ check_contract_inf_list([], _SuccType, _Opaques, OM) ->
check_extraneous([], _SuccType) -> ok;
check_extraneous([C|Cs], SuccType) ->
case check_extraneous_1(C, SuccType) of
- ok -> check_extraneous(Cs, SuccType);
- Error -> Error
+ {error, invalid_contract} = Error ->
+ Error;
+ {error, {extra_range, _, _}} = Error ->
+ {range_warnings, [Error | check_missing(C, SuccType)]};
+ ok ->
+ case check_missing(C, SuccType) of
+ [] -> check_extraneous(Cs, SuccType);
+ ErrorL -> {range_warnings, ErrorL}
+ end
end.
check_extraneous_1(Contract, SuccType) ->
CRng = erl_types:t_fun_range(Contract),
CRngs = erl_types:t_elements(CRng),
STRng = erl_types:t_fun_range(SuccType),
- ?debug("CR = ~tp\nSR = ~tp\n", [CRngs, STRng]),
+ ?debug("\nCR = ~ts\nSR = ~ts\n", [erl_types:t_to_string(CRng),
+ erl_types:t_to_string(STRng)]),
case [CR || CR <- CRngs,
erl_types:t_is_none(erl_types:t_inf(CR, STRng))] of
[] ->
@@ -341,6 +363,18 @@ map_part(Type) ->
is_empty_map(Type) ->
erl_types:t_is_equal(Type, erl_types:t_from_term(#{})).
+check_missing(Contract, SuccType) ->
+ CRng = erl_types:t_fun_range(Contract),
+ STRng = erl_types:t_fun_range(SuccType),
+ STRngs = erl_types:t_elements(STRng),
+ ?debug("\nCR = ~ts\nSR = ~ts\n", [erl_types:t_to_string(CRng),
+ erl_types:t_to_string(STRng)]),
+ case [STR || STR <- STRngs,
+ erl_types:t_is_none(erl_types:t_inf(STR, CRng))] of
+ [] -> [];
+ STRs -> [{error, {missing_range, erl_types:t_sup(STRs), CRng}}]
+ end.
+
%% This is the heart of the "range function"
-spec process_contracts([contract_pair()], [erl_types:erl_type()]) ->
erl_types:erl_type().
@@ -712,22 +746,30 @@ get_invalid_contract_warnings_funs([{MFA, {FileLine, Contract, _Xtra}}|Left],
[W|Acc];
{error, {overlapping_contract, []}} ->
[overlapping_contract_warning(MFA, WarningInfo)|Acc];
- {error, {extra_range, ExtraRanges, STRange}} ->
- Warn =
- case t_from_forms_without_remote(Contract#contract.forms,
- MFA, RecDict) of
- {ok, NoRemoteType} ->
- CRet = erl_types:t_fun_range(NoRemoteType),
- erl_types:t_is_subtype(ExtraRanges, CRet);
- unsupported ->
- true
- end,
- case Warn of
- true ->
- [extra_range_warning(MFA, WarningInfo, ExtraRanges, STRange)|Acc];
- false ->
- Acc
- end;
+ {range_warnings, Errors} ->
+ Fun =
+ fun({error, {extra_range, ExtraRanges, STRange}}, Acc0) ->
+ Warn =
+ case t_from_forms_without_remote(Contract#contract.forms,
+ MFA, RecDict) of
+ {ok, NoRemoteType} ->
+ CRet = erl_types:t_fun_range(NoRemoteType),
+ erl_types:t_is_subtype(ExtraRanges, CRet);
+ unsupported ->
+ true
+ end,
+ case Warn of
+ true ->
+ [extra_range_warning(MFA, WarningInfo,
+ ExtraRanges, STRange)|Acc0];
+ false ->
+ Acc0
+ end;
+ ({error, {missing_range, ExtraRanges, CRange}}, Acc0) ->
+ [missing_range_warning(MFA, WarningInfo,
+ ExtraRanges, CRange)|Acc0]
+ end,
+ lists:foldl(Fun, Acc, Errors);
{error, Msg} ->
[{?WARN_CONTRACT_SYNTAX, WarningInfo, Msg}|Acc];
ok ->
@@ -745,6 +787,9 @@ get_invalid_contract_warnings_funs([{MFA, {FileLine, Contract, _Xtra}}|Left],
{error, _} ->
[invalid_contract_warning(MFA, WarningInfo, BifSig, RecDict)
|Acc];
+ {range_warnings, _} ->
+ picky_contract_check(CSig, BifSig, MFA, WarningInfo,
+ Contract, RecDict, Acc);
ok ->
picky_contract_check(CSig, BifSig, MFA, WarningInfo,
Contract, RecDict, Acc)
@@ -778,6 +823,12 @@ extra_range_warning({M, F, A}, WarningInfo, ExtraRanges, STRange) ->
{?WARN_CONTRACT_SUPERTYPE, WarningInfo,
{extra_range, [M, F, A, ERangesStr, STRangeStr]}}.
+missing_range_warning({M, F, A}, WarningInfo, ExtraRanges, CRange) ->
+ ERangesStr = erl_types:t_to_string(ExtraRanges),
+ CRangeStr = erl_types:t_to_string(CRange),
+ {?WARN_CONTRACT_SUBTYPE, WarningInfo,
+ {missing_range, [M, F, A, ERangesStr, CRangeStr]}}.
+
picky_contract_check(CSig0, Sig0, MFA, WarningInfo, Contract, RecDict, Acc) ->
CSig = erl_types:t_abstract_records(CSig0, RecDict),
Sig = erl_types:t_abstract_records(Sig0, RecDict),
diff --git a/lib/dialyzer/src/typer.erl b/lib/dialyzer/src/typer.erl
index 9d3d9ce438..4b99f5f72e 100644
--- a/lib/dialyzer/src/typer.erl
+++ b/lib/dialyzer/src/typer.erl
@@ -401,7 +401,7 @@ get_type({{M, F, A} = MFA, Range, Arg}, CodeServer, Records) ->
Sig = erl_types:t_fun(Arg, Range),
case dialyzer_contracts:check_contract(Contract, Sig) of
ok -> {{F, A}, {contract, Contract}};
- {error, {extra_range, _, _}} ->
+ {range_warnings, _} ->
{{F, A}, {contract, Contract}};
{error, {overlapping_contract, []}} ->
{{F, A}, {contract, Contract}};
diff --git a/lib/dialyzer/test/overspecs_SUITE_data/dialyzer_options b/lib/dialyzer/test/overspecs_SUITE_data/dialyzer_options
new file mode 100644
index 0000000000..ff4517e59d
--- /dev/null
+++ b/lib/dialyzer/test/overspecs_SUITE_data/dialyzer_options
@@ -0,0 +1 @@
+{dialyzer_options, [{warnings, [overspecs]}]}.
diff --git a/lib/dialyzer/test/overspecs_SUITE_data/results/iodata b/lib/dialyzer/test/overspecs_SUITE_data/results/iodata
new file mode 100644
index 0000000000..d9c70330ec
--- /dev/null
+++ b/lib/dialyzer/test/overspecs_SUITE_data/results/iodata
@@ -0,0 +1,2 @@
+
+iodata.erl:7: The success typing for iodata:encode/2 implies that the function might also return integer() but the specification return is binary() | maybe_improper_list(binary() | maybe_improper_list(any(),binary() | []) | byte(),binary() | [])
diff --git a/lib/dialyzer/test/overspecs_SUITE_data/results/iolist b/lib/dialyzer/test/overspecs_SUITE_data/results/iolist
new file mode 100644
index 0000000000..ca556f017c
--- /dev/null
+++ b/lib/dialyzer/test/overspecs_SUITE_data/results/iolist
@@ -0,0 +1,2 @@
+
+iolist.erl:7: The success typing for iolist:encode/2 implies that the function might also return integer() but the specification return is maybe_improper_list(binary() | maybe_improper_list(any(),binary() | []) | byte(),binary() | [])
diff --git a/lib/dialyzer/test/overspecs_SUITE_data/src/iodata.erl b/lib/dialyzer/test/overspecs_SUITE_data/src/iodata.erl
new file mode 100644
index 0000000000..caa44f6c91
--- /dev/null
+++ b/lib/dialyzer/test/overspecs_SUITE_data/src/iodata.erl
@@ -0,0 +1,41 @@
+-module(iodata).
+
+%% A small part of beam_asm.
+
+-export([encode/2]).
+
+-spec encode(non_neg_integer(), integer()) -> iodata(). % extra range binary()
+
+encode(Tag, N) when Tag >= 0, N < 0 ->
+ encode1(Tag, negative_to_bytes(N));
+encode(Tag, N) when Tag >= 0, N < 16 ->
+ (N bsl 4) bor Tag; % not in the specification
+encode(Tag, N) when Tag >= 0, N < 16#800 ->
+ [((N bsr 3) band 2#11100000) bor Tag bor 2#00001000, N band 16#ff];
+encode(Tag, N) when Tag >= 0 ->
+ encode1(Tag, to_bytes(N)).
+
+encode1(Tag, Bytes) ->
+ case iolist_size(Bytes) of
+ Num when 2 =< Num, Num =< 8 ->
+ [((Num-2) bsl 5) bor 2#00011000 bor Tag| Bytes];
+ Num when 8 < Num ->
+ [2#11111000 bor Tag, encode(0, Num-9)| Bytes]
+ end.
+
+to_bytes(N) ->
+ Bin = binary:encode_unsigned(N),
+ case Bin of
+ <<0:1,_/bits>> -> Bin;
+ <<1:1,_/bits>> -> [0,Bin]
+ end.
+
+negative_to_bytes(N) when N >= -16#8000 ->
+ <<N:16>>;
+negative_to_bytes(N) ->
+ Bytes = byte_size(binary:encode_unsigned(-N)),
+ Bin = <<N:Bytes/unit:8>>,
+ case Bin of
+ <<0:1,_/bits>> -> [16#ff,Bin];
+ <<1:1,_/bits>> -> Bin
+ end.
diff --git a/lib/dialyzer/test/overspecs_SUITE_data/src/iolist.erl b/lib/dialyzer/test/overspecs_SUITE_data/src/iolist.erl
new file mode 100644
index 0000000000..7cceeda24e
--- /dev/null
+++ b/lib/dialyzer/test/overspecs_SUITE_data/src/iolist.erl
@@ -0,0 +1,41 @@
+-module(iolist).
+
+%% A small part of beam_asm.
+
+-export([encode/2]).
+
+-spec encode(non_neg_integer(), integer()) -> iolist().
+
+encode(Tag, N) when Tag >= 0, N < 0 ->
+ encode1(Tag, negative_to_bytes(N));
+encode(Tag, N) when Tag >= 0, N < 16 ->
+ (N bsl 4) bor Tag; % not in the specification
+encode(Tag, N) when Tag >= 0, N < 16#800 ->
+ [((N bsr 3) band 2#11100000) bor Tag bor 2#00001000, N band 16#ff];
+encode(Tag, N) when Tag >= 0 ->
+ encode1(Tag, to_bytes(N)).
+
+encode1(Tag, Bytes) ->
+ case iolist_size(Bytes) of
+ Num when 2 =< Num, Num =< 8 ->
+ [((Num-2) bsl 5) bor 2#00011000 bor Tag| Bytes];
+ Num when 8 < Num ->
+ [2#11111000 bor Tag, encode(0, Num-9)| Bytes]
+ end.
+
+to_bytes(N) ->
+ Bin = binary:encode_unsigned(N),
+ case Bin of
+ <<0:1,_/bits>> -> Bin;
+ <<1:1,_/bits>> -> [0,Bin]
+ end.
+
+negative_to_bytes(N) when N >= -16#8000 ->
+ <<N:16>>;
+negative_to_bytes(N) ->
+ Bytes = byte_size(binary:encode_unsigned(-N)),
+ Bin = <<N:Bytes/unit:8>>,
+ case Bin of
+ <<0:1,_/bits>> -> [16#ff,Bin];
+ <<1:1,_/bits>> -> Bin
+ end.
diff --git a/lib/dialyzer/test/specdiffs_SUITE_data/dialyzer_options b/lib/dialyzer/test/specdiffs_SUITE_data/dialyzer_options
new file mode 100644
index 0000000000..56b36f2ed4
--- /dev/null
+++ b/lib/dialyzer/test/specdiffs_SUITE_data/dialyzer_options
@@ -0,0 +1 @@
+{dialyzer_options, [{warnings, [specdiffs]}]}.
diff --git a/lib/dialyzer/test/specdiffs_SUITE_data/results/iodata b/lib/dialyzer/test/specdiffs_SUITE_data/results/iodata
new file mode 100644
index 0000000000..3fb12fe000
--- /dev/null
+++ b/lib/dialyzer/test/specdiffs_SUITE_data/results/iodata
@@ -0,0 +1,3 @@
+
+iodata.erl:7: The specification for iodata:encode/2 states that the function might also return binary() but the inferred return is nonempty_maybe_improper_list(<<_:8,_:_*8>> | nonempty_maybe_improper_list(<<_:8,_:_*8>> | nonempty_maybe_improper_list(any(),<<_:8,_:_*8>> | []) | byte(),<<_:8,_:_*8>> | []) | integer(),<<_:8,_:_*8>> | []) | integer()
+iodata.erl:7: The success typing for iodata:encode/2 implies that the function might also return integer() but the specification return is binary() | maybe_improper_list(binary() | maybe_improper_list(any(),binary() | []) | byte(),binary() | [])
diff --git a/lib/dialyzer/test/specdiffs_SUITE_data/results/iolist b/lib/dialyzer/test/specdiffs_SUITE_data/results/iolist
new file mode 100644
index 0000000000..ca556f017c
--- /dev/null
+++ b/lib/dialyzer/test/specdiffs_SUITE_data/results/iolist
@@ -0,0 +1,2 @@
+
+iolist.erl:7: The success typing for iolist:encode/2 implies that the function might also return integer() but the specification return is maybe_improper_list(binary() | maybe_improper_list(any(),binary() | []) | byte(),binary() | [])
diff --git a/lib/dialyzer/test/specdiffs_SUITE_data/src/iodata.erl b/lib/dialyzer/test/specdiffs_SUITE_data/src/iodata.erl
new file mode 100644
index 0000000000..caa44f6c91
--- /dev/null
+++ b/lib/dialyzer/test/specdiffs_SUITE_data/src/iodata.erl
@@ -0,0 +1,41 @@
+-module(iodata).
+
+%% A small part of beam_asm.
+
+-export([encode/2]).
+
+-spec encode(non_neg_integer(), integer()) -> iodata(). % extra range binary()
+
+encode(Tag, N) when Tag >= 0, N < 0 ->
+ encode1(Tag, negative_to_bytes(N));
+encode(Tag, N) when Tag >= 0, N < 16 ->
+ (N bsl 4) bor Tag; % not in the specification
+encode(Tag, N) when Tag >= 0, N < 16#800 ->
+ [((N bsr 3) band 2#11100000) bor Tag bor 2#00001000, N band 16#ff];
+encode(Tag, N) when Tag >= 0 ->
+ encode1(Tag, to_bytes(N)).
+
+encode1(Tag, Bytes) ->
+ case iolist_size(Bytes) of
+ Num when 2 =< Num, Num =< 8 ->
+ [((Num-2) bsl 5) bor 2#00011000 bor Tag| Bytes];
+ Num when 8 < Num ->
+ [2#11111000 bor Tag, encode(0, Num-9)| Bytes]
+ end.
+
+to_bytes(N) ->
+ Bin = binary:encode_unsigned(N),
+ case Bin of
+ <<0:1,_/bits>> -> Bin;
+ <<1:1,_/bits>> -> [0,Bin]
+ end.
+
+negative_to_bytes(N) when N >= -16#8000 ->
+ <<N:16>>;
+negative_to_bytes(N) ->
+ Bytes = byte_size(binary:encode_unsigned(-N)),
+ Bin = <<N:Bytes/unit:8>>,
+ case Bin of
+ <<0:1,_/bits>> -> [16#ff,Bin];
+ <<1:1,_/bits>> -> Bin
+ end.
diff --git a/lib/dialyzer/test/specdiffs_SUITE_data/src/iolist.erl b/lib/dialyzer/test/specdiffs_SUITE_data/src/iolist.erl
new file mode 100644
index 0000000000..7cceeda24e
--- /dev/null
+++ b/lib/dialyzer/test/specdiffs_SUITE_data/src/iolist.erl
@@ -0,0 +1,41 @@
+-module(iolist).
+
+%% A small part of beam_asm.
+
+-export([encode/2]).
+
+-spec encode(non_neg_integer(), integer()) -> iolist().
+
+encode(Tag, N) when Tag >= 0, N < 0 ->
+ encode1(Tag, negative_to_bytes(N));
+encode(Tag, N) when Tag >= 0, N < 16 ->
+ (N bsl 4) bor Tag; % not in the specification
+encode(Tag, N) when Tag >= 0, N < 16#800 ->
+ [((N bsr 3) band 2#11100000) bor Tag bor 2#00001000, N band 16#ff];
+encode(Tag, N) when Tag >= 0 ->
+ encode1(Tag, to_bytes(N)).
+
+encode1(Tag, Bytes) ->
+ case iolist_size(Bytes) of
+ Num when 2 =< Num, Num =< 8 ->
+ [((Num-2) bsl 5) bor 2#00011000 bor Tag| Bytes];
+ Num when 8 < Num ->
+ [2#11111000 bor Tag, encode(0, Num-9)| Bytes]
+ end.
+
+to_bytes(N) ->
+ Bin = binary:encode_unsigned(N),
+ case Bin of
+ <<0:1,_/bits>> -> Bin;
+ <<1:1,_/bits>> -> [0,Bin]
+ end.
+
+negative_to_bytes(N) when N >= -16#8000 ->
+ <<N:16>>;
+negative_to_bytes(N) ->
+ Bytes = byte_size(binary:encode_unsigned(-N)),
+ Bin = <<N:Bytes/unit:8>>,
+ case Bin of
+ <<0:1,_/bits>> -> [16#ff,Bin];
+ <<1:1,_/bits>> -> Bin
+ end.
diff --git a/lib/kernel/src/code.erl b/lib/kernel/src/code.erl
index f143a49d2f..7faef93609 100644
--- a/lib/kernel/src/code.erl
+++ b/lib/kernel/src/code.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -630,7 +630,7 @@ do_par_recv(N, Good, Bad) ->
call(Req) ->
code_server:call(Req).
--spec start_link() -> {'ok', pid()} | {'error', 'crash'}.
+-spec start_link() -> {'ok', pid()}.
start_link() ->
do_start().
diff --git a/lib/kernel/src/file_server.erl b/lib/kernel/src/file_server.erl
index ecc1ffbdd6..29eaa23375 100644
--- a/lib/kernel/src/file_server.erl
+++ b/lib/kernel/src/file_server.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2017. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -73,7 +73,7 @@ stop() ->
%% {stop, Reason}
%%----------------------------------------------------------------------
--spec init([]) -> {'ok', state()} | {'stop', term()}.
+-spec init([]) -> {'ok', state()}.
init([]) ->
process_flag(trap_exit, true),
@@ -225,7 +225,7 @@ handle_cast(Msg, State) ->
%%----------------------------------------------------------------------
-spec handle_info(term(), state()) ->
- {'noreply', state()} | {'stop', 'normal', state()}.
+ {'noreply', state()}.
handle_info({'EXIT', Pid, _Reason}, State) when is_pid(Pid) ->
ets:delete(?FILE_IO_SERVER_TABLE, Pid),
diff --git a/lib/kernel/src/kernel_refc.erl b/lib/kernel/src/kernel_refc.erl
index 05076dc885..8e04ff99d8 100644
--- a/lib/kernel/src/kernel_refc.erl
+++ b/lib/kernel/src/kernel_refc.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2017. All Rights Reserved.
+%% Copyright Ericsson AB 2017-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -44,7 +44,7 @@ scheduler_wall_time(Bool) ->
%% Callback functions from gen_server
%%-----------------------------------------------------------------
--spec init([]) -> {'ok', map()} | {'stop', term()}.
+-spec init([]) -> {'ok', map()}.
init([]) ->
resource(scheduler_wall_time, false),
diff --git a/lib/stdlib/src/array.erl b/lib/stdlib/src/array.erl
index a237eaa489..939b1fb488 100644
--- a/lib/stdlib/src/array.erl
+++ b/lib/stdlib/src/array.erl
@@ -290,7 +290,7 @@ new(Size, Fixed, Default) ->
end,
#array{size = Size, max = M, default = Default, elements = E}.
--spec find_max(integer(), integer()) -> integer().
+-spec find_max(integer(), non_neg_integer()) -> non_neg_integer().
find_max(I, M) when I >= M ->
find_max(I, ?extend(M));