%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2000-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(fun_SUITE).
-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,
external/1,eep37/1,eep37_dup/1,badarity/1,badfun/1,
duplicated_fun/1]).
%% Internal exports.
-export([call_me/1,dup1/0,dup2/0]).
-include_lib("common_test/include/ct.hrl").
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[{group,p}].
groups() ->
[{p,[parallel],
[test1,overwritten_fun,otp_7202,bif_fun,external,eep37,
eep37_dup,badarity,badfun,duplicated_fun]}].
init_per_suite(Config) ->
test_lib:recompile(?MODULE),
Config.
end_per_suite(_Config) ->
ok.
init_per_group(_GroupName, Config) ->
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}).
l1() ->
[
?T((begin A = 3, F = fun(A) -> 1; (_) -> 2 end, F(2) end), 1),
?T((begin G = fun(1=0) -> ok end, {'EXIT',_} = (catch G(2)), ok end), ok)
].
test1(Config) when is_list(Config) ->
lists:foreach(fun one_test/1, eval_list(l1(), [])),
ok.
evaluate(Str, Vars) ->
{ok,Tokens,_} =
erl_scan:string(Str ++ " . "),
{ok, [Expr]} = erl_parse:parse_exprs(Tokens),
case erl_eval:expr(Expr, Vars) of
{value, Result, _} ->
Result
end.
eval_list([], _Vars) ->
[];
eval_list([{C_bin, Str, Bytes} | Rest], Vars) ->
case catch evaluate(Str, Vars) of
{'EXIT', Error} ->
io:format("Evaluation error: ~p, ~p, ~p~n", [Str, Vars, Error]),
exit(Error);
E_bin ->
[{C_bin, E_bin, Str, Bytes} | eval_list(Rest, Vars)]
end.
one_test({C, E, Str, Correct}) ->
io:format(" ~s, ~p~n", [Str, Correct]),
if
C == Correct ->
ok;
true ->
io:format("ERROR: Compiled: ~p. Expected ~p. Got ~p.~n",
[Str, Correct, C]),
ct:fail(comp)
end,
if
E == Correct ->
ok;
true ->
io:format("ERROR: Interpreted: ~p. Expected ~p. Got ~p.~n",
[Str, Correct, E]),
ct:fail(comp)
end.
-record(b, {c}).
%% OTP-7102. (Thanks to Simon Cornish.)
overwritten_fun(Config) when is_list(Config) ->
{a2,a} = overwritten_fun_1(a),
{a2,{b,c}} = overwritten_fun_1(#b{c=c}),
one = overwritten_fun_1(#b{c=[]}),
ok.
overwritten_fun_1(A) ->
F = fun() ->
{ok, A}
end,
if A#b.c == [] ->
one;
true ->
case F() of
{ok, A2} ->
{a2, A2};
_ ->
three
end
end.
%% OTP-7202. The liveness calculation for the make_fun2 instruction was wrong.
otp_7202(Config) when is_list(Config) ->
otp_7202().
otp_7202() ->
List = [a],
Error = case otp_7202_func() of
no_value -> true;
{ok, V} -> V
end,
lists:foreach(fun(_E) ->
case Error of
true ->
ok;
false ->
ok
end
end, List).
otp_7202_func() ->
no_value.
bif_fun(Config) when is_list(Config) ->
F = fun abs/1,
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),
42 = (fun erlang:abs/1)(-42),
42 = (id(fun erlang:abs/1))(-42),
42 = apply(fun erlang:abs/1, [-42]),
42 = apply(id(fun erlang:abs/1), [-42]),
6 = (fun lists:sum/1)([1,2,3]),
6 = (id(fun lists:sum/1))([1,2,3]),
{'EXIT',{{badarity,_},_}} = (catch (fun lists:sum/1)(1, 2, 3)),
{'EXIT',{{badarity,_},_}} = (catch (id(fun lists:sum/1))(1, 2, 3)),
{'EXIT',{{badarity,_},_}} = (catch apply(fun lists:sum/1, [1,2,3])),
ok.
call_me(I) ->
{ok,I}.
eep37(Config) when is_list(Config) ->
F = fun Fact(N) when N > 0 -> N * Fact(N - 1); Fact(0) -> 1 end,
Add = fun _(N) -> N + 1 end,
UnusedName = fun BlackAdder(N) -> N + 42 end,
720 = F(6),
10 = Add(9),
50 = UnusedName(8),
ok.
eep37_dup(Config) when is_list(Config) ->
dup1 = (dup1())(),
dup2 = (dup2())(),
ok.
dup1() ->
fun _F() -> dup1 end.
dup2() ->
fun _F() -> dup2 end.
badarity(Config) when is_list(Config) ->
{'EXIT',{{badarity,{_,[]}},_}} = (catch (fun badarity/1)()),
ok.
badfun(_Config) ->
X = not_a_fun,
expect_badfun(42, catch 42()),
expect_badfun(42.0, catch 42.0(1)),
expect_badfun(X, catch X()),
expect_badfun(X, catch X(1)),
Len = length(atom_to_list(X)),
expect_badfun(Len, catch begin length(atom_to_list(X)) end(1)),
expect_badfun(42, catch 42(put(?FUNCTION_NAME, yes))),
yes = erase(?FUNCTION_NAME),
expect_badfun(X, catch X(put(?FUNCTION_NAME, of_course))),
of_course = erase(?FUNCTION_NAME),
%% A literal as a Fun used to crash the code generator. This only happened
%% when type optimization had reduced `Fun` to a literal, hence the match.
Literal = fun(literal = Fun) ->
Fun()
end,
expect_badfun(literal, catch Literal(literal)),
ok.
expect_badfun(Term, Exit) ->
{'EXIT',{{badfun,Term},_}} = Exit.
duplicated_fun(_Config) ->
try
%% The following code used to crash the compiler before
%% v3_core:is_safe/1 was corrected to consider fun variables
%% unsafe.
id([print_result_paths_fun = fun duplicated_fun_helper/1]),
ct:error(should_fail)
catch
error:{badmatch,F} when is_function(F, 1) ->
ok
end.
duplicated_fun_helper(_) ->
ok.
id(I) ->
I.