]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_middle/src/ty/consts/valtree.rs
Rollup merge of #104895 - chenyukang:yukang/fix-104884-serde, r=TaKO8Ki
[rust.git] / compiler / rustc_middle / src / ty / consts / valtree.rs
1 use super::ScalarInt;
2 use crate::mir::interpret::{AllocId, Scalar};
3 use crate::ty::{self, Ty, TyCtxt};
4 use rustc_macros::{HashStable, TyDecodable, TyEncodable};
5
6 #[derive(Copy, Clone, Debug, Hash, TyEncodable, TyDecodable, Eq, PartialEq, Ord, PartialOrd)]
7 #[derive(HashStable)]
8 /// This datastructure is used to represent the value of constants used in the type system.
9 ///
10 /// We explicitly choose a different datastructure from the way values are processed within
11 /// CTFE, as in the type system equal values (according to their `PartialEq`) must also have
12 /// equal representation (`==` on the rustc data structure, e.g. `ValTree`) and vice versa.
13 /// Since CTFE uses `AllocId` to represent pointers, it often happens that two different
14 /// `AllocId`s point to equal values. So we may end up with different representations for
15 /// two constants whose value is `&42`. Furthermore any kind of struct that has padding will
16 /// have arbitrary values within that padding, even if the values of the struct are the same.
17 ///
18 /// `ValTree` does not have this problem with representation, as it only contains integers or
19 /// lists of (nested) `ValTree`.
20 pub enum ValTree<'tcx> {
21     /// integers, `bool`, `char` are represented as scalars.
22     /// See the `ScalarInt` documentation for how `ScalarInt` guarantees that equal values
23     /// of these types have the same representation.
24     Leaf(ScalarInt),
25
26     //SliceOrStr(ValSlice<'tcx>),
27     // dont use SliceOrStr for now
28     /// The fields of any kind of aggregate. Structs, tuples and arrays are represented by
29     /// listing their fields' values in order.
30     ///
31     /// Enums are represented by storing their discriminant as a field, followed by all
32     /// the fields of the variant.
33     ///
34     /// ZST types are represented as an empty slice.
35     Branch(&'tcx [ValTree<'tcx>]),
36 }
37
38 impl<'tcx> ValTree<'tcx> {
39     pub fn zst() -> Self {
40         Self::Branch(&[])
41     }
42
43     #[inline]
44     pub fn unwrap_leaf(self) -> ScalarInt {
45         match self {
46             Self::Leaf(s) => s,
47             _ => bug!("expected leaf, got {:?}", self),
48         }
49     }
50
51     #[inline]
52     pub fn unwrap_branch(self) -> &'tcx [Self] {
53         match self {
54             Self::Branch(branch) => branch,
55             _ => bug!("expected branch, got {:?}", self),
56         }
57     }
58
59     pub fn from_raw_bytes<'a>(tcx: TyCtxt<'tcx>, bytes: &'a [u8]) -> Self {
60         let branches = bytes.iter().map(|b| Self::Leaf(ScalarInt::from(*b)));
61         let interned = tcx.arena.alloc_from_iter(branches);
62
63         Self::Branch(interned)
64     }
65
66     pub fn from_scalar_int(i: ScalarInt) -> Self {
67         Self::Leaf(i)
68     }
69
70     pub fn try_to_scalar(self) -> Option<Scalar<AllocId>> {
71         self.try_to_scalar_int().map(Scalar::Int)
72     }
73
74     pub fn try_to_scalar_int(self) -> Option<ScalarInt> {
75         match self {
76             Self::Leaf(s) => Some(s),
77             Self::Branch(_) => None,
78         }
79     }
80
81     pub fn try_to_machine_usize(self, tcx: TyCtxt<'tcx>) -> Option<u64> {
82         self.try_to_scalar_int().map(|s| s.try_to_machine_usize(tcx).ok()).flatten()
83     }
84
85     /// Get the values inside the ValTree as a slice of bytes. This only works for
86     /// constants with types &str, &[u8], or [u8; _].
87     pub fn try_to_raw_bytes(self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<&'tcx [u8]> {
88         match ty.kind() {
89             ty::Ref(_, inner_ty, _) => match inner_ty.kind() {
90                 // `&str` can be interpreted as raw bytes
91                 ty::Str => {}
92                 // `&[u8]` can be interpreted as raw bytes
93                 ty::Slice(slice_ty) if *slice_ty == tcx.types.u8 => {}
94                 // other `&_` can't be interpreted as raw bytes
95                 _ => return None,
96             },
97             // `[u8; N]` can be interpreted as raw bytes
98             ty::Array(array_ty, _) if *array_ty == tcx.types.u8 => {}
99             // Otherwise, type cannot be interpreted as raw bytes
100             _ => return None,
101         }
102
103         Some(tcx.arena.alloc_from_iter(
104             self.unwrap_branch().into_iter().map(|v| v.unwrap_leaf().try_to_u8().unwrap()),
105         ))
106     }
107 }