aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/compiler/doc/src/compile.xml10
-rw-r--r--lib/compiler/src/beam_bool.erl22
-rw-r--r--lib/compiler/src/beam_jump.erl1
-rw-r--r--lib/compiler/src/beam_utils.erl2
-rw-r--r--lib/compiler/src/cerl_clauses.erl18
-rw-r--r--lib/compiler/src/compile.erl2
-rw-r--r--lib/compiler/src/core_lint.erl27
-rw-r--r--lib/compiler/src/sys_core_fold.erl21
-rw-r--r--lib/compiler/src/sys_pre_expand.erl7
-rw-r--r--lib/compiler/src/v3_codegen.erl6
-rw-r--r--lib/compiler/src/v3_core.erl115
-rw-r--r--lib/compiler/src/v3_kernel.erl2
-rw-r--r--lib/compiler/test/andor_SUITE.erl15
-rw-r--r--lib/compiler/test/core_SUITE.erl5
-rw-r--r--lib/compiler/test/core_SUITE_data/bad_boolean_guard.core32
-rw-r--r--lib/compiler/test/fun_SUITE.erl8
-rw-r--r--lib/compiler/test/guard_SUITE.erl12
-rw-r--r--lib/compiler/test/lc_SUITE.erl8
-rw-r--r--lib/compiler/test/map_SUITE.erl27
-rw-r--r--lib/compiler/test/receive_SUITE.erl1
-rw-r--r--lib/compiler/test/record_SUITE.erl8
-rw-r--r--lib/compiler/test/warnings_SUITE.erl25
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/predef.erl4
-rw-r--r--lib/hipe/cerl/erl_bif_types.erl13
-rw-r--r--lib/kernel/src/hipe_unified_loader.erl24
-rw-r--r--lib/ssl/doc/src/ssl.xml28
-rw-r--r--lib/ssl/src/ssl.erl13
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl14
-rw-r--r--lib/stdlib/doc/src/supervisor.xml6
-rw-r--r--lib/stdlib/src/dets.erl12
-rw-r--r--lib/stdlib/src/erl_lint.erl145
-rw-r--r--lib/stdlib/src/gen.erl6
-rw-r--r--lib/stdlib/src/qlc_pt.erl11
-rw-r--r--lib/stdlib/test/dets_SUITE.erl75
-rw-r--r--lib/stdlib/test/erl_lint_SUITE.erl101
-rw-r--r--lib/stdlib/test/erl_lint_SUITE_data/predef.erl4
-rw-r--r--lib/stdlib/test/qlc_SUITE.erl19
-rw-r--r--lib/tools/src/cover.erl19
38 files changed, 654 insertions, 214 deletions
diff --git a/lib/compiler/doc/src/compile.xml b/lib/compiler/doc/src/compile.xml
index c66c8ea4bf..5fccdcdcb5 100644
--- a/lib/compiler/doc/src/compile.xml
+++ b/lib/compiler/doc/src/compile.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2013</year>
+ <year>1996</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -551,6 +551,14 @@ module.beam: module.erl \
<c>{Module,Name,Arity}</c> or a list of such tuples.</p>
</item>
+ <tag><c>nowarn_deprecated_type</c></tag>
+ <item>
+ <p>Turns off warnings for uses of deprecated types. By
+ default (<c>warn_deprecated_type</c>), warnings are
+ emitted for every use of a type known by the compiler
+ to be deprecated.</p>
+ </item>
+
<tag><c>warn_obsolete_guard</c></tag>
<item>
<p>Causes warnings to be emitted for calls to old type
diff --git a/lib/compiler/src/beam_bool.erl b/lib/compiler/src/beam_bool.erl
index 124abd13c1..d01f9ee13d 100644
--- a/lib/compiler/src/beam_bool.erl
+++ b/lib/compiler/src/beam_bool.erl
@@ -318,6 +318,8 @@ split_block_label_used([{set,[_],_,{bif,_,{f,Fail}}}|_], Fail) ->
true;
split_block_label_used([{set,[_],_,{alloc,_,{gc_bif,_,{f,Fail}}}}|_], Fail) ->
true;
+split_block_label_used([{set,[_],_,{alloc,_,{put_map,_,{f,Fail}}}}|_], Fail) ->
+ true;
split_block_label_used([_|Is], Fail) ->
split_block_label_used(Is, Fail);
split_block_label_used([], _) -> false.
@@ -391,10 +393,14 @@ bopt_tree([{set,_,_,{bif,'xor',_}}|_], _, _) ->
throw('xor');
bopt_tree([{protected,[Dst],Code,_}|Is], Forest0, Pre) ->
ProtForest0 = gb_trees:from_orddict([P || {_,any}=P <- gb_trees:to_list(Forest0)]),
- {ProtPre,[{_,ProtTree}]} = bopt_tree(Code, ProtForest0, []),
- Prot = {prot,ProtPre,ProtTree},
- Forest = gb_trees:enter(Dst, Prot, Forest0),
- bopt_tree(Is, Forest, Pre);
+ case bopt_tree(Code, ProtForest0, []) of
+ {ProtPre,[{_,ProtTree}]} ->
+ Prot = {prot,ProtPre,ProtTree},
+ Forest = gb_trees:enter(Dst, Prot, Forest0),
+ bopt_tree(Is, Forest, Pre);
+ _Res ->
+ throw(not_boolean_expr)
+ end;
bopt_tree([{set,[Dst],[Src],move}=Move|Is], Forest, Pre) ->
case {Src,Dst} of
{{tmp,_},_} -> throw(move);
@@ -525,7 +531,9 @@ bopt_cg({prot,Pre0,Tree}, Fail, Rs0, Acc, St0) ->
bopt_cg({atom,true}, _Fail, _Rs, Acc, St) ->
{Acc,St};
bopt_cg({atom,false}, Fail, _Rs, Acc, St) ->
- {[{jump,{f,Fail}}|Acc],St}.
+ {[{jump,{f,Fail}}|Acc],St};
+bopt_cg(_, _, _, _, _) ->
+ throw(not_boolean_expr).
bopt_cg_not({'and',As0}) ->
As = [bopt_cg_not(A) || A <- As0],
@@ -538,7 +546,9 @@ bopt_cg_not({'not',Arg}) ->
bopt_cg_not({test,Test,Fail,As}) ->
{inverted_test,Test,Fail,As};
bopt_cg_not({atom,Bool}) when is_boolean(Bool) ->
- {atom,not Bool}.
+ {atom,not Bool};
+bopt_cg_not(_) ->
+ throw(not_boolean_expr).
bopt_cg_not_not({'and',As}) ->
{'and',[bopt_cg_not_not(A) || A <- As]};
diff --git a/lib/compiler/src/beam_jump.erl b/lib/compiler/src/beam_jump.erl
index 0fc8d45c80..100acaede3 100644
--- a/lib/compiler/src/beam_jump.erl
+++ b/lib/compiler/src/beam_jump.erl
@@ -451,6 +451,7 @@ is_label_used_in_2({set,_,_,Info}, Lbl) ->
{put_tuple,_} -> false;
{get_tuple_element,_} -> false;
{set_tuple_element,_} -> false;
+ {get_map_elements,{f,F}} -> F =:= Lbl;
{line,_} -> false;
_ when is_atom(Info) -> false
end.
diff --git a/lib/compiler/src/beam_utils.erl b/lib/compiler/src/beam_utils.erl
index 27034aecce..8ca368c167 100644
--- a/lib/compiler/src/beam_utils.erl
+++ b/lib/compiler/src/beam_utils.erl
@@ -748,6 +748,8 @@ live_opt([{try_end,_}=I|Is], Regs, D, Acc) ->
live_opt(Is, Regs, D, [I|Acc]);
live_opt([{loop_rec_end,_}=I|Is], Regs, D, Acc) ->
live_opt(Is, Regs, D, [I|Acc]);
+live_opt([{wait_timeout,_,nil}=I|Is], Regs, D, Acc) ->
+ live_opt(Is, Regs, D, [I|Acc]);
live_opt([{wait_timeout,_,{Tag,_}}=I|Is], Regs, D, Acc) when Tag =/= x ->
live_opt(Is, Regs, D, [I|Acc]);
live_opt([{line,_}=I|Is], Regs, D, Acc) ->
diff --git a/lib/compiler/src/cerl_clauses.erl b/lib/compiler/src/cerl_clauses.erl
index 99fa8dd9d5..76d70dcabf 100644
--- a/lib/compiler/src/cerl_clauses.erl
+++ b/lib/compiler/src/cerl_clauses.erl
@@ -354,6 +354,24 @@ match(P, E, Bs) ->
{false, Bs}
end
end;
+ map ->
+ %% The most we can do is to say "definitely no match" if a
+ %% binary pattern is matched against non-binary data.
+ case E of
+ any ->
+ {false, Bs};
+ _ ->
+ case type(E) of
+ literal ->
+ none;
+ cons ->
+ none;
+ tuple ->
+ none;
+ _ ->
+ {false, Bs}
+ end
+ end;
_ ->
match_1(P, E, Bs)
end.
diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl
index b88f9792a5..9030dd998b 100644
--- a/lib/compiler/src/compile.erl
+++ b/lib/compiler/src/compile.erl
@@ -246,7 +246,7 @@ format_error_reason({Reason, Stack}) when is_list(Stack) ->
end,
FormatFun = fun (Term, _) -> io_lib:format("~tp", [Term]) end,
[io_lib:format("~tp", [Reason]),"\n\n",
- lib:format_stacktrace(1, erlang:get_stacktrace(), StackFun, FormatFun)];
+ lib:format_stacktrace(1, Stack, StackFun, FormatFun)];
format_error_reason(Reason) ->
io_lib:format("~tp", [Reason]).
diff --git a/lib/compiler/src/core_lint.erl b/lib/compiler/src/core_lint.erl
index 36165245a6..25df33a287 100644
--- a/lib/compiler/src/core_lint.erl
+++ b/lib/compiler/src/core_lint.erl
@@ -267,10 +267,21 @@ gexpr(#c_let{vars=Vs,arg=Arg,body=B}, Def, Rt, St0) ->
St1 = gbody(Arg, Def, let_varcount(Vs), St0), %This is a guard body
{Lvs,St2} = variable_list(Vs, St1),
gbody(B, union(Lvs, Def), Rt, St2);
-gexpr(#c_call{module=#c_literal{val=erlang},
- name=#c_literal{},
- args=As}, Def, 1, St) ->
- gexpr_list(As, Def, St);
+gexpr(#c_call{module=#c_literal{val=erlang},name=#c_literal{val=is_record},
+ args=[Arg,#c_literal{val=Tag},#c_literal{val=Size}]},
+ Def, 1, St) when is_atom(Tag), is_integer(Size) ->
+ gexpr(Arg, Def, 1, St);
+gexpr(#c_call{module=#c_literal{val=erlang},name=#c_literal{val=is_record}},
+ _Def, 1, St) ->
+ add_error({illegal_guard,St#lint.func}, St);
+gexpr(#c_call{module=#c_literal{val=erlang},name=#c_literal{val=Name},args=As},
+ Def, 1, St) when is_atom(Name) ->
+ case is_guard_bif(Name, length(As)) of
+ true ->
+ gexpr_list(As, Def, St);
+ false ->
+ add_error({illegal_guard,St#lint.func}, St)
+ end;
gexpr(#c_primop{name=#c_literal{val=A},args=As}, Def, _Rt, St0) when is_atom(A) ->
gexpr_list(As, Def, St0);
gexpr(#c_try{arg=E,vars=[#c_var{name=X}],body=#c_var{name=X},
@@ -298,6 +309,14 @@ gbitstr_list(Es, Def, St0) ->
gbitstr(#c_bitstr{val=V,size=S}, Def, St) ->
gexpr_list([V,S], Def, St).
+%% is_guard_bif(Name, Arity) -> Boolean.
+
+is_guard_bif(Name, Arity) ->
+ erl_internal:guard_bif(Name, Arity)
+ orelse erl_internal:arith_op(Name, Arity)
+ orelse erl_internal:bool_op(Name, Arity)
+ orelse erl_internal:comp_op(Name, Arity).
+
%% expr(Expr, Defined, RetCount, State) -> State.
expr(#c_var{name={_,_}=FA}, Def, _Rt, St) ->
diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl
index eb9c302334..6fdeea51d1 100644
--- a/lib/compiler/src/sys_core_fold.erl
+++ b/lib/compiler/src/sys_core_fold.erl
@@ -2346,16 +2346,31 @@ is_safe_bool_expr(Core, Sub) ->
is_safe_bool_expr_1(Core, Sub, gb_sets:empty()).
is_safe_bool_expr_1(#c_call{module=#c_literal{val=erlang},
- name=#c_literal{val=is_record},
- args=[_,_]},
- _Sub, _BoolVars) ->
+ name=#c_literal{val=is_record},
+ args=[A,#c_literal{val=Tag},#c_literal{val=Size}]},
+ Sub, _BoolVars) when is_atom(Tag), is_integer(Size) ->
+ is_safe_simple(A, Sub);
+is_safe_bool_expr_1(#c_call{module=#c_literal{val=erlang},
+ name=#c_literal{val=is_record}},
+ _Sub, _BoolVars) ->
%% The is_record/2 BIF is NOT allowed in guards.
+ %% The is_record/3 BIF where its second argument is not an atom or its third
+ %% is not an integer is NOT allowed in guards.
%%
%% NOTE: Calls like is_record(Expr, LiteralTag), where LiteralTag
%% is a literal atom referring to a defined record, have already
%% been rewritten to is_record(Expr, LiteralTag, TupleSize).
false;
is_safe_bool_expr_1(#c_call{module=#c_literal{val=erlang},
+ name=#c_literal{val=is_function},
+ args=[A,#c_literal{val=Arity}]},
+ Sub, _BoolVars) when is_integer(Arity), Arity >= 0 ->
+ is_safe_simple(A, Sub);
+is_safe_bool_expr_1(#c_call{module=#c_literal{val=erlang},
+ name=#c_literal{val=is_function}},
+ _Sub, _BoolVars) ->
+ false;
+is_safe_bool_expr_1(#c_call{module=#c_literal{val=erlang},
name=#c_literal{val=Name},args=Args},
Sub, BoolVars) ->
NumArgs = length(Args),
diff --git a/lib/compiler/src/sys_pre_expand.erl b/lib/compiler/src/sys_pre_expand.erl
index 9998043013..91a46a20fe 100644
--- a/lib/compiler/src/sys_pre_expand.erl
+++ b/lib/compiler/src/sys_pre_expand.erl
@@ -331,9 +331,10 @@ expr({tuple,Line,Es0}, St0) ->
expr({map,Line,Es0}, St0) ->
{Es1,St1} = expr_list(Es0, St0),
{{map,Line,Es1},St1};
-expr({map,Line,Var,Es0}, St0) ->
- {Es1,St1} = expr_list(Es0, St0),
- {{map,Line,Var,Es1},St1};
+expr({map,Line,E0,Es0}, St0) ->
+ {E1,St1} = expr(E0, St0),
+ {Es1,St2} = expr_list(Es0, St1),
+ {{map,Line,E1,Es1},St2};
expr({map_field_assoc,Line,K0,V0}, St0) ->
{K,St1} = expr(K0, St0),
{V,St2} = expr(V0, St1),
diff --git a/lib/compiler/src/v3_codegen.erl b/lib/compiler/src/v3_codegen.erl
index e00ee1f3ad..f1331d1fe7 100644
--- a/lib/compiler/src/v3_codegen.erl
+++ b/lib/compiler/src/v3_codegen.erl
@@ -1551,7 +1551,11 @@ map_pair_strip_and_termsort(Es) ->
%% [{map_pair,K,V}]
%% where K is for example {integer, 1} and we want to sort on 1.
Ls = [{K,V}||{_,K,V}<-Es],
- lists:sort(fun({{_,A},_},{{_,B},_}) -> erts_internal:cmp_term(A,B) < 0 end, Ls).
+ lists:sort(fun ({{_,A},_}, {{_,B},_}) -> erts_internal:cmp_term(A,B) =< 0;
+ ({nil,_}, {{_,B},_}) -> [] =< B;
+ ({{_,A},_}, {nil,_}) -> A =< [];
+ ({nil,_}, {nil,_}) -> true
+ end, Ls).
%%%
%%% Code generation for constructing binaries.
diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl
index a50b46bd7b..3d17557e01 100644
--- a/lib/compiler/src/v3_core.erl
+++ b/lib/compiler/src/v3_core.erl
@@ -274,51 +274,67 @@ gexpr({op,L,'orelse',E1,E2}, Bools, St0) ->
True = {atom,L,true},
E = make_bool_switch_guard(L, E1, V, True, E2),
gexpr(E, Bools, St);
-gexpr({op,Line,Op,L,R}=Call, Bools0, St0) ->
+gexpr({op,Line,Op,L,R}=E, Bools, St) ->
case erl_internal:bool_op(Op, 2) of
- true ->
- {Le,Lps,Bools1,St1} = gexpr(L, Bools0, St0),
- {Ll,Llps,St2} = force_safe(Le, St1),
- {Re,Rps,Bools,St3} = gexpr(R, Bools1, St2),
- {Rl,Rlps,St4} = force_safe(Re, St3),
- Anno = lineno_anno(Line, St4),
- {#icall{anno=#a{anno=Anno}, %Must have an #a{}
- module=#c_literal{anno=Anno,val=erlang},
- name=#c_literal{anno=Anno,val=Op},
- args=[Ll,Rl]},Lps ++ Llps ++ Rps ++ Rlps,Bools,St4};
- false ->
- gexpr_test(Call, Bools0, St0)
+ true ->
+ gexpr_bool(Op, L, R, Bools, St, Line);
+ false ->
+ gexpr_test(E, Bools, St)
end;
-gexpr({op,Line,Op,A}=Call, Bools0, St0) ->
- case Op of
- 'not' ->
- {Ae0,Aps,Bools,St1} = gexpr(A, Bools0, St0),
- case Ae0 of
- #icall{module=#c_literal{val=erlang},
- name=#c_literal{val='=:='},
- args=[E,#c_literal{val=true}]}=EqCall ->
- %%
- %% Doing the following transformation
- %% not(Expr =:= true) ==> Expr =:= false
- %% will help eliminating redundant is_boolean/1 tests.
- %%
- Ae = EqCall#icall{args=[E,#c_literal{val=false}]},
- {Al,Alps,St2} = force_safe(Ae, St1),
- {Al,Aps ++ Alps,Bools,St2};
- Ae ->
- {Al,Alps,St2} = force_safe(Ae, St1),
- Anno = lineno_anno(Line, St2),
- {#icall{anno=#a{anno=Anno}, %Must have an #a{}
- module=#c_literal{anno=Anno,val=erlang},
- name=#c_literal{anno=Anno,val=Op},
- args=[Al]},Aps ++ Alps,Bools,St2}
- end;
- _ ->
- gexpr_test(Call, Bools0, St0)
+gexpr({call,Line,{remote,_,{atom,_,erlang},{atom,_,Op}},[L,R]}=E, Bools, St) ->
+ case erl_internal:bool_op(Op, 2) of
+ true ->
+ gexpr_bool(Op, L, R, Bools, St, Line);
+ false ->
+ gexpr_test(E, Bools, St)
end;
+gexpr({op,Line,'not',A}, Bools, St) ->
+ gexpr_not(A, Bools, St, Line);
+gexpr({call,Line,{remote,_,{atom,_,erlang},{atom,_,'not'}},[A]}, Bools, St) ->
+ gexpr_not(A, Bools, St, Line);
gexpr(E0, Bools, St0) ->
gexpr_test(E0, Bools, St0).
+%% gexpr_not(L, R, Bools, State) -> {Cexpr,[PreExp],Bools,State}.
+%% Generate a guard for boolean operators
+
+gexpr_bool(Op, L, R, Bools0, St0, Line) ->
+ {Le,Lps,Bools1,St1} = gexpr(L, Bools0, St0),
+ {Ll,Llps,St2} = force_safe(Le, St1),
+ {Re,Rps,Bools,St3} = gexpr(R, Bools1, St2),
+ {Rl,Rlps,St4} = force_safe(Re, St3),
+ Anno = lineno_anno(Line, St4),
+ {#icall{anno=#a{anno=Anno}, %Must have an #a{}
+ module=#c_literal{anno=Anno,val=erlang},
+ name=#c_literal{anno=Anno,val=Op},
+ args=[Ll,Rl]},Lps ++ Llps ++ Rps ++ Rlps,Bools,St4}.
+
+%% gexpr_not(Expr, Bools, State) -> {Cexpr,[PreExp],Bools,State}.
+%% Generate an erlang:'not'/1 guard test.
+
+gexpr_not(A, Bools0, St0, Line) ->
+ {Ae0,Aps,Bools,St1} = gexpr(A, Bools0, St0),
+ case Ae0 of
+ #icall{module=#c_literal{val=erlang},
+ name=#c_literal{val='=:='},
+ args=[E,#c_literal{val=true}]}=EqCall ->
+ %%
+ %% Doing the following transformation
+ %% not(Expr =:= true) ==> Expr =:= false
+ %% will help eliminating redundant is_boolean/1 tests.
+ %%
+ Ae = EqCall#icall{args=[E,#c_literal{val=false}]},
+ {Al,Alps,St2} = force_safe(Ae, St1),
+ {Al,Aps ++ Alps,Bools,St2};
+ Ae ->
+ {Al,Alps,St2} = force_safe(Ae, St1),
+ Anno = lineno_anno(Line, St2),
+ {#icall{anno=#a{anno=Anno}, %Must have an #a{}
+ module=#c_literal{anno=Anno,val=erlang},
+ name=#c_literal{anno=Anno,val='not'},
+ args=[Al]},Aps ++ Alps,Bools,St2}
+ end.
+
%% gexpr_test(Expr, Bools, State) -> {Cexpr,[PreExp],Bools,State}.
%% Generate a guard test. At this stage we must be sure that we have
%% a proper boolean value here so wrap things with an true test if we
@@ -335,7 +351,8 @@ gexpr_test(E0, Bools0, St0) ->
#icall{anno=Anno,module=#c_literal{val=erlang},name=#c_literal{val=N},args=As} ->
Ar = length(As),
case erl_internal:type_test(N, Ar) orelse
- erl_internal:comp_op(N, Ar) of
+ erl_internal:comp_op(N, Ar) orelse
+ erl_internal:bool_op(N, Ar) of
true -> {E1,Eps0,Bools0,St1};
false ->
Lanno = Anno#a.anno,
@@ -623,7 +640,7 @@ expr({call,Lc,{atom,Lf,F},As0}, St0) ->
Op = #c_var{anno=lineno_anno(Lf, St1),name={F,length(As1)}},
{#iapply{anno=#a{anno=lineno_anno(Lc, St1)},op=Op,args=As1},Aps,St1};
expr({call,L,FunExp,As0}, St0) ->
- {Fun,Fps,St1} = safe(FunExp, St0),
+ {Fun,Fps,St1} = safe_fun(length(As0), FunExp, St0),
{As1,Aps,St2} = safe_list(As0, St1),
Lanno = lineno_anno(L, St2),
{#iapply{anno=#a{anno=Lanno},op=Fun,args=As1},Fps ++ Aps,St2};
@@ -1408,6 +1425,15 @@ safe(E0, St0) ->
{Se,Sps,St2} = force_safe(E1, St1),
{Se,Eps ++ Sps,St2}.
+safe_fun(A0, E0, St0) ->
+ case safe(E0, St0) of
+ {#c_var{name={_,A1}}=E1,Eps,St1} when A1 =/= A0 ->
+ {V,St2} = new_var(St1),
+ {V,Eps ++ [#iset{var=V,arg=E1}],St2};
+ Result ->
+ Result
+ end.
+
safe_list(Es, St) ->
foldr(fun (E, {Ces,Esp,St0}) ->
{Ce,Ep,St1} = safe(E, St0),
@@ -1756,13 +1782,16 @@ uexpr(#iletrec{anno=A,defs=Fs0,body=B0}, Ks, St0) ->
{B1,St2} = uexprs(B0, Ks, St1),
Used = used_in_any(map(fun ({_,F}) -> F end, Fs1) ++ B1),
{#iletrec{anno=A#a{us=Used,ns=[]},defs=Fs1,body=B1},St2};
-uexpr(#icase{anno=A,args=As0,clauses=Cs0,fc=Fc0}, Ks, St0) ->
+uexpr(#icase{anno=#a{anno=Anno}=A,args=As0,clauses=Cs0,fc=Fc0}, Ks, St0) ->
%% As0 will never generate new variables.
{As1,St1} = uexpr_list(As0, Ks, St0),
{Cs1,St2} = uclauses(Cs0, Ks, St1),
{Fc1,St3} = uclause(Fc0, Ks, St2),
Used = union(used_in_any(As1), used_in_any(Cs1)),
- New = new_in_all(Cs1),
+ New = case member(list_comprehension, Anno) of
+ true -> [];
+ false -> new_in_all(Cs1)
+ end,
{#icase{anno=A#a{us=Used,ns=New},args=As1,clauses=Cs1,fc=Fc1},St3};
uexpr(#ifun{anno=A0,id=Id,vars=As,clauses=Cs0,fc=Fc0,name=Name}, Ks0, St0) ->
Avs = lit_list_vars(As),
diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl
index 5675572092..d00dd56f30 100644
--- a/lib/compiler/src/v3_kernel.erl
+++ b/lib/compiler/src/v3_kernel.erl
@@ -519,7 +519,7 @@ map_split_pairs(A, Var, Ces, Sub, St0) ->
Kes1 = [#k_map_pair{key=K,val=V}||{_,{assoc,K,V}} <- Assoc],
{Mvar,Em,St2} = force_atomic(#k_map{anno=A,op=assoc,var=Var,es=Kes1},St1),
Kes2 = [#k_map_pair{key=K,val=V}||{_,{exact,K,V}} <- Exact],
- {#k_map{anno=A,op=exact,var=Mvar,es=Kes2},Em ++ Esp,St2}
+ {#k_map{anno=A,op=exact,var=Mvar,es=Kes2},Esp ++ Em,St2}
end.
diff --git a/lib/compiler/test/andor_SUITE.erl b/lib/compiler/test/andor_SUITE.erl
index 7bef0aa27c..d79696df38 100644
--- a/lib/compiler/test/andor_SUITE.erl
+++ b/lib/compiler/test/andor_SUITE.erl
@@ -129,6 +129,10 @@ t_case_y(X, Y, Z) ->
Y =:= 100
end.
+-define(GUARD(E), if E -> true;
+ true -> false
+ end).
+
t_and_or(Config) when is_list(Config) ->
?line true = true and true,
?line false = true and false,
@@ -160,11 +164,14 @@ t_and_or(Config) when is_list(Config) ->
?line true = false or id(true),
?line false = false or id(false),
- ok.
+ True = id(true),
--define(GUARD(E), if E -> true;
- true -> false
- end).
+ false = ?GUARD(erlang:'and'(bar, True)),
+ false = ?GUARD(erlang:'or'(bar, True)),
+ false = ?GUARD(erlang:'not'(erlang:'and'(bar, True))),
+ false = ?GUARD(erlang:'not'(erlang:'not'(erlang:'and'(bar, True)))),
+
+ ok.
t_andalso(Config) when is_list(Config) ->
Bs = [true,false],
diff --git a/lib/compiler/test/core_SUITE.erl b/lib/compiler/test/core_SUITE.erl
index aa222c48de..428ad65364 100644
--- a/lib/compiler/test/core_SUITE.erl
+++ b/lib/compiler/test/core_SUITE.erl
@@ -24,7 +24,7 @@
dehydrated_itracer/1,nested_tries/1,
seq_in_guard/1,make_effect_seq/1,eval_is_boolean/1,
unsafe_case/1,nomatch_shadow/1,reversed_annos/1,
- map_core_test/1,eval_case/1]).
+ map_core_test/1,eval_case/1,bad_boolean_guard/1]).
-include_lib("test_server/include/test_server.hrl").
@@ -50,7 +50,7 @@ groups() ->
[{p,test_lib:parallel(),
[dehydrated_itracer,nested_tries,seq_in_guard,make_effect_seq,
eval_is_boolean,unsafe_case,nomatch_shadow,reversed_annos,
- map_core_test,eval_case
+ map_core_test,eval_case,bad_boolean_guard
]}].
@@ -77,6 +77,7 @@ end_per_group(_GroupName, Config) ->
?comp(reversed_annos).
?comp(map_core_test).
?comp(eval_case).
+?comp(bad_boolean_guard).
try_it(Mod, Conf) ->
Src = filename:join(?config(data_dir, Conf), atom_to_list(Mod)),
diff --git a/lib/compiler/test/core_SUITE_data/bad_boolean_guard.core b/lib/compiler/test/core_SUITE_data/bad_boolean_guard.core
new file mode 100644
index 0000000000..318f8e3dc7
--- /dev/null
+++ b/lib/compiler/test/core_SUITE_data/bad_boolean_guard.core
@@ -0,0 +1,32 @@
+module 'bad_boolean_guard' ['bad_boolean_guard'/0,
+ 'module_info'/0,
+ 'module_info'/1]
+ attributes []
+'bad_boolean_guard'/0 =
+ fun () ->
+ apply 'f'/1
+ ('true')
+'f'/1 =
+ fun (_X_cor0) ->
+ case _X_cor0 of
+ <X>
+ when try
+ call 'erlang':'and'
+ ('bad', _X_cor0)
+ of <Try> ->
+ Try
+ catch <T,R> ->
+ 'false' ->
+ 'not_ok'
+ <_X_cor3> when 'true' ->
+ 'ok'
+ end
+'module_info'/0 =
+ fun () ->
+ call 'erlang':'get_module_info'
+ ('bad_boolean_guard')
+'module_info'/1 =
+ fun (_X_cor0) ->
+ call 'erlang':'get_module_info'
+ ('bad_boolean_guard', _X_cor0)
+end \ No newline at end of file
diff --git a/lib/compiler/test/fun_SUITE.erl b/lib/compiler/test/fun_SUITE.erl
index e35692efd1..25b7f677b5 100644
--- a/lib/compiler/test/fun_SUITE.erl
+++ b/lib/compiler/test/fun_SUITE.erl
@@ -21,7 +21,7 @@
-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]).
+ external/1,eep37/1,badarity/1]).
%% Internal export.
-export([call_me/1]).
@@ -32,7 +32,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
test_lib:recompile(?MODULE),
- [test1,overwritten_fun,otp_7202,bif_fun,external,eep37].
+ [test1,overwritten_fun,otp_7202,bif_fun,external,eep37,badarity].
groups() ->
[].
@@ -206,5 +206,9 @@ eep37(Config) when is_list(Config) ->
50 = UnusedName(8),
ok.
+badarity(Config) when is_list(Config) ->
+ {'EXIT',{{badarity,{_,[]}},_}} = (catch (fun badarity/1)()),
+ ok.
+
id(I) ->
I.
diff --git a/lib/compiler/test/guard_SUITE.erl b/lib/compiler/test/guard_SUITE.erl
index a0a9bb7ddd..eb205d09a7 100644
--- a/lib/compiler/test/guard_SUITE.erl
+++ b/lib/compiler/test/guard_SUITE.erl
@@ -33,7 +33,7 @@
tricky/1,rel_ops/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_constants/1,bad_guards/1]).
suite() -> [{ct_hooks,[ts_install_cth]}].
@@ -50,7 +50,7 @@ groups() ->
t_is_boolean,is_function_2,tricky,rel_ops,
literal_type_tests,basic_andalso_orelse,traverse_dcd,
check_qlc_hrl,andalso_semi,t_tuple_size,binary_part,
- bad_constants]}].
+ bad_constants,bad_guards]}].
init_per_suite(Config) ->
Config.
@@ -1023,6 +1023,10 @@ is_function_2(Config) when is_list(Config) ->
true = is_function(id(fun() -> ok end), 0),
false = is_function(id(fun ?MODULE:all/1), 0),
false = is_function(id(fun() -> ok end), 1),
+ {'EXIT',{badarg,_}} =
+ (catch is_function(id(fun() -> ok end), -1) orelse error),
+ {'EXIT',{badarg,_}} =
+ (catch is_function(id(fun() -> ok end), '') orelse error),
F = fun(_) -> ok end,
if
@@ -1550,6 +1554,10 @@ bad_constants(Config) when is_list(Config) ->
?line ?FAILING(3.14),
ok.
+bad_guards(Config) when is_list(Config) ->
+ if erlang:float(self()); true -> ok end,
+ ok.
+
%% Call this function to turn off constant propagation.
id(I) -> I.
diff --git a/lib/compiler/test/lc_SUITE.erl b/lib/compiler/test/lc_SUITE.erl
index f5948504b3..398398a397 100644
--- a/lib/compiler/test/lc_SUITE.erl
+++ b/lib/compiler/test/lc_SUITE.erl
@@ -23,7 +23,7 @@
init_per_group/2,end_per_group/2,
init_per_testcase/2,end_per_testcase/2,
basic/1,deeply_nested/1,no_generator/1,
- empty_generator/1]).
+ empty_generator/1,no_export/1]).
-include_lib("test_server/include/test_server.hrl").
@@ -31,7 +31,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
test_lib:recompile(?MODULE),
- [basic, deeply_nested, no_generator, empty_generator].
+ [basic, deeply_nested, no_generator, empty_generator, no_export].
groups() ->
[].
@@ -177,6 +177,10 @@ empty_generator(Config) when is_list(Config) ->
?line [] = [X || {X} <- [], (false or (X/0 > 3))],
ok.
+no_export(Config) when is_list(Config) ->
+ [] = [ _X = a || false ] ++ [ _X = a || false ],
+ ok.
+
id(I) -> I.
fc(Args, {'EXIT',{function_clause,[{?MODULE,_,Args,_}|_]}}) -> ok;
diff --git a/lib/compiler/test/map_SUITE.erl b/lib/compiler/test/map_SUITE.erl
index b4baef461b..4fc4768802 100644
--- a/lib/compiler/test/map_SUITE.erl
+++ b/lib/compiler/test/map_SUITE.erl
@@ -42,7 +42,8 @@
t_build_and_match_val/1,
%% errors in 17.0-rc1
- t_update_values/1
+ t_update_values/1,
+ t_expand_map_update/1
]).
suite() -> [].
@@ -68,7 +69,8 @@ all() -> [
t_build_and_match_val,
%% errors in 17.0-rc1
- t_update_values
+ t_update_values,
+ t_expand_map_update
].
groups() -> [].
@@ -105,6 +107,9 @@ t_build_and_match_literals(Config) when is_list(Config) ->
M = #{ map_1:=#{ map_2:=#{value_3 := third}, value_2:= second}, value_1:=first} =
id(#{ map_1=>#{ map_2=>#{value_3 => third}, value_2=> second}, value_1=>first}),
+ %% nil key
+ #{[]:=ok,1:=2} = id(#{[]=>ok,1=>2}),
+
%% error case
{'EXIT',{{badmatch,_},_}} = (catch (#{x:=3,x:=2} = id(#{x=>3}))),
{'EXIT',{{badmatch,_},_}} = (catch (#{x:=2} = id(#{x=>3}))),
@@ -187,7 +192,9 @@ loop_match_and_update_literals_x_q(#{q:=Q0,x:=X0} = Map, [{X,Q}|Vs]) ->
t_update_map_expressions(Config) when is_list(Config) ->
M = maps:new(),
- #{ a := 1 } = M#{a => 1},
+ X = id(fondue),
+ M1 = #{ a := 1 } = M#{a => 1},
+ #{ b := {X} } = M1#{ a := 1, b => {X} },
#{ b := 2 } = (maps:new())#{ b => 2 },
@@ -273,20 +280,30 @@ t_update_values(Config) when is_list(Config) ->
end, {none, none, #{val1=>none,val2=>none}},List),
ok.
+t_expand_map_update(Config) when is_list(Config) ->
+ M = #{<<"hello">> => <<"world">>}#{<<"hello">> := <<"les gens">>},
+ #{<<"hello">> := <<"les gens">>} = M,
+ ok.
+
check_val(#{val1:=V1, val2:=V2},V1,V2) -> ok.
get_val(#{ "wazzup" := _, val := V}) -> V;
get_val(#{ val := V }) -> {some_val, V}.
t_guard_bifs(Config) when is_list(Config) ->
+ true = map_guard_empty(),
true = map_guard_head(#{a=>1}),
false = map_guard_head([]),
true = map_guard_body(#{a=>1}),
false = map_guard_body({}),
true = map_guard_pattern(#{a=>1, <<"hi">> => "hi" }),
false = map_guard_pattern("list"),
+ true = map_guard_tautology(),
+ true = map_guard_ill_map_size(),
ok.
+map_guard_empty() when is_map(#{}); false -> true.
+
map_guard_head(M) when is_map(M) -> true;
map_guard_head(_) -> false.
@@ -295,6 +312,10 @@ map_guard_body(M) -> is_map(M).
map_guard_pattern(#{}) -> true;
map_guard_pattern(_) -> false.
+map_guard_tautology() when #{} =:= #{}; true -> true.
+
+map_guard_ill_map_size() when true; map_size(0) -> true.
+
t_guard_sequence(Config) when is_list(Config) ->
{1, "a"} = map_guard_sequence_1(#{seq=>1,val=>id("a")}),
{2, "b"} = map_guard_sequence_1(#{seq=>2,val=>id("b")}),
diff --git a/lib/compiler/test/receive_SUITE.erl b/lib/compiler/test/receive_SUITE.erl
index ec49267ded..00a6e900d4 100644
--- a/lib/compiler/test/receive_SUITE.erl
+++ b/lib/compiler/test/receive_SUITE.erl
@@ -257,6 +257,7 @@ wait(Config) when is_list(Config) ->
self() ! <<42>>,
<<42>> = wait_1(r, 1, 2),
{1,2,3} = wait_1(1, 2, 3),
+ {'EXIT',{timeout_value,_}} = (catch receive after [] -> timeout end),
ok.
wait_1(r, _, _) ->
diff --git a/lib/compiler/test/record_SUITE.erl b/lib/compiler/test/record_SUITE.erl
index c9f5a2053e..f736e14bf6 100644
--- a/lib/compiler/test/record_SUITE.erl
+++ b/lib/compiler/test/record_SUITE.erl
@@ -369,6 +369,14 @@ record_test_3(Config) when is_list(Config) ->
?line false = is_record(id(#barf{}), id(barf), id(42)),
?line false = is_record(id(#barf{}), id(foo), id(6)),
+ Rec = id(#barf{}),
+ Good = id(barf),
+ Bad = id(foo),
+ Size = id(6),
+
+ true = is_record(Rec, Good, Size) orelse error,
+ error = is_record(Rec, Bad, Size) orelse error,
+
ok.
record_access_in_guards(Config) when is_list(Config) ->
diff --git a/lib/compiler/test/warnings_SUITE.erl b/lib/compiler/test/warnings_SUITE.erl
index 16d15a59e5..f63299ea35 100644
--- a/lib/compiler/test/warnings_SUITE.erl
+++ b/lib/compiler/test/warnings_SUITE.erl
@@ -37,7 +37,8 @@
-export([pattern/1,pattern2/1,pattern3/1,pattern4/1,
guard/1,bad_arith/1,bool_cases/1,bad_apply/1,
- files/1,effect/1,bin_opt_info/1,bin_construction/1, comprehensions/1]).
+ files/1,effect/1,bin_opt_info/1,bin_construction/1, comprehensions/1,
+ maps/1]).
% Default timetrap timeout (set in init_per_testcase).
-define(default_timeout, ?t:minutes(2)).
@@ -61,7 +62,7 @@ groups() ->
[{p,test_lib:parallel(),
[pattern,pattern2,pattern3,pattern4,guard,
bad_arith,bool_cases,bad_apply,files,effect,
- bin_opt_info,bin_construction,comprehensions]}].
+ bin_opt_info,bin_construction,comprehensions,maps]}].
init_per_suite(Config) ->
Config.
@@ -552,6 +553,26 @@ comprehensions(Config) when is_list(Config) ->
run(Config, Ts),
ok.
+maps(Config) when is_list(Config) ->
+ Ts = [{bad_map,
+ <<"
+ t() ->
+ case maybe_map of
+ #{} -> ok;
+ not_map -> error
+ end.
+ x() ->
+ case true of
+ #{} -> error;
+ true -> ok
+ end.
+ ">>,
+ [],
+ {warnings,[{3,sys_core_fold,no_clause_match},
+ {9,sys_core_fold,nomatch_clause_type}]}}],
+ run(Config, Ts),
+ ok.
+
%%%
%%% End of test cases.
%%%
diff --git a/lib/dialyzer/test/small_SUITE_data/src/predef.erl b/lib/dialyzer/test/small_SUITE_data/src/predef.erl
index c2364fd1c2..ee9073aa67 100644
--- a/lib/dialyzer/test/small_SUITE_data/src/predef.erl
+++ b/lib/dialyzer/test/small_SUITE_data/src/predef.erl
@@ -5,8 +5,8 @@
-export_type([array/0, digraph/0, gb_set/0]).
-%% Before R17B local re-definitions of pre-defined opaque types were
-%% ignored but did not generate any warning.
+%% Before Erlang/OTP 17.0 local re-definitions of pre-defined opaque
+%% types were ignored but did not generate any warning.
-opaque array() :: atom().
-opaque digraph() :: atom().
-opaque gb_set() :: atom().
diff --git a/lib/hipe/cerl/erl_bif_types.erl b/lib/hipe/cerl/erl_bif_types.erl
index 0512d4bc3c..a460f16272 100644
--- a/lib/hipe/cerl/erl_bif_types.erl
+++ b/lib/hipe/cerl/erl_bif_types.erl
@@ -1073,14 +1073,14 @@ type(hipe_bifs, fun_to_address, 1, Xs, Opaques) ->
%% type(hipe_bifs, get_emu_address, 1, Xs, Opaques) ->
%% strict(hipe_bifs, get_emu_address, 1, Xs,
%% fun (_) -> t_integer() end, Opaques); % address
+type(hipe_bifs, get_fe, 2, Xs, Opaques) ->
+ strict(hipe_bifs, get_fe, 2, Xs, fun (_) -> t_integer() end, Opaques);
type(hipe_bifs, get_rts_param, 1, Xs, Opaques) ->
strict(hipe_bifs, get_rts_param, 1, Xs,
fun (_) -> t_sup(t_integer(), t_nil()) end, Opaques);
type(hipe_bifs, invalidate_funinfo_native_addresses, 1, Xs, Opaques) ->
strict(hipe_bifs, invalidate_funinfo_native_addresses, 1, Xs,
fun (_) -> t_nil() end, Opaques);
-type(hipe_bifs, make_fe, 3, Xs, Opaques) ->
- strict(hipe_bifs, make_fe, 3, Xs, fun (_) -> t_integer() end, Opaques);
%% type(hipe_bifs, make_native_stub, 2, Xs, Opaques) ->
%% strict(hipe_bifs, make_native_stub, 2, Xs,
%% fun (_) -> t_integer() end, Opaques); % address
@@ -1116,6 +1116,9 @@ type(hipe_bifs, set_funinfo_native_address, 3, Xs, Opaques) ->
type(hipe_bifs, set_native_address, 3, Xs, Opaques) ->
strict(hipe_bifs, set_native_address, 3, Xs,
fun (_) -> t_nil() end, Opaques);
+type(hipe_bifs, set_native_address_in_fe, 2, Xs, Opaques) ->
+ strict(hipe_bifs, set_native_address_in_fe, 2, Xs,
+ fun (_) -> t_atom('true') end, Opaques);
type(hipe_bifs, system_crc, 1, Xs, Opaques) ->
strict(hipe_bifs, system_crc, 1, Xs, fun (_) -> t_crc32() end, Opaques);
type(hipe_bifs, term_to_word, 1, Xs, Opaques) ->
@@ -2451,12 +2454,12 @@ arg_types(hipe_bifs, fun_to_address, 1) ->
[t_mfa()];
%% arg_types(hipe_bifs, get_emu_address, 1) ->
%% [t_mfa()];
+arg_types(hipe_bifs, get_fe, 2) ->
+ [t_atom(), t_tuple([t_integer(), t_integer(), t_integer()])];
arg_types(hipe_bifs, get_rts_param, 1) ->
[t_fixnum()];
arg_types(hipe_bifs, invalidate_funinfo_native_addresses, 1) ->
[t_list(t_mfa())];
-arg_types(hipe_bifs, make_fe, 3) ->
- [t_integer(), t_atom(), t_tuple([t_integer(), t_integer(), t_integer()])];
%% arg_types(hipe_bifs, make_native_stub, 2) ->
%% [t_integer(), t_arity()];
arg_types(hipe_bifs, mark_referred_from, 1) ->
@@ -2485,6 +2488,8 @@ arg_types(hipe_bifs, set_funinfo_native_address, 3) ->
arg_types(hipe_bifs, set_native_address, 3);
arg_types(hipe_bifs, set_native_address, 3) ->
[t_mfa(), t_integer(), t_boolean()];
+arg_types(hipe_bifs, set_native_address_in_fe, 2) ->
+ [t_integer(), t_integer()];
arg_types(hipe_bifs, system_crc, 1) ->
[t_crc32()];
arg_types(hipe_bifs, term_to_word, 1) ->
diff --git a/lib/kernel/src/hipe_unified_loader.erl b/lib/kernel/src/hipe_unified_loader.erl
index e111cb800e..976d5e35cb 100644
--- a/lib/kernel/src/hipe_unified_loader.erl
+++ b/lib/kernel/src/hipe_unified_loader.erl
@@ -203,6 +203,7 @@ load_common(Mod, Bin, Beam, OldReferencesToPatch) ->
"please regenerate native code for this runtime system\n", [Mod]),
bad_crc;
true ->
+ put(closures_to_patch, []),
%% Create data segment
{ConstAddr,ConstMap2} =
create_data_segment(ConstAlign, ConstSize, ConstMap),
@@ -224,14 +225,23 @@ load_common(Mod, Bin, Beam, OldReferencesToPatch) ->
%% Patch all dynamic references in the code.
%% Function calls, Atoms, Constants, System calls
ok = patch(Refs, CodeAddress, ConstMap2, Addresses, TrampolineMap),
+
%% Tell the system where the loaded funs are.
%% (patches the BEAM code to redirect to native.)
case Beam of
[] ->
+ %% This module was previously loaded as BEAM code during system
+ %% start-up before the code server has started (-enable-native-libs
+ %% is active), so we must now patch the pre-existing entries in the
+ %% fun table with the native code addresses for all closures.
+ lists:foreach(fun({FE, DestAddress}) ->
+ hipe_bifs:set_native_address_in_fe(FE, DestAddress)
+ end, erase(closures_to_patch)),
export_funs(Addresses),
ok;
BeamBinary when is_binary(BeamBinary) ->
%% Find all closures in the code.
+ [] = erase(closures_to_patch), %Clean up, assertion.
ClosurePatches = find_closure_patches(Refs),
AddressesOfClosuresToPatch =
calculate_addresses(ClosurePatches, CodeAddress, Addresses),
@@ -245,6 +255,9 @@ load_common(Mod, Bin, Beam, OldReferencesToPatch) ->
%% The call to export_funs/1 above updated the native addresses
%% for the targets, so passing 'Addresses' is not needed.
redirect(ReferencesToPatch),
+ %% Final clean up.
+ _ = erase(hipe_patch_closures),
+ _ = erase(hipe_assert_code_area),
?debug_msg("****************Loader Finished****************\n", []),
{module,Mod} % for compatibility with code:load_file/1
end.
@@ -562,12 +575,17 @@ patch_closure(DestMFA, Uniq, Index, Address, Addresses) ->
case get(hipe_patch_closures) of
false ->
[]; % This is taken care of when registering the module.
- true -> % We are not loading a module patch these closures
+ true ->
+ %% We are replacing a previosly loaded BEAM module with native code,
+ %% so we must reference the pre-existing entries in the fun table
+ %% from the native code. We must delay actually patching the native
+ %% address into the fun entry to ensure that the native code cannot
+ %% be called until it has been completely fixed up.
RemoteOrLocal = local, % closure code refs are local
DestAddress = get_native_address(DestMFA, Addresses, RemoteOrLocal),
BEAMAddress = hipe_bifs:fun_to_address(DestMFA),
- FE = hipe_bifs:make_fe(DestAddress, mod(DestMFA),
- {Uniq, Index, BEAMAddress}),
+ FE = hipe_bifs:get_fe(mod(DestMFA), {Uniq, Index, BEAMAddress}),
+ put(closures_to_patch, [{FE,DestAddress}|get(closures_to_patch)]),
?debug_msg("Patch FE(~w) to 0x~.16b->0x~.16b (emu:0x~.16b)\n",
[DestMFA, FE, DestAddress, BEAMAddress]),
?ASSERT(assert_local_patch(Address)),
diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml
index 910dca3889..4bc1a9a644 100644
--- a/lib/ssl/doc/src/ssl.xml
+++ b/lib/ssl/doc/src/ssl.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1999</year><year>2013</year>
+ <year>1999</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -184,12 +184,6 @@
<item> The DER encoded trusted certificates. If this option
is supplied it will override the cacertfile option.</item>
- <tag>{cacertfile, path()}</tag>
- <item>Path to file containing PEM encoded
- CA certificates (trusted certificates used for verifying a peer
- certificate). May be omitted if you do not want to verify
- the peer.</item>
-
<tag>{ciphers, ciphers()}</tag>
<item>The cipher suites that should be supported. The function
<c>cipher_suites/0</c> can be used to find all ciphers that are
@@ -354,7 +348,13 @@ fun(srp, Username :: string(), UserState :: term()) ->
<item>Specifies if client should try to reuse sessions
when possible.
</item>
-
+
+ <tag>{cacertfile, path()}</tag>
+ <item>The path to a file containing PEM encoded CA certificates. The CA
+ certificates are used during server authentication and when building the
+ client certificate chain.
+ </item>
+
<tag>{client_preferred_next_protocols, {Precedence :: server | client, ClientPrefs :: [binary()]}}</tag>
<tag>{client_preferred_next_protocols, {Precedence :: server | client, ClientPrefs :: [binary()], Default :: binary()}}</tag>
<item>
@@ -403,7 +403,17 @@ fun(srp, Username :: string(), UserState :: term()) ->
meaning in the server than in the client.</p>
<taglist>
-
+
+ <tag>{cacertfile, path()}</tag>
+ <item>The path to a file containing PEM encoded CA
+ certificates. The CA certificates are used to build the server
+ certificate chain, and for client authentication. Also the CAs
+ are used in the list of acceptable client CAs passed to the
+ client when a certificate is requested. May be omitted if there
+ is no need to verify the client and if there are not any
+ intermediate CAs for the server certificate.
+ </item>
+
<tag>{dh, der_encoded()}</tag>
<item>The DER encoded Diffie Hellman parameters. If this option
is supplied it will override the dhfile option.
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index c3bdeb1a54..9e098e12c4 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -557,6 +557,7 @@ do_connect(Address, Port,
handle_options(Opts0, _Role) ->
Opts = proplists:expand([{binary, [{mode, binary}]},
{list, [{mode, list}]}], Opts0),
+ assert_proplist(Opts),
ReuseSessionFun = fun(_, _, _, _) -> true end,
DefaultVerifyNoneFun =
@@ -1042,3 +1043,15 @@ connection_sup(dtls_connection) ->
binary_filename(FileName) ->
Enc = file:native_name_encoding(),
unicode:characters_to_binary(FileName, unicode, Enc).
+
+assert_proplist([]) ->
+ true;
+assert_proplist([{Key,_} | Rest]) when is_atom(Key) ->
+ assert_proplist(Rest);
+%% Handle exceptions
+assert_proplist([inet | Rest]) ->
+ assert_proplist(Rest);
+assert_proplist([inet6 | Rest]) ->
+ assert_proplist(Rest);
+assert_proplist([Value | _]) ->
+ throw({option_not_a_key_value_tuple, Value}).
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
index 523760aba6..64a93440c7 100644
--- a/lib/ssl/test/ssl_basic_SUITE.erl
+++ b/lib/ssl/test/ssl_basic_SUITE.erl
@@ -96,6 +96,7 @@ basic_tests() ->
options_tests() ->
[der_input,
misc_ssl_options,
+ ssl_options_not_proplist,
socket_options,
invalid_inet_get_option,
invalid_inet_get_option_not_list,
@@ -990,7 +991,7 @@ misc_ssl_options(Config) when is_list(Config) ->
ServerOpts = ?config(server_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- %% Chek that ssl options not tested elsewhere are filtered away e.i. not passed to inet.
+ %% Check that ssl options not tested elsewhere are filtered away e.i. not passed to inet.
TestOpts = [{depth, 1},
{key, undefined},
{password, []},
@@ -1018,6 +1019,17 @@ misc_ssl_options(Config) when is_list(Config) ->
ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
+ssl_options_not_proplist() ->
+ [{doc,"Test what happens if an option is not a key value tuple"}].
+
+ssl_options_not_proplist(Config) when is_list(Config) ->
+ BadOption = {client_preferred_next_protocols,
+ client, [<<"spdy/3">>,<<"http/1.1">>], <<"http/1.1">>},
+ {option_not_a_key_value_tuple, BadOption} =
+ ssl:connect("twitter.com", 443, [binary, {active, false},
+ BadOption]).
+
+%%--------------------------------------------------------------------
versions() ->
[{doc,"Test API function versions/0"}].
diff --git a/lib/stdlib/doc/src/supervisor.xml b/lib/stdlib/doc/src/supervisor.xml
index 8197684d2d..3a5027d595 100644
--- a/lib/stdlib/doc/src/supervisor.xml
+++ b/lib/stdlib/doc/src/supervisor.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2013</year>
+ <year>1996</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -262,12 +262,12 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules}
locally as <c>Name</c> using <c>register/2</c>. If
<c><anno>SupName</anno>={global,Name}</c> the supervisor is registered
globally as <c>Name</c> using <c>global:register_name/2</c>. If
- <c><anno>SupName</anno>={via,Module,Name}</c> the supervisor
+ <c><anno>SupName</anno>={via,<anno>Module</anno>,<anno>Name</anno>}</c> the supervisor
is registered as <c>Name</c> using the registry represented by
<c>Module</c>. The <c>Module</c> callback should export the functions
<c>register_name/2</c>, <c>unregister_name/1</c> and <c>send/2</c>,
which should behave like the corresponding functions in <c>global</c>.
- Thus, <c>{via,global,Name}</c> is a valid reference.</p>
+ Thus, <c>{via,global,<anno>Name</anno>}</c> is a valid reference.</p>
<p>If no name is provided, the supervisor is not registered.</p>
<p><c><anno>Module</anno></c> is the name of the callback module.</p>
<p><c><anno>Args</anno></c> is an arbitrary term which is passed as
diff --git a/lib/stdlib/src/dets.erl b/lib/stdlib/src/dets.erl
index 44dad04f43..c32da1624f 100644
--- a/lib/stdlib/src/dets.erl
+++ b/lib/stdlib/src/dets.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-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
@@ -1785,6 +1785,7 @@ read_file_header(FileName, Access, RamFile) ->
Version =:= 9 ->
dets_v9:read_file_header(Fd, FileName);
true ->
+ _ = file:close(Fd),
throw({error, {not_a_dets_file, FileName}})
end.
@@ -2113,6 +2114,8 @@ test_bchunk_format(Head, Term) ->
do_open_file([Fname, Verbose], Parent, Server, Ref) ->
case catch fopen2(Fname, Ref) of
+ {error, {tooshort, _}} ->
+ err({error, {not_a_dets_file, Fname}});
{error, _Reason} = Error ->
err(Error);
{ok, Head} ->
@@ -2126,11 +2129,10 @@ do_open_file([Fname, Verbose], Parent, Server, Ref) ->
[Bad]),
{error, {dets_bug, Fname, Bad}}
end;
-do_open_file([Tab, OpenArgs, Verb], Parent, Server, Ref) ->
+do_open_file([Tab, OpenArgs, Verb], Parent, Server, _Ref) ->
case catch fopen3(Tab, OpenArgs) of
{error, {tooshort, _}} ->
- _ = file:delete(OpenArgs#open_args.file),
- do_open_file([Tab, OpenArgs, Verb], Parent, Server, Ref);
+ err({error, {not_a_dets_file, OpenArgs#open_args.file}});
{error, _Reason} = Error ->
err(Error);
{ok, Head} ->
@@ -2486,7 +2488,6 @@ fopen2(Fname, Tab) ->
{ok, _} ->
Acc = read_write,
Ram = false,
- %% Fd is not always closed upon error, but exit is soon called.
{ok, Fd, FH} = read_file_header(Fname, Acc, Ram),
Mod = FH#fileheader.mod,
Do = case Mod:check_file_header(FH, Fd) of
@@ -2542,7 +2543,6 @@ fopen_existing_file(Tab, OpenArgs) ->
ram_file = Ram, delayed_write = CacheSz, auto_save =
Auto, access = Acc, version = Version, debug = Debug} =
OpenArgs,
- %% Fd is not always closed upon error, but exit is soon called.
{ok, Fd, FH} = read_file_header(Fname, Acc, Ram),
V9 = (Version =:= 9) or (Version =:= default),
MinF = (MinSlots =:= default) or (MinSlots =:= FH#fileheader.min_no_slots),
diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl
index 9f5be2da37..1118ff9210 100644
--- a/lib/stdlib/src/erl_lint.erl
+++ b/lib/stdlib/src/erl_lint.erl
@@ -344,10 +344,19 @@ format_error(spec_wrong_arity) ->
"spec has the wrong arity";
format_error(callback_wrong_arity) ->
"callback has the wrong arity";
-format_error({deprecated_type, {Name, Arity}, {Mod, NewName}, Rel}) ->
+format_error({deprecated_builtin_type, {Name, Arity},
+ Replacement, Rel}) ->
+ UseS = case Replacement of
+ {Mod, NewName} ->
+ io_lib:format("use ~w:~w/~w", [Mod, NewName, Arity]);
+ {Mod, NewName, NewArity} ->
+ io_lib:format("use ~w:~w/~w or preferably ~w:~w/~w",
+ [Mod, NewName, Arity,
+ Mod, NewName, NewArity])
+ end,
io_lib:format("type ~w/~w is deprecated and will be "
- "removed in ~s; use ~w:~w/~w",
- [Name, Arity, Rel, Mod, NewName, Arity]);
+ "removed in ~s; use ~s",
+ [Name, Arity, Rel, UseS]);
format_error({not_exported_opaque, {TypeName, Arity}}) ->
io_lib:format("opaque type ~w~s is not exported",
[TypeName, gen_type_paren(Arity)]);
@@ -499,6 +508,9 @@ start(File, Opts) ->
{deprecated_function,
bool_option(warn_deprecated_function, nowarn_deprecated_function,
true, Opts)},
+ {deprecated_type,
+ bool_option(warn_deprecated_type, nowarn_deprecated_type,
+ true, Opts)},
{obsolete_guard,
bool_option(warn_obsolete_guard, nowarn_obsolete_guard,
true, Opts)},
@@ -1373,18 +1385,19 @@ pattern({cons,_Line,H,T}, Vt, Old, Bvt, St0) ->
pattern({tuple,_Line,Ps}, Vt, Old, Bvt, St) ->
pattern_list(Ps, Vt, Old, Bvt, St);
pattern({map,_Line,Ps}, Vt, Old, Bvt, St) ->
- pattern_list(Ps, Vt, Old, Bvt, St);
-pattern({map_field_assoc,Line,_,_}, _, _, _, St) ->
- {[],[],add_error(Line, illegal_pattern, St)};
-pattern({map_field_exact,Line,KP,VP}, Vt, Old, Bvt0, St0) ->
- %% if the key pattern has variables we should fail
- case expr(KP,[],St0) of
- {[],_} ->
- pattern(VP, Vt, Old, Bvt0, St0);
- {[Var|_],_} ->
- %% found variables in key expression
- {Vt,Old,add_error(Line,{illegal_map_key_variable,element(1,Var)},St0)}
- end;
+ foldl(fun ({map_field_assoc,L,_,_}, {Psvt,Bvt0,St0}) ->
+ {Psvt,Bvt0,add_error(L, illegal_pattern, St0)};
+ ({map_field_exact,L,KP,VP}, {Psvt,Bvt0,St0}) ->
+ case expr(KP, [], St0) of
+ {[],_} ->
+ {Pvt,Bvt1,St1} = pattern(VP, Vt, Old, Bvt, St0),
+ {vtmerge_pat(Pvt, Psvt),vtmerge_pat(Bvt0, Bvt1),
+ St1};
+ {[Var|_],_} ->
+ Error = {illegal_map_key_variable,element(1, Var)},
+ {Psvt,Bvt0,add_error(L, Error, St0)}
+ end
+ end, {[],[],St}, Ps);
%%pattern({struct,_Line,_Tag,Ps}, Vt, Old, Bvt, St) ->
%% pattern_list(Ps, Vt, Old, Bvt, St);
pattern({record_index,Line,Name,Field}, _Vt, _Old, _Bvt, St) ->
@@ -1773,13 +1786,11 @@ gexpr({cons,_Line,H,T}, Vt, St) ->
gexpr({tuple,_Line,Es}, Vt, St) ->
gexpr_list(Es, Vt, St);
gexpr({map,_Line,Es}, Vt, St) ->
- gexpr_list(Es, Vt, St);
+ map_fields(Es, Vt, check_assoc_fields(Es, St), fun gexpr_list/3);
gexpr({map,_Line,Src,Es}, Vt, St) ->
- gexpr_list([Src|Es], Vt, St);
-gexpr({map_field_assoc,_Line,K,V}, Vt, St) ->
- gexpr_list([K,V], Vt, St);
-gexpr({map_field_exact,_Line,K,V}, Vt, St) ->
- gexpr_list([K,V], Vt, St);
+ {Svt,St1} = gexpr(Src, Vt, St),
+ {Fvt,St2} = map_fields(Es, Vt, St1, fun gexpr_list/3),
+ {vtmerge(Svt, Fvt),St2};
gexpr({record_index,Line,Name,Field}, _Vt, St) ->
check_record(Line, Name, St,
fun (Dfs, St1) -> record_field(Field, Name, Dfs, St1) end );
@@ -1852,6 +1863,10 @@ gexpr({op,Line,Op,A}, Vt, St0) ->
true -> {Avt,St1};
false -> {Avt,add_error(Line, illegal_guard_expr, St1)}
end;
+gexpr({op,_,'andalso',L,R}, Vt, St) ->
+ gexpr_list([L,R], Vt, St);
+gexpr({op,_,'orelse',L,R}, Vt, St) ->
+ gexpr_list([L,R], Vt, St);
gexpr({op,Line,Op,L,R}, Vt, St0) ->
{Avt,St1} = gexpr_list([L,R], Vt, St0),
case is_gexpr_op(Op, 2) of
@@ -1938,12 +1953,14 @@ is_gexpr({call,L,{tuple,Lt,[{atom,Lm,erlang},{atom,Lf,F}]},As}, RDs) ->
is_gexpr({call,L,{remote,Lt,{atom,Lm,erlang},{atom,Lf,F}},As}, RDs);
is_gexpr({op,_L,Op,A}, RDs) ->
is_gexpr_op(Op, 1) andalso is_gexpr(A, RDs);
+is_gexpr({op,_L,'andalso',A1,A2}, RDs) ->
+ is_gexpr_list([A1,A2], RDs);
+is_gexpr({op,_L,'orelse',A1,A2}, RDs) ->
+ is_gexpr_list([A1,A2], RDs);
is_gexpr({op,_L,Op,A1,A2}, RDs) ->
is_gexpr_op(Op, 2) andalso is_gexpr_list([A1,A2], RDs);
is_gexpr(_Other, _RDs) -> false.
-is_gexpr_op('andalso', 2) -> true;
-is_gexpr_op('orelse', 2) -> true;
is_gexpr_op(Op, A) ->
try erl_internal:op_type(Op, A) of
arith -> true;
@@ -1997,24 +2014,12 @@ expr({bc,_Line,E,Qs}, Vt, St) ->
handle_comprehension(E, Qs, Vt, St);
expr({tuple,_Line,Es}, Vt, St) ->
expr_list(Es, Vt, St);
-expr({map,Line,Es}, Vt, St) ->
- {Rvt,St1} = expr_list(Es,Vt,St),
- case is_valid_map_construction(Es) of
- true -> {Rvt,St1};
- false -> {[],add_error(Line,illegal_map_construction,St1)}
- end;
+expr({map,_Line,Es}, Vt, St) ->
+ map_fields(Es, Vt, check_assoc_fields(Es, St), fun expr_list/3);
expr({map,_Line,Src,Es}, Vt, St) ->
- expr_list([Src|Es], Vt, St);
-expr({map_field_assoc,Line,K,V}, Vt, St) ->
- case is_valid_map_key(K,St) of
- true -> expr_list([K,V], Vt, St);
- {false,Var} -> {[],add_error(Line,{illegal_map_key_variable,Var},St)}
- end;
-expr({map_field_exact,Line,K,V}, Vt, St) ->
- case is_valid_map_key(K,St) of
- true -> expr_list([K,V], Vt, St);
- {false,Var} -> {[],add_error(Line,{illegal_map_key_variable,Var},St)}
- end;
+ {Svt,St1} = expr(Src, Vt, St),
+ {Fvt,St2} = map_fields(Es, Vt, St1, fun expr_list/3),
+ {vtupdate(Svt, Fvt),St2};
expr({record_index,Line,Name,Field}, _Vt, St) ->
check_record(Line, Name, St,
fun (Dfs, St1) -> record_field(Field, Name, Dfs, St1) end);
@@ -2222,6 +2227,25 @@ record_expr(Line, Rec, Vt, St0) ->
St1 = warn_invalid_record(Line, Rec, St0),
expr(Rec, Vt, St1).
+check_assoc_fields([{map_field_exact,Line,_,_}|Fs], St) ->
+ check_assoc_fields(Fs, add_error(Line, illegal_map_construction, St));
+check_assoc_fields([{map_field_assoc,_,_,_}|Fs], St) ->
+ check_assoc_fields(Fs, St);
+check_assoc_fields([], St) ->
+ St.
+
+map_fields([{Tag,Line,K,V}|Fs], Vt, St, F) when Tag =:= map_field_assoc;
+ Tag =:= map_field_exact ->
+ St1 = case is_valid_map_key(K, St) of
+ true -> St;
+ {false,Var} -> add_error(Line, {illegal_map_key_variable,Var}, St)
+ end,
+ {Pvt,St2} = F([K,V], Vt, St1),
+ {Vts,St3} = map_fields(Fs, Vt, St2, F),
+ {vtupdate(Pvt, Vts),St3};
+map_fields([], Vt, St, _) ->
+ {Vt,St}.
+
%% warn_invalid_record(Line, Record, State0) -> State
%% Adds warning if the record is invalid.
@@ -2274,13 +2298,6 @@ is_valid_call(Call) ->
_ -> true
end.
-%% check_map_construction
-%% Only #{ K => V }, i.e. assoc is a valid construction
-is_valid_map_construction([{map_field_assoc,_,_,_}|Es]) ->
- is_valid_map_construction(Es);
-is_valid_map_construction([]) -> true;
-is_valid_map_construction(_) -> false.
-
is_valid_map_key(K,St) ->
case expr(K,[],St) of
{[],_} -> true;
@@ -2637,10 +2654,11 @@ check_type({type, La, TypeName, Args}, SeenVars, St) ->
St1 = case is_var_arity_type(TypeName) of
true -> St;
false ->
- Obsolete = obsolete_type(TypePair),
+ Obsolete = (is_warn_enabled(deprecated_type, St)
+ andalso obsolete_builtin_type(TypePair)),
IsObsolete =
case Obsolete of
- {deprecated, {M, _}, _} when M =/= Module ->
+ {deprecated, Repl, _} when element(1, Repl) =/= Module ->
case dict:find(TypePair, Types) of
{ok, _} -> false;
error -> true
@@ -2650,7 +2668,8 @@ check_type({type, La, TypeName, Args}, SeenVars, St) ->
case IsObsolete of
true ->
{deprecated, Replacement, Rel} = Obsolete,
- W = {deprecated_type, TypePair, Replacement, Rel},
+ Tag = deprecated_builtin_type,
+ W = {Tag, TypePair, Replacement, Rel},
add_warning(La, W, St);
false ->
OldUsed = Usage#usage.used_types,
@@ -2780,15 +2799,23 @@ is_newly_introduced_builtin_type({boolean, 0}) -> true;
is_newly_introduced_builtin_type({Name, _}) when is_atom(Name) -> false.
%% Obsolete in OTP 17.0.
-obsolete_type({array, 0}) -> {deprecated, {array, array}, "OTP 18.0"};
-obsolete_type({dict, 0}) -> {deprecated, {dict, dict}, "OTP 18.0"};
-obsolete_type({digraph, 0}) -> {deprecated, {digraph, graph}, "OTP 18.0"};
-obsolete_type({gb_set, 0}) -> {deprecated, {gb_sets, set}, "OTP 18.0"};
-obsolete_type({gb_tree, 0}) -> {deprecated, {gb_trees, tree}, "OTP 18.0"};
-obsolete_type({queue, 0}) -> {deprecated, {queue, queue}, "OTP 18.0"};
-obsolete_type({set, 0}) -> {deprecated, {sets, set}, "OTP 18.0"};
-obsolete_type({tid, 0}) -> {deprecated, {ets, tid}, "OTP 18.0"};
-obsolete_type({Name, _}) when is_atom(Name) -> no.
+obsolete_builtin_type({array, 0}) ->
+ {deprecated, {array, array, 1}, "OTP 18.0"};
+obsolete_builtin_type({dict, 0}) ->
+ {deprecated, {dict, dict, 2}, "OTP 18.0"};
+obsolete_builtin_type({digraph, 0}) ->
+ {deprecated, {digraph, graph}, "OTP 18.0"};
+obsolete_builtin_type({gb_set, 0}) ->
+ {deprecated, {gb_sets, set, 1}, "OTP 18.0"};
+obsolete_builtin_type({gb_tree, 0}) ->
+ {deprecated, {gb_trees, tree, 2}, "OTP 18.0"};
+obsolete_builtin_type({queue, 0}) ->
+ {deprecated, {queue, queue, 1}, "OTP 18.0"};
+obsolete_builtin_type({set, 0}) ->
+ {deprecated, {sets, set, 1}, "OTP 18.0"};
+obsolete_builtin_type({tid, 0}) ->
+ {deprecated, {ets, tid}, "OTP 18.0"};
+obsolete_builtin_type({Name, _}) when is_atom(Name) -> no.
%% spec_decl(Line, Fun, Types, State) -> State.
diff --git a/lib/stdlib/src/gen.erl b/lib/stdlib/src/gen.erl
index 7281549ea7..63116fa16e 100644
--- a/lib/stdlib/src/gen.erl
+++ b/lib/stdlib/src/gen.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-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
@@ -37,7 +37,9 @@
%%-----------------------------------------------------------------
-type linkage() :: 'link' | 'nolink'.
--type emgr_name() :: {'local', atom()} | {'global', term()} | {via, atom(), term()}.
+-type emgr_name() :: {'local', atom()}
+ | {'global', term()}
+ | {'via', Module :: module(), Name :: term()}.
-type start_ret() :: {'ok', pid()} | 'ignore' | {'error', term()}.
diff --git a/lib/stdlib/src/qlc_pt.erl b/lib/stdlib/src/qlc_pt.erl
index c26764eb18..b6bb758dfb 100644
--- a/lib/stdlib/src/qlc_pt.erl
+++ b/lib/stdlib/src/qlc_pt.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2004-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
@@ -1218,13 +1218,14 @@ lu_skip(ColConstants, FilterData, PatternFrame, PatternVars,
%% column, the filter will not be skipped.
%% (an example: {X=1} <- ..., X =:= 1).
length(D = Cols -- PatternColumns) =:= 1,
- Frame <- SFs,
- begin
+ {{_,Col} = Column, Constants} <- D,
+ %% Check that the following holds for all frames.
+ lists:all(
+ fun(Frame) ->
%% The column is compared/matched against a constant.
%% If there are no more comparisons/matches then
%% the filter can be replaced by the lookup of
%% the constant.
- [{{_,Col} = Column, Constants}] = D,
{VarI, FrameI} = unify_column(Frame, PV, Col, BindFun,
Imported),
VarValues = deref_skip(VarI, FrameI, LookupOp, Imported),
@@ -1253,7 +1254,7 @@ lu_skip(ColConstants, FilterData, PatternFrame, PatternVars,
length(VarValues) =< 1 andalso
(Constants -- LookedUpConstants =:= []) andalso
bindings_is_subset(Frame, F2, Imported)
- end],
+ end, SFs)],
ColFils = family_list(ColFil),
%% The skip tag 'all' means that all filters are covered by the lookup.
%% It does not imply that there is only one generator as is the case
diff --git a/lib/stdlib/test/dets_SUITE.erl b/lib/stdlib/test/dets_SUITE.erl
index 059d553b00..00a5da42ad 100644
--- a/lib/stdlib/test/dets_SUITE.erl
+++ b/lib/stdlib/test/dets_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-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
@@ -52,7 +52,7 @@
simultaneous_open/1, insert_new/1, repair_continuation/1,
otp_5487/1, otp_6206/1, otp_6359/1, otp_4738/1, otp_7146/1,
otp_8070/1, otp_8856/1, otp_8898/1, otp_8899/1, otp_8903/1,
- otp_8923/1, otp_9282/1, otp_11245/1]).
+ otp_8923/1, otp_9282/1, otp_11245/1, otp_11709/1]).
-export([dets_dirty_loop/0]).
@@ -109,7 +109,7 @@ all() ->
many_clients, otp_4906, otp_5402, simultaneous_open,
insert_new, repair_continuation, otp_5487, otp_6206,
otp_6359, otp_4738, otp_7146, otp_8070, otp_8856, otp_8898,
- otp_8899, otp_8903, otp_8923, otp_9282, otp_11245
+ otp_8899, otp_8903, otp_8923, otp_9282, otp_11245, otp_11709
].
groups() ->
@@ -772,9 +772,9 @@ open_1(Config, V) ->
crash(Fname, TypePos),
{error, {invalid_type_code,Fname}} = dets:open_file(Fname),
truncate(Fname, HeadSize - 10),
- {error, {tooshort,Fname}} = dets:open_file(Fname),
- {ok, TabRef} = dets:open_file(TabRef, [{file,Fname},{version,V}]),
- ok = dets:close(TabRef),
+ {error,{not_a_dets_file,Fname}} = dets:open_file(Fname),
+ {error,{not_a_dets_file,Fname}} =
+ dets:open_file(TabRef, [{file,Fname},{version,V}]),
file:delete(Fname),
{error,{file_error,{foo,bar},_}} = dets:is_dets_file({foo,bar}),
@@ -967,10 +967,12 @@ fast_init_table(Config) ->
{'EXIT', _} =
(catch dets:init_table(TabRef, fun(foo) -> bar end, {format,bchunk})),
dets:close(TabRef),
+ file:delete(Fname),
{ok, _} = dets:open_file(TabRef, Args),
{'EXIT', _} = (catch dets:init_table(TabRef, fun() -> foo end,
{format,bchunk})),
dets:close(TabRef),
+ file:delete(Fname),
{ok, _} = dets:open_file(TabRef, Args),
{'EXIT', {badarg, _}} =
(catch dets:init_table(TabRef, nofun, {format,bchunk})),
@@ -979,10 +981,12 @@ fast_init_table(Config) ->
away = (catch dets:init_table(TabRef, fun(_) -> throw(away) end,
{format,bchunk})),
dets:close(TabRef),
+ file:delete(Fname),
{ok, _} = dets:open_file(TabRef, Args),
{error, {init_fun, fopp}} =
dets:init_table(TabRef, fun(read) -> fopp end, {format,bchunk}),
dets:close(TabRef),
+ file:delete(Fname),
{ok, _} = dets:open_file(TabRef, Args),
dets:safe_fixtable(TabRef, true),
{error, {fixed_table, TabRef}} =
@@ -1389,23 +1393,6 @@ repair(Config, V) ->
{ok, TabRef} = dets:open_file(TabRef, [{file,Fname},{version,V}]),
ok = ins(TabRef, 100),
ok = dets:close(TabRef),
- truncate(Fname, HeadSize - 10),
- %% a new file is created ('tooshort')
- {ok, TabRef} = dets:open_file(TabRef,
- [{file,Fname},{version,V},
- {min_no_slots,1000},
- {max_no_slots,1000000}]),
- case dets:info(TabRef, no_slots) of
- undefined -> ok;
- {Min1,Slot1,Max1} ->
- true = Min1 =< Slot1, true = Slot1 =< Max1,
- true = 1000 < Min1, true = 1000+256 > Min1,
- true = 1000000 < Max1, true = (1 bsl 20)+256 > Max1
- end,
- 0 = dets:info(TabRef, size),
- no_keys_test(TabRef),
- _ = histogram(TabRef, silent),
- ok = dets:close(TabRef),
file:delete(Fname),
%% version bump (v8)
@@ -3920,6 +3907,48 @@ otp_11245(Config) when is_list(Config) ->
file:delete(File),
ok.
+otp_11709(doc) ->
+ ["OTP-11709. Bugfixes."];
+otp_11709(suite) ->
+ [];
+otp_11709(Config) when is_list(Config) ->
+ Short = <<"foo">>,
+ Long = <<"a sufficiently long text">>,
+
+ %% Bug: leaking file descriptor
+ P0 = pps(),
+ File = filename(otp_11709, Config),
+ ok = file:write_file(File, Long),
+ false = dets:is_dets_file(File),
+ check_pps(P0),
+
+ %% Bug: deleting file
+ Args = [[{access, A}, {repair, R}] ||
+ A <- [read, read_write],
+ R <- [true, false, force]],
+ Fun1 = fun(S, As) ->
+ P1 = pps(),
+ ok = file:write_file(File, S),
+ {error,{not_a_dets_file,File}} = dets:open_file(File, As),
+ {ok, S} = file:read_file(File),
+ check_pps(P1)
+ end,
+ Fun2 = fun(S) ->
+ _ = [Fun1(S, As) || As <- Args],
+ ok
+ end,
+ ok = Fun2(Long), % no change here
+ ok = Fun2(Short), % mimic the behaviour for longer files
+
+ %% open_file/1
+ ok = file:write_file(File, Long),
+ {error,{not_a_dets_file,File}} = dets:open_file(File), % no change
+ ok = file:write_file(File, Short),
+ {error,{not_a_dets_file,File}} = dets:open_file(File), % mimic
+
+ _ = file:delete(File),
+ ok.
+
%%
%% Parts common to several test cases
%%
diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl
index 1614a2722f..67110f0ae2 100644
--- a/lib/stdlib/test/erl_lint_SUITE.erl
+++ b/lib/stdlib/test/erl_lint_SUITE.erl
@@ -61,7 +61,8 @@
on_load_successful/1, on_load_failing/1,
too_many_arguments/1,
basic_errors/1,bin_syntax_errors/1,
- predef/1
+ predef/1,
+ maps/1
]).
% Default timetrap timeout (set in init_per_testcase).
@@ -88,7 +89,7 @@ all() ->
otp_5878, otp_5917, otp_6585, otp_6885, otp_10436, otp_11254,export_all,
bif_clash, behaviour_basic, behaviour_multiple,
otp_7550, otp_8051, format_warn, {group, on_load},
- too_many_arguments, basic_errors, bin_syntax_errors, predef].
+ too_many_arguments, basic_errors, bin_syntax_errors, predef, maps].
groups() ->
[{unused_vars_warn, [],
@@ -1494,7 +1495,15 @@ guard(Config) when is_list(Config) ->
[],
{errors,[{1,erl_lint,illegal_guard_expr},
{2,erl_lint,illegal_guard_expr},
- {3,erl_lint,illegal_guard_expr}],[]}}
+ {3,erl_lint,illegal_guard_expr}],[]}},
+ {guard9,
+ <<"t(X, Y) when erlang:'andalso'(X, Y) -> ok;
+ t(X, Y) when erlang:'orelse'(X, Y) -> ok.
+ ">>,
+ [],
+ {errors,[{1,erl_lint,illegal_guard_expr},
+ {2,erl_lint,illegal_guard_expr}],
+ []}}
],
?line [] = run(Config, Ts1),
ok.
@@ -3243,20 +3252,88 @@ bin_syntax_errors(Config) ->
ok.
predef(doc) ->
- "Predefined types: array(), digraph(), and so on";
+ "OTP-10342: Predefined types: array(), digraph(), and so on";
predef(suite) -> [];
predef(Config) when is_list(Config) ->
W = get_compilation_warnings(Config, "predef", []),
[] = W,
W2 = get_compilation_warnings(Config, "predef2", []),
- [{7,erl_lint,{deprecated_type,{array,0},{array,array},"OTP 18.0"}},
- {12,erl_lint,{deprecated_type,{dict,0},{dict,dict},"OTP 18.0"}},
- {17,erl_lint,{deprecated_type,{digraph,0},{digraph,graph},"OTP 18.0"}},
- {27,erl_lint,{deprecated_type,{gb_set,0},{gb_sets,set},"OTP 18.0"}},
- {32,erl_lint,{deprecated_type,{gb_tree,0},{gb_trees,tree},"OTP 18.0"}},
- {37,erl_lint,{deprecated_type,{queue,0},{queue,queue},"OTP 18.0"}},
- {42,erl_lint,{deprecated_type,{set,0},{sets,set},"OTP 18.0"}},
- {47,erl_lint,{deprecated_type,{tid,0},{ets,tid},"OTP 18.0"}}] = W2,
+ Tag = deprecated_builtin_type,
+ [{7,erl_lint,{Tag,{array,0},{array,array,1},"OTP 18.0"}},
+ {12,erl_lint,{Tag,{dict,0},{dict,dict,2},"OTP 18.0"}},
+ {17,erl_lint,{Tag,{digraph,0},{digraph,graph},"OTP 18.0"}},
+ {27,erl_lint,{Tag,{gb_set,0},{gb_sets,set,1},"OTP 18.0"}},
+ {32,erl_lint,{Tag,{gb_tree,0},{gb_trees,tree,2},"OTP 18.0"}},
+ {37,erl_lint,{Tag,{queue,0},{queue,queue,1},"OTP 18.0"}},
+ {42,erl_lint,{Tag,{set,0},{sets,set,1},"OTP 18.0"}},
+ {47,erl_lint,{Tag,{tid,0},{ets,tid},"OTP 18.0"}}] = W2,
+ Ts = [{otp_10342_1,
+ <<"-compile(nowarn_deprecated_type).
+
+ -spec t(dict()) -> non_neg_integer().
+
+ t(D) ->
+ erlang:phash2(D, 3000).
+ ">>,
+ {[nowarn_unused_function]},
+ []},
+ {otp_10342_2,
+ <<"-spec t(dict()) -> non_neg_integer().
+
+ t(D) ->
+ erlang:phash2(D, 3000).
+ ">>,
+ {[nowarn_unused_function]},
+ {warnings,[{1,erl_lint,
+ {deprecated_builtin_type,{dict,0},{dict,dict,2},
+ "OTP 18.0"}}]}}],
+ [] = run(Config, Ts),
+ ok.
+
+maps(Config) ->
+ %% TODO: test key patterns, not done because map patterns are going to be
+ %% changed a lot.
+ Ts = [{illegal_map_construction,
+ <<"t() ->
+ #{ a := b,
+ c => d,
+ e := f
+ }#{ a := b,
+ c => d,
+ e := f };
+ t() when is_map(#{ a := b,
+ c => d
+ }#{ a := b,
+ c => d,
+ e := f }) ->
+ ok.
+ ">>,
+ [],
+ {errors,[{2,erl_lint,illegal_map_construction},
+ {4,erl_lint,illegal_map_construction},
+ {8,erl_lint,illegal_map_construction}],
+ []}},
+ {illegal_pattern,
+ <<"t(#{ a := A,
+ c => d,
+ e := F,
+ g := 1 + 1,
+ h := _,
+ i := (_X = _Y),
+ j := (x ! y) }) ->
+ {A,F}.
+ ">>,
+ [],
+ {errors,[{2,erl_lint,illegal_pattern},
+ {7,erl_lint,illegal_pattern}],
+ []}},
+ {error_in_illegal_map_construction,
+ <<"t() -> #{ a := X }.">>,
+ [],
+ {errors,[{1,erl_lint,illegal_map_construction},
+ {1,erl_lint,{unbound_var,'X'}}],
+ []}}],
+ [] = run(Config, Ts),
ok.
run(Config, Tests) ->
diff --git a/lib/stdlib/test/erl_lint_SUITE_data/predef.erl b/lib/stdlib/test/erl_lint_SUITE_data/predef.erl
index c2364fd1c2..ee9073aa67 100644
--- a/lib/stdlib/test/erl_lint_SUITE_data/predef.erl
+++ b/lib/stdlib/test/erl_lint_SUITE_data/predef.erl
@@ -5,8 +5,8 @@
-export_type([array/0, digraph/0, gb_set/0]).
-%% Before R17B local re-definitions of pre-defined opaque types were
-%% ignored but did not generate any warning.
+%% Before Erlang/OTP 17.0 local re-definitions of pre-defined opaque
+%% types were ignored but did not generate any warning.
-opaque array() :: atom().
-opaque digraph() :: atom().
-opaque gb_set() :: atom().
diff --git a/lib/stdlib/test/qlc_SUITE.erl b/lib/stdlib/test/qlc_SUITE.erl
index 2846657c09..37fbb5267b 100644
--- a/lib/stdlib/test/qlc_SUITE.erl
+++ b/lib/stdlib/test/qlc_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2004-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
@@ -72,7 +72,7 @@
otp_5644/1, otp_5195/1, otp_6038_bug/1, otp_6359/1, otp_6562/1,
otp_6590/1, otp_6673/1, otp_6964/1, otp_7114/1, otp_7238/1,
- otp_7232/1, otp_7552/1, otp_6674/1, otp_7714/1,
+ otp_7232/1, otp_7552/1, otp_6674/1, otp_7714/1, otp_11758/1,
manpage/1,
@@ -142,7 +142,7 @@ groups() ->
{tickets, [],
[otp_5644, otp_5195, otp_6038_bug, otp_6359, otp_6562,
otp_6590, otp_6673, otp_6964, otp_7114, otp_7232,
- otp_7238, otp_7552, otp_6674, otp_7714]},
+ otp_7238, otp_7552, otp_6674, otp_7714, otp_11758]},
{compat, [], [backward, forward]}].
init_per_suite(Config) ->
@@ -6670,6 +6670,19 @@ otp_7714(Config) when is_list(Config) ->
ets:delete(E2)">>],
?line run(Config, Ts).
+otp_11758(doc) ->
+ "OTP-11758. Bug.";
+otp_11758(suite) -> [];
+otp_11758(Config) when is_list(Config) ->
+ Ts = [<<"T = ets:new(r, [{keypos, 2}]),
+ L = [{rrr, xxx, aaa}, {rrr, yyy, bbb}],
+ true = ets:insert(T, L),
+ QH = qlc:q([{rrr, B, C} || {rrr, B, C} <- ets:table(T),
+ (B =:= xxx) or (B =:= yyy) and (C =:= aaa)]),
+ [{rrr,xxx,aaa}] = qlc:e(QH),
+ ets:delete(T)">>],
+ run(Config, Ts).
+
otp_6674(doc) ->
"OTP-6674. match/comparison.";
otp_6674(suite) -> [];
diff --git a/lib/tools/src/cover.erl b/lib/tools/src/cover.erl
index a24d70ed92..113fa24bd5 100644
--- a/lib/tools/src/cover.erl
+++ b/lib/tools/src/cover.erl
@@ -1764,6 +1764,25 @@ munge_expr({record,Line,Arg,Name,Exprs}, Vars) ->
munge_expr({record_field,Line,ExprL,ExprR}, Vars) ->
{MungedExprR, Vars2} = munge_expr(ExprR, Vars),
{{record_field,Line,ExprL,MungedExprR}, Vars2};
+munge_expr({map,Line,Fields}, Vars) ->
+ %% EEP 43
+ {MungedFields, Vars2} = munge_exprs(Fields, Vars, []),
+ {{map,Line,MungedFields}, Vars2};
+munge_expr({map,Line,Arg,Fields}, Vars) ->
+ %% EEP 43
+ {MungedArg, Vars2} = munge_expr(Arg, Vars),
+ {MungedFields, Vars3} = munge_exprs(Fields, Vars2, []),
+ {{map,Line,MungedArg,MungedFields}, Vars3};
+munge_expr({map_field_assoc,Line,Name,Value}, Vars) ->
+ %% EEP 43
+ {MungedName, Vars2} = munge_expr(Name, Vars),
+ {MungedValue, Vars3} = munge_expr(Value, Vars2),
+ {{map_field_assoc,Line,MungedName,MungedValue}, Vars3};
+munge_expr({map_field_exact,Line,Name,Value}, Vars) ->
+ %% EEP 43
+ {MungedName, Vars2} = munge_expr(Name, Vars),
+ {MungedValue, Vars3} = munge_expr(Value, Vars2),
+ {{map_field_exact,Line,MungedName,MungedValue}, Vars3};
munge_expr({cons,Line,ExprH,ExprT}, Vars) ->
{MungedExprH, Vars2} = munge_expr(ExprH, Vars),
{MungedExprT, Vars3} = munge_expr(ExprT, Vars2),