about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-07-06 20:26:50 +0000
committerbors <bors@rust-lang.org>2024-07-06 20:26:50 +0000
commited7e35f3494045fa1194be29085fa73e2d6dab40 (patch)
tree5dbd26e58a1f27dd18670cd1d22fa0882e6fe2bb
parent8a8ad3433e4168f54ac1364f85da694be9eeca7c (diff)
parent413345c61dc0bf29e259d2cfd50daaaeab008ea8 (diff)
downloadrust-ed7e35f3494045fa1194be29085fa73e2d6dab40.tar.gz
rust-ed7e35f3494045fa1194be29085fa73e2d6dab40.zip
Auto merge of #127430 - compiler-errors:rollup-76ni16s, r=compiler-errors
Rollup of 4 pull requests

Successful merges:

 - #127386 (Uplift outlives components to `rustc_type_ir`)
 - #127405 (uplift `PredicateEmittingRelation`)
 - #127410 (Correct description of E0502)
 - #127417 (Show fnsig's unit output  explicitly when there is output diff in diagnostics)

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--Cargo.lock1
-rw-r--r--compiler/rustc_borrowck/src/type_check/relate_tys.rs9
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0502.md5
-rw-r--r--compiler/rustc_hir_analysis/src/outlives/utils.rs2
-rw-r--r--compiler/rustc_infer/Cargo.toml1
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/mod.rs6
-rw-r--r--compiler/rustc_infer/src/infer/outlives/components.rs266
-rw-r--r--compiler/rustc_infer/src/infer/outlives/mod.rs1
-rw-r--r--compiler/rustc_infer/src/infer/outlives/obligations.rs4
-rw-r--r--compiler/rustc_infer/src/infer/outlives/verify.rs6
-rw-r--r--compiler/rustc_infer/src/infer/relate/combine.rs36
-rw-r--r--compiler/rustc_infer/src/infer/relate/generalize.rs4
-rw-r--r--compiler/rustc_infer/src/infer/relate/glb.rs2
-rw-r--r--compiler/rustc_infer/src/infer/relate/lattice.rs2
-rw-r--r--compiler/rustc_infer/src/infer/relate/lub.rs2
-rw-r--r--compiler/rustc_infer/src/infer/relate/mod.rs4
-rw-r--r--compiler/rustc_infer/src/infer/relate/type_relating.rs4
-rw-r--r--compiler/rustc_infer/src/traits/util.rs2
-rw-r--r--compiler/rustc_middle/src/ty/consts/kind.rs7
-rw-r--r--compiler/rustc_middle/src/ty/context.rs2
-rw-r--r--compiler/rustc_middle/src/ty/relate.rs12
-rw-r--r--compiler/rustc_middle/src/ty/walk.rs17
-rw-r--r--compiler/rustc_next_trait_solver/src/lib.rs1
-rw-r--r--compiler/rustc_next_trait_solver/src/relate.rs15
-rw-r--r--compiler/rustc_next_trait_solver/src/relate/combine.rs34
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs4
-rw-r--r--compiler/rustc_type_ir/src/inherent.rs8
-rw-r--r--compiler/rustc_type_ir/src/interner.rs3
-rw-r--r--compiler/rustc_type_ir/src/lib.rs1
-rw-r--r--compiler/rustc_type_ir/src/outlives.rs335
-rw-r--r--tests/ui/async-await/in-trait/async-generics-and-bounds.stderr12
-rw-r--r--tests/ui/async-await/in-trait/async-generics.stderr12
-rw-r--r--tests/ui/compare-method/bad-self-type.stderr2
-rw-r--r--tests/ui/error-codes/E0308.stderr2
-rw-r--r--tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.stderr4
-rw-r--r--tests/ui/fn/fn_def_opaque_coercion_to_fn_ptr.stderr4
-rw-r--r--tests/ui/impl-trait/in-assoc-type-unconstrained.stderr2
-rw-r--r--tests/ui/impl-trait/trait_type.stderr2
-rw-r--r--tests/ui/lang-items/start_lang_item_args.missing_ret.stderr2
-rw-r--r--tests/ui/method-output-diff-issue-127263.rs8
-rw-r--r--tests/ui/method-output-diff-issue-127263.stderr25
-rw-r--r--tests/ui/panic-handler/panic-handler-bad-signature-1.stderr2
-rw-r--r--tests/ui/traits/impl-method-mismatch.stderr2
43 files changed, 499 insertions, 376 deletions
diff --git a/Cargo.lock b/Cargo.lock
index d1889fb6861..afeb9faec09 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4168,6 +4168,7 @@ dependencies = [
  "rustc_index",
  "rustc_macros",
  "rustc_middle",
+ "rustc_next_trait_solver",
  "rustc_span",
  "rustc_target",
  "rustc_type_ir",
diff --git a/compiler/rustc_borrowck/src/type_check/relate_tys.rs b/compiler/rustc_borrowck/src/type_check/relate_tys.rs
index b9a82046e59..02b9c2d48b1 100644
--- a/compiler/rustc_borrowck/src/type_check/relate_tys.rs
+++ b/compiler/rustc_borrowck/src/type_check/relate_tys.rs
@@ -1,8 +1,9 @@
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::ErrorGuaranteed;
-use rustc_infer::infer::relate::{PredicateEmittingRelation, StructurallyRelateAliases};
-use rustc_infer::infer::relate::{Relate, RelateResult, TypeRelation};
-use rustc_infer::infer::NllRegionVariableOrigin;
+use rustc_infer::infer::relate::{
+    PredicateEmittingRelation, Relate, RelateResult, StructurallyRelateAliases, TypeRelation,
+};
+use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin};
 use rustc_infer::traits::solve::Goal;
 use rustc_infer::traits::Obligation;
 use rustc_middle::mir::ConstraintCategory;
@@ -522,7 +523,7 @@ impl<'bccx, 'tcx> TypeRelation<TyCtxt<'tcx>> for NllTypeRelating<'_, 'bccx, 'tcx
     }
 }
 
