Skip to content

Commit e3c74e1

Browse files
committed
Simplify tuple union implementation
1 parent c184b82 commit e3c74e1

1 file changed

Lines changed: 58 additions & 74 deletions

File tree

lib/elixir/lib/module/types/descr.ex

Lines changed: 58 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -2260,14 +2260,14 @@ defmodule Module.Types.Descr do
22602260
do: :bdd_bot,
22612261
else: bdd_leaf(list1, difference(last1, last2))
22622262
else
2263-
bdd_difference(bdd1, bdd2, &list_leaf_compare/2)
2263+
bdd_difference(bdd1, bdd2, &list_leaf_difference/2)
22642264
end
22652265
end
22662266

22672267
defp list_difference(bdd1, bdd2),
2268-
do: bdd_difference(bdd1, bdd2, &list_leaf_compare/2)
2268+
do: bdd_difference(bdd1, bdd2, &list_leaf_difference/2)
22692269

2270-
defp list_leaf_compare(bdd_leaf(list1, last1), bdd_leaf(list2, last2)) do
2270+
defp list_leaf_difference(bdd_leaf(list1, last1), bdd_leaf(list2, last2)) do
22712271
if disjoint?(list1, list2) or disjoint?(last1, last2) do
22722272
:disjoint
22732273
else
@@ -3009,18 +3009,18 @@ defmodule Module.Types.Descr do
30093009
end
30103010

30113011
_ ->
3012-
bdd_difference(map1, map2, &map_leaf_compare/2)
3012+
bdd_difference(map1, map2, &map_leaf_difference/2)
30133013
end
30143014
end
30153015

30163016
defp map_difference(bdd_leaf(:open, []), bdd2),
30173017
do: bdd_negation(bdd2)
30183018

30193019
defp map_difference(bdd1, bdd2) do
3020-
bdd_difference(bdd1, bdd2, &map_leaf_compare/2)
3020+
bdd_difference(bdd1, bdd2, &map_leaf_difference/2)
30213021
end
30223022

3023-
defp map_leaf_compare(bdd_leaf(tag, fields), bdd_leaf(neg_tag, neg_fields)) do
3023+
defp map_leaf_difference(bdd_leaf(tag, fields), bdd_leaf(neg_tag, neg_fields)) do
30243024
case map_difference_strategy(fields, neg_fields, tag, neg_tag, false) do
30253025
:disjoint -> :disjoint
30263026
:left_subtype_of_right -> :subtype
@@ -4847,10 +4847,9 @@ defmodule Module.Types.Descr do
48474847
end
48484848

48494849
defp tuple_sizes_strategy(:closed, n1, :closed, n2) when n1 != n2, do: :disjoint
4850-
defp tuple_sizes_strategy(:closed, n1, _, n2) when n1 == n2, do: :maybe_subtype
4850+
defp tuple_sizes_strategy(:closed, n1, :closed, n2) when n1 == n2, do: :left_subtype_of_right
48514851
defp tuple_sizes_strategy(:closed, n1, :open, n2) when n1 < n2, do: :disjoint
4852-
defp tuple_sizes_strategy(_, n1, :open, n2) when n1 == n2, do: :maybe_subtype
4853-
defp tuple_sizes_strategy(_, n1, :open, n2) when n1 > n2, do: :maybe_subtype
4852+
defp tuple_sizes_strategy(_, n1, :open, n2) when n1 >= n2, do: :left_subtype_of_right
48544853
defp tuple_sizes_strategy(:open, n1, :closed, n2) when n1 > n2, do: :disjoint
48554854
defp tuple_sizes_strategy(_, _, _, _), do: :none
48564855

@@ -4879,24 +4878,24 @@ defmodule Module.Types.Descr do
48794878
do: bdd_negation(bdd2)
48804879

48814880
defp tuple_difference(bdd1, bdd2),
4882-
do: bdd_difference(bdd1, bdd2, &tuple_leaf_compare/2)
4881+
do: bdd_difference(bdd1, bdd2, &tuple_leaf_difference/2)
48834882

4884-
defp tuple_leaf_compare(bdd_leaf(tag1, elements1), bdd_leaf(tag2, elements2)) do
4883+
defp tuple_leaf_difference(bdd_leaf(tag1, elements1), bdd_leaf(tag2, elements2)) do
48854884
case tuple_sizes_strategy(tag1, length(elements1), tag2, length(elements2)) do
48864885
:disjoint -> :disjoint
4887-
other -> tuple_leaf_compare(elements1, elements2, other == :maybe_subtype)
4886+
other -> tuple_leaf_difference(elements1, elements2, other == :left_subtype_of_right)
48884887
end
48894888
end
48904889

