Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 0 additions & 10 deletions lib/elixir/lib/access.ex
Original file line number Diff line number Diff line change
Expand Up @@ -873,11 +873,6 @@ defmodule Access do
...> end)
{[], [%{name: "john", salary: 10}, %{name: "francine", salary: 30}]}
An error is raised if the predicate is not a function or is of the incorrect arity:
iex> get_in([], [Access.filter(5)])
** (FunctionClauseError) no function clause matching in Access.filter/1
An error is raised if the accessed structure is not a list:
iex> get_in(%{}, [Access.filter(fn a -> a == 10 end)])
Expand Down Expand Up @@ -1154,11 +1149,6 @@ defmodule Access do
...> end)
{nil, [%{name: "john", salary: 10}, %{name: "francine", salary: 30}]}
An error is raised if the predicate is not a function or is of the incorrect arity:
iex> get_in([], [Access.find(5)])
** (FunctionClauseError) no function clause matching in Access.find/1
An error is raised if the accessed structure is not a list:
iex> get_in(%{}, [Access.find(fn a -> a == 10 end)])
Expand Down
4 changes: 2 additions & 2 deletions lib/elixir/lib/exception.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1930,8 +1930,8 @@ defmodule FunctionClauseError do
For example:
iex> URI.parse(:wrong_argument)
** (FunctionClauseError) no function clause matching in URI.parse/1
iex> List.duplicate(:ok, -3)
** (FunctionClauseError) no function clause matching in List.duplicate/2
The following fields of this exception are public and can be accessed freely:
Expand Down
7 changes: 4 additions & 3 deletions lib/elixir/lib/list.ex
Original file line number Diff line number Diff line change
Expand Up @@ -187,9 +187,10 @@ defmodule List do
"""
@spec duplicate(any, 0) :: []
@spec duplicate(elem, pos_integer) :: [elem, ...] when elem: var
def duplicate(elem, n) do
:lists.duplicate(n, elem)
end
def duplicate(elem, n) when is_integer(n) and n >= 0, do: duplicate(n, elem, [])

defp duplicate(0, _elem, acc), do: acc
defp duplicate(n, elem, acc), do: duplicate(n - 1, elem, [elem | acc])

@doc """
Flattens the given `list` of nested lists.
Expand Down
4 changes: 3 additions & 1 deletion lib/elixir/lib/module/types.ex
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,9 @@ defmodule Module.Types do
warnings: [],
# All vars and their types
vars: %{},
# Variables and arguments from patterns
# Variables that are specific to the current environment/conditional
conditional_vars: nil,
# Track metadata specific to matches and guards
pattern_info: nil,
# If type checking has found an error/failure
failed: false,
Expand Down
91 changes: 69 additions & 22 deletions lib/elixir/lib/module/types/apply.ex
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,6 @@ defmodule Module.Types.Apply do
{[float(), float()], float()}
]

is_clauses = [{[term()], boolean()}]

args_or_arity = union(list(term()), integer())
args_or_none = union(list(term()), atom([:none]))
extra_info = kw.(file: list(integer()), line: integer(), error_info: open_map())
Expand All @@ -119,6 +117,11 @@ defmodule Module.Types.Apply do
|> union(tuple([fun(), args_or_arity]))
)

not_signature =
for bool <- [true, false] do
{[atom([bool])], atom([not bool])}
end

