about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2014-04-05 13:31:33 -0700
committerbors <bors@rust-lang.org>2014-04-05 13:31:33 -0700
commitb2b2bbb62893b9655f65cb2813eaaba4f2e742dd (patch)
tree98a40b883f663fc26274c851d0e6989acd5e590a
parent9539be6d746bec6b7b43e358999a91e4d1f9a759 (diff)
parent1d99d37f87d4cc46d285e883c2fc28b3e4e5c405 (diff)
downloadrust-b2b2bbb62893b9655f65cb2813eaaba4f2e742dd.tar.gz
rust-b2b2bbb62893b9655f65cb2813eaaba4f2e742dd.zip
auto merge of #13112 : ktt3ja/rust/issue-13058, r=pnkfelix
Previously, Rebuilder did not visit type parameters when rebuilding
generics and path, so in some cases the suggestion turns out to be
erroneous.
-rw-r--r--src/librustc/middle/typeck/infer/error_reporting.rs134
-rw-r--r--src/test/compile-fail/lifetime-inference-give-expl-lifetime-param-2.rs36
-rw-r--r--src/test/compile-fail/lifetime-inference-give-expl-lifetime-param.rs11
3 files changed, 157 insertions, 24 deletions
diff --git a/src/librustc/middle/typeck/infer/error_reporting.rs b/src/librustc/middle/typeck/infer/error_reporting.rs
index 9c947fb3360..db66c93086a 100644
--- a/src/librustc/middle/typeck/infer/error_reporting.rs
+++ b/src/librustc/middle/typeck/infer/error_reporting.rs
@@ -80,6 +80,7 @@ use syntax::ast;
 use syntax::ast_map;
 use syntax::ast_util;
 use syntax::ast_util::name_to_dummy_lifetime;
+use syntax::owned_slice::OwnedSlice;
 use syntax::parse::token;
 use syntax::print::pprust;
 use util::ppaux::UserString;
@@ -678,6 +679,17 @@ impl<'a> ErrorReporting for InferCtxt<'a> {
     }
 }
 
+struct RebuildPathInfo<'a> {
+    path: &'a ast::Path,
+    // indexes to insert lifetime on path.lifetimes
+    indexes: Vec<uint>,
+    // number of lifetimes we expect to see on the type referred by `path`
+    // (e.g., expected=1 for struct Foo<'a>)
+    expected: uint,
+    anon_nums: &'a HashSet<uint>,
+    region_names: &'a HashSet<ast::Name>
+}
+
 struct Rebuilder<'a> {
     tcx: &'a ty::ctxt,
     fn_decl: ast::P<ast::FnDecl>,
@@ -708,6 +720,7 @@ impl<'a> Rebuilder<'a> {
     fn rebuild(&self) -> (Vec<ast::Arg>, ast::P<ast::Ty>, ast::Generics) {
         let mut inputs = self.fn_decl.inputs.clone();
         let mut output = self.fn_decl.output;
+        let mut ty_params = self.generics.ty_params.clone();
         for sr in self.same_regions.iter() {
             self.cur_anon.set(0);
             self.offset_cur_anon();
@@ -718,12 +731,14 @@ impl<'a> Rebuilder<'a> {
                                           &anon_nums, &region_names);
             output = self.rebuild_arg_ty_or_output(output, lifetime,
                                                    &anon_nums, &region_names);
+            ty_params = self.rebuild_ty_params(ty_params, lifetime,
+                                               &region_names);
         }
         let generated_lifetimes = self.life_giver.get_generated_lifetimes();
         let all_region_names = self.extract_all_region_names();
         let generics = self.rebuild_generics(self.generics,
                                              generated_lifetimes,
-                                             &all_region_names);
+                                             &all_region_names, ty_params);
         (inputs, output, generics)
     }
 
@@ -782,10 +797,62 @@ impl<'a> Rebuilder<'a> {
         self.inserted_anons.borrow_mut().insert(anon);
     }
 
