]> git.lizzy.rs Git - rust.git/blobdiff - src/librustc_mir/interpret/eval_context.rs
Simplify `force_allocation_maybe_sized`
[rust.git] / src / librustc_mir / interpret / eval_context.rs
index 8250cadb01d443a1aa2133b59af731d1b327c7ee..5ba9dcd3aa5c8484c9ce7e4e3c329524733919cc 100644 (file)
@@ -2,29 +2,26 @@
 use std::fmt::Write;
 use std::mem;
 
-use syntax::source_map::{self, Span, DUMMY_SP};
-use rustc::ich::StableHashingContext;
-use rustc::hir::def_id::DefId;
 use rustc::hir::def::DefKind;
+use rustc::hir::def_id::DefId;
+use rustc::ich::StableHashingContext;
 use rustc::mir;
-use rustc::ty::layout::{
-    self, Size, Align, HasDataLayout, LayoutOf, TyLayout
+use rustc::mir::interpret::{
+    sign_extend, truncate, AllocId, FrameInfo, GlobalId, InterpResult, Pointer, Scalar,
 };
+use rustc::ty::layout::{self, Align, HasDataLayout, LayoutOf, Size, TyLayout};
+use rustc::ty::query::TyCtxtAt;
 use rustc::ty::subst::SubstsRef;
 use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
-use rustc::ty::query::TyCtxtAt;
-use rustc_index::vec::IndexVec;
-use rustc::mir::interpret::{
-    GlobalId, Scalar, Pointer, FrameInfo, AllocId,
-    InterpResult, truncate, sign_extend,
-};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
+use rustc_index::vec::IndexVec;
 use rustc_macros::HashStable;
+use syntax::source_map::{self, Span, DUMMY_SP};
 
 use super::{
-    Immediate, Operand, MemPlace, MPlaceTy, Place, PlaceTy, ScalarMaybeUndef,
-    Memory, Machine, StackPopInfo
+    Immediate, MPlaceTy, Machine, MemPlace, Memory, Operand, Place, PlaceTy, ScalarMaybeUndef,
+    StackPopInfo,
 };
 
 pub struct InterpCx<'mir, 'tcx, M: Machine<'mir, 'tcx>> {
@@ -50,7 +47,7 @@ pub struct InterpCx<'mir, 'tcx, M: Machine<'mir, 'tcx>> {
 
 /// A stack frame.
 #[derive(Clone)]
-pub struct Frame<'mir, 'tcx, Tag=(), Extra=()> {
+pub struct Frame<'mir, 'tcx, Tag = (), Extra = ()> {
     ////////////////////////////////////////////////////////////////////////////////
     // Function and callsite information
     ////////////////////////////////////////////////////////////////////////////////
@@ -113,7 +110,7 @@ pub enum StackPopCleanup {
 
 /// State of a local variable including a memoized layout
 #[derive(Clone, PartialEq, Eq, HashStable)]
-pub struct LocalState<'tcx, Tag=(), Id=AllocId> {
+pub struct LocalState<'tcx, Tag = (), Id = AllocId> {
     pub value: LocalValue<Tag, Id>,
     /// Don't modify if `Some`, this is only used to prevent computing the layout twice
     #[stable_hasher(ignore)]
@@ -121,8 +118,8 @@ pub struct LocalState<'tcx, Tag=(), Id=AllocId> {
 }
 
 /// Current value of a local variable
-#[derive(Clone, PartialEq, Eq, Debug, HashStable)] // Miri debug-prints these
-pub enum LocalValue<Tag=(), Id=AllocId> {
+#[derive(Copy, Clone, PartialEq, Eq, Debug, HashStable)] // Miri debug-prints these
+pub enum LocalValue<Tag = (), Id = AllocId> {
     /// This local is not currently alive, and cannot be used at all.
     Dead,
     /// This local is alive but not yet initialized. It can be written to
@@ -142,8 +139,9 @@ impl<'tcx, Tag: Copy + 'static> LocalState<'tcx, Tag> {
     pub fn access(&self) -> InterpResult<'tcx, Operand<Tag>> {
         match self.value {
             LocalValue::Dead => throw_unsup!(DeadLocal),
-            LocalValue::Uninitialized =>
-                bug!("The type checker should prevent reading from a never-written local"),
+            LocalValue::Uninitialized => {
+                bug!("The type checker should prevent reading from a never-written local")
+            }
             LocalValue::Live(val) => Ok(val),
         }
     }
@@ -156,10 +154,8 @@ pub fn access_mut(
         match self.value {
             LocalValue::Dead => throw_unsup!(DeadLocal),
             LocalValue::Live(Operand::Indirect(mplace)) => Ok(Err(mplace)),
-            ref mut local @ LocalValue::Live(Operand::Immediate(_)) |
-            ref mut local @ LocalValue::Uninitialized => {
-                Ok(Ok(local))
-            }
+            ref mut local @ LocalValue::Live(Operand::Immediate(_))
+            | ref mut local @ LocalValue::Uninitialized => Ok(Ok(local)),
         }
     }
 }
@@ -245,7 +241,7 @@ pub fn force_ptr(
     pub fn force_bits(
         &self,
         scalar: Scalar<M::PointerTag>,
-        size: Size
+        size: Size,
     ) -> InterpResult<'tcx, u128> {
         self.memory.force_bits(scalar, size)
     }
@@ -312,7 +308,7 @@ pub fn load_mir(
         &self,
         instance: ty::InstanceDef<'tcx>,
         promoted: Option<mir::Promoted>,
-    ) -> InterpResult<'tcx, &'tcx mir::Body<'tcx>> {
+    ) -> InterpResult<'tcx, mir::ReadOnlyBodyAndCache<'tcx, 'tcx>> {
         // do not continue if typeck errors occurred (can only occur in local crate)
         let did = instance.def_id();
         if did.is_local()
@@ -323,14 +319,16 @@ pub fn load_mir(
         }
         trace!("load mir(instance={:?}, promoted={:?})", instance, promoted);
         if let Some(promoted) = promoted {
-            return Ok(&self.tcx.promoted_mir(did)[promoted]);
+            return Ok(self.tcx.promoted_mir(did)[promoted].unwrap_read_only());
         }
         match instance {
-            ty::InstanceDef::Item(def_id) => if self.tcx.is_mir_available(did) {
-                Ok(self.tcx.optimized_mir(did))
-            } else {
-                throw_unsup!(NoMirFor(self.tcx.def_path_str(def_id)))
-            },
+            ty::InstanceDef::Item(def_id) => {
+                if self.tcx.is_mir_available(did) {
+                    Ok(self.tcx.optimized_mir(did).unwrap_read_only())
+                } else {
+                    throw_unsup!(NoMirFor(self.tcx.def_path_str(def_id)))
+                }
+            }
             _ => Ok(self.tcx.instance_mir(instance)),
         }
     }
@@ -352,17 +350,13 @@ pub(super) fn subst_from_frame_and_normalize_erasing_regions<T: TypeFoldable<'tc
     pub(super) fn resolve(
         &self,
         def_id: DefId,
-        substs: SubstsRef<'tcx>
+        substs: SubstsRef<'tcx>,
     ) -> InterpResult<'tcx, ty::Instance<'tcx>> {
         trace!("resolve: {:?}, {:#?}", def_id, substs);
         trace!("param_env: {:#?}", self.param_env);
         trace!("substs: {:#?}", substs);
-        ty::Instance::resolve(
-            *self.tcx,
-            self.param_env,
-            def_id,
-            substs,
-        ).ok_or_else(|| err_inval!(TooGeneric).into())
+        ty::Instance::resolve(*self.tcx, self.param_env, def_id, substs)
+            .ok_or_else(|| err_inval!(TooGeneric).into())
     }
 
     pub fn layout_of_local(
@@ -436,7 +430,7 @@ pub(super) fn size_and_align_of(
                         // of `extern type`, this should be adapted.  It is just a temporary hack
                         // to get some code to work that probably ought to work.
                         if sized_size == Size::ZERO {
-                            return Ok(None)
+                            return Ok(None);
                         } else {
                             bug!("Fields cannot be extern types, unless they are at offset 0")
                         }
@@ -463,8 +457,10 @@ pub(super) fn size_and_align_of(
 
                 // Check if this brought us over the size limit.
                 if size.bytes() >= self.tcx.data_layout().obj_size_bound() {
-                    throw_ub_format!("wide pointer metadata contains invalid information: \
-                        total size is bigger than largest supported object");
+                    throw_ub_format!(
+                        "wide pointer metadata contains invalid information: \
+                        total size is bigger than largest supported object"
+                    );
                 }
                 Ok(Some((size, align)))
             }
@@ -475,19 +471,21 @@ pub(super) fn size_and_align_of(
             }
 
             ty::Slice(_) | ty::Str => {
-                let len = metadata.expect("slice fat ptr must have length").to_machine_usize(self)?;
+                let len =
+                    metadata.expect("slice fat ptr must have length").to_machine_usize(self)?;
                 let elem = layout.field(self, 0)?;
 
                 // Make sure the slice is not too big.
-                let size = elem.size.checked_mul(len, &*self.tcx)
-                    .ok_or_else(|| err_ub_format!("invalid slice: \
-                        total size is bigger than largest supported object"))?;
+                let size = elem.size.checked_mul(len, &*self.tcx).ok_or_else(|| {
+                    err_ub_format!(
+                        "invalid slice: \
+                        total size is bigger than largest supported object"
+                    )
+                })?;
                 Ok(Some((size, elem.align.abi)))
             }
 
-            ty::Foreign(_) => {
-                Ok(None)
-            }
+            ty::Foreign(_) => Ok(None),
 
             _ => bug!("size_and_align_of::<{:?}> not supported", layout.ty),
         }
@@ -495,7 +493,7 @@ pub(super) fn size_and_align_of(
     #[inline]
     pub fn size_and_align_of_mplace(
         &self,
-        mplace: MPlaceTy<'tcx, M::PointerTag>
+        mplace: MPlaceTy<'tcx, M::PointerTag>,
     ) -> InterpResult<'tcx, Option<(Size, Align)>> {
         self.size_and_align_of(mplace.meta, mplace.layout)
     }
@@ -532,10 +530,7 @@ pub fn push_stack_frame(
         // don't allocate at all for trivial constants
         if body.local_decls.len() > 1 {
             // Locals are initially uninitialized.
-            let dummy = LocalState {
-                value: LocalValue::Uninitialized,
-                layout: Cell::new(None),
-            };
+            let dummy = LocalState { value: LocalValue::Uninitialized, layout: Cell::new(None) };
             let mut locals = IndexVec::from_elem(dummy, &body.local_decls);
             // Return place is handled specially by the `eval_place` functions, and the
             // entry in `locals` should never be used. Make it dead, to be sure.
@@ -543,24 +538,21 @@ pub fn push_stack_frame(
             // Now mark those locals as dead that we do not want to initialize
             match self.tcx.def_kind(instance.def_id()) {
                 // statics and constants don't have `Storage*` statements, no need to look for them
-                Some(DefKind::Static)
-                | Some(DefKind::Const)
-                | Some(DefKind::AssocConst) => {},
+                Some(DefKind::Static) | Some(DefKind::Const) | Some(DefKind::AssocConst) => {}
                 _ => {
                     trace!("push_stack_frame: {:?}: num_bbs: {}", span, body.basic_blocks().len());
                     for block in body.basic_blocks() {
                         for stmt in block.statements.iter() {
                             use rustc::mir::StatementKind::{StorageDead, StorageLive};
                             match stmt.kind {
-                                StorageLive(local) |
-                                StorageDead(local) => {
+                                StorageLive(local) | StorageDead(local) => {
                                     locals[local].value = LocalValue::Dead;
                                 }
                                 _ => {}
                             }
                         }
                     }
-                },
+                }
             }
             // done
             self.frame_mut().locals = locals;
@@ -619,26 +611,25 @@ pub fn unwind_to_block(&mut self, target: Option<mir::BasicBlock>) {
     /// `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> {
-        info!("LEAVING({}) {} (unwinding = {})",
-            self.cur_frame(), self.frame().instance, unwinding);
+    pub(super) fn pop_stack_frame(&mut self, unwinding: bool) -> 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
+                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 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, unwinding)?;
         if let (false, StackPopInfo::StopUnwinding) = (unwinding, stack_pop_info) {
             bug!("Attempted to stop unwinding while there is no unwinding!");
@@ -647,11 +638,8 @@ pub(super) fn pop_stack_frame(
         // Now where do we jump next?
 
         // Determine if we leave this function normally or via unwinding.
-        let cur_unwinding = if let StackPopInfo::StopUnwinding = stack_pop_info {
-            false
-        } else {
-            unwinding
-        };
+        let cur_unwinding =
+            if let StackPopInfo::StopUnwinding = stack_pop_info { false } else { unwinding };
 
         // 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,
@@ -659,8 +647,8 @@ pub(super) fn pop_stack_frame(
         let (cleanup, next_block) = match frame.return_to_block {
             StackPopCleanup::Goto { ret, unwind } => {
                 (true, Some(if cur_unwinding { unwind } else { ret }))
-            },
-            StackPopCleanup::None { cleanup, .. } => (cleanup, None)
+            }
+            StackPopCleanup::None { cleanup, .. } => (cleanup, None),
         };
 
         if !cleanup {
@@ -675,9 +663,12 @@ pub(super) fn pop_stack_frame(
             self.deallocate_local(local.value)?;
         }
 
-
-        trace!("StackPopCleanup: {:?} StackPopInfo: {:?} cur_unwinding = {:?}",
-               frame.return_to_block, stack_pop_info, cur_unwinding);
+        trace!(
+            "StackPopCleanup: {:?} StackPopInfo: {:?} cur_unwinding = {:?}",
+            frame.return_to_block,
+            stack_pop_info,
+            cur_unwinding
+        );
         if cur_unwinding {
             // Follow the unwind edge.
             let unwind = next_block.expect("Encounted StackPopCleanup::None when unwinding!");
@@ -695,11 +686,7 @@ pub(super) fn pop_stack_frame(
                     // invariant -- that is, unless a function somehow has a ptr to
                     // its return place... but the way MIR is currently generated, the
                     // return place is always a local and then this cannot happen.
-                    self.validate_operand(
-                        self.place_to_op(return_place)?,
-                        vec![],
-                        None,
-                    )?;
+                    self.validate_operand(self.place_to_op(return_place)?, vec![], None)?;
                 }
             } else {
                 // Uh, that shouldn't happen... the function did not intend to return
@@ -713,8 +700,12 @@ pub(super) fn pop_stack_frame(
         }
 
         if self.stack.len() > 0 {
-            info!("CONTINUING({}) {} (unwinding = {})",
-                self.cur_frame(), self.frame().instance, cur_unwinding);
+            info!(
+                "CONTINUING({}) {} (unwinding = {})",
+                self.cur_frame(),
+                self.frame().instance,
+                cur_unwinding
+            );
         }
 
         Ok(())
@@ -724,7 +715,7 @@ pub(super) fn pop_stack_frame(
     /// Remember to deallocate that!
     pub fn storage_live(
         &mut self,
-        local: mir::Local
+        local: mir::Local,
     ) -> InterpResult<'tcx, LocalValue<M::PointerTag>> {
         assert!(local != mir::RETURN_PLACE, "Cannot make return place live");
         trace!("{:?} is now live", local);
@@ -752,7 +743,9 @@ pub(super) fn deallocate_local(
         // 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 {
             trace!("deallocating local");
-            let ptr = ptr.to_ptr()?;
+            // All locals have a backing allocation, even if the allocation is empty
+            // due to the local having ZST type.
+            let ptr = ptr.assert_ptr();
             if log_enabled!(::log::Level::Trace) {
                 self.memory.dump_alloc(ptr.alloc_id);
             }
@@ -798,21 +791,22 @@ pub fn dump_place(&self, place: Place<M::PointerTag>) {
                 match self.stack[frame].locals[local].value {
                     LocalValue::Dead => write!(msg, " is dead").unwrap(),
                     LocalValue::Uninitialized => write!(msg, " is uninitialized").unwrap(),
-                    LocalValue::Live(Operand::Indirect(mplace)) => {
-                        match mplace.ptr {
-                            Scalar::Ptr(ptr) => {
-                                write!(msg, " by align({}){} ref:",
-                                    mplace.align.bytes(),
-                                    match mplace.meta {
-                                        Some(meta) => format!(" meta({:?})", meta),
-                                        None => String::new()
-                                    }
-                                ).unwrap();
-                                allocs.push(ptr.alloc_id);
-                            }
-                            ptr => write!(msg, " by integral ref: {:?}", ptr).unwrap(),
+                    LocalValue::Live(Operand::Indirect(mplace)) => match mplace.ptr {
+                        Scalar::Ptr(ptr) => {
+                            write!(
+                                msg,
+                                " by align({}){} ref:",
+                                mplace.align.bytes(),
+                                match mplace.meta {
+                                    Some(meta) => format!(" meta({:?})", meta),
+                                    None => String::new(),
+                                }
+                            )
+                            .unwrap();
+                            allocs.push(ptr.alloc_id);
                         }
-                    }
+                        ptr => write!(msg, " by integral ref: {:?}", ptr).unwrap(),
+                    },
                     LocalValue::Live(Operand::Immediate(Immediate::Scalar(val))) => {
                         write!(msg, " {:?}", val).unwrap();
                         if let ScalarMaybeUndef::Scalar(Scalar::Ptr(ptr)) = val {
@@ -833,15 +827,13 @@ pub fn dump_place(&self, place: Place<M::PointerTag>) {
                 trace!("{}", msg);
                 self.memory.dump_allocs(allocs);
             }
-            Place::Ptr(mplace) => {
-                match mplace.ptr {
-                    Scalar::Ptr(ptr) => {
-                        trace!("by align({}) ref:", mplace.align.bytes());
-                        self.memory.dump_alloc(ptr.alloc_id);
-                    }
-                    ptr => trace!(" integral by ref: {:?}", ptr),
+            Place::Ptr(mplace) => match mplace.ptr {
+                Scalar::Ptr(ptr) => {
+                    trace!("by align({}) ref:", mplace.align.bytes());
+                    self.memory.dump_alloc(ptr.alloc_id);
                 }
-            }
+                ptr => trace!(" integral by ref: {:?}", ptr),
+            },
         }
     }
 
@@ -877,9 +869,10 @@ pub fn generate_stacktrace(&self, explicit_span: Option<Span>) -> Vec<FrameInfo<
 }
 
 impl<'ctx, 'mir, 'tcx, Tag, Extra> HashStable<StableHashingContext<'ctx>>
-for Frame<'mir, 'tcx, Tag, Extra>
-    where Extra: HashStable<StableHashingContext<'ctx>>,
-          Tag:   HashStable<StableHashingContext<'ctx>>
+    for Frame<'mir, 'tcx, Tag, Extra>
+where
+    Extra: HashStable<StableHashingContext<'ctx>>,
+    Tag: HashStable<StableHashingContext<'ctx>>,
 {
     fn hash_stable(&self, hcx: &mut StableHashingContext<'ctx>, hasher: &mut StableHasher) {
         self.body.hash_stable(hcx, hasher);