@@ -36,6 +36,7 @@ pub struct GlobalFulfilledPredicates<'tcx> {
3636 dep_graph : DepGraph ,
3737}
3838
39+ #[ derive( Debug ) ]
3940pub struct LocalFulfilledPredicates < ' tcx > {
4041 set : FnvHashSet < ty:: Predicate < ' tcx > >
4142}
@@ -66,7 +67,8 @@ pub struct FulfillmentContext<'tcx> {
6667
6768 // A list of all obligations that have been registered with this
6869 // fulfillment context.
69- predicates : ObligationForest < PendingPredicateObligation < ' tcx > , ( ) > ,
70+ predicates : ObligationForest < PendingPredicateObligation < ' tcx > ,
71+ LocalFulfilledPredicates < ' tcx > > ,
7072
7173 // A set of constraints that regionck must validate. Each
7274 // constraint has the form `T:'a`, meaning "some type `T` must
@@ -192,7 +194,7 @@ impl<'tcx> FulfillmentContext<'tcx> {
192194 obligation : obligation,
193195 stalled_on : vec ! [ ]
194196 } ;
195- self . predicates . push_tree ( obligation, ( ) ) ;
197+ self . predicates . push_tree ( obligation, LocalFulfilledPredicates :: new ( ) ) ;
196198 }
197199
198200 pub fn region_obligations ( & self ,
@@ -278,7 +280,8 @@ impl<'tcx> FulfillmentContext<'tcx> {
278280 let outcome = {
279281 let region_obligations = & mut self . region_obligations ;
280282 self . predicates . process_obligations (
281- |obligation, _tree, backtrace| process_predicate ( selcx,
283+ |obligation, tree, backtrace| process_predicate ( selcx,
284+ tree,
282285 obligation,
283286 backtrace,
284287 region_obligations) )
@@ -315,61 +318,97 @@ impl<'tcx> FulfillmentContext<'tcx> {
315318
316319/// Like `process_predicate1`, but wrap result into a pending predicate.
317320fn process_predicate < ' a , ' tcx > ( selcx : & mut SelectionContext < ' a , ' tcx > ,
321+ tree_cache : & mut LocalFulfilledPredicates < ' tcx > ,
318322 pending_obligation : & mut PendingPredicateObligation < ' tcx > ,
319- backtrace : Backtrace < PendingPredicateObligation < ' tcx > > ,
323+ mut backtrace : Backtrace < PendingPredicateObligation < ' tcx > > ,
320324 region_obligations : & mut NodeMap < Vec < RegionObligation < ' tcx > > > )
321325 -> Result < Option < Vec < PendingPredicateObligation < ' tcx > > > ,
322326 FulfillmentErrorCode < ' tcx > >
323327{
324- match process_predicate1 ( selcx, pending_obligation, backtrace, region_obligations) {
328+ match process_predicate1 ( selcx, pending_obligation, backtrace. clone ( ) , region_obligations) {
325329 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.
334347
335348 let tcx = selcx. tcx ( ) ;
336349
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+
341384 // Screen out obligations that we know globally
342385 // are true. This should really be the DAG check
343386 // 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 ;
346389 }
347390
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+ }
352404 }
353405
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+ } )
371410 } )
372- . collect ( ) ;
411+ . collect ( ) ;
373412
374413 Ok ( Some ( pending_predicate_obligations) )
375414 }
0 commit comments