Skip to content

Commit 9231515

Browse files
authored
Bdd negation and difference optimizations (#15287)
1 parent d5e032e commit 9231515

File tree

1 file changed

+28
-3
lines changed

1 file changed

+28
-3
lines changed

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

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5693,6 +5693,17 @@ defmodule Module.Types.Descr do
56935693
defp tuple_insert_static(descr, index, type) do
56945694
Map.update!(descr, :tuple, fn bdd ->
56955695
bdd_map(bdd, fn {tag, elements} ->
5696+
# If the tuple is open, then we want List.insert_at to put the new element at the correct
5697+
# index, which requires filling the tuple with `term()` values first.
5698+
# Closed tuples of an incorrect size will be ignored (they are cancelled by the earlier
5699+
# intersection with `tuple_of_size_at_least`).
5700+
elements =
5701+
if tag == :open and length(elements) < index do
5702+
tuple_fill(elements, index)
5703+
else
5704+
elements
5705+
end
5706+
56965707
{tag, List.insert_at(elements, index, type)}
56975708
end)
56985709
end)
@@ -5812,7 +5823,7 @@ defmodule Module.Types.Descr do
58125823
end
58135824

58145825
{:eq, _, {lit, c2, u2, _d2}} ->
5815-
{lit, bdd_negation(bdd_union(c2, u2)), :bdd_bot, :bdd_bot}
5826+
{lit, bdd_negation_union(c2, u2), :bdd_bot, :bdd_bot}
58165827

58175828
{:eq, {lit, _c1, u1, d1}, _} ->
58185829
{lit, :bdd_bot, :bdd_bot, bdd_union(d1, u1)}
@@ -5834,6 +5845,12 @@ defmodule Module.Types.Descr do
58345845
defp bdd_difference_union(i, u1, u2),
58355846
do: bdd_difference(i, bdd_union(u1, u2))
58365847

5848+
# We avoid unions because they are lazy and we prune
5849+
# intersections more actively.
5850+
defp bdd_negation_union(u1, u2) do
5851+
bdd_intersection(bdd_negation(u1), bdd_negation(u2))
5852+
end
5853+
58375854
## Optimize differences
58385855

58395856
defp bdd_difference(bdd_leaf(_, _) = a1, bdd_leaf(_, _) = a2, leaf_compare) do
@@ -6133,13 +6150,21 @@ defmodule Module.Types.Descr do
61336150
end
61346151
end
61356152

6136-
# Lazy negation: eliminate the union, then perform normal negation (switching leaves)
6153+
# {lit, c, u, d} = (lit and c) or u or (not lit and d), so
6154+
# its negation is ((lit and not c) or (not lit and not d)) and not u.
61376155
def bdd_negation(:bdd_top), do: :bdd_bot
61386156
def bdd_negation(:bdd_bot), do: :bdd_top
61396157
def bdd_negation({_, _} = pair), do: {pair, :bdd_bot, :bdd_bot, :bdd_top}
61406158

61416159
def bdd_negation({lit, c, u, d}) do
6142-
{lit, bdd_negation(bdd_union(c, u)), :bdd_bot, bdd_negation(bdd_union(d, u))}
6160+
inner =
6161+
{lit, bdd_negation(c), :bdd_bot, bdd_negation(d)}
6162+
6163+
case bdd_intersection(inner, bdd_negation(u)) do
6164+
# Full simplification necessary for e.g. formatter.ex compilation
6165+
{_lit, c, u, c} -> bdd_union(u, c)
6166+
x -> x
6167+
end
61436168
end
61446169

61456170
def bdd_to_dnf(bdd), do: bdd_to_dnf([], [], [], bdd)

0 commit comments

Comments
 (0)