aboutsummaryrefslogtreecommitdiffstats
path: root/lib/stdlib
diff options
context:
space:
mode:
Diffstat (limited to 'lib/stdlib')
-rw-r--r--lib/stdlib/examples/erl_id_trans.erl2
-rw-r--r--lib/stdlib/src/epp.erl2
-rw-r--r--lib/stdlib/src/erl_eval.erl170
-rw-r--r--lib/stdlib/src/erl_expand_records.erl3
-rw-r--r--lib/stdlib/src/erl_lint.erl10
-rw-r--r--lib/stdlib/src/erl_parse.yrl12
-rw-r--r--lib/stdlib/src/erl_pp.erl20
-rw-r--r--lib/stdlib/src/ets.erl2
-rw-r--r--lib/stdlib/src/ms_transform.erl7
-rw-r--r--lib/stdlib/src/qlc.erl2
-rw-r--r--lib/stdlib/src/qlc_pt.erl16
-rw-r--r--lib/stdlib/src/shell.erl7
-rw-r--r--lib/stdlib/test/erl_eval_SUITE.erl27
-rw-r--r--lib/stdlib/test/erl_lint_SUITE.erl60
-rw-r--r--lib/stdlib/test/erl_pp_SUITE.erl27
-rw-r--r--lib/stdlib/test/ms_transform_SUITE.erl11
-rw-r--r--lib/stdlib/test/qlc_SUITE.erl14
17 files changed, 334 insertions, 58 deletions
diff --git a/lib/stdlib/examples/erl_id_trans.erl b/lib/stdlib/examples/erl_id_trans.erl
index 51def8c8e1..2c842fafc7 100644
--- a/lib/stdlib/examples/erl_id_trans.erl
+++ b/lib/stdlib/examples/erl_id_trans.erl
@@ -419,6 +419,8 @@ expr({'fun',Line,Body}) ->
A = expr(A0),
{'fun',Line,{function,M,F,A}}
end;
+expr({named_fun,Loc,Name,Cs}) ->
+ {named_fun,Loc,Name,fun_clauses(Cs)};
expr({call,Line,F0,As0}) ->
%% N.B. If F an atom then call to local function or BIF, if F a
%% remote structure (see below) then call to other module,
diff --git a/lib/stdlib/src/epp.erl b/lib/stdlib/src/epp.erl
index dd0512be4d..4fd302e612 100644
--- a/lib/stdlib/src/epp.erl
+++ b/lib/stdlib/src/epp.erl
@@ -1247,6 +1247,8 @@ macro_arg([{'case',Lc}|Toks], E, Arg) ->
macro_arg(Toks, ['end'|E], [{'case',Lc}|Arg]);
macro_arg([{'fun',Lc}|[{'(',_}|_]=Toks], E, Arg) ->
macro_arg(Toks, ['end'|E], [{'fun',Lc}|Arg]);
+macro_arg([{'fun',_}=Fun,{var,_,_}=Name|[{'(',_}|_]=Toks], E, Arg) ->
+ macro_arg(Toks, ['end'|E], [Name,Fun|Arg]);
macro_arg([{'receive',Lr}|Toks], E, Arg) ->
macro_arg(Toks, ['end'|E], [{'receive',Lr}|Arg]);
macro_arg([{'try',Lr}|Toks], E, Arg) ->
diff --git a/lib/stdlib/src/erl_eval.erl b/lib/stdlib/src/erl_eval.erl
index ca6a4b5c58..18d8148b15 100644
--- a/lib/stdlib/src/erl_eval.erl
+++ b/lib/stdlib/src/erl_eval.erl
@@ -179,8 +179,12 @@ check_command(Es, Bs) ->
fun_data(F) when is_function(F) ->
case erlang:fun_info(F, module) of
{module,erl_eval} ->
- {env, [FBs,_FEf,_FLf,FCs]} = erlang:fun_info(F, env),
- {fun_data,FBs,FCs};
+ case erlang:fun_info(F, env) of
+ {env,[{FBs,_FLf,_FEf,FCs}]} ->
+ {fun_data,FBs,FCs};
+ {env,[{FBs,_FLf,_FEf,FCs,FName}]} ->
+ {named_fun_data,FBs,FName,FCs}
+ end;
_ ->
false
end;
@@ -262,51 +266,99 @@ expr({'fun',Line,{clauses,Cs}} = Ex, Bs, Lf, Ef, RBs) ->
{Ex1, _} = hide_calls(Ex, 0),
{ok,Used} = erl_lint:used_vars([Ex1], Bs),
En = orddict:filter(fun(K,_V) -> member(K,Used) end, Bs),
+ Info = {En,Lf,Ef,Cs},
%% This is a really ugly hack!
F =
case length(element(3,hd(Cs))) of
- 0 -> fun () -> eval_fun(Cs, [], En, Lf, Ef) end;
- 1 -> fun (A) -> eval_fun(Cs, [A], En, Lf, Ef) end;
- 2 -> fun (A,B) -> eval_fun(Cs, [A,B], En, Lf, Ef) end;
- 3 -> fun (A,B,C) -> eval_fun(Cs, [A,B,C], En, Lf, Ef) end;
- 4 -> fun (A,B,C,D) -> eval_fun(Cs, [A,B,C,D], En, Lf, Ef) end;
- 5 -> fun (A,B,C,D,E) -> eval_fun(Cs, [A,B,C,D,E], En, Lf, Ef) end;
- 6 -> fun (A,B,C,D,E,F) -> eval_fun(Cs, [A,B,C,D,E,F], En, Lf, Ef) end;
- 7 -> fun (A,B,C,D,E,F,G) ->
- eval_fun(Cs, [A,B,C,D,E,F,G], En, Lf, Ef) end;
- 8 -> fun (A,B,C,D,E,F,G,H) ->
- eval_fun(Cs, [A,B,C,D,E,F,G,H], En, Lf, Ef) end;
- 9 -> fun (A,B,C,D,E,F,G,H,I) ->
- eval_fun(Cs, [A,B,C,D,E,F,G,H,I], En, Lf, Ef) end;
- 10 -> fun (A,B,C,D,E,F,G,H,I,J) ->
- eval_fun(Cs, [A,B,C,D,E,F,G,H,I,J], En, Lf, Ef) end;
- 11 -> fun (A,B,C,D,E,F,G,H,I,J,K) ->
- eval_fun(Cs, [A,B,C,D,E,F,G,H,I,J,K], En, Lf, Ef) end;
- 12 -> fun (A,B,C,D,E,F,G,H,I,J,K,L) ->
- eval_fun(Cs, [A,B,C,D,E,F,G,H,I,J,K,L], En, Lf, Ef) end;
- 13 -> fun (A,B,C,D,E,F,G,H,I,J,K,L,M) ->
- eval_fun(Cs, [A,B,C,D,E,F,G,H,I,J,K,L,M], En, Lf, Ef) end;
- 14 -> fun (A,B,C,D,E,F,G,H,I,J,K,L,M,N) ->
- eval_fun(Cs, [A,B,C,D,E,F,G,H,I,J,K,L,M,N], En, Lf, Ef) end;
- 15 -> fun (A,B,C,D,E,F,G,H,I,J,K,L,M,N,O) ->
- eval_fun(Cs, [A,B,C,D,E,F,G,H,I,J,K,L,M,N,O], En, Lf, Ef) end;
- 16 -> fun (A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P) ->
- eval_fun(Cs, [A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P], En, Lf, Ef) end;
- 17 -> fun (A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q) ->
- eval_fun(Cs, [A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q], En, Lf, Ef) end;
- 18 -> fun (A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R) ->
- eval_fun(Cs, [A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R], En, Lf, Ef) end;
- 19 -> fun (A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S) ->
- eval_fun(Cs, [A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S],
- En, Lf, Ef) end;
- 20 -> fun (A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T) ->
- eval_fun(Cs, [A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T],
- En, Lf, Ef) end;
+ 0 -> fun () -> eval_fun([], Info) end;
+ 1 -> fun (A) -> eval_fun([A], Info) end;
+ 2 -> fun (A,B) -> eval_fun([A,B], Info) end;
+ 3 -> fun (A,B,C) -> eval_fun([A,B,C], Info) end;
+ 4 -> fun (A,B,C,D) -> eval_fun([A,B,C,D], Info) end;
+ 5 -> fun (A,B,C,D,E) -> eval_fun([A,B,C,D,E], Info) end;
+ 6 -> fun (A,B,C,D,E,F) -> eval_fun([A,B,C,D,E,F], Info) end;
+ 7 -> fun (A,B,C,D,E,F,G) -> eval_fun([A,B,C,D,E,F,G], Info) end;
+ 8 -> fun (A,B,C,D,E,F,G,H) -> eval_fun([A,B,C,D,E,F,G,H], Info) end;
+ 9 -> fun (A,B,C,D,E,F,G,H,I) -> eval_fun([A,B,C,D,E,F,G,H,I], Info) end;
+ 10 -> fun (A,B,C,D,E,F,G,H,I,J) ->
+ eval_fun([A,B,C,D,E,F,G,H,I,J], Info) end;
+ 11 -> fun (A,B,C,D,E,F,G,H,I,J,K) ->
+ eval_fun([A,B,C,D,E,F,G,H,I,J,K], Info) end;
+ 12 -> fun (A,B,C,D,E,F,G,H,I,J,K,L) ->
+ eval_fun([A,B,C,D,E,F,G,H,I,J,K,L], Info) end;
+ 13 -> fun (A,B,C,D,E,F,G,H,I,J,K,L,M) ->
+ eval_fun([A,B,C,D,E,F,G,H,I,J,K,L,M], Info) end;
+ 14 -> fun (A,B,C,D,E,F,G,H,I,J,K,L,M,N) ->
+ eval_fun([A,B,C,D,E,F,G,H,I,J,K,L,M,N], Info) end;
+ 15 -> fun (A,B,C,D,E,F,G,H,I,J,K,L,M,N,O) ->
+ eval_fun([A,B,C,D,E,F,G,H,I,J,K,L,M,N,O], Info) end;
+ 16 -> fun (A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P) ->
+ eval_fun([A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P], Info) end;
+ 17 -> fun (A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q) ->
+ eval_fun([A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q], Info) end;
+ 18 -> fun (A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R) ->
+ eval_fun([A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R], Info) end;
+ 19 -> fun (A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S) ->
+ eval_fun([A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S], Info) end;
+ 20 -> fun (A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T) ->
+ eval_fun([A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T], Info) end;
_Other ->
erlang:raise(error, {'argument_limit',{'fun',Line,Cs}},
stacktrace())
end,
ret_expr(F, Bs, RBs);
+expr({named_fun,Line,Name,Cs} = Ex, Bs, Lf, Ef, RBs) ->
+ %% Save only used variables in the function environment.
+ %% {value,L,V} are hidden while lint finds used variables.
+ {Ex1, _} = hide_calls(Ex, 0),
+ {ok,Used} = erl_lint:used_vars([Ex1], Bs),
+ En = orddict:filter(fun(K,_V) -> member(K,Used) end, Bs),
+ Info = {En,Lf,Ef,Cs,Name},
+ %% This is a really ugly hack!
+ F =
+ case length(element(3,hd(Cs))) of
+ 0 -> fun RF() -> eval_named_fun([], RF, Info) end;
+ 1 -> fun RF(A) -> eval_named_fun([A], RF, Info) end;
+ 2 -> fun RF(A,B) -> eval_named_fun([A,B], RF, Info) end;
+ 3 -> fun RF(A,B,C) -> eval_named_fun([A,B,C], RF, Info) end;
+ 4 -> fun RF(A,B,C,D) -> eval_named_fun([A,B,C,D], RF, Info) end;
+ 5 -> fun RF(A,B,C,D,E) -> eval_named_fun([A,B,C,D,E], RF, Info) end;
+ 6 -> fun RF(A,B,C,D,E,F) -> eval_named_fun([A,B,C,D,E,F], RF, Info) end;
+ 7 -> fun RF(A,B,C,D,E,F,G) ->
+ eval_named_fun([A,B,C,D,E,F,G], RF, Info) end;
+ 8 -> fun RF(A,B,C,D,E,F,G,H) ->
+ eval_named_fun([A,B,C,D,E,F,G,H], RF, Info) end;
+ 9 -> fun RF(A,B,C,D,E,F,G,H,I) ->
+ eval_named_fun([A,B,C,D,E,F,G,H,I], RF, Info) end;
+ 10 -> fun RF(A,B,C,D,E,F,G,H,I,J) ->
+ eval_named_fun([A,B,C,D,E,F,G,H,I,J], RF, Info) end;
+ 11 -> fun RF(A,B,C,D,E,F,G,H,I,J,K) ->
+ eval_named_fun([A,B,C,D,E,F,G,H,I,J,K], RF, Info) end;
+ 12 -> fun RF(A,B,C,D,E,F,G,H,I,J,K,L) ->
+ eval_named_fun([A,B,C,D,E,F,G,H,I,J,K,L], RF, Info) end;
+ 13 -> fun RF(A,B,C,D,E,F,G,H,I,J,K,L,M) ->
+ eval_named_fun([A,B,C,D,E,F,G,H,I,J,K,L,M], RF, Info) end;
+ 14 -> fun RF(A,B,C,D,E,F,G,H,I,J,K,L,M,N) ->
+ eval_named_fun([A,B,C,D,E,F,G,H,I,J,K,L,M,N], RF, Info) end;
+ 15 -> fun RF(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O) ->
+ eval_named_fun([A,B,C,D,E,F,G,H,I,J,K,L,M,N,O], RF, Info) end;
+ 16 -> fun RF(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P) ->
+ eval_named_fun([A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P], RF, Info) end;
+ 17 -> fun RF(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q) ->
+ eval_named_fun([A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q], RF, Info) end;
+ 18 -> fun RF(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R) ->
+ eval_named_fun([A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R], RF, Info) end;
+ 19 -> fun RF(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S) ->
+ eval_named_fun([A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S],
+ RF, Info) end;
+ 20 -> fun RF(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T) ->
+ eval_named_fun([A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T],
+ RF, Info) end;
+ _Other ->
+ erlang:raise(error, {'argument_limit',{named_fun,Line,Name,Cs}},
+ stacktrace())
+ end,
+ ret_expr(F, Bs, RBs);
expr({call,_,{remote,_,{atom,_,qlc},{atom,_,q}},[{lc,_,_E,_Qs}=LC | As0]},
Bs0, Lf, Ef, RBs) when length(As0) =< 1 ->
%% No expansion or evaluation of module name or function name.
@@ -534,7 +586,7 @@ do_apply(Func, As, Bs0, Ef, RBs) ->
no_env
end,
case {Env,Ef} of
- {{env,[FBs, FEf, FLf, FCs]},_} ->
+ {{env,[{FBs,FLf,FEf,FCs}]},_} ->
%% If we are evaluting within another function body
%% (RBs =/= none), we return RBs when this function body
%% has been evalutated, otherwise we return Bs0, the
@@ -549,6 +601,17 @@ do_apply(Func, As, Bs0, Ef, RBs) ->
_ ->
erlang:raise(error, {badarity,{Func,As}},stacktrace())
end;
+ {{env,[{FBs,FLf,FEf,FCs,FName}]},_} ->
+ NRBs = if
+ RBs =:= none -> Bs0;
+ true -> RBs
+ end,
+ case {erlang:fun_info(Func, arity), length(As)} of
+ {{arity, Arity}, Arity} ->
+ eval_named_fun(FCs, As, FBs, FLf, FEf, FName, Func, NRBs);
+ _ ->
+ erlang:raise(error, {badarity,{Func,As}},stacktrace())
+ end;
{no_env,none} when RBs =:= value ->
%% Make tail recursive calls when possible.
apply(Func, As);
@@ -676,12 +739,12 @@ ret_expr(V, Bs, none) ->
ret_expr(V, _Bs, RBs) when is_list(RBs) ->
{value,V,RBs}.
-%% eval_fun(Clauses, Arguments, Bindings, LocalFunctionHandler,
-%% ExternalFunctionHandler) -> Value
+%% eval_fun(Arguments, {Bindings,LocalFunctionHandler,
+%% ExternalFunctionHandler,Clauses}) -> Value
%% This function is called when the fun is called from compiled code
%% or from apply.
-eval_fun(Cs, As, Bs0, Lf, Ef) ->
+eval_fun(As, {Bs0,Lf,Ef,Cs}) ->
eval_fun(Cs, As, Bs0, Lf, Ef, value).
eval_fun([{clause,_,H,G,B}|Cs], As, Bs0, Lf, Ef, RBs) ->
@@ -699,6 +762,27 @@ eval_fun([], As, _Bs, _Lf, _Ef, _RBs) ->
erlang:raise(error, function_clause,
[{?MODULE,'-inside-an-interpreted-fun-',As}|stacktrace()]).
+
+eval_named_fun(As, Fun, {Bs0,Lf,Ef,Cs,Name}) ->
+ eval_named_fun(Cs, As, Bs0, Lf, Ef, Name, Fun, value).
+
+eval_named_fun([{clause,_,H,G,B}|Cs], As, Bs0, Lf, Ef, Name, Fun, RBs) ->
+ Bs1 = add_binding(Name, Fun, Bs0),
+ case match_list(H, As, new_bindings(), Bs1) of
+ {match,Bsn} -> % The new bindings for the head
+ Bs2 = add_bindings(Bsn, Bs1), % which then shadow!
+ case guard(G, Bs2, Lf, Ef) of
+ true -> exprs(B, Bs2, Lf, Ef, RBs);
+ false -> eval_named_fun(Cs, As, Bs0, Lf, Ef, Name, Fun, RBs)
+ end;
+ nomatch ->
+ eval_named_fun(Cs, As, Bs0, Lf, Ef, Name, Fun, RBs)
+ end;
+eval_named_fun([], As, _Bs, _Lf, _Ef, _Name, _Fun, _RBs) ->
+ erlang:raise(error, function_clause,
+ [{?MODULE,'-inside-an-interpreted-fun-',As}|stacktrace()]).
+
+
%% expr_list(ExpressionList, Bindings)
%% expr_list(ExpressionList, Bindings, LocalFuncHandler)
%% expr_list(ExpressionList, Bindings, LocalFuncHandler, ExternalFuncHandler)
diff --git a/lib/stdlib/src/erl_expand_records.erl b/lib/stdlib/src/erl_expand_records.erl
index d05f630d8e..776b433613 100644
--- a/lib/stdlib/src/erl_expand_records.erl
+++ b/lib/stdlib/src/erl_expand_records.erl
@@ -344,6 +344,9 @@ expr({'fun',_,{function,_M,_F,_A}}=Fun, St) ->
expr({'fun',Line,{clauses,Cs0}}, St0) ->
{Cs,St1} = clauses(Cs0, St0),
{{'fun',Line,{clauses,Cs}},St1};
+expr({named_fun,Line,Name,Cs0}, St0) ->
+ {Cs,St1} = clauses(Cs0, St0),
+ {{named_fun,Line,Name,Cs},St1};
expr({call,Line,{atom,_,is_record},[A,{atom,_,Name}]}, St) ->
record_test(Line, A, Name, St);
expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,is_record}},
diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl
index bcf3ccef3b..cf01e1f8cf 100644
--- a/lib/stdlib/src/erl_lint.erl
+++ b/lib/stdlib/src/erl_lint.erl
@@ -2030,6 +2030,15 @@ expr({'fun',Line,Body}, Vt, St) ->
{Bvt, St1} = expr_list([M,F,A], Vt, St),
{vtupdate(Bvt, Vt),St1}
end;
+expr({named_fun,_,'_',Cs}, Vt, St) ->
+ fun_clauses(Cs, Vt, St);
+expr({named_fun,Line,Name,Cs}, Vt, St0) ->
+ Nvt0 = [{Name,{bound,unused,[Line]}}],
+ St1 = shadow_vars(Nvt0, Vt, 'named fun', St0),
+ Nvt1 = vtupdate(vtsubtract(Vt, Nvt0), Nvt0),
+ {Csvt,St2} = fun_clauses(Cs, Nvt1, St1),
+ {_,St3} = check_unused_vars(vtupdate(Csvt, Nvt0), [], St2),
+ {vtold(Csvt, Vt),St3};
expr({call,_Line,{atom,_Lr,is_record},[E,{atom,Ln,Name}]}, Vt, St0) ->
{Rvt,St1} = expr(E, Vt, St0),
{Rvt,exist_record(Ln, Name, St1)};
@@ -2182,6 +2191,7 @@ is_valid_record(Rec) ->
{lc, _, _, _} -> false;
{record_index, _, _, _} -> false;
{'fun', _, _} -> false;
+ {named_fun, _, _, _} -> false;
_ -> true
end.
diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl
index 7145b0858f..59a05a48ee 100644
--- a/lib/stdlib/src/erl_parse.yrl
+++ b/lib/stdlib/src/erl_parse.yrl
@@ -406,6 +406,9 @@ fun_clause -> argument_list clause_guard clause_body :
{Args,Pos} = '$1',
{clause,Pos,'fun',Args,'$2','$3'}.
+fun_clause -> var argument_list clause_guard clause_body :
+ {clause,element(2, '$1'),element(3, '$1'),element(1, '$2'),'$3','$4'}.
+
try_expr -> 'try' exprs 'of' cr_clauses try_catch :
build_try(?line('$1'),'$2','$4','$5').
try_expr -> 'try' exprs try_catch :
@@ -799,8 +802,15 @@ build_rule(Cs) ->
%% build_fun(Line, [Clause]) -> {'fun',Line,{clauses,[Clause]}}.
build_fun(Line, Cs) ->
+ Name = element(3, hd(Cs)),
Arity = length(element(4, hd(Cs))),
- {'fun',Line,{clauses,check_clauses(Cs, 'fun', Arity)}}.
+ CheckedCs = check_clauses(Cs, Name, Arity),
+ case Name of
+ 'fun' ->
+ {'fun',Line,{clauses,CheckedCs}};
+ Name ->
+ {named_fun,Line,Name,CheckedCs}
+ end.
check_clauses(Cs, Name, Arity) ->
mapl(fun ({clause,L,N,As,G,B}) when N =:= Name, length(As) =:= Arity ->
diff --git a/lib/stdlib/src/erl_pp.erl b/lib/stdlib/src/erl_pp.erl
index 657cb5d34c..8a1d8e0440 100644
--- a/lib/stdlib/src/erl_pp.erl
+++ b/lib/stdlib/src/erl_pp.erl
@@ -511,10 +511,17 @@ lexpr({'fun',_,{function,M,F,A}}, _Prec, Opts) ->
ArityItem = lexpr(A, Opts),
["fun ",NameItem,$:,CallItem,$/,ArityItem];
lexpr({'fun',_,{clauses,Cs}}, _Prec, Opts) ->
- {list,[{first,'fun',fun_clauses(Cs, Opts)},'end']};
+ {list,[{first,'fun',fun_clauses(Cs, Opts, unnamed)},'end']};
+lexpr({named_fun,_,Name,Cs}, _Prec, Opts) ->
+ {list,[{first,['fun', " "],fun_clauses(Cs, Opts, {named, Name})},'end']};
lexpr({'fun',_,{clauses,Cs},Extra}, _Prec, Opts) ->
{force_nl,fun_info(Extra),
- {list,[{first,'fun',fun_clauses(Cs, Opts)},'end']}};
+ {list,[{first,'fun',fun_clauses(Cs, Opts, unnamed)},'end']}};
+lexpr({named_fun,_,Name,Cs,Extra}, _Prec, Opts) ->
+ {force_nl,fun_info(Extra),
+ {list,[{first,['fun', " "],fun_clauses(Cs, Opts, {named, Name})},'end']}};
+lexpr({'query',_,Lc}, _Prec, Opts) ->
+ {list,[{step,leaf("query"),lexpr(Lc, 0, Opts)},'end']};
lexpr({call,_,{remote,_,{atom,_,M},{atom,_,F}=N}=Name,Args}, Prec, Opts) ->
case erl_internal:bif(M, F, length(Args)) of
true ->
@@ -729,8 +736,13 @@ stack_backtrace(S, El, Opts) ->
%% fun_clauses(Clauses, Opts) -> [Char].
%% Print 'fun' clauses.
-fun_clauses(Cs, Opts) ->
- nl_clauses(fun fun_clause/2, [$;], Opts, Cs).
+fun_clauses(Cs, Opts, unnamed) ->
+ nl_clauses(fun fun_clause/2, [$;], Opts, Cs);
+fun_clauses(Cs, Opts, {named, Name}) ->
+ nl_clauses(fun (C, H) ->
+ {step,Gl,Bl} = fun_clause(C, H),
+ {step,[atom_to_list(Name),Gl],Bl}
+ end, [$;], Opts, Cs).
fun_clause({clause,_,A,G,B}, Opts) ->
El = args(A, Opts),
diff --git a/lib/stdlib/src/ets.erl b/lib/stdlib/src/ets.erl
index f05bfd12a7..cc5e69f574 100644
--- a/lib/stdlib/src/ets.erl
+++ b/lib/stdlib/src/ets.erl
@@ -507,7 +507,7 @@ fun2ms(ShellFun) when is_function(ShellFun) ->
Else ->
Else
end;
- false ->
+ _ ->
exit({badarg,{?MODULE,fun2ms,
[function,called,with,real,'fun',
should,be,transformed,with,
diff --git a/lib/stdlib/src/ms_transform.erl b/lib/stdlib/src/ms_transform.erl
index 4e2ce39ec2..25b04fe45e 100644
--- a/lib/stdlib/src/ms_transform.erl
+++ b/lib/stdlib/src/ms_transform.erl
@@ -369,6 +369,13 @@ copy({var,_Line,Name} = VarDef,Bound) ->
copy({'fun',Line,{clauses,Clauses}},Bound) -> % Dont export bindings from funs
{NewClauses,_IgnoredBindings} = copy_list(Clauses,Bound),
{{'fun',Line,{clauses,NewClauses}},Bound};
+copy({named_fun,Line,Name,Clauses},Bound) -> % Dont export bindings from funs
+ Bound1 = case Name of
+ '_' -> Bound;
+ Name -> gb_sets:add(Name,Bound)
+ end,
+ {NewClauses,_IgnoredBindings} = copy_list(Clauses,Bound1),
+ {{named_fun,Line,Name,NewClauses},Bound};
copy({'case',Line,Of,ClausesList},Bound) -> % Dont export bindings from funs
{NewOf,NewBind0} = copy(Of,Bound),
{NewClausesList,NewBindings} = copy_case_clauses(ClausesList,NewBind0,[]),
diff --git a/lib/stdlib/src/qlc.erl b/lib/stdlib/src/qlc.erl
index 48f6622565..002032d48d 100644
--- a/lib/stdlib/src/qlc.erl
+++ b/lib/stdlib/src/qlc.erl
@@ -1266,6 +1266,8 @@ abstr_term(Fun, Line) when is_function(Fun) ->
case erl_eval:fun_data(Fun) of
{fun_data, _Bs, Cs} ->
{'fun', Line, {clauses, Cs}};
+ {named_fun_data, _Bs, Name, Cs} ->
+ {named_fun, Line, Name, Cs};
false ->
{name, Name} = erlang:fun_info(Fun, name),
{arity, Arity} = erlang:fun_info(Fun, arity),
diff --git a/lib/stdlib/src/qlc_pt.erl b/lib/stdlib/src/qlc_pt.erl
index 26bc4d1bdf..c26764eb18 100644
--- a/lib/stdlib/src/qlc_pt.erl
+++ b/lib/stdlib/src/qlc_pt.erl
@@ -2540,6 +2540,19 @@ nos({'fun',L,{clauses,Cs}}, S) ->
{clause,Ln,H,G,B}
end || {clause,Ln,H0,G0,B0} <- Cs],
{{'fun',L,{clauses,NCs}}, S};
+nos({named_fun,Loc,Name,Cs}, S) ->
+ {{var,NLoc,NName}, S1} = case Name of
+ '_' ->
+ S;
+ Name ->
+ nos_pattern({var,Loc,Name}, S)
+ end,
+ NCs = [begin
+ {H, S2} = nos_pattern(H0, S1),
+ {[G, B], _} = nos([G0, B0], S2),
+ {clause,CLoc,H,G,B}
+ end || {clause,CLoc,H0,G0,B0} <- Cs],
+ {{named_fun,NLoc,NName,NCs}, S};
nos({lc,L,E0,Qs0}, S) ->
%% QLCs as well as LCs. It is OK to modify LCs as long as they
%% occur within QLCs--the warning messages have already been found
@@ -2713,6 +2726,9 @@ var2const(E) ->
var_map(F, {var, _, _}=V) ->
F(V);
+var_map(F, {named_fun,NLoc,NName,Cs}) ->
+ {var,Loc,Name} = F({var,NLoc,NName}),
+ {named_fun,Loc,Name,var_map(F, Cs)};
var_map(F, T) when is_tuple(T) ->
list_to_tuple(var_map(F, tuple_to_list(T)));
var_map(F, [E | Es]) ->
diff --git a/lib/stdlib/src/shell.erl b/lib/stdlib/src/shell.erl
index 0d2fc47d13..3b90542452 100644
--- a/lib/stdlib/src/shell.erl
+++ b/lib/stdlib/src/shell.erl
@@ -424,6 +424,8 @@ expand_expr({remote,L,M,F}, C) ->
{remote,L,expand_expr(M, C),expand_expr(F, C)};
expand_expr({'fun',L,{clauses,Cs}}, C) ->
{'fun',L,{clauses,expand_exprs(Cs, C)}};
+expand_expr({named_fun,L,Name,Cs}, C) ->
+ {named_fun,L,Name,expand_exprs(Cs, C)};
expand_expr({clause,L,H,G,B}, C) ->
%% Could expand H and G, but then erl_eval has to be changed as well.
{clause,L,H, G, expand_exprs(B, C)};
@@ -1313,6 +1315,11 @@ list_bindings([{Name,Val}|Bs], RT) ->
F = {'fun',0,{clauses,FCs}},
M = {match,0,{var,0,Name},F},
io:fwrite(<<"~ts\n">>, [erl_pp:expr(M, enc())]);
+ {named_fun_data,_FBs,FName,FCs0} ->
+ FCs = expand_value(FCs0), % looks nicer
+ F = {named_fun,0,FName,FCs},
+ M = {match,0,{var,0,Name},F},
+ io:fwrite(<<"~ts\n">>, [erl_pp:expr(M, enc())]);
false ->
Namel = io_lib:fwrite(<<"~s = ">>, [Name]),
Nl = iolist_size(Namel)+1,
diff --git a/lib/stdlib/test/erl_eval_SUITE.erl b/lib/stdlib/test/erl_eval_SUITE.erl
index 7ceef727f1..c4b6b35e72 100644
--- a/lib/stdlib/test/erl_eval_SUITE.erl
+++ b/lib/stdlib/test/erl_eval_SUITE.erl
@@ -41,7 +41,8 @@
funs/1,
try_catch/1,
eval_expr_5/1,
- zero_width/1]).
+ zero_width/1,
+ eep37/1]).
%%
%% Define to run outside of test server
@@ -80,7 +81,8 @@ all() ->
pattern_expr, match_bin, guard_3, guard_4, lc,
simple_cases, unary_plus, apply_atom, otp_5269,
otp_6539, otp_6543, otp_6787, otp_6977, otp_7550,
- otp_8133, otp_10622, funs, try_catch, eval_expr_5, zero_width].
+ otp_8133, otp_10622, funs, try_catch, eval_expr_5, zero_width,
+ eep37].
groups() ->
[].
@@ -1401,6 +1403,27 @@ zero_width(Config) when is_list(Config) ->
"ok end.", ok),
ok.
+eep37(Config) when is_list(Config) ->
+ check(fun () -> (fun _(X) -> X end)(42) end,
+ "(fun _(X) -> X end)(42).",
+ 42),
+ check(fun () -> (fun _Id(X) -> X end)(42) end,
+ "(fun _Id(X) -> X end)(42).", 42),
+ check(fun () -> is_function((fun Self() -> Self end)(), 0) end,
+ "is_function((fun Self() -> Self end)(), 0).",
+ true),
+ check(fun () ->
+ F = fun Fact(N) when N > 0 ->
+ N * Fact(N - 1);
+ Fact(0) ->
+ 1
+ end,
+ F(6)
+ end,
+ "(fun Fact(N) when N > 0 -> N * Fact(N - 1); Fact(0) -> 1 end)(6).",
+ 720),
+ ok.
+
%% Check the string in different contexts: as is; in fun; from compiled code.
check(F, String, Result) ->
check1(F, String, Result),
diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl
index 6bf87adf14..a71d7f3018 100644
--- a/lib/stdlib/test/erl_lint_SUITE.erl
+++ b/lib/stdlib/test/erl_lint_SUITE.erl
@@ -575,7 +575,7 @@ unused_vars_warn_rec(Config) when is_list(Config) ->
ok.
unused_vars_warn_fun(doc) ->
- "Warnings for unused variables in records.";
+ "Warnings for unused variables in funs.";
unused_vars_warn_fun(suite) -> [];
unused_vars_warn_fun(Config) when is_list(Config) ->
Ts = [{fun1,
@@ -643,7 +643,60 @@ unused_vars_warn_fun(Config) when is_list(Config) ->
{22,erl_lint,{unused_var,'U'}},
{24,erl_lint,{unused_var,'U'}},
{26,erl_lint,{unused_var,'U'}},
- {26,erl_lint,{shadowed_var,'U','fun'}}]}}
+ {26,erl_lint,{shadowed_var,'U','fun'}}]}},
+ {named_fun,
+ <<"u() ->
+ fun U() -> foo end, % U unused.
+ U; % U unbound.
+ u() ->
+ case foo of
+ true ->
+ U = 2;
+ false ->
+ true
+ end,
+ fun U() -> foo end, % U unused.
+ U; % U unsafe.
+ u() ->
+ case foo of
+ true ->
+ U = 2;
+ false ->
+ U = 3
+ end,
+ fun U() -> foo end, % U shadowed. U unused.
+ U;
+ u() ->
+ case foo of
+ true ->
+ U = 2; % U unused.
+ false ->
+ U = 3 % U unused.
+ end,
+ fun U() -> foo end; % U shadowed. U unused.
+ u() ->
+ fun U(U) -> foo end; % U shadowed. U unused.
+ u() ->
+ fun U(1) -> U; U(U) -> foo end; % U shadowed. U unused.
+ u() ->
+ fun _(N) -> N + 1 end. % Cover handling of '_' name.
+ ">>,
+ [warn_unused_vars],
+ {error,[{3,erl_lint,{unbound_var,'U'}},
+ {12,erl_lint,{unsafe_var,'U',{'case',5}}}],
+ [{2,erl_lint,{unused_var,'U'}},
+ {11,erl_lint,{unused_var,'U'}},
+ {20,erl_lint,{unused_var,'U'}},
+ {20,erl_lint,{shadowed_var,'U','named fun'}},
+ {25,erl_lint,{unused_var,'U'}},
+ {27,erl_lint,{unused_var,'U'}},
+ {29,erl_lint,{unused_var,'U'}},
+ {29,erl_lint,{shadowed_var,'U','named fun'}},
+ {31,erl_lint,{unused_var,'U'}},
+ {31,erl_lint,{unused_var,'U'}},
+ {31,erl_lint,{shadowed_var,'U','fun'}},
+ {33,erl_lint,{unused_var,'U'}},
+ {33,erl_lint,{shadowed_var,'U','fun'}}]}}
],
?line [] = run(Config, Ts),
ok.
@@ -2201,7 +2254,8 @@ otp_5878(Config) when is_list(Config) ->
<<"-record(r1, {t = case foo of _ -> 3 end}).
-record(r2, {a = case foo of A -> A; _ -> 3 end}).
-record(r3, {a = case foo of A -> A end}).
- t() -> {#r1{},#r2{},#r3{}}.
+ -record(r4, {a = fun _AllowedFunName() -> allowed end}).
+ t() -> {#r1{},#r2{},#r3{},#r4{}}.
">>,
[warn_unused_record],
{errors,[{2,erl_lint,{variable_in_record_def,'A'}},
diff --git a/lib/stdlib/test/erl_pp_SUITE.erl b/lib/stdlib/test/erl_pp_SUITE.erl
index 70a9d70e5c..cc744ee76b 100644
--- a/lib/stdlib/test/erl_pp_SUITE.erl
+++ b/lib/stdlib/test/erl_pp_SUITE.erl
@@ -130,7 +130,27 @@ func(Config) when is_list(Config) ->
true
end)().">>},
{func_7,
- <<"t(M, F, A) -> fun M:F/A.">>}
+ <<"t(M, F, A) -> fun M:F/A.">>},
+ {func_8,
+ <<"-record(r1, {a,b}).
+ -record(r3, {a = fun Id(_) -> #r1{} end(1), b}).
+
+ t() ->
+ fun Id(A) when record(A#r3.a, r1) -> 7 end(#r3{}).
+ ">>},
+ {func_9,
+ <<"-record(r1, {a,b}).
+ -record(r3, {a = fun Id(_) -> #r1{} end(1), b}).
+
+ t() ->
+ fsdfsdfjsdfjkljf:sdlfjdsfjlf(
+ fun Id(sdfsd) -> {sdkjsdf,sdfjsdkljfsdl,sdfkjdklf} end).
+ ">>},
+ {func_10,
+ <<"t() ->
+ (fun True() ->
+ true
+ end)().">>}
],
?line compile(Config, Ts),
ok.
@@ -158,6 +178,7 @@ recs(Config) when is_list(Config) ->
-record(r1, {a,b}).
-record(r2, {a = #r1{},b,c=length([1,2,3])}).
-record(r3, {a = fun(_) -> #r1{} end(1), b}).
+ -record(r4, {a = fun R1(_) -> #r1{} end(1), b}).
t() ->
foo = fun(A) when A#r1.a > A#r1.b -> foo end(#r1{b = 2}),
@@ -741,6 +762,7 @@ neg_indent(Config) when is_list(Config) ->
?line ok = pp_expr(<<"{[a,b,c],[d,e|f]}">>),
?line ok = pp_expr(<<"f(a,b,c)">>),
?line ok = pp_expr(<<"fun() when a,b;c,d -> a end">>),
+ ?line ok = pp_expr(<<"fun A() when a,b;c,d -> a end">>),
?line ok = pp_expr(<<"<<34:32,17:32>>">>),
?line ok = pp_expr(<<"if a,b,c -> d; e,f,g -> h,i end">>),
?line ok = pp_expr(<<"if a -> d; c -> d end">>),
@@ -763,6 +785,9 @@ neg_indent(Config) when is_list(Config) ->
Fun2 = {'fun',2,{clauses,[{clause,2,[],[],[{atom,3,true}]}]},
{0,108059557,'-t/0-fun-0-'}},
?line "fun() -> true end" = flat_expr(Fun2),
+ Fun3 = {named_fun,3,'True',[{clause,3,[],[],[{atom,3,true}]}],
+ {0,424242424,'-t/0-True-0-'}},
+ ?line "fun True() -> true end" = flat_expr(Fun3),
ok.
diff --git a/lib/stdlib/test/ms_transform_SUITE.erl b/lib/stdlib/test/ms_transform_SUITE.erl
index a17307b07b..4ec13ed472 100644
--- a/lib/stdlib/test/ms_transform_SUITE.erl
+++ b/lib/stdlib/test/ms_transform_SUITE.erl
@@ -40,6 +40,7 @@
-export([action_function/1]).
-export([warnings/1]).
-export([no_warnings/1]).
+-export([eep37/1]).
-export([init_per_testcase/2, end_per_testcase/2]).
init_per_testcase(_Func, Config) ->
@@ -57,7 +58,7 @@ all() ->
record_index, multipass, bitsyntax, record_defaults,
andalso_orelse, float_1_function, action_function,
warnings, no_warnings, top_match, old_guards, autoimported,
- semicolon].
+ semicolon, eep37].
groups() ->
[].
@@ -806,6 +807,14 @@ action_function(Config) when is_list(Config) ->
ok.
+eep37(Config) when is_list(Config) ->
+ setup(Config),
+ [{'$1',[],['$1']}] =
+ compile_and_run(<<"F = fun _Ms() ->\n"
+ " ets:fun2ms(fun (X) -> X end)\n"
+ " end,\n"
+ "F()">>).
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/stdlib/test/qlc_SUITE.erl b/lib/stdlib/test/qlc_SUITE.erl
index 5f9244b479..2846657c09 100644
--- a/lib/stdlib/test/qlc_SUITE.erl
+++ b/lib/stdlib/test/qlc_SUITE.erl
@@ -76,7 +76,9 @@
manpage/1,
- backward/1, forward/1]).
+ backward/1, forward/1,
+
+ eep37/1]).
%% Internal exports.
-export([bad_table_throw/1, bad_table_exit/1, default_table/1, bad_table/1,
@@ -132,7 +134,7 @@ groups() ->
evaluator, string_to_handle, table, process_dies, sort,
keysort, filesort, cache, cache_list, filter, info,
nested_info, lookup1, lookup2, lookup_rec, indices,
- pre_fun, skip_filters]},
+ pre_fun, skip_filters, eep37]},
{table_impls, [], [ets, dets]},
{join, [],
[join_option, join_filter, join_lookup, join_merge,
@@ -7427,6 +7429,14 @@ forward(Config) when is_list(Config) ->
?line run(Config, Ts),
ok.
+eep37(Config) when is_list(Config) ->
+ Ts = [
+ <<"H = (fun _Handle() -> qlc:q([X || X <- []]) end)(),
+ [] = qlc:eval(H)">>
+ ],
+ run(Config, Ts),
+ ok.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
bad_table_throw(Tab) ->