Skip to content

Commit d76694f

Browse files
committed
Provide already mapped domain keys
1 parent 00f593b commit d76694f

File tree

1 file changed

+89
-129
lines changed

1 file changed

+89
-129
lines changed

lib/elixir/lib/module/types/descr.ex

Lines changed: 89 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,18 @@ defmodule Module.Types.Descr do
2929
defmacrop domain_key(key), do: {:domain_key, key}
3030

3131
@domain_key_types [
32-
:binary,
33-
:empty_list,
34-
:integer,
35-
:float,
36-
:pid,
37-
:port,
38-
:reference,
39-
:fun,
40-
:atom,
41-
:tuple,
42-
:map,
43-
:list
32+
{:domain_key, :binary},
33+
{:domain_key, :empty_list},
34+
{:domain_key, :integer},
35+
{:domain_key, :float},
36+
{:domain_key, :pid},
37+
{:domain_key, :port},
38+
{:domain_key, :reference},
39+
{:domain_key, :fun},
40+
{:domain_key, :atom},
41+
{:domain_key, :tuple},
42+
{:domain_key, :map},
43+
{:domain_key, :list}
4444
]
4545

4646
@fun_top :fun_top
@@ -90,40 +90,16 @@ defmodule Module.Types.Descr do
9090
def atom(as), do: %{atom: atom_new(as)}
9191
def atom(), do: %{atom: @atom_top}
9292
def binary(), do: %{bitmap: @bit_binary}
93-
94-
def closed_map(pairs) do
95-
{regular_pairs, domain_pairs} = split_domain_key_pairs(pairs)
96-
domain_pairs = validate_domain_keys(domain_pairs)
97-
98-
if domain_pairs == [],
99-
do: map_descr(:closed, regular_pairs),
100-
else: map_descr(domain_pairs, regular_pairs)
101-
end
102-
93+
def closed_map(pairs), do: map_descr(:closed, pairs, @term_or_optional, false)
10394
def empty_list(), do: %{bitmap: @bit_empty_list}
10495
def empty_map(), do: %{map: @map_empty}
10596
def integer(), do: %{bitmap: @bit_integer}
10697
def float(), do: %{bitmap: @bit_float}
10798
def list(type), do: list_descr(type, @empty_list, true)
10899
def non_empty_list(type, tail \\ @empty_list), do: list_descr(type, tail, false)
109-
110100
def open_map(), do: %{map: @map_top}
111-
def open_map(pairs), do: open_map(pairs, @term_or_optional, false)
112-
def open_map(pairs, default), do: open_map(pairs, if_set(default), true)
113-
114-
defp open_map(pairs, default, force?) do
115-
{regular_pairs, domain_pairs} = split_domain_key_pairs(pairs)
116-
domain_pairs = validate_domain_keys(domain_pairs)
117-
118-
if domain_pairs != [] or force?,
119-
do:
120-
Map.new(@domain_key_types, fn key_type -> {domain_key(key_type), default} end)
121-
|> Map.merge(Map.new(domain_pairs))
122-
|> Map.to_list()
123-
|> map_descr(regular_pairs),
124-
else: map_descr(:open, regular_pairs)
125-
end
126-
101+
def open_map(pairs), do: map_descr(:open, pairs, @term_or_optional, false)
102+
def open_map(pairs, default), do: map_descr(:open, pairs, if_set(default), true)
127103
def open_tuple(elements, _fallback \\ term()), do: tuple_descr(:open, elements)
128104
def pid(), do: %{bitmap: @bit_pid}
129105
def port(), do: %{bitmap: @bit_port}
@@ -2238,43 +2214,56 @@ defmodule Module.Types.Descr do
22382214
# The type %{..., atom() => integer()} represents maps with atom keys bound to integers,
22392215
# and other keys bound to any type, represented by {{:closed, %{atom: integer()}}, %{}, []}.
22402216