4891-
defp tuple_leaf_compare([head1 | tail1], [head2 | tail2], subtype?) do
4890+
defp tuple_leaf_difference([head1 | tail1], [head2 | tail2], subtype?) do
48924891
cond do
48934892
disjoint?(head1, head2) -> :disjoint
4894-
subtype? and subtype?(head1, head2) -> tuple_leaf_compare(tail1, tail2, subtype?)
4893+
subtype? and subtype?(head1, head2) -> tuple_leaf_difference(tail1, tail2, subtype?)
48954894
true -> :none
48964895
end
48974896
end
48984897

4899-
defp tuple_leaf_compare(_tail1, _tail2, subtype?) do
4898+
defp tuple_leaf_difference(_tail1, _tail2, subtype?) do
49004899
if subtype?, do: :subtype, else: :none
49014900
end
49024901

@@ -4996,8 +4995,8 @@ defmodule Module.Types.Descr do
49964995
[{tag, elements}]
49974996
else
49984997
tuple_dnf_union(
4999-
tuple_elim_content([], tag, elements, neg_elements),
5000-
tuple_elim_size(n, m, tag, elements, neg_tag)
4998+
tuple_elim_size(n, m, tag, elements, neg_tag),
4999+
tuple_elim_content([], tag, elements, neg_elements)
50015000
)
50025001
end
50035002
end
@@ -5077,17 +5076,15 @@ defmodule Module.Types.Descr do
50775076
)
50785077
end
50795078

5079+
# Prefer the smaller on the left
50805080
defp tuple_dnf_union(dnf1, dnf2) do
5081-
# Union of tuple DNFs is just concatenation, but we rely on some optimization strategies to
5082-
# avoid the list to grow when possible
5083-
5084-
# first pass trying to identify patterns where two maps can be fused as one
5081+
# Union of tuple DNFs is just concatenation,
5082+
# but we do our best to remove duplicates.
50855083
with [tuple1] <- dnf1,
50865084
[tuple2] <- dnf2,
50875085
optimized when optimized != nil <- maybe_optimize_tuple_union(tuple1, tuple2) do
50885086
[optimized]
50895087
else
5090-
# otherwise we just concatenate and remove structural duplicates
50915088
_ -> dnf1 ++ (dnf2 -- dnf1)
50925089
end
50935090
end
@@ -5106,7 +5103,7 @@ defmodule Module.Types.Descr do
51065103
defp tuple_union(bdd1, bdd2), do: bdd_union(bdd1, bdd2)
51075104

51085105
defp maybe_optimize_tuple_union({tag1, pos1} = tuple1, {tag2, pos2} = tuple2) do
5109-
case tuple_union_optimization_strategy(tag1, pos1, tag2, pos2) do
5106+
case tuple_union_strategy(tag1, pos1, tag2, pos2) do
51105107
:all_equal ->
51115108
tuple1
51125109

@@ -5120,75 +5117,62 @@ defmodule Module.Types.Descr do
51205117
:right_subtype_of_left ->
51215118
tuple1
51225119

5123-
nil ->
5120+
:none ->
51245121
nil
51255122
end
51265123
end
51275124

5128-
defp maybe_optimize_tuple_union(_, _), do: nil
5129-
5130-
defp tuple_union_optimization_strategy(tag1, pos1, tag2, pos2)
5131-
defp tuple_union_optimization_strategy(tag, pos, tag, pos), do: :all_equal
5125+
defp tuple_union_strategy(tag1, pos1, tag2, pos2) do
5126+
case {tag1, tag2} do
5127+
{tag, tag} when length(pos1) == length(pos2) ->
5128+
tuple_union_strategy_index(pos1, pos2, 0, :all_equal)
51325129

5133-
# might be one extra loop but cheap and avoids doing deep subtype comparisons
5134-
defp tuple_union_optimization_strategy(:closed, pos1, :closed, pos2)
5135-
when length(pos1) != length(pos2),
5136-
do: nil
5130+
{:open, _} when length(pos1) <= length(pos2) ->
5131+
tuple_union_strategy_index(pos1, pos2, 0, :right_subtype_of_left)
51375132

