Skip to content

Commit 09c90ee

Browse files
committed
Reuse map_leaf_strategy on difference
1 parent bbe7001 commit 09c90ee

1 file changed

Lines changed: 87 additions & 89 deletions

File tree

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

Lines changed: 87 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -348,9 +348,6 @@ defmodule Module.Types.Descr do
348348
end
349349
end
350350

351-
defp optional_static?(%{optional: 1}), do: true
352-
defp optional_static?(_term_or_descr), do: false
353-
354351
defp pop_optional_static(:term), do: {false, :term}
355352

356353
defp pop_optional_static(type) do
@@ -2737,40 +2734,38 @@ defmodule Module.Types.Descr do
27372734
end
27382735

27392736
defp map_union(bdd_leaf(tag1, fields1), bdd_leaf(tag2, fields2)) do
2740-
case maybe_optimize_map_union({tag1, fields1, []}, {tag2, fields2, []}) do
2741-
{tag, fields, []} -> bdd_leaf(tag, fields)
2737+
case maybe_optimize_map_union(tag1, fields1, tag2, fields2) do
2738+
{tag, fields} -> bdd_leaf(tag, fields)
27422739
nil -> bdd_union(bdd_leaf(tag1, fields1), bdd_leaf(tag2, fields2))
27432740
end
27442741
end
27452742

2746-
@compile {:inline, map_union: 2}
27472743
defp map_union(bdd1, bdd2), do: bdd_union(bdd1, bdd2)
27482744

2749-
defp maybe_optimize_map_union({tag1, pos1, []} = map1, {tag2, pos2, []} = map2)
2745+
defp maybe_optimize_map_union(:open, empty, _, _) when is_fields_empty(empty),
2746+
do: {:open, @fields_new}
2747+
2748+
defp maybe_optimize_map_union(_, _, :open, empty) when is_fields_empty(empty),
2749+
do: {:open, @fields_new}
2750+
2751+
defp maybe_optimize_map_union(tag1, pos1, tag2, pos2)
27502752
when is_atom(tag1) and is_atom(tag2) do
2751-
case map_union_strategy(tag1, pos1, tag2, pos2) do
2752-
:all_equal when tag1 == :open -> map1
2753-
:all_equal -> map2
2754-
:any_map -> {:open, @fields_new, []}
2755-
{:one_key_difference, key, v1, v2} -> {tag1, fields_store(key, union(v1, v2), pos1), []}
2756-
:left_subtype_of_right -> map2
2757-
:right_subtype_of_left -> map1
2753+
case map_leaf_strategy(tag1, pos1, tag2, pos2, true) do
2754+
:all_equal when tag1 == :open -> {tag1, pos1}
2755+
:all_equal -> {tag2, pos2}
2756+
{:one_key_difference, key, v1, v2} -> {tag1, fields_store(key, union(v1, v2), pos1)}
2757+
:left_subtype_of_right -> {tag2, pos2}
2758+
:right_subtype_of_left -> {tag1, pos1}
27582759
:none -> nil
27592760
end
27602761
end
27612762

2762-
defp maybe_optimize_map_union(_, _), do: nil
2763-
2764-
defp map_union_strategy(:open, empty, _, _) when is_fields_empty(empty),
2765-
do: :any_map
2763+
defp maybe_optimize_map_union(_, _, _, _), do: nil
27662764

2767-
defp map_union_strategy(_, _, :open, empty) when is_fields_empty(empty),
2768-
do: :any_map
2765+
defp map_leaf_strategy(tag1, fields1, tag2, fields2, rhs?),
2766+
do: map_leaf_strategy(fields1, fields2, tag1, tag2, rhs?, :all_equal)
27692767

2770-
defp map_union_strategy(tag1, fields1, tag2, fields2),
2771-
do: map_union_strategy(fields1, fields2, tag1, tag2, :all_equal)
2772-
2773-
defp map_union_strategy([{k1, _} | t1], [{k2, _} | _] = l2, tag1, tag2, status)
2768+
defp map_leaf_strategy([{k1, _} | t1], [{k2, _} | _] = l2, tag1, tag2, rhs?, status)
27742769
when k1 < k2 do
27752770
# Left side has a key the right side does not have,
27762771
# left can only be a subtype if the right side is open.
@@ -2779,81 +2774,81 @@ defmodule Module.Types.Descr do
27792774
:none
27802775

27812776
:all_equal ->
2782-
map_union_strategy(t1, l2, tag1, tag2, :left_subtype_of_right)
2777+
map_leaf_strategy(t1, l2, tag1, tag2, rhs?, :left_subtype_of_right)
27832778

27842779
{:one_key_difference, _, p1, p2} ->
27852780
if subtype?(p1, p2),
2786-
do: map_union_strategy(t1, l2, tag1, tag2, :left_subtype_of_right),
2781+
do: map_leaf_strategy(t1, l2, tag1, tag2, rhs?, :left_subtype_of_right),
27872782
else: :none
27882783

27892784
:left_subtype_of_right ->
2790-
map_union_strategy(t1, l2, tag1, tag2, :left_subtype_of_right)
2785+
map_leaf_strategy(t1, l2, tag1, tag2, rhs?, :left_subtype_of_right)
27912786

27922787
_ ->
27932788
:none
27942789
end
27952790
end
27962791

2797-
defp map_union_strategy([{k1, _} | _] = l1, [{k2, _} | t2], tag1, tag2, status)
2792+
defp map_leaf_strategy([{k1, _} | _] = l1, [{k2, _} | t2], tag1, tag2, rhs?, status)
27982793
when k1 > k2 do
27992794
# Right side has a key the left side does not have,
28002795
# right can only be a subtype if the left side is open.
28012796
case status do
2802-
_ when tag1 != :open ->
2797+
_ when tag1 != :open or not rhs? ->
28032798
:none
28042799

28052800
:all_equal ->
2806-
map_union_strategy(l1, t2, tag1, tag2, :right_subtype_of_left)
2801+
map_leaf_strategy(l1, t2, tag1, tag2, rhs?, :right_subtype_of_left)
28072802

28082803
{:one_key_difference, _, p1, p2} ->
28092804
if subtype?(p2, p1),
2810-
do: map_union_strategy(l1, t2, tag1, tag2, :right_subtype_of_left),
2805+
do: map_leaf_strategy(l1, t2, tag1, tag2, rhs?, :right_subtype_of_left),
28112806
else: :none
28122807

28132808
:right_subtype_of_left ->
2814-
map_union_strategy(l1, t2, tag1, tag2, :right_subtype_of_left)
2809+
map_leaf_strategy(l1, t2, tag1, tag2, rhs?, :right_subtype_of_left)
28152810

28162811
_ ->
28172812
:none
28182813
end
28192814
end
28202815

2821-
defp map_union_strategy([{_, v} | t1], [{_, v} | t2], tag1, tag2, status) do
2816+
defp map_leaf_strategy([{_, v} | t1], [{_, v} | t2], tag1, tag2, rhs?, status) do
28222817
# Same key and same value, nothing changes
2823-
map_union_strategy(t1, t2, tag1, tag2, status)
2818+
map_leaf_strategy(t1, t2, tag1, tag2, rhs?, status)
28242819
end
28252820

2826-
defp map_union_strategy([{k1, v1} | t1], [{_, v2} | t2], tag1, tag2, status) do
2821+
defp map_leaf_strategy([{k1, v1} | t1], [{_, v2} | t2], tag1, tag2, rhs?, status) do
28272822
# They have the same key but different values
28282823
case status do
28292824
:all_equal when k1 != :__struct__ ->
28302825
cond do
28312826
tag1 == tag2 ->
2832-
map_union_strategy(t1, t2, tag1, tag2, {:one_key_difference, k1, v1, v2})
2827+
map_leaf_strategy(t1, t2, tag1, tag2, rhs?, {:one_key_difference, k1, v1, v2})
28332828

28342829
subtype?(v1, v2) ->
2835-
map_union_strategy(t1, t2, tag1, tag2, :left_subtype_of_right)
2830+
map_leaf_strategy(t1, t2, tag1, tag2, rhs?, :left_subtype_of_right)
28362831

2837-
subtype?(v2, v1) ->
2838-
map_union_strategy(t1, t2, tag1, tag2, :right_subtype_of_left)
2832+
rhs? and subtype?(v2, v1) ->
2833+
map_leaf_strategy(t1, t2, tag1, tag2, rhs?, :right_subtype_of_left)
28392834

28402835
true ->
28412836
:none
28422837
end
28432838

28442839
:left_subtype_of_right ->
2845-
if subtype?(v1, v2), do: map_union_strategy(t1, t2, tag1, tag2, status), else: :none
2840+
if subtype?(v1, v2), do: map_leaf_strategy(t1, t2, tag1, tag2, rhs?, status), else: :none
28462841

