@@ -372,11 +372,18 @@ defmodule Module.Types.Descr do
372
372
if term_type? ( descr ) do
373
373
{ :term , [ ] , [ ] }
374
374
else
375
+ # Dynamic always come first for visibility
376
+ { dynamic , descr } =
377
+ case :maps . take ( :dynamic , descr ) do
378
+ :error -> { [ ] , descr }
379
+ { dynamic , descr } -> { to_quoted ( :dynamic , dynamic ) , descr }
380
+ end
381
+
382
+ # Merge empty list and list together if they both exist
375
383
{ extra , descr } =
376
384
case descr do
377
- # Merge empty list and list together if they both exist
378
385
% { list: list , bitmap: bitmap } when ( bitmap &&& @ bit_empty_list ) != 0 ->
379
- descr = descr |> Map . delete ( :list ) |> Map . update !( :bitmap , & ( & 1 - @ bit_empty_list ) )
386
+ descr = descr |> Map . delete ( :list ) |> Map . replace !( :bitmap , bitmap - @ bit_empty_list )
380
387
381
388
case list_to_quoted ( list , :list ) do
382
389
[ ] -> { [ { :empty_list , [ ] , [ ] } ] , descr }
@@ -387,9 +394,13 @@ defmodule Module.Types.Descr do
387
394
{ [ ] , descr }
388
395
end
389
396
390
- case extra ++ Enum . flat_map ( descr , fn { key , value } -> to_quoted ( key , value ) end ) do
397
+ unions =
398
+ dynamic ++
399
+ Enum . sort ( extra ++ Enum . flat_map ( descr , fn { key , value } -> to_quoted ( key , value ) end ) )
400
+
401
+ case unions do
391
402
[ ] -> { :none , [ ] , [ ] }
392
- unions -> unions |> Enum . sort ( ) |> Enum . reduce ( & { :or , [ ] , [ & 2 , & 1 ] } )
403
+ unions -> Enum . reduce ( unions , & { :or , [ ] , [ & 2 , & 1 ] } )
393
404
end
394
405
end
395
406
end
@@ -785,17 +796,16 @@ defmodule Module.Types.Descr do
785
796
786
797
defp atom_to_quoted ( { :union , a } ) do
787
798
if :sets . is_subset ( @ boolset , a ) do
788
- :sets . subtract ( a , @ boolset )
789
- |> :sets . to_list ( )
790
- |> Enum . sort ( )
791
- |> Enum . reduce ( { :boolean , [ ] , [ ] } , & { :or , [ ] , [ & 2 , literal_to_quoted ( & 1 ) ] } )
799
+ entries =
800
+ :sets . subtract ( a , @ boolset )
801
+ |> :sets . to_list ( )
802
+ |> Enum . map ( & literal_to_quoted / 1 )
803
+
804
+ [ { :boolean , [ ] , [ ] } | entries ]
792
805
else
793
806
:sets . to_list ( a )
794
- |> Enum . sort ( )
795
807
|> Enum . map ( & literal_to_quoted / 1 )
796
- |> Enum . reduce ( & { :or , [ ] , [ & 2 , & 1 ] } )
797
808
end
798
- |> List . wrap ( )
799
809
end
800
810
801
811
defp atom_to_quoted ( { :negation , a } ) do
@@ -1064,11 +1074,7 @@ defmodule Module.Types.Descr do
1064
1074
|> Enum . reduce ( & { :or , [ ] , [ & 2 , & 1 ] } )
1065
1075
|> Kernel . then (
1066
1076
& [
1067
- { :and , [ ] ,
1068
- [
1069
- { name , [ ] , arguments } ,
1070
- { :not , [ ] , [ & 1 ] }
1071
- ] }
1077
+ { :and , [ ] , [ { name , [ ] , arguments } , { :not , [ ] , [ & 1 ] } ] }
1072
1078
| acc
1073
1079
]
1074
1080
)
@@ -1691,7 +1697,7 @@ defmodule Module.Types.Descr do
1691
1697
if map_empty_negation? ( tag , acc_fields , neg ) do
1692
1698
{ acc_fields , acc_negs }
1693
1699
else
1694
- case all_but_one ?( tag , acc_fields , neg_tag , neg_fields ) do
1700
+ case map_all_but_one ?( tag , acc_fields , neg_tag , neg_fields ) do
1695
1701
{ :one , diff_key } ->
1696
1702
{ Map . update! ( acc_fields , diff_key , & difference ( & 1 , neg_fields [ diff_key ] ) ) ,
1697
1703
acc_negs }
@@ -1714,43 +1720,45 @@ defmodule Module.Types.Descr do
1714
1720
# 1. Group maps by tags and keys
1715
1721
# 2. Try fusions for each group until no fusion is found
1716
1722
# 3. Merge the groups back into a dnf
1717
- dnf
1718
- |> Enum . group_by ( fn { tag , fields , _ } -> { tag , Map . keys ( fields ) } end )
1719
- |> Enum . flat_map ( fn { _ , maps } -> fuse_maps ( maps ) end )
1723
+ { without_negs , with_negs } = Enum . split_with ( dnf , fn { _tag , _fields , negs } -> negs == [ ] end )
1724
+
1725
+ without_negs =
1726
+ without_negs
1727
+ |> Enum . group_by ( fn { tag , fields , _ } -> { tag , Map . keys ( fields ) } end )
1728
+ |> Enum . flat_map ( fn { _ , maps } -> map_non_negated_fuse ( maps ) end )
1729
+
1730
+ without_negs ++ with_negs
1720
1731
end
1721
1732
1722
- defp fuse_maps ( maps ) do
1733
+ defp map_non_negated_fuse ( maps ) do
1723
1734
Enum . reduce ( maps , [ ] , fn map , acc ->
1724
- case Enum . split_while ( acc , & fusible_maps ?( map , & 1 ) ) do
1735
+ case Enum . split_while ( acc , & non_fusible_maps ?( map , & 1 ) ) do
1725
1736
{ _ , [ ] } ->
1726
1737
[ map | acc ]
1727
1738
1728
1739
{ others , [ match | rest ] } ->
1729
- fused = fuse_map_pair ( map , match )
1740
+ fused = map_non_negated_fuse_pair ( map , match )
1730
1741
others ++ [ fused | rest ]
1731
1742
end
1732
1743
end )
1733
1744
end
1734
1745
1735
- # Two maps are fusible if they have no negations and differ in at most one element.
1736
- defp fusible_maps? ( { _ , fields1 , negs1 } , { _ , fields2 , negs2 } ) do
1737
- negs1 != [ ] or negs2 != [ ] or
1738
- Map . keys ( fields1 )
1739
- |> Enum . count ( fn key -> Map . get ( fields1 , key ) != Map . get ( fields2 , key ) end ) > 1
1746
+ # Two maps are fusible if they differ in at most one element.
1747
+ defp non_fusible_maps? ( { _ , fields1 , [ ] } , { _ , fields2 , [ ] } ) do
1748
+ Enum . count_until ( fields1 , fn { key , value } -> Map . fetch! ( fields2 , key ) != value end , 2 ) > 1
1740
1749
end
1741
1750
1742
- defp fuse_map_pair ( { tag , fields1 , [ ] } , { _ , fields2 , [ ] } ) do
1743
- fused_fields =
1744
- Map . new ( fields1 , fn { key , type1 } ->
1745
- type2 = Map . get ( fields2 , key )
1746
- { key , if ( type1 != type2 , do: union ( type1 , type2 ) , else: type1 ) }
1751
+ defp map_non_negated_fuse_pair ( { tag , fields1 , [ ] } , { _ , fields2 , [ ] } ) do
1752
+ fields =
1753
+ symmetrical_merge ( fields1 , fields2 , fn _k , v1 , v2 ->
1754
+ if v1 == v2 , do: v1 , else: union ( v1 , v2 )
1747
1755
end )
1748
1756
1749
- { tag , fused_fields , [ ] }
1757
+ { tag , fields , [ ] }
1750
1758
end
1751
1759
1752
1760
# If all fields are the same except one, we can optimize map difference.
1753
- defp all_but_one ?( tag1 , fields1 , tag2 , fields2 ) do
1761
+ defp map_all_but_one ?( tag1 , fields1 , tag2 , fields2 ) do
1754
1762
keys1 = Map . keys ( fields1 )
1755
1763
keys2 = Map . keys ( fields2 )
1756
1764
@@ -1782,10 +1790,6 @@ defmodule Module.Types.Descr do
1782
1790
dnf
1783
1791
|> map_normalize ( )
1784
1792
|> Enum . map ( & map_each_to_quoted / 1 )
1785
- |> case do
1786
- [ ] -> [ ]
1787
- dnf -> Enum . reduce ( dnf , & { :or , [ ] , [ & 2 , & 1 ] } ) |> List . wrap ( )
1788
- end
1789
1793
end
1790
1794
1791
1795
defp map_each_to_quoted ( { tag , positive_map , negative_maps } ) do
@@ -1970,10 +1974,6 @@ defmodule Module.Types.Descr do
1970
1974
|> tuple_simplify ( )
1971
1975
|> tuple_fusion ( )
1972
1976
|> Enum . map ( & tuple_each_to_quoted / 1 )
1973
- |> case do
1974
- [ ] -> [ ]
1975
- dnf -> Enum . reduce ( dnf , & { :or , [ ] , [ & 2 , & 1 ] } ) |> List . wrap ( )
1976
- end
1977
1977
end
1978
1978
1979
1979
# Given a dnf of tuples, fuses the tuple unions when possible,
@@ -1985,34 +1985,37 @@ defmodule Module.Types.Descr do
1985
1985
# 2. Group tuples by size and tag
1986
1986
# 3. Try fusions for each group until no fusion is found
1987
1987
# 4. Merge the groups back into a dnf
1988
- dnf
1989
- |> Enum . group_by ( fn { tag , elems , _ } -> { tag , length ( elems ) } end )
1990
- |> Enum . flat_map ( fn { _ , tuples } -> fuse_tuples ( tuples ) end )
1988
+ { without_negs , with_negs } = Enum . split_with ( dnf , fn { _tag , _elems , negs } -> negs == [ ] end )
1989
+
1990
+ without_negs =
1991
+ without_negs
1992
+ |> Enum . group_by ( fn { tag , elems , _ } -> { tag , length ( elems ) } end )
1993
+ |> Enum . flat_map ( fn { _ , tuples } -> tuple_non_negated_fuse ( tuples ) end )
1994
+
1995
+ without_negs ++ with_negs
1991
1996
end
1992
1997
1993
- defp fuse_tuples ( tuples ) do
1998
+ defp tuple_non_negated_fuse ( tuples ) do
1994
1999
Enum . reduce ( tuples , [ ] , fn tuple , acc ->
1995
- case Enum . split_while ( acc , & fusible_tuples ?( tuple , & 1 ) ) do
2000
+ case Enum . split_while ( acc , & non_fusible_tuples ?( tuple , & 1 ) ) do
1996
2001
{ _ , [ ] } ->
1997
2002
[ tuple | acc ]
1998
2003
1999
2004
{ others , [ match | rest ] } ->
2000
- fused = fuse_tuple_pair ( tuple , match )
2005
+ fused = tuple_non_negated_fuse_pair ( tuple , match )
2001
2006
others ++ [ fused | rest ]
2002
2007
end
2003
2008
end )
2004
2009
end
2005
2010
2006
2011
# Two tuples are fusible if they have no negations and differ in at most one element.
2007
- defp fusible_tuples? ( { _ , elems1 , negs1 } , { _ , elems2 , negs2 } ) do
2008
- negs1 != [ ] or negs2 != [ ] or
2009
- Enum . zip ( elems1 , elems2 ) |> Enum . count ( fn { a , b } -> a != b end ) > 1
2012
+ defp non_fusible_tuples? ( { _ , elems1 , [ ] } , { _ , elems2 , [ ] } ) do
2013
+ Enum . zip ( elems1 , elems2 ) |> Enum . count_until ( fn { a , b } -> a != b end , 2 ) > 1
2010
2014
end
2011
2015
2012
- defp fuse_tuple_pair ( { tag , elems1 , [ ] } , { _ , elems2 , [ ] } ) do
2016
+ defp tuple_non_negated_fuse_pair ( { tag , elems1 , [ ] } , { _ , elems2 , [ ] } ) do
2013
2017
fused_elements =
2014
- Enum . zip ( elems1 , elems2 )
2015
- |> Enum . map ( fn { a , b } -> if a != b , do: union ( a , b ) , else: a end )
2018
+ Enum . zip_with ( elems1 , elems2 , fn a , b -> if a == b , do: a , else: union ( a , b ) end )
2016
2019
2017
2020
{ tag , fused_elements , [ ] }
2018
2021
end
0 commit comments