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