about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2018-10-23 17:44:19 +0000
committerbors <bors@rust-lang.org>2018-10-23 17:44:19 +0000
commitf99911a4a0bead7dd1f9ef2f90442844434cc391 (patch)
tree39fac6f143105ebb9537d926737a697e78e6d4b4
parentd74b40205f7ecf30c680dc77e62cf4f127f2db09 (diff)
parent4394c83cb7685e1bbaf733a6610df58d34c892ab (diff)
downloadrust-f99911a4a0bead7dd1f9ef2f90442844434cc391.tar.gz
rust-f99911a4a0bead7dd1f9ef2f90442844434cc391.zip
Auto merge of #55229 - nikomatsakis:issue-54692-closure-signatures, r=MatthewJasper
enforce user annotations in closure signatures

Not *quite* ready yet but I'm opening anyway. Still have to finish running tests locally.

Fixes #54692
Fixes #54124

r? @matthewjasper
-rw-r--r--src/librustc/ty/context.rs18
-rw-r--r--src/librustc/ty/mod.rs2
-rw-r--r--src/librustc/ty/sty.rs5
-rw-r--r--src/librustc_mir/borrow_check/nll/region_infer/mod.rs1
-rw-r--r--src/librustc_mir/borrow_check/nll/type_check/input_output.rs76
-rw-r--r--src/librustc_mir/borrow_check/nll/type_check/mod.rs51
-rw-r--r--src/librustc_typeck/check/closure.rs21
-rw-r--r--src/librustc_typeck/check/writeback.rs22
-rw-r--r--src/test/incremental/hashes/closure_expressions.rs2
-rw-r--r--src/test/ui/issues/issue-28848.rs2
-rw-r--r--src/test/ui/issues/issue-28848.stderr10
-rw-r--r--src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.rs1
-rw-r--r--src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr15
-rw-r--r--src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.rs2
-rw-r--r--src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr19
-rw-r--r--src/test/ui/nll/user-annotations/closure-substs.rs43
-rw-r--r--src/test/ui/nll/user-annotations/closure-substs.stderr37
-rw-r--r--src/test/ui/nll/user-annotations/issue-54124.rs9
-rw-r--r--src/test/ui/nll/user-annotations/issue-54124.stderr20
-rw-r--r--src/test/ui/nll/user-annotations/wf-self-type.rs25
-rw-r--r--src/test/ui/nll/user-annotations/wf-self-type.stderr12
-rw-r--r--src/test/ui/regions/regions-static-bound.ll.stderr2
-rw-r--r--src/test/ui/regions/regions-static-bound.nll.stderr25
-rw-r--r--src/test/ui/regions/regions-static-bound.rs2
24 files changed, 321 insertions, 101 deletions
diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs
index 9f718cea58f..c60471c285d 100644
--- a/src/librustc/ty/context.rs
+++ b/src/librustc/ty/context.rs
@@ -50,7 +50,8 @@ use ty::query;
 use ty::steal::Steal;
 use ty::BindingMode;
 use ty::CanonicalTy;
