rustc_middle/ty/consts/
valtree.rs

1use std::fmt;
2use std::ops::Deref;
3
4use rustc_abi::{FIRST_VARIANT, VariantIdx};
5use rustc_data_structures::intern::Interned;
6use rustc_hir::def::Namespace;
7use rustc_macros::{
8    HashStable, Lift, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable, extension,
9};
10
11use super::ScalarInt;
12use crate::mir::interpret::{ErrorHandled, Scalar};
13use crate::ty::print::{FmtPrinter, PrettyPrinter};
14use crate::ty::{self, Ty, TyCtxt, ValTreeKind};
15
16#[extension(pub trait ValTreeKindExt<'tcx>)]
17impl<'tcx> ty::ValTreeKind<TyCtxt<'tcx>> {
18    fn try_to_scalar(&self) -> Option<Scalar> {
19        self.try_to_leaf().map(Scalar::Int)
20    }
21}
22
23/// An interned valtree. Use this rather than `ValTreeKind`, whenever possible.
24///
25/// See the docs of [`ty::ValTreeKind`] or the [dev guide] for an explanation of this type.
26///
27/// [dev guide]: https://rustc-dev-guide.rust-lang.org/mir/index.html#valtrees
28#[derive(Copy, Clone, Hash, Eq, PartialEq)]
29#[derive(HashStable)]
30// FIXME(mgca): Try not interning here. We already intern `ty::Const` which `ValTreeKind`
31// recurses through
32pub struct ValTree<'tcx>(pub(crate) Interned<'tcx, ty::ValTreeKind<TyCtxt<'tcx>>>);
33
34impl<'tcx> rustc_type_ir::inherent::ValTree<TyCtxt<'tcx>> for ValTree<'tcx> {
35    fn kind(&self) -> &ty::ValTreeKind<TyCtxt<'tcx>> {
36        &self
37    }
38}
39
40impl<'tcx> ValTree<'tcx> {
41    /// Returns the zero-sized valtree: `Branch([])`.
42    pub fn zst(tcx: TyCtxt<'tcx>) -> Self {
43        tcx.consts.valtree_zst
44    }
45
46    pub fn is_zst(self) -> bool {
47        matches!(*self, ty::ValTreeKind::Branch(box []))
48    }
49
50    pub fn from_raw_bytes(tcx: TyCtxt<'tcx>, bytes: &[u8]) -> Self {
51        let branches = bytes.iter().map(|&b| {
52            ty::Const::new_value(tcx, Self::from_scalar_int(tcx, b.into()), tcx.types.u8)
53        });
54        Self::from_branches(tcx, branches)
55    }
56
57    pub fn from_branches(
58        tcx: TyCtxt<'tcx>,
59        branches: impl IntoIterator<Item = ty::Const<'tcx>>,
60    ) -> Self {
61        tcx.intern_valtree(ty::ValTreeKind::Branch(branches.into_iter().collect()))
62    }
63
64    pub fn from_scalar_int(tcx: TyCtxt<'tcx>, i: ScalarInt) -> Self {
65        tcx.intern_valtree(ty::ValTreeKind::Leaf(i))
66    }
67}
68
69impl<'tcx> Deref for ValTree<'tcx> {
70    type Target = &'tcx ty::ValTreeKind<TyCtxt<'tcx>>;
71
72    #[inline]
73    fn deref(&self) -> &&'tcx ty::ValTreeKind<TyCtxt<'tcx>> {
74        &self.0.0
75    }
76}
77
78impl fmt::Debug for ValTree<'_> {
79    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80        (**self).fmt(f)
81    }
82}
83
84/// `Ok(Err(ty))` indicates the constant was fine, but the valtree couldn't be constructed
85/// because the value contains something of type `ty` that is not valtree-compatible.
86/// The caller can then show an appropriate error; the query does not have the
87/// necessary context to give good user-facing errors for this case.
88pub type ConstToValTreeResult<'tcx> = Result<Result<ValTree<'tcx>, Ty<'tcx>>, ErrorHandled>;
89
90/// A type-level constant value.
91///
92/// Represents a typed, fully evaluated constant.
93/// Note that this is also used by pattern elaboration to represent values which cannot occur in types,
94/// such as raw pointers and floats.
95#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
96#[derive(HashStable, TyEncodable, TyDecodable, TypeFoldable, TypeVisitable, Lift)]
97pub struct Value<'tcx> {
98    pub ty: Ty<'tcx>,
99    pub valtree: ValTree<'tcx>,
100}
101
102impl<'tcx> Value<'tcx> {
103    /// Attempts to extract the raw bits from the constant.
104    ///
105    /// Fails if the value can't be represented as bits (e.g. because it is a reference
106    /// or an aggregate).
107    #[inline]
108    pub fn try_to_bits(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> Option<u128> {
109        let (ty::Bool | ty::Char | ty::Uint(_) | ty::Int(_) | ty::Float(_)) = self.ty.kind() else {
110            return None;
111        };
112        let scalar = self.try_to_leaf()?;
113        let input = typing_env.with_post_analysis_normalized(tcx).as_query_input(self.ty);
114        let size = tcx.layout_of(input).ok()?.size;
115        Some(scalar.to_bits(size))
116    }
117
118    pub fn try_to_bool(self) -> Option<bool> {
119        if !self.ty.is_bool() {
120            return None;
121        }
122        self.try_to_leaf()?.try_to_bool().ok()
123    }
124
125    pub fn try_to_target_usize(self, tcx: TyCtxt<'tcx>) -> Option<u64> {
126        if !self.ty.is_usize() {
127            return None;
128        }
129        self.try_to_leaf().map(|s| s.to_target_usize(tcx))
130    }
131
132    /// Get the values inside the ValTree as a slice of bytes. This only works for
133    /// constants with types &str, &[u8], or [u8; _].
134    pub fn try_to_raw_bytes(self, tcx: TyCtxt<'tcx>) -> Option<&'tcx [u8]> {
135        match self.ty.kind() {
136            ty::Ref(_, inner_ty, _) => match inner_ty.kind() {
137                // `&str` can be interpreted as raw bytes
138                ty::Str => {}
139                // `&[u8]` can be interpreted as raw bytes
140                ty::Slice(slice_ty) if *slice_ty == tcx.types.u8 => {}
141                // other `&_` can't be interpreted as raw bytes
142                _ => return None,
143            },
144            // `[u8; N]` can be interpreted as raw bytes
145            ty::Array(array_ty, _) if *array_ty == tcx.types.u8 => {}
146            // Otherwise, type cannot be interpreted as raw bytes
147            _ => return None,
148        }
149
150        Some(tcx.arena.alloc_from_iter(self.to_branch().into_iter().map(|ct| ct.to_leaf().to_u8())))
151    }
152
153    /// Converts to a `ValTreeKind::Leaf` value, `panic`'ing
154    /// if this constant is some other kind.
155    #[inline]
156    pub fn to_leaf(self) -> ScalarInt {
157        match &**self.valtree {
158            ValTreeKind::Leaf(s) => *s,
159            ValTreeKind::Branch(..) => bug!("expected leaf, got {:?}", self),
160        }
161    }
162
163    /// Converts to a `ValTreeKind::Branch` value, `panic`'ing
164    /// if this constant is some other kind.
165    #[inline]
166    pub fn to_branch(self) -> &'tcx [ty::Const<'tcx>] {
167        match &**self.valtree {
168            ValTreeKind::Branch(branch) => &**branch,
169            ValTreeKind::Leaf(..) => bug!("expected branch, got {:?}", self),
170        }
171    }
172
173    /// Attempts to convert to a `ValTreeKind::Leaf` value.
174    pub fn try_to_leaf(self) -> Option<ScalarInt> {
175        match &**self.valtree {
176            ValTreeKind::Leaf(s) => Some(*s),
177            ValTreeKind::Branch(_) => None,
178        }
179    }
180
181    /// Attempts to convert to a `ValTreeKind::Leaf` value.
182    pub fn try_to_scalar(&self) -> Option<Scalar> {
183        self.try_to_leaf().map(Scalar::Int)
184    }
185
186    /// Attempts to convert to a `ValTreeKind::Branch` value.
187    pub fn try_to_branch(self) -> Option<&'tcx [ty::Const<'tcx>]> {
188        match &**self.valtree {
189            ValTreeKind::Branch(branch) => Some(&**branch),
190            ValTreeKind::Leaf(_) => None,
191        }
192    }
193
194    /// Destructures array, ADT or tuple constants into the constants
195    /// of their fields.
196    pub fn destructure_adt_const(&self) -> ty::DestructuredAdtConst<'tcx> {
197        let fields = self.to_branch();
198
199        let (variant, fields) = match self.ty.kind() {
200            ty::Adt(def, _) if def.variants().is_empty() => {
201                bug!("unreachable")
202            }
203            ty::Adt(def, _) if def.is_enum() => {
204                let (head, rest) = fields.split_first().unwrap();
205                (VariantIdx::from_u32(head.to_leaf().to_u32()), rest)
206            }
207            ty::Adt(_, _) => (FIRST_VARIANT, fields),
208            _ => bug!("destructure_adt_const called on non-ADT type: {:?}", self.ty),
209        };
210
211        ty::DestructuredAdtConst { variant, fields }
212    }
213}
214
215impl<'tcx> rustc_type_ir::inherent::ValueConst<TyCtxt<'tcx>> for Value<'tcx> {
216    fn ty(self) -> Ty<'tcx> {
217        self.ty
218    }
219
220    fn valtree(self) -> ValTree<'tcx> {
221        self.valtree
222    }
223}
224
225impl<'tcx> fmt::Display for Value<'tcx> {
226    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
227        ty::tls::with(move |tcx| {
228            let cv = tcx.lift(*self).unwrap();
229            let mut p = FmtPrinter::new(tcx, Namespace::ValueNS);
230            p.pretty_print_const_valtree(cv, /*print_ty*/ true)?;
231            f.write_str(&p.into_buffer())
232        })
233    }
234}