const_type_layout/
typeset.rs

1//! Helper module to compute the set of types that a type links to and expand it
2//! into the complete type graph.
3
4#[doc(hidden)]
5pub trait ComputeSet: sealed::ComputeSet {
6    const LEN: usize;
7
8    type Output<H: ComputeTypeSet>: ExpandTypeSet;
9
10    type TyHList: 'static + Copy + core::marker::Freeze;
11    const TYS: &'static Self::TyHList;
12}
13
14mod sealed {
15    pub trait ComputeSet {}
16
17    impl ComputeSet for super::private::Empty {}
18    impl<H2: super::ComputeTypeSet, T: ComputeSet> ComputeSet for super::private::Cons<H2, T> {}
19}
20
21type Set<H, T> = <T as ComputeSet>::Output<H>;
22
23/// Computes the set of types that a type links to.
24///
25/// # Safety
26///
27/// It is only safe to implement this trait if it accurately includes
28/// all inner component types that are referenced by this type's layout. Use
29/// [`#[derive(TypeLayout)]`](const_type_layout_derive::TypeLayout) instead.
30///
31/// # Example
32///
33/// The struct `Foo` with `u8` and `u16` fields links to `u8` and `u16`:
34///
35/// ```rust
36/// # #![feature(const_type_name)]
37/// # #![feature(offset_of)]
38/// # use const_type_layout::{
39/// #    Field, MaybeUninhabited, TypeLayout, TypeLayoutInfo, TypeStructure,
40/// # };
41/// # use const_type_layout::inhabited;
42/// # use const_type_layout::typeset::{ComputeTypeSet, ExpandTypeSet, tset};
43/// struct Foo {
44///     a: u8,
45///     b: u16,
46/// }
47///
48/// # unsafe impl TypeLayout for Foo {
49/// #     type Inhabited = inhabited::all![u8, u16];
50/// #
51/// #     const TYPE_LAYOUT: TypeLayoutInfo<'static> = TypeLayoutInfo {
52/// #         name: ::core::any::type_name::<Self>(),
53/// #         size: ::core::mem::size_of::<Self>(),
54/// #         alignment: ::core::mem::align_of::<Self>(),
55/// #         structure: TypeStructure::Struct {
56/// #             repr: "",
57/// #             fields: &[
58/// #                 Field {
59/// #                     name: "a",
60/// #                     offset: MaybeUninhabited::new::<u8>(::core::mem::offset_of!(Self, a)),
61/// #                     ty: ::core::any::type_name::<u8>(),
62/// #                 },
63/// #                 Field {
64/// #                     name: "b",
65/// #                     offset: MaybeUninhabited::new::<u16>(::core::mem::offset_of!(Self, b)),
66/// #                     ty: ::core::any::type_name::<u16>(),
67/// #                 },
68/// #             ],
69/// #         },
70/// #     };
71/// # }
72///
73/// unsafe impl ComputeTypeSet for Foo {
74///     type Output<T: ExpandTypeSet> = tset![u8, u16];
75/// }
76/// ```
77///
78/// Note that to you implement [`ComputeTypeSet`] you must also implement
79/// [`crate::TypeLayout`] for it.
80pub unsafe trait ComputeTypeSet: crate::TypeLayout {
81    /// Extend the set `T` into a (larger) set containing also the types this
82    /// type links to.
83    ///
84    /// Enums implementing [`crate::TypeLayout`] and [`ComputeTypeSet`]
85    /// manually should include [`crate::ExtractDiscriminant::Discriminant`] in
86    /// their [`ComputeTypeSet::Output`] using the [`tset`] helper macro.
87    type Output<T: ExpandTypeSet>: ExpandTypeSet;
88}
89
90/// Helper macro to expand a list of types, e.g. `H, R1, R2`, and an optional
91/// tail, `.. @ T`, into a set of types.
92///
93/// This macro is used when implementing the [`ComputeTypeSet::Output`]
94/// associated type to specify the list of types a type links to.
95pub macro tset {
96    () => { private::Empty },
97    (.. @ $T:tt) => { $T },
98    ($H:ty $(, $R:ty)*) => {
99        Set<$H, tset![$($R),*]>
100    },
101    ($H:ty, $($R:ty,)* .. @ $T:ty ) => {
102        Set<$H, tset![$($R,)* .. @ $T]>
103    },
104}
105
106#[doc(hidden)]
107pub trait ExpandTypeSet: ComputeSet {
108    type Output<T: ExpandTypeSet>: ExpandTypeSet;
109}
110
111impl ExpandTypeSet for private::Empty {
112    type Output<T: ExpandTypeSet> = T;
113}
114
115impl<H: ComputeTypeSet, T: ExpandTypeSet> ExpandTypeSet for private::Cons<H, T> {
116    type Output<R: ExpandTypeSet> =
117        <T as ExpandTypeSet>::Output<Set<H, <H as ComputeTypeSet>::Output<R>>>;
118}
119
120#[doc(hidden)]
121pub trait TypeSetFixedPoint: ExpandTypeSet {
122    type Output: ExpandTypeSet;
123}
124
125impl<T: ExpandTypeSet> TypeSetFixedPoint for T {
126    type Output = <T as private::ComputeTypeSetFixedPoint<
127        <T as ExpandTypeSet>::Output<private::Empty>,
128    >>::Output;
129}
130
131mod private {
132    use super::{sealed, ComputeSet, ComputeTypeSet, ExpandTypeSet, Set};
133
134    #[repr(C)]
135    #[derive(Copy, Clone)]
136    pub struct Empty;
137
138    #[repr(C)]
139    #[derive(Copy, Clone)]
140    pub struct Cons<H, T> {
141        head: H,
142        tail: T,
143    }
144
145    impl ComputeSet for Empty {
146        type Output<H: ComputeTypeSet> = Cons<H, Self>;
147        type TyHList = Self;
148
149        const LEN: usize = 0;
150        const TYS: &'static Self::TyHList = &Self;
151    }
152
153    impl<H2: ComputeTypeSet, T: ExpandTypeSet> ComputeSet for Cons<H2, T> {
154        type Output<H1: ComputeTypeSet> = <Self as ComputeCons<H1>>::Output;
155        type TyHList = Cons<&'static crate::TypeLayoutInfo<'static>, T::TyHList>;
156
157        const LEN: usize = T::LEN + 1;
158        const TYS: &'static Self::TyHList = &Cons {
159            head: &H2::TYPE_LAYOUT,
160            tail: *T::TYS,
161        };
162    }
163
164    pub trait ComputeCons<H: ComputeTypeSet>: sealed::ComputeSet {
165        type Output: ExpandTypeSet;
166    }
167
168    impl<H: ComputeTypeSet> ComputeCons<H> for Empty {
169        type Output = Cons<H, Self>;
170    }
171
172    impl<H: ComputeTypeSet, T: ExpandTypeSet> ComputeCons<H> for Cons<H, T> {
173        type Output = Self;
174    }
175
176    impl<H1: ComputeTypeSet, H2: ComputeTypeSet, T: ExpandTypeSet> ComputeCons<H1> for Cons<H2, T> {
177        default type Output = Cons<H2, Set<H1, T>>;
178    }
179
180    pub trait ComputeTypeSetFixedPoint<E: ExpandTypeSet>: ExpandTypeSet {
181        type Output: ExpandTypeSet;
182    }
183
184    impl<T: ExpandTypeSet, E: ExpandTypeSet> ComputeTypeSetFixedPoint<E> for T {
185        default type Output = <E as ComputeTypeSetFixedPoint<<E as ExpandTypeSet>::Output<Empty>>>::Output;
186    }
187
188    trait True {}
189    struct Assert<const ASSERT: bool>;
190    impl True for Assert<true> {}
191
192    impl<T: ExpandTypeSet, E: ExpandTypeSet> ComputeTypeSetFixedPoint<E> for T
193    where
194        Assert<{ T::LEN == E::LEN }>: True,
195    {
196        type Output = T;
197    }
198}
199
200pub(super) type TypeSet<T> =
201    <Set<T, <T as ComputeTypeSet>::Output<private::Empty>> as TypeSetFixedPoint>::Output;