-use util::nodemap::{DefIdSet, ItemLocalMap};
+use ty::CanonicalPolyFnSig;
+use util::nodemap::{DefIdMap, DefIdSet, ItemLocalMap};
 use util::nodemap::{FxHashMap, FxHashSet};
 use smallvec::SmallVec;
 use rustc_data_structures::stable_hasher::{HashStable, hash_stable_hashmap,
@@ -344,10 +345,6 @@ pub struct TypeckTables<'tcx> {
     /// belongs, but it may not exist if it's a tuple field (`tuple.0`).
     field_indices: ItemLocalMap<usize>,
 
-    /// Stores the canonicalized types provided by the user. See also
-    /// `AscribeUserType` statement in MIR.
-    user_provided_tys: ItemLocalMap<CanonicalTy<'tcx>>,
-
     /// Stores the types for various nodes in the AST.  Note that this table
     /// is not guaranteed to be populated until after typeck.  See
     /// typeck::check::fn_ctxt for details.
@@ -359,6 +356,14 @@ pub struct TypeckTables<'tcx> {
     /// other items.
     node_substs: ItemLocalMap<&'tcx Substs<'tcx>>,
 
+    /// Stores the canonicalized types provided by the user. See also
+    /// `AscribeUserType` statement in MIR.
+    user_provided_tys: ItemLocalMap<CanonicalTy<'tcx>>,
+
+    /// Stores the canonicalized types provided by the user. See also
+    /// `AscribeUserType` statement in MIR.
+    pub user_provided_sigs: DefIdMap<CanonicalPolyFnSig<'tcx>>,
+
     /// Stores the substitutions that the user explicitly gave (if any)
     /// attached to `id`. These will not include any inferred
     /// values. The canonical form is used to capture things like `_`
@@ -442,6 +447,7 @@ impl<'tcx> TypeckTables<'tcx> {
             type_dependent_defs: ItemLocalMap(),
             field_indices: ItemLocalMap(),
             user_provided_tys: ItemLocalMap(),
+            user_provided_sigs: Default::default(),
             node_types: ItemLocalMap(),
             node_substs: ItemLocalMap(),
             user_substs: ItemLocalMap(),
@@ -748,6 +754,7 @@ impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for TypeckTables<'gcx> {
             ref type_dependent_defs,
             ref field_indices,
             ref user_provided_tys,
+            ref user_provided_sigs,
             ref node_types,
             ref node_substs,
             ref user_substs,
@@ -771,6 +778,7 @@ impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for TypeckTables<'gcx> {
             type_dependent_defs.hash_stable(hcx, hasher);
             field_indices.hash_stable(hcx, hasher);
             user_provided_tys.hash_stable(hcx, hasher);
+            user_provided_sigs.hash_stable(hcx, hasher);
             node_types.hash_stable(hcx, hasher);
             node_substs.hash_stable(hcx, hasher);
             user_substs.hash_stable(hcx, hasher);
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index 5602ce479c8..bb9346f2f46 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -64,7 +64,7 @@ use rustc_data_structures::stable_hasher::{StableHasher, StableHasherResult,
 use hir;
 
 pub use self::sty::{Binder, BoundTy, BoundTyIndex, DebruijnIndex, INNERMOST};
-pub use self::sty::{FnSig, GenSig, PolyFnSig, PolyGenSig};
+pub use self::sty::{FnSig, GenSig, CanonicalPolyFnSig, PolyFnSig, PolyGenSig};
 pub use self::sty::{InferTy, ParamTy, ProjectionTy, ExistentialPredicate};
 pub use self::sty::{ClosureSubsts, GeneratorSubsts, UpvarSubsts, TypeAndMut};
 pub use self::sty::{TraitRef, TyKind, PolyTraitRef};
diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs
index 82891583870..cd9679c8763 100644
--- a/src/librustc/ty/sty.rs
+++ b/src/librustc/ty/sty.rs
@@ -11,7 +11,7 @@
 //! This module contains TyKind and its major components
 
 use hir::def_id::DefId;
-
+use infer::canonical::Canonical;
 use mir::interpret::ConstValue;
 use middle::region;
 use polonius_engine::Atom;
@@ -980,6 +980,9 @@ impl<'tcx> PolyFnSig<'tcx> {
     }
 }
 
+pub type CanonicalPolyFnSig<'tcx> = Canonical<'tcx, Binder<FnSig<'tcx>>>;
+
+
 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)]
 pub struct ParamTy {
     pub idx: u32,
diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs
index 5ed25406da4..50fd4afcd7e 100644
--- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs
@@ -1208,6 +1208,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             // to report the error. This gives better error messages
             // in some cases.
             self.report_error(mir, infcx, mir_def_id, longer_fr, shorter_fr, errors_buffer);
+            return; // continuing to iterate just reports more errors than necessary
         }
     }
 
diff --git a/src/librustc_mir/borrow_check/nll/type_check/input_output.rs b/src/librustc_mir/borrow_check/nll/type_check/input_output.rs
index a4665984d3e..ab4ee3a4ad0 100644
--- a/src/librustc_mir/borrow_check/nll/type_check/input_output.rs
+++ b/src/librustc_mir/borrow_check/nll/type_check/input_output.rs
@@ -18,6 +18,7 @@
 //! contain revealed `impl Trait` values).
 
 use borrow_check::nll::universal_regions::UniversalRegions;
+use rustc::infer::LateBoundRegionConversionTime;
 use rustc::mir::*;
 use rustc::ty::Ty;
 
@@ -36,9 +37,47 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
         let (&normalized_output_ty, normalized_input_tys) =
             normalized_inputs_and_output.split_last().unwrap();
 
