about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2020-09-18 14:08:39 +0000
committerbors <bors@rust-lang.org>2020-09-18 14:08:39 +0000
commitfdc3405c20122fd0f077f5a77addabc873f20e4c (patch)
treec30aefcbafb5ea856c9e5b37c7da1cfb9a9387dc /compiler
parent2c69266c0697b0c0b34abea62cba1a1d3c59c90c (diff)
parent3ccb1c37e6f37070b090b63acb3751438c152ed4 (diff)
downloadrust-fdc3405c20122fd0f077f5a77addabc873f20e4c.tar.gz
rust-fdc3405c20122fd0f077f5a77addabc873f20e4c.zip
Auto merge of #72412 - VFLashM:issue-72408-nested-closures-exponential, r=tmandry
Issue 72408 nested closures exponential

This fixes #72408.

Nested closures were resulting in exponential compilation time.

This PR is enhancing asymptotic complexity, but also increasing the constant, so I would love to see perf run results.
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_infer/Cargo.toml1
-rw-r--r--compiler/rustc_infer/src/infer/combine.rs73
-rw-r--r--compiler/rustc_infer/src/infer/outlives/verify.rs40
-rw-r--r--compiler/rustc_middle/Cargo.toml1
-rw-r--r--compiler/rustc_middle/src/ty/outlives.rs33
-rw-r--r--compiler/rustc_middle/src/ty/print/mod.rs30
-rw-r--r--compiler/rustc_middle/src/ty/print/pretty.rs12
-rw-r--r--compiler/rustc_middle/src/ty/walk.rs80
-rw-r--r--compiler/rustc_mir/src/monomorphize/collector.rs47
9 files changed, 255 insertions, 62 deletions
diff --git a/compiler/rustc_infer/Cargo.toml b/compiler/rustc_infer/Cargo.toml
index 5dba4106c94..a8c1a370cef 100644
--- a/compiler/rustc_infer/Cargo.toml
+++ b/compiler/rustc_infer/Cargo.toml
@@ -21,4 +21,5 @@ rustc_serialize = { path = "../rustc_serialize" }
 rustc_span = { path = "../rustc_span" }
 rustc_target = { path = "../rustc_target" }
 smallvec = { version = "1.0", features = ["union", "may_dangle"] }
+arrayvec = { version = "0.5.1", default-features = false }
 rustc_ast = { path = "../rustc_ast" }
diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs
index ae4612a89f2..5bd6c667fd7 100644
--- a/compiler/rustc_infer/src/infer/combine.rs
+++ b/compiler/rustc_infer/src/infer/combine.rs
@@ -31,6 +31,9 @@ use super::unify_key::replace_if_possible;
 use super::unify_key::{ConstVarValue, ConstVariableValue};
 use super::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
 use super::{InferCtxt, MiscVariable, TypeTrace};
+use arrayvec::ArrayVec;
+use rustc_data_structures::fx::FxHashMap;
+use std::hash::Hash;
 
 use crate::traits::{Obligation, PredicateObligations};
 
@@ -44,6 +47,63 @@ use rustc_middle::ty::{self, InferConst, ToPredicate, Ty, TyCtxt, TypeFoldable};
 use rustc_middle::ty::{IntType, UintType};
 use rustc_span::DUMMY_SP;
 
