@@ -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