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-module(beam_validator_SUITE).
21
22-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
23	 init_per_group/2,end_per_group/2,
24	 init_per_testcase/2,end_per_testcase/2,
25	 compiler_bug/1,stupid_but_valid/1,
26	 xrange/1,yrange/1,stack/1,call_last/1,merge_undefined/1,
27	 uninit/1,unsafe_catch/1,
28	 dead_code/1,
29	 overwrite_catchtag/1,overwrite_trytag/1,accessing_tags/1,bad_catch_try/1,
30	 cons_guard/1,
31	 freg_range/1,freg_uninit/1,
32	 bad_bin_match/1,bad_dsetel/1,
33	 state_after_fault_in_catch/1,no_exception_in_catch/1,
34	 undef_label/1,illegal_instruction/1,failing_gc_guard_bif/1,
35	 map_field_lists/1,cover_bin_opt/1,
36	 val_dsetel/1,bad_tuples/1,bad_try_catch_nesting/1,
37         receive_stacked/1,aliased_types/1,type_conflict/1,
38         infer_on_eq/1,infer_dead_value/1,infer_on_ne/1,
39         branch_to_try_handler/1,call_without_stack/1,
40         receive_marker/1,safe_instructions/1,
41         missing_return_type/1,will_bif_succeed/1,
42         bs_saved_position_units/1,parent_container/1]).
43
44-include_lib("common_test/include/ct.hrl").
45
46init_per_testcase(Case, Config) when is_atom(Case), is_list(Config) ->
47    Config.
48
49end_per_testcase(Case, Config) when is_atom(Case), is_list(Config) ->
50    ok.
51
52suite() ->
53    [{ct_hooks,[ts_install_cth]},
54     {timetrap,{minutes,10}}].
55
56all() ->
57    [{group,p}].
58
59groups() ->
60    [{p,test_lib:parallel(),
61      [compiler_bug,stupid_but_valid,xrange,
62       yrange,stack,call_last,merge_undefined,uninit,
63       unsafe_catch,dead_code,
64       overwrite_catchtag,overwrite_trytag,accessing_tags,
65       bad_catch_try,cons_guard,freg_range,freg_uninit,
66       bad_bin_match,bad_dsetel,
67       state_after_fault_in_catch,no_exception_in_catch,
68       undef_label,illegal_instruction,failing_gc_guard_bif,
69       map_field_lists,cover_bin_opt,val_dsetel,
70       bad_tuples,bad_try_catch_nesting,
71       receive_stacked,aliased_types,type_conflict,
72       infer_on_eq,infer_dead_value,infer_on_ne,
73       branch_to_try_handler,call_without_stack,
74       receive_marker,safe_instructions,
75       missing_return_type,will_bif_succeed,
76       bs_saved_position_units,parent_container]}].
77
78init_per_suite(Config) ->
79    test_lib:recompile(?MODULE),
80    Config.
81
82end_per_suite(_Config) ->
83    ok.
84
85init_per_group(_GroupName, Config) ->
86	Config.
87
88end_per_group(_GroupName, Config) ->
89	Config.
90
91compiler_bug(Config) when is_list(Config) ->
92    %% Check that the compiler returns an error if we try to
93    %% assemble one of the bad '.S' files.
94    Data = proplists:get_value(data_dir, Config),
95    File = filename:join(Data, "compiler_bug"),
96    error = compile:file(File, [from_asm,report_errors,time]),
97
98    %% Make sure that the error was reported by
99    %% the beam_validator module.
100    {error,
101     [{"compiler_bug",
102       [{_Pos,beam_validator,_}]}],
103     []} = compile:file(File, [from_asm,return_errors,time]),
104    ok.
105
106%% The following code is stupid but it should compile.
107stupid_but_valid(Config) when is_list(Config) ->
108    AnAtom = nisse,
109    try setelement(5, setelement(6, AnAtom, value), another_value) of
110	Term -> ct:fail({what_happened,Term})
111    catch
112	error:badarg -> ok
113    end,
114    ok.
115
116xrange(Config) when is_list(Config) ->
117    Errors = do_val(xrange, Config),
118    [{{t,sum_1,2},
119      {{bif,'+',{f,0},[{x,-1},{x,1}],{x,0}},4,
120       {bad_register,{x,-1}}}},
121     {{t,sum_2,2},
122      {{bif,'+',{f,0},[{x,0},{x,1023}],{x,0}},4,limit}},
123     {{t,sum_3,2},
124      {{bif,'+',{f,0},[{x,0},{x,1}],{x,-1}},4,
125       {bad_register,{x,-1}}}},
126     {{t,sum_4,2},
127      {{bif,'+',{f,0},[{x,0},{x,1}],{x,1023}},4,limit}}] = Errors,
128    ok.
129
130yrange(Config) when is_list(Config) ->
131    Errors = do_val(yrange, Config),
132    [{{t,sum_1,2},
133      {{move,{x,1},{y,-1}},5,
134       {bad_register,{y,-1}}}},
135     {{t,sum_2,2},
136      {{bif,'+',{f,0},[{x,0},{y,1024}],{x,0}},7,
137       limit}},
138     {{t,sum_3,2},
139      {{move,{x,1},{y,1024}},5,limit}},
140     {{t,sum_4,2},
141      {{move,{x,1},{y,-1}},5,
142       {bad_register,{y,-1}}}}] = Errors,
143    ok.
144
145stack(Config) when is_list(Config) ->
146    Errors = do_val(stack, Config),
147    [{{t,a,2},{return,9,{stack_frame,2}}},
148     {{t,b,2},{{deallocate,2},4,{allocated,none}}},
149     {{t,bad_1,0},{{allocate_zero,2,10},4,{{x,9},not_live}}},
150     {{t,bad_2,0},{{move,{y,0},{x,0}},5,{unassigned,{y,0}}}},
151     {{t,c,2},{{deallocate,2},10,{allocated,none}}},
152     {{t,d,2},
153      {{allocate,2,2},5,{existing_stack_frame,{size,2}}}},
154     {{t,e,2},{{deallocate,5},6,{allocated,2}}}] = Errors,
155    ok.
156
157call_last(Config) when is_list(Config) ->
158    Errors = do_val(call_last, Config),
159    [{{t,a,1},
160      {{call_last,1,{f,8},2},9,{allocated,1}}},
161     {{t,b,1},
162      {{call_ext_last,2,{extfunc,lists,seq,2},2},10,{allocated,1}}},
163     {{t,baz,2},
164      {{call_ext_only,2,{extfunc,erlang,put,2}},5,{allocated,0}}},
165     {{t,biz,2},
166      {{call_only,2,{f,10}},5,{allocated,0}}}] = Errors,
167    ok.
168
169call_without_stack(Config) when is_list(Config) ->
170    Errors = do_val(call_without_stack, Config),
171    [{{t,local,2},
172        {{call,2,{f,2}},4,{allocated,none}}},
173     {{t,remote,2},
174        {{call_ext,2,{extfunc,lists,seq,2}},4,{allocated,none}}}] = Errors,
175    ok.
176
177merge_undefined(Config) when is_list(Config) ->
178    Errors = do_val(merge_undefined, Config),
179    [{{t,undecided,2},
180      {{call_ext,2,{extfunc,debug,filter,2}},
181       22,
182       {allocated,undecided}}},
183     {{t,uninitialized,2},
184      {{call_ext,2,{extfunc,io,format,2}},
185       17,
186       {uninitialized_reg,{y,1}}}}] = Errors,
187    ok.
188
189uninit(Config) when is_list(Config) ->
190    Errors = do_val(uninit, Config),
191    [{{t,sum_1,2},
192      {{move,{y,0},{x,0}},5,{uninitialized_reg,{y,0}}}},
193     {{t,sum_2,2},
194      {{call,1,{f,8}},5,{uninitialized_reg,{y,0}}}},
195     {{t,sum_3,2},
196      {{bif,'+',{f,0},[{x,0},{y,0}],{x,0}},
197       6,
198       {unassigned,{y,0}}}}] = Errors,
199    ok.
200
201unsafe_catch(Config) when is_list(Config) ->
202    Errors = do_val(unsafe_catch, Config),
203    [{{t,small,2},
204      {{bs_put_integer,{f,0},{integer,16},1,
205        {field_flags,[unsigned,big]},{y,0}},
206       20,
207       {unassigned,{y,0}}}}] = Errors,
208    ok.
209
210dead_code(Config) when is_list(Config) ->
211    [] = do_val(dead_code, Config),
212    ok.
213
214overwrite_catchtag(Config) when is_list(Config) ->
215    Errors = do_val(overwrite_catchtag, Config),
216    [{{overwrite_catchtag,foo,1},
217      {{move,{x,0},{y,0}},6,{catchtag,_}}}] = Errors,
218    ok.
219
220overwrite_trytag(Config) when is_list(Config) ->
221    Errors = do_val(overwrite_trytag, Config),
222    [{{overwrite_trytag,foo,1},
223      {{kill,{y,2}},8,{trytag,_}}}] = Errors,
224    ok.
225
226accessing_tags(Config) when is_list(Config) ->
227    Errors = do_val(accessing_tags, Config),
228    [{{accessing_tags,bar,1},
229      {{move,{y,0},{x,0}},6,{trytag,_}}},
230     {{accessing_tags,foo,1},
231      {{move,{y,0},{x,0}},6,{catchtag,_}}}] = Errors,
232    ok.
233
234bad_catch_try(Config) when is_list(Config) ->
235    Errors = do_val(bad_catch_try, Config),
236    [{{bad_catch_try,bad_1,1},
237      {{'catch',{x,0},{f,3}},
238       5,{invalid_tag_register,{x,0}}}},
239     {{bad_catch_try,bad_2,1},
240      {{catch_end,{x,9}},
241       8,{invalid_tag_register,{x,9}}}},
242     {{bad_catch_try,bad_3,1},
243      {{catch_end,{y,1}},9,{invalid_tag,{y,1},{t_atom,[kalle]}}}},
244     {{bad_catch_try,bad_4,1},
245      {{'try',{x,0},{f,15}},5,{invalid_tag_register,{x,0}}}},
246     {{bad_catch_try,bad_5,1},
247      {{try_case,{y,1}},12,{invalid_tag,{y,1},any}}},
248     {{bad_catch_try,bad_6,1},
249      {{move,{integer,1},{y,1}},7,
250       {invalid_store,{y,1}}}}] = Errors,
251    ok.
252
253cons_guard(Config) when is_list(Config) ->
254    Errors = do_val(cons, Config),
255    [{{cons,foo,1},
256      {{get_list,{x,0},{x,1},{x,2}},
257       5,
258       {bad_type,{needed,{t_cons,any,any}},{actual,any}}}}] = Errors,
259    ok.
260
261freg_range(Config) when is_list(Config) ->
262    Errors = do_val(freg_range, Config),
263    [{{t,sum_1,2},
264      {{bif,fadd,{f,0},[{fr,-1},{fr,1}],{fr,0}},
265       4,
266       {bad_source,{fr,-1}}}},
267     {{t,sum_2,2},
268      {{bif,fadd,{f,0},[{fr,0},{fr,1024}],{fr,0}},
269       5,
270       {uninitialized_reg,{fr,1024}}}},
271     {{t,sum_3,2},
272      {{bif,fadd,{f,0},[{fr,0},{fr,1}],{fr,-1}},
273       6,
274       {bad_register,{fr,-1}}}},
275     {{t,sum_4,2},
276      {{bif,fadd,{f,0},[{fr,0},{fr,1}],{fr,1024}},
277       6,
278       limit}}] = Errors,
279    ok.
280
281freg_uninit(Config) when is_list(Config) ->
282    Errors = do_val(freg_uninit, Config),
283    [{{t,sum_1,2},
284      {{bif,fadd,{f,0},[{fr,0},{fr,1}],{fr,0}},
285       5,
286       {uninitialized_reg,{fr,1}}}},
287     {{t,sum_2,2},
288      {{bif,fadd,{f,0},[{fr,0},{fr,1}],{fr,0}},
289       8,
290       {uninitialized_reg,{fr,0}}}}] = Errors,
291    ok.
292
293bad_bin_match(Config) when is_list(Config) ->
294    [{{t,t,1},{return,5,{match_context,{x,0}}}}] =
295	do_val(bad_bin_match, Config),
296    ok.
297
298bad_dsetel(Config) when is_list(Config) ->
299    Errors = do_val(bad_dsetel, Config),
300    [{{t,t,1},
301      {{set_tuple_element,{x,1},{x,0},1},
302       17,
303       illegal_context_for_set_tuple_element}}] = Errors,
304    ok.
305
306state_after_fault_in_catch(Config) when is_list(Config) ->
307    Errors = do_val(state_after_fault_in_catch, Config),
308    [{{state_after_fault_in_catch,badmatch,1},
309      {{move,{x,1},{x,0}},9,{uninitialized_reg,{x,1}}}},
310     {{state_after_fault_in_catch,case_end,1},
311      {{move,{x,1},{x,0}},9,{uninitialized_reg,{x,1}}}},
312     {{state_after_fault_in_catch,if_end,1},
313      {{move,{x,1},{x,0}},9,{uninitialized_reg,{x,1}}}},
314     {{t,foo,1},
315      {{move,{x,1},{x,0}},10,{uninitialized_reg,{x,1}}}}] = Errors,
316    ok.
317
318no_exception_in_catch(Config) when is_list(Config) ->
319    Errors = do_val(no_exception_in_catch, Config),
320    [{{no_exception_in_catch,nested_of_1,4},
321      {{try_case_end,{x,0}},180,ambiguous_catch_try_state}}] = Errors,
322    ok.
323
324undef_label(Config) when is_list(Config) ->
325    M = {undef_label,
326	 [{t,1}],
327	 [],
328	 [{function,t,1,2,
329	   [{label,1},
330	    {func_info,{atom,undef_label},{atom,t},1},
331	    {label,2},
332	    {test,is_eq_exact,{f,42},[{x,0},{atom,x}]},
333	    {move,{atom,ok},{x,0}},
334	    return]},
335	  {function,x,1,17,
336	   [{label,3},
337	    {func_info,{atom,undef_label},{atom,x},1},
338	    {label,4},
339	    return]}],
340	 5},
341    Errors = beam_val(M),
342    [{{undef_label,t,1},{undef_labels,[42]}},
343     {{undef_label,x,1},no_entry_label}] = Errors,
344    ok.
345
346illegal_instruction(Config) when is_list(Config) ->
347    M = {illegal_instruction,
348	 [{t,1},{x,1},{y,0}],
349	 [],
350	 [{function,t,1,2,
351	   [{label,1},
352	    {func_info,{atom,illegal_instruction},{atom,t},1},
353	    {label,2},
354	    {my_illegal_instruction,{x,0}},
355	    return]},
356	  {function,x,1,4,
357	   [{label,3},
358	    bad_func_info,
359	    {label,4},
360	    {my_illegal_instruction,{x,0}},
361	    return]},
362	  {function,y,0,17,[]}],
363	 5},
364    Errors = beam_val(M),
365    [{{illegal_instruction,t,1},
366      {{my_illegal_instruction,{x,0}},4,unknown_instruction}},
367     {{illegal_instruction,x,1},invalid_function_header},
368     {{illegal_instruction,y,0},invalid_function_header}] = Errors,
369    ok.
370
371%% The beam_validator used to assume that a GC guard BIF could
372%% do a garbage collection even if it failed. That assumption
373%% is not correct, and will cause the beam_validator to reject
374%% valid programs such as this test case.
375%%
376%% (Thanks to Kiran Khaladkar.)
377%%
378failing_gc_guard_bif(Config) when is_list(Config) ->
379    ok = process_request(lists:seq(1, 36)),
380    error = process_request([]),
381    error = process_request(not_a_list),
382    ok.
383
384process_request(ConfId) ->
385    case process_request_foo(ConfId) of
386	false ->
387	    if
388		length(ConfId) == 36 ->
389		    Response = ok;
390		true ->
391		    Response = error
392	    end
393    end,
394    process_request_bar(self(), [Response]).
395
396process_request_foo(_) ->
397    false.
398
399process_request_bar(Pid, [Response]) when is_pid(Pid) ->
400    Response.
401
402map_field_lists(Config) ->
403    Errors = do_val(map_field_lists, Config),
404    [{{map_field_lists,x,1},
405      {{test,has_map_fields,{f,1},{x,0},{list,[{atom,a},{atom,a}]}},
406       6,
407       keys_not_unique}},
408     {{map_field_lists,y,1},
409      {{test,has_map_fields,{f,3},{x,0},{list,[]}},
410       6,
411       empty_field_list}}
412    ] = Errors.
413
414%% Coverage and smoke test of beam_validator.
415cover_bin_opt(_Config) ->
416    Ms = [beam_utils_SUITE,
417	  bs_match_SUITE,
418	  bs_bincomp_SUITE,
419	  bs_bit_binaries_SUITE,
420	  bs_utf_SUITE],
421    test_lib:p_run(fun try_bin_opt/1, Ms),
422    ok.
423
424try_bin_opt(Mod) ->
425    try
426	do_bin_opt(Mod)
427    catch
428	Class:Error:Stk ->
429	    io:format("~p: ~p ~p\n~p\n",
430		      [Mod,Class,Error,Stk]),
431	    error
432    end.
433
434do_bin_opt(Mod) ->
435    Beam = code:which(Mod),
436    {ok,{Mod,[{abstract_code,
437	       {raw_abstract_v1,Abstr}}]}} =
438	beam_lib:chunks(Beam, [abstract_code]),
439    {ok,Mod,Asm} = compile:forms(Abstr, ['S']),
440    do_bin_opt(Mod, Asm).
441
442do_bin_opt(Mod, Asm) ->
443    do_bin_opt(fun enable_bin_opt/1, Mod, Asm),
444    do_bin_opt(fun remove_bs_start_match/1, Mod, Asm),
445    do_bin_opt(fun remove_bs_save/1, Mod, Asm),
446    do_bin_opt(fun destroy_ctxt/1, Mod, Asm),
447    do_bin_opt(fun destroy_save_point/1, Mod, Asm),
448    ok.
449
450do_bin_opt(Transform, Mod, Asm0) ->
451    Asm = Transform(Asm0),
452    case compile:forms(Asm, [from_asm,no_postopt,return]) of
453	{ok,Mod,Code,_Warnings} when is_binary(Code) ->
454	    ok;
455	{error,Errors0,_} ->
456	    %% beam_validator must return errors, not simply crash,
457	    %% when illegal code is found.
458	    ModString = atom_to_list(Mod),
459	    [{ModString,Errors}] = Errors0,
460	    _ = [verify_bin_opt_error(E) || E <- Errors],
461	    ok
462    end.
463
464verify_bin_opt_error({beam_validator,_}) ->
465    ok.
466
467enable_bin_opt(Module) ->
468    transform_is(fun enable_bin_opt_body/1, Module).
469
470enable_bin_opt_body([_,{'%',{no_bin_opt,_Reason,_Anno}}|Is]) ->
471    enable_bin_opt_body(Is);
472enable_bin_opt_body([I|Is]) ->
473    [I|enable_bin_opt_body(Is)];
474enable_bin_opt_body([]) ->
475    [].
476
477remove_bs_start_match(Module) ->
478    transform_remove(fun({test,bs_start_match2,_,_,_,_}) -> true;
479			(_) -> false
480		     end, Module).
481
482remove_bs_save(Module) ->
483    transform_remove(fun({bs_save2,_,_}) -> true;
484			(_) -> false
485		     end, Module).
486
487destroy_save_point(Module) ->
488    transform_i(fun do_destroy_save_point/1, Module).
489
490do_destroy_save_point({I,Ctx,_Point})
491  when I =:= bs_save2; I =:= bs_restore2 ->
492    {I,Ctx,42};
493do_destroy_save_point(I) ->
494    I.
495
496destroy_ctxt(Module) ->
497    transform_i(fun do_destroy_ctxt/1, Module).
498
499do_destroy_ctxt({bs_save2=I,Ctx,Point}) ->
500    {I,destroy_reg(Ctx),Point};
501do_destroy_ctxt({bs_restore2=I,Ctx,Point}) ->
502    {I,destroy_reg(Ctx),Point};
503do_destroy_ctxt({bs_context_to_binary=I,Ctx}) ->
504    {I,destroy_reg(Ctx)};
505do_destroy_ctxt(I) ->
506    I.
507
508destroy_reg({Tag,N}) ->
509    case rand:uniform() of
510	R when R < 0.6 ->
511	    {Tag,N+1};
512	_ ->
513	    {y,N+1}
514    end.
515
516bad_tuples(Config) ->
517    Errors = do_val(bad_tuples, Config),
518    [{{bad_tuples,heap_overflow,1},
519      {{put,{x,0}},9,{heap_overflow,{left,0},{wanted,1}}}},
520     {{bad_tuples,long,2},
521      {{put,{atom,too_long}},9,not_building_a_tuple}},
522     {{bad_tuples,self_referential,1},
523      {{put,{x,1}},8,{unfinished_tuple,{x,1}}}},
524     {{bad_tuples,short,1},
525      {{move,{x,1},{x,0}},8,{unfinished_tuple,{x,1}}}}] = Errors,
526
527    ok.
528
529bad_try_catch_nesting(Config) ->
530    Errors = do_val(bad_try_catch_nesting, Config),
531    [{{bad_try_catch_nesting,main,2},
532      {{'try',{y,2},{f,3}},
533       8,
534       {bad_try_catch_nesting,{y,2},[{{y,1},{trytag,[5]}}]}}}] = Errors,
535    ok.
536
537receive_stacked(Config) ->
538    Mod = ?FUNCTION_NAME,
539    Errors = do_val(Mod, Config),
540    [{{receive_stacked,f1,0},
541      {{loop_rec_end,{f,3}},
542       18,
543       {fragile_message_reference,{y,_}}}},
544     {{receive_stacked,f2,0},
545      {{test_heap,3,0},11,{fragile_message_reference,{y,_}}}},
546     {{receive_stacked,f3,0},
547      {{test_heap,3,0},11,{fragile_message_reference,{y,_}}}},
548     {{receive_stacked,f4,0},
549      {{test_heap,3,0},11,{fragile_message_reference,{y,_}}}},
550     {{receive_stacked,f5,0},
551      {{loop_rec_end,{f,23}},
552       24,
553       {fragile_message_reference,{y,_}}}},
554     {{receive_stacked,f6,0},
555      {{gc_bif,byte_size,{f,29},0,[{y,_}],{x,0}},
556       13,
557       {fragile_message_reference,{y,_}}}},
558     {{receive_stacked,f7,0},
559      {{loop_rec_end,{f,33}},
560       21,
561       {fragile_message_reference,{y,_}}}},
562     {{receive_stacked,f8,0},
563      {{loop_rec_end,{f,38}},
564       21,
565       {fragile_message_reference,{y,_}}}},
566     {{receive_stacked,m1,0},
567      {{loop_rec_end,{f,43}},
568       20,
569       {fragile_message_reference,{y,_}}}},
570     {{receive_stacked,m2,0},
571      {{loop_rec_end,{f,48}},
572       34,
573       {fragile_message_reference,{y,_}}}}] = Errors,
574
575    %% Compile the original source code as a smoke test.
576    Data = proplists:get_value(data_dir, Config),
577    Base = atom_to_list(Mod),
578    File = filename:join(Data, Base),
579    {ok,Mod,_} = compile:file(File, [binary]),
580
581    ok.
582
583aliased_types(Config) ->
584    Seq = lists:seq(1, 5),
585    1 = aliased_types_1(Seq, Config),
586
587    {1,1} = aliased_types_2(Seq),
588    {42,none} = aliased_types_2([]),
589
590    gurka = aliased_types_3([gurka]),
591    gaffel = aliased_types_3([gaffel]),
592
593    ok.
594
595%% ERL-735: validator failed to track types on aliased registers, rejecting
596%% legitimate optimizations.
597%%
598%%    move x0 y0
599%%    bif hd L1 x0
600%%    get_hd y0     %% The validator failed to see that y0 was a list
601%%
602aliased_types_1(Bug, Config) ->
603    if
604        Config =/= [gurka, gaffel] -> %% Pointless branch.
605            _ = hd(Bug),
606            lists:seq(1, 5),
607            hd(Bug)
608    end.
609
610%% ERL-832: validator failed to realize that a Y register was a cons.
611aliased_types_2(Bug) ->
612    Res = case Bug of
613              [] -> id(42);
614              _ -> hd(Bug)
615          end,
616    {Res,case Bug of
617             [] -> none;
618             _ -> hd(Bug)
619         end}.
620
621%% ERL-832 part deux; validator failed to realize that an aliased register was
622%% a cons.
623aliased_types_3(Bug) ->
624    List = [Y || Y <- Bug],
625    case List of
626        [] -> Bug;
627        _ ->
628            if
629                hd(List) -> a:a();
630                true -> ok
631            end,
632            hd(List)
633    end.
634
635
636%% ERL-867; validation proceeded after a type conflict, causing incorrect types
637%% to be joined.
638
639-record(r, { e1 = e1, e2 = e2 }).
640
641type_conflict(Config) when is_list(Config) ->
642    {e1, e2} = type_conflict_1(#r{}),
643    ok.
644
645type_conflict_1(C) ->
646    Src = id(C#r.e2),
647    TRes = try id(Src) of
648               R -> R
649           catch
650               %% C:R can never match, yet it assumed that the type of 'C' was
651               %% an atom from here on.
652               C:R -> R
653           end,
654    {C#r.e1, TRes}.
655
656%% ERL-886; validation failed to infer types on both sides of '=:='
657
658infer_on_eq(Config) when is_list(Config) ->
659    {ok, gurka} = infer_on_eq_1(id({gurka})),
660    {ok, gaffel} = infer_on_eq_2(id({gaffel})),
661    {ok, elefant} = infer_on_eq_3(id({elefant})),
662    {ok, myra} = infer_on_eq_4(id({myra})),
663    ok.
664
665infer_on_eq_1(T) ->
666    1 = erlang:tuple_size(T),
667    {ok, erlang:element(1, T)}.
668
669infer_on_eq_2(T) ->
670    Size = erlang:tuple_size(T),
671    Size = 1,
672    {ok, erlang:element(1, T)}.
673
674infer_on_eq_3(T) ->
675    true = 1 =:= erlang:tuple_size(T),
676    {ok, erlang:element(1, T)}.
677
678infer_on_eq_4(T) ->
679    true = erlang:tuple_size(T) =:= 1,
680    {ok, erlang:element(1, T)}.
681
682%% ERIERL-348; types were inferred for dead values, causing validation to fail.
683
684-record(idv, {key}).
685
686infer_dead_value(Config) when is_list(Config) ->
687    a = idv_1({a, b, c, d, e, f, g}, {0, 0, 0, 0, 0, 0, 0}),
688    b = idv_1({a, b, c, d, 0, 0, 0}, {a, b, c, d, 0, 0, 0}),
689    c = idv_1({0, 0, 0, 0, 0, f, g}, {0, 0, 0, 0, 0, f, g}),
690    error = idv_1(gurka, gaffel),
691
692    ok = idv_2(id(#idv{})),
693
694    ok.
695
696idv_1({_A, _B, _C, _D, _E, _F, _G},
697      {0, 0, 0, 0, 0, 0, 0}) ->
698    a;
699idv_1({A, B, C, D,_E, _F, _G}=_Tuple1,
700      {A, B, C, D, 0, 0, 0}=_Tuple2) ->
701    b;
702idv_1({_A, _B, _C, _D, _E, F, G},
703      {0, 0, 0, 0, 0, F, G}) ->
704    c;
705idv_1(_A, _B) ->
706    error.
707
708%% ERL-998; type inference for select_val (#b_switch{}) was more clever than
709%% that for is_ne_exact (#b_br{}), sometimes failing validation when the type
710%% optimization pass acted on the former and the validator got the latter.
711
712-record(ion, {state}).
713
714infer_on_ne(Config) when is_list(Config) ->
715    #ion{state = closing} = ion_1(#ion{ state = id(open) }),
716    #ion{state = closing} = ion_close(#ion{ state = open }),
717    ok.
718
719ion_1(State = #ion{state = open}) -> ion_2(State);
720ion_1(State = #ion{state = closing}) -> ion_2(State).
721
722ion_2(State = #ion{state = open}) -> ion_close(State);
723ion_2(#ion{state = closing}) -> ok.
724
725ion_close(State = #ion{}) -> State#ion{state = closing}.
726
727%% ERL-995: The first solution to ERIERL-348 was incomplete and caused
728%% validation to fail when living values depended on delayed type inference on
729%% "dead" values.
730
731idv_2(State) ->
732    Flag = (State#idv.key == undefined),
733    case id(gurka) of
734        {_} -> id([Flag]);
735        _ -> ok
736    end,
737    if
738        Flag -> idv_called_once(State);
739        true -> ok
740    end.
741
742idv_called_once(_State) -> ok.
743
744%% Direct jumps to try/catch handlers crash the emulator and must fail
745%% validation. This is provoked by OTP-15945.
746
747branch_to_try_handler(Config) ->
748    Errors = do_val(branch_to_try_handler, Config),
749    [{{branch_to_try_handler,main,1},
750      {{bif,tuple_size,{f,3},[{y,0}],{x,0}},
751       13,
752       {illegal_branch,try_handler,3}}}] = Errors,
753    ok.
754
755receive_marker(Config) when is_list(Config) ->
756    Errors = do_val(receive_marker, Config),
757
758    [{{receive_marker,t1,1},
759      {return,_,
760       {return_in_receive,entered_loop}}},
761     {{receive_marker,t2,1},
762      {{call_last,1,{f,2},1},_,
763       {return_in_receive,entered_loop}}},
764     {{receive_marker,t3,1},
765      {return,_,
766       {return_in_receive,entered_loop}}}] = Errors,
767
768    ok.
769
770%% ERL-1128: the validator erroneously thought that many non-throwing
771%% instructions like is_eq_exact could throw.
772safe_instructions(Config) when is_list(Config) ->
773    Errors = do_val(safe_instructions, Config),
774
775    [] = Errors,
776
777    ok.
778
779missing_return_type(Config) when is_list(Config) ->
780    %% ERL-1161: the validator didn't know that is_map_key always returns a
781    %% bool.
782    Map = #{ hello => there },
783    true = mrt_1(true),
784    false = mrt_1(false),
785    true = mrt_1(is_map_key(id(hello), Map)),
786    false = mrt_1(is_map_key(id(there), Map)),
787
788    ok.
789
790mrt_1(Bool) ->
791    true = is_boolean(Bool),
792    Bool.
793
794%% ERL-1340: the unit of previously saved match positions wasn't updated.
795bs_saved_position_units(Config) when is_list(Config) ->
796    M = {bs_saved_position_units,
797         [{no_errors,1},{some_errors,1}],
798         [],
799         [{function,ctx_test_8,1,2,
800              [{label,1},
801               {func_info,{atom,bs_saved_position_units},{atom,ctx_test_8},1},
802               {label,2},
803               {'%',
804                   {var_info,
805                       {x,0},
806                       [{type,{t_bs_context,8,0,0}},accepts_match_context]}},
807               {move,nil,{x,0}},
808               return]},
809          {function,no_errors,1,4,
810              [{label,3},
811               {func_info,{atom,bs_saved_position_units},{atom,no_errors},1},
812               {label,4},
813               {'%',{var_info,{x,0},[accepts_match_context]}},
814               {test,bs_start_match3,{f,3},1,[{x,0}],{x,1}},
815               {bs_get_position,{x,1},{x,0},2},
816               {test,bs_test_unit,{f,5},[{x,1},8]},
817               {bs_set_position,{x,1},{x,0}},
818               {test,bs_get_binary2,
819                   {f,5},
820                   2,
821                   [{x,1},{atom,all},1,{field_flags,[unsigned,big]}],
822                   {x,2}},
823               {bs_set_position,{x,1},{x,0}},
824               {bs_get_tail,{x,1},{x,0},3},
825               {test,is_eq_exact,{f,5},[{x,2},{x,0}]},
826               {move,{x,1},{x,0}},
827               %% Context unit should be 8 here.
828               {call_only,1,{f,2}},
829               {label,5},
830               {bs_get_tail,{x,1},{x,0},2},
831               {jump,{f,3}}]},
832          {function,some_errors,1,7,
833              [{label,6},
834               {func_info,{atom,bs_saved_position_units},{atom,some_errors},1},
835               {label,7},
836               {'%',{var_info,{x,0},[accepts_match_context]}},
837               {test,bs_start_match3,{f,6},1,[{x,0}],{x,1}},
838               {bs_get_position,{x,1},{x,0},2},
839               {test,bs_get_binary2,
840                   {f,8},
841                   2,
842                   [{x,1},{atom,all},4,{field_flags,[unsigned,big]}],
843                   {x,2}},
844               {bs_set_position,{x,1},{x,0}},
845               {test,bs_test_unit,{f,9},[{x,1},3]},
846               {bs_set_position,{x,1},{x,0}},
847               {bs_get_tail,{x,1},{x,0},3},
848               {test,is_eq_exact,{f,8},[{x,2},{x,0}]},
849               {move,{x,1},{x,0}},
850               %% Context unit should be 12 here, failing validation.
851               {call_only,1,{f,2}},
852               {label,8},
853               {bs_get_tail,{x,1},{x,0},2},
854               {jump,{f,6}},
855               {label,9},
856               %% Context unit should be 4 here.
857               {move,nil,{x,0}},
858               return]}],
859         10},
860
861    Errors = beam_val(M),
862
863    [{{bs_saved_position_units,some_errors,1},
864      {{call_only,1,{f,2}},
865       14,
866       {bad_arg_type,{x,0},
867                     {t_bs_context,12,0,0},
868                     {t_bs_context,8,0,0}}}}] = Errors,
869
870    ok.
871
872%%%-------------------------------------------------------------------------
873
874transform_remove(Remove, Module) ->
875    transform_is(fun(Is) -> [I || I <- Is, not Remove(I)] end, Module).
876
877transform_i(Transform, Module) ->
878    transform_is(fun(Is) -> [Transform(I) || I <- Is] end, Module).
879
880transform_is(Transform, {Mod,Exp,Imp,Fs0,Lc}) ->
881    Fs = [transform_is_1(Transform, F) || F <- Fs0],
882    {Mod,Exp,Imp,Fs,Lc}.
883
884transform_is_1(Transform, {function,N,A,E,Is0}) ->
885    Is = Transform(Is0),
886    {function,N,A,E,Is}.
887
888do_val(Mod, Config) ->
889    Data = proplists:get_value(data_dir, Config),
890    Base = atom_to_list(Mod),
891    File = filename:join(Data, Base),
892    case compile:file(File, [from_asm,no_postopt,return_errors]) of
893	{error,L,[]} ->
894	    [{Base,Errors0}] = L,
895	    Errors = [E || {_Pos,beam_validator,E} <- Errors0],
896	    _ = [io:put_chars(beam_validator:format_error(E)) ||
897		    E <- Errors],
898	    Errors;
899	{ok,Mod} ->
900	    []
901    end.
902
903beam_val(M) ->
904    Name = atom_to_list(element(1, M)),
905    {error,[{Name,Errors0}]} = beam_validator:validate(M, strong),
906    Errors = [E || {_Pos,beam_validator,E} <- Errors0],
907    _ = [io:put_chars(beam_validator:format_error(E)) ||
908	    E <- Errors],
909    Errors.
910
911%%%-------------------------------------------------------------------------
912
913val_dsetel(_Config) ->
914    self() ! 13,
915    {'EXIT',{{try_clause,participating},_}} = (catch night(0)),
916    ok.
917
918night(Turned) ->
919    receive
920	13 ->
921	    try participating of engine -> 16 after false end
922    end,
923    %% The setelement/3 call is unreachable.
924    Turned(setelement(#{true => Turned},
925		      participating(Turned, "suit", 40, []),
926		      Turned < Turned)),
927    ok.
928
929participating(_, _, _, _) -> ok.
930
931%% map_get was known as returning 'none', but 'will_succeed' still returned
932%% 'maybe' causing validation to continue, eventually exploding when the 'none'
933%% value was used.
934will_bif_succeed(_Config) ->
935    ok = f1(body).
936
937%% +no_ssa_opt
938f1(body) when map_get(girl, #{friend => node()}); [], community ->
939    case $q and $K of
940        _V0 ->
941            0.1825965401179273;
942        0 ->
943            state#{[] => 0.10577334580729858, $J => 0}
944    end;
945f1(body) ->
946    ok.
947
948%% ERL-1426: When a value was extracted from a tuple, subsequent type tests did
949%% not update the type of said tuple.
950
951-record(pc, {a}).
952
953parent_container(_Config) ->
954    ok = pc_1(id(#pc{a=true})).
955
956pc_1(#pc{a=A}=R) ->
957    case A of
958        true -> ok;
959        false -> ok
960    end,
961    ok = pc_2(R).
962
963pc_2(_R) ->
964    ok.
965
966id(I) ->
967    I.
968