aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/compiler/src/beam_ssa_opt.erl93
-rw-r--r--lib/compiler/src/beam_ssa_type.erl187
-rw-r--r--lib/compiler/src/erl_bifs.erl16
-rw-r--r--lib/compiler/src/v3_core.erl22
-rw-r--r--lib/kernel/test/logger_disk_log_h_SUITE.erl2
-rw-r--r--lib/observer/test/crashdump_helper.erl2
-rw-r--r--lib/reltool/test/reltool_server_SUITE.erl128
-rw-r--r--lib/reltool/test/reltool_test_lib.erl3
-rw-r--r--lib/reltool/test/reltool_wx_SUITE.erl7
-rw-r--r--lib/sasl/test/test_lib.hrl4
-rw-r--r--lib/ssl/doc/src/ssl.xml51
-rw-r--r--lib/ssl/src/ssl.erl21
-rw-r--r--lib/ssl/test/ssl_bench_SUITE.erl37
-rw-r--r--lib/stdlib/src/erl_pp.erl4
-rw-r--r--lib/stdlib/test/erl_pp_SUITE.erl11
15 files changed, 374 insertions, 214 deletions
diff --git a/lib/compiler/src/beam_ssa_opt.erl b/lib/compiler/src/beam_ssa_opt.erl
index 0f03d0e736..f8e19d0aa7 100644
--- a/lib/compiler/src/beam_ssa_opt.erl
+++ b/lib/compiler/src/beam_ssa_opt.erl
@@ -164,12 +164,14 @@ repeated_passes(Opts) ->
epilogue_passes(Opts) ->
Ps = [?PASS(ssa_opt_type_finish),
?PASS(ssa_opt_float),
- ?PASS(ssa_opt_live), %One last time to clean up the
- %mess left by the float pass.
+ ?PASS(ssa_opt_sw),
+
+ %% Run live one more time to clean up after the float and sw
+ %% passes.
+ ?PASS(ssa_opt_live),
?PASS(ssa_opt_bsm),
?PASS(ssa_opt_bsm_units),
?PASS(ssa_opt_bsm_shortcut),
- ?PASS(ssa_opt_sw),
?PASS(ssa_opt_blockify),
?PASS(ssa_opt_sink),
?PASS(ssa_opt_merge_blocks),
@@ -1830,12 +1832,16 @@ opt_tup_size_is([], _, _, _Acc) -> none.
%%%
ssa_opt_sw({#st{ssa=Linear0,cnt=Count0}=St, FuncDb}) ->
- {Linear,Count} = opt_sw(Linear0, #{}, Count0, []),
+ {Linear,Count} = opt_sw(Linear0, Count0, []),
{St#st{ssa=Linear,cnt=Count}, FuncDb}.
-opt_sw([{L,#b_blk{is=Is,last=#b_switch{}=Last0}=Blk0}|Bs], Phis0, Count0, Acc) ->
- Phis = opt_sw_phis(Is, Phis0),
- case opt_sw_last(Last0, Phis) of
+opt_sw([{L,#b_blk{is=Is,last=#b_switch{}=Sw0}=Blk0}|Bs], Count0, Acc) ->
+ %% Ensure that no label in the switch list is the same
+ %% as the failure label.
+ #b_switch{fail=Fail,list=List0} = Sw0,
+ List = [{Val,Lbl} || {Val,Lbl} <- List0, Lbl =/= Fail],
+ Sw1 = beam_ssa:normalize(Sw0#b_switch{list=List}),
+ case Sw1 of
#b_switch{arg=Arg,fail=Fail,list=[{Lit,Lbl}]} ->
%% Rewrite a single value switch to a br.
Bool = #b_var{name={'@ssa_bool',Count0}},
@@ -1843,7 +1849,7 @@ opt_sw([{L,#b_blk{is=Is,last=#b_switch{}=Last0}=Blk0}|Bs], Phis0, Count0, Acc) -
IsEq = #b_set{op={bif,'=:='},dst=Bool,args=[Arg,Lit]},
Br = #b_br{bool=Bool,succ=Lbl,fail=Fail},
Blk = Blk0#b_blk{is=Is++[IsEq],last=Br},
- opt_sw(Bs, Phis, Count, [{L,Blk}|Acc]);
+ opt_sw(Bs, Count, [{L,Blk}|Acc]);
#b_switch{arg=Arg,fail=Fail,
list=[{#b_literal{val=B1},Lbl},{#b_literal{val=B2},Lbl}]}
when B1 =:= not B2 ->
@@ -1853,71 +1859,18 @@ opt_sw([{L,#b_blk{is=Is,last=#b_switch{}=Last0}=Blk0}|Bs], Phis0, Count0, Acc) -
IsBool = #b_set{op={bif,is_boolean},dst=Bool,args=[Arg]},
Br = #b_br{bool=Bool,succ=Lbl,fail=Fail},
Blk = Blk0#b_blk{is=Is++[IsBool],last=Br},
- opt_sw(Bs, Phis, Count, [{L,Blk}|Acc]);
- Last0 ->
- opt_sw(Bs, Phis, Count0, [{L,Blk0}|Acc]);
- Last ->
- Blk = Blk0#b_blk{last=Last},
- opt_sw(Bs, Phis, Count0, [{L,Blk}|Acc])
+ opt_sw(Bs, Count, [{L,Blk}|Acc]);
+ Sw0 ->
+ opt_sw(Bs, Count0, [{L,Blk0}|Acc]);
+ Sw ->
+ Blk = Blk0#b_blk{last=Sw},
+ opt_sw(Bs, Count0, [{L,Blk}|Acc])
end;
-opt_sw([{L,#b_blk{is=Is}=Blk}|Bs], Phis0, Count, Acc) ->
- Phis = opt_sw_phis(Is, Phis0),
- opt_sw(Bs, Phis, Count, [{L,Blk}|Acc]);
-opt_sw([], _Phis, Count, Acc) ->
+opt_sw([{L,#b_blk{}=Blk}|Bs], Count, Acc) ->
+ opt_sw(Bs, Count, [{L,Blk}|Acc]);
+opt_sw([], Count, Acc) ->
{reverse(Acc),Count}.
-opt_sw_phis([#b_set{op=phi,dst=Dst,args=Args}|Is], Phis) ->
- case opt_sw_literals(Args, []) of
- error ->
- opt_sw_phis(Is, Phis);
- Literals ->
- opt_sw_phis(Is, Phis#{Dst=>Literals})
- end;
-opt_sw_phis(_, Phis) -> Phis.
-
-opt_sw_last(#b_switch{arg=Arg,fail=Fail,list=List0}=Sw0, Phis) ->
- case Phis of
- #{Arg:=Values0} ->
- Values = gb_sets:from_list(Values0),
-
- %% Prune the switch list to only contain the possible values.
- List1 = [P || {Lit,_}=P <- List0, gb_sets:is_member(Lit, Values)],
-
- %% Now test whether the failure label can ever be reached.
- Sw = case gb_sets:size(Values) =:= length(List1) of
- true ->
- %% The switch list has the same number of values as the phi node.
- %% The values must be the same, because the values that were not
- %% possible were pruned from the switch list. Therefore, the
- %% failure label can't possibly be reached, and we can choose a
- %% a new failure label by picking a value from the list.
- case List1 of
- [{#b_literal{},Lbl}|List] ->
- Sw0#b_switch{fail=Lbl,list=List};
- [] ->
- Sw0#b_switch{list=List1}
- end;
- false ->
- %% There are some values in the phi node that are not in the
- %% switch list; thus, the failure label can still be reached.
- Sw0
- end,
- beam_ssa:normalize(Sw);
- #{} ->
- %% Ensure that no label in the switch list is the same
- %% as the failure label.
- List = [{Val,Lbl} || {Val,Lbl} <- List0, Lbl =/= Fail],
- Sw = Sw0#b_switch{list=List},
- beam_ssa:normalize(Sw)
- end.
-
-opt_sw_literals([{#b_literal{}=Lit,_}|T], Acc) ->
- opt_sw_literals(T, [Lit|Acc]);
-opt_sw_literals([_|_], _Acc) ->
- error;
-opt_sw_literals([], Acc) -> Acc.
-
-
%%%
%%% Merge blocks.
%%%
diff --git a/lib/compiler/src/beam_ssa_type.erl b/lib/compiler/src/beam_ssa_type.erl
index 3b5d25efce..5fbb679c6f 100644
--- a/lib/compiler/src/beam_ssa_type.erl
+++ b/lib/compiler/src/beam_ssa_type.erl
@@ -23,7 +23,8 @@
-include("beam_ssa_opt.hrl").
-import(lists, [all/2,any/2,droplast/1,foldl/3,last/1,member/2,
- partition/2,reverse/1,reverse/2,seq/2,sort/1]).
+ keyfind/3,partition/2,reverse/1,reverse/2,
+ seq/2,sort/1]).
-define(UNICODE_INT, #t_integer{elements={0,16#10FFFF}}).
@@ -260,22 +261,33 @@ opt_is([#b_set{op=phi,dst=Dst,args=Args0}=I0|Is],
opt_is(Is, Ts, Ds, Fdb, D, Sub0, [I|Acc])
end;
opt_is([#b_set{op=call,args=Args0,dst=Dst}=I0|Is],
- Ts0, Ds0, Fdb0, D, Sub, Acc) ->
- Args = simplify_args(Args0, Sub, Ts0),
+ Ts0, Ds0, Fdb0, D, Sub0, Acc) ->
+ Args = simplify_args(Args0, Sub0, Ts0),
I1 = beam_ssa:normalize(I0#b_set{args=Args}),
- {Ts,Ds,Fdb,I} = opt_call(I1, D, Ts0, Ds0, Fdb0),
- case {map_get(Dst, Ts),Is} of
- {none,[#b_set{op=succeeded}]} ->
+ {Ts1,Ds,Fdb,I2} = opt_call(I1, D, Ts0, Ds0, Fdb0),
+ case {map_get(Dst, Ts1),Is} of
+ {_,[#b_set{op=succeeded}]} ->
%% This call instruction is inside a try/catch
%% block. Don't attempt to optimize it.
- opt_is(Is, Ts, Ds, Fdb, D, Sub, [I|Acc]);
+ opt_is(Is, Ts1, Ds, Fdb, D, Sub0, [I2|Acc]);
{none,_} ->
%% This call never returns. The rest of the
%% instructions will not be executed.
Ret = #b_ret{arg=Dst},
- {no_return,Ret,reverse(Acc, [I]),Ds,Fdb,Sub};
- _ ->
- opt_is(Is, Ts, Ds, Fdb, D, Sub, [I|Acc])
+ {no_return,Ret,reverse(Acc, [I2]),Ds,Fdb,Sub0};
+ {_,_} ->
+ case simplify_call(I2) of
+ #b_set{}=I ->
+ opt_is(Is, Ts1, Ds, Fdb, D, Sub0, [I|Acc]);
+ #b_literal{}=Lit ->
+ Sub = Sub0#{Dst=>Lit},
+ Ts = maps:remove(Dst, Ts1),
+ opt_is(Is, Ts, Ds0, Fdb, D, Sub, Acc);
+ #b_var{}=Var ->
+ Ts = maps:remove(Dst, Ts1),
+ Sub = Sub0#{Dst=>Var},
+ opt_is(Is, Ts, Ds0, Fdb, D, Sub, Acc)
+ end
end;
opt_is([#b_set{op=succeeded,args=[Arg],dst=Dst}=I],
Ts0, Ds0, Fdb, D, Sub0, Acc) ->
@@ -328,6 +340,48 @@ opt_is([#b_set{args=Args0,dst=Dst}=I0|Is],
opt_is([], Ts, Ds, Fdb, _D, Sub, Acc) ->
{reverse(Acc), Ts, Ds, Fdb, Sub}.
+simplify_call(#b_set{op=call,args=[#b_remote{}=Rem|Args]}=I) ->
+ case Rem of
+ #b_remote{mod=#b_literal{val=Mod},
+ name=#b_literal{val=Name}} ->
+ case erl_bifs:is_pure(Mod, Name, length(Args)) of
+ true ->
+ simplify_remote_call(Mod, Name, Args, I);
+ false ->
+ I
+ end;
+ #b_remote{} ->
+ I
+ end;
+simplify_call(I) -> I.
+
+%% Simplify a remote call to a pure BIF.
+simplify_remote_call(erlang, '++', [#b_literal{val=[]},Tl], _I) ->
+ Tl;
+simplify_remote_call(Mod, Name, Args0, I) ->
+ case make_literal_list(Args0) of
+ none ->
+ I;
+ Args ->
+ %% The arguments are literals. Try to evaluate the BIF.
+ try apply(Mod, Name, Args) of
+ Val ->
+ case cerl:is_literal_term(Val) of
+ true ->
+ #b_literal{val=Val};
+ false ->
+ %% The value can't be expressed as a literal
+ %% (e.g. a pid).
+ I
+ end
+ catch
+ _:_ ->
+ %% Failed. Don't bother trying to optimize
+ %% the call.
+ I
+ end
+ end.
+
opt_call(#b_set{dst=Dst,args=[#b_local{}=Callee|Args]}=I0, D, Ts0, Ds0, Fdb0) ->
{Ts, Ds, I} = opt_local_call(I0, Ts0, Ds0, Fdb0),
case Fdb0 of
@@ -452,10 +506,19 @@ simplify(#b_set{op={bif,'=='},args=Args}=I, Ts) ->
end;
simplify(#b_set{op={bif,'=:='},args=[Same,Same]}, _Ts) ->
#b_literal{val=true};
-simplify(#b_set{op={bif,'=:='},args=Args}=I, Ts) ->
- case meet(get_types(Args, Ts)) of
- none -> #b_literal{val=false};
- _ -> eval_bif(I, Ts)
+simplify(#b_set{op={bif,'=:='},args=[A1,_A2]=Args}=I, Ts) ->
+ [T1,T2] = get_types(Args, Ts),
+ case meet(T1, T2) of
+ none ->
+ #b_literal{val=false};
+ _ ->
+ case {t_is_boolean(T1),T2} of
+ {true,#t_atom{elements=[true]}} ->
+ %% Bool =:= true ==> Bool
+ A1;
+ {_,_} ->
+ eval_bif(I, Ts)
+ end
end;
simplify(#b_set{op={bif,Op},args=Args}=I, Ts) ->
Types = get_types(Args, Ts),
@@ -598,41 +661,49 @@ anno_float_arg(_) -> convert.
opt_terminator(#b_br{bool=#b_literal{}}=Br, _Ts, _Ds) ->
beam_ssa:normalize(Br);
-opt_terminator(#b_br{bool=#b_var{}=V}=Br, Ts, Ds) ->
- #{V:=Set} = Ds,
- case Set of
- #b_set{op={bif,'=:='},args=[Bool,#b_literal{val=true}]} ->
- case t_is_boolean(get_type(Bool, Ts)) of
- true ->
- %% Bool =:= true ==> Bool
- simplify_not(Br#b_br{bool=Bool}, Ts, Ds);
- false ->
- Br
- end;
- #b_set{} ->
- simplify_not(Br, Ts, Ds)
- end;
+opt_terminator(#b_br{bool=#b_var{}}=Br, Ts, Ds) ->
+ simplify_not(Br, Ts, Ds);
opt_terminator(#b_switch{arg=#b_literal{}}=Sw, _Ts, _Ds) ->
beam_ssa:normalize(Sw);
-opt_terminator(#b_switch{arg=#b_var{}=V}=Sw0, Ts, Ds) ->
- Type = get_type(V, Ts),
+opt_terminator(#b_switch{arg=#b_var{}=V}=Sw, Ts, Ds) ->
+ case get_type(V, Ts) of
+ any ->
+ beam_ssa:normalize(Sw);
+ Type ->
+ beam_ssa:normalize(opt_switch(Sw, Type, Ts, Ds))
+ end;
+opt_terminator(#b_ret{}=Ret, _Ts, _Ds) -> Ret.
+
+
+opt_switch(#b_switch{fail=Fail,list=List0}=Sw0, Type, Ts, Ds) ->
+ List = prune_switch_list(List0, Fail, Type, Ts),
+ Sw1 = Sw0#b_switch{list=List},
case Type of
#t_integer{elements={_,_}=Range} ->
- simplify_switch_int(Sw0, Range);
- _ ->
+ simplify_switch_int(Sw1, Range);
+ #t_atom{elements=[_|_]} ->
case t_is_boolean(Type) of
true ->
- case simplify_switch_bool(Sw0, Ts, Ds) of
- #b_br{}=Br ->
- opt_terminator(Br, Ts, Ds);
- Sw ->
- beam_ssa:normalize(Sw)
- end;
+ #b_br{} = Br = simplify_switch_bool(Sw1, Ts, Ds),
+ opt_terminator(Br, Ts, Ds);
false ->
- beam_ssa:normalize(Sw0)
- end
+ simplify_switch_atom(Type, Sw1)
+ end;
+ _ ->
+ Sw1
+ end.
+
+prune_switch_list([{_,Fail}|T], Fail, Type, Ts) ->
+ prune_switch_list(T, Fail, Type, Ts);
+prune_switch_list([{Arg,_}=Pair|T], Fail, Type, Ts) ->
+ case meet(get_type(Arg, Ts), Type) of
+ none ->
+ %% Different types. This value can never match.
+ prune_switch_list(T, Fail, Type, Ts);
+ _ ->
+ [Pair|prune_switch_list(T, Fail, Type, Ts)]
end;
-opt_terminator(#b_ret{}=Ret, _Ts, _Ds) -> Ret.
+prune_switch_list([], _, _, _) -> [].
update_successors(#b_br{bool=#b_literal{val=true},succ=S}, Ts, D) ->
update_successor(S, Ts, D);
@@ -641,8 +712,8 @@ update_successors(#b_br{bool=#b_var{}=Bool,succ=Succ,fail=Fail}, Ts0, D0) ->
true ->
%% This variable is defined in this block and is only
%% referenced by this br terminator. Therefore, there is
- %% no need to include the type database passed on to the
- %% successors of this block.
+ %% no need to include it in the type database passed on to
+ %% the successors of this block.
Ts = maps:remove(Bool, Ts0),
{SuccTs,FailTs} = infer_types_br(Bool, Ts, D0),
D = update_successor(Fail, FailTs, D0),
@@ -1029,6 +1100,17 @@ bs_match_type(utf16, _) ->
bs_match_type(utf32, _) ->
?UNICODE_INT.
+simplify_switch_atom(#t_atom{elements=Atoms}, #b_switch{list=List0}=Sw) ->
+ case sort([A || {#b_literal{val=A},_} <- List0]) of
+ Atoms ->
+ %% All possible atoms are included in the list. The
+ %% failure label will never be used.
+ [{_,Fail}|List] = List0,
+ Sw#b_switch{fail=Fail,list=List};
+ _ ->
+ Sw
+ end.
+
simplify_switch_int(#b_switch{list=List0}=Sw, {Min,Max}) ->
List1 = sort(List0),
Vs = [V || {#b_literal{val=V},_} <- List1],
@@ -1073,14 +1155,14 @@ simplify_is_record(I, any, _Size, _Tag, _Ts) ->
simplify_is_record(_I, _Type, _Size, _Tag, _Ts) ->
#b_literal{val=false}.
-simplify_switch_bool(#b_switch{arg=B,list=List0}=Sw, Ts, Ds) ->
- List = sort(List0),
- case List of
- [{#b_literal{val=false},Fail},{#b_literal{val=true},Succ}] ->
- simplify_not(#b_br{bool=B,succ=Succ,fail=Fail}, Ts, Ds);
- [_|_] ->
- Sw
- end.
+simplify_switch_bool(#b_switch{arg=B,fail=Fail,list=List0}, Ts, Ds) ->
+ FalseVal = #b_literal{val=false},
+ TrueVal = #b_literal{val=true},
+ List1 = List0 ++ [{FalseVal,Fail},{TrueVal,Fail}],
+ {_,FalseLbl} = keyfind(FalseVal, 1, List1),
+ {_,TrueLbl} = keyfind(TrueVal, 1, List1),
+ Br = beam_ssa:normalize(#b_br{bool=B,succ=TrueLbl,fail=FalseLbl}),
+ simplify_not(Br, Ts, Ds).
simplify_not(#b_br{bool=#b_var{}=V,succ=Succ,fail=Fail}=Br0, Ts, Ds) ->
case Ds of
@@ -1094,7 +1176,8 @@ simplify_not(#b_br{bool=#b_var{}=V,succ=Succ,fail=Fail}=Br0, Ts, Ds) ->
end;
#{} ->
Br0
- end.
+ end;
+simplify_not(#b_br{bool=#b_literal{}}=Br, _Ts, _Ds) -> Br.
%%%
%%% Calculate the set of variables that are only used once in the
diff --git a/lib/compiler/src/erl_bifs.erl b/lib/compiler/src/erl_bifs.erl
index d925decce6..94a5dfe012 100644
--- a/lib/compiler/src/erl_bifs.erl
+++ b/lib/compiler/src/erl_bifs.erl
@@ -32,6 +32,22 @@
%% Returns `true' if the function `Module:Name/Arity' does not
%% affect the state, nor depend on the state, although its
%% evaluation is not guaranteed to complete normally for all input.
+%%
+%% NOTE: There is no need to include every new pure BIF
+%% here. Including it here means that the value of the function
+%% will be evaluated at compile-time if the arguments are
+%% constant. If that optimization is not useful/desired, there is
+%% no need to include the new BIF here.
+%%
+%% Functions whose return value could conceivably change in a
+%% future version of the runtime system must NOT be included here.
+%%
+%% Here are some example of functions that should not be
+%% included: `term_to_binary/1', hashing functions, non-trivial
+%% encode/decode functions.
+%%
+%% When unsure whether a new BIF should be included here, the
+%% conservative safe choice is NOT to include it.
-spec is_pure(atom(), atom(), arity()) -> boolean().
diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl
index e2bcd25801..3699c9d22e 100644
--- a/lib/compiler/src/v3_core.erl
+++ b/lib/compiler/src/v3_core.erl
@@ -330,7 +330,7 @@ gexpr({protect,Line,Arg}, Bools0, St0) ->
{#iprotect{anno=#a{anno=Anno},body=Eps++[E]},[],Bools0,St}
end;
gexpr({op,_,'andalso',_,_}=E0, Bools, St0) ->
- {op,L,'andalso',E1,E2} = right_assoc(E0, 'andalso', St0),
+ {op,L,'andalso',E1,E2} = right_assoc(E0, 'andalso'),
Anno = lineno_anno(L, St0),
{#c_var{name=V0},St} = new_var(Anno, St0),
V = {var,L,V0},
@@ -338,7 +338,7 @@ gexpr({op,_,'andalso',_,_}=E0, Bools, St0) ->
E = make_bool_switch_guard(L, E1, V, E2, False),
gexpr(E, Bools, St);
gexpr({op,_,'orelse',_,_}=E0, Bools, St0) ->
- {op,L,'orelse',E1,E2} = right_assoc(E0, 'orelse', St0),
+ {op,L,'orelse',E1,E2} = right_assoc(E0, 'orelse'),
Anno = lineno_anno(L, St0),
{#c_var{name=V0},St} = new_var(Anno, St0),
V = {var,L,V0},
@@ -768,7 +768,7 @@ expr({op,_,'++',{lc,Llc,E,Qs0},More}, St0) ->
{Y,Yps,St} = lc_tq(Llc, E, Qs, Mc, St2),
{Y,Mps++Yps,St};
expr({op,_,'andalso',_,_}=E0, St0) ->
- {op,L,'andalso',E1,E2} = right_assoc(E0, 'andalso', St0),
+ {op,L,'andalso',E1,E2} = right_assoc(E0, 'andalso'),
Anno = lineno_anno(L, St0),
{#c_var{name=V0},St} = new_var(Anno, St0),
V = {var,L,V0},
@@ -776,7 +776,7 @@ expr({op,_,'andalso',_,_}=E0, St0) ->
E = make_bool_switch(L, E1, V, E2, False, St0),
expr(E, St);
expr({op,_,'orelse',_,_}=E0, St0) ->
- {op,L,'orelse',E1,E2} = right_assoc(E0, 'orelse', St0),
+ {op,L,'orelse',E1,E2} = right_assoc(E0, 'orelse'),
Anno = lineno_anno(L, St0),
{#c_var{name=V0},St} = new_var(Anno, St0),
V = {var,L,V0},
@@ -2060,17 +2060,9 @@ fail_clause(Pats, Anno, Arg) ->
args=[Arg]}]}.
%% Optimization for Dialyzer.
-right_assoc(E, Op, St) ->
- case member(dialyzer, St#core.opts) of
- true ->
- right_assoc2(E, Op);
- false ->
- E
- end.
-
-right_assoc2({op,L1,Op,{op,L2,Op,E1,E2},E3}, Op) ->
- right_assoc2({op,L2,Op,E1,{op,L1,Op,E2,E3}}, Op);
-right_assoc2(E, _Op) -> E.
+right_assoc({op,L1,Op,{op,L2,Op,E1,E2},E3}, Op) ->
+ right_assoc({op,L2,Op,E1,{op,L1,Op,E2,E3}}, Op);
+right_assoc(E, _Op) -> E.
annotate_tuple(A, Es, St) ->
case member(dialyzer, St#core.opts) of
diff --git a/lib/kernel/test/logger_disk_log_h_SUITE.erl b/lib/kernel/test/logger_disk_log_h_SUITE.erl
index 9bbec42de8..13b30835a1 100644
--- a/lib/kernel/test/logger_disk_log_h_SUITE.erl
+++ b/lib/kernel/test/logger_disk_log_h_SUITE.erl
@@ -293,7 +293,7 @@ logging(Config) ->
ok = start_and_add(Name, #{filter_default=>log,
formatter=>{?MODULE,self()}},
#{file => LogFile}),
- MsgFormatter = fun(Term) -> {io_lib:format("Term:~p",[Term]),[]} end,
+ MsgFormatter = fun(Term) -> {"Term:~p",[Term]} end,
logger:notice([{x,y}], #{report_cb => MsgFormatter}),
logger:notice([{x,y}], #{}),
ct:pal("Checking contents of ~p", [?log_no(LogFile,1)]),
diff --git a/lib/observer/test/crashdump_helper.erl b/lib/observer/test/crashdump_helper.erl
index d6b5eff9b5..84ed99afa5 100644
--- a/lib/observer/test/crashdump_helper.erl
+++ b/lib/observer/test/crashdump_helper.erl
@@ -24,7 +24,7 @@
create_binaries/0,create_sub_binaries/1,
dump_persistent_terms/0,
create_persistent_terms/0]).
--compile(r18).
+-compile(r20).
-include_lib("common_test/include/ct.hrl").
n1_proc(N2,Creator) ->
diff --git a/lib/reltool/test/reltool_server_SUITE.erl b/lib/reltool/test/reltool_server_SUITE.erl
index f11aae61c6..bb092e8bbf 100644
--- a/lib/reltool/test/reltool_server_SUITE.erl
+++ b/lib/reltool/test/reltool_server_SUITE.erl
@@ -172,7 +172,7 @@ break(_Config) ->
start_server(_Config) ->
{ok, Pid} = ?msym({ok, _}, reltool:start_server([])),
- Libs = lists:sort(erl_libs()),
+ Libs = reltool_test_lib:erl_libs(),
StrippedDefault =
case Libs of
[] -> {sys, []};
@@ -186,7 +186,7 @@ start_server(_Config) ->
%% Start a server process and check that it does not crash
set_config(_Config) ->
- Libs = lists:sort(erl_libs()),
+ Libs = reltool_test_lib:erl_libs(),
Default =
{sys,
[
@@ -220,7 +220,15 @@ get_config(_Config) ->
StdLibDir = filename:join(LibDir,"stdlib-"++StdVsn),
SaslLibDir = filename:join(LibDir,"sasl-"++SaslVsn),
- Sys = {sys,[{incl_cond, exclude},
+ Libs = reltool_test_lib:erl_libs(),
+ LibDirs =
+ case Libs of
+ [] -> [];
+ _ -> [{lib_dirs,Libs}]
+ end,
+
+ Sys = {sys,LibDirs ++
+ [{incl_cond, exclude},
{app,kernel,[{incl_cond,include}]},
{app,sasl,[{incl_cond,include},{vsn,SaslVsn}]},
{app,stdlib,[{incl_cond,include},{lib_dir,StdLibDir}]}]},
@@ -229,13 +237,27 @@ get_config(_Config) ->
?m({ok, Sys}, reltool:get_config(Pid,false,false)),
%% Include derived info
- ?msym({ok,{sys,[{incl_cond, exclude},
- {erts,[]},
- {app,kernel,[{incl_cond,include},{mod,_,[]}|_]},
- {app,sasl,[{incl_cond,include},{vsn,SaslVsn},{mod,_,[]}|_]},
- {app,stdlib,[{incl_cond,include},{lib_dir,StdLibDir},
- {mod,_,[]}|_]}]}},
- reltool:get_config(Pid,false,true)),
+ case Libs of
+ [] ->
+ ?msym({ok,{sys,[{incl_cond, exclude},
+ {erts,[]},
+ {app,kernel,[{incl_cond,include},{mod,_,[]}|_]},
+ {app,sasl,[{incl_cond,include},{vsn,SaslVsn},
+ {mod,_,[]}|_]},
+ {app,stdlib,[{incl_cond,include},{lib_dir,StdLibDir},
+ {mod,_,[]}|_]}]}},
+ reltool:get_config(Pid,false,true));
+ _ ->
+ ?msym({ok,{sys,[{lib_dirs,Libs},
+ {incl_cond, exclude},
+ {erts,[]},
+ {app,kernel,[{incl_cond,include},{mod,_,[]}|_]},
+ {app,sasl,[{incl_cond,include},{vsn,SaslVsn},
+ {mod,_,[]}|_]},
+ {app,stdlib,[{incl_cond,include},{lib_dir,StdLibDir},
+ {mod,_,[]}|_]}]}},
+ reltool:get_config(Pid,false,true))
+ end,
%% Include defaults
?msym({ok,{sys,[{root_dir,_},
@@ -306,11 +328,11 @@ get_config(_Config) ->
%% OTP-9135, test that app_file option can be set to all | keep | strip
otp_9135(_Config) ->
- Libs = lists:sort(erl_libs()),
+ Libs = reltool_test_lib:erl_libs(),
StrippedDefaultSys =
case Libs of
[] -> [];
- _ -> {lib_dirs, Libs}
+ _ -> [{lib_dirs, Libs}]
end,
Config1 = {sys,[{app_file, keep}]}, % this is the default
@@ -1746,13 +1768,19 @@ set_sys_and_undo(Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
load_config_and_undo(Config) ->
- Sys1 = {sys,[{incl_cond, exclude},
- {app,kernel,[{incl_cond,include}]},
- {app,sasl,[{incl_cond,include}]},
- {app,stdlib,[{incl_cond,include}]},
- {app,tools,[{incl_cond,include}]}]},
+ Sys1 = {sys,Cfg1=[{incl_cond, exclude},
+ {app,kernel,[{incl_cond,include}]},
+ {app,sasl,[{incl_cond,include}]},
+ {app,stdlib,[{incl_cond,include}]},
+ {app,tools,[{incl_cond,include}]}]},
{ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys1}])),
- ?m({ok, Sys1}, reltool:get_config(Pid)),
+ Libs = reltool_test_lib:erl_libs(),
+ Sys11 =
+ case Libs of
+ [] -> Sys1;
+ _ -> {sys, [{lib_dirs, Libs}|Cfg1]}
+ end,
+ ?m({ok, Sys11}, reltool:get_config(Pid)),
?m({ok,[]}, reltool_server:get_status(Pid)),
%% Get app and mod
@@ -1807,13 +1835,19 @@ load_config_and_undo(Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Test that load_config is properly rolled back if it fails
load_config_fail(_Config) ->
- Sys1 = {sys,[{incl_cond, exclude},
- {app,kernel,[{incl_cond,include}]},
- {app,sasl,[{incl_cond,include}]},
- {app,stdlib,[{incl_cond,include}]},
- {app,tools,[{incl_cond,include}]}]},
+ Sys1 = {sys,Cfg1=[{incl_cond, exclude},
+ {app,kernel,[{incl_cond,include}]},
+ {app,sasl,[{incl_cond,include}]},
+ {app,stdlib,[{incl_cond,include}]},
+ {app,tools,[{incl_cond,include}]}]},
{ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys1}])),
- ?m({ok, Sys1}, reltool:get_config(Pid)),
+ Libs = reltool_test_lib:erl_libs(),
+ Sys11 =
+ case Libs of
+ [] -> Sys1;
+ _ -> {sys, [{lib_dirs, Libs}|Cfg1]}
+ end,
+ ?m({ok, Sys11}, reltool:get_config(Pid)),
?m({ok,[]}, reltool_server:get_status(Pid)),
%% Get app and mod
@@ -1831,7 +1865,7 @@ load_config_fail(_Config) ->
reltool_server:load_config(Pid,Sys2)),
%% Check that a rollback is done to the old configuration
- ?m({ok, Sys1}, reltool:get_config(Pid,false,false)),
+ ?m({ok, Sys11}, reltool:get_config(Pid,false,false)),
%% and that tools is not changed (i.e. that the new configuration
%% is not applied)
@@ -2101,25 +2135,42 @@ gen_rel_files(_Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
save_config(Config) ->
PrivDir = ?config(priv_dir,Config),
- Sys = {sys,[{incl_cond, exclude},
- {app,kernel,[{incl_cond,include}]},
- {app,sasl,[{incl_cond,include}]},
- {app,stdlib,[{incl_cond,include}]}]},
+ Sys = {sys,Cfg=[{incl_cond, exclude},
+ {app,kernel,[{incl_cond,include}]},
+ {app,sasl,[{incl_cond,include}]},
+ {app,stdlib,[{incl_cond,include}]}]},
{ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])),
- ?m({ok, Sys}, reltool:get_config(Pid)),
+ Libs = reltool_test_lib:erl_libs(),
+ Sys1 =
+ case Libs of
+ [] -> Sys;
+ _ -> {sys, [{lib_dirs, Libs}|Cfg]}
+ end,
+ ?m({ok, Sys1}, reltool:get_config(Pid)),
Simple = filename:join(PrivDir,"save_simple.reltool"),
?m(ok, reltool_server:save_config(Pid,Simple,false,false)),
- ?m({ok,[Sys]}, file:consult(Simple)),
+ ?m({ok,[Sys1]}, file:consult(Simple)),
Derivates = filename:join(PrivDir,"save_derivates.reltool"),
?m(ok, reltool_server:save_config(Pid,Derivates,false,true)),
- ?msym({ok,[{sys,[{incl_cond, exclude},
- {erts,[]},
- {app,kernel,[{incl_cond,include},{mod,_,[]}|_]},
- {app,sasl,[{incl_cond,include},{mod,_,[]}|_]},
- {app,stdlib,[{incl_cond,include},{mod,_,[]}|_]}]}]},
- file:consult(Derivates)),
+ case Libs of
+ [] ->
+ ?msym({ok,[{sys,[{incl_cond, exclude},
+ {erts,[]},
+ {app,kernel,[{incl_cond,include},{mod,_,[]}|_]},
+ {app,sasl,[{incl_cond,include},{mod,_,[]}|_]},
+ {app,stdlib,[{incl_cond,include},{mod,_,[]}|_]}]}]},
+ file:consult(Derivates));
+ _ ->
+ ?msym({ok,[{sys,[{lib_dirs,Libs},
+ {incl_cond, exclude},
+ {erts,[]},
+ {app,kernel,[{incl_cond,include},{mod,_,[]}|_]},
+ {app,sasl,[{incl_cond,include},{mod,_,[]}|_]},
+ {app,stdlib,[{incl_cond,include},{mod,_,[]}|_]}]}]},
+ file:consult(Derivates))
+ end,
Defaults = filename:join(PrivDir,"save_defaults.reltool"),
?m(ok, reltool_server:save_config(Pid,Defaults,true,false)),
@@ -2587,9 +2638,6 @@ windows_erl_libs(_Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Library functions
-erl_libs() ->
- reltool_utils:erl_libs().
-
datadir(Config) ->
%% Removes the trailing slash...
filename:nativename(?config(data_dir,Config)).
diff --git a/lib/reltool/test/reltool_test_lib.erl b/lib/reltool/test/reltool_test_lib.erl
index be48ea4726..033d952d0a 100644
--- a/lib/reltool/test/reltool_test_lib.erl
+++ b/lib/reltool/test/reltool_test_lib.erl
@@ -237,7 +237,8 @@ wait_for_close() ->
wait_for_close()
end.
-
+erl_libs() ->
+ lists:sort([filename:absname(P) || P<-reltool_utils:erl_libs()]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% A small test server, which can be run standalone in a shell
diff --git a/lib/reltool/test/reltool_wx_SUITE.erl b/lib/reltool/test/reltool_wx_SUITE.erl
index f6f7721762..983c8f6c52 100644
--- a/lib/reltool/test/reltool_wx_SUITE.erl
+++ b/lib/reltool/test/reltool_wx_SUITE.erl
@@ -74,7 +74,12 @@ start_all_windows(_Config) ->
%% Test that server pid can be fetched, and that server is alive
{ok, Server} = ?msym({ok,_}, reltool:get_server(SysPid)),
?m(true, erlang:is_process_alive(Server)),
- ?m({ok,{sys,[]}}, reltool:get_config(Server)),
+ Sys =
+ case reltool_test_lib:erl_libs() of
+ [] -> [];
+ Libs -> [{lib_dirs,Libs}]
+ end,
+ ?m({ok,{sys,Sys}}, reltool:get_config(Server)),
%% Terminate
check_no_win_crash(),
diff --git a/lib/sasl/test/test_lib.hrl b/lib/sasl/test/test_lib.hrl
index f5210d4f27..7867d3da39 100644
--- a/lib/sasl/test/test_lib.hrl
+++ b/lib/sasl/test/test_lib.hrl
@@ -1,3 +1,3 @@
-define(ertsvsn,"4.4").
--define(kernelvsn,"5.3").
--define(stdlibvsn,"3.4").
+-define(kernelvsn,"6.0").
+-define(stdlibvsn,"3.5").
diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml
index be5abac7bc..3f643f32e1 100644
--- a/lib/ssl/doc/src/ssl.xml
+++ b/lib/ssl/doc/src/ssl.xml
@@ -190,15 +190,18 @@
<name name="legacy_hash"/>
</datatype>
-
<datatype>
<name name="signature_algs"/>
</datatype>
-
+
<datatype>
<name name="sign_algo"/>
</datatype>
-
+
+ <datatype>
+ <name name="sign_scheme"/>
+ </datatype>
+
<datatype>
<name name="key_algo"/>
</datatype>
@@ -334,7 +337,30 @@
and to restrict their usage when using a cipher suite supporting them.</p>
</desc>
</datatype>
-
+
+ <datatype>
+ <name name="signature_schemes"/>
+ <desc>
+ <p>
+ In addition to the signature_algorithms extension from TLS 1.2,
+ <url href="http://www.ietf.org/rfc/rfc8446.txt#section-4.2.3">TLS 1.3
+ (RFC 5246 Section 4.2.3)</url>adds the signature_algorithms_cert extension
+ which enables having special requirements on the signatures used in the
+ certificates that differs from the requirements on digital signatures as a whole.
+ If this is not required this extension is not needed.
+ </p>
+ <p>
+ The client will send a signature_algorithms_cert extension (ClientHello),
+ if TLS version 1.3 or later is used, and the signature_algs_cert option is
+ explicitly specified. By default, only the signature_algs extension is sent.
+ </p>
+ <p>
+ The signature schemes shall be ordered according to the client's preference
+ (favorite choice first).
+ </p>
+ </desc>
+ </datatype>
+
<datatype>
<name name="secure_renegotiation"/>
<desc><p>Specifies if to reject renegotiation attempt that does
@@ -606,10 +632,19 @@ fun(srp, Username :: string(), UserState :: term()) ->
</desc>
</datatype>
- <datatype>
- <name name="log_alert"/>
- <desc><p>If set to <c>false</c>, error reports are not displayed.</p>
- </desc>
+ <datatype>
+ <name name="log_alert"/>
+ <desc><p>If set to <c>false</c>, error reports are not displayed.
+ Deprecated in OTP 22, use {log_level, <seealso marker="#type-logging_level">logging_level()</seealso>} instead.</p>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="logging_level"/>
+ <desc><p>Specifies the log level for TLS/DTLS. At verbosity level <c>notice</c> and above error reports are
+ displayed in TLS. The level <c>debug</c> triggers verbose logging of TLS protocol
+ messages and logging of ignored alerts in DTLS.</p>
+ </desc>
</datatype>
<datatype>
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index d06f61f17d..980b773a84 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -136,6 +136,22 @@
-type legacy_hash() :: md5.
-type sign_algo() :: rsa | dsa | ecdsa.
+
+-type sign_scheme() :: rsa_pkcs1_sha256
+ | rsa_pkcs1_sha384
+ | rsa_pkcs1_sha512
+ | ecdsa_secp256r1_sha256
+ | ecdsa_secp384r1_sha384
+ | ecdsa_secp521r1_sha512
+ | rsa_pss_rsae_sha256
+ | rsa_pss_rsae_sha384
+ | rsa_pss_rsae_sha512
+ | rsa_pss_pss_sha256
+ | rsa_pss_pss_sha384
+ | rsa_pss_pss_sha512
+ | rsa_pkcs1_sha1
+ | ecdsa_sha1.
+
-type key_algo() :: rsa |
dhe_rsa | dhe_dss |
ecdhe_ecdsa | ecdh_ecdsa | ecdh_rsa |
@@ -229,6 +245,7 @@
{password, key_password()} |
{ciphers, cipher_suites()} |
{eccs, eccs()} |
+ {signature_algs_cert, signature_schemes()} |
{secure_renegotiate, secure_renegotiation()} |
{depth, allowed_cert_chain_length()} |
{verify_fun, custom_verify()} |
@@ -238,6 +255,7 @@
{partial_chain, root_fun()} |
{versions, protocol_versions()} |
{user_lookup_fun, custom_user_lookup()} |
+ {log_level, logging_level()} |
{log_alert, log_alert()} |
{hibernate_after, hibernate_after()} |
{padding_check, padding_check()} |
@@ -272,13 +290,14 @@
-type root_fun() :: fun().
-type protocol_versions() :: [protocol_version()].
-type signature_algs() :: [{hash(), sign_algo()}].
+-type signature_schemes() :: [sign_scheme()].
-type custom_user_lookup() :: {Lookupfun :: fun(), UserState :: term()}.
-type padding_check() :: boolean().
-type beast_mitigation() :: one_n_minus_one | zero_n | disabled.
-type srp_identity() :: {Username :: string(), Password :: string()}.
-type psk_identity() :: string().
-type log_alert() :: boolean().
-
+-type logging_level() :: logger:level().
%% -------------------------------------------------------------------------------------------------------
-type client_option() :: {verify, client_verify_type()} |
diff --git a/lib/ssl/test/ssl_bench_SUITE.erl b/lib/ssl/test/ssl_bench_SUITE.erl
index 0b2011a627..35efa2b8a3 100644
--- a/lib/ssl/test/ssl_bench_SUITE.erl
+++ b/lib/ssl/test/ssl_bench_SUITE.erl
@@ -41,6 +41,7 @@ end_per_group(_GroupName, _Config) ->
ok.
init_per_suite(Config) ->
+ ct:timetrap({minutes, 1}),
case node() of
nonode@nohost ->
{skipped, "Node not distributed"};
@@ -163,7 +164,7 @@ do_test(Type, TC, Loop, ParallellConnections, Server) ->
end
end,
Spawn = fun(Id) ->
- Pid = spawn(fun() -> Test(Id) end),
+ Pid = spawn_link(fun() -> Test(Id) end),
receive {Pid, init} -> Pid end
end,
Pids = [Spawn(Id) || Id <- lists:seq(ParallellConnections, 1, -1)],
@@ -180,42 +181,42 @@ do_test(Type, TC, Loop, ParallellConnections, Server) ->
{ok, TestPerSecond}.
server_init(ssl, setup_connection, _, _, Server) ->
- {ok, Socket} = ssl:listen(0, ssl_opts(listen)),
- {ok, {_Host, Port}} = ssl:sockname(Socket),
+ {ok, LSocket} = ssl:listen(0, ssl_opts(listen)),
+ {ok, {_Host, Port}} = ssl:sockname(LSocket),
{ok, Host} = inet:gethostname(),
?FPROF_SERVER andalso start_profile(fprof, [whereis(ssl_manager), new]),
%%?EPROF_SERVER andalso start_profile(eprof, [ssl_connection_sup, ssl_manager]),
?EPROF_SERVER andalso start_profile(eprof, [ssl_manager]),
Server ! {self(), {init, Host, Port}},
Test = fun(TSocket) ->
- ok = ssl:ssl_accept(TSocket),
- ssl:close(TSocket)
+ {ok, Socket} = ssl:handshake(TSocket),
+ ssl:close(Socket)
end,
- setup_server_connection(Socket, Test);
+ setup_server_connection(LSocket, Test);
server_init(ssl, payload, Loop, _, Server) ->
- {ok, Socket} = ssl:listen(0, ssl_opts(listen)),
- {ok, {_Host, Port}} = ssl:sockname(Socket),
+ {ok, LSocket} = ssl:listen(0, ssl_opts(listen)),
+ {ok, {_Host, Port}} = ssl:sockname(LSocket),
{ok, Host} = inet:gethostname(),
Server ! {self(), {init, Host, Port}},
Test = fun(TSocket) ->
- ok = ssl:ssl_accept(TSocket),
+ {ok, Socket} = ssl:handshake(TSocket),
Size = byte_size(msg()),
- server_echo(TSocket, Size, Loop),
- ssl:close(TSocket)
+ server_echo(Socket, Size, Loop),
+ ssl:close(Socket)
end,
- setup_server_connection(Socket, Test);
+ setup_server_connection(LSocket, Test);
server_init(ssl, pem_cache, Loop, _, Server) ->
- {ok, Socket} = ssl:listen(0, ssl_opts(listen_der)),
- {ok, {_Host, Port}} = ssl:sockname(Socket),
+ {ok, LSocket} = ssl:listen(0, ssl_opts(listen_der)),
+ {ok, {_Host, Port}} = ssl:sockname(LSocket),
{ok, Host} = inet:gethostname(),
Server ! {self(), {init, Host, Port}},
Test = fun(TSocket) ->
- ok = ssl:ssl_accept(TSocket),
+ {ok, Socket} = ssl:handshake(TSocket),
Size = byte_size(msg()),
- server_echo(TSocket, Size, Loop),
- ssl:close(TSocket)
+ server_echo(Socket, Size, Loop),
+ ssl:close(Socket)
end,
- setup_server_connection(Socket, Test);
+ setup_server_connection(LSocket, Test);
server_init(Type, Tc, _, _, Server) ->
io:format("No server init code for ~p ~p~n",[Type, Tc]),
diff --git a/lib/stdlib/src/erl_pp.erl b/lib/stdlib/src/erl_pp.erl
index dd302a2880..ada3ff5de3 100644
--- a/lib/stdlib/src/erl_pp.erl
+++ b/lib/stdlib/src/erl_pp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -697,6 +697,8 @@ fun_info(Extra) ->
%% BITS:
+bit_grp([], _Opts) ->
+ leaf("<<>>");
bit_grp(Fs, Opts) ->
append([['<<'], [bit_elems(Fs, Opts)], ['>>']]).
diff --git a/lib/stdlib/test/erl_pp_SUITE.erl b/lib/stdlib/test/erl_pp_SUITE.erl
index dda8d0a12e..f5d80e7e68 100644
--- a/lib/stdlib/test/erl_pp_SUITE.erl
+++ b/lib/stdlib/test/erl_pp_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2018. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -51,7 +51,7 @@
otp_6321/1, otp_6911/1, otp_6914/1, otp_8150/1, otp_8238/1,
otp_8473/1, otp_8522/1, otp_8567/1, otp_8664/1, otp_9147/1,
otp_10302/1, otp_10820/1, otp_11100/1, otp_11861/1, pr_1014/1,
- otp_13662/1, otp_14285/1]).
+ otp_13662/1, otp_14285/1, otp_15592/1]).
%% Internal export.
-export([ehook/6]).
@@ -81,7 +81,7 @@ groups() ->
[otp_6321, otp_6911, otp_6914, otp_8150, otp_8238,
otp_8473, otp_8522, otp_8567, otp_8664, otp_9147,
otp_10302, otp_10820, otp_11100, otp_11861, pr_1014, otp_13662,
- otp_14285]}].
+ otp_14285, otp_15592]}].
init_per_suite(Config) ->
Config.
@@ -1167,6 +1167,11 @@ otp_14285(_Config) ->
[{encoding,latin1}])),
ok.
+otp_15592(_Config) ->
+ ok = pp_expr(<<"long12345678901234567890123456789012345678901234"
+ "56789012345678901234:f(<<>>)">>),
+ ok.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
compile(Config, Tests) ->