rustc_const_eval/interpret/
stack.rs1use 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, MPlaceTy, Machine, MemPlace,
19 MemPlaceMeta, MemoryKind, Operand, Pointer, Provenance, ReturnAction, Scalar,
20 from_known_layout, 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: MPlaceTy<'tcx, Prov>,
81
82 pub locals: IndexVec<mir::Local, LocalState<'tcx, Prov>>,
90
91 tracing_span: SpanGuard,
95
96 pub(super) loc: Either<mir::Location, Span>,
105}
106
107#[derive(Clone, Copy, Eq, PartialEq, Debug)] pub enum StackPopCleanup {
109 Goto { ret: Option<mir::BasicBlock>, unwind: mir::UnwindAction },
115 Root { cleanup: bool },
120}
121
122pub struct StackPopInfo<'tcx, Prov: Provenance> {
124 pub return_action: ReturnAction,
127
128 pub return_to_block: StackPopCleanup,
130
131 pub return_place: MPlaceTy<'tcx, Prov>,
133}
134
135#[derive(Clone)]
137pub struct LocalState<'tcx, Prov: Provenance = CtfeProvenance> {
138 value: LocalValue<Prov>,
139 layout: Cell<Option<TyAndLayout<'tcx>>>,
142}
143
144impl<Prov: Provenance> std::fmt::Debug for LocalState<'_, Prov> {
145 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
146 f.debug_struct("LocalState")
147 .field("value", &self.value)
148 .field("ty", &self.layout.get().map(|l| l.ty))
149 .finish()
150 }
151}
152
153#[derive(Copy, Clone, Debug)] pub(super) enum LocalValue<Prov: Provenance = CtfeProvenance> {
159 Dead,
161 Live(Operand<Prov>),
167}
168
169impl<'tcx, Prov: Provenance> LocalState<'tcx, Prov> {
170 pub fn make_live_uninit(&mut self) {
171 self.value = LocalValue::Live(Operand::Immediate(Immediate::Uninit));
172 }
173
174 pub fn as_mplace_or_imm(
178 &self,
179 ) -> Option<Either<(Pointer<Option<Prov>>, MemPlaceMeta<Prov>), Immediate<Prov>>> {
180 match self.value {
181 LocalValue::Dead => None,
182 LocalValue::Live(Operand::Indirect(mplace)) => Some(Left((mplace.ptr, mplace.meta))),
183 LocalValue::Live(Operand::Immediate(imm)) => Some(Right(imm)),
184 }
185 }
186
187 #[inline(always)]
189 pub(super) fn access(&self) -> InterpResult<'tcx, &Operand<Prov>> {
190 match &self.value {
191 LocalValue::Dead => throw_ub!(DeadLocal), LocalValue::Live(val) => interp_ok(val),
193 }
194 }
195
196 #[inline(always)]
199 pub(super) fn access_mut(&mut self) -> InterpResult<'tcx, &mut Operand<Prov>> {
200 match &mut self.value {
201 LocalValue::Dead => throw_ub!(DeadLocal), LocalValue::Live(val) => interp_ok(val),
203 }
204 }
205}
206
207#[derive(Clone, Debug)]
209pub struct FrameInfo<'tcx> {
210 pub instance: ty::Instance<'tcx>,
211 pub span: Span,
212}
213
214impl<'tcx> fmt::Display for FrameInfo<'tcx> {
216 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
217 ty::tls::with(|tcx| {
218 if tcx.def_key(self.instance.def_id()).disambiguated_data.data == DefPathData::Closure {
219 write!(f, "inside closure")
220 } else {
221 write!(f, "inside `{}`", self.instance)
225 }
226 })
227 }
228}
229
230impl<'tcx> FrameInfo<'tcx> {
231 pub fn as_note(&self, tcx: TyCtxt<'tcx>) -> errors::FrameNote {
232 let span = self.span;
233 if tcx.def_key(self.instance.def_id()).disambiguated_data.data == DefPathData::Closure {
234 errors::FrameNote { where_: "closure", span, instance: String::new(), times: 0 }
235 } else {
236 let instance = format!("{}", self.instance);
237 errors::FrameNote { where_: "instance", span, instance, times: 0 }
241 }
242 }
243}
244
245impl<'tcx, Prov: Provenance> Frame<'tcx, Prov> {
246 pub fn with_extra<Extra>(self, extra: Extra) -> Frame<'tcx, Prov, Extra> {
247 Frame {
248 body: self.body,
249 instance: self.instance,
250 return_to_block: self.return_to_block,
251 return_place: self.return_place,
252 locals: self.locals,
253 loc: self.loc,
254 extra,
255 tracing_span: self.tracing_span,
256 }
257 }
258}
259
260impl<'tcx, Prov: Provenance, Extra> Frame<'tcx, Prov, Extra> {
261 pub fn current_loc(&self) -> Either<mir::Location, Span> {
269 self.loc
270 }
271
272 pub fn body(&self) -> &'tcx mir::Body<'tcx> {
273 self.body
274 }
275
276 pub fn instance(&self) -> ty::Instance<'tcx> {
277 self.instance
278 }
279
280 pub fn current_source_info(&self) -> Option<&mir::SourceInfo> {
282 self.loc.left().map(|loc| self.body.source_info(loc))
283 }
284
285 pub fn current_span(&self) -> Span {
286 match self.loc {
287 Left(loc) => self.body.source_info(loc).span,
288 Right(span) => span,
289 }
290 }
291
292 pub fn lint_root(&self, tcx: TyCtxt<'tcx>) -> Option<hir::HirId> {
293 self.current_source_info()
296 .and_then(|source_info| match &self.body.source_scopes[source_info.scope].local_data {
297 mir::ClearCrossCrate::Set(data) => Some(data.lint_root),
298 mir::ClearCrossCrate::Clear => None,
299 })
300 .or_else(|| {
301 let def_id = self.body.source.def_id().as_local();
302 def_id.map(|def_id| tcx.local_def_id_to_hir_id(def_id))
303 })
304 }
305
306 #[inline(always)]
309 pub(super) fn locals_addr(&self) -> usize {
310 self.locals.raw.as_ptr().addr()
311 }
312
313 #[must_use]
314 pub fn generate_stacktrace_from_stack(stack: &[Self]) -> Vec<FrameInfo<'tcx>> {
315 let mut frames = Vec::new();
316 for frame in stack.iter().rev() {
319 let span = match frame.loc {
320 Left(loc) => {
321 let mir::SourceInfo { mut span, scope } = *frame.body.source_info(loc);
323 let mut scope_data = &frame.body.source_scopes[scope];
324 while let Some((instance, call_span)) = scope_data.inlined {
325 frames.push(FrameInfo { span, instance });
326 span = call_span;
327 scope_data = &frame.body.source_scopes[scope_data.parent_scope.unwrap()];
328 }
329 span
330 }
331 Right(span) => span,
332 };
333 frames.push(FrameInfo { span, instance: frame.instance });
334 }
335 trace!("generate stacktrace: {:#?}", frames);
336 frames
337 }
338}
339
340impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
341 #[instrument(skip(self, body, return_place, return_to_block), level = "debug")]
346 pub(crate) fn push_stack_frame_raw(
347 &mut self,
348 instance: ty::Instance<'tcx>,
349 body: &'tcx mir::Body<'tcx>,
350 return_place: &MPlaceTy<'tcx, M::Provenance>,
351 return_to_block: StackPopCleanup,
352 ) -> InterpResult<'tcx> {
353 trace!("body: {:#?}", body);
354
355 debug_assert_eq!(
357 self.stack().is_empty(),
358 matches!(return_to_block, StackPopCleanup::Root { .. })
359 );
360
361 let dead_local = LocalState { value: LocalValue::Dead, layout: Cell::new(None) };
364 let locals = IndexVec::from_elem(dead_local, &body.local_decls);
365 let pre_frame = Frame {
366 body,
367 loc: Right(body.span), return_to_block,
369 return_place: return_place.clone(),
370 locals,
371 instance,
372 tracing_span: SpanGuard::new(),
373 extra: (),
374 };
375 let frame = M::init_frame(self, pre_frame)?;
376 self.stack_mut().push(frame);
377
378 for &const_ in body.required_consts() {
380 let c =
381 self.instantiate_from_current_frame_and_normalize_erasing_regions(const_.const_)?;
382 c.eval(*self.tcx, self.typing_env, const_.span).map_err(|err| {
383 err.emit_note(*self.tcx);
384 err
385 })?;
386 }
387
388 M::after_stack_push(self)?;
390 self.frame_mut().loc = Left(mir::Location::START);
391 let span = info_span!("frame", "{}", instance);
392 self.frame_mut().tracing_span.enter(span);
393
394 interp_ok(())
395 }
396
397 pub(super) fn pop_stack_frame_raw(
410 &mut self,
411 unwinding: bool,
412 ) -> InterpResult<'tcx, StackPopInfo<'tcx, M::Provenance>> {
413 let cleanup = self.cleanup_current_frame_locals()?;
414
415 let frame =
416 self.stack_mut().pop().expect("tried to pop a stack frame, but there were none");
417
418 let return_to_block = frame.return_to_block;
419 let return_place = frame.return_place.clone();
420
421 let return_action;
422 if cleanup {
423 return_action = M::after_stack_pop(self, frame, unwinding)?;
424 assert_ne!(return_action, ReturnAction::NoCleanup);
425 } else {
426 return_action = ReturnAction::NoCleanup;
427 };
428
429 interp_ok(StackPopInfo { return_action, return_to_block, return_place })
430 }
431
432 fn cleanup_current_frame_locals(&mut self) -> InterpResult<'tcx, bool> {
435 let return_to_block = self.frame().return_to_block;
439 let cleanup = match return_to_block {
440 StackPopCleanup::Goto { .. } => true,
441 StackPopCleanup::Root { cleanup, .. } => cleanup,
442 };
443
444 if cleanup {
445 let locals = mem::take(&mut self.frame_mut().locals);
447 for local in &locals {
448 self.deallocate_local(local.value)?;
449 }
450 }
451
452 interp_ok(cleanup)
453 }
454
455 pub(crate) fn storage_live_for_always_live_locals(&mut self) -> InterpResult<'tcx> {
458 self.storage_live(mir::RETURN_PLACE)?;
459
460 let body = self.body();
461 let always_live = always_storage_live_locals(body);
462 for local in body.vars_and_temps_iter() {
463 if always_live.contains(local) {
464 self.storage_live(local)?;
465 }
466 }
467 interp_ok(())
468 }
469
470 pub fn storage_live_dyn(
471 &mut self,
472 local: mir::Local,
473 meta: MemPlaceMeta<M::Provenance>,
474 ) -> InterpResult<'tcx> {
475 trace!("{:?} is now live", local);
476
477 fn is_very_trivially_sized(ty: Ty<'_>) -> bool {
479 match ty.kind() {
480 ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
481 | ty::Uint(_)
482 | ty::Int(_)
483 | ty::Bool
484 | ty::Float(_)
485 | ty::FnDef(..)
486 | ty::FnPtr(..)
487 | ty::RawPtr(..)
488 | ty::Char
489 | ty::Ref(..)
490 | ty::Coroutine(..)
491 | ty::CoroutineWitness(..)
492 | ty::Array(..)
493 | ty::Closure(..)
494 | ty::CoroutineClosure(..)
495 | ty::Never
496 | ty::Error(_)
497 | ty::Dynamic(_, _, ty::DynStar) => true,
498
499 ty::Str | ty::Slice(_) | ty::Dynamic(_, _, ty::Dyn) | ty::Foreign(..) => false,
500
501 ty::Tuple(tys) => tys.last().is_none_or(|ty| is_very_trivially_sized(*ty)),
502
503 ty::Pat(ty, ..) => is_very_trivially_sized(*ty),
504
505 ty::Adt(..) => false,
507
508 ty::UnsafeBinder(ty) => is_very_trivially_sized(ty.skip_binder()),
509
510 ty::Alias(..) | ty::Param(_) | ty::Placeholder(..) => false,
511
512 ty::Infer(ty::TyVar(_)) => false,
513
514 ty::Bound(..)
515 | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
516 bug!("`is_very_trivially_sized` applied to unexpected type: {}", ty)
517 }
518 }
519 }
520
521 let unsized_ = if is_very_trivially_sized(self.body().local_decls[local].ty) {
524 None
525 } else {
526 let layout = self.layout_of_local(self.frame(), local, None)?;
528 if layout.is_sized() { None } else { Some(layout) }
529 };
530
531 let local_val = LocalValue::Live(if let Some(layout) = unsized_ {
532 if !meta.has_meta() {
533 throw_unsup!(UnsizedLocal);
534 }
535 let dest_place = self.allocate_dyn(layout, MemoryKind::Stack, meta)?;
537 Operand::Indirect(*dest_place.mplace())
538 } else {
539 assert!(!meta.has_meta()); M::after_local_write(self, local, true)?;
544 Operand::Immediate(Immediate::Uninit)
550 });
551
552 let old = mem::replace(&mut self.frame_mut().locals[local].value, local_val);
554 self.deallocate_local(old)?;
555 interp_ok(())
556 }
557
558 #[inline(always)]
560 pub fn storage_live(&mut self, local: mir::Local) -> InterpResult<'tcx> {
561 self.storage_live_dyn(local, MemPlaceMeta::None)
562 }
563
564 pub fn storage_dead(&mut self, local: mir::Local) -> InterpResult<'tcx> {
565 assert!(local != mir::RETURN_PLACE, "Cannot make return place dead");
566 trace!("{:?} is now dead", local);
567
568 let old = mem::replace(&mut self.frame_mut().locals[local].value, LocalValue::Dead);
570 self.deallocate_local(old)?;
571 interp_ok(())
572 }
573
574 fn deallocate_local(&mut self, local: LocalValue<M::Provenance>) -> InterpResult<'tcx> {
575 if let LocalValue::Live(Operand::Indirect(MemPlace { ptr, .. })) = local {
576 trace!(
579 "deallocating local {:?}: {:?}",
580 local,
581 self.dump_alloc(ptr.provenance.unwrap().get_alloc_id().unwrap())
583 );
584 self.deallocate_ptr(ptr, None, MemoryKind::Stack)?;
585 };
586 interp_ok(())
587 }
588
589 #[inline(always)]
592 pub fn layout_of_local(
593 &self,
594 frame: &Frame<'tcx, M::Provenance, M::FrameExtra>,
595 local: mir::Local,
596 layout: Option<TyAndLayout<'tcx>>,
597 ) -> InterpResult<'tcx, TyAndLayout<'tcx>> {
598 let state = &frame.locals[local];
599 if let Some(layout) = state.layout.get() {
600 return interp_ok(layout);
601 }
602
603 let layout = from_known_layout(self.tcx, self.typing_env, layout, || {
604 let local_ty = frame.body.local_decls[local].ty;
605 let local_ty =
606 self.instantiate_from_frame_and_normalize_erasing_regions(frame, local_ty)?;
607 self.layout_of(local_ty).into()
608 })?;
609
610 state.layout.set(Some(layout));
612 interp_ok(layout)
613 }
614}
615
616impl<'tcx, Prov: Provenance> LocalState<'tcx, Prov> {
617 pub(super) fn print(
618 &self,
619 allocs: &mut Vec<Option<AllocId>>,
620 fmt: &mut std::fmt::Formatter<'_>,
621 ) -> std::fmt::Result {
622 match self.value {
623 LocalValue::Dead => write!(fmt, " is dead")?,
624 LocalValue::Live(Operand::Immediate(Immediate::Uninit)) => {
625 write!(fmt, " is uninitialized")?
626 }
627 LocalValue::Live(Operand::Indirect(mplace)) => {
628 write!(
629 fmt,
630 " by {} ref {:?}:",
631 match mplace.meta {
632 MemPlaceMeta::Meta(meta) => format!(" meta({meta:?})"),
633 MemPlaceMeta::None => String::new(),
634 },
635 mplace.ptr,
636 )?;
637 allocs.extend(mplace.ptr.provenance.map(Provenance::get_alloc_id));
638 }
639 LocalValue::Live(Operand::Immediate(Immediate::Scalar(val))) => {
640 write!(fmt, " {val:?}")?;
641 if let Scalar::Ptr(ptr, _size) = val {
642 allocs.push(ptr.provenance.get_alloc_id());
643 }
644 }
645 LocalValue::Live(Operand::Immediate(Immediate::ScalarPair(val1, val2))) => {
646 write!(fmt, " ({val1:?}, {val2:?})")?;
647 if let Scalar::Ptr(ptr, _size) = val1 {
648 allocs.push(ptr.provenance.get_alloc_id());
649 }
650 if let Scalar::Ptr(ptr, _size) = val2 {
651 allocs.push(ptr.provenance.get_alloc_id());
652 }
653 }
654 }
655
656 Ok(())
657 }
658}