Skip to content

Commit a5e83eb

Browse files
committed
Use separate strategy function for differences
1 parent dd1f529 commit a5e83eb

1 file changed

Lines changed: 125 additions & 34 deletions

File tree

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

Lines changed: 125 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2750,7 +2750,7 @@ defmodule Module.Types.Descr do
27502750

27512751
defp maybe_optimize_map_union(tag1, pos1, tag2, pos2)
27522752
when is_atom(tag1) and is_atom(tag2) do
2753-
case map_leaf_strategy(tag1, pos1, tag2, pos2, true) do
2753+
case map_union_strategy(pos1, pos2, tag1, tag2, :all_equal) do
27542754
:all_equal when tag1 == :open -> {tag1, pos1}
27552755
:all_equal -> {tag2, pos2}
27562756
{:one_key_difference, key, v1, v2} -> {tag1, fields_store(key, union(v1, v2), pos1)}
@@ -2762,10 +2762,7 @@ defmodule Module.Types.Descr do
27622762

27632763
defp maybe_optimize_map_union(_, _, _, _), do: nil
27642764

2765-
defp map_leaf_strategy(tag1, fields1, tag2, fields2, rhs?),
2766-
do: map_leaf_strategy(fields1, fields2, tag1, tag2, rhs?, :all_equal)
2767-
2768-
defp map_leaf_strategy([{k1, _} | t1], [{k2, _} | _] = l2, tag1, tag2, rhs?, status)
2765+
defp map_union_strategy([{k1, _} | t1], [{k2, _} | _] = l2, tag1, tag2, status)
27692766
when k1 < k2 do
27702767
# Left side has a key the right side does not have,
27712768
# left can only be a subtype if the right side is open.
@@ -2774,81 +2771,82 @@ defmodule Module.Types.Descr do
27742771
:none
27752772

27762773
:all_equal ->
2777-
map_leaf_strategy(t1, l2, tag1, tag2, rhs?, :left_subtype_of_right)
2774+
map_union_strategy(t1, l2, tag1, tag2, :left_subtype_of_right)
27782775

27792776
{:one_key_difference, _, p1, p2} ->
27802777
if subtype?(p1, p2),
2781-
do: map_leaf_strategy(t1, l2, tag1, tag2, rhs?, :left_subtype_of_right),
2778+
do: map_union_strategy(t1, l2, tag1, tag2, :left_subtype_of_right),
27822779
else: :none
27832780

27842781
:left_subtype_of_right ->
2785-
map_leaf_strategy(t1, l2, tag1, tag2, rhs?, :left_subtype_of_right)
2782+
map_union_strategy(t1, l2, tag1, tag2, :left_subtype_of_right)
27862783

27872784
_ ->
27882785
:none
27892786
end
27902787
end
27912788

2792-
defp map_leaf_strategy([{k1, _} | _] = l1, [{k2, _} | t2], tag1, tag2, rhs?, status)
2789+
defp map_union_strategy([{k1, _} | _] = l1, [{k2, _} | t2], tag1, tag2, status)
27932790
when k1 > k2 do
27942791
# Right side has a key the left side does not have,
27952792
# right can only be a subtype if the left side is open.
27962793
case status do
2797-
_ when tag1 != :open or not rhs? ->
2794+
_ when tag1 != :open ->
27982795
:none
27992796

28002797
:all_equal ->
2801-
map_leaf_strategy(l1, t2, tag1, tag2, rhs?, :right_subtype_of_left)
2798+
map_union_strategy(l1, t2, tag1, tag2, :right_subtype_of_left)
28022799

28032800
{:one_key_difference, _, p1, p2} ->
28042801
if subtype?(p2, p1),
2805-
do: map_leaf_strategy(l1, t2, tag1, tag2, rhs?, :right_subtype_of_left),
2802+
do: map_union_strategy(l1, t2, tag1, tag2, :right_subtype_of_left),
28062803
else: :none
28072804

28082805
:right_subtype_of_left ->
2809-
map_leaf_strategy(l1, t2, tag1, tag2, rhs?, :right_subtype_of_left)
2806+
map_union_strategy(l1, t2, tag1, tag2, :right_subtype_of_left)
28102807

28112808
_ ->
28122809
:none
28132810
end
28142811
end
28152812

