Skip to content

Commit 088ef2f

Browse files
author
José Valim
committed
Support try/receive in record rewriter
1 parent 498ac84 commit 088ef2f

File tree

2 files changed

+99
-9
lines changed

2 files changed

+99
-9
lines changed

lib/elixir/lib/kernel/record_rewriter.ex

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ defmodule Kernel.RecordRewriter do
104104

105105
defp optimize_expr({ :tuple, line, args }, dict) do
106106
{ args, dict, args_res } = optimize_tuple_args(args, dict)
107+
args_res = if Enum.any?(args_res), do: args_res, else: nil
107108

108109
res =
109110
case args do
@@ -116,7 +117,7 @@ defmodule Kernel.RecordRewriter do
116117

117118
defp optimize_expr({ :var, _, name } = var, dict) do
118119
case :orddict.find(name, dict) do
119-
{ :ok, res } -> { var, dict, { res, nil } }
120+
{ :ok, res } -> { var, dict, res }
120121
:error -> { var, dict, nil }
121122
end
122123
end
@@ -130,6 +131,38 @@ defmodule Kernel.RecordRewriter do
130131
{ { :case, line, expr, clauses }, dict, res }
131132
end
132133

134+
defp optimize_expr({ :receive, line, clauses }, dict) do
135+
tuples = lc clause inlist clauses, do: optimize_clause(clause, dict)
136+
clauses = lc { clause, _, _ } inlist tuples, do: clause
137+
dict = join_dict(tuples)
138+
res = join_result(tuples)
139+
{ { :receive, line, clauses }, dict, res }
140+
end
141+
142+
defp optimize_expr({ :receive, line, clauses, after_key, after_value }, dict) do
143+
tuples = lc clause inlist clauses, do: optimize_clause(clause, dict)
144+
clauses = lc { clause, _, _ } inlist tuples, do: clause
145+
146+
{ after_key, dict, _ } = optimize_expr(after_key, dict)
147+
{ after_value, dict, res } = optimize_body(after_value, dict, [])
148+
149+
dict = join_dict(tuples, dict)
150+
res = join_result(tuples, res)
151+
152+
{ { :receive, line, clauses, after_key, after_value }, dict, res }
153+
end
154+
155+
defp optimize_expr({ :try, line, body, [], clauses, try_after }, dict) do
156+
tuples = lc clause inlist clauses, do: optimize_clause(clause, dict)
157+
clauses = lc { clause, _, _ } inlist tuples, do: clause
158+
159+
{ body, _, res } = optimize_body(body, dict, [])
160+
res = join_result(tuples, res)
161+
162+
{ try_after, _, _ } = optimize_body(try_after, dict, [])
163+
{ { :try, line, body, [], clauses, try_after }, dict, res }
164+
end
165+
133166
defp optimize_expr({ :fun, line, { :function, module, name, arity } }, dict) do
134167
{ module, dict, _ } = optimize_expr(module, dict)
135168
{ name, dict, _ } = optimize_expr(name, dict)
@@ -179,14 +212,16 @@ defmodule Kernel.RecordRewriter do
179212
if is_record?(value) do
180213
dict =
181214
case :orddict.find(key, dict) do
182-
{ :ok, ^value } ->
215+
{ :ok, ^res } ->
183216
dict
217+
{ :ok, { ^value, _ } } ->
218+
:orddict.store(key, { value, nil }, dict)
184219
{ :ok, _ } ->
185220
# We are overriding a type of an existing variable,
186221
# which means the source code is invalid.
187222
:orddict.store(key, nil, dict)
188223
:error ->
189-
:orddict.store(key, value, dict)
224+
:orddict.store(key, res, dict)
190225
end
191226
end
192227

@@ -232,9 +267,10 @@ defmodule Kernel.RecordRewriter do
232267

233268
defp join_dict([{ _, dict, _ }|t], other) do
234269
other = Enum.reduce other, other, fn
235-
{ key, value }, acc ->
270+
{ key, { value, _ } = res }, acc ->
236271
case :orddict.find(key, dict) do
237-
{ :ok, ^value } -> acc
272+
{ :ok, ^res } -> acc
273+
{ :ok, { ^value, _ } } -> :orddict.store(key, { value, nil }, acc)
238274
{ :ok, _ } -> :orddict.store(key, nil, acc)
239275
:error -> :orddict.erase(key, acc)
240276
end
@@ -255,6 +291,10 @@ defmodule Kernel.RecordRewriter do
255291
join_result(t, res)
256292
end
257293

294+
defp join_result([{ _, _, { res, _ } }|t], { res, _ }) do
295+
join_result(t, { res, nil })
296+
end
297+
258298
defp join_result([{ _, _, _ }|_], _res) do
259299
nil
260300
end

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

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ Code.require_file "../../test_helper.exs", __FILE__
33
defmodule Kernel.RecordRewriterTest do
44
use ExUnit.Case, async: true
55

