Skip to content

Commit 99ec752

Browse files
authored
Backport OTP 24 compatibility patches to v1.11 (#10743)
* Update Erlang warnings translation (#10694) Since erlang/otp@8ecc648 instead of `{eval_failure, Reason}` we'd get `{eval_failure, Call, Reason}` * Fix translating erl compiler errors to diagnostics on OTP 24 (#10719) The compiler previously emitted just `line` and now emits `{line, column}`. The diagnostic struct accepts as position either: nil line {start_line, start_col, end_line, end_col} so we could have used the `column` to create the 4-tuple but we don't have enough information to do that correctly, we don't know where the line ends. * Fix remaining warning translations for OTP 24 (#10720) Yecc error message changed so we need to loosen up our assertion: OTP 23 iex> iex Erlang/OTP 23 [erts-11.1.7] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] Interactive Elixir (1.11.3) - press Ctrl+C to exit (type h() ENTER for help) iex(1)> :yecc.file('f.yrl') f.yrl:1: syntax error before: '.' :error OTP 24 iex> iex Erlang/OTP 24 [DEVELOPMENT] [erts-11.1.7] [source-802d2c5083] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] Interactive Elixir (1.12.0-dev) - press Ctrl+C to exit (type h() ENTER for help) iex(1)> :yecc.file('f.yrl') f.yrl:1:5: syntax error before: . :error * Support 16bit floats in bitstrings (#10740) On OTP 24: iex> <<x::float-16>> = <<60, 0>> iex> x 1.0 iex> <<x::float-16>> <<60, 0>> Before OTP 24 we'd get errors or wouldn't match: iex> <<1.0::float-16>> ** (ArgumentError) argument error while evaluating iex at line 1 <<x::float-16>> = <<60, 0>> ** (MatchError) no match of right hand side value: <<60, 0>> iex> (fn <<x::float-16>> -> x; _ -> :nomatch end).(<<60, 0>>) :nomatch * Handle :compile.file/2 returning :error on OTP 24 (#10742) Before OTP 24 when given invalid args, :compile.file/2 would return: iex(1)> :compile.file('a.erl', [{:d, 'foo', 'bar'}, :report]) {:error, :badarg} On OTP 24: iex(1)> :compile.file('a.erl', [{:d, 'foo', 'bar'}, :report]) *** Internal compiler error *** exception error: bad argument in function io_lib:format/2 called as io_lib:format("badly formed '~s'",[{"foo","bar"}]) in call from sys_messages:list_errors/3 (sys_messages.erl, line 53) in call from lists:foreach/2 (lists.erl, line 1342) in call from compile:comp_ret_err/1 (compile.erl, line 546) in call from compile:'-internal_fun/2-anonymous-0-'/2 (compile.erl, line 229) in call from compile:'-do_compile/2-anonymous-0-'/1 (compile.erl, line 219) :error
1 parent d30c5c0 commit 99ec752

File tree

9 files changed

+96
-20
lines changed

9 files changed

+96
-20
lines changed

lib/elixir/lib/kernel/special_forms.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -233,9 +233,9 @@ defmodule Kernel.SpecialForms do
233233
234234
Sizes for types are a bit more nuanced. The default size for integers is 8.
235235
236-
For floats, it is 64. For floats, `size * unit` must result in 32 or 64,
236+
For floats, it is 64. For floats, `size * unit` must result in 16, 32, or 64,
237237
corresponding to [IEEE 754](https://en.wikipedia.org/wiki/IEEE_floating_point)
238-
binary32 and binary64, respectively.
238+
binary16, binary32, and binary64, respectively.
239239
240240
For binaries, the default is the size of the binary. Only the last binary in a
241241
match can use the default size. All others must have their size specified

lib/elixir/src/elixir_bitstring.erl

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -319,8 +319,13 @@ build_spec(Meta, _Size, Unit, Type, _Endianness, Sign, Spec, E) when Type == bin
319319
build_spec(Meta, Size, Unit, Type, Endianness, Sign, Spec, E) when Type == integer; Type == float ->
320320
NumberSize = number_size(Size, Unit),
321321
if
322-
Type == float, is_integer(NumberSize), NumberSize /= 32, NumberSize /= 64 ->
323-
form_error(Meta, E, ?MODULE, {bittype_float_size, NumberSize});
322+
Type == float, is_integer(NumberSize) ->
323+
case valid_float_size(NumberSize) of
324+
true ->
325+
add_spec(Type, add_spec(Endianness, add_spec(Sign, Spec)));
326+
false ->
327+
form_error(Meta, E, ?MODULE, {bittype_float_size, NumberSize})
328+
end;
324329
Size == default, Unit /= default ->
325330
form_error(Meta, E, ?MODULE, bittype_unit);
326331
true ->
@@ -331,6 +336,12 @@ number_size(Size, default) when is_integer(Size) -> Size;
331336
number_size(Size, Unit) when is_integer(Size) -> Size * Unit;
332337
number_size(Size, _) -> Size.
333338

339+
%% TODO: Simplify when we require OTP 24
340+
valid_float_size(16) -> erlang:system_info(otp_release) >= "24";
341+
valid_float_size(32) -> true;
342+
valid_float_size(64) -> true;
343+
valid_float_size(_) -> false.
344+
334345
add_spec(default, Spec) -> Spec;
335346
add_spec(Key, Spec) -> [{Key, [], []} | Spec].
336347

@@ -372,7 +383,12 @@ format_error(bittype_signed) ->
372383
format_error(bittype_unit) ->
373384
"integer and float types require a size specifier if the unit specifier is given";
374385
format_error({bittype_float_size, Other}) ->
375-
io_lib:format("float requires size*unit to be 32 or 64 (default), got: ~p", [Other]);
386+
Message =
387+
case erlang:system_info(otp_release) >= "24" of
388+
true -> "16, 32, or 64";
389+
false -> "32 or 64"
390+
end,
391+
io_lib:format("float requires size*unit to be ~s (default), got: ~p", [Message, Other]);
376392
format_error({invalid_literal, Literal}) ->
377393
io_lib:format("invalid literal ~ts in <<>>", ['Elixir.Macro':to_string(Literal)]);
378394
format_error({undefined_bittype, Expr}) ->

lib/elixir/src/elixir_erl_compiler.erl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,17 @@ custom_format(sys_core_fold, nomatch_guard) ->
132132
"this check/guard will always yield the same result";
133133

134134
%% Handle literal eval failures
135+
custom_format(sys_core_fold, {eval_failure, {Mod, Name, Arity}, Error}) ->
136+
#{'__struct__' := Struct} = 'Elixir.Exception':normalize(error, Error),
137+
{ExMod, ExName, ExArgs} = elixir_rewrite:erl_to_ex(Mod, Name, lists:duplicate(Arity, nil)),
138+
Call = 'Elixir.Exception':format_mfa(ExMod, ExName, length(ExArgs)),
139+
Trimmed = case Call of
140+
<<"Kernel.", Rest/binary>> -> Rest;
141+
_ -> Call
142+
end,
143+
["the call to ", Trimmed, " will fail with ", elixir_aliases:inspect(Struct)];
144+
145+
%% TODO: remove when we require OTP 24
135146
custom_format(sys_core_fold, {eval_failure, Error}) ->
136147
#{'__struct__' := Struct} = 'Elixir.Exception':normalize(error, Error),
137148
["this expression will fail with ", elixir_aliases:inspect(Struct)];

lib/elixir/test/elixir/kernel/expansion_test.exs

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2387,17 +2387,49 @@ defmodule Kernel.ExpansionTest do
23872387
end
23882388
end
23892389

2390-
test "raises for invalid size * unit for floats" do
2391-
message = ~r"float requires size\*unit to be 32 or 64 \(default\), got: 128"
2390+
# TODO: Simplify when we require OTP 24
2391+
if System.otp_release() >= "24" do
2392+
test "16-bit floats" do
2393+
import Kernel, except: [-: 2]
23922394

2393-
assert_raise CompileError, message, fn ->
2394-
expand(quote(do: <<12.3::32*4>>))
2395+
assert expand(quote(do: <<12.3::float-16>>)) |> clean_meta([:alignment]) ==
2396+
quote(do: <<12.3::float()-size(16)>>)
23952397
end
23962398

2397-
message = ~r"float requires size\*unit to be 32 or 64 \(default\), got: 256"
2399+
test "raises for invalid size * unit for floats" do
2400+
message = ~r"float requires size\*unit to be 16, 32, or 64 \(default\), got: 128"
23982401

2399-
assert_raise CompileError, message, fn ->
2400-
expand(quote(do: <<12.3::256>>))
2402+
assert_raise CompileError, message, fn ->
2403+
expand(quote(do: <<12.3::32*4>>))
2404+
end
2405+
2406+
message = ~r"float requires size\*unit to be 16, 32, or 64 \(default\), got: 256"
2407+
2408+
assert_raise CompileError, message, fn ->
2409+
expand(quote(do: <<12.3::256>>))
2410+
end
2411+
end
2412+
else
2413+
test "16-bit floats" do
2414+
message = ~r"float requires size\*unit to be 32 or 64 \(default\), got: 16"
2415+
2416+
assert_raise CompileError, message, fn ->
2417+
expand(quote(do: <<12.3::16>>))
2418+
end
2419+
end
2420+
2421+
test "raises for invalid size * unit for floats" do
2422+
message = ~r"float requires size\*unit to be 32 or 64 \(default\), got: 128"
2423+
2424+
assert_raise CompileError, message, fn ->
2425+
expand(quote(do: <<12.3::32*4>>))
2426+
end
2427+
2428+
message = ~r"float requires size\*unit to be 32 or 64 \(default\), got: 256"
2429+
2430+
assert_raise CompileError, message, fn ->
2431+
expand(quote(do: <<12.3::256>>))
2432+
end
24012433
end
24022434
end
24032435

lib/elixir/test/elixir/kernel/warning_test.exs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -931,22 +931,31 @@ defmodule Kernel.WarningTest do
931931
purge(Sample)
932932
end
933933

934+
# TODO: Simplify when we require OTP 24
935+
if System.otp_release() >= "24" do
936+
@argument_error_message "the call to :erlang.atom_to_binary/2"
937+
@arithmetic_error_message "the call to +/2"
938+
else
939+
@argument_error_message "this expression"
940+
@arithmetic_error_message "this expression"
941+
end
942+
934943
test "eval failure warning" do
935944
assert capture_err(fn ->
936945
Code.eval_string("""
937946
defmodule Sample1 do
938947
def foo, do: Atom.to_string "abc"
939948
end
940949
""")
941-
end) =~ ~r"this expression will fail with ArgumentError\n.*nofile:2"
950+
end) =~ "#{@argument_error_message} will fail with ArgumentError\n nofile:2"
942951

943952
assert capture_err(fn ->
944953
Code.eval_string("""
945954
defmodule Sample2 do
946955
def foo, do: 1 + nil
947956
end
948957
""")
949-
end) =~ ~r"this expression will fail with ArithmeticError\n.*nofile:2"
958+
end) =~ "#{@arithmetic_error_message} will fail with ArithmeticError\n nofile:2"
950959
after
951960
purge([Sample1, Sample2])
952961
end

lib/mix/lib/mix/compilers/erlang.ex

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ defmodule Mix.Compilers.Erlang do
271271
defp to_diagnostics(warnings_or_errors, severity) do
272272
for {file, issues} <- warnings_or_errors,
273273
{line, module, data} <- issues do
274-
position = if is_integer(line) and line >= 1, do: line
274+
position = line(line)
275275

276276
%Mix.Task.Compiler.Diagnostic{
277277
file: Path.absname(file),
@@ -288,7 +288,12 @@ defmodule Mix.Compilers.Erlang do
288288
for {_, warnings} <- entries,
289289
{file, issues} <- warnings,
290290
{line, module, message} <- issues do
291-
IO.puts("#{file}:#{line}: Warning: #{module.format_error(message)}")
291+
IO.puts("#{file}:#{line(line)}: Warning: #{module.format_error(message)}")
292292
end
293293
end
294+
295+
defp line({line, _column}) when is_integer(line) and line >= 1, do: line
296+
# TODO: remove when we require OTP 24
297+
defp line(line) when is_integer(line) and line >= 1, do: line
298+
defp line(_), do: nil
294299
end

lib/mix/lib/mix/tasks/compile.erlang.ex

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,10 @@ defmodule Mix.Tasks.Compile.Erlang do
100100
file = Erlang.to_erl_file(Path.rootname(input, ".erl"))
101101

102102
case :compile.file(file, erlc_options) do
103-
{:error, :badarg} ->
103+
# TODO: Don't handle {:error, :badarg} when we require OTP 24
104+
error when error == :error or error == {:error, :badarg} ->
104105
message =
105-
"Compiling Erlang #{inspect(file)} failed with ArgumentError, probably because of invalid :erlc_options"
106+
"Compiling Erlang file #{inspect(file)} failed, probably because of invalid :erlc_options"
106107

107108
Mix.raise(message)
108109

lib/mix/test/mix/tasks/compile.erlang_test.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ defmodule Mix.Tasks.Compile.ErlangTest do
1414
@tag erlc_options: [{:d, 'foo', 'bar'}]
1515
test "raises on invalid erlc_options" do
1616
in_fixture("compile_erlang", fn ->
17-
assert_raise Mix.Error, ~r"failed with ArgumentError", fn ->
17+
assert_raise Mix.Error, ~r"Compiling Erlang file '.*' failed", fn ->
1818
capture_io(fn ->
1919
Mix.Tasks.Compile.Erlang.run([])
2020
end)

lib/mix/test/mix/tasks/compile.yecc_test.exs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,12 @@ defmodule Mix.Tasks.Compile.YeccTest do
2323
assert %Mix.Task.Compiler.Diagnostic{
2424
compiler_name: "yecc",
2525
file: ^file,
26-
message: "syntax error before: '.'",
26+
message: message,
2727
position: 1,
2828
severity: :error
2929
} = diagnostic
30+
31+
assert message =~ "syntax error before: "
3032
end)
3133

3234
assert File.regular?("src/test_ok.erl")

0 commit comments

Comments
 (0)