Skip to content

Commit 808075d

Browse files
committed
Implement reverse arrows for with
1 parent c14f3c7 commit 808075d

5 files changed

Lines changed: 201 additions & 124 deletions

File tree

lib/elixir/lib/module/types/expr.ex

Lines changed: 65 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ defmodule Module.Types.Expr do
138138
end
139139

140140
# left = right
141-
def of_expr({:=, _, [left_expr, right_expr]} = match, expected, expr, stack, context) do
141+
def of_expr({:=, meta, [left_expr, right_expr]} = match, expected, expr, stack, context) do
142142
{left_expr, right_expr} = repack_match(left_expr, right_expr)
143143

144144
case left_expr do
@@ -151,16 +151,16 @@ defmodule Module.Types.Expr do
151151
fn pattern_type, context ->
152152
# See if we can use the expected type to further refine the pattern type,
153153
# if we cannot, use the pattern type as that will fail later on.
154-
{_ok_or_error, type} = compatible_intersection(dynamic(pattern_type), expected)
154+
type = intersection(pattern_type, expected)
155+
type = if empty?(type), do: pattern_type, else: type
155156
{result, context} = of_expr(right_expr, type, expr, stack, context)
156157

157-
# The function may still return a too broad type, so we refine once again
158-
# to assign the most appropriate one for reverse arrows.
159-
{_ok_or_error, result} = compatible_intersection(result, expected)
160-
{result, context}
158+
# The function may still return a too broad type,
159+
# so we refine once again to assign the most appropriate to the pattern.
160+
{intersection(result, expected), context}
161161
end
162162

163-
Pattern.of_match(left_expr, type_fun, match, stack, context)
163+
Pattern.of_match(left_expr, type_fun, match, meta, stack, context)
164164
end
165165
end
166166

@@ -412,7 +412,7 @@ defmodule Module.Types.Expr do
412412
{type, context} =
413413
if else_block do
414414
{type, context} = of_expr(body, term(), body, stack, original)
415-
info = {:try_else, meta, body, type}
415+
info = {:try_else, type}
416416
of_clauses(else_block, [type], expected, info, stack, context, none())
417417
else
418418
of_expr(body, expected, expr, stack, original)
@@ -500,14 +500,13 @@ defmodule Module.Types.Expr do
500500
case into_kind do
501501
:bitstring ->
502502
{block_type, context} = of_expr(block, bitstring(), block, stack, context)
503+
intersection = intersection(block_type, bitstring())
503504

504-
case compatible_intersection(block_type, bitstring()) do
505-
{:ok, intersection} ->
506-
{union(into_type, intersection), context}
507-
508-
{:error, _} ->
509-
error = {:badbitbody, block_type, block, context}
510-
{error_type(), error(__MODULE__, error, meta, stack, context)}
505+
if empty?(intersection) do
506+
error = {:badbitbody, block_type, block, context}
507+
{error_type(), error(__MODULE__, error, meta, stack, context)}
508+
else
509+
{union(into_type, intersection), context}
511510
end
512511

513512
:non_empty_list ->
@@ -530,12 +529,27 @@ defmodule Module.Types.Expr do
530529
end
531530

532531
# TODO: with pat <- expr do expr end
533-
def of_expr({:with, meta, [_ | _] = clauses}, _expected, _expr, stack, original) do
532+
def of_expr({:with, meta, [_ | _] = clauses}, expected, _expr, stack, original) do
534533
cache_result(meta, stack, original, fn ->
535-
{clauses, [options]} = Enum.split(clauses, -1)
536-
context = Enum.reduce(clauses, original, &with_clause(&1, stack, &2))
537-
context = Enum.reduce(options, context, &with_option(&1, stack, &2, original))
538-
{dynamic(), context}
534+
{clauses, [[do: do_block] ++ options]} = Enum.split(clauses, -1)
535+
536+
{else_types, context} =
537+
Enum.reduce(clauses, {none(), original}, &with_clause(&1, stack, &2))
538+
539+
{do_result, context} = of_expr(do_block, expected, do_block, stack, context)
540+
context = Of.reset_vars(context, original)
541+
542+
{else_result, context} =
543+
case options do
544+
[else: clauses] ->
545+
info = {:with_else, else_types}
546+
of_clauses(clauses, [else_types], expected, info, stack, context, none())
547+
548+
[] ->
549+
{else_types, context}
550+
end
551+
552+
dynamic_unless_static({union(do_result, else_result), context}, stack)
539553
end)
540554
end
541555

