1#![warn(missing_docs, missing_debug_implementations)]
5#![no_std]
6
7use {
8 core::{
9 fmt,
10 hash::{self, Hash},
11 hint::unreachable_unchecked,
12 marker::PhantomData,
13 mem::ManuallyDrop,
14 ops::Deref,
15 ptr,
16 },
17 erasable::{ErasablePtr, ErasedPtr},
18};
19
20const MASK_2: usize = 0b0001;
21const MASK_4: usize = 0b0011;
22const MASK_8: usize = 0b0111;
23const MASK_16: usize = 0b1111;
24const TAG_A: usize = 0b0000;
25const TAG_B: usize = 0b0001;
26const TAG_C: usize = 0b0010;
27const TAG_D: usize = 0b0011;
28const TAG_E: usize = 0b0100;
29const TAG_F: usize = 0b0101;
30const TAG_G: usize = 0b0110;
31const TAG_H: usize = 0b0111;
32const TAG_I: usize = 0b1000;
33const TAG_J: usize = 0b1001;
34const TAG_K: usize = 0b1010;
35const TAG_L: usize = 0b1011;
36const TAG_M: usize = 0b1100;
37const TAG_N: usize = 0b1101;
38const TAG_O: usize = 0b1110;
39const TAG_P: usize = 0b1111;
40
41fn ptr_addr<T>(this: *mut T) -> usize {
42 #[cfg(not(has_strict_provenance))]
43 {
44 this as usize
45 }
46 #[cfg(has_strict_provenance)]
47 {
48 this.addr()
49 }
50}
51
52#[cfg(not(has_strict_provenance))]
53fn ptr_with_addr<T>(this: *mut T, addr: usize) -> *mut T {
54 let this_addr = ptr_addr(this) as isize;
60 let dest_addr = addr as isize;
61 let offset = dest_addr.wrapping_sub(this_addr);
62
63 this.cast::<u8>().wrapping_offset(offset).cast::<T>()
65}
66
67fn ptr_map_addr<T>(this: *mut T, f: impl FnOnce(usize) -> usize) -> *mut T {
68 #[cfg(not(has_strict_provenance))]
69 {
70 ptr_with_addr(this, f(ptr_addr(this)))
71 }
72 #[cfg(has_strict_provenance)]
73 {
74 this.map_addr(f)
75 }
76}
77
78fn ptr_tag<T>(this: *mut T, tag: usize) -> *mut T {
79 ptr_map_addr(this, |addr| addr | tag)
80}
81
82fn ptr_mask<T>(this: *mut T, mask: usize) -> *mut T {
83 ptr_map_addr(this, |addr| addr & mask)
84}
85
86#[inline(always)]
87fn check_tag(ptr: ErasedPtr, mask: usize, tag: usize) -> bool {
88 debug_assert_eq!(tag & mask, tag);
89 ptr_addr(ptr_mask(ptr.as_ptr(), mask)) == tag
90}
91
92#[inline(always)]
93fn set_tag(ptr: ErasedPtr, mask: usize, tag: usize) -> ErasedPtr {
94 debug_assert_eq!(tag & mask, tag);
95 debug_assert!(check_tag(ptr, mask, 0));
96 unsafe { ErasedPtr::new_unchecked(ptr_tag(ptr.as_ptr(), tag)) }
97}
98
99#[inline(always)]
100fn unset_tag(ptr: ErasedPtr, mask: usize, tag: usize) -> ErasedPtr {
101 debug_assert_eq!(tag & mask, tag);
102 debug_assert!(check_tag(ptr, mask, tag));
103 unsafe { ErasedPtr::new_unchecked(ptr_mask(ptr.as_ptr(), !mask)) }
104}
105
106#[inline(always)]
107fn unset_any_tag(ptr: ErasedPtr, mask: usize) -> ErasedPtr {
108 unsafe { ErasedPtr::new_unchecked(ptr_mask(ptr.as_ptr(), !mask)) }
109}
110
111#[cfg(has_never)]
112pub type NeverPtr = !;
113#[cfg(not(has_never))]
114use never_ptr::NeverPtr;
115#[cfg(not(has_never))]
116mod never_ptr {
117 use super::*;
118 #[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
119 pub enum NeverPtr {}
120 unsafe impl ErasablePtr for NeverPtr {
121 #[inline]
122 fn erase(this: Self) -> ErasedPtr {
123 match this {}
124 }
125 #[inline]
126 unsafe fn unerase(_this: ErasedPtr) -> Self {
127 unreachable!()
128 }
129 }
130}
131
132pub struct Union2<A: ErasablePtr, B: ErasablePtr> {
141 raw: ErasedPtr,
142 phantom: PhantomData<Enum2<A, B>>,
143}
144
145pub struct Union4<A: ErasablePtr, B: ErasablePtr, C: ErasablePtr, D: ErasablePtr = NeverPtr> {
158 raw: ErasedPtr,
159 phantom: PhantomData<Enum4<A, B, C, D>>,
160}
161
162pub struct Union8<
175 A: ErasablePtr,
176 B: ErasablePtr,
177 C: ErasablePtr,
178 D: ErasablePtr,
179 E: ErasablePtr,
180 F: ErasablePtr = NeverPtr,
181 G: ErasablePtr = NeverPtr,
182 H: ErasablePtr = NeverPtr,
183> {
184 raw: ErasedPtr,
185 #[allow(clippy::type_complexity)]
186 phantom: PhantomData<Enum8<A, B, C, D, E, F, G, H>>,
187}
188
189pub struct Union16<
202 A: ErasablePtr,
203 B: ErasablePtr,
204 C: ErasablePtr,
205 D: ErasablePtr,
206 E: ErasablePtr,
207 F: ErasablePtr,
208 G: ErasablePtr,
209 H: ErasablePtr,
210 I: ErasablePtr,
211 J: ErasablePtr = NeverPtr,
212 K: ErasablePtr = NeverPtr,
213 L: ErasablePtr = NeverPtr,
214 M: ErasablePtr = NeverPtr,
215 N: ErasablePtr = NeverPtr,
216 O: ErasablePtr = NeverPtr,
217 P: ErasablePtr = NeverPtr,
218> {
219 raw: ErasedPtr,
220 #[allow(clippy::type_complexity)]
221 phantom: PhantomData<Enum16<A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P>>,
222}
223
224#[allow(missing_docs)]
226#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
227pub enum Enum2<A, B> {
228 A(A),
229 B(B),
230}
231
232#[allow(missing_docs)]
234#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
235pub enum Enum4<A, B, C, D> {
236 A(A),
237 B(B),
238 C(C),
239 D(D),
240}
241
242#[allow(missing_docs)]
244#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
245pub enum Enum8<A, B, C, D, E, F, G, H> {
246 A(A),
247 B(B),
248 C(C),
249 D(D),
250 E(E),
251 F(F),
252 G(G),
253 H(H),
254}
255
256#[allow(missing_docs)]
258#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
259pub enum Enum16<A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P> {
260 A(A),
261 B(B),
262 C(C),
263 D(D),
264 E(E),
265 F(F),
266 G(G),
267 H(H),
268 I(I),
269 J(J),
270 K(K),
271 L(L),
272 M(M),
273 N(N),
274 O(O),
275 P(P),
276}
277
278pub struct Builder2<A, B> {
283 phantom: PhantomData<Enum2<A, B>>,
284}
285
286pub struct Builder4<A, B, C, D = NeverPtr> {
291 phantom: PhantomData<Enum4<A, B, C, D>>,
292}
293
294pub struct Builder8<A, B, C, D, E, F = NeverPtr, G = NeverPtr, H = NeverPtr> {
299 #[allow(clippy::type_complexity)]
300 phantom: PhantomData<Enum8<A, B, C, D, E, F, G, H>>,
301}
302
303pub struct Builder16<
308 A,
309 B,
310 C,
311 D,
312 E,
313 F = NeverPtr,
314 G = NeverPtr,
315 H = NeverPtr,
316 I = NeverPtr,
317 J = NeverPtr,
318 K = NeverPtr,
319 L = NeverPtr,
320 M = NeverPtr,
321 N = NeverPtr,
322 O = NeverPtr,
323 P = NeverPtr,
324> {
325 #[allow(clippy::type_complexity)]
326 phantom: PhantomData<Enum16<A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P>>,
327}
328
329macro_rules! impl_builder {
330 ($UnionName:ident $Union:ty, $BuilderName:ident $Builder:ty: $mask:ident $([$a:ident $A:ident])*) => {
331 impl<$($A),*> $Builder {
332 pub const unsafe fn new_unchecked() -> Self {
339 Self { phantom: PhantomData }
340 }
341 }
342
343 impl<$($A: ErasablePtr),*> $Builder {
344 paste::paste! {
345 $(
346 pub fn $a(self, this: $A) -> $Union {
348 $UnionName {
349 raw: set_tag($A::erase(this), $mask, [<TAG_ $A>]),
350 phantom: PhantomData,
351 }
352 }
353 )*
354 }
355 }
356
357 impl<$($A),*> Copy for $Builder {}
358 impl<$($A),*> Clone for $Builder {
359 fn clone(&self) -> Self {
360 *self
361 }
362 }
363 };
364}
365
366macro_rules! impl_union {
367 ($Union:ident, $Enum:ident, $Builder:ident: $mask:ident $([$a:ident $A:ident])*) => {
368 impl_builder!($Union $Union<$($A),*>, $Builder $Builder<$($A),*>: $mask $([$a $A])*);
369
370 impl<$($A: ErasablePtr),*> $Union<$($A),*> {
371 paste::paste! {
372 $(
373 pub fn [<new_ $a>]($a: $A) -> Result<Self, $A> {
375 let $a = $A::erase($a);
376 if check_tag($a, $mask, 0) {
377 Ok($Union {
378 raw: set_tag($a, $mask, [<TAG_ $A>]),
379 phantom: PhantomData,
380 })
381 } else {
382 Err(unsafe { $A::unerase($a) })
383 }
384 }
385
386 pub fn [<is_ $a>](&self) -> bool {
388 check_tag(self.raw, $mask, [<TAG_ $A>])
389 }
390
391 pub fn [<into_ $a>](self) -> Result<$A, Self> {
395 if self.[<is_ $a>]() {
396 let this = ManuallyDrop::new(self);
397 unsafe { Ok($A::unerase(unset_tag(this.raw, $mask, [<TAG_ $A>]))) }
398 } else {
399 Err(self)
400 }
401 }
402
403 pub fn [<with_ $a>]<R>(&self, f: impl FnOnce(&$A) -> R) -> Option<R> {
405 if self.[<is_ $a>]() {
406 unsafe {
407 let this = ManuallyDrop::new($A::unerase(unset_tag(self.raw, $mask, [<TAG_ $A>])));
408 Some(f(&this))
409 }
410 } else {
411 None
412 }
413 }
414
415 pub fn $a(&self) -> Option<&$A::Target>
417 where $A: Deref
418 {
419 self.[<with_ $a>](|this| unsafe { erase_lt(&**this) })
420 }
421
422 pub fn [<clone_ $a>](&self) -> Option<$A>
424 where $A: Clone
425 {
426 self.[<with_ $a>](|this| this.clone())
427 }
428
429 pub fn [<copy_ $a>](&self) -> Option<$A>
431 where $A: Copy
432 {
433 self.[<with_ $a>](|this| *this)
434 }
435 )*
436
437 pub fn unpack(self) -> $Enum<$($A),*> {
439 Err(self)
440 $(.or_else(|this| this.[<into_ $a>]().map($Enum::$A)))*
441 .unwrap_or_else(|_| unsafe { unreachable_unchecked() })
442 }
443 }
444
445 pub fn ptr_eq(&self, other: &Self) -> bool {
448 self.raw == other.raw
449 }
450
451 pub fn as_deref<'a>(
453 &'a self,
454 builder: $Builder<$(&'a $A::Target),*>
455 ) -> $Union<$(&'a $A::Target),*>
456 where
457 $($A: Deref,)*
458 $(&'a $A::Target: ErasablePtr,)*
459 {
460 $(if let Some(this) = self.$a() {
461 builder.$a(this)
462 } else)* {
463 unsafe { unreachable_unchecked() }
464 }
465 }
466
467 pub unsafe fn as_deref_unchecked<'a>(&'a self) -> $Union<$(&'a $A::Target),*>
475 where
476 $($A: Deref,)*
477 $(&'a $A::Target: ErasablePtr,)*
478 {
479 self.as_deref($Builder::new_unchecked())
480 }
481
482 pub fn as_untagged_ptr(&self) -> ErasedPtr {
484 unset_any_tag(self.raw, $mask)
485 }
486
487 paste::paste! {
488 pub fn try_deref<'a>(&'a self) -> Option<$Union<$(&'a $A::Target),*>>
492 where
493 $($A: Deref,)*
494 $(&'a $A::Target: ErasablePtr,)*
495 {
496 $(if let Some(this) = self.$a() {
497 $Union::[<new_ $a>](this).ok()
498 } else)* {
499 None
500 }
501 }
502 }
503 }
504
505 impl<$($A: ErasablePtr),*> $Enum<$($A),*> {
506 pub fn pack(self, builder: $Builder<$($A),*>) -> $Union<$($A),*> {
508 match self {
509 $($Enum::$A(this) => builder.$a(this),)*
510 }
511 }
512
513 paste::paste! {
514 pub fn try_pack(self) -> Result<$Union<$($A),*>, Self> {
516 match self {
517 $($Enum::$A(this) => $Union::[<new_ $a>](this).map_err(Self::$A),)*
518 }
519 }
520 }
521
522 pub unsafe fn pack_unchecked(self) -> $Union<$($A),*> {
530 self.pack($Builder::new_unchecked())
531 }
532 }
533
534 unsafe impl<$($A: ErasablePtr),*> ErasablePtr for $Union<$($A),*> {
535 fn erase(this: Self) -> ErasedPtr {
536 ManuallyDrop::new(this).raw
537 }
538
539 unsafe fn unerase(this: ErasedPtr) -> Self {
540 Self {
541 raw: this,
542 phantom: PhantomData,
543 }
544 }
545 }
546
547 impl<$($A: ErasablePtr),*> Drop for $Union<$($A),*> {
548 fn drop(&mut self) {
549 unsafe { drop(ptr::read(self).unpack()) }
550 }
551 }
552
553 impl<$($A: ErasablePtr),*> fmt::Debug for $Union<$($A),*>
554 where $($A: fmt::Debug),*
555 {
556 paste::paste! {
557 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
558 None
559 $(.or_else(|| self.[<with_ $a>](|this| f
560 .debug_tuple(stringify!($A))
561 .field(this)
562 .finish()
563 )))*
564 .unwrap_or_else(|| unsafe { unreachable_unchecked() })
565 }
566 }
567 }
568
569 impl<$($A: ErasablePtr),*> Clone for $Union<$($A),*>
570 where $($A: Clone),*
571 {
572 paste::paste! {
573 fn clone(&self) -> Self {
574 #[cold]
575 #[inline(never)]
576 fn clone_error<A>() -> ! {
577 panic!("Tried to clone {} in a {}, but the cloned pointer wasn't sufficiently aligned", core::any::type_name::<A>(), stringify!($Union))
578 }
579
580 None
581 $(.or_else(|| self.[<clone_ $a>]().map(|this| Self::[<new_ $a>](this).unwrap_or_else(|_| clone_error::<$A>()))))*
582 .unwrap_or_else(|| unsafe { unreachable_unchecked() })
583 }
584 }
585 }
586
587 impl<$($A: ErasablePtr,)*> Eq for $Union<$($A),*> where $($A: Eq,)* {}
588 impl<$($A: ErasablePtr),*> PartialEq for $Union<$($A),*>
589 where $($A: PartialEq),*
590 {
591 paste::paste! {
592 fn eq(&self, other: &Self) -> bool {
593 None
594 $(.or_else(|| self.[<with_ $a>](|this|
595 other.[<with_ $a>](|that|
596 this == that
597 ).unwrap_or(false)
598 )))*
599 .unwrap_or(false)
600 }
601 }
602 }
603
604 impl<$($A: ErasablePtr,)*> Hash for $Union<$($A),*>
605 where $($A: Hash),*
606 {
607 paste::paste! {
608 fn hash<Hasher>(&self, state: &mut Hasher)
609 where Hasher: hash::Hasher
610 {
611 None
612 $(.or_else(|| self.[<with_ $a>](|this| this.hash(state))))*
613 .unwrap_or_else(|| unsafe { unreachable_unchecked() })
614 }
615 }
616 }
617
618 unsafe impl<$($A: ErasablePtr,)*> Send for $Union<$($A),*> where $($A: Send),* {}
619 unsafe impl<$($A: ErasablePtr,)*> Sync for $Union<$($A),*> where $($A: Sync),* {}
620 };
621}
622
623impl_union!(Union2, Enum2, Builder2: MASK_2 [a A] [b B]);
624impl_union!(Union4, Enum4, Builder4: MASK_4 [a A] [b B] [c C] [d D]);
625impl_union!(Union8, Enum8, Builder8: MASK_8 [a A] [b B] [c C] [d D] [e E] [f F] [g G] [h H]);
626impl_union!(Union16, Enum16, Builder16: MASK_16 [a A] [b B] [c C] [d D] [e E] [f F] [g G] [h H] [i I] [j J] [k K] [l L] [m M] [n N] [o O] [p P]);
627
628impl<A, B> fmt::Debug for Builder2<A, B> {
629 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
630 f.debug_tuple("Builder2")
631 .field(&format_args!(
632 "Union2<{}, {}>",
633 core::any::type_name::<A>(),
634 core::any::type_name::<B>(),
635 ))
636 .finish()
637 }
638}
639
640impl<A, B, C, D> fmt::Debug for Builder4<A, B, C, D> {
641 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
642 f.debug_tuple("Builder4")
643 .field(&format_args!(
644 "Union4<{}, {}, {}, {}>",
645 core::any::type_name::<A>(),
646 core::any::type_name::<B>(),
647 core::any::type_name::<C>(),
648 core::any::type_name::<D>(),
649 ))
650 .finish()
651 }
652}
653
654impl<A, B, C, D, E, F, G, H> fmt::Debug for Builder8<A, B, C, D, E, F, G, H> {
655 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
656 f.debug_tuple("Builder8")
657 .field(&format_args!(
658 "Union8<{}, {}, {}, {}, {}, {}, {}, {},>",
659 core::any::type_name::<A>(),
660 core::any::type_name::<B>(),
661 core::any::type_name::<C>(),
662 core::any::type_name::<D>(),
663 core::any::type_name::<E>(),
664 core::any::type_name::<F>(),
665 core::any::type_name::<G>(),
666 core::any::type_name::<H>(),
667 ))
668 .finish()
669 }
670}
671
672impl<A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P> fmt::Debug
673 for Builder16<A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P>
674{
675 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
676 f.debug_tuple("Builder16")
677 .field(&format_args!(
678 "Union16<{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}>",
679 core::any::type_name::<A>(),
680 core::any::type_name::<B>(),
681 core::any::type_name::<C>(),
682 core::any::type_name::<D>(),
683 core::any::type_name::<E>(),
684 core::any::type_name::<F>(),
685 core::any::type_name::<G>(),
686 core::any::type_name::<H>(),
687 core::any::type_name::<I>(),
688 core::any::type_name::<J>(),
689 core::any::type_name::<K>(),
690 core::any::type_name::<L>(),
691 core::any::type_name::<M>(),
692 core::any::type_name::<N>(),
693 core::any::type_name::<O>(),
694 core::any::type_name::<P>(),
695 ))
696 .finish()
697 }
698}
699
700#[allow(clippy::needless_lifetimes)]
701unsafe fn erase_lt<'a, 'b, T: ?Sized>(r: &'a T) -> &'b T {
702 &*(r as *const T)
703}