+        // 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(self.mir_def_id) {
+            user_provided_sig = None;
+        } else {
+            let typeck_tables = self.tcx().typeck_tables_of(self.mir_def_id);
+            user_provided_sig = match typeck_tables.user_provided_sigs.get(&self.mir_def_id) {
+                None => None,
+                Some(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.infcx.instantiate_canonical_with_fresh_inference_vars(
+                        mir.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.
+                    Some(
+                        self.infcx
+                            .replace_late_bound_regions_with_fresh_var(
+                                mir.span,
+                                LateBoundRegionConversionTime::FnCall,
+                                &poly_sig,
+                            )
+                            .0,
+                    )
+                }
+            }
+        };
+
         // Equate expected input tys with those in the MIR.
-        let argument_locals = (1..).map(Local::new);
-        for (&normalized_input_ty, local) in normalized_input_tys.iter().zip(argument_locals) {
+        for (&normalized_input_ty, argument_index) in normalized_input_tys.iter().zip(0..) {
+            // In MIR, argument N is stored in local N+1.
+            let local = Local::new(argument_index + 1);
+
             debug!(
                 "equate_inputs_and_outputs: normalized_input_ty = {:?}",
                 normalized_input_ty
@@ -53,6 +92,27 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
             );
         }
 
+        if let Some(user_provided_sig) = user_provided_sig {
+            for (&user_provided_input_ty, argument_index) in
+                user_provided_sig.inputs().iter().zip(0..)
+            {
+                // 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 = mir.local_decls[local].ty;
+                let mir_input_span = mir.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,
+                );
+            }
+        }
+
         assert!(
             mir.yield_ty.is_some() && universal_regions.yield_ty.is_some()
                 || mir.yield_ty.is_none() && universal_regions.yield_ty.is_none()
@@ -83,6 +143,18 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
                 terr
             );
         };
+
+        // If the user explicitly annotated the output types, enforce those.
+        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));
+            self.equate_normalized_input_or_output(
+                user_provided_output_ty,
+                mir_output_ty,
+                output_span,
+            );
+        }
     }
 
     fn equate_normalized_input_or_output(&mut self, a: Ty<'tcx>, b: Ty<'tcx>, span: Span) {
diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs
index 6d152fc87bd..6db10734474 100644
--- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs
@@ -1033,6 +1033,12 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
                     assert!(!impl_self_ty.has_infer_types());
 
                     self.eq_types(self_ty, impl_self_ty, locations, category)?;
+
+                    self.prove_predicate(
+                        ty::Predicate::WellFormed(impl_self_ty),
+                        locations,
+                        category,
+                    );
                 }
 
                 // Prove the predicates coming along with `def_id`.
@@ -1070,11 +1076,9 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
     /// particularly necessary -- we'll do it lazilly as we process
     /// the value anyway -- but in some specific cases it is useful to
     /// normalize so we can suppress duplicate error messages.
