about summary refs log tree commit diff
diff options
context:
space:
mode:
authorFelix S. Klock II <pnkfelix@pnkfx.org>2018-10-22 22:50:10 +0200
committerFelix S. Klock II <pnkfelix@pnkfx.org>2018-10-26 23:47:52 +0200
commit740e8a3f37f224927269bf7b205b16142d91bf0f (patch)
tree94b10bf3171886d3c26522085a0af10967231177
parentb569caf267c595d2c2988941fb39f4718cadfdcc (diff)
downloadrust-740e8a3f37f224927269bf7b205b16142d91bf0f.tar.gz
rust-740e8a3f37f224927269bf7b205b16142d91bf0f.zip
Add the actual chain of projections to `UserTypeProjection`.
Update the existing NLL `patterns.rs` test accordingly.

includes changes addressing review feedback:

 * Added example to docs for `UserTypeProjections` illustrating how we
   build up multiple projections when descending into a pattern with
   type ascriptions.

 * Adapted niko's suggested docs for `UserTypeProjection`.

 * Factored out `projection_ty` from more general `projection_ty_core`
   (as a drive-by, made its callback an `FnMut`, as I discovered later
   that I need that).

 * Add note to docs that `PlaceTy.field_ty(..)` does not normalize its result.

 * Normalize as we project out `field_ty`.
-rw-r--r--src/librustc/ich/impls_mir.rs2
-rw-r--r--src/librustc/mir/mod.rs68
-rw-r--r--src/librustc/mir/tcx.rs56
-rw-r--r--src/librustc/mir/visit.rs3
-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/type_check/mod.rs20
-rw-r--r--src/librustc_mir/build/expr/as_place.rs4
-rw-r--r--src/librustc_mir/build/matches/mod.rs27
-rw-r--r--src/librustc_mir/build/matches/simplify.rs4
-rw-r--r--src/librustc_mir/hair/pattern/mod.rs82
-rw-r--r--src/test/ui/nll/user-annotations/patterns.rs32
-rw-r--r--src/test/ui/nll/user-annotations/patterns.stderr31
13 files changed, 272 insertions, 64 deletions
diff --git a/src/librustc/ich/impls_mir.rs b/src/librustc/ich/impls_mir.rs
index 724481da681..274a2df283c 100644
--- a/src/librustc/ich/impls_mir.rs
+++ b/src/librustc/ich/impls_mir.rs
@@ -607,5 +607,5 @@ impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for mir::UserTypeAnnotation<
     }
 }
 
-impl_stable_hash_for!(struct mir::UserTypeProjection<'tcx> { base });
+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 f96c3b432d6..142dc40e5b1 100644
--- a/src/librustc/mir/mod.rs
+++ b/src/librustc/mir/mod.rs
@@ -2456,6 +2456,31 @@ EnumLiftImpl! {
 ///
 /// 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)>,
@@ -2485,14 +2510,49 @@ impl<'tcx> UserTypeProjections<'tcx> {
     }
 }
 
-#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
+/// 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, (), ()>>,
 }
 
-BraceStructTypeFoldableImpl! {
-    impl<'tcx> TypeFoldable<'tcx> for UserTypeProjection<'tcx> {
-        base
+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.
     }
 }
 
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 61eb565fb9a..bfc03923f60 100644
--- a/src/librustc/mir/visit.rs
+++ b/src/librustc/mir/visit.rs
@@ -797,8 +797,9 @@ macro_rules! make_mir_visitor {
             ) {
                 let UserTypeProjection {
                     ref $($mutability)* base,
+                    projs: _, // Note: Does not visit projection elems!
                 } = *ty;
-                self.visit_user_type_annotation(base)
+                self.visit_user_type_annotation(base);
             }
 
             fn super_user_type_annotation(
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/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs
index 828907ddc63..4d1eea4724b 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,
-                &UserTypeProjection { base: user_ty },
+                &UserTypeProjection { base: user_ty, projs: vec![], },
                 location.to_locations(),
                 ConstraintCategory::Boring,
             ) {
@@ -980,7 +980,6 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
             a, v, user_ty, locations,
         );
 
-        // FIXME
         match user_ty.base {
             UserTypeAnnotation::Ty(canonical_ty) => {
                 let (ty, _) = self.infcx
@@ -991,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) => {
@@ -1000,6 +1013,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
                 ) = self.infcx
                     .instantiate_canonical_with_fresh_inference_vars(DUMMY_SP, &canonical_substs);
 
+                // FIXME: add user_ty.projs support to `AscribeUserType`.
                 self.fully_perform_op(
                     locations,
                     category,
@@ -1173,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,
-                        &UserTypeProjection { base: user_ty },
+                        &UserTypeProjection { base: user_ty, projs: vec![], },
                         location.to_locations(),
                         ConstraintCategory::Boring,
                     ) {
diff --git a/src/librustc_mir/build/expr/as_place.rs b/src/librustc_mir/build/expr/as_place.rs
index bc03b6a5dd0..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 UserTypeProjection { base: 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 UserTypeProjection { base: user_ty },
+                                box UserTypeProjection { base: user_ty, projs: vec![], },
                             ),
                         },
                     );
diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs
index 5479ccb2d84..b92f270255a 100644
--- a/src/librustc_mir/build/matches/mod.rs
+++ b/src/librustc_mir/build/matches/mod.rs
@@ -32,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)]
@@ -541,11 +543,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 ref slice,
                 ref suffix,
             } => {
+                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(), f);
+                    self.visit_bindings(subpattern, &pattern_user_ty.subslice(from, to), f);
                 }
                 for subpattern in suffix {
                     self.visit_bindings(subpattern, &pattern_user_ty.index(), f);
@@ -555,25 +559,28 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             PatternKind::Deref { ref subpattern } => {
                 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> = ...;
                 // ```
-                let pattern_user_ty = pattern_user_ty.add_user_type(user_ty, user_ty_span);
-                self.visit_bindings(subpattern, &pattern_user_ty, 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 } => {
-                for (j, subpattern) in subpatterns.iter().enumerate() {
-                    self.visit_bindings(&subpattern.pattern, &pattern_user_ty.leaf(j), f);
+                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 { ref subpatterns, .. } => {
-                for (j, subpattern) in subpatterns.iter().enumerate() {
-                    self.visit_bindings(&subpattern.pattern, &pattern_user_ty.variant(j), f);
+            PatternKind::Variant { adt_def, substs: _, variant_index, ref subpatterns } => {
+                for subpattern in subpatterns {
+                    let subpattern_user_ty = pattern_user_ty.variant(
+                        adt_def, variant_index, subpattern.field);
+                    self.visit_bindings(&subpattern.pattern, &subpattern_user_ty, f);
                 }
             }
         }
@@ -1329,7 +1336,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                     kind: StatementKind::AscribeUserType(
                         ascription.source.clone(),
                         ty::Variance::Covariant,
-                        box ascription.user_ty.user_ty(),
+                        box ascription.user_ty.clone().user_ty(),
                     ),
                 },
             );
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/hair/pattern/mod.rs b/src/librustc_mir/hair/pattern/mod.rs
index 7a04c6e39df..bff87da9c77 100644
--- a/src/librustc_mir/hair/pattern/mod.rs
+++ b/src/librustc_mir/hair/pattern/mod.rs
@@ -21,7 +21,7 @@ use const_eval::{const_field, const_variant_index};
 use hair::util::UserAnnotatedTyHelpers;
 
 use rustc::mir::{fmt_const_val, Field, BorrowKind, Mutability};
-use rustc::mir::{UserTypeAnnotation, UserTypeProjection, UserTypeProjections};
+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};
@@ -86,43 +86,87 @@ impl<'tcx> PatternTypeProjections<'tcx> {
         PatternTypeProjections { contents: vec![] }
     }
 
-    pub(crate) fn index(&self) -> Self {
-        unimplemented!()
+    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 subslice(&self) -> Self {
-        unimplemented!()
-    }
+    pub(crate) fn index(&self) -> Self { self.map_projs(|pat_ty_proj| pat_ty_proj.index()) }
 
-    pub(crate) fn deref(&self) -> Self {
-        unimplemented!()
+    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 add_user_type(&self, user_ty: PatternTypeProjection<'tcx>, sp: Span) -> Self {
-        let mut new = self.clone();
-        new.contents.push((user_ty, sp));
-        new
+    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 leaf(&self, _index: usize) -> Self {
-        unimplemented!()
+    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 variant(&self, _index: usize) -> Self {
-        unimplemented!()
+    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(Copy, Clone, Debug)]
+#[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 })
+        Self::from_user_type_proj(UserTypeProjection { base: u_ty, projs: vec![], })
     }
 
     pub(crate) fn from_user_type_proj(u_ty: UserTypeProjection<'tcx>) -> Self {
@@ -1086,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/test/ui/nll/user-annotations/patterns.rs b/src/test/ui/nll/user-annotations/patterns.rs
index e3bac513fa8..643231b39b4 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,11 @@ 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 variable_with_initializer() {
@@ -91,26 +91,26 @@ 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 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..58b708fcb1b 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,6 +30,16 @@ LL | }
    | - `x` dropped here while still borrowed
 
 error[E0597]: `x` does not live long enough
+  --> $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:46:27
    |
 LL |     let y: &'static u32 = &x; //~ ERROR
@@ -128,6 +148,15 @@ LL |     y //~ ERROR
    |     ^ returning this value requires that `'a` must outlive `'static`
 
 error: unsatisfied lifetime constraints
+  --> $DIR/patterns.rs:113: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:117:18
    |
 LL | fn a_to_static_then_static<'a>(x: &'a u32) -> &'static u32 {
@@ -135,7 +164,7 @@ LL | fn a_to_static_then_static<'a>(x: &'a u32) -> &'static u32 {
 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 17 previous errors
 
 Some errors occurred: E0597, E0716.
 For more information about an error, try `rustc --explain E0597`.