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;
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
27#[instrument(level = "trace", skip(ecx, body))]
29fn eval_body_using_ecx<'tcx, R: InterpretationResult<'tcx>>(
30 ecx: &mut CompileTimeInterpCx<'tcx>,
31 cid: GlobalId<'tcx>,
32 body: &'tcx mir::Body<'tcx>,
33) -> InterpResult<'tcx, R> {
34 let tcx = *ecx.tcx;
35 assert!(
36 cid.promoted.is_some()
37 || matches!(
38 ecx.tcx.def_kind(cid.instance.def_id()),
39 DefKind::Const
40 | DefKind::Static { .. }
41 | DefKind::ConstParam
42 | DefKind::AnonConst
43 | DefKind::InlineConst
44 | DefKind::AssocConst
45 ),
46 "Unexpected DefKind: {:?}",
47 ecx.tcx.def_kind(cid.instance.def_id())
48 );
49 let layout = ecx.layout_of(body.bound_return_ty().instantiate(tcx, cid.instance.args))?;
50 assert!(layout.is_sized());
51
52 let intern_kind = if cid.promoted.is_some() {
53 InternKind::Promoted
54 } else {
55 match tcx.static_mutability(cid.instance.def_id()) {
56 Some(m) => InternKind::Static(m),
57 None => InternKind::Constant,
58 }
59 };
60
61 let ret = if let InternKind::Static(_) = intern_kind {
62 create_static_alloc(ecx, cid.instance.def_id().expect_local(), layout)?
63 } else {
64 ecx.allocate(layout, MemoryKind::Stack)?
65 };
66
67 trace!(
68 "eval_body_using_ecx: pushing stack frame for global: {}{}",
69 with_no_trimmed_paths!(ecx.tcx.def_path_str(cid.instance.def_id())),
70 cid.promoted.map_or_else(String::new, |p| format!("::{p:?}"))
71 );
72
73 ecx.push_stack_frame_raw(
76 cid.instance,
77 body,
78 &ret.clone().into(),
79 ReturnContinuation::Stop { cleanup: false },
80 )?;
81 ecx.storage_live_for_always_live_locals()?;
82
83 while ecx.step()? {
85 if CTRL_C_RECEIVED.load(Relaxed) {
86 throw_exhaust!(Interrupted);
87 }
88 }
89
90 let intern_result = intern_const_alloc_recursive(ecx, intern_kind, &ret);
92
93 const_validate_mplace(ecx, &ret, cid)?;
95
96 match intern_result {
100 Ok(()) => {}
101 Err(InternError::DanglingPointer) => {
102 throw_inval!(AlreadyReported(ReportedErrorInfo::non_const_eval_error(
103 ecx.tcx
104 .dcx()
105 .emit_err(errors::DanglingPtrInFinal { span: ecx.tcx.span, kind: intern_kind }),
106 )));
107 }
108 Err(InternError::BadMutablePointer) => {
109 throw_inval!(AlreadyReported(ReportedErrorInfo::non_const_eval_error(
110 ecx.tcx
111 .dcx()
112 .emit_err(errors::MutablePtrInFinal { span: ecx.tcx.span, kind: intern_kind }),
113 )));
114 }
115 Err(InternError::ConstAllocNotGlobal) => {
116 throw_inval!(AlreadyReported(ReportedErrorInfo::non_const_eval_error(
117 ecx.tcx.dcx().emit_err(errors::ConstHeapPtrInFinal { span: ecx.tcx.span }),
118 )));
119 }
120 Err(InternError::PartialPointer) => {
121 throw_inval!(AlreadyReported(ReportedErrorInfo::non_const_eval_error(
122 ecx.tcx
123 .dcx()
124 .emit_err(errors::PartialPtrInFinal { span: ecx.tcx.span, kind: intern_kind }),
125 )));
126 }
127 }
128
129 interp_ok(R::make_result(ret, ecx))
130}
131
132pub(crate) fn mk_eval_cx_to_read_const_val<'tcx>(
143 tcx: TyCtxt<'tcx>,
144 root_span: Span,
145 typing_env: ty::TypingEnv<'tcx>,
146 can_access_mut_global: CanAccessMutGlobal,
147) -> CompileTimeInterpCx<'tcx> {
148 debug!("mk_eval_cx: {:?}", typing_env);
149 InterpCx::new(
150 tcx,
151 root_span,
152 typing_env,
153 CompileTimeMachine::new(can_access_mut_global, CheckAlignment::No),
154 )
155}
156
157pub fn mk_eval_cx_for_const_val<'tcx>(
160 tcx: TyCtxtAt<'tcx>,
161 typing_env: ty::TypingEnv<'tcx>,
162 val: mir::ConstValue,
163 ty: Ty<'tcx>,
164) -> Option<(CompileTimeInterpCx<'tcx>, OpTy<'tcx>)> {
165 let ecx = mk_eval_cx_to_read_const_val(tcx.tcx, tcx.span, typing_env, CanAccessMutGlobal::No);
166 let op = ecx.const_val_to_op(val, ty, None).discard_err()?;
168 Some((ecx, op))
169}
170
171#[instrument(skip(ecx), level = "debug")]
178pub(super) fn op_to_const<'tcx>(
179 ecx: &CompileTimeInterpCx<'tcx>,
180 op: &OpTy<'tcx>,
181 for_diagnostics: bool,
182) -> ConstValue {
183 if op.layout.is_zst() {
185 return ConstValue::ZeroSized;
186 }
187
188 let force_as_immediate = match op.layout.backend_repr {
194 BackendRepr::Scalar(abi::Scalar::Initialized { .. }) => true,
195 _ => false,
203 };
204 let immediate = if force_as_immediate {
205 match ecx.read_immediate(op).report_err() {
206 Ok(imm) => Right(imm),
207 Err(err) => {
208 if for_diagnostics {
209 op.as_mplace_or_imm()
211 } else {
212 panic!("normalization works on validated constants: {err:?}")
213 }
214 }
215 }
216 } else {
217 op.as_mplace_or_imm()
218 };
219
220 debug!(?immediate);
221
222 match immediate {
223 Left(ref mplace) => {
224 let (prov, offset) =
225 mplace.ptr().into_pointer_or_addr().unwrap().prov_and_relative_offset();
226 let alloc_id = prov.alloc_id();
227 ConstValue::Indirect { alloc_id, offset }
228 }
229 Right(imm) => match *imm {
231 Immediate::Scalar(x) => ConstValue::Scalar(x),
232 Immediate::ScalarPair(a, b) => {
233 debug!("ScalarPair(a: {:?}, b: {:?})", a, b);
234 let pointee_ty = imm.layout.ty.builtin_deref(false).unwrap(); debug_assert!(
239 matches!(
240 ecx.tcx.struct_tail_for_codegen(pointee_ty, ecx.typing_env()).kind(),
241 ty::Str | ty::Slice(..),
242 ),
243 "`ConstValue::Slice` is for slice-tailed types only, but got {}",
244 imm.layout.ty,
245 );
246 let msg = "`op_to_const` on an immediate scalar pair must only be used on slice references to the beginning of an actual allocation";
247 let ptr = a.to_pointer(ecx).expect(msg);
248 let (prov, offset) =
249 ptr.into_pointer_or_addr().expect(msg).prov_and_relative_offset();
250 let alloc_id = prov.alloc_id();
251 assert!(offset == abi::Size::ZERO, "{}", msg);
252 let meta = b.to_target_usize(ecx).expect(msg);
253 ConstValue::Slice { alloc_id, meta }
254 }
255 Immediate::Uninit => bug!("`Uninit` is not a valid value for {}", op.layout.ty),
256 },
257 }
258}
259
260#[instrument(skip(tcx), level = "debug", ret)]
261pub(crate) fn turn_into_const_value<'tcx>(
262 tcx: TyCtxt<'tcx>,
263 constant: ConstAlloc<'tcx>,
264 key: ty::PseudoCanonicalInput<'tcx, GlobalId<'tcx>>,
265) -> ConstValue {
266 let cid = key.value;
267 let def_id = cid.instance.def.def_id();
268 let is_static = tcx.is_static(def_id);
269 let ecx = mk_eval_cx_to_read_const_val(
271 tcx,
272 tcx.def_span(key.value.instance.def_id()),
273 key.typing_env,
274 CanAccessMutGlobal::from(is_static),
275 );
276
277 let mplace = ecx.raw_const_to_mplace(constant).expect(
278 "can only fail if layout computation failed, \
279 which should have given a good error before ever invoking this function",
280 );
281 assert!(
282 !is_static || cid.promoted.is_some(),
283 "the `eval_to_const_value_raw` query should not be used for statics, use `eval_to_allocation` instead"
284 );
285
286 op_to_const(&ecx, &mplace.into(), false)
288}
289
290#[instrument(skip(tcx), level = "debug")]
291pub fn eval_to_const_value_raw_provider<'tcx>(
292 tcx: TyCtxt<'tcx>,
293 key: ty::PseudoCanonicalInput<'tcx, GlobalId<'tcx>>,
294) -> ::rustc_middle::mir::interpret::EvalToConstValueResult<'tcx> {
295 tcx.eval_to_allocation_raw(key).map(|val| turn_into_const_value(tcx, val, key))
296}
297
298#[instrument(skip(tcx), level = "debug")]
299pub fn eval_static_initializer_provider<'tcx>(
300 tcx: TyCtxt<'tcx>,
301 def_id: LocalDefId,
302) -> ::rustc_middle::mir::interpret::EvalStaticInitializerRawResult<'tcx> {
303 assert!(tcx.is_static(def_id.to_def_id()));
304
305 let instance = ty::Instance::mono(tcx, def_id.to_def_id());
306 let cid = rustc_middle::mir::interpret::GlobalId { instance, promoted: None };
307 eval_in_interpreter(tcx, cid, ty::TypingEnv::fully_monomorphized())
308}
309
310pub trait InterpretationResult<'tcx> {
311 fn make_result(
315 mplace: MPlaceTy<'tcx>,
316 ecx: &mut InterpCx<'tcx, CompileTimeMachine<'tcx>>,
317 ) -> Self;
318}
319
320impl<'tcx> InterpretationResult<'tcx> for ConstAlloc<'tcx> {
321 fn make_result(
322 mplace: MPlaceTy<'tcx>,
323 _ecx: &mut InterpCx<'tcx, CompileTimeMachine<'tcx>>,
324 ) -> Self {
325 ConstAlloc { alloc_id: mplace.ptr().provenance.unwrap().alloc_id(), ty: mplace.layout.ty }
326 }
327}
328
329#[instrument(skip(tcx), level = "debug")]
330pub fn eval_to_allocation_raw_provider<'tcx>(
331 tcx: TyCtxt<'tcx>,
332 key: ty::PseudoCanonicalInput<'tcx, GlobalId<'tcx>>,
333) -> ::rustc_middle::mir::interpret::EvalToAllocationRawResult<'tcx> {
334 assert!(key.value.promoted.is_some() || !tcx.is_static(key.value.instance.def_id()));
337 debug_assert_eq!(key.typing_env.typing_mode, ty::TypingMode::PostAnalysis);
340 if cfg!(debug_assertions) {
341 let instance = with_no_trimmed_paths!(key.value.instance.to_string());
347 trace!("const eval: {:?} ({})", key, instance);
348 }
349
350 eval_in_interpreter(tcx, key.value, key.typing_env)
351}
352
353fn eval_in_interpreter<'tcx, R: InterpretationResult<'tcx>>(
354 tcx: TyCtxt<'tcx>,
355 cid: GlobalId<'tcx>,
356 typing_env: ty::TypingEnv<'tcx>,
357) -> Result<R, ErrorHandled> {
358 let def = cid.instance.def.def_id();
359 let is_static = tcx.is_static(def);
360
361 let mut ecx = InterpCx::new(
362 tcx,
363 tcx.def_span(def),
364 typing_env,
365 CompileTimeMachine::new(CanAccessMutGlobal::from(is_static), CheckAlignment::Error),
370 );
371 let res = ecx.load_mir(cid.instance.def, cid.promoted);
372 res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, body))
373 .report_err()
374 .map_err(|error| report_eval_error(&ecx, cid, error))
375}
376
377#[inline(always)]
378fn const_validate_mplace<'tcx>(
379 ecx: &mut InterpCx<'tcx, CompileTimeMachine<'tcx>>,
380 mplace: &MPlaceTy<'tcx>,
381 cid: GlobalId<'tcx>,
382) -> Result<(), ErrorHandled> {
383 let alloc_id = mplace.ptr().provenance.unwrap().alloc_id();
384 let mut ref_tracking = RefTracking::new(mplace.clone());
385 let mut inner = false;
386 while let Some((mplace, path)) = ref_tracking.next() {
387 let mode = match ecx.tcx.static_mutability(cid.instance.def_id()) {
388 _ if cid.promoted.is_some() => CtfeValidationMode::Promoted,
389 Some(mutbl) => CtfeValidationMode::Static { mutbl }, None => {
391 CtfeValidationMode::Const { allow_immutable_unsafe_cell: !inner }
395 }
396 };
397 ecx.const_validate_operand(&mplace.into(), path, &mut ref_tracking, mode)
398 .report_err()
399 .map_err(|error| report_validation_error(&ecx, cid, error, alloc_id))?;
402 inner = true;
403 }
404
405 Ok(())
406}
407
408#[inline(never)]
409fn report_eval_error<'tcx>(
410 ecx: &InterpCx<'tcx, CompileTimeMachine<'tcx>>,
411 cid: GlobalId<'tcx>,
412 error: InterpErrorInfo<'tcx>,
413) -> ErrorHandled {
414 let (error, backtrace) = error.into_parts();
415 backtrace.print_backtrace();
416
417 let instance = with_no_trimmed_paths!(cid.instance.to_string());
418
419 super::report(
420 ecx,
421 error,
422 DUMMY_SP,
423 || super::get_span_and_frames(ecx.tcx, ecx.stack()),
424 |diag, span, frames| {
425 let num_frames = frames.len();
426 diag.code(E0080);
428 diag.span_label(span, crate::fluent_generated::const_eval_error);
429 for frame in frames {
430 diag.subdiagnostic(frame);
431 }
432 diag.arg("instance", instance);
434 diag.arg("num_frames", num_frames);
435 },
436 )
437}
438
439#[inline(never)]
440fn report_validation_error<'tcx>(
441 ecx: &InterpCx<'tcx, CompileTimeMachine<'tcx>>,
442 cid: GlobalId<'tcx>,
443 error: InterpErrorInfo<'tcx>,
444 alloc_id: AllocId,
445) -> ErrorHandled {
446 if !matches!(error.kind(), InterpErrorKind::UndefinedBehavior(_)) {
447 return report_eval_error(ecx, cid, error);
449 }
450
451 let (error, backtrace) = error.into_parts();
452 backtrace.print_backtrace();
453
454 let bytes = ecx.print_alloc_bytes_for_diagnostics(alloc_id);
455 let info = ecx.get_alloc_info(alloc_id);
456 let raw_bytes =
457 errors::RawBytesNote { size: info.size.bytes(), align: info.align.bytes(), bytes };
458
459 crate::const_eval::report(
460 ecx,
461 error,
462 DUMMY_SP,
463 || crate::const_eval::get_span_and_frames(ecx.tcx, ecx.stack()),
464 move |diag, span, frames| {
465 diag.code(E0080);
467 diag.span_label(span, crate::fluent_generated::const_eval_validation_failure);
468 diag.note(crate::fluent_generated::const_eval_validation_failure_note);
469 for frame in frames {
470 diag.subdiagnostic(frame);
471 }
472 diag.subdiagnostic(raw_bytes);
473 },
474 )
475}