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) =
172 ptr.into_pointer_or_addr().ok()?.prov_and_relative_offset();
173 let data = tcx.global_alloc(inner_prov.alloc_id()).unwrap_memory();
174 (data, offset.bytes(), offset.bytes() + len)
175 }
176 };
177
178 let start = start.try_into().unwrap();
180 let end = end.try_into().unwrap();
181 Some(data.inner().inspect_with_uninit_and_ptr_outside_interpreter(start..end))
182 }
183
184 pub fn may_have_provenance(&self, tcx: TyCtxt<'tcx>, size: Size) -> bool {
187 match *self {
188 ConstValue::ZeroSized | ConstValue::Scalar(Scalar::Int(_)) => return false,
189 ConstValue::Scalar(Scalar::Ptr(..)) => return true,
190 ConstValue::Slice { data, meta: _ } => !data.inner().provenance().ptrs().is_empty(),
193 ConstValue::Indirect { alloc_id, offset } => !tcx
194 .global_alloc(alloc_id)
195 .unwrap_memory()
196 .inner()
197 .provenance()
198 .range_empty(AllocRange::from(offset..offset + size), &tcx),
199 }
200 }
201
202 pub fn all_bytes_uninit(&self, tcx: TyCtxt<'tcx>) -> bool {
204 let ConstValue::Indirect { alloc_id, .. } = self else {
205 return false;
206 };
207 let alloc = tcx.global_alloc(*alloc_id);
208 let GlobalAlloc::Memory(alloc) = alloc else {
209 return false;
210 };
211 let init_mask = alloc.0.init_mask();
212 let init_range = init_mask.is_range_initialized(AllocRange {
213 start: Size::ZERO,
214 size: Size::from_bytes(alloc.0.len()),
215 });
216 if let Err(range) = init_range {
217 if range.size == alloc.0.size() {
218 return true;
219 }
220 }
221 false
222 }
223}
224
225#[derive(Clone, Copy, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable, Debug)]
229#[derive(TypeFoldable, TypeVisitable, Lift)]
230pub enum Const<'tcx> {
231 Ty(Ty<'tcx>, ty::Const<'tcx>),
239
240 Unevaluated(UnevaluatedConst<'tcx>, Ty<'tcx>),
247
248 Val(ConstValue<'tcx>, Ty<'tcx>),
251}
252
253impl<'tcx> Const<'tcx> {
254 pub fn from_unevaluated(
257 tcx: TyCtxt<'tcx>,
258 def_id: DefId,
259 ) -> ty::EarlyBinder<'tcx, Const<'tcx>> {
260 ty::EarlyBinder::bind(Const::Unevaluated(
261 UnevaluatedConst {
262 def: def_id,
263 args: ty::GenericArgs::identity_for_item(tcx, def_id),
264 promoted: None,
265 },
266 tcx.type_of(def_id).skip_binder(),
267 ))
268 }
269
270 #[inline(always)]
271 pub fn ty(&self) -> Ty<'tcx> {
272 match self {
273 Const::Ty(ty, ct) => {
274 match ct.kind() {
275 ty::ConstKind::Value(cv) => cv.ty,
279 _ => *ty,
280 }
281 }
282 Const::Val(_, ty) | Const::Unevaluated(_, ty) => *ty,
283 }
284 }
285
286 #[inline]
289 pub fn is_required_const(&self) -> bool {
290 match self {
291 Const::Ty(_, c) => match c.kind() {
292 ty::ConstKind::Value(_) => false, _ => true,
294 },
295 Const::Val(..) => false, Const::Unevaluated(..) => true,
297 }
298 }
299
300 #[inline]
301 pub fn try_to_scalar(self) -> Option<Scalar> {
302 match self {
303 Const::Ty(_, c) => match c.kind() {
304 ty::ConstKind::Value(cv) if cv.ty.is_primitive() => {
305 Some(cv.valtree.unwrap_leaf().into())
309 }
310 _ => None,
311 },
312 Const::Val(val, _) => val.try_to_scalar(),
313 Const::Unevaluated(..) => None,
314 }
315 }
316
317 #[inline]
318 pub fn try_to_scalar_int(self) -> Option<ScalarInt> {
319 match self {
321 Const::Val(ConstValue::Scalar(Scalar::Int(x)), _) => Some(x),
322 Const::Ty(_, c) => match c.kind() {
323 ty::ConstKind::Value(cv) if cv.ty.is_primitive() => Some(cv.valtree.unwrap_leaf()),
324 _ => None,
325 },
326 _ => None,
327 }
328 }
329
330 #[inline]
331 pub fn try_to_bits(self, size: Size) -> Option<u128> {
332 Some(self.try_to_scalar_int()?.to_bits(size))
333 }
334
335 #[inline]
336 pub fn try_to_bool(self) -> Option<bool> {
337 self.try_to_scalar_int()?.try_into().ok()
338 }
339
340 #[inline]
341 pub fn eval(
342 self,
343 tcx: TyCtxt<'tcx>,
344 typing_env: ty::TypingEnv<'tcx>,
345 span: Span,
346 ) -> Result<ConstValue<'tcx>, ErrorHandled> {
347 match self {
348 Const::Ty(_, c) => {
349 if c.has_non_region_param() {
350 return Err(ErrorHandled::TooGeneric(span));
351 }
352
353 match c.kind() {
354 ConstKind::Value(cv) => Ok(tcx.valtree_to_const_val(cv)),
355 ConstKind::Expr(_) => {
356 bug!("Normalization of `ty::ConstKind::Expr` is unimplemented")
357 }
358 _ => Err(ReportedErrorInfo::non_const_eval_error(
359 tcx.dcx().delayed_bug("Unevaluated `ty::Const` in MIR body"),
360 )
361 .into()),
362 }
363 }
364 Const::Unevaluated(uneval, _) => {
365 tcx.const_eval_resolve(typing_env, uneval, span)
367 }
368 Const::Val(val, _) => Ok(val),
369 }
370 }
371
372 #[inline]
373 pub fn try_eval_scalar(
374 self,
375 tcx: TyCtxt<'tcx>,
376 typing_env: ty::TypingEnv<'tcx>,
377 ) -> Option<Scalar> {
378 if let Const::Ty(_, c) = self
379 && let ty::ConstKind::Value(cv) = c.kind()
380 && cv.ty.is_primitive()
381 {
382 Some(cv.valtree.unwrap_leaf().into())
386 } else {
387 self.eval(tcx, typing_env, DUMMY_SP).ok()?.try_to_scalar()
388 }
389 }
390
391 #[inline]
392 pub fn try_eval_scalar_int(
393 self,
394 tcx: TyCtxt<'tcx>,
395 typing_env: ty::TypingEnv<'tcx>,
396 ) -> Option<ScalarInt> {
397 self.try_eval_scalar(tcx, typing_env)?.try_to_scalar_int().ok()
398 }
399
400 #[inline]
401 pub fn try_eval_bits(
402 &self,
403 tcx: TyCtxt<'tcx>,
404 typing_env: ty::TypingEnv<'tcx>,
405 ) -> Option<u128> {
406 let int = self.try_eval_scalar_int(tcx, typing_env)?;
407 let size = tcx
408 .layout_of(typing_env.with_post_analysis_normalized(tcx).as_query_input(self.ty()))
409 .ok()?
410 .size;
411 Some(int.to_bits(size))
412 }
413
414 #[inline]
416 pub fn eval_bits(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> u128 {
417 self.try_eval_bits(tcx, typing_env)
418 .unwrap_or_else(|| bug!("expected bits of {:#?}, got {:#?}", self.ty(), self))
419 }
420
421 #[inline]
422 pub fn try_eval_target_usize(
423 self,
424 tcx: TyCtxt<'tcx>,
425 typing_env: ty::TypingEnv<'tcx>,
426 ) -> Option<u64> {
427 Some(self.try_eval_scalar_int(tcx, typing_env)?.to_target_usize(tcx))
428 }
429
430 #[inline]
431 pub fn eval_target_usize(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> u64 {
433 self.try_eval_target_usize(tcx, typing_env)
434 .unwrap_or_else(|| bug!("expected usize, got {:#?}", self))
435 }
436
437 #[inline]
438 pub fn try_eval_bool(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> Option<bool> {
439 self.try_eval_scalar_int(tcx, typing_env)?.try_into().ok()
440 }
441
442 #[inline]
443 pub fn from_value(val: ConstValue<'tcx>, ty: Ty<'tcx>) -> Self {
444 Self::Val(val, ty)
445 }
446
447 pub fn from_bits(
448 tcx: TyCtxt<'tcx>,
449 bits: u128,
450 typing_env: ty::TypingEnv<'tcx>,
451 ty: Ty<'tcx>,
452 ) -> Self {
453 let size = tcx
454 .layout_of(typing_env.as_query_input(ty))
455 .unwrap_or_else(|e| bug!("could not compute layout for {ty:?}: {e:?}"))
456 .size;
457 let cv = ConstValue::Scalar(Scalar::from_uint(bits, size));
458
459 Self::Val(cv, ty)
460 }
461
462 #[inline]
463 pub fn from_bool(tcx: TyCtxt<'tcx>, v: bool) -> Self {
464 let cv = ConstValue::from_bool(v);
465 Self::Val(cv, tcx.types.bool)
466 }
467
468 #[inline]
469 pub fn zero_sized(ty: Ty<'tcx>) -> Self {
470 let cv = ConstValue::ZeroSized;
471 Self::Val(cv, ty)
472 }
473
474 pub fn from_usize(tcx: TyCtxt<'tcx>, n: u64) -> Self {
475 let ty = tcx.types.usize;
476 let typing_env = ty::TypingEnv::fully_monomorphized();
477 Self::from_bits(tcx, n as u128, typing_env, ty)
478 }
479
480 #[inline]
481 pub fn from_scalar(_tcx: TyCtxt<'tcx>, s: Scalar, ty: Ty<'tcx>) -> Self {
482 let val = ConstValue::Scalar(s);
483 Self::Val(val, ty)
484 }
485
486 pub fn is_deterministic(&self) -> bool {
489 match self {
494 Const::Ty(_, c) => match c.kind() {
495 ty::ConstKind::Param(..) => true,
496 ty::ConstKind::Value(cv) => cv.ty.is_primitive(),
500 ty::ConstKind::Unevaluated(..) | ty::ConstKind::Expr(..) => false,
501 ty::ConstKind::Error(..) => false,
504 ty::ConstKind::Infer(..)
506 | ty::ConstKind::Bound(..)
507 | ty::ConstKind::Placeholder(..) => bug!(),
508 },
509 Const::Unevaluated(..) => false,
510 Const::Val(ConstValue::Slice { .. }, _) => false,
513 Const::Val(
514 ConstValue::ZeroSized | ConstValue::Scalar(_) | ConstValue::Indirect { .. },
515 _,
516 ) => true,
517 }
518 }
519}
520
521#[derive(Copy, Clone, Debug, Eq, PartialEq, TyEncodable, TyDecodable)]
523#[derive(Hash, HashStable, TypeFoldable, TypeVisitable, Lift)]
524pub struct UnevaluatedConst<'tcx> {
525 pub def: DefId,
526 pub args: GenericArgsRef<'tcx>,
527 pub promoted: Option<Promoted>,
528}
529
530impl<'tcx> UnevaluatedConst<'tcx> {
531 #[inline]
532 pub fn shrink(self) -> ty::UnevaluatedConst<'tcx> {
533 assert_eq!(self.promoted, None);
534 ty::UnevaluatedConst { def: self.def, args: self.args }
535 }
536}
537
538impl<'tcx> UnevaluatedConst<'tcx> {
539 #[inline]
540 pub fn new(def: DefId, args: GenericArgsRef<'tcx>) -> UnevaluatedConst<'tcx> {
541 UnevaluatedConst { def, args, promoted: Default::default() }
542 }
543
544 #[inline]
545 pub fn from_instance(instance: ty::Instance<'tcx>) -> Self {
546 UnevaluatedConst::new(instance.def_id(), instance.args)
547 }
548}
549
550impl<'tcx> Display for Const<'tcx> {
551 fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
552 match *self {
553 Const::Ty(_, c) => pretty_print_const(c, fmt, true),
554 Const::Val(val, ty) => pretty_print_const_value(val, ty, fmt),
555 Const::Unevaluated(c, _ty) => {
557 ty::tls::with(move |tcx| {
558 let c = tcx.lift(c).unwrap();
559 let instance =
561 with_no_trimmed_paths!(tcx.def_path_str_with_args(c.def, c.args));
562 write!(fmt, "{instance}")?;
563 if let Some(promoted) = c.promoted {
564 write!(fmt, "::{promoted:?}")?;
565 }
566 Ok(())
567 })
568 }
569 }
570 }
571}
572
573impl<'tcx> TyCtxt<'tcx> {
577 pub fn span_as_caller_location(self, span: Span) -> ConstValue<'tcx> {
578 let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
579 let caller = self.sess.source_map().lookup_char_pos(topmost.lo());
580 self.const_caller_location(
581 Symbol::intern(
582 &caller
583 .file
584 .name
585 .for_scope(self.sess, RemapPathScopeComponents::MACRO)
586 .to_string_lossy(),
587 ),
588 caller.line as u32,
589 caller.col_display as u32 + 1,
590 )
591 }
592}