-impl<'bccx, 'tcx> PredicateEmittingRelation<'tcx> for NllTypeRelating<'_, 'bccx, 'tcx> {
+impl<'bccx, 'tcx> PredicateEmittingRelation<InferCtxt<'tcx>> for NllTypeRelating<'_, 'bccx, 'tcx> {
     fn span(&self) -> Span {
         self.locations.span(self.type_checker.body)
     }
diff --git a/compiler/rustc_error_codes/src/error_codes/E0502.md b/compiler/rustc_error_codes/src/error_codes/E0502.md
index dc3ffdfddd9..85f38b9286f 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0502.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0502.md
@@ -1,4 +1,5 @@
-A variable already borrowed as immutable was borrowed as mutable.
+A variable already borrowed with a certain mutability (either mutable or
+immutable) was borrowed again with a different mutability.
 
 Erroneous code example:
 
@@ -13,7 +14,7 @@ fn foo(a: &mut i32) {
 ```
 
 To fix this error, ensure that you don't have any other references to the
-variable before trying to access it mutably:
+variable before trying to access it with a different mutability:
 
 ```
 fn bar(x: &mut i32) {}
diff --git a/compiler/rustc_hir_analysis/src/outlives/utils.rs b/compiler/rustc_hir_analysis/src/outlives/utils.rs
index 5086c2af3f6..08015c28a26 100644
--- a/compiler/rustc_hir_analysis/src/outlives/utils.rs
+++ b/compiler/rustc_hir_analysis/src/outlives/utils.rs
@@ -1,9 +1,9 @@
 use rustc_data_structures::fx::FxIndexMap;
-use rustc_infer::infer::outlives::components::{push_outlives_components, Component};
 use rustc_middle::ty::{self, Region, Ty, TyCtxt};
 use rustc_middle::ty::{GenericArg, GenericArgKind};
 use rustc_middle::{bug, span_bug};
 use rustc_span::Span;
+use rustc_type_ir::outlives::{push_outlives_components, Component};
 use smallvec::smallvec;
 
 /// Tracks the `T: 'a` or `'a: 'a` predicates that we have inferred
diff --git a/compiler/rustc_infer/Cargo.toml b/compiler/rustc_infer/Cargo.toml
index 5136ab79a0f..1f616710200 100644
--- a/compiler/rustc_infer/Cargo.toml
+++ b/compiler/rustc_infer/Cargo.toml
@@ -16,6 +16,7 @@ rustc_hir = { path = "../rustc_hir" }
 rustc_index = { path = "../rustc_index" }
 rustc_macros = { path = "../rustc_macros" }
 rustc_middle = { path = "../rustc_middle" }
+rustc_next_trait_solver = { path = "../rustc_next_trait_solver" }
 rustc_span = { path = "../rustc_span" }
 rustc_target = { path = "../rustc_target" }
 rustc_type_ir = { path = "../rustc_type_ir" }
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index a8fd3ca8c59..cebb9d09a47 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -1168,14 +1168,16 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         let output1 = sig1.output();
         let output2 = sig2.output();
         let (x1, x2) = self.cmp(output1, output2);
-        if !output1.is_unit() {
+        let output_diff = x1 != x2;
+        if !output1.is_unit() || output_diff {
             values.0.push_normal(" -> ");
             (values.0).0.extend(x1.0);
         }
-        if !output2.is_unit() {
+        if !output2.is_unit() || output_diff {
             values.1.push_normal(" -> ");
             (values.1).0.extend(x2.0);
         }
+
         values
     }
 
diff --git a/compiler/rustc_infer/src/infer/outlives/components.rs b/compiler/rustc_infer/src/infer/outlives/components.rs
deleted file mode 100644
index 6bab3ad6ba3..00000000000
--- a/compiler/rustc_infer/src/infer/outlives/components.rs
+++ /dev/null
@@ -1,266 +0,0 @@
-// The outlines relation `T: 'a` or `'a: 'b`. This code frequently
-// refers to rules defined in RFC 1214 (`OutlivesFooBar`), so see that
-// RFC for reference.
-
-use rustc_data_structures::sso::SsoHashSet;
-use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
-use rustc_middle::ty::{GenericArg, GenericArgKind};
-use smallvec::{smallvec, SmallVec};
-
-#[derive(Debug)]
-pub enum Component<'tcx> {
-    Region(ty::Region<'tcx>),
-    Param(ty::ParamTy),
-    Placeholder(ty::PlaceholderType),
-    UnresolvedInferenceVariable(ty::InferTy),
-
-    // Projections like `T::Foo` are tricky because a constraint like
-    // `T::Foo: 'a` can be satisfied in so many ways. There may be a
-    // where-clause that says `T::Foo: 'a`, or the defining trait may
-    // include a bound like `type Foo: 'static`, or -- in the most
-    // conservative way -- we can prove that `T: 'a` (more generally,
-    // that all components in the projection outlive `'a`). This code
-    // is not in a position to judge which is the best technique, so
-    // we just product the projection as a component and leave it to
-    // the consumer to decide (but see `EscapingProjection` below).
-    Alias(ty::AliasTy<'tcx>),
-
-    // In the case where a projection has escaping regions -- meaning
-    // regions bound within the type itself -- we always use
-    // the most conservative rule, which requires that all components
-    // outlive the bound. So for example if we had a type like this:
-    //
-    //     for<'a> Trait1<  <T as Trait2<'a,'b>>::Foo  >
-    //                      ~~~~~~~~~~~~~~~~~~~~~~~~~
-    //
-    // then the inner projection (underlined) has an escaping region
-    // `'a`. We consider that outer trait `'c` to meet a bound if `'b`
-    // outlives `'b: 'c`, and we don't consider whether the trait
-    // declares that `Foo: 'static` etc. Therefore, we just return the
-    // free components of such a projection (in this case, `'b`).
-    //
-    // However, in the future, we may want to get smarter, and
-    // actually return a "higher-ranked projection" here. Therefore,
-    // we mark that these components are part of an escaping
-    // projection, so that implied bounds code can avoid relying on
-    // them. This gives us room to improve the regionck reasoning in
-    // the future without breaking backwards compat.
-    EscapingAlias(Vec<Component<'tcx>>),
-}
-
-/// Push onto `out` all the things that must outlive `'a` for the condition
-/// `ty0: 'a` to hold. Note that `ty0` must be a **fully resolved type**.
-pub fn push_outlives_components<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    ty0: Ty<'tcx>,
-    out: &mut SmallVec<[Component<'tcx>; 4]>,
-) {
-    let mut visited = SsoHashSet::new();
-    compute_components(tcx, ty0, out, &mut visited);
-    debug!("components({:?}) = {:?}", ty0, out);
-}
-
-fn compute_components<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    ty: Ty<'tcx>,
-    out: &mut SmallVec<[Component<'tcx>; 4]>,
-    visited: &mut SsoHashSet<GenericArg<'tcx>>,
-) {
-    // Descend through the types, looking for the various "base"
-    // components and collecting them into `out`. This is not written
-    // with `collect()` because of the need to sometimes skip subtrees
-    // in the `subtys` iterator (e.g., when encountering a
-    // projection).
-    match *ty.kind() {
-            ty::FnDef(_, args) => {
-                // HACK(eddyb) ignore lifetimes found shallowly in `args`.
-                // This is inconsistent with `ty::Adt` (including all args)
-                // and with `ty::Closure` (ignoring all args other than
-                // upvars, of which a `ty::FnDef` doesn't have any), but
-                // consistent with previous (accidental) behavior.
-                // See https://github.com/rust-lang/rust/issues/70917
-                // for further background and discussion.
-                for child in args {
-                    match child.unpack() {
-                        GenericArgKind::Type(ty) => {
-                            compute_components(tcx, ty, out, visited);
-                        }
-                        GenericArgKind::Lifetime(_) => {}
-                        GenericArgKind::Const(_) => {
-                            compute_components_recursive(tcx, child, out, visited);
-                        }
-                    }
-                }
-            }
-
-            ty::Pat(element, _) |
-            ty::Array(element, _) => {
-                // Don't look into the len const as it doesn't affect regions
-                compute_components(tcx, element, out, visited);
-            }
-
-            ty::Closure(_, args) => {
-                let tupled_ty = args.as_closure().tupled_upvars_ty();
-                compute_components(tcx, tupled_ty, out, visited);
-            }
-
-            ty::CoroutineClosure(_, args) => {
-                let tupled_ty = args.as_coroutine_closure().tupled_upvars_ty();
-                compute_components(tcx, tupled_ty, out, visited);
-            }
-
-            ty::Coroutine(_, args) => {
-                // Same as the closure case
-                let tupled_ty = args.as_coroutine().tupled_upvars_ty();
-                compute_components(tcx, tupled_ty, out, visited);
-
-                // We ignore regions in the coroutine interior as we don't
-                // want these to affect region inference
-            }
-
-            // All regions are bound inside a witness
-            ty::CoroutineWitness(..) => (),
-
-            // OutlivesTypeParameterEnv -- the actual checking that `X:'a`
-            // is implied by the environment is done in regionck.
-            ty::Param(p) => {
-                out.push(Component::Param(p));
-            }
-
-            ty::Placeholder(p) => {
-                out.push(Component::Placeholder(p));
-            }
-
-            // For projections, we prefer to generate an obligation like
-            // `<P0 as Trait<P1...Pn>>::Foo: 'a`, because this gives the
-            // regionck more ways to prove that it holds. However,
-            // regionck is not (at least currently) prepared to deal with
-            // higher-ranked regions that may appear in the
-            // trait-ref. Therefore, if we see any higher-ranked regions,
-            // we simply fallback to the most restrictive rule, which
-            // requires that `Pi: 'a` for all `i`.
-            ty::Alias(_, alias_ty) => {
-                if !alias_ty.has_escaping_bound_vars() {
-                    // best case: no escaping regions, so push the
-                    // projection and skip the subtree (thus generating no
-                    // constraints for Pi). This defers the choice between
-                    // the rules OutlivesProjectionEnv,
-                    // OutlivesProjectionTraitDef, and
-                    // OutlivesProjectionComponents to regionck.
-                    out.push(Component::Alias(alias_ty));
-                } else {
-                    // fallback case: hard code
-                    // OutlivesProjectionComponents. Continue walking
-                    // through and constrain Pi.
-                    let mut subcomponents = smallvec![];
-                    let mut subvisited = SsoHashSet::new();
-                    compute_alias_components_recursive(tcx, ty, &mut subcomponents, &mut subvisited);
-                    out.push(Component::EscapingAlias(subcomponents.into_iter().collect()));
-                }
-            }
-
-            // We assume that inference variables are fully resolved.
-            // So, if we encounter an inference variable, just record
-            // the unresolved variable as a component.
-            ty::Infer(infer_ty) => {
-                out.push(Component::UnresolvedInferenceVariable(infer_ty));
-            }
-
-            // Most types do not introduce any region binders, nor
-            // involve any other subtle cases, and so the WF relation
-            // simply constraints any regions referenced directly by
-            // the type and then visits the types that are lexically
-            // contained within. (The comments refer to relevant rules
-            // from RFC1214.)
-            ty::Bool |            // OutlivesScalar
-            ty::Char |            // OutlivesScalar
-            ty::Int(..) |         // OutlivesScalar
-            ty::Uint(..) |        // OutlivesScalar
-            ty::Float(..) |       // OutlivesScalar
-            ty::Never |           // ...
-            ty::Adt(..) |         // OutlivesNominalType
-            ty::Foreign(..) |     // OutlivesNominalType
-            ty::Str |             // OutlivesScalar (ish)
-            ty::Slice(..) |       // ...
-            ty::RawPtr(..) |      // ...
-            ty::Ref(..) |         // OutlivesReference
-            ty::Tuple(..) |       // ...
-            ty::FnPtr(_) |        // OutlivesFunction (*)
-            ty::Dynamic(..) |     // OutlivesObject, OutlivesFragment (*)
-            ty::Bound(..) |
-            ty::Error(_) => {
-                // (*) Function pointers and trait objects are both binders.
-                // In the RFC, this means we would add the bound regions to
-                // the "bound regions list". In our representation, no such
-                // list is maintained explicitly, because bound regions
-                // themselves can be readily identified.
-                compute_components_recursive(tcx, ty.into(), out, visited);
-            }
-        }
-}
-
-/// Collect [Component]s for *all* the args of `parent`.
-///
-/// This should not be used to get the components of `parent` itself.
-/// Use [push_outlives_components] instead.
-pub(super) fn compute_alias_components_recursive<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    alias_ty: Ty<'tcx>,
-    out: &mut SmallVec<[Component<'tcx>; 4]>,
-    visited: &mut SsoHashSet<GenericArg<'tcx>>,
-) {
-    let ty::Alias(kind, alias_ty) = alias_ty.kind() else {
-        unreachable!("can only call `compute_alias_components_recursive` on an alias type")
-    };
-    let opt_variances = if *kind == ty::Opaque { tcx.variances_of(alias_ty.def_id) } else { &[] };
-    for (index, child) in alias_ty.args.iter().enumerate() {
-        if opt_variances.get(index) == Some(&ty::Bivariant) {
-            continue;
-        }
-        if !visited.insert(child) {
-            continue;
-        }
-        match child.unpack() {
-            GenericArgKind::Type(ty) => {
-                compute_components(tcx, ty, out, visited);
-            }
-            GenericArgKind::Lifetime(lt) => {
-                // Ignore higher ranked regions.
-                if !lt.is_bound() {
-                    out.push(Component::Region(lt));
-                }
-            }
-            GenericArgKind::Const(_) => {
-                compute_components_recursive(tcx, child, out, visited);
-            }
-        }
-    }
-}
-
-/// Collect [Component]s for *all* the args of `parent`.
-///
-/// This should not be used to get the components of `parent` itself.
-/// Use [push_outlives_components] instead.
-fn compute_components_recursive<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    parent: GenericArg<'tcx>,
-    out: &mut SmallVec<[Component<'tcx>; 4]>,
-    visited: &mut SsoHashSet<GenericArg<'tcx>>,
-) {
-    for child in parent.walk_shallow(visited) {
-        match child.unpack() {
-            GenericArgKind::Type(ty) => {
-                compute_components(tcx, ty, out, visited);
-            }
-            GenericArgKind::Lifetime(lt) => {
-                // Ignore higher ranked regions.
-                if !lt.is_bound() {
-                    out.push(Component::Region(lt));
-                }
-            }
-            GenericArgKind::Const(_) => {
-                compute_components_recursive(tcx, child, out, visited);
-            }
-        }
-    }
-}
diff --git a/compiler/rustc_infer/src/infer/outlives/mod.rs b/compiler/rustc_infer/src/infer/outlives/mod.rs
index 48d006e7fbc..89ff4604560 100644
--- a/compiler/rustc_infer/src/infer/outlives/mod.rs
+++ b/compiler/rustc_infer/src/infer/outlives/mod.rs
@@ -8,7 +8,6 @@ use crate::infer::lexical_region_resolve;
 use rustc_middle::traits::query::{NoSolution, OutlivesBound};
 use rustc_middle::ty;
 
-pub mod components;
 pub mod env;
 pub mod for_liveness;
 pub mod obligations;
diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs
index 32c790523b6..9bb5f775e4a 100644
--- a/compiler/rustc_infer/src/infer/outlives/obligations.rs
+++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs
@@ -59,7 +59,6 @@
 //! might later infer `?U` to something like `&'b u32`, which would
 //! imply that `'b: 'a`.
 
-use crate::infer::outlives::components::{push_outlives_components, Component};
 use crate::infer::outlives::env::RegionBoundPairs;
 use crate::infer::outlives::verify::VerifyBoundCx;
 use crate::infer::resolve::OpportunisticRegionResolver;
@@ -75,6 +74,7 @@ use rustc_middle::ty::{
 };
 use rustc_middle::ty::{GenericArgKind, PolyTypeOutlivesPredicate};
 use rustc_span::DUMMY_SP;
+use rustc_type_ir::outlives::{push_outlives_components, Component};
 use smallvec::smallvec;
 
 use super::env::OutlivesEnvironment;
@@ -291,7 +291,7 @@ where
     fn components_must_outlive(
         &mut self,
         origin: infer::SubregionOrigin<'tcx>,
-        components: &[Component<'tcx>],
+        components: &[Component<TyCtxt<'tcx>>],
         region: ty::Region<'tcx>,
         category: ConstraintCategory<'tcx>,
     ) {
diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs
index 7e977b9b954..ad102dfcc1f 100644
--- a/compiler/rustc_infer/src/infer/outlives/verify.rs
+++ b/compiler/rustc_infer/src/infer/outlives/verify.rs
@@ -1,10 +1,10 @@
-use crate::infer::outlives::components::{compute_alias_components_recursive, Component};
 use crate::infer::outlives::env::RegionBoundPairs;
 use crate::infer::region_constraints::VerifyIfEq;
 use crate::infer::{GenericKind, VerifyBound};
 use rustc_data_structures::sso::SsoHashSet;
 use rustc_middle::ty::GenericArg;
 use rustc_middle::ty::{self, OutlivesPredicate, Ty, TyCtxt};
+use rustc_type_ir::outlives::{compute_alias_components_recursive, Component};
 
 use smallvec::smallvec;
 
@@ -139,7 +139,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
 
     fn bound_from_components(
         &self,
-        components: &[Component<'tcx>],
+        components: &[Component<TyCtxt<'tcx>>],
         visited: &mut SsoHashSet<GenericArg<'tcx>>,
     ) -> VerifyBound<'tcx> {
         let mut bounds = components
@@ -158,7 +158,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
 
     fn bound_from_single_component(
         &self,
-        component: &Component<'tcx>,
+        component: &Component<TyCtxt<'tcx>>,
         visited: &mut SsoHashSet<GenericArg<'tcx>>,
     ) -> VerifyBound<'tcx> {
         match *component {
diff --git a/compiler/rustc_infer/src/infer/relate/combine.rs b/compiler/rustc_infer/src/infer/relate/combine.rs
index 0a2e85cc891..c93b89756f9 100644
--- a/compiler/rustc_infer/src/infer/relate/combine.rs
+++ b/compiler/rustc_infer/src/infer/relate/combine.rs
@@ -18,11 +18,13 @@
 //! On success, the  LUB/GLB operations return the appropriate bound. The
 //! return value of `Equate` or `Sub` shouldn't really be used.
 
+pub use rustc_next_trait_solver::relate::combine::*;
+
 use super::glb::Glb;
 use super::lub::Lub;
 use super::type_relating::TypeRelating;
+use super::RelateResult;
 use super::StructurallyRelateAliases;
-use super::{RelateResult, TypeRelation};
 use crate::infer::relate;
 use crate::infer::{DefineOpaqueTypes, InferCtxt, TypeTrace};
 use crate::traits::{Obligation, PredicateObligation};
@@ -32,7 +34,6 @@ use rustc_middle::traits::solve::Goal;
 use rustc_middle::ty::error::{ExpectedFound, TypeError};
 use rustc_middle::ty::{self, InferConst, Ty, TyCtxt, TypeVisitableExt, Upcast};
 use rustc_middle::ty::{IntType, UintType};
-use rustc_span::Span;
 
 #[derive(Clone)]
 pub struct CombineFields<'infcx, 'tcx> {
@@ -76,7 +77,7 @@ impl<'tcx> InferCtxt<'tcx> {
         b: Ty<'tcx>,
     ) -> RelateResult<'tcx, Ty<'tcx>>
     where
-        R: PredicateEmittingRelation<'tcx>,
+        R: PredicateEmittingRelation<InferCtxt<'tcx>>,
     {
         debug_assert!(!a.has_escaping_bound_vars());
         debug_assert!(!b.has_escaping_bound_vars());
@@ -171,7 +172,7 @@ impl<'tcx> InferCtxt<'tcx> {
         b: ty::Const<'tcx>,
     ) -> RelateResult<'tcx, ty::Const<'tcx>>
     where
-        R: PredicateEmittingRelation<'tcx>,
+        R: PredicateEmittingRelation<InferCtxt<'tcx>>,
     {
         debug!("{}.consts({:?}, {:?})", relation.tag(), a, b);
         debug_assert!(!a.has_escaping_bound_vars());
@@ -323,30 +324,3 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
         )
     }
 }
-
-pub trait PredicateEmittingRelation<'tcx>: TypeRelation<TyCtxt<'tcx>> {
-    fn span(&self) -> Span;
-
-    fn param_env(&self) -> ty::ParamEnv<'tcx>;
-
-    /// Whether aliases should be related structurally. This is pretty much
-    /// always `No` unless you're equating in some specific locations of the
-    /// new solver. See the comments in these use-cases for more details.
-    fn structurally_relate_aliases(&self) -> StructurallyRelateAliases;
-
-    /// Register obligations that must hold in order for this relation to hold
-    fn register_goals(
-        &mut self,
-        obligations: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>,
-    );
-
-    /// Register predicates that must hold in order for this relation to hold.
-    /// This uses the default `param_env` of the obligation.
-    fn register_predicates(
-        &mut self,
-        obligations: impl IntoIterator<Item: Upcast<TyCtxt<'tcx>, ty::Predicate<'tcx>>>,
-    );
-
-    /// Register `AliasRelate` obligation(s) that both types must be related to each other.
-    fn register_alias_relate_predicate(&mut self, a: Ty<'tcx>, b: Ty<'tcx>);
-}
diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs
index d6e57d85387..fe3b8d60fb9 100644
--- a/compiler/rustc_infer/src/infer/relate/generalize.rs
+++ b/compiler/rustc_infer/src/infer/relate/generalize.rs
@@ -30,7 +30,7 @@ impl<'tcx> InferCtxt<'tcx> {
     /// `TypeRelation`. Do not use this, and instead please use `At::eq`, for all
     /// other usecases (i.e. setting the value of a type var).
     #[instrument(level = "debug", skip(self, relation))]
-    pub fn instantiate_ty_var<R: PredicateEmittingRelation<'tcx>>(
+    pub fn instantiate_ty_var<R: PredicateEmittingRelation<InferCtxt<'tcx>>>(
         &self,
         relation: &mut R,
         target_is_expected: bool,
@@ -178,7 +178,7 @@ impl<'tcx> InferCtxt<'tcx> {
     ///
     /// See `tests/ui/const-generics/occurs-check/` for more examples where this is relevant.
     #[instrument(level = "debug", skip(self, relation))]
-    pub(super) fn instantiate_const_var<R: PredicateEmittingRelation<'tcx>>(
+    pub(super) fn instantiate_const_var<R: PredicateEmittingRelation<InferCtxt<'tcx>>>(
         &self,
         relation: &mut R,
         target_is_expected: bool,
diff --git a/compiler/rustc_infer/src/infer/relate/glb.rs b/compiler/rustc_infer/src/infer/relate/glb.rs
index 067004ecaeb..5bb8a113e17 100644
--- a/compiler/rustc_infer/src/infer/relate/glb.rs
+++ b/compiler/rustc_infer/src/infer/relate/glb.rs
@@ -123,7 +123,7 @@ impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Glb<'combine, 'infcx,
     }
 }
 
-impl<'tcx> PredicateEmittingRelation<'tcx> for Glb<'_, '_, 'tcx> {
+impl<'tcx> PredicateEmittingRelation<InferCtxt<'tcx>> for Glb<'_, '_, 'tcx> {
     fn span(&self) -> Span {
         self.fields.trace.span()
     }
diff --git a/compiler/rustc_infer/src/infer/relate/lattice.rs b/compiler/rustc_infer/src/infer/relate/lattice.rs
index 6cc8d6d910a..46e7466141a 100644
--- a/compiler/rustc_infer/src/infer/relate/lattice.rs
+++ b/compiler/rustc_infer/src/infer/relate/lattice.rs
@@ -30,7 +30,7 @@ use rustc_middle::ty::{self, Ty};
 ///
 /// GLB moves "down" the lattice (to smaller values); LUB moves
 /// "up" the lattice (to bigger values).
-pub trait LatticeDir<'f, 'tcx>: PredicateEmittingRelation<'tcx> {
+pub trait LatticeDir<'f, 'tcx>: PredicateEmittingRelation<InferCtxt<'tcx>> {
     fn infcx(&self) -> &'f InferCtxt<'tcx>;
 
     fn cause(&self) -> &ObligationCause<'tcx>;
diff --git a/compiler/rustc_infer/src/infer/relate/lub.rs b/compiler/rustc_infer/src/infer/relate/lub.rs
index 2184416b4cc..94c1464817f 100644
--- a/compiler/rustc_infer/src/infer/relate/lub.rs
+++ b/compiler/rustc_infer/src/infer/relate/lub.rs
@@ -123,7 +123,7 @@ impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Lub<'combine, 'infcx,
     }
 }
 
-impl<'tcx> PredicateEmittingRelation<'tcx> for Lub<'_, '_, 'tcx> {
+impl<'tcx> PredicateEmittingRelation<InferCtxt<'tcx>> for Lub<'_, '_, 'tcx> {
     fn span(&self) -> Span {
         self.fields.trace.span()
     }
diff --git a/compiler/rustc_infer/src/infer/relate/mod.rs b/compiler/rustc_infer/src/infer/relate/mod.rs
index 41cc945492d..dd97dc061fe 100644
--- a/compiler/rustc_infer/src/infer/relate/mod.rs
+++ b/compiler/rustc_infer/src/infer/relate/mod.rs
@@ -2,11 +2,13 @@
 //! (except for some relations used for diagnostics and heuristics in the compiler).
 //! As well as the implementation of `Relate` for interned things (`Ty`/`Const`/etc).
 
-pub use rustc_middle::ty::relate::*;
+pub use rustc_middle::ty::relate::RelateResult;
+pub use rustc_next_trait_solver::relate::*;
 
 pub use self::combine::CombineFields;
 pub use self::combine::PredicateEmittingRelation;
 
+#[allow(hidden_glob_reexports)]
 pub(super) mod combine;
 mod generalize;
 mod glb;
diff --git a/compiler/rustc_infer/src/infer/relate/type_relating.rs b/compiler/rustc_infer/src/infer/relate/type_relating.rs
index f2bec9392d5..e206f530519 100644
--- a/compiler/rustc_infer/src/infer/relate/type_relating.rs
+++ b/compiler/rustc_infer/src/infer/relate/type_relating.rs
@@ -1,7 +1,7 @@
 use super::combine::CombineFields;
 use crate::infer::relate::{PredicateEmittingRelation, StructurallyRelateAliases};
 use crate::infer::BoundRegionConversionTime::HigherRankedType;
-use crate::infer::{DefineOpaqueTypes, SubregionOrigin};
+use crate::infer::{DefineOpaqueTypes, InferCtxt, SubregionOrigin};
 use rustc_middle::traits::solve::Goal;
 use rustc_middle::ty::relate::{
     relate_args_invariantly, relate_args_with_variances, Relate, RelateResult, TypeRelation,
@@ -296,7 +296,7 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for TypeRelating<'_, '_, 'tcx> {
     }
 }
 
-impl<'tcx> PredicateEmittingRelation<'tcx> for TypeRelating<'_, '_, 'tcx> {
+impl<'tcx> PredicateEmittingRelation<InferCtxt<'tcx>> for TypeRelating<'_, '_, 'tcx> {
     fn span(&self) -> Span {
         self.fields.trace.span()
     }
diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs
index 18d36c3a5df..b269bfcbfeb 100644
--- a/compiler/rustc_infer/src/traits/util.rs
+++ b/compiler/rustc_infer/src/traits/util.rs
@@ -1,12 +1,12 @@
 use smallvec::smallvec;
 
-use crate::infer::outlives::components::{push_outlives_components, Component};
 use crate::traits::{self, Obligation, ObligationCauseCode, PredicateObligation};
 use rustc_data_structures::fx::FxHashSet;
 use rustc_middle::ty::ToPolyTraitRef;
 use rustc_middle::ty::{self, Ty, TyCtxt, Upcast};
 use rustc_span::symbol::Ident;
 use rustc_span::Span;
+use rustc_type_ir::outlives::{push_outlives_components, Component};
 
 pub fn anonymize_predicate<'tcx>(
     tcx: TyCtxt<'tcx>,
diff --git a/compiler/rustc_middle/src/ty/consts/kind.rs b/compiler/rustc_middle/src/ty/consts/kind.rs
index bf834ef7607..98f35b6b8ab 100644
--- a/compiler/rustc_middle/src/ty/consts/kind.rs
+++ b/compiler/rustc_middle/src/ty/consts/kind.rs
@@ -54,6 +54,13 @@ pub struct Expr<'tcx> {
     pub kind: ExprKind,
     args: ty::GenericArgsRef<'tcx>,
 }
+
+impl<'tcx> rustc_type_ir::inherent::ExprConst<TyCtxt<'tcx>> for Expr<'tcx> {
+    fn args(self) -> ty::GenericArgsRef<'tcx> {
+        self.args
+    }
+}
+
 impl<'tcx> Expr<'tcx> {
     pub fn new_binop(
         tcx: TyCtxt<'tcx>,
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 055749ba3a3..dca48069974 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -92,6 +92,8 @@ use std::ops::{Bound, Deref};
 impl<'tcx> Interner for TyCtxt<'tcx> {
     type DefId = DefId;
     type LocalDefId = LocalDefId;
+    type Span = Span;
+
     type GenericArgs = ty::GenericArgsRef<'tcx>;
 
     type GenericArgsSlice = &'tcx [ty::GenericArg<'tcx>];
diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs
index b169d672a84..ebf0d7ed737 100644
--- a/compiler/rustc_middle/src/ty/relate.rs
+++ b/compiler/rustc_middle/src/ty/relate.rs
@@ -10,18 +10,6 @@ use crate::ty::{self as ty, Ty, TyCtxt};
 
 pub type RelateResult<'tcx, T> = rustc_type_ir::relate::RelateResult<TyCtxt<'tcx>, T>;
 
-/// Whether aliases should be related structurally or not. Used
-/// to adjust the behavior of generalization and combine.
-///
-/// This should always be `No` unless in a few special-cases when
-/// instantiating canonical responses and in the new solver. Each
-/// such case should have a comment explaining why it is used.
-#[derive(Debug, Copy, Clone)]
-pub enum StructurallyRelateAliases {
-    Yes,
-    No,
-}
-
 impl<'tcx> Relate<TyCtxt<'tcx>> for ty::ImplSubject<'tcx> {
     #[inline]
     fn relate<R: TypeRelation<TyCtxt<'tcx>>>(
diff --git a/compiler/rustc_middle/src/ty/walk.rs b/compiler/rustc_middle/src/ty/walk.rs
index e0f204a687f..efcaf89081f 100644
--- a/compiler/rustc_middle/src/ty/walk.rs
+++ b/compiler/rustc_middle/src/ty/walk.rs
@@ -78,23 +78,6 @@ impl<'tcx> GenericArg<'tcx> {
     pub fn walk(self) -> TypeWalker<'tcx> {
         TypeWalker::new(self)
     }
-
-    /// Iterator that walks the immediate children of `self`. Hence
-    /// `Foo<Bar<i32>, u32>` yields the sequence `[Bar<i32>, u32]`
-    /// (but not `i32`, like `walk`).
-    ///
-    /// Iterator only walks items once.
-    /// It accepts visited set, updates it with all visited types
-    /// and skips any types that are already there.
-    pub fn walk_shallow(
-        self,
-        visited: &mut SsoHashSet<GenericArg<'tcx>>,
-    ) -> impl Iterator<Item = GenericArg<'tcx>> {
-        let mut stack = SmallVec::new();
-        push_inner(&mut stack, self);
-        stack.retain(|a| visited.insert(*a));
-        stack.into_iter()
-    }
 }
 
 impl<'tcx> Ty<'tcx> {
diff --git a/compiler/rustc_next_trait_solver/src/lib.rs b/compiler/rustc_next_trait_solver/src/lib.rs
index a6a9c01faaa..761475d3d6b 100644
--- a/compiler/rustc_next_trait_solver/src/lib.rs
+++ b/compiler/rustc_next_trait_solver/src/lib.rs
@@ -6,5 +6,6 @@
 
 pub mod canonicalizer;
 pub mod delegate;
+pub mod relate;
 pub mod resolve;
 pub mod solve;
diff --git a/compiler/rustc_next_trait_solver/src/relate.rs b/compiler/rustc_next_trait_solver/src/relate.rs
new file mode 100644
index 00000000000..db819961bbd
--- /dev/null
+++ b/compiler/rustc_next_trait_solver/src/relate.rs
@@ -0,0 +1,15 @@
+pub use rustc_type_ir::relate::*;
+
+pub mod combine;
+
+/// Whether aliases should be related structurally or not. Used
+/// to adjust the behavior of generalization and combine.
+///
+/// This should always be `No` unless in a few special-cases when
+/// instantiating canonical responses and in the new solver. Each
+/// such case should have a comment explaining why it is used.
+#[derive(Debug, Copy, Clone)]
+pub enum StructurallyRelateAliases {
+    Yes,
+    No,
+}
diff --git a/compiler/rustc_next_trait_solver/src/relate/combine.rs b/compiler/rustc_next_trait_solver/src/relate/combine.rs
new file mode 100644
index 00000000000..96968327d8e
--- /dev/null
+++ b/compiler/rustc_next_trait_solver/src/relate/combine.rs
@@ -0,0 +1,34 @@
+pub use rustc_type_ir::relate::*;
+use rustc_type_ir::solve::Goal;
+use rustc_type_ir::{InferCtxtLike, Interner, Upcast};
+
+use super::StructurallyRelateAliases;
+
+pub trait PredicateEmittingRelation<Infcx, I = <Infcx as InferCtxtLike>::Interner>:
+    TypeRelation<I>
+where
+    Infcx: InferCtxtLike<Interner = I>,
+    I: Interner,
+{
+    fn span(&self) -> I::Span;
+
+    fn param_env(&self) -> I::ParamEnv;
+
+    /// Whether aliases should be related structurally. This is pretty much
+    /// always `No` unless you're equating in some specific locations of the
+    /// new solver. See the comments in these use-cases for more details.
+    fn structurally_relate_aliases(&self) -> StructurallyRelateAliases;
+
+    /// Register obligations that must hold in order for this relation to hold
+    fn register_goals(&mut self, obligations: impl IntoIterator<Item = Goal<I, I::Predicate>>);
+
+    /// Register predicates that must hold in order for this relation to hold.
+    /// This uses the default `param_env` of the obligation.
+    fn register_predicates(
+        &mut self,
+        obligations: impl IntoIterator<Item: Upcast<I, I::Predicate>>,
+    );
+
+    /// Register `AliasRelate` obligation(s) that both types must be related to each other.
+    fn register_alias_relate_predicate(&mut self, a: I::Ty, b: I::Ty);
+}
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs
index b38841db923..8525215a3bc 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs
@@ -3,7 +3,6 @@ use crate::traits::wf;
 use crate::traits::ObligationCtxt;
 
 use rustc_infer::infer::canonical::Canonical;
-use rustc_infer::infer::outlives::components::{push_outlives_components, Component};
 use rustc_infer::infer::resolve::OpportunisticRegionResolver;
 use rustc_infer::traits::query::OutlivesBound;
 use rustc_macros::{HashStable, TypeFoldable, TypeVisitable};
@@ -12,6 +11,7 @@ use rustc_middle::traits::ObligationCause;
 use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeFolder, TypeVisitableExt};
 use rustc_span::def_id::CRATE_DEF_ID;
 use rustc_span::DUMMY_SP;
+use rustc_type_ir::outlives::{push_outlives_components, Component};
 use smallvec::{smallvec, SmallVec};
 
 #[derive(Copy, Clone, Debug, HashStable, TypeFoldable, TypeVisitable)]
@@ -284,7 +284,7 @@ pub fn compute_implied_outlives_bounds_compat_inner<'tcx>(
 /// those relationships.
 fn implied_bounds_from_components<'tcx>(
     sub_region: ty::Region<'tcx>,
-    sup_components: SmallVec<[Component<'tcx>; 4]>,
+    sup_components: SmallVec<[Component<TyCtxt<'tcx>>; 4]>,
 ) -> Vec<OutlivesBound<'tcx>> {
     sup_components
         .into_iter()
diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs
index ffe16964ae5..68c2575258d 100644
--- a/compiler/rustc_type_ir/src/inherent.rs
+++ b/compiler/rustc_type_ir/src/inherent.rs
@@ -232,6 +232,10 @@ pub trait Region<I: Interner<Region = Self>>:
     fn new_anon_bound(interner: I, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self;
 
     fn new_static(interner: I) -> Self;
+
+    fn is_bound(self) -> bool {
+        matches!(self.kind(), ty::ReBound(..))
+    }
 }
 
 pub trait Const<I: Interner<Const = Self>>:
@@ -272,6 +276,10 @@ pub trait Const<I: Interner<Const = Self>>:
     }
 }
 
+pub trait ExprConst<I: Interner<ExprConst = Self>>: Copy + Debug + Hash + Eq + Relate<I> {
+    fn args(self) -> I::GenericArgs;
+}
+
 pub trait GenericsOf<I: Interner<GenericsOf = Self>> {
     fn count(&self) -> usize;
 }
diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs
index db97bdca382..6e013768c3e 100644
--- a/compiler/rustc_type_ir/src/interner.rs
+++ b/compiler/rustc_type_ir/src/interner.rs
@@ -32,6 +32,7 @@ pub trait Interner:
 {
     type DefId: DefId<Self>;
     type LocalDefId: Copy + Debug + Hash + Eq + Into<Self::DefId> + TypeFoldable<Self>;
+    type Span: Copy + Debug + Hash + Eq;
 
     type GenericArgs: GenericArgs<Self>;
     type GenericArgsSlice: Copy + Debug + Hash + Eq + SliceLike<Item = Self::GenericArg>;
@@ -109,7 +110,7 @@ pub trait Interner:
     type ParamConst: Copy + Debug + Hash + Eq + ParamLike;
     type BoundConst: Copy + Debug + Hash + Eq + BoundVarLike<Self>;
     type ValueConst: Copy + Debug + Hash + Eq;
-    type ExprConst: Copy + Debug + Hash + Eq + Relate<Self>;
+    type ExprConst: ExprConst<Self>;
 
     // Kinds of regions
     type Region: Region<Self>;
diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs
index c57ac74cdfd..2a909b06baf 100644
--- a/compiler/rustc_type_ir/src/lib.rs
+++ b/compiler/rustc_type_ir/src/lib.rs
@@ -27,6 +27,7 @@ pub mod inherent;
 pub mod ir_print;
 pub mod lang_items;
 pub mod lift;
+pub mod outlives;
 pub mod relate;
 pub mod solve;
 
diff --git a/compiler/rustc_type_ir/src/outlives.rs b/compiler/rustc_type_ir/src/outlives.rs
new file mode 100644
index 00000000000..61dfa2643d8
--- /dev/null
+++ b/compiler/rustc_type_ir/src/outlives.rs
@@ -0,0 +1,335 @@
+//! The outlives relation `T: 'a` or `'a: 'b`. This code frequently
+//! refers to rules defined in RFC 1214 (`OutlivesFooBar`), so see that
+//! RFC for reference.
+
+use smallvec::{smallvec, SmallVec};
+use tracing::debug;
+
+use crate::data_structures::SsoHashSet;
+use crate::inherent::*;
+use crate::visit::TypeVisitableExt as _;
+use crate::{self as ty, Interner};
+
+#[derive(derivative::Derivative)]
+#[derivative(Debug(bound = ""))]
+pub enum Component<I: Interner> {
+    Region(I::Region),
+    Param(I::ParamTy),
+    Placeholder(I::PlaceholderTy),
+    UnresolvedInferenceVariable(ty::InferTy),
+
+    // Projections like `T::Foo` are tricky because a constraint like
+    // `T::Foo: 'a` can be satisfied in so many ways. There may be a
+    // where-clause that says `T::Foo: 'a`, or the defining trait may
+    // include a bound like `type Foo: 'static`, or -- in the most
+    // conservative way -- we can prove that `T: 'a` (more generally,
+    // that all components in the projection outlive `'a`). This code
+    // is not in a position to judge which is the best technique, so
+    // we just product the projection as a component and leave it to
+    // the consumer to decide (but see `EscapingProjection` below).
+    Alias(ty::AliasTy<I>),
+
+    // In the case where a projection has escaping regions -- meaning
+    // regions bound within the type itself -- we always use
+    // the most conservative rule, which requires that all components
+    // outlive the bound. So for example if we had a type like this:
+    //
+    //     for<'a> Trait1<  <T as Trait2<'a,'b>>::Foo  >
+    //                      ~~~~~~~~~~~~~~~~~~~~~~~~~
+    //
+    // then the inner projection (underlined) has an escaping region
+    // `'a`. We consider that outer trait `'c` to meet a bound if `'b`
+    // outlives `'b: 'c`, and we don't consider whether the trait
+    // declares that `Foo: 'static` etc. Therefore, we just return the
+    // free components of such a projection (in this case, `'b`).
+    //
+    // However, in the future, we may want to get smarter, and
+    // actually return a "higher-ranked projection" here. Therefore,
+    // we mark that these components are part of an escaping
+    // projection, so that implied bounds code can avoid relying on
+    // them. This gives us room to improve the regionck reasoning in
+    // the future without breaking backwards compat.
+    EscapingAlias(Vec<Component<I>>),
+}
+
+/// Push onto `out` all the things that must outlive `'a` for the condition
+/// `ty0: 'a` to hold. Note that `ty0` must be a **fully resolved type**.
+pub fn push_outlives_components<I: Interner>(
+    tcx: I,
+    ty0: I::Ty,
+    out: &mut SmallVec<[Component<I>; 4]>,
+) {
+    let mut visited = SsoHashSet::new();
+    compute_components_for_ty(tcx, ty0, out, &mut visited);
+    debug!("components({:?}) = {:?}", ty0, out);
+}
+
+fn compute_components_for_arg<I: Interner>(
+    tcx: I,
+    arg: I::GenericArg,
+    out: &mut SmallVec<[Component<I>; 4]>,
+    visited: &mut SsoHashSet<I::GenericArg>,
+) {
+    match arg.kind() {
+        ty::GenericArgKind::Type(ty) => {
+            compute_components_for_ty(tcx, ty, out, visited);
+        }
+        ty::GenericArgKind::Lifetime(lt) => {
+            compute_components_for_lt(lt, out);
+        }
+        ty::GenericArgKind::Const(ct) => {
+            compute_components_for_const(tcx, ct, out, visited);
+        }
+    }
+}
+
+fn compute_components_for_ty<I: Interner>(
+    tcx: I,
+    ty: I::Ty,
+    out: &mut SmallVec<[Component<I>; 4]>,
+    visited: &mut SsoHashSet<I::GenericArg>,
+) {
+    if !visited.insert(ty.into()) {
+        return;
+    }
+    // Descend through the types, looking for the various "base"
+    // components and collecting them into `out`. This is not written
+    // with `collect()` because of the need to sometimes skip subtrees
+    // in the `subtys` iterator (e.g., when encountering a
+    // projection).
+    match ty.kind() {
+        ty::FnDef(_, args) => {
+            // HACK(eddyb) ignore lifetimes found shallowly in `args`.
+            // This is inconsistent with `ty::Adt` (including all args)
+            // and with `ty::Closure` (ignoring all args other than
+            // upvars, of which a `ty::FnDef` doesn't have any), but
+            // consistent with previous (accidental) behavior.
+            // See https://github.com/rust-lang/rust/issues/70917
+            // for further background and discussion.
+            for child in args.iter() {
+                match child.kind() {
+                    ty::GenericArgKind::Type(ty) => {
+                        compute_components_for_ty(tcx, ty, out, visited);
+                    }
+                    ty::GenericArgKind::Lifetime(_) => {}
+                    ty::GenericArgKind::Const(ct) => {
+                        compute_components_for_const(tcx, ct, out, visited);
+                    }
+                }
+            }
+        }
+
+        ty::Pat(element, _) | ty::Array(element, _) => {
+            compute_components_for_ty(tcx, element, out, visited);
+        }
+
+        ty::Closure(_, args) => {
+            let tupled_ty = args.as_closure().tupled_upvars_ty();
+            compute_components_for_ty(tcx, tupled_ty, out, visited);
+        }
+
+        ty::CoroutineClosure(_, args) => {
+            let tupled_ty = args.as_coroutine_closure().tupled_upvars_ty();
+            compute_components_for_ty(tcx, tupled_ty, out, visited);
+        }
+
+        ty::Coroutine(_, args) => {
+            // Same as the closure case
+            let tupled_ty = args.as_coroutine().tupled_upvars_ty();
+            compute_components_for_ty(tcx, tupled_ty, out, visited);
+
+            // We ignore regions in the coroutine interior as we don't
+            // want these to affect region inference
+        }
+
+        // All regions are bound inside a witness, and we don't emit
+        // higher-ranked outlives components currently.
+        ty::CoroutineWitness(..) => {},
+
+        // OutlivesTypeParameterEnv -- the actual checking that `X:'a`
+        // is implied by the environment is done in regionck.
+        ty::Param(p) => {
+            out.push(Component::Param(p));
+        }
+
+        ty::Placeholder(p) => {
+            out.push(Component::Placeholder(p));
+        }
+
+        // For projections, we prefer to generate an obligation like
+        // `<P0 as Trait<P1...Pn>>::Foo: 'a`, because this gives the
+        // regionck more ways to prove that it holds. However,
+        // regionck is not (at least currently) prepared to deal with
+        // higher-ranked regions that may appear in the
+        // trait-ref. Therefore, if we see any higher-ranked regions,
+        // we simply fallback to the most restrictive rule, which
+        // requires that `Pi: 'a` for all `i`.
+        ty::Alias(_, alias_ty) => {
+            if !alias_ty.has_escaping_bound_vars() {
+                // best case: no escaping regions, so push the
+                // projection and skip the subtree (thus generating no
+                // constraints for Pi). This defers the choice between
+                // the rules OutlivesProjectionEnv,
+                // OutlivesProjectionTraitDef, and
+                // OutlivesProjectionComponents to regionck.
+                out.push(Component::Alias(alias_ty));
+            } else {
+                // fallback case: hard code
+                // OutlivesProjectionComponents. Continue walking
+                // through and constrain Pi.
+                let mut subcomponents = smallvec![];
+                let mut subvisited = SsoHashSet::new();
+                compute_alias_components_recursive(tcx, ty, &mut subcomponents, &mut subvisited);
+                out.push(Component::EscapingAlias(subcomponents.into_iter().collect()));
+            }
+        }
+
+        // We assume that inference variables are fully resolved.
+        // So, if we encounter an inference variable, just record
+        // the unresolved variable as a component.
+        ty::Infer(infer_ty) => {
+            out.push(Component::UnresolvedInferenceVariable(infer_ty));
+        }
+
+        // Most types do not introduce any region binders, nor
+        // involve any other subtle cases, and so the WF relation
+        // simply constraints any regions referenced directly by
+        // the type and then visits the types that are lexically
+        // contained within. (The comments refer to relevant rules
+        // from RFC1214.)
+
+        ty::Bool |            // OutlivesScalar
+        ty::Char |            // OutlivesScalar
+        ty::Int(..) |         // OutlivesScalar
+        ty::Uint(..) |        // OutlivesScalar
+        ty::Float(..) |       // OutlivesScalar
+        ty::Never |           // OutlivesScalar
+        ty::Foreign(..) |     // OutlivesNominalType
+        ty::Str |             // OutlivesScalar (ish)
+        ty::Bound(..) |
+        ty::Error(_) => {
+            // Trivial.
+        }
+
+        // OutlivesNominalType
+        ty::Adt(_, args) => {
+            for arg in args.iter() {
+                compute_components_for_arg(tcx, arg, out, visited);
+            }
+        }
+
+        // OutlivesNominalType
+        ty::Slice(ty) |
+        ty::RawPtr(ty, _) => {
+            compute_components_for_ty(tcx, ty, out, visited);
+        }
+        ty::Tuple(tys) => {
+            for ty in tys.iter() {
+                compute_components_for_ty(tcx, ty, out, visited);
+            }
+        }
+
+        // OutlivesReference
+        ty::Ref(lt, ty, _) => {
+            compute_components_for_lt(lt, out);
+            compute_components_for_ty(tcx, ty, out, visited);
+        }
+
+        ty::Dynamic(preds, lt, _) => {
+            compute_components_for_lt(lt, out);
+            for pred in preds.iter() {
+                match pred.skip_binder() {
+                    ty::ExistentialPredicate::Trait(tr) => {
+                        for arg in tr.args.iter() {
+                            compute_components_for_arg(tcx, arg, out, visited);
+                        }
+                    }
+                    ty::ExistentialPredicate::Projection(proj) => {
+                        for arg in proj.args.iter() {
+                            compute_components_for_arg(tcx, arg, out, visited);
+                        }
+                        match proj.term.kind() {
+                            ty::TermKind::Ty(ty) => {
+                                compute_components_for_ty(tcx, ty, out, visited)
+                            }
+                            ty::TermKind::Const(ct) => {
+                                compute_components_for_const(tcx, ct, out, visited)
+                            }
+                        }
+                    }
+                    ty::ExistentialPredicate::AutoTrait(..) => {}
+                }
+            }
+        }
+
+        ty::FnPtr(sig) => {
+            for ty in sig.skip_binder().inputs_and_output.iter() {
+                compute_components_for_ty(tcx, ty, out, visited);
+            }
+        }
+    }
+}
+
+/// Collect [Component]s for *all* the args of `parent`.
+///
+/// This should not be used to get the components of `parent` itself.
+/// Use [push_outlives_components] instead.
+pub fn compute_alias_components_recursive<I: Interner>(
+    tcx: I,
+    alias_ty: I::Ty,
+    out: &mut SmallVec<[Component<I>; 4]>,
+    visited: &mut SsoHashSet<I::GenericArg>,
+) {
+    let ty::Alias(kind, alias_ty) = alias_ty.kind() else {
+        unreachable!("can only call `compute_alias_components_recursive` on an alias type")
+    };
+
+    let opt_variances =
+        if kind == ty::Opaque { Some(tcx.variances_of(alias_ty.def_id)) } else { None };
+
+    for (index, child) in alias_ty.args.iter().enumerate() {
+        if opt_variances.and_then(|variances| variances.get(index)) == Some(ty::Bivariant) {
+            continue;
+        }
+        compute_components_for_arg(tcx, child, out, visited);
+    }
+}
+
+fn compute_components_for_lt<I: Interner>(lt: I::Region, out: &mut SmallVec<[Component<I>; 4]>) {
+    if !lt.is_bound() {
+        out.push(Component::Region(lt));
+    }
+}
+
+fn compute_components_for_const<I: Interner>(
+    tcx: I,
+    ct: I::Const,
+    out: &mut SmallVec<[Component<I>; 4]>,
+    visited: &mut SsoHashSet<I::GenericArg>,
+) {
+    if !visited.insert(ct.into()) {
+        return;
+    }
+    match ct.kind() {
+        ty::ConstKind::Param(_)
+        | ty::ConstKind::Bound(_, _)
+        | ty::ConstKind::Infer(_)
+        | ty::ConstKind::Placeholder(_)
+        | ty::ConstKind::Error(_) => {
+            // Trivial
+        }
+        ty::ConstKind::Expr(e) => {
+            for arg in e.args().iter() {
+                compute_components_for_arg(tcx, arg, out, visited);
+            }
+        }
+        ty::ConstKind::Value(ty, _) => {
+            compute_components_for_ty(tcx, ty, out, visited);
+        }
+        ty::ConstKind::Unevaluated(uv) => {
+            for arg in uv.args.iter() {
+                compute_components_for_arg(tcx, arg, out, visited);
+            }
+        }
+    }
+}
diff --git a/tests/ui/async-await/in-trait/async-generics-and-bounds.stderr b/tests/ui/async-await/in-trait/async-generics-and-bounds.stderr
index 3cc35b21409..b547da7126a 100644
--- a/tests/ui/async-await/in-trait/async-generics-and-bounds.stderr
+++ b/tests/ui/async-await/in-trait/async-generics-and-bounds.stderr
@@ -1,29 +1,29 @@
-error[E0311]: the parameter type `U` may not live long enough
+error[E0311]: the parameter type `T` may not live long enough
   --> $DIR/async-generics-and-bounds.rs:9:5
    |
 LL |     async fn foo(&self) -> &(T, U) where T: Debug + Sized, U: Hash;
    |     ^^^^^^^^^^^^^-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |     |            |
-   |     |            the parameter type `U` must be valid for the anonymous lifetime as defined here...
+   |     |            the parameter type `T` must be valid for the anonymous lifetime as defined here...
    |     ...so that the reference type `&(T, U)` does not outlive the data it points at
    |
 help: consider adding an explicit lifetime bound
    |
-LL |     async fn foo<'a>(&'a self) -> &'a (T, U) where T: Debug + Sized, U: Hash, U: 'a;
+LL |     async fn foo<'a>(&'a self) -> &'a (T, U) where T: Debug + Sized, U: Hash, T: 'a;
    |                 ++++  ++           ++                                       +++++++
 
-error[E0311]: the parameter type `T` may not live long enough
+error[E0311]: the parameter type `U` may not live long enough
   --> $DIR/async-generics-and-bounds.rs:9:5
    |
 LL |     async fn foo(&self) -> &(T, U) where T: Debug + Sized, U: Hash;
    |     ^^^^^^^^^^^^^-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |     |            |
-   |     |            the parameter type `T` must be valid for the anonymous lifetime as defined here...
+   |     |            the parameter type `U` must be valid for the anonymous lifetime as defined here...
    |     ...so that the reference type `&(T, U)` does not outlive the data it points at
    |
 help: consider adding an explicit lifetime bound
    |
-LL |     async fn foo<'a>(&'a self) -> &'a (T, U) where T: Debug + Sized, U: Hash, T: 'a;
+LL |     async fn foo<'a>(&'a self) -> &'a (T, U) where T: Debug + Sized, U: Hash, U: 'a;
    |                 ++++  ++           ++                                       +++++++
 
 error: aborting due to 2 previous errors
diff --git a/tests/ui/async-await/in-trait/async-generics.stderr b/tests/ui/async-await/in-trait/async-generics.stderr
index 3b27f8fe2f0..2e29a9bcc77 100644
--- a/tests/ui/async-await/in-trait/async-generics.stderr
+++ b/tests/ui/async-await/in-trait/async-generics.stderr
@@ -1,29 +1,29 @@
-error[E0311]: the parameter type `U` may not live long enough
+error[E0311]: the parameter type `T` may not live long enough
   --> $DIR/async-generics.rs:6:5
    |
 LL |     async fn foo(&self) -> &(T, U);
    |     ^^^^^^^^^^^^^-^^^^^^^^^^^^^^^^^
    |     |            |
-   |     |            the parameter type `U` must be valid for the anonymous lifetime as defined here...
+   |     |            the parameter type `T` must be valid for the anonymous lifetime as defined here...
    |     ...so that the reference type `&(T, U)` does not outlive the data it points at
    |
 help: consider adding an explicit lifetime bound
    |
-LL |     async fn foo<'a>(&'a self) -> &'a (T, U) where U: 'a;
+LL |     async fn foo<'a>(&'a self) -> &'a (T, U) where T: 'a;
    |                 ++++  ++           ++        +++++++++++
 
-error[E0311]: the parameter type `T` may not live long enough
+error[E0311]: the parameter type `U` may not live long enough
   --> $DIR/async-generics.rs:6:5
    |
 LL |     async fn foo(&self) -> &(T, U);
    |     ^^^^^^^^^^^^^-^^^^^^^^^^^^^^^^^
    |     |            |
-   |     |            the parameter type `T` must be valid for the anonymous lifetime as defined here...
+   |     |            the parameter type `U` must be valid for the anonymous lifetime as defined here...
    |     ...so that the reference type `&(T, U)` does not outlive the data it points at
    |
 help: consider adding an explicit lifetime bound
    |
-LL |     async fn foo<'a>(&'a self) -> &'a (T, U) where T: 'a;
+LL |     async fn foo<'a>(&'a self) -> &'a (T, U) where U: 'a;
    |                 ++++  ++           ++        +++++++++++
 
 error: aborting due to 2 previous errors
diff --git a/tests/ui/compare-method/bad-self-type.stderr b/tests/ui/compare-method/bad-self-type.stderr
index 29ebbc5dffb..a3a31f43447 100644
--- a/tests/ui/compare-method/bad-self-type.stderr
+++ b/tests/ui/compare-method/bad-self-type.stderr
@@ -41,7 +41,7 @@ note: type in trait
 LL |     fn bar(self) -> Option<()>;
    |                     ^^^^^^^^^^
    = note: expected signature `fn(MyFuture) -> Option<()>`