2241-
defp map_descr(tag, fields) when is_atom(tag) do
2242-
case map_descr_pairs(fields, [], false) do
2243-
{fields, true} ->
2244-
%{dynamic: %{map: map_new(tag, fields |> Enum.reverse() |> :maps.from_list())}}
2217+
defp map_descr(tag, pairs, default, force?) do
2218+
{fields, domains, dynamic?} = map_descr_pairs(pairs, [], %{}, false)
22452219

2246-
{_, false} ->
2247-
%{map: map_new(tag, :maps.from_list(fields))}
2220+
map_new =
2221+
if domains != %{} or force? do
2222+
domains =
2223+
if tag == :open do
2224+
Enum.reduce(@domain_key_types, domains, &Map.put_new(&2, &1, default))
2225+
else
2226+
domains
2227+
end
2228+
2229+
map_new({tag, domains}, fields)
2230+
else
2231+
map_new(tag, fields)
2232+
end
2233+
2234+
case dynamic? do
2235+
true -> %{dynamic: %{map: map_new}}
2236+
false -> %{map: map_new}
22482237
end
22492238
end
22502239

2251-
defp map_descr(domains, fields) do
2252-
{fields, fields_dynamic?} = map_descr_pairs(fields, [], false)
2253-
{domains, domains_dynamic?} = map_descr_pairs(domains, [], false)
2254-
2255-
fields_map = :maps.from_list(if fields_dynamic?, do: Enum.reverse(fields), else: fields)
2256-
domains_map = :maps.from_list(if domains_dynamic?, do: Enum.reverse(domains), else: domains)
2240+
# TOD: Double check if we indeed want the union here
2241+
defp map_put_domain(domain, key, value) do
2242+
Map.update(domain, key, if_set(value), &union(&1, value))
2243+
end
22572244

2258-
if fields_dynamic? or domains_dynamic? do
2259-
%{dynamic: %{map: map_new(:closed, fields_map, domains_map)}}
2260-
else
2261-
%{map: map_new(:closed, fields_map, domains_map)}
2245+
defp map_descr_pairs([{key, :term} | rest], fields, domain, dynamic?) do
2246+
case is_atom(key) do
2247+
true -> map_descr_pairs(rest, [{key, :term} | fields], domain, dynamic?)
2248+
false -> map_descr_pairs(rest, fields, map_put_domain(domain, key, :term), dynamic?)
22622249
end
22632250
end
22642251

2265-
defp map_descr_pairs([{key, :term} | rest], acc, dynamic?) do
2266-
map_descr_pairs(rest, [{key, :term} | acc], dynamic?)
2267-
end
2252+
defp map_descr_pairs([{key, value} | rest], fields, domain, dynamic?) do
2253+
{value, dynamic?} =
2254+
case :maps.take(:dynamic, value) do
2255+
:error -> {value, dynamic?}
2256+
{dynamic, _static} -> {dynamic, true}
2257+
end
22682258

2269-
defp map_descr_pairs([{key, value} | rest], acc, dynamic?) do
2270-
case :maps.take(:dynamic, value) do
2271-
:error -> map_descr_pairs(rest, [{key, value} | acc], dynamic?)
2272-
{dynamic, _static} -> map_descr_pairs(rest, [{key, dynamic} | acc], true)
2259+
case is_atom(key) do
2260+
true -> map_descr_pairs(rest, [{key, value} | fields], domain, dynamic?)
2261+
false -> map_descr_pairs(rest, fields, map_put_domain(domain, key, value), dynamic?)
22732262
end
22742263
end
22752264

2276-
defp map_descr_pairs([], acc, dynamic?) do
2277-
{acc, dynamic?}
2265+
defp map_descr_pairs([], fields, domain, dynamic?) do
2266+
{fields |> Enum.reverse() |> :maps.from_list(), domain, dynamic?}
22782267
end
22792268

22802269
# TODO: Rename this to tuple_tag_to_type
@@ -2289,40 +2278,10 @@ defmodule Module.Types.Descr do
22892278
defp map_key_tag_to_type({:open, domain}),
22902279
do: Map.get(domain, domain_key(:atom), term_or_optional())
22912280

