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