/// 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 that’s being called
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()
+ }
}
}
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]
+ }
}
}
}
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)?;
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()]
+ }
}
}
}
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));
}
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);
}
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,
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
+ }
+ }
}
}
}
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
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
// 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;
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();
scope_id,
span,
TerminatorKind::Drop {
- value: value,
+ location: location,
target: next_target,
unwind: diverge_target,
});
}
+
+ 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
});
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
});
scope.id,
drop_data.span,
TerminatorKind::Drop {
- value: drop_data.value.clone(),
+ location: drop_data.location.clone(),
target: target,
unwind: None
});
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
}
}
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);
}
});
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
TerminatorKind::Switch {..} |
TerminatorKind::SwitchInt {..} |
+ TerminatorKind::DropAndReplace { .. } |
TerminatorKind::Resume => None,
TerminatorKind::Return => {
// 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 {
})
}
- 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) {
}
}
+ 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);