@@ -11,8 +11,7 @@ defmodule Module.Types.Descr do
11
11
# * DNF - disjunctive normal form which is a pair of unions and negations.
12
12
# In the case of maps, we augment each pair with the open/closed tag.
13
13
14
- # TODO: When we convert from AST to descr, we need to normalize
15
- # the dynamic type.
14
+ # TODO: When we convert from AST to descr, we need to normalize the dynamic type.
16
15
import Bitwise
17
16
18
17
@ bit_binary 1 <<< 0
@@ -28,6 +27,7 @@ defmodule Module.Types.Descr do
28
27
@ bit_fun 1 <<< 9
29
28
@ bit_top ( 1 <<< 10 ) - 1
30
29
30
+ @ bit_list @ bit_empty_list ||| @ bit_non_empty_list
31
31
@ bit_number @ bit_integer ||| @ bit_float
32
32
@ bit_optional 1 <<< 10
33
33
@@ -50,16 +50,16 @@ defmodule Module.Types.Descr do
50
50
def atom ( as ) , do: % { atom: atom_new ( as ) }
51
51
def atom ( ) , do: % { atom: @ atom_top }
52
52
def binary ( ) , do: % { bitmap: @ bit_binary }
53
+ def closed_map ( pairs ) , do: % { map: map_new ( :closed , Map . new ( pairs ) ) }
53
54
def empty_list ( ) , do: % { bitmap: @ bit_empty_list }
55
+ def empty_map ( ) , do: % { map: @ map_empty }
54
56
def integer ( ) , do: % { bitmap: @ bit_integer }
55
57
def float ( ) , do: % { bitmap: @ bit_float }
56
58
def fun ( ) , do: % { bitmap: @ bit_fun }
57
- # TODO: We need to propagate any dynamic up
58
- def open_map ( pairs ) , do: % { map: map_new ( :open , Map . new ( pairs ) ) }
59
- def closed_map ( pairs ) , do: % { map: map_new ( :closed , Map . new ( pairs ) ) }
60
- def open_map ( ) , do: % { map: @ map_top }
61
- def empty_map ( ) , do: % { map: @ map_empty }
59
+ def list ( ) , do: % { bitmap: @ bit_list }
62
60
def non_empty_list ( ) , do: % { bitmap: @ bit_non_empty_list }
61
+ def open_map ( ) , do: % { map: @ map_top }
62
+ def open_map ( pairs ) , do: % { map: map_new ( :open , Map . new ( pairs ) ) }
63
63
def pid ( ) , do: % { bitmap: @ bit_pid }
64
64
def port ( ) , do: % { bitmap: @ bit_port }
65
65
def reference ( ) , do: % { bitmap: @ bit_reference }
@@ -81,33 +81,17 @@ defmodule Module.Types.Descr do
81
81
# `not_set()` has no meaning outside of map types.
82
82
83
83
@ not_set % { bitmap: @ bit_optional }
84
- @ term_or_not_set % { bitmap: @ bit_top ||| @ bit_optional , atom: @ atom_top , map: @ map_top }
84
+ @ term_or_optional % { bitmap: @ bit_top ||| @ bit_optional , atom: @ atom_top , map: @ map_top }
85
85
86
86
def not_set ( ) , do: @ not_set
87
87
def if_set ( type ) , do: Map . update ( type , :bitmap , @ bit_optional , & ( & 1 ||| @ bit_optional ) )
88
- defp term_or_not_set ( ) , do: @ term_or_not_set
88
+ defp term_or_optional ( ) , do: @ term_or_optional
89
89
90
- ## Query operations
90
+ ## Set operations
91
91
92
92
def term_type? ( descr ) , do: subtype_static ( @ term , Map . delete ( descr , :dynamic ) )
93
93
def gradual? ( descr ) , do: is_map_key ( descr , :dynamic )
94
94
95
- @ doc """
96
- Optimized check if the type represents any number.
97
- """
98
- def number_type? ( % { dynamic: % { bitmap: bitmap } } ) when ( bitmap &&& @ bit_number ) != 0 , do: true
99
- def number_type? ( % { bitmap: bitmap } ) when ( bitmap &&& @ bit_number ) != 0 , do: true
100
- def number_type? ( % { } ) , do: false
101
-
102
- @ doc """
103
- Optimized check if the type represents any atom.
104
- """
105
- def atom_type? ( % { dynamic: % { atom: _ } } ) , do: true
106
- def atom_type? ( % { atom: _ } ) , do: true
107
- def atom_type? ( % { } ) , do: false
108
-
109
- ## Set operations
110
-
111
95
@ doc """
112
96
Make a whole type dynamic.
113
97
@@ -228,6 +212,10 @@ defmodule Module.Types.Descr do
228
212
@ doc """
229
213
Converts a descr to its quoted representation.
230
214
"""
215
+ def to_quoted ( @ term ) do
216
+ { :term , [ ] , [ ] }
217
+ end
218
+
231
219
def to_quoted ( % { } = descr ) do
232
220
case Enum . flat_map ( descr , fn { key , value } -> to_quoted ( key , value ) end ) do
233
221
[ ] -> { :none , [ ] , [ ] }
@@ -299,11 +287,6 @@ defmodule Module.Types.Descr do
299
287
left == right or ( subtype? ( left , right ) and subtype? ( right , left ) )
300
288
end
301
289
302
- @ doc """
303
- Check if two types have a non-empty intersection.
304
- """
305
- def intersect? ( left , right ) , do: not empty? ( intersection ( left , right ) )
306
-
307
290
@ doc """
308
291
Checks if a type is a compatible subtype of another.
309
292
@@ -325,14 +308,42 @@ defmodule Module.Types.Descr do
325
308
expected_dynamic = Map . get ( expected_type , :dynamic , expected_type )
326
309
327
310
if empty? ( input_static ) do
328
- intersect? ( input_dynamic , expected_dynamic )
311
+ not empty? ( intersection ( input_dynamic , expected_dynamic ) )
329
312
else
330
313
subtype_static ( input_static , expected_dynamic )
331
314
end
332
315
end
333
316
334
317
## Bitmaps
335
318
319
+ @ doc """
320
+ Optimized version of `not empty?(intersection(binary(), type))`.
321
+ """
322
+ def binary_type? ( % { dynamic: % { bitmap: bitmap } } ) when ( bitmap &&& @ bit_binary ) != 0 , do: true
323
+ def binary_type? ( % { bitmap: bitmap } ) when ( bitmap &&& @ bit_binary ) != 0 , do: true
324
+ def binary_type? ( % { } ) , do: false
325
+
326
+ @ doc """
327
+ Optimized version of `not empty?(intersection(integer(), type))`.
328
+ """
329
+ def integer_type? ( % { dynamic: % { bitmap: bitmap } } ) when ( bitmap &&& @ bit_integer ) != 0 , do: true
330
+ def integer_type? ( % { bitmap: bitmap } ) when ( bitmap &&& @ bit_integer ) != 0 , do: true
331
+ def integer_type? ( % { } ) , do: false
332
+
333
+ @ doc """
334
+ Optimized version of `not empty?(intersection(float(), type))`.
335
+ """
336
+ def float_type? ( % { dynamic: % { bitmap: bitmap } } ) when ( bitmap &&& @ bit_float ) != 0 , do: true
337
+ def float_type? ( % { bitmap: bitmap } ) when ( bitmap &&& @ bit_float ) != 0 , do: true
338
+ def float_type? ( % { } ) , do: false
339
+
340
+ @ doc """
341
+ Optimized version of `not empty?(intersection(integer() or float(), type))`.
342
+ """
343
+ def number_type? ( % { dynamic: % { bitmap: bitmap } } ) when ( bitmap &&& @ bit_number ) != 0 , do: true
344
+ def number_type? ( % { bitmap: bitmap } ) when ( bitmap &&& @ bit_number ) != 0 , do: true
345
+ def number_type? ( % { } ) , do: false
346
+
336
347
defp bitmap_union ( v1 , v2 ) , do: v1 ||| v2
337
348
defp bitmap_intersection ( v1 , v2 ) , do: v1 &&& v2
338
349
defp bitmap_difference ( v1 , v2 ) , do: v1 - ( v1 &&& v2 )
@@ -375,6 +386,31 @@ defmodule Module.Types.Descr do
375
386
# an empty list of atoms. It is simplified to `0` in set operations, and the key
376
387
# is removed from the map.
377
388
389
+ @ doc """
390
+ Optimized version of `not empty?(intersection(atom(), type))`.
391
+ """
392
+ def atom_type? ( % { dynamic: % { atom: _ } } ) , do: true
393
+ def atom_type? ( % { atom: _ } ) , do: true
394
+ def atom_type? ( % { } ) , do: false
395
+
396
+ @ doc """
397
+ Optimized version of `not empty?(intersection(atom([atom]), type))`.
398
+ """
399
+ def atom_type? ( % { } = descr , atom ) do
400
+ case :maps . take ( :dynamic , descr ) do
401
+ :error ->
402
+ atom_only? ( descr ) and atom_type_static? ( descr , atom )
403
+
404
+ { dynamic , static } ->
405
+ atom_only? ( static ) and
406
+ ( atom_type_static? ( dynamic , atom ) or atom_type_static? ( static , atom ) )
407
+ end
408
+ end
409
+
410
+ defp atom_type_static? ( % { atom: { :union , set } } , atom ) , do: :sets . is_element ( atom , set )
411
+ defp atom_type_static? ( % { atom: { :negation , set } } , atom ) , do: not :sets . is_element ( atom , set )
412
+ defp atom_type_static? ( % { } , _atom ) , do: false
413
+
378
414
@ doc """
379
415
Returns a set of all known atoms.
380
416
@@ -592,28 +628,16 @@ defmodule Module.Types.Descr do
592
628
defp optional? ( % { bitmap: bitmap } ) when ( bitmap &&& @ bit_optional ) != 0 , do: true
593
629
defp optional? ( _ ) , do: false
594
630
595
- defp pop_not_set ( type ) do
596
- case type do
597
- % { bitmap: @ bit_optional } ->
598
- { true , Map . delete ( type , :bitmap ) }
599
-
600
- % { bitmap: bitmap } when ( bitmap &&& @ bit_optional ) != 0 ->
601
- { true , % { type | bitmap: bitmap - @ bit_optional } }
602
-
603
- _ ->
604
- { false , type }
605
- end
606
- end
607
-
608
- defp map_tag_to_type ( :open ) , do: term_or_not_set ( )
631
+ defp map_tag_to_type ( :open ) , do: term_or_optional ( )
609
632
defp map_tag_to_type ( :closed ) , do: not_set ( )
610
633
634
+ # TODO: We need to propagate any dynamic up
611
635
defp map_new ( tag , fields ) , do: [ { tag , fields , [ ] } ]
612
636
defp map_descr ( tag , fields ) , do: % { map: map_new ( tag , fields ) }
613
637
614
638
@ doc """
615
- Gets the type of the value returned by accessing `key` on `map`
616
- with the assumption that the descr is exclusively a map.
639
+ Fetches the type of the value returned by accessing `key` on `map`
640
+ with the assumption that the descr is exclusively a map (or dynamic) .
617
641
618
642
It returns a two element tuple or `:error`. The first element says
619
643
if the type is optional or not, the second element is the type.
@@ -625,18 +649,24 @@ defmodule Module.Types.Descr do
625
649
:error ->
626
650
if is_map_key ( descr , :map ) and map_only? ( descr ) do
627
651
map_fetch_static ( descr , key )
652
+ |> none_if_not_set ( )
653
+ |> badkey_if_empty_or_tag ( false )
628
654
else
629
- :error
655
+ :badmap
630
656
end
631
657
658
+ { % { map: { :open , fields , [ ] } } , static } when fields == % { } and static == @ none ->
659
+ { true , dynamic ( ) }
660
+
632
661
{ dynamic , static } ->
633
662
if ( is_map_key ( dynamic , :map ) or is_map_key ( static , :map ) ) and map_only? ( static ) do
634
- { dynamic_optional ?, dynamic_type } = map_fetch_static ( dynamic , key )
635
- { static_optional? , static_type } = map_fetch_static ( static , key )
663
+ { optional ?, dynamic_type } = map_fetch_static ( dynamic , key ) |> pop_not_set ( )
664
+ static_type = map_fetch_static ( static , key ) |> none_if_not_set ( )
636
665
637
- { dynamic_optional? or static_optional? , union ( dynamic ( dynamic_type ) , static_type ) }
666
+ union ( dynamic ( dynamic_type ) , static_type )
667
+ |> badkey_if_empty_or_tag ( optional? )
638
668
else
639
- :error
669
+ :badmap
640
670
end
641
671
end
642
672
end
@@ -645,13 +675,32 @@ defmodule Module.Types.Descr do
645
675
646
676
defp map_fetch_static ( descr , key ) when is_atom ( key ) do
647
677
case descr do
648
- % { map: map } ->
649
- map_split_on_key ( map , key )
650
- |> Enum . reduce ( none ( ) , & union / 2 )
651
- |> pop_not_set ( )
678
+ % { map: map } -> Enum . reduce ( map_split_on_key ( map , key ) , none ( ) , & union / 2 )
679
+ % { } -> none ( )
680
+ end
681
+ end
682
+
683
+ defp badkey_if_empty_or_tag ( type , tag ) do
684
+ if empty? ( type ) , do: :badkey , else: { tag , type }
685
+ end
652
686
653
- % { } ->
654
- { false , none ( ) }
687
+ defp pop_not_set ( type ) do
688
+ case type do
689
+ % { bitmap: @ bit_optional } ->
690
+ { true , Map . delete ( type , :bitmap ) }
691
+
692
+ % { bitmap: bitmap } when ( bitmap &&& @ bit_optional ) != 0 ->
693
+ { true , % { type | bitmap: bitmap - @ bit_optional } }
694
+
695
+ _ ->
696
+ { false , type }
697
+ end
698
+ end
699
+
700
+ defp none_if_not_set ( type ) do
701
+ case type do
702
+ % { bitmap: bitmap } when ( bitmap &&& @ bit_optional ) != 0 -> @ none
703
+ _ -> type
655
704
end
656
705
end
657
706
@@ -780,7 +829,7 @@ defmodule Module.Types.Descr do
780
829
781
830
# There may be value in common
782
831
tag == :open ->
783
- diff = difference ( term_or_not_set ( ) , neg_type )
832
+ diff = difference ( term_or_optional ( ) , neg_type )
784
833
empty? ( diff ) or map_empty? ( tag , Map . put ( fields , neg_key , diff ) , negs )
785
834
end
786
835
end
@@ -816,7 +865,7 @@ defmodule Module.Types.Descr do
816
865
# %{...} the open map in a positive intersection can be ignored
817
866
{ fst , snd } =
818
867
if tag == :open and fields == % { } do
819
- { term_or_not_set ( ) , term_or_not_set ( ) }
868
+ { term_or_optional ( ) , term_or_optional ( ) }
820
869
else
821
870
map_pop_key ( tag , fields , key )
822
871
end
0 commit comments