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