-              found signature `fn(MyFuture)`
+              found signature `fn(MyFuture) -> ()`
 help: change the output type to match the trait
    |
 LL |     fn bar(self) -> Option<()> {}
diff --git a/tests/ui/error-codes/E0308.stderr b/tests/ui/error-codes/E0308.stderr
index bc6c5a632a1..709b3119276 100644
--- a/tests/ui/error-codes/E0308.stderr
+++ b/tests/ui/error-codes/E0308.stderr
@@ -5,7 +5,7 @@ LL |     fn size_of<T>();
    |                    ^ expected `usize`, found `()`
    |
    = note: expected signature `extern "rust-intrinsic" fn() -> usize`
-              found signature `extern "rust-intrinsic" fn()`
+              found signature `extern "rust-intrinsic" fn() -> ()`
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.stderr b/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.stderr
index 06a606b09dc..584724dfe59 100644
--- a/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.stderr
+++ b/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.stderr
@@ -92,7 +92,7 @@ LL |     extern "rust-call" fn call(self, args: ()) -> () {}
    |                                ^^^^ expected `&Foo`, found `Foo`
    |
    = note: expected signature `extern "rust-call" fn(&Foo, ()) -> _`
-              found signature `extern "rust-call" fn(Foo, ())`
+              found signature `extern "rust-call" fn(Foo, ()) -> ()`
 help: change the self-receiver type to match the trait
    |
 LL |     extern "rust-call" fn call(&self, args: ()) -> () {}
