Skip to content

Commit 9ee3c7a

Browse files
authored
Rollup merge of #107739 - spastorino:check-overflow-evaluate_canonical_goal, r=lcnr
Check for overflow in evaluate_canonical_goal r? `@lcnr`
2 parents 202c706 + 26136c6 commit 9ee3c7a

File tree

3 files changed

+139
-88
lines changed

3 files changed

+139
-88
lines changed

compiler/rustc_trait_selection/src/solve/mod.rs

Lines changed: 33 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ use rustc_middle::ty::{
3131
};
3232
use rustc_span::DUMMY_SP;
3333

34+
use crate::solve::search_graph::OverflowHandler;
3435
use crate::traits::ObligationCause;
3536

3637
mod assembly;
@@ -210,27 +211,16 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
210211
search_graph: &'a mut search_graph::SearchGraph<'tcx>,
211212
canonical_goal: CanonicalGoal<'tcx>,
212213
) -> QueryResult<'tcx> {
213-
match search_graph.try_push_stack(tcx, canonical_goal) {
214-
Ok(()) => {}
215-
// Our goal is already on the stack, eager return.
216-
Err(response) => return response,
217-
}
218-
219-
// We may have to repeatedly recompute the goal in case of coinductive cycles,
220-
// check out the `cache` module for more information.
214+
// Deal with overflow, caching, and coinduction.
221215
//
222-
// FIXME: Similar to `evaluate_all`, this has to check for overflow.
223-
loop {
216+
// The actual solver logic happens in `ecx.compute_goal`.
217+
search_graph.with_new_goal(tcx, canonical_goal, |search_graph| {
224218
let (ref infcx, goal, var_values) =
225219
tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &canonical_goal);
226220
let mut ecx =
227221
EvalCtxt { infcx, var_values, search_graph, in_projection_eq_hack: false };
228-
let result = ecx.compute_goal(goal);
229-
230-
if search_graph.try_finalize_goal(tcx, canonical_goal, result) {
231-
return result;
232-
}
233-
}
222+
ecx.compute_goal(goal)
223+
})
234224
}
235225

