@@ -372,11 +372,18 @@ defmodule Module.Types.Descr do
372372 if term_type? ( descr ) do
373373 { :term , [ ] , [ ] }
374374 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
375383 { extra , descr } =
376384 case descr do
377- # Merge empty list and list together if they both exist
378385 % { 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 )
380387
381388 case list_to_quoted ( list , :list ) do
382389 [ ] -> { [ { :empty_list , [ ] , [ ] } ] , descr }
@@ -387,9 +394,13 @@ defmodule Module.Types.Descr do
387394 { [ ] , descr }
388395 end
389396
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
391402 [ ] -> { :none , [ ] , [ ] }
392- unions -> unions |> Enum . sort ( ) |> Enum . reduce ( & { :or , [ ] , [ & 2 , & 1 ] } )
403+ unions -> Enum . reduce ( unions , & { :or , [ ] , [ & 2 , & 1 ] } )
393404 end
394405 end
395406 end
@@ -785,17 +796,16 @@ defmodule Module.Types.Descr do
785796
786797 defp atom_to_quoted ( { :union , a } ) do
787798 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 ]
792805 else
793806 :sets . to_list ( a )
794- |> Enum . sort ( )
795807 |> Enum . map ( & literal_to_quoted / 1 )
796- |> Enum . reduce ( & { :or , [ ] , [ & 2 , & 1 ] } )
797808 end
798- |> List . wrap ( )
799809 end
800810
801811 defp atom_to_quoted ( { :negation , a } ) do
@@ -1064,11 +1074,7 @@ defmodule Module.Types.Descr do
10641074 |> Enum . reduce ( & { :or , [ ] , [ & 2 , & 1 ] } )
10651075 |> Kernel . then (
10661076 & [
1067- { :and , [ ] ,
1068- [
1069- { name , [ ] , arguments } ,
1070- { :not , [ ] , [ & 1 ] }
1071- ] }
1077+ { :and , [ ] , [ { name , [ ] , arguments } , { :not , [ ] , [ & 1 ] } ] }
10721078 | acc
10731079 ]
10741080 )
@@ -1691,7 +1697,7 @@ defmodule Module.Types.Descr do
16911697 if map_empty_negation? ( tag , acc_fields , neg ) do
16921698 { acc_fields , acc_negs }
16931699 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
16951701 { :one , diff_key } ->
16961702 { Map . update! ( acc_fields , diff_key , & difference ( & 1 , neg_fields [ diff_key ] ) ) ,
16971703 acc_negs }
@@ -1714,43 +1720,45 @@ defmodule Module.Types.Descr do
17141720 # 1. Group maps by tags and keys
17151721 # 2. Try fusions for each group until no fusion is found
17161722 # 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
17201731 end
17211732
1722- defp fuse_maps ( maps ) do
1733+ defp map_non_negated_fuse ( maps ) do
17231734 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
17251736 { _ , [ ] } ->
17261737 [ map | acc ]
17271738
17281739 { others , [ match | rest ] } ->
1729- fused = fuse_map_pair ( map , match )
1740+ fused = map_non_negated_fuse_pair ( map , match )
17301741 others ++ [ fused | rest ]
17311742 end
17321743 end )
17331744 end
17341745
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
17401749 end
17411750
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 )
17471755 end )
17481756
1749- { tag , fused_fields , [ ] }
1757+ { tag , fields , [ ] }
17501758 end
17511759
17521760 # 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
17541762 keys1 = Map . keys ( fields1 )
17551763 keys2 = Map . keys ( fields2 )
17561764
@@ -1782,10 +1790,6 @@ defmodule Module.Types.Descr do
17821790 dnf
17831791 |> map_normalize ( )
17841792 |> Enum . map ( & map_each_to_quoted / 1 )
1785- |> case do
1786- [ ] -> [ ]
1787- dnf -> Enum . reduce ( dnf , & { :or , [ ] , [ & 2 , & 1 ] } ) |> List . wrap ( )
1788- end
17891793 end
17901794
17911795 defp map_each_to_quoted ( { tag , positive_map , negative_maps } ) do
@@ -1970,10 +1974,6 @@ defmodule Module.Types.Descr do
19701974 |> tuple_simplify ( )
19711975 |> tuple_fusion ( )
19721976 |> Enum . map ( & tuple_each_to_quoted / 1 )
1973- |> case do
1974- [ ] -> [ ]
1975- dnf -> Enum . reduce ( dnf , & { :or , [ ] , [ & 2 , & 1 ] } ) |> List . wrap ( )
1976- end
19771977 end
19781978
19791979 # Given a dnf of tuples, fuses the tuple unions when possible,
@@ -1985,34 +1985,37 @@ defmodule Module.Types.Descr do
19851985 # 2. Group tuples by size and tag
19861986 # 3. Try fusions for each group until no fusion is found
19871987 # 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
19911996 end
19921997
1993- defp fuse_tuples ( tuples ) do
1998+ defp tuple_non_negated_fuse ( tuples ) do
19941999 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
19962001 { _ , [ ] } ->
19972002 [ tuple | acc ]
19982003
19992004 { others , [ match | rest ] } ->
2000- fused = fuse_tuple_pair ( tuple , match )
2005+ fused = tuple_non_negated_fuse_pair ( tuple , match )
20012006 others ++ [ fused | rest ]
20022007 end
20032008 end )
20042009 end
20052010
20062011 # 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
20102014 end
20112015
2012- defp fuse_tuple_pair ( { tag , elems1 , [ ] } , { _ , elems2 , [ ] } ) do
2016+ defp tuple_non_negated_fuse_pair ( { tag , elems1 , [ ] } , { _ , elems2 , [ ] } ) do
20132017 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 )
20162019
20172020 { tag , fused_elements , [ ] }
20182021 end
0 commit comments