Skip to content
Closed
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
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ categories = ["no-std", "rust-patterns"]
license = "MIT OR Apache-2.0"

[dev-dependencies]
trybuild = "1.0.85"
trybuild = "=1.0.85"
40 changes: 27 additions & 13 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#![cfg_attr(not(test), no_std)]
#![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
Expand Down Expand Up @@ -72,10 +72,12 @@ impl<'id> Id<'id> {
/// brand. Using this function directly is the "I know what I'm doing"
/// button; restrict the lifetime to a known brand immediately to avoid
/// introducing potential unsoundness.
pub unsafe fn new() -> Self {
Id {
phantom: PhantomData,
}
#[inline(always)]
pub const unsafe fn new() -> Self {
// HACK: using `transmute` here lets us call `Id::new` in const contexts on older rust versions.
// Without it, the compiler complains about the function pointer in the PhantomData field.
// SAFETY: PhantomData is always safe to create
core_::mem::transmute(())
}
}

Expand All @@ -86,6 +88,7 @@ impl<'id> fmt::Debug for Id<'id> {
}

impl<'id> From<Guard<'id>> for Id<'id> {
#[inline(always)]
fn from(guard: Guard<'id>) -> Self {
guard.id
}
Expand Down Expand Up @@ -114,7 +117,8 @@ impl<'id> Guard<'id> {
/// brand. Using this function directly is the "I know what I'm doing"
/// button; restrict the lifetime to a known brand immediately to avoid
/// introducing potential unsoundness.
pub unsafe fn new(id: Id<'id>) -> Guard<'id> {
#[inline(always)]
pub const unsafe fn new(id: Id<'id>) -> Guard<'id> {
Guard { id }
}
}
Expand Down Expand Up @@ -174,7 +178,7 @@ impl<'id> LifetimeBrand<'id> {
/// ```
#[macro_export]
macro_rules! make_guard {
($name:ident) => {
($($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
Expand All @@ -183,17 +187,19 @@ macro_rules! make_guard {
// Some other variant lifetime could be constrained to be equal to the
// brand lifetime, but no other lifetime branded by `make_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) };
};
#[allow(unused_unsafe)]
let $name = unsafe { $crate::Id::new() };
#[allow(unused, unused_unsafe)]
let lifetime_brand = unsafe { $crate::LifetimeBrand::new(&$name) };
#[allow(unused_unsafe)]
let $name = unsafe { $crate::Guard::new($name) };
)*};
}

#[cfg(test)]
mod test {
use super::*;
use std::panic::{RefUnwindSafe, UnwindSafe};
use core_::panic::{RefUnwindSafe, UnwindSafe};

#[test]
fn dont_error_in_general() {
Expand All @@ -203,6 +209,14 @@ mod test {
assert_eq!(b, b);
}

#[test]
fn multiple_guards() {
make_guard!(a, b, c);
assert_eq!(a, a);
assert_eq!(b, b);
assert_eq!(c, c);
}

#[test]
fn test_oibits() {
fn assert_oibits<T>(_: &T)
Expand Down
6 changes: 3 additions & 3 deletions tests/ui/crossed_streams.stderr
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
error[E0597]: `branded_place` does not live long enough
error[E0597]: `b` does not live long enough
--> tests/ui/crossed_streams.rs:5:5
|
5 | make_guard!(b);
| ^^^^^^^^^^^^^^
| |
| borrowed value does not live long enough
| binding `branded_place` declared here
| binding `b` declared here
6 | dbg!(a == b); // ERROR (here == is a static check)
7 | }
| -
| |
| `branded_place` dropped here while still borrowed
| `b` dropped here while still borrowed
| borrow might be used here, when `lifetime_brand` is dropped and runs the `Drop` code for type `LifetimeBrand`
|
= note: values in a scope are dropped in the opposite order they are defined
Expand Down
Loading