about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJason Newcomb <jsnewcomb@pm.me>2024-02-24 17:22:28 -0500
committerJason Newcomb <jsnewcomb@pm.me>2024-03-05 13:28:15 -0500
commitbe9b125d4180126f259d392fdf72ddb33e63013a (patch)
treea8766be6e1360cde6b9bfca49898116ab8c200d6
parent5abfb3775da61ed9059c6efa3f9bec5b86b67c7a (diff)
downloadrust-be9b125d4180126f259d392fdf72ddb33e63013a.tar.gz
rust-be9b125d4180126f259d392fdf72ddb33e63013a.zip
Convert `TypeVisitor` and `DefIdVisitor` to use `VisitorResult`
-rw-r--r--Cargo.lock4
-rw-r--r--compiler/rustc_const_eval/src/interpret/util.rs6
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/check.rs6
-rw-r--r--compiler/rustc_hir_analysis/src/check/check.rs5
-rw-r--r--compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs10
-rw-r--r--compiler/rustc_hir_analysis/src/check/wfcheck.rs13
-rw-r--r--compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs13
-rw-r--r--compiler/rustc_hir_analysis/src/constrained_generic_params.rs14
-rw-r--r--compiler/rustc_hir_analysis/src/variance/mod.rs13
-rw-r--r--compiler/rustc_hir_typeck/Cargo.toml1
-rw-r--r--compiler/rustc_hir_typeck/src/closure.rs4
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs4
-rw-r--r--compiler/rustc_infer/Cargo.toml1
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/mod.rs4
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs4
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs5
-rw-r--r--compiler/rustc_infer/src/infer/opaque_types.rs22
-rw-r--r--compiler/rustc_infer/src/infer/outlives/for_liveness.rs31
-rw-r--r--compiler/rustc_infer/src/traits/structural_impls.rs6
-rw-r--r--compiler/rustc_lint/src/types.rs8
-rw-r--r--compiler/rustc_macros/src/type_visitable.rs13
-rw-r--r--compiler/rustc_middle/src/macros.rs4
-rw-r--r--compiler/rustc_middle/src/traits/solve.rs20
-rw-r--r--compiler/rustc_middle/src/ty/diagnostics.rs6
-rw-r--r--compiler/rustc_middle/src/ty/generic_args.rs11
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs9
-rw-r--r--compiler/rustc_middle/src/ty/print/pretty.rs12
-rw-r--r--compiler/rustc_middle/src/ty/structural_impls.rs75
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs8
-rw-r--r--compiler/rustc_middle/src/ty/visit.rs46
-rw-r--r--compiler/rustc_monomorphize/src/polymorphize.rs15
-rw-r--r--compiler/rustc_privacy/src/lib.rs121
-rw-r--r--compiler/rustc_trait_selection/Cargo.toml1
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs6
-rw-r--r--compiler/rustc_trait_selection/src/traits/coherence.rs21
-rw-r--r--compiler/rustc_trait_selection/src/traits/const_evaluatable.rs7
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/mod.rs8
-rw-r--r--compiler/rustc_trait_selection/src/traits/object_safety.rs6
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/normalize.rs19
-rw-r--r--compiler/rustc_trait_selection/src/traits/structural_match.rs4
-rw-r--r--compiler/rustc_transmute/Cargo.toml16
-rw-r--r--compiler/rustc_ty_utils/src/opaque_types.rs15
-rw-r--r--compiler/rustc_ty_utils/src/ty.rs10
-rw-r--r--compiler/rustc_type_ir/src/canonical.rs15
-rw-r--r--compiler/rustc_type_ir/src/lib.rs4
-rw-r--r--compiler/rustc_type_ir/src/macros.rs4
-rw-r--r--compiler/rustc_type_ir/src/predicate_kind.rs17
-rw-r--r--compiler/rustc_type_ir/src/ty_kind.rs5
-rw-r--r--compiler/rustc_type_ir/src/visit.rs103
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/useless_asref.rs4
-rw-r--r--src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs4
-rw-r--r--src/tools/clippy/clippy_utils/src/ty.rs6
53 files changed, 345 insertions, 448 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 5feb21a65b7..dfb0c066ee6 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3970,6 +3970,7 @@ version = "0.0.0"
 dependencies = [
  "itertools 0.11.0",
  "rustc_ast",
+ "rustc_ast_ir",
  "rustc_attr",
  "rustc_data_structures",
  "rustc_errors",
@@ -4038,6 +4039,7 @@ dependencies = [
 name = "rustc_infer"
 version = "0.0.0"
 dependencies = [
+ "rustc_ast_ir",
  "rustc_data_structures",
  "rustc_errors",
  "rustc_fluent_macro",
@@ -4632,6 +4634,7 @@ dependencies = [
  "bitflags 2.4.2",
  "itertools 0.11.0",
  "rustc_ast",
+ "rustc_ast_ir",
  "rustc_attr",
  "rustc_data_structures",
  "rustc_errors",
@@ -4670,6 +4673,7 @@ name = "rustc_transmute"
 version = "0.0.0"
 dependencies = [
  "itertools 0.11.0",
+ "rustc_ast_ir",
  "rustc_data_structures",
  "rustc_hir",
  "rustc_infer",
diff --git a/compiler/rustc_const_eval/src/interpret/util.rs b/compiler/rustc_const_eval/src/interpret/util.rs
index 2a13671a829..3427368421f 100644
--- a/compiler/rustc_const_eval/src/interpret/util.rs
+++ b/compiler/rustc_const_eval/src/interpret/util.rs
@@ -30,9 +30,9 @@ where
     }
 
     impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for UsedParamsNeedInstantiationVisitor<'tcx> {
-        type BreakTy = FoundParam;
+        type Result = ControlFlow<FoundParam>;
 
-        fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+        fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
             if !ty.has_param() {
                 return ControlFlow::Continue(());
             }
@@ -64,7 +64,7 @@ where
             }
         }
 
-        fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
+        fn visit_const(&mut self, c: ty::Const<'tcx>) -> Self::Result {
             match c.kind() {
                 ty::ConstKind::Param(..) => ControlFlow::Break(FoundParam),
                 _ => c.super_visit_with(self),
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
index c98dc4deb75..0b41b4f9682 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
@@ -17,7 +17,7 @@ use rustc_trait_selection::traits::{self, ObligationCauseCode, ObligationCtxt};
 use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitor};
 
 use std::mem;
-use std::ops::{ControlFlow, Deref};
+use std::ops::Deref;
 
 use super::ops::{self, NonConstOp, Status};
 use super::qualifs::{self, HasMutInterior, NeedsDrop, NeedsNonConstDrop};
@@ -164,9 +164,9 @@ struct LocalReturnTyVisitor<'ck, 'mir, 'tcx> {
 }
 
 impl<'ck, 'mir, 'tcx> TypeVisitor<TyCtxt<'tcx>> for LocalReturnTyVisitor<'ck, 'mir, 'tcx> {
-    fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+    fn visit_ty(&mut self, t: Ty<'tcx>) {
         match t.kind() {
-            ty::FnPtr(_) => ControlFlow::Continue(()),
+            ty::FnPtr(_) => {}
             ty::Ref(_, _, hir::Mutability::Mut) => {
                 self.checker.check_op(ops::ty::MutRef(self.kind));
                 t.super_visit_with(self)
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index 9421d9b5ae0..298bff99844 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -1474,15 +1474,14 @@ fn opaque_type_cycle_error(
                     closures: Vec<DefId>,
                 }
                 impl<'tcx> ty::visit::TypeVisitor<TyCtxt<'tcx>> for OpaqueTypeCollector {
-                    fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+                    fn visit_ty(&mut self, t: Ty<'tcx>) {
                         match *t.kind() {
                             ty::Alias(ty::Opaque, ty::AliasTy { def_id: def, .. }) => {
                                 self.opaques.push(def);
-                                ControlFlow::Continue(())
                             }
                             ty::Closure(def_id, ..) | ty::Coroutine(def_id, ..) => {
                                 self.closures.push(def_id);
-                                t.super_visit_with(self)
+                                t.super_visit_with(self);
                             }
                             _ => t.super_visit_with(self),
                         }
diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs
index 29dc434ab45..8c4c4fc774e 100644
--- a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs
+++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs
@@ -12,7 +12,6 @@ use rustc_trait_selection::regions::InferCtxtRegionExt;
 use rustc_trait_selection::traits::{
     elaborate, normalize_param_env_or_error, outlives_bounds::InferCtxtExt, ObligationCtxt,
 };
-use std::ops::ControlFlow;
 
 /// Check that an implementation does not refine an RPITIT from a trait method signature.
 pub(super) fn check_refining_return_position_impl_trait_in_trait<'tcx>(
@@ -211,9 +210,7 @@ struct ImplTraitInTraitCollector<'tcx> {
 }
 
 impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ImplTraitInTraitCollector<'tcx> {
-    type BreakTy = !;
-
-    fn visit_ty(&mut self, ty: Ty<'tcx>) -> std::ops::ControlFlow<Self::BreakTy> {
+    fn visit_ty(&mut self, ty: Ty<'tcx>) {
         if let ty::Alias(ty::Projection, proj) = *ty.kind()
             && self.tcx.is_impl_trait_in_trait(proj.def_id)
         {
@@ -223,12 +220,11 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ImplTraitInTraitCollector<'tcx> {
                     .explicit_item_bounds(proj.def_id)
                     .iter_instantiated_copied(self.tcx, proj.args)
                 {
-                    pred.visit_with(self)?;
+                    pred.visit_with(self);
                 }
             }
-            ControlFlow::Continue(())
         } else {
-            ty.super_visit_with(self)
+            ty.super_visit_with(self);
         }
     }
 }
diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index f03d0f8a885..bec7b7bd974 100644
--- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -809,9 +809,7 @@ impl<'tcx> GATArgsCollector<'tcx> {
 }
 
 impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for GATArgsCollector<'tcx> {
-    type BreakTy = !;
-
-    fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+    fn visit_ty(&mut self, t: Ty<'tcx>) {
         match t.kind() {
             ty::Alias(ty::Projection, p) if p.def_id == self.gat => {
                 for (idx, arg) in p.args.iter().enumerate() {
@@ -1456,20 +1454,19 @@ fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id
                 params: FxHashSet<u32>,
             }
             impl<'tcx> ty::visit::TypeVisitor<TyCtxt<'tcx>> for CountParams {
-                type BreakTy = ();
-
-                fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+                type Result = ControlFlow<()>;
+                fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
                     if let ty::Param(param) = t.kind() {
                         self.params.insert(param.index);
                     }
                     t.super_visit_with(self)
                 }
 
-                fn visit_region(&mut self, _: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
+                fn visit_region(&mut self, _: ty::Region<'tcx>) -> Self::Result {
                     ControlFlow::Break(())
                 }
 
-                fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
+                fn visit_const(&mut self, c: ty::Const<'tcx>) -> Self::Result {
                     if let ty::ConstKind::Param(param) = c.kind() {
                         self.params.insert(param.index);
                     }
diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
index 4a73ea57339..ad8ec1036ef 100644
--- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
+++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
@@ -1964,31 +1964,26 @@ fn is_late_bound_map(
         arg_is_constrained: Box<[bool]>,
     }
 
-    use std::ops::ControlFlow;
     use ty::Ty;
     impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ConstrainedCollectorPostAstConv {
-        fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<!> {
+        fn visit_ty(&mut self, t: Ty<'tcx>) {
             match t.kind() {
                 ty::Param(param_ty) => {
                     self.arg_is_constrained[param_ty.index as usize] = true;
                 }
-                ty::Alias(ty::Projection | ty::Inherent, _) => return ControlFlow::Continue(()),
+                ty::Alias(ty::Projection | ty::Inherent, _) => return,
                 _ => (),
             }
             t.super_visit_with(self)
         }
 
-        fn visit_const(&mut self, _: ty::Const<'tcx>) -> ControlFlow<!> {
-            ControlFlow::Continue(())
-        }
+        fn visit_const(&mut self, _: ty::Const<'tcx>) {}
 
-        fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<!> {
+        fn visit_region(&mut self, r: ty::Region<'tcx>) {
             debug!("r={:?}", r.kind());
             if let ty::RegionKind::ReEarlyParam(region) = r.kind() {
                 self.arg_is_constrained[region.index as usize] = true;
             }
-
-            ControlFlow::Continue(())
         }
     }
 
diff --git a/compiler/rustc_hir_analysis/src/constrained_generic_params.rs b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs
index b8de2e46934..3b8bb0731fb 100644
--- a/compiler/rustc_hir_analysis/src/constrained_generic_params.rs
+++ b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs
@@ -1,9 +1,8 @@
 use rustc_data_structures::fx::FxHashSet;
-use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor};
+use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitor};
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_span::Span;
 use rustc_type_ir::fold::TypeFoldable;
-use std::ops::ControlFlow;
 
 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
 pub struct Parameter(pub u32);
@@ -61,13 +60,13 @@ struct ParameterCollector {
 }
 
 impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ParameterCollector {
-    fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+    fn visit_ty(&mut self, t: Ty<'tcx>) {
         match *t.kind() {
             // Projections are not injective in general.
             ty::Alias(ty::Projection | ty::Inherent | ty::Opaque, _)
                 if !self.include_nonconstraining =>
             {
-                return ControlFlow::Continue(());
+                return;
             }
             // All weak alias types should've been expanded beforehand.
             ty::Alias(ty::Weak, _) if !self.include_nonconstraining => {
@@ -80,18 +79,17 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ParameterCollector {
         t.super_visit_with(self)
     }
 
-    fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
+    fn visit_region(&mut self, r: ty::Region<'tcx>) {
         if let ty::ReEarlyParam(data) = *r {
             self.parameters.push(Parameter::from(data));
         }
-        ControlFlow::Continue(())
     }
 
-    fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
+    fn visit_const(&mut self, c: ty::Const<'tcx>) {
         match c.kind() {
             ty::ConstKind::Unevaluated(..) if !self.include_nonconstraining => {
                 // Constant expressions are not injective in general.
-                return c.ty().visit_with(self);
+                return;
             }
             ty::ConstKind::Param(data) => {
                 self.parameters.push(Parameter::from(data));
diff --git a/compiler/rustc_hir_analysis/src/variance/mod.rs b/compiler/rustc_hir_analysis/src/variance/mod.rs
index 4d5dcbadc4a..9618c6d8011 100644
--- a/compiler/rustc_hir_analysis/src/variance/mod.rs
+++ b/compiler/rustc_hir_analysis/src/variance/mod.rs
@@ -10,7 +10,6 @@ use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_middle::query::Providers;
 use rustc_middle::ty::{self, CrateVariancesMap, GenericArgsRef, Ty, TyCtxt};
 use rustc_middle::ty::{TypeSuperVisitable, TypeVisitable};
-use std::ops::ControlFlow;
 
 /// Defines the `TermsContext` basically houses an arena where we can
 /// allocate terms.
@@ -89,15 +88,14 @@ fn variance_of_opaque(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Varianc
 
     impl<'tcx> OpaqueTypeLifetimeCollector<'tcx> {
         #[instrument(level = "trace", skip(self), ret)]
-        fn visit_opaque(&mut self, def_id: DefId, args: GenericArgsRef<'tcx>) -> ControlFlow<!> {
+        fn visit_opaque(&mut self, def_id: DefId, args: GenericArgsRef<'tcx>) {
             if def_id != self.root_def_id && self.tcx.is_descendant_of(def_id, self.root_def_id) {
                 let child_variances = self.tcx.variances_of(def_id);
                 for (a, v) in args.iter().zip_eq(child_variances) {
                     if *v != ty::Bivariant {
-                        a.visit_with(self)?;
+                        a.visit_with(self);
                     }
                 }
-                ControlFlow::Continue(())
             } else {
                 args.visit_with(self)
             }
@@ -106,20 +104,19 @@ fn variance_of_opaque(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Varianc
 
     impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for OpaqueTypeLifetimeCollector<'tcx> {
         #[instrument(level = "trace", skip(self), ret)]
-        fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
+        fn visit_region(&mut self, r: ty::Region<'tcx>) {
             if let ty::RegionKind::ReEarlyParam(ebr) = r.kind() {
                 self.variances[ebr.index as usize] = ty::Invariant;
             }
-            ControlFlow::Continue(())
         }
 
         #[instrument(level = "trace", skip(self), ret)]
-        fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+        fn visit_ty(&mut self, t: Ty<'tcx>) {
             match t.kind() {
                 ty::Alias(_, ty::AliasTy { def_id, args, .. })
                     if matches!(self.tcx.def_kind(*def_id), DefKind::OpaqueTy) =>
                 {
-                    self.visit_opaque(*def_id, args)
+                    self.visit_opaque(*def_id, args);
                 }
                 _ => t.super_visit_with(self),
             }
diff --git a/compiler/rustc_hir_typeck/Cargo.toml b/compiler/rustc_hir_typeck/Cargo.toml
index 975f9375642..0a5fa37ed04 100644
--- a/compiler/rustc_hir_typeck/Cargo.toml
+++ b/compiler/rustc_hir_typeck/Cargo.toml
@@ -7,6 +7,7 @@ edition = "2021"
 # tidy-alphabetical-start
 itertools = "0.11"
 rustc_ast = { path = "../rustc_ast" }
+rustc_ast_ir = { path = "../rustc_ast_ir" }
 rustc_attr = { path = "../rustc_attr" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_errors = { path = "../rustc_errors" }
diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs
index 5bdd9412d0e..b8078413611 100644
--- a/compiler/rustc_hir_typeck/src/closure.rs
+++ b/compiler/rustc_hir_typeck/src/closure.rs
@@ -396,9 +396,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     expected_ty: Ty<'tcx>,
                 }
                 impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for MentionsTy<'tcx> {
-                    type BreakTy = ();
+                    type Result = ControlFlow<()>;
 
-                    fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+                    fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
                         if t == self.expected_ty {
                             ControlFlow::Break(())
                         } else {
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs
index f0631dd4b5f..a9a5a89a413 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs
@@ -337,8 +337,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     ) -> Option<ty::GenericArg<'tcx>> {
         struct FindAmbiguousParameter<'a, 'tcx>(&'a FnCtxt<'a, 'tcx>, DefId);
         impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for FindAmbiguousParameter<'_, 'tcx> {
-            type BreakTy = ty::GenericArg<'tcx>;
-            fn visit_ty(&mut self, ty: Ty<'tcx>) -> std::ops::ControlFlow<Self::BreakTy> {
+            type Result = ControlFlow<ty::GenericArg<'tcx>>;
+            fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
                 if let Some(origin) = self.0.type_var_origin(ty)
                     && let TypeVariableOriginKind::TypeParameterDefinition(_, def_id) = origin.kind
                     && let generics = self.0.tcx.generics_of(self.1)
diff --git a/compiler/rustc_infer/Cargo.toml b/compiler/rustc_infer/Cargo.toml
index 73a02a431df..c1565a7d40f 100644
--- a/compiler/rustc_infer/Cargo.toml
+++ b/compiler/rustc_infer/Cargo.toml
@@ -8,6 +8,7 @@ doctest = false
 
 [dependencies]
 # tidy-alphabetical-start
+rustc_ast_ir = { path = "../rustc_ast_ir" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_errors = { path = "../rustc_errors" }
 rustc_fluent_macro = { path = "../rustc_fluent_macro" }
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index 342f18a18dc..362ca3b4833 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -79,7 +79,7 @@ use rustc_middle::ty::{
 use rustc_span::{sym, symbol::kw, BytePos, DesugaringKind, Pos, Span};
 use rustc_target::spec::abi;
 use std::borrow::Cow;
-use std::ops::{ControlFlow, Deref};
+use std::ops::Deref;
 use std::path::PathBuf;
 use std::{cmp, fmt, iter};
 
@@ -1623,7 +1623,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         }
 
         impl<'tcx> ty::visit::TypeVisitor<TyCtxt<'tcx>> for OpaqueTypesVisitor<'tcx> {
-            fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+            fn visit_ty(&mut self, t: Ty<'tcx>) {
                 if let Some((kind, def_id)) = TyCategory::from_ty(self.tcx, t) {
                     let span = self.tcx.def_span(def_id);
                     // Avoid cluttering the output when the "found" and error span overlap:
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs
index 33538309b04..adde45f081a 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs
@@ -23,7 +23,6 @@ use rustc_span::symbol::Ident;
 use rustc_span::Span;
 
 use rustc_span::def_id::LocalDefId;
-use std::ops::ControlFlow;
 
 impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
     /// Print the error message for lifetime errors when the return type is a static `impl Trait`,
@@ -545,13 +544,12 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
 pub struct TraitObjectVisitor(pub FxIndexSet<DefId>);
 
 impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for TraitObjectVisitor {
-    fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+    fn visit_ty(&mut self, t: Ty<'tcx>) {
         match t.kind() {
             ty::Dynamic(preds, re, _) if re.is_static() => {
                 if let Some(def_id) = preds.principal_def_id() {
                     self.0.insert(def_id);
                 }
-                ControlFlow::Continue(())
             }
             _ => t.super_visit_with(self),
         }
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs
index ded628fe136..b4cf727bf8f 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs
@@ -16,8 +16,6 @@ use rustc_middle::ty::print::RegionHighlightMode;
 use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitor};
 use rustc_span::Span;
 
-use std::ops::ControlFlow;
-
 impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
     /// Print the error message for lifetime errors when the `impl` doesn't conform to the `trait`.
     pub(super) fn try_report_impl_not_conforming_to_trait(&self) -> Option<ErrorGuaranteed> {
@@ -76,12 +74,11 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
         }
 
         impl<'tcx> ty::visit::TypeVisitor<TyCtxt<'tcx>> for HighlightBuilder<'tcx> {
-            fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
+            fn visit_region(&mut self, r: ty::Region<'tcx>) {
                 if !r.has_name() && self.counter <= 3 {
                     self.highlight.highlighting_region(r, self.counter);
                     self.counter += 1;
                 }
-                ControlFlow::Continue(())
             }
         }
 
diff --git a/compiler/rustc_infer/src/infer/opaque_types.rs b/compiler/rustc_infer/src/infer/opaque_types.rs
index 7a789a1b41b..9f11b3ddb61 100644
--- a/compiler/rustc_infer/src/infer/opaque_types.rs
+++ b/compiler/rustc_infer/src/infer/opaque_types.rs
@@ -17,7 +17,6 @@ use rustc_middle::ty::{
     TypeVisitable, TypeVisitableExt, TypeVisitor,
 };
 use rustc_span::Span;
-use std::ops::ControlFlow;
 
 mod table;
 
@@ -415,29 +414,22 @@ impl<'tcx, OP> TypeVisitor<TyCtxt<'tcx>> for ConstrainOpaqueTypeRegionVisitor<'t
 where
     OP: FnMut(ty::Region<'tcx>),
 {
-    fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
-        &mut self,
-        t: &ty::Binder<'tcx, T>,
-    ) -> ControlFlow<Self::BreakTy> {
+    fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(&mut self, t: &ty::Binder<'tcx, T>) {
         t.super_visit_with(self);
-        ControlFlow::Continue(())
     }
 
-    fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
+    fn visit_region(&mut self, r: ty::Region<'tcx>) {
         match *r {
             // ignore bound regions, keep visiting
-            ty::ReBound(_, _) => ControlFlow::Continue(()),
-            _ => {
-                (self.op)(r);
-                ControlFlow::Continue(())
-            }
+            ty::ReBound(_, _) => {}
+            _ => (self.op)(r),
         }
     }
 
-    fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+    fn visit_ty(&mut self, ty: Ty<'tcx>) {
         // We're only interested in types involving regions
         if !ty.flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS) {
-            return ControlFlow::Continue(());
+            return;
         }
 
         match ty.kind() {
@@ -488,8 +480,6 @@ where
                 ty.super_visit_with(self);
             }
         }
-
-        ControlFlow::Continue(())
     }
 }
 
diff --git a/compiler/rustc_infer/src/infer/outlives/for_liveness.rs b/compiler/rustc_infer/src/infer/outlives/for_liveness.rs
index 42e3d6cad5a..488f435994d 100644
--- a/compiler/rustc_infer/src/infer/outlives/for_liveness.rs
+++ b/compiler/rustc_infer/src/infer/outlives/for_liveness.rs
@@ -2,8 +2,6 @@ use rustc_middle::ty::{
     self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
 };
 
-use std::ops::ControlFlow;
-
 use crate::infer::outlives::test_type_match;
 use crate::infer::region_constraints::VerifyIfEq;
 
@@ -26,29 +24,22 @@ impl<'tcx, OP> TypeVisitor<TyCtxt<'tcx>> for FreeRegionsVisitor<'tcx, OP>
 where
     OP: FnMut(ty::Region<'tcx>),
 {
-    fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
-        &mut self,
-        t: &ty::Binder<'tcx, T>,
-    ) -> ControlFlow<Self::BreakTy> {
+    fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(&mut self, t: &ty::Binder<'tcx, T>) {
         t.super_visit_with(self);
-        ControlFlow::Continue(())
     }
 
-    fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
+    fn visit_region(&mut self, r: ty::Region<'tcx>) {
         match *r {
             // ignore bound regions, keep visiting
-            ty::ReBound(_, _) => ControlFlow::Continue(()),
-            _ => {
-                (self.op)(r);
-                ControlFlow::Continue(())
-            }
+            ty::ReBound(_, _) => {}
+            _ => (self.op)(r),
         }
     }
 
-    fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+    fn visit_ty(&mut self, ty: Ty<'tcx>) {
         // We're only interested in types involving regions
         if !ty.flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS) {
-            return ControlFlow::Continue(());
+            return;
         }
 
         // FIXME: Don't consider alias bounds on types that have escaping bound
@@ -102,7 +93,7 @@ where
                     && outlives_bounds[1..].iter().all(|other_r| other_r == r)
                 {
                     assert!(r.type_flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS));
-                    r.visit_with(self)?;
+                    r.visit_with(self);
                 } else {
                     // Skip lifetime parameters that are not captures.
                     let variances = match kind {
@@ -114,17 +105,13 @@ where
                         if variances.map(|variances| variances[idx])
                             != Some(ty::Variance::Bivariant)
                         {
-                            s.visit_with(self)?;
+                            s.visit_with(self);
                         }
                     }
                 }
             }
 
-            _ => {
-                ty.super_visit_with(self)?;
-            }
+            _ => ty.super_visit_with(self),
         }
-
-        ControlFlow::Continue(())
     }
 }
diff --git a/compiler/rustc_infer/src/traits/structural_impls.rs b/compiler/rustc_infer/src/traits/structural_impls.rs
index bf4f88530d0..064e09b8750 100644
--- a/compiler/rustc_infer/src/traits/structural_impls.rs
+++ b/compiler/rustc_infer/src/traits/structural_impls.rs
@@ -1,11 +1,11 @@
 use crate::traits;
 use crate::traits::project::Normalized;
+use rustc_ast_ir::try_visit;
 use rustc_middle::ty::fold::{FallibleTypeFolder, TypeFoldable};
 use rustc_middle::ty::visit::{TypeVisitable, TypeVisitor};
 use rustc_middle::ty::{self, TyCtxt};
 
 use std::fmt;
-use std::ops::ControlFlow;
 
 // Structural impls for the structs in `traits`.
 
@@ -84,8 +84,8 @@ impl<'tcx, O: TypeFoldable<TyCtxt<'tcx>>> TypeFoldable<TyCtxt<'tcx>>
 impl<'tcx, O: TypeVisitable<TyCtxt<'tcx>>> TypeVisitable<TyCtxt<'tcx>>
     for traits::Obligation<'tcx, O>
 {
-    fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
-        self.predicate.visit_with(visitor)?;
+    fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> V::Result {
+        try_visit!(self.predicate.visit_with(visitor));
         self.param_env.visit_with(visitor)
     }
 }
diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs
index 71f330b5bf7..5d36a8b3d0e 100644
--- a/compiler/rustc_lint/src/types.rs
+++ b/compiler/rustc_lint/src/types.rs
@@ -1474,9 +1474,9 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
     fn check_for_opaque_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool {
         struct ProhibitOpaqueTypes;
         impl<'tcx> ty::visit::TypeVisitor<TyCtxt<'tcx>> for ProhibitOpaqueTypes {
-            type BreakTy = Ty<'tcx>;
+            type Result = ControlFlow<Ty<'tcx>>;
 
-            fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+            fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
                 if !ty.has_opaque_types() {
                     return ControlFlow::Continue(());
                 }
@@ -1620,9 +1620,9 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
         }
 
         impl<'vis, 'a, 'tcx> ty::visit::TypeVisitor<TyCtxt<'tcx>> for FnPtrFinder<'vis, 'a, 'tcx> {
-            type BreakTy = Ty<'tcx>;
+            type Result = ControlFlow<Ty<'tcx>>;
 
-            fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+            fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
                 if let ty::FnPtr(sig) = ty.kind()
                     && !self.visitor.is_internal_abi(sig.abi())
                 {
diff --git a/compiler/rustc_macros/src/type_visitable.rs b/compiler/rustc_macros/src/type_visitable.rs
index c8430380345..94e86e0e246 100644
--- a/compiler/rustc_macros/src/type_visitable.rs
+++ b/compiler/rustc_macros/src/type_visitable.rs
@@ -34,7 +34,14 @@ pub fn type_visitable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2:
     s.add_bounds(synstructure::AddBounds::Generics);
     let body_visit = s.each(|bind| {
         quote! {
-            ::rustc_middle::ty::visit::TypeVisitable::visit_with(#bind, __visitor)?;
+            match ::rustc_ast_ir::visit::VisitorResult::branch(
+                ::rustc_middle::ty::visit::TypeVisitable::visit_with(#bind, __visitor)
+            ) {
+                ::core::ops::ControlFlow::Continue(()) => {},
+                ::core::ops::ControlFlow::Break(r) => {
+                    return ::rustc_ast_ir::visit::VisitorResult::from_residual(r);
+                },
+            }
         }
     });
     s.bind_with(|_| synstructure::BindStyle::Move);
@@ -45,9 +52,9 @@ pub fn type_visitable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2:
             fn visit_with<__V: ::rustc_middle::ty::visit::TypeVisitor<::rustc_middle::ty::TyCtxt<'tcx>>>(
                 &self,
                 __visitor: &mut __V
-            ) -> ::std::ops::ControlFlow<__V::BreakTy> {
+            ) -> __V::Result {
                 match *self { #body_visit }
-                ::std::ops::ControlFlow::Continue(())
+                <__V::Result as ::rustc_ast_ir::visit::VisitorResult>::output()
             }
         },
     )
diff --git a/compiler/rustc_middle/src/macros.rs b/compiler/rustc_middle/src/macros.rs
index 26ca90db018..0ee97a6bed0 100644
--- a/compiler/rustc_middle/src/macros.rs
+++ b/compiler/rustc_middle/src/macros.rs
@@ -85,9 +85,9 @@ macro_rules! TrivialTypeTraversalImpls {
                 fn visit_with<F: $crate::ty::visit::TypeVisitor<$crate::ty::TyCtxt<'tcx>>>(
                     &self,
                     _: &mut F)
-                    -> ::std::ops::ControlFlow<F::BreakTy>
+                    -> F::Result
                 {
-                    ::std::ops::ControlFlow::Continue(())
+                    <F::Result as ::rustc_ast_ir::visit::VisitorResult>::output()
                 }
             }
         )+
diff --git a/compiler/rustc_middle/src/traits/solve.rs b/compiler/rustc_middle/src/traits/solve.rs
index 0dc65126011..95139b50cb8 100644
--- a/compiler/rustc_middle/src/traits/solve.rs
+++ b/compiler/rustc_middle/src/traits/solve.rs
@@ -1,6 +1,6 @@
-use std::ops::ControlFlow;
-
+use rustc_ast_ir::try_visit;
 use rustc_data_structures::intern::Interned;
+use rustc_span::def_id::DefId;
 
 use crate::infer::canonical::{CanonicalVarValues, QueryRegionConstraints};
 use crate::traits::query::NoSolution;
@@ -9,7 +9,6 @@ use crate::ty::{
     self, FallibleTypeFolder, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeVisitable,
     TypeVisitor,
 };
-use rustc_span::def_id::DefId;
 
 use super::BuiltinImplSource;
 
@@ -196,13 +195,9 @@ impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for ExternalConstraints<'tcx> {
 }
 
 impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for ExternalConstraints<'tcx> {
-    fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(
-        &self,
-        visitor: &mut V,
-    ) -> std::ops::ControlFlow<V::BreakTy> {
-        self.region_constraints.visit_with(visitor)?;
-        self.opaque_types.visit_with(visitor)?;
-        ControlFlow::Continue(())
+    fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> V::Result {
+        try_visit!(self.region_constraints.visit_with(visitor));
+        self.opaque_types.visit_with(visitor)
     }
 }
 
@@ -235,10 +230,7 @@ impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for PredefinedOpaques<'tcx> {
 }
 
 impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for PredefinedOpaques<'tcx> {
-    fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(
-        &self,
-        visitor: &mut V,
-    ) -> std::ops::ControlFlow<V::BreakTy> {
+    fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> V::Result {
         self.opaque_types.visit_with(visitor)
     }
 }
diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs
index 712fbf24440..2506456f1fb 100644
--- a/compiler/rustc_middle/src/ty/diagnostics.rs
+++ b/compiler/rustc_middle/src/ty/diagnostics.rs
@@ -482,9 +482,9 @@ pub struct IsSuggestableVisitor<'tcx> {
 }
 
 impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for IsSuggestableVisitor<'tcx> {
-    type BreakTy = ();
+    type Result = ControlFlow<()>;
 
-    fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+    fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
         match *t.kind() {
             Infer(InferTy::TyVar(_)) if self.infer_suggestable => {}
 
@@ -536,7 +536,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for IsSuggestableVisitor<'tcx> {
         t.super_visit_with(self)
     }
 
-    fn visit_const(&mut self, c: Const<'tcx>) -> ControlFlow<Self::BreakTy> {
+    fn visit_const(&mut self, c: Const<'tcx>) -> Self::Result {
         match c.kind() {
             ConstKind::Infer(InferConst::Var(_)) if self.infer_suggestable => {}
 
diff --git a/compiler/rustc_middle/src/ty/generic_args.rs b/compiler/rustc_middle/src/ty/generic_args.rs
index c87ef870a08..8079ad121ad 100644
--- a/compiler/rustc_middle/src/ty/generic_args.rs
+++ b/compiler/rustc_middle/src/ty/generic_args.rs
@@ -6,6 +6,8 @@ use crate::ty::sty::{ClosureArgs, CoroutineArgs, CoroutineClosureArgs, InlineCon
 use crate::ty::visit::{TypeVisitable, TypeVisitableExt, TypeVisitor};
 use crate::ty::{self, Lift, List, ParamConst, Ty, TyCtxt};
 
+use rustc_ast_ir::visit::VisitorResult;
+use rustc_ast_ir::walk_visitable_list;
 use rustc_data_structures::intern::Interned;
 use rustc_errors::{DiagArgValue, IntoDiagnosticArg};
 use rustc_hir::def_id::DefId;
@@ -19,7 +21,7 @@ use std::cmp::Ordering;
 use std::marker::PhantomData;
 use std::mem;
 use std::num::NonZero;
-use std::ops::{ControlFlow, Deref};
+use std::ops::Deref;
 use std::ptr::NonNull;
 
 /// An entity in the Rust type system, which can be one of
@@ -240,7 +242,7 @@ impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for GenericArg<'tcx> {
 }
 
 impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for GenericArg<'tcx> {
-    fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
+    fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> V::Result {
         match self.unpack() {
             GenericArgKind::Lifetime(lt) => lt.visit_with(visitor),
             GenericArgKind::Type(ty) => ty.visit_with(visitor),
@@ -554,8 +556,9 @@ impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for &'tcx ty::List<Ty<'tcx>> {
 
 impl<'tcx, T: TypeVisitable<TyCtxt<'tcx>>> TypeVisitable<TyCtxt<'tcx>> for &'tcx ty::List<T> {
     #[inline]
-    fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
-        self.iter().try_for_each(|t| t.visit_with(visitor))
+    fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> V::Result {
+        walk_visitable_list!(visitor, self.iter());
+        V::Result::output()
     }
 }
 
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index c6bb7032ace..0a38d379a52 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -33,7 +33,7 @@ pub use generics::*;
 pub use intrinsic::IntrinsicDef;
 use rustc_ast as ast;
 use rustc_ast::node_id::NodeMap;
-pub use rustc_ast_ir::{Movability, Mutability};
+pub use rustc_ast_ir::{try_visit, Movability, Mutability};
 use rustc_attr as attr;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet};
 use rustc_data_structures::intern::Interned;
@@ -64,7 +64,6 @@ use std::hash::{Hash, Hasher};
 use std::marker::PhantomData;
 use std::mem;
 use std::num::NonZero;
-use std::ops::ControlFlow;
 use std::ptr::NonNull;
 use std::{fmt, str};
 
@@ -599,7 +598,7 @@ impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for Term<'tcx> {
 }
 
 impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for Term<'tcx> {
-    fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
+    fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> V::Result {
         self.unpack().visit_with(visitor)
     }
 }
@@ -1043,8 +1042,8 @@ impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for ParamEnv<'tcx> {
 }
 
 impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for ParamEnv<'tcx> {
-    fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
-        self.caller_bounds().visit_with(visitor)?;
+    fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> V::Result {
+        try_visit!(self.caller_bounds().visit_with(visitor));
         self.reveal().visit_with(visitor)
     }
 }
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index 540803d9530..c8c9afa7f4d 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -27,7 +27,7 @@ use std::cell::Cell;
 use std::collections::BTreeMap;
 use std::fmt::{self, Write as _};
 use std::iter;
-use std::ops::{ControlFlow, Deref, DerefMut};
+use std::ops::{Deref, DerefMut};
 
 // `pretty` is a separate module only for organization.
 use super::*;
@@ -2667,9 +2667,7 @@ impl<'tcx> FmtPrinter<'_, 'tcx> {
         }
 
         impl<'tcx> ty::visit::TypeVisitor<TyCtxt<'tcx>> for RegionNameCollector<'tcx> {
-            type BreakTy = ();
-
-            fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
+            fn visit_region(&mut self, r: ty::Region<'tcx>) {
                 trace!("address: {:p}", r.0.0);
 
                 // Collect all named lifetimes. These allow us to prevent duplication
@@ -2678,18 +2676,14 @@ impl<'tcx> FmtPrinter<'_, 'tcx> {
                 if let Some(name) = r.get_name() {
                     self.used_region_names.insert(name);
                 }
-
-                ControlFlow::Continue(())
             }
 
             // We collect types in order to prevent really large types from compiling for
             // a really long time. See issue #83150 for why this is necessary.
-            fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+            fn visit_ty(&mut self, ty: Ty<'tcx>) {
                 let not_previously_inserted = self.type_collector.insert(ty);
                 if not_previously_inserted {
                     ty.super_visit_with(self)
-                } else {
-                    ControlFlow::Continue(())
                 }
             }
         }
diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs
index c6805ba9323..c8fb11673cf 100644
--- a/compiler/rustc_middle/src/ty/structural_impls.rs
+++ b/compiler/rustc_middle/src/ty/structural_impls.rs
@@ -8,13 +8,14 @@ use crate::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable};
 use crate::ty::print::{with_no_trimmed_paths, FmtPrinter, Printer};
 use crate::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor};
 use crate::ty::{self, AliasTy, InferConst, Lift, Term, TermKind, Ty, TyCtxt};
+use rustc_ast_ir::try_visit;
+use rustc_ast_ir::visit::VisitorResult;
 use rustc_hir::def::Namespace;
 use rustc_span::source_map::Spanned;
 use rustc_target::abi::TyAndLayout;
 use rustc_type_ir::{ConstKind, DebugWithInfcx, InferCtxtLike, WithInfcx};
 
 use std::fmt::{self, Debug};
-use std::ops::ControlFlow;
 
 use super::print::PrettyPrinter;
 use super::{GenericArg, GenericArgKind, Region};
@@ -482,11 +483,8 @@ impl<'a, 'tcx> Lift<'tcx> for Term<'a> {
 // Traversal implementations.
 
 impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for ty::AdtDef<'tcx> {
-    fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(
-        &self,
-        _visitor: &mut V,
-    ) -> ControlFlow<V::BreakTy> {
-        ControlFlow::Continue(())
+    fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, _visitor: &mut V) -> V::Result {
+        V::Result::output()
     }
 }
 
@@ -500,7 +498,7 @@ impl<'tcx, T: TypeFoldable<TyCtxt<'tcx>>> TypeFoldable<TyCtxt<'tcx>> for ty::Bin
 }
 
 impl<'tcx, T: TypeVisitable<TyCtxt<'tcx>>> TypeVisitable<TyCtxt<'tcx>> for ty::Binder<'tcx, T> {
-    fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
+    fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> V::Result {
         visitor.visit_binder(self)
     }
 }
@@ -517,10 +515,7 @@ impl<'tcx, T: TypeFoldable<TyCtxt<'tcx>>> TypeSuperFoldable<TyCtxt<'tcx>> for ty
 impl<'tcx, T: TypeVisitable<TyCtxt<'tcx>>> TypeSuperVisitable<TyCtxt<'tcx>>
     for ty::Binder<'tcx, T>
 {
-    fn super_visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(
-        &self,
-        visitor: &mut V,
-    ) -> ControlFlow<V::BreakTy> {
+    fn super_visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> V::Result {
         self.as_ref().skip_binder().visit_with(visitor)
     }
 }
@@ -553,7 +548,7 @@ impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for Ty<'tcx> {
 }
 
 impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for Ty<'tcx> {
-    fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
+    fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> V::Result {
         visitor.visit_ty(*self)
     }
 }
@@ -609,27 +604,24 @@ impl<'tcx> TypeSuperFoldable<TyCtxt<'tcx>> for Ty<'tcx> {
 }
 
 impl<'tcx> TypeSuperVisitable<TyCtxt<'tcx>> for Ty<'tcx> {
-    fn super_visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(
-        &self,
-        visitor: &mut V,
-    ) -> ControlFlow<V::BreakTy> {
+    fn super_visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> V::Result {
         match self.kind() {
             ty::RawPtr(ref tm) => tm.visit_with(visitor),
             ty::Array(typ, sz) => {
-                typ.visit_with(visitor)?;
+                try_visit!(typ.visit_with(visitor));
                 sz.visit_with(visitor)
             }
             ty::Slice(typ) => typ.visit_with(visitor),
             ty::Adt(_, args) => args.visit_with(visitor),
             ty::Dynamic(ref trait_ty, ref reg, _) => {
-                trait_ty.visit_with(visitor)?;
+                try_visit!(trait_ty.visit_with(visitor));
                 reg.visit_with(visitor)
             }
             ty::Tuple(ts) => ts.visit_with(visitor),
             ty::FnDef(_, args) => args.visit_with(visitor),
             ty::FnPtr(ref f) => f.visit_with(visitor),
             ty::Ref(r, ty, _) => {
-                r.visit_with(visitor)?;
+                try_visit!(r.visit_with(visitor));
                 ty.visit_with(visitor)
             }
             ty::Coroutine(_did, ref args) => args.visit_with(visitor),
@@ -650,7 +642,7 @@ impl<'tcx> TypeSuperVisitable<TyCtxt<'tcx>> for Ty<'tcx> {
             | ty::Placeholder(..)
             | ty::Param(..)
             | ty::Never
-            | ty::Foreign(..) => ControlFlow::Continue(()),
+            | ty::Foreign(..) => V::Result::output(),
         }
     }
 }
@@ -665,7 +657,7 @@ impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for ty::Region<'tcx> {
 }
 
 impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for ty::Region<'tcx> {
-    fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
+    fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> V::Result {
         visitor.visit_region(*self)
     }
 }
@@ -690,13 +682,13 @@ impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for ty::Clause<'tcx> {
 }
 
 impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for ty::Predicate<'tcx> {
-    fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
+    fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> V::Result {
         visitor.visit_predicate(*self)
     }
 }
 
 impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for ty::Clause<'tcx> {
-    fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
+    fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> V::Result {
         visitor.visit_predicate(self.as_predicate())
     }
 }
@@ -712,10 +704,7 @@ impl<'tcx> TypeSuperFoldable<TyCtxt<'tcx>> for ty::Predicate<'tcx> {
 }
 
 impl<'tcx> TypeSuperVisitable<TyCtxt<'tcx>> for ty::Predicate<'tcx> {
-    fn super_visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(
-        &self,
-        visitor: &mut V,
-    ) -> ControlFlow<V::BreakTy> {
+    fn super_visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> V::Result {
         self.kind().visit_with(visitor)
     }
 }
@@ -739,7 +728,7 @@ impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for ty::Const<'tcx> {
 }
 
 impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for ty::Const<'tcx> {
-    fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
+    fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> V::Result {
         visitor.visit_const(*self)
     }
 }
@@ -771,16 +760,13 @@ impl<'tcx> TypeSuperFoldable<TyCtxt<'tcx>> for ty::Const<'tcx> {
 }
 
 impl<'tcx> TypeSuperVisitable<TyCtxt<'tcx>> for ty::Const<'tcx> {
-    fn super_visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(
-        &self,
-        visitor: &mut V,
-    ) -> ControlFlow<V::BreakTy> {
-        self.ty().visit_with(visitor)?;
+    fn super_visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> V::Result {
+        try_visit!(self.ty().visit_with(visitor));
         match self.kind() {
             ConstKind::Param(p) => p.visit_with(visitor),
             ConstKind::Infer(i) => i.visit_with(visitor),
             ConstKind::Bound(d, b) => {
-                d.visit_with(visitor)?;
+                try_visit!(d.visit_with(visitor));
                 b.visit_with(visitor)
             }
             ConstKind::Placeholder(p) => p.visit_with(visitor),
@@ -802,25 +788,19 @@ impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for InferConst {
 }
 
 impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for InferConst {
-    fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(
-        &self,
-        _visitor: &mut V,
-    ) -> ControlFlow<V::BreakTy> {
-        ControlFlow::Continue(())
+    fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, _visitor: &mut V) -> V::Result {
+        V::Result::output()
     }
 }
 
 impl<'tcx> TypeSuperVisitable<TyCtxt<'tcx>> for ty::UnevaluatedConst<'tcx> {
-    fn super_visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(
-        &self,
-        visitor: &mut V,
-    ) -> ControlFlow<V::BreakTy> {
+    fn super_visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> V::Result {
         self.args.visit_with(visitor)
     }
 }
 
 impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for TyAndLayout<'tcx, Ty<'tcx>> {
-    fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
+    fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> V::Result {
         visitor.visit_ty(self.ty)
     }
 }
@@ -828,10 +808,9 @@ impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for TyAndLayout<'tcx, Ty<'tcx>> {
 impl<'tcx, T: TypeVisitable<TyCtxt<'tcx>> + Debug + Clone> TypeVisitable<TyCtxt<'tcx>>
     for Spanned<T>
 {
-    fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
-        self.node.visit_with(visitor)?;
-        self.span.visit_with(visitor)?;
-        ControlFlow::Continue(())
+    fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> V::Result {
+        try_visit!(self.node.visit_with(visitor));
+        self.span.visit_with(visitor)
     }
 }
 
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index 17659ac2330..4b349e7836c 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -2113,9 +2113,9 @@ impl<'tcx> Ty<'tcx> {
         struct ContainsTyVisitor<'tcx>(Ty<'tcx>);
 
         impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ContainsTyVisitor<'tcx> {
-            type BreakTy = ();
+            type Result = ControlFlow<()>;
 
-            fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+            fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
                 if self.0 == t { ControlFlow::Break(()) } else { t.super_visit_with(self) }
             }
         }
@@ -2131,9 +2131,9 @@ impl<'tcx> Ty<'tcx> {
         struct ContainsClosureVisitor;
 
         impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ContainsClosureVisitor {
-            type BreakTy = ();
+            type Result = ControlFlow<()>;
 
-            fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+            fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
                 if let ty::Closure(..) = t.kind() {
                     ControlFlow::Break(())
                 } else {
diff --git a/compiler/rustc_middle/src/ty/visit.rs b/compiler/rustc_middle/src/ty/visit.rs
index 59d09c3dc78..218567bbd59 100644
--- a/compiler/rustc_middle/src/ty/visit.rs
+++ b/compiler/rustc_middle/src/ty/visit.rs
@@ -64,19 +64,19 @@ impl<'tcx> TyCtxt<'tcx> {
         where
             F: FnMut(ty::Region<'tcx>) -> bool,
         {
-            type BreakTy = ();
+            type Result = ControlFlow<()>;
 
             fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
                 &mut self,
                 t: &Binder<'tcx, T>,
-            ) -> ControlFlow<Self::BreakTy> {
+            ) -> Self::Result {
                 self.outer_index.shift_in(1);
                 let result = t.super_visit_with(self);
                 self.outer_index.shift_out(1);
                 result
             }
 
-            fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
+            fn visit_region(&mut self, r: ty::Region<'tcx>) -> Self::Result {
                 match *r {
                     ty::ReBound(debruijn, _) if debruijn < self.outer_index => {
                         ControlFlow::Continue(())
@@ -91,7 +91,7 @@ impl<'tcx> TyCtxt<'tcx> {
                 }
             }
 
-            fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+            fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
                 // We're only interested in types involving regions
                 if ty.flags().intersects(TypeFlags::HAS_FREE_REGIONS) {
                     ty.super_visit_with(self)
@@ -140,8 +140,7 @@ impl<'tcx> TyCtxt<'tcx> {
         let mut collector = LateBoundRegionsCollector::new(just_constrained);
         let value = value.skip_binder();
         let value = if just_constrained { self.expand_weak_alias_tys(value) } else { value };
-        let result = value.visit_with(&mut collector);
-        assert!(result.is_continue()); // should never have stopped early
+        value.visit_with(&mut collector);
         collector.regions
     }
 }
@@ -165,19 +164,19 @@ impl<'tcx> ValidateBoundVars<'tcx> {
 }
 
 impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ValidateBoundVars<'tcx> {
-    type BreakTy = ();
+    type Result = ControlFlow<()>;
 
     fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
         &mut self,
         t: &Binder<'tcx, T>,
-    ) -> ControlFlow<Self::BreakTy> {
+    ) -> Self::Result {
         self.binder_index.shift_in(1);
         let result = t.super_visit_with(self);
         self.binder_index.shift_out(1);
         result
     }
 
-    fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+    fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
         if t.outer_exclusive_binder() < self.binder_index
             || !self.visited.insert((self.binder_index, t))
         {
@@ -211,7 +210,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ValidateBoundVars<'tcx> {
         t.super_visit_with(self)
     }
 
-    fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
+    fn visit_region(&mut self, r: ty::Region<'tcx>) -> Self::Result {
         match *r {
             ty::ReBound(index, br) if index == self.binder_index => {
                 if self.bound_vars.len() <= br.var.as_usize() {
@@ -266,23 +265,19 @@ impl LateBoundRegionsCollector {
 }
 
 impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for LateBoundRegionsCollector {
-    fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
-        &mut self,
-        t: &Binder<'tcx, T>,
-    ) -> ControlFlow<Self::BreakTy> {
+    fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(&mut self, t: &Binder<'tcx, T>) {
         self.current_index.shift_in(1);
-        let result = t.super_visit_with(self);
+        t.super_visit_with(self);
         self.current_index.shift_out(1);
-        result
     }
 
-    fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+    fn visit_ty(&mut self, t: Ty<'tcx>) {
         if self.just_constrained {
             match t.kind() {
                 // If we are only looking for "constrained" regions, we have to ignore the
                 // inputs to a projection as they may not appear in the normalized form.
                 ty::Alias(ty::Projection | ty::Inherent | ty::Opaque, _) => {
-                    return ControlFlow::Continue(());
+                    return;
                 }
                 // All weak alias types should've been expanded beforehand.
                 ty::Alias(ty::Weak, _) => bug!("unexpected weak alias type"),
@@ -293,26 +288,25 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for LateBoundRegionsCollector {
         t.super_visit_with(self)
     }
 
-    fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
+    fn visit_const(&mut self, c: ty::Const<'tcx>) {
         // if we are only looking for "constrained" region, we have to
         // ignore the inputs of an unevaluated const, as they may not appear
         // in the normalized form
         if self.just_constrained {
             if let ty::ConstKind::Unevaluated(..) = c.kind() {
-                return ControlFlow::Continue(());
+                return;
             }
         }
 
         c.super_visit_with(self)
     }
 
-    fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
+    fn visit_region(&mut self, r: ty::Region<'tcx>) {
         if let ty::ReBound(debruijn, br) = *r {
             if debruijn == self.current_index {
                 self.regions.insert(br.kind);
             }
         }
-        ControlFlow::Continue(())
     }
 }
 
@@ -332,7 +326,7 @@ impl MaxUniverse {
 }
 
 impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for MaxUniverse {
-    fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+    fn visit_ty(&mut self, t: Ty<'tcx>) {
         if let ty::Placeholder(placeholder) = t.kind() {
             self.max_universe = ty::UniverseIndex::from_u32(
                 self.max_universe.as_u32().max(placeholder.universe.as_u32()),
@@ -342,7 +336,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for MaxUniverse {
         t.super_visit_with(self)
     }
 
-    fn visit_const(&mut self, c: ty::consts::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
+    fn visit_const(&mut self, c: ty::consts::Const<'tcx>) {
         if let ty::ConstKind::Placeholder(placeholder) = c.kind() {
             self.max_universe = ty::UniverseIndex::from_u32(
                 self.max_universe.as_u32().max(placeholder.universe.as_u32()),
@@ -352,13 +346,11 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for MaxUniverse {
         c.super_visit_with(self)
     }
 
-    fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
+    fn visit_region(&mut self, r: ty::Region<'tcx>) {
         if let ty::RePlaceholder(placeholder) = *r {
             self.max_universe = ty::UniverseIndex::from_u32(
                 self.max_universe.as_u32().max(placeholder.universe.as_u32()),
             );
         }
-
-        ControlFlow::Continue(())
     }
 }
diff --git a/compiler/rustc_monomorphize/src/polymorphize.rs b/compiler/rustc_monomorphize/src/polymorphize.rs
index fb42532e247..1070d1a1380 100644
--- a/compiler/rustc_monomorphize/src/polymorphize.rs
+++ b/compiler/rustc_monomorphize/src/polymorphize.rs
@@ -18,7 +18,6 @@ use rustc_middle::ty::{
     GenericArgsRef, Ty, TyCtxt, UnusedGenericParams,
 };
 use rustc_span::symbol::sym;
-use std::ops::ControlFlow;
 
 use crate::errors::UnusedGenericParamsHint;
 
@@ -291,31 +290,29 @@ impl<'a, 'tcx> Visitor<'tcx> for MarkUsedGenericParams<'a, 'tcx> {
 
 impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for MarkUsedGenericParams<'a, 'tcx> {
     #[instrument(level = "debug", skip(self))]
-    fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
+    fn visit_const(&mut self, c: ty::Const<'tcx>) {
         if !c.has_non_region_param() {
-            return ControlFlow::Continue(());
+            return;
         }
 
         match c.kind() {
             ty::ConstKind::Param(param) => {
                 debug!(?param);
                 self.unused_parameters.mark_used(param.index);
-                ControlFlow::Continue(())
             }
             ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, args })
                 if matches!(self.tcx.def_kind(def), DefKind::AnonConst) =>
             {
                 self.visit_child_body(def, args);
-                ControlFlow::Continue(())
             }
             _ => c.super_visit_with(self),
         }
     }
 
     #[instrument(level = "debug", skip(self))]
-    fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+    fn visit_ty(&mut self, ty: Ty<'tcx>) {
         if !ty.has_non_region_param() {
-            return ControlFlow::Continue(());
+            return;
         }
 
         match *ty.kind() {
@@ -323,18 +320,16 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for MarkUsedGenericParams<'a, 'tcx> {
                 debug!(?def_id);
                 // Avoid cycle errors with coroutines.
                 if def_id == self.def_id {
-                    return ControlFlow::Continue(());
+                    return;
                 }
 
                 // Consider any generic parameters used by any closures/coroutines as used in the
                 // parent.
                 self.visit_child_body(def_id, args);
-                ControlFlow::Continue(())
             }
             ty::Param(param) => {
                 debug!(?param);
                 self.unused_parameters.mark_used(param.index);
-                ControlFlow::Continue(())
             }
             _ => ty.super_visit_with(self),
         }
diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs
index 9d8a9f5fce3..fc110afd8e4 100644
--- a/compiler/rustc_privacy/src/lib.rs
+++ b/compiler/rustc_privacy/src/lib.rs
@@ -11,6 +11,7 @@ extern crate tracing;
 
 mod errors;
 
+use rustc_ast::visit::{try_visit, VisitorResult};
 use rustc_ast::MacroDef;
 use rustc_attr as attr;
 use rustc_data_structures::fx::FxHashSet;
@@ -67,17 +68,13 @@ impl<'tcx> fmt::Display for LazyDefPathStr<'tcx> {
 /// manually. Second, it doesn't visit some type components like signatures of fn types, or traits
 /// in `impl Trait`, see individual comments in `DefIdVisitorSkeleton::visit_ty`.
 trait DefIdVisitor<'tcx> {
-    type BreakTy = ();
+    type Result: VisitorResult = ();
     const SHALLOW: bool = false;
     const SKIP_ASSOC_TYS: bool = false;
 
     fn tcx(&self) -> TyCtxt<'tcx>;
-    fn visit_def_id(
-        &mut self,
-        def_id: DefId,
-        kind: &str,
-        descr: &dyn fmt::Display,
-    ) -> ControlFlow<Self::BreakTy>;
+    fn visit_def_id(&mut self, def_id: DefId, kind: &str, descr: &dyn fmt::Display)
+    -> Self::Result;
 
     /// Not overridden, but used to actually visit types and traits.
     fn skeleton(&mut self) -> DefIdVisitorSkeleton<'_, 'tcx, Self> {
@@ -87,25 +84,16 @@ trait DefIdVisitor<'tcx> {
             dummy: Default::default(),
         }
     }
-    fn visit(
-        &mut self,
-        ty_fragment: impl TypeVisitable<TyCtxt<'tcx>>,
-    ) -> ControlFlow<Self::BreakTy> {
+    fn visit(&mut self, ty_fragment: impl TypeVisitable<TyCtxt<'tcx>>) -> Self::Result {
         ty_fragment.visit_with(&mut self.skeleton())
     }
-    fn visit_trait(&mut self, trait_ref: TraitRef<'tcx>) -> ControlFlow<Self::BreakTy> {
+    fn visit_trait(&mut self, trait_ref: TraitRef<'tcx>) -> Self::Result {
         self.skeleton().visit_trait(trait_ref)
     }
-    fn visit_predicates(
-        &mut self,
-        predicates: ty::GenericPredicates<'tcx>,
-    ) -> ControlFlow<Self::BreakTy> {
+    fn visit_predicates(&mut self, predicates: ty::GenericPredicates<'tcx>) -> Self::Result {
         self.skeleton().visit_clauses(predicates.predicates)
     }
-    fn visit_clauses(
-        &mut self,
-        clauses: &[(ty::Clause<'tcx>, Span)],
-    ) -> ControlFlow<Self::BreakTy> {
+    fn visit_clauses(&mut self, clauses: &[(ty::Clause<'tcx>, Span)]) -> Self::Result {
         self.skeleton().visit_clauses(clauses)
     }
 }
@@ -120,36 +108,42 @@ impl<'tcx, V> DefIdVisitorSkeleton<'_, 'tcx, V>
 where
     V: DefIdVisitor<'tcx> + ?Sized,
 {
-    fn visit_trait(&mut self, trait_ref: TraitRef<'tcx>) -> ControlFlow<V::BreakTy> {
+    fn visit_trait(&mut self, trait_ref: TraitRef<'tcx>) -> V::Result {
         let TraitRef { def_id, args, .. } = trait_ref;
-        self.def_id_visitor.visit_def_id(def_id, "trait", &trait_ref.print_only_trait_path())?;
-        if V::SHALLOW { ControlFlow::Continue(()) } else { args.visit_with(self) }
+        try_visit!(self.def_id_visitor.visit_def_id(
+            def_id,
+            "trait",
+            &trait_ref.print_only_trait_path()
+        ));
+        if V::SHALLOW { V::Result::output() } else { args.visit_with(self) }
     }
 
-    fn visit_projection_ty(&mut self, projection: ty::AliasTy<'tcx>) -> ControlFlow<V::BreakTy> {
+    fn visit_projection_ty(&mut self, projection: ty::AliasTy<'tcx>) -> V::Result {
         let tcx = self.def_id_visitor.tcx();
         let (trait_ref, assoc_args) = projection.trait_ref_and_own_args(tcx);
-        self.visit_trait(trait_ref)?;
+        try_visit!(self.visit_trait(trait_ref));
         if V::SHALLOW {
-            ControlFlow::Continue(())
+            V::Result::output()
         } else {
-            assoc_args.iter().try_for_each(|arg| arg.visit_with(self))
+            V::Result::from_branch(
+                assoc_args.iter().try_for_each(|arg| arg.visit_with(self).branch()),
+            )
         }
     }
 
-    fn visit_clause(&mut self, clause: ty::Clause<'tcx>) -> ControlFlow<V::BreakTy> {
+    fn visit_clause(&mut self, clause: ty::Clause<'tcx>) -> V::Result {
         match clause.kind().skip_binder() {
             ty::ClauseKind::Trait(ty::TraitPredicate { trait_ref, polarity: _ }) => {
                 self.visit_trait(trait_ref)
             }
             ty::ClauseKind::Projection(ty::ProjectionPredicate { projection_ty, term }) => {
-                term.visit_with(self)?;
+                try_visit!(term.visit_with(self));
                 self.visit_projection_ty(projection_ty)
             }
             ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty, _region)) => ty.visit_with(self),
-            ty::ClauseKind::RegionOutlives(..) => ControlFlow::Continue(()),
+            ty::ClauseKind::RegionOutlives(..) => V::Result::output(),
             ty::ClauseKind::ConstArgHasType(ct, ty) => {
-                ct.visit_with(self)?;
+                try_visit!(ct.visit_with(self));
                 ty.visit_with(self)
             }
             ty::ClauseKind::ConstEvaluatable(ct) => ct.visit_with(self),
@@ -157,8 +151,11 @@ where
         }
     }
 
-    fn visit_clauses(&mut self, clauses: &[(ty::Clause<'tcx>, Span)]) -> ControlFlow<V::BreakTy> {
-        clauses.into_iter().try_for_each(|&(clause, _span)| self.visit_clause(clause))
+    fn visit_clauses(&mut self, clauses: &[(ty::Clause<'tcx>, Span)]) -> V::Result {
+        for &(clause, _) in clauses {
+            try_visit!(self.visit_clause(clause));
+        }
+        V::Result::output()
     }
 }
 
@@ -166,13 +163,13 @@ impl<'tcx, V> TypeVisitor<TyCtxt<'tcx>> for DefIdVisitorSkeleton<'_, 'tcx, V>
 where
     V: DefIdVisitor<'tcx> + ?Sized,
 {
-    type BreakTy = V::BreakTy;
+    type Result = V::Result;
 
-    fn visit_predicate(&mut self, p: ty::Predicate<'tcx>) -> ControlFlow<Self::BreakTy> {
+    fn visit_predicate(&mut self, p: ty::Predicate<'tcx>) -> Self::Result {
         self.visit_clause(p.as_clause().unwrap())
     }
 
-    fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<V::BreakTy> {
+    fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
         let tcx = self.def_id_visitor.tcx();
         // GenericArgs are not visited here because they are visited below
         // in `super_visit_with`.
@@ -183,16 +180,16 @@ where
             | ty::Closure(def_id, ..)
             | ty::CoroutineClosure(def_id, ..)
             | ty::Coroutine(def_id, ..) => {
-                self.def_id_visitor.visit_def_id(def_id, "type", &ty)?;
+                try_visit!(self.def_id_visitor.visit_def_id(def_id, "type", &ty));
                 if V::SHALLOW {
-                    return ControlFlow::Continue(());
+                    return V::Result::output();
                 }
                 // Default type visitor doesn't visit signatures of fn types.
                 // Something like `fn() -> Priv {my_func}` is considered a private type even if
                 // `my_func` is public, so we need to visit signatures.
                 if let ty::FnDef(..) = ty.kind() {
                     // FIXME: this should probably use `args` from `FnDef`
-                    tcx.fn_sig(def_id).instantiate_identity().visit_with(self)?;
+                    try_visit!(tcx.fn_sig(def_id).instantiate_identity().visit_with(self));
                 }
                 // Inherent static methods don't have self type in args.
                 // Something like `fn() {my_method}` type of the method
@@ -200,7 +197,9 @@ where
                 // so we need to visit the self type additionally.
                 if let Some(assoc_item) = tcx.opt_associated_item(def_id) {
                     if let Some(impl_def_id) = assoc_item.impl_container(tcx) {
-                        tcx.type_of(impl_def_id).instantiate_identity().visit_with(self)?;
+                        try_visit!(
+                            tcx.type_of(impl_def_id).instantiate_identity().visit_with(self)
+                        );
                     }
                 }
             }
@@ -211,10 +210,10 @@ where
                     // as visible/reachable even if `Type` is private.
                     // Ideally, associated types should be instantiated in the same way as
                     // free type aliases, but this isn't done yet.
-                    return ControlFlow::Continue(());
+                    return V::Result::output();
                 }
 
-                self.def_id_visitor.visit_def_id(
+                try_visit!(self.def_id_visitor.visit_def_id(
                     data.def_id,
                     match kind {
                         ty::Inherent | ty::Projection => "associated type",
@@ -222,15 +221,17 @@ where
                         ty::Opaque => unreachable!(),
                     },
                     &LazyDefPathStr { def_id: data.def_id, tcx },
-                )?;
+                ));
 
                 // This will also visit args if necessary, so we don't need to recurse.
                 return if V::SHALLOW {
-                    ControlFlow::Continue(())
+                    V::Result::output()
                 } else if kind == ty::Projection {
                     self.visit_projection_ty(data)
                 } else {
-                    data.args.iter().try_for_each(|arg| arg.visit_with(self))
+                    V::Result::from_branch(
+                        data.args.iter().try_for_each(|arg| arg.visit_with(self).branch()),
+                    )
                 };
             }
             ty::Dynamic(predicates, ..) => {
@@ -245,7 +246,7 @@ where
                         }
                     };
                     let ty::ExistentialTraitRef { def_id, args: _ } = trait_ref;
-                    self.def_id_visitor.visit_def_id(def_id, "trait", &trait_ref)?;
+                    try_visit!(self.def_id_visitor.visit_def_id(def_id, "trait", &trait_ref));
                 }
             }
             ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) => {
@@ -258,7 +259,7 @@ where
                     // through the trait list (default type visitor doesn't visit those traits).
                     // All traits in the list are considered the "primary" part of the type
                     // and are visited by shallow visitors.
-                    self.visit_clauses(tcx.explicit_item_bounds(def_id).skip_binder())?;
+                    try_visit!(self.visit_clauses(tcx.explicit_item_bounds(def_id).skip_binder()));
                 }
             }
             // These types don't have their own def-ids (but may have subcomponents
@@ -285,10 +286,10 @@ where
             }
         }
 
-        if V::SHALLOW { ControlFlow::Continue(()) } else { ty.super_visit_with(self) }
+        if V::SHALLOW { V::Result::output() } else { ty.super_visit_with(self) }
     }
 
-    fn visit_const(&mut self, c: Const<'tcx>) -> ControlFlow<Self::BreakTy> {
+    fn visit_const(&mut self, c: Const<'tcx>) -> Self::Result {
         let tcx = self.def_id_visitor.tcx();
         tcx.expand_abstract_consts(c).super_visit_with(self)
     }
@@ -316,16 +317,10 @@ impl<'a, 'tcx, VL: VisibilityLike, const SHALLOW: bool> DefIdVisitor<'tcx>
     fn tcx(&self) -> TyCtxt<'tcx> {
         self.tcx
     }
-    fn visit_def_id(
-        &mut self,
-        def_id: DefId,
-        _kind: &str,
-        _descr: &dyn fmt::Display,
-    ) -> ControlFlow<Self::BreakTy> {
+    fn visit_def_id(&mut self, def_id: DefId, _kind: &str, _descr: &dyn fmt::Display) {
         if let Some(def_id) = def_id.as_local() {
             self.min = VL::new_min(self, def_id);
         }
-        ControlFlow::Continue(())
     }
 }
 
@@ -836,12 +831,7 @@ impl<'tcx> DefIdVisitor<'tcx> for ReachEverythingInTheInterfaceVisitor<'_, 'tcx>
     fn tcx(&self) -> TyCtxt<'tcx> {
         self.ev.tcx
     }
-    fn visit_def_id(
-        &mut self,
-        def_id: DefId,
-        _kind: &str,
-        _descr: &dyn fmt::Display,
-    ) -> ControlFlow<Self::BreakTy> {
+    fn visit_def_id(&mut self, def_id: DefId, _kind: &str, _descr: &dyn fmt::Display) {
         if let Some(def_id) = def_id.as_local() {
             // All effective visibilities except `reachable_through_impl_trait` are limited to
             // nominal visibility. If any type or trait is leaked farther than that, it will
@@ -850,7 +840,6 @@ impl<'tcx> DefIdVisitor<'tcx> for ReachEverythingInTheInterfaceVisitor<'_, 'tcx>
                 .then(|| self.ev.tcx.local_visibility(def_id));
             self.ev.update_eff_vis(def_id, self.effective_vis, max_vis, self.level);
         }
-        ControlFlow::Continue(())
     }
 }
 
@@ -1233,6 +1222,7 @@ impl<'tcx> Visitor<'tcx> for TypePrivacyVisitor<'tcx> {
 }
 
 impl<'tcx> DefIdVisitor<'tcx> for TypePrivacyVisitor<'tcx> {
+    type Result = ControlFlow<()>;
     fn tcx(&self) -> TyCtxt<'tcx> {
         self.tcx
     }
@@ -1241,7 +1231,7 @@ impl<'tcx> DefIdVisitor<'tcx> for TypePrivacyVisitor<'tcx> {
         def_id: DefId,
         kind: &str,
         descr: &dyn fmt::Display,
-    ) -> ControlFlow<Self::BreakTy> {
+    ) -> Self::Result {
         if self.check_def_id(def_id, kind, descr) {
             ControlFlow::Break(())
         } else {
@@ -1407,6 +1397,7 @@ impl SearchInterfaceForPrivateItemsVisitor<'_> {
 }
 
 impl<'tcx> DefIdVisitor<'tcx> for SearchInterfaceForPrivateItemsVisitor<'tcx> {
+    type Result = ControlFlow<()>;
     fn tcx(&self) -> TyCtxt<'tcx> {
         self.tcx
     }
@@ -1415,7 +1406,7 @@ impl<'tcx> DefIdVisitor<'tcx> for SearchInterfaceForPrivateItemsVisitor<'tcx> {
         def_id: DefId,
         kind: &str,
         descr: &dyn fmt::Display,
-    ) -> ControlFlow<Self::BreakTy> {
+    ) -> Self::Result {
         if self.check_def_id(def_id, kind, descr) {
             ControlFlow::Break(())
         } else {
diff --git a/compiler/rustc_trait_selection/Cargo.toml b/compiler/rustc_trait_selection/Cargo.toml
index 00ce9fbe758..3a58d41fcd0 100644
--- a/compiler/rustc_trait_selection/Cargo.toml
+++ b/compiler/rustc_trait_selection/Cargo.toml
@@ -8,6 +8,7 @@ edition = "2021"
 bitflags = "2.4.1"
 itertools = "0.11.0"
 rustc_ast = { path = "../rustc_ast" }
+rustc_ast_ir = { path = "../rustc_ast_ir" }
 rustc_attr = { path = "../rustc_attr" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_errors = { path = "../rustc_errors" }
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs
index 98d28f10a54..22f52c27362 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs
@@ -648,8 +648,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         }
 
         impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ContainsTermOrNotNameable<'_, 'tcx> {
-            type BreakTy = ();
-            fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+            type Result = ControlFlow<()>;
+            fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
                 match *t.kind() {
                     ty::Infer(ty::TyVar(vid)) => {
                         if let ty::TermKind::Ty(term) = self.term.unpack()
@@ -672,7 +672,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
                 }
             }
 
-            fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
+            fn visit_const(&mut self, c: ty::Const<'tcx>) -> Self::Result {
                 match c.kind() {
                     ty::ConstKind::Infer(ty::InferConst::Var(vid)) => {
                         if let ty::TermKind::Const(term) = self.term.unpack()
diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs
index 68111c4cc1f..20af016eb00 100644
--- a/compiler/rustc_trait_selection/src/traits/coherence.rs
+++ b/compiler/rustc_trait_selection/src/traits/coherence.rs
@@ -474,7 +474,7 @@ fn plug_infer_with_placeholders<'tcx>(
     }
 
     impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for PlugInferWithPlaceholder<'_, 'tcx> {
-        fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+        fn visit_ty(&mut self, ty: Ty<'tcx>) {
             let ty = self.infcx.shallow_resolve(ty);
             if ty.is_ty_var() {
                 let Ok(InferOk { value: (), obligations }) =
@@ -496,13 +496,12 @@ fn plug_infer_with_placeholders<'tcx>(
                     bug!("we always expect to be able to plug an infer var with placeholder")
                 };
                 assert_eq!(obligations, &[]);
-                ControlFlow::Continue(())
             } else {
-                ty.super_visit_with(self)
+                ty.super_visit_with(self);
             }
         }
 
-        fn visit_const(&mut self, ct: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
+        fn visit_const(&mut self, ct: ty::Const<'tcx>) {
             let ct = self.infcx.shallow_resolve(ct);
             if ct.is_ct_infer() {
                 let Ok(InferOk { value: (), obligations }) =
@@ -519,13 +518,12 @@ fn plug_infer_with_placeholders<'tcx>(
                     bug!("we always expect to be able to plug an infer var with placeholder")
                 };
                 assert_eq!(obligations, &[]);
-                ControlFlow::Continue(())
             } else {
-                ct.super_visit_with(self)
+                ct.super_visit_with(self);
             }
         }
 
-        fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
+        fn visit_region(&mut self, r: ty::Region<'tcx>) {
             if let ty::ReVar(vid) = *r {
                 let r = self
                     .infcx
@@ -555,7 +553,6 @@ fn plug_infer_with_placeholders<'tcx>(
                     assert_eq!(obligations, &[]);
                 }
             }
-            ControlFlow::Continue(())
         }
     }
 
@@ -868,12 +865,12 @@ impl<'tcx, F, E> TypeVisitor<TyCtxt<'tcx>> for OrphanChecker<'tcx, F>
 where
     F: FnMut(Ty<'tcx>) -> Result<Ty<'tcx>, E>,
 {
-    type BreakTy = OrphanCheckEarlyExit<'tcx, E>;
-    fn visit_region(&mut self, _r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
+    type Result = ControlFlow<OrphanCheckEarlyExit<'tcx, E>>;
+    fn visit_region(&mut self, _r: ty::Region<'tcx>) -> Self::Result {
         ControlFlow::Continue(())
     }
 
-    fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+    fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
         // Need to lazily normalize here in with `-Znext-solver=coherence`.
         let ty = match (self.lazily_normalize_ty)(ty) {
             Ok(ty) => ty,
@@ -996,7 +993,7 @@ where
     /// As these should be quite rare as const arguments and especially rare as impl
     /// parameters, allowing uncovered const parameters in impls seems more useful
     /// than allowing `impl<T> Trait<local_fn_ptr, T> for i32` to compile.
-    fn visit_const(&mut self, _c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
+    fn visit_const(&mut self, _c: ty::Const<'tcx>) -> Self::Result {
         ControlFlow::Continue(())
     }
 }
diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
index 189e1ba54bc..7f0f9a12d6a 100644
--- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
+++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
@@ -17,7 +17,6 @@ use rustc_middle::ty::abstract_const::NotConstEvaluatable;
 use rustc_middle::ty::{self, TyCtxt, TypeVisitable, TypeVisitableExt, TypeVisitor};
 
 use rustc_span::Span;
-use std::ops::ControlFlow;
 
 use crate::traits::ObligationCtxt;
 
@@ -170,8 +169,7 @@ fn satisfied_from_param_env<'tcx>(
     }
 
     impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for Visitor<'a, 'tcx> {
-        type BreakTy = ();
-        fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
+        fn visit_const(&mut self, c: ty::Const<'tcx>) {
             debug!("is_const_evaluatable: candidate={:?}", c);
             if self.infcx.probe(|_| {
                 let ocx = ObligationCtxt::new(self.infcx);
@@ -187,7 +185,7 @@ fn satisfied_from_param_env<'tcx>(
             }
 
             if let ty::ConstKind::Expr(e) = c.kind() {
-                e.visit_with(self)
+                e.visit_with(self);
             } else {
                 // FIXME(generic_const_exprs): This doesn't recurse into `<T as Trait<U>>::ASSOC`'s args.
                 // This is currently unobservable as `<T as Trait<{ U + 1 }>>::ASSOC` creates an anon const
@@ -196,7 +194,6 @@ fn satisfied_from_param_env<'tcx>(
                 // If we start allowing directly writing `ConstKind::Expr` without an intermediate anon const
                 // this will be incorrect. It might be worth investigating making `predicates_of` elaborate
                 // all of the `ConstEvaluatable` bounds rather than having a visitor here.
-                ControlFlow::Continue(())
             }
         }
     }
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
index 0796cb57d97..0515b09ae46 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -152,9 +152,9 @@ impl ArgKind {
 struct HasNumericInferVisitor;
 
 impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for HasNumericInferVisitor {
-    type BreakTy = ();
+    type Result = ControlFlow<()>;
 
-    fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+    fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
         if matches!(ty.kind(), ty::Infer(ty::FloatVar(_) | ty::IntVar(_))) {
             ControlFlow::Break(())
         } else {
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index 32447aca390..cc8b8f72cf3 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -435,8 +435,8 @@ fn is_impossible_associated_item(
         trait_item_def_id: DefId,
     }
     impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for ReferencesOnlyParentGenerics<'tcx> {
-        type BreakTy = ();
-        fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+        type Result = ControlFlow<()>;
+        fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
             // If this is a parameter from the trait item's own generics, then bail
             if let ty::Param(param) = t.kind()
                 && let param_def_id = self.generics.type_param(param, self.tcx).def_id
@@ -446,7 +446,7 @@ fn is_impossible_associated_item(
             }
             t.super_visit_with(self)
         }
-        fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
+        fn visit_region(&mut self, r: ty::Region<'tcx>) -> Self::Result {
             if let ty::ReEarlyParam(param) = r.kind()
                 && let param_def_id = self.generics.region_param(&param, self.tcx).def_id
                 && self.tcx.parent(param_def_id) == self.trait_item_def_id
@@ -455,7 +455,7 @@ fn is_impossible_associated_item(
             }
             ControlFlow::Continue(())
         }
-        fn visit_const(&mut self, ct: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
+        fn visit_const(&mut self, ct: ty::Const<'tcx>) -> Self::Result {
             if let ty::ConstKind::Param(param) = ct.kind()
                 && let param_def_id = self.generics.const_param(&param, self.tcx).def_id
                 && self.tcx.parent(param_def_id) == self.trait_item_def_id
diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs
index 7b715984c2b..1816b98a636 100644
--- a/compiler/rustc_trait_selection/src/traits/object_safety.rs
+++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs
@@ -834,9 +834,9 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>(
     }
 
     impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for IllegalSelfTypeVisitor<'tcx> {
-        type BreakTy = ();
+        type Result = ControlFlow<()>;
 
-        fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+        fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
             match t.kind() {
                 ty::Param(_) => {
                     if t == self.tcx.types.self_param {
@@ -887,7 +887,7 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>(
             }
         }
 
-        fn visit_const(&mut self, ct: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
+        fn visit_const(&mut self, ct: ty::Const<'tcx>) -> Self::Result {
             // Constants can only influence object safety if they are generic and reference `Self`.
             // This is only possible for unevaluated constants, so we walk these here.
             self.tcx.expand_abstract_consts(ct).super_visit_with(self)
diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
index e5f8e336860..c520e699bf5 100644
--- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
@@ -18,8 +18,6 @@ use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableEx
 use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitor};
 use rustc_span::DUMMY_SP;
 
-use std::ops::ControlFlow;
-
 use super::NoSolution;
 
 pub use rustc_middle::traits::query::NormalizationResult;
@@ -123,28 +121,23 @@ struct MaxEscapingBoundVarVisitor {
 }
 
 impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for MaxEscapingBoundVarVisitor {
-    fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
-        &mut self,
-        t: &ty::Binder<'tcx, T>,
-    ) -> ControlFlow<Self::BreakTy> {
+    fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(&mut self, t: &ty::Binder<'tcx, T>) {
         self.outer_index.shift_in(1);
-        let result = t.super_visit_with(self);
+        t.super_visit_with(self);
         self.outer_index.shift_out(1);
-        result
     }
 
     #[inline]
-    fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+    fn visit_ty(&mut self, t: Ty<'tcx>) {
         if t.outer_exclusive_binder() > self.outer_index {
             self.escaping = self
                 .escaping
                 .max(t.outer_exclusive_binder().as_usize() - self.outer_index.as_usize());
         }
-        ControlFlow::Continue(())
     }
 
     #[inline]
-    fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
+    fn visit_region(&mut self, r: ty::Region<'tcx>) {
         match *r {
             ty::ReBound(debruijn, _) if debruijn > self.outer_index => {
                 self.escaping =
@@ -152,16 +145,14 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for MaxEscapingBoundVarVisitor {
             }
             _ => {}
         }
-        ControlFlow::Continue(())
     }
 
-    fn visit_const(&mut self, ct: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
+    fn visit_const(&mut self, ct: ty::Const<'tcx>) {
         if ct.outer_exclusive_binder() > self.outer_index {
             self.escaping = self
                 .escaping
                 .max(ct.outer_exclusive_binder().as_usize() - self.outer_index.as_usize());
         }
-        ControlFlow::Continue(())
     }
 }
 
diff --git a/compiler/rustc_trait_selection/src/traits/structural_match.rs b/compiler/rustc_trait_selection/src/traits/structural_match.rs
index e6b42f15d51..b89406ca023 100644
--- a/compiler/rustc_trait_selection/src/traits/structural_match.rs
+++ b/compiler/rustc_trait_selection/src/traits/structural_match.rs
@@ -53,9 +53,9 @@ impl<'tcx> Search<'tcx> {
 }
 
 impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for Search<'tcx> {
-    type BreakTy = Ty<'tcx>;
+    type Result = ControlFlow<Ty<'tcx>>;
 
-    fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+    fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
         debug!("Search visiting ty: {:?}", ty);
 
         let (adt_def, args) = match *ty.kind() {
diff --git a/compiler/rustc_transmute/Cargo.toml b/compiler/rustc_transmute/Cargo.toml
index 066016231fa..3d0ad31747f 100644
--- a/compiler/rustc_transmute/Cargo.toml
+++ b/compiler/rustc_transmute/Cargo.toml
@@ -5,13 +5,14 @@ edition = "2021"
 
 [dependencies]
 # tidy-alphabetical-start
-rustc_data_structures = { path = "../rustc_data_structures"}
-rustc_hir = { path = "../rustc_hir", optional = true}
-rustc_infer = { path = "../rustc_infer", optional = true}
-rustc_macros = { path = "../rustc_macros", optional = true}
-rustc_middle = { path = "../rustc_middle", optional = true}
-rustc_span = { path = "../rustc_span", optional = true}
-rustc_target = { path = "../rustc_target", optional = true}
+rustc_ast_ir = { path = "../rustc_ast_ir", optional = true }
+rustc_data_structures = { path = "../rustc_data_structures" }
+rustc_hir = { path = "../rustc_hir", optional = true }
+rustc_infer = { path = "../rustc_infer", optional = true }
+rustc_macros = { path = "../rustc_macros", optional = true }
+rustc_middle = { path = "../rustc_middle", optional = true }
+rustc_span = { path = "../rustc_span", optional = true }
+rustc_target = { path = "../rustc_target", optional = true }
 tracing = "0.1"
 # tidy-alphabetical-end
 
@@ -23,6 +24,7 @@ rustc = [
     "rustc_middle",
     "rustc_span",
     "rustc_target",
+    "rustc_ast_ir",
 ]
 
 [dev-dependencies]
diff --git a/compiler/rustc_ty_utils/src/opaque_types.rs b/compiler/rustc_ty_utils/src/opaque_types.rs
index 28259516973..c69aba1c352 100644
--- a/compiler/rustc_ty_utils/src/opaque_types.rs
+++ b/compiler/rustc_ty_utils/src/opaque_types.rs
@@ -202,8 +202,8 @@ impl<'tcx> super::sig_types::SpannedTypeVisitor<'tcx> for OpaqueTypeCollector<'t
 
 impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OpaqueTypeCollector<'tcx> {
     #[instrument(skip(self), ret, level = "trace")]
-    fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<!> {
-        t.super_visit_with(self)?;
+    fn visit_ty(&mut self, t: Ty<'tcx>) {
+        t.super_visit_with(self);
         match t.kind() {
             ty::Alias(ty::Opaque, alias_ty) if alias_ty.def_id.is_local() => {
                 self.visit_opaque_ty(alias_ty);
@@ -212,7 +212,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OpaqueTypeCollector<'tcx> {
                 self.tcx
                     .type_of(alias_ty.def_id)
                     .instantiate(self.tcx, alias_ty.args)
-                    .visit_with(self)?;
+                    .visit_with(self);
             }
             ty::Alias(ty::Projection, alias_ty) => {
                 // This avoids having to do normalization of `Self::AssocTy` by only
@@ -244,11 +244,11 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OpaqueTypeCollector<'tcx> {
                             );
 
                             if check_args_compatible(self.tcx, assoc, impl_args) {
-                                return self
-                                    .tcx
+                                self.tcx
                                     .type_of(assoc.def_id)
                                     .instantiate(self.tcx, impl_args)
                                     .visit_with(self);
+                                return;
                             } else {
                                 self.tcx.dcx().span_delayed_bug(
                                     self.tcx.def_span(assoc.def_id),
@@ -261,10 +261,10 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OpaqueTypeCollector<'tcx> {
             }
             ty::Adt(def, _) if def.did().is_local() => {
                 if let CollectionMode::ImplTraitInAssocTypes = self.mode {
-                    return ControlFlow::Continue(());
+                    return;
                 }
                 if !self.seen.insert(def.did().expect_local()) {
-                    return ControlFlow::Continue(());
+                    return;
                 }
                 for variant in def.variants().iter() {
                     for field in variant.fields.iter() {
@@ -283,7 +283,6 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OpaqueTypeCollector<'tcx> {
             }
             _ => trace!(kind=?t.kind()),
         }
-        ControlFlow::Continue(())
     }
 }
 
diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs
index b725eda33ce..48d9a5e27b7 100644
--- a/compiler/rustc_ty_utils/src/ty.rs
+++ b/compiler/rustc_ty_utils/src/ty.rs
@@ -174,17 +174,13 @@ struct ImplTraitInTraitFinder<'a, 'tcx> {
 }
 
 impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ImplTraitInTraitFinder<'_, 'tcx> {
-    fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
-        &mut self,
-        binder: &ty::Binder<'tcx, T>,
-    ) -> std::ops::ControlFlow<Self::BreakTy> {
+    fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(&mut self, binder: &ty::Binder<'tcx, T>) {
         self.depth.shift_in(1);
-        let binder = binder.super_visit_with(self);
+        binder.super_visit_with(self);
         self.depth.shift_out(1);
-        binder
     }
 
-    fn visit_ty(&mut self, ty: Ty<'tcx>) -> std::ops::ControlFlow<Self::BreakTy> {
+    fn visit_ty(&mut self, ty: Ty<'tcx>) {
         if let ty::Alias(ty::Projection, unshifted_alias_ty) = *ty.kind()
             && let Some(
                 ty::ImplTraitInTraitData::Trait { fn_def_id, .. }
diff --git a/compiler/rustc_type_ir/src/canonical.rs b/compiler/rustc_type_ir/src/canonical.rs
index 5bc2bfe2893..ad18ef24984 100644
--- a/compiler/rustc_type_ir/src/canonical.rs
+++ b/compiler/rustc_type_ir/src/canonical.rs
@@ -1,6 +1,7 @@
+use rustc_ast_ir::try_visit;
+use rustc_ast_ir::visit::VisitorResult;
 use std::fmt;
 use std::hash::Hash;
-use std::ops::ControlFlow;
 
 use crate::fold::{FallibleTypeFolder, TypeFoldable};
 use crate::visit::{TypeVisitable, TypeVisitor};
@@ -107,9 +108,9 @@ impl<I: Interner, V: TypeVisitable<I>> TypeVisitable<I> for Canonical<I, V>
 where
     I::CanonicalVars: TypeVisitable<I>,
 {
-    fn visit_with<F: TypeVisitor<I>>(&self, folder: &mut F) -> ControlFlow<F::BreakTy> {
-        self.value.visit_with(folder)?;
-        self.max_universe.visit_with(folder)?;
+    fn visit_with<F: TypeVisitor<I>>(&self, folder: &mut F) -> F::Result {
+        try_visit!(self.value.visit_with(folder));
+        try_visit!(self.max_universe.visit_with(folder));
         self.variables.visit_with(folder)
     }
 }
@@ -137,7 +138,7 @@ impl<I: Interner> TypeVisitable<I> for CanonicalVarInfo<I>
 where
     I::Ty: TypeVisitable<I>,
 {
-    fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
+    fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> V::Result {
         self.kind.visit_with(visitor)
     }
 }
@@ -251,13 +252,13 @@ impl<I: Interner> TypeVisitable<I> for CanonicalVarKind<I>
 where
     I::Ty: TypeVisitable<I>,
 {
-    fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
+    fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> V::Result {
         match self {
             CanonicalVarKind::Ty(_)
             | CanonicalVarKind::PlaceholderTy(_)
             | CanonicalVarKind::Region(_)
             | CanonicalVarKind::PlaceholderRegion(_)
-            | CanonicalVarKind::Effect => ControlFlow::Continue(()),
+            | CanonicalVarKind::Effect => V::Result::output(),
             CanonicalVarKind::Const(_, ty) | CanonicalVarKind::PlaceholderConst(_, ty) => {
                 ty.visit_with(visitor)
             }
diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs
index 01bf79bce1c..2ded1b956e5 100644
--- a/compiler/rustc_type_ir/src/lib.rs
+++ b/compiler/rustc_type_ir/src/lib.rs
@@ -21,13 +21,15 @@ use std::hash::Hash;
 #[cfg(not(feature = "nightly"))]
 use std::sync::Arc as Lrc;
 
+#[macro_use]
+pub mod visit;
+
 #[cfg(feature = "nightly")]
 pub mod codec;
 pub mod fold;
 pub mod new;
 pub mod ty_info;
 pub mod ty_kind;
-pub mod visit;
 
 #[macro_use]
 mod macros;
diff --git a/compiler/rustc_type_ir/src/macros.rs b/compiler/rustc_type_ir/src/macros.rs
index 231546287d0..7dcc8851a43 100644
--- a/compiler/rustc_type_ir/src/macros.rs
+++ b/compiler/rustc_type_ir/src/macros.rs
@@ -25,9 +25,9 @@ macro_rules! TrivialTypeTraversalImpls {
                 fn visit_with<F: $crate::visit::TypeVisitor<I>>(
                     &self,
                     _: &mut F)
-                    -> ::std::ops::ControlFlow<F::BreakTy>
+                    -> F::Result
                 {
-                    ::std::ops::ControlFlow::Continue(())
+                    <F::Result as rustc_ast_ir::visit::VisitorResult>::output()
                 }
             }
         )+
diff --git a/compiler/rustc_type_ir/src/predicate_kind.rs b/compiler/rustc_type_ir/src/predicate_kind.rs
index 8c4d0fda6af..a0759f7df7f 100644
--- a/compiler/rustc_type_ir/src/predicate_kind.rs
+++ b/compiler/rustc_type_ir/src/predicate_kind.rs
@@ -1,5 +1,6 @@
+use rustc_ast_ir::try_visit;
+use rustc_ast_ir::visit::VisitorResult;
 use std::fmt;
-use std::ops::ControlFlow;
 
 use crate::fold::{FallibleTypeFolder, TypeFoldable};
 use crate::visit::{TypeVisitable, TypeVisitor};
@@ -91,14 +92,14 @@ where
     I::TypeOutlivesPredicate: TypeVisitable<I>,
     I::RegionOutlivesPredicate: TypeVisitable<I>,
 {
-    fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
+    fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> V::Result {
         match self {
             ClauseKind::Trait(p) => p.visit_with(visitor),
             ClauseKind::RegionOutlives(p) => p.visit_with(visitor),
             ClauseKind::TypeOutlives(p) => p.visit_with(visitor),
             ClauseKind::Projection(p) => p.visit_with(visitor),
             ClauseKind::ConstArgHasType(c, t) => {
-                c.visit_with(visitor)?;
+                try_visit!(c.visit_with(visitor));
                 t.visit_with(visitor)
             }
             ClauseKind::WellFormed(p) => p.visit_with(visitor),
@@ -205,21 +206,21 @@ where
     I::NormalizesTo: TypeVisitable<I>,
     ClauseKind<I>: TypeVisitable<I>,
 {
-    fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
+    fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> V::Result {
         match self {
             PredicateKind::Clause(p) => p.visit_with(visitor),
             PredicateKind::ObjectSafe(d) => d.visit_with(visitor),
             PredicateKind::Subtype(s) => s.visit_with(visitor),
             PredicateKind::Coerce(s) => s.visit_with(visitor),
             PredicateKind::ConstEquate(a, b) => {
-                a.visit_with(visitor)?;
+                try_visit!(a.visit_with(visitor));
                 b.visit_with(visitor)
             }
-            PredicateKind::Ambiguous => ControlFlow::Continue(()),
+            PredicateKind::Ambiguous => V::Result::output(),
             PredicateKind::NormalizesTo(p) => p.visit_with(visitor),
             PredicateKind::AliasRelate(a, b, d) => {
-                a.visit_with(visitor)?;
-                b.visit_with(visitor)?;
+                try_visit!(a.visit_with(visitor));
+                try_visit!(b.visit_with(visitor));
                 d.visit_with(visitor)
             }
         }
diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs
index 00f49a07f9d..de8f56618d0 100644
--- a/compiler/rustc_type_ir/src/ty_kind.rs
+++ b/compiler/rustc_type_ir/src/ty_kind.rs
@@ -1,3 +1,4 @@
+use rustc_ast_ir::try_visit;
 #[cfg(feature = "nightly")]
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 #[cfg(feature = "nightly")]
@@ -831,8 +832,8 @@ impl<I: Interner> TypeVisitable<I> for TypeAndMut<I>
 where
     I::Ty: TypeVisitable<I>,
 {
-    fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> std::ops::ControlFlow<V::BreakTy> {
-        self.ty.visit_with(visitor)?;
+    fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> V::Result {
+        try_visit!(self.ty.visit_with(visitor));
         self.mutbl.visit_with(visitor)
     }
 }
diff --git a/compiler/rustc_type_ir/src/visit.rs b/compiler/rustc_type_ir/src/visit.rs
index 638fb9f7fa9..839e75dba4c 100644
--- a/compiler/rustc_type_ir/src/visit.rs
+++ b/compiler/rustc_type_ir/src/visit.rs
@@ -41,6 +41,8 @@
 //! - u.visit_with(visitor)
 //! ```
 
+use rustc_ast_ir::visit::VisitorResult;
+use rustc_ast_ir::{try_visit, walk_visitable_list};
 use rustc_index::{Idx, IndexVec};
 use std::fmt;
 use std::ops::ControlFlow;
@@ -63,7 +65,7 @@ pub trait TypeVisitable<I: Interner>: fmt::Debug + Clone {
     /// that calls a visitor method specifically for that type (such as
     /// `V::visit_ty`). This is where control transfers from `TypeVisitable` to
     /// `TypeVisitor`.
-    fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy>;
+    fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> V::Result;
 }
 
 // This trait is implemented for types of interest.
@@ -74,7 +76,7 @@ pub trait TypeSuperVisitable<I: Interner>: TypeVisitable<I> {
     /// that method. For example, in `MyVisitor::visit_ty(ty)`, it is valid to
     /// call `ty.super_visit_with(self)`, but any other visiting should be done
     /// with `xyz.visit_with(self)`.
-    fn super_visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy>;
+    fn super_visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> V::Result;
 }
 
 /// This trait is implemented for every visiting traversal. There is a visit
@@ -82,33 +84,30 @@ pub trait TypeSuperVisitable<I: Interner>: TypeVisitable<I> {
 /// that recurses into the type's fields in a non-custom fashion.
 pub trait TypeVisitor<I: Interner>: Sized {
     #[cfg(feature = "nightly")]
-    type BreakTy = !;
+    type Result: VisitorResult = ();
 
     #[cfg(not(feature = "nightly"))]
-    type BreakTy;
+    type Result: VisitorResult;
 
-    fn visit_binder<T: TypeVisitable<I>>(
-        &mut self,
-        t: &I::Binder<T>,
-    ) -> ControlFlow<Self::BreakTy> {
+    fn visit_binder<T: TypeVisitable<I>>(&mut self, t: &I::Binder<T>) -> Self::Result {
         t.super_visit_with(self)
     }
 
-    fn visit_ty(&mut self, t: I::Ty) -> ControlFlow<Self::BreakTy> {
+    fn visit_ty(&mut self, t: I::Ty) -> Self::Result {
         t.super_visit_with(self)
     }
 
     // The default region visitor is a no-op because `Region` is non-recursive
     // and has no `super_visit_with` method to call.
-    fn visit_region(&mut self, _r: I::Region) -> ControlFlow<Self::BreakTy> {
-        ControlFlow::Continue(())
+    fn visit_region(&mut self, _r: I::Region) -> Self::Result {
+        Self::Result::output()
     }
 
-    fn visit_const(&mut self, c: I::Const) -> ControlFlow<Self::BreakTy> {
+    fn visit_const(&mut self, c: I::Const) -> Self::Result {
         c.super_visit_with(self)
     }
 
-    fn visit_predicate(&mut self, p: I::Predicate) -> ControlFlow<Self::BreakTy> {
+    fn visit_predicate(&mut self, p: I::Predicate) -> Self::Result {
         p.super_visit_with(self)
     }
 }
@@ -117,8 +116,8 @@ pub trait TypeVisitor<I: Interner>: Sized {
 // Traversal implementations.
 
 impl<I: Interner, T: TypeVisitable<I>, U: TypeVisitable<I>> TypeVisitable<I> for (T, U) {
-    fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
-        self.0.visit_with(visitor)?;
+    fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> V::Result {
+        try_visit!(self.0.visit_with(visitor));
         self.1.visit_with(visitor)
     }
 }
@@ -126,24 +125,24 @@ impl<I: Interner, T: TypeVisitable<I>, U: TypeVisitable<I>> TypeVisitable<I> for
 impl<I: Interner, A: TypeVisitable<I>, B: TypeVisitable<I>, C: TypeVisitable<I>> TypeVisitable<I>
     for (A, B, C)
 {
-    fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
-        self.0.visit_with(visitor)?;
-        self.1.visit_with(visitor)?;
+    fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> V::Result {
+        try_visit!(self.0.visit_with(visitor));
+        try_visit!(self.1.visit_with(visitor));
         self.2.visit_with(visitor)
     }
 }
 
 impl<I: Interner, T: TypeVisitable<I>> TypeVisitable<I> for Option<T> {
-    fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
+    fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> V::Result {
         match self {
             Some(v) => v.visit_with(visitor),
-            None => ControlFlow::Continue(()),
+            None => V::Result::output(),
         }
     }
 }
 
 impl<I: Interner, T: TypeVisitable<I>, E: TypeVisitable<I>> TypeVisitable<I> for Result<T, E> {
-    fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
+    fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> V::Result {
         match self {
             Ok(v) => v.visit_with(visitor),
             Err(e) => e.visit_with(visitor),
@@ -152,20 +151,21 @@ impl<I: Interner, T: TypeVisitable<I>, E: TypeVisitable<I>> TypeVisitable<I> for
 }
 
 impl<I: Interner, T: TypeVisitable<I>> TypeVisitable<I> for Lrc<T> {
-    fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
+    fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> V::Result {
         (**self).visit_with(visitor)
     }
 }
 
 impl<I: Interner, T: TypeVisitable<I>> TypeVisitable<I> for Box<T> {
-    fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
+    fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> V::Result {
         (**self).visit_with(visitor)
     }
 }
 
 impl<I: Interner, T: TypeVisitable<I>> TypeVisitable<I> for Vec<T> {
-    fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
-        self.iter().try_for_each(|t| t.visit_with(visitor))
+    fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> V::Result {
+        walk_visitable_list!(visitor, self.iter());
+        V::Result::output()
     }
 }
 
@@ -173,20 +173,23 @@ impl<I: Interner, T: TypeVisitable<I>> TypeVisitable<I> for Vec<T> {
 // case, because we can't return a new slice. But note that there are a couple
 // of trivial impls of `TypeFoldable` for specific slice types elsewhere.
 impl<I: Interner, T: TypeVisitable<I>> TypeVisitable<I> for &[T] {
-    fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
-        self.iter().try_for_each(|t| t.visit_with(visitor))
+    fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> V::Result {
+        walk_visitable_list!(visitor, self.iter());
+        V::Result::output()
     }
 }
 
 impl<I: Interner, T: TypeVisitable<I>> TypeVisitable<I> for Box<[T]> {
-    fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
-        self.iter().try_for_each(|t| t.visit_with(visitor))
+    fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> V::Result {
+        walk_visitable_list!(visitor, self.iter());
+        V::Result::output()
     }
 }
 
 impl<I: Interner, T: TypeVisitable<I>, Ix: Idx> TypeVisitable<I> for IndexVec<Ix, T> {
-    fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
-        self.iter().try_for_each(|t| t.visit_with(visitor))
+    fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> V::Result {
+        walk_visitable_list!(visitor, self.iter());
+        V::Result::output()
     }
 }
 
@@ -366,12 +369,9 @@ impl std::fmt::Debug for HasTypeFlagsVisitor {
 // is important for anonymization of binders in `TyCtxt::erase_regions`. We
 // specifically detect this case in `visit_binder`.
 impl<I: Interner> TypeVisitor<I> for HasTypeFlagsVisitor {
-    type BreakTy = FoundFlags;
+    type Result = ControlFlow<FoundFlags>;
 
-    fn visit_binder<T: TypeVisitable<I>>(
-        &mut self,
-        t: &I::Binder<T>,
-    ) -> ControlFlow<Self::BreakTy> {
+    fn visit_binder<T: TypeVisitable<I>>(&mut self, t: &I::Binder<T>) -> Self::Result {
         // If we're looking for the HAS_BINDER_VARS flag, check if the
         // binder has vars. This won't be present in the binder's bound
         // value, so we need to check here too.
@@ -383,7 +383,7 @@ impl<I: Interner> TypeVisitor<I> for HasTypeFlagsVisitor {
     }
 
     #[inline]
-    fn visit_ty(&mut self, t: I::Ty) -> ControlFlow<Self::BreakTy> {
+    fn visit_ty(&mut self, t: I::Ty) -> Self::Result {
         // Note: no `super_visit_with` call.
         let flags = t.flags();
         if flags.intersects(self.flags) {
@@ -394,7 +394,7 @@ impl<I: Interner> TypeVisitor<I> for HasTypeFlagsVisitor {
     }
 
     #[inline]
-    fn visit_region(&mut self, r: I::Region) -> ControlFlow<Self::BreakTy> {
+    fn visit_region(&mut self, r: I::Region) -> Self::Result {
         // Note: no `super_visit_with` call, as usual for `Region`.
         let flags = r.flags();
         if flags.intersects(self.flags) {
@@ -405,7 +405,7 @@ impl<I: Interner> TypeVisitor<I> for HasTypeFlagsVisitor {
     }
 
     #[inline]
-    fn visit_const(&mut self, c: I::Const) -> ControlFlow<Self::BreakTy> {
+    fn visit_const(&mut self, c: I::Const) -> Self::Result {
         // Note: no `super_visit_with` call.
         if c.flags().intersects(self.flags) {
             ControlFlow::Break(FoundFlags)
@@ -415,7 +415,7 @@ impl<I: Interner> TypeVisitor<I> for HasTypeFlagsVisitor {
     }
 
     #[inline]
-    fn visit_predicate(&mut self, predicate: I::Predicate) -> ControlFlow<Self::BreakTy> {
+    fn visit_predicate(&mut self, predicate: I::Predicate) -> Self::Result {
         // Note: no `super_visit_with` call.
         if predicate.flags().intersects(self.flags) {
             ControlFlow::Break(FoundFlags)
@@ -459,12 +459,9 @@ struct HasEscapingVarsVisitor {
 }
 
 impl<I: Interner> TypeVisitor<I> for HasEscapingVarsVisitor {
-    type BreakTy = FoundEscapingVars;
+    type Result = ControlFlow<FoundEscapingVars>;
 
-    fn visit_binder<T: TypeVisitable<I>>(
-        &mut self,
-        t: &I::Binder<T>,
-    ) -> ControlFlow<Self::BreakTy> {
+    fn visit_binder<T: TypeVisitable<I>>(&mut self, t: &I::Binder<T>) -> Self::Result {
         self.outer_index.shift_in(1);
         let result = t.super_visit_with(self);
         self.outer_index.shift_out(1);
@@ -472,7 +469,7 @@ impl<I: Interner> TypeVisitor<I> for HasEscapingVarsVisitor {
     }
 
     #[inline]
-    fn visit_ty(&mut self, t: I::Ty) -> ControlFlow<Self::BreakTy> {
+    fn visit_ty(&mut self, t: I::Ty) -> Self::Result {
         // If the outer-exclusive-binder is *strictly greater* than
         // `outer_index`, that means that `t` contains some content
         // bound at `outer_index` or above (because
@@ -486,7 +483,7 @@ impl<I: Interner> TypeVisitor<I> for HasEscapingVarsVisitor {
     }
 
     #[inline]
-    fn visit_region(&mut self, r: I::Region) -> ControlFlow<Self::BreakTy> {
+    fn visit_region(&mut self, r: I::Region) -> Self::Result {
         // If the region is bound by `outer_index` or anything outside
         // of outer index, then it escapes the binders we have
         // visited.
@@ -497,7 +494,7 @@ impl<I: Interner> TypeVisitor<I> for HasEscapingVarsVisitor {
         }
     }
 
-    fn visit_const(&mut self, ct: I::Const) -> ControlFlow<Self::BreakTy> {
+    fn visit_const(&mut self, ct: I::Const) -> Self::Result {
         // If the outer-exclusive-binder is *strictly greater* than
         // `outer_index`, that means that `ct` contains some content
         // bound at `outer_index` or above (because
@@ -511,7 +508,7 @@ impl<I: Interner> TypeVisitor<I> for HasEscapingVarsVisitor {
     }
 
     #[inline]
-    fn visit_predicate(&mut self, predicate: I::Predicate) -> ControlFlow<Self::BreakTy> {
+    fn visit_predicate(&mut self, predicate: I::Predicate) -> Self::Result {
         if predicate.outer_exclusive_binder() > self.outer_index {
             ControlFlow::Break(FoundEscapingVars)
         } else {
@@ -523,9 +520,9 @@ impl<I: Interner> TypeVisitor<I> for HasEscapingVarsVisitor {
 struct HasErrorVisitor;
 
 impl<I: Interner> TypeVisitor<I> for HasErrorVisitor {
-    type BreakTy = I::ErrorGuaranteed;
+    type Result = ControlFlow<I::ErrorGuaranteed>;
 
-    fn visit_ty(&mut self, t: <I as Interner>::Ty) -> ControlFlow<Self::BreakTy> {
+    fn visit_ty(&mut self, t: <I as Interner>::Ty) -> Self::Result {
         if let ty::Error(guar) = t.kind() {
             ControlFlow::Break(guar)
         } else {
@@ -533,7 +530,7 @@ impl<I: Interner> TypeVisitor<I> for HasErrorVisitor {
         }
     }
 
-    fn visit_const(&mut self, c: <I as Interner>::Const) -> ControlFlow<Self::BreakTy> {
+    fn visit_const(&mut self, c: <I as Interner>::Const) -> Self::Result {
         if let ty::ConstKind::Error(guar) = c.kind() {
             ControlFlow::Break(guar)
         } else {
@@ -541,7 +538,7 @@ impl<I: Interner> TypeVisitor<I> for HasErrorVisitor {
         }
     }
 
-    fn visit_region(&mut self, r: <I as Interner>::Region) -> ControlFlow<Self::BreakTy> {
+    fn visit_region(&mut self, r: <I as Interner>::Region) -> Self::Result {
         if let ty::ReError(guar) = r.kind() {
             ControlFlow::Break(guar)
         } else {
diff --git a/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs b/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs
index 514015af045..b8baad18cc8 100644
--- a/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs
@@ -22,9 +22,9 @@ fn get_enum_ty(enum_ty: Ty<'_>) -> Option<Ty<'_>> {
     }
 
     impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ContainsTyVisitor {
-        type BreakTy = Ty<'tcx>;
+        type Result = ControlFlow<Ty<'tcx>>;
 
-        fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+        fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
             self.level += 1;
             if self.level == 1 {
                 t.super_visit_with(self)
diff --git a/src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs b/src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs
index f9cc5f19125..06229ac938f 100644
--- a/src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs
+++ b/src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs
@@ -141,9 +141,9 @@ impl<'a, 'b, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'b,
 struct ContainsRegion;
 
 impl TypeVisitor<TyCtxt<'_>> for ContainsRegion {
-    type BreakTy = ();
+    type Result = ControlFlow<()>;
 
-    fn visit_region(&mut self, _: ty::Region<'_>) -> ControlFlow<Self::BreakTy> {
+    fn visit_region(&mut self, _: ty::Region<'_>) -> Self::Result {
         ControlFlow::Break(())
     }
 }
diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs
index 6762c883005..6e011a28bb7 100644
--- a/src/tools/clippy/clippy_utils/src/ty.rs
+++ b/src/tools/clippy/clippy_utils/src/ty.rs
@@ -916,8 +916,8 @@ pub fn for_each_top_level_late_bound_region<B>(
         f: F,
     }
     impl<'tcx, B, F: FnMut(BoundRegion) -> ControlFlow<B>> TypeVisitor<TyCtxt<'tcx>> for V<F> {
-        type BreakTy = B;
-        fn visit_region(&mut self, r: Region<'tcx>) -> ControlFlow<Self::BreakTy> {
+        type Result = ControlFlow<B>;
+        fn visit_region(&mut self, r: Region<'tcx>) -> Self::Result {
             if let RegionKind::ReBound(idx, bound) = r.kind()
                 && idx.as_u32() == self.index
             {
@@ -926,7 +926,7 @@ pub fn for_each_top_level_late_bound_region<B>(
                 ControlFlow::Continue(())
             }
         }
-        fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(&mut self, t: &Binder<'tcx, T>) -> ControlFlow<Self::BreakTy> {
+        fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(&mut self, t: &Binder<'tcx, T>) -> Self::Result {
             self.index += 1;
             let res = t.super_visit_with(self);
             self.index -= 1;