1
Fork 0

Only visit types once when walking the type tree

This fixes #72408.

Nested closures were resulting in exponential compilation time.

As a performance optimization this change introduces MiniSet,
which is a simple small storage optimized set.
This commit is contained in:
Valerii Lashmanov 2020-09-15 09:37:19 -05:00
parent 255ceeb5ff
commit 2f3296192b
10 changed files with 193 additions and 39 deletions

View file

@ -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.