Repository: rust-lang/rust
Pull request #140168: transmute: Mark edges by byte sets, not byte values
Author: login: joshlf
Labels: name: T-compiler
description: Relevant to the compiler team, which will review and decide on the PR/issue.
name: S-waiting-on-bors
description: Status: Waiting on bors to run and complete tests. Bors will change the label on completion.
name: merged-by-bors
description: This PR was explicitly merged by bors.
Created: 2025-04-22T18:33:00Z | Merged: 2025-04-24T08:49:45Z

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`.

r? @jswrenn 



Diff:
diff --git a/Cargo.lock b/Cargo.lock
index 49c1eb5b45f77..0553cf76f0a2e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4562,6 +4562,7 @@ dependencies = [
  "rustc_hir",
  "rustc_middle",
  "rustc_span",
+ "smallvec",
  "tracing",
 ]
 
diff --git a/compiler/rustc_transmute/Cargo.toml b/compiler/rustc_transmute/Cargo.toml
index f0c783b30020e..0250cc0ea0788 100644
--- a/compiler/rustc_transmute/Cargo.toml
+++ b/compiler/rustc_transmute/Cargo.toml
@@ -5,11 +5,13 @@ edition = "2024"
 
 [dependencies]
 # tidy-alphabetical-start
+itertools = "0.12"
 rustc_abi = { path = "../rustc_abi", optional = true }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_hir = { path = "../rustc_hir", optional = true }
 rustc_middle = { path = "../rustc_middle", optional = true }
 rustc_span = { path = "../rustc_span", optional = true }
+smallvec = "1.8.1"
 tracing = "0.1"
 # tidy-alphabetical-end
 
@@ -20,8 +22,3 @@ rustc = [
     "dep:rustc_middle",
     "dep:rustc_span",
 ]
-
-[dev-dependencies]
-# tidy-alphabetical-start
-itertools = "0.12"
-# tidy-alphabetical-end
diff --git a/compiler/rustc_transmute/src/layout/dfa.rs b/compiler/rustc_transmute/src/layout/dfa.rs
index bb909c54d2bc3..d1f58157b696b 100644
--- a/compiler/rustc_transmute/src/layout/dfa.rs
+++ b/compiler/rustc_transmute/src/layout/dfa.rs
@@ -1,8 +1,9 @@
 use std::fmt;
+use std::ops::RangeInclusive;
 use std::sync::atomic::{AtomicU32, Ordering};
 
 use super::{Byte, Ref, Tree, Uninhabited};
-use crate::Map;
+use crate::{Map, Set};
 
 #[derive(PartialEq)]
 #[cfg_attr(test, derive(Clone))]
@@ -20,7 +21,7 @@ pub(crate) struct Transitions<R>
 where
     R: Ref,
 {
-    byte_transitions: Map<Byte, State>,
+    byte_transitions: EdgeSet<State>,
     ref_transitions: Map<R, State>,
 }
 
@@ -29,7 +30,7 @@ where
     R: Ref,
 {
     fn default() -> Self {
-        Self { byte_transitions: Map::default(), ref_transitions: Map::default() }
+        Self { byte_transitions: EdgeSet::empty(), ref_transitions: Map::default() }
     }
 }
 
@@ -56,15 +57,10 @@ where
 {
     #[cfg(test)]
     pub(crate) fn bool() -> Self {
-        let mut transitions: Map<State, Transitions<R>> = Map::default();
-        let start = State::new();
-        let accept = State::new();
-
-        transitions.entry(start).or_default().byte_transitions.insert(Byte::Init(0x00), accept);
-
-        transitions.entry(start).or_default().byte_transitions.insert(Byte::Init(0x01), accept);
-
-        Self { transitions, start, accept }
+        Self::from_transitions(|accept| Transitions {
+            byte_transitions: EdgeSet::new(Byte::new(0x00..=0x01), accept),
+            ref_transitions: Map::default(),
+        })
     }
 
     pub(crate) fn unit() -> Self {
@@ -76,23 +72,24 @@ where
     }
 
     pub(crate) fn from_byte(byte: Byte) -> Self {
-        let mut transitions: Map<State, Transitions<R>> = Map::default();
-        let start = State::new();
-        let accept = State::new();
-
-        transitions.entry(start).or_default().byte_transitions.insert(byte, accept);
-
-        Self { transitions, start, accept }
+        Self::from_transitions(|accept| Transitions {
+            byte_transitions: EdgeSet::new(byte, accept),
+            ref_transitions: Map::default(),
+        })
     }
 
     pub(crate) fn from_ref(r: R) -> Self {
-        let mut transitions: Map<State, Transitions<R>> = Map::default();
+        Self::from_transitions(|accept| Transitions {
+            byte_transitions: EdgeSet::empty(),
+            ref_transitions: [(r, accept)].into_iter().collect(),
+        })
+    }
+
+    fn from_transitions(f: impl FnOnce(State) -> Transitions<R>) -> Self {
         let start = State::new();
         let accept = State::new();
 
-        transitions.entry(start).or_default().ref_transitions.insert(r, accept);
-
-        Self { transitions, start, accept }
+        Self { transitions: [(start, f(accept))].into_iter().collect(), start, accept }
     }
 
     pub(crate) fn from_tree(tree: Tree<!, R>) -> Result<Self, Uninhabited> {
@@ -132,13 +129,16 @@ where
 
         for (source, transition) in other.transitions {
             let fix_state = |state| if state == other.start { self.accept } else { state };
-            let entry = transitions.entry(fix_state(source)).or_default();
-            for (edge, destination) in transition.byte_transitions {
-                entry.byte_transitions.insert(edge, fix_state(destination));
-            }
-            for (edge, destination) in transition.ref_transitions {
-                entry.ref_transitions.insert(edge, fix_state(destination));
-            }
+            let byte_transitions = transition.byte_transitions.map_states(&fix_state);
+            let ref_transitions = transition
+                .ref_transitions
+                .into_iter()
+                .map(|(r, state)| (r, fix_state(state)))
+                .collect();
+
+            let old = transitions
+                .insert(fix_state(source), Transitions { byte_transitions, ref_transitions });
+            assert!(old.is_none());
         }
 
         Self { transitions, start, accept }
@@ -170,67 +170,111 @@ where
 
         let start = mapped((Some(a.start), Some(b.start)));
         let mut transitions: Map<State, Transitions<R>> = Map::default();
-        let mut queue = vec![(Some(a.start), Some(b.start))];
         let empty_transitions = Transitions::default();
 
-        while let Some((a_src, b_src)) = queue.pop() {
+        struct WorkQueue {
+            queue: Vec<(Option<State>, Option<State>)>,
+            // Track all entries ever enqueued to avoid duplicating work. This
+            // gives us a guarantee that a given (a_state, b_state) pair will
+            // only ever be visited once.
+            enqueued: Set<(Option<State>, Option<State>)>,
+        }
+        impl WorkQueue {
+            fn enqueue(&mut self, a_state: Option<State>, b_state: Option<State>) {
+                if self.enqueued.insert((a_state, b_state)) {
+                    self.queue.push((a_state, b_state));
+                }
+            }
+        }
+        let mut queue = WorkQueue { queue: Vec::new(), enqueued: Set::default() };
+        queue.enqueue(Some(a.start), Some(b.start));
+
+        while let Some((a_src, b_src)) = queue.queue.pop() {
+            let src = mapped((a_src, b_src));
+            if src == accept {
+                // While it's possible to have a DFA whose accept state has
+                // out-edges, these do not affect the semantics of the DFA, and
+                // so there's no point in processing them. Continuing here also
+                // has the advantage of guaranteeing that we only ever process a
+                // given node in the output DFA once. In particular, with the
+                // exception of the accept state, we ensure that we only push a
+                // given node to the `queue` once. This allows the following
+                // code to assume that we're processing a node we've never
+                // processed before, which means we never need to merge two edge
+                // sets - we only ever need to construct a new edge set from
+                // whole cloth.
+                continue;
+            }
+
             let a_transitions =
                 a_src.and_then(|a_src| a.transitions.get(&a_src)).unwrap_or(&empty_transitions);
             let b_transitions =
                 b_src.and_then(|b_src| b.transitions.get(&b_src)).unwrap_or(&empty_transitions);
 
             let byte_transitions =
-                a_transitions.byte_transitions.keys().chain(b_transitions.byte_transitions.keys());
-
-            for byte_transition in byte_transitions {
-                let a_dst = a_transitions.byte_transitions.get(byte_transition).copied();
-                let b_dst = b_transitions.byte_transitions.get(byte_transition).copied();
+                a_transitions.byte_transitions.union(&b_transitions.byte_transitions);
 
+            let byte_transitions = byte_transitions.map_states(|(a_dst, b_dst)| {
                 assert!(a_dst.is_some() || b_dst.is_some());
 
-                let src = mapped((a_src, b_src));
-                let dst = mapped((a_dst, b_dst));
-
-                transitions.entry(src).or_default().byte_transitions.insert(*byte_transition, dst);
-
-                if !transitions.contains_key(&dst) {
-                    queue.push((a_dst, b_dst))
-                }
-            }
+                queue.enqueue(a_dst, b_dst);
+                mapped((a_dst, b_dst))
+            });
 
             let ref_transitions =
                 a_transitions.ref_transitions.keys().chain(b_transitions.ref_transitions.keys());
 
-            for ref_transition in ref_transitions {
-                let a_dst = a_transitions.ref_transitions.get(ref_transition).copied();
-                let b_dst = b_transitions.ref_transitions.get(ref_transition).copied();
+            let ref_transitions = ref_transitions
+                .map(|ref_transition| {
+                    let a_dst = a_transitions.ref_transitions.get(ref_transition).copied();
+                    let b_dst = b_transitions.ref_transitions.get(ref_transition).copied();
 
-                assert!(a_dst.is_some() || b_dst.is_some());
-
-                let src = mapped((a_src, b_src));
-                let dst = mapped((a_dst, b_dst));
+                    assert!(a_dst.is_some() || b_dst.is_some());
 
-                transitions.entry(src).or_default().ref_transitions.insert(*ref_transition, dst);
+                    queue.enqueue(a_dst, b_dst);
+                    (*ref_transition, mapped((a_dst, b_dst)))
+                })
+                .collect();
 
-                if !transitions.contains_key(&dst) {
-                    queue.push((a_dst, b_dst))
-                }
-            }
+            let old = transitions.insert(src, Transitions { byte_transitions, ref_transitions });
+            // See `if src == accept { ... }` above. The comment there explains
+            // why this assert is valid.
+            assert_eq!(old, None);
         }
 
         Self { transitions, start, accept }
     }
 
-    pub(crate) fn bytes_from(&self, start: State) -> Option<&Map<Byte, State>> {
-        Some(&self.transitions.get(&start)?.byte_transitions)
+    pub(crate) fn states_from(
+        &self,
+        state: State,
+        src_validity: RangeInclusive<u8>,
+    ) -> impl Iterator<Item = (Byte, State)> {
+        self.transitions
+            .get(&state)
+            .map(move |t| t.byte_transitions.states_from(src_validity))
+            .into_iter()
+            .flatten()
+    }
+
+    pub(crate) fn get_uninit_edge_dst(&self, state: State) -> Option<State> {
+        let transitions = self.transitions.get(&state)?;
+        transitions.byte_transitions.get_uninit_edge_dst()
     }
 
-    pub(crate) fn byte_from(&self, start: State, byte: Byte) -> Option<State> {
-        self.transitions.get(&start)?.byte_transitions.get(&byte).copied()
+    pub(crate) fn bytes_from(&self, start: State) -> impl Iterator<Item = (Byte, State)> {
+        self.transitions
+            .get(&start)
+            .into_iter()
+            .flat_map(|transitions| transitions.byte_transitions.iter())
     }
 
-    pub(crate) fn refs_from(&self, start: State) -> Option<&Map<R, State>> {
-        Some(&self.transitions.get(&start)?.ref_transitions)
+    pub(crate) fn refs_from(&self, start: State) -> impl Iterator<Item = (R, State)> {
+        self.transitions
+            .get(&start)
+            .into_iter()
+            .flat_map(|transitions| transitions.ref_transitions.iter())
+            .map(|(r, s)| (*r, *s))
     }
 
     #[cfg(test)]
@@ -241,15 +285,25 @@ where
     ) -> Self {
         let start = State(start);
         let accept = State(accept);
-        let mut transitions: Map<State, Transitions<R>> = Map::default();
+        let mut transitions: Map<State, Vec<(Byte, State)>> = Map::default();
 
-        for &(src, edge, dst) in edges {
-            let src = State(src);
-            let dst = State(dst);
-            let old = transitions.entry(src).or_default().byte_transitions.insert(edge.into(), dst);
-            assert!(old.is_none());
+        for (src, edge, dst) in edges.iter().copied() {
+            transitions.entry(State(src)).or_default().push((edge.into(), State(dst)));
         }
 
+        let transitions = transitions
+            .into_iter()
+            .map(|(src, edges)| {
+                (
+                    src,
+                    Transitions {
+                        byte_transitions: EdgeSet::from_edges(edges),
+                        ref_transitions: Map::default(),
+                    },
+                )
+            })
+            .collect();
+
         Self { start, accept, transitions }
     }
 }
@@ -277,3 +331,242 @@ where
         writeln!(f, "}}")
     }
 }
+
+use edge_set::EdgeSet;
+mod edge_set {
+    use std::cmp;
+
+    use run::*;
+    use smallvec::{SmallVec, smallvec};
+
+    use super::*;
+    mod run {
+        use std::ops::{Range, RangeInclusive};
+
+        use super::*;
+        use crate::layout::Byte;
+
+        /// A logical set of edges.
+        ///
+        /// A `Run` encodes one edge for every byte value in `start..=end`
+        /// pointing to `dst`.
+        #[derive(Eq, PartialEq, Copy, Clone, Debug)]
+        pub(super) struct Run<S> {
+            // `start` and `end` are both inclusive (ie, closed) bounds, as this
+            // is required in order to be able to store 0..=255. We provide
+            // setters and getters which operate on closed/open ranges, which
+            // are more intuitive and easier for performing offset math.
+            start: u8,
+            end: u8,
+            pub(super) dst: S,
+        }
+
+        impl<S> Run<S> {
+            pub(super) fn new(range: RangeInclusive<u8>, dst: S) -> Self {
+                Self { start: *range.start(), end: *range.end(), dst }
+            }
+
+            pub(super) fn from_inclusive_exclusive(range: Range<u16>, dst: S) -> Self {
+                Self {
+                    start: range.start.try_into().unwrap(),
+                    end: (range.end - 1).try_into().unwrap(),
+                    dst,
+                }
+            }
+
+            pub(super) fn contains(&self, idx: u16) -> bool {
+                idx >= u16::from(self.start) && idx <= u16::from(self.end)
+            }
+
+            pub(super) fn as_inclusive_exclusive(&self) -> (u16, u16) {
+                (u16::from(self.start), u16::from(self.end) + 1)
+            }
+
+            pub(super) fn as_byte(&self) -> Byte {
+                Byte::new(self.start..=self.end)
+            }
+
+            pub(super) fn map_state<SS>(self, f: impl FnOnce(S) -> SS) -> Run<SS> {
+                let Run { start, end, dst } = self;
+                Run { start, end, dst: f(dst) }
+            }
+
+            /// Produces a new `Run` whose lower bound is the greater of
+            /// `self`'s existing lower bound and `lower_bound`.
+            pub(super) fn clamp_lower(self, lower_bound: u8) -> Self {
+                let Run { start, end, dst } = self;
+                Run { start: cmp::max(start, lower_bound), end, dst }
+            }
+        }
+    }
+
+    /// The set of outbound byte edges associated with a DFA node (not including
+    /// reference edges).
+    #[derive(Eq, PartialEq, Clone, Debug)]
+    pub(super) struct EdgeSet<S = State> {
+        // A sequence of runs stored in ascending order. Since the graph is a
+        // DFA, these must be non-overlapping with one another.
+        runs: SmallVec<[Run<S>; 1]>,
+        // The edge labeled with the uninit byte, if any.
+        //
+        // FIXME(@joshlf): Make `State` a `NonZero` so that this is NPO'd.
+        uninit: Option<S>,
+    }
+
+    impl<S> EdgeSet<S> {
+        pub(crate) fn new(byte: Byte, dst: S) -> Self {
+            match byte.range() {
+                Some(range) => Self { runs: smallvec![Run::new(range, dst)], uninit: None },
+                None => Self { runs: SmallVec::new(), uninit: Some(dst) },
+            }
+        }
+
+        pub(crate) fn empty() -> Self {
+            Self { runs: SmallVec::new(), uninit: None }
+        }
+
+        #[cfg(test)]
+        pub(crate) fn from_edges(mut edges: Vec<(Byte, S)>) -> Self
+        where
+            S: Ord,
+        {
+            edges.sort();
+            Self {
+                runs: edges
+                    .into_iter()
+                    .map(|(byte, state)| Run::new(byte.range().unwrap(), state))
+                    .collect(),
+                uninit: None,
+            }
+        }
+
+        pub(crate) fn iter(&self) -> impl Iterator<Item = (Byte, S)>
+        where
+            S: Copy,
+        {
+            self.uninit
+                .map(|dst| (Byte::uninit(), dst))
+                .into_iter()
+                .chain(self.runs.iter().map(|run| (run.as_byte(), run.dst)))
+        }
+
+        pub(crate) fn states_from(
+            &self,
+            byte: RangeInclusive<u8>,
+        ) -> impl Iterator<Item = (Byte, S)>
+        where
+            S: Copy,
+        {
+            // FIXME(@joshlf): Optimize this. A manual scan over `self.runs` may
+            // permit us to more efficiently discard runs which will not be
+            // produced by this iterator.
+            self.iter().filter(move |(o, _)| Byte::new(byte.clone()).transmutable_into(&o))
+        }
+
+        pub(crate) fn get_uninit_edge_dst(&self) -> Option<S>
+        where
+            S: Copy,
+        {
+            self.uninit
+        }
+
+        pub(crate) fn map_states<SS>(self, mut f: impl FnMut(S) -> SS) -> EdgeSet<SS> {
+            EdgeSet {
+                // NOTE: It appears as through `<Vec<_> as
+                // IntoIterator>::IntoIter` and `std::iter::Map` both implement
+                // `TrustedLen`, which in turn means that this `.collect()`
+                // allocates the correct number of elements once up-front [1].
+                //
+                // [1] https://doc.rust-lang.org/1.85.0/src/alloc/vec/spec_from_iter_nested.rs.html#47
+                runs: self.runs.into_iter().map(|run| run.map_state(&mut f)).collect(),
+                uninit: self.uninit.map(f),
+            }
+        }
+
+        /// Unions two edge sets together.
+        ///
+        /// If `u = a.union(b)`, then for each byte value, `u` will have an edge
+        /// with that byte value and with the destination `(Some(_), None)`,
+        /// `(None, Some(_))`, or `(Some(_), Some(_))` depending on whether `a`,
+        /// `b`, or both have an edge with that byte value.
+        ///
+        /// If neither `a` nor `b` have an edge with a particular byte value,
+        /// then no edge with that value will be present in `u`.
+        pub(crate) fn union(&self, other: &Self) -> EdgeSet<(Option<S>, Option<S>)>
+        where
+            S: Copy,
+        {
+            let uninit = match (self.uninit, other.uninit) {
+                (None, None) => None,
+                (s, o) => Some((s, o)),
+            };
+
+            let mut runs = SmallVec::new();
+
+            // Iterate over `self.runs` and `other.runs` simultaneously,
+            // advancing `idx` as we go. At each step, we advance `idx` as far
+            // as we can without crossing a run boundary in either `self.runs`
+            // or `other.runs`.
+
+            // INVARIANT: `idx < s[0].end && idx < o[0].end`.
+            let (mut s, mut o) = (self.runs.as_slice(), other.runs.as_slice());
+            let mut idx = 0u16;
+            while let (Some((s_run, s_rest)), Some((o_run, o_rest))) =
+                (s.split_first(), o.split_first())
+            {
+                let (s_start, s_end) = s_run.as_inclusive_exclusive();
+                let (o_start, o_end) = o_run.as_inclusive_exclusive();
+
+                // Compute `end` as the end of the current run (which starts
+                // with `idx`).
+                let (end, dst) = match (s_run.contains(idx), o_run.contains(idx)) {
+                    // `idx` is in an existing run in both `s` and `o`, so `end`
+                    // is equal to the smallest of the two ends of those runs.
+                    (true, true) => (cmp::min(s_end, o_end), (Some(s_run.dst), Some(o_run.dst))),
+                    // `idx` is in an existing run in `s`, but not in any run in
+                    // `o`. `end` is either the end of the `s` run or the
+                    // beginning of the next `o` run, whichever comes first.
+                    (true, false) => (cmp::min(s_end, o_start), (Some(s_run.dst), None)),
+                    // The inverse of the previous case.
+                    (false, true) => (cmp::min(s_start, o_end), (None, Some(o_run.dst))),
+                    // `idx` is not in a run in either `s` or `o`, so advance it
+                    // to the beginning of the next run.
+                    (false, false) => {
+                        idx = cmp::min(s_start, o_start);
+                        continue;
+                    }
+                };
+
+                // FIXME(@joshlf): If this is contiguous with the previous run
+                // and has the same `dst`, just merge it into that run rather
+                // than adding a new one.
+                runs.push(Run::from_inclusive_exclusive(idx..end, dst));
+                idx = end;
+
+                if idx >= s_end {
+                    s = s_rest;
+                }
+                if idx >= o_end {
+                    o = o_rest;
+                }
+            }
+
+            // At this point, either `s` or `o` have been exhausted, so the
+            // remaining elements in the other slice are guaranteed to be
+            // non-overlapping. We can add all remaining runs to `runs` with no
+            // further processing.
+            if let Ok(idx) = u8::try_from(idx) {
+                let (slc, map) = if !s.is_empty() {
+                    let map: fn(_) -> _ = |st| (Some(st), None);
+                    (s, map)
+                } else {
+                    let map: fn(_) -> _ = |st| (None, Some(st));
+                    (o, map)
+                };
+                runs.extend(slc.iter().map(|run| run.clamp_lower(idx).map_state(map)));
+            }
+
+            EdgeSet { runs, uninit }
+        }
+    }
+}
diff --git a/compiler/rustc_transmute/src/layout/mod.rs b/compiler/rustc_transmute/src/layout/mod.rs
index c940f7c42a82f..d555ea702a9fe 100644
--- a/compiler/rustc_transmute/src/layout/mod.rs
+++ b/compiler/rustc_transmute/src/layout/mod.rs
@@ -1,5 +1,6 @@
 use std::fmt::{self, Debug};
 use std::hash::Hash;