@@ -162,7 +162,7 @@ LL |     extern "rust-call" fn call_mut(&self, args: ()) -> () {}
    |                                    ^^^^^ types differ in mutability
    |
    = note: expected signature `extern "rust-call" fn(&mut Bar, ()) -> _`
-              found signature `extern "rust-call" fn(&Bar, ())`
+              found signature `extern "rust-call" fn(&Bar, ()) -> ()`
 help: change the self-receiver type to match the trait
    |
 LL |     extern "rust-call" fn call_mut(&mut self, args: ()) -> () {}
diff --git a/tests/ui/fn/fn_def_opaque_coercion_to_fn_ptr.stderr b/tests/ui/fn/fn_def_opaque_coercion_to_fn_ptr.stderr
index 0b3331b040d..5000601e90f 100644
--- a/tests/ui/fn/fn_def_opaque_coercion_to_fn_ptr.stderr
+++ b/tests/ui/fn/fn_def_opaque_coercion_to_fn_ptr.stderr
@@ -10,7 +10,7 @@ LL |     x = foo::<()>;
    |         ^^^^^^^^^ expected fn item, found a different fn item
    |
    = note: expected fn item `fn(F) -> F {bar::<F>}`
-              found fn item `fn(()) {foo::<()>}`
+              found fn item `fn(()) -> () {foo::<()>}`
 
 error[E0308]: mismatched types
   --> $DIR/fn_def_opaque_coercion_to_fn_ptr.rs:27:9
