Skip to content

Commit 79480ec

Browse files
committed
stash overflowing obligations in fulfill
1 parent ef8e46f commit 79480ec

File tree

1 file changed

+79
-16
lines changed
  • compiler/rustc_trait_selection/src/solve

1 file changed

+79
-16
lines changed

compiler/rustc_trait_selection/src/solve/fulfill.rs

+79-16
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use super::{Certainty, Goal, InferCtxtEvalExt};
2525
/// It is also likely that we want to use slightly different datastructures
2626
/// here as this will have to deal with far more root goals than `evaluate_all`.
2727
pub struct FulfillmentCtxt<'tcx> {
28-
obligations: Vec<PredicateObligation<'tcx>>,
28+
obligations: ObligationStorage<'tcx>,
2929

3030
/// The snapshot in which this context was created. Using the context
3131
/// outside of this snapshot leads to subtle bugs if the snapshot
@@ -34,14 +34,68 @@ pub struct FulfillmentCtxt<'tcx> {
3434
usable_in_snapshot: usize,
3535
}
3636

37+
#[derive(Default)]
38+
struct ObligationStorage<'tcx> {
39+
/// Obligations which resulted in an overflow in fulfillment itself.
40+
///
41+
/// We cannot eagerly return these as error so we instead store them here
42+
/// to avoid recomputing them each time `select_where_possible` is called.
43+
/// This also allows us to return the correct `FulfillmentError` for them.
44+
overflowed: Vec<PredicateObligation<'tcx>>,
45+
pending: Vec<PredicateObligation<'tcx>>,
46+
}
47+
48+
impl<'tcx> ObligationStorage<'tcx> {
49+
fn register(&mut self, obligation: PredicateObligation<'tcx>) {
50+
self.pending.push(obligation);
51+
}
52+
53+
fn clone_pending(&self) -> Vec<PredicateObligation<'tcx>> {
54+
let mut obligations = self.pending.clone();
55+
obligations.extend(self.overflowed.iter().cloned());
56+
obligations
57+
}
58+
59+
fn take_pending(&mut self) -> Vec<PredicateObligation<'tcx>> {
60+
let mut obligations = mem::take(&mut self.pending);
61+
obligations.extend(self.overflowed.drain(..));
62+
obligations
63+
}
64+
65+
fn unstalled_for_select(&mut self) -> impl Iterator<Item = PredicateObligation<'tcx>> {
66+
mem::take(&mut self.pending).into_iter()
67+
}
68+
69+
fn on_fulfillment_overflow(&mut self, infcx: &InferCtxt<'tcx>) {
70+
infcx.probe(|_| {
71+
// IMPORTANT: we must not use solve any inference variables in the obligations
72+
// as this is all happening inside of a probe. We use a probe to make sure
73+
// we get all obligations involved in the overflow. We pretty much check: if
74+
// we were to do another step of `select_where_possible`, which goals would
75+
// change.
76+
self.overflowed.extend(self.pending.extract_if(|o| {
77+
let goal = o.clone().into();
78+
let result = infcx.evaluate_root_goal(goal, GenerateProofTree::Never).0;
79+
match result {
80+
Ok((has_changed, _, _)) => has_changed,
81+
_ => false,
82+
}
83+
}));
84+
})
85+
}
86+
}
87+
3788
impl<'tcx> FulfillmentCtxt<'tcx> {
3889
pub fn new(infcx: &InferCtxt<'tcx>) -> FulfillmentCtxt<'tcx> {
3990
assert!(
4091
infcx.next_trait_solver(),
4192
"new trait solver fulfillment context created when \
4293
infcx is set up for old trait solver"
4394
);
44-
FulfillmentCtxt { obligations: Vec::new(), usable_in_snapshot: infcx.num_open_snapshots() }
95+
FulfillmentCtxt {
96+
obligations: Default::default(),
97+
usable_in_snapshot: infcx.num_open_snapshots(),
98+
}
4599
}
46100

47101
fn inspect_evaluated_obligation(
@@ -68,29 +122,38 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
68122
obligation: PredicateObligation<'tcx>,
69123
) {
70124
assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
71-
self.obligations.push(obligation);
125+
self.obligations.register(obligation);
72126
}
73127

74128
fn collect_remaining_errors(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> {
75-
self.obligations
129+
let mut errors: Vec<_> = self
130+
.obligations
131+
.pending
76132
.drain(..)
77133
.map(|obligation| fulfillment_error_for_stalled(infcx, obligation))
78-
.collect()
134+
.collect();
135+
136+
errors.extend(self.obligations.overflowed.drain(..).map(|obligation| FulfillmentError {
137+
root_obligation: obligation.clone(),
138+
code: FulfillmentErrorCode::Ambiguity { overflow: Some(true) },
139+
obligation,
140+
}));
141+
142+
errors
79143
}
80144

81145
fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> {
82146
assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
83147
let mut errors = Vec::new();
84148
for i in 0.. {
85149
if !infcx.tcx.recursion_limit().value_within_limit(i) {
86-
// Only return true errors that we have accumulated while processing;
87-
// keep ambiguities around, *including overflows*, because they shouldn't
88-
// be considered true errors.
150+
self.obligations.on_fulfillment_overflow(infcx);
151+
// Only return true errors that we have accumulated while processing.
89152
return errors;
90153
}
91154

92155
let mut has_changed = false;
93-
for obligation in mem::take(&mut self.obligations) {
156+
for obligation in self.obligations.unstalled_for_select() {
94157
let goal = obligation.clone().into();
95158
let result = infcx.evaluate_root_goal(goal, GenerateProofTree::IfEnabled).0;
96159
self.inspect_evaluated_obligation(infcx, &obligation, &result);
@@ -103,18 +166,18 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
103166
};
104167
// Push any nested goals that we get from unifying our canonical response
105168
// with our obligation onto the fulfillment context.
106-
self.obligations.extend(nested_goals.into_iter().map(|goal| {
107-
Obligation::new(
169+
for goal in nested_goals {
170+
self.obligations.register(Obligation::new(
108171
infcx.tcx,
109172
obligation.cause.clone(),
110173
goal.param_env,
111174
goal.predicate,
112-
)
113-
}));
175+
));
176+
}
114177
has_changed |= changed;
115178
match certainty {
116179
Certainty::Yes => {}
117-
Certainty::Maybe(_) => self.obligations.push(obligation),
180+
Certainty::Maybe(_) => self.obligations.register(obligation),
118181
}
119182
}
120183

@@ -127,14 +190,14 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
127190
}
128191

129192
fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>> {
130-
self.obligations.clone()
193+
self.obligations.clone_pending()
131194
}
132195

133196
fn drain_unstalled_obligations(
134197
&mut self,
135198
_: &InferCtxt<'tcx>,
136199
) -> Vec<PredicateObligation<'tcx>> {
137-
std::mem::take(&mut self.obligations)
200+
self.obligations.take_pending()
138201
}
139202
}
140203

0 commit comments

Comments
 (0)