1use std::assert_matches::assert_matches;
6
7use either::{Either, Left, Right};
8use rustc_abi::{BackendRepr, HasDataLayout, Size};
9use rustc_middle::ty::Ty;
10use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
11use rustc_middle::{bug, mir, span_bug};
12use tracing::{instrument, trace};
13
14use super::{
15 AllocInit, AllocRef, AllocRefMut, CheckAlignMsg, CtfeProvenance, ImmTy, Immediate, InterpCx,
16 InterpResult, Machine, MemoryKind, Misalignment, OffsetMode, OpTy, Operand, Pointer,
17 Projectable, Provenance, Scalar, alloc_range, interp_ok, mir_assign_valid_types,
18};
19
20#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
21pub enum MemPlaceMeta<Prov: Provenance = CtfeProvenance> {
23 Meta(Scalar<Prov>),
25 None,
27}
28
29impl<Prov: Provenance> MemPlaceMeta<Prov> {
30 #[cfg_attr(debug_assertions, track_caller)] pub fn unwrap_meta(self) -> Scalar<Prov> {
32 match self {
33 Self::Meta(s) => s,
34 Self::None => {
35 bug!("expected wide pointer extra data (e.g. slice length or trait object vtable)")
36 }
37 }
38 }
39
40 #[inline(always)]
41 pub fn has_meta(self) -> bool {
42 match self {
43 Self::Meta(_) => true,
44 Self::None => false,
45 }
46 }
47}
48
49#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
50pub(super) struct MemPlace<Prov: Provenance = CtfeProvenance> {
51 pub ptr: Pointer<Option<Prov>>,
53 pub meta: MemPlaceMeta<Prov>,
57 misaligned: Option<Misalignment>,
59}
60
61impl<Prov: Provenance> MemPlace<Prov> {
62 fn map_provenance(self, f: impl FnOnce(Prov) -> Prov) -> Self {
64 MemPlace { ptr: self.ptr.map_provenance(|p| p.map(f)), ..self }
65 }
66
67 #[inline]
69 fn to_ref(self, cx: &impl HasDataLayout) -> Immediate<Prov> {
70 Immediate::new_pointer_with_meta(self.ptr, self.meta, cx)
71 }
72
73 #[inline]
74 fn offset_with_meta_<'tcx, M: Machine<'tcx, Provenance = Prov>>(
76 self,
77 offset: Size,
78 mode: OffsetMode,
79 meta: MemPlaceMeta<Prov>,
80 ecx: &InterpCx<'tcx, M>,
81 ) -> InterpResult<'tcx, Self> {
82 debug_assert!(
83 !meta.has_meta() || self.meta.has_meta(),
84 "cannot use `offset_with_meta` to add metadata to a place"
85 );
86 let ptr = match mode {
87 OffsetMode::Inbounds => {
88 ecx.ptr_offset_inbounds(self.ptr, offset.bytes().try_into().unwrap())?
89 }
90 OffsetMode::Wrapping => self.ptr.wrapping_offset(offset, ecx),
91 };
92 interp_ok(MemPlace { ptr, meta, misaligned: self.misaligned })
93 }
94}
95
96#[derive(Clone, Hash, Eq, PartialEq)]
98pub struct MPlaceTy<'tcx, Prov: Provenance = CtfeProvenance> {
99 mplace: MemPlace<Prov>,
100 pub layout: TyAndLayout<'tcx>,
101}
102
103impl<Prov: Provenance> std::fmt::Debug for MPlaceTy<'_, Prov> {
104 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
105 f.debug_struct("MPlaceTy")
107 .field("mplace", &self.mplace)
108 .field("ty", &format_args!("{}", self.layout.ty))
109 .finish()
110 }
111}
112
113impl<'tcx, Prov: Provenance> MPlaceTy<'tcx, Prov> {
114 #[inline]
118 pub fn fake_alloc_zst(layout: TyAndLayout<'tcx>) -> Self {
119 assert!(layout.is_zst());
120 let align = layout.align.abi;
121 let ptr = Pointer::from_addr_invalid(align.bytes()); MPlaceTy { mplace: MemPlace { ptr, meta: MemPlaceMeta::None, misaligned: None }, layout }
123 }
124
125 pub fn map_provenance(self, f: impl FnOnce(Prov) -> Prov) -> Self {
127 MPlaceTy { mplace: self.mplace.map_provenance(f), ..self }
128 }
129
130 #[inline(always)]
131 pub(super) fn mplace(&self) -> &MemPlace<Prov> {
132 &self.mplace
133 }
134
135 #[inline(always)]
136 pub fn ptr(&self) -> Pointer<Option<Prov>> {
137 self.mplace.ptr
138 }
139
140 #[inline(always)]
141 pub fn to_ref(&self, cx: &impl HasDataLayout) -> Immediate<Prov> {
142 self.mplace.to_ref(cx)
143 }
144}
145
146impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for MPlaceTy<'tcx, Prov> {
147 #[inline(always)]
148 fn layout(&self) -> TyAndLayout<'tcx> {
149 self.layout
150 }
151
152 #[inline(always)]
153 fn meta(&self) -> MemPlaceMeta<Prov> {
154 self.mplace.meta
155 }
156
157 fn offset_with_meta<M: Machine<'tcx, Provenance = Prov>>(
158 &self,
159 offset: Size,
160 mode: OffsetMode,
161 meta: MemPlaceMeta<Prov>,
162 layout: TyAndLayout<'tcx>,
163 ecx: &InterpCx<'tcx, M>,
164 ) -> InterpResult<'tcx, Self> {
165 interp_ok(MPlaceTy {
166 mplace: self.mplace.offset_with_meta_(offset, mode, meta, ecx)?,
167 layout,
168 })
169 }
170
171 #[inline(always)]
172 fn to_op<M: Machine<'tcx, Provenance = Prov>>(
173 &self,
174 _ecx: &InterpCx<'tcx, M>,
175 ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
176 interp_ok(self.clone().into())
177 }
178}
179
180#[derive(Copy, Clone, Debug)]
181pub(super) enum Place<Prov: Provenance = CtfeProvenance> {
182 Ptr(MemPlace<Prov>),
184
185 Local { local: mir::Local, offset: Option<Size>, locals_addr: usize },
197}
198
199#[derive(Clone)]
206pub struct PlaceTy<'tcx, Prov: Provenance = CtfeProvenance> {
207 place: Place<Prov>, pub layout: TyAndLayout<'tcx>,
209}
210
211impl<Prov: Provenance> std::fmt::Debug for PlaceTy<'_, Prov> {
212 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
213 f.debug_struct("PlaceTy")
215 .field("place", &self.place)
216 .field("ty", &format_args!("{}", self.layout.ty))
217 .finish()
218 }
219}
220
221impl<'tcx, Prov: Provenance> From<MPlaceTy<'tcx, Prov>> for PlaceTy<'tcx, Prov> {
222 #[inline(always)]
223 fn from(mplace: MPlaceTy<'tcx, Prov>) -> Self {
224 PlaceTy { place: Place::Ptr(mplace.mplace), layout: mplace.layout }
225 }
226}
227
228impl<'tcx, Prov: Provenance> PlaceTy<'tcx, Prov> {
229 #[inline(always)]
230 pub(super) fn place(&self) -> &Place<Prov> {
231 &self.place
232 }
233
234 #[inline(always)]
236 pub fn as_mplace_or_local(
237 &self,
238 ) -> Either<MPlaceTy<'tcx, Prov>, (mir::Local, Option<Size>, usize, TyAndLayout<'tcx>)> {
239 match self.place {
240 Place::Ptr(mplace) => Left(MPlaceTy { mplace, layout: self.layout }),
241 Place::Local { local, offset, locals_addr } => {
242 Right((local, offset, locals_addr, self.layout))
243 }
244 }
245 }
246
247 #[inline(always)]
248 #[cfg_attr(debug_assertions, track_caller)] pub fn assert_mem_place(&self) -> MPlaceTy<'tcx, Prov> {
250 self.as_mplace_or_local().left().unwrap_or_else(|| {
251 bug!(
252 "PlaceTy of type {} was a local when it was expected to be an MPlace",
253 self.layout.ty
254 )
255 })
256 }
257}
258
259impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for PlaceTy<'tcx, Prov> {
260 #[inline(always)]
261 fn layout(&self) -> TyAndLayout<'tcx> {
262 self.layout
263 }
264
265 #[inline]
266 fn meta(&self) -> MemPlaceMeta<Prov> {
267 match self.as_mplace_or_local() {
268 Left(mplace) => mplace.meta(),
269 Right(_) => {
270 debug_assert!(self.layout.is_sized(), "unsized locals should live in memory");
271 MemPlaceMeta::None
272 }
273 }
274 }
275
276 fn offset_with_meta<M: Machine<'tcx, Provenance = Prov>>(
277 &self,
278 offset: Size,
279 mode: OffsetMode,
280 meta: MemPlaceMeta<Prov>,
281 layout: TyAndLayout<'tcx>,
282 ecx: &InterpCx<'tcx, M>,
283 ) -> InterpResult<'tcx, Self> {
284 interp_ok(match self.as_mplace_or_local() {
285 Left(mplace) => mplace.offset_with_meta(offset, mode, meta, layout, ecx)?.into(),
286 Right((local, old_offset, locals_addr, _)) => {
287 debug_assert!(layout.is_sized(), "unsized locals should live in memory");
288 assert_matches!(meta, MemPlaceMeta::None); assert!(offset + layout.size <= self.layout.size);
293
294 let new_offset = old_offset.unwrap_or(Size::ZERO) + offset;
296
297 PlaceTy {
298 place: Place::Local { local, offset: Some(new_offset), locals_addr },
299 layout,
300 }
301 }
302 })
303 }
304
305 #[inline(always)]
306 fn to_op<M: Machine<'tcx, Provenance = Prov>>(
307 &self,
308 ecx: &InterpCx<'tcx, M>,
309 ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
310 ecx.place_to_op(self)
311 }
312}
313
314impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> {
316 #[inline(always)]
317 pub fn as_mplace_or_imm(&self) -> Either<MPlaceTy<'tcx, Prov>, ImmTy<'tcx, Prov>> {
318 match self.op() {
319 Operand::Indirect(mplace) => Left(MPlaceTy { mplace: *mplace, layout: self.layout }),
320 Operand::Immediate(imm) => Right(ImmTy::from_immediate(*imm, self.layout)),
321 }
322 }
323
324 #[inline(always)]
325 #[cfg_attr(debug_assertions, track_caller)] pub fn assert_mem_place(&self) -> MPlaceTy<'tcx, Prov> {
327 self.as_mplace_or_imm().left().unwrap_or_else(|| {
328 bug!(
329 "OpTy of type {} was immediate when it was expected to be an MPlace",
330 self.layout.ty
331 )
332 })
333 }
334}
335
336pub trait Writeable<'tcx, Prov: Provenance>: Projectable<'tcx, Prov> {
338 fn to_place(&self) -> PlaceTy<'tcx, Prov>;
339
340 fn force_mplace<M: Machine<'tcx, Provenance = Prov>>(
341 &self,
342 ecx: &mut InterpCx<'tcx, M>,
343 ) -> InterpResult<'tcx, MPlaceTy<'tcx, Prov>>;
344}
345
346impl<'tcx, Prov: Provenance> Writeable<'tcx, Prov> for PlaceTy<'tcx, Prov> {
347 #[inline(always)]
348 fn to_place(&self) -> PlaceTy<'tcx, Prov> {
349 self.clone()
350 }
351
352 #[inline(always)]
353 fn force_mplace<M: Machine<'tcx, Provenance = Prov>>(
354 &self,
355 ecx: &mut InterpCx<'tcx, M>,
356 ) -> InterpResult<'tcx, MPlaceTy<'tcx, Prov>> {
357 ecx.force_allocation(self)
358 }
359}
360
361impl<'tcx, Prov: Provenance> Writeable<'tcx, Prov> for MPlaceTy<'tcx, Prov> {
362 #[inline(always)]
363 fn to_place(&self) -> PlaceTy<'tcx, Prov> {
364 self.clone().into()
365 }
366
367 #[inline(always)]
368 fn force_mplace<M: Machine<'tcx, Provenance = Prov>>(
369 &self,
370 _ecx: &mut InterpCx<'tcx, M>,
371 ) -> InterpResult<'tcx, MPlaceTy<'tcx, Prov>> {
372 interp_ok(self.clone())
373 }
374}
375
376impl<'tcx, Prov, M> InterpCx<'tcx, M>
378where
379 Prov: Provenance,
380 M: Machine<'tcx, Provenance = Prov>,
381{
382 fn ptr_with_meta_to_mplace(
383 &self,
384 ptr: Pointer<Option<M::Provenance>>,
385 meta: MemPlaceMeta<M::Provenance>,
386 layout: TyAndLayout<'tcx>,
387 unaligned: bool,
388 ) -> MPlaceTy<'tcx, M::Provenance> {
389 let misaligned =
390 if unaligned { None } else { self.is_ptr_misaligned(ptr, layout.align.abi) };
391 MPlaceTy { mplace: MemPlace { ptr, meta, misaligned }, layout }
392 }
393
394 pub fn ptr_to_mplace(
395 &self,
396 ptr: Pointer<Option<M::Provenance>>,
397 layout: TyAndLayout<'tcx>,
398 ) -> MPlaceTy<'tcx, M::Provenance> {
399 assert!(layout.is_sized());
400 self.ptr_with_meta_to_mplace(ptr, MemPlaceMeta::None, layout, false)
401 }
402
403 pub fn ptr_to_mplace_unaligned(
404 &self,
405 ptr: Pointer<Option<M::Provenance>>,
406 layout: TyAndLayout<'tcx>,
407 ) -> MPlaceTy<'tcx, M::Provenance> {
408 assert!(layout.is_sized());
409 self.ptr_with_meta_to_mplace(ptr, MemPlaceMeta::None, layout, true)
410 }
411
412 pub fn ref_to_mplace(
419 &self,
420 val: &ImmTy<'tcx, M::Provenance>,
421 ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
422 let pointee_type =
423 val.layout.ty.builtin_deref(true).expect("`ref_to_mplace` called on non-ptr type");
424 let layout = self.layout_of(pointee_type)?;
425 let (ptr, meta) = val.to_scalar_and_meta();
426
427 let ptr = ptr.to_pointer(self)?;
430 interp_ok(self.ptr_with_meta_to_mplace(ptr, meta, layout, false))
431 }
432
433 pub fn mplace_to_ref(
437 &self,
438 mplace: &MPlaceTy<'tcx, M::Provenance>,
439 ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> {
440 let imm = mplace.mplace.to_ref(self);
441 let layout = self.layout_of(Ty::new_mut_ptr(self.tcx.tcx, mplace.layout.ty))?;
442 interp_ok(ImmTy::from_immediate(imm, layout))
443 }
444
445 #[instrument(skip(self), level = "trace")]
448 pub fn deref_pointer(
449 &self,
450 src: &impl Projectable<'tcx, M::Provenance>,
451 ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
452 if src.layout().ty.is_box() {
453 bug!("dereferencing {}", src.layout().ty);
457 }
458
459 let val = self.read_immediate(src)?;
460 trace!("deref to {} on {:?}", val.layout.ty, *val);
461
462 let mplace = self.ref_to_mplace(&val)?;
463 interp_ok(mplace)
464 }
465
466 #[inline]
467 pub(super) fn get_place_alloc(
468 &self,
469 mplace: &MPlaceTy<'tcx, M::Provenance>,
470 ) -> InterpResult<'tcx, Option<AllocRef<'_, 'tcx, M::Provenance, M::AllocExtra, M::Bytes>>>
471 {
472 let (size, _align) = self
473 .size_and_align_of_mplace(mplace)?
474 .unwrap_or((mplace.layout.size, mplace.layout.align.abi));
475 let a = self.get_ptr_alloc(mplace.ptr(), size)?;
478 self.check_misalign(mplace.mplace.misaligned, CheckAlignMsg::BasedOn)?;
479 interp_ok(a)
480 }
481
482 #[inline]
483 pub(super) fn get_place_alloc_mut(
484 &mut self,
485 mplace: &MPlaceTy<'tcx, M::Provenance>,
486 ) -> InterpResult<'tcx, Option<AllocRefMut<'_, 'tcx, M::Provenance, M::AllocExtra, M::Bytes>>>
487 {
488 let (size, _align) = self
489 .size_and_align_of_mplace(mplace)?
490 .unwrap_or((mplace.layout.size, mplace.layout.align.abi));
491 let misalign_res = self.check_misalign(mplace.mplace.misaligned, CheckAlignMsg::BasedOn);
495 let (a, ()) = self.get_ptr_alloc_mut(mplace.ptr(), size).and(misalign_res)?;
497 interp_ok(a)
498 }
499
500 pub fn local_to_place(
502 &self,
503 local: mir::Local,
504 ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> {
505 let frame = self.frame();
506 let layout = self.layout_of_local(frame, local, None)?;
507 let place = if layout.is_sized() {
508 Place::Local { local, offset: None, locals_addr: frame.locals_addr() }
510 } else {
511 match frame.locals[local].access()? {
513 Operand::Immediate(_) => bug!(),
514 Operand::Indirect(mplace) => Place::Ptr(*mplace),
515 }
516 };
517 interp_ok(PlaceTy { place, layout })
518 }
519
520 #[instrument(skip(self), level = "trace")]
523 pub fn eval_place(
524 &self,
525 mir_place: mir::Place<'tcx>,
526 ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> {
527 let mut place = self.local_to_place(mir_place.local)?;
528 for elem in mir_place.projection.iter() {
530 place = self.project(&place, elem)?
531 }
532
533 trace!("{:?}", self.dump_place(&place));
534 if cfg!(debug_assertions) {
536 let normalized_place_ty = self
537 .instantiate_from_current_frame_and_normalize_erasing_regions(
538 mir_place.ty(&self.frame().body.local_decls, *self.tcx).ty,
539 )?;
540 if !mir_assign_valid_types(
541 *self.tcx,
542 self.typing_env,
543 self.layout_of(normalized_place_ty)?,
544 place.layout,
545 ) {
546 span_bug!(
547 self.cur_span(),
548 "eval_place of a MIR place with type {} produced an interpreter place with type {}",
549 normalized_place_ty,
550 place.layout.ty,
551 )
552 }
553 }
554 interp_ok(place)
555 }
556
557 #[inline(always)]
560 fn as_mplace_or_mutable_local(
561 &mut self,
562 place: &PlaceTy<'tcx, M::Provenance>,
563 ) -> InterpResult<
564 'tcx,
565 Either<
566 MPlaceTy<'tcx, M::Provenance>,
567 (&mut Immediate<M::Provenance>, TyAndLayout<'tcx>, mir::Local),
568 >,
569 > {
570 interp_ok(match place.to_place().as_mplace_or_local() {
571 Left(mplace) => Left(mplace),
572 Right((local, offset, locals_addr, layout)) => {
573 if offset.is_some() {
574 Left(place.force_mplace(self)?)
577 } else {
578 debug_assert_eq!(locals_addr, self.frame().locals_addr());
579 debug_assert_eq!(self.layout_of_local(self.frame(), local, None)?, layout);
580 match self.frame_mut().locals[local].access_mut()? {
581 Operand::Indirect(mplace) => {
582 Left(MPlaceTy { mplace: *mplace, layout })
584 }
585 Operand::Immediate(local_val) => {
586 Right((local_val, layout, local))
588 }
589 }
590 }
591 }
592 })
593 }
594
595 #[inline(always)]
597 #[instrument(skip(self), level = "trace")]
598 pub fn write_immediate(
599 &mut self,
600 src: Immediate<M::Provenance>,
601 dest: &impl Writeable<'tcx, M::Provenance>,
602 ) -> InterpResult<'tcx> {
603 self.write_immediate_no_validate(src, dest)?;
604
605 if M::enforce_validity(self, dest.layout()) {
606 self.validate_operand(
609 &dest.to_place(),
610 M::enforce_validity_recursively(self, dest.layout()),
611 true,
612 )?;
613 }
614
615 interp_ok(())
616 }
617
618 #[inline(always)]
620 pub fn write_scalar(
621 &mut self,
622 val: impl Into<Scalar<M::Provenance>>,
623 dest: &impl Writeable<'tcx, M::Provenance>,
624 ) -> InterpResult<'tcx> {
625 self.write_immediate(Immediate::Scalar(val.into()), dest)
626 }
627
628 #[inline(always)]
630 pub fn write_pointer(
631 &mut self,
632 ptr: impl Into<Pointer<Option<M::Provenance>>>,
633 dest: &impl Writeable<'tcx, M::Provenance>,
634 ) -> InterpResult<'tcx> {
635 self.write_scalar(Scalar::from_maybe_pointer(ptr.into(), self), dest)
636 }
637
638 pub(super) fn write_immediate_no_validate(
642 &mut self,
643 src: Immediate<M::Provenance>,
644 dest: &impl Writeable<'tcx, M::Provenance>,
645 ) -> InterpResult<'tcx> {
646 assert!(dest.layout().is_sized(), "Cannot write unsized immediate data");
647
648 match self.as_mplace_or_mutable_local(&dest.to_place())? {
649 Right((local_val, local_layout, local)) => {
650 *local_val = src;
652 if !self.validation_in_progress() {
654 M::after_local_write(self, local, false)?;
655 }
656 if cfg!(debug_assertions) {
660 src.assert_matches_abi(
661 local_layout.backend_repr,
662 "invalid immediate for given destination place",
663 self,
664 );
665 }
666 }
667 Left(mplace) => {
668 self.write_immediate_to_mplace_no_validate(src, mplace.layout, mplace.mplace)?;
669 }
670 }
671 interp_ok(())
672 }
673
674 fn write_immediate_to_mplace_no_validate(
678 &mut self,
679 value: Immediate<M::Provenance>,
680 layout: TyAndLayout<'tcx>,
681 dest: MemPlace<M::Provenance>,
682 ) -> InterpResult<'tcx> {
683 value.assert_matches_abi(
686 layout.backend_repr,
687 "invalid immediate for given destination place",
688 self,
689 );
690 let tcx = *self.tcx;
696 let Some(mut alloc) = self.get_place_alloc_mut(&MPlaceTy { mplace: dest, layout })? else {
697 return interp_ok(());
699 };
700
701 match value {
702 Immediate::Scalar(scalar) => {
703 alloc.write_scalar(alloc_range(Size::ZERO, scalar.size()), scalar)
704 }
705 Immediate::ScalarPair(a_val, b_val) => {
706 let BackendRepr::ScalarPair(a, b) = layout.backend_repr else {
707 span_bug!(
708 self.cur_span(),
709 "write_immediate_to_mplace: invalid ScalarPair layout: {:#?}",
710 layout
711 )
712 };
713 let b_offset = a.size(&tcx).align_to(b.align(&tcx).abi);
714 assert!(b_offset.bytes() > 0); alloc.write_scalar(alloc_range(Size::ZERO, a_val.size()), a_val)?;
721 alloc.write_scalar(alloc_range(b_offset, b_val.size()), b_val)?;
722 interp_ok(())
724 }
725 Immediate::Uninit => alloc.write_uninit_full(),
726 }
727 }
728
729 pub fn write_uninit(
730 &mut self,
731 dest: &impl Writeable<'tcx, M::Provenance>,
732 ) -> InterpResult<'tcx> {
733 match self.as_mplace_or_mutable_local(&dest.to_place())? {
734 Right((local_val, _local_layout, local)) => {
735 *local_val = Immediate::Uninit;
736 if !self.validation_in_progress() {
738 M::after_local_write(self, local, false)?;
739 }
740 }
741 Left(mplace) => {
742 let Some(mut alloc) = self.get_place_alloc_mut(&mplace)? else {
743 return interp_ok(());
745 };
746 alloc.write_uninit_full()?;
747 }
748 }
749 interp_ok(())
750 }
751
752 pub fn clear_provenance(
754 &mut self,
755 dest: &impl Writeable<'tcx, M::Provenance>,
756 ) -> InterpResult<'tcx> {
757 match self.as_mplace_or_mutable_local(&dest.to_place())? {
758 Right((local_val, _local_layout, local)) => {
759 local_val.clear_provenance()?;
760 if !self.validation_in_progress() {
762 M::after_local_write(self, local, false)?;
763 }
764 }
765 Left(mplace) => {
766 let Some(mut alloc) = self.get_place_alloc_mut(&mplace)? else {
767 return interp_ok(());
769 };
770 alloc.clear_provenance()?;
771 }
772 }
773 interp_ok(())
774 }
775
776 #[inline(always)]
779 pub fn copy_op_allow_transmute(
780 &mut self,
781 src: &impl Projectable<'tcx, M::Provenance>,
782 dest: &impl Writeable<'tcx, M::Provenance>,
783 ) -> InterpResult<'tcx> {
784 self.copy_op_inner(src, dest, true)
785 }
786
787 #[inline(always)]
790 pub fn copy_op(
791 &mut self,
792 src: &impl Projectable<'tcx, M::Provenance>,
793 dest: &impl Writeable<'tcx, M::Provenance>,
794 ) -> InterpResult<'tcx> {
795 self.copy_op_inner(src, dest, false)
796 }
797
798 #[inline(always)]
801 #[instrument(skip(self), level = "trace")]
802 fn copy_op_inner(
803 &mut self,
804 src: &impl Projectable<'tcx, M::Provenance>,
805 dest: &impl Writeable<'tcx, M::Provenance>,
806 allow_transmute: bool,
807 ) -> InterpResult<'tcx> {
808 self.copy_op_no_validate(src, dest, allow_transmute)?;
815
816 if M::enforce_validity(self, dest.layout()) {
817 let dest = dest.to_place();
818 if src.layout().ty != dest.layout().ty {
822 self.validate_operand(
823 &dest.transmute(src.layout(), self)?,
824 M::enforce_validity_recursively(self, src.layout()),
825 true,
826 )?;
827 }
828 self.validate_operand(
829 &dest,
830 M::enforce_validity_recursively(self, dest.layout()),
831 true,
832 )?;
833 }
834
835 interp_ok(())
836 }
837
838 #[instrument(skip(self), level = "trace")]
843 fn copy_op_no_validate(
844 &mut self,
845 src: &impl Projectable<'tcx, M::Provenance>,
846 dest: &impl Writeable<'tcx, M::Provenance>,
847 allow_transmute: bool,
848 ) -> InterpResult<'tcx> {
849 let layout_compat =
852 mir_assign_valid_types(*self.tcx, self.typing_env, src.layout(), dest.layout());
853 if !allow_transmute && !layout_compat {
854 span_bug!(
855 self.cur_span(),
856 "type mismatch when copying!\nsrc: {},\ndest: {}",
857 src.layout().ty,
858 dest.layout().ty,
859 );
860 }
861
862 let src = match self.read_immediate_raw(src)? {
865 Right(src_val) => {
866 assert!(!src.layout().is_unsized());
867 assert!(!dest.layout().is_unsized());
868 assert_eq!(src.layout().size, dest.layout().size);
869 return if layout_compat {
871 self.write_immediate_no_validate(*src_val, dest)
872 } else {
873 let dest_mem = dest.force_mplace(self)?;
878 self.write_immediate_to_mplace_no_validate(
879 *src_val,
880 src.layout(),
881 dest_mem.mplace,
882 )
883 };
884 }
885 Left(mplace) => mplace,
886 };
887 trace!("copy_op: {:?} <- {:?}: {}", *dest, src, dest.layout().ty);
889
890 let dest = dest.force_mplace(self)?;
891 let Some((dest_size, _)) = self.size_and_align_of_mplace(&dest)? else {
892 span_bug!(self.cur_span(), "copy_op needs (dynamically) sized values")
893 };
894 if cfg!(debug_assertions) {
895 let src_size = self.size_and_align_of_mplace(&src)?.unwrap().0;
896 assert_eq!(src_size, dest_size, "Cannot copy differently-sized data");
897 } else {
898 assert_eq!(src.layout.size, dest.layout.size);
900 }
901
902 self.mem_copy(src.ptr(), dest.ptr(), dest_size, true)?;
910 self.check_misalign(src.mplace.misaligned, CheckAlignMsg::BasedOn)?;
911 self.check_misalign(dest.mplace.misaligned, CheckAlignMsg::BasedOn)?;
912 interp_ok(())
913 }
914
915 #[instrument(skip(self), level = "trace")]
920 pub fn force_allocation(
921 &mut self,
922 place: &PlaceTy<'tcx, M::Provenance>,
923 ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
924 let mplace = match place.place {
925 Place::Local { local, offset, locals_addr } => {
926 debug_assert_eq!(locals_addr, self.frame().locals_addr());
927 let whole_local = match self.frame_mut().locals[local].access_mut()? {
928 &mut Operand::Immediate(local_val) => {
929 let local_layout = self.layout_of_local(&self.frame(), local, None)?;
935 assert!(local_layout.is_sized(), "unsized locals cannot be immediate");
936 let mplace = self.allocate(local_layout, MemoryKind::Stack)?;
937 if !matches!(local_val, Immediate::Uninit) {
939 self.write_immediate_to_mplace_no_validate(
943 local_val,
944 local_layout,
945 mplace.mplace,
946 )?;
947 }
948 M::after_local_moved_to_memory(self, local, &mplace)?;
949 *self.frame_mut().locals[local].access_mut().unwrap() =
953 Operand::Indirect(mplace.mplace);
954 mplace.mplace
955 }
956 &mut Operand::Indirect(mplace) => mplace, };
958 if let Some(offset) = offset {
959 whole_local.offset_with_meta_(
961 offset,
962 OffsetMode::Wrapping,
963 MemPlaceMeta::None,
964 self,
965 )?
966 } else {
967 whole_local
969 }
970 }
971 Place::Ptr(mplace) => mplace,
972 };
973 interp_ok(MPlaceTy { mplace, layout: place.layout })
975 }
976
977 pub fn allocate_dyn(
978 &mut self,
979 layout: TyAndLayout<'tcx>,
980 kind: MemoryKind<M::MemoryKind>,
981 meta: MemPlaceMeta<M::Provenance>,
982 ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
983 let Some((size, align)) = self.size_and_align_of(&meta, &layout)? else {
984 span_bug!(self.cur_span(), "cannot allocate space for `extern` type, size is not known")
985 };
986 let ptr = self.allocate_ptr(size, align, kind, AllocInit::Uninit)?;
987 interp_ok(self.ptr_with_meta_to_mplace(ptr.into(), meta, layout, false))
988 }
989
990 pub fn allocate(
991 &mut self,
992 layout: TyAndLayout<'tcx>,
993 kind: MemoryKind<M::MemoryKind>,
994 ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
995 assert!(layout.is_sized());
996 self.allocate_dyn(layout, kind, MemPlaceMeta::None)
997 }
998
999 pub fn allocate_bytes_dedup(
1002 &mut self,
1003 bytes: &[u8],
1004 ) -> InterpResult<'tcx, Pointer<M::Provenance>> {
1005 let salt = M::get_global_alloc_salt(self, None);
1006 let id = self.tcx.allocate_bytes_dedup(bytes, salt);
1007
1008 M::adjust_alloc_root_pointer(
1010 &self,
1011 Pointer::from(id),
1012 M::GLOBAL_KIND.map(MemoryKind::Machine),
1013 )
1014 }
1015
1016 pub fn allocate_str_dedup(
1019 &mut self,
1020 s: &str,
1021 ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
1022 let bytes = s.as_bytes();
1023 let ptr = self.allocate_bytes_dedup(bytes)?;
1024
1025 let meta = Scalar::from_target_usize(u64::try_from(bytes.len()).unwrap(), self);
1027
1028 let layout = self.layout_of(self.tcx.types.str_).unwrap();
1030
1031 interp_ok(self.ptr_with_meta_to_mplace(
1033 ptr.into(),
1034 MemPlaceMeta::Meta(meta),
1035 layout,
1036 false,
1037 ))
1038 }
1039
1040 pub fn raw_const_to_mplace(
1041 &self,
1042 raw: mir::ConstAlloc<'tcx>,
1043 ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
1044 let _ = self.tcx.global_alloc(raw.alloc_id);
1046 let ptr = self.global_root_pointer(Pointer::from(raw.alloc_id))?;
1047 let layout = self.layout_of(raw.ty)?;
1048 interp_ok(self.ptr_to_mplace(ptr.into(), layout))
1049 }
1050}
1051
1052#[cfg(target_pointer_width = "64")]
1054mod size_asserts {
1055 use rustc_data_structures::static_assert_size;
1056
1057 use super::*;
1058 static_assert_size!(MemPlace, 48);
1060 static_assert_size!(MemPlaceMeta, 24);
1061 static_assert_size!(MPlaceTy<'_>, 64);
1062 static_assert_size!(Place, 48);
1063 static_assert_size!(PlaceTy<'_>, 64);
1064 }