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         type_optimized_markers/1]).
33
34-include_lib("common_test/include/ct.hrl").
35
36init_per_testcase(_Case, Config) ->
37    Config.
38
39end_per_testcase(_Case, _Config) ->
40    ok.
41
42suite() ->
43    [{ct_hooks,[ts_install_cth]},
44     {timetrap,{minutes,2}}].
45
46all() ->
47    slow_group() ++ [{group,p}].
48
49groups() ->
50    [{p,test_lib:parallel(),
51      [recv,coverage,otp_7980,export,wait,
52       recv_in_try,double_recv,receive_var_zero,
53       match_built_terms,elusive_common_exit,
54       return_before_receive,trapping,
55       after_expression,in_after,
56       type_optimized_markers]},
57     {slow,[],[ref_opt]}].
58
59init_per_suite(Config) ->
60    test_lib:recompile(?MODULE),
61    Config.
62
63end_per_suite(_Config) ->
64    ok.
65
66init_per_group(_GroupName, Config) ->
67    Config.
68
69end_per_group(_GroupName, Config) ->
70    Config.
71
72slow_group() ->
73    case ?MODULE of
74	receive_SUITE ->
75            %% Canononical module name. Run slow cases.
76            [{group,slow}];
77        _ ->
78            %% Cloned module. Don't run.
79            []
80    end.
81
82-record(state, {ena = true}).
83
84recv(Config) when is_list(Config) ->
85    Pid = spawn_link(fun() -> loop(#state{}) end),
86    Self = self(),
87    Pid ! {Self,test},
88    receive
89	{ok,test} -> ok;
90	{error,Other} ->
91	    io:format("Got unpexected ~p", [Other]),
92	    ct:fail(unexpected)
93    after 10000 ->
94	    ct:fail(no_answer)
95    end,
96    receive
97	X ->
98	    io:format("Unexpected extra message: ~p", [X]),
99	    ct:fail(unexpected)
100    after 10 ->
101            unlink(Pid),
102            exit(Pid, kill),
103	    ok
104    end,
105    ok.
106
107loop(S) ->
108    receive
109	_ when S#state.ena == false ->
110	    loop(S);
111	{P,test} ->
112	    P ! {ok,test},
113	    loop(S);
114	_X ->
115	    loop(S)
116    end.
117
118coverage(Config) when is_list(Config) ->
119    do_link(self()),
120    do_unlink(self()),
121    do_monitor_node(node(), true),
122    do_monitor_node(node(), false),
123    do_group_leader(group_leader(), self()),
124    id(node(self())),
125
126    erlang:'!'(self(), {a,10}),
127    self() ! {b,20},
128    [{a,10},{b,20}] = receive_all(),
129    self() ! {c,42},
130    receive
131	{c,42} ->
132	    ok
133    after infinity ->
134	    exit(cant_happen)
135    end,
136
137    self() ! 17,
138    self() ! 19,
139    59 = tuple_to_values(infinity, x),
140    61 = tuple_to_values(999999, x),
141    0 = tuple_to_values(1, x),
142
143    {'EXIT',{{badmap,[]},_}} = (catch monitor_plus_badmap(self())),
144
145
146    self() ! {data,no_data},
147    ok = receive_sink_tuple({any,pattern}),
148    {b,a} = receive_sink_tuple({a,b}),
149
150    %% Basically a smoke test of no_clauses_left/0.
151    smoke_receive(fun no_clauses_left_1/0),
152    smoke_receive(fun no_clauses_left_2/0),
153    smoke_receive(fun no_clauses_left_3/0),
154
155    receive_in_called_function(),
156
157    %% Make we haven't broken `timeout_locked` by inserting recv_marker_clear
158    %% in the wrong place.
159    Ref = make_ref(),
160    receive Ref -> ok after 0 -> ok end,
161
162    %% Cover handling of critical edges in beam_ssa_pre_codegen.
163    self() ! ok, ok = mc_fail_requests(),
164    self() ! {error, true}, ok = mc_fail_requests(),
165    self() ! {error, false}, self() ! {'DOWN', false}, ok = mc_fail_requests(),
166
167    Tid = {commit,[]},
168    self() ! {Tid, pre_commit},
169    self() ! {Tid, committed},
170    ok = commit_participant(whatever, Tid),
171
172    %% Cover handling in beam_kernel_to_ssa of a `receive` at the end of
173    %% an `after` block.
174    X = id(fun() -> ok end),
175    self() ! whatever,
176    try
177        X()
178    after
179        %% Smallish `after` blocks are not moved out to a wrapper
180        %% function, so we will need some filler code to force it
181        %% to move out.
182        length([X]), length([X]), length([X]), length([X]), length([X]),
183        length([X]), length([X]), length([X]), length([X]), length([X]),
184        length([X]), length([X]), length([X]), length([X]), length([X]),
185        length([X]), length([X]), length([X]), length([X]), length([X]),
186        receive _ -> ignored end
187    end,
188
189    %% Cover code for handling a non-boolean `br` in beam_ssa_dead.
190    self() ! whatever,
191    {'EXIT',{{badmatch,_},_}} = (catch [a || other = receive whatever -> false end]),
192
193    ok.
194
195receive_in_called_function() ->
196    RefA = make_ref(),
197    RefB = make_ref(),
198
199    self() ! hello,
200    self() ! RefA,
201
202    ricf_1(hello, RefA),
203
204    self() ! RefB,
205    self() ! hello,
206
207    ricf_1(RefB, hello),
208
209    Foo = id(gurka),
210    Bar = id(gaffel),
211
212    self() ! Foo,
213    self() ! Bar,
214
215    ricf_1(Foo, Bar),
216
217    ok.
218
219ricf_1(A, B) ->
220    %% Both A and B are fed a reference at least once, so both of these loops
221    %% ought to be optimized.
222    receive A -> ok end,
223    receive B -> ok end.
224
225monitor_plus_badmap(Pid) ->
226    monitor(process, Pid) + []#{}.
227
228receive_all() ->
229    receive
230	Any ->
231	    [Any|receive_all()]
232    after 0 ->
233	    []
234    end.
235
236do_monitor_node(Node, Bool) ->
237    monitor_node(Node, Bool).
238
239do_link(Pid) ->
240    link(Pid).
241
242do_unlink(Pid) ->
243    unlink(Pid).
244
245do_group_leader(Leader, Pid) ->
246    group_leader(Leader, Pid).
247
248
249%% cover sys_core_fold:tuple_to_values/2
250tuple_to_values(infinity, X) ->
251    {A,B} = case X of
252		x ->
253		    receive
254			Any ->
255			    {42,Any}
256		    end
257	    end,
258    A+B;
259tuple_to_values(Timeout, X) ->
260    {A,B} = case X of
261		x ->
262		    receive
263			Any ->
264			    {42,Any}
265		    after Timeout ->
266			    {0,0}
267		    end
268	    end,
269    A+B.
270
271no_clauses_left_1() ->
272    receive
273        %% This clause would be removed because it cannot match...
274        a = b ->
275            V = whatever
276    end,
277    %% ... leaving a reference to an unbound variable. Crash.
278    V.
279
280no_clauses_left_2() ->
281    [receive
282         %% This clause would be removed because it cannot match...
283         a = <<V0:(node())>> ->
284             year
285     end],
286    %% ... leaving a reference to an unbound variable. Crash.
287    V0.
288
289no_clauses_left_3() ->
290    case id([]) of
291        [] ->
292            receive
293                [Var] = [] ->
294                    ok
295            end
296    end,
297    Var.
298
299%% Cover a help function for beam_ssa_opt:ssa_opt_sink/1.
300receive_sink_tuple({Line,Pattern}) ->
301    receive
302        {data,_} ->
303            ok
304    after 1 ->
305            id({Pattern,Line})
306    end.
307
308%% Cover handling of critical edges in beam_ssa_pre_codegen.
309mc_fail_requests() ->
310    receive
311        ok ->
312            ok;
313        {error, ReqId} ->
314            case id(ReqId) of
315                true ->
316                    ok;
317                false ->
318                    receive
319                        {'DOWN', ReqId} ->
320                            ok
321                    after 0 ->
322                            ct:fail(failed)
323                    end
324            end
325    after 0 ->
326            ct:fail(failed)
327    end,
328    ok.
329
330%% Cover handling of critical edges in beam_ssa_pre_codegen.
331-record(commit, {schema_ops}).
332commit_participant(Coord, Tid) ->
333    try id(Tid) of
334        C ->
335            receive
336                {Tid, pre_commit} ->
337                    ExpectAck = C#commit.schema_ops /= [],
338                    receive
339                        {Tid, committed} ->
340                            case ExpectAck of
341                                false ->
342                                    ignore;
343                                true ->
344                                    id(Coord)
345                            end;
346                        Other ->
347                            Other
348                    end
349            after 0 ->
350                    ct:fail(failed)
351            end
352    catch
353        _:_ ->
354            ok
355    end,
356    ok.
357
358%% OTP-7980. Thanks to Vincent de Phily. The following code would
359%% be inccorrectly optimized by beam_jump.
360
361otp_7980(Config) when is_list(Config) ->
362    7 = otp_7980_add_clients(10),
363    ok.
364
365otp_7980_add_clients(Count) ->
366    Timeout = 42,
367    lists:foldl(fun(_, N) ->
368			case N of
369                           1 -> ok;
370			    _ -> receive after Timeout -> ok end
371			end,
372			N - 1
373		end, Count, [1,2,3]).
374
375ref_opt(Config) when is_list(Config) ->
376    DataDir = proplists:get_value(data_dir, Config),
377    PrivDir = proplists:get_value(priv_dir, Config),
378    Sources = filelib:wildcard(filename:join([DataDir,"ref_opt","*.{erl,S}"])),
379    test_lib:p_run(fun(Src) ->
380			   do_ref_opt(Src, PrivDir)
381		   end, Sources),
382    cover_recv_instructions(),
383    ok.
384
385do_ref_opt(Source, PrivDir) ->
386    try
387	Ext = filename:extension(Source),
388	{ok,Mod} = compile:file(Source, [report_errors,report_warnings,
389					 {outdir,PrivDir}] ++
390					[from_asm || Ext =:= ".S" ]),
391	Base = filename:rootname(filename:basename(Source), Ext),
392	code:purge(list_to_atom(Base)),
393	BeamFile = filename:join(PrivDir, Base),
394	code:load_abs(BeamFile),
395	ok = Mod:Mod(),
396	{beam_file,Mod,_,_,_,Code} = beam_disasm:file(BeamFile),
397	case Base of
398	    "no_"++_ ->
399		[] = collect_recv_opt_instrs(Code);
400	    "yes_"++_ ->
401                Instrs = collect_recv_opt_instrs(Code),
402
403                %% At least one of each marker instruction must be present in
404                %% the module.
405                true = lists:any(fun
406                                     ({recv_marker_reserve,_}) -> true;
407                                     (_) -> false
408                                end, Instrs),
409                true = lists:any(fun
410                                     ({recv_marker_bind,_,_}) -> true;
411                                     (_) -> false
412                                end, Instrs),
413                true = lists:any(fun
414                                     ({recv_marker_clear,_}) -> true;
415                                     (_) -> false
416                                end, Instrs),
417                true = lists:any(fun
418                                     ({recv_marker_use,_}) -> true;
419                                     (_) -> false
420                                end, Instrs)
421	end,
422	ok
423    catch Class:Error:Stk ->
424	    io:format("~s: ~p ~p\n~p\n", [Source,Class,Error,Stk]),
425	    error
426    end.
427
428collect_recv_opt_instrs(Code) ->
429    L = [ [I || I <- Is,
430		begin
431		    case I of
432			{recv_marker_bind,_,_} -> true;
433			{recv_marker_clear,_} -> true;
434			{recv_marker_reserve,_} -> true;
435			{recv_marker_use,_} -> true;
436			_ -> false
437		    end
438		end] || {function,_,_,_,Is} <- Code],
439    lists:append(L).
440
441cover_recv_instructions() ->
442    %% We want to cover the handling of receive markers in beam_utils.
443    %% Since those instructions are introduced in a late optimization pass,
444    %% beam_utils:live_opt() will not see them unless the compilation is
445    %% started from a .S file. The compile_SUITE:asm/1 test case will
446    %% compile all test suite files to .S and then run them through the
447    %% compiler again.
448    %%
449    %% Here will we will ensure that this modules contains recv_mark
450    %% and recv_set instructions.
451    Pid = spawn_link(fun() ->
452			     receive {Parent,Ref} ->
453				     Parent ! Ref
454			     end
455		     end),
456    Ref = make_ref(),
457    Pid ! {self(),Ref},
458    receive
459	Ref -> ok
460    end.
461
462export(Config) when is_list(Config) ->
463    Ref = make_ref(),
464    self() ! {result,Ref,42},
465    42 = export_1(Ref),
466    {error,timeout} = export_1(Ref),
467
468    self() ! {result,Ref},
469    {ok,Ref} = export_2(),
470
471    ok.
472
473export_1(Reference) ->
474    id(Reference),
475    receive
476	{result,Reference,Result} ->
477	    Result
478    after 1 ->
479	    Result = {error,timeout}
480    end,
481    %% Result ({x,1}) is used, but not the return value ({x,0})
482    %% of the receive. Used to be incorrectly optimized
483    %% by beam_block.
484    id({build,self()}),
485    Result.
486
487export_2() ->
488    receive {result,Result} -> ok end,
489    {ok,Result}.
490
491wait(Config) when is_list(Config) ->
492    self() ! <<42>>,
493    <<42>> = wait_1(r, 1, 2),
494    {1,2,3} = wait_1(1, 2, 3),
495    {'EXIT',{timeout_value,_}} = (catch receive after [] -> timeout end),
496    ok.
497
498wait_1(r, _, _) ->
499    receive
500	B when byte_size(B) > 0 ->
501	    B
502    end;
503%% beam_utils would wrongly assume that wait/1 could fall through
504%% to the next clause.
505wait_1(A, B, C) ->
506    {A,B,C}.
507
508recv_in_try(_Config) ->
509    self() ! {ok,fh}, {ok,fh} = recv_in_try_1(infinity, native),
510    self() ! {ok,ignored}, {ok,42} = recv_in_try_1(infinity, plain),
511    self() ! {error,ignored}, nok = recv_in_try_1(infinity, plain),
512    timeout = recv_in_try_1(1, plain),
513
514    smoke_receive(fun recv_in_try_2/0),
515    smoke_receive(fun recv_in_try_3/0),
516    smoke_receive(fun recv_in_try_4/0),
517    smoke_receive(fun recv_in_try_5/0),
518    smoke_receive(fun recv_in_catch_1/0),
519
520    ok.
521
522recv_in_try_1(Timeout, Format) ->
523    try
524	receive
525	    {Status,History} ->
526                %% {test,is_tuple,{f,148},[{x,0}]}.
527                %% {test,test_arity,{f,148},[{x,0},2]}.
528                %% {get_tuple_element,{x,0},0,{y,1}}.  %y1 is fragile.
529                %%
530                %% %% Here the fragility of y1 would be be progated to
531                %% %% the 'catch' below. Incorrect, since get_tuple_element
532                %% %% can't fail.
533                %% {get_tuple_element,{x,0},1,{x,2}}.
534                %%
535                %% remove_message.                     %y1 fragility cleared.
536		FH = case Format of
537			native ->
538                             id(History);
539			plain ->
540                             id(42)
541		    end,
542		case Status of
543		    ok ->
544			{ok,FH};
545		    error ->
546			nok
547		end
548	after Timeout ->
549		timeout
550	end
551    catch
552        %% The fragility of y1 incorrectly propagated to here.
553        %% beam_validator would complain.
554	throw:{error,Reason} ->
555	    {nok,Reason}
556    end.
557
558recv_in_try_2() ->
559    try
560        %% The live range of the try tag would stop here because of
561        %% the infinite receive below. The code generator would
562        %% generate a kill instruction that would kill the try tag.
563        %% Although probably safe in practice, beam_validator does not
564        %% consider it safe.
565        _ = (catch try a after [] end),
566        receive after infinity -> ok end
567    after
568        []
569    end.
570
571recv_in_try_3() ->
572    #{make_ref() =>
573          not (catch
574                   (catch 9 = kid)#{key =>
575                                        receive after infinity ->
576                                                        ok
577                                                end})}.
578
579recv_in_try_4() ->
580    #{make_ref() =>
581          not (catch
582                   (catch 9 = kid)#{key =>
583                                        receive
584                                        [] when false ->
585                                                ok
586                                        end})}.
587recv_in_try_5() ->
588    try [] of
589        [] ->
590            <<
591              << <<0>> || <<3:4>> <= <<3:4>> >>:(get()),
592              receive
593              after
594                  infinity -> ok
595              end
596            >>
597    after
598        ok
599    end.
600
601recv_in_catch_1() ->
602    catch
603        (catch
604             try
605                 some_module
606             after
607                 ok
608             end):some_function(receive
609                                after infinity -> ok
610                                end#{key := value}).
611
612%% ERL-703. The compiler would crash because beam_utils:anno_defs/1
613%% failed to take into account that code after loop_rec_end is
614%% unreachable.
615
616double_recv(_Config) ->
617    self() ! {more,{a,term}},
618    ok = do_double_recv({more,{a,term}}, any),
619    self() ! message,
620    ok = do_double_recv(whatever, message),
621
622    error = do_double_recv({more,42}, whatever),
623    error = do_double_recv(whatever, whatever),
624    ok.
625
626do_double_recv({more, Rest}, _Msg) ->
627    receive
628        {more, Rest} ->
629            ok
630    after 0 ->
631            error
632    end;
633do_double_recv(_, Msg) ->
634    receive
635        Msg ->
636            ok
637    after 0 ->
638            error
639    end.
640
641%% Test 'after Z', when Z =:= 0 been propagated as an immediate by the type
642%% optimization pass.
643receive_var_zero(Config) when is_list(Config) ->
644    self() ! x,
645    self() ! y,
646    Z = zero(),
647    timeout = receive
648                  z -> ok
649              after Z -> timeout
650              end,
651    timeout = receive
652              after Z -> timeout
653              end,
654    self() ! w,
655    receive
656	x ->
657            receive y -> ok end,
658            receive w -> ok end;
659	Other ->
660	    ct:fail({bad_message,Other})
661    end.
662
663zero() -> 0.
664
665%% ERL-862; the validator would explode when a term was constructed in a
666%% receive guard.
667
668-define(MATCH_BUILT_TERM(Ref, Expr),
669        (fun() ->
670                 Ref = make_ref(),
671                 A = id($a),
672                 B = id($b),
673                 Built = id(Expr),
674                 self() ! {Ref, A, B},
675                 receive
676                     {Ref, A, B} when Expr =:= Built ->
677                         ok
678                 after 5000 ->
679                     ct:fail("Failed to match message with term built in "
680                             "receive guard.")
681                 end
682         end)()).
683
684match_built_terms(Config) when is_list(Config) ->
685    ?MATCH_BUILT_TERM(Ref, [A, B]),
686    ?MATCH_BUILT_TERM(Ref, {A, B}),
687    ?MATCH_BUILT_TERM(Ref, <<A, B>>),
688    ?MATCH_BUILT_TERM(Ref, #{ 1 => A, 2 => B}).
689
690elusive_common_exit(_Config) ->
691    self() ! {1, a},
692    self() ! {2, b},
693    {[z], [{2,b},{1,a}]} = elusive_loop([x,y,z], 2, []),
694
695    CodeServer = whereis(code_server),
696    Self = self(),
697    Self ! {Self, abc},
698    Self ! {CodeServer, []},
699    Self ! {Self, other},
700    try elusive2([]) of
701        Unexpected ->
702            ct:fail("Expected an exception; got ~p\n", [Unexpected])
703    catch
704        throw:[other, CodeServer, Self] ->
705            ok
706    end,
707
708    ok.
709
710elusive_loop(List, 0, Results) ->
711    {List, Results};
712elusive_loop(List, ToReceive, Results) ->
713    {Result, RemList} =
714        receive
715            {_Pos, _R} = Res when List =/= [] ->
716                [_H|T] = List,
717                {Res, T};
718            {_Pos, _R} = Res when List =:= [] ->
719                {Res, []}
720        end,
721    %% beam_ssa_pre_codegen:fix_receives() would fail to find
722    %% the common exit block for this receive. That would mean
723    %% that it would not insert all necessary copy instructions.
724    elusive_loop(RemList, ToReceive-1, [Result | Results]).
725
726
727elusive2(Acc) ->
728    receive
729        {Pid, abc} ->
730            ok;
731        {Pid, []} ->
732            ok;
733        {Pid, Res} ->
734            %% beam_ssa_pre_codegen:find_loop_exit/2 attempts to find
735            %% the first block of the common code after the receive
736            %% statement. It used to only look at the two last clauses
737            %% of the receive. In this function, the last two clauses
738            %% don't have any common block, so it would be assumed
739            %% that there was no common block for any of the
740            %% clauses. That would mean that copy instructions would
741            %% not be inserted as needed.
742            throw([Res | Acc])
743    end,
744    %% Common code.
745    elusive2([Pid | Acc]).
746
747return_before_receive(_Config) ->
748    ref_received = do_return_before_receive(),
749    ok.
750
751do_return_before_receive() ->
752    Ref = make_ref(),
753    self() ! {ref,Ref},
754    maybe_receive(id(false)),
755    receive
756        {ref,Ref} ->
757            ref_received
758    after 1 ->
759            %% Can only be reached if maybe_receive/1 returned
760            %% with the receive marker set.
761            timeout
762    end.
763
764maybe_receive(Bool) ->
765    NewRef = make_ref(),
766    case Bool of
767        true ->
768            receive
769                NewRef ->
770                    ok
771            end;
772        false ->
773            %% The receive marker must not be set when
774            %% leaving this function.
775            ok
776    end.
777
778trapping(_Config) ->
779    ok = do_trapping(0),
780    ok = do_trapping(1),
781    ok.
782
783%% Simplified from emulator's binary_SUITE:trapping/1.
784do_trapping(N) ->
785    Ref = make_ref(),
786    self() ! Ref,
787    case N rem 2 of
788	0 ->
789            %% Would generate recv_set _, label _, wait_timeout _ _,
790            %% which the loader can't handle.
791            receive after 1 -> ok end;
792	1 ->
793            void
794    end,
795    receive Ref -> ok end,
796    receive after 1 -> ok end.
797
798after_expression(_Config) ->
799    self() ! {a,message},
800    {a,message} = after_expr(0),
801    timeout = after_expr(0),
802    timeout = after_expr(10),
803    ok = after_expr_timeout(0),
804    ok = after_expr_timeout(1),
805    ok.
806
807after_expr(Timeout) ->
808    receive
809        Msg -> Msg
810    after id(Timeout) ->
811            timeout
812    end.
813
814after_expr_timeout(Timeout) ->
815    receive
816    after id(Timeout) ->
817            ok
818    end.
819
820in_after(_Config) ->
821    self() ! first,
822    self() ! message,
823    do_in_after(fun() -> ok end),
824    do_in_after(fun() -> ok end),
825    self() ! message,
826    catch do_in_after(fun() -> error(bad) end),
827    catch do_in_after(fun() -> error(bad) end),
828    self() ! last,
829    first = receive M1 -> M1 end,
830    last = receive M2 -> M2 end,
831    ok.
832
833do_in_after(E) ->
834    try
835        E()
836    after
837        receive
838            message ->
839                ok
840        after 1 ->
841                ok
842        end
843    end,
844    ok.
845
846type_optimized_markers(_Config) ->
847    Ref = make_ref(),
848
849    self() ! foobar,
850    gurka = tom_1(id(undefined)),
851
852    self() ! Ref,
853    gaffel = tom_1(Ref),
854
855    self() ! foobar,
856    self() ! undefined,
857    gurka = tom_2(id(undefined)),
858
859    self() ! Ref,
860    gaffel = tom_2(Ref),
861
862    ok.
863
864tom_1(Ref) ->
865    receive
866        foobar ->
867            case Ref of
868                undefined ->
869                    %% Type optimization might replace the reference with
870                    %% 'undefined' here, passing an illegal literal argument
871                    %% to 'recv_marker_clear'.
872                    gurka;
873                _ ->
874                    demonitor(Ref, [flush]),
875                    gaffel
876            end;
877        Ref ->
878            gaffel
879    end.
880
881tom_2(Ref) ->
882    receive
883        foobar ->
884            case Ref of
885                undefined ->
886                    %% As tom_1/1 but with 'recv_marker_use'
887                    %% instead.
888                    receive
889                        Ref ->
890                            gurka
891                    end;
892                _ ->
893                    demonitor(Ref, [flush]),
894                    gaffel
895            end;
896        Ref ->
897            gaffel
898    end.
899
900%%%
901%%% Common utilities.
902%%%
903
904smoke_receive(Fun) ->
905    NoClausesLeft = spawn(Fun),
906    receive after 1 -> ok end,
907    exit(NoClausesLeft, kill).
908
909id(I) -> I.
910