Skip to content

Commit f0f42eb

Browse files
committed
Refactor tuple insert/delete
1 parent d59b298 commit f0f42eb

File tree

2 files changed

+54
-39
lines changed

2 files changed

+54
-39
lines changed

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

Lines changed: 51 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -70,18 +70,13 @@ defmodule Module.Types.Descr do
7070
def non_empty_list(type, tail \\ @empty_list), do: list_descr(type, tail, false)
7171
def open_map(), do: %{map: @map_top}
7272
def open_map(pairs), do: map_descr(:open, pairs)
73+
def open_tuple(elements), do: tuple_descr(:open, elements)
7374
def pid(), do: %{bitmap: @bit_pid}
7475
def port(), do: %{bitmap: @bit_port}
7576
def reference(), do: %{bitmap: @bit_reference}
7677
def tuple(), do: %{tuple: @tuple_top}
77-
def open_tuple(elements), do: tuple_descr(:open, elements)
7878
def tuple(elements), do: tuple_descr(:closed, elements)
7979

80-
# Tuple helper
81-
defp tuple_of_size_at_least(n) when is_integer(n) and n >= 0 do
82-
open_tuple(List.duplicate(term(), n))
83-
end
84-
8580
@boolset :sets.from_list([true, false], version: 2)
8681
def boolean(), do: %{atom: {:union, @boolset}}
8782

@@ -1166,19 +1161,23 @@ defmodule Module.Types.Descr do
11661161
end
11671162
end
11681163

1169-
defp map_put_static_descr(static, _, _) when static == @none, do: @none
1170-
11711164
# Directly inserts a key of a given type into every positive and negative map
11721165
defp map_put_static_descr(descr, key, type) do
1173-
map_delete_static(descr, key)
1174-
|> Map.update!(:map, fn dnf ->
1175-
Enum.map(dnf, fn {tag, fields, negs} ->
1176-
{tag, Map.put(fields, key, type),
1177-
Enum.map(negs, fn {neg_tag, neg_fields} ->
1178-
{neg_tag, Map.put(neg_fields, key, type)}
1179-
end)}
1180-
end)
1181-
end)
1166+
case map_delete_static(descr, key) do
1167+
%{map: dnf} = descr ->
1168+
dnf =
1169+
Enum.map(dnf, fn {tag, fields, negs} ->
1170+
{tag, Map.put(fields, key, type),
1171+
Enum.map(negs, fn {neg_tag, neg_fields} ->
1172+
{neg_tag, Map.put(neg_fields, key, type)}
1173+
end)}
1174+
end)
1175+
1176+
%{descr | map: dnf}
1177+
1178+
%{} ->
1179+
descr
1180+
end
11821181
end
11831182

11841183
defp pop_optional_static(type) do
@@ -1883,6 +1882,11 @@ defmodule Module.Types.Descr do
18831882
end
18841883
end
18851884

1885+
@doc """
1886+
Delete an element from the tuple.
1887+
1888+
It returns the same as `tuple_fetch/2`.
1889+
"""
18861890
# Same as tuple_delete but checks if the index is out of range.
18871891
def tuple_delete_at(:term, _key), do: :badtuple
18881892

@@ -1891,21 +1895,22 @@ defmodule Module.Types.Descr do
18911895
:error ->
18921896
# Note: the empty type is not a valid input
18931897
is_proper_tuple? = descr_key?(descr, :tuple) and tuple_only?(descr)
1894-
is_proper_size? = subtype?(Map.take(descr, [:tuple]), tuple_of_size_at_least(index + 1))
1898+
is_proper_size? = tuple_of_size_at_least?(descr, index + 1)
18951899

18961900
cond do
18971901
is_proper_tuple? and is_proper_size? -> tuple_delete_static(descr, index)
1898-
is_proper_tuple? -> :badrange
1902+
is_proper_tuple? -> :badindex
18991903
true -> :badtuple
19001904
end
19011905

19021906
{dynamic, static} ->
19031907
is_proper_tuple? = descr_key?(dynamic, :tuple) and tuple_only?(static)
1904-
is_proper_size? = subtype?(Map.take(static, [:tuple]), tuple_of_size_at_least(index + 1))
1908+
is_proper_size? = tuple_of_size_at_least?(static, index + 1)
19051909

19061910
cond do
19071911
is_proper_tuple? and is_proper_size? ->
19081912
static_result = tuple_delete_static(static, index)
1913+
19091914
# Prune for dynamic values make the intersection succeed
19101915
dynamic_result =
19111916
intersection(dynamic, tuple_of_size_at_least(index))
@@ -1915,7 +1920,7 @@ defmodule Module.Types.Descr do
19151920

19161921
# Highlight the case where the issue is an index out of range from the tuple
19171922
is_proper_tuple? ->
1918-
:badrange
1923+
:badindex
19191924

19201925
true ->
19211926
:badtuple
@@ -1925,45 +1930,47 @@ defmodule Module.Types.Descr do
19251930

19261931
def tuple_delete_at(_, _), do: :badindex
19271932

1933+
@doc """
1934+
Insert an element at the tuple.
1935+
1936+
It returns the same as `tuple_fetch/2` but with `:badrange` instead of `:badindex`.
1937+
"""
19281938
def tuple_insert_at(:term, _key, _type), do: :badtuple
19291939

