about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAli MJ Al-Nasrawy <alimjalnasrawy@gmail.com>2022-11-16 13:11:44 +0300
committerAli MJ Al-Nasrawy <alimjalnasrawy@gmail.com>2023-01-07 13:41:41 +0300
commitc6a17bf8bcfa60c8b0436251d2cf70d8eca4d198 (patch)
treee52979baba2e361406595ae5ade5e2cd52600bc5
parent37b40e471a62425cb34781bad763b5cb5047f13c (diff)
downloadrust-c6a17bf8bcfa60c8b0436251d2cf70d8eca4d198.tar.gz
rust-c6a17bf8bcfa60c8b0436251d2cf70d8eca4d198.zip
make ascribe_user_type a TypeOp
Projection types in user annotations may contain inference variables.
This makes the normalization depend on the unification with the actual
type and thus requires a separate TypeOp to track the obligations.
Otherwise simply calling `TypeChecker::normalize` would ICE with
"unexpected ambiguity"
-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_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.rs53
-rw-r--r--src/test/ui/nll/ty-outlives/wf-unreachable.stderr16
-rw-r--r--src/test/ui/nll/user-annotations/closure-sig.rs15
-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/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
15 files changed, 350 insertions, 186 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_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 f6eca977430..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);
@@ -97,8 +130,7 @@ pub fn type_op_ascribe_user_type_with_span<'tcx>(
 
         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));
     }
 
@@ -113,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/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/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-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/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`