@@ -5286,100 +5286,93 @@ defmodule Module.Types.Descr do
52865286 end
52875287
52885288 defp tuple_bdd_fetch_static ( bdd , index ) do
5289- bdd
5290- |> tuple_bdd_to_dnf_with_negations ( )
5291- |> Enum . reduce ( { none ( ) , false } , fn
5292- # Optimization: if there are no negatives
5293- { tag , elements , [ ] } , { acc_descr , acc_optional? } ->
5294- { descr , optional? } = tuple_fetch_element ( elements , index , tag )
5295- { opt_union ( descr , acc_descr ) , optional? or acc_optional? }
5289+ { descr , optional? } =
5290+ bdd
5291+ |> tuple_bdd_to_dnf_with_negations ( )
5292+ |> Enum . reduce_while ( { none ( ) , false } , fn
5293+ # Optimization: if there are no negatives
5294+ { tag , elements , [ ] } , { acc_descr , acc_optional? } ->
5295+ { descr , optional? } = tuple_fetch_element ( elements , index , tag )
5296+
5297+ { opt_union ( descr , acc_descr ) , optional? or acc_optional? }
5298+ |> tuple_fetch_halt_if_saturated ( )
5299+
5300+ { tag , elements , negs } , { acc_descr , acc_optional? } = acc ->
5301+ result =
5302+ case tuple_take_element ( elements , index , tag ) do
5303+ :empty ->
5304+ acc
52965305
5297- { tag , elements , negs } , { acc_descr , acc_optional? } ->
5298- { _ , value , bdd } = tuple_take_element ( elements , index , tag )
5306+ { value , bdd } ->
5307+ negative = tuple_split_negative_pairs ( negs , index )
52995308
5300- case tuple_split_negative_pairs_index ( negs , index ) do
5301- :empty ->
5302- { acc_descr , acc_optional? }
5309+ value =
5310+ if tuple_pair_projection_keeps_full_fst? ( negative , bdd ) do
5311+ value
5312+ else
5313+ negative
5314+ |> tuple_project_negative_pairs ( value , bdd )
5315+ |> Enum . reduce ( none ( ) , fn { field , _ } , acc ->
5316+ opt_union ( field , acc )
5317+ end )
5318+ end
53035319
5304- negative ->
5305- value =
5306- if tuple_pair_projection_keeps_full_fst? ( negative , bdd ) do
5307- value
5308- else
5309- negs
5310- |> tuple_split_negative ( index , value , bdd )
5311- |> Enum . reduce ( { none ( ) , false } , fn { field , _ } , acc ->
5312- field_opt_union ( field , acc , % { } )
5313- end )
5314- end
5320+ { opt_union ( value , acc_descr ) , acc_optional? }
5321+ end
53155322
5316- { descr , optional? } = value
5317- { opt_union ( descr , acc_descr ) , optional? or acc_optional? }
5318- end
5319- end )
5320- catch
5321- :open -> { term ( ) , true }
5323+ tuple_fetch_halt_if_saturated ( result )
5324+ end )
5325+
5326+ { descr , optional? or not tuple_of_size_at_least_static? ( % { tuple: bdd } , index + 1 ) }
53225327 end
53235328
5324- # Remove negatives:
5325- # {t, s} \ {t₁, s₁} = {t \ t₁, s} ∪ {t ∩ t₁, s \ s₁}
5326- defp tuple_split_negative ( negs , index , value , bdd ) do
5327- Enum . reduce ( negs , [ { value , bdd } ] , fn
5328- bdd_leaf ( :open , [ ] ) , _acc ->
5329- throw ( :empty )
5329+ defp tuple_fetch_halt_if_saturated ( { :term , true } = result ) , do: { :halt , result }
5330+ defp tuple_fetch_halt_if_saturated ( result ) , do: { :cont , result }
53305331
5331- bdd_leaf ( neg_tag , neg_elements ) , acc ->
5332- { found? , neg_value , neg_bdd } = tuple_take_element ( neg_elements , index , neg_tag )
5332+ # Projects a difference of field/remainder products. The literals
5333+ # have been mapped through the inverse image of tuple insertion, so
5334+ # no optional-field cases remain.
5335+ # Removal formula is {t, s} \ {t₁, s₁} = {t \ t₁, s} ∪ {t ∩ t₁, s \ s₁}
5336+ defp tuple_project_negative_pairs ( negative , value , bdd ) do
5337+ Enum . reduce ( negative , [ { value , bdd } ] , fn { neg_value , neg_bdd } , acc ->
5338+ Enum . reduce ( acc , [ ] , fn { field , bdd } , acc ->
5339+ # If the negative tag is closed, then they are likely disjoint,
5340+ # so we can drastically cut down the amount of operations.
5341+ if match? ( bdd_leaf ( :closed , _ ) , neg_bdd ) and
5342+ tuple_empty? ( tuple_intersection ( bdd , neg_bdd ) , % { } ) do
5343+ [ { field , bdd } | acc ]
5344+ else
5345+ intersection_field = opt_intersection ( field , neg_value )
53335346
5334- if not found? and neg_tag == :open do
5335- # In case the tuple is open, t \ t₁ is always empty,
5336- # t ∩ t₁ is always t, so we just need to deal with the bdd.
5337- Enum . reduce ( acc , [ ] , fn { field , bdd } , acc ->
5347+ if empty? ( intersection_field ) do
5348+ [ { field , bdd } | acc ]
5349+ else
53385350 diff_bdd = tuple_difference ( bdd , neg_bdd )
53395351
53405352 if tuple_empty? ( diff_bdd , % { } ) do
5341- acc
5353+ prepend_tuple_pair_unless_empty_diff ( field , neg_value , bdd , acc )
53425354 else
5343- [ { field , diff_bdd } | acc ]
5355+ acc = [ { intersection_field , diff_bdd } | acc ]
5356+ prepend_tuple_pair_unless_empty_diff ( field , neg_value , bdd , acc )
53445357 end
5345- end )
5346- else
5347- Enum . reduce ( acc , [ ] , fn { field , bdd } , acc ->
5348- # If the negative tag is closed, then they are likely disjoint,
5349- # so we can drastically cut down the amount of operations.
5350- if neg_tag == :closed and tuple_empty? ( tuple_intersection ( bdd , neg_bdd ) , % { } ) do
5351- [ { field , bdd } | acc ]
5352- else
5353- intersection_field = field_intersection ( field , neg_value )
5354-
5355- if field_empty? ( intersection_field ) do
5356- [ { field , bdd } | acc ]
5357- else
5358- diff_bdd = tuple_difference ( bdd , neg_bdd )
5359-
5360- if tuple_empty? ( diff_bdd , % { } ) do
5361- prepend_map_pair_unless_empty_diff ( field , neg_value , bdd , acc )
5362- else
5363- acc = [ { intersection_field , diff_bdd } | acc ]
5364- prepend_map_pair_unless_empty_diff ( field , neg_value , bdd , acc )
5365- end
5366- end
5367- end
5368- end )
5358+ end
53695359 end
5360+ end )
53705361 end )
5371- catch
5372- :empty -> [ ]
53735362 end
53745363
5375- defp tuple_split_negative_pairs_index ( negs , index ) do
5376- Enum . reduce_while ( negs , [ ] , fn
5377- bdd_leaf ( :open , [ ] ) , _acc ->
5378- { :halt , :empty }
5364+ defp prepend_tuple_pair_unless_empty_diff ( value , neg_value , bdd , acc ) do
5365+ diff = opt_difference ( value , neg_value )
5366+ if empty? ( diff ) , do: acc , else: [ { diff , bdd } | acc ]
5367+ end
53795368
5380- bdd_leaf ( tag , elements ) , neg_acc ->
5381- { _found? , neg_value , neg_bdd } = tuple_take_element ( elements , index , tag )
5382- { :cont , [ { neg_value , neg_bdd } | neg_acc ] }
5369+ # Splits every negative tuple literal into its value/remainder pair at `index`.
5370+ defp tuple_split_negative_pairs ( negs , index ) do
5371+ Enum . reduce ( negs , [ ] , fn bdd_leaf ( tag , elements ) , acc ->
5372+ case tuple_take_element ( elements , index , tag ) do
5373+ :empty -> acc
5374+ pair -> [ pair | acc ]
5375+ end
53835376 end )
53845377 end
53855378
@@ -5399,32 +5392,33 @@ defmodule Module.Types.Descr do
53995392
54005393 defp tuple_pair_projection_keeps_full_snd? ( negative , value ) do
54015394 neg_values =
5402- Enum . reduce ( negative , { none ( ) , false } , fn { neg_value , _neg_bdd } , acc ->
5403- field_union ( neg_value , acc )
5395+ Enum . reduce ( negative , none ( ) , fn { neg_value , _neg_bdd } , acc ->
5396+ opt_union ( neg_value , acc )
54045397 end )
54055398
5406- not field_empty? ( field_difference ( value , neg_values ) )
5399+ not empty? ( opt_difference ( value , neg_values ) )
54075400 end
54085401
54095402 defp tuple_fetch_element ( [ ] , _ , :open ) , do: { term ( ) , true }
54105403 defp tuple_fetch_element ( [ ] , _ , :closed ) , do: { none ( ) , true }
54115404 defp tuple_fetch_element ( [ h | _ ] , 0 , _tag ) , do: { h , false }
54125405 defp tuple_fetch_element ( [ _ | t ] , i , tag ) , do: tuple_fetch_element ( t , i - 1 , tag )
54135406
5407+ # Computes the inverse image of a tuple literal under insertion at `index`:
5408+ # {value, remainder} represents all pairs whose insertion belongs to the
5409+ # literal.
54145410 defp tuple_take_element ( elements , index , tag ) do
54155411 case do_tuple_take_element ( elements , index , [ ] ) do
5416- :error -> { false , tuple_tag_to_field ( tag ) , tuple_new ( tag , elements ) }
5417- { value , elements } -> { true , { value , false } , tuple_new ( tag , elements ) }
5412+ :error when tag == :closed -> :empty
5413+ :error -> { term ( ) , tuple_new ( :open , tuple_fill ( elements , index ) ) }
5414+ { value , elements } -> { value , tuple_new ( tag , elements ) }
54185415 end
54195416 end
54205417
54215418 defp do_tuple_take_element ( [ ] , _ , _ ) , do: :error
54225419 defp do_tuple_take_element ( [ h | t ] , 0 , acc ) , do: { h , Enum . reverse ( acc , t ) }
54235420 defp do_tuple_take_element ( [ h | t ] , i , acc ) , do: do_tuple_take_element ( t , i - 1 , [ h | acc ] )
54245421
5425- defp tuple_tag_to_field ( :open ) , do: { term ( ) , true }
5426- defp tuple_tag_to_field ( :closed ) , do: { none ( ) , false }
5427-
54285422 @ doc """
54295423 Returns all of the values that are part of a tuple.
54305424 """
@@ -5526,22 +5520,27 @@ defmodule Module.Types.Descr do
55265520 |> tuple_bdd_to_dnf_with_negations ( )
55275521 |> Enum . reduce ( :bdd_bot , fn
55285522 { tag , elements , [ ] } , acc ->
5529- { _ , _ , bdd } = tuple_take_element ( elements , index , tag )
5530- opt_tuple_union ( bdd , acc )
5523+ case tuple_take_element ( elements , index , tag ) do
5524+ :empty ->
5525+ acc
55315526
5532- { tag , elements , negs } , acc ->
5533- { _ , value , bdd } = tuple_take_element ( elements , index , tag )
5527+ { _value , bdd } ->
5528+ opt_tuple_union ( bdd , acc )
5529+ end
55345530
5535- case tuple_split_negative_pairs_index ( negs , index ) do
5531+ { tag , elements , negs } , acc ->
5532+ case tuple_take_element ( elements , index , tag ) do
55365533 :empty ->
55375534 acc
55385535
5539- negative ->
5536+ { value , bdd } ->
5537+ negative = tuple_split_negative_pairs ( negs , index )
5538+
55405539 if tuple_pair_projection_keeps_full_snd? ( negative , value ) do
55415540 opt_tuple_union ( bdd , acc )
55425541 else
5543- negs
5544- |> tuple_split_negative ( index , value , bdd )
5542+ negative
5543+ |> tuple_project_negative_pairs ( value , bdd )
55455544 |> Enum . reduce ( acc , fn { _ , bdd } , acc -> opt_tuple_union ( bdd , acc ) end )
55465545 end
55475546 end
0 commit comments