Ah. In other words, seen end-to-end, you're saying that PNVI-ae-udi allows a superset of the programs that PVI does.
I think the only case where that might not be true has to do with sneakily converting a pointer to an integer using some form of type punning, without going through pointer-to-integer instructions. Per previous discussion, it's not clear whether this can be allowed or not without breaking optimizability, especially if you don't have type-based alias analysis to fall back on. Yet there is no reason to disallow it in PVI. I suppose you could take PVI and then artificially disallow such sneaky conversions, in an attempt to guarantee it doesn't allow any programs that are not allowed by PNVI.
However, there are reasons to want to allow programs that are allowed by PNVI but not PVI. For one, it's nice to be able to reassure users that an integer is just an integer and doesn't carry any spooky hidden state around with it.
In addition, contrary to your statement at the beginning of the thread, separating integers from pointers can make it easier to implement CHERI. That's because it allows clearly differentiating pointer operations, which can be guaranteed to preserve runtime provenance, with integer operations, which are more expansive and cannot in general have that guarantee.
For example, breaking a pointer into individual bytes and then reconstituting it is something that compile-time provenance models typically want to allow, in order to support naive implementations of
memcpy. But under CHERI this is in general impossible. (If those bytes stay in registers, then it can work, because at least in Morello, both the lower and upper bounds of a capability are tracked separately from the value itself. But if the user sticks the byte in memory somewhere, then there's nowhere to put the bounds.)
So the rule would be: on CHERI, there is no
uintptr. If you want an opaque value that can store either a pointer or integer, use a pointer type. Sure, that breaks compatibility with Rust and C programs that assume that
uintptr_t can be used for opaque values. But even in C, many of those programs also assume that pointers and/or
uintptr_t can fit in
uint64_t, and will break anyway. In Rust, essentially every existing program is broken because they all use
usize to store pointers – unless we change
usize to 128-bit, but that would have its own problems.
That said, Rust may be forced to allow some subset of ptr->int->ptr if C implementers do, and they may want to allow it because it will make it easier to port some C programs, and because C doesn't have
wrapping_add so integers are the only way to do potentially-overflowing pointer arithmetic. So I guess we'll see?
Also, even in Rust, the use case of sticking extra data in pointer low/high bits is easier with integer types – though we could always add convenience methods the standard library to do that with pointer types.