about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-01-09 15:29:59 +0000
committerbors <bors@rust-lang.org>2023-01-09 15:29:59 +0000
commitaf58fc869910eae0ec525d49cbb6d953ea10a8c6 (patch)
tree68bda15813c499969b94bcf6dd966a61f214e839
parent89e0576bd3aec2f1acf935b353900bd75742860b (diff)
parentbf228ace5cf6824078d6d36144ad8a65f07fa8d3 (diff)
downloadrust-af58fc869910eae0ec525d49cbb6d953ea10a8c6.tar.gz
rust-af58fc869910eae0ec525d49cbb6d953ea10a8c6.zip
Auto merge of #101947 - aliemjay:astconv-normalize, r=lcnr
Don't normalize in AstConv

See individual commits.

Fixes #101350
Fixes #54940
-rw-r--r--compiler/rustc_borrowck/src/type_check/canonical.rs78
-rw-r--r--compiler/rustc_borrowck/src/type_check/input_output.rs126
-rw-r--r--compiler/rustc_borrowck/src/type_check/mod.rs76
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/mod.rs51
-rw-r--r--compiler/rustc_hir_analysis/src/collect.rs6
-rw-r--r--compiler/rustc_hir_typeck/src/closure.rs6
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs139
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs40
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs32
-rw-r--r--compiler/rustc_hir_typeck/src/gather_locals.rs5
-rw-r--r--compiler/rustc_hir_typeck/src/lib.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/method/confirm.rs50
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs4
-rw-r--r--compiler/rustc_middle/src/traits/query.rs11
-rw-r--r--compiler/rustc_middle/src/ty/typeck_results.rs2
-rw-r--r--compiler/rustc_traits/src/type_op.rs54
-rw-r--r--src/test/ui/generic-associated-types/issue-91139.rs8
-rw-r--r--src/test/ui/generic-associated-types/issue-91139.stderr61
-rw-r--r--src/test/ui/higher-rank-trait-bounds/normalize-under-binder/issue-85455.rs1
-rw-r--r--src/test/ui/higher-rank-trait-bounds/normalize-under-binder/issue-85455.stderr13
-rw-r--r--src/test/ui/nll/closure-malformed-projection-input-issue-102800.rs12
-rw-r--r--src/test/ui/nll/closure-malformed-projection-input-issue-102800.stderr86
-rw-r--r--src/test/ui/nll/ty-outlives/wf-unreachable.stderr16
-rw-r--r--src/test/ui/nll/user-annotations/ascribed-type-wf.rs5
-rw-r--r--src/test/ui/nll/user-annotations/ascribed-type-wf.stderr10
-rw-r--r--src/test/ui/nll/user-annotations/closure-sig.rs15
-rw-r--r--src/test/ui/nll/user-annotations/normalization-2.rs152
-rw-r--r--src/test/ui/nll/user-annotations/normalization-2.stderr296
-rw-r--r--src/test/ui/nll/user-annotations/normalization-default.rs22
-rw-r--r--src/test/ui/nll/user-annotations/normalization-default.stderr36
-rw-r--r--src/test/ui/nll/user-annotations/normalization-infer.rs40
-rw-r--r--src/test/ui/nll/user-annotations/normalization-infer.stderr101
-rw-r--r--src/test/ui/nll/user-annotations/normalization-self.rs26
-rw-r--r--src/test/ui/nll/user-annotations/normalization-self.stderr36
-rw-r--r--src/test/ui/nll/user-annotations/normalization.rs9
-rw-r--r--src/test/ui/nll/user-annotations/normalization.stderr18
-rw-r--r--src/test/ui/regions/regions-assoc-type-in-supertrait-outlives-container.stderr2
-rw-r--r--src/test/ui/regions/regions-free-region-ordering-caller.stderr6
-rw-r--r--src/test/ui/regions/regions-outlives-projection-container-hrtb.stderr4
-rw-r--r--src/test/ui/regions/regions-outlives-projection-container-wc.stderr2
-rw-r--r--src/test/ui/regions/regions-outlives-projection-container.stderr4
-rw-r--r--src/test/ui/type-alias-impl-trait/associated-type-impl-trait-lifetime.rs20
-rw-r--r--src/test/ui/ufcs/ufcs-partially-resolved.stderr2
43 files changed, 1182 insertions, 503 deletions
diff --git a/compiler/rustc_borrowck/src/type_check/canonical.rs b/compiler/rustc_borrowck/src/type_check/canonical.rs
index 3617bf58be9..02222c0a03c 100644
--- a/compiler/rustc_borrowck/src/type_check/canonical.rs
+++ b/compiler/rustc_borrowck/src/type_check/canonical.rs
@@ -1,13 +1,13 @@
 use std::fmt;
 
-use rustc_infer::infer::canonical::Canonical;
-use rustc_infer::traits::query::NoSolution;
+use rustc_infer::infer::{canonical::Canonical, InferOk};
 use rustc_middle::mir::ConstraintCategory;
-use rustc_middle::ty::{self, ToPredicate, TypeFoldable};
+use rustc_middle::ty::{self, ToPredicate, Ty, TypeFoldable};
 use rustc_span::def_id::DefId;
 use rustc_span::Span;
 use rustc_trait_selection::traits::query::type_op::{self, TypeOpOutput};
-use rustc_trait_selection::traits::query::Fallible;
+use rustc_trait_selection::traits::query::{Fallible, NoSolution};
+use rustc_trait_selection::traits::{ObligationCause, ObligationCtxt};
 
 use crate::diagnostics::{ToUniverseInfo, UniverseInfo};
 
@@ -177,4 +177,74 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
             value
         })
     }
+
+    #[instrument(skip(self), level = "debug")]
+    pub(super) fn ascribe_user_type(
+        &mut self,
+        mir_ty: Ty<'tcx>,
+        user_ty: ty::UserType<'tcx>,
+        span: Span,
+    ) {
+        // FIXME: Ideally MIR types are normalized, but this is not always true.
+        let mir_ty = self.normalize(mir_ty, Locations::All(span));
+
+        self.fully_perform_op(
+            Locations::All(span),
+            ConstraintCategory::Boring,
+            self.param_env.and(type_op::ascribe_user_type::AscribeUserType::new(mir_ty, user_ty)),
+        )
+        .unwrap_or_else(|err| {
+            span_mirbug!(
+                self,
+                span,
+                "ascribe_user_type `{mir_ty:?}=={user_ty:?}` failed with `{err:?}`",
+            );
+        });
+    }
+
+    /// *Incorrectly* skips the WF checks we normally do in `ascribe_user_type`.
+    ///
+    /// FIXME(#104478, #104477): This is a hack for backward-compatibility.
+    #[instrument(skip(self), level = "debug")]
+    pub(super) fn ascribe_user_type_skip_wf(
+        &mut self,
+        mir_ty: Ty<'tcx>,
+        user_ty: ty::UserType<'tcx>,
+        span: Span,
+    ) {
+        let ty::UserType::Ty(user_ty) = user_ty else { bug!() };
+
+        // A fast path for a common case with closure input/output types.
+        if let ty::Infer(_) = user_ty.kind() {
+            self.eq_types(user_ty, mir_ty, Locations::All(span), ConstraintCategory::Boring)
+                .unwrap();
+            return;
+        }
+
+        let mir_ty = self.normalize(mir_ty, Locations::All(span));
+        let cause = ObligationCause::dummy_with_span(span);
+        let param_env = self.param_env;
+        let op = |infcx: &'_ _| {
+            let ocx = ObligationCtxt::new_in_snapshot(infcx);
+            let user_ty = ocx.normalize(&cause, param_env, user_ty);
+            ocx.eq(&cause, param_env, user_ty, mir_ty)?;
+            if !ocx.select_all_or_error().is_empty() {
+                return Err(NoSolution);
+            }
+            Ok(InferOk { value: (), obligations: vec![] })
+        };
+
+        self.fully_perform_op(
+            Locations::All(span),
+            ConstraintCategory::Boring,
+            type_op::custom::CustomTypeOp::new(op, || "ascribe_user_type_skip_wf".to_string()),
+        )
+        .unwrap_or_else(|err| {
+            span_mirbug!(
+                self,
+                span,
+                "ascribe_user_type_skip_wf `{mir_ty:?}=={user_ty:?}` failed with `{err:?}`",
+            );
+        });
+    }
 }
diff --git a/compiler/rustc_borrowck/src/type_check/input_output.rs b/compiler/rustc_borrowck/src/type_check/input_output.rs
index 8fa43f8528c..fa9ea769a14 100644
--- a/compiler/rustc_borrowck/src/type_check/input_output.rs
+++ b/compiler/rustc_borrowck/src/type_check/input_output.rs
@@ -10,7 +10,7 @@
 use rustc_index::vec::Idx;
 use rustc_infer::infer::LateBoundRegionConversionTime;
 use rustc_middle::mir::*;
-use rustc_middle::ty::Ty;
+use rustc_middle::ty::{self, Ty};
 use rustc_span::Span;
 
 use crate::universal_regions::UniversalRegions;
@@ -18,6 +18,52 @@ use crate::universal_regions::UniversalRegions;
 use super::{Locations, TypeChecker};
 
 impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
+    /// Check explicit closure signature annotation,
+    /// e.g., `|x: FxHashMap<_, &'static u32>| ...`.
+    #[instrument(skip(self, body), level = "debug")]
+    pub(super) fn check_signature_annotation(&mut self, body: &Body<'tcx>) {
+        let mir_def_id = body.source.def_id().expect_local();
+        if !self.tcx().is_closure(mir_def_id.to_def_id()) {
+            return;
+        }
+        let Some(user_provided_poly_sig) =
+            self.tcx().typeck(mir_def_id).user_provided_sigs.get(&mir_def_id)
+        else {
+            return;
+        };
+
+        // Instantiate the canonicalized variables from user-provided signature
+        // (e.g., the `_` in the code above) with fresh variables.
+        // Then replace the bound items in the fn sig with fresh variables,
+        // so that they represent the view from "inside" the closure.
+        let user_provided_sig = self
+            .instantiate_canonical_with_fresh_inference_vars(body.span, &user_provided_poly_sig);
+        let user_provided_sig = self.infcx.replace_bound_vars_with_fresh_vars(
+            body.span,
+            LateBoundRegionConversionTime::FnCall,
+            user_provided_sig,
+        );
+
+        for (&user_ty, arg_decl) in user_provided_sig.inputs().iter().zip(
+            // In MIR, closure args begin with an implicit `self`. Skip it!
+            body.args_iter().skip(1).map(|local| &body.local_decls[local]),
+        ) {
+            self.ascribe_user_type_skip_wf(
+                arg_decl.ty,
+                ty::UserType::Ty(user_ty),
+                arg_decl.source_info.span,
+            );
+        }
+
+        // If the user explicitly annotated the output type, enforce it.
+        let output_decl = &body.local_decls[RETURN_PLACE];
+        self.ascribe_user_type_skip_wf(
+            output_decl.ty,
+            ty::UserType::Ty(user_provided_sig.output()),
+            output_decl.source_info.span,
+        );
+    }
+
     #[instrument(skip(self, body, universal_regions), level = "debug")]
     pub(super) fn equate_inputs_and_outputs(
         &mut self,
@@ -31,39 +77,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
         debug!(?normalized_output_ty);
         debug!(?normalized_input_tys);
 
-        let mir_def_id = body.source.def_id().expect_local();
-
-        // If the user explicitly annotated the input types, extract
-        // those.
-        //
-        // e.g., `|x: FxHashMap<_, &'static u32>| ...`
-        let user_provided_sig = if !self.tcx().is_closure(mir_def_id.to_def_id()) {
-            None
-        } else {
-            let typeck_results = self.tcx().typeck(mir_def_id);
-
-            typeck_results.user_provided_sigs.get(&mir_def_id).map(|user_provided_poly_sig| {
-                // Instantiate the canonicalized variables from
-                // user-provided signature (e.g., the `_` in the code
-                // above) with fresh variables.
-                let poly_sig = self.instantiate_canonical_with_fresh_inference_vars(
-                    body.span,
-                    &user_provided_poly_sig,
-                );
-
-                // Replace the bound items in the fn sig with fresh
-                // variables, so that they represent the view from
-                // "inside" the closure.
-                self.infcx.replace_bound_vars_with_fresh_vars(
-                    body.span,
-                    LateBoundRegionConversionTime::FnCall,
-                    poly_sig,
-                )
-            })
-        };
-
-        debug!(?normalized_input_tys, ?body.local_decls);
-
         // Equate expected input tys with those in the MIR.
         for (argument_index, &normalized_input_ty) in normalized_input_tys.iter().enumerate() {
             if argument_index + 1 >= body.local_decls.len() {
@@ -86,28 +99,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
             );
         }
 
-        if let Some(user_provided_sig) = user_provided_sig {
-            for (argument_index, &user_provided_input_ty) in
-                user_provided_sig.inputs().iter().enumerate()
-            {
-                // In MIR, closures begin an implicit `self`, so
-                // argument N is stored in local N+2.
-                let local = Local::new(argument_index + 2);
-                let mir_input_ty = body.local_decls[local].ty;
-                let mir_input_span = body.local_decls[local].source_info.span;
-
-                // If the user explicitly annotated the input types, enforce those.
-                let user_provided_input_ty =
-                    self.normalize(user_provided_input_ty, Locations::All(mir_input_span));
-
-                self.equate_normalized_input_or_output(
-                    user_provided_input_ty,
-                    mir_input_ty,
-                    mir_input_span,
-                );
-            }
-        }
-
         debug!(
             "equate_inputs_and_outputs: body.yield_ty {:?}, universal_regions.yield_ty {:?}",
             body.yield_ty(),
@@ -153,29 +144,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                 terr
             );
         };
-
-        // If the user explicitly annotated the output types, enforce those.
-        // Note that this only happens for closures.
-        if let Some(user_provided_sig) = user_provided_sig {
-            let user_provided_output_ty = user_provided_sig.output();
-            let user_provided_output_ty =
-                self.normalize(user_provided_output_ty, Locations::All(output_span));
-            if let Err(err) = self.eq_types(
-                user_provided_output_ty,
-                mir_output_ty,
-                Locations::All(output_span),
-                ConstraintCategory::BoringNoLocation,
-            ) {
-                span_mirbug!(
-                    self,
-                    Location::START,
-                    "equate_inputs_and_outputs: `{:?}=={:?}` failed with `{:?}`",
-                    mir_output_ty,
-                    user_provided_output_ty,
-                    err
-                );
-            }
-        }
     }
 
     #[instrument(skip(self), level = "debug")]
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index 247607ff29e..8778a19eeda 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -38,7 +38,6 @@ use rustc_middle::ty::{
 use rustc_span::def_id::CRATE_DEF_ID;
 use rustc_span::{Span, DUMMY_SP};
 use rustc_target::abi::VariantIdx;
-use rustc_trait_selection::traits::query::type_op;
 use rustc_trait_selection::traits::query::type_op::custom::scrape_region_constraints;
 use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp;
 use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput};