236226
fn make_canonical_response(&self, certainty: Certainty) -> QueryResult<'tcx> {
@@ -485,35 +475,38 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
485475
mut goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
486476
) -> Result<Certainty, NoSolution> {
487477
let mut new_goals = Vec::new();
488-
self.repeat_while_none(|this| {
489-
let mut has_changed = Err(Certainty::Yes);
490-
for goal in goals.drain(..) {
491-
let (changed, certainty) = match this.evaluate_goal(goal) {
492-
Ok(result) => result,
493-
Err(NoSolution) => return Some(Err(NoSolution)),
494-
};
495-
496-
if changed {
497-
has_changed = Ok(());
498-
}
478+
self.repeat_while_none(
479+
|_| Ok(Certainty::Maybe(MaybeCause::Overflow)),
480+
|this| {
481+
let mut has_changed = Err(Certainty::Yes);
482+
for goal in goals.drain(..) {
483+
let (changed, certainty) = match this.evaluate_goal(goal) {
484+
Ok(result) => result,
485+
Err(NoSolution) => return Some(Err(NoSolution)),
486+
};
487+
488+
if changed {
489+
has_changed = Ok(());
490+
}
499491

500-
match certainty {
501-
Certainty::Yes => {}
502-
Certainty::Maybe(_) => {
503-
new_goals.push(goal);
504-
has_changed = has_changed.map_err(|c| c.unify_and(certainty));
492+
match certainty {
493+
Certainty::Yes => {}
494+
Certainty::Maybe(_) => {
495+
new_goals.push(goal);
496+
has_changed = has_changed.map_err(|c| c.unify_and(certainty));
497+
}
505498
}
506499
}
507-
}
508500

509-
match has_changed {
510-
Ok(()) => {
511-
mem::swap(&mut new_goals, &mut goals);
512-
None
501+
match has_changed {
502+
Ok(()) => {
503+
mem::swap(&mut new_goals, &mut goals);
504+
None
505+
}
506+
Err(certainty) => Some(Ok(certainty)),
513507
}
514-
Err(certainty) => Some(Ok(certainty)),
515-
}
516-
})
508+
},
509+
)
517510
}
518511

519512
// Recursively evaluates a list of goals to completion, making a query response.

compiler/rustc_trait_selection/src/solve/search_graph/mod.rs

Lines changed: 70 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ mod overflow;
33

44
use self::cache::ProvisionalEntry;
55
use super::{CanonicalGoal, Certainty, MaybeCause, QueryResult};
6+
pub(super) use crate::solve::search_graph::overflow::OverflowHandler;
67
use cache::ProvisionalCache;
78
use overflow::OverflowData;
89
use rustc_index::vec::IndexVec;
@@ -46,7 +47,7 @@ impl<'tcx> SearchGraph<'tcx> {
4647
///
4748
/// This correctly updates the provisional cache if there is a cycle.
4849
#[instrument(level = "debug", skip(self, tcx), ret)]
49-
pub(super) fn try_push_stack(
50+
fn try_push_stack(
5051
&mut self,
5152
tcx: TyCtxt<'tcx>,
5253
goal: CanonicalGoal<'tcx>,
@@ -121,19 +122,19 @@ impl<'tcx> SearchGraph<'tcx> {
121122
///
122123
/// FIXME: Refer to the rustc-dev-guide entry once it exists.
123124
#[instrument(level = "debug", skip(self, tcx, actual_goal), ret)]
124-
pub(super) fn try_finalize_goal(
125+
fn try_finalize_goal(
125126
&mut self,
126127
tcx: TyCtxt<'tcx>,
127128
actual_goal: CanonicalGoal<'tcx>,
128129
response: QueryResult<'tcx>,
129130
) -> bool {
130-
let StackElem { goal, has_been_used } = self.stack.pop().unwrap();
131+
let stack_elem = self.stack.pop().unwrap();
132+
let StackElem { goal, has_been_used } = stack_elem;
131133
assert_eq!(goal, actual_goal);
132134

133135
let cache = &mut self.provisional_cache;
134136
let provisional_entry_index = *cache.lookup_table.get(&goal).unwrap();
135137
let provisional_entry = &mut cache.entries[provisional_entry_index];
136-
let depth = provisional_entry.depth;
137138
// We eagerly update the response in the cache here. If we have to reevaluate
138139
// this goal we use the new response when hitting a cycle, and we definitely
139140
// want to access the final response whenever we look at the cache.
@@ -157,29 +158,72 @@ impl<'tcx> SearchGraph<'tcx> {
157158
self.stack.push(StackElem { goal, has_been_used: false });
158159
false
159160
} else {
160-
// If not, we're done with this goal.
161-
//
162-
// Check whether that this goal doesn't depend on a goal deeper on the stack
163-
// and if so, move it and all nested goals to the global cache.
164-
//
165-
// Note that if any nested goal were to depend on something deeper on the stack,
166-
// this would have also updated the depth of the current goal.
167-
if depth == self.stack.next_index() {
168-
for (i, entry) in cache.entries.drain_enumerated(provisional_entry_index.index()..)
169-
{
170-
let actual_index = cache.lookup_table.remove(&entry.goal);
171-
debug_assert_eq!(Some(i), actual_index);
172-
debug_assert!(entry.depth == depth);
173-
cache::try_move_finished_goal_to_global_cache(
174-
tcx,
175-
&mut self.overflow_data,
176-
&self.stack,
177-
entry.goal,
178-
entry.response,
179-
);
180-
}
181-
}
161+
self.try_move_finished_goal_to_global_cache(tcx, stack_elem);
182162
true
183163
}
184164
}
165+
166+
fn try_move_finished_goal_to_global_cache(
167+
&mut self,
168+
tcx: TyCtxt<'tcx>,
169+
stack_elem: StackElem<'tcx>,
170+
) {
171+
let StackElem { goal, .. } = stack_elem;
172+
let cache = &mut self.provisional_cache;
173+
let provisional_entry_index = *cache.lookup_table.get(&goal).unwrap();
174+
let provisional_entry = &mut cache.entries[provisional_entry_index];
175+
let depth = provisional_entry.depth;
176+
177+
// If not, we're done with this goal.
178+
//
179+
// Check whether that this goal doesn't depend on a goal deeper on the stack
180+
// and if so, move it and all nested goals to the global cache.
181+
//
182+
// Note that if any nested goal were to depend on something deeper on the stack,
183+
// this would have also updated the depth of the current goal.
184+
if depth == self.stack.next_index() {
185+
for (i, entry) in cache.entries.drain_enumerated(provisional_entry_index.index()..) {
186+
let actual_index = cache.lookup_table.remove(&entry.goal);
187+
debug_assert_eq!(Some(i), actual_index);
188+
debug_assert!(entry.depth == depth);
189+
cache::try_move_finished_goal_to_global_cache(
190+
tcx,
191+
&mut self.overflow_data,
192+
&self.stack,
193+
entry.goal,
194+
entry.response,
195+
);
196+
}
197+
}
198+
}
199+
200+
pub(super) fn with_new_goal(
201+
&mut self,
202+
tcx: TyCtxt<'tcx>,
203+
canonical_goal: CanonicalGoal<'tcx>,
204+
mut loop_body: impl FnMut(&mut Self) -> QueryResult<'tcx>,
205+
) -> QueryResult<'tcx> {
206+
match self.try_push_stack(tcx, canonical_goal) {
207+
Ok(()) => {}
208+
// Our goal is already on the stack, eager return.
209+
Err(response) => return response,
210+
}
211+
212+
self.repeat_while_none(
213+
|this| {
214+
let result = this.deal_with_overflow(tcx, canonical_goal);
215+
let stack_elem = this.stack.pop().unwrap();
216+
this.try_move_finished_goal_to_global_cache(tcx, stack_elem);
217+
result
218+
},
219+
|this| {
220+
let result = loop_body(this);
221+
if this.try_finalize_goal(tcx, canonical_goal, result) {
222+
Some(result)
223+
} else {
224+
None
225+
}
226+
},
227+
)
228+
}
185229
}

compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,42 @@ impl OverflowData {
5050
}
5151
}
5252

53+
pub(in crate::solve) trait OverflowHandler<'tcx> {
54+
fn search_graph(&mut self) -> &mut SearchGraph<'tcx>;
55+
56+
fn repeat_while_none<T>(
57+
&mut self,
58+
on_overflow: impl FnOnce(&mut Self) -> Result<T, NoSolution>,
59+
mut loop_body: impl FnMut(&mut Self) -> Option<Result<T, NoSolution>>,
60+
) -> Result<T, NoSolution> {
61+
let start_depth = self.search_graph().overflow_data.additional_depth;
62+
let depth = self.search_graph().stack.len();
63+
while !self.search_graph().overflow_data.has_overflow(depth) {
64+
if let Some(result) = loop_body(self) {
65+
self.search_graph().overflow_data.additional_depth = start_depth;
66+
return result;
67+
}
68+
69+
self.search_graph().overflow_data.additional_depth += 1;
70+
}
71+
self.search_graph().overflow_data.additional_depth = start_depth;
72+
self.search_graph().overflow_data.deal_with_overflow();
73+
on_overflow(self)
74+
}
75+
}
76+
77+
impl<'tcx> OverflowHandler<'tcx> for EvalCtxt<'_, 'tcx> {
78+
fn search_graph(&mut self) -> &mut SearchGraph<'tcx> {
79+
&mut self.search_graph
80+
}
81+
}
82+
83+
impl<'tcx> OverflowHandler<'tcx> for SearchGraph<'tcx> {
84+
fn search_graph(&mut self) -> &mut SearchGraph<'tcx> {
85+
self
86+
}
87+
}
88+
5389
impl<'tcx> SearchGraph<'tcx> {
5490
pub fn deal_with_overflow(
5591
&mut self,
@@ -60,25 +96,3 @@ impl<'tcx> SearchGraph<'tcx> {
6096
response_no_constraints(tcx, goal, Certainty::Maybe(MaybeCause::Overflow))
6197
}
6298
}
63-
64-
impl<'tcx> EvalCtxt<'_, 'tcx> {
65-
/// A `while`-loop which tracks overflow.
66-
pub fn repeat_while_none(
67-
&mut self,
68-
mut loop_body: impl FnMut(&mut Self) -> Option<Result<Certainty, NoSolution>>,
69-
) -> Result<Certainty, NoSolution> {
70-
let start_depth = self.search_graph.overflow_data.additional_depth;
71-
let depth = self.search_graph.stack.len();
72-
while !self.search_graph.overflow_data.has_overflow(depth) {
73-
if let Some(result) = loop_body(self) {
74-
self.search_graph.overflow_data.additional_depth = start_depth;
75-
return result;
76-
}
77-
78-
self.search_graph.overflow_data.additional_depth += 1;
79-
}
80-
self.search_graph.overflow_data.additional_depth = start_depth;
81-
self.search_graph.overflow_data.deal_with_overflow();
82-
Ok(Certainty::Maybe(MaybeCause::Overflow))
83-
}
84-
}

0 commit comments

Comments
 (0)