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