Auto merge of #98542 - jackh726:coinductive-wf, r=oli-obk · rust-lang/rust@116edb6 (original) (raw)

`@@ -488,20 +488,93 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {

`

488

488

`}

`

489

489

`}

`

490

490

``

491

``

`-

ty::PredicateKind::WellFormed(arg) => match wf::obligations(

`

492

``

`-

self.infcx,

`

493

``

`-

obligation.param_env,

`

494

``

`-

obligation.cause.body_id,

`

495

``

`-

obligation.recursion_depth + 1,

`

496

``

`-

arg,

`

497

``

`-

obligation.cause.span,

`

498

``

`-

) {

`

499

``

`-

Some(mut obligations) => {

`

500

``

`-

self.add_depth(obligations.iter_mut(), obligation.recursion_depth);

`

501

``

`-

self.evaluate_predicates_recursively(previous_stack, obligations)

`

``

491

`+

ty::PredicateKind::WellFormed(arg) => {

`

``

492

`` +

// So, there is a bit going on here. First, WellFormed predicates

``

``

493

`+

// are coinductive, like trait predicates with auto traits.

`

``

494

`+

// This means that we need to detect if we have recursively

`

``

495

`` +

// evaluated WellFormed(X). Otherwise, we would run into

``

``

496

`+

// a "natural" overflow error.

`

``

497

`+

//

`

``

498

`+

// Now, the next question is whether we need to do anything

`

``

499

`+

// special with caching. Considering the following tree:

`

``

500

`` +

// - WF(Foo<T>)

``

``

501

`` +

// - Bar<T>: Send

``

``

502

`` +

// - WF(Foo<T>)

``

``

503

`` +

// - Foo<T>: Trait

``

``

504

`` +

// In this case, the innermost WF(Foo<T>) should return

``

``

505

`` +

// EvaluatedToOk, since it's coinductive. Then if

``

``

506

`` +

// Bar<T>: Send is resolved to EvaluatedToOk, it can be

``

``

507

`` +

// inserted into a cache (because without thinking about WF

``

``

508

`` +

// goals, it isn't in a cycle). If Foo<T>: Trait later doesn't

``

``

509

`` +

// hold, then Bar<T>: Send shouldn't hold. Therefore, we

``

``

510

`+

// do need to keep track of coinductive cycles.

`

``

511

+

``

512

`+

let cache = previous_stack.cache;

`

``

513

`+

let dfn = cache.next_dfn();

`

``

514

+

``

515

`+

for stack_arg in previous_stack.cache.wf_args.borrow().iter().rev() {

`

``

516

`+

if stack_arg.0 != arg {

`

``

517

`+

continue;

`

``

518

`+

}

`

``

519

`+

debug!("WellFormed({:?}) on stack", arg);

`

``

520

`+

if let Some(stack) = previous_stack.head {

`

``

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

`+

}

`

``

545

`+

}

`

``

546

`+

return Ok(EvaluatedToOk);

`

502

547

`}

`

503

``

`-

None => Ok(EvaluatedToAmbig),

`

504

``

`-

},

`

``

548

+

``

549

`+

match wf::obligations(

`

``

550

`+

self.infcx,

`

``

551

`+

obligation.param_env,

`

``

552

`+

obligation.cause.body_id,

`

``

553

`+

obligation.recursion_depth + 1,

`

``

554

`+

arg,

`

``

555

`+

obligation.cause.span,

`

``

556

`+

) {

`

``

557

`+

Some(mut obligations) => {

`

``

558

`+

self.add_depth(obligations.iter_mut(), obligation.recursion_depth);

`

``

559

+

``

560

`+

cache.wf_args.borrow_mut().push((arg, previous_stack.depth()));

`

``

561

`+

let result =

`

``

562

`+

self.evaluate_predicates_recursively(previous_stack, obligations);

`

``

563

`+

cache.wf_args.borrow_mut().pop();

`

``

564

+

``

565

`+

let result = result?;

`

``

566

+

``

567

