@@ -528,15 +528,19 @@ defmodule Module.Types.Descr do
528528    if  term_type? ( descr )  do 
529529      { :term ,  [ ] ,  [ ] } 
530530    else 
531-       # Dynamic always come first for visibility 
532-       { dynamic ,  static }  = 
531+       { dynamic ,  static ,  extra }  = 
533532        case  :maps . take ( :dynamic ,  descr )  do 
534-           :error  ->  { % { } ,  descr } 
535-           { :term ,  static }  ->  { :term ,  static } 
536-           { dynamic ,  static }  ->  { difference ( dynamic ,  static ) ,  static } 
537-         end 
533+           :error  -> 
534+             { % { } ,  descr ,  [ ] } 
535+ 
536+           { :term ,  static }  -> 
537+             { :term ,  static ,  [ ] } 
538538
539-       { static ,  dynamic ,  extra }  =  fun_denormalize ( static ,  dynamic ,  opts ) 
539+           { dynamic ,  static }  -> 
540+             # Denormalize functions before we do the difference 
541+             { static ,  dynamic ,  extra }  =  fun_denormalize ( static ,  dynamic ,  opts ) 
542+             { difference ( dynamic ,  static ) ,  static ,  extra } 
543+         end 
540544
541545      # Merge empty list and list together if they both exist 
542546      { extra ,  static }  = 
@@ -553,6 +557,7 @@ defmodule Module.Types.Descr do
553557            { extra ,  static } 
554558        end 
555559
560+       # Dynamic always come first for visibility 
556561      unions  = 
557562        to_quoted ( :dynamic ,  dynamic ,  opts )  ++ 
558563          Enum . sort ( 
@@ -1464,6 +1469,42 @@ defmodule Module.Types.Descr do
14641469  end 
14651470
14661471  defp  fun_intersection ( bdd1 ,  bdd2 )  do 
1472+     # If intersecting with the top type for that arity, no-op 
1473+     case  { bdd1 ,  bdd2 }  do 
1474+       { bdd ,  { { args ,  return }  =  fun ,  :fun_top ,  :fun_bottom } }  when  is_tuple ( bdd )  -> 
1475+         if  return  ==  :term  and  Enum . all? ( args ,  & ( & 1  ==  % { } ) )  and 
1476+              matching_arity_left? ( bdd ,  length ( args ) )  do 
1477+           bdd 
1478+         else 
1479+           { fun ,  bdd ,  :fun_bottom } 
1480+         end 
1481+ 
1482+       { { { args ,  return }  =  fun ,  :fun_top ,  :fun_bottom } ,  bdd }  when  is_tuple ( bdd )  -> 
1483+         if  return  ==  :term  and  Enum . all? ( args ,  & ( & 1  ==  % { } ) )  and 
1484+              matching_arity_left? ( bdd ,  length ( args ) )  do 
1485+           bdd 
1486+         else 
1487+           { fun ,  bdd ,  :fun_bottom } 
1488+         end 
1489+ 
1490+       _  -> 
1491+         fun_intersection_recur ( bdd1 ,  bdd2 ) 
1492+     end 
1493+   end 
1494+ 
1495+   defp  matching_arity_left? ( { { args ,  _return } ,  l ,  r } ,  arity )  do 
1496+     length ( args )  ==  arity  and  matching_arity_left? ( l ,  arity )  and  matching_arity_right? ( r ,  arity ) 
1497+   end 
1498+ 
1499+   defp  matching_arity_left? ( _ ,  _arity ) ,  do:  true 
1500+ 
1501+   defp  matching_arity_right? ( { _ ,  l ,  r } ,  arity )  do 
1502+     matching_arity_left? ( l ,  arity )  and  matching_arity_right? ( r ,  arity ) 
1503+   end 
1504+ 
1505+   defp  matching_arity_right? ( _ ,  _arity ) ,  do:  true 
1506+ 
1507+   defp  fun_intersection_recur ( bdd1 ,  bdd2 )  do 
14671508    case  { bdd1 ,  bdd2 }  do 
14681509      # Base cases 
14691510      { _ ,  :fun_bottom }  -> 
@@ -1482,45 +1523,27 @@ defmodule Module.Types.Descr do
14821523      # If intersecting with a single positive or negative function, we insert 
14831524      # it at the root instead of merging the trees (this avoids going down the 
14841525      # whole bdd). 
1485-       { bdd ,  { { args ,  return }  =  fun ,  :fun_top ,  :fun_bottom } }  -> 
1486-         # If intersecting with the top type for that arity, no-op 
1487-         if  return  ==  :term  and  Enum . all? ( args ,  & ( & 1  ==  % { } ) )  and 
1488-              matching_arity? ( bdd ,  length ( args ) )  do 
1489-           bdd 
1490-         else 
1491-           { fun ,  bdd ,  :fun_bottom } 
1492-         end 
1526+       { bdd ,  { fun ,  :fun_top ,  :fun_bottom } }  -> 
1527+         { fun ,  bdd ,  :fun_bottom } 
14931528
14941529      { bdd ,  { fun ,  :fun_bottom ,  :fun_top } }  -> 
14951530        { fun ,  :fun_bottom ,  bdd } 
14961531
1497-       { { { args ,  return }  =  fun ,  :fun_top ,  :fun_bottom } ,  bdd }  -> 
1498-         # If intersecting with the top type for that arity, no-op 
1499-         if  return  ==  :term  and  Enum . all? ( args ,  & ( & 1  ==  % { } ) )  and 
1500-              matching_arity? ( bdd ,  length ( args ) )  do 
1501-           bdd 
1502-         else 
1503-           { fun ,  bdd ,  :fun_bottom } 
1504-         end 
1532+       { { fun ,  :fun_top ,  :fun_bottom } ,  bdd }  -> 
1533+         { fun ,  bdd ,  :fun_bottom } 
15051534
15061535      { { fun ,  :fun_bottom ,  :fun_top } ,  bdd }  -> 
15071536        { fun ,  :fun_bottom ,  bdd } 
15081537
15091538      # General cases 
15101539      { { fun ,  l1 ,  r1 } ,  { fun ,  l2 ,  r2 } }  -> 
1511-         { fun ,  fun_intersection ( l1 ,  l2 ) ,  fun_intersection ( r1 ,  r2 ) } 
1540+         { fun ,  fun_intersection_recur ( l1 ,  l2 ) ,  fun_intersection_recur ( r1 ,  r2 ) } 
15121541
15131542      { { fun ,  l ,  r } ,  bdd }  -> 
1514-         { fun ,  fun_intersection ( l ,  bdd ) ,  fun_intersection ( r ,  bdd ) } 
1543+         { fun ,  fun_intersection_recur ( l ,  bdd ) ,  fun_intersection_recur ( r ,  bdd ) } 
15151544    end 
15161545  end 
15171546
1518-   defp  matching_arity? ( { { args ,  _return } ,  l ,  r } ,  arity )  do 
1519-     length ( args )  ==  arity  and  matching_arity? ( l ,  arity )  and  matching_arity? ( r ,  arity ) 
1520-   end 
1521- 
1522-   defp  matching_arity? ( _ ,  _arity ) ,  do:  true 
1523- 
15241547  defp  fun_difference ( bdd1 ,  bdd2 )  do 
15251548    case  { bdd1 ,  bdd2 }  do 
15261549      { :fun_bottom ,  _ }  ->  :fun_bottom 
@@ -1541,7 +1564,7 @@ defmodule Module.Types.Descr do
15411564    dynamic_pos  =  fun_get_pos ( dynamic_bdd ) 
15421565
15431566    if  static_pos  !=  [ ]  and  dynamic_pos  !=  [ ]  do 
1544-       { dynamic_pos ,   static_pos }  =  fun_denormalize_pos ( dynamic_pos ,   static_pos ) 
1567+       { static_pos ,   dynamic_pos }  =  fun_denormalize_pos ( static_pos ,   dynamic_pos ) 
15451568
15461569      quoted  = 
15471570        if  dynamic_pos  ==  [ ]  do 
@@ -1564,50 +1587,51 @@ defmodule Module.Types.Descr do
15641587    { static ,  dynamic ,  [ ] } 
15651588  end 
15661589
1567-   defp  fun_denormalize_pos ( dynamic_unions ,   static_unions )  do 
1568-     Enum . reduce ( dynamic_unions ,   { [ ] ,   static_unions } ,  fn 
1590+   defp  fun_denormalize_pos ( static_unions ,   dynamic_unions )  do 
1591+     Enum . map_reduce ( static_unions ,   dynamic_unions ,  fn 
15691592      # Handle fun() types accordingly 
1570-       [ ] ,  { dynamic_unions ,  static_unions }  -> 
1571-         { [ [ ]  |  dynamic_unions ] ,  static_unions } 
1572- 
1573-       dynamic_intersections ,  { dynamic_unions ,  static_unions }  -> 
1574-         { dynamic_intersections ,  static_unions }  = 
1575-           Enum . reduce ( dynamic_intersections ,  { [ ] ,  static_unions } ,  fn 
1576-             { args ,  return } ,  { acc ,  static_unions }  -> 
1577-               case  fun_denormalize_arrow ( args ,  return ,  static_unions )  do 
1578-                 { :ok ,  static_unions }  ->  { acc ,  static_unions } 
1579-                 :error  ->  { [ { args ,  return }  |  acc ] ,  static_unions } 
1580-               end 
1581-           end ) 
1593+       [ ] ,  dynamic_unions  -> 
1594+         { [ ] ,  List . delete ( dynamic_unions ,  [ ] ) } 
15821595
1583-          if   dynamic_intersections   ==   [ ]   do 
1584-            { dynamic_unions ,  static_unions } 
1585-         else 
1586-           { [ dynamic_intersections   |   dynamic_unions ] ,   static_unions } 
1596+       static_intersections ,   dynamic_unions   -> 
1597+         case   pivot ( dynamic_unions ,  [ ] ,   & fun_denormalize_intersections ( static_intersections ,   & 1 ) )   do 
1598+            { match ,   dynamic_unions }   ->   { match ,   dynamic_unions } 
1599+           :error   ->   { static_intersections ,   dynamic_unions } 
15871600        end 
15881601    end ) 
15891602  end 
15901603
1591-   defp  fun_denormalize_arrow ( dynamic_args ,  dynamic_return ,  static_unions )  do 
1592-     pivot ( static_unions ,  [ ] ,  fn  static_intersections  -> 
1593-       pivot ( static_intersections ,  [ ] ,  fn  { static_args ,  static_return }  -> 
1594-         if  subtype? ( static_return ,  dynamic_return )  and  args_subtype? ( dynamic_args ,  static_args )  do 
1595-           args  = 
1596-             Enum . zip_with ( static_args ,  dynamic_args ,  fn  static_arg ,  dynamic_arg  -> 
1597-               union ( dynamic ( difference ( static_arg ,  dynamic_arg ) ) ,  dynamic_arg ) 
1598-             end ) 
1604+   defp  fun_denormalize_intersections ( statics ,  dynamics )  do 
1605+     if  length ( statics )  ==  length ( dynamics )  do 
1606+       fun_denormalize_intersections ( statics ,  dynamics ,  [ ] ) 
1607+     else 
1608+       :error 
1609+     end 
1610+   end 
15991611
1600-           return  =  union ( dynamic ( difference ( dynamic_return ,  static_return ) ) ,  static_return ) 
1601-           { :ok ,  { args ,  return } } 
1602-         else 
1603-           :error 
1604-         end 
1605-       end ) 
1606-     end ) 
1612+   # We assume those pairs are always formed in the same order 
1613+   defp  fun_denormalize_intersections ( 
1614+          [ { static_args ,  static_return }  |  statics ] , 
1615+          [ { dynamic_args ,  dynamic_return }  |  dynamics ] , 
1616+          acc 
1617+        )  do 
1618+     if  subtype? ( static_return ,  dynamic_return )  and  args_subtype? ( dynamic_args ,  static_args )  do 
1619+       args  = 
1620+         Enum . zip_with ( static_args ,  dynamic_args ,  fn  static_arg ,  dynamic_arg  -> 
1621+           union ( dynamic ( static_arg ) ,  dynamic_arg ) 
1622+         end ) 
1623+ 
1624+       return  =  union ( dynamic ( dynamic_return ) ,  static_return ) 
1625+       fun_denormalize_intersections ( statics ,  dynamics ,  [ { args ,  return }  |  acc ] ) 
1626+     else 
1627+       :error 
1628+     end 
16071629  end 
16081630
1631+   defp  fun_denormalize_intersections ( [ ] ,  [ ] ,  acc ) ,  do:  { :ok ,  acc } 
1632+ 
16091633  defp  arrow_subtype? ( left_args ,  left_return ,  right_args ,  right_return )  do 
1610-     subtype? ( right_return ,   left_return )  and  args_subtype? ( left_args ,   right_args ) 
1634+     subtype? ( left_return ,   right_return )  and  args_subtype? ( right_args ,   left_args ) 
16111635  end 
16121636
16131637  defp  args_subtype? ( left_args ,  right_args )  do 
@@ -1618,7 +1642,7 @@ defmodule Module.Types.Descr do
16181642
16191643  defp  pivot ( [ head  |  tail ] ,  acc ,  fun )  do 
16201644    case  fun . ( head )  do 
1621-       { :ok ,  value }  ->  { :ok ,  acc  ++  [ value   |   tail ] } 
1645+       { :ok ,  value }  ->  { value ,  acc  ++  tail } 
16221646      :error  ->  pivot ( tail ,  [ head  |  acc ] ,  fun ) 
16231647    end 
16241648  end 
0 commit comments