Skip to content

Commit 26318eb

Browse files
authored
Improve perf of tuple_difference (#13988)
1 parent 2c3a906 commit 26318eb

File tree

2 files changed

+181
-18
lines changed

2 files changed

+181
-18
lines changed

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

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1792,10 +1792,8 @@ defmodule Module.Types.Descr do
17921792
{tag2, elements2, negs2} <- dnf2,
17931793
reduce: [] do
17941794
acc ->
1795-
try do
1796-
{tag, fields} = tuple_literal_intersection(tag1, elements1, tag2, elements2)
1797-
[{tag, fields, negs1 ++ negs2} | acc]
1798-
catch
1795+
case tuple_literal_intersection(tag1, elements1, tag2, elements2) do
1796+
{tag, fields} -> [{tag, fields, negs1 ++ negs2} | acc]
17991797
:empty -> acc
18001798
end
18011799
end
@@ -1811,13 +1809,21 @@ defmodule Module.Types.Descr do
18111809

18121810
cond do
18131811
(tag1 == :closed and n < m) or (tag2 == :closed and n > m) ->
1814-
throw(:empty)
1812+
:empty
18151813

18161814
tag1 == :open and tag2 == :open ->
1817-
{:open, zip_non_empty_intersection!(elements1, elements2, [])}
1815+
try do
1816+
{:open, zip_non_empty_intersection!(elements1, elements2, [])}
1817+
catch
1818+
:empty -> :empty
1819+
end
18181820

18191821
true ->
1820-
{:closed, zip_non_empty_intersection!(elements1, elements2, [])}
1822+
try do
1823+
{:closed, zip_non_empty_intersection!(elements1, elements2, [])}
1824+
catch
1825+
:empty -> :empty
1826+
end
18211827
end
18221828
end
18231829

@@ -1832,14 +1838,17 @@ defmodule Module.Types.Descr do
18321838
defp tuple_difference(dnf1, dnf2) do
18331839
Enum.reduce(dnf2, dnf1, fn {tag2, elements2, negs2}, dnf1 ->
18341840
Enum.reduce(dnf1, [], fn {tag1, elements1, negs1}, acc ->
1835-
acc = [{tag1, elements1, [{tag2, elements2} | negs1]}] ++ acc
1836-
1837-
Enum.reduce(negs2, acc, fn {neg_tag2, neg_elements2}, acc ->
1838-
try do
1839-
{tag, fields} = tuple_literal_intersection(tag1, elements1, neg_tag2, neg_elements2)
1840-
[{tag, fields, negs1}] ++ acc
1841-
catch
1842-
:empty -> acc
1841+
# Prune negations that have no values in common
1842+
acc =
1843+
case tuple_literal_intersection(tag1, elements1, tag2, elements2) do
1844+
:empty -> [{tag1, elements1, negs1}] ++ acc
1845+
_ -> [{tag1, elements1, [{tag2, elements2} | negs1]}] ++ acc
1846+
end
1847+
1848+
Enum.reduce(negs2, acc, fn {neg_tag2, neg_elements2}, inner_acc ->
1849+
case tuple_literal_intersection(tag1, elements1, neg_tag2, neg_elements2) do
1850+
:empty -> inner_acc
1851+
{tag, fields} -> [{tag, fields, negs1} | inner_acc]
18431852
end
18441853
end)
18451854
end)
@@ -1850,7 +1859,8 @@ defmodule Module.Types.Descr do
18501859
end
18511860
end
18521861

1853-
defp tuple_union(left, right), do: left ++ right
1862+
# Removes duplicates in union, which should trickle to other operations.
1863+
defp tuple_union(left, right), do: left ++ (right -- left)
18541864

18551865
defp tuple_to_quoted(dnf) do
18561866
dnf
@@ -1902,7 +1912,7 @@ defmodule Module.Types.Descr do
19021912
Enum.all?(dnf, fn {tag, pos, negs} -> tuple_empty?(tag, pos, negs) end)
19031913
end
19041914

1905-
# No negations, so not empty
1915+
# No negations, so not empty unless there's an empty type
19061916
defp tuple_empty?(_, pos, []), do: Enum.any?(pos, &empty?/1)
19071917
# Open empty negation makes it empty
19081918
defp tuple_empty?(_, _, [{:open, []} | _]), do: true

lib/elixir/test/elixir/module/types/descr_test.exs

Lines changed: 154 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,159 @@ defmodule Module.Types.DescrTest do
334334

335335
assert difference(tuple(), open_tuple([term(), term()]))
336336
|> equal?(union(tuple([term()]), tuple([])))
337+
338+
# Large difference with no duplicates
339+
descr1 = %{
340+
dynamic: %{
341+
atom: {:union, %{reset: [], ignored: []}},
342+
tuple: [
343+
{:closed, [%{atom: {:union, %{font_style: []}}}, %{atom: {:union, %{italic: []}}}],
344+
[]}
345+
]
346+
}
347+
}
348+
349+
descr2 = %{
350+
dynamic: %{
351+
atom: {:union, %{reset: [], ignored: []}},
352+
tuple: [
353+
{:closed, [%{atom: {:union, %{font_weight: []}}}, %{atom: {:union, %{bold: []}}}],
354+
[]},
355+
{:closed, [%{atom: {:union, %{font_style: []}}}, %{atom: {:union, %{italic: []}}}],
356+
[]},
357+
{:closed, [%{atom: {:union, %{font_weight: []}}}, %{atom: {:union, %{clear: []}}}],
358+
[]},
359+
{:closed,
360+
[
361+
%{atom: {:union, %{text_decoration: []}}},
362+
%{atom: {:union, %{clear_decoration: []}}}
363+
], []},
364+
{:closed,
365+
[
366+
%{atom: {:union, %{text_decoration: []}}},
367+
%{atom: {:union, %{remove_decoration: []}}}
368+
], []},
369+
{:closed,
370+
[
371+
%{atom: {:union, %{text_decoration: []}}},
372+
%{atom: {:union, %{undo_decoration: []}}}
373+
], []},
374+
{:closed,
375+
[
376+
%{atom: {:union, %{text_decoration: []}}},
377+
%{atom: {:union, %{default_decoration: []}}}
378+
], []},
379+
{:closed,
380+
[
381+
%{atom: {:union, %{foreground_color: []}}},
382+
%{atom: {:union, %{clear_foreground: []}}}
383+
], []},
384+
{:closed,
385+
[
386+
%{atom: {:union, %{foreground_color: []}}},
387+
%{atom: {:union, %{remove_foreground: []}}}
388+
], []},
389+
{:closed,
390+
[
391+
%{atom: {:union, %{foreground_color: []}}},
392+
%{atom: {:union, %{undo_foreground: []}}}
393+
], []},
394+
{:closed,
395+
[
396+
%{atom: {:union, %{foreground_color: []}}},
397+
%{atom: {:union, %{default_foreground: []}}}
398+
], []},
399+
{:closed,
400+
[
401+
%{atom: {:union, %{background_color: []}}},
402+
%{atom: {:union, %{clear_background: []}}}
403+
], []},
404+
{:closed,
405+
[
406+
%{atom: {:union, %{background_color: []}}},
407+
%{atom: {:union, %{remove_background: []}}}
408+
], []},
409+
{:closed,
410+
[
411+
%{atom: {:union, %{background_color: []}}},
412+
%{atom: {:union, %{undo_background: []}}}
413+
], []},
414+
{:closed,
415+
[
416+
%{atom: {:union, %{background_color: []}}},
417+
%{atom: {:union, %{default_background: []}}}
418+
], []},
419+
{:closed,
420+
[
421+
%{atom: {:union, %{text_decoration: []}}},
422+
%{atom: {:union, %{overline: []}}}
423+
], []},
424+
{:closed,
425+
[
426+
%{atom: {:union, %{text_decoration: []}}},
427+
%{atom: {:union, %{clear_text: []}}}
428+
], []},
429+
{:closed,
430+
[
431+
%{atom: {:union, %{text_decoration: []}}},
432+
%{atom: {:union, %{remove_text: []}}}
433+
], []},
434+
{:closed,
435+
[
436+
%{atom: {:union, %{text_decoration: []}}},
437+
%{atom: {:union, %{undo_text: []}}}
438+
], []},
439+
{:closed,
440+
[
441+
%{atom: {:union, %{text_decoration: []}}},
442+
%{atom: {:union, %{default_text: []}}}
443+
], []},
444+
{:closed,
445+
[
446+
%{atom: {:union, %{foreground_color: []}}},
447+
%{atom: {:union, %{clear_fg: []}}}
448+
], []},
449+
{:closed,
450+
[
451+
%{atom: {:union, %{foreground_color: []}}},
452+
%{atom: {:union, %{remove_fg: []}}}
453+
], []},
454+
{:closed,
455+
[
456+
%{atom: {:union, %{foreground_color: []}}},
457+
%{atom: {:union, %{undo_fg: []}}}
458+
], []},
459+
{:closed,
460+
[
461+
%{atom: {:union, %{foreground_color: []}}},
462+
%{atom: {:union, %{default_fg: []}}}
463+
], []},
464+
{:closed,
465+
[
466+
%{atom: {:union, %{background_color: []}}},
467+
%{atom: {:union, %{clear_bg: []}}}
468+
], []},
469+
{:closed,
470+
[
471+
%{atom: {:union, %{background_color: []}}},
472+
%{atom: {:union, %{remove_bg: []}}}
473+
], []},
474+
{:closed,
475+
[
476+
%{atom: {:union, %{background_color: []}}},
477+
%{atom: {:union, %{undo_bg: []}}}
478+
], []},
479+
{:closed,
480+
[
481+
%{atom: {:union, %{background_color: []}}},
482+
%{atom: {:union, %{default_bg: []}}}
483+
], []}
484+
]
485+
}
486+
}
487+
488+
assert subtype?(descr1, descr2)
489+
refute subtype?(descr2, descr1)
337490
end
338491

339492
test "map" do
@@ -1243,7 +1396,7 @@ defmodule Module.Types.DescrTest do
12431396
"{integer(), atom()} or {atom(), ...}"
12441397

12451398
assert difference(tuple([integer(), atom()]), open_tuple([atom()])) |> to_quoted_string() ==
1246-
"{integer(), atom()} and not {atom(), ...}"
1399+
"{integer(), atom()}"
12471400

12481401
assert tuple([closed_map(a: integer()), open_map()]) |> to_quoted_string() ==
12491402
"{%{a: integer()}, %{...}}"

0 commit comments

Comments
 (0)