and_signature =
for left <- [true, false], right <- [true, false] do
{[atom([left]), atom([right])], atom([left and right])}
Expand Down Expand Up @@ -180,22 +183,8 @@ defmodule Module.Types.Apply do
{:erlang, :integer_to_binary, [{[integer(), integer()], binary()}]},
{:erlang, :integer_to_list, [{[integer()], non_empty_list(integer())}]},
{:erlang, :integer_to_list, [{[integer(), integer()], non_empty_list(integer())}]},
{:erlang, :is_atom, is_clauses},
{:erlang, :is_binary, is_clauses},
{:erlang, :is_bitstring, is_clauses},
{:erlang, :is_boolean, is_clauses},
{:erlang, :is_float, is_clauses},
{:erlang, :is_function, is_clauses},
{:erlang, :is_function, [{[term(), integer()], boolean()}]},
{:erlang, :is_integer, is_clauses},
{:erlang, :is_list, is_clauses},
{:erlang, :is_map, is_clauses},
{:erlang, :is_map_key, [{[term(), open_map()], boolean()}]},
{:erlang, :is_number, is_clauses},
{:erlang, :is_pid, is_clauses},
{:erlang, :is_port, is_clauses},
{:erlang, :is_reference, is_clauses},
{:erlang, :is_tuple, is_clauses},
{:erlang, :length, [{[list(term())], integer()}]},
{:erlang, :list_to_atom, [{[list(integer())], atom()}]},
{:erlang, :list_to_existing_atom, [{[list(integer())], atom()}]},
Expand All @@ -206,7 +195,7 @@ defmodule Module.Types.Apply do
{:erlang, :map_size, [{[open_map()], integer()}]},
{:erlang, :node, [{[], atom()}]},
{:erlang, :node, [{[pid() |> union(reference()) |> union(port())], atom()}]},
{:erlang, :not, [{[atom([false])], atom([true])}, {[atom([true])], atom([false])}]},
{:erlang, :not, not_signature},
{:erlang, :or, or_signature},
{:erlang, :raise, [{[atom([:error, :exit, :throw]), term(), raise_stacktrace], none()}]},
{:erlang, :rem, [{[integer(), integer()], integer()}]},
Expand All @@ -216,8 +205,8 @@ defmodule Module.Types.Apply do
{:erlang, :spawn, [{mfargs, pid()}]},
{:erlang, :spawn_link, [{[fun(0)], pid()}]},
{:erlang, :spawn_link, [{mfargs, pid()}]},
{:erlang, :spawn_monitor, [{[fun(0)], tuple([reference(), pid()])}]},
{:erlang, :spawn_monitor, [{mfargs, tuple([reference(), pid()])}]},
{:erlang, :spawn_monitor, [{[fun(0)], tuple([pid(), reference()])}]},
{:erlang, :spawn_monitor, [{mfargs, tuple([pid(), reference()])}]},
{:erlang, :tuple_size, [{[open_tuple([])], integer()}]},
{:erlang, :trunc, [{[union(integer(), float())], integer()}]},

Expand Down Expand Up @@ -263,14 +252,14 @@ defmodule Module.Types.Apply do
[{[term(), open_map()], tuple([atom([:ok]), term()]) |> union(atom([:error]))}]},
{:maps, :get, [{[term(), open_map()], term()}]},
{:maps, :is_key, [{[term(), open_map()], boolean()}]},
{:maps, :keys, [{[open_map()], dynamic(list(term()))}]},
{:maps, :keys, [{[open_map()], list(term())}]},
{:maps, :put, [{[term(), term(), open_map()], open_map()}]},
{:maps, :remove, [{[term(), open_map()], open_map()}]},
{:maps, :take,
[{[term(), open_map()], tuple([term(), open_map()]) |> union(atom([:error]))}]},
{:maps, :to_list, [{[open_map()], dynamic(list(tuple([term(), term()])))}]},
{:maps, :to_list, [{[open_map()], list(tuple([term(), term()]))}]},
{:maps, :update, [{[term(), term(), open_map()], open_map()}]},
{:maps, :values, [{[open_map()], dynamic(list(term()))}]}
{:maps, :values, [{[open_map()], list(term())}]}
] do
[arity] = Enum.map(clauses, fn {args, _return} -> length(args) end) |> Enum.uniq()

Expand All @@ -296,6 +285,38 @@ defmodule Module.Types.Apply do
do: unquote(Macro.escape(domain_clauses))
end

is_guards = [
is_atom: atom(),
is_binary: binary(),
is_bitstring: binary(),
is_boolean: boolean(),
is_float: float(),
is_function: fun(),
is_integer: integer(),
is_list: union(empty_list(), non_empty_list(term(), term())),
is_map: open_map(),
is_number: union(float(), integer()),
is_pid: pid(),
is_port: port(),
is_reference: reference(),
is_tuple: tuple()
]

