Skip to content

Commit b32f6ef

Browse files
author
José Valim
committed
Keep variables in a single dictionary
This ensures we give the same treatment to quoted and non-quoted variables.
1 parent 9b3d851 commit b32f6ef

File tree

7 files changed

+47
-78
lines changed

7 files changed

+47
-78
lines changed

lib/elixir/include/elixir.hrl

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,8 @@
1616
name_args=false, %% when true, it means arguments should be named
1717
module=nil, %% the current module
1818
function=nil, %% the current function
19-
recur=nil, %% the current loop function to be recurred
2019
vars=[], %% a dict of defined variables and their alias
2120
temp_vars=[], %% a dict of all variables defined in a particular assign
22-
quote_vars=[], %% a dict of all quoted variables
2321
clause_vars=nil, %% a dict of all variables defined in a particular clause
2422
extra_guards=nil, %% extra guards from args expansion
2523
counter=[], %% a counter for the variables defined

lib/elixir/src/elixir.erl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,6 @@ eval_forms(Tree, Binding, RawScope) ->
117117
Scope = RawScope#elixir_scope{
118118
vars=binding_dict(Binding),
119119
temp_vars=[],
120-
quote_vars=[],
121120
clause_vars=nil,
122121
counter=[]
123122
},
@@ -135,7 +134,8 @@ to_binary(Bin) when is_binary(Bin) -> Bin;
135134
to_binary(List) when is_list(List) -> list_to_binary(List).
136135

137136
binding_dict(List) -> binding_dict(List, orddict:new()).
138-
binding_dict([{H,_}|T], Dict) -> binding_dict(T, orddict:store(H, H, Dict));
137+
binding_dict([{{H,Kind},_}|T], Dict) -> binding_dict(T, orddict:store({ H, Kind }, H, Dict));
138+
binding_dict([{H,_}|T], Dict) -> binding_dict(T, orddict:store({ H, nil }, H, Dict));
139139
binding_dict([], Dict) -> Dict.
140140

141141
final_binding(Binding, Vars) -> final_binding(Binding, [], Binding, Vars).
@@ -144,7 +144,7 @@ final_binding([{Var,_}|T], Acc, Binding, Vars) ->
144144
true ->
145145
final_binding(T, Acc, Binding, Vars);
146146
false ->
147-
RealName = orddict:fetch(Var, Vars),
147+
RealName = orddict:fetch({ Var, nil }, Vars),
148148
RealValue = proplists:get_value(RealName, Binding, nil),
149149
final_binding(T, [{Var, RealValue}|Acc], Binding, Vars)
150150
end;

lib/elixir/src/elixir_clauses.erl

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,8 @@ do_match(Line, DecoupledClauses, S) ->
131131
end.
132132

133133
expand_clauses(Line, [Clause|T], [ClauseVars|V], LeftVars, FinalVars, Acc, S) ->
134-
RightVars = [normalize_clause_var(Var, Kind, OldValue, ClauseVars) || { Var, Kind, _, OldValue } <- FinalVars],
134+
RightVars = [normalize_clause_var(Var, Kind, OldValue, ClauseVars) ||
135+
{ Var, Kind, _, OldValue } <- FinalVars],
135136

136137
AssignExpr = generate_match(Line, LeftVars, RightVars),
137138
ClauseExprs = element(5, Clause),
@@ -202,29 +203,23 @@ has_match_tuple(_) -> false.
202203

203204
% Normalize the given var checking its existence in the scope var dictionary.
204205