2292-
# Helpers for domain key validation
2293-
# TODO: Merge this and the next clause into one
2294-
defp split_domain_key_pairs(pairs) do
2295-
Enum.split_with(pairs, fn
2296-
{domain_key(_), _} -> false
2297-
_ -> true
2298-
end)
2299-
end
2300-
2301-
defp validate_domain_keys(pairs) do
2302-
# Check if domain keys are valid and don't overlap
2303-
domains = Enum.map(pairs, fn {domain_key(domain), _} -> domain end)
2304-
2305-
if length(domains) != length(Enum.uniq(domains)) do
2306-
raise ArgumentError, "Domain key types should not overlap"
2307-
end
2308-
2309-
# Check that all domain keys are valid
2310-
invalid_domains = Enum.reject(domains, &(&1 in @domain_key_types))
2311-
2312-
if invalid_domains != [] do
2313-
raise ArgumentError,
2314-
"Invalid domain key types: #{inspect(invalid_domains)}. " <>
2315-
"Valid types are: #{inspect(@domain_key_types)}"
2316-
end
2317-
2318-
Enum.map(pairs, fn {key, type} -> {key, if_set(type)} end)
2319-
end
2320-
23212281
defguardp is_optional_static(map)
23222282
when is_map(map) and is_map_key(map, :optional)
23232283

23242284
defp map_new(tag, fields = %{}), do: [{tag, fields, []}]
2325-
defp map_new(tag, fields = %{}, domains = %{}), do: [{{tag, domains}, fields, []}]
23262285

23272286
defp map_only?(descr), do: empty?(Map.delete(descr, :map))
23282287

@@ -2534,15 +2493,15 @@ defmodule Module.Types.Descr do
25342493
new_domains =
25352494
for domain_key <- @domain_key_types, reduce: %{} do
25362495
acc_domains ->
2537-
type1 = Map.get(domains1, domain_key(domain_key), default1)
2538-
type2 = Map.get(domains2, domain_key(domain_key), default2)
2496+
type1 = Map.get(domains1, domain_key, default1)
2497+
type2 = Map.get(domains2, domain_key, default2)
25392498

25402499
inter = intersection(type1, type2)
25412500

25422501
if empty?(inter) do
25432502
acc_domains
25442503
else
2545-
Map.put(acc_domains, domain_key(domain_key), inter)
2504+
Map.put(acc_domains, domain_key, inter)
25462505
end
25472506
end
25482507

@@ -2781,8 +2740,8 @@ defmodule Module.Types.Descr do
27812740
{:atom, atom_key}, acc ->
27822741
map_refresh_atom(acc, atom_key, type)
27832742

2784-
key, acc ->
2785-
map_refresh_domain(acc, key, type)
2743+
domain_key, acc ->
2744+
map_refresh_domain(acc, domain_key, type)
27862745
end)
27872746

27882747
{:ok, new_descr}
@@ -2861,27 +2820,27 @@ defmodule Module.Types.Descr do
28612820
considered_keys
28622821
|> :sets.to_list()
28632822
|> Enum.reduce(descr, fn key, acc -> map_refresh_key(acc, key, type) end)
2864-
|> map_refresh_domain(:atom, type)
2823+
|> map_refresh_domain(domain_key(:atom), type)
28652824
end
28662825
end
28672826

2868-
def map_refresh_tag(tag, domain, type) do
2827+
def map_refresh_tag(tag, domain_key, type) do
28692828
case tag do
28702829
:open ->
28712830
:open
28722831

28732832
:closed ->
2874-
{:closed, %{domain_key(domain) => if_set(type)}}
2833+
{:closed, %{domain_key => if_set(type)}}
28752834

28762835
{:open, domains} ->
2877-
if Map.has_key?(domains, domain_key(domain)) do
2878-
{:open, Map.update!(domains, domain_key(domain), &union(&1, type))}
2836+
if Map.has_key?(domains, domain_key) do
2837+
{:open, Map.update!(domains, domain_key, &union(&1, type))}
28792838
else
28802839
{:open, domains}
28812840
end
28822841

