From ff432e262e65243cbc983fcb002527f8fae8c78b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= <bjorn@erlang.org>
Date: Fri, 9 Apr 2010 15:50:17 +0200
Subject: 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
---
 lib/compiler/src/sys_pre_expand.erl             |  11 ++-
 lib/compiler/src/v3_core.erl                    |   7 ++
 lib/compiler/test/fun_SUITE.erl                 |  52 +++++++++++++-
 lib/debugger/src/dbg_ieval.erl                  |  15 ++++
 lib/debugger/src/dbg_iload.erl                  |   8 +++
 lib/debugger/test/fun_SUITE.erl                 |  52 +++++++++++++-
 lib/stdlib/examples/erl_id_trans.erl            |   9 ++-
 lib/stdlib/src/erl_eval.erl                     |   3 +-
 lib/stdlib/src/erl_lint.erl                     |   9 ++-
 lib/stdlib/src/erl_parse.yrl                    |  12 +++-
 lib/stdlib/src/erl_pp.erl                       |  10 ++-
 lib/stdlib/src/qlc.erl                          |   5 +-
 lib/stdlib/test/erl_eval_SUITE.erl              |   6 ++
 lib/stdlib/test/erl_pp_SUITE.erl                |   5 +-
 lib/stdlib/test/qlc_SUITE.erl                   |   2 +-
 lib/syntax_tools/src/erl_syntax.erl             |   7 +-
 lib/tools/src/xref_reader.erl                   |  25 ++++---
 lib/tools/test/xref_SUITE.erl                   |  87 +++++++++++++++++++++++-
 lib/tools/test/xref_SUITE_data/fun_mfa_r14.beam | Bin 0 -> 1116 bytes
 lib/tools/test/xref_SUITE_data/fun_mfa_r14.erl  |  18 +++++
 20 files changed, 309 insertions(+), 34 deletions(-)
 create mode 100644 lib/tools/test/xref_SUITE_data/fun_mfa_r14.beam
 create mode 100644 lib/tools/test/xref_SUITE_data/fun_mfa_r14.erl

(limited to 'lib')

diff --git a/lib/compiler/src/sys_pre_expand.erl b/lib/compiler/src/sys_pre_expand.erl
index 0fa1fea09f..7facbafb6d 100644
--- a/lib/compiler/src/sys_pre_expand.erl
+++ b/lib/compiler/src/sys_pre_expand.erl
@@ -554,9 +554,14 @@ fun_tq(Lf, {function,F,A}=Function, St0) ->
             {{'fun',Lf,Function,{Index,Uniq,Fname}},
              St2#expand{fun_index=Index+1}}
     end;
-fun_tq(L, {function,M,F,A}, St) ->
-    {{call,L,{remote,L,{atom,L,erlang},{atom,L,make_fun}},
-      [{atom,L,M},{atom,L,F},{integer,L,A}]},St};
+fun_tq(L, {function,M,F,A}, St) when is_atom(M), is_atom(F), is_integer(A) ->
+    %% This is the old format for external funs, generated by a pre-R15
+    %% compiler. That means that a tool, such as the debugger or xref,
+    %% directly invoked this module with the abstract code from a
+    %% pre-R15 BEAM file. Be helpful, and translate it to the new format.
+    fun_tq(L, {function,{atom,L,M},{atom,L,F},{integer,L,A}}, St);
+fun_tq(Lf, {function,_,_,_}=ExtFun, St) ->
+    {{'fun',Lf,ExtFun},St};
 fun_tq(Lf, {clauses,Cs0}, St0) ->
     Uniq = erlang:hash(Cs0, (1 bsl 27)-1),
     {Cs1,St1} = fun_clauses(Cs0, St0),
diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl
index 6f3590b156..6885405ae0 100644
--- a/lib/compiler/src/v3_core.erl
+++ b/lib/compiler/src/v3_core.erl
@@ -573,6 +573,13 @@ expr({'catch',L,E0}, St0) ->
 expr({'fun',L,{function,F,A},{_,_,_}=Id}, St) ->
     Lanno = lineno_anno(L, St),
     {#c_var{anno=Lanno++[{id,Id}],name={F,A}},[],St};
+expr({'fun',L,{function,M,F,A}}, St0) ->
+    {As,Aps,St1} = safe_list([M,F,A], St0),
+    Lanno = lineno_anno(L, St1),
+    {#icall{anno=#a{anno=Lanno},
+	    module=#c_literal{val=erlang},
+	    name=#c_literal{val=make_fun},
+	    args=As},Aps,St1};
 expr({'fun',L,{clauses,Cs},Id}, St) ->
     fun_tq(Id, Cs, L, St);
 expr({call,L,{remote,_,M,F},As0}, #core{wanted=Wanted}=St0) ->
diff --git a/lib/compiler/test/fun_SUITE.erl b/lib/compiler/test/fun_SUITE.erl
index 368a5815bf..6067ee8e06 100644
--- a/lib/compiler/test/fun_SUITE.erl
+++ b/lib/compiler/test/fun_SUITE.erl
@@ -20,7 +20,11 @@
 
 -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 
 	 init_per_group/2,end_per_group/2,
-	 test1/1,overwritten_fun/1,otp_7202/1,bif_fun/1]).
+	 test1/1,overwritten_fun/1,otp_7202/1,bif_fun/1,
+	 external/1]).
+
+%% Internal export.
+-export([call_me/1]).
 
 -include_lib("test_server/include/test_server.hrl").
 
@@ -28,7 +32,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
 
 all() -> 
     test_lib:recompile(?MODULE),
-    [test1, overwritten_fun, otp_7202, bif_fun].
+    [test1,overwritten_fun,otp_7202,bif_fun,external].
 
 groups() -> 
     [].
@@ -45,7 +49,6 @@ init_per_group(_GroupName, Config) ->
 end_per_group(_GroupName, Config) ->
     Config.
 
-
 %%% The help functions below are copied from emulator:bs_construct_SUITE.
 
 -define(T(B, L), {B, ??B, L}).
@@ -152,4 +155,47 @@ bif_fun(Config) when is_list(Config) ->
     ?line F = fun abs/1,
     ?line 5 = F(-5),
     ok.
+
+-define(APPLY(M, F, A), (fun(Fun) -> {ok,{a,b}} = Fun({a,b}) end)(fun M:F/A)).
+-define(APPLY2(M, F, A),
+	(fun(Map) ->
+		 Id = fun(I) -> I end,
+		 List = [x,y],
+		 List = Map(Id, List),
+		 {type,external} = erlang:fun_info(Map, type)
+	 end)(fun M:F/A)).
     
+external(Config) when is_list(Config) ->
+    Mod = id(?MODULE),
+    Func = id(call_me),
+    Arity = id(1),
+
+    ?APPLY(?MODULE, call_me, 1),
+    ?APPLY(?MODULE, call_me, Arity),
+    ?APPLY(?MODULE, Func, 1),
+    ?APPLY(?MODULE, Func, Arity),
+    ?APPLY(Mod, call_me, 1),
+    ?APPLY(Mod, call_me, Arity),
+    ?APPLY(Mod, Func, 1),
+    ?APPLY(Mod, Func, Arity),
+
+    ListsMod = id(lists),
+    ListsMap = id(map),
+    ListsArity = id(2),
+
+    ?APPLY2(lists, map, 2),
+    ?APPLY2(lists, map, ListsArity),
+    ?APPLY2(lists, ListsMap, 2),
+    ?APPLY2(lists, ListsMap, ListsArity),
+    ?APPLY2(ListsMod, map, 2),
+    ?APPLY2(ListsMod, map, ListsArity),
+    ?APPLY2(ListsMod, ListsMap, 2),
+    ?APPLY2(ListsMod, ListsMap, ListsArity),
+
+    ok.
+
+call_me(I) ->
+    {ok,I}.
+
+id(I) ->
+    I.
diff --git a/lib/debugger/src/dbg_ieval.erl b/lib/debugger/src/dbg_ieval.erl
index df725ed9e5..2e88c35741 100644
--- a/lib/debugger/src/dbg_ieval.erl
+++ b/lib/debugger/src/dbg_ieval.erl
@@ -768,6 +768,21 @@ expr({make_fun,Line,Name,Cs}, Bs, #ieval{module=Module}=Ieval) ->
 	end,
     {value,Fun,Bs};
 
+%% Construct an external fun.
+expr({make_ext_fun,Line,MFA0}, Bs0, Ieval0) ->
+    {[M,F,A],Bs} = eval_list(MFA0, Bs0, Ieval0),
+    try erlang:make_fun(M, F, A) of
+	Value ->
+	    {value,Value,Bs}
+    catch
+	error:badarg ->
+	    Ieval1 = Ieval0#ieval{line=Line},
+	    Ieval2 = dbg_istk:push(Bs0, Ieval1, false),
+	    Ieval = Ieval2#ieval{module=erlang,function=make_fun,
+				 arguments=[M,F,A],line=-1},
+	    exception(error, badarg, Bs, Ieval, true)
+    end;
+
 %% Common test adaptation
 expr({call_remote,0,ct_line,line,As0,Lc}, Bs0, Ieval0) ->
     {As,_Bs} = eval_list(As0, Bs0, Ieval0),
