I think the main things you're lacking are documentation (for both crate consumers and reviewers) and examples that actually exercise the intended guarantee for soundness.
If I figured out your intentions correctly, I haven't thought of any holes.
Let me try to summarize, and correct me if I'm wrong.
The crate
- Every macro use site corresponds to a unique type, and creates a value of that type
- The value contains an exclusive borrow also local to the macro use site (and thus the type contains the lifetime of that borrow)
- Therefore no two values of the same type can exist at the same time
- But you also can't use the value (or type) outside of the current scope
Those are the guarantees you want to provide, and the limitation that comes with it.
The intention is that the singleton tokens can provide unique branding via the type of the token. That is, when used correctly, they provide a way to create a singleton of some other type like a collection, which is branded with the unique type. That brand can be used to created branded indices that must belong to the same collection and so on.
The implementation
I'm sure you understand this part, but it'd be good to document it for the sake of reviewers and maintainers.
The exclusive borrow means you can't get multiple values out from the same macro call site at the same time -- by returning from a function, yes, but also by being in a loop, say. The idea is to plug a hole of many unique-type-based singleton attempts: every (monomorphized) expression has a particular type; if that code runs more than once, the same type results each run, even if that type is unique to the expression. So creating a value of a "unique type" is not enough to actually be a singleton at run time.
And the plug is: the liveness of token values of the same type cannot overlap, enforced by the borrow checker. It's like a lending iterator, but the "iterator" is tied to a macro call site.
The example
Because there's nothing in the example of relying on the crate for soundness, it's hard to give a complete review. Presumably you want to use get_unchecked
instead of indexing in resolve
and have it be sound. The change would rely on the implementation generally, not just the guarantees the crate wants to provide (although they are a crucial part).
If my interpretations are correct, I think the change would be sound. But the example should actually exercise the crate for soundness and have comments justifying it. Besides the crate, other necessary ingredients include
- Every branded interner is a singleton
- You require a token value to create the branded interner
- There's no way to recover the token value post-creation
- Only the branded interner can create branded indices, and they can't be modified
- You never shrink the
Vec
(The crate provides a way to create singletons and ensures the branding is unique.)
Other random comments
You would probably be interested in the GhostCell paper
, which contains another approach to branding.
I don't know that I like the name Sealed
, since it's not actually sealed. You could just make Token
an unsafe
trait instead, since you're not actually sealing and only adding obscurity.
Your TokenType<'a, _>
is covariant in 'a
. &'a mut T
is invariant in T
, but covariant in 'a
. Maybe you just wanted "exclusivity" and not "invariance"? I'm not sure. (If you wanted invariance, you don't have it; maybe you thought of something I didn't.)
The line idea to enhance errors is neat.
In your example I believe you only need the T: Token
bound on the constructor.