diff options
Diffstat (limited to 'lib/compiler/test')
-rw-r--r-- | lib/compiler/test/bs_match_SUITE.erl | 50 | ||||
-rw-r--r-- | lib/compiler/test/compile_SUITE.erl | 44 | ||||
-rw-r--r-- | lib/compiler/test/guard_SUITE.erl | 79 | ||||
-rw-r--r-- | lib/compiler/test/map_SUITE.erl | 61 |
4 files changed, 225 insertions, 9 deletions
diff --git a/lib/compiler/test/bs_match_SUITE.erl b/lib/compiler/test/bs_match_SUITE.erl index 6e138b0a43..7fb0a16540 100644 --- a/lib/compiler/test/bs_match_SUITE.erl +++ b/lib/compiler/test/bs_match_SUITE.erl @@ -36,7 +36,8 @@ match_string/1,zero_width/1,bad_size/1,haystack/1, cover_beam_bool/1,matched_out_size/1,follow_fail_branch/1, no_partition/1,calling_a_binary/1,binary_in_map/1, - match_string_opt/1]). + match_string_opt/1,map_and_binary/1, + unsafe_branch_caching/1]). -export([coverage_id/1,coverage_external_ignore/2]). @@ -62,7 +63,8 @@ groups() -> otp_7498,match_string,zero_width,bad_size,haystack, cover_beam_bool,matched_out_size,follow_fail_branch, no_partition,calling_a_binary,binary_in_map, - match_string_opt]}]. + match_string_opt,map_and_binary, + unsafe_branch_caching]}]. init_per_suite(Config) -> @@ -1225,6 +1227,50 @@ match_string_opt(Config) when is_list(Config) -> do_match_string_opt({<<1>>,{v,V}}=T) -> {x,V,T}. +%% If 'bin_opt_info' was given the warning would lack filename +%% and line number. + +map_and_binary(_Config) -> + {<<"10">>,<<"37">>,<<"am">>} = do_map_and_binary(<<"10:37am">>), + Map1 = #{time => "noon"}, + {ok,Map1} = do_map_and_binary(Map1), + Map2 = #{hour => 8, min => 42}, + {8,42,Map2} = do_map_and_binary(Map2), + ok. + +do_map_and_binary(<<Hour:2/bytes, $:, Min:2/bytes, Rest/binary>>) -> + {Hour, Min, Rest}; +do_map_and_binary(#{time := _} = T) -> + {ok, T}; +do_map_and_binary(#{hour := Hour, min := Min} = T) -> + {Hour, Min, T}. + +%% Unsafe caching of branch outcomes in beam_bsm would cause the +%% delayed creation of sub-binaries optimization to be applied even +%% when it was unsafe. + +unsafe_branch_caching(_Config) -> + <<>> = do_unsafe_branch_caching(<<42,1>>), + <<>> = do_unsafe_branch_caching(<<42,2>>), + <<>> = do_unsafe_branch_caching(<<42,3>>), + <<17,18>> = do_unsafe_branch_caching(<<42,3,17,18>>), + <<>> = do_unsafe_branch_caching(<<1,3,42,2>>), + + ok. + +do_unsafe_branch_caching(<<Code/integer, Bin/binary>>) -> + <<C1/integer, B1/binary>> = Bin, + case C1 of + X when X =:= 1 orelse X =:= 2 -> + Bin2 = <<>>; + _ -> + Bin2 = B1 + end, + case Code of + 1 -> do_unsafe_branch_caching(Bin2); + _ -> Bin2 + end. + check(F, R) -> R = F(). diff --git a/lib/compiler/test/compile_SUITE.erl b/lib/compiler/test/compile_SUITE.erl index df401ccc2b..cbdd9ce8cd 100644 --- a/lib/compiler/test/compile_SUITE.erl +++ b/lib/compiler/test/compile_SUITE.erl @@ -31,7 +31,9 @@ other_output/1, encrypted_abstr/1, bad_record_use1/1, bad_record_use2/1, strict_record/1, missing_testheap/1, cover/1, env/1, core/1, asm/1, - sys_pre_attributes/1, dialyzer/1]). + sys_pre_attributes/1, dialyzer/1, + warnings/1 + ]). -export([init/3]). @@ -48,7 +50,7 @@ all() -> other_output, encrypted_abstr, {group, bad_record_use}, strict_record, missing_testheap, cover, env, core, asm, - sys_pre_attributes, dialyzer]. + sys_pre_attributes, dialyzer, warnings]. groups() -> [{bad_record_use, [], @@ -895,6 +897,44 @@ dialyzer(Config) -> [{a,b,c}] = M:M(), ok. + +%% Test that warnings contain filenames and line numbers. +warnings(_Config) -> + TestDir = filename:dirname(code:which(?MODULE)), + Files = filelib:wildcard(filename:join(TestDir, "*.erl")), + test_lib:p_run(fun do_warnings/1, Files). + +do_warnings(F) -> + {ok,_,_,Ws} = compile:file(F, [binary,bin_opt_info,return]), + do_warnings_1(Ws, F). + +do_warnings_1([{"no_file",Ws}|_], F) -> + io:format("~s:\nMissing file for warnings: ~p\n", + [F,Ws]), + error; +do_warnings_1([{Name,Ws}|T], F) -> + case filename:extension(Name) of + ".erl" -> + do_warnings_2(Ws, T, F); + _ -> + io:format("~s:\nNo .erl extension\n", [F]), + error + end; +do_warnings_1([], _) -> ok. + +do_warnings_2([{Int,_,_}=W|T], Next, F) -> + if + is_integer(Int) -> + do_warnings_2(T, Next, F); + true -> + io:format("~s:\nMissing line number: ~p\n", + [F,W]), + error + end; +do_warnings_2([], Next, F) -> + do_warnings_1(Next, F). + + %%% %%% Utilities. %%% diff --git a/lib/compiler/test/guard_SUITE.erl b/lib/compiler/test/guard_SUITE.erl index b3b67155b3..47eb1ba78b 100644 --- a/lib/compiler/test/guard_SUITE.erl +++ b/lib/compiler/test/guard_SUITE.erl @@ -34,7 +34,8 @@ tricky/1,rel_ops/1,rel_op_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]). + bad_constants/1,bad_guards/1,scotland/1, + guard_in_catch/1]). suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -52,7 +53,7 @@ groups() -> rel_ops,rel_op_combinations, literal_type_tests,basic_andalso_orelse,traverse_dcd, check_qlc_hrl,andalso_semi,t_tuple_size,binary_part, - bad_constants,bad_guards]}]. + bad_constants,bad_guards,scotland,guard_in_catch]}]. init_per_suite(Config) -> Config. @@ -1831,6 +1832,80 @@ bad_guards_2(M, [_]) when M#{a := 0, b => 0}, map_size(M) -> bad_guards_3(M, [_]) when is_map(M) andalso M#{a := 0, b => 0}, length(M) -> ok. +%% beam_bool would remove the initialization of {y,0}. +%% (Thanks to Thomas Arts and QuickCheck.) + +scotland(_Config) -> + million = do_scotland(placed), + {'EXIT',{{badmatch,placed},_}} = (catch do_scotland(false)), + {'EXIT',{{badmatch,placed},_}} = (catch do_scotland(true)), + {'EXIT',{{badmatch,placed},_}} = (catch do_scotland(echo)), + ok. + +do_scotland(Echo) -> + found(case Echo of + Echo when true; Echo, Echo, Echo -> + Echo; + echo -> + [] + end, + Echo = placed). + +found(_, _) -> million. + +%% Building maps in a guard in a 'catch' would crash v3_codegen. + +guard_in_catch(_Config) -> + {'EXIT',{if_clause,_}} = do_guard_in_catch_map_1(#{}), + {'EXIT',{if_clause,_}} = do_guard_in_catch_map_1(#{a=>b}), + {'EXIT',{if_clause,_}} = do_guard_in_catch_map_1(atom), + + {'EXIT',{if_clause,_}} = do_guard_in_catch_map_2(#{}), + {'EXIT',{if_clause,_}} = do_guard_in_catch_map_2(#{a=>b}), + {'EXIT',{if_clause,_}} = do_guard_in_catch_map_2(atom), + + {'EXIT',{if_clause,_}} = (catch do_guard_in_catch_map_3()), + + {'EXIT',{if_clause,_}} = do_guard_in_catch_bin(42), + {'EXIT',{if_clause,_}} = do_guard_in_catch_bin(<<1,2,3>>), + {'EXIT',{if_clause,_}} = do_guard_in_catch_bin(atom), + {'EXIT',{if_clause,_}} = do_guard_in_catch_bin(#{}), + + ok. + +do_guard_in_catch_map_1(From) -> + catch + if + From#{[] => sufficient} -> + saint + end. + +do_guard_in_catch_map_2(From) -> + catch + if + From#{From => sufficient} -> + saint + end. + +do_guard_in_catch_map_3() -> + try + if [] -> solo end + catch + Friendly when Friendly#{0 => []} -> minutes + after + membership + end. + +do_guard_in_catch_bin(From) -> + %% Would not crash v3_codegen, but there would be an unnecessary + %% 'move' to a Y register. + catch + if + <<From:32>> -> + saint + end. + + %% Call this function to turn off constant propagation. id(I) -> I. diff --git a/lib/compiler/test/map_SUITE.erl b/lib/compiler/test/map_SUITE.erl index abc12a359d..cff3b5deb4 100644 --- a/lib/compiler/test/map_SUITE.erl +++ b/lib/compiler/test/map_SUITE.erl @@ -63,7 +63,10 @@ %% errors in 17.0-rc1 t_update_values/1, t_expand_map_update/1, - t_export/1 + t_export/1, + + %% errors in 18 + t_register_corruption/1 ]). suite() -> []. @@ -108,11 +111,13 @@ all() -> t_build_and_match_nil, t_build_and_match_structure, - %% errors in 17.0-rc1 t_update_values, t_expand_map_update, - t_export + t_export, + + %% errors in 18 + t_register_corruption ]. groups() -> []. @@ -878,6 +883,9 @@ t_update_map_expressions(Config) when is_list(Config) -> %% Error cases. {'EXIT',{{badmap,<<>>},_}} = (catch (id(<<>>))#{ a := 42, b => 2 }), {'EXIT',{{badmap,[]},_}} = (catch (id([]))#{ a := 42, b => 2 }), + {'EXIT',{{badmap,_},_}} = + (catch (fun t_update_map_expressions/1)#{u => 42}), + ok. @@ -1827,6 +1835,53 @@ map_guard_sequence_mixed(K1,K2,M) -> #{ K1 := 1, c := 6, K2 := 8, h := 3} -> 6 end. +%% register corruption discovered in 18 due to +%% get_map_elements might destroys registers when fail-label is taken. +%% Only seen when patterns have two targets, +%% specifically: we copy one register, and then jump. +%% {test,is_map,{f,5},[{x,1}]}. +%% +%% {get_map_elements,{f,7},{x,1},{list,[{atom,a},{x,1},{atom,b},{x,2}]}}. +%% %% if 'a' exists but not 'b' {x,1} is overwritten, jump {f,7} +%% +%% {move,{integer,1},{x,0}}. +%% {call_only,3,{f,10}}. +%% +%% {label,7}. +%% {get_map_elements,{f,8},{x,1},{list,[{atom,b},{x,2}]}}. +%% %% {x,1} (src) is now corrupt +%% +%% {move,{x,0},{x,1}}. +%% {move,{integer,2},{x,0}}. +%% {call_only,3,{f,10}}. +%% +%% Only happens in beam_block opt_move pass with two destinations. + +t_register_corruption(Config) when is_list(Config) -> + M = #{a=> <<"value">>, c=>3}, + {3,wanted,<<"value">>} = register_corruption_bar(M,wanted), + {3,wanted,<<"value">>} = register_corruption_foo(wanted,M), + ok. + +register_corruption_foo(A,#{a := V1, b := V2}) -> + register_corruption_dummy_call(1,V1,V2); +register_corruption_foo(A,#{b := V}) -> + register_corruption_dummy_call(2,A,V); +register_corruption_foo(A,#{a := V}) -> + register_corruption_dummy_call(3,A,V). + +register_corruption_bar(M,A) -> + case M of + #{a := V1, b := V2} -> + register_corruption_dummy_call(1,V1,V2); + #{b := V} -> + register_corruption_dummy_call(2,A,V); + #{a := V} -> + register_corruption_dummy_call(3,A,V) + end. + + +register_corruption_dummy_call(A,B,C) -> {A,B,C}. t_frequency_table(Config) when is_list(Config) -> |