@@ -41,10 +41,16 @@ defmodule Module.Types.Descr do
4141 list: @ non_empty_list_top
4242 }
4343
44+ @ empty_intersection [ 0 , @ none ]
45+ @ empty_difference [ 0 , [ ] ]
46+
4447 # Type definitions
4548
4649 defguard is_descr ( descr ) when is_map ( descr ) or descr == :term
4750
51+ defp descr_key? ( :term , _key ) , do: true
52+ defp descr_key? ( descr , key ) , do: is_map_key ( descr , key )
53+
4854 def dynamic ( ) , do: % { dynamic: :term }
4955 def none ( ) , do: @ none
5056 def term ( ) , do: :term
@@ -91,21 +97,33 @@ defmodule Module.Types.Descr do
9197
9298 @ not_set % { optional: 1 }
9399 @ term_or_optional Map . put ( @ term , :optional , 1 )
100+ @ term_or_dynamic_optional Map . put ( @ term , :dynamic , % { optional: 1 } )
94101
95102 def not_set ( ) , do: @ not_set
96- defp term_or_optional ( ) , do: @ term_or_optional
97-
98103 def if_set ( :term ) , do: term_or_optional ( )
99104 def if_set ( type ) , do: Map . put ( type , :optional , 1 )
105+ defp term_or_optional ( ) , do: @ term_or_optional
100106
101- defp descr_key? ( :term , _key ) , do: true
102- defp descr_key? ( descr , key ) , do: is_map_key ( descr , key )
107+ @ compile { :inline ,
108+ keep_optional: 1 , remove_optional: 1 , remove_optional_static: 1 , optional_to_term: 1 }
109+ defp keep_optional ( descr ) do
110+ case descr do
111+ % { dynamic: % { optional: 1 } } -> % { dynamic: % { optional: 1 } }
112+ % { optional: 1 } -> % { optional: 1 }
113+ _ -> % { }
114+ end
115+ end
103116
104- @ compile { :inline , remove_optional: 1 , remove_optional_static: 1 , optional_to_term: 1 }
105117 defp remove_optional ( descr ) do
106118 case descr do
107- % { dynamic: % { optional: _ } = dynamic } -> % { descr | dynamic: Map . delete ( dynamic , :optional ) }
108- _ -> remove_optional_static ( descr )
119+ % { dynamic: % { optional: _ } = dynamic } when map_size ( dynamic ) == 1 ->
120+ Map . delete ( descr , :dynamic )
121+
122+ % { dynamic: % { optional: _ } = dynamic } ->
123+ % { descr | dynamic: Map . delete ( dynamic , :optional ) }
124+
125+ _ ->
126+ remove_optional_static ( descr )
109127 end
110128 end
111129
@@ -118,7 +136,7 @@ defmodule Module.Types.Descr do
118136
119137 defp optional_to_term ( descr ) do
120138 case descr do
121- % { dynamic: % { optional: _ } } -> dynamic ( term_or_optional ( ) )
139+ % { dynamic: % { optional: _ } } -> @ term_or_dynamic_optional
122140 % { optional: _ } -> term_or_optional ( )
123141 _ -> :term
124142 end
@@ -230,7 +248,7 @@ defmodule Module.Types.Descr do
230248 @ doc """
231249 Computes the difference between two types.
232250 """
233- def difference ( left , :term ) , do: Map . take ( unfold ( left ) , [ :optional ] )
251+ def difference ( left , :term ) , do: keep_optional ( left )
234252
235253 def difference ( left , right ) do
236254 left = unfold ( left )
@@ -250,7 +268,7 @@ defmodule Module.Types.Descr do
250268 end
251269
252270 # For static types, the difference is component-wise.
253- defp difference_static ( left , :term ) , do: Map . take ( unfold ( left ) , [ :optional ] )
271+ defp difference_static ( left , :term ) , do: keep_optional ( left )
254272
255273 defp difference_static ( left , right ) do
256274 iterator_difference_static ( :maps . next ( :maps . iterator ( unfold ( right ) ) ) , unfold ( left ) )
@@ -260,10 +278,12 @@ defmodule Module.Types.Descr do
260278 acc =
261279 case map do
262280 % { ^ key => v1 } ->
263- case difference ( key , v1 , v2 ) do
264- 0 -> Map . delete ( map , key )
265- [ ] -> Map . delete ( map , key )
266- value -> % { map | key => value }
281+ value = difference ( key , v1 , v2 )
282+
283+ if value in @ empty_difference do
284+ Map . delete ( map , key )
285+ else
286+ % { map | key => value }
267287 end
268288
269289 % { } ->
@@ -319,6 +339,13 @@ defmodule Module.Types.Descr do
319339 end
320340 end
321341
342+ # For atom, bitmap, and optional, if the key is present,
343+ # then they are not empty,
344+ defp empty_key? ( :map , value ) , do: map_empty? ( value )
345+ defp empty_key? ( :list , value ) , do: list_empty? ( value )
346+ defp empty_key? ( :tuple , value ) , do: tuple_empty? ( value )
347+ defp empty_key? ( _ , _value ) , do: false
348+
322349 @ doc """
323350 Converts a descr to its quoted representation.
324351 """
@@ -370,8 +397,6 @@ defmodule Module.Types.Descr do
370397 Because of the dynamic/static invariant in the `descr`, subtyping can be
371398 simplified in several cases according to which type is gradual or not.
372399 """
373- def subtype? ( left , :term ) , do: left != @ not_set
374-
375400 def subtype? ( left , right ) do
376401 left = unfold ( left )
377402 right = unfold ( right )
@@ -450,20 +475,21 @@ defmodule Module.Types.Descr do
450475 include `dynamic()`, `integer()`, but also `dynamic() and (integer() or atom())`.
451476 Incompatible subtypes include `integer() or list()`, `dynamic() and atom()`.
452477 """
453- def compatible? ( left , :term ) , do: not empty? ( remove_optional ( left ) )
454-
455478 def compatible? ( left , right ) do
456- left = unfold ( left )
457- right = unfold ( right )
458- { left_dynamic , left_static } = Map . pop ( left , :dynamic , left )
459- right_dynamic = Map . get ( right , :dynamic , right )
479+ { left_dynamic , left_static } =
480+ case left do
481+ :term -> { :term , :term }
482+ _ -> Map . pop ( left , :dynamic , left )
483+ end
460484
461- if empty? ( left_static ) do
462- cond do
463- left_dynamic == :term -> not empty? ( right_dynamic )
464- right_dynamic == :term -> not empty? ( left_dynamic )
465- true -> non_disjoint_intersection? ( left_dynamic , right_dynamic )
485+ right_dynamic =
486+ case right do
487+ % { dynamic: dynamic } -> dynamic
488+ _ -> right
466489 end
490+
491+ if empty? ( left_static ) do
492+ not disjoint? ( left_dynamic , right_dynamic )
467493 else
468494 subtype_static? ( left_static , right_dynamic )
469495 end
@@ -1035,7 +1061,12 @@ defmodule Module.Types.Descr do
10351061 defp dynamic_intersection ( left , right ) ,
10361062 do: symmetrical_intersection ( unfold ( left ) , unfold ( right ) , & intersection / 3 )
10371063
1038- defp dynamic_difference ( left , right ) , do: difference_static ( left , right )
1064+ defp dynamic_difference ( left , right ) do
1065+ case difference_static ( left , right ) do
1066+ value when value == @ none -> 0
1067+ value -> value
1068+ end
1069+ end
10391070
10401071 defp dynamic_to_quoted ( descr ) do
10411072 cond do
@@ -2248,10 +2279,12 @@ defmodule Module.Types.Descr do
22482279 acc =
22492280 case map do
22502281 % { ^ key => v2 } ->
2251- case fun . ( key , v1 , v2 ) do
2252- 0 -> acc
2253- value when value == @ none -> acc
2254- value -> [ { key , value } | acc ]
2282+ value = fun . ( key , v1 , v2 )
2283+
2284+ if value in @ empty_intersection do
2285+ acc
2286+ else
2287+ [ { key , value } | acc ]
22552288 end
22562289
22572290 % { } ->
@@ -2275,7 +2308,8 @@ defmodule Module.Types.Descr do
22752308
22762309 defp iterator_non_disjoint_intersection? ( { key , v1 , iterator } , map ) do
22772310 with % { ^ key => v2 } <- map ,
2278- value when value != 0 and value != @ none <- intersection ( key , v1 , v2 ) do
2311+ value when value not in @ empty_intersection <- intersection ( key , v1 , v2 ) ,
2312+ false <- empty_key? ( key , value ) do
22792313 true
22802314 else
22812315 _ -> iterator_non_disjoint_intersection? ( :maps . next ( iterator ) , map )
0 commit comments