@@ -2894,12 +2894,17 @@ defmodule Module.Types.Descr do
28942894 # Case 1: we are removing an open map with one field. Just do the difference of that field.
28952895 if neg_tag == :open and map_size ( neg_fields ) == 1 do
28962896 [ { key , value } ] = Map . to_list ( neg_fields )
2897- t_diff = difference ( Map . get ( fields , key , map_key_tag_to_type ( tag ) ) , value )
28982897
2899- if empty? ( t_diff ) do
2900- :bdd_bot
2898+ if tag == :closed and not is_map_key ( fields , key ) and not optional_static? ( value ) do
2899+ map1
29012900 else
2902- bdd_leaf ( tag , Map . put ( fields , key , t_diff ) )
2901+ t_diff = difference ( Map . get ( fields , key , map_key_tag_to_type ( tag ) ) , value )
2902+
2903+ if empty? ( t_diff ) do
2904+ :bdd_bot
2905+ else
2906+ bdd_leaf ( tag , Map . put ( fields , key , t_diff ) )
2907+ end
29032908 end
29042909 else
29052910 # Case 2: the maps have all but one key in common. Do the difference of that key.
@@ -2948,25 +2953,20 @@ defmodule Module.Types.Descr do
29482953
29492954 # Both closed: the result is closed.
29502955 defp map_literal_intersection ( :closed , map1 , :closed , map2 ) do
2951- new_fields =
2952- symmetrical_intersection ( map1 , map2 , fn _ , type1 , type2 ->
2953- non_empty_intersection! ( type1 , type2 )
2954- end )
2955-
2956- if map_size ( new_fields ) < map_size ( map1 ) or map_size ( new_fields ) < map_size ( map2 ) do
2957- throw ( :empty )
2956+ if map_size ( map1 ) > map_size ( map2 ) do
2957+ :maps . iterator ( map1 ) |> :maps . next ( ) |> map_literal_intersection_closed ( map2 , [ ] )
2958+ else
2959+ :maps . iterator ( map2 ) |> :maps . next ( ) |> map_literal_intersection_closed ( map1 , [ ] )
29582960 end
2959-
2960- { :closed , new_fields }
29612961 end
29622962
29632963 # Open and closed: result is closed, all fields from open should be in closed, except not_set ones.
29642964 defp map_literal_intersection ( :open , open , :closed , closed ) do
2965- :maps . iterator ( open ) |> :maps . next ( ) |> map_literal_intersection_loop ( closed )
2965+ :maps . iterator ( open ) |> :maps . next ( ) |> map_literal_intersection_open_closed ( closed )
29662966 end
29672967
29682968 defp map_literal_intersection ( :closed , closed , :open , open ) do
2969- :maps . iterator ( open ) |> :maps . next ( ) |> map_literal_intersection_loop ( closed )
2969+ :maps . iterator ( open ) |> :maps . next ( ) |> map_literal_intersection_open_closed ( closed )
29702970 end
29712971
29722972 # At least one tag is a tag-domain pair.
@@ -3021,23 +3021,56 @@ defmodule Module.Types.Descr do
30213021 if map_size ( new_domains ) == 0 , do: :closed , else: new_domains
30223022 end
30233023
3024- defp map_literal_intersection_loop ( :none , acc ) , do: { :closed , acc }
3024+ defp map_literal_intersection_open_closed ( :none , acc ) , do: { :closed , acc }
30253025
3026- defp map_literal_intersection_loop ( { key , type1 , iterator } , acc ) do
3026+ defp map_literal_intersection_open_closed ( { key , type1 , iterator } , acc ) do
30273027 case acc do
30283028 % { ^ key => type2 } ->
30293029 acc = % { acc | key => non_empty_intersection! ( type1 , type2 ) }
3030- :maps . next ( iterator ) |> map_literal_intersection_loop ( acc )
3030+ map_literal_intersection_open_closed ( :maps . next ( iterator ) , acc )
30313031
30323032 _ ->
30333033 # If the key is optional in the open map, we can ignore it
30343034 case type1 do
3035- % { optional: 1 } -> :maps . next ( iterator ) |> map_literal_intersection_loop ( acc )
3035+ % { optional: 1 } -> map_literal_intersection_open_closed ( :maps . next ( iterator ) , acc )
30363036 _ -> throw ( :empty )
30373037 end
30383038 end
30393039 end
30403040
3041+ defp map_literal_intersection_closed ( :none , map , acc ) do
3042+ fields = :maps . from_list ( acc )
3043+
3044+ # If the number of fields match, then it is empty unless the mismatched fields are not set
3045+ if map_size ( map ) != map_size ( fields ) do
3046+ :maps . fold (
3047+ fn
3048+ key , value , _acc when is_map_key ( fields , key ) or value == @ not_set -> :ok
3049+ _key , _value , _acc -> throw ( :empty )
3050+ end ,
3051+ :ok ,
3052+ map
3053+ )
3054+ end
3055+
3056+ { :closed , fields }
3057+ end
3058+
3059+ defp map_literal_intersection_closed ( { key , type1 , iterator } , map , acc ) do
3060+ case map do
3061+ % { ^ key => type2 } ->
3062+ acc = [ { key , non_empty_intersection! ( type1 , type2 ) } | acc ]
3063+ map_literal_intersection_closed ( :maps . next ( iterator ) , map , acc )
3064+
3065+ # If the field is literally not set, we are fine
3066+ _ when type1 == @ not_set ->
3067+ map_literal_intersection_closed ( :maps . next ( iterator ) , map , acc )
3068+
3069+ _ ->
3070+ throw ( :empty )
3071+ end
3072+ end
3073+
30413074 defp non_empty_intersection! ( type1 , type2 ) do
30423075 type = intersection ( type1 , type2 )
30433076 if empty? ( type ) , do: throw ( :empty ) , else: type
0 commit comments