1use std::cell::Cell;
4use std::{fmt, mem};
5
6use either::{Either, Left, Right};
7use rustc_hir as hir;
8use rustc_hir::definitions::DefPathData;
9use rustc_index::IndexVec;
10use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
11use rustc_middle::ty::{self, Ty, TyCtxt};
12use rustc_middle::{bug, mir};
13use rustc_mir_dataflow::impls::always_storage_live_locals;
14use rustc_span::Span;
15use tracing::{info_span, instrument, trace};
16
17use super::{
18 AllocId, CtfeProvenance, Immediate, InterpCx, InterpResult, Machine, MemPlace, MemPlaceMeta,
19 MemoryKind, Operand, PlaceTy, Pointer, Provenance, ReturnAction, Scalar, from_known_layout,
20 interp_ok, throw_ub, throw_unsup,
21};
22use crate::errors;
23
24struct SpanGuard(tracing::Span, std::marker::PhantomData<*const u8>);
27
28impl SpanGuard {
29 fn new() -> Self {
31 Self(tracing::Span::none(), std::marker::PhantomData)
32 }
33
34 fn enter(&mut self, span: tracing::Span) {
39 *self = Self(span, std::marker::PhantomData);
44 self.0.with_subscriber(|(id, dispatch)| {
45 dispatch.enter(id);
46 });
47 }
48}
49
50impl Drop for SpanGuard {
51 fn drop(&mut self) {
52 self.0.with_subscriber(|(id, dispatch)| {
53 dispatch.exit(id);
54 });
55 }
56}
57
58pub struct Frame<'tcx, Prov: Provenance = CtfeProvenance, Extra = ()> {
60 pub(super) body: &'tcx mir::Body<'tcx>,
65
66 pub(super) instance: ty::Instance<'tcx>,
68
69 pub extra: Extra,
71
72 return_to_block: StackPopCleanup,
77
78 pub return_place: PlaceTy<'tcx, Prov>,
83
84 pub locals: IndexVec<mir::Local, LocalState<'tcx, Prov>>,
92
93 tracing_span: SpanGuard,
97
98 pub(super) loc: Either<mir::Location, Span>,
107}
108
109#[derive(Clone, Copy, Eq, PartialEq, Debug)] pub enum StackPopCleanup {
111 Goto { ret: Option<mir::BasicBlock>, unwind: mir::UnwindAction },
117 Root { cleanup: bool },
122}
123
124pub struct StackPopInfo<'tcx, Prov: Provenance> {
126 pub return_action: ReturnAction,
129
130 pub return_to_block: StackPopCleanup,
132
133 pub return_place: PlaceTy<'tcx, Prov>,
135}
136
137#[derive(Clone)]
139pub struct LocalState<'tcx, Prov: Provenance = CtfeProvenance> {
140 value: LocalValue<Prov>,
141 layout: Cell<Option<TyAndLayout<'tcx>>>,
144}
145
146impl<Prov: Provenance> std::fmt::Debug for LocalState<'_, Prov> {
147 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
148 f.debug_struct("LocalState")
149 .field("value", &self.value)
150 .field("ty", &self.layout.get().map(|l| l.ty))
151 .finish()
152 }
153}
154
155#[derive(Copy, Clone, Debug)] pub(super) enum LocalValue<Prov: Provenance = CtfeProvenance> {
161 Dead,
163 Live(Operand<Prov>),
169}
170
171impl<'tcx, Prov: Provenance> LocalState<'tcx, Prov> {
172 pub fn make_live_uninit(&mut self) {
173 self.value = LocalValue::Live(Operand::Immediate(Immediate::Uninit));
174 }
175
176 pub fn as_mplace_or_imm(
180 &self,
181 ) -> Option<Either<(Pointer<Option<Prov>>, MemPlaceMeta<Prov>), Immediate<Prov>>> {
182 match self.value {
183 LocalValue::Dead => None,
184 LocalValue::Live(Operand::Indirect(mplace)) => Some(Left((mplace.ptr, mplace.meta))),
185 LocalValue::Live(Operand::Immediate(imm)) => Some(Right(imm)),
186 }
187 }
188
189 #[inline(always)]
191 pub(super) fn access(&self) -> InterpResult<'tcx, &Operand<Prov>> {
192 match &self.value {
193 LocalValue::Dead => throw_ub!(DeadLocal), LocalValue::Live(val) => interp_ok(val),
195 }
196 }
197
198 #[inline(always)]
201 pub(super) fn access_mut(&mut self) -> InterpResult<'tcx, &mut Operand<Prov>> {
202 match &mut self.value {
203 LocalValue::Dead => throw_ub!(DeadLocal), LocalValue::Live(val) => interp_ok(val),
205 }
206 }
207}
208
209#[derive(Clone, Debug)]
211pub struct FrameInfo<'tcx> {
212 pub instance: ty::Instance<'tcx>,
213 pub span: Span,
214}
215
216impl<'tcx> fmt::Display for FrameInfo<'tcx> {
218 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
219 ty::tls::with(|tcx| {
220 if tcx.def_key(self.instance.def_id()).disambiguated_data.data == DefPathData::Closure {
221 write!(f, "inside closure")
222 } else {
223 write!(f, "inside `{}`", self.instance)
227 }
228 })
229 }
230}
231
232impl<'tcx> FrameInfo<'tcx> {
233 pub fn as_note(&self, tcx: TyCtxt<'tcx>) -> errors::FrameNote {
234 let span = self.span;
235 if tcx.def_key(self.instance.def_id()).disambiguated_data.data == DefPathData::Closure {
236 errors::FrameNote {
237 where_: "closure",
238 span,
239 instance: String::new(),
240 times: 0,
241 has_label: false,
242 }
243 } else {
244 let instance = format!("{}", self.instance);
245 errors::FrameNote { where_: "instance", span, instance, times: 0, has_label: false }
249 }
250 }
251}
252
253impl<'tcx, Prov: Provenance> Frame<'tcx, Prov> {
254 pub fn with_extra<Extra>(self, extra: Extra) -> Frame<'tcx, Prov, Extra> {
255 Frame {
256 body: self.body,
257 instance: self.instance,
258 return_to_block: self.return_to_block,
259 return_place: self.return_place,
260 locals: self.locals,
261 loc: self.loc,
262 extra,
263 tracing_span: self.tracing_span,
264 }
265 }
266}
267
268impl<'tcx, Prov: Provenance, Extra> Frame<'tcx, Prov, Extra> {
269 pub fn current_loc(&self) -> Either<mir::Location, Span> {
277 self.loc
278 }
279
280 pub fn body(&self) -> &'tcx mir::Body<'tcx> {
281 self.body
282 }
283
284 pub fn instance(&self) -> ty::Instance<'tcx> {
285 self.instance
286 }
287
288 pub fn current_source_info(&self) -> Option<&mir::SourceInfo> {
290 self.loc.left().map(|loc| self.body.source_info(loc))
291 }
292
293 pub fn current_span(&self) -> Span {
294 match self.loc {
295 Left(loc) => self.body.source_info(loc).span,
296 Right(span) => span,
297 }
298 }
299
300 pub fn lint_root(&self, tcx: TyCtxt<'tcx>) -> Option<hir::HirId> {
301 self.current_source_info()
304 .and_then(|source_info| match &self.body.source_scopes[source_info.scope].local_data {
305 mir::ClearCrossCrate::Set(data) => Some(data.lint_root),
306 mir::ClearCrossCrate::Clear => None,
307 })
308 .or_else(|| {
309 let def_id = self.body.source.def_id().as_local();
310 def_id.map(|def_id| tcx.local_def_id_to_hir_id(def_id))
311 })
312 }
313
314 #[inline(always)]
317 pub(super) fn locals_addr(&self) -> usize {
318 self.locals.raw.as_ptr().addr()
319 }
320
321 #[must_use]
322 pub fn generate_stacktrace_from_stack(stack: &[Self]) -> Vec<FrameInfo<'tcx>> {
323 let mut frames = Vec::new();
324 for frame in stack.iter().rev() {
327 let span = match frame.loc {
328 Left(loc) => {
329 let mir::SourceInfo { mut span, scope } = *frame.body.source_info(loc);
331 let mut scope_data = &frame.body.source_scopes[scope];
332 while let Some((instance, call_span)) = scope_data.inlined {
333 frames.push(FrameInfo { span, instance });
334 span = call_span;
335 scope_data = &frame.body.source_scopes[scope_data.parent_scope.unwrap()];
336 }
337 span
338 }
339 Right(span) => span,
340 };
341 frames.push(FrameInfo { span, instance: frame.instance });
342 }
343 trace!("generate stacktrace: {:#?}", frames);
344 frames
345 }
346}
347
348impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
349 #[instrument(skip(self, body, return_place, return_to_block), level = "debug")]
354 pub(crate) fn push_stack_frame_raw(
355 &mut self,
356 instance: ty::Instance<'tcx>,
357 body: &'tcx mir::Body<'tcx>,
358 return_place: &PlaceTy<'tcx, M::Provenance>,
359 return_to_block: StackPopCleanup,
360 ) -> InterpResult<'tcx> {
361 trace!("body: {:#?}", body);
362
363 debug_assert_eq!(
365 self.stack().is_empty(),
366 matches!(return_to_block, StackPopCleanup::Root { .. })
367 );
368
369 let dead_local = LocalState { value: LocalValue::Dead, layout: Cell::new(None) };
372 let locals = IndexVec::from_elem(dead_local, &body.local_decls);
373 let pre_frame = Frame {
374 body,
375 loc: Right(body.span), return_to_block,
377 return_place: return_place.clone(),
378 locals,
379 instance,
380 tracing_span: SpanGuard::new(),
381 extra: (),
382 };
383 let frame = M::init_frame(self, pre_frame)?;
384 self.stack_mut().push(frame);
385
386 for &const_ in body.required_consts() {
388 let c =
389 self.instantiate_from_current_frame_and_normalize_erasing_regions(const_.const_)?;
390 c.eval(*self.tcx, self.typing_env, const_.span).map_err(|err| {
391 err.emit_note(*self.tcx);
392 err
393 })?;
394 }
395
396 M::after_stack_push(self)?;
398 self.frame_mut().loc = Left(mir::Location::START);
399 let span = info_span!("frame", "{}", instance);
400 self.frame_mut().tracing_span.enter(span);
401
402 interp_ok(())
403 }
404
405 pub(super) fn pop_stack_frame_raw(
419 &mut self,
420 unwinding: bool,
421 copy_ret_val: impl FnOnce(&mut Self, &PlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx>,
422 ) -> InterpResult<'tcx, StackPopInfo<'tcx, M::Provenance>> {
423 M::before_stack_pop(self)?;
424 let frame =
425 self.stack_mut().pop().expect("tried to pop a stack frame, but there were none");
426
427 if !unwinding {
429 copy_ret_val(self, &frame.return_place)?;
430 }
431
432 let return_to_block = frame.return_to_block;
433 let return_place = frame.return_place.clone();
434
435 let cleanup = match return_to_block {
439 StackPopCleanup::Goto { .. } => true,
440 StackPopCleanup::Root { cleanup, .. } => cleanup,
441 };
442
443 let return_action = if cleanup {
444 for local in &frame.locals {
446 self.deallocate_local(local.value)?;
447 }
448
449 let return_action = M::after_stack_pop(self, frame, unwinding)?;
451 assert_ne!(return_action, ReturnAction::NoCleanup);
452 return_action
453 } else {
454 ReturnAction::NoCleanup
456 };
457
458 interp_ok(StackPopInfo { return_action, return_to_block, return_place })
459 }
460
461 pub(crate) fn storage_live_for_always_live_locals(&mut self) -> InterpResult<'tcx> {
464 self.storage_live(mir::RETURN_PLACE)?;
465
466 let body = self.body();
467 let always_live = always_storage_live_locals(body);
468 for local in body.vars_and_temps_iter() {
469 if always_live.contains(local) {
470 self.storage_live(local)?;
471 }
472 }
473 interp_ok(())
474 }
475
476 pub fn storage_live_dyn(
477 &mut self,
478 local: mir::Local,
479 meta: MemPlaceMeta<M::Provenance>,
480 ) -> InterpResult<'tcx> {
481 trace!("{:?} is now live", local);
482
483 fn is_very_trivially_sized(ty: Ty<'_>) -> bool {
485 match ty.kind() {
486 ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
487 | ty::Uint(_)
488 | ty::Int(_)
489 | ty::Bool
490 | ty::Float(_)
491 | ty::FnDef(..)
492 | ty::FnPtr(..)
493 | ty::RawPtr(..)
494 | ty::Char
495 | ty::Ref(..)
496 | ty::Coroutine(..)
497 | ty::CoroutineWitness(..)
498 | ty::Array(..)
499 | ty::Closure(..)
500 | ty::CoroutineClosure(..)
501 | ty::Never
502 | ty::Error(_)
503 | ty::Dynamic(_, _, ty::DynStar) => true,
504
505 ty::Str | ty::Slice(_) | ty::Dynamic(_, _, ty::Dyn) | ty::Foreign(..) => false,
506
507 ty::Tuple(tys) => tys.last().is_none_or(|ty| is_very_trivially_sized(*ty)),
508
509 ty::Pat(ty, ..) => is_very_trivially_sized(*ty),
510
511 ty::Adt(..) => false,
513
514 ty::UnsafeBinder(ty) => is_very_trivially_sized(ty.skip_binder()),
515
516 ty::Alias(..) | ty::Param(_) | ty::Placeholder(..) => false,
517
518 ty::Infer(ty::TyVar(_)) => false,
519
520 ty::Bound(..)
521 | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
522 bug!("`is_very_trivially_sized` applied to unexpected type: {}", ty)
523 }
524 }
525 }
526
527 let unsized_ = if is_very_trivially_sized(self.body().local_decls[local].ty) {
530 None
531 } else {
532 let layout = self.layout_of_local(self.frame(), local, None)?;
534 if layout.is_sized() { None } else { Some(layout) }
535 };
536
537 let local_val = LocalValue::Live(if let Some(layout) = unsized_ {
538 if !meta.has_meta() {
539 throw_unsup!(UnsizedLocal);
540 }
541 let dest_place = self.allocate_dyn(layout, MemoryKind::Stack, meta)?;
543 Operand::Indirect(*dest_place.mplace())
544 } else {
545 assert!(!meta.has_meta()); M::after_local_write(self, local, true)?;
550 Operand::Immediate(Immediate::Uninit)
556 });
557
558 let old = mem::replace(&mut self.frame_mut().locals[local].value, local_val);
560 self.deallocate_local(old)?;
561 interp_ok(())
562 }
563
564 #[inline(always)]
566 pub fn storage_live(&mut self, local: mir::Local) -> InterpResult<'tcx> {
567 self.storage_live_dyn(local, MemPlaceMeta::None)
568 }
569
570 pub fn storage_dead(&mut self, local: mir::Local) -> InterpResult<'tcx> {
571 assert!(local != mir::RETURN_PLACE, "Cannot make return place dead");
572 trace!("{:?} is now dead", local);
573
574 let old = mem::replace(&mut self.frame_mut().locals[local].value, LocalValue::Dead);
576 self.deallocate_local(old)?;
577 interp_ok(())
578 }
579
580 fn deallocate_local(&mut self, local: LocalValue<M::Provenance>) -> InterpResult<'tcx> {
581 if let LocalValue::Live(Operand::Indirect(MemPlace { ptr, .. })) = local {
582 trace!(
585 "deallocating local {:?}: {:?}",
586 local,
587 self.dump_alloc(ptr.provenance.unwrap().get_alloc_id().unwrap())
589 );
590 self.deallocate_ptr(ptr, None, MemoryKind::Stack)?;
591 };
592 interp_ok(())
593 }
594
595 #[inline(always)]
598 pub fn layout_of_local(
599 &self,
600 frame: &Frame<'tcx, M::Provenance, M::FrameExtra>,
601 local: mir::Local,
602 layout: Option<TyAndLayout<'tcx>>,
603 ) -> InterpResult<'tcx, TyAndLayout<'tcx>> {
604 let state = &frame.locals[local];
605 if let Some(layout) = state.layout.get() {
606 return interp_ok(layout);
607 }
608
609 let layout = from_known_layout(self.tcx, self.typing_env, layout, || {
610 let local_ty = frame.body.local_decls[local].ty;
611 let local_ty =
612 self.instantiate_from_frame_and_normalize_erasing_regions(frame, local_ty)?;
613 self.layout_of(local_ty).into()
614 })?;
615
616 state.layout.set(Some(layout));
618 interp_ok(layout)
619 }
620}
621
622impl<'tcx, Prov: Provenance> LocalState<'tcx, Prov> {
623 pub(super) fn print(
624 &self,
625 allocs: &mut Vec<Option<AllocId>>,
626 fmt: &mut std::fmt::Formatter<'_>,
627 ) -> std::fmt::Result {
628 match self.value {
629 LocalValue::Dead => write!(fmt, " is dead")?,
630 LocalValue::Live(Operand::Immediate(Immediate::Uninit)) => {
631 write!(fmt, " is uninitialized")?
632 }
633 LocalValue::Live(Operand::Indirect(mplace)) => {
634 write!(
635 fmt,
636 " by {} ref {:?}:",
637 match mplace.meta {
638 MemPlaceMeta::Meta(meta) => format!(" meta({meta:?})"),
639 MemPlaceMeta::None => String::new(),
640 },
641 mplace.ptr,
642 )?;
643 allocs.extend(mplace.ptr.provenance.map(Provenance::get_alloc_id));
644 }
645 LocalValue::Live(Operand::Immediate(Immediate::Scalar(val))) => {
646 write!(fmt, " {val:?}")?;
647 if let Scalar::Ptr(ptr, _size) = val {
648 allocs.push(ptr.provenance.get_alloc_id());
649 }
650 }
651 LocalValue::Live(Operand::Immediate(Immediate::ScalarPair(val1, val2))) => {
652 write!(fmt, " ({val1:?}, {val2:?})")?;
653 if let Scalar::Ptr(ptr, _size) = val1 {
654 allocs.push(ptr.provenance.get_alloc_id());
655 }
656 if let Scalar::Ptr(ptr, _size) = val2 {
657 allocs.push(ptr.provenance.get_alloc_id());
658 }
659 }
660 }
661
662 Ok(())
663 }
664}