Skip to content

Commit d4c2436

Browse files
author
José Valim
committed
Extend reach of record rewriting
1 parent 0f1584c commit d4c2436

File tree

2 files changed

+128
-7
lines changed

2 files changed

+128
-7
lines changed

lib/elixir/lib/kernel/record_rewriter.ex

Lines changed: 78 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,29 +40,74 @@ defmodule Kernel.RecordRewriter do
4040

4141
## Expr
4242

43+
defp optimize_expr({ :call, call_line, { :remote, line, left, right }, args }, dict) do
44+
{ left, dict, _ } = optimize_expr(left, dict)
45+
{ right, dict, _ } = optimize_expr(right, dict)
46+
{ args, dict } = optimize_args(args, dict)
47+
{ { :call, call_line, { :remote, line, left, right }, args }, dict, nil }
48+
end
49+
50+
defp optimize_expr({ :call, line, expr, args }, dict) do
51+
{ expr, dict, _ } = optimize_expr(expr, dict)
52+
{ args, dict } = optimize_args(args, dict)
53+
{ { :call, line, expr, args }, dict, nil }
54+
end
55+
4356
defp optimize_expr({ :match, line, left, right }, dict) do
4457
{ left, dict, left_res } = optimize_expr(left, dict)
4558
{ right, dict, right_res } = optimize_expr(right, dict)
4659

4760
match = { :match, line, left, right }
4861

49-
if right_res do
50-
dict = assign_vars(extract_vars(left, []), dict, right_res)
51-
end
52-
5362
if left_res do
5463
dict = assign_vars(extract_vars(right, []), dict, left_res)
5564
end
5665

66+
if right_res do
67+
dict = assign_vars(extract_vars(left, []), dict, right_res)
68+
end
69+
5770
{ match, dict, right_res || left_res }
5871
end
5972

73+
defp optimize_expr({ :op, line, op, left, right }, dict) do
74+
{ left, dict, _ } = optimize_expr(left, dict)
75+
{ right, dict, _ } = optimize_expr(right, dict)
76+
{ { :op, line, op, left, right }, dict, nil }
77+
end
78+
79+
defp optimize_expr({ :op, line, op, expr }, dict) do
80+
{ expr, dict, _ } = optimize_expr(expr, dict)
81+
{ { :op, line, op, expr }, dict, nil }
82+
end
83+
84+
defp optimize_expr({ :bin, line, elements }, dict) do
85+
{ elements, dict } = optimize_args(elements, dict)
86+
{ { :bin, line, elements }, dict, nil }
87+
end
88+
89+
defp optimize_expr({ :bin_element, line, expr, type1, type2 }, dict) do
90+
{ expr, dict, _ } = optimize_expr(expr, dict)
91+
{ { :bin_element, line, expr, type1, type2 }, dict, nil }
92+
end
93+
94+
defp optimize_expr({ :cons, line, left, right }, dict) do
95+
{ left, dict, _ } = optimize_expr(left, dict)
96+
{ right, dict, _ } = optimize_expr(right, dict)
97+
{ { :cons, line, left, right }, dict, nil }
98+
end
99+
100+
defp optimize_expr({ :block, line, args }, dict) do
101+
{ args, dict, res } = optimize_body(args, dict, [])
102+
{ { :block, line, args }, dict, res }
103+
end
104+
60105
defp optimize_expr({ :tuple, line, args }, dict) do
61106
{ args, dict, args_res } = optimize_tuple_args(args, dict)
62107

63108
res =
64109
case args do
65-
[{ :atom, _, atom }|t] -> if is_record?(atom), do: atom
110+
[{ :atom, _, atom }|t] -> atom
66111
_ -> nil
67112
end
68113

@@ -76,7 +121,19 @@ defmodule Kernel.RecordRewriter do
76121
end
77122
end
78123

