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.
This commit is contained in:
commit
fdc3405c20
22 changed files with 344 additions and 82 deletions
|
@ -3605,6 +3605,7 @@ dependencies = [
|
||||||
name = "rustc_infer"
|
name = "rustc_infer"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"arrayvec",
|
||||||
"rustc_ast",
|
"rustc_ast",
|
||||||
"rustc_data_structures",
|
"rustc_data_structures",
|
||||||
"rustc_errors",
|
"rustc_errors",
|
||||||
|
@ -3744,6 +3745,7 @@ dependencies = [
|
||||||
name = "rustc_middle"
|
name = "rustc_middle"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"arrayvec",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"chalk-ir",
|
"chalk-ir",
|
||||||
"measureme",
|
"measureme",
|
||||||
|
|
|
@ -21,4 +21,5 @@ rustc_serialize = { path = "../rustc_serialize" }
|
||||||
rustc_span = { path = "../rustc_span" }
|
rustc_span = { path = "../rustc_span" }
|
||||||
rustc_target = { path = "../rustc_target" }
|
rustc_target = { path = "../rustc_target" }
|
||||||
smallvec = { version = "1.0", features = ["union", "may_dangle"] }
|
smallvec = { version = "1.0", features = ["union", "may_dangle"] }
|
||||||
|
arrayvec = { version = "0.5.1", default-features = false }
|
||||||
rustc_ast = { path = "../rustc_ast" }
|
rustc_ast = { path = "../rustc_ast" }
|
||||||
|
|
|
@ -31,6 +31,9 @@ use super::unify_key::replace_if_possible;
|
||||||
use super::unify_key::{ConstVarValue, ConstVariableValue};
|
use super::unify_key::{ConstVarValue, ConstVariableValue};
|
||||||
use super::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
|
use super::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
|
||||||
use super::{InferCtxt, MiscVariable, TypeTrace};
|
use super::{InferCtxt, MiscVariable, TypeTrace};
|
||||||
|
use arrayvec::ArrayVec;
|
||||||
|
use rustc_data_structures::fx::FxHashMap;
|
||||||
|
use std::hash::Hash;
|
||||||
|
|
||||||
use crate::traits::{Obligation, PredicateObligations};
|
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_middle::ty::{IntType, UintType};
|
||||||
use rustc_span::DUMMY_SP;
|
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)]
|
#[derive(Clone)]
|
||||||
pub struct CombineFields<'infcx, 'tcx> {
|
pub struct CombineFields<'infcx, 'tcx> {
|
||||||
pub infcx: &'infcx InferCtxt<'infcx, 'tcx>,
|
pub infcx: &'infcx InferCtxt<'infcx, 'tcx>,
|
||||||
|
@ -379,6 +439,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
|
||||||
needs_wf: false,
|
needs_wf: false,
|
||||||
root_ty: ty,
|
root_ty: ty,
|
||||||
param_env: self.param_env,
|
param_env: self.param_env,
|
||||||
|
cache: MiniMap::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let ty = match generalize.relate(ty, ty) {
|
let ty = match generalize.relate(ty, ty) {
|
||||||
|
@ -438,6 +499,8 @@ struct Generalizer<'cx, 'tcx> {
|
||||||
root_ty: Ty<'tcx>,
|
root_ty: Ty<'tcx>,
|
||||||
|
|
||||||
param_env: ty::ParamEnv<'tcx>,
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
|
|
||||||
|
cache: MiniMap<Ty<'tcx>, RelateResult<'tcx, Ty<'tcx>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Result from a generalization operation. This includes
|
/// 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>> {
|
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 ==
|
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);
|
debug!("generalize: t={:?}", t);
|
||||||
|
|
||||||
// Check to see whether the type we are generalizing references
|
// Check to see whether the type we are generalizing references
|
||||||
// any other type variable related to `vid` via
|
// any other type variable related to `vid` via
|
||||||
// subtyping. This is basically our "occurs check", preventing
|
// subtyping. This is basically our "occurs check", preventing
|
||||||
// us from creating infinitely sized types.
|
// us from creating infinitely sized types.
|
||||||
match *t.kind() {
|
let result = match *t.kind() {
|
||||||
ty::Infer(ty::TyVar(vid)) => {
|
ty::Infer(ty::TyVar(vid)) => {
|
||||||
let vid = self.infcx.inner.borrow_mut().type_variables().root_var(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);
|
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)
|
Ok(t)
|
||||||
}
|
}
|
||||||
_ => relate::super_relate_tys(self, t, t),
|
_ => relate::super_relate_tys(self, t, t),
|
||||||
}
|
};
|
||||||
|
|
||||||
|
self.cache.insert(t, result.clone());
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn regions(
|
fn regions(
|
||||||
|
|
|
@ -3,6 +3,7 @@ use crate::infer::{GenericKind, VerifyBound};
|
||||||
use rustc_data_structures::captures::Captures;
|
use rustc_data_structures::captures::Captures;
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst};
|
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst};
|
||||||
|
use rustc_middle::ty::walk::MiniSet;
|
||||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||||
|
|
||||||
/// The `TypeOutlives` struct has the job of "lowering" a `T: 'a`
|
/// 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
|
/// Returns a "verify bound" that encodes what we know about
|
||||||
/// `generic` and the regions it outlives.
|
/// `generic` and the regions it outlives.
|
||||||
pub fn generic_bound(&self, generic: GenericKind<'tcx>) -> VerifyBound<'tcx> {
|
pub fn generic_bound(&self, generic: GenericKind<'tcx>) -> VerifyBound<'tcx> {
|
||||||
|
let mut visited = MiniSet::new();
|
||||||
match generic {
|
match generic {
|
||||||
GenericKind::Param(param_ty) => self.param_bound(param_ty),
|
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() {
|
match *ty.kind() {
|
||||||
ty::Param(p) => self.param_bound(p),
|
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) => {
|
ty::FnDef(_, substs) => {
|
||||||
// HACK(eddyb) ignore lifetimes found shallowly in `substs`.
|
// HACK(eddyb) ignore lifetimes found shallowly in `substs`.
|
||||||
// This is inconsistent with `ty::Adt` (including all substs),
|
// This is inconsistent with `ty::Adt` (including all substs),
|
||||||
|
@ -50,9 +58,9 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
|
||||||
let mut bounds = substs
|
let mut bounds = substs
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|child| match child.unpack() {
|
.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::Lifetime(_) => None,
|
||||||
GenericArgKind::Const(_) => Some(self.recursive_bound(child)),
|
GenericArgKind::Const(_) => Some(self.recursive_bound(child, visited)),
|
||||||
})
|
})
|
||||||
.filter(|bound| {
|
.filter(|bound| {
|
||||||
// Remove bounds that must hold, since they are not interesting.
|
// 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)
|
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);
|
debug!("projection_bound(projection_ty={:?})", projection_ty);
|
||||||
|
|
||||||
let projection_ty_as_ty =
|
let projection_ty_as_ty =
|
||||||
|
@ -166,21 +178,25 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
|
||||||
|
|
||||||
// see the extensive comment in projection_must_outlive
|
// see the extensive comment in projection_must_outlive
|
||||||
let ty = self.tcx.mk_projection(projection_ty.item_def_id, projection_ty.substs);
|
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)
|
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
|
let mut bounds = parent
|
||||||
.walk_shallow()
|
.walk_shallow(visited)
|
||||||
.filter_map(|child| match child.unpack() {
|
.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) => {
|
GenericArgKind::Lifetime(lt) => {
|
||||||
// Ignore late-bound regions.
|
// Ignore late-bound regions.
|
||||||
if !lt.is_late_bound() { Some(VerifyBound::OutlivedBy(lt)) } else { None }
|
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| {
|
.filter(|bound| {
|
||||||
// Remove bounds that must hold, since they are not interesting.
|
// Remove bounds that must hold, since they are not interesting.
|
||||||
|
|
|
@ -28,5 +28,6 @@ rustc_ast = { path = "../rustc_ast" }
|
||||||
rustc_span = { path = "../rustc_span" }
|
rustc_span = { path = "../rustc_span" }
|
||||||
chalk-ir = "0.21.0"
|
chalk-ir = "0.21.0"
|
||||||
smallvec = { version = "1.0", features = ["union", "may_dangle"] }
|
smallvec = { version = "1.0", features = ["union", "may_dangle"] }
|
||||||
|
arrayvec = { version = "0.5.1", default-features = false }
|
||||||
measureme = "0.7.1"
|
measureme = "0.7.1"
|
||||||
rustc_session = { path = "../rustc_session" }
|
rustc_session = { path = "../rustc_session" }
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
// RFC for reference.
|
// RFC for reference.
|
||||||
|
|
||||||
use crate::ty::subst::{GenericArg, GenericArgKind};
|
use crate::ty::subst::{GenericArg, GenericArgKind};
|
||||||
|
use crate::ty::walk::MiniSet;
|
||||||
use crate::ty::{self, Ty, TyCtxt, TypeFoldable};
|
use crate::ty::{self, Ty, TyCtxt, TypeFoldable};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
|
@ -50,12 +51,18 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||||
/// Push onto `out` all the things that must outlive `'a` for the condition
|
/// 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**.
|
/// `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]>) {
|
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);
|
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"
|
// Descend through the types, looking for the various "base"
|
||||||
// components and collecting them into `out`. This is not written
|
// components and collecting them into `out`. This is not written
|
||||||
// with `collect()` because of the need to sometimes skip subtrees
|
// 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 {
|
for child in substs {
|
||||||
match child.unpack() {
|
match child.unpack() {
|
||||||
GenericArgKind::Type(ty) => {
|
GenericArgKind::Type(ty) => {
|
||||||
compute_components(tcx, ty, out);
|
compute_components(tcx, ty, out, visited);
|
||||||
}
|
}
|
||||||
GenericArgKind::Lifetime(_) => {}
|
GenericArgKind::Lifetime(_) => {}
|
||||||
GenericArgKind::Const(_) => {
|
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, _) => {
|
ty::Array(element, _) => {
|
||||||
// Don't look into the len const as it doesn't affect regions
|
// 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) => {
|
ty::Closure(_, ref substs) => {
|
||||||
for upvar_ty in substs.as_closure().upvar_tys() {
|
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, _) => {
|
ty::Generator(_, ref substs, _) => {
|
||||||
// Same as the closure case
|
// Same as the closure case
|
||||||
for upvar_ty in substs.as_generator().upvar_tys() {
|
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
|
// 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
|
// OutlivesProjectionComponents. Continue walking
|
||||||
// through and constrain Pi.
|
// through and constrain Pi.
|
||||||
let mut subcomponents = smallvec![];
|
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()));
|
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
|
// the "bound regions list". In our representation, no such
|
||||||
// list is maintained explicitly, because bound regions
|
// list is maintained explicitly, because bound regions
|
||||||
// themselves can be readily identified.
|
// 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>,
|
tcx: TyCtxt<'tcx>,
|
||||||
parent: GenericArg<'tcx>,
|
parent: GenericArg<'tcx>,
|
||||||
out: &mut SmallVec<[Component<'tcx>; 4]>,
|
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() {
|
match child.unpack() {
|
||||||
GenericArgKind::Type(ty) => {
|
GenericArgKind::Type(ty) => {
|
||||||
compute_components(tcx, ty, out);
|
compute_components(tcx, ty, out, visited);
|
||||||
}
|
}
|
||||||
GenericArgKind::Lifetime(lt) => {
|
GenericArgKind::Lifetime(lt) => {
|
||||||
// Ignore late-bound regions.
|
// Ignore late-bound regions.
|
||||||
|
@ -199,7 +208,7 @@ fn compute_components_recursive(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GenericArgKind::Const(_) => {
|
GenericArgKind::Const(_) => {
|
||||||
compute_components_recursive(tcx, child, out);
|
compute_components_recursive(tcx, child, out, visited);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ use crate::ty::{self, DefIdTree, Ty, TyCtxt};
|
||||||
use rustc_data_structures::fx::FxHashSet;
|
use rustc_data_structures::fx::FxHashSet;
|
||||||
use rustc_hir::def_id::{CrateNum, DefId};
|
use rustc_hir::def_id::{CrateNum, DefId};
|
||||||
use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData};
|
use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData};
|
||||||
|
use rustc_middle::ty::walk::MiniSet;
|
||||||
|
|
||||||
// `pretty` is a separate module only for organization.
|
// `pretty` is a separate module only for organization.
|
||||||
mod pretty;
|
mod pretty;
|
||||||
|
@ -263,22 +264,34 @@ pub trait Printer<'tcx>: Sized {
|
||||||
/// function tries to find a "characteristic `DefId`" for a
|
/// function tries to find a "characteristic `DefId`" for a
|
||||||
/// type. It's just a heuristic so it makes some questionable
|
/// type. It's just a heuristic so it makes some questionable
|
||||||
/// decisions and we may want to adjust it later.
|
/// 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() {
|
match *ty.kind() {
|
||||||
ty::Adt(adt_def, _) => Some(adt_def.did),
|
ty::Adt(adt_def, _) => Some(adt_def.did),
|
||||||
|
|
||||||
ty::Dynamic(data, ..) => data.principal_def_id(),
|
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::Ref(_, ty, _) => characteristic_def_id_of_type(ty),
|
|
||||||
|
|
||||||
ty::Tuple(ref tys) => {
|
|
||||||
tys.iter().find_map(|ty| characteristic_def_id_of_type(ty.expect_ty()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ty::RawPtr(mt) => characteristic_def_id_of_type_cached(mt.ty, visited),
|
||||||
|
|
||||||
|
ty::Ref(_, ty, _) => characteristic_def_id_of_type_cached(ty, visited),
|
||||||
|
|
||||||
|
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::FnDef(def_id, _)
|
||||||
| ty::Closure(def_id, _)
|
| ty::Closure(def_id, _)
|
||||||
| ty::Generator(def_id, _, _)
|
| ty::Generator(def_id, _, _)
|
||||||
|
@ -302,6 +315,9 @@ pub fn characteristic_def_id_of_type(ty: Ty<'_>) -> Option<DefId> {
|
||||||
| ty::Float(_) => None,
|
| 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 {
|
impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for ty::RegionKind {
|
||||||
type Output = P::Region;
|
type Output = P::Region;
|
||||||
|
|
|
@ -1264,6 +1264,7 @@ pub struct FmtPrinterData<'a, 'tcx, F> {
|
||||||
used_region_names: FxHashSet<Symbol>,
|
used_region_names: FxHashSet<Symbol>,
|
||||||
region_index: usize,
|
region_index: usize,
|
||||||
binder_depth: usize,
|
binder_depth: usize,
|
||||||
|
printed_type_count: usize,
|
||||||
|
|
||||||
pub region_highlight_mode: RegionHighlightMode,
|
pub region_highlight_mode: RegionHighlightMode,
|
||||||
|
|
||||||
|
@ -1294,6 +1295,7 @@ impl<F> FmtPrinter<'a, 'tcx, F> {
|
||||||
used_region_names: Default::default(),
|
used_region_names: Default::default(),
|
||||||
region_index: 0,
|
region_index: 0,
|
||||||
binder_depth: 0,
|
binder_depth: 0,
|
||||||
|
printed_type_count: 0,
|
||||||
region_highlight_mode: RegionHighlightMode::default(),
|
region_highlight_mode: RegionHighlightMode::default(),
|
||||||
name_resolver: None,
|
name_resolver: None,
|
||||||
}))
|
}))
|
||||||
|
@ -1411,8 +1413,14 @@ impl<F: fmt::Write> Printer<'tcx> for FmtPrinter<'_, 'tcx, F> {
|
||||||
self.pretty_print_region(region)
|
self.pretty_print_region(region)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_type(self, ty: Ty<'tcx>) -> Result<Self::Type, Self::Error> {
|
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)
|
self.pretty_print_type(ty)
|
||||||
|
} else {
|
||||||
|
write!(self, "...")?;
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_dyn_existential(
|
fn print_dyn_existential(
|
||||||
|
|
|
@ -3,7 +3,50 @@
|
||||||
|
|
||||||
use crate::ty;
|
use crate::ty;
|
||||||
use crate::ty::subst::{GenericArg, GenericArgKind};
|
use crate::ty::subst::{GenericArg, GenericArgKind};
|
||||||
|
use arrayvec::ArrayVec;
|
||||||
|
use rustc_data_structures::fx::FxHashSet;
|
||||||
use smallvec::{self, SmallVec};
|
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
|
// The TypeWalker's stack is hot enough that it's worth going to some effort to
|
||||||
// avoid heap allocations.
|
// avoid heap allocations.
|
||||||
|
@ -12,11 +55,20 @@ type TypeWalkerStack<'tcx> = SmallVec<[GenericArg<'tcx>; 8]>;
|
||||||
pub struct TypeWalker<'tcx> {
|
pub struct TypeWalker<'tcx> {
|
||||||
stack: TypeWalkerStack<'tcx>,
|
stack: TypeWalkerStack<'tcx>,
|
||||||
last_subtree: usize,
|
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> {
|
impl<'tcx> TypeWalker<'tcx> {
|
||||||
pub fn new(root: GenericArg<'tcx>) -> TypeWalker<'tcx> {
|
pub fn new(root: GenericArg<'tcx>) -> Self {
|
||||||
TypeWalker { stack: smallvec![root], last_subtree: 1 }
|
Self { stack: smallvec![root], last_subtree: 1, visited: MiniSet::new() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Skips the subtree corresponding to the last type
|
/// 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>> {
|
fn next(&mut self) -> Option<GenericArg<'tcx>> {
|
||||||
debug!("next(): stack={:?}", self.stack);
|
debug!("next(): stack={:?}", self.stack);
|
||||||
|
loop {
|
||||||
let next = self.stack.pop()?;
|
let next = self.stack.pop()?;
|
||||||
self.last_subtree = self.stack.len();
|
self.last_subtree = self.stack.len();
|
||||||
|
if self.visited.insert(next) {
|
||||||
push_inner(&mut self.stack, next);
|
push_inner(&mut self.stack, next);
|
||||||
debug!("next: stack={:?}", self.stack);
|
debug!("next: stack={:?}", self.stack);
|
||||||
Some(next)
|
return Some(next);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,9 +123,17 @@ impl GenericArg<'tcx> {
|
||||||
/// Iterator that walks the immediate children of `self`. Hence
|
/// Iterator that walks the immediate children of `self`. Hence
|
||||||
/// `Foo<Bar<i32>, u32>` yields the sequence `[Bar<i32>, u32]`
|
/// `Foo<Bar<i32>, u32>` yields the sequence `[Bar<i32>, u32]`
|
||||||
/// (but not `i32`, like `walk`).
|
/// (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();
|
let mut stack = SmallVec::new();
|
||||||
push_inner(&mut stack, self);
|
push_inner(&mut stack, self);
|
||||||
|
stack.retain(|a| visited.insert(*a));
|
||||||
stack.into_iter()
|
stack.into_iter()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -418,6 +418,29 @@ fn record_accesses<'a, 'tcx: 'a>(
|
||||||
inlining_map.lock_mut().record_accesses(caller, &accesses);
|
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>(
|
fn check_recursion_limit<'tcx>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
instance: Instance<'tcx>,
|
instance: Instance<'tcx>,
|
||||||
|
@ -440,7 +463,10 @@ fn check_recursion_limit<'tcx>(
|
||||||
// more than the recursion limit is assumed to be causing an
|
// more than the recursion limit is assumed to be causing an
|
||||||
// infinite expansion.
|
// infinite expansion.
|
||||||
if !tcx.sess.recursion_limit().value_within_limit(adjusted_recursion_depth) {
|
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);
|
let mut err = tcx.sess.struct_span_fatal(span, &error);
|
||||||
err.span_note(
|
err.span_note(
|
||||||
tcx.def_span(def_id),
|
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.
|
// Bail out in these cases to avoid that bad user experience.
|
||||||
if !tcx.sess.type_length_limit().value_within_limit(type_length) {
|
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!(
|
let msg = format!(
|
||||||
"reached the type-length limit while instantiating `{}`",
|
"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);
|
let mut diag = tcx.sess.struct_span_fatal(tcx.def_span(instance.def_id()), &msg);
|
||||||
diag.note(&format!(
|
diag.note(&format!(
|
||||||
|
|
|
@ -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));
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
error: reached the recursion limit while instantiating `function::<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<usize>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>`
|
error: reached the recursion limit while instantiating `function::<Option<Option<Option<...>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>`
|
||||||
--> $DIR/infinite-instantiation.rs:21:9
|
--> $DIR/infinite-instantiation.rs:21:9
|
||||||
|
|
|
|
||||||
LL | function(counter - 1, t.to_option());
|
LL | function(counter - 1, t.to_option());
|
||||||
|
|
|
@ -51,9 +51,9 @@ struct D (Box<A>);
|
||||||
|
|
||||||
impl D {
|
impl D {
|
||||||
pub fn matches<F: Fn()>(&self, f: &F) {
|
pub fn matches<F: Fn()>(&self, f: &F) {
|
||||||
//~^ ERROR reached the type-length limit while instantiating `D::matches::<[closure
|
|
||||||
let &D(ref a) = self;
|
let &D(ref a) = self;
|
||||||
a.matches(f)
|
a.matches(f)
|
||||||
|
//~^ ERROR reached the recursion limit while instantiating `A::matches::<[closure
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
error: reached the type-length limit while instantiating `D::matches::$CLOSURE`
|
error: reached the recursion limit while instantiating `A::matches::$CLOSURE`
|
||||||
--> $DIR/issue-22638.rs:53:5
|
--> $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<F: Fn()>(&self, f: &F) {
|
LL | pub fn matches<F: Fn()>(&self, f: &F) {
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
= note: consider adding a `#![type_length_limit="30408681"]` attribute to your crate
|
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
|
|
@ -12,8 +12,8 @@ trait Foo {
|
||||||
|
|
||||||
impl<T> Foo for T {
|
impl<T> Foo for T {
|
||||||
#[allow(unconditional_recursion)]
|
#[allow(unconditional_recursion)]
|
||||||
fn recurse(&self) { //~ ERROR reached the type-length limit
|
fn recurse(&self) {
|
||||||
(self, self).recurse();
|
(self, self).recurse(); //~ ERROR reached the recursion limit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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: `<T as Foo>::recurse` defined here
|
||||||
--> $DIR/issue-37311.rs:15:5
|
--> $DIR/issue-37311.rs:15:5
|
||||||
|
|
|
|
||||||
LL | fn recurse(&self) {
|
LL | fn recurse(&self) {
|
||||||
| ^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
= note: consider adding a `#![type_length_limit="2097149"]` attribute to your crate
|
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
|
|
@ -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
|
--> $DIR/issue-67552.rs:27:9
|
||||||
|
|
|
|
||||||
LL | rec(identity(&mut it))
|
LL | rec(identity(&mut it))
|
||||||
|
|
|
@ -9,7 +9,7 @@ LL | generic::<Option<T>>();
|
||||||
= note: `#[warn(unconditional_recursion)]` on by default
|
= note: `#[warn(unconditional_recursion)]` on by default
|
||||||
= help: a `loop` may express intention better if this is on purpose
|
= help: a `loop` may express intention better if this is on purpose
|
||||||
|
|
||||||
error: reached the recursion limit while instantiating `generic::<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<Option<i32>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>`
|
error: reached the recursion limit while instantiating `generic::<Option<Option<Option<O...>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>`
|
||||||
--> $DIR/issue-8727.rs:7:5
|
--> $DIR/issue-8727.rs:7:5
|
||||||
|
|
|
|
||||||
LL | generic::<Option<T>>();
|
LL | generic::<Option<T>>();
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
error: reached the recursion limit while instantiating `drop_in_place::<S<fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(u32))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))>> - shim(Some(S<fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(u32))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))>))`
|
error: reached the recursion limit while instantiating `drop_in_place::<S<fn(fn(fn(fn(fn...)))))))))))))))))))))))))))))>))`
|
||||||
--> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
|
--> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
|
||||||
|
|
|
|
||||||
LL | / pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
|
LL | / pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
error: reached the recursion limit while instantiating `test::<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Nil>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>`
|
error: reached the recursion limit while instantiating `test::<Cons<Cons<Cons<Cons<Cons<...>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>`
|
||||||
--> $DIR/recursion.rs:17:11
|
--> $DIR/recursion.rs:17:11
|
||||||
|
|
|
|
||||||
LL | _ => {test (n-1, i+1, Cons {head:2*i+1, tail:first}, Cons{head:i*i, tail:second})}
|
LL | _ => {test (n-1, i+1, Cons {head:2*i+1, tail:first}, Cons{head:i*i, tail:second})}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
// Test that the type length limit can be changed.
|
// Test that the type length limit can be changed.
|
||||||
|
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
#![type_length_limit="256"]
|
#![type_length_limit="4"]
|
||||||
|
|
||||||
macro_rules! link {
|
macro_rules! link {
|
||||||
($id:ident, $t:ty) => {
|
($id:ident, $t:ty) => {
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
error: reached the type-length limit while instantiating `std::mem::drop::<Option<((((((G,... G), (G, G, G), (G, G, G))))))>>`
|
error: reached the type-length limit while instantiating `std::mem::drop::<Option<((((...,....., ...), ..., ...), ..., ...)>>`
|
||||||
--> $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
--> $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
||||||
|
|
|
|
||||||
LL | pub fn drop<T>(_x: T) {}
|
LL | pub fn drop<T>(_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
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue