@@ -36,6 +36,7 @@ pub struct GlobalFulfilledPredicates<'tcx> {
36
36
dep_graph : DepGraph ,
37
37
}
38
38
39
+ #[ derive( Debug ) ]
39
40
pub struct LocalFulfilledPredicates < ' tcx > {
40
41
set : FnvHashSet < ty:: Predicate < ' tcx > >
41
42
}
@@ -66,7 +67,8 @@ pub struct FulfillmentContext<'tcx> {
66
67
67
68
// A list of all obligations that have been registered with this
68
69
// fulfillment context.
69
- predicates : ObligationForest < PendingPredicateObligation < ' tcx > , ( ) > ,
70
+ predicates : ObligationForest < PendingPredicateObligation < ' tcx > ,
71
+ LocalFulfilledPredicates < ' tcx > > ,
70
72
71
73
// A set of constraints that regionck must validate. Each
72
74
// constraint has the form `T:'a`, meaning "some type `T` must
@@ -192,7 +194,7 @@ impl<'tcx> FulfillmentContext<'tcx> {
192
194
obligation : obligation,
193
195
stalled_on : vec ! [ ]
194
196
} ;
195
- self . predicates . push_tree ( obligation, ( ) ) ;
197
+ self . predicates . push_tree ( obligation, LocalFulfilledPredicates :: new ( ) ) ;
196
198
}
197
199
198
200
pub fn region_obligations ( & self ,
@@ -278,7 +280,8 @@ impl<'tcx> FulfillmentContext<'tcx> {
278
280
let outcome = {
279
281
let region_obligations = & mut self . region_obligations ;
280
282
self . predicates . process_obligations (
281
- |obligation, _tree, backtrace| process_predicate ( selcx,
283
+ |obligation, tree, backtrace| process_predicate ( selcx,
284
+ tree,
282
285
obligation,
283
286
backtrace,
284
287
region_obligations) )
@@ -315,61 +318,97 @@ impl<'tcx> FulfillmentContext<'tcx> {
315
318
316
319
/// Like `process_predicate1`, but wrap result into a pending predicate.
317
320
fn process_predicate < ' a , ' tcx > ( selcx : & mut SelectionContext < ' a , ' tcx > ,
321
+ tree_cache : & mut LocalFulfilledPredicates < ' tcx > ,
318
322
pending_obligation : & mut PendingPredicateObligation < ' tcx > ,
319
- backtrace : Backtrace < PendingPredicateObligation < ' tcx > > ,
323
+ mut backtrace : Backtrace < PendingPredicateObligation < ' tcx > > ,
320
324
region_obligations : & mut NodeMap < Vec < RegionObligation < ' tcx > > > )
321
325
-> Result < Option < Vec < PendingPredicateObligation < ' tcx > > > ,
322
326
FulfillmentErrorCode < ' tcx > >
323
327
{
324
- match process_predicate1 ( selcx, pending_obligation, backtrace, region_obligations) {
328
+ match process_predicate1 ( selcx, pending_obligation, backtrace. clone ( ) , region_obligations) {
325
329
Ok ( Some ( v) ) => {
326
- // FIXME(#30977) the right thing to do here, I think, is to permit
327
- // DAGs. That is, we should detect whenever this predicate
328
- // has appeared somewhere in the current tree./ If it's a
329
- // parent, that's a cycle, and we should either error out
330
- // or consider it ok. But if it's NOT a parent, we can
331
- // ignore it, since it will be proven (or not) separately.
332
- // However, this is a touch tricky, so I'm doing something
333
- // a bit hackier for now so that the `huge-struct.rs` passes.
330
+ // FIXME(#30977) The code below is designed to detect (and
331
+ // permit) DAGs, while still ensuring that the reasoning
332
+ // is acyclic. However, it does a few things
333
+ // suboptimally. For example, it refreshes type variables
334
+ // a lot, probably more than needed, but also less than
335
+ // you might want.
336
+ //
337
+ // - more than needed: I want to be very sure we don't
338
+ // accidentally treat a cycle as a DAG, so I am
339
+ // refreshing type variables as we walk the ancestors;
340
+ // but we are going to repeat this a lot, which is
341
+ // sort of silly, and it would be nicer to refresh
342
+ // them *in place* so that later predicate processing
343
+ // can benefit from the same work;
344
+ // - less than you might want: we only add items in the cache here,
345
+ // but maybe we learn more about type variables and could add them into
346
+ // the cache later on.
334
347
335
348
let tcx = selcx. tcx ( ) ;
336
349
337
- let retain_vec: Vec < _ > = {
338
- let mut dedup = FnvHashSet ( ) ;
339
- v. iter ( )
340
- . map ( |o| {
350
+ // Compute a little FnvHashSet for the ancestors. We only
351
+ // do this the first time that we care.
352
+ let mut cache = None ;
353
+ let mut is_ancestor = |predicate : & ty:: Predicate < ' tcx > | {
354
+ if cache. is_none ( ) {
355
+ let mut c = FnvHashSet ( ) ;
356
+ for ancestor in backtrace. by_ref ( ) {
357
+ // Ugh. This just feels ridiculously
358
+ // inefficient. But we need to compare
359
+ // predicates without being concerned about
360
+ // the vagaries of type inference, so for now
361
+ // just ensure that they are always
362
+ // up-to-date. (I suppose we could just use a
363
+ // snapshot and check if they are unifiable?)
364
+ let resolved_predicate =
365
+ selcx. infcx ( ) . resolve_type_vars_if_possible (
366
+ & ancestor. obligation . predicate ) ;
367
+ c. insert ( resolved_predicate) ;
368
+ }
369
+ cache = Some ( c) ;
370
+ }
371
+
372
+ cache. as_ref ( ) . unwrap ( ) . contains ( predicate)
373
+ } ;
374
+
375
+ let pending_predicate_obligations: Vec < _ > =
376
+ v. into_iter ( )
377
+ . filter_map ( |obligation| {
378
+ // Probably silly, but remove any inference
379
+ // variables. This is actually crucial to the
380
+ // ancestor check below, but it's not clear that
381
+ // it makes sense to ALWAYS do it.
382
+ let obligation = selcx. infcx ( ) . resolve_type_vars_if_possible ( & obligation) ;
383
+
341
384
// Screen out obligations that we know globally
342
385
// are true. This should really be the DAG check
343
386
// mentioned above.
344
- if tcx. fulfilled_predicates . borrow ( ) . check_duplicate ( & o . predicate ) {
345
- return false ;
387
+ if tcx. fulfilled_predicates . borrow ( ) . check_duplicate ( & obligation . predicate ) {
388
+ return None ;
346
389
}
347
390
348
- // If we see two siblings that are exactly the
349
- // same, no need to add them twice.
350
- if !dedup. insert ( & o. predicate ) {
351
- return false ;
391
+ // Check whether this obligation appears somewhere else in the tree.
392
+ if tree_cache. is_duplicate_or_add ( & obligation. predicate ) {
393
+ // If the obligation appears as a parent,
394
+ // allow it, because that is a cycle.
395
+ // Otherwise though we can just ignore
396
+ // it. Note that we have to be careful around
397
+ // inference variables here -- for the
398
+ // purposes of the ancestor check, we retain
399
+ // the invariant that all type variables are
400
+ // fully refreshed.
401
+ if !( & mut is_ancestor) ( & obligation. predicate ) {
402
+ return None ;
403
+ }
352
404
}
353
405
354
- true
355
- } )
356
- . collect ( )
357
- } ;
358
-
359
- let pending_predicate_obligations =
360
- v. into_iter ( )
361
- . zip ( retain_vec)
362
- . flat_map ( |( o, retain) | {
363
- if retain {
364
- Some ( PendingPredicateObligation {
365
- obligation : o,
366
- stalled_on : vec ! [ ]
367
- } )
368
- } else {
369
- None
370
- }
406
+ Some ( PendingPredicateObligation {
407
+ obligation : obligation,
408
+ stalled_on : vec ! [ ]
409
+ } )
371
410
} )
372
- . collect ( ) ;
411
+ . collect ( ) ;
373
412
374
413
Ok ( Some ( pending_predicate_obligations) )
375
414
}
0 commit comments