]> git.lizzy.rs Git - rust.git/blobdiff - src/librustc_mir/interpret/eval_context.rs
Some cleanup
[rust.git] / src / librustc_mir / interpret / eval_context.rs
index d126d10620bdb201d93aa056abe8c9c4018e77d6..6598b0f99f2a99e4e73606ed637672c7fa39ad74 100644 (file)
@@ -21,7 +21,7 @@
 
 use super::{
     Immediate, Operand, MemPlace, MPlaceTy, Place, PlaceTy, ScalarMaybeUndef,
-    Memory, Machine, PointerArithmetic, FnVal, StackPopInfo
+    Memory, Machine, StackPopInfo
 };
 
 pub struct InterpCx<'mir, 'tcx, M: Machine<'mir, 'tcx>> {
@@ -60,6 +60,9 @@ pub struct Frame<'mir, 'tcx, Tag=(), Extra=()> {
     /// The span of the call site.
     pub span: source_map::Span,
 
+    /// Extra data for the machine.
+    pub extra: Extra,
+
     ////////////////////////////////////////////////////////////////////////////////
     // Return place and locals
     ////////////////////////////////////////////////////////////////////////////////
@@ -82,13 +85,12 @@ pub struct Frame<'mir, 'tcx, Tag=(), Extra=()> {
     ////////////////////////////////////////////////////////////////////////////////
     /// The block that is currently executed (or will be executed after the above call stacks
     /// return).
-    pub block: mir::BasicBlock,
+    /// If this is `None`, we are unwinding and this function doesn't need any clean-up.
+    /// Just continue the same as with `Resume`.
+    pub block: Option<mir::BasicBlock>,
 
     /// The index of the currently evaluated statement.
     pub stmt: usize,
-
-    /// Extra data for the machine.
-    pub extra: Extra,
 }
 
 #[derive(Clone, Eq, PartialEq, Debug)] // Miri debug-prints these
@@ -96,7 +98,7 @@ pub enum StackPopCleanup {
     /// 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.
-    /// 'ret' stores the block we jump to on a normal return, while 'unwind'
+    /// `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: Option<mir::BasicBlock> },
     /// Just do nohing: Used by Main and for the box_alloc hook in miri.
@@ -491,7 +493,7 @@ pub fn push_stack_frame(
         let extra = M::stack_push(self)?;
         self.stack.push(Frame {
             body,
-            block: mir::START_BLOCK,
+            block: Some(mir::START_BLOCK),
             return_to_block,
             return_place,
             // empty local array, we fill it in below, after we are inside the stack frame and
@@ -549,51 +551,88 @@ pub fn push_stack_frame(
         }
     }
 
-    pub(super) fn pop_stack_frame_internal(
+    /// Pops the current frame from the stack, deallocating the
+    /// memory for allocated locals.
+    ///
+    /// If `unwinding` is `false`, then we are performing a normal return
+    /// from a function. In this case, we jump back into the frame of the caller,
+    /// and continue execution as normal.
+    ///
+    /// If `unwinding` is `true`, then we are in the middle of a panic,
+    /// and need to unwind this frame. In this case, we jump to the
+    /// `cleanup` block for the function, which is responsible for running
+    /// `Drop` impls for any locals that have been initialized at this point.
+    /// The cleanup block ends with a special `Resume` terminator, which will
+    /// cause us to continue unwinding.
+    pub(super) fn pop_stack_frame(
         &mut self,
         unwinding: bool
-    ) -> InterpResult<'tcx, (StackPopCleanup, StackPopInfo)> {
+    ) -> InterpResult<'tcx> {
         info!("LEAVING({}) {} (unwinding = {})",
             self.cur_frame(), self.frame().instance, unwinding);
 
+        // Sanity check `unwinding`.
+        assert_eq!(
+            unwinding,
+            match self.frame().block {
+                None => true,
+                Some(block) => self.body().basic_blocks()[block].is_cleanup
+            }
+        );
+
         ::log_settings::settings().indentation -= 1;
         let frame = self.stack.pop().expect(
             "tried to pop a stack frame, but there were none",
         );
         let stack_pop_info = M::stack_pop(self, frame.extra)?;
+        match (unwinding, stack_pop_info) {
+            (true, StackPopInfo::StartUnwinding) =>
+                bug!("Attempted to start unwinding while already unwinding!"),
+            (false, StackPopInfo::StopUnwinding) =>
+                bug!("Attempted to stop unwinding while there is no unwinding!"),
+            _ => {}
+        }
 
-        // Abort early if we do not want to clean up: We also avoid validation in that case,
+        // 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.
-        match frame.return_to_block {
-            StackPopCleanup::Goto{ .. } => {},
+        let cleanup = unwinding || match frame.return_to_block {
+            StackPopCleanup::Goto{ .. } => true,
             StackPopCleanup::None { cleanup, .. } => {
-                assert!(!unwinding, "Encountered StackPopCleanup::None while unwinding");
-
-                if !cleanup {
-                    assert!(self.stack.is_empty(), "only the topmost frame should ever be leaked");
-                    // Leak the locals, skip validation.
-                    return Ok((frame.return_to_block, stack_pop_info));
-                }
+                cleanup
             }
+        };
+        if !cleanup {
+            assert!(self.stack.is_empty(), "only the topmost frame should ever be leaked");
+            // Leak the locals, skip validation.
+            return Ok(());
         }
-        // Deallocate all locals that are backed by an allocation.
+
+        // Cleanup: deallocate all locals that are backed by an allocation.
         for local in frame.locals {
             self.deallocate_local(local.value)?;
         }
 
-        // If we're popping frames due to unwinding, and we didn't just exit
-        // unwinding, we skip a bunch of validation and cleanup logic (including
-        // jumping to the regular return block specified in the StackPopCleanup)
-        let cur_unwinding = unwinding && stack_pop_info != StackPopInfo::StopUnwinding;
+        // Now where do we jump next?
 
-        info!("StackPopCleanup: {:?} StackPopInfo: {:?} cur_unwinding = {:?}",
+        // Determine if we leave this function normally or via unwinding.
+        let cur_unwinding = unwinding && stack_pop_info != StackPopInfo::StopUnwinding;
+        trace!("StackPopCleanup: {:?} StackPopInfo: {:?} cur_unwinding = {:?}",
                frame.return_to_block, stack_pop_info, cur_unwinding);
-
-
-        // When we're popping a stack frame for unwinding purposes,
-        // we don't care at all about returning-related stuff (i.e. return_place
-        // and return_to_block), because we're not performing a return from this frame.
-        if !cur_unwinding  {
+        if cur_unwinding {
+            // Follow the unwind edge.
+            match frame.return_to_block {
+                StackPopCleanup::Goto { unwind, .. } => {
+                    let next_frame = self.frame_mut();
+                    // If `unwind` is `None`, we'll leave that function immediately again.
+                    next_frame.block = unwind;
+                    next_frame.stmt = 0;
+                },
+                StackPopCleanup::None { .. } =>
+                    bug!("Encountered StackPopCleanup::None while unwinding"),
+            }
+        } else {
+            // Follow the normal return edge.
             // Validate the return value. Do this after deallocating so that we catch dangling
             // references.
             if let Some(return_place) = frame.return_place {
@@ -625,70 +664,9 @@ pub(super) fn pop_stack_frame_internal(
             }
         }
 
-
-        Ok((frame.return_to_block, stack_pop_info))
-    }
-
-    pub(super) fn pop_stack_frame(&mut self, unwinding: bool) -> InterpResult<'tcx> {
-        let (mut cleanup, mut stack_pop_info) = self.pop_stack_frame_internal(unwinding)?;
-
-        // There are two cases where we want to unwind the stack:
-        // * The caller explicitly told us (i.e. we hit a Resume terminator)
-        // * The machine indicated that we've just started unwinding (i.e.
-        // a panic has just occured)
-        if unwinding || stack_pop_info == StackPopInfo::StartUnwinding {
-            trace!("unwinding: starting stack unwind...");
-            // Overwrite our current stack_pop_info, so that the check
-            // below doesn't fail.
-            stack_pop_info = StackPopInfo::Normal;
-            // There are three posible ways that we can exit the loop:
-            // 1) We find an unwind block - we jump to it to allow cleanup
-            // to occur for that frame
-            // 2) pop_stack_frame_internal reports that we're no longer unwinding
-            //    - this means that the panic has been caught, and that execution
-            //    should continue as normal
-            // 3) We pop all of our frames off the stack - this should never happen.
-            while !self.stack.is_empty() {
-                match stack_pop_info {
-                    // We tried to start unwinding while we were already
-                    // unwinding. Note that this **is not** the same thing
-                    // as a double panic, which will be intercepted by
-                    // libcore/libstd before we actually attempt to unwind.
-                    StackPopInfo::StartUnwinding => {
-                        throw_ub_format!("Attempted to start unwinding while already unwinding!");
-                    },
-                    StackPopInfo::StopUnwinding => {
-                        trace!("unwinding: no longer unwinding!");
-                        break;
-                    }
-                    StackPopInfo::Normal => {}
-                }
-
-                match cleanup {
-                    StackPopCleanup::Goto { unwind, .. } if unwind.is_some() => {
-
-                        info!("unwind: found cleanup block {:?}", unwind);
-                        self.goto_block(unwind)?;
-                        break;
-                    },
-                    _ => {}
-                }
-
-                info!("unwinding: popping frame!");
-                let res = self.pop_stack_frame_internal(true)?;
-                cleanup = res.0;
-                stack_pop_info = res.1;
-            }
-            if self.stack.is_empty() {
-                // We should never get here:
-                // The 'start_fn' lang item should always install a panic handler
-                throw_ub!(Unreachable);
-            }
-
-        }
-
         if self.stack.len() > 0 {
-            info!("CONTINUING({}) {}", self.cur_frame(), self.frame().instance);
+            info!("CONTINUING({}) {} (unwinding = {})",
+                self.cur_frame(), self.frame().instance, cur_unwinding);
         }
 
         Ok(())
@@ -833,40 +811,23 @@ pub fn generate_stacktrace(&self, explicit_span: Option<Span>) -> Vec<FrameInfo<
             } else {
                 last_span = Some(span);
             }
-            let block = &body.basic_blocks()[block];
-            let source_info = if stmt < block.statements.len() {
-                block.statements[stmt].source_info
-            } else {
-                block.terminator().source_info
-            };
-            let lint_root = match body.source_scope_local_data {
-                mir::ClearCrossCrate::Set(ref ivs) => Some(ivs[source_info.scope].lint_root),
-                mir::ClearCrossCrate::Clear => None,
-            };
+
+            let lint_root = block.and_then(|block| {
+                let block = &body.basic_blocks()[block];
+                let source_info = if stmt < block.statements.len() {
+                    block.statements[stmt].source_info
+                } else {
+                    block.terminator().source_info
+                };
+                match body.source_scope_local_data {
+                    mir::ClearCrossCrate::Set(ref ivs) => Some(ivs[source_info.scope].lint_root),
+                    mir::ClearCrossCrate::Clear => None,
+                }
+            });
+
             frames.push(FrameInfo { call_site: span, instance, lint_root });
         }
         trace!("generate stacktrace: {:#?}, {:?}", frames, explicit_span);
         frames
     }
-
-    /// Resolve the function at the specified slot in the provided
-    /// vtable. An index of '0' corresponds to the first method
-    /// declared in the trait of the provided vtable
-    pub fn get_vtable_slot(
-        &self,
-        vtable: Scalar<M::PointerTag>,
-        idx: usize
-    ) -> InterpResult<'tcx, FnVal<'tcx, M::ExtraFnVal>> {
-        let ptr_size = self.pointer_size();
-        // Skip over the 'drop_ptr', 'size', and 'align' fields
-        let vtable_slot = vtable.ptr_offset(ptr_size * (idx as u64 + 3), self)?;
-        let vtable_slot = self.memory.check_ptr_access(
-            vtable_slot,
-            ptr_size,
-            self.tcx.data_layout.pointer_align.abi,
-        )?.expect("cannot be a ZST");
-        let fn_ptr = self.memory.get(vtable_slot.alloc_id)?
-            .read_ptr_sized(self, vtable_slot)?.not_undef()?;
-        Ok(self.memory.get_fn(fn_ptr)?)
-    }
 }