Skip to content

Commit cc9e9b2

Browse files
v0idpwnjosevalim
authored andcommitted
Fix duplicate bindings causing weird behaviour (#11584)
Before this fix, evaluating `b = a` with assignments of: `a: 1, a: 2, c: 3` would eval AST equivalent to `^c = a`. This happened due to binding normalization generating version numbers larger than the total number of bindings. Later in the pipeline, the number of bindings is used to compute the next version number, which would then conflict with an existing binding. This commit fixes that by not increasing the version number when a repeated binding is normalized.
1 parent 67f7c6b commit cc9e9b2

File tree

2 files changed

+17
-7
lines changed

2 files changed

+17
-7
lines changed

lib/elixir/src/elixir.erl

Lines changed: 14 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, [], [], 0),
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,19 @@ 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, 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}.
278+
normalize_binding([Binding | NextBindings], VarsMap, Normalized, Counter) ->
279+
{Pair, Value} = normalize_pair(Binding),
280+
case VarsMap of
281+
#{Pair := _} ->
282+
normalize_binding(NextBindings, VarsMap, [{Pair, Value} | Normalized], Counter);
283+
#{} ->
284+
normalize_binding(NextBindings, VarsMap#{Pair => Counter}, [{Pair, Value} | Normalized], Counter + 1)
285+
end;
286+
normalize_binding([], VarsMap, Normalized, _Counter) ->
287+
{VarsMap, Normalized}.
288+
289+
normalize_pair({Key, Value}) when is_atom(Key) -> {{Key, nil}, Value};
290+
normalize_pair({Pair, Value}) -> {Pair, Value}.
284291

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

lib/elixir/test/elixir/code_test.exs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ defmodule CodeTest do
5252
# The order of which values win is not guaranteed, but it should evaluate successfully.
5353
assert Code.eval_string("b = String.Chars.to_string(a)", a: 0, a: 1) ==
5454
{"1", [{:b, "1"}, {:a, 1}]}
55+
56+
assert Code.eval_string("b = String.Chars.to_string(a)", a: 0, a: 1, c: 2) ==
57+
{"1", [{:c, 2}, {:b, "1"}, {:a, 1}]}
5558
end
5659

5760
test "with many options" do

0 commit comments

Comments
 (0)