├── .gitignore ├── rustfmt.toml ├── .editorconfig ├── tests ├── trybuild.rs └── ui │ ├── crossed_streams.rs │ ├── issue_15_panic_dropck.rs │ ├── diverging_fn.rs │ ├── diverging_fn_cursed.rs │ ├── is_trans_compatible.rs │ ├── issue_15_panic_dropck.stderr │ ├── crossed_streams.stderr │ ├── is_trans_compatible.stderr │ ├── diverging_fn_cursed.stderr │ └── diverging_fn.stderr ├── .github └── workflows │ ├── publish.yaml │ └── ci.yaml ├── Cargo.toml ├── LICENSE-MIT ├── README.md ├── Cargo.lock ├── LICENSE-APACHE └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | edition = "2018" 2 | newline_style = "Unix" 3 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | charset = utf-8 7 | -------------------------------------------------------------------------------- /tests/trybuild.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn ui() { 3 | let t = trybuild::TestCases::new(); 4 | t.compile_fail("tests/ui/*.rs"); 5 | } 6 | -------------------------------------------------------------------------------- /tests/ui/crossed_streams.rs: -------------------------------------------------------------------------------- 1 | use generativity::make_guard; 2 | 3 | fn main() { 4 | make_guard!(a); 5 | make_guard!(b); 6 | dbg!(a == b); // ERROR (here == is a static check) 7 | } 8 | -------------------------------------------------------------------------------- /tests/ui/issue_15_panic_dropck.rs: -------------------------------------------------------------------------------- 1 | use generativity::{Id, make_guard}; 2 | 3 | fn assert_eq_lt<'id>(_: Id<'id>, _: Id<'id>) {} 4 | 5 | fn main() { 6 | make_guard!(g_a); 7 | make_guard!(g_b); 8 | 9 | let a: Id = g_a.into(); 10 | let b: Id = g_b.into(); 11 | 12 | assert_eq_lt(a, b); 13 | 14 | loop {} 15 | } 16 | -------------------------------------------------------------------------------- /tests/ui/diverging_fn.rs: -------------------------------------------------------------------------------- 1 | use generativity::{Id, make_guard}; 2 | 3 | fn assert_eq_lt<'id>(_: Id<'id>, _: Id<'id>) {} 4 | 5 | fn diverging_fn() -> ! { 6 | make_guard!(g_a); 7 | make_guard!(g_b); 8 | 9 | let a: Id = g_a.into(); 10 | let b: Id = g_b.into(); 11 | 12 | assert_eq_lt(a, b); 13 | 14 | loop {} 15 | } 16 | 17 | fn main() {} 18 | -------------------------------------------------------------------------------- /.github/workflows/publish.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | tags: 4 | - "v*" 5 | 6 | jobs: 7 | publish: 8 | name: Publish 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout 12 | uses: actions/checkout@v4 13 | 14 | - name: Publish 15 | run: cargo publish 16 | env: 17 | CARGO_REGISTRY_TOKEN: ${{secrets.CARGO_REGISTRY_TOKEN}} 18 | -------------------------------------------------------------------------------- /tests/ui/diverging_fn_cursed.rs: -------------------------------------------------------------------------------- 1 | use generativity::{Id, make_guard}; 2 | use never_say_never::Never; 3 | 4 | fn assert_eq_lt<'id>(_: Id<'id>, _: Id<'id>) {} 5 | 6 | fn diverging_fn_cursed() -> (Never, Never) { 7 | make_guard!(g_a); 8 | make_guard!(g_b); 9 | 10 | let a: Id = g_a.into(); 11 | let b: Id = g_b.into(); 12 | 13 | assert_eq_lt(a, b); 14 | 15 | loop {} 16 | } 17 | 18 | fn main() {} 19 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "generativity" 3 | version = "1.2.0" 4 | edition = "2024" 5 | rust-version = "1.85" 6 | 7 | description = "Generation of unique invariant lifetimes" 8 | repository = "https://github.com/CAD97/generativity" 9 | keywords = ["generativity", "no-std", "unsafe-tools"] 10 | categories = ["no-std", "rust-patterns"] 11 | license = "MIT OR Apache-2.0" 12 | 13 | [dev-dependencies] 14 | never-say-never = "6.6.666" 15 | trybuild = "1.0.110" 16 | -------------------------------------------------------------------------------- /tests/ui/is_trans_compatible.rs: -------------------------------------------------------------------------------- 1 | // see cad97/generativity#13 and rust-lang/rust#78586 2 | // if this fails in crater *please* ping me; I *want* this to not hit the lint! 3 | #![deny(repr_transparent_external_private_fields)] // this should not trigger 4 | 5 | use generativity::{make_guard, Id}; 6 | 7 | #[repr(transparent)] 8 | pub struct BOption<'id, T>(Option, Id<'id>); // this should work 9 | 10 | fn main() { 11 | make_guard!(a); 12 | let _ = BOption(Some(0), a.into()); 13 | } 14 | -------------------------------------------------------------------------------- /tests/ui/issue_15_panic_dropck.stderr: -------------------------------------------------------------------------------- 1 | error[E0716]: temporary value dropped while borrowed 2 | --> tests/ui/issue_15_panic_dropck.rs:7:5 3 | | 4 | 7 | make_guard!(g_b); 5 | | ^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use 6 | ... 7 | 15 | } 8 | | - 9 | | | 10 | | temporary value is freed at the end of this statement 11 | | borrow might be used here, when `lifetime_brand` is dropped and runs the `Drop` code for type `LifetimeBrand` 12 | | 13 | = note: consider using a `let` binding to create a longer lived value 14 | = note: this error originates in the macro `make_guard` (in Nightly builds, run with -Z macro-backtrace for more info) 15 | -------------------------------------------------------------------------------- /tests/ui/crossed_streams.stderr: -------------------------------------------------------------------------------- 1 | error[E0716]: temporary value dropped while borrowed 2 | --> tests/ui/crossed_streams.rs:5:5 3 | | 4 | 5 | make_guard!(b); 5 | | ^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use 6 | 6 | dbg!(a == b); // ERROR (here == is a static check) 7 | 7 | } 8 | | - 9 | | | 10 | | temporary value is freed at the end of this statement 11 | | borrow might be used here, when `lifetime_brand` is dropped and runs the `Drop` code for type `LifetimeBrand` 12 | | 13 | = note: consider using a `let` binding to create a longer lived value 14 | = note: this error originates in the macro `make_guard` (in Nightly builds, run with -Z macro-backtrace for more info) 15 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | pull_request: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | tests: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout repository 13 | uses: actions/checkout@v4 14 | 15 | - name: Install stable toolchain 16 | uses: dtolnay/rust-toolchain@stable 17 | 18 | - name: Run tests 19 | run: cargo test --all-features --no-fail-fast 20 | 21 | msrv: 22 | runs-on: ubuntu-latest 23 | steps: 24 | - name: Checkout repository 25 | uses: actions/checkout@v4 26 | 27 | - name: Install 1.85 toolchain 28 | uses: dtolnay/rust-toolchain@1.85 29 | 30 | - name: Check tests 31 | run: cargo test --all-features --no-run 32 | -------------------------------------------------------------------------------- /tests/ui/is_trans_compatible.stderr: -------------------------------------------------------------------------------- 1 | error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types 2 | --> tests/ui/is_trans_compatible.rs:8:39 3 | | 4 | 8 | pub struct BOption<'id, T>(Option, Id<'id>); // this should work 5 | | ^^^^^^^ 6 | | 7 | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! 8 | = note: for more information, see issue #78586 9 | = note: this struct contains `Id<'id>`, which contains private fields, and makes it not a breaking change to become non-zero-sized in the future. 10 | note: the lint level is defined here 11 | --> tests/ui/is_trans_compatible.rs:3:9 12 | | 13 | 3 | #![deny(repr_transparent_external_private_fields)] // this should not trigger 14 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 15 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019, 2022 Christopher Durham (CAD97) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /tests/ui/diverging_fn_cursed.stderr: -------------------------------------------------------------------------------- 1 | error: unreachable expression 2 | --> tests/ui/diverging_fn_cursed.rs:8:5 3 | | 4 | 8 | make_guard!(g_b); 5 | | ^^^^^^^^^^^^^^^^ 6 | | | 7 | | unreachable expression 8 | | any code following this expression is unreachable 9 | | 10 | note: this expression has type `(!, !)`, which is uninhabited 11 | --> tests/ui/diverging_fn_cursed.rs:8:5 12 | | 13 | 8 | make_guard!(g_b); 14 | | ^^^^^^^^^^^^^^^^ 15 | note: the lint level is defined here 16 | --> tests/ui/diverging_fn_cursed.rs:8:5 17 | | 18 | 8 | make_guard!(g_b); 19 | | ^^^^^^^^^^^^^^^^ 20 | = note: this error originates in the macro `make_guard` (in Nightly builds, run with -Z macro-backtrace for more info) 21 | 22 | error: unreachable expression 23 | --> tests/ui/diverging_fn_cursed.rs:7:5 24 | | 25 | 7 | make_guard!(g_a); 26 | | ^^^^^^^^^^^^^^^^ 27 | | | 28 | | unreachable expression 29 | | any code following this expression is unreachable 30 | | 31 | note: this expression has type `(!, !)`, which is uninhabited 32 | --> tests/ui/diverging_fn_cursed.rs:7:5 33 | | 34 | 7 | make_guard!(g_a); 35 | | ^^^^^^^^^^^^^^^^ 36 | note: the lint level is defined here 37 | --> tests/ui/diverging_fn_cursed.rs:7:5 38 | | 39 | 7 | make_guard!(g_a); 40 | | ^^^^^^^^^^^^^^^^ 41 | = note: this error originates in the macro `make_guard` (in Nightly builds, run with -Z macro-backtrace for more info) 42 | -------------------------------------------------------------------------------- /tests/ui/diverging_fn.stderr: -------------------------------------------------------------------------------- 1 | error: unreachable expression 2 | --> tests/ui/diverging_fn.rs:6:5 3 | | 4 | 6 | make_guard!(g_a); 5 | | ^^^^^^^^^^^^^^^^ 6 | | | 7 | | unreachable expression 8 | | any code following this expression is unreachable 9 | | 10 | note: the lint level is defined here 11 | --> tests/ui/diverging_fn.rs:6:5 12 | | 13 | 6 | make_guard!(g_a); 14 | | ^^^^^^^^^^^^^^^^ 15 | = note: this error originates in the macro `make_guard` (in Nightly builds, run with -Z macro-backtrace for more info) 16 | 17 | error[E0277]: `make_guard!()` cannot be used in a diverging/`!`-returning function 18 | --> tests/ui/diverging_fn.rs:6:5 19 | | 20 | 6 | make_guard!(g_a); 21 | | ^^^^^^^^^^^^^^^^ encompassing functions "diverges", e.g., returns `-> !` 22 | | 23 | = help: the trait `__private::sealed::SupportedReturnType` is not implemented for `!` 24 | = note: `make_guard!()` temporary and lifetime shenanigans, on which its soundness model hinges, are broken whenever both a diverging expression follows the `make_guard!()` statement(s), and also if the return type of the encompassing function is `!`, for technical reasons. 25 | 26 | To this day, no workaround is known, so there is no other choice but to reject the `-> !`-returning function case: it is quite niche, and sacrificing it allows every other single instance of `make_guard!()` to remain sound. 27 | See https://github.com/CAD97/generativity/issues/15 for more info. 28 | 29 | note: required by a bound in `PhantomReturnType::< ! as __private::never_say_never::FnPtr>::Never>::reify` 30 | --> src/lib.rs 31 | | 32 | | pub fn reify(&self) -> Never 33 | | ----- required by a bound in this associated function 34 | ... 35 | | for<'trivial> Never : sealed::SupportedReturnType, 36 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `PhantomReturnType::< ! as FnPtr>::Never>::reify` 37 | = note: this error originates in the macro `make_guard` (in Nightly builds, run with -Z macro-backtrace for more info) 38 | 39 | error: unreachable expression 40 | --> tests/ui/diverging_fn.rs:7:5 41 | | 42 | 7 | make_guard!(g_b); 43 | | ^^^^^^^^^^^^^^^^ 44 | | | 45 | | unreachable expression 46 | | any code following this expression is unreachable 47 | | 48 | note: the lint level is defined here 49 | --> tests/ui/diverging_fn.rs:7:5 50 | | 51 | 7 | make_guard!(g_b); 52 | | ^^^^^^^^^^^^^^^^ 53 | = note: this error originates in the macro `make_guard` (in Nightly builds, run with -Z macro-backtrace for more info) 54 | 55 | error[E0277]: `make_guard!()` cannot be used in a diverging/`!`-returning function 56 | --> tests/ui/diverging_fn.rs:7:5 57 | | 58 | 7 | make_guard!(g_b); 59 | | ^^^^^^^^^^^^^^^^ encompassing functions "diverges", e.g., returns `-> !` 60 | | 61 | = help: the trait `__private::sealed::SupportedReturnType` is not implemented for `!` 62 | = note: `make_guard!()` temporary and lifetime shenanigans, on which its soundness model hinges, are broken whenever both a diverging expression follows the `make_guard!()` statement(s), and also if the return type of the encompassing function is `!`, for technical reasons. 63 | 64 | To this day, no workaround is known, so there is no other choice but to reject the `-> !`-returning function case: it is quite niche, and sacrificing it allows every other single instance of `make_guard!()` to remain sound. 65 | See https://github.com/CAD97/generativity/issues/15 for more info. 66 | 67 | note: required by a bound in `PhantomReturnType::< ! as __private::never_say_never::FnPtr>::Never>::reify` 68 | --> src/lib.rs 69 | | 70 | | pub fn reify(&self) -> Never 71 | | ----- required by a bound in this associated function 72 | ... 73 | | for<'trivial> Never : sealed::SupportedReturnType, 74 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `PhantomReturnType::< ! as FnPtr>::Never>::reify` 75 | = note: this error originates in the macro `make_guard` (in Nightly builds, run with -Z macro-backtrace for more info) 76 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Generativity [![Chat on Discord](https://img.shields.io/discord/496481045684944897?style=flat&logo=discord)][Discord] 2 | 3 | Generativity refers to the creation of a unique lifetime: one that the Rust 4 | borrow checker will not unify with any other lifetime. This can be used to 5 | brand types such that you know that you, and not another copy of you, created 6 | them. This is required for sound unchecked indexing and similar tricks. 7 | 8 | The first step of achieving generativity is an [invariant][variance] lifetime. 9 | Then the consumer must not be able to unify that lifetime with any other lifetime. 10 | 11 | Traditionally, this is achieved with a closure. If you have the user write their code in a 12 | `for<'a> fn(Invariant<'a>) -> _` callback, the local typechecking within this callback 13 | is forced to assume that it can be handed _any_ lifetime (the `for<'a>` bound), and that 14 | it cannot possibly use another lifetime, even `'static`, in its place (the invariance). 15 | 16 | This crate implements a different approach using macros and a `Drop`-based scope guard. 17 | When you call `generativity::make_guard!` to make a unique lifetime guard, the macro 18 | first creates an `Id` holding an invariant lifetime. It then puts within a `Drop` type 19 | a `&'id Id<'id>` reference. A different invocation of the macro has a different end timing 20 | to its tag lifetime, so the lifetimes cannot be unified. These types have no safe way to 21 | construct them other than via `make_guard!`, thus the lifetime is guaranteed unique. 22 | 23 | This effectively does the same thing as wrapping the rest of the function in an 24 | immediately invoked closure. We do some pre-work (create the tag and give the caller the 25 | guard), run the user code (the code after here, the closure previously), and then run 26 | some cleanup code after (in the drop implementation). This same technique of a macro 27 | hygiene hidden `impl Drop` can be used by most APIs that would normally use a closure 28 | argument, enabling them to avoid introducing a closure boundary to control flow "effect"s 29 | such as `async.await` and `?`. But this also comes with a subtle downside: being able to 30 | `.await` means that locals could be forgotten instead of dropped, and thus the drop glue 31 | must not be relied on to run for soundness. This is okay for this crate, since the actual 32 | drop impl is a no-op that just exists for the lifetime analysis impacts, but it means that 33 | more interesting cases like scoped threads still need to use a closure callback to ensure 34 | their soundness-critical cleanup gets run (e.g. to wait on and join the scoped threads). 35 | 36 | It's important to note that lifetimes are only trusted to carry lifetime brands when they 37 | are in fact invariant. Variant lifetimes, such as `&'a T`, can still be shrunk to fit the 38 | branded lifetime; `&'static T` can be used where `&'a T` is expected, for *any* `'a`. 39 | 40 | ## How does it work? 41 | 42 | ```rust 43 | // SAFETY: The lifetime given to `$name` is unique among trusted brands. 44 | // We know this because of how we carefully control drop timing here. 45 | // The branded lifetime's end is bound to be no later than when the 46 | // `branded_place` is invalidated at the end of scope, but also must be 47 | // no sooner than `lifetime_brand` is dropped, also at the end of scope. 48 | // Some other variant lifetime could be constrained to be equal to the 49 | // brand lifetime, but no other lifetime branded by `make_guard!` can, 50 | // as its brand lifetime has a distinct drop time from this one. QED 51 | let branded_place = unsafe { $crate::Id::new() }; 52 | #[allow(unused)] 53 | let lifetime_brand = unsafe { $crate::LifetimeBrand::new(&branded_place) }; 54 | // implicit when exiting scope: drop(lifetime_brand), as LifetimeBrand: Drop 55 | let $name = unsafe { $crate::Guard::new(branded_place) }; 56 | ``` 57 | 58 | A previous version of this crate emitted more code in the macro, and defined the 59 | `LifetimeBrand` type inline. This improved compiler errors on compiler versions 60 | from the time, but the current compiler produces an equivalently good or better 61 | error with `LifetimeBrand` defined in the `generativity` crate. 62 | 63 | The `LifetimeBrand` type is *not public API* and must not be used directly. It 64 | is not covered by stability guarantees; only usage of `make_guard!` and other 65 | documented APIs are considered stable. 66 | 67 | ## Huge generativity disclaimer 68 | 69 | That last point above is ***VERY*** important. We _cannot_ guarantee that the 70 | lifetime is fully unique. We can only guarantee that it's unique among trusted 71 | carriers. This applies equally to the traditional method: 72 | 73 | ```rust 74 | fn scope(f: F) 75 | where F: for<'id> FnOnce(Guard<'id>) 76 | { 77 | make_guard!(guard); 78 | f(guard); 79 | } 80 | 81 | fn unify<'a>(_: &'a (), _: &Guard<'a>) { 82 | // here, you have two `'a` which are equivalent to `guard`'s `'id` 83 | } 84 | 85 | fn main() { 86 | let place = (); 87 | make_guard!(guard); 88 | unify(&place, &guard); 89 | scope(|guard| { 90 | unify(&place, &guard); 91 | }) 92 | } 93 | ``` 94 | 95 | Other variant lifetimes can still shrink to unify with `'id`. What all this 96 | means is that you can't just trust any old `'id` lifetime floating around. It 97 | has to be carried in a trusted carrier, and one that wasn't created from an 98 | untrusted lifetime. 99 | 100 | ## Impl Disclaimer 101 | 102 | This relies on dead code (an `#[inline(always)]` no-op drop) to impact borrow 103 | checking. In theory, a sufficiently advanced borrow checker looking at the CFG 104 | after some inlining would be able to see that dropping `lifetime_brand` doesn't 105 | require the captured lifetime to be live, and destroy the uniqueness guarantee 106 | which we've created. This would be much more difficult with the higher-ranked 107 | closure formulation, but would still theoretically be possible with sufficient 108 | inlining. Thankfully, based on the direction around the unstable "borrowck 109 | eyepatch" which is the reason e.g. `Box<&'a T>` can be dropped at end of scope 110 | despite the `&'a T` borrow being invalidated beforehand, and the further 111 | stability implications of inferring whether a generic is "used" by `Drop::drop`, 112 | it seems like any such weakening of an explicit `impl Drop` "using" captured 113 | lifetimes in the eyes of borrowck will be opt-in. This crate won't opt in to 114 | such a feature, and thus will remain sound. 115 | 116 | However, in the specific case that the containing function returns `!` (is known 117 | to diverge at the type level), it is possible to get borrow checking to ignore 118 | the impact of drop glue, if it is never reached in any arm. `make_guard!` will 119 | refuse to compile in this case; in the case of any other return type, we ensure 120 | that the drop glue is considered in determining the full lifetime of the brand. 121 | 122 | ## Minimum supported Rust version 123 | 124 | The crate currently requires Rust 1.85. I have no intent of increasing the 125 | compiler version requirement of this crate beyond this. However, this is only 126 | guaranteed within a given minor version number. 127 | 128 | ## License 129 | 130 | Licensed under either of 131 | 132 | - Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or ) 133 | - MIT license ([LICENSE-MIT](LICENSE-MIT) or ) 134 | 135 | at your option. 136 | 137 | ### Contribution 138 | 139 | Unless you explicitly state otherwise, any contribution intentionally submitted 140 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall 141 | be dual licensed as above, without any additional terms or conditions. 142 | 143 | [Discord]: 144 | [variance]: 145 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "equivalent" 7 | version = "1.0.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" 10 | 11 | [[package]] 12 | name = "generativity" 13 | version = "1.2.0" 14 | dependencies = [ 15 | "never-say-never", 16 | "trybuild", 17 | ] 18 | 19 | [[package]] 20 | name = "glob" 21 | version = "0.3.2" 22 | source = "registry+https://github.com/rust-lang/crates.io-index" 23 | checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" 24 | 25 | [[package]] 26 | name = "hashbrown" 27 | version = "0.15.4" 28 | source = "registry+https://github.com/rust-lang/crates.io-index" 29 | checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" 30 | 31 | [[package]] 32 | name = "indexmap" 33 | version = "2.10.0" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" 36 | dependencies = [ 37 | "equivalent", 38 | "hashbrown", 39 | ] 40 | 41 | [[package]] 42 | name = "itoa" 43 | version = "1.0.15" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 46 | 47 | [[package]] 48 | name = "memchr" 49 | version = "2.7.5" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" 52 | 53 | [[package]] 54 | name = "never-say-never" 55 | version = "6.6.666" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "cf5a574dadd7941adeaa71823ecba5e28331b8313fb2e1c6a5c7e5981ea53ad6" 58 | 59 | [[package]] 60 | name = "proc-macro2" 61 | version = "1.0.95" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" 64 | dependencies = [ 65 | "unicode-ident", 66 | ] 67 | 68 | [[package]] 69 | name = "quote" 70 | version = "1.0.40" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 73 | dependencies = [ 74 | "proc-macro2", 75 | ] 76 | 77 | [[package]] 78 | name = "ryu" 79 | version = "1.0.20" 80 | source = "registry+https://github.com/rust-lang/crates.io-index" 81 | checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" 82 | 83 | [[package]] 84 | name = "serde" 85 | version = "1.0.219" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" 88 | dependencies = [ 89 | "serde_derive", 90 | ] 91 | 92 | [[package]] 93 | name = "serde_derive" 94 | version = "1.0.219" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" 97 | dependencies = [ 98 | "proc-macro2", 99 | "quote", 100 | "syn", 101 | ] 102 | 103 | [[package]] 104 | name = "serde_json" 105 | version = "1.0.142" 106 | source = "registry+https://github.com/rust-lang/crates.io-index" 107 | checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7" 108 | dependencies = [ 109 | "itoa", 110 | "memchr", 111 | "ryu", 112 | "serde", 113 | ] 114 | 115 | [[package]] 116 | name = "serde_spanned" 117 | version = "1.0.0" 118 | source = "registry+https://github.com/rust-lang/crates.io-index" 119 | checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83" 120 | dependencies = [ 121 | "serde", 122 | ] 123 | 124 | [[package]] 125 | name = "syn" 126 | version = "2.0.104" 127 | source = "registry+https://github.com/rust-lang/crates.io-index" 128 | checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" 129 | dependencies = [ 130 | "proc-macro2", 131 | "quote", 132 | "unicode-ident", 133 | ] 134 | 135 | [[package]] 136 | name = "target-triple" 137 | version = "0.1.4" 138 | source = "registry+https://github.com/rust-lang/crates.io-index" 139 | checksum = "1ac9aa371f599d22256307c24a9d748c041e548cbf599f35d890f9d365361790" 140 | 141 | [[package]] 142 | name = "termcolor" 143 | version = "1.4.1" 144 | source = "registry+https://github.com/rust-lang/crates.io-index" 145 | checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" 146 | dependencies = [ 147 | "winapi-util", 148 | ] 149 | 150 | [[package]] 151 | name = "toml" 152 | version = "0.9.5" 153 | source = "registry+https://github.com/rust-lang/crates.io-index" 154 | checksum = "75129e1dc5000bfbaa9fee9d1b21f974f9fbad9daec557a521ee6e080825f6e8" 155 | dependencies = [ 156 | "indexmap", 157 | "serde", 158 | "serde_spanned", 159 | "toml_datetime", 160 | "toml_parser", 161 | "toml_writer", 162 | "winnow", 163 | ] 164 | 165 | [[package]] 166 | name = "toml_datetime" 167 | version = "0.7.0" 168 | source = "registry+https://github.com/rust-lang/crates.io-index" 169 | checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3" 170 | dependencies = [ 171 | "serde", 172 | ] 173 | 174 | [[package]] 175 | name = "toml_parser" 176 | version = "1.0.2" 177 | source = "registry+https://github.com/rust-lang/crates.io-index" 178 | checksum = "b551886f449aa90d4fe2bdaa9f4a2577ad2dde302c61ecf262d80b116db95c10" 179 | dependencies = [ 180 | "winnow", 181 | ] 182 | 183 | [[package]] 184 | name = "toml_writer" 185 | version = "1.0.2" 186 | source = "registry+https://github.com/rust-lang/crates.io-index" 187 | checksum = "fcc842091f2def52017664b53082ecbbeb5c7731092bad69d2c63050401dfd64" 188 | 189 | [[package]] 190 | name = "trybuild" 191 | version = "1.0.110" 192 | source = "registry+https://github.com/rust-lang/crates.io-index" 193 | checksum = "32e257d7246e7a9fd015fb0b28b330a8d4142151a33f03e6a497754f4b1f6a8e" 194 | dependencies = [ 195 | "glob", 196 | "serde", 197 | "serde_derive", 198 | "serde_json", 199 | "target-triple", 200 | "termcolor", 201 | "toml", 202 | ] 203 | 204 | [[package]] 205 | name = "unicode-ident" 206 | version = "1.0.18" 207 | source = "registry+https://github.com/rust-lang/crates.io-index" 208 | checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 209 | 210 | [[package]] 211 | name = "winapi-util" 212 | version = "0.1.9" 213 | source = "registry+https://github.com/rust-lang/crates.io-index" 214 | checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" 215 | dependencies = [ 216 | "windows-sys", 217 | ] 218 | 219 | [[package]] 220 | name = "windows-sys" 221 | version = "0.59.0" 222 | source = "registry+https://github.com/rust-lang/crates.io-index" 223 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 224 | dependencies = [ 225 | "windows-targets", 226 | ] 227 | 228 | [[package]] 229 | name = "windows-targets" 230 | version = "0.52.6" 231 | source = "registry+https://github.com/rust-lang/crates.io-index" 232 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 233 | dependencies = [ 234 | "windows_aarch64_gnullvm", 235 | "windows_aarch64_msvc", 236 | "windows_i686_gnu", 237 | "windows_i686_gnullvm", 238 | "windows_i686_msvc", 239 | "windows_x86_64_gnu", 240 | "windows_x86_64_gnullvm", 241 | "windows_x86_64_msvc", 242 | ] 243 | 244 | [[package]] 245 | name = "windows_aarch64_gnullvm" 246 | version = "0.52.6" 247 | source = "registry+https://github.com/rust-lang/crates.io-index" 248 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 249 | 250 | [[package]] 251 | name = "windows_aarch64_msvc" 252 | version = "0.52.6" 253 | source = "registry+https://github.com/rust-lang/crates.io-index" 254 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 255 | 256 | [[package]] 257 | name = "windows_i686_gnu" 258 | version = "0.52.6" 259 | source = "registry+https://github.com/rust-lang/crates.io-index" 260 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 261 | 262 | [[package]] 263 | name = "windows_i686_gnullvm" 264 | version = "0.52.6" 265 | source = "registry+https://github.com/rust-lang/crates.io-index" 266 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 267 | 268 | [[package]] 269 | name = "windows_i686_msvc" 270 | version = "0.52.6" 271 | source = "registry+https://github.com/rust-lang/crates.io-index" 272 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 273 | 274 | [[package]] 275 | name = "windows_x86_64_gnu" 276 | version = "0.52.6" 277 | source = "registry+https://github.com/rust-lang/crates.io-index" 278 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 279 | 280 | [[package]] 281 | name = "windows_x86_64_gnullvm" 282 | version = "0.52.6" 283 | source = "registry+https://github.com/rust-lang/crates.io-index" 284 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 285 | 286 | [[package]] 287 | name = "windows_x86_64_msvc" 288 | version = "0.52.6" 289 | source = "registry+https://github.com/rust-lang/crates.io-index" 290 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 291 | 292 | [[package]] 293 | name = "winnow" 294 | version = "0.7.12" 295 | source = "registry+https://github.com/rust-lang/crates.io-index" 296 | checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" 297 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(test), no_std)] 2 | 3 | //! Create a trusted carrier with a new lifetime that is guaranteed to be 4 | //! unique among other trusted carriers. When you call [`make_guard!`] to make a 5 | //! unique lifetime, the macro creates a [`Guard`] to hold it. This guard can be 6 | //! converted `into` an [`Id`], which can be stored in structures to uniquely 7 | //! "brand" them. A different invocation of the macro will produce a new 8 | //! lifetime that cannot be unified. The only way to construct these types is 9 | //! with [`make_guard!`] or `unsafe` code. 10 | //! 11 | //! ```rust 12 | //! use generativity::{Id, make_guard}; 13 | //! struct Struct<'id>(Id<'id>); 14 | //! make_guard!(a); 15 | //! Struct(a.into()); 16 | //! ``` 17 | //! 18 | //! This is the concept of "generative" lifetime brands. `Guard` and `Id` are 19 | //! [invariant](https://doc.rust-lang.org/nomicon/subtyping.html#variance) over 20 | //! their lifetime parameter, meaning that it is never valid to substitute or 21 | //! otherwise coerce `Id<'a>` into `Id<'b>`, for *any* concrete `'a` or `'b`, 22 | //! *including* the `'static` lifetime. 23 | //! 24 | //! Any invariant lifetime can be "trusted" to carry a brand, so long as they 25 | //! are known to be restricted to carrying a brand, and haven't been derived 26 | //! from some untrusted lifetime (or are completely unbound). When using this 27 | //! library, it is recommended to always use `Id<'id>` to carry the brand, as 28 | //! this reduces the risk of accidentally trusting an untrusted lifetime. 29 | //! Importantly, non-invariant lifetimes *cannot* be trusted, as the variance 30 | //! allows lifetimes to be contracted to match and copy the brand lifetime. 31 | //! 32 | //! To achieve lifetime invariance without `Id`, there are two standard ways: 33 | //! `PhantomData<&'a mut &'a ()>` and `PhantomData &'a ()>`. The 34 | //! former works because `&mut T` is invariant over `T`, and the latter works 35 | //! because `fn(T)` is *contra*variant over `T` and `fn() -> T` is *co*variant 36 | //! over `T`, which combines to *in*variance. Both are equivalent in this case 37 | //! with `T = ()`, but `fn(T) -> T` is generally preferred if the only purpose 38 | //! is to indicate invariance, as function pointers are a perfect cover for all 39 | //! auto traits (e.g. `Send`, `Sync`, `Unpin`, `UnwindSafe`, etc.) and thus 40 | //! only indicates invariance, whereas `&mut T` can carry further implication 41 | //! of "by example" use of `PhantomData`. 42 | 43 | use core_::fmt; 44 | use core_::marker::PhantomData; 45 | 46 | #[doc(hidden)] 47 | /// NOT STABLE PUBLIC API. Previously Used by the expansion of [`make_guard!`]. 48 | pub extern crate core as core_; 49 | 50 | /// A phantomdata-like type taking a single invariant lifetime. 51 | /// 52 | /// Used to manipulate and store the unique invariant lifetime obtained from 53 | /// [`Guard`]. Use `guard.into()` to create a new `Id`. 54 | /// 55 | /// Holding `Id<'id>` indicates that the lifetime `'id` is a trusted brand. 56 | /// `'id` will not unify with another trusted brand lifetime unless it comes 57 | /// from the same original brand (i.e. the same invocation of [`make_guard!`]). 58 | #[repr(transparent)] 59 | #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] 60 | pub struct Id<'id> { 61 | phantom: PhantomData &'id ()>, 62 | } 63 | 64 | impl<'id> Id<'id> { 65 | /// Construct an `Id` with an unbounded lifetime. 66 | /// 67 | /// You should not need to use this function; use [`make_guard!`] instead. 68 | /// 69 | /// # Safety 70 | /// 71 | /// `Id` holds an invariant lifetime that must be derived from a generative 72 | /// brand. Using this function directly is the "I know what I'm doing" 73 | /// button; restrict the lifetime to a known brand immediately to avoid 74 | /// introducing potential unsoundness. 75 | pub unsafe fn new() -> Self { 76 | Id { 77 | phantom: PhantomData, 78 | } 79 | } 80 | } 81 | 82 | impl<'id> fmt::Debug for Id<'id> { 83 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 84 | f.debug_struct("#[invariant] 'id").finish() 85 | } 86 | } 87 | 88 | impl<'id> From> for Id<'id> { 89 | fn from(guard: Guard<'id>) -> Self { 90 | guard.id 91 | } 92 | } 93 | 94 | /// An invariant lifetime phantomdata-alike that is guaranteed to be unique 95 | /// with respect to other trusted invariant lifetimes. 96 | /// 97 | /// In effect, this means that `'id` is a "generative brand". Use [`make_guard`] 98 | /// to obtain a new `Guard`. 99 | #[repr(transparent)] 100 | #[derive(Eq, PartialEq)] 101 | pub struct Guard<'id> { 102 | #[allow(unused)] 103 | id: Id<'id>, 104 | } 105 | 106 | impl<'id> Guard<'id> { 107 | /// Construct a `Guard` with an unbound lifetime. 108 | /// 109 | /// You should not need to use this function; use [`make_guard!`] instead. 110 | /// 111 | /// # Safety 112 | /// 113 | /// `Guard` holds an invariant lifetime that must be an unused generative 114 | /// brand. Using this function directly is the "I know what I'm doing" 115 | /// button; restrict the lifetime to a known brand immediately to avoid 116 | /// introducing potential unsoundness. 117 | pub unsafe fn new(id: Id<'id>) -> Guard<'id> { 118 | Guard { id } 119 | } 120 | } 121 | 122 | impl<'id> fmt::Debug for Guard<'id> { 123 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 124 | f.debug_struct("#[unique] 'id").finish() 125 | } 126 | } 127 | 128 | #[doc(hidden)] 129 | /// NOT STABLE PUBLIC API. Used by the expansion of [`make_guard!`]. 130 | pub struct LifetimeBrand<'id> { 131 | phantom: PhantomData<&'id Id<'id>>, 132 | } 133 | 134 | impl<'id> Drop for LifetimeBrand<'id> { 135 | #[inline(always)] 136 | fn drop(&mut self) { 137 | // This impl purposefully left blank. The presence of a Drop impl gives 138 | // the `make_guard` type drop glue, dropping it at the end of scope. 139 | // Importantly, this ensures that the compiler has to consider `'id` 140 | // live at the point that this type is dropped, because this impl could 141 | // potentially use data borrowed that lifetime. #[inline(always)] just 142 | // serves to make it easier to optimize out the noop function call. 143 | } 144 | } 145 | 146 | #[doc(hidden)] 147 | /// NOT STABLE PUBLIC API. Used by the expansion of [`make_guard!`]. 148 | impl<'id> LifetimeBrand<'id> { 149 | #[doc(hidden)] 150 | #[inline(always)] 151 | /// NOT STABLE PUBLIC API. Used by the expansion of [`make_guard!`]. 152 | pub unsafe fn new(_: &'id Id<'id>) -> LifetimeBrand<'id> { 153 | // This function serves to entangle the `'id` lifetime, making it into 154 | // a proper lifetime brand. The `'id` region may open at any point, but 155 | // it must end in-between the drop timing of this `LifetimeBrand` and 156 | // the `Id` binding used to create it. 157 | LifetimeBrand { 158 | phantom: PhantomData, 159 | } 160 | } 161 | } 162 | 163 | /// Create a `Guard` with a unique invariant lifetime (with respect to other 164 | /// trusted/invariant lifetime brands). 165 | /// 166 | /// Multiple `make_guard` lifetimes will always fail to unify: 167 | /// 168 | /// ```rust,compile_fail,E0597 169 | /// # // trybuild ui test tests/ui/crossed_streams.rs 170 | /// # use generativity::make_guard; 171 | /// make_guard!(a); 172 | /// make_guard!(b); 173 | /// dbg!(a == b); // ERROR (here == is a static check) 174 | /// ``` 175 | #[macro_export] 176 | macro_rules! make_guard { 177 | ($name:ident) => { 178 | // SAFETY: The lifetime given to `$name` is unique among trusted brands. 179 | // We know this because of how we carefully control drop timing here. 180 | // The branded lifetime's end is bound to be no later than when the 181 | // `branded_place` is invalidated at the end of scope, but also must be 182 | // no sooner than `lifetime_brand` is dropped, also at the end of scope. 183 | // Some other variant lifetime could be constrained to be equal to the 184 | // brand lifetime, but no other lifetime branded by `make_guard!` can, 185 | // as its brand lifetime has a distinct drop time from this one. QED 186 | let branded_place = unsafe { $crate::Id::new() }; 187 | #[allow(unused)] 188 | let lifetime_brand = unsafe { $crate::LifetimeBrand::new(&branded_place) }; 189 | let $name = unsafe { $crate::Guard::new(branded_place) }; 190 | 191 | // The whole following `if false {}` block has only one role: to handle 192 | // the case where follow-up code might diverge. 193 | // See https://github.com/CAD97/generativity/issues/15 for more info. 194 | if false { 195 | #[allow(unreachable_code)] { 196 | let phantom_ret_ty = $crate::__private::PhantomReturnType::<_>::NEW; 197 | if false { 198 | // Use inference to set the type parameter to that of the return type. 199 | return $crate::__private::DefaultReify::reify(&phantom_ret_ty); 200 | } 201 | 202 | // Guarding against `PhantomReturnType` itself does not suffice, 203 | // we may be dealing with `PhantomReturnType<(!, !)>`, for instance. 204 | // 205 | // Observation: the very same mechanism which causes us trouble 206 | // yields an `unreachable_code` warning in the following situation: 207 | if false { 208 | let _reified_ret = 209 | $crate::__private::DefaultReify::reify(&phantom_ret_ty); 210 | #[forbid(unreachable_code)] { 211 | // any arbitrary statement works to trigger the lint. 212 | if true {} 213 | } 214 | } 215 | 216 | // Poorman's specialization; only shadowed in the 217 | // `PhantomReturnType` case. 218 | // 219 | // This is not strictly needed for soundness *per se*, since the above 220 | // `forbid(unreachable_code)` takes care of that. 221 | // 222 | // But it greatly improves the diagnostics for the non-niche case. 223 | #[allow(unused)] 224 | use $crate::__private::DefaultReify as _; 225 | return phantom_ret_ty.reify(); 226 | } 227 | } 228 | }; 229 | } 230 | 231 | #[doc(hidden)] 232 | /// NOT STABLE PUBLIC API. Used by the expansion of [`make_guard!`]. 233 | pub mod __private { 234 | pub struct PhantomReturnType(::core::marker::PhantomData); 235 | 236 | impl PhantomReturnType { 237 | pub const NEW: Self = Self(::core::marker::PhantomData); 238 | } 239 | 240 | /// Inlined [`::never-say-never`](https://docs.rs/never-say-never). 241 | mod never_say_never { 242 | pub trait FnPtr { type Never; } 243 | impl FnPtr for fn() -> R { type Never = R; } 244 | } 245 | type Never = ! as never_say_never::FnPtr>::Never; 246 | 247 | /// Poorman's specialization for `Phony::::get()`. 248 | pub trait DefaultReify { 249 | /// Function to be used in dead code to reify/synthesize a `T` instance 250 | /// out of our [`PhantomReturnType`]. 251 | fn reify(&self) -> T { unreachable!() } 252 | } 253 | 254 | impl DefaultReify for PhantomReturnType {} 255 | 256 | impl PhantomReturnType { 257 | /// Uncallable method, via an unmet predicate. 258 | pub fn reify(&self) -> Never 259 | where 260 | // Clause needs to involve some lifetime parameter in order not to 261 | // cause a `trivial_bounds` eager error. 262 | // `for<'trivial>` is the "typical" workaround so far. 263 | for<'trivial> Never : sealed::SupportedReturnType, 264 | { 265 | unreachable!() 266 | } 267 | } 268 | 269 | mod sealed { 270 | #[diagnostic::on_unimplemented( 271 | message = "\ 272 | `make_guard!()` cannot be used in a diverging/`!`-returning function\ 273 | ", 274 | label = "encompassing functions \"diverges\", e.g., returns `-> !`", 275 | note = "\ 276 | `make_guard!()` temporary and lifetime shenanigans, on which its soundness model hinges, \ 277 | are broken whenever both a diverging expression follows the `make_guard!()` statement(s), \ 278 | and also if the return type of the encompassing function is `!`, for technical reasons. \ 279 | \n\ 280 | \n\ 281 | To this day, no workaround is known, so there is no other choice but to reject the \ 282 | `-> !`-returning function case: it is quite niche, and sacrificing it allows every \ 283 | other single instance of `make_guard!()` to remain sound.\ 284 | \n\ 285 | See https://github.com/CAD97/generativity/issues/15 for more info. 286 | ", 287 | )] 288 | pub trait SupportedReturnType {} 289 | } 290 | } 291 | 292 | #[cfg(test)] 293 | mod test { 294 | use super::*; 295 | use std::panic::{RefUnwindSafe, UnwindSafe}; 296 | 297 | #[test] 298 | fn dont_error_in_general() { 299 | make_guard!(a); 300 | make_guard!(b); 301 | assert_eq!(a, a); 302 | assert_eq!(b, b); 303 | } 304 | 305 | #[test] 306 | fn test_oibits() { 307 | fn assert_oibits(_: &T) 308 | where 309 | T: Send + Sync + Unpin + UnwindSafe + RefUnwindSafe, 310 | { 311 | } 312 | 313 | make_guard!(a); 314 | assert_oibits(&a); 315 | let id: Id<'_> = a.into(); 316 | assert_oibits(&id); 317 | 318 | // const compatible (e.g. const_refs_to_cell, const destructor) 319 | const fn _const_id(_: Id<'_>) {} 320 | const fn _const_ref_id(_: &'_ Id<'_>) {} 321 | const fn _const_guard(_: Guard<'_>) {} 322 | const fn _const_ref_guard(_: &'_ Guard<'_>) {} 323 | } 324 | } 325 | --------------------------------------------------------------------------------