Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 28 additions & 3 deletions lib/elixir/lib/module/types/descr.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5749,6 +5749,17 @@ defmodule Module.Types.Descr do
defp tuple_insert_static(descr, index, type) do
Map.update!(descr, :tuple, fn bdd ->
bdd_map(bdd, fn {tag, elements} ->
# If the tuple is open, then we want List.insert_at to put the new element at the correct
# index, which requires filling the tuple with `term()` values first.
# Closed tuples of an incorrect size will be ignored (they are cancelled by the earlier
# intersection with `tuple_of_size_at_least`).
elements =
Comment thread
josevalim marked this conversation as resolved.
if tag == :open and length(elements) < index do
tuple_fill(elements, index)
else
elements
end

{tag, List.insert_at(elements, index, type)}
end)
end)
Expand Down Expand Up @@ -5868,7 +5879,7 @@ defmodule Module.Types.Descr do
end

{:eq, _, {lit, c2, u2, _d2}} ->
{lit, bdd_negation(bdd_union(c2, u2)), :bdd_bot, :bdd_bot}
{lit, bdd_negation_union(c2, u2), :bdd_bot, :bdd_bot}

{:eq, {lit, _c1, u1, d1}, _} ->
{lit, :bdd_bot, :bdd_bot, bdd_union(d1, u1)}
Expand All @@ -5890,6 +5901,12 @@ defmodule Module.Types.Descr do
defp bdd_difference_union(i, u1, u2),
do: bdd_difference(i, bdd_union(u1, u2))

# We avoid unions because they are lazy and we prune
# intersections more actively.
defp bdd_negation_union(u1, u2) do
bdd_intersection(bdd_negation(u1), bdd_negation(u2))
end
Comment thread
josevalim marked this conversation as resolved.

## Optimize differences

defp bdd_difference(bdd_leaf(_, _) = a1, bdd_leaf(_, _) = a2, leaf_compare) do
Expand Down Expand Up @@ -6179,13 +6196,21 @@ defmodule Module.Types.Descr do
end
end

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

def bdd_negation({lit, c, u, d}) do
{lit, bdd_negation(bdd_union(c, u)), :bdd_bot, bdd_negation(bdd_union(d, u))}
inner =
{lit, bdd_negation(c), :bdd_bot, bdd_negation(d)}

case bdd_intersection(inner, bdd_negation(u)) do
# Full simplification necessary for e.g. formatter.ex compilation
{_lit, c, u, c} -> bdd_union(u, c)
x -> x
end
end

def bdd_to_dnf(bdd), do: bdd_to_dnf([], [], [], bdd)
Expand Down
Loading