]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/interpret/visitor.rs
Rollup merge of #71980 - steveklabnik:warnings-fixes, r=Mark-Simulacrum
[rust.git] / src / librustc_mir / interpret / visitor.rs
1 //! Visitor for a run-time value with a given layout: Traverse enums, structs and other compound
2 //! types until we arrive at the leaves, with custom handling for primitive types.
3
4 use rustc_middle::mir::interpret::InterpResult;
5 use rustc_middle::ty;
6 use rustc_middle::ty::layout::TyAndLayout;
7 use rustc_target::abi::{FieldsShape, VariantIdx, Variants};
8
9 use std::num::NonZeroUsize;
10
11 use super::{InterpCx, MPlaceTy, Machine, OpTy};
12
13 // A thing that we can project into, and that has a layout.
14 // This wouldn't have to depend on `Machine` but with the current type inference,
15 // that's just more convenient to work with (avoids repeating all the `Machine` bounds).
16 pub trait Value<'mir, 'tcx, M: Machine<'mir, 'tcx>>: Copy {
17     /// Gets this value's layout.
18     fn layout(&self) -> TyAndLayout<'tcx>;
19
20     /// Makes this into an `OpTy`.
21     fn to_op(self, ecx: &InterpCx<'mir, 'tcx, M>) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>>;
22
23     /// Creates this from an `MPlaceTy`.
24     fn from_mem_place(mplace: MPlaceTy<'tcx, M::PointerTag>) -> Self;
25
26     /// Projects to the given enum variant.
27     fn project_downcast(
28         self,
29         ecx: &InterpCx<'mir, 'tcx, M>,
30         variant: VariantIdx,
31     ) -> InterpResult<'tcx, Self>;
32
33     /// Projects to the n-th field.
34     fn project_field(self, ecx: &InterpCx<'mir, 'tcx, M>, field: usize)
35     -> InterpResult<'tcx, Self>;
36 }
37
38 // Operands and memory-places are both values.
39 // Places in general are not due to `place_field` having to do `force_allocation`.
40 impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> for OpTy<'tcx, M::PointerTag> {
41     #[inline(always)]
42     fn layout(&self) -> TyAndLayout<'tcx> {
43         self.layout
44     }
45
46     #[inline(always)]
47     fn to_op(
48         self,
49         _ecx: &InterpCx<'mir, 'tcx, M>,
50     ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
51         Ok(self)
52     }
53
54     #[inline(always)]
55     fn from_mem_place(mplace: MPlaceTy<'tcx, M::PointerTag>) -> Self {
56         mplace.into()
57     }
58
59     #[inline(always)]
60     fn project_downcast(
61         self,
62         ecx: &InterpCx<'mir, 'tcx, M>,
63         variant: VariantIdx,
64     ) -> InterpResult<'tcx, Self> {
65         ecx.operand_downcast(self, variant)
66     }
67
68     #[inline(always)]
69     fn project_field(
70         self,
71         ecx: &InterpCx<'mir, 'tcx, M>,
72         field: usize,
73     ) -> InterpResult<'tcx, Self> {
74         ecx.operand_field(self, field)
75     }
76 }
77
78 impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M>
79     for MPlaceTy<'tcx, M::PointerTag>
80 {
81     #[inline(always)]
82     fn layout(&self) -> TyAndLayout<'tcx> {
83         self.layout
84     }
85
86     #[inline(always)]
87     fn to_op(
88         self,
89         _ecx: &InterpCx<'mir, 'tcx, M>,
90     ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
91         Ok(self.into())
92     }
93
94     #[inline(always)]
95     fn from_mem_place(mplace: MPlaceTy<'tcx, M::PointerTag>) -> Self {
96         mplace
97     }
98
99     #[inline(always)]
100     fn project_downcast(
101         self,
102         ecx: &InterpCx<'mir, 'tcx, M>,
103         variant: VariantIdx,
104     ) -> InterpResult<'tcx, Self> {
105         ecx.mplace_downcast(self, variant)
106     }
107
108     #[inline(always)]
109     fn project_field(
110         self,
111         ecx: &InterpCx<'mir, 'tcx, M>,
112         field: usize,
113     ) -> InterpResult<'tcx, Self> {
114         ecx.mplace_field(self, field)
115     }
116 }
117
118 macro_rules! make_value_visitor {
119     ($visitor_trait_name:ident, $($mutability:ident)?) => {
120         // How to traverse a value and what to do when we are at the leaves.
121         pub trait $visitor_trait_name<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized {
122             type V: Value<'mir, 'tcx, M>;
123
124             /// The visitor must have an `InterpCx` in it.
125             fn ecx(&$($mutability)? self)
126                 -> &$($mutability)? InterpCx<'mir, 'tcx, M>;
127
128             // Recursive actions, ready to be overloaded.
129             /// Visits the given value, dispatching as appropriate to more specialized visitors.
130             #[inline(always)]
131             fn visit_value(&mut self, v: Self::V) -> InterpResult<'tcx>
132             {
133                 self.walk_value(v)
134             }
135             /// Visits the given value as a union. No automatic recursion can happen here.
136             #[inline(always)]
137             fn visit_union(&mut self, _v: Self::V, _fields: NonZeroUsize) -> InterpResult<'tcx>
138             {
139                 Ok(())
140             }
141             /// Visits this value as an aggregate, you are getting an iterator yielding
142             /// all the fields (still in an `InterpResult`, you have to do error handling yourself).
143             /// Recurses into the fields.
144             #[inline(always)]
145             fn visit_aggregate(
146                 &mut self,
147                 v: Self::V,
148                 fields: impl Iterator<Item=InterpResult<'tcx, Self::V>>,
149             ) -> InterpResult<'tcx> {
150                 self.walk_aggregate(v, fields)
151             }
152
153             /// Called each time we recurse down to a field of a "product-like" aggregate
154             /// (structs, tuples, arrays and the like, but not enums), passing in old (outer)
155             /// and new (inner) value.
156             /// This gives the visitor the chance to track the stack of nested fields that
157             /// we are descending through.
158             #[inline(always)]
159             fn visit_field(
160                 &mut self,
161                 _old_val: Self::V,
162                 _field: usize,
163                 new_val: Self::V,
164             ) -> InterpResult<'tcx> {
165                 self.visit_value(new_val)
166             }
167             /// Called when recursing into an enum variant.
168             /// This gives the visitor the chance to track the stack of nested fields that
169             /// we are descending through.
170             #[inline(always)]
171             fn visit_variant(
172                 &mut self,
173                 _old_val: Self::V,
174                 _variant: VariantIdx,
175                 new_val: Self::V,
176             ) -> InterpResult<'tcx> {
177                 self.visit_value(new_val)
178             }
179
180             // Default recursors. Not meant to be overloaded.
181             fn walk_aggregate(
182                 &mut self,
183                 v: Self::V,
184                 fields: impl Iterator<Item=InterpResult<'tcx, Self::V>>,
185             ) -> InterpResult<'tcx> {
186                 // Now iterate over it.
187                 for (idx, field_val) in fields.enumerate() {
188                     self.visit_field(v, idx, field_val?)?;
189                 }
190                 Ok(())
191             }
192             fn walk_value(&mut self, v: Self::V) -> InterpResult<'tcx>
193             {
194                 trace!("walk_value: type: {}", v.layout().ty);
195
196                 // Special treatment for special types, where the (static) layout is not sufficient.
197                 match v.layout().ty.kind {
198                     // If it is a trait object, switch to the real type that was used to create it.
199                     ty::Dynamic(..) => {
200                         // immediate trait objects are not a thing
201                         let dest = v.to_op(self.ecx())?.assert_mem_place(self.ecx());
202                         let inner = self.ecx().unpack_dyn_trait(dest)?.1;
203                         trace!("walk_value: dyn object layout: {:#?}", inner.layout);
204                         // recurse with the inner type
205                         return self.visit_field(v, 0, Value::from_mem_place(inner));
206                     },
207                     // Slices do not need special handling here: they have `Array` field
208                     // placement with length 0, so we enter the `Array` case below which
209                     // indirectly uses the metadata to determine the actual length.
210                     _ => {},
211                 };
212
213                 // Visit the fields of this value.
214                 match v.layout().fields {
215                     FieldsShape::Primitive => {},
216                     FieldsShape::Union(fields) => {
217                         self.visit_union(v, fields)?;
218                     },
219                     FieldsShape::Arbitrary { ref offsets, .. } => {
220                         // FIXME: We collect in a vec because otherwise there are lifetime
221                         // errors: Projecting to a field needs access to `ecx`.
222                         let fields: Vec<InterpResult<'tcx, Self::V>> =
223                             (0..offsets.len()).map(|i| {
224                                 v.project_field(self.ecx(), i)
225                             })
226                             .collect();
227                         self.visit_aggregate(v, fields.into_iter())?;
228                     },
229                     FieldsShape::Array { .. } => {
230                         // Let's get an mplace first.
231                         let mplace = v.to_op(self.ecx())?.assert_mem_place(self.ecx());
232                         // Now we can go over all the fields.
233                         // This uses the *run-time length*, i.e., if we are a slice,
234                         // the dynamic info from the metadata is used.
235                         let iter = self.ecx().mplace_array_fields(mplace)?
236                             .map(|f| f.and_then(|f| {
237                                 Ok(Value::from_mem_place(f))
238                             }));
239                         self.visit_aggregate(v, iter)?;
240                     }
241                 }
242
243                 match v.layout().variants {
244                     // If this is a multi-variant layout, find the right variant and proceed
245                     // with *its* fields.
246                     Variants::Multiple { .. } => {
247                         let op = v.to_op(self.ecx())?;
248                         let idx = self.ecx().read_discriminant(op)?.1;
249                         let inner = v.project_downcast(self.ecx(), idx)?;
250                         trace!("walk_value: variant layout: {:#?}", inner.layout());
251                         // recurse with the inner type
252                         self.visit_variant(v, idx, inner)
253                     }
254                     // For single-variant layouts, we already did anything there is to do.
255                     Variants::Single { .. } => Ok(())
256                 }
257             }
258         }
259     }
260 }
261
262 make_value_visitor!(ValueVisitor,);
263 make_value_visitor!(MutValueVisitor, mut);