Skip to content

Commit 41fa1db

Browse files
committed
Do not recursively traverse vars to simplify checker, closes #12149
1 parent 887e218 commit 41fa1db

File tree

4 files changed

+16
-116
lines changed

4 files changed

+16
-116
lines changed

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

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -153,14 +153,18 @@ defmodule Module.Types.Expr do
153153
args_type = {:map, dynamic_value_pairs ++ [{:optional, :dynamic, :dynamic}]},
154154
{:ok, type, context} <- unify(map_type, args_type, stack, context) do
155155
# Retrieve map type and overwrite with the new value types from the map update
156-
{:map, pairs} = resolve_var(type, context)
156+
case resolve_var(type, context) do
157+
{:map, pairs} ->
158+
updated_pairs =
159+
Enum.reduce(arg_pairs, pairs, fn {:required, key, value}, pairs ->
160+
List.keyreplace(pairs, key, 1, {:required, key, value})
161+
end)
157162

158-
updated_pairs =
159-
Enum.reduce(arg_pairs, pairs, fn {:required, key, value}, pairs ->
160-
List.keyreplace(pairs, key, 1, {:required, key, value})
161-
end)
163+
{:ok, {:map, updated_pairs}, context}
162164

163-
{:ok, {:map, updated_pairs}, context}
165+
_ ->
166+
{:ok, :dynamic, context}
167+
end
164168
end
165169
end
166170

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

Lines changed: 6 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -128,35 +128,10 @@ defmodule Module.Types.Unify do
128128
{:ok, {:var, var}, context}
129129
end
130130

131-
%{^var => {:var, new_var} = var_type} ->
132-
unify_result =
133-
cond do
134-
recursive_type?(var_type, [], context) ->
135-
{:ok, var_type, put_in(context.types[var], var_type)}
136-
137-
var_source? ->
138-
unify(var_type, type, stack, context)
139-
140-
true ->
141-
unify(type, var_type, stack, context)
142-
end
143-
144-
case unify_result do
145-
{:ok, type, context} ->
146-
{:ok, type, context}
147-
148-
{:error, {type, reason, %{traces: error_traces} = error_context}} ->
149-
old_var_traces = Map.get(context.traces, new_var, [])
150-
new_var_traces = Map.get(error_traces, new_var, [])
151-
add_var_traces = Enum.drop(new_var_traces, -length(old_var_traces))
152-
153-
error_traces =
154-
error_traces
155-
|> Map.update(var, add_var_traces, &(add_var_traces ++ &1))
156-
|> Map.put(new_var, old_var_traces)
157-
158-
{:error, {type, reason, %{error_context | traces: error_traces}}}
159-
end
131+
%{^var => {:var, _} = var_type} ->
132+
# Do not recursively traverse type vars for now
133+
# to avoid pathological cases related to performance.
134+
{:ok, var_type, context}
160135

161136
%{^var => var_type} ->
162137
# Only add trace if the variable wasn't already "expanded"
@@ -384,11 +359,11 @@ defmodule Module.Types.Unify do
384359
end
385360

386361
@doc """
387-
Resolves a variable raising if it is unbound.
362+
Maybe resolves a variable.
388363
"""
389364
def resolve_var({:var, var}, context) do
390365
case context.types do
391-
%{^var => :unbound} -> raise "cannot resolve unbound var"
366+
%{^var => :unbound} -> {:var, var}
392367
%{^var => type} -> resolve_var(type, context)
393368
end
394369
end

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

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,6 @@ defmodule Module.Types.PatternTest do
222222

223223
assert quoted_pattern(x = y = 123) == {:ok, :integer}
224224
assert quoted_pattern(x = 123 = y) == {:ok, :integer}
225-
assert quoted_pattern(123 = x = y) == {:ok, :integer}
226225

227226
assert {:error, {:unable_unify, {{:tuple, 1, [var: 0]}, {:var, 0}, _}}} =
228227
quoted_pattern({x} = x)
@@ -296,15 +295,6 @@ defmodule Module.Types.PatternTest do
296295

297296
assert quoted_head([x], [is_atom(x) > :foo]) == {:ok, [var: 0]}
298297

299-
assert quoted_head([x, x = y, y = z], [is_atom(x)]) ==
300-
{:ok, [:atom, :atom, :atom]}
301-
302-
assert quoted_head([x = y, y, y = z], [is_atom(y)]) ==
303-
{:ok, [:atom, :atom, :atom]}
304-
305-
assert quoted_head([x = y, y = z, z], [is_atom(z)]) ==
306-
{:ok, [:atom, :atom, :atom]}
307-
308298
assert quoted_head([x, y], [is_atom(x) or is_integer(y)]) ==
309299
{:ok, [{:var, 0}, {:var, 1}]}
310300

@@ -320,15 +310,6 @@ defmodule Module.Types.PatternTest do
320310
assert quoted_head([x, y], [is_atom(y) or is_integer(y)]) ==
321311
{:ok, [{:var, 0}, {:union, [:atom, :integer]}]}
322312

323-
assert quoted_head([x = y], [is_atom(y) or is_integer(y)]) ==
324-
{:ok, [{:union, [:atom, :integer]}]}
325-
326-
assert quoted_head([x = y], [is_atom(x) or is_integer(x)]) ==
327-
{:ok, [{:union, [:atom, :integer]}]}
328-
329-
assert quoted_head([x = y], [is_atom(x) or is_integer(x)]) ==
330-
{:ok, [{:union, [:atom, :integer]}]}
331-
332313
assert quoted_head([x], [true == false or is_integer(x)]) ==
333314
{:ok, [var: 0]}
334315

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

Lines changed: 0 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -187,36 +187,6 @@ defmodule Module.Types.TypesTest do
187187
"""
188188
end
189189

190-
test "warns on guards with multiple variables" do
191-
string = warning([x = y], [is_integer(x) and is_binary(y)], {x, y})
192-
193-
assert string == """
194-
incompatible types:
195-
196-
integer() !~ binary()
197-
198-
in expression:
199-
200-
# types_test.ex:1
201-
is_binary(y)
202-
203-
where "x" was given the same type as "y" in:
204-
205-
# types_test.ex:1
206-
x = y
207-
208-
where "y" was given the type integer() in:
209-
210-
# types_test.ex:1
211-
is_integer(x)
212-
213-
where "y" was given the type binary() in:
214-
215-
# types_test.ex:1
216-
is_binary(y)
217-
"""
218-
end
219-
220190
test "warns on guards from cases unless generated" do
221191
string =
222192
warning(
@@ -243,36 +213,6 @@ defmodule Module.Types.TypesTest do
243213
assert string == :none
244214
end
245215

246-
test "only show relevant traces in warning" do
247-
string = warning([x = y, z], [is_integer(x) and is_binary(y) and is_boolean(z)], {x, y, z})
248-
249-
assert string == """
250-
incompatible types:
251-
252-
integer() !~ binary()
253-
254-
in expression:
255-
256-
# types_test.ex:1
257-
is_binary(y)
258-
259-
where "x" was given the same type as "y" in:
260-
261-
# types_test.ex:1
262-
x = y
263-
264-
where "y" was given the type integer() in:
265-
266-
# types_test.ex:1
267-
is_integer(x)
268-
269-
where "y" was given the type binary() in:
270-
271-
# types_test.ex:1
272-
is_binary(y)
273-
"""
274-
end
275-
276216
test "check body" do
277217
string = warning([x], [is_integer(x)], :foo = x)
278218

0 commit comments

Comments
 (0)