aboutsummaryrefslogtreecommitdiffstats
path: root/lib/compiler/test
diff options
context:
space:
mode:
Diffstat (limited to 'lib/compiler/test')
-rw-r--r--lib/compiler/test/Makefile3
-rw-r--r--lib/compiler/test/beam_ssa_SUITE.erl107
-rw-r--r--lib/compiler/test/beam_types_SUITE.erl124
-rw-r--r--lib/compiler/test/beam_validator_SUITE.erl46
-rw-r--r--lib/compiler/test/beam_validator_SUITE_data/branch_to_try_handler.S48
-rw-r--r--lib/compiler/test/bs_match_SUITE.erl162
-rw-r--r--lib/compiler/test/compile_SUITE.erl38
-rw-r--r--lib/compiler/test/guard_SUITE.erl123
-rw-r--r--lib/compiler/test/misc_SUITE.erl33
-rw-r--r--lib/compiler/test/property_test/beam_types_prop.erl228
-rw-r--r--lib/compiler/test/test_lib.erl2
11 files changed, 837 insertions, 77 deletions
diff --git a/lib/compiler/test/Makefile b/lib/compiler/test/Makefile
index 7be23fbb93..44cff40128 100644
--- a/lib/compiler/test/Makefile
+++ b/lib/compiler/test/Makefile
@@ -16,6 +16,7 @@ MODULES= \
beam_reorder_SUITE \
beam_ssa_SUITE \
beam_type_SUITE \
+ beam_types_SUITE \
beam_utils_SUITE \
bif_SUITE \
bs_bincomp_SUITE \
@@ -236,6 +237,6 @@ release_tests_spec: make_emakefile
$(INSTALL_DATA) $(ERL_DUMMY_FILES) "$(RELSYSDIR)"
rm $(ERL_DUMMY_FILES)
chmod -R u+w "$(RELSYSDIR)"
- @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -)
+ @tar cf - *_SUITE_data property_test | (cd "$(RELSYSDIR)"; tar xf -)
release_docs_spec:
diff --git a/lib/compiler/test/beam_ssa_SUITE.erl b/lib/compiler/test/beam_ssa_SUITE.erl
index 3b510f3528..054f86731a 100644
--- a/lib/compiler/test/beam_ssa_SUITE.erl
+++ b/lib/compiler/test/beam_ssa_SUITE.erl
@@ -200,6 +200,8 @@ recv(_Config) ->
%% tricky_recv_6/0 is a compile-time error.
tricky_recv_6(),
+ recv_coverage(),
+
ok.
sync_wait_mon({Pid, Ref}, Timeout) ->
@@ -326,7 +328,7 @@ tricky_recv_5() ->
end.
%% When fixing tricky_recv_5, we introduced a compiler crash when the common
-%% exit block was ?BADARG_BLOCK and floats were in the picture.
+%% exit block was ?EXCEPTION_BLOCK and floats were in the picture.
tricky_recv_6() ->
RefA = make_ref(),
RefB = make_ref(),
@@ -337,6 +339,69 @@ tricky_recv_6() ->
ok
end.
+recv_coverage() ->
+ self() ! 1,
+ a = recv_coverage_1(),
+ self() ! 2,
+ b = recv_coverage_1(),
+
+ self() ! 1,
+ a = recv_coverage_2(),
+ self() ! 2,
+ b = recv_coverage_2(),
+
+ ok.
+
+%% Similar to tricky_recv_5/0, but provides test coverage for the #b_switch{}
+%% terminator.
+recv_coverage_1() ->
+ receive
+ X=1 ->
+ %% Jump to common exit block through #b_switch{list=L}
+ case id(0) of
+ 0 -> a;
+ 1 -> b;
+ 2 -> c;
+ 3 -> d
+ end;
+ X=2 ->
+ %% Jump to common exit block through #b_switch{fail=F}
+ case id(42) of
+ 0 -> exit(quit);
+ 1 -> exit(quit);
+ 2 -> exit(quit);
+ 3 -> exit(quit);
+ _ -> b
+ end
+ end,
+ case X of
+ 1 -> a;
+ 2 -> b
+ end.
+
+%% Similar to recv_coverage_1/0, providing test coverage for #b_br{}.
+recv_coverage_2() ->
+ receive
+ X=1 ->
+ A = id(1),
+ %% Jump to common exit block through #b_br{succ=S}.
+ if
+ A =:= 1 -> a;
+ true -> exit(quit)
+ end;
+ X=2 ->
+ A = id(2),
+ %% Jump to common exit block through #b_br{fail=F}.
+ if
+ A =:= 1 -> exit(quit);
+ true -> a
+ end
+ end,
+ case X of
+ 1 -> a;
+ 2 -> b
+ end.
+
maps(_Config) ->
{'EXIT',{{badmatch,#{}},_}} = (catch maps_1(any)),
ok.
@@ -388,48 +453,8 @@ cover_ssa_dead(_Config) ->
40.0 = percentage(4.0, 10.0),
60.0 = percentage(6, 10),
- %% Cover '=:=', followed by '=/='.
- false = 'cover__=:=__=/='(41),
- true = 'cover__=:=__=/='(42),
- false = 'cover__=:=__=/='(43),
-
- %% Cover '<', followed by '=/='.
- true = 'cover__<__=/='(41),
- false = 'cover__<__=/='(42),
- false = 'cover__<__=/='(43),
-
- %% Cover '=<', followed by '=/='.
- true = 'cover__=<__=/='(41),
- true = 'cover__=<__=/='(42),
- false = 'cover__=<__=/='(43),
-
- %% Cover '>=', followed by '=/='.
- false = 'cover__>=__=/='(41),
- true = 'cover__>=__=/='(42),
- true = 'cover__>=__=/='(43),
-
- %% Cover '>', followed by '=/='.
- false = 'cover__>__=/='(41),
- false = 'cover__>__=/='(42),
- true = 'cover__>__=/='(43),
-
ok.
-'cover__=:=__=/='(X) when X =:= 42 -> X =/= 43;
-'cover__=:=__=/='(_) -> false.
-
-'cover__<__=/='(X) when X < 42 -> X =/= 42;
-'cover__<__=/='(_) -> false.
-
-'cover__=<__=/='(X) when X =< 42 -> X =/= 43;
-'cover__=<__=/='(_) -> false.
-
-'cover__>=__=/='(X) when X >= 42 -> X =/= 41;
-'cover__>=__=/='(_) -> false.
-
-'cover__>__=/='(X) when X > 42 -> X =/= 42;
-'cover__>__=/='(_) -> false.
-
format_str(Str, FormatData, IoList, EscChars) ->
Escapable = FormatData =:= escapable,
case id(Str) of
diff --git a/lib/compiler/test/beam_types_SUITE.erl b/lib/compiler/test/beam_types_SUITE.erl
new file mode 100644
index 0000000000..8e71a716cd
--- /dev/null
+++ b/lib/compiler/test/beam_types_SUITE.erl
@@ -0,0 +1,124 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2019. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(beam_types_SUITE).
+
+-include_lib("compiler/src/beam_types.hrl").
+
+-export([all/0, suite/0, groups/0,
+ init_per_suite/1, end_per_suite/1]).
+
+-export([absorption/1,
+ associativity/1,
+ commutativity/1,
+ idempotence/1,
+ identity/1]).
+
+-export([binary_absorption/1,
+ integer_absorption/1,
+ integer_associativity/1]).
+
+suite() ->
+ [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [{group,property_tests},
+ binary_absorption,
+ integer_absorption,
+ integer_associativity].
+
+groups() ->
+ [{property_tests,[parallel],
+ [absorption,
+ associativity,
+ commutativity,
+ idempotence,
+ identity]}].
+
+init_per_suite(Config) ->
+ ct_property_test:init_per_suite(Config).
+
+end_per_suite(Config) ->
+ Config.
+
+absorption(Config) when is_list(Config) ->
+ %% manual test: proper:quickcheck(beam_types_prop:absorption()).
+ true = ct_property_test:quickcheck(beam_types_prop:absorption(), Config).
+
+associativity(Config) when is_list(Config) ->
+ %% manual test: proper:quickcheck(beam_types_prop:associativity()).
+ true = ct_property_test:quickcheck(beam_types_prop:associativity(), Config).
+
+commutativity(Config) when is_list(Config) ->
+ %% manual test: proper:quickcheck(beam_types_prop:commutativity()).
+ true = ct_property_test:quickcheck(beam_types_prop:commutativity(), Config).
+
+idempotence(Config) when is_list(Config) ->
+ %% manual test: proper:quickcheck(beam_types_prop:idempotence()).
+ true = ct_property_test:quickcheck(beam_types_prop:idempotence(), Config).
+
+identity(Config) when is_list(Config) ->
+ %% manual test: proper:quickcheck(beam_types_prop:identity()).
+ true = ct_property_test:quickcheck(beam_types_prop:identity(), Config).
+
+binary_absorption(Config) when is_list(Config) ->
+ %% These binaries should meet into {binary,12} as that's the best common
+ %% unit for both types.
+ A = #t_bitstring{unit=4},
+ B = #t_bitstring{unit=6},
+
+ #t_bitstring{unit=12} = beam_types:meet(A, B),
+ #t_bitstring{unit=2} = beam_types:join(A, B),
+
+ A = beam_types:meet(A, beam_types:join(A, B)),
+ A = beam_types:join(A, beam_types:meet(A, B)),
+
+ ok.
+
+integer_absorption(Config) when is_list(Config) ->
+ %% Integers that don't overlap at all should never meet.
+ A = #t_integer{elements={2,3}},
+ B = #t_integer{elements={4,5}},
+
+ none = beam_types:meet(A, B),
+ #t_integer{elements={2,5}} = beam_types:join(A, B),
+
+ A = beam_types:meet(A, beam_types:join(A, B)),
+ A = beam_types:join(A, beam_types:meet(A, B)),
+
+ ok.
+
+integer_associativity(Config) when is_list(Config) ->
+ A = #t_integer{elements={3,5}},
+ B = #t_integer{elements={4,6}},
+ C = #t_integer{elements={5,5}},
+
+ %% a ∨ (b ∨ c) = (a ∨ b) ∨ c,
+ LHS_Join = beam_types:join(A, beam_types:join(B, C)),
+ RHS_Join = beam_types:join(beam_types:join(A, B), C),
+ #t_integer{elements={3,6}} = LHS_Join = RHS_Join,
+
+ %% a ∧ (b ∧ c) = (a ∧ b) ∧ c.
+ LHS_Meet = beam_types:meet(A, beam_types:meet(B, C)),
+ RHS_Meet = beam_types:meet(beam_types:meet(A, B), C),
+ #t_integer{elements={5,5}} = LHS_Meet = RHS_Meet,
+
+ ok.
+
diff --git a/lib/compiler/test/beam_validator_SUITE.erl b/lib/compiler/test/beam_validator_SUITE.erl
index 20f6cb2691..68b665fbc3 100644
--- a/lib/compiler/test/beam_validator_SUITE.erl
+++ b/lib/compiler/test/beam_validator_SUITE.erl
@@ -35,7 +35,8 @@
map_field_lists/1,cover_bin_opt/1,
val_dsetel/1,bad_tuples/1,bad_try_catch_nesting/1,
receive_stacked/1,aliased_types/1,type_conflict/1,
- infer_on_eq/1,infer_dead_value/1]).
+ infer_on_eq/1,infer_dead_value/1,infer_on_ne/1,
+ branch_to_try_handler/1]).
-include_lib("common_test/include/ct.hrl").
@@ -65,7 +66,8 @@ groups() ->
map_field_lists,cover_bin_opt,val_dsetel,
bad_tuples,bad_try_catch_nesting,
receive_stacked,aliased_types,type_conflict,
- infer_on_eq,infer_dead_value]}].
+ infer_on_eq,infer_dead_value,infer_on_ne,
+ branch_to_try_handler]}].
init_per_suite(Config) ->
test_lib:recompile(?MODULE),
@@ -217,11 +219,11 @@ bad_catch_try(Config) when is_list(Config) ->
{{catch_end,{x,9}},
8,{invalid_tag_register,{x,9}}}},
{{bad_catch_try,bad_3,1},
- {{catch_end,{y,1}},9,{invalid_tag,{y,1},{atom,kalle}}}},
+ {{catch_end,{y,1}},9,{invalid_tag,{y,1},{t_atom,[kalle]}}}},
{{bad_catch_try,bad_4,1},
{{'try',{x,0},{f,15}},5,{invalid_tag_register,{x,0}}}},
{{bad_catch_try,bad_5,1},
- {{try_case,{y,1}},12,{invalid_tag,{y,1},term}}},
+ {{try_case,{y,1}},12,{invalid_tag,{y,1},any}}},
{{bad_catch_try,bad_6,1},
{{move,{integer,1},{y,1}},7,
{invalid_store,{y,1}}}}] = Errors,
@@ -232,7 +234,7 @@ cons_guard(Config) when is_list(Config) ->
[{{cons,foo,1},
{{get_list,{x,0},{x,1},{x,2}},
5,
- {bad_type,{needed,cons},{actual,term}}}}] = Errors,
+ {bad_type,{needed,cons},{actual,any}}}}] = Errors,
ok.
freg_range(Config) when is_list(Config) ->
@@ -520,9 +522,9 @@ bad_tuples(Config) ->
{{bad_tuples,long,2},
{{put,{atom,too_long}},8,not_building_a_tuple}},
{{bad_tuples,self_referential,1},
- {{put,{x,1}},7,{tuple_in_progress,{x,1}}}},
+ {{put,{x,1}},7,{unfinished_tuple,{x,1}}}},
{{bad_tuples,short,1},
- {{move,{x,1},{x,0}},7,{tuple_in_progress,{x,1}}}}] = Errors,
+ {{move,{x,1},{x,0}},7,{unfinished_tuple,{x,1}}}}] = Errors,
ok.
@@ -705,6 +707,25 @@ idv_1({_A, _B, _C, _D, _E, F, G},
idv_1(_A, _B) ->
error.
+%% ERL-998; type inference for select_val (#b_switch{}) was more clever than
+%% that for is_ne_exact (#b_br{}), sometimes failing validation when the type
+%% optimization pass acted on the former and the validator got the latter.
+
+-record(ion, {state}).
+
+infer_on_ne(Config) when is_list(Config) ->
+ #ion{state = closing} = ion_1(#ion{ state = id(open) }),
+ #ion{state = closing} = ion_close(#ion{ state = open }),
+ ok.
+
+ion_1(State = #ion{state = open}) -> ion_2(State);
+ion_1(State = #ion{state = closing}) -> ion_2(State).
+
+ion_2(State = #ion{state = open}) -> ion_close(State);
+ion_2(#ion{state = closing}) -> ok.
+
+ion_close(State = #ion{}) -> State#ion{state = closing}.
+
%% ERL-995: The first solution to ERIERL-348 was incomplete and caused
%% validation to fail when living values depended on delayed type inference on
%% "dead" values.
@@ -722,6 +743,17 @@ idv_2(State) ->
idv_called_once(_State) -> ok.
+%% Direct jumps to try/catch handlers crash the emulator and must fail
+%% validation. This is provoked by OTP-15945.
+
+branch_to_try_handler(Config) ->
+ Errors = do_val(branch_to_try_handler, Config),
+ [{{branch_to_try_handler,main,1},
+ {{bif,tuple_size,{f,3},[{y,0}],{x,0}},
+ 12,
+ {illegal_branch,try_handler,3}}}] = Errors,
+ ok.
+
%%%-------------------------------------------------------------------------
transform_remove(Remove, Module) ->
diff --git a/lib/compiler/test/beam_validator_SUITE_data/branch_to_try_handler.S b/lib/compiler/test/beam_validator_SUITE_data/branch_to_try_handler.S
new file mode 100644
index 0000000000..6d43ec7b54
--- /dev/null
+++ b/lib/compiler/test/beam_validator_SUITE_data/branch_to_try_handler.S
@@ -0,0 +1,48 @@
+{module, branch_to_try_handler}. %% version = 0
+
+{exports, [{main,1}]}.
+
+{attributes, []}.
+
+{labels, 11}.
+
+{function, main, 1, 2}.
+ {label,1}.
+ {line,[{location,"t.erl",4}]}.
+ {func_info,{atom,branch_to_try_handler},{atom,main},1}.
+ {label,2}.
+ {allocate,2,1}.
+ {move,{x,0},{y,0}}.
+ {'try',{y,1},{f,3}}.
+ {move,{atom,ignored},{x,0}}.
+ {line,[{location,"t.erl",6}]}.
+ {call,1,{f,6}}.
+ {'%',{type_info,{x,0},{t_atom,[ignored]}}}.
+ {line,[{location,"t.erl",7}]}.
+ %%
+ %% Fail directly to the try handler instead of throwing an exception; this
+ %% will crash the emulator.
+ %%
+ {bif,tuple_size,{f,3},[{y,0}],{x,0}}.
+ %%
+ {test,is_eq_exact,{f,4},[{x,0},{integer,1}]}.
+ {move,{atom,error},{x,0}}.
+ {try_end,{y,1}}.
+ {deallocate,2}.
+ return.
+ {label,3}.
+ {try_case,{y,1}}.
+ {move,{atom,ok},{x,0}}.
+ {deallocate,2}.
+ return.
+ {label,4}.
+ {line,[{location,"t.erl",7}]}.
+ {badmatch,{x,0}}.
+
+{function, id, 1, 6}.
+ {label,5}.
+ {line,[{location,"t.erl",13}]}.
+ {func_info,{atom,branch_to_try_handler},{atom,id},1}.
+ {label,6}.
+ {'%',{type_info,{x,0},{t_atom,[ignored]}}}.
+ return.
diff --git a/lib/compiler/test/bs_match_SUITE.erl b/lib/compiler/test/bs_match_SUITE.erl
index 145a50f4ad..0dc1d64eeb 100644
--- a/lib/compiler/test/bs_match_SUITE.erl
+++ b/lib/compiler/test/bs_match_SUITE.erl
@@ -24,7 +24,7 @@
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2,
init_per_testcase/2,end_per_testcase/2,
- verify_highest_opcode/1,
+ verify_highest_opcode/1, expand_and_squeeze/1,
size_shadow/1,int_float/1,otp_5269/1,null_fields/1,wiger/1,
bin_tail/1,save_restore/1,
partitioned_bs_match/1,function_clause/1,
@@ -64,7 +64,7 @@ groups() ->
[{p,[],
[verify_highest_opcode,
size_shadow,int_float,otp_5269,null_fields,wiger,
- bin_tail,save_restore,
+ bin_tail,save_restore,expand_and_squeeze,
partitioned_bs_match,function_clause,unit,
shared_sub_bins,bin_and_float,dec_subidentifiers,
skip_optional_tag,decode_integer,wfbm,degenerated_match,bs_sum,
@@ -2021,3 +2021,161 @@ do_exceptions_after_match_failure(Other) ->
ok.
id(I) -> I.
+
+expand_and_squeeze(Config) when is_list(Config) ->
+ %% UTF8 literals are expanded and then squeezed into integer16
+ [
+ {test,bs_get_integer2,_,_,[_,{integer,16}|_],_}
+ | _
+ ] = binary_match_to_asm([
+ ?Q("<<$á/utf8,_/binary>>"),
+ ?Q("<<$é/utf8,_/binary>>")
+ ]),
+
+ %% Sized integers are expanded and then squeezed into integer16
+ [
+ {test,bs_get_integer2,_,_,[_,{integer,16}|_],_}
+ | _
+ ] = binary_match_to_asm([
+ ?Q("<<0:32,_/binary>>"),
+ ?Q("<<\"bbbb\",_/binary>>")
+ ]),
+
+ %% Groups of 8 bits are squeezed into integer16
+ [
+ {test,bs_get_integer2,_,_,[_,{integer,16}|_],_}
+ | _
+ ] = binary_match_to_asm([
+ ?Q("<<\"aaaa\",_/binary>>"),
+ ?Q("<<\"bbbb\",_/binary>>")
+ ]),
+
+ %% Groups of 8 bits with empty binary are also squeezed
+ [
+ {test,bs_get_integer2,_,_,[_,{integer,16}|_],_}
+ | _
+ ] = binary_match_to_asm([
+ ?Q("<<\"aaaa\",_/binary>>"),
+ ?Q("<<\"bbbb\",_/binary>>"),
+ ?Q("<<>>")
+ ]),
+
+ %% Groups of 8 bits with float lookup are not squeezed
+ [
+ {test,bs_get_integer2,_,_,[_,{integer,8}|_],_}
+ | _
+ ] = binary_match_to_asm([
+ ?Q("<<\"aaaa\",_/binary>>"),
+ ?Q("<<\"bbbb\",_/binary>>"),
+ ?Q("<<_/float>>")
+ ]),
+
+ %% Groups of diverse bits go with minimum possible
+ [
+ {test,bs_get_integer2,_,_,[_,{integer,8}|_],_}
+ | _
+ ] = binary_match_to_asm([
+ ?Q("<<\"aa\",_/binary>>"),
+ ?Q("<<\"bb\",_/binary>>"),
+ ?Q("<<\"c\",_/binary>>")
+ ]),
+
+ %% Groups of diverse bits go with minimum possible but are recursive...
+ [
+ {test,bs_get_integer2,_,_,[_,{integer,8}|_],_}
+ | RestDiverse
+ ] = binary_match_to_asm([
+ ?Q("<<\"aaa\",_/binary>>"),
+ ?Q("<<\"abb\",_/binary>>"),
+ ?Q("<<\"c\",_/binary>>")
+ ]),
+
+ %% so we still perform a 16 bits lookup for the remaining
+ true = lists:any(fun({test,bs_get_integer2,_,_,[_,{integer,16}|_],_}) -> true;
+ (_) -> false end, RestDiverse),
+
+ %% Large match is kept as is if there is a sized match later
+ [
+ {test,bs_get_integer2,_,_,[_,{integer,64}|_],_}
+ | _
+ ] = binary_match_to_asm([
+ ?Q("<<255,255,255,255,255,255,255,255>>"),
+ ?Q("<<_:64>>")
+ ]),
+
+ %% Large match is kept as is with large matches before and after
+ [
+ {test,bs_get_integer2,_,_,[_,{integer,32}|_],_}
+ | _
+ ] = binary_match_to_asm([
+ ?Q("<<A:32,_:A>>"),
+ ?Q("<<0:32>>"),
+ ?Q("<<_:32>>")
+ ]),
+
+ %% Large match is kept as is with large matches before and after
+ [
+ {test,bs_get_integer2,_,_,[_,{integer,32}|_],_}
+ | _
+ ] = binary_match_to_asm([
+ ?Q("<<A:32,_:A>>"),
+ ?Q("<<0,0,0,0>>"),
+ ?Q("<<_:32>>")
+ ]),
+
+ %% Large match is kept as is with smaller but still large matches before and after
+ [
+ {test,bs_get_integer2,_,_,[_,{integer,32}|_],_}
+ | _
+ ] = binary_match_to_asm([
+ ?Q("<<A:32, _:A>>"),
+ ?Q("<<0:64>>"),
+ ?Q("<<_:32>>")
+ ]),
+
+ %% There is no squeezing for groups with more than 16 matches
+ [
+ {test,bs_get_integer2,_,_,[_,{integer,8}|_],_}
+ | _
+ ] = binary_match_to_asm([
+ ?Q("<<\"aa\", _/binary>>"),
+ ?Q("<<\"bb\", _/binary>>"),
+ ?Q("<<\"cc\", _/binary>>"),
+ ?Q("<<\"dd\", _/binary>>"),
+ ?Q("<<\"ee\", _/binary>>"),
+ ?Q("<<\"ff\", _/binary>>"),
+ ?Q("<<\"gg\", _/binary>>"),
+ ?Q("<<\"hh\", _/binary>>"),
+ ?Q("<<\"ii\", _/binary>>"),
+ ?Q("<<\"jj\", _/binary>>"),
+ ?Q("<<\"kk\", _/binary>>"),
+ ?Q("<<\"ll\", _/binary>>"),
+ ?Q("<<\"mm\", _/binary>>"),
+ ?Q("<<\"nn\", _/binary>>"),
+ ?Q("<<\"oo\", _/binary>>"),
+ ?Q("<<\"pp\", _/binary>>")
+ ]),
+
+ ok.
+
+binary_match_to_asm(Matches) ->
+ Clauses = [
+ begin
+ Ann = element(2, Match),
+ {clause,Ann,[Match],[],[{integer,Ann,Return}]}
+ end || {Match,Return} <- lists:zip(Matches, lists:seq(1, length(Matches)))
+ ],
+
+ Module = [
+ {attribute,1,module,match_to_asm},
+ {attribute,2,export,[{example,1}]},
+ {function,3,example,1,Clauses}
+ ],
+
+ {ok,match_to_asm,{match_to_asm,_Exports,_Attrs,Funs,_},_} =
+ compile:forms(Module, [return, to_asm]),
+
+ [{function,example,1,2,AllInstructions}|_] = Funs,
+ [{label,_},{line,_},{func_info,_,_,_},{label,_},{'%',_},
+ {test,bs_start_match3,_,_,_,_},{bs_get_position,_,_,_}|Instructions] = AllInstructions,
+ Instructions.
diff --git a/lib/compiler/test/compile_SUITE.erl b/lib/compiler/test/compile_SUITE.erl
index 53627b9d81..453debc0c1 100644
--- a/lib/compiler/test/compile_SUITE.erl
+++ b/lib/compiler/test/compile_SUITE.erl
@@ -378,7 +378,6 @@ do_file_listings(DataDir, PrivDir, [File|Files]) ->
{dprecg, ".precodegen"},
{dcg, ".codegen"},
{dblk, ".block"},
- {dexcept, ".except"},
{djmp, ".jump"},
{dclean, ".clean"},
{dpeep, ".peep"},
@@ -1383,27 +1382,33 @@ env_compiler_options(_Config) ->
bc_options(Config) ->
DataDir = proplists:get_value(data_dir, Config),
- L = [{101, small_float, [no_get_hd_tl,no_line_info]},
- {103, big, [no_put_tuple2,no_get_hd_tl,no_ssa_opt_record,
+ L = [{101, small_float, [no_shared_fun_wrappers,
+ no_get_hd_tl,no_line_info]},
+ {103, big, [no_shared_fun_wrappers,
+ no_put_tuple2,no_get_hd_tl,no_ssa_opt_record,
no_line_info,no_stack_trimming]},
- {125, small_float, [no_get_hd_tl,no_line_info,no_ssa_opt_float]},
+ {125, small_float, [no_shared_fun_wrappers,no_get_hd_tl,
+ no_line_info,
+ no_ssa_opt_float]},
- {132, small, [no_put_tuple2,no_get_hd_tl,no_ssa_opt_record,
+ {132, small, [no_shared_fun_wrappers,
+ no_put_tuple2,no_get_hd_tl,no_ssa_opt_record,
no_ssa_opt_float,no_line_info,no_bsm3]},
+ {136, big, [no_shared_fun_wrappers,no_put_tuple2,no_get_hd_tl,
+ no_ssa_opt_record,no_line_info]},
+
{153, small, [r20]},
{153, small, [r21]},
- {136, big, [no_put_tuple2,no_get_hd_tl,
- no_ssa_opt_record,no_line_info]},
-
- {153, big, [no_put_tuple2,no_get_hd_tl, no_ssa_opt_record]},
+ {153, big, [no_shared_fun_wrappers,
+ no_put_tuple2,no_get_hd_tl, no_ssa_opt_record]},
{153, big, [r16]},
{153, big, [r17]},
{153, big, [r18]},
{153, big, [r19]},
{153, small_float, [r16]},
- {153, small_float, []},
+ {153, small_float, [no_shared_fun_wrappers]},
{158, small_maps, [r17]},
{158, small_maps, [r18]},
@@ -1411,8 +1416,17 @@ bc_options(Config) ->
{158, small_maps, [r20]},
{158, small_maps, [r21]},
- {164, small_maps, []},
- {164, big, []}
+ {164, small_maps, [r22]},
+ {164, big, [r22]},
+ {164, small_maps, [no_shared_fun_wrappers]},
+ {164, big, [no_shared_fun_wrappers]},
+
+ {168, small, [r22]},
+ {168, small, [no_shared_fun_wrappers]},
+
+ {169, small_maps, []},
+ {169, big, []},
+ {169, small, []}
],
Test = fun({Expected,Mod,Options}) ->
diff --git a/lib/compiler/test/guard_SUITE.erl b/lib/compiler/test/guard_SUITE.erl
index cea7a374cd..d3d62b53f5 100644
--- a/lib/compiler/test/guard_SUITE.erl
+++ b/lib/compiler/test/guard_SUITE.erl
@@ -19,7 +19,7 @@
%%
-module(guard_SUITE).
--include_lib("common_test/include/ct.hrl").
+-include_lib("syntax_tools/include/merl.hrl").
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2,
@@ -31,7 +31,8 @@
old_guard_tests/1,complex_guard/1,
build_in_guard/1,gbif/1,
t_is_boolean/1,is_function_2/1,
- tricky/1,rel_ops/1,rel_op_combinations/1,literal_type_tests/1,
+ tricky/1,rel_ops/1,rel_op_combinations/1,
+ generated_combinations/1,literal_type_tests/1,
basic_andalso_orelse/1,traverse_dcd/1,
check_qlc_hrl/1,andalso_semi/1,t_tuple_size/1,binary_part/1,
bad_constants/1,bad_guards/1,
@@ -51,7 +52,7 @@ groups() ->
more_xor_guards,build_in_guard,
old_guard_tests,complex_guard,gbif,
t_is_boolean,is_function_2,tricky,
- rel_ops,rel_op_combinations,
+ rel_ops,rel_op_combinations,generated_combinations,
literal_type_tests,basic_andalso_orelse,traverse_dcd,
check_qlc_hrl,andalso_semi,t_tuple_size,binary_part,
bad_constants,bad_guards,guard_in_catch,beam_bool_SUITE,
@@ -1579,6 +1580,122 @@ redundant_12(X) when X >= 50, X =< 80 -> 2*X;
redundant_12(X) when X < 51 -> 5*X;
redundant_12(_) -> none.
+generated_combinations(Config) ->
+ case ?MODULE of
+ guard_SUITE -> generated_combinations_1(Config);
+ _ -> {skip,"Enough to run this case once."}
+ end.
+
+%% Exhaustively test all combinations of relational operators
+%% to ensure the correctness of the optimizations in beam_ssa_dead.
+
+generated_combinations_1(Config) ->
+ Mod = ?FUNCTION_NAME,
+ RelOps = ['=:=','=/=','==','/=','<','=<','>=','>'],
+ Combinations0 = [{Op1,Op2} || Op1 <- RelOps, Op2 <- RelOps],
+ Combinations1 = gen_lit_combs(Combinations0),
+ Combinations2 = [{neq,Comb} ||
+ {_Op1,_Lit1,Op2,_Lit2}=Comb <- Combinations1,
+ Op2 =:= '=/=' orelse Op2 =:= '/='] ++ Combinations1,
+ Combinations = gen_func_names(Combinations2, 0),
+ Fs = gen_rel_op_functions(Combinations),
+ Tree = ?Q(["-module('@Mod@').",
+ "-compile([export_all,nowarn_export_all])."]) ++ Fs,
+ %%merl:print(Tree),
+ Opts = test_lib:opt_opts(?MODULE),
+ {ok,_Bin} = merl:compile_and_load(Tree, Opts),
+ test_combinations(Combinations, Mod).
+
+gen_lit_combs([{Op1,Op2}|T]) ->
+ [{Op1,7,Op2,6},
+ {Op1,7.0,Op2,6},
+ {Op1,7,Op2,6.0},
+ {Op1,7.0,Op2,6.0},
+
+ {Op1,7,Op2,7},
+ {Op1,7.0,Op2,7},
+ {Op1,7,Op2,7.0},
+ {Op1,7.0,Op2,7.0},
+
+ {Op1,6,Op2,7},
+ {Op1,6.0,Op2,7},
+ {Op1,6,Op2,7.0},
+ {Op1,6.0,Op2,7.0}|gen_lit_combs(T)];
+gen_lit_combs([]) -> [].
+
+gen_func_names([E|Es], I) ->
+ Name = list_to_atom("f" ++ integer_to_list(I)),
+ [{Name,E}|gen_func_names(Es, I+1)];
+gen_func_names([], _) -> [].
+
+gen_rel_op_functions([{Name,{neq,{Op1,Lit1,Op2,Lit2}}}|T]) ->
+ %% Note that in the translation to SSA, '=/=' will be
+ %% translated to '=:=' in a guard (with switched success
+ %% and failure labels). Therefore, to test the optimization,
+ %% we must use '=/=' (or '/=') in a body context.
+ %%
+ %% Here is an example of a generated function:
+ %%
+ %% f160(A) when erlang:'>='(A, 7) ->
+ %% one;
+ %% f160(A) ->
+ %% true = erlang:'/='(A, 7),
+ %% two.
+ [?Q("'@Name@'(A) when erlang:'@Op1@'(A, _@Lit1@) -> one;
+ '@Name@'(A) -> true = erlang:'@Op2@'(A, _@Lit2@), two. ")|
+ gen_rel_op_functions(T)];
+gen_rel_op_functions([{Name,{Op1,Lit1,Op2,Lit2}}|T]) ->
+ %% Example of a generated function:
+ %%
+ %% f721(A) when erlang:'=<'(A, 7.0) -> one;
+ %% f721(A) when erlang:'<'(A, 6) -> two;
+ %% f721(_) -> three.
+ [?Q("'@Name@'(A) when erlang:'@Op1@'(A, _@Lit1@) -> one;
+ '@Name@'(A) when erlang:'@Op2@'(A, _@Lit2@) -> two;
+ '@Name@'(_) -> three.")|gen_rel_op_functions(T)];
+gen_rel_op_functions([]) -> [].
+
+test_combinations([{Name,E}|T], Mod) ->
+ try
+ test_combinations_1([5,6,7,8,9], E, fun Mod:Name/1),
+ test_combination(6.5, E, fun Mod:Name/1)
+ catch
+ error:Reason:Stk ->
+ io:format("~p: ~p\n", [Name,E]),
+ erlang:raise(error, Reason, Stk)
+ end,
+ test_combinations(T, Mod);
+test_combinations([], _Mod) -> ok.
+
+test_combinations_1([V|Vs], E, Fun) ->
+ test_combination(V, E, Fun),
+ test_combination(float(V), E, Fun),
+ test_combinations_1(Vs, E, Fun);
+test_combinations_1([], _, _) -> ok.
+
+test_combination(Val, {neq,Expr}, Fun) ->
+ Result = eval_combination_expr(Expr, Val),
+ Result = try
+ Fun(Val) %Returns 'one' or 'two'.
+ catch
+ error:{badmatch,_} ->
+ three
+ end;
+test_combination(Val, Expr, Fun) ->
+ Result = eval_combination_expr(Expr, Val),
+ Result = Fun(Val).
+
+eval_combination_expr({Op1,Lit1,Op2,Lit2}, Val) ->
+ case erlang:Op1(Val, Lit1) of
+ true ->
+ one;
+ false ->
+ case erlang:Op2(Val, Lit2) of
+ true -> two;
+ false -> three
+ end
+ end.
+
%% Test type tests on literal values. (From emulator test suites.)
literal_type_tests(Config) when is_list(Config) ->
case ?MODULE of
diff --git a/lib/compiler/test/misc_SUITE.erl b/lib/compiler/test/misc_SUITE.erl
index a0b415ceaa..20fadc4fdb 100644
--- a/lib/compiler/test/misc_SUITE.erl
+++ b/lib/compiler/test/misc_SUITE.erl
@@ -227,15 +227,6 @@ silly_coverage(Config) when is_list(Config) ->
{label,2}|non_proper_list]}],99},
expect_error(fun() -> beam_block:module(BlockInput, []) end),
- %% beam_except
- ExceptInput = {?MODULE,[{foo,0}],[],
- [{function,foo,0,2,
- [{label,1},
- {line,loc},
- {func_info,{atom,?MODULE},{atom,foo},0},
- {label,2}|non_proper_list]}],99},
- expect_error(fun() -> beam_except:module(ExceptInput, []) end),
-
%% beam_jump
JumpInput = BlockInput,
expect_error(fun() -> beam_jump:module(JumpInput, []) end),
@@ -283,14 +274,34 @@ silly_coverage(Config) when is_list(Config) ->
bad_ssa_lint_input() ->
{b_module,#{},t,
- [{foobar,1},{module_info,0},{module_info,1}],
+ [{a,1},{b,1},{c,1},{module_info,0},{module_info,1}],
[],
[{b_function,
- #{func_info => {t,foobar,1},location => {"t.erl",4}},
+ #{func_info => {t,a,1},location => {"t.erl",4}},
[{b_var,0}],
#{0 => {b_blk,#{},[],{b_ret,#{},{b_var,'@undefined_var'}}}},
3},
{b_function,
+ #{func_info => {t,b,1},location => {"t.erl",5}},
+ [{b_var,0}],
+ #{0 =>
+ {b_blk,#{},
+ [{b_set,#{},{b_var,'@first_var'},first_op,[]},
+ {b_set,#{},{b_var,'@second_var'},second_op,[]},
+ {b_set,#{},{b_var,'@ret'},succeeded,[{b_var,'@first_var'}]}],
+ {b_ret,#{},{b_var,'@ret'}}}},
+ 3},
+ {b_function,
+ #{func_info => {t,c,1},location => {"t.erl",6}},
+ [{b_var,0}],
+ #{0 =>
+ {b_blk,#{},
+ [{b_set,#{},{b_var,'@first_var'},first_op,[]},
+ {b_set,#{},{b_var,'@ret'},succeeded,[{b_var,'@first_var'}]},
+ {b_set,#{},{b_var,'@second_var'},second_op,[]}],
+ {b_ret,#{},{b_var,'@ret'}}}},
+ 3},
+ {b_function,
#{func_info => {t,module_info,0}},
[],
#{0 =>
diff --git a/lib/compiler/test/property_test/beam_types_prop.erl b/lib/compiler/test/property_test/beam_types_prop.erl
new file mode 100644
index 0000000000..8199d1bd5a
--- /dev/null
+++ b/lib/compiler/test/property_test/beam_types_prop.erl
@@ -0,0 +1,228 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2019. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(beam_types_prop).
+
+-compile([export_all, nowarn_export_all]).
+
+%% This module only supports proper, as we don't have an eqc license to test
+%% with.
+
+-proptest([proper]).
+
+-ifdef(PROPER).
+
+-include_lib("compiler/src/beam_types.hrl").
+
+-include_lib("proper/include/proper.hrl").
+-define(MOD_eqc,proper).
+
+%% The default repetitions of 100 is a bit too low to reliably cover all type
+%% combinations, so we crank it up a bit.
+-define(REPETITIONS, 1000).
+
+absorption() ->
+ numtests(?REPETITIONS, absorption_1()).
+
+absorption_1() ->
+ ?FORALL({TypeA, TypeB},
+ ?LET(TypeA, type(),
+ ?LET(TypeB, type(), {TypeA, TypeB})),
+ absorption_check(TypeA, TypeB)).
+
+absorption_check(A, B) ->
+ %% a ∨ (a ∧ b) = a,
+ A = join(A, meet(A, B)),
+
+ %% a ∧ (a ∨ b) = a.
+ A = meet(A, join(A, B)),
+
+ true.
+
+associativity() ->
+ numtests(?REPETITIONS, associativity_1()).
+
+associativity_1() ->
+ ?FORALL({TypeA, TypeB, TypeC},
+ ?LET(TypeA, type(),
+ ?LET(TypeB, type(),
+ ?LET(TypeC, type(), {TypeA, TypeB, TypeC}))),
+ associativity_check(TypeA, TypeB, TypeC)).
+
+associativity_check(A, B, C) ->
+ %% a ∨ (b ∨ c) = (a ∨ b) ∨ c,
+ LHS_Join = join(A, join(B, C)),
+ RHS_Join = join(join(A, B), C),
+ LHS_Join = RHS_Join,
+
+ %% a ∧ (b ∧ c) = (a ∧ b) ∧ c.
+ LHS_Meet = meet(A, meet(B, C)),
+ RHS_Meet = meet(meet(A, B), C),
+ LHS_Meet = RHS_Meet,
+
+ true.
+
+commutativity() ->
+ numtests(?REPETITIONS, commutativity_1()).
+
+commutativity_1() ->
+ ?FORALL({TypeA, TypeB},
+ ?LET(TypeA, type(),
+ ?LET(TypeB, type(), {TypeA, TypeB})),
+ commutativity_check(TypeA, TypeB)).
+
+commutativity_check(A, B) ->
+ %% a ∨ b = b ∨ a,
+ true = join(A, B) =:= join(B, A),
+
+ %% a ∧ b = b ∧ a.
+ true = meet(A, B) =:= meet(B, A),
+
+ true.
+
+idempotence() ->
+ numtests(?REPETITIONS, idempotence_1()).
+
+idempotence_1() ->
+ ?FORALL(Type, type(), idempotence_check(Type)).
+
+idempotence_check(Type) ->
+ %% a ∨ a = a,
+ Type = join(Type, Type),
+
+ %% a ∧ a = a.
+ Type = meet(Type, Type),
+
+ true.
+
+identity() ->
+ ?FORALL(Type, type(), identity_check(Type)).
+
+identity_check(Type) ->
+ %% a ∨ [bottom element] = a,
+ Type = join(Type, none),
+
+ %% a ∧ [top element] = a.
+ Type = meet(Type, any),
+
+ true.
+
+meet(A, B) -> beam_types:meet(A, B).
+join(A, B) -> beam_types:join(A, B).
+
+%%%
+%%% Generators
+%%%
+
+type() ->
+ type(0).
+
+type(Depth) ->
+ oneof(nested_types(Depth) ++
+ numerical_types() ++
+ list_types() ++
+ other_types()).
+
+other_types() ->
+ [any,
+ gen_atom(),
+ gen_binary(),
+ none].
+
+list_types() ->
+ [cons, list, nil].
+
+numerical_types() ->
+ [gen_integer(), float, number].
+
+nested_types(Depth) when Depth >= 3 -> [none];
+nested_types(Depth) -> [#t_map{}, gen_union(Depth + 1), gen_tuple(Depth + 1)].
+
+gen_atom() ->
+ ?LET(Size, range(0, ?ATOM_SET_SIZE),
+ case Size of
+ 0 ->
+ #t_atom{};
+ _ ->
+ ?LET(Set, sized_list(Size, gen_atom_val()),
+ begin
+ #t_atom{elements=ordsets:from_list(Set)}
+ end)
+ end).
+
+gen_atom_val() ->
+ ?LET(N, range($0, $~), list_to_atom([N])).
+
+gen_binary() ->
+ ?SHRINK(#t_bitstring{unit=range(1, 128)}, [#t_bitstring{unit=1}]).
+
+gen_integer() ->
+ oneof([gen_integer_bounded(), #t_integer{}]).
+
+gen_integer_bounded() ->
+ ?LET({A, B}, {integer(), integer()},
+ begin
+ #t_integer{elements={min(A,B), max(A,B)}}
+ end).
+
+gen_tuple(Depth) ->
+ ?SIZED(Size,
+ ?LET({Exact, Elements}, {boolean(), gen_tuple_elements(Size, Depth)},
+ begin
+ #t_tuple{exact=Exact,
+ size=Size,
+ elements=Elements}
+ end)).
+
+gen_union(Depth) ->
+ ?LAZY(oneof([gen_wide_union(Depth), gen_tuple_union(Depth)])).
+
+gen_wide_union(Depth) ->
+ ?LET({A, B, C, D}, {oneof(nested_types(Depth)),
+ oneof(numerical_types()),
+ oneof(list_types()),
+ oneof(other_types())},
+ begin
+ T0 = join(A, B),
+ T1 = join(T0, C),
+ join(T1, D)
+ end).
+
+gen_tuple_union(Depth) ->
+ ?SIZED(Size,
+ ?LET(Tuples, sized_list(Size, gen_tuple(Depth)),
+ lists:foldl(fun join/2, none, Tuples))).
+
+gen_tuple_elements(Size, Depth) ->
+ ?LET(Types, sized_list(rand:uniform(Size div 4 + 1), gen_element(Depth)),
+ maps:from_list([{rand:uniform(Size), T} || T <- Types])).
+
+gen_element(Depth) ->
+ ?LAZY(?SUCHTHAT(Type, type(Depth),
+ case Type of
+ any -> false;
+ none -> false;
+ _ -> true
+ end)).
+
+sized_list(0, _Gen) -> [];
+sized_list(N, Gen) -> [Gen | sized_list(N - 1, Gen)].
+
+-endif.
diff --git a/lib/compiler/test/test_lib.erl b/lib/compiler/test/test_lib.erl
index 34410e4b2a..4b68e663cd 100644
--- a/lib/compiler/test/test_lib.erl
+++ b/lib/compiler/test/test_lib.erl
@@ -79,9 +79,11 @@ opt_opts(Mod) ->
(no_put_tuple2) -> true;
(no_recv_opt) -> true;
(no_share_opt) -> true;
+ (no_shared_fun_wrappers) -> true;
(no_ssa_float) -> true;
(no_ssa_opt) -> true;
(no_stack_trimming) -> true;
+ (no_swap) -> true;
(no_type_opt) -> true;
(_) -> false
end, Opts).