Skip to content

Commit 8e9ece0

Browse files
committed
More typing improvements (#10333)
1. Move push_expr_stack to unify and keep helpers typing free 2. Add get_var! when accessing variables so it helps us find variables that have not been properly processed 3. Properly process all variables found missing on step 2
1 parent 54ba096 commit 8e9ece0

File tree

7 files changed

+38
-35
lines changed

7 files changed

+38
-35
lines changed

lib/elixir/lib/module/types.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ defmodule Module.Types do
5757
end
5858

5959
defp warnings_from_clause(args, guards, body, def_expr, stack, context) do
60-
head_stack = push_expr_stack(def_expr, stack)
60+
head_stack = Unify.push_expr_stack(def_expr, stack)
6161

6262
with {:ok, _types, context} <- Pattern.of_head(args, guards, head_stack, context),
6363
{:ok, _type, context} <- Expr.of_expr(body, stack, context) do

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,7 @@ defmodule Module.Types.Expr do
109109

110110
# var
111111
def of_expr(var, _stack, context) when is_var(var) do
112-
{type, context} = new_var(var, context)
113-
{:ok, type, context}
112+
{:ok, get_var!(var, context), context}
114113
end
115114

116115
# {left, right}
@@ -379,6 +378,13 @@ defmodule Module.Types.Expr do
379378
do: {:ok, context}
380379
end
381380

381+
defp for_clause({:<<>>, _, [{:<-, _, [pattern, expr]}]}, stack, context) do
382+
# TODO: the compiler guarantees pattern is a binary but we need to check expr is a binary
383+
with {:ok, _pattern_type, context} <- Pattern.of_pattern(pattern, stack, context),
384+
{:ok, _expr_type, context} <- of_expr(expr, stack, context),
385+
do: {:ok, context}
386+
end
387+
382388
defp for_clause(list, stack, context) when is_list(list) do
383389
reduce_ok(list, context, &for_option(&1, stack, &2))
384390
end

lib/elixir/lib/module/types/helpers.ex

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,6 @@ defmodule Module.Types.Helpers do
2525
def get_meta({_, meta, _}), do: meta
2626
def get_meta(_other), do: []
2727

28-
@doc """
29-
Push expression to stack.
30-
31-
The expression stack is used to give the context where a type variable
32-
was refined when show a type conflict error.
33-
"""
34-
def push_expr_stack(expr, stack) do
35-
%{stack | last_expr: expr}
36-
end
37-
3828
@doc """
3929
Like `Enum.reduce/3` but only continues while `fun` returns `{:ok, acc}`
4030
and stops on `{:error, reason}`.

lib/elixir/lib/module/types/of.ex

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ defmodule Module.Types.Of do
66
@prefix quote(do: ...)
77
@suffix quote(do: ...)
88

9-
alias Module.Types.Unify
109
alias Module.ParallelChecker
1110

1211
import Module.Types.Helpers
12+
import Module.Types.Unify
1313

1414
@doc """
1515
Handles open maps (with dynamic => dynamic).
@@ -40,7 +40,7 @@ defmodule Module.Types.Of do
4040
defp pairs_to_unions([{key, value}], _context), do: [{:required, key, value}]
4141

4242
defp pairs_to_unions(pairs, context) do
43-
case Enum.split_with(pairs, fn {key, _value} -> Unify.has_unbound_var?(key, context) end) do
43+
case Enum.split_with(pairs, fn {key, _value} -> has_unbound_var?(key, context) end) do
4444
{[], pairs} -> pairs_to_unions(pairs, [], context)
4545
{[_ | _], pairs} -> pairs_to_unions([{:dynamic, :dynamic} | pairs], [], context)
4646
end
@@ -57,17 +57,17 @@ defmodule Module.Types.Of do
5757
find_subtype_values(ahead, key, context) ++
5858
find_subtype_values(behind, key, context)
5959

60-
pairs_to_unions(ahead, [{key, Unify.to_union(all_values, context)} | behind], context)
60+
pairs_to_unions(ahead, [{key, to_union(all_values, context)} | behind], context)
6161
end
6262

6363
defp pairs_to_unions([], acc, context) do
6464
acc
65-
|> Enum.sort(&Unify.subtype?(elem(&1, 0), elem(&2, 0), context))
65+
|> Enum.sort(&subtype?(elem(&1, 0), elem(&2, 0), context))
6666
|> Enum.map(fn {key, value} -> {:required, key, value} end)
6767
end
6868

6969
defp find_subtype_values(pairs, key, context) do
70-
for {pair_key, pair_value} <- pairs, Unify.subtype?(pair_key, key, context), do: pair_value
70+
for {pair_key, pair_value} <- pairs, subtype?(pair_key, key, context), do: pair_value
7171
end
7272

7373
defp find_matching_values([{key, value} | ahead], key, acc, values) do
@@ -153,16 +153,11 @@ defmodule Module.Types.Of do
153153

154154
true ->
155155
with {:ok, type, context} <- fun.(expr, stack, context),
156-
{:ok, _type, context} <- Unify.unify(type, expected_type, stack, context),
156+
{:ok, _type, context} <- unify(type, expected_type, stack, context),
157157
do: {:ok, context}
158158
end
159159
end
160160

161-
# TODO: Remove this clause once we properly handle comprehensions
162-
defp binary_segment({:<-, _, _}, _stack, context, _fun) do
163-
{:ok, context}
164-
end
165-
166161
# Collect binary type specifiers,
167162
# from `<<pattern::integer-size(10)>>` collect `integer`
168163
defp collect_binary_specifier({:-, _meta, [left, right]}, fun) do

lib/elixir/lib/module/types/pattern.ex

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,8 @@ defmodule Module.Types.Pattern do
129129
end
130130

131131
# ^var
132-
def of_pattern({:^, _meta, [var]}, stack, context) do
133-
of_pattern(var, stack, context)
132+
def of_pattern({:^, _meta, [var]}, _stack, context) do
133+
{:ok, get_var!(var, context), context}
134134
end
135135

136136
# var
@@ -194,16 +194,11 @@ defmodule Module.Types.Pattern do
194194
end
195195
end
196196

197-
# %^var{...}
198-
def of_pattern({:%, meta1, [{:^, _meta2, [var]}, args]}, stack, context) do
199-
of_pattern({:%, meta1, [var, args]}, stack, context)
200-
end
201-
202-
# %var{...}
197+
# %var{...} and %^var{...}
203198
def of_pattern({:%, _meta1, [var, {:%{}, _meta2, args}]} = expr, stack, context) do
204199
stack = push_expr_stack(expr, stack)
205200

206-
with {var_type, context} = new_var(var, context),
201+
with {:ok, var_type, context} = of_pattern(var, stack, context),
207202
{:ok, _, context} <- unify(var_type, :atom, stack, context),
208203
{:ok, {:map, pairs}, context} <- Of.open_map(args, stack, context, &of_pattern/3) do
209204
{:ok, {:map, [{:required, {:atom, :__struct__}, var_type} | pairs]}, context}

lib/elixir/lib/module/types/unify.ex

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,23 @@ defmodule Module.Types.Unify do
301301

302302
defp error(type, reason, context), do: {:error, {type, reason, context}}
303303

304+
@doc """
305+
Push expression to stack.
306+
307+
The expression stack is used to give the context where a type variable
308+
was refined when show a type conflict error.
309+
"""
310+
def push_expr_stack(expr, stack) do
311+
%{stack | last_expr: expr}
312+
end
313+
314+
@doc """
315+
Gets a variable.
316+
"""
317+
def get_var!(var, context) do
318+
Map.fetch!(context.vars, var_name(var))
319+
end
320+
304321
@doc """
305322
Adds a variable to the typing context and returns its type variable.
306323
If the variable has already been added, return the existing type variable.

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@ defmodule Module.Types.MapTest do
297297
end
298298
end
299299

300-
test "with bound var keys" do
300+
test "map creation with bound var keys" do
301301
assert quoted_expr(
302302
[atom, bool, true = var],
303303
[is_atom(atom) and is_boolean(bool)],
@@ -341,7 +341,7 @@ defmodule Module.Types.MapTest do
341341
]}}
342342
end
343343

344-
test "with unbound var keys" do
344+
test "map creation with unbound var keys" do
345345
assert quoted_expr(
346346
[var, struct],
347347
(

0 commit comments

Comments
 (0)