Skip to content

Commit 498ac84

Browse files
author
José Valim
committed
Record rewriting works with case clauses
1 parent d4c2436 commit 498ac84

File tree

3 files changed

+117
-22
lines changed

3 files changed

+117
-22
lines changed

lib/elixir/lib/kernel/record_rewriter.ex

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,28 @@ defmodule Kernel.RecordRewriter do
121121
end
122122
end
123123

124+
defp optimize_expr({ :case, line, expr, clauses }, dict) do
125+
{ expr, dict, _ } = optimize_expr(expr, dict)
126+
tuples = lc clause inlist clauses, do: optimize_clause(clause, dict)
127+
clauses = lc { clause, _, _ } inlist tuples, do: clause
128+
dict = join_dict(tuples)
129+
res = join_result(tuples)
130+
{ { :case, line, expr, clauses }, dict, res }
131+
end
132+
133+
defp optimize_expr({ :fun, line, { :function, module, name, arity } }, dict) do
134+
{ module, dict, _ } = optimize_expr(module, dict)
135+
{ name, dict, _ } = optimize_expr(name, dict)
136+
{ arity, dict, _ } = optimize_expr(arity, dict)
137+
{ { :fun, line, { :function, module, name, arity } }, dict, nil }
138+
end
139+
140+
defp optimize_expr({ :fun, line, { :clauses, clauses } }, dict) do
141+
tuples = lc clause inlist clauses, do: optimize_clause(clause, dict)
142+
clauses = lc { clause, _, _ } inlist tuples, do: clause
143+
{ { :fun, line, { :clauses, clauses } }, dict, nil }
144+
end
145+
124146
defp optimize_expr({ comprehension, line, expr, args }, dict) when comprehension in [:lc, :bc] do
125147
{ args, new_dict } = optimize_args(args, dict)
126148
{ expr, _, _ } = optimize_expr(expr, new_dict)
@@ -133,11 +155,11 @@ defmodule Kernel.RecordRewriter do
133155
{ { generate, line, left, right }, dict, nil }
134156
end
135157

136-
defp optimize_expr(other, dict) when elem(other, 0) in [:string, :atom, :integer, :float, :nil] do
158+
defp optimize_expr(other, dict) when elem(other, 0) in [:string, :atom, :integer, :float, :nil, :fun] do
137159
{ other, dict, nil }
138160
end
139161

140-
## Match related
162+
## Helpers
141163

142164
defp optimize_tuple_args(args, dict) do
143165
{ final_args, { final_dict, final_acc } } =
@@ -204,6 +226,43 @@ defmodule Kernel.RecordRewriter do
204226
vars
205227
end
206228

229+
defp join_dict([{ _, dict, _ }|t]) do
230+
join_dict(t, dict)
231+
end
232+
233+
defp join_dict([{ _, dict, _ }|t], other) do
234+
other = Enum.reduce other, other, fn
235+
{ key, value }, acc ->
236+
case :orddict.find(key, dict) do
237+
{ :ok, ^value } -> acc
238+
{ :ok, _ } -> :orddict.store(key, nil, acc)
239+
:error -> :orddict.erase(key, acc)
240+
end
241+
end
242+
243+
join_dict(t, other)
244+
end
245+
246+
defp join_dict([], other) do
247+
other
248+
end
249+
250+
defp join_result([{ _, _, res }|t]) do
251+
join_result(t, res)
252+
end
253+
254+
defp join_result([{ _, _, res }|t], res) do
255+
join_result(t, res)
256+
end
257+
258+
defp join_result([{ _, _, _ }|_], _res) do
259+
nil
260+
end
261+
262+
defp join_result([], res) do
263+
res
264+
end
265+
207266
## Record helpers
208267

209268
# TODO: Implement proper record check

lib/elixir/src/elixir_clauses.erl

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -110,17 +110,19 @@ do_match(Line, DecoupledClauses, S) ->
110110

111111
% Now get all the variables defined inside each clause
112112
CV = lists:reverse(RawCV),
113-
NewVars = lists:umerge([lists:sort(orddict:fetch_keys(X)) || X <- CV]),
114113

115-
case NewVars of
114+
AllVars = lists:umerge([orddict:fetch_keys(X) || X <- CV]),
115+
SharedVars = ordsets:intersection(CV),
116+
117+
case AllVars of
116118
[] -> { TClauses, TS };
117119
_ ->
118120
% Create a new scope that contains a list of all variables
119121
% defined inside all the clauses. It returns this new scope and
120122
% a list of tuples where the first element is the variable name,
121123
% the second one is the new pointer to the variable and the third
122124
% is the old pointer.
123-
{ FinalVars, FS } = lists:mapfoldl(fun normalize_vars/2, TS, NewVars),
125+
{ FinalVars, FS } = lists:mapfoldl(fun(X, Acc) -> normalize_vars(X, SharedVars, Acc) end, TS, AllVars),
124126

125127
% Defines a tuple that will be used as left side of the match operator
126128
LeftVars = [{var, Line, NewValue} || { _, _, NewValue, _ } <- FinalVars],
@@ -204,11 +206,18 @@ has_match_tuple(_) -> false.
204206

205207
% Normalize the given var checking its existence in the scope var dictionary.
206208

