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