Auto merge of #64595 - Mark-Simulacrum:trivial-query, r=pnkfelix
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.
This commit is contained in:
commit
b04338087e
5 changed files with 73 additions and 66 deletions
|
@ -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> {}
|
||||
|
|
|
@ -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
|
||||
|
@ -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![],
|
||||
|
@ -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() {
|
||||
|
@ -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
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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};
|
||||
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
@ -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(_)
|
||||
|
@ -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
|
||||
|
@ -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) => {
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue