1use std::sync::atomic::Ordering::Relaxed;
2
3use either::{Left, Right};
4use rustc_abi::{self as abi, BackendRepr};
5use rustc_errors::E0080;
6use rustc_hir::def::DefKind;
7use rustc_middle::mir::interpret::{AllocId, ErrorHandled, InterpErrorInfo, ReportedErrorInfo};
8use rustc_middle::mir::{self, ConstAlloc, ConstValue};
9use rustc_middle::query::TyCtxtAt;
10use rustc_middle::ty::layout::{HasTypingEnv, TyAndLayout};
11use rustc_middle::ty::print::with_no_trimmed_paths;
12use rustc_middle::ty::{self, Ty, TyCtxt};
13use rustc_middle::{bug, throw_inval};
14use rustc_span::def_id::LocalDefId;
15use rustc_span::{DUMMY_SP, Span};
16use tracing::{debug, instrument, trace};
17
18use super::{CanAccessMutGlobal, CompileTimeInterpCx, CompileTimeMachine};
19use crate::const_eval::CheckAlignment;
20use crate::interpret::{
21 CtfeValidationMode, GlobalId, Immediate, InternError, InternKind, InterpCx, InterpErrorKind,
22 InterpResult, MPlaceTy, MemoryKind, OpTy, RefTracking, ReturnContinuation, create_static_alloc,
23 intern_const_alloc_recursive, interp_ok, throw_exhaust,
24};
25use crate::{CTRL_C_RECEIVED, errors};
26
27fn setup_for_eval<'tcx>(
28 ecx: &mut CompileTimeInterpCx<'tcx>,
29 cid: GlobalId<'tcx>,
30 layout: TyAndLayout<'tcx>,
31) -> InterpResult<'tcx, (InternKind, MPlaceTy<'tcx>)> {
32 let tcx = *ecx.tcx;
33 assert!(
34 cid.promoted.is_some()
35 || matches!(
36 ecx.tcx.def_kind(cid.instance.def_id()),
37 DefKind::Const
38 | DefKind::Static { .. }
39 | DefKind::ConstParam
40 | DefKind::AnonConst
41 | DefKind::InlineConst
42 | DefKind::AssocConst
43 ),
44 "Unexpected DefKind: {:?}",
45 ecx.tcx.def_kind(cid.instance.def_id())
46 );
47 assert!(layout.is_sized());
48
49 let intern_kind = if cid.promoted.is_some() {
50 InternKind::Promoted
51 } else {
52 match tcx.static_mutability(cid.instance.def_id()) {
53 Some(m) => InternKind::Static(m),
54 None => InternKind::Constant,
55 }
56 };
57
58 let return_place = if let InternKind::Static(_) = intern_kind {
59 create_static_alloc(ecx, cid.instance.def_id().expect_local(), layout)
60 } else {
61 ecx.allocate(layout, MemoryKind::Stack)
62 };
63
64 return_place.map(|ret| (intern_kind, ret))
65}
66
67#[instrument(level = "trace", skip(ecx, body))]
68fn eval_body_using_ecx<'tcx, R: InterpretationResult<'tcx>>(
69 ecx: &mut CompileTimeInterpCx<'tcx>,
70 cid: GlobalId<'tcx>,
71 body: &'tcx mir::Body<'tcx>,
72) -> InterpResult<'tcx, R> {
73 let tcx = *ecx.tcx;
74 let layout = ecx.layout_of(body.bound_return_ty().instantiate(tcx, cid.instance.args))?;
75 let (intern_kind, ret) = setup_for_eval(ecx, cid, layout)?;
76
77 trace!(
78 "eval_body_using_ecx: pushing stack frame for global: {}{}",
79 with_no_trimmed_paths!(ecx.tcx.def_path_str(cid.instance.def_id())),
80 cid.promoted.map_or_else(String::new, |p| format!("::{p:?}"))
81 );
82
83 ecx.push_stack_frame_raw(
86 cid.instance,
87 body,
88 &ret.clone().into(),
89 ReturnContinuation::Stop { cleanup: false },
90 )?;
91 ecx.storage_live_for_always_live_locals()?;
92
93 while ecx.step()? {
95 if CTRL_C_RECEIVED.load(Relaxed) {
96 throw_exhaust!(Interrupted);
97 }
98 }
99
100 intern_and_validate(ecx, cid, intern_kind, ret)
101}
102
103#[instrument(level = "trace", skip(ecx))]
104fn eval_trivial_const_using_ecx<'tcx, R: InterpretationResult<'tcx>>(
105 ecx: &mut CompileTimeInterpCx<'tcx>,
106 cid: GlobalId<'tcx>,
107 val: ConstValue,
108 ty: Ty<'tcx>,
109) -> InterpResult<'tcx, R> {
110 let layout = ecx.layout_of(ty)?;
111 let (intern_kind, return_place) = setup_for_eval(ecx, cid, layout)?;
112
113 let opty = ecx.const_val_to_op(val, ty, Some(layout))?;
114 ecx.copy_op(&opty, &return_place)?;
115
116 intern_and_validate(ecx, cid, intern_kind, return_place)
117}
118
119fn intern_and_validate<'tcx, R: InterpretationResult<'tcx>>(
120 ecx: &mut CompileTimeInterpCx<'tcx>,
121 cid: GlobalId<'tcx>,
122 intern_kind: InternKind,
123 ret: MPlaceTy<'tcx>,
124) -> InterpResult<'tcx, R> {
125 let intern_result = intern_const_alloc_recursive(ecx, intern_kind, &ret);
127
128 const_validate_mplace(ecx, &ret, cid)?;
130
131 match intern_result {
135 Ok(()) => {}
136 Err(InternError::DanglingPointer) => {
137 throw_inval!(AlreadyReported(ReportedErrorInfo::non_const_eval_error(
138 ecx.tcx
139 .dcx()
140 .emit_err(errors::DanglingPtrInFinal { span: ecx.tcx.span, kind: intern_kind }),
141 )));
142 }
143 Err(InternError::BadMutablePointer) => {
144 throw_inval!(AlreadyReported(ReportedErrorInfo::non_const_eval_error(
145 ecx.tcx
146 .dcx()
147 .emit_err(errors::MutablePtrInFinal { span: ecx.tcx.span, kind: intern_kind }),
148 )));
149 }
150 Err(InternError::ConstAllocNotGlobal) => {
151 throw_inval!(AlreadyReported(ReportedErrorInfo::non_const_eval_error(
152 ecx.tcx.dcx().emit_err(errors::ConstHeapPtrInFinal { span: ecx.tcx.span }),
153 )));
154 }
155 Err(InternError::PartialPointer) => {
156 throw_inval!(AlreadyReported(ReportedErrorInfo::non_const_eval_error(
157 ecx.tcx
158 .dcx()
159 .emit_err(errors::PartialPtrInFinal { span: ecx.tcx.span, kind: intern_kind }),
160 )));
161 }
162 }
163
164 interp_ok(R::make_result(ret, ecx))
165}
166
167pub(crate) fn mk_eval_cx_to_read_const_val<'tcx>(
178 tcx: TyCtxt<'tcx>,
179 root_span: Span,
180 typing_env: ty::TypingEnv<'tcx>,
181 can_access_mut_global: CanAccessMutGlobal,
182) -> CompileTimeInterpCx<'tcx> {
183 debug!("mk_eval_cx: {:?}", typing_env);
184 InterpCx::new(
185 tcx,
186 root_span,
187 typing_env,
188 CompileTimeMachine::new(can_access_mut_global, CheckAlignment::No),
189 )
190}
191
192pub fn mk_eval_cx_for_const_val<'tcx>(
195 tcx: TyCtxtAt<'tcx>,
196 typing_env: ty::TypingEnv<'tcx>,
197 val: mir::ConstValue,
198 ty: Ty<'tcx>,
199) -> Option<(CompileTimeInterpCx<'tcx>, OpTy<'tcx>)> {
200 let ecx = mk_eval_cx_to_read_const_val(tcx.tcx, tcx.span, typing_env, CanAccessMutGlobal::No);
201 let op = ecx.const_val_to_op(val, ty, None).discard_err()?;
203 Some((ecx, op))
204}
205
206#[instrument(skip(ecx), level = "debug")]
213pub(super) fn op_to_const<'tcx>(
214 ecx: &CompileTimeInterpCx<'tcx>,
215 op: &OpTy<'tcx>,
216 for_diagnostics: bool,
217) -> ConstValue {
218 if op.layout.is_zst() {
220 return ConstValue::ZeroSized;
221 }
222
223 let force_as_immediate = match op.layout.backend_repr {
229 BackendRepr::Scalar(abi::Scalar::Initialized { .. }) => true,
230 _ => false,
238 };
239 let immediate = if force_as_immediate {
240 match ecx.read_immediate(op).report_err() {
241 Ok(imm) => Right(imm),
242 Err(err) => {
243 if for_diagnostics {
244 op.as_mplace_or_imm()
246 } else {
247 panic!("normalization works on validated constants: {err:?}")
248 }
249 }
250 }
251 } else {
252 op.as_mplace_or_imm()
253 };
254
255 debug!(?immediate);
256
257 match immediate {
258 Left(ref mplace) => {
259 let (prov, offset) =
260 mplace.ptr().into_pointer_or_addr().unwrap().prov_and_relative_offset();
261 let alloc_id = prov.alloc_id();
262 ConstValue::Indirect { alloc_id, offset }
263 }
264 Right(imm) => match *imm {
266 Immediate::Scalar(x) => ConstValue::Scalar(x),
267 Immediate::ScalarPair(a, b) => {
268 debug!("ScalarPair(a: {:?}, b: {:?})", a, b);
269 let pointee_ty = imm.layout.ty.builtin_deref(false).unwrap(); debug_assert!(
274 matches!(
275 ecx.tcx.struct_tail_for_codegen(pointee_ty, ecx.typing_env()).kind(),
276 ty::Str | ty::Slice(..),
277 ),
278 "`ConstValue::Slice` is for slice-tailed types only, but got {}",
279 imm.layout.ty,
280 );
281 let msg = "`op_to_const` on an immediate scalar pair must only be used on slice references to the beginning of an actual allocation";
282 let ptr = a.to_pointer(ecx).expect(msg);
283 let (prov, offset) =
284 ptr.into_pointer_or_addr().expect(msg).prov_and_relative_offset();
285 let alloc_id = prov.alloc_id();
286 assert!(offset == abi::Size::ZERO, "{}", msg);
287 let meta = b.to_target_usize(ecx).expect(msg);
288 ConstValue::Slice { alloc_id, meta }
289 }
290 Immediate::Uninit => bug!("`Uninit` is not a valid value for {}", op.layout.ty),
291 },
292 }
293}
294
295#[instrument(skip(tcx), level = "debug", ret)]
296pub(crate) fn turn_into_const_value<'tcx>(
297 tcx: TyCtxt<'tcx>,
298 constant: ConstAlloc<'tcx>,
299 key: ty::PseudoCanonicalInput<'tcx, GlobalId<'tcx>>,
300) -> ConstValue {
301 let cid = key.value;
302 let def_id = cid.instance.def.def_id();
303 let is_static = tcx.is_static(def_id);
304 let ecx = mk_eval_cx_to_read_const_val(
306 tcx,
307 tcx.def_span(key.value.instance.def_id()),
308 key.typing_env,
309 CanAccessMutGlobal::from(is_static),
310 );
311
312 let mplace = ecx.raw_const_to_mplace(constant).expect(
313 "can only fail if layout computation failed, \
314 which should have given a good error before ever invoking this function",
315 );
316 assert!(
317 !is_static || cid.promoted.is_some(),
318 "the `eval_to_const_value_raw` query should not be used for statics, use `eval_to_allocation` instead"
319 );
320
321 op_to_const(&ecx, &mplace.into(), false)
323}
324
325#[instrument(skip(tcx), level = "debug")]
326pub fn eval_to_const_value_raw_provider<'tcx>(
327 tcx: TyCtxt<'tcx>,
328 key: ty::PseudoCanonicalInput<'tcx, GlobalId<'tcx>>,
329) -> ::rustc_middle::mir::interpret::EvalToConstValueResult<'tcx> {
330 if let Some((value, _ty)) = tcx.trivial_const(key.value.instance.def_id()) {
331 return Ok(value);
332 }
333 tcx.eval_to_allocation_raw(key).map(|val| turn_into_const_value(tcx, val, key))
334}
335
336#[instrument(skip(tcx), level = "debug")]
337pub fn eval_static_initializer_provider<'tcx>(
338 tcx: TyCtxt<'tcx>,
339 def_id: LocalDefId,
340) -> ::rustc_middle::mir::interpret::EvalStaticInitializerRawResult<'tcx> {
341 assert!(tcx.is_static(def_id.to_def_id()));
342
343 let instance = ty::Instance::mono(tcx, def_id.to_def_id());
344 let cid = rustc_middle::mir::interpret::GlobalId { instance, promoted: None };
345 eval_in_interpreter(tcx, cid, ty::TypingEnv::fully_monomorphized())
346}
347
348pub trait InterpretationResult<'tcx> {
349 fn make_result(
353 mplace: MPlaceTy<'tcx>,
354 ecx: &mut InterpCx<'tcx, CompileTimeMachine<'tcx>>,
355 ) -> Self;
356}
357
358impl<'tcx> InterpretationResult<'tcx> for ConstAlloc<'tcx> {
359 fn make_result(
360 mplace: MPlaceTy<'tcx>,
361 _ecx: &mut InterpCx<'tcx, CompileTimeMachine<'tcx>>,
362 ) -> Self {
363 ConstAlloc { alloc_id: mplace.ptr().provenance.unwrap().alloc_id(), ty: mplace.layout.ty }
364 }
365}
366
367#[instrument(skip(tcx), level = "debug")]
368pub fn eval_to_allocation_raw_provider<'tcx>(
369 tcx: TyCtxt<'tcx>,
370 key: ty::PseudoCanonicalInput<'tcx, GlobalId<'tcx>>,
371) -> ::rustc_middle::mir::interpret::EvalToAllocationRawResult<'tcx> {
372 assert!(key.value.promoted.is_some() || !tcx.is_static(key.value.instance.def_id()));
375 debug_assert_eq!(key.typing_env.typing_mode, ty::TypingMode::PostAnalysis);
378 if cfg!(debug_assertions) {
379 let instance = with_no_trimmed_paths!(key.value.instance.to_string());
385 trace!("const eval: {:?} ({})", key, instance);
386 }
387
388 eval_in_interpreter(tcx, key.value, key.typing_env)
389}
390
391fn eval_in_interpreter<'tcx, R: InterpretationResult<'tcx>>(
392 tcx: TyCtxt<'tcx>,
393 cid: GlobalId<'tcx>,
394 typing_env: ty::TypingEnv<'tcx>,
395) -> Result<R, ErrorHandled> {
396 let def = cid.instance.def.def_id();
397 let is_static = tcx.is_static(def);
398
399 let mut ecx = InterpCx::new(
400 tcx,
401 tcx.def_span(def),
402 typing_env,
403 CompileTimeMachine::new(CanAccessMutGlobal::from(is_static), CheckAlignment::Error),
408 );
409
410 let result = if let Some((value, ty)) = tcx.trivial_const(def) {
411 eval_trivial_const_using_ecx(&mut ecx, cid, value, ty)
412 } else {
413 ecx.load_mir(cid.instance.def, cid.promoted)
414 .and_then(|body| eval_body_using_ecx(&mut ecx, cid, body))
415 };
416 result.report_err().map_err(|error| report_eval_error(&ecx, cid, error))
417}
418
419#[inline(always)]
420fn const_validate_mplace<'tcx>(
421 ecx: &mut InterpCx<'tcx, CompileTimeMachine<'tcx>>,
422 mplace: &MPlaceTy<'tcx>,
423 cid: GlobalId<'tcx>,
424) -> Result<(), ErrorHandled> {
425 let alloc_id = mplace.ptr().provenance.unwrap().alloc_id();
426 let mut ref_tracking = RefTracking::new(mplace.clone());
427 let mut inner = false;
428 while let Some((mplace, path)) = ref_tracking.next() {
429 let mode = match ecx.tcx.static_mutability(cid.instance.def_id()) {
430 _ if cid.promoted.is_some() => CtfeValidationMode::Promoted,
431 Some(mutbl) => CtfeValidationMode::Static { mutbl }, None => {
433 CtfeValidationMode::Const { allow_immutable_unsafe_cell: !inner }
437 }
438 };
439 ecx.const_validate_operand(&mplace.into(), path, &mut ref_tracking, mode)
440 .report_err()
441 .map_err(|error| report_validation_error(&ecx, cid, error, alloc_id))?;
444 inner = true;
445 }
446
447 Ok(())
448}
449
450#[inline(never)]
451fn report_eval_error<'tcx>(
452 ecx: &InterpCx<'tcx, CompileTimeMachine<'tcx>>,
453 cid: GlobalId<'tcx>,
454 error: InterpErrorInfo<'tcx>,
455) -> ErrorHandled {
456 let (error, backtrace) = error.into_parts();
457 backtrace.print_backtrace();
458
459 super::report(
460 ecx,
461 error,
462 DUMMY_SP,
463 || super::get_span_and_frames(ecx.tcx, ecx.stack()),
464 |diag, span, frames| {
465 let num_frames = frames.len();
466 diag.code(E0080);
468 diag.span_label(span, crate::fluent_generated::const_eval_error);
469 for frame in frames {
470 diag.subdiagnostic(frame);
471 }
472 diag.arg("instance", with_no_trimmed_paths!(cid.instance.to_string()));
474 diag.arg("num_frames", num_frames);
475 },
476 )
477}
478
479#[inline(never)]
480fn report_validation_error<'tcx>(
481 ecx: &InterpCx<'tcx, CompileTimeMachine<'tcx>>,
482 cid: GlobalId<'tcx>,
483 error: InterpErrorInfo<'tcx>,
484 alloc_id: AllocId,
485) -> ErrorHandled {
486 if !matches!(error.kind(), InterpErrorKind::UndefinedBehavior(_)) {
487 return report_eval_error(ecx, cid, error);
489 }
490
491 let (error, backtrace) = error.into_parts();
492 backtrace.print_backtrace();
493
494 let bytes = ecx.print_alloc_bytes_for_diagnostics(alloc_id);
495 let info = ecx.get_alloc_info(alloc_id);
496 let raw_bytes =
497 errors::RawBytesNote { size: info.size.bytes(), align: info.align.bytes(), bytes };
498
499 crate::const_eval::report(
500 ecx,
501 error,
502 DUMMY_SP,
503 || crate::const_eval::get_span_and_frames(ecx.tcx, ecx.stack()),
504 move |diag, span, frames| {
505 diag.code(E0080);
507 diag.span_label(span, crate::fluent_generated::const_eval_validation_failure);
508 diag.note(crate::fluent_generated::const_eval_validation_failure_note);
509 for frame in frames {
510 diag.subdiagnostic(frame);
511 }
512 diag.subdiagnostic(raw_bytes);
513 },
514 )
515}