about summary refs log tree commit diff
path: root/src/librustc_mir/interpret/visitor.rs
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2018-10-31 18:44:00 +0100
committerRalf Jung <post@ralfj.de>2018-11-05 09:17:48 +0100
commit7d7bd9b6c24a89ebafd4400a710a868fb3e70242 (patch)
tree2d9f76095690b54b90872c4acce0711eef0e2dc4 /src/librustc_mir/interpret/visitor.rs
parent5b5e076b473eb31736381f3c2cd73a169a66cbf5 (diff)
downloadrust-7d7bd9b6c24a89ebafd4400a710a868fb3e70242.tar.gz
rust-7d7bd9b6c24a89ebafd4400a710a868fb3e70242.zip
reduce the amount of traversal/projection code that the visitor has to implement itself
Diffstat (limited to 'src/librustc_mir/interpret/visitor.rs')
-rw-r--r--src/librustc_mir/interpret/visitor.rs144
1 files changed, 117 insertions, 27 deletions
diff --git a/src/librustc_mir/interpret/visitor.rs b/src/librustc_mir/interpret/visitor.rs
index 892eaceff1b..7d6029d6424 100644
--- a/src/librustc_mir/interpret/visitor.rs
+++ b/src/librustc_mir/interpret/visitor.rs
@@ -10,34 +10,103 @@ use rustc::mir::interpret::{
 };
 
 use super::{
-    Machine, EvalContext,
+    Machine, EvalContext, MPlaceTy, OpTy,
 };
 