79-
defp optimize_expr(other, dict) do
124+
defp optimize_expr({ comprehension, line, expr, args }, dict) when comprehension in [:lc, :bc] do
125+
{ args, new_dict } = optimize_args(args, dict)
126+
{ expr, _, _ } = optimize_expr(expr, new_dict)
127+
{ { comprehension, line, expr, args }, dict, nil }
128+
end
129+
130+
defp optimize_expr({ generate, line, left, right }, dict) when generate in [:generate, :b_generate] do
131+
{ left, dict, _ } = optimize_expr(left, dict)
132+
{ right, dict, _ } = optimize_expr(right, dict)
133+
{ { generate, line, left, right }, dict, nil }
134+
end
135+
136+
defp optimize_expr(other, dict) when elem(other, 0) in [:string, :atom, :integer, :float, :nil] do
80137
{ other, dict, nil }
81138
end
82139

@@ -97,7 +154,21 @@ defmodule Kernel.RecordRewriter do
97154
end
98155

99156
defp assign_vars([key|t], dict, { value, _ } = res) when is_atom(key) and value != nil do
100-
assign_vars t, :orddict.store(key, value, dict), res
157+
if is_record?(value) do
158+
dict =
159+
case :orddict.find(key, dict) do
160+
{ :ok, ^value } ->
161+
dict
162+
{ :ok, _ } ->
163+
# We are overriding a type of an existing variable,
164+
# which means the source code is invalid.
165+
:orddict.store(key, nil, dict)
166+
:error ->
167+
:orddict.store(key, value, dict)
168+
end
169+
end
170+
171+
assign_vars t, dict, res
101172
end
102173

103174
defp assign_vars([_|t], dict, res) do

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

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,54 @@ defmodule Kernel.RecordRewriterTest do
7171
clause = clause(fn(x = Macro.Env[], y = Range[]) -> y = x end)
7272
assert optimize_clause(clause) == { clause, [x: Macro.Env, y: Range, "y@1": Macro.Env], { Macro.Env, nil } }
7373
end
74+
75+
test "conflicting definition" do
76+
clause = clause(fn(x = Macro.Env[]) -> ^x = Range[]; :foo end)
77+
assert optimize_clause(clause) == { clause, [x: nil], nil }
78+
end
79+
80+
test "inside list" do
81+
clause = clause(fn -> [x = Macro.Env[]]; :foo end)
82+
assert optimize_clause(clause) == { clause, [x: Macro.Env], nil }
83+
end
84+
85+
test "inside tuple" do
86+
clause = clause(fn -> { x = Macro.Env[] }; :foo end)
87+
assert optimize_clause(clause) == { clause, [x: Macro.Env], nil }
88+
end
89+
90+
test "inside bin" do
91+
clause = clause(fn -> << x = Macro.Env[] >>; :foo end)
92+
assert optimize_clause(clause) == { clause, [x: Macro.Env], nil }
93+
end
94+
95+
test "inside operator" do
96+
clause = clause(fn -> 1 + (x = Macro.Env[]); :foo end)
97+
assert optimize_clause(clause) == { clause, [x: Macro.Env], nil }
98+
end
99+
100+
test "inside block" do
101+
clause = clause(fn -> :foo; (x = Macro.Env[]; x) end)
102+
assert optimize_clause(clause) == { clause, [x: Macro.Env], { Macro.Env, nil } }
103+
end
104+
105+
test "inside local call" do
106+
clause = clause(fn -> (x = Macro.Env[]).(y = Range[]) end)
107+
assert optimize_clause(clause) == { clause, [x: Macro.Env, y: Range], nil }
108+
end
109+
110+
test "inside remote call" do
111+
clause = clause(fn -> (x = Macro.Env[]).call(y = Range[]) end)
112+
assert optimize_clause(clause) == { clause, [x: Macro.Env, y: Range], nil }
113+
end
114+
115+
test "inside list comprehension" do
116+
clause = clause(fn -> lc x = Macro.Env[] inlist sample, do: x end)
117+
assert optimize_clause(clause) == { clause, [], nil }
118+
end
119+
120+
test "inside bit comprehension" do
121+
clause = clause(fn -> bc x = Macro.Env[] inbits sample, do: <<x>> end)
122+
assert optimize_clause(clause) == { clause, [], nil }
123+
end
74124
end

0 commit comments

Comments
 (0)