Skip to content

Commit d3476f4

Browse files
arielb1Ariel Ben-Yehuda
authored and
Ariel Ben-Yehuda
committed
cache ADT dtorck results
This avoids visiting the fields of all structs multiple times, improving item-bodies checking time by 10% (!).
1 parent 5412587 commit d3476f4

File tree

8 files changed

+299
-398
lines changed

8 files changed

+299
-398
lines changed

src/librustc/dep_graph/dep_node.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ pub enum DepNode<D: Clone + Debug> {
9191
IsForeignItem(D),
9292
TypeParamPredicates((D, D)),
9393
SizedConstraint(D),
94+
DtorckConstraint(D),
9495
AdtDestructor(D),
9596
AssociatedItemDefIds(D),
9697
InherentImpls(D),
@@ -228,6 +229,7 @@ impl<D: Clone + Debug> DepNode<D> {
228229
Some(TypeParamPredicates((try_opt!(op(item)), try_opt!(op(param)))))
229230
}
230231
SizedConstraint(ref d) => op(d).map(SizedConstraint),
232+
DtorckConstraint(ref d) => op(d).map(DtorckConstraint),
231233
AdtDestructor(ref d) => op(d).map(AdtDestructor),
232234
AssociatedItemDefIds(ref d) => op(d).map(AssociatedItemDefIds),
233235
InherentImpls(ref d) => op(d).map(InherentImpls),

src/librustc/diagnostics.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1829,6 +1829,7 @@ register_diagnostics! {
18291829
E0314, // closure outlives stack frame
18301830
E0315, // cannot invoke closure outside of its lifetime
18311831
E0316, // nested quantification of lifetimes
1832+
E0320, // recursive overflow during dropck
18321833
E0473, // dereference of reference outside its lifetime
18331834
E0474, // captured variable `..` does not outlive the enclosing closure
18341835
E0475, // index of slice outside its lifetime

src/librustc/ty/maps.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,13 @@ impl<'tcx> Value<'tcx> for Ty<'tcx> {
107107
}
108108
}
109109

110+
111+
impl<'tcx> Value<'tcx> for ty::DtorckConstraint<'tcx> {
112+
fn from_cycle_error<'a>(_: TyCtxt<'a, 'tcx, 'tcx>) -> Self {
113+
Self::empty()
114+
}
115+
}
116+
110117
pub struct CycleError<'a, 'tcx: 'a> {
111118
span: Span,
112119
cycle: RefMut<'a, [(Span, Query<'tcx>)]>,
@@ -397,6 +404,7 @@ define_maps! { <'tcx>
397404
pub adt_def: ItemSignature(DefId) -> &'tcx ty::AdtDef,
398405
pub adt_destructor: AdtDestructor(DefId) -> Option<ty::Destructor>,
399406
pub adt_sized_constraint: SizedConstraint(DefId) -> &'tcx [Ty<'tcx>],
407+
pub adt_dtorck_constraint: DtorckConstraint(DefId) -> ty::DtorckConstraint<'tcx>,
400408

401409
/// True if this is a foreign item (i.e., linked via `extern { ... }`).
402410
pub is_foreign_item: IsForeignItem(DefId) -> bool,

src/librustc/ty/mod.rs

Lines changed: 80 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,15 @@ use ty;
3131
use ty::subst::{Subst, Substs};
3232
use ty::util::IntTypeExt;
3333
use ty::walk::TypeWalker;
34-
use util::nodemap::{NodeSet, DefIdMap, FxHashMap};
34+
use util::common::ErrorReported;
35+
use util::nodemap::{NodeSet, DefIdMap, FxHashMap, FxHashSet};
3536

3637
use serialize::{self, Encodable, Encoder};
3738
use std::cell::{Cell, RefCell, Ref};
3839
use std::collections::BTreeMap;
3940
use std::cmp;
4041
use std::hash::{Hash, Hasher};
42+
use std::iter::FromIterator;
4143
use std::ops::Deref;
4244
use std::rc::Rc;
4345
use std::slice;
@@ -1332,17 +1334,6 @@ impl<'a, 'tcx> ParameterEnvironment<'tcx> {
13321334
pub struct Destructor {
13331335
/// The def-id of the destructor method
13341336
pub did: DefId,
1335-
/// Invoking the destructor of a dtorck type during usual cleanup
1336-
/// (e.g. the glue emitted for stack unwinding) requires all
1337-
/// lifetimes in the type-structure of `adt` to strictly outlive
1338-
/// the adt value itself.
1339-
///
1340-
/// If `adt` is not dtorck, then the adt's destructor can be
1341-
/// invoked even when there are lifetimes in the type-structure of
1342-
/// `adt` that do not strictly outlive the adt value itself.
1343-
/// (This allows programs to make cyclic structures without
1344-
/// resorting to unsafe means; see RFCs 769 and 1238).
1345-
pub is_dtorck: bool,
13461337
}
13471338