+/// Small-storage-optimized implementation of a map
+/// made specifically for caching results.
+///
+/// Stores elements in a small array up to a certain length
+/// and switches to `HashMap` when that length is exceeded.
+enum MiniMap<K, V> {
+    Array(ArrayVec<[(K, V); 8]>),
+    Map(FxHashMap<K, V>),
+}
+
+impl<K: Eq + Hash, V> MiniMap<K, V> {
+    /// Creates an empty `MiniMap`.
+    pub fn new() -> Self {
+        MiniMap::Array(ArrayVec::new())
+    }
+
+    /// Inserts or updates value in the map.
+    pub fn insert(&mut self, key: K, value: V) {
+        match self {
+            MiniMap::Array(array) => {
+                for pair in array.iter_mut() {
+                    if pair.0 == key {
+                        pair.1 = value;
+                        return;
+                    }
+                }
+                if let Err(error) = array.try_push((key, value)) {
+                    let mut map: FxHashMap<K, V> = array.drain(..).collect();
+                    let (key, value) = error.element();
+                    map.insert(key, value);
+                    *self = MiniMap::Map(map);
+                }
+            }
+            MiniMap::Map(map) => {
+                map.insert(key, value);
+            }
+        }
+    }
+
+    /// Return value by key if any.
+    pub fn get(&self, key: &K) -> Option<&V> {
+        match self {
+            MiniMap::Array(array) => {
+                for pair in array {
+                    if pair.0 == *key {
+                        return Some(&pair.1);
+                    }
+                }
+                return None;
+            }
+            MiniMap::Map(map) => {
+                return map.get(key);
+            }
+        }
+    }
+}
+
 #[derive(Clone)]
 pub struct CombineFields<'infcx, 'tcx> {
     pub infcx: &'infcx InferCtxt<'infcx, 'tcx>,
@@ -379,6 +439,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
             needs_wf: false,
             root_ty: ty,
             param_env: self.param_env,
+            cache: MiniMap::new(),
         };
 
         let ty = match generalize.relate(ty, ty) {
@@ -438,6 +499,8 @@ struct Generalizer<'cx, 'tcx> {
     root_ty: Ty<'tcx>,
 
     param_env: ty::ParamEnv<'tcx>,
+
+    cache: MiniMap<Ty<'tcx>, RelateResult<'tcx, Ty<'tcx>>>,
 }
 
 /// Result from a generalization operation. This includes
@@ -535,13 +598,16 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
     fn tys(&mut self, t: Ty<'tcx>, t2: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
         assert_eq!(t, t2); // we are abusing TypeRelation here; both LHS and RHS ought to be ==
 
+        if let Some(result) = self.cache.get(&t) {
+            return result.clone();
+        }
         debug!("generalize: t={:?}", t);
 
         // Check to see whether the type we are generalizing references
         // any other type variable related to `vid` via
         // subtyping. This is basically our "occurs check", preventing
         // us from creating infinitely sized types.
-        match *t.kind() {
+        let result = match *t.kind() {
             ty::Infer(ty::TyVar(vid)) => {
                 let vid = self.infcx.inner.borrow_mut().type_variables().root_var(vid);
                 let sub_vid = self.infcx.inner.borrow_mut().type_variables().sub_root_var(vid);
@@ -598,7 +664,10 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
                 Ok(t)
             }
             _ => relate::super_relate_tys(self, t, t),
-        }
+        };
+
+        self.cache.insert(t, result.clone());
+        return result;
     }
 
     fn regions(
diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs
index d6f1ca3cf95..e06bfb59580 100644
--- a/compiler/rustc_infer/src/infer/outlives/verify.rs
+++ b/compiler/rustc_infer/src/infer/outlives/verify.rs
@@ -3,6 +3,7 @@ use crate::infer::{GenericKind, VerifyBound};
 use rustc_data_structures::captures::Captures;
 use rustc_hir::def_id::DefId;
 use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst};
+use rustc_middle::ty::walk::MiniSet;
 use rustc_middle::ty::{self, Ty, TyCtxt};
 
 /// The `TypeOutlives` struct has the job of "lowering" a `T: 'a`
@@ -31,16 +32,23 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
     /// Returns a "verify bound" that encodes what we know about
     /// `generic` and the regions it outlives.
     pub fn generic_bound(&self, generic: GenericKind<'tcx>) -> VerifyBound<'tcx> {
+        let mut visited = MiniSet::new();
         match generic {
             GenericKind::Param(param_ty) => self.param_bound(param_ty),
-            GenericKind::Projection(projection_ty) => self.projection_bound(projection_ty),
+            GenericKind::Projection(projection_ty) => {
+                self.projection_bound(projection_ty, &mut visited)
+            }
         }
     }
 
-    fn type_bound(&self, ty: Ty<'tcx>) -> VerifyBound<'tcx> {
+    fn type_bound(
+        &self,
+        ty: Ty<'tcx>,
+        visited: &mut MiniSet<GenericArg<'tcx>>,
+    ) -> VerifyBound<'tcx> {
         match *ty.kind() {
             ty::Param(p) => self.param_bound(p),
-            ty::Projection(data) => self.projection_bound(data),
+            ty::Projection(data) => self.projection_bound(data, visited),
             ty::FnDef(_, substs) => {
                 // HACK(eddyb) ignore lifetimes found shallowly in `substs`.
                 // This is inconsistent with `ty::Adt` (including all substs),
@@ -50,9 +58,9 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
                 let mut bounds = substs
                     .iter()
                     .filter_map(|child| match child.unpack() {
-                        GenericArgKind::Type(ty) => Some(self.type_bound(ty)),
+                        GenericArgKind::Type(ty) => Some(self.type_bound(ty, visited)),
                         GenericArgKind::Lifetime(_) => None,
-                        GenericArgKind::Const(_) => Some(self.recursive_bound(child)),
+                        GenericArgKind::Const(_) => Some(self.recursive_bound(child, visited)),
                     })
                     .filter(|bound| {
                         // Remove bounds that must hold, since they are not interesting.
@@ -66,7 +74,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
                     ),
                 }
             }
-            _ => self.recursive_bound(ty.into()),
+            _ => self.recursive_bound(ty.into(), visited),
         }
     }
 
@@ -137,7 +145,11 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
         self.declared_projection_bounds_from_trait(projection_ty)
     }
 
-    pub fn projection_bound(&self, projection_ty: ty::ProjectionTy<'tcx>) -> VerifyBound<'tcx> {
+    pub fn projection_bound(
+        &self,
+        projection_ty: ty::ProjectionTy<'tcx>,
+        visited: &mut MiniSet<GenericArg<'tcx>>,
+    ) -> VerifyBound<'tcx> {
         debug!("projection_bound(projection_ty={:?})", projection_ty);
 
         let projection_ty_as_ty =
@@ -166,21 +178,25 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
 
         // see the extensive comment in projection_must_outlive
         let ty = self.tcx.mk_projection(projection_ty.item_def_id, projection_ty.substs);
-        let recursive_bound = self.recursive_bound(ty.into());
+        let recursive_bound = self.recursive_bound(ty.into(), visited);
 
         VerifyBound::AnyBound(env_bounds.chain(trait_bounds).collect()).or(recursive_bound)
     }
 
-    fn recursive_bound(&self, parent: GenericArg<'tcx>) -> VerifyBound<'tcx> {
+    fn recursive_bound(
+        &self,
+        parent: GenericArg<'tcx>,
+        visited: &mut MiniSet<GenericArg<'tcx>>,
+    ) -> VerifyBound<'tcx> {
         let mut bounds = parent
-            .walk_shallow()
+            .walk_shallow(visited)
             .filter_map(|child| match child.unpack() {
-                GenericArgKind::Type(ty) => Some(self.type_bound(ty)),
+                GenericArgKind::Type(ty) => Some(self.type_bound(ty, visited)),
                 GenericArgKind::Lifetime(lt) => {
                     // Ignore late-bound regions.
                     if !lt.is_late_bound() { Some(VerifyBound::OutlivedBy(lt)) } else { None }
                 }
-                GenericArgKind::Const(_) => Some(self.recursive_bound(child)),
+                GenericArgKind::Const(_) => Some(self.recursive_bound(child, visited)),
             })
             .filter(|bound| {
                 // Remove bounds that must hold, since they are not interesting.
diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml
index a5a860a38b3..1d84ddad7f5 100644
--- a/compiler/rustc_middle/Cargo.toml
+++ b/compiler/rustc_middle/Cargo.toml
@@ -28,5 +28,6 @@ rustc_ast = { path = "../rustc_ast" }
 rustc_span = { path = "../rustc_span" }
 chalk-ir = "0.21.0"
 smallvec = { version = "1.0", features = ["union", "may_dangle"] }
+arrayvec = { version = "0.5.1", default-features = false }
 measureme = "0.7.1"
 rustc_session = { path = "../rustc_session" }
diff --git a/compiler/rustc_middle/src/ty/outlives.rs b/compiler/rustc_middle/src/ty/outlives.rs
index 783f116a87d..01649f44c88 100644
--- a/compiler/rustc_middle/src/ty/outlives.rs
+++ b/compiler/rustc_middle/src/ty/outlives.rs
@@ -3,6 +3,7 @@
 // RFC for reference.
 
 use crate::ty::subst::{GenericArg, GenericArgKind};
+use crate::ty::walk::MiniSet;
 use crate::ty::{self, Ty, TyCtxt, TypeFoldable};
 use smallvec::SmallVec;
 
@@ -50,12 +51,18 @@ impl<'tcx> TyCtxt<'tcx> {
     /// Push onto `out` all the things that must outlive `'a` for the condition
     /// `ty0: 'a` to hold. Note that `ty0` must be a **fully resolved type**.
     pub fn push_outlives_components(self, ty0: Ty<'tcx>, out: &mut SmallVec<[Component<'tcx>; 4]>) {
-        compute_components(self, ty0, out);
+        let mut visited = MiniSet::new();
+        compute_components(self, ty0, out, &mut visited);
         debug!("components({:?}) = {:?}", ty0, out);
     }
 }
 
-fn compute_components(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, out: &mut SmallVec<[Component<'tcx>; 4]>) {
+fn compute_components(
+    tcx: TyCtxt<'tcx>,
+    ty: Ty<'tcx>,
+    out: &mut SmallVec<[Component<'tcx>; 4]>,
+    visited: &mut MiniSet<GenericArg<'tcx>>,
+) {
     // Descend through the types, looking for the various "base"
     // components and collecting them into `out`. This is not written
     // with `collect()` because of the need to sometimes skip subtrees
@@ -73,11 +80,11 @@ fn compute_components(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, out: &mut SmallVec<[Compo
                 for child in substs {
                     match child.unpack() {
                         GenericArgKind::Type(ty) => {
-                            compute_components(tcx, ty, out);
+                            compute_components(tcx, ty, out, visited);
                         }
                         GenericArgKind::Lifetime(_) => {}
                         GenericArgKind::Const(_) => {
-                            compute_components_recursive(tcx, child, out);
+                            compute_components_recursive(tcx, child, out, visited);
                         }
                     }
                 }
@@ -85,19 +92,19 @@ fn compute_components(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, out: &mut SmallVec<[Compo
 
             ty::Array(element, _) => {
                 // Don't look into the len const as it doesn't affect regions
-                compute_components(tcx, element, out);
+                compute_components(tcx, element, out, visited);
             }
 
             ty::Closure(_, ref substs) => {
                 for upvar_ty in substs.as_closure().upvar_tys() {
-                    compute_components(tcx, upvar_ty, out);
+                    compute_components(tcx, upvar_ty, out, visited);
                 }
             }
 
             ty::Generator(_, ref substs, _) => {
                 // Same as the closure case
                 for upvar_ty in substs.as_generator().upvar_tys() {
-                    compute_components(tcx, upvar_ty, out);
+                    compute_components(tcx, upvar_ty, out, visited);
                 }
 
                 // We ignore regions in the generator interior as we don't
@@ -135,7 +142,8 @@ fn compute_components(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, out: &mut SmallVec<[Compo
                     // OutlivesProjectionComponents.  Continue walking
                     // through and constrain Pi.
                     let mut subcomponents = smallvec![];
-                    compute_components_recursive(tcx, ty.into(), &mut subcomponents);
+                    let mut subvisited = MiniSet::new();
+                    compute_components_recursive(tcx, ty.into(), &mut subcomponents, &mut subvisited);
                     out.push(Component::EscapingProjection(subcomponents.into_iter().collect()));
                 }
             }
@@ -177,7 +185,7 @@ fn compute_components(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, out: &mut SmallVec<[Compo
                 // the "bound regions list".  In our representation, no such
                 // list is maintained explicitly, because bound regions
                 // themselves can be readily identified.
-                compute_components_recursive(tcx, ty.into(), out);
+                compute_components_recursive(tcx, ty.into(), out, visited);
             }
         }
 }
@@ -186,11 +194,12 @@ fn compute_components_recursive(
     tcx: TyCtxt<'tcx>,
     parent: GenericArg<'tcx>,
     out: &mut SmallVec<[Component<'tcx>; 4]>,
+    visited: &mut MiniSet<GenericArg<'tcx>>,
 ) {
-    for child in parent.walk_shallow() {
+    for child in parent.walk_shallow(visited) {
         match child.unpack() {
             GenericArgKind::Type(ty) => {
-                compute_components(tcx, ty, out);
+                compute_components(tcx, ty, out, visited);
             }
             GenericArgKind::Lifetime(lt) => {
                 // Ignore late-bound regions.
@@ -199,7 +208,7 @@ fn compute_components_recursive(
                 }
             }
             GenericArgKind::Const(_) => {
-                compute_components_recursive(tcx, child, out);
+                compute_components_recursive(tcx, child, out, visited);
             }
         }
     }
diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs
index 709a4018d80..f315292dab5 100644
--- a/compiler/rustc_middle/src/ty/print/mod.rs
+++ b/compiler/rustc_middle/src/ty/print/mod.rs
@@ -4,6 +4,7 @@ use crate::ty::{self, DefIdTree, Ty, TyCtxt};
 use rustc_data_structures::fx::FxHashSet;
 use rustc_hir::def_id::{CrateNum, DefId};
 use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData};
+use rustc_middle::ty::walk::MiniSet;
 
 // `pretty` is a separate module only for organization.
 mod pretty;
@@ -263,21 +264,33 @@ pub trait Printer<'tcx>: Sized {
 /// function tries to find a "characteristic `DefId`" for a
 /// type. It's just a heuristic so it makes some questionable
 /// decisions and we may want to adjust it later.
-pub fn characteristic_def_id_of_type(ty: Ty<'_>) -> Option<DefId> {
+///
+/// Visited set is needed to avoid full iteration over
+/// deeply nested tuples that have no DefId.
+fn characteristic_def_id_of_type_cached<'a>(
+    ty: Ty<'a>,
+    visited: &mut MiniSet<Ty<'a>>,
+) -> Option<DefId> {
     match *ty.kind() {
         ty::Adt(adt_def, _) => Some(adt_def.did),
 
         ty::Dynamic(data, ..) => data.principal_def_id(),
 
-        ty::Array(subty, _) | ty::Slice(subty) => characteristic_def_id_of_type(subty),
+        ty::Array(subty, _) | ty::Slice(subty) => {
+            characteristic_def_id_of_type_cached(subty, visited)
+        }
 
-        ty::RawPtr(mt) => characteristic_def_id_of_type(mt.ty),
+        ty::RawPtr(mt) => characteristic_def_id_of_type_cached(mt.ty, visited),
 
-        ty::Ref(_, ty, _) => characteristic_def_id_of_type(ty),
+        ty::Ref(_, ty, _) => characteristic_def_id_of_type_cached(ty, visited),
 
-        ty::Tuple(ref tys) => {
-            tys.iter().find_map(|ty| characteristic_def_id_of_type(ty.expect_ty()))
-        }
+        ty::Tuple(ref tys) => tys.iter().find_map(|ty| {
+            let ty = ty.expect_ty();
+            if visited.insert(ty) {
+                return characteristic_def_id_of_type_cached(ty, visited);
+            }
+            return None;
+        }),
 
         ty::FnDef(def_id, _)
         | ty::Closure(def_id, _)
@@ -302,6 +315,9 @@ pub fn characteristic_def_id_of_type(ty: Ty<'_>) -> Option<DefId> {
         | ty::Float(_) => None,
     }
 }
+pub fn characteristic_def_id_of_type(ty: Ty<'_>) -> Option<DefId> {
+    characteristic_def_id_of_type_cached(ty, &mut MiniSet::new())
+}
 
 impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for ty::RegionKind {
     type Output = P::Region;
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index 4b2e9a16d4a..f0bae48cc12 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -1264,6 +1264,7 @@ pub struct FmtPrinterData<'a, 'tcx, F> {
     used_region_names: FxHashSet<Symbol>,
     region_index: usize,
     binder_depth: usize,
+    printed_type_count: usize,
 
     pub region_highlight_mode: RegionHighlightMode,
 
@@ -1294,6 +1295,7 @@ impl<F> FmtPrinter<'a, 'tcx, F> {
             used_region_names: Default::default(),
             region_index: 0,
             binder_depth: 0,
+            printed_type_count: 0,
             region_highlight_mode: RegionHighlightMode::default(),
             name_resolver: None,
         }))
@@ -1411,8 +1413,14 @@ impl<F: fmt::Write> Printer<'tcx> for FmtPrinter<'_, 'tcx, F> {
         self.pretty_print_region(region)
     }
 
-    fn print_type(self, ty: Ty<'tcx>) -> Result<Self::Type, Self::Error> {
-        self.pretty_print_type(ty)
+    fn print_type(mut self, ty: Ty<'tcx>) -> Result<Self::Type, Self::Error> {
+        if self.tcx.sess.type_length_limit().value_within_limit(self.printed_type_count) {
+            self.printed_type_count += 1;
+            self.pretty_print_type(ty)
+        } else {
+            write!(self, "...")?;
+            Ok(self)
+        }
     }
 
     fn print_dyn_existential(
diff --git a/compiler/rustc_middle/src/ty/walk.rs b/compiler/rustc_middle/src/ty/walk.rs
index 4f55517c6f4..7afa6e6cc05 100644
--- a/compiler/rustc_middle/src/ty/walk.rs
+++ b/compiler/rustc_middle/src/ty/walk.rs
@@ -3,7 +3,50 @@
 
 use crate::ty;
 use crate::ty::subst::{GenericArg, GenericArgKind};
+use arrayvec::ArrayVec;
+use rustc_data_structures::fx::FxHashSet;
 use smallvec::{self, SmallVec};
+use std::hash::Hash;
+
+/// Small-storage-optimized implementation of a set
+/// made specifically for walking type tree.
+///
+/// Stores elements in a small array up to a certain length
+/// and switches to `HashSet` when that length is exceeded.
+pub enum MiniSet<T> {
+    Array(ArrayVec<[T; 8]>),
+    Set(FxHashSet<T>),
+}
+
+impl<T: Eq + Hash + Copy> MiniSet<T> {
+    /// Creates an empty `MiniSet`.
+    pub fn new() -> Self {
+        MiniSet::Array(ArrayVec::new())
+    }
+
+    /// Adds a value to the set.
+    ///
+    /// If the set did not have this value present, true is returned.
+    ///
+    /// If the set did have this value present, false is returned.
+    pub fn insert(&mut self, elem: T) -> bool {
+        match self {
+            MiniSet::Array(array) => {
+                if array.iter().any(|e| *e == elem) {
+                    false
+                } else {
+                    if array.try_push(elem).is_err() {
+                        let mut set: FxHashSet<T> = array.iter().copied().collect();
+                        set.insert(elem);
+                        *self = MiniSet::Set(set);
+                    }
+                    true
+                }
+            }
+            MiniSet::Set(set) => set.insert(elem),
+        }
+    }
+}
 
 // The TypeWalker's stack is hot enough that it's worth going to some effort to
 // avoid heap allocations.
@@ -12,11 +55,20 @@ type TypeWalkerStack<'tcx> = SmallVec<[GenericArg<'tcx>; 8]>;
 pub struct TypeWalker<'tcx> {
     stack: TypeWalkerStack<'tcx>,
     last_subtree: usize,
+    visited: MiniSet<GenericArg<'tcx>>,
 }
 
+/// An iterator for walking the type tree.
+///
+/// It's very easy to produce a deeply
+/// nested type tree with a lot of
+/// identical subtrees. In order to work efficiently
+/// in this situation walker only visits each type once.
+/// It maintains a set of visited types and
+/// skips any types that are already there.
 impl<'tcx> TypeWalker<'tcx> {
-    pub fn new(root: GenericArg<'tcx>) -> TypeWalker<'tcx> {
-        TypeWalker { stack: smallvec![root], last_subtree: 1 }
+    pub fn new(root: GenericArg<'tcx>) -> Self {
+        Self { stack: smallvec![root], last_subtree: 1, visited: MiniSet::new() }
     }
 
     /// Skips the subtree corresponding to the last type
@@ -41,11 +93,15 @@ impl<'tcx> Iterator for TypeWalker<'tcx> {
 
     fn next(&mut self) -> Option<GenericArg<'tcx>> {
         debug!("next(): stack={:?}", self.stack);
-        let next = self.stack.pop()?;
-        self.last_subtree = self.stack.len();
-        push_inner(&mut self.stack, next);
-        debug!("next: stack={:?}", self.stack);
-        Some(next)
+        loop {
+            let next = self.stack.pop()?;
+            self.last_subtree = self.stack.len();
+            if self.visited.insert(next) {
+                push_inner(&mut self.stack, next);
+                debug!("next: stack={:?}", self.stack);
+                return Some(next);
+            }
+        }
     }
 }
 
@@ -67,9 +123,17 @@ impl GenericArg<'tcx> {
     /// Iterator that walks the immediate children of `self`. Hence
     /// `Foo<Bar<i32>, u32>` yields the sequence `[Bar<i32>, u32]`
     /// (but not `i32`, like `walk`).
-    pub fn walk_shallow(self) -> impl Iterator<Item = GenericArg<'tcx>> {
+    ///
+    /// Iterator only walks items once.
+    /// It accepts visited set, updates it with all visited types
+    /// and skips any types that are already there.
+    pub fn walk_shallow(
+        self,
+        visited: &mut MiniSet<GenericArg<'tcx>>,
+    ) -> impl Iterator<Item = GenericArg<'tcx>> {
         let mut stack = SmallVec::new();
         push_inner(&mut stack, self);
+        stack.retain(|a| visited.insert(*a));
         stack.into_iter()
     }
 }
diff --git a/compiler/rustc_mir/src/monomorphize/collector.rs b/compiler/rustc_mir/src/monomorphize/collector.rs
index 9ea103463d5..0dbb4b1015e 100644
--- a/compiler/rustc_mir/src/monomorphize/collector.rs
+++ b/compiler/rustc_mir/src/monomorphize/collector.rs
@@ -418,6 +418,29 @@ fn record_accesses<'a, 'tcx: 'a>(
     inlining_map.lock_mut().record_accesses(caller, &accesses);
 }
 
+// Shrinks string by keeping prefix and suffix of given sizes.
+fn shrink(s: String, before: usize, after: usize) -> String {
+    // An iterator of all byte positions including the end of the string.
+    let positions = || s.char_indices().map(|(i, _)| i).chain(iter::once(s.len()));
+
+    let shrunk = format!(
+        "{before}...{after}",
+        before = &s[..positions().nth(before).unwrap_or(s.len())],
+        after = &s[positions().rev().nth(after).unwrap_or(0)..],
+    );
+
+    // Only use the shrunk version if it's really shorter.
+    // This also avoids the case where before and after slices overlap.
+    if shrunk.len() < s.len() { shrunk } else { s }
+}
+
+// Format instance name that is already known to be too long for rustc.
+// Show only the first and last 32 characters to avoid blasting
+// the user's terminal with thousands of lines of type-name.
+fn shrunk_instance_name(instance: &Instance<'tcx>) -> String {
+    shrink(instance.to_string(), 32, 32)
+}
+
 fn check_recursion_limit<'tcx>(
     tcx: TyCtxt<'tcx>,
     instance: Instance<'tcx>,
@@ -440,7 +463,10 @@ fn check_recursion_limit<'tcx>(
     // more than the recursion limit is assumed to be causing an
     // infinite expansion.
     if !tcx.sess.recursion_limit().value_within_limit(adjusted_recursion_depth) {
-        let error = format!("reached the recursion limit while instantiating `{}`", instance);
+        let error = format!(
+            "reached the recursion limit while instantiating `{}`",
+            shrunk_instance_name(&instance),
+        );
         let mut err = tcx.sess.struct_span_fatal(span, &error);
         err.span_note(
             tcx.def_span(def_id),
@@ -474,26 +500,9 @@ fn check_type_length_limit<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) {
     //
     // Bail out in these cases to avoid that bad user experience.
     if !tcx.sess.type_length_limit().value_within_limit(type_length) {
-        // The instance name is already known to be too long for rustc.
-        // Show only the first and last 32 characters to avoid blasting
-        // the user's terminal with thousands of lines of type-name.
-        let shrink = |s: String, before: usize, after: usize| {
-            // An iterator of all byte positions including the end of the string.
-            let positions = || s.char_indices().map(|(i, _)| i).chain(iter::once(s.len()));
-
-            let shrunk = format!(
-                "{before}...{after}",
-                before = &s[..positions().nth(before).unwrap_or(s.len())],
-                after = &s[positions().rev().nth(after).unwrap_or(0)..],
-            );
-
-            // Only use the shrunk version if it's really shorter.
-            // This also avoids the case where before and after slices overlap.
-            if shrunk.len() < s.len() { shrunk } else { s }
-        };
         let msg = format!(
             "reached the type-length limit while instantiating `{}`",
-            shrink(instance.to_string(), 32, 32)
+            shrunk_instance_name(&instance),
         );
         let mut diag = tcx.sess.struct_span_fatal(tcx.def_span(instance.def_id()), &msg);
         diag.note(&format!(