make redundant StorageLive UB
This commit is contained in:
parent
cc03ee6702
commit
78deacc2ec
2 changed files with 16 additions and 23 deletions
|
@ -840,36 +840,31 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mark a storage as live, killing the previous content and returning it.
|
/// Mark a storage as live, killing the previous content.
|
||||||
/// Remember to deallocate that!
|
pub fn storage_live(&mut self, local: mir::Local) -> InterpResult<'tcx> {
|
||||||
pub fn storage_live(
|
|
||||||
&mut self,
|
|
||||||
local: mir::Local,
|
|
||||||
) -> InterpResult<'tcx, LocalValue<M::PointerTag>> {
|
|
||||||
assert!(local != mir::RETURN_PLACE, "Cannot make return place live");
|
assert!(local != mir::RETURN_PLACE, "Cannot make return place live");
|
||||||
trace!("{:?} is now live", local);
|
trace!("{:?} is now live", local);
|
||||||
|
|
||||||
let local_val = LocalValue::Uninitialized;
|
let local_val = LocalValue::Uninitialized;
|
||||||
// StorageLive *always* kills the value that's currently stored.
|
// StorageLive expects the local to be dead, and marks it live.
|
||||||
// However, we do not error if the variable already is live;
|
let old = mem::replace(&mut self.frame_mut().locals[local].value, local_val);
|
||||||
// see <https://github.com/rust-lang/rust/issues/42371>.
|
if !matches!(old, LocalValue::Dead) {
|
||||||
Ok(mem::replace(&mut self.frame_mut().locals[local].value, local_val))
|
throw_ub_format!("StorageLive on a local that was already live");
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the old value of the local.
|
pub fn storage_dead(&mut self, local: mir::Local) -> InterpResult<'tcx> {
|
||||||
/// Remember to deallocate that!
|
|
||||||
pub fn storage_dead(&mut self, local: mir::Local) -> LocalValue<M::PointerTag> {
|
|
||||||
assert!(local != mir::RETURN_PLACE, "Cannot make return place dead");
|
assert!(local != mir::RETURN_PLACE, "Cannot make return place dead");
|
||||||
trace!("{:?} is now dead", local);
|
trace!("{:?} is now dead", local);
|
||||||
|
|
||||||
mem::replace(&mut self.frame_mut().locals[local].value, LocalValue::Dead)
|
// It is entirely okay for this local to be already dead (at least that's how we currently generate MIR)
|
||||||
|
let old = mem::replace(&mut self.frame_mut().locals[local].value, LocalValue::Dead);
|
||||||
|
self.deallocate_local(old)?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn deallocate_local(
|
fn deallocate_local(&mut self, local: LocalValue<M::PointerTag>) -> InterpResult<'tcx> {
|
||||||
&mut self,
|
|
||||||
local: LocalValue<M::PointerTag>,
|
|
||||||
) -> InterpResult<'tcx> {
|
|
||||||
// FIXME: should we tell the user that there was a local which was never written to?
|
|
||||||
if let LocalValue::Live(Operand::Indirect(MemPlace { ptr, .. })) = local {
|
if let LocalValue::Live(Operand::Indirect(MemPlace { ptr, .. })) = local {
|
||||||
// All locals have a backing allocation, even if the allocation is empty
|
// All locals have a backing allocation, even if the allocation is empty
|
||||||
// due to the local having ZST type.
|
// due to the local having ZST type.
|
||||||
|
|
|
@ -95,14 +95,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
|
|
||||||
// Mark locals as alive
|
// Mark locals as alive
|
||||||
StorageLive(local) => {
|
StorageLive(local) => {
|
||||||
let old_val = self.storage_live(*local)?;
|
self.storage_live(*local)?;
|
||||||
self.deallocate_local(old_val)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark locals as dead
|
// Mark locals as dead
|
||||||
StorageDead(local) => {
|
StorageDead(local) => {
|
||||||
let old_val = self.storage_dead(*local);
|
self.storage_dead(*local)?;
|
||||||
self.deallocate_local(old_val)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// No dynamic semantics attached to `FakeRead`; MIR
|
// No dynamic semantics attached to `FakeRead`; MIR
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue