allocator: refactor for stabilisation#157428
Conversation
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
| /// - the allocator is mutated through public API taking `&mut` access (notably, | ||
| /// running the allocator's destructor is such a mutation), or |
There was a problem hiding this comment.
This guarantee seems fine on the surface, but I'm trying to wrap my head around what's actually being guaranteed here. Like, clearly, it'd be wildly unsafe to offer an invalidate_everything method on an allocator that just deletes the backing memory without requiring any of the things that are using it to be dropped, but this feels like it's opening the door for that kind of method "as long as you're careful" which, doesn't make a lot of sense.
Like, I'm trying to gauge what value is being gained by this guarantee and it mostly just feels like it's making things more confusing without actually helping.
There was a problem hiding this comment.
we're saying that such an invalidate_everything method is allowed to exist, and you can't rely on the allocator having not yet been dropped for soundness. in other words, so long as you hold a &alloc (thus preventing a &mut alloc from being created), you can trust the memory you have is fine; but if you lose the &alloc and get a new one back, your memory might have been scribbled over and you must act as such
There was a problem hiding this comment.
Okay, but wouldn't that be the same thing as the lifetime expiring as before? Technically, even though both of them are written as &A, you've gotten a new &A lifetime in that case.
There was a problem hiding this comment.
i think the extra guarantee here is that if you do hold a &mut alloc, you can call methods that take alloc by-shared-ref without worry but you can't pass the actual &mut to an untrusted function and expect your allocator to be okay at the end. but i agree that's not obviously guaranteed
There was a problem hiding this comment.
Isn't that just totally breaking the aliasing guarantees, though? Since that &mut reference wouldn't be unique.
There was a problem hiding this comment.
...you know, you make a good point. i'll revisit the reasoning for this, i recall adding this in response to something being brought up
There was a problem hiding this comment.
nvm, i'm being stupid. the following is the reason:
let mut alloc = SomeAllocator::new();
let ptr = alloc.allocate(...);
alloc.something_by_shared_ref();
// ptr is still guaranteed to be valid
alloc.trusted_method_by_unique_ref();
// ptr is still valid because we know for sure the method is trusted not to mess w/ allocator state
alloc.untrusted_method_by_unique_ref();
// ptr must be assumed to be maybe-invalid even if the lifetime of alloc is not expired and ptr hasn't yet been deallocatedThere was a problem hiding this comment.
I guess that I was technically thinking of Box whose lifecycle is intrinsically tied to the lifetime of the allocator parameter, whereas in this case if you just call alloc and dealloc manually the "lifetime" is not really tracked at all. So, yes, mutable borrows can happen on the allocator and it's fine, and you guarantee this doesn't happen by taking a non-mutable borrow.
There was a problem hiding this comment.
Seeing this thread from @RalfJung: #157428 (comment)
I think we probably also need to be careful about how we define the relationship between these rules and StaticAllocator, since "lifetime expiration" in those cases refers to the allocator value and not references in that case.
|
@rustbot author (mostly so you can more clearly signal when you think things are ready; I've commented here already so I'll see any additional changes for review as they're made) |
| /// | ||
| /// [`Pin`]: ../../core/pin/struct.Pin.html | ||
| #[unstable(feature = "allocator_api", issue = "32838")] | ||
| pub unsafe trait StaticAllocator: Allocator {} |
There was a problem hiding this comment.
I dropped the 'static bound here (and thus implicitly in Box::pin_in, etc.); since this trait is about being able to be lifetime-subtyped safely, it would mean that you need to be able to coerce to a StaticAllocator + 'a so the whole guarantee about "this is Actually Static I Promise" has weight. cc @rust-lang/opsem in case i did a bad here
There was a problem hiding this comment.
That's more of a @rust-lang/types question
This comment has been minimized.
This comment has been minimized.
|
Luckily |
|
The |
|
thanks, i hate it :D the plan for initial stabilisation is to not stabilise the 2nd param on |
|
upd: fixed i hope |
This comment has been minimized.
This comment has been minimized.
b66ef35 to
5af9c06
Compare
| /// Implementors must ensure that memory cannot be freed except via a call to | ||
| /// `Allocator::deallocate`, and that subtype coercion preserves this invariant. |
There was a problem hiding this comment.
| /// Implementors must ensure that memory cannot be freed except via a call to | |
| /// `Allocator::deallocate`, and that subtype coercion preserves this invariant. | |
| /// Implementors must ensure that memory cannot be freed except via a call to | |
| /// `Allocator::deallocate`, `Allocator::grow`, `Allocator::grow_zeroed`, or `Allocator::shrink`, | |
| /// and that subtype coercion preserves this invariant. |
| /// `Pin::clone`. Namely, an impl of `StaticAllocator for MyAllocator + 'long` guarantees that an | ||
| /// impl of `StaticAllocator for MyAllocator + 'short` would be sound to write. |
There was a problem hiding this comment.
| /// `Pin::clone`. Namely, an impl of `StaticAllocator for MyAllocator + 'long` guarantees that an | |
| /// impl of `StaticAllocator for MyAllocator + 'short` would be sound to write. | |
| /// `Pin::clone`. Namely, an impl of `StaticAllocator for MyAllocator<'long>` guarantees that an | |
| /// impl of `StaticAllocator for MyAllocator<'short>` would be sound to write (for covariant allocators). |
This comment has been minimized.
This comment has been minimized.
| unsafe impl<T, A> AllocatorClone for Box<T, A> | ||
| where | ||
| T: AllocatorClone, | ||
| // Otherwise, the clone impl *here* may unwind. | ||
| A: AllocatorClone, | ||
| Box<T, A>: Clone, |
There was a problem hiding this comment.
| unsafe impl<T, A> AllocatorClone for Box<T, A> | |
| where | |
| T: AllocatorClone, | |
| // Otherwise, the clone impl *here* may unwind. | |
| A: AllocatorClone, | |
| Box<T, A>: Clone, | |
| unsafe impl<T, A> AllocatorClone for Box<T, A> | |
| where | |
| T: AllocatorClone, | |
| // Otherwise, the clone impl *here* may unwind. | |
| A: AllocatorClone, |
The Box: Clone bound here is redundant
There was a problem hiding this comment.
i think we could relax this a little more to A: Allocator + Clone iff we do some abort-on-unwind guard in the clone impl of box, but i'm unsure it's worth it. i guess worth flagging as a thing to consider /shrug AllocatorClone & company could/should be made marker traits as well before they're stabilised so overlapping impls here aren't an issue
Co-authored-by: Tim (Theemathas) Chirananthavat <theemathas@gmail.com>
This comment has been minimized.
This comment has been minimized.
|
I think the safety requirements of More generally, in my eyes the source of the confusion is that we allow blocks to be invalidated when all equivalent allocators have their destructor run, are mutably accessed, or have a lifetime expire. Here, the lifetime of Therefore, I think the requirements should specify that all equivalent allocators must ensure that currently allocated blocks cannot be invalidated, except via a call to |
|
i agree and intended the wording to reflect that, i think i just forgot about it. the intent of the trait is an assertion that nothing on god's green earth could possibly delete an allocation except a call to |
|
i think this doc change fixes it? |
|
I think this could be written more succinctly but yeah that should be correct |
|
I think
Therefore, it is never possible for all equivalent versions of the If this is not sound, the requirements need to be adjusted. |
|
In general, using (Since we can |
|
But I think the requirements are strict enough to imply that, barring any weird typing quirks we missed. |
|
The job Click to see the possible cause of the failure (guessed by this bot)Important For more information how to resolve CI failures of this job, visit this link. |
View all comments
Adds my current proposal per the doc in #156882 and follow-up Zulip conversations (notably for dyn-compat) unstably.
r? libs