graph: CFGGraph,
fn_exit: CFGIndex,
loop_scopes: Vec<LoopScope>,
+ breakable_block_scopes: Vec<BlockScope>,
+}
+
+#[derive(Copy, Clone)]
+struct BlockScope {
+ block_expr_id: ast::NodeId, // id of breakable block expr node
+ break_index: CFGIndex, // where to go on `break`
}
#[derive(Copy, Clone)]
struct LoopScope {
loop_id: ast::NodeId, // id of loop/while node
continue_index: CFGIndex, // where to go on a `loop`
- break_index: CFGIndex, // where to go on a `break
+ break_index: CFGIndex, // where to go on a `break`
}
pub fn construct<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
graph: graph,
fn_exit: fn_exit,
loop_scopes: Vec::new(),
+ breakable_block_scopes: Vec::new(),
};
body_exit = cfg_builder.expr(&body.value, entry);
cfg_builder.add_contained_edge(body_exit, fn_exit);
impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
fn block(&mut self, blk: &hir::Block, pred: CFGIndex) -> CFGIndex {
- let mut stmts_exit = pred;
- for stmt in &blk.stmts {
- stmts_exit = self.stmt(stmt, stmts_exit);
- }
+ if let Some(break_to_expr_id) = blk.break_to_expr_id {
+ let expr_exit = self.add_ast_node(blk.id, &[]);
+
+ self.breakable_block_scopes.push(BlockScope {
+ block_expr_id: break_to_expr_id,
+ break_index: expr_exit,
+ });
+
+ let mut stmts_exit = pred;
+ for stmt in &blk.stmts {
+ stmts_exit = self.stmt(stmt, stmts_exit);
+ }
+ let blk_expr_exit = self.opt_expr(&blk.expr, stmts_exit);
+ self.add_contained_edge(blk_expr_exit, blk_expr_exit);
+
+ self.breakable_block_scopes.pop();
+
+ expr_exit
+ } else {
+ let mut stmts_exit = pred;
+ for stmt in &blk.stmts {
+ stmts_exit = self.stmt(stmt, stmts_exit);
+ }
- let expr_exit = self.opt_expr(&blk.expr, stmts_exit);
+ let expr_exit = self.opt_expr(&blk.expr, stmts_exit);
- self.add_ast_node(blk.id, &[expr_exit])
+ self.add_ast_node(blk.id, &[expr_exit])
+ }
}
fn stmt(&mut self, stmt: &hir::Stmt, pred: CFGIndex) -> CFGIndex {
hir::ExprBreak(destination, ref opt_expr) => {
let v = self.opt_expr(opt_expr, pred);
- let loop_scope = self.find_scope(expr, destination);
+ let (scope_id, break_dest) =
+ self.find_scope_edge(expr, destination, ScopeCfKind::Break);
let b = self.add_ast_node(expr.id, &[v]);
- self.add_exiting_edge(expr, b,
- loop_scope, loop_scope.break_index);
+ self.add_exiting_edge(expr, b, scope_id, break_dest);
self.add_unreachable_node()
}
hir::ExprAgain(destination) => {
- let loop_scope = self.find_scope(expr, destination);
+ let (scope_id, cont_dest) =
+ self.find_scope_edge(expr, destination, ScopeCfKind::Continue);
let a = self.add_ast_node(expr.id, &[pred]);
- self.add_exiting_edge(expr, a,
- loop_scope, loop_scope.continue_index);
+ self.add_exiting_edge(expr, a, scope_id, cont_dest);
self.add_unreachable_node()
}
fn add_exiting_edge(&mut self,
from_expr: &hir::Expr,
from_index: CFGIndex,
- to_loop: LoopScope,
+ scope_id: ast::NodeId,
to_index: CFGIndex) {
let mut data = CFGEdgeData { exiting_scopes: vec![] };
let mut scope = self.tcx.region_maps.node_extent(from_expr.id);
- let target_scope = self.tcx.region_maps.node_extent(to_loop.loop_id);
+ let target_scope = self.tcx.region_maps.node_extent(scope_id);
while scope != target_scope {
data.exiting_scopes.push(scope.node_id(&self.tcx.region_maps));
scope = self.tcx.region_maps.encl_scope(scope);
self.graph.add_edge(from_index, self.fn_exit, data);
}
- fn find_scope(&self,
+ fn find_scope_edge(&self,
expr: &hir::Expr,
- destination: hir::Destination) -> LoopScope {
-
- match destination.loop_id.into() {
- Ok(loop_id) => {
+ destination: hir::Destination,
+ scope_cf_kind: ScopeCfKind) -> (ast::NodeId, CFGIndex) {
+
+ match destination.target_id {
+ hir::ScopeTarget::Block(block_expr_id) => {
+ for b in &self.breakable_block_scopes {
+ if b.block_expr_id == block_expr_id {
+ return (block_expr_id, match scope_cf_kind {
+ ScopeCfKind::Break => b.break_index,
+ ScopeCfKind::Continue => bug!("can't continue to block"),
+ });
+ }
+ }
+ span_bug!(expr.span, "no block expr for id {}", block_expr_id);
+ }
+ hir::ScopeTarget::Loop(hir::LoopIdResult::Ok(loop_id)) => {
for l in &self.loop_scopes {
if l.loop_id == loop_id {
- return *l;
+ return (loop_id, match scope_cf_kind {
+ ScopeCfKind::Break => l.break_index,
+ ScopeCfKind::Continue => l.continue_index,
+ });
}
}
span_bug!(expr.span, "no loop scope for id {}", loop_id);
}
- Err(err) => span_bug!(expr.span, "loop scope error: {}", err),
+ hir::ScopeTarget::Loop(hir::LoopIdResult::Err(err)) =>
+ span_bug!(expr.span, "loop scope error: {}", err),
}
}
}
+
+#[derive(Copy, Clone, Eq, PartialEq)]
+enum ScopeCfKind {
+ Break,
+ Continue,
+}
}
ExprBreak(label, ref opt_expr) => {
label.ident.map(|ident| {
- if let Ok(loop_id) = label.loop_id.into() {
- visitor.visit_def_mention(Def::Label(loop_id));
- }
+ match label.target_id {
+ ScopeTarget::Block(node_id) |
+ ScopeTarget::Loop(LoopIdResult::Ok(node_id)) =>
+ visitor.visit_def_mention(Def::Label(node_id)),
+ ScopeTarget::Loop(LoopIdResult::Err(_)) => {},
+ };
visitor.visit_name(ident.span, ident.node.name);
});
walk_list!(visitor, visit_expr, opt_expr);
}
ExprAgain(label) => {
label.ident.map(|ident| {
- if let Ok(loop_id) = label.loop_id.into() {
- visitor.visit_def_mention(Def::Label(loop_id));
- }
+ match label.target_id {
+ ScopeTarget::Block(_) => bug!("can't `continue` to a non-loop block"),
+ ScopeTarget::Loop(LoopIdResult::Ok(node_id)) =>
+ visitor.visit_def_mention(Def::Label(node_id)),
+ ScopeTarget::Loop(LoopIdResult::Err(_)) => {},
+ };
visitor.visit_name(ident.span, ident.node.name);
});
}
o_id.map(|sp_ident| respan(sp_ident.span, sp_ident.node.name))
}
- fn lower_destination(&mut self, destination: Option<(NodeId, Spanned<Ident>)>)
+ fn lower_loop_destination(&mut self, destination: Option<(NodeId, Spanned<Ident>)>)
-> hir::Destination
{
match destination {
- Some((id, label_ident)) => hir::Destination {
- ident: Some(label_ident),
- loop_id: if let Def::Label(loop_id) = self.expect_full_def(id) {
+ Some((id, label_ident)) => {
+ let target = if let Def::Label(loop_id) = self.expect_full_def(id) {
hir::LoopIdResult::Ok(loop_id)
} else {
hir::LoopIdResult::Err(hir::LoopIdError::UnresolvedLabel)
+ };
+ hir::Destination {
+ ident: Some(label_ident),
+ target_id: hir::ScopeTarget::Loop(target),
}
},
None => hir::Destination {
ident: None,
- loop_id: self.loop_scopes.last().map(|innermost_loop_id| Ok(*innermost_loop_id))
- .unwrap_or(Err(hir::LoopIdError::OutsideLoopScope)).into()
+ target_id: hir::ScopeTarget::Loop(
+ self.loop_scopes.last().map(|innermost_loop_id| Ok(*innermost_loop_id))
+ .unwrap_or(Err(hir::LoopIdError::OutsideLoopScope)).into())
}
}
}
bounds.iter().map(|bound| self.lower_ty_param_bound(bound)).collect()
}
- fn lower_block(&mut self, b: &Block) -> P<hir::Block> {
+ fn lower_block(&mut self, b: &Block, break_to: Option<NodeId>) -> P<hir::Block> {
let mut expr = None;
let mut stmts = b.stmts.iter().flat_map(|s| self.lower_stmt(s)).collect::<Vec<_>>();
expr: expr,
rules: self.lower_block_check_mode(&b.rules),
span: b.span,
+ break_to_expr_id: break_to,
})
}
}
ItemKind::Fn(ref decl, unsafety, constness, abi, ref generics, ref body) => {
self.with_new_scopes(|this| {
- let body = this.lower_block(body);
+ let body = this.lower_block(body, None);
let body = this.expr_block(body, ThinVec::new());
let body_id = this.record_body(body, Some(decl));
hir::ItemFn(this.lower_fn_decl(decl),
hir::TraitMethod::Required(names))
}
TraitItemKind::Method(ref sig, Some(ref body)) => {
- let body = this.lower_block(body);
+ let body = this.lower_block(body, None);
let expr = this.expr_block(body, ThinVec::new());
let body_id = this.record_body(expr, Some(&sig.decl));
hir::TraitItemKind::Method(this.lower_method_sig(sig),
hir::ImplItemKind::Const(this.lower_ty(ty), body_id)
}
ImplItemKind::Method(ref sig, ref body) => {
- let body = this.lower_block(body);
+ let body = this.lower_block(body, None);
let expr = this.expr_block(body, ThinVec::new());
let body_id = this.record_body(expr, Some(&sig.decl));
hir::ImplItemKind::Method(this.lower_method_sig(sig), body_id)
id: id,
rules: hir::DefaultBlock,
span: span,
+ break_to_expr_id: None,
});
P(self.expr_block(blk, ThinVec::new()))
}
}
});
- hir::ExprIf(P(self.lower_expr(cond)), self.lower_block(blk), else_opt)
+ hir::ExprIf(P(self.lower_expr(cond)), self.lower_block(blk, None), else_opt)
}
ExprKind::While(ref cond, ref body, opt_ident) => {
self.with_loop_scope(e.id, |this|
hir::ExprWhile(
this.with_loop_condition_scope(|this| P(this.lower_expr(cond))),
- this.lower_block(body),
+ this.lower_block(body, None),
this.lower_opt_sp_ident(opt_ident)))
}
ExprKind::Loop(ref body, opt_ident) => {
self.with_loop_scope(e.id, |this|
- hir::ExprLoop(this.lower_block(body),
+ hir::ExprLoop(this.lower_block(body, None),
this.lower_opt_sp_ident(opt_ident),
hir::LoopSource::Loop))
}
ExprKind::Catch(ref body) => {
- // FIXME(cramertj): Add catch to HIR
- self.with_catch_scope(e.id, |this| hir::ExprBlock(this.lower_block(body)))
+ self.with_catch_scope(e.id, |this|
+ hir::ExprBlock(this.lower_block(body, Some(e.id))))
}
ExprKind::Match(ref expr, ref arms) => {
hir::ExprMatch(P(self.lower_expr(expr)),
})
})
}
- ExprKind::Block(ref blk) => hir::ExprBlock(self.lower_block(blk)),
+ ExprKind::Block(ref blk) => hir::ExprBlock(self.lower_block(blk, None)),
ExprKind::Assign(ref el, ref er) => {
hir::ExprAssign(P(self.lower_expr(el)), P(self.lower_expr(er)))
}
let label_result = if self.is_in_loop_condition && opt_ident.is_none() {
hir::Destination {
ident: opt_ident,
- loop_id: Err(hir::LoopIdError::UnlabeledCfInWhileCondition).into(),
+ target_id: hir::ScopeTarget::Loop(
+ Err(hir::LoopIdError::UnlabeledCfInWhileCondition).into()),
}
} else {
- self.lower_destination(opt_ident.map(|ident| (e.id, ident)))
+ self.lower_loop_destination(opt_ident.map(|ident| (e.id, ident)))
};
hir::ExprBreak(
label_result,
if self.is_in_loop_condition && opt_ident.is_none() {
hir::Destination {
ident: opt_ident,
- loop_id: Err(
- hir::LoopIdError::UnlabeledCfInWhileCondition).into(),
+ target_id: hir::ScopeTarget::Loop(Err(
+ hir::LoopIdError::UnlabeledCfInWhileCondition).into()),
}
} else {
- self.lower_destination(opt_ident.map( |ident| (e.id, ident)))
+ self.lower_loop_destination(opt_ident.map( |ident| (e.id, ident)))
}),
ExprKind::Ret(ref e) => hir::ExprRet(e.as_ref().map(|x| P(self.lower_expr(x)))),
ExprKind::InlineAsm(ref asm) => {
// `<pat> => <body>`
let pat_arm = {
- let body = self.lower_block(body);
+ let body = self.lower_block(body, None);
let body_expr = P(self.expr_block(body, ThinVec::new()));
let pat = self.lower_pat(pat);
self.arm(hir_vec![pat], body_expr)
// Note that the block AND the condition are evaluated in the loop scope.
// This is done to allow `break` from inside the condition of the loop.
let (body, break_expr, sub_expr) = self.with_loop_scope(e.id, |this| (
- this.lower_block(body),
+ this.lower_block(body, None),
this.expr_break(e.span, ThinVec::new()),
this.with_loop_condition_scope(|this| P(this.lower_expr(sub_expr))),
));
// `::std::option::Option::Some(<pat>) => <body>`
let pat_arm = {
- let body_block = self.with_loop_scope(e.id, |this| this.lower_block(body));
+ let body_block = self.with_loop_scope(e.id,
+ |this| this.lower_block(body, None));
let body_expr = P(self.expr_block(body_block, ThinVec::new()));
let pat = self.lower_pat(pat);
let some_pat = self.pat_some(e.span, pat);
// match Carrier::translate(<expr>) {
// Ok(val) => #[allow(unreachable_code)] val,
// Err(err) => #[allow(unreachable_code)]
+ // // If there is an enclosing `catch {...}`
+ // break 'catch_target Carrier::from_error(From::from(err)),
+ // // Otherwise
// return Carrier::from_error(From::from(err)),
// }
- // FIXME(cramertj): implement breaking to catch
- if !self.catch_scopes.is_empty() {
- bug!("`?` in catch scopes is unimplemented")
- }
-
let unstable_span = self.allow_internal_unstable("?", e.span);
// Carrier::translate(<expr>)
P(self.expr_call(e.span, from_err, hir_vec![from_expr]))
};
- let ret_expr = P(self.expr(e.span,
- hir::Expr_::ExprRet(Some(from_err_expr)),
- ThinVec::from(attrs)));
+ let thin_attrs = ThinVec::from(attrs);
+ let catch_scope = self.catch_scopes.last().map(|x| *x);
+ let ret_expr = if let Some(catch_node) = catch_scope {
+ P(self.expr(
+ e.span,
+ hir::ExprBreak(
+ hir::Destination {
+ ident: None,
+ target_id: hir::ScopeTarget::Block(catch_node),
+ },
+ Some(from_err_expr)
+ ),
+ thin_attrs))
+ } else {
+ P(self.expr(e.span,
+ hir::Expr_::ExprRet(Some(from_err_expr)),
+ thin_attrs))
+ };
let err_pat = self.pat_err(e.span, err_local);
self.arm(hir_vec![err_pat], ret_expr)
}
fn expr_break(&mut self, span: Span, attrs: ThinVec<Attribute>) -> P<hir::Expr> {
- let expr_break = hir::ExprBreak(self.lower_destination(None), None);
+ let expr_break = hir::ExprBreak(self.lower_loop_destination(None), None);
P(self.expr(span, expr_break, attrs))
}
id: self.next_id(),
rules: hir::DefaultBlock,
span: span,
+ break_to_expr_id: None,
}
}
id: id,
stmts: stmts,
expr: Some(expr),
+ break_to_expr_id: None,
});
self.expr_block(block, attrs)
}
/// Distinguishes between `unsafe { ... }` and `{ ... }`
pub rules: BlockCheckMode,
pub span: Span,
+ /// The id of the expression that `break` breaks to if the block can be broken out of.
+ /// Currently only `Some(_)` for `catch {}` blocks
+ pub break_to_expr_id: Option<NodeId>,
}
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash)]
}
}
+#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
+pub enum ScopeTarget {
+ Block(NodeId),
+ Loop(LoopIdResult),
+}
+
+impl ScopeTarget {
+ pub fn opt_id(self) -> Option<NodeId> {
+ match self {
+ ScopeTarget::Block(node_id) |
+ ScopeTarget::Loop(LoopIdResult::Ok(node_id)) => Some(node_id),
+ ScopeTarget::Loop(LoopIdResult::Err(_)) => None,
+ }
+ }
+}
+
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
pub struct Destination {
// This is `Some(_)` iff there is an explicit user-specified `label
// These errors are caught and then reported during the diagnostics pass in
// librustc_passes/loops.rs
- pub loop_id: LoopIdResult,
+ pub target_id: ScopeTarget,
}
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
s: Specials,
successors: Vec<LiveNode>,
users: Vec<Users>,
- // The list of node IDs for the nested loop scopes
- // we're in.
- loop_scope: Vec<NodeId>,
+
// mappings from loop node ID to LiveNode
// ("break" label should map to loop node ID,
// it probably doesn't now)
break_ln: NodeMap<LiveNode>,
- cont_ln: NodeMap<LiveNode>
+ cont_ln: NodeMap<LiveNode>,
+
+ // mappings from node ID to LiveNode for "breakable" blocks-- currently only `catch {...}`
+ breakable_block_ln: NodeMap<LiveNode>,
}
impl<'a, 'tcx> Liveness<'a, 'tcx> {
s: specials,
successors: vec![invalid_node(); num_live_nodes],
users: vec![invalid_users(); num_live_nodes * num_vars],
- loop_scope: Vec::new(),
break_ln: NodeMap(),
cont_ln: NodeMap(),
+ breakable_block_ln: NodeMap(),
}
}
debug!("compute: using id for body, {}", self.ir.tcx.hir.node_to_pretty_string(body.id));
let exit_ln = self.s.exit_ln;
- let entry_ln: LiveNode = self.with_loop_nodes(body.id, exit_ln, exit_ln, |this| {
- // the fallthrough exit is only for those cases where we do not
- // explicitly return:
- let s = this.s;
- this.init_from_succ(s.fallthrough_ln, s.exit_ln);
- this.acc(s.fallthrough_ln, s.clean_exit_var, ACC_READ);
-
- this.propagate_through_expr(body, s.fallthrough_ln)
- });
+
+ self.break_ln.insert(body.id, exit_ln);
+ self.cont_ln.insert(body.id, exit_ln);
+
+ // the fallthrough exit is only for those cases where we do not
+ // explicitly return:
+ let s = self.s;
+ self.init_from_succ(s.fallthrough_ln, s.exit_ln);
+ self.acc(s.fallthrough_ln, s.clean_exit_var, ACC_READ);
+
+ let entry_ln = self.propagate_through_expr(body, s.fallthrough_ln);
// hack to skip the loop unless debug! is enabled:
debug!("^^ liveness computation results for body {} (entry={:?})",
fn propagate_through_block(&mut self, blk: &hir::Block, succ: LiveNode)
-> LiveNode {
+ if let Some(break_to_expr_id) = blk.break_to_expr_id {
+ self.breakable_block_ln.insert(break_to_expr_id, succ);
+ }
let succ = self.propagate_through_opt_expr(blk.expr.as_ref().map(|e| &**e), succ);
blk.stmts.iter().rev().fold(succ, |succ, stmt| {
self.propagate_through_stmt(stmt, succ)
}
hir::ExprClosure(.., blk_id, _) => {
- debug!("{} is an ExprClosure",
- self.ir.tcx.hir.node_to_pretty_string(expr.id));
+ debug!("{} is an ExprClosure", self.ir.tcx.hir.node_to_pretty_string(expr.id));
/*
The next-node for a break is the successor of the entire
loop. The next-node for a continue is the top of this loop.
*/
let node = self.live_node(expr.id, expr.span);
- self.with_loop_nodes(blk_id.node_id, succ, node, |this| {
-
- // the construction of a closure itself is not important,
- // but we have to consider the closed over variables.
- let caps = match this.ir.capture_info_map.get(&expr.id) {
- Some(caps) => caps.clone(),
- None => {
- span_bug!(expr.span, "no registered caps");
- }
- };
- caps.iter().rev().fold(succ, |succ, cap| {
- this.init_from_succ(cap.ln, succ);
- let var = this.variable(cap.var_nid, expr.span);
- this.acc(cap.ln, var, ACC_READ | ACC_USE);
- cap.ln
- })
+
+ let break_ln = succ;
+ let cont_ln = node;
+ self.break_ln.insert(blk_id.node_id, break_ln);
+ self.cont_ln.insert(blk_id.node_id, cont_ln);
+
+ // the construction of a closure itself is not important,
+ // but we have to consider the closed over variables.
+ let caps = match self.ir.capture_info_map.get(&expr.id) {
+ Some(caps) => caps.clone(),
+ None => {
+ span_bug!(expr.span, "no registered caps");
+ }
+ };
+ caps.iter().rev().fold(succ, |succ, cap| {
+ self.init_from_succ(cap.ln, succ);
+ let var = self.variable(cap.var_nid, expr.span);
+ self.acc(cap.ln, var, ACC_READ | ACC_USE);
+ cap.ln
})
}
hir::ExprBreak(label, ref opt_expr) => {
// Find which label this break jumps to
- let sc = match label.loop_id.into() {
- Ok(loop_id) => loop_id,
- Err(err) => span_bug!(expr.span, "loop scope error: {}", err),
- };
+ let target = match label.target_id {
+ hir::ScopeTarget::Block(node_id) =>
+ self.breakable_block_ln.get(&node_id),
+ hir::ScopeTarget::Loop(hir::LoopIdResult::Ok(node_id)) =>
+ self.break_ln.get(&node_id),
+ hir::ScopeTarget::Loop(hir::LoopIdResult::Err(err)) =>
+ span_bug!(expr.span, "loop scope error: {}", err),
+ }.map(|x| *x);
// Now that we know the label we're going to,
// look it up in the break loop nodes table
- match self.break_ln.get(&sc) {
- Some(&b) => self.propagate_through_opt_expr(opt_expr.as_ref().map(|e| &**e), b),
+ match target {
+ Some(b) => self.propagate_through_opt_expr(opt_expr.as_ref().map(|e| &**e), b),
None => span_bug!(expr.span, "break to unknown label")
}
}
hir::ExprAgain(label) => {
// Find which label this expr continues to
- let sc = match label.loop_id.into() {
- Ok(loop_id) => loop_id,
- Err(err) => span_bug!(expr.span, "loop scope error: {}", err),
+ let sc = match label.target_id {
+ hir::ScopeTarget::Block(_) => bug!("can't `continue` to a non-loop block"),
+ hir::ScopeTarget::Loop(hir::LoopIdResult::Ok(node_id)) => node_id,
+ hir::ScopeTarget::Loop(hir::LoopIdResult::Err(err)) =>
+ span_bug!(expr.span, "loop scope error: {}", err),
};
-
// Now that we know the label we're going to,
// look it up in the continue loop nodes table
debug!("propagate_through_loop: using id for loop body {} {}",
expr.id, self.ir.tcx.hir.node_to_pretty_string(body.id));
- let (cond_ln, body_ln) = self.with_loop_nodes(expr.id, succ, ln, |this| {
- let cond_ln = match kind {
- LoopLoop => ln,
- WhileLoop(ref cond) => this.propagate_through_expr(&cond, ln),
- };
- let body_ln = this.propagate_through_block(body, cond_ln);
- (cond_ln, body_ln)
- });
+ let break_ln = succ;
+ let cont_ln = ln;
+ self.break_ln.insert(expr.id, break_ln);
+ self.cont_ln.insert(expr.id, cont_ln);
+
+ let cond_ln = match kind {
+ LoopLoop => ln,
+ WhileLoop(ref cond) => self.propagate_through_expr(&cond, ln),
+ };
+ let body_ln = self.propagate_through_block(body, cond_ln);
// repeat until fixed point is reached:
while self.merge_from_succ(ln, body_ln, first_merge) {
}
};
assert!(cond_ln == new_cond_ln);
- assert!(body_ln == self.with_loop_nodes(expr.id, succ, ln,
- |this| this.propagate_through_block(body, cond_ln)));
+ assert!(body_ln == self.propagate_through_block(body, cond_ln));
}
cond_ln
}
-
- fn with_loop_nodes<R, F>(&mut self,
- loop_node_id: NodeId,
- break_ln: LiveNode,
- cont_ln: LiveNode,
- f: F)
- -> R where
- F: FnOnce(&mut Liveness<'a, 'tcx>) -> R,
- {
- debug!("with_loop_nodes: {} {}", loop_node_id, break_ln.get());
- self.loop_scope.push(loop_node_id);
- self.break_ln.insert(loop_node_id, break_ln);
- self.cont_ln.insert(loop_node_id, cont_ln);
- let r = f(self);
- self.loop_scope.pop();
- r
- }
}
// _______________________________________________________________________
this.in_scope(extent, block, |this| this.into(destination, block, value))
}
ExprKind::Block { body: ast_block } => {
- this.ast_block(destination, block, ast_block)
+ if let Some(_) = ast_block.break_to_expr_id {
+ // This is a `break`-able block (currently only `catch { ... }`)
+ let exit_block = this.cfg.start_new_block();
+ let block_exit = this.in_breakable_scope(None, exit_block,
+ destination.clone(), |this| {
+ this.ast_block(destination, block, ast_block)
+ });
+ this.cfg.terminate(unpack!(block_exit), source_info,
+ TerminatorKind::Goto { target: exit_block });
+ exit_block.unit()
+ } else {
+ this.ast_block(destination, block, ast_block)
+ }
}
ExprKind::Match { discriminant, arms } => {
this.match_expr(destination, expr_span, block, discriminant, arms)
this.cfg.terminate(block, source_info,
TerminatorKind::Goto { target: loop_block });
- this.in_loop_scope(
- loop_block, exit_block, destination.clone(),
+ this.in_breakable_scope(
+ Some(loop_block), exit_block, destination.clone(),
move |this| {
// conduct the test, if necessary
let body_block;
// except according to those terms.
use build::{BlockAnd, BlockAndExtension, Builder};
-use build::scope::LoopScope;
+use build::scope::BreakableScope;
use hair::*;
use rustc::mir::*;
block.unit()
}
ExprKind::Continue { label } => {
- let LoopScope { continue_block, extent, .. } =
- *this.find_loop_scope(expr_span, label);
+ let BreakableScope { continue_block, extent, .. } =
+ *this.find_breakable_scope(expr_span, label);
+ let continue_block = continue_block.expect(
+ "Attempted to continue in non-continuable breakable block");
this.exit_scope(expr_span, extent, block, continue_block);
this.cfg.start_new_block().unit()
}
ExprKind::Break { label, value } => {
let (break_block, extent, destination) = {
- let LoopScope {
+ let BreakableScope {
break_block,
extent,
ref break_destination,
..
- } = *this.find_loop_scope(expr_span, label);
+ } = *this.find_breakable_scope(expr_span, label);
(break_block, extent, break_destination.clone())
};
if let Some(value) = value {
/// see the `scope` module for more details
scopes: Vec<scope::Scope<'tcx>>,
- /// the current set of loops; see the `scope` module for more
+ /// the current set of breakables; see the `scope` module for more
/// details
- loop_scopes: Vec<scope::LoopScope<'tcx>>,
+ breakable_scopes: Vec<scope::BreakableScope<'tcx>>,
/// the vector of all scopes that we have created thus far;
/// we track this for debuginfo later
scopes: vec![],
visibility_scopes: IndexVec::new(),
visibility_scope: ARGUMENT_VISIBILITY_SCOPE,
- loop_scopes: vec![],
+ breakable_scopes: vec![],
local_decls: IndexVec::from_elem_n(LocalDecl::new_return_pointer(return_ty), 1),
var_indices: NodeMap(),
unit_temp: None,
}
#[derive(Clone, Debug)]
-pub struct LoopScope<'tcx> {
+pub struct BreakableScope<'tcx> {
/// Extent of the loop
pub extent: CodeExtent,
- /// Where the body of the loop begins
- pub continue_block: BasicBlock,
- /// Block to branch into when the loop terminates (either by being `break`-en out from, or by
- /// having its condition to become false)
+ /// Where the body of the loop begins. `None` if block
+ pub continue_block: Option<BasicBlock>,
+ /// Block to branch into when the loop or block terminates (either by being `break`-en out
+ /// from, or by having its condition to become false)
pub break_block: BasicBlock,
- /// The destination of the loop expression itself (i.e. where to put the result of a `break`
- /// expression)
+ /// The destination of the loop/block expression itself (i.e. where to put the result of a
+ /// `break` expression)
pub break_destination: Lvalue<'tcx>,
}
impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
// Adding and removing scopes
// ==========================
- /// Start a loop scope, which tracks where `continue` and `break`
+ /// Start a breakable scope, which tracks where `continue` and `break`
/// should branch to. See module comment for more details.
///
- /// Returns the might_break attribute of the LoopScope used.
- pub fn in_loop_scope<F>(&mut self,
- loop_block: BasicBlock,
+ /// Returns the might_break attribute of the BreakableScope used.
+ pub fn in_breakable_scope<F, R>(&mut self,
+ loop_block: Option<BasicBlock>,
break_block: BasicBlock,
break_destination: Lvalue<'tcx>,
- f: F)
- where F: FnOnce(&mut Builder<'a, 'gcx, 'tcx>)
+ f: F) -> R
+ where F: FnOnce(&mut Builder<'a, 'gcx, 'tcx>) -> R
{
let extent = self.topmost_scope();
- let loop_scope = LoopScope {
+ let scope = BreakableScope {
extent: extent,
continue_block: loop_block,
break_block: break_block,
break_destination: break_destination,
};
- self.loop_scopes.push(loop_scope);
- f(self);
- let loop_scope = self.loop_scopes.pop().unwrap();
- assert!(loop_scope.extent == extent);
+ self.breakable_scopes.push(scope);
+ let res = f(self);
+ let breakable_scope = self.breakable_scopes.pop().unwrap();
+ assert!(breakable_scope.extent == extent);
+ res
}
/// Convenience wrapper that pushes a scope and then executes `f`
// Finding scopes
// ==============
- /// Finds the loop scope for a given label. This is used for
+ /// Finds the breakable scope for a given label. This is used for
/// resolving `break` and `continue`.
- pub fn find_loop_scope(&mut self,
+ pub fn find_breakable_scope(&mut self,
span: Span,
label: CodeExtent)
- -> &mut LoopScope<'tcx> {
+ -> &mut BreakableScope<'tcx> {
// find the loop-scope with the correct id
- self.loop_scopes.iter_mut()
+ self.breakable_scopes.iter_mut()
.rev()
- .filter(|loop_scope| loop_scope.extent == label)
+ .filter(|breakable_scope| breakable_scope.extent == label)
.next()
- .unwrap_or_else(|| span_bug!(span, "no enclosing loop scope found?"))
+ .unwrap_or_else(|| span_bug!(span, "no enclosing breakable scope found"))
}
/// Given a span and the current visibility scope, make a SourceInfo.
}
}
hir::ExprRet(ref v) => ExprKind::Return { value: v.to_ref() },
- hir::ExprBreak(label, ref value) => {
- match label.loop_id.into() {
- Ok(loop_id) => ExprKind::Break {
- label: cx.tcx.region_maps.node_extent(loop_id),
+ hir::ExprBreak(dest, ref value) => {
+ match dest.target_id {
+ hir::ScopeTarget::Block(target_id) |
+ hir::ScopeTarget::Loop(hir::LoopIdResult::Ok(target_id)) => ExprKind::Break {
+ label: cx.tcx.region_maps.node_extent(target_id),
value: value.to_ref(),
},
- Err(err) => bug!("invalid loop id for break: {}", err)
+ hir::ScopeTarget::Loop(hir::LoopIdResult::Err(err)) =>
+ bug!("invalid loop id for break: {}", err)
}
-
}
- hir::ExprAgain(label) => {
- match label.loop_id.into() {
- Ok(loop_id) => ExprKind::Continue {
+ hir::ExprAgain(dest) => {
+ match dest.target_id {
+ hir::ScopeTarget::Block(_) => bug!("cannot continue to blocks"),
+ hir::ScopeTarget::Loop(hir::LoopIdResult::Ok(loop_id)) => ExprKind::Continue {
label: cx.tcx.region_maps.node_extent(loop_id),
},
- Err(err) => bug!("invalid loop id for continue: {}", err)
+ hir::ScopeTarget::Loop(hir::LoopIdResult::Err(err)) =>
+ bug!("invalid loop id for continue: {}", err)
}
}
hir::ExprMatch(ref discr, ref arms, _) => {
self.with_context(Closure, |v| v.visit_nested_body(b));
}
hir::ExprBreak(label, ref opt_expr) => {
- let loop_id = match label.loop_id.into() {
- Ok(loop_id) => loop_id,
- Err(hir::LoopIdError::OutsideLoopScope) => ast::DUMMY_NODE_ID,
- Err(hir::LoopIdError::UnlabeledCfInWhileCondition) => {
- self.emit_unlabled_cf_in_while_condition(e.span, "break");
- ast::DUMMY_NODE_ID
- },
- Err(hir::LoopIdError::UnresolvedLabel) => ast::DUMMY_NODE_ID,
+ let loop_id = match label.target_id {
+ hir::ScopeTarget::Block(_) => return,
+ hir::ScopeTarget::Loop(loop_res) => {
+ match loop_res.into() {
+ Ok(loop_id) => loop_id,
+ Err(hir::LoopIdError::OutsideLoopScope) => ast::DUMMY_NODE_ID,
+ Err(hir::LoopIdError::UnlabeledCfInWhileCondition) => {
+ self.emit_unlabled_cf_in_while_condition(e.span, "break");
+ ast::DUMMY_NODE_ID
+ },
+ Err(hir::LoopIdError::UnresolvedLabel) => ast::DUMMY_NODE_ID,
+ }
+ }
};
if opt_expr.is_some() {
self.require_loop("break", e.span);
}
hir::ExprAgain(label) => {
- if let Err(hir::LoopIdError::UnlabeledCfInWhileCondition) = label.loop_id.into() {
+ if let hir::ScopeTarget::Loop(
+ hir::LoopIdResult::Err(
+ hir::LoopIdError::UnlabeledCfInWhileCondition)) = label.target_id {
self.emit_unlabled_cf_in_while_condition(e.span, "continue");
}
self.require_loop("continue", e.span)
}
#[derive(Clone)]
-pub struct LoopCtxt<'gcx, 'tcx> {
+pub struct BreakableCtxt<'gcx, 'tcx> {
unified: Ty<'tcx>,
coerce_to: Ty<'tcx>,
break_exprs: Vec<&'gcx hir::Expr>,
}
#[derive(Clone)]
-pub struct EnclosingLoops<'gcx, 'tcx> {
- stack: Vec<LoopCtxt<'gcx, 'tcx>>,
+pub struct EnclosingBreakables<'gcx, 'tcx> {
+ stack: Vec<BreakableCtxt<'gcx, 'tcx>>,
by_id: NodeMap<usize>,
}
-impl<'gcx, 'tcx> EnclosingLoops<'gcx, 'tcx> {
- fn find_loop(&mut self, id: hir::LoopIdResult) -> Option<&mut LoopCtxt<'gcx, 'tcx>> {
- let id_res: Result<_,_> = id.into();
- if let Some(ix) = id_res.ok().and_then(|id| self.by_id.get(&id).cloned()) {
+impl<'gcx, 'tcx> EnclosingBreakables<'gcx, 'tcx> {
+ fn find_breakable(&mut self, target: hir::ScopeTarget)
+ -> Option<&mut BreakableCtxt<'gcx, 'tcx>>
+ {
+ let opt_index = target.opt_id().and_then(|id| self.by_id.get(&id).cloned());
+ if let Some(ix) = opt_index {
Some(&mut self.stack[ix])
} else {
None
/// Whether any child nodes have any type errors.
has_errors: Cell<bool>,
- enclosing_loops: RefCell<EnclosingLoops<'gcx, 'tcx>>,
+ enclosing_breakables: RefCell<EnclosingBreakables<'gcx, 'tcx>>,
inh: &'a Inherited<'a, 'gcx, 'tcx>,
}
ast::CRATE_NODE_ID)),
diverges: Cell::new(Diverges::Maybe),
has_errors: Cell::new(false),
- enclosing_loops: RefCell::new(EnclosingLoops {
+ enclosing_breakables: RefCell::new(EnclosingBreakables {
stack: Vec::new(),
by_id: NodeMap(),
}),
}
tcx.mk_nil()
}
- hir::ExprBreak(label, ref expr_opt) => {
+ hir::ExprBreak(destination, ref expr_opt) => {
let coerce_to = {
- let mut enclosing_loops = self.enclosing_loops.borrow_mut();
- enclosing_loops.find_loop(label.loop_id).map(|ctxt| ctxt.coerce_to)
+ let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
+ enclosing_breakables
+ .find_breakable(destination.target_id).map(|ctxt| ctxt.coerce_to)
};
if let Some(coerce_to) = coerce_to {
let e_ty;
cause = self.misc(expr.span);
}
- let mut enclosing_loops = self.enclosing_loops.borrow_mut();
- let ctxt = enclosing_loops.find_loop(label.loop_id).unwrap();
+ let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
+ let ctxt = enclosing_breakables.find_breakable(destination.target_id).unwrap();
let result = if let Some(ref e) = *expr_opt {
// Special-case the first element, as it has no "previous expressions".
ctxt.may_break = true;
}
- // Otherwise, we failed to find the enclosing loop; this can only happen if the
- // `break` was not inside a loop at all, which is caught by the loop-checking pass.
+ // Otherwise, we failed to find the enclosing breakable; this can only happen if the
+ // `break` target was not found, which is caught in HIR lowering and reported by the
+ // loop-checking pass.
tcx.types.never
}
hir::ExprAgain(_) => { tcx.types.never }
hir::ExprWhile(ref cond, ref body, _) => {
let unified = self.tcx.mk_nil();
let coerce_to = unified;
- let ctxt = LoopCtxt {
+ let ctxt = BreakableCtxt {
unified: unified,
coerce_to: coerce_to,
break_exprs: vec![],
may_break: true,
};
- self.with_loop_ctxt(expr.id, ctxt, || {
+ self.with_breakable_ctxt(expr.id, ctxt, || {
self.check_expr_has_type(&cond, tcx.types.bool);
let cond_diverging = self.diverges.get();
self.check_block_no_value(&body);
hir::ExprLoop(ref body, _, _) => {
let unified = self.next_ty_var(TypeVariableOrigin::TypeInference(body.span));
let coerce_to = expected.only_has_type(self).unwrap_or(unified);
- let ctxt = LoopCtxt {
+ let ctxt = BreakableCtxt {
unified: unified,
coerce_to: coerce_to,
break_exprs: vec![],
may_break: false,
};
- let ctxt = self.with_loop_ctxt(expr.id, ctxt, || {
+ let (ctxt, ()) = self.with_breakable_ctxt(expr.id, ctxt, || {
self.check_block_no_value(&body);
});
if ctxt.may_break {
hir::ExprClosure(capture, ref decl, body_id, _) => {
self.check_expr_closure(expr, capture, &decl, body_id, expected)
}
- hir::ExprBlock(ref b) => {
- self.check_block_with_expected(&b, expected)
+ hir::ExprBlock(ref body) => {
+ self.check_block_with_expected(&body, expected)
}
hir::ExprCall(ref callee, ref args) => {
self.check_call(expr, &callee, args, expected)
replace(&mut *fcx_ps, unsafety_state)
};
- for s in &blk.stmts {
- self.check_stmt(s);
- }
+ let mut ty = if let Some(break_to_expr_id) = blk.break_to_expr_id {
+ let unified = self.next_ty_var(TypeVariableOrigin::TypeInference(blk.span));
+ let coerce_to = expected.only_has_type(self).unwrap_or(unified);
+ let ctxt = BreakableCtxt {
+ unified: unified,
+ coerce_to: coerce_to,
+ break_exprs: vec![],
+ may_break: false,
+ };
- let mut ty = match blk.expr {
- Some(ref e) => self.check_expr_with_expectation(e, expected),
- None => self.tcx.mk_nil()
- };
+ let (mut ctxt, (e_ty, cause)) = self.with_breakable_ctxt(break_to_expr_id, ctxt, || {
+ for s in &blk.stmts {
+ self.check_stmt(s);
+ }
+ let coerce_to = {
+ let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
+ enclosing_breakables.find_breakable(
+ hir::ScopeTarget::Block(break_to_expr_id)
+ ).unwrap().coerce_to
+ };
+ let e_ty;
+ let cause;
+ match blk.expr {
+ Some(ref e) => {
+ e_ty = self.check_expr_with_hint(e, coerce_to);
+ cause = self.misc(e.span);
+ },
+ None => {
+ e_ty = self.tcx.mk_nil();
+ cause = self.misc(blk.span);
+ }
+ };
+
+ (e_ty, cause)
+ });
- if self.diverges.get().always() {
- ty = self.next_diverging_ty_var(TypeVariableOrigin::DivergingBlockExpr(blk.span));
- } else if let ExpectHasType(ety) = expected {
if let Some(ref e) = blk.expr {
- // Coerce the tail expression to the right type.
- self.demand_coerce(e, ty, ety);
+ let result = if !ctxt.may_break {
+ self.try_coerce(e, e_ty, ctxt.coerce_to)
+ } else {
+ self.try_find_coercion_lub(&cause, || ctxt.break_exprs.iter().cloned(),
+ ctxt.unified, e, e_ty)
+ };
+ match result {
+ Ok(ty) => ctxt.unified = ty,
+ Err(err) =>
+ self.report_mismatched_types(&cause, ctxt.unified, e_ty, err).emit(),
+ }
} else {
- // We're not diverging and there's an expected type, which,
- // in case it's not `()`, could result in an error higher-up.
- // We have a chance to error here early and be more helpful.
- let cause = self.misc(blk.span);
- let trace = TypeTrace::types(&cause, false, ty, ety);
- match self.sub_types(false, &cause, ty, ety) {
- Ok(InferOk { obligations, .. }) => {
- // FIXME(#32730) propagate obligations
- assert!(obligations.is_empty());
- },
- Err(err) => {
- let mut err = self.report_and_explain_type_error(trace, &err);
-
- // Be helpful when the user wrote `{... expr;}` and
- // taking the `;` off is enough to fix the error.
- let mut extra_semi = None;
- if let Some(stmt) = blk.stmts.last() {
- if let hir::StmtSemi(ref e, _) = stmt.node {
- if self.can_sub_types(self.node_ty(e.id), ety).is_ok() {
- extra_semi = Some(stmt);
- }
- }
- }
- if let Some(last_stmt) = extra_semi {
- let original_span = original_sp(self.tcx.sess.codemap(),
- last_stmt.span, blk.span);
- let span_semi = Span {
- lo: original_span.hi - BytePos(1),
- hi: original_span.hi,
- expn_id: original_span.expn_id
- };
- err.span_help(span_semi, "consider removing this semicolon:");
- }
+ self.check_block_no_expr(blk, self.tcx.mk_nil(), e_ty);
+ };
- err.emit();
- }
- }
+ ctxt.unified
+ } else {
+ for s in &blk.stmts {
+ self.check_stmt(s);
}
- // We already applied the type (and potentially errored),
- // use the expected type to avoid further errors out.
- ty = ety;
- }
+ let mut ty = match blk.expr {
+ Some(ref e) => self.check_expr_with_expectation(e, expected),
+ None => self.tcx.mk_nil()
+ };
+
+ if self.diverges.get().always() {
+ ty = self.next_diverging_ty_var(TypeVariableOrigin::DivergingBlockExpr(blk.span));
+ } else if let ExpectHasType(ety) = expected {
+ if let Some(ref e) = blk.expr {
+ // Coerce the tail expression to the right type.
+ self.demand_coerce(e, ty, ety);
+ } else {
+ self.check_block_no_expr(blk, ty, ety);
+ }
+
+ // We already applied the type (and potentially errored),
+ // use the expected type to avoid further errors out.
+ ty = ety;
+ }
+ ty
+ };
if self.has_errors.get() || ty.references_error() {
ty = self.tcx.types.err
ty
}
+ pub fn check_block_no_expr(&self, blk: &'gcx hir::Block, ty: Ty<'tcx>, ety: Ty<'tcx>) {
+ // We're not diverging and there's an expected type, which,
+ // in case it's not `()`, could result in an error higher-up.
+ // We have a chance to error here early and be more helpful.
+ let cause = self.misc(blk.span);
+ let trace = TypeTrace::types(&cause, false, ty, ety);
+ match self.sub_types(false, &cause, ty, ety) {
+ Ok(InferOk { obligations, .. }) => {
+ // FIXME(#32730) propagate obligations
+ assert!(obligations.is_empty());
+ },
+ Err(err) => {
+ let mut err = self.report_and_explain_type_error(trace, &err);
+
+ // Be helpful when the user wrote `{... expr;}` and
+ // taking the `;` off is enough to fix the error.
+ let mut extra_semi = None;
+ if let Some(stmt) = blk.stmts.last() {
+ if let hir::StmtSemi(ref e, _) = stmt.node {
+ if self.can_sub_types(self.node_ty(e.id), ety).is_ok() {
+ extra_semi = Some(stmt);
+ }
+ }
+ }
+ if let Some(last_stmt) = extra_semi {
+ let original_span = original_sp(self.tcx.sess.codemap(),
+ last_stmt.span, blk.span);
+ let span_semi = Span {
+ lo: original_span.hi - BytePos(1),
+ hi: original_span.hi,
+ expn_id: original_span.expn_id
+ };
+ err.span_help(span_semi, "consider removing this semicolon:");
+ }
+
+ err.emit();
+ }
+ }
+ }
+
// Instantiates the given path, which must refer to an item with the given
// number of type parameters and type.
pub fn instantiate_value_path(&self,
})
}
- fn with_loop_ctxt<F: FnOnce()>(&self, id: ast::NodeId, ctxt: LoopCtxt<'gcx, 'tcx>, f: F)
- -> LoopCtxt<'gcx, 'tcx> {
+ fn with_breakable_ctxt<F: FnOnce() -> R, R>(&self, id: ast::NodeId,
+ ctxt: BreakableCtxt<'gcx, 'tcx>, f: F)
+ -> (BreakableCtxt<'gcx, 'tcx>, R) {
let index;
{
- let mut enclosing_loops = self.enclosing_loops.borrow_mut();
- index = enclosing_loops.stack.len();
- enclosing_loops.by_id.insert(id, index);
- enclosing_loops.stack.push(ctxt);
- }
- f();
- {
- let mut enclosing_loops = self.enclosing_loops.borrow_mut();
- debug_assert!(enclosing_loops.stack.len() == index + 1);
- enclosing_loops.by_id.remove(&id).expect("missing loop context");
- (enclosing_loops.stack.pop().expect("missing loop context"))
+ let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
+ index = enclosing_breakables.stack.len();
+ enclosing_breakables.by_id.insert(id, index);
+ enclosing_breakables.stack.push(ctxt);
}
+ let result = f();
+ let ctxt = {
+ let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
+ debug_assert!(enclosing_breakables.stack.len() == index + 1);
+ enclosing_breakables.by_id.remove(&id).expect("missing breakable context");
+ enclosing_breakables.stack.pop().expect("missing breakable context")
+ };
+ (ctxt, result)
}
}
--- /dev/null
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(catch_expr)]
+
+pub fn main() {
+ let _: Result<(), &str> = do catch {
+ let my_string = String::from("");
+ let my_str: &str = &my_string;
+ Err(my_str)?;
+ Err("")?;
+ Ok(())
+ }; //~ ERROR `my_string` does not live long enough
+
+ let mut i = 5;
+ let k = &mut i;
+ let mut j: Result<(), &mut i32> = do catch {
+ Err(k)?;
+ i = 10; //~ ERROR cannot assign to `i` because it is borrowed
+ Ok(())
+ };
+ ::std::mem::drop(k); //~ ERROR use of moved value: `k`
+ i = 40; //~ ERROR cannot assign to `i` because it is borrowed
+
+ let i_ptr = if let Err(i_ptr) = j { i_ptr } else { panic!("") };
+ *i_ptr = 50;
+}
+
--- /dev/null
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(catch_expr)]
+
+pub fn main() {
+ let res: Result<i32, i32> = do catch {
+ Err("")?; //~ ERROR the trait bound `i32: std::convert::From<&str>` is not satisfied
+ Ok(5)
+ };
+ let res: Result<i32, i32> = do catch {
+ Ok("") //~ mismatched types
+ };
+}
--- /dev/null
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(catch_expr)]
+
+pub fn main() {
+ let cfg_res;
+ let _: Result<(), ()> = do catch {
+ Err(())?;
+ cfg_res = 5;
+ Ok(())
+ };
+ assert_eq!(cfg_res, 5); //~ ERROR use of possibly uninitialized variable
+}
+
match catch {
_ => {}
};
+
+ let catch_err = do catch {
+ Err(22)?;
+ Ok(1)
+ };
+ assert_eq!(catch_err, Err(22));
+
+ let catch_okay: Result<i32, i32> = do catch {
+ if false { Err(25)?; }
+ Ok::<(), i32>(())?;
+ Ok(28)
+ };
+ assert_eq!(catch_okay, Ok(28));
+
+ let catch_from_loop: Result<i32, i32> = do catch {
+ for i in 0..10 {
+ if i < 5 { Ok::<i32, i32>(i)?; } else { Err(i)?; }
+ }
+ Ok(22)
+ };
+ assert_eq!(catch_from_loop, Err(5));
+
+ let cfg_init;
+ let _res: Result<(), ()> = do catch {
+ cfg_init = 5;
+ Ok(())
+ };
+ assert_eq!(cfg_init, 5);
+
+ let my_string = "test".to_string();
+ let res: Result<&str, ()> = do catch {
+ Ok(&my_string)
+ };
+ assert_eq!(res, Ok("test"));
}