%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 2003-2018. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, software %% distributed under the License is distributed on an "AS IS" BASIS, %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. %% %% %CopyrightEnd% %% -module(trycatch_SUITE). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2,basic/1,lean_throw/1, try_of/1,try_after/1,%after_bind/1, catch_oops/1,after_oops/1,eclectic/1,rethrow/1, nested_of/1,nested_catch/1,nested_after/1, nested_horrid/1,last_call_optimization/1,bool/1, plain_catch_coverage/1,andalso_orelse/1,get_in_try/1, hockey/1,handle_info/1,catch_in_catch/1,grab_bag/1, stacktrace/1,nested_stacktrace/1,raise/1]). -include_lib("common_test/include/ct.hrl"). suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> test_lib:recompile(?MODULE), [{group,p}]. groups() -> [{p,[parallel], [basic,lean_throw,try_of,try_after,catch_oops, after_oops,eclectic,rethrow,nested_of,nested_catch, nested_after,nested_horrid,last_call_optimization, bool,plain_catch_coverage,andalso_orelse,get_in_try, hockey,handle_info,catch_in_catch,grab_bag, stacktrace,nested_stacktrace,raise]}]. init_per_suite(Config) -> Config. end_per_suite(_Config) -> ok. init_per_group(_GroupName, Config) -> Config. end_per_group(_GroupName, Config) -> Config. basic(Conf) when is_list(Conf) -> 2 = try my_div(4, 2) catch Class:Reason -> {Class,Reason} end, error = try my_div(1, 0) catch error:badarith -> error end, error = try 1.0 / zero() catch error:badarith -> error end, ok = try my_add(53, atom) catch error:badarith -> ok end, exit_nisse = try exit(nisse) catch exit:nisse -> exit_nisse end, ok = try throw(kalle) catch kalle -> ok end, %% Try some stuff where the compiler will optimize away the try. V = id({a,variable}), V = try V catch nisse -> error end, 42 = try 42 catch nisse -> error end, [V] = try [V] catch nisse -> error end, {ok,V} = try {ok,V} catch nisse -> error end, %% Same idea, but use an after too. V = try V catch nisse -> error after after_call() end, after_clean(), 42 = try 42 after after_call() end, after_clean(), [V] = try [V] catch nisse -> error after after_call() end, after_clean(), {ok,V} = try {ok,V} after after_call() end, %% Try/of ok = try V of {a,variable} -> ok catch nisse -> erro end, %% Unmatchable clauses. try throw(thrown) catch {a,b}={a,b,c} -> %Intentionally no match. ok; thrown -> ok end, ok. after_call() -> put(basic, after_was_called). after_clean() -> after_was_called = erase(basic). lean_throw(Conf) when is_list(Conf) -> {throw,kalle} = try throw(kalle) catch Kalle -> {throw,Kalle} end, {exit,kalle} = try exit(kalle) catch Throw1 -> {throw,Throw1}; exit:Reason1 -> {exit,Reason1} end, {exit,kalle} = try exit(kalle) catch exit:Reason2 -> {exit,Reason2}; Throw2 -> {throw,Throw2} end, {exit,kalle} = try try exit(kalle) catch Throw3 -> {throw,Throw3} end catch exit:Reason3 -> {exit,Reason3} end, ok. try_of(Conf) when is_list(Conf) -> {ok,{some,content}} = try_of_1({value,{good,{some,content}}}), {error,[other,content]} = try_of_1({value,{bad,[other,content]}}), {caught,{exit,{ex,it,[reason]}}} = try_of_1({exit,{ex,it,[reason]}}), {caught,{throw,[term,{in,a,{tuple}}]}} = try_of_1({throw,[term,{in,a,{tuple}}]}), {caught,{error,[bad,arg]}} = try_of_1({error,[bad,arg]}), {caught,{error,badarith}} = try_of_1({'div',{1,0}}), {caught,{error,badarith}} = try_of_1({'add',{a,0}}), {caught,{error,badarg}} = try_of_1({'abs',x}), {caught,{error,function_clause}} = try_of_1(illegal), {error,{try_clause,{some,other_garbage}}} = try try_of_1({value,{some,other_garbage}}) catch error:Reason -> {error,Reason} end, ok. try_of_1(X) -> try foo(X) of {good,Y} -> {ok,Y}; {bad,Y} -> {error,Y} catch Class:Reason -> {caught,{Class,Reason}} end. try_after(Conf) when is_list(Conf) -> {{ok,[some,value],undefined},finalized} = try_after_1({value,{ok,[some,value]}},finalized), {{error,badarith,undefined},finalized} = try_after_1({'div',{1,0}},finalized), {{error,badarith,undefined},finalized} = try_after_1({'add',{1,a}},finalized), {{error,badarg,undefined},finalized} = try_after_1({'abs',a},finalized), {{error,[the,{reason}],undefined},finalized} = try_after_1({error,[the,{reason}]},finalized), {{throw,{thrown,[reason]},undefined},finalized} = try_after_1({throw,{thrown,[reason]}},finalized), {{exit,{exited,{reason}},undefined},finalized} = try_after_1({exit,{exited,{reason}}},finalized), {{error,function_clause,undefined},finalized} = try_after_1(function_clause,finalized), ok = try try_after_1({'add',{1,1}}, finalized) catch error:{try_clause,2} -> ok end, finalized = erase(try_after), ok = try try foo({exit,[reaso,{n}]}) after put(try_after, finalized) end catch exit:[reaso,{n}] -> ok end, ok. try_after_1(X, Y) -> erase(try_after), Try = try foo(X) of {ok,Value} -> {ok,Value,get(try_after)} catch Reason -> {throw,Reason,get(try_after)}; error:Reason -> {error,Reason,get(try_after)}; exit:Reason -> {exit,Reason,get(try_after)} after put(try_after, Y) end, {Try,erase(try_after)}. -ifdef(begone). after_bind(Conf) when is_list(Conf) -> V = [make_ref(),self()|value], {value,{value,V}} = after_bind_1({value,V}, V, {value,V}), ok. after_bind_1(X, V, Y) -> try Try = try foo(X) of V -> value catch C1:V -> {caught,C1} after After = foo(Y) end, {Try,After} of V -> {value,V} catch C:D -> {caught,{C,D}} end. -endif. catch_oops(Conf) when is_list(Conf) -> V = {v,[a,l|u],{e},self()}, {value,V} = catch_oops_1({value,V}), {value,1} = catch_oops_1({'div',{1,1}}), {error,badarith} = catch_oops_1({'div',{1,0}}), {error,function_clause} = catch_oops_1(function_clause), {throw,V} = catch_oops_1({throw,V}), {exit,V} = catch_oops_1({exit,V}), ok. catch_oops_1(X) -> Ref = make_ref(), try try foo({error,Ref}) catch error:Ref -> foo(X) end of Value -> {value,Value} catch Class:Data -> {Class,Data} end. after_oops(Conf) when is_list(Conf) -> V = {self(),make_ref()}, {{value,V},V} = after_oops_1({value,V}, {value,V}), {{exit,V},V} = after_oops_1({exit,V}, {value,V}), {{error,V},undefined} = after_oops_1({value,V}, {error,V}), {{error,function_clause},undefined} = after_oops_1({exit,V}, function_clause), ok. after_oops_1(X, Y) -> erase(after_oops), Try = try try foo(X) after put(after_oops, foo(Y)) end of V -> {value,V} catch C:D -> {C,D} end, {Try,erase(after_oops)}. eclectic(Conf) when is_list(Conf) -> V = {make_ref(),3.1415926535,[[]|{}]}, {{value,{value,V},V},V} = eclectic_1({foo,{value,{value,V}}}, undefined, {value,V}), {{'EXIT',{V,[{?MODULE,foo,1,_}|_]}},V} = eclectic_1({catch_foo,{error,V}}, undefined, {value,V}), {{error,{exit,V},{'EXIT',V}},V} = eclectic_1({foo,{error,{exit,V}}}, error, {value,V}), {{value,{value,V},V}, {'EXIT',{badarith,[{erlang,'+',[0,a],_},{?MODULE,my_add,2,_}|_]}}} = eclectic_1({foo,{value,{value,V}}}, undefined, {'add',{0,a}}), {{'EXIT',V},V} = eclectic_1({catch_foo,{exit,V}}, undefined, {throw,V}), {{error,{'div',{1,0}},{'EXIT',{badarith,[{erlang,'div',[1,0],_},{?MODULE,my_div,2,_}|_]}}}, {'EXIT',V}} = eclectic_1({foo,{error,{'div',{1,0}}}}, error, {exit,V}), {{{error,V},{'EXIT',{V,[{?MODULE,foo,1,_}|_]}}}, {'EXIT',V}} = eclectic_1({catch_foo,{throw,{error,V}}}, undefined, {exit,V}), %% {{value,{value,{value,V},V}},V} = eclectic_2({value,{value,V}}, undefined, {value,V}), {{value,{throw,{value,V},V}},V} = eclectic_2({throw,{value,V}}, throw, {value,V}), {{caught,{'EXIT',V}},undefined} = eclectic_2({value,{value,V}}, undefined, {exit,V}), {{caught,{'EXIT',{V,[{?MODULE,foo,1,_}|_]}}},undefined} = eclectic_2({error,{value,V}}, throw, {error,V}), {{caught,{'EXIT',{badarg,[{erlang,abs,[V],_}|_]}}},V} = eclectic_2({value,{'abs',V}}, undefined, {value,V}), {{caught,{'EXIT',{badarith,[{erlang,'+',[0,a],_},{?MODULE,my_add,2,_}|_]}}},V} = eclectic_2({exit,{'add',{0,a}}}, exit, {value,V}), {{caught,{'EXIT',V}},undefined} = eclectic_2({value,{error,V}}, undefined, {exit,V}), {{caught,{'EXIT',{V,[{?MODULE,foo,1,_}|_]}}},undefined} = eclectic_2({throw,{'div',{1,0}}}, throw, {error,V}), ok. eclectic_1(X, C, Y) -> erase(eclectic), Done = make_ref(), Try = try case X of {catch_foo,V} -> catch {Done,foo(V)}; {foo,V} -> {Done,foo(V)} end of {Done,D} -> {value,D,catch foo(D)}; {'EXIT',_}=Exit -> Exit; D -> {D,catch foo(D)} catch C:D -> {C,D,catch foo(D)} after put(eclectic, catch foo(Y)) end, {Try,erase(eclectic)}. eclectic_2(X, C, Y) -> Done = make_ref(), erase(eclectic), Catch = case catch {Done, try foo(X) of V -> {value,V,foo(V)} catch C:D -> {C,D,foo(D)} after put(eclectic, foo(Y)) end} of {Done,Z} -> {value,Z}; Z -> {caught,Z} end, {Catch,erase(eclectic)}. rethrow(Conf) when is_list(Conf) -> V = {a,[b,{c,self()},make_ref]}, {value2,value1} = rethrow_1({value,V}, V), {caught2,{error,V}} = rethrow_2({error,V}, undefined), {caught2,{exit,V}} = rethrow_1({exit,V}, error), {caught2,{throw,V}} = rethrow_1({throw,V}, undefined), {caught2,{throw,V}} = rethrow_2({throw,V}, undefined), {caught2,{error,badarith}} = rethrow_1({'add',{0,a}}, throw), {caught2,{error,function_clause}} = rethrow_2(function_clause, undefined), {caught2,{error,{try_clause,V}}} = rethrow_1({value,V}, exit), {value2,{caught1,V}} = rethrow_1({error,V}, error), {value2,{caught1,V}} = rethrow_1({exit,V}, exit), {value2,caught1} = rethrow_2({throw,V}, V), ok. rethrow_1(X, C1) -> try try foo(X) of C1 -> value1 catch C1:D1 -> {caught1,D1} end of V2 -> {value2,V2} catch C2:D2 -> {caught2,{C2,D2}} end. rethrow_2(X, C1) -> try try foo(X) of C1 -> value1 catch C1 -> caught1 % Implicit class throw: end of V2 -> {value2,V2} catch C2:D2 -> {caught2,{C2,D2}} end. nested_of(Conf) when is_list(Conf) -> V = {[self()|make_ref()],1.4142136}, {{value,{value1,{V,x2}}}, {V,x3}, {V,x4}, finalized} = nested_of_1({{value,{V,x1}},void,{V,x1}}, {value,{V,x2}}, {value,{V,x3}}, {value,{V,x4}}), {{caught,{throw,{V,x2}}}, {V,x3}, {V,x4}, finalized} = nested_of_1({{value,{V,x1}},void,{V,x1}}, {throw,{V,x2}}, {value,{V,x3}}, {value,{V,x4}}), {{caught,{error,badarith}}, undefined, {V,x4}, finalized} = nested_of_1({{value,{V,x1}},void,{V,x1}}, {throw,{V,x2}}, {'div',{1,0}}, {value,{V,x4}}), {{caught,{error,badarith}}, undefined, undefined, finalized} = nested_of_1({{value,{V,x1}},void,{V,x1}}, {throw,{V,x2}}, {'div',{1,0}}, {'add',{0,b}}), %% {{caught,{error,{try_clause,{V,x1}}}}, {V,x3}, {V,x4}, finalized} = nested_of_1({{value,{V,x1}},void,try_clause}, void, {value,{V,x3}}, {value,{V,x4}}), {{caught,{exit,{V,x3}}}, undefined, {V,x4}, finalized} = nested_of_1({{value,{V,x1}},void,try_clause}, void, {exit,{V,x3}}, {value,{V,x4}}), {{caught,{throw,{V,x4}}}, undefined, undefined, finalized} = nested_of_1({{value,{V,x1}},void,try_clause}, void, {exit,{V,x3}}, {throw,{V,x4}}), %% {{value,{caught1,{V,x2}}}, {V,x3}, {V,x4}, finalized} = nested_of_1({{error,{V,x1}},error,{V,x1}}, {value,{V,x2}}, {value,{V,x3}}, {value,{V,x4}}), {{caught,{error,badarith}}, {V,x3}, {V,x4}, finalized} = nested_of_1({{error,{V,x1}},error,{V,x1}}, {'add',{1,c}}, {value,{V,x3}}, {value,{V,x4}}), {{caught,{error,badarith}}, undefined, {V,x4}, finalized} = nested_of_1({{error,{V,x1}},error,{V,x1}}, {'add',{1,c}}, {'div',{17,0}}, {value,{V,x4}}), {{caught,{error,badarg}}, undefined, undefined, finalized} = nested_of_1({{error,{V,x1}},error,{V,x1}}, {'add',{1,c}}, {'div',{17,0}}, {'abs',V}), %% {{caught,{error,badarith}}, {V,x3}, {V,x4}, finalized} = nested_of_1({{'add',{2,c}},rethrow,void}, void, {value,{V,x3}}, {value,{V,x4}}), {{caught,{error,badarg}}, undefined, {V,x4}, finalized} = nested_of_1({{'add',{2,c}},rethrow,void}, void, {'abs',V}, {value,{V,x4}}), {{caught,{error,function_clause}}, undefined, undefined, finalized} = nested_of_1({{'add',{2,c}},rethrow,void}, void, {'abs',V}, function_clause), ok. nested_of_1({X1,C1,V1}, X2, X3, X4) -> erase(nested3), erase(nested4), erase(nested), Self = self(), Try = try try self() of Self -> try foo(X1) of V1 -> {value1,foo(X2)} catch C1:V1 -> {caught1,foo(X2)} after put(nested3, foo(X3)) end after put(nested4, foo(X4)) end of V -> {value,V} catch C:D -> {caught,{C,D}} after put(nested, finalized) end, {Try,erase(nested3),erase(nested4),erase(nested)}. nested_catch(Conf) when is_list(Conf) -> V = {[make_ref(),1.4142136,self()]}, {{value,{value1,{V,x2}}}, {V,x3}, {V,x4}, finalized} = nested_catch_1({{value,{V,x1}},void,{V,x1}}, {value,{V,x2}}, {value,{V,x3}}, {value,{V,x4}}), {{caught,{throw,{V,x2}}}, {V,x3}, {V,x4}, finalized} = nested_catch_1({{value,{V,x1}},void,{V,x1}}, {throw,{V,x2}}, {value,{V,x3}}, {value,{V,x4}}), {{caught,{error,badarith}}, undefined, {V,x4}, finalized} = nested_catch_1({{value,{V,x1}},void,{V,x1}}, {throw,{V,x2}}, {'div',{1,0}}, {value,{V,x4}}), {{caught,{error,badarith}}, undefined, undefined, finalized} = nested_catch_1({{value,{V,x1}},void,{V,x1}}, {throw,{V,x2}}, {'div',{1,0}}, {'add',{0,b}}), %% {{caught,{error,{try_clause,{V,x1}}}}, {V,x3}, {V,x4}, finalized} = nested_catch_1({{value,{V,x1}},void,try_clause}, void, {value,{V,x3}}, {value,{V,x4}}), {{caught,{exit,{V,x3}}}, undefined, {V,x4}, finalized} = nested_catch_1({{value,{V,x1}},void,try_clause}, void, {exit,{V,x3}}, {value,{V,x4}}), {{caught,{throw,{V,x4}}}, undefined, undefined, finalized} = nested_catch_1({{value,{V,x1}},void,try_clause}, void, {exit,{V,x3}}, {throw,{V,x4}}), %% {{value,{caught1,{V,x2}}}, {V,x3}, {V,x4}, finalized} = nested_catch_1({{error,{V,x1}},error,{V,x1}}, {value,{V,x2}}, {value,{V,x3}}, {value,{V,x4}}), {{caught,{error,badarith}}, {V,x3}, {V,x4}, finalized} = nested_catch_1({{error,{V,x1}},error,{V,x1}}, {'add',{1,c}}, {value,{V,x3}}, {value,{V,x4}}), {{caught,{error,badarith}}, undefined, {V,x4}, finalized} = nested_catch_1({{error,{V,x1}},error,{V,x1}}, {'add',{1,c}}, {'div',{17,0}}, {value,{V,x4}}), {{caught,{error,badarg}}, undefined, undefined, finalized} = nested_catch_1({{error,{V,x1}},error,{V,x1}}, {'add',{1,c}}, {'div',{17,0}}, {'abs',V}), %% {{caught,{error,badarith}}, {V,x3}, {V,x4}, finalized} = nested_catch_1({{'add',{2,c}},rethrow,void}, void, {value,{V,x3}}, {value,{V,x4}}), {{caught,{error,badarg}}, undefined, {V,x4}, finalized} = nested_catch_1({{'add',{2,c}},rethrow,void}, void, {'abs',V}, {value,{V,x4}}), {{caught,{error,function_clause}}, undefined, undefined, finalized} = nested_catch_1({{'add',{2,c}},rethrow,void}, void, {'abs',V}, function_clause), ok. nested_catch_1({X1,C1,V1}, X2, X3, X4) -> erase(nested3), erase(nested4), erase(nested), Throw = make_ref(), Try = try try throw(Throw) catch Throw -> try foo(X1) of V1 -> {value1,foo(X2)} catch C1:V1 -> {caught1,foo(X2)} after put(nested3, foo(X3)) end after put(nested4, foo(X4)) end of V -> {value,V} catch C:D -> {caught,{C,D}} after put(nested, finalized) end, {Try,erase(nested3),erase(nested4),erase(nested)}. nested_after(Conf) when is_list(Conf) -> V = [{make_ref(),1.4142136,self()}], {value, {V,x3}, {value1,{V,x2}}, finalized} = nested_after_1({{value,{V,x1}},void,{V,x1}}, {value,{V,x2}}, {value,{V,x3}}), {{caught,{error,{V,x2}}}, {V,x3}, undefined, finalized} = nested_after_1({{value,{V,x1}},void,{V,x1}}, {error,{V,x2}}, {value,{V,x3}}), {{caught,{exit,{V,x3}}}, undefined, undefined, finalized} = nested_after_1({{value,{V,x1}},void,{V,x1}}, {error,{V,x2}}, {exit,{V,x3}}), %% {{caught,{error,{try_clause,{V,x1}}}}, {V,x3}, undefined, finalized} = nested_after_1({{value,{V,x1}},void,try_clause}, void, {value,{V,x3}}), {{caught,{error,badarith}}, undefined, undefined, finalized} = nested_after_1({{value,{V,x1}},void,try_clause}, void, {'div',{17,0}}), %% {value, {V,x3}, {caught1,{V,x2}}, finalized} = nested_after_1({{throw,{V,x1}},throw,{V,x1}}, {value,{V,x2}}, {value,{V,x3}}), {{caught,{error,badarith}}, {V,x3}, undefined, finalized} = nested_after_1({{throw,{V,x1}},throw,{V,x1}}, {'add',{a,b}}, {value,{V,x3}}), {{caught,{error,badarg}}, undefined, undefined, finalized} = nested_after_1({{throw,{V,x1}},throw,{V,x1}}, {'add',{a,b}}, {'abs',V}), %% {{caught,{throw,{V,x1}}}, {V,x3}, undefined, finalized} = nested_after_1({{throw,{V,x1}},rethrow,void}, void, {value,{V,x3}}), {{caught,{error,badarith}}, undefined, undefined, finalized} = nested_after_1({{throw,{V,x1}},rethrow,void}, void, {'div',{1,0}}), ok. nested_after_1({X1,C1,V1}, X2, X3) -> erase(nested3), erase(nested4), erase(nested), Self = self(), Try = try try self() after After = try foo(X1) of V1 -> {value1,foo(X2)} catch C1:V1 -> {caught1,foo(X2)} after put(nested3, foo(X3)) end, put(nested4, After) end of Self -> value catch C:D -> {caught,{C,D}} after put(nested, finalized) end, {Try,erase(nested3),erase(nested4),erase(nested)}. nested_horrid(Config) when is_list(Config) -> {[true,true],{[true,1.0],1.0}} = nested_horrid_1({true,void,void}, 1.0), ok. nested_horrid_1({X1,C1,V1}, X2) -> try A1 = [X1,X1], B1 = if X1 -> A2 = [X1,X2], B2 = foo(X2), {A2,B2}; true -> A3 = [X2,X1], B3 = foo(X2), {A3,B3} end, {A1,B1} catch C1:V1 -> caught1 end. foo({value,Value}) -> Value; foo({'div',{A,B}}) -> my_div(A, B); foo({'add',{A,B}}) -> my_add(A, B); foo({'abs',X}) -> my_abs(X); foo({error,Error}) -> erlang:error(Error); foo({throw,Throw}) -> erlang:throw(Throw); foo({exit,Exit}) -> erlang:exit(Exit); foo({raise,{Class,Reason}}) -> erlang:raise(Class, Reason); foo(Term) when not is_atom(Term) -> Term. %%foo(Atom) when is_atom(Atom) -> % must not be defined! my_div(A, B) -> A div B. my_add(A, B) -> A + B. my_abs(X) -> abs(X). last_call_optimization(Config) when is_list(Config) -> error = in_tail(dum), StkSize0 = in_tail(0), StkSize = in_tail(50000), io:format("StkSize0 = ~p", [StkSize0]), io:format("StkSize = ~p", [StkSize]), StkSize = StkSize0, ok. in_tail(E) -> try erlang:abs(E) of T -> A = id([]), B = id([]), C = id([]), id([A,B,C]), do_tail(T) catch error:badarg -> error end. do_tail(0) -> process_info(self(), stack_size); do_tail(N) -> in_tail(N-1). bool(Config) when is_list(Config) -> ok = do_bool(false, false), error = do_bool(false, true), error = do_bool(true, false), error = do_bool(true, true), error = do_bool(true, blurf), {'EXIT',_} = (catch do_bool(blurf, false)), ok. %% The following function used to cause a crash in beam_bool. do_bool(A0, B) -> A = not A0, try id(42), if A, not B -> ok end catch _:_ -> error end. plain_catch_coverage(Config) when is_list(Config) -> %% Cover some code in beam_block:alloc_may_pass/1. {a,[42]} = do_plain_catch_list(42). do_plain_catch_list(X) -> B = [X], catch id({a,B}). andalso_orelse(Config) when is_list(Config) -> {2,{a,42}} = andalso_orelse_1(true, {a,42}), {b,{b}} = andalso_orelse_1(false, {b}), {catched,no_tuple} = andalso_orelse_1(false, no_tuple), ok = andalso_orelse_2({type,[a]}), also_ok = andalso_orelse_2({type,[]}), also_ok = andalso_orelse_2({type,{a}}), ok. andalso_orelse_1(A, B) -> {try if A andalso element(1, B) =:= a -> tuple_size(B); true -> element(1, B) end catch error:_ -> catched end,B}. andalso_orelse_2({Type,Keyval}) -> try if is_atom(Type) andalso length(Keyval) > 0 -> ok; true -> also_ok end catch _:_ -> fail end. zero() -> 0.0. get_in_try(_) -> undefined = get_valid_line([a], []), ok. get_valid_line([_|T]=Path, Annotations) -> try get(Path) %% beam_dead used to optimize away an assignment to {y,1} %% because it didn't appear to be used. catch _:not_found -> get_valid_line(T, Annotations) end. hockey(_) -> {'EXIT',{{badmatch,_},[_|_]}} = (catch hockey()), ok. hockey() -> %% beam_jump used to generate a call into the try block. %% beam_validator disapproved. receive _ -> (b = fun() -> ok end) + hockey, +x after 0 -> ok end, try (a = fun() -> ok end) + hockey, + y catch _ -> ok end. -record(state, {foo}). handle_info(_Config) -> do_handle_info({foo}, #state{}), ok. do_handle_info({_}, State) -> handle_info_ok(), State#state{foo = bar}, case ok of _ -> case catch handle_info_ok() of ok -> {stop, State} end end; do_handle_info(_, State) -> (catch begin handle_info_ok(), State#state{foo = bar} end), case ok of _ -> case catch handle_info_ok() of ok -> {stop, State} end end. handle_info_ok() -> ok. 'catch_in_catch'(_Config) -> process_flag(trap_exit, true), Pid = spawn_link(fun() -> catch_in_catch_init(x), exit(good_exit) end), receive {'EXIT',Pid,good_exit} -> ok; Other -> io:format("Unexpected: ~p\n", [Other]), error after 32000 -> io:format("No message received\n"), error end. 'catch_in_catch_init'(Param) -> process_flag(trap_exit, true), %% The catches were improperly nested, causing a "No catch found" crash. (catch begin id(Param), (catch exit(bar)) end ), ignore. grab_bag(_Config) -> %% Thanks to Martin Bjorklund. _ = fun() -> ok end, try fun() -> ok end after fun({A, B}) -> A + B end end, %% Thanks to Tim Rath. A = {6}, try io:fwrite("") after fun () -> fun () -> {_} = A end end end, %% Unnecessary catch. 22 = (catch 22), ok. stacktrace(_Config) -> V = [make_ref()|self()], case ?MODULE:module_info(native) of false -> {value2,{caught1,badarg,[{erlang,abs,[V],_}|_]}} = stacktrace_1({'abs',V}, error, {value,V}), {caught2,{error,badarith},[{erlang,'+',[0,a],_}, {?MODULE,my_add,2,_}|_]} = stacktrace_1({'div',{1,0}}, error, {'add',{0,a}}); true -> {value2,{caught1,badarg,[{?MODULE,my_abs,1,_}|_]}} = stacktrace_1({'abs',V}, error, {value,V}), {caught2,{error,badarith},[{?MODULE,my_add,2,_}|_]} = stacktrace_1({'div',{1,0}}, error, {'add',{0,a}}) end, {caught2,{error,{try_clause,V}},[{?MODULE,stacktrace_1,3,_}|_]} = stacktrace_1({value,V}, error, {value,V}), {caught2,{throw,V},[{?MODULE,foo,1,_}|_]} = stacktrace_1({value,V}, error, {throw,V}), try stacktrace_2() catch error:{badmatch,_}:Stk2 -> [{?MODULE,stacktrace_2,0,_}, {?MODULE,stacktrace,1,_}|_] = Stk2, Stk2 = erlang:get_stacktrace(), ok end, try stacktrace_3(a, b) catch error:function_clause:Stk3 -> Stk3 = erlang:get_stacktrace(), case lists:module_info(native) of false -> [{lists,prefix,[a,b],_}|_] = Stk3; true -> [{lists,prefix,2,_}|_] = Stk3 end end, try throw(x) catch throw:x:IntentionallyUnused -> ok end. stacktrace_1(X, C1, Y) -> try try foo(X) of C1 -> value1 catch C1:D1:Stk1 -> Stk1 = erlang:get_stacktrace(), {caught1,D1,Stk1} after foo(Y) end of V2 -> {value2,V2} catch C2:D2:Stk2 -> {caught2,{C2,D2},Stk2=erlang:get_stacktrace()} end. stacktrace_2() -> ok = erlang:process_info(self(), current_function), ok. stacktrace_3(A, B) -> {ok,lists:prefix(A, B)}. nested_stacktrace(_Config) -> V = [{make_ref()}|[self()]], value1 = nested_stacktrace_1({{value,{V,x1}},void,{V,x1}}, {void,void,void}), case ?MODULE:module_info(native) of false -> {caught1, [{erlang,'+',[V,x1],_},{?MODULE,my_add,2,_}|_], value2} = nested_stacktrace_1({{'add',{V,x1}},error,badarith}, {{value,{V,x2}},void,{V,x2}}), {caught1, [{erlang,'+',[V,x1],_},{?MODULE,my_add,2,_}|_], {caught2,[{erlang,abs,[V],_}|_]}} = nested_stacktrace_1({{'add',{V,x1}},error,badarith}, {{'abs',V},error,badarg}); true -> {caught1, [{?MODULE,my_add,2,_}|_], value2} = nested_stacktrace_1({{'add',{V,x1}},error,badarith}, {{value,{V,x2}},void,{V,x2}}), {caught1, [{?MODULE,my_add,2,_}|_], {caught2,[{?MODULE,my_abs,1,_}|_]}} = nested_stacktrace_1({{'add',{V,x1}},error,badarith}, {{'abs',V},error,badarg}) end, ok. nested_stacktrace_1({X1,C1,V1}, {X2,C2,V2}) -> try foo(X1) of V1 -> value1 catch C1:V1:S1 -> S1 = erlang:get_stacktrace(), T2 = try foo(X2) of V2 -> value2 catch C2:V2:S2 -> S2 = erlang:get_stacktrace(), {caught2,S2} end, {caught1,S1,T2} end. raise(_Config) -> test_raise(fun() -> exit({exit,tuple}) end), test_raise(fun() -> abs(id(x)) end), test_raise(fun() -> throw({was,thrown}) end), badarg = bad_raise(fun() -> abs(id(x)) end), ok. bad_raise(Expr) -> try Expr() catch _:E:Stk -> erlang:raise(bad_class, E, Stk) end. test_raise(Expr) -> test_raise_1(Expr), test_raise_2(Expr), test_raise_3(Expr). test_raise_1(Expr) -> erase(exception), try do_test_raise_1(Expr) catch C:E:Stk -> {C,E,Stk} = erase(exception) end. do_test_raise_1(Expr) -> try Expr() catch C:E:Stk -> %% Here the stacktrace must be built. put(exception, {C,E,Stk}), erlang:raise(C, E, Stk) end. test_raise_2(Expr) -> erase(exception), try do_test_raise_2(Expr) catch C:E:Stk -> {C,E} = erase(exception), try Expr() catch _:_:S -> [StkTop|_] = S, [StkTop|_] = Stk end end. do_test_raise_2(Expr) -> try Expr() catch C:E:Stk -> %% Here it is possible to replace erlang:raise/3 with %% the raw_raise/3 instruction since the stacktrace is %% not actually used. put(exception, {C,E}), erlang:raise(C, E, Stk) end. test_raise_3(Expr) -> try do_test_raise_3(Expr) catch exit:{exception,C,E}:Stk -> try Expr() catch C:E:S -> [StkTop|_] = S, [StkTop|_] = Stk end end. do_test_raise_3(Expr) -> try Expr() catch C:E:Stk -> %% Here it is possible to replace erlang:raise/3 with %% the raw_raise/3 instruction since the stacktrace is %% not actually used. erlang:raise(exit, {exception,C,E}, Stk) end. id(I) -> I.