+use std::ops::RangeInclusive;
 
 pub(crate) mod tree;
 pub(crate) use tree::Tree;
@@ -10,18 +11,56 @@ pub(crate) use dfa::Dfa;
 #[derive(Debug)]
 pub(crate) struct Uninhabited;
 
-/// An instance of a byte is either initialized to a particular value, or uninitialized.
-#[derive(Hash, Eq, PartialEq, Clone, Copy)]
-pub(crate) enum Byte {
-    Uninit,
-    Init(u8),
+/// 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,
+        }
+    }
 }
 
 impl fmt::Debug for Byte {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match &self {
-            Self::Uninit => f.write_str("??u8"),
-            Self::Init(b) => write!(f, "{b:#04x}u8"),
+        match self.range {
+            None => write!(f, "uninit"),
+            Some((lo, hi)) => write!(f, "{lo}..={hi}"),
         }
     }
 }
@@ -29,7 +68,7 @@ impl fmt::Debug for Byte {
 #[cfg(test)]
 impl From<u8> for Byte {
     fn from(src: u8) -> Self {
-        Self::Init(src)
+        Self::from_val(src)
     }
 }
 
@@ -62,6 +101,21 @@ impl Ref for ! {
     }
 }
 
+#[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
+    }
+}
+
 #[cfg(feature = "rustc")]
 pub mod rustc {
     use std::fmt::{self, Write};
diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs
index 70ecc75403fd8..6a09be18ef944 100644
--- a/compiler/rustc_transmute/src/layout/tree.rs
+++ b/compiler/rustc_transmute/src/layout/tree.rs
@@ -54,22 +54,22 @@ where
 
     /// A `Tree` containing a single, uninitialized byte.
     pub(crate) fn uninit() -> Self {
-        Self::Byte(Byte::Uninit)
+        Self::Byte(Byte::uninit())
     }
 
     /// A `Tree` representing the layout of `bool`.
     pub(crate) fn bool() -> Self {
-        Self::from_bits(0x00).or(Self::from_bits(0x01))
+        Self::Byte(Byte::new(0x00..=0x01))
     }
 
     /// A `Tree` whose layout matches that of a `u8`.
     pub(crate) fn u8() -> Self {
-        Self::Alt((0u8..=255).map(Self::from_bits).collect())
+        Self::Byte(Byte::new(0x00..=0xFF))
     }
 
     /// A `Tree` whose layout accepts exactly the given bit pattern.
     pub(crate) fn from_bits(bits: u8) -> Self {
-        Self::Byte(Byte::Init(bits))
+        Self::Byte(Byte::from_val(bits))
     }
 
     /// A `Tree` whose layout is a number of the given width.
diff --git a/compiler/rustc_transmute/src/lib.rs b/compiler/rustc_transmute/src/lib.rs
index 76fa6ceabe7e7..ce18dad55179c 100644
--- a/compiler/rustc_transmute/src/lib.rs
+++ b/compiler/rustc_transmute/src/lib.rs
@@ -1,8 +1,9 @@
 // tidy-alphabetical-start
+#![cfg_attr(test, feature(test))]
 #![feature(never_type)]
 // tidy-alphabetical-end
 
-pub(crate) use rustc_data_structures::fx::FxIndexMap as Map;
+pub(crate) use rustc_data_structures::fx::{FxIndexMap as Map, FxIndexSet as Set};
 
 pub mod layout;
 mod maybe_transmutable;
diff --git a/compiler/rustc_transmute/src/maybe_transmutable/mod.rs b/compiler/rustc_transmute/src/maybe_transmutable/mod.rs
index db0e1ab8e986a..0a19cccc2ed03 100644
--- a/compiler/rustc_transmute/src/maybe_transmutable/mod.rs
+++ b/compiler/rustc_transmute/src/maybe_transmutable/mod.rs
@@ -1,10 +1,14 @@
+use std::rc::Rc;
+use std::{cmp, iter};
+
+use itertools::Either;
 use tracing::{debug, instrument, trace};
 
 pub(crate) mod query_context;
 #[cfg(test)]
 mod tests;
 
-use crate::layout::{self, Byte, Def, Dfa, Ref, Tree, Uninhabited, dfa};
+use crate::layout::{self, Byte, Def, Dfa, Ref, Tree, dfa};
 use crate::maybe_transmutable::query_context::QueryContext;
 use crate::{Answer, Condition, Map, Reason};
 
@@ -111,7 +115,7 @@ where
         // the `src` type do not exist.
         let src = match Dfa::from_tree(src) {
             Ok(src) => src,
-            Err(Uninhabited) => return Answer::Yes,
+            Err(layout::Uninhabited) => return Answer::Yes,
         };
 
         // Convert `dst` from a tree-based representation to an DFA-based
@@ -122,7 +126,7 @@ where
         // free of safety invariants.
         let dst = match Dfa::from_tree(dst) {
             Ok(dst) => dst,
-            Err(Uninhabited) => return Answer::No(Reason::DstMayHaveSafetyInvariants),
+            Err(layout::Uninhabited) => return Answer::No(Reason::DstMayHaveSafetyInvariants),
         };
 
         MaybeTransmutableQuery { src, dst, assume, context }.answer()
@@ -174,8 +178,8 @@ where
                 // are able to safely transmute, even with truncation.
                 Answer::Yes
             } else if src_state == self.src.accept {
-                // extension: `size_of(Src) >= size_of(Dst)`
-                if let Some(dst_state_prime) = self.dst.byte_from(dst_state, Byte::Uninit) {
+                // extension: `size_of(Src) <= size_of(Dst)`
+                if let Some(dst_state_prime) = self.dst.get_uninit_edge_dst(dst_state) {
                     self.answer_memo(cache, src_state, dst_state_prime)
                 } else {
                     Answer::No(Reason::DstIsTooBig)
@@ -193,26 +197,120 @@ where
                     Quantifier::ForAll
                 };
 
+                let c = &core::cell::RefCell::new(&mut *cache);
                 let bytes_answer = src_quantifier.apply(
-                    // for each of the byte transitions out of the `src_state`...
-                    self.src.bytes_from(src_state).unwrap_or(&Map::default()).into_iter().map(
-                        |(&src_validity, &src_state_prime)| {
-                            // ...try to find a matching transition out of `dst_state`.
-                            if let Some(dst_state_prime) =
-                                self.dst.byte_from(dst_state, src_validity)
-                            {
-                                self.answer_memo(cache, src_state_prime, dst_state_prime)
-                            } else if let Some(dst_state_prime) =
-                                // otherwise, see if `dst_state` has any outgoing `Uninit` transitions
-                                // (any init byte is a valid uninit byte)
-                                self.dst.byte_from(dst_state, Byte::Uninit)
-                            {
-                                self.answer_memo(cache, src_state_prime, dst_state_prime)
-                            } else {
-                                // otherwise, we've exhausted our options.
-                                // the DFAs, from this point onwards, are bit-incompatible.
-                                Answer::No(Reason::DstIsBitIncompatible)
+                    // for each of the byte set transitions out of the `src_state`...
+                    self.src.bytes_from(src_state).flat_map(
+                        move |(src_validity, src_state_prime)| {
+                            // ...find all matching transitions out of `dst_state`.
+
+                            let Some(src_validity) = src_validity.range() else {
+                                // NOTE: We construct an iterator here rather
+                                // than just computing the value directly (via
+                                // `self.answer_memo`) so that, if the iterator
+                                // we produce from this branch is
+                                // short-circuited, we don't waste time
+                                // computing `self.answer_memo` unnecessarily.
+                                // That will specifically happen if
+                                // `src_quantifier == Quantifier::ThereExists`,
+                                // since we emit `Answer::Yes` first (before
+                                // chaining `answer_iter`).
+                                let answer_iter = if let Some(dst_state_prime) =
+                                    self.dst.get_uninit_edge_dst(dst_state)
+                                {
+                                    Either::Left(iter::once_with(move || {
+                                        let mut c = c.borrow_mut();
+                                        self.answer_memo(&mut *c, src_state_prime, dst_state_prime)
+                                    }))
+                                } else {
+                                    Either::Right(iter::once(Answer::No(
+                                        Reason::DstIsBitIncompatible,
+                                    )))
+                                };
+
+                                // When `answer == Answer::No(...)`, there are
+                                // two cases to consider:
+                                // - If `assume.validity`, then we should
+                                //   succeed because the user is responsible for
+                                //   ensuring that the *specific* byte value
+                                //   appearing at runtime is valid for the
+                                //   destination type. When `assume.validity`,
+                                //   `src_quantifier ==
+                                //   Quantifier::ThereExists`, so adding an
+                                //   `Answer::Yes` has the effect of ensuring
+                                //   that the "there exists" is always
+                                //   satisfied.
+                                // - If `!assume.validity`, then we should fail.
+                                //   In this case, `src_quantifier ==
+                                //   Quantifier::ForAll`, so adding an
+                                //   `Answer::Yes` has no effect.
+                                return Either::Left(iter::once(Answer::Yes).chain(answer_iter));
+                            };
+
+                            #[derive(Copy, Clone, Debug)]
+                            struct Accum {
+                                // The number of matching byte edges that we
+                                // have found in the destination so far.
+                                sum: usize,
+                                found_uninit: bool,
                             }
+
+                            let accum1 = Rc::new(std::cell::Cell::new(Accum {
+                                sum: 0,
+                                found_uninit: false,
+                            }));
+                            let accum2 = Rc::clone(&accum1);
+                            let sv = src_validity.clone();
+                            let update_accum = move |mut accum: Accum, dst_validity: Byte| {
+                                if let Some(dst_validity) = dst_validity.range() {
+                                    // Only add the part of `dst_validity` that
+                                    // overlaps with `src_validity`.
+                                    let start = cmp::max(*sv.start(), *dst_validity.start());
+                                    let end = cmp::min(*sv.end(), *dst_validity.end());
+
+                                    // We add 1 here to account for the fact
+                                    // that `end` is an inclusive bound.
+                                    accum.sum += 1 + usize::from(end.saturating_sub(start));
+                                } else {
+                                    accum.found_uninit = true;
+                                }
+                                accum
+                            };
+
+                            let answers = self
+                                .dst
+                                .states_from(dst_state, src_validity.clone())
+                                .map(move |(dst_validity, dst_state_prime)| {
+                                    let mut c = c.borrow_mut();
+                                    accum1.set(update_accum(accum1.get(), dst_validity));
+                                    let answer =
+                                        self.answer_memo(&mut *c, src_state_prime, dst_state_prime);
+                                    answer
+                                })
+                                .chain(
+                                    iter::once_with(move || {
+                                        let src_validity_len = usize::from(*src_validity.end())
+                                            - usize::from(*src_validity.start())
+                                            + 1;
+                                        let accum = accum2.get();
+
+                                        // If this condition is false, then
+                                        // there are some byte values in the
+                                        // source which have no corresponding
+                                        // transition in the destination DFA. In
+                                        // that case, we add a `No` to our list
+                                        // of answers. When
+                                        // `!self.assume.validity`, this will
+                                        // cause the query to fail.
+                                        if accum.found_uninit || accum.sum == src_validity_len {
+                                            None
+                                        } else {
+                                            Some(Answer::No(Reason::DstIsBitIncompatible))
+                                        }
+                                    })
+                                    .flatten(),
+                                );
+                            Either::Right(answers)
                         },
                     ),
                 );
@@ -235,48 +333,38 @@ where
 
                 let refs_answer = src_quantifier.apply(
                     // for each reference transition out of `src_state`...
-                    self.src.refs_from(src_state).unwrap_or(&Map::default()).into_iter().map(
-                        |(&src_ref, &src_state_prime)| {
-                            // ...there exists a reference transition out of `dst_state`...
-                            Quantifier::ThereExists.apply(
-                                self.dst
-                                    .refs_from(dst_state)
-                                    .unwrap_or(&Map::default())
-                                    .into_iter()
-                                    .map(|(&dst_ref, &dst_state_prime)| {
-                                        if !src_ref.is_mutable() && dst_ref.is_mutable() {
-                                            Answer::No(Reason::DstIsMoreUnique)
-                                        } else if !self.assume.alignment
-                                            && src_ref.min_align() < dst_ref.min_align()
-                                        {
-                                            Answer::No(Reason::DstHasStricterAlignment {
-                                                src_min_align: src_ref.min_align(),
-                                                dst_min_align: dst_ref.min_align(),
-                                            })
-                                        } else if dst_ref.size() > src_ref.size() {
-                                            Answer::No(Reason::DstRefIsTooBig {
-                                                src: src_ref,
-                                                dst: dst_ref,
-                                            })
-                                        } else {
-                                            // ...such that `src` is transmutable into `dst`, if
-                                            // `src_ref` is transmutability into `dst_ref`.
-                                            and(
-                                                Answer::If(Condition::IfTransmutable {
-                                                    src: src_ref,
-                                                    dst: dst_ref,
-                                                }),
-                                                self.answer_memo(
-                                                    cache,
-                                                    src_state_prime,
-                                                    dst_state_prime,
-                                                ),
-                                            )
-                                        }
-                                    }),
-                            )
-                        },
-                    ),
+                    self.src.refs_from(src_state).map(|(src_ref, src_state_prime)| {
+                        // ...there exists a reference transition out of `dst_state`...
+                        Quantifier::ThereExists.apply(self.dst.refs_from(dst_state).map(
+                            |(dst_ref, dst_state_prime)| {
+                                if !src_ref.is_mutable() && dst_ref.is_mutable() {
+                                    Answer::No(Reason::DstIsMoreUnique)
+                                } else if !self.assume.alignment
+                                    && src_ref.min_align() < dst_ref.min_align()
+                                {
+                                    Answer::No(Reason::DstHasStricterAlignment {
+                                        src_min_align: src_ref.min_align(),
+                                        dst_min_align: dst_ref.min_align(),
+                                    })
+                                } else if dst_ref.size() > src_ref.size() {
+                                    Answer::No(Reason::DstRefIsTooBig {
+                                        src: src_ref,
+                                        dst: dst_ref,
+                                    })
+                                } else {
+                                    // ...such that `src` is transmutable into `dst`, if
+                                    // `src_ref` is transmutability into `dst_ref`.
+                                    and(
+                                        Answer::If(Condition::IfTransmutable {
+                                            src: src_ref,
+                                            dst: dst_ref,
+                                        }),
+                                        self.answer_memo(cache, src_state_prime, dst_state_prime),
+                                    )
+                                }
+                            },
+                        ))
+                    }),
                 );
 
                 if self.assume.validity {
diff --git a/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs b/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs
index f8b59bdf32684..214da101be375 100644
--- a/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs
+++ b/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs
@@ -8,9 +8,17 @@ pub(crate) trait QueryContext {
 
 #[cfg(test)]
 pub(crate) mod test {
+    use std::marker::PhantomData;
+
     use super::QueryContext;
 
-    pub(crate) struct UltraMinimal;
+    pub(crate) struct UltraMinimal<R = !>(PhantomData<R>);
+
+    impl<R> Default for UltraMinimal<R> {
+        fn default() -> Self {
+            Self(PhantomData)
+        }
+    }
 
     #[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)]
     pub(crate) enum Def {
@@ -24,9 +32,9 @@ pub(crate) mod test {
         }
     }
 
-    impl QueryContext for UltraMinimal {
+    impl<R: crate::layout::Ref> QueryContext for UltraMinimal<R> {
         type Def = Def;
-        type Ref = !;
+        type Ref = R;
     }
 }
 
diff --git a/compiler/rustc_transmute/src/maybe_transmutable/tests.rs b/compiler/rustc_transmute/src/maybe_transmutable/tests.rs
index cc6a4dce17b63..24e2a1acadd68 100644
--- a/compiler/rustc_transmute/src/maybe_transmutable/tests.rs
+++ b/compiler/rustc_transmute/src/maybe_transmutable/tests.rs
@@ -1,3 +1,5 @@
+extern crate test;
+
 use itertools::Itertools;
 
 use super::query_context::test::{Def, UltraMinimal};
@@ -12,15 +14,25 @@ trait Representation {
 
 impl Representation for Tree {
     fn is_transmutable(src: Self, dst: Self, assume: Assume) -> Answer<!> {
-        crate::maybe_transmutable::MaybeTransmutableQuery::new(src, dst, assume, UltraMinimal)
-            .answer()
+        crate::maybe_transmutable::MaybeTransmutableQuery::new(
+            src,
+            dst,
+            assume,
+            UltraMinimal::default(),
+        )
+        .answer()
     }
 }
 
 impl Representation for Dfa {
     fn is_transmutable(src: Self, dst: Self, assume: Assume) -> Answer<!> {
-        crate::maybe_transmutable::MaybeTransmutableQuery::new(src, dst, assume, UltraMinimal)
-            .answer()
+        crate::maybe_transmutable::MaybeTransmutableQuery::new(
+            src,
+            dst,
+            assume,
+            UltraMinimal::default(),
+        )
+        .answer()
     }
 }
 
@@ -89,6 +101,36 @@ mod safety {
     }
 }
 
+mod size {
+    use super::*;
+
+    #[test]
+    fn size() {
+        let small = Tree::number(1);
+        let large = Tree::number(2);
+
+        for alignment in [false, true] {
+            for lifetimes in [false, true] {
+                for safety in [false, true] {
+                    for validity in [false, true] {
+                        let assume = Assume { alignment, lifetimes, safety, validity };
+                        assert_eq!(
+                            is_transmutable(&small, &large, assume),
+                            Answer::No(Reason::DstIsTooBig),
+                            "assume: {assume:?}"
+                        );
+                        assert_eq!(
+                            is_transmutable(&large, &small, assume),
+                            Answer::Yes,
+                            "assume: {assume:?}"
+                        );
+                    }
+                }
+            }
+        }
+    }
+}
+
 mod bool {
     use super::*;
 
@@ -112,6 +154,27 @@ mod bool {
         );
     }
 
+    #[test]
+    fn transmute_u8() {
+        let bool = &Tree::bool();
+        let u8 = &Tree::u8();
+        for (src, dst, assume_validity, answer) in [
+            (bool, u8, false, Answer::Yes),
+            (bool, u8, true, Answer::Yes),
+            (u8, bool, false, Answer::No(Reason::DstIsBitIncompatible)),
+            (u8, bool, true, Answer::Yes),
+        ] {
+            assert_eq!(
+                is_transmutable(
+                    src,
+                    dst,
+                    Assume { validity: assume_validity, ..Assume::default() }
+                ),
+                answer
+            );
+        }
+    }
+
     #[test]
     fn should_permit_validity_expansion_and_reject_contraction() {
         let b0 = layout::Tree::<Def, !>::from_bits(0);
@@ -175,6 +238,62 @@ mod bool {
     }
 }
 
+mod uninit {
+    use super::*;
+
+    #[test]
+    fn size() {
+        let mu = Tree::uninit();
+        let u8 = Tree::u8();
+
+        for alignment in [false, true] {
+            for lifetimes in [false, true] {
+                for safety in [false, true] {
+                    for validity in [false, true] {
+                        let assume = Assume { alignment, lifetimes, safety, validity };
+
+                        let want = if validity {
+                            Answer::Yes
+                        } else {
+                            Answer::No(Reason::DstIsBitIncompatible)
+                        };
+
+                        assert_eq!(is_transmutable(&mu, &u8, assume), want, "assume: {assume:?}");
+                        assert_eq!(
+                            is_transmutable(&u8, &mu, assume),
+                            Answer::Yes,
+                            "assume: {assume:?}"
+                        );
+                    }
+                }
+            }
+        }
+    }
+}
+
+mod alt {
+    use super::*;
+    use crate::Answer;
+
+    #[test]
+    fn should_permit_identity_transmutation() {
+        type Tree = layout::Tree<Def, !>;
+
+        let x = Tree::Seq(vec![Tree::from_bits(0), Tree::from_bits(0)]);
+        let y = Tree::Seq(vec![Tree::bool(), Tree::from_bits(1)]);
+        let layout = Tree::Alt(vec![x, y]);
+
+        let answer = crate::maybe_transmutable::MaybeTransmutableQuery::new(
+            layout.clone(),
+            layout.clone(),
+            crate::Assume::default(),
+            UltraMinimal::default(),
+        )
+        .answer();
+        assert_eq!(answer, Answer::Yes, "layout:{:#?}", layout);
+    }
+}
+
 mod union {
     use super::*;
 
@@ -203,3 +322,59 @@ mod union {
         assert_eq!(is_transmutable(&t, &u, Assume::default()), Answer::Yes);
     }
 }
+
+mod r#ref {
+    use super::*;
+
+    #[test]
+    fn should_permit_identity_transmutation() {
+        type Tree = crate::layout::Tree<Def, [(); 1]>;
+
+        let layout = Tree::Seq(vec![Tree::from_bits(0), Tree::Ref([()])]);
+
+        let answer = crate::maybe_transmutable::MaybeTransmutableQuery::new(
+            layout.clone(),
+            layout,
+            Assume::default(),
+            UltraMinimal::default(),
+        )
+        .answer();
+        assert_eq!(answer, Answer::If(crate::Condition::IfTransmutable { src: [()], dst: [()] }));
+    }
+}
+
+mod benches {
+    use std::hint::black_box;
+
+    use test::Bencher;
+
+    use super::*;
+
+    #[bench]
+    fn bench_dfa_from_tree(b: &mut Bencher) {
+        let num = Tree::number(8).prune(&|_| false);
+        let num = black_box(num);
+
+        b.iter(|| {
+            let _ = black_box(Dfa::from_tree(num.clone()));
+        })
+    }
+
+    #[bench]
+    fn bench_transmute(b: &mut Bencher) {
+        let num = Tree::number(8).prune(&|_| false);
+        let dfa = black_box(Dfa::from_tree(num).unwrap());
+
+        b.iter(|| {
+            let answer = crate::maybe_transmutable::MaybeTransmutableQuery::new(
+                dfa.clone(),
+                dfa.clone(),
+                Assume::default(),
+                UltraMinimal::default(),
+            )
+            .answer();
+            let answer = std::hint::black_box(answer);
+            assert_eq!(answer, Answer::Yes);
+        })
+    }
+}


Reviews:
user: login: jswrenn
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-23T17:34:39Z
user: login: jswrenn
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-23T17:38:53Z
user: login: jswrenn
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-23T17:39:40Z
user: login: jswrenn
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-23T17:41:27Z
user: login: jswrenn
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-23T17:44:11Z
user: login: joshlf
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T17:46:47Z
user: login: jswrenn
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-23T17:47:17Z
user: login: jswrenn
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-23T17:50:20Z
user: login: jswrenn
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-23T17:51:45Z
user: login: jswrenn
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-23T17:57:52Z
user: login: jswrenn
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-23T17:58:25Z
user: login: joshlf
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T18:06:10Z
user: login: joshlf
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T18:06:29Z
user: login: joshlf
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T18:06:37Z
user: login: joshlf
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T18:06:41Z
user: login: joshlf
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T18:06:43Z
user: login: joshlf
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T18:06:45Z
user: login: joshlf
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T18:07:05Z
user: login: joshlf
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T18:07:07Z
user: login: joshlf
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T18:07:13Z
user: login: joshlf
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T18:07:24Z
user: login: tmiasko
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-26T20:56:27Z
user: login: jswrenn
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-26T21:04:11Z
user: login: tmiasko
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T08:58:42Z

Review comments:
diff_hunk: @@ -76,23 +72,24 @@ where
     }
 
     pub(crate) fn from_byte(byte: Byte) -> Self {
-        let mut transitions: Map<State, Transitions<R>> = Map::default();
-        let start = State::new();
-        let accept = State::new();
-
-        transitions.entry(start).or_default().byte_transitions.insert(byte, accept);
-
-        Self { transitions, start, accept }
+        Self::from_transitions(|accept| Transitions {
+            byte_transitions: EdgeSet::new(byte, accept),
+            ref_transitions: Map::default(),
+        })
     }
 
     pub(crate) fn from_ref(r: R) -> Self {
-        let mut transitions: Map<State, Transitions<R>> = Map::default();
+        Self::from_transitions(|accept| Transitions {
+            byte_transitions: EdgeSet::empty(),
+            ref_transitions: [(r, accept)].into_iter().collect(),
+        })
+    }
+
+    fn from_transitions(f: impl FnOnce(State) -> Transitions<R>) -> Self {
         let start = State::new();
         let accept = State::new();
 
-        transitions.entry(start).or_default().ref_transitions.insert(r, accept);
-
-        Self { transitions, start, accept }
+        Self { transitions: [(start, f(accept))].into_iter().collect(), start, accept }
path: compiler/rustc_transmute/src/layout/dfa.rs
user: login: jswrenn
body: I think you can just write `.into()`, here and elsewhere:
```suggestion
        Self { transitions: [(start, f(accept))].into(), start, accept }
```
created_at: 2025-04-23T17:34:39Z
author_association: MEMBER
diff_hunk: @@ -170,67 +170,111 @@ where
 
         let start = mapped((Some(a.start), Some(b.start)));
         let mut transitions: Map<State, Transitions<R>> = Map::default();
-        let mut queue = vec![(Some(a.start), Some(b.start))];
         let empty_transitions = Transitions::default();
 
-        while let Some((a_src, b_src)) = queue.pop() {
+        struct WorkQueue {
+            queue: Vec<(Option<State>, Option<State>)>,
+            // Track all entries ever enqueued to avoid duplicating work. This
+            // gives us a guarantee that a given (a_state, b_state) pair will
+            // only ever be visited once.
+            enqueued: Set<(Option<State>, Option<State>)>,
+        }
+        impl WorkQueue {
+            fn enqueue(&mut self, a_state: Option<State>, b_state: Option<State>) {
+                if self.enqueued.insert((a_state, b_state)) {
+                    self.queue.push((a_state, b_state));
+                }
+            }
+        }
+        let mut queue = WorkQueue { queue: Vec::new(), enqueued: Set::default() };
+        queue.enqueue(Some(a.start), Some(b.start));
+
+        while let Some((a_src, b_src)) = queue.queue.pop() {
+            let src = mapped((a_src, b_src));
+            if src == accept {
+                // While it's possible to have a DFA whose accept state has
+                // out-edges, these do not affect the semantics of the DFA, and
+                // so there's no point in processing them. Continuing here also
+                // has the advantage of guaranteeing that we only ever process a
+                // given node in the output DFA once. In particular, with the
+                // exception of the accept state, we ensure that we only push a
+                // given node to the `queue` once. This allows the following
+                // code to assume that we're processing a node we've never
+                // processed before, which means we never need to merge two edge
+                // sets - we only ever need to construct a new edge set from
+                // whole cloth.
+                continue;
+            }
+
             let a_transitions =
                 a_src.and_then(|a_src| a.transitions.get(&a_src)).unwrap_or(&empty_transitions);
             let b_transitions =
                 b_src.and_then(|b_src| b.transitions.get(&b_src)).unwrap_or(&empty_transitions);
 
             let byte_transitions =
-                a_transitions.byte_transitions.keys().chain(b_transitions.byte_transitions.keys());
-
-            for byte_transition in byte_transitions {
-                let a_dst = a_transitions.byte_transitions.get(byte_transition).copied();
-                let b_dst = b_transitions.byte_transitions.get(byte_transition).copied();
+                a_transitions.byte_transitions.union(&b_transitions.byte_transitions);
 
+            let byte_transitions = byte_transitions.map(|(a_dst, b_dst)| {
                 assert!(a_dst.is_some() || b_dst.is_some());
 
-                let src = mapped((a_src, b_src));
-                let dst = mapped((a_dst, b_dst));
-
-                transitions.entry(src).or_default().byte_transitions.insert(*byte_transition, dst);
-
-                if !transitions.contains_key(&dst) {
-                    queue.push((a_dst, b_dst))
-                }
-            }
+                queue.enqueue(a_dst, b_dst);
+                mapped((a_dst, b_dst))
+            });
 
             let ref_transitions =
                 a_transitions.ref_transitions.keys().chain(b_transitions.ref_transitions.keys());
 
-            for ref_transition in ref_transitions {
-                let a_dst = a_transitions.ref_transitions.get(ref_transition).copied();
-                let b_dst = b_transitions.ref_transitions.get(ref_transition).copied();
-
-                assert!(a_dst.is_some() || b_dst.is_some());
+            let ref_transitions = ref_transitions
+                .map(|ref_transition| {
+                    let a_dst = a_transitions.ref_transitions.get(ref_transition).copied();
+                    let b_dst = b_transitions.ref_transitions.get(ref_transition).copied();
 
-                let src = mapped((a_src, b_src));
-                let dst = mapped((a_dst, b_dst));
+                    assert!(a_dst.is_some() || b_dst.is_some());
 
-                transitions.entry(src).or_default().ref_transitions.insert(*ref_transition, dst);
+                    queue.enqueue(a_dst, b_dst);
+                    (*ref_transition, mapped((a_dst, b_dst)))
+                })
+                .collect();
 
-                if !transitions.contains_key(&dst) {
-                    queue.push((a_dst, b_dst))
-                }
-            }
+            let old = transitions.insert(src, Transitions { byte_transitions, ref_transitions });
+            // See `if src == accept { ... }` above. The comment there explains
+            // why this assert is valid.
+            assert_eq!(old, None);
         }
 
         Self { transitions, start, accept }
     }
 
-    pub(crate) fn bytes_from(&self, start: State) -> Option<&Map<Byte, State>> {
-        Some(&self.transitions.get(&start)?.byte_transitions)
+    pub(crate) fn iter_states_from(
+        &self,
+        state: State,
+        src_validity: RangeInclusive<u8>,
+    ) -> impl Iterator<Item = (Byte, State)> {
+        self.transitions
+            .get(&state)
+            .map(move |t| t.byte_transitions.iter_states_from(src_validity))
+            .into_iter()
+            .flatten()
     }
 
-    pub(crate) fn byte_from(&self, start: State, byte: Byte) -> Option<State> {
-        self.transitions.get(&start)?.byte_transitions.get(&byte).copied()
+    pub(crate) fn get_uninit_edge_dst(&self, state: State) -> Option<State> {
+        let transitions = self.transitions.get(&state)?;
+        transitions.byte_transitions.get_uninit_edge_dst()
     }
 
-    pub(crate) fn refs_from(&self, start: State) -> Option<&Map<R, State>> {
-        Some(&self.transitions.get(&start)?.ref_transitions)
+    pub(crate) fn iter_bytes_from(&self, start: State) -> impl Iterator<Item = (Byte, State)> {
path: compiler/rustc_transmute/src/layout/dfa.rs
user: login: jswrenn
body: Would lightly prefer dropping the `iter_` prefix, so long as there's no conflict between this and a hypothetical `bytes_from` method that doesn't return an iter:
```suggestion
    pub(crate) fn iter_bytes_from(&self, start: State) -> impl Iterator<Item = (Byte, State)> {
```
created_at: 2025-04-23T17:38:53Z
author_association: MEMBER
diff_hunk: @@ -277,3 +331,235 @@ where
         writeln!(f, "}}")
     }
 }
+
+use edge_set::EdgeSet;
+mod edge_set {
+    use std::cmp;
+
+    use run::*;
+
+    use super::*;
+    mod run {
+        use std::ops::{Range, RangeInclusive};
+
+        use super::*;
+        use crate::layout::Byte;
+
+        #[derive(Eq, PartialEq, Copy, Clone, Debug)]
+        pub(super) struct Run<S> {
path: compiler/rustc_transmute/src/layout/dfa.rs
user: login: jswrenn
body: Doc comment?
created_at: 2025-04-23T17:39:40Z
author_association: MEMBER
diff_hunk: @@ -277,3 +331,235 @@ where
         writeln!(f, "}}")
     }
 }
+
+use edge_set::EdgeSet;
+mod edge_set {
+    use std::cmp;
+
+    use run::*;
+
+    use super::*;
+    mod run {
+        use std::ops::{Range, RangeInclusive};
+
+        use super::*;
+        use crate::layout::Byte;
+
+        #[derive(Eq, PartialEq, Copy, Clone, Debug)]
+        pub(super) struct Run<S> {
+            // `start` and `end` are both inclusive (ie, closed) bounds, as this
+            // is required in order to be able to store 0..=255. We provide
+            // setters and getters which operate on closed/open ranges, which
+            // are more intuitive and easier for performing offset math.
+            start: u8,
+            end: u8,
+            pub(super) dst: S,
+        }
+
+        impl<S> Run<S> {
+            pub(super) fn new(range: RangeInclusive<u8>, dst: S) -> Self {
+                Self { start: *range.start(), end: *range.end(), dst }
+            }
+
+            pub(super) fn from_closed_open(range: Range<u16>, dst: S) -> Self {
path: compiler/rustc_transmute/src/layout/dfa.rs
user: login: jswrenn
body: `from_inclusive_exclusive`, perhaps? that nomenclature is consistent with  https://doc.rust-lang.org/stable/std/ops/enum.Bound.html
created_at: 2025-04-23T17:41:27Z
author_association: MEMBER
diff_hunk: @@ -277,3 +331,235 @@ where
         writeln!(f, "}}")
     }
 }
+
+use edge_set::EdgeSet;
+mod edge_set {
+    use std::cmp;
+
+    use run::*;
+
+    use super::*;
+    mod run {
+        use std::ops::{Range, RangeInclusive};
+
+        use super::*;
+        use crate::layout::Byte;
+
+        #[derive(Eq, PartialEq, Copy, Clone, Debug)]
+        pub(super) struct Run<S> {
+            // `start` and `end` are both inclusive (ie, closed) bounds, as this
+            // is required in order to be able to store 0..=255. We provide
+            // setters and getters which operate on closed/open ranges, which
+            // are more intuitive and easier for performing offset math.
+            start: u8,
+            end: u8,
+            pub(super) dst: S,
+        }
+
+        impl<S> Run<S> {
+            pub(super) fn new(range: RangeInclusive<u8>, dst: S) -> Self {
+                Self { start: *range.start(), end: *range.end(), dst }
+            }
+
+            pub(super) fn from_closed_open(range: Range<u16>, dst: S) -> Self {
+                Self {
+                    start: range.start.try_into().unwrap(),
+                    end: (range.end - 1).try_into().unwrap(),
+                    dst,
+                }
+            }
+
+            pub(super) fn contains(&self, idx: u16) -> bool {
+                idx >= u16::from(self.start) && idx <= u16::from(self.end)
+            }
+
+            pub(super) fn as_closed_open(&self) -> (u16, u16) {
path: compiler/rustc_transmute/src/layout/dfa.rs
user: login: jswrenn
body: Same naming note here.
created_at: 2025-04-23T17:44:11Z
author_association: MEMBER
diff_hunk: @@ -277,3 +331,235 @@ where
         writeln!(f, "}}")
     }
 }
+
+use edge_set::EdgeSet;
+mod edge_set {
+    use std::cmp;
+
+    use run::*;
+
+    use super::*;
+    mod run {
+        use std::ops::{Range, RangeInclusive};
+
+        use super::*;
+        use crate::layout::Byte;
+
+        #[derive(Eq, PartialEq, Copy, Clone, Debug)]
+        pub(super) struct Run<S> {
+            // `start` and `end` are both inclusive (ie, closed) bounds, as this
+            // is required in order to be able to store 0..=255. We provide
+            // setters and getters which operate on closed/open ranges, which
+            // are more intuitive and easier for performing offset math.
+            start: u8,
+            end: u8,
+            pub(super) dst: S,
+        }
+
+        impl<S> Run<S> {
+            pub(super) fn new(range: RangeInclusive<u8>, dst: S) -> Self {
+                Self { start: *range.start(), end: *range.end(), dst }
+            }
+
+            pub(super) fn from_closed_open(range: Range<u16>, dst: S) -> Self {
+                Self {
+                    start: range.start.try_into().unwrap(),
+                    end: (range.end - 1).try_into().unwrap(),
+                    dst,
+                }
+            }
+
+            pub(super) fn contains(&self, idx: u16) -> bool {
+                idx >= u16::from(self.start) && idx <= u16::from(self.end)
+            }
+
+            pub(super) fn as_closed_open(&self) -> (u16, u16) {
+                (u16::from(self.start), u16::from(self.end) + 1)
+            }
+
+            pub(super) fn as_byte(&self) -> Byte {
+                Byte::new(self.start..=self.end)
+            }
+
+            pub(super) fn map<SS>(self, f: impl FnOnce(S) -> SS) -> Run<SS> {
path: compiler/rustc_transmute/src/layout/dfa.rs
user: login: jswrenn
body: `map_state`
created_at: 2025-04-23T17:45:13Z
author_association: MEMBER
diff_hunk: @@ -76,23 +72,24 @@ where
     }
 
     pub(crate) fn from_byte(byte: Byte) -> Self {
-        let mut transitions: Map<State, Transitions<R>> = Map::default();
-        let start = State::new();
-        let accept = State::new();
-
-        transitions.entry(start).or_default().byte_transitions.insert(byte, accept);
-
-        Self { transitions, start, accept }
+        Self::from_transitions(|accept| Transitions {
+            byte_transitions: EdgeSet::new(byte, accept),
+            ref_transitions: Map::default(),
+        })
     }
 
     pub(crate) fn from_ref(r: R) -> Self {
-        let mut transitions: Map<State, Transitions<R>> = Map::default();
+        Self::from_transitions(|accept| Transitions {
+            byte_transitions: EdgeSet::empty(),
+            ref_transitions: [(r, accept)].into_iter().collect(),
+        })
+    }
+
+    fn from_transitions(f: impl FnOnce(State) -> Transitions<R>) -> Self {
         let start = State::new();
         let accept = State::new();
 
-        transitions.entry(start).or_default().ref_transitions.insert(r, accept);
-
-        Self { transitions, start, accept }
+        Self { transitions: [(start, f(accept))].into_iter().collect(), start, accept }
path: compiler/rustc_transmute/src/layout/dfa.rs
user: login: joshlf
body: Doesn't work unfortunately.
created_at: 2025-04-23T17:46:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -277,3 +331,235 @@ where
         writeln!(f, "}}")
     }
 }
+
+use edge_set::EdgeSet;
+mod edge_set {
+    use std::cmp;
+
+    use run::*;
+
+    use super::*;
+    mod run {
+        use std::ops::{Range, RangeInclusive};
+
+        use super::*;
+        use crate::layout::Byte;
+
+        #[derive(Eq, PartialEq, Copy, Clone, Debug)]
+        pub(super) struct Run<S> {
+            // `start` and `end` are both inclusive (ie, closed) bounds, as this
+            // is required in order to be able to store 0..=255. We provide
+            // setters and getters which operate on closed/open ranges, which
+            // are more intuitive and easier for performing offset math.
+            start: u8,
+            end: u8,
+            pub(super) dst: S,
+        }
+
+        impl<S> Run<S> {
+            pub(super) fn new(range: RangeInclusive<u8>, dst: S) -> Self {
+                Self { start: *range.start(), end: *range.end(), dst }
+            }
+
+            pub(super) fn from_closed_open(range: Range<u16>, dst: S) -> Self {
+                Self {
+                    start: range.start.try_into().unwrap(),
+                    end: (range.end - 1).try_into().unwrap(),
+                    dst,
+                }
+            }
+
+            pub(super) fn contains(&self, idx: u16) -> bool {
+                idx >= u16::from(self.start) && idx <= u16::from(self.end)
+            }
+
+            pub(super) fn as_closed_open(&self) -> (u16, u16) {
+                (u16::from(self.start), u16::from(self.end) + 1)
+            }
+
+            pub(super) fn as_byte(&self) -> Byte {
+                Byte::new(self.start..=self.end)
+            }
+
+            pub(super) fn map<SS>(self, f: impl FnOnce(S) -> SS) -> Run<SS> {
+                let Run { start, end, dst } = self;
+                Run { start, end, dst: f(dst) }
+            }
+
+            /// Produces a new `Run` whose lower bound is the greater of
+            /// `self`'s existing lower bound and `lower_bound`.
+            pub(super) fn clamp_lower(self, lower_bound: u8) -> Self {
+                let Run { start, end, dst } = self;
+                Run { start: cmp::max(start, lower_bound), end, dst }
+            }
+        }
+    }
+
+    /// The set of outbound byte edges associated with a DFA node (not including
+    /// reference edges).
+    #[derive(Eq, PartialEq, Clone, Debug)]
+    pub(super) struct EdgeSet<S = State> {
+        // A sequence of runs stored in ascending order. Since the graph is a
+        // DFA, these must be non-overlapping with one another.
+        runs: Vec<Run<S>>,
path: compiler/rustc_transmute/src/layout/dfa.rs
user: login: jswrenn
body: This would be a great candidate for `SmallVec<[Run<S>; 1]>`.
created_at: 2025-04-23T17:47:08Z
author_association: MEMBER
diff_hunk: @@ -277,3 +331,235 @@ where
         writeln!(f, "}}")
     }
 }
+
+use edge_set::EdgeSet;
+mod edge_set {
+    use std::cmp;
+
+    use run::*;
+
+    use super::*;
+    mod run {
+        use std::ops::{Range, RangeInclusive};
+
+        use super::*;
+        use crate::layout::Byte;
+
+        #[derive(Eq, PartialEq, Copy, Clone, Debug)]
+        pub(super) struct Run<S> {
+            // `start` and `end` are both inclusive (ie, closed) bounds, as this
+            // is required in order to be able to store 0..=255. We provide
+            // setters and getters which operate on closed/open ranges, which
+            // are more intuitive and easier for performing offset math.
+            start: u8,
+            end: u8,
+            pub(super) dst: S,
+        }
+
+        impl<S> Run<S> {
+            pub(super) fn new(range: RangeInclusive<u8>, dst: S) -> Self {
+                Self { start: *range.start(), end: *range.end(), dst }
+            }
+
+            pub(super) fn from_closed_open(range: Range<u16>, dst: S) -> Self {
+                Self {
+                    start: range.start.try_into().unwrap(),
+                    end: (range.end - 1).try_into().unwrap(),
+                    dst,
+                }
+            }
+
+            pub(super) fn contains(&self, idx: u16) -> bool {
+                idx >= u16::from(self.start) && idx <= u16::from(self.end)
+            }
+
+            pub(super) fn as_closed_open(&self) -> (u16, u16) {
+                (u16::from(self.start), u16::from(self.end) + 1)
+            }
+
+            pub(super) fn as_byte(&self) -> Byte {
+                Byte::new(self.start..=self.end)
+            }
+
+            pub(super) fn map<SS>(self, f: impl FnOnce(S) -> SS) -> Run<SS> {
+                let Run { start, end, dst } = self;
+                Run { start, end, dst: f(dst) }
+            }
+
+            /// Produces a new `Run` whose lower bound is the greater of
+            /// `self`'s existing lower bound and `lower_bound`.
+            pub(super) fn clamp_lower(self, lower_bound: u8) -> Self {
+                let Run { start, end, dst } = self;
+                Run { start: cmp::max(start, lower_bound), end, dst }
+            }
+        }
+    }
+
+    /// The set of outbound byte edges associated with a DFA node (not including
+    /// reference edges).
+    #[derive(Eq, PartialEq, Clone, Debug)]
+    pub(super) struct EdgeSet<S = State> {
+        // A sequence of runs stored in ascending order. Since the graph is a
+        // DFA, these must be non-overlapping with one another.
+        runs: Vec<Run<S>>,
+        // The edge labeled with the uninit byte, if any.
+        //
+        // FIXME(@joshlf): Make `State` a `NonZero` so that this is NPO'd.
+        uninit: Option<S>,
+    }
+
+    impl<S> EdgeSet<S> {
+        pub(crate) fn new(byte: Byte, dst: S) -> Self {
+            match byte.range() {
+                Some(range) => Self { runs: vec![Run::new(range, dst)], uninit: None },
+                None => Self { runs: Vec::new(), uninit: Some(dst) },
+            }
+        }
+
+        pub(crate) fn empty() -> Self {
+            Self { runs: Vec::new(), uninit: None }
+        }
+
+        #[cfg(test)]
+        pub(crate) fn from_vec(mut edges: Vec<(Byte, S)>) -> Self
path: compiler/rustc_transmute/src/layout/dfa.rs
user: login: jswrenn
body: `from_edges`
created_at: 2025-04-23T17:49:34Z
author_association: MEMBER
diff_hunk: @@ -277,3 +331,235 @@ where
         writeln!(f, "}}")
     }
 }
+
+use edge_set::EdgeSet;
+mod edge_set {
+    use std::cmp;
+
+    use run::*;
+
+    use super::*;
+    mod run {
+        use std::ops::{Range, RangeInclusive};
+
+        use super::*;
+        use crate::layout::Byte;
+
+        #[derive(Eq, PartialEq, Copy, Clone, Debug)]
+        pub(super) struct Run<S> {
+            // `start` and `end` are both inclusive (ie, closed) bounds, as this
+            // is required in order to be able to store 0..=255. We provide
+            // setters and getters which operate on closed/open ranges, which
+            // are more intuitive and easier for performing offset math.
+            start: u8,
+            end: u8,
+            pub(super) dst: S,
+        }
+
+        impl<S> Run<S> {
+            pub(super) fn new(range: RangeInclusive<u8>, dst: S) -> Self {
+                Self { start: *range.start(), end: *range.end(), dst }
+            }
+
+            pub(super) fn from_closed_open(range: Range<u16>, dst: S) -> Self {
+                Self {
+                    start: range.start.try_into().unwrap(),
+                    end: (range.end - 1).try_into().unwrap(),
+                    dst,
+                }
+            }
+
+            pub(super) fn contains(&self, idx: u16) -> bool {
+                idx >= u16::from(self.start) && idx <= u16::from(self.end)
+            }
+
+            pub(super) fn as_closed_open(&self) -> (u16, u16) {
+                (u16::from(self.start), u16::from(self.end) + 1)
+            }
+
+            pub(super) fn as_byte(&self) -> Byte {
+                Byte::new(self.start..=self.end)
+            }
+
+            pub(super) fn map<SS>(self, f: impl FnOnce(S) -> SS) -> Run<SS> {
+                let Run { start, end, dst } = self;
+                Run { start, end, dst: f(dst) }
+            }
+
+            /// Produces a new `Run` whose lower bound is the greater of
+            /// `self`'s existing lower bound and `lower_bound`.
+            pub(super) fn clamp_lower(self, lower_bound: u8) -> Self {
+                let Run { start, end, dst } = self;
+                Run { start: cmp::max(start, lower_bound), end, dst }
+            }
+        }
+    }
+
+    /// The set of outbound byte edges associated with a DFA node (not including
+    /// reference edges).
+    #[derive(Eq, PartialEq, Clone, Debug)]
+    pub(super) struct EdgeSet<S = State> {
+        // A sequence of runs stored in ascending order. Since the graph is a
+        // DFA, these must be non-overlapping with one another.
+        runs: Vec<Run<S>>,
+        // The edge labeled with the uninit byte, if any.
+        //
+        // FIXME(@joshlf): Make `State` a `NonZero` so that this is NPO'd.
+        uninit: Option<S>,
+    }
+
+    impl<S> EdgeSet<S> {
+        pub(crate) fn new(byte: Byte, dst: S) -> Self {
+            match byte.range() {
+                Some(range) => Self { runs: vec![Run::new(range, dst)], uninit: None },
+                None => Self { runs: Vec::new(), uninit: Some(dst) },
+            }
+        }
+
+        pub(crate) fn empty() -> Self {
+            Self { runs: Vec::new(), uninit: None }
+        }
+
+        #[cfg(test)]
+        pub(crate) fn from_vec(mut edges: Vec<(Byte, S)>) -> Self
+        where
+            S: Ord,
+        {
+            edges.sort();
+            Self {
+                runs: edges
+                    .into_iter()
+                    .map(|(byte, state)| Run::new(byte.range().unwrap(), state))
+                    .collect(),
+                uninit: None,
+            }
+        }
+
+        pub(crate) fn iter(&self) -> impl Iterator<Item = (Byte, S)>
+        where
+            S: Copy,
+        {
+            self.uninit
+                .map(|dst| (Byte::uninit(), dst))
+                .into_iter()
+                .chain(self.runs.iter().map(|run| (run.as_byte(), run.dst)))
+        }
+
+        pub(crate) fn iter_states_from(
+            &self,
+            byte: RangeInclusive<u8>,
+        ) -> impl Iterator<Item = (Byte, S)>
+        where
+            S: Copy,
+        {
+            // FIXME(@joshlf): Optimize this
path: compiler/rustc_transmute/src/layout/dfa.rs
user: login: jswrenn
body: Say more: What's the optimization here? 
created_at: 2025-04-23T17:50:14Z
author_association: MEMBER
diff_hunk: @@ -277,3 +331,235 @@ where
         writeln!(f, "}}")
     }
 }
+
+use edge_set::EdgeSet;
+mod edge_set {
+    use std::cmp;
+
+    use run::*;
+
+    use super::*;
+    mod run {
+        use std::ops::{Range, RangeInclusive};
+
+        use super::*;
+        use crate::layout::Byte;
+
+        #[derive(Eq, PartialEq, Copy, Clone, Debug)]
+        pub(super) struct Run<S> {
+            // `start` and `end` are both inclusive (ie, closed) bounds, as this
+            // is required in order to be able to store 0..=255. We provide
+            // setters and getters which operate on closed/open ranges, which
+            // are more intuitive and easier for performing offset math.
+            start: u8,
+            end: u8,
+            pub(super) dst: S,
+        }
+
+        impl<S> Run<S> {
+            pub(super) fn new(range: RangeInclusive<u8>, dst: S) -> Self {
+                Self { start: *range.start(), end: *range.end(), dst }
+            }
+
+            pub(super) fn from_closed_open(range: Range<u16>, dst: S) -> Self {
+                Self {
+                    start: range.start.try_into().unwrap(),
+                    end: (range.end - 1).try_into().unwrap(),
+                    dst,
+                }
+            }
+
+            pub(super) fn contains(&self, idx: u16) -> bool {
+                idx >= u16::from(self.start) && idx <= u16::from(self.end)
+            }
+
+            pub(super) fn as_closed_open(&self) -> (u16, u16) {
+                (u16::from(self.start), u16::from(self.end) + 1)
+            }
+
+            pub(super) fn as_byte(&self) -> Byte {
+                Byte::new(self.start..=self.end)
+            }
+
+            pub(super) fn map<SS>(self, f: impl FnOnce(S) -> SS) -> Run<SS> {
+                let Run { start, end, dst } = self;
+                Run { start, end, dst: f(dst) }
+            }
+
+            /// Produces a new `Run` whose lower bound is the greater of
+            /// `self`'s existing lower bound and `lower_bound`.
+            pub(super) fn clamp_lower(self, lower_bound: u8) -> Self {
+                let Run { start, end, dst } = self;
+                Run { start: cmp::max(start, lower_bound), end, dst }
+            }
+        }
+    }
+
+    /// The set of outbound byte edges associated with a DFA node (not including
+    /// reference edges).
+    #[derive(Eq, PartialEq, Clone, Debug)]
+    pub(super) struct EdgeSet<S = State> {
+        // A sequence of runs stored in ascending order. Since the graph is a
+        // DFA, these must be non-overlapping with one another.
+        runs: Vec<Run<S>>,
+        // The edge labeled with the uninit byte, if any.
+        //
+        // FIXME(@joshlf): Make `State` a `NonZero` so that this is NPO'd.
+        uninit: Option<S>,
+    }
+
+    impl<S> EdgeSet<S> {
+        pub(crate) fn new(byte: Byte, dst: S) -> Self {
+            match byte.range() {
+                Some(range) => Self { runs: vec![Run::new(range, dst)], uninit: None },
+                None => Self { runs: Vec::new(), uninit: Some(dst) },
+            }
+        }
+
+        pub(crate) fn empty() -> Self {
+            Self { runs: Vec::new(), uninit: None }
+        }
+
+        #[cfg(test)]
+        pub(crate) fn from_vec(mut edges: Vec<(Byte, S)>) -> Self
+        where
+            S: Ord,
+        {
+            edges.sort();
+            Self {
+                runs: edges
+                    .into_iter()
+                    .map(|(byte, state)| Run::new(byte.range().unwrap(), state))
+                    .collect(),
+                uninit: None,
+            }
+        }
+
+        pub(crate) fn iter(&self) -> impl Iterator<Item = (Byte, S)>
+        where
+            S: Copy,
+        {
+            self.uninit
+                .map(|dst| (Byte::uninit(), dst))
+                .into_iter()
+                .chain(self.runs.iter().map(|run| (run.as_byte(), run.dst)))
+        }
+
+        pub(crate) fn iter_states_from(
+            &self,
+            byte: RangeInclusive<u8>,
+        ) -> impl Iterator<Item = (Byte, S)>
+        where
+            S: Copy,
+        {
+            // FIXME(@joshlf): Optimize this
+            self.iter().filter(move |(o, _)| Byte::new(byte.clone()).transmutable_into(&o))
+        }
+
+        pub(crate) fn get_uninit_edge_dst(&self) -> Option<S>
+        where
+            S: Copy,
+        {
+            self.uninit
+        }
+
+        pub(crate) fn map<SS>(self, mut f: impl FnMut(S) -> SS) -> EdgeSet<SS> {
path: compiler/rustc_transmute/src/layout/dfa.rs
user: login: jswrenn
body: `map_states`
created_at: 2025-04-23T17:51:45Z
author_association: MEMBER
diff_hunk: @@ -62,6 +101,21 @@ impl Ref for ! {
     }
 }
 
+#[cfg(test)]
+impl<const N: usize> Ref for [u8; N] {
path: compiler/rustc_transmute/src/layout/mod.rs
user: login: jswrenn
body: Can you make this `[(); N]`, since the bytes do not currently encode anything?
created_at: 2025-04-23T17:57:52Z
author_association: MEMBER
diff_hunk: @@ -62,6 +101,21 @@ impl Ref for ! {
     }
 }
 
+#[cfg(test)]
+impl<const N: usize> Ref for [u8; N] {
path: compiler/rustc_transmute/src/layout/mod.rs
user: login: jswrenn
body: Or, alternatively, do what we do for `Def`: define a named type, `Ref`, that stands in for references in tests.
created_at: 2025-04-23T17:58:25Z
author_association: MEMBER
diff_hunk: @@ -277,3 +331,235 @@ where
         writeln!(f, "}}")
     }
 }
+
+use edge_set::EdgeSet;
+mod edge_set {
+    use std::cmp;
+
+    use run::*;
+
+    use super::*;
+    mod run {
+        use std::ops::{Range, RangeInclusive};
+
+        use super::*;
+        use crate::layout::Byte;
+
+        #[derive(Eq, PartialEq, Copy, Clone, Debug)]
+        pub(super) struct Run<S> {
path: compiler/rustc_transmute/src/layout/dfa.rs
user: login: joshlf
body: Done
created_at: 2025-04-23T18:06:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -170,67 +170,111 @@ where
 
         let start = mapped((Some(a.start), Some(b.start)));
         let mut transitions: Map<State, Transitions<R>> = Map::default();
-        let mut queue = vec![(Some(a.start), Some(b.start))];
         let empty_transitions = Transitions::default();
 
-        while let Some((a_src, b_src)) = queue.pop() {
+        struct WorkQueue {
+            queue: Vec<(Option<State>, Option<State>)>,
+            // Track all entries ever enqueued to avoid duplicating work. This
+            // gives us a guarantee that a given (a_state, b_state) pair will
+            // only ever be visited once.
+            enqueued: Set<(Option<State>, Option<State>)>,
+        }
+        impl WorkQueue {
+            fn enqueue(&mut self, a_state: Option<State>, b_state: Option<State>) {
+                if self.enqueued.insert((a_state, b_state)) {
+                    self.queue.push((a_state, b_state));
+                }
+            }
+        }
+        let mut queue = WorkQueue { queue: Vec::new(), enqueued: Set::default() };
+        queue.enqueue(Some(a.start), Some(b.start));
+
+        while let Some((a_src, b_src)) = queue.queue.pop() {
+            let src = mapped((a_src, b_src));
+            if src == accept {
+                // While it's possible to have a DFA whose accept state has
+                // out-edges, these do not affect the semantics of the DFA, and
+                // so there's no point in processing them. Continuing here also
+                // has the advantage of guaranteeing that we only ever process a
+                // given node in the output DFA once. In particular, with the
+                // exception of the accept state, we ensure that we only push a
+                // given node to the `queue` once. This allows the following
+                // code to assume that we're processing a node we've never
+                // processed before, which means we never need to merge two edge
+                // sets - we only ever need to construct a new edge set from
+                // whole cloth.
+                continue;
+            }
+
             let a_transitions =
                 a_src.and_then(|a_src| a.transitions.get(&a_src)).unwrap_or(&empty_transitions);
             let b_transitions =
                 b_src.and_then(|b_src| b.transitions.get(&b_src)).unwrap_or(&empty_transitions);
 
             let byte_transitions =
-                a_transitions.byte_transitions.keys().chain(b_transitions.byte_transitions.keys());
-
-            for byte_transition in byte_transitions {
-                let a_dst = a_transitions.byte_transitions.get(byte_transition).copied();
-                let b_dst = b_transitions.byte_transitions.get(byte_transition).copied();
+                a_transitions.byte_transitions.union(&b_transitions.byte_transitions);
 
+            let byte_transitions = byte_transitions.map(|(a_dst, b_dst)| {
                 assert!(a_dst.is_some() || b_dst.is_some());
 
-                let src = mapped((a_src, b_src));
-                let dst = mapped((a_dst, b_dst));
-
-                transitions.entry(src).or_default().byte_transitions.insert(*byte_transition, dst);
-
-                if !transitions.contains_key(&dst) {
-                    queue.push((a_dst, b_dst))
-                }
-            }
+                queue.enqueue(a_dst, b_dst);
+                mapped((a_dst, b_dst))
+            });
 
             let ref_transitions =
                 a_transitions.ref_transitions.keys().chain(b_transitions.ref_transitions.keys());
 
-            for ref_transition in ref_transitions {
-                let a_dst = a_transitions.ref_transitions.get(ref_transition).copied();
-                let b_dst = b_transitions.ref_transitions.get(ref_transition).copied();
-
-                assert!(a_dst.is_some() || b_dst.is_some());
+            let ref_transitions = ref_transitions
+                .map(|ref_transition| {
+                    let a_dst = a_transitions.ref_transitions.get(ref_transition).copied();
+                    let b_dst = b_transitions.ref_transitions.get(ref_transition).copied();
 
-                let src = mapped((a_src, b_src));
-                let dst = mapped((a_dst, b_dst));
+                    assert!(a_dst.is_some() || b_dst.is_some());
 
-                transitions.entry(src).or_default().ref_transitions.insert(*ref_transition, dst);
+                    queue.enqueue(a_dst, b_dst);
+                    (*ref_transition, mapped((a_dst, b_dst)))
+                })
+                .collect();
 
-                if !transitions.contains_key(&dst) {
-                    queue.push((a_dst, b_dst))
-                }
-            }
+            let old = transitions.insert(src, Transitions { byte_transitions, ref_transitions });
+            // See `if src == accept { ... }` above. The comment there explains
+            // why this assert is valid.
+            assert_eq!(old, None);
         }
 
         Self { transitions, start, accept }
     }
 
-    pub(crate) fn bytes_from(&self, start: State) -> Option<&Map<Byte, State>> {
-        Some(&self.transitions.get(&start)?.byte_transitions)
+    pub(crate) fn iter_states_from(
+        &self,
+        state: State,
+        src_validity: RangeInclusive<u8>,
+    ) -> impl Iterator<Item = (Byte, State)> {
+        self.transitions
+            .get(&state)
+            .map(move |t| t.byte_transitions.iter_states_from(src_validity))
+            .into_iter()
+            .flatten()
     }
 
-    pub(crate) fn byte_from(&self, start: State, byte: Byte) -> Option<State> {
-        self.transitions.get(&start)?.byte_transitions.get(&byte).copied()
+    pub(crate) fn get_uninit_edge_dst(&self, state: State) -> Option<State> {
+        let transitions = self.transitions.get(&state)?;
+        transitions.byte_transitions.get_uninit_edge_dst()
     }
 
-    pub(crate) fn refs_from(&self, start: State) -> Option<&Map<R, State>> {
-        Some(&self.transitions.get(&start)?.ref_transitions)
+    pub(crate) fn iter_bytes_from(&self, start: State) -> impl Iterator<Item = (Byte, State)> {
path: compiler/rustc_transmute/src/layout/dfa.rs
user: login: joshlf
body: Done
created_at: 2025-04-23T18:06:29Z
author_association: CONTRIBUTOR
diff_hunk: @@ -277,3 +331,235 @@ where
         writeln!(f, "}}")
     }
 }
+
+use edge_set::EdgeSet;
+mod edge_set {
+    use std::cmp;
+
+    use run::*;
+
+    use super::*;
+    mod run {
+        use std::ops::{Range, RangeInclusive};
+
+        use super::*;
+        use crate::layout::Byte;
+
+        #[derive(Eq, PartialEq, Copy, Clone, Debug)]
+        pub(super) struct Run<S> {
+            // `start` and `end` are both inclusive (ie, closed) bounds, as this
+            // is required in order to be able to store 0..=255. We provide
+            // setters and getters which operate on closed/open ranges, which
+            // are more intuitive and easier for performing offset math.
+            start: u8,
+            end: u8,
+            pub(super) dst: S,
+        }
+
+        impl<S> Run<S> {
+            pub(super) fn new(range: RangeInclusive<u8>, dst: S) -> Self {
+                Self { start: *range.start(), end: *range.end(), dst }
+            }
+
+            pub(super) fn from_closed_open(range: Range<u16>, dst: S) -> Self {
path: compiler/rustc_transmute/src/layout/dfa.rs
user: login: joshlf
body: Done
created_at: 2025-04-23T18:06:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -277,3 +331,235 @@ where
         writeln!(f, "}}")
     }
 }
+
+use edge_set::EdgeSet;
+mod edge_set {
+    use std::cmp;
+
+    use run::*;
+
+    use super::*;
+    mod run {
+        use std::ops::{Range, RangeInclusive};
+
+        use super::*;
+        use crate::layout::Byte;
+
+        #[derive(Eq, PartialEq, Copy, Clone, Debug)]
+        pub(super) struct Run<S> {
+            // `start` and `end` are both inclusive (ie, closed) bounds, as this
+            // is required in order to be able to store 0..=255. We provide
+            // setters and getters which operate on closed/open ranges, which
+            // are more intuitive and easier for performing offset math.
+            start: u8,
+            end: u8,
+            pub(super) dst: S,
+        }
+
+        impl<S> Run<S> {
+            pub(super) fn new(range: RangeInclusive<u8>, dst: S) -> Self {
+                Self { start: *range.start(), end: *range.end(), dst }
+            }
+
+            pub(super) fn from_closed_open(range: Range<u16>, dst: S) -> Self {
+                Self {
+                    start: range.start.try_into().unwrap(),
+                    end: (range.end - 1).try_into().unwrap(),
+                    dst,
+                }
+            }
+
+            pub(super) fn contains(&self, idx: u16) -> bool {
+                idx >= u16::from(self.start) && idx <= u16::from(self.end)
+            }
+
+            pub(super) fn as_closed_open(&self) -> (u16, u16) {
path: compiler/rustc_transmute/src/layout/dfa.rs
user: login: joshlf
body: Done
created_at: 2025-04-23T18:06:40Z
author_association: CONTRIBUTOR
diff_hunk: @@ -277,3 +331,235 @@ where
         writeln!(f, "}}")
     }
 }
+
+use edge_set::EdgeSet;
+mod edge_set {
+    use std::cmp;
+
+    use run::*;
+
+    use super::*;
+    mod run {
+        use std::ops::{Range, RangeInclusive};
+
+        use super::*;
+        use crate::layout::Byte;
+
+        #[derive(Eq, PartialEq, Copy, Clone, Debug)]
+        pub(super) struct Run<S> {
+            // `start` and `end` are both inclusive (ie, closed) bounds, as this
+            // is required in order to be able to store 0..=255. We provide
+            // setters and getters which operate on closed/open ranges, which
+            // are more intuitive and easier for performing offset math.
+            start: u8,
+            end: u8,
+            pub(super) dst: S,
+        }
+
+        impl<S> Run<S> {
+            pub(super) fn new(range: RangeInclusive<u8>, dst: S) -> Self {
+                Self { start: *range.start(), end: *range.end(), dst }
+            }
+
+            pub(super) fn from_closed_open(range: Range<u16>, dst: S) -> Self {
+                Self {
+                    start: range.start.try_into().unwrap(),
+                    end: (range.end - 1).try_into().unwrap(),
+                    dst,
+                }
+            }
+
+            pub(super) fn contains(&self, idx: u16) -> bool {
+                idx >= u16::from(self.start) && idx <= u16::from(self.end)
+            }
+
+            pub(super) fn as_closed_open(&self) -> (u16, u16) {
+                (u16::from(self.start), u16::from(self.end) + 1)
+            }
+
+            pub(super) fn as_byte(&self) -> Byte {
+                Byte::new(self.start..=self.end)
+            }
+
+            pub(super) fn map<SS>(self, f: impl FnOnce(S) -> SS) -> Run<SS> {
path: compiler/rustc_transmute/src/layout/dfa.rs
user: login: joshlf
body: Done
created_at: 2025-04-23T18:06:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -277,3 +331,235 @@ where
         writeln!(f, "}}")
     }
 }
+
+use edge_set::EdgeSet;
+mod edge_set {
+    use std::cmp;
+
+    use run::*;
+
+    use super::*;
+    mod run {
+        use std::ops::{Range, RangeInclusive};
+
+        use super::*;
+        use crate::layout::Byte;
+
+        #[derive(Eq, PartialEq, Copy, Clone, Debug)]
+        pub(super) struct Run<S> {
+            // `start` and `end` are both inclusive (ie, closed) bounds, as this
+            // is required in order to be able to store 0..=255. We provide
+            // setters and getters which operate on closed/open ranges, which
+            // are more intuitive and easier for performing offset math.
+            start: u8,
+            end: u8,
+            pub(super) dst: S,
+        }
+
+        impl<S> Run<S> {
+            pub(super) fn new(range: RangeInclusive<u8>, dst: S) -> Self {
+                Self { start: *range.start(), end: *range.end(), dst }
+            }
+
+            pub(super) fn from_closed_open(range: Range<u16>, dst: S) -> Self {
+                Self {
+                    start: range.start.try_into().unwrap(),
+                    end: (range.end - 1).try_into().unwrap(),
+                    dst,
+                }
+            }
+
+            pub(super) fn contains(&self, idx: u16) -> bool {
+                idx >= u16::from(self.start) && idx <= u16::from(self.end)
+            }
+
+            pub(super) fn as_closed_open(&self) -> (u16, u16) {
+                (u16::from(self.start), u16::from(self.end) + 1)
+            }
+
+            pub(super) fn as_byte(&self) -> Byte {
+                Byte::new(self.start..=self.end)
+            }
+
+            pub(super) fn map<SS>(self, f: impl FnOnce(S) -> SS) -> Run<SS> {
+                let Run { start, end, dst } = self;
+                Run { start, end, dst: f(dst) }
+            }
+
+            /// Produces a new `Run` whose lower bound is the greater of
+            /// `self`'s existing lower bound and `lower_bound`.
+            pub(super) fn clamp_lower(self, lower_bound: u8) -> Self {
+                let Run { start, end, dst } = self;
+                Run { start: cmp::max(start, lower_bound), end, dst }
+            }
+        }
+    }
+
+    /// The set of outbound byte edges associated with a DFA node (not including
+    /// reference edges).
+    #[derive(Eq, PartialEq, Clone, Debug)]
+    pub(super) struct EdgeSet<S = State> {
+        // A sequence of runs stored in ascending order. Since the graph is a
+        // DFA, these must be non-overlapping with one another.
+        runs: Vec<Run<S>>,
path: compiler/rustc_transmute/src/layout/dfa.rs
user: login: joshlf
body: Done
created_at: 2025-04-23T18:06:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -277,3 +331,235 @@ where
         writeln!(f, "}}")
     }
 }
+
+use edge_set::EdgeSet;
+mod edge_set {
+    use std::cmp;
+
+    use run::*;
+
+    use super::*;
+    mod run {
+        use std::ops::{Range, RangeInclusive};
+
+        use super::*;
+        use crate::layout::Byte;
+
+        #[derive(Eq, PartialEq, Copy, Clone, Debug)]
+        pub(super) struct Run<S> {
+            // `start` and `end` are both inclusive (ie, closed) bounds, as this
+            // is required in order to be able to store 0..=255. We provide
+            // setters and getters which operate on closed/open ranges, which
+            // are more intuitive and easier for performing offset math.
+            start: u8,
+            end: u8,
+            pub(super) dst: S,
+        }
+
+        impl<S> Run<S> {
+            pub(super) fn new(range: RangeInclusive<u8>, dst: S) -> Self {
+                Self { start: *range.start(), end: *range.end(), dst }
+            }
+
+            pub(super) fn from_closed_open(range: Range<u16>, dst: S) -> Self {
+                Self {
+                    start: range.start.try_into().unwrap(),
+                    end: (range.end - 1).try_into().unwrap(),
+                    dst,
+                }
+            }
+
+            pub(super) fn contains(&self, idx: u16) -> bool {
+                idx >= u16::from(self.start) && idx <= u16::from(self.end)
+            }
+
+            pub(super) fn as_closed_open(&self) -> (u16, u16) {
+                (u16::from(self.start), u16::from(self.end) + 1)
+            }
+
+            pub(super) fn as_byte(&self) -> Byte {
+                Byte::new(self.start..=self.end)
+            }
+
+            pub(super) fn map<SS>(self, f: impl FnOnce(S) -> SS) -> Run<SS> {
+                let Run { start, end, dst } = self;
+                Run { start, end, dst: f(dst) }
+            }
+
+            /// Produces a new `Run` whose lower bound is the greater of
+            /// `self`'s existing lower bound and `lower_bound`.
+            pub(super) fn clamp_lower(self, lower_bound: u8) -> Self {
+                let Run { start, end, dst } = self;
+                Run { start: cmp::max(start, lower_bound), end, dst }
+            }
+        }
+    }
+
+    /// The set of outbound byte edges associated with a DFA node (not including
+    /// reference edges).
+    #[derive(Eq, PartialEq, Clone, Debug)]
+    pub(super) struct EdgeSet<S = State> {
+        // A sequence of runs stored in ascending order. Since the graph is a
+        // DFA, these must be non-overlapping with one another.
+        runs: Vec<Run<S>>,
+        // The edge labeled with the uninit byte, if any.
+        //
+        // FIXME(@joshlf): Make `State` a `NonZero` so that this is NPO'd.
+        uninit: Option<S>,
+    }
+
+    impl<S> EdgeSet<S> {
+        pub(crate) fn new(byte: Byte, dst: S) -> Self {
+            match byte.range() {
+                Some(range) => Self { runs: vec![Run::new(range, dst)], uninit: None },
+                None => Self { runs: Vec::new(), uninit: Some(dst) },
+            }
+        }
+
+        pub(crate) fn empty() -> Self {
+            Self { runs: Vec::new(), uninit: None }
+        }
+
+        #[cfg(test)]
+        pub(crate) fn from_vec(mut edges: Vec<(Byte, S)>) -> Self
path: compiler/rustc_transmute/src/layout/dfa.rs
user: login: joshlf
body: Done
created_at: 2025-04-23T18:07:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -277,3 +331,235 @@ where
         writeln!(f, "}}")
     }
 }
+
+use edge_set::EdgeSet;
+mod edge_set {
+    use std::cmp;
+
+    use run::*;
+
+    use super::*;
+    mod run {
+        use std::ops::{Range, RangeInclusive};
+
+        use super::*;
+        use crate::layout::Byte;
+
+        #[derive(Eq, PartialEq, Copy, Clone, Debug)]
+        pub(super) struct Run<S> {
+            // `start` and `end` are both inclusive (ie, closed) bounds, as this
+            // is required in order to be able to store 0..=255. We provide
+            // setters and getters which operate on closed/open ranges, which
+            // are more intuitive and easier for performing offset math.
+            start: u8,
+            end: u8,
+            pub(super) dst: S,
+        }
+
+        impl<S> Run<S> {
+            pub(super) fn new(range: RangeInclusive<u8>, dst: S) -> Self {
+                Self { start: *range.start(), end: *range.end(), dst }
+            }
+
+            pub(super) fn from_closed_open(range: Range<u16>, dst: S) -> Self {
+                Self {
+                    start: range.start.try_into().unwrap(),
+                    end: (range.end - 1).try_into().unwrap(),
+                    dst,
+                }
+            }
+
+            pub(super) fn contains(&self, idx: u16) -> bool {
+                idx >= u16::from(self.start) && idx <= u16::from(self.end)
+            }
+
+            pub(super) fn as_closed_open(&self) -> (u16, u16) {
+                (u16::from(self.start), u16::from(self.end) + 1)
+            }
+
+            pub(super) fn as_byte(&self) -> Byte {
+                Byte::new(self.start..=self.end)
+            }
+
+            pub(super) fn map<SS>(self, f: impl FnOnce(S) -> SS) -> Run<SS> {
+                let Run { start, end, dst } = self;
+                Run { start, end, dst: f(dst) }
+            }
+
+            /// Produces a new `Run` whose lower bound is the greater of
+            /// `self`'s existing lower bound and `lower_bound`.
+            pub(super) fn clamp_lower(self, lower_bound: u8) -> Self {
+                let Run { start, end, dst } = self;
+                Run { start: cmp::max(start, lower_bound), end, dst }
+            }
+        }
+    }
+
+    /// The set of outbound byte edges associated with a DFA node (not including
+    /// reference edges).
+    #[derive(Eq, PartialEq, Clone, Debug)]
+    pub(super) struct EdgeSet<S = State> {
+        // A sequence of runs stored in ascending order. Since the graph is a
+        // DFA, these must be non-overlapping with one another.
+        runs: Vec<Run<S>>,
+        // The edge labeled with the uninit byte, if any.
+        //
+        // FIXME(@joshlf): Make `State` a `NonZero` so that this is NPO'd.
+        uninit: Option<S>,
+    }
+
+    impl<S> EdgeSet<S> {
+        pub(crate) fn new(byte: Byte, dst: S) -> Self {
+            match byte.range() {
+                Some(range) => Self { runs: vec![Run::new(range, dst)], uninit: None },
+                None => Self { runs: Vec::new(), uninit: Some(dst) },
+            }
+        }
+
+        pub(crate) fn empty() -> Self {
+            Self { runs: Vec::new(), uninit: None }
+        }
+
+        #[cfg(test)]
+        pub(crate) fn from_vec(mut edges: Vec<(Byte, S)>) -> Self
+        where
+            S: Ord,
+        {
+            edges.sort();
+            Self {
+                runs: edges
+                    .into_iter()
+                    .map(|(byte, state)| Run::new(byte.range().unwrap(), state))
+                    .collect(),
+                uninit: None,
+            }
+        }
+
+        pub(crate) fn iter(&self) -> impl Iterator<Item = (Byte, S)>
+        where
+            S: Copy,
+        {
+            self.uninit
+                .map(|dst| (Byte::uninit(), dst))
+                .into_iter()
+                .chain(self.runs.iter().map(|run| (run.as_byte(), run.dst)))
+        }
+
+        pub(crate) fn iter_states_from(
+            &self,
+            byte: RangeInclusive<u8>,
+        ) -> impl Iterator<Item = (Byte, S)>
+        where
+            S: Copy,
+        {
+            // FIXME(@joshlf): Optimize this
path: compiler/rustc_transmute/src/layout/dfa.rs
user: login: joshlf
body: Done
created_at: 2025-04-23T18:07:07Z
author_association: CONTRIBUTOR
diff_hunk: @@ -277,3 +331,235 @@ where
         writeln!(f, "}}")
     }
 }
+
+use edge_set::EdgeSet;
+mod edge_set {
+    use std::cmp;
+
+    use run::*;
+
+    use super::*;
+    mod run {
+        use std::ops::{Range, RangeInclusive};
+
+        use super::*;
+        use crate::layout::Byte;
+
+        #[derive(Eq, PartialEq, Copy, Clone, Debug)]
+        pub(super) struct Run<S> {
+            // `start` and `end` are both inclusive (ie, closed) bounds, as this
+            // is required in order to be able to store 0..=255. We provide
+            // setters and getters which operate on closed/open ranges, which
+            // are more intuitive and easier for performing offset math.
+            start: u8,
+            end: u8,
+            pub(super) dst: S,
+        }
+
+        impl<S> Run<S> {
+            pub(super) fn new(range: RangeInclusive<u8>, dst: S) -> Self {
+                Self { start: *range.start(), end: *range.end(), dst }
+            }
+
+            pub(super) fn from_closed_open(range: Range<u16>, dst: S) -> Self {
+                Self {
+                    start: range.start.try_into().unwrap(),
+                    end: (range.end - 1).try_into().unwrap(),
+                    dst,
+                }
+            }
+
+            pub(super) fn contains(&self, idx: u16) -> bool {
+                idx >= u16::from(self.start) && idx <= u16::from(self.end)
+            }
+
+            pub(super) fn as_closed_open(&self) -> (u16, u16) {
+                (u16::from(self.start), u16::from(self.end) + 1)
+            }
+
+            pub(super) fn as_byte(&self) -> Byte {
+                Byte::new(self.start..=self.end)
+            }
+
+            pub(super) fn map<SS>(self, f: impl FnOnce(S) -> SS) -> Run<SS> {
+                let Run { start, end, dst } = self;
+                Run { start, end, dst: f(dst) }
+            }
+
+            /// Produces a new `Run` whose lower bound is the greater of
+            /// `self`'s existing lower bound and `lower_bound`.
+            pub(super) fn clamp_lower(self, lower_bound: u8) -> Self {
+                let Run { start, end, dst } = self;
+                Run { start: cmp::max(start, lower_bound), end, dst }
+            }
+        }
+    }
+
+    /// The set of outbound byte edges associated with a DFA node (not including
+    /// reference edges).
+    #[derive(Eq, PartialEq, Clone, Debug)]
+    pub(super) struct EdgeSet<S = State> {
+        // A sequence of runs stored in ascending order. Since the graph is a
+        // DFA, these must be non-overlapping with one another.
+        runs: Vec<Run<S>>,
+        // The edge labeled with the uninit byte, if any.
+        //
+        // FIXME(@joshlf): Make `State` a `NonZero` so that this is NPO'd.
+        uninit: Option<S>,
+    }
+
+    impl<S> EdgeSet<S> {
+        pub(crate) fn new(byte: Byte, dst: S) -> Self {
+            match byte.range() {
+                Some(range) => Self { runs: vec![Run::new(range, dst)], uninit: None },
+                None => Self { runs: Vec::new(), uninit: Some(dst) },
+            }
+        }
+
+        pub(crate) fn empty() -> Self {
+            Self { runs: Vec::new(), uninit: None }
+        }
+
+        #[cfg(test)]
+        pub(crate) fn from_vec(mut edges: Vec<(Byte, S)>) -> Self
+        where
+            S: Ord,
+        {
+            edges.sort();
+            Self {
+                runs: edges
+                    .into_iter()
+                    .map(|(byte, state)| Run::new(byte.range().unwrap(), state))
+                    .collect(),
+                uninit: None,
+            }
+        }
+
+        pub(crate) fn iter(&self) -> impl Iterator<Item = (Byte, S)>
+        where
+            S: Copy,
+        {
+            self.uninit
+                .map(|dst| (Byte::uninit(), dst))
+                .into_iter()
+                .chain(self.runs.iter().map(|run| (run.as_byte(), run.dst)))
+        }
+
+        pub(crate) fn iter_states_from(
+            &self,
+            byte: RangeInclusive<u8>,
+        ) -> impl Iterator<Item = (Byte, S)>
+        where
+            S: Copy,
+        {
+            // FIXME(@joshlf): Optimize this
+            self.iter().filter(move |(o, _)| Byte::new(byte.clone()).transmutable_into(&o))
+        }
+
+        pub(crate) fn get_uninit_edge_dst(&self) -> Option<S>
+        where
+            S: Copy,
+        {
+            self.uninit
+        }
+
+        pub(crate) fn map<SS>(self, mut f: impl FnMut(S) -> SS) -> EdgeSet<SS> {
path: compiler/rustc_transmute/src/layout/dfa.rs
user: login: joshlf
body: Done
created_at: 2025-04-23T18:07:13Z
author_association: CONTRIBUTOR
diff_hunk: @@ -62,6 +101,21 @@ impl Ref for ! {
     }
 }
 
+#[cfg(test)]
+impl<const N: usize> Ref for [u8; N] {
path: compiler/rustc_transmute/src/layout/mod.rs
user: login: joshlf
body: Went with the `[(); N]` approach.
created_at: 2025-04-23T18:07:24Z
author_association: CONTRIBUTOR
diff_hunk: @@ -193,26 +197,120 @@ where
                     Quantifier::ForAll
                 };
 
+                let c = &core::cell::RefCell::new(&mut *cache);
                 let bytes_answer = src_quantifier.apply(
-                    // for each of the byte transitions out of the `src_state`...
-                    self.src.bytes_from(src_state).unwrap_or(&Map::default()).into_iter().map(
-                        |(&src_validity, &src_state_prime)| {
-                            // ...try to find a matching transition out of `dst_state`.
-                            if let Some(dst_state_prime) =
-                                self.dst.byte_from(dst_state, src_validity)
-                            {
-                                self.answer_memo(cache, src_state_prime, dst_state_prime)
-                            } else if let Some(dst_state_prime) =
-                                // otherwise, see if `dst_state` has any outgoing `Uninit` transitions
-                                // (any init byte is a valid uninit byte)
-                                self.dst.byte_from(dst_state, Byte::Uninit)
-                            {
-                                self.answer_memo(cache, src_state_prime, dst_state_prime)
-                            } else {
-                                // otherwise, we've exhausted our options.
-                                // the DFAs, from this point onwards, are bit-incompatible.
-                                Answer::No(Reason::DstIsBitIncompatible)
+                    // for each of the byte set transitions out of the `src_state`...
+                    self.src.bytes_from(src_state).flat_map(
+                        move |(src_validity, src_state_prime)| {
+                            // ...find all matching transitions out of `dst_state`.
+
+                            let Some(src_validity) = src_validity.range() else {
+                                // NOTE: We construct an iterator here rather
+                                // than just computing the value directly (via
+                                // `self.answer_memo`) so that, if the iterator
+                                // we produce from this branch is
+                                // short-circuited, we don't waste time
+                                // computing `self.answer_memo` unnecessarily.
+                                // That will specifically happen if
+                                // `src_quantifier == Quantifier::ThereExists`,
+                                // since we emit `Answer::Yes` first (before
+                                // chaining `answer_iter`).
+                                let answer_iter = if let Some(dst_state_prime) =
+                                    self.dst.get_uninit_edge_dst(dst_state)
+                                {
+                                    Either::Left(iter::once_with(move || {
+                                        let mut c = c.borrow_mut();
+                                        self.answer_memo(&mut *c, src_state_prime, dst_state_prime)
+                                    }))
+                                } else {
+                                    Either::Right(iter::once(Answer::No(
+                                        Reason::DstIsBitIncompatible,
+                                    )))
+                                };
+
+                                // When `answer == Answer::No(...)`, there are
+                                // two cases to consider:
+                                // - If `assume.validity`, then we should
+                                //   succeed because the user is responsible for
+                                //   ensuring that the *specific* byte value
+                                //   appearing at runtime is valid for the
+                                //   destination type. When `assume.validity`,
+                                //   `src_quantifier ==
+                                //   Quantifier::ThereExists`, so adding an
+                                //   `Answer::Yes` has the effect of ensuring
+                                //   that the "there exists" is always
+                                //   satisfied.
path: compiler/rustc_transmute/src/maybe_transmutable/mod.rs
user: login: tmiasko
body: Previously, when assuming validity, query nevertheless ensured that there exists a value that is valid for both src and dst:

```rust
#![feature(transmutability)]
use std::mem::{Assume, TransmuteFrom};

pub fn is_transmutable<Src, Dst>()
where
    Dst: TransmuteFrom<Src, { Assume::VALIDITY }>,
{
}

pub union MaybeUninitByte {
    pub uninit: (),
    pub byte: u8,
}

fn main() {
    is_transmutable::<MaybeUninitByte, [u8; 2]>(); // previously rejected, now accepted
}
```
created_at: 2025-04-26T20:56:27Z
author_association: CONTRIBUTOR
diff_hunk: @@ -193,26 +197,120 @@ where
                     Quantifier::ForAll
                 };
 
+                let c = &core::cell::RefCell::new(&mut *cache);
                 let bytes_answer = src_quantifier.apply(
-                    // for each of the byte transitions out of the `src_state`...
-                    self.src.bytes_from(src_state).unwrap_or(&Map::default()).into_iter().map(
-                        |(&src_validity, &src_state_prime)| {
-                            // ...try to find a matching transition out of `dst_state`.
-                            if let Some(dst_state_prime) =
-                                self.dst.byte_from(dst_state, src_validity)
-                            {
-                                self.answer_memo(cache, src_state_prime, dst_state_prime)
-                            } else if let Some(dst_state_prime) =
-                                // otherwise, see if `dst_state` has any outgoing `Uninit` transitions
-                                // (any init byte is a valid uninit byte)
-                                self.dst.byte_from(dst_state, Byte::Uninit)
-                            {
-                                self.answer_memo(cache, src_state_prime, dst_state_prime)
-                            } else {
-                                // otherwise, we've exhausted our options.
-                                // the DFAs, from this point onwards, are bit-incompatible.
-                                Answer::No(Reason::DstIsBitIncompatible)
+                    // for each of the byte set transitions out of the `src_state`...
+                    self.src.bytes_from(src_state).flat_map(
+                        move |(src_validity, src_state_prime)| {
+                            // ...find all matching transitions out of `dst_state`.
+
+                            let Some(src_validity) = src_validity.range() else {
+                                // NOTE: We construct an iterator here rather
+                                // than just computing the value directly (via
+                                // `self.answer_memo`) so that, if the iterator
+                                // we produce from this branch is
+                                // short-circuited, we don't waste time
+                                // computing `self.answer_memo` unnecessarily.
+                                // That will specifically happen if
+                                // `src_quantifier == Quantifier::ThereExists`,
+                                // since we emit `Answer::Yes` first (before
+                                // chaining `answer_iter`).
+                                let answer_iter = if let Some(dst_state_prime) =
+                                    self.dst.get_uninit_edge_dst(dst_state)
+                                {
+                                    Either::Left(iter::once_with(move || {
+                                        let mut c = c.borrow_mut();
+                                        self.answer_memo(&mut *c, src_state_prime, dst_state_prime)
+                                    }))
+                                } else {
+                                    Either::Right(iter::once(Answer::No(
+                                        Reason::DstIsBitIncompatible,
+                                    )))
+                                };
+
+                                // When `answer == Answer::No(...)`, there are
+                                // two cases to consider:
+                                // - If `assume.validity`, then we should
+                                //   succeed because the user is responsible for
+                                //   ensuring that the *specific* byte value
+                                //   appearing at runtime is valid for the
+                                //   destination type. When `assume.validity`,
+                                //   `src_quantifier ==
+                                //   Quantifier::ThereExists`, so adding an
+                                //   `Answer::Yes` has the effect of ensuring
+                                //   that the "there exists" is always
+                                //   satisfied.
path: compiler/rustc_transmute/src/maybe_transmutable/mod.rs
user: login: jswrenn
body: I'd expect this to hit the `Answer::No(Reason::DstIsTooBig)` branch here: 

https://github.com/rust-lang/rust/blob/4326a44e6f0859077b7789d42416b9291b0ff4d1/compiler/rustc_transmute/src/maybe_transmutable/mod.rs#L180-L187

...but that's clearly not happening. cc @joshlf 
created_at: 2025-04-26T21:04:11Z
author_association: MEMBER
diff_hunk: @@ -193,26 +197,120 @@ where
                     Quantifier::ForAll
                 };
 
+                let c = &core::cell::RefCell::new(&mut *cache);
                 let bytes_answer = src_quantifier.apply(
-                    // for each of the byte transitions out of the `src_state`...
-                    self.src.bytes_from(src_state).unwrap_or(&Map::default()).into_iter().map(
-                        |(&src_validity, &src_state_prime)| {
-                            // ...try to find a matching transition out of `dst_state`.
-                            if let Some(dst_state_prime) =
-                                self.dst.byte_from(dst_state, src_validity)
-                            {
-                                self.answer_memo(cache, src_state_prime, dst_state_prime)
-                            } else if let Some(dst_state_prime) =
-                                // otherwise, see if `dst_state` has any outgoing `Uninit` transitions
-                                // (any init byte is a valid uninit byte)
-                                self.dst.byte_from(dst_state, Byte::Uninit)
-                            {
-                                self.answer_memo(cache, src_state_prime, dst_state_prime)
-                            } else {
-                                // otherwise, we've exhausted our options.
-                                // the DFAs, from this point onwards, are bit-incompatible.
-                                Answer::No(Reason::DstIsBitIncompatible)
+                    // for each of the byte set transitions out of the `src_state`...
+                    self.src.bytes_from(src_state).flat_map(
+                        move |(src_validity, src_state_prime)| {
+                            // ...find all matching transitions out of `dst_state`.
+
+                            let Some(src_validity) = src_validity.range() else {
+                                // NOTE: We construct an iterator here rather
+                                // than just computing the value directly (via
+                                // `self.answer_memo`) so that, if the iterator
+                                // we produce from this branch is
+                                // short-circuited, we don't waste time
+                                // computing `self.answer_memo` unnecessarily.
+                                // That will specifically happen if
+                                // `src_quantifier == Quantifier::ThereExists`,
+                                // since we emit `Answer::Yes` first (before
+                                // chaining `answer_iter`).
+                                let answer_iter = if let Some(dst_state_prime) =
+                                    self.dst.get_uninit_edge_dst(dst_state)
+                                {
+                                    Either::Left(iter::once_with(move || {
+                                        let mut c = c.borrow_mut();
+                                        self.answer_memo(&mut *c, src_state_prime, dst_state_prime)
+                                    }))
+                                } else {
+                                    Either::Right(iter::once(Answer::No(
+                                        Reason::DstIsBitIncompatible,
+                                    )))
+                                };
+
+                                // When `answer == Answer::No(...)`, there are
+                                // two cases to consider:
+                                // - If `assume.validity`, then we should
+                                //   succeed because the user is responsible for
+                                //   ensuring that the *specific* byte value
+                                //   appearing at runtime is valid for the
+                                //   destination type. When `assume.validity`,
+                                //   `src_quantifier ==
+                                //   Quantifier::ThereExists`, so adding an
+                                //   `Answer::Yes` has the effect of ensuring
+                                //   that the "there exists" is always
+                                //   satisfied.
path: compiler/rustc_transmute/src/maybe_transmutable/mod.rs
user: login: tmiasko
body: For the record, this was resolved in #140380.
created_at: 2025-05-06T08:58:42Z
author_association: CONTRIBUTOR

PR comments:
user: login: matthiaskrgr
created_at: 2025-04-22T18:41:44Z
body: @bors try @rust-timer queue
author_association: MEMBER
user: login: rust-timer
created_at: 2025-04-22T18:41:46Z
body: Awaiting bors try build completion.

@rustbot label: +S-waiting-on-perf

<!-- rust-timer: temporary -->
author_association: COLLABORATOR
user: login: bors
created_at: 2025-04-22T18:42:55Z
body: :hourglass: Testing commit 4198021968b9471acda67496a70677b9fcf4856f with merge 4bd3736603ed95f1685990b1cd8a7827a64a4c44...
<!-- homu: {"type":"BuildStarted","head_sha":"4198021968b9471acda67496a70677b9fcf4856f","merge_sha":"4bd3736603ed95f1685990b1cd8a7827a64a4c44"} -->
author_association: COLLABORATOR
user: login: rust-log-analyzer
created_at: 2025-04-22T18:52:00Z
body: 
The job **`mingw-check-tidy`** failed! Check out the build log: [(web)](https://github.com/rust-lang/rust/actions/runs/14602216899/job/40962917078) [(plain)](https://github.com/rust-lang/rust/commit/4198021968b9471acda67496a70677b9fcf4856f/checks/40962917078/logs)

<details><summary><i>Click to see the possible cause of the failure (guessed by this bot)</i></summary>

```plain
info: removing rustup binaries
info: rustup is uninstalled
##[group]Image checksum input
mingw-check-tidy
# We use the ghcr base image because ghcr doesn't have a rate limit
# and the mingw-check-tidy job doesn't cache docker images in CI.
FROM ghcr.io/rust-lang/ubuntu:22.04

ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
  g++ \
  make \
---

COPY host-x86_64/mingw-check/validate-toolstate.sh /scripts/
COPY host-x86_64/mingw-check/validate-error-codes.sh /scripts/

# NOTE: intentionally uses python2 for x.py so we can test it still works.
# validate-toolstate only runs in our CI, so it's ok for it to only support python3.
ENV SCRIPT TIDY_PRINT_DIFF=1 python2.7 ../x.py test \
           --stage 0 src/tools/tidy tidyselftest --extra-checks=py,cpp
#
# This file is autogenerated by pip-compile with Python 3.10
# by the following command:
#
#    pip-compile --allow-unsafe --generate-hashes reuse-requirements.in
---
#12 2.799 Building wheels for collected packages: reuse
#12 2.801   Building wheel for reuse (pyproject.toml): started
#12 3.015   Building wheel for reuse (pyproject.toml): finished with status 'done'
#12 3.016   Created wheel for reuse: filename=reuse-4.0.3-cp310-cp310-manylinux_2_35_x86_64.whl size=132719 sha256=5bb60f62728aaedff7162745ce743c7f2f55069b3e7f82e6a37d70df455797cc
#12 3.016   Stored in directory: /tmp/pip-ephem-wheel-cache-j8it_v_8/wheels/3d/8d/0a/e0fc6aba4494b28a967ab5eaf951c121d9c677958714e34532
#12 3.019 Successfully built reuse
#12 3.019 Installing collected packages: boolean-py, binaryornot, tomlkit, reuse, python-debian, markupsafe, license-expression, jinja2, chardet, attrs
#12 3.420 Successfully installed attrs-23.2.0 binaryornot-0.4.4 boolean-py-4.0 chardet-5.2.0 jinja2-3.1.4 license-expression-30.3.0 markupsafe-2.1.5 python-debian-0.1.49 reuse-4.0.3 tomlkit-0.13.0
#12 3.420 WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
#12 3.972 Collecting virtualenv
#12 4.012   Downloading virtualenv-20.30.0-py3-none-any.whl (4.3 MB)
#12 4.084      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.3/4.3 MB 62.7 MB/s eta 0:00:00
#12 4.140 Collecting platformdirs<5,>=3.9.1
#12 4.144   Downloading platformdirs-4.3.7-py3-none-any.whl (18 kB)
#12 4.163 Collecting distlib<1,>=0.3.7
#12 4.167   Downloading distlib-0.3.9-py2.py3-none-any.whl (468 kB)
#12 4.175      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 469.0/469.0 KB 89.4 MB/s eta 0:00:00
#12 4.210 Collecting filelock<4,>=3.12.2
#12 4.214   Downloading filelock-3.18.0-py3-none-any.whl (16 kB)
#12 4.296 Installing collected packages: distlib, platformdirs, filelock, virtualenv
#12 4.483 Successfully installed distlib-0.3.9 filelock-3.18.0 platformdirs-4.3.7 virtualenv-20.30.0
#12 4.483 WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
#12 DONE 4.6s

#13 [7/8] COPY host-x86_64/mingw-check/validate-toolstate.sh /scripts/
#13 DONE 0.0s
---
DirectMap4k:      120768 kB
DirectMap2M:     7219200 kB
DirectMap1G:    11534336 kB
##[endgroup]
Executing TIDY_PRINT_DIFF=1 python2.7 ../x.py test            --stage 0 src/tools/tidy tidyselftest --extra-checks=py,cpp
+ TIDY_PRINT_DIFF=1 python2.7 ../x.py test --stage 0 src/tools/tidy tidyselftest --extra-checks=py,cpp
##[group]Building bootstrap
    Finished `dev` profile [unoptimized] target(s) in 0.05s
##[endgroup]
WARN: currently no CI rustc builds have rustc debug assertions enabled. Please either set `rust.debug-assertions` to `false` if you want to use download CI rustc or set `rust.download-rustc` to `false`.
[TIMING] core::build_steps::tool::LibcxxVersionTool { target: x86_64-unknown-linux-gnu } -- 0.223
---
Diff in /checkout/compiler/rustc_transmute/src/lib.rs:3:
 #![feature(never_type)]
 // tidy-alphabetical-end
 
-pub(crate) use rustc_data_structures::fx::FxIndexMap as Map;
-pub(crate) use rustc_data_structures::fx::FxIndexSet as Set;
+pub(crate) use rustc_data_structures::fx::{FxIndexMap as Map, FxIndexSet as Set};
 
 pub mod layout;
 mod maybe_transmutable;
fmt: checked 5981 files
Build completed unsuccessfully in 0:01:17
```

</details>

author_association: COLLABORATOR
user: login: rust-log-analyzer
created_at: 2025-04-22T20:18:53Z
body: 
The job **`mingw-check-tidy`** failed! Check out the build log: [(web)](https://github.com/rust-lang/rust/actions/runs/14603653398/job/40967663118) [(plain)](https://github.com/rust-lang/rust/commit/ae823fd8f050e5d960f2aa81bb9be73e164f7a61/checks/40967663118/logs)

<details><summary><i>Click to see the possible cause of the failure (guessed by this bot)</i></summary>

```plain
info: removing rustup binaries
info: rustup is uninstalled
##[group]Image checksum input
mingw-check-tidy
# We use the ghcr base image because ghcr doesn't have a rate limit
# and the mingw-check-tidy job doesn't cache docker images in CI.
FROM ghcr.io/rust-lang/ubuntu:22.04

ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
  g++ \
  make \
---

COPY host-x86_64/mingw-check/validate-toolstate.sh /scripts/
COPY host-x86_64/mingw-check/validate-error-codes.sh /scripts/

# NOTE: intentionally uses python2 for x.py so we can test it still works.
# validate-toolstate only runs in our CI, so it's ok for it to only support python3.
ENV SCRIPT TIDY_PRINT_DIFF=1 python2.7 ../x.py test \
           --stage 0 src/tools/tidy tidyselftest --extra-checks=py,cpp
#
# This file is autogenerated by pip-compile with Python 3.10
# by the following command:
#
#    pip-compile --allow-unsafe --generate-hashes reuse-requirements.in
---
#12 2.786 Building wheels for collected packages: reuse
#12 2.788   Building wheel for reuse (pyproject.toml): started
#12 3.003   Building wheel for reuse (pyproject.toml): finished with status 'done'
#12 3.004   Created wheel for reuse: filename=reuse-4.0.3-cp310-cp310-manylinux_2_35_x86_64.whl size=132719 sha256=5bb60f62728aaedff7162745ce743c7f2f55069b3e7f82e6a37d70df455797cc
#12 3.004   Stored in directory: /tmp/pip-ephem-wheel-cache-y3g7oq2x/wheels/3d/8d/0a/e0fc6aba4494b28a967ab5eaf951c121d9c677958714e34532
#12 3.007 Successfully built reuse
#12 3.007 Installing collected packages: boolean-py, binaryornot, tomlkit, reuse, python-debian, markupsafe, license-expression, jinja2, chardet, attrs
#12 3.398 Successfully installed attrs-23.2.0 binaryornot-0.4.4 boolean-py-4.0 chardet-5.2.0 jinja2-3.1.4 license-expression-30.3.0 markupsafe-2.1.5 python-debian-0.1.49 reuse-4.0.3 tomlkit-0.13.0
#12 3.398 WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
#12 3.948 Collecting virtualenv
#12 3.985   Downloading virtualenv-20.30.0-py3-none-any.whl (4.3 MB)
#12 4.053      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.3/4.3 MB 66.3 MB/s eta 0:00:00
#12 4.094 Collecting distlib<1,>=0.3.7
#12 4.097   Downloading distlib-0.3.9-py2.py3-none-any.whl (468 kB)
#12 4.104      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 469.0/469.0 KB 93.0 MB/s eta 0:00:00
#12 4.138 Collecting platformdirs<5,>=3.9.1
#12 4.141   Downloading platformdirs-4.3.7-py3-none-any.whl (18 kB)
#12 4.179 Collecting filelock<4,>=3.12.2
#12 4.182   Downloading filelock-3.18.0-py3-none-any.whl (16 kB)
#12 4.265 Installing collected packages: distlib, platformdirs, filelock, virtualenv
#12 4.449 Successfully installed distlib-0.3.9 filelock-3.18.0 platformdirs-4.3.7 virtualenv-20.30.0
#12 4.449 WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
#12 DONE 4.5s

#13 [7/8] COPY host-x86_64/mingw-check/validate-toolstate.sh /scripts/
#13 DONE 0.0s
---
DirectMap4k:      124864 kB
DirectMap2M:     6166528 kB
DirectMap1G:    12582912 kB
##[endgroup]
Executing TIDY_PRINT_DIFF=1 python2.7 ../x.py test            --stage 0 src/tools/tidy tidyselftest --extra-checks=py,cpp
+ TIDY_PRINT_DIFF=1 python2.7 ../x.py test --stage 0 src/tools/tidy tidyselftest --extra-checks=py,cpp
##[group]Building bootstrap
    Finished `dev` profile [unoptimized] target(s) in 0.05s
##[endgroup]
WARN: currently no CI rustc builds have rustc debug assertions enabled. Please either set `rust.debug-assertions` to `false` if you want to use download CI rustc or set `rust.download-rustc` to `false`.
[TIMING] core::build_steps::tool::LibcxxVersionTool { target: x86_64-unknown-linux-gnu } -- 0.231
---
fmt check
fmt: checked 5981 files
tidy check
tidy: Skipping binary file check, read-only filesystem
##[error]tidy error: /checkout/compiler/rustc_transmute/src/layout/mod.rs:21: TODO is used for tasks that should be done before merging a PR; If you want to leave a message in the codebase use FIXME
##[error]tidy error: /checkout/compiler/rustc_transmute/src/layout/dfa.rs:240: TODO is used for tasks that should be done before merging a PR; If you want to leave a message in the codebase use FIXME
##[error]tidy error: /checkout/compiler/rustc_transmute/src/layout/dfa.rs:395: TODO is used for tasks that should be done before merging a PR; If you want to leave a message in the codebase use FIXME
##[error]tidy error: /checkout/compiler/rustc_transmute/src/layout/dfa.rs:443: TODO is used for tasks that should be done before merging a PR; If you want to leave a message in the codebase use FIXME
##[error]tidy error: /checkout/compiler/rustc_transmute/src/layout/dfa.rs:497: TODO is used for tasks that should be done before merging a PR; If you want to leave a message in the codebase use FIXME
removing old virtual environment
creating virtual environment at '/checkout/obj/build/venv' using 'python3.10' and 'venv'
creating virtual environment at '/checkout/obj/build/venv' using 'python3.10' and 'virtualenv'
Requirement already satisfied: pip in ./build/venv/lib/python3.10/site-packages (25.0.1)
linting python files
All checks passed!
checking python file formatting
26 files already formatted
checking C++ file formatting
some tidy checks failed
Command has failed. Rerun with -v to see more details.
Build completed unsuccessfully in 0:01:49
  local time: Tue Apr 22 20:18:38 UTC 2025
  network time: Tue, 22 Apr 2025 20:18:38 GMT
##[error]Process completed with exit code 1.
Post job cleanup.
```

</details>

author_association: COLLABORATOR
user: login: rust-log-analyzer
created_at: 2025-04-22T21:04:38Z
body: 
The job **`mingw-check-tidy`** failed! Check out the build log: [(web)](https://github.com/rust-lang/rust/actions/runs/14604441879/job/40970199874) [(plain)](https://github.com/rust-lang/rust/commit/ab0949471a006c9d565aec9fa61aeb4936f66818/checks/40970199874/logs)

<details><summary><i>Click to see the possible cause of the failure (guessed by this bot)</i></summary>

```plain
info: removing rustup binaries
info: rustup is uninstalled
##[group]Image checksum input
mingw-check-tidy
# We use the ghcr base image because ghcr doesn't have a rate limit
# and the mingw-check-tidy job doesn't cache docker images in CI.
FROM ghcr.io/rust-lang/ubuntu:22.04

ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
  g++ \
  make \
---

COPY host-x86_64/mingw-check/validate-toolstate.sh /scripts/
COPY host-x86_64/mingw-check/validate-error-codes.sh /scripts/

# NOTE: intentionally uses python2 for x.py so we can test it still works.
# validate-toolstate only runs in our CI, so it's ok for it to only support python3.
ENV SCRIPT TIDY_PRINT_DIFF=1 python2.7 ../x.py test \
           --stage 0 src/tools/tidy tidyselftest --extra-checks=py,cpp
#
# This file is autogenerated by pip-compile with Python 3.10
# by the following command:
#
#    pip-compile --allow-unsafe --generate-hashes reuse-requirements.in
---
#12 2.729 Building wheels for collected packages: reuse
#12 2.730   Building wheel for reuse (pyproject.toml): started
#12 2.946   Building wheel for reuse (pyproject.toml): finished with status 'done'
#12 2.947   Created wheel for reuse: filename=reuse-4.0.3-cp310-cp310-manylinux_2_35_x86_64.whl size=132719 sha256=5bb60f62728aaedff7162745ce743c7f2f55069b3e7f82e6a37d70df455797cc
#12 2.948   Stored in directory: /tmp/pip-ephem-wheel-cache-p9vxh_e4/wheels/3d/8d/0a/e0fc6aba4494b28a967ab5eaf951c121d9c677958714e34532
#12 2.950 Successfully built reuse
#12 2.951 Installing collected packages: boolean-py, binaryornot, tomlkit, reuse, python-debian, markupsafe, license-expression, jinja2, chardet, attrs
#12 3.340 Successfully installed attrs-23.2.0 binaryornot-0.4.4 boolean-py-4.0 chardet-5.2.0 jinja2-3.1.4 license-expression-30.3.0 markupsafe-2.1.5 python-debian-0.1.49 reuse-4.0.3 tomlkit-0.13.0
#12 3.340 WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
#12 3.870 Collecting virtualenv
#12 3.908   Downloading virtualenv-20.30.0-py3-none-any.whl (4.3 MB)
#12 3.970      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.3/4.3 MB 73.1 MB/s eta 0:00:00
#12 4.024 Collecting platformdirs<5,>=3.9.1
#12 4.027   Downloading platformdirs-4.3.7-py3-none-any.whl (18 kB)
#12 4.045 Collecting distlib<1,>=0.3.7
#12 4.048   Downloading distlib-0.3.9-py2.py3-none-any.whl (468 kB)
#12 4.055      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 469.0/469.0 KB 94.4 MB/s eta 0:00:00
#12 4.089 Collecting filelock<4,>=3.12.2
#12 4.092   Downloading filelock-3.18.0-py3-none-any.whl (16 kB)
#12 4.172 Installing collected packages: distlib, platformdirs, filelock, virtualenv
#12 4.354 Successfully installed distlib-0.3.9 filelock-3.18.0 platformdirs-4.3.7 virtualenv-20.30.0
#12 4.354 WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
#12 DONE 4.4s

#13 [7/8] COPY host-x86_64/mingw-check/validate-toolstate.sh /scripts/
#13 DONE 0.0s
---
DirectMap4k:      116672 kB
DirectMap2M:     8271872 kB
DirectMap1G:    10485760 kB
##[endgroup]
Executing TIDY_PRINT_DIFF=1 python2.7 ../x.py test            --stage 0 src/tools/tidy tidyselftest --extra-checks=py,cpp
+ TIDY_PRINT_DIFF=1 python2.7 ../x.py test --stage 0 src/tools/tidy tidyselftest --extra-checks=py,cpp
##[group]Building bootstrap
    Finished `dev` profile [unoptimized] target(s) in 0.05s
##[endgroup]
WARN: currently no CI rustc builds have rustc debug assertions enabled. Please either set `rust.debug-assertions` to `false` if you want to use download CI rustc or set `rust.download-rustc` to `false`.
[TIMING] core::build_steps::tool::LibcxxVersionTool { target: x86_64-unknown-linux-gnu } -- 0.221
---
fmt check
fmt: checked 5981 files
tidy check
tidy: Skipping binary file check, read-only filesystem
##[error]tidy error: /checkout/compiler/rustc_transmute/src/layout/mod.rs:22: TODO is used for tasks that should be done before merging a PR; If you want to leave a message in the codebase use FIXME
##[error]tidy error: /checkout/compiler/rustc_transmute/src/layout/dfa.rs:407: TODO is used for tasks that should be done before merging a PR; If you want to leave a message in the codebase use FIXME
##[error]tidy error: /checkout/compiler/rustc_transmute/src/layout/dfa.rs:455: TODO is used for tasks that should be done before merging a PR; If you want to leave a message in the codebase use FIXME
##[error]tidy error: /checkout/compiler/rustc_transmute/src/layout/dfa.rs:533: TODO is used for tasks that should be done before merging a PR; If you want to leave a message in the codebase use FIXME
removing old virtual environment
creating virtual environment at '/checkout/obj/build/venv' using 'python3.10' and 'venv'
creating virtual environment at '/checkout/obj/build/venv' using 'python3.10' and 'virtualenv'
Requirement already satisfied: pip in ./build/venv/lib/python3.10/site-packages (25.0.1)
linting python files
All checks passed!
checking python file formatting
26 files already formatted
checking C++ file formatting
some tidy checks failed
Command has failed. Rerun with -v to see more details.
Build completed unsuccessfully in 0:01:46
  local time: Tue Apr 22 21:04:27 UTC 2025
  network time: Tue, 22 Apr 2025 21:04:27 GMT
##[error]Process completed with exit code 1.
Post job cleanup.
```

</details>

author_association: COLLABORATOR
user: login: matthiaskrgr
created_at: 2025-04-23T06:09:29Z
body: @bors try @rust-timer queue
author_association: MEMBER
user: login: rust-timer
created_at: 2025-04-23T06:09:32Z
body: Awaiting bors try build completion.

@rustbot label: +S-waiting-on-perf

<!-- rust-timer: temporary -->
author_association: COLLABORATOR
user: login: bors
created_at: 2025-04-23T06:10:39Z
body: :hourglass: Trying commit 2431d882fb8c6dccb44f089d8a638248f008e18e with merge a0d017e8bbf248582473ad0c1a931dbb8755d47d...
<!-- homu: {"type":"TryBuildStarted","head_sha":"2431d882fb8c6dccb44f089d8a638248f008e18e","merge_sha":"a0d017e8bbf248582473ad0c1a931dbb8755d47d"} -->
author_association: COLLABORATOR
user: login: bors
created_at: 2025-04-23T08:17:26Z
body: :sunny: Try build successful - [checks-actions](https://github.com/rust-lang-ci/rust/actions/runs/14611217500/job/40995726797)
Build commit: a0d017e8bbf248582473ad0c1a931dbb8755d47d (`a0d017e8bbf248582473ad0c1a931dbb8755d47d`)
<!-- homu: {"type":"TryBuildCompleted","builders":{"checks-actions":"https://github.com/rust-lang-ci/rust/actions/runs/14611217500/job/40995726797"},"merge_sha":"a0d017e8bbf248582473ad0c1a931dbb8755d47d"} -->
author_association: COLLABORATOR
user: login: rust-timer
created_at: 2025-04-23T08:17:29Z
body: Queued a0d017e8bbf248582473ad0c1a931dbb8755d47d with parent 1a5bf12f6586d724ed5ff40e58e06c0233560c0e, future [comparison URL](https://perf.rust-lang.org/compare.html?start=1a5bf12f6586d724ed5ff40e58e06c0233560c0e&end=a0d017e8bbf248582473ad0c1a931dbb8755d47d).
There are currently 0 preceding artifacts in the [queue](https://perf.rust-lang.org/status.html).
It will probably take at least ~1.5 hours until the benchmark run finishes.
<!-- rust-timer: temporary -->
author_association: COLLABORATOR
user: login: rust-timer
created_at: 2025-04-23T09:48:15Z
body: Finished benchmarking commit (a0d017e8bbf248582473ad0c1a931dbb8755d47d): [comparison URL](https://perf.rust-lang.org/compare.html?start=1a5bf12f6586d724ed5ff40e58e06c0233560c0e&end=a0d017e8bbf248582473ad0c1a931dbb8755d47d&stat=instructions:u).

### Overall result: ❌ regressions - please read the text below


Benchmarking this pull request likely means that it is perf-sensitive, so we're automatically marking it as not fit for rolling up. While you can manually mark this PR as fit for rollup, we strongly recommend not doing so since this PR may lead to changes in compiler perf.

**Next Steps**: If you can justify the regressions found in this try perf run, please indicate this with `@rustbot label: +perf-regression-triaged` along with sufficient written justification. If you cannot justify the regressions please fix the regressions and do another perf run. If the next run shows neutral or positive results, the label will be automatically removed.

@bors rollup=never
@rustbot label: -S-waiting-on-perf +perf-regression


### [Instruction count](https://perf.rust-lang.org/compare.html?start=1a5bf12f6586d724ed5ff40e58e06c0233560c0e&end=a0d017e8bbf248582473ad0c1a931dbb8755d47d&stat=instructions:u)
This is the most reliable metric that we have; it was used to determine the overall result at the top of this comment. However, even this metric can sometimes exhibit noise.

|                                    | mean  | range          | count |
|:----------------------------------:|:-----:|:--------------:|:-----:|
| Regressions ❌ <br /> (primary)    | 0.2%  | [0.1%, 0.3%]   | 21    |
| Regressions ❌ <br /> (secondary)  | 0.3%  | [0.1%, 0.6%]   | 58    |
| Improvements ✅ <br /> (primary)   | -0.2% | [-0.2%, -0.2%] | 1     |
| Improvements ✅ <br /> (secondary) | -     | -              | 0     |
| All ❌✅ (primary)                 | 0.2%  | [-0.2%, 0.3%]  | 22    |

### [Max RSS (memory usage)](https://perf.rust-lang.org/compare.html?start=1a5bf12f6586d724ed5ff40e58e06c0233560c0e&end=a0d017e8bbf248582473ad0c1a931dbb8755d47d&stat=max-rss)
<details>
<summary>Results (primary 0.0%, secondary 0.2%)</summary>

This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.

|                                    | mean  | range          | count |
|:----------------------------------:|:-----:|:--------------:|:-----:|
| Regressions ❌ <br /> (primary)    | 1.1%  | [0.4%, 2.5%]   | 3     |
| Regressions ❌ <br /> (secondary)  | 2.3%  | [1.9%, 2.5%]   | 6     |
| Improvements ✅ <br /> (primary)   | -1.6% | [-2.4%, -0.7%] | 2     |
| Improvements ✅ <br /> (secondary) | -6.1% | [-7.9%, -4.3%] | 2     |
| All ❌✅ (primary)                 | 0.0%  | [-2.4%, 2.5%]  | 5     |
</details>

### [Cycles](https://perf.rust-lang.org/compare.html?start=1a5bf12f6586d724ed5ff40e58e06c0233560c0e&end=a0d017e8bbf248582473ad0c1a931dbb8755d47d&stat=cycles:u)
<details>
<summary>Results (secondary -2.3%)</summary>

This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.

|                                    | mean  | range          | count |
|:----------------------------------:|:-----:|:--------------:|:-----:|
| Regressions ❌ <br /> (primary)    | -     | -              | 0     |
| Regressions ❌ <br /> (secondary)  | -     | -              | 0     |
| Improvements ✅ <br /> (primary)   | -     | -              | 0     |
| Improvements ✅ <br /> (secondary) | -2.3% | [-2.8%, -2.0%] | 3     |
| All ❌✅ (primary)                 | -     | -              | 0     |
</details>

### [Binary size](https://perf.rust-lang.org/compare.html?start=1a5bf12f6586d724ed5ff40e58e06c0233560c0e&end=a0d017e8bbf248582473ad0c1a931dbb8755d47d&stat=size:linked_artifact)
This benchmark run did not return any relevant results for this metric.

**Bootstrap**: 774.578s -> 775.487s (0.12%)
**Artifact size**: 365.08 MiB -> 365.07 MiB (-0.00%)
author_association: COLLABORATOR
user: login: rustbot
created_at: 2025-04-23T18:05:14Z
body: These commits modify the `Cargo.lock` file. Unintentional changes to `Cargo.lock` can be introduced when switching branches and rebasing PRs.

If this was unintentional then you should revert the changes before this PR is merged.
Otherwise, you can ignore this comment.

author_association: COLLABORATOR
user: login: jswrenn
created_at: 2025-04-23T18:11:53Z
body: Thanks!

@matthiaskrgr these changes are limited to the performance of the experimental `TransmuteFrom` trait. This trait is currently only used in ui tests of the `TransmuteFrom` trait. The integration of `TransmuteFrom` into the trait solver is unmodified here, so this PR should not have any impact on broader rustc performance.

@bors r+
author_association: MEMBER
user: login: bors
created_at: 2025-04-23T18:11:56Z
body: :pushpin: Commit 048863e1bd4a36046e9dd03e1c288be96d86efb1 has been approved by `jswrenn`

It is now in the [queue](https://bors.rust-lang.org/homu/queue/rust) for this repository.

<!-- @bors r=jswrenn 048863e1bd4a36046e9dd03e1c288be96d86efb1 -->
<!-- homu: {"type":"Approved","sha":"048863e1bd4a36046e9dd03e1c288be96d86efb1","approver":"jswrenn","queue":"https://bors.rust-lang.org/homu/queue/rust"} -->
author_association: COLLABORATOR
user: login: rust-log-analyzer
created_at: 2025-04-23T18:15:37Z
body: 
The job **`mingw-check-tidy`** failed! Check out the build log: [(web)](https://github.com/rust-lang/rust/actions/runs/14624980695/job/41034274301) [(plain)](https://github.com/rust-lang/rust/commit/048863e1bd4a36046e9dd03e1c288be96d86efb1/checks/41034274301/logs)

<details><summary><i>Click to see the possible cause of the failure (guessed by this bot)</i></summary>

```plain
info: removing rustup binaries
info: rustup is uninstalled
##[group]Image checksum input
mingw-check-tidy
# We use the ghcr base image because ghcr doesn't have a rate limit
# and the mingw-check-tidy job doesn't cache docker images in CI.
FROM ghcr.io/rust-lang/ubuntu:22.04

ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
  g++ \
  make \
---

COPY host-x86_64/mingw-check/validate-toolstate.sh /scripts/
COPY host-x86_64/mingw-check/validate-error-codes.sh /scripts/

# NOTE: intentionally uses python2 for x.py so we can test it still works.
# validate-toolstate only runs in our CI, so it's ok for it to only support python3.
ENV SCRIPT TIDY_PRINT_DIFF=1 python2.7 ../x.py test \
           --stage 0 src/tools/tidy tidyselftest --extra-checks=py,cpp
#
# This file is autogenerated by pip-compile with Python 3.10
# by the following command:
#
#    pip-compile --allow-unsafe --generate-hashes reuse-requirements.in
---
#12 4.544   Downloading filelock-3.18.0-py3-none-any.whl (16 kB)
#12 4.568 Collecting distlib<1,>=0.3.7
#12 4.578   Downloading distlib-0.3.9-py2.py3-none-any.whl (468 kB)
#12 4.586      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 469.0/469.0 KB 79.3 MB/s eta 0:00:00
#12 4.626 Collecting platformdirs<5,>=3.9.1
#12 4.637   Downloading platformdirs-4.3.7-py3-none-any.whl (18 kB)
#12 4.720 Installing collected packages: distlib, platformdirs, filelock, virtualenv
#12 4.909 Successfully installed distlib-0.3.9 filelock-3.18.0 platformdirs-4.3.7 virtualenv-20.30.0
#12 4.910 WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
#12 DONE 5.0s

#13 [7/8] COPY host-x86_64/mingw-check/validate-toolstate.sh /scripts/
#13 DONE 0.0s
---
DirectMap4k:      128960 kB
DirectMap2M:     7211008 kB
DirectMap1G:    11534336 kB
##[endgroup]
Executing TIDY_PRINT_DIFF=1 python2.7 ../x.py test            --stage 0 src/tools/tidy tidyselftest --extra-checks=py,cpp
+ TIDY_PRINT_DIFF=1 python2.7 ../x.py test --stage 0 src/tools/tidy tidyselftest --extra-checks=py,cpp
##[group]Building bootstrap
    Finished `dev` profile [unoptimized] target(s) in 0.05s
##[endgroup]
WARN: currently no CI rustc builds have rustc debug assertions enabled. Please either set `rust.debug-assertions` to `false` if you want to use download CI rustc or set `rust.download-rustc` to `false`.
[TIMING] core::build_steps::tool::LibcxxVersionTool { target: x86_64-unknown-linux-gnu } -- 0.273
---
[TIMING] core::build_steps::tool::Tidy { compiler: Compiler { stage: 0, host: x86_64-unknown-linux-gnu, forced_compiler: false }, target: x86_64-unknown-linux-gnu } -- 0.000
fmt check
Diff in /checkout/compiler/rustc_transmute/src/layout/dfa.rs:334:
 
 use edge_set::EdgeSet;
 mod edge_set {
-    use smallvec::{SmallVec, smallvec};
-
     use std::cmp;
 
     use run::*;
Diff in /checkout/compiler/rustc_transmute/src/layout/dfa.rs:342:
+    use smallvec::{SmallVec, smallvec};
 
     use super::*;
     mod run {
fmt: checked 5982 files
Build completed unsuccessfully in 0:01:18
```

</details>

author_association: COLLABORATOR
user: login: jswrenn
created_at: 2025-04-23T18:46:40Z
body: @bors r+
author_association: MEMBER
user: login: bors
created_at: 2025-04-23T18:46:42Z
body: :pushpin: Commit 4326a44e6f0859077b7789d42416b9291b0ff4d1 has been approved by `jswrenn`

It is now in the [queue](https://bors.rust-lang.org/homu/queue/rust) for this repository.

<!-- @bors r=jswrenn 4326a44e6f0859077b7789d42416b9291b0ff4d1 -->
<!-- homu: {"type":"Approved","sha":"4326a44e6f0859077b7789d42416b9291b0ff4d1","approver":"jswrenn","queue":"https://bors.rust-lang.org/homu/queue/rust"} -->
author_association: COLLABORATOR
user: login: Zalathar
created_at: 2025-04-24T03:24:11Z
body: Scheduling: Prefer rollup=never PRs over non-rolled-up iffy PRs.

@bors p=1
author_association: MEMBER
user: login: bors
created_at: 2025-04-24T05:36:42Z
body: :hourglass: Testing commit 4326a44e6f0859077b7789d42416b9291b0ff4d1 with merge 7f695232a80fa1833e2282f2577c5e1ff066bf39...
<!-- homu: {"type":"BuildStarted","head_sha":"4326a44e6f0859077b7789d42416b9291b0ff4d1","merge_sha":"7f695232a80fa1833e2282f2577c5e1ff066bf39"} -->
author_association: COLLABORATOR
user: login: panstromek
created_at: 2025-04-24T05:55:59Z
body: > @matthiaskrgr these changes are limited to the performance of the experimental TransmuteFrom trait. This trait is currently only used in ui tests of the TransmuteFrom trait. The integration of TransmuteFrom into the trait solver is unmodified here, so this PR should not have any impact on broader rustc performance.

Regressions from the perf run are all real, so this doesn't seem to inluence only `TransmuteFrom` (various unrelated benchmarks regressed). This will definitely come up in next week perf triage, do we have some explanation for what happened there and if it's justified before this lands?
author_association: CONTRIBUTOR
user: login: bors
created_at: 2025-04-24T08:49:40Z
body: :sunny: Test successful - [checks-actions](https://github.com/rust-lang-ci/rust/actions/runs/14634233447/job/41071666223)
Approved by: jswrenn
Pushing 7f695232a80fa1833e2282f2577c5e1ff066bf39 to master...
<!-- homu: {"type":"BuildCompleted","approved_by":"jswrenn","base_ref":"master","builders":{"checks-actions":"https://github.com/rust-lang-ci/rust/actions/runs/14634233447/job/41071666223"},"merge_sha":"7f695232a80fa1833e2282f2577c5e1ff066bf39"} -->
author_association: COLLABORATOR
user: login: github-actions[bot]
created_at: 2025-04-24T08:52:49Z
body: <details>
<summary>What is this?</summary>
This is an experimental post-merge analysis report that shows differences in test outcomes between the merged PR and its parent PR.
</details>


Comparing c02a4f0852e6665cf3df3867982021383f5615df (parent) -> 7f695232a80fa1833e2282f2577c5e1ff066bf39 (this PR)

# Test differences
<details>
<summary>Show 7 test diffs
</summary>

## Stage 1
- `maybe_transmutable::tests::alt::should_permit_identity_transmutation`: [missing] -> pass (**J0**)
- `maybe_transmutable::tests::benches::bench_dfa_from_tree`: [missing] -> pass (**J0**)
- `maybe_transmutable::tests::benches::bench_transmute`: [missing] -> pass (**J0**)
- `maybe_transmutable::tests::bool::transmute_u8`: [missing] -> pass (**J0**)
- `maybe_transmutable::tests::r#ref::should_permit_identity_transmutation`: [missing] -> pass (**J0**)
- `maybe_transmutable::tests::size::size`: [missing] -> pass (**J0**)
- `maybe_transmutable::tests::uninit::size`: [missing] -> pass (**J0**)

**Job group index**

- **J0**: [aarch64-apple](https://github.com/rust-lang-ci/rust/actions/runs/14634233447#summary-41062035564), [aarch64-gnu](https://github.com/rust-lang-ci/rust/actions/runs/14634233447#summary-41062017821), [i686-gnu-2](https://github.com/rust-lang-ci/rust/actions/runs/14634233447#summary-41062028927), [i686-gnu-nopt-2](https://github.com/rust-lang-ci/rust/actions/runs/14634233447#summary-41062029475), [i686-msvc-2](https://github.com/rust-lang-ci/rust/actions/runs/14634233447#summary-41062036682), [x86_64-apple-1](https://github.com/rust-lang-ci/rust/actions/runs/14634233447#summary-41062034768), [x86_64-gnu](https://github.com/rust-lang-ci/rust/actions/runs/14634233447#summary-41062030439), [x86_64-gnu-llvm-19-3](https://github.com/rust-lang-ci/rust/actions/runs/14634233447#summary-41062033417), [x86_64-gnu-llvm-20-3](https://github.com/rust-lang-ci/rust/actions/runs/14634233447#summary-41062032230), [x86_64-gnu-nopt](https://github.com/rust-lang-ci/rust/actions/runs/14634233447#summary-41062033699), [x86_64-gnu-stable](https://github.com/rust-lang-ci/rust/actions/runs/14634233447#summary-41062030719), [x86_64-mingw-2](https://github.com/rust-lang-ci/rust/actions/runs/14634233447#summary-41062038387), [x86_64-msvc-2](https://github.com/rust-lang-ci/rust/actions/runs/14634233447#summary-41062036150)
</details>

<details>
<summary>Test dashboard</summary>

Run

```bash
cargo run --manifest-path src/ci/citool/Cargo.toml -- \
    test-dashboard 7f695232a80fa1833e2282f2577c5e1ff066bf39 --output-dir test-dashboard
```
And then open `test-dashboard/index.html` in your browser to see an overview of all executed tests.

</details>

# Job duration changes
1. [dist-aarch64-linux](https://github.com/rust-lang-ci/rust/actions/runs/14634233447#summary-41062020280): 5460.7s -> 7562.7s (38.5%)
2. [dist-apple-various](https://github.com/rust-lang-ci/rust/actions/runs/14634233447#summary-41062034500): 6406.0s -> 7955.6s (24.2%)
3. [aarch64-apple](https://github.com/rust-lang-ci/rust/actions/runs/14634233447#summary-41062035564): 4239.3s -> 3691.7s (-12.9%)
4. [x86_64-apple-2](https://github.com/rust-lang-ci/rust/actions/runs/14634233447#summary-41062035063): 5451.5s -> 4825.3s (-11.5%)
5. [x86_64-apple-1](https://github.com/rust-lang-ci/rust/actions/runs/14634233447#summary-41062034768): 7714.6s -> 7103.8s (-7.9%)
6. [x86_64-msvc-2](https://github.com/rust-lang-ci/rust/actions/runs/14634233447#summary-41062036150): 6765.3s -> 7184.7s (6.2%)
7. [i686-gnu-2](https://github.com/rust-lang-ci/rust/actions/runs/14634233447#summary-41062028927): 6739.7s -> 6334.7s (-6.0%)
8. [x86_64-msvc-ext3](https://github.com/rust-lang-ci/rust/actions/runs/14634233447#summary-41062037612): 7891.1s -> 7430.1s (-5.8%)
9. [dist-ohos](https://github.com/rust-lang-ci/rust/actions/runs/14634233447#summary-41062025485): 10561.0s -> 11037.3s (4.5%)
10. [dist-x86_64-apple](https://github.com/rust-lang-ci/rust/actions/runs/14634233447#summary-41062034194): 9242.5s -> 9631.3s (4.2%)

<details>
<summary>How to interpret the job duration changes?</summary>

Job durations can vary a lot, based on the actual runner instance
that executed the job, system noise, invalidated caches, etc. The table above is provided
mostly for t-infra members, for simpler debugging of potential CI slow-downs.
</details>


author_association: CONTRIBUTOR
user: login: rust-timer
created_at: 2025-04-24T10:20:54Z
body: Finished benchmarking commit (7f695232a80fa1833e2282f2577c5e1ff066bf39): [comparison URL](https://perf.rust-lang.org/compare.html?start=c02a4f0852e6665cf3df3867982021383f5615df&end=7f695232a80fa1833e2282f2577c5e1ff066bf39&stat=instructions:u).

### Overall result: no relevant changes - no action needed


@rustbot label: -perf-regression



### [Instruction count](https://perf.rust-lang.org/compare.html?start=c02a4f0852e6665cf3df3867982021383f5615df&end=7f695232a80fa1833e2282f2577c5e1ff066bf39&stat=instructions:u)
This benchmark run did not return any relevant results for this metric.

### [Max RSS (memory usage)](https://perf.rust-lang.org/compare.html?start=c02a4f0852e6665cf3df3867982021383f5615df&end=7f695232a80fa1833e2282f2577c5e1ff066bf39&stat=max-rss)
<details>
<summary>Results (primary 0.1%, secondary 3.3%)</summary>

This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.

|                                    | mean  | range          | count |
|:----------------------------------:|:-----:|:--------------:|:-----:|
| Regressions ❌ <br /> (primary)    | 0.7%  | [0.7%, 0.7%]   | 1     |
| Regressions ❌ <br /> (secondary)  | 3.3%  | [2.0%, 4.6%]   | 2     |
| Improvements ✅ <br /> (primary)   | -0.5% | [-0.5%, -0.5%] | 1     |
| Improvements ✅ <br /> (secondary) | -     | -              | 0     |
| All ❌✅ (primary)                 | 0.1%  | [-0.5%, 0.7%]  | 2     |
</details>

### [Cycles](https://perf.rust-lang.org/compare.html?start=c02a4f0852e6665cf3df3867982021383f5615df&end=7f695232a80fa1833e2282f2577c5e1ff066bf39&stat=cycles:u)
<details>
<summary>Results (primary -0.4%)</summary>

This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.

|                                    | mean  | range          | count |
|:----------------------------------:|:-----:|:--------------:|:-----:|
| Regressions ❌ <br /> (primary)    | -     | -              | 0     |
| Regressions ❌ <br /> (secondary)  | -     | -              | 0     |
| Improvements ✅ <br /> (primary)   | -0.4% | [-0.4%, -0.4%] | 1     |
| Improvements ✅ <br /> (secondary) | -     | -              | 0     |
| All ❌✅ (primary)                 | -0.4% | [-0.4%, -0.4%] | 1     |
</details>

### [Binary size](https://perf.rust-lang.org/compare.html?start=c02a4f0852e6665cf3df3867982021383f5615df&end=7f695232a80fa1833e2282f2577c5e1ff066bf39&stat=size:linked_artifact)
This benchmark run did not return any relevant results for this metric.

**Bootstrap**: 774.94s -> 776.02s (0.14%)
**Artifact size**: 365.09 MiB -> 365.11 MiB (0.00%)
author_association: COLLABORATOR
user: login: panstromek
created_at: 2025-04-24T10:43:40Z
body: Looks like the regression is gone now, so nevermind then.
author_association: CONTRIBUTOR