summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJosé Valim <jose.valim@dashbit.co>2020-04-05 20:13:24 +0200
committerJosé Valim <jose.valim@dashbit.co>2020-04-05 20:15:50 +0200
commit3d3e233255fdc5b870a2825222b0e93f2c26bda6 (patch)
tree96247dabd2613d6e760fbf36b37752d45eafd7b1
parentc257b13291d799364623af02c6049d8a5f7b298b (diff)
downloadelixir-3d3e233255fdc5b870a2825222b0e93f2c26bda6.tar.gz
Refactor binary handling
-rw-r--r--lib/elixir/lib/macro.ex2
-rw-r--r--lib/elixir/src/elixir.hrl2
-rw-r--r--lib/elixir/src/elixir_bitstring.erl166
-rw-r--r--lib/elixir/test/elixir/kernel/binary_test.exs4
-rw-r--r--lib/elixir/test/elixir/kernel/expansion_test.exs12
5 files changed, 93 insertions, 93 deletions
diff --git a/lib/elixir/lib/macro.ex b/lib/elixir/lib/macro.ex
index 836001e48..47a25b098 100644
--- a/lib/elixir/lib/macro.ex
+++ b/lib/elixir/lib/macro.ex
@@ -965,7 +965,7 @@ defmodule Macro do
defp bitpart_to_string({:"::", meta, [left, right]} = ast, fun) do
result =
- if meta[:inferred_binary_type] do
+ if meta[:inferred_bitstring_spec] do
to_string(left, fun)
else
op_to_string(left, fun, :"::", :left) <>
diff --git a/lib/elixir/src/elixir.hrl b/lib/elixir/src/elixir.hrl
index faa39456c..e22a9d6ca 100644
--- a/lib/elixir/src/elixir.hrl
+++ b/lib/elixir/src/elixir.hrl
@@ -1,4 +1,4 @@
--define(key(M, K), maps:get(K, M)).
+-define(key(M, K), map_get(K, M)).
-define(ann(Opts), elixir_erl:get_ann(Opts)).
-define(line(Opts), elixir_utils:get_line(Opts)).
-define(generated(Meta), [{generated, true} | Meta]).
diff --git a/lib/elixir/src/elixir_bitstring.erl b/lib/elixir/src/elixir_bitstring.erl
index ef36b576c..3b6534534 100644
--- a/lib/elixir/src/elixir_bitstring.erl
+++ b/lib/elixir/src/elixir_bitstring.erl
@@ -33,78 +33,82 @@ expand(_BitstrMeta, _Fun, [], Acc, E, Alignment, _RequireSize) ->
expand(BitstrMeta, Fun, [{'::', Meta, [Left, Right]} | T], Acc, E, Alignment, RequireSize) ->
{ELeft, {EL, OriginalE}} = expand_expr(Meta, Left, Fun, E),
- %% Variables defined outside the binary can be accounted
- %% on subparts, however we can't assign new variables.
- MatchSize =
- case EL of
- #{context := match} -> T /= [];
- _ -> false
- end,
-
- {EType, _} = expr_type(ELeft),
- {ERight, EAlignment, ES} = expand_specs(EType, Meta, Right, EL, OriginalE, RequireSize or MatchSize),
- EE = {ES, OriginalE},
-
- EAcc =
- %% If the Etype is a bitstring (which implies a literal <<>>)
- %% and we have no further modifiers other than binary or bitstring,
- %% we can attempt to merge the inner <<>> into the outer one.
- case ERight of
- {binary, _, []} when EType == bitstring ->
- case byte_parts(ELeft) of
- {ok, Parts} -> lists:reverse(Parts, Acc);
- error -> prepend_unless_bitstring_in_match(EType, Meta, ELeft, ERight, Acc, OriginalE)
- end;
- {bitstring, _, []} when EType == bitstring ->
- lists:reverse(element(3, ELeft), Acc);
- _ ->
- prepend_unless_bitstring_in_match(EType, Meta, ELeft, ERight, Acc, OriginalE)
- end,
-
- expand(BitstrMeta, Fun, T, EAcc, EE, alignment(Alignment, EAlignment), RequireSize);
-expand(BitstrMeta, Fun, [{'<<>>', _Meta, Parts} | T], Acc, E, Alignment, RequireSize) ->
- expand(BitstrMeta, Fun, Parts ++ T, Acc, E, Alignment, RequireSize);
-expand(BitstrMeta, Fun, [{_, Meta, _} = H | T], Acc, E, Alignment, RequireSize) ->
- {Expr, ES} = expand_expr(Meta, H, Fun, E),
- expand(BitstrMeta, Fun, T, [infer_expr(Expr) | Acc], ES, Alignment, RequireSize);
-expand(Meta, Fun, [H | T], Acc, E, Alignment, RequireSize) ->
- {Expr, ES} = expand_expr(Meta, H, Fun, E),
- expand(Meta, Fun, T, [infer_expr(Expr) | Acc], ES, Alignment, RequireSize).
-
-prepend_unless_bitstring_in_match(Type, Meta, Left, Right, Acc, E) ->
- Expr = {'::', Meta, [Left, Right]},
-
+ MatchOrRequireSize = RequireSize or is_match_size(T, EL),
+ EType = expr_type(ELeft),
+ {ERight, EAlignment, ES} = expand_specs(EType, Meta, Right, EL, OriginalE, MatchOrRequireSize),
+
+ EAcc = concat_or_prepend_bitstring(Meta, ELeft, ERight, Acc, ES, MatchOrRequireSize),
+ expand(BitstrMeta, Fun, T, EAcc, {ES, OriginalE}, alignment(Alignment, EAlignment), RequireSize);
+expand(BitstrMeta, Fun, [H | T], Acc, E, Alignment, RequireSize) ->
+ Meta = extract_meta(H, BitstrMeta),
+ {ELeft, {ES, OriginalE}} = expand_expr(Meta, H, Fun, E),
+
+ MatchOrRequireSize = RequireSize or is_match_size(T, ES),
+ EType = expr_type(ELeft),
+ ERight = infer_spec(EType, Meta),
+
+ InferredMeta = [{inferred_bitstring_spec, true} | Meta],
+ EAcc = concat_or_prepend_bitstring(InferredMeta, ELeft, ERight, Acc, ES, MatchOrRequireSize),
+ expand(Meta, Fun, T, EAcc, {ES, OriginalE}, Alignment, RequireSize).
+
+extract_meta({_, Meta, _}, _) -> Meta;
+extract_meta(_, Meta) -> Meta.
+
+%% Variables defined outside the binary can be accounted
+%% on subparts, however we can't assign new variables.
+is_match_size([_ | _], #{context := match}) -> true;
+is_match_size(_, _) -> false.
+
+expr_type(Integer) when is_integer(Integer) -> integer;
+expr_type(Float) when is_float(Float) -> float;
+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, []}.
+
+concat_or_prepend_bitstring(_Meta, {'<<>>', _, []}, _ERight, Acc, _E, _RequireSize) ->
+ Acc;
+concat_or_prepend_bitstring(Meta, {'<<>>', PartsMeta, Parts} = ELeft, ERight, Acc, E, RequireSize) ->
case E of
- #{context := match} when Type == bitstring ->
- form_error(Meta, E, ?MODULE, {unaligned_bitstring_in_match, Expr});
- #{} ->
- [Expr | Acc]
- end.
+ #{context := match} when RequireSize ->
+ case lists:last(Parts) of
+ {'::', SpecMeta, [Bin, {binary, _, []}]} when not is_binary(Bin) ->
+ form_error(SpecMeta, E, ?MODULE, unsized_binary);
-byte_parts({'<<>>', Meta, Parts}) ->
- case lists:keyfind(alignment, 1, Meta) of
- {alignment, 0} -> {ok, Parts};
- _ -> error
- end.
+ {'::', SpecMeta, [_, {bitstring, _, []}]} ->
+ form_error(SpecMeta, E, ?MODULE, unsized_binary);
-infer_expr(Expr) ->
- case expr_type(Expr) of
- {binary, Meta} ->
- {'::', [{inferred_binary_type, true} | Meta], [Expr, {binary, Meta, []}]};
- {float, Meta} ->
- {'::', [{inferred_binary_type, true} | Meta], [Expr, {float, Meta, []}]};
- {integer, Meta} ->
- {'::', [{inferred_binary_type, true} | Meta], [Expr, {integer, Meta, []}]};
- {default, Meta} ->
- {'::', [{inferred_binary_type, true} | Meta], [Expr, {integer, Meta, []}]}
- end.
+ _ ->
+ ok
+ end;
+ _ ->
+ ok
+ end,
-expr_type(Integer) when is_integer(Integer) -> {integer, []};
-expr_type(Float) when is_float(Float) -> {float, []};
-expr_type(Binary) when is_binary(Binary) -> {binary, []};
-expr_type({'<<>>', Meta, _}) -> {bitstring, Meta};
-expr_type({_, Meta, _}) -> {default, Meta};
-expr_type(_) -> {default, []}.
+ case ERight of
+ {binary, _, []} ->
+ {alignment, Alignment} = lists:keyfind(alignment, 1, PartsMeta),
+
+ if
+ Alignment == 0 ->
+ lists:reverse(Parts, Acc);
+
+ is_integer(Alignment) ->
+ form_error(Meta, E, ?MODULE, {unaligned_binary, ELeft});
+
+ true ->
+ [{'::', Meta, [ELeft, ERight]} | Acc]
+ end;
+ {bitstring, _, []} ->
+ lists:reverse(Parts, Acc)
+ end;
+concat_or_prepend_bitstring(Meta, ELeft, ERight, Acc, _E, _RequireSize) ->
+ [{'::', Meta, [ELeft, ERight]} | Acc].
%% Handling of alignment
@@ -342,23 +346,19 @@ find_match([_Arg | Rest]) ->
find_match([]) ->
false.
-format_error({unaligned_bitstring_in_match, Expr}) ->
- Message =
- "cannot verify size of binary expression in match. "
- "If you are concatenating two binaries or nesting a binary inside a bitstring, "
- "you need to make sure the size of all fields in the binary expression are known. "
- "The following examples are invalid:\n\n"
- " \"foo\" <> <<field, rest::bits>>\n"
- " <<\"foo\", <<field, rest::bitstring>>::binary>>\n\n"
- "They are invalid because there is a bits/bitstring component of unknown size given "
- "as argument. Those examples could be fixed as:\n\n"
- " \"foo\" <> <<field, rest::binary>>\n"
- " <<\"foo\", <<field, rest::bitstring>>::bitstring>>\n\n"
- "Got: ~ts",
+format_error({unaligned_binary, Expr}) ->
+ Message = "expected ~ts to be a binary but its number of bits is not divisible by 8",
io_lib:format(Message, ['Elixir.Macro':to_string(Expr)]);
format_error(unsized_binary) ->
- "a binary field without size is only allowed at the end of a binary pattern "
- "and never allowed in binary generators";
+ "a binary field without size is only allowed at the end of a binary pattern, "
+ "at the right side of binary concatenation and and never allowed in binary generators. "
+ "The following examples are invalid:\n\n"
+ " rest <> \"foo\"\n"
+ " <<rest::binary, \"foo\">>\n\n"
+ "They are invalid because there is a bits/bitstring component not at the end. "
+ "However, the \"reverse\" would work:\n\n"
+ " \"foo\" <> rest\n"
+ " <<\"foo\", rest::binary>>\n\n";
format_error(bittype_literal_bitstring) ->
"literal <<>> in bitstring supports only type specifiers, which must be one of: "
"binary or bitstring";
diff --git a/lib/elixir/test/elixir/kernel/binary_test.exs b/lib/elixir/test/elixir/kernel/binary_test.exs
index f533d1de7..376ca1086 100644
--- a/lib/elixir/test/elixir/kernel/binary_test.exs
+++ b/lib/elixir/test/elixir/kernel/binary_test.exs
@@ -199,10 +199,6 @@ defmodule Kernel.BinaryTest do
assert_raise CompileError, message, fn ->
Code.eval_string(~s[<<'foo'::binary>>])
end
-
- assert_raise ArgumentError, fn ->
- Code.eval_string(~s[<<1::4>> <> "foo"])
- end
end
@bitstring <<"foo", 16::4>>
diff --git a/lib/elixir/test/elixir/kernel/expansion_test.exs b/lib/elixir/test/elixir/kernel/expansion_test.exs
index 5dccba13e..de61d336b 100644
--- a/lib/elixir/test/elixir/kernel/expansion_test.exs
+++ b/lib/elixir/test/elixir/kernel/expansion_test.exs
@@ -2356,14 +2356,14 @@ defmodule Kernel.ExpansionTest do
end
test "raises on unaligned binaries in match" do
- message = ~r"cannot verify size of binary expression in match"
+ message = ~r"its number of bits is not divisible by 8"
assert_raise CompileError, message, fn ->
- expand(quote(do: <<rest::bits>> <> _ = "foo"))
+ expand(quote(do: <<rest::size(3)>> <> _ = "foo"))
end
assert_raise CompileError, message, fn ->
- expand(quote(do: <<rest::size(3)>> <> _ = "foo"))
+ expand(quote(do: <<1::4>> <> "foo"))
end
end
@@ -2493,6 +2493,10 @@ defmodule Kernel.ExpansionTest do
assert_raise CompileError, message, fn ->
expand(quote(do: <<(<<x::bitstring>>), y::bitstring>> = "foobar"))
end
+
+ assert_raise CompileError, message, fn ->
+ expand(quote(do: <<(<<x::bitstring>>)::bitstring, y::bitstring>> = "foobar"))
+ end
end
end
@@ -2623,7 +2627,7 @@ defmodule Kernel.ExpansionTest do
end)
receive do
- {:expand_env, {expr, env}} -> {clean_meta(expr, [:version, :inferred_binary_type]), env}
+ {:expand_env, {expr, env}} -> {clean_meta(expr, [:version, :inferred_bitstring_spec]), env}
end
end
end