Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 55 additions & 25 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
#![cfg_attr(not(test), no_std)]

//! Create a trusted carrier with a new lifetime that is guaranteed to be
//! unique among other trusted carriers. When you call [`make_guard!`] to make a
//! unique among other trusted carriers. When you call [`guard!`] to make a
//! unique lifetime, the macro creates a [`Guard`] to hold it. This guard can be
//! converted `into` an [`Id`], which can be stored in structures to uniquely
//! "brand" them. A different invocation of the macro will produce a new
//! lifetime that cannot be unified. The only way to construct these types is
//! with [`make_guard!`] or `unsafe` code.
//! with [`guard!`] or `unsafe` code.
//!
//! ```rust
//! use generativity::{Id, make_guard};
//! use generativity::{Id, guard};
//! struct Struct<'id>(Id<'id>);
//! make_guard!(a);
//! let a = guard!();
//! Struct(a.into());
//! ```
//!
Expand Down Expand Up @@ -50,7 +50,7 @@ use core::marker::PhantomData;
///
/// Holding `Id<'id>` indicates that the lifetime `'id` is a trusted brand.
/// `'id` will not unify with another trusted brand lifetime unless it comes
/// from the same original brand (i.e. the same invocation of [`make_guard!`]).
/// from the same original brand (i.e. the same invocation of [`guard!`]).
#[repr(transparent)]
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub struct Id<'id> {
Expand All @@ -60,7 +60,7 @@ pub struct Id<'id> {
impl<'id> Id<'id> {
/// Construct an `Id` with an unbounded lifetime.
///
/// You should not need to use this function; use [`make_guard!`] instead.
/// You should not need to use this function; use [`guard!`] instead.
///
/// # Safety
///
Expand Down Expand Up @@ -90,7 +90,7 @@ impl<'id> From<Guard<'id>> for Id<'id> {
/// An invariant lifetime phantomdata-alike that is guaranteed to be unique
/// with respect to other trusted invariant lifetimes.
///
/// In effect, this means that `'id` is a "generative brand". Use [`make_guard`]
/// In effect, this means that `'id` is a "generative brand". Use [`guard`]
/// to obtain a new `Guard`.
#[repr(transparent)]
#[derive(Eq, PartialEq)]
Expand All @@ -102,7 +102,7 @@ pub struct Guard<'id> {
impl<'id> Guard<'id> {
/// Construct a `Guard` with an unbound lifetime.
///
/// You should not need to use this function; use [`make_guard!`] instead.
/// You should not need to use this function; use [`guard!`] instead.
///
/// # Safety
///
Expand All @@ -122,7 +122,7 @@ impl<'id> fmt::Debug for Guard<'id> {
}

#[doc(hidden)]
/// NOT STABLE PUBLIC API. Used by the expansion of [`make_guard!`].
/// NOT STABLE PUBLIC API. Used by the expansion of [`guard!`].
pub struct LifetimeBrand<'id> {
phantom: PhantomData<&'id Id<'id>>,
}
Expand All @@ -140,11 +140,11 @@ impl<'id> Drop for LifetimeBrand<'id> {
}

#[doc(hidden)]
/// NOT STABLE PUBLIC API. Used by the expansion of [`make_guard!`].
/// NOT STABLE PUBLIC API. Used by the expansion of [`guard!`].
impl<'id> LifetimeBrand<'id> {
#[doc(hidden)]
#[inline(always)]
/// NOT STABLE PUBLIC API. Used by the expansion of [`make_guard!`].
/// NOT STABLE PUBLIC API. Used by the expansion of [`guard!`].
pub unsafe fn new(_: &'id Id<'id>) -> LifetimeBrand<'id> {
// This function serves to entangle the `'id` lifetime, making it into
// a proper lifetime brand. The `'id` region may open at any point, but
Expand All @@ -159,30 +159,35 @@ impl<'id> LifetimeBrand<'id> {
/// Create a `Guard` with a unique invariant lifetime (with respect to other
/// trusted/invariant lifetime brands).
///
/// Multiple `make_guard` lifetimes will always fail to unify:
/// An optional identifier can be provided so that local errors can point out
/// both conflicting brands if they are confused.
///
/// Multiple `guard!`ed lifetimes will always fail to unify:
///
/// ```rust,compile_fail,E0716
/// # // trybuild ui test tests/ui/crossed_streams.rs
/// # use generativity::make_guard;
/// make_guard!(a);
/// make_guard!(b);
/// # // trybuild ui test tests/ui/crossed_streams-expr.rs
/// # #![feature(super_let)]
/// # use generativity::guard;
/// let a = guard!(a);
/// let b = guard!(b);
/// dbg!(a == b); // ERROR (here == is a static check)
/// ```
#[macro_export]
macro_rules! make_guard {
($name:ident) => {
macro_rules! guard {
() => {
$crate::guard! { anonymous_generativity_brand }
};
($name:ident) => {{
// SAFETY: The lifetime given to `$name` is unique among trusted brands.
// We know this because of how we carefully control drop timing here.
// The branded lifetime's end is bound to be no later than when the
// `branded_place` is invalidated at the end of scope, but also must be
// no sooner than `lifetime_brand` is dropped, also at the end of scope.
// no sooner than when `$name` is dropped, also at the end of scope.
// Some other variant lifetime could be constrained to be equal to the
// brand lifetime, but no other lifetime branded by `make_guard!` can,
// brand lifetime, but no other lifetime branded by `guard!` can,
// as its brand lifetime has a distinct drop time from this one. QED
let branded_place = unsafe { $crate::Id::new() };
#[allow(unused)]
let lifetime_brand = unsafe { $crate::LifetimeBrand::new(&branded_place) };
let $name = unsafe { $crate::Guard::new(branded_place) };
super let branded_place = unsafe { $crate::Id::new() };
#[allow(unused)] super let $name = unsafe { $crate::LifetimeBrand::new(&branded_place) };

// The whole following `if let Some(_) = None {}` block has only one role: to handle
// the case where follow-up code might diverge.
Expand All @@ -204,14 +209,39 @@ macro_rules! make_guard {
//
// This branch ensures that there is at least one place where the `LifetimeBrand`
// is dropped. Which ensures that all `LifetimeBrand`s created will have unique lifetimes
if let $crate::__private::Some(x) = $crate::__private::None {
return x;
} else {
unsafe { $crate::Guard::new(branded_place) }
}
}};
}

/// Create a `Guard` with a unique invariant lifetime (with respect to other
/// trusted/invariant lifetime brands).
///
/// This is a statement macro version of [`guard!`] that works on Rust versions
/// before `#![feature(super_let)]`
#[macro_export]
macro_rules! make_guard {
($name:ident) => {
// SAFETY: See guard! above.
let branded_place = unsafe { $crate::Id::new() };
// We could use $name instead of anonymous_generativity_brand, but this
// leads to confusion of whether the drop timing note is about this or
// the created Guard value.
#[allow(unused)]
let lifetime_brand = unsafe { $crate::LifetimeBrand::new(&branded_place) };
let $name = unsafe { $crate::Guard::new(branded_place) };

if let $crate::__private::Some(x) = $crate::__private::None {
return x;
}
};
}

#[doc(hidden)]
/// NOT STABLE PUBLIC API. Used by the expansion of [`make_guard!`].
/// NOT STABLE PUBLIC API. Used by the expansion of [`guard!`].
pub mod __private {
pub use {None, Some};
}
Expand Down
7 changes: 7 additions & 0 deletions tests/ui/crossed_streams-expr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use generativity::guard;

fn main() {
let a = guard!(a);
let b = guard!(b);
dbg!(a == b); // ERROR (here == is a static check)
}
14 changes: 14 additions & 0 deletions tests/ui/crossed_streams-expr.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
error[E0716]: temporary value dropped while borrowed
--> tests/ui/crossed_streams-expr.rs:7:13
|
5 | let b = guard!(b);
| ^^^^^^^^^ creates a temporary value which is freed while still in use
6 | dbg!(a == b); // ERROR (here == is a static check)
7 | }
| -
| |
| temporary value is freed at the end of this statement
| borrow might be used here, when `a` is dropped and runs the `Drop` code for type `generativity::LifetimeBrand`
|
= note: consider using a `let` binding to create a longer lived value
= note: this error originates in the macro `guard` (in Nightly builds, run with -Z macro-backtrace for more info)
2 changes: 1 addition & 1 deletion tests/ui/is_trans_compatible.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ error: zero-sized fields in `repr(transparent)` cannot contain external types wi
8 | pub struct BOption<'id, T>(Option<T>, Id<'id>); // this should work
| ^^^^^^^
|
= note: this field contains `Id<'_>`, which contains private fields, so it could become non-zero-sized in the future.
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
= note: this field contains `Id<'_>`, which contains private fields, so it could become non-zero-sized in the future.
note: the lint level is defined here
--> tests/ui/is_trans_compatible.rs:3:9
|
Expand Down
Loading