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

Introduce trait engine #49202

Merged
merged 7 commits into from
Mar 27, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/librustc/infer/outlives/bounds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
use infer::InferCtxt;
use syntax::ast;
use syntax::codemap::Span;
use traits::FulfillmentContext;
use traits::{FulfillmentContext, TraitEngine};
use ty::{self, Ty, TypeFoldable};
use ty::outlives::Component;
use ty::wf;
Expand Down
71 changes: 71 additions & 0 deletions src/librustc/traits/engine.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// 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 ty::{self, Ty, TyCtxt};
use hir::def_id::DefId;

use super::{FulfillmentContext, FulfillmentError};
use super::{ObligationCause, PendingPredicateObligation, PredicateObligation};

pub trait TraitEngine<'tcx>: 'tcx {
fn normalize_projection_type<'a, 'gcx>(
&mut self,
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
projection_ty: ty::ProjectionTy<'tcx>,
cause: ObligationCause<'tcx>,
) -> Ty<'tcx>;

fn register_bound<'a, 'gcx>(
&mut self,
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
ty: Ty<'tcx>,
def_id: DefId,
cause: ObligationCause<'tcx>,
);

fn register_predicate_obligation<'a, 'gcx>(
&mut self,
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
obligation: PredicateObligation<'tcx>,
);

fn select_all_or_error<'a, 'gcx>(
&mut self,
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
) -> Result<(), Vec<FulfillmentError<'tcx>>>;

fn select_where_possible<'a, 'gcx>(
&mut self,
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
) -> Result<(), Vec<FulfillmentError<'tcx>>>;

fn pending_obligations(&self) -> Vec<PendingPredicateObligation<'tcx>>;
}

impl<'a, 'gcx, 'tcx> dyn TraitEngine<'tcx> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be:

impl<'a, 'gcx, 'tcx> dyn TraitEngine<'tcx> + 'tcx {
  ..
}

Whenever you create a trait object type dyn Trait, if that trait object may close over references, then the lifetime of those references needs to be part of its bound. In this case, FulfillmentContext closes over data of type with lifetime 'tcx, so we need dyn TraitEngine<'tcx> + 'tcx.

pub fn new(_tcx: TyCtxt<'_, '_, 'tcx>) -> Box<Self> {
Box::new(FulfillmentContext::new())
}

pub fn register_predicate_obligations<I>(
&mut self,
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
obligations: I,
) where
I: IntoIterator<Item = PredicateObligation<'tcx>>,
{
for obligation in obligations {
self.register_predicate_obligation(infcx, obligation);
}
}
}
149 changes: 76 additions & 73 deletions src/librustc/traits/fulfill.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use middle::const_val::{ConstEvalErr, ErrKind};
use super::CodeAmbiguity;
use super::CodeProjectionError;
use super::CodeSelectionError;
use super::engine::TraitEngine;
use super::{FulfillmentError, FulfillmentErrorCode};
use super::{ObligationCause, PredicateObligation, Obligation};
use super::project;
Expand Down Expand Up @@ -85,19 +86,72 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> {
}
}

pub fn register_predicate_obligations<I>(&mut self,
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
obligations: I)
where I: IntoIterator<Item = PredicateObligation<'tcx>>
{
for obligation in obligations {
self.register_predicate_obligation(infcx, obligation);
}
}

/// Attempts to select obligations using `selcx`. If `only_new_obligations` is true, then it
/// only attempts to select obligations that haven't been seen before.
fn select(&mut self, selcx: &mut SelectionContext<'a, 'gcx, 'tcx>)
-> Result<(),Vec<FulfillmentError<'tcx>>> {
debug!("select(obligation-forest-size={})", self.predicates.len());

let mut errors = Vec::new();

loop {
debug!("select: starting another iteration");

// Process pending obligations.
let outcome = self.predicates.process_obligations(&mut FulfillProcessor {
selcx,
register_region_obligations: self.register_region_obligations
});
debug!("select: outcome={:?}", outcome);

// FIXME: if we kept the original cache key, we could mark projection
// obligations as complete for the projection cache here.

errors.extend(
outcome.errors.into_iter()
.map(|e| to_fulfillment_error(e)));

// If nothing new was added, no need to keep looping.
if outcome.stalled {
break;
}
}

debug!("select({} predicates remaining, {} errors) done",
self.predicates.len(), errors.len());

if errors.is_empty() {
Ok(())
} else {
Err(errors)
}
}
}

impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> {
/// "Normalize" a projection type `<SomeType as SomeTrait>::X` by
/// creating a fresh type variable `$0` as well as a projection
/// predicate `<SomeType as SomeTrait>::X == $0`. When the
/// inference engine runs, it will attempt to find an impl of
/// `SomeTrait` or a where clause that lets us unify `$0` with
/// something concrete. If this fails, we'll unify `$0` with
/// `projection_ty` again.
pub fn normalize_projection_type(&mut self,
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
projection_ty: ty::ProjectionTy<'tcx>,
cause: ObligationCause<'tcx>)
-> Ty<'tcx>
fn normalize_projection_type<'a, 'gcx>(&mut self,
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
projection_ty: ty::ProjectionTy<'tcx>,
cause: ObligationCause<'tcx>)
-> Ty<'tcx>
{
debug!("normalize_projection_type(projection_ty={:?})",
projection_ty);
Expand Down Expand Up @@ -125,12 +179,12 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> {
/// Requires that `ty` must implement the trait with `def_id` in
/// the given environment. This trait must not have any type
/// parameters (except for `Self`).
pub fn register_bound(&mut self,
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
ty: Ty<'tcx>,
def_id: DefId,
cause: ObligationCause<'tcx>)
fn register_bound<'a, 'gcx>(&mut self,
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
ty: Ty<'tcx>,
def_id: DefId,
cause: ObligationCause<'tcx>)
{
let trait_ref = ty::TraitRef {
def_id,
Expand All @@ -144,9 +198,9 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> {
});
}

pub fn register_predicate_obligation(&mut self,
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
obligation: PredicateObligation<'tcx>)
fn register_predicate_obligation<'a, 'gcx>(&mut self,
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
obligation: PredicateObligation<'tcx>)
{
// this helps to reduce duplicate errors, as well as making
// debug output much nicer to read and so on.
Expand All @@ -162,19 +216,9 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> {
});
}

pub fn register_predicate_obligations<I>(&mut self,
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
obligations: I)
where I: IntoIterator<Item = PredicateObligation<'tcx>>
{
for obligation in obligations {
self.register_predicate_obligation(infcx, obligation);
}
}

pub fn select_all_or_error(&mut self,
infcx: &InferCtxt<'a, 'gcx, 'tcx>)
-> Result<(),Vec<FulfillmentError<'tcx>>>
fn select_all_or_error<'a, 'gcx>(&mut self,
infcx: &InferCtxt<'a, 'gcx, 'tcx>)
-> Result<(),Vec<FulfillmentError<'tcx>>>
{
self.select_where_possible(infcx)?;

Expand All @@ -190,58 +234,17 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> {
}
}

pub fn select_where_possible(&mut self,
infcx: &InferCtxt<'a, 'gcx, 'tcx>)
-> Result<(),Vec<FulfillmentError<'tcx>>>
fn select_where_possible<'a, 'gcx>(&mut self,
infcx: &InferCtxt<'a, 'gcx, 'tcx>)
-> Result<(),Vec<FulfillmentError<'tcx>>>
{
let mut selcx = SelectionContext::new(infcx);
self.select(&mut selcx)
}

pub fn pending_obligations(&self) -> Vec<PendingPredicateObligation<'tcx>> {
fn pending_obligations(&self) -> Vec<PendingPredicateObligation<'tcx>> {
self.predicates.pending_obligations()
}

/// Attempts to select obligations using `selcx`. If `only_new_obligations` is true, then it
/// only attempts to select obligations that haven't been seen before.
fn select(&mut self, selcx: &mut SelectionContext<'a, 'gcx, 'tcx>)
-> Result<(),Vec<FulfillmentError<'tcx>>> {
debug!("select(obligation-forest-size={})", self.predicates.len());

let mut errors = Vec::new();

loop {
debug!("select: starting another iteration");

// Process pending obligations.
let outcome = self.predicates.process_obligations(&mut FulfillProcessor {
selcx,
register_region_obligations: self.register_region_obligations
});
debug!("select: outcome={:?}", outcome);

// FIXME: if we kept the original cache key, we could mark projection
// obligations as complete for the projection cache here.

errors.extend(
outcome.errors.into_iter()
.map(|e| to_fulfillment_error(e)));

// If nothing new was added, no need to keep looping.
if outcome.stalled {
break;
}
}

debug!("select({} predicates remaining, {} errors) done",
self.predicates.len(), errors.len());

if errors.is_empty() {
Ok(())
} else {
Err(errors)
}
}
}

struct FulfillProcessor<'a, 'b: 'a, 'gcx: 'tcx, 'tcx: 'b> {
Expand Down
4 changes: 3 additions & 1 deletion src/librustc/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ use syntax::ast;
use syntax_pos::{Span, DUMMY_SP};

pub use self::coherence::{orphan_check, overlapping_impls, OrphanCheckErr, OverlapResult};
pub use self::fulfill::FulfillmentContext;
pub use self::fulfill::{FulfillmentContext, PendingPredicateObligation};
pub use self::project::MismatchedProjectionTypes;
pub use self::project::{normalize, normalize_projection_type, poly_project_and_unify_type};
pub use self::project::{ProjectionCache, ProjectionCacheSnapshot, Reveal, Normalized};
Expand All @@ -45,6 +45,7 @@ pub use self::select::{EvaluationCache, SelectionContext, SelectionCache};
pub use self::select::IntercrateAmbiguityCause;
pub use self::specialize::{OverlapError, specialization_graph, translate_substs};
pub use self::specialize::{SpecializesCache, find_associated_item};
pub use self::engine::TraitEngine;
pub use self::util::elaborate_predicates;
pub use self::util::supertraits;
pub use self::util::Supertraits;
Expand All @@ -54,6 +55,7 @@ pub use self::util::transitive_bounds;

mod coherence;
pub mod error_reporting;
mod engine;
mod fulfill;
mod project;
mod object_safety;
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/traits/specialize/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use hir::def_id::DefId;
use infer::{InferCtxt, InferOk};
use ty::subst::{Subst, Substs};
use traits::{self, ObligationCause};
use traits::{self, ObligationCause, TraitEngine};
use traits::select::IntercrateAmbiguityCause;
use ty::{self, TyCtxt, TypeFoldable};
use syntax_pos::DUMMY_SP;
Expand Down
3 changes: 2 additions & 1 deletion src/librustc/traits/trans/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ use std::marker::PhantomData;
use syntax_pos::DUMMY_SP;
use infer::InferCtxt;
use syntax_pos::Span;
use traits::{FulfillmentContext, Obligation, ObligationCause, SelectionContext, Vtable};
use traits::{FulfillmentContext, Obligation, ObligationCause, SelectionContext,
TraitEngine, Vtable};
use ty::{self, Ty, TyCtxt};
use ty::subst::{Subst, Substs};
use ty::fold::TypeFoldable;
Expand Down
4 changes: 2 additions & 2 deletions src/librustc_mir/borrow_check/nll/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use dataflow::move_paths::MoveData;
use rustc::hir::def_id::DefId;
use rustc::infer::{InferCtxt, InferOk, InferResult, LateBoundRegionConversionTime, UnitResult};
use rustc::infer::region_constraints::{GenericKind, RegionConstraintData};
use rustc::traits::{self, Normalized, FulfillmentContext};
use rustc::traits::{self, Normalized, TraitEngine};
use rustc::traits::query::NoSolution;
use rustc::ty::error::TypeError;
use rustc::ty::fold::TypeFoldable;
Expand Down Expand Up @@ -662,7 +662,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
where
OP: FnOnce(&mut Self) -> InferResult<'tcx, R>,
{
let mut fulfill_cx = FulfillmentContext::new();
let mut fulfill_cx = TraitEngine::new(self.infcx.tcx);
let InferOk { value, obligations } = self.infcx.commit_if_ok(|_| op(self))?;
fulfill_cx.register_predicate_obligations(self.infcx, obligations);
if let Err(e) = fulfill_cx.select_all_or_error(self.infcx) {
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_mir/transform/qualify_consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use rustc_data_structures::fx::FxHashSet;
use rustc::hir;
use rustc::hir::def_id::DefId;
use rustc::middle::const_val::ConstVal;
use rustc::traits;
use rustc::traits::{self, TraitEngine};
use rustc::ty::{self, TyCtxt, Ty, TypeFoldable};
use rustc::ty::cast::CastTy;
use rustc::ty::maps::Providers;
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_traits/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use rustc::infer::InferCtxt;
use rustc::infer::canonical::{CanonicalVarValues, Canonicalize, Certainty, QueryRegionConstraints,
QueryResult};
use rustc::infer::region_constraints::{Constraint, RegionConstraintData};
use rustc::traits::FulfillmentContext;
use rustc::traits::{FulfillmentContext, TraitEngine};
use rustc::traits::query::NoSolution;
use rustc::ty;
use std::fmt::Debug;
Expand Down
4 changes: 2 additions & 2 deletions src/librustc_typeck/check/dropck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use rustc::infer::outlives::env::OutlivesEnvironment;
use rustc::middle::region;
use rustc::ty::subst::{Subst, Substs, UnpackedKind};
use rustc::ty::{self, Ty, TyCtxt};
use rustc::traits::{self, ObligationCause};
use rustc::traits::{ObligationCause, TraitEngine};
use util::common::ErrorReported;

use syntax::ast;
Expand Down Expand Up @@ -84,7 +84,7 @@ fn ensure_drop_params_and_item_params_correspond<'a, 'tcx>(
tcx.infer_ctxt().enter(|ref infcx| {
let impl_param_env = tcx.param_env(self_type_did);
let tcx = infcx.tcx;
let mut fulfillment_cx = traits::FulfillmentContext::new();
let mut fulfillment_cx = TraitEngine::new(tcx);

let named_type = tcx.type_of(self_type_did);

Expand Down
Loading