// A list of all obligations that have been registered with this
// fulfillment context.
trait_obligations: Vec<Obligation>,
+
+ // 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 select_new_obligations<'a,'tcx>(&mut self,
+ infcx: &InferCtxt<'a,'tcx>,
+ param_env: &ty::ParameterEnvironment,
+ typer: &Typer<'tcx>)
+ -> Result<(),Vec<FulfillmentError>>
+ {
+ /*!
+ * 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<FulfillmentError>>
{
- let tcx = infcx.tcx;
let mut selcx = SelectionContext::new(infcx, param_env, typer);
+ self.select(&mut selcx, false)
+ }
- debug!("select_where_possible({} obligations) start",
- self.trait_obligations.len());
+ fn select(&mut self,
+ selcx: &mut SelectionContext,
+ only_new_obligations: bool)
+ -> Result<(),Vec<FulfillmentError>>
+ {
+ /*!
+ * 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 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| {
- 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
+ // 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;
}
}
- debug!("select_where_possible({} obligations, {} errors) done",
+ debug!("select({} obligations, {} errors) done",
self.trait_obligations.len(),
errors.len());
use middle::typeck::astconv::AstConv;
use middle::typeck::check::{FnCtxt, NoPreference, PreferMutLvalue};
use middle::typeck::check::{impl_self_ty};
-use middle::typeck::check::vtable::select_fcx_obligations_where_possible;
+use middle::typeck::check::vtable::select_new_fcx_obligations;
use middle::typeck::check;
use middle::typeck::infer;
use middle::typeck::{MethodCall, MethodCallee};
// the `Self` trait).
let callee = self.confirm_candidate(rcvr_ty, &candidate);
- select_fcx_obligations_where_possible(self.fcx);
+ select_new_fcx_obligations(self.fcx);
Some(Ok(callee))
}
use middle::typeck::check::method::{CheckTraitsAndInherentMethods};
use middle::typeck::check::regionmanip::replace_late_bound_regions;
use middle::typeck::CrateCtxt;
-use middle::typeck::infer::{resolve_type, force_tvar};
use middle::typeck::infer;
use middle::typeck::rscope::RegionScope;
use middle::typeck::{lookup_def_ccx};
}
// casts from C-like enums are allowed
} else if t_1_is_char {
- let t_e = fcx.infcx().resolve_type_vars_if_possible(t_e);
+ let t_e = fcx.infcx().shallow_resolve(t_e);
if ty::get(t_e).sty != ty::ty_uint(ast::TyU8) {
fcx.type_error_message(span, |actual| {
format!("only `u8` can be cast as \
// an "opportunistic" vtable resolution of any trait
// bounds on the call.
if check_blocks {
- vtable::select_fcx_obligations_where_possible(fcx);
+ vtable::select_new_fcx_obligations(fcx);
}
// For variadic functions, we don't have a declared type for all of
// 'else' branch.
let expected = match expected.only_has_type() {
ExpectHasType(ety) => {
- match infer::resolve_type(fcx.infcx(), Some(sp), ety, force_tvar) {
- Ok(rty) if !ty::type_is_ty_var(rty) => ExpectHasType(rty),
- _ => NoExpectation
+ let ety = fcx.infcx().shallow_resolve(ety);
+ if !ty::type_is_ty_var(ety) {
+ ExpectHasType(ety)
+ } else {
+ NoExpectation
}
}
_ => NoExpectation
ast::ExprForLoop(ref pat, ref head, ref block, _) => {
check_expr(fcx, &**head);
let typ = lookup_method_for_for_loop(fcx, &**head, expr.id);
- vtable::select_fcx_obligations_where_possible(fcx);
+ vtable::select_new_fcx_obligations(fcx);
let pcx = pat_ctxt {
fcx: fcx,
// Resolves `typ` by a single level if `typ` is a type variable. If no
// resolution is possible, then an error is reported.
-pub fn structurally_resolved_type(fcx: &FnCtxt, sp: Span, tp: ty::t) -> ty::t {
- match infer::resolve_type(fcx.infcx(), Some(sp), tp, force_tvar) {
- Ok(t_s) if !ty::type_is_ty_var(t_s) => t_s,
- _ => {
- fcx.type_error_message(sp, |_actual| {
- "the type of this value must be known in this \
- context".to_string()
- }, tp, None);
- demand::suptype(fcx, sp, ty::mk_err(), tp);
- tp
- }
+pub fn structurally_resolved_type(fcx: &FnCtxt, sp: Span, mut ty: ty::t) -> ty::t {
+ // If `ty` is a type variable, see whether we already know what it is.
+ ty = fcx.infcx().shallow_resolve(ty);
+
+ // If not, try resolve pending fcx obligations. Those can shed light.
+ //
+ // FIXME(#18391) -- This current strategy can lead to bad performance in
+ // extreme cases. We probably ought to smarter in general about
+ // only resolving when we need help and only resolving obligations
+ // will actually help.
+ if ty::type_is_ty_var(ty) {
+ vtable::select_fcx_obligations_where_possible(fcx);
+ ty = fcx.infcx().shallow_resolve(ty);
}
+
+ // If not, error.
+ if ty::type_is_ty_var(ty) {
+ fcx.type_error_message(sp, |_actual| {
+ "the type of this value must be known in this \
+ context".to_string()
+ }, ty, None);
+ demand::suptype(fcx, sp, ty::mk_err(), ty);
+ ty = ty::mk_err();
+ }
+
+ ty
}
// Returns the one-level-deep structure of the given type.
}
}
+pub fn select_new_fcx_obligations(fcx: &FnCtxt) {
+ /*!
+ * Try to select any fcx obligation that we haven't tried yet,
+ * in an effort to improve inference. You could just call
+ * `select_fcx_obligations_where_possible` except that it leads
+ * to repeated work.
+ */
+
+ match
+ fcx.inh.fulfillment_cx
+ .borrow_mut()
+ .select_new_obligations(fcx.infcx(), &fcx.inh.param_env, fcx)
+ {
+ Ok(()) => { }
+ Err(errors) => { report_fulfillment_errors(fcx, &errors); }
+ }
+}
+
fn note_obligation_cause(fcx: &FnCtxt,
obligation: &Obligation) {
let tcx = fcx.tcx();