@@ -107,7 +107,12 @@ impl GotoStateMachine {
107107
108108impl StateMachine for GotoStateMachine {
109109 fn emit ( self ) -> TokenStream2 {
110- let StateMachineNames { label : lbl, state : s, break_flag, cont_flag } = self . names ;
110+ let StateMachineNames {
111+ label : lbl,
112+ state : s,
113+ break_flag,
114+ cont_flag,
115+ } = self . names ;
111116
112117 let n = self . arms . len ( ) ;
113118 let mut arms_have_break = false ;
@@ -118,6 +123,17 @@ impl StateMachine for GotoStateMachine {
118123 . enumerate ( )
119124 . map ( |( i, arm) | {
120125 let mut body = arm. body . clone ( ) ;
126+ GotoRewriter {
127+ map : & self
128+ . arms
129+ . iter ( )
130+ . enumerate ( )
131+ . map ( |( i, a) | ( a. label . clone ( ) , i as u32 ) )
132+ . collect ( ) ,
133+ state : s. clone ( ) ,
134+ sm_label : lbl. clone ( ) ,
135+ }
136+ . visit_expr_mut ( & mut body) ;
121137 let ( had_br, had_cn) =
122138 Self :: propagate_rewrite ( & mut body, & lbl, & break_flag, & cont_flag) ;
123139 arms_have_break |= had_br;
@@ -148,6 +164,58 @@ impl StateMachine for GotoStateMachine {
148164 }
149165}
150166
167+ // Rewrites `goto!('label)` into `{ __s = <target index>; continue '__sm; }`.
168+ struct GotoRewriter < ' a > {
169+ // Map with labels and their indices inside the current state machine. Used to check if the
170+ // label the goto jumps to is part of the current state machine. If it is, emit
171+ // `__s = map[label]`
172+ map : & ' a HashMap < String , u32 > ,
173+ state : Ident ,
174+ sm_label : Lifetime ,
175+ }
176+
177+ impl GotoRewriter < ' _ > {
178+ fn expand_goto_into_state_machine_jump ( & self , tokens : & TokenStream2 ) -> Option < Expr > {
179+ let idx = * self . map . get (
180+ & syn:: parse2 :: < Lifetime > ( tokens. clone ( ) )
181+ . expect ( "goto! expects a lifetime label" )
182+ . ident
183+ . to_string ( ) ,
184+ ) ?;
185+ let state = & self . state ;
186+ let sm_label = & self . sm_label ;
187+ Some ( parse_quote ! ( { #state = #idx; continue #sm_label; } ) )
188+ }
189+
190+ fn recurse_into_inner_goto_block ( & mut self , mac : & mut syn:: Macro ) -> bool {
191+ if mac. path . is_ident ( "switch" ) || mac. path . is_ident ( "goto_block" ) {
192+ if let Ok ( mut inner) = syn:: parse2 :: < Expr > ( mac. tokens . clone ( ) ) {
193+ self . visit_expr_mut ( & mut inner) ;
194+ mac. tokens = quote ! ( #inner) ;
195+ }
196+ return true ;
197+ }
198+ false
199+ }
200+ }
201+
202+ impl VisitMut for GotoRewriter < ' _ > {
203+ fn visit_stmt_mut ( & mut self , stmt : & mut Stmt ) {
204+ if let Stmt :: Macro ( sm) = stmt {
205+ if sm. mac . path . is_ident ( "goto" ) {
206+ if let Some ( jump) = self . expand_goto_into_state_machine_jump ( & sm. mac . tokens ) {
207+ * stmt = Stmt :: Expr ( jump, Some ( Default :: default ( ) ) ) ;
208+ }
209+ return ;
210+ }
211+ if self . recurse_into_inner_goto_block ( & mut sm. mac ) {
212+ return ;
213+ }
214+ }
215+ visit_mut:: visit_stmt_mut ( self , stmt) ;
216+ }
217+ }
218+
151219// GotoStateMachine(dispatch arm + cases)
152220pub struct SwitchStateMachine {
153221 pub goto : GotoStateMachine ,
0 commit comments