207-
normalize_vars({ Var, Kind } = Key, #elixir_scope{vars=Vars,clause_vars=ClauseVars} = S) ->
208-
{ { _, _, NewValue }, S1 } = if
209-
(Kind == quoted) or (S#elixir_scope.noname) -> elixir_scope:build_erl_var(0, S);
210-
true -> elixir_scope:build_erl_var(0, Var, "_@" ++ atom_to_list(Var), S)
211-
end,
209+
normalize_vars({ Var, Kind } = Key, Shared, #elixir_scope{vars=Vars,clause_vars=ClauseVars} = S) ->
210+
{ NewValue, S1 } =
211+
case orddict:find(Key, Shared) of
212+
{ ok, SharedValue } ->
213+
{ SharedValue, S };
214+
error ->
215+
{ { _, _, ErlValue }, ErlS } = case (Kind == quoted) or (S#elixir_scope.noname) of
216+
true -> elixir_scope:build_erl_var(0, S);
217+
false -> elixir_scope:build_erl_var(0, Var, "_@" ++ atom_to_list(Var), S)
218+
end,
219+
{ ErlValue, ErlS }
220+
end,
212221

213222
S2 = S1#elixir_scope{
214223
vars=orddict:store(Key, NewValue, Vars),

lib/elixir/test/elixir/kernel/record_rewriter_test.exs

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,33 +25,33 @@ defmodule Kernel.RecordRewriterTest do
2525
end
2626

2727
test "with left-side arg match" do
28-
clause = clause(fn(arg = Macro.Env[]) -> :foo end)
29-
assert optimize_clause(clause) == { clause, [arg: Macro.Env], nil }
28+
clause = clause(fn(x = Macro.Env[]) -> :foo end)
29+
assert optimize_clause(clause) == { clause, [x: Macro.Env], nil }
3030
end
3131

3232
test "with right-side arg match" do
33-
clause = clause(fn(Macro.Env[] = arg) -> :foo end)
34-
assert optimize_clause(clause) == { clause, [arg: Macro.Env], nil }
33+
clause = clause(fn(Macro.Env[] = x) -> :foo end)
34+
assert optimize_clause(clause) == { clause, [x: Macro.Env], nil }
3535
end
3636

3737
test "with nested left-side arg match" do
38-
clause = clause(fn(arg = other = Macro.Env[]) -> :foo end)
39-
assert optimize_clause(clause) == { clause, [arg: Macro.Env, other: Macro.Env], nil }
38+
clause = clause(fn(x = y = Macro.Env[]) -> :foo end)
39+
assert optimize_clause(clause) == { clause, [x: Macro.Env, y: Macro.Env], nil }
4040
end
4141

4242
test "with nested right-side arg match" do
43-
clause = clause(fn(Macro.Env[] = arg = other) -> :foo end)
44-
assert optimize_clause(clause) == { clause, [arg: Macro.Env, other: Macro.Env], nil }
43+
clause = clause(fn(Macro.Env[] = x = y) -> :foo end)
44+
assert optimize_clause(clause) == { clause, [x: Macro.Env, y: Macro.Env], nil }
4545
end
4646

4747
test "with deep left-side arg match" do
48-
clause = clause(fn({ x, arg = Macro.Env[] }) -> :foo end)
49-
assert optimize_clause(clause) == { clause, [arg: Macro.Env], nil }
48+
clause = clause(fn({ x, y = Macro.Env[] }) -> :foo end)
49+
assert optimize_clause(clause) == { clause, [y: Macro.Env], nil }
5050
end
5151

5252
test "with deep right-side arg match" do
53-
clause = clause(fn({ x, Macro.Env[] = arg }) -> :foo end)
54-
assert optimize_clause(clause) == { clause, [arg: Macro.Env], nil }
53+
clause = clause(fn({ x, Macro.Env[] = y }) -> :foo end)
54+
assert optimize_clause(clause) == { clause, [y: Macro.Env], nil }
5555
end
5656

5757
test "with tuple match" do
@@ -121,4 +121,31 @@ defmodule Kernel.RecordRewriterTest do
121121
clause = clause(fn -> bc x = Macro.Env[] inbits sample, do: <<x>> end)
122122
assert optimize_clause(clause) == { clause, [], nil }
123123
end
124+
125+
test "inside function retrieval" do
126+
clause = clause(fn -> function(x = Macro.Env[], y, z) end)
127+
assert optimize_clause(clause) == { clause, [x: Macro.Env], nil }
128+
end
129+
130+
test "inside anonymous function" do
131+
clause = clause(fn -> fn (x = Macro.Env[]) -> x end end)
132+
assert optimize_clause(clause) == { clause, [], nil }
133+
end
134+
135+
test "inside case" do
136+
clause = clause(fn -> case x = Macro.Env[] do _ -> x end end)
137+
assert optimize_clause(clause) == { clause, [x: Macro.Env], { Macro.Env, nil } }
138+
139+
clause = clause(fn -> case something do x = Macro.Env[] -> x; Macro.Env[] = x -> x end end)
140+
assert optimize_clause(clause) == { clause, [x: Macro.Env], { Macro.Env, nil } }
141+
142+
clause = clause(fn -> case something do x = Macro.Env[] -> x; Macro.Env[] = y -> y end end)
143+
assert optimize_clause(clause) == { clause, [], { Macro.Env, nil } }
144+
145+
clause = clause(fn -> case something do 1 -> x = Macro.Env[]; 2 -> x = Macro.Env[] end end)
146+
assert optimize_clause(clause) == { clause, [x: Macro.Env], { Macro.Env, nil } }
147+
148+
clause = clause(fn -> case something do 1 -> x = Macro.Env[]; 2 -> x = Range[] end end)
149+
assert optimize_clause(clause) == { clause, [x: nil], nil }
150+
end
124151
end

0 commit comments

Comments
 (0)