@@ -26,7 +26,7 @@ LL |     let mut x = bar::<()>;
 LL |     x = foo::<I>;
    |         ^^^^^^^^ expected fn item, found a different fn item
    |
-   = note: expected fn item `fn(()) {bar::<()>}`
+   = note: expected fn item `fn(()) -> () {bar::<()>}`
               found fn item `fn(I) -> I {foo::<I>}`
 help: use parentheses to call this function
    |
diff --git a/tests/ui/impl-trait/in-assoc-type-unconstrained.stderr b/tests/ui/impl-trait/in-assoc-type-unconstrained.stderr
index e32c59a75c6..75cbe43eeb4 100644
--- a/tests/ui/impl-trait/in-assoc-type-unconstrained.stderr
+++ b/tests/ui/impl-trait/in-assoc-type-unconstrained.stderr
@@ -35,7 +35,7 @@ note: type in trait
 LL |         fn method() -> Self::Ty;
    |                        ^^^^^^^^
    = note: expected signature `fn() -> <() as compare_method::Trait>::Ty`
-              found signature `fn()`
+              found signature `fn() -> ()`
 note: this item must have the opaque type in its signature in order to be able to register hidden types
   --> $DIR/in-assoc-type-unconstrained.rs:22:12
    |
diff --git a/tests/ui/impl-trait/trait_type.stderr b/tests/ui/impl-trait/trait_type.stderr
index 0eb132c7a19..989779a6178 100644
--- a/tests/ui/impl-trait/trait_type.stderr
+++ b/tests/ui/impl-trait/trait_type.stderr
@@ -5,7 +5,7 @@ LL |    fn fmt(&self, x: &str) -> () { }
    |                     ^^^^ types differ in mutability
    |
    = note: expected signature `fn(&MyType, &mut Formatter<'_>) -> Result<(), std::fmt::Error>`
