]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_const_eval/src/interpret/visitor.rs
:arrow_up: rust-analyzer
[rust.git] / compiler / rustc_const_eval / src / 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, PlaceTy};
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>>: Sized {
17     /// Gets this value's layout.
18     fn layout(&self) -> TyAndLayout<'tcx>;
19
20     /// Makes this into an `OpTy`, in a cheap way that is good for reading.
21     fn to_op_for_read(
22         &self,
23         ecx: &InterpCx<'mir, 'tcx, M>,
24     ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>>;
25
26     /// Makes this into an `OpTy`, in a potentially more expensive way that is good for projections.
27     fn to_op_for_proj(
28         &self,
29         ecx: &InterpCx<'mir, 'tcx, M>,
30     ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
31         self.to_op_for_read(ecx)
32     }
33
34     /// Creates this from an `OpTy`.
35     ///
36     /// If `to_op_for_proj` only ever produces `Indirect` operands, then this one is definitely `Indirect`.
37     fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self;
38
39     /// Projects to the given enum variant.
40     fn project_downcast(
41         &self,
42         ecx: &InterpCx<'mir, 'tcx, M>,
43         variant: VariantIdx,
44     ) -> InterpResult<'tcx, Self>;
45
46     /// Projects to the n-th field.
47     fn project_field(
48         &self,
49         ecx: &InterpCx<'mir, 'tcx, M>,
50         field: usize,
51     ) -> InterpResult<'tcx, Self>;
52 }
53
54 /// A thing that we can project into given *mutable* access to `ecx`, and that has a layout.
55 /// This wouldn't have to depend on `Machine` but with the current type inference,
56 /// that's just more convenient to work with (avoids repeating all the `Machine` bounds).
57 pub trait ValueMut<'mir, 'tcx, M: Machine<'mir, 'tcx>>: Sized {
58     /// Gets this value's layout.
59     fn layout(&self) -> TyAndLayout<'tcx>;
60
61     /// Makes this into an `OpTy`, in a cheap way that is good for reading.
62     fn to_op_for_read(
63         &self,
64         ecx: &InterpCx<'mir, 'tcx, M>,
65     ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>>;
66
67     /// Makes this into an `OpTy`, in a potentially more expensive way that is good for projections.
68     fn to_op_for_proj(
69         &self,
70         ecx: &mut InterpCx<'mir, 'tcx, M>,
71     ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>>;
72
73     /// Creates this from an `OpTy`.
74     ///
75     /// If `to_op_for_proj` only ever produces `Indirect` operands, then this one is definitely `Indirect`.
76     fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self;
77
78     /// Projects to the given enum variant.
79     fn project_downcast(
80         &self,
81         ecx: &mut InterpCx<'mir, 'tcx, M>,
82         variant: VariantIdx,
83     ) -> InterpResult<'tcx, Self>;
84
85     /// Projects to the n-th field.
86     fn project_field(
87         &self,
88         ecx: &mut InterpCx<'mir, 'tcx, M>,
89         field: usize,
90     ) -> InterpResult<'tcx, Self>;
91 }
92
93 // We cannot have a general impl which shows that Value implies ValueMut. (When we do, it says we
94 // cannot `impl ValueMut for PlaceTy` because some downstream crate could `impl Value for PlaceTy`.)
95 // So we have some copy-paste here. (We could have a macro but since we only have 2 types with this
96 // double-impl, that would barely make the code shorter, if at all.)
97
98 impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> for OpTy<'tcx, M::Provenance> {
99     #[inline(always)]
100     fn layout(&self) -> TyAndLayout<'tcx> {
101         self.layout
102     }
103
104     #[inline(always)]
105     fn to_op_for_read(
106         &self,
107         _ecx: &InterpCx<'mir, 'tcx, M>,
108     ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
109         Ok(self.clone())
110     }
111
112     #[inline(always)]
113     fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self {
114         op.clone()
115     }
116
117     #[inline(always)]
118     fn project_downcast(
119         &self,
120         ecx: &InterpCx<'mir, 'tcx, M>,
121         variant: VariantIdx,
122     ) -> InterpResult<'tcx, Self> {
123         ecx.operand_downcast(self, variant)
124     }
125
126     #[inline(always)]
127     fn project_field(
128         &self,
129         ecx: &InterpCx<'mir, 'tcx, M>,
130         field: usize,
131     ) -> InterpResult<'tcx, Self> {
132         ecx.operand_field(self, field)
133     }
134 }
135
136 impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M>
137     for OpTy<'tcx, M::Provenance>
138 {
139     #[inline(always)]
140     fn layout(&self) -> TyAndLayout<'tcx> {
141         self.layout
142     }
143
144     #[inline(always)]
145     fn to_op_for_read(
146         &self,
147         _ecx: &InterpCx<'mir, 'tcx, M>,
148     ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
149         Ok(self.clone())
150     }
151
152     #[inline(always)]
153     fn to_op_for_proj(
154         &self,
155         _ecx: &mut InterpCx<'mir, 'tcx, M>,
156     ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
157         Ok(self.clone())
158     }
159
160     #[inline(always)]
161     fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self {
162         op.clone()
163     }
164
165     #[inline(always)]
166     fn project_downcast(
167         &self,
168         ecx: &mut InterpCx<'mir, 'tcx, M>,
169         variant: VariantIdx,
170     ) -> InterpResult<'tcx, Self> {
171         ecx.operand_downcast(self, variant)
172     }
173
174     #[inline(always)]
175     fn project_field(
176         &self,
177         ecx: &mut InterpCx<'mir, 'tcx, M>,
178         field: usize,
179     ) -> InterpResult<'tcx, Self> {
180         ecx.operand_field(self, field)
181     }
182 }
183
184 impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M>
185     for MPlaceTy<'tcx, M::Provenance>
186 {
187     #[inline(always)]
188     fn layout(&self) -> TyAndLayout<'tcx> {
189         self.layout
190     }
191
192     #[inline(always)]
193     fn to_op_for_read(
194         &self,
195         _ecx: &InterpCx<'mir, 'tcx, M>,
196     ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
197         Ok(self.into())
198     }
199
200     #[inline(always)]
201     fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self {
202         // assert is justified because our `to_op_for_read` only ever produces `Indirect` operands.
203         op.assert_mem_place()
204     }
205
206     #[inline(always)]
207     fn project_downcast(
208         &self,
209         ecx: &InterpCx<'mir, 'tcx, M>,
210         variant: VariantIdx,
211     ) -> InterpResult<'tcx, Self> {
212         ecx.mplace_downcast(self, variant)
213     }
214
215     #[inline(always)]
216     fn project_field(
217         &self,
218         ecx: &InterpCx<'mir, 'tcx, M>,
219         field: usize,
220     ) -> InterpResult<'tcx, Self> {
221         ecx.mplace_field(self, field)
222     }
223 }
224
225 impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M>
226     for MPlaceTy<'tcx, M::Provenance>
227 {
228     #[inline(always)]
229     fn layout(&self) -> TyAndLayout<'tcx> {
230         self.layout
231     }
232
233     #[inline(always)]
234     fn to_op_for_read(
235         &self,
236         _ecx: &InterpCx<'mir, 'tcx, M>,
237     ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
238         Ok(self.into())
239     }
240
241     #[inline(always)]
242     fn to_op_for_proj(
243         &self,
244         _ecx: &mut InterpCx<'mir, 'tcx, M>,
245     ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
246         Ok(self.into())
247     }
248
249     #[inline(always)]
250     fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self {
251         // assert is justified because our `to_op_for_proj` only ever produces `Indirect` operands.
252         op.assert_mem_place()
253     }
254
255     #[inline(always)]
256     fn project_downcast(
257         &self,
258         ecx: &mut InterpCx<'mir, 'tcx, M>,
259         variant: VariantIdx,
260     ) -> InterpResult<'tcx, Self> {
261         ecx.mplace_downcast(self, variant)
262     }
263
264     #[inline(always)]
265     fn project_field(
266         &self,
267         ecx: &mut InterpCx<'mir, 'tcx, M>,
268         field: usize,
269     ) -> InterpResult<'tcx, Self> {
270         ecx.mplace_field(self, field)
271     }
272 }
273
274 impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M>
275     for PlaceTy<'tcx, M::Provenance>
276 {
277     #[inline(always)]
278     fn layout(&self) -> TyAndLayout<'tcx> {
279         self.layout
280     }
281
282     #[inline(always)]
283     fn to_op_for_read(
284         &self,
285         ecx: &InterpCx<'mir, 'tcx, M>,
286     ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
287         // We `force_allocation` here so that `from_op` below can work.
288         ecx.place_to_op(self)
289     }
290
291     #[inline(always)]
292     fn to_op_for_proj(
293         &self,
294         ecx: &mut InterpCx<'mir, 'tcx, M>,
295     ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
296         // We `force_allocation` here so that `from_op` below can work.
297         Ok(ecx.force_allocation(self)?.into())
298     }
299
300     #[inline(always)]
301     fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self {
302         // assert is justified because our `to_op` only ever produces `Indirect` operands.
303         op.assert_mem_place().into()
304     }
305
306     #[inline(always)]
307     fn project_downcast(
308         &self,
309         ecx: &mut InterpCx<'mir, 'tcx, M>,
310         variant: VariantIdx,
311     ) -> InterpResult<'tcx, Self> {
312         ecx.place_downcast(self, variant)
313     }
314
315     #[inline(always)]
316     fn project_field(
317         &self,
318         ecx: &mut InterpCx<'mir, 'tcx, M>,
319         field: usize,
320     ) -> InterpResult<'tcx, Self> {
321         ecx.place_field(self, field)
322     }
323 }
324
325 macro_rules! make_value_visitor {
326     ($visitor_trait:ident, $value_trait:ident, $($mutability:ident)?) => {
327         // How to traverse a value and what to do when we are at the leaves.
328         pub trait $visitor_trait<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized {
329             type V: $value_trait<'mir, 'tcx, M>;
330
331             /// The visitor must have an `InterpCx` in it.
332             fn ecx(&$($mutability)? self)
333                 -> &$($mutability)? InterpCx<'mir, 'tcx, M>;
334
335             /// `read_discriminant` can be hooked for better error messages.
336             #[inline(always)]
337             fn read_discriminant(
338                 &mut self,
339                 op: &OpTy<'tcx, M::Provenance>,
340             ) -> InterpResult<'tcx, VariantIdx> {
341                 Ok(self.ecx().read_discriminant(op)?.1)
342             }
343
344             // Recursive actions, ready to be overloaded.
345             /// Visits the given value, dispatching as appropriate to more specialized visitors.
346             #[inline(always)]
347             fn visit_value(&mut self, v: &Self::V) -> InterpResult<'tcx>
348             {
349                 self.walk_value(v)
350             }
351             /// Visits the given value as a union. No automatic recursion can happen here.
352             #[inline(always)]
353             fn visit_union(&mut self, _v: &Self::V, _fields: NonZeroUsize) -> InterpResult<'tcx>
354             {
355                 Ok(())
356             }
357             /// Visits the given value as the pointer of a `Box`. There is nothing to recurse into.
358             /// The type of `v` will be a raw pointer, but this is a field of `Box<T>` and the
359             /// pointee type is the actual `T`.
360             #[inline(always)]
361             fn visit_box(&mut self, _v: &Self::V) -> InterpResult<'tcx>
362             {
363                 Ok(())
364             }
365             /// Visits this value as an aggregate, you are getting an iterator yielding
366             /// all the fields (still in an `InterpResult`, you have to do error handling yourself).
367             /// Recurses into the fields.
368             #[inline(always)]
369             fn visit_aggregate(
370                 &mut self,
371                 v: &Self::V,
372                 fields: impl Iterator<Item=InterpResult<'tcx, Self::V>>,
373             ) -> InterpResult<'tcx> {
374                 self.walk_aggregate(v, fields)
375             }
376
377             /// Called each time we recurse down to a field of a "product-like" aggregate
378             /// (structs, tuples, arrays and the like, but not enums), passing in old (outer)
379             /// and new (inner) value.
380             /// This gives the visitor the chance to track the stack of nested fields that
381             /// we are descending through.
382             #[inline(always)]
383             fn visit_field(
384                 &mut self,
385                 _old_val: &Self::V,
386                 _field: usize,
387                 new_val: &Self::V,
388             ) -> InterpResult<'tcx> {
389                 self.visit_value(new_val)
390             }
391             /// Called when recursing into an enum variant.
392             /// This gives the visitor the chance to track the stack of nested fields that
393             /// we are descending through.
394             #[inline(always)]
395             fn visit_variant(
396                 &mut self,
397                 _old_val: &Self::V,
398                 _variant: VariantIdx,
399                 new_val: &Self::V,
400             ) -> InterpResult<'tcx> {
401                 self.visit_value(new_val)
402             }
403
404             // Default recursors. Not meant to be overloaded.
405             fn walk_aggregate(
406                 &mut self,
407                 v: &Self::V,
408                 fields: impl Iterator<Item=InterpResult<'tcx, Self::V>>,
409             ) -> InterpResult<'tcx> {
410                 // Now iterate over it.
411                 for (idx, field_val) in fields.enumerate() {
412                     self.visit_field(v, idx, &field_val?)?;
413                 }
414                 Ok(())
415             }
416             fn walk_value(&mut self, v: &Self::V) -> InterpResult<'tcx>
417             {
418                 let ty = v.layout().ty;
419                 trace!("walk_value: type: {ty}");
420
421                 // Special treatment for special types, where the (static) layout is not sufficient.
422                 match *ty.kind() {
423                     // If it is a trait object, switch to the real type that was used to create it.
424                     ty::Dynamic(..) => {
425                         // unsized values are never immediate, so we can assert_mem_place
426                         let op = v.to_op_for_read(self.ecx())?;
427                         let dest = op.assert_mem_place();
428                         let inner_mplace = self.ecx().unpack_dyn_trait(&dest)?;
429                         trace!("walk_value: dyn object layout: {:#?}", inner_mplace.layout);
430                         // recurse with the inner type
431                         return self.visit_field(&v, 0, &$value_trait::from_op(&inner_mplace.into()));
432                     },
433                     // Slices do not need special handling here: they have `Array` field
434                     // placement with length 0, so we enter the `Array` case below which
435                     // indirectly uses the metadata to determine the actual length.
436
437                     // However, `Box`... let's talk about `Box`.
438                     ty::Adt(def, ..) if def.is_box() => {
439                         // `Box` is a hybrid primitive-library-defined type that one the one hand is
440                         // a dereferenceable pointer, on the other hand has *basically arbitrary
441                         // user-defined layout* since the user controls the 'allocator' field. So it
442                         // cannot be treated like a normal pointer, since it does not fit into an
443                         // `Immediate`. Yeah, it is quite terrible. But many visitors want to do
444                         // something with "all boxed pointers", so we handle this mess for them.
445                         //
446                         // When we hit a `Box`, we do not do the usual `visit_aggregate`; instead,
447                         // we (a) call `visit_box` on the pointer value, and (b) recurse on the
448                         // allocator field. We also assert tons of things to ensure we do not miss
449                         // any other fields.
450
451                         // `Box` has two fields: the pointer we care about, and the allocator.
452                         assert_eq!(v.layout().fields.count(), 2, "`Box` must have exactly 2 fields");
453                         let (unique_ptr, alloc) =
454                             (v.project_field(self.ecx(), 0)?, v.project_field(self.ecx(), 1)?);
455                         // Unfortunately there is some type junk in the way here: `unique_ptr` is a `Unique`...
456                         // (which means another 2 fields, the second of which is a `PhantomData`)
457                         assert_eq!(unique_ptr.layout().fields.count(), 2);
458                         let (nonnull_ptr, phantom) = (
459                             unique_ptr.project_field(self.ecx(), 0)?,
460                             unique_ptr.project_field(self.ecx(), 1)?,
461                         );
462                         assert!(
463                             phantom.layout().ty.ty_adt_def().is_some_and(|adt| adt.is_phantom_data()),
464                             "2nd field of `Unique` should be PhantomData but is {:?}",
465                             phantom.layout().ty,
466                         );
467                         // ... that contains a `NonNull`... (gladly, only a single field here)
468                         assert_eq!(nonnull_ptr.layout().fields.count(), 1);
469                         let raw_ptr = nonnull_ptr.project_field(self.ecx(), 0)?; // the actual raw ptr
470                         // ... whose only field finally is a raw ptr we can dereference.
471                         self.visit_box(&raw_ptr)?;
472
473                         // The second `Box` field is the allocator, which we recursively check for validity
474                         // like in regular structs.
475                         self.visit_field(v, 1, &alloc)?;
476
477                         // We visited all parts of this one.
478                         return Ok(());
479                     }
480                     _ => {},
481                 };
482
483                 // Visit the fields of this value.
484                 match v.layout().fields {
485                     FieldsShape::Primitive => {}
486                     FieldsShape::Union(fields) => {
487                         self.visit_union(v, fields)?;
488                     }
489                     FieldsShape::Arbitrary { ref offsets, .. } => {
490                         // FIXME: We collect in a vec because otherwise there are lifetime
491                         // errors: Projecting to a field needs access to `ecx`.
492                         let fields: Vec<InterpResult<'tcx, Self::V>> =
493                             (0..offsets.len()).map(|i| {
494                                 v.project_field(self.ecx(), i)
495                             })
496                             .collect();
497                         self.visit_aggregate(v, fields.into_iter())?;
498                     }
499                     FieldsShape::Array { .. } => {
500                         // Let's get an mplace (or immediate) first.
501                         // This might `force_allocate` if `v` is a `PlaceTy`, but `place_index` does that anyway.
502                         let op = v.to_op_for_proj(self.ecx())?;
503                         // Now we can go over all the fields.
504                         // This uses the *run-time length*, i.e., if we are a slice,
505                         // the dynamic info from the metadata is used.
506                         let iter = self.ecx().operand_array_fields(&op)?
507                             .map(|f| f.and_then(|f| {
508                                 Ok($value_trait::from_op(&f))
509                             }));
510                         self.visit_aggregate(v, iter)?;
511                     }
512                 }
513
514                 match v.layout().variants {
515                     // If this is a multi-variant layout, find the right variant and proceed
516                     // with *its* fields.
517                     Variants::Multiple { .. } => {
518                         let op = v.to_op_for_read(self.ecx())?;
519                         let idx = self.read_discriminant(&op)?;
520                         let inner = v.project_downcast(self.ecx(), idx)?;
521                         trace!("walk_value: variant layout: {:#?}", inner.layout());
522                         // recurse with the inner type
523                         self.visit_variant(v, idx, &inner)
524                     }
525                     // For single-variant layouts, we already did anything there is to do.
526                     Variants::Single { .. } => Ok(())
527                 }
528             }
529         }
530     }
531 }
532
533 make_value_visitor!(ValueVisitor, Value,);
534 make_value_visitor!(MutValueVisitor, ValueMut, mut);