Skip to content

Commit 85d9dae

Browse files
committed
perf: Reuse the Elaborator used in outlives/ty.rs
Profiling the build of https://github.com/Marwes/combine shows that ~1.3% of the time were spent resizing and rehashing the hash set used in `Elaborator`. By reusing the `Elaborator` between calls this should be mostly eliminated. It does result in some awkwardness since the `RefCell` needs to be borrowed in the returned `Iterator` but it works fine for the current code.
1 parent f65dfa2 commit 85d9dae

File tree

2 files changed

+80
-55
lines changed

2 files changed

+80
-55
lines changed

src/librustc/infer/outlives/verify.rs

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::cell::RefCell;
2+
13
use crate::infer::outlives::env::RegionBoundPairs;
24
use crate::infer::{GenericKind, VerifyBound};
35
use crate::traits;
@@ -17,6 +19,7 @@ pub struct VerifyBoundCx<'cx, 'tcx> {
1719
region_bound_pairs: &'cx RegionBoundPairs<'tcx>,
1820
implicit_region_bound: Option<ty::Region<'tcx>>,
1921
param_env: ty::ParamEnv<'tcx>,
22+
elaborator: RefCell<traits::Elaborator<'tcx>>,
2023
}
2124

2225
impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
@@ -26,7 +29,13 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
2629
implicit_region_bound: Option<ty::Region<'tcx>>,
2730
param_env: ty::ParamEnv<'tcx>,
2831
) -> Self {
29-
Self { tcx, region_bound_pairs, implicit_region_bound, param_env }
32+
Self {
33+
tcx,
34+
region_bound_pairs,
35+
implicit_region_bound,
36+
param_env,
37+
elaborator: RefCell::new(traits::Elaborator::new(tcx)),
38+
}
3039
}
3140

3241
/// Returns a "verify bound" that encodes what we know about
@@ -113,10 +122,10 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
113122
self.tcx.mk_projection(projection_ty.item_def_id, projection_ty.substs);
114123

115124
// Search the env for where clauses like `P: 'a`.
116-
let env_bounds = self
117-
.projection_approx_declared_bounds_from_env(projection_ty)
118-
.into_iter()
119-
.map(|ty::OutlivesPredicate(ty, r)| {
125+
let mut bounds = Vec::new();
126+
127+
bounds.extend(self.projection_approx_declared_bounds_from_env(projection_ty).map(
128+
|ty::OutlivesPredicate(ty, r)| {
120129
let vb = VerifyBound::OutlivedBy(r);
121130
if ty == projection_ty_as_ty {
122131
// Micro-optimize if this is an exact match (this
@@ -126,18 +135,20 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
126135
} else {
127136
VerifyBound::IfEq(ty, Box::new(vb))
128137
}
129-
});
138+
},
139+
));
130140

131141
// Extend with bounds that we can find from the trait.
132-
let trait_bounds = self
133-
.projection_declared_bounds_from_trait(projection_ty)
134-
.map(|r| VerifyBound::OutlivedBy(r));
142+
bounds.extend(
143+
self.projection_declared_bounds_from_trait(projection_ty)
144+
.map(|r| VerifyBound::OutlivedBy(r)),
145+
);
135146

136147
// see the extensive comment in projection_must_outlive
137148
let ty = self.tcx.mk_projection(projection_ty.item_def_id, projection_ty.substs);
138149
let recursive_bound = self.recursive_type_bound(ty);
139150

140-
VerifyBound::AnyBound(env_bounds.chain(trait_bounds).collect()).or(recursive_bound)
151+
VerifyBound::AnyBound(bounds).or(recursive_bound)
141152
}
142153

143154
fn recursive_type_bound(&self, ty: Ty<'tcx>) -> VerifyBound<'tcx> {
@@ -154,7 +165,11 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
154165
.filter(|b| !b.must_hold())
155166
.collect::<Vec<_>>();
156167

157-
if bounds.len() == 1 { bounds.pop().unwrap() } else { VerifyBound::AllBounds(bounds) }
168+
if bounds.len() == 1 {
169+
bounds.pop().unwrap()
170+
} else {
171+
VerifyBound::AllBounds(bounds)
172+
}
158173
}
159174

160175
/// Searches the environment for where-clauses like `G: 'a` where
@@ -281,13 +296,16 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
281296
let tcx = self.tcx;
282297
let assoc_item = tcx.associated_item(assoc_item_def_id);
283298
let trait_def_id = assoc_item.container.assert_trait();
284-
let trait_predicates =
285-
tcx.predicates_of(trait_def_id).predicates.iter().map(|(p, _)| *p).collect();
299+
let trait_predicates = tcx.predicates_of(trait_def_id).predicates.iter().map(|(p, _)| *p);
300+
let mut elaborator = self.elaborator.borrow_mut();
301+
elaborator.clear();
302+
elaborator.extend(trait_predicates);
286303
let identity_substs = InternalSubsts::identity_for_item(tcx, assoc_item_def_id);
287304
let identity_proj = tcx.mk_projection(assoc_item_def_id, identity_substs);
305+
288306
self.collect_outlives_from_predicate_list(
289307
move |ty| ty == identity_proj,
290-
traits::elaborate_predicates(tcx, trait_predicates),
308+
std::iter::from_fn(move || elaborator.next()),
291309
)
292310
.map(|b| b.1)
293311
}

