aboutsummaryrefslogtreecommitdiffstats
path: root/lib/stdlib
diff options
context:
space:
mode:
Diffstat (limited to 'lib/stdlib')
-rw-r--r--lib/stdlib/doc/src/gen_statem.xml6
-rw-r--r--lib/stdlib/src/erl_lint.erl97
-rw-r--r--lib/stdlib/src/gen_statem.erl493
-rw-r--r--lib/stdlib/src/proc_lib.erl4
-rw-r--r--lib/stdlib/test/erl_lint_SUITE.erl156
-rw-r--r--lib/stdlib/test/gen_statem_SUITE.erl10
6 files changed, 443 insertions, 323 deletions
diff --git a/lib/stdlib/doc/src/gen_statem.xml b/lib/stdlib/doc/src/gen_statem.xml
index ec7f267c64..d83e17b177 100644
--- a/lib/stdlib/doc/src/gen_statem.xml
+++ b/lib/stdlib/doc/src/gen_statem.xml
@@ -396,17 +396,17 @@ handle_event(_, _, State, Data) ->
<tag><c>pid()</c><br />
<c>LocalName</c></tag>
<item>The <c>gen_statem</c> is locally registered.</item>
- <tag><c>Name, Node</c></tag>
+ <tag><c>{Name,Node}</c></tag>
<item>
The <c>gen_statem</c> is locally registered
on another node.
</item>
- <tag><c>GlobalName</c></tag>
+ <tag><c>{global,GlobalName}</c></tag>
<item>
The <c>gen_statem</c> is globally registered
in <seealso marker="kernel:global"><c>global</c></seealso>.
</item>
- <tag><c>RegMod, ViaName</c></tag>
+ <tag><c>{via,RegMod,ViaName}</c></tag>
<item>
The <c>gen_statem</c> is registered through
an alternative process registry.
diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl
index b14102ac38..2508f96b91 100644
--- a/lib/stdlib/src/erl_lint.erl
+++ b/lib/stdlib/src/erl_lint.erl
@@ -1502,7 +1502,7 @@ pattern({op,_Line,'++',{string,_Li,_S},R}, Vt, Old, Bvt, St) ->
pattern({match,_Line,Pat1,Pat2}, Vt, Old, Bvt, St0) ->
{Lvt,Bvt1,St1} = pattern(Pat1, Vt, Old, Bvt, St0),
{Rvt,Bvt2,St2} = pattern(Pat2, Vt, Old, Bvt, St1),
- St3 = reject_bin_alias(Pat1, Pat2, St2),
+ St3 = reject_invalid_alias(Pat1, Pat2, Vt, St2),
{vtmerge_pat(Lvt, Rvt),vtmerge_pat(Bvt1,Bvt2),St3};
%% Catch legal constant expressions, including unary +,-.
pattern(Pat, _Vt, _Old, _Bvt, St) ->
@@ -1517,56 +1517,77 @@ pattern_list(Ps, Vt, Old, Bvt0, St) ->
{vtmerge_pat(Pvt, Psvt),vtmerge_pat(Bvt,Bvt1),St1}
end, {[],[],St}, Ps).
-%% reject_bin_alias(Pat, Expr, St) -> St'
+
+
+%% reject_invalid_alias(Pat, Expr, Vt, St) -> St'
%% Reject aliases for binary patterns at the top level.
+%% Reject aliases for maps patterns at the top level.
+%% The variables table (Vt) are for maps checkking.
+
+reject_invalid_alias_expr({bin,_,_}=P, {match,_,P0,E}, Vt, St0) ->
+ St = reject_invalid_alias(P, P0, Vt, St0),
+ reject_invalid_alias_expr(P, E, Vt, St);
+reject_invalid_alias_expr({map,_,_}=P, {match,_,P0,E}, Vt, St0) ->
+ St = reject_invalid_alias(P, P0, Vt, St0),
+ reject_invalid_alias_expr(P, E, Vt, St);
+reject_invalid_alias_expr({match,_,_,_}=P, {match,_,P0,E}, Vt, St0) ->
+ St = reject_invalid_alias(P, P0, Vt, St0),
+ reject_invalid_alias_expr(P, E, Vt, St);
+reject_invalid_alias_expr(_, _, _, St) -> St.
-reject_bin_alias_expr({bin,_,_}=P, {match,_,P0,E}, St0) ->
- St = reject_bin_alias(P, P0, St0),
- reject_bin_alias_expr(P, E, St);
-reject_bin_alias_expr({match,_,_,_}=P, {match,_,P0,E}, St0) ->
- St = reject_bin_alias(P, P0, St0),
- reject_bin_alias_expr(P, E, St);
-reject_bin_alias_expr(_, _, St) -> St.
-%% reject_bin_alias(Pat1, Pat2, St) -> St'
+%% reject_invalid_alias(Pat1, Pat2, St) -> St'
%% Aliases of binary patterns, such as <<A:8>> = <<B:4,C:4>> or even
%% <<A:8>> = <<A:8>>, are not allowed. Traverse the patterns in parallel
%% and generate an error if any binary aliases are found.
%% We generate an error even if is obvious that the overall pattern can't
%% possibly match, for instance, {a,<<A:8>>,c}={x,<<A:8>>} WILL generate an
%% error.
+%% Maps should reject unbound variables here.
-reject_bin_alias({bin,Line,_}, {bin,_,_}, St) ->
+reject_invalid_alias({bin,Line,_}, {bin,_,_}, _, St) ->
add_error(Line, illegal_bin_pattern, St);
-reject_bin_alias({cons,_,H1,T1}, {cons,_,H2,T2}, St0) ->
- St = reject_bin_alias(H1, H2, St0),
- reject_bin_alias(T1, T2, St);
-reject_bin_alias({tuple,_,Es1}, {tuple,_,Es2}, St) ->
- reject_bin_alias_list(Es1, Es2, St);
-reject_bin_alias({record,_,Name1,Pfs1}, {record,_,Name2,Pfs2},
+reject_invalid_alias({map,_Line,Ps1}, {map,_,Ps2}, Vt, St0) ->
+ Fun = fun ({map_field_exact,L,{var,_,K},_V}, Sti) ->
+ case is_var_bound(K,Vt) of
+ true ->
+ Sti;
+ false ->
+ add_error(L, {unbound_var,K}, Sti)
+ end;
+ ({map_field_exact,_L,_K,_V}, Sti) ->
+ Sti
+ end,
+ foldl(Fun, foldl(Fun, St0, Ps1), Ps2);
+reject_invalid_alias({cons,_,H1,T1}, {cons,_,H2,T2}, Vt, St0) ->
+ St = reject_invalid_alias(H1, H2, Vt, St0),
+ reject_invalid_alias(T1, T2, Vt, St);
+reject_invalid_alias({tuple,_,Es1}, {tuple,_,Es2}, Vt, St) ->
+ reject_invalid_alias_list(Es1, Es2, Vt, St);
+reject_invalid_alias({record,_,Name1,Pfs1}, {record,_,Name2,Pfs2}, Vt,
#lint{records=Recs}=St) ->
case {dict:find(Name1, Recs),dict:find(Name2, Recs)} of
{{ok,{_Line1,Fields1}},{ok,{_Line2,Fields2}}} ->
- reject_bin_alias_rec(Pfs1, Pfs2, Fields1, Fields2, St);
+ reject_invalid_alias_rec(Pfs1, Pfs2, Fields1, Fields2, Vt, St);
{_,_} ->
%% One or more non-existing records. (An error messages has
%% already been generated, so we are done here.)
St
end;
-reject_bin_alias({match,_,P1,P2}, P, St0) ->
- St = reject_bin_alias(P1, P, St0),
- reject_bin_alias(P2, P, St);
-reject_bin_alias(P, {match,_,_,_}=M, St) ->
- reject_bin_alias(M, P, St);
-reject_bin_alias(_P1, _P2, St) -> St.
-
-reject_bin_alias_list([E1|Es1], [E2|Es2], St0) ->
- St = reject_bin_alias(E1, E2, St0),
- reject_bin_alias_list(Es1, Es2, St);
-reject_bin_alias_list(_, _, St) -> St.
-
-reject_bin_alias_rec(PfsA0, PfsB0, FieldsA0, FieldsB0, St) ->
+reject_invalid_alias({match,_,P1,P2}, P, Vt, St0) ->
+ St = reject_invalid_alias(P1, P, Vt, St0),
+ reject_invalid_alias(P2, P, Vt, St);
+reject_invalid_alias(P, {match,_,_,_}=M, Vt, St) ->
+ reject_invalid_alias(M, P, Vt, St);
+reject_invalid_alias(_P1, _P2, _Vt, St) -> St.
+
+reject_invalid_alias_list([E1|Es1], [E2|Es2], Vt, St0) ->
+ St = reject_invalid_alias(E1, E2, Vt, St0),
+ reject_invalid_alias_list(Es1, Es2, Vt, St);
+reject_invalid_alias_list(_, _, _, St) -> St.
+
+reject_invalid_alias_rec(PfsA0, PfsB0, FieldsA0, FieldsB0, Vt, St) ->
%% We treat records as if they have been converted to tuples.
PfsA1 = rbia_field_vars(PfsA0),
PfsB1 = rbia_field_vars(PfsB0),
@@ -1582,7 +1603,7 @@ reject_bin_alias_rec(PfsA0, PfsB0, FieldsA0, FieldsB0, St) ->
D = sofs:projection({external,fun({_,_,P1,_,P2}) -> {P1,P2} end}, C),
E = sofs:to_external(D),
{Ps1,Ps2} = lists:unzip(E),
- reject_bin_alias_list(Ps1, Ps2, St).
+ reject_invalid_alias_list(Ps1, Ps2, Vt, St).
rbia_field_vars(Fs) ->
[{Name,Pat} || {record_field,_,{atom,_,Name},Pat} <- Fs].
@@ -2284,7 +2305,7 @@ expr({'catch',Line,E}, Vt, St0) ->
expr({match,_Line,P,E}, Vt, St0) ->
{Evt,St1} = expr(E, Vt, St0),
{Pvt,Bvt,St2} = pattern(P, vtupdate(Evt, Vt), St1),
- St = reject_bin_alias_expr(P, E, St2),
+ St = reject_invalid_alias_expr(P, E, Vt, St2),
{vtupdate(Bvt, vtmerge(Evt, Pvt)),St};
%% No comparison or boolean operators yet.
expr({op,_Line,_Op,A}, Vt, St) ->
@@ -2381,7 +2402,7 @@ is_valid_call(Call) ->
_ -> true
end.
-%% is_valid_map_key(K,St) -> true | false
+%% is_valid_map_key(K) -> true | false
%% variables are allowed for patterns only at the top of the tree
is_valid_map_key({var,_,_}) -> true;
@@ -3413,6 +3434,14 @@ warn_unused_vars(U, Vt, St0) ->
UVt = map(fun ({V,{State,_,Ls}}) -> {V,{State,used,Ls}} end, U),
{vtmerge(Vt, UVt), St1}.
+
+is_var_bound(V, Vt) ->
+ case orddict:find(V, Vt) of
+ {ok,{bound,_Usage,_}} -> true;
+ _ -> false
+ end.
+
+
%% vtupdate(UpdVarTable, VarTable) -> VarTable.
%% Add the variables in the updated vartable to VarTable. The variables
%% will be updated with their property in UpdVarTable. The state of
diff --git a/lib/stdlib/src/gen_statem.erl b/lib/stdlib/src/gen_statem.erl
index f9e2e5f7d2..23bddafeed 100644
--- a/lib/stdlib/src/gen_statem.erl
+++ b/lib/stdlib/src/gen_statem.erl
@@ -97,7 +97,7 @@
%% * Postponing the current event is performed
%% iff 'postpone' is 'true'.
%% * A state timer is started iff 'timeout' is set.
- %% * Pending events are processed or if there are
+ %% * Pending events are handled or if there are
%% no pending events the server goes into receive
%% or hibernate (iff 'hibernate' is 'true')
%%
@@ -282,16 +282,6 @@ event_type(Type) ->
STACKTRACE(),
try throw(ok) catch _ -> erlang:get_stacktrace() end).
--define(
- TERMINATE(Class, Reason, Debug, S, Q),
- terminate(
- begin Class end,
- begin Reason end,
- ?STACKTRACE(),
- begin Debug end,
- begin S end,
- begin Q end)).
-
%%%==========================================================================
%%% API
@@ -300,11 +290,11 @@ event_type(Type) ->
| {'via', RegMod :: module(), Name :: term()}
| {'local', atom()}.
-type server_ref() ::
- {'global', GlobalName :: term()}
- | {'via', RegMod :: module(), ViaName :: term()}
+ pid()
| (LocalName :: atom())
| {Name :: atom(), Node :: atom()}
- | pid().
+ | {'global', GlobalName :: term()}
+ | {'via', RegMod :: module(), ViaName :: term()}.
-type debug_opt() ::
{'debug',
Dbgs ::
@@ -523,12 +513,15 @@ send(Proc, Msg) ->
ok
end.
-%% Here init_it/6 and enter_loop/5,6,7 functions converge
+%% Here the init_it/6 and enter_loop/5,6,7 functions converge
enter(Module, Opts, CallbackMode, State, Data, Server, Actions, Parent) ->
%% The values should already have been type checked
Name = gen:get_proc_name(Server),
Debug = gen:debug_options(Name, Opts),
- PrevState = make_ref(), % Will be discarded by loop_event_actions/9
+ P = Events = [],
+ Event = {internal,initial_state},
+ %% We enforce {postpone,false} to ensure that
+ %% our fake Event gets discarded, thought it might get logged
NewActions =
if
is_list(Actions) ->
@@ -540,15 +533,17 @@ enter(Module, Opts, CallbackMode, State, Data, Server, Actions, Parent) ->
callback_mode => CallbackMode,
module => Module,
name => Name,
- state => PrevState,
+ %% All fields below will be replaced according to the arguments to
+ %% loop_event_actions/10 when it finally loops back to loop/3
+ state => State,
data => Data,
- timer => undefined,
- postponed => [],
- hibernate => false},
+ postponed => P,
+ hibernate => false,
+ timer => undefined},
+ NewDebug = sys_debug(Debug, S, State, {enter,Event,State}),
loop_event_actions(
- Parent, Debug, S, [],
- {event,undefined}, % Will be discarded thanks to {postpone,false}
- PrevState, State, Data, NewActions).
+ Parent, NewDebug, S, Events,
+ State, Data, P, Event, State, NewActions).
%%%==========================================================================
%%% gen callbacks
@@ -617,8 +612,12 @@ init_result(Starter, Parent, ServerRef, Module, Result, Opts) ->
system_continue(Parent, Debug, S) ->
loop(Parent, Debug, S).
-system_terminate(Reason, _Parent, Debug, S) ->
- ?TERMINATE(exit, Reason, Debug, S, []).
+system_terminate(
+ Reason, _Parent, Debug,
+ #{state := State, data := Data, postponed := P} = S) ->
+ terminate(
+ exit, Reason, ?STACKTRACE(),
+ Debug, S, [], State, Data, P).
system_code_change(
#{module := Module,
@@ -634,7 +633,10 @@ system_code_change(
{NewCallbackMode,NewState,NewData} ->
callback_mode(NewCallbackMode) orelse
error({callback_mode,NewCallbackMode}),
- {ok,S#{state := NewState, data := NewData}};
+ {ok,
+ S#{callback_mode := NewCallbackMode,
+ state := NewState,
+ data := NewData}};
{ok,_} = Error ->
error({case_clause,Error});
Error ->
@@ -654,7 +656,7 @@ system_replace_state(
format_status(
Opt,
[PDict,SysState,Parent,Debug,
- #{name := Name, postponed := P} = S]) ->
+ #{name := Name, postponed := P, state := State, data := Data} = S]) ->
Header = gen:format_status_header("Status for state machine", Name),
Log = sys:get_debug(log, Debug, []),
[{header,Header},
@@ -663,7 +665,7 @@ format_status(
{"Parent",Parent},
{"Logged Events",Log},
{"Postponed",P}]} |
- case format_status(Opt, PDict, S) of
+ case format_status(Opt, PDict, S, State, Data) of
L when is_list(L) -> L;
T -> [T]
end].
@@ -673,21 +675,21 @@ format_status(
%% them, not as the real erlang messages. Use trace for that.
%%---------------------------------------------------------------------------
-print_event(Dev, {in,Event}, #{name := Name}) ->
+print_event(Dev, {in,Event}, {Name,_}) ->
io:format(
Dev, "*DBG* ~p received ~s~n",
[Name,event_string(Event)]);
-print_event(Dev, {out,Reply,{To,_Tag}}, #{name := Name}) ->
+print_event(Dev, {out,Reply,{To,_Tag}}, {Name,_}) ->
io:format(
Dev, "*DBG* ~p sent ~p to ~p~n",
[Name,Reply,To]);
-print_event(Dev, {Tag,Event,NewState}, #{name := Name, state := State}) ->
+print_event(Dev, {Tag,Event,NextState}, {Name,State}) ->
StateString =
- case NewState of
+ case NextState of
State ->
io_lib:format("~p", [State]);
_ ->
- io_lib:format("~p => ~p", [State,NewState])
+ io_lib:format("~p => ~p", [State,NextState])
end,
io:format(
Dev, "*DBG* ~p ~w ~s in state ~s~n",
@@ -697,16 +699,17 @@ event_string(Event) ->
case Event of
{{call,{Pid,_Tag}},Request} ->
io_lib:format("call ~p from ~w", [Request,Pid]);
- {Tag,Content} ->
- io_lib:format("~w ~p", [Tag,Content])
+ {EventType,EventContent} ->
+ io_lib:format("~w ~p", [EventType,EventContent])
end.
-sys_debug(Debug, S, Entry) ->
+sys_debug(Debug, #{name := Name}, State, Entry) ->
case Debug of
[] ->
Debug;
_ ->
- sys:handle_debug(Debug, fun print_event/3, S, Entry)
+ sys:handle_debug(
+ Debug, fun print_event/3, {Name,State}, Entry)
end.
%%%==========================================================================
@@ -720,7 +723,7 @@ wakeup_from_hibernate(Parent, Debug, S) ->
%%% State Machine engine implementation of proc_lib/gen server
%% Server loop, consists of all loop* functions
-%% and some detours through sys and proc_lib
+%% and detours through sys:handle_system_message/7 and proc_lib:hibernate/3
%% Entry point for system_continue/3
loop(Parent, Debug, #{hibernate := Hibernate} = S) ->
@@ -749,12 +752,16 @@ loop_receive(Parent, Debug, #{timer := Timer} = S) ->
sys:handle_system_msg(
Req, Pid, Parent, ?MODULE, Debug, S, Hibernate);
{'EXIT',Parent,Reason} = EXIT ->
+ #{state := State, data := Data, postponed := P} = S,
%% EXIT is not a 2-tuple and therefore
%% not an event and has no event_type(),
%% but this will stand out in the crash report...
- ?TERMINATE(exit, Reason, Debug, S, [EXIT]);
+ terminate(
+ exit, Reason, ?STACKTRACE(),
+ Debug, S, [EXIT], State, Data, P);
{timeout,Timer,Content} when Timer =/= undefined ->
- loop_event(Parent, Debug, S, {timeout,Content});
+ loop_receive_result(
+ Parent, Debug, S, {timeout,Content});
_ ->
%% Cancel Timer if running
case Timer of
@@ -782,30 +789,81 @@ loop_receive(Parent, Debug, #{timer := Timer} = S) ->
_ ->
{info,Msg}
end,
- loop_event(Parent, Debug, S, Event)
+ loop_receive_result(Parent, Debug, S, Event)
end
end.
-loop_event(Parent, Debug, S, Event) ->
- %% The timer field and the hibernate flag in S
- %% are now invalid and ignored until we get back to loop/3
- NewDebug = sys_debug(Debug, S, {in,Event}),
- %% Here the queue of not yet processed events is created
- loop_events(Parent, NewDebug, S, [Event], false).
-
-%% Process first the event queue, or if it is empty
-%% loop back to receive a new event
-loop_events(Parent, Debug, S, [], _Hibernate) ->
- loop(Parent, Debug, S);
+loop_receive_result(
+ Parent, Debug,
+ #{state := State,
+ data := Data,
+ postponed := P} = S,
+ Event) ->
+ %% The engine state map S is now dismantled
+ %% and will not be restored until we return to loop/3.
+ %%
+ %% The fields 'callback_mode', 'module', and 'name' are still valid.
+ %% The fields 'state', 'data', and 'postponed' are held in arguments.
+ %% The fields 'timer' and 'hibernate' will be recalculated.
+ %%
+ NewDebug = sys_debug(Debug, S, State, {in,Event}),
+ %% Here the queue of not yet handled events is created
+ Events = [],
+ Hibernate = false,
+ loop_event(
+ Parent, NewDebug, S, Events, State, Data, P, Event, Hibernate).
+
+%% Process the event queue, or if it is empty
+%% loop back to loop/3 to receive a new event
+loop_events(
+ Parent, Debug, S, [Event|Events],
+ State, Data, P, Hibernate, _Timeout) ->
+ %%
+ %% If there was a state timer requested we just ignore that
+ %% since we have events to handle which cancels the timer
+ loop_event(
+ Parent, Debug, S, Events, State, Data, P, Event, Hibernate);
loop_events(
+ Parent, Debug, S, [],
+ State, Data, P, Hibernate, Timeout) ->
+ case Timeout of
+ {timeout,0,EventContent} ->
+ %% Immediate timeout - simulate it
+ %% so we do not get the timeout message
+ %% after any received event
+ loop_event(
+ Parent, Debug, S, [],
+ State, Data, P, {timeout,EventContent}, Hibernate);
+ {timeout,Time,EventContent} ->
+ %% Actually start a timer
+ Timer = erlang:start_timer(Time, self(), EventContent),
+ loop_events_done(
+ Parent, Debug, S, Timer, State, Data, P, Hibernate);
+ undefined ->
+ %% No state timeout has been requested
+ Timer = undefined,
+ loop_events_done(
+ Parent, Debug, S, Timer, State, Data, P, Hibernate)
+ end.
+%%
+loop_events_done(Parent, Debug, S, Timer, State, Data, P, Hibernate) ->
+ NewS =
+ S#{
+ state := State,
+ data := Data,
+ postponed := P,
+ hibernate := Hibernate,
+ timer := Timer},
+ loop(Parent, Debug, NewS).
+
+loop_event(
Parent, Debug,
#{callback_mode := CallbackMode,
- module := Module,
- state := State,
- data := Data} = S,
- [{Type,Content} = Event|Events] = Q,
- Hibernate) ->
- %% If the Hibernate flag is true here it can only be
+ module := Module} = S,
+ Events,
+ State, Data, P, {Type,Content} = Event, Hibernate) ->
+ %%
+ %% If Hibernate is true here it can only be
%% because it was set from an event action
%% and we did not go into hibernation since there
%% were events in queue, so we do what the user
@@ -813,6 +871,7 @@ loop_events(
%% would have happened if we actually hibernated
%% and immediately was awakened
Hibernate andalso garbage_collect(),
+ %%
try
case CallbackMode of
state_functions ->
@@ -822,11 +881,11 @@ loop_events(
end of
Result ->
loop_event_result(
- Parent, Debug, S, Events, Event, Result)
+ Parent, Debug, S, Events, State, Data, P, Event, Result)
catch
Result ->
loop_event_result(
- Parent, Debug, S, Events, Event, Result);
+ Parent, Debug, S, Events, State, Data, P, Event, Result);
error:badarg when CallbackMode =:= state_functions ->
case erlang:get_stacktrace() of
[{erlang,apply,[Module,State,_],_}|Stacktrace] ->
@@ -835,9 +894,11 @@ loop_events(
error,
{undef_state_function,{Module,State,Args}},
Stacktrace,
- Debug, S, Q);
+ Debug, S, [Event|Events], State, Data, P);
Stacktrace ->
- terminate(error, badarg, Stacktrace, Debug, S, Q)
+ terminate(
+ error, badarg, Stacktrace,
+ Debug, S, [Event|Events], State, Data, P)
end;
error:undef ->
%% Process an undef to check for the simple mistake
@@ -852,7 +913,7 @@ loop_events(
error,
{undef_state_function,{Module,State,Args}},
Stacktrace,
- Debug, S, Q);
+ Debug, S, [Event|Events], State, Data, P);
[{Module,handle_event,
[Type,Content,State,Data]=Args,
_}
@@ -860,86 +921,85 @@ loop_events(
when CallbackMode =:= handle_event_function ->
terminate(
error,
- {undef_state_function,
- {Module,handle_event,Args}},
+ {undef_state_function,{Module,handle_event,Args}},
Stacktrace,
- Debug, S, Q);
+ Debug, S, [Event|Events], State, Data, P);
Stacktrace ->
- terminate(error, undef, Stacktrace, Debug, S, Q)
+ terminate(
+ error, undef, Stacktrace,
+ Debug, S, [Event|Events], State, Data, P)
end;
Class:Reason ->
Stacktrace = erlang:get_stacktrace(),
- terminate(Class, Reason, Stacktrace, Debug, S, Q)
+ terminate(
+ Class, Reason, Stacktrace,
+ Debug, S, [Event|Events], State, Data, P)
end.
%% Interpret all callback return variants
loop_event_result(
- Parent, Debug,
- #{state := State, data := Data} = S,
- Events, Event, Result) ->
- %% From now until we loop back to the loop_events/4
- %% the state and data fields in S are old
+ Parent, Debug, S, Events, State, Data, P, Event, Result) ->
case Result of
stop ->
- ?TERMINATE(exit, normal, Debug, S, [Event|Events]);
+ terminate(
+ exit, normal, ?STACKTRACE(),
+ Debug, S, [Event|Events], State, Data, P);
{stop,Reason} ->
- ?TERMINATE(exit, Reason, Debug, S, [Event|Events]);
+ terminate(
+ exit, Reason, ?STACKTRACE(),
+ Debug, S, [Event|Events], State, Data, P);
{stop,Reason,NewData} ->
- NewS = S#{data := NewData},
- Q = [Event|Events],
- ?TERMINATE(exit, Reason, Debug, NewS, Q);
+ terminate(
+ exit, Reason, ?STACKTRACE(),
+ Debug, S, [Event|Events], State, NewData, P);
{stop_and_reply,Reason,Replies} ->
Q = [Event|Events],
- [Class,NewReason,Stacktrace,NewDebug] =
- reply_then_terminate(
- exit, Reason, ?STACKTRACE(), Debug, S, Q, Replies),
- %% Since we got back here Replies was bad
- terminate(Class, NewReason, Stacktrace, NewDebug, S, Q);
+ reply_then_terminate(
+ exit, Reason, ?STACKTRACE(),
+ Debug, S, Q, State, Data, P, Replies);
{stop_and_reply,Reason,Replies,NewData} ->
- NewS = S#{data := NewData},
Q = [Event|Events],
- [Class,NewReason,Stacktrace,NewDebug] =
- reply_then_terminate(
- exit, Reason, ?STACKTRACE(), Debug, NewS, Q, Replies),
- %% Since we got back here Replies was bad
- terminate(Class, NewReason, Stacktrace, NewDebug, NewS, Q);
+ reply_then_terminate(
+ exit, Reason, ?STACKTRACE(),
+ Debug, S, Q, State, NewData, P, Replies);
{next_state,NextState,NewData} ->
loop_event_actions(
- Parent, Debug, S, Events, Event,
- State, NextState, NewData, []);
+ Parent, Debug, S, Events,
+ State, NewData, P, Event, NextState, []);
{next_state,NextState,NewData,Actions} ->
loop_event_actions(
- Parent, Debug, S, Events, Event,
- State, NextState, NewData, Actions);
+ Parent, Debug, S, Events,
+ State, NewData, P, Event, NextState, Actions);
{keep_state,NewData} ->
loop_event_actions(
- Parent, Debug, S, Events, Event,
- State, State, NewData, []);
+ Parent, Debug, S, Events,
+ State, NewData, P, Event, State, []);
{keep_state,NewData,Actions} ->
loop_event_actions(
- Parent, Debug, S, Events, Event,
- State, State, NewData, Actions);
+ Parent, Debug, S, Events,
+ State, NewData, P, Event, State, Actions);
keep_state_and_data ->
loop_event_actions(
- Parent, Debug, S, Events, Event,
- State, State, Data, []);
+ Parent, Debug, S, Events,
+ State, Data, P, Event, State, []);
{keep_state_and_data,Actions} ->
loop_event_actions(
- Parent, Debug, S, Events, Event,
- State, State, Data, Actions);
+ Parent, Debug, S, Events,
+ State, Data, P, Event, State, Actions);
_ ->
- ?TERMINATE(
- error, {bad_return_value,Result}, Debug, S, [Event|Events])
+ terminate(
+ error, {bad_return_value,Result}, ?STACKTRACE(),
+ Debug, S, [Event|Events], State, Data, P)
end.
loop_event_actions(
- Parent, Debug, S, Events, Event, State, NextState, NewData, Actions) ->
- Postpone = false, % Shall we postpone this event, true or false
+ Parent, Debug, S, Events, State, NewData, P, Event, NextState, Actions) ->
+ Postpone = false, % Shall we postpone this event; boolean()
Hibernate = false,
Timeout = undefined,
NextEvents = [],
loop_event_actions(
- Parent, Debug, S, Events, Event, State, NextState, NewData,
+ Parent, Debug, S, Events, State, NewData, P, Event, NextState,
if
is_list(Actions) ->
Actions;
@@ -948,97 +1008,103 @@ loop_event_actions(
end,
Postpone, Hibernate, Timeout, NextEvents).
%%
-%% Process all action()s
+%% Process all actions
loop_event_actions(
- Parent, Debug, S, Events, Event,
- State, NextState, NewData, [Action|Actions],
+ Parent, Debug, S, Events,
+ State, NewData, P, Event, NextState, [Action|Actions],
Postpone, Hibernate, Timeout, NextEvents) ->
case Action of
%% Actual actions
{reply,From,Reply} ->
case from(From) of
true ->
- NewDebug = do_reply(Debug, S, From, Reply),
+ NewDebug = do_reply(Debug, S, State, From, Reply),
loop_event_actions(
- Parent, NewDebug, S, Events, Event,
- State, NextState, NewData, Actions,
+ Parent, NewDebug, S, Events,
+ State, NewData, P, Event, NextState, Actions,
Postpone, Hibernate, Timeout, NextEvents);
false ->
- ?TERMINATE(
- error, {bad_action,Action}, Debug, S, [Event|Events])
+ terminate(
+ error, {bad_action,Action}, ?STACKTRACE(),
+ Debug, S, [Event|Events], State, NewData, P)
end;
{next_event,Type,Content} ->
case event_type(Type) of
true ->
loop_event_actions(
- Parent, Debug, S, Events, Event,
- State, NextState, NewData, Actions,
+ Parent, Debug, S, Events,
+ State, NewData, P, Event, NextState, Actions,
Postpone, Hibernate, Timeout,
[{Type,Content}|NextEvents]);
false ->
- ?TERMINATE(
- error, {bad_action,Action}, Debug, S, [Event|Events])
+ terminate(
+ error, {bad_action,Action}, ?STACKTRACE(),
+ Debug, S, [Event|Events], State, NewData, P)
end;
%% Actions that set options
{postpone,NewPostpone} when is_boolean(NewPostpone) ->
loop_event_actions(
- Parent, Debug, S, Events, Event,
- State, NextState, NewData, Actions,
+ Parent, Debug, S, Events,
+ State, NewData, P, Event, NextState, Actions,
NewPostpone, Hibernate, Timeout, NextEvents);
{postpone,_} ->
- ?TERMINATE(
- error, {bad_action,Action}, Debug, S, [Event|Events]);
+ terminate(
+ error, {bad_action,Action}, ?STACKTRACE(),
+ Debug, S, [Event|Events], State, NewData, P);
postpone ->
loop_event_actions(
- Parent, Debug, S, Events, Event,
- State, NextState, NewData, Actions,
+ Parent, Debug, S, Events,
+ State, NewData, P, Event, NextState, Actions,
true, Hibernate, Timeout, NextEvents);
{hibernate,NewHibernate} when is_boolean(NewHibernate) ->
loop_event_actions(
- Parent, Debug, S, Events, Event,
- State, NextState, NewData, Actions,
+ Parent, Debug, S, Events,
+ State, NewData, P, Event, NextState, Actions,
Postpone, NewHibernate, Timeout, NextEvents);
{hibernate,_} ->
- ?TERMINATE(
- error, {bad_action,Action}, Debug, S, [Event|Events]);
+ terminate(
+ error, {bad_action,Action}, ?STACKTRACE(),
+ Debug, S, [Event|Events], State, NewData, P);
hibernate ->
loop_event_actions(
- Parent, Debug, S, Events, Event,
- State, NextState, NewData, Actions,
+ Parent, Debug, S, Events,
+ State, NewData, P, Event, NextState, Actions,
Postpone, true, Timeout, NextEvents);
{timeout,infinity,_} -> % Clear timer - it will never trigger
loop_event_actions(
- Parent, Debug, S, Events, Event,
- State, NextState, NewData, Actions,
+ Parent, Debug, S, Events,
+ State, NewData, P, Event, NextState, Actions,
Postpone, Hibernate, undefined, NextEvents);
{timeout,Time,_} = NewTimeout when is_integer(Time), Time >= 0 ->
loop_event_actions(
- Parent, Debug, S, Events, Event,
- State, NextState, NewData, Actions,
+ Parent, Debug, S, Events,
+ State, NewData, P, Event, NextState, Actions,
Postpone, Hibernate, NewTimeout, NextEvents);
{timeout,_,_} ->
- ?TERMINATE(
- error, {bad_action,Action}, Debug, S, [Event|Events]);
+ terminate(
+ error, {bad_action,Action}, ?STACKTRACE(),
+ Debug, S, [Event|Events], State, NewData, P);
infinity -> % Clear timer - it will never trigger
loop_event_actions(
- Parent, Debug, S, Events, Event,
- State, NextState, NewData, Actions,
+ Parent, Debug, S, Events,
+ State, NewData, P, Event, NextState, Actions,
Postpone, Hibernate, undefined, NextEvents);
Time when is_integer(Time), Time >= 0 ->
NewTimeout = {timeout,Time,Time},
loop_event_actions(
- Parent, Debug, S, Events, Event,
- State, NextState, NewData, Actions,
+ Parent, Debug, S, Events,
+ State, NewData, P, Event, NextState, Actions,
Postpone, Hibernate, NewTimeout, NextEvents);
_ ->
- ?TERMINATE(
- error, {bad_action,Action}, Debug, S, [Event|Events])
+ terminate(
+ error, {bad_action,Action}, ?STACKTRACE(),
+ Debug, S, [Event|Events], State, NewData, P)
end;
%%
%% End of actions list
loop_event_actions(
- Parent, Debug, #{postponed := P0} = S, Events, Event,
- State, NextState, NewData, [],
+ Parent, Debug, S, Events,
+ State, NewData, P0, Event, NextState, [],
Postpone, Hibernate, Timeout, NextEvents) ->
%%
%% All options have been collected and next_events are buffered.
@@ -1059,89 +1125,62 @@ loop_event_actions(
{lists:reverse(P1, Events),[]}
end,
%% Place next events first in queue
- Q3 = lists:reverse(NextEvents, Q2),
+ Q = lists:reverse(NextEvents, Q2),
%%
NewDebug =
sys_debug(
- Debug, S,
+ Debug, S, State,
case Postpone of
true ->
{postpone,Event,NextState};
false ->
{consume,Event,NextState}
end),
- %% Have a peek on the event queue so we can avoid starting
- %% the state timer unless we have to
- {Q,Timer} =
- case Timeout of
- undefined ->
- %% No state timeout has been requested
- {Q3,undefined};
- {timeout,Time,EventContent} ->
- %% A state timeout has been requested
- case Q3 of
- [] when Time =:= 0 ->
- %% Immediate timeout - simulate it
- %% so we do not get the timeout message
- %% after any received event
- {[{timeout,EventContent}],undefined};
- [] ->
- %% Actually start a timer
- {Q3,erlang:start_timer(Time, self(), EventContent)};
- _ ->
- %% Do not start a timer since any queued
- %% event cancels the state timer so we pretend
- %% that the timer has been started and cancelled
- {Q3,undefined}
- end
- end,
- %% Loop to top of event queue loop; process next event
loop_events(
- Parent, NewDebug,
- S#{
- state := NextState,
- data := NewData,
- timer := Timer,
- postponed := P,
- hibernate := Hibernate},
- Q, Hibernate).
+ Parent, NewDebug, S, Q, NextState, NewData, P, Hibernate, Timeout).
%%---------------------------------------------------------------------------
%% Server helpers
-reply_then_terminate(Class, Reason, Stacktrace, Debug, S, Q, Replies) ->
+reply_then_terminate(
+ Class, Reason, Stacktrace,
+ Debug, S, Q, State, Data, P, Replies) ->
if
is_list(Replies) ->
do_reply_then_terminate(
- Class, Reason, Stacktrace, Debug, S, Q, Replies);
+ Class, Reason, Stacktrace,
+ Debug, S, Q, State, Data, P, Replies);
true ->
do_reply_then_terminate(
- Class, Reason, Stacktrace, Debug, S, Q, [Replies])
+ Class, Reason, Stacktrace,
+ Debug, S, Q, State, Data, P, [Replies])
end.
%%
-do_reply_then_terminate(Class, Reason, Stacktrace, Debug, S, Q, []) ->
- terminate(Class, Reason, Stacktrace, Debug, S, Q);
do_reply_then_terminate(
- Class, Reason, Stacktrace, Debug, S, Q, [R|Rs]) ->
+ Class, Reason, Stacktrace, Debug, S, Q, State, Data, P, []) ->
+ terminate(Class, Reason, Stacktrace, Debug, S, Q, State, Data, P);
+do_reply_then_terminate(
+ Class, Reason, Stacktrace, Debug, S, Q, State, Data, P, [R|Rs]) ->
case R of
{reply,{_To,_Tag}=From,Reply} ->
- NewDebug = do_reply(Debug, S, From, Reply),
+ NewDebug = do_reply(Debug, S, State, From, Reply),
do_reply_then_terminate(
- Class, Reason, Stacktrace, NewDebug, S, Q, Rs);
+ Class, Reason, Stacktrace,
+ NewDebug, S, Q, State, Data, P, Rs);
_ ->
- [error,{bad_action,R},?STACKTRACE(),Debug]
+ terminate(
+ error, {bad_action,R}, ?STACKTRACE(),
+ Debug, S, Q, State, Data, P)
end.
-do_reply(Debug, S, From, Reply) ->
+do_reply(Debug, S, State, From, Reply) ->
reply(From, Reply),
- sys_debug(Debug, S, {out,Reply,From}).
+ sys_debug(Debug, S, State, {out,Reply,From}).
terminate(
- Class, Reason, Stacktrace, Debug,
- #{module := Module,
- state := State, data := Data} = S,
- Q) ->
+ Class, Reason, Stacktrace,
+ Debug, #{module := Module} = S, Q, State, Data, P) ->
try Module:terminate(Reason, State, Data) of
_ -> ok
catch
@@ -1149,8 +1188,8 @@ terminate(
C:R ->
ST = erlang:get_stacktrace(),
error_info(
- C, R, ST, Debug, S, Q,
- format_status(terminate, get(), S)),
+ C, R, ST, Debug, S, Q, P,
+ format_status(terminate, get(), S, State, Data)),
erlang:raise(C, R, ST)
end,
case Reason of
@@ -1159,8 +1198,8 @@ terminate(
{shutdown,_} -> ok;
_ ->
error_info(
- Class, Reason, Stacktrace, Debug, S, Q,
- format_status(terminate, get(), S))
+ Class, Reason, Stacktrace, Debug, S, Q, P,
+ format_status(terminate, get(), S, State, Data))
end,
case Stacktrace of
[] ->
@@ -1171,9 +1210,8 @@ terminate(
error_info(
Class, Reason, Stacktrace, Debug,
- #{name := Name, callback_mode := CallbackMode,
- state := State, postponed := P},
- Q, FmtData) ->
+ #{name := Name, callback_mode := CallbackMode},
+ Q, P, FmtData) ->
{FixedReason,FixedStacktrace} =
case Stacktrace of
[{M,F,Args,_}|ST]
@@ -1202,46 +1240,49 @@ error_info(
error_logger:format(
"** State machine ~p terminating~n" ++
case Q of
- [] ->
- "";
- _ ->
- "** Last event = ~p~n"
+ [] -> "";
+ _ -> "** Last event = ~p~n"
end ++
- "** When Server state = ~p~n" ++
+ "** When server state = ~p~n" ++
"** Reason for termination = ~w:~p~n" ++
- "** State = ~p~n" ++
"** Callback mode = ~p~n" ++
- "** Queued/Postponed = ~w/~w~n" ++
+ case Q of
+ [_,_|_] -> "** Queued = ~p~n";
+ _ -> ""
+ end ++
+ case P of
+ [] -> "";
+ _ -> "** Postponed = ~p~n"
+ end ++
case FixedStacktrace of
- [] ->
- "";
- _ ->
- "** Stacktrace =~n"
- "** ~p~n"
+ [] -> "";
+ _ -> "** Stacktrace =~n** ~p~n"
end,
[Name |
case Q of
- [] ->
- [];
- [Event|_] ->
- [Event]
+ [] -> [];
+ [Event|_] -> [Event]
end] ++
[FmtData,Class,FixedReason,
- State,CallbackMode,length(Q),length(P)] ++
+ CallbackMode] ++
+ case Q of
+ [_|[_|_] = Events] -> [Events];
+ _ -> []
+ end ++
+ case P of
+ [] -> [];
+ _ -> [P]
+ end ++
case FixedStacktrace of
- [] ->
- [];
- _ ->
- [FixedStacktrace]
+ [] -> [];
+ _ -> [FixedStacktrace]
end),
sys:print_log(Debug),
ok.
%% Call Module:format_status/2 or return a default value
-format_status(
- Opt, PDict,
- #{module := Module, state := State, data := Data}) ->
+format_status(Opt, PDict, #{module := Module}, State, Data) ->
case erlang:function_exported(Module, format_status, 2) of
true ->
try Module:format_status(Opt, [PDict,State,Data])
diff --git a/lib/stdlib/src/proc_lib.erl b/lib/stdlib/src/proc_lib.erl
index 3f79ed0f87..fdc2ae4070 100644
--- a/lib/stdlib/src/proc_lib.erl
+++ b/lib/stdlib/src/proc_lib.erl
@@ -48,7 +48,9 @@
| {'priority', priority_level()}
| {'min_heap_size', non_neg_integer()}
| {'min_bin_vheap_size', non_neg_integer()}
- | {'fullsweep_after', non_neg_integer()}.
+ | {'fullsweep_after', non_neg_integer()}
+ | {'message_queue_data',
+ 'off_heap' | 'on_heap' | 'mixed' }.
-type dict_or_pid() :: pid()
| (ProcInfo :: [_])
diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl
index 6fea198af3..b0214e5238 100644
--- a/lib/stdlib/test/erl_lint_SUITE.erl
+++ b/lib/stdlib/test/erl_lint_SUITE.erl
@@ -33,47 +33,38 @@
-define(privdir, proplists:get_value(priv_dir, Conf)).
-endif.
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
- init_per_testcase/2, end_per_testcase/2]).
-
--export([
- unused_vars_warn_basic/1,
- unused_vars_warn_lc/1,
- unused_vars_warn_rec/1,
- unused_vars_warn_fun/1,
- unused_vars_OTP_4858/1,
- unused_unsafe_vars_warn/1,
- export_vars_warn/1,
- shadow_vars/1,
- unused_import/1,
- unused_function/1,
- unsafe_vars/1,unsafe_vars2/1,
- unsafe_vars_try/1,
- unsized_binary_in_bin_gen_pattern/1,
- guard/1, otp_4886/1, otp_4988/1, otp_5091/1, otp_5276/1, otp_5338/1,
- otp_5362/1, otp_5371/1, otp_7227/1, otp_5494/1, otp_5644/1, otp_5878/1,
- otp_5917/1, otp_6585/1, otp_6885/1, otp_10436/1, otp_11254/1,
- otp_11772/1, otp_11771/1, otp_11872/1,
- export_all/1,
- bif_clash/1,
- behaviour_basic/1, behaviour_multiple/1, otp_11861/1,
- otp_7550/1,
- otp_8051/1,
- format_warn/1,
- on_load_successful/1, on_load_failing/1,
- too_many_arguments/1,
- basic_errors/1,bin_syntax_errors/1,
- predef/1,
- maps/1,maps_type/1,otp_11851/1,otp_11879/1,otp_13230/1,
- record_errors/1
- ]).
-
-init_per_testcase(_Case, Config) ->
- Config.
-
-end_per_testcase(_Case, _Config) ->
- ok.
+-export([all/0, suite/0, groups/0]).
+
+-export([unused_vars_warn_basic/1,
+ unused_vars_warn_lc/1,
+ unused_vars_warn_rec/1,
+ unused_vars_warn_fun/1,
+ unused_vars_OTP_4858/1,
+ unused_unsafe_vars_warn/1,
+ export_vars_warn/1,
+ shadow_vars/1,
+ unused_import/1,
+ unused_function/1,
+ unsafe_vars/1,unsafe_vars2/1,
+ unsafe_vars_try/1,
+ unsized_binary_in_bin_gen_pattern/1,
+ guard/1, otp_4886/1, otp_4988/1, otp_5091/1, otp_5276/1, otp_5338/1,
+ otp_5362/1, otp_5371/1, otp_7227/1, otp_5494/1, otp_5644/1, otp_5878/1,
+ otp_5917/1, otp_6585/1, otp_6885/1, otp_10436/1, otp_11254/1,
+ otp_11772/1, otp_11771/1, otp_11872/1,
+ export_all/1,
+ bif_clash/1,
+ behaviour_basic/1, behaviour_multiple/1, otp_11861/1,
+ otp_7550/1,
+ otp_8051/1,
+ format_warn/1,
+ on_load_successful/1, on_load_failing/1,
+ too_many_arguments/1,
+ basic_errors/1,bin_syntax_errors/1,
+ predef/1,
+ maps/1,maps_type/1,maps_parallel_match/1,
+ otp_11851/1,otp_11879/1,otp_13230/1,
+ record_errors/1]).
suite() ->
[{ct_hooks,[ts_install_cth]},
@@ -91,7 +82,8 @@ all() ->
bif_clash, behaviour_basic, behaviour_multiple, otp_11861,
otp_7550, otp_8051, format_warn, {group, on_load},
too_many_arguments, basic_errors, bin_syntax_errors, predef,
- maps, maps_type, otp_11851, otp_11879, otp_13230,
+ maps, maps_type, maps_parallel_match,
+ otp_11851, otp_11879, otp_13230,
record_errors].
groups() ->
@@ -101,19 +93,6 @@ groups() ->
unused_vars_OTP_4858, unused_unsafe_vars_warn]},
{on_load, [], [on_load_successful, on_load_failing]}].
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
%% Warnings for unused variables in some simple cases.
unused_vars_warn_basic(Config) when is_list(Config) ->
@@ -3583,8 +3562,6 @@ predef(Config) when is_list(Config) ->
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,
@@ -3626,6 +3603,26 @@ maps(Config) ->
{errors,[{1,erl_lint,illegal_map_construction},
{1,erl_lint,{unbound_var,'X'}}],
[]}},
+ {legal_map_pattern,
+ <<"
+ -record(mapkey, {a=1,b=2}).
+ t(M,K1) ->
+ #{ a := 1,
+ $a := 1, $z := 99,
+ #{a=>val} := 2,
+ K1 := 1337,
+ #mapkey{a = 10} := wat,
+ #{{a,val}=>val} := 2,
+ #{ \"hi\" => wazzup, hi => ho } := yep,
+ ok := 1.0,
+ [3+3] := nope,
+ 1.0 := yep,
+ {3.0+3} := nope,
+ {yep} := yep
+ } = M.
+ ">>,
+ [],
+ []},
{legal_map_construction,
<<"t(V) -> #{ a => 1,
#{a=>V} => 2,
@@ -3692,6 +3689,51 @@ maps_type(Config) when is_list(Config) ->
[] = run(Config, Ts),
ok.
+maps_parallel_match(Config) when is_list(Config) ->
+ Ts = [{parallel_map_patterns_unbound1,
+ <<"
+ t(#{} = M) ->
+ #{K := V} = #{k := K} = M,
+ V.
+ ">>,
+ [],
+ {errors,[{3,erl_lint,{unbound_var,'K'}}],[]}},
+ {parallel_map_patterns_unbound2,
+ <<"
+ t(#{} = M) ->
+ #{K1 := V1} =
+ #{K2 := V2} =
+ #{k1 := K1,k2 := K2} = M,
+ [V1,V2].
+ ">>,
+ [],
+ {errors,[{3,erl_lint,{unbound_var,'K1'}},
+ {3,erl_lint,{unbound_var,'K1'}},
+ {4,erl_lint,{unbound_var,'K2'}},
+ {4,erl_lint,{unbound_var,'K2'}}],[]}},
+ {parallel_map_patterns_bound,
+ <<"
+ t(#{} = M,K1,K2) ->
+ #{K1 := V1} =
+ #{K2 := V2} =
+ #{k1 := K1,k2 := K2} = M,
+ [V1,V2].
+ ">>,
+ [],
+ []},
+ {parallel_map_patterns_literal,
+ <<"
+ t(#{} = M) ->
+ #{k1 := V1} =
+ #{k2 := V2} =
+ #{k1 := V1,k2 := V2} = M,
+ [V1,V2].
+ ">>,
+ [],
+ []}],
+ [] = run(Config, Ts),
+ ok.
+
%% OTP-11851: More atoms can be used as type names + bug fixes.
otp_11851(Config) when is_list(Config) ->
Ts = [
diff --git a/lib/stdlib/test/gen_statem_SUITE.erl b/lib/stdlib/test/gen_statem_SUITE.erl
index 3deb5fd986..364314f91b 100644
--- a/lib/stdlib/test/gen_statem_SUITE.erl
+++ b/lib/stdlib/test/gen_statem_SUITE.erl
@@ -634,12 +634,14 @@ sys1(Config) ->
stop_it(Pid).
code_change(Config) ->
+ Mode = handle_event_function,
{ok,Pid} = gen_statem:start(?MODULE, start_arg(Config, []), []),
{idle,data} = sys:get_state(Pid),
sys:suspend(Pid),
- sys:change_code(Pid, ?MODULE, old_vsn, state_functions),
+ sys:change_code(Pid, ?MODULE, old_vsn, Mode),
sys:resume(Pid),
- {idle,{old_vsn,data,state_functions}} = sys:get_state(Pid),
+ {idle,{old_vsn,data,Mode}} = sys:get_state(Pid),
+ Mode = gen_statem:call(Pid, get_callback_mode),
stop_it(Pid).
call_format_status(Config) ->
@@ -1478,6 +1480,8 @@ next_events(Type, Content, Data) ->
end.
+handle_common_events({call,From}, get_callback_mode, _, _) ->
+ {keep_state_and_data,{reply,From,state_functions}};
handle_common_events({call,From}, get, State, Data) ->
{keep_state,Data,
[{reply,From,{state,State,Data}}]};
@@ -1501,6 +1505,8 @@ handle_common_events(cast, {'alive?',Pid}, _, Data) ->
handle_common_events(_, _, _, _) ->
undefined.
+handle_event({call,From}, get_callback_mode, _, _) ->
+ {keep_state_and_data,{reply,From,handle_event_function}};
%% Wrapper state machine that uses a map state machine spec
handle_event(
Type, Event, State, [Data|Machine])