Skip to content

Commit bd298ad

Browse files
committed
Take into account trait predicate coinductiveness
1 parent e16dbb5 commit bd298ad

File tree

1 file changed

+36
-6
lines changed
  • compiler/rustc_trait_selection/src/traits/select

1 file changed

+36
-6
lines changed

compiler/rustc_trait_selection/src/traits/select/mod.rs

+36-6
Original file line numberDiff line numberDiff line change
@@ -512,13 +512,36 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
512512
let cache = previous_stack.cache;
513513
let dfn = cache.next_dfn();
514514

515-
for stack_arg in previous_stack.cache.wf_tys.borrow().iter().rev() {
515+
for stack_arg in previous_stack.cache.wf_args.borrow().iter().rev() {
516516
if stack_arg.0 != arg {
517517
continue;
518518
}
519519
debug!("WellFormed({:?}) on stack", arg);
520520
if let Some(stack) = previous_stack.head {
521-
stack.update_reached_depth(stack_arg.1);
521+
// Okay, let's imagine we have two different stacks:
522+
// `T: NonAutoTrait -> WF(T) -> T: NonAutoTrait`
523+
// `WF(T) -> T: NonAutoTrait -> WF(T)`
524+
// Because of this, we need to check that all
525+
// predicates between the WF goals are coinductive.
526+
// Otherwise, we can say that `T: NonAutoTrait` is
527+
// true.
528+
// Let's imagine we have a predicate stack like
529+
// `Foo: Bar -> WF(T) -> T: NonAutoTrait -> T: Auto
530+
// depth ^1 ^2 ^3
531+
// and the current predicate is `WF(T)`. `wf_args`
532+
// would contain `(T, 1)`. We want to check all
533+
// trait predicates greater than `1`. The previous
534+
// stack would be `T: Auto`.
535+
let cycle = stack.iter().take_while(|s| s.depth > stack_arg.1);
536+
let tcx = self.tcx();
537+
let cycle =
538+
cycle.map(|stack| stack.obligation.predicate.to_predicate(tcx));
539+
if self.coinductive_match(cycle) {
540+
stack.update_reached_depth(stack_arg.1);
541+
return Ok(EvaluatedToOk);
542+
} else {
543+
return Ok(EvaluatedToRecur);
544+
}
522545
}
523546
return Ok(EvaluatedToOk);
524547
}
@@ -534,10 +557,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
534557
Some(mut obligations) => {
535558
self.add_depth(obligations.iter_mut(), obligation.recursion_depth);
536559

537-
cache.wf_tys.borrow_mut().push((arg, previous_stack.depth()));
560+
cache.wf_args.borrow_mut().push((arg, previous_stack.depth()));
538561
let result =
539562
self.evaluate_predicates_recursively(previous_stack, obligations);
540-
cache.wf_tys.borrow_mut().pop();
563+
cache.wf_args.borrow_mut().pop();
541564

542565
let result = result?;
543566

@@ -2465,7 +2488,14 @@ struct ProvisionalEvaluationCache<'tcx> {
24652488
/// means the cached value for `F`.
24662489
map: RefCell<FxHashMap<ty::PolyTraitPredicate<'tcx>, ProvisionalEvaluation>>,
24672490

2468-
wf_tys: RefCell<Vec<(ty::GenericArg<'tcx>, usize)>>,
2491+
/// The stack of args that we assume to be true because a `WF(arg)` predicate
2492+
/// is on the stack above (and because of wellformedness is coinductive).
2493+
/// In an "ideal" world, this would share a stack with trait predicates in
2494+
/// `TraitObligationStack`. However, trait predicates are *much* hotter than
2495+
/// `WellFormed` predicates, and it's very likely that the additional matches
2496+
/// will have a perf effect. The value here is the well-formed `GenericArg`
2497+
/// and the depth of the trait predicate *above* that well-formed predicate.
2498+
wf_args: RefCell<Vec<(ty::GenericArg<'tcx>, usize)>>,
24692499
}
24702500

24712501
/// A cache value for the provisional cache: contains the depth-first
@@ -2479,7 +2509,7 @@ struct ProvisionalEvaluation {
24792509

24802510
impl<'tcx> Default for ProvisionalEvaluationCache<'tcx> {
24812511
fn default() -> Self {
2482-
Self { dfn: Cell::new(0), map: Default::default(), wf_tys: Default::default() }
2512+
Self { dfn: Cell::new(0), map: Default::default(), wf_args: Default::default() }
24832513
}
24842514
}
24852515

0 commit comments

Comments
 (0)