+    fn rebuild_ty_params(&self,
+                         ty_params: OwnedSlice<ast::TyParam>,
+                         lifetime: ast::Lifetime,
+                         region_names: &HashSet<ast::Name>)
+                         -> OwnedSlice<ast::TyParam> {
+        ty_params.map(|ty_param| {
+            let bounds = self.rebuild_ty_param_bounds(ty_param.bounds.clone(),
+                                                      lifetime,
+                                                      region_names);
+            ast::TyParam {
+                ident: ty_param.ident,
+                id: ty_param.id,
+                bounds: bounds,
+                default: ty_param.default,
+            }
+        })
+    }
+
+    fn rebuild_ty_param_bounds(&self,
+                               ty_param_bounds: OwnedSlice<ast::TyParamBound>,
+                               lifetime: ast::Lifetime,
+                               region_names: &HashSet<ast::Name>)
+                               -> OwnedSlice<ast::TyParamBound> {
+        ty_param_bounds.map(|tpb| {
+            match tpb {
+                &ast::RegionTyParamBound => ast::RegionTyParamBound,
+                &ast::TraitTyParamBound(ref tr) => {
+                    let last_seg = tr.path.segments.last().unwrap();
+                    let mut insert = Vec::new();
+                    for (i, lt) in last_seg.lifetimes.iter().enumerate() {
+                        if region_names.contains(&lt.name) {
+                            insert.push(i);
+                        }
+                    }
+                    let rebuild_info = RebuildPathInfo {
+                        path: &tr.path,
+                        indexes: insert,
+                        expected: last_seg.lifetimes.len(),
+                        anon_nums: &HashSet::new(),
+                        region_names: region_names
+                    };
+                    let new_path = self.rebuild_path(rebuild_info, lifetime);
+                    ast::TraitTyParamBound(ast::TraitRef {
+                        path: new_path,
+                        ref_id: tr.ref_id,
+                    })
+                }
+            }
+        })
+    }
+
     fn rebuild_generics(&self,
                         generics: &ast::Generics,
                         add: Vec<ast::Lifetime>,
-                        remove: &HashSet<ast::Name>)
+                        remove: &HashSet<ast::Name>,
+                        ty_params: OwnedSlice<ast::TyParam>)
                         -> ast::Generics {
         let mut lifetimes = Vec::new();
         for lt in add.iter() {
@@ -798,7 +865,7 @@ impl<'a> Rebuilder<'a> {
         }
         ast::Generics {
             lifetimes: lifetimes,
-            ty_params: generics.ty_params.clone()
+            ty_params: ty_params
         }
     }
 
@@ -886,11 +953,16 @@ impl<'a> Rebuilder<'a> {
                                     }
                                 }
                             }
-                            for i in insert.iter() {
-                                new_ty = self.rebuild_ty(new_ty, cur_ty,
-                                                         lifetime,
-                                                         Some((*i, expected)));
-                            }
+                            let rebuild_info = RebuildPathInfo {
+                                path: path,
+                                indexes: insert,
+                                expected: expected,
+                                anon_nums: anon_nums,
+                                region_names: region_names
+                            };
+                            new_ty = self.rebuild_ty(new_ty, cur_ty,
+                                                     lifetime,
+                                                     Some(rebuild_info));
                         }
                         _ => ()
                     }
