rustc_middle/ty/consts/
valtree.rs

1use std::fmt;
2use std::ops::Deref;
3
4use rustc_data_structures::intern::Interned;
5use rustc_hir::def::Namespace;
6use rustc_macros::{HashStable, Lift, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable};
7
8use super::ScalarInt;
9use crate::mir::interpret::{ErrorHandled, Scalar};
10use crate::ty::print::{FmtPrinter, PrettyPrinter};
11use crate::ty::{self, Ty, TyCtxt};
12
13/// This datastructure is used to represent the value of constants used in the type system.
14///
15/// We explicitly choose a different datastructure from the way values are processed within
16/// CTFE, as in the type system equal values (according to their `PartialEq`) must also have
17/// equal representation (`==` on the rustc data structure, e.g. `ValTree`) and vice versa.
18/// Since CTFE uses `AllocId` to represent pointers, it often happens that two different
19/// `AllocId`s point to equal values. So we may end up with different representations for
20/// two constants whose value is `&42`. Furthermore any kind of struct that has padding will
21/// have arbitrary values within that padding, even if the values of the struct are the same.
22///
23/// `ValTree` does not have this problem with representation, as it only contains integers or
24/// lists of (nested) `ValTree`.
25#[derive(Clone, Debug, Hash, Eq, PartialEq)]
26#[derive(HashStable, TyEncodable, TyDecodable)]
27pub enum ValTreeKind<'tcx> {
28    /// integers, `bool`, `char` are represented as scalars.
29    /// See the `ScalarInt` documentation for how `ScalarInt` guarantees that equal values
30    /// of these types have the same representation.
31    Leaf(ScalarInt),
32
33    //SliceOrStr(ValSlice<'tcx>),
34    // don't use SliceOrStr for now
35    /// The fields of any kind of aggregate. Structs, tuples and arrays are represented by
36    /// listing their fields' values in order.
37    ///
38    /// Enums are represented by storing their variant index as a u32 field, followed by all
39    /// the fields of the variant.
40    ///
41    /// ZST types are represented as an empty slice.
42    Branch(Box<[ValTree<'tcx>]>),
43}
44
45impl<'tcx> ValTreeKind<'tcx> {
46    #[inline]
47    pub fn unwrap_leaf(&self) -> ScalarInt {
48        match self {
49            Self::Leaf(s) => *s,
50            _ => bug!("expected leaf, got {:?}", self),
51        }
52    }
53
54    #[inline]
55    pub fn unwrap_branch(&self) -> &[ValTree<'tcx>] {
56        match self {
57            Self::Branch(branch) => &**branch,
58            _ => bug!("expected branch, got {:?}", self),
59        }
60    }
61
62    pub fn try_to_scalar(&self) -> Option<Scalar> {
63        self.try_to_scalar_int().map(Scalar::Int)
64    }
65
66    pub fn try_to_scalar_int(&self) -> Option<ScalarInt> {
67        match self {
68            Self::Leaf(s) => Some(*s),
69            Self::Branch(_) => None,
70        }
71    }
72
73    pub fn try_to_branch(&self) -> Option<&[ValTree<'tcx>]> {
74        match self {
75            Self::Branch(branch) => Some(&**branch),
76            Self::Leaf(_) => None,
77        }
78    }
79}
80
81/// An interned valtree. Use this rather than `ValTreeKind`, whenever possible.
82///
83/// See the docs of [`ValTreeKind`] or the [dev guide] for an explanation of this type.
84///
85/// [dev guide]: https://rustc-dev-guide.rust-lang.org/mir/index.html#valtrees
86#[derive(Copy, Clone, Hash, Eq, PartialEq)]
87#[derive(HashStable)]
88pub struct ValTree<'tcx>(pub(crate) Interned<'tcx, ValTreeKind<'tcx>>);
89
90impl<'tcx> ValTree<'tcx> {
91    /// Returns the zero-sized valtree: `Branch([])`.
92    pub fn zst(tcx: TyCtxt<'tcx>) -> Self {
93        tcx.consts.valtree_zst
94    }
95
96    pub fn is_zst(self) -> bool {
97        matches!(*self, ValTreeKind::Branch(box []))
98    }
99
100    pub fn from_raw_bytes(tcx: TyCtxt<'tcx>, bytes: &[u8]) -> Self {
101        let branches = bytes.iter().map(|&b| Self::from_scalar_int(tcx, b.into()));
102        Self::from_branches(tcx, branches)
103    }
104
105    pub fn from_branches(tcx: TyCtxt<'tcx>, branches: impl IntoIterator<Item = Self>) -> Self {
106        tcx.intern_valtree(ValTreeKind::Branch(branches.into_iter().collect()))
107    }
108
109    pub fn from_scalar_int(tcx: TyCtxt<'tcx>, i: ScalarInt) -> Self {
110        tcx.intern_valtree(ValTreeKind::Leaf(i))
111    }
112}
113
114impl<'tcx> Deref for ValTree<'tcx> {
115    type Target = &'tcx ValTreeKind<'tcx>;
116
117    #[inline]
118    fn deref(&self) -> &&'tcx ValTreeKind<'tcx> {
119        &self.0.0
120    }
121}
122
123impl fmt::Debug for ValTree<'_> {
124    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
125        (**self).fmt(f)
126    }
127}
128
129/// `Ok(Err(ty))` indicates the constant was fine, but the valtree couldn't be constructed
130/// because the value contains something of type `ty` that is not valtree-compatible.
131/// The caller can then show an appropriate error; the query does not have the
132/// necessary context to give good user-facing errors for this case.
133pub type ConstToValTreeResult<'tcx> = Result<Result<ValTree<'tcx>, Ty<'tcx>>, ErrorHandled>;
134
135/// A type-level constant value.
136///
137/// Represents a typed, fully evaluated constant.
138/// Note that this is also used by pattern elaboration to represent values which cannot occur in types,
139/// such as raw pointers and floats.
140#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
141#[derive(HashStable, TyEncodable, TyDecodable, TypeFoldable, TypeVisitable, Lift)]
142pub struct Value<'tcx> {
143    pub ty: Ty<'tcx>,
144    pub valtree: ValTree<'tcx>,
145}
146
147impl<'tcx> Value<'tcx> {
148    /// Attempts to extract the raw bits from the constant.
149    ///
150    /// Fails if the value can't be represented as bits (e.g. because it is a reference
151    /// or an aggregate).
152    #[inline]
153    pub fn try_to_bits(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> Option<u128> {
154        let (ty::Bool | ty::Char | ty::Uint(_) | ty::Int(_) | ty::Float(_)) = self.ty.kind() else {
155            return None;
156        };
157        let scalar = self.valtree.try_to_scalar_int()?;
158        let input = typing_env.with_post_analysis_normalized(tcx).as_query_input(self.ty);
159        let size = tcx.layout_of(input).ok()?.size;
160        Some(scalar.to_bits(size))
161    }
162
163    pub fn try_to_bool(self) -> Option<bool> {
164        if !self.ty.is_bool() {
165            return None;
166        }
167        self.valtree.try_to_scalar_int()?.try_to_bool().ok()
168    }
169
170    pub fn try_to_target_usize(self, tcx: TyCtxt<'tcx>) -> Option<u64> {
171        if !self.ty.is_usize() {
172            return None;
173        }
174        self.valtree.try_to_scalar_int().map(|s| s.to_target_usize(tcx))
175    }
176
177    /// Get the values inside the ValTree as a slice of bytes. This only works for
178    /// constants with types &str, &[u8], or [u8; _].
179    pub fn try_to_raw_bytes(self, tcx: TyCtxt<'tcx>) -> Option<&'tcx [u8]> {
180        match self.ty.kind() {
181            ty::Ref(_, inner_ty, _) => match inner_ty.kind() {
182                // `&str` can be interpreted as raw bytes
183                ty::Str => {}
184                // `&[u8]` can be interpreted as raw bytes
185                ty::Slice(slice_ty) if *slice_ty == tcx.types.u8 => {}
186                // other `&_` can't be interpreted as raw bytes
187                _ => return None,
188            },
189            // `[u8; N]` can be interpreted as raw bytes
190            ty::Array(array_ty, _) if *array_ty == tcx.types.u8 => {}
191            // Otherwise, type cannot be interpreted as raw bytes
192            _ => return None,
193        }
194
195        Some(tcx.arena.alloc_from_iter(
196            self.valtree.unwrap_branch().into_iter().map(|v| v.unwrap_leaf().to_u8()),
197        ))
198    }
199}
200
201impl<'tcx> rustc_type_ir::inherent::ValueConst<TyCtxt<'tcx>> for Value<'tcx> {
202    fn ty(self) -> Ty<'tcx> {
203        self.ty
204    }
205
206    fn valtree(self) -> ValTree<'tcx> {
207        self.valtree
208    }
209}
210
211impl<'tcx> fmt::Display for Value<'tcx> {
212    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
213        ty::tls::with(move |tcx| {
214            let cv = tcx.lift(*self).unwrap();
215            let mut p = FmtPrinter::new(tcx, Namespace::ValueNS);
216            p.pretty_print_const_valtree(cv, /*print_ty*/ true)?;
217            f.write_str(&p.into_buffer())
218        })
219    }
220}