Skip to content

Commit 0e3465d

Browse files
committed
Improvements to list_proper?
1 parent ee75b22 commit 0e3465d

File tree

2 files changed

+39
-30
lines changed

2 files changed

+39
-30
lines changed

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

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1882,32 +1882,40 @@ defmodule Module.Types.Descr do
18821882
Checks if a list type is a proper list (terminated by empty list).
18831883
"""
18841884
def list_proper?(:term), do: false
1885-
def list_proper?(%{} = descr), do: Map.get(descr, :dynamic, descr) |> list_proper_static?()
18861885

1887-
defp list_proper_static?(:term), do: false
1886+
def list_proper?(descr) do
1887+
{dynamic, static} = Map.pop(descr, :dynamic, descr)
18881888

1889-
defp list_proper_static?(%{} = descr) do
18901889
# A list is proper if it's either the empty list alone, or all non-empty
1891-
# list types have tails that are subtypes of empty list
1892-
case descr do
1893-
%{bitmap: bitmap, list: bdd} ->
1894-
(bitmap &&& @bit_empty_list) != 0 and empty?(Map.drop(descr, [:list, :bitmap])) and
1895-
list_bdd_proper?(bdd)
1896-
1897-
%{bitmap: bitmap} ->
1898-
(bitmap &&& @bit_empty_list) != 0 and empty?(Map.delete(descr, :bitmap))
1899-
1890+
# list types have tails that are subtypes of empty list.
1891+
case static do
19001892
%{list: bdd} ->
1901-
empty?(Map.delete(descr, :list)) and list_bdd_proper?(bdd)
1893+
Map.get(static, :bitmap, @bit_empty_list) == @bit_empty_list and
1894+
empty?(Map.drop(static, [:list, :bitmap])) and
1895+
list_bdd_to_pos_dnf(bdd)
1896+
|> Enum.all?(fn {_list, last, _negs} -> subtype?(last, @empty_list) end)
1897+
1898+
_ when static == %{bitmap: @bit_empty_list} ->
1899+
true
19021900

19031901
%{} ->
1904-
empty?(descr)
1905-
end
1906-
end
1902+
# Dynamic requires only the empty list or a single proper list
1903+
empty?(static) and
1904+
case dynamic do
1905+
:term ->
1906+
true
19071907

1908-
defp list_bdd_proper?(bdd) do
1909-
list_bdd_to_pos_dnf(bdd)
1910-
|> Enum.all?(fn {_list, last, _negs} -> subtype?(last, @empty_list) end)
1908+
%{bitmap: bitmap} when (bitmap &&& @bit_empty_list) != 0 ->
1909+
true
1910+
1911+
%{list: bdd} ->
1912+
list_bdd_to_pos_dnf(bdd)
1913+
|> Enum.any?(fn {_list, last, _negs} -> subtype?(last, @empty_list) end)
1914+
1915+
%{} ->
1916+
false
1917+
end
1918+
end
19111919
end
19121920

19131921
defp list_intersection(bdd_leaf(list1, last1), bdd_leaf(list2, last2)) do

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

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1269,24 +1269,25 @@ defmodule Module.Types.DescrTest do
12691269
end
12701270

12711271
test "list_proper?" do
1272-
assert list_proper?(term()) == false
1273-
assert list_proper?(none()) == true
1274-
assert list_proper?(empty_list()) == true
1275-
assert list_proper?(non_empty_list(integer())) == true
1276-
assert list_proper?(non_empty_list(integer(), atom())) == false
1277-
assert list_proper?(non_empty_list(integer(), term())) == false
1278-
assert list_proper?(non_empty_list(integer(), list(term()))) == true
1279-
assert list_proper?(list(integer()) |> union(list(integer(), integer()))) == false
1280-
assert list_proper?(dynamic(list(integer()))) == true
1281-
assert list_proper?(dynamic(list(integer(), atom()))) == false
1272+
refute list_proper?(term())
1273+
refute list_proper?(none())
1274+
assert list_proper?(empty_list())
1275+
assert list_proper?(non_empty_list(integer()))
1276+
refute list_proper?(non_empty_list(integer(), atom()))
1277+
refute list_proper?(non_empty_list(integer(), term()))
1278+
assert list_proper?(non_empty_list(integer(), list(term())))
1279+
refute list_proper?(list(integer()) |> union(list(integer(), integer())))
1280+
assert list_proper?(dynamic(list(integer())))
1281+
assert list_proper?(dynamic(list(integer(), atom())))
1282+
refute list_proper?(dynamic(non_empty_list(integer(), atom())))
12821283

12831284
# An empty list
12841285
list_with_tail =
12851286
non_empty_list(atom(), union(integer(), empty_list()))
12861287
|> difference(non_empty_list(atom([:ok]), integer()))
12871288
|> difference(non_empty_list(atom(), term()))
12881289

1289-
assert list_proper?(list_with_tail) == true
1290+
assert list_proper?(list_with_tail)
12901291
end
12911292

12921293
test "list_tl" do

0 commit comments

Comments
 (0)