diff --git a/lib/debugger/src/dbg_iload.erl b/lib/debugger/src/dbg_iload.erl
index ce5631e45f..3c95ef8068 100644
--- a/lib/debugger/src/dbg_iload.erl
+++ b/lib/debugger/src/dbg_iload.erl
@@ -369,6 +369,14 @@ expr({'fun',Line,{function,F,A},{_Index,_OldUniq,Name}}, _Lc) ->
     As = new_vars(A, Line),
     Cs = [{clause,Line,As,[],[{local_call,Line,F,As,true}]}],
     {make_fun,Line,Name,Cs};
+expr({'fun',Line,{function,{atom,_,M},{atom,_,F},{integer,_,A}}}, _Lc)
+  when 0 =< A, A =< 255 ->
+    %% New format in R15 for fun M:F/A (literal values).
+    {value,Line,erlang:make_fun(M, F, A)};
+expr({'fun',Line,{function,M,F,A}}, _Lc) ->
+    %% New format in R15 for fun M:F/A (one or more variables).
+    MFA = expr_list([M,F,A]),
+    {make_ext_fun,Line,MFA};
 expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,self}},[]}, _Lc) ->
     {dbg,Line,self,[]};
 expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,get_stacktrace}},[]}, _Lc) ->
diff --git a/lib/debugger/test/fun_SUITE.erl b/lib/debugger/test/fun_SUITE.erl
index 8103d9c692..a06cdc7165 100644
--- a/lib/debugger/test/fun_SUITE.erl
+++ b/lib/debugger/test/fun_SUITE.erl
@@ -24,8 +24,10 @@
 	 init_per_testcase/2,end_per_testcase/2,
 	 init_per_suite/1,end_per_suite/1,
 	 good_call/1,bad_apply/1,bad_fun_call/1,badarity/1,
-	 ext_badarity/1,otp_6061/1]).
--export([nothing/0]).
+	 ext_badarity/1,otp_6061/1,external/1]).
+
+%% Internal exports.
+-export([nothing/0,call_me/1]).
 
 -include_lib("test_server/include/test_server.hrl").
 
@@ -46,7 +48,7 @@ end_per_group(_GroupName, Config) ->
 
 cases() -> 
     [good_call, bad_apply, bad_fun_call, badarity,
-     ext_badarity, otp_6061].
+     ext_badarity, otp_6061, external].
 
 init_per_testcase(_Case, Config) ->
     test_lib:interpret(?MODULE),
@@ -244,3 +246,47 @@ test_otp_6061(Starter) ->
 	       fun() -> Starter ! working end,
 	       fun() -> Starter ! not_working end],
     lists:foreach(fun(P)->(lists:nth(P,PassesF))() end,Passes).
