diff options
| author | Ralf Jung <post@ralfj.de> | 2018-10-31 18:44:00 +0100 |
|---|---|---|
| committer | Ralf Jung <post@ralfj.de> | 2018-11-05 09:17:48 +0100 |
| commit | 7d7bd9b6c24a89ebafd4400a710a868fb3e70242 (patch) | |
| tree | 2d9f76095690b54b90872c4acce0711eef0e2dc4 /src/librustc_mir/interpret/visitor.rs | |
| parent | 5b5e076b473eb31736381f3c2cd73a169a66cbf5 (diff) | |
| download | rust-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.rs | 144 |
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(()) } } |
