Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions lib/compiler/src/beam_ssa_alias.erl
Original file line number Diff line number Diff line change
Expand Up @@ -1215,10 +1215,13 @@ aa_bif(Dst, tl, [Pair], _Types, SS, _AAS) ->
aa_pair_extraction(Dst, Pair, tl, SS);
aa_bif(Dst, map_get, [_Key,Map], _Types, SS, AAS) ->
aa_map_extraction(Dst, Map, SS, AAS);
aa_bif(Dst, binary_part, Args, _Types, SS0, _AAS) ->
%% bif:binary_part/{2,3} is the only guard bif which could lead to
%% aliasing, it extracts a sub-binary with a reference to its
%% argument.
aa_bif(Dst, Bif, Args, _Types, SS0, _AAS) when Bif =:= binary_part;
Bif =:= min;
Bif =:= max ->
%% bif:binary_part/{2,3}, min/2, max/2 are guard bifs that could lead to
%% aliasing. binary_part extracts a sub-binary with a reference to its
%% argument. min and max return one of their arguments unchanged, so the
%% result aliases one of them.
SS = beam_ssa_ss:add_var(Dst, unique, SS0),
aa_set_aliased([Dst|Args], SS);
aa_bif(Dst, Bif, Args, Types, SS, _AAS) ->
Expand Down
2 changes: 1 addition & 1 deletion lib/compiler/src/beam_ssa_lint.erl
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,7 @@ format_error_1({unknown_phi_variable, Name, {From, _To}, I}) ->
[format_var(Name), format_instr(I), From]);
format_error_1({unknown_block, Label, I}) ->
io_lib:format("Unknown block ~p referenced in ~ts",
[Label, I]);
[Label, format_instr(I)]);
format_error_1({unknown_variable, Name, I}) ->
io_lib:format("Unbound variable ~ts used in ~ts",
[format_var(Name), format_instr(I)]);
Expand Down
13 changes: 12 additions & 1 deletion lib/compiler/src/beam_ssa_pp.erl
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,8 @@ format_type(identifier) ->
"identifier()";
format_type(none) ->
"none()";
format_type(#t_union{atom=A,list=L,number=N,tuple_set=Ts,other=O}) ->
format_type(#t_union{atom=A,list=L,number=N,tuple_set=Ts,
native_record_set=Rs,other=O}) ->
Es = case A of
none -> [];
_ -> [format_type(A)]
Expand All @@ -410,6 +411,10 @@ format_type(#t_union{atom=A,list=L,number=N,tuple_set=Ts,other=O}) ->
none -> [];
_ -> [format_tuple_set(Ts)]
end
++ case Rs of
none -> [];
_ -> [format_native_record_set(Rs)]
end
++ case O of
none -> [];
_ -> [format_type(O)]
Expand Down Expand Up @@ -443,3 +448,9 @@ format_tuple_set(RecordSet) ->
format_tuple_set_1({{Arity,Key},#t_tuple{size=Arity,elements=Elems}=Tuple}) ->
false = none =:= beam_types:meet(Key, map_get(1, Elems)), % Assertion
format_type(Tuple).

format_native_record_set(#t_record{}=R) ->
format_type(R);
format_native_record_set(RecordSet) ->
string:join([format_type(R) || R <- ordsets:to_list(RecordSet)],
" | ").
2 changes: 1 addition & 1 deletion lib/compiler/src/beam_ssa_type.erl
Original file line number Diff line number Diff line change
Expand Up @@ -1875,7 +1875,7 @@ eval_bif_1(#b_set{args=Args}=I, Op, Ts, Ds) ->
false ->
I
end;
[T,#t_integer{elements={1,1}}] when Op =:= '*'; Op =:= 'div' ->
[T,#t_integer{elements={1,1}}] when Op =:= '*' ->
case beam_types:is_numerical_type(T) of
true ->
#b_set{args=[Result,_]} = I,
Expand Down
8 changes: 6 additions & 2 deletions lib/compiler/src/cerl.erl
Original file line number Diff line number Diff line change
Expand Up @@ -3894,11 +3894,11 @@ subtrees(T) ->
tuple ->
[tuple_es(T)];
record ->
[record_es(T)];
[[record_arg(T)], [record_id(T)], record_es(T)];
record_pair ->
[[record_pair_key(T)], [record_pair_val(T)]];
map ->
[map_es(T)];
[[map_arg(T)], map_es(T)];
map_pair ->
[[map_pair_op(T)],[map_pair_key(T)],[map_pair_val(T)]];
'let' ->
Expand Down Expand Up @@ -4030,6 +4030,10 @@ ann_make_tree(As, alias, [[V], [P]]) -> ann_c_alias(As, V, P);
ann_make_tree(As, 'fun', [Vs, [B]]) -> ann_c_fun(As, Vs, B);
ann_make_tree(As, 'receive', [Cs, [T], [A]]) ->
ann_c_receive(As, Cs, T, A);
ann_make_tree(As, record, [[A], [Id], Es]) ->
ann_c_record(As, A, Id, Es);
ann_make_tree(As, record_pair, [[K],[V]]) ->
ann_c_record_pair(As, K, V);
ann_make_tree(As, 'try', [[E], Vs, [B], Evs, [H]]) ->
ann_c_try(As, E, Vs, B, Evs, H);
ann_make_tree(As, 'catch', [[B]]) -> ann_c_catch(As, B);
Expand Down
16 changes: 14 additions & 2 deletions lib/compiler/test/beam_ssa_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
mapfoldl/0,mapfoldl/1,
grab_bag/1,redundant_br/1,
coverage/1,normalize/1,
trycatch/1,gh_6599/1]).
trycatch/1,gh_6599/1,cs_div/1]).

-import_record(beam_ssa, [b_set, b_var, b_literal]).

Expand All @@ -57,7 +57,8 @@ groups() ->
coverage,
normalize,
trycatch,
gh_6599
gh_6599,
cs_div
]}].

