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