Skip to main content

hir_ty/next_solver/consts/
valtree.rs

1use std::{fmt, hash::Hash, num::NonZero};
2
3use intern::{Interned, InternedRef, impl_internable};
4use macros::{GenericTypeVisitable, TypeFoldable, TypeVisitable};
5use rustc_abi::{Size, TargetDataLayout};
6use rustc_type_ir::{GenericTypeVisitable, TypeFoldable, TypeVisitable, inherent::IntoKind};
7use stdx::never;
8
9use crate::{
10    MemoryMap, ParamEnvAndCrate, consteval,
11    db::HirDatabase,
12    mir::{IsSigned, pad16},
13    next_solver::{Const, Consts, TyKind, WorldExposer},
14};
15
16use super::{DbInterner, Ty};
17
18pub type ValTreeKind<'db> = rustc_type_ir::ValTreeKind<DbInterner<'db>>;
19
20/// A type-level constant value.
21///
22/// Represents a typed, fully evaluated constant.
23#[derive(
24    Debug, Copy, Clone, Eq, PartialEq, Hash, TypeFoldable, TypeVisitable, GenericTypeVisitable,
25)]
26pub struct ValueConst<'db> {
27    pub ty: Ty<'db>,
28    pub value: ValTree<'db>,
29}
30
31impl<'db> ValueConst<'db> {
32    pub fn new(ty: Ty<'db>, kind: ValTreeKind<'db>) -> Self {
33        let value = ValTree::new(kind);
34        ValueConst { ty, value }
35    }
36
37    /// Attempts to convert to a `ValTreeKind::Leaf` value.
38    pub fn try_to_leaf(self) -> Option<ScalarInt> {
39        match self.value.inner() {
40            ValTreeKind::Leaf(s) => Some(*s),
41            ValTreeKind::Branch(_) => None,
42        }
43    }
44
45    /// Attempts to extract the raw bits from the constant.
46    ///
47    /// Fails if the value can't be represented as bits (e.g. because it is a reference
48    /// or an aggregate).
49    #[inline]
50    pub fn try_to_bits(
51        self,
52        db: &'db dyn HirDatabase,
53        param_env: ParamEnvAndCrate<'db>,
54    ) -> Option<u128> {
55        let (TyKind::Bool | TyKind::Char | TyKind::Uint(_) | TyKind::Int(_) | TyKind::Float(_)) =
56            self.ty.kind()
57        else {
58            return None;
59        };
60        let scalar = self.try_to_leaf()?;
61        let size = db.layout_of_ty(self.ty.store(), param_env.store()).ok()?.size;
62        Some(scalar.to_bits(size))
63    }
64
65    pub fn try_to_target_usize(self, data_layout: &TargetDataLayout) -> Option<u64> {
66        if !self.ty.is_usize() {
67            return None;
68        }
69        self.try_to_leaf().map(|s| s.to_target_usize(data_layout))
70    }
71}
72
73pub(super) fn allocation_to_const<'db>(
74    interner: DbInterner<'db>,
75    ty: Ty<'db>,
76    memory: &[u8],
77    memory_map: &MemoryMap<'db>,
78    param_env: ParamEnvAndCrate<'db>,
79) -> Const<'db> {
80    let Ok(data_layout) = interner.db.target_data_layout(param_env.krate) else {
81        return Const::error(interner);
82    };
83    let valtree = match ty.kind() {
84        TyKind::Bool => ValTreeKind::Leaf(ScalarInt::from(memory[0] != 0)),
85        TyKind::Char => {
86            let it = u128::from_le_bytes(pad16(memory, IsSigned::No)) as u32;
87            let Ok(c) = char::try_from(it) else {
88                return Const::error(interner);
89            };
90            ValTreeKind::Leaf(ScalarInt::from(c))
91        }
92        TyKind::Int(int) => {
93            let it = i128::from_le_bytes(pad16(memory, IsSigned::Yes));
94            let size = int.bit_width().map(Size::from_bits).unwrap_or(data_layout.pointer_size());
95            let scalar = ScalarInt::try_from_int(it, size).unwrap();
96            ValTreeKind::Leaf(scalar)
97        }
98        TyKind::Uint(uint) => {
99            let it = u128::from_le_bytes(pad16(memory, IsSigned::No));
100            let size = uint.bit_width().map(Size::from_bits).unwrap_or(data_layout.pointer_size());
101            let scalar = ScalarInt::try_from_uint(it, size).unwrap();
102            ValTreeKind::Leaf(scalar)
103        }
104        TyKind::Float(float) => {
105            let scalar = match float {
106                rustc_ast_ir::FloatTy::F16 => {
107                    ScalarInt::from(u16::from_le_bytes(memory.try_into().unwrap()))
108                }
109                rustc_ast_ir::FloatTy::F32 => {
110                    ScalarInt::from(u32::from_le_bytes(memory.try_into().unwrap()))
111                }
112                rustc_ast_ir::FloatTy::F64 => {
113                    ScalarInt::from(u64::from_le_bytes(memory.try_into().unwrap()))
114                }
115                rustc_ast_ir::FloatTy::F128 => {
116                    ScalarInt::from(u128::from_le_bytes(memory.try_into().unwrap()))
117                }
118            };
119            ValTreeKind::Leaf(scalar)
120        }
121        TyKind::Ref(_, t, _) => match t.kind() {
122            TyKind::Str => {
123                let addr = usize::from_le_bytes(memory[0..memory.len() / 2].try_into().unwrap());
124                let size = usize::from_le_bytes(memory[memory.len() / 2..].try_into().unwrap());
125                let Some(bytes) = memory_map.get(addr, size) else {
126                    return Const::error(interner);
127                };
128                let u8_values = &interner.default_types().consts.u8_values;
129                ValTreeKind::Branch(Consts::new_from_iter(
130                    interner,
131                    bytes.iter().map(|&byte| u8_values[usize::from(byte)]),
132                ))
133            }
134            TyKind::Slice(ty) => {
135                let addr = usize::from_le_bytes(memory[0..memory.len() / 2].try_into().unwrap());
136                let count = usize::from_le_bytes(memory[memory.len() / 2..].try_into().unwrap());
137                let Ok(layout) = interner.db.layout_of_ty(ty.store(), param_env.store()) else {
138                    return Const::error(interner);
139                };
140                let size_one = layout.size.bytes_usize();
141                let Some(bytes) = memory_map.get(addr, size_one * count) else {
142                    return Const::error(interner);
143                };
144                let expected_len = count * size_one;
145                if bytes.len() < expected_len {
146                    never!(
147                        "Memory map size is too small. Expected {expected_len}, got {}",
148                        bytes.len(),
149                    );
150                    return Const::error(interner);
151                }
152                let items = (0..count).map(|i| {
153                    let offset = size_one * i;
154                    let bytes = &bytes[offset..offset + size_one];
155                    allocation_to_const(interner, ty, bytes, memory_map, param_env)
156                });
157                ValTreeKind::Branch(Consts::new_from_iter(interner, items))
158            }
159            TyKind::Dynamic(_, _) => {
160                let addr = usize::from_le_bytes(memory[0..memory.len() / 2].try_into().unwrap());
161                let ty_id = usize::from_le_bytes(memory[memory.len() / 2..].try_into().unwrap());
162                let Ok(t) = memory_map.vtable_ty(ty_id) else {
163                    return Const::error(interner);
164                };
165                let Ok(layout) = interner.db.layout_of_ty(t.store(), param_env.store()) else {
166                    return Const::error(interner);
167                };
168                let size = layout.size.bytes_usize();
169                let Some(bytes) = memory_map.get(addr, size) else {
170                    return Const::error(interner);
171                };
172                return allocation_to_const(interner, t, bytes, memory_map, param_env);
173            }
174            TyKind::Adt(..) if memory.len() == 2 * size_of::<usize>() => {
175                // FIXME: Unsized ADT.
176                return Const::error(interner);
177            }
178            _ => {
179                let addr = usize::from_le_bytes(match memory.try_into() {
180                    Ok(b) => b,
181                    Err(_) => {
182                        never!(
183                            "tried rendering ty {:?} in const ref with incorrect byte count {}",
184                            t,
185                            memory.len()
186                        );
187                        return Const::error(interner);
188                    }
189                });
190                let Ok(layout) = interner.db.layout_of_ty(t.store(), param_env.store()) else {
191                    return Const::error(interner);
192                };
193                let size = layout.size.bytes_usize();
194                let Some(bytes) = memory_map.get(addr, size) else {
195                    return Const::error(interner);
196                };
197                return allocation_to_const(interner, t, bytes, memory_map, param_env);
198            }
199        },
200        TyKind::Tuple(tys) => {
201            let Ok(layout) = interner.db.layout_of_ty(ty.store(), param_env.store()) else {
202                return Const::error(interner);
203            };
204            let items = tys.iter().enumerate().map(|(id, ty)| {
205                let offset = layout.fields.offset(id).bytes_usize();
206                let Ok(layout) = interner.db.layout_of_ty(ty.store(), param_env.store()) else {
207                    return Const::error(interner);
208                };
209                let size = layout.size.bytes_usize();
210                allocation_to_const(
211                    interner,
212                    ty,
213                    &memory[offset..offset + size],
214                    memory_map,
215                    param_env,
216                )
217            });
218            ValTreeKind::Branch(Consts::new_from_iter(interner, items))
219        }
220        TyKind::Adt(..) => {
221            // FIXME: This requires `adt_const_params`.
222            return Const::error(interner);
223        }
224        TyKind::FnDef(..) => {
225            // FIXME: Fn items.
226            return Const::error(interner);
227        }
228        TyKind::FnPtr(_, _) | TyKind::RawPtr(_, _) => {
229            let it = u128::from_le_bytes(pad16(memory, IsSigned::No));
230            // FIXME: Unsized pointers.
231            let scalar = ScalarInt::try_from_uint(it, data_layout.pointer_size()).unwrap();
232            ValTreeKind::Leaf(scalar)
233        }
234        TyKind::Array(ty, len) => {
235            let Some(len) = consteval::try_const_usize(interner.db, len) else {
236                return Const::error(interner);
237            };
238            let Ok(layout) = interner.db.layout_of_ty(ty.store(), param_env.store()) else {
239                return Const::error(interner);
240            };
241            let size_one = layout.size.bytes_usize();
242            let items = (0..len as usize).map(|i| {
243                let offset = size_one * i;
244                allocation_to_const(
245                    interner,
246                    ty,
247                    &memory[offset..offset + size_one],
248                    memory_map,
249                    param_env,
250                )
251            });
252            ValTreeKind::Branch(Consts::new_from_iter(interner, items))
253        }
254        TyKind::Never => return Const::error(interner),
255        // FIXME:
256        TyKind::Closure(_, _)
257        | TyKind::Coroutine(_, _)
258        | TyKind::CoroutineWitness(_, _)
259        | TyKind::CoroutineClosure(_, _)
260        | TyKind::UnsafeBinder(_) => return Const::error(interner),
261        // The below arms are unreachable, since const eval will bail out before here.
262        TyKind::Foreign(_) => return Const::error(interner),
263        TyKind::Pat(_, _) => return Const::error(interner),
264        TyKind::Error(..)
265        | TyKind::Placeholder(_)
266        | TyKind::Alias(..)
267        | TyKind::Param(_)
268        | TyKind::Bound(_, _)
269        | TyKind::Infer(_) => return Const::error(interner),
270        // The below arms are unreachable, since we handled them in ref case.
271        TyKind::Slice(_) | TyKind::Str | TyKind::Dynamic(_, _) => {
272            return Const::error(interner);
273        }
274    };
275    Const::new_valtree(interner, ty, valtree)
276}
277
278impl<'db> rustc_type_ir::inherent::ValueConst<DbInterner<'db>> for ValueConst<'db> {
279    fn ty(self) -> Ty<'db> {
280        self.ty
281    }
282
283    fn valtree(self) -> ValTree<'db> {
284        self.value
285    }
286}
287
288#[derive(Clone, Copy, PartialEq, Eq, Hash)]
289pub struct ValTree<'db> {
290    interned: InternedRef<'db, ValTreeInterned>,
291}
292
293impl<'db, V: WorldExposer> GenericTypeVisitable<V> for ValTree<'db> {
294    fn generic_visit_with(&self, visitor: &mut V) {
295        if visitor.on_interned(self.interned).is_continue() {
296            self.inner().generic_visit_with(visitor);
297        }
298    }
299}
300
301impl<'db> TypeVisitable<DbInterner<'db>> for ValTree<'db> {
302    fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>(
303        &self,
304        visitor: &mut V,
305    ) -> V::Result {
306        self.inner().visit_with(visitor)
307    }
308}
309
310impl<'db> TypeFoldable<DbInterner<'db>> for ValTree<'db> {
311    fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>(
312        self,
313        folder: &mut F,
314    ) -> Result<Self, F::Error> {
315        self.inner().try_fold_with(folder).map(ValTree::new)
316    }
317
318    fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self {
319        ValTree::new(self.inner().fold_with(folder))
320    }
321}
322
323#[derive(Debug, PartialEq, Eq, Hash, GenericTypeVisitable)]
324pub(in super::super) struct ValTreeInterned(ValTreeKind<'static>);
325
326impl_internable!(gc; ValTreeInterned);
327
328const _: () = {
329    const fn is_copy<T: Copy>() {}
330    is_copy::<ValTree<'static>>();
331};
332
333impl<'db> IntoKind for ValTree<'db> {
334    type Kind = ValTreeKind<'db>;
335
336    fn kind(self) -> Self::Kind {
337        *self.inner()
338    }
339}
340
341impl<'db> ValTree<'db> {
342    #[inline]
343    pub fn new(kind: ValTreeKind<'db>) -> Self {
344        let kind = unsafe { std::mem::transmute::<ValTreeKind<'db>, ValTreeKind<'static>>(kind) };
345        Self { interned: Interned::new_gc(ValTreeInterned(kind)) }
346    }
347
348    #[inline]
349    pub fn inner(&self) -> &ValTreeKind<'db> {
350        let inner = &self.interned.0;
351        unsafe { std::mem::transmute::<&ValTreeKind<'static>, &ValTreeKind<'db>>(inner) }
352    }
353
354    pub fn from_scalar_int(_interner: DbInterner<'db>, i: ScalarInt) -> Self {
355        ValTree::new(ValTreeKind::Leaf(i))
356    }
357}
358
359impl std::fmt::Debug for ValTree<'_> {
360    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
361        self.interned.fmt(f)
362    }
363}
364
365/// The raw bytes of a simple value.
366///
367/// This is a packed struct in order to allow this type to be optimally embedded in enums
368/// (like Scalar).
369#[derive(Clone, Copy, Eq, PartialEq, Hash)]
370#[repr(Rust, packed)]
371pub struct ScalarInt {
372    /// The first `size` bytes of `data` are the value.
373    /// Do not try to read less or more bytes than that. The remaining bytes must be 0.
374    data: u128,
375    size: NonZero<u8>,
376}
377
378impl ScalarInt {
379    pub const TRUE: ScalarInt = ScalarInt { data: 1_u128, size: NonZero::new(1).unwrap() };
380    pub const FALSE: ScalarInt = ScalarInt { data: 0_u128, size: NonZero::new(1).unwrap() };
381
382    fn raw(data: u128, size: Size) -> Self {
383        Self { data, size: NonZero::new(size.bytes() as u8).unwrap() }
384    }
385
386    #[inline]
387    pub fn size(self) -> Size {
388        Size::from_bytes(self.size.get())
389    }
390
391    /// Make sure the `data` fits in `size`.
392    /// This is guaranteed by all constructors here, but having had this check saved us from
393    /// bugs many times in the past, so keeping it around is definitely worth it.
394    #[inline(always)]
395    fn check_data(self) {
396        // Using a block `{self.data}` here to force a copy instead of using `self.data`
397        // directly, because `debug_assert_eq` takes references to its arguments and formatting
398        // arguments and would thus borrow `self.data`. Since `Self`
399        // is a packed struct, that would create a possibly unaligned reference, which
400        // is UB.
401        debug_assert_eq!(
402            self.size().truncate(self.data),
403            { self.data },
404            "Scalar value {:#x} exceeds size of {} bytes",
405            { self.data },
406            self.size
407        );
408    }
409
410    #[inline]
411    pub fn null(size: Size) -> Self {
412        Self::raw(0, size)
413    }
414
415    #[inline]
416    pub fn is_null(self) -> bool {
417        self.data == 0
418    }
419
420    #[inline]
421    pub fn try_from_uint(i: impl Into<u128>, size: Size) -> Option<Self> {
422        let (r, overflow) = Self::truncate_from_uint(i, size);
423        if overflow { None } else { Some(r) }
424    }
425
426    /// Returns the truncated result, and whether truncation changed the value.
427    #[inline]
428    pub fn truncate_from_uint(i: impl Into<u128>, size: Size) -> (Self, bool) {
429        let data = i.into();
430        let r = Self::raw(size.truncate(data), size);
431        (r, r.data != data)
432    }
433
434    #[inline]
435    pub fn try_from_int(i: impl Into<i128>, size: Size) -> Option<Self> {
436        let (r, overflow) = Self::truncate_from_int(i, size);
437        if overflow { None } else { Some(r) }
438    }
439
440    /// Returns the truncated result, and whether truncation changed the value.
441    #[inline]
442    pub fn truncate_from_int(i: impl Into<i128>, size: Size) -> (Self, bool) {
443        let data = i.into();
444        // `into` performed sign extension, we have to truncate
445        let r = Self::raw(size.truncate(data as u128), size);
446        (r, size.sign_extend(r.data) != data)
447    }
448
449    #[inline]
450    pub fn try_from_target_usize(
451        i: impl Into<u128>,
452        data_layout: &TargetDataLayout,
453    ) -> Option<Self> {
454        Self::try_from_uint(i, data_layout.pointer_size())
455    }
456
457    /// Try to convert this ScalarInt to the raw underlying bits.
458    /// Fails if the size is wrong. Generally a wrong size should lead to a panic,
459    /// but Miri sometimes wants to be resilient to size mismatches,
460    /// so the interpreter will generally use this `try` method.
461    #[inline]
462    pub fn try_to_bits(self, target_size: Size) -> Result<u128, Size> {
463        assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST");
464        if target_size.bytes() == u64::from(self.size.get()) {
465            self.check_data();
466            Ok(self.data)
467        } else {
468            Err(self.size())
469        }
470    }
471
472    #[inline]
473    pub fn to_bits(self, target_size: Size) -> u128 {
474        self.try_to_bits(target_size).unwrap_or_else(|size| {
475            panic!("expected int of size {}, but got size {}", target_size.bytes(), size.bytes())
476        })
477    }
478
479    /// Extracts the bits from the scalar without checking the size.
480    #[inline]
481    pub fn to_bits_unchecked(self) -> u128 {
482        self.check_data();
483        self.data
484    }
485
486    /// Converts the `ScalarInt` to an unsigned integer of the given size.
487    /// Panics if the size of the `ScalarInt` is not equal to `size`.
488    #[inline]
489    pub fn to_uint(self, size: Size) -> u128 {
490        self.to_bits(size)
491    }
492
493    #[inline]
494    pub fn to_uint_unchecked(self) -> u128 {
495        self.data
496    }
497
498    /// Converts the `ScalarInt` to `u8`.
499    /// Panics if the `size` of the `ScalarInt`in not equal to 1 byte.
500    #[inline]
501    pub fn to_u8(self) -> u8 {
502        self.to_uint(Size::from_bits(8)).try_into().unwrap()
503    }
504
505    /// Converts the `ScalarInt` to `u16`.
506    /// Panics if the size of the `ScalarInt` in not equal to 2 bytes.
507    #[inline]
508    pub fn to_u16(self) -> u16 {
509        self.to_uint(Size::from_bits(16)).try_into().unwrap()
510    }
511
512    /// Converts the `ScalarInt` to `u32`.
513    /// Panics if the `size` of the `ScalarInt` in not equal to 4 bytes.
514    #[inline]
515    pub fn to_u32(self) -> u32 {
516        self.to_uint(Size::from_bits(32)).try_into().unwrap()
517    }
518
519    /// Converts the `ScalarInt` to `u64`.
520    /// Panics if the `size` of the `ScalarInt` in not equal to 8 bytes.
521    #[inline]
522    pub fn to_u64(self) -> u64 {
523        self.to_uint(Size::from_bits(64)).try_into().unwrap()
524    }
525
526    /// Converts the `ScalarInt` to `u128`.
527    /// Panics if the `size` of the `ScalarInt` in not equal to 16 bytes.
528    #[inline]
529    pub fn to_u128(self) -> u128 {
530        self.to_uint(Size::from_bits(128))
531    }
532
533    #[inline]
534    pub fn to_target_usize(&self, data_layout: &TargetDataLayout) -> u64 {
535        self.to_uint(data_layout.pointer_size()).try_into().unwrap()
536    }
537
538    /// Converts the `ScalarInt` to `bool`.
539    /// Panics if the `size` of the `ScalarInt` is not equal to 1 byte.
540    /// Errors if it is not a valid `bool`.
541    #[inline]
542    pub fn try_to_bool(self) -> Result<bool, ()> {
543        match self.to_u8() {
544            0 => Ok(false),
545            1 => Ok(true),
546            _ => Err(()),
547        }
548    }
549
550    /// Converts the `ScalarInt` to a signed integer of the given size.
551    /// Panics if the size of the `ScalarInt` is not equal to `size`.
552    #[inline]
553    pub fn to_int(self, size: Size) -> i128 {
554        let b = self.to_bits(size);
555        size.sign_extend(b)
556    }
557
558    #[inline]
559    pub fn to_int_unchecked(self) -> i128 {
560        self.size().sign_extend(self.data)
561    }
562
563    /// Converts the `ScalarInt` to i8.
564    /// Panics if the size of the `ScalarInt` is not equal to 1 byte.
565    pub fn to_i8(self) -> i8 {
566        self.to_int(Size::from_bits(8)).try_into().unwrap()
567    }
568
569    /// Converts the `ScalarInt` to i16.
570    /// Panics if the size of the `ScalarInt` is not equal to 2 bytes.
571    pub fn to_i16(self) -> i16 {
572        self.to_int(Size::from_bits(16)).try_into().unwrap()
573    }
574
575    /// Converts the `ScalarInt` to i32.
576    /// Panics if the size of the `ScalarInt` is not equal to 4 bytes.
577    pub fn to_i32(self) -> i32 {
578        self.to_int(Size::from_bits(32)).try_into().unwrap()
579    }
580
581    /// Converts the `ScalarInt` to i64.
582    /// Panics if the size of the `ScalarInt` is not equal to 8 bytes.
583    pub fn to_i64(self) -> i64 {
584        self.to_int(Size::from_bits(64)).try_into().unwrap()
585    }
586
587    /// Converts the `ScalarInt` to i128.
588    /// Panics if the size of the `ScalarInt` is not equal to 16 bytes.
589    pub fn to_i128(self) -> i128 {
590        self.to_int(Size::from_bits(128))
591    }
592
593    #[inline]
594    pub fn to_target_isize(&self, data_layout: &TargetDataLayout) -> i64 {
595        self.to_int(data_layout.pointer_size()).try_into().unwrap()
596    }
597}
598
599macro_rules! from_x_for_scalar_int {
600    ($($ty:ty),*) => {
601        $(
602            impl From<$ty> for ScalarInt {
603                #[inline]
604                fn from(u: $ty) -> Self {
605                    Self {
606                        data: u128::from(u),
607                        size: NonZero::new(size_of::<$ty>() as u8).unwrap(),
608                    }
609                }
610            }
611        )*
612    }
613}
614
615macro_rules! from_scalar_int_for_x {
616    ($($ty:ty),*) => {
617        $(
618            impl From<ScalarInt> for $ty {
619                #[inline]
620                fn from(int: ScalarInt) -> Self {
621                    // The `unwrap` cannot fail because to_uint (if it succeeds)
622                    // is guaranteed to return a value that fits into the size.
623                    int.to_uint(Size::from_bytes(size_of::<$ty>()))
624                       .try_into().unwrap()
625                }
626            }
627        )*
628    }
629}
630
631from_x_for_scalar_int!(u8, u16, u32, u64, u128, bool);
632from_scalar_int_for_x!(u8, u16, u32, u64, u128);
633
634impl TryFrom<ScalarInt> for bool {
635    type Error = ();
636    #[inline]
637    fn try_from(int: ScalarInt) -> Result<Self, ()> {
638        int.try_to_bool()
639    }
640}
641
642impl From<char> for ScalarInt {
643    #[inline]
644    fn from(c: char) -> Self {
645        (c as u32).into()
646    }
647}
648
649macro_rules! from_x_for_scalar_int_signed {
650    ($($ty:ty),*) => {
651        $(
652            impl From<$ty> for ScalarInt {
653                #[inline]
654                fn from(u: $ty) -> Self {
655                    Self {
656                        data: u128::from(u.cast_unsigned()), // go via the unsigned type of the same size
657                        size: NonZero::new(size_of::<$ty>() as u8).unwrap(),
658                    }
659                }
660            }
661        )*
662    }
663}
664
665macro_rules! from_scalar_int_for_x_signed {
666    ($($ty:ty),*) => {
667        $(
668            impl From<ScalarInt> for $ty {
669                #[inline]
670                fn from(int: ScalarInt) -> Self {
671                    // The `unwrap` cannot fail because to_int (if it succeeds)
672                    // is guaranteed to return a value that fits into the size.
673                    int.to_int(Size::from_bytes(size_of::<$ty>()))
674                       .try_into().unwrap()
675                }
676            }
677        )*
678    }
679}
680
681from_x_for_scalar_int_signed!(i8, i16, i32, i64, i128);
682from_scalar_int_for_x_signed!(i8, i16, i32, i64, i128);
683
684impl From<std::cmp::Ordering> for ScalarInt {
685    #[inline]
686    fn from(c: std::cmp::Ordering) -> Self {
687        // Here we rely on `cmp::Ordering` having the same values in host and target!
688        ScalarInt::from(c as i8)
689    }
690}
691
692/// Error returned when a conversion from ScalarInt to char fails.
693#[derive(Debug)]
694pub struct CharTryFromScalarInt;
695
696impl TryFrom<ScalarInt> for char {
697    type Error = CharTryFromScalarInt;
698
699    #[inline]
700    fn try_from(int: ScalarInt) -> Result<Self, Self::Error> {
701        match char::from_u32(int.to_u32()) {
702            Some(c) => Ok(c),
703            None => Err(CharTryFromScalarInt),
704        }
705    }
706}
707
708impl fmt::Debug for ScalarInt {
709    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
710        // Dispatch to LowerHex below.
711        write!(f, "0x{self:x}")
712    }
713}
714
715impl fmt::LowerHex for ScalarInt {
716    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
717        self.check_data();
718        if f.alternate() {
719            // Like regular ints, alternate flag adds leading `0x`.
720            write!(f, "0x")?;
721        }
722        // Format as hex number wide enough to fit any value of the given `size`.
723        // So data=20, size=1 will be "0x14", but with size=4 it'll be "0x00000014".
724        // Using a block `{self.data}` here to force a copy instead of using `self.data`
725        // directly, because `write!` takes references to its formatting arguments and
726        // would thus borrow `self.data`. Since `Self`
727        // is a packed struct, that would create a possibly unaligned reference, which
728        // is UB.
729        write!(f, "{:01$x}", { self.data }, self.size.get() as usize * 2)
730    }
731}
732
733impl fmt::UpperHex for ScalarInt {
734    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
735        self.check_data();
736        // Format as hex number wide enough to fit any value of the given `size`.
737        // So data=20, size=1 will be "0x14", but with size=4 it'll be "0x00000014".
738        // Using a block `{self.data}` here to force a copy instead of using `self.data`
739        // directly, because `write!` takes references to its formatting arguments and
740        // would thus borrow `self.data`. Since `Self`
741        // is a packed struct, that would create a possibly unaligned reference, which
742        // is UB.
743        write!(f, "{:01$X}", { self.data }, self.size.get() as usize * 2)
744    }
745}
746
747impl fmt::Display for ScalarInt {
748    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
749        self.check_data();
750        write!(f, "{}", { self.data })
751    }
752}