@@ -197,6 +196,8 @@ pub(crate) fn type_check<'mir, 'tcx>(
     }
 
     checker.equate_inputs_and_outputs(&body, universal_regions, &normalized_inputs_and_output);
+    checker.check_signature_annotation(&body);
+
     liveness::generate(
         &mut checker,
         body,
@@ -391,23 +392,14 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
                         check_err(self, promoted_body, ty, promoted_ty);
                     }
                 } else {
-                    if let Err(terr) = self.cx.fully_perform_op(
-                        locations,
-                        ConstraintCategory::Boring,
-                        self.cx.param_env.and(type_op::ascribe_user_type::AscribeUserType::new(
-                            constant.literal.ty(),
+                    self.cx.ascribe_user_type(
+                        constant.literal.ty(),
+                        UserType::TypeOf(
                             uv.def.did,
                             UserSubsts { substs: uv.substs, user_self_ty: None },
-                        )),
-                    ) {
-                        span_mirbug!(
-                            self,
-                            constant,
-                            "bad constant type {:?} ({:?})",
-                            constant,
-                            terr
-                        );
-                    }
+                        ),
+                        locations.span(&self.cx.body),
+                    );
                 }
             } else if let Some(static_def_id) = constant.check_static_ptr(tcx) {
                 let unnormalized_ty = tcx.type_of(static_def_id);
@@ -1041,58 +1033,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
         debug!(?self.user_type_annotations);
         for user_annotation in self.user_type_annotations {
             let CanonicalUserTypeAnnotation { span, ref user_ty, inferred_ty } = *user_annotation;
-            let inferred_ty = self.normalize(inferred_ty, Locations::All(span));
             let annotation = self.instantiate_canonical_with_fresh_inference_vars(span, user_ty);
-            debug!(?annotation);
-            match annotation {
-                UserType::Ty(mut ty) => {
-                    ty = self.normalize(ty, Locations::All(span));
-
-                    if let Err(terr) = self.eq_types(
-                        ty,
-                        inferred_ty,
-                        Locations::All(span),
-                        ConstraintCategory::BoringNoLocation,
-                    ) {
-                        span_mirbug!(
-                            self,
-                            user_annotation,
-                            "bad user type ({:?} = {:?}): {:?}",
-                            ty,
-                            inferred_ty,
-                            terr
-                        );
-                    }
-
-                    self.prove_predicate(
-                        ty::Binder::dummy(ty::PredicateKind::WellFormed(inferred_ty.into())),
-                        Locations::All(span),
-                        ConstraintCategory::TypeAnnotation,
-                    );
-                }
-                UserType::TypeOf(def_id, user_substs) => {
-                    if let Err(terr) = self.fully_perform_op(
-                        Locations::All(span),
-                        ConstraintCategory::BoringNoLocation,
-                        self.param_env.and(type_op::ascribe_user_type::AscribeUserType::new(
-                            inferred_ty,
-                            def_id,
-                            user_substs,
-                        )),
-                    ) {
-                        span_mirbug!(
-                            self,
-                            user_annotation,
-                            "bad user type AscribeUserType({:?}, {:?} {:?}, type_of={:?}): {:?}",
-                            inferred_ty,
-                            def_id,
-                            user_substs,
-                            self.tcx().type_of(def_id),
-                            terr,
-                        );
-                    }
-                }
-            }
+            self.ascribe_user_type(inferred_ty, annotation, span);
         }
     }
 
diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs
index 9a111cb8609..5a7957be318 100644
--- a/compiler/rustc_hir_analysis/src/astconv/mod.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs
@@ -106,11 +106,12 @@ pub trait AstConv<'tcx> {
         poly_trait_ref: ty::PolyTraitRef<'tcx>,
     ) -> Ty<'tcx>;
 
-    /// Normalize an associated type coming from the user.
-    ///
-    /// This should only be used by astconv. Use `FnCtxt::normalize`
-    /// or `ObligationCtxt::normalize` in downstream crates.
-    fn normalize_ty(&self, span: Span, ty: Ty<'tcx>) -> Ty<'tcx>;
+    /// Returns `AdtDef` if `ty` is an ADT.
+    /// Note that `ty` might be a projection type that needs normalization.
+    /// This used to get the enum variants in scope of the type.
+    /// For example, `Self::A` could refer to an associated type
+    /// or to an enum variant depending on the result of this function.
+    fn probe_adt(&self, span: Span, ty: Ty<'tcx>) -> Option<ty::AdtDef<'tcx>>;
 
     /// Invoked when we encounter an error from some prior pass
     /// (e.g., resolve) that is translated into a ty-error. This is
@@ -485,14 +486,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                                 // Avoid ICE #86756 when type error recovery goes awry.
                                 return tcx.ty_error().into();
                             }
-                            self.astconv
-                                .normalize_ty(
-                                    self.span,
-                                    tcx.at(self.span)
-                                        .bound_type_of(param.def_id)
-                                        .subst(tcx, substs),
-                                )
-                                .into()
+                            tcx.at(self.span).bound_type_of(param.def_id).subst(tcx, substs).into()
                         } else if infer_args {
                             self.astconv.ty_infer(Some(param), self.span).into()
                         } else {
@@ -1267,7 +1261,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         item_segment: &hir::PathSegment<'_>,
     ) -> Ty<'tcx> {
         let substs = self.ast_path_substs_for_ty(span, did, item_segment);
-        self.normalize_ty(span, self.tcx().at(span).bound_type_of(did).subst(self.tcx(), substs))
+        self.tcx().at(span).bound_type_of(did).subst(self.tcx(), substs)
     }
 
     fn conv_object_ty_poly_trait_ref(
@@ -1832,7 +1826,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         Ok(bound)
     }
 
-    // Create a type from a path to an associated type.
+    // Create a type from a path to an associated type or to an enum variant.
     // For a path `A::B::C::D`, `qself_ty` and `qself_def` are the type and def for `A::B::C`
     // and item_segment is the path segment for `D`. We return a type and a def for
     // the whole path.
@@ -1860,7 +1854,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
 
         // Check if we have an enum variant.
         let mut variant_resolution = None;
-        if let ty::Adt(adt_def, adt_substs) = qself_ty.kind() {
+        if let Some(adt_def) = self.probe_adt(span, qself_ty) {
             if adt_def.is_enum() {
                 let variant_def = adt_def
                     .variants()
@@ -1962,6 +1956,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 let Some(assoc_ty_did) = self.lookup_assoc_ty(assoc_ident, hir_ref_id, span, impl_) else {
                     continue;
                 };
+                let ty::Adt(_, adt_substs) = qself_ty.kind() else {
+                    // FIXME(inherent_associated_types)
+                    bug!("unimplemented: non-adt self of inherent assoc ty");
+                };
                 let item_substs = self.create_substs_for_associated_item(
                     span,
                     assoc_ty_did,
@@ -1969,7 +1967,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                     adt_substs,
                 );
                 let ty = tcx.bound_type_of(assoc_ty_did).subst(tcx, item_substs);
-                let ty = self.normalize_ty(span, ty);
                 return Ok((ty, DefKind::AssocTy, assoc_ty_did));
             }
         }
@@ -2066,7 +2063,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         };
 
         let ty = self.projected_ty_from_poly_trait_ref(span, assoc_ty_did, assoc_segment, bound);
-        let ty = self.normalize_ty(span, ty);
 
         if let Some(variant_def_id) = variant_resolution {
             tcx.struct_span_lint_hir(
@@ -2202,7 +2198,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
 
         debug!("qpath_to_ty: trait_ref={:?}", trait_ref);
 
-        self.normalize_ty(span, tcx.mk_projection(item_def_id, item_substs))
+        tcx.mk_projection(item_def_id, item_substs)
     }
 
     pub fn prohibit_generics<'a>(
@@ -2319,6 +2315,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         self_ty: Option<Ty<'tcx>>,
         kind: DefKind,
         def_id: DefId,
+        span: Span,
     ) -> Vec<PathSeg> {
         // We need to extract the type parameters supplied by the user in
         // the path `path`. Due to the current setup, this is a bit of a
@@ -2386,8 +2383,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
 
             // Case 2. Reference to a variant constructor.
             DefKind::Ctor(CtorOf::Variant, ..) | DefKind::Variant => {
-                let adt_def = self_ty.map(|t| t.ty_adt_def().unwrap());
-                let (generics_def_id, index) = if let Some(adt_def) = adt_def {
+                let (generics_def_id, index) = if let Some(self_ty) = self_ty {
+                    let adt_def = self.probe_adt(span, self_ty).unwrap();
                     debug_assert!(adt_def.is_enum());
                     (adt_def.did(), last)
                 } else if last >= 1 && segments[last - 1].args.is_some() {
@@ -2463,7 +2460,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                     err.note("`impl Trait` types can't have type parameters");
                 });
                 let substs = self.ast_path_substs_for_ty(span, did, item_segment.0);
-                self.normalize_ty(span, tcx.mk_opaque(did, substs))
+                tcx.mk_opaque(did, substs)
             }
             Res::Def(
                 DefKind::Enum
@@ -2483,7 +2480,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 assert_eq!(opt_self_ty, None);
 
                 let path_segs =
-                    self.def_ids_for_value_path_segments(path.segments, None, kind, def_id);
+                    self.def_ids_for_value_path_segments(path.segments, None, kind, def_id, span);
                 let generic_segs: FxHashSet<_> =
                     path_segs.iter().map(|PathSeg(_, index)| index).collect();
                 self.prohibit_generics(
@@ -2623,7 +2620,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                     }
                     tcx.ty_error_with_guaranteed(err.emit())
                 } else {
-                    self.normalize_ty(span, ty)
+                    ty
                 }
             }
             Res::Def(DefKind::AssocTy, def_id) => {
@@ -2766,8 +2763,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                     None,
                     ty::BoundConstness::NotConst,
                 );
-                EarlyBinder(self.normalize_ty(span, tcx.at(span).type_of(def_id)))
-                    .subst(tcx, substs)
+                EarlyBinder(tcx.at(span).type_of(def_id)).subst(tcx, substs)
             }
             hir::TyKind::Array(ref ty, ref length) => {
                 let length = match length {
@@ -2777,8 +2773,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                     }
                 };
 
-                let array_ty = tcx.mk_ty(ty::Array(self.ast_ty_to_ty(ty), length));
-                self.normalize_ty(ast_ty.span, array_ty)
+                tcx.mk_ty(ty::Array(self.ast_ty_to_ty(ty), length))
             }
             hir::TyKind::Typeof(ref e) => {
                 let ty_erased = tcx.type_of(e.def_id);
diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs
index 7afde550b42..b7f259668a1 100644
--- a/compiler/rustc_hir_analysis/src/collect.rs
+++ b/compiler/rustc_hir_analysis/src/collect.rs
@@ -505,9 +505,9 @@ impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> {
         }
     }
 
-    fn normalize_ty(&self, _span: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
-        // Types in item signatures are not normalized to avoid undue dependencies.
-        ty
+    fn probe_adt(&self, _span: Span, ty: Ty<'tcx>) -> Option<ty::AdtDef<'tcx>> {
+        // FIXME(#103640): Should we handle the case where `ty` is a projection?
+        ty.ty_adt_def()
     }
 
     fn set_tainted_by_errors(&self, _: ErrorGuaranteed) {
diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs
index 3453da500b9..399702fd41a 100644
--- a/compiler/rustc_hir_typeck/src/closure.rs
+++ b/compiler/rustc_hir_typeck/src/closure.rs
@@ -647,14 +647,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             ),
             bound_vars,
         );
-        // Astconv can't normalize inputs or outputs with escaping bound vars,
-        // so normalize them here, after we've wrapped them in a binder.
-        let result = self.normalize(self.tcx.hir().span(hir_id), result);
 
         let c_result = self.inh.infcx.canonicalize_response(result);
         self.typeck_results.borrow_mut().user_provided_sigs.insert(expr_def_id, c_result);
 
-        result
+        // Normalize only after registering in `user_provided_sigs`.
+        self.normalize(self.tcx.hir().span(hir_id), result)
     }
 
     /// Invoked when we are translating the generator that results
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
index 634688de01a..b7681d108ed 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
@@ -1,7 +1,7 @@
 use crate::callee::{self, DeferredCallResolution};
 use crate::method::{self, MethodCallee, SelfSource};
 use crate::rvalue_scopes;
-use crate::{BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy};
+use crate::{BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy, RawTy};
 use rustc_data_structures::captures::Captures;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::{Applicability, Diagnostic, ErrorGuaranteed, MultiSpan};
@@ -24,7 +24,7 @@ use rustc_middle::ty::visit::TypeVisitable;
 use rustc_middle::ty::{
     self, AdtKind, CanonicalUserType, DefIdTree, GenericParamDefKind, Ty, UserType,
 };
-use rustc_middle::ty::{GenericArgKind, InternalSubsts, SubstsRef, UserSelfTy, UserSubsts};
+use rustc_middle::ty::{GenericArgKind, SubstsRef, UserSelfTy, UserSubsts};
 use rustc_session::lint;
 use rustc_span::def_id::LocalDefId;
 use rustc_span::hygiene::DesugaringKind;