28832842
{:closed, domains} ->
2884-
{:closed, Map.update(domains, domain_key(domain), if_set(type), &union(&1, type))}
2843+
{:closed, Map.update(domains, domain_key, if_set(type), &union(&1, type))}
28852844
end
28862845
end
28872846

@@ -3007,21 +2966,22 @@ defmodule Module.Types.Descr do
30072966
cond do
30082967
type_kind == :atom -> [{:atom, type} | acc]
30092968
type_kind == :bitmap -> bitmap_to_domain_keys(type) ++ acc
3010-
not empty?(%{type_kind => type}) -> [type_kind | acc]
2969+
not empty?(%{type_kind => type}) -> [domain_key(type_kind) | acc]
30112970
true -> acc
30122971
end
30132972
end
30142973
end
30152974

2975+
# TODO: Optimize this
30162976
defp bitmap_to_domain_keys(bitmap) do
30172977
[
3018-
if((bitmap &&& @bit_binary) != 0, do: :binary),
3019-
if((bitmap &&& @bit_empty_list) != 0, do: :empty_list),
3020-
if((bitmap &&& @bit_integer) != 0, do: :integer),
3021-
if((bitmap &&& @bit_float) != 0, do: :float),
3022-
if((bitmap &&& @bit_pid) != 0, do: :pid),
3023-
if((bitmap &&& @bit_port) != 0, do: :port),
3024-
if((bitmap &&& @bit_reference) != 0, do: :reference)
2978+
if((bitmap &&& @bit_binary) != 0, do: domain_key(:binary)),
2979+
if((bitmap &&& @bit_empty_list) != 0, do: domain_key(:empty_list)),
2980+
if((bitmap &&& @bit_integer) != 0, do: domain_key(:integer)),
2981+
if((bitmap &&& @bit_float) != 0, do: domain_key(:float)),
2982+
if((bitmap &&& @bit_pid) != 0, do: domain_key(:pid)),
2983+
if((bitmap &&& @bit_port) != 0, do: domain_key(:port)),
2984+
if((bitmap &&& @bit_reference) != 0, do: domain_key(:reference))
30252985
]
30262986
|> Enum.reject(&is_nil/1)
30272987
end
@@ -3042,7 +3002,7 @@ defmodule Module.Types.Descr do
30423002

30433003
key_type, acc ->
30443004
# Note: we could stop if we reach term()_or_optional()
3045-
Map.get(domains, domain_key(key_type), map_key_tag_to_type(tag)) |> union(acc)
3005+
Map.get(domains, key_type, map_key_tag_to_type(tag)) |> union(acc)
30463006
end)
30473007
end
30483008

@@ -3053,8 +3013,8 @@ defmodule Module.Types.Descr do
30533013
{:atom, atom_type}, acc ->
30543014
map_get_atom(dnf, atom_type) |> union(acc)
30553015

3056-
key_type, acc ->
3057-
map_get_domain(dnf, key_type) |> union(acc)
3016+
domain_key, acc ->
3017+
map_get_domain(dnf, domain_key) |> union(acc)
30583018
end)
30593019
end
30603020

@@ -3107,7 +3067,7 @@ defmodule Module.Types.Descr do
31073067
union(type, acc)
31083068
end
31093069
end)
3110-
|> union(map_get_domain(dnf, :atom))
3070+
|> union(map_get_domain(dnf, domain_key(:atom)))
31113071
end
31123072
end
31133073

@@ -3127,24 +3087,24 @@ defmodule Module.Types.Descr do
31273087
end
31283088

