Skip to content

Commit c758109

Browse files
committed
Tuple handling
1 parent bc54e5f commit c758109

File tree

2 files changed

+42
-21
lines changed

2 files changed

+42
-21
lines changed

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

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ defmodule Module.Types.Expr do
3030
versioned_vars: open_map()
3131
)
3232

33-
# This is used temporarily until reverse arrows are defined
33+
# An annotation for terms where the reverse arrow is not yet fully defined
3434
@pending term()
3535
@atom_true atom([true])
3636
@exception open_map(__struct__: atom(), __exception__: @atom_true)
@@ -90,28 +90,13 @@ defmodule Module.Types.Expr do
9090
end
9191

9292
# {left, right}
93-
# PENDING: here
94-
def of_expr({left, right}, _expected, expr, stack, context) do
95-
{left, context} = of_expr(left, @pending, expr, stack, context)
96-
{right, context} = of_expr(right, @pending, expr, stack, context)
97-
98-
if stack.mode == :traversal do
99-
{dynamic(), context}
100-
else
101-
{tuple([left, right]), context}
102-
end
93+
def of_expr({left, right}, expected, expr, stack, context) do
94+
of_tuple([left, right], expected, expr, stack, context)
10395
end
10496

10597
# {...}
106-
# PENDING: here
107-
def of_expr({:{}, _meta, exprs}, _expected, expr, stack, context) do
108-
{types, context} = Enum.map_reduce(exprs, context, &of_expr(&1, @pending, expr, stack, &2))
109-
110-
if stack.mode == :traversal do
111-
{dynamic(), context}
112-
else
113-
{tuple(types), context}
114-
end
98+
def of_expr({:{}, _meta, exprs}, expected, expr, stack, context) do
99+
of_tuple(exprs, expected, expr, stack, context)
115100
end
116101

117102
# <<...>>>
@@ -533,6 +518,32 @@ defmodule Module.Types.Expr do
533518
end
534519
end
535520

521+
## Tuples
522+
523+
defp of_tuple(elems, _expected, expr, %{mode: :traversal} = stack, context) do
524+
{_types, context} = Enum.map_reduce(elems, context, &of_expr(&1, term(), expr, stack, &2))
525+
{dynamic(), context}
526+
end
527+
528+
defp of_tuple(elems, expected, expr, stack, context) do
529+
of_tuple(elems, 0, [], expected, expr, stack, context)
530+
end
531+
532+
defp of_tuple([elem | elems], index, acc, expected, expr, stack, context) do
533+
expr_expected =
534+
case tuple_fetch(expected, index) do
535+
{_, type} -> type
536+
_ -> term()
537+
end
538+
539+
{type, context} = of_expr(elem, expr_expected, expr, stack, context)
540+
of_tuple(elems, index + 1, [type | acc], expected, expr, stack, context)
541+
end
542+
543+
defp of_tuple([], _index, acc, _expected, _expr, _stack, context) do
544+
{tuple(Enum.reverse(acc)), context}
545+
end
546+
536547
## Try
537548

538549
defp of_rescue(var, exceptions, body, expr, info, meta, stack, original) do

lib/elixir/test/elixir/module/types/expr_test.exs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,16 @@ defmodule Module.Types.ExprTest do
495495
assert typecheck!([x], {:ok, x}) == dynamic(tuple([atom([:ok]), term()]))
496496
end
497497

498+
test "inference" do
499+
assert typecheck!(
500+
[x, y],
501+
(
502+
{:ok, :error} = {x, y}
503+
{x, y}
504+
)
505+
) == dynamic(tuple([atom([:ok]), atom([:error])]))
506+
end
507+
498508
test "elem/2" do
499509
assert typecheck!(elem({:ok, 123}, 0)) == atom([:ok])
500510
assert typecheck!(elem({:ok, 123}, 1)) == integer()
@@ -618,7 +628,7 @@ defmodule Module.Types.ExprTest do
618628
"""
619629
end
620630

621-
test "duplicate/2" do
631+
test "Tuple.duplicate/2" do
622632
assert typecheck!(Tuple.duplicate(123, 0)) == tuple([])
623633
assert typecheck!(Tuple.duplicate(123, 1)) == tuple([integer()])
624634
assert typecheck!(Tuple.duplicate(123, 2)) == tuple([integer(), integer()])

0 commit comments

Comments
 (0)