-    fn fold_to_region_vid<T>(
-        &self,
-        value: T
-    ) -> T
-    where T: TypeFoldable<'tcx>
+    fn fold_to_region_vid<T>(&self, value: T) -> T
+    where
+        T: TypeFoldable<'tcx>,
     {
         if let Some(borrowck_context) = &self.borrowck_context {
             self.tcx().fold_regions(&value, &mut false, |r, _debruijn| {
@@ -1210,12 +1214,14 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
                 // though.
                 let category = match *place {
                     Place::Local(RETURN_PLACE) => if let Some(BorrowCheckContext {
-                        universal_regions: UniversalRegions {
-                            defining_ty: DefiningTy::Const(def_id, _),
-                            ..
-                        },
+                        universal_regions:
+                            UniversalRegions {
+                                defining_ty: DefiningTy::Const(def_id, _),
+                                ..
+                            },
                         ..
-                    }) = self.borrowck_context {
+                    }) = self.borrowck_context
+                    {
                         if tcx.is_static(*def_id).is_some() {
                             ConstraintCategory::UseAsStatic
                         } else {
@@ -1223,7 +1229,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
                         }
                     } else {
                         ConstraintCategory::Return
-                    }
+                    },
                     Place::Local(l) if !mir.local_decls[l].is_user_variable.is_some() => {
                         ConstraintCategory::Boring
                     }
@@ -1510,12 +1516,14 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
                 let category = match *dest {
                     Place::Local(RETURN_PLACE) => {
                         if let Some(BorrowCheckContext {
-                            universal_regions: UniversalRegions {
-                                defining_ty: DefiningTy::Const(def_id, _),
-                                ..
-                            },
+                            universal_regions:
+                                UniversalRegions {
+                                    defining_ty: DefiningTy::Const(def_id, _),
+                                    ..
+                                },
                             ..
-                        }) = self.borrowck_context {
+                        }) = self.borrowck_context
+                        {
                             if tcx.is_static(*def_id).is_some() {
                                 ConstraintCategory::UseAsStatic
                             } else {
@@ -1524,7 +1532,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
                         } else {
                             ConstraintCategory::Return
                         }
-                    },
+                    }
                     Place::Local(l) if !mir.local_decls[l].is_user_variable.is_some() => {
                         ConstraintCategory::Boring
                     }
@@ -1582,12 +1590,9 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
             } else {
                 ConstraintCategory::Boring
             };
-            if let Err(terr) = self.sub_types(
-                op_arg_ty,
-                fn_arg,
-                term_location.to_locations(),
-                category,
-            ) {
+            if let Err(terr) =
+                self.sub_types(op_arg_ty, fn_arg, term_location.to_locations(), category)
+            {
                 span_mirbug!(
                     self,
                     term,
diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs
index 940fa4d3916..3f4d187813d 100644
--- a/src/librustc_typeck/check/closure.rs
+++ b/src/librustc_typeck/check/closure.rs
@@ -377,7 +377,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
     ) -> ClosureSignatures<'tcx> {
         debug!("sig_of_closure_no_expectation()");
 
-        let bound_sig = self.supplied_sig_of_closure(decl);
+        let bound_sig = self.supplied_sig_of_closure(expr_def_id, decl);
 
         self.closure_sigs(expr_def_id, body, bound_sig)
     }
@@ -479,7 +479,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         // Along the way, it also writes out entries for types that the user
         // wrote into our tables, which are then later used by the privacy
         // check.
-        match self.check_supplied_sig_against_expectation(decl, &closure_sigs) {
+        match self.check_supplied_sig_against_expectation(expr_def_id, decl, &closure_sigs) {
             Ok(infer_ok) => self.register_infer_ok_obligations(infer_ok),
             Err(_) => return self.sig_of_closure_no_expectation(expr_def_id, decl, body),
         }
@@ -521,6 +521,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
     /// strategy.
     fn check_supplied_sig_against_expectation(
         &self,
+        expr_def_id: DefId,
         decl: &hir::FnDecl,
         expected_sigs: &ClosureSignatures<'tcx>,
     ) -> InferResult<'tcx, ()> {
@@ -528,7 +529,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         //
         // (See comment on `sig_of_closure_with_expectation` for the
         // meaning of these letters.)
-        let supplied_sig = self.supplied_sig_of_closure(decl);
+        let supplied_sig = self.supplied_sig_of_closure(expr_def_id, decl);
 
         debug!(
             "check_supplied_sig_against_expectation: supplied_sig={:?}",
@@ -598,7 +599,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
 
     /// If there is no expected signature, then we will convert the
     /// types that the user gave into a signature.
-    fn supplied_sig_of_closure(&self, decl: &hir::FnDecl) -> ty::PolyFnSig<'tcx> {
+    ///
+    /// Also, record this closure signature for later.
+    fn supplied_sig_of_closure(
+        &self,
+        expr_def_id: DefId,
+        decl: &hir::FnDecl,
+    ) -> ty::PolyFnSig<'tcx> {
         let astconv: &dyn AstConv = self;
 
         // First, convert the types that the user supplied (if any).
@@ -618,6 +625,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
 
         debug!("supplied_sig_of_closure: result={:?}", result);
 
+        let c_result = self.inh.infcx.canonicalize_response(&result);
+        self.tables.borrow_mut().user_provided_sigs.insert(
+            expr_def_id,
+            c_result,
+        );
+
         result
     }
 
diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs
index 40f5d27356c..d968bf222aa 100644
--- a/src/librustc_typeck/check/writeback.rs
+++ b/src/librustc_typeck/check/writeback.rs
@@ -52,6 +52,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         wbcx.visit_cast_types();
         wbcx.visit_free_region_map();
         wbcx.visit_user_provided_tys();
+        wbcx.visit_user_provided_sigs();
 
         let used_trait_imports = mem::replace(
             &mut self.tables.borrow_mut().used_trait_imports,
@@ -388,6 +389,27 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> {
         }
     }
 
+    fn visit_user_provided_sigs(&mut self) {
+        let fcx_tables = self.fcx.tables.borrow();
+        debug_assert_eq!(fcx_tables.local_id_root, self.tables.local_id_root);
+
+        for (&def_id, c_sig) in fcx_tables.user_provided_sigs.iter() {
+            let c_sig = if let Some(c_sig) = self.tcx().lift_to_global(c_sig) {
+                c_sig
+            } else {
+                span_bug!(
+                    self.fcx.tcx.hir.span_if_local(def_id).unwrap(),
+                    "writeback: `{:?}` missing from the global type context",
+                    c_sig
+                );
+            };
+
+            self.tables
+                .user_provided_sigs
+                .insert(def_id, c_sig.clone());
+        }
+    }
+
     fn visit_opaque_types(&mut self, span: Span) {
         for (&def_id, opaque_defn) in self.fcx.opaque_types.borrow().iter() {
             let node_id = self.tcx().hir.as_local_node_id(def_id).unwrap();
diff --git a/src/test/incremental/hashes/closure_expressions.rs b/src/test/incremental/hashes/closure_expressions.rs
index 839e0ca8e01..8469f0aa664 100644
--- a/src/test/incremental/hashes/closure_expressions.rs
+++ b/src/test/incremental/hashes/closure_expressions.rs
@@ -95,7 +95,7 @@ pub fn add_type_ascription_to_parameter() {
 }
 
 #[cfg(not(cfail1))]
-#[rustc_clean(cfg="cfail2", except="HirBody")]
+#[rustc_clean(cfg="cfail2", except="HirBody, TypeckTables")]
 #[rustc_clean(cfg="cfail3")]
 pub fn add_type_ascription_to_parameter() {
     let closure = |x: u32| x + 1u32;
diff --git a/src/test/ui/issues/issue-28848.rs b/src/test/ui/issues/issue-28848.rs
index c13fd5fa716..1a06d59f0b1 100644
--- a/src/test/ui/issues/issue-28848.rs
+++ b/src/test/ui/issues/issue-28848.rs
@@ -8,8 +8,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// ignore-compare-mode-nll
-
 struct Foo<'a, 'b: 'a>(&'a &'b ());
 
 impl<'a, 'b> Foo<'a, 'b> {
diff --git a/src/test/ui/issues/issue-28848.stderr b/src/test/ui/issues/issue-28848.stderr
index dadcff226f5..738a1c0c6f6 100644
--- a/src/test/ui/issues/issue-28848.stderr
+++ b/src/test/ui/issues/issue-28848.stderr
@@ -1,16 +1,16 @@
 error[E0478]: lifetime bound not satisfied
-  --> $DIR/issue-28848.rs:22:5
+  --> $DIR/issue-28848.rs:20:5
    |
 LL |     Foo::<'a, 'b>::xmute(u) //~ ERROR lifetime bound not satisfied
    |     ^^^^^^^^^^^^^^^^^^^^
    |
-note: lifetime parameter instantiated with the lifetime 'b as defined on the function body at 21:16
-  --> $DIR/issue-28848.rs:21:16
+note: lifetime parameter instantiated with the lifetime 'b as defined on the function body at 19:16
+  --> $DIR/issue-28848.rs:19:16
    |
 LL | pub fn foo<'a, 'b>(u: &'b ()) -> &'a () {
    |                ^^
-note: but lifetime parameter must outlive the lifetime 'a as defined on the function body at 21:12
-  --> $DIR/issue-28848.rs:21:12
+note: but lifetime parameter must outlive the lifetime 'a as defined on the function body at 19:12
+  --> $DIR/issue-28848.rs:19:12
    |
 LL | pub fn foo<'a, 'b>(u: &'b ()) -> &'a () {
    |            ^^
diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.rs b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.rs
index 49b62581640..4bef6cf8cd1 100644
--- a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.rs
+++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.rs
@@ -47,7 +47,6 @@ fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) {
 
         // Only works if 'x: 'y:
         demand_y(x, y, x.get())
-        //~^ ERROR unsatisfied lifetime constraints
     });
 }
 
diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr
index a5435cc65ca..f22dbdba934 100644
--- a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr
+++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr
@@ -7,7 +7,6 @@ LL | |         //~^ ERROR borrowed data escapes outside of function
 LL | |
 LL | |         // Only works if 'x: 'y:
 LL | |         demand_y(x, y, x.get())
-LL | |         //~^ ERROR unsatisfied lifetime constraints
 LL | |     });
    | |_____^
    |
@@ -44,21 +43,9 @@ LL | |         //~^ ERROR borrowed data escapes outside of function
 LL | |
 LL | |         // Only works if 'x: 'y:
 LL | |         demand_y(x, y, x.get())
-LL | |         //~^ ERROR unsatisfied lifetime constraints
 LL | |     });
    | |______^ `cell_a` escapes the function body here
 
-error: unsatisfied lifetime constraints
-  --> $DIR/propagate-approximated-shorter-to-static-no-bound.rs:49:9
-   |
-LL | fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) {
-   |           --  -- lifetime `'b` defined here
-   |           |
-   |           lifetime `'a` defined here
-...
-LL |         demand_y(x, y, x.get())
-   |         ^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'a` must outlive `'b`
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0521`.
diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.rs b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.rs
index 0181792cbdc..05e9818b91e 100644
--- a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.rs
+++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.rs
@@ -47,9 +47,9 @@ fn demand_y<'x, 'y>(_cell_x: &Cell<&'x u32>, _cell_y: &Cell<&'y u32>, _y: &'y u3
 fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) {
     establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| {
         //~^ ERROR borrowed data escapes outside of function
+
         // Only works if 'x: 'y:
         demand_y(x, y, x.get())
-        //~^ ERROR unsatisfied lifetime constraints
     });
 }
 
diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr
index 35692414081..2b1cb5a30ce 100644
--- a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr
+++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr
@@ -4,9 +4,9 @@ note: External requirements
 LL |       establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| {
    |  _______________________________________________^
 LL | |         //~^ ERROR borrowed data escapes outside of function
+LL | |
 LL | |         // Only works if 'x: 'y:
 LL | |         demand_y(x, y, x.get())
-LL | |         //~^ ERROR unsatisfied lifetime constraints
 LL | |     });
    | |_____^
    |
@@ -25,7 +25,7 @@ note: No external requirements
 LL | / fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) {
 LL | |     establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| {
 LL | |         //~^ ERROR borrowed data escapes outside of function
-LL | |         // Only works if 'x: 'y:
+LL | |
 ...  |
 LL | |     });
 LL | | }
@@ -40,23 +40,12 @@ LL |   fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) {
    |                     ------ `cell_a` is a reference that is only valid in the function body
 LL | /     establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| {
 LL | |         //~^ ERROR borrowed data escapes outside of function
+LL | |
 LL | |         // Only works if 'x: 'y:
 LL | |         demand_y(x, y, x.get())
-LL | |         //~^ ERROR unsatisfied lifetime constraints
 LL | |     });
    | |______^ `cell_a` escapes the function body here
 
