Move the check to unwind_to_block

This commit is contained in:
hyd-dev 2021-05-25 20:43:54 +08:00
parent 116172d180
commit 38472e0d5c
No known key found for this signature in database
GPG Key ID: 74FA7FD5B8DA14B8

View File

@ -757,13 +757,20 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
/// *Unwind* to the given `target` basic block.
/// Do *not* use for returning! Use `return_to_block` instead.
///
/// If `target` is `None`, that indicates the function does not need cleanup during
/// unwinding, and we will just keep propagating that upwards.
pub fn unwind_to_block(&mut self, target: Option<mir::BasicBlock>) {
/// If `target` is `StackPopUnwind::Skip`, that indicates the function does not need cleanup
/// during unwinding, and we will just keep propagating that upwards.
///
/// If `target` is `StackPopUnwind::NotAllowed`, that indicates the function does not allow
/// unwinding, and doing so is UB.
pub fn unwind_to_block(&mut self, target: StackPopUnwind) -> InterpResult<'tcx> {
self.frame_mut().loc = match target {
Some(block) => Ok(mir::Location { block, statement_index: 0 }),
None => Err(self.frame_mut().body.span),
StackPopUnwind::Cleanup(block) => Ok(mir::Location { block, statement_index: 0 }),
StackPopUnwind::Skip => Err(self.frame_mut().body.span),
StackPopUnwind::NotAllowed => {
throw_ub_format!("unwinding past a frame that does not allow unwinding")
}
};
Ok(())
}
/// Pops the current frame from the stack, deallocating the
@ -798,27 +805,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
throw_ub_format!("unwinding past the topmost frame of the stack");
}
// Where do we jump next?
// Usually we want to clean up (deallocate locals), but in a few rare cases we don't.
// In that case, we return early. We also avoid validation in that case,
// because this is CTFE and the final value will be thoroughly validated anyway.
let (cleanup, next_block) = match (self.frame().return_to_block, unwinding) {
(StackPopCleanup::Goto { ret, .. }, false) => (true, Some(ret)),
(StackPopCleanup::Goto { unwind, .. }, true) => (
true,
Some(match unwind {
StackPopUnwind::Cleanup(unwind) => Some(unwind),
StackPopUnwind::Skip => None,
StackPopUnwind::NotAllowed => {
throw_ub_format!("unwinding past a frame that does not allow unwinding")
}
}),
),
(StackPopCleanup::None { cleanup, .. }, _) => (cleanup, None),
};
let frame = self.stack_mut().pop().unwrap();
let frame =
self.stack_mut().pop().expect("tried to pop a stack frame, but there were none");
if !unwinding {
// Copy the return value to the caller's stack frame.
@ -831,9 +819,20 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
}
}
let return_to_block = frame.return_to_block;
// Now where do we jump next?
// Usually we want to clean up (deallocate locals), but in a few rare cases we don't.
// In that case, we return early. We also avoid validation in that case,
// because this is CTFE and the final value will be thoroughly validated anyway.
let cleanup = match return_to_block {
StackPopCleanup::Goto { .. } => true,
StackPopCleanup::None { cleanup, .. } => cleanup,
};
if !cleanup {
assert!(self.stack().is_empty(), "only the topmost frame should ever be leaked");
assert!(next_block.is_none(), "tried to skip cleanup when we have a next block!");
assert!(!unwinding, "tried to skip cleanup during unwinding");
// Leak the locals, skip validation, skip machine hook.
return Ok(());
@ -852,11 +851,16 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// Normal return, figure out where to jump.
if unwinding {
// Follow the unwind edge.
let unwind = next_block.expect("Encountered StackPopCleanup::None when unwinding!");
self.unwind_to_block(unwind);
let unwind = match return_to_block {
StackPopCleanup::Goto { unwind, .. } => unwind,
StackPopCleanup::None { .. } => {
panic!("Encountered StackPopCleanup::None when unwinding!")
}
};
self.unwind_to_block(unwind)?;
} else {
// Follow the normal return edge.
if let Some(ret) = next_block {
if let StackPopCleanup::Goto { ret, .. } = return_to_block {
self.return_to_block(ret)?;
}
}