28472842
:right_subtype_of_left ->
2848-
if subtype?(v2, v1), do: map_union_strategy(t1, t2, tag1, tag2, status), else: :none
2843+
if subtype?(v2, v1), do: map_leaf_strategy(t1, t2, tag1, tag2, rhs?, status), else: :none
28492844

28502845
{:one_key_difference, _key, p1, p2} ->
28512846
cond do
28522847
subtype?(p1, p2) and subtype?(v1, v2) ->
2853-
map_union_strategy(t1, t2, tag1, tag2, :left_subtype_of_right)
2848+
map_leaf_strategy(t1, t2, tag1, tag2, rhs?, :left_subtype_of_right)
28542849

2855-
subtype?(p2, p1) and subtype?(v2, v1) ->
2856-
map_union_strategy(t1, t2, tag1, tag2, :right_subtype_of_left)
2850+
rhs? and subtype?(p2, p1) and subtype?(v2, v1) ->
2851+
map_leaf_strategy(t1, t2, tag1, tag2, rhs?, :right_subtype_of_left)
28572852

28582853
true ->
28592854
:none
@@ -2864,29 +2859,32 @@ defmodule Module.Types.Descr do
28642859
end
28652860
end
28662861

2867-
defp map_union_strategy([], [], _tag1, _tag2, status) do
2862+
defp map_leaf_strategy([], [], _tag1, _tag2, _rhs?, status) do
28682863
status
28692864
end
28702865

2871-
defp map_union_strategy(l1, l2, tag1, tag2, status) do
2866+
defp map_leaf_strategy(l1, l2, tag1, tag2, rhs?, status) do
2867+
lhs? = tag2 == :open and l2 == []
2868+
rhs? = rhs? and tag1 == :open and l1 == []
2869+
28722870
case status do
2873-
:all_equal when tag2 == :open and l2 == [] ->
2871+
:all_equal when lhs? ->
28742872
:left_subtype_of_right
28752873

2876-
:all_equal when tag1 == :open and l1 == [] ->
2874+
:all_equal when rhs? ->
28772875
:right_subtype_of_left
28782876

28792877
{:one_key_difference, _, p1, p2} ->
28802878
cond do
2881-
tag2 == :open and l2 == [] and subtype?(p1, p2) -> :left_subtype_of_right
2882-
tag1 == :open and l1 == [] and subtype?(p2, p1) -> :right_subtype_of_left
2879+
lhs? and subtype?(p1, p2) -> :left_subtype_of_right
2880+
rhs? and subtype?(p2, p1) -> :right_subtype_of_left
28832881
true -> :none
28842882
end
28852883

2886-
:left_subtype_of_right when tag2 == :open and l2 == [] ->
2884+
:left_subtype_of_right when lhs? ->
28872885
:left_subtype_of_right
28882886

2889-
:right_subtype_of_left when tag1 == :open and l1 == [] ->
2887+
:right_subtype_of_left when rhs? ->
28902888
:right_subtype_of_left
28912889

28922890
_ ->
@@ -2960,7 +2958,7 @@ defmodule Module.Types.Descr do
29602958
:error -> {false, map_key_tag_to_type(tag)}
29612959
end
29622960

2963-
if tag == :closed and not found? and not optional_static?(value) do
2961+
if tag == :closed and not found? and not is_optional_static(value) do
29642962
map1
29652963
else
29662964
t_diff = difference(fields_get(fields, key, pos_value), value)
@@ -2972,15 +2970,29 @@ defmodule Module.Types.Descr do
29722970
end
29732971
end
29742972

2975-
_ ->
2976-
# Case 2: the maps have all but one key in common. Do the difference of that key.
2977-
case map_all_but_one(tag, fields, neg_tag, neg_fields) do
2978-
{diff_key, type1, type2} ->
2979-
bdd_leaf(tag, fields_store(diff_key, difference(type1, type2), fields))
2973+
_ when is_atom(tag) and is_atom(neg_tag) ->
2974+
case map_leaf_strategy(tag, fields, neg_tag, neg_fields, false) do
2975+
:all_equal when tag == neg_tag or neg_tag == :open ->
2976+
:bdd_bot
2977+
2978+
{:one_key_difference, key, v1, v2} ->
2979+
t_diff = difference(fields_get(fields, key, v1), v2)
2980+
2981+
if empty?(t_diff) do
2982+
:bdd_bot
2983+
else
2984+
bdd_leaf(tag, fields_store(key, t_diff, fields))
2985+
end
2986+
2987+
:left_subtype_of_right ->
2988+
:bdd_bot
29802989

