@@ -178,9 +178,9 @@ defmodule Module.Types.Descr do
178178    end 
179179  end 
180180
181-   @ compile  { :inline ,  lazy_union : 2 } 
182-   defp  lazy_union ( :term ,  _fun ) ,  do:  :term 
183-   defp  lazy_union ( descr ,  fun ) ,  do:  union ( descr ,  fun . ( ) ) 
181+   @ compile  { :inline ,  maybe_union : 2 } 
182+   defp  maybe_union ( nil ,  _fun ) ,  do:  nil 
183+   defp  maybe_union ( descr ,  fun ) ,  do:  union ( descr ,  fun . ( ) ) 
184184
185185  @ doc  """ 
186186  Computes the union of two descrs. 
@@ -1377,31 +1377,20 @@ defmodule Module.Types.Descr do
13771377    end 
13781378  end 
13791379
1380-   defp  map_fetch_static ( :term ,  _key ) ,  do:  { true ,  term ( ) } 
1381- 
1382-   defp  map_fetch_static ( descr ,  key )  when  is_atom ( key )  do 
1383-     case  descr  do 
1384-       # Optimization: if the key does not exist in the map, 
1385-       # avoid building if_set/not_set pairs and return the 
1386-       # popped value directly. 
1387-       % { map:  [ { tag ,  fields ,  [ ] } ] }  when  not  is_map_key ( fields ,  key )  -> 
1388-         case  tag  do 
1389-           :open  ->  { true ,  term ( ) } 
1390-           :closed  ->  { true ,  none ( ) } 
1391-         end 
1392- 
1393-       % { map:  map }  -> 
1394-         map_fetch_dnf ( map ,  key )  |>  pop_optional_static ( ) 
1395- 
1396-       % { }  -> 
1397-         { false ,  none ( ) } 
1380+   # Optimization: if the key does not exist in the map, avoid building 
1381+   # if_set/not_set pairs and return the popped value directly. 
1382+   defp  map_fetch_static ( % { map:  [ { tag ,  fields ,  [ ] } ] } ,  key )  when  not  is_map_key ( fields ,  key )  do 
1383+     case  tag  do 
1384+       :open  ->  { true ,  term ( ) } 
1385+       :closed  ->  { true ,  none ( ) } 
13981386    end 
13991387  end 
14001388
14011389  # Takes a map dnf and returns the union of types it can take for a given key. 
14021390  # If the key may be undefined, it will contain the `not_set()` type. 
1403-   defp  map_fetch_dnf ( dnf ,  key )  do 
1404-     Enum . reduce ( dnf ,  none ( ) ,  fn 
1391+   defp  map_fetch_static ( % { map:  dnf } ,  key )  do 
1392+     dnf 
1393+     |>  Enum . reduce ( none ( ) ,  fn 
14051394      # Optimization: if there are no negatives, 
14061395      # we can return the value directly. 
14071396      { _tag ,  % { ^ key  =>  value } ,  [ ] } ,  acc  -> 
@@ -1426,8 +1415,12 @@ defmodule Module.Types.Descr do
14261415            |>  union ( acc ) 
14271416        end 
14281417    end ) 
1418+     |>  pop_optional_static ( ) 
14291419  end 
14301420
1421+   defp  map_fetch_static ( % { } ,  _key ) ,  do:  { false ,  none ( ) } 
1422+   defp  map_fetch_static ( :term ,  _key ) ,  do:  { true ,  term ( ) } 
1423+ 
14311424  @ doc  """ 
14321425  Fetches and puts a `key` of a given type, assuming that the descr is exclusively 
14331426  a map (or dynamic). 
@@ -1445,13 +1438,7 @@ defmodule Module.Types.Descr do
14451438  end 
14461439
14471440  defp  map_fetch_and_put_shared ( descr ,  key ,  type )  do 
1448-     with  { value ,  descr }  <-  map_take ( descr ,  key ,  none ( ) ,  & map_put_static ( & 1 ,  key ,  type ) )  do 
1449-       if  empty? ( value )  do 
1450-         :badkey 
1451-       else 
1452-         { value ,  descr } 
1453-       end 
1454-     end 
1441+     map_take ( descr ,  key ,  none ( ) ,  & map_put_static ( & 1 ,  key ,  type ) ) 
14551442  end 
14561443
14571444  @ doc  """ 
@@ -1469,7 +1456,7 @@ defmodule Module.Types.Descr do
14691456  end 
14701457
14711458  defp  map_put_shared ( descr ,  key ,  type )  do 
1472-     with  { _value ,  descr }  <-  map_take ( descr ,  key ,  :term ,  & map_put_static ( & 1 ,  key ,  type ) )  do 
1459+     with  { nil ,  descr }  <-  map_take ( descr ,  key ,  nil ,  & map_put_static ( & 1 ,  key ,  type ) )  do 
14731460      { :ok ,  descr } 
14741461    end 
14751462  end 
@@ -1493,9 +1480,9 @@ defmodule Module.Types.Descr do
14931480  Removes a key from a map type. 
14941481  """ 
14951482  def  map_delete ( descr ,  key )  do 
1496-     # We pass :term  as the initial value so we can avoid computing the unions. 
1497-     with  { _value ,  descr }  <- 
1498-            map_take ( descr ,  key ,  :term ,  & intersection_static ( & 1 ,  open_map ( [ { key ,  not_set ( ) } ] ) ) )  do 
1483+     # We pass nil  as the initial value so we can avoid computing the unions. 
1484+     with  { nil ,  descr }  <- 
1485+            map_take ( descr ,  key ,  nil ,  & intersection_static ( & 1 ,  open_map ( [ { key ,  not_set ( ) } ] ) ) )  do 
14991486      { :ok ,  descr } 
15001487    end 
15011488  end 
@@ -1521,25 +1508,29 @@ defmodule Module.Types.Descr do
15211508  defp  map_take ( descr ,  key ,  initial ,  updater )  when  is_atom ( key )  do 
15221509    case  :maps . take ( :dynamic ,  descr )  do 
15231510      :error  -> 
1524-         # Note: the empty type is not a valid input 
15251511        if  descr_key? ( descr ,  :map )  and  map_only? ( descr )  do 
1526-           { taken ,  result }  =  map_take_static ( descr ,  key ,  initial ) 
1527-           { taken ,  updater . ( result ) } 
1512+           { optional? ,  taken ,  result }  =  map_take_static ( descr ,  key ,  initial ) 
1513+ 
1514+           cond  do 
1515+             taken  ==  nil  ->  { nil ,  updater . ( result ) } 
1516+             optional?  or  empty? ( taken )  ->  :badkey 
1517+             true  ->  { taken ,  updater . ( result ) } 
1518+           end 
15281519        else 
15291520          :badmap 
15301521        end 
15311522
15321523      { dynamic ,  static }  -> 
15331524        if  descr_key? ( dynamic ,  :map )  and  map_only? ( static )  do 
1534-           { dynamic_taken ,  dynamic_result }  =  map_take_static ( dynamic ,  key ,  initial ) 
1535-           { static_taken ,  static_result }  =  map_take_static ( static ,  key ,  initial ) 
1536- 
1537-            taken   = 
1538-              if   dynamic_taken   ==   :term , 
1539-                do:  dynamic ( ) , 
1540-                else:  union ( dynamic ( dynamic_taken ) ,   static_taken ) 
1541- 
1542-           { taken ,   union ( dynamic ( updater . ( dynamic_result ) ) ,   updater . ( static_result ) ) } 
1525+           { _ ,   dynamic_taken ,  dynamic_result }  =  map_take_static ( dynamic ,  key ,  initial ) 
1526+           { static_optional? ,   static_taken ,  static_result }  =  map_take_static ( static ,  key ,  initial ) 
1527+            result   =   union ( dynamic ( updater . ( dynamic_result ) ) ,   updater . ( static_result ) ) 
1528+ 
1529+           cond   do 
1530+             static_taken   ==   nil   and   dynamic_taken   ==   nil   ->   { nil ,   result } 
1531+             static_optional?   or   empty? ( dynamic_taken )   ->   :badkey 
1532+              true   ->   { union ( dynamic ( dynamic_taken ) ,   static_taken ) ,   result } 
1533+           end 
15431534        else 
15441535          :badmap 
15451536        end 
@@ -1548,36 +1539,51 @@ defmodule Module.Types.Descr do
15481539
15491540  # Takes a static map type and removes a key from it. 
15501541  # This allows the key to be put or deleted later on. 
1542+   defp  map_take_static ( % { map:  [ { tag ,  fields ,  [ ] } ] }  =  descr ,  key ,  initial ) 
1543+        when  not  is_map_key ( fields ,  key )  do 
1544+     case  tag  do 
1545+       :open  ->  { true ,  maybe_union ( initial ,  fn  ->  term ( )  end ) ,  descr } 
1546+       :closed  ->  { true ,  initial ,  descr } 
1547+     end 
1548+   end 
1549+ 
15511550  defp  map_take_static ( % { map:  dnf } ,  key ,  initial )  do 
15521551    { value ,  map }  = 
15531552      Enum . reduce ( dnf ,  { initial ,  none ( ) } ,  fn 
15541553        # Optimization: if there are no negatives, we can directly remove the key. 
1555-         { tag ,  fields ,  [ ] } ,  { taken ,  map }  -> 
1554+         { tag ,  fields ,  [ ] } ,  { value ,  map }  -> 
15561555          { fst ,  snd }  =  map_pop_key ( tag ,  fields ,  key ) 
1557-           { lazy_union ( taken ,  fn  ->  fst  end ) ,  union ( map ,  snd ) } 
1556+           { maybe_union ( value ,  fn  ->  fst  end ) ,  union ( map ,  snd ) } 
15581557
1559-         { tag ,  fields ,  negs } ,  { taken ,  map }  -> 
1558+         { tag ,  fields ,  negs } ,  { value ,  map }  -> 
15601559          { fst ,  snd }  =  map_pop_key ( tag ,  fields ,  key ) 
15611560
15621561          case  map_split_negative ( negs ,  key )  do 
15631562            :empty  -> 
1564-               { taken ,  map } 
1563+               { value ,  map } 
15651564
15661565            negative  -> 
15671566              disjoint  =  pair_make_disjoint ( negative ) 
15681567
1569-               { lazy_union ( taken ,  fn  ->  pair_eliminate_negations_fst ( disjoint ,  fst ,  snd )  end ) , 
1568+               { maybe_union ( value ,  fn  ->  pair_eliminate_negations_fst ( disjoint ,  fst ,  snd )  end ) , 
15701569               disjoint  |>  pair_eliminate_negations_snd ( fst ,  snd )  |>  union ( map ) } 
15711570          end 
15721571      end ) 
15731572
1574-     { remove_optional_static ( value ) ,  map } 
1573+     if  value  ==  nil  do 
1574+       { false ,  value ,  map } 
1575+     else 
1576+       { optional? ,  value }  =  pop_optional_static ( value ) 
1577+       { optional? ,  value ,  map } 
1578+     end 
15751579  end 
15761580
1577-   defp  map_take_static ( :term ,  _key ,  _initial ) ,  do:  { :term ,  open_map ( ) } 
1578- 
15791581  # If there is no map part to this static type, there is nothing to delete. 
1580-   defp  map_take_static ( _type ,  _key ,  initial ) ,  do:  { initial ,  none ( ) } 
1582+   defp  map_take_static ( % { } ,  _key ,  initial ) ,  do:  { false ,  initial ,  none ( ) } 
1583+ 
1584+   defp  map_take_static ( :term ,  _key ,  initial )  do 
1585+     { true ,  maybe_union ( initial ,  fn  ->  term ( )  end ) ,  open_map ( ) } 
1586+   end 
15811587
15821588  # Short-circuits if it finds a non-empty map literal in the union. 
15831589  # Since the algorithm is recursive, we implement the short-circuiting 
0 commit comments