@@ -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_root ( obligation) ;
197
+ self . predicates . push_tree ( obligation, LocalFulfilledPredicates :: new ( ) ) ;
196
198
}
197
199
198
200
pub fn region_obligations ( & self ,
@@ -278,10 +280,11 @@ 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, backtrace| process_predicate ( selcx,
282
- obligation,
283
- backtrace,
284
- region_obligations) )
283
+ |obligation, tree, backtrace| process_predicate ( selcx,
284
+ tree,
285
+ obligation,
286
+ backtrace,
287
+ region_obligations) )
285
288
} ;
286
289
287
290
debug ! ( "select_where_possible: outcome={:?}" , outcome) ;
@@ -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
406
+ Some ( PendingPredicateObligation {
407
+ obligation : obligation,
408
+ stalled_on : vec ! [ ]
409
+ } )
355
410
} )
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
- }
371
- } )
372
- . collect ( ) ;
411
+ . collect ( ) ;
373
412
374
413
Ok ( Some ( pending_predicate_obligations) )
375
414
}
@@ -405,7 +444,7 @@ fn process_predicate1<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>,
405
444
pending_obligation. stalled_on = vec ! [ ] ;
406
445
}
407
446
408
- let obligation = & pending_obligation. obligation ;
447
+ let obligation = & mut pending_obligation. obligation ;
409
448
410
449
// If we exceed the recursion limit, take a moment to look for a
411
450
// cycle so we can give a better error report from here, where we
@@ -417,18 +456,31 @@ fn process_predicate1<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>,
417
456
}
418
457
}
419
458
459
+ if obligation. predicate . has_infer_types ( ) {
460
+ obligation. predicate = selcx. infcx ( ) . resolve_type_vars_if_possible ( & obligation. predicate ) ;
461
+ }
462
+
420
463
match obligation. predicate {
421
464
ty:: Predicate :: Trait ( ref data) => {
465
+ if selcx. tcx ( ) . fulfilled_predicates . borrow ( ) . check_duplicate_trait ( data) {
466
+ return Ok ( Some ( vec ! [ ] ) ) ;
467
+ }
468
+
422
469
if coinductive_match ( selcx, obligation, data, & backtrace) {
423
470
return Ok ( Some ( vec ! [ ] ) ) ;
424
471
}
425
472
426
473
let trait_obligation = obligation. with ( data. clone ( ) ) ;
427
474
match selcx. select ( & trait_obligation) {
428
475
Ok ( Some ( vtable) ) => {
476
+ info ! ( "selecting trait `{:?}` at depth {} yielded Ok(Some)" ,
477
+ data, obligation. recursion_depth) ;
429
478
Ok ( Some ( vtable. nested_obligations ( ) ) )
430
479
}
431
480
Ok ( None ) => {
481
+ info ! ( "selecting trait `{:?}` at depth {} yielded Ok(None)" ,
482
+ data, obligation. recursion_depth) ;
483
+
432
484
// This is a bit subtle: for the most part, the
433
485
// only reason we can fail to make progress on
434
486
// trait selection is because we don't have enough
@@ -457,6 +509,8 @@ fn process_predicate1<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>,
457
509
Ok ( None )
458
510
}
459
511
Err ( selection_err) => {
512
+ info ! ( "selecting trait `{:?}` at depth {} yielded Err" ,
513
+ data, obligation. recursion_depth) ;
460
514
Err ( CodeSelectionError ( selection_err) )
461
515
}
462
516
}
@@ -642,18 +696,28 @@ impl<'tcx> GlobalFulfilledPredicates<'tcx> {
642
696
643
697
pub fn check_duplicate ( & self , key : & ty:: Predicate < ' tcx > ) -> bool {
644
698
if let ty:: Predicate :: Trait ( ref data) = * key {
645
- // For the global predicate registry, when we find a match, it
646
- // may have been computed by some other task, so we want to
647
- // add a read from the node corresponding to the predicate
648
- // processing to make sure we get the transitive dependencies.
649
- if self . set . contains ( data) {
650
- debug_assert ! ( data. is_global( ) ) ;
651
- self . dep_graph . read ( data. dep_node ( ) ) ;
652
- return true ;
653
- }
699
+ self . check_duplicate_trait ( data)
700
+ } else {
701
+ false
654
702
}
703
+ }
704
+
705
+ pub fn check_duplicate_trait ( & self , data : & ty:: PolyTraitPredicate < ' tcx > ) -> bool {
706
+ // For the global predicate registry, when we find a match, it
707
+ // may have been computed by some other task, so we want to
708
+ // add a read from the node corresponding to the predicate
709
+ // processing to make sure we get the transitive dependencies.
710
+ if self . set . contains ( data) {
711
+ debug_assert ! ( data. is_global( ) ) ;
712
+ self . dep_graph . read ( data. dep_node ( ) ) ;
713
+ debug ! ( "check_duplicate: global predicate `{:?}` already proved elsewhere" , data) ;
714
+
715
+ info ! ( "check_duplicate_trait hit: `{:?}`" , data) ;
655
716
656
- return false ;
717
+ true
718
+ } else {
719
+ false
720
+ }
657
721
}
658
722
659
723
fn add_if_global ( & mut self , key : & ty:: Predicate < ' tcx > ) {
@@ -663,7 +727,10 @@ impl<'tcx> GlobalFulfilledPredicates<'tcx> {
663
727
// already has the required read edges, so we don't need
664
728
// to add any more edges here.
665
729
if data. is_global ( ) {
666
- self . set . insert ( data. clone ( ) ) ;
730
+ if self . set . insert ( data. clone ( ) ) {
731
+ debug ! ( "add_if_global: global predicate `{:?}` added" , data) ;
732
+ info ! ( "check_duplicate_trait entry: `{:?}`" , data) ;
733
+ }
667
734
}
668
735
}
669
736
}
0 commit comments