summaryrefslogtreecommitdiff
path: root/lib/elixir
diff options
context:
space:
mode:
authorsabiwara <sabiwara@gmail.com>2022-08-06 16:28:07 +0900
committerGitHub <noreply@github.com>2022-08-06 09:28:07 +0200
commit1c77deb1996023350b0e05b6b510ad9952dc9106 (patch)
treebe5f7bc22bf691f2bff7cf1c4d0350e206599857 /lib/elixir
parentf3fef857c78211d822bc87cb5cebced2435ed3e0 (diff)
downloadelixir-1c77deb1996023350b0e05b6b510ad9952dc9106.tar.gz
Replace inner AST for bitstring modifiers (#12055)
Diffstat (limited to 'lib/elixir')
-rw-r--r--lib/elixir/src/elixir_bitstring.erl20
-rw-r--r--lib/elixir/src/elixir_erl_pass.erl2
-rw-r--r--lib/elixir/test/elixir/kernel/expansion_test.exs140
3 files changed, 101 insertions, 61 deletions
diff --git a/lib/elixir/src/elixir_bitstring.erl b/lib/elixir/src/elixir_bitstring.erl
index 7977a289f..97d61fc60 100644
--- a/lib/elixir/src/elixir_bitstring.erl
+++ b/lib/elixir/src/elixir_bitstring.erl
@@ -65,11 +65,11 @@ expr_type(Binary) when is_binary(Binary) -> binary;
expr_type({'<<>>', _, _}) -> bitstring;
expr_type(_) -> default.
-infer_spec(bitstring, Meta) -> {bitstring, Meta, []};
-infer_spec(binary, Meta) -> {binary, Meta, []};
-infer_spec(float, Meta) -> {float, Meta, []};
-infer_spec(integer, Meta) -> {integer, Meta, []};
-infer_spec(default, Meta) -> {integer, Meta, []}.
+infer_spec(bitstring, Meta) -> {bitstring, Meta, nil};
+infer_spec(binary, Meta) -> {binary, Meta, nil};
+infer_spec(float, Meta) -> {float, Meta, nil};
+infer_spec(integer, Meta) -> {integer, Meta, nil};
+infer_spec(default, Meta) -> {integer, Meta, nil}.
concat_or_prepend_bitstring(_Meta, {'<<>>', _, []}, _ERight, Acc, _E, _RequireSize) ->
Acc;
@@ -77,10 +77,10 @@ concat_or_prepend_bitstring(Meta, {'<<>>', PartsMeta, Parts} = ELeft, ERight, Ac
case E of
#{context := match} when RequireSize ->
case lists:last(Parts) of
- {'::', SpecMeta, [Bin, {binary, _, []}]} when not is_binary(Bin) ->
+ {'::', SpecMeta, [Bin, {binary, _, nil}]} when not is_binary(Bin) ->
form_error(SpecMeta, E, ?MODULE, unsized_binary);
- {'::', SpecMeta, [_, {bitstring, _, []}]} ->
+ {'::', SpecMeta, [_, {bitstring, _, nil}]} ->
form_error(SpecMeta, E, ?MODULE, unsized_binary);
_ ->
@@ -91,7 +91,7 @@ concat_or_prepend_bitstring(Meta, {'<<>>', PartsMeta, Parts} = ELeft, ERight, Ac
end,
case ERight of
- {binary, _, []} ->
+ {binary, _, nil} ->
{alignment, Alignment} = lists:keyfind(alignment, 1, PartsMeta),
if
@@ -104,7 +104,7 @@ concat_or_prepend_bitstring(Meta, {'<<>>', PartsMeta, Parts} = ELeft, ERight, Ac
true ->
[{'::', Meta, [ELeft, ERight]} | Acc]
end;
- {bitstring, _, []} ->
+ {bitstring, _, nil} ->
lists:reverse(Parts, Acc)
end;
concat_or_prepend_bitstring(Meta, ELeft, ERight, Acc, _E, _RequireSize) ->
@@ -335,7 +335,7 @@ valid_float_size(64) -> true;
valid_float_size(_) -> false.
add_spec(default, Spec) -> Spec;
-add_spec(Key, Spec) -> [{Key, [], []} | Spec].
+add_spec(Key, Spec) -> [{Key, [], nil} | Spec].
find_match([{'=', _, [_Left, _Right]} = Expr | _Rest]) ->
Expr;
diff --git a/lib/elixir/src/elixir_erl_pass.erl b/lib/elixir/src/elixir_erl_pass.erl
index f5143cf17..77771b81f 100644
--- a/lib/elixir/src/elixir_erl_pass.erl
+++ b/lib/elixir/src/elixir_erl_pass.erl
@@ -566,7 +566,7 @@ extract_bit_type({'-', _, [L, R]}, Acc) ->
extract_bit_type(L, extract_bit_type(R, Acc));
extract_bit_type({unit, _, [Arg]}, Acc) ->
[{unit, Arg} | Acc];
-extract_bit_type({Other, _, []}, Acc) ->
+extract_bit_type({Other, _, nil}, Acc) ->
[Other | Acc].
%% Optimizations that are specific to Erlang and change
diff --git a/lib/elixir/test/elixir/kernel/expansion_test.exs b/lib/elixir/test/elixir/kernel/expansion_test.exs
index accaac010..5584c3501 100644
--- a/lib/elixir/test/elixir/kernel/expansion_test.exs
+++ b/lib/elixir/test/elixir/kernel/expansion_test.exs
@@ -730,11 +730,12 @@ defmodule Kernel.ExpansionTest do
after_expansion =
quote do
- for(<<(<<a::integer()>> <- b())>>, do: c = 1)
+ for(<<(<<a::integer>> <- b())>>, do: c = 1)
c()
end
- assert expand(before_expansion) |> clean_meta([:alignment]) == after_expansion
+ assert expand(before_expansion) |> clean_meta([:alignment]) ==
+ clean_bit_modifiers(after_expansion)
end
test "variables inside generator args do not leak" do
@@ -2182,13 +2183,16 @@ defmodule Kernel.ExpansionTest do
describe "bitstrings" do
test "parallel match" do
assert expand(quote(do: <<foo>> = <<bar>>)) |> clean_meta([:alignment]) ==
- quote(do: <<foo::integer()>> = <<bar()::integer()>>)
+ quote(do: <<foo::integer>> = <<bar()::integer>>)
+ |> clean_bit_modifiers()
assert expand(quote(do: <<foo>> = baz = <<bar>>)) |> clean_meta([:alignment]) ==
- quote(do: <<foo::integer()>> = baz = <<bar()::integer()>>)
+ quote(do: <<foo::integer>> = baz = <<bar()::integer>>)
+ |> clean_bit_modifiers()
assert expand(quote(do: <<foo>> = {<<baz>>} = bar())) |> clean_meta([:alignment]) ==
- quote(do: <<foo::integer()>> = {<<baz::integer()>>} = bar())
+ quote(do: <<foo::integer>> = {<<baz::integer>>} = bar())
+ |> clean_bit_modifiers()
message = ~r"binary patterns cannot be matched in parallel using \"=\""
@@ -2265,11 +2269,12 @@ defmodule Kernel.ExpansionTest do
test "nested match" do
assert expand(quote(do: <<foo = bar>>)) |> clean_meta([:alignment]) ==
- quote(do: <<foo = bar()::integer()>>)
+ quote(do: <<foo = bar()::integer>>) |> clean_bit_modifiers()
assert expand(quote(do: <<?-, <<_, _::binary>> = rest()::binary>>))
|> clean_meta([:alignment]) ==
- quote(do: <<45::integer(), <<_::integer(), _::binary()>> = rest()::binary()>>)
+ quote(do: <<45::integer, <<_::integer, _::binary>> = rest()::binary>>)
+ |> clean_bit_modifiers()
message = ~r"cannot pattern match inside a bitstring that is already in match"
@@ -2287,14 +2292,15 @@ defmodule Kernel.ExpansionTest do
# Check expansion happens only once
assert expand(quote(do: "foo#{message_hello("bar")}")) |> clean_meta([:alignment]) ==
- quote(do: <<"foo"::binary(), "bar"::binary()>>)
+ quote(do: <<"foo"::binary, "bar"::binary>>) |> clean_bit_modifiers()
assert_received :hello
refute_received :hello
# And it also works in match
assert expand(quote(do: "foo#{bar()}" = "foobar")) |> clean_meta([:alignment]) ==
- quote(do: <<"foo"::binary(), "bar"::binary()>> = "foobar")
+ quote(do: <<"foo"::binary, "bar"::binary>> = "foobar")
+ |> clean_bit_modifiers()
end
test "inlines binaries inside interpolation is isomorphic after manual expansion" do
@@ -2303,7 +2309,8 @@ defmodule Kernel.ExpansionTest do
quoted = Macro.prewalk(quote(do: "foo#{bar()}" = "foobar"), &Macro.expand(&1, __ENV__))
assert expand(quoted) |> clean_meta([:alignment]) ==
- quote(do: <<"foo"::binary(), "bar"::binary()>> = "foobar")
+ quote(do: <<"foo"::binary, "bar"::binary>> = "foobar")
+ |> clean_bit_modifiers()
end
test "expands size * unit" do
@@ -2311,50 +2318,50 @@ defmodule Kernel.ExpansionTest do
import Kernel.ExpansionTarget
assert expand(quote(do: <<x::13>>)) |> clean_meta([:alignment]) ==
- quote(do: <<x()::integer()-size(13)>>)
+ quote(do: <<x()::integer-size(13)>>) |> clean_bit_modifiers()
assert expand(quote(do: <<x::13*6>>)) |> clean_meta([:alignment]) ==
- quote(do: <<x()::integer()-unit(6)-size(13)>>)
+ quote(do: <<x()::integer-unit(6)-size(13)>>) |> clean_bit_modifiers()
assert expand(quote(do: <<x::_*6-binary>>)) |> clean_meta([:alignment]) ==
- quote(do: <<x()::binary()-unit(6)>>)
+ quote(do: <<x()::binary-unit(6)>>) |> clean_bit_modifiers()
assert expand(quote(do: <<x::13*6-binary>>)) |> clean_meta([:alignment]) ==
- quote(do: <<x()::binary()-unit(6)-size(13)>>)
+ quote(do: <<x()::binary-unit(6)-size(13)>>) |> clean_bit_modifiers()
assert expand(quote(do: <<x::binary-(13 * 6)-binary>>)) |> clean_meta([:alignment]) ==
- quote(do: <<x()::binary()-unit(6)-size(13)>>)
+ quote(do: <<x()::binary-unit(6)-size(13)>>) |> clean_bit_modifiers()
assert expand(quote(do: <<x::seventeen()>>)) |> clean_meta([:alignment]) ==
- quote(do: <<x()::integer()-size(17)>>)
+ quote(do: <<x()::integer-size(17)>>) |> clean_bit_modifiers()
assert expand(quote(do: <<x::seventeen()*2>>)) |> clean_meta([:alignment]) ==
- quote(do: <<x()::integer()-unit(2)-size(17)>>)
+ quote(do: <<x()::integer-unit(2)-size(17)>>) |> clean_bit_modifiers()
assert expand(quote(do: <<x::seventeen()*seventeen()>>)) |> clean_meta([:alignment]) ==
- quote(do: <<x()::integer()-unit(17)-size(17)>>)
+ quote(do: <<x()::integer-unit(17)-size(17)>>) |> clean_bit_modifiers()
assert expand(quote(do: <<x::_*seventeen()-binary>>)) |> clean_meta([:alignment]) ==
- quote(do: <<x()::binary()-unit(17)>>)
+ quote(do: <<x()::binary-unit(17)>>) |> clean_bit_modifiers()
end
test "expands binary/bitstring specifiers" do
import Kernel, except: [-: 1, -: 2]
assert expand(quote(do: <<x::binary>>)) |> clean_meta([:alignment]) ==
- quote(do: <<x()::binary()>>)
+ quote(do: <<x()::binary>>) |> clean_bit_modifiers()
assert expand(quote(do: <<x::bytes>>)) |> clean_meta([:alignment]) ==
- quote(do: <<x()::binary()>>)
+ quote(do: <<x()::binary>>) |> clean_bit_modifiers()
assert expand(quote(do: <<x::bitstring>>)) |> clean_meta([:alignment]) ==
- quote(do: <<x()::bitstring()>>)
+ quote(do: <<x()::bitstring>>) |> clean_bit_modifiers()
assert expand(quote(do: <<x::bits>>)) |> clean_meta([:alignment]) ==
- quote(do: <<x()::bitstring()>>)
+ quote(do: <<x()::bitstring>>) |> clean_bit_modifiers()
assert expand(quote(do: <<x::binary-little>>)) |> clean_meta([:alignment]) ==
- quote(do: <<x()::binary()>>)
+ quote(do: <<x()::binary>>) |> clean_bit_modifiers()
message = ~r"signed and unsigned specifiers are supported only on integer and float type"
@@ -2367,13 +2374,13 @@ defmodule Kernel.ExpansionTest do
import Kernel, except: [-: 1, -: 2]
assert expand(quote(do: <<x::utf8>>)) |> clean_meta([:alignment]) ==
- quote(do: <<x()::utf8()>>)
+ quote(do: <<x()::utf8>>) |> clean_bit_modifiers()
assert expand(quote(do: <<x::utf16>>)) |> clean_meta([:alignment]) ==
- quote(do: <<x()::utf16()>>)
+ quote(do: <<x()::utf16>>) |> clean_bit_modifiers()
assert expand(quote(do: <<x::utf32-little>>)) |> clean_meta([:alignment]) ==
- quote(do: <<x()::utf32()-little()>>)
+ quote(do: <<x()::utf32-little>>) |> clean_bit_modifiers()
message = ~r"signed and unsigned specifiers are supported only on integer and float type"
@@ -2390,19 +2397,19 @@ defmodule Kernel.ExpansionTest do
import Kernel, except: [-: 1, -: 2]
assert expand(quote(do: <<x::integer>>)) |> clean_meta([:alignment]) ==
- quote(do: <<x()::integer()>>)
+ quote(do: <<x()::integer>>) |> clean_bit_modifiers()
assert expand(quote(do: <<x::little>>)) |> clean_meta([:alignment]) ==
- quote(do: <<x()::integer()-little()>>)
+ quote(do: <<x()::integer-little>>) |> clean_bit_modifiers()
assert expand(quote(do: <<x::signed>>)) |> clean_meta([:alignment]) ==
- quote(do: <<x()::integer()-signed()>>)
+ quote(do: <<x()::integer-signed>>) |> clean_bit_modifiers()
assert expand(quote(do: <<x::signed-native>>)) |> clean_meta([:alignment]) ==
- quote(do: <<x()::integer()-native()-signed()>>)
+ quote(do: <<x()::integer-native-signed>>) |> clean_bit_modifiers()
assert expand(quote(do: <<x::float-signed-native>>)) |> clean_meta([:alignment]) ==
- quote(do: <<x()::float()-native()-signed()>>)
+ quote(do: <<x()::float-native-signed>>) |> clean_bit_modifiers()
message =
~r"integer and float types require a size specifier if the unit specifier is given"
@@ -2417,11 +2424,12 @@ defmodule Kernel.ExpansionTest do
import Kernel.ExpansionTarget
assert expand(quote(do: <<x::seventeen>>)) |> clean_meta([:alignment]) ==
- quote(do: <<x()::integer()-size(17)>>)
+ quote(do: <<x()::integer-size(17)>>) |> clean_bit_modifiers()
assert expand(quote(do: <<seventeen::seventeen, x::size(seventeen)>> = 1))
|> clean_meta([:alignment]) ==
- quote(do: <<seventeen::integer()-size(17), x::integer()-size(seventeen)>> = 1)
+ quote(do: <<seventeen::integer-size(17), x::integer-size(seventeen)>> = 1)
+ |> clean_bit_modifiers()
end
test "expands macro in args" do
@@ -2436,10 +2444,11 @@ defmodule Kernel.ExpansionTest do
after_expansion =
quote do
:"Elixir.Kernel.ExpansionTarget"
- <<x()::integer()-size(17)>>
+ <<x()::integer-size(17)>>
end
- assert expand(before_expansion) |> clean_meta([:alignment]) == after_expansion
+ assert expand(before_expansion) |> clean_meta([:alignment]) ==
+ clean_bit_modifiers(after_expansion)
end
test "supports dynamic size" do
@@ -2454,10 +2463,11 @@ defmodule Kernel.ExpansionTest do
after_expansion =
quote do
var = 1
- <<x()::integer()-unit(8)-size(var)>>
+ <<x()::integer-unit(8)-size(var)>>
end
- assert expand(before_expansion) |> clean_meta([:alignment]) == after_expansion
+ assert expand(before_expansion) |> clean_meta([:alignment]) ==
+ clean_bit_modifiers(after_expansion)
end
defmacro offset(size, binary) do
@@ -2475,25 +2485,29 @@ defmodule Kernel.ExpansionTest do
import Kernel, except: [-: 1, -: 2]
assert expand(quote(do: <<x, <<y::signed-native>>, z>>)) |> clean_meta([:alignment]) ==
- quote(do: <<x()::integer(), y()::integer()-native()-signed(), z()::integer()>>)
+ quote(do: <<x()::integer, y()::integer-native-signed, z()::integer>>)
+ |> clean_bit_modifiers()
assert expand(quote(do: <<x, <<y::signed-native>>::bitstring, z>>))
|> clean_meta([:alignment]) ==
- quote(do: <<x()::integer(), y()::integer()-native()-signed(), z()::integer()>>)
+ quote(do: <<x()::integer, y()::integer-native-signed, z()::integer>>)
+ |> clean_bit_modifiers()
end
test "merges binaries" do
import Kernel, except: [-: 1, -: 2]
assert expand(quote(do: "foo" <> x)) |> clean_meta([:alignment]) ==
- quote(do: <<"foo"::binary(), x()::binary()>>)
+ quote(do: <<"foo"::binary, x()::binary>>) |> clean_bit_modifiers()
assert expand(quote(do: "foo" <> <<x::size(4), y::size(4)>>)) |> clean_meta([:alignment]) ==
- quote(do: <<"foo"::binary(), x()::integer()-size(4), y()::integer()-size(4)>>)
+ quote(do: <<"foo"::binary, x()::integer-size(4), y()::integer-size(4)>>)
+ |> clean_bit_modifiers()
assert expand(quote(do: <<"foo", <<x::size(4), y::size(4)>>::binary>>))
|> clean_meta([:alignment]) ==
- quote(do: <<"foo"::binary(), x()::integer()-size(4), y()::integer()-size(4)>>)
+ quote(do: <<"foo"::binary, x()::integer-size(4), y()::integer-size(4)>>)
+ |> clean_bit_modifiers()
end
test "guard expressions on size" do
@@ -2511,17 +2525,19 @@ defmodule Kernel.ExpansionTest do
after_expansion =
quote do
var = 1
- <<x()::integer()-size(:erlang.+(var, 3))>>
+ <<x()::integer-size(:erlang.+(var, 3))>>
end
- assert expand(before_expansion) |> clean_meta([:alignment]) == after_expansion
+ assert expand(before_expansion) |> clean_meta([:alignment]) ==
+ clean_bit_modifiers(after_expansion)
# Other valid guard expressions are also legal for bitstring size in OTP 23+
before_expansion = quote(do: <<x::size(length('test'))>>)
- after_expansion = quote(do: <<x()::integer()-size(:erlang.length('test'))>>)
+ after_expansion = quote(do: <<x()::integer-size(:erlang.length('test'))>>)
- assert expand(before_expansion) |> clean_meta([:alignment]) == after_expansion
+ assert expand(before_expansion) |> clean_meta([:alignment]) ==
+ clean_bit_modifiers(after_expansion)
end
test "map lookup on size" do
@@ -2536,10 +2552,11 @@ defmodule Kernel.ExpansionTest do
after_expansion =
quote do
var = %{foo: 3}
- <<x()::integer()-size(var.foo)>>
+ <<x()::integer-size(var.foo)>>
end
- assert expand(before_expansion) |> clean_meta([:alignment]) == after_expansion
+ assert expand(before_expansion) |> clean_meta([:alignment]) ==
+ clean_bit_modifiers(after_expansion)
end
test "raises on unaligned binaries in match" do
@@ -2574,7 +2591,7 @@ defmodule Kernel.ExpansionTest do
import Kernel, except: [-: 1, -: 2]
assert expand(quote(do: <<12.3::float-16>>)) |> clean_meta([:alignment]) ==
- quote(do: <<12.3::float()-size(16)>>)
+ quote(do: <<12.3::float-size(16)>>) |> clean_bit_modifiers()
end
test "raises for invalid size * unit for floats" do
@@ -2861,6 +2878,29 @@ defmodule Kernel.ExpansionTest do
Macro.prewalk(expr, &Macro.update_meta(&1, cleaner))
end
+ @bitstring_modifiers [
+ :integer,
+ :float,
+ :binary,
+ :utf8,
+ :utf16,
+ :utf32,
+ :native,
+ :signed,
+ :bitstring,
+ :little
+ ]
+
+ defp clean_bit_modifiers(expr) do
+ Macro.prewalk(expr, fn
+ {expr, meta, atom} when expr in @bitstring_modifiers and is_atom(atom) ->
+ {expr, meta, nil}
+
+ other ->
+ other
+ end)
+ end
+
defp expand(expr) do
expand_env(expr, __ENV__) |> elem(0)
end