Skip to content

Commit a4e7a2d

Browse files
committed
Move variable deps and paths to context for reuse
1 parent ad67ac0 commit a4e7a2d

2 files changed

Lines changed: 58 additions & 61 deletions

File tree

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,24 @@ defmodule Module.Types.Of do
5959
type: term(),
6060
name: var_name,
6161
context: var_context,
62-
off_traces: []
62+
off_traces: [],
63+
paths: [],
64+
deps: %{}
6365
}
6466

6567
%{context | vars: Map.put(vars, version, data)}
6668
end
6769
end
6870

71+
@doc """
72+
Tracks metadata about variables dependencies and paths.
73+
"""
74+
def track_var(version, new_deps, new_paths, context) do
75+
update_in(context.vars[version], fn %{paths: paths, deps: deps} = data ->
76+
%{data | paths: new_paths ++ paths, deps: Enum.reduce(new_deps, deps, &Map.put(&2, &1, []))}
77+
end)
78+
end
79+
6980
@doc """
7081
Refines a variable that already exists (in a body).
7182

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

Lines changed: 46 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,9 @@ defmodule Module.Types.Pattern do
5656
end
5757

5858
defp of_pattern_args(patterns, expected, tag, stack, context) do
59-
context = init_match_info(context)
59+
context = init_pattern_info(context)
6060
{trees, context} = of_pattern_args_zip(patterns, expected, 0, [], stack, context)
61-
{pattern_info, context} = pop_match_info(context)
61+
{pattern_info, context} = pop_pattern_info(context)
6262

6363
context =
6464
case of_pattern_intersect(trees, 0, [], pattern_info, tag, stack, context) do
@@ -82,9 +82,9 @@ defmodule Module.Types.Pattern do
8282
Handles the match operator.
8383
"""
8484
def of_match(pattern, expected_fun, expr, stack, context) do
85-
context = init_match_info(context)
85+
context = init_pattern_info(context)
8686
{tree, context} = of_pattern(pattern, [%{root: {:arg, 0}, expr: expr}], stack, context)
87-
{pattern_info, context} = pop_match_info(context)
87+
{pattern_info, context} = pop_pattern_info(context)
8888
{expected, context} = expected_fun.(of_pattern_tree(tree, context), context)
8989

