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::visit::TypeVisitableExt;
10
11use super::interpret::ReportedErrorInfo;
12use crate::mir::interpret::{AllocId, ConstAllocation, ErrorHandled, 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, Lift)]
35pub enum ConstValue<'tcx> {
36 Scalar(Scalar),
40
41 ZeroSized,
43
44 Slice {
51 data: ConstAllocation<'tcx>,
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<'tcx> ConstValue<'tcx> {
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<'tcx>) -> Option<u64> {
100 Some(self.try_to_scalar_int()?.to_target_usize(tcx))
101 }
102
103 pub fn try_to_bits_for_ty(
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(&self, tcx: TyCtxt<'tcx>) -> Option<&'tcx [u8]> {
134 let (data, start, end) = match self {
135 ConstValue::Scalar(_) | ConstValue::ZeroSized => {
136 bug!("`try_get_slice_bytes` on non-slice constant")
137 }
138 &ConstValue::Slice { data, meta } => (data, 0, meta),
139 &ConstValue::Indirect { alloc_id, offset } => {
140 let a = tcx.global_alloc(alloc_id).unwrap_memory().inner();
143 let ptr_size = tcx.data_layout.pointer_size;
144 if a.size() < offset + 2 * ptr_size {
145 return None;
147 }
148 let ptr = a
150 .read_scalar(
151 &tcx,
152 alloc_range(offset, ptr_size),
153 true,
154 )
155 .ok()?;
156 let ptr = ptr.to_pointer(&tcx).discard_err()?;
157 let len = a
158 .read_scalar(
159 &tcx,
160 alloc_range(offset + ptr_size, ptr_size),
161 false,
162 )
163 .ok()?;
164 let len = len.to_target_usize(&tcx).discard_err()?;
165 if len == 0 {
166 return Some(&[]);
167 }
168 let (inner_prov, offset) = ptr.into_parts();
170 let data = tcx.global_alloc(inner_prov?.alloc_id()).unwrap_memory();
171 (data, offset.bytes(), offset.bytes() + len)
172 }
173 };
174
175 let start = start.try_into().unwrap();
177 let end = end.try_into().unwrap();
178 Some(data.inner().inspect_with_uninit_and_ptr_outside_interpreter(start..end))
179 }
180
181 pub fn may_have_provenance(&self, tcx: TyCtxt<'tcx>, size: Size) -> bool {
184 match *self {
185 ConstValue::ZeroSized | ConstValue::Scalar(Scalar::Int(_)) => return false,
186 ConstValue::Scalar(Scalar::Ptr(..)) => return true,
187 ConstValue::Slice { data, meta: _ } => !data.inner().provenance().ptrs().is_empty(),
190 ConstValue::Indirect { alloc_id, offset } => !tcx
191 .global_alloc(alloc_id)
192 .unwrap_memory()
193 .inner()
194 .provenance()
195 .range_empty(super::AllocRange::from(offset..offset + size), &tcx),
196 }
197 }
198}
199
200#[derive(Clone, Copy, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable, Debug)]
204#[derive(TypeFoldable, TypeVisitable, Lift)]
205pub enum Const<'tcx> {
206 Ty(Ty<'tcx>, ty::Const<'tcx>),
214
215 Unevaluated(UnevaluatedConst<'tcx>, Ty<'tcx>),
222
223 Val(ConstValue<'tcx>, Ty<'tcx>),
226}
227
228impl<'tcx> Const<'tcx> {
229 pub fn from_unevaluated(
232 tcx: TyCtxt<'tcx>,
233 def_id: DefId,
234 ) -> ty::EarlyBinder<'tcx, Const<'tcx>> {
235 ty::EarlyBinder::bind(Const::Unevaluated(
236 UnevaluatedConst {
237 def: def_id,
238 args: ty::GenericArgs::identity_for_item(tcx, def_id),
239 promoted: None,
240 },
241 tcx.type_of(def_id).skip_binder(),
242 ))
243 }
244
245 #[inline(always)]
246 pub fn ty(&self) -> Ty<'tcx> {
247 match self {
248 Const::Ty(ty, ct) => {
249 match ct.kind() {
250 ty::ConstKind::Value(cv) => cv.ty,
254 _ => *ty,
255 }
256 }
257 Const::Val(_, ty) | Const::Unevaluated(_, ty) => *ty,
258 }
259 }
260
261 #[inline]
264 pub fn is_required_const(&self) -> bool {
265 match self {
266 Const::Ty(_, c) => match c.kind() {
267 ty::ConstKind::Value(_) => false, _ => true,
269 },
270 Const::Val(..) => false, Const::Unevaluated(..) => true,
272 }
273 }
274
275 #[inline]
276 pub fn try_to_scalar(self) -> Option<Scalar> {
277 match self {
278 Const::Ty(_, c) => match c.kind() {
279 ty::ConstKind::Value(cv) if cv.ty.is_primitive() => {
280 Some(cv.valtree.unwrap_leaf().into())
284 }
285 _ => None,
286 },
287 Const::Val(val, _) => val.try_to_scalar(),
288 Const::Unevaluated(..) => None,
289 }
290 }
291
292 #[inline]
293 pub fn try_to_scalar_int(self) -> Option<ScalarInt> {
294 match self {
296 Const::Val(ConstValue::Scalar(Scalar::Int(x)), _) => Some(x),
297 Const::Ty(_, c) => match c.kind() {
298 ty::ConstKind::Value(cv) if cv.ty.is_primitive() => Some(cv.valtree.unwrap_leaf()),
299 _ => None,
300 },
301 _ => None,
302 }
303 }
304
305 #[inline]
306 pub fn try_to_bits(self, size: Size) -> Option<u128> {
307 Some(self.try_to_scalar_int()?.to_bits(size))
308 }
309
310 #[inline]
311 pub fn try_to_bool(self) -> Option<bool> {
312 self.try_to_scalar_int()?.try_into().ok()
313 }
314
315 #[inline]
316 pub fn eval(
317 self,
318 tcx: TyCtxt<'tcx>,
319 typing_env: ty::TypingEnv<'tcx>,
320 span: Span,
321 ) -> Result<ConstValue<'tcx>, ErrorHandled> {
322 match self {
323 Const::Ty(_, c) => {
324 if c.has_non_region_param() {
325 return Err(ErrorHandled::TooGeneric(span));
326 }
327
328 match c.kind() {
329 ConstKind::Value(cv) => Ok(tcx.valtree_to_const_val(cv)),
330 ConstKind::Expr(_) => {
331 bug!("Normalization of `ty::ConstKind::Expr` is unimplemented")
332 }
333 _ => Err(ReportedErrorInfo::non_const_eval_error(
334 tcx.dcx().delayed_bug("Unevaluated `ty::Const` in MIR body"),
335 )
336 .into()),
337 }
338 }
339 Const::Unevaluated(uneval, _) => {
340 tcx.const_eval_resolve(typing_env, uneval, span)
342 }
343 Const::Val(val, _) => Ok(val),
344 }
345 }
346
347 #[inline]
348 pub fn try_eval_scalar(
349 self,
350 tcx: TyCtxt<'tcx>,
351 typing_env: ty::TypingEnv<'tcx>,
352 ) -> Option<Scalar> {
353 if let Const::Ty(_, c) = self
354 && let ty::ConstKind::Value(cv) = c.kind()
355 && cv.ty.is_primitive()
356 {
357 Some(cv.valtree.unwrap_leaf().into())
361 } else {
362 self.eval(tcx, typing_env, DUMMY_SP).ok()?.try_to_scalar()
363 }
364 }
365
366 #[inline]
367 pub fn try_eval_scalar_int(
368 self,
369 tcx: TyCtxt<'tcx>,
370 typing_env: ty::TypingEnv<'tcx>,
371 ) -> Option<ScalarInt> {
372 self.try_eval_scalar(tcx, typing_env)?.try_to_scalar_int().ok()
373 }
374
375 #[inline]
376 pub fn try_eval_bits(
377 &self,
378 tcx: TyCtxt<'tcx>,
379 typing_env: ty::TypingEnv<'tcx>,
380 ) -> Option<u128> {
381 let int = self.try_eval_scalar_int(tcx, typing_env)?;
382 let size = tcx
383 .layout_of(typing_env.with_post_analysis_normalized(tcx).as_query_input(self.ty()))
384 .ok()?
385 .size;
386 Some(int.to_bits(size))
387 }
388
389 #[inline]
391 pub fn eval_bits(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> u128 {
392 self.try_eval_bits(tcx, typing_env)
393 .unwrap_or_else(|| bug!("expected bits of {:#?}, got {:#?}", self.ty(), self))
394 }
395
396 #[inline]
397 pub fn try_eval_target_usize(
398 self,
399 tcx: TyCtxt<'tcx>,
400 typing_env: ty::TypingEnv<'tcx>,
401 ) -> Option<u64> {
402 Some(self.try_eval_scalar_int(tcx, typing_env)?.to_target_usize(tcx))
403 }
404
405 #[inline]
406 pub fn eval_target_usize(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> u64 {
408 self.try_eval_target_usize(tcx, typing_env)
409 .unwrap_or_else(|| bug!("expected usize, got {:#?}", self))
410 }
411
412 #[inline]
413 pub fn try_eval_bool(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> Option<bool> {
414 self.try_eval_scalar_int(tcx, typing_env)?.try_into().ok()
415 }
416
417 #[inline]
418 pub fn from_value(val: ConstValue<'tcx>, ty: Ty<'tcx>) -> Self {
419 Self::Val(val, ty)
420 }
421
422 pub fn from_bits(
423 tcx: TyCtxt<'tcx>,
424 bits: u128,
425 typing_env: ty::TypingEnv<'tcx>,
426 ty: Ty<'tcx>,
427 ) -> Self {
428 let size = tcx
429 .layout_of(typing_env.as_query_input(ty))
430 .unwrap_or_else(|e| bug!("could not compute layout for {ty:?}: {e:?}"))
431 .size;
432 let cv = ConstValue::Scalar(Scalar::from_uint(bits, size));
433
434 Self::Val(cv, ty)
435 }
436
437 #[inline]
438 pub fn from_bool(tcx: TyCtxt<'tcx>, v: bool) -> Self {
439 let cv = ConstValue::from_bool(v);
440 Self::Val(cv, tcx.types.bool)
441 }
442
443 #[inline]
444 pub fn zero_sized(ty: Ty<'tcx>) -> Self {
445 let cv = ConstValue::ZeroSized;
446 Self::Val(cv, ty)
447 }
448
449 pub fn from_usize(tcx: TyCtxt<'tcx>, n: u64) -> Self {
450 let ty = tcx.types.usize;
451 let typing_env = ty::TypingEnv::fully_monomorphized();
452 Self::from_bits(tcx, n as u128, typing_env, ty)
453 }
454
455 #[inline]
456 pub fn from_scalar(_tcx: TyCtxt<'tcx>, s: Scalar, ty: Ty<'tcx>) -> Self {
457 let val = ConstValue::Scalar(s);
458 Self::Val(val, ty)
459 }
460
461 pub fn is_deterministic(&self) -> bool {
464 match self {
469 Const::Ty(_, c) => match c.kind() {
470 ty::ConstKind::Param(..) => true,
471 ty::ConstKind::Value(cv) => cv.ty.is_primitive(),
475 ty::ConstKind::Unevaluated(..) | ty::ConstKind::Expr(..) => false,
476 ty::ConstKind::Error(..) => false,
479 ty::ConstKind::Infer(..)
481 | ty::ConstKind::Bound(..)
482 | ty::ConstKind::Placeholder(..) => bug!(),
483 },
484 Const::Unevaluated(..) => false,
485 Const::Val(ConstValue::Slice { .. }, _) => false,
488 Const::Val(
489 ConstValue::ZeroSized | ConstValue::Scalar(_) | ConstValue::Indirect { .. },
490 _,
491 ) => true,
492 }
493 }
494}
495
496#[derive(Copy, Clone, Debug, Eq, PartialEq, TyEncodable, TyDecodable)]
498#[derive(Hash, HashStable, TypeFoldable, TypeVisitable, Lift)]
499pub struct UnevaluatedConst<'tcx> {
500 pub def: DefId,
501 pub args: GenericArgsRef<'tcx>,
502 pub promoted: Option<Promoted>,
503}
504
505impl<'tcx> UnevaluatedConst<'tcx> {
506 #[inline]
507 pub fn shrink(self) -> ty::UnevaluatedConst<'tcx> {
508 assert_eq!(self.promoted, None);
509 ty::UnevaluatedConst { def: self.def, args: self.args }
510 }
511}
512
513impl<'tcx> UnevaluatedConst<'tcx> {
514 #[inline]
515 pub fn new(def: DefId, args: GenericArgsRef<'tcx>) -> UnevaluatedConst<'tcx> {
516 UnevaluatedConst { def, args, promoted: Default::default() }
517 }
518
519 #[inline]
520 pub fn from_instance(instance: ty::Instance<'tcx>) -> Self {
521 UnevaluatedConst::new(instance.def_id(), instance.args)
522 }
523}
524
525impl<'tcx> Display for Const<'tcx> {
526 fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
527 match *self {
528 Const::Ty(_, c) => pretty_print_const(c, fmt, true),
529 Const::Val(val, ty) => pretty_print_const_value(val, ty, fmt),
530 Const::Unevaluated(c, _ty) => {
532 ty::tls::with(move |tcx| {
533 let c = tcx.lift(c).unwrap();
534 let instance =
536 with_no_trimmed_paths!(tcx.def_path_str_with_args(c.def, c.args));
537 write!(fmt, "{instance}")?;
538 if let Some(promoted) = c.promoted {
539 write!(fmt, "::{promoted:?}")?;
540 }
541 Ok(())
542 })
543 }
544 }
545 }
546}
547
548impl<'tcx> TyCtxt<'tcx> {
552 pub fn span_as_caller_location(self, span: Span) -> ConstValue<'tcx> {
553 let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
554 let caller = self.sess.source_map().lookup_char_pos(topmost.lo());
555 self.const_caller_location(
556 Symbol::intern(
557 &caller
558 .file
559 .name
560 .for_scope(self.sess, RemapPathScopeComponents::MACRO)
561 .to_string_lossy(),
562 ),
563 caller.line as u32,
564 caller.col_display as u32 + 1,
565 )
566 }
567}