6-
import Kernel.RecordRewriter
7-
86
## Helpers
97

108
defmacrop clause(expr) do
@@ -17,6 +15,12 @@ defmodule Kernel.RecordRewriterTest do
1715
hd(clauses)
1816
end
1917

18+
defp optimize_clause(clause) do
19+
{ clause, dict, res } = Kernel.RecordRewriter.optimize_clause(clause)
20+
dict = Enum.map dict, fn { k, { v, _ } } -> { k, v }; other -> other; end
21+
{ clause, dict, res }
22+
end
23+
2024
## Dictionary tests
2125

2226
test "simple atom" do
@@ -62,16 +66,21 @@ defmodule Kernel.RecordRewriterTest do
6266
assert optimize_clause(clause) == { clause, [x: Macro.Env, y: Range], nil }
6367
end
6468

65-
test "with body variable" do
69+
test "inside body" do
6670
clause = clause(fn(x = Macro.Env[]) -> y = x end)
6771
assert optimize_clause(clause) == { clause, [x: Macro.Env, y: Macro.Env], { Macro.Env, nil } }
6872
end
6973

70-
test "with body variable overridden" do
74+
test "inside body with variable overridden" do
7175
clause = clause(fn(x = Macro.Env[], y = Range[]) -> y = x end)
7276
assert optimize_clause(clause) == { clause, [x: Macro.Env, y: Range, "y@1": Macro.Env], { Macro.Env, nil } }
7377
end
7478

79+
test "inside body with nested tuple" do
80+
clause = clause(fn(x = Range[]) -> ^x = Range[first: { :hello, 2 }] end)
81+
assert optimize_clause(clause) == { clause, [x: Range], { Range, [nil, { :hello, nil }, nil] } }
82+
end
83+
7584
test "conflicting definition" do
7685
clause = clause(fn(x = Macro.Env[]) -> ^x = Range[]; :foo end)
7786
assert optimize_clause(clause) == { clause, [x: nil], nil }
@@ -148,4 +157,45 @@ defmodule Kernel.RecordRewriterTest do
148157
clause = clause(fn -> case something do 1 -> x = Macro.Env[]; 2 -> x = Range[] end end)
149158
assert optimize_clause(clause) == { clause, [x: nil], nil }
150159
end
160+
161+
test "inside case with nested tuple" do
162+
clause = clause(fn -> case something do x = Range[first: { :foo, 2 }] -> x; Range[] = x -> x end end)
163+
assert optimize_clause(clause) == { clause, [x: Range], { Range, nil } }
164+
165+
clause = clause(fn -> case something do x = Range[first: { :foo, 2 }] -> x; Range[first: { :foo, 2 }] = x -> x end end)
166+
assert optimize_clause(clause) == { clause, [x: Range], { Range, [nil, { :foo, nil }, nil] } }
167+
end
168+
169+
test "inside receive" do
170+
clause = clause(fn -> receive do x = Macro.Env[] -> x; Macro.Env[] = x -> x end end)
171+
assert optimize_clause(clause) == { clause, [x: Macro.Env], { Macro.Env, nil } }
172+
173+
clause = clause(fn -> receive do x = Macro.Env[] -> x; Macro.Env[] = y -> y end end)
174+
assert optimize_clause(clause) == { clause, [], { Macro.Env, nil } }
175+
176+
clause = clause(fn -> receive do 1 -> x = Macro.Env[]; 2 -> x = Macro.Env[] end end)
177+
assert optimize_clause(clause) == { clause, [x: Macro.Env], { Macro.Env, nil } }
178+
179+
clause = clause(fn -> receive do 1 -> x = Macro.Env[]; 2 -> x = Range[] end end)
180+
assert optimize_clause(clause) == { clause, [x: nil], nil }
181+
end
182+
183+
test "inside receive with after" do
184+
clause = clause(fn -> receive do 1 -> x = Macro.Env[]; after 2 -> x = Macro.Env[]; end end)
185+
assert optimize_clause(clause) == { clause, [x: Macro.Env], { Macro.Env, nil } }
186+
187+
clause = clause(fn -> receive do 1 -> x = Macro.Env[]; after 2 -> x = Range[]; end end)
188+
assert optimize_clause(clause) == { clause, [x: nil], nil }
189+
end
190+
191+
test "inside try" do
192+
clause = clause(fn -> try do x = Macro.Env[]; x end end)
193+
assert optimize_clause(clause) == { clause, [], { Macro.Env, nil } }
194+
195+
clause = clause(fn -> try do x = Macro.Env[]; x; catch :oops -> x = Macro.Env[]; end end)
196+
assert optimize_clause(clause) == { clause, [], { Macro.Env, nil } }
197+
198+
clause = clause(fn -> try do x = Macro.Env[]; x; after x = Macro.Env[]; end end)
199+
assert optimize_clause(clause) == { clause, [], { Macro.Env, nil } }
200+
end
151201
end

0 commit comments

Comments
 (0)