dungeon_cell/
bound.rs

1//! Type encoded dynamic trait bounds.
2//!
3//! [`dungeon_cell`][crate] uses generic types for configuration.
4//! Part of this configuration is what traits a type stored in a
5//! [`dungeon_cell`][crate] type require.
6//!
7//! An example is wanting a [`DungeonCore`][crate::DungeonCore] to
8//! be [`Send`][std::marker::Send]. To allow this, all types the `DungeonCore` can store
9//! must implement `Send`. This is expressed using the following
10//! trait bound. The `bounds::Send` would be part of the `DungeonCore`'s
11//! generics.
12//! ```ignore
13//! where
14//!     T: Dynamic<bounds::Send>
15//! ```
16//!
17//! See the [`traits`] module for the supported traits.
18//! Only a small collection of traits are supported because of how the encoding has to be setup for
19//! rustc to understand it. The chosen traits are all from `core` and are the most commonly used
20//! and implemented.
21//!
22//! # Subtraiting and Variance
23//!
24//! In Rust, a trait bound is of the form `T: X + Y + Z` which means the type
25//! `T` must implement traits `X`, `Y`, and `Z`. It doesn't say those are
26//! the only traits that type `T` implements. Additionally, there isn't a way
27//! in stable Rust to bound on a type `T` **not** implementing a type.
28//!
29//! This module encodes trait bounds as types. Because of this encoding we
30//! can ask questions about the trait bound in the type system. The four questions
31//! we can ask about a bound `A` are:
32//! - is the bound `B` the same as `A`,
33//! - is the bound `B` a subset of `A`,
34//! - is the bound `B` a superset of `A`,
35//! - is the bound `B` disjoint from `A`.
36//!
37//! The first question is provided by `B: Equal<A>` and simply checks that `A`
38//! and `B` are the same type.
39//!
40//! For example of the second question, given the bound `A` of the form `X + Y + Z`,
41//! and bound `B` of the form `X + Z`, the bound `B` is a subset of `A`. A subset bound
42//! is a bound that requires **less** when used to bound a type. The `B: Subset<A>` trait bound
43//! allows checking this.
44//!
45//! For example of the third question, given the bound `A` of the form `X + Y`,
46//! and bound `B` of the form `X + Y + Z`, the bound `B` is a superset of `A`.
47//! A subset bound is a bound that requires **more** when used to bound a type.
48//! In reality this is the inverse of the subset question so if we switch `A` and `B`
49//! we get: is `A` a subset of `B`.
50//!
51//! For example of the forth question, given the bound `A` of the form `X + Y`,
52//! and bound `B` of the form `Z`, the bound `B` is disjoint from `A`.
53//! A disjoint bound is a bound that requires **different** functionality when
54//! used to bound a type. The `B: Disjoint<A>` trait allows checking this.
55
56mod disjoint;
57mod equal;
58mod subset;
59
60use core::fmt::Formatter;
61use core::marker::PhantomData;
62
63use crate::marker_traits::IsBound;
64
65pub use disjoint::Disjoint;
66pub use equal::Equal;
67pub use subset::Subset;
68
69/// Markers for traits that can be used in a dynamic trait bound.
70///
71/// See the [`bounds`] module for a collection pf predefined bounds
72/// built from these markers.
73///
74/// The supported traits are:
75/// - [`Send`][std::marker::Send]
76/// - [`Sync`][std::marker::Sync]
77/// - [`Copy`][std::marker::Copy]
78/// - [`Clone`][std::clone::Clone]
79/// - [`Unpin`][std::marker::Unpin]
80/// - [`Debug`][std::fmt::Debug]
81///
82/// The special marker `__` signifies that a trait is **not** included in the dynamic bound.
83pub mod traits {
84    /// Marker for a trait not being bounded on.
85    #[derive(Copy, Clone, Debug)]
86    pub enum __ {}
87
88    /// Marker for `Send`.
89    #[derive(Copy, Clone, Debug)]
90    pub enum Send {}
91
92    /// Marker for `Sync`.
93    #[derive(Copy, Clone, Debug)]
94    pub enum Sync {}
95
96    /// Marker for `Copy`.
97    #[derive(Copy, Clone, Debug)]
98    pub enum Copy {}
99
100    /// Marker for `Clone`.
101    #[derive(Copy, Clone, Debug)]
102    pub enum Clone {}
103
104    /// Marker for `Unpin`.
105    #[derive(Copy, Clone, Debug)]
106    pub enum Unpin {}
107
108    /// Marker for `Debug`.
109    #[derive(Copy, Clone, Debug)]
110    pub enum Debug {}
111}
112
113/// Predefined dynamic trait bounds.
114///
115/// A custom trait bound can be made by defining a type alias
116/// and using [`Bound`] along with the markers in the [`traits`] module.
117/// The order of the markers needs to match the definition of [`Bound`].
118/// ```
119/// use dungeon_cell::bound::{Bound, traits};
120/// use dungeon_cell::bound::traits::__;
121///
122/// type MyBound = Bound<traits::Send, __, traits::Copy, __, __, __>;
123/// ```
124pub mod bounds {
125    use super::traits::__;
126    use super::{traits, Bound};
127
128    /// No bounds.
129    ///
130    /// # Examples
131    /// ```
132    /// use dungeon_cell::bound::bounds::Empty;
133    /// use dungeon_cell::bound::Dynamic;
134    /// use dungeon_cell::marker_traits::IsBound;
135    ///
136    /// fn test<T: Dynamic<B>, B: IsBound>() {}
137    ///
138    /// // all types can be bounded by Empty
139    /// test::<(), Empty>();
140    /// test::<i32, Empty>();
141    /// test::<String, Empty>();
142    /// ```
143    pub type Empty = Bound<__, __, __, __, __, __>;
144
145    /// Bound for [`Send`][std::marker::Send].
146    ///
147    /// # Examples
148    /// ```
149    /// use dungeon_cell::bound::bounds::Send;
150    /// use dungeon_cell::bound::Dynamic;
151    /// use dungeon_cell::marker_traits::IsBound;
152    ///
153    /// fn test<T: Dynamic<B>, B: IsBound>() {}
154    ///
155    /// test::<(), Send>();
156    /// test::<i32, Send>();
157    /// test::<String, Send>();
158    /// ```
159    pub type Send = Bound<traits::Send, __, __, __, __, __>;
160
161    /// Bound for [`Sync`][std::marker::Sync].
162    ///
163    /// # Examples
164    /// ```
165    /// use dungeon_cell::bound::bounds::Sync;
166    /// use dungeon_cell::bound::Dynamic;
167    /// use dungeon_cell::marker_traits::IsBound;
168    ///
169    /// fn test<T: Dynamic<B>, B: IsBound>() {}
170    ///
171    /// test::<(), Sync>();
172    /// test::<i32, Sync>();
173    /// test::<String, Sync>();
174    /// ```
175    pub type Sync = Bound<__, traits::Sync, __, __, __, __>;
176
177    /// Bound for [`Copy`][std::marker::Copy].
178    ///
179    /// # Examples
180    /// ```
181    /// use dungeon_cell::bound::bounds::Copy;
182    /// use dungeon_cell::bound::Dynamic;
183    /// use dungeon_cell::marker_traits::IsBound;
184    ///
185    /// fn test<T: Dynamic<B>, B: IsBound>() {}
186    ///
187    /// test::<(), Copy>();
188    /// test::<i32, Copy>();
189    /// test::<&str, Copy>();
190    /// ```
191    pub type Copy = Bound<__, __, traits::Copy, __, __, __>;
192
193    /// Bound for [`Clone`][std::clone::Clone].
194    ///
195    /// # Examples
196    /// ```
197    /// use dungeon_cell::bound::bounds::Clone;
198    /// use dungeon_cell::bound::Dynamic;
199    /// use dungeon_cell::marker_traits::IsBound;
200    ///
201    /// fn test<T: Dynamic<B>, B: IsBound>() {}
202    ///
203    /// test::<(), Clone>();
204    /// test::<i32, Clone>();
205    /// test::<String, Clone>();
206    /// ```
207    pub type Clone = Bound<__, __, __, traits::Clone, __, __>;
208
209    /// Bound for [`Unpin`][std::marker::Unpin].
210    ///
211    /// # Examples
212    /// ```
213    /// use dungeon_cell::bound::bounds::Unpin;
214    /// use dungeon_cell::bound::Dynamic;
215    /// use dungeon_cell::marker_traits::IsBound;
216    ///
217    /// fn test<T: Dynamic<B>, B: IsBound>() {}
218    ///
219    /// test::<(), Unpin>();
220    /// test::<i32, Unpin>();
221    /// test::<String, Unpin>();
222    /// ```
223    pub type Unpin = Bound<__, __, __, __, traits::Unpin, __>;
224
225    /// Bound for [`Debug`][std::fmt::Debug].
226    ///
227    /// # Examples
228    /// ```
229    /// use dungeon_cell::bound::bounds::Debug;
230    /// use dungeon_cell::bound::Dynamic;
231    /// use dungeon_cell::marker_traits::IsBound;
232    ///
233    /// fn test<T: Dynamic<B>, B: IsBound>() {}
234    ///
235    /// test::<(), Debug>();
236    /// test::<i32, Debug>();
237    /// test::<String, Debug>();
238    /// ```
239    pub type Debug = Bound<__, __, __, __, __, traits::Debug>;
240
241    /// Bound for `Sync + Send`.
242    pub type SyncSend = Bound<traits::Send, traits::Sync, __, __, __, __>;
243
244    /// Bound for `Send + Sync`.
245    pub type SendSync = SyncSend;
246
247    /// Bound for `Clone + Copy`.
248    pub type CloneCopy = Bound<__, __, traits::Copy, traits::Clone, __, __>;
249
250    /// Bound for `Copy + Clone`. Same as [`CopyClone`].
251    pub type CopyClone = CloneCopy;
252
253    /// Bound that most types satisfy `Send + Sync + Unpin + Debug`.
254    pub type Normal =
255        Bound<traits::Send, traits::Sync, __, __, traits::Unpin, traits::Debug>;
256
257    /// Bound for implementing the auto traits `Send + Sync + Unpin`.
258    pub type AutoTraits =
259        Bound<traits::Send, traits::Sync, __, __, traits::Unpin, __>;
260}
261
262/// Type encoded trait bound.
263///
264/// This is the type to use where a [`IsBound`] generic is required.
265///
266/// See the [`bounds`] module for a collection of predefined bounds.
267///
268/// Using this type in combination with [`Dynamic`] allows for applying a trait bound
269/// based on a generic type.
270/// ```
271/// use dungeon_cell::bound::{Dynamic, bounds};
272///
273/// fn test<T: Dynamic<bounds::Normal>>() {}
274///
275/// test::<String>();
276/// ```
277#[derive(Copy, Clone, Debug)]
278pub struct Bound<Send, Sync, Copy, Clone, Unpin, Debug> {
279    _phantom: PhantomData<(Send, Sync, Copy, Clone, Unpin, Debug)>,
280}
281
282/// Trait implemented when the bound given by `B` is satisfied by `Self`.
283///
284/// Use this trait in combination with [`Bound`] to apply a trait bound
285/// based on a generic type.
286/// ```compile_fail
287/// use dungeon_cell::bound::{Dynamic, bounds};
288/// use dungeon_cell::marker_traits::IsBound;
289///
290/// fn test<T: Dynamic<B>, B: IsBound>() {}
291///
292/// // this failes because String isn't Copy
293/// test::<String, bounds::Copy>();
294/// ```
295///
296/// The traits with functionality ([`Clone`][std::clone::Clone] and [`Debug`][std::fmt::Debug])
297/// have their implementation for the type available as [`Self::clone()`] and [`Self::debug()`]. If
298/// the type doesn't implement one or both of these traits then it's method will be implemented
299/// with [`unreachable_unchecked()`][std::hint::unreachable_unchecked].
300///
301/// This trait is [sealed](https://rust-lang.github.io/api-guidelines/future-proofing.html#sealed-traits-protect-against-downstream-implementations-c-sealed)
302/// and cannot be implemented outside of the implementations here.
303pub trait Dynamic<B: IsBound>: Sealed<B> {
304    /// Call the [`Clone::clone()`][std::clone::Clone::clone] implementation of [`Self`].
305    ///
306    /// This method is always safe to call because it's bounded by the bound including
307    /// `Clone`. To emulate specialization, use [`Self::clone_unchecked`] and check
308    /// the `B::BOUND_BY_CLONE` flag at runtime.
309    fn clone(this: &Self) -> Self
310    where
311        Self: Sized,
312        <B as IsBound>::CloneMarker: Clone,
313    {
314        // SAFETY: We have checked that B::CloneMarker: Clone.
315        unsafe { Self::clone_unchecked(this) }
316    }
317
318    /// Call the [`Debug::fmt()`][std::fmt::Debug::fmt] implementation of [`Self`].
319    ///
320    /// This method is always safe to call because it's bounded by the bound including
321    /// `Debug`. To emulate specialization, use [`Self::debug_unchecked`] and check
322    /// the `B::BOUND_BY_DEBUG` flag at runtime.
323    fn debug(this: &Self, f: &mut Formatter<'_>) -> Result<(), core::fmt::Error>
324    where
325        Self: Sized,
326        <B as IsBound>::DebugMarker: ::core::fmt::Debug,
327    {
328        // SAFETY: We have checked that B::DebugMarker: Debug.
329        unsafe { Self::debug_unchecked(this, f) }
330    }
331
332    /// Unsafe form of [`Self::clone()`].
333    ///
334    /// This method is only valid to call if `B` has `Clone` as part of it's bound.
335    ///
336    /// # Safety
337    /// This function must only be called if [`B::BOUND_BY_CLONE`][IsBound::BOUND_BY_CLONE] == `true` and/or [`B::CloneMarker`][IsBound::CloneMarker]: [`Clone`][std::clone::Clone].
338    unsafe fn clone_unchecked(this: &Self) -> Self
339    where
340        Self: Sized;
341
342    /// Unsafe form of [`Self::debug()`].
343    ///
344    /// This method is only valid to call if `B` has `Debug` as part of it's bound.
345    ///
346    /// # Safety
347    /// This function must only be called if [`B::BOUND_BY_DEBUG`][IsBound::BOUND_BY_DEBUG] == `true` and/or [`B::DebugMarker`][IsBound::DebugMarker]: [`Debug`][std::fmt::Debug].
348    unsafe fn debug_unchecked(
349        this: &Self,
350        f: &mut Formatter<'_>,
351    ) -> Result<(), core::fmt::Error>;
352}
353
354use sealed::Sealed;
355
356mod sealed {
357    use super::*;
358    use crate::unreachable;
359
360    pub trait Sealed<T> {}
361
362    impl<T> Sealed<bounds::Empty> for T {}
363    impl<T> Dynamic<bounds::Empty> for T {
364        unsafe fn clone_unchecked(_this: &Self) -> T {
365            // SAFETY: This method will not be called when `Self`
366            // isn't bound by `Clone`.
367            unsafe { unreachable() };
368        }
369
370        unsafe fn debug_unchecked(
371            _this: &Self,
372            _f: &mut Formatter<'_>,
373        ) -> Result<(), core::fmt::Error> {
374            // SAFETY: This method will not be called when `Self`
375            // isn't bound by `Debug`.
376            unsafe { unreachable() };
377        }
378    }
379
380    macro_rules! bound_impl {
381        (@send, __) => {};
382        (@sync, __) => {};
383        (@copy, __) => {};
384        (@unpin, __) => {};
385        (@send, Send) => {};
386        (@sync, Sync) => {};
387        (@copy, Copy) => {};
388        (@unpin, Unpin) => {};
389        (@clone, __) => {
390            unsafe fn clone_unchecked(_this: &Self) -> T {
391                // SAFETY: This method will not be called when `Self`
392                // isn't bound by `Clone`.
393                unsafe { unreachable() };
394            }
395        };
396        (@clone, Clone) => {
397            unsafe fn clone_unchecked(this: &Self) -> T {
398                Clone::clone(this)
399            }
400        };
401        (@debug, __) => {
402            unsafe fn debug_unchecked(
403                _this: &Self,
404                _f: &mut Formatter<'_>,
405            ) -> Result<(), core::fmt::Error> {
406                // SAFETY: This method will not be called when `Self`
407                // isn't bound by `Debug`.
408                unsafe { unreachable() };
409            }
410        };
411        (@debug, Debug) => {
412            unsafe fn debug_unchecked(
413                this: &Self,
414                f: &mut Formatter<'_>,
415            ) -> Result<(), core::fmt::Error> {
416                core::fmt::Debug::fmt(this, f)
417            }
418        };
419    }
420
421    macro_rules! powerset {
422        (
423            @helper
424            $empty:ident
425            []
426            [$($bounds:ident),*]
427        ) => {
428        };
429        (
430            @helper
431            $empty:ident
432            [$trait:ident $(, $traits:ident)*]
433            [$send:ident, $sync:ident, $copy:ident, $clone:ident, $unpin:ident, $debug:ident]
434        ) => {
435            impl<T: $trait $(+ $traits)*> Sealed<Bound<
436              traits::$send, traits::$sync, traits::$copy, traits::$clone, traits::$unpin, traits::$debug
437            >> for T {
438            }
439
440            impl<T: $trait $(+ $traits)*> Dynamic<Bound<
441              traits::$send, traits::$sync, traits::$copy, traits::$clone, traits::$unpin, traits::$debug
442            >> for T {
443                bound_impl!(@clone, $clone);
444                bound_impl!(@debug, $debug);
445                bound_impl!(@send, $send);
446                bound_impl!(@sync, $sync);
447                bound_impl!(@copy, $copy);
448                bound_impl!(@unpin, $unpin);
449            }
450        };
451        // @helper [traitB, traitD] [(boundA, false), (boundB, true), (boundC, false), (boundD, true)] bound => trait, ...
452        (
453            @helper
454            $empty:ident
455            [$($trait:ident),*]
456            [$($bound:ident),*]
457            $next_trait:ident
458            $(, $traits:ident)*
459        ) => {
460            powerset!(@helper $empty [$($trait),*] [$($bound,)* $empty] $($traits),*);
461            powerset!(@helper $empty [$($trait,)* $next_trait] [$($bound,)* $next_trait] $($traits),*);
462        };
463        ($empty:ident, $($trait:ident),*) => {
464            powerset!(@helper $empty [] [] $($trait),*);
465        };
466    }
467
468    use core::fmt::Debug;
469
470    powerset![__, Send, Sync, Copy, Clone, Unpin, Debug];
471}