+
+-define(APPLY(M, F, A), (fun(Fun) -> {ok,{a,b}} = Fun({a,b}) end)(fun M:F/A)).
+-define(APPLY2(M, F, A),
+	(fun(Map) ->
+		 Id = fun(I) -> I end,
+		 List = [x,y],
+		 List = Map(Id, List),
+		 {type,external} = erlang:fun_info(Map, type)
+	 end)(fun M:F/A)).
+
+external(Config) when is_list(Config) ->
+    Mod = id(?MODULE),
+    Func = id(call_me),
+    Arity = id(1),
+
+    ?APPLY(?MODULE, call_me, 1),
+    ?APPLY(?MODULE, call_me, Arity),
+    ?APPLY(?MODULE, Func, 1),
+    ?APPLY(?MODULE, Func, Arity),
+    ?APPLY(Mod, call_me, 1),
+    ?APPLY(Mod, call_me, Arity),
+    ?APPLY(Mod, Func, 1),
+    ?APPLY(Mod, Func, Arity),
+
+    ListsMod = id(lists),
+    ListsMap = id(map),
+    ListsArity = id(2),
+
+    ?APPLY2(lists, map, 2),
+    ?APPLY2(lists, map, ListsArity),
+    ?APPLY2(lists, ListsMap, 2),
+    ?APPLY2(lists, ListsMap, ListsArity),
+    ?APPLY2(ListsMod, map, 2),
+    ?APPLY2(ListsMod, map, ListsArity),
+    ?APPLY2(ListsMod, ListsMap, 2),
+    ?APPLY2(ListsMod, ListsMap, ListsArity),
+
+    ok.
+
+call_me(I) ->
+    {ok,I}.
+
+id(I) ->
+    I.
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,_}}}},
diff --git a/lib/syntax_tools/src/erl_syntax.erl b/lib/syntax_tools/src/erl_syntax.erl
index 9df5f26454..7f58fda519 100644
--- a/lib/syntax_tools/src/erl_syntax.erl
+++ b/lib/syntax_tools/src/erl_syntax.erl
@@ -6093,11 +6093,16 @@ implicit_fun_name(Node) ->
 	{'fun', Pos, {function, Atom, Arity}} ->
 	    arity_qualifier(set_pos(atom(Atom), Pos),
 			    set_pos(integer(Arity), Pos));
-	{'fun', Pos, {function, Module, Atom, Arity}} ->
+	{'fun', Pos, {function, Module, Atom, Arity}}
+	  when is_atom(Module), is_atom(Atom), is_integer(Arity) ->
+	    %% Backward compatibility with pre-R15 abstract format.
 	    module_qualifier(set_pos(atom(Module), Pos),
 			     arity_qualifier(
 			       set_pos(atom(Atom), Pos),
 			       set_pos(integer(Arity), Pos)));
+	{'fun', Pos, {function, Module, Atom, Arity}} ->
+	    %% New in R15: fun M:F/A.
+	    module_qualifier(Module, arity_qualifier(Atom, Arity));
 	Node1 ->
 	    data(Node1)
     end.
diff --git a/lib/tools/src/xref_reader.erl b/lib/tools/src/xref_reader.erl
index d22f0df164..92f0c45c7b 100644
--- a/lib/tools/src/xref_reader.erl
+++ b/lib/tools/src/xref_reader.erl
@@ -158,15 +158,20 @@ expr({'try',_Line,Es,Scs,Ccs,As}, S) ->
     S2 = clauses(Scs, S1),
     S3 = clauses(Ccs, S2),
     expr(As, S3);
-expr({call, Line,
-      {remote, _, {atom,_,erlang}, {atom,_,make_fun}},
-      [{atom,_,Mod}, {atom,_,Fun}, {integer,_,Arity}]}, S) ->
-    %% Added in R10B-6. M:F/A.
-    expr({'fun', Line, {function, Mod, Fun, Arity}}, S);
-expr({'fun', Line, {function, Mod, Name, Arity}}, S) ->
-    %% Added in R10B-6. M:F/A.
+expr({'fun', Line, {function, {atom,_,Mod},
+		    {atom,_,Name},
+		    {integer,_,Arity}}}, S) ->
+    %% New format in R15. M:F/A (literals).
     As = lists:duplicate(Arity, {atom, Line, foo}),
     external_call(Mod, Name, As, Line, false, S);
+expr({'fun', Line, {function, Mod, Name, {integer,_,Arity}}}, S) ->
+    %% New format in R15. M:F/A (one or more variables).
+    As = lists:duplicate(Arity, {atom, Line, foo}),
+    external_call(erlang, apply, [Mod, Name, list2term(As)], Line, true, S);
+expr({'fun', Line, {function, Mod, Name, _Arity}}, S) ->
+    %% New format in R15. M:F/A (one or more variables).
+    As = {var, Line, '_'},
+    external_call(erlang, apply, [Mod, Name, As], Line, true, S);
 expr({'fun', Line, {function, Name, Arity}, _Extra}, S) ->
     %% Added in R8.
     handle_call(local, S#xrefr.module, Name, Arity, Line, S);
@@ -286,10 +291,10 @@ check_funarg(W, ArgsList, Line, S) ->
     expr(ArgsList, S1).
 
 funarg({'fun', _, _Clauses, _Extra}, _S) -> true;
-funarg({var, _, Var}, S) -> member(Var, S#xrefr.funvars);
-funarg({call,_,{remote,_,{atom,_,erlang},{atom,_,make_fun}},_MFA}, _S) ->
-    %% R10B-6. M:F/A.
+funarg({'fun', _, {function,_,_,_}}, _S) ->
+    %% New abstract format for fun M:F/A in R15.
     true;
+funarg({var, _, Var}, S) -> member(Var, S#xrefr.funvars);
 funarg(_, _S) -> false.
 
 fun_args(apply2, [FunArg, Args]) -> {FunArg, Args};
diff --git a/lib/tools/test/xref_SUITE.erl b/lib/tools/test/xref_SUITE.erl
index 2f83ab4995..e0876381ca 100644
--- a/lib/tools/test/xref_SUITE.erl
+++ b/lib/tools/test/xref_SUITE.erl
@@ -46,7 +46,8 @@
 -export([
 	 add/1, default/1, info/1, lib/1, read/1, read2/1, remove/1,
 	 replace/1, update/1, deprecated/1, trycatch/1,
-         abstract_modules/1, fun_mfa/1, qlc/1]).
+         abstract_modules/1, fun_mfa/1, fun_mfa_r14/1,
+	 fun_mfa_vars/1, qlc/1]).
 
 -export([
 	 analyze/1, basic/1, md/1, q/1, variables/1, unused_locals/1]).
@@ -82,7 +83,7 @@ groups() ->
      {files, [],
       [add, default, info, lib, read, read2, remove, replace,
        update, deprecated, trycatch, abstract_modules, fun_mfa,
-       qlc]},
+       fun_mfa_r14, fun_mfa_vars, qlc]},
      {analyses, [],
       [analyze, basic, md, q, variables, unused_locals]},
      {misc, [], [format_error, otp_7423, otp_7831]}].
@@ -1771,6 +1772,88 @@ fun_mfa(Conf) when is_list(Conf) ->
     ?line ok = file:delete(Beam),
     ok.
 
