Safe Transmute: Check mutability before creating dst -> src obligation

- Only create dst -> src obligation if Dst is mutable
- Add some long comments to explain parts of the transmutability code that were
  unclear to me when reading
- Update/add tests
This commit is contained in:
Bryan Garza 2023-04-28 14:06:10 -07:00
parent db3275c962
commit 6266358237
18 changed files with 129 additions and 64 deletions

View file

@ -31,17 +31,20 @@ impl fmt::Debug for Byte {
pub(crate) trait Def: Debug + Hash + Eq + PartialEq + Copy + Clone {}
pub trait Ref: Debug + Hash + Eq + PartialEq + Copy + Clone {
fn min_align(&self) -> usize {
1
}
fn min_align(&self) -> usize;
fn is_mutable(&self) -> bool {
false
}
fn is_mutable(&self) -> bool;
}
impl Def for ! {}
impl Ref for ! {}
impl Ref for ! {
fn min_align(&self) -> usize {
unreachable!()
}
fn is_mutable(&self) -> bool {
unreachable!()
}
}
#[cfg(feature = "rustc")]
pub mod rustc {

View file

@ -1,4 +1,4 @@
#![feature(alloc_layout_extra, decl_macro, iterator_try_reduce, never_type, let_chains)]
#![feature(alloc_layout_extra, decl_macro, iterator_try_reduce, never_type)]
#![allow(dead_code, unused_variables)]
#![deny(rustc::untranslatable_diagnostic)]
#![deny(rustc::diagnostic_outside_of_impl)]

View file

@ -203,8 +203,29 @@ where
if let Some(answer) = cache.get(&(src_state, dst_state)) {
answer.clone()
} else {
debug!(?src_state, ?dst_state);
debug!(src = ?self.src);
debug!(dst = ?self.dst);
debug!(
src_transitions_len = self.src.transitions.len(),
dst_transitions_len = self.dst.transitions.len()
);
let answer = if dst_state == self.dst.accepting {
// truncation: `size_of(Src) >= size_of(Dst)`
//
// Why is truncation OK to do? Because even though the Src is bigger, all we care about
// is whether we have enough data for the Dst to be valid in accordance with what its
// type dictates.
// For example, in a u8 to `()` transmutation, we have enough data available from the u8
// to transmute it to a `()` (though in this case does `()` really need any data to
// begin with? It doesn't). Same thing with u8 to fieldless struct.
// Now then, why is something like u8 to bool not allowed? That is not because the bool
// is smaller in size, but rather because those 2 bits that we are re-interpreting from
// the u8 could introduce invalid states for the bool type.
//
// So, if it's possible to transmute to a smaller Dst by truncating, and we can guarantee
// that none of the actually-used data can introduce an invalid state for Dst's type, we
// are able to safely transmute, even with truncation.
Ok(None)
} else if src_state == self.src.accepting {
// extension: `size_of(Src) >= size_of(Dst)`
@ -259,6 +280,7 @@ where
// ...if `refs_answer` was computed lazily. The below early
// returns can be deleted without impacting the correctness of
// the algoritm; only its performance.
debug!(?bytes_answer);
match bytes_answer {
Err(_) if !self.assume.validity => return bytes_answer,
Ok(None) if self.assume.validity => return bytes_answer,