-error: unsatisfied lifetime constraints
-  --> $DIR/propagate-approximated-shorter-to-static-wrong-bound.rs:51:9
-   |
-LL | fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) {
-   |           --  -- lifetime `'b` defined here
-   |           |
-   |           lifetime `'a` defined here
-...
-LL |         demand_y(x, y, x.get())
-   |         ^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'a` must outlive `'b`
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0521`.
diff --git a/src/test/ui/nll/user-annotations/closure-substs.rs b/src/test/ui/nll/user-annotations/closure-substs.rs
new file mode 100644
index 00000000000..77c7315fa66
--- /dev/null
+++ b/src/test/ui/nll/user-annotations/closure-substs.rs
@@ -0,0 +1,43 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(nll)]
+
+// Test that we enforce user-provided type annotations on closures.
+
+fn foo<'a>() {
+    // Here `x` is free in the closure sig:
+    |x: &'a i32| -> &'static i32 {
+        return x; //~ ERROR unsatisfied lifetime constraints
+    };
+}
+
+fn foo1() {
+    // Here `x` is bound in the closure sig:
+    |x: &i32| -> &'static i32 {
+        return x; //~ ERROR unsatisfied lifetime constraints
+    };
+}
+
+fn bar<'a>() {
+    // Here `x` is free in the closure sig:
+    |x: &'a i32, b: fn(&'static i32)| {
+        b(x); //~ ERROR unsatisfied lifetime constraints
+    };
+}
+
+fn bar1() {
+    // Here `x` is bound in the closure sig:
+    |x: &i32, b: fn(&'static i32)| {
+        b(x); //~ ERROR borrowed data escapes outside of closure
+    };
+}
+
+fn main() { }
diff --git a/src/test/ui/nll/user-annotations/closure-substs.stderr b/src/test/ui/nll/user-annotations/closure-substs.stderr
new file mode 100644
index 00000000000..c69d514d9e6
--- /dev/null
+++ b/src/test/ui/nll/user-annotations/closure-substs.stderr
@@ -0,0 +1,37 @@
+error: unsatisfied lifetime constraints
+  --> $DIR/closure-substs.rs:18:16
+   |
+LL | fn foo<'a>() {
+   |        -- lifetime `'a` defined here
+...
+LL |         return x; //~ ERROR unsatisfied lifetime constraints
+   |                ^ returning this value requires that `'a` must outlive `'static`
+
+error: unsatisfied lifetime constraints
+  --> $DIR/closure-substs.rs:25:16
+   |
+LL |     |x: &i32| -> &'static i32 {
+   |         - let's call the lifetime of this reference `'1`
+LL |         return x; //~ ERROR unsatisfied lifetime constraints
+   |                ^ returning this value requires that `'1` must outlive `'static`
+
+error: unsatisfied lifetime constraints
+  --> $DIR/closure-substs.rs:32:9
+   |
+LL | fn bar<'a>() {
+   |        -- lifetime `'a` defined here
+...
+LL |         b(x); //~ ERROR unsatisfied lifetime constraints
+   |         ^^^^ argument requires that `'a` must outlive `'static`
+
+error[E0521]: borrowed data escapes outside of closure
+  --> $DIR/closure-substs.rs:39:9
+   |
+LL |     |x: &i32, b: fn(&'static i32)| {
+   |      - `x` is a reference that is only valid in the closure body
+LL |         b(x); //~ ERROR borrowed data escapes outside of closure
+   |         ^^^^ `x` escapes the closure body here
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0521`.
diff --git a/src/test/ui/nll/user-annotations/issue-54124.rs b/src/test/ui/nll/user-annotations/issue-54124.rs
new file mode 100644
index 00000000000..8cdd390540a
--- /dev/null
+++ b/src/test/ui/nll/user-annotations/issue-54124.rs
@@ -0,0 +1,9 @@
+#![feature(nll)]
+
+fn test<'a>() {
+    let _:fn(&()) = |_:&'a ()| {};
+}
+
+fn main() {
+    test();
+}
diff --git a/src/test/ui/nll/user-annotations/issue-54124.stderr b/src/test/ui/nll/user-annotations/issue-54124.stderr
new file mode 100644
index 00000000000..df5e4b0447c
--- /dev/null
+++ b/src/test/ui/nll/user-annotations/issue-54124.stderr
@@ -0,0 +1,20 @@
+error: unsatisfied lifetime constraints
+  --> $DIR/issue-54124.rs:4:22
+   |
+LL | fn test<'a>() {
+   |         -- lifetime `'a` defined here
+LL |     let _:fn(&()) = |_:&'a ()| {};
+   |                      ^ - let's call the lifetime of this reference `'1`
+   |                      |
+   |                      requires that `'1` must outlive `'a`
+
+error: unsatisfied lifetime constraints
+  --> $DIR/issue-54124.rs:4:22
+   |
+LL | fn test<'a>() {
+   |         -- lifetime `'a` defined here
+LL |     let _:fn(&()) = |_:&'a ()| {};
+   |                      ^ requires that `'a` must outlive `'static`
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/nll/user-annotations/wf-self-type.rs b/src/test/ui/nll/user-annotations/wf-self-type.rs
new file mode 100644
index 00000000000..c3cb3068bce
--- /dev/null
+++ b/src/test/ui/nll/user-annotations/wf-self-type.rs
@@ -0,0 +1,25 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(nll)]
+
+struct Foo<'a, 'b: 'a>(&'a &'b ());
+
+impl<'a, 'b> Foo<'a, 'b> {
+    fn xmute(a: &'b ()) -> &'a () {
+        unreachable!()
+    }
+}
+
+pub fn foo<'a, 'b>(u: &'b ()) -> &'a () {
+    Foo::xmute(u) //~ ERROR unsatisfied lifetime constraints
+}
+
+fn main() {}
diff --git a/src/test/ui/nll/user-annotations/wf-self-type.stderr b/src/test/ui/nll/user-annotations/wf-self-type.stderr
new file mode 100644
index 00000000000..78d86fbf17e
--- /dev/null
+++ b/src/test/ui/nll/user-annotations/wf-self-type.stderr
@@ -0,0 +1,12 @@
+error: unsatisfied lifetime constraints
+  --> $DIR/wf-self-type.rs:22:5
+   |
+LL | pub fn foo<'a, 'b>(u: &'b ()) -> &'a () {
+   |            --  -- lifetime `'b` defined here
+   |            |
+   |            lifetime `'a` defined here
+LL |     Foo::xmute(u) //~ ERROR unsatisfied lifetime constraints
+   |     ^^^^^^^^^^^^^ returning this value requires that `'b` must outlive `'a`
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/regions/regions-static-bound.ll.stderr b/src/test/ui/regions/regions-static-bound.ll.stderr
index 16add00eb41..cf291279210 100644
--- a/src/test/ui/regions/regions-static-bound.ll.stderr
+++ b/src/test/ui/regions/regions-static-bound.ll.stderr
@@ -20,7 +20,7 @@ LL |     static_id(&u); //[ll]~ ERROR explicit lifetime required in the type of
    |     ^^^^^^^^^ lifetime `'static` required
 
 error[E0621]: explicit lifetime required in the type of `v`
-  --> $DIR/regions-static-bound.rs:27:5
+  --> $DIR/regions-static-bound.rs:26:5
    |
 LL | fn error(u: &(), v: &()) {
    |                     --- help: add explicit lifetime `'static` to the type of `v`: `&'static ()`
diff --git a/src/test/ui/regions/regions-static-bound.nll.stderr b/src/test/ui/regions/regions-static-bound.nll.stderr
index dc3a32cec6a..462fbe8ee19 100644
--- a/src/test/ui/regions/regions-static-bound.nll.stderr
+++ b/src/test/ui/regions/regions-static-bound.nll.stderr
@@ -15,7 +15,7 @@ LL |     static_id(&u); //[ll]~ ERROR explicit lifetime required in the type of
    |     ^^^^^^^^^^^^^ lifetime `'static` required
 
 error[E0621]: explicit lifetime required in the type of `v`
-  --> $DIR/regions-static-bound.rs:27:5
+  --> $DIR/regions-static-bound.rs:26:5
    |
 LL | fn error(u: &(), v: &()) {
    |                     --- help: add explicit lifetime `'static` to the type of `v`: `&'static ()`
@@ -23,27 +23,6 @@ LL | fn error(u: &(), v: &()) {
 LL |     static_id_indirect(&v); //[ll]~ ERROR explicit lifetime required in the type of `v` [E0621]
    |     ^^^^^^^^^^^^^^^^^^^^^^ lifetime `'static` required
 
-error: unsatisfied lifetime constraints
-  --> $DIR/regions-static-bound.rs:24:5
-   |
-LL | fn error(u: &(), v: &()) {
-   |             -       - let's call the lifetime of this reference `'2`
-   |             |
-   |             let's call the lifetime of this reference `'1`
-LL |     static_id(&u); //[ll]~ ERROR explicit lifetime required in the type of `u` [E0621]
-   |     ^^^^^^^^^^^^^ argument requires that `'1` must outlive `'2`
-
-error: unsatisfied lifetime constraints
-  --> $DIR/regions-static-bound.rs:27:5
-   |
-LL | fn error(u: &(), v: &()) {
-   |             -       - let's call the lifetime of this reference `'1`
-   |             |
-   |             let's call the lifetime of this reference `'2`
-...
-LL |     static_id_indirect(&v); //[ll]~ ERROR explicit lifetime required in the type of `v` [E0621]
-   |     ^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'2`
-
-error: aborting due to 5 previous errors
+error: aborting due to 3 previous errors
 
 For more information about this error, try `rustc --explain E0621`.
diff --git a/src/test/ui/regions/regions-static-bound.rs b/src/test/ui/regions/regions-static-bound.rs
index 34baf5ffff0..c5dc6000e83 100644
--- a/src/test/ui/regions/regions-static-bound.rs
+++ b/src/test/ui/regions/regions-static-bound.rs
@@ -23,10 +23,8 @@ fn static_id_wrong_way<'a>(t: &'a ()) -> &'static () where 'static: 'a {
 fn error(u: &(), v: &()) {
     static_id(&u); //[ll]~ ERROR explicit lifetime required in the type of `u` [E0621]
     //[nll]~^ ERROR explicit lifetime required in the type of `u` [E0621]
-    //[nll]~| ERROR unsatisfied lifetime constraints
     static_id_indirect(&v); //[ll]~ ERROR explicit lifetime required in the type of `v` [E0621]
     //[nll]~^ ERROR explicit lifetime required in the type of `v` [E0621]
-    //[nll]~| ERROR unsatisfied lifetime constraints
 }
 
 fn main() {}