Skip to content

Commit 603602e

Browse files
authored
Implement reverse arrows for case (#15260)
1 parent 9222193 commit 603602e

18 files changed

Lines changed: 413 additions & 251 deletions

File tree

lib/elixir/lib/module/types.ex

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,9 @@ defmodule Module.Types do
437437
# The mode to be used, see the @modes attribute
438438
mode: mode,
439439
# The function for handling local calls
440-
local_handler: handler
440+
local_handler: handler,
441+
# Reverse arrow handling (nil | :cache | :use)
442+
reverse_arrow: nil
441443
}
442444
end
443445

@@ -459,20 +461,26 @@ defmodule Module.Types do
459461
# Local signatures used by local handler
460462
local_sigs: %{},
461463
# Track which clauses have been used across private local calls
462-
local_used: %{}
464+
local_used: %{},
465+
# Cached reverse arrows
466+
reverse_arrows: %{}
463467
}
464468
end
465469

466470
defp fresh_stack(stack, mode, function) when mode in @modes do
467-
%{stack | mode: mode, function: function}
471+
%{stack | mode: mode, function: function, reverse_arrow: nil}
468472
end
469473

470474
defp fresh_context(context) do
471-
%{context | vars: %{}, failed: false}
475+
%{context | vars: %{}, failed: false, reverse_arrows: %{}}
472476
end
473477

474-
defp restore_context(later_context, %{vars: vars, failed: failed}) do
475-
%{later_context | vars: vars, failed: failed}
478+
defp restore_context(later_context, %{
479+
vars: vars,
480+
failed: failed,
481+
reverse_arrows: reverse_arrows
482+
}) do
483+
%{later_context | vars: vars, failed: failed, reverse_arrows: reverse_arrows}
476484
end
477485

478486
## Diagnostics

lib/elixir/lib/module/types/apply.ex

Lines changed: 40 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -497,50 +497,54 @@ defmodule Module.Types.Apply do
497497
end
498498

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

509-
{expected, singleton?, context} =
510-
Enum.reduce(list, {acc, true, context}, fn literal, {acc, all_singleton?, context} ->
511-
{type, context} = of_fun.(literal, term(), expr, stack, context)
512+
{expected, singleton?, context} =
513+
Enum.reduce(list, {acc, true, context}, fn literal, {acc, all_singleton?, context} ->
514+
{type, context} = of_fun.(literal, term(), expr, stack, context)
512515

513-
if singleton?(type) do
514-
acc = if polarity, do: union(acc, type), else: intersection(acc, negation(type))
515-
{acc, all_singleton?, context}
516-
else
517-
acc = if polarity, do: union(acc, type), else: acc
518-
{acc, false, context}
519-
end
520-
end)
516+
if singleton?(type) do
517+
acc = if polarity, do: union(acc, type), else: intersection(acc, negation(type))
518+
{acc, all_singleton?, context}
519+
else
520+
acc = if polarity, do: union(acc, type), else: acc
521+
{acc, false, context}
522+
end
523+
end)
521524

522-
{arg_type, context} = of_fun.(arg, expected, expr, stack, context)
525+
{arg_type, context} = of_fun.(arg, expected, expr, stack, context)
523526

524-
cond do
525-
# Return a precise result
526-
singleton? and subtype?(arg_type, expected) ->
527-
{return(return, [arg_type, expected], stack), context}
527+
cond do
528+
# Return a precise result
529+
singleton? and subtype?(arg_type, expected) ->
530+
{return(return, [arg_type, expected], stack), context}
528531

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

533-
# Nothing in common between left and right, emit a warning
534-
disjoint?(arg_type, expected) ->
535-
error = {:mismatched_comparison, arg_type, list(expected)}
536-
remote_error(error, :lists, :member, 2, expr, stack, context)
536+
# Nothing in common between left and right, emit a warning
537+
disjoint?(arg_type, expected) ->
538+
error = {:mismatched_comparison, arg_type, list(expected)}
539+
remote_error(error, :lists, :member, 2, expr, stack, context)
537540

538-
true ->
539-
{return(boolean(), [arg_type, expected], stack), context}
540-
end
541+
true ->
542+
{return(boolean(), [arg_type, expected], stack), context}
543+
end
541544

542-
_ ->
543-
remote_domain(:lists, :member, args, expected, elem(expr, 1), stack, context)
545+
_ ->
546+
remote_domain(:lists, :member, args, expected, elem(expr, 1), stack, context)
547+
end
544548
end
545549
end
546550

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -971,6 +971,8 @@ defmodule Module.Types.Descr do
971971
domain of a function. It is used to refine dynamic types
972972
as we traverse the program.
973973
"""
974+
def compatible_intersection(other, :term), do: {:ok, remove_optional(other)}
975+
974976
def compatible_intersection(left, right) do
975977
{left_dynamic, left_static} = pop_dynamic(left)
976978

@@ -1810,7 +1812,7 @@ defmodule Module.Types.Descr do
18101812
cache = Map.put(cache, cache_key, false)
18111813
{false, cache}
18121814
else
1813-
{_index, result2, cache} =
1815+
{_index, result, cache} =
18141816
Enum.reduce_while(arguments, {0, true, cache}, fn
18151817
type, {index, acc_result, acc_cache} ->
18161818
{new_result, new_cache} =
@@ -1825,7 +1827,6 @@ defmodule Module.Types.Descr do
18251827
end
18261828
end)
18271829

1828-
result = result1 and result2
18291830
cache = Map.put(cache, cache_key, result)
18301831
{result, cache}
18311832
end

0 commit comments

Comments
 (0)