Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions lib/elixir/src/elixir_erl.erl
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,14 @@ elixir_to_erl(Tree, Ann) when is_atom(Tree) ->
{atom, Ann, Tree};
elixir_to_erl(Tree, Ann) when is_integer(Tree) ->
{integer, Ann, Tree};
elixir_to_erl(Tree, Ann) when is_float(Tree), Tree == 0.0 ->
% 0.0 needs to be rewritten as the AST for +0.0 in matches
Op =
case <<Tree/float>> of
<<1:1,_:63>> -> '-';
_ -> '+'
end,
{op, Ann, Op, {float, Ann, 0.0}};
elixir_to_erl(Tree, Ann) when is_float(Tree) ->
{float, Ann, Tree};
elixir_to_erl(Tree, Ann) when is_binary(Tree) ->
Expand Down
6 changes: 6 additions & 0 deletions lib/elixir/src/elixir_expand.erl
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,10 @@ expand(Pid, S, E) when is_pid(Pid) ->
{Pid, E}
end;

expand(Zero, S, #{context := match} = E) when is_float(Zero), Zero == 0.0 ->
elixir_errors:file_warn([], E, ?MODULE, invalid_match_on_zero_float),
{Zero, S, E};

expand(Other, S, E) when is_number(Other); is_atom(Other); is_binary(Other) ->
{Other, S, E};

Expand Down Expand Up @@ -1163,6 +1167,8 @@ guard_context(_) -> "guards".
format_error({remote_nullary_no_parens, Expr}) ->
String = 'Elixir.String':replace_suffix('Elixir.Macro':to_string(Expr), <<"()">>, <<>>),
io_lib:format("parentheses are required for function calls with no arguments, got: ~ts", [String]);
format_error(invalid_match_on_zero_float) ->
"pattern matching on 0.0 is equivalent to matching only on +0.0 from Erlang/OTP 27+. Instead you must match on +0.0 or -0.0";
format_error({useless_literal, Term}) ->
io_lib:format("code block contains unused literal ~ts "
"(remove the literal or assign it to _ to avoid warnings)",
Expand Down
17 changes: 15 additions & 2 deletions lib/elixir/test/elixir/kernel/expansion_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,16 @@ defmodule Kernel.ExpansionTest do
end
end

describe "floats" do
test "cannot be 0.0 inside match" do
assert capture_io(:stderr, fn -> expand(quote(do: 0.0 = 0.0)) end) =~
"pattern matching on 0.0 is equivalent to matching only on +0.0 from Erlang/OTP 27+"

assert {:=, [], [+0.0, +0.0]} = expand(quote(do: +0.0 = 0.0))
assert {:=, [], [-0.0, +0.0]} = expand(quote(do: -0.0 = 0.0))
end
end

describe "tuples" do
test "expanded as arguments" do
assert expand(quote(do: {after_expansion = 1, a})) == quote(do: {after_expansion = 1, a()})
Expand Down Expand Up @@ -715,8 +725,11 @@ defmodule Kernel.ExpansionTest do
expand(quote(do: [1] ++ 2 ++ [3] = [1, 2, 3]))
end)

assert {:=, _, [-1, {{:., _, [:erlang, :-]}, _, [1]}]} = expand(quote(do: -1 = -1))
assert {:=, _, [1, {{:., _, [:erlang, :+]}, _, [1]}]} = expand(quote(do: +1 = +1))
assert {:=, _, [-1, {{:., _, [:erlang, :-]}, _, [1]}]} =
expand(quote(do: -1 = -1))

assert {:=, _, [1, {{:., _, [:erlang, :+]}, _, [1]}]} =
expand(quote(do: +1 = +1))

assert {:=, _, [[{:|, _, [1, [{:|, _, [2, 3]}]]}], [1, 2, 3]]} =
expand(quote(do: [1] ++ [2] ++ 3 = [1, 2, 3]))
Expand Down
22 changes: 21 additions & 1 deletion lib/elixir/test/elixir/module/types/pattern_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,20 @@ defmodule Module.Types.PatternTest do
end
end

defmacrop quoted_pattern_with_diagnostics(patterns) do
{ast, diagnostics} = Code.with_diagnostics(fn -> expand_head(patterns, true) end)

quote do
{patterns, true} = unquote(Macro.escape(ast))

result =
Pattern.of_pattern(patterns, new_stack(), new_context())
|> lift_result()

{result, unquote(Macro.escape(diagnostics))}
end
end

defmacrop quoted_head(patterns, guards \\ []) do
quote do
{patterns, guards} = unquote(Macro.escape(expand_head(patterns, guards)))
Expand Down Expand Up @@ -76,8 +90,14 @@ defmodule Module.Types.PatternTest do
assert quoted_pattern(false) == {:ok, {:atom, false}}
assert quoted_pattern(:foo) == {:ok, {:atom, :foo}}
assert quoted_pattern(0) == {:ok, :integer}
assert quoted_pattern(0.0) == {:ok, :float}
assert quoted_pattern(+0.0) == {:ok, :float}
assert quoted_pattern(-0.0) == {:ok, :float}
assert quoted_pattern("foo") == {:ok, :binary}

assert {{:ok, :float}, [diagnostic]} = quoted_pattern_with_diagnostics(0.0)

assert diagnostic.message =~
"pattern matching on 0.0 is equivalent to matching only on +0.0"
Comment on lines +97 to +100
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I struggled way too much to disable this warning 😅
It is also on #12950 I believe

end

test "list" do
Expand Down
6 changes: 6 additions & 0 deletions lib/elixir/test/erlang/control_test.erl
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ cond_line_test() ->
{clause, 3, _, _, _}]
} = to_erl("cond do\n 1 -> :ok\n 2 -> :ok\nend").

float_match_test() ->
{'case', _, _,
[{clause, _, [{op, _, '+', {float, _, 0.0}}], [], [{atom, _, pos}]},
{clause, _, [{op, _, '-', {float, _, 0.0}}], [], [{atom, _, neg}]}]
} = to_erl("case X do\n +0.0 -> :pos\n -0.0 -> :neg\nend").

% Optimized

optimized_if_test() ->
Expand Down