about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock1
-rw-r--r--compiler/rustc_hir_analysis/messages.ftl8
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/orphan.rs312
-rw-r--r--compiler/rustc_hir_analysis/src/errors.rs35
-rw-r--r--compiler/rustc_lint_defs/src/builtin.rs66
-rw-r--r--compiler/rustc_trait_selection/src/traits/coherence.rs177
-rw-r--r--compiler/rustc_trait_selection/src/traits/mod.rs5
-rw-r--r--src/bootstrap/src/core/build_steps/dist.rs17
-rw-r--r--src/tools/compiletest/src/runtest.rs1
-rw-r--r--src/tools/run-make-support/Cargo.toml1
-rw-r--r--src/tools/run-make-support/src/diff/mod.rs28
-rw-r--r--src/tools/run-make-support/src/diff/tests.rs22
-rw-r--r--src/tools/run-make-support/src/lib.rs1
-rw-r--r--src/tools/tidy/src/allowed_run_make_makefiles.txt1
-rw-r--r--tests/assembly/manual-eq-efficient.rs22
-rw-r--r--tests/run-make/repr128-dwarf/Makefile16
-rw-r--r--tests/run-make/repr128-dwarf/main.rs (renamed from tests/run-make/repr128-dwarf/lib.rs)5
-rw-r--r--tests/run-make/repr128-dwarf/rmake.rs75
-rw-r--r--tests/ui/coherence/auxiliary/parametrized-trait.rs2
-rw-r--r--tests/ui/coherence/auxiliary/trait-with-assoc-ty.rs3
-rw-r--r--tests/ui/coherence/coherence-overlap-unnormalizable-projection-0.classic.stderr19
-rw-r--r--tests/ui/coherence/coherence-overlap-unnormalizable-projection-0.next.stderr18
-rw-r--r--tests/ui/coherence/coherence-overlap-unnormalizable-projection-0.rs42
-rw-r--r--tests/ui/coherence/coherence-overlap-unnormalizable-projection-1.classic.stderr19
-rw-r--r--tests/ui/coherence/coherence-overlap-unnormalizable-projection-1.next.stderr19
-rw-r--r--tests/ui/coherence/coherence-overlap-unnormalizable-projection-1.rs44
-rw-r--r--tests/ui/coherence/orphan-check-projections-covering.rs25
-rw-r--r--tests/ui/coherence/orphan-check-projections-nested.rs23
-rw-r--r--tests/ui/coherence/orphan-check-projections-not-covering-ambiguity.classic.stderr15
-rw-r--r--tests/ui/coherence/orphan-check-projections-not-covering-ambiguity.next.stderr15
-rw-r--r--tests/ui/coherence/orphan-check-projections-not-covering-ambiguity.rs29
-rw-r--r--tests/ui/coherence/orphan-check-projections-not-covering-multiple-params.classic.stderr26
-rw-r--r--tests/ui/coherence/orphan-check-projections-not-covering-multiple-params.next.stderr26
-rw-r--r--tests/ui/coherence/orphan-check-projections-not-covering-multiple-params.rs24
-rw-r--r--tests/ui/coherence/orphan-check-projections-not-covering.classic.stderr37
-rw-r--r--tests/ui/coherence/orphan-check-projections-not-covering.next.stderr37
-rw-r--r--tests/ui/coherence/orphan-check-projections-not-covering.rs42
-rw-r--r--tests/ui/coherence/orphan-check-projections-unsat-bounds.classic.stderr15
-rw-r--r--tests/ui/coherence/orphan-check-projections-unsat-bounds.next.stderr15
-rw-r--r--tests/ui/coherence/orphan-check-projections-unsat-bounds.rs35
-rw-r--r--tests/ui/coherence/orphan-check-weak-aliases-covering.rs20
-rw-r--r--tests/ui/coherence/orphan-check-weak-aliases-not-covering.classic.stderr12
-rw-r--r--tests/ui/coherence/orphan-check-weak-aliases-not-covering.next.stderr12
-rw-r--r--tests/ui/coherence/orphan-check-weak-aliases-not-covering.rs19
-rw-r--r--tests/ui/type-alias-impl-trait/coherence.stderr2
45 files changed, 1218 insertions, 170 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 656e9d40d30..ca69536855e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3318,6 +3318,7 @@ dependencies = [
 name = "run_make_support"
 version = "0.0.0"
 dependencies = [
+ "gimli",
  "object 0.34.0",
  "regex",
  "similar",
diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl
index 2143caf2dee..3edea0191fa 100644
--- a/compiler/rustc_hir_analysis/messages.ftl
+++ b/compiler/rustc_hir_analysis/messages.ftl
@@ -492,13 +492,13 @@ hir_analysis_transparent_non_zero_sized_enum = the variant of a transparent {$de
 
 hir_analysis_ty_of_assoc_const_binding_note = `{$assoc_const}` has type `{$ty}`
 
-hir_analysis_ty_param_first_local = type parameter `{$param_ty}` must be covered by another type when it appears before the first local type (`{$local_type}`)
-    .label = type parameter `{$param_ty}` must be covered by another type when it appears before the first local type (`{$local_type}`)
+hir_analysis_ty_param_first_local = type parameter `{$param}` must be covered by another type when it appears before the first local type (`{$local_type}`)
+    .label = type parameter `{$param}` must be covered by another type when it appears before the first local type (`{$local_type}`)
     .note = implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type
     .case_note = in this case, 'before' refers to the following order: `impl<..> ForeignTrait<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last
 
-hir_analysis_ty_param_some = type parameter `{$param_ty}` must be used as the type parameter for some local type (e.g., `MyStruct<{$param_ty}>`)
-    .label = type parameter `{$param_ty}` must be used as the type parameter for some local type
+hir_analysis_ty_param_some = type parameter `{$param}` must be used as the type parameter for some local type (e.g., `MyStruct<{$param}>`)
+    .label = type parameter `{$param}` must be used as the type parameter for some local type
     .note = implementing a foreign trait is only possible if at least one of the types for which it is implemented is local
     .only_note = only traits defined in the current crate can be implemented for a type parameter
 
diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs
index 5585d2e069c..a256d11fd5a 100644
--- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs
@@ -2,14 +2,20 @@
 //! crate or pertains to a type defined in this crate.
 
 use crate::errors;
+
+use rustc_data_structures::fx::FxIndexSet;
 use rustc_errors::ErrorGuaranteed;
-use rustc_hir as hir;
-use rustc_middle::ty::{self, AliasKind, TyCtxt, TypeVisitableExt};
-use rustc_span::def_id::LocalDefId;
-use rustc_span::Span;
-use rustc_trait_selection::traits::{self, IsFirstInputType};
+use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
+use rustc_lint_defs::builtin::UNCOVERED_PARAM_IN_PROJECTION;
+use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::ty::{TypeFoldable, TypeFolder, TypeSuperFoldable};
+use rustc_middle::ty::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor};
+use rustc_span::def_id::{DefId, LocalDefId};
+use rustc_trait_selection::traits::{self, IsFirstInputType, UncoveredTyParams};
+use rustc_trait_selection::traits::{OrphanCheckErr, OrphanCheckMode};
+use rustc_trait_selection::traits::{StructurallyNormalizeExt, TraitEngineExt};
 
-#[instrument(skip(tcx), level = "debug")]
+#[instrument(level = "debug", skip(tcx))]
 pub(crate) fn orphan_check_impl(
     tcx: TyCtxt<'_>,
     impl_def_id: LocalDefId,
@@ -17,31 +23,23 @@ pub(crate) fn orphan_check_impl(
     let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().instantiate_identity();
     trait_ref.error_reported()?;
 
-    let trait_def_id = trait_ref.def_id;
-
-    match traits::orphan_check(tcx, impl_def_id.to_def_id()) {
+    match orphan_check(tcx, impl_def_id, OrphanCheckMode::Proper) {
         Ok(()) => {}
-        Err(err) => {
-            let item = tcx.hir().expect_item(impl_def_id);
-            let hir::ItemKind::Impl(impl_) = item.kind else {
-                bug!("{:?} is not an impl: {:?}", impl_def_id, item);
-            };
-            let tr = impl_.of_trait.as_ref().unwrap();
-            let sp = tcx.def_span(impl_def_id);
-
-            emit_orphan_check_error(
-                tcx,
-                sp,
-                item.span,
-                tr.path.span,
-                trait_ref,
-                impl_.self_ty.span,
-                impl_.generics,
-                err,
-            )?
-        }
+        Err(err) => match orphan_check(tcx, impl_def_id, OrphanCheckMode::Compat) {
+            Ok(()) => match err {
+                OrphanCheckErr::UncoveredTyParams(uncovered_ty_params) => {
+                    lint_uncovered_ty_params(tcx, uncovered_ty_params, impl_def_id)
+                }
+                OrphanCheckErr::NonLocalInputType(_) => {
+                    bug!("orphanck: shouldn't've gotten non-local input tys in compat mode")
+                }
+            },
+            Err(err) => return Err(emit_orphan_check_error(tcx, trait_ref, impl_def_id, err)),
+        },
     }
 
+    let trait_def_id = trait_ref.def_id;
+
     // In addition to the above rules, we restrict impls of auto traits
     // so that they can only be implemented on nominal types, such as structs,
     // enums or foreign types. To see why this restriction exists, consider the
@@ -186,13 +184,13 @@ pub(crate) fn orphan_check_impl(
                     //     type This = T;
                     // }
                     // impl<T: ?Sized> AutoTrait for <T as Id>::This {}
-                    AliasKind::Projection => "associated type",
+                    ty::Projection => "associated type",
                     // type Foo = (impl Sized, bool)
                     // impl AutoTrait for Foo {}
-                    AliasKind::Weak => "type alias",
+                    ty::Weak => "type alias",
                     // type Opaque = impl Trait;
                     // impl AutoTrait for Opaque {}
-                    AliasKind::Opaque => "opaque type",
+                    ty::Opaque => "opaque type",
                     // ```
                     // struct S<T>(T);
                     // impl<T: ?Sized> S<T> {
@@ -201,7 +199,7 @@ pub(crate) fn orphan_check_impl(
                     // impl<T: ?Sized> AutoTrait for S<T>::This {}
                     // ```
                     // FIXME(inherent_associated_types): The example code above currently leads to a cycle
-                    AliasKind::Inherent => "associated type",
+                    ty::Inherent => "associated type",
                 };
                 (LocalImpl::Disallow { problematic_kind }, NonlocalImpl::DisallowOther)
             }
@@ -275,34 +273,125 @@ pub(crate) fn orphan_check_impl(
     Ok(())
 }
 
+/// Checks the coherence orphan rules.
+///
+/// `impl_def_id` should be the `DefId` of a trait impl.
+///
+/// To pass, either the trait must be local, or else two conditions must be satisfied:
+///
+/// 1. All type parameters in `Self` must be "covered" by some local type constructor.
+/// 2. Some local type must appear in `Self`.
+#[instrument(level = "debug", skip(tcx), ret)]
+fn orphan_check<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    impl_def_id: LocalDefId,
+    mode: OrphanCheckMode,
+) -> Result<(), OrphanCheckErr<'tcx, FxIndexSet<DefId>>> {
+    // We only accept this routine to be invoked on implementations
+    // of a trait, not inherent implementations.
+    let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
+    debug!(trait_ref = ?trait_ref.skip_binder());
+
+    // If the *trait* is local to the crate, ok.
+    if let Some(def_id) = trait_ref.skip_binder().def_id.as_local() {
+        debug!("trait {def_id:?} is local to current crate");
+        return Ok(());
+    }
+
+    // (1)  Instantiate all generic params with fresh inference vars.
+    let infcx = tcx.infer_ctxt().intercrate(true).build();
+    let cause = traits::ObligationCause::dummy();
+    let args = infcx.fresh_args_for_item(cause.span, impl_def_id.to_def_id());
+    let trait_ref = trait_ref.instantiate(tcx, args);
+
+    let lazily_normalize_ty = |user_ty: Ty<'tcx>| {
+        let ty::Alias(..) = user_ty.kind() else { return Ok(user_ty) };
+
+        let ocx = traits::ObligationCtxt::new(&infcx);
+        let ty = ocx.normalize(&cause, ty::ParamEnv::empty(), user_ty);
+        let ty = infcx.resolve_vars_if_possible(ty);
+        let errors = ocx.select_where_possible();
+        if !errors.is_empty() {
+            return Ok(user_ty);
+        }
+
+        let ty = if infcx.next_trait_solver() {
+            let mut fulfill_cx = <dyn traits::TraitEngine<'_>>::new(&infcx);
+            infcx
+                .at(&cause, ty::ParamEnv::empty())
+                .structurally_normalize(ty, &mut *fulfill_cx)
+                .map(|ty| infcx.resolve_vars_if_possible(ty))
+                .unwrap_or(ty)
+        } else {
+            ty
+        };
+
+        Ok(ty)
+    };
+
+    let Ok(result) = traits::orphan_check_trait_ref::<!>(
+        trait_ref,
+        traits::InCrate::Local { mode },
+        lazily_normalize_ty,
+    ) else {
+        unreachable!()
+    };
+
+    // (2)  Try to map the remaining inference vars back to generic params.
+    result.map_err(|err| match err {
+        OrphanCheckErr::UncoveredTyParams(UncoveredTyParams { uncovered, local_ty }) => {
+            let mut collector =
+                UncoveredTyParamCollector { infcx: &infcx, uncovered_params: Default::default() };
+            uncovered.visit_with(&mut collector);
+            // FIXME(fmease): This is very likely reachable.
+            debug_assert!(!collector.uncovered_params.is_empty());
+
+            OrphanCheckErr::UncoveredTyParams(UncoveredTyParams {
+                uncovered: collector.uncovered_params,
+                local_ty,
+            })
+        }
+        OrphanCheckErr::NonLocalInputType(tys) => {
+            let generics = tcx.generics_of(impl_def_id);
+            let tys = tys
+                .into_iter()
+                .map(|(ty, is_target_ty)| {
+                    (ty.fold_with(&mut TyVarReplacer { infcx: &infcx, generics }), is_target_ty)
+                })
+                .collect();
+            OrphanCheckErr::NonLocalInputType(tys)
+        }
+    })
+}
+
 fn emit_orphan_check_error<'tcx>(
     tcx: TyCtxt<'tcx>,
-    sp: Span,
-    full_impl_span: Span,
-    trait_span: Span,
     trait_ref: ty::TraitRef<'tcx>,
-    self_ty_span: Span,
-    generics: &hir::Generics<'tcx>,
-    err: traits::OrphanCheckErr<'tcx>,
-) -> Result<!, ErrorGuaranteed> {
-    let self_ty = trait_ref.self_ty();
-    Err(match err {
+    impl_def_id: LocalDefId,
+    err: traits::OrphanCheckErr<'tcx, FxIndexSet<DefId>>,
+) -> ErrorGuaranteed {
+    match err {
         traits::OrphanCheckErr::NonLocalInputType(tys) => {
-            let mut diag = tcx.dcx().create_err(match self_ty.kind() {
-                ty::Adt(..) => errors::OnlyCurrentTraits::Outside { span: sp, note: () },
-                _ if self_ty.is_primitive() => {
-                    errors::OnlyCurrentTraits::Primitive { span: sp, note: () }
+            let item = tcx.hir().expect_item(impl_def_id);
+            let impl_ = item.expect_impl();
+            let hir_trait_ref = impl_.of_trait.as_ref().unwrap();
+
+            let span = tcx.def_span(impl_def_id);
+            let mut diag = tcx.dcx().create_err(match trait_ref.self_ty().kind() {
+                ty::Adt(..) => errors::OnlyCurrentTraits::Outside { span, note: () },
+                _ if trait_ref.self_ty().is_primitive() => {
+                    errors::OnlyCurrentTraits::Primitive { span, note: () }
                 }
-                _ => errors::OnlyCurrentTraits::Arbitrary { span: sp, note: () },
+                _ => errors::OnlyCurrentTraits::Arbitrary { span, note: () },
             });
 
             for &(mut ty, is_target_ty) in &tys {
                 let span = if matches!(is_target_ty, IsFirstInputType::Yes) {
                     // Point at `D<A>` in `impl<A, B> for C<B> in D<A>`
-                    self_ty_span
+                    impl_.self_ty.span
                 } else {
                     // Point at `C<B>` in `impl<A, B> for C<B> in D<A>`
-                    trait_span
+                    hir_trait_ref.path.span
                 };
 
                 ty = tcx.erase_regions(ty);
@@ -354,12 +443,12 @@ fn emit_orphan_check_error<'tcx>(
                         diag.subdiagnostic(tcx.dcx(), errors::OnlyCurrentTraitsOpaque { span });
                     }
                     ty::RawPtr(ptr_ty, mutbl) => {
-                        if !self_ty.has_param() {
+                        if !trait_ref.self_ty().has_param() {
                             diag.subdiagnostic(
                                 tcx.dcx(),
                                 errors::OnlyCurrentTraitsPointerSugg {
-                                    wrapper_span: self_ty_span,
-                                    struct_span: full_impl_span.shrink_to_lo(),
+                                    wrapper_span: impl_.self_ty.span,
+                                    struct_span: item.span.shrink_to_lo(),
                                     mut_key: mutbl.prefix_str(),
                                     ptr_ty,
                                 },
@@ -387,23 +476,112 @@ fn emit_orphan_check_error<'tcx>(
 
             diag.emit()
         }
-        traits::OrphanCheckErr::UncoveredTy(param_ty, local_type) => {
-            let mut sp = sp;
-            for param in generics.params {
-                if param.name.ident().to_string() == param_ty.to_string() {
-                    sp = param.span;
-                }
-            }
+        traits::OrphanCheckErr::UncoveredTyParams(UncoveredTyParams { uncovered, local_ty }) => {
+            let mut reported = None;
+            for param_def_id in uncovered {
+                let span = tcx.def_ident_span(param_def_id).unwrap();
+                let name = tcx.item_name(param_def_id);
 
-            match local_type {
-                Some(local_type) => tcx.dcx().emit_err(errors::TyParamFirstLocal {
-                    span: sp,
-                    note: (),
-                    param_ty,
-                    local_type,
-                }),
-                None => tcx.dcx().emit_err(errors::TyParamSome { span: sp, note: (), param_ty }),
+                reported.get_or_insert(match local_ty {
+                    Some(local_type) => tcx.dcx().emit_err(errors::TyParamFirstLocal {
+                        span,
+                        note: (),
+                        param: name,
+                        local_type,
+                    }),
+                    None => tcx.dcx().emit_err(errors::TyParamSome { span, note: (), param: name }),
+                });
             }
+            reported.unwrap() // FIXME(fmease): This is very likely reachable.
         }
-    })
+    }
+}
+
+fn lint_uncovered_ty_params<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    UncoveredTyParams { uncovered, local_ty }: UncoveredTyParams<'tcx, FxIndexSet<DefId>>,
+    impl_def_id: LocalDefId,
+) {
+    let hir_id = tcx.local_def_id_to_hir_id(impl_def_id);
+
+    for param_def_id in uncovered {
+        let span = tcx.def_ident_span(param_def_id).unwrap();
+        let name = tcx.item_name(param_def_id);
+
+        match local_ty {
+            Some(local_type) => tcx.emit_node_span_lint(
+                UNCOVERED_PARAM_IN_PROJECTION,
+                hir_id,
+                span,
+                errors::TyParamFirstLocalLint { span, note: (), param: name, local_type },
+            ),
+            None => tcx.emit_node_span_lint(
+                UNCOVERED_PARAM_IN_PROJECTION,
+                hir_id,
+                span,
+                errors::TyParamSomeLint { span, note: (), param: name },
+            ),
+        };
+    }
+}
+
+struct UncoveredTyParamCollector<'cx, 'tcx> {
+    infcx: &'cx InferCtxt<'tcx>,
+    uncovered_params: FxIndexSet<DefId>,
+}
+
+impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for UncoveredTyParamCollector<'_, 'tcx> {
+    fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
+        if !ty.has_type_flags(ty::TypeFlags::HAS_TY_INFER) {
+            return;
+        }
+        let Some(origin) = self.infcx.type_var_origin(ty) else {
+            return ty.super_visit_with(self);
+        };
+        if let Some(def_id) = origin.param_def_id {
+            self.uncovered_params.insert(def_id);
+        }
+    }
+
+    fn visit_const(&mut self, ct: ty::Const<'tcx>) -> Self::Result {
+        if ct.has_type_flags(ty::TypeFlags::HAS_TY_INFER) {
+            ct.super_visit_with(self)
+        }
+    }
+}
+
+struct TyVarReplacer<'cx, 'tcx> {
+    infcx: &'cx InferCtxt<'tcx>,
+    generics: &'tcx ty::Generics,
+}
+
+impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for TyVarReplacer<'cx, 'tcx> {
+    fn interner(&self) -> TyCtxt<'tcx> {
+        self.infcx.tcx
+    }
+
+    fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
+        if !ty.has_type_flags(ty::TypeFlags::HAS_TY_INFER) {
+            return ty;
+        }
+        let Some(origin) = self.infcx.type_var_origin(ty) else {
+            return ty.super_fold_with(self);
+        };
+        if let Some(def_id) = origin.param_def_id {
+            // The generics of an `impl` don't have a parent, we can index directly.
+            let index = self.generics.param_def_id_to_index[&def_id];
+            let name = self.generics.params[index as usize].name;
+
+            Ty::new_param(self.infcx.tcx, index, name)
+        } else {
+            ty
+        }
+    }
+
+    fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
+        if !ct.has_type_flags(ty::TypeFlags::HAS_TY_INFER) {
+            return ct;
+        }
+        ct.super_fold_with(self)
+    }
 }
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index cc8f1495e3c..1c99713b3ae 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -1355,29 +1355,54 @@ pub struct CrossCrateTraitsDefined {
     pub traits: String,
 }
 
+// FIXME(fmease): Deduplicate:
+
 #[derive(Diagnostic)]
 #[diag(hir_analysis_ty_param_first_local, code = E0210)]
 #[note]
-pub struct TyParamFirstLocal<'a> {
+pub struct TyParamFirstLocal<'tcx> {
     #[primary_span]
     #[label]
     pub span: Span,
     #[note(hir_analysis_case_note)]
     pub note: (),
-    pub param_ty: Ty<'a>,
-    pub local_type: Ty<'a>,
+    pub param: Symbol,
+    pub local_type: Ty<'tcx>,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(hir_analysis_ty_param_first_local, code = E0210)]
+#[note]
+pub struct TyParamFirstLocalLint<'tcx> {
+    #[label]
+    pub span: Span,
+    #[note(hir_analysis_case_note)]
+    pub note: (),
+    pub param: Symbol,
+    pub local_type: Ty<'tcx>,
 }
 
 #[derive(Diagnostic)]
 #[diag(hir_analysis_ty_param_some, code = E0210)]
 #[note]
-pub struct TyParamSome<'a> {
+pub struct TyParamSome {
     #[primary_span]
     #[label]
     pub span: Span,
     #[note(hir_analysis_only_note)]
     pub note: (),
-    pub param_ty: Ty<'a>,
+    pub param: Symbol,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(hir_analysis_ty_param_some, code = E0210)]
+#[note]
+pub struct TyParamSomeLint {
+    #[label]
+    pub span: Span,
+    #[note(hir_analysis_only_note)]
+    pub note: (),
+    pub param: Symbol,
 }
 
 #[derive(Diagnostic)]
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index e74cc388cab..86a0f33a8d1 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -101,6 +101,7 @@ declare_lint_pass! {
         TYVAR_BEHIND_RAW_POINTER,
         UNCONDITIONAL_PANIC,
         UNCONDITIONAL_RECURSION,
+        UNCOVERED_PARAM_IN_PROJECTION,
         UNDEFINED_NAKED_FUNCTION_ABI,
         UNEXPECTED_CFGS,
         UNFULFILLED_LINT_EXPECTATIONS,
@@ -4741,3 +4742,68 @@ declare_lint! {
     };
     crate_level_only
 }
+
+declare_lint! {
+    /// The `uncovered_param_in_projection` lint detects a violation of one of Rust's orphan rules for
+    /// foreign trait implementations that concerns the use of type parameters inside trait associated
+    /// type paths ("projections") whose output may not be a local type that is mistakenly considered
+    /// to "cover" said parameters which is **unsound** and which may be rejected by a future version
+    /// of the compiler.
+    ///
+    /// Originally reported in [#99554].
+    ///
+    /// [#99554]: https://github.com/rust-lang/rust/issues/99554
+    ///
+    /// ### Example
+    ///
+    /// ```rust,ignore (dependent)
+    /// // dependency.rs
+    /// #![crate_type = "lib"]
+    ///
+    /// pub trait Trait<T, U> {}
+    /// ```
+    ///
+    /// ```edition2021,ignore (needs dependency)
+    /// // dependent.rs
+    /// trait Identity {
+    ///     type Output;
+    /// }
+    ///
+    /// impl<T> Identity for T {
+    ///     type Output = T;
+    /// }
+    ///
+    /// struct Local;
+    ///
+    /// impl<T> dependency::Trait<Local, T> for <T as Identity>::Output {}
+    ///
+    /// fn main() {}
+    /// ```
+    ///
+    /// This will produce:
+    ///
+    /// ```text
+    /// warning[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
+    ///   --> dependent.rs:11:6
+    ///    |
+    /// 11 | impl<T> dependency::Trait<Local, T> for <T as Identity>::Output {}
+    ///    |      ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
+    ///    |
+    ///    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+    ///    = note: for more information, see issue #124559 <https://github.com/rust-lang/rust/issues/124559>
+    ///    = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type
+    ///    = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last
+    ///    = note: `#[warn(uncovered_param_in_projection)]` on by default
+    /// ```
+    ///
+    /// ### Explanation
+    ///
+    /// FIXME(fmease): Write explainer.
+    pub UNCOVERED_PARAM_IN_PROJECTION,
+    Warn,
+    "impl contains type parameters that are not covered",
+    @future_incompatible = FutureIncompatibleInfo {
+        reason: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps,
+        reference: "issue #124559 <https://github.com/rust-lang/rust/issues/124559>",
+    };
+}
diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs
index c430da10b0c..730b7ea70c6 100644
--- a/compiler/rustc_trait_selection/src/traits/coherence.rs
+++ b/compiler/rustc_trait_selection/src/traits/coherence.rs
@@ -20,15 +20,15 @@ use crate::traits::{
 use rustc_data_structures::fx::FxIndexSet;
 use rustc_errors::{Diag, EmissionGuarantee};
 use rustc_hir::def::DefKind;
-use rustc_hir::def_id::{DefId, LOCAL_CRATE};
+use rustc_hir::def_id::DefId;
 use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt};
 use rustc_infer::traits::{util, FulfillmentErrorCode, TraitEngine, TraitEngineExt};
 use rustc_middle::traits::query::NoSolution;
 use rustc_middle::traits::solve::{CandidateSource, Certainty, Goal};
 use rustc_middle::traits::specialization_graph::OverlapMode;
 use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
-use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt};
-use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor};
+use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor};
+use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_span::symbol::sym;
 use rustc_span::{Span, DUMMY_SP};
 use std::fmt::Debug;
