Rollup merge of #143528 - RalfJung:stack-pop-cleanup, r=oli-obk
interpret: rename StackPopCleanup The name `StackPopCleanup` stopped making sense a long time ago IMO -- in the common case, it has nothing to do with "cleanup", and everything with where the program should jump next. If we didn't have unwinding this would be just the return block, but given that we do have unwinding I figured maybe "continuation" would be a good name. This comes up in [continuation-passing style](https://en.wikipedia.org/wiki/Continuation-passing_style) and refers to where the program will *continue* when a function is done. So from a PL perspective it is the most fitting term I think -- but it may be too jargony. r? `@oli-obk` what do you think?
This commit is contained in:
commit
eed55947ac
10 changed files with 56 additions and 56 deletions
|
|
@ -19,7 +19,7 @@ use super::{CanAccessMutGlobal, CompileTimeInterpCx, CompileTimeMachine};
|
|||
use crate::const_eval::CheckAlignment;
|
||||
use crate::interpret::{
|
||||
CtfeValidationMode, GlobalId, Immediate, InternKind, InternResult, InterpCx, InterpErrorKind,
|
||||
InterpResult, MPlaceTy, MemoryKind, OpTy, RefTracking, StackPopCleanup, create_static_alloc,
|
||||
InterpResult, MPlaceTy, MemoryKind, OpTy, RefTracking, ReturnContinuation, create_static_alloc,
|
||||
intern_const_alloc_recursive, interp_ok, throw_exhaust,
|
||||
};
|
||||
use crate::{CTRL_C_RECEIVED, errors};
|
||||
|
|
@ -76,7 +76,7 @@ fn eval_body_using_ecx<'tcx, R: InterpretationResult<'tcx>>(
|
|||
cid.instance,
|
||||
body,
|
||||
&ret.clone().into(),
|
||||
StackPopCleanup::Root { cleanup: false },
|
||||
ReturnContinuation::Stop { cleanup: false },
|
||||
)?;
|
||||
ecx.storage_live_for_always_live_locals()?;
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ use tracing::{info, instrument, trace};
|
|||
|
||||
use super::{
|
||||
CtfeProvenance, FnVal, ImmTy, InterpCx, InterpResult, MPlaceTy, Machine, OpTy, PlaceTy,
|
||||
Projectable, Provenance, ReturnAction, Scalar, StackPopCleanup, StackPopInfo, interp_ok,
|
||||
Projectable, Provenance, ReturnAction, ReturnContinuation, Scalar, StackPopInfo, interp_ok,
|
||||
throw_ub, throw_ub_custom, throw_unsup_format,
|
||||
};
|
||||
use crate::fluent_generated as fluent;
|
||||
|
|
@ -340,7 +340,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
args: &[FnArg<'tcx, M::Provenance>],
|
||||
with_caller_location: bool,
|
||||
destination: &PlaceTy<'tcx, M::Provenance>,
|
||||
mut stack_pop: StackPopCleanup,
|
||||
mut cont: ReturnContinuation,
|
||||
) -> InterpResult<'tcx> {
|
||||
// Compute callee information.
|
||||
// FIXME: for variadic support, do we have to somehow determine callee's extra_args?
|
||||
|
|
@ -365,15 +365,15 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
|
||||
if !callee_fn_abi.can_unwind {
|
||||
// The callee cannot unwind, so force the `Unreachable` unwind handling.
|
||||
match &mut stack_pop {
|
||||
StackPopCleanup::Root { .. } => {}
|
||||
StackPopCleanup::Goto { unwind, .. } => {
|
||||
match &mut cont {
|
||||
ReturnContinuation::Stop { .. } => {}
|
||||
ReturnContinuation::Goto { unwind, .. } => {
|
||||
*unwind = mir::UnwindAction::Unreachable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.push_stack_frame_raw(instance, body, destination, stack_pop)?;
|
||||
self.push_stack_frame_raw(instance, body, destination, cont)?;
|
||||
|
||||
// If an error is raised here, pop the frame again to get an accurate backtrace.
|
||||
// To this end, we wrap it all in a `try` block.
|
||||
|
|
@ -617,7 +617,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
&args,
|
||||
with_caller_location,
|
||||
destination,
|
||||
StackPopCleanup::Goto { ret: target, unwind },
|
||||
ReturnContinuation::Goto { ret: target, unwind },
|
||||
)
|
||||
}
|
||||
// `InstanceKind::Virtual` does not have callable MIR. Calls to `Virtual` instances must be
|
||||
|
|
@ -755,8 +755,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
// Note that we are using `pop_stack_frame_raw` and not `return_from_current_stack_frame`,
|
||||
// as the latter "executes" the goto to the return block, but we don't want to,
|
||||
// only the tail called function should return to the current return block.
|
||||
let StackPopInfo { return_action, return_to_block, return_place } = self
|
||||
.pop_stack_frame_raw(false, |_this, _return_place| {
|
||||
let StackPopInfo { return_action, return_cont, return_place } =
|
||||
self.pop_stack_frame_raw(false, |_this, _return_place| {
|
||||
// This function's return value is just discarded, the tail-callee will fill in the return place instead.
|
||||
interp_ok(())
|
||||
})?;
|
||||
|
|
@ -764,7 +764,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
assert_eq!(return_action, ReturnAction::Normal);
|
||||
|
||||
// Take the "stack pop cleanup" info, and use that to initiate the next call.
|
||||
let StackPopCleanup::Goto { ret, unwind } = return_to_block else {
|
||||
let ReturnContinuation::Goto { ret, unwind } = return_cont else {
|
||||
bug!("can't tailcall as root");
|
||||
};
|
||||
|
||||
|
|
@ -896,23 +896,23 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
// Normal return, figure out where to jump.
|
||||
if unwinding {
|
||||
// Follow the unwind edge.
|
||||
match stack_pop_info.return_to_block {
|
||||
StackPopCleanup::Goto { unwind, .. } => {
|
||||
match stack_pop_info.return_cont {
|
||||
ReturnContinuation::Goto { unwind, .. } => {
|
||||
// This must be the very last thing that happens, since it can in fact push a new stack frame.
|
||||
self.unwind_to_block(unwind)
|
||||
}
|
||||
StackPopCleanup::Root { .. } => {
|
||||
panic!("encountered StackPopCleanup::Root when unwinding!")
|
||||
ReturnContinuation::Stop { .. } => {
|
||||
panic!("encountered ReturnContinuation::Stop when unwinding!")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Follow the normal return edge.
|
||||
match stack_pop_info.return_to_block {
|
||||
StackPopCleanup::Goto { ret, .. } => self.return_to_block(ret),
|
||||
StackPopCleanup::Root { .. } => {
|
||||
match stack_pop_info.return_cont {
|
||||
ReturnContinuation::Goto { ret, .. } => self.return_to_block(ret),
|
||||
ReturnContinuation::Stop { .. } => {
|
||||
assert!(
|
||||
self.stack().is_empty(),
|
||||
"only the bottommost frame can have StackPopCleanup::Root"
|
||||
"only the bottommost frame can have ReturnContinuation::Stop"
|
||||
);
|
||||
interp_ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ pub use self::operand::{ImmTy, Immediate, OpTy};
|
|||
pub use self::place::{MPlaceTy, MemPlaceMeta, PlaceTy, Writeable};
|
||||
use self::place::{MemPlace, Place};
|
||||
pub use self::projection::{OffsetMode, Projectable};
|
||||
pub use self::stack::{Frame, FrameInfo, LocalState, StackPopCleanup, StackPopInfo};
|
||||
pub use self::stack::{Frame, FrameInfo, LocalState, ReturnContinuation, StackPopInfo};
|
||||
pub(crate) use self::util::create_static_alloc;
|
||||
pub use self::validity::{CtfeValidationMode, RangeSet, RefTracking};
|
||||
pub use self::visitor::ValueVisitor;
|
||||
|
|
|
|||
|
|
@ -72,8 +72,8 @@ pub struct Frame<'tcx, Prov: Provenance = CtfeProvenance, Extra = ()> {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Return place and locals
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// Work to perform when returning from this function.
|
||||
return_to_block: StackPopCleanup,
|
||||
/// Where to continue when returning from this function.
|
||||
return_cont: ReturnContinuation,
|
||||
|
||||
/// The location where the result of the current stack frame should be written to,
|
||||
/// and its layout in the caller. This place is to be interpreted relative to the
|
||||
|
|
@ -106,19 +106,19 @@ pub struct Frame<'tcx, Prov: Provenance = CtfeProvenance, Extra = ()> {
|
|||
pub(super) loc: Either<mir::Location, Span>,
|
||||
}
|
||||
|
||||
/// Where and how to continue when returning/unwinding from the current function.
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Debug)] // Miri debug-prints these
|
||||
pub enum StackPopCleanup {
|
||||
pub enum ReturnContinuation {
|
||||
/// Jump to the next block in the caller, or cause UB if None (that's a function
|
||||
/// that may never return). Also store layout of return place so
|
||||
/// we can validate it at that layout.
|
||||
/// that may never return).
|
||||
/// `ret` stores the block we jump to on a normal return, while `unwind`
|
||||
/// stores the block used for cleanup during unwinding.
|
||||
Goto { ret: Option<mir::BasicBlock>, unwind: mir::UnwindAction },
|
||||
/// The root frame of the stack: nowhere else to jump to.
|
||||
/// The root frame of the stack: nowhere else to jump to, so we stop.
|
||||
/// `cleanup` says whether locals are deallocated. Static computation
|
||||
/// wants them leaked to intern what they need (and just throw away
|
||||
/// the entire `ecx` when it is done).
|
||||
Root { cleanup: bool },
|
||||
Stop { cleanup: bool },
|
||||
}
|
||||
|
||||
/// Return type of [`InterpCx::pop_stack_frame_raw`].
|
||||
|
|
@ -127,8 +127,8 @@ pub struct StackPopInfo<'tcx, Prov: Provenance> {
|
|||
/// stack frame.
|
||||
pub return_action: ReturnAction,
|
||||
|
||||
/// [`return_to_block`](Frame::return_to_block) of the popped stack frame.
|
||||
pub return_to_block: StackPopCleanup,
|
||||
/// [`return_cont`](Frame::return_cont) of the popped stack frame.
|
||||
pub return_cont: ReturnContinuation,
|
||||
|
||||
/// [`return_place`](Frame::return_place) of the popped stack frame.
|
||||
pub return_place: PlaceTy<'tcx, Prov>,
|
||||
|
|
@ -255,7 +255,7 @@ impl<'tcx, Prov: Provenance> Frame<'tcx, Prov> {
|
|||
Frame {
|
||||
body: self.body,
|
||||
instance: self.instance,
|
||||
return_to_block: self.return_to_block,
|
||||
return_cont: self.return_cont,
|
||||
return_place: self.return_place,
|
||||
locals: self.locals,
|
||||
loc: self.loc,
|
||||
|
|
@ -350,20 +350,20 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
/// the arguments or local variables.
|
||||
///
|
||||
/// The high-level version of this is `init_stack_frame`.
|
||||
#[instrument(skip(self, body, return_place, return_to_block), level = "debug")]
|
||||
#[instrument(skip(self, body, return_place, return_cont), level = "debug")]
|
||||
pub(crate) fn push_stack_frame_raw(
|
||||
&mut self,
|
||||
instance: ty::Instance<'tcx>,
|
||||
body: &'tcx mir::Body<'tcx>,
|
||||
return_place: &PlaceTy<'tcx, M::Provenance>,
|
||||
return_to_block: StackPopCleanup,
|
||||
return_cont: ReturnContinuation,
|
||||
) -> InterpResult<'tcx> {
|
||||
trace!("body: {:#?}", body);
|
||||
|
||||
// We can push a `Root` frame if and only if the stack is empty.
|
||||
debug_assert_eq!(
|
||||
self.stack().is_empty(),
|
||||
matches!(return_to_block, StackPopCleanup::Root { .. })
|
||||
matches!(return_cont, ReturnContinuation::Stop { .. })
|
||||
);
|
||||
|
||||
// First push a stack frame so we have access to `instantiate_from_current_frame` and other
|
||||
|
|
@ -373,7 +373,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
let pre_frame = Frame {
|
||||
body,
|
||||
loc: Right(body.span), // Span used for errors caused during preamble.
|
||||
return_to_block,
|
||||
return_cont,
|
||||
return_place: return_place.clone(),
|
||||
locals,
|
||||
instance,
|
||||
|
|
@ -429,15 +429,15 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
copy_ret_val(self, &frame.return_place)?;
|
||||
}
|
||||
|
||||
let return_to_block = frame.return_to_block;
|
||||
let return_cont = frame.return_cont;
|
||||
let return_place = frame.return_place.clone();
|
||||
|
||||
// Cleanup: deallocate locals.
|
||||
// Usually we want to clean up (deallocate locals), but in a few rare cases we don't.
|
||||
// We do this while the frame is still on the stack, so errors point to the callee.
|
||||
let cleanup = match return_to_block {
|
||||
StackPopCleanup::Goto { .. } => true,
|
||||
StackPopCleanup::Root { cleanup, .. } => cleanup,
|
||||
let cleanup = match return_cont {
|
||||
ReturnContinuation::Goto { .. } => true,
|
||||
ReturnContinuation::Stop { cleanup, .. } => cleanup,
|
||||
};
|
||||
|
||||
let return_action = if cleanup {
|
||||
|
|
@ -455,7 +455,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
ReturnAction::NoCleanup
|
||||
};
|
||||
|
||||
interp_ok(StackPopInfo { return_action, return_to_block, return_place })
|
||||
interp_ok(StackPopInfo { return_action, return_cont, return_place })
|
||||
}
|
||||
|
||||
/// In the current stack frame, mark all locals as live that are not arguments and don't have
|
||||
|
|
|
|||
|
|
@ -894,7 +894,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
start_abi,
|
||||
&[func_arg],
|
||||
Some(&ret_place),
|
||||
StackPopCleanup::Root { cleanup: true },
|
||||
ReturnContinuation::Stop { cleanup: true },
|
||||
)?;
|
||||
|
||||
// Restore the old active thread frame.
|
||||
|
|
|
|||
|
|
@ -436,7 +436,7 @@ pub fn create_ecx<'tcx>(
|
|||
ImmTy::from_uint(sigpipe, ecx.machine.layouts.u8),
|
||||
],
|
||||
Some(&ret_place),
|
||||
StackPopCleanup::Root { cleanup: true },
|
||||
ReturnContinuation::Stop { cleanup: true },
|
||||
)?;
|
||||
}
|
||||
MiriEntryFnType::MiriStart => {
|
||||
|
|
@ -445,7 +445,7 @@ pub fn create_ecx<'tcx>(
|
|||
ExternAbi::Rust,
|
||||
&[argc, argv],
|
||||
Some(&ret_place),
|
||||
StackPopCleanup::Root { cleanup: true },
|
||||
ReturnContinuation::Stop { cleanup: true },
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -444,7 +444,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
caller_abi: ExternAbi,
|
||||
args: &[ImmTy<'tcx>],
|
||||
dest: Option<&MPlaceTy<'tcx>>,
|
||||
stack_pop: StackPopCleanup,
|
||||
cont: ReturnContinuation,
|
||||
) -> InterpResult<'tcx> {
|
||||
let this = self.eval_context_mut();
|
||||
|
||||
|
|
@ -472,7 +472,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
&args.iter().map(|a| FnArg::Copy(a.clone().into())).collect::<Vec<_>>(),
|
||||
/*with_caller_location*/ false,
|
||||
&dest.into(),
|
||||
stack_pop,
|
||||
cont,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1199,7 +1199,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
|
|||
ExternAbi::Rust,
|
||||
&[],
|
||||
None,
|
||||
StackPopCleanup::Goto { ret: None, unwind: mir::UnwindAction::Unreachable },
|
||||
ReturnContinuation::Goto { ret: None, unwind: mir::UnwindAction::Unreachable },
|
||||
)?;
|
||||
interp_ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
&[data.clone()],
|
||||
None,
|
||||
// Directly return to caller.
|
||||
StackPopCleanup::Goto { ret, unwind: mir::UnwindAction::Continue },
|
||||
ReturnContinuation::Goto { ret, unwind: mir::UnwindAction::Continue },
|
||||
)?;
|
||||
|
||||
// We ourselves will return `0`, eventually (will be overwritten if we catch a panic).
|
||||
|
|
@ -143,7 +143,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
&[catch_unwind.data, payload],
|
||||
None,
|
||||
// Directly return to caller of `catch_unwind`.
|
||||
StackPopCleanup::Goto {
|
||||
ReturnContinuation::Goto {
|
||||
ret: catch_unwind.ret,
|
||||
// `catch_fn` must not unwind.
|
||||
unwind: mir::UnwindAction::Unreachable,
|
||||
|
|
@ -172,7 +172,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
ExternAbi::Rust,
|
||||
&[this.mplace_to_ref(&msg)?],
|
||||
None,
|
||||
StackPopCleanup::Goto { ret: None, unwind },
|
||||
ReturnContinuation::Goto { ret: None, unwind },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -191,7 +191,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
ExternAbi::Rust,
|
||||
&[this.mplace_to_ref(&msg)?],
|
||||
None,
|
||||
StackPopCleanup::Goto { ret: None, unwind: mir::UnwindAction::Unreachable },
|
||||
ReturnContinuation::Goto { ret: None, unwind: mir::UnwindAction::Unreachable },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -220,7 +220,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
ExternAbi::Rust,
|
||||
&[index, len],
|
||||
None,
|
||||
StackPopCleanup::Goto { ret: None, unwind },
|
||||
ReturnContinuation::Goto { ret: None, unwind },
|
||||
)?;
|
||||
}
|
||||
MisalignedPointerDereference { required, found } => {
|
||||
|
|
@ -241,7 +241,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
ExternAbi::Rust,
|
||||
&[required, found],
|
||||
None,
|
||||
StackPopCleanup::Goto { ret: None, unwind },
|
||||
ReturnContinuation::Goto { ret: None, unwind },
|
||||
)?;
|
||||
}
|
||||
|
||||
|
|
@ -254,7 +254,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
ExternAbi::Rust,
|
||||
&[],
|
||||
None,
|
||||
StackPopCleanup::Goto { ret: None, unwind },
|
||||
ReturnContinuation::Goto { ret: None, unwind },
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -325,7 +325,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
ExternAbi::System { unwind: false },
|
||||
&[null_ptr.clone(), ImmTy::from_scalar(reason, this.machine.layouts.u32), null_ptr],
|
||||
None,
|
||||
StackPopCleanup::Root { cleanup: true },
|
||||
ReturnContinuation::Stop { cleanup: true },
|
||||
)?;
|
||||
interp_ok(())
|
||||
}
|
||||
|
|
@ -346,7 +346,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
ExternAbi::C { unwind: false },
|
||||
&[ImmTy::from_scalar(data, this.machine.layouts.mut_raw_ptr)],
|
||||
None,
|
||||
StackPopCleanup::Root { cleanup: true },
|
||||
ReturnContinuation::Stop { cleanup: true },
|
||||
)?;
|
||||
|
||||
return interp_ok(Poll::Pending);
|
||||
|
|
@ -383,7 +383,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
ExternAbi::C { unwind: false },
|
||||
&[ImmTy::from_scalar(ptr, this.machine.layouts.mut_raw_ptr)],
|
||||
None,
|
||||
StackPopCleanup::Root { cleanup: true },
|
||||
ReturnContinuation::Stop { cleanup: true },
|
||||
)?;
|
||||
|
||||
return interp_ok(Poll::Pending);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue