@@ -348,9 +348,6 @@ defmodule Module.Types.Descr do
348348 end
349349 end
350350
351- defp optional_static? ( % { optional: 1 } ) , do: true
352- defp optional_static? ( _term_or_descr ) , do: false
353-
354351 defp pop_optional_static ( :term ) , do: { false , :term }
355352
356353 defp pop_optional_static ( type ) do
@@ -2737,40 +2734,38 @@ defmodule Module.Types.Descr do
27372734 end
27382735
27392736 defp map_union ( bdd_leaf ( tag1 , fields1 ) , bdd_leaf ( tag2 , fields2 ) ) do
2740- case maybe_optimize_map_union ( { tag1 , fields1 , [ ] } , { tag2 , fields2 , [ ] } ) do
2741- { tag , fields , [ ] } -> bdd_leaf ( tag , fields )
2737+ case maybe_optimize_map_union ( tag1 , fields1 , tag2 , fields2 ) do
2738+ { tag , fields } -> bdd_leaf ( tag , fields )
27422739 nil -> bdd_union ( bdd_leaf ( tag1 , fields1 ) , bdd_leaf ( tag2 , fields2 ) )
27432740 end
27442741 end
27452742
2746- @ compile { :inline , map_union: 2 }
27472743 defp map_union ( bdd1 , bdd2 ) , do: bdd_union ( bdd1 , bdd2 )
27482744
2749- defp maybe_optimize_map_union ( { tag1 , pos1 , [ ] } = map1 , { tag2 , pos2 , [ ] } = map2 )
2745+ defp maybe_optimize_map_union ( :open , empty , _ , _ ) when is_fields_empty ( empty ) ,
2746+ do: { :open , @ fields_new }
2747+
2748+ defp maybe_optimize_map_union ( _ , _ , :open , empty ) when is_fields_empty ( empty ) ,
2749+ do: { :open , @ fields_new }
2750+
2751+ defp maybe_optimize_map_union ( tag1 , pos1 , tag2 , pos2 )
27502752 when is_atom ( tag1 ) and is_atom ( tag2 ) do
2751- case map_union_strategy ( tag1 , pos1 , tag2 , pos2 ) do
2752- :all_equal when tag1 == :open -> map1
2753- :all_equal -> map2
2754- :any_map -> { :open , @ fields_new , [ ] }
2755- { :one_key_difference , key , v1 , v2 } -> { tag1 , fields_store ( key , union ( v1 , v2 ) , pos1 ) , [ ] }
2756- :left_subtype_of_right -> map2
2757- :right_subtype_of_left -> map1
2753+ case map_leaf_strategy ( tag1 , pos1 , tag2 , pos2 , true ) do
2754+ :all_equal when tag1 == :open -> { tag1 , pos1 }
2755+ :all_equal -> { tag2 , pos2 }
2756+ { :one_key_difference , key , v1 , v2 } -> { tag1 , fields_store ( key , union ( v1 , v2 ) , pos1 ) }
2757+ :left_subtype_of_right -> { tag2 , pos2 }
2758+ :right_subtype_of_left -> { tag1 , pos1 }
27582759 :none -> nil
27592760 end
27602761 end
27612762
2762- defp maybe_optimize_map_union ( _ , _ ) , do: nil
2763-
2764- defp map_union_strategy ( :open , empty , _ , _ ) when is_fields_empty ( empty ) ,
2765- do: :any_map
2763+ defp maybe_optimize_map_union ( _ , _ , _ , _ ) , do: nil
27662764
2767- defp map_union_strategy ( _ , _ , :open , empty ) when is_fields_empty ( empty ) ,
2768- do: :any_map
2765+ defp map_leaf_strategy ( tag1 , fields1 , tag2 , fields2 , rhs? ) ,
2766+ do: map_leaf_strategy ( fields1 , fields2 , tag1 , tag2 , rhs? , :all_equal )
27692767
2770- defp map_union_strategy ( tag1 , fields1 , tag2 , fields2 ) ,
2771- do: map_union_strategy ( fields1 , fields2 , tag1 , tag2 , :all_equal )
2772-
2773- defp map_union_strategy ( [ { k1 , _ } | t1 ] , [ { k2 , _ } | _ ] = l2 , tag1 , tag2 , status )
2768+ defp map_leaf_strategy ( [ { k1 , _ } | t1 ] , [ { k2 , _ } | _ ] = l2 , tag1 , tag2 , rhs? , status )
27742769 when k1 < k2 do
27752770 # Left side has a key the right side does not have,
27762771 # left can only be a subtype if the right side is open.
@@ -2779,81 +2774,81 @@ defmodule Module.Types.Descr do
27792774 :none
27802775
27812776 :all_equal ->
2782- map_union_strategy ( t1 , l2 , tag1 , tag2 , :left_subtype_of_right )
2777+ map_leaf_strategy ( t1 , l2 , tag1 , tag2 , rhs? , :left_subtype_of_right )
27832778
27842779 { :one_key_difference , _ , p1 , p2 } ->
27852780 if subtype? ( p1 , p2 ) ,
2786- do: map_union_strategy ( t1 , l2 , tag1 , tag2 , :left_subtype_of_right ) ,
2781+ do: map_leaf_strategy ( t1 , l2 , tag1 , tag2 , rhs? , :left_subtype_of_right ) ,
27872782 else: :none
27882783
27892784 :left_subtype_of_right ->
2790- map_union_strategy ( t1 , l2 , tag1 , tag2 , :left_subtype_of_right )
2785+ map_leaf_strategy ( t1 , l2 , tag1 , tag2 , rhs? , :left_subtype_of_right )
27912786
27922787 _ ->
27932788 :none
27942789 end
27952790 end
27962791
2797- defp map_union_strategy ( [ { k1 , _ } | _ ] = l1 , [ { k2 , _ } | t2 ] , tag1 , tag2 , status )
2792+ defp map_leaf_strategy ( [ { k1 , _ } | _ ] = l1 , [ { k2 , _ } | t2 ] , tag1 , tag2 , rhs? , status )
27982793 when k1 > k2 do
27992794 # Right side has a key the left side does not have,
28002795 # right can only be a subtype if the left side is open.
28012796 case status do
2802- _ when tag1 != :open ->
2797+ _ when tag1 != :open or not rhs? ->
28032798 :none
28042799
28052800 :all_equal ->
2806- map_union_strategy ( l1 , t2 , tag1 , tag2 , :right_subtype_of_left )
2801+ map_leaf_strategy ( l1 , t2 , tag1 , tag2 , rhs? , :right_subtype_of_left )
28072802
28082803 { :one_key_difference , _ , p1 , p2 } ->
28092804 if subtype? ( p2 , p1 ) ,
2810- do: map_union_strategy ( l1 , t2 , tag1 , tag2 , :right_subtype_of_left ) ,
2805+ do: map_leaf_strategy ( l1 , t2 , tag1 , tag2 , rhs? , :right_subtype_of_left ) ,
28112806 else: :none
28122807
28132808 :right_subtype_of_left ->
2814- map_union_strategy ( l1 , t2 , tag1 , tag2 , :right_subtype_of_left )
2809+ map_leaf_strategy ( l1 , t2 , tag1 , tag2 , rhs? , :right_subtype_of_left )
28152810
28162811 _ ->
28172812 :none
28182813 end
28192814 end
28202815
2821- defp map_union_strategy ( [ { _ , v } | t1 ] , [ { _ , v } | t2 ] , tag1 , tag2 , status ) do
2816+ defp map_leaf_strategy ( [ { _ , v } | t1 ] , [ { _ , v } | t2 ] , tag1 , tag2 , rhs? , status ) do
28222817 # Same key and same value, nothing changes
2823- map_union_strategy ( t1 , t2 , tag1 , tag2 , status )
2818+ map_leaf_strategy ( t1 , t2 , tag1 , tag2 , rhs? , status )
28242819 end
28252820
2826- defp map_union_strategy ( [ { k1 , v1 } | t1 ] , [ { _ , v2 } | t2 ] , tag1 , tag2 , status ) do
2821+ defp map_leaf_strategy ( [ { k1 , v1 } | t1 ] , [ { _ , v2 } | t2 ] , tag1 , tag2 , rhs? , status ) do
28272822 # They have the same key but different values
28282823 case status do
28292824 :all_equal when k1 != :__struct__ ->
28302825 cond do
28312826 tag1 == tag2 ->
2832- map_union_strategy ( t1 , t2 , tag1 , tag2 , { :one_key_difference , k1 , v1 , v2 } )
2827+ map_leaf_strategy ( t1 , t2 , tag1 , tag2 , rhs? , { :one_key_difference , k1 , v1 , v2 } )
28332828
28342829 subtype? ( v1 , v2 ) ->
2835- map_union_strategy ( t1 , t2 , tag1 , tag2 , :left_subtype_of_right )
2830+ map_leaf_strategy ( t1 , t2 , tag1 , tag2 , rhs? , :left_subtype_of_right )
28362831
2837- subtype? ( v2 , v1 ) ->
2838- map_union_strategy ( t1 , t2 , tag1 , tag2 , :right_subtype_of_left )
2832+ rhs? and subtype? ( v2 , v1 ) ->
2833+ map_leaf_strategy ( t1 , t2 , tag1 , tag2 , rhs? , :right_subtype_of_left )
28392834
28402835 true ->
28412836 :none
28422837 end
28432838
28442839 :left_subtype_of_right ->
2845- if subtype? ( v1 , v2 ) , do: map_union_strategy ( t1 , t2 , tag1 , tag2 , status ) , else: :none
2840+ if subtype? ( v1 , v2 ) , do: map_leaf_strategy ( t1 , t2 , tag1 , tag2 , rhs? , status ) , else: :none
28462841
28472842 :right_subtype_of_left ->
2848- if subtype? ( v2 , v1 ) , do: map_union_strategy ( t1 , t2 , tag1 , tag2 , status ) , else: :none
2843+ if subtype? ( v2 , v1 ) , do: map_leaf_strategy ( t1 , t2 , tag1 , tag2 , rhs? , status ) , else: :none
28492844
28502845 { :one_key_difference , _key , p1 , p2 } ->
28512846 cond do
28522847 subtype? ( p1 , p2 ) and subtype? ( v1 , v2 ) ->
2853- map_union_strategy ( t1 , t2 , tag1 , tag2 , :left_subtype_of_right )
2848+ map_leaf_strategy ( t1 , t2 , tag1 , tag2 , rhs? , :left_subtype_of_right )
28542849
2855- subtype? ( p2 , p1 ) and subtype? ( v2 , v1 ) ->
2856- map_union_strategy ( t1 , t2 , tag1 , tag2 , :right_subtype_of_left )
2850+ rhs? and subtype? ( p2 , p1 ) and subtype? ( v2 , v1 ) ->
2851+ map_leaf_strategy ( t1 , t2 , tag1 , tag2 , rhs? , :right_subtype_of_left )
28572852
28582853 true ->
28592854 :none
@@ -2864,29 +2859,32 @@ defmodule Module.Types.Descr do
28642859 end
28652860 end
28662861
2867- defp map_union_strategy ( [ ] , [ ] , _tag1 , _tag2 , status ) do
2862+ defp map_leaf_strategy ( [ ] , [ ] , _tag1 , _tag2 , _rhs? , status ) do
28682863 status
28692864 end
28702865
2871- defp map_union_strategy ( l1 , l2 , tag1 , tag2 , status ) do
2866+ defp map_leaf_strategy ( l1 , l2 , tag1 , tag2 , rhs? , status ) do
2867+ lhs? = tag2 == :open and l2 == [ ]
2868+ rhs? = rhs? and tag1 == :open and l1 == [ ]
2869+
28722870 case status do
2873- :all_equal when tag2 == :open and l2 == [ ] ->
2871+ :all_equal when lhs? ->
28742872 :left_subtype_of_right
28752873
2876- :all_equal when tag1 == :open and l1 == [ ] ->
2874+ :all_equal when rhs? ->
28772875 :right_subtype_of_left
28782876
28792877 { :one_key_difference , _ , p1 , p2 } ->
28802878 cond do
2881- tag2 == :open and l2 == [ ] and subtype? ( p1 , p2 ) -> :left_subtype_of_right
2882- tag1 == :open and l1 == [ ] and subtype? ( p2 , p1 ) -> :right_subtype_of_left
2879+ lhs? and subtype? ( p1 , p2 ) -> :left_subtype_of_right
2880+ rhs? and subtype? ( p2 , p1 ) -> :right_subtype_of_left
28832881 true -> :none
28842882 end
28852883
2886- :left_subtype_of_right when tag2 == :open and l2 == [ ] ->
2884+ :left_subtype_of_right when lhs? ->
28872885 :left_subtype_of_right
28882886
2889- :right_subtype_of_left when tag1 == :open and l1 == [ ] ->
2887+ :right_subtype_of_left when rhs? ->
28902888 :right_subtype_of_left
28912889
28922890 _ ->
@@ -2960,7 +2958,7 @@ defmodule Module.Types.Descr do
29602958 :error -> { false , map_key_tag_to_type ( tag ) }
29612959 end
29622960
2963- if tag == :closed and not found? and not optional_static? ( value ) do
2961+ if tag == :closed and not found? and not is_optional_static ( value ) do
29642962 map1
29652963 else
29662964 t_diff = difference ( fields_get ( fields , key , pos_value ) , value )
@@ -2972,15 +2970,29 @@ defmodule Module.Types.Descr do
29722970 end
29732971 end
29742972
2975- _ ->
2976- # Case 2: the maps have all but one key in common. Do the difference of that key.
2977- case map_all_but_one ( tag , fields , neg_tag , neg_fields ) do
2978- { diff_key , type1 , type2 } ->
2979- bdd_leaf ( tag , fields_store ( diff_key , difference ( type1 , type2 ) , fields ) )
2973+ _ when is_atom ( tag ) and is_atom ( neg_tag ) ->
2974+ case map_leaf_strategy ( tag , fields , neg_tag , neg_fields , false ) do
2975+ :all_equal when tag == neg_tag or neg_tag == :open ->
2976+ :bdd_bot
2977+
2978+ { :one_key_difference , key , v1 , v2 } ->
2979+ t_diff = difference ( fields_get ( fields , key , v1 ) , v2 )
2980+
2981+ if empty? ( t_diff ) do
2982+ :bdd_bot
2983+ else
2984+ bdd_leaf ( tag , fields_store ( key , t_diff , fields ) )
2985+ end
2986+
2987+ :left_subtype_of_right ->
2988+ :bdd_bot
29802989
29812990 _ ->
29822991 bdd_difference ( map1 , map2 , & map_leaf_disjoint? / 2 )
29832992 end
2993+
2994+ _ ->
2995+ bdd_difference ( map1 , map2 , & map_leaf_disjoint? / 2 )
29842996 end
29852997 end
29862998
@@ -3012,9 +3024,7 @@ defmodule Module.Types.Descr do
30123024 defp map_literal_intersection ( :open , map1 , :open , map2 ) do
30133025 new_fields =
30143026 fields_merge (
3015- fn _ , type1 , type2 ->
3016- non_empty_intersection! ( type1 , type2 )
3017- end ,
3027+ fn _ , type1 , type2 -> non_empty_intersection! ( type1 , type2 ) end ,
30183028 map1 ,
30193029 map2
30203030 )
@@ -3326,9 +3336,9 @@ defmodule Module.Types.Descr do
33263336
33273337 defp has_empty_map? ( dnf ) do
33283338 Enum . any? ( dnf , fn { _ , fields , negs } ->
3329- Enum . all? ( fields_to_list ( fields ) , fn { _key , value } -> optional_static? ( value ) end ) and
3339+ Enum . all? ( fields_to_list ( fields ) , fn { _key , value } -> is_optional_static ( value ) end ) and
33303340 Enum . all? ( negs , fn { _ , fields } ->
3331- not Enum . all? ( fields_to_list ( fields ) , fn { _key , value } -> optional_static? ( value ) end )
3341+ not Enum . all? ( fields_to_list ( fields ) , fn { _key , value } -> is_optional_static ( value ) end )
33323342 end )
33333343 end )
33343344 end
@@ -4313,9 +4323,15 @@ defmodule Module.Types.Descr do
43134323 if empty_intersection? do
43144324 { acc_fields , acc_negs }
43154325 else
4316- case map_all_but_one ( tag , acc_fields , neg_tag , neg_fields ) do
4317- { diff_key , type1 , type2 } ->
4318- { fields_store ( diff_key , difference ( type1 , type2 ) , acc_fields ) , acc_negs }
4326+ case map_leaf_strategy ( tag , acc_fields , neg_tag , neg_fields , false ) do
4327+ :all_equal when tag == neg_tag or neg_tag == :open ->
4328+ { acc_fields , acc_negs }
4329+
4330+ { :one_key_difference , key , v1 , v2 } ->
4331+ { fields_store ( key , difference ( v1 , v2 ) , acc_fields ) , acc_negs }
4332+
4333+ :left_subtype_of_right ->
4334+ { acc_fields , acc_negs }
43194335
43204336 _ ->
43214337 { acc_fields , [ neg | acc_negs ] }
@@ -4350,34 +4366,16 @@ defmodule Module.Types.Descr do
43504366 defp map_fuse_with_first_fusible ( map , [ ] ) , do: [ map ]
43514367
43524368 defp map_fuse_with_first_fusible ( map , [ candidate | rest ] ) do
4353- case maybe_optimize_map_union ( map , candidate ) do
4369+ { tag1 , fields1 , [ ] } = map
4370+ { tag2 , fields2 , [ ] } = candidate
4371+
4372+ case maybe_optimize_map_union ( tag1 , fields1 , tag2 , fields2 ) do
43544373 nil -> [ candidate | map_fuse_with_first_fusible ( map , rest ) ]
43554374 # we found a fusible candidate, we're done
4356- fused -> [ fused | rest ]
4357- end
4358- end
4359-
4360- # If all fields are the same except one, we can optimize map difference.
4361- defp map_all_but_one ( tag1 , fields1 , tag2 , fields2 ) do
4362- with true <- { tag1 , tag2 } != { :open , :closed } ,
4363- [ triplet ] <- map_all_but_one ( fields1 , fields2 , [ ] ) do
4364- triplet
4365- else
4366- _ -> :error
4375+ { tag , fields } -> [ { tag , fields , [ ] } | rest ]
43674376 end
43684377 end
43694378
4370- defp map_all_but_one ( [ { k , v1 } | t1 ] , [ { k , v2 } | t2 ] , found ) do
4371- cond do
4372- v1 == v2 -> map_all_but_one ( t1 , t2 , found )
4373- found == [ ] -> map_all_but_one ( t1 , t2 , [ { k , v1 , v2 } ] )
4374- true -> [ ]
4375- end
4376- end
4377-
4378- defp map_all_but_one ( [ ] , [ ] , found ) , do: found
4379- defp map_all_but_one ( _ , _ , _found ) , do: [ ]
4380-
43814379 defp map_to_quoted ( bdd , opts ) do
43824380 bdd
43834381 |> map_bdd_to_dnf ( )
0 commit comments