@nikomatsakis I did some more refactoring after we talked yesterday, and now I have something that I am pretty happy with. It might be even nicer if we had type information on actual memory accesses, but alas, this is good enough I think. The key idea was to accept that we have two different "stack traversals":
*operator), typed, which checks whether the
Uniqtag is present and whether things are sufficiently frozen. This does not change the stack, but if it finds a problem that means we got UB.
Uniqpointer with a
Shritem because more reads are always okay. So this is more liberal than the deref check, but it does pop the stack until it finds something, invalidating pointers that may no longer be used. (We do not match
Shritems when writing, so that we can be sure after a write through a mutable reference that this is now again the exclusive ref to be used for this location.)
This means we have three core per-location borrow operations: deref, access, create.
A reborrow is then generally described as doing all of those operations (deref original ptr, then access with it [write access if we are creating a mutable borrow], then create new borrow), except that if we create a raw borrow, we might skip the last two steps.
I had to disable reborrowing in
UnsafeCell::get because that one needs to immediately take a raw ptr, or else the deref done as part of the reborrow chokes: It sees a shared reference and expects it to be frozen.
@RalfJ nice! This is not, I suppose, specific to
UnsafeCell::get, right? That is, this is basically one of those cases where the "special semantics" of
&x as *const u32 is important?
@nikomatsakis Yes, exactly.
Also after talking with a friend in the bus yesterday, I was able to make the special exception for raw borrows slightly less special
It is now a general "redundant reborrow" rule: For all reborrows, we check if they are redundant, and if they are we do not do them.
A reborrow is redundant if the new reference is already dereferencable, and if moreover its item in the stack lives above the item of the old reference in the stack.
Is there a case where this applies other than raw pointers? I guess
It certainly never applies for mutable references
I am still pondering whether this changes behavior for
the test suite did not see any change, that much I can say^^
No it does not change behavior. The redundancy case can only occur if the location is already frozen, in which case both a read access and pushing a new
Shr onto the stack do nothing.
The nice thing about this is that I can now assert that we will never push to a frozen stack, instead of having to handle that case. This will just not happen any more, it is caught by the redundancy check.