Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create a canonical trait query for evaluate_obligation #48995

Merged
merged 8 commits into from
Apr 27, 2018
4 changes: 3 additions & 1 deletion src/librustc/dep_graph/dep_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ use rustc_data_structures::stable_hasher::{StableHasher, HashStable};
use std::fmt;
use std::hash::Hash;
use syntax_pos::symbol::InternedString;
use traits::query::{CanonicalProjectionGoal, CanonicalTyGoal};
use traits::query::{CanonicalProjectionGoal,
CanonicalTyGoal, CanonicalPredicateGoal};
use ty::{TyCtxt, Instance, InstanceDef, ParamEnv, ParamEnvAnd, PolyTraitRef, Ty};
use ty::subst::Substs;

Expand Down Expand Up @@ -643,6 +644,7 @@ define_dep_nodes!( <'tcx>
[] NormalizeProjectionTy(CanonicalProjectionGoal<'tcx>),
[] NormalizeTyAfterErasingRegions(ParamEnvAnd<'tcx, Ty<'tcx>>),
[] DropckOutlives(CanonicalTyGoal<'tcx>),
[] EvaluateObligation(CanonicalPredicateGoal<'tcx>),

[] SubstituteNormalizeAndTestPredicates { key: (DefId, &'tcx Substs<'tcx>) },

Expand Down
5 changes: 4 additions & 1 deletion src/librustc/traits/coherence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,10 @@ fn overlap<'cx, 'gcx, 'tcx>(selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
recursion_depth: 0,
predicate: p })
.chain(obligations)
.find(|o| !selcx.evaluate_obligation(o));
.find(|o| !selcx.predicate_may_hold_fatal(o));
// FIXME: the call to `selcx.predicate_may_hold_fatal` above should be ported
// to the canonical trait query form, `infcx.predicate_may_hold`, once
// the new system supports intercrate mode (which coherence needs).

if let Some(failing_obligation) = opt_failing_obligation {
debug!("overlap: obligation unsatisfiable {:?}", failing_obligation);
Expand Down
13 changes: 8 additions & 5 deletions src/librustc/traits/error_reporting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ use super::{
SelectionContext,
SelectionError,
ObjectSafetyViolation,
Overflow,
};

use errors::DiagnosticBuilder;
Expand Down Expand Up @@ -659,8 +660,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
predicate: ty::Predicate::Trait(predicate),
.. obligation.clone()
};
let mut selcx = SelectionContext::new(self);
if selcx.evaluate_obligation(&unit_obligation) {
if self.predicate_may_hold(&unit_obligation) {
err.note("the trait is implemented for `()`. \
Possibly this error has been caused by changes to \
Rust's type-inference algorithm \
Expand Down Expand Up @@ -830,6 +830,10 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
}
err.struct_error(self.tcx, span, "constant expression")
}

Overflow => {
bug!("overflow should be handled before the `report_selection_error` path");
}
};
self.note_obligation_cause(&mut err, obligation);
err.emit();
Expand Down Expand Up @@ -872,7 +876,6 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
.count();

let mut trait_type = trait_ref.self_ty();
let mut selcx = SelectionContext::new(self);

for refs_remaining in 0..refs_number {
if let ty::TypeVariants::TyRef(_, ty::TypeAndMut{ ty: t_type, mutbl: _ }) =
Expand All @@ -886,7 +889,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
obligation.param_env,
new_trait_ref.to_predicate());

if selcx.evaluate_obligation(&new_obligation) {
if self.predicate_may_hold(&new_obligation) {
let sp = self.tcx.sess.codemap()
.span_take_while(span, |c| c.is_whitespace() || *c == '&');

Expand Down Expand Up @@ -1322,7 +1325,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
cleaned_pred.to_predicate()
);

selcx.evaluate_obligation(&obligation)
self.predicate_may_hold(&obligation)
})
}