31293089
# Take a map dnf and return the union of types for the given key domain.
3130-
def map_get_domain(dnf, key_domain) when is_atom(key_domain) do
3090+
def map_get_domain(dnf, domain_key(_) = domain_key) do
31313091
dnf
31323092
|> Enum.reduce(none(), fn
31333093
{tag, _fields, []}, acc when is_atom(tag) ->
31343094
map_key_tag_to_type(tag) |> union(acc)
31353095

31363096
# Optimization: if there are no negatives and domains exists, return its value
3137-
{{_tag, %{domain_key(^key_domain) => value}}, _fields, []}, acc ->
3097+
{{_tag, %{^domain_key => value}}, _fields, []}, acc ->
31383098
value |> union(acc)
31393099

31403100
# Optimization: if there are no negatives and the key does not exist, return the default type.
31413101
{{tag, %{}}, _fields, []}, acc ->
31423102
map_key_tag_to_type(tag) |> union(acc)
31433103

31443104
{tag, fields, negs}, acc ->
3145-
{fst, snd} = map_pop_domain(tag, fields, key_domain)
3105+
{fst, snd} = map_pop_domain(tag, fields, domain_key)
31463106

3147-
case map_split_negative_domain(negs, key_domain) do
3107+
case map_split_negative_domain(negs, domain_key) do
31483108
:empty ->
31493109
acc
31503110

@@ -3374,12 +3334,12 @@ defmodule Module.Types.Descr do
33743334
# Negative must contain all domain key types
33753335
negative_check =
33763336
Enum.all?(@domain_key_types, fn domain_key ->
3377-
domain_key_present = Map.has_key?(neg_domains, domain_key(domain_key))
3378-
pos_has_key = Map.has_key?(pos_domains, domain_key(domain_key))
3337+
domain_key_present = Map.has_key?(neg_domains, domain_key)
3338+
pos_has_key = Map.has_key?(pos_domains, domain_key)
33793339

33803340
domain_key_present &&
33813341
(pos_has_key ||
3382-
subtype?(term_or_optional(), Map.get(neg_domains, domain_key(domain_key))))
3342+
subtype?(term_or_optional(), Map.get(neg_domains, domain_key)))
33833343
end)
33843344

33853345
positive_check && negative_check
@@ -3404,7 +3364,7 @@ defmodule Module.Types.Descr do
34043364
{:open, {:closed, neg_domains}} ->
34053365
# The domains must include all possible domain key types, and they must be at least term_or_optional()
34063366
Enum.all?(@domain_key_types, fn domain_key ->
3407-
case Map.get(neg_domains, domain_key(domain_key)) do
3367+
case Map.get(neg_domains, domain_key) do
34083368
# Not all domain keys are present
34093369
nil -> false
34103370
type -> subtype?(term_or_optional(), type)
@@ -3422,7 +3382,7 @@ defmodule Module.Types.Descr do
34223382
{{:open, pos_domains}, :closed} ->
34233383
# The pos_domains must include all possible domain key types, and they must be subtypes of not_set()
34243384
Enum.all?(@domain_key_types, fn domain_key ->
3425-
case Map.get(pos_domains, domain_key(domain_key)) do
3385+
case Map.get(pos_domains, domain_key) do
34263386
# Not all domain keys are present
34273387
nil -> false
34283388
type -> subtype?(type, not_set())
@@ -3442,9 +3402,9 @@ defmodule Module.Types.Descr do
34423402
# returns {if_set(integer()), %{integer() => if_set(binary())}}
34433403
# If the domain is not present, use the tag to type as default.
34443404
defp map_pop_domain({tag, domains}, fields, domain_key) do
3445-
case :maps.take(domain_key(domain_key), domains) do
3446-
{value, domains} -> {value, %{map: map_new(tag, fields, domains)}}
3447-
:error -> {map_key_tag_to_type(tag), %{map: map_new(tag, fields, domains)}}
3405+
case :maps.take(domain_key, domains) do
3406+
{value, domains} -> {value, %{map: map_new({tag, domains}, fields)}}
3407+
:error -> {map_key_tag_to_type(tag), %{map: map_new({tag, domains}, fields)}}
34483408
end
34493409
end
34503410

0 commit comments

Comments
 (0)