-// How to traverse a value and what to do when we are at the leaves.
-// In the future, we might want to turn this into two traits, but so far the
-// only implementations we have couldn't share any code anyway.
-pub trait ValueVisitor<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>>: fmt::Debug {
+// A thing that we can project into, and that has a layout.
+// This wouldn't have to depend on `Machine` but with the current type inference,
+// that's just more convenient to work with (avoids repeading all the `Machine` bounds).
+pub trait Value<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>>: Copy
+{
     // Get this value's layout.
     fn layout(&self) -> TyLayout<'tcx>;
 
-    // Downcast functions.  These change the value as a side-effect.
+    // Get the underlying `MPlaceTy`, or panic if there is no such thing.
+    fn to_mem_place(self) -> MPlaceTy<'tcx, M::PointerTag>;
+
+    // Create this from an `MPlaceTy`
+    fn from_mem_place(MPlaceTy<'tcx, M::PointerTag>) -> Self;
+
+    // Project to the n-th field
+    fn project_field(
+        self,
+        ectx: &mut EvalContext<'a, 'mir, 'tcx, M>,
+        field: u64,
+    ) -> EvalResult<'tcx, Self>;
+}
+
+// Operands and places are both values
+impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Value<'a, 'mir, 'tcx, M>
+    for OpTy<'tcx, M::PointerTag>
+{
+    #[inline(always)]
+    fn layout(&self) -> TyLayout<'tcx> {
+        self.layout
+    }
+
+    #[inline(always)]
+    fn to_mem_place(self) -> MPlaceTy<'tcx, M::PointerTag> {
+        self.to_mem_place()
+    }
+
+    #[inline(always)]
+    fn from_mem_place(mplace: MPlaceTy<'tcx, M::PointerTag>) -> Self {
+        mplace.into()
+    }
+
+    #[inline(always)]
+    fn project_field(
+        self,
+        ectx: &mut EvalContext<'a, 'mir, 'tcx, M>,
+        field: u64,
+    ) -> EvalResult<'tcx, Self> {
+        ectx.operand_field(self, field)
+    }
+}
+
+// How to traverse a value and what to do when we are at the leaves.
+pub trait ValueVisitor<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>>: fmt::Debug {
+    type V: Value<'a, 'mir, 'tcx, M>;
+
+    // There's a value in here.
+    fn value(&self) -> &Self::V;
+
+    // The value's layout (not meant to be overwritten).
+    #[inline(always)]
+    fn layout(&self) -> TyLayout<'tcx> {
+        self.value().layout()
+    }
+
+    // Replace the value by `val`, which must be the `field`th field of `self`,
+    // then call `f` and then un-do everything that might have happened to the visitor state.
+    // The point of this is that some visitors keep a stack of fields that we projected below,
+    // and this lets us avoid copying that stack; instead they will pop the stack after
+    // executing `f`.
+    fn with_field(
+        &mut self,
+        val: Self::V,
+        field: usize,
+        f: impl FnOnce(&mut Self) -> EvalResult<'tcx>,
+    ) -> EvalResult<'tcx>;
+
+    // This is an enum, downcast it to whatever the current variant is.
+    // (We do this here and not in `Value` to keep error handling
+    // under control of th visitor.)
     fn downcast_enum(&mut self, ectx: &EvalContext<'a, 'mir, 'tcx, M>)
         -> EvalResult<'tcx>;
-    fn downcast_dyn_trait(&mut self, ectx: &EvalContext<'a, 'mir, 'tcx, M>)
-        -> EvalResult<'tcx>;
 
-    // Visit all fields of a compound.
-    // Just call `visit_value` if you want to go on recursively.
-    fn visit_fields(&mut self, ectx: &mut EvalContext<'a, 'mir, 'tcx, M>, num_fields: usize)
-        -> EvalResult<'tcx>;
-    // Optimized handling for arrays -- avoid computing the layout for every field.
-    // Also it is the value's responsibility to figure out the length.
-    fn visit_array(&mut self, ectx: &mut EvalContext<'a, 'mir, 'tcx, M>) -> EvalResult<'tcx>;
-    // Special handling for strings.
-    fn visit_str(&mut self, ectx: &mut EvalContext<'a, 'mir, 'tcx, M>)
-        -> EvalResult<'tcx>;
+    // A chance for the visitor to do special (different or more efficient) handling for some
+    // array types.  Return `true` if the value was handled and we should return.
+    #[inline]
+    fn handle_array(&mut self, _ectx: &EvalContext<'a, 'mir, 'tcx, M>)
+        -> EvalResult<'tcx, bool>
+    {
+        Ok(false)
+    }
 
     // Actions on the leaves.
+    fn visit_uninhabited(&mut self, ectx: &mut EvalContext<'a, 'mir, 'tcx, M>)
+        -> EvalResult<'tcx>;
     fn visit_scalar(&mut self, ectx: &mut EvalContext<'a, 'mir, 'tcx, M>, layout: &layout::Scalar)
         -> EvalResult<'tcx>;
     fn visit_primitive(&mut self, ectx: &mut EvalContext<'a, 'mir, 'tcx, M>)
@@ -45,7 +114,10 @@ pub trait ValueVisitor<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>>: fmt::Debug {
 }
 
 impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
-    pub fn visit_value<V: ValueVisitor<'a, 'mir, 'tcx, M>>(&mut self, v: &mut V) -> EvalResult<'tcx> {
+    pub fn visit_value<V: ValueVisitor<'a, 'mir, 'tcx, M>>(
+        &mut self,
+        v: &mut V,
+    ) -> EvalResult<'tcx> {
         trace!("visit_value: {:?}", v);
 
         // If this is a multi-variant layout, we have find the right one and proceed with that.
@@ -63,7 +135,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
         // If it is a trait object, switch to the actual type that was used to create it.
         match v.layout().ty.sty {
             ty::Dynamic(..) => {
-                v.downcast_dyn_trait(self)?;
+                let dest = v.value().to_mem_place(); // immediate trait objects are not a thing
+                let inner = self.unpack_dyn_trait(dest)?.1;
+                // recurse with the inner type
+                return v.with_field(Value::from_mem_place(inner), 0, |v| self.visit_value(v));
             },
             _ => {},
         };
@@ -76,6 +151,9 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
         // scalars, we do the same check on every "level" (e.g. first we check
         // MyNewtype and then the scalar in there).
         match v.layout().abi {
+            layout::Abi::Uninhabited => {
+                v.visit_uninhabited(self)?;
+            }
             layout::Abi::Scalar(ref layout) => {
                 v.visit_scalar(self, layout)?;
             }
@@ -107,19 +185,31 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
                 // We can't traverse unions, their bits are allowed to be anything.
                 // The fields don't need to correspond to any bit pattern of the union's fields.
                 // See https://github.com/rust-lang/rust/issues/32836#issuecomment-406875389
-                Ok(())
             },
             layout::FieldPlacement::Arbitrary { ref offsets, .. } => {
-                v.visit_fields(self, offsets.len())
+                for i in 0..offsets.len() {
+                    let val = v.value().project_field(self, i as u64)?;
+                    v.with_field(val, i, |v| self.visit_value(v))?;
+                }
             },
             layout::FieldPlacement::Array { .. } => {
-                match v.layout().ty.sty {
-                    // Strings have properties that cannot be expressed pointwise.
-                    ty::Str => v.visit_str(self),
-                    // General case -- might also be SIMD vector or so
-                    _ => v.visit_array(self),
+                if !v.handle_array(self)? {
+                    // We still have to work!
+                    // Let's get an mplace first.
+                    let mplace = if v.layout().is_zst() {
+                        // it's a ZST, the memory content cannot matter
+                        MPlaceTy::dangling(v.layout(), self)
+                    } else {
+                        // non-ZST array/slice/str cannot be immediate
+                        v.value().to_mem_place()
+                    };
+                    // Now iterate over it.
+                    for (i, field) in self.mplace_array_fields(mplace)?.enumerate() {
+                        v.with_field(Value::from_mem_place(field?), i, |v| self.visit_value(v))?;
+                    }
                 }
             }
         }
+        Ok(())
     }
 }