2014-09-12 10:53:35 -04:00
|
|
|
// Copyright 2014 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.
|
|
|
|
|
2014-09-18 11:08:04 -04:00
|
|
|
use middle::mem_categorization::Typer;
|
2014-09-12 10:53:35 -04:00
|
|
|
use middle::ty;
|
2014-09-18 11:08:04 -04:00
|
|
|
use middle::typeck::infer::InferCtxt;
|
2014-09-12 10:53:35 -04:00
|
|
|
use util::ppaux::Repr;
|
|
|
|
|
2014-09-11 17:07:49 +12:00
|
|
|
use super::CodeAmbiguity;
|
2014-09-12 10:53:35 -04:00
|
|
|
use super::Obligation;
|
|
|
|
use super::FulfillmentError;
|
2014-09-11 17:07:49 +12:00
|
|
|
use super::CodeSelectionError;
|
2014-09-12 10:53:35 -04:00
|
|
|
use super::select::SelectionContext;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The fulfillment context is used to drive trait resolution. It
|
|
|
|
* consists of a list of obligations that must be (eventually)
|
|
|
|
* satisfied. The job is to track which are satisfied, which yielded
|
|
|
|
* errors, and which are still pending. At any point, users can call
|
|
|
|
* `select_where_possible`, and the fulfilment context will try to do
|
|
|
|
* selection, retaining only those obligations that remain
|
|
|
|
* ambiguous. This may be helpful in pushing type inference
|
|
|
|
* along. Once all type inference constraints have been generated, the
|
|
|
|
* method `select_all_or_error` can be used to report any remaining
|
|
|
|
* ambiguous cases as errors.
|
|
|
|
*/
|
2014-09-29 22:11:30 +03:00
|
|
|
pub struct FulfillmentContext<'tcx> {
|
2014-09-12 10:53:35 -04:00
|
|
|
// A list of all obligations that have been registered with this
|
|
|
|
// fulfillment context.
|
2014-09-29 22:11:30 +03:00
|
|
|
trait_obligations: Vec<Obligation<'tcx>>,
|
2014-10-28 07:13:15 -04:00
|
|
|
|
|
|
|
// Remembers the count of trait obligations that we have already
|
|
|
|
// attempted to select. This is used to avoid repeating work
|
|
|
|
// when `select_new_obligations` is called.
|
|
|
|
attempted_mark: uint,
|
2014-09-12 10:53:35 -04:00
|
|
|
}
|
|
|
|
|
2014-09-29 22:11:30 +03:00
|
|
|
impl<'tcx> FulfillmentContext<'tcx> {
|
|
|
|
pub fn new() -> FulfillmentContext<'tcx> {
|
2014-09-12 10:53:35 -04:00
|
|
|
FulfillmentContext {
|
|
|
|
trait_obligations: Vec::new(),
|
2014-10-28 07:13:15 -04:00
|
|
|
attempted_mark: 0,
|
2014-09-12 10:53:35 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn register_obligation(&mut self,
|
2014-09-29 22:11:30 +03:00
|
|
|
tcx: &ty::ctxt<'tcx>,
|
|
|
|
obligation: Obligation<'tcx>)
|
2014-09-12 10:53:35 -04:00
|
|
|
{
|
|
|
|
debug!("register_obligation({})", obligation.repr(tcx));
|
2014-11-15 17:09:51 -05:00
|
|
|
assert!(!obligation.trait_ref.has_escaping_regions());
|
2014-09-18 11:08:04 -04:00
|
|
|
self.trait_obligations.push(obligation);
|
2014-09-12 10:53:35 -04:00
|
|
|
}
|
|
|
|
|
2014-09-29 22:11:30 +03:00
|
|
|
pub fn select_all_or_error<'a>(&mut self,
|
|
|
|
infcx: &InferCtxt<'a,'tcx>,
|
|
|
|
param_env: &ty::ParameterEnvironment<'tcx>,
|
|
|
|
typer: &Typer<'tcx>)
|
|
|
|
-> Result<(),Vec<FulfillmentError<'tcx>>>
|
2014-09-12 10:53:35 -04:00
|
|
|
{
|
2014-09-18 11:08:04 -04:00
|
|
|
try!(self.select_where_possible(infcx, param_env, typer));
|
2014-09-12 10:53:35 -04:00
|
|
|
|
|
|
|
// Anything left is ambiguous.
|
|
|
|
let errors: Vec<FulfillmentError> =
|
|
|
|
self.trait_obligations
|
|
|
|
.iter()
|
2014-09-11 17:07:49 +12:00
|
|
|
.map(|o| FulfillmentError::new((*o).clone(), CodeAmbiguity))
|
2014-09-12 10:53:35 -04:00
|
|
|
.collect();
|
|
|
|
|
|
|
|
if errors.is_empty() {
|
|
|
|
Ok(())
|
|
|
|
} else {
|
|
|
|
Err(errors)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-29 22:11:30 +03:00
|
|
|
pub fn select_new_obligations<'a>(&mut self,
|
|
|
|
infcx: &InferCtxt<'a,'tcx>,
|
|
|
|
param_env: &ty::ParameterEnvironment<'tcx>,
|
|
|
|
typer: &Typer<'tcx>)
|
|
|
|
-> Result<(),Vec<FulfillmentError<'tcx>>>
|
2014-10-28 07:13:15 -04:00
|
|
|
{
|
|
|
|
/*!
|
|
|
|
* Attempts to select obligations that were registered since
|
|
|
|
* the call to a selection routine. This is used by the type checker
|
|
|
|
* to eagerly attempt to resolve obligations in hopes of gaining
|
|
|
|
* type information. It'd be equally valid to use `select_where_possible`
|
|
|
|
* but it results in `O(n^2)` performance (#18208).
|
|
|
|
*/
|
|
|
|
|
|
|
|
let mut selcx = SelectionContext::new(infcx, param_env, typer);
|
|
|
|
self.select(&mut selcx, true)
|
|
|
|
}
|
|
|
|
|
2014-09-29 22:11:30 +03:00
|
|
|
pub fn select_where_possible<'a>(&mut self,
|
|
|
|
infcx: &InferCtxt<'a,'tcx>,
|
|
|
|
param_env: &ty::ParameterEnvironment<'tcx>,
|
|
|
|
typer: &Typer<'tcx>)
|
|
|
|
-> Result<(),Vec<FulfillmentError<'tcx>>>
|
2014-09-12 10:53:35 -04:00
|
|
|
{
|
2014-09-18 11:08:04 -04:00
|
|
|
let mut selcx = SelectionContext::new(infcx, param_env, typer);
|
2014-10-28 07:13:15 -04:00
|
|
|
self.select(&mut selcx, false)
|
|
|
|
}
|
2014-09-12 10:53:35 -04:00
|
|
|
|
2014-09-29 22:11:30 +03:00
|
|
|
fn select<'a>(&mut self,
|
|
|
|
selcx: &mut SelectionContext<'a, 'tcx>,
|
|
|
|
only_new_obligations: bool)
|
|
|
|
-> Result<(),Vec<FulfillmentError<'tcx>>>
|
2014-10-28 07:13:15 -04:00
|
|
|
{
|
|
|
|
/*!
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
debug!("select({} obligations, only_new_obligations={}) start",
|
|
|
|
self.trait_obligations.len(),
|
|
|
|
only_new_obligations);
|
2014-09-12 10:53:35 -04:00
|
|
|
|
2014-10-28 07:13:15 -04:00
|
|
|
let tcx = selcx.tcx();
|
2014-09-12 10:53:35 -04:00
|
|
|
let mut errors = Vec::new();
|
|
|
|
|
|
|
|
loop {
|
|
|
|
let count = self.trait_obligations.len();
|
|
|
|
|
|
|
|
debug!("select_where_possible({} obligations) iteration",
|
|
|
|
count);
|
|
|
|
|
|
|
|
let mut selections = Vec::new();
|
|
|
|
|
2014-10-28 07:13:15 -04:00
|
|
|
// If we are only attempting obligations we haven't seen yet,
|
|
|
|
// then set `skip` to the number of obligations we've already
|
|
|
|
// seen.
|
|
|
|
let mut skip = if only_new_obligations {
|
|
|
|
self.attempted_mark
|
|
|
|
} else {
|
|
|
|
0
|
|
|
|
};
|
|
|
|
|
2014-09-12 10:53:35 -04:00
|
|
|
// First pass: walk each obligation, retaining
|
|
|
|
// only those that we cannot yet process.
|
|
|
|
self.trait_obligations.retain(|obligation| {
|
2014-10-28 07:13:15 -04:00
|
|
|
// Hack: Retain does not pass in the index, but we want
|
|
|
|
// to avoid processing the first `start_count` entries.
|
|
|
|
if skip > 0 {
|
|
|
|
skip -= 1;
|
|
|
|
true
|
|
|
|
} else {
|
|
|
|
match selcx.select(obligation) {
|
|
|
|
Ok(None) => {
|
|
|
|
true
|
|
|
|
}
|
|
|
|
Ok(Some(s)) => {
|
|
|
|
selections.push(s);
|
|
|
|
false
|
|
|
|
}
|
|
|
|
Err(selection_err) => {
|
|
|
|
debug!("obligation: {} error: {}",
|
|
|
|
obligation.repr(tcx),
|
|
|
|
selection_err.repr(tcx));
|
|
|
|
errors.push(FulfillmentError::new(
|
|
|
|
(*obligation).clone(),
|
|
|
|
CodeSelectionError(selection_err)));
|
|
|
|
false
|
|
|
|
}
|
2014-09-12 10:53:35 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2014-10-28 07:13:15 -04:00
|
|
|
self.attempted_mark = self.trait_obligations.len();
|
|
|
|
|
2014-09-12 10:53:35 -04:00
|
|
|
if self.trait_obligations.len() == count {
|
|
|
|
// Nothing changed.
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now go through all the successful ones,
|
|
|
|
// registering any nested obligations for the future.
|
2014-09-14 20:27:36 -07:00
|
|
|
for selection in selections.into_iter() {
|
2014-09-12 10:53:35 -04:00
|
|
|
selection.map_move_nested(
|
|
|
|
|o| self.register_obligation(tcx, o));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-28 07:13:15 -04:00
|
|
|
debug!("select({} obligations, {} errors) done",
|
2014-09-12 10:53:35 -04:00
|
|
|
self.trait_obligations.len(),
|
|
|
|
errors.len());
|
|
|
|
|
|
|
|
if errors.len() == 0 {
|
|
|
|
Ok(())
|
|
|
|
} else {
|
|
|
|
Err(errors)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|