about summary refs log tree commit diff
path: root/compiler/rustc_const_eval/src
diff options
context:
space:
mode:
authorJubilee Young <workingjubilee@gmail.com>2024-05-24 01:14:31 -0700
committerJubilee Young <workingjubilee@gmail.com>2024-05-24 09:56:56 -0700
commitdb6ec2618a2abb82efe9e119f301f4ae8ec07682 (patch)
treef15295db6c066103ed0925e0f9242545b30626d9 /compiler/rustc_const_eval/src
parent213ad10c8f0fc275648552366275dc4e07f97462 (diff)
downloadrust-db6ec2618a2abb82efe9e119f301f4ae8ec07682.tar.gz
rust-db6ec2618a2abb82efe9e119f301f4ae8ec07682.zip
compiler: const_eval/transform/validate.rs -> mir_transform/validate.rs
Diffstat (limited to 'compiler/rustc_const_eval/src')
-rw-r--r--compiler/rustc_const_eval/src/transform/mod.rs1
-rw-r--r--compiler/rustc_const_eval/src/transform/validate.rs1406
2 files changed, 0 insertions, 1407 deletions
diff --git a/compiler/rustc_const_eval/src/transform/mod.rs b/compiler/rustc_const_eval/src/transform/mod.rs
index e3582c7d317..b4516a412ef 100644
--- a/compiler/rustc_const_eval/src/transform/mod.rs
+++ b/compiler/rustc_const_eval/src/transform/mod.rs
@@ -1,2 +1 @@
 pub mod check_consts;
