Skip to content

Commit 4c5e1ac

Browse files
committed
done
1 parent f0cc63f commit 4c5e1ac

File tree

1 file changed

+178
-129
lines changed

1 file changed

+178
-129
lines changed

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

Lines changed: 178 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -2311,6 +2311,19 @@ defmodule Module.Types.Descr do
23112311
acc
23122312
end
23132313

2314+
defp domain_key_to_descr(domain_key(:atom)), do: atom()
2315+
defp domain_key_to_descr(domain_key(:binary)), do: binary()
2316+
defp domain_key_to_descr(domain_key(:empty_list)), do: empty_list()
2317+
defp domain_key_to_descr(domain_key(:integer)), do: integer()
2318+
defp domain_key_to_descr(domain_key(:float)), do: float()
2319+
defp domain_key_to_descr(domain_key(:pid)), do: pid()
2320+
defp domain_key_to_descr(domain_key(:port)), do: port()
2321+
defp domain_key_to_descr(domain_key(:reference)), do: reference()
2322+
defp domain_key_to_descr(domain_key(:fun)), do: fun()
2323+
defp domain_key_to_descr(domain_key(:tuple)), do: tuple()
2324+
defp domain_key_to_descr(domain_key(:map)), do: open_map()
2325+
defp domain_key_to_descr(domain_key(:list)), do: non_empty_list(term(), term())
2326+
23142327
defp map_descr(tag, pairs, default, force_domains?) do
23152328
{fields, domains, dynamic?} = map_descr_pairs(pairs, [], %{}, false)
23162329

@@ -2921,148 +2934,184 @@ defmodule Module.Types.Descr do
29212934
end
29222935
end
29232936

2924-
# def map_update(:term, key_descr, _type), do: :badmap
2925-
2926-
# def map_update(descr, key_descr, :term),
2927-
# do: map_update_shared(descr, key_descr, :term)
2928-
2929-
# def map_update(descr, key_descr, type) do
2930-
# case :maps.take(:dynamic, type) do
2931-
# :error -> map_update_shared(descr, key_descr, type)
2932-
# {dynamic, _static} -> map_update_shared(dynamic(descr), key_descr, dynamic)
2933-
# end
2934-
# end
2935-
2936-
# defp map_update_shared(descr, key_descr, type) do
2937-
# key_descr = upper_bound(key_descr)
2938-
2939-
# case :maps.take(:dynamic, descr) do
2940-
# :error ->
2941-
# if descr_key?(descr, :map) and map_only?(descr) do
2942-
# case map_existing_domains(descr, domain_keys) do
2943-
# [] -> :badkey
2944-
# domain_keys -> {:ok, map_update_domains(descr, domain_keys, type)}
2945-
# end
2946-
# else
2947-
# :badmap
2948-
# end
2949-
2950-
# {dynamic, static} ->
2951-
# if descr_key?(dynamic, :map) and map_only?(static) do
2952-
# case map_existing_domains(dynamic, domain_keys) do
2953-
# [] ->
2954-
# :badkey
2955-
2956-
# domain_keys ->
2957-
# {:ok,
2958-
# union(
2959-
# dynamic(map_update_domains(dynamic, domain_keys, type)),
2960-
# map_update_domains(static, domain_keys, type)
2961-
# )}
2962-
# end
2963-
# else
2964-
# :badmap
2965-
# end
2966-
# end
2967-
# end
2968-
2969-
# defp map_update_static(%{map: bdd}, key_descr, type) do
2970-
# dnf = map_bdd_to_dnf(bdd)
2971-
# {keys, key_descr} = map_split_keys(key_descr, bdd)
2972-
# {optional, value, descr} = map_update_keys(dnf, keys, type)
2973-
2974-
# key_descr
2975-
# |> to_domain_keys()
2976-
# |> Enum.reduce({optional, value, descr}, fn domain_key, acc ->
2977-
# map_update_domain(dnf, domain_key) |> union(acc)
2978-
# end)
2979-
# end
2980-
2981-
# defp map_update_static(%{}, _key_descr, _type) do
2982-
# # No optional keys, no returned value, none is returned
2983-
# {none(), none(), none()}
2984-
# end
2985-
2986-
# defp map_update_static(:term, key_descr, _type) do
2987-
# # The whole key_descr is optional, returned value is term, open map is returned
2988-
# {key_descr, term(), open_map()}
2989-
# end
2990-
2991-
# defp map_update_keys(dnf, keys, type) do
2992-
# Enum.reduce(keys, {none(), none(), none()}, fn atom, {optional, value, acc} ->
2993-
# {optional?, value, descr} = map_dnf_take_static(dnf, atom, value)
2994-
# optional = if optional?, do: union(optional, atom([atom])), else: optional
2995-
# descr = union(map_put_key_static(descr, atom, type), acc)
2996-
# {optional, value, descr}
2997-
# end)
2998-
# end
2999-
3000-
# defp map_update_atom_negation(%{atom: {:negation, atoms}} = descr, type) do
3001-
# # 1) Fetch all the possible keys in the bdd
3002-
# # 2) Get them all, except the ones in neg_atoms
3003-
# all_fields = map_merge_all_fields(dnf)
3004-
# acc = map_get_domain(dnf, domain_key(:atom))
3005-
3006-
# for {atom, _} <- all_fields, not :sets.is_element(atom, atoms), reduce: acc do
3007-
# acc ->
3008-
# {static_optional?, type} = map_dnf_fetch_static(dnf, atom)
3009-
3010-
# if static_optional? do
3011-
# union(type, acc) |> nil_or_type() |> if_set()
3012-
# else
3013-
# union(type, acc)
3014-
# end
3015-
# end
3016-
# end
3017-
3018-
defp map_existing_domains(%{map: bdd}, domain_keys) do
3019-
dnf = map_bdd_to_dnf(bdd)
2937+
@doc """
2938+
TODO
2939+
"""
2940+
def map_update(:term, _key_descr, _type), do: :badmap
30202941

