Skip to content

Optimize dropck #64595

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Oct 17, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -3394,6 +3394,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