@@ -36,14 +36,28 @@ use std::ops::ControlFlow;
 
 use super::error_reporting::suggest_new_overflow_limit;
 
-/// Whether we do the orphan check relative to this crate or
-/// to some remote crate.
+/// Whether we do the orphan check relative to this crate or to some remote crate.
 #[derive(Copy, Clone, Debug)]
-enum InCrate {
-    Local,
+pub enum InCrate {
+    Local { mode: OrphanCheckMode },
     Remote,
 }
 
+#[derive(Copy, Clone, Debug)]
+pub enum OrphanCheckMode {
+    /// Proper orphan check.
+    Proper,
+    /// Improper orphan check for backward compatibility.
+    ///
+    /// In this mode, type params inside projections are considered to be covered
+    /// even if the projection may normalize to a type that doesn't actually cover
+    /// them. This is unsound. See also [#124559] and [#99554].
+    ///
+    /// [#124559]: https://github.com/rust-lang/rust/issues/124559
+    /// [#99554]: https://github.com/rust-lang/rust/issues/99554
+    Compat,
+}
+
 #[derive(Debug, Copy, Clone)]
 pub enum Conflict {
     Upstream,
@@ -633,7 +647,13 @@ pub fn trait_ref_is_knowable<'tcx, E: Debug>(
     // and if we are an intermediate owner, then we don't care
     // about future-compatibility, which means that we're OK if
     // we are an owner.
-    if orphan_check_trait_ref(trait_ref, InCrate::Local, &mut lazily_normalize_ty)?.is_ok() {
+    if orphan_check_trait_ref(
+        trait_ref,
+        InCrate::Local { mode: OrphanCheckMode::Proper },
+        &mut lazily_normalize_ty,
+    )?
+    .is_ok()
+    {
         Ok(Ok(()))
     } else {
         Ok(Err(Conflict::Upstream))
@@ -644,7 +664,7 @@ pub fn trait_ref_is_local_or_fundamental<'tcx>(
     tcx: TyCtxt<'tcx>,
     trait_ref: ty::TraitRef<'tcx>,
 ) -> bool {
-    trait_ref.def_id.krate == LOCAL_CRATE || tcx.has_attr(trait_ref.def_id, sym::fundamental)
+    trait_ref.def_id.is_local() || tcx.has_attr(trait_ref.def_id, sym::fundamental)
 }
 
 #[derive(Debug, Copy, Clone)]
@@ -663,31 +683,15 @@ impl From<bool> for IsFirstInputType {
 }
 
 #[derive(Debug)]
-pub enum OrphanCheckErr<'tcx> {
+pub enum OrphanCheckErr<'tcx, T> {
     NonLocalInputType(Vec<(Ty<'tcx>, IsFirstInputType)>),
-    UncoveredTy(Ty<'tcx>, Option<Ty<'tcx>>),
+    UncoveredTyParams(UncoveredTyParams<'tcx, T>),
 }
 
-/// Checks the coherence orphan rules. `impl_def_id` should be the
-/// `DefId` of a trait impl. To pass, either the trait must be local, or else
-/// two conditions must be satisfied:
-///
-/// 1. All type parameters in `Self` must be "covered" by some local type constructor.
-/// 2. Some local type must appear in `Self`.
-#[instrument(level = "debug", skip(tcx), ret)]
-pub fn orphan_check(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Result<(), OrphanCheckErr<'_>> {
-    // We only except this routine to be invoked on implementations
-    // of a trait, not inherent implementations.
-    let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().instantiate_identity();
-    debug!(?trait_ref);
-
-    // If the *trait* is local to the crate, ok.
-    if trait_ref.def_id.is_local() {
-        debug!("trait {:?} is local to current crate", trait_ref.def_id);
-        return Ok(());
-    }
-
-    orphan_check_trait_ref::<!>(trait_ref, InCrate::Local, |ty| Ok(ty)).unwrap()
+#[derive(Debug)]
+pub struct UncoveredTyParams<'tcx, T> {
+    pub uncovered: T,
+    pub local_ty: Option<Ty<'tcx>>,
 }
 
 /// Checks whether a trait-ref is potentially implementable by a crate.
@@ -735,6 +739,9 @@ pub fn orphan_check(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Result<(), OrphanChe
 ///    To check that a local impl follows the orphan rules, we check it in
 ///    InCrate::Local mode, using type parameters for the "generic" types.
 ///
+///    In InCrate::Local mode the orphan check succeeds if the current crate
+///    is definitely allowed to implement the given trait (no false positives).
+///
 /// 2. They ground negative reasoning for coherence. If a user wants to
 ///    write both a conditional blanket impl and a specific impl, we need to
 ///    make sure they do not overlap. For example, if we write
@@ -753,6 +760,9 @@ pub fn orphan_check(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Result<(), OrphanChe
 ///    try to implement this trait-ref. To check for this, we use InCrate::Remote
 ///    mode. That is sound because we already know all the impls from known crates.
 ///
+///    In InCrate::Remote mode the orphan check succeeds if a foreign crate
+///    *could* implement the given trait (no false negatives).
+///
 /// 3. For non-`#[fundamental]` traits, they guarantee that parent crates can
 ///    add "non-blanket" impls without breaking negative reasoning in dependent
 ///    crates. This is the "rebalancing coherence" (RFC 1023) restriction.
@@ -777,11 +787,11 @@ pub fn orphan_check(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Result<(), OrphanChe
 /// Note that this function is never called for types that have both type
 /// parameters and inference variables.
 #[instrument(level = "trace", skip(lazily_normalize_ty), ret)]
-fn orphan_check_trait_ref<'tcx, E: Debug>(
+pub fn orphan_check_trait_ref<'tcx, E: Debug>(
     trait_ref: ty::TraitRef<'tcx>,
     in_crate: InCrate,
     lazily_normalize_ty: impl FnMut(Ty<'tcx>) -> Result<Ty<'tcx>, E>,
-) -> Result<Result<(), OrphanCheckErr<'tcx>>, E> {
+) -> Result<Result<(), OrphanCheckErr<'tcx, Ty<'tcx>>>, E> {
     if trait_ref.has_infer() && trait_ref.has_param() {
         bug!(
             "can't orphan check a trait ref with both params and inference variables {:?}",
@@ -790,21 +800,28 @@ fn orphan_check_trait_ref<'tcx, E: Debug>(
     }
 
     let mut checker = OrphanChecker::new(in_crate, lazily_normalize_ty);
+
+    // Does there exist some local type after the `ParamTy`.
+    let search_first_local_ty = |checker: &mut OrphanChecker<'tcx, _>| {
+        checker.search_first_local_ty = true;
+        match trait_ref.visit_with(checker).break_value() {
+            Some(OrphanCheckEarlyExit::LocalTy(local_ty)) => Some(local_ty),
+            _ => None,
+        }
+    };
+
     Ok(match trait_ref.visit_with(&mut checker) {
         ControlFlow::Continue(()) => Err(OrphanCheckErr::NonLocalInputType(checker.non_local_tys)),
-        ControlFlow::Break(OrphanCheckEarlyExit::NormalizationFailure(err)) => return Err(err),
-        ControlFlow::Break(OrphanCheckEarlyExit::ParamTy(ty)) => {
-            // Does there exist some local type after the `ParamTy`.
-            checker.search_first_local_ty = true;
-            if let Some(OrphanCheckEarlyExit::LocalTy(local_ty)) =
-                trait_ref.visit_with(&mut checker).break_value()
-            {
-                Err(OrphanCheckErr::UncoveredTy(ty, Some(local_ty)))
-            } else {
-                Err(OrphanCheckErr::UncoveredTy(ty, None))
+        ControlFlow::Break(residual) => match residual {
+            OrphanCheckEarlyExit::NormalizationFailure(err) => return Err(err),
+            OrphanCheckEarlyExit::UncoveredTyParam(ty) => {
+                Err(OrphanCheckErr::UncoveredTyParams(UncoveredTyParams {
+                    uncovered: ty,
+                    local_ty: search_first_local_ty(&mut checker),
+                }))
             }
-        }
-        ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(_)) => Ok(()),
+            OrphanCheckEarlyExit::LocalTy(_) => Ok(()),
+        },
     })
 }
 
@@ -812,8 +829,7 @@ struct OrphanChecker<'tcx, F> {
     in_crate: InCrate,
     in_self_ty: bool,
     lazily_normalize_ty: F,
-    /// Ignore orphan check failures and exclusively search for the first
-    /// local type.
+    /// Ignore orphan check failures and exclusively search for the first local type.
     search_first_local_ty: bool,
     non_local_tys: Vec<(Ty<'tcx>, IsFirstInputType)>,
 }
@@ -837,17 +853,20 @@ where
         ControlFlow::Continue(())
     }
 
-    fn found_param_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<OrphanCheckEarlyExit<'tcx, E>> {
+    fn found_uncovered_ty_param(
+        &mut self,
+        ty: Ty<'tcx>,
+    ) -> ControlFlow<OrphanCheckEarlyExit<'tcx, E>> {
         if self.search_first_local_ty {
-            ControlFlow::Continue(())
-        } else {
-            ControlFlow::Break(OrphanCheckEarlyExit::ParamTy(t))
+            return ControlFlow::Continue(());
         }
+
+        ControlFlow::Break(OrphanCheckEarlyExit::UncoveredTyParam(ty))
     }
 
     fn def_id_is_local(&mut self, def_id: DefId) -> bool {
         match self.in_crate {
-            InCrate::Local => def_id.is_local(),
+            InCrate::Local { .. } => def_id.is_local(),
             InCrate::Remote => false,
         }
     }
@@ -855,7 +874,7 @@ where
 
 enum OrphanCheckEarlyExit<'tcx, E> {
     NormalizationFailure(E),
-    ParamTy(Ty<'tcx>),
+    UncoveredTyParam(Ty<'tcx>),
     LocalTy(Ty<'tcx>),
 }
 
@@ -864,14 +883,15 @@ where
     F: FnMut(Ty<'tcx>) -> Result<Ty<'tcx>, E>,
 {
     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>) -> Self::Result {
-        // Need to lazily normalize here in with `-Znext-solver=coherence`.
         let ty = match (self.lazily_normalize_ty)(ty) {
-            Ok(ty) => ty,
+            Ok(norm_ty) if norm_ty.is_ty_var() => ty,
+            Ok(norm_ty) => norm_ty,
             Err(err) => return ControlFlow::Break(OrphanCheckEarlyExit::NormalizationFailure(err)),
         };
 
@@ -889,19 +909,46 @@ where
             | ty::Slice(..)
             | ty::RawPtr(..)
             | ty::Never
-            | ty::Tuple(..)
-            | ty::Alias(ty::Projection | ty::Inherent | ty::Weak, ..) => {
-                self.found_non_local_ty(ty)
+            | ty::Tuple(..) => self.found_non_local_ty(ty),
+
+            ty::Param(..) => bug!("unexpected ty param"),
+
+            ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) => {
+                match self.in_crate {
+                    InCrate::Local { .. } => self.found_uncovered_ty_param(ty),
+                    // The inference variable might be unified with a local
+                    // type in that remote crate.
+                    InCrate::Remote => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)),
+                }
             }
 
-            ty::Param(..) => self.found_param_ty(ty),
+            ty::Alias(kind @ (ty::Projection | ty::Inherent | ty::Weak), ..) => {
+                if ty.has_type_flags(ty::TypeFlags::HAS_TY_PARAM) {
+                    bug!("unexpected ty param in alias ty");
+                }
 
-            ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) => match self.in_crate {
-                InCrate::Local => self.found_non_local_ty(ty),
-                // The inference variable might be unified with a local
-                // type in that remote crate.
-                InCrate::Remote => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)),
-            },
+                if ty.has_type_flags(
+                    ty::TypeFlags::HAS_TY_PLACEHOLDER
+                        | ty::TypeFlags::HAS_TY_BOUND
+                        | ty::TypeFlags::HAS_TY_INFER,
+                ) {
+                    match self.in_crate {
+                        InCrate::Local { mode } => match kind {
+                            ty::Projection if let OrphanCheckMode::Compat = mode => {
+                                ControlFlow::Continue(())
+                            }
+                            _ => self.found_uncovered_ty_param(ty),
+                        },
+                        InCrate::Remote => {
+                            // The inference variable might be unified with a local
+                            // type in that remote crate.
+                            ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty))
+                        }
+                    }
+                } else {
+                    ControlFlow::Continue(())
+                }
+            }
 
             // For fundamental types, we just look inside of them.
             ty::Ref(_, ty, _) => ty.visit_with(self),
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index 88ebf8754d3..56f8b4b9cdb 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -41,8 +41,9 @@ use rustc_span::Span;
 use std::fmt::Debug;
 use std::ops::ControlFlow;
 
-pub use self::coherence::{add_placeholder_note, orphan_check, overlapping_impls};
-pub use self::coherence::{IsFirstInputType, OrphanCheckErr, OverlapResult};
+pub use self::coherence::{add_placeholder_note, orphan_check_trait_ref, overlapping_impls};
+pub use self::coherence::{InCrate, IsFirstInputType, UncoveredTyParams};
+pub use self::coherence::{OrphanCheckErr, OrphanCheckMode, OverlapResult};
 pub use self::engine::{ObligationCtxt, TraitEngineExt};
 pub use self::fulfill::{FulfillmentContext, PendingPredicateObligation};
 pub use self::normalize::NormalizeExt;
diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs
index 770a5cdb232..5bc9d7615e2 100644
--- a/src/bootstrap/src/core/build_steps/dist.rs
+++ b/src/bootstrap/src/core/build_steps/dist.rs
@@ -25,7 +25,7 @@ use crate::core::build_steps::llvm;
 use crate::core::build_steps::tool::{self, Tool};
 use crate::core::builder::{Builder, Kind, RunConfig, ShouldRun, Step};
 use crate::core::config::TargetSelection;
-use crate::utils::channel;
+use crate::utils::channel::{self, Info};
 use crate::utils::helpers::{exe, is_dylib, output, t, target_supports_cranelift_backend, timeit};
 use crate::utils::tarball::{GeneratedTarball, OverlayKind, Tarball};
 use crate::{Compiler, DependencyType, Mode, LLVM_TOOLS};
@@ -991,10 +991,17 @@ impl Step for PlainSourceTarball {
 
         // Create the version file
         builder.create(&plain_dst_src.join("version"), &builder.rust_version());
-        if let Some(info) = builder.rust_info().info() {
-            channel::write_commit_hash_file(plain_dst_src, &info.sha);
-            channel::write_commit_info_file(plain_dst_src, info);
-        }
+
+        // Create the files containing git info, to ensure --version outputs the same.
+        let write_git_info = |info: Option<&Info>, path: &Path| {
+            if let Some(info) = info {
+                t!(std::fs::create_dir_all(path));
+                channel::write_commit_hash_file(path, &info.sha);
+                channel::write_commit_info_file(path, info);
+            }
+        };
+        write_git_info(builder.rust_info().info(), plain_dst_src);
+        write_git_info(builder.cargo_info.info(), &plain_dst_src.join("./src/tools/cargo"));
 
         // If we're building from git or tarball sources, we need to vendor
         // a complete distribution.
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index da51b33a9d6..0a861d62c37 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -3825,6 +3825,7 @@ impl<'test> TestCx<'test> {
             .arg(format!("-Ldependency={}", &support_lib_deps_deps.to_string_lossy()))
             .arg("--extern")
             .arg(format!("run_make_support={}", &support_lib_path.to_string_lossy()))
+            .arg("--edition=2021")
             .arg(&self.testpaths.file.join("rmake.rs"))
             .env("TARGET", &self.config.target)
             .env("PYTHON", &self.config.python)
diff --git a/src/tools/run-make-support/Cargo.toml b/src/tools/run-make-support/Cargo.toml
index 61a24c97e77..cf4ae4b16cd 100644
--- a/src/tools/run-make-support/Cargo.toml
+++ b/src/tools/run-make-support/Cargo.toml
@@ -8,3 +8,4 @@ object = "0.34.0"
 similar = "2.5.0"
 wasmparser = "0.118.2"
 regex = "1.8" # 1.8 to avoid memchr 2.6.0, as 2.5.0 is pinned in the workspace
+gimli = "0.28.1"
diff --git a/src/tools/run-make-support/src/diff/mod.rs b/src/tools/run-make-support/src/diff/mod.rs
index 54532c6e35b..332126939c0 100644
--- a/src/tools/run-make-support/src/diff/mod.rs
+++ b/src/tools/run-make-support/src/diff/mod.rs
@@ -1,3 +1,4 @@
+use regex::Regex;
 use similar::TextDiff;
 use std::path::Path;
 
@@ -14,12 +15,19 @@ pub struct Diff {
     expected_name: Option<String>,
     actual: Option<String>,
     actual_name: Option<String>,
+    normalizers: Vec<(String, String)>,
 }
 
 impl Diff {
     /// Construct a bare `diff` invocation.
     pub fn new() -> Self {
-        Self { expected: None, expected_name: None, actual: None, actual_name: None }
+        Self {
+            expected: None,
+            expected_name: None,
+            actual: None,
+            actual_name: None,
+            normalizers: Vec::new(),
+        }
     }
 
     /// Specify the expected output for the diff from a file.
@@ -58,15 +66,29 @@ impl Diff {
         self
     }
 
+    /// Specify a regex that should replace text in the "actual" text that will be compared.
+    pub fn normalize<R: Into<String>, I: Into<String>>(
+        &mut self,
+        regex: R,
+        replacement: I,
+    ) -> &mut Self {
+        self.normalizers.push((regex.into(), replacement.into()));
+        self
+    }
+
     /// Executes the diff process, prints any differences to the standard error.
     #[track_caller]
     pub fn run(&self) {
         let expected = self.expected.as_ref().expect("expected text not set");
-        let actual = self.actual.as_ref().expect("actual text not set");
+        let mut actual = self.actual.as_ref().expect("actual text not set").to_string();
         let expected_name = self.expected_name.as_ref().unwrap();
         let actual_name = self.actual_name.as_ref().unwrap();
+        for (regex, replacement) in &self.normalizers {
+            let re = Regex::new(regex).expect("bad regex in custom normalization rule");
+            actual = re.replace_all(&actual, replacement).into_owned();
+        }
 
-        let output = TextDiff::from_lines(expected, actual)
+        let output = TextDiff::from_lines(expected, &actual)
             .unified_diff()
             .header(expected_name, actual_name)
             .to_string();
diff --git a/src/tools/run-make-support/src/diff/tests.rs b/src/tools/run-make-support/src/diff/tests.rs
index e6d72544b7e..286548bef61 100644
--- a/src/tools/run-make-support/src/diff/tests.rs
+++ b/src/tools/run-make-support/src/diff/tests.rs
@@ -36,4 +36,26 @@ test failed: `EXPECTED_TEXT` is different from `ACTUAL_TEXT`
 
         assert_eq!(output.downcast_ref::<String>().unwrap(), expected_output);
     }
+
+    #[test]
+    fn test_normalize() {
+        let expected = "
+running 2 tests
+..
+
+test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
+";
+        let actual = "
+running 2 tests
+..
+
+test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s
+";
+
+        diff()
+            .expected_text("EXPECTED_TEXT", expected)
+            .actual_text("ACTUAL_TEXT", actual)
+            .normalize(r#"finished in \d+\.\d+s"#, "finished in $$TIME")
+            .run();
+    }
 }
diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs
index 76e8838d27c..a874c1313b5 100644
--- a/src/tools/run-make-support/src/lib.rs
+++ b/src/tools/run-make-support/src/lib.rs
@@ -15,6 +15,7 @@ use std::env;
 use std::path::{Path, PathBuf};
 use std::process::{Command, Output};
 
+pub use gimli;
 pub use object;
 pub use regex;
 pub use wasmparser;
diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt
index a2179b895b3..69c6dda4b19 100644
--- a/src/tools/tidy/src/allowed_run_make_makefiles.txt
+++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt
@@ -235,7 +235,6 @@ run-make/relocation-model/Makefile
 run-make/relro-levels/Makefile
 run-make/remap-path-prefix-dwarf/Makefile
 run-make/remap-path-prefix/Makefile
-run-make/repr128-dwarf/Makefile
 run-make/reproducible-build-2/Makefile
 run-make/reproducible-build/Makefile
 run-make/resolve-rename/Makefile
diff --git a/tests/assembly/manual-eq-efficient.rs b/tests/assembly/manual-eq-efficient.rs
new file mode 100644
index 00000000000..817ce94f476
--- /dev/null
+++ b/tests/assembly/manual-eq-efficient.rs
@@ -0,0 +1,22 @@
+// Regression test for #106269
+//@ assembly-output: emit-asm
+//@ compile-flags: --crate-type=lib -O -C llvm-args=-x86-asm-syntax=intel
+//@ only-x86_64
+//@ ignore-sgx
+
+pub struct S {
+    a: u8,
+    b: u8,
+    c: u8,
+    d: u8,
+}
+
+// CHECK-LABEL: manual_eq:
+#[no_mangle]
+pub fn manual_eq(s1: &S, s2: &S) -> bool {
+    // CHECK: mov [[REG:[a-z0-9]+]], dword ptr [{{[a-z0-9]+}}]
+    // CHECK-NEXT: cmp [[REG]], dword ptr [{{[a-z0-9]+}}]
+    // CHECK-NEXT: sete al
+    // CHECK: ret
+    s1.a == s2.a && s1.b == s2.b && s1.c == s2.c && s1.d == s2.d
+}
diff --git a/tests/run-make/repr128-dwarf/Makefile b/tests/run-make/repr128-dwarf/Makefile
deleted file mode 100644
index 3f933042724..00000000000
--- a/tests/run-make/repr128-dwarf/Makefile
+++ /dev/null
@@ -1,16 +0,0 @@
-# ignore-windows
-# This test should be replaced with one in tests/debuginfo once GDB or LLDB support 128-bit
-# enums.
-
-include ../tools.mk
-
-all:
-	$(RUSTC) -Cdebuginfo=2 lib.rs -o $(TMPDIR)/repr128.rlib
-	"$(LLVM_BIN_DIR)"/llvm-dwarfdump -n U128A $(TMPDIR)/repr128.rlib | $(CGREP) "DW_AT_const_value	(<0x10> 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 )"
-	"$(LLVM_BIN_DIR)"/llvm-dwarfdump -n U128B $(TMPDIR)/repr128.rlib | $(CGREP) "DW_AT_const_value	(<0x10> 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 )"
-	"$(LLVM_BIN_DIR)"/llvm-dwarfdump -n U128C $(TMPDIR)/repr128.rlib | $(CGREP) "DW_AT_const_value	(<0x10> 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 )"
-	"$(LLVM_BIN_DIR)"/llvm-dwarfdump -n U128D $(TMPDIR)/repr128.rlib | $(CGREP) "DW_AT_const_value	(<0x10> ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff )"
-	"$(LLVM_BIN_DIR)"/llvm-dwarfdump -n I128A $(TMPDIR)/repr128.rlib | $(CGREP) "DW_AT_const_value	(<0x10> 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 )"
-	"$(LLVM_BIN_DIR)"/llvm-dwarfdump -n I128B $(TMPDIR)/repr128.rlib | $(CGREP) "DW_AT_const_value	(<0x10> ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff )"
-	"$(LLVM_BIN_DIR)"/llvm-dwarfdump -n I128C $(TMPDIR)/repr128.rlib | $(CGREP) "DW_AT_const_value	(<0x10> 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 )"
-	"$(LLVM_BIN_DIR)"/llvm-dwarfdump -n I128D $(TMPDIR)/repr128.rlib | $(CGREP) "DW_AT_const_value	(<0x10> ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 7f )"
diff --git a/tests/run-make/repr128-dwarf/lib.rs b/tests/run-make/repr128-dwarf/main.rs
index 63675441d4b..57923a8386d 100644
--- a/tests/run-make/repr128-dwarf/lib.rs
+++ b/tests/run-make/repr128-dwarf/main.rs
@@ -1,4 +1,3 @@
-#![crate_type = "lib"]
 #![feature(repr128)]
 
 // Use .to_le() to ensure that the bytes are in the same order on both little- and big-endian
@@ -21,3 +20,7 @@ pub enum I128Enum {
 }
 
 pub fn f(_: U128Enum, _: I128Enum) {}
+
+fn main() {
+    f(U128Enum::U128A, I128Enum::I128A);
+}
diff --git a/tests/run-make/repr128-dwarf/rmake.rs b/tests/run-make/repr128-dwarf/rmake.rs
new file mode 100644
index 00000000000..f6375344f8f
--- /dev/null
+++ b/tests/run-make/repr128-dwarf/rmake.rs
@@ -0,0 +1,75 @@
+//@ ignore-windows
+// This test should be replaced with one in tests/debuginfo once GDB or LLDB support 128-bit enums.
+
+extern crate run_make_support;
+
+use gimli::{AttributeValue, Dwarf, EndianRcSlice, Reader, RunTimeEndian};
+use object::{Object, ObjectSection};
+use run_make_support::{gimli, object, rustc, tmp_dir};
+use std::borrow::Cow;
+use std::collections::HashMap;
+use std::rc::Rc;
+
+fn main() {
+    let output = tmp_dir().join("repr128");
+    rustc().input("main.rs").arg("-o").arg(&output).arg("-Cdebuginfo=2").run();
+    // Mach-O uses packed debug info
+    let dsym_location = output
+        .with_extension("dSYM")
+        .join("Contents")
+        .join("Resources")
+        .join("DWARF")
+        .join("repr128");
+    let output =
+        std::fs::read(if dsym_location.try_exists().unwrap() { dsym_location } else { output })
+            .unwrap();
+    let obj = object::File::parse(output.as_slice()).unwrap();
+    let endian = if obj.is_little_endian() { RunTimeEndian::Little } else { RunTimeEndian::Big };
+    let dwarf = gimli::Dwarf::load(|section| -> Result<_, ()> {
+        let data = obj.section_by_name(section.name()).map(|s| s.uncompressed_data().unwrap());
+        Ok(EndianRcSlice::new(Rc::from(data.unwrap_or_default().as_ref()), endian))
+    })
+    .unwrap();
+    let mut iter = dwarf.units();
+    let mut still_to_find = HashMap::from([
+        ("U128A", 0_u128),
+        ("U128B", 1_u128),
+        ("U128C", u64::MAX as u128 + 1),
+        ("U128D", u128::MAX),
+        ("I128A", 0_i128 as u128),
+        ("I128B", (-1_i128) as u128),
+        ("I128C", i128::MIN as u128),
+        ("I128D", i128::MAX as u128),
+    ]);
+    while let Some(header) = iter.next().unwrap() {
+        let unit = dwarf.unit(header).unwrap();
+        let mut cursor = unit.entries();
+        while let Some((_, entry)) = cursor.next_dfs().unwrap() {
+            if entry.tag() == gimli::constants::DW_TAG_enumerator {
+                let name = dwarf
+                    .attr_string(
+                        &unit,
+                        entry.attr(gimli::constants::DW_AT_name).unwrap().unwrap().value(),
+                    )
+                    .unwrap();
+                let name = name.to_string().unwrap();
+                if let Some(expected) = still_to_find.remove(name.as_ref()) {
+                    match entry.attr(gimli::constants::DW_AT_const_value).unwrap().unwrap().value()
+                    {
+                        AttributeValue::Block(value) => {
+                            assert_eq!(
+                                value.to_slice().unwrap(),
+                                expected.to_le_bytes().as_slice(),
+                                "{name}"
+                            );
+                        }
+                        value => panic!("{name}: unexpected DW_AT_const_value of {value:?}"),
+                    }
+                }
+            }
+        }
+    }
+    if !still_to_find.is_empty() {
+        panic!("Didn't find debug entries for {still_to_find:?}");
+    }
+}
diff --git a/tests/ui/coherence/auxiliary/parametrized-trait.rs b/tests/ui/coherence/auxiliary/parametrized-trait.rs
new file mode 100644
index 00000000000..88a3d5cd52d
--- /dev/null
+++ b/tests/ui/coherence/auxiliary/parametrized-trait.rs
@@ -0,0 +1,2 @@
+pub trait Trait0<T, U, V> {}
+pub trait Trait1<T, U> {}
diff --git a/tests/ui/coherence/auxiliary/trait-with-assoc-ty.rs b/tests/ui/coherence/auxiliary/trait-with-assoc-ty.rs
new file mode 100644
index 00000000000..d49538de580
--- /dev/null
+++ b/tests/ui/coherence/auxiliary/trait-with-assoc-ty.rs
@@ -0,0 +1,3 @@
+pub trait Trait {
+    type Assoc;
+}
diff --git a/tests/ui/coherence/coherence-overlap-unnormalizable-projection-0.classic.stderr b/tests/ui/coherence/coherence-overlap-unnormalizable-projection-0.classic.stderr
new file mode 100644
index 00000000000..2ffb6000ec8
--- /dev/null
+++ b/tests/ui/coherence/coherence-overlap-unnormalizable-projection-0.classic.stderr
@@ -0,0 +1,19 @@
+error[E0119]: conflicting implementations of trait `Trait` for type `Box<_>`
+  --> $DIR/coherence-overlap-unnormalizable-projection-0.rs:27:1
+   |
+LL | / impl<T> Trait for T
+LL | | where
+LL | |     T: 'static,
+LL | |     for<'a> T: WithAssoc<'a>,
+LL | |     for<'a> <T as WithAssoc<'a>>::Assoc: WhereBound,
+   | |____________________________________________________- first implementation here
+...
+LL |   impl<T> Trait for Box<T> {}
+   |   ^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Box<_>`
+   |
+   = note: downstream crates may implement trait `WithAssoc<'a>` for type `std::boxed::Box<_>`
+   = note: downstream crates may implement trait `WhereBound` for type `<std::boxed::Box<_> as WithAssoc<'a>>::Assoc`
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0119`.
diff --git a/tests/ui/coherence/coherence-overlap-unnormalizable-projection-0.next.stderr b/tests/ui/coherence/coherence-overlap-unnormalizable-projection-0.next.stderr
new file mode 100644
index 00000000000..99abdf65abd
--- /dev/null
+++ b/tests/ui/coherence/coherence-overlap-unnormalizable-projection-0.next.stderr
@@ -0,0 +1,18 @@
+error[E0119]: conflicting implementations of trait `Trait` for type `Box<_>`
+  --> $DIR/coherence-overlap-unnormalizable-projection-0.rs:27:1
+   |
+LL | / impl<T> Trait for T
+LL | | where
+LL | |     T: 'static,
+LL | |     for<'a> T: WithAssoc<'a>,
+LL | |     for<'a> <T as WithAssoc<'a>>::Assoc: WhereBound,
+   | |____________________________________________________- first implementation here
+...
+LL |   impl<T> Trait for Box<T> {}
+   |   ^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Box<_>`
+   |
+   = note: downstream crates may implement trait `WithAssoc<'a>` for type `std::boxed::Box<_>`
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0119`.
diff --git a/tests/ui/coherence/coherence-overlap-unnormalizable-projection-0.rs b/tests/ui/coherence/coherence-overlap-unnormalizable-projection-0.rs
new file mode 100644
index 00000000000..b8b6d8846ef
--- /dev/null
+++ b/tests/ui/coherence/coherence-overlap-unnormalizable-projection-0.rs
@@ -0,0 +1,42 @@
+// Regression test for soundness issue #114061:
+// "Coherence incorrectly considers `unnormalizable_projection: Trait` to not hold even if it could"
+#![crate_type = "lib"]
+
+//@ revisions: classic next
+//@[next] compile-flags: -Znext-solver
+
+trait WhereBound {}
+impl WhereBound for () {}
+
+
+pub trait WithAssoc<'a> {
+    type Assoc;
+}
+
+// These two impls of `Trait` overlap:
+
+pub trait Trait {}
+impl<T> Trait for T
+where
+    T: 'static,
+    for<'a> T: WithAssoc<'a>,
+    for<'a> <T as WithAssoc<'a>>::Assoc: WhereBound,
+{
+}
+
+impl<T> Trait for Box<T> {} //~ ERROR conflicting implementations of trait `Trait` for type `Box<_>`
+
+// A downstream crate could write:
+//
+//     use upstream::*;
+//
+//     struct Local;
+//     impl WithAssoc<'_> for Box<Local> {
+//         type Assoc = ();
+//     }
+//
+//     fn impls_trait<T: Trait>() {}
+//
+//     fn main() {
+//         impls_trait::<Box<Local>>();
+//     }
diff --git a/tests/ui/coherence/coherence-overlap-unnormalizable-projection-1.classic.stderr b/tests/ui/coherence/coherence-overlap-unnormalizable-projection-1.classic.stderr
new file mode 100644
index 00000000000..49b236f9d2a
--- /dev/null
+++ b/tests/ui/coherence/coherence-overlap-unnormalizable-projection-1.classic.stderr
@@ -0,0 +1,19 @@
+error[E0119]: conflicting implementations of trait `Trait` for type `Box<_>`
+  --> $DIR/coherence-overlap-unnormalizable-projection-1.rs:26:1
+   |
+LL | / impl<T> Trait for T
+LL | | where
+LL | |     T: 'static,
+LL | |     for<'a> T: WithAssoc<'a>,
+LL | |     for<'a> Box<<T as WithAssoc<'a>>::Assoc>: WhereBound,
+   | |_________________________________________________________- first implementation here
+...
+LL |   impl<T> Trait for Box<T> {}
+   |   ^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Box<_>`
+   |
+   = note: downstream crates may implement trait `WithAssoc<'a>` for type `std::boxed::Box<_>`
+   = note: downstream crates may implement trait `WhereBound` for type `std::boxed::Box<<std::boxed::Box<_> as WithAssoc<'a>>::Assoc>`
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0119`.
diff --git a/tests/ui/coherence/coherence-overlap-unnormalizable-projection-1.next.stderr b/tests/ui/coherence/coherence-overlap-unnormalizable-projection-1.next.stderr
new file mode 100644
index 00000000000..49b236f9d2a
--- /dev/null
+++ b/tests/ui/coherence/coherence-overlap-unnormalizable-projection-1.next.stderr
@@ -0,0 +1,19 @@
+error[E0119]: conflicting implementations of trait `Trait` for type `Box<_>`
+  --> $DIR/coherence-overlap-unnormalizable-projection-1.rs:26:1
+   |
+LL | / impl<T> Trait for T
+LL | | where
+LL | |     T: 'static,
+LL | |     for<'a> T: WithAssoc<'a>,
+LL | |     for<'a> Box<<T as WithAssoc<'a>>::Assoc>: WhereBound,
+   | |_________________________________________________________- first implementation here
+...
+LL |   impl<T> Trait for Box<T> {}
+   |   ^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Box<_>`
+   |
+   = note: downstream crates may implement trait `WithAssoc<'a>` for type `std::boxed::Box<_>`
+   = note: downstream crates may implement trait `WhereBound` for type `std::boxed::Box<<std::boxed::Box<_> as WithAssoc<'a>>::Assoc>`
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0119`.
diff --git a/tests/ui/coherence/coherence-overlap-unnormalizable-projection-1.rs b/tests/ui/coherence/coherence-overlap-unnormalizable-projection-1.rs
new file mode 100644
index 00000000000..8eeadb3dc75
--- /dev/null
+++ b/tests/ui/coherence/coherence-overlap-unnormalizable-projection-1.rs
@@ -0,0 +1,44 @@
+// Regression test for soundness issue #114061:
+// "Coherence incorrectly considers `unnormalizable_projection: Trait` to not hold even if it could"
+#![crate_type = "lib"]
+
+//@ revisions: classic next
+//@[next] compile-flags: -Znext-solver
+
+pub trait WhereBound {}
+impl WhereBound for () {}
+
+pub trait WithAssoc<'a> {
+    type Assoc;
+}
+
+// These two impls of `Trait` overlap:
+
+pub trait Trait {}
+impl<T> Trait for T
+where
+    T: 'static,
+    for<'a> T: WithAssoc<'a>,
+    for<'a> Box<<T as WithAssoc<'a>>::Assoc>: WhereBound,
+{
+}
+
+impl<T> Trait for Box<T> {} //~ ERROR conflicting implementations of trait `Trait` for type `Box<_>`
+
+// A downstream crate could write:
+//
+//
+//     use upstream::*;
+//
+//     struct Local;
+//     impl WithAssoc<'_> for Box<Local> {
+//         type Assoc = Local;
+//     }
+//
+//     impl WhereBound for Box<Local> {}
+//
+//     fn impls_trait<T: Trait>() {}
+//
+//     fn main() {
+//         impls_trait::<Box<Local>>();
+//     }
diff --git a/tests/ui/coherence/orphan-check-projections-covering.rs b/tests/ui/coherence/orphan-check-projections-covering.rs
new file mode 100644
index 00000000000..ae1917ec161
--- /dev/null
+++ b/tests/ui/coherence/orphan-check-projections-covering.rs
@@ -0,0 +1,25 @@
+// Projections cover type parameters if they normalize to a (local) type that covers them.
+// This ensures that we don't perform an overly strict check on
+// projections like in closed PR #100555 which did a syntactic
+// check for type parameters in projections without normalizing
+// first which would've lead to real-word regressions.
+
+//@ check-pass
+//@ revisions: classic next
+//@[next] compile-flags: -Znext-solver
+
+//@ aux-crate:foreign=parametrized-trait.rs
+//@ edition:2021
+
+trait Project { type Output; }
+
+impl<T> Project for Wrapper<T> {
+    type Output = Local;
+}
+
+struct Wrapper<T>(T);
+struct Local;
+
+impl<T> foreign::Trait1<Local, T> for <Wrapper<T> as Project>::Output {}
+
+fn main() {}
diff --git a/tests/ui/coherence/orphan-check-projections-nested.rs b/tests/ui/coherence/orphan-check-projections-nested.rs
new file mode 100644
index 00000000000..ec244a8005b
--- /dev/null
+++ b/tests/ui/coherence/orphan-check-projections-nested.rs
@@ -0,0 +1,23 @@
+// This used to ICE in an earlier iteration of #117164. Minimized from crate `proqnt`.
+
+//@ check-pass
+//@ revisions: classic next
+//@[next] compile-flags: -Znext-solver
+//@ aux-crate:dep=trait-with-assoc-ty.rs
+//@ edition: 2021
+
+pub(crate) trait Trait<T> {
+    type Assoc;
+}
+
+pub(crate) struct Type<T, U, V>(T, U, V);
+
+impl<T, U> dep::Trait for Type<T, <<T as dep::Trait>::Assoc as Trait<U>>::Assoc, U>
+where
+    T: dep::Trait,
+    <T as dep::Trait>::Assoc: Trait<U>,
+{
+    type Assoc = U;
+}
+
+fn main() {}
diff --git a/tests/ui/coherence/orphan-check-projections-not-covering-ambiguity.classic.stderr b/tests/ui/coherence/orphan-check-projections-not-covering-ambiguity.classic.stderr
new file mode 100644
index 00000000000..d83a56c0bd0
--- /dev/null
+++ b/tests/ui/coherence/orphan-check-projections-not-covering-ambiguity.classic.stderr
@@ -0,0 +1,15 @@
+warning[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
+  --> $DIR/orphan-check-projections-not-covering-ambiguity.rs:25:6
+   |
+LL | impl<T> foreign::Trait1<Local, T> for <T as Project>::Output {}
+   |      ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #124559 <https://github.com/rust-lang/rust/issues/124559>
+   = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type
+   = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last
+   = note: `#[warn(uncovered_param_in_projection)]` on by default
+
+warning: 1 warning emitted
+
+For more information about this error, try `rustc --explain E0210`.
diff --git a/tests/ui/coherence/orphan-check-projections-not-covering-ambiguity.next.stderr b/tests/ui/coherence/orphan-check-projections-not-covering-ambiguity.next.stderr
new file mode 100644
index 00000000000..d83a56c0bd0
--- /dev/null
+++ b/tests/ui/coherence/orphan-check-projections-not-covering-ambiguity.next.stderr
@@ -0,0 +1,15 @@
+warning[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
+  --> $DIR/orphan-check-projections-not-covering-ambiguity.rs:25:6
+   |
+LL | impl<T> foreign::Trait1<Local, T> for <T as Project>::Output {}
+   |      ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #124559 <https://github.com/rust-lang/rust/issues/124559>
+   = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type
+   = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last
+   = note: `#[warn(uncovered_param_in_projection)]` on by default
+
+warning: 1 warning emitted
+
+For more information about this error, try `rustc --explain E0210`.
diff --git a/tests/ui/coherence/orphan-check-projections-not-covering-ambiguity.rs b/tests/ui/coherence/orphan-check-projections-not-covering-ambiguity.rs
new file mode 100644
index 00000000000..227d8535232
--- /dev/null
+++ b/tests/ui/coherence/orphan-check-projections-not-covering-ambiguity.rs
@@ -0,0 +1,29 @@
+// This test demonstrates a limitation of the trait solver.
+// Basically, one might think that `T` was covered by the projection since the
+// latter appears to normalize to a local type. However, since we instantiate the
+// constituent types of the self type of impls with fresh infer vars and try to
+// normalize them during orphan checking, we wind up trying to normalize a
+// projection whose self type is an infer var which unconditionally fails due to
+// ambiguity.
+
+//@ revisions: classic next
+//@[next] compile-flags: -Znext-solver
+
+//@ check-pass
+//@ compile-flags: --crate-type=lib
+//@ aux-crate:foreign=parametrized-trait.rs
+//@ edition:2021
+
+trait Project { type Output; }
+
+impl<T> Project for T {
+    type Output = Local;
+}
+
+struct Local;
+
+impl<T> foreign::Trait1<Local, T> for <T as Project>::Output {}
+//~^ WARNING type parameter `T` must be covered by another type
+//~| WARNING this was previously accepted by the compiler
+
+fn main() {}
diff --git a/tests/ui/coherence/orphan-check-projections-not-covering-multiple-params.classic.stderr b/tests/ui/coherence/orphan-check-projections-not-covering-multiple-params.classic.stderr
new file mode 100644
index 00000000000..8964fefedd4
--- /dev/null
+++ b/tests/ui/coherence/orphan-check-projections-not-covering-multiple-params.classic.stderr
@@ -0,0 +1,26 @@
+warning[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`LocalTy`)
+  --> $DIR/orphan-check-projections-not-covering-multiple-params.rs:17:6
+   |
+LL | impl<T, U> foreign::Trait0<LocalTy, T, U> for <() as Trait<T, U>>::Assoc {}
+   |      ^ type parameter `T` must be covered by another type when it appears before the first local type (`LocalTy`)
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #124559 <https://github.com/rust-lang/rust/issues/124559>
+   = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type
+   = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last
+   = note: `#[warn(uncovered_param_in_projection)]` on by default
+
+warning[E0210]: type parameter `U` must be covered by another type when it appears before the first local type (`LocalTy`)
+  --> $DIR/orphan-check-projections-not-covering-multiple-params.rs:17:9
+   |
+LL | impl<T, U> foreign::Trait0<LocalTy, T, U> for <() as Trait<T, U>>::Assoc {}
+   |         ^ type parameter `U` must be covered by another type when it appears before the first local type (`LocalTy`)
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #124559 <https://github.com/rust-lang/rust/issues/124559>
+   = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type
+   = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last
+
+warning: 2 warnings emitted
+
+For more information about this error, try `rustc --explain E0210`.
diff --git a/tests/ui/coherence/orphan-check-projections-not-covering-multiple-params.next.stderr b/tests/ui/coherence/orphan-check-projections-not-covering-multiple-params.next.stderr
new file mode 100644
index 00000000000..8964fefedd4
--- /dev/null
+++ b/tests/ui/coherence/orphan-check-projections-not-covering-multiple-params.next.stderr
@@ -0,0 +1,26 @@
+warning[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`LocalTy`)
+  --> $DIR/orphan-check-projections-not-covering-multiple-params.rs:17:6
+   |
+LL | impl<T, U> foreign::Trait0<LocalTy, T, U> for <() as Trait<T, U>>::Assoc {}
+   |      ^ type parameter `T` must be covered by another type when it appears before the first local type (`LocalTy`)
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #124559 <https://github.com/rust-lang/rust/issues/124559>
+   = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type
+   = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last
+   = note: `#[warn(uncovered_param_in_projection)]` on by default
+
+warning[E0210]: type parameter `U` must be covered by another type when it appears before the first local type (`LocalTy`)
+  --> $DIR/orphan-check-projections-not-covering-multiple-params.rs:17:9
+   |
+LL | impl<T, U> foreign::Trait0<LocalTy, T, U> for <() as Trait<T, U>>::Assoc {}
+   |         ^ type parameter `U` must be covered by another type when it appears before the first local type (`LocalTy`)
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #124559 <https://github.com/rust-lang/rust/issues/124559>
+   = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type
+   = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last
+
+warning: 2 warnings emitted
+
+For more information about this error, try `rustc --explain E0210`.
diff --git a/tests/ui/coherence/orphan-check-projections-not-covering-multiple-params.rs b/tests/ui/coherence/orphan-check-projections-not-covering-multiple-params.rs
new file mode 100644
index 00000000000..c5340e85405
--- /dev/null
+++ b/tests/ui/coherence/orphan-check-projections-not-covering-multiple-params.rs
@@ -0,0 +1,24 @@
+//@ revisions: classic next
+//@[next] compile-flags: -Znext-solver
+
+//@ check-pass
+//@ compile-flags: --crate-type=lib
+//@ aux-crate:foreign=parametrized-trait.rs
+//@ edition:2021
+
+trait Trait<T, U> { type Assoc; }
+
+impl<T, U> Trait<T, U> for () {
+    type Assoc = LocalTy;
+}
+
+struct LocalTy;
+
+impl<T, U> foreign::Trait0<LocalTy, T, U> for <() as Trait<T, U>>::Assoc {}
+//~^ WARNING type parameter `T` must be covered by another type
+//~| WARNING this was previously accepted by the compiler
+//~| WARNING type parameter `U` must be covered by another type
+//~| WARNING this was previously accepted by the compiler
+
+
+fn main() {}
diff --git a/tests/ui/coherence/orphan-check-projections-not-covering.classic.stderr b/tests/ui/coherence/orphan-check-projections-not-covering.classic.stderr
new file mode 100644
index 00000000000..28b8c3f4a94
--- /dev/null
+++ b/tests/ui/coherence/orphan-check-projections-not-covering.classic.stderr
@@ -0,0 +1,37 @@
+warning[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
+  --> $DIR/orphan-check-projections-not-covering.rs:22:6
+   |
+LL | impl<T> foreign::Trait0<Local, T, ()> for <T as Identity>::Output {}
+   |      ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #124559 <https://github.com/rust-lang/rust/issues/124559>
+   = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type
+   = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last
+   = note: `#[warn(uncovered_param_in_projection)]` on by default
+
+warning[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
+  --> $DIR/orphan-check-projections-not-covering.rs:27:6
+   |
+LL | impl<T> foreign::Trait0<<T as Identity>::Output, Local, T> for Option<T> {}
+   |      ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #124559 <https://github.com/rust-lang/rust/issues/124559>
+   = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type
+   = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last
+
+warning[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
+  --> $DIR/orphan-check-projections-not-covering.rs:40:6
+   |
+LL | impl<T: Deferred> foreign::Trait1<Local, T> for <T as Deferred>::Output {}
+   |      ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #124559 <https://github.com/rust-lang/rust/issues/124559>
+   = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type
+   = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last
+
+warning: 3 warnings emitted
+
+For more information about this error, try `rustc --explain E0210`.
diff --git a/tests/ui/coherence/orphan-check-projections-not-covering.next.stderr b/tests/ui/coherence/orphan-check-projections-not-covering.next.stderr
new file mode 100644
index 00000000000..28b8c3f4a94
--- /dev/null
+++ b/tests/ui/coherence/orphan-check-projections-not-covering.next.stderr
@@ -0,0 +1,37 @@
+warning[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
+  --> $DIR/orphan-check-projections-not-covering.rs:22:6
+   |
+LL | impl<T> foreign::Trait0<Local, T, ()> for <T as Identity>::Output {}
+   |      ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #124559 <https://github.com/rust-lang/rust/issues/124559>
+   = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type
+   = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last
+   = note: `#[warn(uncovered_param_in_projection)]` on by default
+
+warning[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
+  --> $DIR/orphan-check-projections-not-covering.rs:27:6
+   |
+LL | impl<T> foreign::Trait0<<T as Identity>::Output, Local, T> for Option<T> {}
+   |      ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #124559 <https://github.com/rust-lang/rust/issues/124559>
+   = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type
+   = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last
+
+warning[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
+  --> $DIR/orphan-check-projections-not-covering.rs:40:6
+   |
+LL | impl<T: Deferred> foreign::Trait1<Local, T> for <T as Deferred>::Output {}
+   |      ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #124559 <https://github.com/rust-lang/rust/issues/124559>
+   = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type
+   = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last
+
+warning: 3 warnings emitted
+
+For more information about this error, try `rustc --explain E0210`.
diff --git a/tests/ui/coherence/orphan-check-projections-not-covering.rs b/tests/ui/coherence/orphan-check-projections-not-covering.rs
new file mode 100644
index 00000000000..8c2a9a17e2b
--- /dev/null
+++ b/tests/ui/coherence/orphan-check-projections-not-covering.rs
@@ -0,0 +1,42 @@
+// Regression test for issue #99554.
+// Projections might not cover type parameters.
+
+//@ revisions: classic next
+//@[next] compile-flags: -Znext-solver
+
+//@ check-pass
+//@ compile-flags: --crate-type=lib
+//@ aux-crate:foreign=parametrized-trait.rs
+//@ edition:2021
+
+trait Identity {
+    type Output;
+}
+
+impl<T> Identity for T {
+    type Output = T;
+}
+
+struct Local;
+
+impl<T> foreign::Trait0<Local, T, ()> for <T as Identity>::Output {}
+//~^ WARNING type parameter `T` must be covered by another type
+//~| WARNING this was previously accepted by the compiler
+
+
+impl<T> foreign::Trait0<<T as Identity>::Output, Local, T> for Option<T> {}
+//~^ WARNING type parameter `T` must be covered by another type
+//~| WARNING this was previously accepted by the compiler
+
+pub trait Deferred {
+    type Output;
+}
+
+// A downstream user could implement
+//
+//     impl<T> Deferred for Type<T> { type Output = T; }
+//     struct Type<T>(T);
+//
+impl<T: Deferred> foreign::Trait1<Local, T> for <T as Deferred>::Output {}
+//~^ WARNING type parameter `T` must be covered by another type
+//~| WARNING this was previously accepted by the compiler
diff --git a/tests/ui/coherence/orphan-check-projections-unsat-bounds.classic.stderr b/tests/ui/coherence/orphan-check-projections-unsat-bounds.classic.stderr
new file mode 100644
index 00000000000..0346a9d665c
--- /dev/null
+++ b/tests/ui/coherence/orphan-check-projections-unsat-bounds.classic.stderr
@@ -0,0 +1,15 @@
+warning[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`LocalTy`)
+  --> $DIR/orphan-check-projections-unsat-bounds.rs:28:6
+   |
+LL | impl<T> foreign::Trait1<LocalTy, T> for <Wrapper<T> as Discard>::Output
+   |      ^ type parameter `T` must be covered by another type when it appears before the first local type (`LocalTy`)
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #124559 <https://github.com/rust-lang/rust/issues/124559>
+   = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type
+   = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last
+   = note: `#[warn(uncovered_param_in_projection)]` on by default
+
+warning: 1 warning emitted
+
+For more information about this error, try `rustc --explain E0210`.
diff --git a/tests/ui/coherence/orphan-check-projections-unsat-bounds.next.stderr b/tests/ui/coherence/orphan-check-projections-unsat-bounds.next.stderr
new file mode 100644
index 00000000000..0346a9d665c
--- /dev/null
+++ b/tests/ui/coherence/orphan-check-projections-unsat-bounds.next.stderr
@@ -0,0 +1,15 @@
+warning[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`LocalTy`)
+  --> $DIR/orphan-check-projections-unsat-bounds.rs:28:6
+   |
+LL | impl<T> foreign::Trait1<LocalTy, T> for <Wrapper<T> as Discard>::Output
+   |      ^ type parameter `T` must be covered by another type when it appears before the first local type (`LocalTy`)
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #124559 <https://github.com/rust-lang/rust/issues/124559>
+   = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type
+   = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last
+   = note: `#[warn(uncovered_param_in_projection)]` on by default
+
+warning: 1 warning emitted
+
+For more information about this error, try `rustc --explain E0210`.
diff --git a/tests/ui/coherence/orphan-check-projections-unsat-bounds.rs b/tests/ui/coherence/orphan-check-projections-unsat-bounds.rs
new file mode 100644
index 00000000000..bc52673a016
--- /dev/null
+++ b/tests/ui/coherence/orphan-check-projections-unsat-bounds.rs
@@ -0,0 +1,35 @@
+// This used to ICE in an earlier iteration of #117164.
+// The normalization performed during orphan checking happens inside an empty ParamEnv and
+// with type parameters mapped to fresh infer vars. Therefore it may fail for example due to
+// unsatisfied bounds while normalization outside of orphan checking succeeds.
+
+//@ revisions: classic next
+//@[next] compile-flags: -Znext-solver
+
+//@ check-pass
+//@ aux-crate:foreign=parametrized-trait.rs
+//@ edition:2021
+
+struct Wrapper<T>(T);
+
+trait Bound {}
+
+trait Discard { type Output; }
+
+impl<T> Discard for Wrapper<T>
+where
+    Wrapper<T>: Bound
+{
+    type Output = LocalTy;
+}
+
+struct LocalTy;
+
+impl<T> foreign::Trait1<LocalTy, T> for <Wrapper<T> as Discard>::Output
+//~^ WARNING type parameter `T` must be covered by another type
+//~| WARNING this was previously accepted by the compiler
+where
+    Wrapper<T>: Bound
+{}
+
+fn main() {}
diff --git a/tests/ui/coherence/orphan-check-weak-aliases-covering.rs b/tests/ui/coherence/orphan-check-weak-aliases-covering.rs
new file mode 100644
index 00000000000..a8b9e905c70
--- /dev/null
+++ b/tests/ui/coherence/orphan-check-weak-aliases-covering.rs
@@ -0,0 +1,20 @@
+// Weak aliases cover type parameters if they normalize to a (local) type that covers them.
+
+//@ check-pass
+//@ revisions: classic next
+//@[next] compile-flags: -Znext-solver
+
+//@ aux-crate:foreign=parametrized-trait.rs
+//@ edition:2021
+
+#![feature(lazy_type_alias)]
+#![allow(incomplete_features)]
+
+type Alias<T> = LocalWrapper<T>;
+
+struct Local;
+struct LocalWrapper<T>(T);
+
+impl<T> foreign::Trait1<Local, T> for Alias<T> {}
+
+fn main() {}
diff --git a/tests/ui/coherence/orphan-check-weak-aliases-not-covering.classic.stderr b/tests/ui/coherence/orphan-check-weak-aliases-not-covering.classic.stderr
new file mode 100644
index 00000000000..276833fa171
--- /dev/null
+++ b/tests/ui/coherence/orphan-check-weak-aliases-not-covering.classic.stderr
@@ -0,0 +1,12 @@
+error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
+  --> $DIR/orphan-check-weak-aliases-not-covering.rs:16:6
+   |
+LL | impl<T> foreign::Trait1<Local, T> for Identity<T> {}
+   |      ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
+   |
+   = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type
+   = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0210`.
diff --git a/tests/ui/coherence/orphan-check-weak-aliases-not-covering.next.stderr b/tests/ui/coherence/orphan-check-weak-aliases-not-covering.next.stderr
new file mode 100644
index 00000000000..276833fa171
--- /dev/null
+++ b/tests/ui/coherence/orphan-check-weak-aliases-not-covering.next.stderr
@@ -0,0 +1,12 @@
+error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
+  --> $DIR/orphan-check-weak-aliases-not-covering.rs:16:6
+   |
+LL | impl<T> foreign::Trait1<Local, T> for Identity<T> {}
+   |      ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
+   |
+   = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type
+   = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0210`.
diff --git a/tests/ui/coherence/orphan-check-weak-aliases-not-covering.rs b/tests/ui/coherence/orphan-check-weak-aliases-not-covering.rs
new file mode 100644
index 00000000000..9ebc45a8829
--- /dev/null
+++ b/tests/ui/coherence/orphan-check-weak-aliases-not-covering.rs
@@ -0,0 +1,19 @@
+// Weak aliases might not cover type parameters.
+
+//@ revisions: classic next
+//@[next] compile-flags: -Znext-solver
+
+//@ aux-crate:foreign=parametrized-trait.rs
+//@ edition:2021
+
+#![feature(lazy_type_alias)]
+#![allow(incomplete_features)]
+
+type Identity<T> = T;
+
+struct Local;
+
+impl<T> foreign::Trait1<Local, T> for Identity<T> {}
+//~^ ERROR type parameter `T` must be covered by another type
+
+fn main() {}
diff --git a/tests/ui/type-alias-impl-trait/coherence.stderr b/tests/ui/type-alias-impl-trait/coherence.stderr
index 4462b70f782..266a532a1db 100644
--- a/tests/ui/type-alias-impl-trait/coherence.stderr
+++ b/tests/ui/type-alias-impl-trait/coherence.stderr
@@ -4,7 +4,7 @@ error[E0117]: only traits defined in the current crate can be implemented for ar
 LL | impl foreign_crate::ForeignTrait for AliasOfForeignType<()> {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^----------------------
    | |                                    |
-   | |                                    `AliasOfForeignType<()>` is not defined in the current crate
+   | |                                    type alias impl trait is treated as if it were foreign, because its hidden type could be from a foreign crate
    | impl doesn't use only types from inside the current crate
    |
    = note: define and implement a trait or new type instead