src/librustc/traits/util.rs

Lines changed: 48 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,15 @@ pub fn elaborate_predicates<'tcx>(
120120
}
121121

122122
impl Elaborator<'tcx> {
123+
pub fn new(tcx: TyCtxt<'tcx>) -> Self {
124+
Self { stack: Vec::new(), visited: PredicateSet::new(tcx) }
125+
}
126+
127+
pub fn clear(&mut self) {
128+
self.stack.clear();
129+
self.visited.set.clear();
130+
}
131+
123132
pub fn filter_to_traits(self) -> FilterToTraits<Self> {
124133
FilterToTraits::new(self)
125134
}
@@ -137,14 +146,7 @@ impl Elaborator<'tcx> {
137146
.map(|(pred, _)| pred.subst_supertrait(tcx, &data.to_poly_trait_ref()));
138147
debug!("super_predicates: data={:?} predicates={:?}", data, predicates.clone());
139148

140-
// Only keep those bounds that we haven't already seen.
141-
// This is necessary to prevent infinite recursion in some
142-
// cases. One common case is when people define
143-
// `trait Sized: Sized { }` rather than `trait Sized { }`.
144-
let visited = &mut self.visited;
145-
let predicates = predicates.filter(|pred| visited.insert(pred));
146-
147-
self.stack.extend(predicates);
149+
self.extend(predicates);
148150
}
149151
ty::Predicate::WellFormed(..) => {
150152
// Currently, we do not elaborate WF predicates,
@@ -192,46 +194,51 @@ impl Elaborator<'tcx> {
192194
return;
193195
}
194196

195-
let visited = &mut self.visited;
196197
let mut components = smallvec![];
197198
tcx.push_outlives_components(ty_max, &mut components);
198-
self.stack.extend(
199-
components
200-
.into_iter()
201-
.filter_map(|component| match component {
202-
Component::Region(r) => {
203-
if r.is_late_bound() {
204-
None
205-
} else {
206-
Some(ty::Predicate::RegionOutlives(ty::Binder::dummy(
207-
ty::OutlivesPredicate(r, r_min),
208-
)))
209-
}
210-
}
211-
212-
Component::Param(p) => {
213-
let ty = tcx.mk_ty_param(p.index, p.name);
214-
Some(ty::Predicate::TypeOutlives(ty::Binder::dummy(
215-
ty::OutlivesPredicate(ty, r_min),
216-
)))
217-
}
218-
219-
Component::UnresolvedInferenceVariable(_) => None,
220-
221-
Component::Projection(_) | Component::EscapingProjection(_) => {
222-
// We can probably do more here. This
223-
// corresponds to a case like `<T as
224-
// Foo<'a>>::U: 'b`.
225-
None
226-
}
227-
})
228-
.filter(|p| visited.insert(p)),
229-
);
199+
self.extend(components.into_iter().filter_map(|component| match component {
200+
Component::Region(r) => {
201+
if r.is_late_bound() {
202+
None
203+
} else {
204+
Some(ty::Predicate::RegionOutlives(ty::Binder::dummy(
205+
ty::OutlivesPredicate(r, r_min),
206+
)))
207+
}
208+
}
209+
210+
Component::Param(p) => {
211+
let ty = tcx.mk_ty_param(p.index, p.name);
212+
Some(ty::Predicate::TypeOutlives(ty::Binder::dummy(ty::OutlivesPredicate(
213+
ty, r_min,
214+
))))
215+
}
216+
217+
Component::UnresolvedInferenceVariable(_) => None,
218+
219+
Component::Projection(_) | Component::EscapingProjection(_) => {
220+
// We can probably do more here. This
221+
// corresponds to a case like `<T as
222+
// Foo<'a>>::U: 'b`.
223+
None
224+
}
225+
}));
230226
}
231227
}
232228
}
233229
}
234230

231+
impl<'tcx> Extend<ty::Predicate<'tcx>> for Elaborator<'tcx> {
232+
fn extend<I: IntoIterator<Item = ty::Predicate<'tcx>>>(&mut self, iter: I) {
233+
let visited = &mut self.visited;
234+
// Only keep those bounds that we haven't already seen.
235+
// This is necessary to prevent infinite recursion in some
236+
// cases. One common case is when people define
237+
// `trait Sized: Sized { }` rather than `trait Sized { }`.
238+
self.stack.extend(iter.into_iter().filter(|pred| visited.insert(pred)));
239+
}
240+
}
241+
235242
impl Iterator for Elaborator<'tcx> {
236243
type Item = ty::Predicate<'tcx>;
237244

0 commit comments

Comments
 (0)