-              found signature `fn(&MyType, &str)`
+              found signature `fn(&MyType, &str) -> ()`
 help: change the parameter type to match the trait
    |
 LL |    fn fmt(&self, x: &mut Formatter<'_>) -> () { }
diff --git a/tests/ui/lang-items/start_lang_item_args.missing_ret.stderr b/tests/ui/lang-items/start_lang_item_args.missing_ret.stderr
index aa1b1b73bae..2672efe51c9 100644
--- a/tests/ui/lang-items/start_lang_item_args.missing_ret.stderr
+++ b/tests/ui/lang-items/start_lang_item_args.missing_ret.stderr
@@ -5,7 +5,7 @@ LL | fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpi
    |                                                                                   ^ expected `isize`, found `()`
    |
    = note: expected signature `fn(fn() -> _, _, _, _) -> isize`
-              found signature `fn(fn() -> _, _, _, _)`
+              found signature `fn(fn() -> _, _, _, _) -> ()`
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/method-output-diff-issue-127263.rs b/tests/ui/method-output-diff-issue-127263.rs
new file mode 100644
index 00000000000..85a903e2453
--- /dev/null
+++ b/tests/ui/method-output-diff-issue-127263.rs
@@ -0,0 +1,8 @@
+fn bar() {}
+fn foo(x: i32) -> u32 {
+    0
+}
+fn main() {
+    let b: fn() -> u32 = bar; //~ ERROR mismatched types [E0308]
+    let f: fn(i32) = foo; //~ ERROR mismatched types [E0308]
+}
diff --git a/tests/ui/method-output-diff-issue-127263.stderr b/tests/ui/method-output-diff-issue-127263.stderr
new file mode 100644
index 00000000000..35b86114f16
--- /dev/null
+++ b/tests/ui/method-output-diff-issue-127263.stderr
@@ -0,0 +1,25 @@
+error[E0308]: mismatched types
+  --> $DIR/method-output-diff-issue-127263.rs:6:26
+   |
+LL |     let b: fn() -> u32 = bar;
+   |            -----------   ^^^ expected fn pointer, found fn item
+   |            |
+   |            expected due to this
+   |
+   = note: expected fn pointer `fn() -> u32`
+                 found fn item `fn() -> () {bar}`
+
+error[E0308]: mismatched types
+  --> $DIR/method-output-diff-issue-127263.rs:7:22
+   |
+LL |     let f: fn(i32) = foo;
+   |            -------   ^^^ expected fn pointer, found fn item
+   |            |
+   |            expected due to this
+   |
+   = note: expected fn pointer `fn(_) -> ()`
+                 found fn item `fn(_) -> u32 {foo}`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/panic-handler/panic-handler-bad-signature-1.stderr b/tests/ui/panic-handler/panic-handler-bad-signature-1.stderr
index 812f7a0692f..4fea52fec6e 100644
--- a/tests/ui/panic-handler/panic-handler-bad-signature-1.stderr
+++ b/tests/ui/panic-handler/panic-handler-bad-signature-1.stderr
@@ -5,7 +5,7 @@ LL | fn panic(info: PanicInfo) -> () {}
    |                ^^^^^^^^^ expected `&PanicInfo<'_>`, found `PanicInfo<'_>`
    |
    = note: expected signature `for<'a, 'b> fn(&'a PanicInfo<'b>) -> !`
-              found signature `for<'a> fn(PanicInfo<'a>)`
+              found signature `for<'a> fn(PanicInfo<'a>) -> ()`
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/traits/impl-method-mismatch.stderr b/tests/ui/traits/impl-method-mismatch.stderr
index 77d542c729a..db457b77a23 100644
--- a/tests/ui/traits/impl-method-mismatch.stderr
+++ b/tests/ui/traits/impl-method-mismatch.stderr
@@ -10,7 +10,7 @@ note: type in trait
 LL |     fn jumbo(&self, x: &usize) -> usize;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = note: expected signature `fn(&_, &_) -> usize`
-              found signature `unsafe fn(&_, &_)`
+              found signature `unsafe fn(&_, &_) -> ()`
 
 error: aborting due to 1 previous error