Skip to content

Commit

Permalink
Tuple handling
Browse files Browse the repository at this point in the history
  • Loading branch information
josevalim committed Jan 7, 2025
1 parent bc54e5f commit c758109
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 21 deletions.
51 changes: 31 additions & 20 deletions lib/elixir/lib/module/types/expr.ex
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ defmodule Module.Types.Expr do
versioned_vars: open_map()
)

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

# {left, right}
# PENDING: here
def of_expr({left, right}, _expected, expr, stack, context) do
{left, context} = of_expr(left, @pending, expr, stack, context)
{right, context} = of_expr(right, @pending, expr, stack, context)

if stack.mode == :traversal do
{dynamic(), context}
else
{tuple([left, right]), context}
end
def of_expr({left, right}, expected, expr, stack, context) do
of_tuple([left, right], expected, expr, stack, context)
end

# {...}
# PENDING: here
def of_expr({:{}, _meta, exprs}, _expected, expr, stack, context) do
{types, context} = Enum.map_reduce(exprs, context, &of_expr(&1, @pending, expr, stack, &2))

if stack.mode == :traversal do
{dynamic(), context}
else
{tuple(types), context}
end
def of_expr({:{}, _meta, exprs}, expected, expr, stack, context) do
of_tuple(exprs, expected, expr, stack, context)
end

# <<...>>>
Expand Down Expand Up @@ -533,6 +518,32 @@ defmodule Module.Types.Expr do
end
end

## Tuples

defp of_tuple(elems, _expected, expr, %{mode: :traversal} = stack, context) do
{_types, context} = Enum.map_reduce(elems, context, &of_expr(&1, term(), expr, stack, &2))
{dynamic(), context}
end

defp of_tuple(elems, expected, expr, stack, context) do
of_tuple(elems, 0, [], expected, expr, stack, context)
end

defp of_tuple([elem | elems], index, acc, expected, expr, stack, context) do
expr_expected =
case tuple_fetch(expected, index) do
{_, type} -> type
_ -> term()
end

{type, context} = of_expr(elem, expr_expected, expr, stack, context)
of_tuple(elems, index + 1, [type | acc], expected, expr, stack, context)
end

defp of_tuple([], _index, acc, _expected, _expr, _stack, context) do
{tuple(Enum.reverse(acc)), context}
end

## Try

defp of_rescue(var, exceptions, body, expr, info, meta, stack, original) do
Expand Down
12 changes: 11 additions & 1 deletion lib/elixir/test/elixir/module/types/expr_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,16 @@ defmodule Module.Types.ExprTest do
assert typecheck!([x], {:ok, x}) == dynamic(tuple([atom([:ok]), term()]))
end

test "inference" do
assert typecheck!(
[x, y],
(
{:ok, :error} = {x, y}
{x, y}
)
) == dynamic(tuple([atom([:ok]), atom([:error])]))
end

test "elem/2" do
assert typecheck!(elem({:ok, 123}, 0)) == atom([:ok])
assert typecheck!(elem({:ok, 123}, 1)) == integer()
Expand Down Expand Up @@ -618,7 +628,7 @@ defmodule Module.Types.ExprTest do
"""
end

test "duplicate/2" do
test "Tuple.duplicate/2" do
assert typecheck!(Tuple.duplicate(123, 0)) == tuple([])
assert typecheck!(Tuple.duplicate(123, 1)) == tuple([integer()])
assert typecheck!(Tuple.duplicate(123, 2)) == tuple([integer(), integer()])
Expand Down

0 comments on commit c758109

Please sign in to comment.