2816-
defp map_leaf_strategy([{_, v} | t1], [{_, v} | t2], tag1, tag2, rhs?, status) do
2813+
defp map_union_strategy([{_, v} | t1], [{_, v} | t2], tag1, tag2, status) do
28172814
# Same key and same value, nothing changes
2818-
map_leaf_strategy(t1, t2, tag1, tag2, rhs?, status)
2815+
map_union_strategy(t1, t2, tag1, tag2, status)
28192816
end
28202817

2821-
defp map_leaf_strategy([{k1, v1} | t1], [{_, v2} | t2], tag1, tag2, rhs?, status) do
2818+
defp map_union_strategy([{k1, v1} | t1], [{_, v2} | t2], tag1, tag2, status) do
28222819
# They have the same key but different values
28232820
case status do
2824-
:all_equal when k1 != :__struct__ ->
2821+
:all_equal ->
28252822
cond do
2826-
tag1 == tag2 ->
2827-
map_leaf_strategy(t1, t2, tag1, tag2, rhs?, {:one_key_difference, k1, v1, v2})
2823+
# Don't do difference on struct keys
2824+
k1 != :__struct__ and tag1 == tag2 ->
2825+
map_union_strategy(t1, t2, tag1, tag2, {:one_key_difference, k1, v1, v2})
28282826

28292827
subtype?(v1, v2) ->
2830-
map_leaf_strategy(t1, t2, tag1, tag2, rhs?, :left_subtype_of_right)
2828+
map_union_strategy(t1, t2, tag1, tag2, :left_subtype_of_right)
28312829

2832-
rhs? and subtype?(v2, v1) ->
2833-
map_leaf_strategy(t1, t2, tag1, tag2, rhs?, :right_subtype_of_left)
2830+
subtype?(v2, v1) ->
2831+
map_union_strategy(t1, t2, tag1, tag2, :right_subtype_of_left)
28342832

28352833
true ->
28362834
:none
28372835
end
28382836

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

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

28452843
{:one_key_difference, _key, p1, p2} ->
28462844
cond do
28472845
subtype?(p1, p2) and subtype?(v1, v2) ->
2848-
map_leaf_strategy(t1, t2, tag1, tag2, rhs?, :left_subtype_of_right)
2846+
map_union_strategy(t1, t2, tag1, tag2, :left_subtype_of_right)
28492847

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

28532851
true ->
28542852
:none
@@ -2859,13 +2857,13 @@ defmodule Module.Types.Descr do
28592857
end
28602858
end
28612859

2862-
defp map_leaf_strategy([], [], _tag1, _tag2, _rhs?, status) do
2860+
defp map_union_strategy([], [], _tag1, _tag2, status) do
28632861
status
28642862
end
28652863

2866-
defp map_leaf_strategy(l1, l2, tag1, tag2, rhs?, status) do
2864+
defp map_union_strategy(l1, l2, tag1, tag2, status) do
28672865
lhs? = tag2 == :open and l2 == []
2868-
rhs? = rhs? and tag1 == :open and l1 == []
2866+
rhs? = tag1 == :open and l1 == []
28692867

28702868
case status do
28712869
:all_equal when lhs? ->
@@ -2971,10 +2969,13 @@ defmodule Module.Types.Descr do
29712969
end
29722970

29732971
_ when is_atom(tag) and is_atom(neg_tag) ->
2974-
case map_leaf_strategy(tag, fields, neg_tag, neg_fields, false) do
2972+
case map_difference_strategy(fields, neg_fields, tag, neg_tag, :all_equal) do
29752973
:all_equal when tag == neg_tag or neg_tag == :open ->
29762974
:bdd_bot
29772975

2976+
:disjoint ->
2977+
bdd_leaf(tag, fields)
2978+
29782979
{:one_key_difference, key, v1, v2} ->
29792980
t_diff = difference(fields_get(fields, key, v1), v2)
29802981

@@ -2988,7 +2989,7 @@ defmodule Module.Types.Descr do
29882989
:bdd_bot
29892990

29902991
_ ->
2991-
bdd_difference(map1, map2, &map_leaf_disjoint?/2)
2992+
bdd_difference(map1, map2)
29922993
end
29932994

29942995
_ ->
@@ -4360,14 +4361,14 @@ defmodule Module.Types.Descr do
43604361
if empty_intersection? do
43614362
{acc_fields, acc_negs}
43624363
else
4363-
case map_leaf_strategy(tag, acc_fields, neg_tag, neg_fields, false) do
4364+
case map_difference_strategy(acc_fields, neg_fields, tag, neg_tag, :all_equal) do
43644365
:all_equal when tag == neg_tag or neg_tag == :open ->
43654366
{acc_fields, acc_negs}
43664367

