@@ -83,8 +83,8 @@ defmodule Module.Types.Descr do
8383 @ boolset :sets . from_list ( [ true , false ] , version: 2 )
8484 def boolean ( ) , do: % { atom: { :union , @ boolset } }
8585
86- # Map helpers
87- #
86+ ## Optional
87+
8888 # `not_set()` is a special base type that represents an not_set field in a map.
8989 # E.g., `%{a: integer(), b: not_set(), ...}` represents a map with an integer
9090 # field `a` and an not_set field `b`, and possibly other fields.
@@ -153,12 +153,15 @@ defmodule Module.Types.Descr do
153153
154154 ## Set operations
155155
156- def term_type? ( :term ) , do: true
157- def term_type? ( descr ) , do: subtype_static? ( unfolded_term ( ) , Map . delete ( descr , :dynamic ) )
158-
156+ @ doc """
157+ Returns true if the type has a gradual part.
158+ """
159159 def gradual? ( :term ) , do: false
160160 def gradual? ( descr ) , do: is_map_key ( descr , :dynamic )
161161
162+ @ doc """
163+ Returns true if hte type only has a gradual part.
164+ """
162165 def only_gradual? ( % { dynamic: _ } = descr ) , do: map_size ( descr ) == 1
163166 def only_gradual? ( _ ) , do: false
164167
@@ -175,11 +178,17 @@ defmodule Module.Types.Descr do
175178 end
176179 end
177180
181+ @ compile { :inline , lazy_union: 2 }
182+ defp lazy_union ( :term , _fun ) , do: :term
183+ defp lazy_union ( descr , fun ) , do: union ( descr , fun . ( ) )
184+
178185 @ doc """
179186 Computes the union of two descrs.
180187 """
181188 def union ( :term , other ) , do: optional_to_term ( other )
182189 def union ( other , :term ) , do: optional_to_term ( other )
190+ def union ( none , other ) when none == @ none , do: other
191+ def union ( other , none ) when none == @ none , do: other
183192
184193 def union ( left , right ) do
185194 left = unfold ( left )
@@ -201,7 +210,6 @@ defmodule Module.Types.Descr do
201210 end
202211 end
203212
204- @ compile { :inline , union: 3 }
205213 defp union ( :atom , v1 , v2 ) , do: atom_union ( v1 , v2 )
206214 defp union ( :bitmap , v1 , v2 ) , do: v1 ||| v2
207215 defp union ( :dynamic , v1 , v2 ) , do: dynamic_union ( v1 , v2 )
@@ -239,7 +247,6 @@ defmodule Module.Types.Descr do
239247 end
240248
241249 # Returning 0 from the callback is taken as none() for that subtype.
242- @ compile { :inline , intersection: 3 }
243250 defp intersection ( :atom , v1 , v2 ) , do: atom_intersection ( v1 , v2 )
244251 defp intersection ( :bitmap , v1 , v2 ) , do: v1 &&& v2
245252 defp intersection ( :dynamic , v1 , v2 ) , do: dynamic_intersection ( v1 , v2 )
@@ -299,7 +306,6 @@ defmodule Module.Types.Descr do
299306 defp iterator_difference_static ( :none , map ) , do: map
300307
301308 # Returning 0 from the callback is taken as none() for that subtype.
302- @ compile { :inline , difference: 3 }
303309 defp difference ( :atom , v1 , v2 ) , do: atom_difference ( v1 , v2 )
304310 defp difference ( :bitmap , v1 , v2 ) , do: v1 - ( v1 &&& v2 )
305311 defp difference ( :dynamic , v1 , v2 ) , do: dynamic_difference ( v1 , v2 )
@@ -378,7 +384,6 @@ defmodule Module.Types.Descr do
378384 end
379385 end
380386
381- @ compile { :inline , to_quoted: 2 }
382387 defp to_quoted ( :atom , val ) , do: atom_to_quoted ( val )
383388 defp to_quoted ( :bitmap , val ) , do: bitmap_to_quoted ( val )
384389 defp to_quoted ( :dynamic , descr ) , do: dynamic_to_quoted ( descr )
@@ -513,6 +518,12 @@ defmodule Module.Types.Descr do
513518 end
514519 end
515520
521+ @ doc """
522+ Optimized version of `not empty?(term(), type)`.
523+ """
524+ def term_type? ( :term ) , do: true
525+ def term_type? ( descr ) , do: subtype_static? ( unfolded_term ( ) , Map . delete ( descr , :dynamic ) )
526+
516527 @ doc """
517528 Optimized version of `not empty?(intersection(empty_list(), type))`.
518529 """
@@ -1061,7 +1072,7 @@ defmodule Module.Types.Descr do
10611072 end
10621073 end
10631074
1064- # TODO: Eliminate empty lists from the union
1075+ # TODO: Eliminate empty lists from the union.
10651076 defp list_normalize ( dnf ) , do: dnf
10661077 # Enum.filter(dnf, fn {list_type, last_type, negs} ->
10671078 # not Enum.any?(negs, fn neg -> subtype?(list_type, neg) end)
@@ -1280,7 +1291,7 @@ defmodule Module.Types.Descr do
12801291 defp map_put_static_value ( descr , key , type ) do
12811292 case :maps . take ( :dynamic , descr ) do
12821293 :error ->
1283- if map_only? ( descr ) do
1294+ if descr_key? ( descr , :map ) and map_only? ( descr ) do
12841295 map_put_static_shared ( descr , key , type )
12851296 else
12861297 :badmap
@@ -1306,8 +1317,8 @@ defmodule Module.Types.Descr do
13061317
13071318 # Directly inserts a key of a given type into every positive and negative map
13081319 defp map_put_static_shared ( descr , key , type ) do
1309- case map_delete_static ( descr , key ) do
1310- % { map: dnf } = descr ->
1320+ case map_take_static ( descr , key , :term ) do
1321+ { _ , % { map: dnf } = descr } ->
13111322 dnf =
13121323 Enum . map ( dnf , fn { tag , fields , negs } ->
13131324 { tag , Map . put ( fields , key , type ) ,
@@ -1318,7 +1329,7 @@ defmodule Module.Types.Descr do
13181329
13191330 % { descr | map: dnf }
13201331
1321- % { } ->
1332+ { _ , % { } } ->
13221333 descr
13231334 end
13241335 end
@@ -1439,6 +1450,17 @@ defmodule Module.Types.Descr do
14391450
14401451 @ doc """
14411452 Removes a key from a map type.
1453+ """
1454+ def map_delete ( descr , key ) do
1455+ # We pass :term as the initial value so we can avoid computing the unions.
1456+ case map_take ( descr , key , :term ) do
1457+ { _ , descr } -> { :ok , descr }
1458+ error -> error
1459+ end
1460+ end
1461+
1462+ @ doc """
1463+ Removes a key from a map type and return its type.
14421464
14431465 ## Algorithm
14441466
@@ -1448,62 +1470,76 @@ defmodule Module.Types.Descr do
14481470 3. Intersect this with an open record type where the key is explicitly absent.
14491471 This step eliminates the key from open record types where it was implicitly present.
14501472 """
1451- def map_delete ( :term , _key ) , do: :badmap
1473+ def map_take ( descr , key ) do
1474+ map_take ( descr , key , none ( ) )
1475+ end
1476+
1477+ defp map_take ( :term , _key , _initial ) , do: :badmap
14521478
1453- def map_delete ( descr , key ) when is_atom ( key ) do
1479+ defp map_take ( descr , key , initial ) when is_atom ( key ) do
14541480 case :maps . take ( :dynamic , descr ) do
14551481 :error ->
1456- # Note: the empty typ is not a valid input
1482+ # Note: the empty type is not a valid input
14571483 if descr_key? ( descr , :map ) and map_only? ( descr ) do
1458- map_delete_static ( descr , key )
1459- |> intersection ( open_map ( [ { key , not_set ( ) } ] ) )
1484+ { taken , descr } = map_take_static ( descr , key , initial )
1485+ { taken , intersection ( descr , open_map ( [ { key , not_set ( ) } ] ) ) }
14601486 else
14611487 :badmap
14621488 end
14631489
14641490 { dynamic , static } ->
14651491 if descr_key? ( dynamic , :map ) and map_only? ( static ) do
1466- dynamic_result = map_delete_static ( dynamic , key )
1467- static_result = map_delete_static ( static , key )
1492+ { dynamic_taken , dynamic_result } = map_take_static ( dynamic , key , initial )
1493+ { static_taken , static_result } = map_take_static ( static , key , initial )
14681494
1469- union ( dynamic ( dynamic_result ) , static_result )
1470- |> intersection ( open_map ( [ { key , not_set ( ) } ] ) )
1495+ taken =
1496+ if dynamic_taken == :term ,
1497+ do: dynamic ( ) ,
1498+ else: union ( dynamic ( dynamic_taken ) , static_taken )
1499+
1500+ result =
1501+ union ( dynamic ( dynamic_result ) , static_result )
1502+ |> intersection ( open_map ( [ { key , not_set ( ) } ] ) )
1503+
1504+ { taken , result }
14711505 else
14721506 :badmap
14731507 end
14741508 end
14751509 end
14761510
14771511 # Takes a static map type and removes a key from it.
1478- defp map_delete_static ( % { map: dnf } , key ) do
1479- Enum . reduce ( dnf , none ( ) , fn
1480- # Optimization: if there are no negatives, we can directly remove the key.
1481- { tag , fields , [ ] } , acc ->
1482- union ( acc , % { map: map_new ( tag , :maps . remove ( key , fields ) ) } )
1512+ defp map_take_static ( % { map: dnf } , key , initial ) do
1513+ { value , map } =
1514+ Enum . reduce ( dnf , { initial , none ( ) } , fn
1515+ # Optimization: if there are no negatives, we can directly remove the key.
1516+ { tag , fields , [ ] } , { taken , map } ->
1517+ { fst , snd } = map_pop_key ( tag , fields , key )
1518+ { lazy_union ( taken , fn -> fst end ) , union ( map , snd ) }
14831519
1484- { tag , fields , negs } , acc ->
1485- { fst , snd } = map_pop_key ( tag , fields , key )
1520+ { tag , fields , negs } , { taken , map } ->
1521+ { fst , snd } = map_pop_key ( tag , fields , key )
14861522
1487- union (
1488- acc ,
14891523 case map_split_negative ( negs , key ) do
14901524 :empty ->
1491- none ( )
1525+ { taken , map }
14921526
14931527 negative ->
1494- negative |> pair_make_disjoint ( ) |> pair_eliminate_negations_snd ( fst , snd )
1528+ disjoint = pair_make_disjoint ( negative )
1529+
1530+ { lazy_union ( taken , fn -> pair_eliminate_negations_fst ( disjoint , fst , snd ) end ) ,
1531+ disjoint |> pair_eliminate_negations_snd ( fst , snd ) |> union ( map ) }
14951532 end
1496- )
1497- end )
1533+ end )
1534+
1535+ { remove_optional_static ( value ) , map }
14981536 end
14991537
1500- defp map_delete_static ( :term , key ) , do: open_map ( [ { key , not_set ( ) } ] )
1538+ defp map_take_static ( :term , key , _initial ) , do: { :term , open_map ( [ { key , not_set ( ) } ] ) }
15011539
15021540 # If there is no map part to this static type, there is nothing to delete.
1503- defp map_delete_static ( _type , _key ) , do: none ( )
1541+ defp map_take_static ( _type , _key , initial ) , do: { initial , none ( ) }
15041542
1505- # Emptiness checking for maps.
1506- #
15071543 # Short-circuits if it finds a non-empty map literal in the union.
15081544 # Since the algorithm is recursive, we implement the short-circuiting
15091545 # as throw/catch.
@@ -1574,10 +1610,15 @@ defmodule Module.Types.Descr do
15741610 { fst , snd } = map_pop_key ( tag , fields , key )
15751611
15761612 case map_split_negative ( negs , key ) do
1577- :empty -> none ( )
1578- negative -> negative |> pair_make_disjoint ( ) |> pair_eliminate_negations_fst ( fst , snd )
1613+ :empty ->
1614+ acc
1615+
1616+ negative ->
1617+ negative
1618+ |> pair_make_disjoint ( )
1619+ |> pair_eliminate_negations_fst ( fst , snd )
1620+ |> union ( acc )
15791621 end
1580- |> union ( acc )
15811622 end )
15821623 end
15831624
@@ -1597,7 +1638,7 @@ defmodule Module.Types.Descr do
15971638 end
15981639
15991640 # Use heuristics to normalize a map dnf for pretty printing.
1600- # TODO: eliminate some simple negations, those which have only zero or one key in common.
1641+ # TODO: Eliminate some simple negations, those which have only zero or one key in common.
16011642 defp map_normalize ( dnf ) do
16021643 dnf
16031644 |> Enum . reject ( & map_empty? ( [ & 1 ] ) )
@@ -1989,10 +2030,15 @@ defmodule Module.Types.Descr do
19892030 { fst , snd } = tuple_pop_index ( tag , elements , index )
19902031
19912032 case tuple_split_negative ( negs , index ) do
1992- :empty -> none ( )
1993- negative -> negative |> pair_make_disjoint ( ) |> pair_eliminate_negations_fst ( fst , snd )
2033+ :empty ->
2034+ acc
2035+
2036+ negative ->
2037+ negative
2038+ |> pair_make_disjoint ( )
2039+ |> pair_eliminate_negations_fst ( fst , snd )
2040+ |> union ( acc )
19942041 end
1995- |> union ( acc )
19962042 end )
19972043 end
19982044
@@ -2077,13 +2123,16 @@ defmodule Module.Types.Descr do
20772123 { tag , elements , negs } , acc ->
20782124 { fst , snd } = tuple_pop_index ( tag , elements , index )
20792125
2080- union (
2081- acc ,
2082- case tuple_split_negative ( negs , index ) do
2083- :empty -> none ( )
2084- negative -> negative |> pair_make_disjoint ( ) |> pair_eliminate_negations_snd ( fst , snd )
2085- end
2086- )
2126+ case tuple_split_negative ( negs , index ) do
2127+ :empty ->
2128+ acc
2129+
2130+ negative ->
2131+ negative
2132+ |> pair_make_disjoint ( )
2133+ |> pair_eliminate_negations_snd ( fst , snd )
2134+ |> union ( acc )
2135+ end
20872136 end )
20882137 end
20892138
@@ -2181,7 +2230,7 @@ defmodule Module.Types.Descr do
21812230 end
21822231
21832232 ## Pairs
2184- #
2233+
21852234 # To simplify disjunctive normal forms of e.g., map types, it is useful to
21862235 # convert them into disjunctive normal forms of pairs of types, and define
21872236 # normalization algorithms on pairs.
0 commit comments