Skip to content

Commit

Permalink
Auto merge of #64595 - Mark-Simulacrum:trivial-query, r=pnkfelix
Browse files Browse the repository at this point in the history
Optimize dropck

This does two things: caches the `trivial_dropck` check by making it a query, and shifts around the implementation of the primary dropck itself to avoid allocating many small vectors.
  • Loading branch information
bors committed Oct 17, 2019
2 parents ea45150 + 8de7fd8 commit b043380
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 66 deletions.
6 changes: 6 additions & 0 deletions src/librustc/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,12 @@ rustc_queries! {
cycle_delay_bug
}

query trivial_dropck_outlives(ty: Ty<'tcx>) -> bool {
anon
no_force
desc { "checking if `{:?}` has trivial dropck", ty }
}

query adt_dtorck_constraint(
_: DefId
) -> Result<DtorckConstraint<'tcx>, NoSolution> {}
Expand Down
16 changes: 12 additions & 4 deletions src/librustc/traits/query/dropck_outlives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use std::iter::FromIterator;
use syntax::source_map::Span;
use crate::ty::subst::GenericArg;
use crate::ty::{self, Ty, TyCtxt};
use crate::ty::query::Providers;

impl<'cx, 'tcx> At<'cx, 'tcx> {
/// Given a type `ty` of some value being dropped, computes a set
Expand Down Expand Up @@ -33,7 +34,7 @@ impl<'cx, 'tcx> At<'cx, 'tcx> {
// Quick check: there are a number of cases that we know do not require
// any destructor.
let tcx = self.infcx.tcx;
if trivial_dropck_outlives(tcx, ty) {
if tcx.trivial_dropck_outlives(ty) {
return InferOk {
value: vec![],
obligations: vec![],
Expand Down Expand Up @@ -207,15 +208,15 @@ pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
| ty::Error => true,

// [T; N] and [T] have same properties as T.
ty::Array(ty, _) | ty::Slice(ty) => trivial_dropck_outlives(tcx, ty),
ty::Array(ty, _) | ty::Slice(ty) => tcx.trivial_dropck_outlives(ty),

// (T1..Tn) and closures have same properties as T1..Tn --
// check if *any* of those are trivial.
ty::Tuple(ref tys) => tys.iter().all(|t| trivial_dropck_outlives(tcx, t.expect_ty())),
ty::Tuple(ref tys) => tys.iter().all(|t| tcx.trivial_dropck_outlives(t.expect_ty())),
ty::Closure(def_id, ref substs) => substs
.as_closure()
.upvar_tys(def_id, tcx)
.all(|t| trivial_dropck_outlives(tcx, t)),
.all(|t| tcx.trivial_dropck_outlives(t)),

ty::Adt(def, _) => {
if Some(def.did) == tcx.lang_items().manually_drop() {
Expand Down Expand Up @@ -243,3 +244,10 @@ pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
ty::UnnormalizedProjection(..) => bug!("only used with chalk-engine"),
}
}

crate fn provide(p: &mut Providers<'_>) {
*p = Providers {
trivial_dropck_outlives,
..*p
};
}
3 changes: 1 addition & 2 deletions src/librustc/traits/query/type_op/outlives.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use crate::infer::canonical::{Canonicalized, CanonicalizedQueryResponse};
use crate::traits::query::dropck_outlives::trivial_dropck_outlives;
use crate::traits::query::dropck_outlives::DropckOutlivesResult;
use crate::traits::query::Fallible;
use crate::ty::{ParamEnvAnd, Ty, TyCtxt};
Expand All @@ -22,7 +21,7 @@ impl super::QueryTypeOp<'tcx> for DropckOutlives<'tcx> {
tcx: TyCtxt<'tcx>,
key: &ParamEnvAnd<'tcx, Self>,
) -> Option<Self::QueryResponse> {
if trivial_dropck_outlives(tcx, key.value.dropped_ty) {
if tcx.trivial_dropck_outlives(key.value.dropped_ty) {
Some(DropckOutlivesResult::default())
} else {
None
Expand Down
1 change: 1 addition & 0 deletions src/librustc/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3403,6 +3403,7 @@ pub fn provide(providers: &mut ty::query::Providers<'_>) {
layout::provide(providers);
util::provide(providers);
constness::provide(providers);
crate::traits::query::dropck_outlives::provide(providers);
*providers = ty::query::Providers {
asyncness,
associated_item,
Expand Down
113 changes: 53 additions & 60 deletions src/librustc_traits/dropck_outlives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,22 +80,30 @@ fn dropck_outlives<'tcx>(
let mut fulfill_cx = TraitEngine::new(infcx.tcx);

let cause = ObligationCause::dummy();
let mut constraints = DtorckConstraint::empty();
while let Some((ty, depth)) = ty_stack.pop() {
let DtorckConstraint {
dtorck_types,
outlives,
overflows,
} = dtorck_constraint_for_ty(tcx, DUMMY_SP, for_ty, depth, ty)?;
info!("{} kinds, {} overflows, {} ty_stack",
result.kinds.len(), result.overflows.len(), ty_stack.len());
dtorck_constraint_for_ty(tcx, DUMMY_SP, for_ty, depth, ty, &mut constraints)?;

// "outlives" represent types/regions that may be touched
// by a destructor.
result.kinds.extend(outlives);
result.overflows.extend(overflows);
result.kinds.extend(constraints.outlives.drain(..));
result.overflows.extend(constraints.overflows.drain(..));

// If we have even one overflow, we should stop trying to evaluate further --
// chances are, the subsequent overflows for this evaluation won't provide useful
// information and will just decrease the speed at which we can emit these errors
// (since we'll be printing for just that much longer for the often enormous types
// that result here).
if result.overflows.len() >= 1 {
break;
}

// dtorck types are "types that will get dropped but which
// do not themselves define a destructor", more or less. We have
// to push them onto the stack to be expanded.
for ty in dtorck_types {
for ty in constraints.dtorck_types.drain(..) {
match infcx.at(&cause, param_env).normalize(&ty) {
Ok(Normalized {
value: ty,
Expand Down Expand Up @@ -152,21 +160,23 @@ fn dtorck_constraint_for_ty<'tcx>(
for_ty: Ty<'tcx>,
depth: usize,
ty: Ty<'tcx>,
) -> Result<DtorckConstraint<'tcx>, NoSolution> {
constraints: &mut DtorckConstraint<'tcx>,
) -> Result<(), NoSolution> {
debug!(
"dtorck_constraint_for_ty({:?}, {:?}, {:?}, {:?})",
span, for_ty, depth, ty
);

if depth >= *tcx.sess.recursion_limit.get() {
return Ok(DtorckConstraint {
outlives: vec![],
dtorck_types: vec![],
overflows: vec![ty],
});
constraints.overflows.push(ty);
return Ok(());
}

let result = match ty.kind {
if tcx.trivial_dropck_outlives(ty) {
return Ok(());
}

match ty.kind {
ty::Bool
| ty::Char
| ty::Int(_)
Expand All @@ -181,22 +191,20 @@ fn dtorck_constraint_for_ty<'tcx>(
| ty::FnPtr(_)
| ty::GeneratorWitness(..) => {
// these types never have a destructor
Ok(DtorckConstraint::empty())
}

ty::Array(ety, _) | ty::Slice(ety) => {
// single-element containers, behave like their element
dtorck_constraint_for_ty(tcx, span, for_ty, depth + 1, ety)
dtorck_constraint_for_ty(tcx, span, for_ty, depth + 1, ety, constraints)?;
}

ty::Tuple(tys) => tys.iter()
.map(|ty| dtorck_constraint_for_ty(tcx, span, for_ty, depth + 1, ty.expect_ty()))
.collect(),
ty::Tuple(tys) => for ty in tys.iter() {
dtorck_constraint_for_ty(tcx, span, for_ty, depth + 1, ty.expect_ty(), constraints)?;
},

ty::Closure(def_id, substs) => substs.as_closure()
.upvar_tys(def_id, tcx)
.map(|ty| dtorck_constraint_for_ty(tcx, span, for_ty, depth + 1, ty))
.collect(),
ty::Closure(def_id, substs) => for ty in substs.as_closure().upvar_tys(def_id, tcx) {
dtorck_constraint_for_ty(tcx, span, for_ty, depth + 1, ty, constraints)?;
}

ty::Generator(def_id, substs, _movability) => {
// rust-lang/rust#49918: types can be constructed, stored
Expand All @@ -222,17 +230,8 @@ fn dtorck_constraint_for_ty<'tcx>(
// derived from lifetimes attached to the upvars, and we
// *do* incorporate the upvars here.

let constraint = DtorckConstraint {
outlives: substs.as_generator().upvar_tys(def_id, tcx).map(|t| t.into()).collect(),
dtorck_types: vec![],
overflows: vec![],
};
debug!(
"dtorck_constraint: generator {:?} => {:?}",
def_id, constraint
);

Ok(constraint)
constraints.outlives.extend(substs.as_generator().upvar_tys(def_id, tcx)
.map(|t| -> ty::subst::GenericArg<'tcx> { t.into() }));
}

ty::Adt(def, substs) => {
Expand All @@ -241,41 +240,34 @@ fn dtorck_constraint_for_ty<'tcx>(
outlives,
overflows,
} = tcx.at(span).adt_dtorck_constraint(def.did)?;
Ok(DtorckConstraint {
// FIXME: we can try to recursively `dtorck_constraint_on_ty`
// there, but that needs some way to handle cycles.
dtorck_types: dtorck_types.subst(tcx, substs),
outlives: outlives.subst(tcx, substs),
overflows: overflows.subst(tcx, substs),
})
// FIXME: we can try to recursively `dtorck_constraint_on_ty`
// there, but that needs some way to handle cycles.
constraints.dtorck_types.extend(dtorck_types.subst(tcx, substs));
constraints.outlives.extend(outlives.subst(tcx, substs));
constraints.overflows.extend(overflows.subst(tcx, substs));
}

// Objects must be alive in order for their destructor
// to be called.
ty::Dynamic(..) => Ok(DtorckConstraint {
outlives: vec![ty.into()],
dtorck_types: vec![],
overflows: vec![],
}),
ty::Dynamic(..) => {
constraints.outlives.push(ty.into());
},

// Types that can't be resolved. Pass them forward.
ty::Projection(..) | ty::Opaque(..) | ty::Param(..) => Ok(DtorckConstraint {
outlives: vec![],
dtorck_types: vec![ty],
overflows: vec![],
}),
ty::Projection(..) | ty::Opaque(..) | ty::Param(..) => {
constraints.dtorck_types.push(ty);
},

ty::UnnormalizedProjection(..) => bug!("only used with chalk-engine"),

ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error => {
// By the time this code runs, all type variables ought to
// be fully resolved.
Err(NoSolution)
return Err(NoSolution)
}
};
}

debug!("dtorck_constraint_for_ty({:?}) = {:?}", ty, result);
result
Ok(())
}

/// Calculates the dtorck constraint for a type.
Expand All @@ -301,10 +293,11 @@ crate fn adt_dtorck_constraint(
return Ok(result);
}

let mut result = def.all_fields()
.map(|field| tcx.type_of(field.did))
.map(|fty| dtorck_constraint_for_ty(tcx, span, fty, 0, fty))
.collect::<Result<DtorckConstraint<'_>, NoSolution>>()?;
let mut result = DtorckConstraint::empty();
for field in def.all_fields() {
let fty = tcx.type_of(field.did);
dtorck_constraint_for_ty(tcx, span, fty, 0, fty, &mut result)?;
}
result.outlives.extend(tcx.destructor_constraints(def));
dedup_dtorck_constraint(&mut result);

Expand Down

0 comments on commit b043380

Please sign in to comment.