1use std::fmt::{self, Debug, Display, Formatter};
2
3use rustc_abi::{HasDataLayout, Size};
4use rustc_hir::def_id::DefId;
5use rustc_macros::{HashStable, Lift, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable};
6use rustc_session::RemapFileNameExt;
7use rustc_session::config::RemapPathScopeComponents;
8use rustc_span::{DUMMY_SP, Span, Symbol};
9use rustc_type_ir::TypeVisitableExt;
10
11use super::interpret::ReportedErrorInfo;
12use crate::mir::interpret::{AllocId, AllocRange, ErrorHandled, GlobalAlloc, Scalar, alloc_range};
13use crate::mir::{Promoted, pretty_print_const_value};
14use crate::ty::print::{pretty_print_const, with_no_trimmed_paths};
15use crate::ty::{self, ConstKind, GenericArgsRef, ScalarInt, Ty, TyCtxt};
16
17#[derive(Copy, Clone, HashStable, TyEncodable, TyDecodable, Debug, Hash, Eq, PartialEq)]
24pub struct ConstAlloc<'tcx> {
25 pub alloc_id: AllocId,
28 pub ty: Ty<'tcx>,
29}
30
31#[derive(Copy, Clone, Debug, Eq, PartialEq, TyEncodable, TyDecodable, Hash)]
34#[derive(HashStable)]
35pub enum ConstValue {
36 Scalar(Scalar),
40
41 ZeroSized,
43
44 Slice {
51 alloc_id: AllocId,
54 meta: u64,
57 },
58
59 Indirect {
63 alloc_id: AllocId,
70 offset: Size,
72 },
73}
74
75#[cfg(target_pointer_width = "64")]
76rustc_data_structures::static_assert_size!(ConstValue, 24);
77
78impl ConstValue {
79 #[inline]
80 pub fn try_to_scalar(&self) -> Option<Scalar> {
81 match *self {
82 ConstValue::Indirect { .. } | ConstValue::Slice { .. } | ConstValue::ZeroSized => None,
83 ConstValue::Scalar(val) => Some(val),
84 }
85 }
86
87 pub fn try_to_scalar_int(&self) -> Option<ScalarInt> {
88 self.try_to_scalar()?.try_to_scalar_int().ok()
89 }
90
91 pub fn try_to_bits(&self, size: Size) -> Option<u128> {
92 Some(self.try_to_scalar_int()?.to_bits(size))
93 }
94
95 pub fn try_to_bool(&self) -> Option<bool> {
96 self.try_to_scalar_int()?.try_into().ok()
97 }
98
99 pub fn try_to_target_usize(&self, tcx: TyCtxt<'_>) -> Option<u64> {
100 Some(self.try_to_scalar_int()?.to_target_usize(tcx))
101 }
102
103 pub fn try_to_bits_for_ty<'tcx>(
104 &self,
105 tcx: TyCtxt<'tcx>,
106 typing_env: ty::TypingEnv<'tcx>,
107 ty: Ty<'tcx>,
108 ) -> Option<u128> {
109 let size = tcx
110 .layout_of(typing_env.with_post_analysis_normalized(tcx).as_query_input(ty))
111 .ok()?
112 .size;
113 self.try_to_bits(size)
114 }
115
116 pub fn from_bool(b: bool) -> Self {
117 ConstValue::Scalar(Scalar::from_bool(b))
118 }
119
120 pub fn from_u64(i: u64) -> Self {
121 ConstValue::Scalar(Scalar::from_u64(i))
122 }
123
124 pub fn from_u128(i: u128) -> Self {
125 ConstValue::Scalar(Scalar::from_u128(i))
126 }
127
128 pub fn from_target_usize(i: u64, cx: &impl HasDataLayout) -> Self {
129 ConstValue::Scalar(Scalar::from_target_usize(i, cx))
130 }
131
132 pub fn try_get_slice_bytes_for_diagnostics<'tcx>(
134 &self,
135 tcx: TyCtxt<'tcx>,
136 ) -> Option<&'tcx [u8]> {
137 let (alloc_id, start, len) = match self {
138 ConstValue::Scalar(_) | ConstValue::ZeroSized => {
139 bug!("`try_get_slice_bytes` on non-slice constant")
140 }
141 &ConstValue::Slice { alloc_id, meta } => (alloc_id, 0, meta),
142 &ConstValue::Indirect { alloc_id, offset } => {
143 let a = tcx.global_alloc(alloc_id).unwrap_memory().inner();
146 let ptr_size = tcx.data_layout.pointer_size();
147 if a.size() < offset + 2 * ptr_size {
148 return None;
150 }
151 let ptr = a
153 .read_scalar(
154 &tcx,
155 alloc_range(offset, ptr_size),
156 true,
157 )
158 .ok()?;
159 let ptr = ptr.to_pointer(&tcx).discard_err()?;
160 let len = a
161 .read_scalar(
162 &tcx,
163 alloc_range(offset + ptr_size, ptr_size),
164 false,
165 )
166 .ok()?;
167 let len = len.to_target_usize(&tcx).discard_err()?;
168 if len == 0 {
169 return Some(&[]);
170 }
171 let (inner_prov, offset) =
173 ptr.into_pointer_or_addr().ok()?.prov_and_relative_offset();
174 (inner_prov.alloc_id(), offset.bytes(), len)
175 }
176 };
177
178 let data = tcx.global_alloc(alloc_id).unwrap_memory();
179
180 let start = start.try_into().unwrap();
182 let end = start + usize::try_from(len).unwrap();
183 Some(data.inner().inspect_with_uninit_and_ptr_outside_interpreter(start..end))
184 }
185
186 pub fn may_have_provenance(&self, tcx: TyCtxt<'_>, size: Size) -> bool {
189 match *self {
190 ConstValue::ZeroSized | ConstValue::Scalar(Scalar::Int(_)) => return false,
191 ConstValue::Scalar(Scalar::Ptr(..)) => return true,
192 ConstValue::Slice { alloc_id, meta: _ } => {
195 !tcx.global_alloc(alloc_id).unwrap_memory().inner().provenance().ptrs().is_empty()
196 }
197 ConstValue::Indirect { alloc_id, offset } => !tcx
198 .global_alloc(alloc_id)
199 .unwrap_memory()
200 .inner()
201 .provenance()
202 .range_empty(AllocRange::from(offset..offset + size), &tcx),
203 }
204 }
205
206 pub fn all_bytes_uninit(&self, tcx: TyCtxt<'_>) -> bool {
208 let ConstValue::Indirect { alloc_id, .. } = self else {
209 return false;
210 };
211 let alloc = tcx.global_alloc(*alloc_id);
212 let GlobalAlloc::Memory(alloc) = alloc else {
213 return false;
214 };
215 let init_mask = alloc.0.init_mask();
216 let init_range = init_mask.is_range_initialized(AllocRange {
217 start: Size::ZERO,
218 size: Size::from_bytes(alloc.0.len()),
219 });
220 if let Err(range) = init_range {
221 if range.size == alloc.0.size() {
222 return true;
223 }
224 }
225 false
226 }
227}
228
229#[derive(Clone, Copy, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable, Debug)]
233#[derive(TypeFoldable, TypeVisitable, Lift)]
234pub enum Const<'tcx> {
235 Ty(Ty<'tcx>, ty::Const<'tcx>),
243
244 Unevaluated(UnevaluatedConst<'tcx>, Ty<'tcx>),
251
252 Val(ConstValue, Ty<'tcx>),
255}
256
257impl<'tcx> Const<'tcx> {
258 pub fn from_unevaluated(
261 tcx: TyCtxt<'tcx>,
262 def_id: DefId,
263 ) -> ty::EarlyBinder<'tcx, Const<'tcx>> {
264 ty::EarlyBinder::bind(Const::Unevaluated(
265 UnevaluatedConst {
266 def: def_id,
267 args: ty::GenericArgs::identity_for_item(tcx, def_id),
268 promoted: None,
269 },
270 tcx.type_of(def_id).skip_binder(),
271 ))
272 }
273
274 #[inline(always)]
275 pub fn ty(&self) -> Ty<'tcx> {
276 match self {
277 Const::Ty(ty, ct) => {
278 match ct.kind() {
279 ty::ConstKind::Value(cv) => cv.ty,
283 _ => *ty,
284 }
285 }
286 Const::Val(_, ty) | Const::Unevaluated(_, ty) => *ty,
287 }
288 }
289
290 #[inline]
293 pub fn is_required_const(&self) -> bool {
294 match self {
295 Const::Ty(_, c) => match c.kind() {
296 ty::ConstKind::Value(_) => false, _ => true,
298 },
299 Const::Val(..) => false, Const::Unevaluated(..) => true,
301 }
302 }
303
304 #[inline]
305 pub fn try_to_scalar(self) -> Option<Scalar> {
306 match self {
307 Const::Ty(_, c) => match c.kind() {
308 ty::ConstKind::Value(cv) if cv.ty.is_primitive() => {
309 Some(cv.valtree.unwrap_leaf().into())
313 }
314 _ => None,
315 },
316 Const::Val(val, _) => val.try_to_scalar(),
317 Const::Unevaluated(..) => None,
318 }
319 }
320
321 #[inline]
322 pub fn try_to_scalar_int(self) -> Option<ScalarInt> {
323 match self {
325 Const::Val(ConstValue::Scalar(Scalar::Int(x)), _) => Some(x),
326 Const::Ty(_, c) => match c.kind() {
327 ty::ConstKind::Value(cv) if cv.ty.is_primitive() => Some(cv.valtree.unwrap_leaf()),
328 _ => None,
329 },
330 _ => None,
331 }
332 }
333
334 #[inline]
335 pub fn try_to_bits(self, size: Size) -> Option<u128> {
336 Some(self.try_to_scalar_int()?.to_bits(size))
337 }
338
339 #[inline]
340 pub fn try_to_bool(self) -> Option<bool> {
341 self.try_to_scalar_int()?.try_into().ok()
342 }
343
344 #[inline]
345 pub fn eval(
346 self,
347 tcx: TyCtxt<'tcx>,
348 typing_env: ty::TypingEnv<'tcx>,
349 span: Span,
350 ) -> Result<ConstValue, ErrorHandled> {
351 match self {
352 Const::Ty(_, c) => {
353 if c.has_non_region_param() {
354 return Err(ErrorHandled::TooGeneric(span));
355 }
356
357 match c.kind() {
358 ConstKind::Value(cv) => Ok(tcx.valtree_to_const_val(cv)),
359 ConstKind::Expr(_) => {
360 bug!("Normalization of `ty::ConstKind::Expr` is unimplemented")
361 }
362 _ => Err(ReportedErrorInfo::non_const_eval_error(
363 tcx.dcx().delayed_bug("Unevaluated `ty::Const` in MIR body"),
364 )
365 .into()),
366 }
367 }
368 Const::Unevaluated(uneval, _) => {
369 tcx.const_eval_resolve(typing_env, uneval, span)
371 }
372 Const::Val(val, _) => Ok(val),
373 }
374 }
375
376 #[inline]
377 pub fn try_eval_scalar(
378 self,
379 tcx: TyCtxt<'tcx>,
380 typing_env: ty::TypingEnv<'tcx>,
381 ) -> Option<Scalar> {
382 if let Const::Ty(_, c) = self
383 && let ty::ConstKind::Value(cv) = c.kind()
384 && cv.ty.is_primitive()
385 {
386 Some(cv.valtree.unwrap_leaf().into())
390 } else {
391 self.eval(tcx, typing_env, DUMMY_SP).ok()?.try_to_scalar()
392 }
393 }
394
395 #[inline]
396 pub fn try_eval_scalar_int(
397 self,
398 tcx: TyCtxt<'tcx>,
399 typing_env: ty::TypingEnv<'tcx>,
400 ) -> Option<ScalarInt> {
401 self.try_eval_scalar(tcx, typing_env)?.try_to_scalar_int().ok()
402 }
403
404 #[inline]
405 pub fn try_eval_bits(
406 &self,
407 tcx: TyCtxt<'tcx>,
408 typing_env: ty::TypingEnv<'tcx>,
409 ) -> Option<u128> {
410 let int = self.try_eval_scalar_int(tcx, typing_env)?;
411 let size = tcx
412 .layout_of(typing_env.with_post_analysis_normalized(tcx).as_query_input(self.ty()))
413 .ok()?
414 .size;
415 Some(int.to_bits(size))
416 }
417
418 #[inline]
420 pub fn eval_bits(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> u128 {
421 self.try_eval_bits(tcx, typing_env)
422 .unwrap_or_else(|| bug!("expected bits of {:#?}, got {:#?}", self.ty(), self))
423 }
424
425 #[inline]
426 pub fn try_eval_target_usize(
427 self,
428 tcx: TyCtxt<'tcx>,
429 typing_env: ty::TypingEnv<'tcx>,
430 ) -> Option<u64> {
431 Some(self.try_eval_scalar_int(tcx, typing_env)?.to_target_usize(tcx))
432 }
433
434 #[inline]
435 pub fn eval_target_usize(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> u64 {
437 self.try_eval_target_usize(tcx, typing_env)
438 .unwrap_or_else(|| bug!("expected usize, got {:#?}", self))
439 }
440
441 #[inline]
442 pub fn try_eval_bool(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> Option<bool> {
443 self.try_eval_scalar_int(tcx, typing_env)?.try_into().ok()
444 }
445
446 #[inline]
447 pub fn from_value(val: ConstValue, ty: Ty<'tcx>) -> Self {
448 Self::Val(val, ty)
449 }
450
451 #[inline]
452 pub fn from_ty_value(tcx: TyCtxt<'tcx>, val: ty::Value<'tcx>) -> Self {
453 Self::Ty(val.ty, ty::Const::new_value(tcx, val.valtree, val.ty))
454 }
455
456 pub fn from_bits(
457 tcx: TyCtxt<'tcx>,
458 bits: u128,
459 typing_env: ty::TypingEnv<'tcx>,
460 ty: Ty<'tcx>,
461 ) -> Self {
462 let size = tcx
463 .layout_of(typing_env.as_query_input(ty))
464 .unwrap_or_else(|e| bug!("could not compute layout for {ty:?}: {e:?}"))
465 .size;
466 let cv = ConstValue::Scalar(Scalar::from_uint(bits, size));
467
468 Self::Val(cv, ty)
469 }
470
471 #[inline]
472 pub fn from_bool(tcx: TyCtxt<'tcx>, v: bool) -> Self {
473 let cv = ConstValue::from_bool(v);
474 Self::Val(cv, tcx.types.bool)
475 }
476
477 #[inline]
478 pub fn zero_sized(ty: Ty<'tcx>) -> Self {
479 let cv = ConstValue::ZeroSized;
480 Self::Val(cv, ty)
481 }
482
483 pub fn from_usize(tcx: TyCtxt<'tcx>, n: u64) -> Self {
484 let ty = tcx.types.usize;
485 let typing_env = ty::TypingEnv::fully_monomorphized();
486 Self::from_bits(tcx, n as u128, typing_env, ty)
487 }
488
489 #[inline]
490 pub fn from_scalar(_tcx: TyCtxt<'tcx>, s: Scalar, ty: Ty<'tcx>) -> Self {
491 let val = ConstValue::Scalar(s);
492 Self::Val(val, ty)
493 }
494
495 pub fn is_deterministic(&self) -> bool {
498 match self {
502 Const::Ty(_, c) => match c.kind() {
503 ty::ConstKind::Param(..) => true,
504 ty::ConstKind::Value(cv) => cv.ty.is_primitive(),
508 ty::ConstKind::Unevaluated(..) | ty::ConstKind::Expr(..) => false,
509 ty::ConstKind::Error(..) => false,
512 ty::ConstKind::Infer(..)
514 | ty::ConstKind::Bound(..)
515 | ty::ConstKind::Placeholder(..) => bug!(),
516 },
517 Const::Unevaluated(..) => false,
518 Const::Val(
519 ConstValue::Slice { .. }
520 | ConstValue::ZeroSized
521 | ConstValue::Scalar(_)
522 | ConstValue::Indirect { .. },
523 _,
524 ) => true,
525 }
526 }
527}
528
529#[derive(Copy, Clone, Debug, Eq, PartialEq, TyEncodable, TyDecodable)]
531#[derive(Hash, HashStable, TypeFoldable, TypeVisitable, Lift)]
532pub struct UnevaluatedConst<'tcx> {
533 pub def: DefId,
534 pub args: GenericArgsRef<'tcx>,
535 pub promoted: Option<Promoted>,
536}
537
538impl<'tcx> UnevaluatedConst<'tcx> {
539 #[inline]
540 pub fn shrink(self) -> ty::UnevaluatedConst<'tcx> {
541 assert_eq!(self.promoted, None);
542 ty::UnevaluatedConst { def: self.def, args: self.args }
543 }
544}
545
546impl<'tcx> UnevaluatedConst<'tcx> {
547 #[inline]
548 pub fn new(def: DefId, args: GenericArgsRef<'tcx>) -> UnevaluatedConst<'tcx> {
549 UnevaluatedConst { def, args, promoted: Default::default() }
550 }
551
552 #[inline]
553 pub fn from_instance(instance: ty::Instance<'tcx>) -> Self {
554 UnevaluatedConst::new(instance.def_id(), instance.args)
555 }
556}
557
558impl<'tcx> Display for Const<'tcx> {
559 fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
560 match *self {
561 Const::Ty(_, c) => pretty_print_const(c, fmt, true),
562 Const::Val(val, ty) => pretty_print_const_value(val, ty, fmt),
563 Const::Unevaluated(c, _ty) => {
565 ty::tls::with(move |tcx| {
566 let c = tcx.lift(c).unwrap();
567 let instance =
569 with_no_trimmed_paths!(tcx.def_path_str_with_args(c.def, c.args));
570 write!(fmt, "{instance}")?;
571 if let Some(promoted) = c.promoted {
572 write!(fmt, "::{promoted:?}")?;
573 }
574 Ok(())
575 })
576 }
577 }
578 }
579}
580
581impl<'tcx> TyCtxt<'tcx> {
585 pub fn span_as_caller_location(self, span: Span) -> ConstValue {
586 let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
587 let caller = self.sess.source_map().lookup_char_pos(topmost.lo());
588 self.const_caller_location(
589 Symbol::intern(
590 &caller
591 .file
592 .name
593 .for_scope(self.sess, RemapPathScopeComponents::MACRO)
594 .to_string_lossy(),
595 ),
596 caller.line as u32,
597 caller.col_display as u32 + 1,
598 )
599 }
600}