about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2018-10-27 00:39:11 +0000
committerbors <bors@rust-lang.org>2018-10-27 00:39:11 +0000
commit10f42cbde015c44a019e8b6dceca472a1532f36a (patch)
treef3be514f7f0d9d31f457bf1643683dc9f5b20c2b
parentfa45602b71c59c6315fdb07e925dec61f5827ad9 (diff)
parent639a3ffadcfec5165d434cbbf320678f69b01a5b (diff)
downloadrust-10f42cbde015c44a019e8b6dceca472a1532f36a.tar.gz
rust-10f42cbde015c44a019e8b6dceca472a1532f36a.zip
Auto merge of #55274 - pnkfelix:issue-54570-proj-path-into-pats-with-type-take-2, r=nikomatsakis
Handle bindings in substructure of patterns with type ascriptions

This attempts to follow the outline described by @nikomatsakis [here](https://github.com/rust-lang/rust/issues/47184#issuecomment-420041056). Its a bit more complicated than expected for two reasons:

 1. In general it handles sets of type ascriptions, because such ascriptions can be nested within patterns
 2.  It has a separate types in the HAIR, `PatternTypeProjections` and `PatternTypeProjection`, which are analogues to the corresponding types in the MIR.

The main reason I added the new HAIR types was because I am worried that the current implementation is inefficent, and asymptotically so: It makes copies of vectors as it descends the patterns, even when those accumulated vectors are never used.

Longer term, I would like to used a linked tree structure for the `PatternTypeProjections` and `PatternTypeProjection`, and save the construction of standalone vectors for the MIR types. I didn't want to block landing this on that hypoethetical revision; but I figured I could at least make the future change easier by differentiating between the two types now.

Oh, one more thing: This doesn't attempt to handle `ref x` (in terms of ensuring that any necessary types are ascribed to `x` in that scenario as well). We should open an issue to investigate supporting that as well. But I didn't want to block this PR on that future work.

Fix #54570
-rw-r--r--src/librustc/ich/impls_mir.rs3
-rw-r--r--src/librustc/mir/mod.rs123
-rw-r--r--src/librustc/mir/tcx.rs56
-rw-r--r--src/librustc/mir/visit.rs34
-rw-r--r--src/librustc/traits/query/type_op/ascribe_user_type.rs11
-rw-r--r--src/librustc/ty/context.rs40
-rw-r--r--src/librustc/ty/structural_impls.rs12
-rw-r--r--src/librustc_codegen_llvm/mir/analyze.rs4
-rw-r--r--src/librustc_codegen_llvm/mir/place.rs3
-rw-r--r--src/librustc_mir/borrow_check/nll/constraint_generation.rs4
-rw-r--r--src/librustc_mir/borrow_check/nll/type_check/mod.rs31
-rw-r--r--src/librustc_mir/build/block.rs11
-rw-r--r--src/librustc_mir/build/expr/as_place.rs4
-rw-r--r--src/librustc_mir/build/expr/into.rs2
-rw-r--r--src/librustc_mir/build/matches/mod.rs85
-rw-r--r--src/librustc_mir/build/matches/simplify.rs4
-rw-r--r--src/librustc_mir/build/mod.rs2
-rw-r--r--src/librustc_mir/hair/cx/block.rs2
-rw-r--r--src/librustc_mir/hair/mod.rs1
-rw-r--r--src/librustc_mir/hair/pattern/mod.rs124
-rw-r--r--src/librustc_mir/shim.rs2
-rw-r--r--src/librustc_mir/transform/generator.rs6
-rw-r--r--src/librustc_mir/util/pretty.rs2
-rw-r--r--src/librustc_passes/mir_stats.rs3
-rw-r--r--src/librustc_traits/type_op.rs24
-rw-r--r--src/test/mir-opt/basic_assignment.rs2
-rw-r--r--src/test/ui/nll/user-annotations/issue-54570-bootstrapping.rs31
-rw-r--r--src/test/ui/nll/user-annotations/patterns.rs49
-rw-r--r--src/test/ui/nll/user-annotations/patterns.stderr74
29 files changed, 622 insertions, 127 deletions
diff --git a/src/librustc/ich/impls_mir.rs b/src/librustc/ich/impls_mir.rs
index 4f68569c600..274a2df283c 100644
--- a/src/librustc/ich/impls_mir.rs
+++ b/src/librustc/ich/impls_mir.rs
@@ -606,3 +606,6 @@ impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for mir::UserTypeAnnotation<
         }
     }
 }
