diff --git a/lib/elixir/lib/module/types/apply.ex b/lib/elixir/lib/module/types/apply.ex index a5d7da239e..0ea21f856d 100644 --- a/lib/elixir/lib/module/types/apply.ex +++ b/lib/elixir/lib/module/types/apply.ex @@ -360,14 +360,14 @@ defmodule Module.Types.Apply do defp remote_apply(:erlang, :hd, _info, [list], _stack) do case list_hd(list) do - {_, value_type} -> {:ok, value_type} + {:ok, value_type} -> {:ok, value_type} :badnonemptylist -> {:error, badremote(:erlang, :hd, 1)} end end defp remote_apply(:erlang, :tl, _info, [list], _stack) do case list_tl(list) do - {_, value_type} -> {:ok, value_type} + {:ok, value_type} -> {:ok, value_type} :badnonemptylist -> {:error, badremote(:erlang, :tl, 1)} end end diff --git a/lib/elixir/lib/module/types/descr.ex b/lib/elixir/lib/module/types/descr.ex index e6ec0abd7d..185e60dee2 100644 --- a/lib/elixir/lib/module/types/descr.ex +++ b/lib/elixir/lib/module/types/descr.ex @@ -54,7 +54,6 @@ defmodule Module.Types.Descr do @list_top %{bitmap: @bit_empty_list, list: @non_empty_list_top} @empty_list %{bitmap: @bit_empty_list} @not_non_empty_list Map.delete(@term, :list) - @not_list Map.replace!(@not_non_empty_list, :bitmap, @bit_top - @bit_empty_list) @not_set %{optional: 1} @term_or_optional Map.put(@term, :optional, 1) @@ -1972,7 +1971,7 @@ defmodule Module.Types.Descr do static_value = list_hd_static(descr) if non_empty_list_only?(descr) and not empty?(static_value) do - {false, static_value} + {:ok, static_value} else :badnonemptylist end @@ -1981,7 +1980,7 @@ defmodule Module.Types.Descr do dynamic_value = list_hd_static(dynamic) if non_empty_list_only?(static) and not empty?(dynamic_value) do - {true, union(dynamic(dynamic_value), list_hd_static(static))} + {:ok, union(dynamic(dynamic_value), list_hd_static(static))} else :badnonemptylist end @@ -2015,7 +2014,7 @@ defmodule Module.Types.Descr do static_value = list_tl_static(descr) if non_empty_list_only?(descr) and not empty?(static_value) do - {false, static_value} + {:ok, static_value} else :badnonemptylist end @@ -2024,7 +2023,7 @@ defmodule Module.Types.Descr do dynamic_value = list_tl_static(dynamic) if non_empty_list_only?(static) and not empty?(dynamic_value) do - {true, union(dynamic(dynamic_value), list_tl_static(static))} + {:ok, union(dynamic(dynamic_value), list_tl_static(static))} else :badnonemptylist end @@ -2048,10 +2047,6 @@ defmodule Module.Types.Descr do defp list_tl_static(%{}), do: none() - defp list_improper_static?(:term), do: false - defp list_improper_static?(%{bitmap: bitmap}) when (bitmap &&& @bit_empty_list) != 0, do: false - defp list_improper_static?(term), do: equal?(term, @not_list) - defp list_to_quoted(bdd, empty?, opts) do dnf = list_normalize(bdd) @@ -2059,17 +2054,12 @@ defmodule Module.Types.Descr do dnf |> Enum.reduce({[], false}, fn {list_type, last_type, negs}, {acc, list_rendered?} -> {name, arguments, list_rendered?} = - cond do - list_type == term() and list_improper_static?(last_type) -> - {:improper_list, [], list_rendered?} - - subtype?(last_type, @empty_list) -> - name = if empty?, do: :list, else: :non_empty_list - {name, [to_quoted(list_type, opts)], empty?} - - true -> - args = [to_quoted(list_type, opts), to_quoted(last_type, opts)] - {:non_empty_list, args, list_rendered?} + if subtype?(last_type, @empty_list) do + name = if empty?, do: :list, else: :non_empty_list + {name, [to_quoted(list_type, opts)], empty?} + else + args = [to_quoted(list_type, opts), to_quoted(last_type, opts)] + {:non_empty_list, args, list_rendered?} end acc = diff --git a/lib/elixir/lib/module/types/expr.ex b/lib/elixir/lib/module/types/expr.ex index 066ffa5d6a..7a9de17f06 100644 --- a/lib/elixir/lib/module/types/expr.ex +++ b/lib/elixir/lib/module/types/expr.ex @@ -94,7 +94,7 @@ defmodule Module.Types.Expr do else hd_type = case list_hd(expected) do - {_, type} -> type + {:ok, type} -> type _ -> term() end @@ -106,8 +106,8 @@ defmodule Module.Types.Expr do else tl_type = case list_tl(expected) do - {_, type} -> type - _ -> term() + {:ok, type} -> type + :badnonemptylist -> term() end of_expr(suffix, tl_type, expr, stack, context) diff --git a/lib/elixir/lib/module/types/pattern.ex b/lib/elixir/lib/module/types/pattern.ex index 792b41921b..ecfca9fd64 100644 --- a/lib/elixir/lib/module/types/pattern.ex +++ b/lib/elixir/lib/module/types/pattern.ex @@ -305,7 +305,7 @@ defmodule Module.Types.Pattern do defp of_pattern_var([{:head, counter} | rest], type, _reachable_var?, info, context) do case list_hd(type) do - {_, head} -> + {:ok, head} -> tree = Map.fetch!(info, -counter) type = intersection(of_pattern_tree(tree, context), head) of_pattern_var(rest, type, false, info, context) @@ -317,8 +317,8 @@ defmodule Module.Types.Pattern do defp of_pattern_var([:tail | rest], type, reachable_var?, info, context) do case list_tl(type) do - {_, tail} -> of_pattern_var(rest, tail, reachable_var?, info, context) - _ -> :error + {:ok, tail} -> of_pattern_var(rest, tail, reachable_var?, info, context) + :badnonemptylist -> :error end end diff --git a/lib/elixir/pages/cheatsheets/types-cheat.cheatmd b/lib/elixir/pages/cheatsheets/types-cheat.cheatmd index 66a0d7f9b3..0b60cd1808 100644 --- a/lib/elixir/pages/cheatsheets/types-cheat.cheatmd +++ b/lib/elixir/pages/cheatsheets/types-cheat.cheatmd @@ -5,6 +5,32 @@ # Set-theoretic types cheatsheet +## Set operators + +#### Union + +```elixir +type1 or type2 +``` + +#### Intersection + +```elixir +type1 and type2 +``` + +#### Difference + +```elixir +type1 and not type2 +``` + +#### Negation + +```elixir +not type +``` + ## Data types ### Indivisible types diff --git a/lib/elixir/test/elixir/module/types/descr_test.exs b/lib/elixir/test/elixir/module/types/descr_test.exs index 33d86fe730..49d3eed89b 100644 --- a/lib/elixir/test/elixir/module/types/descr_test.exs +++ b/lib/elixir/test/elixir/module/types/descr_test.exs @@ -1246,12 +1246,12 @@ defmodule Module.Types.DescrTest do assert list_hd(term()) == :badnonemptylist assert list_hd(list(term())) == :badnonemptylist assert list_hd(empty_list()) == :badnonemptylist - assert list_hd(non_empty_list(term())) == {false, term()} - assert list_hd(non_empty_list(integer())) == {false, integer()} - assert list_hd(difference(list(number()), list(integer()))) == {false, number()} + assert list_hd(non_empty_list(term())) == {:ok, term()} + assert list_hd(non_empty_list(integer())) == {:ok, integer()} + assert list_hd(difference(list(number()), list(integer()))) == {:ok, number()} - assert list_hd(dynamic()) == {true, dynamic()} - assert list_hd(dynamic(list(integer()))) == {true, dynamic(integer())} + assert list_hd(dynamic()) == {:ok, dynamic()} + assert list_hd(dynamic(list(integer()))) == {:ok, dynamic(integer())} assert list_hd(union(dynamic(), atom())) == :badnonemptylist assert list_hd(union(dynamic(), list(term()))) == :badnonemptylist @@ -1259,12 +1259,12 @@ defmodule Module.Types.DescrTest do 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())} + {:ok, union(dynamic(float()), atom())} # If term() is in the tail, it means list(term()) is in the tail # and therefore any term can be returned from hd. - assert list_hd(non_empty_list(atom(), term())) == {false, term()} - assert list_hd(non_empty_list(atom(), negation(list(term(), term())))) == {false, atom()} + assert list_hd(non_empty_list(atom(), term())) == {:ok, term()} + assert list_hd(non_empty_list(atom(), negation(list(term(), term())))) == {:ok, atom()} end test "list_tl" do @@ -1274,27 +1274,27 @@ defmodule Module.Types.DescrTest do 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())) == {:ok, list(integer())} assert list_tl(non_empty_list(integer(), atom())) == - {false, union(atom(), non_empty_list(integer(), atom()))} + {:ok, union(atom(), non_empty_list(integer(), atom()))} # The tail of either a (non empty) list of integers with an atom tail or a (non empty) list # of tuples with a float tail is either an atom, or a float, or a (possibly empty) list of # integers with an atom tail, or a (possibly empty) list of tuples with a float tail. assert list_tl(union(non_empty_list(integer(), atom()), non_empty_list(tuple(), float()))) == - {false, + {:ok, atom() |> union(float()) |> union( union(non_empty_list(integer(), atom()), non_empty_list(tuple(), float())) )} - assert list_tl(dynamic()) == {true, dynamic()} - assert list_tl(dynamic(list(integer()))) == {true, dynamic(list(integer()))} + assert list_tl(dynamic()) == {:ok, dynamic()} + assert list_tl(dynamic(list(integer()))) == {:ok, dynamic(list(integer()))} assert list_tl(dynamic(list(integer(), atom()))) == - {true, dynamic(union(atom(), list(integer(), atom())))} + {:ok, dynamic(union(atom(), list(integer(), atom())))} end test "tuple_fetch" do @@ -2374,9 +2374,6 @@ defmodule Module.Types.DescrTest do assert list(term(), term()) |> to_quoted_string() == "empty_list() or non_empty_list(term(), term())" - assert non_empty_list(term(), difference(term(), list(term()))) |> to_quoted_string() == - "improper_list()" - # Test normalization # Remove duplicates