Skip to content

Commit a9a0969

Browse files
committed
Also optimize different between structs
1 parent dd284bb commit a9a0969

2 files changed

Lines changed: 47 additions & 7 deletions

File tree

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

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2754,13 +2754,29 @@ defmodule Module.Types.Descr do
27542754
bdd_leaf(tag, Map.replace!(fields, diff_key, difference(type1, type2)))
27552755

27562756
_ ->
2757-
bdd_difference(map1, map2, &map_leaf_intersection/2, nil)
2757+
bdd_difference(map1, map2, &map_leaf_intersection/2, &map_leaf_disjoint?/2)
27582758
end
27592759
end
27602760
end
27612761

27622762
defp map_difference(bdd1, bdd2),
2763-
do: bdd_difference(bdd1, bdd2, &map_leaf_intersection/2, nil)
2763+
do: bdd_difference(bdd1, bdd2, &map_leaf_intersection/2, &map_leaf_disjoint?/2)
2764+
2765+
defp map_leaf_disjoint?(bdd_leaf(_tag1, fields1), bdd_leaf(_tag2, fields2)) do
2766+
disjoint_structs?(fields1, fields2)
2767+
end
2768+
2769+
defp disjoint_structs?(%{__struct__: %{atom: atom} = d1}, %{__struct__: d2})
2770+
when map_size(d1) == 1 do
2771+
disjoint_atom_descr?(atom, d2)
2772+
end
2773+
2774+
defp disjoint_structs?(%{__struct__: d1}, %{__struct__: %{atom: atom} = d2})
2775+
when map_size(d2) == 1 do
2776+
disjoint_atom_descr?(atom, d1)
2777+
end
2778+
2779+
defp disjoint_structs?(_, _), do: false
27642780

27652781
# Intersects two map literals; throws if their intersection is empty.
27662782
# Both open: the result is open.
@@ -4301,16 +4317,16 @@ defmodule Module.Types.Descr do
43014317

43024318
# A very cheap check for tagged tuples
43034319
defp disjoint_tagged_tuples?([%{atom: atom} = d1 | _], [d2 | _]) when map_size(d1) == 1,
4304-
do: disjoint_tagged_atom?(atom, d2)
4320+
do: disjoint_atom_descr?(atom, d2)
43054321

43064322
defp disjoint_tagged_tuples?([d1 | _], [%{atom: atom} = d2 | _]) when map_size(d2) == 1,
4307-
do: disjoint_tagged_atom?(atom, d1)
4323+
do: disjoint_atom_descr?(atom, d1)
43084324

43094325
defp disjoint_tagged_tuples?(_, _), do: false
43104326

4311-
defp disjoint_tagged_atom?(_atom, :term), do: false
4312-
defp disjoint_tagged_atom?(atom1, %{atom: atom2}), do: atom_intersection(atom1, atom2) === 0
4313-
defp disjoint_tagged_atom?(_atom, %{}), do: true
4327+
defp disjoint_atom_descr?(_atom, :term), do: false
4328+
defp disjoint_atom_descr?(atom1, %{atom: atom2}), do: atom_intersection(atom1, atom2) === 0
4329+
defp disjoint_atom_descr?(_atom, %{}), do: true
43144330

43154331
defp non_empty_tuple_literals_intersection(tuples) do
43164332
try do

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,30 @@ defmodule Module.Types.DescrTest do
568568
|> equal?(open_map(a: integer()))
569569
end
570570

571+
test "map (struct optimizations)" do
572+
# We do direct assertions because we want to check how it works underneath
573+
atom_foo = atom([:foo])
574+
atom_bar = atom([:bar])
575+
576+
assert difference(open_map(__struct__: atom_foo), open_map(__struct__: atom_bar)) ==
577+
open_map(__struct__: atom_foo)
578+
579+
assert difference(closed_map(__struct__: atom_foo), open_map(__struct__: atom_bar)) ==
580+
closed_map(__struct__: atom_foo)
581+
582+
assert difference(open_map(__struct__: atom_foo), closed_map(__struct__: atom_bar)) ==
583+
open_map(__struct__: atom_foo)
584+
585+
assert difference(closed_map(__struct__: atom_foo), closed_map(__struct__: atom_bar)) ==
586+
closed_map(__struct__: atom_foo)
587+
588+
refute difference(closed_map(__struct__: atom_foo), closed_map(__struct__: term())) ==
589+
closed_map(__struct__: atom_foo)
590+
591+
refute difference(closed_map(__struct__: atom()), closed_map(__struct__: atom_bar)) ==
592+
closed_map(__struct__: atom())
593+
end
594+
571595
test "map with domain keys" do
572596
# Non-overlapping domain keys
573597
t1 = closed_map([{domain_key(:integer), atom()}])

0 commit comments

Comments
 (0)