Skip to content

Commit 82c4343

Browse files
committed
modify trait checker to track the variables on which trait resolution is
stalled rather than keeping this annoying mark; I checked that the original compile-time regression that the mark was intended to fix (#18208) was still reasonable, but I've not done exhaustive measurements to see how important this "optimization" really is anymore
1 parent 02fbf31 commit 82c4343

File tree

4 files changed

+74
-73
lines changed

4 files changed

+74
-73
lines changed

src/librustc/middle/traits/fulfill.rs

+69-46
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,7 @@ pub struct FulfillmentContext<'tcx> {
5757

5858
// A list of all obligations that have been registered with this
5959
// fulfillment context.
60-
predicates: Vec<PredicateObligation<'tcx>>,
61-
62-
// Remembers the count of trait obligations that we have already
63-
// attempted to select. This is used to avoid repeating work
64-
// when `select_new_obligations` is called.
65-
attempted_mark: usize,
60+
predicates: Vec<PendingPredicateObligation<'tcx>>,
6661

6762
// A set of constraints that regionck must validate. Each
6863
// constraint has the form `T:'a`, meaning "some type `T` must
@@ -100,6 +95,12 @@ pub struct RegionObligation<'tcx> {
10095
pub cause: ObligationCause<'tcx>,
10196
}
10297

98+
#[derive(Clone, Debug)]
99+
pub struct PendingPredicateObligation<'tcx> {
100+
pub obligation: PredicateObligation<'tcx>,
101+
pub stalled_on: Vec<Ty<'tcx>>,
102+
}
103+
103104
impl<'tcx> FulfillmentContext<'tcx> {
104105
/// Creates a new fulfillment context.
105106
///
@@ -122,7 +123,6 @@ impl<'tcx> FulfillmentContext<'tcx> {
122123
FulfillmentContext {
123124
duplicate_set: FulfilledPredicates::new(),
124125
predicates: Vec::new(),
125-
attempted_mark: 0,
126126
region_obligations: NodeMap(),
127127
errors_will_be_reported: errors_will_be_reported,
128128
}
@@ -198,6 +198,10 @@ impl<'tcx> FulfillmentContext<'tcx> {
198198
}
199199

200200
debug!("register_predicate({:?})", obligation);
201+
let obligation = PendingPredicateObligation {
202+
obligation: obligation,
203+
stalled_on: vec![]
204+
};
201205
self.predicates.push(obligation);
202206
}
203207

@@ -221,7 +225,7 @@ impl<'tcx> FulfillmentContext<'tcx> {
221225
let errors: Vec<FulfillmentError> =
222226
self.predicates
223227
.iter()
224-
.map(|o| FulfillmentError::new((*o).clone(), CodeAmbiguity))
228+
.map(|o| FulfillmentError::new(o.obligation.clone(), CodeAmbiguity))
225229
.collect();
226230

227231
if errors.is_empty() {
@@ -231,18 +235,6 @@ impl<'tcx> FulfillmentContext<'tcx> {
231235
}
232236
}
233237

234-
/// Attempts to select obligations that were registered since the call to a selection routine.
235-
/// This is used by the type checker to eagerly attempt to resolve obligations in hopes of
236-
/// gaining type information. It'd be equally valid to use `select_where_possible` but it
237-
/// results in `O(n^2)` performance (#18208).
238-
pub fn select_new_obligations<'a>(&mut self,
239-
infcx: &InferCtxt<'a,'tcx>)
240-
-> Result<(),Vec<FulfillmentError<'tcx>>>
241-
{
242-
let mut selcx = SelectionContext::new(infcx);
243-
self.select(&mut selcx, true)
244-
}
245-
246238
pub fn select_where_possible<'a>(&mut self,
247239
infcx: &InferCtxt<'a,'tcx>)
248240
-> Result<(),Vec<FulfillmentError<'tcx>>>
@@ -251,7 +243,7 @@ impl<'tcx> FulfillmentContext<'tcx> {
251243
self.select(&mut selcx, false)
252244
}
253245

254-
pub fn pending_obligations(&self) -> &[PredicateObligation<'tcx>] {
246+
pub fn pending_obligations(&self) -> &[PendingPredicateObligation<'tcx>] {
255247
&self.predicates
256248
}
257249

@@ -299,36 +291,25 @@ impl<'tcx> FulfillmentContext<'tcx> {
299291

300292
let mut new_obligations = Vec::new();
301293

302-
// If we are only attempting obligations we haven't seen yet,
303-
// then set `skip` to the number of obligations we've already
304-
// seen.
305-
let mut skip = if only_new_obligations {
306-
self.attempted_mark
307-
} else {
308-
0
309-
};
310-
311294
// First pass: walk each obligation, retaining
312295
// only those that we cannot yet process.
313296
{
314297
let region_obligations = &mut self.region_obligations;
315-
self.predicates.retain(|predicate| {
316-
// Hack: Retain does not pass in the index, but we want
317-
// to avoid processing the first `start_count` entries.
318-
let processed =
319-
if skip == 0 {
320-
process_predicate(selcx, predicate,
321-
&mut new_obligations, &mut errors, region_obligations)
322-
} else {
323-
skip -= 1;
324-
false
325-
};
326-
!processed
327-
});
298+
let mut i = 0;
299+
while i < self.predicates.len() {
300+
let processed = process_predicate(selcx,
301+
&mut self.predicates[i],
302+
&mut new_obligations,
303+
&mut errors,
304+
region_obligations);
305+
if processed {
306+
self.predicates.swap_remove(i);
307+
} else {
308+
i += 1;
309+
}
310+
}
328311
}
329312

330-
self.attempted_mark = self.predicates.len();
331-
332313
if self.predicates.len() == count {
333314
// Nothing changed.
334315
break;
@@ -354,7 +335,7 @@ impl<'tcx> FulfillmentContext<'tcx> {
354335
}
355336

356337
fn process_predicate<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>,
357-
obligation: &PredicateObligation<'tcx>,
338+
pending_obligation: &mut PendingPredicateObligation<'tcx>,
358339
new_obligations: &mut Vec<PredicateObligation<'tcx>>,
359340
errors: &mut Vec<FulfillmentError<'tcx>>,
360341
region_obligations: &mut NodeMap<Vec<RegionObligation<'tcx>>>)
@@ -367,11 +348,53 @@ fn process_predicate<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>,
367348
* type inference.
368349
*/
369350

351+
// if we were stalled on some unresolved variables, first check
352+
// whether any of them have been resolved; if not, don't bother
353+
// doing more work yet
354+
if !pending_obligation.stalled_on.is_empty() {
355+
if pending_obligation.stalled_on.iter().all(|&ty| {
356+
let resolved_ty = selcx.infcx().resolve_type_vars_if_possible(&ty);
357+
resolved_ty == ty // nothing changed here
358+
}) {
359+
debug!("process_predicate: pending obligation {:?} still stalled on {:?}",
360+
selcx.infcx().resolve_type_vars_if_possible(&pending_obligation.obligation),
361+
pending_obligation.stalled_on);
362+
return false;
363+
}
364+
pending_obligation.stalled_on = vec![];
365+
}
366+
367+
let obligation = &mut pending_obligation.obligation;
370368
match obligation.predicate {
371369
ty::Predicate::Trait(ref data) => {
372370
let trait_obligation = obligation.with(data.clone());
373371
match selcx.select(&trait_obligation) {
374372
Ok(None) => {
373+
// This is a bit subtle: for the most part, the
374+
// only reason we can fail to make progress on
375+
// trait selection is because we don't have enough
376+
// information about the types in the trait. One
377+
// exception is that we sometimes haven't decided
378+
// what kind of closure a closure is. *But*, in
379+
// that case, it turns out, the type of the
380+
// closure will also change, because the closure
381+
// also includes references to its upvars as part
382+
// of its type, and those types are resolved at
383+
// the same time.
384+
pending_obligation.stalled_on =
385+
data.skip_binder() // ok b/c this check doesn't care about regions
386+
.input_types()
387+
.iter()
388+
.map(|t| selcx.infcx().resolve_type_vars_if_possible(t))
389+
.filter(|t| t.has_infer_types())
390+
.flat_map(|t| t.walk())
391+
.filter(|t| t.is_ty_var())
392+
.collect();
393+
394+
debug!("process_predicate: pending obligation {:?} now stalled on {:?}",
395+
selcx.infcx().resolve_type_vars_if_possible(obligation),
396+
pending_obligation.stalled_on);
397+
375398
false
376399
}
377400
Ok(Some(s)) => {

src/librustc_typeck/check/closure.rs

+2
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ fn deduce_expectations_from_obligations<'a,'tcx>(
141141
fulfillment_cx
142142
.pending_obligations()
143143
.iter()
144+
.map(|obligation| &obligation.obligation)
144145
.filter_map(|obligation| {
145146
debug!("deduce_expectations_from_obligations: obligation.predicate={:?}",
146147
obligation.predicate);
@@ -168,6 +169,7 @@ fn deduce_expectations_from_obligations<'a,'tcx>(
168169
fulfillment_cx
169170
.pending_obligations()
170171
.iter()
172+
.map(|obligation| &obligation.obligation)
171173
.filter_map(|obligation| {
172174
let opt_trait_ref = match obligation.predicate {
173175
ty::Predicate::Projection(ref data) => Some(data.to_poly_trait_ref()),

src/librustc_typeck/check/method/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ pub fn lookup_in_trait_adjusted<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
261261
// FIXME(#18653) -- Try to resolve obligations, giving us more
262262
// typing information, which can sometimes be needed to avoid
263263
// pathological region inference failures.
264-
fcx.select_new_obligations();
264+
fcx.select_obligations_where_possible();
265265

266266
// Insert any adjustments needed (always an autoref of some mutability).
267267
match self_expr {

src/librustc_typeck/check/mod.rs

+2-26
Original file line numberDiff line numberDiff line change
@@ -1235,15 +1235,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
12351235
return ty;
12361236
}
12371237

1238-
// If not, try resolving any new fcx obligations that have cropped up.
1239-
self.select_new_obligations();
1240-
ty = self.infcx().resolve_type_vars_if_possible(&ty);
1241-
if !ty.has_infer_types() {
1242-
debug!("resolve_type_vars_if_possible: ty={:?}", ty);
1243-
return ty;
1244-
}
1245-
1246-
// If not, try resolving *all* pending obligations as much as
1238+
// If not, try resolving pending obligations as much as
12471239
// possible. This can help substantially when there are
12481240
// indirect dependencies that don't seem worth tracking
12491241
// precisely.
@@ -2029,22 +2021,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
20292021
Err(errors) => { report_fulfillment_errors(self.infcx(), &errors); }
20302022
}
20312023
}
2032-
2033-
/// Try to select any fcx obligation that we haven't tried yet, in an effort
2034-
/// to improve inference. You could just call
2035-
/// `select_obligations_where_possible` except that it leads to repeated
2036-
/// work.
2037-
fn select_new_obligations(&self) {
2038-
match
2039-
self.inh.infcx.fulfillment_cx
2040-
.borrow_mut()
2041-
.select_new_obligations(self.infcx())
2042-
{
2043-
Ok(()) => { }
2044-
Err(errors) => { report_fulfillment_errors(self.infcx(), &errors); }
2045-
}
2046-
}
2047-
20482024
}
20492025

20502026
impl<'a, 'tcx> RegionScope for FnCtxt<'a, 'tcx> {
@@ -2496,7 +2472,7 @@ fn check_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
24962472
// an "opportunistic" vtable resolution of any trait bounds on
24972473
// the call. This helps coercions.
24982474
if check_blocks {
2499-
fcx.select_new_obligations();
2475+
fcx.select_obligations_where_possible();
25002476
}
25012477

25022478
// For variadic functions, we don't have a declared type for all of

0 commit comments

Comments
 (0)