Skip to content

Commit 09e1448

Browse files
committed
Simplify precision tracking in bitstrings
1 parent 502421c commit 09e1448

3 files changed

Lines changed: 31 additions & 67 deletions

File tree

lib/elixir/lib/module/types/of.ex

Lines changed: 17 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -549,47 +549,30 @@ defmodule Module.Types.Of do
549549
In the stack, we add nodes such as <<expr>>, <<..., expr>>, etc,
550550
based on the position of the expression within the binary.
551551
"""
552-
def bitstring(args, kind, stack, context) do
553-
{type, nil, context} =
554-
bitstring(args, kind, stack, context, nil, fn _segment, nil, context ->
555-
{nil, context}
556-
end)
557-
558-
{type, context}
559-
end
560-
561-
def bitstring(args, kind, stack, context, acc, fun) when is_function(fun, 3) do
562-
do_bitstring(args, kind, stack, context, acc, fun)
563-
end
564-
565-
defp do_bitstring([], _kind, _stack, context, acc, _fun) do
566-
{binary(), acc, context}
552+
def bitstring([], _kind, _stack, context) do
553+
{binary(), context}
567554
end
568555

569-
defp do_bitstring([head], kind, stack, context, acc, fun) do
570-
{alignment, acc, context} = bitstring_segment(head, kind, [head], stack, context, acc, fun)
571-
{alignment_to_type(alignment), acc, context}
556+
def bitstring([head], kind, stack, context) do
557+
{alignment, context} = bitstring_segment(head, kind, [head], stack, context)
558+
{alignment_to_type(alignment), context}
572559
end
573560

574-
defp do_bitstring([head | tail], kind, stack, context, acc, fun) do
575-
{alignment, acc, context} =
576-
bitstring_segment(head, kind, [head, @suffix], stack, context, acc, fun)
577-
578-
bitstring_tail(tail, alignment, kind, stack, context, acc, fun)
561+
def bitstring([head | tail], kind, stack, context) do
562+
{alignment, context} = bitstring_segment(head, kind, [head, @suffix], stack, context)
563+
bitstring_tail(tail, alignment, kind, stack, context)
579564
end
580565

581-
defp bitstring_tail([last], alignment, kind, stack, context, acc, fun) do
582-
{seg_alignment, acc, context} =
583-
bitstring_segment(last, kind, [@prefix, last], stack, context, acc, fun)
584-
585-
{alignment_to_type(alignment(seg_alignment, alignment)), acc, context}
566+
defp bitstring_tail([last], alignment, kind, stack, context) do
567+
{seg_alignment, context} = bitstring_segment(last, kind, [@prefix, last], stack, context)
568+
{alignment_to_type(alignment(seg_alignment, alignment)), context}
586569
end
587570

588-
defp bitstring_tail([head | tail], alignment, kind, stack, context, acc, fun) do
589-
{seg_alignment, acc, context} =
590-
bitstring_segment(head, kind, [@prefix, head, @suffix], stack, context, acc, fun)
571+
defp bitstring_tail([head | tail], alignment, kind, stack, context) do
572+
{seg_alignment, context} =
573+
bitstring_segment(head, kind, [@prefix, head, @suffix], stack, context)
591574

592-
bitstring_tail(tail, alignment(seg_alignment, alignment), kind, stack, context, acc, fun)
575+
bitstring_tail(tail, alignment(seg_alignment, alignment), kind, stack, context)
593576
end
594577

595578
defp alignment(left, right) when is_integer(left) and is_integer(right), do: left + right
@@ -599,15 +582,9 @@ defmodule Module.Types.Of do
599582
defp alignment_to_type(integer) when rem(integer, 8) == 0, do: binary()
600583
defp alignment_to_type(_integer), do: bitstring_no_binary()
601584

602-
defp bitstring_segment(segment, kind, args, stack, context, acc, fun) do
603-
{acc, context} = fun.(segment, acc, context)
604-
{alignment, context} = do_bitstring_segment(segment, kind, args, stack, context)
605-
{alignment, acc, context}
606-
end
607-
608585
# If the segment is a literal, the compiler has already checked its validity,
609586
# so we just check the size.
610-
defp do_bitstring_segment({:"::", _meta, [left, right]}, kind, _args, stack, context)
587+
defp bitstring_segment({:"::", _meta, [left, right]}, kind, _args, stack, context)
611588
when is_binary(left) or is_number(left) do
612589
{_type, alignment_type} = specifier_type(kind, right)
613590
{alignment_value, context} = specifier_size(kind, right, stack, {:default, context})
@@ -622,7 +599,7 @@ defmodule Module.Types.Of do
622599
end
623600
end
624601

625-
defp do_bitstring_segment({:"::", meta, [left, right]}, kind, args, stack, context) do
602+
defp bitstring_segment({:"::", meta, [left, right]}, kind, args, stack, context) do
626603
{type, alignment_type} = specifier_type(kind, right)
627604
expr = {:<<>>, meta, args}
628605

lib/elixir/lib/module/types/pattern.ex

Lines changed: 10 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -786,12 +786,9 @@ defmodule Module.Types.Pattern do
786786

787787
# <<...>>>
788788
defp of_pattern({:<<>>, _meta, args} = node, _path, stack, context) do
789-
{type, reused?, context} =
790-
Of.bitstring(args, :match, stack, context, false, fn segment, reused?, context ->
791-
{reused? or reused_bitstring_segment?(segment, context.vars), context}
792-
end)
793-
794-
{type, of_precise_bitstring?(node) and not reused?, context}
789+
precise? = of_precise_bitstring?(node, context.vars)
790+
{type, context} = Of.bitstring(args, :match, stack, context)
791+
{type, precise?, context}
795792
end
796793

797794
# left ++ right
@@ -991,33 +988,19 @@ defmodule Module.Types.Pattern do
991988
{type, precise?, put_in(context.subpatterns[key], of_pattern_tree(type, stack, context))}
992989
end
993990

994-
defp reused_bitstring_segment?({:"::", _, [expr, _type]}, vars) do
995-
reused_bitstring_expr?(expr, vars)
991+
defp of_precise_bitstring?({:<<>>, _meta, [{:"::", _, [expr, {type, _, _}]}]}, vars)
992+
when type in [:binary, :bitstring, :bytes, :bits] do
993+
new_var?(expr, vars) or of_precise_bitstring?(expr, vars)
996994
end
997995

998-
defp reused_bitstring_segment?(_segment, _vars) do
999-
false
1000-
end
996+
defp of_precise_bitstring?(_expr, _vars), do: false
1001997

1002-
defp reused_bitstring_expr?({_, meta, _} = var, vars) when is_var(var) do
998+
defp new_var?({_, meta, _} = var, vars) when is_var(var) do
1003999
version = Keyword.fetch!(meta, :version)
1004-
is_map_key(vars, version)
1005-
end
1006-
1007-
defp reused_bitstring_expr?({:<<>>, _, args}, vars) do
1008-
Enum.any?(args, &reused_bitstring_segment?(&1, vars))
1009-
end
1010-
1011-
defp reused_bitstring_expr?(_expr, _vars) do
1012-
false
1013-
end
1014-
1015-
defp of_precise_bitstring?({:<<>>, _meta, [{:"::", _, [expr, {type, _, _}]}]})
1016-
when type in [:binary, :bitstring, :bytes, :bits] do
1017-
is_var(expr) or of_precise_bitstring?(expr)
1000+
not is_map_key(vars, version)
10181001
end
10191002

1020-
defp of_precise_bitstring?(_), do: false
1003+
defp new_var?(_expr, _vars), do: false
10211004

10221005
## Guards
10231006
#

lib/elixir/test/elixir/module/types/pattern_test.exs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1380,6 +1380,10 @@ defmodule Module.Types.PatternTest do
13801380
refute precise?([x, <<x>>])
13811381
refute precise?([x, <<y, x>>])
13821382
refute precise?([x, <<(<<x::binary>>)::binary>>])
1383+
refute precise?([<<x::binary>>, x])
1384+
refute precise?([<<x>>, x])
1385+
refute precise?([<<y, x>>, x])
1386+
refute precise?([<<(<<x::binary>>)::binary>>, x])
13831387
end
13841388

13851389
test "tuples in patterns" do

0 commit comments

Comments
 (0)