@@ -906,7 +978,7 @@ impl<'a> Rebuilder<'a> {
                   from: ast::P<ast::Ty>,
                   to: ast::P<ast::Ty>,
                   lifetime: ast::Lifetime,
-                  index_opt: Option<(uint, uint)>)
+                  rebuild_path_info: Option<RebuildPathInfo>)
                   -> ast::P<ast::Ty> {
 
         fn build_to(from: ast::P<ast::Ty>,
@@ -950,13 +1022,12 @@ impl<'a> Rebuilder<'a> {
 
         let new_ty_node = match to.node {
             ast::TyRptr(_, mut_ty) => ast::TyRptr(Some(lifetime), mut_ty),
-            ast::TyPath(ref path, ref bounds, id) => {
-                let (index, expected) = match index_opt {
-                    Some((i, e)) => (i, e),
+            ast::TyPath(_, ref bounds, id) => {
+                let rebuild_info = match rebuild_path_info {
+                    Some(ri) => ri,
                     None => fail!("expect index_opt in rebuild_ty/ast::TyPath")
                 };
-                let new_path = self.rebuild_path(path, index,
-                                                 expected, lifetime);
+                let new_path = self.rebuild_path(rebuild_info, lifetime);
                 ast::TyPath(new_path, bounds.clone(), id)
             }
             _ => fail!("expect ast::TyRptr or ast::TyPath")
@@ -970,34 +1041,49 @@ impl<'a> Rebuilder<'a> {
     }
 
     fn rebuild_path(&self,
-                    path: &ast::Path,
-                    index: uint,
-                    expected: uint,
+                    rebuild_info: RebuildPathInfo,
                     lifetime: ast::Lifetime)
                     -> ast::Path {
+        let RebuildPathInfo {
+            path: path,
+            indexes: indexes,
+            expected: expected,
+            anon_nums: anon_nums,
+            region_names: region_names,
+        } = rebuild_info;
+
         let last_seg = path.segments.last().unwrap();
         let mut new_lts = Vec::new();
         if last_seg.lifetimes.len() == 0 {
-            for i in range(0, expected) {
-                if i == index {
-                    new_lts.push(lifetime);
-                } else {
-                    new_lts.push(self.life_giver.give_lifetime());
+            // traverse once to see if there's a need to insert lifetime
+            let need_insert = range(0, expected).any(|i| {
+                indexes.contains(&i)
+            });
+            if need_insert {
+                for i in range(0, expected) {
+                    if indexes.contains(&i) {
+                        new_lts.push(lifetime);
+                    } else {
+                        new_lts.push(self.life_giver.give_lifetime());
+                    }
                 }
             }
         } else {
             for (i, lt) in last_seg.lifetimes.iter().enumerate() {
-                if i == index {
+                if indexes.contains(&i) {
                     new_lts.push(lifetime);
                 } else {
                     new_lts.push(*lt);
                 }
             }
         }
+        let new_types = last_seg.types.map(|&t| {
+            self.rebuild_arg_ty_or_output(t, lifetime, anon_nums, region_names)
+        });
         let new_seg = ast::PathSegment {
             identifier: last_seg.identifier,
             lifetimes: new_lts,
-            types: last_seg.types.clone(),
+            types: new_types,
         };
         let mut new_segs = Vec::new();
         new_segs.push_all(path.segments.init());
diff --git a/src/test/compile-fail/lifetime-inference-give-expl-lifetime-param-2.rs b/src/test/compile-fail/lifetime-inference-give-expl-lifetime-param-2.rs
new file mode 100644
index 00000000000..a6bf5a4b653
--- /dev/null
+++ b/src/test/compile-fail/lifetime-inference-give-expl-lifetime-param-2.rs
@@ -0,0 +1,36 @@
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// ignore-tidy-linelength
+
+use std::iter::{Range,range};
+
+trait Itble<'r, T, I: Iterator<T>> { fn iter(&'r self) -> I; }
+
+impl<'r> Itble<'r, uint, Range<uint>> for (uint, uint) {
+    fn iter(&'r self) -> Range<uint> {
+        let &(min, max) = self;
+        range(min, max)
+    }
+}
+
+fn check<'r, I: Iterator<uint>, T: Itble<'r, uint, I>>(cont: &T) -> bool {
+//~^ NOTE: consider using an explicit lifetime parameter as shown: fn check<'a, I: Iterator<uint>, T: Itble<'a, uint, I>>(cont: &'a T) -> bool
+    let cont_iter = cont.iter(); //~ ERROR: cannot infer
+    let result = cont_iter.fold(Some(0u16), |state, val| {
+        state.map_or(None, |mask| {
+            let bit = 1 << val;
+            if mask & bit == 0 {Some(mask|bit)} else {None}
+        })
+    });
+    result.is_some()
+}
+
+fn main() {}
diff --git a/src/test/compile-fail/lifetime-inference-give-expl-lifetime-param.rs b/src/test/compile-fail/lifetime-inference-give-expl-lifetime-param.rs
index 709f15d3552..33b849f346a 100644
--- a/src/test/compile-fail/lifetime-inference-give-expl-lifetime-param.rs
+++ b/src/test/compile-fail/lifetime-inference-give-expl-lifetime-param.rs
@@ -54,5 +54,16 @@ fn bar2<'a, 'b, 'c>(x: &Bar<'a, 'b, 'c>) -> (&int, &int, &int) {
     //~^^ ERROR: cannot infer
 }
 
+struct Cat<'x, T> { cat: &'x int, t: T }
+struct Dog<'y> { dog: &'y int }
+fn cat<'x>(x: Cat<'x, Dog>) -> &int {
+//~^ NOTE: consider using an explicit lifetime parameter as shown: fn cat<'a, 'x>(x: Cat<'x, Dog<'a>>) -> &'a int
+    x.t.dog //~ ERROR: mismatched types
+}
+
+fn cat2<'x, 'y>(x: Cat<'x, Dog<'y>>) -> &int {
+//~^ NOTE: consider using an explicit lifetime parameter as shown: fn cat2<'a, 'x>(x: Cat<'x, Dog<'a>>) -> &'a int
+    x.t.dog //~ ERROR: mismatched types
+}
 
 fn main() {}