-pub mod validate;
diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs
deleted file mode 100644
index 66cc65de647..00000000000
--- a/compiler/rustc_const_eval/src/transform/validate.rs
+++ /dev/null
@@ -1,1406 +0,0 @@
-//! Validates the MIR to ensure that invariants are upheld.
-
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_index::bit_set::BitSet;
-use rustc_index::IndexVec;
-use rustc_infer::traits::Reveal;
-use rustc_middle::mir::coverage::CoverageKind;
-use rustc_middle::mir::interpret::Scalar;
-use rustc_middle::mir::visit::{NonUseContext, PlaceContext, Visitor};
-use rustc_middle::mir::*;
-use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt, TypeVisitableExt, Variance};
-use rustc_middle::{bug, span_bug};
-use rustc_target::abi::{Size, FIRST_VARIANT};
-use rustc_target::spec::abi::Abi;
-
-use crate::util::is_within_packed;
-
-use crate::util::relate_types;
-
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-enum EdgeKind {
-    Unwind,
-    Normal,
-}
-
-pub struct Validator {
-    /// Describes at which point in the pipeline this validation is happening.
-    pub when: String,
-    /// The phase for which we are upholding the dialect. If the given phase forbids a specific
-    /// element, this validator will now emit errors if that specific element is encountered.
-    /// Note that phases that change the dialect cause all *following* phases to check the
-    /// invariants of the new dialect. A phase that changes dialects never checks the new invariants
-    /// itself.
-    pub mir_phase: MirPhase,
-}
-
-impl<'tcx> MirPass<'tcx> for Validator {
-    fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
-        // FIXME(JakobDegen): These bodies never instantiated in codegend anyway, so it's not
-        // terribly important that they pass the validator. However, I think other passes might
-        // still see them, in which case they might be surprised. It would probably be better if we
-        // didn't put this through the MIR pipeline at all.
-        if matches!(body.source.instance, InstanceDef::Intrinsic(..) | InstanceDef::Virtual(..)) {
-            return;
-        }
-        let def_id = body.source.def_id();
-        let mir_phase = self.mir_phase;
-        let param_env = match mir_phase.reveal() {
-            Reveal::UserFacing => tcx.param_env(def_id),
-            Reveal::All => tcx.param_env_reveal_all_normalized(def_id),
-        };
-
-        let can_unwind = if mir_phase <= MirPhase::Runtime(RuntimePhase::Initial) {
-            // In this case `AbortUnwindingCalls` haven't yet been executed.
-            true
-        } else if !tcx.def_kind(def_id).is_fn_like() {
-            true
-        } else {
-            let body_ty = tcx.type_of(def_id).skip_binder();
-            let body_abi = match body_ty.kind() {
-                ty::FnDef(..) => body_ty.fn_sig(tcx).abi(),
-                ty::Closure(..) => Abi::RustCall,
-                ty::CoroutineClosure(..) => Abi::RustCall,
-                ty::Coroutine(..) => Abi::Rust,
-                // No need to do MIR validation on error bodies
-                ty::Error(_) => return,
-                _ => {
-                    span_bug!(body.span, "unexpected body ty: {:?} phase {:?}", body_ty, mir_phase)
-                }
-            };
-
-            ty::layout::fn_can_unwind(tcx, Some(def_id), body_abi)
-        };
-
-        let mut cfg_checker = CfgChecker {
-            when: &self.when,
-            body,
-            tcx,
-            mir_phase,
-            unwind_edge_count: 0,
-            reachable_blocks: traversal::reachable_as_bitset(body),
-            value_cache: FxHashSet::default(),
-            can_unwind,
-        };
-        cfg_checker.visit_body(body);
-        cfg_checker.check_cleanup_control_flow();
-
-        // Also run the TypeChecker.
-        for (location, msg) in validate_types(tcx, self.mir_phase, param_env, body, body) {
-            cfg_checker.fail(location, msg);
-        }
-
-        if let MirPhase::Runtime(_) = body.phase {
-            if let ty::InstanceDef::Item(_) = body.source.instance {
-                if body.has_free_regions() {
-                    cfg_checker.fail(
-                        Location::START,
-                        format!("Free regions in optimized {} MIR", body.phase.name()),
-                    );
-                }
-            }
-        }
-
-        // Enforce that coroutine-closure layouts are identical.
-        if let Some(layout) = body.coroutine_layout_raw()
-            && let Some(by_move_body) = body.coroutine_by_move_body()
-            && let Some(by_move_layout) = by_move_body.coroutine_layout_raw()
-        {
-            // FIXME(async_closures): We could do other validation here?
-            if layout.variant_fields.len() != by_move_layout.variant_fields.len() {
-                cfg_checker.fail(
-                    Location::START,
-                    format!(
-                        "Coroutine layout has different number of variant fields from \
-                        by-move coroutine layout:\n\
-                        layout: {layout:#?}\n\
-                        by_move_layout: {by_move_layout:#?}",
-                    ),
-                );
-            }
-        }
-    }
-}
-
-struct CfgChecker<'a, 'tcx> {
-    when: &'a str,
-    body: &'a Body<'tcx>,
-    tcx: TyCtxt<'tcx>,
-    mir_phase: MirPhase,
-    unwind_edge_count: usize,
-    reachable_blocks: BitSet<BasicBlock>,
-    value_cache: FxHashSet<u128>,
-    // If `false`, then the MIR must not contain `UnwindAction::Continue` or
-    // `TerminatorKind::Resume`.
-    can_unwind: bool,
-}
-
-impl<'a, 'tcx> CfgChecker<'a, 'tcx> {
-    #[track_caller]
-    fn fail(&self, location: Location, msg: impl AsRef<str>) {
-        // We might see broken MIR when other errors have already occurred.
-        assert!(
-            self.tcx.dcx().has_errors().is_some(),
-            "broken MIR in {:?} ({}) at {:?}:\n{}",
-            self.body.source.instance,
-            self.when,
-            location,
-            msg.as_ref(),
-        );
-    }
-
-    fn check_edge(&mut self, location: Location, bb: BasicBlock, edge_kind: EdgeKind) {
-        if bb == START_BLOCK {
-            self.fail(location, "start block must not have predecessors")
-        }
-        if let Some(bb) = self.body.basic_blocks.get(bb) {
-            let src = self.body.basic_blocks.get(location.block).unwrap();
-            match (src.is_cleanup, bb.is_cleanup, edge_kind) {
-                // Non-cleanup blocks can jump to non-cleanup blocks along non-unwind edges
-                (false, false, EdgeKind::Normal)
-                // Cleanup blocks can jump to cleanup blocks along non-unwind edges
-                | (true, true, EdgeKind::Normal) => {}
-                // Non-cleanup blocks can jump to cleanup blocks along unwind edges
-                (false, true, EdgeKind::Unwind) => {
-                    self.unwind_edge_count += 1;
-                }
-                // All other jumps are invalid
-                _ => {
-                    self.fail(
-                        location,
-                        format!(
-                            "{:?} edge to {:?} violates unwind invariants (cleanup {:?} -> {:?})",
-                            edge_kind,
-                            bb,
-                            src.is_cleanup,
-                            bb.is_cleanup,
-                        )
-                    )
-                }
-            }
-        } else {
-            self.fail(location, format!("encountered jump to invalid basic block {bb:?}"))
-        }
-    }
-
-    fn check_cleanup_control_flow(&self) {
-        if self.unwind_edge_count <= 1 {
-            return;
-        }
-        let doms = self.body.basic_blocks.dominators();
-        let mut post_contract_node = FxHashMap::default();
-        // Reusing the allocation across invocations of the closure
-        let mut dom_path = vec![];
-        let mut get_post_contract_node = |mut bb| {
-            let root = loop {
-                if let Some(root) = post_contract_node.get(&bb) {
-                    break *root;
-                }
-                let parent = doms.immediate_dominator(bb).unwrap();
-                dom_path.push(bb);
-                if !self.body.basic_blocks[parent].is_cleanup {
-                    break bb;
-                }
-                bb = parent;
-            };
-            for bb in dom_path.drain(..) {
-                post_contract_node.insert(bb, root);
-            }
-            root
-        };
-
-        let mut parent = IndexVec::from_elem(None, &self.body.basic_blocks);
-        for (bb, bb_data) in self.body.basic_blocks.iter_enumerated() {
-            if !bb_data.is_cleanup || !self.reachable_blocks.contains(bb) {
-                continue;
-            }
-            let bb = get_post_contract_node(bb);
-            for s in bb_data.terminator().successors() {
-                let s = get_post_contract_node(s);
-                if s == bb {
-                    continue;
-                }
-                let parent = &mut parent[bb];
-                match parent {
-                    None => {
-                        *parent = Some(s);
-                    }
-                    Some(e) if *e == s => (),
-                    Some(e) => self.fail(
-                        Location { block: bb, statement_index: 0 },
-                        format!(
-                            "Cleanup control flow violation: The blocks dominated by {:?} have edges to both {:?} and {:?}",
-                            bb,
-                            s,
-                            *e
-                        )
-                    ),
-                }
-            }
-        }
-
-        // Check for cycles
-        let mut stack = FxHashSet::default();
-        for i in 0..parent.len() {
-            let mut bb = BasicBlock::from_usize(i);
-            stack.clear();
-            stack.insert(bb);
-            loop {
-                let Some(parent) = parent[bb].take() else { break };
-                let no_cycle = stack.insert(parent);
-                if !no_cycle {
-                    self.fail(
-                        Location { block: bb, statement_index: 0 },
-                        format!(
-                            "Cleanup control flow violation: Cycle involving edge {bb:?} -> {parent:?}",
-                        ),
-                    );
-                    break;
-                }
-                bb = parent;
-            }
-        }
-    }
-
-    fn check_unwind_edge(&mut self, location: Location, unwind: UnwindAction) {
-        let is_cleanup = self.body.basic_blocks[location.block].is_cleanup;
-        match unwind {
-            UnwindAction::Cleanup(unwind) => {
-                if is_cleanup {
-                    self.fail(location, "`UnwindAction::Cleanup` in cleanup block");
-                }
-                self.check_edge(location, unwind, EdgeKind::Unwind);
-            }
-            UnwindAction::Continue => {
-                if is_cleanup {
-                    self.fail(location, "`UnwindAction::Continue` in cleanup block");
-                }
-
-                if !self.can_unwind {
-                    self.fail(location, "`UnwindAction::Continue` in no-unwind function");
-                }
-            }
-            UnwindAction::Terminate(UnwindTerminateReason::InCleanup) => {
-                if !is_cleanup {
-                    self.fail(
-                        location,
-                        "`UnwindAction::Terminate(InCleanup)` in a non-cleanup block",
-                    );
-                }
-            }
-            // These are allowed everywhere.
-            UnwindAction::Unreachable | UnwindAction::Terminate(UnwindTerminateReason::Abi) => (),
-        }
-    }
-
-    fn is_critical_call_edge(&self, target: Option<BasicBlock>, unwind: UnwindAction) -> bool {
-        let Some(target) = target else { return false };
-        matches!(unwind, UnwindAction::Cleanup(_) | UnwindAction::Terminate(_))
-            && self.body.basic_blocks.predecessors()[target].len() > 1
-    }
-}
-
-impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
-    fn visit_local(&mut self, local: Local, _context: PlaceContext, location: Location) {
-        if self.body.local_decls.get(local).is_none() {
-            self.fail(
-                location,
-                format!("local {local:?} has no corresponding declaration in `body.local_decls`"),
-            );
-        }
-    }
-
-    fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
-        match &statement.kind {
-            StatementKind::AscribeUserType(..) => {
-                if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
-                    self.fail(
-                        location,
-                        "`AscribeUserType` should have been removed after drop lowering phase",
-                    );
-                }
-            }
-            StatementKind::FakeRead(..) => {
-                if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
-                    self.fail(
-                        location,
-                        "`FakeRead` should have been removed after drop lowering phase",
-                    );
-                }
-            }
-            StatementKind::SetDiscriminant { .. } => {
-                if self.mir_phase < MirPhase::Runtime(RuntimePhase::Initial) {
-                    self.fail(location, "`SetDiscriminant`is not allowed until deaggregation");
-                }
-            }
-            StatementKind::Deinit(..) => {
-                if self.mir_phase < MirPhase::Runtime(RuntimePhase::Initial) {
-                    self.fail(location, "`Deinit`is not allowed until deaggregation");
-                }
-            }
-            StatementKind::Retag(kind, _) => {
-                // FIXME(JakobDegen) The validator should check that `self.mir_phase <
-                // DropsLowered`. However, this causes ICEs with generation of drop shims, which
-                // seem to fail to set their `MirPhase` correctly.
-                if matches!(kind, RetagKind::TwoPhase) {
-                    self.fail(location, format!("explicit `{kind:?}` is forbidden"));
-                }
-            }
-            StatementKind::Coverage(kind) => {
-                if self.mir_phase >= MirPhase::Analysis(AnalysisPhase::PostCleanup)
-                    && let CoverageKind::BlockMarker { .. } | CoverageKind::SpanMarker { .. } = kind
-                {
-                    self.fail(
-                        location,
-                        format!("{kind:?} should have been removed after analysis"),
-                    );
-                }
-            }
-            StatementKind::Assign(..)
-            | StatementKind::StorageLive(_)
-            | StatementKind::StorageDead(_)
-            | StatementKind::Intrinsic(_)
-            | StatementKind::ConstEvalCounter
-            | StatementKind::PlaceMention(..)
-            | StatementKind::Nop => {}
-        }
-
-        self.super_statement(statement, location);
-    }
-
-    fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
-        match &terminator.kind {
-            TerminatorKind::Goto { target } => {
-                self.check_edge(location, *target, EdgeKind::Normal);
-            }
-            TerminatorKind::SwitchInt { targets, discr: _ } => {
-                for (_, target) in targets.iter() {
-                    self.check_edge(location, target, EdgeKind::Normal);
-                }
-                self.check_edge(location, targets.otherwise(), EdgeKind::Normal);
-
-                self.value_cache.clear();
-                self.value_cache.extend(targets.iter().map(|(value, _)| value));
-                let has_duplicates = targets.iter().len() != self.value_cache.len();
-                if has_duplicates {
-                    self.fail(
-                        location,
-                        format!(
-                            "duplicated values in `SwitchInt` terminator: {:?}",
-                            terminator.kind,
-                        ),
-                    );
-                }
-            }
-            TerminatorKind::Drop { target, unwind, .. } => {
-                self.check_edge(location, *target, EdgeKind::Normal);
-                self.check_unwind_edge(location, *unwind);
-            }
-            TerminatorKind::Call { args, destination, target, unwind, .. } => {
-                if let Some(target) = target {
-                    self.check_edge(location, *target, EdgeKind::Normal);
-                }
-                self.check_unwind_edge(location, *unwind);
-
-                // The code generation assumes that there are no critical call edges. The assumption
-                // is used to simplify inserting code that should be executed along the return edge
-                // from the call. FIXME(tmiasko): Since this is a strictly code generation concern,
-                // the code generation should be responsible for handling it.
-                if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Optimized)
-                    && self.is_critical_call_edge(*target, *unwind)
-                {
-                    self.fail(
-                        location,
-                        format!(
-                            "encountered critical edge in `Call` terminator {:?}",
-                            terminator.kind,
-                        ),
-                    );
-                }
-
-                // The call destination place and Operand::Move place used as an argument might be
-                // passed by a reference to the callee. Consequently they cannot be packed.
-                if is_within_packed(self.tcx, &self.body.local_decls, *destination).is_some() {
-                    // This is bad! The callee will expect the memory to be aligned.
-                    self.fail(
-                        location,
-                        format!(
-                            "encountered packed place in `Call` terminator destination: {:?}",
-                            terminator.kind,
-                        ),
-                    );
-                }
-                for arg in args {
-                    if let Operand::Move(place) = &arg.node {
-                        if is_within_packed(self.tcx, &self.body.local_decls, *place).is_some() {
-                            // This is bad! The callee will expect the memory to be aligned.
-                            self.fail(
-                                location,
-                                format!(
-                                    "encountered `Move` of a packed place in `Call` terminator: {:?}",
-                                    terminator.kind,
-                                ),
-                            );
-                        }
-                    }
-                }
-            }
-            TerminatorKind::Assert { target, unwind, .. } => {
-                self.check_edge(location, *target, EdgeKind::Normal);
-                self.check_unwind_edge(location, *unwind);
-            }
-            TerminatorKind::Yield { resume, drop, .. } => {
-                if self.body.coroutine.is_none() {
-                    self.fail(location, "`Yield` cannot appear outside coroutine bodies");
-                }
-                if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
-                    self.fail(location, "`Yield` should have been replaced by coroutine lowering");
-                }
-                self.check_edge(location, *resume, EdgeKind::Normal);
-                if let Some(drop) = drop {
-                    self.check_edge(location, *drop, EdgeKind::Normal);
-                }
-            }
-            TerminatorKind::FalseEdge { real_target, imaginary_target } => {
-                if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
-                    self.fail(
-                        location,
-                        "`FalseEdge` should have been removed after drop elaboration",
-                    );
-                }
-                self.check_edge(location, *real_target, EdgeKind::Normal);
-                self.check_edge(location, *imaginary_target, EdgeKind::Normal);
-            }
-            TerminatorKind::FalseUnwind { real_target, unwind } => {
-                if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
-                    self.fail(
-                        location,
-                        "`FalseUnwind` should have been removed after drop elaboration",
-                    );
-                }
-                self.check_edge(location, *real_target, EdgeKind::Normal);
-                self.check_unwind_edge(location, *unwind);
-            }
-            TerminatorKind::InlineAsm { targets, unwind, .. } => {
-                for &target in targets {
-                    self.check_edge(location, target, EdgeKind::Normal);
-                }
-                self.check_unwind_edge(location, *unwind);
-            }
-            TerminatorKind::CoroutineDrop => {
-                if self.body.coroutine.is_none() {
-                    self.fail(location, "`CoroutineDrop` cannot appear outside coroutine bodies");
-                }
-                if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
-                    self.fail(
-                        location,
-                        "`CoroutineDrop` should have been replaced by coroutine lowering",
-                    );
-                }
-            }
-            TerminatorKind::UnwindResume => {
-                let bb = location.block;
-                if !self.body.basic_blocks[bb].is_cleanup {
-                    self.fail(location, "Cannot `UnwindResume` from non-cleanup basic block")
-                }
-                if !self.can_unwind {
-                    self.fail(location, "Cannot `UnwindResume` in a function that cannot unwind")
-                }
-            }
-            TerminatorKind::UnwindTerminate(_) => {
-                let bb = location.block;
-                if !self.body.basic_blocks[bb].is_cleanup {
-                    self.fail(location, "Cannot `UnwindTerminate` from non-cleanup basic block")
-                }
-            }
-            TerminatorKind::Return => {
-                let bb = location.block;
-                if self.body.basic_blocks[bb].is_cleanup {
-                    self.fail(location, "Cannot `Return` from cleanup basic block")
-                }
-            }
-            TerminatorKind::Unreachable => {}
-        }
-
-        self.super_terminator(terminator, location);
-    }
-
-    fn visit_source_scope(&mut self, scope: SourceScope) {
-        if self.body.source_scopes.get(scope).is_none() {
-            self.tcx.dcx().span_bug(
-                self.body.span,
-                format!(
-                    "broken MIR in {:?} ({}):\ninvalid source scope {:?}",
-                    self.body.source.instance, self.when, scope,
-                ),
-            );
-        }
-    }
-}
-
-/// A faster version of the validation pass that only checks those things which may break when
-/// instantiating any generic parameters.
-///
-/// `caller_body` is used to detect cycles in MIR inlining and MIR validation before
-/// `optimized_mir` is available.
-pub fn validate_types<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    mir_phase: MirPhase,
-    param_env: ty::ParamEnv<'tcx>,
-    body: &Body<'tcx>,
-    caller_body: &Body<'tcx>,
-) -> Vec<(Location, String)> {
-    let mut type_checker =
-        TypeChecker { body, caller_body, tcx, param_env, mir_phase, failures: Vec::new() };
-    type_checker.visit_body(body);
-    type_checker.failures
-}
-
-struct TypeChecker<'a, 'tcx> {
-    body: &'a Body<'tcx>,
-    caller_body: &'a Body<'tcx>,
-    tcx: TyCtxt<'tcx>,
-    param_env: ParamEnv<'tcx>,
-    mir_phase: MirPhase,
-    failures: Vec<(Location, String)>,
-}
-
-impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
-    fn fail(&mut self, location: Location, msg: impl Into<String>) {
-        self.failures.push((location, msg.into()));
-    }
-
-    /// Check if src can be assigned into dest.
-    /// This is not precise, it will accept some incorrect assignments.
-    fn mir_assign_valid_types(&self, src: Ty<'tcx>, dest: Ty<'tcx>) -> bool {
-        // Fast path before we normalize.
-        if src == dest {
-            // Equal types, all is good.
-            return true;
-        }
-
-        // We sometimes have to use `defining_opaque_types` for subtyping
-        // to succeed here and figuring out how exactly that should work
-        // is annoying. It is harmless enough to just not validate anything
-        // in that case. We still check this after analysis as all opaque
-        // types have been revealed at this point.
-        if (src, dest).has_opaque_types() {
-            return true;
-        }
-
-        // After borrowck subtyping should be fully explicit via
-        // `Subtype` projections.
-        let variance = if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
-            Variance::Invariant
-        } else {
-            Variance::Covariant
-        };
-
-        crate::util::relate_types(self.tcx, self.param_env, variance, src, dest)
-    }
-}
-
-impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
-    fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
-        // This check is somewhat expensive, so only run it when -Zvalidate-mir is passed.
-        if self.tcx.sess.opts.unstable_opts.validate_mir
-            && self.mir_phase < MirPhase::Runtime(RuntimePhase::Initial)
-        {
-            // `Operand::Copy` is only supposed to be used with `Copy` types.
-            if let Operand::Copy(place) = operand {
-                let ty = place.ty(&self.body.local_decls, self.tcx).ty;
-
-                if !ty.is_copy_modulo_regions(self.tcx, self.param_env) {
-                    self.fail(location, format!("`Operand::Copy` with non-`Copy` type {ty}"));
-                }
-            }
-        }
-
-        self.super_operand(operand, location);
-    }
-
-    fn visit_projection_elem(
-        &mut self,
-        place_ref: PlaceRef<'tcx>,
-        elem: PlaceElem<'tcx>,
-        context: PlaceContext,
-        location: Location,
-    ) {
-        match elem {
-            ProjectionElem::OpaqueCast(ty)
-                if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) =>
-            {
-                self.fail(
-                    location,
-                    format!("explicit opaque type cast to `{ty}` after `RevealAll`"),
-                )
-            }
-            ProjectionElem::Index(index) => {
-                let index_ty = self.body.local_decls[index].ty;
-                if index_ty != self.tcx.types.usize {
-                    self.fail(location, format!("bad index ({index_ty:?} != usize)"))
-                }
-            }
-            ProjectionElem::Deref
-                if self.mir_phase >= MirPhase::Runtime(RuntimePhase::PostCleanup) =>
-            {
-                let base_ty = place_ref.ty(&self.body.local_decls, self.tcx).ty;
-
-                if base_ty.is_box() {
-                    self.fail(
-                        location,
-                        format!("{base_ty:?} dereferenced after ElaborateBoxDerefs"),
-                    )
-                }
-            }
-            ProjectionElem::Field(f, ty) => {
-                let parent_ty = place_ref.ty(&self.body.local_decls, self.tcx);
-                let fail_out_of_bounds = |this: &mut Self, location| {
-                    this.fail(location, format!("Out of bounds field {f:?} for {parent_ty:?}"));
-                };
-                let check_equal = |this: &mut Self, location, f_ty| {
-                    if !this.mir_assign_valid_types(ty, f_ty) {
-                        this.fail(
-                            location,
-                            format!(
-                                "Field projection `{place_ref:?}.{f:?}` specified type `{ty:?}`, but actual type is `{f_ty:?}`"
-                            )
-                        )
-                    }
-                };
-
-                let kind = match parent_ty.ty.kind() {
-                    &ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
-                        self.tcx.type_of(def_id).instantiate(self.tcx, args).kind()
-                    }
-                    kind => kind,
-                };
-
-                match kind {
-                    ty::Tuple(fields) => {
-                        let Some(f_ty) = fields.get(f.as_usize()) else {
-                            fail_out_of_bounds(self, location);
-                            return;
-                        };
-                        check_equal(self, location, *f_ty);
-                    }
-                    ty::Adt(adt_def, args) => {
-                        // see <https://github.com/rust-lang/rust/blob/7601adcc764d42c9f2984082b49948af652df986/compiler/rustc_middle/src/ty/layout.rs#L861-L864>
-                        if Some(adt_def.did()) == self.tcx.lang_items().dyn_metadata() {
-                            self.fail(
-                                location,
-                                format!("You can't project to field {f:?} of `DynMetadata` because \
-                                         layout is weird and thinks it doesn't have fields."),
-                            );
-                        }
-
-                        let var = parent_ty.variant_index.unwrap_or(FIRST_VARIANT);
-                        let Some(field) = adt_def.variant(var).fields.get(f) else {
-                            fail_out_of_bounds(self, location);
-                            return;
-                        };
-                        check_equal(self, location, field.ty(self.tcx, args));
-                    }
-                    ty::Closure(_, args) => {
-                        let args = args.as_closure();
-                        let Some(&f_ty) = args.upvar_tys().get(f.as_usize()) else {
-                            fail_out_of_bounds(self, location);
-                            return;
-                        };
-                        check_equal(self, location, f_ty);
-                    }
-                    ty::CoroutineClosure(_, args) => {
-                        let args = args.as_coroutine_closure();
-                        let Some(&f_ty) = args.upvar_tys().get(f.as_usize()) else {
-                            fail_out_of_bounds(self, location);
-                            return;
-                        };
-                        check_equal(self, location, f_ty);
-                    }
-                    &ty::Coroutine(def_id, args) => {
-                        let f_ty = if let Some(var) = parent_ty.variant_index {
-                            // If we're currently validating an inlined copy of this body,
-                            // then it will no longer be parameterized over the original
-                            // args of the coroutine. Otherwise, we prefer to use this body
-                            // since we may be in the process of computing this MIR in the
-                            // first place.
-                            let layout = if def_id == self.caller_body.source.def_id() {
-                                // FIXME: This is not right for async closures.
-                                self.caller_body.coroutine_layout_raw()
-                            } else {
-                                self.tcx.coroutine_layout(def_id, args.as_coroutine().kind_ty())
-                            };
-
-                            let Some(layout) = layout else {
-                                self.fail(
-                                    location,
-                                    format!("No coroutine layout for {parent_ty:?}"),
-                                );
-                                return;
-                            };
-
-                            let Some(&local) = layout.variant_fields[var].get(f) else {
-                                fail_out_of_bounds(self, location);
-                                return;
-                            };
-
-                            let Some(f_ty) = layout.field_tys.get(local) else {
-                                self.fail(
-                                    location,
-                                    format!("Out of bounds local {local:?} for {parent_ty:?}"),
-                                );
-                                return;
-                            };
-
-                            ty::EarlyBinder::bind(f_ty.ty).instantiate(self.tcx, args)
-                        } else {
-                            let Some(&f_ty) = args.as_coroutine().prefix_tys().get(f.index())
-                            else {
-                                fail_out_of_bounds(self, location);
-                                return;
-                            };
-
-                            f_ty
-                        };
-
-                        check_equal(self, location, f_ty);
-                    }
-                    _ => {
-                        self.fail(location, format!("{:?} does not have fields", parent_ty.ty));
-                    }
-                }
-            }
-            ProjectionElem::Subtype(ty) => {
-                if !relate_types(
-                    self.tcx,
-                    self.param_env,
-                    Variance::Covariant,
-                    ty,
-                    place_ref.ty(&self.body.local_decls, self.tcx).ty,
-                ) {
-                    self.fail(
-                        location,
-                        format!(
-                            "Failed subtyping {ty:#?} and {:#?}",
-                            place_ref.ty(&self.body.local_decls, self.tcx).ty
-                        ),
-                    )
-                }
-            }
-            _ => {}
-        }
-        self.super_projection_elem(place_ref, elem, context, location);
-    }
-
-    fn visit_var_debug_info(&mut self, debuginfo: &VarDebugInfo<'tcx>) {
-        if let Some(box VarDebugInfoFragment { ty, ref projection }) = debuginfo.composite {
-            if ty.is_union() || ty.is_enum() {
-                self.fail(
-                    START_BLOCK.start_location(),
-                    format!("invalid type {ty:?} in debuginfo for {:?}", debuginfo.name),
-                );
-            }
-            if projection.is_empty() {
-                self.fail(
-                    START_BLOCK.start_location(),
-                    format!("invalid empty projection in debuginfo for {:?}", debuginfo.name),
-                );
-            }
-            if projection.iter().any(|p| !matches!(p, PlaceElem::Field(..))) {
-                self.fail(
-                    START_BLOCK.start_location(),
-                    format!(
-                        "illegal projection {:?} in debuginfo for {:?}",
-                        projection, debuginfo.name
-                    ),
-                );
-            }
-        }
-        match debuginfo.value {
-            VarDebugInfoContents::Const(_) => {}
-            VarDebugInfoContents::Place(place) => {
-                if place.projection.iter().any(|p| !p.can_use_in_debuginfo()) {
-                    self.fail(
-                        START_BLOCK.start_location(),
-                        format!("illegal place {:?} in debuginfo for {:?}", place, debuginfo.name),
-                    );
-                }
-            }
-        }
-        self.super_var_debug_info(debuginfo);
-    }
-
-    fn visit_place(&mut self, place: &Place<'tcx>, cntxt: PlaceContext, location: Location) {
-        // Set off any `bug!`s in the type computation code
-        let _ = place.ty(&self.body.local_decls, self.tcx);
-
-        if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial)
-            && place.projection.len() > 1
-            && cntxt != PlaceContext::NonUse(NonUseContext::VarDebugInfo)
-            && place.projection[1..].contains(&ProjectionElem::Deref)
-        {
-            self.fail(location, format!("{place:?}, has deref at the wrong place"));
-        }
-
-        self.super_place(place, cntxt, location);
-    }
-
-    fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
-        macro_rules! check_kinds {
-            ($t:expr, $text:literal, $typat:pat) => {
-                if !matches!(($t).kind(), $typat) {
-                    self.fail(location, format!($text, $t));
-                }
-            };
-        }
-        match rvalue {
-            Rvalue::Use(_) | Rvalue::CopyForDeref(_) => {}
-            Rvalue::Aggregate(kind, fields) => match **kind {
-                AggregateKind::Tuple => {}
-                AggregateKind::Array(dest) => {
-                    for src in fields {
-                        if !self.mir_assign_valid_types(src.ty(self.body, self.tcx), dest) {
-                            self.fail(location, "array field has the wrong type");
-                        }
-                    }
-                }
-                AggregateKind::Adt(def_id, idx, args, _, Some(field)) => {
-                    let adt_def = self.tcx.adt_def(def_id);
-                    assert!(adt_def.is_union());
-                    assert_eq!(idx, FIRST_VARIANT);
-                    let dest_ty = self.tcx.normalize_erasing_regions(
-                        self.param_env,
-                        adt_def.non_enum_variant().fields[field].ty(self.tcx, args),
-                    );
-                    if fields.len() == 1 {
-                        let src_ty = fields.raw[0].ty(self.body, self.tcx);
-                        if !self.mir_assign_valid_types(src_ty, dest_ty) {
-                            self.fail(location, "union field has the wrong type");
-                        }
-                    } else {
-                        self.fail(location, "unions should have one initialized field");
-                    }
-                }
-                AggregateKind::Adt(def_id, idx, args, _, None) => {
-                    let adt_def = self.tcx.adt_def(def_id);
-                    assert!(!adt_def.is_union());
-                    let variant = &adt_def.variants()[idx];
-                    if variant.fields.len() != fields.len() {
-                        self.fail(location, "adt has the wrong number of initialized fields");
-                    }
-                    for (src, dest) in std::iter::zip(fields, &variant.fields) {
-                        let dest_ty = self
-                            .tcx
-                            .normalize_erasing_regions(self.param_env, dest.ty(self.tcx, args));
-                        if !self.mir_assign_valid_types(src.ty(self.body, self.tcx), dest_ty) {
-                            self.fail(location, "adt field has the wrong type");
-                        }
-                    }
-                }
-                AggregateKind::Closure(_, args) => {
-                    let upvars = args.as_closure().upvar_tys();
-                    if upvars.len() != fields.len() {
-                        self.fail(location, "closure has the wrong number of initialized fields");
-                    }
-                    for (src, dest) in std::iter::zip(fields, upvars) {
-                        if !self.mir_assign_valid_types(src.ty(self.body, self.tcx), dest) {
-                            self.fail(location, "closure field has the wrong type");
-                        }
-                    }
-                }
-                AggregateKind::Coroutine(_, args) => {
-                    let upvars = args.as_coroutine().upvar_tys();
-                    if upvars.len() != fields.len() {
-                        self.fail(location, "coroutine has the wrong number of initialized fields");
-                    }
-                    for (src, dest) in std::iter::zip(fields, upvars) {
-                        if !self.mir_assign_valid_types(src.ty(self.body, self.tcx), dest) {
-                            self.fail(location, "coroutine field has the wrong type");
-                        }
-                    }
-                }
-                AggregateKind::CoroutineClosure(_, args) => {
-                    let upvars = args.as_coroutine_closure().upvar_tys();
-                    if upvars.len() != fields.len() {
-                        self.fail(
-                            location,
-                            "coroutine-closure has the wrong number of initialized fields",
-                        );
-                    }
-                    for (src, dest) in std::iter::zip(fields, upvars) {
-                        if !self.mir_assign_valid_types(src.ty(self.body, self.tcx), dest) {
-                            self.fail(location, "coroutine-closure field has the wrong type");
-                        }
-                    }
-                }
-                AggregateKind::RawPtr(pointee_ty, mutability) => {
-                    if !matches!(self.mir_phase, MirPhase::Runtime(_)) {
-                        // It would probably be fine to support this in earlier phases,
-                        // but at the time of writing it's only ever introduced from intrinsic lowering,
-                        // so earlier things just `bug!` on it.
-                        self.fail(location, "RawPtr should be in runtime MIR only");
-                    }
-
-                    if fields.len() != 2 {
-                        self.fail(location, "raw pointer aggregate must have 2 fields");
-                    } else {
-                        let data_ptr_ty = fields.raw[0].ty(self.body, self.tcx);
-                        let metadata_ty = fields.raw[1].ty(self.body, self.tcx);
-                        if let ty::RawPtr(in_pointee, in_mut) = data_ptr_ty.kind() {
-                            if *in_mut != mutability {
-                                self.fail(location, "input and output mutability must match");
-                            }
-
-                            // FIXME: check `Thin` instead of `Sized`
-                            if !in_pointee.is_sized(self.tcx, self.param_env) {
-                                self.fail(location, "input pointer must be thin");
-                            }
-                        } else {
-                            self.fail(
-                                location,
-                                "first operand to raw pointer aggregate must be a raw pointer",
-                            );
-                        }
-
-                        // FIXME: Check metadata more generally
-                        if pointee_ty.is_slice() {
-                            if !self.mir_assign_valid_types(metadata_ty, self.tcx.types.usize) {
-                                self.fail(location, "slice metadata must be usize");
-                            }
-                        } else if pointee_ty.is_sized(self.tcx, self.param_env) {
-                            if metadata_ty != self.tcx.types.unit {
-                                self.fail(location, "metadata for pointer-to-thin must be unit");
-                            }
-                        }
-                    }
-                }
-            },
-            Rvalue::Ref(_, BorrowKind::Fake(_), _) => {
-                if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
-                    self.fail(
-                        location,
-                        "`Assign` statement with a `Fake` borrow should have been removed in runtime MIR",
-                    );
-                }
-            }
-            Rvalue::Ref(..) => {}
-            Rvalue::Len(p) => {
-                let pty = p.ty(&self.body.local_decls, self.tcx).ty;
-                check_kinds!(
-                    pty,
-                    "Cannot compute length of non-array type {:?}",
-                    ty::Array(..) | ty::Slice(..)
-                );
-            }
-            Rvalue::BinaryOp(op, vals) => {
-                use BinOp::*;
-                let a = vals.0.ty(&self.body.local_decls, self.tcx);
-                let b = vals.1.ty(&self.body.local_decls, self.tcx);
-                if crate::util::binop_right_homogeneous(*op) {
-                    if let Eq | Lt | Le | Ne | Ge | Gt = op {
-                        // The function pointer types can have lifetimes
-                        if !self.mir_assign_valid_types(a, b) {
-                            self.fail(
-                                location,
-                                format!("Cannot {op:?} compare incompatible types {a:?} and {b:?}"),
-                            );
-                        }
-                    } else if a != b {
-                        self.fail(
-                            location,
-                            format!(
-                                "Cannot perform binary op {op:?} on unequal types {a:?} and {b:?}"
-                            ),
-                        );
-                    }
-                }
-
-                match op {
-                    Offset => {
-                        check_kinds!(a, "Cannot offset non-pointer type {:?}", ty::RawPtr(..));
-                        if b != self.tcx.types.isize && b != self.tcx.types.usize {
-                            self.fail(location, format!("Cannot offset by non-isize type {b:?}"));
-                        }
-                    }
-                    Eq | Lt | Le | Ne | Ge | Gt => {
-                        for x in [a, b] {
-                            check_kinds!(
-                                x,
-                                "Cannot {op:?} compare type {:?}",
-                                ty::Bool
-                                    | ty::Char
-                                    | ty::Int(..)
-                                    | ty::Uint(..)
-                                    | ty::Float(..)
-                                    | ty::RawPtr(..)
-                                    | ty::FnPtr(..)
-                            )
-                        }
-                    }
-                    Cmp => {
-                        for x in [a, b] {
-                            check_kinds!(
-                                x,
-                                "Cannot three-way compare non-integer type {:?}",
-                                ty::Char | ty::Uint(..) | ty::Int(..)
-                            )
-                        }
-                    }
-                    AddUnchecked | AddWithOverflow | SubUnchecked | SubWithOverflow
-                    | MulUnchecked | MulWithOverflow | Shl | ShlUnchecked | Shr | ShrUnchecked => {
-                        for x in [a, b] {
-                            check_kinds!(
-                                x,
-                                "Cannot {op:?} non-integer type {:?}",
-                                ty::Uint(..) | ty::Int(..)
-                            )
-                        }
-                    }
-                    BitAnd | BitOr | BitXor => {
-                        for x in [a, b] {
-                            check_kinds!(
-                                x,
-                                "Cannot perform bitwise op {op:?} on type {:?}",
-                                ty::Uint(..) | ty::Int(..) | ty::Bool
-                            )
-                        }
-                    }
-                    Add | Sub | Mul | Div | Rem => {
-                        for x in [a, b] {
-                            check_kinds!(
-                                x,
-                                "Cannot perform arithmetic {op:?} on type {:?}",
-                                ty::Uint(..) | ty::Int(..) | ty::Float(..)
-                            )
-                        }
-                    }
-                }
-            }
-            Rvalue::UnaryOp(op, operand) => {
-                let a = operand.ty(&self.body.local_decls, self.tcx);
-                match op {
-                    UnOp::Neg => {
-                        check_kinds!(a, "Cannot negate type {:?}", ty::Int(..) | ty::Float(..))
-                    }
-                    UnOp::Not => {
-                        check_kinds!(
-                            a,
-                            "Cannot binary not type {:?}",
-                            ty::Int(..) | ty::Uint(..) | ty::Bool
-                        );
-                    }
-                }
-            }
-            Rvalue::ShallowInitBox(operand, _) => {
-                let a = operand.ty(&self.body.local_decls, self.tcx);
-                check_kinds!(a, "Cannot shallow init type {:?}", ty::RawPtr(..));
-            }
-            Rvalue::Cast(kind, operand, target_type) => {
-                let op_ty = operand.ty(self.body, self.tcx);
-                match kind {
-                    CastKind::DynStar => {
-                        // FIXME(dyn-star): make sure nothing needs to be done here.
-                    }
-                    // FIXME: Add Checks for these
-                    CastKind::PointerWithExposedProvenance
-                    | CastKind::PointerExposeProvenance
-                    | CastKind::PointerCoercion(_) => {}
-                    CastKind::IntToInt | CastKind::IntToFloat => {
-                        let input_valid = op_ty.is_integral() || op_ty.is_char() || op_ty.is_bool();
-                        let target_valid = target_type.is_numeric() || target_type.is_char();
-                        if !input_valid || !target_valid {
-                            self.fail(
-                                location,
-                                format!("Wrong cast kind {kind:?} for the type {op_ty}",),
-                            );
-                        }
-                    }
-                    CastKind::FnPtrToPtr | CastKind::PtrToPtr => {
-                        if !(op_ty.is_any_ptr() && target_type.is_unsafe_ptr()) {
-                            self.fail(location, "Can't cast {op_ty} into 'Ptr'");
-                        }
-                    }
-                    CastKind::FloatToFloat | CastKind::FloatToInt => {
-                        if !op_ty.is_floating_point() || !target_type.is_numeric() {
-                            self.fail(
-                                location,
-                                format!(
-                                    "Trying to cast non 'Float' as {kind:?} into {target_type:?}"
-                                ),
-                            );
-                        }
-                    }
-                    CastKind::Transmute => {
-                        if let MirPhase::Runtime(..) = self.mir_phase {
-                            // Unlike `mem::transmute`, a MIR `Transmute` is well-formed
-                            // for any two `Sized` types, just potentially UB to run.
-
-                            if !self
-                                .tcx
-                                .normalize_erasing_regions(self.param_env, op_ty)
-                                .is_sized(self.tcx, self.param_env)
-                            {
-                                self.fail(
-                                    location,
-                                    format!("Cannot transmute from non-`Sized` type {op_ty:?}"),
-                                );
-                            }
-                            if !self
-                                .tcx
-                                .normalize_erasing_regions(self.param_env, *target_type)
-                                .is_sized(self.tcx, self.param_env)
-                            {
-                                self.fail(
-                                    location,
-                                    format!("Cannot transmute to non-`Sized` type {target_type:?}"),
-                                );
-                            }
-                        } else {
-                            self.fail(
-                                location,
-                                format!(
-                                    "Transmute is not supported in non-runtime phase {:?}.",
-                                    self.mir_phase
-                                ),
-                            );
-                        }
-                    }
-                }
-            }
-            Rvalue::NullaryOp(NullOp::OffsetOf(indices), container) => {
-                let fail_out_of_bounds = |this: &mut Self, location, field, ty| {
-                    this.fail(location, format!("Out of bounds field {field:?} for {ty:?}"));
-                };
-
-                let mut current_ty = *container;
-
-                for (variant, field) in indices.iter() {
-                    match current_ty.kind() {
-                        ty::Tuple(fields) => {
-                            if variant != FIRST_VARIANT {
-                                self.fail(
-                                    location,
-                                    format!("tried to get variant {variant:?} of tuple"),
-                                );
-                                return;
-                            }
-                            let Some(&f_ty) = fields.get(field.as_usize()) else {
-                                fail_out_of_bounds(self, location, field, current_ty);
-                                return;
-                            };
-
-                            current_ty = self.tcx.normalize_erasing_regions(self.param_env, f_ty);
-                        }
-                        ty::Adt(adt_def, args) => {
-                            let Some(field) = adt_def.variant(variant).fields.get(field) else {
-                                fail_out_of_bounds(self, location, field, current_ty);
-                                return;
-                            };
-
-                            let f_ty = field.ty(self.tcx, args);
-                            current_ty = self.tcx.normalize_erasing_regions(self.param_env, f_ty);
-                        }
-                        _ => {
-                            self.fail(
-                                location,
-                                format!("Cannot get offset ({variant:?}, {field:?}) from type {current_ty:?}"),
-                            );
-                            return;
-                        }
-                    }
-                }
-            }
-            Rvalue::Repeat(_, _)
-            | Rvalue::ThreadLocalRef(_)
-            | Rvalue::AddressOf(_, _)
-            | Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::UbChecks, _)
-            | Rvalue::Discriminant(_) => {}
-        }
-        self.super_rvalue(rvalue, location);
-    }
-
-    fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
-        match &statement.kind {
-            StatementKind::Assign(box (dest, rvalue)) => {
-                // LHS and RHS of the assignment must have the same type.
-                let left_ty = dest.ty(&self.body.local_decls, self.tcx).ty;
-                let right_ty = rvalue.ty(&self.body.local_decls, self.tcx);
-
-                if !self.mir_assign_valid_types(right_ty, left_ty) {
-                    self.fail(
-                        location,
-                        format!(
-                            "encountered `{:?}` with incompatible types:\n\
-                            left-hand side has type: {}\n\
-                            right-hand side has type: {}",
-                            statement.kind, left_ty, right_ty,
-                        ),
-                    );
-                }
-                if let Rvalue::CopyForDeref(place) = rvalue {
-                    if place.ty(&self.body.local_decls, self.tcx).ty.builtin_deref(true).is_none() {
-                        self.fail(
-                            location,
-                            "`CopyForDeref` should only be used for dereferenceable types",
-                        )
-                    }
-                }
-            }
-            StatementKind::AscribeUserType(..) => {
-                if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
-                    self.fail(
-                        location,
-                        "`AscribeUserType` should have been removed after drop lowering phase",
-                    );
-                }
-            }
-            StatementKind::FakeRead(..) => {
-                if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
-                    self.fail(
-                        location,
-                        "`FakeRead` should have been removed after drop lowering phase",
-                    );
-                }
-            }
-            StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(op)) => {
-                let ty = op.ty(&self.body.local_decls, self.tcx);
-                if !ty.is_bool() {
-                    self.fail(
-                        location,
-                        format!("`assume` argument must be `bool`, but got: `{ty}`"),
-                    );
-                }
-            }
-            StatementKind::Intrinsic(box NonDivergingIntrinsic::CopyNonOverlapping(
-                CopyNonOverlapping { src, dst, count },
-            )) => {
-                let src_ty = src.ty(&self.body.local_decls, self.tcx);
-                let op_src_ty = if let Some(src_deref) = src_ty.builtin_deref(true) {
-                    src_deref
-                } else {
-                    self.fail(
-                        location,
-                        format!("Expected src to be ptr in copy_nonoverlapping, got: {src_ty}"),
-                    );
-                    return;
-                };
-                let dst_ty = dst.ty(&self.body.local_decls, self.tcx);
-                let op_dst_ty = if let Some(dst_deref) = dst_ty.builtin_deref(true) {
-                    dst_deref
-                } else {
-                    self.fail(
-                        location,
-                        format!("Expected dst to be ptr in copy_nonoverlapping, got: {dst_ty}"),
-                    );
-                    return;
-                };
-                // since CopyNonOverlapping is parametrized by 1 type,
-                // we only need to check that they are equal and not keep an extra parameter.
-                if !self.mir_assign_valid_types(op_src_ty, op_dst_ty) {
-                    self.fail(location, format!("bad arg ({op_src_ty:?} != {op_dst_ty:?})"));
-                }
-
-                let op_cnt_ty = count.ty(&self.body.local_decls, self.tcx);
-                if op_cnt_ty != self.tcx.types.usize {
-                    self.fail(location, format!("bad arg ({op_cnt_ty:?} != usize)"))
-                }
-            }
-            StatementKind::SetDiscriminant { place, .. } => {
-                if self.mir_phase < MirPhase::Runtime(RuntimePhase::Initial) {
-                    self.fail(location, "`SetDiscriminant`is not allowed until deaggregation");
-                }
-                let pty = place.ty(&self.body.local_decls, self.tcx).ty.kind();
-                if !matches!(pty, ty::Adt(..) | ty::Coroutine(..) | ty::Alias(ty::Opaque, ..)) {
-                    self.fail(
-                        location,
-                        format!(
-                            "`SetDiscriminant` is only allowed on ADTs and coroutines, not {pty:?}"
-                        ),
-                    );
-                }
-            }
-            StatementKind::Deinit(..) => {
-                if self.mir_phase < MirPhase::Runtime(RuntimePhase::Initial) {
-                    self.fail(location, "`Deinit`is not allowed until deaggregation");
-                }
-            }
-            StatementKind::Retag(kind, _) => {
-                // FIXME(JakobDegen) The validator should check that `self.mir_phase <
-                // DropsLowered`. However, this causes ICEs with generation of drop shims, which
-                // seem to fail to set their `MirPhase` correctly.
-                if matches!(kind, RetagKind::TwoPhase) {
-                    self.fail(location, format!("explicit `{kind:?}` is forbidden"));
-                }
-            }
-            StatementKind::StorageLive(_)
-            | StatementKind::StorageDead(_)
-            | StatementKind::Coverage(_)
-            | StatementKind::ConstEvalCounter
-            | StatementKind::PlaceMention(..)
-            | StatementKind::Nop => {}
-        }
-
-        self.super_statement(statement, location);
-    }
-
-    fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
-        match &terminator.kind {
-            TerminatorKind::SwitchInt { targets, discr } => {
-                let switch_ty = discr.ty(&self.body.local_decls, self.tcx);
-
-                let target_width = self.tcx.sess.target.pointer_width;
-
-                let size = Size::from_bits(match switch_ty.kind() {
-                    ty::Uint(uint) => uint.normalize(target_width).bit_width().unwrap(),
-                    ty::Int(int) => int.normalize(target_width).bit_width().unwrap(),
-                    ty::Char => 32,
-                    ty::Bool => 1,
-                    other => bug!("unhandled type: {:?}", other),
-                });
-
-                for (value, _) in targets.iter() {
-                    if Scalar::<()>::try_from_uint(value, size).is_none() {
-                        self.fail(
-                            location,
-                            format!("the value {value:#x} is not a proper {switch_ty:?}"),
-                        )
-                    }
-                }
-            }
-            TerminatorKind::Call { func, .. } => {
-                let func_ty = func.ty(&self.body.local_decls, self.tcx);
-                match func_ty.kind() {
-                    ty::FnPtr(..) | ty::FnDef(..) => {}
-                    _ => self.fail(
-                        location,
-                        format!("encountered non-callable type {func_ty} in `Call` terminator"),
-                    ),
-                }
-            }
-            TerminatorKind::Assert { cond, .. } => {
-                let cond_ty = cond.ty(&self.body.local_decls, self.tcx);
-                if cond_ty != self.tcx.types.bool {
-                    self.fail(
-                        location,
-                        format!(
-                            "encountered non-boolean condition of type {cond_ty} in `Assert` terminator"
-                        ),
-                    );
-                }
-            }
-            TerminatorKind::Goto { .. }
-            | TerminatorKind::Drop { .. }
-            | TerminatorKind::Yield { .. }
-            | TerminatorKind::FalseEdge { .. }
-            | TerminatorKind::FalseUnwind { .. }
-            | TerminatorKind::InlineAsm { .. }
-            | TerminatorKind::CoroutineDrop
-            | TerminatorKind::UnwindResume
-            | TerminatorKind::UnwindTerminate(_)
-            | TerminatorKind::Return
-            | TerminatorKind::Unreachable => {}
-        }
-
-        self.super_terminator(terminator, location);
-    }
-}