Skip to content

Commit

Permalink
Auto merge of rust-lang#49202 - csmoe:trait_engine, r=nikomatsakis
Browse files Browse the repository at this point in the history
Introduce trait engine

address rust-lang#48895 step 1: introduce trait engine
  • Loading branch information
bors committed Mar 27, 2018
2 parents 3efe61c + 39712e5 commit 9c9424d
Show file tree
Hide file tree
Showing 13 changed files with 196 additions and 118 deletions.
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> {
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

0 comments on commit 9c9424d

Please sign in to comment.