Skip to content

Commit 47b762b

Browse files
committed
Track reused variables in bitstring patterns
1 parent b48221d commit 47b762b

3 files changed

Lines changed: 71 additions & 19 deletions

File tree

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

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -549,30 +549,47 @@ 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([], _kind, _stack, context) do
553-
{binary(), context}
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}
554567
end
555568

556-
def bitstring([head], kind, stack, context) do
557-
{alignment, context} = bitstring_segment(head, kind, [head], stack, context)
558-
{alignment_to_type(alignment), context}
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}
559572
end
560573

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)
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)
564579
end
565580

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}
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}
569586
end
570587

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)
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)
574591

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

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

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+
585608
# If the segment is a literal, the compiler has already checked its validity,
586609
# so we just check the size.
587-
defp bitstring_segment({:"::", _meta, [left, right]}, kind, _args, stack, context)
610+
defp do_bitstring_segment({:"::", _meta, [left, right]}, kind, _args, stack, context)
588611
when is_binary(left) or is_number(left) do
589612
{_type, alignment_type} = specifier_type(kind, right)
590613
{alignment_value, context} = specifier_size(kind, right, stack, {:default, context})
@@ -599,7 +622,7 @@ defmodule Module.Types.Of do
599622
end
600623
end
601624

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

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

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

787787
# <<...>>>
788788
defp of_pattern({:<<>>, _meta, args} = node, _path, stack, context) do
789-
{type, context} = Of.bitstring(args, :match, stack, context)
790-
{type, of_precise_bitstring?(node), context}
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}
791795
end
792796

793797
# left ++ right
@@ -987,6 +991,27 @@ defmodule Module.Types.Pattern do
987991
{type, precise?, put_in(context.subpatterns[key], of_pattern_tree(type, stack, context))}
988992
end
989993

994+
defp reused_bitstring_segment?({:"::", _, [expr, _type]}, vars) do
995+
reused_bitstring_expr?(expr, vars)
996+
end
997+
998+
defp reused_bitstring_segment?(_segment, _vars) do
999+
false
1000+
end
1001+
1002+
defp reused_bitstring_expr?({_, meta, _} = var, vars) when is_var(var) do
1003+
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+
9901015
defp of_precise_bitstring?({:<<>>, _meta, [{:"::", _, [expr, {type, _, _}]}]})
9911016
when type in [:binary, :bitstring, :bytes, :bits] do
9921017
is_var(expr) or of_precise_bitstring?(expr)

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1376,6 +1376,10 @@ defmodule Module.Types.PatternTest do
13761376
refute precise?([<<_::binary-size(8)>>])
13771377
refute precise?([<<_::bitstring-size(8)>>])
13781378
refute precise?([<<(<<123>>)::bits>>])
1379+
refute precise?([x, <<x::binary>>])
1380+
refute precise?([x, <<x>>])
1381+
refute precise?([x, <<y, x>>])
1382+
refute precise?([x, <<(<<x::binary>>)::binary>>])
13791383
end
13801384

13811385
test "tuples in patterns" do

0 commit comments

Comments
 (0)