Description
TryFromBytes::is_bit_valid
is currently generic over aliasing, permitting both Shared
and Exclusive
Ptr
s.
In adding support for runtime-checked type-to-type transmutation (#1359), we ran into a problem. TryFromBytes::is_bit_valid
needs to be able to read the bytes of its referent, but cannot assume that those bytes are bit-valid, so it takes a Ptr<Self>
with Initialized
validity. Such a Ptr
is equivalent to a Ptr<[u8]>
with Valid
validity.
A checked transmute which uses TryFromBytes::is_bit_valid
thus takes a type through three representations:
Src
with validityValid
Dst
with validityInitialized
Dst
with validityValid
Current TryFromBytes
methods always take &[u8]
or &mut [u8]
as a source type. As a result, two representation paths are possible (note that, here, &
and &mut
are used as shorthand for shared or exclusive Ptr
s):
&[u8] -> &[u8] -> &Dst
&mut [u8] -> &mut [u8] -> &mut Dst
The middle types in both cases are actually Initialized
Ptr<Dst>
s, but these have the same validity as [u8]
.
When supporting source types other than [u8]
, this can become problematic. Consider transmuting a bool
into a bool
:
&bool -> &[u8] -> &bool
&mut bool -> &mut [u8] -> &mut bool
Shared references are not problematic, but exclusive references pose a problem: An Initialized
Ptr<bool>
, which is effectively a &mut [u8]
, can be used to write arbitrary bytes to its referent. However, this permits writing arbitrary bytes which will later be observed as a &mut bool
, which is unsound. Thus, the first step (&mut bool -> &mut [u8]
) is unsound.
It would be tempting to solve this using reborrowing: First reborrow &mut bool
as &bool
, then convert it to a Ptr<bool, (Shared, Valid)>
in order to call <bool as TryFromBytes>::is_bit_valid
. Unfortunately, this is insufficient: types with interior mutability can use this type to mutate their referent despite being behind a shared Ptr
.
The solution proposed by this issue is a read-only type. This could be instantiated as a ReadOnly
type a la #1760 or as a new aliasing mode, although the former is probably simpler. Unlike #1760, this ReadOnly
type would disable any mutation whatsoever, including through &mut
references. In particular, ReadOnly<T>: AsRef<T> where T: Immutable
.
Since neither &ReadOnly<T>
nor &mut ReadOnly<T>
permit mutation, sequences like the following would be sound:
&mut bool -> &mut ReadOnly<[u8]> -> &mut bool
&Cell<bool> -> &ReadOnly<Cell<[u8]>> -> &bool
In order to make this work, we'd need to figure out how to prevent a &mut ReadOnly<T>
from being overwritten in its entirety (e.g. via mem::swap
). This limitation may require us to abandon ReadOnly
as a type and instead make it an aliasing invariant.