abi_stable/abi_stability/
const_generics.rs

1//! This module implements the trait object used to check const generics.
2
3use crate::{
4    abi_stability::{
5        check_layout_compatibility,
6        extra_checks::{ExtraChecksError, TypeCheckerMut},
7    },
8    erased_types::{
9        c_functions::{adapt_std_fmt, debug_impl, partial_eq_impl},
10        FormattingMode,
11    },
12    marker_type::ErasedObject,
13    prefix_type::WithMetadata,
14    sabi_types::RRef,
15    std_types::{RErr, ROk, RResult, RString},
16    type_layout::TypeLayout,
17    StableAbi,
18};
19
20use std::{
21    cmp::{Eq, PartialEq},
22    fmt::{self, Debug},
23};
24
25///////////////////////////////////////////////////////////////////////////////
26
27/// A trait object used to check equality between const generic parameters.
28#[repr(C)]
29#[derive(Copy, Clone, StableAbi)]
30pub struct ConstGeneric {
31    ptr: RRef<'static, ErasedObject>,
32    vtable: ConstGenericVTable_Ref,
33}
34
35unsafe impl Send for ConstGeneric {}
36unsafe impl Sync for ConstGeneric {}
37
38impl ConstGeneric {
39    /// Constructs a ConstGeneric from a reference.
40    pub const fn new<T>(this: &'static T) -> Self
41    where
42        T: StableAbi + Eq + PartialEq + Debug + Send + Sync + 'static,
43    {
44        Self {
45            ptr: unsafe { RRef::from_raw(this as *const T as *const ErasedObject) },
46            vtable: ConstGenericVTableFor::<T>::VTABLE,
47        }
48    }
49
50    /// Compares this to another `ConstGeneric` for equality,
51    /// returning an error if the type layout of `self` and `other` is not compatible.
52    pub fn is_equal(
53        &self,
54        other: &Self,
55        mut checker: TypeCheckerMut<'_>,
56    ) -> Result<bool, ExtraChecksError> {
57        match checker.check_compatibility(self.vtable.layout(), other.vtable.layout()) {
58            ROk(_) => unsafe { Ok(self.vtable.partial_eq()(self.ptr, other.ptr)) },
59            RErr(e) => Err(e),
60        }
61    }
62}
63
64impl Debug for ConstGeneric {
65    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66        unsafe { adapt_std_fmt::<ErasedObject>(self.ptr, self.vtable.debug(), f) }
67    }
68}
69
70// Make sure that this isn't called within `check_layout_compatibility` itself,
71// since it would cause infinite recursion.
72impl PartialEq for ConstGeneric {
73    fn eq(&self, other: &Self) -> bool {
74        if check_layout_compatibility(self.vtable.layout(), other.vtable.layout()).is_err() {
75            false
76        } else {
77            unsafe { self.vtable.partial_eq()(self.ptr, other.ptr) }
78        }
79    }
80}
81
82impl Eq for ConstGeneric {}
83
84///////////////////////////////////////////////////////////////////////////////
85
86/// The vtable of `ConstGeneric`
87#[repr(C)]
88#[derive(StableAbi)]
89#[sabi(kind(Prefix))]
90#[sabi(missing_field(panic))]
91struct ConstGenericVTable {
92    layout: &'static TypeLayout,
93    partial_eq: unsafe extern "C" fn(RRef<'_, ErasedObject>, RRef<'_, ErasedObject>) -> bool,
94    #[sabi(last_prefix_field)]
95    debug: unsafe extern "C" fn(
96        RRef<'_, ErasedObject>,
97        FormattingMode,
98        &mut RString,
99    ) -> RResult<(), ()>,
100}
101
102/// A type that contains the vtable stored in the `ConstGeneric` constructed from a `T`.
103/// This is used as a workaround for `const fn` not allowing trait bounds.
104struct ConstGenericVTableFor<T>(T);
105
106impl<T> ConstGenericVTableFor<T>
107where
108    T: StableAbi + Eq + PartialEq + Debug + Send + Sync + 'static,
109{
110    const _VTABLE_STATIC: &'static WithMetadata<ConstGenericVTable> = &{
111        WithMetadata::new(ConstGenericVTable {
112            layout: <T as StableAbi>::LAYOUT,
113            partial_eq: partial_eq_impl::<T>,
114            debug: debug_impl::<T>,
115        })
116    };
117
118    /// Constructs a `ConstGenericVTableFor`
119    const VTABLE: ConstGenericVTable_Ref =
120        ConstGenericVTable_Ref(Self::_VTABLE_STATIC.static_as_prefix());
121}