diff options
27 files changed, 704 insertions, 43 deletions
diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index d31a438ede..1f372823e8 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -31,6 +31,35 @@ </header> <p>This document describes the changes made to the ERTS application.</p> +<section><title>Erts 13.2.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + If a runtime system which was starting the distribution + already had existing pids, ports, or references referring + to a node with the same nodename/creation pair that the + runtime system was about to use, these already existing + pids, ports, or references would not work as expected in + various situations after the node had gone alive. This + could only occur if the runtime system was communicated + such pids, ports, or references prior to the distribution + was started. That is, it was extremely unlikely to happen + unless the distribution was started dynamically and was + even then very unlikely to happen. The runtime system now + checks for already existing pids, ports, and references + with the same nodename/creation pair that it is about to + use. If such are found another creation will be chosen in + order to avoid these issues.</p> + <p> + Own Id: OTP-18570 Aux Id: PR-7190 </p> + </item> + </list> + </section> + +</section> + <section><title>Erts 13.2.1</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/erts/emulator/beam/erl_node_tables.c b/erts/emulator/beam/erl_node_tables.c index c2d9b0967d..dac24d0310 100644 --- a/erts/emulator/beam/erl_node_tables.c +++ b/erts/emulator/beam/erl_node_tables.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2001-2022. All Rights Reserved. + * Copyright Ericsson AB 2001-2023. 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. diff --git a/erts/etc/common/erlc.c b/erts/etc/common/erlc.c index 3683a47064..1493c6f6ff 100644 --- a/erts/etc/common/erlc.c +++ b/erts/etc/common/erlc.c @@ -748,8 +748,10 @@ call_compile_server(char** argv) ei_x_encode_atom(&args, "command_line"); argc = 0; while (argv[argc]) { + char *arg; ei_x_encode_list_header(&args, 1); - ei_x_encode_binary(&args, possibly_unquote(argv[argc]), strlen(argv[argc])); + arg = possibly_unquote(argv[argc]); + ei_x_encode_binary(&args, arg, strlen(arg)); argc++; } ei_x_encode_empty_list(&args); /* End of command_line */ diff --git a/erts/vsn.mk b/erts/vsn.mk index 44cd2f0475..67df301d2e 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -18,7 +18,7 @@ # %CopyrightEnd% # -VSN = 13.2.1 +VSN = 13.2.2 # Port number 4365 in 4.2 # Port number 4366 in 4.3 diff --git a/lib/common_test/doc/src/ct_suite.xml b/lib/common_test/doc/src/ct_suite.xml index 8740a3ff79..cc926fbcbb 100644 --- a/lib/common_test/doc/src/ct_suite.xml +++ b/lib/common_test/doc/src/ct_suite.xml @@ -85,7 +85,9 @@ <fsummary>Returns the list of all test case groups and test cases in the module.</fsummary> <type> - <v><seetype marker="#ct_test_def">ct_test_def()</seetype> = TestCase | {group, GroupName} | {group, GroupName, Properties} | {group, GroupName, Properties, SubGroups}</v> + <v><seetype marker="#ct_test_def">ct_test_def()</seetype> = TestCase | + {group, GroupName} | {group, GroupName, Properties} | {group, GroupName, + Properties, SubGroups} | {testcase, TestCase, TestCaseRepeatType}</v> <v>TestCase = <seetype marker="#ct_testname">ct_testname()</seetype></v> <v>GroupName = <seetype marker="#ct_groupname">ct_groupname()</seetype></v> <v>Properties = [parallel | sequence | Shuffle | {RepeatType, N}] | default</v> @@ -93,6 +95,7 @@ <v>Shuffle = shuffle | {shuffle, Seed}</v> <v>Seed = {integer(), integer(), integer()}</v> <v>RepeatType = repeat | repeat_until_all_ok | repeat_until_all_fail | repeat_until_any_ok | repeat_until_any_fail</v> + <v>TestCaseRepeatType = [{repeat, N} | {repeat_until_ok, N} | {repeat_until_fail, N}]</v> <v>N = integer() | forever</v> <v>Reason = term()</v> </type> @@ -135,11 +138,12 @@ <v><seetype marker="#ct_group_def">ct_group_def()</seetype> = {GroupName, Properties, GroupsAndTestCases}</v> <v>GroupName = <seetype marker="#ct_groupname">ct_groupname()</seetype></v> <v>Properties = [parallel | sequence | Shuffle | {RepeatType, N}]</v> - <v>GroupsAndTestCases = [Group | {group, GroupName} | TestCase]</v> + <v>GroupsAndTestCases = [Group | {group, GroupName} | TestCase | {testcase, TestCase, TestCaseRepeatType}]</v> <v>TestCase = <seetype marker="#ct_testname">ct_testname()</seetype></v> <v>Shuffle = shuffle | {shuffle, Seed}</v> <v>Seed = {integer(), integer(), integer()}</v> <v>RepeatType = repeat | repeat_until_all_ok | repeat_until_all_fail | repeat_until_any_ok | repeat_until_any_fail</v> + <v>TestCaseRepeatType = [{repeat, N} | {repeat_until_ok, N} | {repeat_until_fail, N}]</v> <v>N = integer() | forever</v> </type> diff --git a/lib/common_test/doc/src/run_test_chapter.xml b/lib/common_test/doc/src/run_test_chapter.xml index f9416e1bee..ea33b9d8f9 100644 --- a/lib/common_test/doc/src/run_test_chapter.xml +++ b/lib/common_test/doc/src/run_test_chapter.xml @@ -763,7 +763,7 @@ finally function <c>end_per_group</c>. Also, if particular test cases in a group are specified, <c>init_per_group</c> and <c>end_per_group</c>, for the group in question, are - called. If a group defined (in <c>Suite:group/0</c>) as + called. If a group defined (in <c>Suite:groups/0</c>) as a subgroup of another group, is specified (or if particular test cases of a subgroup are), <c>Common Test</c> calls the configuration functions for the top-level groups and for the subgroup diff --git a/lib/common_test/doc/src/write_test_chapter.xml b/lib/common_test/doc/src/write_test_chapter.xml index 8f7c7e8177..99571bbdae 100644 --- a/lib/common_test/doc/src/write_test_chapter.xml +++ b/lib/common_test/doc/src/write_test_chapter.xml @@ -515,14 +515,14 @@ <c>{group,GroupName,Properties,SubGroups}</c> Where, <c>SubGroups</c> is a list of tuples, <c>{GroupName,Properties}</c> or <c>{GroupName,Properties,SubGroups}</c> representing the subgroups. - Any subgroups defined in <c>group/0</c> for a group, that are not specified + Any subgroups defined in <c>groups/0</c> for a group, that are not specified in the <c>SubGroups</c> list, executes with their predefined properties.</p> <p><em>Example:</em></p> <pre> - groups() -> {tests1, [], [{tests2, [], [t2a,t2b]}, - {tests3, [], [t31,t3b]}]}.</pre> + groups() -> [{tests1, [], [{tests2, [], [t2a,t2b]}, + {tests3, [], [t31,t3b]}]}].</pre> <p>To execute group <c>tests1</c> twice with different properties for <c>tests2</c> each time:</p> <pre> diff --git a/lib/common_test/src/ct_suite.erl b/lib/common_test/src/ct_suite.erl index a2d23e15ef..860efe3ae2 100644 --- a/lib/common_test/src/ct_suite.erl +++ b/lib/common_test/src/ct_suite.erl @@ -47,9 +47,9 @@ {group, ct_groupname(), ct_group_props_ref()} | {group, ct_groupname(), ct_group_props_ref(), ct_subgroups_def()}. -type ct_testcase_ref() :: {testcase, ct_testname(), ct_testcase_repeat_prop()}. --type ct_testcase_repeat_prop() :: {repeat, ct_test_repeat()} | +-type ct_testcase_repeat_prop() :: [{repeat, ct_test_repeat()} | {repeat_until_ok, ct_test_repeat()} | - {repeat_until_fail, ct_test_repeat()}. + {repeat_until_fail, ct_test_repeat()}]. -type ct_info() :: {timetrap, ct_info_timetrap()} | {require, ct_info_required()} | {require, Name :: atom(), ct_info_required()} | diff --git a/lib/compiler/doc/src/notes.xml b/lib/compiler/doc/src/notes.xml index 75396f4c0f..f92505409c 100644 --- a/lib/compiler/doc/src/notes.xml +++ b/lib/compiler/doc/src/notes.xml @@ -32,6 +32,21 @@ <p>This document describes the changes made to the Compiler application.</p> +<section><title>Compiler 8.2.6</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p>Fixed type handling bugs that could cause an internal + error in the compiler for correct code.</p> + <p> + Own Id: OTP-18565 Aux Id: GH-7147 </p> + </item> + </list> + </section> + +</section> + <section><title>Compiler 8.2.5</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/compiler/src/beam_ssa_check.erl b/lib/compiler/src/beam_ssa_check.erl index 0d3faa0ee2..f344286943 100644 --- a/lib/compiler/src/beam_ssa_check.erl +++ b/lib/compiler/src/beam_ssa_check.erl @@ -285,8 +285,9 @@ env_post1(_Pattern, _Actual, _Env) -> ?DP("Failed to match ~p <-> ~p~n", [_Pattern, _Actual]), error({internal_pattern_match_error,env_post1}). -post_bitstring(Bytes, Actual, _Env) -> - Actual = build_bitstring(Bytes, <<>>). +post_bitstring(Bytes, Actual, Env) -> + Actual = build_bitstring(Bytes, <<>>), + Env. %% Convert the parsed literal binary to an actual bitstring. build_bitstring([{integer,_,V}|Bytes], Acc) -> diff --git a/lib/compiler/src/beam_ssa_private_append.erl b/lib/compiler/src/beam_ssa_private_append.erl index bf51e8b81a..c295492762 100644 --- a/lib/compiler/src/beam_ssa_private_append.erl +++ b/lib/compiler/src/beam_ssa_private_append.erl @@ -86,6 +86,17 @@ find_appends_blk([], _, Found) -> Found. find_appends_is([#b_set{dst=Dst, op=bs_create_bin, + args=[#b_literal{val=append}, + _, + Lit=#b_literal{val= <<>>}|_]}|Is], + Fun, Found0) -> + %% Special case for when the first fragment is a literal <<>> as + %% it won't be annotated as unique nor will it die with the + %% instruction. + AlreadyFound = maps:get(Fun, Found0, []), + Found = Found0#{Fun => [{append,Dst,Lit}|AlreadyFound]}, + find_appends_is(Is, Fun, Found); +find_appends_is([#b_set{dst=Dst, op=bs_create_bin, args=[#b_literal{val=append},SegmentInfo,Var|_], anno=#{first_fragment_dies:=Dies}=Anno}|Is], Fun, Found0) -> @@ -468,6 +479,16 @@ patch_appends_is([I0=#b_set{dst=Dst}|Rest], PD0, Cnt0, Acc, BlockAdditions0) Ps = keysort(1, map(ExtractOpargs, Patches)), {Is, Cnt} = patch_opargs(I0, Ps, Cnt0), patch_appends_is(Rest, PD, Cnt, Is++Acc, BlockAdditions0); + [{append,Dst,#b_literal{val= <<>>}=Lit}] -> + %% Special case for when the first fragment is a literal + %% <<>> and it has to be replaced with a bs_init_writable. + #b_set{op=bs_create_bin,dst=Dst,args=Args0}=I0, + [#b_literal{val=append},SegInfo,Lit|OtherArgs] = Args0, + {V,Cnt} = new_var(Cnt0), + Init = #b_set{op=bs_init_writable,dst=V,args=[#b_literal{val=256}]}, + I = I0#b_set{args=[#b_literal{val=private_append}, + SegInfo,V|OtherArgs]}, + patch_appends_is(Rest, PD, Cnt, [I,Init|Acc], BlockAdditions0); [{append,Dst,_}] -> #b_set{op=bs_create_bin,dst=Dst,args=Args0}=I0, [#b_literal{val=append}|OtherArgs] = Args0, diff --git a/lib/compiler/test/beam_ssa_check_SUITE_data/private_append.erl b/lib/compiler/test/beam_ssa_check_SUITE_data/private_append.erl index 4d4b0f1bbd..c1edc54460 100644 --- a/lib/compiler/test/beam_ssa_check_SUITE_data/private_append.erl +++ b/lib/compiler/test/beam_ssa_check_SUITE_data/private_append.erl @@ -22,6 +22,8 @@ %% -module(private_append). +-feature(maybe_expr, enable). + -export([transformable0/1, transformable1/1, transformable1b/1, @@ -76,7 +78,9 @@ not_transformable14/0, not_transformable15/2, - id/1]). + id/1, + + bs_create_bin_on_literal/0]). %% Trivial smoke test transformable0(L) -> @@ -977,3 +981,24 @@ not_transformable15(_, V) -> id(I) -> I. + +%% Check that we don't try to private_append to something created by +%% bs_create_bin `append`, _, `<<>>`, ... +bs_create_bin_on_literal() -> +%ssa% () when post_ssa_opt -> +%ssa% X = bs_init_writable(_), +%ssa% Y = bs_create_bin(private_append, _, X, ...), +%ssa% Z = bs_create_bin(private_append, _, Y, ...), +%ssa% ret(Z). + << + << + (maybe + 2147483647 ?= ok + else + <<_>> -> + ok; + _ -> + <<>> + end)/bytes + >>/binary + >>. diff --git a/lib/compiler/test/beam_ssa_check_SUITE_data/sanity_checks.erl b/lib/compiler/test/beam_ssa_check_SUITE_data/sanity_checks.erl index ae4bb28eea..47c60fd8d6 100644 --- a/lib/compiler/test/beam_ssa_check_SUITE_data/sanity_checks.erl +++ b/lib/compiler/test/beam_ssa_check_SUITE_data/sanity_checks.erl @@ -18,6 +18,8 @@ -module(sanity_checks). +-compile(no_ssa_opt_private_append). + -export([check_fail/0, check_wrong_pass/0, check_xfail/0, @@ -33,7 +35,9 @@ t25/0, t26/0, t27/0, t28/0, t29/0, t30/0, t31/0, t32/1, t33/1, t34/1, t35/1, t36/0, t37/0, t38/0, t39/1, - t40/0, t41/0, t42/0, t43/0, t44/0]). + t40/0, t41/0, t42/0, t43/0, t44/0, + + check_env/0]). %% Check that we do not trigger on the wrong pass check_wrong_pass() -> @@ -325,3 +329,13 @@ t44() -> %ssa% () when post_ssa_opt -> %ssa% _ = call(fun e:f0/1, {...}). e:f0({}). + +%% Ensure bug which trashed the environment after matching a literal +%% bitstring stays fixed. +check_env() -> +%ssa% () when post_ssa_opt -> +%ssa% X = bs_create_bin(append, _, <<>>, ...), +%ssa% ret(X). + A = <<>>, + B = ex:f(), + <<A/binary, B/binary>>. diff --git a/lib/compiler/vsn.mk b/lib/compiler/vsn.mk index 0c4eae4d45..fa46ea4097 100644 --- a/lib/compiler/vsn.mk +++ b/lib/compiler/vsn.mk @@ -1 +1 @@ -COMPILER_VSN = 8.2.5 +COMPILER_VSN = 8.2.6 diff --git a/lib/os_mon/doc/src/notes.xml b/lib/os_mon/doc/src/notes.xml index 1fac53d330..439c1e839a 100644 --- a/lib/os_mon/doc/src/notes.xml +++ b/lib/os_mon/doc/src/notes.xml @@ -31,6 +31,23 @@ </header> <p>This document describes the changes made to the OS_Mon application.</p> +<section><title>Os_Mon 2.8.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Avoid error report from failing <c>erlang:port_close</c> + at shutdown of <c>cpu_sup</c> and <c>memsup</c>. Bug + exists since OTP 25.3 (os_mon-2.8.1).</p> + <p> + Own Id: OTP-18559 Aux Id: ERIERL-942 </p> + </item> + </list> + </section> + +</section> + <section><title>Os_Mon 2.8.1</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/os_mon/vsn.mk b/lib/os_mon/vsn.mk index 9e64a83612..d6878e6773 100644 --- a/lib/os_mon/vsn.mk +++ b/lib/os_mon/vsn.mk @@ -1 +1 @@ -OS_MON_VSN = 2.8.1 +OS_MON_VSN = 2.8.2 diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml index 700709ed7a..b4a30a68c8 100644 --- a/lib/ssl/doc/src/ssl.xml +++ b/lib/ssl/doc/src/ssl.xml @@ -477,12 +477,10 @@ {sha384, ecdsa}, {sha384, rsa}, {sha256, ecdsa}, -{sha256, rsa}, -{sha224, ecdsa}, -{sha224, rsa} +{sha256, rsa} ]</code> -<p>Support for {md5, rsa} was removed from the the TLS-1.2 default in ssl-8.0 (OTP-22) and support for SHA1 {sha, _} was removed in ssl-11.0 (OTP-26) </p> +<p>Support for {md5, rsa} was removed from the the TLS-1.2 default in ssl-8.0 (OTP-22) and support for SHA1 {sha, _} and SHA224 {sha224, _} was removed in ssl-11.0 (OTP-26) </p> <p><c> rsa_pss_schemes =</c></p> <code> @@ -521,10 +519,10 @@ rsa_pss_schemes() <p>EDDSA was made highest priority in ssl-10.8 (OTP-25) </p> <p>TLS-1.3 default is</p> -<code>Default_TLS_13_Schemes ++ Legacy_TLS_13_Schemes </code> +<code>Default_TLS_13_Schemes</code> <p>If both TLS-1.3 and TLS-1.2 are supported the default will be</p> -<code>Default_TLS_13_Schemes ++ Default_TLS_12_Alg_Pairs </code> +<code>Default_TLS_13_Schemes ++ TLS_13_Legacy_Schemes ++ Default_TLS_12_Alg_Pairs (not represented in TLS_13_Legacy_Schemes) </code> <p>so appropriate algorithms can be chosen for the negotiated version. @@ -2175,6 +2173,60 @@ fun(srp, Username :: binary(), UserState :: term()) -> is useful.</p> </desc> </func> + + <func> + <name since="OTP @OTP-18572@" name="signature_algs" arity="2" /> + <fsummary>Returns a list of signature algorithms/schemes </fsummary> + <desc> + <p>Lists all possible signature algorithms corresponding to + <c>Description</c> that are available. The + <c>exclusive</c> option will exclusively list + algorithms/schemes for that protocol version, whereas the + <c>default</c> and <c>all</c> options lists the combined list to support the + range of protocols from (D)TLS-1.2, the first version to support + configuration of the signature algorithms, to <c>Version</c>.</p> + + <p> Example: <c> + + 1> ssl:signature_algs(default, 'tlsv1.3'). + [eddsa_ed25519,eddsa_ed448,ecdsa_secp521r1_sha512, + ecdsa_secp384r1_sha384,ecdsa_secp256r1_sha256, + rsa_pss_pss_sha512,rsa_pss_pss_sha384,rsa_pss_pss_sha256, + rsa_pss_rsae_sha512,rsa_pss_rsae_sha384,rsa_pss_rsae_sha256, + rsa_pkcs1_sha512,rsa_pkcs1_sha384,rsa_pkcs1_sha256, + {sha512,ecdsa}, + {sha384,ecdsa}, + {sha256,ecdsa}] + + 2>ssl:signature_algs(all, 'tlsv1.3'). + [eddsa_ed25519,eddsa_ed448,ecdsa_secp521r1_sha512, + ecdsa_secp384r1_sha384,ecdsa_secp256r1_sha256, + rsa_pss_pss_sha512,rsa_pss_pss_sha384,rsa_pss_pss_sha256, + rsa_pss_rsae_sha512,rsa_pss_rsae_sha384,rsa_pss_rsae_sha256, + rsa_pkcs1_sha512,rsa_pkcs1_sha384,rsa_pkcs1_sha256, + {sha512,ecdsa}, + {sha384,ecdsa}, + {sha256,ecdsa}, + {sha224,ecdsa}, + {sha224,rsa}, + {sha,rsa}, + {sha,dsa}] + + 3> ssl:signature_algs(exclusive, 'tlsv1.3'). + [eddsa_ed25519,eddsa_ed448,ecdsa_secp521r1_sha512, + ecdsa_secp384r1_sha384,ecdsa_secp256r1_sha256, + rsa_pss_pss_sha512,rsa_pss_pss_sha384,rsa_pss_pss_sha256, + rsa_pss_rsae_sha512,rsa_pss_rsae_sha384,rsa_pss_rsae_sha256] + </c></p> + + <note><p>Some TLS-1-3 scheme names overlap with TLS-1.2 + algorithm-tuple-pair-names and then TLS-1.3 names will be + used, for example <c>rsa_pkcs1_sha256</c> instead of + <c>{sha256, rsa}</c> these are legacy algorithms in TLS-1.3 + that apply only to certificate signatures in this version of + the protocol.</p></note> + </desc> + </func> <func> <name since="" name="sockname" arity="1" /> diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index c96173e98b..0a1db06804 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -81,6 +81,7 @@ filter_cipher_suites/2, prepend_cipher_suites/2, append_cipher_suites/2, + signature_algs/2, eccs/0, eccs/1, versions/0, @@ -183,12 +184,11 @@ -type hash() :: sha2() | legacy_hash(). % exported --type sha2() :: sha224 | - sha256 | - sha384 | - sha512. +-type sha2() :: sha256 | + sha384 | + sha512. --type legacy_hash() :: sha | md5. +-type legacy_hash() :: sha224 | sha | md5. -type sign_algo() :: rsa | dsa | ecdsa | eddsa. % exported @@ -1106,6 +1106,41 @@ append_cipher_suites(Filters, Suites) -> (Suites -- Deferred) ++ Deferred. %%-------------------------------------------------------------------- +-spec signature_algs(Description, Version) -> [signature_algs()] when + Description :: default | all | exclusive, + Version :: protocol_version(). + +%% Description: Returns possible signature algorithms/schemes +%% for TLS/DTLS version +%%-------------------------------------------------------------------- + +signature_algs(default, 'tlsv1.3') -> + tls_v1:default_signature_algs([tls_record:protocol_version_name('tlsv1.3'), + tls_record:protocol_version_name('tlsv1.2')]); +signature_algs(default, 'tlsv1.2') -> + tls_v1:default_signature_algs([tls_record:protocol_version_name('tlsv1.2')]); +signature_algs(all, 'tlsv1.3') -> + tls_v1:default_signature_algs([tls_record:protocol_version_name('tlsv1.3'), + tls_record:protocol_version_name('tlsv1.2')]) ++ + tls_v1:legacy_signature_algs_pre_13(); +signature_algs(all, 'tlsv1.2') -> + tls_v1:default_signature_algs([tls_record:protocol_version_name('tlsv1.2')]) ++ + tls_v1:legacy_signature_algs_pre_13(); +signature_algs(exclusive, 'tlsv1.3') -> + tls_v1:default_signature_algs([tls_record:protocol_version_name('tlsv1.3')]); +signature_algs(exclusive, 'tlsv1.2') -> + Algs = tls_v1:default_signature_algs([tls_record:protocol_version_name('tlsv1.2')]), + Algs ++ tls_v1:legacy_signature_algs_pre_13(); +signature_algs(Description, 'dtlsv1.2') -> + signature_algs(Description, 'tlsv1.2'); +signature_algs(Description, Version) when Description == default; + Description == all; + Description == exclusive-> + {error, {signature_algs_not_supported_in_protocol_version, Version}}; +signature_algs(Description,_) -> + {error, {badarg, Description}}. + +%%-------------------------------------------------------------------- -spec eccs() -> NamedCurves when NamedCurves :: [named_curve()]. diff --git a/lib/ssl/src/tls_client_connection_1_3.erl b/lib/ssl/src/tls_client_connection_1_3.erl index d5742ea390..8f7486d419 100644 --- a/lib/ssl/src/tls_client_connection_1_3.erl +++ b/lib/ssl/src/tls_client_connection_1_3.erl @@ -316,6 +316,10 @@ hello_middlebox_assert(enter, _, State) -> {keep_state, State}; hello_middlebox_assert(internal, #change_cipher_spec{}, State) -> tls_gen_connection:next_event(wait_ee, no_record, State); +hello_middlebox_assert(internal = Type, #encrypted_extensions{} = Msg, #state{ssl_options = #{log_level := Level}} = State) -> + ssl_logger:log(warning, Level, #{description => "Failed to assert middlebox server message", + reason => [{missing, #change_cipher_spec{}}]}, ?LOCATION), + ssl_gen_statem:handle_common_event(Type, Msg, ?FUNCTION_NAME, State); hello_middlebox_assert(info, Msg, State) -> tls_gen_connection:handle_info(Msg, ?FUNCTION_NAME, State); hello_middlebox_assert(Type, Msg, State) -> @@ -331,8 +335,10 @@ hello_retry_middlebox_assert(enter, _, State) -> {keep_state, State}; hello_retry_middlebox_assert(internal, #change_cipher_spec{}, State) -> tls_gen_connection:next_event(wait_sh, no_record, State); -hello_retry_middlebox_assert(internal, #server_hello{}, State) -> - tls_gen_connection:next_event(?FUNCTION_NAME, no_record, State, [postpone]); +hello_retry_middlebox_assert(internal = Type, #server_hello{} = Msg, #state{ssl_options = #{log_level := Level}} = State) -> + ssl_logger:log(warning, Level, #{description => "Failed to assert middlebox server message", + reason => [{missing, #change_cipher_spec{}}]}, ?LOCATION), + ssl_gen_statem:handle_common_event(Type, Msg, ?FUNCTION_NAME, State); hello_retry_middlebox_assert(info, Msg, State) -> tls_gen_connection:handle_info(Msg, ?FUNCTION_NAME, State); hello_retry_middlebox_assert(Type, Msg, State) -> diff --git a/lib/ssl/src/tls_v1.erl b/lib/ssl/src/tls_v1.erl index 7404365520..8eda30506a 100644 --- a/lib/ssl/src/tls_v1.erl +++ b/lib/ssl/src/tls_v1.erl @@ -55,6 +55,7 @@ oid_to_enum/1, enum_to_oid/1, default_signature_algs/1, + legacy_signature_algs_pre_13/0, signature_algs/2, signature_schemes/2, rsa_schemes/0, @@ -885,7 +886,8 @@ signature_algs(?TLS_1_2, HashSigns) -> default_signature_algs([?TLS_1_3]) -> default_signature_schemes(?TLS_1_3) ++ legacy_signature_schemes(?TLS_1_3); default_signature_algs([?TLS_1_3, ?TLS_1_2 | _]) -> - default_signature_schemes(?TLS_1_3) ++ default_pre_1_3_signature_algs_only(); + default_signature_schemes(?TLS_1_3) ++ legacy_signature_schemes(?TLS_1_3) + ++ default_pre_1_3_signature_algs_only(); default_signature_algs([?TLS_1_2 = Version |_]) -> Default = [%% SHA2 ++ PSS {sha512, ecdsa}, @@ -899,9 +901,7 @@ default_signature_algs([?TLS_1_2 = Version |_]) -> {sha256, ecdsa}, rsa_pss_pss_sha256, rsa_pss_rsae_sha256, - {sha256, rsa}, - {sha224, ecdsa}, - {sha224, rsa} + {sha256, rsa} ], signature_algs(Version, Default); default_signature_algs(_) -> @@ -910,16 +910,14 @@ default_signature_algs(_) -> default_pre_1_3_signature_algs_only() -> Default = [%% SHA2 {sha512, ecdsa}, - {sha512, rsa}, {sha384, ecdsa}, - {sha384, rsa}, - {sha256, ecdsa}, - {sha256, rsa}, - {sha224, ecdsa}, - {sha224, rsa} + {sha256, ecdsa} ], signature_algs(?TLS_1_2, Default). +legacy_signature_algs_pre_13() -> + [{sha224, ecdsa}, {sha224, rsa}, {sha, rsa}, {sha, dsa}]. + signature_schemes(Version, [_|_] =SignatureSchemes) when is_tuple(Version) andalso ?TLS_GTE(Version, ?TLS_1_2) -> CryptoSupports = crypto:supports(), diff --git a/lib/ssl/test/ssl_reject_SUITE.erl b/lib/ssl/test/ssl_reject_SUITE.erl index cc24ffbcfe..be79e0543b 100644 --- a/lib/ssl/test/ssl_reject_SUITE.erl +++ b/lib/ssl/test/ssl_reject_SUITE.erl @@ -184,9 +184,12 @@ accept_sslv3_record_hello(Config) when is_list(Config) -> Allversions = all_versions(), + AllSigAlgs = ssl:signature_algs(all, 'tlsv1.3'), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, - {options, [{versions, Allversions} | ServerOpts]}]), + {options, [{versions, Allversions}, + {signature_algs, AllSigAlgs} | ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), %% TLS-1.X Hello with SSL-3.0 record version diff --git a/lib/ssl/test/tls_api_SUITE.erl b/lib/ssl/test/tls_api_SUITE.erl index 405bb74b2d..74b4e76566 100644 --- a/lib/ssl/test/tls_api_SUITE.erl +++ b/lib/ssl/test/tls_api_SUITE.erl @@ -109,7 +109,9 @@ accept_pool/0, accept_pool/1, reuseaddr/0, - reuseaddr/1 + reuseaddr/1, + signature_algs/0, + signature_algs/1 ]). %% Apply export @@ -1307,6 +1309,24 @@ tls_password_badarg(Config) when is_list(Config) -> {error, {keyfile,badarg}}). %%-------------------------------------------------------------------- +signature_algs() -> + [{doc, "Check that listing of signature algorithms for different version and configure combinations"}]. +signature_algs(Config) when is_list(Config) -> + true = [] =/= [Alg || Alg <- ssl:signature_algs(default, 'tlsv1.3'), is_tuple(Alg)], + true = [] == [Alg || Alg <- ssl:signature_algs(exclusive, 'tlsv1.3'), is_tuple(Alg)], + true = ssl:signature_algs(exclusive, 'tlsv1.3') =/= ssl:signature_algs(exclusive, 'tlsv1.2'), + true = length(ssl:signature_algs(defalt, 'tlsv1.2')) < + length(ssl:signature_algs(all, 'tlsv1.2')), + TLS_1_3_All = ssl:signature_algs(all, 'tlsv1.3'), + true = lists:member(rsa_pkcs1_sha512, TLS_1_3_All) andalso (not lists:member({sha512, rsa}, TLS_1_3_All)), + true = lists:member(rsa_pkcs1_sha384, TLS_1_3_All) andalso (not lists:member({sha384, rsa}, TLS_1_3_All)), + true = lists:member(rsa_pkcs1_sha256, TLS_1_3_All) andalso (not lists:member({sha256, rsa}, TLS_1_3_All)), + TLS_1_2_All = ssl:signature_algs(all, 'tlsv1.2'), + true = (not lists:member(rsa_pkcs1_sha512, TLS_1_2_All)) andalso lists:member({sha512, rsa}, TLS_1_2_All), + true = (not lists:member(rsa_pkcs1_sha384, TLS_1_2_All)) andalso lists:member({sha384, rsa}, TLS_1_2_All), + true = (not lists:member(rsa_pkcs1_sha256, TLS_1_2_All)) andalso lists:member({sha256, rsa}, TLS_1_2_All). + +%%-------------------------------------------------------------------- %% Internal functions ------------------------------------------------ %%-------------------------------------------------------------------- diff --git a/lib/wx/c_src/Makefile.in b/lib/wx/c_src/Makefile.in index 87fc0354b6..6371f401c0 100644 --- a/lib/wx/c_src/Makefile.in +++ b/lib/wx/c_src/Makefile.in @@ -199,7 +199,11 @@ release_spec: opt $(INSTALL_PROGRAM) $(TARGET_DIR)/wxe_driver$(SO_EXT) "$(RELSYSDIR)/priv/" $(INSTALL_PROGRAM) $(TARGET_DIR)/erl_gl$(SO_EXT) "$(RELSYSDIR)/priv/" ifneq ($(WEBVIEW_LOADER_DLL_ORIG),) - $(INSTALL_PROGRAM) $(WEBVIEW_LOADER_DLL_DEST) "$(RELSYSDIR)/priv/" + $(INSTALL_PROGRAM) $(WEBVIEW_LOADER_DLL_DEST) "$(RELSYSDIR)/priv/" +endif +ifeq ($(SYS_TYPE),win32) + $(INSTALL_PROGRAM) $(TARGET_DIR)/wxe_driver.pdb "$(RELSYSDIR)/priv/" + $(INSTALL_PROGRAM) $(TARGET_DIR)/erl_gl.pdb "$(RELSYSDIR)/priv/" endif release_docs_spec: @@ -56,6 +56,8 @@ usage () echo " release <target_dir> - creates a small release to <target_dir>" echo " release [-a|-s|-t] <target_dir> - creates full release to <target_dir>" echo " tests <dir> - Build testsuites to <dir>" + echo " check [--help|...] - Perform various build checks. See --help for more info" + echo " and options." echo "" echo "-a builds all applications" echo "-s builds a small system (default)" @@ -998,6 +1000,11 @@ do_tests () fi } +do_check () +{ + exec $ERL_TOP/scripts/otp_build_check "$@" +} + do_debuginfo_win32 () { setup_make @@ -1294,6 +1301,9 @@ case "$1" in echo_env_cross "$2";; env_bootstrap) echo_env_bootstrap;; + check) + shift; + do_check "$@";; *) usage;; esac diff --git a/otp_versions.table b/otp_versions.table index 0032616639..9da37263b9 100644 --- a/otp_versions.table +++ b/otp_versions.table @@ -1,3 +1,4 @@ +OTP-25.3.2 : compiler-8.2.6 erts-13.2.2 os_mon-2.8.2 # asn1-5.0.21 common_test-1.24 crypto-5.1.4 debugger-5.3.1 dialyzer-5.0.5 diameter-2.2.7 edoc-1.2 eldap-1.2.11 erl_docgen-1.4 erl_interface-5.3.2 et-1.6.5 eunit-2.8.2 ftp-1.1.4 inets-8.3.1 jinterface-1.13.2 kernel-8.5.4 megaco-4.4.3 mnesia-4.21.4 observer-2.14 odbc-2.14 parsetools-2.4.1 public_key-1.13.3 reltool-0.9.1 runtime_tools-1.19 sasl-4.2 snmp-5.13.5 ssh-4.15.3 ssl-10.9.1 stdlib-4.3.1 syntax_tools-3.0.1 tftp-1.0.4 tools-3.5.3 wx-2.2.2 xmerl-1.3.31 : OTP-25.3.1 : compiler-8.2.5 crypto-5.1.4 eldap-1.2.11 erl_interface-5.3.2 erts-13.2.1 inets-8.3.1 snmp-5.13.5 ssl-10.9.1 stdlib-4.3.1 wx-2.2.2 # asn1-5.0.21 common_test-1.24 debugger-5.3.1 dialyzer-5.0.5 diameter-2.2.7 edoc-1.2 erl_docgen-1.4 et-1.6.5 eunit-2.8.2 ftp-1.1.4 jinterface-1.13.2 kernel-8.5.4 megaco-4.4.3 mnesia-4.21.4 observer-2.14 odbc-2.14 os_mon-2.8.1 parsetools-2.4.1 public_key-1.13.3 reltool-0.9.1 runtime_tools-1.19 sasl-4.2 ssh-4.15.3 syntax_tools-3.0.1 tftp-1.0.4 tools-3.5.3 xmerl-1.3.31 : OTP-25.3 : common_test-1.24 compiler-8.2.4 crypto-5.1.3 debugger-5.3.1 dialyzer-5.0.5 erl_interface-5.3.1 erts-13.2 eunit-2.8.2 ftp-1.1.4 inets-8.3 jinterface-1.13.2 kernel-8.5.4 megaco-4.4.3 mnesia-4.21.4 os_mon-2.8.1 public_key-1.13.3 reltool-0.9.1 snmp-5.13.4 ssh-4.15.3 ssl-10.9 stdlib-4.3 syntax_tools-3.0.1 tftp-1.0.4 xmerl-1.3.31 # asn1-5.0.21 diameter-2.2.7 edoc-1.2 eldap-1.2.10 erl_docgen-1.4 et-1.6.5 observer-2.14 odbc-2.14 parsetools-2.4.1 runtime_tools-1.19 sasl-4.2 tools-3.5.3 wx-2.2.1 : OTP-25.2.3 : erts-13.1.5 inets-8.2.2 ssh-4.15.2 ssl-10.8.7 # asn1-5.0.21 common_test-1.23.3 compiler-8.2.3 crypto-5.1.2 debugger-5.3 dialyzer-5.0.4 diameter-2.2.7 edoc-1.2 eldap-1.2.10 erl_docgen-1.4 erl_interface-5.3 et-1.6.5 eunit-2.8.1 ftp-1.1.3 jinterface-1.13.1 kernel-8.5.3 megaco-4.4.2 mnesia-4.21.3 observer-2.14 odbc-2.14 os_mon-2.8 parsetools-2.4.1 public_key-1.13.2 reltool-0.9 runtime_tools-1.19 sasl-4.2 snmp-5.13.3 stdlib-4.2 syntax_tools-3.0 tftp-1.0.3 tools-3.5.3 wx-2.2.1 xmerl-1.3.30 : diff --git a/scripts/otp_build_check b/scripts/otp_build_check new file mode 100755 index 0000000000..ee6e656cdf --- /dev/null +++ b/scripts/otp_build_check @@ -0,0 +1,344 @@ +#!/bin/sh +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2023. 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% +# + +# +# Performs a lot of (but not always all) build checking that you want to do... +# + +docs=yes +dialyzer=yes +tests=all +opt_only=no +format_check=yes + +have_printf=no + +print_error() { + if [ $have_printf = yes ]; then + printf "\033[31mERROR: %s\033[0m\n" "$1" 1>&2 + else + echo "ERROR: $1" 1>&2 + fi +} + +progress() { + if [ $have_printf = yes ]; then + printf "\n\033[33m*** %s ***\033[0m\n\n" "$1" 1>&2 + else + echo "\n*** $1 ***\n" 1>&2 + fi +} + +print_usage() { + cat <<EOF +Usage: + otp_build check [--help|-h] [--only-opt|-o] [--no-docs|-d] \\ + [--no-dialyzer|-y] [--no-tests|-n] [--no-format-check|-f] \\ + [--tests|-t <App0> ... <AppN>] + + +By default all currently implemented checks will be performed. If any of the +currently used tools are missing, checking will fail. If libraries or tools +needed to support certain conditional features are missing, those features +will not be checked. + +If any of these checks do not pass, the code is *not* ready for testing in OTP +daily builds. Note that this script does not check all requirements for testing +in OTP daily builds. These checks are the bare minimum for even considering +testing in OTP daily builds. Currently the following will be performed by +default: + + * Build all applications in optimized mode. If configure already has been run, + it wont be run again. + * Debug compile C-code in all applications. + * Format checking of JIT code. + * Run dialyzer on all applications. + * Build all documentation. + * Run xmllint on all documentation. + * Run html link check on all documentation. + * Build all test suites. + +Certain build checking can be disabled using the following options: +* [--only-opt|-o] - Only build optimized system. No debug, etc. +* [--no-format-check|-f] - No JIT format checking. +* [--no-docs|-d] - No documentation checking. +* [--no-dialyzer|-y] - No dialyzer checking. +* [--no-tests|-n] - No build checking of test suites. +* [--tests|-t <App0> ... <AppN>] - Only build checking of test suites for + listed applications. + +Only disable build checking for parts of the system that you are certain your +changes wont effect. Note that even though you've made no changes in +documentation source files, documentation build is effected by type changes +in code. + +Environment variables used: +* CONFIG_FLAGS - Arguments to pass to configure if it is executed. + +Build results will be placed under the \$ERL_TOP/release/<TARGET> directory + +EOF +} + +usage () { + print_error "$1" + print_usage >&2 + exit 1 +} + +fail () { + print_error "$1" + exit 1 +} + +type printf >/dev/null 2>&1 +if [ $? -eq 0 ]; then + have_printf=yes +fi + +[ "$ERL_TOP" != "" ] || fail "ERL_TOP needs to be set" + +cd "$ERL_TOP" 2>&1 >/dev/null || { + fail "Failed to change directory into $ERL_TOP" +} + +[ -f "$ERL_TOP/OTP_VERSION" ] || fail "ERL_TOP not valid" + +while [ $# -gt 0 ]; do + case $1 in + --help|-h) + print_usage + exit 0;; + --only-opt|-o) + opt_only=yes;; + --no-docs|-d) + docs=no;; + --no-dialyzer|-y) + dialyzer=no;; + --no-format-check|-f) + format_check=no;; + --no-tests|-n) + tests=none;; + --tests|-t) + shift + while [ $# -gt 0 ]; do + case $1 in + -*) + break;; + test_server|emulator|system|epmd) + ;; + *) + [ -d "$ERL_TOP/lib/$1/test" ] || { + fail "Invalid application: $1" + } + ;; + esac + tapps="$1 $tapps" + shift + done + [ "$tapps" != "" ] || usage "No test apps given" + tests="$tapps" + continue;; + *) + usage "Invalid option: $1" + esac + shift +done + +[ -f "$ERL_TOP/make/config.status" ] || { + ./configure $CONFIG_FLAGS || fail "configure failed" +} + +type gmake >/dev/null 2>&1 +if [ $? -eq 0 ]; then + export MAKE="gmake" +else + type make >/dev/null 2>&1 + if [ $? -eq 0 ]; then + export MAKE="make" + fi +fi + +target=`$MAKE target_configured` || fail "Failed to determine target directory" + +unset TESTROOT +unset RELEASE_ROOT +unset ERL_LIBS +unset OTP_SMALL_BUILD +unset OTP_TINY_BUILD +unset TARGET + +progress "Building OTP in $ERL_TOP" +$MAKE || fail "OTP build failed" +progress "Booting optimized runtime" +$ERL_TOP/bin/cerl -eval "erlang:halt()" || { + fail "Failed to boot runtime" +} + +[ $opt_only = yes ] || { + progress "Building debug OTP in $ERL_TOP" + $MAKE TYPE=debug || fail "OTP debug build failed" + progress "Booting debug runtime" + $ERL_TOP/bin/cerl -debug -eval "erlang:halt()" || { + fail "Failed to boot debug runtime" + } +} + +[ $format_check = no ] || { + clang-format --help 2>&1 >/dev/null || { + fail "clang-format not installed. Use --no-format-check option to skip." + } + + progress "Checking C & C++ code formatting" + $MAKE format-check || { + fail "Failed to format C & C++ code" + } +} + +progress "Releasing OTP" +$MAKE release || fail "Releasing OTP failed" + +cd "$ERL_TOP/release/$target" 2>&1 >/dev/null || { + fail "Failed to change directory into release directory: $ERL_TOP/release/$target" +} + +progress "Installing OTP" +./Install -minimal `pwd` || fail "Failed to install release" + +progress "Booting optimized installed runtime" +./bin/erl -eval "erlang:halt()" || { + fail "Failed to boot installed runtime" +} + +cd "$ERL_TOP" 2>&1 >/dev/null || { + fail "Failed to change directory into $ERL_TOP" +} + +export PATH="$ERL_TOP/release/$target/bin:$PATH" + +[ $docs = no ] || { + progress "Building OTP documentation" + $MAKE docs || fail "Building documentation failed" + progress "Releasing OTP documentation" + $MAKE release_docs || fail "Releasing documentation failed" + progress "Running xmllint on OTP documentation" + $MAKE xmllint || fail "xmllint of documentation failed" + progress "Running HTML check on OTP documentation" + ./scripts/otp_html_check "$ERL_TOP/release/$target/" doc/index.html || { + fail "HTML check of documentation failed" + } +} + +[ $dialyzer = no ] || { + progress "Running dialyzer on OTP" + $MAKE dialyzer || fail "Dialyzing OTP failed" +} + +[ "$tests" = none ] || { + test_dir="$ERL_TOP/release/$target/test" + [ -d "$test_dir" ] || mkdir "$test_dir" 2>&1 >/dev/null || { + fail "Failed to create $test_dir directory" + } + if [ "$tests" = all ]; then + progress "Releasing all OTP tests" + ./otp_build tests "$test_dir" || fail "Release of tests failed" + else + for tapp in $tests; do + case $tapp in + test_server) + tapp_dir="$ERL_TOP/lib/test_server" + if [ ! -d "$tapp_dir" ]; then + tapp_dir="$ERL_TOP/lib/common_test/test_server" + fi + ;; + emulator) tapp_dir="$ERL_TOP/erts/emulator/test";; + system) tapp_dir="$ERL_TOP/erts/test";; + epmd) tapp_dir="$ERL_TOP/erts/epmd/test";; + *) tapp_dir="$ERL_TOP/lib/$tapp/test";; + esac + cd "$tapp_dir" 2>&1 >/dev/null || { + fail "Failed to change directory into $tapp_dir" + } + progress "Releasing $tapp tests" + $MAKE "TESTROOT=$test_dir" release_tests || { + fail "Release of $tapp tests failed" + } + done + + fi + + cd "$test_dir" 2>&1 >/dev/null || { + fail "Failed to change directory into $test_dir" + } + + for dir in *; do + + [ -d "$test_dir/$dir" ] || continue + + progress "Building $dir tests" + + cd "$test_dir/$dir" 2>&1 >/dev/null || { + fail "Failed to change directory into $test_dir/$dir" + } + + for mfile in *_data/Makefile.first; do + + [ "$mfile" != '*_data/Makefile.first' ] || continue + + ddir=`dirname $mfile` || fail "dirname on $test_dir/$dir/$mfile failed" + + cd "$test_dir/$dir/$ddir" 2>&1 >/dev/null || { + fail "Failed to change directory into $test_dir/$dir/$ddir" + } + + $MAKE -f Makefile.first || { + fail "Make of $test_dir/$dir/$ddir/Makefile.first failed" + } + + done + + cd "$test_dir/$dir" 2>&1 >/dev/null || { + fail "Failed to change directory into $test_dir/$dir" + } + + if [ -f Emakefile ]; then + erl -noshell -eval 'case make:all() of error -> erlang:halt(1); _ -> init:stop() end.' || { + fail "Build of $dir failed" + } + else + erlc -I `erl -noshell -eval 'io:format("~ts",[code:lib_dir(common_test)]),init:stop()'`/include -I . *.erl || { + fail "Build of $dir failed" + } + fi + + done + + cd "$ERL_TOP" 2>&1 >/dev/null || { + fail "Failed to change directory into $ERL_TOP" + } +} + +if [ $have_printf = yes ]; then + printf "\n\033[32m*** Success! ***\033[0m\n\n" +else + echo "\n*** Success! ***\n" +fi +exit 0 diff --git a/system/doc/general_info/upcoming_incompatibilities.xml b/system/doc/general_info/upcoming_incompatibilities.xml index 04ab4459c0..8538067b57 100644 --- a/system/doc/general_info/upcoming_incompatibilities.xml +++ b/system/doc/general_info/upcoming_incompatibilities.xml @@ -53,6 +53,7 @@ </section> <section> + <marker id="maybe_expr"/> <title>Feature maybe_expr will be enabled by default</title> <p> As of OTP 27, the <c>maybe_expr</c> feature will be approved @@ -119,6 +120,65 @@ the regular expression before matching with it.</p></item> </list> </section> + + <section> + <marker id="float_matching"/> + <title>0.0 and -0.0 will no longer be exactly equal</title> + + <p>Currently, the floating point numbers <c>0.0</c> and <c>-0.0</c> + have distinct internal representations. That can be seen if they are + converted to binaries:</p> + <pre> +1> <input><<0.0/float>>.</input> +<<0,0,0,0,0,0,0,0>> +2> <input><<-0.0/float>>.</input> +<<128,0,0,0,0,0,0,0>></pre> + + <p>However, when they are matched against each other or compared + using the <c>=:=</c> operator, they are considered to be + equal. Thus, <c>0.0 =:= -0.0</c> currently returns + <c>true</c>.</p> + + <p>In Erlang/OTP 27, <c>0.0 =:= -0.0</c> will return <c>false</c>, and matching + <c>0.0</c> against <c>-0.0</c> will fail. When used as map keys, <c>0.0</c> and + <c>-0.0</c> will be considered to be distinct.</p> + + <p>The <c>==</c> operator will continue to return <c>true</c> + for <c>0.0 == -0.0</c>.</p> + + <p>To help to find code that might need to be revised, in OTP 27 + there will be a new compiler warning when matching against + <c>0.0</c> or comparing to that value using the <c>=:=</c> + operator. The warning can be suppressed by matching against + <c>+0.0</c> instead of <c>0.0</c>.</p> + + <p>We plan to introduce the same warning in OTP 26.1, but by default it + will be disabled.</p> + </section> + + <section> + <marker id="singleton_typevars"/> + <title>Singleton type variables will become a compile-time error</title> + + <p>Before Erlang/OTP 26, the compiler would silenty accept the + following spec:</p> + + <pre> +-spec f(Opts) -> term() when + Opts :: {ok, Unknown} | {error, Unknown}. +f(_) -> error.</pre> + + <p>In OTP 26, the compiler emits a warning pointing out that the type variable + <c>Unknown</c> is unbound:</p> + + <pre> +t.erl:6:18: Warning: type variable 'Unknown' is only used once (is unbound) +% 6| Opts :: {ok, Unknown} | {error, Unknown}. +% | ^</pre> + + <p>In OTP 27, that warning will become an error.</p> + </section> + </section> <section> |