Skip to content

Commit 3f062a4

Browse files
authored
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 74a24c3 commit 3f062a4

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
@@ -265,7 +265,7 @@ eval_quoted(Tree, Binding, #{line := Line} = E) ->
265265
eval_forms(Tree, Binding, Opts) when is_list(Opts) ->
266266
eval_forms(Tree, Binding, env_for_eval(Opts));
267267
eval_forms(Tree, RawBinding, OrigE) ->
268-
{Vars, Binding} = normalize_binding(RawBinding, [], [], 0),
268+
{Vars, Binding} = normalize_binding(RawBinding, #{}, [], 0),
269269
E = elixir_env:with_vars(OrigE, Vars),
270270
{_, S} = elixir_env:env_to_erl(E),
271271
{Erl, NewErlS, NewExS, NewE} = quoted_to_erl(Tree, E, S),
@@ -286,12 +286,19 @@ eval_forms(Tree, RawBinding, OrigE) ->
286286
{Value, elixir_erl_var:dump_binding(NewBinding, NewExS, NewErlS), NewE}
287287
end.
288288

289-
normalize_binding([{Key, Value} | Binding], Vars, Acc, I) when is_atom(Key) ->
290-
normalize_binding(Binding, [{{Key, nil}, I} | Vars], [{{Key, nil}, Value} | Acc], I + 1);
291-
normalize_binding([{Pair, Value} | Binding], Vars, Acc, I) ->
292-
normalize_binding(Binding, [{Pair, I} | Vars], [{Pair, Value} | Acc], I + 1);
293-
normalize_binding([], Vars, Acc, _I) ->
294-
{maps:from_list(Vars), Acc}.
289+
normalize_binding([Binding | NextBindings], VarsMap, Normalized, Counter) ->
290+
{Pair, Value} = normalize_pair(Binding),
291+
case VarsMap of
292+
#{Pair := _} ->
293+
normalize_binding(NextBindings, VarsMap, [{Pair, Value} | Normalized], Counter);
294+
#{} ->
295+
normalize_binding(NextBindings, VarsMap#{Pair => Counter}, [{Pair, Value} | Normalized], Counter + 1)
296+
end;
297+
normalize_binding([], VarsMap, Normalized, _Counter) ->
298+
{VarsMap, Normalized}.
299+
300+
normalize_pair({Key, Value}) when is_atom(Key) -> {{Key, nil}, Value};
301+
normalize_pair({Pair, Value}) -> {Pair, Value}.
295302

296303
recur_eval([Expr | Exprs], Binding, Env) ->
297304
{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)