for {guard, type} <- is_guards do
# is_binary can actually fail for binaries if they are bitstrings
return = if guard == :is_binary, do: boolean(), else: atom([true])

domain_clauses =
{:strong, [term()],
[
{[type], return},
{[negation(type)], atom([false])}
]}

def signature(:erlang, unquote(guard), 1),
do: unquote(Macro.escape(domain_clauses))
end

def signature(_mod, _fun, _arity), do: :none

@doc """
Expand All @@ -320,6 +341,32 @@ defmodule Module.Types.Apply do
{:none, Enum.map(args, fn _ -> term() end), context}
end

def remote_domain(:erlang, :is_function, [_, arity], expected, _meta, _stack, context)
when is_integer(arity) and arity >= 0 do
type = fun(arity)

info =
{:strong, [term(), integer()],
[
{[type, integer()], atom([true])},
{[negation(type), integer()], atom([false])}
]}

{info, filter_domain(info, expected, 2), context}
end

def remote_domain(:erlang, :is_map_key, [key, _map], expected, _meta, _stack, context)
when is_atom(key) do
info =
{:strong, [term(), open_map()],
[
{[term(), open_map([{key, term()}])], atom([true])},
{[term(), open_map([{key, not_set()}])], atom([false])}
]}

{info, filter_domain(info, expected, 2), context}
end

def remote_domain(:erlang, :element, [index, _], expected, _meta, _stack, context)
when is_integer(index) do
tuple = open_tuple(List.duplicate(term(), max(index - 1, 0)) ++ [expected])
Expand Down
29 changes: 28 additions & 1 deletion lib/elixir/lib/module/types/descr.ex
Original file line number Diff line number Diff line change
Expand Up @@ -902,6 +902,24 @@ defmodule Module.Types.Descr do
:sets.from_list([false], version: 2)
]

@doc """
Returns true if the type can never be true.
"""
def never_true?(:term), do: false

def never_true?(%{} = descr) do
descr = Map.get(descr, :dynamic, descr)

case descr do
:term -> false
%{atom: {:union, %{true => _}}} -> false
%{atom: {:union, _}} -> true
%{atom: {:negation, %{true => _}}} -> true
%{atom: {:negation, _}} -> false
_ -> true
end
end

@doc """
Compute the truthiness of an element.

Expand Down Expand Up @@ -1687,7 +1705,16 @@ defmodule Module.Types.Descr do
defp pivot([], _acc, _fun), do: :error

# Converts a function BDD (Binary Decision Diagram) to its quoted representation
defp fun_to_quoted({:negation, _bdds}, _opts), do: [{:fun, [], []}]
defp fun_to_quoted({:negation, bdds}, opts) do
case fun_to_quoted({:union, bdds}, opts) do
[] ->
[{:fun, [], []}]

parts ->
ors = Enum.reduce(parts, &{:or, [], [&2, &1]})
[{:and, [], [{:fun, [], []}, {:not, [], [ors]}]}]
end
end

defp fun_to_quoted({:union, bdds}, opts) do
for {arity, bdd} <- bdds,
Expand Down
6 changes: 3 additions & 3 deletions lib/elixir/lib/module/types/of.ex
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,7 @@ defmodule Module.Types.Of do
Module.Types.Pattern.of_match_var(left, type, expr, stack, context)

:guard ->
Module.Types.Pattern.of_guard(left, type, expr, stack, context)
Module.Types.Pattern.of_guard(left, {false, type}, expr, stack, context)

:expr ->
left = annotate_interpolation(left, right)
Expand Down Expand Up @@ -511,9 +511,9 @@ defmodule Module.Types.Of do
compatible_size(actual, expr, stack, context)
end

defp specifier_size(_pattern_or_guard, {:size, _, [arg]} = expr, stack, context)
defp specifier_size(match_or_guard, {:size, _, [arg]} = expr, stack, context)
when not is_integer(arg) do
{actual, context} = Module.Types.Pattern.of_guard(arg, integer(), expr, stack, context)
{actual, context} = Module.Types.Pattern.of_size(match_or_guard, arg, expr, stack, context)
compatible_size(actual, expr, stack, context)
end

Expand Down
Loading
Loading