1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2015-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_type_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	 integers/1,numbers/1,coverage/1,booleans/1,setelement/1,
25	 cons/1,tuple/1,record_float/1,binary_float/1,float_compare/1,
26	 arity_checks/1,elixir_binaries/1,find_best/1,
27         test_size/1,cover_lists_functions/1,list_append/1,bad_binary_unit/1,
28         none_argument/1,success_type_oscillation/1,type_subtraction/1,
29         container_subtraction/1]).
30
31%% Force id/1 to return 'any'.
32-export([id/1]).
33
34suite() -> [{ct_hooks,[ts_install_cth]}].
35
36all() ->
37    [{group,p}].
38
39groups() ->
40    [{p,[parallel],
41      [integers,
42       numbers,
43       coverage,
44       booleans,
45       setelement,
46       cons,
47       tuple,
48       record_float,
49       binary_float,
50       float_compare,
51       arity_checks,
52       elixir_binaries,
53       find_best,
54       test_size,
55       cover_lists_functions,
56       list_append,
57       bad_binary_unit,
58       none_argument,
59       success_type_oscillation,
60       type_subtraction,
61       container_subtraction
62      ]}].
63
64init_per_suite(Config) ->
65    test_lib:recompile(?MODULE),
66    Config.
67
68end_per_suite(_Config) ->
69    ok.
70
71init_per_group(_GroupName, Config) ->
72    Config.
73
74end_per_group(_GroupName, Config) ->
75    Config.
76
77integers(_Config) ->
78    a = do_integers_1(2#11000),
79    b = do_integers_1(2#11001),
80
81    a = do_integers_2(<<0:1>>),
82    {'EXIT',{{case_clause,-1},_}} = (catch do_integers_2(<<1:1>>)),
83
84    college = do_integers_3(),
85
86    zero = do_integers_4(<<0:1>>, 0),
87    one = do_integers_4(<<1:1>>, 0),
88    other = do_integers_4(<<1:1>>, 2),
89
90    zero = do_integers_5(0, 0),
91    one = do_integers_5(0, 1),
92    two = do_integers_5(0, 2),
93    three = do_integers_5(0, 3),
94
95    {'EXIT',{badarith,_}} = (catch do_integers_6()),
96
97    house = do_integers_7(),
98
99    {'EXIT',{badarith,_}} = (catch do_integers_8()),
100
101    ok.
102
103do_integers_1(B0) ->
104    B = B0 band 1,
105    case B band 15 of
106	0 -> a;
107	1 -> b
108    end.
109
110do_integers_2(Bin) ->
111    <<B:1/signed>> = Bin,
112    case B of
113	0 -> a;
114	1 -> b
115    end.
116
117do_integers_3() ->
118    case try 0 after [] end of
119	0 -> college;
120	1 -> 0
121    end.
122
123do_integers_4(<<X:1,T/bits>>, C) ->
124    %% Binary matching gives the range 0-1 for X.
125    %% The range for `X bor C` is unknown. It must not be inherited
126    %% from X. (`X bor C` will reuse the register used for X.)
127    case X bor C of
128        0 -> do_integers_4(T, C, zero);
129        1 -> do_integers_4(T, C, one);
130        _ -> do_integers_4(T, C, other)
131    end.
132
133do_integers_4(_, _, Res) ->
134    Res.
135
136do_integers_5(X0, Y0) ->
137    %% _X and Y will use the same register.
138    _X = X0 band 1,
139    Y = Y0 band 3,
140    case Y of
141        0 -> zero;
142        1 -> one;
143        2 -> two;
144        3 -> three
145    end.
146
147do_integers_6() ->
148    try b after 1 end band 0.
149
150do_integers_7() ->
151    try
152        0
153        band
154        try
155            0:any(),
156            ok
157        catch
158            bad_class:_:_ ->
159                {tag, "nt"}
160        end
161    catch
162        _:_:_ ->
163            house
164    end.
165
166do_integers_8() ->
167    -1 band ((0 div 0) band 0).
168
169numbers(_Config) ->
170    Int = id(42),
171    true = is_integer(Int),
172    true = is_number(Int),
173    false = is_float(Int),
174
175    Float = id(42.0),
176    true = is_float(Float),
177    true = is_number(Float),
178    false = is_integer(Float),
179
180    Number = id(1) + id(2),
181    true = is_number(Number),
182    true = is_integer(Number),
183    false = is_float(Number),
184
185    AnotherNumber = id(99.0) + id(1),
186    true = is_float(AnotherNumber),
187    true = is_number(AnotherNumber),
188    false = is_integer(AnotherNumber),
189
190    NotNumber = id(atom),
191    true = is_atom(NotNumber),
192    false = is_number(NotNumber),
193    false = is_integer(NotNumber),
194    false = is_float(NotNumber),
195
196    true = is_number(Int),
197    true = is_number(Float),
198    true = is_number(Number),
199    true = is_number(AnotherNumber),
200
201    %% Cover beam_ssa_type:join/2.
202
203    Join1 = case id(a) of
204                a -> 3 + id(7);                 %Number.
205                b -> id(5) / id(2)              %Float.
206            end,
207    true = is_integer(Join1),
208
209    Join2 = case id(a) of
210                a -> id(5) / 2;                 %Float.
211                b -> 3 + id(7)                  %Number.
212            end,
213    true = is_float(Join2),
214
215    %% Cover beam_ssa_type:meet/2.
216
217    Meet1 = id(0) + -10.0,                       %Float.
218    10.0 = abs(Meet1),                           %Number.
219
220    ok.
221
222coverage(Config) ->
223    {'EXIT',{badarith,_}} = (catch id(1) bsl 0.5),
224    {'EXIT',{badarith,_}} = (catch id(2.0) bsl 2),
225    {'EXIT',{badarith,_}} = (catch a + 0.5),
226    {'EXIT',{badarith,_}} = (catch 2.0 * b),
227
228    {'EXIT',{badarith,_}} = (catch id(42.0) / (1 bsl 2000)),
229
230    id(id(42) band 387439739874298734983787934283479243879),
231    id(-1 band id(13)),
232
233    error = if
234                is_map(Config), is_integer(Config) -> ok;
235                true -> error
236            end,
237    error = if
238                is_map(Config), is_atom(Config) -> ok;
239                true -> error
240            end,
241    error = if
242                is_map(Config), is_tuple(Config) -> ok;
243                true -> error
244            end,
245    error = if
246                is_integer(Config), is_bitstring(Config) -> ok;
247                true -> error
248            end,
249
250    ok = case Config of
251             <<_>> when is_binary(Config) ->
252                 impossible;
253             [_|_] ->
254                 ok
255         end,
256
257    %% Cover beam_type:verified_type(none).
258    {'EXIT',{badarith,_}} = (catch (id(2) / id(1)) band 16#ff),
259
260    false = fun lot:life/147 == #{},
261
262    ok.
263
264booleans(_Config) ->
265    {'EXIT',{{case_clause,_},_}} = (catch do_booleans_1(42)),
266
267    ok = do_booleans_2(42, 41),
268    error = do_booleans_2(42, 42),
269
270    AnyAtom = id(atom),
271    true = is_atom(AnyAtom),
272    false = is_boolean(AnyAtom),
273
274    MaybeBool = id(maybe),
275    case MaybeBool of
276        true -> ok;
277        maybe -> ok;
278        false -> ok
279    end,
280    false = is_boolean(MaybeBool),
281
282    NotBool = id(a),
283    case NotBool of
284        a -> ok;
285        b -> ok;
286        c -> ok
287    end,
288    false = is_boolean(NotBool),
289
290    ok.
291
292do_booleans_1(B) ->
293    case is_integer(B) of
294	yes -> yes;
295	no -> no
296    end.
297
298do_booleans_2(A, B) ->
299    Not = not do_booleans_cmp(A, B),
300    case Not of
301        true ->
302            case Not of
303                true -> error;
304                false -> ok
305            end;
306        false -> ok
307    end.
308
309do_booleans_cmp(A, B) -> A > B.
310
311setelement(_Config) ->
312    T0 = id({a,42}),
313    {a,_} = T0,
314    {b,_} = setelement(1, T0, b),
315    {z,b} = do_setelement_1(<<(id(1)):32>>, {a,b}, z),
316    {new,two} = do_setelement_2(<<(id(1)):1>>, {one,two}, new),
317    ok.
318
319do_setelement_1(<<N:32>>, Tuple, NewValue) ->
320    _ = element(N, Tuple),
321    %% While updating the type for Tuple, beam_ssa_type would do:
322    %%   maps:without(lists:seq(0, 4294967295), Elements)
323    setelement(N, Tuple, NewValue).
324
325do_setelement_2(<<N:1>>, Tuple, NewValue) ->
326    %% Cover the second clause in remove_element_info/2. The
327    %% type for the second element will be kept.
328    two = element(2, Tuple),
329    setelement(N, Tuple, NewValue).
330
331cons(_Config) ->
332    [did] = cons(assigned, did),
333
334    true = cons_is_empty_list([]),
335    false = cons_is_empty_list([a]),
336
337    false = cons_not(true),
338    true = cons_not(false),
339
340    {$a,"bc"} = cons_hdtl(true),
341    {$d,"ef"} = cons_hdtl(false),
342    ok.
343
344cons(assigned, Instrument) ->
345    [Instrument] = [did].
346
347cons_is_empty_list(L) ->
348    Cons = case L of
349               [] -> "true";
350               _ -> "false"
351           end,
352    id(1),
353    case Cons of
354        "true" -> true;
355        "false" -> false
356    end.
357
358cons_not(B) ->
359    Cons = case B of
360               true -> "true";
361               false -> "false"
362           end,
363    id(1),
364    case Cons of
365        "true" -> false;
366        "false" -> true
367    end.
368
369cons_hdtl(B) ->
370    Cons = case B of
371               true -> "abc";
372               false -> "def"
373           end,
374    id(1),
375    {id(hd(Cons)),id(tl(Cons))}.
376
377-record(bird, {a=a,b=id(42)}).
378
379tuple(_Config) ->
380    {'EXIT',{{badmatch,{necessary}},_}} = (catch do_tuple()),
381
382    [] = [X || X <- [], #bird{a = a} == {r,X,foo}],
383    [] = [X || X <- [], #bird{b = b} == {bird,X}],
384    [] = [X || X <- [], 3 == X#bird.a],
385
386    ok.
387
388do_tuple() ->
389    {0, _} = {necessary}.
390
391-record(x, {a}).
392
393record_float(_Config) ->
394    17.0 = record_float(#x{a={0}}, 1700),
395    23.0 = record_float(#x{a={0}}, 2300.0),
396    {'EXIT',{if_clause,_}} = (catch record_float(#x{a={1}}, 88)),
397    {'EXIT',{if_clause,_}} = (catch record_float(#x{a={}}, 88)),
398    {'EXIT',{if_clause,_}} = (catch record_float(#x{}, 88)),
399    ok.
400
401record_float(R, N0) ->
402    N = N0 / 100,
403    if element(1, R#x.a) =:= 0 ->
404            N
405    end.
406
407binary_float(_Config) ->
408    <<-1/float>> = binary_negate_float(<<1/float>>),
409    ok.
410
411binary_negate_float(<<Float/float>>) ->
412    <<-Float/float>>.
413
414float_compare(_Config) ->
415    false = do_float_compare(-42.0),
416    false = do_float_compare(-42),
417    false = do_float_compare(0),
418    false = do_float_compare(0.0),
419    true = do_float_compare(42),
420    true = do_float_compare(42.0),
421    ok.
422
423do_float_compare(X) ->
424    %% ERL-433: Used to fail before OTP 20. Was accidentally fixed
425    %% in OTP 20. Add a test case to ensure it stays fixed.
426
427    Y = X + 1.0,
428    case X > 0 of
429        T when (T =:= nil) or (T =:= false) -> T;
430        _T -> Y > 0
431    end.
432
433arity_checks(_Config) ->
434    %% ERL-549: an unsafe optimization removed a test_arity instruction,
435    %% causing the following to return 'broken' instead of 'ok'.
436    ok = do_record_arity_check({rgb, 255, 255, 255, 1}),
437    ok = do_tuple_arity_check({255, 255, 255, 1}).
438
439-record(rgb, {r = 255, g = 255, b = 255}).
440
441do_record_arity_check(RGB) when
442        (element(2, RGB) >= 0), (element(2, RGB) =< 255),
443        (element(3, RGB) >= 0), (element(3, RGB) =< 255),
444        (element(4, RGB) >= 0), (element(4, RGB) =< 255) ->
445    if
446        element(1, RGB) =:= rgb, is_record(RGB, rgb) -> broken;
447        true -> ok
448    end.
449
450do_tuple_arity_check(RGB) when is_tuple(RGB),
451        (element(1, RGB) >= 0), (element(1, RGB) =< 255),
452        (element(2, RGB) >= 0), (element(2, RGB) =< 255),
453        (element(3, RGB) >= 0), (element(3, RGB) =< 255) ->
454    case RGB of
455        {255, _, _} -> broken;
456        _ -> ok
457    end.
458
459elixir_binaries(_Config) ->
460    <<"foo blitzky baz">> = elixir_binary_1(<<"blitzky">>),
461    <<"foo * baz">> = elixir_binary_2($*),
462    <<7:4,755:10>> = elixir_bitstring_3(<<755:10>>),
463    ok.
464
465elixir_binary_1(Bar) when is_binary(Bar) ->
466    <<"foo ",
467      case Bar of
468          Rewrite when is_binary(Rewrite) ->
469              Rewrite;
470          Rewrite ->
471              list_to_binary(Rewrite)
472      end/binary,
473      " baz">>.
474
475elixir_binary_2(Arg) ->
476    Bin = <<Arg>>,
477    <<"foo ",
478      case Bin of
479          Rewrite when is_binary(Rewrite) ->
480              Rewrite;
481          Rewrite ->
482              list_to_binary:to_string(Rewrite)
483      end/binary,
484      " baz">>.
485
486elixir_bitstring_3(Bar) when is_bitstring(Bar) ->
487    <<7:4,
488      case Bar of
489          Rewrite when is_bitstring(Rewrite) ->
490              Rewrite;
491          Rewrite ->
492              list_to_bitstring(Rewrite)
493      end/bitstring>>.
494
495find_best(_Config) ->
496    ok = find_best([a], nil),
497    ok = find_best([<<"a">>], nil),
498    {error,_} = find_best([], nil),
499    ok.
500
501%% Failed because beam_type assumed that the operand
502%% for bs_context_binary must be a binary. Not true!
503find_best([a|Tail], Best) ->
504    find_best(Tail,
505      case Best of
506          X when X =:= nil orelse X =:= false -> a;
507          X -> X
508      end);
509find_best([<<"a">>|Tail], Best) ->
510    find_best(Tail,
511      case Best of
512          X when X =:= nil orelse X =:= false -> <<"a">>;
513          X -> X
514      end);
515find_best([], a) ->
516    ok;
517find_best([], <<"a">>) ->
518    ok;
519find_best([], nil) ->
520    {error,<<"should not get here">>}.
521
522test_size(_Config) ->
523    2 = do_test_size({a,b}),
524    4 = do_test_size(<<42:32>>),
525    ok.
526
527do_test_size(Term) when is_tuple(Term) ->
528    size(Term);
529do_test_size(Term) when is_binary(Term) ->
530    size(Term).
531
532cover_lists_functions(Config) ->
533    case lists:suffix([no|Config], Config) of
534        true ->
535            ct:fail(should_be_false);
536        false ->
537            ok
538    end,
539    Zipped = lists:zipwith(fun(A, B) -> {A,B} end,
540                           lists:duplicate(length(Config), zip),
541                           Config),
542    true = is_list(Zipped),
543    ok.
544
545list_append(_Config) ->
546    %% '++'/2 has a quirk where it returns the right-hand argument as-is when
547    %% the left-hand is [].
548    hello = id([]) ++ id(hello),
549    ok.
550
551%% OTP-15872: The compiler would treat the "Unit" of bs_init instructions as
552%% the unit of the result instead of the required unit of the input, causing
553%% is_binary checks to be wrongly optimized away.
554bad_binary_unit(_Config) ->
555    Bin = id(<<1,2,3>>),
556    Bitstring = <<Bin/binary,1:1>>,
557    false = is_binary(Bitstring),
558    ok.
559
560%% ERL-1013: The compiler would crash during the type optimization pass.
561none_argument(_Config) ->
562    Binary = id(<<3:16, 42>>),
563    error = id(case Binary of
564                   <<Len:16, Body/binary>> when length(Body) == Len - 2 ->
565                       %% The type for Body will be none. It means
566                       %% that this clause will never match and that
567                       %% uncompress/1 will never be called.
568                       uncompress(Body);
569                   _ ->
570                       error
571               end),
572    ok.
573
574uncompress(CompressedBinary) ->
575    %% The type for CompressedBinary is none, which beam_ssa_type
576    %% did not handle properly.
577    zlib:uncompress(CompressedBinary).
578
579%% ERL-1289: The compiler could enter an endless loop when a return/argument
580%% type pairing was joined with another.
581%%
582%% While this always resulted in correct success types, the joined argument
583%% types could now cover more cases than they did before, making the effective
584%% return type less specific. When a function affected by this was analyzed
585%% again its success typing could become more specific again and start the
586%% process anew.
587success_type_oscillation(_Config) ->
588    Base = {a, []},
589
590    Base = sto_1(id(case_1_1)),
591    Base = sto_1(id(case_2_1)),
592    {b, [Base]} = sto_1(id(case_2_2)),
593
594    ok.
595
596sto_1(case_1_1) -> {a, []};
597sto_1(case_1_2) -> {a, []};
598sto_1(case_2_1) -> sto_1(case_1_1);
599sto_1(case_2_2) -> {b, [sto_1(case_1_1)]};
600sto_1(case_2_3) -> {b, [sto_1(case_1_1)]};
601sto_1(case_2_4) -> {b, [sto_1(case_1_2)]};
602sto_1(case_3_1) -> {b, [sto_1(case_2_1)]};
603sto_1(case_3_2) -> {b, [sto_1(case_2_2)]};
604sto_1(case_3_3) -> {b, [sto_1(case_2_3)]};
605sto_1(case_3_4) -> {b, [sto_1(case_2_4)]};
606sto_1(case_4_1) -> {b, [sto_1(case_3_1)]};
607sto_1(case_4_2) -> {b, [sto_1(case_3_2)]};
608sto_1(step_4_3) -> {b, [sto_1(case_3_3)]}.
609
610%% ERL-1440: On inequality, we subtracted the type *common to* both variables
611%% rather than the left-hand type from the right-hand variable and vice versa,
612%% giving an erroneously narrow type.
613%%
614%% In the test below, we have functions returning integers ranged 1..2 and
615%% 2..3 and test for their equality. We know that it can only succeed when both
616%% return 2, but they can fail when the former returns 1 or the latter returns
617%% 3, so we must not subtract 2 on the failure path.
618type_subtraction(Config) when is_list(Config) ->
619    true = type_subtraction_1(id(<<"A">>)),
620    ok.
621
622type_subtraction_1(_x@1) ->
623    _a@1 = ts_12(_x@1),
624    _b@1 = ts_23(_x@1),
625    case _a@1 /= _b@1 of
626        false -> error;
627        true -> _a@1 =:= 3 andalso _b@1 =:= 2
628    end.
629
630ts_12(_x@1) ->
631    case _x@1 == <<"A">> of
632        false ->
633            2;
634        true ->
635            3
636    end.
637
638ts_23(_x@1) ->
639    case _x@1 == <<"A">> of
640        false ->
641            1;
642        true ->
643            2
644    end.
645
646%% GH-4774: The validator didn't update container contents on type subtraction.
647container_subtraction(Config) when is_list(Config) ->
648    A = id(baz),
649
650    cs_1({foo,[]}),
651    cs_1({bar,A}),
652    cs_2({bar,A}),
653
654    ok.
655
656cs_1({_,[]}) ->
657    ok;
658cs_1({_,_}=Other) ->
659    cs_2(Other).
660
661cs_2({bar,baz}) ->
662    ok.
663
664id(I) ->
665    I.
666