From c8dae10f14122555bfc1625b037b92eb104e67ad Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Mon, 6 Feb 2023 17:11:43 -0300 Subject: [PATCH] Check for overflow in evaluate_canonical_goal --- .../rustc_trait_selection/src/solve/mod.rs | 23 +++------- .../src/solve/search_graph/mod.rs | 43 ++++++++++++++++--- .../src/solve/search_graph/overflow.rs | 4 +- 3 files changed, 46 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index 358a2bcc7b9..d444ca69df1 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -211,27 +211,16 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { search_graph: &'a mut search_graph::SearchGraph<'tcx>, canonical_goal: CanonicalGoal<'tcx>, ) -> QueryResult<'tcx> { - match search_graph.try_push_stack(tcx, canonical_goal) { - Ok(()) => {} - // Our goal is already on the stack, eager return. - Err(response) => return response, - } - - // We may have to repeatedly recompute the goal in case of coinductive cycles, - // check out the `cache` module for more information. + // Deal with overflow, caching, and coinduction. // - // FIXME: Similar to `evaluate_all`, this has to check for overflow. - loop { + // The actual solver logic happens in `ecx.compute_goal`. + search_graph.with_new_goal(tcx, canonical_goal, |search_graph| { let (ref infcx, goal, var_values) = tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &canonical_goal); let mut ecx = EvalCtxt { infcx, var_values, search_graph, in_projection_eq_hack: false }; - let result = ecx.compute_goal(goal); - - if search_graph.try_finalize_goal(tcx, canonical_goal, result) { - return result; - } - } + ecx.compute_goal(goal) + }) } fn make_canonical_response(&self, certainty: Certainty) -> QueryResult<'tcx> { @@ -487,7 +476,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ) -> Result { let mut new_goals = Vec::new(); self.repeat_while_none( - |_| Certainty::Maybe(MaybeCause::Overflow), + |_| Ok(Certainty::Maybe(MaybeCause::Overflow)), |this| { let mut has_changed = Err(Certainty::Yes); for goal in goals.drain(..) { diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs index 438bcd9a7d6..9b398ef0e62 100644 --- a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs @@ -3,6 +3,7 @@ pub(crate) mod overflow; use self::cache::ProvisionalEntry; use super::{CanonicalGoal, Certainty, MaybeCause, QueryResult}; +use crate::solve::search_graph::overflow::OverflowHandler; use cache::ProvisionalCache; use overflow::OverflowData; use rustc_index::vec::IndexVec; @@ -13,7 +14,7 @@ rustc_index::newtype_index! { pub struct StackDepth {} } -struct StackElem<'tcx> { +pub(crate) struct StackElem<'tcx> { goal: CanonicalGoal<'tcx>, has_been_used: bool, } @@ -127,7 +128,8 @@ impl<'tcx> SearchGraph<'tcx> { actual_goal: CanonicalGoal<'tcx>, response: QueryResult<'tcx>, ) -> bool { - let StackElem { goal, has_been_used } = self.stack.pop().unwrap(); + let stack_elem = self.stack.pop().unwrap(); + let StackElem { goal, has_been_used } = stack_elem; assert_eq!(goal, actual_goal); let cache = &mut self.provisional_cache; @@ -156,7 +158,7 @@ impl<'tcx> SearchGraph<'tcx> { self.stack.push(StackElem { goal, has_been_used: false }); false } else { - self.try_move_finished_goal_to_global_cache(tcx, &goal); + self.try_move_finished_goal_to_global_cache(tcx, stack_elem); true } } @@ -164,10 +166,11 @@ impl<'tcx> SearchGraph<'tcx> { pub(super) fn try_move_finished_goal_to_global_cache( &mut self, tcx: TyCtxt<'tcx>, - goal: &CanonicalGoal<'tcx>, + stack_elem: StackElem<'tcx>, ) { + let StackElem { goal, .. } = stack_elem; let cache = &mut self.provisional_cache; - let provisional_entry_index = *cache.lookup_table.get(goal).unwrap(); + let provisional_entry_index = *cache.lookup_table.get(&goal).unwrap(); let provisional_entry = &mut cache.entries[provisional_entry_index]; let depth = provisional_entry.depth; @@ -193,4 +196,34 @@ impl<'tcx> SearchGraph<'tcx> { } } } + + pub(super) fn with_new_goal( + &mut self, + tcx: TyCtxt<'tcx>, + canonical_goal: CanonicalGoal<'tcx>, + mut loop_body: impl FnMut(&mut Self) -> QueryResult<'tcx>, + ) -> QueryResult<'tcx> { + match self.try_push_stack(tcx, canonical_goal) { + Ok(()) => {} + // Our goal is already on the stack, eager return. + Err(response) => return response, + } + + self.repeat_while_none( + |this| { + let result = this.deal_with_overflow(tcx, canonical_goal); + let stack_elem = this.stack.pop().unwrap(); + this.try_move_finished_goal_to_global_cache(tcx, stack_elem); + result + }, + |this| { + let result = loop_body(this); + if this.try_finalize_goal(tcx, canonical_goal, result) { + Some(result) + } else { + None + } + }, + ) + } } diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs b/compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs index 0d6863b1e81..ea62152789e 100644 --- a/compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs +++ b/compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs @@ -55,7 +55,7 @@ pub(crate) trait OverflowHandler<'tcx> { fn repeat_while_none( &mut self, - on_overflow: impl FnOnce(&mut Self) -> T, + on_overflow: impl FnOnce(&mut Self) -> Result, mut loop_body: impl FnMut(&mut Self) -> Option>, ) -> Result { let start_depth = self.search_graph().overflow_data.additional_depth; @@ -70,7 +70,7 @@ pub(crate) trait OverflowHandler<'tcx> { } self.search_graph().overflow_data.additional_depth = start_depth; self.search_graph().overflow_data.deal_with_overflow(); - Ok(on_overflow(self)) + on_overflow(self) } }