@@ -2750,7 +2750,7 @@ defmodule Module.Types.Descr do
27502750
27512751 defp maybe_optimize_map_union ( tag1 , pos1 , tag2 , pos2 )
27522752 when is_atom ( tag1 ) and is_atom ( tag2 ) do
2753- case map_leaf_strategy ( tag1 , pos1 , tag2 , pos2 , true ) do
2753+ case map_union_strategy ( pos1 , pos2 , tag1 , tag2 , :all_equal ) do
27542754 :all_equal when tag1 == :open -> { tag1 , pos1 }
27552755 :all_equal -> { tag2 , pos2 }
27562756 { :one_key_difference , key , v1 , v2 } -> { tag1 , fields_store ( key , union ( v1 , v2 ) , pos1 ) }
@@ -2762,10 +2762,7 @@ defmodule Module.Types.Descr do
27622762
27632763 defp maybe_optimize_map_union ( _ , _ , _ , _ ) , do: nil
27642764
2765- defp map_leaf_strategy ( tag1 , fields1 , tag2 , fields2 , rhs? ) ,
2766- do: map_leaf_strategy ( fields1 , fields2 , tag1 , tag2 , rhs? , :all_equal )
2767-
2768- defp map_leaf_strategy ( [ { k1 , _ } | t1 ] , [ { k2 , _ } | _ ] = l2 , tag1 , tag2 , rhs? , status )
2765+ defp map_union_strategy ( [ { k1 , _ } | t1 ] , [ { k2 , _ } | _ ] = l2 , tag1 , tag2 , status )
27692766 when k1 < k2 do
27702767 # Left side has a key the right side does not have,
27712768 # left can only be a subtype if the right side is open.
@@ -2774,81 +2771,82 @@ defmodule Module.Types.Descr do
27742771 :none
27752772
27762773 :all_equal ->
2777- map_leaf_strategy ( t1 , l2 , tag1 , tag2 , rhs? , :left_subtype_of_right )
2774+ map_union_strategy ( t1 , l2 , tag1 , tag2 , :left_subtype_of_right )
27782775
27792776 { :one_key_difference , _ , p1 , p2 } ->
27802777 if subtype? ( p1 , p2 ) ,
2781- do: map_leaf_strategy ( t1 , l2 , tag1 , tag2 , rhs? , :left_subtype_of_right ) ,
2778+ do: map_union_strategy ( t1 , l2 , tag1 , tag2 , :left_subtype_of_right ) ,
27822779 else: :none
27832780
27842781 :left_subtype_of_right ->
2785- map_leaf_strategy ( t1 , l2 , tag1 , tag2 , rhs? , :left_subtype_of_right )
2782+ map_union_strategy ( t1 , l2 , tag1 , tag2 , :left_subtype_of_right )
27862783
27872784 _ ->
27882785 :none
27892786 end
27902787 end
27912788
2792- defp map_leaf_strategy ( [ { k1 , _ } | _ ] = l1 , [ { k2 , _ } | t2 ] , tag1 , tag2 , rhs? , status )
2789+ defp map_union_strategy ( [ { k1 , _ } | _ ] = l1 , [ { k2 , _ } | t2 ] , tag1 , tag2 , status )
27932790 when k1 > k2 do
27942791 # Right side has a key the left side does not have,
27952792 # right can only be a subtype if the left side is open.
27962793 case status do
2797- _ when tag1 != :open or not rhs? ->
2794+ _ when tag1 != :open ->
27982795 :none
27992796
28002797 :all_equal ->
2801- map_leaf_strategy ( l1 , t2 , tag1 , tag2 , rhs? , :right_subtype_of_left )
2798+ map_union_strategy ( l1 , t2 , tag1 , tag2 , :right_subtype_of_left )
28022799
28032800 { :one_key_difference , _ , p1 , p2 } ->
28042801 if subtype? ( p2 , p1 ) ,
2805- do: map_leaf_strategy ( l1 , t2 , tag1 , tag2 , rhs? , :right_subtype_of_left ) ,
2802+ do: map_union_strategy ( l1 , t2 , tag1 , tag2 , :right_subtype_of_left ) ,
28062803 else: :none
28072804
28082805 :right_subtype_of_left ->
2809- map_leaf_strategy ( l1 , t2 , tag1 , tag2 , rhs? , :right_subtype_of_left )
2806+ map_union_strategy ( l1 , t2 , tag1 , tag2 , :right_subtype_of_left )
28102807
28112808 _ ->
28122809 :none
28132810 end
28142811 end
28152812
2816- defp map_leaf_strategy ( [ { _ , v } | t1 ] , [ { _ , v } | t2 ] , tag1 , tag2 , rhs? , status ) do
2813+ defp map_union_strategy ( [ { _ , v } | t1 ] , [ { _ , v } | t2 ] , tag1 , tag2 , status ) do
28172814 # Same key and same value, nothing changes
2818- map_leaf_strategy ( t1 , t2 , tag1 , tag2 , rhs? , status )
2815+ map_union_strategy ( t1 , t2 , tag1 , tag2 , status )
28192816 end
28202817
2821- defp map_leaf_strategy ( [ { k1 , v1 } | t1 ] , [ { _ , v2 } | t2 ] , tag1 , tag2 , rhs? , status ) do
2818+ defp map_union_strategy ( [ { k1 , v1 } | t1 ] , [ { _ , v2 } | t2 ] , tag1 , tag2 , status ) do
28222819 # They have the same key but different values
28232820 case status do
2824- :all_equal when k1 != :__struct__ ->
2821+ :all_equal ->
28252822 cond do
2826- tag1 == tag2 ->
2827- map_leaf_strategy ( t1 , t2 , tag1 , tag2 , rhs? , { :one_key_difference , k1 , v1 , v2 } )
2823+ # Don't do difference on struct keys
2824+ k1 != :__struct__ and tag1 == tag2 ->
2825+ map_union_strategy ( t1 , t2 , tag1 , tag2 , { :one_key_difference , k1 , v1 , v2 } )
28282826
28292827 subtype? ( v1 , v2 ) ->
2830- map_leaf_strategy ( t1 , t2 , tag1 , tag2 , rhs? , :left_subtype_of_right )
2828+ map_union_strategy ( t1 , t2 , tag1 , tag2 , :left_subtype_of_right )
28312829
2832- rhs? and subtype? ( v2 , v1 ) ->
2833- map_leaf_strategy ( t1 , t2 , tag1 , tag2 , rhs? , :right_subtype_of_left )
2830+ subtype? ( v2 , v1 ) ->
2831+ map_union_strategy ( t1 , t2 , tag1 , tag2 , :right_subtype_of_left )
28342832
28352833 true ->
28362834 :none
28372835 end
28382836
28392837 :left_subtype_of_right ->
2840- if subtype? ( v1 , v2 ) , do: map_leaf_strategy ( t1 , t2 , tag1 , tag2 , rhs? , status ) , else: :none
2838+ if subtype? ( v1 , v2 ) , do: map_union_strategy ( t1 , t2 , tag1 , tag2 , status ) , else: :none
28412839
28422840 :right_subtype_of_left ->
2843- if subtype? ( v2 , v1 ) , do: map_leaf_strategy ( t1 , t2 , tag1 , tag2 , rhs? , status ) , else: :none
2841+ if subtype? ( v2 , v1 ) , do: map_union_strategy ( t1 , t2 , tag1 , tag2 , status ) , else: :none
28442842
28452843 { :one_key_difference , _key , p1 , p2 } ->
28462844 cond do
28472845 subtype? ( p1 , p2 ) and subtype? ( v1 , v2 ) ->
2848- map_leaf_strategy ( t1 , t2 , tag1 , tag2 , rhs? , :left_subtype_of_right )
2846+ map_union_strategy ( t1 , t2 , tag1 , tag2 , :left_subtype_of_right )
28492847
2850- rhs? and subtype? ( p2 , p1 ) and subtype? ( v2 , v1 ) ->
2851- map_leaf_strategy ( t1 , t2 , tag1 , tag2 , rhs? , :right_subtype_of_left )
2848+ subtype? ( p2 , p1 ) and subtype? ( v2 , v1 ) ->
2849+ map_union_strategy ( t1 , t2 , tag1 , tag2 , :right_subtype_of_left )
28522850
28532851 true ->
28542852 :none
@@ -2859,13 +2857,13 @@ defmodule Module.Types.Descr do
28592857 end
28602858 end
28612859
2862- defp map_leaf_strategy ( [ ] , [ ] , _tag1 , _tag2 , _rhs? , status ) do
2860+ defp map_union_strategy ( [ ] , [ ] , _tag1 , _tag2 , status ) do
28632861 status
28642862 end
28652863
2866- defp map_leaf_strategy ( l1 , l2 , tag1 , tag2 , rhs? , status ) do
2864+ defp map_union_strategy ( l1 , l2 , tag1 , tag2 , status ) do
28672865 lhs? = tag2 == :open and l2 == [ ]
2868- rhs? = rhs? and tag1 == :open and l1 == [ ]
2866+ rhs? = tag1 == :open and l1 == [ ]
28692867
28702868 case status do
28712869 :all_equal when lhs? ->
@@ -2971,10 +2969,13 @@ defmodule Module.Types.Descr do
29712969 end
29722970
29732971 _ when is_atom ( tag ) and is_atom ( neg_tag ) ->
2974- case map_leaf_strategy ( tag , fields , neg_tag , neg_fields , false ) do
2972+ case map_difference_strategy ( fields , neg_fields , tag , neg_tag , :all_equal ) do
29752973 :all_equal when tag == neg_tag or neg_tag == :open ->
29762974 :bdd_bot
29772975
2976+ :disjoint ->
2977+ bdd_leaf ( tag , fields )
2978+
29782979 { :one_key_difference , key , v1 , v2 } ->
29792980 t_diff = difference ( fields_get ( fields , key , v1 ) , v2 )
29802981
@@ -2988,7 +2989,7 @@ defmodule Module.Types.Descr do
29882989 :bdd_bot
29892990
29902991 _ ->
2991- bdd_difference ( map1 , map2 , & map_leaf_disjoint? / 2 )
2992+ bdd_difference ( map1 , map2 )
29922993 end
29932994
29942995 _ ->
@@ -4360,14 +4361,14 @@ defmodule Module.Types.Descr do
43604361 if empty_intersection? do
43614362 { acc_fields , acc_negs }
43624363 else
4363- case map_leaf_strategy ( tag , acc_fields , neg_tag , neg_fields , false ) do
4364+ case map_difference_strategy ( acc_fields , neg_fields , tag , neg_tag , :all_equal ) do
43644365 :all_equal when tag == neg_tag or neg_tag == :open ->
43654366 { acc_fields , acc_negs }
43664367
43674368 { :one_key_difference , key , v1 , v2 } ->
43684369 { fields_store ( key , difference ( v1 , v2 ) , acc_fields ) , acc_negs }
43694370
4370- :left_subtype_of_right ->
4371+ :disjoint ->
43714372 { acc_fields , acc_negs }
43724373
43734374 _ ->
@@ -4377,6 +4378,96 @@ defmodule Module.Types.Descr do
43774378 end )
43784379 end
43794380
4381+ defp map_difference_strategy ( [ { k1 , _ } | t1 ] , [ { k2 , _ } | _ ] = l2 , tag1 , tag2 , status )
4382+ when k1 < k2 do
4383+ # Left side has a key the right side does not have,
4384+ # left can only be a subtype if the right side is open.
4385+ case status do
4386+ _ when tag2 != :open ->
4387+ :none
4388+
4389+ :all_equal ->
4390+ map_difference_strategy ( t1 , l2 , tag1 , tag2 , :left_subtype_of_right )
4391+
4392+ { :one_key_difference , _ , p1 , p2 } ->
4393+ if subtype? ( p1 , p2 ) ,
4394+ do: map_difference_strategy ( t1 , l2 , tag1 , tag2 , :left_subtype_of_right ) ,
4395+ else: :none
4396+
4397+ :left_subtype_of_right ->
4398+ map_difference_strategy ( t1 , l2 , tag1 , tag2 , :left_subtype_of_right )
4399+
4400+ _ ->
4401+ :none
4402+ end
4403+ end
4404+
4405+ defp map_difference_strategy ( [ { k1 , _ } | _ ] , [ { k2 , _ } | _ ] , tag1 , _tag2 , _status )
4406+ when k1 > k2 do
4407+ # Right side has a key the left side does not have,
4408+ # if left-side is closed, they are disjoint.
4409+ if tag1 == :closed , do: :disjoint , else: :none
4410+ end
4411+
4412+ defp map_difference_strategy ( [ { _ , v } | t1 ] , [ { _ , v } | t2 ] , tag1 , tag2 , status ) do
4413+ # Same key and same value, nothing changes
4414+ map_difference_strategy ( t1 , t2 , tag1 , tag2 , status )
4415+ end
4416+
4417+ defp map_difference_strategy ( [ { k1 , v1 } | t1 ] , [ { _ , v2 } | t2 ] , tag1 , tag2 , status ) do
4418+ # They have the same key but different values
4419+ if disjoint? ( v1 , v2 ) do
4420+ :disjoint
4421+ else
4422+ case status do
4423+ :all_equal when tag1 == tag2 ->
4424+ map_difference_strategy ( t1 , t2 , tag1 , tag2 , { :one_key_difference , k1 , v1 , v2 } )
4425+
4426+ { :one_key_difference , _key , p1 , p2 } ->
4427+ if subtype? ( p1 , p2 ) and subtype? ( v1 , v2 ) do
4428+ map_difference_strategy ( t1 , t2 , tag1 , tag2 , :left_subtype_of_right )
4429+ else
4430+ :none
4431+ end
4432+
4433+ # all_equal or left_subtype_of_right
4434+ _ ->
4435+ if subtype? ( v1 , v2 ) ,
4436+ do: map_difference_strategy ( t1 , t2 , tag1 , tag2 , :left_subtype_of_right ) ,
4437+ else: :none
4438+ end
4439+ end
4440+ end
4441+
4442+ defp map_difference_strategy ( [ ] , [ ] , _tag1 , _tag2 , status ) do
4443+ status
4444+ end
4445+
4446+ defp map_difference_strategy ( _l1 , l2 , tag1 , tag2 , status ) do
4447+ cond do
4448+ tag1 == :closed and l2 != [ ] ->
4449+ :disjoint
4450+
4451+ tag2 == :open and l2 == [ ] ->
4452+ case status do
4453+ :all_equal ->
4454+ :left_subtype_of_right
4455+
4456+ { :one_key_difference , _ , p1 , p2 } ->
4457+ if subtype? ( p1 , p2 ) , do: :left_subtype_of_right , else: :none
4458+
4459+ :left_subtype_of_right ->
4460+ :left_subtype_of_right
4461+
4462+ _ ->
4463+ :none
4464+ end
4465+
4466+ true ->
4467+ :none
4468+ end
4469+ end
4470+
43804471 # Given a dnf, fuse maps when possible
43814472 # e.g. %{a: integer(), b: atom()} or %{a: float(), b: atom()} into %{a: number(), b: atom()}
43824473 defp map_fusion ( dnf ) do
0 commit comments