@@ -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