]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/interpret/visitor.rs
Auto merge of #56215 - pietroalbini:rollup, r=pietroalbini
[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::ty::layout::{self, TyLayout, VariantIdx};
5 use rustc::ty;
6 use rustc::mir::interpret::{
7     EvalResult,
8 };
9
10 use super::{
11     Machine, EvalContext, MPlaceTy, OpTy,
12 };
13
14 // A thing that we can project into, and that has a layout.
15 // This wouldn't have to depend on `Machine` but with the current type inference,
16 // that's just more convenient to work with (avoids repeating all the `Machine` bounds).
17 pub trait Value<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>>: Copy
18 {
19     /// Get this value's layout.
20     fn layout(&self) -> TyLayout<'tcx>;
21
22     /// Make this into an `OpTy`.
23     fn to_op(
24         self,
25         ecx: &EvalContext<'a, 'mir, 'tcx, M>,
26     ) -> EvalResult<'tcx, OpTy<'tcx, M::PointerTag>>;
27
28     /// Create this from an `MPlaceTy`.
29     fn from_mem_place(MPlaceTy<'tcx, M::PointerTag>) -> Self;
30
31     /// Project to the given enum variant.
32     fn project_downcast(
33         self,
34         ecx: &EvalContext<'a, 'mir, 'tcx, M>,
35         variant: VariantIdx,
36     ) -> EvalResult<'tcx, Self>;
37
38     /// Project to the n-th field.
39     fn project_field(
40         self,
41         ecx: &EvalContext<'a, 'mir, 'tcx, M>,
42         field: u64,
43     ) -> EvalResult<'tcx, Self>;
44 }
45
46 // Operands and memory-places are both values.
47 // Places in general are not due to `place_field` having to do `force_allocation`.
48 impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Value<'a, 'mir, 'tcx, M>
49     for OpTy<'tcx, M::PointerTag>
50 {
51     #[inline(always)]
52     fn layout(&self) -> TyLayout<'tcx> {
53         self.layout
54     }
55
56     #[inline(always)]
57     fn to_op(
58         self,
59         _ecx: &EvalContext<'a, 'mir, 'tcx, M>,
60     ) -> EvalResult<'tcx, OpTy<'tcx, M::PointerTag>> {
61         Ok(self)
62     }
63
64     #[inline(always)]
65     fn from_mem_place(mplace: MPlaceTy<'tcx, M::PointerTag>) -> Self {
66         mplace.into()
67     }
68
69     #[inline(always)]
70     fn project_downcast(
71         self,
72         ecx: &EvalContext<'a, 'mir, 'tcx, M>,
73         variant: VariantIdx,
74     ) -> EvalResult<'tcx, Self> {
75         ecx.operand_downcast(self, variant)
76     }
77
78     #[inline(always)]
79     fn project_field(
80         self,
81         ecx: &EvalContext<'a, 'mir, 'tcx, M>,
82         field: u64,
83     ) -> EvalResult<'tcx, Self> {
84         ecx.operand_field(self, field)
85     }
86 }
87 impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Value<'a, 'mir, 'tcx, M>
88     for MPlaceTy<'tcx, M::PointerTag>
89 {
90     #[inline(always)]
91     fn layout(&self) -> TyLayout<'tcx> {
92         self.layout
93     }
94
95     #[inline(always)]
96     fn to_op(
97         self,
98         _ecx: &EvalContext<'a, 'mir, 'tcx, M>,
99     ) -> EvalResult<'tcx, OpTy<'tcx, M::PointerTag>> {
100         Ok(self.into())
101     }
102
103     #[inline(always)]
104     fn from_mem_place(mplace: MPlaceTy<'tcx, M::PointerTag>) -> Self {
105         mplace
106     }
107
108     #[inline(always)]
109     fn project_downcast(
110         self,
111         ecx: &EvalContext<'a, 'mir, 'tcx, M>,
112         variant: VariantIdx,
113     ) -> EvalResult<'tcx, Self> {
114         ecx.mplace_downcast(self, variant)
115     }
116
117     #[inline(always)]
118     fn project_field(
119         self,
120         ecx: &EvalContext<'a, 'mir, 'tcx, M>,
121         field: u64,
122     ) -> EvalResult<'tcx, Self> {
123         ecx.mplace_field(self, field)
124     }
125 }
126
127 macro_rules! make_value_visitor {
128     ($visitor_trait_name:ident, $($mutability:ident)*) => {
129         // How to traverse a value and what to do when we are at the leaves.
130         pub trait $visitor_trait_name<'a, 'mir, 'tcx: 'mir+'a, M: Machine<'a, 'mir, 'tcx>>: Sized {
131             type V: Value<'a, 'mir, 'tcx, M>;
132
133             /// The visitor must have an `EvalContext` in it.
134             fn ecx(&$($mutability)* self)
135                 -> &$($mutability)* EvalContext<'a, 'mir, 'tcx, M>;
136
137             // Recursive actions, ready to be overloaded.
138             /// Visit the given value, dispatching as appropriate to more specialized visitors.
139             #[inline(always)]
140             fn visit_value(&mut self, v: Self::V) -> EvalResult<'tcx>
141             {
142                 self.walk_value(v)
143             }
144             /// Visit the given value as a union.  No automatic recursion can happen here.
145             #[inline(always)]
146             fn visit_union(&mut self, _v: Self::V) -> EvalResult<'tcx>
147             {
148                 Ok(())
149             }
150             /// Visit this vale as an aggregate, you are even getting an iterator yielding
151             /// all the fields (still in an `EvalResult`, you have to do error handling yourself).
152             /// Recurses into the fields.
153             #[inline(always)]
154             fn visit_aggregate(
155                 &mut self,
156                 v: Self::V,
157                 fields: impl Iterator<Item=EvalResult<'tcx, Self::V>>,
158             ) -> EvalResult<'tcx> {
159                 self.walk_aggregate(v, fields)
160             }
161
162             /// Called each time we recurse down to a field of a "product-like" aggregate
163             /// (structs, tuples, arrays and the like, but not enums), passing in old and new value.
164             /// This gives the visitor the chance to track the stack of nested fields that
165             /// we are descending through.
166             #[inline(always)]
167             fn visit_field(
168                 &mut self,
169                 _old_val: Self::V,
170                 _field: usize,
171                 new_val: Self::V,
172             ) -> EvalResult<'tcx> {
173                 self.visit_value(new_val)
174             }
175
176             /// Called for recursing into the field of a generator.  These are not known to be
177             /// initialized, so we treat them like unions.
178             #[inline(always)]
179             fn visit_generator_field(
180                 &mut self,
181                 _old_val: Self::V,
182                 _field: usize,
183                 new_val: Self::V,
184             ) -> EvalResult<'tcx> {
185                 self.visit_union(new_val)
186             }
187
188             /// Called when recursing into an enum variant.
189             #[inline(always)]
190             fn visit_variant(
191                 &mut self,
192                 _old_val: Self::V,
193                 _variant: VariantIdx,
194                 new_val: Self::V,
195             ) -> EvalResult<'tcx> {
196                 self.visit_value(new_val)
197             }
198
199             /// Called whenever we reach a value with uninhabited layout.
200             /// Recursing to fields will *always* continue after this!  This is not meant to control
201             /// whether and how we descend recursively/ into the scalar's fields if there are any,
202             /// it is meant to provide the chance for additional checks when a value of uninhabited
203             /// layout is detected.
204             #[inline(always)]
205             fn visit_uninhabited(&mut self) -> EvalResult<'tcx>
206             { Ok(()) }
207             /// Called whenever we reach a value with scalar layout.
208             /// We do NOT provide a `ScalarMaybeUndef` here to avoid accessing memory if the
209             /// visitor is not even interested in scalars.
210             /// Recursing to fields will *always* continue after this!  This is not meant to control
211             /// whether and how we descend recursively/ into the scalar's fields if there are any,
212             /// it is meant to provide the chance for additional checks when a value of scalar
213             /// layout is detected.
214             #[inline(always)]
215             fn visit_scalar(&mut self, _v: Self::V, _layout: &layout::Scalar) -> EvalResult<'tcx>
216             { Ok(()) }
217
218             /// Called whenever we reach a value of primitive type.  There can be no recursion
219             /// below such a value.  This is the leaf function.
220             /// We do *not* provide an `ImmTy` here because some implementations might want
221             /// to write to the place this primitive lives in.
222             #[inline(always)]
223             fn visit_primitive(&mut self, _v: Self::V) -> EvalResult<'tcx>
224             { Ok(()) }
225
226             // Default recursors. Not meant to be overloaded.
227             fn walk_aggregate(
228                 &mut self,
229                 v: Self::V,
230                 fields: impl Iterator<Item=EvalResult<'tcx, Self::V>>,
231             ) -> EvalResult<'tcx> {
232                 // Now iterate over it.
233                 for (idx, field_val) in fields.enumerate() {
234                     self.visit_field(v, idx, field_val?)?;
235                 }
236                 Ok(())
237             }
238             fn walk_value(&mut self, v: Self::V) -> EvalResult<'tcx>
239             {
240                 trace!("walk_value: type: {}", v.layout().ty);
241                 // If this is a multi-variant layout, we have find the right one and proceed with
242                 // that.
243                 match v.layout().variants {
244                     layout::Variants::NicheFilling { .. } |
245                     layout::Variants::Tagged { .. } => {
246                         let op = v.to_op(self.ecx())?;
247                         let idx = self.ecx().read_discriminant(op)?.1;
248                         let inner = v.project_downcast(self.ecx(), idx)?;
249                         trace!("walk_value: variant layout: {:#?}", inner.layout());
250                         // recurse with the inner type
251                         return self.visit_variant(v, idx, inner);
252                     }
253                     layout::Variants::Single { .. } => {}
254                 }
255
256                 // Even for single variants, we might be able to get a more refined type:
257                 // If it is a trait object, switch to the actual type that was used to create it.
258                 match v.layout().ty.sty {
259                     ty::Dynamic(..) => {
260                         // immediate trait objects are not a thing
261                         let dest = v.to_op(self.ecx())?.to_mem_place();
262                         let inner = self.ecx().unpack_dyn_trait(dest)?.1;
263                         trace!("walk_value: dyn object layout: {:#?}", inner.layout);
264                         // recurse with the inner type
265                         return self.visit_field(v, 0, Value::from_mem_place(inner));
266                     },
267                     _ => {},
268                 };
269
270                 // If this is a scalar, visit it as such.
271                 // Things can be aggregates and have scalar layout at the same time, and that
272                 // is very relevant for `NonNull` and similar structs: We need to visit them
273                 // at their scalar layout *before* descending into their fields.
274                 // FIXME: We could avoid some redundant checks here. For newtypes wrapping
275                 // scalars, we do the same check on every "level" (e.g. first we check
276                 // MyNewtype and then the scalar in there).
277                 match v.layout().abi {
278                     layout::Abi::Uninhabited => {
279                         self.visit_uninhabited()?;
280                     }
281                     layout::Abi::Scalar(ref layout) => {
282                         self.visit_scalar(v, layout)?;
283                     }
284                     // FIXME: Should we do something for ScalarPair? Vector?
285                     _ => {}
286                 }
287
288                 // Check primitive types.  We do this after checking the scalar layout,
289                 // just to have that done as well.  Primitives can have varying layout,
290                 // so we check them separately and before aggregate handling.
291                 // It is CRITICAL that we get this check right, or we might be
292                 // validating the wrong thing!
293                 let primitive = match v.layout().fields {
294                     // Primitives appear as Union with 0 fields - except for Boxes and fat pointers.
295                     layout::FieldPlacement::Union(0) => true,
296                     _ => v.layout().ty.builtin_deref(true).is_some(),
297                 };
298                 if primitive {
299                     return self.visit_primitive(v);
300                 }
301
302                 // Proceed into the fields.
303                 match v.layout().fields {
304                     layout::FieldPlacement::Union(fields) => {
305                         // Empty unions are not accepted by rustc. That's great, it means we can
306                         // use that as an unambiguous signal for detecting primitives.  Make sure
307                         // we did not miss any primitive.
308                         debug_assert!(fields > 0);
309                         self.visit_union(v)
310                     },
311                     layout::FieldPlacement::Arbitrary { ref offsets, .. } => {
312                         // Special handling needed for generators: All but the first field
313                         // (which is the state) are actually implicitly `MaybeUninit`, i.e.,
314                         // they may or may not be initialized, so we cannot visit them.
315                         match v.layout().ty.sty {
316                             ty::Generator(..) => {
317                                 let field = v.project_field(self.ecx(), 0)?;
318                                 self.visit_aggregate(v, std::iter::once(Ok(field)))?;
319                                 for i in 1..offsets.len() {
320                                     let field = v.project_field(self.ecx(), i as u64)?;
321                                     self.visit_generator_field(v, i, field)?;
322                                 }
323                                 Ok(())
324                             }
325                             _ => {
326                                 // FIXME: We collect in a vec because otherwise there are lifetime
327                                 // errors: Projecting to a field needs access to `ecx`.
328                                 let fields: Vec<EvalResult<'tcx, Self::V>> =
329                                     (0..offsets.len()).map(|i| {
330                                         v.project_field(self.ecx(), i as u64)
331                                     })
332                                     .collect();
333                                 self.visit_aggregate(v, fields.into_iter())
334                             }
335                         }
336                     },
337                     layout::FieldPlacement::Array { .. } => {
338                         // Let's get an mplace first.
339                         let mplace = if v.layout().is_zst() {
340                             // it's a ZST, the memory content cannot matter
341                             MPlaceTy::dangling(v.layout(), self.ecx())
342                         } else {
343                             // non-ZST array/slice/str cannot be immediate
344                             v.to_op(self.ecx())?.to_mem_place()
345                         };
346                         // Now we can go over all the fields.
347                         let iter = self.ecx().mplace_array_fields(mplace)?
348                             .map(|f| f.and_then(|f| {
349                                 Ok(Value::from_mem_place(f))
350                             }));
351                         self.visit_aggregate(v, iter)
352                     }
353                 }
354             }
355         }
356     }
357 }
358
359 make_value_visitor!(ValueVisitor,);
360 make_value_visitor!(MutValueVisitor,mut);