init_per_suite(Config) ->
Expand Down Expand Up @@ -1563,6 +1564,17 @@ gh_6599_7(X, Y) ->
ok
end.

cs_div(_Config) ->
?assertError(badarith, cs_div_1(id(2.0))),
2 = cs_div_2(id(2)),
ok.

cs_div_1(X) when is_number(X) ->
X div 1.

cs_div_2(X) when is_integer(X) ->
X div 1.


%% The identity function.
id(I) -> I.
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
update_record0/0, fc/0, track_update_record/1,
gh8124_a/0, gh8124_b/0, tuple_set_a/1, tuple_set_b/0,
failure_to_patch_list/0, erierl1208/0, gh_9903/0,
gh10367/0]).
gh10367/0, cs_min_max/2]).
-record(r, {a=0,b=0,c=0,tot=0}).
-record(r1, {a}).
-record(r2, {b}).
Expand Down Expand Up @@ -334,3 +334,10 @@ gh10367() ->
Expected = P2#r4{a = a},
timer:sleep(0),
Expected = gh10367_update([P1, P2]).

cs_min_max(X,Y) ->
%ssa% (X, Y) when post_ssa_opt ->
%ssa% _ = update_record(reuse, ...).
A = #r{a=X,b=1}, B = #r{a=Y,b=2},
Min = min(A,B), Updated = Min#r{a=updated},
{A,B,Updated}.
3 changes: 3 additions & 0 deletions lib/compiler/test/compile_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -2119,6 +2119,9 @@ types_pp(Config) when is_list(Config) ->
"'foo' | nonempty_list(1..3) | number(3, 7) |"
" {'tag0', 1, 2} | {'tag1', 3, 4} | bitstring(8)"},
{make_bitstring, "bitstring(8)"},
{make_native_record_set,
"#types_pp:rec_a{present:x=1} |"
" #types_pp:rec_b{present:y=2.0}"},
{make_none, "none()"}],
lists:foreach(fun({FunName, Expected}) ->
Actual = map_get(atom_to_list(FunName), ResultTypes),
Expand Down
32 changes: 32 additions & 0 deletions lib/compiler/test/compile_SUITE_data/types_pp.erl
Original file line number Diff line number Diff line change
@@ -1,3 +1,25 @@
%%
%% %CopyrightBegin%
%%
%% SPDX-License-Identifier: Apache-2.0
%%
%% Copyright Ericsson AB 2021-2026. 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(types_pp).

-export([doit/0]).
Expand Down Expand Up @@ -103,6 +125,15 @@ make_union() ->
make_bitstring() ->
<<1, 2, 3>>.

-record #rec_a{x=0}.
-record #rec_b{y=0.0}.

make_native_record_set() ->
case ext:f() of
0 -> #rec_a{x=1};
_ -> #rec_b{y=2.0}
end.

doit() ->
{make_number(ext:f(), ext:f()), make_atom(),
make_float(),
Expand All @@ -121,6 +152,7 @@ doit() ->
make_inexact_tuple(ext:f()),
make_union(),
make_bitstring(),
make_native_record_set(),
make_none()
}.

Expand Down
15 changes: 13 additions & 2 deletions lib/compiler/test/map_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,8 @@
t_cse_assoc/1,
shared_key_tuples/1,
map_aliases/1,
coverage/1
coverage/1,
cerl_update_tree/1
]).

-define(BADMAP(V, F, Args, Expr),
Expand Down Expand Up @@ -177,7 +178,8 @@ all() ->
t_cse_assoc,
shared_key_tuples,
map_aliases,
coverage
coverage,
cerl_update_tree
].

groups() -> [].
Expand Down Expand Up @@ -2690,5 +2692,14 @@ do_badmap(Test) ->
[],{a,b,c},[a,b],atom,10.0,42,(1 bsl 65) + 3],
[Test(T) || T <- Terms].

%% cerl:update_tree/2 did not preserve the map argument for update maps.

