1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
//! A [`Scoped`] [`Identifier`]
//!
//! This works on two different principles.
//!
//! The first is that HRTB (higher rank type bounds) produces
//! fresh lifetimes on each call, and they can't be unified
//! against each other across multiple calls to `Scoped::with`.
//!
//! This allows [`Scoped::with`] to prevent the following at
//! compile time:
//! ```rust,compile_fail
//! # use pui_core::Scoped;
//! Scoped::with(|a: Scoped<'_>| {
//!     Scoped::with(|b: Scoped<'_>| {
//!         assert_eq!(a, b);
//!     })
//! })
//! ```
//!
//! This means that all `Scoped` types created with `Scoped::with`
//! are guaranteed to be unique! If you can't compare them, then
//! they can never compare equal.
//!
//! The second principle is based off of
//! [`generativity`](https://crates.io/crates/generativity)
//! where macros are used to generate the boilerplate to create
//! unique lifetimes. You can read about the details in `generativity`'s
//! documentation.
//!
//! This allows [`scope`] to produce unique `Scoped`s.
//!
//! ```rust,compile_fail
//! # use pui_core::scope;
//! scope!(a);
//! scope!(b);
//! assert_eq!(a, b);
//! ```
//!
//! ## How to decide which to use
//!
//! `Scoped::with` generally produces better error messages, and tends
//! to be easier to reason about. So it should be the first choice.
//! However, if you are using some strange control flow or need early returns,
//! and it would be easier to embed a `Scoped` directly in the current scope,
//! then you should use the `scope` macro.
//!

use core::marker::PhantomData;

use crate::{Identifier, Token};

/// Create a new [`Scoped<'_>`](Scoped) with the given name
/// without using a closure
///
/// ```rust
/// pui_core::scope!(scope);
/// ```
///
/// Two calls to `scope` will never return the same type
/// (the difference is in the lifetimes)
///
/// ```rust, compile_fail
/// pui_core::scope!(a);
/// pui_core::scope!(b);
/// assert_eq!(a, b);
/// ```
///
/// ```rust
/// pui_core::scope!(a);
/// assert_eq!(a, a);
/// ```
#[macro_export]
macro_rules! scope {
    ($scope:ident) => {
        let $scope = unsafe { $crate::scoped::Scoped::new_unchecked() };

        let scope = ();
        let assert_unique_scope = $crate::scoped::export::AssertUniqueScope::new(&$scope);
        assert_unique_scope.bind(&scope);
    };
}

#[doc(hidden)]
pub mod export {
    use core::marker::PhantomData;

    pub struct AssertUniqueScope<'a>(PhantomData<super::Invariant<'a>>);

    impl Drop for AssertUniqueScope<'_> {
        #[inline(always)]
        fn drop(&mut self) {}
    }

    impl<'a> AssertUniqueScope<'a> {
        #[inline(always)]
        pub fn new(_: &super::Scoped<'a>) -> Self { Self(PhantomData) }

        #[inline(always)]
        pub fn bind(&self, _: &'a ()) {}
    }
}

struct Invariant<'a>(fn() -> *mut &'a ());

/// A scoped [`Identifier`]
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Scoped<'a> {
    invariant: PhantomData<Invariant<'a>>,
}

/// The [`Trivial`](crate::Trivial) [`Token`] generated by [`Scoped`]
#[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ScopedToken<'a> {
    invariant: PhantomData<Invariant<'a>>,
}

impl<'scope> Scoped<'scope> {
    #[doc(hidden)]
    #[inline(always)]
    pub unsafe fn new_unchecked() -> Self { Self { invariant: PhantomData } }

    /// Call the given closure with a new `Scoped`
    #[inline]
    pub fn with<R, F: FnOnce(Scoped<'_>) -> R>(f: F) -> R { f(unsafe { Self::new_unchecked() }) }

    /// Create a new scoped token
    pub const fn token(&self) -> ScopedToken<'scope> { ScopedToken::new() }
}

impl ScopedToken<'_> {
    /// Create a new scoped token
    #[inline]
    pub const fn new() -> Self { Self { invariant: PhantomData } }
}

impl crate::Init for ScopedToken<'_> {
    const INIT: Self = Self { invariant: PhantomData };
}

impl crate::Trivial for ScopedToken<'_> {}
unsafe impl Token for ScopedToken<'_> {}
unsafe impl crate::OneShotIdentifier for Scoped<'_> {}
unsafe impl<'a> Identifier for Scoped<'a> {
    type Token = ScopedToken<'a>;

    #[inline]
    fn owns_token(&self, _: &Self::Token) -> bool { true }

    #[inline]
    fn token(&self) -> Self::Token { ScopedToken::new() }
}

use core::fmt;

impl fmt::Debug for Scoped<'_> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Scoped").finish() }
}

impl fmt::Debug for ScopedToken<'_> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("ScopedToken").finish() }
}