From d5b2e907444cb202407771eee9dd41c2518c1724 Mon Sep 17 00:00:00 2001 From: Aravind Gollakota Date: Thu, 19 Apr 2018 03:15:36 -0500 Subject: [PATCH] Retry canonical trait query in standard mode if overflow occurs This is slightly hacky and hopefully only a somewhat temporary solution. --- .../traits/query/evaluate_obligation.rs | 32 ++++++++++++++----- src/librustc/traits/select.rs | 19 +++++++---- src/librustc/ty/maps/mod.rs | 5 +-- src/librustc_traits/evaluate_obligation.rs | 9 ++---- 4 files changed, 41 insertions(+), 24 deletions(-) diff --git a/src/librustc/traits/query/evaluate_obligation.rs b/src/librustc/traits/query/evaluate_obligation.rs index 88c51d006db06..4e028cac49abe 100644 --- a/src/librustc/traits/query/evaluate_obligation.rs +++ b/src/librustc/traits/query/evaluate_obligation.rs @@ -10,7 +10,8 @@ use infer::InferCtxt; use infer::canonical::{Canonical, Canonicalize}; -use traits::{EvaluationResult, PredicateObligation}; +use traits::{EvaluationResult, PredicateObligation, SelectionContext, + TraitQueryMode, OverflowError}; use traits::query::CanonicalPredicateGoal; use ty::{ParamEnvAnd, Predicate, TyCtxt}; @@ -21,10 +22,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { &self, obligation: &PredicateObligation<'tcx>, ) -> bool { - let (c_pred, _) = - self.canonicalize_query(&obligation.param_env.and(obligation.predicate)); - - self.tcx.global_tcx().evaluate_obligation(c_pred).may_apply() + self.evaluate_obligation(obligation).may_apply() } /// Evaluates whether the predicate can be satisfied in the given @@ -34,11 +32,29 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { &self, obligation: &PredicateObligation<'tcx>, ) -> bool { + self.evaluate_obligation(obligation) == EvaluationResult::EvaluatedToOk + } + + // Helper function that canonicalizes and runs the query, as well as handles + // overflow. + fn evaluate_obligation( + &self, + obligation: &PredicateObligation<'tcx>, + ) -> EvaluationResult { let (c_pred, _) = self.canonicalize_query(&obligation.param_env.and(obligation.predicate)); - - self.tcx.global_tcx().evaluate_obligation(c_pred) == - EvaluationResult::EvaluatedToOk + // Run canonical query. If overflow occurs, rerun from scratch but this time + // in standard trait query mode so that overflow is handled appropriately + // within `SelectionContext`. + match self.tcx.global_tcx().evaluate_obligation(c_pred) { + Ok(result) => result, + Err(OverflowError) => { + let mut selcx = + SelectionContext::with_query_mode(&self, TraitQueryMode::Standard); + selcx.evaluate_obligation_recursively(obligation) + .expect("Overflow should be caught earlier in standard query mode") + } + } } } diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs index fdf6dcf4bf37d..4ba3655bb644a 100644 --- a/src/librustc/traits/select.rs +++ b/src/librustc/traits/select.rs @@ -425,6 +425,8 @@ impl_stable_hash_for!(enum self::EvaluationResult { /// Indicates that trait evaluation caused overflow. pub struct OverflowError; +impl_stable_hash_for!(struct OverflowError { }); + impl<'tcx> From for SelectionError<'tcx> { fn from(OverflowError: OverflowError) -> SelectionError<'tcx> { SelectionError::Overflow @@ -568,20 +570,23 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { let stack = self.push_stack(TraitObligationStackList::empty(), obligation); - // `select` is currently only called in standard query mode - assert!(self.query_mode == TraitQueryMode::Standard); - let candidate = match self.candidate_from_obligation(&stack) { - Err(SelectionError::Overflow) => - bug!("Overflow should be caught earlier in standard query mode"), + Err(SelectionError::Overflow) => { + // In standard mode, overflow must have been caught and reported + // earlier. + assert!(self.query_mode == TraitQueryMode::Canonical); + return Err(SelectionError::Overflow); + }, Err(e) => { return Err(e); }, Ok(None) => { return Ok(None); }, Ok(Some(candidate)) => candidate }; match self.confirm_candidate(obligation, candidate) { - Err(SelectionError::Overflow) => - bug!("Overflow should be caught earlier in standard query mode"), + Err(SelectionError::Overflow) => { + assert!(self.query_mode == TraitQueryMode::Canonical); + return Err(SelectionError::Overflow); + }, Err(e) => Err(e), Ok(candidate) => Ok(Some(candidate)) } diff --git a/src/librustc/ty/maps/mod.rs b/src/librustc/ty/maps/mod.rs index 9343eccd38e9f..cb929225bcdcf 100644 --- a/src/librustc/ty/maps/mod.rs +++ b/src/librustc/ty/maps/mod.rs @@ -436,8 +436,9 @@ define_maps! { <'tcx> /// Do not call this query directly: invoke `infcx.predicate_may_hold()` or /// `infcx.predicate_must_hold()` instead. - [] fn evaluate_obligation: - EvaluateObligation(CanonicalPredicateGoal<'tcx>) -> traits::EvaluationResult, + [] fn evaluate_obligation: EvaluateObligation( + CanonicalPredicateGoal<'tcx> + ) -> Result, [] fn substitute_normalize_and_test_predicates: substitute_normalize_and_test_predicates_node((DefId, &'tcx Substs<'tcx>)) -> bool, diff --git a/src/librustc_traits/evaluate_obligation.rs b/src/librustc_traits/evaluate_obligation.rs index f346bb8dc996b..21259bbcd38ff 100644 --- a/src/librustc_traits/evaluate_obligation.rs +++ b/src/librustc_traits/evaluate_obligation.rs @@ -17,7 +17,7 @@ use syntax::codemap::DUMMY_SP; crate fn evaluate_obligation<'tcx>( tcx: TyCtxt<'_, 'tcx, 'tcx>, goal: CanonicalPredicateGoal<'tcx>, -) -> EvaluationResult { +) -> Result { tcx.infer_ctxt().enter(|ref infcx| { let ( ParamEnvAnd { @@ -30,11 +30,6 @@ crate fn evaluate_obligation<'tcx>( let mut selcx = SelectionContext::with_query_mode(&infcx, TraitQueryMode::Canonical); let obligation = Obligation::new(ObligationCause::dummy(), param_env, predicate); - match selcx.evaluate_obligation_recursively(&obligation) { - Ok(result) => result, - Err(OverflowError) => { - infcx.report_overflow_error(&obligation, true) - } - } + selcx.evaluate_obligation_recursively(&obligation) }) }