From 53bd4974a1012669fb7db09815fabac01b586c12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 21 Dec 2011 06:44:28 +0100 Subject: Eliminate the match_fail primop in v3_kernel and later passes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the v3_life pass, it is assumed that a 'match_fail' primop only occur at the top-level and at the end of a function. But this code: do_split_cases(A) -> case A of x -> Z = dummy1; _ -> Z = dummy2, a=b end, Z. will be optimized by sys_core_fold to the following code: 'split_cases'/1 = fun (_cor0) -> let <_cor7,Z> = case _cor0 of <'x'> when 'true' -> < 'dummy1','dummy1' > <_cor6> when 'true' -> %% Here follows a 'match_fail' primop inside %% multiple return values: < primop 'match_fail'({'badmatch','b'}),'dummy2' > end in Z moving the 'match_fail' primop into a "values" construction. In the future, we would like to get rid of the v3_life pass (it is there for historical reasons), so in the mean-time we prefer to not add more code to it by generalizing the handling of 'match_fail'. Since the 'match_fail' primop can be simulated by erlang:error/{1,2}, the simplest solution is to translate 'match_fail' to a call to erlang:error/{1,2} in v3_kernel and remove the handling of 'match_fail' in v3_life and v3_codegen. It is tempting to get rid of 'match_fail' also in the Core Erlang format, but there are two issues: - Removing the support for 'match_fail' completely may break tools that generate Core Erlang code. We should not do that in a minor release. - There is no easy way to generate a 'function_clause' exception that will remain correct if it will be inlined into another function. (Calling "erlang:error(function_clause, Args)" is fine only if it is not inlined into another function.) A good solution probably involves introducing new instructions, which is better done in a major release. Noticed-by: Håkan Matsson Minimized-test-case-by: Erik Søe Sørensen --- lib/compiler/src/v3_codegen.erl | 37 --------------------------------- lib/compiler/src/v3_kernel.erl | 22 ++++++++++++-------- lib/compiler/src/v3_life.erl | 34 ++---------------------------- lib/compiler/test/compilation_SUITE.erl | 16 +++++++++++++- 4 files changed, 30 insertions(+), 79 deletions(-) (limited to 'lib') diff --git a/lib/compiler/src/v3_codegen.erl b/lib/compiler/src/v3_codegen.erl index e7dae67085..6623485609 100644 --- a/lib/compiler/src/v3_codegen.erl +++ b/lib/compiler/src/v3_codegen.erl @@ -53,7 +53,6 @@ %% Main codegen structure. -record(cg, {lcount=1, %Label counter - finfo, %Function info label bfail, %Fail label for BIFs break, %Break label recv, %Receive label @@ -126,7 +125,6 @@ cg_fun(Les, Hvs, Vdb, AtomMod, NameArity, Anno, St0) -> stk=[]}, 0, Vdb), {B,_Aft,St} = cg_list(Les, 0, Vdb, Bef, St3#cg{bfail=0, - finfo=Fi, ultimate_failure=UltimateMatchFail, is_top_block=true}), {Name,Arity} = NameArity, @@ -147,8 +145,6 @@ cg({match,M,Rs}, Le, Vdb, Bef, St) -> match_cg(M, Rs, Le, Vdb, Bef, St); cg({guard_match,M,Rs}, Le, Vdb, Bef, St) -> guard_match_cg(M, Rs, Le, Vdb, Bef, St); -cg({match_fail,F}, Le, Vdb, Bef, St) -> - match_fail_cg(F, Le, Vdb, Bef, St); cg({call,Func,As,Rs}, Le, Vdb, Bef, St) -> call_cg(Func, As, Rs, Le, Vdb, Bef, St); cg({enter,Func,As}, Le, Vdb, Bef, St) -> @@ -294,39 +290,6 @@ match_cg({block,Es}, Le, _Fail, Bef, St) -> Int = clear_dead(Bef, Le#l.i, Le#l.vdb), block_cg(Es, Le, Int, St). -%% match_fail_cg(FailReason, Le, Vdb, StackReg, State) -> -%% {[Ainstr],StackReg,State}. -%% Generate code for the match_fail "call". N.B. there is no generic -%% case for when the fail value has been created elsewhere. - -match_fail_cg({function_clause,As}, Le, Vdb, Bef, St) -> - %% Must have the args in {x,0}, {x,1},... - {Sis,Int} = cg_setup_call(As, Bef, Le#l.i, Vdb), - {Sis ++ [{jump,{f,St#cg.finfo}}], - Int#sr{reg=clear_regs(Int#sr.reg)},St}; -match_fail_cg({badmatch,Term}, Le, Vdb, Bef, St) -> - R = cg_reg_arg(Term, Bef), - Int0 = clear_dead(Bef, Le#l.i, Vdb), - {Sis,Int} = adjust_stack(Int0, Le#l.i, Le#l.i+1, Vdb), - {Sis ++ [line(Le),{badmatch,R}], - Int#sr{reg=clear_regs(Int0#sr.reg)},St}; -match_fail_cg({case_clause,Reason}, Le, Vdb, Bef, St) -> - R = cg_reg_arg(Reason, Bef), - Int0 = clear_dead(Bef, Le#l.i, Vdb), - {Sis,Int} = adjust_stack(Int0, Le#l.i, Le#l.i+1, Vdb), - {Sis++[line(Le),{case_end,R}], - Int#sr{reg=clear_regs(Bef#sr.reg)},St}; -match_fail_cg(if_clause, Le, Vdb, Bef, St) -> - Int0 = clear_dead(Bef, Le#l.i, Vdb), - {Sis,Int1} = adjust_stack(Int0, Le#l.i, Le#l.i+1, Vdb), - {Sis++[line(Le),if_end],Int1#sr{reg=clear_regs(Int1#sr.reg)},St}; -match_fail_cg({try_clause,Reason}, Le, Vdb, Bef, St) -> - R = cg_reg_arg(Reason, Bef), - Int0 = clear_dead(Bef, Le#l.i, Vdb), - {Sis,Int} = adjust_stack(Int0, Le#l.i, Le#l.i+1, Vdb), - {Sis ++ [line(Le),{try_case_end,R}], - Int#sr{reg=clear_regs(Int0#sr.reg)},St}. - %% bsm_rename_ctx([Clause], Var) -> [Clause] %% We know from an annotation that the register for a binary can %% be reused for the match context because the two are not truly diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl index d76291f57f..f2eaa37617 100644 --- a/lib/compiler/src/v3_kernel.erl +++ b/lib/compiler/src/v3_kernel.erl @@ -83,6 +83,7 @@ -import(lists, [map/2,foldl/3,foldr/3,mapfoldl/3,splitwith/2,member/2, keymember/3,keyfind/3]). -import(ordsets, [add_element/2,del_element/2,union/2,union/1,subtract/2]). +-import(cerl, [c_tuple/1]). -include("core_parse.hrl"). -include("v3_kernel.hrl"). @@ -422,10 +423,11 @@ expr(#c_call{anno=A,module=M0,name=F0,args=Cargs}, Sub, St0) -> end; expr(#c_primop{anno=A,name=#c_literal{val=match_fail},args=Cargs0}, Sub, St0) -> Cargs = translate_match_fail(Cargs0, Sub, A, St0), - %% This special case will disappear. {Kargs,Ap,St} = atomic_list(Cargs, Sub, St0), Ar = length(Cargs), - Call = #k_call{anno=A,op=#k_internal{name=match_fail,arity=Ar},args=Kargs}, + Call = #k_call{anno=A,op=#k_remote{mod=#k_atom{val=erlang}, + name=#k_atom{val=error}, + arity=Ar},args=Kargs}, {Call,Ap,St}; expr(#c_primop{anno=A,name=#c_literal{val=N},args=Cargs}, Sub, St0) -> {Kargs,Ap,St1} = atomic_list(Cargs, Sub, St0), @@ -455,14 +457,14 @@ expr(#ireceive_accept{anno=A}, _Sub, St) -> {#k_receive_accept{anno=A},[],St}. translate_match_fail(Args, Sub, Anno, St) -> case Args of [#c_tuple{es=[#c_literal{val=function_clause}|As]}] -> - translate_match_fail_1(Anno, Args, As, Sub, St); + translate_match_fail_1(Anno, As, Sub, St); [#c_literal{val=Tuple}] when is_tuple(Tuple) -> %% The inliner may have created a literal out of %% the original #c_tuple{}. case tuple_to_list(Tuple) of [function_clause|As0] -> As = [#c_literal{val=E} || E <- As0], - translate_match_fail_1(Anno, Args, As, Sub, St); + translate_match_fail_1(Anno, As, Sub, St); _ -> Args end; @@ -471,7 +473,7 @@ translate_match_fail(Args, Sub, Anno, St) -> Args end. -translate_match_fail_1(Anno, Args, As, Sub, #kern{ff=FF}) -> +translate_match_fail_1(Anno, As, Sub, #kern{ff=FF}) -> AnnoFunc = case keyfind(function_name, 1, Anno) of false -> none; %Force rewrite. @@ -481,10 +483,10 @@ translate_match_fail_1(Anno, Args, As, Sub, #kern{ff=FF}) -> case {AnnoFunc,FF} of {Same,Same} -> %% Still in the correct function. - Args; + translate_fc(As); {{F,_},F} -> %% Still in the correct function. - Args; + translate_fc(As); _ -> %% Wrong function or no function_name annotation. %% @@ -493,9 +495,12 @@ translate_match_fail_1(Anno, Args, As, Sub, #kern{ff=FF}) -> %% the current function). match_fail(function_clause) will %% only work at the top level of the function it was originally %% defined in, so we will need to rewrite it to a case_clause. - [#c_tuple{es=[#c_literal{val=case_clause},#c_tuple{es=As}]}] + [c_tuple([#c_literal{val=case_clause},c_tuple(As)])] end. +translate_fc(Args) -> + [#c_literal{val=function_clause},make_list(Args)]. + %% call_type(Module, Function, Arity) -> call | bif | apply | error. %% Classify the call. call_type(#c_literal{val=M}, #c_literal{val=F}, Ar) when is_atom(M), is_atom(F) -> @@ -1494,7 +1499,6 @@ iletrec_funs_gen(Fs, FreeVs, St) -> %% is_exit_expr(Kexpr) -> boolean(). %% Test whether Kexpr always exits and never returns. -is_exit_expr(#k_call{op=#k_internal{name=match_fail,arity=1}}) -> true; is_exit_expr(#k_receive_next{}) -> true; is_exit_expr(_) -> false. diff --git a/lib/compiler/src/v3_life.erl b/lib/compiler/src/v3_life.erl index fac9a9843d..93f8034230 100644 --- a/lib/compiler/src/v3_life.erl +++ b/lib/compiler/src/v3_life.erl @@ -89,19 +89,8 @@ function(#k_fdef{anno=#k{a=Anno},func=F,arity=Ar,vars=Vs,body=Kb}) -> end. %% body(Kbody, I, Vdb) -> {[Expr],MaxI,Vdb}. -%% Handle a body, need special cases for transforming match_fails. -%% We KNOW that they only occur last in a body. - -body(#k_seq{arg=#k_put{anno=Pa,arg=Arg,ret=[R]}, - body=#k_enter{anno=Ea,op=#k_internal{name=match_fail,arity=1}, - args=[R]}}, - I, Vdb0) -> - Vdb1 = use_vars(Pa#k.us, I, Vdb0), %All used here - {[match_fail(Arg, I, Pa#k.a ++ Ea#k.a)],I,Vdb1}; -body(#k_enter{anno=Ea,op=#k_internal{name=match_fail,arity=1},args=[Arg]}, - I, Vdb0) -> - Vdb1 = use_vars(Ea#k.us, I, Vdb0), - {[match_fail(Arg, I, Ea#k.a)],I,Vdb1}; +%% Handle a body. + body(#k_seq{arg=Ke,body=Kb}, I, Vdb0) -> %%ok = io:fwrite("life ~w:~p~n", [?LINE,{Ke,I,Vdb0}]), A = get_kanno(Ke), @@ -353,25 +342,6 @@ guard_clause(#k_guard_clause{anno=A,guard=Kg,body=Kb}, Ls, I, Ctxt, Vdb0) -> i=I,vdb=use_vars((get_kanno(Kg))#k.us, I+2, Vdb1), a=A#k.a}. -%% match_fail(FailValue, I, Anno) -> Expr. -%% Generate the correct match_fail instruction. N.B. there is no -%% generic case for when the fail value has been created elsewhere. - -match_fail(#k_literal{anno=Anno,val={Atom,Val}}, I, A) when is_atom(Atom) -> - match_fail(#k_tuple{anno=Anno,es=[#k_atom{val=Atom},#k_literal{val=Val}]}, I, A); -match_fail(#k_literal{anno=Anno,val={Atom}}, I, A) when is_atom(Atom) -> - match_fail(#k_tuple{anno=Anno,es=[#k_atom{val=Atom}]}, I, A); -match_fail(#k_tuple{es=[#k_atom{val=function_clause}|As]}, I, A) -> - #l{ke={match_fail,{function_clause,literal_list(As, [])}},i=I,a=A}; -match_fail(#k_tuple{es=[#k_atom{val=badmatch},Val]}, I, A) -> - #l{ke={match_fail,{badmatch,literal(Val, [])}},i=I,a=A}; -match_fail(#k_tuple{es=[#k_atom{val=case_clause},Val]}, I, A) -> - #l{ke={match_fail,{case_clause,literal(Val, [])}},i=I,a=A}; -match_fail(#k_atom{val=if_clause}, I, A) -> - #l{ke={match_fail,if_clause},i=I,a=A}; -match_fail(#k_tuple{es=[#k_atom{val=try_clause},Val]}, I, A) -> - #l{ke={match_fail,{try_clause,literal(Val, [])}},i=I,a=A}. - %% type(Ktype) -> Type. type(k_literal) -> literal; diff --git a/lib/compiler/test/compilation_SUITE.erl b/lib/compiler/test/compilation_SUITE.erl index 1343fbd1c9..1f17664b54 100644 --- a/lib/compiler/test/compilation_SUITE.erl +++ b/lib/compiler/test/compilation_SUITE.erl @@ -44,7 +44,7 @@ all() -> trycatch_4, opt_crash, otp_5404, otp_5436, otp_5481, otp_5553, otp_5632, otp_5714, otp_5872, otp_6121, otp_6121a, otp_6121b, otp_7202, otp_7345, on_load, - string_table,otp_8949_a,otp_8949_a]. + string_table,otp_8949_a,otp_8949_a,split_cases]. groups() -> [{vsn, [], [vsn_1, vsn_2, vsn_3]}]. @@ -657,5 +657,19 @@ otp_8949_b(A, B) -> id(Var) end. +split_cases(_) -> + dummy1 = do_split_cases(x), + {'EXIT',{{badmatch,b},_}} = (catch do_split_cases(y)), + ok. + +do_split_cases(A) -> + case A of + x -> + Z = dummy1; + _ -> + Z = dummy2, + a=b + end, + Z. id(I) -> I. -- cgit v1.2.3 From 726f6e4c7afe8ce37b30eedbebe583e7b9bfc51b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 21 Dec 2011 07:41:48 +0100 Subject: Add the beam_except pass to optimize exceptions In order to save space, rewrite suitable calls to erlang:error/{1,2} to special BEAM instructions. This code is probably longer than the code taken out of v3_life and v3_codegen in the previous commit, but it is much easier to understand and maintain since the BEAM assembler format is better understood than the v3_life format. --- lib/compiler/src/Makefile | 1 + lib/compiler/src/beam_except.erl | 149 ++++++++++++++++++++++++++++++++ lib/compiler/src/compile.erl | 2 + lib/compiler/src/compiler.app.src | 1 + lib/compiler/test/Makefile | 2 + lib/compiler/test/beam_expect_SUITE.erl | 67 ++++++++++++++ lib/compiler/test/misc_SUITE.erl | 20 ++++- 7 files changed, 240 insertions(+), 2 deletions(-) create mode 100644 lib/compiler/src/beam_except.erl create mode 100644 lib/compiler/test/beam_expect_SUITE.erl (limited to 'lib') diff --git a/lib/compiler/src/Makefile b/lib/compiler/src/Makefile index 7a237608ad..3415517fff 100644 --- a/lib/compiler/src/Makefile +++ b/lib/compiler/src/Makefile @@ -53,6 +53,7 @@ MODULES = \ beam_dead \ beam_dict \ beam_disasm \ + beam_except \ beam_flatten \ beam_jump \ beam_listing \ diff --git a/lib/compiler/src/beam_except.erl b/lib/compiler/src/beam_except.erl new file mode 100644 index 0000000000..fb1a43cd9e --- /dev/null +++ b/lib/compiler/src/beam_except.erl @@ -0,0 +1,149 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2011. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +-module(beam_except). +-export([module/2]). + +%%% Rewrite certain calls to erlang:error/{1,2} to specialized +%%% instructions: +%%% +%%% erlang:error({badmatch,Value}) => badmatch Value +%%% erlang:error({case_clause,Value}) => case_end Value +%%% erlang:error({try_clause,Value}) => try_case_end Value +%%% erlang:error(if_clause) => if_end +%%% erlang:error(function_clause, Args) => jump FuncInfoLabel +%%% + +-import(lists, [reverse/1]). + +module({Mod,Exp,Attr,Fs0,Lc}, _Opt) -> + Fs = [function(F) || F <- Fs0], + {ok,{Mod,Exp,Attr,Fs,Lc}}. + +function({function,Name,Arity,CLabel,Is0}) -> + try + Is = function_1(Is0), + {function,Name,Arity,CLabel,Is} + catch + Class:Error -> + Stack = erlang:get_stacktrace(), + io:fwrite("Function: ~w/~w\n", [Name,Arity]), + erlang:raise(Class, Error, Stack) + end. + +-record(st, + {lbl, %func_info label + loc %location for func_info + }). + +function_1(Is0) -> + case Is0 of + [{label,Lbl},{line,Loc}|_] -> + St = #st{lbl=Lbl,loc=Loc}, + translate(Is0, St, []); + [{label,_}|_] -> + %% No line numbers. The source must be a .S file. + %% There is no need to do anything. + Is0 + end. + +translate([{call_ext,Ar,{extfunc,erlang,error,Ar}}=I|Is], St, Acc) -> + translate_1(Ar, I, Is, St, Acc); +translate([{call_ext_only,Ar,{extfunc,erlang,error,Ar}}=I|Is], St, Acc) -> + translate_1(Ar, I, Is, St, Acc); +translate([{call_ext_last,Ar,{extfunc,erlang,error,Ar},_}=I|Is], St, Acc) -> + translate_1(Ar, I, Is, St, Acc); +translate([I|Is], St, Acc) -> + translate(Is, St, [I|Acc]); +translate([], _, Acc) -> + reverse(Acc). + +translate_1(Ar, I, Is, St, [{line,_}=Line|Acc1]=Acc0) -> + case dig_out(Ar, Acc1) of + no -> + translate(Is, St, [I|Acc0]); + {yes,function_clause,Acc2} -> + case {Line,St} of + {{line,Loc},#st{lbl=Fi,loc=Loc}} -> + Instr = {jump,{f,Fi}}, + translate(Is, St, [Instr|Acc2]); + {_,_} -> + %% This must be "error(function_clause, Args)" in + %% the Erlang source code. Don't translate. + translate(Is, St, [I|Acc0]) + end; + {yes,Instr,Acc2} -> + translate(Is, St, [Instr,Line|Acc2]) + end. + +dig_out(Ar, [{kill,_}|Is]) -> + dig_out(Ar, Is); +dig_out(1, [{block,Bl0}|Is]) -> + case dig_out_block(reverse(Bl0)) of + no -> no; + {yes,What,[]} -> + {yes,What,Is}; + {yes,What,Bl} -> + {yes,What,[{block,Bl}|Is]} + end; +dig_out(2, [{block,Bl}|Is]) -> + case dig_out_block_fc(Bl) of + no -> no; + {yes,What} -> {yes,What,Is} + end; +dig_out(_, _) -> no. + +dig_out_block([{set,[{x,0}],[{atom,if_clause}],move}]) -> + {yes,if_end,[]}; +dig_out_block([{set,[{x,0}],[{literal,{Exc,Value}}],move}|Is]) -> + translate_exception(Exc, {literal,Value}, Is, 0); +dig_out_block([{set,[{x,0}],[Tuple],move}, + {set,[],[Value],put}, + {set,[],[{atom,Exc}],put}, + {set,[Tuple],[],{put_tuple,2}}|Is]) -> + translate_exception(Exc, Value, Is, 3); +dig_out_block([{set,[],[Value],put}, + {set,[],[{atom,Exc}],put}, + {set,[{x,0}],[],{put_tuple,2}}|Is]) -> + translate_exception(Exc, Value, Is, 3); +dig_out_block(_) -> no. + +translate_exception(badmatch, Val, Is, Words) -> + {yes,{badmatch,Val},fix_block(Is, Words)}; +translate_exception(case_clause, Val, Is, Words) -> + {yes,{case_end,Val},fix_block(Is, Words)}; +translate_exception(try_clause, Val, Is, Words) -> + {yes,{try_case_end,Val},fix_block(Is, Words)}; +translate_exception(_, _, _, _) -> no. + +fix_block(Is, 0) -> + reverse(Is); +fix_block(Is0, Words) -> + [{set,[],[],{alloc,Live,{F1,F2,Needed,F3}}}|Is] = reverse(Is0), + [{set,[],[],{alloc,Live,{F1,F2,Needed-Words,F3}}}|Is]. + +dig_out_block_fc([{set,[],[],{alloc,Live,_}}|Bl]) -> + dig_out_fc(Bl, Live-1, nil); +dig_out_block_fc(_) -> no. + +dig_out_fc([{set,[Dst],[{x,Reg},Dst0],put_list}|Is], Reg, Dst0) -> + dig_out_fc(Is, Reg-1, Dst); +dig_out_fc([{set,[{x,0}],[{atom,function_clause}],move}], -1, {x,1}) -> + {yes,function_clause}; +dig_out_fc(_, _, _) -> no. diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl index a17a10046e..9b505ad15c 100644 --- a/lib/compiler/src/compile.erl +++ b/lib/compiler/src/compile.erl @@ -629,6 +629,8 @@ asm_passes() -> [{unless,no_postopt, [{pass,beam_block}, {iff,dblk,{listing,"block"}}, + {unless,no_except,{pass,beam_except}}, + {iff,dexcept,{listing,"except"}}, {unless,no_bopt,{pass,beam_bool}}, {iff,dbool,{listing,"bool"}}, {unless,no_topt,{pass,beam_type}}, diff --git a/lib/compiler/src/compiler.app.src b/lib/compiler/src/compiler.app.src index fb06f2521c..1133882728 100644 --- a/lib/compiler/src/compiler.app.src +++ b/lib/compiler/src/compiler.app.src @@ -28,6 +28,7 @@ beam_dead, beam_dict, beam_disasm, + beam_except, beam_flatten, beam_jump, beam_listing, diff --git a/lib/compiler/test/Makefile b/lib/compiler/test/Makefile index b90adaf917..2ea50b9112 100644 --- a/lib/compiler/test/Makefile +++ b/lib/compiler/test/Makefile @@ -10,6 +10,7 @@ MODULES= \ apply_SUITE \ beam_validator_SUITE \ beam_disasm_SUITE \ + beam_expect_SUITE \ bs_bincomp_SUITE \ bs_bit_binaries_SUITE \ bs_construct_SUITE \ @@ -39,6 +40,7 @@ MODULES= \ NO_OPT= \ andor \ apply \ + beam_expect \ bs_construct \ bs_match \ bs_utf \ diff --git a/lib/compiler/test/beam_expect_SUITE.erl b/lib/compiler/test/beam_expect_SUITE.erl new file mode 100644 index 0000000000..6f216eac4f --- /dev/null +++ b/lib/compiler/test/beam_expect_SUITE.erl @@ -0,0 +1,67 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2011. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +-module(beam_expect_SUITE). + +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2, + coverage/1]). + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [coverage]. + +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + +coverage(_) -> + File = {file,"fake.erl"}, + ok = fc(a), + {'EXIT',{function_clause, + [{?MODULE,fc,[[x]],[File,{line,2}]}|_]}} = + (catch fc([x])), + {'EXIT',{function_clause, + [{?MODULE,fc,[y],[File,{line,2}]}|_]}} = + (catch fc(y)), + {'EXIT',{function_clause, + [{?MODULE,fc,[[a,b,c]],[File,{line,6}]}|_]}} = + (catch fc([a,b,c])), + + {'EXIT',{undef,[{erlang,error,[a,b,c],_}|_]}} = + (catch erlang:error(a, b, c)), + ok. + +-file("fake.erl", 1). +fc(a) -> %Line 2 + ok; %Line 3 +fc(L) when length(L) > 2 -> %Line 4 + %% Not the same as a "real" function_clause error. + error(function_clause, [L]). %Line 6 diff --git a/lib/compiler/test/misc_SUITE.erl b/lib/compiler/test/misc_SUITE.erl index 9b414cade6..5e13a93c52 100644 --- a/lib/compiler/test/misc_SUITE.erl +++ b/lib/compiler/test/misc_SUITE.erl @@ -190,6 +190,15 @@ silly_coverage(Config) when is_list(Config) -> {label,2}|non_proper_list]}],99}, ?line expect_error(fun() -> beam_block:module(BlockInput, []) end), + %% beam_except + ExceptInput = {?MODULE,[{foo,0}],[], + [{function,foo,0,2, + [{label,1}, + {line,loc}, + {func_info,{atom,?MODULE},{atom,foo},0}, + {label,2}|non_proper_list]}],99}, + expect_error(fun() -> beam_except:module(ExceptInput, []) end), + %% beam_bool BoolInput = {?MODULE,[{foo,0}],[], [{function,foo,0,2, @@ -253,8 +262,15 @@ expect_error(Fun) -> io:format("~p", [Any]), ?t:fail(call_was_supposed_to_fail) catch - _:_ -> - io:format("~p\n", [erlang:get_stacktrace()]) + Class:Reason -> + Stk = erlang:get_stacktrace(), + io:format("~p:~p\n~p\n", [Class,Reason,Stk]), + case {Class,Reason} of + {error,undef} -> + ?t:fail(not_supposed_to_fail_with_undef); + {_,_} -> + ok + end end. confused_literals(Config) when is_list(Config) -> -- cgit v1.2.3