@@ -2823,9 +2823,6 @@ defmodule Module.Types.Descr do
28232823 { fields_from_reverse_list ( fields ) , domain , dynamic? }
28242824 end
28252825
2826- defp tuple_tag_to_type ( :open ) , do: term_or_optional ( )
2827- defp tuple_tag_to_type ( :closed ) , do: not_set ( )
2828-
28292826 # Gets the default type associated to atom keys in a map.
28302827 defp map_key_tag_to_type ( :open ) , do: term_or_optional ( )
28312828 defp map_key_tag_to_type ( :closed ) , do: not_set ( )
@@ -5481,18 +5478,105 @@ defmodule Module.Types.Descr do
54815478 defp tuple_fetch_static ( descr , index ) when is_integer ( index ) do
54825479 case descr do
54835480 :term -> { true , term ( ) }
5484- % { tuple: tuple } -> tuple_get ( tuple , index ) |> pop_optional_static ( )
5481+ % { tuple: bdd_leaf ( tag , elements ) } -> tuple_fetch_element ( elements , index , tag )
5482+ % { tuple: bdd } -> tuple_bdd_fetch_static ( bdd , index )
54855483 % { } -> { false , none ( ) }
54865484 end
54875485 end
54885486
5489- defp tuple_get ( bdd , index ) do
5490- tuple_bdd_to_dnf_no_negations ( bdd )
5491- |> Enum . reduce ( none ( ) , fn
5492- { tag , elements } , acc -> Enum . at ( elements , index , tuple_tag_to_type ( tag ) ) |> union ( acc )
5487+ defp tuple_bdd_fetch_static ( bdd , index ) do
5488+ bdd
5489+ |> tuple_bdd_to_dnf_with_negations ( )
5490+ |> Enum . reduce ( { false , none ( ) } , fn
5491+ # Optimization: if there are no negatives
5492+ { tag , elements , [ ] } , { acc_optional? , acc_descr } ->
5493+ { optional? , descr } = tuple_fetch_element ( elements , index , tag )
5494+ { optional? or acc_optional? , union ( descr , acc_descr ) }
5495+
5496+ { tag , elements , negs } , acc ->
5497+ { _ , value , bdd } = tuple_take_element ( elements , index , tag )
5498+
5499+ negs
5500+ |> tuple_split_negative ( index , value , bdd )
5501+ |> Enum . reduce ( acc , fn { value , _ } , { acc_optional? , acc_descr } ->
5502+ { optional? , descr } = pop_optional_static ( value )
5503+ { optional? or acc_optional? , union ( descr , acc_descr ) }
5504+ end )
54935505 end )
5506+ catch
5507+ :open -> { true , term ( ) }
54945508 end
54955509
5510+ # Remove negatives:
5511+ # {t, s} \ {t₁, s₁} = {t \ t₁, s} ∪ {t ∩ t₁, s \ s₁}
5512+ defp tuple_split_negative ( negs , index , value , bdd ) do
5513+ Enum . reduce ( negs , [ { value , bdd } ] , fn
5514+ { :open , [ ] } , _acc ->
5515+ throw ( :empty )
5516+
5517+ { neg_tag , neg_elements } , acc ->
5518+ { found? , neg_value , neg_bdd } = tuple_take_element ( neg_elements , index , neg_tag )
5519+
5520+ if not found? and neg_tag == :open do
5521+ # In case the tuple is open, t \ t₁ is always empty,
5522+ # t ∩ t₁ is always t, so we just need to deal with the bdd.
5523+ Enum . reduce ( acc , [ ] , fn { value , bdd } , acc ->
5524+ diff_bdd = tuple_difference ( bdd , neg_bdd )
5525+
5526+ if tuple_empty? ( diff_bdd ) do
5527+ acc
5528+ else
5529+ [ { value , diff_bdd } | acc ]
5530+ end
5531+ end )
5532+ else
5533+ Enum . reduce ( acc , [ ] , fn { value , bdd } , acc ->
5534+ # If the negative tag is closed, then they are likely disjoint,
5535+ # so we can drastically cut down the amount of operations.
5536+ if neg_tag == :closed and tuple_empty? ( tuple_intersection ( bdd , neg_bdd ) ) do
5537+ [ { value , bdd } | acc ]
5538+ else
5539+ intersection_value = intersection ( value , neg_value )
5540+
5541+ if empty? ( intersection_value ) do
5542+ [ { value , bdd } | acc ]
5543+ else
5544+ diff_bdd = tuple_difference ( bdd , neg_bdd )
5545+
5546+ if tuple_empty? ( diff_bdd ) do
5547+ prepend_pair_unless_empty_diff ( value , neg_value , bdd , acc )
5548+ else
5549+ acc = [ { intersection_value , diff_bdd } | acc ]
5550+ prepend_pair_unless_empty_diff ( value , neg_value , bdd , acc )
5551+ end
5552+ end
5553+ end
5554+ end )
5555+ end
5556+ end )
5557+ catch
5558+ :empty -> [ ]
5559+ end
5560+
5561+ defp tuple_fetch_element ( [ ] , _ , :open ) , do: { true , term ( ) }
5562+ defp tuple_fetch_element ( [ ] , _ , :closed ) , do: { true , none ( ) }
5563+ defp tuple_fetch_element ( [ h | _ ] , 0 , _tag ) , do: { false , h }
5564+ defp tuple_fetch_element ( [ _ | t ] , i , tag ) , do: tuple_fetch_element ( t , i - 1 , tag )
5565+
5566+ defp tuple_take_element ( elements , index , tag ) do
5567+ case do_tuple_take_element ( elements , index , [ ] ) do
5568+ :error -> { false , tuple_tag_to_type ( tag ) , tuple_new ( tag , elements ) }
5569+ { value , elements } -> { true , value , tuple_new ( tag , elements ) }
5570+ end
5571+ end
5572+
5573+ defp do_tuple_take_element ( [ ] , _ , _ ) , do: :error
5574+ defp do_tuple_take_element ( [ h | t ] , 0 , acc ) , do: { h , Enum . reverse ( acc , t ) }
5575+ defp do_tuple_take_element ( [ h | t ] , i , acc ) , do: do_tuple_take_element ( t , i - 1 , [ h | acc ] )
5576+
5577+ defp tuple_tag_to_type ( :open ) , do: term_or_optional ( )
5578+ defp tuple_tag_to_type ( :closed ) , do: none ( )
5579+
54965580 @ doc """
54975581 Returns all of the values that are part of a tuple.
54985582 """
0 commit comments