rustc_middle/ty/consts/
valtree.rs

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