Skip to content

Commit 453a435

Browse files
committed
Optimize vars recomputation in patterns
1 parent d267911 commit 453a435

File tree

1 file changed

+23
-29
lines changed

1 file changed

+23
-29
lines changed

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

Lines changed: 23 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -114,26 +114,37 @@ defmodule Module.Types.Pattern do
114114
{type, context}
115115
end
116116

117+
defp all_single_path?(vars, info, index) do
118+
info
119+
|> Map.get(index, [])
120+
|> Enum.all?(fn version -> match?([_], Map.fetch!(vars, version)) end)
121+
end
122+
117123
defp of_pattern_recur(types, tag, stack, context, callback) do
118-
%{pattern_info: {pattern_vars, pattern_info, _counter}} = context
124+
%{pattern_info: {vars, info, _counter}} = context
119125
context = nilify_pattern_info(context)
120-
pattern_vars = Map.to_list(pattern_vars)
121126
changed = :lists.seq(0, length(types) - 1)
122127

128+
# If all variables in a given index have a single path,
129+
# then there are no changes to propagate
130+
unchangeable = for index <- changed, all_single_path?(vars, info, index), do: index
131+
132+
vars = Map.to_list(vars)
133+
123134
try do
124135
case callback.(types, changed, context) do
125136
{:ok, types, context} ->
126-
of_pattern_recur(types, pattern_vars, pattern_info, tag, stack, context, callback)
137+
of_pattern_recur(types, unchangeable, vars, info, tag, stack, context, callback)
127138

128139
{:error, context} ->
129-
{types, error_vars(pattern_vars, context)}
140+
{types, error_vars(vars, context)}
130141
end
131142
catch
132-
{types, context} -> {types, error_vars(pattern_vars, context)}
143+
{types, context} -> {types, error_vars(vars, context)}
133144
end
134145
end
135146

136-
defp of_pattern_recur(types, vars, info, tag, stack, context, callback) do
147+
defp of_pattern_recur(types, unchangeable, vars, info, tag, stack, context, callback) do
137148
{changed, context} =
138149
Enum.reduce(vars, {[], context}, fn {version, paths}, {changed, context} ->
139150
{var_changed?, context} =
@@ -165,23 +176,12 @@ defmodule Module.Types.Pattern do
165176
{changed, context}
166177

167178
true ->
168-
case paths do
169-
# A single change, check if there are other variables in this index.
170-
[[_var, {:arg, index, _} | _]] ->
171-
case info do
172-
%{^index => true} -> {[index | changed], context}
173-
%{^index => false} -> {changed, context}
174-
end
175-
176-
# Several changes, we have to recompute all indexes.
177-
_ ->
178-
var_changed = Enum.map(paths, fn [_var, {:arg, index, _} | _] -> index end)
179-
{var_changed ++ changed, context}
180-
end
179+
var_changed = Enum.map(paths, fn [_var, {:arg, index, _} | _] -> index end)
180+
{var_changed ++ changed, context}
181181
end
182182
end)
183183

184-
case :lists.usort(changed) do
184+
case :lists.usort(changed) -- unchangeable do
185185
[] ->
186186
{types, context}
187187

@@ -192,7 +192,7 @@ defmodule Module.Types.Pattern do
192192
{types, context}
193193

194194
{:ok, types, context} ->
195-
of_pattern_recur(types, vars, info, tag, stack, context, callback)
195+
of_pattern_recur(types, unchangeable, vars, info, tag, stack, context, callback)
196196

197197
{:error, context} ->
198198
{types, error_vars(vars, context)}
@@ -487,14 +487,8 @@ defmodule Module.Types.Pattern do
487487
paths = [[var | path] | Map.get(vars, version, [])]
488488
vars = Map.put(vars, version, paths)
489489

490-
# Our goal here is to compute if an argument has more than one variable.
491-
info =
492-
case info do
493-
%{^arg => false} -> %{info | arg => true}
494-
%{^arg => true} -> info
495-
%{} -> Map.put(info, arg, false)
496-
end
497-
490+
# Stores all variables used at any given argument
491+
info = Map.update(info, arg, [version], &[version | &1])
498492
{{:var, version}, %{context | pattern_info: {vars, info, counter}}}
499493
end
500494

0 commit comments

Comments
 (0)