aboutsummaryrefslogtreecommitdiffstats
path: root/lib/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'lib/compiler')
-rw-r--r--lib/compiler/doc/src/notes.xml76
-rw-r--r--lib/compiler/src/beam_bool.erl75
-rw-r--r--lib/compiler/src/core_lib.erl9
-rw-r--r--lib/compiler/src/sys_core_fold.erl213
-rw-r--r--lib/compiler/src/v3_core.erl112
-rw-r--r--lib/compiler/test/bs_match_SUITE.erl24
-rw-r--r--lib/compiler/test/core_fold_SUITE.erl16
-rw-r--r--lib/compiler/test/fun_SUITE.erl19
-rw-r--r--lib/compiler/test/guard_SUITE.erl18
-rw-r--r--lib/compiler/test/match_SUITE.erl23
-rw-r--r--lib/compiler/test/warnings_SUITE.erl14
-rw-r--r--lib/compiler/vsn.mk2
12 files changed, 445 insertions, 156 deletions
diff --git a/lib/compiler/doc/src/notes.xml b/lib/compiler/doc/src/notes.xml
index a0f2e617cb..84ebd2f210 100644
--- a/lib/compiler/doc/src/notes.xml
+++ b/lib/compiler/doc/src/notes.xml
@@ -31,6 +31,74 @@
<p>This document describes the changes made to the Compiler
application.</p>
+<section><title>Compiler 5.0.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Named funs with the same name and arity could get mixed
+ up with each other.</p>
+ <p>
+ Own Id: OTP-12262</p>
+ </item>
+ <item>
+ <p>
+ Coalesce map keys in dialyzer mode</p>
+ <p>
+ This fixes a regression introduced in commit
+ 805f9c89fc01220bc1bb0f27e1b68fd4eca688ba The problem
+ occurred with compounded map keys compiled with dialyzer
+ option turned on, '+dialyzer'.</p>
+ <p>
+ Reported by: Ivan Uemlianin</p>
+ <p>
+ Own Id: OTP-12347</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Compiler 5.0.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Corrected a bug with incorrect code generation when
+ inlining was turned on.</p>
+ <p>
+ Own Id: OTP-12132</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Compiler 5.0.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ A Dialyzer crash involving analysis of Map types has now
+ been fixed.</p>
+ <p>
+ Own Id: OTP-11947</p>
+ </item>
+ <item>
+ <p>The compiler would fail to compile a file with a
+ latin-1 character in the false branch of an <c>-ifdef</c>
+ or <c>-indef</c>.</p>
+ <p>
+ Own Id: OTP-11987</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Compiler 5.0</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -211,10 +279,10 @@
"hello"}, % add new associations</c></item> <item><c>#{
"hi" := V1, a := V2, b := V3} = M2. % match keys with
values</c></item> </taglist></p>
- <p>
- For information on how to use Maps please see the
- <seealso marker="doc/reference_manual:maps">Reference
- Manual</seealso>.</p>
+ <p>
+ For information on how to use Maps please see Map Expressions in the
+ <seealso marker="doc/reference_manual:expressions#map_expressions">
+ Reference Manual</seealso>.</p>
<p>
The current implementation is without the following
features: <taglist> <item>No variable keys</item>
diff --git a/lib/compiler/src/beam_bool.erl b/lib/compiler/src/beam_bool.erl
index 5a4621dc37..a452d30b61 100644
--- a/lib/compiler/src/beam_bool.erl
+++ b/lib/compiler/src/beam_bool.erl
@@ -126,44 +126,53 @@ bopt_block(Reg, Fail, OldIs, [{block,Bl0}|Acc0], St0) ->
%% There was a reference to a boolean expression
%% from inside a protected block (try/catch), to
%% a boolean expression outside.
- throw:protected_barrier ->
+ throw:protected_barrier ->
failed;
- %% The 'xor' operator was used. We currently don't
- %% find it worthwile to translate 'xor' operators
- %% (the code would be clumsy).
- throw:'xor' ->
+ %% The 'xor' operator was used. We currently don't
+ %% find it worthwile to translate 'xor' operators
+ %% (the code would be clumsy).
+ throw:'xor' ->
failed;
- %% The block does not contain a boolean expression,
- %% but only a call to a guard BIF.
- %% For instance: ... when element(1, T) ->
- throw:not_boolean_expr ->
+ %% The block does not contain a boolean expression,
+ %% but only a call to a guard BIF.
+ %% For instance: ... when element(1, T) ->
+ throw:not_boolean_expr ->
failed;
- %% The block contains a 'move' instruction that could
- %% not be handled.
- throw:move ->
+ %% The block contains a 'move' instruction that could
+ %% not be handled.
+ throw:move ->
failed;
- %% The optimization is not safe. (A register
- %% used by the instructions following the
- %% optimized code is either not assigned a
- %% value at all or assigned a different value.)
- throw:all_registers_not_killed ->
+ %% The optimization is not safe. (A register
+ %% used by the instructions following the
+ %% optimized code is either not assigned a
+ %% value at all or assigned a different value.)
+ throw:all_registers_not_killed ->
failed;
- throw:registers_used ->
+ throw:registers_used ->
failed;
- %% A protected block refered to the value
- %% returned by another protected block,
- %% probably because the Core Erlang code
- %% used nested try/catches in the guard.
- %% (v3_core never produces nested try/catches
- %% in guards, so it must have been another
- %% Core Erlang translator.)
- throw:protected_violation ->
+ %% A protected block refered to the value
+ %% returned by another protected block,
+ %% probably because the Core Erlang code
+ %% used nested try/catches in the guard.
+ %% (v3_core never produces nested try/catches
+ %% in guards, so it must have been another
+ %% Core Erlang translator.)
+ throw:protected_violation ->
+ failed;
+
+ %% Failed to work out the live registers for a GC
+ %% BIF. For example, if the number of live registers
+ %% needed to be 4 because {x,3} was a source register,
+ %% but {x,2} was not known to be initialized, this
+ %% exception would be thrown.
+ throw:gc_bif_alloc_failure ->
failed
+
end
end.
@@ -665,10 +674,16 @@ put_reg_1(V, [], I) -> [{I,V}].
fetch_reg(V, [{I,V}|_]) -> {x,I};
fetch_reg(V, [_|SRs]) -> fetch_reg(V, SRs).
-live_regs(Regs) ->
- foldl(fun ({I,_}, _) ->
- I
- end, -1, Regs)+1.
+live_regs([{_,reserved}|_]) ->
+ %% We are not sure that this register is initialized, so we must
+ %% abort the optimization.
+ throw(gc_bif_alloc_failure);
+live_regs([{I,_}]) ->
+ I+1;
+live_regs([{_,_}|Regs]) ->
+ live_regs(Regs);
+live_regs([]) ->
+ 0.
%%%
diff --git a/lib/compiler/src/core_lib.erl b/lib/compiler/src/core_lib.erl
index 2792fd8fa5..0d95971f91 100644
--- a/lib/compiler/src/core_lib.erl
+++ b/lib/compiler/src/core_lib.erl
@@ -212,6 +212,8 @@ vu_pattern(V, #c_tuple{es=Es}, St) ->
vu_pattern_list(V, Es, St);
vu_pattern(V, #c_binary{segments=Ss}, St) ->
vu_pat_seg_list(V, Ss, St);
+vu_pattern(V, #c_map{es=Es}, St) ->
+ vu_map_pairs(V, Es, St);
vu_pattern(V, #c_alias{var=Var,pat=P}, St0) ->
case vu_pattern(V, Var, St0) of
{true,_}=St1 -> St1;
@@ -234,6 +236,13 @@ vu_pat_seg_list(V, Ss, St) ->
end
end, St, Ss).
+vu_map_pairs(V, [#c_map_pair{val=Pat}|T], St0) ->
+ case vu_pattern(V, Pat, St0) of
+ {true,_}=St -> St;
+ St -> vu_map_pairs(V, T, St)
+ end;
+vu_map_pairs(_, [], St) -> St.
+
-spec vu_var_list(cerl:var_name(), [cerl:c_var()]) -> boolean().
vu_var_list(V, Vs) ->
diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl
index ce40213bad..ed8f609082 100644
--- a/lib/compiler/src/sys_core_fold.erl
+++ b/lib/compiler/src/sys_core_fold.erl
@@ -1229,6 +1229,11 @@ is_non_numeric([H|T]) ->
is_non_numeric(H) andalso is_non_numeric(T);
is_non_numeric(Tuple) when is_tuple(Tuple) ->
is_non_numeric_tuple(Tuple, tuple_size(Tuple));
+is_non_numeric(Map) when is_map(Map) ->
+ %% Note that 17.x and 18.x compare keys in different ways.
+ %% Be very conservative -- require that both keys and values
+ %% are non-numeric.
+ is_non_numeric(maps:to_list(Map));
is_non_numeric(Num) when is_number(Num) ->
false;
is_non_numeric(_) -> true.
@@ -1338,9 +1343,12 @@ eval_element(Call, #c_literal{val=Pos}, #c_var{name=V}, Types)
{ok,#c_tuple{es=Elements}} ->
if
1 =< Pos, Pos =< length(Elements) ->
- case lists:nth(Pos, Elements) of
- #c_alias{var=Alias} -> Alias;
- Res -> Res
+ El = lists:nth(Pos, Elements),
+ try
+ pat_to_expr(El)
+ catch
+ throw:impossible ->
+ Call
end;
true ->
eval_failure(Call, badarg)
@@ -2040,17 +2048,18 @@ case_opt_args([], Cs, _Sub, _LitExpr, Acc) ->
%% Try to expand one argument to several arguments (if tuple/list)
%% or to remove a literal argument.
%%
-case_opt_arg(E0, Sub, Cs, LitExpr) ->
+case_opt_arg(E0, Sub, Cs0, LitExpr) ->
E = maybe_replace_var(E0, Sub),
case cerl:is_data(E) of
false ->
- {error,Cs};
+ {error,Cs0};
true ->
+ Cs = case_opt_nomatch(E, Cs0, LitExpr),
case cerl:data_type(E) of
{atomic,_} ->
- case_opt_lit(E, Cs, LitExpr);
+ case_opt_lit(E, Cs);
_ ->
- case_opt_data(E, Cs, LitExpr)
+ case_opt_data(E, Cs)
end
end.
@@ -2072,19 +2081,67 @@ maybe_replace_var_1(E, #sub{t=Tdb}) ->
false ->
E;
true ->
- cerl_trees:map(fun(C) ->
- case cerl:is_c_alias(C) of
- false -> C;
- true -> cerl:alias_pat(C)
- end
- end, T0)
+ %% The pattern was a tuple. Now we must make sure
+ %% that the elements of the tuple are suitable. In
+ %% particular, we don't want binary or map
+ %% construction here, since that means that the
+ %% binary or map will be constructed in the 'case'
+ %% argument. That is wasteful for binaries. Even
+ %% worse is that any map pattern that use the ':='
+ %% operator will fail when used in map
+ %% construction (only the '=>' operator is allowed
+ %% when constructing a map from scratch).
+ ToData = fun coerce_to_data/1,
+ try
+ cerl_trees:map(ToData, T0)
+ catch
+ throw:impossible ->
+ %% Something unsuitable was found (map or
+ %% or binary). Keep the variable.
+ E
+ end
end;
error ->
E
end.
-%% case_opt_lit(Literal, Clauses0, LitExpr) ->
-%% {ok,[],Clauses} | error
+%% coerce_to_data(Core) -> Core'
+%% Coerce an element originally from a pattern to an data item or or
+%% variable. Throw an 'impossible' exception if non-data Core Erlang
+%% terms such as binary construction or map construction are
+%% encountered.
+
+coerce_to_data(C) ->
+ case cerl:is_c_alias(C) of
+ false ->
+ case cerl:is_data(C) orelse cerl:is_c_var(C) of
+ true -> C;
+ false -> throw(impossible)
+ end;
+ true ->
+ coerce_to_data(cerl:alias_pat(C))
+ end.
+
+%% case_opt_nomatch(E, Clauses, LitExpr) -> Clauses'
+%% Remove all clauses that cannot possibly match.
+
+case_opt_nomatch(E, [{[P|_],C,_,_}=Current|Cs], LitExpr) ->
+ case cerl_clauses:match(P, E) of
+ none ->
+ %% The pattern will not match the case expression. Remove
+ %% the clause. Unless the entire case expression is a
+ %% literal, also emit a warning.
+ case LitExpr of
+ false -> add_warning(C, nomatch_clause_type);
+ true -> ok
+ end,
+ case_opt_nomatch(E, Cs, LitExpr);
+ _ ->
+ [Current|case_opt_nomatch(E, Cs, LitExpr)]
+ end;
+case_opt_nomatch(_, [], _) -> [].
+
+%% case_opt_lit(Literal, Clauses0) -> {ok,[],Clauses} | error
%% The current part of the case expression is a literal. That
%% means that we will know at compile-time whether a clause
%% will match, and we can remove the corresponding pattern from
@@ -2093,68 +2150,48 @@ maybe_replace_var_1(E, #sub{t=Tdb}) ->
%% The only complication is if the literal is a binary. Binary
%% pattern matching is tricky, so we will give up in that case.
-case_opt_lit(Lit, Cs0, LitExpr) ->
- Cs1 = case_opt_lit_1(Lit, Cs0, LitExpr),
- try case_opt_lit_2(Lit, Cs1) of
+case_opt_lit(Lit, Cs0) ->
+ try case_opt_lit_1(Lit, Cs0) of
Cs ->
{ok,[],Cs}
catch
throw:impossible ->
- {error,Cs1}
+ {error,Cs0}
end.
-case_opt_lit_1(E, [{[P|_],C,_,_}=Current|Cs], LitExpr) ->
- case cerl_clauses:match(P, E) of
- none ->
- %% The pattern will not match the literal. Remove the clause.
- %% Unless the entire case expression is a literal, also
- %% emit a warning.
- case LitExpr of
- false -> add_warning(C, nomatch_clause_type);
- true -> ok
- end,
- case_opt_lit_1(E, Cs, LitExpr);
- _ ->
- [Current|case_opt_lit_1(E, Cs, LitExpr)]
- end;
-case_opt_lit_1(_, [], _) -> [].
-
-case_opt_lit_2(E, [{[P|Ps],C,PsAcc,Bs0}|Cs]) ->
- %% Non-matching clauses have already been removed in case_opt_lit_1/3.
+case_opt_lit_1(E, [{[P|Ps],C,PsAcc,Bs0}|Cs]) ->
+ %% Non-matching clauses have already been removed
+ %% in case_opt_nomatch/3.
case cerl_clauses:match(P, E) of
{true,Bs} ->
%% The pattern matches the literal. Remove the pattern
%% and update the bindings.
- [{Ps,C,PsAcc,Bs++Bs0}|case_opt_lit_2(E, Cs)];
+ [{Ps,C,PsAcc,Bs++Bs0}|case_opt_lit_1(E, Cs)];
{false,_} ->
%% Binary literal and pattern. We are not sure whether
%% the pattern will match.
throw(impossible)
end;
-case_opt_lit_2(_, []) -> [].
+case_opt_lit_1(_, []) -> [].
%% case_opt_data(Expr, Clauses0, LitExpr) -> {ok,Exprs,Clauses}
-case_opt_data(E, Cs0, LitExpr) ->
+case_opt_data(E, Cs0) ->
Es = cerl:data_es(E),
- Cs = case_opt_data_1(Cs0, Es,
- {cerl:data_type(E),cerl:data_arity(E)},
- LitExpr),
- {ok,Es,Cs}.
-
-case_opt_data_1([{[P|Ps0],C,PsAcc,Bs0}|Cs], Es, TypeSig, LitExpr) ->
- case case_data_pat(P, TypeSig) of
- {ok,Ps1,Bs1} ->
- [{Ps1++Ps0,C,PsAcc,Bs1++Bs0}|
- case_opt_data_1(Cs, Es, TypeSig,LitExpr)];
- error ->
- case LitExpr of
- false -> add_warning(C, nomatch_clause_type);
- true -> ok
- end,
- case_opt_data_1(Cs, Es, TypeSig, LitExpr)
- end;
-case_opt_data_1([], _, _, _) -> [].
+ TypeSig = {cerl:data_type(E),cerl:data_arity(E)},
+ try case_opt_data_1(Cs0, Es, TypeSig) of
+ Cs ->
+ {ok,Es,Cs}
+ catch
+ throw:impossible ->
+ {error,Cs0}
+ end.
+
+case_opt_data_1([{[P|Ps0],C,PsAcc,Bs0}|Cs], Es, TypeSig) ->
+ {ok,Ps1,Bs1} = case_data_pat(P, TypeSig),
+ [{Ps1++Ps0,C,PsAcc,Bs1++Bs0}|
+ case_opt_data_1(Cs, Es, TypeSig)];
+case_opt_data_1([], _, _) -> [].
%% case_data_pat(Pattern, Type, Arity) -> {ok,[Pattern],[{AliasVar,Pat}]} | error.
@@ -2163,12 +2200,7 @@ case_data_pat(P, TypeSig) ->
false ->
case_data_pat_var(P, TypeSig);
true ->
- case {cerl:data_type(P),cerl:data_arity(P)} of
- TypeSig ->
- {ok,cerl:data_es(P),[]};
- {_,_} ->
- error
- end
+ {ok,cerl:data_es(P),[]}
end.
%% case_data_pat_var(Pattern, {DataType,ArityType}) ->
@@ -2188,35 +2220,38 @@ case_data_pat_var(P, {Type,Arity}=TypeSig) ->
alias ->
V = cerl:alias_var(P),
Apat = cerl:alias_pat(P),
- case case_data_pat(Apat, TypeSig) of
- {ok,Ps,Bs} ->
- {ok,Ps,[{V,cerl:ann_make_data(Ann, Type, unalias_pat_list(Ps))}|Bs]};
- error ->
- error
- end;
- _ ->
- error
+ {ok,Ps,Bs} = case_data_pat(Apat, TypeSig),
+ {ok,Ps,[{V,cerl:ann_make_data(Ann, Type,
+ pat_to_expr_list(Ps))}|Bs]}
end.
-%% unalias_pat(Pattern) -> Pattern.
-%% Remove all the aliases in a pattern but using the alias variables
-%% instead of the values. We KNOW they will be bound.
+%% pat_to_expr(Pattern) -> Expression.
+%% Convert a pattern to an expression if possible. We KNOW that
+%% all variables in the pattern will be bound.
+%%
+%% Throw an 'impossible' exception if a map or (non-literal)
+%% binary is encountered. Trying to use a map pattern as an
+%% expression is incorrect, while rebuilding a potentially
+%% huge binary in an expression would be wasteful.
-unalias_pat(P) ->
- case cerl:is_c_alias(P) of
- true ->
+pat_to_expr(P) ->
+ case cerl:type(P) of
+ alias ->
cerl:alias_var(P);
- false ->
+ var ->
+ P;
+ _ ->
case cerl:is_data(P) of
false ->
- P;
+ %% Map or binary.
+ throw(impossible);
true ->
- Es = unalias_pat_list(cerl:data_es(P)),
+ Es = pat_to_expr_list(cerl:data_es(P)),
cerl:update_data(P, cerl:data_type(P), Es)
end
end.
-unalias_pat_list(Ps) -> [unalias_pat(P) || P <- Ps].
+pat_to_expr_list(Ps) -> [pat_to_expr(P) || P <- Ps].
make_vars(A, Max) ->
make_vars(A, 1, Max).
@@ -2248,23 +2283,23 @@ letify(#c_var{name=Vname}=Var, Val, Body) ->
%% opt_case_in_let(LetExpr) -> LetExpr'
-opt_case_in_let(#c_let{vars=Vs,arg=Arg,body=B}=Let) ->
- opt_case_in_let_0(Vs, Arg, B, Let).
+opt_case_in_let(#c_let{vars=Vs,arg=Arg,body=B}=Let, Sub) ->
+ opt_case_in_let_0(Vs, Arg, B, Let, Sub).
opt_case_in_let_0([#c_var{name=V}], Arg,
- #c_case{arg=#c_var{name=V},clauses=Cs}=Case, Let) ->
+ #c_case{arg=#c_var{name=V},clauses=Cs}=Case, Let, Sub) ->
case opt_case_in_let_1(V, Arg, Cs) of
impossible ->
case is_simple_case_arg(Arg) andalso
not core_lib:is_var_used(V, Case#c_case{arg=#c_literal{val=nil}}) of
true ->
- expr(opt_bool_case(Case#c_case{arg=Arg,clauses=Cs}), sub_new());
+ expr(opt_bool_case(Case#c_case{arg=Arg,clauses=Cs}), sub_new(Sub));
false ->
Let
end;
Expr -> Expr
end;
-opt_case_in_let_0(_, _, _, Let) -> Let.
+opt_case_in_let_0(_, _, _, Let, _) -> Let.
opt_case_in_let_1(V, Arg, Cs) ->
try
@@ -2607,7 +2642,7 @@ opt_simple_let_2(Let0, Vs0, Arg0, Body0, effect, Sub) ->
expr(#c_seq{arg=Arg,body=Body}, effect, sub_new_preserve_types(Sub));
true ->
Let = Let0#c_let{vars=Vs,arg=Arg,body=Body},
- opt_case_in_let_arg(opt_case_in_let(Let), effect, Sub)
+ opt_case_in_let_arg(opt_case_in_let(Let, Sub), effect, Sub)
end
end;
opt_simple_let_2(Let, Vs0, Arg0, Body, value, Sub) ->
@@ -2630,7 +2665,7 @@ opt_simple_let_2(Let, Vs0, Arg0, Body, value, Sub) ->
expr(#c_seq{arg=Arg,body=Body}, value, sub_new_preserve_types(Sub));
{Vs,Arg,Body} ->
opt_case_in_let_arg(
- opt_case_in_let(Let#c_let{vars=Vs,arg=Arg,body=Body}),
+ opt_case_in_let(Let#c_let{vars=Vs,arg=Arg,body=Body}, Sub),
value, Sub)
end.
diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl
index 8c18f6a9f7..59ec0d4199 100644
--- a/lib/compiler/src/v3_core.erl
+++ b/lib/compiler/src/v3_core.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -82,6 +82,8 @@
-include("core_parse.hrl").
+-define(REC_OFFSET, 100000000). % Also in erl_expand_records.
+
%% Internal core expressions and help functions.
%% N.B. annotations fields in place as normal Core expressions.
@@ -501,7 +503,7 @@ expr({cons,L,H0,T0}, St0) ->
{H1,Hps,St1} = safe(H0, St0),
{T1,Tps,St2} = safe(T0, St1),
A = lineno_anno(L, St2),
- {ann_c_cons(A, H1, T1),Hps ++ Tps,St2};
+ {annotate_cons(A, H1, T1, St2),Hps ++ Tps,St2};
expr({lc,L,E,Qs0}, St0) ->
{Qs1,St1} = preprocess_quals(L, Qs0, St0),
lc_tq(L, E, Qs1, #c_literal{anno=lineno_anno(L, St1),val=[]}, St1);
@@ -509,8 +511,8 @@ expr({bc,L,E,Qs}, St) ->
bc_tq(L, E, Qs, {nil,L}, St);
expr({tuple,L,Es0}, St0) ->
{Es1,Eps,St1} = safe_list(Es0, St0),
- A = lineno_anno(L, St1),
- {ann_c_tuple(A, Es1),Eps,St1};
+ A = record_anno(L, St1),
+ {annotate_tuple(A, Es1, St1),Eps,St1};
expr({map,L,Es0}, St0) ->
% erl_lint should make sure only #{ K => V } are allowed
% in map construction.
@@ -645,8 +647,8 @@ expr({'fun',L,{clauses,Cs},Id}, St) ->
fun_tq(Id, Cs, L, St, unnamed);
expr({named_fun,L,'_',Cs,Id}, St) ->
fun_tq(Id, Cs, L, St, unnamed);
-expr({named_fun,L,Name,Cs,{Index,Uniq,_Fname}}, St) ->
- fun_tq({Index,Uniq,Name}, Cs, L, St, {named, Name});
+expr({named_fun,L,Name,Cs,Id}, St) ->
+ fun_tq(Id, Cs, L, St, {named,Name});
expr({call,L,{remote,_,M,F},As0}, #core{wanted=Wanted}=St0) ->
{[M1,F1|As1],Aps,St1} = safe_list([M,F|As0], St0),
Lanno = lineno_anno(L, St1),
@@ -784,23 +786,42 @@ is_valid_map_src(_) -> false.
map_pair_list(Es, St) ->
foldr(fun
({map_field_assoc,L,K0,V0}, {Ces,Esp,St0}) ->
- {K,Ep0,St1} = safe(K0, St0),
- ok = ensure_valid_map_key(K),
+ {K1,Ep0,St1} = safe(K0, St0),
+ K = ensure_valid_map_key(K1),
{V,Ep1,St2} = safe(V0, St1),
A = lineno_anno(L, St2),
Pair = #c_map_pair{op=#c_literal{val=assoc},anno=A,key=K,val=V},
{[Pair|Ces],Ep0 ++ Ep1 ++ Esp,St2};
({map_field_exact,L,K0,V0}, {Ces,Esp,St0}) ->
- {K,Ep0,St1} = safe(K0, St0),
- ok = ensure_valid_map_key(K),
+ {K1,Ep0,St1} = safe(K0, St0),
+ K = ensure_valid_map_key(K1),
{V,Ep1,St2} = safe(V0, St1),
A = lineno_anno(L, St2),
Pair = #c_map_pair{op=#c_literal{val=exact},anno=A,key=K,val=V},
{[Pair|Ces],Ep0 ++ Ep1 ++ Esp,St2}
end, {[],[],St}, Es).
-ensure_valid_map_key(#c_literal{}) -> ok;
-ensure_valid_map_key(_) -> throw({bad_map,bad_map_key}).
+ensure_valid_map_key(K0) ->
+ case coalesced_map_key(K0) of
+ {ok,K1} -> K1;
+ error -> throw({bad_map,bad_map_key})
+ end.
+
+coalesced_map_key(#c_literal{}=K) -> {ok,K};
+%% Dialyzer hack redux
+%% DO coalesce tuples and list in maps for dialyzer
+%% Dialyzer tries to break this apart, don't let it
+coalesced_map_key(#c_tuple{}=K) ->
+ case core_lib:is_literal(K) of
+ true -> {ok,cerl:fold_literal(K)};
+ false -> error
+ end;
+coalesced_map_key(#c_cons{}=K) ->
+ case core_lib:is_literal(K) of
+ true -> {ok,cerl:fold_literal(K)};
+ false -> error
+ end;
+coalesced_map_key(_) -> error.
%% try_exception([ExcpClause], St) -> {[ExcpVar],Handler,St}.
@@ -1557,9 +1578,9 @@ pattern({atom,L,A}, St) -> #c_literal{anno=lineno_anno(L, St),val=A};
pattern({string,L,S}, St) -> #c_literal{anno=lineno_anno(L, St),val=S};
pattern({nil,L}, St) -> #c_literal{anno=lineno_anno(L, St),val=[]};
pattern({cons,L,H,T}, St) ->
- ann_c_cons(lineno_anno(L, St), pattern(H, St), pattern(T, St));
+ annotate_cons(lineno_anno(L, St), pattern(H, St), pattern(T, St), St);
pattern({tuple,L,Ps}, St) ->
- ann_c_tuple(lineno_anno(L, St), pattern_list(Ps, St));
+ annotate_tuple(record_anno(L, St), pattern_list(Ps, St), St);
pattern({map,L,Ps}, St) ->
#c_map{anno=lineno_anno(L, St), es=pattern_map_pairs(Ps, St)};
pattern({bin,L,Ps}, St) ->
@@ -1604,17 +1625,23 @@ pattern_alias_map_pair_patterns([Cv]) -> Cv;
pattern_alias_map_pair_patterns([Cv1,Cv2|Cvs]) ->
pattern_alias_map_pair_patterns([pat_alias(Cv1,Cv2)|Cvs]).
-pattern_map_pair({map_field_exact,L,K,V}, St) ->
+pattern_map_pair({map_field_exact,L,K,V},St) ->
+ #c_map_pair{anno=lineno_anno(L, St),
+ op=#c_literal{val=exact},
+ key=pattern_map_key(K,St),
+ val=pattern(V, St)}.
+
+pattern_map_key(K,St) ->
+ %% Throws 'nomatch' if the key can't be a literal
+ %% this will be a cryptic error message but it is better than nothing
case expr(K,St) of
- {#c_literal{}=Key,_,_} ->
- #c_map_pair{anno=lineno_anno(L, St),
- op=#c_literal{val=exact},
- key=Key,
- val=pattern(V, St)};
- _ ->
- %% this will throw a cryptic error message
- %% but it is better than nothing
- throw(nomatch)
+ {Key0,[],_} ->
+ %% Dialyzer hack redux
+ case coalesced_map_key(Key0) of
+ {ok,Key1} -> Key1;
+ error -> throw(nomatch)
+ end;
+ _ -> throw(nomatch)
end.
%% pat_bin([BinElement], State) -> [BinSeg].
@@ -1721,6 +1748,26 @@ fail_clause(Pats, Anno, Arg) ->
body=[#iprimop{anno=#a{anno=Anno},name=#c_literal{val=match_fail},
args=[Arg]}]}.
+annotate_tuple(A, Es, St) ->
+ case member(dialyzer, St#core.opts) of
+ true ->
+ %% Do not coalesce constant tuple elements. A Hack.
+ Node = cerl:ann_c_tuple(A, [cerl:c_var(any)]),
+ cerl:update_c_tuple_skel(Node, Es);
+ false ->
+ ann_c_tuple(A, Es)
+ end.
+
+annotate_cons(A, H, T, St) ->
+ case member(dialyzer, St#core.opts) of
+ true ->
+ %% Do not coalesce constant conses. A Hack.
+ Node= cerl:ann_c_cons(A, cerl:c_var(any), cerl:c_var(any)),
+ cerl:update_c_cons_skel(Node, H, T);
+ false ->
+ ann_c_cons(A, H, T)
+ end.
+
ubody(B, St) -> uexpr(B, [], St).
%% uclauses([Lclause], [KnownVar], State) -> {[Lclause],State}.
@@ -2238,6 +2285,23 @@ bitstr_vars(Segs, Vs) ->
lit_vars(V, lit_vars(S, Vs0))
end, Vs, Segs).
+record_anno(L, St) when L >= ?REC_OFFSET ->
+ case member(dialyzer, St#core.opts) of
+ true ->
+ [record | lineno_anno(L - ?REC_OFFSET, St)];
+ false ->
+ lineno_anno(L, St)
+ end;
+record_anno(L, St) when L < -?REC_OFFSET ->
+ case member(dialyzer, St#core.opts) of
+ true ->
+ [record | lineno_anno(L + ?REC_OFFSET, St)];
+ false ->
+ lineno_anno(L, St)
+ end;
+record_anno(L, St) ->
+ lineno_anno(L, St).
+
lineno_anno(L, St) ->
{line, Line} = erl_parse:get_attribute(L, line),
if
diff --git a/lib/compiler/test/bs_match_SUITE.erl b/lib/compiler/test/bs_match_SUITE.erl
index 149b9bbb8f..10e3451e8f 100644
--- a/lib/compiler/test/bs_match_SUITE.erl
+++ b/lib/compiler/test/bs_match_SUITE.erl
@@ -34,7 +34,7 @@
otp_7188/1,otp_7233/1,otp_7240/1,otp_7498/1,
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]).
+ no_partition/1,calling_a_binary/1,binary_in_map/1]).
-export([coverage_id/1,coverage_external_ignore/2]).
@@ -59,7 +59,7 @@ groups() ->
matching_and_andalso,otp_7188,otp_7233,otp_7240,
otp_7498,match_string,zero_width,bad_size,haystack,
cover_beam_bool,matched_out_size,follow_fail_branch,
- no_partition,calling_a_binary]}].
+ no_partition,calling_a_binary,binary_in_map]}].
init_per_suite(Config) ->
@@ -1189,6 +1189,26 @@ call_binary(<<>>, Acc) ->
call_binary(<<H,T/bits>>, Acc) ->
T(<<Acc/binary,H>>).
+binary_in_map(Config) when is_list(Config) ->
+ ok = match_binary_in_map(#{key => <<42:8>>}),
+ {'EXIT',{{badmatch,#{key := 1}},_}} =
+ (catch match_binary_in_map(#{key => 1})),
+ {'EXIT',{{badmatch,#{key := <<1023:16>>}},_}} =
+ (catch match_binary_in_map(#{key => <<1023:16>>})),
+ {'EXIT',{{badmatch,#{key := <<1:8>>}},_}} =
+ (catch match_binary_in_map(#{key => <<1:8>>})),
+ {'EXIT',{{badmatch,not_a_map},_}} =
+ (catch match_binary_in_map(not_a_map)),
+ ok.
+
+match_binary_in_map(Map) ->
+ case 8 of
+ N ->
+ #{key := <<42:N>>} = Map,
+ ok
+ end.
+
+
check(F, R) ->
R = F().
diff --git a/lib/compiler/test/core_fold_SUITE.erl b/lib/compiler/test/core_fold_SUITE.erl
index 6a7036d728..2de17e7653 100644
--- a/lib/compiler/test/core_fold_SUITE.erl
+++ b/lib/compiler/test/core_fold_SUITE.erl
@@ -60,6 +60,12 @@ t_element(Config) when is_list(Config) ->
X = make_ref(),
?line X = id(element(1, {X,y,z})),
?line b = id(element(2, {a,b,c,d})),
+ (fun() ->
+ case {a,#{k=>X}} of
+ {a,#{k:=X}}=Tuple ->
+ #{k:=X} = id(element(2, Tuple))
+ end
+ end)(),
%% No optimization, but should work.
Tuple = id({x,y,z}),
@@ -204,6 +210,16 @@ eq(Config) when is_list(Config) ->
?line ?CMP_DIFF(a, [a]),
?line ?CMP_DIFF(a, {1,2,3}),
+ ?CMP_SAME(#{a=>1.0,b=>2}, #{b=>2.0,a=>1}),
+ ?CMP_SAME(#{a=>[1.0],b=>[2]}, #{b=>[2.0],a=>[1]}),
+
+ %% The rule for comparing keys are different in 17.x and 18.x.
+ %% Just test that the results are consistent.
+ Bool = id(#{1=>a}) == id(#{1.0=>a}), %Unoptimizable.
+ Bool = id(#{1=>a}) == #{1.0=>a}, %Optimizable.
+ Bool = #{1=>a} == #{1.0=>a}, %Optimizable.
+ io:format("Bool = ~p\n", [Bool]),
+
ok.
%% OTP-7117.
diff --git a/lib/compiler/test/fun_SUITE.erl b/lib/compiler/test/fun_SUITE.erl
index 25b7f677b5..a3e9b7fe4e 100644
--- a/lib/compiler/test/fun_SUITE.erl
+++ b/lib/compiler/test/fun_SUITE.erl
@@ -21,10 +21,10 @@
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2,
test1/1,overwritten_fun/1,otp_7202/1,bif_fun/1,
- external/1,eep37/1,badarity/1]).
+ external/1,eep37/1,eep37_dup/1,badarity/1]).
-%% Internal export.
--export([call_me/1]).
+%% Internal exports.
+-export([call_me/1,dup1/0,dup2/0]).
-include_lib("test_server/include/test_server.hrl").
@@ -32,7 +32,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
test_lib:recompile(?MODULE),
- [test1,overwritten_fun,otp_7202,bif_fun,external,eep37,badarity].
+ [test1,overwritten_fun,otp_7202,bif_fun,external,eep37,eep37_dup,badarity].
groups() ->
[].
@@ -206,6 +206,17 @@ eep37(Config) when is_list(Config) ->
50 = UnusedName(8),
ok.
+eep37_dup(Config) when is_list(Config) ->
+ dup1 = (dup1())(),
+ dup2 = (dup2())(),
+ ok.
+
+dup1() ->
+ fun _F() -> dup1 end.
+
+dup2() ->
+ fun _F() -> dup2 end.
+
badarity(Config) when is_list(Config) ->
{'EXIT',{{badarity,{_,[]}},_}} = (catch (fun badarity/1)()),
ok.
diff --git a/lib/compiler/test/guard_SUITE.erl b/lib/compiler/test/guard_SUITE.erl
index eb205d09a7..34bfdeb1e5 100644
--- a/lib/compiler/test/guard_SUITE.erl
+++ b/lib/compiler/test/guard_SUITE.erl
@@ -1556,6 +1556,24 @@ bad_constants(Config) when is_list(Config) ->
bad_guards(Config) when is_list(Config) ->
if erlang:float(self()); true -> ok end,
+
+ fc(catch bad_guards_1(1, [])),
+ fc(catch bad_guards_1(1, [2])),
+ fc(catch bad_guards_1(atom, [2])),
+
+ fc(catch bad_guards_2(#{a=>0,b=>0}, [])),
+ fc(catch bad_guards_2(#{a=>0,b=>0}, [x])),
+ fc(catch bad_guards_2(not_a_map, [x])),
+ fc(catch bad_guards_2(42, [x])),
+ ok.
+
+%% beam_bool used to produce GC BIF instructions whose
+%% Live operands included uninitialized registers.
+
+bad_guards_1(X, [_]) when {{X}}, -X ->
+ ok.
+
+bad_guards_2(M, [_]) when M#{a := 0, b => 0}, map_size(M) ->
ok.
%% Call this function to turn off constant propagation.
diff --git a/lib/compiler/test/match_SUITE.erl b/lib/compiler/test/match_SUITE.erl
index ae7d764535..1e778dca24 100644
--- a/lib/compiler/test/match_SUITE.erl
+++ b/lib/compiler/test/match_SUITE.erl
@@ -22,7 +22,7 @@
init_per_group/2,end_per_group/2,
pmatch/1,mixed/1,aliases/1,match_in_call/1,
untuplify/1,shortcut_boolean/1,letify_guard/1,
- selectify/1,underscore/1,coverage/1]).
+ selectify/1,underscore/1,match_map/1,coverage/1]).
-include_lib("test_server/include/test_server.hrl").
@@ -35,7 +35,8 @@ all() ->
groups() ->
[{p,test_lib:parallel(),
[pmatch,mixed,aliases,match_in_call,untuplify,
- shortcut_boolean,letify_guard,selectify,underscore,coverage]}].
+ shortcut_boolean,letify_guard,selectify,
+ underscore,match_map,coverage]}].
init_per_suite(Config) ->
@@ -400,6 +401,24 @@ underscore(Config) when is_list(Config) ->
_ = is_list(Config),
ok.
+-record(s, {map,t}).
+
+match_map(Config) when is_list(Config) ->
+ Map = #{key=>{x,y},ignore=>anything},
+ #s{map=Map,t={x,y}} = do_match_map(#s{map=Map}),
+ {a,#{k:={a,b,c}}} = do_match_map_2(#{k=>{a,b,c}}),
+ ok.
+
+do_match_map(#s{map=#{key:=Val}}=S) ->
+ %% Would crash with a 'badarg' exception.
+ S#s{t=Val}.
+
+do_match_map_2(Map) ->
+ case {a,Map} of
+ {a,#{k:=_}}=Tuple ->
+ Tuple
+ end.
+
coverage(Config) when is_list(Config) ->
%% Cover beam_dead.
ok = coverage_1(x, a),
diff --git a/lib/compiler/test/warnings_SUITE.erl b/lib/compiler/test/warnings_SUITE.erl
index ad4ad91f74..0637041873 100644
--- a/lib/compiler/test/warnings_SUITE.erl
+++ b/lib/compiler/test/warnings_SUITE.erl
@@ -662,6 +662,20 @@ latin1_fallback(Conf) when is_list(Conf) ->
{warnings,[{1,compile,reparsing_invalid_unicode}]}
}],
[] = run(Conf, Ts2),
+
+ Ts3 = [{latin1_fallback3,
+ %% Test that the compiler fall backs to latin-1 with
+ %% a warning if a file has no encoding and does not
+ %% contain correct UTF-8 sequences.
+ <<"-ifdef(NOTDEFINED).
+ t(_) -> \"",246,"\";
+ t(x) -> ok.
+ -endif.
+ ">>,
+ [],
+ {warnings,[{2,compile,reparsing_invalid_unicode}]}}],
+ [] = run(Conf, Ts3),
+
ok.
%%%
diff --git a/lib/compiler/vsn.mk b/lib/compiler/vsn.mk
index c0c3d56472..b1a6c15ac9 100644
--- a/lib/compiler/vsn.mk
+++ b/lib/compiler/vsn.mk
@@ -1 +1 @@
-COMPILER_VSN = 5.0
+COMPILER_VSN = 5.0.3