@@ -2469,8 +2469,8 @@ defmodule Module.Types.Descr do
24692469
24702470 defp list_leaf_intersection ( bdd_leaf ( list1 , last1 ) , bdd_leaf ( list2 , last2 ) ) do
24712471 try do
2472- list = non_empty_intersection! ( list1 , list2 )
2473- last = non_empty_intersection! ( last1 , last2 )
2472+ list = non_empty_intersection! ( list1 , list2 , & opt_intersection / 2 )
2473+ last = non_empty_intersection! ( last1 , last2 , & opt_intersection / 2 )
24742474 bdd_leaf_new ( list , last )
24752475 catch
24762476 :empty -> :bdd_bot
@@ -3029,18 +3029,18 @@ defmodule Module.Types.Descr do
30293029 defp map_union ( bdd1 , bdd2 ) , do: bdd_union ( bdd1 , bdd2 )
30303030
30313031 defp opt_map_union ( bdd_leaf ( tag1 , fields1 ) , bdd_leaf ( tag2 , fields2 ) ) do
3032- case maybe_optimize_map_union ( tag1 , fields1 , tag2 , fields2 ) do
3032+ case opt_map_union ( tag1 , fields1 , tag2 , fields2 ) do
30333033 { tag , fields } -> bdd_leaf_new ( tag , fields )
30343034 nil -> bdd_union ( bdd_leaf_new ( tag1 , fields1 ) , bdd_leaf_new ( tag2 , fields2 ) )
30353035 end
30363036 end
30373037
30383038 defp opt_map_union ( bdd1 , bdd2 ) , do: map_union ( bdd1 , bdd2 )
30393039
3040- defp maybe_optimize_map_union ( :open , [ ] , _ , _ ) , do: { :open , @ fields_new }
3041- defp maybe_optimize_map_union ( _ , _ , :open , [ ] ) , do: { :open , @ fields_new }
3040+ defp opt_map_union ( :open , [ ] , _ , _ ) , do: { :open , @ fields_new }
3041+ defp opt_map_union ( _ , _ , :open , [ ] ) , do: { :open , @ fields_new }
30423042
3043- defp maybe_optimize_map_union ( tag1 , pos1 , tag2 , pos2 )
3043+ defp opt_map_union ( tag1 , pos1 , tag2 , pos2 )
30443044 when is_atom ( tag1 ) and is_atom ( tag2 ) do
30453045 case map_union_strategy ( pos1 , pos2 , tag1 , tag2 , :all_equal ) do
30463046 :all_equal when tag1 == :open -> { tag1 , pos1 }
@@ -3052,7 +3052,7 @@ defmodule Module.Types.Descr do
30523052 end
30533053 end
30543054
3055- defp maybe_optimize_map_union ( _ , _ , _ , _ ) , do: nil
3055+ defp opt_map_union ( _ , _ , _ , _ ) , do: nil
30563056
30573057 defp map_union_strategy ( [ { k1 , _ } | t1 ] , [ { k2 , _ } | _ ] = l2 , tag1 , tag2 , status )
30583058 when k1 < k2 do
@@ -3198,7 +3198,7 @@ defmodule Module.Types.Descr do
31983198
31993199 defp map_leaf_intersection ( bdd_leaf ( tag1 , fields1 ) , bdd_leaf ( tag2 , fields2 ) ) do
32003200 try do
3201- { tag , fields } = map_literal_intersection ( tag1 , fields1 , tag2 , fields2 )
3201+ { tag , fields } = map_literal_intersection ( tag1 , fields1 , tag2 , fields2 , & opt_intersection / 2 )
32023202 bdd_leaf_new ( tag , fields )
32033203 catch
32043204 :empty -> :bdd_bot
@@ -3285,10 +3285,12 @@ defmodule Module.Types.Descr do
32853285
32863286 # Intersects two map literals; throws if their intersection is empty.
32873287 # Both open: the result is open.
3288- defp map_literal_intersection ( :open , map1 , :open , map2 ) do
3288+ defp map_literal_intersection ( tag1 , map1 , tag2 , map2 , intersection_fun \\ & bare_intersection / 2 )
3289+
3290+ defp map_literal_intersection ( :open , map1 , :open , map2 , intersection_fun ) do
32893291 new_fields =
32903292 fields_merge (
3291- fn _ , type1 , type2 -> non_empty_intersection! ( type1 , type2 ) end ,
3293+ fn _ , type1 , type2 -> non_empty_intersection! ( type1 , type2 , intersection_fun ) end ,
32923294 map1 ,
32933295 map2
32943296 )
@@ -3297,21 +3299,21 @@ defmodule Module.Types.Descr do
32973299 end
32983300
32993301 # Both closed: the result is closed.
3300- defp map_literal_intersection ( :closed , map1 , :closed , map2 ) do
3301- { :closed , map_literal_intersection_closed ( map1 , map2 ) }
3302+ defp map_literal_intersection ( :closed , map1 , :closed , map2 , intersection_fun ) do
3303+ { :closed , map_literal_intersection_closed ( map1 , map2 , intersection_fun ) }
33023304 end
33033305
33043306 # Open and closed: result is closed, all fields from open should be in closed, except not_set ones.
3305- defp map_literal_intersection ( :open , open , :closed , closed ) do
3306- { :closed , map_literal_intersection_open_closed ( open , closed ) }
3307+ defp map_literal_intersection ( :open , open , :closed , closed , intersection_fun ) do
3308+ { :closed , map_literal_intersection_open_closed ( open , closed , intersection_fun ) }
33073309 end
33083310
3309- defp map_literal_intersection ( :closed , closed , :open , open ) do
3310- { :closed , map_literal_intersection_open_closed ( open , closed ) }
3311+ defp map_literal_intersection ( :closed , closed , :open , open , intersection_fun ) do
3312+ { :closed , map_literal_intersection_open_closed ( open , closed , intersection_fun ) }
33113313 end
33123314
33133315 # At least one tag is a tag-domain pair.
3314- defp map_literal_intersection ( tag_or_domains1 , map1 , tag_or_domains2 , map2 ) do
3316+ defp map_literal_intersection ( tag_or_domains1 , map1 , tag_or_domains2 , map2 , intersection_fun ) do
33153317 # For a closed map with domains intersected with an open map with domains:
33163318 # 1. The result is closed (more restrictive)
33173319 # 2. We need to check each domain in the open map against the closed map
@@ -3329,7 +3331,7 @@ defmodule Module.Types.Descr do
33293331 # using default values when a key is not present.
33303332 { tag_or_domains ,
33313333 fields_merge_with_defaults ( map1 , default1 , map2 , default2 , fn _key , v1 , v2 ->
3332- non_empty_intersection! ( v1 , v2 )
3334+ non_empty_intersection! ( v1 , v2 , intersection_fun )
33333335 end ) }
33343336 end
33353337
@@ -3367,52 +3369,62 @@ defmodule Module.Types.Descr do
33673369
33683370 defp map_domain_intersection_fields ( _ , _ ) , do: [ ]
33693371
3370- defp map_literal_intersection_open_closed ( [ { k1 , v1 } | t1 ] , [ { k2 , _ } | _ ] = l2 ) when k1 < k2 do
3372+ defp map_literal_intersection_open_closed ( [ { k1 , v1 } | t1 ] , [ { k2 , _ } | _ ] = l2 , intersection_fun )
3373+ when k1 < k2 do
33713374 # If the type in the open map is optional, we continue
33723375 case v1 do
3373- % { optional: 1 } -> map_literal_intersection_open_closed ( t1 , l2 )
3376+ % { optional: 1 } -> map_literal_intersection_open_closed ( t1 , l2 , intersection_fun )
33743377 _ -> throw ( :empty )
33753378 end
33763379 end
33773380
3378- defp map_literal_intersection_open_closed ( [ { k1 , _ } | _ ] = l1 , [ { k2 , v2 } | t2 ] ) when k1 > k2 do
3381+ defp map_literal_intersection_open_closed ( [ { k1 , _ } | _ ] = l1 , [ { k2 , v2 } | t2 ] , intersection_fun )
3382+ when k1 > k2 do
33793383 # Anything in the closed map not in open is preserved
3380- [ { k2 , v2 } | map_literal_intersection_open_closed ( l1 , t2 ) ]
3384+ [ { k2 , v2 } | map_literal_intersection_open_closed ( l1 , t2 , intersection_fun ) ]
33813385 end
33823386
3383- defp map_literal_intersection_open_closed ( [ { key , v1 } | t1 ] , [ { _ , v2 } | t2 ] ) do
3384- [ { key , non_empty_intersection! ( v1 , v2 ) } | map_literal_intersection_open_closed ( t1 , t2 ) ]
3387+ defp map_literal_intersection_open_closed ( [ { key , v1 } | t1 ] , [ { _ , v2 } | t2 ] , intersection_fun ) do
3388+ [
3389+ { key , non_empty_intersection! ( v1 , v2 , intersection_fun ) }
3390+ | map_literal_intersection_open_closed ( t1 , t2 , intersection_fun )
3391+ ]
33853392 end
33863393
3387- defp map_literal_intersection_open_closed ( t1 , t2 ) do
3394+ defp map_literal_intersection_open_closed ( t1 , t2 , _intersection_fun ) do
33883395 if Enum . all? ( t1 , fn { _ , v } -> match? ( % { optional: 1 } , v ) end ) do
33893396 t2
33903397 else
33913398 throw ( :empty )
33923399 end
33933400 end
33943401
3395- defp map_literal_intersection_closed ( [ { k1 , v1 } | t1 ] , [ { k2 , _ } | _ ] = l2 ) when k1 < k2 do
3402+ defp map_literal_intersection_closed ( [ { k1 , v1 } | t1 ] , [ { k2 , _ } | _ ] = l2 , intersection_fun )
3403+ when k1 < k2 do
33963404 if is_optional_static ( v1 ) do
3397- map_literal_intersection_closed ( t1 , l2 )
3405+ map_literal_intersection_closed ( t1 , l2 , intersection_fun )
33983406 else
33993407 throw ( :empty )
34003408 end
34013409 end
34023410
3403- defp map_literal_intersection_closed ( [ { k1 , _ } | _ ] = l1 , [ { k2 , v2 } | t2 ] ) when k1 > k2 do
3411+ defp map_literal_intersection_closed ( [ { k1 , _ } | _ ] = l1 , [ { k2 , v2 } | t2 ] , intersection_fun )
3412+ when k1 > k2 do
34043413 if is_optional_static ( v2 ) do
3405- map_literal_intersection_closed ( l1 , t2 )
3414+ map_literal_intersection_closed ( l1 , t2 , intersection_fun )
34063415 else
34073416 throw ( :empty )
34083417 end
34093418 end
34103419
3411- defp map_literal_intersection_closed ( [ { key , v1 } | t1 ] , [ { _ , v2 } | t2 ] ) do
3412- [ { key , non_empty_intersection! ( v1 , v2 ) } | map_literal_intersection_closed ( t1 , t2 ) ]
3420+ defp map_literal_intersection_closed ( [ { key , v1 } | t1 ] , [ { _ , v2 } | t2 ] , intersection_fun ) do
3421+ [
3422+ { key , non_empty_intersection! ( v1 , v2 , intersection_fun ) }
3423+ | map_literal_intersection_closed ( t1 , t2 , intersection_fun )
3424+ ]
34133425 end
34143426
3415- defp map_literal_intersection_closed ( t1 , t2 ) do
3427+ defp map_literal_intersection_closed ( t1 , t2 , _intersection_fun ) do
34163428 if Enum . any? ( t1 , fn { _ , v } -> not is_optional_static ( v ) end ) or
34173429 Enum . any? ( t2 , fn { _ , v } -> not is_optional_static ( v ) end ) do
34183430 throw ( :empty )
@@ -3421,8 +3433,8 @@ defmodule Module.Types.Descr do
34213433 [ ]
34223434 end
34233435
3424- defp non_empty_intersection! ( type1 , type2 ) do
3425- type = opt_intersection ( type1 , type2 )
3436+ defp non_empty_intersection! ( type1 , type2 , intersection_fun ) do
3437+ type = intersection_fun . ( type1 , type2 )
34263438 if empty? ( type ) , do: throw ( :empty ) , else: type
34273439 end
34283440
@@ -4481,10 +4493,10 @@ defmodule Module.Types.Descr do
44814493 { [ ] , [ ] , nil , to_domain_keys ( key_descr ) , [ ] }
44824494 end
44834495
4484- defp non_empty_map_literals_intersection ( maps ) do
4496+ defp non_empty_map_literals_intersection ( maps , intersection_fun \\ & bare_intersection / 2 ) do
44854497 try do
44864498 Enum . reduce ( maps , { :open , [ ] } , fn bdd_leaf ( next_tag , next_fields ) , { tag , fields } ->
4487- map_literal_intersection ( tag , fields , next_tag , next_fields )
4499+ map_literal_intersection ( tag , fields , next_tag , next_fields , intersection_fun )
44884500 end )
44894501 catch
44904502 :empty -> :empty
@@ -4589,8 +4601,8 @@ defmodule Module.Types.Descr do
45894601 end
45904602
45914603 defp map_line_meet_empty? ( key , type , neg_type , t1 , t2 , tag , neg_tag , acc_meet , negs ) do
4592- diff = opt_difference ( type , neg_type )
4593- meet = opt_intersection ( type , neg_type )
4604+ diff = bare_difference ( type , neg_type )
4605+ meet = bare_intersection ( type , neg_type )
45944606
45954607 ( empty? ( diff ) or map_line_empty? ( tag , Enum . reverse ( acc_meet , [ { key , diff } | t1 ] ) , negs ) ) and
45964608 ( empty? ( meet ) or map_line_meet_empty? ( t1 , t2 , tag , neg_tag , [ { key , meet } | acc_meet ] , negs ) )
@@ -4646,7 +4658,7 @@ defmodule Module.Types.Descr do
46464658 end
46474659
46484660 defp map_line_fields_empty_recur? ( key , v1 , v2 , tag , fields , negs ) do
4649- diff = opt_difference ( v1 , v2 )
4661+ diff = bare_difference ( v1 , v2 )
46504662 empty? ( diff ) or map_line_empty? ( tag , fields_store ( key , diff , fields ) , negs )
46514663 end
46524664
@@ -4877,7 +4889,7 @@ defmodule Module.Types.Descr do
48774889 { tag1 , fields1 , [ ] } = map
48784890 { tag2 , fields2 , [ ] } = candidate
48794891
4880- case maybe_optimize_map_union ( tag1 , fields1 , tag2 , fields2 ) do
4892+ case opt_map_union ( tag1 , fields1 , tag2 , fields2 ) do
48814893 nil -> [ candidate | map_fuse_with_first_fusible ( map , rest ) ]
48824894 # we found a fusible candidate, we're done
48834895 { tag , fields } -> [ { tag , fields , [ ] } | rest ]
@@ -5149,20 +5161,26 @@ defmodule Module.Types.Descr do
51495161 do: bdd_intersection ( bdd1 , bdd2 , & tuple_leaf_intersection / 2 )
51505162
51515163 defp tuple_leaf_intersection ( bdd_leaf ( tag1 , elements1 ) , bdd_leaf ( tag2 , elements2 ) ) do
5152- case tuple_literal_intersection ( tag1 , elements1 , tag2 , elements2 ) do
5164+ case tuple_literal_intersection ( tag1 , elements1 , tag2 , elements2 , & opt_intersection / 2 ) do
51535165 { tag , elements } -> bdd_leaf_new ( tag , elements )
51545166 :empty -> :bdd_bot
51555167 end
51565168 end
51575169
5158- defp tuple_literal_intersection ( tag1 , elements1 , tag2 , elements2 ) do
5170+ defp tuple_literal_intersection (
5171+ tag1 ,
5172+ elements1 ,
5173+ tag2 ,
5174+ elements2 ,
5175+ intersection_fun
5176+ ) do
51595177 case tuple_sizes_strategy ( tag1 , length ( elements1 ) , tag2 , length ( elements2 ) ) do
51605178 :disjoint ->
51615179 :empty
51625180
51635181 _ ->
51645182 try do
5165- zip_non_empty_intersection! ( elements1 , elements2 , [ ] )
5183+ zip_non_empty_intersection! ( elements1 , elements2 , [ ] , intersection_fun )
51665184 catch
51675185 :empty -> :empty
51685186 else
@@ -5173,18 +5191,26 @@ defmodule Module.Types.Descr do
51735191 end
51745192
51755193 # Intersects two lists of types, and _appends_ the extra elements to the result.
5176- defp zip_non_empty_intersection! ( [ ] , types2 , acc ) , do: Enum . reverse ( acc , types2 )
5177- defp zip_non_empty_intersection! ( types1 , [ ] , acc ) , do: Enum . reverse ( acc , types1 )
5178-
5179- defp zip_non_empty_intersection! ( [ type1 | rest1 ] , [ type2 | rest2 ] , acc ) do
5180- zip_non_empty_intersection! ( rest1 , rest2 , [ non_empty_intersection! ( type1 , type2 ) | acc ] )
5194+ defp zip_non_empty_intersection! ( [ ] , types2 , acc , _intersection_fun ) ,
5195+ do: Enum . reverse ( acc , types2 )
5196+
5197+ defp zip_non_empty_intersection! ( types1 , [ ] , acc , _intersection_fun ) ,
5198+ do: Enum . reverse ( acc , types1 )
5199+
5200+ defp zip_non_empty_intersection! ( [ type1 | rest1 ] , [ type2 | rest2 ] , acc , intersection_fun ) do
5201+ zip_non_empty_intersection! (
5202+ rest1 ,
5203+ rest2 ,
5204+ [ non_empty_intersection! ( type1 , type2 , intersection_fun ) | acc ] ,
5205+ intersection_fun
5206+ )
51815207 end
51825208
51835209 defp zip_empty_intersection? ( [ ] , _types2 ) , do: false
51845210 defp zip_empty_intersection? ( _types1 , [ ] ) , do: false
51855211
51865212 defp zip_empty_intersection? ( [ type1 | rest1 ] , [ type2 | rest2 ] ) do
5187- case empty? ( opt_intersection ( type1 , type2 ) ) do
5213+ case empty? ( bare_intersection ( type1 , type2 ) ) do
51885214 true -> true
51895215 false -> zip_empty_intersection? ( rest1 , rest2 )
51905216 end
@@ -5234,7 +5260,7 @@ defmodule Module.Types.Descr do
52345260 if subtype? , do: :subtype , else: :none
52355261 end
52365262
5237- defp non_empty_tuple_literals_intersection ( tuples ) do
5263+ defp non_empty_tuple_literals_intersection ( tuples , intersection_fun \\ & bare_intersection / 2 ) do
52385264 try do
52395265 Enum . reduce ( tuples , { :open , [ ] } , fn bdd_leaf ( tag1 , elements1 ) , { tag2 , elements2 } ->
52405266 case tuple_sizes_strategy ( tag1 , length ( elements1 ) , tag2 , length ( elements2 ) ) do
@@ -5243,7 +5269,7 @@ defmodule Module.Types.Descr do
52435269
52445270 _ ->
52455271 tag = if tag1 == :open and tag2 == :open , do: :open , else: :closed
5246- { tag , zip_intersection ( elements1 , elements2 , [ ] ) }
5272+ { tag , zip_intersection ( elements1 , elements2 , [ ] , intersection_fun ) }
52475273 end
52485274 end )
52495275 catch
@@ -5258,11 +5284,11 @@ defmodule Module.Types.Descr do
52585284 end
52595285 end
52605286
5261- defp zip_intersection ( [ ] , types2 , acc ) , do: Enum . reverse ( acc , types2 )
5262- defp zip_intersection ( types1 , [ ] , acc ) , do: Enum . reverse ( acc , types1 )
5287+ defp zip_intersection ( [ ] , types2 , acc , _intersection_fun ) , do: Enum . reverse ( acc , types2 )
5288+ defp zip_intersection ( types1 , [ ] , acc , _intersection_fun ) , do: Enum . reverse ( acc , types1 )
52635289
5264- defp zip_intersection ( [ type1 | rest1 ] , [ type2 | rest2 ] , acc ) do
5265- zip_intersection ( rest1 , rest2 , [ opt_intersection ( type1 , type2 ) | acc ] )
5290+ defp zip_intersection ( [ type1 | rest1 ] , [ type2 | rest2 ] , acc , intersection_fun ) do
5291+ zip_intersection ( rest1 , rest2 , [ intersection_fun . ( type1 , type2 ) | acc ] , intersection_fun )
52665292 end
52675293
52685294 defp tuple_empty? ( bdd ) do
@@ -5306,8 +5332,8 @@ defmodule Module.Types.Descr do
53065332 defp tuple_elements_empty? ( acc_meet , tag , elements , [ neg_type | neg_elements ] , negs ) do
53075333 # Handles the case where {tag, elements} is an open tuple, like {:open, []}
53085334 { ty , elements } = List . pop_at ( elements , 0 , term ( ) )
5309- diff = opt_difference ( ty , neg_type )
5310- meet = opt_intersection ( ty , neg_type )
5335+ diff = bare_difference ( ty , neg_type )
5336+ meet = bare_intersection ( ty , neg_type )
53115337
53125338 # In this case, there is no intersection between the positive and this negative.
53135339 # So we should just "go next"
@@ -5376,9 +5402,9 @@ defmodule Module.Types.Descr do
53765402 end
53775403
53785404 # ti \ ui (i.e., ti and not ui)
5379- diff = opt_difference ( ty , neg_type )
5405+ diff = bare_difference ( ty , neg_type )
53805406 # ti /\ ui
5381- meet = opt_intersection ( ty , neg_type )
5407+ meet = bare_intersection ( ty , neg_type )
53825408
53835409 # Branch where the earliest difference is *here*.
53845410 # Earlier positions are the accumulated matches in `acc`;
@@ -5430,13 +5456,17 @@ defmodule Module.Types.Descr do
54305456 defp tuple_union ( _ , bdd_leaf ( :open , [ ] ) = leaf ) , do: leaf
54315457 defp tuple_union ( bdd1 , bdd2 ) , do: bdd_union ( bdd1 , bdd2 )
54325458
5459+ defp opt_tuple_union ( { tag1 , elements1 } , { tag2 , elements2 } ) do
5460+ maybe_optimize_tuple_union ( { tag1 , elements1 } , { tag2 , elements2 } )
5461+ end
5462+
54335463 defp opt_tuple_union (
5434- bdd_leaf ( tag1 , elements1 ) = tuple1 ,
5435- bdd_leaf ( tag2 , elements2 ) = tuple2
5464+ bdd_leaf ( tag1 , elements1 ) ,
5465+ bdd_leaf ( tag2 , elements2 )
54365466 ) do
54375467 case maybe_optimize_tuple_union ( { tag1 , elements1 } , { tag2 , elements2 } ) do
54385468 { tag , elements } -> bdd_leaf_new ( tag , elements )
5439- nil -> bdd_union ( tuple1 , tuple2 )
5469+ nil -> bdd_union ( bdd_leaf_new ( tag1 , elements1 ) , bdd_leaf_new ( tag2 , elements2 ) )
54405470 end
54415471 end
54425472
@@ -5590,7 +5620,7 @@ defmodule Module.Types.Descr do
55905620 defp tuple_fuse_with_first_fusible ( tuple , [ ] ) , do: [ tuple ]
55915621
55925622 defp tuple_fuse_with_first_fusible ( tuple , [ candidate | rest ] ) do
5593- if fused = maybe_optimize_tuple_union ( tuple , candidate ) do
5623+ if fused = opt_tuple_union ( tuple , candidate ) do
55945624 # we found a fusible candidate, we're done
55955625 [ fused | rest ]
55965626 else
0 commit comments