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}