]> git.lizzy.rs Git - rust.git/commitdiff
Refactor the default type parameter algorithm
authorJared Roesch <roeschinc@gmail.com>
Mon, 13 Jul 2015 03:33:17 +0000 (20:33 -0700)
committerJared Roesch <jroesch@MacBook.home>
Sun, 26 Jul 2015 02:57:58 +0000 (19:57 -0700)
The algorithm was not correctly detecting conflicts after moving
defaults into TypeVariableValue. The updated algorithm
correctly detects and reports conflicts with information about
where the conflict occured and which items the defaults were
introduced by. The span's for said items are not being correctly
attached and still need to be patched.

src/librustc/middle/infer/error_reporting.rs
src/librustc/middle/infer/mod.rs
src/librustc/middle/infer/type_variable.rs
src/librustc/middle/ty.rs
src/librustc_typeck/astconv.rs
src/librustc_typeck/check/method/confirm.rs
src/librustc_typeck/check/method/mod.rs
src/librustc_typeck/check/method/probe.rs
src/librustc_typeck/check/mod.rs
src/librustc_typeck/collect.rs
src/rust-installer

index 8d66ffac5d17ca09568a186671d5efb89c401f32..fbf19a10d93bf025deab497f004d342a9279c779 100644 (file)
@@ -893,8 +893,8 @@ fn report_processed_errors(&self,
             self.report_inference_failure(vo.clone());
         }
         self.give_suggestion(same_regions);
-        for &(ref trace, terr) in trace_origins {
-            self.report_and_explain_type_error(trace.clone(), &terr);
+        for &(ref trace, ref terr) in trace_origins {
+            self.report_and_explain_type_error(trace.clone(), terr);
         }
     }
 
index db6e0ad1d4a83bc7b05c55072dc82e6352cc292d..d38417143ce06d50a134709880d3f7ef6afe091d 100644 (file)
@@ -40,6 +40,7 @@
 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;
@@ -72,7 +73,7 @@ pub struct InferCtxt<'a, 'tcx: 'a> {
     // 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>>,
@@ -690,7 +691,7 @@ pub fn unsolved_variables(&self) -> Vec<ty::Ty<'tcx>> {
         variables.extend(unbound_ty_vars);
         variables.extend(unbound_int_vars);
         variables.extend(unbound_float_vars);
-        
+
         return variables;
     }
 
@@ -1047,15 +1048,36 @@ pub fn region_vars_for_defs(&self,
     }
 
     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)
@@ -1077,7 +1099,8 @@ pub fn fresh_substs_for_generics(&self,
         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 =
@@ -1102,7 +1125,7 @@ pub fn fresh_substs_for_trait(&self,
         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);
@@ -1344,13 +1367,13 @@ pub fn report_mismatched_types(&self,
 
     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
             })
         };
 
index fa7cd143e3b21da93c32ec5215490bf976958d2f..8707306a149c7f1514d09883ab610515fde65094 100644 (file)
@@ -11,8 +11,9 @@
 pub use self::RelationDir::*;
 use self::TypeVariableValue::*;
 use self::UndoEntry::*;
-
 use middle::ty::{self, Ty};
+use syntax::codemap::Span;
+
 use std::cmp::min;
 use std::marker::PhantomData;
 use std::mem;
@@ -38,9 +39,13 @@ enum TypeVariableValue<'tcx> {
 
 // We will use this to store the required information to recapitulate what happened when
 // an error occurs.
-#[derive(Clone)]
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
 pub struct Default<'tcx> {
-    pub ty: Ty<'tcx>
+    pub ty: Ty<'tcx>,
+    /// The span where the default was incurred
+    pub origin_span: Span,
+    /// The definition that the default originates from
+    pub definition_span: Span
 }
 
 pub struct Snapshot {
index a08c5e1f73fa20037f04ecde096ce65e6dc08ae4..19add679bbfdc90dda1756fd2f70c81570414d6d 100644 (file)
@@ -54,6 +54,7 @@
 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;
@@ -2068,7 +2069,7 @@ pub enum TypeError<'tcx> {
     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
@@ -5083,9 +5084,9 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                        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)
             }
         }
     }
@@ -5405,7 +5406,7 @@ pub fn field_idx_strict(&self, name: ast::Name, fields: &[Field<'tcx>])
     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 ",
@@ -5444,10 +5445,21 @@ pub fn note_and_explain_type_err(&self, err: &TypeError<'tcx>, sp: Span) {
                                   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"));
             }
             _ => {}
         }
index 5ed1da2fedebc04dbcce600a3afc3032ead2efe7..3925f4e751c20097cdbe96f5de4d440529ea7728 100644 (file)
@@ -111,7 +111,7 @@ fn get_free_substs(&self) -> Option<&Substs<'tcx>> {
     }
 
     /// What type should we use when a type is omitted?
-    fn ty_infer(&self, default: Option<Ty<'tcx>>, span: Span) -> Ty<'tcx>;
+    fn ty_infer(&self, default: Option<ty::TypeParameterDef<'tcx>>, span: Span) -> Ty<'tcx>;
 
     /// Projecting an associated type from a (potentially)
     /// higher-ranked trait reference is more complicated, because of
@@ -403,7 +403,7 @@ fn create_substs_for_ast_path<'tcx>(
     // they were optional (e.g. paths inside expressions).
     let mut type_substs = if param_mode == PathParamMode::Optional &&
                              types_provided.is_empty() {
-        ty_param_defs.iter().map(|p| this.ty_infer(p.default, span)).collect()
+        ty_param_defs.iter().map(|p| this.ty_infer(Some(p.clone()), span)).collect()
     } else {
         types_provided
     };
index e3b9b990cb9e57f8451cd25d72594b1ee63f5292..488428833ac13c4a8a6b7d21f89d180fb38da248 100644 (file)
@@ -315,11 +315,11 @@ fn instantiate_method_substs(&mut self,
 
         let method_types = {
             if num_supplied_types == 0 {
-                self.fcx.infcx().type_vars_for_defs(method_types)
+                self.fcx.infcx().type_vars_for_defs(self.span, method_types)
             } else if num_method_types == 0 {
                 span_err!(self.tcx().sess, self.span, E0035,
                     "does not take type parameters");
-                self.fcx.infcx().type_vars_for_defs(method_types)
+                self.fcx.infcx().type_vars_for_defs(self.span, method_types)
             } else if num_supplied_types != num_method_types {
                 span_err!(self.tcx().sess, self.span, E0036,
                     "incorrect number of type parameters given for this method");
index 2ca5a88fb06ddbaae974aa929960fa9c60392c67..767797e843caffb104336ad2eee15da575cf1a45 100644 (file)
@@ -176,7 +176,7 @@ pub fn lookup_in_trait_adjusted<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
         }
 
         None => {
-            fcx.inh.infcx.type_vars_for_defs(type_parameter_defs)
+            fcx.inh.infcx.type_vars_for_defs(span, type_parameter_defs)
         }
     };
 
index 88bd000cfdd65340fb1907a907e84fe4680030f5..b190e7a7a48305381d0ec07ca67c946fea59d9b5 100644 (file)
@@ -1207,7 +1207,7 @@ fn xform_method_self_ty(&self,
             !method.generics.regions.is_empty_in(subst::FnSpace)
         {
             let method_types =
-                self.infcx().type_vars_for_defs(
+                self.infcx().type_vars_for_defs(self.span,
                     method.generics.types.get_slice(subst::FnSpace));
 
             // In general, during probe we erase regions. See
index f6a4dbca291bb759f3aa416b6477a0a39eee98e0..3a93c7ed9161d2e632dc1884f2314af1884d3255 100644 (file)
@@ -1139,8 +1139,9 @@ fn trait_defines_associated_type_named(&self,
         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)
     }
 
@@ -1694,39 +1695,61 @@ fn check_casts(&self) {
     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(_)) => {
@@ -1745,11 +1768,21 @@ fn select_all_obligations_and_apply_defaults(&self) {
                 }
             }
 
+            // 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());
@@ -1762,16 +1795,14 @@ fn select_all_obligations_and_apply_defaults(&self) {
                             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));
                                     }
                                 }
                             }
@@ -1780,8 +1811,82 @@ fn select_all_obligations_and_apply_defaults(&self) {
                 }
             }
 
-            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) {
@@ -2495,7 +2600,7 @@ pub fn impl_self_ty<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
     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);
 
@@ -4715,7 +4820,7 @@ fn adjust_type_parameters<'a, 'tcx>(
         // 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;
         }
 
index 695991a97f06190f6690967ddce4136f64d45007..8b38c58ce7a1bdf97b0edfc8ed7b5e00fdb00bdb 100644 (file)
@@ -404,7 +404,7 @@ fn trait_defines_associated_type_named(&self,
         }
     }
 
-    fn ty_infer(&self, _default: Option<Ty<'tcx>>, span: Span) -> Ty<'tcx> {
+    fn ty_infer(&self, _default: Option<ty::TypeParameterDef<'tcx>>, span: Span) -> Ty<'tcx> {
         span_err!(self.tcx().sess, span, E0121,
                   "the type placeholder `_` is not allowed within types on item signatures");
         self.tcx().types.err
index c37d3747da75c280237dc2d6b925078e69555499..8e4f8ea581502a2edc8177a040300e05ff7f91e3 160000 (submodule)
@@ -1 +1 @@
-Subproject commit c37d3747da75c280237dc2d6b925078e69555499
+Subproject commit 8e4f8ea581502a2edc8177a040300e05ff7f91e3