aside
Mea culpa; it was intended to stay positive/constructive but I can see how it could be interpreted otherwise. More "and that's how we got here" than anything else. The "working model" is a lot looser here than SB/TB as "working model" for the dynamic borrow rules.
It's only doing safe operations, so it's necessarily sound. It might potentially be incorrect and improperly return Some
if object addresses are allowed to overlap, but it'll always be sound.
The most provenance-aware way of writing that would be
fn subslice_offset<T>(outer: &[T], inner: &[T]) -> Option<usize> {
let outer_range = outer.as_ptr_range();
let inner_ptr = inner.as_ptr();
if outer_range.contains(inner_ptr) {
Some((inner_ptr.addr() - outer_range.start.addr()) / size_of::<T>())
} else {
None
}
}
(doing comparisons directly with pointers) but it does seem improper to require the use of wrapping_
semantics here instead of sub_ptr
.
As you observe in the earlier example (many highly aligned allocs), today's compilers will break the idea of .addr()
existing instead of having address overlapping happen. That example is actually the most I've questioned the viability of a strict PNVI model, since "obviously" p1.addr() == p2.addr()
should fold to false
when p1
and p2
are separate live allocations, but if you do that peephole optimization enough times, suddenly you're implying that more numbers exist than do.
The general expectation has been that pointers would always maintain a strict ordering (thus a < b && b < c
where a
and c
are within the same object implies b
is also within that same object if within any object). The typical current implementation is that testing for a specific order between allocations realizes them (puts them in the concrete allocation plane).
I'm now wondering if the only fully coherent model is for .addr()
to effectively have a side-effect of realizing the allocation. It'd be quite unfortunate to lose purity, but it's still a meaningfully less impactful side-effect than .expose_addr()
.
Or maybe we end up with some model between PVI and PNVI that can better approximate current behavior, e.g. (I just made up) "provenance via integers only if convenient" where integers may still have provenance attached for a demonically nondeterministic period after being derived from provenance-carrying pointers.