Skip to content

Commit b043380

Browse files
committed
Auto merge of rust-lang#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.
2 parents ea45150 + 8de7fd8 commit b043380

File tree

5 files changed

+73
-66
lines changed

5 files changed

+73
-66
lines changed

Diff for: src/librustc/query/mod.rs

+6
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,12 @@ rustc_queries! {
231231
cycle_delay_bug
232232
}
233233

234+
query trivial_dropck_outlives(ty: Ty<'tcx>) -> bool {
235+
anon
236+
no_force
237+
desc { "checking if `{:?}` has trivial dropck", ty }
238+
}
239+
234240
query adt_dtorck_constraint(
235241
_: DefId
236242
) -> Result<DtorckConstraint<'tcx>, NoSolution> {}

Diff for: src/librustc/traits/query/dropck_outlives.rs

+12-4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use std::iter::FromIterator;
55
use syntax::source_map::Span;
66
use crate::ty::subst::GenericArg;
77
use crate::ty::{self, Ty, TyCtxt};
8+
use crate::ty::query::Providers;
89

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

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

212213
// (T1..Tn) and closures have same properties as T1..Tn --
213214
// check if *any* of those are trivial.
214-
ty::Tuple(ref tys) => tys.iter().all(|t| trivial_dropck_outlives(tcx, t.expect_ty())),
215+
ty::Tuple(ref tys) => tys.iter().all(|t| tcx.trivial_dropck_outlives(t.expect_ty())),
215216
ty::Closure(def_id, ref substs) => substs
216217
.as_closure()
217218
.upvar_tys(def_id, tcx)
218-
.all(|t| trivial_dropck_outlives(tcx, t)),
219+
.all(|t| tcx.trivial_dropck_outlives(t)),
219220

220221
ty::Adt(def, _) => {
221222
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 {
243244
ty::UnnormalizedProjection(..) => bug!("only used with chalk-engine"),
244245
}
245246
}
247+
248+
crate fn provide(p: &mut Providers<'_>) {
249+
*p = Providers {
250+
trivial_dropck_outlives,
251+
..*p
252+
};
253+
}

