Skip to content

Commit 0e59c68

Browse files
committed
Do not crash on duplicate bindings, closes #11378
1 parent 913d401 commit 0e59c68

File tree

3 files changed

+18
-13
lines changed

3 files changed

+18
-13
lines changed

lib/elixir/src/elixir.erl

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ eval_quoted(Tree, Binding, #{line := Line} = E) ->
254254
eval_forms(Tree, Binding, Opts) when is_list(Opts) ->
255255
eval_forms(Tree, Binding, env_for_eval(Opts));
256256
eval_forms(Tree, RawBinding, OrigE) ->
257-
{Vars, Binding} = normalize_binding(RawBinding, [], []),
257+
{Vars, Binding} = normalize_binding(RawBinding, [], [], 0),
258258
E = elixir_env:with_vars(OrigE, Vars),
259259
{_, S} = elixir_env:env_to_erl(E),
260260
{Erl, NewErlS, NewExS, NewE} = quoted_to_erl(Tree, E, S),
@@ -275,12 +275,12 @@ eval_forms(Tree, RawBinding, OrigE) ->
275275
{Value, elixir_erl_var:dump_binding(NewBinding, NewExS, NewErlS), NewE}
276276
end.
277277

278-
normalize_binding([{Key, Value} | Binding], Vars, Acc) when is_atom(Key) ->
279-
normalize_binding(Binding, [{Key, nil} | Vars], [{{Key, nil}, Value} | Acc]);
280-
normalize_binding([{Pair, Value} | Binding], Vars, Acc) ->
281-
normalize_binding(Binding, [Pair | Vars], [{Pair, Value} | Acc]);
282-
normalize_binding([], Vars, Acc) ->
283-
{Vars, Acc}.
278+
normalize_binding([{Key, Value} | Binding], Vars, Acc, I) when is_atom(Key) ->
279+
normalize_binding(Binding, [{{Key, nil}, I} | Vars], [{{Key, nil}, Value} | Acc], I + 1);
280+
normalize_binding([{Pair, Value} | Binding], Vars, Acc, I) ->
281+
normalize_binding(Binding, [{Pair, I} | Vars], [{Pair, Value} | Acc], I + 1);
282+
normalize_binding([], Vars, Acc, _I) ->
283+
{maps:from_list(Vars), Acc}.
284284

285285
recur_eval([Expr | Exprs], Binding, Env) ->
286286
{value, Value, NewBinding} =

lib/elixir/src/elixir_env.erl

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,7 @@ to_caller(#{} = Env) ->
3838
Env.
3939

4040
with_vars(Env, Vars) ->
41-
{VarVersions, _} = lists:mapfoldl(fun(Var, I) -> {{Var, I}, I + 1} end, 0, Vars),
42-
Env#{versioned_vars := maps:from_list(VarVersions)}.
41+
Env#{versioned_vars := Vars}.
4342

4443
reset_vars(Env) ->
4544
Env#{versioned_vars := #{}}.

lib/elixir/test/elixir/code_test.exs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,6 @@ defmodule CodeTest do
3535
assert {3, _} = Code.eval_string("a + b", [a: 1, b: 2], __ENV__)
3636
end
3737

38-
test "returns bindings from a different context" do
39-
assert Code.eval_string("var!(a, Sample) = 1") == {1, [{{:a, Sample}, 1}]}
40-
end
41-
4238
test "supports unnamed scopes" do
4339
assert {%RuntimeError{}, [a: %RuntimeError{}]} =
4440
Code.eval_string("a = (try do (raise \"hello\") rescue e -> e end)")
@@ -48,6 +44,16 @@ defmodule CodeTest do
4844
assert Code.eval_string("Kernel.if true, do: :ok", [], requires: [Z, Kernel]) == {:ok, []}
4945
end
5046

47+
test "returns bindings from a different context" do
48+
assert Code.eval_string("var!(a, Sample) = 1") == {1, [{{:a, Sample}, 1}]}
49+
end
50+
51+
test "does not raise on duplicate bindings" do
52+
# The order of which values win is not guaranteed, but it should evaluate successfully.
53+
assert Code.eval_string("b = String.Chars.to_string(a)", a: 0, a: 1) ==
54+
{"1", [{:b, "1"}, {:a, 1}]}
55+
end
56+
5157
test "with many options" do
5258
options = [
5359
functions: [{Kernel, [is_atom: 1]}],

0 commit comments

Comments
 (0)