205-
normalize_vars({ Var, quoted }, S) ->
206-
normalize_vars(Var, quoted, #elixir_scope.quote_vars, S);
207-
208-
normalize_vars({ Var, Kind }, S) ->
209-
normalize_vars(Var, Kind, #elixir_scope.vars, S).
210-
211-
normalize_vars(Var, Kind, Index, #elixir_scope{clause_vars=ClauseVars} = S) ->
212-
Vars = element(Index, S),
213-
206+
normalize_vars({ Var, Kind } = Key, #elixir_scope{vars=Vars,clause_vars=ClauseVars} = S) ->
214207
{ { _, _, NewValue }, S1 } = if
215208
(Kind == quoted) or (S#elixir_scope.noname) -> elixir_scope:build_erl_var(0, S);
216209
true -> elixir_scope:build_erl_var(0, Var, "_@" ++ atom_to_list(Var), S)
217210
end,
218211

219-
S2 = setelement(Index, S1, orddict:store(Var, NewValue, Vars)),
220-
S3 = S2#elixir_scope{clause_vars=orddict:store({ Var, Kind }, NewValue, ClauseVars)},
212+
S2 = S1#elixir_scope{
213+
vars=orddict:store(Key, NewValue, Vars),
214+
clause_vars=orddict:store(Key, NewValue, ClauseVars)
215+
},
221216

222-
Expr = case orddict:find(Var, Vars) of
217+
Expr = case orddict:find(Key, Vars) of
223218
{ ok, OldValue } -> { var, 0, OldValue };
224219
error -> { atom, 0, nil }
225220
end,
226221

227-
{ { Var, Kind, NewValue, Expr }, S3 }.
222+
{ { Var, Kind, NewValue, Expr }, S2 }.
228223

229224
% Normalize a var by checking if it was defined in the clause.
230225
% If so, use it, otherwise use from main scope.

lib/elixir/src/elixir_compiler.erl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ eval_forms(Forms, Line, Value, Vars, S) ->
6262
end.
6363

6464
eval_compilation(Forms, Vars, S) ->
65-
Binding = [{ Var, Value } || { _, Var, Value } <- Vars],
65+
Binding = [{ { Var, Kind }, Value } || { _, Kind, Var, Value } <- Vars],
6666
{ Result, _Binding, FS } = elixir:eval_forms(Forms, [{'_@MODULE',nil}|Binding], S),
6767
{ Result, FS }.
6868

@@ -71,7 +71,7 @@ code_loading_compilation(Forms, Line, Value, Vars, S) ->
7171
{ Exprs, FS } = elixir_translator:translate(Forms, S),
7272
ModuleForm = module_form(Exprs, Line, S#elixir_scope.file, Module, Vars),
7373

74-
Args = [X || { _, _, X } <- Vars],
74+
Args = [X || { _, _, _, X } <- Vars],
7575

7676
%% Pass { native, false } to speed up bootstrap
7777
%% process when native is set to true
@@ -127,7 +127,7 @@ no_auto_import() ->
127127
module_form(Exprs, Line, File, Module, Vars) when
128128
is_binary(File), is_list(Exprs), is_integer(Line), is_atom(Module) ->
129129

130-
Cons = lists:foldr(fun({ _, Var, _ }, Acc) ->
130+
Cons = lists:foldr(fun({ _, _, Var, _ }, Acc) ->
131131
{ cons, Line, { var, Line, Var }, Acc }
132132
end, { nil, Line }, Vars),
133133

lib/elixir/src/elixir_module.erl

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,16 @@ translate(Line, Ref, Block, S) ->
4343
MetaBlock = elixir_tree_helpers:abstract_syntax(Block),
4444
MetaS = elixir_scope:serialize(S),
4545

46-
Vars = orddict:fold(fun(Key, Value, { Acc, Counter }) ->
46+
{ Vars, _ } = orddict:fold(fun({ Key, Kind }, Value, { Acc, Counter }) ->
4747
{ { cons, Line, { tuple, Line, [
4848
{ atom, Line, Key },
49+
{ atom, Line, Kind },
4950
{ atom, Line, ?ELIXIR_ATOM_CONCAT(["_@", Counter]) },
5051
{ var, Line, Value }
5152
] }, Acc }, Counter + 1 }
5253
end, { { nil, Line }, 0 }, S#elixir_scope.vars),
5354

54-
Args = [{integer, Line, Line}, Ref, MetaBlock, element(1, Vars), MetaS],
55+
Args = [{integer, Line, Line}, Ref, MetaBlock, Vars, MetaS],
5556
?ELIXIR_WRAP_CALL(Line, ?MODULE, compile, Args).
5657

5758
%% The compilation hook.
@@ -92,7 +93,7 @@ compile(Line, Other, _Block, _Vars, #elixir_scope{file=File}) ->
9293
elixir_errors:form_error(Line, File, ?MODULE, { invalid_module, Other });
9394

9495
compile(Line, Module, Block, Vars, RawS) ->
95-
Dict = [{ X, Y } || { X, Y, _ } <- Vars],
96+
Dict = [{ { Name, Kind }, Value } || { Name, Kind, Value, _ } <- Vars],
9697
S = elixir_scope:deserialize(RawS, Dict),
9798
compile(Line, Module, Block, Vars, S).
9899

lib/elixir/src/elixir_scope.erl

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
%% Convenience functions used to manipulate scope
32
%% and its variables.
43
-module(elixir_scope).
@@ -23,20 +22,20 @@ translate_var(Line, Name, Kind, S) ->
2322
case S#elixir_scope.context of
2423
assign ->
2524
TempVars = S#elixir_scope.temp_vars,
26-
case { orddict:is_key(Name, Vars), orddict:find(Name, TempVars) } of
25+
case { orddict:is_key({ Name, Kind }, Vars), orddict:find(Name, TempVars) } of
2726
{ true, { ok, Kind } } ->
28-
{ {var, Line, orddict:fetch(Name, Vars) }, S };
27+
{ { var, Line, orddict:fetch({ Name, Kind }, Vars) }, S };
2928
{ Else, _ } ->
3029
{ NewVar, NS } = if
3130
Kind == quoted -> build_erl_var(Line, S);
3231
Else -> build_erl_var(Line, Name, S);
3332
S#elixir_scope.noname -> build_erl_var(Line, Name, S);
34-
true -> { {var, Line, Name}, S }
33+
true -> { { var, Line, Name }, S }
3534
end,
3635
RealName = element(3, NewVar),
3736
ClauseVars = S#elixir_scope.clause_vars,
3837
{ NewVar, NS#elixir_scope{
39-
vars=orddict:store(Name, RealName, Vars),
38+
vars=orddict:store({ Name, Kind }, RealName, Vars),
4039
temp_vars=orddict:store(Name, Kind, TempVars),
4140
clause_vars=if
4241
ClauseVars == nil -> nil;
@@ -45,9 +44,9 @@ translate_var(Line, Name, Kind, S) ->
4544
} }
4645
end;
4746
_ ->
48-
case orddict:is_key(Name, Vars) of
49-
false -> elixir_translator:translate_each({Name, Line, []}, S);
50-
true -> { {var, Line, orddict:fetch(Name, Vars) }, S }
47+
case orddict:find({ Name, Kind }, Vars) of
48+
{ ok, VarName } -> { { var, Line, VarName }, S };
49+
error -> elixir_translator:translate_each({ Name, Line, [] }, S)
5150
end
5251
end
5352
end.
@@ -121,13 +120,10 @@ deserialize({ File, Functions, CheckClauses, Requires, Macros, Aliases, Schedule
121120
umergev(S1, S2) ->
122121
V1 = S1#elixir_scope.vars,
123122
V2 = S2#elixir_scope.vars,
124-
Q1 = S1#elixir_scope.quote_vars,
125-
Q2 = S2#elixir_scope.quote_vars,
126123
C1 = S1#elixir_scope.clause_vars,
127124
C2 = S2#elixir_scope.clause_vars,
128125
S2#elixir_scope{
129126
vars=orddict:merge(fun var_merger/3, V1, V2),
130-
quote_vars=orddict:merge(fun var_merger/3, Q1, Q2),
131127
clause_vars=merge_clause_vars(C1, C2)
132128
}.
133129

@@ -148,13 +144,10 @@ umergec(S1, S2) ->
148144
merge_clause_vars(nil, _C2) -> nil;
149145
merge_clause_vars(_C1, nil) -> nil;
150146
merge_clause_vars(C1, C2) ->
151-
orddict:merge(fun clause_var_merger/3, C1, C2).
152-
153-
clause_var_merger({ Var, _ }, K1, K2) ->
154-
var_merger(Var, K1, K2).
147+
orddict:merge(fun var_merger/3, C1, C2).
155148

156-
var_merger(Var, Var, K2) -> K2;
157-
var_merger(Var, K1, Var) -> K1;
149+
var_merger({ Var, _ }, Var, K2) -> K2;
150+
var_merger({ Var, _ }, K1, Var) -> K1;
158151
var_merger(_Var, K1, K2) ->
159152
V1 = var_number(atom_to_list(K1), []),
160153
V2 = var_number(atom_to_list(K2), []),

lib/elixir/src/elixir_translator.erl

Lines changed: 18 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,9 @@ translate_each({ '__op__', Line, [Op, Left, Right] }, S) when is_atom(Op) ->
7777
{ { op, Line, convert_op(Op), TLeft, TRight }, NS };
7878

7979
translate_each({ '__ambiguousop__', Line, [Var, H|T] }, S) ->
80-
{ Name, _, _ } = Var,
80+
{ Name, _, Kind } = Var,
8181

82-
case orddict:find(Name, S#elixir_scope.vars) of
82+
case orddict:find({ Name, Kind }, S#elixir_scope.vars) of
8383
error -> translate_each({ Name, Line, [H|T] }, S);
8484
_ ->
8585
case T of
@@ -103,7 +103,7 @@ translate_each({ alias, Line, [Ref, KV] }, S) ->
103103
{ atom, _, Old } ->
104104
{ New, SF } = case lists:keyfind(as, 1, KV) of
105105
Opt when Opt == { as, true }; Opt == false ->
106-
{ elixir_aliases:last(Old), SR };
106+
{ elixir_aliases:last(Old), SR };
107107
{ as, false } ->
108108
{ Old, SR };
109109
{ as, Other } ->
@@ -284,7 +284,7 @@ translate_each({ quote, GivenLine, [T] }, S) when is_list(T) ->
284284

285285
translate_each({ quote, GivenLine, [_] }, S) ->
286286
syntax_error(GivenLine, S#elixir_scope.file, "invalid args for quote");
287-
287+
288288
%% Functions
289289

290290
translate_each({ fn, Line, [[{do, { '->', _, Pairs }}]] }, S) ->
@@ -330,41 +330,23 @@ translate_each({ 'super?', Line, [] }, S) ->
330330

331331
%% Variables
332332

333-
translate_each({ '^', Line, [ { Name, _, Args } ] }, S) ->
334-
Dict = case Args of
335-
nil -> S#elixir_scope.vars;
336-
quoted -> S#elixir_scope.quote_vars;
337-
_ ->
338-
syntax_error(Line, S#elixir_scope.file, "cannot use ^ with expression at ^~s, ^ must be used only with variables", [Name])
339-
end,
340-
341-
Result = case S#elixir_scope.context of
342-
assign ->
343-
case orddict:find(Name, Dict) of
344-
error -> "unbound variable ^~s";
345-
{ ok, Value } -> { {var, Line, Value}, S }
346-
end;
347-
_ -> "cannot access variable ^~s outside of assignment"
348-
end,
333+
translate_each({ '^', Line, [ { Name, _, Kind } ] }, S) when is_list(Kind) ->
334+
syntax_error(Line, S#elixir_scope.file, "cannot use ^ with expression at ^~s, ^ must be used only with variables", [Name]);
349335

350-
case is_list(Result) of
351-
true ->
352-
syntax_error(Line, S#elixir_scope.file, Result, [Name]);
353-
false ->
354-
Result
336+
translate_each({ '^', Line, [ { Name, _, Kind } ] }, #elixir_scope{context=assign} = S) when is_atom(Kind) ->
337+
case orddict:find({ Name, Kind }, S#elixir_scope.vars) of
338+
{ ok, Value } ->
339+
{ { var, Line, Value }, S };
340+
error ->
341+
syntax_error(Line, S#elixir_scope.file, "unbound variable ^~s", [Name])
355342
end;
356343

357-
translate_each({ Name, Line, quoted }, S) when is_atom(Name) ->
358-
NewS = S#elixir_scope{vars=S#elixir_scope.quote_vars,noname=true},
359-
{ TVar, VS } = elixir_scope:translate_var(Line, Name, quoted, NewS),
360-
{ TVar, VS#elixir_scope{
361-
quote_vars=VS#elixir_scope.vars,
362-
noname=S#elixir_scope.noname,
363-
vars=S#elixir_scope.vars
364-
} };
365-
366-
translate_each({ Name, Line, nil }, S) when is_atom(Name) ->
367-
elixir_scope:translate_var(Line, Name, nil, S);
344+
translate_each({ '^', Line, [ { Name, _, Kind } ] }, S) when is_atom(Kind) ->
345+
syntax_error(Line, S#elixir_scope.file,
346+
"cannot access variable ^~s outside of assignment", [Name]);
347+
348+
translate_each({ Name, Line, Kind }, S) when is_atom(Name), (Kind == nil orelse Kind == quoted) ->
349+
elixir_scope:translate_var(Line, Name, Kind, S);
368350

369351
%% Local calls
370352

0 commit comments

Comments
 (0)