Skip to content
Merged
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
20 changes: 14 additions & 6 deletions lib/elixir/lib/module/types.ex
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,9 @@ defmodule Module.Types do
# The mode to be used, see the @modes attribute
mode: mode,
# The function for handling local calls
local_handler: handler
local_handler: handler,
# Reverse arrow handling (nil | :cache | :use)
reverse_arrow: nil
}
end

Expand All @@ -459,20 +461,26 @@ defmodule Module.Types do
# Local signatures used by local handler
local_sigs: %{},
# Track which clauses have been used across private local calls
local_used: %{}
local_used: %{},
# Cached reverse arrows
reverse_arrows: %{}
}
end

defp fresh_stack(stack, mode, function) when mode in @modes do
%{stack | mode: mode, function: function}
%{stack | mode: mode, function: function, reverse_arrow: nil}
end

defp fresh_context(context) do
%{context | vars: %{}, failed: false}
%{context | vars: %{}, failed: false, reverse_arrows: %{}}
end

defp restore_context(later_context, %{vars: vars, failed: failed}) do
%{later_context | vars: vars, failed: failed}
defp restore_context(later_context, %{
vars: vars,
failed: failed,
reverse_arrows: reverse_arrows
}) do
%{later_context | vars: vars, failed: failed, reverse_arrows: reverse_arrows}
end

## Diagnostics
Expand Down
76 changes: 40 additions & 36 deletions lib/elixir/lib/module/types/apply.ex
Original file line number Diff line number Diff line change
Expand Up @@ -497,50 +497,54 @@ defmodule Module.Types.Apply do
end

defp do_remote(:lists, :member, [arg, list] = args, expected, expr, stack, context, of_fun)
when is_list(list) and list != [] do
case booleaness(expected) do
{polarity, _maybe_or_always} ->
{return, acc} =
case polarity do
true -> {@atom_true, none()}
false -> {@atom_false, term()}
end
when is_list(list) do
if list == [] or Enum.any?(list, &match?({:|, _, [_, _]}, &1)) do
remote_domain(:lists, :member, args, expected, elem(expr, 1), stack, context)
else
case booleaness(expected) do
{polarity, _maybe_or_always} ->
{return, acc} =
case polarity do
true -> {@atom_true, none()}
false -> {@atom_false, term()}
end

{expected, singleton?, context} =
Enum.reduce(list, {acc, true, context}, fn literal, {acc, all_singleton?, context} ->
{type, context} = of_fun.(literal, term(), expr, stack, context)
{expected, singleton?, context} =
Enum.reduce(list, {acc, true, context}, fn literal, {acc, all_singleton?, context} ->
{type, context} = of_fun.(literal, term(), expr, stack, context)

if singleton?(type) do
acc = if polarity, do: union(acc, type), else: intersection(acc, negation(type))
{acc, all_singleton?, context}
else
acc = if polarity, do: union(acc, type), else: acc
{acc, false, context}
end
end)
if singleton?(type) do
acc = if polarity, do: union(acc, type), else: intersection(acc, negation(type))
{acc, all_singleton?, context}
else
acc = if polarity, do: union(acc, type), else: acc
{acc, false, context}
end
end)

{arg_type, context} = of_fun.(arg, expected, expr, stack, context)
{arg_type, context} = of_fun.(arg, expected, expr, stack, context)

cond do
# Return a precise result
singleton? and subtype?(arg_type, expected) ->
{return(return, [arg_type, expected], stack), context}
cond do
# Return a precise result
singleton? and subtype?(arg_type, expected) ->
{return(return, [arg_type, expected], stack), context}

# Singleton types with reverse polarity are negated, so we don't check for disjoint
(singleton? and not polarity) or not is_warning(stack) ->
{return(boolean(), [arg_type, expected], stack), context}
# Singleton types with reverse polarity are negated, so we don't check for disjoint
(singleton? and not polarity) or not is_warning(stack) ->
{return(boolean(), [arg_type, expected], stack), context}

# Nothing in common between left and right, emit a warning
disjoint?(arg_type, expected) ->
error = {:mismatched_comparison, arg_type, list(expected)}
remote_error(error, :lists, :member, 2, expr, stack, context)
# Nothing in common between left and right, emit a warning
disjoint?(arg_type, expected) ->
error = {:mismatched_comparison, arg_type, list(expected)}
remote_error(error, :lists, :member, 2, expr, stack, context)

true ->
{return(boolean(), [arg_type, expected], stack), context}
end
true ->
{return(boolean(), [arg_type, expected], stack), context}
end

_ ->
remote_domain(:lists, :member, args, expected, elem(expr, 1), stack, context)
_ ->
remote_domain(:lists, :member, args, expected, elem(expr, 1), stack, context)
end
end
end

Expand Down
5 changes: 3 additions & 2 deletions lib/elixir/lib/module/types/descr.ex
Original file line number Diff line number Diff line change
Expand Up @@ -971,6 +971,8 @@ defmodule Module.Types.Descr do
domain of a function. It is used to refine dynamic types
as we traverse the program.
"""
def compatible_intersection(other, :term), do: {:ok, remove_optional(other)}

def compatible_intersection(left, right) do
{left_dynamic, left_static} = pop_dynamic(left)

Expand Down Expand Up @@ -1810,7 +1812,7 @@ defmodule Module.Types.Descr do
cache = Map.put(cache, cache_key, false)
{false, cache}
else
{_index, result2, cache} =
{_index, result, cache} =
Enum.reduce_while(arguments, {0, true, cache}, fn
type, {index, acc_result, acc_cache} ->
{new_result, new_cache} =
Expand All @@ -1825,7 +1827,6 @@ defmodule Module.Types.Descr do
end
end)

result = result1 and result2
cache = Map.put(cache, cache_key, result)
{result, cache}
end
Expand Down
Loading
Loading