diff --git a/Cargo.lock b/Cargo.lock index 9b5a4c24d25ed..23878f7ade7a8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3594,6 +3594,7 @@ dependencies = [ name = "rustc_infer" version = "0.0.0" dependencies = [ + "arrayvec", "rustc_ast", "rustc_data_structures", "rustc_errors", @@ -3733,6 +3734,7 @@ dependencies = [ name = "rustc_middle" version = "0.0.0" dependencies = [ + "arrayvec", "bitflags", "chalk-ir", "measureme", diff --git a/compiler/rustc_infer/Cargo.toml b/compiler/rustc_infer/Cargo.toml index 5dba4106c9423..a8c1a370cef82 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 ae4612a89f277..5bd6c667fd7f6 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 { + Array(ArrayVec<[(K, V); 8]>), + Map(FxHashMap), +} + +impl MiniMap { + /// 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 = 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, 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 d6f1ca3cf9536..e06bfb5958086 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>, + ) -> 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>, + ) -> 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>, + ) -> 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 a5a860a38b3e8..1d84ddad7f52d 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 783f116a87d38..01649f44c8861 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>, +) { // 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>, ) { - 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 709a4018d809c..f315292dab546 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 { +/// +/// 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>, +) -> Option { 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 { | ty::Float(_) => None, } } +pub fn characteristic_def_id_of_type(ty: Ty<'_>) -> Option { + 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 9562d43791493..1ed98d1f2cafd 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, region_index: usize, binder_depth: usize, + printed_type_count: usize, pub region_highlight_mode: RegionHighlightMode, @@ -1294,6 +1295,7 @@ impl 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 Printer<'tcx> for FmtPrinter<'_, 'tcx, F> { self.pretty_print_region(region) } - fn print_type(self, ty: Ty<'tcx>) -> Result { - self.pretty_print_type(ty) + fn print_type(mut self, ty: Ty<'tcx>) -> Result { + 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 4f55517c6f435..7afa6e6cc056d 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 { + Array(ArrayVec<[T; 8]>), + Set(FxHashSet), +} + +impl MiniSet { + /// 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 = 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>, } +/// 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> { 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, u32>` yields the sequence `[Bar, u32]` /// (but not `i32`, like `walk`). - pub fn walk_shallow(self) -> impl Iterator> { + /// + /// 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>, + ) -> impl Iterator> { 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 9ea103463d5eb..0dbb4b1015e79 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!( diff --git a/src/test/ui/closures/issue-72408-nested-closures-exponential.rs b/src/test/ui/closures/issue-72408-nested-closures-exponential.rs new file mode 100644 index 0000000000000..2d6ba936572d5 --- /dev/null +++ b/src/test/ui/closures/issue-72408-nested-closures-exponential.rs @@ -0,0 +1,59 @@ +// build-pass + +// Closures include captured types twice in a type tree. +// +// Wrapping one closure with another leads to doubling +// the amount of types in the type tree. +// +// This test ensures that rust can handle +// deeply nested type trees with a lot +// of duplicated subtrees. + +fn dup(f: impl Fn(i32) -> i32) -> impl Fn(i32) -> i32 { + move |a| f(a * 2) +} + +fn main() { + let f = |a| a; + + let f = dup(f); + let f = dup(f); + let f = dup(f); + let f = dup(f); + let f = dup(f); + + let f = dup(f); + let f = dup(f); + let f = dup(f); + let f = dup(f); + let f = dup(f); + + let f = dup(f); + let f = dup(f); + let f = dup(f); + let f = dup(f); + let f = dup(f); + + let f = dup(f); + let f = dup(f); + let f = dup(f); + let f = dup(f); + let f = dup(f); + + // Compiler dies around here if it tries + // to walk the tree exhaustively. + + let f = dup(f); + let f = dup(f); + let f = dup(f); + let f = dup(f); + let f = dup(f); + + let f = dup(f); + let f = dup(f); + let f = dup(f); + let f = dup(f); + let f = dup(f); + + println!("Type size was at least {}", f(1)); +} diff --git a/src/test/ui/infinite/infinite-instantiation.stderr b/src/test/ui/infinite/infinite-instantiation.stderr index 1aaefea9f042c..d27d14842ce99 100644 --- a/src/test/ui/infinite/infinite-instantiation.stderr +++ b/src/test/ui/infinite/infinite-instantiation.stderr @@ -1,4 +1,4 @@ -error: reached the recursion limit while instantiating `function::>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` +error: reached the recursion limit while instantiating `function::>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` --> $DIR/infinite-instantiation.rs:21:9 | LL | function(counter - 1, t.to_option()); diff --git a/src/test/ui/issues/issue-22638.rs b/src/test/ui/issues/issue-22638.rs index 72c16fddb4b12..89137538425bf 100644 --- a/src/test/ui/issues/issue-22638.rs +++ b/src/test/ui/issues/issue-22638.rs @@ -51,9 +51,9 @@ struct D (Box); impl D { pub fn matches(&self, f: &F) { - //~^ ERROR reached the type-length limit while instantiating `D::matches::<[closure let &D(ref a) = self; a.matches(f) + //~^ ERROR reached the recursion limit while instantiating `A::matches::<[closure } } diff --git a/src/test/ui/issues/issue-22638.stderr b/src/test/ui/issues/issue-22638.stderr index b0df46b11fadb..c4255b95b704e 100644 --- a/src/test/ui/issues/issue-22638.stderr +++ b/src/test/ui/issues/issue-22638.stderr @@ -1,10 +1,14 @@ -error: reached the type-length limit while instantiating `D::matches::$CLOSURE` - --> $DIR/issue-22638.rs:53:5 +error: reached the recursion limit while instantiating `A::matches::$CLOSURE` + --> $DIR/issue-22638.rs:55:9 + | +LL | a.matches(f) + | ^^^^^^^^^^^^ + | +note: `A::matches` defined here + --> $DIR/issue-22638.rs:14:5 | LL | pub fn matches(&self, f: &F) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: consider adding a `#![type_length_limit="30408681"]` attribute to your crate error: aborting due to previous error diff --git a/src/test/ui/issues/issue-37311-type-length-limit/issue-37311.rs b/src/test/ui/issues/issue-37311-type-length-limit/issue-37311.rs index fec4b17153609..d3d5863ddb3c9 100644 --- a/src/test/ui/issues/issue-37311-type-length-limit/issue-37311.rs +++ b/src/test/ui/issues/issue-37311-type-length-limit/issue-37311.rs @@ -12,8 +12,8 @@ trait Foo { impl Foo for T { #[allow(unconditional_recursion)] - fn recurse(&self) { //~ ERROR reached the type-length limit - (self, self).recurse(); + fn recurse(&self) { + (self, self).recurse(); //~ ERROR reached the recursion limit } } diff --git a/src/test/ui/issues/issue-37311-type-length-limit/issue-37311.stderr b/src/test/ui/issues/issue-37311-type-length-limit/issue-37311.stderr index 6229d90d4b477..a94f190d6b25d 100644 --- a/src/test/ui/issues/issue-37311-type-length-limit/issue-37311.stderr +++ b/src/test/ui/issues/issue-37311-type-length-limit/issue-37311.stderr @@ -1,10 +1,14 @@ -error: reached the type-length limit while instantiating `<(&(&(&(&(&(&(&(&(&(&(&(&(&(&(&(...))))))))))))))) as Foo>::recurse` +error: reached the recursion limit while instantiating `<(&(&(&(&(&(&(&(&(&(&(&(&(&(&(&(.....), ...), ...) as Foo>::recurse` + --> $DIR/issue-37311.rs:16:9 + | +LL | (self, self).recurse(); + | ^^^^^^^^^^^^^^^^^^^^^^ + | +note: `::recurse` defined here --> $DIR/issue-37311.rs:15:5 | LL | fn recurse(&self) { | ^^^^^^^^^^^^^^^^^ - | - = note: consider adding a `#![type_length_limit="2097149"]` attribute to your crate error: aborting due to previous error diff --git a/src/test/ui/issues/issue-67552.stderr b/src/test/ui/issues/issue-67552.stderr index 8243e52039d48..f3e73399b57ce 100644 --- a/src/test/ui/issues/issue-67552.stderr +++ b/src/test/ui/issues/issue-67552.stderr @@ -1,4 +1,4 @@ -error: reached the recursion limit while instantiating `rec::<&mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut Empty>` +error: reached the recursion limit while instantiating `rec::<&mut &mut &mut &mut &mut &... &mut &mut &mut &mut &mut Empty>` --> $DIR/issue-67552.rs:27:9 | LL | rec(identity(&mut it)) diff --git a/src/test/ui/issues/issue-8727.stderr b/src/test/ui/issues/issue-8727.stderr index dd57e69f2cff0..279e3ffbb4a41 100644 --- a/src/test/ui/issues/issue-8727.stderr +++ b/src/test/ui/issues/issue-8727.stderr @@ -9,7 +9,7 @@ LL | generic::>(); = note: `#[warn(unconditional_recursion)]` on by default = help: a `loop` may express intention better if this is on purpose -error: reached the recursion limit while instantiating `generic::>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` +error: reached the recursion limit while instantiating `generic::>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` --> $DIR/issue-8727.rs:7:5 | LL | generic::>(); diff --git a/src/test/ui/recursion/issue-38591-non-regular-dropck-recursion.stderr b/src/test/ui/recursion/issue-38591-non-regular-dropck-recursion.stderr index 536e26d295569..4d77b3d295c00 100644 --- a/src/test/ui/recursion/issue-38591-non-regular-dropck-recursion.stderr +++ b/src/test/ui/recursion/issue-38591-non-regular-dropck-recursion.stderr @@ -1,4 +1,4 @@ -error: reached the recursion limit while instantiating `drop_in_place::> - shim(Some(S))` +error: reached the recursion limit while instantiating `drop_in_place::))` --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL | LL | / pub unsafe fn drop_in_place(to_drop: *mut T) { diff --git a/src/test/ui/recursion/recursion.stderr b/src/test/ui/recursion/recursion.stderr index db4c99eeb8b16..085bf82ef8b93 100644 --- a/src/test/ui/recursion/recursion.stderr +++ b/src/test/ui/recursion/recursion.stderr @@ -1,4 +1,4 @@ -error: reached the recursion limit while instantiating `test::>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` +error: reached the recursion limit while instantiating `test::>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` --> $DIR/recursion.rs:17:11 | LL | _ => {test (n-1, i+1, Cons {head:2*i+1, tail:first}, Cons{head:i*i, tail:second})} diff --git a/src/test/ui/type_length_limit.rs b/src/test/ui/type_length_limit.rs index 1f1c8ad962690..921cded5037b6 100644 --- a/src/test/ui/type_length_limit.rs +++ b/src/test/ui/type_length_limit.rs @@ -4,7 +4,7 @@ // Test that the type length limit can be changed. #![allow(dead_code)] -#![type_length_limit="256"] +#![type_length_limit="4"] macro_rules! link { ($id:ident, $t:ty) => { diff --git a/src/test/ui/type_length_limit.stderr b/src/test/ui/type_length_limit.stderr index f12f259d2f6e2..cf3d64d734ba0 100644 --- a/src/test/ui/type_length_limit.stderr +++ b/src/test/ui/type_length_limit.stderr @@ -1,10 +1,10 @@ -error: reached the type-length limit while instantiating `std::mem::drop::>` +error: reached the type-length limit while instantiating `std::mem::drop::>` --> $SRC_DIR/core/src/mem/mod.rs:LL:COL | LL | pub fn drop(_x: T) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: consider adding a `#![type_length_limit="1094"]` attribute to your crate + = note: consider adding a `#![type_length_limit="8"]` attribute to your crate error: aborting due to previous error