+ fn new_select_all_obligations_and_apply_defaults(&self) {
+ use middle::ty::UnconstrainedNumeric::{UnconstrainedInt, UnconstrainedFloat, Neither};
+
+ // 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();
+
+ 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 | UnconstrainedFloat => {
+ unbound_tyvars.insert(resolved);
+ },
+ Neither => {}
+ }
+ }
+ }
+
+ // 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(_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, _default);
+
+ match resolved.sty {
+ ty::TyInfer(ty::TyVar(_)) => {
+ unbound_tyvars.insert(ty);
+ }
+
+ ty::TyInfer(ty::IntVar(_)) | ty::TyInfer(ty::FloatVar(_)) => {
+ unbound_tyvars.insert(ty);
+ if unbound_tyvars.contains(resolved) {
+ unbound_tyvars.remove(resolved);
+ }
+ }
+
+ _ => {}
+ }
+ }
+ }
+
+ // 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;
+ }
+
+ // 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 wrap this in a transaction for error reporting, if we detect a conflict
+ // we will rollback the inference context to its prior state so we can probe
+ // for conflicts and correctly report them.
+
+
+ let _ = self.infcx().commit_if_ok(|_: &infer::CombinedSnapshot| {
+ 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(_) => {
+ conflicts.push((*ty, default));
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // If there are conflicts we rollback, otherwise commit
+ if conflicts.len() > 0 {
+ Err(())
+ } else {
+ Ok(())
+ }
+ });
+
+ if conflicts.len() > 0 {
+ // Loop through each conflicting default, figuring out the default that caused
+ // a unification failure and then report an error for each.
+ 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,
+ def_id: local_def(0) // what do I put here?
+ });
+
+ // This is to ensure that we elimnate any non-determinism from the error
+ // reporting by fixing an order, it doesn't matter what order we choose
+ // just that it is consistent.
+ let (first_default, second_default) =
+ if default.def_id < conflicting_default.def_id {
+ (default, conflicting_default)
+ } else {
+ (conflicting_default, default)
+ };
+
+
+ self.infcx().report_conflicting_default_types(
+ first_default.origin_span,
+ first_default,
+ second_default)
+ }
+ }
+ }
+
+ 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 we apply the conflicting default 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.
+
+ // We also run this inside snapshot that never commits so we can do error
+ // reporting for more then one conflict.
+ 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;
+ }
+