rustc_middle/mir/
consts.rs

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///////////////////////////////////////////////////////////////////////////
20/// Evaluated Constants
21
22/// Represents the result of const evaluation via the `eval_to_allocation` query.
23/// Not to be confused with `ConstAllocation`, which directly refers to the underlying data!
24/// Here we indirect via an `AllocId`.
25#[derive(Copy, Clone, HashStable, TyEncodable, TyDecodable, Debug, Hash, Eq, PartialEq)]
26pub struct ConstAlloc<'tcx> {
27    /// The value lives here, at offset 0, and that allocation definitely is an `AllocKind::Memory`
28    /// (so you can use `AllocMap::unwrap_memory`).
29    pub alloc_id: AllocId,
30    pub ty: Ty<'tcx>,
31}
32
33/// Represents a constant value in Rust. `Scalar` and `Slice` are optimizations for
34/// array length computations, enum discriminants and the pattern matching logic.
35#[derive(Copy, Clone, Debug, Eq, PartialEq, TyEncodable, TyDecodable, Hash)]
36#[derive(HashStable, Lift)]
37pub enum ConstValue<'tcx> {
38    /// Used for types with `layout::abi::Scalar` ABI.
39    ///
40    /// Not using the enum `Value` to encode that this must not be `Uninit`.
41    Scalar(Scalar),
42
43    /// Only for ZSTs.
44    ZeroSized,
45
46    /// Used for references to unsized types with slice tail.
47    ///
48    /// This is worth an optimized representation since Rust has literals of type `&str` and
49    /// `&[u8]`. Not having to indirect those through an `AllocId` (or two, if we used `Indirect`)
50    /// has shown measurable performance improvements on stress tests. We then reuse this
51    /// optimization for slice-tail types more generally during valtree-to-constval conversion.
52    Slice {
53        /// The allocation storing the slice contents.
54        /// This always points to the beginning of the allocation.
55        data: ConstAllocation<'tcx>,
56        /// The metadata field of the reference.
57        /// This is a "target usize", so we use `u64` as in the interpreter.
58        meta: u64,
59    },
60
61    /// A value not representable by the other variants; needs to be stored in-memory.
62    ///
63    /// Must *not* be used for scalars or ZST, but having `&str` or other slices in this variant is fine.
64    Indirect {
65        /// The backing memory of the value. May contain more memory than needed for just the value
66        /// if this points into some other larger ConstValue.
67        ///
68        /// We use an `AllocId` here instead of a `ConstAllocation<'tcx>` to make sure that when a
69        /// raw constant (which is basically just an `AllocId`) is turned into a `ConstValue` and
70        /// back, we can preserve the original `AllocId`.
71        alloc_id: AllocId,
72        /// Offset into `alloc`
73        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    /// Must only be called on constants of type `&str` or `&[u8]`!
135    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                // The reference itself is stored behind an indirection.
143                // Load the reference, and then load the actual slice contents.
144                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                    // (partially) dangling reference
148                    return None;
149                }
150                // Read the wide pointer components.
151                let ptr = a
152                    .read_scalar(
153                        &tcx,
154                        alloc_range(offset, ptr_size),
155                        /* read_provenance */ 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                        /* read_provenance */ false,
164                    )
165                    .ok()?;
166                let len = len.to_target_usize(&tcx).discard_err()?;
167                if len == 0 {
168                    return Some(&[]);
169                }
170                // Non-empty slice, must have memory. We know this is a relative pointer.
171                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        // This is for diagnostics only, so we are okay to use `inspect_with_uninit_and_ptr_outside_interpreter`.
179        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    /// Check if a constant may contain provenance information. This is used by MIR opts.
185    /// Can return `true` even if there is no provenance.
186    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            // It's hard to find out the part of the allocation we point to;
191            // just conservatively check everything.
192            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    /// Check if a constant only contains uninitialized bytes.
203    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///////////////////////////////////////////////////////////////////////////
226/// Constants
227
228#[derive(Clone, Copy, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable, Debug)]
229#[derive(TypeFoldable, TypeVisitable, Lift)]
230pub enum Const<'tcx> {
231    /// This constant came from the type system.
232    ///
233    /// Any way of turning `ty::Const` into `ConstValue` should go through `valtree_to_const_val`;
234    /// this ensures that we consistently produce "clean" values without data in the padding or
235    /// anything like that.
236    ///
237    /// FIXME(BoxyUwU): We should remove this `Ty` and look up the type for params via `ParamEnv`
238    Ty(Ty<'tcx>, ty::Const<'tcx>),
239
240    /// An unevaluated mir constant which is not part of the type system.
241    ///
242    /// Note that `Ty(ty::ConstKind::Unevaluated)` and this variant are *not* identical! `Ty` will
243    /// always flow through a valtree, so all data not captured in the valtree is lost. This variant
244    /// directly uses the evaluated result of the given constant, including e.g. data stored in
245    /// padding.
246    Unevaluated(UnevaluatedConst<'tcx>, Ty<'tcx>),
247
248    /// This constant cannot go back into the type system, as it represents
249    /// something the type system cannot handle (e.g. pointers).
250    Val(ConstValue<'tcx>, Ty<'tcx>),
251}
252
253impl<'tcx> Const<'tcx> {
254    /// Creates an unevaluated const from a `DefId` for a const item.
255    /// The binders of the const item still need to be instantiated.
256    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                    // Dont use the outer ty as on invalid code we can wind up with them not being the same.
276                    // this then results in allowing const eval to add `1_i64 + 1_usize` in cases where the mir
277                    // was originally `({N: usize} + 1_usize)` under `generic_const_exprs`.
278                    ty::ConstKind::Value(cv) => cv.ty,
279                    _ => *ty,
280                }
281            }
282            Const::Val(_, ty) | Const::Unevaluated(_, ty) => *ty,
283        }
284    }
285
286    /// Determines whether we need to add this const to `required_consts`. This is the case if and
287    /// only if evaluating it may error.
288    #[inline]
289    pub fn is_required_const(&self) -> bool {
290        match self {
291            Const::Ty(_, c) => match c.kind() {
292                ty::ConstKind::Value(_) => false, // already a value, cannot error
293                _ => true,
294            },
295            Const::Val(..) => false, // already a value, cannot error
296            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                    // A valtree of a type where leaves directly represent the scalar const value.
306                    // Just checking whether it is a leaf is insufficient as e.g. references are leafs
307                    // but the leaf value is the value they point to, not the reference itself!
308                    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        // This is equivalent to `self.try_to_scalar()?.try_to_int().ok()`, but measurably faster.
320        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                // FIXME: We might want to have a `try_eval`-like function on `Unevaluated`
366                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            // Avoid the `valtree_to_const_val` query. Can only be done on primitive types that
383            // are valtree leaves, and *not* on references. (References should return the
384            // pointer here, which valtrees don't represent.)
385            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    /// Panics if the value cannot be evaluated or doesn't contain a valid integer of the given type.
415    #[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    /// Panics if the value cannot be evaluated or doesn't contain a valid `usize`.
432    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    /// Return true if any evaluation of this constant always returns the same value,
487    /// taking into account even pointer identity tests.
488    pub fn is_deterministic(&self) -> bool {
489        // Some constants may generate fresh allocations for pointers they contain,
490        // so using the same constant twice can yield two different results:
491        // - valtrees purposefully generate new allocations
492        // - ConstValue::Slice also generate new allocations
493        match self {
494            Const::Ty(_, c) => match c.kind() {
495                ty::ConstKind::Param(..) => true,
496                // A valtree may be a reference. Valtree references correspond to a
497                // different allocation each time they are evaluated. Valtrees for primitive
498                // types are fine though.
499                ty::ConstKind::Value(cv) => cv.ty.is_primitive(),
500                ty::ConstKind::Unevaluated(..) | ty::ConstKind::Expr(..) => false,
501                // This can happen if evaluation of a constant failed. The result does not matter
502                // much since compilation is doomed.
503                ty::ConstKind::Error(..) => false,
504                // Should not appear in runtime MIR.
505                ty::ConstKind::Infer(..)
506                | ty::ConstKind::Bound(..)
507                | ty::ConstKind::Placeholder(..) => bug!(),
508            },
509            Const::Unevaluated(..) => false,
510            // If the same slice appears twice in the MIR, we cannot guarantee that we will
511            // give the same `AllocId` to the data.
512            Const::Val(ConstValue::Slice { .. }, _) => false,
513            Const::Val(
514                ConstValue::ZeroSized | ConstValue::Scalar(_) | ConstValue::Indirect { .. },
515                _,
516            ) => true,
517        }
518    }
519}
520
521/// An unevaluated (potentially generic) constant used in MIR.
522#[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            // FIXME(valtrees): Correctly print mir constants.
556            Const::Unevaluated(c, _ty) => {
557                ty::tls::with(move |tcx| {
558                    let c = tcx.lift(c).unwrap();
559                    // Matches `GlobalId` printing.
560                    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
573///////////////////////////////////////////////////////////////////////////
574/// Const-related utilities
575
576impl<'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}