29812990
_ ->
29822991
bdd_difference(map1, map2, &map_leaf_disjoint?/2)
29832992
end
2993+
2994+
_ ->
2995+
bdd_difference(map1, map2, &map_leaf_disjoint?/2)
29842996
end
29852997
end
29862998

@@ -3012,9 +3024,7 @@ defmodule Module.Types.Descr do
30123024
defp map_literal_intersection(:open, map1, :open, map2) do
30133025
new_fields =
30143026
fields_merge(
3015-
fn _, type1, type2 ->
3016-
non_empty_intersection!(type1, type2)
3017-
end,
3027+
fn _, type1, type2 -> non_empty_intersection!(type1, type2) end,
30183028
map1,
30193029
map2
30203030
)
@@ -3326,9 +3336,9 @@ defmodule Module.Types.Descr do
33263336

33273337
defp has_empty_map?(dnf) do
33283338
Enum.any?(dnf, fn {_, fields, negs} ->
3329-
Enum.all?(fields_to_list(fields), fn {_key, value} -> optional_static?(value) end) and
3339+
Enum.all?(fields_to_list(fields), fn {_key, value} -> is_optional_static(value) end) and
33303340
Enum.all?(negs, fn {_, fields} ->
3331-
not Enum.all?(fields_to_list(fields), fn {_key, value} -> optional_static?(value) end)
3341+
not Enum.all?(fields_to_list(fields), fn {_key, value} -> is_optional_static(value) end)
33323342
end)
33333343
end)
33343344
end
@@ -4313,9 +4323,15 @@ defmodule Module.Types.Descr do
43134323
if empty_intersection? do
43144324
{acc_fields, acc_negs}
43154325
else
4316-
case map_all_but_one(tag, acc_fields, neg_tag, neg_fields) do
4317-
{diff_key, type1, type2} ->
4318-
{fields_store(diff_key, difference(type1, type2), acc_fields), acc_negs}
4326+
case map_leaf_strategy(tag, acc_fields, neg_tag, neg_fields, false) do
4327+
:all_equal when tag == neg_tag or neg_tag == :open ->
4328+
{acc_fields, acc_negs}
4329+
4330+
{:one_key_difference, key, v1, v2} ->
4331+
{fields_store(key, difference(v1, v2), acc_fields), acc_negs}
4332+
4333+
:left_subtype_of_right ->
4334+
{acc_fields, acc_negs}
43194335

43204336
_ ->
43214337
{acc_fields, [neg | acc_negs]}
@@ -4350,34 +4366,16 @@ defmodule Module.Types.Descr do
43504366
defp map_fuse_with_first_fusible(map, []), do: [map]
43514367

43524368
defp map_fuse_with_first_fusible(map, [candidate | rest]) do
4353-
case maybe_optimize_map_union(map, candidate) do
4369+
{tag1, fields1, []} = map
4370+
{tag2, fields2, []} = candidate
4371+
4372+
case maybe_optimize_map_union(tag1, fields1, tag2, fields2) do
43544373
nil -> [candidate | map_fuse_with_first_fusible(map, rest)]
43554374
# we found a fusible candidate, we're done
4356-
fused -> [fused | rest]
4357-
end
4358-
end
4359-
4360-
# If all fields are the same except one, we can optimize map difference.
4361-
defp map_all_but_one(tag1, fields1, tag2, fields2) do
4362-
with true <- {tag1, tag2} != {:open, :closed},
4363-
[triplet] <- map_all_but_one(fields1, fields2, []) do
4364-
triplet
4365-
else
4366-
_ -> :error
4375+
{tag, fields} -> [{tag, fields, []} | rest]
43674376
end
43684377
end
43694378

4370-
defp map_all_but_one([{k, v1} | t1], [{k, v2} | t2], found) do
4371-
cond do
4372-
v1 == v2 -> map_all_but_one(t1, t2, found)
4373-
found == [] -> map_all_but_one(t1, t2, [{k, v1, v2}])
4374-
true -> []
4375-
end
4376-
end
4377-
4378-
defp map_all_but_one([], [], found), do: found
4379-
defp map_all_but_one(_, _, _found), do: []
4380-
43814379
defp map_to_quoted(bdd, opts) do
43824380
bdd
43834381
|> map_bdd_to_dnf()

0 commit comments

Comments
 (0)