`+

if !result.must_apply_modulo_regions() {

`

``

568

`+

cache.on_failure(dfn);

`

``

569

`+

}

`

``

570

+

``

571

`+

cache.on_completion(dfn);

`

``

572

+

``

573

`+

Ok(result)

`

``

574

`+

}

`

``

575

`+

None => Ok(EvaluatedToAmbig),

`

``

576

`+

}

`

``

577

`+

}

`

505

578

``

506

579

` ty::PredicateKind::TypeOutlives(pred) => {

`

507

580

`// A global type with no late-bound regions can only

`

`@@ -718,6 +791,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {

`

718

791

``

719

792

`debug!(?fresh_trait_pred);

`

720

793

``

``

794

`+

// If a trait predicate is in the (local or global) evaluation cache,

`

``

795

`+

// then we know it holds without cycles.

`

721

796

`if let Some(result) = self.check_evaluation_cache(param_env, fresh_trait_pred) {

`

722

797

`debug!(?result, "CACHE HIT");

`

723

798

`return Ok(result);

`

`@@ -921,7 +996,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {

`

921

996

`` /// - it also appears in the backtrace at some position X,

``

922

997

`` /// - all the predicates at positions X.. between X and the top are

``

923

998

`/// also defaulted traits.

`

924

``

`-

pub fn coinductive_match(&mut self, mut cycle: I) -> bool

`

``

999

`+

pub(crate) fn coinductive_match(&mut self, mut cycle: I) -> bool

`

925

1000

`where

`

926

1001

`I: Iterator<Item = ty::Predicate<'tcx>>,

`

927

1002

`{

`

`@@ -931,6 +1006,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {

`

931

1006

`fn coinductive_predicate(&self, predicate: ty::Predicate<'tcx>) -> bool {

`

932

1007

`let result = match predicate.kind().skip_binder() {

`

933

1008

` ty::PredicateKind::Trait(ref data) => self.tcx().trait_is_auto(data.def_id()),

`

``

1009

`+

ty::PredicateKind::WellFormed(_) => true,

`

934

1010

` _ => false,

`

935

1011

`};

`

936

1012

`debug!(?predicate, ?result, "coinductive_predicate");

`

`@@ -2410,6 +2486,15 @@ struct ProvisionalEvaluationCache<'tcx> {

`

2410

2486

`/// all cache values whose DFN is >= 4 -- in this case, that

`

2411

2487

`` /// means the cached value for F.

``

2412

2488

`map: RefCell<FxHashMap<ty::PolyTraitPredicate<'tcx>, ProvisionalEvaluation>>,

`

``

2489

+

``

2490

`` +

/// The stack of args that we assume to be true because a WF(arg) predicate

``

``

2491

`+

/// is on the stack above (and because of wellformedness is coinductive).

`

``

2492

`+

/// In an "ideal" world, this would share a stack with trait predicates in

`

``

2493

`` +

/// TraitObligationStack. However, trait predicates are much hotter than

``

``

2494

`` +

/// WellFormed predicates, and it's very likely that the additional matches

``

``

2495

`` +

/// will have a perf effect. The value here is the well-formed GenericArg

``

``

2496

`+

/// and the depth of the trait predicate above that well-formed predicate.

`

``

2497

`+

wf_args: RefCell<Vec<(ty::GenericArg<'tcx>, usize)>>,

`

2413

2498

`}

`

2414

2499

``

2415

2500

`/// A cache value for the provisional cache: contains the depth-first

`

`@@ -2423,7 +2508,7 @@ struct ProvisionalEvaluation {

`

2423

2508

``

2424

2509

`impl<'tcx> Default for ProvisionalEvaluationCache<'tcx> {

`

2425

2510

`fn default() -> Self {

`

2426

``

`-

Self { dfn: Cell::new(0), map: Default::default() }

`

``

2511

`+

Self { dfn: Cell::new(0), map: Default::default(), wf_args: Default::default() }

`

2427

2512

`}

`

2428

2513

`}

`

2429

2514

``