@@ -527,28 +527,34 @@ defmodule Module.Types.Descr do
527527 { :term , [ ] , [ ] }
528528 else
529529 # Dynamic always come first for visibility
530- { dynamic , descr } =
530+ { dynamic , static } =
531531 case :maps . take ( :dynamic , descr ) do
532- :error -> { [ ] , descr }
533- { :term , descr } -> { to_quoted ( :dynamic , : term, opts ) , descr }
534- { dynamic , descr } -> { to_quoted ( :dynamic , difference ( dynamic , descr ) , opts ) , descr }
532+ :error -> { % { } , descr }
533+ { :term , static } -> { : term, static }
534+ { dynamic , static } -> { difference ( dynamic , static ) , static }
535535 end
536536
537+ { static , dynamic , extra } = fun_denormalize ( static , dynamic , opts )
538+
537539 # Merge empty list and list together if they both exist
538- { extra , descr } =
539- case descr do
540+ { extra , static } =
541+ case static do
540542 % { list: list , bitmap: bitmap } when ( bitmap &&& @ bit_empty_list ) != 0 ->
541- descr = descr |> Map . delete ( :list ) |> Map . replace! ( :bitmap , bitmap - @ bit_empty_list )
542- { list_to_quoted ( list , true , opts ) , descr }
543+ static =
544+ static
545+ |> Map . delete ( :list )
546+ |> Map . replace! ( :bitmap , bitmap - @ bit_empty_list )
547+
548+ { list_to_quoted ( list , true , opts ) ++ extra , static }
543549
544550 % { } ->
545- { [ ] , descr }
551+ { extra , static }
546552 end
547553
548554 unions =
549- dynamic ++
555+ to_quoted ( : dynamic, dynamic , opts ) ++
550556 Enum . sort (
551- extra ++ Enum . flat_map ( descr , fn { key , value } -> to_quoted ( key , value , opts ) end )
557+ extra ++ Enum . flat_map ( static , fn { key , value } -> to_quoted ( key , value , opts ) end )
552558 )
553559
554560 case unions do
@@ -564,7 +570,7 @@ defmodule Module.Types.Descr do
564570 defp to_quoted ( :map , dnf , opts ) , do: map_to_quoted ( dnf , opts )
565571 defp to_quoted ( :list , dnf , opts ) , do: list_to_quoted ( dnf , false , opts )
566572 defp to_quoted ( :tuple , dnf , opts ) , do: tuple_to_quoted ( dnf , opts )
567- defp to_quoted ( :fun , dnf , opts ) , do: fun_to_quoted ( dnf , opts )
573+ defp to_quoted ( :fun , bdd , opts ) , do: fun_to_quoted ( bdd , opts )
568574
569575 @ doc """
570576 Converts a descr to its quoted string representation.
@@ -1488,21 +1494,140 @@ defmodule Module.Types.Descr do
14881494 end
14891495 end
14901496
1491- # Converts a function BDD (Binary Decision Diagram) to its quoted representation.
1492- defp fun_to_quoted ( bdd , opts ) do
1493- arrows = fun_get ( bdd )
1497+ # Converts the static and dynamic parts of descr to its quoted
1498+ # representation. The goal here is to the opposite of fun_descr
1499+ # and put static and dynamic parts back together to improve
1500+ # pretty printing.
1501+ defp fun_denormalize ( % { fun: static_bdd } = static , % { fun: dynamic_bdd } = dynamic , opts ) do
1502+ static_pos = fun_get_pos ( static_bdd )
1503+ dynamic_pos = fun_get_pos ( dynamic_bdd )
1504+
1505+ if static_pos != [ ] and dynamic_pos != [ ] do
1506+ { dynamic_pos , static_pos } = fun_denormalize_pos ( dynamic_pos , static_pos )
14941507
1495- for { positives , negatives } <- arrows , not fun_empty? ( positives , negatives ) do
1496- fun_intersection_to_quoted ( positives , opts )
1508+ quoted =
1509+ if dynamic_pos == [ ] do
1510+ fun_pos_to_quoted ( static_pos , opts )
1511+ else
1512+ { :or , [ ] ,
1513+ [
1514+ { :dynamic , [ ] , [ fun_pos_to_quoted ( dynamic_pos , opts ) ] } ,
1515+ fun_pos_to_quoted ( static_pos , opts )
1516+ ] }
1517+ end
1518+
1519+ { Map . delete ( static , :fun ) , Map . delete ( dynamic , :fun ) , [ quoted ] }
1520+ else
1521+ { static , dynamic , [ ] }
14971522 end
1498- |> case do
1523+ end
1524+
1525+ defp fun_denormalize ( static , dynamic , _opts ) do
1526+ { static , dynamic , [ ] }
1527+ end
1528+
1529+ defp fun_denormalize_pos ( dynamic_unions , static_unions ) do
1530+ Enum . reduce ( dynamic_unions , { [ ] , static_unions } , fn
1531+ # Handle fun() types accordingly
1532+ [ ] , { dynamic_unions , static_unions } ->
1533+ { [ [ ] | dynamic_unions ] , static_unions }
1534+
1535+ dynamic_intersections , { dynamic_unions , static_unions } ->
1536+ { dynamic_intersections , static_unions } =
1537+ Enum . reduce ( dynamic_intersections , { [ ] , static_unions } , fn
1538+ { args , return } , { acc , static_unions } ->
1539+ case fun_denormalize_arrow ( args , return , static_unions ) do
1540+ { :ok , static_unions } -> { acc , static_unions }
1541+ :error -> { [ { args , return } | acc ] , static_unions }
1542+ end
1543+ end )
1544+
1545+ if dynamic_intersections == [ ] do
1546+ { dynamic_unions , static_unions }
1547+ else
1548+ { [ dynamic_intersections | dynamic_unions ] , static_unions }
1549+ end
1550+ end )
1551+ end
1552+
1553+ defp fun_denormalize_arrow ( dynamic_args , dynamic_return , static_unions ) do
1554+ pivot ( static_unions , [ ] , fn static_intersections ->
1555+ pivot ( static_intersections , [ ] , fn { static_args , static_return } ->
1556+ if subtype? ( static_return , dynamic_return ) and args_subtype? ( dynamic_args , static_args ) do
1557+ args =
1558+ Enum . zip_with ( static_args , dynamic_args , fn static_arg , dynamic_arg ->
1559+ union ( dynamic ( difference ( static_arg , dynamic_arg ) ) , dynamic_arg )
1560+ end )
1561+
1562+ return = union ( dynamic ( difference ( dynamic_return , static_return ) ) , static_return )
1563+ { :ok , { args , return } }
1564+ else
1565+ :error
1566+ end
1567+ end )
1568+ end )
1569+ end
1570+
1571+ defp arrow_subtype? ( left_args , left_return , right_args , right_return ) do
1572+ subtype? ( right_return , left_return ) and args_subtype? ( left_args , right_args )
1573+ end
1574+
1575+ defp args_subtype? ( left_args , right_args ) do
1576+ Enum . zip_reduce ( left_args , right_args , true , fn left , right , acc ->
1577+ acc and subtype? ( left , right )
1578+ end )
1579+ end
1580+
1581+ defp pivot ( [ head | tail ] , acc , fun ) do
1582+ case fun . ( head ) do
1583+ { :ok , value } -> { :ok , acc ++ [ value | tail ] }
1584+ :error -> pivot ( tail , [ head | acc ] , fun )
1585+ end
1586+ end
1587+
1588+ defp pivot ( [ ] , _acc , _fun ) , do: :error
1589+
1590+ # Converts a function BDD (Binary Decision Diagram) to its quoted representation
1591+ defp fun_to_quoted ( bdd , opts ) do
1592+ case fun_get_pos ( bdd ) do
14991593 [ ] -> [ ]
1500- multiple -> [ Enum . reduce ( multiple , & { :or , [ ] , [ & 2 , & 1 ] } ) ]
1594+ pos -> [ fun_pos_to_quoted ( pos , opts ) ]
15011595 end
15021596 end
15031597
1598+ defp fun_get_pos ( bdd ) do
1599+ for { pos , negs } <- fun_get ( bdd ) , not fun_empty? ( pos , negs ) do
1600+ fun_filter_subset ( pos , [ ] )
1601+ end
1602+ end
1603+
1604+ # If one arrow is the subset of another arrow in the intersection,
1605+ # we just remove it.
1606+ defp fun_filter_subset ( [ ] , acc ) , do: acc
1607+
1608+ defp fun_filter_subset ( [ { args , return } | tail ] , acc ) do
1609+ if Enum . any? ( tail , fn { other_args , other_return } ->
1610+ arrow_subtype? ( other_args , other_return , args , return )
1611+ end ) or
1612+ Enum . any? ( acc , fn { other_args , other_return } ->
1613+ arrow_subtype? ( other_args , other_return , args , return )
1614+ end ) do
1615+ fun_filter_subset ( tail , acc )
1616+ else
1617+ fun_filter_subset ( tail , [ { args , return } | acc ] )
1618+ end
1619+ end
1620+
1621+ defp fun_pos_to_quoted ( [ _ | _ ] = pos , opts ) do
1622+ pos
1623+ |> Enum . sort ( )
1624+ |> Enum . map ( & fun_intersection_to_quoted ( & 1 , opts ) )
1625+ |> Enum . reduce ( & { :or , [ ] , [ & 2 , & 1 ] } )
1626+ end
1627+
15041628 defp fun_intersection_to_quoted ( intersection , opts ) do
15051629 intersection
1630+ |> Enum . sort ( )
15061631 |> Enum . map ( fn { args , ret } ->
15071632 { :__block__ , [ ] ,
15081633 [ [ { :-> , [ ] , [ Enum . map ( args , & to_quoted ( & 1 , opts ) ) , to_quoted ( ret , opts ) ] } ] ] }
@@ -1904,6 +2029,9 @@ defmodule Module.Types.Descr do
19042029
19052030 defp dynamic_to_quoted ( descr , opts ) do
19062031 cond do
2032+ descr == % { } ->
2033+ [ ]
2034+
19072035 term_type? ( descr ) ->
19082036 [ { :dynamic , [ ] , [ ] } ]
19092037
0 commit comments