|
11 | 11 | use middle::cfg::*;
|
12 | 12 | use middle::def;
|
13 | 13 | use middle::graph;
|
| 14 | +use middle::pat_util; |
14 | 15 | use middle::region::CodeExtent;
|
15 | 16 | use middle::ty;
|
16 | 17 | use syntax::ast;
|
@@ -149,23 +150,6 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
|
149 | 150 | pats.fold(pred, |pred, pat| self.pat(&**pat, pred))
|
150 | 151 | }
|
151 | 152 |
|
152 |
| - fn pats_any(&mut self, |
153 |
| - pats: &[P<ast::Pat>], |
154 |
| - pred: CFGIndex) -> CFGIndex { |
155 |
| - //! Handles case where just one of the patterns must match. |
156 |
| -
|
157 |
| - if pats.len() == 1 { |
158 |
| - self.pat(&*pats[0], pred) |
159 |
| - } else { |
160 |
| - let collect = self.add_dummy_node(&[]); |
161 |
| - for pat in pats { |
162 |
| - let pat_exit = self.pat(&**pat, pred); |
163 |
| - self.add_contained_edge(pat_exit, collect); |
164 |
| - } |
165 |
| - collect |
166 |
| - } |
167 |
| - } |
168 |
| - |
169 | 153 | fn expr(&mut self, expr: &ast::Expr, pred: CFGIndex) -> CFGIndex {
|
170 | 154 | match expr.node {
|
171 | 155 | ast::ExprBlock(ref blk) => {
|
@@ -288,45 +272,7 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
|
288 | 272 | }
|
289 | 273 |
|
290 | 274 | ast::ExprMatch(ref discr, ref arms, _) => {
|
291 |
| - // |
292 |
| - // [pred] |
293 |
| - // | |
294 |
| - // v 1 |
295 |
| - // [discr] |
296 |
| - // | |
297 |
| - // v 2 |
298 |
| - // [cond1] |
299 |
| - // / \ |
300 |
| - // | \ |
301 |
| - // v 3 \ |
302 |
| - // [pat1] \ |
303 |
| - // | | |
304 |
| - // v 4 | |
305 |
| - // [guard1] | |
306 |
| - // | | |
307 |
| - // | | |
308 |
| - // v 5 v |
309 |
| - // [body1] [cond2] |
310 |
| - // | / \ |
311 |
| - // | ... ... |
312 |
| - // | | | |
313 |
| - // v 6 v v |
314 |
| - // [.....expr.....] |
315 |
| - // |
316 |
| - let discr_exit = self.expr(&**discr, pred); // 1 |
317 |
| - |
318 |
| - let expr_exit = self.add_ast_node(expr.id, &[]); |
319 |
| - let mut cond_exit = discr_exit; |
320 |
| - for arm in arms { |
321 |
| - cond_exit = self.add_dummy_node(&[cond_exit]); // 2 |
322 |
| - let pats_exit = self.pats_any(&arm.pats, |
323 |
| - cond_exit); // 3 |
324 |
| - let guard_exit = self.opt_expr(&arm.guard, |
325 |
| - pats_exit); // 4 |
326 |
| - let body_exit = self.expr(&*arm.body, guard_exit); // 5 |
327 |
| - self.add_contained_edge(body_exit, expr_exit); // 6 |
328 |
| - } |
329 |
| - expr_exit |
| 275 | + self.match_(expr.id, &discr, &arms, pred) |
330 | 276 | }
|
331 | 277 |
|
332 | 278 | ast::ExprBinary(op, ref l, ref r) if ast_util::lazy_binop(op.node) => {
|
@@ -503,6 +449,108 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
|
503 | 449 | self.add_ast_node(expr.id, &[subexprs_exit])
|
504 | 450 | }
|
505 | 451 |
|
| 452 | + fn match_(&mut self, id: ast::NodeId, discr: &ast::Expr, |
| 453 | + arms: &[ast::Arm], pred: CFGIndex) -> CFGIndex { |
| 454 | + // The CFG for match expression is quite complex, so no ASCII |
| 455 | + // art for it (yet). |
| 456 | + // |
| 457 | + // The CFG generated below matches roughly what trans puts |
| 458 | + // out. Each pattern and guard is visited in parallel, with |
| 459 | + // arms containing multiple patterns generating multiple nodes |
| 460 | + // for the same guard expression. The guard expressions chain |
| 461 | + // into each other from top to bottom, with a specific |
| 462 | + // exception to allow some additional valid programs |
| 463 | + // (explained below). Trans differs slightly in that the |
| 464 | + // pattern matching may continue after a guard but the visible |
| 465 | + // behaviour should be the same. |
| 466 | + // |
| 467 | + // What is going on is explained in further comments. |
| 468 | + |
| 469 | + // Visit the discriminant expression |
| 470 | + let discr_exit = self.expr(discr, pred); |
| 471 | + |
| 472 | + // Add a node for the exit of the match expression as a whole. |
| 473 | + let expr_exit = self.add_ast_node(id, &[]); |
| 474 | + |
| 475 | + // Keep track of the previous guard expressions |
| 476 | + let mut prev_guards = Vec::new(); |
| 477 | + // Track if the previous pattern contained bindings or wildcards |
| 478 | + let mut prev_has_bindings = false; |
| 479 | + |
| 480 | + for arm in arms { |
| 481 | + // Add an exit node for when we've visited all the |
| 482 | + // patterns and the guard (if there is one) in the arm. |
| 483 | + let arm_exit = self.add_dummy_node(&[]); |
| 484 | + |
| 485 | + for pat in &arm.pats { |
| 486 | + // Visit the pattern, coming from the discriminant exit |
| 487 | + let mut pat_exit = self.pat(&**pat, discr_exit); |
| 488 | + |
| 489 | + // If there is a guard expression, handle it here |
| 490 | + if let Some(ref guard) = arm.guard { |
| 491 | + // Add a dummy node for the previous guard |
| 492 | + // expression to target |
| 493 | + let guard_start = self.add_dummy_node(&[pat_exit]); |
| 494 | + // Visit the guard expression |
| 495 | + let guard_exit = self.expr(&**guard, guard_start); |
| 496 | + |
| 497 | + let this_has_bindings = pat_util::pat_contains_bindings_or_wild( |
| 498 | + &self.tcx.def_map, &**pat); |
| 499 | + |
| 500 | + // If both this pattern and the previous pattern |
| 501 | + // were free of bindings, they must consist only |
| 502 | + // of "constant" patterns. Note we cannot match an |
| 503 | + // all-constant pattern, fail the guard, and then |
| 504 | + // match *another* all-constant pattern. This is |
| 505 | + // because if the previous pattern matches, then |
| 506 | + // we *cannot* match this one, unless all the |
| 507 | + // constants are the same (which is rejected by |
| 508 | + // `check_match`). |
| 509 | + // |
| 510 | + // We can use this to be smarter about the flow |
| 511 | + // along guards. If the previous pattern matched, |
| 512 | + // then we know we will not visit the guard in |
| 513 | + // this one (whether or not the guard succeeded), |
| 514 | + // if the previous pattern failed, then we know |
| 515 | + // the guard for that pattern will not have been |
| 516 | + // visited. Thus, it is not possible to visit both |
| 517 | + // the previous guard and the current one when |
| 518 | + // both patterns consist only of constant |
| 519 | + // sub-patterns. |
| 520 | + // |
| 521 | + // However, if the above does not hold, then all |
| 522 | + // previous guards need to be wired to visit the |
| 523 | + // current guard pattern. |
| 524 | + if prev_has_bindings || this_has_bindings { |
| 525 | + while let Some(prev) = prev_guards.pop() { |
| 526 | + self.add_contained_edge(prev, guard_start); |
| 527 | + } |
| 528 | + } |
| 529 | + |
| 530 | + prev_has_bindings = this_has_bindings; |
| 531 | + |
| 532 | + // Push the guard onto the list of previous guards |
| 533 | + prev_guards.push(guard_exit); |
| 534 | + |
| 535 | + // Update the exit node for the pattern |
| 536 | + pat_exit = guard_exit; |
| 537 | + } |
| 538 | + |
| 539 | + // Add an edge from the exit of this pattern to the |
| 540 | + // exit of the arm |
| 541 | + self.add_contained_edge(pat_exit, arm_exit); |
| 542 | + } |
| 543 | + |
| 544 | + // Visit the body of this arm |
| 545 | + let body_exit = self.expr(&arm.body, arm_exit); |
| 546 | + |
| 547 | + // Link the body to the exit of the expression |
| 548 | + self.add_contained_edge(body_exit, expr_exit); |
| 549 | + } |
| 550 | + |
| 551 | + expr_exit |
| 552 | + } |
| 553 | + |
506 | 554 | fn add_dummy_node(&mut self, preds: &[CFGIndex]) -> CFGIndex {
|
507 | 555 | self.add_node(CFGNodeData::Dummy, preds)
|
508 | 556 | }
|
|
0 commit comments