Expand Down
2 changes: 1 addition & 1 deletion src/librustc/traits/fulfill.rs
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ fn process_predicate<'a, 'gcx, 'tcx>(
if data.is_global() {
// no type variables present, can use evaluation for better caching.
// FIXME: consider caching errors too.
if selcx.evaluate_obligation_conservatively(&obligation) {
if selcx.infcx().predicate_must_hold(&obligation) {
debug!("selecting trait `{:?}` at depth {} evaluated to holds",
data, obligation.recursion_depth);
return Ok(Some(vec![]))
Expand Down
19 changes: 16 additions & 3 deletions src/librustc/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ pub use self::object_safety::ObjectSafetyViolation;
pub use self::object_safety::MethodViolationCode;
pub use self::on_unimplemented::{OnUnimplementedDirective, OnUnimplementedNote};
pub use self::select::{EvaluationCache, SelectionContext, SelectionCache};
pub use self::select::IntercrateAmbiguityCause;
pub use self::select::{EvaluationResult, IntercrateAmbiguityCause, OverflowError};
pub use self::specialize::{OverlapError, specialization_graph, translate_substs};
pub use self::specialize::{SpecializesCache, find_associated_item};
pub use self::engine::TraitEngine;
Expand Down Expand Up @@ -74,6 +74,19 @@ pub enum IntercrateMode {
Fixed
}

// The mode that trait queries run in
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum TraitQueryMode {
// Standard/un-canonicalized queries get accurate
// spans etc. passed in and hence can do reasonable
// error reporting on their own.
Standard,
// Canonicalized queries get dummy spans and hence
// must generally propagate errors to
// pre-canonicalization callsites.
Canonical,
}

/// An `Obligation` represents some trait reference (e.g. `int:Eq`) for
/// which the vtable must be found. The process of finding a vtable is
/// called "resolving" the `Obligation`. This process consists of
Expand Down Expand Up @@ -349,6 +362,7 @@ pub enum SelectionError<'tcx> {
ty::error::TypeError<'tcx>),
TraitNotObjectSafe(DefId),
ConstEvalFailure(ConstEvalErr<'tcx>),
Overflow,
}

pub struct FulfillmentError<'tcx> {
Expand Down Expand Up @@ -550,8 +564,7 @@ pub fn type_known_to_meet_bound<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx
predicate: trait_ref.to_predicate(),
};

let result = SelectionContext::new(infcx)
.evaluate_obligation_conservatively(&obligation);
let result = infcx.predicate_must_hold(&obligation);
debug!("type_known_to_meet_ty={:?} bound={} => {:?}",
ty, infcx.tcx.item_path_str(def_id), result);

Expand Down
70 changes: 70 additions & 0 deletions src/librustc/traits/query/evaluate_obligation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use infer::InferCtxt;
use infer::canonical::{Canonical, Canonicalize};
use traits::{EvaluationResult, PredicateObligation, SelectionContext,
TraitQueryMode, OverflowError};
use traits::query::CanonicalPredicateGoal;
use ty::{ParamEnvAnd, Predicate, TyCtxt};

impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
/// Evaluates whether the predicate can be satisfied (by any means)
/// in the given `ParamEnv`.
pub fn predicate_may_hold(
&self,
obligation: &PredicateObligation<'tcx>,
) -> bool {
self.evaluate_obligation(obligation).may_apply()
}

/// Evaluates whether the predicate can be satisfied in the given
/// `ParamEnv`, and returns `false` if not certain. However, this is
/// not entirely accurate if inference variables are involved.
pub fn predicate_must_hold(
&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));
// 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")
}
}
}
}

impl<'gcx: 'tcx, 'tcx> Canonicalize<'gcx, 'tcx> for ParamEnvAnd<'tcx, Predicate<'tcx>> {
type Canonicalized = CanonicalPredicateGoal<'gcx>;

fn intern(
_gcx: TyCtxt<'_, 'gcx, 'gcx>,
value: Canonical<'gcx, Self::Lifted>,
) -> Self::Canonicalized {
value
}
}
4 changes: 4 additions & 0 deletions src/librustc/traits/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use infer::canonical::Canonical;
use ty::{self, Ty};

pub mod dropck_outlives;
pub mod evaluate_obligation;
pub mod normalize;
pub mod normalize_erasing_regions;

Expand All @@ -27,6 +28,9 @@ pub type CanonicalProjectionGoal<'tcx> =

pub type CanonicalTyGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, Ty<'tcx>>>;

pub type CanonicalPredicateGoal<'tcx> =
Canonical<'tcx, ty::ParamEnvAnd<'tcx, ty::Predicate<'tcx>>>;

#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct NoSolution;

Expand Down
Loading