aboutsummaryrefslogtreecommitdiffstats
path: root/lib/stdlib
diff options
context:
space:
mode:
authorBjörn Gustavsson <[email protected]>2010-04-09 15:50:17 +0200
committerBjörn Gustavsson <[email protected]>2011-11-07 13:57:03 +0100
commitff432e262e65243cbc983fcb002527f8fae8c78b (patch)
tree1966b14f94663375eb0333c3e2d707aab501a960 /lib/stdlib
parent33c08d87a9395ae088eebcad9fb62193d26b6846 (diff)
downloadotp-ff432e262e65243cbc983fcb002527f8fae8c78b.tar.gz
otp-ff432e262e65243cbc983fcb002527f8fae8c78b.tar.bz2
otp-ff432e262e65243cbc983fcb002527f8fae8c78b.zip
EEP-23: Allow variables in fun M:F/A
Currently, the external fun syntax "fun M:F/A" only supports literals. That is, "fun lists:reverse/1" is allowed but not "fun M:F/A". In many real-life situations, some or all of M, F, A are not known until run-time, and one is forced to either use the undocumented erlang:make_fun/3 BIF or to use a "tuple fun" (which is deprecated). EEP-23 suggests that the parser (erl_parse) should immediately transform "fun M:F/A" to "erlang:make_fun(M, F, A)". We have not followed that approach in this implementation, because we want the abstract code to mirror the source code as closely as possible, and we also consider erlang:make_fun/3 to be an implementation detail that we might want to remove in the future. Instead, we will change the abstract format for "fun M:F/A" (in a way that is not backwards compatible), and while we are at it, we will move the translation from "fun M:F/A" to "erlang:make_fun(M, F, A)" from sys_pre_expand down to the v3_core pass. We will also update the debugger and xref to use the new format. We did consider making the abstract format backward compatible if no variables were used in the fun, but decided against it. Keeping it backward compatible would mean that there would be different abstract formats for the no-variable and variable case, and tools would have to handle both formats, probably forever. Reference: http://www.erlang.org/eeps/eep-0023.html
Diffstat (limited to 'lib/stdlib')
-rw-r--r--lib/stdlib/examples/erl_id_trans.erl9
-rw-r--r--lib/stdlib/src/erl_eval.erl3
-rw-r--r--lib/stdlib/src/erl_lint.erl9
-rw-r--r--lib/stdlib/src/erl_parse.yrl12
-rw-r--r--lib/stdlib/src/erl_pp.erl10
-rw-r--r--lib/stdlib/src/qlc.erl5
-rw-r--r--lib/stdlib/test/erl_eval_SUITE.erl6
-rw-r--r--lib/stdlib/test/erl_pp_SUITE.erl5
-rw-r--r--lib/stdlib/test/qlc_SUITE.erl2
9 files changed, 49 insertions, 12 deletions
diff --git a/lib/stdlib/examples/erl_id_trans.erl b/lib/stdlib/examples/erl_id_trans.erl
index b63acdd40a..72e41d6473 100644
--- a/lib/stdlib/examples/erl_id_trans.erl
+++ b/lib/stdlib/examples/erl_id_trans.erl
@@ -419,7 +419,14 @@ expr({'fun',Line,Body}) ->
{'fun',Line,{clauses,Cs1}};
{function,F,A} ->
{'fun',Line,{function,F,A}};
- {function,M,F,A} -> %R10B-6: fun M:F/A.
+ {function,M,F,A} when is_atom(M), is_atom(F), is_integer(A) ->
+ %% R10B-6: fun M:F/A. (Backward compatibility)
+ {'fun',Line,{function,M,F,A}};
+ {function,M0,F0,A0} ->
+ %% R15: fun M:F/A with variables.
+ M = expr(M0),
+ F = expr(F0),
+ A = expr(A0),
{'fun',Line,{function,M,F,A}}
end;
expr({call,Line,F0,As0}) ->
diff --git a/lib/stdlib/src/erl_eval.erl b/lib/stdlib/src/erl_eval.erl
index 4f4fa16040..88a0094d57 100644
--- a/lib/stdlib/src/erl_eval.erl
+++ b/lib/stdlib/src/erl_eval.erl
@@ -256,7 +256,8 @@ expr({'receive',_,Cs}, Bs, Lf, Ef, RBs) ->
expr({'receive',_, Cs, E, TB}, Bs0, Lf, Ef, RBs) ->
{value,T,Bs} = expr(E, Bs0, Lf, Ef, none),
receive_clauses(T, Cs, {TB,Bs}, Bs0, Lf, Ef, [], RBs);
-expr({'fun',_Line,{function,Mod,Name,Arity}}, Bs, _Lf, _Ef, RBs) ->
+expr({'fun',_Line,{function,Mod0,Name0,Arity0}}, Bs0, Lf, Ef, RBs) ->
+ {[Mod,Name,Arity],Bs} = expr_list([Mod0,Name0,Arity0], Bs0, Lf, Ef),
F = erlang:make_fun(Mod, Name, Arity),
ret_expr(F, Bs, RBs);
expr({'fun',_Line,{function,Name,Arity}}, _Bs0, _Lf, _Ef, _RBs) -> % R8
diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl
index 78b996d94b..5d45260fe9 100644
--- a/lib/stdlib/src/erl_lint.erl
+++ b/lib/stdlib/src/erl_lint.erl
@@ -2127,8 +2127,13 @@ expr({'fun',Line,Body}, Vt, St) ->
true -> {[],St};
false -> {[],call_function(Line, F, A, St)}
end;
- {function,_M,_F,_A} ->
- {[],St}
+ {function,M,F,A} when is_atom(M), is_atom(F), is_integer(A) ->
+ %% Compatibility with pre-R15 abstract format.
+ {[],St};
+ {function,M,F,A} ->
+ %% New in R15.
+ {Bvt, St1} = expr_list([M,F,A], Vt, St),
+ {vtupdate(Bvt, Vt),St1}
end;
expr({call,_Line,{atom,_Lr,is_record},[E,{atom,Ln,Name}]}, Vt, St0) ->
{Rvt,St1} = expr(E, Vt, St0),
diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl
index 709bd83e6f..928c10f7f2 100644
--- a/lib/stdlib/src/erl_parse.yrl
+++ b/lib/stdlib/src/erl_parse.yrl
@@ -35,7 +35,7 @@ tuple
%struct
record_expr record_tuple record_field record_fields
if_expr if_clause if_clauses case_expr cr_clause cr_clauses receive_expr
-fun_expr fun_clause fun_clauses
+fun_expr fun_clause fun_clauses atom_or_var integer_or_var
try_expr try_catch try_clause try_clauses query_expr
function_call argument_list
exprs guard
@@ -395,11 +395,17 @@ receive_expr -> 'receive' cr_clauses 'after' expr clause_body 'end' :
fun_expr -> 'fun' atom '/' integer :
{'fun',?line('$1'),{function,element(3, '$2'),element(3, '$4')}}.
-fun_expr -> 'fun' atom ':' atom '/' integer :
- {'fun',?line('$1'),{function,element(3, '$2'),element(3, '$4'),element(3,'$6')}}.
+fun_expr -> 'fun' atom_or_var ':' atom_or_var '/' integer_or_var :
+ {'fun',?line('$1'),{function,'$2','$4','$6'}}.
fun_expr -> 'fun' fun_clauses 'end' :
build_fun(?line('$1'), '$2').
+atom_or_var -> atom : '$1'.
+atom_or_var -> var : '$1'.
+
+integer_or_var -> integer : '$1'.
+integer_or_var -> var : '$1'.
+
fun_clauses -> fun_clause : ['$1'].
fun_clauses -> fun_clause ';' fun_clauses : ['$1' | '$3'].
diff --git a/lib/stdlib/src/erl_pp.erl b/lib/stdlib/src/erl_pp.erl
index 7dc19f2e9b..6b5aa951cf 100644
--- a/lib/stdlib/src/erl_pp.erl
+++ b/lib/stdlib/src/erl_pp.erl
@@ -457,8 +457,16 @@ lexpr({'fun',_,{function,F,A}}, _Prec, _Hook) ->
leaf(format("fun ~w/~w", [F,A]));
lexpr({'fun',_,{function,F,A},Extra}, _Prec, _Hook) ->
{force_nl,fun_info(Extra),leaf(format("fun ~w/~w", [F,A]))};
-lexpr({'fun',_,{function,M,F,A}}, _Prec, _Hook) ->
+lexpr({'fun',_,{function,M,F,A}}, _Prec, _Hook)
+ when is_atom(M), is_atom(F), is_integer(A) ->
+ %% For backward compatibility with pre-R15 abstract format.
leaf(format("fun ~w:~w/~w", [M,F,A]));
+lexpr({'fun',_,{function,M,F,A}}, _Prec, Hook) ->
+ %% New format in R15.
+ NameItem = lexpr(M, Hook),
+ CallItem = lexpr(F, Hook),
+ ArityItem = lexpr(A, Hook),
+ ["fun ",NameItem,$:,CallItem,$/,ArityItem];
lexpr({'fun',_,{clauses,Cs}}, _Prec, Hook) ->
{list,[{first,'fun',fun_clauses(Cs, Hook)},'end']};
lexpr({'fun',_,{clauses,Cs},Extra}, _Prec, Hook) ->
diff --git a/lib/stdlib/src/qlc.erl b/lib/stdlib/src/qlc.erl
index f5e180b4bd..2b691e6abf 100644
--- a/lib/stdlib/src/qlc.erl
+++ b/lib/stdlib/src/qlc.erl
@@ -1272,7 +1272,10 @@ abstr_term(Fun, Line) when is_function(Fun) ->
case erlang:fun_info(Fun, type) of
{type, external} ->
{module, Module} = erlang:fun_info(Fun, module),
- {'fun', Line, {function,Module,Name,Arity}};
+ {'fun', Line, {function,
+ {atom,Line,Module},
+ {atom,Line,Name},
+ {integer,Line,Arity}}};
{type, local} ->
{'fun', Line, {function,Name,Arity}}
end
diff --git a/lib/stdlib/test/erl_eval_SUITE.erl b/lib/stdlib/test/erl_eval_SUITE.erl
index 784c7cb86e..369d8b224e 100644
--- a/lib/stdlib/test/erl_eval_SUITE.erl
+++ b/lib/stdlib/test/erl_eval_SUITE.erl
@@ -1036,6 +1036,12 @@ funs(Config) when is_list(Config) ->
lists:usort([run_many_args(SAs) || SAs <- many_args(MaxArgs)]),
?line {'EXIT',{{argument_limit,_},_}} =
(catch run_many_args(many_args1(MaxArgs+1))),
+
+ ?line check(fun() -> M = lists, F = fun M:reverse/1,
+ [1,2] = F([2,1]), ok end,
+ "begin M = lists, F = fun M:reverse/1,"
+ " [1,2] = F([2,1]), ok end.",
+ ok),
ok.
run_many_args({S, As}) ->
diff --git a/lib/stdlib/test/erl_pp_SUITE.erl b/lib/stdlib/test/erl_pp_SUITE.erl
index 280c95b1aa..64853ca078 100644
--- a/lib/stdlib/test/erl_pp_SUITE.erl
+++ b/lib/stdlib/test/erl_pp_SUITE.erl
@@ -116,7 +116,6 @@ func(Config) when is_list(Config) ->
{func_3,
<<"t() -> fun t/0.">>},
{func_4,
- %% Has already been expanded away in sys_pre_expand.
<<"t() -> fun modul:foo/3.">>},
{func_5, % 'when' is moved down one line
<<"tkjlksjflksdjflsdjlk()
@@ -127,7 +126,9 @@ func(Config) when is_list(Config) ->
<<"t() ->
(fun() ->
true
- end)().">>}
+ end)().">>},
+ {func_7,
+ <<"t(M, F, A) -> fun M:F/A.">>}
],
?line compile(Config, Ts),
ok.
diff --git a/lib/stdlib/test/qlc_SUITE.erl b/lib/stdlib/test/qlc_SUITE.erl
index 98eeaee118..8a9d8f7883 100644
--- a/lib/stdlib/test/qlc_SUITE.erl
+++ b/lib/stdlib/test/qlc_SUITE.erl
@@ -6632,7 +6632,7 @@ otp_7232(Config) when is_list(Config) ->
{call,_,
{remote,_,{atom,_,qlc},{atom,_,sort}},
[{cons,_,
- {'fun',_,{function,math,sqrt,_}},
+ {'fun',_,{function,{atom,_,math},{atom,_,sqrt},_}},
{cons,_,
{string,_,\"<0.4.1>\"}, % could use list_to_pid..
{cons,_,{string,_,\"#Ref<\"++_},{nil,_}}}},