@@ -528,15 +528,19 @@ defmodule Module.Types.Descr do
528
528
if term_type? ( descr ) do
529
529
{ :term , [ ] , [ ] }
530
530
else
531
- # Dynamic always come first for visibility
532
- { dynamic , static } =
531
+ { dynamic , static , extra } =
533
532
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 , [ ] }
538
538
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
540
544
541
545
# Merge empty list and list together if they both exist
542
546
{ extra , static } =
@@ -553,6 +557,7 @@ defmodule Module.Types.Descr do
553
557
{ extra , static }
554
558
end
555
559
560
+ # Dynamic always come first for visibility
556
561
unions =
557
562
to_quoted ( :dynamic , dynamic , opts ) ++
558
563
Enum . sort (
@@ -1464,6 +1469,42 @@ defmodule Module.Types.Descr do
1464
1469
end
1465
1470
1466
1471
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
1467
1508
case { bdd1 , bdd2 } do
1468
1509
# Base cases
1469
1510
{ _ , :fun_bottom } ->
@@ -1482,45 +1523,27 @@ defmodule Module.Types.Descr do
1482
1523
# If intersecting with a single positive or negative function, we insert
1483
1524
# it at the root instead of merging the trees (this avoids going down the
1484
1525
# 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 }
1493
1528
1494
1529
{ bdd , { fun , :fun_bottom , :fun_top } } ->
1495
1530
{ fun , :fun_bottom , bdd }
1496
1531
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 }
1505
1534
1506
1535
{ { fun , :fun_bottom , :fun_top } , bdd } ->
1507
1536
{ fun , :fun_bottom , bdd }
1508
1537
1509
1538
# General cases
1510
1539
{ { 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 ) }
1512
1541
1513
1542
{ { 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 ) }
1515
1544
end
1516
1545
end
1517
1546
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
-
1524
1547
defp fun_difference ( bdd1 , bdd2 ) do
1525
1548
case { bdd1 , bdd2 } do
1526
1549
{ :fun_bottom , _ } -> :fun_bottom
@@ -1541,7 +1564,7 @@ defmodule Module.Types.Descr do
1541
1564
dynamic_pos = fun_get_pos ( dynamic_bdd )
1542
1565
1543
1566
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 )
1545
1568
1546
1569
quoted =
1547
1570
if dynamic_pos == [ ] do
@@ -1564,50 +1587,51 @@ defmodule Module.Types.Descr do
1564
1587
{ static , dynamic , [ ] }
1565
1588
end
1566
1589
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
1569
1592
# 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 , [ ] ) }
1582
1595
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 }
1587
1600
end
1588
1601
end )
1589
1602
end
1590
1603
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
1599
1611
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
1607
1629
end
1608
1630
1631
+ defp fun_denormalize_intersections ( [ ] , [ ] , acc ) , do: { :ok , acc }
1632
+
1609
1633
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 )
1611
1635
end
1612
1636
1613
1637
defp args_subtype? ( left_args , right_args ) do
@@ -1618,7 +1642,7 @@ defmodule Module.Types.Descr do
1618
1642
1619
1643
defp pivot ( [ head | tail ] , acc , fun ) do
1620
1644
case fun . ( head ) do
1621
- { :ok , value } -> { :ok , acc ++ [ value | tail ] }
1645
+ { :ok , value } -> { value , acc ++ tail }
1622
1646
:error -> pivot ( tail , [ head | acc ] , fun )
1623
1647
end
1624
1648
end
0 commit comments