+
+impl_stable_hash_for!(struct mir::UserTypeProjection<'tcx> { base, projs });
+impl_stable_hash_for!(struct mir::UserTypeProjections<'tcx> { contents });
diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs
index 7c3d4713572..952783a91b2 100644
--- a/src/librustc/mir/mod.rs
+++ b/src/librustc/mir/mod.rs
@@ -710,7 +710,7 @@ pub struct LocalDecl<'tcx> {
     /// e.g. via `let x: T`, then we carry that type here. The MIR
     /// borrow checker needs this information since it can affect
     /// region inference.
-    pub user_ty: Option<(UserTypeAnnotation<'tcx>, Span)>,
+    pub user_ty: UserTypeProjections<'tcx>,
 
     /// Name of the local, used in debuginfo and pretty-printing.
     ///
@@ -882,7 +882,7 @@ impl<'tcx> LocalDecl<'tcx> {
         LocalDecl {
             mutability,
             ty,
-            user_ty: None,
+            user_ty: UserTypeProjections::none(),
             name: None,
             source_info: SourceInfo {
                 span,
@@ -903,7 +903,7 @@ impl<'tcx> LocalDecl<'tcx> {
         LocalDecl {
             mutability: Mutability::Mut,
             ty: return_ty,
-            user_ty: None,
+            user_ty: UserTypeProjections::none(),
             source_info: SourceInfo {
                 span,
                 scope: OUTERMOST_SOURCE_SCOPE,
@@ -1741,7 +1741,7 @@ pub enum StatementKind<'tcx> {
     /// - `Contravariant` -- requires that `T_y :> T`
     /// - `Invariant` -- requires that `T_y == T`
     /// - `Bivariant` -- no effect
-    AscribeUserType(Place<'tcx>, ty::Variance, Box<UserTypeAnnotation<'tcx>>),
+    AscribeUserType(Place<'tcx>, ty::Variance, Box<UserTypeProjection<'tcx>>),
 
     /// No-op. Useful for deleting instructions without affecting statement indices.
     Nop,
@@ -1944,6 +1944,10 @@ pub type PlaceProjection<'tcx> = Projection<'tcx, Place<'tcx>, Local, Ty<'tcx>>;
 /// and the index is a local.
 pub type PlaceElem<'tcx> = ProjectionElem<'tcx, Local, Ty<'tcx>>;
 
+/// Alias for projections as they appear in `UserTypeProjection`, where we
+/// need neither the `V` parameter for `Index` nor the `T` for `Field`.
+pub type ProjectionKind<'tcx> = ProjectionElem<'tcx, (), ()>;
+
 newtype_index! {
     pub struct Field {
         DEBUG_FORMAT = "field[{}]"
@@ -2449,6 +2453,117 @@ EnumLiftImpl! {
     }
 }
 
+/// A collection of projections into user types.
+///
+/// They are projections because a binding can occur a part of a
+/// parent pattern that has been ascribed a type.
+///
+/// Its a collection because there can be multiple type ascriptions on
+/// the path from the root of the pattern down to the binding itself.
+///
+/// An example:
+///
+/// ```rust
+/// struct S<'a>((i32, &'a str), String);
+/// let S((_, w): (i32, &'static str), _): S = ...;
+/// //    ------  ^^^^^^^^^^^^^^^^^^^ (1)
+/// //  ---------------------------------  ^ (2)
+/// ```
+///
+/// The highlights labelled `(1)` show the subpattern `(_, w)` being
+/// ascribed the type `(i32, &'static str)`.
+///
+/// The highlights labelled `(2)` show the whole pattern being
+/// ascribed the type `S`.
+///
+/// In this example, when we descend to `w`, we will have built up the
+/// following two projected types:
+///
+///   * base: `S`,                   projection: `(base.0).1`
+///   * base: `(i32, &'static str)`, projection: `base.1`
+///
+/// The first will lead to the constraint `w: &'1 str` (for some
+/// inferred region `'1`). The second will lead to the constraint `w:
+/// &'static str`.
+#[derive(Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
+pub struct UserTypeProjections<'tcx> {
+    pub(crate) contents: Vec<(UserTypeProjection<'tcx>, Span)>,
+}
+
+BraceStructTypeFoldableImpl! {
+    impl<'tcx> TypeFoldable<'tcx> for UserTypeProjections<'tcx> {
+        contents
+    }
+}
+
+impl<'tcx> UserTypeProjections<'tcx> {
+    pub fn none() -> Self {
+        UserTypeProjections { contents: vec![] }
+    }
+
+    pub fn from_projections(projs: impl Iterator<Item=(UserTypeProjection<'tcx>, Span)>) -> Self {
+        UserTypeProjections { contents: projs.collect() }
+    }
+
+    pub fn projections_and_spans(&self) -> impl Iterator<Item=&(UserTypeProjection<'tcx>, Span)> {
+        self.contents.iter()
+    }
+
+    pub fn projections(&self) -> impl Iterator<Item=&UserTypeProjection<'tcx>> {
+        self.contents.iter().map(|&(ref user_type, _span)| user_type)
+    }
+}
+
+/// Encodes the effect of a user-supplied type annotation on the
+/// subcomponents of a pattern. The effect is determined by applying the
+/// given list of proejctions to some underlying base type. Often,
+/// the projection element list `projs` is empty, in which case this
+/// directly encodes a type in `base`. But in the case of complex patterns with
+/// subpatterns and bindings, we want to apply only a *part* of the type to a variable,
+/// in which case the `projs` vector is used.
+///
+/// Examples:
+///
+/// * `let x: T = ...` -- here, the `projs` vector is empty.
+///
+/// * `let (x, _): T = ...` -- here, the `projs` vector would contain
+///   `field[0]` (aka `.0`), indicating that the type of `s` is
+///   determined by finding the type of the `.0` field from `T`.
+#[derive(Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
+pub struct UserTypeProjection<'tcx> {
+    pub base: UserTypeAnnotation<'tcx>,
+    pub projs: Vec<ProjectionElem<'tcx, (), ()>>,
+}
+
+impl<'tcx> Copy for ProjectionKind<'tcx> { }
+
+CloneTypeFoldableAndLiftImpls! { ProjectionKind<'tcx>, }
+
+impl<'tcx> TypeFoldable<'tcx> for UserTypeProjection<'tcx> {
+    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
+        use mir::ProjectionElem::*;
+
+        let base = self.base.fold_with(folder);
+        let projs: Vec<_> = self.projs
+            .iter()
+            .map(|elem| {
+                match elem {
+                    Deref => Deref,
+                    Field(f, ()) => Field(f.clone(), ()),
+                    Index(()) => Index(()),
+                    elem => elem.clone(),
+                }})
+            .collect();
+
+        UserTypeProjection { base, projs }
+    }
+
+    fn super_visit_with<Vs: TypeVisitor<'tcx>>(&self, visitor: &mut Vs) -> bool {
+        self.base.visit_with(visitor)
+        // Note: there's nothing in `self.proj` to visit.
+    }
+}
+
 newtype_index! {
     pub struct Promoted {
         DEBUG_FORMAT = "promoted[{}]"
diff --git a/src/librustc/mir/tcx.rs b/src/librustc/mir/tcx.rs
index fc7b4862b0a..473730c5489 100644
--- a/src/librustc/mir/tcx.rs
+++ b/src/librustc/mir/tcx.rs
@@ -44,11 +44,59 @@ impl<'a, 'gcx, 'tcx> PlaceTy<'tcx> {
         }
     }
 
+    /// `place_ty.field_ty(tcx, f)` computes the type at a given field
+    /// of a record or enum-variant. (Most clients of `PlaceTy` can
+    /// instead just extract the relevant type directly from their
+    /// `PlaceElem`, but some instances of `ProjectionElem<V, T>` do
+    /// not carry a `Ty` for `T`.)
+    ///
+    /// Note that the resulting type has not been normalized.
+    pub fn field_ty(self, tcx: TyCtxt<'a, 'gcx, 'tcx>, f: &Field) -> Ty<'tcx>
+    {
+        // Pass `0` here so it can be used as a "default" variant_index in first arm below
+        let answer = match (self, 0) {
+            (PlaceTy::Ty {
+                ty: &ty::TyS { sty: ty::TyKind::Adt(adt_def, substs), .. } }, variant_index) |
+            (PlaceTy::Downcast { adt_def, substs, variant_index }, _) => {
+                let variant_def = &adt_def.variants[variant_index];
+                let field_def = &variant_def.fields[f.index()];
+                field_def.ty(tcx, substs)
+            }
+            (PlaceTy::Ty { ty }, _) => {
+                match ty.sty {
+                    ty::Tuple(ref tys) => tys[f.index()],
+                    _ => bug!("extracting field of non-tuple non-adt: {:?}", self),
+                }
+            }
+        };
+        debug!("field_ty self: {:?} f: {:?} yields: {:?}", self, f, answer);
+        answer
+    }
+
+    /// Convenience wrapper around `projection_ty_core` for
+    /// `PlaceElem`, where we can just use the `Ty` that is already
+    /// stored inline on field projection elems.
     pub fn projection_ty(self, tcx: TyCtxt<'a, 'gcx, 'tcx>,
                          elem: &PlaceElem<'tcx>)
                          -> PlaceTy<'tcx>
     {
-        match *elem {
+        self.projection_ty_core(tcx, elem, |_, _, ty| ty)
+    }
+
+    /// `place_ty.projection_ty_core(tcx, elem, |...| { ... })`
+    /// projects `place_ty` onto `elem`, returning the appropriate
+    /// `Ty` or downcast variant corresponding to that projection.
+    /// The `handle_field` callback must map a `Field` to its `Ty`,
+    /// (which should be trivial when `T` = `Ty`).
+    pub fn projection_ty_core<V, T>(self,
+                                    tcx: TyCtxt<'a, 'gcx, 'tcx>,
+                                    elem: &ProjectionElem<'tcx, V, T>,
+                                    mut handle_field: impl FnMut(&Self, &Field, &T) -> Ty<'tcx>)
+                                    -> PlaceTy<'tcx>
+    where
+        V: ::std::fmt::Debug, T: ::std::fmt::Debug
+    {
+        let answer = match *elem {
             ProjectionElem::Deref => {
                 let ty = self.to_ty(tcx)
                              .builtin_deref(true)
@@ -94,8 +142,10 @@ impl<'a, 'gcx, 'tcx> PlaceTy<'tcx> {
                         bug!("cannot downcast non-ADT type: `{:?}`", self)
                     }
                 },
-            ProjectionElem::Field(_, fty) => PlaceTy::Ty { ty: fty }
-        }
+            ProjectionElem::Field(ref f, ref fty) => PlaceTy::Ty { ty: handle_field(&self, f, fty) }
+        };
+        debug!("projection_ty self: {:?} elem: {:?} yields: {:?}", self, elem, answer);
+        answer
     }
 }
 
diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs
index 76c76404d2f..bfc03923f60 100644
--- a/src/librustc/mir/visit.rs
+++ b/src/librustc/mir/visit.rs
@@ -147,7 +147,7 @@ macro_rules! make_mir_visitor {
             fn visit_ascribe_user_ty(&mut self,
                                      place: & $($mutability)* Place<'tcx>,
                                      variance: & $($mutability)* ty::Variance,
-                                     user_ty: & $($mutability)* UserTypeAnnotation<'tcx>,
+                                     user_ty: & $($mutability)* UserTypeProjection<'tcx>,
                                      location: Location) {
                 self.super_ascribe_user_ty(place, variance, user_ty, location);
             }
@@ -175,9 +175,8 @@ macro_rules! make_mir_visitor {
 
             fn visit_projection_elem(&mut self,
                                      place: & $($mutability)* PlaceElem<'tcx>,
-                                     context: PlaceContext<'tcx>,
                                      location: Location) {
-                self.super_projection_elem(place, context, location);
+                self.super_projection_elem(place, location);
             }
 
             fn visit_branch(&mut self,
@@ -214,6 +213,13 @@ macro_rules! make_mir_visitor {
                 self.super_ty(ty);
             }
 
+            fn visit_user_type_projection(
+                &mut self,
+                ty: & $($mutability)* UserTypeProjection<'tcx>,
+            ) {
+                self.super_user_type_projection(ty);
+            }
+
             fn visit_user_type_annotation(
                 &mut self,
                 ty: & $($mutability)* UserTypeAnnotation<'tcx>,
@@ -640,10 +646,10 @@ macro_rules! make_mir_visitor {
             fn super_ascribe_user_ty(&mut self,
                                      place: & $($mutability)* Place<'tcx>,
                                      _variance: & $($mutability)* ty::Variance,
-                                     user_ty: & $($mutability)* UserTypeAnnotation<'tcx>,
+                                     user_ty: & $($mutability)* UserTypeProjection<'tcx>,
                                      location: Location) {
                 self.visit_place(place, PlaceContext::Validate, location);
-                self.visit_user_type_annotation(user_ty);
+                self.visit_user_type_projection(user_ty);
             }
 
             fn super_place(&mut self,
@@ -692,12 +698,11 @@ macro_rules! make_mir_visitor {
                     PlaceContext::Projection(Mutability::Not)
                 };
                 self.visit_place(base, context, location);
-                self.visit_projection_elem(elem, context, location);
+                self.visit_projection_elem(elem, location);
             }
 
             fn super_projection_elem(&mut self,
                                      proj: & $($mutability)* PlaceElem<'tcx>,
-                                     _context: PlaceContext<'tcx>,
                                      location: Location) {
                 match *proj {
                     ProjectionElem::Deref => {
@@ -738,8 +743,8 @@ macro_rules! make_mir_visitor {
                     local,
                     source_info: *source_info,
                 });
-                if let Some((user_ty, _)) = user_ty {
-                    self.visit_user_type_annotation(user_ty);
+                for (user_ty, _) in & $($mutability)* user_ty.contents {
+                    self.visit_user_type_projection(user_ty);
                 }
                 self.visit_source_info(source_info);
                 self.visit_source_scope(visibility_scope);
@@ -786,6 +791,17 @@ macro_rules! make_mir_visitor {
                 self.visit_source_scope(scope);
             }
 
+            fn super_user_type_projection(
+                &mut self,
+                ty: & $($mutability)* UserTypeProjection<'tcx>,
+            ) {
+                let UserTypeProjection {
+                    ref $($mutability)* base,
+                    projs: _, // Note: Does not visit projection elems!
+                } = *ty;
+                self.visit_user_type_annotation(base);
+            }
+
             fn super_user_type_annotation(
                 &mut self,
                 _ty: & $($mutability)* UserTypeAnnotation<'tcx>,
diff --git a/src/librustc/traits/query/type_op/ascribe_user_type.rs b/src/librustc/traits/query/type_op/ascribe_user_type.rs
index b3955b8f864..23445781eb2 100644
--- a/src/librustc/traits/query/type_op/ascribe_user_type.rs
+++ b/src/librustc/traits/query/type_op/ascribe_user_type.rs
@@ -11,6 +11,7 @@
 use infer::canonical::{Canonical, Canonicalized, CanonicalizedQueryResponse, QueryResponse};
 use traits::query::Fallible;
 use hir::def_id::DefId;
+use mir::ProjectionKind;
 use ty::{self, ParamEnvAnd, Ty, TyCtxt};
 use ty::subst::UserSubsts;
 
@@ -20,6 +21,7 @@ pub struct AscribeUserType<'tcx> {
     pub variance: ty::Variance,
     pub def_id: DefId,
     pub user_substs: UserSubsts<'tcx>,
+    pub projs: &'tcx ty::List<ProjectionKind<'tcx>>,
 }
 
 impl<'tcx> AscribeUserType<'tcx> {
@@ -28,8 +30,9 @@ impl<'tcx> AscribeUserType<'tcx> {
         variance: ty::Variance,
         def_id: DefId,
         user_substs: UserSubsts<'tcx>,
+        projs: &'tcx ty::List<ProjectionKind<'tcx>>,
     ) -> Self {
-        AscribeUserType { mir_ty, variance, def_id, user_substs }
+        AscribeUserType { mir_ty, variance, def_id, user_substs, projs }
     }
 }
 
@@ -59,19 +62,19 @@ impl<'gcx: 'tcx, 'tcx> super::QueryTypeOp<'gcx, 'tcx> for AscribeUserType<'tcx>
 
 BraceStructTypeFoldableImpl! {
     impl<'tcx> TypeFoldable<'tcx> for AscribeUserType<'tcx> {
-        mir_ty, variance, def_id, user_substs
+        mir_ty, variance, def_id, user_substs, projs
     }
 }
 
 BraceStructLiftImpl! {
     impl<'a, 'tcx> Lift<'tcx> for AscribeUserType<'a> {
         type Lifted = AscribeUserType<'tcx>;
-        mir_ty, variance, def_id, user_substs
+        mir_ty, variance, def_id, user_substs, projs
     }
 }
 
 impl_stable_hash_for! {
     struct AscribeUserType<'tcx> {
-        mir_ty, variance, def_id, user_substs
+        mir_ty, variance, def_id, user_substs, projs
     }
 }
diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs
index 1686e3e0e0c..d4b47db6081 100644
--- a/src/librustc/ty/context.rs
+++ b/src/librustc/ty/context.rs
@@ -31,7 +31,7 @@ use middle::cstore::EncodedMetadata;
 use middle::lang_items;
 use middle::resolve_lifetime::{self, ObjectLifetimeDefault};
 use middle::stability;
-use mir::{self, Mir, interpret};
+use mir::{self, Mir, interpret, ProjectionKind};
 use mir::interpret::Allocation;
 use ty::subst::{CanonicalUserSubsts, Kind, Substs, Subst};
 use ty::ReprOptions;
@@ -132,6 +132,7 @@ pub struct CtxtInterners<'tcx> {
     clauses: InternedSet<'tcx, List<Clause<'tcx>>>,
     goal: InternedSet<'tcx, GoalKind<'tcx>>,
     goal_list: InternedSet<'tcx, List<Goal<'tcx>>>,
+    projs: InternedSet<'tcx, List<ProjectionKind<'tcx>>>,
 }
 
 impl<'gcx: 'tcx, 'tcx> CtxtInterners<'tcx> {
@@ -149,6 +150,7 @@ impl<'gcx: 'tcx, 'tcx> CtxtInterners<'tcx> {
             clauses: Default::default(),
             goal: Default::default(),
             goal_list: Default::default(),
+            projs: Default::default(),
         }
     }
 
@@ -1886,6 +1888,24 @@ impl<'a, 'tcx> Lift<'tcx> for &'a List<CanonicalVarInfo> {
     }
 }
 
+impl<'a, 'tcx> Lift<'tcx> for &'a List<ProjectionKind<'a>> {
+    type Lifted = &'tcx List<ProjectionKind<'tcx>>;
+    fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<Self::Lifted> {
+        if self.len() == 0 {
+            return Some(List::empty());
+        }
+        if tcx.interners.arena.in_arena(*self as *const _) {
+            return Some(unsafe { mem::transmute(*self) });
+        }
+        // Also try in the global tcx if we're not that.
+        if !tcx.is_global() {
+            self.lift_to_tcx(tcx.global_tcx())
+        } else {
+            None
+        }
+    }
+}
+
 pub mod tls {
     use super::{GlobalCtxt, TyCtxt};
 
@@ -2294,6 +2314,13 @@ impl<'tcx: 'lcx, 'lcx> Borrow<[Kind<'lcx>]> for Interned<'tcx, Substs<'tcx>> {
     }
 }
 
+impl<'tcx: 'lcx, 'lcx> Borrow<[ProjectionKind<'lcx>]>
+    for Interned<'tcx, List<ProjectionKind<'tcx>>> {
+    fn borrow<'a>(&'a self) -> &'a [ProjectionKind<'lcx>] {
+        &self.0[..]
+    }
+}
+
 impl<'tcx> Borrow<RegionKind> for Interned<'tcx, RegionKind> {
     fn borrow<'a>(&'a self) -> &'a RegionKind {
         &self.0
@@ -2441,7 +2468,8 @@ slice_interners!(
     type_list: _intern_type_list(Ty),
     substs: _intern_substs(Kind),
     clauses: _intern_clauses(Clause),
-    goal_list: _intern_goals(Goal)
+    goal_list: _intern_goals(Goal),
+    projs: _intern_projs(ProjectionKind)
 );
 
 // This isn't a perfect fit: CanonicalVarInfo slices are always
@@ -2743,6 +2771,14 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
         }
     }
 
+    pub fn intern_projs(self, ps: &[ProjectionKind<'tcx>]) -> &'tcx List<ProjectionKind<'tcx>> {
+        if ps.len() == 0 {
+            List::empty()
+        } else {
+            self._intern_projs(ps)
+        }
+    }
+
     pub fn intern_canonical_var_infos(self, ts: &[CanonicalVarInfo]) -> CanonicalVarInfos<'gcx> {
         if ts.len() == 0 {
             List::empty()
diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs
index a93dca4af42..62827ea20c3 100644
--- a/src/librustc/ty/structural_impls.rs
+++ b/src/librustc/ty/structural_impls.rs
@@ -13,6 +13,7 @@
 //! hand, though we've recently added some macros (e.g.,
 //! `BraceStructLiftImpl!`) to help with the tedium.
 
+use mir::ProjectionKind;
 use mir::interpret::ConstValue;
 use ty::{self, Lift, Ty, TyCtxt};
 use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor};
@@ -628,6 +629,17 @@ impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List<Ty<'tcx>> {
     }
 }
 
+impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List<ProjectionKind<'tcx>> {
+    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
+        let v = self.iter().map(|t| t.fold_with(folder)).collect::<SmallVec<[_; 8]>>();
+        folder.tcx().intern_projs(&v)
+    }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        self.iter().any(|t| t.visit_with(visitor))
+    }
+}
+
 impl<'tcx> TypeFoldable<'tcx> for ty::instance::Instance<'tcx> {
     fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
         use ty::InstanceDef::*;
diff --git a/src/librustc_codegen_llvm/mir/analyze.rs b/src/librustc_codegen_llvm/mir/analyze.rs
index a0d6cc46295..1602ef3c5b7 100644
--- a/src/librustc_codegen_llvm/mir/analyze.rs
+++ b/src/librustc_codegen_llvm/mir/analyze.rs
@@ -168,7 +168,9 @@ impl Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'll, 'tcx> {
                 let base_ty = self.fx.monomorphize(&base_ty);
 
                 // ZSTs don't require any actual memory access.
-                let elem_ty = base_ty.projection_ty(cx.tcx, &proj.elem).to_ty(cx.tcx);
+                let elem_ty = base_ty
+                    .projection_ty(cx.tcx, &proj.elem)
+                    .to_ty(cx.tcx);
                 let elem_ty = self.fx.monomorphize(&elem_ty);
                 if cx.layout_of(elem_ty).is_zst() {
                     return;
diff --git a/src/librustc_codegen_llvm/mir/place.rs b/src/librustc_codegen_llvm/mir/place.rs
index e7b6f5908a4..062f7174680 100644
--- a/src/librustc_codegen_llvm/mir/place.rs
+++ b/src/librustc_codegen_llvm/mir/place.rs
@@ -517,7 +517,8 @@ impl FunctionCx<'a, 'll, 'tcx> {
                         let mut subslice = cg_base.project_index(bx,
                             C_usize(bx.cx, from as u64));
                         let projected_ty = PlaceTy::Ty { ty: cg_base.layout.ty }
-                            .projection_ty(tcx, &projection.elem).to_ty(bx.tcx());
+                            .projection_ty(tcx, &projection.elem)
+                            .to_ty(bx.tcx());
                         subslice.layout = bx.cx.layout_of(self.monomorphize(&projected_ty));
 
                         if subslice.layout.is_unsized() {
diff --git a/src/librustc_mir/borrow_check/nll/constraint_generation.rs b/src/librustc_mir/borrow_check/nll/constraint_generation.rs
index 495e84528a3..1d8d028137a 100644
--- a/src/librustc_mir/borrow_check/nll/constraint_generation.rs
+++ b/src/librustc_mir/borrow_check/nll/constraint_generation.rs
@@ -18,7 +18,7 @@ use rustc::mir::visit::TyContext;
 use rustc::mir::visit::Visitor;
 use rustc::mir::{BasicBlock, BasicBlockData, Location, Mir, Place, Rvalue};
 use rustc::mir::{Statement, Terminator};
-use rustc::mir::UserTypeAnnotation;
+use rustc::mir::UserTypeProjection;
 use rustc::ty::fold::TypeFoldable;
 use rustc::ty::subst::Substs;
 use rustc::ty::{self, ClosureSubsts, GeneratorSubsts, RegionVid};
@@ -183,7 +183,7 @@ impl<'cg, 'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'gcx
         &mut self,
         _place: &Place<'tcx>,
         _variance: &ty::Variance,
-        _user_ty: &UserTypeAnnotation<'tcx>,
+        _user_ty: &UserTypeProjection<'tcx>,
         _location: Location,
     ) {
     }
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 ff4651dfb45..953fe0c9521 100644
--- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs
@@ -284,7 +284,7 @@ impl<'a, 'b, 'gcx, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'gcx, 'tcx> {
             if let Err(terr) = self.cx.relate_type_and_user_type(
                 constant.ty,
                 ty::Variance::Invariant,
-                user_ty,
+                &UserTypeProjection { base: user_ty, projs: vec![], },
                 location.to_locations(),
                 ConstraintCategory::Boring,
             ) {
@@ -310,12 +310,12 @@ impl<'a, 'b, 'gcx, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'gcx, 'tcx> {
         self.super_local_decl(local, local_decl);
         self.sanitize_type(local_decl, local_decl.ty);
 
-        if let Some((user_ty, span)) = local_decl.user_ty {
+        for (user_ty, span) in local_decl.user_ty.projections_and_spans() {
             if let Err(terr) = self.cx.relate_type_and_user_type(
                 local_decl.ty,
                 ty::Variance::Invariant,
                 user_ty,
-                Locations::All(span),
+                Locations::All(*span),
                 ConstraintCategory::TypeAnnotation,
             ) {
                 span_mirbug!(
@@ -971,7 +971,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
         &mut self,
         a: Ty<'tcx>,
         v: ty::Variance,
-        user_ty: UserTypeAnnotation<'tcx>,
+        user_ty: &UserTypeProjection<'tcx>,
         locations: Locations,
         category: ConstraintCategory,
     ) -> Fallible<()> {
@@ -980,7 +980,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
             a, v, user_ty, locations,
         );
 
-        match user_ty {
+        match user_ty.base {
             UserTypeAnnotation::Ty(canonical_ty) => {
                 let (ty, _) = self.infcx
                     .instantiate_canonical_with_fresh_inference_vars(DUMMY_SP, &canonical_ty);
@@ -990,6 +990,20 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
                 // ambient variance to get the right relationship.
                 let v1 = ty::Contravariant.xform(v);
 
+                let tcx = self.infcx.tcx;
+                let mut projected_ty = PlaceTy::from_ty(ty);
+                for proj in &user_ty.projs {
+                    projected_ty = projected_ty.projection_ty_core(
+                        tcx, proj, |this, field, &()| {
+                            let ty = this.field_ty(tcx, field);
+                            self.normalize(ty, locations)
+                        });
+                }
+                debug!("user_ty base: {:?} freshened: {:?} projs: {:?} yields: {:?}",
+                       user_ty.base, ty, user_ty.projs, projected_ty);
+
+                let ty = projected_ty.to_ty(tcx);
+
                 self.relate_types(ty, v1, a, locations, category)?;
             }
             UserTypeAnnotation::TypeOf(def_id, canonical_substs) => {
@@ -999,11 +1013,12 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
                 ) = self.infcx
                     .instantiate_canonical_with_fresh_inference_vars(DUMMY_SP, &canonical_substs);
 
+                let projs = self.infcx.tcx.intern_projs(&user_ty.projs);
                 self.fully_perform_op(
                     locations,
                     category,
                     self.param_env.and(type_op::ascribe_user_type::AscribeUserType::new(
-                        a, v, def_id, user_substs,
+                        a, v, def_id, user_substs, projs,
                     )),
                 )?;
             }
@@ -1172,7 +1187,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
                     if let Err(terr) = self.relate_type_and_user_type(
                         rv_ty,
                         ty::Variance::Invariant,
-                        user_ty,
+                        &UserTypeProjection { base: user_ty, projs: vec![], },
                         location.to_locations(),
                         ConstraintCategory::Boring,
                     ) {
@@ -1225,7 +1240,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
                     );
                 };
             }
-            StatementKind::AscribeUserType(ref place, variance, box c_ty) => {
+            StatementKind::AscribeUserType(ref place, variance, box ref c_ty) => {
                 let place_ty = place.ty(mir, tcx).to_ty(tcx);
                 if let Err(terr) = self.relate_type_and_user_type(
                     place_ty,
diff --git a/src/librustc_mir/build/block.rs b/src/librustc_mir/build/block.rs
index b754d63f718..aa383a123b6 100644
--- a/src/librustc_mir/build/block.rs
+++ b/src/librustc_mir/build/block.rs
@@ -151,10 +151,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                             None, remainder_span, lint_level, slice::from_ref(&pattern),
                             ArmHasGuard(false), None);
 
-                        this.visit_bindings(&pattern, None, &mut |this, _, _, _, node, span, _, _| {
-                            this.storage_live_binding(block, node, span, OutsideGuard);
-                            this.schedule_drop_for_binding(node, span, OutsideGuard);
-                        })
+                        this.visit_bindings(
+                            &pattern,
+                            &PatternTypeProjections::none(),
+                            &mut |this, _, _, _, node, span, _, _| {
+                                this.storage_live_binding(block, node, span, OutsideGuard);
+                                this.schedule_drop_for_binding(node, span, OutsideGuard);
+                            })
                     }
 
                     // Enter the source scope, after evaluating the initializer.
diff --git a/src/librustc_mir/build/expr/as_place.rs b/src/librustc_mir/build/expr/as_place.rs
index 8d6b6bb5c74..77746e5538d 100644
--- a/src/librustc_mir/build/expr/as_place.rs
+++ b/src/librustc_mir/build/expr/as_place.rs
@@ -147,7 +147,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                             kind: StatementKind::AscribeUserType(
                                 place.clone(),
                                 Variance::Invariant,
-                                box user_ty,
+                                box UserTypeProjection { base: user_ty, projs: vec![], },
                             ),
                         },
                     );
@@ -167,7 +167,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                             kind: StatementKind::AscribeUserType(
                                 Place::Local(temp.clone()),
                                 Variance::Invariant,
-                                box user_ty,
+                                box UserTypeProjection { base: user_ty, projs: vec![], },
                             ),
                         },
                     );
diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs
index 4f5ed34a461..d2913872fca 100644
--- a/src/librustc_mir/build/expr/into.rs
+++ b/src/librustc_mir/build/expr/into.rs
@@ -306,7 +306,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                     let ptr_temp = this.local_decls.push(LocalDecl {
                         mutability: Mutability::Mut,
                         ty: ptr_ty,
-                        user_ty: None,
+                        user_ty: UserTypeProjections::none(),
                         name: None,
                         source_info,
                         visibility_scope: source_info.scope,
diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs
index 2f271e072c6..b92f270255a 100644
--- a/src/librustc_mir/build/matches/mod.rs
+++ b/src/librustc_mir/build/matches/mod.rs
@@ -18,6 +18,7 @@ use build::ForGuard::{self, OutsideGuard, RefWithinGuard, ValWithinGuard};
 use build::{BlockAnd, BlockAndExtension, Builder};
 use build::{GuardFrame, GuardFrameLocal, LocalsForNode};
 use hair::*;
+use hair::pattern::PatternTypeProjections;
 use rustc::hir;
 use rustc::mir::*;
 use rustc::ty::{self, Ty};
@@ -31,6 +32,8 @@ mod simplify;
 mod test;
 mod util;
 
+use std::convert::TryFrom;
+
 /// ArmHasGuard is isomorphic to a boolean flag. It indicates whether
 /// a match arm has a guard expression attached to it.
 #[derive(Copy, Clone, Debug)]
@@ -240,7 +243,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         end_block.unit()
     }
 
-    pub fn expr_into_pattern(
+    pub(super) fn expr_into_pattern(
         &mut self,
         mut block: BasicBlock,
         irrefutable_pat: Pattern<'tcx>,
@@ -291,7 +294,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                     },
                     ..
                 },
-                user_ty: ascription_user_ty,
+                user_ty: pat_ascription_ty,
                 user_ty_span,
             } => {
                 let place =
@@ -316,7 +319,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                         kind: StatementKind::AscribeUserType(
                             place,
                             ty::Variance::Invariant,
-                            box ascription_user_ty,
+                            box pat_ascription_ty.user_ty(),
                         ),
                     },
                 );
@@ -415,7 +418,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         let num_patterns = patterns.len();
         self.visit_bindings(
             &patterns[0],
-            None,
+            &PatternTypeProjections::none(),
             &mut |this, mutability, name, mode, var, span, ty, user_ty| {
                 if visibility_scope.is_none() {
                     visibility_scope =
@@ -488,10 +491,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         );
     }
 
-    pub fn visit_bindings(
+    pub(super) fn visit_bindings(
         &mut self,
         pattern: &Pattern<'tcx>,
-        mut pattern_user_ty: Option<(UserTypeAnnotation<'tcx>, Span)>,
+        pattern_user_ty: &PatternTypeProjections<'tcx>,
         f: &mut impl FnMut(
             &mut Self,
             Mutability,
@@ -500,7 +503,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             NodeId,
             Span,
             Ty<'tcx>,
-            Option<(UserTypeAnnotation<'tcx>, Span)>,
+            &PatternTypeProjections<'tcx>,
         ),
     ) {
         match *pattern.kind {
@@ -513,20 +516,19 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 ref subpattern,
                 ..
             } => {
-                match mode {
-                    BindingMode::ByValue => { }
+                let pattern_ref_binding; // sidestep temp lifetime limitations.
+                let binding_user_ty = match mode {
+                    BindingMode::ByValue => { pattern_user_ty }
                     BindingMode::ByRef(..) => {
                         // If this is a `ref` binding (e.g., `let ref
                         // x: T = ..`), then the type of `x` is not
-                        // `T` but rather `&T`, so ignore
-                        // `pattern_user_ty` for now.
-                        //
-                        // FIXME(#47184): extract or handle `pattern_user_ty` somehow
-                        pattern_user_ty = None;
+                        // `T` but rather `&T`.
+                        pattern_ref_binding = pattern_user_ty.ref_binding();
+                        &pattern_ref_binding
                     }
-                }
+                };
 
-                f(self, mutability, name, mode, var, pattern.span, ty, pattern_user_ty);
+                f(self, mutability, name, mode, var, pattern.span, ty, binding_user_ty);
                 if let Some(subpattern) = subpattern.as_ref() {
                     self.visit_bindings(subpattern, pattern_user_ty, f);
                 }
@@ -541,33 +543,44 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 ref slice,
                 ref suffix,
             } => {
-                // FIXME(#47184): extract or handle `pattern_user_ty` somehow
-                for subpattern in prefix.iter().chain(slice).chain(suffix) {
-                    self.visit_bindings(subpattern, None, f);
+                let from = u32::try_from(prefix.len()).unwrap();
+                let to = u32::try_from(suffix.len()).unwrap();
+                for subpattern in prefix {
+                    self.visit_bindings(subpattern, &pattern_user_ty.index(), f);
+                }
+                for subpattern in slice {
+                    self.visit_bindings(subpattern, &pattern_user_ty.subslice(from, to), f);
+                }
+                for subpattern in suffix {
+                    self.visit_bindings(subpattern, &pattern_user_ty.index(), f);
                 }
             }
             PatternKind::Constant { .. } | PatternKind::Range { .. } | PatternKind::Wild => {}
             PatternKind::Deref { ref subpattern } => {
-                // FIXME(#47184): extract or handle `pattern_user_ty` somehow
-                self.visit_bindings(subpattern, None, f);
+                self.visit_bindings(subpattern, &pattern_user_ty.deref(), f);
             }
-            PatternKind::AscribeUserType { ref subpattern, user_ty, user_ty_span } => {
+            PatternKind::AscribeUserType { ref subpattern, ref user_ty, user_ty_span } => {
                 // This corresponds to something like
                 //
                 // ```
                 // let A::<'a>(_): A<'static> = ...;
                 // ```
-                //
-                // FIXME(#47184): handle `pattern_user_ty` somehow
-                self.visit_bindings(subpattern, Some((user_ty, user_ty_span)), f)
+                let subpattern_user_ty = pattern_user_ty.add_user_type(user_ty, user_ty_span);
+                self.visit_bindings(subpattern, &subpattern_user_ty, f)
             }
-            PatternKind::Leaf { ref subpatterns }
-            | PatternKind::Variant {
-                ref subpatterns, ..
-            } => {
-                // FIXME(#47184): extract or handle `pattern_user_ty` somehow
+
+            PatternKind::Leaf { ref subpatterns } => {
+                for subpattern in subpatterns {
+                    let subpattern_user_ty = pattern_user_ty.leaf(subpattern.field);
+                    self.visit_bindings(&subpattern.pattern, &subpattern_user_ty, f);
+                }
+            }
+
+            PatternKind::Variant { adt_def, substs: _, variant_index, ref subpatterns } => {
                 for subpattern in subpatterns {
-                    self.visit_bindings(&subpattern.pattern, None, f);
+                    let subpattern_user_ty = pattern_user_ty.variant(
+                        adt_def, variant_index, subpattern.field);
+                    self.visit_bindings(&subpattern.pattern, &subpattern_user_ty, f);
                 }
             }
         }
@@ -626,7 +639,7 @@ struct Binding<'tcx> {
 struct Ascription<'tcx> {
     span: Span,
     source: Place<'tcx>,
-    user_ty: UserTypeAnnotation<'tcx>,
+    user_ty: PatternTypeProjection<'tcx>,
 }
 
 #[derive(Clone, Debug)]
@@ -1323,7 +1336,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                     kind: StatementKind::AscribeUserType(
                         ascription.source.clone(),
                         ty::Variance::Covariant,
-                        box ascription.user_ty,
+                        box ascription.user_ty.clone().user_ty(),
                     ),
                 },
             );
@@ -1470,7 +1483,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         num_patterns: usize,
         var_id: NodeId,
         var_ty: Ty<'tcx>,
-        user_var_ty: Option<(UserTypeAnnotation<'tcx>, Span)>,
+        user_var_ty: &PatternTypeProjections<'tcx>,
         has_guard: ArmHasGuard,
         opt_match_place: Option<(Option<Place<'tcx>>, Span)>,
         pat_span: Span,
@@ -1489,7 +1502,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         let local = LocalDecl::<'tcx> {
             mutability,
             ty: var_ty,
-            user_ty: user_var_ty,
+            user_ty: user_var_ty.clone().user_ty(),
             name: Some(name),
             source_info,
             visibility_scope,
@@ -1522,7 +1535,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 // See previous comment.
                 mutability: Mutability::Not,
                 ty: tcx.mk_imm_ref(tcx.types.re_empty, var_ty),
-                user_ty: None,
+                user_ty: UserTypeProjections::none(),
                 name: Some(name),
                 source_info,
                 visibility_scope,
diff --git a/src/librustc_mir/build/matches/simplify.rs b/src/librustc_mir/build/matches/simplify.rs
index 494e7c03c3e..349d877d524 100644
--- a/src/librustc_mir/build/matches/simplify.rs
+++ b/src/librustc_mir/build/matches/simplify.rs
@@ -63,10 +63,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                                  candidate: &mut Candidate<'pat, 'tcx>)
                                  -> Result<(), MatchPair<'pat, 'tcx>> {
         match *match_pair.pattern.kind {
-            PatternKind::AscribeUserType { ref subpattern, user_ty, user_ty_span } => {
+            PatternKind::AscribeUserType { ref subpattern, ref user_ty, user_ty_span } => {
                 candidate.ascriptions.push(Ascription {
                     span: user_ty_span,
-                    user_ty,
+                    user_ty: user_ty.clone(),
                     source: match_pair.place.clone(),
                 });
 
diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs
index 6ea4628de24..5b4001f0652 100644
--- a/src/librustc_mir/build/mod.rs
+++ b/src/librustc_mir/build/mod.rs
@@ -845,7 +845,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             self.local_decls.push(LocalDecl {
                 mutability: Mutability::Mut,
                 ty,
-                user_ty: None,
+                user_ty: UserTypeProjections::none(),
                 source_info,
                 visibility_scope: source_info.scope,
                 name,
diff --git a/src/librustc_mir/hair/cx/block.rs b/src/librustc_mir/hair/cx/block.rs
index 9865867a196..586d6d87fa0 100644
--- a/src/librustc_mir/hair/cx/block.rs
+++ b/src/librustc_mir/hair/cx/block.rs
@@ -91,7 +91,7 @@ fn mirror_stmts<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
                                     ty: pattern.ty,
                                     span: pattern.span,
                                     kind: Box::new(PatternKind::AscribeUserType {
-                                        user_ty: UserTypeAnnotation::Ty(user_ty),
+                                        user_ty: PatternTypeProjection::from_canonical_ty(user_ty),
                                         user_ty_span: ty.span,
                                         subpattern: pattern
                                     })
diff --git a/src/librustc_mir/hair/mod.rs b/src/librustc_mir/hair/mod.rs
index 788db5c0b7e..8a24851de81 100644
--- a/src/librustc_mir/hair/mod.rs
+++ b/src/librustc_mir/hair/mod.rs
@@ -28,6 +28,7 @@ pub mod cx;
 
 pub mod pattern;
 pub use self::pattern::{BindingMode, Pattern, PatternKind, FieldPattern};
+pub(crate) use self::pattern::{PatternTypeProjection, PatternTypeProjections};
 
 mod util;
 
diff --git a/src/librustc_mir/hair/pattern/mod.rs b/src/librustc_mir/hair/pattern/mod.rs
index 4649c28aff5..bff87da9c77 100644
--- a/src/librustc_mir/hair/pattern/mod.rs
+++ b/src/librustc_mir/hair/pattern/mod.rs
@@ -20,7 +20,8 @@ use const_eval::{const_field, const_variant_index};
 
 use hair::util::UserAnnotatedTyHelpers;
 
-use rustc::mir::{fmt_const_val, Field, BorrowKind, Mutability, UserTypeAnnotation};
+use rustc::mir::{fmt_const_val, Field, BorrowKind, Mutability};
+use rustc::mir::{ProjectionElem, UserTypeAnnotation, UserTypeProjection, UserTypeProjections};
 use rustc::mir::interpret::{Scalar, GlobalId, ConstValue, sign_extend};
 use rustc::ty::{self, Region, TyCtxt, AdtDef, Ty};
 use rustc::ty::subst::{Substs, Kind};
@@ -64,12 +65,123 @@ pub struct Pattern<'tcx> {
     pub kind: Box<PatternKind<'tcx>>,
 }
 
+
+#[derive(Clone, Debug)]
+pub(crate) struct PatternTypeProjections<'tcx> {
+    contents: Vec<(PatternTypeProjection<'tcx>, Span)>,
+}
+
+impl<'tcx> PatternTypeProjections<'tcx> {
+    pub(crate) fn user_ty(self) -> UserTypeProjections<'tcx> {
+        UserTypeProjections::from_projections(
+            self.contents.into_iter().map(|(pat_ty_proj, span)| (pat_ty_proj.user_ty(), span)))
+    }
+
+    pub(crate) fn none() -> Self {
+        PatternTypeProjections { contents: vec![] }
+    }
+
+    pub(crate) fn ref_binding(&self) -> Self {
+        // FIXME(#47184): ignore for now
+        PatternTypeProjections { contents: vec![] }
+    }
+
+    fn map_projs(&self,
+                 mut f: impl FnMut(&PatternTypeProjection<'tcx>) -> PatternTypeProjection<'tcx>)
+                 -> Self
+    {
+        PatternTypeProjections {
+            contents: self.contents
+                .iter()
+                .map(|(proj, span)| (f(proj), *span))
+                .collect(), }
+    }
+
+    pub(crate) fn index(&self) -> Self { self.map_projs(|pat_ty_proj| pat_ty_proj.index()) }
+
+    pub(crate) fn subslice(&self, from: u32, to: u32) -> Self {
+        self.map_projs(|pat_ty_proj| pat_ty_proj.subslice(from, to))
+    }
+
+    pub(crate) fn deref(&self) -> Self { self.map_projs(|pat_ty_proj| pat_ty_proj.deref()) }
+
+    pub(crate) fn leaf(&self, field: Field) -> Self {
+        self.map_projs(|pat_ty_proj| pat_ty_proj.leaf(field))
+    }
+
+    pub(crate) fn variant(&self,
+                          adt_def: &'tcx AdtDef,
+                          variant_index: usize,
+                          field: Field) -> Self {
+        self.map_projs(|pat_ty_proj| pat_ty_proj.variant(adt_def, variant_index, field))
+    }
+
+    pub(crate) fn add_user_type(&self, user_ty: &PatternTypeProjection<'tcx>, sp: Span) -> Self {
+        let mut new = self.clone();
+        new.contents.push((user_ty.clone(), sp));
+        new
+    }
+}
+
+#[derive(Clone, Debug)]
+pub struct PatternTypeProjection<'tcx>(UserTypeProjection<'tcx>);
+
+impl<'tcx> PatternTypeProjection<'tcx> {
+    pub(crate) fn index(&self) -> Self {
+        let mut new = self.clone();
+        new.0.projs.push(ProjectionElem::Index(()));
+        new
+    }
+
+    pub(crate) fn subslice(&self, from: u32, to: u32) -> Self {
+        let mut new = self.clone();
+        new.0.projs.push(ProjectionElem::Subslice { from, to });
+        new
+    }
+
+    pub(crate) fn deref(&self) -> Self {
+        let mut new = self.clone();
+        new.0.projs.push(ProjectionElem::Deref);
+        new
+    }
+
+    pub(crate) fn leaf(&self, field: Field) -> Self {
+        let mut new = self.clone();
+        new.0.projs.push(ProjectionElem::Field(field, ()));
+        new
+    }
+
+    pub(crate) fn variant(&self,
+                          adt_def: &'tcx AdtDef,
+                          variant_index: usize,
+                          field: Field) -> Self {
+        let mut new = self.clone();
+        new.0.projs.push(ProjectionElem::Downcast(adt_def, variant_index));
+        new.0.projs.push(ProjectionElem::Field(field, ()));
+        new
+    }
+
+    pub(crate) fn from_canonical_ty(c_ty: ty::CanonicalTy<'tcx>) -> Self {
+        Self::from_user_type(UserTypeAnnotation::Ty(c_ty))
+    }
+
+    pub(crate) fn from_user_type(u_ty: UserTypeAnnotation<'tcx>) -> Self {
+        Self::from_user_type_proj(UserTypeProjection { base: u_ty, projs: vec![], })
+    }
+
+    pub(crate) fn from_user_type_proj(u_ty: UserTypeProjection<'tcx>) -> Self {
+        PatternTypeProjection(u_ty)
+    }
+
+    pub(crate) fn user_ty(self) -> UserTypeProjection<'tcx> { self.0 }
+}
+
 #[derive(Clone, Debug)]
 pub enum PatternKind<'tcx> {
     Wild,
 
     AscribeUserType {
-        user_ty: UserTypeAnnotation<'tcx>,
+        user_ty: PatternTypeProjection<'tcx>,
         subpattern: Pattern<'tcx>,
         user_ty_span: Span,
     },
@@ -690,9 +802,10 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
 
             debug!("pattern user_ty = {:?} for pattern at {:?}", user_ty, span);
 
+            let pat_ty = PatternTypeProjection::from_user_type(user_ty);
             kind = PatternKind::AscribeUserType {
                 subpattern,
-                user_ty,
+                user_ty: pat_ty,
                 user_ty_span: span,
             };
         }
@@ -980,7 +1093,8 @@ macro_rules! CloneImpls {
 CloneImpls!{ <'tcx>
     Span, Field, Mutability, ast::Name, ast::NodeId, usize, &'tcx ty::Const<'tcx>,
     Region<'tcx>, Ty<'tcx>, BindingMode<'tcx>, &'tcx AdtDef,
-    &'tcx Substs<'tcx>, &'tcx Kind<'tcx>, UserTypeAnnotation<'tcx>
+    &'tcx Substs<'tcx>, &'tcx Kind<'tcx>, UserTypeAnnotation<'tcx>,
+    UserTypeProjection<'tcx>, PatternTypeProjection<'tcx>
 }
 
 impl<'tcx> PatternFoldable<'tcx> for FieldPattern<'tcx> {
@@ -1016,7 +1130,7 @@ impl<'tcx> PatternFoldable<'tcx> for PatternKind<'tcx> {
             PatternKind::Wild => PatternKind::Wild,
             PatternKind::AscribeUserType {
                 ref subpattern,
-                user_ty,
+                ref user_ty,
                 user_ty_span,
             } => PatternKind::AscribeUserType {
                 subpattern: subpattern.fold_with(folder),
diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs
index d8f627fcf4d..6c32690cdb3 100644
--- a/src/librustc_mir/shim.rs
+++ b/src/librustc_mir/shim.rs
@@ -142,7 +142,7 @@ fn temp_decl(mutability: Mutability, ty: Ty, span: Span) -> LocalDecl {
     LocalDecl {
         mutability,
         ty,
-        user_ty: None,
+        user_ty: UserTypeProjections::none(),
         name: None,
         source_info,
         visibility_scope: source_info.scope,
diff --git a/src/librustc_mir/transform/generator.rs b/src/librustc_mir/transform/generator.rs
index c2ae6832cc0..5889fabee9d 100644
--- a/src/librustc_mir/transform/generator.rs
+++ b/src/librustc_mir/transform/generator.rs
@@ -303,7 +303,7 @@ fn replace_result_variable<'tcx>(
     let new_ret = LocalDecl {
         mutability: Mutability::Mut,
         ty: ret_ty,
-        user_ty: None,
+        user_ty: UserTypeProjections::none(),
         name: None,
         source_info,
         visibility_scope: source_info.scope,
@@ -658,7 +658,7 @@ fn create_generator_drop_shim<'a, 'tcx>(
     mir.local_decls[RETURN_PLACE] = LocalDecl {
         mutability: Mutability::Mut,
         ty: tcx.mk_unit(),
-        user_ty: None,
+        user_ty: UserTypeProjections::none(),
         name: None,
         source_info,
         visibility_scope: source_info.scope,
@@ -676,7 +676,7 @@ fn create_generator_drop_shim<'a, 'tcx>(
             ty: gen_ty,
             mutbl: hir::Mutability::MutMutable,
         }),
-        user_ty: None,
+        user_ty: UserTypeProjections::none(),
         name: None,
         source_info,
         visibility_scope: source_info.scope,
diff --git a/src/librustc_mir/util/pretty.rs b/src/librustc_mir/util/pretty.rs
index 25bd02ff6dc..c74492fe649 100644
--- a/src/librustc_mir/util/pretty.rs
+++ b/src/librustc_mir/util/pretty.rs
@@ -502,7 +502,7 @@ fn write_scope_tree(
                 local,
                 var.ty
             );
-            if let Some(user_ty) = var.user_ty {
+            for user_ty in var.user_ty.projections() {
                 write!(indented_var, " as {:?}", user_ty).unwrap();
             }
             indented_var.push_str(";");
diff --git a/src/librustc_passes/mir_stats.rs b/src/librustc_passes/mir_stats.rs
index 0b9b20d3c45..06c8545aacf 100644
--- a/src/librustc_passes/mir_stats.rs
+++ b/src/librustc_passes/mir_stats.rs
@@ -211,7 +211,6 @@ impl<'a, 'tcx> mir_visit::Visitor<'tcx> for StatCollector<'a, 'tcx> {
 
     fn visit_projection_elem(&mut self,
                              place: &PlaceElem<'tcx>,
-                             context: mir_visit::PlaceContext<'tcx>,
                              location: Location) {
         self.record("PlaceElem", place);
         self.record(match *place {
@@ -222,7 +221,7 @@ impl<'a, 'tcx> mir_visit::Visitor<'tcx> for StatCollector<'a, 'tcx> {
             ProjectionElem::ConstantIndex { .. } => "PlaceElem::ConstantIndex",
             ProjectionElem::Downcast(..) => "PlaceElem::Downcast",
         }, place);
-        self.super_projection_elem(place, context, location);
+        self.super_projection_elem(place, location);
     }
 
     fn visit_constant(&mut self, constant: &Constant<'tcx>, location: Location) {
diff --git a/src/librustc_traits/type_op.rs b/src/librustc_traits/type_op.rs
index cf274a9c851..2ed02a4cdab 100644
--- a/src/librustc_traits/type_op.rs
+++ b/src/librustc_traits/type_op.rs
@@ -12,6 +12,8 @@ use rustc::infer::at::ToTrace;
 use rustc::infer::canonical::{Canonical, QueryResponse};
 use rustc::infer::InferCtxt;
 use rustc::hir::def_id::DefId;
+use rustc::mir::ProjectionKind;
+use rustc::mir::tcx::PlaceTy;
 use rustc::traits::query::type_op::ascribe_user_type::AscribeUserType;
 use rustc::traits::query::type_op::eq::Eq;
 use rustc::traits::query::type_op::normalize::Normalize;
@@ -58,14 +60,15 @@ fn type_op_ascribe_user_type<'tcx>(
                     variance,
                     def_id,
                     user_substs,
+                    projs,
                 },
             ) = key.into_parts();
 
             debug!(
                 "type_op_ascribe_user_type(\
-                 mir_ty={:?}, variance={:?}, def_id={:?}, user_substs={:?}\
+                 mir_ty={:?}, variance={:?}, def_id={:?}, user_substs={:?}, projs={:?}\
                  )",
-                mir_ty, variance, def_id, user_substs,
+                mir_ty, variance, def_id, user_substs, projs,
             );
 
             let mut cx = AscribeUserTypeCx {
@@ -73,7 +76,7 @@ fn type_op_ascribe_user_type<'tcx>(
                 param_env,
                 fulfill_cx,
             };
-            cx.relate_mir_and_user_ty(mir_ty, variance, def_id, user_substs)?;
+            cx.relate_mir_and_user_ty(mir_ty, variance, def_id, user_substs, projs)?;
 
             Ok(())
         })
@@ -134,17 +137,30 @@ impl AscribeUserTypeCx<'me, 'gcx, 'tcx> {
         variance: Variance,
         def_id: DefId,
         user_substs: UserSubsts<'tcx>,
+        projs: &[ProjectionKind<'tcx>],
     ) -> Result<(), NoSolution> {
         let UserSubsts {
             substs,
             user_self_ty,
         } = user_substs;
 
-        let ty = self.tcx().type_of(def_id);
+        let tcx = self.tcx();
+
+        let ty = tcx.type_of(def_id);
         let ty = self.subst(ty, substs);
         debug!("relate_type_and_user_type: ty of def-id is {:?}", ty);
         let ty = self.normalize(ty);
 
+        let mut projected_ty = PlaceTy::from_ty(ty);
+        for proj in projs {
+            projected_ty = projected_ty.projection_ty_core(
+                tcx, proj, |this, field, &()| {
+                    let ty = this.field_ty(tcx, field);
+                    self.normalize(ty)
+                });
+        }
+        let ty = projected_ty.to_ty(tcx);
+
         self.relate(mir_ty, variance, ty)?;
 
         if let Some(UserSelfTy {
diff --git a/src/test/mir-opt/basic_assignment.rs b/src/test/mir-opt/basic_assignment.rs
index 64e574fa8ae..7ca1d01f20b 100644
--- a/src/test/mir-opt/basic_assignment.rs
+++ b/src/test/mir-opt/basic_assignment.rs
@@ -56,7 +56,7 @@ fn main() {
 //        StorageLive(_4);
 //        _4 = std::option::Option<std::boxed::Box<u32>>::None;
 //        FakeRead(ForLet, _4);
-//        AscribeUserType(_4, o, Ty(Canonical { variables: [], value: std::option::Option<std::boxed::Box<u32>> }));
+//        AscribeUserType(_4, o, UserTypeProjection { base: Ty(Canonical { variables: [], value: std::option::Option<std::boxed::Box<u32>> }), projs: [] });
 //        StorageLive(_5);
 //        StorageLive(_6);
 //        _6 = move _4;
diff --git a/src/test/ui/nll/user-annotations/issue-54570-bootstrapping.rs b/src/test/ui/nll/user-annotations/issue-54570-bootstrapping.rs
new file mode 100644
index 00000000000..0a8a6793dc7
--- /dev/null
+++ b/src/test/ui/nll/user-annotations/issue-54570-bootstrapping.rs
@@ -0,0 +1,31 @@
+// compile-pass
+#![feature(nll)]
+
+// This test is reduced from a scenario pnkfelix encountered while
+// bootstrapping the compiler.
+
+#[derive(Copy, Clone)]
+pub struct Spanned<T> {
+    pub node: T,
+    pub span: Span,
+}
+
+pub type Variant = Spanned<VariantKind>;
+// #[derive(Clone)] pub struct Variant { pub node: VariantKind, pub span: Span, }
+
+#[derive(Clone)]
+pub struct VariantKind { }
+
+#[derive(Copy, Clone)]
+pub struct Span;
+
+pub fn variant_to_span(variant: Variant) {
+    match variant {
+        Variant {
+            span: _span,
+            ..
+        } => { }
+    };
+}
+
+fn main() { }
diff --git a/src/test/ui/nll/user-annotations/patterns.rs b/src/test/ui/nll/user-annotations/patterns.rs
index e3bac513fa8..8c8e61cd6fb 100644
--- a/src/test/ui/nll/user-annotations/patterns.rs
+++ b/src/test/ui/nll/user-annotations/patterns.rs
@@ -9,11 +9,11 @@ fn variable_no_initializer() {
 }
 
 fn tuple_no_initializer() {
-    // FIXME(#47187): We are not propagating ascribed type through tuples.
+
 
     let x = 22;
     let (y, z): (&'static u32, &'static u32);
-    y = &x;
+    y = &x; //~ ERROR
 }
 
 fn ref_with_ascribed_static_type() -> u32 {
@@ -34,11 +34,23 @@ fn ref_with_ascribed_any_type() -> u32 {
 struct Single<T> { value: T }
 
 fn struct_no_initializer() {
-    // FIXME(#47187): We are not propagating ascribed type through patterns.
+
 
     let x = 22;
     let Single { value: y }: Single<&'static u32>;
-    y = &x;
+    y = &x; //~ ERROR
+}
+
+
+fn struct_no_initializer_must_normalize() {
+    trait Indirect { type Assoc; }
+    struct StaticU32;
+    impl Indirect for StaticU32 { type Assoc = &'static u32; }
+    struct Single2<T: Indirect> { value: <T as Indirect>::Assoc }
+
+    let x = 22;
+    let Single2 { value: mut _y }: Single2<StaticU32>;
+    _y = &x; //~ ERROR
 }
 
 fn variable_with_initializer() {
@@ -91,26 +103,31 @@ fn struct_double_field_underscore_with_initializer() {
 }
 
 fn static_to_a_to_static_through_variable<'a>(x: &'a u32) -> &'static u32 {
-    // The error in this test is inconsistency with
-    // `static_to_a_to_static_through_tuple`, but "feels right" to
-    // me. It occurs because we special case the single binding case
-    // and force the type of `y` to be `&'a u32`, even though the
-    // right-hand side has type `&'static u32`.
+
+
+
+
+
 
     let y: &'a u32 = &22;
     y //~ ERROR
 }
 
 fn static_to_a_to_static_through_tuple<'a>(x: &'a u32) -> &'static u32 {
-    // FIXME(#47187): The fact that this type-checks is perhaps surprising.
-    // What happens is that the right-hand side is constrained to have
-    // type `&'a u32`, which is possible, because it has type
-    // `&'static u32`. The variable `y` is then forced to have type
-    // `&'static u32`, but it is constrained only by the right-hand
-    // side, not the ascribed type, and hence it passes.
+
+
+
+
+
+
 
     let (y, _z): (&'a u32, u32) = (&22, 44);
-    y
+    y //~ ERROR
+}
+
+fn static_to_a_to_static_through_struct<'a>(_x: &'a u32) -> &'static u32 {
+    let Single { value: y }: Single<&'a u32> = Single { value: &22 };
+    y //~ ERROR
 }
 
 fn a_to_static_then_static<'a>(x: &'a u32) -> &'static u32 {
diff --git a/src/test/ui/nll/user-annotations/patterns.stderr b/src/test/ui/nll/user-annotations/patterns.stderr
index 0c50b98ee27..b0c554e6ca1 100644
--- a/src/test/ui/nll/user-annotations/patterns.stderr
+++ b/src/test/ui/nll/user-annotations/patterns.stderr
@@ -9,6 +9,16 @@ LL | }
    | - `x` dropped here while still borrowed
 
 error[E0597]: `x` does not live long enough
+  --> $DIR/patterns.rs:16:9
+   |
+LL |     let (y, z): (&'static u32, &'static u32);
+   |                 ---------------------------- type annotation requires that `x` is borrowed for `'static`
+LL |     y = &x; //~ ERROR
+   |         ^^ borrowed value does not live long enough
+LL | }
+   | - `x` dropped here while still borrowed
+
+error[E0597]: `x` does not live long enough
   --> $DIR/patterns.rs:22:13
    |
 LL |     let y = &x; //~ ERROR
@@ -20,7 +30,27 @@ LL | }
    | - `x` dropped here while still borrowed
 
 error[E0597]: `x` does not live long enough
-  --> $DIR/patterns.rs:46:27
+  --> $DIR/patterns.rs:41:9
+   |
+LL |     let Single { value: y }: Single<&'static u32>;
+   |                              -------------------- type annotation requires that `x` is borrowed for `'static`
+LL |     y = &x; //~ ERROR
+   |         ^^ borrowed value does not live long enough
+LL | }
+   | - `x` dropped here while still borrowed
+
+error[E0597]: `x` does not live long enough
+  --> $DIR/patterns.rs:53:10
+   |
+LL |     let Single2 { value: mut _y }: Single2<StaticU32>;
+   |                                    ------------------ type annotation requires that `x` is borrowed for `'static`
+LL |     _y = &x; //~ ERROR
+   |          ^^ borrowed value does not live long enough
+LL | }
+   | - `x` dropped here while still borrowed
+
+error[E0597]: `x` does not live long enough
+  --> $DIR/patterns.rs:58:27
    |
 LL |     let y: &'static u32 = &x; //~ ERROR
    |            ------------   ^^ borrowed value does not live long enough
@@ -30,7 +60,7 @@ LL | }
    | - `x` dropped here while still borrowed
 
 error[E0597]: `x` does not live long enough
-  --> $DIR/patterns.rs:51:27
+  --> $DIR/patterns.rs:63:27
    |
 LL |     let _: &'static u32 = &x; //~ ERROR
    |            ------------   ^^ borrowed value does not live long enough
@@ -41,7 +71,7 @@ LL | }
    | - `x` dropped here while still borrowed
 
 error[E0716]: temporary value dropped while borrowed
-  --> $DIR/patterns.rs:53:41
+  --> $DIR/patterns.rs:65:41
    |
 LL |     let _: Vec<&'static String> = vec![&String::new()];
    |            --------------------         ^^^^^^^^^^^^^ - temporary value is freed at the end of this statement
@@ -50,7 +80,7 @@ LL |     let _: Vec<&'static String> = vec![&String::new()];
    |            type annotation requires that borrow lasts for `'static`
 
 error[E0716]: temporary value dropped while borrowed
-  --> $DIR/patterns.rs:56:52
+  --> $DIR/patterns.rs:68:52
    |
 LL |     let (_, a): (Vec<&'static String>, _) = (vec![&String::new()], 44);
    |                 -------------------------          ^^^^^^^^^^^^^      - temporary value is freed at the end of this statement
@@ -59,7 +89,7 @@ LL |     let (_, a): (Vec<&'static String>, _) = (vec![&String::new()], 44);
    |                 type annotation requires that borrow lasts for `'static`
 
 error[E0716]: temporary value dropped while borrowed
-  --> $DIR/patterns.rs:59:53
+  --> $DIR/patterns.rs:71:53
    |
 LL |     let (_a, b): (Vec<&'static String>, _) = (vec![&String::new()], 44);
    |                  -------------------------          ^^^^^^^^^^^^^      - temporary value is freed at the end of this statement
@@ -68,7 +98,7 @@ LL |     let (_a, b): (Vec<&'static String>, _) = (vec![&String::new()], 44);
    |                  type annotation requires that borrow lasts for `'static`
 
 error[E0597]: `x` does not live long enough
-  --> $DIR/patterns.rs:65:40
+  --> $DIR/patterns.rs:77:40
    |
 LL |     let (_, _): (&'static u32, u32) = (&x, 44); //~ ERROR
    |                 -------------------    ^^ borrowed value does not live long enough
@@ -78,7 +108,7 @@ LL | }
    | - `x` dropped here while still borrowed
 
 error[E0597]: `x` does not live long enough
-  --> $DIR/patterns.rs:70:40
+  --> $DIR/patterns.rs:82:40
    |
 LL |     let (y, _): (&'static u32, u32) = (&x, 44); //~ ERROR
    |                 -------------------    ^^ borrowed value does not live long enough
@@ -88,7 +118,7 @@ LL | }
    | - `x` dropped here while still borrowed
 
 error[E0597]: `x` does not live long enough
-  --> $DIR/patterns.rs:75:69
+  --> $DIR/patterns.rs:87:69
    |
 LL |     let Single { value: y }: Single<&'static u32> = Single { value: &x }; //~ ERROR
    |                              --------------------                   ^^ borrowed value does not live long enough
@@ -98,7 +128,7 @@ LL | }
    | - `x` dropped here while still borrowed
 
 error[E0597]: `x` does not live long enough
-  --> $DIR/patterns.rs:80:69
+  --> $DIR/patterns.rs:92:69
    |
 LL |     let Single { value: _ }: Single<&'static u32> = Single { value: &x }; //~ ERROR
    |                              --------------------                   ^^ borrowed value does not live long enough
@@ -108,7 +138,7 @@ LL | }
    | - `x` dropped here while still borrowed
 
 error[E0597]: `x` does not live long enough
-  --> $DIR/patterns.rs:88:17
+  --> $DIR/patterns.rs:100:17
    |
 LL |     let Double { value1: _, value2: _ }: Double<&'static u32> = Double {
    |                                          -------------------- type annotation requires that `x` is borrowed for `'static`
@@ -119,7 +149,7 @@ LL | }
    | - `x` dropped here while still borrowed
 
 error: unsatisfied lifetime constraints
-  --> $DIR/patterns.rs:101:5
+  --> $DIR/patterns.rs:113:5
    |
 LL | fn static_to_a_to_static_through_variable<'a>(x: &'a u32) -> &'static u32 {
    |                                           -- lifetime `'a` defined here
@@ -128,14 +158,32 @@ LL |     y //~ ERROR
    |     ^ returning this value requires that `'a` must outlive `'static`
 
 error: unsatisfied lifetime constraints
-  --> $DIR/patterns.rs:117:18
+  --> $DIR/patterns.rs:125:5
+   |
+LL | fn static_to_a_to_static_through_tuple<'a>(x: &'a u32) -> &'static u32 {
+   |                                        -- lifetime `'a` defined here
+...
+LL |     y //~ ERROR
+   |     ^ returning this value requires that `'a` must outlive `'static`
+
+error: unsatisfied lifetime constraints
+  --> $DIR/patterns.rs:130:5
+   |
+LL | fn static_to_a_to_static_through_struct<'a>(_x: &'a u32) -> &'static u32 {
+   |                                         -- lifetime `'a` defined here
+LL |     let Single { value: y }: Single<&'a u32> = Single { value: &22 };
+LL |     y //~ ERROR
+   |     ^ returning this value requires that `'a` must outlive `'static`
+
+error: unsatisfied lifetime constraints
+  --> $DIR/patterns.rs:134:18
    |
 LL | fn a_to_static_then_static<'a>(x: &'a u32) -> &'static u32 {
    |                            -- lifetime `'a` defined here
 LL |     let (y, _z): (&'static u32, u32) = (x, 44); //~ ERROR
    |                  ^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static`
 
-error: aborting due to 14 previous errors
+error: aborting due to 19 previous errors
 
 Some errors occurred: E0597, E0716.
 For more information about an error, try `rustc --explain E0597`.