9090
args = [{tree, expected, expr}]
@@ -100,9 +100,9 @@ defmodule Module.Types.Pattern do
100100
Handles matches in generators.
101101
"""
102102
def of_generator(pattern, guards, expected, tag, expr, stack, context) do
103-
context = init_match_info(context)
103+
context = init_pattern_info(context)
104104
{tree, context} = of_pattern(pattern, [%{root: {:arg, 0}, expr: expr}], stack, context)
105-
{pattern_info, context} = pop_match_info(context)
105+
{pattern_info, context} = pop_pattern_info(context)
106106
args = [{tree, expected, pattern}]
107107

108108
context =
@@ -128,11 +128,11 @@ defmodule Module.Types.Pattern do
128128
end
129129

130130
defp of_pattern_intersect([], _index, acc, pattern_info, tag, stack, context) do
131-
{args_paths, vars_paths, vars_deps} = pattern_info
131+
%{vars: context_vars} = context
132132
types = Enum.reverse(acc)
133133

134134
try do
135-
args_paths
135+
pattern_info
136136
|> Enum.reverse()
137137
|> Enum.reduce({%{}, context}, fn {version, node}, {changed, context} ->
138138
%{var: var, expr: expr, root: root, path: path} = node
@@ -162,56 +162,54 @@ defmodule Module.Types.Pattern do
162162
throw(badpattern_error(expr, index, tag, stack, context))
163163
end
164164

165-
{Map.merge(changed, Map.get(vars_deps, version, %{})), context}
165+
%{^version => %{deps: deps}} = context_vars
166+
{Map.merge(changed, deps), context}
166167
end)
167168
catch
168169
context -> {:error, error_vars(pattern_info, context)}
169170
else
170171
{changed, context} ->
171-
{:ok, types, of_pattern_recur(changed, vars_paths, vars_deps, stack, context)}
172+
{:ok, types, of_pattern_recur(changed, stack, context)}
172173
end
173174
end
174175

175-
defp of_pattern_recur(changed, _vars_paths, _vars_deps, _stack, context)
176+
defp of_pattern_recur(changed, _stack, context)
176177
when changed == %{} do
177178
context
178179
end
179180

180-
defp of_pattern_recur(previous_changed, vars_paths, vars_deps, stack, context) do
181+
defp of_pattern_recur(previous_changed, stack, context) do
181182
{changed, context} =
182183
previous_changed
183184
|> Map.keys()
184185
|> Enum.reduce({%{}, context}, fn version, {changed, context} ->
185-
{var_changed?, context} = of_pattern_recur_var(vars_paths, version, stack, context)
186-
187-
case var_changed? do
188-
false -> {changed, context}
189-
true -> {Map.merge(changed, Map.get(vars_deps, version, %{})), context}
190-
end
186+
{new_deps, context} = of_pattern_recur_var(version, stack, context)
187+
{Map.merge(changed, new_deps), context}
191188
end)
192189

193-
of_pattern_recur(changed, vars_paths, vars_deps, stack, context)
190+
of_pattern_recur(changed, stack, context)
194191
end
195192

196-
defp of_pattern_recur_var(vars_paths, version, stack, context) do
197-
paths = Map.get(vars_paths, version, [])
198-
193+
defp of_pattern_recur_var(version, stack, context) do
199194
case context.vars do
200-
%{^version => %{type: old_type} = data} when not is_map_key(data, :errored) ->
195+
%{^version => %{type: old_type, deps: deps, paths: paths} = data}
196+
when not is_map_key(data, :errored) ->
201197
try do
202-
Enum.reduce(paths, {false, context}, fn
203-
%{var: var, expr: expr, root: root, path: path}, {var_changed?, context} ->
198+
Enum.reduce(paths, {%{}, context}, fn
199+
%{var: var, expr: expr, root: root, path: path}, {new_deps, context} ->
204200
actual = of_pattern_tree(root, context)
205201

206202
case of_pattern_var(path, actual, context) do
207203
{:ok, new_type} ->
208-
# Optimization: if current type is already a subtype, there is nothing to refine.
204+
# Optimization: if current type is already a subtype,
205+
# there is nothing to refine
209206
if old_type != term() and subtype?(old_type, new_type) do
210-
{var_changed?, context}
207+
{new_deps, context}
211208
else
212209
case Of.refine_head_var(var, new_type, expr, stack, context) do
213210
{:ok, _type, context} ->
214-
{true, context}
211+
# Return the actual deps to be recomputed
212+
{deps, context}
215213

216214
{:error, _old_type, error_context} ->
217215
if match_error?(var, new_type) do
@@ -227,16 +225,16 @@ defmodule Module.Types.Pattern do
227225
end
228226
end)
229227
catch
230-
context -> {false, context}
228+
context -> {%{}, context}
231229
end
232230

233231
_ ->
234-
{false, context}
232+
{%{}, context}
235233
end
236234
end
237235

238-
defp error_vars({args_paths, _vars_paths, _vars_deps}, context) do
239-
Enum.reduce(args_paths, context, fn {_version, %{var: var}}, context ->
236+
defp error_vars(pattern_info, context) do
237+
Enum.reduce(pattern_info, context, fn {_version, %{var: var}}, context ->
240238
Of.error_var(var, context)
241239
end)
242240
end
@@ -275,16 +273,12 @@ defmodule Module.Types.Pattern do
275273
end
276274
end
277275

278-
# pattern_info stores the variables defined in patterns,
279-
# additional information about the number of variables in
280-
# arguments and list heads, and a counter used to compute
281-
# the number of list heads.
282-
# TODO: Move vars_deps and vars_paths into context.vars.
283-
defp init_match_info(context) do
284-
%{context | pattern_info: {[], %{}, %{}}}
276+
# pattern_info stores the paths defined from patterns.
277+
defp init_pattern_info(context) do
278+
%{context | pattern_info: []}
285279
end
286280

287-
defp pop_match_info(%{pattern_info: pattern_info} = context) do
281+
defp pop_pattern_info(%{pattern_info: pattern_info} = context) do
288282
{pattern_info, %{context | pattern_info: nil}}
289283
end
290284

@@ -623,30 +617,22 @@ defmodule Module.Types.Pattern do
623617
defp is_versioned_var(_), do: false
624618

625619
defp of_var(var, version, reverse_path, context) do
626-
context = Of.declare_var(var, context)
627-
{args_paths, vars_paths, vars_deps} = context.pattern_info
628620
[%{root: root, expr: expr} | path] = Enum.reverse(reverse_path)
629621
node = %{root: root, var: var, expr: expr, path: path}
622+
context = Of.declare_var(var, context)
623+
context = %{context | pattern_info: [{version, node} | context.pattern_info]}
630624

631-
pattern_info =
632-
case root do
633-
{:arg, _} ->
634-
{[{version, node} | args_paths], vars_paths, vars_deps}
635-
636-
{:var, other} ->
637-
paths = [node | Map.get(vars_paths, version, [])]
638-
vars_paths = Map.put(vars_paths, version, paths)
639-
vars_deps = Map.update(vars_deps, version, %{other => []}, &Map.put(&1, other, []))
640-
vars_deps = Map.update(vars_deps, other, %{version => []}, &Map.put(&1, version, []))
641-
{[{version, node} | args_paths], vars_paths, vars_deps}
642-
643-
_ ->
644-
paths = [node | Map.get(vars_paths, version, [])]
645-
vars_paths = Map.put(vars_paths, version, paths)
646-
{[{version, node} | args_paths], vars_paths, vars_deps}
647-
end
625+
case root do
626+
{:arg, _} ->
627+
context
628+
629+
{:var, other} ->
630+
context = Of.track_var(version, [other], [node], context)
631+
Of.track_var(other, [version], [], context)
648632

649-
%{context | pattern_info: pattern_info}
633+
_ ->
634+
Of.track_var(version, [], [node], context)
635+
end
650636
end
651637

652638
defp of_open_map(args, static, dynamic, path, stack, context) do

0 commit comments

Comments
 (0)