@@ -59,12 +59,12 @@ defmodule Module.Types.Descr do
59
59
def non_empty_list ( ) , do: % { bitmap: @ bit_non_empty_list }
60
60
def open_map ( ) , do: % { map: @ map_top }
61
61
def open_map ( pairs ) , do: map_descr ( :open , pairs )
62
+ def open_tuple ( elements ) , do: tuple_descr ( :open , elements )
62
63
def pid ( ) , do: % { bitmap: @ bit_pid }
63
64
def port ( ) , do: % { bitmap: @ bit_port }
64
65
def reference ( ) , do: % { bitmap: @ bit_reference }
65
- def open_tuple ( elements ) , do: % { tuple: tuple_new ( :open , elements ) }
66
- def tuple ( elements ) , do: % { tuple: tuple_new ( :closed , elements ) }
67
66
def tuple ( ) , do: % { tuple: @ tuple_top }
67
+ def tuple ( elements ) , do: tuple_descr ( :closed , elements )
68
68
69
69
@ boolset :sets . from_list ( [ true , false ] , version: 2 )
70
70
def boolean ( ) , do: % { atom: { :union , @ boolset } }
@@ -727,9 +727,8 @@ defmodule Module.Types.Descr do
727
727
with the assumption that the descr is exclusively a map (or dynamic).
728
728
729
729
It returns a two element tuple or `:error`. The first element says
730
- if the type is optional or not, the second element is the type.
731
- In static mode, we likely want to raise if `map.field`
732
- (or pattern matching?) is called on an optional key.
730
+ if the type is dynamically optional or not, the second element is
731
+ the type. In static mode, optional keys are not allowed.
733
732
"""
734
733
def map_fetch ( :term , _key ) , do: :badmap
735
734
@@ -1234,6 +1233,28 @@ defmodule Module.Types.Descr do
1234
1233
# - {integer(), atom()} is encoded as {:closed, [integer(), atom()]}
1235
1234
# - {atom(), boolean(), ...} is encoded as {:open, [atom(), boolean()]}
1236
1235
1236
+ defp tuple_descr ( tag , fields ) do
1237
+ case tuple_descr ( fields , [ ] , false ) do
1238
+ { fields , true } -> % { dynamic: % { tuple: tuple_new ( tag , Enum . reverse ( fields ) ) } }
1239
+ { _ , false } -> % { tuple: tuple_new ( tag , fields ) }
1240
+ end
1241
+ end
1242
+
1243
+ defp tuple_descr ( [ :term | rest ] , acc , dynamic? ) do
1244
+ tuple_descr ( rest , [ :term | acc ] , dynamic? )
1245
+ end
1246
+
1247
+ defp tuple_descr ( [ value | rest ] , acc , dynamic? ) do
1248
+ case :maps . take ( :dynamic , value ) do
1249
+ :error -> tuple_descr ( rest , [ value | acc ] , dynamic? )
1250
+ { dynamic , _static } -> tuple_descr ( rest , [ dynamic | acc ] , true )
1251
+ end
1252
+ end
1253
+
1254
+ defp tuple_descr ( [ ] , acc , dynamic? ) do
1255
+ { acc , dynamic? }
1256
+ end
1257
+
1237
1258
defp tuple_new ( tag , elements ) , do: [ { tag , elements , [ ] } ]
1238
1259
1239
1260
defp tuple_intersection ( dnf1 , dnf2 ) do
@@ -1395,8 +1416,9 @@ defmodule Module.Types.Descr do
1395
1416
with the assumption that the descr is exclusively a tuple (or dynamic).
1396
1417
1397
1418
Returns one of:
1419
+
1398
1420
- `{false, type}` if the element is always accessible and has the given `type`.
1399
- - `{true, type}` if the element may exist and has the given `type`.
1421
+ - `{true, type}` if the element is dynamically optional and has the given `type`.
1400
1422
- `:badindex` if the index is never accessible in the tuple type.
1401
1423
- `:badtuple` if the descr is not a tuple type.
1402
1424
@@ -1405,8 +1427,6 @@ defmodule Module.Types.Descr do
1405
1427
iex> tuple_fetch(tuple([integer(), atom()]), 0)
1406
1428
{false, integer()}
1407
1429
1408
- :badindex
1409
-
1410
1430
iex> tuple_fetch(union(tuple([integer()]), tuple([integer(), atom()])), 1)
1411
1431
{true, atom()}
1412
1432
@@ -1420,7 +1440,7 @@ defmodule Module.Types.Descr do
1420
1440
def tuple_fetch ( _ , index ) when index < 0 , do: :badindex
1421
1441
def tuple_fetch ( :term , _key ) , do: :badtuple
1422
1442
1423
- def tuple_fetch ( % { } = descr , key ) do
1443
+ def tuple_fetch ( % { } = descr , key ) when is_integer ( key ) do
1424
1444
case :maps . take ( :dynamic , descr ) do
1425
1445
:error ->
1426
1446
if descr_key? ( descr , :tuple ) and tuple_only? ( descr ) do
0 commit comments