use syntax::codemap::{Span, DUMMY_SP};
use util::nodemap::{FnvHashMap, NodeMap};
+use ast_map;
use self::combine::CombineFields;
use self::region_inference::{RegionVarBindings, RegionSnapshot};
use self::error_reporting::ErrorReporting;
// We instantiate UnificationTable with bounds<Ty> because the
// types that might instantiate a general type variable have an
// order, represented by its upper and lower bounds.
- type_variables: RefCell<type_variable::TypeVariableTable<'tcx>>,
+ pub type_variables: RefCell<type_variable::TypeVariableTable<'tcx>>,
// Map from integral variable to the kind of integer it represents
int_unification_table: RefCell<UnificationTable<ty::IntVid>>,
variables.extend(unbound_ty_vars);
variables.extend(unbound_int_vars);
variables.extend(unbound_float_vars);
-
+
return variables;
}
}
pub fn type_vars_for_defs(&self,
+ span: Span,
+ // substs: Substs,
defs: &[ty::TypeParameterDef<'tcx>])
-> Vec<ty::Ty<'tcx>> {
+
+ fn definition_span<'tcx>(tcx: &ty::ctxt<'tcx>, def_id: ast::DefId) -> Span {
+ let parent = tcx.map.get_parent(def_id.node);
+ debug!("definition_span def_id={:?} parent={:?} node={:?} parent_node={:?}",
+ def_id, parent, tcx.map.find(def_id.node), tcx.map.find(parent));
+ match tcx.map.find(parent) {
+ None => DUMMY_SP,
+ Some(ref node) => match *node {
+ ast_map::NodeItem(ref item) => item.span,
+ ast_map::NodeForeignItem(ref item) => item.span,
+ ast_map::NodeTraitItem(ref item) => item.span,
+ ast_map::NodeImplItem(ref item) => item.span,
+ _ => DUMMY_SP
+ }
+ }
+ }
+
let mut substs = Substs::empty();
let mut vars = Vec::with_capacity(defs.len());
for def in defs.iter() {
let default = def.default.map(|default| {
type_variable::Default {
- ty: default
+ ty: default,
+ origin_span: span,
+ definition_span: definition_span(self.tcx, def.def_id)
}
});
//.subst(self.tcx, &substs)
let mut type_params = subst::VecPerParamSpace::empty();
for space in subst::ParamSpace::all().iter() {
- type_params.replace(*space, self.type_vars_for_defs(generics.types.get_slice(*space)))
+ type_params.replace(*space,
+ self.type_vars_for_defs(span, generics.types.get_slice(*space)))
}
let region_params =
assert!(generics.regions.len(subst::FnSpace) == 0);
let type_parameter_defs = generics.types.get_slice(subst::TypeSpace);
- let type_parameters = self.type_vars_for_defs(type_parameter_defs);
+ let type_parameters = self.type_vars_for_defs(span, type_parameter_defs);
let region_param_defs = generics.regions.get_slice(subst::TypeSpace);
let regions = self.region_vars_for_defs(span, region_param_defs);
pub fn report_conflicting_default_types(&self,
span: Span,
- expected: Ty<'tcx>,
- actual: Ty<'tcx>) {
+ expected: type_variable::Default<'tcx>,
+ actual: type_variable::Default<'tcx>) {
let trace = TypeTrace {
origin: Misc(span),
values: Types(ty::expected_found {
- expected: expected,
- found: actual
+ expected: expected.ty,
+ found: actual.ty
})
};
use middle::region;
use middle::resolve_lifetime;
use middle::infer;
+use middle::infer::type_variable;
use middle::pat_util;
use middle::region::RegionMaps;
use middle::stability;
ConvergenceMismatch(ExpectedFound<bool>),
ProjectionNameMismatched(ExpectedFound<ast::Name>),
ProjectionBoundsLength(ExpectedFound<usize>),
- terr_ty_param_default_mismatch(expected_found<Ty<'tcx>>)
+ TyParamDefaultMismatch(ExpectedFound<Ty<'tcx>>)
}
/// Bounds suitable for an existentially quantified type parameter
values.found)
},
terr_ty_param_default_mismatch(ref values) => {
- write!(f, "conflicting type parameter defaults {} {}",
- values.expected,
- values.found)
+ write!(f, "conflicting type parameter defaults {} and {}",
+ values.expected.ty,
+ values.found.ty)
}
}
}
pub fn note_and_explain_type_err(&self, err: &TypeError<'tcx>, sp: Span) {
use self::TypeError::*;
- match *err {
+ match err.clone() {
RegionsDoesNotOutlive(subregion, superregion) => {
self.note_and_explain_region("", subregion, "...");
self.note_and_explain_region("...does not necessarily outlive ",
using it as a trait object"));
}
},
- terr_ty_param_default_mismatch(expected) => {
+ terr_ty_param_default_mismatch(values) => {
+ let expected = values.expected;
+ let found = values.found;
self.sess.span_note(sp,
- &format!("found conflicting defaults {:?} {:?}",
- expected.expected, expected.found))
+ &format!("conflicting type parameter defaults {} and {}",
+ expected.ty,
+ found.ty));
+ self.sess.span_note(expected.definition_span,
+ &format!("...a default was defined"));
+ self.sess.span_note(expected.origin_span,
+ &format!("...that was applied to an unconstrained type variable here"));
+ self.sess.span_note(found.definition_span,
+ &format!("...a second default was defined"));
+ self.sess.span_note(found.origin_span,
+ &format!("...that also applies to the same type variable here"));
}
_ => {}
}
trait_def.associated_type_names.contains(&assoc_name)
}
- fn ty_infer(&self, default: Option<Ty<'tcx>>, _span: Span) -> Ty<'tcx> {
- let default = default.map(|t| type_variable::Default { ty: t });
+ fn ty_infer(&self, ty_param_def: Option<ty::TypeParameterDef<'tcx>>, span: Span) -> Ty<'tcx> {
+ let default = ty_param_def.and_then(|t|
+ t.default.map(|ty| type_variable::Default { ty: ty, origin_span: span, definition_span: span }));
self.infcx().next_ty_var_with_default(default)
}
fn select_all_obligations_and_apply_defaults(&self) {
use middle::ty::UnconstrainedNumeric::{UnconstrainedInt, UnconstrainedFloat, Neither};
- // debug!("select_all_obligations_and_apply_defaults: defaults={:?}", self.infcx().defaults);
+ // For the time being this errs on the side of being memory wasteful but provides better
+ // error reporting.
+ // let type_variables = self.infcx().type_variables.clone();
+ // There is a possibility that this algorithm will have to run an arbitrary number of times
+ // to terminate so we bound it by the compiler's recursion limit.
for _ in (0..self.tcx().sess.recursion_limit.get()) {
+ // First we try to solve all obligations, it is possible that the last iteration
+ // has made it possible to make more progress.
self.select_obligations_where_possible();
+ let mut conflicts = Vec::new();
+
+ // Collect all unsolved type, integral and floating point variables.
let unsolved_variables = self.inh.infcx.unsolved_variables();
+
+ // We must collect the defaults *before* we do any unification. Because we have
+ // directly attached defaults to the type variables any unification that occurs
+ // will erase defaults causing conflicting defaults to be completely ignored.
+ let default_map: FnvHashMap<_, _> =
+ unsolved_variables
+ .iter()
+ .filter_map(|t| self.infcx().default(t).map(|d| (t, d)))
+ .collect();
+
let mut unbound_tyvars = HashSet::new();
- // Gather all unconstrainted integer and float variables
+ debug!("select_all_obligations_and_apply_defaults: defaults={:?}", default_map);
+
+ // We loop over the unsolved variables, resolving them and if they are
+ // and unconstrainted numberic type we add them to the set of unbound
+ // variables. We do this so we only apply literal fallback to type
+ // variables without defaults.
for ty in &unsolved_variables {
let resolved = self.infcx().resolve_type_vars_if_possible(ty);
if self.infcx().type_var_diverges(resolved) {
demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().mk_nil());
} else {
match self.infcx().type_is_unconstrained_numeric(resolved) {
- UnconstrainedInt => {
+ UnconstrainedInt | UnconstrainedFloat => {
unbound_tyvars.insert(resolved);
},
- UnconstrainedFloat => {
- unbound_tyvars.insert(resolved);
- }
Neither => {}
}
}
}
- // Collect the set of variables that need fallback applied
+ // We now remove any numeric types that also have defaults, and instead insert
+ // the type variable with a defined fallback.
for ty in &unsolved_variables {
- if let Some(_) = self.inh.infcx.default(ty) {
+ if let Some(_default) = default_map.get(ty) {
let resolved = self.infcx().resolve_type_vars_if_possible(ty);
- // debug!("select_all_obligations_and_apply_defaults: ty: {:?} with default: {:?}",
- // ty, self.inh.infcx.defaults.borrow().get(ty));
+ debug!("select_all_obligations_and_apply_defaults: ty: {:?} with default: {:?}",
+ ty, _default);
match resolved.sty {
ty::TyInfer(ty::TyVar(_)) => {
}
}
+ // If there are no more fallbacks to apply at this point we have applied all possible
+ // defaults and type inference will procede as normal.
if unbound_tyvars.is_empty() {
break;
}
- // Go through the unbound variables and unify them with the proper fallbacks
+ // Finally we go through each of the unbound type variables and unify them with
+ // the proper fallback, reporting a conflicting default error if any of the
+ // unifications fail. We know it must be a conflicting default because the
+ // variable would only be in `unbound_tyvars` and have a concrete value if
+ // it had been solved by previously applying a default.
+
+ // We take a snapshot for use in error reporting.
+ let snapshot = self.infcx().type_variables.borrow_mut().snapshot();
+
for ty in &unbound_tyvars {
if self.infcx().type_var_diverges(ty) {
demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().mk_nil());
demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().types.f64)
}
Neither => {
- if let Some(default) = self.inh.infcx.default(ty) {
+ if let Some(default) = default_map.get(ty) {
+ let default = default.clone();
match infer::mk_eqty(self.infcx(), false,
- infer::Misc(codemap::DUMMY_SP),
+ infer::Misc(default.origin_span),
ty, default.ty) {
- Ok(()) => { /* ok */ }
+ Ok(()) => {}
Err(_) => {
- self.infcx().report_conflicting_default_types(
- codemap::DUMMY_SP,
- ty,
- default.ty)
+ conflicts.push((*ty, default));
}
}
}
}
}
- self.select_obligations_where_possible();
+ // There were some errors to report
+ if conflicts.len() > 0 {
+ self.infcx().type_variables.borrow_mut().rollback_to(snapshot);
+
+ for (conflict, default) in conflicts {
+ let conflicting_default =
+ self.find_conflicting_default(
+ &unbound_tyvars,
+ &default_map,
+ conflict).unwrap_or(type_variable::Default {
+ ty: self.infcx().next_ty_var(),
+ origin_span: codemap::DUMMY_SP,
+ definition_span: codemap::DUMMY_SP
+ });
+
+ self.infcx().report_conflicting_default_types(
+ conflicting_default.origin_span,
+ conflicting_default,
+ default)
+ }
+ } else {
+ self.infcx().type_variables.borrow_mut().commit(snapshot)
+ }
}
+
+ self.select_obligations_where_possible();
+ }
+
+ // For use in error handling related to default type parameter fallback. We explicitly
+ // apply the default that caused conflict first to a local version of the type variable
+ // table then apply defaults until we find a conflict. That default must be the one
+ // that caused conflict earlier.
+ fn find_conflicting_default(&self,
+ unbound_vars: &HashSet<Ty<'tcx>>,
+ default_map: &FnvHashMap<&Ty<'tcx>, type_variable::Default<'tcx>>,
+ conflict: Ty<'tcx>)
+ -> Option<type_variable::Default<'tcx>> {
+ use middle::ty::UnconstrainedNumeric::{UnconstrainedInt, UnconstrainedFloat, Neither};
+
+ // Ensure that the conflicting default is applied first
+ let mut unbound_tyvars = Vec::with_capacity(unbound_vars.len() + 1);
+ unbound_tyvars.push(conflict);
+ unbound_tyvars.extend(unbound_vars.iter());
+
+ let mut result = None;
+ // We run the same code as above applying defaults in order, this time when
+ // we find the conflict we just return it for error reporting above.
+ for ty in &unbound_tyvars {
+ if self.infcx().type_var_diverges(ty) {
+ demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().mk_nil());
+ } else {
+ match self.infcx().type_is_unconstrained_numeric(ty) {
+ UnconstrainedInt => {
+ demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().types.i32)
+ },
+ UnconstrainedFloat => {
+ demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().types.f64)
+ },
+ Neither => {
+ if let Some(default) = default_map.get(ty) {
+ let default = default.clone();
+ match infer::mk_eqty(self.infcx(), false,
+ infer::Misc(default.origin_span),
+ ty, default.ty) {
+ Ok(()) => {}
+ Err(_) => {
+ result = Some(default);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return result;
}
fn select_all_obligations_or_error(&self) {
debug!("impl_self_ty: tps={:?} rps={:?} raw_ty={:?}", tps, rps, raw_ty);
let rps = fcx.inh.infcx.region_vars_for_defs(span, rps);
- let tps = fcx.inh.infcx.type_vars_for_defs(tps);
+ let tps = fcx.inh.infcx.type_vars_for_defs(span, tps);
let substs = subst::Substs::new_type(tps, rps);
let substd_ty = fcx.instantiate_type_scheme(span, &substs, &raw_ty);
// Nothing specified at all: supply inference variables for
// everything.
if provided_len == 0 && !(require_type_space && space == subst::TypeSpace) {
- substs.types.replace(space, fcx.infcx().type_vars_for_defs(&desired[..]));
+ substs.types.replace(space, fcx.infcx().type_vars_for_defs(span, &desired[..]));
return;
}