charon_lib/ast/
values_utils.rs

1//! Implementations for [crate::values]
2use crate::ast::*;
3
4#[derive(Debug, Clone)]
5pub enum ScalarError {
6    /// Attempt to use a signed scalar as an unsigned scalar or vice-versa
7    IncorrectSign,
8    /// Out of bounds scalar
9    OutOfBounds,
10    UnsupportedPtrSize,
11}
12/// Our redefinition of Result - we don't care much about the I/O part.
13pub type ScalarResult<T> = std::result::Result<T, ScalarError>;
14
15macro_rules! from_le_bytes {
16    ($m:ident, $b:ident, [$(($i_ty: ty, $i:ident, $s:ident, $n_ty:ty, $t:ty)),*]) => {
17        match $m {
18            $(
19                IntegerTy::$s(<$i_ty>::$i) => {
20                    let n = size_of::<$n_ty>();
21                    let b: [u8; _] = $b[0..n].try_into().unwrap();
22                    ScalarValue::$s(<$i_ty>::$i, <$n_ty>::from_le_bytes(b) as $t)
23                }
24            )*
25        }
26    }
27}
28
29impl ScalarValue {
30    fn ptr_size_max(ptr_size: ByteCount, signed: bool) -> ScalarResult<u128> {
31        match ptr_size {
32            2 => Ok(if signed {
33                i16::MAX as u128
34            } else {
35                u16::MAX as u128
36            }),
37            4 => Ok(if signed {
38                i32::MAX as u128
39            } else {
40                u32::MAX as u128
41            }),
42            8 => Ok(if signed {
43                i64::MAX as u128
44            } else {
45                u64::MAX as u128
46            }),
47            _ => Err(ScalarError::UnsupportedPtrSize),
48        }
49    }
50
51    fn ptr_size_min(ptr_size: ByteCount, signed: bool) -> ScalarResult<i128> {
52        match ptr_size {
53            2 => Ok(if signed {
54                i16::MIN as i128
55            } else {
56                u16::MIN as i128
57            }),
58            4 => Ok(if signed {
59                i32::MIN as i128
60            } else {
61                u32::MIN as i128
62            }),
63            8 => Ok(if signed {
64                i64::MIN as i128
65            } else {
66                u64::MIN as i128
67            }),
68            _ => Err(ScalarError::UnsupportedPtrSize),
69        }
70    }
71
72    pub fn get_integer_ty(&self) -> IntegerTy {
73        match self {
74            ScalarValue::Signed(ty, _) => IntegerTy::Signed(*ty),
75            ScalarValue::Unsigned(ty, _) => IntegerTy::Unsigned(*ty),
76        }
77    }
78
79    pub fn is_int(&self) -> bool {
80        matches!(self, ScalarValue::Signed(_, _))
81    }
82
83    pub fn is_uint(&self) -> bool {
84        matches!(self, ScalarValue::Unsigned(_, _))
85    }
86
87    /// When computing the result of binary operations, we convert the values
88    /// to u128 then back to the target type (while performing dynamic checks
89    /// of course).
90    pub fn as_uint(&self) -> ScalarResult<u128> {
91        match self {
92            ScalarValue::Unsigned(_, v) => Ok(*v),
93            _ => Err(ScalarError::IncorrectSign),
94        }
95    }
96
97    pub fn uint_is_in_bounds(ptr_size: ByteCount, ty: UIntTy, v: u128) -> bool {
98        match ty {
99            UIntTy::Usize => v <= Self::ptr_size_max(ptr_size, false).unwrap(),
100            UIntTy::U8 => v <= (u8::MAX as u128),
101            UIntTy::U16 => v <= (u16::MAX as u128),
102            UIntTy::U32 => v <= (u32::MAX as u128),
103            UIntTy::U64 => v <= (u64::MAX as u128),
104            UIntTy::U128 => true,
105        }
106    }
107
108    pub fn from_unchecked_uint(ty: UIntTy, v: u128) -> ScalarValue {
109        ScalarValue::Unsigned(ty, v)
110    }
111
112    pub fn from_uint(ptr_size: ByteCount, ty: UIntTy, v: u128) -> ScalarResult<ScalarValue> {
113        if !ScalarValue::uint_is_in_bounds(ptr_size, ty, v) {
114            trace!("Not in bounds for {:?}: {}", ty, v);
115            Err(ScalarError::OutOfBounds)
116        } else {
117            Ok(ScalarValue::from_unchecked_uint(ty, v))
118        }
119    }
120
121    /// When computing the result of binary operations, we convert the values
122    /// to i128 then back to the target type (while performing dynamic checks
123    /// of course).
124    pub fn as_int(&self) -> ScalarResult<i128> {
125        match self {
126            ScalarValue::Signed(_, v) => Ok(*v),
127            _ => Err(ScalarError::IncorrectSign),
128        }
129    }
130
131    pub fn int_is_in_bounds(ptr_size: ByteCount, ty: IntTy, v: i128) -> bool {
132        match ty {
133            IntTy::Isize => {
134                v >= Self::ptr_size_min(ptr_size, true).unwrap()
135                    && v <= Self::ptr_size_max(ptr_size, true).unwrap() as i128
136            }
137            IntTy::I8 => v >= (i8::MIN as i128) && v <= (i8::MAX as i128),
138            IntTy::I16 => v >= (i16::MIN as i128) && v <= (i16::MAX as i128),
139            IntTy::I32 => v >= (i32::MIN as i128) && v <= (i32::MAX as i128),
140            IntTy::I64 => v >= (i64::MIN as i128) && v <= (i64::MAX as i128),
141            IntTy::I128 => true,
142        }
143    }
144
145    pub fn from_unchecked_int(ty: IntTy, v: i128) -> ScalarValue {
146        ScalarValue::Signed(ty, v)
147    }
148
149    /// Most integers are represented as `u128` by rustc. We must be careful not to sign-extend.
150    pub fn to_bits(&self) -> u128 {
151        match *self {
152            ScalarValue::Unsigned(_, v) => v,
153            ScalarValue::Signed(_, v) => u128::from_le_bytes(v.to_le_bytes()),
154        }
155    }
156
157    /// Translates little endian bytes into a corresponding `ScalarValue`.
158    /// This needs to do the round-trip to the correct integer type to guarantee
159    /// that the values are correctly sign-extended (e.g. if the bytes encode -1i8, taking all 16 bytes
160    /// would lead to the value 255i128 instead of -1i128).
161    pub fn from_le_bytes(ty: IntegerTy, bytes: [u8; 16]) -> Self {
162        from_le_bytes!(
163            ty,
164            bytes,
165            [
166                (IntTy, Isize, Signed, isize, i128),
167                (IntTy, I8, Signed, i8, i128),
168                (IntTy, I16, Signed, i16, i128),
169                (IntTy, I32, Signed, i32, i128),
170                (IntTy, I64, Signed, i64, i128),
171                (IntTy, I128, Signed, i128, i128),
172                (UIntTy, Usize, Unsigned, usize, u128),
173                (UIntTy, U8, Unsigned, u8, u128),
174                (UIntTy, U16, Unsigned, u16, u128),
175                (UIntTy, U32, Unsigned, u32, u128),
176                (UIntTy, U64, Unsigned, u64, u128),
177                (UIntTy, U128, Unsigned, u128, u128)
178            ]
179        )
180    }
181
182    pub fn from_bits(ty: IntegerTy, bits: u128) -> Self {
183        let bytes = bits.to_le_bytes();
184        Self::from_le_bytes(ty, bytes)
185    }
186
187    /// **Warning**: most constants are stored as u128 by rustc. When converting
188    /// to i128, it is not correct to do `v as i128`, we must reinterpret the
189    /// bits (see [ScalarValue::from_le_bytes]).
190    pub fn from_int(ptr_size: ByteCount, ty: IntTy, v: i128) -> ScalarResult<ScalarValue> {
191        if !ScalarValue::int_is_in_bounds(ptr_size, ty, v) {
192            Err(ScalarError::OutOfBounds)
193        } else {
194            Ok(ScalarValue::from_unchecked_int(ty, v))
195        }
196    }
197
198    pub fn to_constant(self) -> ConstantExpr {
199        let literal_ty = match self {
200            ScalarValue::Signed(int_ty, _) => LiteralTy::Int(int_ty),
201            ScalarValue::Unsigned(uint_ty, _) => LiteralTy::UInt(uint_ty),
202        };
203        ConstantExpr {
204            value: RawConstantExpr::Literal(Literal::Scalar(self)),
205            ty: TyKind::Literal(literal_ty).into_ty(),
206        }
207    }
208}
209
210/// Custom serializer that stores 128 bit integers as strings to avoid overflow.
211pub(crate) mod scalar_value_ser_de {
212    use std::{marker::PhantomData, str::FromStr};
213
214    use serde::de::{Deserializer, Error};
215
216    pub fn serialize<S, V>(val: &V, serializer: S) -> Result<S::Ok, S::Error>
217    where
218        S: serde::ser::Serializer,
219        V: ToString,
220    {
221        serializer.serialize_str(&val.to_string())
222    }
223
224    pub fn deserialize<'de, D, V>(deserializer: D) -> Result<V, D::Error>
225    where
226        D: Deserializer<'de>,
227        V: FromStr,
228    {
229        struct Visitor<V> {
230            _val: PhantomData<V>,
231        }
232        impl<'de, V> serde::de::Visitor<'de> for Visitor<V>
233        where
234            V: FromStr,
235        {
236            type Value = V;
237            fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
238                write!(f, "ScalarValue value")
239            }
240            fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
241            where
242                E: Error,
243            {
244                v.parse()
245                    .map_err(|_| E::custom("Could not parse 128 bit integer!"))
246            }
247        }
248        deserializer.deserialize_str(Visitor { _val: PhantomData })
249    }
250}
251
252#[cfg(test)]
253mod test {
254    use super::*;
255
256    #[test]
257    fn test_big_endian_scalars() -> ScalarResult<()> {
258        let u128 = 0x12345678901234567890123456789012u128;
259        let le_bytes = u128.to_le_bytes();
260
261        let le_scalar = ScalarValue::from_le_bytes(IntegerTy::Unsigned(UIntTy::U128), le_bytes);
262        assert_eq!(le_scalar, ScalarValue::Unsigned(UIntTy::U128, u128));
263
264        let i64 = 0x1234567890123456i64;
265        let le_bytes = (i64 as i128).to_le_bytes();
266        let le_scalar = ScalarValue::from_le_bytes(IntegerTy::Signed(IntTy::I64), le_bytes);
267        assert_eq!(le_scalar, ScalarValue::Signed(IntTy::I64, i64 as i128));
268
269        Ok(())
270    }
271}