Skip to content

Commit

Permalink
Add more tests around list hd/tl
Browse files Browse the repository at this point in the history
  • Loading branch information
josevalim committed Nov 4, 2024
1 parent e54b87c commit 7e1aaac
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 28 deletions.
41 changes: 13 additions & 28 deletions lib/elixir/lib/module/types/descr.ex
Original file line number Diff line number Diff line change
Expand Up @@ -561,15 +561,6 @@ defmodule Module.Types.Descr do
def number_type?(%{bitmap: bitmap}) when (bitmap &&& @bit_number) != 0, do: true
def number_type?(_), do: false

@doc """
Optimized version of `not empty?(intersection(list(), type))`.
"""
def list_type?(:term), do: true
def list_type?(%{dynamic: :term}), do: true
def list_type?(%{dynamic: %{list: _}}), do: true
def list_type?(%{list: _}), do: true
def list_type?(_), do: false

## Bitmaps

defp bitmap_to_quoted(val) do
Expand Down Expand Up @@ -917,7 +908,7 @@ defmodule Module.Types.Descr do
end)
end

defp list_only?(descr), do: subtype?(Map.delete(descr, :list), empty_list())
defp non_empty_list_only?(descr), do: empty?(Map.delete(descr, :list))

@doc """
Returns the head of a list.
Expand All @@ -931,22 +922,19 @@ defmodule Module.Types.Descr do
def list_hd(%{} = descr) do
case :maps.take(:dynamic, descr) do
:error ->
has_empty = empty_list_type?(descr)
is_list_type = list_only?(descr)
static_value = list_hd_static(descr)

if is_list_type and not has_empty do
{false, list_hd_static(descr)}
if non_empty_list_only?(descr) and not empty?(static_value) do
{false, static_value}
else
:badnonemptylist
end

{dynamic, static} ->
has_empty = empty_list_type?(static)
only_list = list_only?(static)
is_dynamic_list = list_type?(dynamic)
dynamic_value = list_hd_static(dynamic)

if is_dynamic_list and only_list and not has_empty do
{is_dynamic_list, union(dynamic(list_hd_static(dynamic)), list_hd_static(static))}
if non_empty_list_only?(static) and not empty?(dynamic_value) do
{true, union(dynamic(dynamic_value), list_hd_static(static))}
else
:badnonemptylist
end
Expand Down Expand Up @@ -981,22 +969,19 @@ defmodule Module.Types.Descr do
def list_tl(descr) do
case :maps.take(:dynamic, descr) do
:error ->
has_empty = empty_list_type?(descr)
is_list_type = list_only?(descr)
static_value = list_tl_static(descr)

if is_list_type and not has_empty do
{false, list_tl_static(descr)}
if non_empty_list_only?(descr) and not empty?(static_value) do
{false, static_value}
else
:badnonemptylist
end

{dynamic, static} ->
has_empty = empty_list_type?(static)
only_list = list_only?(static)
is_dynamic_list = list_type?(dynamic)
dynamic_value = list_tl_static(dynamic)

if is_dynamic_list and only_list and not has_empty do
{is_dynamic_list, union(dynamic(list_tl_static(dynamic)), list_tl_static(static))}
if non_empty_list_only?(static) and not empty?(dynamic_value) do
{true, union(dynamic(dynamic_value), list_tl_static(static))}
else
:badnonemptylist
end
Expand Down
7 changes: 7 additions & 0 deletions lib/elixir/test/elixir/module/types/descr_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,7 @@ defmodule Module.Types.DescrTest do
end

test "list_hd" do
assert list_hd(none()) == :badnonemptylist
assert list_hd(term()) == :badnonemptylist
assert list_hd(list(term())) == :badnonemptylist
assert list_hd(empty_list()) == :badnonemptylist
Expand All @@ -639,6 +640,9 @@ defmodule Module.Types.DescrTest do
assert list_hd(union(dynamic(), atom())) == :badnonemptylist
assert list_hd(union(dynamic(), list(term()))) == :badnonemptylist

assert list_hd(difference(list(number()), list(number()))) == :badnonemptylist
assert list_hd(dynamic(difference(list(number()), list(number())))) == :badnonemptylist

assert list_hd(union(dynamic(list(float())), non_empty_list(atom()))) ==
{true, union(dynamic(float()), atom())}

Expand All @@ -649,9 +653,12 @@ defmodule Module.Types.DescrTest do
end

test "list_tl" do
assert list_tl(none()) == :badnonemptylist
assert list_tl(term()) == :badnonemptylist
assert list_tl(empty_list()) == :badnonemptylist
assert list_tl(list(integer())) == :badnonemptylist
assert list_tl(difference(list(number()), list(number()))) == :badnonemptylist

assert list_tl(non_empty_list(integer())) == {false, list(integer())}

assert list_tl(non_empty_list(integer(), atom())) ==
Expand Down

0 comments on commit 7e1aaac

Please sign in to comment.