1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2003-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 : Test records.
21
22-module(record_SUITE).
23
24-include_lib("common_test/include/ct.hrl").
25
26-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
27	 init_per_group/2,end_per_group/2,
28	 init_per_testcase/2,end_per_testcase/2,
29	 errors/1,record_test_2/1,record_test_3/1,record_access_in_guards/1,
30	 guard_opt/1,eval_once/1,foobar/1,missing_test_heap/1,
31	 nested_access/1,coverage/1,grab_bag/1,slow_compilation/1]).
32
33init_per_testcase(_Case, Config) ->
34    Config.
35
36end_per_testcase(_Case, _Config) ->
37    ok.
38
39suite() ->
40    [{ct_hooks,[ts_install_cth]},
41     {timetrap,{minutes,2}}].
42
43all() ->
44    [{group,p}].
45
46groups() ->
47    [{p,test_lib:parallel(),
48      [errors,record_test_2,record_test_3,
49       record_access_in_guards,guard_opt,eval_once,foobar,
50       missing_test_heap,nested_access,coverage,grab_bag,
51       slow_compilation]}].
52
53
54init_per_suite(Config) ->
55    test_lib:recompile(?MODULE),
56    Config.
57
58end_per_suite(_Config) ->
59    ok.
60
61init_per_group(_GroupName, Config) ->
62    Config.
63
64end_per_group(_GroupName, Config) ->
65    Config.
66
67
68-record(foo, {a,b,c,d}).
69-record(bar, {a,b,c,d}).
70-record(barf, {a,b,c,d,e}).
71
72errors(Config) when is_list(Config) ->
73    Foo = #foo{a=1,b=2,c=3,d=4},
74    #foo{a=19,b=42,c=3,d=4} = update_foo(Foo, 19, 42),
75
76    {'EXIT',{{badrecord,bar},_}} = (catch update_foo_bar(Foo, 19)),
77    {'EXIT',{{badrecord,bar},_}} = (catch update_foo_bar(Foo, 19, 35)),
78    {'EXIT',{{badrecord,bar},_}} = (catch update_foo_bar(Foo, 19, 35, 17)),
79    {'EXIT',{{badrecord,bar},_}} = (catch update_foo_bar(Foo, 19, 35, 17, 42)),
80
81    {'EXIT',{{badrecord,barf},_}} = (catch update_foo_barf(Foo, 19)),
82    {'EXIT',{{badrecord,barf},_}} = (catch update_foo_barf(Foo, 19, 35)),
83    {'EXIT',{{badrecord,barf},_}} = (catch update_foo_barf(Foo, 19, 35, 17)),
84    {'EXIT',{{badrecord,barf},_}} = (catch update_foo_barf(Foo, 19, 35, 17, 42)),
85    {'EXIT',{{badrecord,barf},_}} = (catch update_foo_barf(Foo, 19,
86								 35, 17, 42, -2)),
87
88    ok.
89
90update_foo(#foo{}=R, A, B) ->
91    R#foo{a=A,b=B}.
92
93update_foo_bar(#foo{}=R, A) ->
94    R#bar{a=A}.
95
96update_foo_bar(#foo{}=R, A, _B) ->
97    R#bar{a=A,b=A}.
98
99update_foo_bar(#foo{}=R, A, _B, C) ->
100    R#bar{a=A,b=A,c=C}.
101
102update_foo_bar(#foo{}=R, A, _B, C, D) ->
103    R#bar{a=A,b=A,c=C,d=D}.
104
105update_foo_barf(#foo{}=R, A) ->
106    R#barf{a=A}.
107
108update_foo_barf(#foo{}=R, A, _B) ->
109    R#barf{a=A,b=A}.
110
111update_foo_barf(#foo{}=R, A, _B, C) ->
112    R#barf{a=A,b=A,c=C}.
113
114update_foo_barf(#foo{}=R, A, _B, C, D) ->
115    R#barf{a=A,b=A,c=C,d=D}.
116
117update_foo_barf(#foo{}=R, A, _B, C, D, E) ->
118    R#barf{a=A,b=A,c=C,d=D,e=E}.
119
120
121-define(TrueGuard(Expr), if Expr -> ok; true -> ct:fail(failed) end).
122-define(FalseGuard(Expr), if Expr -> ct:fail(failed); true -> ok end).
123
124record_test_2(Config) when is_list(Config) ->
125    true = is_record(#foo{}, foo),
126    false = is_record(#foo{}, barf),
127    false = is_record({foo}, foo),
128
129    true = erlang:is_record(#foo{}, foo),
130    false = erlang:is_record(#foo{}, barf),
131    false = erlang:is_record({foo}, foo),
132
133    false = is_record([], foo),
134    false = is_record(Config, foo),
135
136    ?TrueGuard(is_record(#foo{}, foo)),
137    ?FalseGuard(is_record(#foo{}, barf)),
138    ?FalseGuard(is_record({foo}, foo)),
139
140    ?TrueGuard(erlang:is_record(#foo{}, foo)),
141    ?FalseGuard(erlang:is_record(#foo{}, barf)),
142    ?FalseGuard(erlang:is_record({foo}, foo)),
143
144    ?FalseGuard(is_record([], foo)),
145    ?FalseGuard(is_record(Config, foo)),
146
147    %% 'not is_record/2' to test guard optimization.
148
149    ?FalseGuard(not is_record(#foo{}, foo)),
150    ?TrueGuard(not is_record(#foo{}, barf)),
151    ?TrueGuard(not is_record({foo}, foo)),
152
153    ?FalseGuard(not erlang:is_record(#foo{}, foo)),
154    ?TrueGuard(not erlang:is_record(#foo{}, barf)),
155    ?TrueGuard(not erlang:is_record({foo}, foo)),
156
157    Foo = id(#foo{}),
158    ?FalseGuard(not erlang:is_record(Foo, foo)),
159    ?TrueGuard(not erlang:is_record(Foo, barf)),
160
161    ?TrueGuard(not is_record(Config, foo)),
162
163    ?TrueGuard(not is_record(a, foo)),
164    ?TrueGuard(not is_record([], foo)),
165
166    %% Pass non-literal first argument.
167
168    true = is_record(id(#foo{}), foo),
169    false = is_record(id(#foo{}), barf),
170    false = is_record(id({foo}), foo),
171
172    true = erlang:is_record(id(#foo{}), foo),
173    false = erlang:is_record(id(#foo{}), barf),
174    false = erlang:is_record(id({foo}), foo),
175
176    NoRec1 = id(blurf),
177    NoRec2 = id([]),
178
179    ?TrueGuard(not is_record(NoRec1, foo)),
180    ?TrueGuard(not is_record(NoRec2, foo)),
181
182    %% The optimizer attempts to move expressions to guards,
183    %% but it must not move an is_record/2 call that is not
184    %% allowed in a guard in the first place.
185
186    ok = case is_record(id({a}), id(a)) of
187		   true -> ok;
188		   false -> error
189	       end,
190
191    %% Force the use of guard bifs by using the 'xor' operation.
192
193    False = id(false),
194    ?TrueGuard(is_record(#foo{}, foo) xor False),
195    ?FalseGuard(is_record(#foo{}, barf) xor False),
196    ?FalseGuard(is_record({foo}, foo) xor False ),
197
198    ?TrueGuard(is_record(Foo, foo) xor False),
199    ?FalseGuard(is_record(Foo, barf) xor False),
200
201
202    %% Implicit guards by using a list comprehension.
203
204    List = id([1,#foo{a=2},3,#bar{d=4},5,#foo{a=6},7]),
205
206    [#foo{a=2},#foo{a=6}] = [X || X <- List, is_record(X, foo)],
207    [#bar{d=4}] = [X || X <- List, is_record(X, bar)],
208    [1,#foo{a=2},3,5,#foo{a=6},7] =
209	[X || X <- List, not is_record(X, bar)],
210    [1,3,5,7] =
211	[X || X <- List, ((not is_record(X, bar)) and (not is_record(X, foo)))],
212    [#foo{a=2},#bar{d=4},#foo{a=6}] =
213	[X || X <- List, ((is_record(X, bar)) or (is_record(X, foo)))],
214    [1,3,#bar{d=4}] =
215	[X || X <- List, ((is_record(X, bar)) or (X < 5))],
216
217    MyList = [#foo{a=3},x,[],{a,b}],
218    [#foo{a=3}] = [X || X <- MyList, is_record(X, foo)],
219    [x,[],{a,b}] = [X || X <- MyList, not is_record(X, foo)],
220    [#foo{a=3}] = [X || X <- MyList, begin is_record(X, foo) end],
221    [x,[],{a,b}] = [X || X <- MyList, begin not is_record(X, foo) end],
222    [#foo{a=3},x,[],{a,b}] = [X || X <- MyList, is_record(X, foo) or
223				       not is_binary(X)],
224    [#foo{a=3},x,[],{a,b}] = [X || X <- MyList, not is_record(X, foo) or
225				       not is_binary(X)],
226    [#foo{a=3}] = [X || X <- MyList, is_record(X, foo) or is_reference(X)],
227    [x,[],{a,b}] = [X || X <- MyList, not is_record(X, foo) or
228			     is_reference(X)],
229    [#foo{a=3},x,[],{a,b}] = [X || X <- MyList,
230				   begin is_record(X, foo) or
231					     not is_binary(X) end],
232    [#foo{a=3},x,[],{a,b}] = [X || X <- MyList,
233				   begin not is_record(X, foo) or
234					     not is_binary(X) end],
235    [#foo{a=3}] = [X || X <- MyList,
236			begin is_record(X, foo) or is_reference(X) end],
237    [x,[],{a,b}] = [X || X <- MyList,
238			 begin not is_record(X, foo) or
239				   is_reference(X) end],
240
241    %% Call is_record/2 with illegal arguments.
242    [] = [X || X <- [], is_record(t, id(X))],
243    {'EXIT',{badarg,_}} = (catch [X || X <- [1], is_record(t, id(X))]),
244
245    %% Update several fields with a string literal.
246    #barf{} = Barf0 = id(#barf{}),
247    Barf = update_barf(Barf0),
248    #barf{a="abc",b=1} = id(Barf),
249
250    %% Test optimization of is_record/3.
251    false = case id({a,b}) of
252		{_,_}=Tuple -> is_record(Tuple, foo)
253	    end,
254    false = case id(true) of
255		true=Bool -> is_record(Bool, foo)
256	    end,
257
258    ok.
259
260record_test_3(Config) when is_list(Config) ->
261    true = is_record(#foo{}, foo, 5),
262    false = is_record(#foo{}, barf, 5),
263    false = is_record(#foo{}, barf, 6),
264    false = is_record({foo}, foo, 5),
265
266    true = erlang:is_record(#foo{}, foo, 5),
267    false = erlang:is_record(#foo{}, barf, 5),
268    false = erlang:is_record({foo}, foo, 5),
269
270    false = is_record([], foo),
271    false = is_record(Config, foo),
272
273    ?TrueGuard(is_record(#foo{}, foo, 5)),
274    ?FalseGuard(is_record(#foo{}, barf, 5)),
275    ?FalseGuard(is_record(#foo{}, barf, 6)),
276    ?FalseGuard(is_record({foo}, foo, 5)),
277
278    ?TrueGuard(erlang:is_record(#foo{}, foo, 5)),
279    ?FalseGuard(erlang:is_record(#foo{}, barf, 5)),
280    ?FalseGuard(erlang:is_record(#foo{}, barf, 6)),
281    ?FalseGuard(erlang:is_record({foo}, foo, 5)),
282
283    ?FalseGuard(is_record([], foo, 5)),
284    ?FalseGuard(is_record(Config, foo, 5)),
285
286    %% 'not is_record/2' to test guard optimization.
287
288    ?FalseGuard(not is_record(#foo{}, foo, 5)),
289    ?TrueGuard(not is_record(#foo{}, barf, 6)),
290    ?TrueGuard(not is_record({foo}, foo, 5)),
291
292    ?FalseGuard(not erlang:is_record(#foo{}, foo, 5)),
293    ?TrueGuard(not erlang:is_record(#foo{}, barf, 5)),
294    ?TrueGuard(not erlang:is_record({foo}, foo, 5)),
295
296    Foo = id(#foo{}),
297    ?FalseGuard(not erlang:is_record(Foo, foo, 5)),
298    ?TrueGuard(not erlang:is_record(Foo, barf, 6)),
299
300    ?TrueGuard(not is_record(Config, foo, 5)),
301
302    ?TrueGuard(not is_record(a, foo, 5)),
303    ?TrueGuard(not is_record([], foo, 5)),
304
305    %% Pass non-literal first argument.
306
307    true = is_record(id(#foo{}), foo, 5),
308    false = is_record(id(#foo{}), barf, 6),
309    false = is_record(id({foo}), foo, 5),
310
311    true = erlang:is_record(id(#foo{}), foo, 5),
312    false = erlang:is_record(id(#foo{}), barf, 6),
313    false = erlang:is_record(id({foo}), foo, 5),
314
315    NoRec1 = id(blurf),
316    NoRec2 = id([]),
317
318    ?TrueGuard(not is_record(NoRec1, foo, 5)),
319    ?TrueGuard(not is_record(NoRec2, foo, 5)),
320
321    %% Force the use of guard bifs by using the 'xor' operation.
322
323    False = id(false),
324    ?TrueGuard(is_record(#foo{}, foo, 5) xor False),
325    ?FalseGuard(is_record(#foo{}, barf, 6) xor False),
326    ?FalseGuard(is_record({foo}, foo, 5) xor False ),
327
328    ?TrueGuard(is_record(Foo, foo, 5) xor False),
329    ?FalseGuard(is_record(Foo, barf, 6) xor False),
330
331
332    %% Implicit guards by using a list comprehension.
333
334    List = id([1,#foo{a=2},3,#bar{d=4},5,#foo{a=6},7]),
335
336    [#foo{a=2},#foo{a=6}] = [X || X <- List, is_record(X, foo, 5)],
337    [#bar{d=4}] = [X || X <- List, is_record(X, bar, 5)],
338    [1,#foo{a=2},3,5,#foo{a=6},7] =
339	[X || X <- List, not is_record(X, bar, 5)],
340    [1,3,5,7] =
341	[X || X <- List, ((not is_record(X, bar, 5)) and (not is_record(X, foo, 5)))],
342    [#foo{a=2},#bar{d=4},#foo{a=6}] =
343	[X || X <- List, ((is_record(X, bar, 5)) or (is_record(X, foo, 5)))],
344    [1,3,#bar{d=4}] =
345	[X || X <- List, ((is_record(X, bar, 5)) or (X < 5))],
346
347    MyList = [#foo{a=3},x,[],{a,b}],
348    [#foo{a=3}] = [X || X <- MyList, is_record(X, foo, 5)],
349    [x,[],{a,b}] = [X || X <- MyList, not is_record(X, foo, 5)],
350    [#foo{a=3}] = [X || X <- MyList, begin is_record(X, foo, 5) end],
351    [x,[],{a,b}] = [X || X <- MyList, begin not is_record(X, foo, 5) end],
352    [#foo{a=3},x,[],{a,b}] = [X || X <- MyList, is_record(X, foo, 5) or
353				       not is_binary(X)],
354    [#foo{a=3},x,[],{a,b}] = [X || X <- MyList, not is_record(X, foo, 5) or
355				       not is_binary(X)],
356    [#foo{a=3}] = [X || X <- MyList, is_record(X, foo) or is_reference(X)],
357    [x,[],{a,b}] = [X || X <- MyList, not is_record(X, foo) or
358			     is_reference(X)],
359    [#foo{a=3},x,[],{a,b}] = [X || X <- MyList,
360				   begin is_record(X, foo, 5) or
361					     not is_binary(X) end],
362    [#foo{a=3},x,[],{a,b}] = [X || X <- MyList,
363				   begin not is_record(X, foo, 5) or
364					     not is_binary(X) end],
365    [#foo{a=3}] = [X || X <- MyList,
366			begin is_record(X, foo, 5) or is_reference(X) end],
367    [x,[],{a,b}] = [X || X <- MyList,
368			 begin not is_record(X, foo, 5) or
369				   is_reference(X) end],
370
371    %% Update several fields with a string literal.
372    #barf{} = Barf0 = id(#barf{}),
373    Barf = update_barf(Barf0),
374    #barf{a="abc",b=1} = id(Barf),
375
376    %% Non-literal arguments.
377    true = is_record(id(#barf{}), id(barf), id(6)),
378    false = is_record(id(#barf{}), id(barf), id(42)),
379    false = is_record(id(#barf{}), id(foo), id(6)),
380
381    Rec = id(#barf{}),
382    Good = id(barf),
383    Bad = id(foo),
384    Size = id(6),
385
386    true = is_record(Rec, Good, Size) orelse error,
387    error = is_record(Rec, Bad, Size) orelse error,
388
389    ok.
390
391record_access_in_guards(Config) when is_list(Config) ->
392    Priv = proplists:get_value(priv_dir, Config),
393    file:set_cwd(test_lib:get_data_dir(Config)),
394    Opts0 = [{outdir,Priv},report_errors|test_lib:opt_opts(?MODULE)],
395    M = record_access_in_guards,
396
397    Opts = [strict_record_tests|Opts0],
398    io:format("Options: ~p\n", [Opts]),
399    {ok,M} = c:c(M, Opts),
400    ok = M:t(),
401    ok.
402
403
404%% Test optimization of record access and is_record/2 in guards.
405
406-record(r, {a = 4,b}).
407-record(r1, {a,b}).
408-record(r2, {a = #r1{},b,c=length([1,2,3])}).
409-record(r3, {a = fun(_) -> #r1{} end(1), b}).
410
411guard_opt(Config) when is_list(Config) ->
412    ok = fun() ->
413		 F = fun(F, [H,H|T]) when is_record(H, r) ->
414			     [H|F(F, T)];
415			(F, [H|T]) when is_record(H, r) ->
416			     [H|F(F, T)];
417			(_, []) -> []
418		     end,
419		 [#r{a=4,b=7},#r{a=1,b=42}] =
420		     F(F, [#r{a=4,b=7},#r{a=4,b=7},#r{a=1,b=42}]),
421		 {'EXIT',_} = (catch F(F, [#r1{}])),
422		 ok
423	 end(),
424
425    true = fun() ->
426		   R = #r{},
427		   if is_record(R, r) -> true; true -> false end
428	   end(),
429
430    ok = fun() ->
431		 F = fun(true, B) when B#r1.a -> ok;
432			(false, _) -> error
433		     end,
434		 ok = F(true, #r1{a=true}),
435		 error = F(false, anything_goes),
436		 {'EXIT',_} = (catch F(true, #r1{})),
437		 {'EXIT',_} = (catch F(true, #r{})),
438		 ok
439	 end(),
440
441    ok = fun() ->
442		 F = fun([{a,R}=T]) when R#r.a =:= 42 ->
443			     {ok,tuple_size(T)};
444			([{a,R}=T]) when R#r1.a =:= 7 ->
445			     {ok,tuple_size(T)};
446			(_) -> error
447		     end,
448		 {ok,2} = F([{a,#r{a=42}}]),
449		 {ok,2} = F([{a,#r1{a=7}}]),
450		 error = F([{a,#r1{}}]),
451		 error = F({a,b,c}),
452		 error = F([]),
453		 ok
454	 end(),
455
456    ok = fun() ->
457		 F = fun(X, Y, Z) when is_record(X, r1) andalso
458				       (is_record(Y, r2) orelse
459					is_record(Z, r3)) -> true;
460			(_, _, _) -> false
461		     end,
462		 true = F(#r1{}, #r2{}, #r3{}),
463		 true = F(#r1{}, #r2{}, blurf),
464		 true = F(#r1{}, blurf, #r3{}),
465		 false = F(#r1{}, blurf, blurf),
466		 false = F(blurf, #r2{}, #r3{}),
467		 false = F(blurf, #r2{}, blurf),
468		 false = F(blurf, blurf, #r3{}),
469		 false = F(blurf, blurf, blurf),
470		 ok
471	 end(),
472
473    ok = fun() ->
474		 F = fun(R=#r{a=42}) when R#r.b =:= 7 ->
475			     {ok,R};
476			(_) -> error
477		     end,
478		 {ok,#r{a=42,b=7}} = F(#r{a=42,b=7}),
479		 error = F(#r{}),
480		 error = F([a,b,c]),
481		 ok
482	 end(),
483
484    ok.
485
486update_barf(R) ->
487    R#barf{a="abc",b=1}.
488
489eval_once(Config) when is_list(Config) ->
490    once(fun(GetRec) ->
491		       true = erlang:is_record(GetRec(), foo)
492	       end, #foo{}),
493    once(fun(GetRec) ->
494		       (GetRec())#foo{a=1}
495	       end, #foo{}),
496    once(fun(GetRec) ->
497		       (GetRec())#foo{a=1,b=2}
498	       end, #foo{}),
499    once(fun(GetRec) ->
500		       (GetRec())#foo{a=1,b=2,c=3}
501	       end, #foo{}),
502    once(fun(GetRec) ->
503		       (GetRec())#foo{a=1,b=2,c=3,d=4}
504	       end, #foo{}),
505    ok.
506
507once(Test, Record) ->
508    put(?MODULE, 0),
509    GetRec = fun() ->
510		     put(?MODULE, 1+get(?MODULE)),
511		     Record
512	     end,
513    Result = Test(GetRec),
514    case get(?MODULE) of
515	1 -> ok;
516	N ->
517	    io:format("Evaluated ~w times\n", [N]),
518	    ct:fail(more_than_once)
519    end,
520    Result.
521
522%% Thanks to Martin Bjorklund.
523
524-record(foobar, {status}).
525
526foobar(Config) when is_list(Config) ->
527    {ok,_,_} = x({foo, 1}),
528    ok.
529
530get_bar() ->
531    #foobar{status = 1}.
532
533x(Trans) ->
534    {foo, Barno} = Trans,
535    case get_bar() of
536	Bar when Bar#foobar.status == 1 ->
537	    noop(Bar),
538	    Bar33 = Bar#foobar{status = 1},
539	    {ok, Bar33, Barno};
540	_ ->
541	    Trans
542    end.
543
544noop(_) ->
545    ok.
546
547-record(foo_rec,
548	{foo_1,
549	 foo_2 = 0,
550	 foo_3 = 0}).
551
552missing_test_heap(Config) when is_list(Config) ->
553    #foo_rec{foo_2=2,foo_3=5} = missing_test_heap_1(#foo_rec{foo_2=1,foo_3=4}),
554    ok.
555
556
557%% Two test_heap instructions would be incorrectly merged (not allowed
558%% because of gc_bif instructions for addition).
559missing_test_heap_1(A = #foo_rec {foo_1 = _B,
560				  foo_3 = C,
561				  foo_2 = D}) ->
562    A#foo_rec {foo_1 = {C, D},
563  	       foo_3 = C + 1,
564  	       foo_2 = D + 1}.
565
566-record(nrec0, {name = <<"nested0">>}).
567-record(nrec1, {name = <<"nested1">>, nrec0=#nrec0{}}).
568-record(nrec2, {name = <<"nested2">>, nrec1=#nrec1{}}).
569
570nested_access(Config) when is_list(Config) ->
571    N0 = #nrec0{},
572    N1 = #nrec1{},
573    N2 = #nrec2{},
574    <<"nested0">> = N0#nrec0.name,
575    <<"nested1">> = N1#nrec1.name,
576    <<"nested2">> = N2#nrec2.name,
577    <<"nested0">> = N1#nrec1.nrec0#nrec0.name,
578    <<"nested0">> = N2#nrec2.nrec1#nrec1.nrec0#nrec0.name,
579    <<"nested1">> = N2#nrec2.nrec1#nrec1.name,
580    <<"nested0">> = ((N2#nrec2.nrec1)#nrec1.nrec0)#nrec0.name,
581
582    N1a = N2#nrec2.nrec1#nrec1{name = <<"nested1a">>},
583    <<"nested1a">> = N1a#nrec1.name,
584
585    N2a = N2#nrec2.nrec1#nrec1.nrec0#nrec0{name = <<"nested0a">>},
586    N2b = ((N2#nrec2.nrec1)#nrec1.nrec0)#nrec0{name = <<"nested0a">>},
587    <<"nested0a">> = N2a#nrec0.name,
588    N2a = N2b,
589    ok.
590
591-record(rr, {a,b,c}).
592-record(fileheader, {read_md5,md5,eof,trailer}).
593
594coverage(Config) when is_list(Config) ->
595    %% There should only remain one record test in the code below.
596    R0 = id(#rr{a=1,b=2,c=3}),
597    B = R0#rr.b,				%Test the record here.
598    R = R0#rr{c=42},				%No need to test here.
599    if
600	B > R#rr.a ->				%No need to test here.
601	    ok
602    end,
603    #rr{a=1,b=2,c=42} = id(R),			%Test for correctness.
604
605    %% Cover beam_ssa_opt:ssa_opt_element/1 and friends.
606    error1 = check_file_header(#fileheader{read_md5=1,md5=2}),
607    error2 = check_file_header(#fileheader{trailer=true,eof=false}),
608    error3 = check_file_header(#fileheader{}),
609
610    %% Cover sanitization of is_tagged_tuple in beam_ssa_pre_codegen.
611    {'EXIT',_} = catch id((catch 22)#rr.a),
612
613    %% Cover beam_ssa_bool.
614    error = coverage_1(true),
615
616    ok.
617
618check_file_header(FH) ->
619    if
620        FH#fileheader.read_md5 =/= FH#fileheader.md5 ->
621            error1;
622        FH#fileheader.trailer =/= FH#fileheader.eof ->
623            error2;
624        true ->
625            error3
626    end.
627
628coverage_1(V) when false#rr.b; V, "abc" ->
629    ok;
630coverage_1(_) ->
631    error.
632
633-record(default_fun, {a = fun(X) -> X*X end}).
634
635%% compiler treats records with 1 and 2 fields differently...
636-record(gb_nil, {}).
637-record(gb_foo, {hello=1}).
638-record(gb_bar, {hello=2,there=3}).
639-record(gb_rh, {mod,mid}).
640
641%% Taken from compilation_SUITE and other places.
642grab_bag(_Config) ->
643    T1 = fun() ->
644		 X = #foo{},
645		 Y = #foo{},
646		 {X#foo.a == Y#foo.a,X#foo.b}
647	 end,
648    {true,undefined} = T1(),
649
650    T2 = fun(X, Y) ->
651		 first_arg(X#foo.a =/= Y#foo.a, X#foo.b =/= X#foo.b)
652	 end,
653    true = T2(#foo{a=x,b=z}, #foo{a=y,b=z}),
654
655    T3 = fun() ->
656		 #default_fun{a=Fun} = id(#default_fun{}),
657		 9 = Fun(3)
658	 end,
659    T3(),
660
661    %% Stupid code, but the compiler used to crash.
662    T4 = fun() ->
663		 F0 = fun() ->
664			      R1 = #gb_nil{},
665			      R2 = R1#gb_nil{},
666			      R1 = R2
667		      end,
668		 F1 = fun() ->
669			      R1 = #gb_foo{},
670			      R2 = R1#gb_foo{},
671			      R1 = R2
672		      end,
673
674		 F2 = fun() ->
675			      R1 = #gb_bar{},
676			      R2 = R1#gb_bar{},
677			      R1 = R2
678		      end,
679		 F0(),
680		 F1(),
681		 F2()
682	 end,
683    T4(),
684
685    %% Used to crash beam_ssa_bool during its development.
686    T5 = fun(RH) ->
687                 if
688                     is_record(RH, gb_rh) andalso
689                     is_atom(RH#gb_rh.mod) andalso
690                     RH#gb_rh.mid /= 42 -> ok;
691                     true -> error
692                 end
693         end,
694    ok = T5(#gb_rh{}),
695    ok = T5(#gb_rh{mod=atom,mid=0}),
696    error = T5(#gb_rh{mod=100,mid=0}),
697    error = T5(#gb_rh{mod=atom,mid=42}),
698    error = T5(#gb_nil{}),
699    error = T5(#gb_bar{}),
700    error = T5(atom),
701
702    ok.
703
704%% ERIERL-436; the following code used to be very slow to compile.
705%%
706%% #slow_r{} should have about 4x as many fields for the test to be effective
707%% (all of them matched in slow_compilation/1), but unfortunately the memory
708%% use scales together with the speed so we'll run out of memory on many of
709%% our test machines before we reach noticeable levels (2+ minutes before the
710%% fix).
711%%
712%% We've therefore scaled it down to the current level, at least it it'll guard
713%% against excessive regressions.
714
715-record(slow_r,
716        {f0,  f1, f2, f3, f4, f5, f6, f7, f8, f9,
717         f10,f11,f12,f13,f14,f15,f16,f17,f18,f19,
718         f20,f21,f22,f23,f24,f25,f26,f27,f28,f29,
719         f30,f31,f32,f33,f34,f35,f36,f37,f38,f39,
720         f40,f41,f42,f43,f44,f45,f46,f47,f48,f49,
721         f50,f51,f52,f53,f54,f55,f56,f57,f58,f59}).
722
723slow_compilation(Config) when is_list(Config) ->
724    R = id(#slow_r{}),
725
726    [{f0,R#slow_r.f0},{f1,R#slow_r.f0},{f1,R#slow_r.f1},
727     {f2,R#slow_r.f2},{f3,R#slow_r.f3},{f4,R#slow_r.f4},
728     {f5,R#slow_r.f5},{f6,R#slow_r.f6},{f7,R#slow_r.f7},
729     {f8,R#slow_r.f8},{f9,R#slow_r.f9},{f10,R#slow_r.f10},
730     {f11,R#slow_r.f11},{f12,R#slow_r.f12},{f13,R#slow_r.f13},
731     {f14,R#slow_r.f14},{f15,R#slow_r.f15},{f16,R#slow_r.f16},
732     {f17,R#slow_r.f17},{f18,R#slow_r.f18},{f19,R#slow_r.f19},
733     {f20,R#slow_r.f20},{f21,R#slow_r.f21},{f22,R#slow_r.f22},
734     {f23,R#slow_r.f23},{f24,R#slow_r.f24},{f25,R#slow_r.f25},
735     {f26,R#slow_r.f26},{f27,R#slow_r.f27},{f28,R#slow_r.f28},
736     {f29,R#slow_r.f29},{f30,R#slow_r.f30},{f31,R#slow_r.f31},
737     {f32,R#slow_r.f32},{f33,R#slow_r.f33},{f34,R#slow_r.f34},
738     {f35,R#slow_r.f35},{f36,R#slow_r.f36},{f37,R#slow_r.f37},
739     {f38,R#slow_r.f38},{f39,R#slow_r.f39},{f40,R#slow_r.f40},
740     {f41,R#slow_r.f41},{f42,R#slow_r.f42},{f43,R#slow_r.f43},
741     {f44,R#slow_r.f44},{f45,R#slow_r.f45},{f46,R#slow_r.f46},
742     {f47,R#slow_r.f47},{f48,R#slow_r.f48},{f49,R#slow_r.f49},
743     {f40,R#slow_r.f50},{f51,R#slow_r.f51},{f52,R#slow_r.f52},
744     {f53,R#slow_r.f53},{f54,R#slow_r.f54},{f55,R#slow_r.f55},
745     {f56,R#slow_r.f56},{f57,R#slow_r.f57},{f58,R#slow_r.f58},
746     {f59,R#slow_r.f59}].
747
748first_arg(First, _) -> First.
749
750id(I) -> I.
751