From 0bfe184b1ad14db4b002c3a272adf44e1839822f Mon Sep 17 00:00:00 2001
From: David Wood <david@davidtw.co>
Date: Wed, 19 Dec 2018 16:47:06 +0100
Subject: [PATCH] Stop duplicating projections of type annotation.

This commit changes how type annotations are handled in bindings during
MIR building.

Instead of building up a `PatternTypeProjections` with the
`CanonicalUserTypeAnnotation` and projections, the
`CanonicalUserTypeAnnotation` is stored in the
`canonical_user_type_annotations` map at the start and the (equivalent)
`UserTypeProjections` is built up with the new index and same projections.

This has the effect of deduplicating type annotations as instead of type
annotations being added to the `canonical_user_type_annotations` map
multiple times at the end after being duplicated (which happens in building
up `PatternTypeProjections`), it is instead added once.
---
 src/librustc/mir/mod.rs               | 75 +++++++++++++++++++++
 src/librustc_mir/build/block.rs       |  2 +-
 src/librustc_mir/build/matches/mod.rs | 37 +++++-----
 src/librustc_mir/hair/mod.rs          |  2 +-
 src/librustc_mir/hair/pattern/mod.rs  | 97 +--------------------------
 5 files changed, 99 insertions(+), 114 deletions(-)

diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs
index ecbe14c093f..2936405ebd0 100644
--- a/src/librustc/mir/mod.rs
+++ b/src/librustc/mir/mod.rs
@@ -2519,6 +2519,48 @@ impl<'tcx> UserTypeProjections<'tcx> {
     pub fn projections(&self) -> impl Iterator<Item=&UserTypeProjection<'tcx>> {
         self.contents.iter().map(|&(ref user_type, _span)| user_type)
     }
+
+    pub fn push_projection(
+        mut self,
+        user_ty: &UserTypeProjection<'tcx>,
+        span: Span,
+    ) -> Self {
+        self.contents.push((user_ty.clone(), span));
+        self
+    }
+
+    fn map_projections(
+        mut self,
+        mut f: impl FnMut(UserTypeProjection<'tcx>) -> UserTypeProjection<'tcx>
+    ) -> Self {
+        self.contents = self.contents.drain(..).map(|(proj, span)| (f(proj), span)).collect();
+        self
+    }
+
+    pub fn index(self) -> Self {
+        self.map_projections(|pat_ty_proj| pat_ty_proj.index())
+    }
+
+    pub fn subslice(self, from: u32, to: u32) -> Self {
+        self.map_projections(|pat_ty_proj| pat_ty_proj.subslice(from, to))
+    }
+
+    pub fn deref(self) -> Self {
+        self.map_projections(|pat_ty_proj| pat_ty_proj.deref())
+    }
+
+    pub fn leaf(self, field: Field) -> Self {
+        self.map_projections(|pat_ty_proj| pat_ty_proj.leaf(field))
+    }
+
+    pub fn variant(
+        self,
+        adt_def: &'tcx AdtDef,
+        variant_index: VariantIdx,
+        field: Field,
+    ) -> Self {
+        self.map_projections(|pat_ty_proj| pat_ty_proj.variant(adt_def, variant_index, field))
+    }
 }
 
 /// Encodes the effect of a user-supplied type annotation on the
@@ -2544,6 +2586,39 @@ pub struct UserTypeProjection<'tcx> {
 
 impl<'tcx> Copy for ProjectionKind<'tcx> { }
 
