Skip to content

Commit b8a2577

Browse files
Auto merge of #144699 - compiler-errors:extract-if-fulfill, r=<try>
Use `ExtractIf` in fulfillment loop
2 parents 3fb1b53 + dfc9304 commit b8a2577

File tree

13 files changed

+114
-96
lines changed

13 files changed

+114
-96
lines changed

compiler/rustc_next_trait_solver/src/delegate.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
use std::ops::Deref;
22

3+
use rustc_type_ir::inherent::*;
34
use rustc_type_ir::solve::{Certainty, Goal, NoSolution};
45
use rustc_type_ir::{self as ty, InferCtxtLike, Interner, TypeFoldable};
56

7+
use crate::solve::GoalStalledOn;
8+
69
pub trait SolverDelegate: Deref<Target = Self::Infcx> + Sized {
710
type Infcx: InferCtxtLike<Interner = Self::Interner>;
811
type Interner: Interner;
@@ -23,6 +26,11 @@ pub trait SolverDelegate: Deref<Target = Self::Infcx> + Sized {
2326
span: <Self::Interner as Interner>::Span,
2427
) -> Option<Certainty>;
2528

29+
fn is_still_stalled(&self, stalled_on: &GoalStalledOn<Self::Interner>) -> bool {
30+
!stalled_on.stalled_vars.iter().any(|value| self.is_changed_arg(*value))
31+
&& !self.opaque_types_storage_num_entries().needs_reevaluation(stalled_on.num_opaques)
32+
}
33+
2634
fn fresh_var_for_kind_with_span(
2735
&self,
2836
arg: <Self::Interner as Interner>::GenericArg,

compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -432,11 +432,7 @@ where
432432
// args have changed. Otherwise, we don't need to re-run the goal because it'll remain
433433
// stalled, since it'll canonicalize the same way and evaluation is pure.
434434
if let Some(stalled_on) = stalled_on
435-
&& !stalled_on.stalled_vars.iter().any(|value| self.delegate.is_changed_arg(*value))
436-
&& !self
437-
.delegate
438-
.opaque_types_storage_num_entries()
439-
.needs_reevaluation(stalled_on.num_opaques)
435+
&& self.delegate.is_still_stalled(&stalled_on)
440436
{
441437
return Ok((
442438
NestedNormalizationGoals::empty(),
@@ -639,15 +635,24 @@ where
639635
///
640636
/// Goals for the next step get directly added to the nested goals of the `EvalCtxt`.
641637
fn evaluate_added_goals_step(&mut self) -> Result<Option<Certainty>, NoSolution> {
638+
if self.nested_goals.is_empty() {
639+
return Ok(Some(Certainty::Yes));
640+
}
641+
642642
let cx = self.cx();
643643
// If this loop did not result in any progress, what's our final certainty.
644644
let mut unchanged_certainty = Some(Certainty::Yes);
645-
for (source, goal, stalled_on) in mem::take(&mut self.nested_goals) {
645+
646+
let mut nested_goals = mem::take(&mut self.nested_goals);
647+
let mut pending_goals = vec![];
648+
for (source, goal, stalled_on) in nested_goals.extract_if(.., |(_, _, stalled_on)| {
649+
stalled_on.as_ref().is_none_or(|s| !self.delegate.is_still_stalled(s))
650+
}) {
646651
if let Some(certainty) = self.delegate.compute_goal_fast_path(goal, self.origin_span) {
647652
match certainty {
648653
Certainty::Yes => {}
649654
Certainty::Maybe(_) => {
650-
self.nested_goals.push((source, goal, None));
655+
pending_goals.push((source, goal, None));
651656
unchanged_certainty = unchanged_certainty.map(|c| c.and(certainty));
652657
}
653658
}
@@ -684,7 +689,7 @@ where
684689
)?;
685690
// Add the nested goals from normalization to our own nested goals.
686691
trace!(?nested_goals);
687-
self.nested_goals.extend(nested_goals.into_iter().map(|(s, g)| (s, g, None)));
692+
pending_goals.extend(nested_goals.into_iter().map(|(s, g)| (s, g, None)));
688693

689694
// Finally, equate the goal's RHS with the unconstrained var.
690695
//
@@ -728,7 +733,7 @@ where
728733
match certainty {
729734
Certainty::Yes => {}
730735
Certainty::Maybe(_) => {
731-
self.nested_goals.push((source, with_resolved_vars, stalled_on));
736+
pending_goals.push((source, with_resolved_vars, stalled_on));
732737
unchanged_certainty = unchanged_certainty.map(|c| c.and(certainty));
733738
}
734739
}
@@ -742,13 +747,25 @@ where
742747
match certainty {
743748
Certainty::Yes => {}
744749
Certainty::Maybe(_) => {
745-
self.nested_goals.push((source, goal, stalled_on));
750+
pending_goals.push((source, goal, stalled_on));
746751
unchanged_certainty = unchanged_certainty.map(|c| c.and(certainty));
747752
}
748753
}
749754
}
750755
}
751756

757+
// Nested goals still need to be accounted for in the `unchanged_certainty`.
758+
for (_, _, stalled_on) in &nested_goals {
759+
if let Some(GoalStalledOn { stalled_cause, .. }) = stalled_on {
760+
unchanged_certainty =
761+
unchanged_certainty.map(|c| c.and(Certainty::Maybe(*stalled_cause)));
762+
}
763+
}
764+
765+
debug_assert!(self.nested_goals.is_empty());
766+
nested_goals.extend(pending_goals);
767+
self.nested_goals = nested_goals;
768+
752769
Ok(unchanged_certainty)
753770
}
754771

compiler/rustc_trait_selection/src/solve/fulfill.rs

Lines changed: 47 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use std::marker::PhantomData;
2-
use std::mem;
32
use std::ops::ControlFlow;
43

54
use rustc_data_structures::thinvec::ExtractIf;
@@ -75,6 +74,13 @@ impl<'tcx> ObligationStorage<'tcx> {
7574
self.pending.push((obligation, stalled_on));
7675
}
7776

77+
fn register_overflowed(
78+
&mut self,
79+
overflowed: impl IntoIterator<Item = PredicateObligation<'tcx>>,
80+
) {
81+
self.overflowed.extend(overflowed);
82+
}
83+
7884
fn has_pending_obligations(&self) -> bool {
7985
!self.pending.is_empty() || !self.overflowed.is_empty()
8086
}
@@ -88,35 +94,14 @@ impl<'tcx> ObligationStorage<'tcx> {
8894

8995
fn drain_pending(
9096
&mut self,
91-
cond: impl Fn(&PredicateObligation<'tcx>) -> bool,
92-
) -> PendingObligations<'tcx> {
93-
let (unstalled, pending) =
94-
mem::take(&mut self.pending).into_iter().partition(|(o, _)| cond(o));
95-
self.pending = pending;
96-
unstalled
97+
cond: impl Fn(&PredicateObligation<'tcx>, Option<&GoalStalledOn<TyCtxt<'tcx>>>) -> bool,
98+
) -> impl Iterator<Item = (PredicateObligation<'tcx>, Option<GoalStalledOn<TyCtxt<'tcx>>>)>
99+
{
100+
ExtractIf::new(&mut self.pending, move |(o, stalled)| cond(o, stalled.as_ref()))
97101
}
98102

99-
fn on_fulfillment_overflow(&mut self, infcx: &InferCtxt<'tcx>) {
100-
infcx.probe(|_| {
101-
// IMPORTANT: we must not use solve any inference variables in the obligations
102-
// as this is all happening inside of a probe. We use a probe to make sure
103-
// we get all obligations involved in the overflow. We pretty much check: if
104-
// we were to do another step of `select_where_possible`, which goals would
105-
// change.
106-
// FIXME: <https://github.com/Gankra/thin-vec/pull/66> is merged, this can be removed.
107-
self.overflowed.extend(
108-
ExtractIf::new(&mut self.pending, |(o, stalled_on)| {
109-
let goal = o.as_goal();
110-
let result = <&SolverDelegate<'tcx>>::from(infcx).evaluate_root_goal(
111-
goal,
112-
o.cause.span,
113-
stalled_on.take(),
114-
);
115-
matches!(result, Ok(GoalEvaluation { has_changed: HasChanged::Yes, .. }))
116-
})
117-
.map(|(o, _)| o),
118-
);
119-
})
103+
fn num_pending(&self) -> usize {
104+
self.pending.len()
120105
}
121106
}
122107

@@ -133,21 +118,6 @@ impl<'tcx, E: 'tcx> FulfillmentCtxt<'tcx, E> {
133118
_errors: PhantomData,
134119
}
135120
}
136-
137-
fn inspect_evaluated_obligation(
138-
&self,
139-
infcx: &InferCtxt<'tcx>,
140-
obligation: &PredicateObligation<'tcx>,
141-
result: &Result<GoalEvaluation<TyCtxt<'tcx>>, NoSolution>,
142-
) {
143-
if let Some(inspector) = infcx.obligation_inspector.get() {
144-
let result = match result {
145-
Ok(GoalEvaluation { certainty, .. }) => Ok(*certainty),
146-
Err(NoSolution) => Err(NoSolution),
147-
};
148-
(inspector)(infcx, &obligation, result);
149-
}
150-
}
151121
}
152122

153123
impl<'tcx, E> TraitEngine<'tcx, E> for FulfillmentCtxt<'tcx, E>
@@ -180,19 +150,27 @@ where
180150
}
181151

182152
fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<E> {
153+
if self.obligations.num_pending() == 0 {
154+
return vec![];
155+
}
156+
183157
assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
158+
let delegate = <&SolverDelegate<'tcx>>::from(infcx);
184159
let mut errors = Vec::new();
185160
loop {
186161
let mut any_changed = false;
187-
for (mut obligation, stalled_on) in self.obligations.drain_pending(|_| true) {
162+
let mut overflowed = vec![];
163+
let mut pending = vec![];
164+
165+
for (mut obligation, stalled_on) in self.obligations.drain_pending(|_, stalled_on| {
166+
stalled_on.is_none_or(|s| !delegate.is_still_stalled(s))
167+
}) {
188168
if !infcx.tcx.recursion_limit().value_within_limit(obligation.recursion_depth) {
189-
self.obligations.on_fulfillment_overflow(infcx);
190-
// Only return true errors that we have accumulated while processing.
191-
return errors;
169+
overflowed.push(obligation);
170+
continue;
192171
}
193172

194173
let goal = obligation.as_goal();
195-
let delegate = <&SolverDelegate<'tcx>>::from(infcx);
196174
if let Some(certainty) =
197175
delegate.compute_goal_fast_path(goal, obligation.cause.span)
198176
{
@@ -204,15 +182,21 @@ where
204182
//
205183
// Only goals proven via the trait solver should be region dependent.
206184
Certainty::Yes => {}
207-
Certainty::Maybe(_) => {
208-
self.obligations.register(obligation, None);
209-
}
185+
Certainty::Maybe(_) => pending.push((obligation, None)),
210186
}
211187
continue;
212188
}
213189

214190
let result = delegate.evaluate_root_goal(goal, obligation.cause.span, stalled_on);
215-
self.inspect_evaluated_obligation(infcx, &obligation, &result);
191+
192+
if let Some(inspector) = infcx.obligation_inspector.get() {
193+
let result = match result {
194+
Ok(GoalEvaluation { certainty, .. }) => Ok(certainty),
195+
Err(NoSolution) => Err(NoSolution),
196+
};
197+
(inspector)(infcx, &obligation, result);
198+
}
199+
216200
let GoalEvaluation { goal, certainty, has_changed, stalled_on } = match result {
217201
Ok(result) => result,
218202
Err(NoSolution) => {
@@ -256,10 +240,19 @@ where
256240
infcx.push_hir_typeck_potentially_region_dependent_goal(obligation);
257241
}
258242
}
259-
Certainty::Maybe(_) => self.obligations.register(obligation, stalled_on),
243+
Certainty::Maybe(_) => pending.push((obligation, stalled_on)),
260244
}
261245
}
262246

247+
if !overflowed.is_empty() {
248+
self.obligations.register_overflowed(overflowed);
249+
return errors;
250+
}
251+
252+
for (obligation, stalled_on) in pending {
253+
self.obligations.register(obligation, stalled_on);
254+
}
255+
263256
if !any_changed {
264257
break;
265258
}
@@ -295,7 +288,7 @@ where
295288
}
296289

297290
self.obligations
298-
.drain_pending(|obl| {
291+
.drain_pending(|obl, _| {
299292
infcx.probe(|_| {
300293
infcx
301294
.visit_proof_tree(

tests/ui/impl-trait/recursive-in-exhaustiveness.next.stderr

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,16 @@ LL | build(x)
55
| ^^^^^^^^ cannot normalize `build<_>::{opaque#0}`
66

77
error[E0271]: type mismatch resolving `build2<(_,)>::{opaque#0} normalizes-to _`
8-
--> $DIR/recursive-in-exhaustiveness.rs:30:6
8+
--> $DIR/recursive-in-exhaustiveness.rs:30:5
99
|
1010
LL | (build2(x),)
11-
| ^^^^^^^^^ types differ
11+
| ^^^^^^^^^^^^ types differ
1212

1313
error[E0271]: type mismatch resolving `build2<(_,)>::{opaque#0} normalizes-to _`
14-
--> $DIR/recursive-in-exhaustiveness.rs:30:5
14+
--> $DIR/recursive-in-exhaustiveness.rs:30:6
1515
|
1616
LL | (build2(x),)
17-
| ^^^^^^^^^^^^ types differ
17+
| ^^^^^^^^^ types differ
1818

1919
error[E0277]: the size for values of type `(impl Sized,)` cannot be known at compilation time
2020
--> $DIR/recursive-in-exhaustiveness.rs:30:5

tests/ui/impl-trait/two_tait_defining_each_other2.next.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error[E0282]: type annotations needed
22
--> $DIR/two_tait_defining_each_other2.rs:12:11
33
|
44
LL | fn muh(x: A) -> B {
5-
| ^ cannot infer type
5+
| ^ cannot infer type for type alias `A`
66

77
error: aborting due to 1 previous error
88

tests/ui/traits/next-solver/alias-bound-unsound.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ fn main() {
2525
//~^ ERROR overflow evaluating the requirement `String <: <() as Foo>::Item`
2626
//~| ERROR overflow evaluating the requirement `<() as Foo>::Item well-formed`
2727
//~| ERROR overflow evaluating the requirement `&<() as Foo>::Item well-formed`
28+
//~| ERROR overflow evaluating the requirement `<() as Foo>::Item: Sized`
2829
//~| ERROR overflow evaluating the requirement `<() as Foo>::Item == _`
2930
//~| ERROR overflow evaluating the requirement `<() as Foo>::Item == _`
3031
//~| ERROR overflow evaluating the requirement `<() as Foo>::Item == _`

tests/ui/traits/next-solver/alias-bound-unsound.stderr

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,14 @@ LL | drop(<() as Foo>::copy_me(&x));
3232
|
3333
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
3434

35+
error[E0275]: overflow evaluating the requirement `<() as Foo>::Item: Sized`
36+
--> $DIR/alias-bound-unsound.rs:24:10
37+
|
38+
LL | drop(<() as Foo>::copy_me(&x));
39+
| ^^^^^^^^^^^^^^^^^^^^^^^^
40+
|
41+
= note: the return type of a function must have a statically known size
42+
3543
error[E0275]: overflow evaluating the requirement `&<() as Foo>::Item well-formed`
3644
--> $DIR/alias-bound-unsound.rs:24:31
3745
|
@@ -58,6 +66,6 @@ error[E0275]: overflow evaluating the requirement `<() as Foo>::Item == _`
5866
LL | drop(<() as Foo>::copy_me(&x));
5967
| ^^
6068

61-
error: aborting due to 8 previous errors
69+
error: aborting due to 9 previous errors
6270

6371
For more information about this error, try `rustc --explain E0275`.
Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,9 @@
1-
error[E0283]: type annotations needed
1+
error[E0284]: type annotations needed: cannot normalize `<_ as Iterator>::Item`
22
--> $DIR/runaway-impl-candidate-selection.rs:13:22
33
|
44
LL | println!("{:?}", iter::<_>());
5-
| ^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `iter`
6-
|
7-
= note: cannot satisfy `_: Iterator`
8-
note: required by a bound in `iter`
9-
--> $DIR/runaway-impl-candidate-selection.rs:8:12
10-
|
11-
LL | fn iter<T: Iterator>() -> <T as Iterator>::Item {
12-
| ^^^^^^^^ required by this bound in `iter`
5+
| ^^^^^^^^^^^ cannot normalize `<_ as Iterator>::Item`
136

147
error: aborting due to 1 previous error
158

16-
For more information about this error, try `rustc --explain E0283`.
9+
For more information about this error, try `rustc --explain E0284`.

tests/ui/traits/next-solver/coherence/coherence-fulfill-overflow.stderr

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ LL | impl<T: TwoW> Trait for W<T> {}
55
| ---------------------------- first implementation here
66
LL | impl<T: TwoW> Trait for T {}
77
| ^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<_>>>>>>>>>>>>>>>>>>>>>>>`
8+
|
9+
= note: overflow evaluating the requirement `W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<_>>>>>>>>>>>>>>>>>>>>>>>: TwoW`
10+
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "20"]` attribute to your crate (`coherence_fulfill_overflow`)
811

912
error: aborting due to 1 previous error
1013

tests/ui/traits/next-solver/coroutine.fail.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ LL | | },
1111
| |_________^ the trait `Coroutine<A>` is not implemented for `{coroutine@$DIR/coroutine.rs:20:9: 20:11}`
1212
|
1313
note: required by a bound in `needs_coroutine`
14-
--> $DIR/coroutine.rs:14:28
14+
--> $DIR/coroutine.rs:14:41
1515
|
1616
LL | fn needs_coroutine(_: impl Coroutine<A, Yield = B, Return = C>) {}
17-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `needs_coroutine`
17+
| ^^^^^^^^^ required by this bound in `needs_coroutine`
1818

1919
error: aborting due to 1 previous error
2020

0 commit comments

Comments
 (0)