13481339
bitflags! {
@@ -1609,14 +1600,6 @@ impl<'a, 'gcx, 'tcx> AdtDef {
16091600
}
16101601
}
16111602

1612-
/// Returns whether this is a dtorck type. If this returns
1613-
/// true, this type being safe for destruction requires it to be
1614-
/// alive; Otherwise, only the contents are required to be.
1615-
#[inline]
1616-
pub fn is_dtorck(&'gcx self, tcx: TyCtxt) -> bool {
1617-
self.destructor(tcx).map_or(false, |d| d.is_dtorck)
1618-
}
1619-
16201603
/// Returns whether this type is #[fundamental] for the purposes
16211604
/// of coherence checking.
16221605
#[inline]
@@ -2708,6 +2691,38 @@ fn adt_sized_constraint<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
27082691
result
27092692
}
27102693

2694+
/// Calculates the dtorck constraint for a type.
2695+
fn adt_dtorck_constraint<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
2696+
def_id: DefId)
2697+
-> DtorckConstraint<'tcx> {
2698+
let def = tcx.lookup_adt_def(def_id);
2699+
let span = tcx.def_span(def_id);
2700+
debug!("dtorck_constraint: {:?}", def);
2701+
2702+
if def.is_phantom_data() {
2703+
let result = DtorckConstraint {
2704+
outlives: vec![],
2705+
dtorck_types: vec![
2706+
tcx.mk_param_from_def(&tcx.item_generics(def_id).types[0])
2707+
]
2708+
};
2709+
debug!("dtorck_constraint: {:?} => {:?}", def, result);
2710+
return result;
2711+
}
2712+
2713+
let mut result = def.all_fields()
2714+
.map(|field| tcx.item_type(field.did))
2715+
.map(|fty| tcx.dtorck_constraint_for_ty(span, fty, 0, fty))
2716+
.collect::<Result<DtorckConstraint, ErrorReported>>()
2717+
.unwrap_or(DtorckConstraint::empty());
2718+
result.outlives.extend(tcx.destructor_constraints(def));
2719+
result.dedup();
2720+
2721+
debug!("dtorck_constraint: {:?} => {:?}", def, result);
2722+
2723+
result
2724+
}
2725+
27112726
fn associated_item_def_ids<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
27122727
def_id: DefId)
27132728
-> Rc<Vec<DefId>> {
@@ -2736,13 +2751,15 @@ pub fn provide(providers: &mut ty::maps::Providers) {
27362751
associated_item,
27372752
associated_item_def_ids,
27382753
adt_sized_constraint,
2754+
adt_dtorck_constraint,
27392755
..*providers
27402756
};
27412757
}
27422758

27432759
pub fn provide_extern(providers: &mut ty::maps::Providers) {
27442760
*providers = ty::maps::Providers {
27452761
adt_sized_constraint,
2762+
adt_dtorck_constraint,
27462763
..*providers
27472764
};
27482765
}
@@ -2758,3 +2775,46 @@ pub fn provide_extern(providers: &mut ty::maps::Providers) {
27582775
pub struct CrateInherentImpls {
27592776
pub inherent_impls: DefIdMap<Rc<Vec<DefId>>>,
27602777
}
2778+
2779+
/// A set of constraints that need to be satisfied in order for
2780+
/// a type to be valid for destruction.
2781+
#[derive(Clone, Debug)]
2782+
pub struct DtorckConstraint<'tcx> {
2783+
/// Types that are required to be alive in order for this
2784+
/// type to be valid for destruction.
2785+
pub outlives: Vec<ty::subst::Kind<'tcx>>,
2786+
/// Types that could not be resolved: projections and params.
2787+
pub dtorck_types: Vec<Ty<'tcx>>,
2788+
}
2789+
2790+
impl<'tcx> FromIterator<DtorckConstraint<'tcx>> for DtorckConstraint<'tcx>
2791+
{
2792+
fn from_iter<I: IntoIterator<Item=DtorckConstraint<'tcx>>>(iter: I) -> Self {
2793+
let mut result = Self::empty();
2794+
2795+
for constraint in iter {
2796+
result.outlives.extend(constraint.outlives);
2797+
result.dtorck_types.extend(constraint.dtorck_types);
2798+
}
2799+
2800+
result
2801+
}
2802+
}
2803+
2804+
2805+
impl<'tcx> DtorckConstraint<'tcx> {
2806+
fn empty() -> DtorckConstraint<'tcx> {
2807+
DtorckConstraint {
2808+
outlives: vec![],
2809+
dtorck_types: vec![]
2810+
}
2811+
}
2812+
2813+
fn dedup<'a>(&mut self) {
2814+
let mut outlives = FxHashSet();
2815+
let mut dtorck_types = FxHashSet();
2816+
2817+
self.outlives.retain(|&val| outlives.replace(val).is_none());
2818+
self.dtorck_types.retain(|&val| dtorck_types.replace(val).is_none());
2819+
}
2820+
}

