introduce DropAndReplace for translating assignments

this introduces a DropAndReplace terminator as a fix to #30380. That terminator
is suppsoed to be translated by desugaring during drop elaboration, which is
not implemented in this commit, so this breaks `-Z orbit` temporarily.
This commit is contained in:
Ariel Ben-Yehuda 2016-05-17 01:06:52 +03:00
parent 95206f438f
commit de7cb0fdd6
13 changed files with 139 additions and 45 deletions

View File

@ -330,11 +330,19 @@ pub enum TerminatorKind<'tcx> {
/// Drop the Lvalue
Drop {
value: Lvalue<'tcx>,
location: Lvalue<'tcx>,
target: BasicBlock,
unwind: Option<BasicBlock>
},
/// Drop the Lvalue and assign the new value over it
DropAndReplace {
location: Lvalue<'tcx>,
value: Operand<'tcx>,
target: BasicBlock,
unwind: Option<BasicBlock>,
},
/// Block ends with a call of a converging function
Call {
/// The function thats being called
@ -373,8 +381,14 @@ impl<'tcx> TerminatorKind<'tcx> {
slice::ref_slice(t).into_cow(),
Call { destination: None, cleanup: Some(ref c), .. } => slice::ref_slice(c).into_cow(),
Call { destination: None, cleanup: None, .. } => (&[]).into_cow(),
Drop { target, unwind: Some(unwind), .. } => vec![target, unwind].into_cow(),
Drop { ref target, .. } => slice::ref_slice(target).into_cow(),
DropAndReplace { target, unwind: Some(unwind), .. } |
Drop { target, unwind: Some(unwind), .. } => {
vec![target, unwind].into_cow()
}
DropAndReplace { ref target, unwind: None, .. } |
Drop { ref target, unwind: None, .. } => {
slice::ref_slice(target).into_cow()
}
}
}
@ -393,8 +407,12 @@ impl<'tcx> TerminatorKind<'tcx> {
Call { destination: Some((_, ref mut t)), cleanup: None, .. } => vec![t],
Call { destination: None, cleanup: Some(ref mut c), .. } => vec![c],
Call { destination: None, cleanup: None, .. } => vec![],
DropAndReplace { ref mut target, unwind: Some(ref mut unwind), .. } |
Drop { ref mut target, unwind: Some(ref mut unwind), .. } => vec![target, unwind],
Drop { ref mut target, .. } => vec![target]
DropAndReplace { ref mut target, unwind: None, .. } |
Drop { ref mut target, unwind: None, .. } => {
vec![target]
}
}
}
}
@ -461,7 +479,9 @@ impl<'tcx> TerminatorKind<'tcx> {
SwitchInt { discr: ref lv, .. } => write!(fmt, "switchInt({:?})", lv),
Return => write!(fmt, "return"),
Resume => write!(fmt, "resume"),
Drop { ref value, .. } => write!(fmt, "drop({:?})", value),
Drop { ref location, .. } => write!(fmt, "drop({:?})", location),
DropAndReplace { ref location, ref value, .. } =>
write!(fmt, "replace({:?} <- {:?})", location, value),
Call { ref func, ref args, ref destination, .. } => {
if let Some((ref destination, _)) = *destination {
write!(fmt, "{:?} = ", destination)?;
@ -506,8 +526,12 @@ impl<'tcx> TerminatorKind<'tcx> {
Call { destination: Some(_), cleanup: None, .. } => vec!["return".into_cow()],
Call { destination: None, cleanup: Some(_), .. } => vec!["unwind".into_cow()],
Call { destination: None, cleanup: None, .. } => vec![],
DropAndReplace { unwind: None, .. } |
Drop { unwind: None, .. } => vec!["return".into_cow()],
Drop { .. } => vec!["return".into_cow(), "unwind".into_cow()],
DropAndReplace { unwind: Some(_), .. } |
Drop { unwind: Some(_), .. } => {
vec!["return".into_cow(), "unwind".into_cow()]
}
}
}
}

View File

@ -394,10 +394,20 @@ macro_rules! make_mir_visitor {
TerminatorKind::Return => {
}
TerminatorKind::Drop { ref $($mutability)* value,
TerminatorKind::Drop { ref $($mutability)* location,
target,
unwind } => {
self.visit_lvalue(value, LvalueContext::Drop);
self.visit_lvalue(location, LvalueContext::Drop);
self.visit_branch(block, target);
unwind.map(|t| self.visit_branch(block, t));
}
TerminatorKind::DropAndReplace { ref $($mutability)* location,
ref $($mutability)* value,
target,
unwind } => {
self.visit_lvalue(location, LvalueContext::Drop);
self.visit_operand(value);
self.visit_branch(block, target);
unwind.map(|t| self.visit_branch(block, t));
}

View File

@ -444,10 +444,17 @@ impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D>
repr::TerminatorKind::Return |
repr::TerminatorKind::Resume => {}
repr::TerminatorKind::Goto { ref target } |
repr::TerminatorKind::Drop { ref target, value: _, unwind: None } => {
repr::TerminatorKind::Drop { ref target, location: _, unwind: None } |
repr::TerminatorKind::DropAndReplace {
ref target, value: _, location: _, unwind: None
} => {
self.propagate_bits_into_entry_set_for(in_out, changed, target);
}
repr::TerminatorKind::Drop { ref target, value: _, unwind: Some(ref unwind) } => {
repr::TerminatorKind::Drop { ref target, location: _, unwind: Some(ref unwind) } |
repr::TerminatorKind::DropAndReplace {
ref target, value: _, location: _, unwind: Some(ref unwind)
} => {
self.propagate_bits_into_entry_set_for(in_out, changed, target);
self.propagate_bits_into_entry_set_for(in_out, changed, unwind);
}

View File

@ -671,10 +671,18 @@ fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveD
let _ = discr;
}
TerminatorKind::Drop { value: ref lval, target: _, unwind: _ } => {
TerminatorKind::Drop { ref location, target: _, unwind: _ } => {
let source = Location { block: bb,
index: bb_data.statements.len() };
bb_ctxt.on_move_out_lval(SK::Drop, lval, source);
bb_ctxt.on_move_out_lval(SK::Drop, location, source);
}
TerminatorKind::DropAndReplace { ref location, ref value, .. } => {
let assigned_path = bb_ctxt.builder.move_path_for(location);
bb_ctxt.path_map.fill_to(assigned_path.idx());
let source = Location { block: bb,
index: bb_data.statements.len() };
bb_ctxt.on_operand(SK::Use, value, source);
}
TerminatorKind::Call { ref func, ref args, ref destination, cleanup: _ } => {
let source = Location { block: bb,

View File

@ -309,15 +309,23 @@ fn drop_flag_effects_for_location<'a, 'tcx, F>(
Some(stmt) => match stmt.kind {
repr::StatementKind::Assign(ref lvalue, _) => {
debug!("drop_flag_effects: assignment {:?}", stmt);
on_all_children_bits(tcx, mir, move_data,
on_all_children_bits(tcx, mir, move_data,
move_data.rev_lookup.find(lvalue),
|moi| callback(moi, DropFlagState::Present))
}
},
None => {
// terminator - no move-ins except for function return edge
let term = bb.terminator();
debug!("drop_flag_effects: terminator {:?}", term);
debug!("drop_flag_effects: replace {:?}", bb.terminator());
match bb.terminator().kind {
repr::TerminatorKind::DropAndReplace { ref location, .. } => {
on_all_children_bits(tcx, mir, move_data,
move_data.rev_lookup.find(location),
|moi| callback(moi, DropFlagState::Present))
}
_ => {
// other terminators do not contain move-ins
}
}
}
}
}

View File

@ -34,29 +34,25 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
let scope_id = this.innermost_scope_id();
let lhs_span = lhs.span;
let lhs_ty = lhs.ty;
let rhs_ty = rhs.ty;
let lhs_needs_drop = this.hir.needs_drop(lhs_ty);
let rhs_needs_drop = this.hir.needs_drop(rhs_ty);
// Note: we evaluate assignments right-to-left. This
// is better for borrowck interaction with overloaded
// operators like x[j] = x[i].
// Generate better code for things that don't need to be
// dropped.
let rhs = if lhs_needs_drop || rhs_needs_drop {
let op = unpack!(block = this.as_operand(block, rhs));
Rvalue::Use(op)
if this.hir.needs_drop(lhs.ty) {
let rhs = unpack!(block = this.as_operand(block, rhs));
let lhs = unpack!(block = this.as_lvalue(block, lhs));
unpack!(block = this.build_drop_and_replace(
block, lhs_span, lhs, rhs
));
block.unit()
} else {
unpack!(block = this.as_rvalue(block, rhs))
};
let lhs = unpack!(block = this.as_lvalue(block, lhs));
unpack!(block = this.build_drop(block, lhs_span, lhs.clone(), lhs_ty));
this.cfg.push_assign(block, scope_id, expr_span, &lhs, rhs);
block.unit()
let rhs = unpack!(block = this.as_rvalue(block, rhs));
let lhs = unpack!(block = this.as_lvalue(block, lhs));
this.cfg.push_assign(block, scope_id, expr_span, &lhs, rhs);
block.unit()
}
}
ExprKind::AssignOp { op, lhs, rhs } => {
// FIXME(#28160) there is an interesting semantics

View File

@ -139,7 +139,7 @@ struct DropData<'tcx> {
span: Span,
/// lvalue to drop
value: Lvalue<'tcx>,
location: Lvalue<'tcx>,
/// The cached block for the cleanups-on-diverge path. This block
/// contains code to run the current drop and all the preceding
@ -402,7 +402,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
// the drop that comes before it in the vector.
scope.drops.push(DropData {
span: span,
value: lvalue.clone(),
location: lvalue.clone(),
cached_block: None
});
return;
@ -497,7 +497,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
pub fn build_drop(&mut self,
block: BasicBlock,
span: Span,
value: Lvalue<'tcx>,
location: Lvalue<'tcx>,
ty: Ty<'tcx>) -> BlockAnd<()> {
if !self.hir.needs_drop(ty) {
return block.unit();
@ -509,7 +509,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
scope_id,
span,
TerminatorKind::Drop {
value: value,
location: location,
target: next_target,
unwind: diverge_target,
});
@ -517,6 +517,27 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
}
pub fn build_drop_and_replace(&mut self,
block: BasicBlock,
span: Span,
location: Lvalue<'tcx>,
value: Operand<'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,
scope_id,
span,
TerminatorKind::DropAndReplace {
location: location,
value: value,
target: next_target,
unwind: diverge_target,
});
next_target.unit()
}
// Panicking
// =========
// FIXME: should be moved into their own module
@ -653,7 +674,7 @@ fn build_scope_drops<'tcx>(cfg: &mut CFG<'tcx>,
});
let next = cfg.start_new_block();
cfg.terminate(block, scope.id, drop_data.span, TerminatorKind::Drop {
value: drop_data.value.clone(),
location: drop_data.location.clone(),
target: next,
unwind: on_diverge
});
@ -709,7 +730,7 @@ fn build_diverge_scope<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
scope.id,
drop_data.span,
TerminatorKind::Drop {
value: drop_data.value.clone(),
location: drop_data.location.clone(),
target: target,
unwind: None
});

View File

@ -105,7 +105,9 @@ impl Pass for BreakCleanupEdges {}
fn term_is_invoke(term: &Terminator) -> bool {
match term.kind {
TerminatorKind::Call { cleanup: Some(_), .. } |
TerminatorKind::Drop { unwind: Some(_), .. } => true,
// FIXME: not sure whether we need this one
TerminatorKind::Drop { unwind: Some(_), .. } |
TerminatorKind::DropAndReplace { .. } => true,
_ => false
}
}

View File

@ -29,12 +29,11 @@ impl<'tcx> MutVisitor<'tcx> for NoLandingPads {
TerminatorKind::SwitchInt { .. } => {
/* nothing to do */
},
TerminatorKind::Call { cleanup: ref mut unwind, .. } |
TerminatorKind::DropAndReplace { ref mut unwind, .. } |
TerminatorKind::Drop { ref mut unwind, .. } => {
unwind.take();
},
TerminatorKind::Call { ref mut cleanup, .. } => {
cleanup.take();
},
}
self.super_terminator(bb, terminator);
}

View File

@ -399,7 +399,7 @@ pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>,
});
let terminator = block.terminator_mut();
match terminator.kind {
TerminatorKind::Drop { value: Lvalue::Temp(index), target, .. } => {
TerminatorKind::Drop { location: Lvalue::Temp(index), target, .. } => {
if promoted(index) {
terminator.kind = TerminatorKind::Goto {
target: target

View File

@ -422,6 +422,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
TerminatorKind::Switch {..} |
TerminatorKind::SwitchInt {..} |
TerminatorKind::DropAndReplace { .. } |
TerminatorKind::Resume => None,
TerminatorKind::Return => {

View File

@ -363,6 +363,20 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
// no checks needed for these
}
TerminatorKind::DropAndReplace {
ref location,
ref value,
..
} => {
let lv_ty = mir.lvalue_ty(tcx, location).to_ty(tcx);
let rv_ty = mir.operand_ty(tcx, value);
if let Err(terr) = self.sub_types(self.last_span, rv_ty, lv_ty) {
span_mirbug!(self, term, "bad DropAndReplace ({:?} = {:?}): {:?}",
lv_ty, rv_ty, terr);
}
}
TerminatorKind::If { ref cond, .. } => {
let cond_ty = mir.operand_ty(tcx, cond);
match cond_ty.sty {

View File

@ -143,8 +143,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
})
}
mir::TerminatorKind::Drop { ref value, target, unwind } => {
let lvalue = self.trans_lvalue(&bcx, value);
mir::TerminatorKind::Drop { ref location, target, unwind } => {
let lvalue = self.trans_lvalue(&bcx, location);
let ty = lvalue.ty.to_ty(bcx.tcx());
// Double check for necessity to drop
if !glue::type_needs_drop(bcx.tcx(), ty) {
@ -177,6 +177,10 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
}
}
mir::TerminatorKind::DropAndReplace { .. } => {
bug!("undesugared DropAndReplace in trans: {:?}", data);
}
mir::TerminatorKind::Call { ref func, ref args, ref destination, ref cleanup } => {
// Create the callee. This is a fn ptr or zero-sized and hence a kind of scalar.
let callee = self.trans_operand(&bcx, func);