@@ -220,9 +220,9 @@ defmodule Module.Types.Descr do
220220  defp  union ( :bitmap ,  v1 ,  v2 ) ,  do:  v1  |||  v2 
221221  defp  union ( :dynamic ,  v1 ,  v2 ) ,  do:  dynamic_union ( v1 ,  v2 ) 
222222  defp  union ( :list ,  v1 ,  v2 ) ,  do:  list_union ( v1 ,  v2 ) 
223-   defp  union ( :map ,  v1 ,  v2 ) ,  do:  map_union ( v1 ,  v2 ) 
223+   defp  union ( :map ,  v1 ,  v2 ) ,  do:  map_or_tuple_union ( v1 ,  v2 ) 
224224  defp  union ( :optional ,  1 ,  1 ) ,  do:  1 
225-   defp  union ( :tuple ,  v1 ,  v2 ) ,  do:  tuple_union ( v1 ,  v2 ) 
225+   defp  union ( :tuple ,  v1 ,  v2 ) ,  do:  map_or_tuple_union ( v1 ,  v2 ) 
226226
227227  @ doc  """ 
228228  Computes the intersection of two descrs. 
@@ -1278,8 +1278,75 @@ defmodule Module.Types.Descr do
12781278
12791279  defp  map_only? ( descr ) ,  do:  empty? ( Map . delete ( descr ,  :map ) ) 
12801280
1281-   # Union is list concatenation 
1282-   defp  map_union ( dnf1 ,  dnf2 ) ,  do:  dnf1  ++  ( dnf2  --  dnf1 ) 
1281+   defp  map_or_tuple_union ( dnf1 ,  dnf2 )  do 
1282+     if  optimized  =  maybe_optimize_union ( dnf1 ,  dnf2 )  do 
1283+       optimized 
1284+     else 
1285+       # Union is list concatenation 
1286+       # Removes duplicates in union, which should trickle to other operations. 
1287+       # This is a cheap optimization that relies on structural equality. 
1288+       dnf1  ++  ( dnf2  --  dnf1 ) 
1289+     end 
1290+   end 
1291+ 
1292+   defp  maybe_optimize_union ( [ { tag1 ,  pos1 ,  [ ] } ]  =  dnf1 ,  [ { tag2 ,  pos2 ,  [ ] } ]  =  dnf2 )  do 
1293+     # Avoid the explosion for unions of very close maps or tuples, 
1294+     # by returning only one if it contains the other 
1295+     # e.g. %{a: integer()} and %{..., a: term()} -> %{..., a: term()} 
1296+     cond  do 
1297+       map_or_tuple_simple_subtype? ( tag1 ,  pos1 ,  tag2 ,  pos2 )  ->  dnf2 
1298+       map_or_tuple_simple_subtype? ( tag2 ,  pos2 ,  tag1 ,  pos1 )  ->  dnf1 
1299+       true  ->  nil 
1300+     end 
1301+   end 
1302+ 
1303+   defp  maybe_optimize_union ( _ ,  _ ) ,  do:  nil 
1304+ 
1305+   defp  map_or_tuple_simple_subtype? ( _ ,  _ ,  :open ,  pos2 )  when  pos2  ==  % { } ,  do:  true 
1306+ 
1307+   defp  map_or_tuple_simple_subtype? ( :closed ,  pos1 ,  :closed ,  pos2 ) 
1308+        when  map_size ( pos1 )  ==  map_size ( pos2 )  do 
1309+     all_key_simple_subtypes? ( pos1 ,  pos2 ) 
1310+   end 
1311+ 
1312+   defp  map_or_tuple_simple_subtype? ( _ ,  pos1 ,  :open ,  pos2 )  when  map_size ( pos1 )  >=  map_size ( pos2 )  do 
1313+     all_key_simple_subtypes? ( pos1 ,  pos2 ) 
1314+   end 
1315+ 
1316+   defp  map_or_tuple_simple_subtype? ( _ ,  _ ,  _ ,  _ ) ,  do:  false 
1317+ 
1318+   defp  all_key_simple_subtypes? ( pos1 ,  pos2 )  do 
1319+     Enum . all? ( pos1 ,  fn  { key ,  type1 }  -> 
1320+       case  pos2  do 
1321+         % { ^ key  =>  type2 }  ->  simple_subtype? ( type1 ,  type2 ) 
1322+         _  ->  false 
1323+       end 
1324+     end ) 
1325+   end 
1326+ 
1327+   defp  simple_subtype? ( _ ,  :term ) ,  do:  true 
1328+   defp  simple_subtype? ( same ,  same ) ,  do:  true 
1329+ 
1330+   defp  simple_subtype? ( type1 ,  type2 )  when  map_size ( type1 )  ==  1  and  map_size ( type2 )  ==  1  do 
1331+     case  { type1 ,  type2 }  do 
1332+       { % { atom:  _ } ,  % { atom:  { :negation ,  neg } } }  when  neg  ==  % { }  -> 
1333+         true 
1334+ 
1335+       { % { bitmap:  bitmap1 } ,  % { bitmap:  bitmap2 } }  -> 
1336+         ( bitmap1  &&&  bitmap2 )  ===  bitmap2 
1337+ 
1338+       { % { map:  [ { tag1 ,  pos1 ,  [ ] } ] } ,  % { map:  [ { tag2 ,  pos2 ,  [ ] } ] } }  -> 
1339+         map_or_tuple_simple_subtype? ( tag1 ,  pos1 ,  tag2 ,  pos2 ) 
1340+ 
1341+       { % { tuple:  [ { tag1 ,  pos1 ,  [ ] } ] } ,  % { tuple:  [ { tag2 ,  pos2 ,  [ ] } ] } }  -> 
1342+         map_or_tuple_simple_subtype? ( tag1 ,  pos1 ,  tag2 ,  pos2 ) 
1343+ 
1344+       _  -> 
1345+         false 
1346+     end 
1347+   end 
1348+ 
1349+   defp  simple_subtype? ( _ ,  _ ) ,  do:  false 
12831350
12841351  # Given two unions of maps, intersects each pair of maps. 
12851352  defp  map_intersection ( dnf1 ,  dnf2 )  do 
@@ -2049,10 +2116,6 @@ defmodule Module.Types.Descr do
20492116    end 
20502117  end 
20512118
2052-   # Removes duplicates in union, which should trickle to other operations. 
2053-   # This is a cheap optimization that relies on structural equality. 
2054-   defp  tuple_union ( left ,  right ) ,  do:  left  ++  ( right  --  left ) 
2055- 
20562119  defp  tuple_to_quoted ( dnf ,  opts )  do 
20572120    dnf 
20582121    |>  tuple_simplify ( ) 
0 commit comments