Skip to content

Commit

Permalink
Retry canonical trait query in standard mode if overflow occurs
Browse files Browse the repository at this point in the history
This is slightly hacky and hopefully only a somewhat temporary solution.
  • Loading branch information
aravind-pg committed Apr 27, 2018
1 parent 5cb0372 commit d5b2e90
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 24 deletions.
32 changes: 24 additions & 8 deletions src/librustc/traits/query/evaluate_obligation.rs
Expand Up @@ -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};

Expand All @@ -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
Expand All @@ -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")
}
}
}
}

Expand Down
19 changes: 12 additions & 7 deletions src/librustc/traits/select.rs
Expand Up @@ -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<OverflowError> for SelectionError<'tcx> {
fn from(OverflowError: OverflowError) -> SelectionError<'tcx> {
SelectionError::Overflow
Expand Down Expand Up @@ -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))
}
Expand Down
5 changes: 3 additions & 2 deletions src/librustc/ty/maps/mod.rs
Expand Up @@ -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<traits::EvaluationResult, traits::OverflowError>,

[] fn substitute_normalize_and_test_predicates:
substitute_normalize_and_test_predicates_node((DefId, &'tcx Substs<'tcx>)) -> bool,
Expand Down
9 changes: 2 additions & 7 deletions src/librustc_traits/evaluate_obligation.rs
Expand Up @@ -17,7 +17,7 @@ use syntax::codemap::DUMMY_SP;
crate fn evaluate_obligation<'tcx>(
tcx: TyCtxt<'_, 'tcx, 'tcx>,
goal: CanonicalPredicateGoal<'tcx>,
) -> EvaluationResult {
) -> Result<EvaluationResult, OverflowError> {
tcx.infer_ctxt().enter(|ref infcx| {
let (
ParamEnvAnd {
Expand All @@ -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)
})
}

0 comments on commit d5b2e90

Please sign in to comment.