@@ -161,47 +161,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     pub fn write_method_call(&self, hir_id: hir::HirId, method: MethodCallee<'tcx>) {
         self.write_resolution(hir_id, Ok((DefKind::AssocFn, method.def_id)));
         self.write_substs(hir_id, method.substs);
-
-        // When the method is confirmed, the `method.substs` includes
-        // parameters from not just the method, but also the impl of
-        // the method -- in particular, the `Self` type will be fully
-        // resolved. However, those are not something that the "user
-        // specified" -- i.e., those types come from the inferred type
-        // of the receiver, not something the user wrote. So when we
-        // create the user-substs, we want to replace those earlier
-        // types with just the types that the user actually wrote --
-        // that is, those that appear on the *method itself*.
-        //
-        // As an example, if the user wrote something like
-        // `foo.bar::<u32>(...)` -- the `Self` type here will be the
-        // type of `foo` (possibly adjusted), but we don't want to
-        // include that. We want just the `[_, u32]` part.
-        if !method.substs.is_empty() {
-            let method_generics = self.tcx.generics_of(method.def_id);
-            if !method_generics.params.is_empty() {
-                let user_type_annotation = self.probe(|_| {
-                    let user_substs = UserSubsts {
-                        substs: InternalSubsts::for_item(self.tcx, method.def_id, |param, _| {
-                            let i = param.index as usize;
-                            if i < method_generics.parent_count {
-                                self.var_for_def(DUMMY_SP, param)
-                            } else {
-                                method.substs[i]
-                            }
-                        }),
-                        user_self_ty: None, // not relevant here
-                    };
-
-                    self.canonicalize_user_type_annotation(UserType::TypeOf(
-                        method.def_id,
-                        user_substs,
-                    ))
-                });
-
-                debug!("write_method_call: user_type_annotation={:?}", user_type_annotation);
-                self.write_user_type_annotation(hir_id, user_type_annotation);
-            }
-        }
     }
 
     pub fn write_substs(&self, node_id: hir::HirId, substs: SubstsRef<'tcx>) {
@@ -410,23 +369,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
-    pub fn to_ty(&self, ast_t: &hir::Ty<'_>) -> Ty<'tcx> {
+    pub fn handle_raw_ty(&self, span: Span, ty: Ty<'tcx>) -> RawTy<'tcx> {
+        RawTy { raw: ty, normalized: self.normalize(span, ty) }
+    }
+
+    pub fn to_ty(&self, ast_t: &hir::Ty<'_>) -> RawTy<'tcx> {
         let t = <dyn AstConv<'_>>::ast_ty_to_ty(self, ast_t);
         self.register_wf_obligation(t.into(), ast_t.span, traits::WellFormed(None));
-        t
+        self.handle_raw_ty(ast_t.span, t)
     }
 
     pub fn to_ty_saving_user_provided_ty(&self, ast_ty: &hir::Ty<'_>) -> Ty<'tcx> {
         let ty = self.to_ty(ast_ty);
         debug!("to_ty_saving_user_provided_ty: ty={:?}", ty);
 
-        if Self::can_contain_user_lifetime_bounds(ty) {
-            let c_ty = self.canonicalize_response(UserType::Ty(ty));
+        if Self::can_contain_user_lifetime_bounds(ty.raw) {
+            let c_ty = self.canonicalize_response(UserType::Ty(ty.raw));
             debug!("to_ty_saving_user_provided_ty: c_ty={:?}", c_ty);
             self.typeck_results.borrow_mut().user_provided_types_mut().insert(ast_ty.hir_id, c_ty);
         }
 
-        ty
+        ty.normalized
+    }
+
+    pub(super) fn user_substs_for_adt(ty: RawTy<'tcx>) -> UserSubsts<'tcx> {
+        match (ty.raw.kind(), ty.normalized.kind()) {
+            (ty::Adt(_, substs), _) => UserSubsts { substs, user_self_ty: None },
+            (_, ty::Adt(adt, substs)) => UserSubsts {
+                substs,
+                user_self_ty: Some(UserSelfTy { impl_def_id: adt.did(), self_ty: ty.raw }),
+            },
+            _ => bug!("non-adt type {:?}", ty),
+        }
     }
 
     pub fn array_length_to_const(&self, length: &hir::ArrayLen) -> ty::Const<'tcx> {
@@ -780,7 +754,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         qpath: &'tcx QPath<'tcx>,
         hir_id: hir::HirId,
         span: Span,
-    ) -> (Res, Option<Ty<'tcx>>, &'tcx [hir::PathSegment<'tcx>]) {
+    ) -> (Res, Option<RawTy<'tcx>>, &'tcx [hir::PathSegment<'tcx>]) {
         debug!(
             "resolve_ty_and_res_fully_qualified_call: qpath={:?} hir_id={:?} span={:?}",
             qpath, hir_id, span
@@ -803,7 +777,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // to be object-safe.
                 // We manually call `register_wf_obligation` in the success path
                 // below.
-                (<dyn AstConv<'_>>::ast_ty_to_ty_in_path(self, qself), qself, segment)
+                let ty = <dyn AstConv<'_>>::ast_ty_to_ty_in_path(self, qself);
+                (self.handle_raw_ty(span, ty), qself, segment)
             }
             QPath::LangItem(..) => {
                 bug!("`resolve_ty_and_res_fully_qualified_call` called on `LangItem`")
@@ -811,7 +786,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         };
         if let Some(&cached_result) = self.typeck_results.borrow().type_dependent_defs().get(hir_id)
         {
-            self.register_wf_obligation(ty.into(), qself.span, traits::WellFormed(None));
+            self.register_wf_obligation(ty.raw.into(), qself.span, traits::WellFormed(None));
             // Return directly on cache hit. This is useful to avoid doubly reporting
             // errors with default match binding modes. See #44614.
             let def = cached_result.map_or(Res::Err, |(kind, def_id)| Res::Def(kind, def_id));
@@ -819,7 +794,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
         let item_name = item_segment.ident;
         let result = self
-            .resolve_fully_qualified_call(span, item_name, ty, qself.span, hir_id)
+            .resolve_fully_qualified_call(span, item_name, ty.normalized, qself.span, hir_id)
             .or_else(|error| {
                 let result = match error {
                     method::MethodError::PrivateMatch(kind, def_id, _) => Ok((kind, def_id)),
@@ -830,13 +805,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // a WF obligation for `dyn MyTrait` when method lookup fails. Otherwise,
                 // register a WF obligation so that we can detect any additional
                 // errors in the self type.
-                if !(matches!(error, method::MethodError::NoMatch(_)) && ty.is_trait()) {
-                    self.register_wf_obligation(ty.into(), qself.span, traits::WellFormed(None));
+                if !(matches!(error, method::MethodError::NoMatch(_)) && ty.normalized.is_trait()) {
+                    self.register_wf_obligation(
+                        ty.raw.into(),
+                        qself.span,
+                        traits::WellFormed(None),
+                    );
                 }
                 if item_name.name != kw::Empty {
                     if let Some(mut e) = self.report_method_error(
                         span,
-                        ty,
+                        ty.normalized,
                         item_name,
                         SelfSource::QPath(qself),
                         error,
@@ -849,7 +828,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             });
 
         if result.is_ok() {
-            self.register_wf_obligation(ty.into(), qself.span, traits::WellFormed(None));
+            self.register_wf_obligation(ty.raw.into(), qself.span, traits::WellFormed(None));
         }
 
         // Write back the new resolution.
@@ -986,7 +965,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     pub fn instantiate_value_path(
         &self,
         segments: &[hir::PathSegment<'_>],
-        self_ty: Option<Ty<'tcx>>,
+        self_ty: Option<RawTy<'tcx>>,
         res: Res,
         span: Span,
         hir_id: hir::HirId,
@@ -996,7 +975,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let path_segs = match res {
             Res::Local(_) | Res::SelfCtor(_) => vec![],
             Res::Def(kind, def_id) => <dyn AstConv<'_>>::def_ids_for_value_path_segments(
-                self, segments, self_ty, kind, def_id,
+                self,
+                segments,
+                self_ty.map(|ty| ty.raw),
+                kind,
+                def_id,
+                span,
             ),
             _ => bug!("instantiate_value_path on {:?}", res),
         };
@@ -1007,8 +991,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             Res::Def(DefKind::Ctor(CtorOf::Variant, _), _)
                 if let Some(self_ty) = self_ty =>
             {
-                let adt_def = self_ty.ty_adt_def().unwrap();
-                user_self_ty = Some(UserSelfTy { impl_def_id: adt_def.did(), self_ty });
+                let adt_def = self_ty.normalized.ty_adt_def().unwrap();
+                user_self_ty = Some(UserSelfTy { impl_def_id: adt_def.did(), self_ty: self_ty.raw });
                 is_alias_variant_ctor = true;
             }
             Res::Def(DefKind::AssocFn | DefKind::AssocConst, def_id) => {
@@ -1027,7 +1011,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             // inherent impl, we need to record the
                             // `T` for posterity (see `UserSelfTy` for
                             // details).
-                            let self_ty = self_ty.expect("UFCS sugared assoc missing Self");
+                            let self_ty = self_ty.expect("UFCS sugared assoc missing Self").raw;
                             user_self_ty = Some(UserSelfTy { impl_def_id: container_id, self_ty });
                         }
                     }
@@ -1109,19 +1093,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             .unwrap_or(false);
 
         let (res, self_ctor_substs) = if let Res::SelfCtor(impl_def_id) = res {
-            let ty = self.normalize_ty(span, tcx.at(span).type_of(impl_def_id));
-            match *ty.kind() {
-                ty::Adt(adt_def, substs) if adt_def.has_ctor() => {
-                    let variant = adt_def.non_enum_variant();
-                    let (ctor_kind, ctor_def_id) = variant.ctor.unwrap();
-                    (Res::Def(DefKind::Ctor(CtorOf::Struct, ctor_kind), ctor_def_id), Some(substs))
+            let ty = self.handle_raw_ty(span, tcx.at(span).type_of(impl_def_id));
+            match ty.normalized.ty_adt_def() {
+                Some(adt_def) if adt_def.has_ctor() => {
+                    let (ctor_kind, ctor_def_id) = adt_def.non_enum_variant().ctor.unwrap();
+                    let new_res = Res::Def(DefKind::Ctor(CtorOf::Struct, ctor_kind), ctor_def_id);
+                    let user_substs = Self::user_substs_for_adt(ty);
+                    user_self_ty = user_substs.user_self_ty;
+                    (new_res, Some(user_substs.substs))
                 }
                 _ => {
                     let mut err = tcx.sess.struct_span_err(
                         span,
                         "the `Self` constructor can only be used with tuple or unit structs",
                     );
-                    if let Some(adt_def) = ty.ty_adt_def() {
+                    if let Some(adt_def) = ty.normalized.ty_adt_def() {
                         match adt_def.adt_kind() {
                             AdtKind::Enum => {
                                 err.help("did you mean to use one of the enum's variants?");
@@ -1193,7 +1179,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         <dyn AstConv<'_>>::ast_region_to_region(self.fcx, lt, Some(param)).into()
                     }
                     (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => {
-                        self.fcx.to_ty(ty).into()
+                        self.fcx.to_ty(ty).raw.into()
                     }
                     (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => {
                         self.fcx.const_arg_to_const(&ct.value, param.def_id).into()
@@ -1225,10 +1211,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             // If we have a default, then we it doesn't matter that we're not
                             // inferring the type arguments: we provide the default where any
                             // is missing.
-                            let default = tcx.bound_type_of(param.def_id);
-                            self.fcx
-                                .normalize_ty(self.span, default.subst(tcx, substs.unwrap()))
-                                .into()
+                            tcx.bound_type_of(param.def_id).subst(tcx, substs.unwrap()).into()
                         } else {
                             // If no type arguments were provided, we have to infer them.
                             // This case also occurs as a result of some malformed input, e.g.
@@ -1250,13 +1233,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             }
         }
 
-        let substs = self_ctor_substs.unwrap_or_else(|| {
+        let substs_raw = self_ctor_substs.unwrap_or_else(|| {
             <dyn AstConv<'_>>::create_substs_for_generic_args(
                 tcx,
                 def_id,
                 &[],
                 has_self,
-                self_ty,
+                self_ty.map(|s| s.raw),
                 &arg_count,
                 &mut CreateCtorSubstsContext {
                     fcx: self,
@@ -1269,7 +1252,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         });
 
         // First, store the "user substs" for later.
-        self.write_user_type_annotation_from_substs(hir_id, def_id, substs, user_self_ty);
+        self.write_user_type_annotation_from_substs(hir_id, def_id, substs_raw, user_self_ty);
+
+        // Normalize only after registering type annotations.
+        let substs = self.normalize(span, substs_raw);
 
         self.add_required_obligations_for_hir(span, def_id, &substs, hir_id);
 
@@ -1287,6 +1273,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // with the substituted impl type.
             // This also occurs for an enum variant on a type alias.
             let impl_ty = self.normalize(span, tcx.bound_type_of(impl_def_id).subst(tcx, substs));
+            let self_ty = self.normalize(span, self_ty);
             match self.at(&self.misc(span), self.param_env).eq(impl_ty, self_ty) {
                 Ok(ok) => self.register_infer_ok_obligations(ok),
                 Err(_) => {
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index 7d6b4aaebf4..da411d0642e 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -5,7 +5,7 @@ use crate::method::MethodCallee;
 use crate::Expectation::*;
 use crate::TupleArgumentsFlag::*;
 use crate::{
-    struct_span_err, BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy, Needs,
+    struct_span_err, BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy, Needs, RawTy,
     TupleArgumentsFlag,
 };
 use rustc_ast as ast;
@@ -1231,31 +1231,35 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 );
                 return None;
             }
-            Res::Def(DefKind::Variant, _) => match ty.kind() {
-                ty::Adt(adt, substs) => Some((adt.variant_of_res(def), adt.did(), substs)),
-                _ => bug!("unexpected type: {:?}", ty),
+            Res::Def(DefKind::Variant, _) => match ty.normalized.ty_adt_def() {
+                Some(adt) => {
+                    Some((adt.variant_of_res(def), adt.did(), Self::user_substs_for_adt(ty)))
+                }
+                _ => bug!("unexpected type: {:?}", ty.normalized),
             },
             Res::Def(DefKind::Struct | DefKind::Union | DefKind::TyAlias | DefKind::AssocTy, _)
             | Res::SelfTyParam { .. }
-            | Res::SelfTyAlias { .. } => match ty.kind() {
-                ty::Adt(adt, substs) if !adt.is_enum() => {
-                    Some((adt.non_enum_variant(), adt.did(), substs))
+            | Res::SelfTyAlias { .. } => match ty.normalized.ty_adt_def() {
+                Some(adt) if !adt.is_enum() => {
+                    Some((adt.non_enum_variant(), adt.did(), Self::user_substs_for_adt(ty)))
                 }
                 _ => None,
             },
             _ => bug!("unexpected definition: {:?}", def),
         };
 
-        if let Some((variant, did, substs)) = variant {
+        if let Some((variant, did, ty::UserSubsts { substs, user_self_ty })) = variant {
             debug!("check_struct_path: did={:?} substs={:?}", did, substs);
-            self.write_user_type_annotation_from_substs(hir_id, did, substs, None);
+
+            // Register type annotation.
+            self.write_user_type_annotation_from_substs(hir_id, did, substs, user_self_ty);
 
             // Check bounds on type arguments used in the path.
             self.add_required_obligations_for_hir(path_span, did, substs, hir_id);
 
-            Some((variant, ty))
+            Some((variant, ty.normalized))
         } else {
-            match ty.kind() {
+            match ty.normalized.kind() {
                 ty::Error(_) => {
                     // E0071 might be caused by a spelling error, which will have
                     // already caused an error message and probably a suggestion
@@ -1268,7 +1272,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         path_span,
                         E0071,
                         "expected struct, variant or union type, found {}",
-                        ty.sort_string(self.tcx)
+                        ty.normalized.sort_string(self.tcx)
                     )
                     .span_label(path_span, "not a struct")
                     .emit();
@@ -1656,20 +1660,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         qpath: &QPath<'_>,
         path_span: Span,
         hir_id: hir::HirId,
-    ) -> (Res, Ty<'tcx>) {
+    ) -> (Res, RawTy<'tcx>) {
         match *qpath {
             QPath::Resolved(ref maybe_qself, ref path) => {
-                let self_ty = maybe_qself.as_ref().map(|qself| self.to_ty(qself));
+                let self_ty = maybe_qself.as_ref().map(|qself| self.to_ty(qself).raw);
                 let ty = <dyn AstConv<'_>>::res_to_ty(self, self_ty, path, true);
-                (path.res, ty)
+                (path.res, self.handle_raw_ty(path_span, ty))
             }
             QPath::TypeRelative(ref qself, ref segment) => {
                 let ty = self.to_ty(qself);
 
                 let result = <dyn AstConv<'_>>::associated_path_to_ty(
-                    self, hir_id, path_span, ty, qself, segment, true,
+                    self, hir_id, path_span, ty.raw, qself, segment, true,
                 );
                 let ty = result.map(|(ty, _, _)| ty).unwrap_or_else(|_| self.tcx().ty_error());
+                let ty = self.handle_raw_ty(path_span, ty);
                 let result = result.map(|(_, kind, def_id)| (kind, def_id));
 
                 // Write back the new resolution.
@@ -1678,7 +1683,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 (result.map_or(Res::Err, |(kind, def_id)| Res::Def(kind, def_id)), ty)
             }
             QPath::LangItem(lang_item, span, id) => {
-                self.resolve_lang_item_path(lang_item, span, hir_id, id)
+                let (res, ty) = self.resolve_lang_item_path(lang_item, span, hir_id, id);
+                (res, self.handle_raw_ty(path_span, ty))
             }
         }
     }
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
index 30b59da7852..6347b9a69a0 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
@@ -17,8 +17,7 @@ use rustc_infer::infer::error_reporting::TypeErrCtxt;
 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
 use rustc_middle::ty::subst::GenericArgKind;
-use rustc_middle::ty::visit::TypeVisitable;
-use rustc_middle::ty::{self, Const, Ty, TyCtxt};
+use rustc_middle::ty::{self, Const, Ty, TyCtxt, TypeVisitable};
 use rustc_session::Session;
 use rustc_span::symbol::Ident;
 use rustc_span::{self, Span};
@@ -298,11 +297,14 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
         self.tcx().mk_projection(item_def_id, item_substs)
     }
 
-    fn normalize_ty(&self, span: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
-        if ty.has_escaping_bound_vars() {
-            ty // FIXME: normalization and escaping regions
-        } else {
-            self.normalize(span, ty)
+    fn probe_adt(&self, span: Span, ty: Ty<'tcx>) -> Option<ty::AdtDef<'tcx>> {
+        match ty.kind() {
+            ty::Adt(adt_def, _) => Some(*adt_def),
+            // FIXME(#104767): Should we handle bound regions here?
+            ty::Alias(ty::Projection, _) if !ty.has_escaping_bound_vars() => {
+                self.normalize(span, ty).ty_adt_def()
+            }
+            _ => None,
         }
     }
 
@@ -310,7 +312,21 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
         self.infcx.set_tainted_by_errors(e)
     }
 
-    fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, _span: Span) {
+    fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, span: Span) {
+        // FIXME: normalization and escaping regions
+        let ty = if !ty.has_escaping_bound_vars() { self.normalize(span, ty) } else { ty };
         self.write_ty(hir_id, ty)
     }
 }
+
+/// Represents a user-provided type in the raw form (never normalized).
+///
+/// This is a bridge between the interface of `AstConv`, which outputs a raw `Ty`,
+/// and the API in this module, which expect `Ty` to be fully normalized.
+#[derive(Clone, Copy, Debug)]
+pub struct RawTy<'tcx> {
+    pub raw: Ty<'tcx>,
+
+    /// The normalized form of `raw`, stored here for efficiency.
+    pub normalized: Ty<'tcx>,
+}
diff --git a/compiler/rustc_hir_typeck/src/gather_locals.rs b/compiler/rustc_hir_typeck/src/gather_locals.rs
index 9a096f24fac..15dd3412c34 100644
--- a/compiler/rustc_hir_typeck/src/gather_locals.rs
+++ b/compiler/rustc_hir_typeck/src/gather_locals.rs
@@ -77,7 +77,8 @@ impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> {
             Some(ref ty) => {
                 let o_ty = self.fcx.to_ty(&ty);
 
-                let c_ty = self.fcx.inh.infcx.canonicalize_user_type_annotation(UserType::Ty(o_ty));
+                let c_ty =
+                    self.fcx.inh.infcx.canonicalize_user_type_annotation(UserType::Ty(o_ty.raw));
                 debug!("visit_local: ty.hir_id={:?} o_ty={:?} c_ty={:?}", ty.hir_id, o_ty, c_ty);
                 self.fcx
                     .typeck_results
@@ -85,7 +86,7 @@ impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> {
                     .user_provided_types_mut()
                     .insert(ty.hir_id, c_ty);
 
-                Some(LocalTy { decl_ty: o_ty, revealed_ty: o_ty })
+                Some(LocalTy { decl_ty: o_ty.normalized, revealed_ty: o_ty.normalized })
             }
             None => None,
         };
diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs
index 99e09b86a23..69929589541 100644
--- a/compiler/rustc_hir_typeck/src/lib.rs
+++ b/compiler/rustc_hir_typeck/src/lib.rs
@@ -297,7 +297,7 @@ fn typeck_with_fallback<'tcx>(
         fcx.resolve_generator_interiors(def_id.to_def_id());
 
         for (ty, span, code) in fcx.deferred_sized_obligations.borrow_mut().drain(..) {
-            let ty = fcx.normalize_ty(span, ty);
+            let ty = fcx.normalize(span, ty);
             fcx.require_type_is_sized(ty, span, code);
         }
 
diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs
index 218c54688aa..a2c6e246610 100644
--- a/compiler/rustc_hir_typeck/src/method/confirm.rs
+++ b/compiler/rustc_hir_typeck/src/method/confirm.rs
@@ -12,7 +12,8 @@ use rustc_middle::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutabili
 use rustc_middle::ty::fold::TypeFoldable;
 use rustc_middle::ty::subst::{self, SubstsRef};
 use rustc_middle::ty::{self, GenericParamDefKind, Ty};
-use rustc_span::Span;
+use rustc_middle::ty::{InternalSubsts, UserSubsts, UserType};
+use rustc_span::{Span, DUMMY_SP};
 use rustc_trait_selection::traits;
 
 use std::iter;
@@ -372,7 +373,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
                             .into()
                     }
                     (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => {
-                        self.cfcx.to_ty(ty).into()
+                        self.cfcx.to_ty(ty).raw.into()
                     }
                     (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => {
                         self.cfcx.const_arg_to_const(&ct.value, param.def_id).into()
@@ -397,7 +398,8 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
                 self.cfcx.var_for_def(self.cfcx.span, param)
             }
         }
-        <dyn AstConv<'_>>::create_substs_for_generic_args(
+
+        let substs = <dyn AstConv<'_>>::create_substs_for_generic_args(
             self.tcx,
             pick.item.def_id,
             parent_substs,
@@ -405,7 +407,47 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
             None,
             &arg_count_correct,
             &mut MethodSubstsCtxt { cfcx: self, pick, seg },
-        )
+        );
+
+        // When the method is confirmed, the `substs` includes
+        // parameters from not just the method, but also the impl of
+        // the method -- in particular, the `Self` type will be fully
+        // resolved. However, those are not something that the "user
+        // specified" -- i.e., those types come from the inferred type
+        // of the receiver, not something the user wrote. So when we
+        // create the user-substs, we want to replace those earlier
+        // types with just the types that the user actually wrote --
+        // that is, those that appear on the *method itself*.
+        //
+        // As an example, if the user wrote something like
+        // `foo.bar::<u32>(...)` -- the `Self` type here will be the
+        // type of `foo` (possibly adjusted), but we don't want to
+        // include that. We want just the `[_, u32]` part.
+        if !substs.is_empty() && !generics.params.is_empty() {
+            let user_type_annotation = self.probe(|_| {
+                let user_substs = UserSubsts {
+                    substs: InternalSubsts::for_item(self.tcx, pick.item.def_id, |param, _| {
+                        let i = param.index as usize;
+                        if i < generics.parent_count {
+                            self.fcx.var_for_def(DUMMY_SP, param)
+                        } else {
+                            substs[i]
+                        }
+                    }),
+                    user_self_ty: None, // not relevant here
+                };
+
+                self.fcx.canonicalize_user_type_annotation(UserType::TypeOf(
+                    pick.item.def_id,
+                    user_substs,
+                ))
+            });
+
+            debug!("instantiate_method_substs: user_type_annotation={:?}", user_type_annotation);
+            self.fcx.write_user_type_annotation(self.call_expr.hir_id, user_type_annotation);
+        }
+
+        self.normalize(self.span, substs)
     }
 
     fn unify_receivers(
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index e0304fa2d3b..1075378a159 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -1,4 +1,4 @@
-use crate::FnCtxt;
+use crate::{FnCtxt, RawTy};
 use rustc_ast as ast;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::{
@@ -842,7 +842,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         &self,
         pat: &Pat<'tcx>,
         qpath: &hir::QPath<'_>,
-        path_resolution: (Res, Option<Ty<'tcx>>, &'tcx [hir::PathSegment<'tcx>]),
+        path_resolution: (Res, Option<RawTy<'tcx>>, &'tcx [hir::PathSegment<'tcx>]),
         expected: Ty<'tcx>,
         ti: TopInfo<'tcx>,
     ) -> Ty<'tcx> {
diff --git a/compiler/rustc_middle/src/traits/query.rs b/compiler/rustc_middle/src/traits/query.rs
index 6a149be3137..543f5b87e00 100644
--- a/compiler/rustc_middle/src/traits/query.rs
+++ b/compiler/rustc_middle/src/traits/query.rs
@@ -15,22 +15,19 @@ use rustc_span::source_map::Span;
 
 pub mod type_op {
     use crate::ty::fold::TypeFoldable;
-    use crate::ty::subst::UserSubsts;
-    use crate::ty::{Predicate, Ty};
-    use rustc_hir::def_id::DefId;
+    use crate::ty::{Predicate, Ty, UserType};
     use std::fmt;
 
     #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, Lift)]
     #[derive(TypeFoldable, TypeVisitable)]
     pub struct AscribeUserType<'tcx> {
         pub mir_ty: Ty<'tcx>,
-        pub def_id: DefId,
-        pub user_substs: UserSubsts<'tcx>,
+        pub user_ty: UserType<'tcx>,
     }
 
     impl<'tcx> AscribeUserType<'tcx> {
-        pub fn new(mir_ty: Ty<'tcx>, def_id: DefId, user_substs: UserSubsts<'tcx>) -> Self {
-            Self { mir_ty, def_id, user_substs }
+        pub fn new(mir_ty: Ty<'tcx>, user_ty: UserType<'tcx>) -> Self {
+            Self { mir_ty, user_ty }
         }
     }
 
diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs
index 136a4906c58..bc7895c3970 100644
--- a/compiler/rustc_middle/src/ty/typeck_results.rs
+++ b/compiler/rustc_middle/src/ty/typeck_results.rs
@@ -679,7 +679,7 @@ impl<'tcx> CanonicalUserType<'tcx> {
 /// from constants that are named via paths, like `Foo::<A>::new` and
 /// so forth.
 #[derive(Copy, Clone, Debug, PartialEq, TyEncodable, TyDecodable)]
-#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)]
+#[derive(Eq, Hash, HashStable, TypeFoldable, TypeVisitable, Lift)]
 pub enum UserType<'tcx> {
     Ty(Ty<'tcx>),
 
diff --git a/compiler/rustc_traits/src/type_op.rs b/compiler/rustc_traits/src/type_op.rs
index 7f964afde80..aa5c83ac2e6 100644
--- a/compiler/rustc_traits/src/type_op.rs
+++ b/compiler/rustc_traits/src/type_op.rs
@@ -4,8 +4,8 @@ use rustc_infer::infer::{DefiningAnchor, TyCtxtInferExt};
 use rustc_infer::traits::ObligationCauseCode;
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::{self, FnSig, Lift, PolyFnSig, Ty, TyCtxt, TypeFoldable};
-use rustc_middle::ty::{ParamEnvAnd, Predicate, ToPredicate};
-use rustc_middle::ty::{UserSelfTy, UserSubsts};
+use rustc_middle::ty::{ParamEnvAnd, Predicate};
+use rustc_middle::ty::{UserSelfTy, UserSubsts, UserType};
 use rustc_span::{Span, DUMMY_SP};
 use rustc_trait_selection::infer::InferCtxtBuilderExt;
 use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt;
@@ -50,13 +50,46 @@ pub fn type_op_ascribe_user_type_with_span<'tcx>(
     key: ParamEnvAnd<'tcx, AscribeUserType<'tcx>>,
     span: Option<Span>,
 ) -> Result<(), NoSolution> {
-    let (param_env, AscribeUserType { mir_ty, def_id, user_substs }) = key.into_parts();
-    debug!(
-        "type_op_ascribe_user_type: mir_ty={:?} def_id={:?} user_substs={:?}",
-        mir_ty, def_id, user_substs
-    );
+    let (param_env, AscribeUserType { mir_ty, user_ty }) = key.into_parts();
+    debug!("type_op_ascribe_user_type: mir_ty={:?} user_ty={:?}", mir_ty, user_ty);
     let span = span.unwrap_or(DUMMY_SP);
+    match user_ty {
+        UserType::Ty(user_ty) => relate_mir_and_user_ty(ocx, param_env, span, mir_ty, user_ty)?,
+        UserType::TypeOf(def_id, user_substs) => {
+            relate_mir_and_user_substs(ocx, param_env, span, mir_ty, def_id, user_substs)?
+        }
+    };
+    Ok(())
+}
+
+#[instrument(level = "debug", skip(ocx, param_env, span))]
+fn relate_mir_and_user_ty<'tcx>(
+    ocx: &ObligationCtxt<'_, 'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    span: Span,
+    mir_ty: Ty<'tcx>,
+    user_ty: Ty<'tcx>,
+) -> Result<(), NoSolution> {
+    let cause = ObligationCause::dummy_with_span(span);
+    let user_ty = ocx.normalize(&cause, param_env, user_ty);
+    ocx.eq(&cause, param_env, mir_ty, user_ty)?;
 
+    // FIXME(#104764): We should check well-formedness before normalization.
+    let predicate = ty::Binder::dummy(ty::PredicateKind::WellFormed(user_ty.into()));
+    ocx.register_obligation(Obligation::new(ocx.infcx.tcx, cause, param_env, predicate));
+
+    Ok(())
+}
+
+#[instrument(level = "debug", skip(ocx, param_env, span))]
+fn relate_mir_and_user_substs<'tcx>(
+    ocx: &ObligationCtxt<'_, 'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    span: Span,
+    mir_ty: Ty<'tcx>,
+    def_id: hir::def_id::DefId,
+    user_substs: UserSubsts<'tcx>,
+) -> Result<(), NoSolution> {
     let UserSubsts { user_self_ty, substs } = user_substs;
     let tcx = ocx.infcx.tcx;
     let cause = ObligationCause::dummy_with_span(span);
@@ -91,13 +124,13 @@ pub fn type_op_ascribe_user_type_with_span<'tcx>(
     }
 
     if let Some(UserSelfTy { impl_def_id, self_ty }) = user_self_ty {
+        let self_ty = ocx.normalize(&cause, param_env, self_ty);
         let impl_self_ty = tcx.bound_type_of(impl_def_id).subst(tcx, substs);
         let impl_self_ty = ocx.normalize(&cause, param_env, impl_self_ty);
 
         ocx.eq(&cause, param_env, self_ty, impl_self_ty)?;
 
-        let predicate: Predicate<'tcx> =
-            ty::Binder::dummy(ty::PredicateKind::WellFormed(impl_self_ty.into())).to_predicate(tcx);
+        let predicate = ty::Binder::dummy(ty::PredicateKind::WellFormed(impl_self_ty.into()));
         ocx.register_obligation(Obligation::new(tcx, cause.clone(), param_env, predicate));
     }
 
@@ -112,8 +145,7 @@ pub fn type_op_ascribe_user_type_with_span<'tcx>(
     // them?  This would only be relevant if some input
     // type were ill-formed but did not appear in `ty`,
     // which...could happen with normalization...
-    let predicate: Predicate<'tcx> =
-        ty::Binder::dummy(ty::PredicateKind::WellFormed(ty.into())).to_predicate(tcx);
+    let predicate = ty::Binder::dummy(ty::PredicateKind::WellFormed(ty.into()));
     ocx.register_obligation(Obligation::new(tcx, cause, param_env, predicate));
     Ok(())
 }
diff --git a/src/test/ui/generic-associated-types/issue-91139.rs b/src/test/ui/generic-associated-types/issue-91139.rs
index e321da53d56..adc0cb4e042 100644
--- a/src/test/ui/generic-associated-types/issue-91139.rs
+++ b/src/test/ui/generic-associated-types/issue-91139.rs
@@ -14,14 +14,6 @@ fn foo<T>() {
     let _: for<'a> fn(<() as Foo<T>>::Type<'a>, &'a T) = |_, _| ();
     //~^ ERROR `T` does not live long enough
     //~| ERROR `T` does not live long enough
-    //~| ERROR `T` does not live long enough
-    //~| ERROR `T` does not live long enough
-    //~| ERROR `T` does not live long enough
-    //~| ERROR `T` does not live long enough
-    //~| ERROR `T` does not live long enough
-    //~| ERROR `T` does not live long enough
-    //~| ERROR `T` may not live long enough
-    //~| ERROR `T` may not live long enough
     //
     // FIXME: This error is bogus, but it arises because we try to validate
     // that `<() as Foo<T>>::Type<'a>` is valid, which requires proving
diff --git a/src/test/ui/generic-associated-types/issue-91139.stderr b/src/test/ui/generic-associated-types/issue-91139.stderr
index 5485570cecd..d9d76adfbb5 100644
--- a/src/test/ui/generic-associated-types/issue-91139.stderr
+++ b/src/test/ui/generic-associated-types/issue-91139.stderr
@@ -10,64 +10,5 @@ error: `T` does not live long enough
 LL |     let _: for<'a> fn(<() as Foo<T>>::Type<'a>, &'a T) = |_, _| ();
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: `T` does not live long enough
-  --> $DIR/issue-91139.rs:14:12
-   |
-LL |     let _: for<'a> fn(<() as Foo<T>>::Type<'a>, &'a T) = |_, _| ();
-   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: `T` does not live long enough
-  --> $DIR/issue-91139.rs:14:12
-   |
-LL |     let _: for<'a> fn(<() as Foo<T>>::Type<'a>, &'a T) = |_, _| ();
-   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0310]: the parameter type `T` may not live long enough
-  --> $DIR/issue-91139.rs:14:58
-   |
-LL |     let _: for<'a> fn(<() as Foo<T>>::Type<'a>, &'a T) = |_, _| ();
-   |                                                          ^^^^^^ ...so that the type `T` will meet its required lifetime bounds
-   |
-help: consider adding an explicit lifetime bound...
-   |
-LL | fn foo<T: 'static>() {
-   |         +++++++++
-
-error: `T` does not live long enough
-  --> $DIR/issue-91139.rs:14:58
-   |
-LL |     let _: for<'a> fn(<() as Foo<T>>::Type<'a>, &'a T) = |_, _| ();
-   |                                                          ^^^^^^^^^
-
-error: `T` does not live long enough
-  --> $DIR/issue-91139.rs:14:58
-   |
-LL |     let _: for<'a> fn(<() as Foo<T>>::Type<'a>, &'a T) = |_, _| ();
-   |                                                          ^^^^^^^^^
-
-error[E0310]: the parameter type `T` may not live long enough
-  --> $DIR/issue-91139.rs:14:58
-   |
-LL |     let _: for<'a> fn(<() as Foo<T>>::Type<'a>, &'a T) = |_, _| ();
-   |                                                          ^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
-   |
-help: consider adding an explicit lifetime bound...
-   |
-LL | fn foo<T: 'static>() {
-   |         +++++++++
-
-error: `T` does not live long enough
-  --> $DIR/issue-91139.rs:14:58
-   |
-LL |     let _: for<'a> fn(<() as Foo<T>>::Type<'a>, &'a T) = |_, _| ();
-   |                                                          ^^^^^^^^^
-
-error: `T` does not live long enough
-  --> $DIR/issue-91139.rs:14:58
-   |
-LL |     let _: for<'a> fn(<() as Foo<T>>::Type<'a>, &'a T) = |_, _| ();
-   |                                                          ^^^^^^^^^
-
-error: aborting due to 10 previous errors
+error: aborting due to 2 previous errors
 
-For more information about this error, try `rustc --explain E0310`.
diff --git a/src/test/ui/higher-rank-trait-bounds/normalize-under-binder/issue-85455.rs b/src/test/ui/higher-rank-trait-bounds/normalize-under-binder/issue-85455.rs
index c10a0888a4f..8aa29926d4f 100644
--- a/src/test/ui/higher-rank-trait-bounds/normalize-under-binder/issue-85455.rs
+++ b/src/test/ui/higher-rank-trait-bounds/normalize-under-binder/issue-85455.rs
@@ -7,6 +7,7 @@ trait SomeTrait<'a> {
 fn give_me_ice<T>() {
     callee::<fn(&()) -> <T as SomeTrait<'_>>::Associated>();
     //~^ ERROR the trait bound `for<'a> T: SomeTrait<'a>` is not satisfied [E0277]
+    //~| ERROR the trait bound `for<'a> T: SomeTrait<'a>` is not satisfied [E0277]
 }
 
 fn callee<T: Fn<(&'static (),)>>() {
diff --git a/src/test/ui/higher-rank-trait-bounds/normalize-under-binder/issue-85455.stderr b/src/test/ui/higher-rank-trait-bounds/normalize-under-binder/issue-85455.stderr
index 25a4f6088de..3240518fbbe 100644
--- a/src/test/ui/higher-rank-trait-bounds/normalize-under-binder/issue-85455.stderr
+++ b/src/test/ui/higher-rank-trait-bounds/normalize-under-binder/issue-85455.stderr
@@ -1,4 +1,15 @@
 error[E0277]: the trait bound `for<'a> T: SomeTrait<'a>` is not satisfied
+  --> $DIR/issue-85455.rs:8:14
+   |
+LL |     callee::<fn(&()) -> <T as SomeTrait<'_>>::Associated>();
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `for<'a> SomeTrait<'a>` is not implemented for `T`
+   |
+help: consider restricting type parameter `T`
+   |
+LL | fn give_me_ice<T: for<'a> SomeTrait<'a>>() {
+   |                 +++++++++++++++++++++++
+
+error[E0277]: the trait bound `for<'a> T: SomeTrait<'a>` is not satisfied
   --> $DIR/issue-85455.rs:8:5
    |
 LL |     callee::<fn(&()) -> <T as SomeTrait<'_>>::Associated>();
@@ -9,6 +20,6 @@ help: consider restricting type parameter `T`
 LL | fn give_me_ice<T: for<'a> SomeTrait<'a>>() {
    |                 +++++++++++++++++++++++
 
-error: aborting due to previous error
+error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/nll/closure-malformed-projection-input-issue-102800.rs b/src/test/ui/nll/closure-malformed-projection-input-issue-102800.rs
index 25f47f5b6f6..260c16c17d4 100644
--- a/src/test/ui/nll/closure-malformed-projection-input-issue-102800.rs
+++ b/src/test/ui/nll/closure-malformed-projection-input-issue-102800.rs
@@ -16,16 +16,6 @@ impl Trait for &'static () {
 
 fn main() {
     let _: for<'a> fn(<&'a () as Trait>::Ty) = |_| {};
-    //~^ ERROR lifetime may not live long enough
-    //~| ERROR higher-ranked subtype error
-    //~| ERROR higher-ranked subtype error
-    //~| ERROR implementation of `Trait` is not general enough
-    //~| ERROR implementation of `Trait` is not general enough
-    //~| ERROR implementation of `Trait` is not general enough
-    //~| ERROR implementation of `Trait` is not general enough
-    //~| ERROR implementation of `Trait` is not general enough
-    //~| ERROR implementation of `Trait` is not general enough
-    //~| ERROR implementation of `Trait` is not general enough
-    //~| ERROR implementation of `Trait` is not general enough
+    //~^ ERROR implementation of `Trait` is not general enough
     //~| ERROR implementation of `Trait` is not general enough
 }
diff --git a/src/test/ui/nll/closure-malformed-projection-input-issue-102800.stderr b/src/test/ui/nll/closure-malformed-projection-input-issue-102800.stderr
index dbd5dabd1da..46dba006433 100644
--- a/src/test/ui/nll/closure-malformed-projection-input-issue-102800.stderr
+++ b/src/test/ui/nll/closure-malformed-projection-input-issue-102800.stderr
@@ -1,51 +1,3 @@
-error: lifetime may not live long enough
-  --> $DIR/closure-malformed-projection-input-issue-102800.rs:18:48
-   |
-LL |     let _: for<'a> fn(<&'a () as Trait>::Ty) = |_| {};
-   |                                                ^-^
-   |                                                ||
-   |                                                |has type `<&'1 () as Trait>::Ty`
-   |                                                requires that `'1` must outlive `'static`
-
-error: higher-ranked subtype error
-  --> $DIR/closure-malformed-projection-input-issue-102800.rs:18:48
-   |
-LL |     let _: for<'a> fn(<&'a () as Trait>::Ty) = |_| {};
-   |                                                ^^^
-
-error: higher-ranked subtype error
-  --> $DIR/closure-malformed-projection-input-issue-102800.rs:18:48
-   |
-LL |     let _: for<'a> fn(<&'a () as Trait>::Ty) = |_| {};
-   |                                                ^^^
-
-error: implementation of `Trait` is not general enough
-  --> $DIR/closure-malformed-projection-input-issue-102800.rs:18:48
-   |
-LL |     let _: for<'a> fn(<&'a () as Trait>::Ty) = |_| {};
-   |                                                ^^^^^^ implementation of `Trait` is not general enough
-   |
-   = note: `&'0 ()` must implement `Trait`, for any lifetime `'0`...
-   = note: ...but `Trait` is actually implemented for the type `&'static ()`
-
-error: implementation of `Trait` is not general enough
-  --> $DIR/closure-malformed-projection-input-issue-102800.rs:18:12
-   |
-LL |     let _: for<'a> fn(<&'a () as Trait>::Ty) = |_| {};
-   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Trait` is not general enough
-   |
-   = note: `&'0 ()` must implement `Trait`, for any lifetime `'0`...
-   = note: ...but `Trait` is actually implemented for the type `&'static ()`
-
-error: implementation of `Trait` is not general enough
-  --> $DIR/closure-malformed-projection-input-issue-102800.rs:18:12
-   |
-LL |     let _: for<'a> fn(<&'a () as Trait>::Ty) = |_| {};
-   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Trait` is not general enough
-   |
-   = note: `&'0 ()` must implement `Trait`, for any lifetime `'0`...
-   = note: ...but `Trait` is actually implemented for the type `&'static ()`
-
 error: implementation of `Trait` is not general enough
   --> $DIR/closure-malformed-projection-input-issue-102800.rs:18:12
    |
@@ -64,41 +16,5 @@ LL |     let _: for<'a> fn(<&'a () as Trait>::Ty) = |_| {};
    = note: `&'0 ()` must implement `Trait`, for any lifetime `'0`...
    = note: ...but `Trait` is actually implemented for the type `&'static ()`
 
-error: implementation of `Trait` is not general enough
-  --> $DIR/closure-malformed-projection-input-issue-102800.rs:18:48
-   |
-LL |     let _: for<'a> fn(<&'a () as Trait>::Ty) = |_| {};
-   |                                                ^^^^^^ implementation of `Trait` is not general enough
-   |
-   = note: `&'0 ()` must implement `Trait`, for any lifetime `'0`...
-   = note: ...but `Trait` is actually implemented for the type `&'static ()`
-
-error: implementation of `Trait` is not general enough
-  --> $DIR/closure-malformed-projection-input-issue-102800.rs:18:48
-   |
-LL |     let _: for<'a> fn(<&'a () as Trait>::Ty) = |_| {};
-   |                                                ^^^^^^ implementation of `Trait` is not general enough
-   |
-   = note: `&'0 ()` must implement `Trait`, for any lifetime `'0`...
-   = note: ...but `Trait` is actually implemented for the type `&'static ()`
-
-error: implementation of `Trait` is not general enough
-  --> $DIR/closure-malformed-projection-input-issue-102800.rs:18:48
-   |
-LL |     let _: for<'a> fn(<&'a () as Trait>::Ty) = |_| {};
-   |                                                ^^^^^^ implementation of `Trait` is not general enough
-   |
-   = note: `&'0 ()` must implement `Trait`, for any lifetime `'0`...
-   = note: ...but `Trait` is actually implemented for the type `&'static ()`
-
-error: implementation of `Trait` is not general enough
-  --> $DIR/closure-malformed-projection-input-issue-102800.rs:18:48
-   |
-LL |     let _: for<'a> fn(<&'a () as Trait>::Ty) = |_| {};
-   |                                                ^^^^^^ implementation of `Trait` is not general enough
-   |
-   = note: `&'0 ()` must implement `Trait`, for any lifetime `'0`...
-   = note: ...but `Trait` is actually implemented for the type `&'static ()`
-
-error: aborting due to 12 previous errors
+error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/nll/ty-outlives/wf-unreachable.stderr b/src/test/ui/nll/ty-outlives/wf-unreachable.stderr
index a62157f44f5..da3bc208322 100644
--- a/src/test/ui/nll/ty-outlives/wf-unreachable.stderr
+++ b/src/test/ui/nll/ty-outlives/wf-unreachable.stderr
@@ -5,7 +5,7 @@ LL | fn uninit<'a>() {
    |           -- lifetime `'a` defined here
 LL |     return;
 LL |     let x: &'static &'a ();
-   |            ^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static`
+   |            ^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static`
 
 error: lifetime may not live long enough
   --> $DIR/wf-unreachable.rs:11:12
@@ -14,7 +14,7 @@ LL | fn var_type<'a>() {
    |             -- lifetime `'a` defined here
 LL |     return;
 LL |     let x: &'static &'a () = &&();
-   |            ^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static`
+   |            ^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static`
 
 error: lifetime may not live long enough
   --> $DIR/wf-unreachable.rs:15:12
@@ -22,7 +22,7 @@ error: lifetime may not live long enough
 LL | fn uninit_infer<'a>() {
    |                 -- lifetime `'a` defined here
 LL |     let x: &'static &'a _;
-   |            ^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static`
+   |            ^^^^^^^^^^^^^^ requires that `'a` must outlive `'static`
 
 error: lifetime may not live long enough
   --> $DIR/wf-unreachable.rs:21:12
@@ -31,7 +31,7 @@ LL | fn infer<'a>() {
    |          -- lifetime `'a` defined here
 LL |     return;
 LL |     let x: &'static &'a _ = &&();
-   |            ^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static`
+   |            ^^^^^^^^^^^^^^ requires that `'a` must outlive `'static`
 
 error: lifetime may not live long enough
   --> $DIR/wf-unreachable.rs:26:12
@@ -40,7 +40,7 @@ LL | fn uninit_no_var<'a>() {
    |                  -- lifetime `'a` defined here
 LL |     return;
 LL |     let _: &'static &'a ();
-   |            ^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static`
+   |            ^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static`
 
 error: lifetime may not live long enough
   --> $DIR/wf-unreachable.rs:31:12
@@ -49,7 +49,7 @@ LL | fn no_var<'a>() {
    |           -- lifetime `'a` defined here
 LL |     return;
 LL |     let _: &'static &'a () = &&();
-   |            ^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static`
+   |            ^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static`
 
 error: lifetime may not live long enough
   --> $DIR/wf-unreachable.rs:36:12
@@ -58,7 +58,7 @@ LL | fn infer_no_var<'a>() {
    |                 -- lifetime `'a` defined here
 LL |     return;
 LL |     let _: &'static &'a _ = &&();
-   |            ^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static`
+   |            ^^^^^^^^^^^^^^ requires that `'a` must outlive `'static`
 
 error: lifetime may not live long enough
   --> $DIR/wf-unreachable.rs:49:12
@@ -67,7 +67,7 @@ LL | fn required_substs<'a>() {
    |                    -- lifetime `'a` defined here
 LL |     return;
 LL |     let _: C<'static, 'a, _> = C((), &(), &());
-   |            ^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static`
+   |            ^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static`
 
 error: aborting due to 8 previous errors
 
diff --git a/src/test/ui/nll/user-annotations/ascribed-type-wf.rs b/src/test/ui/nll/user-annotations/ascribed-type-wf.rs
index 14460dea5b5..5db02c46ec3 100644
--- a/src/test/ui/nll/user-annotations/ascribed-type-wf.rs
+++ b/src/test/ui/nll/user-annotations/ascribed-type-wf.rs
@@ -1,5 +1,5 @@
-// check-pass
-// known-bug: #101350
+// Regression test for #101350.
+// check-fail
 
 trait Trait {
     type Ty;
@@ -11,6 +11,7 @@ impl Trait for &'static () {
 
 fn extend<'a>() {
     None::<<&'a () as Trait>::Ty>;
+    //~^ ERROR lifetime may not live long enough
 }
 
 fn main() {}
diff --git a/src/test/ui/nll/user-annotations/ascribed-type-wf.stderr b/src/test/ui/nll/user-annotations/ascribed-type-wf.stderr
new file mode 100644
index 00000000000..91e7c6b8ecf
--- /dev/null
+++ b/src/test/ui/nll/user-annotations/ascribed-type-wf.stderr
@@ -0,0 +1,10 @@
+error: lifetime may not live long enough
+  --> $DIR/ascribed-type-wf.rs:13:5
+   |
+LL | fn extend<'a>() {
+   |           -- lifetime `'a` defined here
+LL |     None::<<&'a () as Trait>::Ty>;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static`
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/nll/user-annotations/closure-sig.rs b/src/test/ui/nll/user-annotations/closure-sig.rs
new file mode 100644
index 00000000000..4dbd3fd8d81
--- /dev/null
+++ b/src/test/ui/nll/user-annotations/closure-sig.rs
@@ -0,0 +1,15 @@
+// This test fails if #104478 is fixed before #104477.
+
+// check-pass
+
+struct Printer<'a, 'b>(&'a (), &'b ());
+
+impl Printer<'_, '_> {
+    fn test(self) {
+        let clo = |_: &'_ Self| {};
+        clo(&self);
+        clo(&self);
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/nll/user-annotations/normalization-2.rs b/src/test/ui/nll/user-annotations/normalization-2.rs
new file mode 100644
index 00000000000..be23c3b7478
--- /dev/null
+++ b/src/test/ui/nll/user-annotations/normalization-2.rs
@@ -0,0 +1,152 @@
+// Make sure we honor region constraints when normalizing type annotations.
+
+// check-fail
+
+#![feature(more_qualified_paths)]
+
+trait Trait {
+    type Assoc;
+}
+
+impl<T> Trait for T
+where
+    T: 'static,
+{
+    type Assoc = MyTy<()>;
+}
+
+enum MyTy<T> {
+    Unit,
+    Tuple(),
+    Struct {},
+    Dumb(T),
+}
+
+impl<T> MyTy<T> {
+    const CONST: () = ();
+    fn method<X>() {}
+    fn method2<X>(&self) {}
+}
+
+trait TraitAssoc {
+    const TRAIT_CONST: ();
+    fn trait_method<X>(&self);
+}
+impl<T> TraitAssoc for T {
+    const TRAIT_CONST: () = ();
+    fn trait_method<X>(&self) {}
+}
+
+type Ty<'a> = <&'a () as Trait>::Assoc;
+
+fn test_local<'a>() {
+    let _: Ty<'a> = MyTy::Unit;
+    //~^ ERROR lifetime may not live long enough
+}
+
+fn test_closure_sig<'a, 'b>() {
+    |_: Ty<'a>| {};
+    //~^ ERROR lifetime may not live long enough
+    || -> Option<Ty<'b>> { None };
+    //~^ ERROR lifetime may not live long enough
+}
+
+fn test_path<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h>() {
+    <Ty<'a>>::method::<Ty<'static>>;
+    //~^ ERROR lifetime may not live long enough
+    <Ty<'static>>::method::<Ty<'b>>;
+    //~^ ERROR lifetime may not live long enough
+
+    <Ty<'c>>::trait_method::<Ty<'static>>;
+    //~^ ERROR lifetime may not live long enough
+    <Ty<'static>>::trait_method::<Ty<'d>>;
+    //~^ ERROR lifetime may not live long enough
+
+    <Ty<'e>>::CONST;
+    //~^ ERROR lifetime may not live long enough
+    <Ty<'f>>::TRAIT_CONST;
+    //~^ ERROR lifetime may not live long enough
+
+    <Ty<'static>>::method::<Ty<'static>>;
+    <Ty<'static>>::trait_method::<Ty<'static>>;
+    <Ty<'static>>::CONST;
+    <Ty<'static>>::TRAIT_CONST;
+
+    MyTy::Unit::<Ty<'g>>;
+    //~^ ERROR lifetime may not live long enough
+    MyTy::<Ty<'h>>::Unit;
+    //~^ ERROR lifetime may not live long enough
+}
+
+fn test_call<'a, 'b, 'c>() {
+    <Ty<'a>>::method::<Ty<'static>>();
+    //~^ ERROR lifetime may not live long enough
+    <Ty<'static>>::method::<Ty<'b>>();
+    //~^ ERROR lifetime may not live long enough
+}
+
+fn test_variants<'a, 'b, 'c>() {
+    <Ty<'a>>::Struct {};
+    //~^ ERROR lifetime may not live long enough
+    <Ty<'b>>::Tuple();
+    //~^ ERROR lifetime may not live long enough
+    <Ty<'c>>::Unit;
+    //~^ ERROR lifetime may not live long enough
+}
+
+fn test_method_call<'a, 'b>(x: MyTy<()>) {
+    x.method2::<Ty<'a>>();
+    //~^ ERROR lifetime may not live long enough
+    x.trait_method::<Ty<'b>>();
+    //~^ ERROR lifetime may not live long enough
+}
+
+fn test_struct_path<'a, 'b, 'c, 'd>() {
+    struct Struct<T> { x: Option<T>, }
+
+    trait Project {
+        type Struct;
+        type Enum;
+    }
+    impl<T> Project for T {
+        type Struct = Struct<()>;
+        type Enum = MyTy<()>;
+    }
+
+    // Resolves to enum variant
+    MyTy::<Ty<'a>>::Struct {}; // without SelfTy
+    //~^ ERROR lifetime may not live long enough
+    <Ty<'b> as Project>::Enum::Struct {}; // with SelfTy
+    //~^ ERROR lifetime may not live long enough
+
+    // Resolves to struct and associated type respectively
+    Struct::<Ty<'c>> { x: None, }; // without SelfTy
+    //~^ ERROR lifetime may not live long enough
+    <Ty<'d> as Project>::Struct { x: None, }; // with SelfTy
+    //~^ ERROR lifetime may not live long enough
+}
+
+fn test_pattern<'a, 'b, 'c, 'd, 'e, 'f>() {
+    use MyTy::*;
+    match MyTy::Unit {
+        Struct::<Ty<'a>> {..} => {},
+        //~^ ERROR lifetime may not live long enough
+        Tuple::<Ty<'b>> (..) => {},
+        //~^ ERROR lifetime may not live long enough
+        Unit::<Ty<'c>> => {},
+        //~^ ERROR lifetime may not live long enough
+        Dumb(_) => {},
+    };
+    match MyTy::Unit {
+        <Ty<'d>>::Struct {..} => {},
+        //~^ ERROR lifetime may not live long enough
+        <Ty<'e>>::Tuple (..) => {},
+        //~^ ERROR lifetime may not live long enough
+        <Ty<'f>>::Unit => {},
+        //~^ ERROR lifetime may not live long enough
+        Dumb(_) => {},
+    };
+}
+
+
+fn main() {}
diff --git a/src/test/ui/nll/user-annotations/normalization-2.stderr b/src/test/ui/nll/user-annotations/normalization-2.stderr
new file mode 100644
index 00000000000..5299282ea15
--- /dev/null
+++ b/src/test/ui/nll/user-annotations/normalization-2.stderr
@@ -0,0 +1,296 @@
+error: lifetime may not live long enough
+  --> $DIR/normalization-2.rs:43:12
+   |
+LL | fn test_local<'a>() {
+   |               -- lifetime `'a` defined here
+LL |     let _: Ty<'a> = MyTy::Unit;
+   |            ^^^^^^ requires that `'a` must outlive `'static`
+
+error: lifetime may not live long enough
+  --> $DIR/normalization-2.rs:48:6
+   |
+LL | fn test_closure_sig<'a, 'b>() {
+   |                     -- lifetime `'a` defined here
+LL |     |_: Ty<'a>| {};
+   |      ^ requires that `'a` must outlive `'static`
+
+error: lifetime may not live long enough
+  --> $DIR/normalization-2.rs:50:11
+   |
+LL | fn test_closure_sig<'a, 'b>() {
+   |                         -- lifetime `'b` defined here
+...
+LL |     || -> Option<Ty<'b>> { None };
+   |           ^^^^^^^^^^^^^^ requires that `'b` must outlive `'static`
+
+help: the following changes may resolve your lifetime errors
+   |
+   = help: replace `'a` with `'static`
+   = help: replace `'b` with `'static`
+
+error: lifetime may not live long enough
+  --> $DIR/normalization-2.rs:55:5
+   |
+LL | fn test_path<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h>() {
+   |              -- lifetime `'a` defined here
+LL |     <Ty<'a>>::method::<Ty<'static>>;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static`
+
+error: lifetime may not live long enough
+  --> $DIR/normalization-2.rs:57:5
+   |
+LL | fn test_path<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h>() {
+   |                  -- lifetime `'b` defined here
+...
+LL |     <Ty<'static>>::method::<Ty<'b>>;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'b` must outlive `'static`
+
+error: lifetime may not live long enough
+  --> $DIR/normalization-2.rs:60:5
+   |
+LL | fn test_path<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h>() {
+   |                      -- lifetime `'c` defined here
+...
+LL |     <Ty<'c>>::trait_method::<Ty<'static>>;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'c` must outlive `'static`
+
+error: lifetime may not live long enough
+  --> $DIR/normalization-2.rs:62:5
+   |
+LL | fn test_path<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h>() {
+   |                          -- lifetime `'d` defined here
+...
+LL |     <Ty<'static>>::trait_method::<Ty<'d>>;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'d` must outlive `'static`
+
+error: lifetime may not live long enough
+  --> $DIR/normalization-2.rs:65:5
+   |
+LL | fn test_path<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h>() {
+   |                              -- lifetime `'e` defined here
+...
+LL |     <Ty<'e>>::CONST;
+   |     ^^^^^^^^^^^^^^^ requires that `'e` must outlive `'static`
+
+error: lifetime may not live long enough
+  --> $DIR/normalization-2.rs:67:5
+   |
+LL | fn test_path<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h>() {
+   |                                  -- lifetime `'f` defined here
+...
+LL |     <Ty<'f>>::TRAIT_CONST;
+   |     ^^^^^^^^^^^^^^^^^^^^^ requires that `'f` must outlive `'static`
+
+error: lifetime may not live long enough
+  --> $DIR/normalization-2.rs:75:5
+   |
+LL | fn test_path<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h>() {
+   |                                      -- lifetime `'g` defined here
+...
+LL |     MyTy::Unit::<Ty<'g>>;
+   |     ^^^^^^^^^^^^^^^^^^^^ requires that `'g` must outlive `'static`
+
+error: lifetime may not live long enough
+  --> $DIR/normalization-2.rs:77:5
+   |
+LL | fn test_path<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h>() {
+   |                                          -- lifetime `'h` defined here
+...
+LL |     MyTy::<Ty<'h>>::Unit;
+   |     ^^^^^^^^^^^^^^^^^^^^ requires that `'h` must outlive `'static`
+
+help: the following changes may resolve your lifetime errors
+   |
+   = help: replace `'a` with `'static`
+   = help: replace `'b` with `'static`
+   = help: replace `'c` with `'static`
+   = help: replace `'d` with `'static`
+   = help: replace `'e` with `'static`
+   = help: replace `'f` with `'static`
+   = help: replace `'g` with `'static`
+   = help: replace `'h` with `'static`
+
+error: lifetime may not live long enough
+  --> $DIR/normalization-2.rs:82:5
+   |
+LL | fn test_call<'a, 'b, 'c>() {
+   |              -- lifetime `'a` defined here
+LL |     <Ty<'a>>::method::<Ty<'static>>();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static`
+
+error: lifetime may not live long enough
+  --> $DIR/normalization-2.rs:84:5
+   |
+LL | fn test_call<'a, 'b, 'c>() {
+   |                  -- lifetime `'b` defined here
+...
+LL |     <Ty<'static>>::method::<Ty<'b>>();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'b` must outlive `'static`
+
+help: the following changes may resolve your lifetime errors
+   |
+   = help: replace `'a` with `'static`
+   = help: replace `'b` with `'static`
+
+error: lifetime may not live long enough
+  --> $DIR/normalization-2.rs:89:5
+   |
+LL | fn test_variants<'a, 'b, 'c>() {
+   |                  -- lifetime `'a` defined here
+LL |     <Ty<'a>>::Struct {};
+   |     ^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static`
+
+error: lifetime may not live long enough
+  --> $DIR/normalization-2.rs:91:5
+   |
+LL | fn test_variants<'a, 'b, 'c>() {
+   |                      -- lifetime `'b` defined here
+...
+LL |     <Ty<'b>>::Tuple();
+   |     ^^^^^^^^^^^^^^^ requires that `'b` must outlive `'static`
+
+error: lifetime may not live long enough
+  --> $DIR/normalization-2.rs:93:5
+   |
+LL | fn test_variants<'a, 'b, 'c>() {
+   |                          -- lifetime `'c` defined here
+...
+LL |     <Ty<'c>>::Unit;
+   |     ^^^^^^^^^^^^^^ requires that `'c` must outlive `'static`
+
+help: the following changes may resolve your lifetime errors
+   |
+   = help: replace `'a` with `'static`
+   = help: replace `'b` with `'static`
+   = help: replace `'c` with `'static`
+
+error: lifetime may not live long enough
+  --> $DIR/normalization-2.rs:98:7
+   |
+LL | fn test_method_call<'a, 'b>(x: MyTy<()>) {
+   |                     -- lifetime `'a` defined here
+LL |     x.method2::<Ty<'a>>();
+   |       ^^^^^^^ requires that `'a` must outlive `'static`
+
+error: lifetime may not live long enough
+  --> $DIR/normalization-2.rs:100:7
+   |
+LL | fn test_method_call<'a, 'b>(x: MyTy<()>) {
+   |                         -- lifetime `'b` defined here
+...
+LL |     x.trait_method::<Ty<'b>>();
+   |       ^^^^^^^^^^^^ requires that `'b` must outlive `'static`
+
+help: the following changes may resolve your lifetime errors
+   |
+   = help: replace `'a` with `'static`
+   = help: replace `'b` with `'static`
+
+error: lifetime may not live long enough
+  --> $DIR/normalization-2.rs:117:5
+   |
+LL | fn test_struct_path<'a, 'b, 'c, 'd>() {
+   |                     -- lifetime `'a` defined here
+...
+LL |     MyTy::<Ty<'a>>::Struct {}; // without SelfTy
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static`
+
+error: lifetime may not live long enough
+  --> $DIR/normalization-2.rs:119:5
+   |
+LL | fn test_struct_path<'a, 'b, 'c, 'd>() {
+   |                         -- lifetime `'b` defined here
+...
+LL |     <Ty<'b> as Project>::Enum::Struct {}; // with SelfTy
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'b` must outlive `'static`
+
+error: lifetime may not live long enough
+  --> $DIR/normalization-2.rs:123:5
+   |
+LL | fn test_struct_path<'a, 'b, 'c, 'd>() {
+   |                             -- lifetime `'c` defined here
+...
+LL |     Struct::<Ty<'c>> { x: None, }; // without SelfTy
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'c` must outlive `'static`
+
+error: lifetime may not live long enough
+  --> $DIR/normalization-2.rs:125:5
+   |
+LL | fn test_struct_path<'a, 'b, 'c, 'd>() {
+   |                                 -- lifetime `'d` defined here
+...
+LL |     <Ty<'d> as Project>::Struct { x: None, }; // with SelfTy
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'d` must outlive `'static`
+
+help: the following changes may resolve your lifetime errors
+   |
+   = help: replace `'a` with `'static`
+   = help: replace `'b` with `'static`
+   = help: replace `'c` with `'static`
+   = help: replace `'d` with `'static`
+
+error: lifetime may not live long enough
+  --> $DIR/normalization-2.rs:132:9
+   |
+LL | fn test_pattern<'a, 'b, 'c, 'd, 'e, 'f>() {
+   |                 -- lifetime `'a` defined here
+...
+LL |         Struct::<Ty<'a>> {..} => {},
+   |         ^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static`
+
+error: lifetime may not live long enough
+  --> $DIR/normalization-2.rs:134:9
+   |
+LL | fn test_pattern<'a, 'b, 'c, 'd, 'e, 'f>() {
+   |                     -- lifetime `'b` defined here
+...
+LL |         Tuple::<Ty<'b>> (..) => {},
+   |         ^^^^^^^^^^^^^^^^^^^^ requires that `'b` must outlive `'static`
+
+error: lifetime may not live long enough
+  --> $DIR/normalization-2.rs:136:9
+   |
+LL | fn test_pattern<'a, 'b, 'c, 'd, 'e, 'f>() {
+   |                         -- lifetime `'c` defined here
+...
+LL |         Unit::<Ty<'c>> => {},
+   |         ^^^^^^^^^^^^^^ requires that `'c` must outlive `'static`
+
+error: lifetime may not live long enough
+  --> $DIR/normalization-2.rs:141:9
+   |
+LL | fn test_pattern<'a, 'b, 'c, 'd, 'e, 'f>() {
+   |                             -- lifetime `'d` defined here
+...
+LL |         <Ty<'d>>::Struct {..} => {},
+   |         ^^^^^^^^^^^^^^^^^^^^^ requires that `'d` must outlive `'static`
+
+error: lifetime may not live long enough
+  --> $DIR/normalization-2.rs:143:9
+   |
+LL | fn test_pattern<'a, 'b, 'c, 'd, 'e, 'f>() {
+   |                                 -- lifetime `'e` defined here
+...
+LL |         <Ty<'e>>::Tuple (..) => {},
+   |         ^^^^^^^^^^^^^^^^^^^^ requires that `'e` must outlive `'static`
+
+error: lifetime may not live long enough
+  --> $DIR/normalization-2.rs:145:9
+   |
+LL | fn test_pattern<'a, 'b, 'c, 'd, 'e, 'f>() {
+   |                                     -- lifetime `'f` defined here
+...
+LL |         <Ty<'f>>::Unit => {},
+   |         ^^^^^^^^^^^^^^ requires that `'f` must outlive `'static`
+
+help: the following changes may resolve your lifetime errors
+   |
+   = help: replace `'a` with `'static`
+   = help: replace `'b` with `'static`
+   = help: replace `'c` with `'static`
+   = help: replace `'d` with `'static`
+   = help: replace `'e` with `'static`
+   = help: replace `'f` with `'static`
+
+error: aborting due to 28 previous errors
+
diff --git a/src/test/ui/nll/user-annotations/normalization-default.rs b/src/test/ui/nll/user-annotations/normalization-default.rs
new file mode 100644
index 00000000000..fa52e6d857f
--- /dev/null
+++ b/src/test/ui/nll/user-annotations/normalization-default.rs
@@ -0,0 +1,22 @@
+// check-fail
+
+trait Trait { type Assoc; }
+impl<'a> Trait for &'a () { type Assoc = &'a (); }
+
+struct MyTuple<T, U = <&'static () as Trait>::Assoc>(T, U);
+fn test_tuple(x: &(), y: &()) {
+    MyTuple::<_>((), x);
+    //~^ ERROR
+    let _: MyTuple::<_> = MyTuple((), y);
+    //~^ ERROR
+}
+
+struct MyStruct<T, U = <&'static () as Trait>::Assoc> { val: (T, U), }
+fn test_struct(x: &(), y: &()) {
+    MyStruct::<_> { val: ((), x) };
+    //~^ ERROR
+    let _: MyStruct::<_> = MyStruct { val: ((), y) };
+    //~^ ERROR
+}
+
+fn main() {}
diff --git a/src/test/ui/nll/user-annotations/normalization-default.stderr b/src/test/ui/nll/user-annotations/normalization-default.stderr
new file mode 100644
index 00000000000..6c73ac69254
--- /dev/null
+++ b/src/test/ui/nll/user-annotations/normalization-default.stderr
@@ -0,0 +1,36 @@
+error: lifetime may not live long enough
+  --> $DIR/normalization-default.rs:8:22
+   |
+LL | fn test_tuple(x: &(), y: &()) {
+   |                  - let's call the lifetime of this reference `'1`
+LL |     MyTuple::<_>((), x);
+   |                      ^ this usage requires that `'1` must outlive `'static`
+
+error: lifetime may not live long enough
+  --> $DIR/normalization-default.rs:10:12
+   |
+LL | fn test_tuple(x: &(), y: &()) {
+   |                          - let's call the lifetime of this reference `'2`
+...
+LL |     let _: MyTuple::<_> = MyTuple((), y);
+   |            ^^^^^^^^^^^^ type annotation requires that `'2` must outlive `'static`
+
+error: lifetime may not live long enough
+  --> $DIR/normalization-default.rs:16:26
+   |
+LL | fn test_struct(x: &(), y: &()) {
+   |                   - let's call the lifetime of this reference `'1`
+LL |     MyStruct::<_> { val: ((), x) };
+   |                          ^^^^^^^ this usage requires that `'1` must outlive `'static`
+
+error: lifetime may not live long enough
+  --> $DIR/normalization-default.rs:18:12
+   |
+LL | fn test_struct(x: &(), y: &()) {
+   |                           - let's call the lifetime of this reference `'2`
+...
+LL |     let _: MyStruct::<_> = MyStruct { val: ((), y) };
+   |            ^^^^^^^^^^^^^ type annotation requires that `'2` must outlive `'static`
+
+error: aborting due to 4 previous errors
+
diff --git a/src/test/ui/nll/user-annotations/normalization-infer.rs b/src/test/ui/nll/user-annotations/normalization-infer.rs
new file mode 100644
index 00000000000..8bfc272d4ba
--- /dev/null
+++ b/src/test/ui/nll/user-annotations/normalization-infer.rs
@@ -0,0 +1,40 @@
+// Annnotations may contain projection types with inference variables as input.
+// Make sure we don't get ambiguities when normalizing them.
+
+// check-fail
+
+// Single impl.
+fn test1<A, B, C, D>(a: A, b: B, c: C) {
+    trait Tr { type Ty; }
+    impl<T: 'static> Tr for (T,) { type Ty = T; }
+
+    let _: <(_,) as Tr>::Ty = a; //~ ERROR type `A`
+    Some::<<(_,) as Tr>::Ty>(b); //~ ERROR type `B`
+    || -> <(_,) as Tr>::Ty { c }; //~ ERROR type `C`
+    |d: <(_,) as Tr>::Ty| -> D { d }; //~ ERROR type `D`
+}
+
+
+// Two impls. The selected impl depends on the actual type.
+fn test2<A, B, C>(a: A, b: B, c: C) {
+    trait Tr { type Ty; }
+    impl<T: 'static> Tr for (u8, T) { type Ty = T; }
+    impl<T>          Tr for (i8, T) { type Ty = T; }
+    type Alias<X, Y> = (<(X, Y) as Tr>::Ty, X);
+
+    fn temp() -> String { todo!() }
+
+    // `u8` impl, requires static.
+    let _: Alias<_, _> = (a, 0u8); //~ ERROR type `A`
+    Some::<Alias<_, _>>((b, 0u8)); //~ ERROR type `B`
+    || -> Alias<_, _> { (c, 0u8) }; //~ ERROR type `C`
+
+    let _: Alias<_, _> = (&temp(), 0u8); //~ ERROR temporary value
+    Some::<Alias<_, _>>((&temp(), 0u8)); //~ ERROR temporary value
+
+    // `i8` impl, no region constraints.
+    let _: Alias<_, _> = (&temp(), 0i8);
+    Some::<Alias<_, _>>((&temp(), 0i8));
+}
+
+fn main() {}
diff --git a/src/test/ui/nll/user-annotations/normalization-infer.stderr b/src/test/ui/nll/user-annotations/normalization-infer.stderr
new file mode 100644
index 00000000000..12854ab6816
--- /dev/null
+++ b/src/test/ui/nll/user-annotations/normalization-infer.stderr
@@ -0,0 +1,101 @@
+error[E0310]: the parameter type `A` may not live long enough
+  --> $DIR/normalization-infer.rs:11:12
+   |
+LL |     let _: <(_,) as Tr>::Ty = a;
+   |            ^^^^^^^^^^^^^^^^ ...so that the type `A` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound...
+   |
+LL | fn test1<A: 'static, B, C, D>(a: A, b: B, c: C) {
+   |           +++++++++
+
+error[E0310]: the parameter type `B` may not live long enough
+  --> $DIR/normalization-infer.rs:12:5
+   |
+LL |     Some::<<(_,) as Tr>::Ty>(b);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `B` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound...
+   |
+LL | fn test1<A, B: 'static, C, D>(a: A, b: B, c: C) {
+   |              +++++++++
+
+error[E0310]: the parameter type `C` may not live long enough
+  --> $DIR/normalization-infer.rs:13:11
+   |
+LL |     || -> <(_,) as Tr>::Ty { c };
+   |           ^^^^^^^^^^^^^^^^ ...so that the type `C` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound...
+   |
+LL | fn test1<A, B, C: 'static, D>(a: A, b: B, c: C) {
+   |                 +++++++++
+
+error[E0310]: the parameter type `D` may not live long enough
+  --> $DIR/normalization-infer.rs:14:6
+   |
+LL |     |d: <(_,) as Tr>::Ty| -> D { d };
+   |      ^ ...so that the type `D` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound...
+   |
+LL | fn test1<A, B, C, D: 'static>(a: A, b: B, c: C) {
+   |                    +++++++++
+
+error[E0310]: the parameter type `A` may not live long enough
+  --> $DIR/normalization-infer.rs:28:12
+   |
+LL |     let _: Alias<_, _> = (a, 0u8);
+   |            ^^^^^^^^^^^ ...so that the type `A` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound...
+   |
+LL | fn test2<A: 'static, B, C>(a: A, b: B, c: C) {
+   |           +++++++++
+
+error[E0310]: the parameter type `B` may not live long enough
+  --> $DIR/normalization-infer.rs:29:5
+   |
+LL |     Some::<Alias<_, _>>((b, 0u8));
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `B` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound...
+   |
+LL | fn test2<A, B: 'static, C>(a: A, b: B, c: C) {
+   |              +++++++++
+
+error[E0310]: the parameter type `C` may not live long enough
+  --> $DIR/normalization-infer.rs:30:11
+   |
+LL |     || -> Alias<_, _> { (c, 0u8) };
+   |           ^^^^^^^^^^^ ...so that the type `C` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound...
+   |
+LL | fn test2<A, B, C: 'static>(a: A, b: B, c: C) {
+   |                 +++++++++
+
+error[E0716]: temporary value dropped while borrowed
+  --> $DIR/normalization-infer.rs:32:28
+   |
+LL |     let _: Alias<_, _> = (&temp(), 0u8);
+   |            -----------     ^^^^^^ creates a temporary value which is freed while still in use
+   |            |
+   |            type annotation requires that borrow lasts for `'static`
+...
+LL | }
+   | - temporary value is freed at the end of this statement
+
+error[E0716]: temporary value dropped while borrowed
+  --> $DIR/normalization-infer.rs:33:27
+   |
+LL |     Some::<Alias<_, _>>((&temp(), 0u8));
+   |                         --^^^^^^------ - temporary value is freed at the end of this statement
+   |                         | |
+   |                         | creates a temporary value which is freed while still in use
+   |                         this usage requires that borrow lasts for `'static`
+
+error: aborting due to 9 previous errors
+
+Some errors have detailed explanations: E0310, E0716.
+For more information about an error, try `rustc --explain E0310`.
diff --git a/src/test/ui/nll/user-annotations/normalization-self.rs b/src/test/ui/nll/user-annotations/normalization-self.rs
new file mode 100644
index 00000000000..c18760b53cf
--- /dev/null
+++ b/src/test/ui/nll/user-annotations/normalization-self.rs
@@ -0,0 +1,26 @@
+// check-fail
+
+trait Trait { type Assoc; }
+impl<'a> Trait for &'a () { type Assoc = &'a (); }
+
+struct MyTuple<T>(T);
+impl MyTuple<<&'static () as Trait>::Assoc> {
+    fn test(x: &(), y: &()) {
+        Self(x);
+        //~^ ERROR
+        let _: Self = MyTuple(y);
+        //~^ ERROR
+    }
+}
+
+struct MyStruct<T> { val: T, }
+impl MyStruct<<&'static () as Trait>::Assoc> {
+    fn test(x: &(), y: &()) {
+        Self { val: x };
+        //~^ ERROR
+        let _: Self = MyStruct { val: y };
+        //~^ ERROR
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/nll/user-annotations/normalization-self.stderr b/src/test/ui/nll/user-annotations/normalization-self.stderr
new file mode 100644
index 00000000000..e231ed03c2e
--- /dev/null
+++ b/src/test/ui/nll/user-annotations/normalization-self.stderr
@@ -0,0 +1,36 @@
+error: lifetime may not live long enough
+  --> $DIR/normalization-self.rs:9:14
+   |
+LL |     fn test(x: &(), y: &()) {
+   |                - let's call the lifetime of this reference `'1`
+LL |         Self(x);
+   |              ^ this usage requires that `'1` must outlive `'static`
+
+error: lifetime may not live long enough
+  --> $DIR/normalization-self.rs:11:16
+   |
+LL |     fn test(x: &(), y: &()) {
+   |                        - let's call the lifetime of this reference `'2`
+...
+LL |         let _: Self = MyTuple(y);
+   |                ^^^^ type annotation requires that `'2` must outlive `'static`
+
+error: lifetime may not live long enough
+  --> $DIR/normalization-self.rs:19:21
+   |
+LL |     fn test(x: &(), y: &()) {
+   |                - let's call the lifetime of this reference `'1`
+LL |         Self { val: x };
+   |                     ^ this usage requires that `'1` must outlive `'static`
+
+error: lifetime may not live long enough
+  --> $DIR/normalization-self.rs:21:16
+   |
+LL |     fn test(x: &(), y: &()) {
+   |                        - let's call the lifetime of this reference `'2`
+...
+LL |         let _: Self = MyStruct { val: y };
+   |                ^^^^ type annotation requires that `'2` must outlive `'static`
+
+error: aborting due to 4 previous errors
+
diff --git a/src/test/ui/nll/user-annotations/normalization.rs b/src/test/ui/nll/user-annotations/normalization.rs
index 870e3d8110c..c2e892f573c 100644
--- a/src/test/ui/nll/user-annotations/normalization.rs
+++ b/src/test/ui/nll/user-annotations/normalization.rs
@@ -3,8 +3,15 @@
 
 trait Foo { type Out; }
 impl Foo for () { type Out = &'static u32; }
+impl<'a> Foo for &'a () { type Out = &'a u32; }
 
 fn main() {
     let a = 22;
-    let b: <() as Foo>::Out = &a; //~ ERROR
+    let _: <() as Foo>::Out = &a; //~ ERROR
+
+    let a = 22;
+    let _: <&'static () as Foo>::Out = &a; //~ ERROR
+
+    let a = 22;
+    let _: <&'_ () as Foo>::Out = &a;
 }
diff --git a/src/test/ui/nll/user-annotations/normalization.stderr b/src/test/ui/nll/user-annotations/normalization.stderr
index 4c7893789a5..975cb4b66d9 100644
--- a/src/test/ui/nll/user-annotations/normalization.stderr
+++ b/src/test/ui/nll/user-annotations/normalization.stderr
@@ -1,13 +1,25 @@
 error[E0597]: `a` does not live long enough
-  --> $DIR/normalization.rs:9:31
+  --> $DIR/normalization.rs:10:31
    |
-LL |     let b: <() as Foo>::Out = &a;
+LL |     let _: <() as Foo>::Out = &a;
    |            ----------------   ^^ borrowed value does not live long enough
    |            |
    |            type annotation requires that `a` is borrowed for `'static`
+...
 LL | }
    | - `a` dropped here while still borrowed
 
-error: aborting due to previous error
+error[E0597]: `a` does not live long enough
+  --> $DIR/normalization.rs:13:40
+   |
+LL |     let _: <&'static () as Foo>::Out = &a;
+   |            -------------------------   ^^ borrowed value does not live long enough
+   |            |
+   |            type annotation requires that `a` is borrowed for `'static`
+...
+LL | }
+   | - `a` dropped here while still borrowed
+
+error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0597`.
diff --git a/src/test/ui/regions/regions-assoc-type-in-supertrait-outlives-container.stderr b/src/test/ui/regions/regions-assoc-type-in-supertrait-outlives-container.stderr
index 87e33e1ccff..2a262520361 100644
--- a/src/test/ui/regions/regions-assoc-type-in-supertrait-outlives-container.stderr
+++ b/src/test/ui/regions/regions-assoc-type-in-supertrait-outlives-container.stderr
@@ -7,7 +7,7 @@ LL | fn with_assoc<'a,'b>() {
    |               lifetime `'a` defined here
 ...
 LL |     let _: &'a WithAssoc<TheType<'b>> = loop { };
-   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'b` must outlive `'a`
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'b` must outlive `'a`
    |
    = help: consider adding the following bound: `'b: 'a`
 
diff --git a/src/test/ui/regions/regions-free-region-ordering-caller.stderr b/src/test/ui/regions/regions-free-region-ordering-caller.stderr
index c79ed50c6a4..cdf70d2a5be 100644
--- a/src/test/ui/regions/regions-free-region-ordering-caller.stderr
+++ b/src/test/ui/regions/regions-free-region-ordering-caller.stderr
@@ -6,7 +6,7 @@ LL | fn call2<'a, 'b>(a: &'a usize, b: &'b usize) {
    |          |
    |          lifetime `'a` defined here
 LL |     let z: Option<&'b &'a usize> = None;
-   |            ^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'b`
+   |            ^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'b`
    |
    = help: consider adding the following bound: `'a: 'b`
 
@@ -19,7 +19,7 @@ LL | fn call3<'a, 'b>(a: &'a usize, b: &'b usize) {
    |          lifetime `'a` defined here
 LL |     let y: Paramd<'a> = Paramd { x: a };
 LL |     let z: Option<&'b Paramd<'a>> = None;
-   |            ^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'b`
+   |            ^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'b`
    |
    = help: consider adding the following bound: `'a: 'b`
 
@@ -31,7 +31,7 @@ LL | fn call4<'a, 'b>(a: &'a usize, b: &'b usize) {
    |          |
    |          lifetime `'a` defined here
 LL |     let z: Option<&'a &'b usize> = None;
-   |            ^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'b` must outlive `'a`
+   |            ^^^^^^^^^^^^^^^^^^^^^ requires that `'b` must outlive `'a`
    |
    = help: consider adding the following bound: `'b: 'a`
 
diff --git a/src/test/ui/regions/regions-outlives-projection-container-hrtb.stderr b/src/test/ui/regions/regions-outlives-projection-container-hrtb.stderr
index 187e9056e11..6a7c908fa40 100644
--- a/src/test/ui/regions/regions-outlives-projection-container-hrtb.stderr
+++ b/src/test/ui/regions/regions-outlives-projection-container-hrtb.stderr
@@ -7,7 +7,7 @@ LL | fn with_assoc<'a,'b>() {
    |               lifetime `'a` defined here
 ...
 LL |     let _: &'a WithHrAssoc<TheType<'b>> = loop { };
-   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'b` must outlive `'a`
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'b` must outlive `'a`
    |
    = help: consider adding the following bound: `'b: 'a`
 
@@ -20,7 +20,7 @@ LL | fn with_assoc_sub<'a,'b>() {
    |                   lifetime `'a` defined here
 ...
 LL |     let _: &'a WithHrAssocSub<TheType<'b>> = loop { };
-   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'b` must outlive `'a`
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'b` must outlive `'a`
    |
    = help: consider adding the following bound: `'b: 'a`
 
diff --git a/src/test/ui/regions/regions-outlives-projection-container-wc.stderr b/src/test/ui/regions/regions-outlives-projection-container-wc.stderr
index 4178e951c86..eba2a0d5853 100644
--- a/src/test/ui/regions/regions-outlives-projection-container-wc.stderr
+++ b/src/test/ui/regions/regions-outlives-projection-container-wc.stderr
@@ -7,7 +7,7 @@ LL | fn with_assoc<'a,'b>() {
    |               lifetime `'a` defined here
 ...
 LL |     let _: &'a WithAssoc<TheType<'b>> = loop { };
-   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'b` must outlive `'a`
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'b` must outlive `'a`
    |
    = help: consider adding the following bound: `'b: 'a`
 
diff --git a/src/test/ui/regions/regions-outlives-projection-container.stderr b/src/test/ui/regions/regions-outlives-projection-container.stderr
index 073a3190022..d20a2f06adf 100644
--- a/src/test/ui/regions/regions-outlives-projection-container.stderr
+++ b/src/test/ui/regions/regions-outlives-projection-container.stderr
@@ -7,7 +7,7 @@ LL | fn with_assoc<'a,'b>() {
    |               lifetime `'a` defined here
 ...
 LL |     let _x: &'a WithAssoc<TheType<'b>> = loop { };
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'b` must outlive `'a`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'b` must outlive `'a`
    |
    = help: consider adding the following bound: `'b: 'a`
 
@@ -20,7 +20,7 @@ LL | fn without_assoc<'a,'b>() {
    |                  lifetime `'a` defined here
 ...
 LL |     let _x: &'a WithoutAssoc<TheType<'b>> = loop { };
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'b` must outlive `'a`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'b` must outlive `'a`
    |
    = help: consider adding the following bound: `'b: 'a`
 
diff --git a/src/test/ui/type-alias-impl-trait/associated-type-impl-trait-lifetime.rs b/src/test/ui/type-alias-impl-trait/associated-type-impl-trait-lifetime.rs
new file mode 100644
index 00000000000..962606508be
--- /dev/null
+++ b/src/test/ui/type-alias-impl-trait/associated-type-impl-trait-lifetime.rs
@@ -0,0 +1,20 @@
+//check-pass
+
+#![feature(type_alias_impl_trait)]
+
+trait Trait {
+    type Opaque1;
+    type Opaque2;
+    fn constrain(self);
+}
+
+impl<'a> Trait for &'a () {
+    type Opaque1 = impl Sized;
+    type Opaque2 = impl Sized + 'a;
+    fn constrain(self) {
+        let _: Self::Opaque1 = ();
+        let _: Self::Opaque2 = self;
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/ufcs/ufcs-partially-resolved.stderr b/src/test/ui/ufcs/ufcs-partially-resolved.stderr
index 3950dc9877c..5f7f6aa9f6e 100644
--- a/src/test/ui/ufcs/ufcs-partially-resolved.stderr
+++ b/src/test/ui/ufcs/ufcs-partially-resolved.stderr
@@ -205,7 +205,7 @@ error[E0223]: ambiguous associated type
   --> $DIR/ufcs-partially-resolved.rs:36:12
    |
 LL |     let _: <u8 as Tr>::Y::NN;
-   |            ^^^^^^^^^^^^^^^^^ help: use fully-qualified syntax: `<u16 as Trait>::NN`
+   |            ^^^^^^^^^^^^^^^^^ help: use fully-qualified syntax: `<<u8 as Tr>::Y as Trait>::NN`
 
 error[E0599]: no associated item named `NN` found for type `u16` in the current scope
   --> $DIR/ufcs-partially-resolved.rs:38:20