2021-07-03 12:18:13 -04:00
|
|
|
use std::fmt::{self, Debug};
|
|
|
|
|
use std::hash::Hash;
|
transmutability: Mark edges by ranges, not values
In the `Tree` and `Dfa` representations of a type's layout, store byte
ranges rather than needing to separately store each byte value. This
permits us to, for example, represent a `u8` using a single 0..=255 edge
in the DFA rather than using 256 separate edges.
This leads to drastic performance improvements. For example, on the
author's 2024 MacBook Pro, the time to convert the `Tree` representation
of a `u64` to its equivalent DFA representation drops from ~8.5ms to
~1us, a reduction of ~8,500x. See `bench_dfa_from_tree`.
Similarly, the time to execute a transmutability query from `u64` to
`u64` drops from ~35us to ~1.7us, a reduction of ~20x. See
`bench_transmute`.
2025-04-10 13:45:39 -07:00
|
|
|
use std::ops::RangeInclusive;
|
2021-07-03 12:18:13 -04:00
|
|
|
|
|
|
|
|
pub(crate) mod tree;
|
|
|
|
|
pub(crate) use tree::Tree;
|
|
|
|
|
|
|
|
|
|
pub(crate) mod dfa;
|
|
|
|
|
pub(crate) use dfa::Dfa;
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub(crate) struct Uninhabited;
|
|
|
|
|
|
transmutability: Mark edges by ranges, not values
In the `Tree` and `Dfa` representations of a type's layout, store byte
ranges rather than needing to separately store each byte value. This
permits us to, for example, represent a `u8` using a single 0..=255 edge
in the DFA rather than using 256 separate edges.
This leads to drastic performance improvements. For example, on the
author's 2024 MacBook Pro, the time to convert the `Tree` representation
of a `u64` to its equivalent DFA representation drops from ~8.5ms to
~1us, a reduction of ~8,500x. See `bench_dfa_from_tree`.
Similarly, the time to execute a transmutability query from `u64` to
`u64` drops from ~35us to ~1.7us, a reduction of ~20x. See
`bench_transmute`.
2025-04-10 13:45:39 -07:00
|
|
|
/// A range of byte values, or the uninit byte.
|
|
|
|
|
#[derive(Hash, Eq, PartialEq, Ord, PartialOrd, Clone, Copy)]
|
|
|
|
|
pub(crate) struct Byte {
|
|
|
|
|
// An inclusive-inclusive range. We use this instead of `RangeInclusive`
|
|
|
|
|
// because `RangeInclusive: !Copy`.
|
|
|
|
|
//
|
|
|
|
|
// `None` means uninit.
|
|
|
|
|
//
|
|
|
|
|
// FIXME(@joshlf): Optimize this representation. Some pairs of values (where
|
|
|
|
|
// `lo > hi`) are illegal, and we could use these to represent `None`.
|
|
|
|
|
range: Option<(u8, u8)>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Byte {
|
|
|
|
|
fn new(range: RangeInclusive<u8>) -> Self {
|
|
|
|
|
Self { range: Some((*range.start(), *range.end())) }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn from_val(val: u8) -> Self {
|
|
|
|
|
Self { range: Some((val, val)) }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(crate) fn uninit() -> Byte {
|
|
|
|
|
Byte { range: None }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Returns `None` if `self` is the uninit byte.
|
|
|
|
|
pub(crate) fn range(&self) -> Option<RangeInclusive<u8>> {
|
|
|
|
|
self.range.map(|(lo, hi)| lo..=hi)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Are any of the values in `self` transmutable into `other`?
|
|
|
|
|
///
|
|
|
|
|
/// Note two special cases: An uninit byte is only transmutable into another
|
|
|
|
|
/// uninit byte. Any byte is transmutable into an uninit byte.
|
|
|
|
|
pub(crate) fn transmutable_into(&self, other: &Byte) -> bool {
|
|
|
|
|
match (self.range, other.range) {
|
|
|
|
|
(None, None) => true,
|
|
|
|
|
(None, Some(_)) => false,
|
|
|
|
|
(Some(_), None) => true,
|
|
|
|
|
(Some((slo, shi)), Some((olo, ohi))) => slo <= ohi && olo <= shi,
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-07-03 12:18:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl fmt::Debug for Byte {
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
transmutability: Mark edges by ranges, not values
In the `Tree` and `Dfa` representations of a type's layout, store byte
ranges rather than needing to separately store each byte value. This
permits us to, for example, represent a `u8` using a single 0..=255 edge
in the DFA rather than using 256 separate edges.
This leads to drastic performance improvements. For example, on the
author's 2024 MacBook Pro, the time to convert the `Tree` representation
of a `u64` to its equivalent DFA representation drops from ~8.5ms to
~1us, a reduction of ~8,500x. See `bench_dfa_from_tree`.
Similarly, the time to execute a transmutability query from `u64` to
`u64` drops from ~35us to ~1.7us, a reduction of ~20x. See
`bench_transmute`.
2025-04-10 13:45:39 -07:00
|
|
|
match self.range {
|
|
|
|
|
None => write!(f, "uninit"),
|
|
|
|
|
Some((lo, hi)) => write!(f, "{lo}..={hi}"),
|
2021-07-03 12:18:13 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-17 17:58:34 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
|
impl From<u8> for Byte {
|
|
|
|
|
fn from(src: u8) -> Self {
|
transmutability: Mark edges by ranges, not values
In the `Tree` and `Dfa` representations of a type's layout, store byte
ranges rather than needing to separately store each byte value. This
permits us to, for example, represent a `u8` using a single 0..=255 edge
in the DFA rather than using 256 separate edges.
This leads to drastic performance improvements. For example, on the
author's 2024 MacBook Pro, the time to convert the `Tree` representation
of a `u64` to its equivalent DFA representation drops from ~8.5ms to
~1us, a reduction of ~8,500x. See `bench_dfa_from_tree`.
Similarly, the time to execute a transmutability query from `u64` to
`u64` drops from ~35us to ~1.7us, a reduction of ~20x. See
`bench_transmute`.
2025-04-10 13:45:39 -07:00
|
|
|
Self::from_val(src)
|
2025-04-17 17:58:34 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-26 16:49:25 +00:00
|
|
|
pub(crate) trait Def: Debug + Hash + Eq + PartialEq + Copy + Clone {
|
|
|
|
|
fn has_safety_invariants(&self) -> bool;
|
|
|
|
|
}
|
2023-04-21 16:49:36 -07:00
|
|
|
pub trait Ref: Debug + Hash + Eq + PartialEq + Copy + Clone {
|
2023-04-28 14:06:10 -07:00
|
|
|
fn min_align(&self) -> usize;
|
|
|
|
|
|
2024-03-13 00:11:36 +00:00
|
|
|
fn size(&self) -> usize;
|
|
|
|
|
|
2023-04-28 14:06:10 -07:00
|
|
|
fn is_mutable(&self) -> bool;
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-26 16:49:25 +00:00
|
|
|
impl Def for ! {
|
|
|
|
|
fn has_safety_invariants(&self) -> bool {
|
|
|
|
|
unreachable!()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-28 14:06:10 -07:00
|
|
|
impl Ref for ! {
|
2023-04-21 16:49:36 -07:00
|
|
|
fn min_align(&self) -> usize {
|
2023-04-28 14:06:10 -07:00
|
|
|
unreachable!()
|
2023-04-21 16:49:36 -07:00
|
|
|
}
|
2024-03-13 00:11:36 +00:00
|
|
|
fn size(&self) -> usize {
|
|
|
|
|
unreachable!()
|
|
|
|
|
}
|
2023-04-21 16:49:36 -07:00
|
|
|
fn is_mutable(&self) -> bool {
|
2023-04-28 14:06:10 -07:00
|
|
|
unreachable!()
|
2023-04-21 16:49:36 -07:00
|
|
|
}
|
|
|
|
|
}
|
2021-07-03 12:18:13 -04:00
|
|
|
|
transmutability: Mark edges by ranges, not values
In the `Tree` and `Dfa` representations of a type's layout, store byte
ranges rather than needing to separately store each byte value. This
permits us to, for example, represent a `u8` using a single 0..=255 edge
in the DFA rather than using 256 separate edges.
This leads to drastic performance improvements. For example, on the
author's 2024 MacBook Pro, the time to convert the `Tree` representation
of a `u64` to its equivalent DFA representation drops from ~8.5ms to
~1us, a reduction of ~8,500x. See `bench_dfa_from_tree`.
Similarly, the time to execute a transmutability query from `u64` to
`u64` drops from ~35us to ~1.7us, a reduction of ~20x. See
`bench_transmute`.
2025-04-10 13:45:39 -07:00
|
|
|
#[cfg(test)]
|
|
|
|
|
impl<const N: usize> Ref for [(); N] {
|
|
|
|
|
fn min_align(&self) -> usize {
|
|
|
|
|
N
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn size(&self) -> usize {
|
|
|
|
|
N
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn is_mutable(&self) -> bool {
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-03 12:18:13 -04:00
|
|
|
#[cfg(feature = "rustc")]
|
2023-04-21 16:49:36 -07:00
|
|
|
pub mod rustc {
|
2024-03-13 00:11:36 +00:00
|
|
|
use std::fmt::{self, Write};
|
2024-07-29 08:13:50 +10:00
|
|
|
|
2024-10-27 17:21:23 -07:00
|
|
|
use rustc_abi::Layout;
|
2021-07-03 12:18:13 -04:00
|
|
|
use rustc_middle::mir::Mutability;
|
2024-09-15 22:16:21 +02:00
|
|
|
use rustc_middle::ty::layout::{HasTyCtxt, LayoutCx, LayoutError};
|
2024-09-15 21:59:51 +02:00
|
|
|
use rustc_middle::ty::{self, Ty};
|
2021-07-03 12:18:13 -04:00
|
|
|
|
2022-08-02 14:44:23 +00:00
|
|
|
/// A reference in the layout.
|
2024-03-21 12:33:11 +00:00
|
|
|
#[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)]
|
2021-07-03 12:18:13 -04:00
|
|
|
pub struct Ref<'tcx> {
|
2023-04-21 16:49:36 -07:00
|
|
|
pub lifetime: ty::Region<'tcx>,
|
|
|
|
|
pub ty: Ty<'tcx>,
|
|
|
|
|
pub mutability: Mutability,
|
|
|
|
|
pub align: usize,
|
2024-03-13 00:11:36 +00:00
|
|
|
pub size: usize,
|
2021-07-03 12:18:13 -04:00
|
|
|
}
|
|
|
|
|
|
2023-04-21 16:49:36 -07:00
|
|
|
impl<'tcx> super::Ref for Ref<'tcx> {
|
|
|
|
|
fn min_align(&self) -> usize {
|
|
|
|
|
self.align
|
|
|
|
|
}
|
2021-07-03 12:18:13 -04:00
|
|
|
|
2024-03-13 00:11:36 +00:00
|
|
|
fn size(&self) -> usize {
|
|
|
|
|
self.size
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-21 16:49:36 -07:00
|
|
|
fn is_mutable(&self) -> bool {
|
|
|
|
|
match self.mutability {
|
|
|
|
|
Mutability::Mut => true,
|
|
|
|
|
Mutability::Not => false,
|
|
|
|
|
}
|
2021-07-03 12:18:13 -04:00
|
|
|
}
|
|
|
|
|
}
|
2023-04-21 16:49:36 -07:00
|
|
|
impl<'tcx> Ref<'tcx> {}
|
2021-07-03 12:18:13 -04:00
|
|
|
|
2024-03-13 00:11:36 +00:00
|
|
|
impl<'tcx> fmt::Display for Ref<'tcx> {
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
|
f.write_char('&')?;
|
|
|
|
|
if self.mutability == Mutability::Mut {
|
|
|
|
|
f.write_str("mut ")?;
|
|
|
|
|
}
|
|
|
|
|
self.ty.fmt(f)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-02 14:44:23 +00:00
|
|
|
/// A visibility node in the layout.
|
2021-07-03 12:18:13 -04:00
|
|
|
#[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)]
|
|
|
|
|
pub enum Def<'tcx> {
|
|
|
|
|
Adt(ty::AdtDef<'tcx>),
|
|
|
|
|
Variant(&'tcx ty::VariantDef),
|
|
|
|
|
Field(&'tcx ty::FieldDef),
|
|
|
|
|
Primitive,
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-26 16:49:25 +00:00
|
|
|
impl<'tcx> super::Def for Def<'tcx> {
|
|
|
|
|
fn has_safety_invariants(&self) -> bool {
|
|
|
|
|
// Rust presently has no notion of 'unsafe fields', so for now we
|
|
|
|
|
// make the conservative assumption that everything besides
|
|
|
|
|
// primitive types carry safety invariants.
|
|
|
|
|
self != &Self::Primitive
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-08-14 20:10:28 +00:00
|
|
|
|
|
|
|
|
pub(crate) fn layout_of<'tcx>(
|
2024-09-15 21:59:51 +02:00
|
|
|
cx: LayoutCx<'tcx>,
|
2024-08-14 20:10:28 +00:00
|
|
|
ty: Ty<'tcx>,
|
|
|
|
|
) -> Result<Layout<'tcx>, &'tcx LayoutError<'tcx>> {
|
|
|
|
|
use rustc_middle::ty::layout::LayoutOf;
|
2024-09-15 22:16:21 +02:00
|
|
|
let ty = cx.tcx().erase_regions(ty);
|
2024-08-14 20:10:28 +00:00
|
|
|
cx.layout_of(ty).map(|tl| tl.layout)
|
|
|
|
|
}
|
2021-07-03 12:18:13 -04:00
|
|
|
}
|