src/librustc/ty/util.rs

Lines changed: 161 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use ty::{self, Ty, TyCtxt, TypeAndMut, TypeFlags, TypeFoldable};
1919
use ty::ParameterEnvironment;
2020
use ty::fold::TypeVisitor;
2121
use ty::layout::{Layout, LayoutError};
22+
use ty::subst::{Subst, Kind};
2223
use ty::TypeVariants::*;
2324
use util::common::ErrorReported;
2425
use util::nodemap::{FxHashMap, FxHashSet};
@@ -385,6 +386,27 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
385386
None => return None,
386387
};
387388

389+
Some(ty::Destructor { did: dtor_did })
390+
}
391+
392+
/// Return the set of types that are required to be alive in
393+
/// order to run the destructor of `def` (see RFCs 769 and
394+
/// 1238).
395+
///
396+
/// Note that this returns only the constraints for the
397+
/// destructor of `def` itself. For the destructors of the
398+
/// contents, you need `adt_dtorck_constraint`.
399+
pub fn destructor_constraints(self, def: &'tcx ty::AdtDef)
400+
-> Vec<ty::subst::Kind<'tcx>>
401+
{
402+
let dtor = match def.destructor(self) {
403+
None => {
404+
debug!("destructor_constraints({:?}) - no dtor", def.did);
405+
return vec![]
406+
}
407+
Some(dtor) => dtor.did
408+
};
409+
388410
// RFC 1238: if the destructor method is tagged with the
389411
// attribute `unsafe_destructor_blind_to_params`, then the
390412
// compiler is being instructed to *assume* that the
@@ -394,11 +416,147 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
394416
// Such access can be in plain sight (e.g. dereferencing
395417
// `*foo.0` of `Foo<'a>(&'a u32)`) or indirectly hidden
396418
// (e.g. calling `foo.0.clone()` of `Foo<T:Clone>`).
397-
let is_dtorck = !self.has_attr(dtor_did, "unsafe_destructor_blind_to_params");
398-
Some(ty::Destructor { did: dtor_did, is_dtorck: is_dtorck })
419+
if self.has_attr(dtor, "unsafe_destructor_blind_to_params") {
420+
debug!("destructor_constraint({:?}) - blind", def.did);
421+
return vec![];
422+
}
423+
424+
let impl_def_id = self.associated_item(dtor).container.id();
425+
let impl_generics = self.item_generics(impl_def_id);
426+
427+
// We have a destructor - all the parameters that are not
428+
// pure_wrt_drop (i.e, don't have a #[may_dangle] attribute)
429+
// must be live.
430+
431+
// We need to return the list of parameters from the ADTs
432+
// generics/substs that correspond to impure parameters on the
433+
// impl's generics. This is a bit ugly, but conceptually simple:
434+
//
435+
// Suppose our ADT looks like the following
436+
//
437+
// struct S<X, Y, Z>(X, Y, Z);
438+
//
439+
// and the impl is
440+
//
441+
// impl<#[may_dangle] P0, P1, P2> Drop for S<P1, P2, P0>
442+
//
443+
// We want to return the parameters (X, Y). For that, we match
444+
// up the item-substs <X, Y, Z> with the substs on the impl ADT,
445+
// <P1, P2, P0>, and then look up which of the impl substs refer to
446+
// parameters marked as pure.
447+
448+
let impl_substs = match self.item_type(impl_def_id).sty {
449+
ty::TyAdt(def_, substs) if def_ == def => substs,
450+
_ => bug!()
451+
};
452+
453+
let item_substs = match self.item_type(def.did).sty {
454+
ty::TyAdt(def_, substs) if def_ == def => substs,
455+
_ => bug!()
456+
};
457+
458+
let result = item_substs.iter().zip(impl_substs.iter())
459+
.filter(|&(_, &k)| {
460+
if let Some(&ty::Region::ReEarlyBound(ref ebr)) = k.as_region() {
461+
!impl_generics.region_param(ebr).pure_wrt_drop
462+
} else if let Some(&ty::TyS {
463+
sty: ty::TypeVariants::TyParam(ref pt), ..
464+
}) = k.as_type() {
465+
!impl_generics.type_param(pt).pure_wrt_drop
466+
} else {
467+
// not a type or region param - this should be reported
468+
// as an error.
469+
false
470+
}
471+
}).map(|(&item_param, _)| item_param).collect();
472+
debug!("destructor_constraint({:?}) = {:?}", def.did, result);
473+
result
474+
}
475+
476+
/// Return a set of constraints that needs to be satisfied in
477+
/// order for `ty` to be valid for destruction.
478+
pub fn dtorck_constraint_for_ty(self,
479+
span: Span,
480+
for_ty: Ty<'tcx>,
481+
depth: usize,
482+
ty: Ty<'tcx>)
483+
-> Result<ty::DtorckConstraint<'tcx>, ErrorReported>
484+
{
485+
debug!("dtorck_constraint_for_ty({:?}, {:?}, {:?}, {:?})",
486+
span, for_ty, depth, ty);
487+
488+
if depth >= self.sess.recursion_limit.get() {
489+
let mut err = struct_span_err!(
490+
self.sess, span, E0320,
491+
"overflow while adding drop-check rules for {}", for_ty);
492+
err.note(&format!("overflowed on {}", ty));
493+
err.emit();
494+
return Err(ErrorReported);
495+
}
496+
497+
let result = match ty.sty {
498+
ty::TyBool | ty::TyChar | ty::TyInt(_) | ty::TyUint(_) |
499+
ty::TyFloat(_) | ty::TyStr | ty::TyNever |
500+
ty::TyRawPtr(..) | ty::TyRef(..) | ty::TyFnDef(..) | ty::TyFnPtr(_) => {
501+
// these types never have a destructor
502+
Ok(ty::DtorckConstraint::empty())
503+
}
504+
505+
ty::TyArray(ety, _) | ty::TySlice(ety) => {
506+
// single-element containers, behave like their element
507+
self.dtorck_constraint_for_ty(span, for_ty, depth+1, ety)
508+
}
509+
510+
ty::TyTuple(tys, _) => {
511+
tys.iter().map(|ty| {
512+
self.dtorck_constraint_for_ty(span, for_ty, depth+1, ty)
513+
}).collect()
514+
}
515+
516+
ty::TyClosure(def_id, substs) => {
517+
substs.upvar_tys(def_id, self).map(|ty| {
518+
self.dtorck_constraint_for_ty(span, for_ty, depth+1, ty)
519+
}).collect()
520+
}
521+
522+
ty::TyAdt(def, substs) => {
523+
let ty::DtorckConstraint {
524+
dtorck_types, outlives
525+
} = ty::queries::adt_dtorck_constraint::get(self, span, def.did);
526+
Ok(ty::DtorckConstraint {
527+
// FIXME: we can try to recursively `dtorck_constraint_on_ty`
528+
// there, but that needs some way to handle cycles.
529+
dtorck_types: dtorck_types.subst(self, substs),
530+
outlives: outlives.subst(self, substs)
531+
})
532+
}
533+
534+
// Objects must be alive in order for their destructor
535+
// to be called.
536+
ty::TyDynamic(..) => Ok(ty::DtorckConstraint {
537+
outlives: vec![Kind::from(ty)],
538+
dtorck_types: vec![],
539+
}),
540+
541+
// Types that can't be resolved. Pass them forward.
542+
ty::TyProjection(..) | ty::TyAnon(..) | ty::TyParam(..) => {
543+
Ok(ty::DtorckConstraint {
544+
outlives: vec![],
545+
dtorck_types: vec![ty],
546+
})
547+
}
548+
549+
ty::TyInfer(..) | ty::TyError => {
550+
self.sess.delay_span_bug(span, "unresolved type in dtorck");
551+
Err(ErrorReported)
552+
}
553+
};
554+
555+
debug!("dtorck_constraint_for_ty({:?}) = {:?}", ty, result);
556+
result
399557
}
400558

401-
pub fn closure_base_def_id(&self, def_id: DefId) -> DefId {
559+
pub fn closure_base_def_id(self, def_id: DefId) -> DefId {
402560
let mut def_id = def_id;
403561
while self.def_key(def_id).disambiguated_data.data == DefPathData::ClosureExpr {
404562
def_id = self.parent_def_id(def_id).unwrap_or_else(|| {

0 commit comments

Comments
 (0)