+%% Same as the previous test case, except that we use a BEAM file
+%% that was compiled by an R14 compiler to test backward compatibility.
+fun_mfa_r14(Conf) when is_list(Conf) ->
+    Dir = ?config(data_dir, Conf),
+    MFile = fname(Dir, "fun_mfa_r14"),
+
+    A = fun_mfa_r14,
+    {ok, _} = xref:start(s),
+    {ok, A} = xref:add_module(s, MFile, {warnings,false}),
+    {ok, [{{{A,t,0},{'$M_EXPR','$F_EXPR',0}},[7]},
+	  {{{A,t,0},{A,t,0}},[6]},
+	  {{{A,t1,0},{'$M_EXPR','$F_EXPR',0}},[11]},
+	  {{{A,t1,0},{A,t,0}},[10]},
+	  {{{A,t2,0},{A,t,0}},[14]},
+	  {{{A,t3,0},{A,t3,0}},[17]}]} =
+        xref:q(s, "(Lin) E"),
+
+    ok = check_state(s),
+    xref:stop(s),
+
+    ok.
+
+%% fun M:F/A with varibles.
+fun_mfa_vars(Conf) when is_list(Conf) ->
+    Dir = ?copydir,
+    File = fname(Dir, "fun_mfa_vars.erl"),
+    MFile = fname(Dir, "fun_mfa_vars"),
+    Beam = fname(Dir, "fun_mfa_vars.beam"),
+    Test = <<"-module(fun_mfa_vars).
+
+              -export([t/1, t1/1, t2/3]).
+
+              t(Mod) ->
+                  F = fun Mod:bar/2,
+                  (F)(a, b).
+
+              t1(Name) ->
+                  F = fun ?MODULE:Name/1,
+                  (F)(a).
+
+              t2(Mod, Name, Arity) ->
+                  F = fun Mod:Name/Arity,
+                  (F)(a).
+
+              t3(Arity) ->
+                  F = fun ?MODULE:t/Arity,
+                  (F)(1, 2, 3).
+
+              t4(Mod, Name) ->
+                  F = fun Mod:Name/3,
+                  (F)(a, b, c).
+
+              t5(Mod, Arity) ->
+                  F = fun Mod:t/Arity,
+                  (F)().
+             ">>,
+
+    ok = file:write_file(File, Test),
+    A = fun_mfa_vars,
+    {ok, A} = compile:file(File, [report,debug_info,{outdir,Dir}]),
+    {ok, _} = xref:start(s),
+    {ok, A} = xref:add_module(s, MFile, {warnings,false}),
+    {ok, [{{{A,t,1},{'$M_EXPR','$F_EXPR',2}},[7]},
+	  {{{A,t,1},{'$M_EXPR',bar,2}},[6]},
+	  {{{A,t1,1},{'$M_EXPR','$F_EXPR',1}},[11]},
+	  {{{A,t1,1},{A,'$F_EXPR',1}},[10]},
+	  {{{A,t2,3},{'$M_EXPR','$F_EXPR',-1}},[14]},
+	  {{{A,t2,3},{'$M_EXPR','$F_EXPR',1}},[15]},
+	  {{{A,t3,1},{'$M_EXPR','$F_EXPR',3}},[19]},
+	  {{{A,t3,1},{fun_mfa_vars,t,-1}},[18]},
+	  {{{A,t4,2},{'$M_EXPR','$F_EXPR',3}},[22,23]},
+	  {{{A,t5,2},{'$M_EXPR','$F_EXPR',0}},[27]},
+	  {{{A,t5,2},{'$M_EXPR',t,-1}},[26]}]} =
+	xref:q(s, "(Lin) E"),
+
+    ok = check_state(s),
+    xref:stop(s),
+
+    ok = file:delete(File),
+    ok = file:delete(Beam),
+    ok.
+
 qlc(suite) -> [];
 qlc(doc) -> ["OTP-5195: A bug fix when using qlc:q/1,2."];
 qlc(Conf) when is_list(Conf) ->
diff --git a/lib/tools/test/xref_SUITE_data/fun_mfa_r14.beam b/lib/tools/test/xref_SUITE_data/fun_mfa_r14.beam
new file mode 100644
index 0000000000..4645525690
Binary files /dev/null and b/lib/tools/test/xref_SUITE_data/fun_mfa_r14.beam differ
diff --git a/lib/tools/test/xref_SUITE_data/fun_mfa_r14.erl b/lib/tools/test/xref_SUITE_data/fun_mfa_r14.erl
new file mode 100644
index 0000000000..293bd83a8b
--- /dev/null
+++ b/lib/tools/test/xref_SUITE_data/fun_mfa_r14.erl
@@ -0,0 +1,18 @@
+-module(fun_mfa_r14).
+
+-export([t/0, t1/0, t2/0, t3/0]).
+
+t() ->
+    F = fun ?MODULE:t/0,
+    (F)().
+
+t1() ->
+    F = fun t/0,
+    (F)().
+
+t2() ->
+    fun ?MODULE:t/0().
+
+t3() ->
+    fun t3/0().
+             
-- 
cgit v1.2.3