19301940
def tuple_insert_at(descr, index, type) when is_integer(index) and index >= 0 do
19311941
case :maps.take(:dynamic, unfold(type)) do
1932-
:error -> tuple_insert_static_value(descr, index, type)
1933-
{dynamic, _static} -> dynamic(tuple_insert_static_value(descr, index, dynamic))
1942+
:error -> tuple_insert_at_checked(descr, index, type)
1943+
{dynamic, _static} -> dynamic(tuple_insert_at_checked(descr, index, dynamic))
19341944
end
19351945
end
19361946

1937-
def tuple_insert_at(_, _, _), do: :badindex
1947+
def tuple_insert_at(_, _, _), do: :badrange
19381948

1939-
defp tuple_insert_static_value(descr, index, type) do
1949+
defp tuple_insert_at_checked(descr, index, type) do
19401950
case :maps.take(:dynamic, descr) do
19411951
:error ->
19421952
# Note: the empty type is not a valid input
19431953
is_proper_tuple? = descr_key?(descr, :tuple) and tuple_only?(descr)
1944-
1945-
is_proper_size? =
1946-
index == 0 or subtype?(Map.take(descr, [:tuple]), tuple_of_size_at_least(index))
1954+
is_proper_size? = index == 0 or tuple_of_size_at_least?(descr, index)
19471955

19481956
cond do
1949-
is_proper_tuple? and is_proper_size? -> insert_element(descr, index, type)
1957+
is_proper_tuple? and is_proper_size? -> tuple_insert_static(descr, index, type)
19501958
is_proper_tuple? -> :badrange
19511959
true -> :badtuple
19521960
end
19531961

19541962
{dynamic, static} ->
19551963
is_proper_tuple? = descr_key?(dynamic, :tuple) and tuple_only?(static)
1956-
1957-
is_proper_size? =
1958-
index == 0 or subtype?(Map.take(static, [:tuple]), tuple_of_size_at_least(index))
1964+
is_proper_size? = index == 0 or tuple_of_size_at_least?(static, index)
19591965

19601966
cond do
19611967
is_proper_tuple? and is_proper_size? ->
1962-
static_result = insert_element(static, index, type)
1968+
static_result = tuple_insert_static(static, index, type)
1969+
19631970
# Prune for dynamic values that make the intersection succeed
19641971
dynamic_result =
19651972
intersection(dynamic, tuple_of_size_at_least(index))
1966-
|> insert_element(index, type)
1973+
|> tuple_insert_static(index, type)
19671974

19681975
union(dynamic(dynamic_result), static_result)
19691976

@@ -1977,9 +1984,9 @@ defmodule Module.Types.Descr do
19771984
end
19781985
end
19791986

1980-
defp insert_element(descr, _, _) when descr == @none, do: none()
1987+
defp tuple_insert_static(descr, _, _) when descr == @none, do: none()
19811988

1982-
defp insert_element(descr, index, type) do
1989+
defp tuple_insert_static(descr, index, type) do
19831990
Map.update!(descr, :tuple, fn dnf ->
19841991
Enum.map(dnf, fn {tag, elements, negs} ->
19851992
{tag, List.insert_at(elements, index, type),
@@ -2021,6 +2028,14 @@ defmodule Module.Types.Descr do
20212028
(tag == :closed and n < m) or (neg_tag == :closed and n > m)
20222029
end
20232030

2031+
defp tuple_of_size_at_least(n) when is_integer(n) and n >= 0 do
2032+
open_tuple(List.duplicate(term(), n))
2033+
end
2034+
2035+
defp tuple_of_size_at_least?(descr, index) do
2036+
subtype?(Map.take(descr, [:tuple]), tuple_of_size_at_least(index))
2037+
end
2038+
20242039
## Pairs
20252040
#
20262041
# To simplify disjunctive normal forms of e.g., map types, it is useful to

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -692,9 +692,9 @@ defmodule Module.Types.DescrTest do
692692
end
693693

694694
test "tuple_delete_at" do
695-
assert tuple_delete_at(tuple([integer(), atom()]), 3) == :badrange
695+
assert tuple_delete_at(tuple([integer(), atom()]), 3) == :badindex
696696
assert tuple_delete_at(tuple([integer(), atom()]), -1) == :badindex
697-
assert tuple_delete_at(empty_tuple(), 0) == :badrange
697+
assert tuple_delete_at(empty_tuple(), 0) == :badindex
698698
assert tuple_delete_at(integer(), 0) == :badtuple
699699
assert tuple_delete_at(term(), 0) == :badtuple
700700

@@ -743,7 +743,7 @@ defmodule Module.Types.DescrTest do
743743

744744
test "tuple_insert_at" do
745745
assert tuple_insert_at(tuple([integer(), atom()]), 3, boolean()) == :badrange
746-
assert tuple_insert_at(tuple([integer(), atom()]), -1, boolean()) == :badindex
746+
assert tuple_insert_at(tuple([integer(), atom()]), -1, boolean()) == :badrange
747747
assert tuple_insert_at(integer(), 0, boolean()) == :badtuple
748748
assert tuple_insert_at(term(), 0, boolean()) == :badtuple
749749

0 commit comments

Comments
 (0)