diff options
author | Fredrik Gustafsson <[email protected]> | 2013-01-18 10:36:49 +0100 |
---|---|---|
committer | Fredrik Gustafsson <[email protected]> | 2013-01-18 10:36:49 +0100 |
commit | 2cfa0466c3b3c7bd5e3621aff0f3e2ca30addb68 (patch) | |
tree | 39879f3e5cbf5d87ec795e7e4b0360524bcf956a | |
parent | b5e456083979a25404670feb3fb7f977cbe32d8b (diff) | |
parent | 7b0b3688ba8b921b8cc1eff39ed39a14c424d557 (diff) | |
download | otp-2cfa0466c3b3c7bd5e3621aff0f3e2ca30addb68.tar.gz otp-2cfa0466c3b3c7bd5e3621aff0f3e2ca30addb68.tar.bz2 otp-2cfa0466c3b3c7bd5e3621aff0f3e2ca30addb68.zip |
Merge branch 'nox/promote-inline_list_funcs/OTP-10690'
* nox/promote-inline_list_funcs/OTP-10690:
Raise a function_clause error with the right arguments when inlining
Properly guard against badly-typed arguments when inlining
Make inlined list functions fail with function_clause
Document compiler option 'inline_list_funcs'
Silence some wrong warnings triggered by inline_list_funcs
-rw-r--r-- | lib/compiler/doc/src/compile.xml | 26 | ||||
-rw-r--r-- | lib/compiler/src/sys_core_fold.erl | 112 | ||||
-rw-r--r-- | lib/compiler/test/inline_SUITE.erl | 43 |
3 files changed, 145 insertions, 36 deletions
diff --git a/lib/compiler/doc/src/compile.xml b/lib/compiler/doc/src/compile.xml index 27d750f929..ddaae2655d 100644 --- a/lib/compiler/doc/src/compile.xml +++ b/lib/compiler/doc/src/compile.xml @@ -816,6 +816,32 @@ pi() -> 3.1416. </section> <section> + <title>Inlining of list functions</title> + <p>The compiler can also inline a variety of list manipulation functions + from the stdlib's lists module.</p> + + <p>This feature must be explicitly enabled with a compiler option or a + <c>-compile()</c> attribute in the source module.</p> + + <p>To enable inlining of list functions, use the <c>inline_list_funcs</c> + option.</p> + + <p>The following functions are inlined:</p> + <list type="bulleted"> + <item><seealso marker="stdlib:lists#all/2">lists:all/2</seealso></item> + <item><seealso marker="stdlib:lists#any/2">lists:any/2</seealso></item> + <item><seealso marker="stdlib:lists#foreach/2">lists:foreach/2</seealso></item> + <item><seealso marker="stdlib:lists#map/2">lists:map/2</seealso></item> + <item><seealso marker="stdlib:lists#flatmap/2">lists:flatmap/2</seealso></item> + <item><seealso marker="stdlib:lists#filter/2">lists:filter/2</seealso></item> + <item><seealso marker="stdlib:lists#foldl/3">lists:foldl/3</seealso></item> + <item><seealso marker="stdlib:lists#foldr/3">lists:foldr/3</seealso></item> + <item><seealso marker="stdlib:lists#mapfoldl/3">lists:mapfoldl/3</seealso></item> + <item><seealso marker="stdlib:lists#mapfoldr/3">lists:mapfoldr/3</seealso></item> + </list> + </section> + + <section> <title>Parse Transformations</title> <p>Parse transformations are used when a programmer wants to use diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl index 18fba7962b..f17b0bd130 100644 --- a/lib/compiler/src/sys_core_fold.erl +++ b/lib/compiler/src/sys_core_fold.erl @@ -686,11 +686,14 @@ call_1(#c_call{anno=Anno}, lists, all, [Arg1,Arg2], Sub) -> C1 = #c_clause{pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true}, body=#c_case{arg=#c_apply{anno=Anno, op=F, args=[X]}, clauses = [CC1, CC2, CC3]}}, - C2 = #c_clause{pats=[#c_literal{val=[]}], guard=#c_literal{val=true}, + C2 = #c_clause{pats=[#c_literal{val=[]}], + guard=#c_call{module=#c_literal{val=erlang}, + name=#c_literal{val=is_function}, + args=[F, #c_literal{val=1}]}, body=#c_literal{val=true}}, - Err2 = #c_tuple{es=[#c_literal{val='function_clause'}, Xs]}, + Err2 = #c_tuple{es=[#c_literal{val='function_clause'}, F, Xs]}, C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true}, - body=match_fail(Anno, Err2)}, + body=match_fail([{function_name,{'lists^all',1}}|Anno], Err2)}, Fun = #c_fun{vars=[Xs], body=#c_case{arg=Xs, clauses=[C1, C2, C3]}}, L = #c_var{name='L'}, @@ -713,11 +716,14 @@ call_1(#c_call{anno=Anno}, lists, any, [Arg1,Arg2], Sub) -> C1 = #c_clause{pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true}, body=#c_case{arg=#c_apply{anno=Anno, op=F, args=[X]}, clauses = [CC1, CC2, CC3]}}, - C2 = #c_clause{pats=[#c_literal{val=[]}], guard=#c_literal{val=true}, + C2 = #c_clause{pats=[#c_literal{val=[]}], + guard=#c_call{module=#c_literal{val=erlang}, + name=#c_literal{val=is_function}, + args=[F, #c_literal{val=1}]}, body=#c_literal{val=false}}, - Err2 = #c_tuple{es=[#c_literal{val='function_clause'}, Xs]}, + Err2 = #c_tuple{es=[#c_literal{val='function_clause'}, F, Xs]}, C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true}, - body=match_fail(Anno, Err2)}, + body=match_fail([{function_name,{'lists^any',1}}|Anno], Err2)}, Fun = #c_fun{vars=[Xs], body=#c_case{arg=Xs, clauses=[C1, C2, C3]}}, L = #c_var{name='L'}, @@ -733,11 +739,14 @@ call_1(#c_call{anno=Anno}, lists, foreach, [Arg1,Arg2], Sub) -> C1 = #c_clause{pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true}, body=#c_seq{arg=#c_apply{anno=Anno, op=F, args=[X]}, body=#c_apply{anno=Anno, op=Loop, args=[Xs]}}}, - C2 = #c_clause{pats=[#c_literal{val=[]}], guard=#c_literal{val=true}, + C2 = #c_clause{pats=[#c_literal{val=[]}], + guard=#c_call{module=#c_literal{val=erlang}, + name=#c_literal{val=is_function}, + args=[F, #c_literal{val=1}]}, body=#c_literal{val=ok}}, - Err = #c_tuple{es=[#c_literal{val='function_clause'}, Xs]}, + Err = #c_tuple{es=[#c_literal{val='function_clause'}, F, Xs]}, C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true}, - body=match_fail(Anno, Err)}, + body=match_fail([{function_name,{'lists^foreach',1}}|Anno], Err)}, Fun = #c_fun{vars=[Xs], body=#c_case{arg=Xs, clauses=[C1, C2, C3]}}, L = #c_var{name='L'}, @@ -756,14 +765,18 @@ call_1(#c_call{anno=Anno}, lists, map, [Arg1,Arg2], Sub) -> op=F, args=[X]}, body=#c_cons{hd=H, + anno=[compiler_generated], tl=#c_apply{anno=Anno, op=Loop, args=[Xs]}}}}, - C2 = #c_clause{pats=[#c_literal{val=[]}], guard=#c_literal{val=true}, + C2 = #c_clause{pats=[#c_literal{val=[]}], + guard=#c_call{module=#c_literal{val=erlang}, + name=#c_literal{val=is_function}, + args=[F, #c_literal{val=1}]}, body=#c_literal{val=[]}}, - Err = #c_tuple{es=[#c_literal{val='function_clause'}, Xs]}, + Err = #c_tuple{es=[#c_literal{val='function_clause'}, F, Xs]}, C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true}, - body=match_fail(Anno, Err)}, + body=match_fail([{function_name,{'lists^map',1}}|Anno], Err)}, Fun = #c_fun{vars=[Xs], body=#c_case{arg=Xs, clauses=[C1, C2, C3]}}, L = #c_var{name='L'}, @@ -780,18 +793,21 @@ call_1(#c_call{anno=Anno}, lists, flatmap, [Arg1,Arg2], Sub) -> C1 = #c_clause{pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true}, body=#c_let{vars=[H], arg=#c_apply{anno=Anno, op=F, args=[X]}, - body=#c_call{anno=Anno, + body=#c_call{anno=[compiler_generated|Anno], module=#c_literal{val=erlang}, name=#c_literal{val='++'}, args=[H, #c_apply{anno=Anno, op=Loop, args=[Xs]}]}}}, - C2 = #c_clause{pats=[#c_literal{val=[]}], guard=#c_literal{val=true}, + C2 = #c_clause{pats=[#c_literal{val=[]}], + guard=#c_call{module=#c_literal{val=erlang}, + name=#c_literal{val=is_function}, + args=[F, #c_literal{val=1}]}, body=#c_literal{val=[]}}, - Err = #c_tuple{es=[#c_literal{val='function_clause'}, Xs]}, + Err = #c_tuple{es=[#c_literal{val='function_clause'}, F, Xs]}, C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true}, - body=match_fail(Anno, Err)}, + body=match_fail([{function_name,{'lists^flatmap',1}}|Anno], Err)}, Fun = #c_fun{vars=[Xs], body=#c_case{arg=Xs, clauses=[C1, C2, C3]}}, L = #c_var{name='L'}, @@ -807,7 +823,7 @@ call_1(#c_call{anno=Anno}, lists, filter, [Arg1,Arg2], Sub) -> B = #c_var{name='B'}, Err1 = #c_tuple{es=[#c_literal{val='case_clause'}, X]}, CC1 = #c_clause{pats=[#c_literal{val=true}], guard=#c_literal{val=true}, - body=#c_cons{hd=X, tl=Xs}}, + body=#c_cons{anno=[compiler_generated], hd=X, tl=Xs}}, CC2 = #c_clause{pats=[#c_literal{val=false}], guard=#c_literal{val=true}, body=Xs}, CC3 = #c_clause{pats=[X], guard=#c_literal{val=true}, @@ -821,11 +837,14 @@ call_1(#c_call{anno=Anno}, lists, filter, [Arg1,Arg2], Sub) -> op=Loop, args=[Xs]}, body=Case}}}, - C2 = #c_clause{pats=[#c_literal{val=[]}], guard=#c_literal{val=true}, + C2 = #c_clause{pats=[#c_literal{val=[]}], + guard=#c_call{module=#c_literal{val=erlang}, + name=#c_literal{val=is_function}, + args=[F, #c_literal{val=1}]}, body=#c_literal{val=[]}}, - Err2 = #c_tuple{es=[#c_literal{val='function_clause'}, Xs]}, + Err2 = #c_tuple{es=[#c_literal{val='function_clause'}, F, Xs]}, C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true}, - body=match_fail(Anno, Err2)}, + body=match_fail([{function_name,{'lists^filter',1}}|Anno], Err2)}, Fun = #c_fun{vars=[Xs], body=#c_case{arg=Xs, clauses=[C1, C2, C3]}}, L = #c_var{name='L'}, @@ -845,10 +864,14 @@ call_1(#c_call{anno=Anno}, lists, foldl, [Arg1,Arg2,Arg3], Sub) -> args=[Xs, #c_apply{anno=Anno, op=F, args=[X, A]}]}}, - C2 = #c_clause{pats=[#c_literal{val=[]}], guard=#c_literal{val=true}, body=A}, - Err = #c_tuple{es=[#c_literal{val='function_clause'}, Xs]}, + C2 = #c_clause{pats=[#c_literal{val=[]}], + guard=#c_call{module=#c_literal{val=erlang}, + name=#c_literal{val=is_function}, + args=[F, #c_literal{val=2}]}, + body=A}, + Err = #c_tuple{es=[#c_literal{val='function_clause'}, F, A, Xs]}, C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true}, - body=match_fail(Anno, Err)}, + body=match_fail([{function_name,{'lists^foldl',2}}|Anno], Err)}, Fun = #c_fun{vars=[Xs, A], body=#c_case{arg=Xs, clauses=[C1, C2, C3]}}, L = #c_var{name='L'}, @@ -868,10 +891,14 @@ call_1(#c_call{anno=Anno}, lists, foldr, [Arg1,Arg2,Arg3], Sub) -> args=[X, #c_apply{anno=Anno, op=Loop, args=[Xs, A]}]}}, - C2 = #c_clause{pats=[#c_literal{val=[]}], guard=#c_literal{val=true}, body=A}, - Err = #c_tuple{es=[#c_literal{val='function_clause'}, Xs]}, + C2 = #c_clause{pats=[#c_literal{val=[]}], + guard=#c_call{module=#c_literal{val=erlang}, + name=#c_literal{val=is_function}, + args=[F, #c_literal{val=2}]}, + body=A}, + Err = #c_tuple{es=[#c_literal{val='function_clause'}, F, A, Xs]}, C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true}, - body=match_fail(Anno, Err)}, + body=match_fail([{function_name,{'lists^foldr',2}}|Anno], Err)}, Fun = #c_fun{vars=[Xs, A], body=#c_case{arg=Xs, clauses=[C1, C2, C3]}}, L = #c_var{name='L'}, @@ -901,7 +928,10 @@ call_1(#c_call{anno=Anno}, lists, mapfoldl, [Arg1,Arg2,Arg3], Sub) -> op=Loop, args=[Xs, Avar]}, #c_tuple{es=[Xs, Avar]}, - #c_tuple{es=[#c_cons{hd=X, tl=Xs}, Avar]}) + #c_tuple{anno=[compiler_generated], + es=[#c_cons{anno=[compiler_generated], + hd=X, tl=Xs}, + Avar]}) %%% Multiple-value version %%% #c_let{vars=[Xs,A], %%% %% The tuple here will be optimised @@ -910,14 +940,18 @@ call_1(#c_call{anno=Anno}, lists, mapfoldl, [Arg1,Arg2,Arg3], Sub) -> %%% body=#c_values{es=[#c_cons{hd=X, tl=Xs}, %%% A]}} )}, - C2 = #c_clause{pats=[#c_literal{val=[]}], guard=#c_literal{val=true}, + C2 = #c_clause{pats=[#c_literal{val=[]}], + guard=#c_call{module=#c_literal{val=erlang}, + name=#c_literal{val=is_function}, + args=[F, #c_literal{val=2}]}, %%% Tuple passing version - body=#c_tuple{es=[#c_literal{val=[]}, Avar]}}, + body=#c_tuple{anno=[compiler_generated], + es=[#c_literal{val=[]}, Avar]}}, %%% Multiple-value version %%% body=#c_values{es=[#c_literal{val=[]}, A]}}, - Err = #c_tuple{es=[#c_literal{val='function_clause'}, Xs]}, + Err = #c_tuple{es=[#c_literal{val='function_clause'}, F, Avar, Xs]}, C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true}, - body=match_fail(Anno, Err)}, + body=match_fail([{function_name,{'lists^mapfoldl',2}}|Anno], Err)}, Fun = #c_fun{vars=[Xs, Avar], body=#c_case{arg=Xs, clauses=[C1, C2, C3]}}, L = #c_var{name='L'}, @@ -955,7 +989,9 @@ call_1(#c_call{anno=Anno}, lists, mapfoldr, [Arg1,Arg2,Arg3], Sub) -> #c_tuple{es=[Xs, Avar]}, Match(#c_apply{anno=Anno, op=F, args=[X, Avar]}, #c_tuple{es=[X, Avar]}, - #c_tuple{es=[#c_cons{hd=X, tl=Xs}, Avar]})) + #c_tuple{anno=[compiler_generated], + es=[#c_cons{anno=[compiler_generated], + hd=X, tl=Xs}, Avar]})) %%% Multiple-value version %%% body=#c_let{vars=[Xs,A], %%% %% The tuple will be optimised away @@ -965,14 +1001,18 @@ call_1(#c_call{anno=Anno}, lists, mapfoldr, [Arg1,Arg2,Arg3], Sub) -> %%% #c_values{es=[#c_cons{hd=X, tl=Xs}, %%% A]})} }, - C2 = #c_clause{pats=[#c_literal{val=[]}], guard=#c_literal{val=true}, + C2 = #c_clause{pats=[#c_literal{val=[]}], + guard=#c_call{module=#c_literal{val=erlang}, + name=#c_literal{val=is_function}, + args=[F, #c_literal{val=2}]}, %%% Tuple passing version - body=#c_tuple{es=[#c_literal{val=[]}, Avar]}}, + body=#c_tuple{anno=[compiler_generated], + es=[#c_literal{val=[]}, Avar]}}, %%% Multiple-value version %%% body=#c_values{es=[#c_literal{val=[]}, A]}}, - Err = #c_tuple{es=[#c_literal{val='function_clause'}, Xs]}, + Err = #c_tuple{es=[#c_literal{val='function_clause'}, F, Avar, Xs]}, C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true}, - body=match_fail(Anno, Err)}, + body=match_fail([{function_name,{'lists^mapfoldr',2}}|Anno], Err)}, Fun = #c_fun{vars=[Xs, Avar], body=#c_case{arg=Xs, clauses=[C1, C2, C3]}}, L = #c_var{name='L'}, diff --git a/lib/compiler/test/inline_SUITE.erl b/lib/compiler/test/inline_SUITE.erl index e2eb6a0dec..6dc7548437 100644 --- a/lib/compiler/test/inline_SUITE.erl +++ b/lib/compiler/test/inline_SUITE.erl @@ -258,6 +258,49 @@ lists(Config) when is_list(Config) -> %% Cleanup. erase(?MODULE), + + {'EXIT',{function_clause,[{?MODULE,_,[_,not_a_list],_}|_]}} = + (catch lists:map(fun (X) -> X end, not_a_list)), + {'EXIT',{function_clause,[{?MODULE,_,[_,not_a_list],_}|_]}} = + (catch lists:flatmap(fun (X) -> X end, not_a_list)), + {'EXIT',{function_clause,[{?MODULE,_,[_,not_a_list],_}|_]}} = + (catch lists:foreach(fun (X) -> X end, not_a_list)), + {'EXIT',{function_clause,[{?MODULE,_,[_,not_a_list],_}|_]}} = + (catch lists:filter(fun (_) -> true end, not_a_list)), + {'EXIT',{function_clause,[{?MODULE,_,[_,not_a_list],_}|_]}} = + (catch lists:any(fun (_) -> false end, not_a_list)), + {'EXIT',{function_clause,[{?MODULE,_,[_,not_a_list],_}|_]}} = + (catch lists:all(fun (_) -> true end, not_a_list)), + {'EXIT',{function_clause,[{?MODULE,_,[_,acc,not_a_list],_}|_]}} = + (catch lists:foldl(fun (X, Acc) -> {X,Acc} end, acc, not_a_list)), + {'EXIT',{function_clause,[{?MODULE,_,[_,acc,not_a_list],_}|_]}} = + (catch lists:foldr(fun (X, Acc) -> {X,Acc} end, acc, not_a_list)), + {'EXIT',{function_clause,[{?MODULE,_,[_,acc,not_a_list],_}|_]}} = + (catch lists:mapfoldl(fun (X, Acc) -> {X,Acc} end, acc, not_a_list)), + {'EXIT',{function_clause,[{?MODULE,_,[_,acc,not_a_list],_}|_]}} = + (catch lists:mapfoldr(fun (X, Acc) -> {X,Acc} end, acc, not_a_list)), + + {'EXIT',{function_clause,[{?MODULE,_,[not_a_function,[]],_}|_]}} = + (catch lists:map(not_a_function, [])), + {'EXIT',{function_clause,[{?MODULE,_,[not_a_function,[]],_}|_]}} = + (catch lists:flatmap(not_a_function, [])), + {'EXIT',{function_clause,[{?MODULE,_,[not_a_function,[]],_}|_]}} = + (catch lists:foreach(not_a_function, [])), + {'EXIT',{function_clause,[{?MODULE,_,[not_a_function,[]],_}|_]}} = + (catch lists:filter(not_a_function, [])), + {'EXIT',{function_clause,[{?MODULE,_,[not_a_function,[]],_}|_]}} = + (catch lists:any(not_a_function, [])), + {'EXIT',{function_clause,[{?MODULE,_,[not_a_function,[]],_}|_]}} = + (catch lists:all(not_a_function, [])), + {'EXIT',{function_clause,[{?MODULE,_,[not_a_function,acc,[]],_}|_]}} = + (catch lists:foldl(not_a_function, acc, [])), + {'EXIT',{function_clause,[{?MODULE,_,[not_a_function,acc,[]],_}|_]}} = + (catch lists:foldr(not_a_function, acc, [])), + {'EXIT',{function_clause,[{?MODULE,_,[not_a_function,acc,[]],_}|_]}} = + (catch lists:mapfoldl(not_a_function, acc, [])), + {'EXIT',{function_clause,[{?MODULE,_,[not_a_function,acc,[]],_}|_]}} = + (catch lists:mapfoldr(not_a_function, acc, [])), + ok. my_apply(M, F, A, Init) -> |