1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2004-2020. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20%%% Purpose : Compiles various modules with tough code
21
22-module(receive_SUITE).
23
24-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
25	 init_per_group/2,end_per_group/2,
26	 init_per_testcase/2,end_per_testcase/2,
27	 export/1,recv/1,coverage/1,otp_7980/1,ref_opt/1,
28	 wait/1,recv_in_try/1,double_recv/1,receive_var_zero/1,
29         match_built_terms/1,elusive_common_exit/1,
30         return_before_receive/1,trapping/1,
31         after_expression/1,in_after/1]).
32
33-include_lib("common_test/include/ct.hrl").
34
35init_per_testcase(_Case, Config) ->
36    Config.
37
38end_per_testcase(_Case, _Config) ->
39    ok.
40
41suite() ->
42    [{ct_hooks,[ts_install_cth]},
43     {timetrap,{minutes,2}}].
44
45all() ->
46    slow_group() ++ [{group,p}].
47
48groups() ->
49    [{p,test_lib:parallel(),
50      [recv,coverage,otp_7980,export,wait,
51       recv_in_try,double_recv,receive_var_zero,
52       match_built_terms,elusive_common_exit,
53       return_before_receive,trapping,
54       after_expression,in_after]},
55     {slow,[],[ref_opt]}].
56
57init_per_suite(Config) ->
58    test_lib:recompile(?MODULE),
59    Config.
60
61end_per_suite(_Config) ->
62    ok.
63
64init_per_group(_GroupName, Config) ->
65    Config.
66
67end_per_group(_GroupName, Config) ->
68    Config.
69
70slow_group() ->
71    case ?MODULE of
72	receive_SUITE ->
73            %% Canononical module name. Run slow cases.
74            [{group,slow}];
75        _ ->
76            %% Cloned module. Don't run.
77            []
78    end.
79
80-record(state, {ena = true}).
81
82recv(Config) when is_list(Config) ->
83    Pid = spawn_link(fun() -> loop(#state{}) end),
84    Self = self(),
85    Pid ! {Self,test},
86    receive
87	{ok,test} -> ok;
88	{error,Other} ->
89	    io:format("Got unpexected ~p", [Other]),
90	    ct:fail(unexpected)
91    after 10000 ->
92	    ct:fail(no_answer)
93    end,
94    receive
95	X ->
96	    io:format("Unexpected extra message: ~p", [X]),
97	    ct:fail(unexpected)
98    after 10 ->
99            unlink(Pid),
100            exit(Pid, kill),
101	    ok
102    end,
103    ok.
104
105loop(S) ->
106    receive
107	_ when S#state.ena == false ->
108	    loop(S);
109	{P,test} ->
110	    P ! {ok,test},
111	    loop(S);
112	_X ->
113	    loop(S)
114    end.
115
116coverage(Config) when is_list(Config) ->
117    do_link(self()),
118    do_unlink(self()),
119    do_monitor_node(node(), true),
120    do_monitor_node(node(), false),
121    do_group_leader(group_leader(), self()),
122    id(node(self())),
123
124    erlang:'!'(self(), {a,10}),
125    self() ! {b,20},
126    [{a,10},{b,20}] = receive_all(),
127    self() ! {c,42},
128    receive
129	{c,42} ->
130	    ok
131    after infinity ->
132	    exit(cant_happen)
133    end,
134
135    self() ! 17,
136    self() ! 19,
137    59 = tuple_to_values(infinity, x),
138    61 = tuple_to_values(999999, x),
139    0 = tuple_to_values(1, x),
140
141    {'EXIT',{{badmap,[]},_}} = (catch monitor_plus_badmap(self())),
142
143
144    self() ! {data,no_data},
145    ok = receive_sink_tuple({any,pattern}),
146    {b,a} = receive_sink_tuple({a,b}),
147
148    %% Basically a smoke test of no_clauses_left/0.
149    smoke_receive(fun no_clauses_left_1/0),
150    smoke_receive(fun no_clauses_left_2/0),
151    smoke_receive(fun no_clauses_left_3/0),
152
153    ok.
154
155monitor_plus_badmap(Pid) ->
156    monitor(process, Pid) + []#{}.
157
158receive_all() ->
159    receive
160	Any ->
161	    [Any|receive_all()]
162    after 0 ->
163	    []
164    end.
165
166do_monitor_node(Node, Bool) ->
167    monitor_node(Node, Bool).
168
169do_link(Pid) ->
170    link(Pid).
171
172do_unlink(Pid) ->
173    unlink(Pid).
174
175do_group_leader(Leader, Pid) ->
176    group_leader(Leader, Pid).
177
178
179%% cover sys_core_fold:tuple_to_values/2
180tuple_to_values(infinity, X) ->
181    {A,B} = case X of
182		x ->
183		    receive
184			Any ->
185			    {42,Any}
186		    end
187	    end,
188    A+B;
189tuple_to_values(Timeout, X) ->
190    {A,B} = case X of
191		x ->
192		    receive
193			Any ->
194			    {42,Any}
195		    after Timeout ->
196			    {0,0}
197		    end
198	    end,
199    A+B.
200
201no_clauses_left_1() ->
202    receive
203        %% This clause would be removed because it cannot match...
204        a = b ->
205            V = whatever
206    end,
207    %% ... leaving a reference to an unbound variable. Crash.
208    V.
209
210no_clauses_left_2() ->
211    [receive
212         %% This clause would be removed because it cannot match...
213         a = <<V0:(node())>> ->
214             year
215     end],
216    %% ... leaving a reference to an unbound variable. Crash.
217    V0.
218
219no_clauses_left_3() ->
220    case id([]) of
221        [] ->
222            receive
223                [Var] = [] ->
224                    ok
225            end
226    end,
227    Var.
228
229%% Cover a help function for beam_ssa_opt:ssa_opt_sink/1.
230receive_sink_tuple({Line,Pattern}) ->
231    receive
232        {data,_} ->
233            ok
234    after 1 ->
235            id({Pattern,Line})
236    end.
237
238
239%% OTP-7980. Thanks to Vincent de Phily. The following code would
240%% be inccorrectly optimized by beam_jump.
241
242otp_7980(Config) when is_list(Config) ->
243    7 = otp_7980_add_clients(10),
244    ok.
245
246otp_7980_add_clients(Count) ->
247    Timeout = 42,
248    lists:foldl(fun(_, N) ->
249			case N of
250                           1 -> ok;
251			    _ -> receive after Timeout -> ok end
252			end,
253			N - 1
254		end, Count, [1,2,3]).
255
256ref_opt(Config) when is_list(Config) ->
257    DataDir = proplists:get_value(data_dir, Config),
258    PrivDir = proplists:get_value(priv_dir, Config),
259    Sources = filelib:wildcard(filename:join([DataDir,"ref_opt","*.{erl,S}"])),
260    test_lib:p_run(fun(Src) ->
261			   do_ref_opt(Src, PrivDir)
262		   end, Sources),
263    cover_recv_instructions(),
264    ok.
265
266do_ref_opt(Source, PrivDir) ->
267    try
268	Ext = filename:extension(Source),
269	{ok,Mod} = compile:file(Source, [report_errors,report_warnings,
270					 {outdir,PrivDir}] ++
271					[from_asm || Ext =:= ".S" ]),
272	Base = filename:rootname(filename:basename(Source), Ext),
273	code:purge(list_to_atom(Base)),
274	BeamFile = filename:join(PrivDir, Base),
275	code:load_abs(BeamFile),
276	ok = Mod:Mod(),
277	{beam_file,Mod,_,_,_,Code} = beam_disasm:file(BeamFile),
278	case Base of
279	    "no_"++_ ->
280		[] = collect_recv_opt_instrs(Code);
281	    "yes_"++_ ->
282		[{recv_mark,{f,L}},{recv_set,{f,L}}] =
283		    collect_recv_opt_instrs(Code)
284	end,
285	ok
286    catch Class:Error:Stk ->
287	    io:format("~s: ~p ~p\n~p\n", [Source,Class,Error,Stk]),
288	    error
289    end.
290
291collect_recv_opt_instrs(Code) ->
292    L = [ [I || I <- Is,
293		begin
294		    case I of
295			{recv_mark,{f,_}} -> true;
296			{recv_set,{f,_}} -> true;
297			_ -> false
298		    end
299		end] || {function,_,_,_,Is} <- Code],
300    lists:append(L).
301
302cover_recv_instructions() ->
303    %% We want to cover the handling of recv_mark and recv_set in beam_utils.
304    %% Since those instructions are introduced in a late optimization pass,
305    %% beam_utils:live_opt() will not see them unless the compilation is
306    %% started from a .S file. The compile_SUITE:asm/1 test case will
307    %% compile all test suite files to .S and then run them through the
308    %% compiler again.
309    %%
310    %% Here will we will ensure that this modules contains recv_mark
311    %% and recv_set instructions.
312    Pid = spawn_link(fun() ->
313			     receive {Parent,Ref} ->
314				     Parent ! Ref
315			     end
316		     end),
317    Ref = make_ref(),
318    Pid ! {self(),Ref},
319    receive
320	Ref -> ok
321    end.
322
323export(Config) when is_list(Config) ->
324    Ref = make_ref(),
325    self() ! {result,Ref,42},
326    42 = export_1(Ref),
327    {error,timeout} = export_1(Ref),
328
329    self() ! {result,Ref},
330    {ok,Ref} = export_2(),
331
332    ok.
333
334export_1(Reference) ->
335    id(Reference),
336    receive
337	{result,Reference,Result} ->
338	    Result
339    after 1 ->
340	    Result = {error,timeout}
341    end,
342    %% Result ({x,1}) is used, but not the return value ({x,0})
343    %% of the receive. Used to be incorrectly optimized
344    %% by beam_block.
345    id({build,self()}),
346    Result.
347
348export_2() ->
349    receive {result,Result} -> ok end,
350    {ok,Result}.
351
352wait(Config) when is_list(Config) ->
353    self() ! <<42>>,
354    <<42>> = wait_1(r, 1, 2),
355    {1,2,3} = wait_1(1, 2, 3),
356    {'EXIT',{timeout_value,_}} = (catch receive after [] -> timeout end),
357    ok.
358
359wait_1(r, _, _) ->
360    receive
361	B when byte_size(B) > 0 ->
362	    B
363    end;
364%% beam_utils would wrongly assume that wait/1 could fall through
365%% to the next clause.
366wait_1(A, B, C) ->
367    {A,B,C}.
368
369recv_in_try(_Config) ->
370    self() ! {ok,fh}, {ok,fh} = recv_in_try_1(infinity, native),
371    self() ! {ok,ignored}, {ok,42} = recv_in_try_1(infinity, plain),
372    self() ! {error,ignored}, nok = recv_in_try_1(infinity, plain),
373    timeout = recv_in_try_1(1, plain),
374
375    smoke_receive(fun recv_in_try_2/0),
376    smoke_receive(fun recv_in_try_3/0),
377    smoke_receive(fun recv_in_try_4/0),
378    smoke_receive(fun recv_in_catch_1/0),
379
380    ok.
381
382recv_in_try_1(Timeout, Format) ->
383    try
384	receive
385	    {Status,History} ->
386                %% {test,is_tuple,{f,148},[{x,0}]}.
387                %% {test,test_arity,{f,148},[{x,0},2]}.
388                %% {get_tuple_element,{x,0},0,{y,1}}.  %y1 is fragile.
389                %%
390                %% %% Here the fragility of y1 would be be progated to
391                %% %% the 'catch' below. Incorrect, since get_tuple_element
392                %% %% can't fail.
393                %% {get_tuple_element,{x,0},1,{x,2}}.
394                %%
395                %% remove_message.                     %y1 fragility cleared.
396		FH = case Format of
397			native ->
398                             id(History);
399			plain ->
400                             id(42)
401		    end,
402		case Status of
403		    ok ->
404			{ok,FH};
405		    error ->
406			nok
407		end
408	after Timeout ->
409		timeout
410	end
411    catch
412        %% The fragility of y1 incorrectly propagated to here.
413        %% beam_validator would complain.
414	throw:{error,Reason} ->
415	    {nok,Reason}
416    end.
417
418recv_in_try_2() ->
419    try
420        %% The live range of the try tag would stop here because of
421        %% the infinite receive below. The code generator would
422        %% generate a kill instruction that would kill the try tag.
423        %% Although probably safe in practice, beam_validator does not
424        %% consider it safe.
425        _ = (catch try a after [] end),
426        receive after infinity -> ok end
427    after
428        []
429    end.
430
431recv_in_try_3() ->
432    #{make_ref() =>
433          not (catch
434                   (catch 9 = kid)#{key =>
435                                        receive after infinity ->
436                                                        ok
437                                                end})}.
438
439recv_in_try_4() ->
440    #{make_ref() =>
441          not (catch
442                   (catch 9 = kid)#{key =>
443                                        receive
444                                        [] when false ->
445                                                ok
446                                        end})}.
447
448recv_in_catch_1() ->
449    catch
450        (catch
451             try
452                 some_module
453             after
454                 ok
455             end):some_function(receive
456                                after infinity -> ok
457                                end#{key := value}).
458
459%% ERL-703. The compiler would crash because beam_utils:anno_defs/1
460%% failed to take into account that code after loop_rec_end is
461%% unreachable.
462
463double_recv(_Config) ->
464    self() ! {more,{a,term}},
465    ok = do_double_recv({more,{a,term}}, any),
466    self() ! message,
467    ok = do_double_recv(whatever, message),
468
469    error = do_double_recv({more,42}, whatever),
470    error = do_double_recv(whatever, whatever),
471    ok.
472
473do_double_recv({more, Rest}, _Msg) ->
474    receive
475        {more, Rest} ->
476            ok
477    after 0 ->
478            error
479    end;
480do_double_recv(_, Msg) ->
481    receive
482        Msg ->
483            ok
484    after 0 ->
485            error
486    end.
487
488%% Test 'after Z', when Z =:= 0 been propagated as an immediate by the type
489%% optimization pass.
490receive_var_zero(Config) when is_list(Config) ->
491    self() ! x,
492    self() ! y,
493    Z = zero(),
494    timeout = receive
495                  z -> ok
496              after Z -> timeout
497              end,
498    timeout = receive
499              after Z -> timeout
500              end,
501    self() ! w,
502    receive
503	x ->
504            receive y -> ok end,
505            receive w -> ok end;
506	Other ->
507	    ct:fail({bad_message,Other})
508    end.
509
510zero() -> 0.
511
512%% ERL-862; the validator would explode when a term was constructed in a
513%% receive guard.
514
515-define(MATCH_BUILT_TERM(Ref, Expr),
516        (fun() ->
517                 Ref = make_ref(),
518                 A = id($a),
519                 B = id($b),
520                 Built = id(Expr),
521                 self() ! {Ref, A, B},
522                 receive
523                     {Ref, A, B} when Expr =:= Built ->
524                         ok
525                 after 5000 ->
526                     ct:fail("Failed to match message with term built in "
527                             "receive guard.")
528                 end
529         end)()).
530
531match_built_terms(Config) when is_list(Config) ->
532    ?MATCH_BUILT_TERM(Ref, [A, B]),
533    ?MATCH_BUILT_TERM(Ref, {A, B}),
534    ?MATCH_BUILT_TERM(Ref, <<A, B>>),
535    ?MATCH_BUILT_TERM(Ref, #{ 1 => A, 2 => B}).
536
537elusive_common_exit(_Config) ->
538    self() ! {1, a},
539    self() ! {2, b},
540    {[z], [{2,b},{1,a}]} = elusive_loop([x,y,z], 2, []),
541
542    CodeServer = whereis(code_server),
543    Self = self(),
544    Self ! {Self, abc},
545    Self ! {CodeServer, []},
546    Self ! {Self, other},
547    try elusive2([]) of
548        Unexpected ->
549            ct:fail("Expected an exception; got ~p\n", [Unexpected])
550    catch
551        throw:[other, CodeServer, Self] ->
552            ok
553    end,
554
555    ok.
556
557elusive_loop(List, 0, Results) ->
558    {List, Results};
559elusive_loop(List, ToReceive, Results) ->
560    {Result, RemList} =
561        receive
562            {_Pos, _R} = Res when List =/= [] ->
563                [_H|T] = List,
564                {Res, T};
565            {_Pos, _R} = Res when List =:= [] ->
566                {Res, []}
567        end,
568    %% beam_ssa_pre_codegen:fix_receives() would fail to find
569    %% the common exit block for this receive. That would mean
570    %% that it would not insert all necessary copy instructions.
571    elusive_loop(RemList, ToReceive-1, [Result | Results]).
572
573
574elusive2(Acc) ->
575    receive
576        {Pid, abc} ->
577            ok;
578        {Pid, []} ->
579            ok;
580        {Pid, Res} ->
581            %% beam_ssa_pre_codegen:find_loop_exit/2 attempts to find
582            %% the first block of the common code after the receive
583            %% statement. It used to only look at the two last clauses
584            %% of the receive. In this function, the last two clauses
585            %% don't have any common block, so it would be assumed
586            %% that there was no common block for any of the
587            %% clauses. That would mean that copy instructions would
588            %% not be inserted as needed.
589            throw([Res | Acc])
590    end,
591    %% Common code.
592    elusive2([Pid | Acc]).
593
594return_before_receive(_Config) ->
595    ref_received = do_return_before_receive(),
596    ok.
597
598do_return_before_receive() ->
599    Ref = make_ref(),
600    self() ! {ref,Ref},
601    maybe_receive(id(false)),
602    receive
603        {ref,Ref} ->
604            ref_received
605    after 1 ->
606            %% Can only be reached if maybe_receive/1 returned
607            %% with the receive marker set.
608            timeout
609    end.
610
611maybe_receive(Bool) ->
612    NewRef = make_ref(),
613    case Bool of
614        true ->
615            receive
616                NewRef ->
617                    ok
618            end;
619        false ->
620            %% The receive marker must not be set when
621            %% leaving this function.
622            ok
623    end.
624
625trapping(_Config) ->
626    ok = do_trapping(0),
627    ok = do_trapping(1),
628    ok.
629
630%% Simplified from emulator's binary_SUITE:trapping/1.
631do_trapping(N) ->
632    Ref = make_ref(),
633    self() ! Ref,
634    case N rem 2 of
635	0 ->
636            %% Would generate recv_set _, label _, wait_timeout _ _,
637            %% which the loader can't handle.
638            receive after 1 -> ok end;
639	1 ->
640            void
641    end,
642    receive Ref -> ok end,
643    receive after 1 -> ok end.
644
645after_expression(_Config) ->
646    self() ! {a,message},
647    {a,message} = after_expr(0),
648    timeout = after_expr(0),
649    timeout = after_expr(10),
650    ok = after_expr_timeout(0),
651    ok = after_expr_timeout(1),
652    ok.
653
654after_expr(Timeout) ->
655    receive
656        Msg -> Msg
657    after id(Timeout) ->
658            timeout
659    end.
660
661after_expr_timeout(Timeout) ->
662    receive
663    after id(Timeout) ->
664            ok
665    end.
666
667in_after(_Config) ->
668    self() ! first,
669    self() ! message,
670    do_in_after(fun() -> ok end),
671    do_in_after(fun() -> ok end),
672    self() ! message,
673    catch do_in_after(fun() -> error(bad) end),
674    catch do_in_after(fun() -> error(bad) end),
675    self() ! last,
676    first = receive M1 -> M1 end,
677    last = receive M2 -> M2 end,
678    ok.
679
680do_in_after(E) ->
681    try
682        E()
683    after
684        receive
685            message ->
686                ok
687        after 1 ->
688                ok
689        end
690    end,
691    ok.
692
693%%%
694%%% Common utilities.
695%%%
696
697smoke_receive(Fun) ->
698    NoClausesLeft = spawn(Fun),
699    receive after 1 -> ok end,
700    exit(NoClausesLeft, kill).
701
702id(I) -> I.
703