Skip to content

Commit a53e851

Browse files
committed
Track the current collectable accumulator during for ..., into: ...
Pass the current accumulator to final :halt invocation on exception Fixes #15265
1 parent 8011552 commit a53e851

2 files changed

Lines changed: 66 additions & 7 deletions

File tree

lib/elixir/src/elixir_erl_for.erl

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -172,35 +172,48 @@ build_into(Ann, Clauses, Expr, Into, Uniq, S) ->
172172
{Reason, SR} = build_var(Ann, SK),
173173
{Stack, ST} = build_var(Ann, SR),
174174
{Done, SD} = build_var(Ann, ST),
175+
{Ref, SRef} = build_var(Ann, SD),
176+
{Current, SC} = build_var(Ann, SRef),
175177

176178
InnerFun = fun(InnerExpr, InnerAcc) ->
177-
{call, Ann, Fun, [InnerAcc, pair(Ann, cont, InnerExpr)]}
179+
{block, Ann, [
180+
{match, Ann, Current, {call, Ann, Fun, [InnerAcc, pair(Ann, cont, InnerExpr)]}},
181+
?remote(Ann, erlang, put, [Ref, Current]),
182+
Current
183+
]}
178184
end,
179185

180186
MatchExpr = {match, Ann,
181187
{tuple, Ann, [Acc, Fun]},
182188
?remote(Ann, 'Elixir.Collectable', into, [Into])
183189
},
184190

185-
{IntoReduceExpr, SN} = build_reduce(Ann, Clauses, InnerFun, Expr, Acc, Uniq, SD),
191+
{IntoReduceExpr, SN} = build_reduce(Ann, Clauses, InnerFun, Expr, Acc, Uniq, SC),
192+
RefExpr = {match, Ann, Ref, ?remote(Ann, erlang, make_ref, [])},
186193

187194
TryExpr =
188195
{'try', Ann,
189196
[IntoReduceExpr],
190197
[{clause, Ann,
191198
[Done],
192199
[],
193-
[{call, Ann, Fun, [Done, {atom, Ann, done}]}]}],
194-
[stacktrace_clause(Ann, Fun, Acc, Kind, Reason, Stack)],
200+
[
201+
?remote(Ann, erlang, erase, [Ref]),
202+
{call, Ann, Fun, [Done, {atom, Ann, done}]}
203+
]}],
204+
[stacktrace_clause(Ann, Fun, Ref, Kind, Reason, Stack)],
195205
[]},
196206

197-
{{block, Ann, [MatchExpr, TryExpr]}, SN}.
207+
{{block, Ann, [RefExpr, MatchExpr, ?remote(Ann, erlang, put, [Ref, Acc]), TryExpr]}, SN}.
198208

199-
stacktrace_clause(Ann, Fun, Acc, Kind, Reason, Stack) ->
209+
stacktrace_clause(Ann, Fun, Ref, Kind, Reason, Stack) ->
210+
CurrentAcc = ?remote(Ann, erlang, get, [Ref]),
200211
{clause, Ann,
201212
[{tuple, Ann, [Kind, Reason, Stack]}],
202213
[],
203-
[{call, Ann, Fun, [Acc, {atom, Ann, halt}]},
214+
[
215+
{call, Ann, Fun, [CurrentAcc, {atom, Ann, halt}]},
216+
?remote(Ann, erlang, erase, [Ref]),
204217
?remote(Ann, erlang, raise, [Kind, Reason, Stack])]}.
205218

206219
%% Helpers

lib/elixir/test/elixir/kernel/comprehension_test.exs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,31 @@ defmodule Kernel.ComprehensionTest do
2626
end
2727
end
2828

29+
defmodule EvolvingCollectable do
30+
defstruct []
31+
32+
defimpl Collectable do
33+
def into(struct) do
34+
fun = fn
35+
acc, {:cont, x} ->
36+
next = acc ++ [x]
37+
Process.put(:into_trace, [{acc, x, next} | Process.get(:into_trace)])
38+
next
39+
40+
acc, :done ->
41+
Process.put(:into_done_acc, acc)
42+
acc
43+
44+
acc, :halt ->
45+
Process.put(:into_halt_acc, acc)
46+
acc
47+
end
48+
49+
{[struct], fun}
50+
end
51+
end
52+
end
53+
2954
defp to_bin(x) do
3055
<<x>>
3156
end
@@ -102,6 +127,27 @@ defmodule Kernel.ComprehensionTest do
102127
assert Process.get(:into_halt)
103128
end
104129

130+
test "for into halts with the current accumulator" do
131+
Process.put(:into_trace, [])
132+
Process.put(:into_halt_acc, nil)
133+
Process.put(:into_done_acc, nil)
134+
135+
assert_raise RuntimeError, "oops", fn ->
136+
for x <- [1, 2, 3], into: %EvolvingCollectable{} do
137+
if x == 3, do: raise("oops")
138+
x
139+
end
140+
end
141+
142+
assert Process.get(:into_trace) |> Enum.reverse() == [
143+
{[%EvolvingCollectable{}], 1, [%EvolvingCollectable{}, 1]},
144+
{[%EvolvingCollectable{}, 1], 2, [%EvolvingCollectable{}, 1, 2]}
145+
]
146+
147+
assert Process.get(:into_halt_acc) == [%EvolvingCollectable{}, 1, 2]
148+
assert Process.get(:into_done_acc) == nil
149+
end
150+
105151
test "nested for comprehensions with unique values" do
106152
assert for(x <- [1, 1, 2], uniq: true, do: for(y <- [3, 3], uniq: true, do: x * y)) == [
107153
[3],

0 commit comments

Comments
 (0)