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