Skip to content

Commit fc04eaa

Browse files
committed
Implement ? in catch expressions and add tests
1 parent 703b246 commit fc04eaa

File tree

16 files changed

+570
-272
lines changed

16 files changed

+570
-272
lines changed

Diff for: src/librustc/cfg/construct.rs

+72-22
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,20 @@ struct CFGBuilder<'a, 'tcx: 'a> {
2222
graph: CFGGraph,
2323
fn_exit: CFGIndex,
2424
loop_scopes: Vec<LoopScope>,
25+
breakable_block_scopes: Vec<BlockScope>,
26+
}
27+
28+
#[derive(Copy, Clone)]
29+
struct BlockScope {
30+
block_expr_id: ast::NodeId, // id of breakable block expr node
31+
break_index: CFGIndex, // where to go on `break`
2532
}
2633

2734
#[derive(Copy, Clone)]
2835
struct LoopScope {
2936
loop_id: ast::NodeId, // id of loop/while node
3037
continue_index: CFGIndex, // where to go on a `loop`
31-
break_index: CFGIndex, // where to go on a `break
38+
break_index: CFGIndex, // where to go on a `break`
3239
}
3340

3441
pub fn construct<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
@@ -53,6 +60,7 @@ pub fn construct<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
5360
graph: graph,
5461
fn_exit: fn_exit,
5562
loop_scopes: Vec::new(),
63+
breakable_block_scopes: Vec::new(),
5664
};
5765
body_exit = cfg_builder.expr(&body.value, entry);
5866
cfg_builder.add_contained_edge(body_exit, fn_exit);
@@ -66,14 +74,34 @@ pub fn construct<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
6674

6775
impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
6876
fn block(&mut self, blk: &hir::Block, pred: CFGIndex) -> CFGIndex {
69-
let mut stmts_exit = pred;
70-
for stmt in &blk.stmts {
71-
stmts_exit = self.stmt(stmt, stmts_exit);
72-
}
77+
if let Some(break_to_expr_id) = blk.break_to_expr_id {
78+
let expr_exit = self.add_ast_node(blk.id, &[]);
79+
80+
self.breakable_block_scopes.push(BlockScope {
81+
block_expr_id: break_to_expr_id,
82+
break_index: expr_exit,
83+
});
84+
85+
let mut stmts_exit = pred;
86+
for stmt in &blk.stmts {
87+
stmts_exit = self.stmt(stmt, stmts_exit);
88+
}
89+
let blk_expr_exit = self.opt_expr(&blk.expr, stmts_exit);
90+
self.add_contained_edge(blk_expr_exit, blk_expr_exit);
91+
92+
self.breakable_block_scopes.pop();
93+
94+
expr_exit
95+
} else {
96+
let mut stmts_exit = pred;
97+
for stmt in &blk.stmts {
98+
stmts_exit = self.stmt(stmt, stmts_exit);
99+
}
73100

74-
let expr_exit = self.opt_expr(&blk.expr, stmts_exit);
101+
let expr_exit = self.opt_expr(&blk.expr, stmts_exit);
75102

76-
self.add_ast_node(blk.id, &[expr_exit])
103+
self.add_ast_node(blk.id, &[expr_exit])
104+
}
77105
}
78106

79107
fn stmt(&mut self, stmt: &hir::Stmt, pred: CFGIndex) -> CFGIndex {
@@ -295,18 +323,18 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
295323

296324
hir::ExprBreak(destination, ref opt_expr) => {
297325
let v = self.opt_expr(opt_expr, pred);
298-
let loop_scope = self.find_scope(expr, destination);
326+
let (scope_id, break_dest) =
327+
self.find_scope_edge(expr, destination, ScopeCfKind::Break);
299328
let b = self.add_ast_node(expr.id, &[v]);
300-
self.add_exiting_edge(expr, b,
301-
loop_scope, loop_scope.break_index);
329+
self.add_exiting_edge(expr, b, scope_id, break_dest);
302330
self.add_unreachable_node()
303331
}
304332

305333
hir::ExprAgain(destination) => {
306-
let loop_scope = self.find_scope(expr, destination);
334+
let (scope_id, cont_dest) =
335+
self.find_scope_edge(expr, destination, ScopeCfKind::Continue);
307336
let a = self.add_ast_node(expr.id, &[pred]);
308-
self.add_exiting_edge(expr, a,
309-
loop_scope, loop_scope.continue_index);
337+
self.add_exiting_edge(expr, a, scope_id, cont_dest);
310338
self.add_unreachable_node()
311339
}
312340

@@ -552,11 +580,11 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
552580
fn add_exiting_edge(&mut self,
553581
from_expr: &hir::Expr,
554582
from_index: CFGIndex,
555-
to_loop: LoopScope,
583+
scope_id: ast::NodeId,
556584
to_index: CFGIndex) {
557585
let mut data = CFGEdgeData { exiting_scopes: vec![] };
558586
let mut scope = self.tcx.region_maps.node_extent(from_expr.id);
559-
let target_scope = self.tcx.region_maps.node_extent(to_loop.loop_id);
587+
let target_scope = self.tcx.region_maps.node_extent(scope_id);
560588
while scope != target_scope {
561589
data.exiting_scopes.push(scope.node_id(&self.tcx.region_maps));
562590
scope = self.tcx.region_maps.encl_scope(scope);
@@ -576,20 +604,42 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
576604
self.graph.add_edge(from_index, self.fn_exit, data);
577605
}
578606

579-
fn find_scope(&self,
607+
fn find_scope_edge(&self,
580608
expr: &hir::Expr,
581-
destination: hir::Destination) -> LoopScope {
582-
583-
match destination.loop_id.into() {
584-
Ok(loop_id) => {
609+
destination: hir::Destination,
610+
scope_cf_kind: ScopeCfKind) -> (ast::NodeId, CFGIndex) {
611+
612+
match destination.target_id {
613+
hir::ScopeTarget::Block(block_expr_id) => {
614+
for b in &self.breakable_block_scopes {
615+
if b.block_expr_id == block_expr_id {
616+
return (block_expr_id, match scope_cf_kind {
617+
ScopeCfKind::Break => b.break_index,
618+
ScopeCfKind::Continue => bug!("can't continue to block"),
619+
});
620+
}
621+
}
622+
span_bug!(expr.span, "no block expr for id {}", block_expr_id);
623+
}
624+
hir::ScopeTarget::Loop(hir::LoopIdResult::Ok(loop_id)) => {
585625
for l in &self.loop_scopes {
586626
if l.loop_id == loop_id {
587-
return *l;
627+
return (loop_id, match scope_cf_kind {
628+
ScopeCfKind::Break => l.break_index,
629+
ScopeCfKind::Continue => l.continue_index,
630+
});
588631
}
589632
}
590633
span_bug!(expr.span, "no loop scope for id {}", loop_id);
591634
}
592-
Err(err) => span_bug!(expr.span, "loop scope error: {}", err),
635+
hir::ScopeTarget::Loop(hir::LoopIdResult::Err(err)) =>
636+
span_bug!(expr.span, "loop scope error: {}", err),
593637
}
594638
}
595639
}
640+
641+
#[derive(Copy, Clone, Eq, PartialEq)]
642+
enum ScopeCfKind {
643+
Break,
644+
Continue,
645+
}

Diff for: src/librustc/hir/intravisit.rs

+12-6
Original file line numberDiff line numberDiff line change
@@ -1008,18 +1008,24 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) {
10081008
}
10091009
ExprBreak(label, ref opt_expr) => {
10101010
label.ident.map(|ident| {
1011-
if let Ok(loop_id) = label.loop_id.into() {
1012-
visitor.visit_def_mention(Def::Label(loop_id));
1013-
}
1011+
match label.target_id {
1012+
ScopeTarget::Block(node_id) |
1013+
ScopeTarget::Loop(LoopIdResult::Ok(node_id)) =>
1014+
visitor.visit_def_mention(Def::Label(node_id)),
1015+
ScopeTarget::Loop(LoopIdResult::Err(_)) => {},
1016+
};
10141017
visitor.visit_name(ident.span, ident.node.name);
10151018
});
10161019
walk_list!(visitor, visit_expr, opt_expr);
10171020
}
10181021
ExprAgain(label) => {
10191022
label.ident.map(|ident| {
1020-
if let Ok(loop_id) = label.loop_id.into() {
1021-
visitor.visit_def_mention(Def::Label(loop_id));
1022-
}
1023+
match label.target_id {
1024+
ScopeTarget::Block(_) => bug!("can't `continue` to a non-loop block"),
1025+
ScopeTarget::Loop(LoopIdResult::Ok(node_id)) =>
1026+
visitor.visit_def_mention(Def::Label(node_id)),
1027+
ScopeTarget::Loop(LoopIdResult::Err(_)) => {},
1028+
};
10231029
visitor.visit_name(ident.span, ident.node.name);
10241030
});
10251031
}

0 commit comments

Comments
 (0)