3021-
Enum.filter(domain_keys, fn domain_key ->
3022-
Enum.any?(dnf, fn
3023-
{:open, _fields, []} ->
3024-
true
2942+
def map_update(descr, key_descr, :term),
2943+
do: map_update_shared(descr, key_descr, :term)
30252944

3026-
{:closed, _fields, []} ->
3027-
false
2945+
def map_update(descr, key_descr, type) do
2946+
case :maps.take(:dynamic, type) do
2947+
:error -> map_update_shared(descr, key_descr, type)
2948+
{dynamic, _static} -> map_update_shared(dynamic(descr), key_descr, dynamic)
2949+
end
2950+
end
2951+
2952+
defp map_update_shared(descr, key_descr, type) do
2953+
split_keys = map_split_keys_and_domains(key_descr)
30282954

3029-
{domains, _fields, []} ->
3030-
case domains do
3031-
%{^domain_key => type} -> not empty_or_optional?(type)
3032-
%{} -> false
2955+
case :maps.take(:dynamic, descr) do
2956+
:error ->
2957+
if descr_key?(descr, :map) and map_only?(descr) do
2958+
{present?, _value, descr} =
2959+
map_update_static(descr, split_keys, type, fn optional?, value ->
2960+
optional? or empty?(value)
2961+
end)
2962+
2963+
if present? do
2964+
{:ok, descr}
2965+
else
2966+
{:badomain, key_descr}
30332967
end
2968+
else
2969+
:badmap
2970+
end
30342971

3035-
{tag_or_domains, fields, negs} ->
3036-
{fst, snd} = map_pop_domain(tag_or_domains, fields, domain_key)
2972+
{dynamic, static} ->
2973+
if descr_key?(dynamic, :map) and map_only?(static) do
2974+
{static_present?, _value, static_descr} =
2975+
map_update_static(descr, split_keys, type, fn optional?, _ -> optional? end)
30372976

3038-
case map_split_negative_domain(negs, domain_key) do
3039-
:empty ->
3040-
false
2977+
{dynamic_present?, _value, dynamic_descr} =
2978+
map_update_static(descr, split_keys, type, fn _, value -> empty?(value) end)
30412979

3042-
negative ->
3043-
type =
2980+
if static_present? or dynamic_present? do
2981+
{:ok, union(static_descr, dynamic(dynamic_descr))}
2982+
else
2983+
{:badomain, key_descr}
2984+
end
2985+
else
2986+
:badmap
2987+
end
2988+
end
2989+
end
2990+
2991+
defp map_update_static(%{map: bdd}, split_keys, type, missing_fun) do
2992+
{required_keys, optional_keys, maybe_negated_set, required_domains, optional_domains} =
2993+
split_keys
2994+
2995+
non_negated = map_materialize_negated_set(maybe_negated_set, bdd)
2996+
dnf = map_bdd_to_dnf(bdd)
2997+
2998+
case map_update_get_domains(dnf, required_domains, none()) do
2999+
{required_domains, [], value} ->
3000+
# Optional domains can miss
3001+
{optional_domains, _, value} = map_update_get_domains(dnf, optional_domains, value)
3002+
3003+
# If any of required or optional domains are satisfied, we are already good
3004+
present? = required_domains != [] or optional_domains != []
3005+
3006+
# Now compute the initial return type. Note that map_update_static_keys works
3007+
# on the original bdd/dnf, not the one with updated domains
3008+
descr = map_update_put_domains(bdd, required_domains ++ optional_domains, type)
3009+
3010+
map_update_static_keys(
3011+
dnf,
3012+
required_keys,
3013+
optional_keys,
3014+
non_negated,
3015+
type,
3016+
missing_fun,
3017+
{present?, value, descr}
3018+
)
3019+
3020+
{_, [missing_domain | _], _} ->
3021+
{:baddomain, domain_key_to_descr(missing_domain)}
3022+
end
3023+
end
3024+
3025+
defp map_update_static(%{}, _split_keys, _type, _missing_fun) do
3026+
{false, none(), none()}
3027+
end
3028+
3029+
defp map_update_static(:term, split_keys, type, missing_fun) do
3030+
# Since it is an open map, we don't need to check the domains...
3031+
{required_keys, optional_keys, maybe_negated_set, required_domains, optional_domains} =
3032+
split_keys
3033+
3034+
non_negated = :sets.to_list(maybe_negated_set)
3035+
dnf = map_bdd_to_dnf(@map_top)
3036+
acc = {required_domains != [] or optional_domains != [], term(), open_map()}
3037+
map_update_static_keys(dnf, required_keys, optional_keys, non_negated, type, missing_fun, acc)
3038+
end
3039+
3040+
defp map_update_static_keys(dnf, required, optional, non_negated, type, missing_fun, acc) do
3041+
acc = map_update_keys(dnf, required, type, :required, missing_fun, acc)
3042+
acc = map_update_keys(dnf, optional, type, :optional, missing_fun, acc)
3043+
acc = map_update_keys(dnf, non_negated, type, :optional, missing_fun, acc)
3044+
acc
3045+
catch
3046+
{:badkey, key} -> {:badkey, key}
3047+
end
3048+
3049+
defp map_update_keys(dnf, keys, type, required_or_optional, missing_fun, acc) do
3050+
Enum.reduce(keys, acc, fn key, {present?, acc_value, acc_descr} ->
3051+
{optional?, value, descr} = map_dnf_take_static(dnf, key, none())
3052+
value = union(value, acc_value)
3053+
descr = union(map_put_key_static(descr, key, type), acc_descr)
3054+
3055+
missing? = missing_fun.(optional?, value)
3056+
required_or_optional == :required and missing? and throw({:badkey, key})
3057+
{present? or not missing?, value, descr}
3058+
end)
3059+
end
3060+
3061+
defp map_update_get_domains(dnf, domain_keys, acc) do
3062+
Enum.reduce(domain_keys, {[], [], acc}, fn domain_key, {valid, invalid, acc} ->
3063+
value =
3064+
Enum.reduce(dnf, none(), fn
3065+
{:open, _fields, []}, _acc ->
3066+
term()
3067+
3068+
{:closed, _fields, []}, acc ->
3069+
acc
3070+
3071+
{domains, _fields, []}, acc ->
3072+
case domains do
3073+
%{^domain_key => type} -> union(remove_optional_static(type), acc)
3074+
%{} -> acc
3075+
end
3076+
3077+
{tag_or_domains, fields, negs}, acc ->
3078+
{fst, snd} = map_pop_domain(tag_or_domains, fields, domain_key)
3079+
3080+
case map_split_negative_domain(negs, domain_key) do
3081+
:empty ->
3082+
acc
3083+
3084+
negative ->
30443085
negative
30453086
|> pair_make_disjoint()
30463087
|> pair_eliminate_negations_fst(fst, snd)
3088+
|> remove_optional_static()
3089+
|> union(acc)
3090+
end
3091+
end)
30473092

3048-
not empty_or_optional?(type)
3049-
end
3050-
end)
3093+
if empty?(value) do
3094+
{valid, [domain_key | invalid], acc}
3095+
else
3096+
{[domain_key | valid], invalid, union(acc, value)}
3097+
end
30513098
end)
30523099
end
30533100

3054-
defp map_update_domains(%{map: bdd}, domain_keys, type) do
3101+
defp map_update_put_domains(bdd, domain_keys, type) do
30553102
# For negations, we count on the idea that a negation will not remove any
30563103
# type from a domain unless it completely cancels out the type.
30573104
# So for any non-empty map bdd, we just update the domain with the new type,
30583105
# as well as its negations to keep them accurate.
30593106
%{
30603107
map:
3061-
bdd_map(bdd, fn {tag, fields} -> {map_update_domain(tag, domain_keys, type), fields} end)
3108+
bdd_map(bdd, fn {tag, fields} ->
3109+
{map_update_put_domain(tag, domain_keys, type), fields}
3110+
end)
30623111
}
30633112
end
30643113

3065-
defp map_update_domain(tag_or_domains, domain_keys, type) do
3114+
defp map_update_put_domain(tag_or_domains, domain_keys, type) do
30663115
case tag_or_domains do
30673116
:open ->
30683117
:open
@@ -3122,7 +3171,7 @@ defmodule Module.Types.Descr do
31223171
def map_get(:term, _key_descr), do: :badmap
31233172

31243173
def map_get(%{} = descr, key_descr) do
3125-
split_keys = map_split_keys(key_descr)
3174+
split_keys = map_split_keys_and_domains(key_descr)
31263175

31273176
case :maps.take(:dynamic, descr) do
31283177
:error ->
@@ -3162,11 +3211,11 @@ defmodule Module.Types.Descr do
31623211
defp nil_or_type(type), do: union(type, atom([nil]))
31633212

31643213
defp map_get_static(%{map: bdd}, split_keys) do
3165-
dnf = map_bdd_to_dnf(bdd)
3166-
31673214
{required_keys, optional_keys, maybe_negated_set, required_domains, optional_domains} =
31683215
split_keys
31693216

3217+
dnf = map_bdd_to_dnf(bdd)
3218+
31703219
acc = none()
31713220
acc = map_get_keys(dnf, required_keys, acc)
31723221
acc = map_get_keys(dnf, optional_keys, acc)
@@ -3232,7 +3281,7 @@ defmodule Module.Types.Descr do
32323281
end
32333282

32343283
# Compute which keys are optional, which ones are required, as well as domain keys
3235-
defp map_split_keys(%{dynamic: dynamic} = static) do
3284+
defp map_split_keys_and_domains(%{dynamic: dynamic} = static) do
32363285
{required_keys, optional_keys, maybe_negated_set} =
32373286
case {static, dynamic} do
32383287
{%{atom: {:union, static_union}}, %{atom: {:union, dynamic_union}}} ->
@@ -3264,20 +3313,20 @@ defmodule Module.Types.Descr do
32643313
{[], [], nil}
32653314
end
32663315

3267-
required_domain = to_domain_keys(Map.delete(static, :dynamic))
3268-
optional_domain = to_domain_keys(dynamic) -- required_domain
3269-
{required_keys, optional_keys, maybe_negated_set, required_domain, optional_domain}
3316+
required_domains = to_domain_keys(Map.delete(static, :dynamic))
3317+
optional_domains = to_domain_keys(dynamic) -- required_domains
3318+
{required_keys, optional_keys, maybe_negated_set, required_domains, optional_domains}
32703319
end
32713320

3272-
defp map_split_keys(%{atom: {:union, atoms}} = key_descr) do
3321+
defp map_split_keys_and_domains(%{atom: {:union, atoms}} = key_descr) do
32733322
{:sets.to_list(atoms), [], nil, to_domain_keys(key_descr), []}
32743323
end
32753324

3276-
defp map_split_keys(%{atom: {:negation, atoms}} = key_descr) do
3325+
defp map_split_keys_and_domains(%{atom: {:negation, atoms}} = key_descr) do
32773326
{[], [], atoms, to_domain_keys(key_descr), []}
32783327
end
32793328

3280-
defp map_split_keys(key_descr) do
3329+
defp map_split_keys_and_domains(key_descr) do
32813330
{[], [], nil, to_domain_keys(key_descr), []}
32823331
end
32833332

0 commit comments

Comments
 (0)