+impl<'tcx> UserTypeProjection<'tcx> {
+    pub(crate) fn index(mut self) -> Self {
+        self.projs.push(ProjectionElem::Index(()));
+        self
+    }
+
+    pub(crate) fn subslice(mut self, from: u32, to: u32) -> Self {
+        self.projs.push(ProjectionElem::Subslice { from, to });
+        self
+    }
+
+    pub(crate) fn deref(mut self) -> Self {
+        self.projs.push(ProjectionElem::Deref);
+        self
+    }
+
+    pub(crate) fn leaf(mut self, field: Field) -> Self {
+        self.projs.push(ProjectionElem::Field(field, ()));
+        self
+    }
+
+    pub(crate) fn variant(
+        mut self,
+        adt_def: &'tcx AdtDef,
+        variant_index: VariantIdx,
+        field: Field,
+    ) -> Self {
+        self.projs.push(ProjectionElem::Downcast(adt_def, variant_index));
+        self.projs.push(ProjectionElem::Field(field, ()));
+        self
+    }
+}
+
 CloneTypeFoldableAndLiftImpls! { ProjectionKind<'tcx>, }
 
 impl<'tcx> TypeFoldable<'tcx> for UserTypeProjection<'tcx> {
diff --git a/src/librustc_mir/build/block.rs b/src/librustc_mir/build/block.rs
index 41718cfc870..f3d89a7a025 100644
--- a/src/librustc_mir/build/block.rs
+++ b/src/librustc_mir/build/block.rs
@@ -144,7 +144,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                         debug!("ast_block_stmts: pattern={:?}", pattern);
                         this.visit_bindings(
                             &pattern,
-                            &PatternTypeProjections::none(),
+                            UserTypeProjections::none(),
                             &mut |this, _, _, _, node, span, _, _| {
                                 this.storage_live_binding(block, node, span, OutsideGuard);
                                 this.schedule_drop_for_binding(node, span, OutsideGuard);
diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs
index 085c58ef5ff..fe5bc6e19db 100644
--- a/src/librustc_mir/build/matches/mod.rs
+++ b/src/librustc_mir/build/matches/mod.rs
@@ -8,7 +8,6 @@ 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::mir::*;
 use rustc::ty::{self, Ty};
 use rustc::ty::layout::VariantIdx;
@@ -412,7 +411,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         debug!("declare_bindings: patterns={:?}", patterns);
         self.visit_bindings(
             &patterns[0],
-            &PatternTypeProjections::none(),
+            UserTypeProjections::none(),
             &mut |this, mutability, name, mode, var, span, ty, user_ty| {
                 if visibility_scope.is_none() {
                     visibility_scope =
@@ -488,7 +487,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
     pub(super) fn visit_bindings(
         &mut self,
         pattern: &Pattern<'tcx>,
-        pattern_user_ty: &PatternTypeProjections<'tcx>,
+        pattern_user_ty: UserTypeProjections<'tcx>,
         f: &mut impl FnMut(
             &mut Self,
             Mutability,
@@ -497,7 +496,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             NodeId,
             Span,
             Ty<'tcx>,
-            &PatternTypeProjections<'tcx>,
+            UserTypeProjections<'tcx>,
         ),
     ) {
         debug!("visit_bindings: pattern={:?} pattern_user_ty={:?}", pattern, pattern_user_ty);
@@ -511,7 +510,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 ref subpattern,
                 ..
             } => {
-                f(self, mutability, name, mode, var, pattern.span, ty, pattern_user_ty);
+                f(self, mutability, name, mode, var, pattern.span, ty, pattern_user_ty.clone());
                 if let Some(subpattern) = subpattern.as_ref() {
                     self.visit_bindings(subpattern, pattern_user_ty, f);
                 }
@@ -529,18 +528,18 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 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);
+                    self.visit_bindings(subpattern, pattern_user_ty.clone().index(), f);
                 }
                 for subpattern in slice {
-                    self.visit_bindings(subpattern, &pattern_user_ty.subslice(from, to), f);
+                    self.visit_bindings(subpattern, pattern_user_ty.clone().subslice(from, to), f);
                 }
                 for subpattern in suffix {
-                    self.visit_bindings(subpattern, &pattern_user_ty.index(), f);
+                    self.visit_bindings(subpattern, pattern_user_ty.clone().index(), f);
                 }
             }
             PatternKind::Constant { .. } | PatternKind::Range { .. } | PatternKind::Wild => {}
             PatternKind::Deref { ref subpattern } => {
-                self.visit_bindings(subpattern, &pattern_user_ty.deref(), f);
+                self.visit_bindings(subpattern, pattern_user_ty.deref(), f);
             }
             PatternKind::AscribeUserType { ref subpattern, ref user_ty, user_ty_span } => {
                 // This corresponds to something like
@@ -548,23 +547,28 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 // ```
                 // let A::<'a>(_): A<'static> = ...;
                 // ```
-                let subpattern_user_ty = pattern_user_ty.add_user_type(user_ty, user_ty_span);
-                self.visit_bindings(subpattern, &subpattern_user_ty, f)
+                let annotation = (user_ty_span, user_ty.base);
+                let projection = UserTypeProjection {
+                    base: self.canonical_user_type_annotations.push(annotation),
+                    projs: user_ty.projs.clone(),
+                };
+                let subpattern_user_ty = pattern_user_ty.push_projection(&projection, user_ty_span);
+                self.visit_bindings(subpattern, subpattern_user_ty, f)
             }
 
             PatternKind::Leaf { ref subpatterns } => {
                 for subpattern in subpatterns {
-                    let subpattern_user_ty = pattern_user_ty.leaf(subpattern.field);
+                    let subpattern_user_ty = pattern_user_ty.clone().leaf(subpattern.field);
                     debug!("visit_bindings: subpattern_user_ty={:?}", subpattern_user_ty);
-                    self.visit_bindings(&subpattern.pattern, &subpattern_user_ty, f);
+                    self.visit_bindings(&subpattern.pattern, subpattern_user_ty, f);
                 }
             }
 
             PatternKind::Variant { adt_def, substs: _, variant_index, ref subpatterns } => {
                 for subpattern in subpatterns {
-                    let subpattern_user_ty = pattern_user_ty.variant(
+                    let subpattern_user_ty = pattern_user_ty.clone().variant(
                         adt_def, variant_index, subpattern.field);
-                    self.visit_bindings(&subpattern.pattern, &subpattern_user_ty, f);
+                    self.visit_bindings(&subpattern.pattern, subpattern_user_ty, f);
                 }
             }
         }
@@ -1465,7 +1469,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         num_patterns: usize,
         var_id: NodeId,
         var_ty: Ty<'tcx>,
-        user_var_ty: &PatternTypeProjections<'tcx>,
+        user_ty: UserTypeProjections<'tcx>,
         has_guard: ArmHasGuard,
         opt_match_place: Option<(Option<Place<'tcx>>, Span)>,
         pat_span: Span,
@@ -1481,7 +1485,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             BindingMode::ByValue => ty::BindingMode::BindByValue(mutability.into()),
             BindingMode::ByRef { .. } => ty::BindingMode::BindByReference(mutability.into()),
         };
-        let user_ty = user_var_ty.clone().user_ty(&mut self.canonical_user_type_annotations);
         debug!("declare_binding: user_ty={:?}", user_ty);
         let local = LocalDecl::<'tcx> {
             mutability,
diff --git a/src/librustc_mir/hair/mod.rs b/src/librustc_mir/hair/mod.rs
index d36a6eb58b8..b56e3d4e773 100644
--- a/src/librustc_mir/hair/mod.rs
+++ b/src/librustc_mir/hair/mod.rs
@@ -21,7 +21,7 @@ mod constant;
 
 pub mod pattern;
 pub use self::pattern::{BindingMode, Pattern, PatternKind, PatternRange, FieldPattern};
-pub(crate) use self::pattern::{PatternTypeProjection, PatternTypeProjections};
+pub(crate) use self::pattern::PatternTypeProjection;
 
 mod util;
 
diff --git a/src/librustc_mir/hair/pattern/mod.rs b/src/librustc_mir/hair/pattern/mod.rs
index 6d562c69b2d..10d2d7bc1b1 100644
--- a/src/librustc_mir/hair/pattern/mod.rs
+++ b/src/librustc_mir/hair/pattern/mod.rs
@@ -12,7 +12,7 @@ use hair::util::UserAnnotatedTyHelpers;
 use hair::constant::*;
 
 use rustc::mir::{fmt_const_val, Field, BorrowKind, Mutability};
-use rustc::mir::{ProjectionElem, UserTypeProjection, UserTypeProjections};
+use rustc::mir::{ProjectionElem, UserTypeProjection};
 use rustc::mir::interpret::{Scalar, GlobalId, ConstValue, sign_extend};
 use rustc::ty::{self, Region, TyCtxt, AdtDef, Ty, Lift};
 use rustc::ty::{CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, UserTypeAnnotation};
@@ -58,64 +58,6 @@ pub struct Pattern<'tcx> {
 }
 
 
-#[derive(Clone, Debug)]
-pub(crate) struct PatternTypeProjections<'tcx> {
-    contents: Vec<(PatternTypeProjection<'tcx>, Span)>,
-}
-
-impl<'tcx> PatternTypeProjections<'tcx> {
-    pub(crate) fn user_ty(
-        self,
-        annotations: &mut CanonicalUserTypeAnnotations<'tcx>,
-    ) -> UserTypeProjections<'tcx> {
-        UserTypeProjections::from_projections(
-            self.contents
-                .into_iter()
-                .map(|(pat_ty_proj, span)| (pat_ty_proj.user_ty(annotations, span), span))
-        )
-    }
-
-    pub(crate) fn none() -> Self {
-        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: VariantIdx,
-                          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> {
     pub base: CanonicalUserTypeAnnotation<'tcx>,
@@ -123,40 +65,6 @@ pub struct PatternTypeProjection<'tcx> {
 }
 
 impl<'tcx> PatternTypeProjection<'tcx> {
-    pub(crate) fn index(&self) -> Self {
-        let mut new = self.clone();
-        new.projs.push(ProjectionElem::Index(()));
-        new
-    }
-
-    pub(crate) fn subslice(&self, from: u32, to: u32) -> Self {
-        let mut new = self.clone();
-        new.projs.push(ProjectionElem::Subslice { from, to });
-        new
-    }
-
-    pub(crate) fn deref(&self) -> Self {
-        let mut new = self.clone();
-        new.projs.push(ProjectionElem::Deref);
-        new
-    }
-
-    pub(crate) fn leaf(&self, field: Field) -> Self {
-        let mut new = self.clone();
-        new.projs.push(ProjectionElem::Field(field, ()));
-        new
-    }
-
-    pub(crate) fn variant(&self,
-                          adt_def: &'tcx AdtDef,
-                          variant_index: VariantIdx,
-                          field: Field) -> Self {
-        let mut new = self.clone();
-        new.projs.push(ProjectionElem::Downcast(adt_def, variant_index));
-        new.projs.push(ProjectionElem::Field(field, ()));
-        new
-    }
-
     pub(crate) fn from_user_type(user_annotation: CanonicalUserTypeAnnotation<'tcx>) -> Self {
         Self {
             base: user_annotation,
@@ -169,9 +77,8 @@ impl<'tcx> PatternTypeProjection<'tcx> {
         annotations: &mut CanonicalUserTypeAnnotations<'tcx>,
         span: Span,
     ) -> UserTypeProjection<'tcx> {
-        let annotation_index = annotations.push((span, self.base));
         UserTypeProjection {
-            base: annotation_index,
+            base: annotations.push((span, self.base)),
             projs: self.projs
         }
     }