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::{AllocId, AllocRange, ErrorHandled, GlobalAlloc, 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///////////////////////////////////////////////////////////////////////////
18/// Evaluated Constants
19
20/// Represents the result of const evaluation via the `eval_to_allocation` query.
21/// Not to be confused with `ConstAllocation`, which directly refers to the underlying data!
22/// Here we indirect via an `AllocId`.
23#[derive(Copy, Clone, HashStable, TyEncodable, TyDecodable, Debug, Hash, Eq, PartialEq)]
24pub struct ConstAlloc<'tcx> {
25    /// The value lives here, at offset 0, and that allocation definitely is an `AllocKind::Memory`
26    /// (so you can use `AllocMap::unwrap_memory`).
27    pub alloc_id: AllocId,
28    pub ty: Ty<'tcx>,
29}
30
31/// Represents a constant value in Rust. `Scalar` and `Slice` are optimizations for
32/// array length computations, enum discriminants and the pattern matching logic.
33#[derive(Copy, Clone, Debug, Eq, PartialEq, TyEncodable, TyDecodable, Hash)]
34#[derive(HashStable)]
35pub enum ConstValue {
36    /// Used for types with `layout::abi::Scalar` ABI.
37    ///
38    /// Not using the enum `Value` to encode that this must not be `Uninit`.
39    Scalar(Scalar),
40
41    /// Only for ZSTs.
42    ZeroSized,
43
44    /// Used for references to unsized types with slice tail.
45    ///
46    /// This is worth an optimized representation since Rust has literals of type `&str` and
47    /// `&[u8]`. Not having to indirect those through an `AllocId` (or two, if we used `Indirect`)
48    /// has shown measurable performance improvements on stress tests. We then reuse this
49    /// optimization for slice-tail types more generally during valtree-to-constval conversion.
50    Slice {
51        /// The allocation storing the slice contents.
52        /// This always points to the beginning of the allocation.
53        alloc_id: AllocId,
54        /// The metadata field of the reference.
55        /// This is a "target usize", so we use `u64` as in the interpreter.
56        meta: u64,
57    },
58
59    /// A value not representable by the other variants; needs to be stored in-memory.
60    ///
61    /// Must *not* be used for scalars or ZST, but having `&str` or other slices in this variant is fine.
62    Indirect {
63        /// The backing memory of the value. May contain more memory than needed for just the value
64        /// if this points into some other larger ConstValue.
65        ///
66        /// We use an `AllocId` here instead of a `ConstAllocation<'tcx>` to make sure that when a
67        /// raw constant (which is basically just an `AllocId`) is turned into a `ConstValue` and
68        /// back, we can preserve the original `AllocId`.
69        alloc_id: AllocId,
70        /// Offset into `alloc`
71        offset: Size,
72    },
73}
74
75#[cfg(target_pointer_width = "64")]
76rustc_data_structures::static_assert_size!(ConstValue, 24);
77
78impl ConstValue {
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<'_>) -> Option<u64> {
100        Some(self.try_to_scalar_int()?.to_target_usize(tcx))
101    }
102
103    pub fn try_to_bits_for_ty<'tcx>(
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    /// Must only be called on constants of type `&str` or `&[u8]`!
133    pub fn try_get_slice_bytes_for_diagnostics<'tcx>(
134        &self,
135        tcx: TyCtxt<'tcx>,
136    ) -> Option<&'tcx [u8]> {
137        let (alloc_id, start, len) = match self {
138            ConstValue::Scalar(_) | ConstValue::ZeroSized => {
139                bug!("`try_get_slice_bytes` on non-slice constant")
140            }
141            &ConstValue::Slice { alloc_id, meta } => (alloc_id, 0, meta),
142            &ConstValue::Indirect { alloc_id, offset } => {
143                // The reference itself is stored behind an indirection.
144                // Load the reference, and then load the actual slice contents.
145                let a = tcx.global_alloc(alloc_id).unwrap_memory().inner();
146                let ptr_size = tcx.data_layout.pointer_size();
147                if a.size() < offset + 2 * ptr_size {
148                    // (partially) dangling reference
149                    return None;
150                }
151                // Read the wide pointer components.
152                let ptr = a
153                    .read_scalar(
154                        &tcx,
155                        alloc_range(offset, ptr_size),
156                        /* read_provenance */ true,
157                    )
158                    .ok()?;
159                let ptr = ptr.to_pointer(&tcx).discard_err()?;
160                let len = a
161                    .read_scalar(
162                        &tcx,
163                        alloc_range(offset + ptr_size, ptr_size),
164                        /* read_provenance */ false,
165                    )
166                    .ok()?;
167                let len = len.to_target_usize(&tcx).discard_err()?;
168                if len == 0 {
169                    return Some(&[]);
170                }
171                // Non-empty slice, must have memory. We know this is a relative pointer.
172                let (inner_prov, offset) =
173                    ptr.into_pointer_or_addr().ok()?.prov_and_relative_offset();
174                (inner_prov.alloc_id(), offset.bytes(), len)
175            }
176        };
177
178        let data = tcx.global_alloc(alloc_id).unwrap_memory();
179
180        // This is for diagnostics only, so we are okay to use `inspect_with_uninit_and_ptr_outside_interpreter`.
181        let start = start.try_into().unwrap();
182        let end = start + usize::try_from(len).unwrap();
183        Some(data.inner().inspect_with_uninit_and_ptr_outside_interpreter(start..end))
184    }
185
186    /// Check if a constant may contain provenance information. This is used by MIR opts.
187    /// Can return `true` even if there is no provenance.
188    pub fn may_have_provenance(&self, tcx: TyCtxt<'_>, size: Size) -> bool {
189        match *self {
190            ConstValue::ZeroSized | ConstValue::Scalar(Scalar::Int(_)) => return false,
191            ConstValue::Scalar(Scalar::Ptr(..)) => return true,
192            // It's hard to find out the part of the allocation we point to;
193            // just conservatively check everything.
194            ConstValue::Slice { alloc_id, meta: _ } => {
195                !tcx.global_alloc(alloc_id).unwrap_memory().inner().provenance().ptrs().is_empty()
196            }
197            ConstValue::Indirect { alloc_id, offset } => !tcx
198                .global_alloc(alloc_id)
199                .unwrap_memory()
200                .inner()
201                .provenance()
202                .range_empty(AllocRange::from(offset..offset + size), &tcx),
203        }
204    }
205
206    /// Check if a constant only contains uninitialized bytes.
207    pub fn all_bytes_uninit(&self, tcx: TyCtxt<'_>) -> bool {
208        let ConstValue::Indirect { alloc_id, .. } = self else {
209            return false;
210        };
211        let alloc = tcx.global_alloc(*alloc_id);
212        let GlobalAlloc::Memory(alloc) = alloc else {
213            return false;
214        };
215        let init_mask = alloc.0.init_mask();
216        let init_range = init_mask.is_range_initialized(AllocRange {
217            start: Size::ZERO,
218            size: Size::from_bytes(alloc.0.len()),
219        });
220        if let Err(range) = init_range {
221            if range.size == alloc.0.size() {
222                return true;
223            }
224        }
225        false
226    }
227}
228
229///////////////////////////////////////////////////////////////////////////
230/// Constants
231
232#[derive(Clone, Copy, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable, Debug)]
233#[derive(TypeFoldable, TypeVisitable, Lift)]
234pub enum Const<'tcx> {
235    /// This constant came from the type system.
236    ///
237    /// Any way of turning `ty::Const` into `ConstValue` should go through `valtree_to_const_val`;
238    /// this ensures that we consistently produce "clean" values without data in the padding or
239    /// anything like that.
240    ///
241    /// FIXME(BoxyUwU): We should remove this `Ty` and look up the type for params via `ParamEnv`
242    Ty(Ty<'tcx>, ty::Const<'tcx>),
243
244    /// An unevaluated mir constant which is not part of the type system.
245    ///
246    /// Note that `Ty(ty::ConstKind::Unevaluated)` and this variant are *not* identical! `Ty` will
247    /// always flow through a valtree, so all data not captured in the valtree is lost. This variant
248    /// directly uses the evaluated result of the given constant, including e.g. data stored in
249    /// padding.
250    Unevaluated(UnevaluatedConst<'tcx>, Ty<'tcx>),
251
252    /// This constant cannot go back into the type system, as it represents
253    /// something the type system cannot handle (e.g. pointers).
254    Val(ConstValue, Ty<'tcx>),
255}
256
257impl<'tcx> Const<'tcx> {
258    /// Creates an unevaluated const from a `DefId` for a const item.
259    /// The binders of the const item still need to be instantiated.
260    pub fn from_unevaluated(
261        tcx: TyCtxt<'tcx>,
262        def_id: DefId,
263    ) -> ty::EarlyBinder<'tcx, Const<'tcx>> {
264        ty::EarlyBinder::bind(Const::Unevaluated(
265            UnevaluatedConst {
266                def: def_id,
267                args: ty::GenericArgs::identity_for_item(tcx, def_id),
268                promoted: None,
269            },
270            tcx.type_of(def_id).skip_binder(),
271        ))
272    }
273
274    #[inline(always)]
275    pub fn ty(&self) -> Ty<'tcx> {
276        match self {
277            Const::Ty(ty, ct) => {
278                match ct.kind() {
279                    // Dont use the outer ty as on invalid code we can wind up with them not being the same.
280                    // this then results in allowing const eval to add `1_i64 + 1_usize` in cases where the mir
281                    // was originally `({N: usize} + 1_usize)` under `generic_const_exprs`.
282                    ty::ConstKind::Value(cv) => cv.ty,
283                    _ => *ty,
284                }
285            }
286            Const::Val(_, ty) | Const::Unevaluated(_, ty) => *ty,
287        }
288    }
289
290    /// Determines whether we need to add this const to `required_consts`. This is the case if and
291    /// only if evaluating it may error.
292    #[inline]
293    pub fn is_required_const(&self) -> bool {
294        match self {
295            Const::Ty(_, c) => match c.kind() {
296                ty::ConstKind::Value(_) => false, // already a value, cannot error
297                _ => true,
298            },
299            Const::Val(..) => false, // already a value, cannot error
300            Const::Unevaluated(..) => true,
301        }
302    }
303
304    #[inline]
305    pub fn try_to_scalar(self) -> Option<Scalar> {
306        match self {
307            Const::Ty(_, c) => match c.kind() {
308                ty::ConstKind::Value(cv) if cv.ty.is_primitive() => {
309                    // A valtree of a type where leaves directly represent the scalar const value.
310                    // Just checking whether it is a leaf is insufficient as e.g. references are leafs
311                    // but the leaf value is the value they point to, not the reference itself!
312                    Some(cv.valtree.unwrap_leaf().into())
313                }
314                _ => None,
315            },
316            Const::Val(val, _) => val.try_to_scalar(),
317            Const::Unevaluated(..) => None,
318        }
319    }
320
321    #[inline]
322    pub fn try_to_scalar_int(self) -> Option<ScalarInt> {
323        // This is equivalent to `self.try_to_scalar()?.try_to_int().ok()`, but measurably faster.
324        match self {
325            Const::Val(ConstValue::Scalar(Scalar::Int(x)), _) => Some(x),
326            Const::Ty(_, c) => match c.kind() {
327                ty::ConstKind::Value(cv) if cv.ty.is_primitive() => Some(cv.valtree.unwrap_leaf()),
328                _ => None,
329            },
330            _ => None,
331        }
332    }
333
334    #[inline]
335    pub fn try_to_bits(self, size: Size) -> Option<u128> {
336        Some(self.try_to_scalar_int()?.to_bits(size))
337    }
338
339    #[inline]
340    pub fn try_to_bool(self) -> Option<bool> {
341        self.try_to_scalar_int()?.try_into().ok()
342    }
343
344    #[inline]
345    pub fn eval(
346        self,
347        tcx: TyCtxt<'tcx>,
348        typing_env: ty::TypingEnv<'tcx>,
349        span: Span,
350    ) -> Result<ConstValue, ErrorHandled> {
351        match self {
352            Const::Ty(_, c) => {
353                if c.has_non_region_param() {
354                    return Err(ErrorHandled::TooGeneric(span));
355                }
356
357                match c.kind() {
358                    ConstKind::Value(cv) => Ok(tcx.valtree_to_const_val(cv)),
359                    ConstKind::Expr(_) => {
360                        bug!("Normalization of `ty::ConstKind::Expr` is unimplemented")
361                    }
362                    _ => Err(ReportedErrorInfo::non_const_eval_error(
363                        tcx.dcx().delayed_bug("Unevaluated `ty::Const` in MIR body"),
364                    )
365                    .into()),
366                }
367            }
368            Const::Unevaluated(uneval, _) => {
369                // FIXME: We might want to have a `try_eval`-like function on `Unevaluated`
370                tcx.const_eval_resolve(typing_env, uneval, span)
371            }
372            Const::Val(val, _) => Ok(val),
373        }
374    }
375
376    #[inline]
377    pub fn try_eval_scalar(
378        self,
379        tcx: TyCtxt<'tcx>,
380        typing_env: ty::TypingEnv<'tcx>,
381    ) -> Option<Scalar> {
382        if let Const::Ty(_, c) = self
383            && let ty::ConstKind::Value(cv) = c.kind()
384            && cv.ty.is_primitive()
385        {
386            // Avoid the `valtree_to_const_val` query. Can only be done on primitive types that
387            // are valtree leaves, and *not* on references. (References should return the
388            // pointer here, which valtrees don't represent.)
389            Some(cv.valtree.unwrap_leaf().into())
390        } else {
391            self.eval(tcx, typing_env, DUMMY_SP).ok()?.try_to_scalar()
392        }
393    }
394
395    #[inline]
396    pub fn try_eval_scalar_int(
397        self,
398        tcx: TyCtxt<'tcx>,
399        typing_env: ty::TypingEnv<'tcx>,
400    ) -> Option<ScalarInt> {
401        self.try_eval_scalar(tcx, typing_env)?.try_to_scalar_int().ok()
402    }
403
404    #[inline]
405    pub fn try_eval_bits(
406        &self,
407        tcx: TyCtxt<'tcx>,
408        typing_env: ty::TypingEnv<'tcx>,
409    ) -> Option<u128> {
410        let int = self.try_eval_scalar_int(tcx, typing_env)?;
411        let size = tcx
412            .layout_of(typing_env.with_post_analysis_normalized(tcx).as_query_input(self.ty()))
413            .ok()?
414            .size;
415        Some(int.to_bits(size))
416    }
417
418    /// Panics if the value cannot be evaluated or doesn't contain a valid integer of the given type.
419    #[inline]
420    pub fn eval_bits(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> u128 {
421        self.try_eval_bits(tcx, typing_env)
422            .unwrap_or_else(|| bug!("expected bits of {:#?}, got {:#?}", self.ty(), self))
423    }
424
425    #[inline]
426    pub fn try_eval_target_usize(
427        self,
428        tcx: TyCtxt<'tcx>,
429        typing_env: ty::TypingEnv<'tcx>,
430    ) -> Option<u64> {
431        Some(self.try_eval_scalar_int(tcx, typing_env)?.to_target_usize(tcx))
432    }
433
434    #[inline]
435    /// Panics if the value cannot be evaluated or doesn't contain a valid `usize`.
436    pub fn eval_target_usize(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> u64 {
437        self.try_eval_target_usize(tcx, typing_env)
438            .unwrap_or_else(|| bug!("expected usize, got {:#?}", self))
439    }
440
441    #[inline]
442    pub fn try_eval_bool(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> Option<bool> {
443        self.try_eval_scalar_int(tcx, typing_env)?.try_into().ok()
444    }
445
446    #[inline]
447    pub fn from_value(val: ConstValue, ty: Ty<'tcx>) -> Self {
448        Self::Val(val, ty)
449    }
450
451    #[inline]
452    pub fn from_ty_value(tcx: TyCtxt<'tcx>, val: ty::Value<'tcx>) -> Self {
453        Self::Ty(val.ty, ty::Const::new_value(tcx, val.valtree, val.ty))
454    }
455
456    pub fn from_bits(
457        tcx: TyCtxt<'tcx>,
458        bits: u128,
459        typing_env: ty::TypingEnv<'tcx>,
460        ty: Ty<'tcx>,
461    ) -> Self {
462        let size = tcx
463            .layout_of(typing_env.as_query_input(ty))
464            .unwrap_or_else(|e| bug!("could not compute layout for {ty:?}: {e:?}"))
465            .size;
466        let cv = ConstValue::Scalar(Scalar::from_uint(bits, size));
467
468        Self::Val(cv, ty)
469    }
470
471    #[inline]
472    pub fn from_bool(tcx: TyCtxt<'tcx>, v: bool) -> Self {
473        let cv = ConstValue::from_bool(v);
474        Self::Val(cv, tcx.types.bool)
475    }
476
477    #[inline]
478    pub fn zero_sized(ty: Ty<'tcx>) -> Self {
479        let cv = ConstValue::ZeroSized;
480        Self::Val(cv, ty)
481    }
482
483    pub fn from_usize(tcx: TyCtxt<'tcx>, n: u64) -> Self {
484        let ty = tcx.types.usize;
485        let typing_env = ty::TypingEnv::fully_monomorphized();
486        Self::from_bits(tcx, n as u128, typing_env, ty)
487    }
488
489    #[inline]
490    pub fn from_scalar(_tcx: TyCtxt<'tcx>, s: Scalar, ty: Ty<'tcx>) -> Self {
491        let val = ConstValue::Scalar(s);
492        Self::Val(val, ty)
493    }
494
495    /// Return true if any evaluation of this constant always returns the same value,
496    /// taking into account even pointer identity tests.
497    pub fn is_deterministic(&self) -> bool {
498        // Some constants may generate fresh allocations for pointers they contain,
499        // so using the same constant twice can yield two different results.
500        // Notably, valtrees purposefully generate new allocations.
501        match self {
502            Const::Ty(_, c) => match c.kind() {
503                ty::ConstKind::Param(..) => true,
504                // A valtree may be a reference. Valtree references correspond to a
505                // different allocation each time they are evaluated. Valtrees for primitive
506                // types are fine though.
507                ty::ConstKind::Value(cv) => cv.ty.is_primitive(),
508                ty::ConstKind::Unevaluated(..) | ty::ConstKind::Expr(..) => false,
509                // This can happen if evaluation of a constant failed. The result does not matter
510                // much since compilation is doomed.
511                ty::ConstKind::Error(..) => false,
512                // Should not appear in runtime MIR.
513                ty::ConstKind::Infer(..)
514                | ty::ConstKind::Bound(..)
515                | ty::ConstKind::Placeholder(..) => bug!(),
516            },
517            Const::Unevaluated(..) => false,
518            Const::Val(
519                ConstValue::Slice { .. }
520                | ConstValue::ZeroSized
521                | ConstValue::Scalar(_)
522                | ConstValue::Indirect { .. },
523                _,
524            ) => true,
525        }
526    }
527}
528
529/// An unevaluated (potentially generic) constant used in MIR.
530#[derive(Copy, Clone, Debug, Eq, PartialEq, TyEncodable, TyDecodable)]
531#[derive(Hash, HashStable, TypeFoldable, TypeVisitable, Lift)]
532pub struct UnevaluatedConst<'tcx> {
533    pub def: DefId,
534    pub args: GenericArgsRef<'tcx>,
535    pub promoted: Option<Promoted>,
536}
537
538impl<'tcx> UnevaluatedConst<'tcx> {
539    #[inline]
540    pub fn shrink(self) -> ty::UnevaluatedConst<'tcx> {
541        assert_eq!(self.promoted, None);
542        ty::UnevaluatedConst { def: self.def, args: self.args }
543    }
544}
545
546impl<'tcx> UnevaluatedConst<'tcx> {
547    #[inline]
548    pub fn new(def: DefId, args: GenericArgsRef<'tcx>) -> UnevaluatedConst<'tcx> {
549        UnevaluatedConst { def, args, promoted: Default::default() }
550    }
551
552    #[inline]
553    pub fn from_instance(instance: ty::Instance<'tcx>) -> Self {
554        UnevaluatedConst::new(instance.def_id(), instance.args)
555    }
556}
557
558impl<'tcx> Display for Const<'tcx> {
559    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
560        match *self {
561            Const::Ty(_, c) => pretty_print_const(c, fmt, true),
562            Const::Val(val, ty) => pretty_print_const_value(val, ty, fmt),
563            // FIXME(valtrees): Correctly print mir constants.
564            Const::Unevaluated(c, _ty) => {
565                ty::tls::with(move |tcx| {
566                    let c = tcx.lift(c).unwrap();
567                    // Matches `GlobalId` printing.
568                    let instance =
569                        with_no_trimmed_paths!(tcx.def_path_str_with_args(c.def, c.args));
570                    write!(fmt, "{instance}")?;
571                    if let Some(promoted) = c.promoted {
572                        write!(fmt, "::{promoted:?}")?;
573                    }
574                    Ok(())
575                })
576            }
577        }
578    }
579}
580
581///////////////////////////////////////////////////////////////////////////
582/// Const-related utilities
583
584impl<'tcx> TyCtxt<'tcx> {
585    pub fn span_as_caller_location(self, span: Span) -> ConstValue {
586        let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
587        let caller = self.sess.source_map().lookup_char_pos(topmost.lo());
588        self.const_caller_location(
589            Symbol::intern(
590                &caller
591                    .file
592                    .name
593                    .for_scope(self.sess, RemapPathScopeComponents::MACRO)
594                    .to_string_lossy(),
595            ),
596            caller.line as u32,
597            caller.col_display as u32 + 1,
598        )
599    }
600}