cerl_update_tree(_Config) ->
V = cerl:c_var('M'),
Pair = cerl:c_map_pair(cerl:c_atom(k), cerl:c_int(1)),
Upd = cerl:ann_c_map([], V, [Pair]),
Upd = cerl:update_tree(Upd, cerl:subtrees(Upd)),
ok.

%% Use this function to avoid compile-time evaluation of an expression.
id(I) -> I.
15 changes: 13 additions & 2 deletions lib/compiler/test/native_record_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
init_per_group/2,end_per_group/2,
local_basic/1,local_updates/1,non_atomic_names/1,
external_records/1,any_record/1,
matching/1,is_record_bif/1,type_opts/1]).
matching/1,is_record_bif/1,type_opts/1,
cerl_update_tree/1]).

%% Unexported records.
-record #empty{}.
Expand Down Expand Up @@ -68,7 +69,8 @@ groups() ->
external_records,
matching,
is_record_bif,
type_opts
type_opts,
cerl_update_tree
]}].

init_per_suite(Config) ->
Expand Down Expand Up @@ -798,6 +800,15 @@ type_opt_ccc(A, B) ->
ok
end.

%% cerl:update_tree/2 did not handle record and record_pair nodes.

cerl_update_tree(_Config) ->
Pair = cerl:c_record_pair(cerl:c_atom(f), cerl:c_int(1)),
Pair = cerl:update_tree(Pair, cerl:subtrees(Pair)),
R = cerl:c_record(cerl:c_atom(rec), [Pair]),
R = cerl:update_tree(R, cerl:subtrees(R)),
ok.

%%% Common utilities.

id(I) ->
Expand Down
65 changes: 44 additions & 21 deletions lib/stdlib/src/erl_lint.erl
Original file line number Diff line number Diff line change
Expand Up @@ -1419,27 +1419,28 @@ disallowed_compile_flags(Forms, St0) ->
post_traversal_check(Forms, St0) ->
St1 = check_behaviour(St0),
St2 = check_deprecated(Forms, St1),
St3 = check_imports(Forms, St2),
St4 = check_inlines(Forms, St3),
St5 = check_undefined_functions(St4),
St6 = check_unused_functions(Forms, St5),
St7 = check_bif_clashes(Forms, St6),
St8 = check_specs_without_function(St7),
St9 = check_functions_without_spec(Forms, St8),
StA = check_undefined_types(St9),
StB = check_unused_types(Forms, StA),
StC = check_untyped_records(Forms, StB),
StD = check_on_load(StC),
StE = check_export_record(Forms, StD),
StF = check_unused_records(Forms, StE),
StG = check_native_records_header(Forms, StF),
StH = check_local_opaque_types(StG),
StI = check_dialyzer_attribute(Forms, StH),
StJ = check_callback_information(StI),
StK = check_nifs(Forms, StJ),
StL = check_unexported_functions(StK),
StM = check_removed(Forms, StL),
check_unsafe(Forms, StM).
St3 = check_deprecated_type_callback(Forms, St2),
St4 = check_imports(Forms, St3),
St5 = check_inlines(Forms, St4),
St6 = check_undefined_functions(St5),
St7 = check_unused_functions(Forms, St6),
St8 = check_bif_clashes(Forms, St7),
St9 = check_specs_without_function(St8),
StA = check_functions_without_spec(Forms, St9),
StB = check_undefined_types(StA),
StC = check_unused_types(Forms, StB),
StD = check_untyped_records(Forms, StC),
StE = check_on_load(StD),
StF = check_export_record(Forms, StE),
StG = check_unused_records(Forms, StF),
StH = check_native_records_header(Forms, StG),
StI = check_local_opaque_types(StH),
StJ = check_dialyzer_attribute(Forms, StI),
StK = check_callback_information(StJ),
StL = check_nifs(Forms, StK),
StM = check_unexported_functions(StL),
StN = check_removed(Forms, StM),
check_unsafe(Forms, StN).

%% check_behaviour(State0) -> State
%% Check that the behaviour attribute is valid.
Expand Down Expand Up @@ -1636,6 +1637,28 @@ deprecated_desc([Char | Str]) when is_integer(Char) -> deprecated_desc(Str);
deprecated_desc([]) -> true;
deprecated_desc(_) -> false.

%% check_deprecated_type_callback(Forms, State0) -> State

check_deprecated_type_callback(Forms, St0) ->
Bad = [{E,Anno} || {attribute, Anno, Attr, Depr} <- Forms,
(Attr =:= deprecated_type orelse
Attr =:= deprecated_callback),
D <- lists:flatten([Depr]),
E <- depr_cat_flag(D)],
foldl(fun ({E,Anno}, St1) ->
add_error(Anno, E, St1)
end, St0, Bad).

depr_cat_flag({_F, _A, Flg}=D) ->
case deprecated_flag(Flg) of
false -> [{invalid_deprecated,D}];
true -> []
end;
depr_cat_flag({_F, _A}) ->
[];
depr_cat_flag(D) ->
[{invalid_deprecated,D}].

%% check_removed(Forms, State0) -> State

check_removed(Forms, St0) ->
Expand Down
Loading
Loading