]> git.lizzy.rs Git - rust.git/blob - src/librustc/cfg/construct.rs
add -Z pre-link-arg{,s} to rustc
[rust.git] / src / librustc / cfg / construct.rs
1 // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use rustc_data_structures::graph;
12 use cfg::*;
13 use ty::{self, TyCtxt};
14 use syntax::ast;
15 use syntax::ptr::P;
16
17 use hir::{self, PatKind};
18 use hir::def_id::DefId;
19
20 struct CFGBuilder<'a, 'tcx: 'a> {
21     tcx: TyCtxt<'a, 'tcx, 'tcx>,
22     owner_def_id: DefId,
23     tables: &'a ty::TypeckTables<'tcx>,
24     graph: CFGGraph,
25     fn_exit: CFGIndex,
26     loop_scopes: Vec<LoopScope>,
27     breakable_block_scopes: Vec<BlockScope>,
28 }
29
30 #[derive(Copy, Clone)]
31 struct BlockScope {
32     block_expr_id: ast::NodeId, // id of breakable block expr node
33     break_index: CFGIndex, // where to go on `break`
34 }
35
36 #[derive(Copy, Clone)]
37 struct LoopScope {
38     loop_id: ast::NodeId,     // id of loop/while node
39     continue_index: CFGIndex, // where to go on a `loop`
40     break_index: CFGIndex,    // where to go on a `break`
41 }
42
43 pub fn construct<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
44                            body: &hir::Body) -> CFG {
45     let mut graph = graph::Graph::new();
46     let entry = graph.add_node(CFGNodeData::Entry);
47
48     // `fn_exit` is target of return exprs, which lies somewhere
49     // outside input `body`. (Distinguishing `fn_exit` and `body_exit`
50     // also resolves chicken-and-egg problem that arises if you try to
51     // have return exprs jump to `body_exit` during construction.)
52     let fn_exit = graph.add_node(CFGNodeData::Exit);
53     let body_exit;
54
55     // Find the tables for this body.
56     let owner_def_id = tcx.hir.local_def_id(tcx.hir.body_owner(body.id()));
57     let tables = tcx.typeck_tables_of(owner_def_id);
58
59     let mut cfg_builder = CFGBuilder {
60         tcx: tcx,
61         owner_def_id,
62         tables: tables,
63         graph: graph,
64         fn_exit: fn_exit,
65         loop_scopes: Vec::new(),
66         breakable_block_scopes: Vec::new(),
67     };
68     body_exit = cfg_builder.expr(&body.value, entry);
69     cfg_builder.add_contained_edge(body_exit, fn_exit);
70     let CFGBuilder { graph, .. } = cfg_builder;
71     CFG {
72         graph: graph,
73         entry: entry,
74         exit: fn_exit,
75     }
76 }
77
78 impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
79     fn block(&mut self, blk: &hir::Block, pred: CFGIndex) -> CFGIndex {
80         if blk.targeted_by_break {
81             let expr_exit = self.add_ast_node(blk.id, &[]);
82
83             self.breakable_block_scopes.push(BlockScope {
84                 block_expr_id: blk.id,
85                 break_index: expr_exit,
86             });
87
88             let mut stmts_exit = pred;
89             for stmt in &blk.stmts {
90                 stmts_exit = self.stmt(stmt, stmts_exit);
91             }
92             let blk_expr_exit = self.opt_expr(&blk.expr, stmts_exit);
93             self.add_contained_edge(blk_expr_exit, expr_exit);
94
95             self.breakable_block_scopes.pop();
96
97             expr_exit
98         } else {
99             let mut stmts_exit = pred;
100             for stmt in &blk.stmts {
101                 stmts_exit = self.stmt(stmt, stmts_exit);
102             }
103
104             let expr_exit = self.opt_expr(&blk.expr, stmts_exit);
105
106             self.add_ast_node(blk.id, &[expr_exit])
107         }
108     }
109
110     fn stmt(&mut self, stmt: &hir::Stmt, pred: CFGIndex) -> CFGIndex {
111         match stmt.node {
112             hir::StmtDecl(ref decl, id) => {
113                 let exit = self.decl(&decl, pred);
114                 self.add_ast_node(id, &[exit])
115             }
116
117             hir::StmtExpr(ref expr, id) |
118             hir::StmtSemi(ref expr, id) => {
119                 let exit = self.expr(&expr, pred);
120                 self.add_ast_node(id, &[exit])
121             }
122         }
123     }
124
125     fn decl(&mut self, decl: &hir::Decl, pred: CFGIndex) -> CFGIndex {
126         match decl.node {
127             hir::DeclLocal(ref local) => {
128                 let init_exit = self.opt_expr(&local.init, pred);
129                 self.pat(&local.pat, init_exit)
130             }
131
132             hir::DeclItem(_) => pred,
133         }
134     }
135
136     fn pat(&mut self, pat: &hir::Pat, pred: CFGIndex) -> CFGIndex {
137         match pat.node {
138             PatKind::Binding(.., None) |
139             PatKind::Path(_) |
140             PatKind::Lit(..) |
141             PatKind::Range(..) |
142             PatKind::Wild => self.add_ast_node(pat.id, &[pred]),
143
144             PatKind::Box(ref subpat) |
145             PatKind::Ref(ref subpat, _) |
146             PatKind::Binding(.., Some(ref subpat)) => {
147                 let subpat_exit = self.pat(&subpat, pred);
148                 self.add_ast_node(pat.id, &[subpat_exit])
149             }
150
151             PatKind::TupleStruct(_, ref subpats, _) |
152             PatKind::Tuple(ref subpats, _) => {
153                 let pats_exit = self.pats_all(subpats.iter(), pred);
154                 self.add_ast_node(pat.id, &[pats_exit])
155             }
156
157             PatKind::Struct(_, ref subpats, _) => {
158                 let pats_exit = self.pats_all(subpats.iter().map(|f| &f.node.pat), pred);
159                 self.add_ast_node(pat.id, &[pats_exit])
160             }
161
162             PatKind::Slice(ref pre, ref vec, ref post) => {
163                 let pre_exit = self.pats_all(pre.iter(), pred);
164                 let vec_exit = self.pats_all(vec.iter(), pre_exit);
165                 let post_exit = self.pats_all(post.iter(), vec_exit);
166                 self.add_ast_node(pat.id, &[post_exit])
167             }
168         }
169     }
170
171     fn pats_all<'b, I: Iterator<Item=&'b P<hir::Pat>>>(&mut self,
172                                           pats: I,
173                                           pred: CFGIndex) -> CFGIndex {
174         //! Handles case where all of the patterns must match.
175         pats.fold(pred, |pred, pat| self.pat(&pat, pred))
176     }
177
178     fn expr(&mut self, expr: &hir::Expr, pred: CFGIndex) -> CFGIndex {
179         match expr.node {
180             hir::ExprBlock(ref blk) => {
181                 let blk_exit = self.block(&blk, pred);
182                 self.add_ast_node(expr.id, &[blk_exit])
183             }
184
185             hir::ExprIf(ref cond, ref then, None) => {
186                 //
187                 //     [pred]
188                 //       |
189                 //       v 1
190                 //     [cond]
191                 //       |
192                 //      / \
193                 //     /   \
194                 //    v 2   *
195                 //  [then]  |
196                 //    |     |
197                 //    v 3   v 4
198                 //   [..expr..]
199                 //
200                 let cond_exit = self.expr(&cond, pred);                // 1
201                 let then_exit = self.expr(&then, cond_exit);          // 2
202                 self.add_ast_node(expr.id, &[cond_exit, then_exit])      // 3,4
203             }
204
205             hir::ExprIf(ref cond, ref then, Some(ref otherwise)) => {
206                 //
207                 //     [pred]
208                 //       |
209                 //       v 1
210                 //     [cond]
211                 //       |
212                 //      / \
213                 //     /   \
214                 //    v 2   v 3
215                 //  [then][otherwise]
216                 //    |     |
217                 //    v 4   v 5
218                 //   [..expr..]
219                 //
220                 let cond_exit = self.expr(&cond, pred);                // 1
221                 let then_exit = self.expr(&then, cond_exit);          // 2
222                 let else_exit = self.expr(&otherwise, cond_exit);      // 3
223                 self.add_ast_node(expr.id, &[then_exit, else_exit])      // 4, 5
224             }
225
226             hir::ExprWhile(ref cond, ref body, _) => {
227                 //
228                 //         [pred]
229                 //           |
230                 //           v 1
231                 //       [loopback] <--+ 5
232                 //           |         |
233                 //           v 2       |
234                 //   +-----[cond]      |
235                 //   |       |         |
236                 //   |       v 4       |
237                 //   |     [body] -----+
238                 //   v 3
239                 // [expr]
240                 //
241                 // Note that `break` and `continue` statements
242                 // may cause additional edges.
243
244                 let loopback = self.add_dummy_node(&[pred]);              // 1
245
246                 // Create expr_exit without pred (cond_exit)
247                 let expr_exit = self.add_ast_node(expr.id, &[]);         // 3
248
249                 // The LoopScope needs to be on the loop_scopes stack while evaluating the
250                 // condition and the body of the loop (both can break out of the loop)
251                 self.loop_scopes.push(LoopScope {
252                     loop_id: expr.id,
253                     continue_index: loopback,
254                     break_index: expr_exit
255                 });
256
257                 let cond_exit = self.expr(&cond, loopback);             // 2
258
259                 // Add pred (cond_exit) to expr_exit
260                 self.add_contained_edge(cond_exit, expr_exit);
261
262                 let body_exit = self.block(&body, cond_exit);          // 4
263                 self.add_contained_edge(body_exit, loopback);            // 5
264                 self.loop_scopes.pop();
265                 expr_exit
266             }
267
268             hir::ExprLoop(ref body, _, _) => {
269                 //
270                 //     [pred]
271                 //       |
272                 //       v 1
273                 //   [loopback] <---+
274                 //       |      4   |
275                 //       v 3        |
276                 //     [body] ------+
277                 //
278                 //     [expr] 2
279                 //
280                 // Note that `break` and `loop` statements
281                 // may cause additional edges.
282
283                 let loopback = self.add_dummy_node(&[pred]);              // 1
284                 let expr_exit = self.add_ast_node(expr.id, &[]);          // 2
285                 self.loop_scopes.push(LoopScope {
286                     loop_id: expr.id,
287                     continue_index: loopback,
288                     break_index: expr_exit,
289                 });
290                 let body_exit = self.block(&body, loopback);           // 3
291                 self.add_contained_edge(body_exit, loopback);            // 4
292                 self.loop_scopes.pop();
293                 expr_exit
294             }
295
296             hir::ExprMatch(ref discr, ref arms, _) => {
297                 self.match_(expr.id, &discr, &arms, pred)
298             }
299
300             hir::ExprBinary(op, ref l, ref r) if op.node.is_lazy() => {
301                 //
302                 //     [pred]
303                 //       |
304                 //       v 1
305                 //      [l]
306                 //       |
307                 //      / \
308                 //     /   \
309                 //    v 2  *
310                 //   [r]   |
311                 //    |    |
312                 //    v 3  v 4
313                 //   [..exit..]
314                 //
315                 let l_exit = self.expr(&l, pred);                      // 1
316                 let r_exit = self.expr(&r, l_exit);                    // 2
317                 self.add_ast_node(expr.id, &[l_exit, r_exit])            // 3,4
318             }
319
320             hir::ExprRet(ref v) => {
321                 let v_exit = self.opt_expr(v, pred);
322                 let b = self.add_ast_node(expr.id, &[v_exit]);
323                 self.add_returning_edge(expr, b);
324                 self.add_unreachable_node()
325             }
326
327             hir::ExprBreak(destination, ref opt_expr) => {
328                 let v = self.opt_expr(opt_expr, pred);
329                 let (scope_id, break_dest) =
330                     self.find_scope_edge(expr, destination, ScopeCfKind::Break);
331                 let b = self.add_ast_node(expr.id, &[v]);
332                 self.add_exiting_edge(expr, b, scope_id, break_dest);
333                 self.add_unreachable_node()
334             }
335
336             hir::ExprAgain(destination) => {
337                 let (scope_id, cont_dest) =
338                     self.find_scope_edge(expr, destination, ScopeCfKind::Continue);
339                 let a = self.add_ast_node(expr.id, &[pred]);
340                 self.add_exiting_edge(expr, a, scope_id, cont_dest);
341                 self.add_unreachable_node()
342             }
343
344             hir::ExprArray(ref elems) => {
345                 self.straightline(expr, pred, elems.iter().map(|e| &*e))
346             }
347
348             hir::ExprCall(ref func, ref args) => {
349                 self.call(expr, pred, &func, args.iter().map(|e| &*e))
350             }
351
352             hir::ExprMethodCall(.., ref args) => {
353                 self.call(expr, pred, &args[0], args[1..].iter().map(|e| &*e))
354             }
355
356             hir::ExprIndex(ref l, ref r) |
357             hir::ExprBinary(_, ref l, ref r) if self.tables.is_method_call(expr.id) => {
358                 self.call(expr, pred, &l, Some(&**r).into_iter())
359             }
360
361             hir::ExprUnary(_, ref e) if self.tables.is_method_call(expr.id) => {
362                 self.call(expr, pred, &e, None::<hir::Expr>.iter())
363             }
364
365             hir::ExprTup(ref exprs) => {
366                 self.straightline(expr, pred, exprs.iter().map(|e| &*e))
367             }
368
369             hir::ExprStruct(_, ref fields, ref base) => {
370                 let field_cfg = self.straightline(expr, pred, fields.iter().map(|f| &*f.expr));
371                 self.opt_expr(base, field_cfg)
372             }
373
374             hir::ExprAssign(ref l, ref r) |
375             hir::ExprAssignOp(_, ref l, ref r) => {
376                 self.straightline(expr, pred, [r, l].iter().map(|&e| &**e))
377             }
378
379             hir::ExprIndex(ref l, ref r) |
380             hir::ExprBinary(_, ref l, ref r) => { // NB: && and || handled earlier
381                 self.straightline(expr, pred, [l, r].iter().map(|&e| &**e))
382             }
383
384             hir::ExprBox(ref e) |
385             hir::ExprAddrOf(_, ref e) |
386             hir::ExprCast(ref e, _) |
387             hir::ExprType(ref e, _) |
388             hir::ExprUnary(_, ref e) |
389             hir::ExprField(ref e, _) |
390             hir::ExprTupField(ref e, _) |
391             hir::ExprRepeat(ref e, _) => {
392                 self.straightline(expr, pred, Some(&**e).into_iter())
393             }
394
395             hir::ExprInlineAsm(_, ref outputs, ref inputs) => {
396                 let post_outputs = self.exprs(outputs.iter().map(|e| &*e), pred);
397                 let post_inputs = self.exprs(inputs.iter().map(|e| &*e), post_outputs);
398                 self.add_ast_node(expr.id, &[post_inputs])
399             }
400
401             hir::ExprClosure(..) |
402             hir::ExprLit(..) |
403             hir::ExprPath(_) => {
404                 self.straightline(expr, pred, None::<hir::Expr>.iter())
405             }
406         }
407     }
408
409     fn call<'b, I: Iterator<Item=&'b hir::Expr>>(&mut self,
410             call_expr: &hir::Expr,
411             pred: CFGIndex,
412             func_or_rcvr: &hir::Expr,
413             args: I) -> CFGIndex {
414         let method_call = ty::MethodCall::expr(call_expr.id);
415         let fn_ty = match self.tables.method_map.get(&method_call) {
416             Some(method) => method.ty,
417             None => self.tables.expr_ty_adjusted(func_or_rcvr),
418         };
419
420         let func_or_rcvr_exit = self.expr(func_or_rcvr, pred);
421         let ret = self.straightline(call_expr, func_or_rcvr_exit, args);
422         // FIXME(canndrew): This is_never should probably be an is_uninhabited.
423         if fn_ty.fn_ret().0.is_never() {
424             self.add_unreachable_node()
425         } else {
426             ret
427         }
428     }
429
430     fn exprs<'b, I: Iterator<Item=&'b hir::Expr>>(&mut self,
431                                              exprs: I,
432                                              pred: CFGIndex) -> CFGIndex {
433         //! Constructs graph for `exprs` evaluated in order
434         exprs.fold(pred, |p, e| self.expr(e, p))
435     }
436
437     fn opt_expr(&mut self,
438                 opt_expr: &Option<P<hir::Expr>>,
439                 pred: CFGIndex) -> CFGIndex {
440         //! Constructs graph for `opt_expr` evaluated, if Some
441         opt_expr.iter().fold(pred, |p, e| self.expr(&e, p))
442     }
443
444     fn straightline<'b, I: Iterator<Item=&'b hir::Expr>>(&mut self,
445                     expr: &hir::Expr,
446                     pred: CFGIndex,
447                     subexprs: I) -> CFGIndex {
448         //! Handles case of an expression that evaluates `subexprs` in order
449
450         let subexprs_exit = self.exprs(subexprs, pred);
451         self.add_ast_node(expr.id, &[subexprs_exit])
452     }
453
454     fn match_(&mut self, id: ast::NodeId, discr: &hir::Expr,
455               arms: &[hir::Arm], pred: CFGIndex) -> CFGIndex {
456         // The CFG for match expression is quite complex, so no ASCII
457         // art for it (yet).
458         //
459         // The CFG generated below matches roughly what trans puts
460         // out. Each pattern and guard is visited in parallel, with
461         // arms containing multiple patterns generating multiple nodes
462         // for the same guard expression. The guard expressions chain
463         // into each other from top to bottom, with a specific
464         // exception to allow some additional valid programs
465         // (explained below). Trans differs slightly in that the
466         // pattern matching may continue after a guard but the visible
467         // behaviour should be the same.
468         //
469         // What is going on is explained in further comments.
470
471         // Visit the discriminant expression
472         let discr_exit = self.expr(discr, pred);
473
474         // Add a node for the exit of the match expression as a whole.
475         let expr_exit = self.add_ast_node(id, &[]);
476
477         // Keep track of the previous guard expressions
478         let mut prev_guards = Vec::new();
479         // Track if the previous pattern contained bindings or wildcards
480         let mut prev_has_bindings = false;
481
482         for arm in arms {
483             // Add an exit node for when we've visited all the
484             // patterns and the guard (if there is one) in the arm.
485             let arm_exit = self.add_dummy_node(&[]);
486
487             for pat in &arm.pats {
488                 // Visit the pattern, coming from the discriminant exit
489                 let mut pat_exit = self.pat(&pat, discr_exit);
490
491                 // If there is a guard expression, handle it here
492                 if let Some(ref guard) = arm.guard {
493                     // Add a dummy node for the previous guard
494                     // expression to target
495                     let guard_start = self.add_dummy_node(&[pat_exit]);
496                     // Visit the guard expression
497                     let guard_exit = self.expr(&guard, guard_start);
498
499                     let this_has_bindings = pat.contains_bindings_or_wild();
500
501                     // If both this pattern and the previous pattern
502                     // were free of bindings, they must consist only
503                     // of "constant" patterns. Note we cannot match an
504                     // all-constant pattern, fail the guard, and then
505                     // match *another* all-constant pattern. This is
506                     // because if the previous pattern matches, then
507                     // we *cannot* match this one, unless all the
508                     // constants are the same (which is rejected by
509                     // `check_match`).
510                     //
511                     // We can use this to be smarter about the flow
512                     // along guards. If the previous pattern matched,
513                     // then we know we will not visit the guard in
514                     // this one (whether or not the guard succeeded),
515                     // if the previous pattern failed, then we know
516                     // the guard for that pattern will not have been
517                     // visited. Thus, it is not possible to visit both
518                     // the previous guard and the current one when
519                     // both patterns consist only of constant
520                     // sub-patterns.
521                     //
522                     // However, if the above does not hold, then all
523                     // previous guards need to be wired to visit the
524                     // current guard pattern.
525                     if prev_has_bindings || this_has_bindings {
526                         while let Some(prev) = prev_guards.pop() {
527                             self.add_contained_edge(prev, guard_start);
528                         }
529                     }
530
531                     prev_has_bindings = this_has_bindings;
532
533                     // Push the guard onto the list of previous guards
534                     prev_guards.push(guard_exit);
535
536                     // Update the exit node for the pattern
537                     pat_exit = guard_exit;
538                 }
539
540                 // Add an edge from the exit of this pattern to the
541                 // exit of the arm
542                 self.add_contained_edge(pat_exit, arm_exit);
543             }
544
545             // Visit the body of this arm
546             let body_exit = self.expr(&arm.body, arm_exit);
547
548             // Link the body to the exit of the expression
549             self.add_contained_edge(body_exit, expr_exit);
550         }
551
552         expr_exit
553     }
554
555     fn add_dummy_node(&mut self, preds: &[CFGIndex]) -> CFGIndex {
556         self.add_node(CFGNodeData::Dummy, preds)
557     }
558
559     fn add_ast_node(&mut self, id: ast::NodeId, preds: &[CFGIndex]) -> CFGIndex {
560         assert!(id != ast::DUMMY_NODE_ID);
561         self.add_node(CFGNodeData::AST(id), preds)
562     }
563
564     fn add_unreachable_node(&mut self) -> CFGIndex {
565         self.add_node(CFGNodeData::Unreachable, &[])
566     }
567
568     fn add_node(&mut self, data: CFGNodeData, preds: &[CFGIndex]) -> CFGIndex {
569         let node = self.graph.add_node(data);
570         for &pred in preds {
571             self.add_contained_edge(pred, node);
572         }
573         node
574     }
575
576     fn add_contained_edge(&mut self,
577                           source: CFGIndex,
578                           target: CFGIndex) {
579         let data = CFGEdgeData {exiting_scopes: vec![] };
580         self.graph.add_edge(source, target, data);
581     }
582
583     fn add_exiting_edge(&mut self,
584                         from_expr: &hir::Expr,
585                         from_index: CFGIndex,
586                         scope_id: ast::NodeId,
587                         to_index: CFGIndex) {
588         let mut data = CFGEdgeData { exiting_scopes: vec![] };
589         let mut scope = self.tcx.node_extent(from_expr.id);
590         let target_scope = self.tcx.node_extent(scope_id);
591         let region_maps = self.tcx.region_maps(self.owner_def_id);
592         while scope != target_scope {
593             data.exiting_scopes.push(scope.node_id());
594             scope = region_maps.encl_scope(scope);
595         }
596         self.graph.add_edge(from_index, to_index, data);
597     }
598
599     fn add_returning_edge(&mut self,
600                           _from_expr: &hir::Expr,
601                           from_index: CFGIndex) {
602         let mut data = CFGEdgeData {
603             exiting_scopes: vec![],
604         };
605         for &LoopScope { loop_id: id, .. } in self.loop_scopes.iter().rev() {
606             data.exiting_scopes.push(id);
607         }
608         self.graph.add_edge(from_index, self.fn_exit, data);
609     }
610
611     fn find_scope_edge(&self,
612                   expr: &hir::Expr,
613                   destination: hir::Destination,
614                   scope_cf_kind: ScopeCfKind) -> (ast::NodeId, CFGIndex) {
615
616         match destination.target_id {
617             hir::ScopeTarget::Block(block_expr_id) => {
618                 for b in &self.breakable_block_scopes {
619                     if b.block_expr_id == block_expr_id {
620                         return (block_expr_id, match scope_cf_kind {
621                             ScopeCfKind::Break => b.break_index,
622                             ScopeCfKind::Continue => bug!("can't continue to block"),
623                         });
624                     }
625                 }
626                 span_bug!(expr.span, "no block expr for id {}", block_expr_id);
627             }
628             hir::ScopeTarget::Loop(hir::LoopIdResult::Ok(loop_id)) => {
629                 for l in &self.loop_scopes {
630                     if l.loop_id == loop_id {
631                         return (loop_id, match scope_cf_kind {
632                             ScopeCfKind::Break => l.break_index,
633                             ScopeCfKind::Continue => l.continue_index,
634                         });
635                     }
636                 }
637                 span_bug!(expr.span, "no loop scope for id {}", loop_id);
638             }
639             hir::ScopeTarget::Loop(hir::LoopIdResult::Err(err)) =>
640                 span_bug!(expr.span, "loop scope error: {}",  err),
641         }
642     }
643 }
644
645 #[derive(Copy, Clone, Eq, PartialEq)]
646 enum ScopeCfKind {
647     Break,
648     Continue,
649 }