@@ -664,19 +678,25 @@ defmodule Module.Types.Expr do
664678
## Comprehensions
665679

666680
defp for_clause({:<-, meta, [left, right]}, stack, context) do
667-
expr = {:<-, [type_check: :generator] ++ meta, [left, right]}
668-
{pattern, guards} = extract_head([left])
681+
meta = [type_check: :generator] ++ meta
682+
expr = {:<-, meta, [left, right]}
683+
{[pattern], guards} = extract_head([left])
669684

670685
# TODO: Extract the type from enumerable protocol
671686
{_type, context} =
672687
Apply.remote(Enumerable, :count, [right], term(), expr, stack, context, &of_expr/5)
673688

674-
Pattern.of_generator(pattern, guards, dynamic(), :for, expr, stack, context)
689+
{_tree, _precise, context} =
690+
Pattern.of_generator(pattern, guards, dynamic(), :for, expr, meta, stack, context)
691+
692+
context
675693
end
676694

677695
defp for_clause({:<<>>, _, [{:<-, meta, [left, right]}]} = expr, stack, context) do
678696
{right_type, context} = of_expr(right, bitstring(), expr, stack, context)
679-
context = Pattern.of_generator(left, [], bitstring(), :for, expr, stack, context)
697+
698+
{_tree, _precise?, context} =
699+
Pattern.of_generator(left, [], bitstring(), :for, expr, meta, stack, context)
680700

681701
if compatible?(right_type, bitstring()) do
682702
context
@@ -739,27 +759,31 @@ defmodule Module.Types.Expr do
739759

740760
## With
741761

742-
defp with_clause({:<-, _meta, [left, right]} = expr, stack, context) do
743-
{pattern, guards} = extract_head([left])
744-
{_type, context} = of_expr(right, @pending, right, stack, context)
745-
Pattern.of_generator(pattern, guards, dynamic(), :with, expr, stack, context)
746-
end
762+
defp with_clause({:<-, meta, [left, right]} = expr, stack, {else_types, context}) do
763+
{[pattern], guards} = extract_head([left])
747764

748-
defp with_clause(expr, stack, context) do
749-
{_type, context} = of_expr(expr, @pending, expr, stack, context)
750-
context
751-
end
765+
{type, context} =
766+
of_expr(right, term(), right, %{stack | reverse_arrow: :cache}, context)
752767

753-
defp with_option({:do, body}, stack, context, original) do
754-
{_type, context} = of_expr(body, @pending, body, stack, context)
755-
Of.reset_vars(context, original)
756-
end
768+
{trees, precise?, context} =
769+
Pattern.of_generator(pattern, guards, type, :with, expr, meta, stack, context)
757770

758-
defp with_option({:else, clauses}, stack, context, _original) do
759-
{_, context} =
760-
of_clauses(clauses, [dynamic()], @pending, :with_else, stack, context, none())
771+
if precise? do
772+
[pattern_type] = Pattern.of_domain(trees, stack, context)
761773

762-
context
774+
{_, refined_context} =
775+
of_expr(right, pattern_type, right, %{stack | reverse_arrow: :use}, context)
776+
777+
{union(difference(type, pattern_type), else_types),
778+
reset_warnings(refined_context, context)}
779+
else
780+
{union(type, else_types), context}
781+
end
782+
end
783+
784+
defp with_clause(expr, stack, {else_types, context}) do
785+
{_type, context} = of_expr(expr, term(), expr, stack, context)
786+
{else_types, context}
763787
end
764788

765789
## General helpers

0 commit comments

Comments
 (0)