43674368
{:one_key_difference, key, v1, v2} ->
43684369
{fields_store(key, difference(v1, v2), acc_fields), acc_negs}
43694370

4370-
:left_subtype_of_right ->
4371+
:disjoint ->
43714372
{acc_fields, acc_negs}
43724373

43734374
_ ->
@@ -4377,6 +4378,96 @@ defmodule Module.Types.Descr do
43774378
end)
43784379
end
43794380

4381+
defp map_difference_strategy([{k1, _} | t1], [{k2, _} | _] = l2, tag1, tag2, status)
4382+
when k1 < k2 do
4383+
# Left side has a key the right side does not have,
4384+
# left can only be a subtype if the right side is open.
4385+
case status do
4386+
_ when tag2 != :open ->
4387+
:none
4388+
4389+
:all_equal ->
4390+
map_difference_strategy(t1, l2, tag1, tag2, :left_subtype_of_right)
4391+
4392+
{:one_key_difference, _, p1, p2} ->
4393+
if subtype?(p1, p2),
4394+
do: map_difference_strategy(t1, l2, tag1, tag2, :left_subtype_of_right),
4395+
else: :none
4396+
4397+
:left_subtype_of_right ->
4398+
map_difference_strategy(t1, l2, tag1, tag2, :left_subtype_of_right)
4399+
4400+
_ ->
4401+
:none
4402+
end
4403+
end
4404+
4405+
defp map_difference_strategy([{k1, _} | _], [{k2, _} | _], tag1, _tag2, _status)
4406+
when k1 > k2 do
4407+
# Right side has a key the left side does not have,
4408+
# if left-side is closed, they are disjoint.
4409+
if tag1 == :closed, do: :disjoint, else: :none
4410+
end
4411+
4412+
defp map_difference_strategy([{_, v} | t1], [{_, v} | t2], tag1, tag2, status) do
4413+
# Same key and same value, nothing changes
4414+
map_difference_strategy(t1, t2, tag1, tag2, status)
4415+
end
4416+
4417+
defp map_difference_strategy([{k1, v1} | t1], [{_, v2} | t2], tag1, tag2, status) do
4418+
# They have the same key but different values
4419+
if disjoint?(v1, v2) do
4420+
:disjoint
4421+
else
4422+
case status do
4423+
:all_equal when tag1 == tag2 ->
4424+
map_difference_strategy(t1, t2, tag1, tag2, {:one_key_difference, k1, v1, v2})
4425+
4426+
{:one_key_difference, _key, p1, p2} ->
4427+
if subtype?(p1, p2) and subtype?(v1, v2) do
4428+
map_difference_strategy(t1, t2, tag1, tag2, :left_subtype_of_right)
4429+
else
4430+
:none
4431+
end
4432+
4433+
# all_equal or left_subtype_of_right
4434+
_ ->
4435+
if subtype?(v1, v2),
4436+
do: map_difference_strategy(t1, t2, tag1, tag2, :left_subtype_of_right),
4437+
else: :none
4438+
end
4439+
end
4440+
end
4441+
4442+
defp map_difference_strategy([], [], _tag1, _tag2, status) do
4443+
status
4444+
end
4445+
4446+
defp map_difference_strategy(_l1, l2, tag1, tag2, status) do
4447+
cond do
4448+
tag1 == :closed and l2 != [] ->
4449+
:disjoint
4450+
4451+
tag2 == :open and l2 == [] ->
4452+
case status do
4453+
:all_equal ->
4454+
:left_subtype_of_right
4455+
4456+
{:one_key_difference, _, p1, p2} ->
4457+
if subtype?(p1, p2), do: :left_subtype_of_right, else: :none
4458+
4459+
:left_subtype_of_right ->
4460+
:left_subtype_of_right
4461+
4462+
_ ->
4463+
:none
4464+
end
4465+
4466+
true ->
4467+
:none
4468+
end
4469+
end
4470+
43804471
# Given a dnf, fuse maps when possible
43814472
# e.g. %{a: integer(), b: atom()} or %{a: float(), b: atom()} into %{a: number(), b: atom()}
43824473
defp map_fusion(dnf) do

0 commit comments

Comments
 (0)