Skip to content

Commit 3d5cd3c

Browse files
committed
Nested handling in case
1 parent fff0060 commit 3d5cd3c

2 files changed

Lines changed: 36 additions & 8 deletions

File tree

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

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -317,17 +317,25 @@ defmodule Module.Types.Expr do
317317
|> dynamic_unless_static(stack)
318318
end
319319

320-
def of_expr({:case, meta, [_case_expr, [{:do, _clauses}]]}, _expected, _expr, stack, context)
320+
def of_expr({:case, meta, [case_expr, [{:do, _clauses}]]}, expected, _expr, stack, context)
321321
when stack.reverse_arrow == :use do
322322
version = Keyword.fetch!(meta, :version)
323323
clauses = Map.fetch!(context.reverse_arrows, version)
324-
result = Enum.reduce(clauses, none(), &union(elem(&1, 1), &2))
325-
dynamic_unless_static({result, context}, stack)
324+
325+
case_expected =
326+
Enum.reduce(clauses, none(), fn {arg_type, body_type}, acc ->
327+
if disjoint?(body_type, expected) do
328+
acc
329+
else
330+
union(arg_type, acc)
331+
end
332+
end)
333+
334+
{_, context} = of_expr(case_expr, case_expected, case_expr, stack, context)
335+
{expected, context}
326336
end
327337

328338
def of_expr({:case, meta, [case_expr, [{:do, clauses}]]}, expected, _expr, stack, base_context) do
329-
version = Keyword.fetch!(meta, :version)
330-
331339
{case_type, context} =
332340
of_expr(case_expr, term(), case_expr, %{stack | reverse_arrow: :cache}, base_context)
333341

@@ -360,7 +368,7 @@ defmodule Module.Types.Expr do
360368
end
361369

362370
result_context =
363-
cache_arrows(version, stack, fn ->
371+
cache_arrows(meta, stack, fn ->
364372
of_clauses_fun(clauses, [case_type], info, stack, context, of_body, [], fn
365373
trees, body_type, context, acc ->
366374
[arg_type] = Pattern.of_domain(trees, stack, context)
@@ -767,10 +775,11 @@ defmodule Module.Types.Expr do
767775
defp dynamic_unless_static({_, _} = output, %{mode: :static}), do: output
768776
defp dynamic_unless_static({type, context}, %{mode: _}), do: {dynamic(type), context}
769777

770-
defp cache_arrows(_version, %{reverse_arrow: nil}, _fun), do: nil
778+
defp cache_arrows(_meta, %{reverse_arrow: nil}, _fun), do: nil
771779

772-
defp cache_arrows(version, %{reverse_arrow: :cache}, fun) do
780+
defp cache_arrows(meta, %{reverse_arrow: :cache}, fun) do
773781
{clauses, context} = fun.()
782+
version = Keyword.fetch!(meta, :version)
774783
context = put_in(context.reverse_arrows[version], clauses)
775784
result = Enum.reduce(clauses, none(), &union(elem(&1, 1), &2))
776785
{result, context}

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1931,6 +1931,25 @@ defmodule Module.Types.ExprTest do
19311931
)
19321932
end
19331933

1934+
test "refines nested expression type" do
1935+
assert typecheck!(
1936+
case (if x = System.get_env("HELLO") do
1937+
:do
1938+
else
1939+
:else
1940+
end) do
1941+
:do -> {:ok, x}
1942+
:else -> {:error, x}
1943+
end
1944+
) ==
1945+
dynamic(
1946+
union(
1947+
tuple([atom([:ok]), binary()]),
1948+
tuple([atom([:error]), atom([nil])])
1949+
)
1950+
)
1951+
end
1952+
19341953
test "discards warnings from refinements" do
19351954
assert {_, [_]} =
19361955
typediag!(

0 commit comments

Comments
 (0)