Diff for: src/librustc/traits/query/type_op/outlives.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use crate::infer::canonical::{Canonicalized, CanonicalizedQueryResponse};
2-
use crate::traits::query::dropck_outlives::trivial_dropck_outlives;
32
use crate::traits::query::dropck_outlives::DropckOutlivesResult;
43
use crate::traits::query::Fallible;
54
use crate::ty::{ParamEnvAnd, Ty, TyCtxt};
@@ -22,7 +21,7 @@ impl super::QueryTypeOp<'tcx> for DropckOutlives<'tcx> {
2221
tcx: TyCtxt<'tcx>,
2322
key: &ParamEnvAnd<'tcx, Self>,
2423
) -> Option<Self::QueryResponse> {
25-
if trivial_dropck_outlives(tcx, key.value.dropped_ty) {
24+
if tcx.trivial_dropck_outlives(key.value.dropped_ty) {
2625
Some(DropckOutlivesResult::default())
2726
} else {
2827
None

Diff for: src/librustc/ty/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -3403,6 +3403,7 @@ pub fn provide(providers: &mut ty::query::Providers<'_>) {
34033403
layout::provide(providers);
34043404
util::provide(providers);
34053405
constness::provide(providers);
3406+
crate::traits::query::dropck_outlives::provide(providers);
34063407
*providers = ty::query::Providers {
34073408
asyncness,
34083409
associated_item,

Diff for: src/librustc_traits/dropck_outlives.rs

+53-60
Original file line numberDiff line numberDiff line change
@@ -80,22 +80,30 @@ fn dropck_outlives<'tcx>(
8080
let mut fulfill_cx = TraitEngine::new(infcx.tcx);
8181

8282
let cause = ObligationCause::dummy();
83+
let mut constraints = DtorckConstraint::empty();
8384
while let Some((ty, depth)) = ty_stack.pop() {
84-
let DtorckConstraint {
85-
dtorck_types,
86-
outlives,
87-
overflows,
88-
} = dtorck_constraint_for_ty(tcx, DUMMY_SP, for_ty, depth, ty)?;
85+
info!("{} kinds, {} overflows, {} ty_stack",
86+
result.kinds.len(), result.overflows.len(), ty_stack.len());
87+
dtorck_constraint_for_ty(tcx, DUMMY_SP, for_ty, depth, ty, &mut constraints)?;
8988

9089
// "outlives" represent types/regions that may be touched
9190
// by a destructor.
92-
result.kinds.extend(outlives);
93-
result.overflows.extend(overflows);
91+
result.kinds.extend(constraints.outlives.drain(..));
92+
result.overflows.extend(constraints.overflows.drain(..));
93+
94+
// If we have even one overflow, we should stop trying to evaluate further --
95+
// chances are, the subsequent overflows for this evaluation won't provide useful
96+
// information and will just decrease the speed at which we can emit these errors
97+
// (since we'll be printing for just that much longer for the often enormous types
98+
// that result here).
99+
if result.overflows.len() >= 1 {
100+
break;
101+
}
94102

95103
// dtorck types are "types that will get dropped but which
96104
// do not themselves define a destructor", more or less. We have
97105
// to push them onto the stack to be expanded.
98-
for ty in dtorck_types {
106+
for ty in constraints.dtorck_types.drain(..) {
99107
match infcx.at(&cause, param_env).normalize(&ty) {
100108
Ok(Normalized {
101109
value: ty,
@@ -152,21 +160,23 @@ fn dtorck_constraint_for_ty<'tcx>(
152160
for_ty: Ty<'tcx>,
153161
depth: usize,
154162
ty: Ty<'tcx>,
155-
) -> Result<DtorckConstraint<'tcx>, NoSolution> {
163+
constraints: &mut DtorckConstraint<'tcx>,
164+
) -> Result<(), NoSolution> {
156165
debug!(
157166
"dtorck_constraint_for_ty({:?}, {:?}, {:?}, {:?})",
158167
span, for_ty, depth, ty
159168
);
160169

161170
if depth >= *tcx.sess.recursion_limit.get() {
162-
return Ok(DtorckConstraint {
163-
outlives: vec![],
164-
dtorck_types: vec![],
165-
overflows: vec![ty],
166-
});
171+
constraints.overflows.push(ty);
172+
return Ok(());
167173
}
168174

169-
let result = match ty.kind {
175+
if tcx.trivial_dropck_outlives(ty) {
176+
return Ok(());
177+
}
178+
179+
match ty.kind {
170180
ty::Bool
171181
| ty::Char
172182
| ty::Int(_)
@@ -181,22 +191,20 @@ fn dtorck_constraint_for_ty<'tcx>(
181191
| ty::FnPtr(_)
182192
| ty::GeneratorWitness(..) => {
183193
// these types never have a destructor
184-
Ok(DtorckConstraint::empty())
185194
}
186195

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

192-
ty::Tuple(tys) => tys.iter()
193-
.map(|ty| dtorck_constraint_for_ty(tcx, span, for_ty, depth + 1, ty.expect_ty()))
194-
.collect(),
201+
ty::Tuple(tys) => for ty in tys.iter() {
202+
dtorck_constraint_for_ty(tcx, span, for_ty, depth + 1, ty.expect_ty(), constraints)?;
203+
},
195204

196-
ty::Closure(def_id, substs) => substs.as_closure()
197-
.upvar_tys(def_id, tcx)
198-
.map(|ty| dtorck_constraint_for_ty(tcx, span, for_ty, depth + 1, ty))
199-
.collect(),
205+
ty::Closure(def_id, substs) => for ty in substs.as_closure().upvar_tys(def_id, tcx) {
206+
dtorck_constraint_for_ty(tcx, span, for_ty, depth + 1, ty, constraints)?;
207+
}
200208

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

225-
let constraint = DtorckConstraint {
226-
outlives: substs.as_generator().upvar_tys(def_id, tcx).map(|t| t.into()).collect(),
227-
dtorck_types: vec![],
228-
overflows: vec![],
229-
};
230-
debug!(
231-
"dtorck_constraint: generator {:?} => {:?}",
232-
def_id, constraint
233-
);
234-
235-
Ok(constraint)
233+
constraints.outlives.extend(substs.as_generator().upvar_tys(def_id, tcx)
234+
.map(|t| -> ty::subst::GenericArg<'tcx> { t.into() }));
236235
}
237236

238237
ty::Adt(def, substs) => {
@@ -241,41 +240,34 @@ fn dtorck_constraint_for_ty<'tcx>(
241240
outlives,
242241
overflows,
243242
} = tcx.at(span).adt_dtorck_constraint(def.did)?;
244-
Ok(DtorckConstraint {
245-
// FIXME: we can try to recursively `dtorck_constraint_on_ty`
246-
// there, but that needs some way to handle cycles.
247-
dtorck_types: dtorck_types.subst(tcx, substs),
248-
outlives: outlives.subst(tcx, substs),
249-
overflows: overflows.subst(tcx, substs),
250-
})
243+
// FIXME: we can try to recursively `dtorck_constraint_on_ty`
244+
// there, but that needs some way to handle cycles.
245+
constraints.dtorck_types.extend(dtorck_types.subst(tcx, substs));
246+
constraints.outlives.extend(outlives.subst(tcx, substs));
247+
constraints.overflows.extend(overflows.subst(tcx, substs));
251248
}
252249

253250
// Objects must be alive in order for their destructor
254251
// to be called.
255-
ty::Dynamic(..) => Ok(DtorckConstraint {
256-
outlives: vec![ty.into()],
257-
dtorck_types: vec![],
258-
overflows: vec![],
259-
}),
252+
ty::Dynamic(..) => {
253+
constraints.outlives.push(ty.into());
254+
},
260255

261256
// Types that can't be resolved. Pass them forward.
262-
ty::Projection(..) | ty::Opaque(..) | ty::Param(..) => Ok(DtorckConstraint {
263-
outlives: vec![],
264-
dtorck_types: vec![ty],
265-
overflows: vec![],
266-
}),
257+
ty::Projection(..) | ty::Opaque(..) | ty::Param(..) => {
258+
constraints.dtorck_types.push(ty);
259+
},
267260

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

270263
ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error => {
271264
// By the time this code runs, all type variables ought to
272265
// be fully resolved.
273-
Err(NoSolution)
266+
return Err(NoSolution)
274267
}
275-
};
268+
}
276269

277-
debug!("dtorck_constraint_for_ty({:?}) = {:?}", ty, result);
278-
result
270+
Ok(())
279271
}
280272

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

304-
let mut result = def.all_fields()
305-
.map(|field| tcx.type_of(field.did))
306-
.map(|fty| dtorck_constraint_for_ty(tcx, span, fty, 0, fty))
307-
.collect::<Result<DtorckConstraint<'_>, NoSolution>>()?;
296+
let mut result = DtorckConstraint::empty();
297+
for field in def.all_fields() {
298+
let fty = tcx.type_of(field.did);
299+
dtorck_constraint_for_ty(tcx, span, fty, 0, fty, &mut result)?;
300+
}
308301
result.outlives.extend(tcx.destructor_constraints(def));
309302
dedup_dtorck_constraint(&mut result);
310303

0 commit comments

Comments
 (0)