Skip to content

Commit 8664e7f

Browse files
committed
Make map_empty? short-circuiting
1 parent 4a0c84b commit 8664e7f

File tree

1 file changed

+30
-12
lines changed

1 file changed

+30
-12
lines changed

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

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2759,7 +2759,7 @@ defmodule Module.Types.Descr do
27592759
acc
27602760

27612761
:bdd_top ->
2762-
if map_empty?(tag, fields, negs), do: acc, else: [{tag, fields, negs} | acc]
2762+
if map_line_empty?(tag, fields, negs), do: acc, else: [{tag, fields, negs} | acc]
27632763

27642764
{{next_tag, next_fields} = next_map, left, right} ->
27652765
acc =
@@ -3407,13 +3407,31 @@ defmodule Module.Types.Descr do
34073407
# Short-circuits if it finds a non-empty map literal in the union.
34083408
# Since the algorithm is recursive, we implement the short-circuiting
34093409
# as throw/catch.
3410-
defp map_empty?(bdd), do: map_bdd_get(bdd) == []
3410+
defp map_empty?(bdd), do: map_bdd_empty?({:open, %{}}, [], bdd)
34113411

3412-
defp map_empty?(_, pos, []), do: Enum.any?(Map.to_list(pos), fn {_, v} -> empty?(v) end)
3413-
defp map_empty?(_, _, [{:open, neg_fields} | _]) when neg_fields == %{}, do: true
3414-
defp map_empty?(:open, fs, [{:closed, _} | negs]), do: map_empty?(:open, fs, negs)
3412+
defp map_bdd_empty?({tag, fields} = map, negs, bdd) do
3413+
case bdd do
3414+
:bdd_bot ->
3415+
true
3416+
3417+
:bdd_top ->
3418+
map_line_empty?(tag, fields, negs)
3419+
3420+
{{next_tag, next_fields} = next_map, left, right} ->
3421+
try do
3422+
new_map = map_literal_intersection(tag, fields, next_tag, next_fields)
3423+
map_bdd_empty?(new_map, negs, left) and map_bdd_empty?(map, [next_map | negs], right)
3424+
catch
3425+
:empty -> map_bdd_empty?(map, [next_map | negs], right)
3426+
end
3427+
end
3428+
end
3429+
3430+
defp map_line_empty?(_, pos, []), do: Enum.any?(Map.to_list(pos), fn {_, v} -> empty?(v) end)
3431+
defp map_line_empty?(_, _, [{:open, neg_fields} | _]) when neg_fields == %{}, do: true
3432+
defp map_line_empty?(:open, fs, [{:closed, _} | negs]), do: map_line_empty?(:open, fs, negs)
34153433

3416-
defp map_empty?(tag, fields, [{neg_tag, neg_fields} | negs]) do
3434+
defp map_line_empty?(tag, fields, [{neg_tag, neg_fields} | negs]) do
34173435
if map_check_domain_keys(tag, neg_tag) do
34183436
atom_default = map_key_tag_to_type(tag)
34193437
neg_atom_default = map_key_tag_to_type(neg_tag)
@@ -3432,18 +3450,18 @@ defmodule Module.Types.Descr do
34323450
# There may be value in common
34333451
tag == :open ->
34343452
diff = difference(term_or_optional(), neg_type)
3435-
empty?(diff) or map_empty?(tag, Map.put(fields, neg_key, diff), negs)
3453+
empty?(diff) or map_line_empty?(tag, Map.put(fields, neg_key, diff), negs)
34363454

34373455
true ->
34383456
diff = difference(atom_default, neg_type)
3439-
empty?(diff) or map_empty?(tag, Map.put(fields, neg_key, diff), negs)
3457+
empty?(diff) or map_line_empty?(tag, Map.put(fields, neg_key, diff), negs)
34403458
end
34413459
end) and
34423460
Enum.all?(Map.to_list(fields), fn {key, type} ->
34433461
case neg_fields do
34443462
%{^key => neg_type} ->
34453463
diff = difference(type, neg_type)
3446-
empty?(diff) or map_empty?(tag, Map.put(fields, key, diff), negs)
3464+
empty?(diff) or map_line_empty?(tag, Map.put(fields, key, diff), negs)
34473465

34483466
%{} ->
34493467
cond do
@@ -3456,12 +3474,12 @@ defmodule Module.Types.Descr do
34563474
true ->
34573475
# an absent key in a open negative map can be ignored
34583476
diff = difference(type, neg_atom_default)
3459-
empty?(diff) or map_empty?(tag, Map.put(fields, key, diff), negs)
3477+
empty?(diff) or map_line_empty?(tag, Map.put(fields, key, diff), negs)
34603478
end
34613479
end
3462-
end)) or map_empty?(tag, fields, negs)
3480+
end)) or map_line_empty?(tag, fields, negs)
34633481
else
3464-
map_empty?(tag, fields, negs)
3482+
map_line_empty?(tag, fields, negs)
34653483
end
34663484
end
34673485

0 commit comments

Comments
 (0)