5138-
defp tuple_union_optimization_strategy(tag1, pos1, tag2, pos2) do
5139-
status =
5140-
case {tag1, tag2} do
5141-
{:open, :closed} -> :right_subtype_of_left
5142-
{:closed, :open} -> :left_subtype_of_right
5143-
{same, same} -> :all_equal
5144-
end
5133+
{_, :open} when length(pos1) >= length(pos2) ->
5134+
tuple_union_strategy_index(pos1, pos2, 0, :left_subtype_of_right)
51455135

5146-
do_tuple_union_optimization_strategy(tag1, pos1, tag2, pos2, 0, status)
5136+
{_, _} ->
5137+
:none
5138+
end
51475139
end
51485140

5149-
defp do_tuple_union_optimization_strategy(_tag1, [], _tag2, [], _i, status), do: status
5150-
5151-
defp do_tuple_union_optimization_strategy(:open, [], _tag2, _pos2, _i, status)
5152-
when status in [:all_equal, :right_subtype_of_left],
5153-
do: :right_subtype_of_left
5154-
5155-
defp do_tuple_union_optimization_strategy(_tag1, _pos1, :open, [], _i, status)
5156-
when status in [:all_equal, :left_subtype_of_right],
5157-
do: :left_subtype_of_right
5158-
5159-
defp do_tuple_union_optimization_strategy(tag1, [v1 | pos1], tag2, [v2 | pos2], i, status) do
5160-
if next_status = tuple_union_next_strategy(i, v1, v2, status) do
5161-
do_tuple_union_optimization_strategy(tag1, pos1, tag2, pos2, i + 1, next_status)
5162-
end
5141+
defp tuple_union_strategy_index([v | pos1], [v | pos2], i, status) do
5142+
tuple_union_strategy_index(pos1, pos2, i + 1, status)
51635143
end
51645144

5165-
defp do_tuple_union_optimization_strategy(_tag1, _pos1, _tag2, _pos2, _i, _status), do: nil
5145+
defp tuple_union_strategy_index([v1 | pos1], [v2 | pos2], i, status) do
5146+
case status do
5147+
:all_equal ->
5148+
tuple_union_strategy_index(pos1, pos2, i + 1, {:one_index_difference, i, v1, v2})
5149+
5150+
{:one_index_difference, _, d1, d2} ->
5151+
cond do
5152+
subtype?(d1, d2) and subtype?(v1, v2) ->
5153+
tuple_union_strategy_index(pos1, pos2, i + 1, :left_subtype_of_right)
51665154

5167-
defp tuple_union_next_strategy(index, v1, v2, status)
5155+
subtype?(d2, d1) and subtype?(v2, v1) ->
5156+
tuple_union_strategy_index(pos1, pos2, i + 1, :right_subtype_of_left)
51685157

5169-
# structurally equal values do not impact the ongoing strategy
5170-
defp tuple_union_next_strategy(_index, same, same, status), do: status
5158+
true ->
5159+
:none
5160+
end
51715161

5172-
defp tuple_union_next_strategy(index, v1, v2, :all_equal) do
5173-
{:one_index_difference, index, v1, v2}
5174-
end
5162+
:left_subtype_of_right ->
5163+
if subtype?(v1, v2),
5164+
do: tuple_union_strategy_index(pos1, pos2, i + 1, :left_subtype_of_right),
5165+
else: :none
51755166

5176-
defp tuple_union_next_strategy(_index, v1, v2, {:one_index_difference, _, d1, d2}) do
5177-
# we have at least two differences now, we switch strategy
5178-
# if both are subtypes in one direction, keep checking
5179-
cond do
5180-
subtype?(d1, d2) and subtype?(v1, v2) -> :left_subtype_of_right
5181-
subtype?(d2, d1) and subtype?(v2, v1) -> :right_subtype_of_left
5182-
true -> nil
5167+
:right_subtype_of_left ->
5168+
if subtype?(v2, v1),
5169+
do: tuple_union_strategy_index(pos1, pos2, i + 1, :right_subtype_of_left),
5170+
else: :none
51835171
end
51845172
end
51855173

5186-
defp tuple_union_next_strategy(_index, v1, v2, :left_subtype_of_right) do
5187-
if subtype?(v1, v2), do: :left_subtype_of_right
5188-
end
5189-
5190-
defp tuple_union_next_strategy(_index, v1, v2, :right_subtype_of_left) do
5191-
if subtype?(v2, v1), do: :right_subtype_of_left
5174+
defp tuple_union_strategy_index(_pos1, _pos2, _i, status) do
5175+
status
51925176
end
51935177

51945178
defp tuple_to_quoted(bdd, opts) do

0 commit comments

Comments
 (0)