// 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 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use middle::mem_categorization::Typer; use middle::ty; use middle::typeck::infer::InferCtxt; use util::ppaux::Repr; use super::CodeAmbiguity; use super::Obligation; use super::FulfillmentError; use super::CodeSelectionError; 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. */ pub struct FulfillmentContext { // A list of all obligations that have been registered with this // fulfillment context. trait_obligations: Vec, // 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, } impl FulfillmentContext { pub fn new() -> FulfillmentContext { FulfillmentContext { trait_obligations: Vec::new(), attempted_mark: 0, } } pub fn register_obligation(&mut self, tcx: &ty::ctxt, obligation: Obligation) { debug!("register_obligation({})", obligation.repr(tcx)); assert!(!obligation.trait_ref.has_escaping_regions()); self.trait_obligations.push(obligation); } pub fn select_all_or_error<'a,'tcx>(&mut self, infcx: &InferCtxt<'a,'tcx>, param_env: &ty::ParameterEnvironment, typer: &Typer<'tcx>) -> Result<(),Vec> { try!(self.select_where_possible(infcx, param_env, typer)); // Anything left is ambiguous. let errors: Vec = self.trait_obligations .iter() .map(|o| FulfillmentError::new((*o).clone(), CodeAmbiguity)) .collect(); if errors.is_empty() { Ok(()) } else { Err(errors) } } pub fn select_new_obligations<'a,'tcx>(&mut self, infcx: &InferCtxt<'a,'tcx>, param_env: &ty::ParameterEnvironment, typer: &Typer<'tcx>) -> Result<(),Vec> { /*! * 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) } pub fn select_where_possible<'a,'tcx>(&mut self, infcx: &InferCtxt<'a,'tcx>, param_env: &ty::ParameterEnvironment, typer: &Typer<'tcx>) -> Result<(),Vec> { let mut selcx = SelectionContext::new(infcx, param_env, typer); self.select(&mut selcx, false) } fn select(&mut self, selcx: &mut SelectionContext, only_new_obligations: bool) -> Result<(),Vec> { /*! * 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); let tcx = selcx.tcx(); let mut errors = Vec::new(); loop { let count = self.trait_obligations.len(); debug!("select_where_possible({} obligations) iteration", count); let mut selections = Vec::new(); // 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 }; // First pass: walk each obligation, retaining // only those that we cannot yet process. self.trait_obligations.retain(|obligation| { // 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 } } } }); self.attempted_mark = self.trait_obligations.len(); if self.trait_obligations.len() == count { // Nothing changed. break; } // Now go through all the successful ones, // registering any nested obligations for the future. for selection in selections.into_iter() { selection.map_move_nested( |o| self.register_obligation(tcx, o)); } } debug!("select({} obligations, {} errors) done", self.trait_obligations.len(), errors.len()); if errors.len() == 0 { Ok(()) } else { Err(errors) } } }