1use std::assert_matches::debug_assert_matches;
2
3use either::{Left, Right};
4use rustc_abi::{Align, HasDataLayout, Size, TargetDataLayout};
5use rustc_errors::DiagCtxtHandle;
6use rustc_hir::def_id::DefId;
7use rustc_middle::mir::interpret::{ErrorHandled, InvalidMetaKind, ReportedErrorInfo};
8use rustc_middle::query::TyCtxtAt;
9use rustc_middle::ty::layout::{
10 self, FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOfHelpers, TyAndLayout,
11};
12use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeFoldable, TypingEnv, Variance};
13use rustc_middle::{mir, span_bug};
14use rustc_session::Limit;
15use rustc_span::Span;
16use rustc_target::callconv::FnAbi;
17use tracing::{debug, trace};
18
19use super::{
20 Frame, FrameInfo, GlobalId, InterpErrorInfo, InterpErrorKind, InterpResult, MPlaceTy, Machine,
21 MemPlaceMeta, Memory, OpTy, Place, PlaceTy, PointerArithmetic, Projectable, Provenance,
22 err_inval, interp_ok, throw_inval, throw_ub, throw_ub_custom,
23};
24use crate::{ReportErrorExt, fluent_generated as fluent, util};
25
26pub struct InterpCx<'tcx, M: Machine<'tcx>> {
27 pub machine: M,
31
32 pub tcx: TyCtxtAt<'tcx>,
36
37 pub(super) typing_env: ty::TypingEnv<'tcx>,
40
41 pub memory: Memory<'tcx, M>,
43
44 pub recursion_limit: Limit,
46}
47
48impl<'tcx, M: Machine<'tcx>> HasDataLayout for InterpCx<'tcx, M> {
49 #[inline]
50 fn data_layout(&self) -> &TargetDataLayout {
51 &self.tcx.data_layout
52 }
53}
54
55impl<'tcx, M> layout::HasTyCtxt<'tcx> for InterpCx<'tcx, M>
56where
57 M: Machine<'tcx>,
58{
59 #[inline]
60 fn tcx(&self) -> TyCtxt<'tcx> {
61 *self.tcx
62 }
63}
64
65impl<'tcx, M> layout::HasTypingEnv<'tcx> for InterpCx<'tcx, M>
66where
67 M: Machine<'tcx>,
68{
69 fn typing_env(&self) -> ty::TypingEnv<'tcx> {
70 self.typing_env
71 }
72}
73
74impl<'tcx, M: Machine<'tcx>> LayoutOfHelpers<'tcx> for InterpCx<'tcx, M> {
75 type LayoutOfResult = Result<TyAndLayout<'tcx>, InterpErrorKind<'tcx>>;
76
77 #[inline]
78 fn layout_tcx_at_span(&self) -> Span {
79 self.tcx.span
81 }
82
83 #[inline]
84 fn handle_layout_err(
85 &self,
86 err: LayoutError<'tcx>,
87 _: Span,
88 _: Ty<'tcx>,
89 ) -> InterpErrorKind<'tcx> {
90 err_inval!(Layout(err))
91 }
92}
93
94impl<'tcx, M: Machine<'tcx>> FnAbiOfHelpers<'tcx> for InterpCx<'tcx, M> {
95 type FnAbiOfResult = Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, InterpErrorKind<'tcx>>;
96
97 fn handle_fn_abi_err(
98 &self,
99 err: FnAbiError<'tcx>,
100 _span: Span,
101 _fn_abi_request: FnAbiRequest<'tcx>,
102 ) -> InterpErrorKind<'tcx> {
103 match err {
104 FnAbiError::Layout(err) => err_inval!(Layout(err)),
105 }
106 }
107}
108
109pub(super) fn mir_assign_valid_types<'tcx>(
112 tcx: TyCtxt<'tcx>,
113 typing_env: TypingEnv<'tcx>,
114 src: TyAndLayout<'tcx>,
115 dest: TyAndLayout<'tcx>,
116) -> bool {
117 if util::relate_types(tcx, typing_env, Variance::Covariant, src.ty, dest.ty) {
122 if cfg!(debug_assertions) || src.ty != dest.ty {
128 assert_eq!(src.layout, dest.layout);
129 }
130 true
131 } else {
132 false
133 }
134}
135
136#[cfg_attr(not(debug_assertions), inline(always))]
139pub(super) fn from_known_layout<'tcx>(
140 tcx: TyCtxtAt<'tcx>,
141 typing_env: TypingEnv<'tcx>,
142 known_layout: Option<TyAndLayout<'tcx>>,
143 compute: impl FnOnce() -> InterpResult<'tcx, TyAndLayout<'tcx>>,
144) -> InterpResult<'tcx, TyAndLayout<'tcx>> {
145 match known_layout {
146 None => compute(),
147 Some(known_layout) => {
148 if cfg!(debug_assertions) {
149 let check_layout = compute()?;
150 if !mir_assign_valid_types(tcx.tcx, typing_env, check_layout, known_layout) {
151 span_bug!(
152 tcx.span,
153 "expected type differs from actual type.\nexpected: {}\nactual: {}",
154 known_layout.ty,
155 check_layout.ty,
156 );
157 }
158 }
159 interp_ok(known_layout)
160 }
161 }
162}
163
164pub fn format_interp_error<'tcx>(dcx: DiagCtxtHandle<'_>, e: InterpErrorInfo<'tcx>) -> String {
171 let (e, backtrace) = e.into_parts();
172 backtrace.print_backtrace();
173 #[allow(rustc::untranslatable_diagnostic)]
176 let mut diag = dcx.struct_allow("");
177 let msg = e.diagnostic_message();
178 e.add_args(&mut diag);
179 let s = dcx.eagerly_translate_to_string(msg, diag.args.iter());
180 diag.cancel();
181 s
182}
183
184impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
185 pub fn new(
186 tcx: TyCtxt<'tcx>,
187 root_span: Span,
188 typing_env: ty::TypingEnv<'tcx>,
189 machine: M,
190 ) -> Self {
191 debug_assert_matches!(typing_env.typing_mode, ty::TypingMode::PostAnalysis);
196 InterpCx {
197 machine,
198 tcx: tcx.at(root_span),
199 typing_env,
200 memory: Memory::new(),
201 recursion_limit: tcx.recursion_limit(),
202 }
203 }
204
205 #[inline(always)]
208 pub fn cur_span(&self) -> Span {
209 self.stack().last().map_or(self.tcx.span, |f| f.current_span())
212 }
213
214 pub(crate) fn stack(&self) -> &[Frame<'tcx, M::Provenance, M::FrameExtra>] {
215 M::stack(self)
216 }
217
218 #[inline(always)]
219 pub(crate) fn stack_mut(&mut self) -> &mut Vec<Frame<'tcx, M::Provenance, M::FrameExtra>> {
220 M::stack_mut(self)
221 }
222
223 #[inline(always)]
224 pub fn frame_idx(&self) -> usize {
225 let stack = self.stack();
226 assert!(!stack.is_empty());
227 stack.len() - 1
228 }
229
230 #[inline(always)]
231 pub fn frame(&self) -> &Frame<'tcx, M::Provenance, M::FrameExtra> {
232 self.stack().last().expect("no call frames exist")
233 }
234
235 #[inline(always)]
236 pub fn frame_mut(&mut self) -> &mut Frame<'tcx, M::Provenance, M::FrameExtra> {
237 self.stack_mut().last_mut().expect("no call frames exist")
238 }
239
240 #[inline(always)]
241 pub fn body(&self) -> &'tcx mir::Body<'tcx> {
242 self.frame().body
243 }
244
245 #[inline]
246 pub fn type_is_freeze(&self, ty: Ty<'tcx>) -> bool {
247 ty.is_freeze(*self.tcx, self.typing_env)
248 }
249
250 pub fn load_mir(
251 &self,
252 instance: ty::InstanceKind<'tcx>,
253 promoted: Option<mir::Promoted>,
254 ) -> InterpResult<'tcx, &'tcx mir::Body<'tcx>> {
255 trace!("load mir(instance={:?}, promoted={:?})", instance, promoted);
256 let body = if let Some(promoted) = promoted {
257 let def = instance.def_id();
258 &self.tcx.promoted_mir(def)[promoted]
259 } else {
260 M::load_mir(self, instance)?
261 };
262 if let Some(err) = body.tainted_by_errors {
264 throw_inval!(AlreadyReported(ReportedErrorInfo::non_const_eval_error(err)));
265 }
266 interp_ok(body)
267 }
268
269 pub(super) fn instantiate_from_current_frame_and_normalize_erasing_regions<
272 T: TypeFoldable<TyCtxt<'tcx>>,
273 >(
274 &self,
275 value: T,
276 ) -> Result<T, ErrorHandled> {
277 self.instantiate_from_frame_and_normalize_erasing_regions(self.frame(), value)
278 }
279
280 pub(super) fn instantiate_from_frame_and_normalize_erasing_regions<
283 T: TypeFoldable<TyCtxt<'tcx>>,
284 >(
285 &self,
286 frame: &Frame<'tcx, M::Provenance, M::FrameExtra>,
287 value: T,
288 ) -> Result<T, ErrorHandled> {
289 frame
290 .instance
291 .try_instantiate_mir_and_normalize_erasing_regions(
292 *self.tcx,
293 self.typing_env,
294 ty::EarlyBinder::bind(value),
295 )
296 .map_err(|_| ErrorHandled::TooGeneric(self.cur_span()))
297 }
298
299 pub(super) fn resolve(
301 &self,
302 def: DefId,
303 args: GenericArgsRef<'tcx>,
304 ) -> InterpResult<'tcx, ty::Instance<'tcx>> {
305 trace!("resolve: {:?}, {:#?}", def, args);
306 trace!("typing_env: {:#?}", self.typing_env);
307 trace!("args: {:#?}", args);
308 match ty::Instance::try_resolve(*self.tcx, self.typing_env, def, args) {
309 Ok(Some(instance)) => interp_ok(instance),
310 Ok(None) => throw_inval!(TooGeneric),
311
312 Err(error_guaranteed) => throw_inval!(AlreadyReported(
314 ReportedErrorInfo::non_const_eval_error(error_guaranteed)
315 )),
316 }
317 }
318
319 pub(crate) fn find_closest_untracked_caller_location(&self) -> Span {
323 for frame in self.stack().iter().rev() {
324 debug!("find_closest_untracked_caller_location: checking frame {:?}", frame.instance);
325
326 let loc = frame.loc.left().unwrap();
329
330 let mut source_info = *frame.body.source_info(loc);
333
334 let block = &frame.body.basic_blocks[loc.block];
336 if loc.statement_index == block.statements.len() {
337 debug!(
338 "find_closest_untracked_caller_location: got terminator {:?} ({:?})",
339 block.terminator(),
340 block.terminator().kind,
341 );
342 if let mir::TerminatorKind::Call { fn_span, .. } = block.terminator().kind {
343 source_info.span = fn_span;
344 }
345 }
346
347 let caller_location = if frame.instance.def.requires_caller_location(*self.tcx) {
348 Some(Err(()))
351 } else {
352 None
353 };
354 if let Ok(span) =
355 frame.body.caller_location_span(source_info, caller_location, *self.tcx, Ok)
356 {
357 return span;
358 }
359 }
360
361 span_bug!(self.cur_span(), "no non-`#[track_caller]` frame found")
362 }
363
364 pub(super) fn size_and_align_of(
368 &self,
369 metadata: &MemPlaceMeta<M::Provenance>,
370 layout: &TyAndLayout<'tcx>,
371 ) -> InterpResult<'tcx, Option<(Size, Align)>> {
372 if layout.is_sized() {
373 return interp_ok(Some((layout.size, layout.align.abi)));
374 }
375 match layout.ty.kind() {
376 ty::Adt(..) | ty::Tuple(..) => {
377 assert!(!layout.ty.is_simd());
382 assert!(layout.fields.count() > 0);
383 trace!("DST layout: {:?}", layout);
384
385 let unsized_offset_unadjusted = layout.fields.offset(layout.fields.count() - 1);
386 let sized_align = layout.align.abi;
387
388 let field = layout.field(self, layout.fields.count() - 1);
392 let Some((unsized_size, mut unsized_align)) =
393 self.size_and_align_of(metadata, &field)?
394 else {
395 return interp_ok(None);
398 };
399
400 if let ty::Adt(def, _) = layout.ty.kind() {
404 if let Some(packed) = def.repr().pack {
405 unsized_align = unsized_align.min(packed);
406 }
407 }
408
409 let full_align = sized_align.max(unsized_align);
412
413 let unsized_offset_adjusted = unsized_offset_unadjusted.align_to(unsized_align);
416 let full_size = (unsized_offset_adjusted + unsized_size).align_to(full_align);
417
418 assert_eq!(
420 full_size,
421 (unsized_offset_unadjusted + unsized_size).align_to(full_align)
422 );
423
424 if full_size > self.max_size_of_val() {
426 throw_ub!(InvalidMeta(InvalidMetaKind::TooBig));
427 }
428 interp_ok(Some((full_size, full_align)))
429 }
430 ty::Dynamic(expected_trait, _, ty::Dyn) => {
431 let vtable = metadata.unwrap_meta().to_pointer(self)?;
432 interp_ok(Some(self.get_vtable_size_and_align(vtable, Some(expected_trait))?))
434 }
435
436 ty::Slice(_) | ty::Str => {
437 let len = metadata.unwrap_meta().to_target_usize(self)?;
438 let elem = layout.field(self, 0);
439
440 let size = elem.size.bytes().saturating_mul(len); let size = Size::from_bytes(size);
443 if size > self.max_size_of_val() {
444 throw_ub!(InvalidMeta(InvalidMetaKind::SliceTooBig));
445 }
446 interp_ok(Some((size, elem.align.abi)))
447 }
448
449 ty::Foreign(_) => interp_ok(None),
450
451 _ => span_bug!(self.cur_span(), "size_and_align_of::<{}> not supported", layout.ty),
452 }
453 }
454 #[inline]
455 pub fn size_and_align_of_mplace(
456 &self,
457 mplace: &MPlaceTy<'tcx, M::Provenance>,
458 ) -> InterpResult<'tcx, Option<(Size, Align)>> {
459 self.size_and_align_of(&mplace.meta(), &mplace.layout)
460 }
461
462 #[inline]
464 pub fn go_to_block(&mut self, target: mir::BasicBlock) {
465 self.frame_mut().loc = Left(mir::Location { block: target, statement_index: 0 });
466 }
467
468 pub fn return_to_block(&mut self, target: Option<mir::BasicBlock>) -> InterpResult<'tcx> {
473 if let Some(target) = target {
474 self.go_to_block(target);
475 interp_ok(())
476 } else {
477 throw_ub!(Unreachable)
478 }
479 }
480
481 #[cold] pub fn unwind_to_block(&mut self, target: mir::UnwindAction) -> InterpResult<'tcx> {
491 self.frame_mut().loc = match target {
492 mir::UnwindAction::Cleanup(block) => Left(mir::Location { block, statement_index: 0 }),
493 mir::UnwindAction::Continue => Right(self.frame_mut().body.span),
494 mir::UnwindAction::Unreachable => {
495 throw_ub_custom!(fluent::const_eval_unreachable_unwind);
496 }
497 mir::UnwindAction::Terminate(reason) => {
498 self.frame_mut().loc = Right(self.frame_mut().body.span);
499 M::unwind_terminate(self, reason)?;
500 return interp_ok(());
503 }
504 };
505 interp_ok(())
506 }
507
508 pub fn ctfe_query<T>(
511 &self,
512 query: impl FnOnce(TyCtxtAt<'tcx>) -> Result<T, ErrorHandled>,
513 ) -> Result<T, ErrorHandled> {
514 query(self.tcx.at(self.cur_span())).map_err(|err| {
516 err.emit_note(*self.tcx);
517 err
518 })
519 }
520
521 pub fn eval_global(
522 &self,
523 instance: ty::Instance<'tcx>,
524 ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
525 let gid = GlobalId { instance, promoted: None };
526 let val = if self.tcx.is_static(gid.instance.def_id()) {
527 let alloc_id = self.tcx.reserve_and_set_static_alloc(gid.instance.def_id());
528
529 let ty = instance.ty(self.tcx.tcx, self.typing_env);
530 mir::ConstAlloc { alloc_id, ty }
531 } else {
532 self.ctfe_query(|tcx| tcx.eval_to_allocation_raw(self.typing_env.as_query_input(gid)))?
533 };
534 self.raw_const_to_mplace(val)
535 }
536
537 pub fn eval_mir_constant(
538 &self,
539 val: &mir::Const<'tcx>,
540 span: Span,
541 layout: Option<TyAndLayout<'tcx>>,
542 ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
543 M::eval_mir_constant(self, *val, span, layout, |ecx, val, span, layout| {
544 let const_val = val.eval(*ecx.tcx, ecx.typing_env, span).map_err(|err| {
545 if M::ALL_CONSTS_ARE_PRECHECKED {
546 match err {
547 ErrorHandled::TooGeneric(..) => {},
548 ErrorHandled::Reported(reported, span) => {
549 if reported.is_allowed_in_infallible() {
550 } else {
554 span_bug!(span, "interpret const eval failure of {val:?} which is not in required_consts");
556 }
557 }
558 }
559 }
560 err.emit_note(*ecx.tcx);
561 err
562 })?;
563 ecx.const_val_to_op(const_val, val.ty(), layout)
564 })
565 }
566
567 #[must_use]
568 pub fn dump_place(&self, place: &PlaceTy<'tcx, M::Provenance>) -> PlacePrinter<'_, 'tcx, M> {
569 PlacePrinter { ecx: self, place: *place.place() }
570 }
571
572 #[must_use]
573 pub fn generate_stacktrace(&self) -> Vec<FrameInfo<'tcx>> {
574 Frame::generate_stacktrace_from_stack(self.stack())
575 }
576
577 pub fn adjust_nan<F1, F2>(&self, f: F2, inputs: &[F1]) -> F2
578 where
579 F1: rustc_apfloat::Float + rustc_apfloat::FloatConvert<F2>,
580 F2: rustc_apfloat::Float,
581 {
582 if f.is_nan() { M::generate_nan(self, inputs) } else { f }
583 }
584}
585
586#[doc(hidden)]
587pub struct PlacePrinter<'a, 'tcx, M: Machine<'tcx>> {
589 ecx: &'a InterpCx<'tcx, M>,
590 place: Place<M::Provenance>,
591}
592
593impl<'a, 'tcx, M: Machine<'tcx>> std::fmt::Debug for PlacePrinter<'a, 'tcx, M> {
594 fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
595 match self.place {
596 Place::Local { local, offset, locals_addr } => {
597 debug_assert_eq!(locals_addr, self.ecx.frame().locals_addr());
598 let mut allocs = Vec::new();
599 write!(fmt, "{local:?}")?;
600 if let Some(offset) = offset {
601 write!(fmt, "+{:#x}", offset.bytes())?;
602 }
603 write!(fmt, ":")?;
604
605 self.ecx.frame().locals[local].print(&mut allocs, fmt)?;
606
607 write!(fmt, ": {:?}", self.ecx.dump_allocs(allocs.into_iter().flatten().collect()))
608 }
609 Place::Ptr(mplace) => match mplace.ptr.provenance.and_then(Provenance::get_alloc_id) {
610 Some(alloc_id) => {
611 write!(fmt, "by ref {:?}: {:?}", mplace.ptr, self.ecx.dump_alloc(alloc_id))
612 }
613 ptr => write!(fmt, " integral by ref: {ptr:?}"),
614 },
615 }
616 }
617}