]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_const_eval/src/interpret/place.rs
Rollup merge of #101101 - RalfJung:read-pointer-as-bytes, r=oli-obk
[rust.git] / compiler / rustc_const_eval / src / interpret / place.rs
1 //! Computations on places -- field projections, going from mir::Place, and writing
2 //! into a place.
3 //! All high-level functions to write to memory work on places as destinations.
4
5 use rustc_ast::Mutability;
6 use rustc_middle::mir;
7 use rustc_middle::ty;
8 use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt, TyAndLayout};
9 use rustc_target::abi::{self, Abi, Align, HasDataLayout, Size, TagEncoding, VariantIdx};
10
11 use super::{
12     alloc_range, mir_assign_valid_types, AllocId, AllocRef, AllocRefMut, CheckInAllocMsg,
13     ConstAlloc, ImmTy, Immediate, InterpCx, InterpResult, Machine, MemoryKind, OpTy, Operand,
14     Pointer, Provenance, Scalar,
15 };
16
17 #[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
18 /// Information required for the sound usage of a `MemPlace`.
19 pub enum MemPlaceMeta<Prov: Provenance = AllocId> {
20     /// The unsized payload (e.g. length for slices or vtable pointer for trait objects).
21     Meta(Scalar<Prov>),
22     /// `Sized` types or unsized `extern type`
23     None,
24 }
25
26 impl<Prov: Provenance> MemPlaceMeta<Prov> {
27     pub fn unwrap_meta(self) -> Scalar<Prov> {
28         match self {
29             Self::Meta(s) => s,
30             Self::None => {
31                 bug!("expected wide pointer extra data (e.g. slice length or trait object vtable)")
32             }
33         }
34     }
35
36     pub fn has_meta(self) -> bool {
37         match self {
38             Self::Meta(_) => true,
39             Self::None => false,
40         }
41     }
42 }
43
44 #[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
45 pub struct MemPlace<Prov: Provenance = AllocId> {
46     /// The pointer can be a pure integer, with the `None` provenance.
47     pub ptr: Pointer<Option<Prov>>,
48     /// Metadata for unsized places. Interpretation is up to the type.
49     /// Must not be present for sized types, but can be missing for unsized types
50     /// (e.g., `extern type`).
51     pub meta: MemPlaceMeta<Prov>,
52 }
53
54 /// A MemPlace with its layout. Constructing it is only possible in this module.
55 #[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)]
56 pub struct MPlaceTy<'tcx, Prov: Provenance = AllocId> {
57     mplace: MemPlace<Prov>,
58     pub layout: TyAndLayout<'tcx>,
59     /// rustc does not have a proper way to represent the type of a field of a `repr(packed)` struct:
60     /// it needs to have a different alignment than the field type would usually have.
61     /// So we represent this here with a separate field that "overwrites" `layout.align`.
62     /// This means `layout.align` should never be used for a `MPlaceTy`!
63     pub align: Align,
64 }
65
66 #[derive(Copy, Clone, Debug)]
67 pub enum Place<Prov: Provenance = AllocId> {
68     /// A place referring to a value allocated in the `Memory` system.
69     Ptr(MemPlace<Prov>),
70
71     /// To support alloc-free locals, we are able to write directly to a local.
72     /// (Without that optimization, we'd just always be a `MemPlace`.)
73     Local { frame: usize, local: mir::Local },
74 }
75
76 #[derive(Clone, Debug)]
77 pub struct PlaceTy<'tcx, Prov: Provenance = AllocId> {
78     place: Place<Prov>, // Keep this private; it helps enforce invariants.
79     pub layout: TyAndLayout<'tcx>,
80     /// rustc does not have a proper way to represent the type of a field of a `repr(packed)` struct:
81     /// it needs to have a different alignment than the field type would usually have.
82     /// So we represent this here with a separate field that "overwrites" `layout.align`.
83     /// This means `layout.align` should never be used for a `PlaceTy`!
84     pub align: Align,
85 }
86
87 impl<'tcx, Prov: Provenance> std::ops::Deref for PlaceTy<'tcx, Prov> {
88     type Target = Place<Prov>;
89     #[inline(always)]
90     fn deref(&self) -> &Place<Prov> {
91         &self.place
92     }
93 }
94
95 impl<'tcx, Prov: Provenance> std::ops::Deref for MPlaceTy<'tcx, Prov> {
96     type Target = MemPlace<Prov>;
97     #[inline(always)]
98     fn deref(&self) -> &MemPlace<Prov> {
99         &self.mplace
100     }
101 }
102
103 impl<'tcx, Prov: Provenance> From<MPlaceTy<'tcx, Prov>> for PlaceTy<'tcx, Prov> {
104     #[inline(always)]
105     fn from(mplace: MPlaceTy<'tcx, Prov>) -> Self {
106         PlaceTy { place: Place::Ptr(*mplace), layout: mplace.layout, align: mplace.align }
107     }
108 }
109
110 impl<'tcx, Prov: Provenance> From<&'_ MPlaceTy<'tcx, Prov>> for PlaceTy<'tcx, Prov> {
111     #[inline(always)]
112     fn from(mplace: &MPlaceTy<'tcx, Prov>) -> Self {
113         PlaceTy { place: Place::Ptr(**mplace), layout: mplace.layout, align: mplace.align }
114     }
115 }
116
117 impl<'tcx, Prov: Provenance> From<&'_ mut MPlaceTy<'tcx, Prov>> for PlaceTy<'tcx, Prov> {
118     #[inline(always)]
119     fn from(mplace: &mut MPlaceTy<'tcx, Prov>) -> Self {
120         PlaceTy { place: Place::Ptr(**mplace), layout: mplace.layout, align: mplace.align }
121     }
122 }
123
124 impl<Prov: Provenance> MemPlace<Prov> {
125     #[inline(always)]
126     pub fn from_ptr(ptr: Pointer<Option<Prov>>) -> Self {
127         MemPlace { ptr, meta: MemPlaceMeta::None }
128     }
129
130     /// Adjust the provenance of the main pointer (metadata is unaffected).
131     pub fn map_provenance(self, f: impl FnOnce(Option<Prov>) -> Option<Prov>) -> Self {
132         MemPlace { ptr: self.ptr.map_provenance(f), ..self }
133     }
134
135     /// Turn a mplace into a (thin or wide) pointer, as a reference, pointing to the same space.
136     /// This is the inverse of `ref_to_mplace`.
137     #[inline(always)]
138     pub fn to_ref(self, cx: &impl HasDataLayout) -> Immediate<Prov> {
139         match self.meta {
140             MemPlaceMeta::None => Immediate::from(Scalar::from_maybe_pointer(self.ptr, cx)),
141             MemPlaceMeta::Meta(meta) => {
142                 Immediate::ScalarPair(Scalar::from_maybe_pointer(self.ptr, cx).into(), meta.into())
143             }
144         }
145     }
146
147     #[inline]
148     pub fn offset_with_meta<'tcx>(
149         self,
150         offset: Size,
151         meta: MemPlaceMeta<Prov>,
152         cx: &impl HasDataLayout,
153     ) -> InterpResult<'tcx, Self> {
154         Ok(MemPlace { ptr: self.ptr.offset(offset, cx)?, meta })
155     }
156 }
157
158 impl<Prov: Provenance> Place<Prov> {
159     /// Asserts that this points to some local variable.
160     /// Returns the frame idx and the variable idx.
161     #[inline]
162     #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980)
163     pub fn assert_local(&self) -> (usize, mir::Local) {
164         match self {
165             Place::Local { frame, local } => (*frame, *local),
166             _ => bug!("assert_local: expected Place::Local, got {:?}", self),
167         }
168     }
169 }
170
171 impl<'tcx, Prov: Provenance> MPlaceTy<'tcx, Prov> {
172     /// Produces a MemPlace that works for ZST but nothing else.
173     /// Conceptually this is a new allocation, but it doesn't actually create an allocation so you
174     /// don't need to worry about memory leaks.
175     #[inline]
176     pub fn fake_alloc_zst(layout: TyAndLayout<'tcx>) -> Self {
177         assert!(layout.is_zst());
178         let align = layout.align.abi;
179         let ptr = Pointer::from_addr(align.bytes()); // no provenance, absolute address
180         MPlaceTy { mplace: MemPlace { ptr, meta: MemPlaceMeta::None }, layout, align }
181     }
182
183     #[inline]
184     pub fn offset_with_meta(
185         &self,
186         offset: Size,
187         meta: MemPlaceMeta<Prov>,
188         layout: TyAndLayout<'tcx>,
189         cx: &impl HasDataLayout,
190     ) -> InterpResult<'tcx, Self> {
191         Ok(MPlaceTy {
192             mplace: self.mplace.offset_with_meta(offset, meta, cx)?,
193             align: self.align.restrict_for_offset(offset),
194             layout,
195         })
196     }
197
198     pub fn offset(
199         &self,
200         offset: Size,
201         layout: TyAndLayout<'tcx>,
202         cx: &impl HasDataLayout,
203     ) -> InterpResult<'tcx, Self> {
204         assert!(!layout.is_unsized());
205         self.offset_with_meta(offset, MemPlaceMeta::None, layout, cx)
206     }
207
208     #[inline]
209     pub fn from_aligned_ptr(ptr: Pointer<Option<Prov>>, layout: TyAndLayout<'tcx>) -> Self {
210         MPlaceTy { mplace: MemPlace::from_ptr(ptr), layout, align: layout.align.abi }
211     }
212
213     #[inline]
214     pub fn from_aligned_ptr_with_meta(
215         ptr: Pointer<Option<Prov>>,
216         layout: TyAndLayout<'tcx>,
217         meta: MemPlaceMeta<Prov>,
218     ) -> Self {
219         let mut mplace = MemPlace::from_ptr(ptr);
220         mplace.meta = meta;
221
222         MPlaceTy { mplace, layout, align: layout.align.abi }
223     }
224
225     #[inline]
226     pub(crate) fn len(&self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64> {
227         if self.layout.is_unsized() {
228             // We need to consult `meta` metadata
229             match self.layout.ty.kind() {
230                 ty::Slice(..) | ty::Str => self.mplace.meta.unwrap_meta().to_machine_usize(cx),
231                 _ => bug!("len not supported on unsized type {:?}", self.layout.ty),
232             }
233         } else {
234             // Go through the layout.  There are lots of types that support a length,
235             // e.g., SIMD types. (But not all repr(simd) types even have FieldsShape::Array!)
236             match self.layout.fields {
237                 abi::FieldsShape::Array { count, .. } => Ok(count),
238                 _ => bug!("len not supported on sized type {:?}", self.layout.ty),
239             }
240         }
241     }
242
243     #[inline]
244     pub(super) fn vtable(&self) -> Scalar<Prov> {
245         match self.layout.ty.kind() {
246             ty::Dynamic(..) => self.mplace.meta.unwrap_meta(),
247             _ => bug!("vtable not supported on type {:?}", self.layout.ty),
248         }
249     }
250 }
251
252 // These are defined here because they produce a place.
253 impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> {
254     #[inline(always)]
255     pub fn try_as_mplace(&self) -> Result<MPlaceTy<'tcx, Prov>, ImmTy<'tcx, Prov>> {
256         match **self {
257             Operand::Indirect(mplace) => {
258                 Ok(MPlaceTy { mplace, layout: self.layout, align: self.align.unwrap() })
259             }
260             Operand::Immediate(imm) => Err(ImmTy::from_immediate(imm, self.layout)),
261         }
262     }
263
264     #[inline(always)]
265     #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980)
266     pub fn assert_mem_place(&self) -> MPlaceTy<'tcx, Prov> {
267         self.try_as_mplace().unwrap()
268     }
269 }
270
271 impl<'tcx, Prov: Provenance> PlaceTy<'tcx, Prov> {
272     /// A place is either an mplace or some local.
273     #[inline]
274     pub fn try_as_mplace(&self) -> Result<MPlaceTy<'tcx, Prov>, (usize, mir::Local)> {
275         match **self {
276             Place::Ptr(mplace) => Ok(MPlaceTy { mplace, layout: self.layout, align: self.align }),
277             Place::Local { frame, local } => Err((frame, local)),
278         }
279     }
280
281     #[inline(always)]
282     #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980)
283     pub fn assert_mem_place(self) -> MPlaceTy<'tcx, Prov> {
284         self.try_as_mplace().unwrap()
285     }
286 }
287
288 // FIXME: Working around https://github.com/rust-lang/rust/issues/54385
289 impl<'mir, 'tcx: 'mir, Prov, M> InterpCx<'mir, 'tcx, M>
290 where
291     Prov: Provenance + 'static,
292     M: Machine<'mir, 'tcx, Provenance = Prov>,
293 {
294     /// Take a value, which represents a (thin or wide) reference, and make it a place.
295     /// Alignment is just based on the type.  This is the inverse of `MemPlace::to_ref()`.
296     ///
297     /// Only call this if you are sure the place is "valid" (aligned and inbounds), or do not
298     /// want to ever use the place for memory access!
299     /// Generally prefer `deref_operand`.
300     pub fn ref_to_mplace(
301         &self,
302         val: &ImmTy<'tcx, M::Provenance>,
303     ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
304         let pointee_type =
305             val.layout.ty.builtin_deref(true).expect("`ref_to_mplace` called on non-ptr type").ty;
306         let layout = self.layout_of(pointee_type)?;
307         let (ptr, meta) = match **val {
308             Immediate::Scalar(ptr) => (ptr, MemPlaceMeta::None),
309             Immediate::ScalarPair(ptr, meta) => (ptr, MemPlaceMeta::Meta(meta)),
310             Immediate::Uninit => throw_ub!(InvalidUninitBytes(None)),
311         };
312
313         let mplace = MemPlace { ptr: ptr.to_pointer(self)?, meta };
314         // When deref'ing a pointer, the *static* alignment given by the type is what matters.
315         let align = layout.align.abi;
316         Ok(MPlaceTy { mplace, layout, align })
317     }
318
319     /// Take an operand, representing a pointer, and dereference it to a place -- that
320     /// will always be a MemPlace.  Lives in `place.rs` because it creates a place.
321     #[instrument(skip(self), level = "debug")]
322     pub fn deref_operand(
323         &self,
324         src: &OpTy<'tcx, M::Provenance>,
325     ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
326         let val = self.read_immediate(src)?;
327         trace!("deref to {} on {:?}", val.layout.ty, *val);
328
329         if val.layout.ty.is_box() {
330             bug!("dereferencing {:?}", val.layout.ty);
331         }
332
333         let mplace = self.ref_to_mplace(&val)?;
334         self.check_mplace_access(mplace, CheckInAllocMsg::DerefTest)?;
335         Ok(mplace)
336     }
337
338     #[inline]
339     pub(super) fn get_place_alloc(
340         &self,
341         place: &MPlaceTy<'tcx, M::Provenance>,
342     ) -> InterpResult<'tcx, Option<AllocRef<'_, 'tcx, M::Provenance, M::AllocExtra>>> {
343         assert!(!place.layout.is_unsized());
344         assert!(!place.meta.has_meta());
345         let size = place.layout.size;
346         self.get_ptr_alloc(place.ptr, size, place.align)
347     }
348
349     #[inline]
350     pub(super) fn get_place_alloc_mut(
351         &mut self,
352         place: &MPlaceTy<'tcx, M::Provenance>,
353     ) -> InterpResult<'tcx, Option<AllocRefMut<'_, 'tcx, M::Provenance, M::AllocExtra>>> {
354         assert!(!place.layout.is_unsized());
355         assert!(!place.meta.has_meta());
356         let size = place.layout.size;
357         self.get_ptr_alloc_mut(place.ptr, size, place.align)
358     }
359
360     /// Check if this mplace is dereferenceable and sufficiently aligned.
361     fn check_mplace_access(
362         &self,
363         mplace: MPlaceTy<'tcx, M::Provenance>,
364         msg: CheckInAllocMsg,
365     ) -> InterpResult<'tcx> {
366         let (size, align) = self
367             .size_and_align_of_mplace(&mplace)?
368             .unwrap_or((mplace.layout.size, mplace.layout.align.abi));
369         assert!(mplace.align <= align, "dynamic alignment less strict than static one?");
370         let align = M::enforce_alignment(self).then_some(align);
371         self.check_ptr_access_align(mplace.ptr, size, align.unwrap_or(Align::ONE), msg)?;
372         Ok(())
373     }
374
375     /// Converts a repr(simd) place into a place where `place_index` accesses the SIMD elements.
376     /// Also returns the number of elements.
377     pub fn mplace_to_simd(
378         &self,
379         mplace: &MPlaceTy<'tcx, M::Provenance>,
380     ) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::Provenance>, u64)> {
381         // Basically we just transmute this place into an array following simd_size_and_type.
382         // (Transmuting is okay since this is an in-memory place. We also double-check the size
383         // stays the same.)
384         let (len, e_ty) = mplace.layout.ty.simd_size_and_type(*self.tcx);
385         let array = self.tcx.mk_array(e_ty, len);
386         let layout = self.layout_of(array)?;
387         assert_eq!(layout.size, mplace.layout.size);
388         Ok((MPlaceTy { layout, ..*mplace }, len))
389     }
390
391     /// Converts a repr(simd) place into a place where `place_index` accesses the SIMD elements.
392     /// Also returns the number of elements.
393     pub fn place_to_simd(
394         &mut self,
395         place: &PlaceTy<'tcx, M::Provenance>,
396     ) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::Provenance>, u64)> {
397         let mplace = self.force_allocation(place)?;
398         self.mplace_to_simd(&mplace)
399     }
400
401     pub fn local_to_place(
402         &self,
403         frame: usize,
404         local: mir::Local,
405     ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> {
406         let layout = self.layout_of_local(&self.stack()[frame], local, None)?;
407         let place = Place::Local { frame, local };
408         Ok(PlaceTy { place, layout, align: layout.align.abi })
409     }
410
411     /// Computes a place. You should only use this if you intend to write into this
412     /// place; for reading, a more efficient alternative is `eval_place_to_op`.
413     #[instrument(skip(self), level = "debug")]
414     pub fn eval_place(
415         &mut self,
416         mir_place: mir::Place<'tcx>,
417     ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> {
418         let mut place = self.local_to_place(self.frame_idx(), mir_place.local)?;
419         // Using `try_fold` turned out to be bad for performance, hence the loop.
420         for elem in mir_place.projection.iter() {
421             place = self.place_projection(&place, elem)?
422         }
423
424         trace!("{:?}", self.dump_place(place.place));
425         // Sanity-check the type we ended up with.
426         debug_assert!(
427             mir_assign_valid_types(
428                 *self.tcx,
429                 self.param_env,
430                 self.layout_of(self.subst_from_current_frame_and_normalize_erasing_regions(
431                     mir_place.ty(&self.frame().body.local_decls, *self.tcx).ty
432                 )?)?,
433                 place.layout,
434             ),
435             "eval_place of a MIR place with type {:?} produced an interpreter place with type {:?}",
436             mir_place.ty(&self.frame().body.local_decls, *self.tcx).ty,
437             place.layout.ty,
438         );
439         Ok(place)
440     }
441
442     /// Write an immediate to a place
443     #[inline(always)]
444     #[instrument(skip(self), level = "debug")]
445     pub fn write_immediate(
446         &mut self,
447         src: Immediate<M::Provenance>,
448         dest: &PlaceTy<'tcx, M::Provenance>,
449     ) -> InterpResult<'tcx> {
450         self.write_immediate_no_validate(src, dest)?;
451
452         if M::enforce_validity(self) {
453             // Data got changed, better make sure it matches the type!
454             self.validate_operand(&self.place_to_op(dest)?)?;
455         }
456
457         Ok(())
458     }
459
460     /// Write a scalar to a place
461     #[inline(always)]
462     pub fn write_scalar(
463         &mut self,
464         val: impl Into<Scalar<M::Provenance>>,
465         dest: &PlaceTy<'tcx, M::Provenance>,
466     ) -> InterpResult<'tcx> {
467         self.write_immediate(Immediate::Scalar(val.into()), dest)
468     }
469
470     /// Write a pointer to a place
471     #[inline(always)]
472     pub fn write_pointer(
473         &mut self,
474         ptr: impl Into<Pointer<Option<M::Provenance>>>,
475         dest: &PlaceTy<'tcx, M::Provenance>,
476     ) -> InterpResult<'tcx> {
477         self.write_scalar(Scalar::from_maybe_pointer(ptr.into(), self), dest)
478     }
479
480     /// Write an immediate to a place.
481     /// If you use this you are responsible for validating that things got copied at the
482     /// right type.
483     fn write_immediate_no_validate(
484         &mut self,
485         src: Immediate<M::Provenance>,
486         dest: &PlaceTy<'tcx, M::Provenance>,
487     ) -> InterpResult<'tcx> {
488         assert!(!dest.layout.is_unsized(), "Cannot write unsized data");
489         trace!("write_immediate: {:?} <- {:?}: {}", *dest, src, dest.layout.ty);
490
491         // See if we can avoid an allocation. This is the counterpart to `read_immediate_raw`,
492         // but not factored as a separate function.
493         let mplace = match dest.place {
494             Place::Local { frame, local } => {
495                 match M::access_local_mut(self, frame, local)? {
496                     Operand::Immediate(local) => {
497                         // Local can be updated in-place.
498                         *local = src;
499                         return Ok(());
500                     }
501                     Operand::Indirect(mplace) => {
502                         // The local is in memory, go on below.
503                         *mplace
504                     }
505                 }
506             }
507             Place::Ptr(mplace) => mplace, // already referring to memory
508         };
509
510         // This is already in memory, write there.
511         self.write_immediate_to_mplace_no_validate(src, dest.layout, dest.align, mplace)
512     }
513
514     /// Write an immediate to memory.
515     /// If you use this you are responsible for validating that things got copied at the
516     /// right layout.
517     fn write_immediate_to_mplace_no_validate(
518         &mut self,
519         value: Immediate<M::Provenance>,
520         layout: TyAndLayout<'tcx>,
521         align: Align,
522         dest: MemPlace<M::Provenance>,
523     ) -> InterpResult<'tcx> {
524         // Note that it is really important that the type here is the right one, and matches the
525         // type things are read at. In case `value` is a `ScalarPair`, we don't do any magic here
526         // to handle padding properly, which is only correct if we never look at this data with the
527         // wrong type.
528
529         let tcx = *self.tcx;
530         let Some(mut alloc) = self.get_place_alloc_mut(&MPlaceTy { mplace: dest, layout, align })? else {
531             // zero-sized access
532             return Ok(());
533         };
534
535         match value {
536             Immediate::Scalar(scalar) => {
537                 let Abi::Scalar(s) = layout.abi else { span_bug!(
538                         self.cur_span(),
539                         "write_immediate_to_mplace: invalid Scalar layout: {layout:#?}",
540                     )
541                 };
542                 let size = s.size(&tcx);
543                 assert_eq!(size, layout.size, "abi::Scalar size does not match layout size");
544                 alloc.write_scalar(alloc_range(Size::ZERO, size), scalar)
545             }
546             Immediate::ScalarPair(a_val, b_val) => {
547                 // We checked `ptr_align` above, so all fields will have the alignment they need.
548                 // We would anyway check against `ptr_align.restrict_for_offset(b_offset)`,
549                 // which `ptr.offset(b_offset)` cannot possibly fail to satisfy.
550                 let Abi::ScalarPair(a, b) = layout.abi else { span_bug!(
551                         self.cur_span(),
552                         "write_immediate_to_mplace: invalid ScalarPair layout: {:#?}",
553                         layout
554                     )
555                 };
556                 let (a_size, b_size) = (a.size(&tcx), b.size(&tcx));
557                 let b_offset = a_size.align_to(b.align(&tcx).abi);
558                 assert!(b_offset.bytes() > 0); // in `operand_field` we use the offset to tell apart the fields
559
560                 // It is tempting to verify `b_offset` against `layout.fields.offset(1)`,
561                 // but that does not work: We could be a newtype around a pair, then the
562                 // fields do not match the `ScalarPair` components.
563
564                 alloc.write_scalar(alloc_range(Size::ZERO, a_size), a_val)?;
565                 alloc.write_scalar(alloc_range(b_offset, b_size), b_val)
566             }
567             Immediate::Uninit => alloc.write_uninit(),
568         }
569     }
570
571     pub fn write_uninit(&mut self, dest: &PlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx> {
572         let mplace = match dest.try_as_mplace() {
573             Ok(mplace) => mplace,
574             Err((frame, local)) => {
575                 match M::access_local_mut(self, frame, local)? {
576                     Operand::Immediate(local) => {
577                         *local = Immediate::Uninit;
578                         return Ok(());
579                     }
580                     Operand::Indirect(mplace) => {
581                         // The local is in memory, go on below.
582                         MPlaceTy { mplace: *mplace, layout: dest.layout, align: dest.align }
583                     }
584                 }
585             }
586         };
587         let Some(mut alloc) = self.get_place_alloc_mut(&mplace)? else {
588             // Zero-sized access
589             return Ok(());
590         };
591         alloc.write_uninit()?;
592         Ok(())
593     }
594
595     /// Copies the data from an operand to a place.
596     /// `allow_transmute` indicates whether the layouts may disagree.
597     #[inline(always)]
598     #[instrument(skip(self), level = "debug")]
599     pub fn copy_op(
600         &mut self,
601         src: &OpTy<'tcx, M::Provenance>,
602         dest: &PlaceTy<'tcx, M::Provenance>,
603         allow_transmute: bool,
604     ) -> InterpResult<'tcx> {
605         self.copy_op_no_validate(src, dest, allow_transmute)?;
606
607         if M::enforce_validity(self) {
608             // Data got changed, better make sure it matches the type!
609             self.validate_operand(&self.place_to_op(dest)?)?;
610         }
611
612         Ok(())
613     }
614
615     /// Copies the data from an operand to a place.
616     /// `allow_transmute` indicates whether the layouts may disagree.
617     /// Also, if you use this you are responsible for validating that things get copied at the
618     /// right type.
619     #[instrument(skip(self), level = "debug")]
620     fn copy_op_no_validate(
621         &mut self,
622         src: &OpTy<'tcx, M::Provenance>,
623         dest: &PlaceTy<'tcx, M::Provenance>,
624         allow_transmute: bool,
625     ) -> InterpResult<'tcx> {
626         // We do NOT compare the types for equality, because well-typed code can
627         // actually "transmute" `&mut T` to `&T` in an assignment without a cast.
628         let layout_compat =
629             mir_assign_valid_types(*self.tcx, self.param_env, src.layout, dest.layout);
630         if !allow_transmute && !layout_compat {
631             span_bug!(
632                 self.cur_span(),
633                 "type mismatch when copying!\nsrc: {:?},\ndest: {:?}",
634                 src.layout.ty,
635                 dest.layout.ty,
636             );
637         }
638
639         // Let us see if the layout is simple so we take a shortcut,
640         // avoid force_allocation.
641         let src = match self.read_immediate_raw(src)? {
642             Ok(src_val) => {
643                 assert!(!src.layout.is_unsized(), "cannot copy unsized immediates");
644                 assert!(
645                     !dest.layout.is_unsized(),
646                     "the src is sized, so the dest must also be sized"
647                 );
648                 assert_eq!(src.layout.size, dest.layout.size);
649                 // Yay, we got a value that we can write directly.
650                 return if layout_compat {
651                     self.write_immediate_no_validate(*src_val, dest)
652                 } else {
653                     // This is tricky. The problematic case is `ScalarPair`: the `src_val` was
654                     // loaded using the offsets defined by `src.layout`. When we put this back into
655                     // the destination, we have to use the same offsets! So (a) we make sure we
656                     // write back to memory, and (b) we use `dest` *with the source layout*.
657                     let dest_mem = self.force_allocation(dest)?;
658                     self.write_immediate_to_mplace_no_validate(
659                         *src_val,
660                         src.layout,
661                         dest_mem.align,
662                         *dest_mem,
663                     )
664                 };
665             }
666             Err(mplace) => mplace,
667         };
668         // Slow path, this does not fit into an immediate. Just memcpy.
669         trace!("copy_op: {:?} <- {:?}: {}", *dest, src, dest.layout.ty);
670
671         let dest = self.force_allocation(&dest)?;
672         let Some((dest_size, _)) = self.size_and_align_of_mplace(&dest)? else {
673             span_bug!(self.cur_span(), "copy_op needs (dynamically) sized values")
674         };
675         if cfg!(debug_assertions) {
676             let src_size = self.size_and_align_of_mplace(&src)?.unwrap().0;
677             assert_eq!(src_size, dest_size, "Cannot copy differently-sized data");
678         } else {
679             // As a cheap approximation, we compare the fixed parts of the size.
680             assert_eq!(src.layout.size, dest.layout.size);
681         }
682
683         self.mem_copy(
684             src.ptr, src.align, dest.ptr, dest.align, dest_size, /*nonoverlapping*/ false,
685         )
686     }
687
688     /// Ensures that a place is in memory, and returns where it is.
689     /// If the place currently refers to a local that doesn't yet have a matching allocation,
690     /// create such an allocation.
691     /// This is essentially `force_to_memplace`.
692     #[instrument(skip(self), level = "debug")]
693     pub fn force_allocation(
694         &mut self,
695         place: &PlaceTy<'tcx, M::Provenance>,
696     ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
697         let mplace = match place.place {
698             Place::Local { frame, local } => {
699                 match M::access_local_mut(self, frame, local)? {
700                     &mut Operand::Immediate(local_val) => {
701                         // We need to make an allocation.
702
703                         // We need the layout of the local.  We can NOT use the layout we got,
704                         // that might e.g., be an inner field of a struct with `Scalar` layout,
705                         // that has different alignment than the outer field.
706                         let local_layout =
707                             self.layout_of_local(&self.stack()[frame], local, None)?;
708                         if local_layout.is_unsized() {
709                             throw_unsup_format!("unsized locals are not supported");
710                         }
711                         let mplace = *self.allocate(local_layout, MemoryKind::Stack)?;
712                         if !matches!(local_val, Immediate::Uninit) {
713                             // Preserve old value. (As an optimization, we can skip this if it was uninit.)
714                             // We don't have to validate as we can assume the local
715                             // was already valid for its type.
716                             self.write_immediate_to_mplace_no_validate(
717                                 local_val,
718                                 local_layout,
719                                 local_layout.align.abi,
720                                 mplace,
721                             )?;
722                         }
723                         // Now we can call `access_mut` again, asserting it goes well,
724                         // and actually overwrite things.
725                         *M::access_local_mut(self, frame, local).unwrap() =
726                             Operand::Indirect(mplace);
727                         mplace
728                     }
729                     &mut Operand::Indirect(mplace) => mplace, // this already was an indirect local
730                 }
731             }
732             Place::Ptr(mplace) => mplace,
733         };
734         // Return with the original layout, so that the caller can go on
735         Ok(MPlaceTy { mplace, layout: place.layout, align: place.align })
736     }
737
738     pub fn allocate(
739         &mut self,
740         layout: TyAndLayout<'tcx>,
741         kind: MemoryKind<M::MemoryKind>,
742     ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
743         assert!(!layout.is_unsized());
744         let ptr = self.allocate_ptr(layout.size, layout.align.abi, kind)?;
745         Ok(MPlaceTy::from_aligned_ptr(ptr.into(), layout))
746     }
747
748     /// Returns a wide MPlace of type `&'static [mut] str` to a new 1-aligned allocation.
749     pub fn allocate_str(
750         &mut self,
751         str: &str,
752         kind: MemoryKind<M::MemoryKind>,
753         mutbl: Mutability,
754     ) -> MPlaceTy<'tcx, M::Provenance> {
755         let ptr = self.allocate_bytes_ptr(str.as_bytes(), Align::ONE, kind, mutbl);
756         let meta = Scalar::from_machine_usize(u64::try_from(str.len()).unwrap(), self);
757         let mplace = MemPlace { ptr: ptr.into(), meta: MemPlaceMeta::Meta(meta) };
758
759         let ty = self.tcx.mk_ref(
760             self.tcx.lifetimes.re_static,
761             ty::TypeAndMut { ty: self.tcx.types.str_, mutbl },
762         );
763         let layout = self.layout_of(ty).unwrap();
764         MPlaceTy { mplace, layout, align: layout.align.abi }
765     }
766
767     /// Writes the discriminant of the given variant.
768     #[instrument(skip(self), level = "debug")]
769     pub fn write_discriminant(
770         &mut self,
771         variant_index: VariantIdx,
772         dest: &PlaceTy<'tcx, M::Provenance>,
773     ) -> InterpResult<'tcx> {
774         // This must be an enum or generator.
775         match dest.layout.ty.kind() {
776             ty::Adt(adt, _) => assert!(adt.is_enum()),
777             ty::Generator(..) => {}
778             _ => span_bug!(
779                 self.cur_span(),
780                 "write_discriminant called on non-variant-type (neither enum nor generator)"
781             ),
782         }
783         // Layout computation excludes uninhabited variants from consideration
784         // therefore there's no way to represent those variants in the given layout.
785         // Essentially, uninhabited variants do not have a tag that corresponds to their
786         // discriminant, so we cannot do anything here.
787         // When evaluating we will always error before even getting here, but ConstProp 'executes'
788         // dead code, so we cannot ICE here.
789         if dest.layout.for_variant(self, variant_index).abi.is_uninhabited() {
790             throw_ub!(UninhabitedEnumVariantWritten)
791         }
792
793         match dest.layout.variants {
794             abi::Variants::Single { index } => {
795                 assert_eq!(index, variant_index);
796             }
797             abi::Variants::Multiple {
798                 tag_encoding: TagEncoding::Direct,
799                 tag: tag_layout,
800                 tag_field,
801                 ..
802             } => {
803                 // No need to validate that the discriminant here because the
804                 // `TyAndLayout::for_variant()` call earlier already checks the variant is valid.
805
806                 let discr_val =
807                     dest.layout.ty.discriminant_for_variant(*self.tcx, variant_index).unwrap().val;
808
809                 // raw discriminants for enums are isize or bigger during
810                 // their computation, but the in-memory tag is the smallest possible
811                 // representation
812                 let size = tag_layout.size(self);
813                 let tag_val = size.truncate(discr_val);
814
815                 let tag_dest = self.place_field(dest, tag_field)?;
816                 self.write_scalar(Scalar::from_uint(tag_val, size), &tag_dest)?;
817             }
818             abi::Variants::Multiple {
819                 tag_encoding:
820                     TagEncoding::Niche { dataful_variant, ref niche_variants, niche_start },
821                 tag: tag_layout,
822                 tag_field,
823                 ..
824             } => {
825                 // No need to validate that the discriminant here because the
826                 // `TyAndLayout::for_variant()` call earlier already checks the variant is valid.
827
828                 if variant_index != dataful_variant {
829                     let variants_start = niche_variants.start().as_u32();
830                     let variant_index_relative = variant_index
831                         .as_u32()
832                         .checked_sub(variants_start)
833                         .expect("overflow computing relative variant idx");
834                     // We need to use machine arithmetic when taking into account `niche_start`:
835                     // tag_val = variant_index_relative + niche_start_val
836                     let tag_layout = self.layout_of(tag_layout.primitive().to_int_ty(*self.tcx))?;
837                     let niche_start_val = ImmTy::from_uint(niche_start, tag_layout);
838                     let variant_index_relative_val =
839                         ImmTy::from_uint(variant_index_relative, tag_layout);
840                     let tag_val = self.binary_op(
841                         mir::BinOp::Add,
842                         &variant_index_relative_val,
843                         &niche_start_val,
844                     )?;
845                     // Write result.
846                     let niche_dest = self.place_field(dest, tag_field)?;
847                     self.write_immediate(*tag_val, &niche_dest)?;
848                 }
849             }
850         }
851
852         Ok(())
853     }
854
855     pub fn raw_const_to_mplace(
856         &self,
857         raw: ConstAlloc<'tcx>,
858     ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
859         // This must be an allocation in `tcx`
860         let _ = self.tcx.global_alloc(raw.alloc_id);
861         let ptr = self.global_base_pointer(Pointer::from(raw.alloc_id))?;
862         let layout = self.layout_of(raw.ty)?;
863         Ok(MPlaceTy::from_aligned_ptr(ptr.into(), layout))
864     }
865
866     /// Turn a place with a `dyn Trait` type into a place with the actual dynamic type.
867     pub(super) fn unpack_dyn_trait(
868         &self,
869         mplace: &MPlaceTy<'tcx, M::Provenance>,
870     ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
871         let vtable = mplace.vtable().to_pointer(self)?; // also sanity checks the type
872         let (ty, _) = self.get_ptr_vtable(vtable)?;
873         let layout = self.layout_of(ty)?;
874
875         let mplace = MPlaceTy {
876             mplace: MemPlace { meta: MemPlaceMeta::None, ..**mplace },
877             layout,
878             align: layout.align.abi,
879         };
880         Ok(mplace)
881     }
882 }
883
884 // Some nodes are used a lot. Make sure they don't unintentionally get bigger.
885 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
886 mod size_asserts {
887     use super::*;
888     use rustc_data_structures::static_assert_size;
889     // These are in alphabetical order, which is easy to maintain.
890     static_assert_size!(MemPlaceMeta, 24);
891     static_assert_size!(MemPlace, 40);
892     static_assert_size!(MPlaceTy<'_>, 64);
893     static_assert_size!(Place, 48);
894     static_assert_size!(PlaceTy<'_>, 72);
895 }