#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
pub struct Terminator<'tcx> {
+ pub span: Span,
+ pub scope: ScopeId,
pub kind: TerminatorKind<'tcx>
}
StmtKind::Expr { scope, expr } => {
unpack!(block = this.in_scope(scope, block, |this, _| {
let expr = this.hir.mirror(expr);
+ let expr_span = expr.span;
let temp = this.temp(expr.ty.clone());
unpack!(block = this.into(&temp, block, expr));
- unpack!(block = this.build_drop(block, temp));
+ unpack!(block = this.build_drop(block, expr_span, temp));
block.unit()
}));
}
pub fn terminate(&mut self,
block: BasicBlock,
+ scope: ScopeId,
+ span: Span,
kind: TerminatorKind<'tcx>) {
debug_assert!(self.block_data(block).terminator.is_none(),
"terminate: block {:?} already has a terminator set", block);
self.block_data_mut(block).terminator = Some(Terminator {
- kind: kind
+ span: span,
+ scope: scope,
+ kind: kind,
});
}
}
let (success, failure) = (this.cfg.start_new_block(), this.cfg.start_new_block());
this.cfg.terminate(block,
+ scope_id,
+ expr_span,
TerminatorKind::If {
cond: Operand::Consume(lt),
targets: (success, failure),
let mut then_block = this.cfg.start_new_block();
let mut else_block = this.cfg.start_new_block();
- this.cfg.terminate(block, TerminatorKind::If {
+ this.cfg.terminate(block, scope_id, expr_span, TerminatorKind::If {
cond: operand,
targets: (then_block, else_block)
});
};
let join_block = this.cfg.start_new_block();
- this.cfg.terminate(then_block, TerminatorKind::Goto { target: join_block });
- this.cfg.terminate(else_block, TerminatorKind::Goto { target: join_block });
+ this.cfg.terminate(then_block,
+ scope_id,
+ expr_span,
+ TerminatorKind::Goto { target: join_block });
+ this.cfg.terminate(else_block,
+ scope_id,
+ expr_span,
+ TerminatorKind::Goto { target: join_block });
join_block.unit()
}
LogicalOp::And => (else_block, false_block),
LogicalOp::Or => (true_block, else_block),
};
- this.cfg.terminate(block, TerminatorKind::If { cond: lhs, targets: blocks });
+ this.cfg.terminate(block,
+ scope_id,
+ expr_span,
+ TerminatorKind::If { cond: lhs, targets: blocks });
let rhs = unpack!(else_block = this.as_operand(else_block, rhs));
- this.cfg.terminate(else_block, TerminatorKind::If {
+ this.cfg.terminate(else_block, scope_id, expr_span, TerminatorKind::If {
cond: rhs,
targets: (true_block, false_block)
});
literal: this.hir.false_literal(),
});
- this.cfg.terminate(true_block, TerminatorKind::Goto { target: join_block });
- this.cfg.terminate(false_block, TerminatorKind::Goto { target: join_block });
+ this.cfg.terminate(true_block,
+ scope_id,
+ expr_span,
+ TerminatorKind::Goto { target: join_block });
+ this.cfg.terminate(false_block,
+ scope_id,
+ expr_span,
+ TerminatorKind::Goto { target: join_block });
join_block.unit()
}
let exit_block = this.cfg.start_new_block();
// start the loop
- this.cfg.terminate(block, TerminatorKind::Goto { target: loop_block });
+ this.cfg.terminate(block,
+ scope_id,
+ expr_span,
+ TerminatorKind::Goto { target: loop_block });
let might_break = this.in_loop_scope(loop_block, exit_block, move |this| {
// conduct the test, if necessary
let cond = unpack!(loop_block_end = this.as_operand(loop_block, cond_expr));
body_block = this.cfg.start_new_block();
this.cfg.terminate(loop_block_end,
+ scope_id,
+ expr_span,
TerminatorKind::If {
cond: cond,
targets: (body_block, exit_block)
let tmp = this.get_unit_temp();
// Execute the body, branching back to the test.
let body_block_end = unpack!(this.into(&tmp, body_block, body));
- this.cfg.terminate(body_block_end, TerminatorKind::Goto { target: loop_block });
+ this.cfg.terminate(body_block_end,
+ scope_id,
+ expr_span,
+ TerminatorKind::Goto { target: loop_block });
});
// If the loop may reach its exit_block, we assign an empty tuple to the
// destination to keep the MIR well-formed.
// Note: we evaluate assignments right-to-left. This
// is better for borrowck interaction with overloaded
// operators like x[j] = x[i].
+ let lhs = this.hir.mirror(lhs);
+ let lhs_span = lhs.span;
let rhs = unpack!(block = this.as_operand(block, rhs));
let lhs = unpack!(block = this.as_lvalue(block, lhs));
- unpack!(block = this.build_drop(block, lhs.clone()));
+ unpack!(block = this.build_drop(block, lhs_span, lhs.clone()));
this.cfg.push_assign(block, scope_id, expr_span, &lhs, Rvalue::Use(rhs));
block.unit()
}
let success = this.cfg.start_new_block();
let cleanup = this.diverge_cleanup();
- this.cfg.terminate(block, TerminatorKind::Call {
+ this.cfg.terminate(block, scope_id, expr_span, TerminatorKind::Call {
func: fun,
args: args,
cleanup: cleanup,
})
.map(|(arm_index, pattern, guard)| {
Candidate {
+ span: pattern.span,
match_pairs: vec![MatchPair::new(discriminant_lvalue.clone(), pattern)],
bindings: vec![],
guard: guard,
// an empty vector to be returned here, but the algorithm is
// not entirely precise
if !otherwise.is_empty() {
- let join_block = self.join_otherwise_blocks(otherwise);
+ let join_block = self.join_otherwise_blocks(span, otherwise);
self.panic(join_block, "something about matches algorithm not being precise", span);
}
for (arm_index, arm_body) in arm_bodies.into_iter().enumerate() {
let mut arm_block = arm_blocks.blocks[arm_index];
unpack!(arm_block = self.into(destination, arm_block, arm_body));
- self.cfg.terminate(arm_block, TerminatorKind::Goto { target: end_block });
+ self.cfg.terminate(arm_block,
+ var_scope_id,
+ span,
+ TerminatorKind::Goto { target: end_block });
}
end_block.unit()
// create a dummy candidate
let mut candidate = Candidate {
+ span: irrefutable_pat.span,
match_pairs: vec![MatchPair::new(initializer.clone(), &irrefutable_pat)],
bindings: vec![],
guard: None,
#[derive(Clone, Debug)]
pub struct Candidate<'pat, 'tcx:'pat> {
+ // span of the original pattern that gave rise to this candidate
+ span: Span,
+
// all of these must be satisfied...
match_pairs: Vec<MatchPair<'pat, 'tcx>>,
}
// Otherwise, let's process those remaining candidates.
- let join_block = self.join_otherwise_blocks(otherwise);
+ let join_block = self.join_otherwise_blocks(span, otherwise);
self.match_candidates(span, arm_blocks, untested_candidates, join_block)
}
fn join_otherwise_blocks(&mut self,
+ span: Span,
otherwise: Vec<BasicBlock>)
-> BasicBlock
{
+ let scope_id = self.innermost_scope_id();
if otherwise.len() == 1 {
otherwise[0]
} else {
let join_block = self.cfg.start_new_block();
for block in otherwise {
- self.cfg.terminate(block, TerminatorKind::Goto { target: join_block });
+ self.cfg.terminate(block,
+ scope_id,
+ span,
+ TerminatorKind::Goto { target: join_block });
}
join_block
}
let arm_block = arm_blocks.blocks[candidate.arm_index];
+ let scope_id = self.innermost_scope_id();
if let Some(guard) = candidate.guard {
// the block to branch to if the guard fails; if there is no
// guard, this block is simply unreachable
+ let guard = self.hir.mirror(guard);
+ let guard_span = guard.span;
let cond = unpack!(block = self.as_operand(block, guard));
let otherwise = self.cfg.start_new_block();
- self.cfg.terminate(block, TerminatorKind::If { cond: cond,
- targets: (arm_block, otherwise)});
+ self.cfg.terminate(block,
+ scope_id,
+ guard_span,
+ TerminatorKind::If { cond: cond,
+ targets: (arm_block, otherwise)});
Some(otherwise)
} else {
- self.cfg.terminate(block, TerminatorKind::Goto { target: arm_block });
+ self.cfg.terminate(block,
+ scope_id,
+ candidate.span,
+ TerminatorKind::Goto { target: arm_block });
None
}
}
let target_blocks: Vec<_> =
(0..num_enum_variants).map(|_| self.cfg.start_new_block())
.collect();
- self.cfg.terminate(block, TerminatorKind::Switch {
+ self.cfg.terminate(block, scope_id, test.span, TerminatorKind::Switch {
discr: lvalue.clone(),
adt_def: adt_def,
targets: target_blocks.clone()
.map(|_| self.cfg.start_new_block())
.chain(Some(otherwise))
.collect();
- self.cfg.terminate(block, TerminatorKind::SwitchInt {
- discr: lvalue.clone(),
- switch_ty: switch_ty,
- values: options.clone(),
- targets: targets.clone(),
- });
+ self.cfg.terminate(block,
+ scope_id,
+ test.span,
+ TerminatorKind::SwitchInt {
+ discr: lvalue.clone(),
+ switch_ty: switch_ty,
+ values: options.clone(),
+ targets: targets.clone(),
+ });
targets
}
let eq_result = self.temp(bool_ty);
let eq_block = self.cfg.start_new_block();
let cleanup = self.diverge_cleanup();
- self.cfg.terminate(block, Terminator::Call {
+ self.cfg.terminate(block, scope_id, test.span, TerminatorKind::Call {
func: Operand::Constant(Constant {
span: test.span,
ty: mty,
// check the result
let block = self.cfg.start_new_block();
- self.cfg.terminate(eq_block, Terminator::If {
+ self.cfg.terminate(eq_block, scope_id, test.span, TerminatorKind::If {
cond: Operand::Consume(eq_result),
targets: (block, fail),
});
// branch based on result
let target_blocks: Vec<_> = vec![self.cfg.start_new_block(),
self.cfg.start_new_block()];
- self.cfg.terminate(block, TerminatorKind::If {
+ self.cfg.terminate(block, scope_id, test.span, TerminatorKind::If {
cond: Operand::Consume(result),
targets: (target_blocks[0], target_blocks[1])
});
// branch based on result
let target_block = self.cfg.start_new_block();
- self.cfg.terminate(block, TerminatorKind::If {
+ self.cfg.terminate(block, scope_id, span, TerminatorKind::If {
cond: Operand::Consume(result),
targets: (target_block, fail_block)
});
.map(|(_, mp)| mp.clone())
.collect();
Candidate {
+ span: candidate.span,
match_pairs: other_match_pairs,
bindings: candidate.bindings.clone(),
guard: candidate.guard.clone(),
let all_match_pairs = consequent_match_pairs.chain(other_match_pairs).collect();
Candidate {
+ span: candidate.span,
match_pairs: all_match_pairs,
bindings: candidate.bindings.clone(),
guard: candidate.guard.clone(),
assert_eq!(builder.cfg.start_new_block(), END_BLOCK);
let mut block = START_BLOCK;
- let arg_decls = unpack!(block = builder.args_and_body(block,
- implicit_arguments,
- explicit_arguments,
- argument_extent,
- ast_block));
-
- builder.cfg.terminate(block, TerminatorKind::Goto { target: END_BLOCK });
- builder.cfg.terminate(END_BLOCK, TerminatorKind::Return);
+ let (arg_decls, arg_scope_id) =
+ unpack!(block = builder.args_and_body(block,
+ implicit_arguments,
+ explicit_arguments,
+ argument_extent,
+ ast_block));
+
+ builder.cfg.terminate(block, arg_scope_id, span,
+ TerminatorKind::Goto { target: END_BLOCK });
+ builder.cfg.terminate(END_BLOCK, arg_scope_id, span,
+ TerminatorKind::Return);
MirPlusPlus {
mir: Mir {
explicit_arguments: Vec<(Ty<'tcx>, &'tcx hir::Pat)>,
argument_extent: CodeExtent,
ast_block: &'tcx hir::Block)
- -> BlockAnd<Vec<ArgDecl<'tcx>>>
+ -> BlockAnd<(Vec<ArgDecl<'tcx>>, ScopeId)>
{
self.in_scope(argument_extent, block, |this, argument_scope_id| {
// to start, translate the argument patterns and collect the argument types.
// start the first basic block and translate the body
unpack!(block = this.ast_block(&Lvalue::ReturnPointer, block, ast_block));
- block.and(arg_decls)
+ block.and((arg_decls, argument_scope_id))
})
}
}
struct DropData<'tcx> {
+ span: Span,
value: Lvalue<'tcx>,
// NB: per-drop “cache” is necessary for the build_scope_drops function below.
/// The cached block for the cleanups-on-diverge path. This block contains code to run the
if let Some(ref free_data) = scope.free {
let next = self.cfg.start_new_block();
let free = build_free(self.hir.tcx(), tmp.clone(), free_data, next);
- self.cfg.terminate(block, free);
+ self.cfg.terminate(block, scope.id, span, free);
block = next;
}
self.scope_auxiliary[scope.id.index()]
.postdoms
.push(self.cfg.current_location(block));
}
- self.cfg.terminate(block, TerminatorKind::Goto { target: target });
+
+ let scope_id = self.innermost_scope_id();
+ self.cfg.terminate(block,
+ scope_id,
+ span,
+ TerminatorKind::Goto { target: target });
}
// Finding scopes
// No need to invalidate any caches here. The just-scheduled drop will branch into
// the drop that comes before it in the vector.
scope.drops.push(DropData {
+ span: span,
value: lvalue.clone(),
cached_block: None
});
}
/// Utility function for *non*-scope code to build their own drops
- pub fn build_drop(&mut self, block: BasicBlock, value: Lvalue<'tcx>) -> BlockAnd<()> {
+ pub fn build_drop(&mut self,
+ block: BasicBlock,
+ span: Span,
+ value: Lvalue<'tcx>)
+ -> BlockAnd<()> {
+ let scope_id = self.innermost_scope_id();
let next_target = self.cfg.start_new_block();
let diverge_target = self.diverge_cleanup();
- self.cfg.terminate(block, TerminatorKind::Drop {
- value: value,
- target: next_target,
- unwind: diverge_target,
- });
+ self.cfg.terminate(block,
+ scope_id,
+ span,
+ TerminatorKind::Drop {
+ value: value,
+ target: next_target,
+ unwind: diverge_target,
+ });
next_target.unit()
}
// =========
// FIXME: should be moved into their own module
pub fn panic_bounds_check(&mut self,
- block: BasicBlock,
- index: Operand<'tcx>,
- len: Operand<'tcx>,
- span: Span) {
+ block: BasicBlock,
+ index: Operand<'tcx>,
+ len: Operand<'tcx>,
+ span: Span) {
// fn(&(filename: &'static str, line: u32), index: usize, length: usize) -> !
let region = ty::ReStatic; // FIXME(mir-borrowck): use a better region?
let func = self.lang_function(lang_items::PanicBoundsCheckFnLangItem);
self.cfg.push_assign(block, scope_id, span, &tuple_ref, // tuple_ref = &tuple;
Rvalue::Ref(region, BorrowKind::Shared, tuple));
let cleanup = self.diverge_cleanup();
- self.cfg.terminate(block, TerminatorKind::Call {
+ self.cfg.terminate(block, scope_id, span, TerminatorKind::Call {
func: Operand::Constant(func),
args: vec![Operand::Consume(tuple_ref), index, len],
destination: None,
self.cfg.push_assign(block, scope_id, span, &tuple_ref, // tuple_ref = &tuple;
Rvalue::Ref(region, BorrowKind::Shared, tuple));
let cleanup = self.diverge_cleanup();
- self.cfg.terminate(block, TerminatorKind::Call {
+ self.cfg.terminate(block, scope_id, span, TerminatorKind::Call {
func: Operand::Constant(func),
args: vec![Operand::Consume(tuple_ref)],
cleanup: cleanup,
earlier_scopes.iter().rev().flat_map(|s| s.cached_block()).next()
});
let next = cfg.start_new_block();
- cfg.terminate(block, TerminatorKind::Drop {
+ cfg.terminate(block, scope.id, drop_data.span, TerminatorKind::Drop {
value: drop_data.value.clone(),
target: next,
unwind: on_diverge
let mut last_drop_block = None;
for drop_data in scope.drops.iter_mut().rev() {
if let Some(cached_block) = drop_data.cached_block {
- if let Some((previous_block, previous_value)) = previous {
- cfg.terminate(previous_block, TerminatorKind::Drop {
- value: previous_value,
- target: cached_block,
- unwind: None
- });
+ if let Some((previous_block, previous_span, previous_value)) = previous {
+ cfg.terminate(previous_block,
+ scope.id,
+ previous_span,
+ TerminatorKind::Drop {
+ value: previous_value,
+ target: cached_block,
+ unwind: None
+ });
return last_drop_block.unwrap();
} else {
return cached_block;
} else {
let block = cfg.start_new_cleanup_block();
drop_data.cached_block = Some(block);
- if let Some((previous_block, previous_value)) = previous {
- cfg.terminate(previous_block, TerminatorKind::Drop {
- value: previous_value,
- target: block,
- unwind: None
- });
+ if let Some((previous_block, previous_span, previous_value)) = previous {
+ cfg.terminate(previous_block,
+ scope.id,
+ previous_span,
+ TerminatorKind::Drop {
+ value: previous_value,
+ target: block,
+ unwind: None
+ });
} else {
last_drop_block = Some(block);
}
- previous = Some((block, drop_data.value.clone()));
+ previous = Some((block, drop_data.span, drop_data.value.clone()));
}
}
// Prepare the end target for this chain.
let mut target = target.unwrap_or_else(||{
let b = cfg.start_new_cleanup_block();
- cfg.terminate(b, TerminatorKind::Resume);
+ cfg.terminate(b, scope.id, DUMMY_SP, TerminatorKind::Resume); // TODO
b
});
cached_block
} else {
let into = cfg.start_new_cleanup_block();
- cfg.terminate(into, build_free(tcx, unit_temp, free_data, target));
+ cfg.terminate(into,
+ scope.id,
+ free_data.span,
+ build_free(tcx, unit_temp, free_data, target));
free_data.cached_block = Some(into);
into
}
};
- if let Some((previous_block, previous_value)) = previous {
+ if let Some((previous_block, previous_span, previous_value)) = previous {
// Finally, branch into that just-built `target` from the `previous_block`.
- cfg.terminate(previous_block, TerminatorKind::Drop {
- value: previous_value,
- target: target,
- unwind: None
- });
+ cfg.terminate(previous_block,
+ scope.id,
+ previous_span,
+ TerminatorKind::Drop {
+ value: previous_value,
+ target: target,
+ unwind: None
+ });
last_drop_block.unwrap()
} else {
// If `previous.is_none()`, there were no drops in this scope – we return the