Skip to content

Commit 1d99d37

Browse files
committed
Visit type parameter in lifetime suggestion
Previously, Rebuilder did not visit type parameters when rebuilding generics and path, so in some cases the suggestion turns out to be erroneous.
1 parent 533a526 commit 1d99d37

File tree

3 files changed

+157
-24
lines changed

3 files changed

+157
-24
lines changed

src/librustc/middle/typeck/infer/error_reporting.rs

+110-24
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ use syntax::ast;
8080
use syntax::ast_map;
8181
use syntax::ast_util;
8282
use syntax::ast_util::name_to_dummy_lifetime;
83+
use syntax::owned_slice::OwnedSlice;
8384
use syntax::parse::token;
8485
use syntax::print::pprust;
8586
use util::ppaux::UserString;
@@ -678,6 +679,17 @@ impl<'a> ErrorReporting for InferCtxt<'a> {
678679
}
679680
}
680681

682+
struct RebuildPathInfo<'a> {
683+
path: &'a ast::Path,
684+
// indexes to insert lifetime on path.lifetimes
685+
indexes: Vec<uint>,
686+
// number of lifetimes we expect to see on the type referred by `path`
687+
// (e.g., expected=1 for struct Foo<'a>)
688+
expected: uint,
689+
anon_nums: &'a HashSet<uint>,
690+
region_names: &'a HashSet<ast::Name>
691+
}
692+
681693
struct Rebuilder<'a> {
682694
tcx: &'a ty::ctxt,
683695
fn_decl: ast::P<ast::FnDecl>,
@@ -708,6 +720,7 @@ impl<'a> Rebuilder<'a> {
708720
fn rebuild(&self) -> (Vec<ast::Arg>, ast::P<ast::Ty>, ast::Generics) {
709721
let mut inputs = self.fn_decl.inputs.clone();
710722
let mut output = self.fn_decl.output;
723+
let mut ty_params = self.generics.ty_params.clone();
711724
for sr in self.same_regions.iter() {
712725
self.cur_anon.set(0);
713726
self.offset_cur_anon();
@@ -718,12 +731,14 @@ impl<'a> Rebuilder<'a> {
718731
&anon_nums, &region_names);
719732
output = self.rebuild_arg_ty_or_output(output, lifetime,
720733
&anon_nums, &region_names);
734+
ty_params = self.rebuild_ty_params(ty_params, lifetime,
735+
&region_names);
721736
}
722737
let generated_lifetimes = self.life_giver.get_generated_lifetimes();
723738
let all_region_names = self.extract_all_region_names();
724739
let generics = self.rebuild_generics(self.generics,
725740
generated_lifetimes,
726-
&all_region_names);
741+
&all_region_names, ty_params);
727742
(inputs, output, generics)
728743
}
729744

@@ -782,10 +797,62 @@ impl<'a> Rebuilder<'a> {
782797
self.inserted_anons.borrow_mut().insert(anon);
783798
}
784799

800+
fn rebuild_ty_params(&self,
801+
ty_params: OwnedSlice<ast::TyParam>,
802+
lifetime: ast::Lifetime,
803+
region_names: &HashSet<ast::Name>)
804+
-> OwnedSlice<ast::TyParam> {
805+
ty_params.map(|ty_param| {
806+
let bounds = self.rebuild_ty_param_bounds(ty_param.bounds.clone(),
807+
lifetime,
808+
region_names);
809+
ast::TyParam {
810+
ident: ty_param.ident,
811+
id: ty_param.id,
812+
bounds: bounds,
813+
default: ty_param.default,
814+
}
815+
})
816+
}
817+
818+
fn rebuild_ty_param_bounds(&self,
819+
ty_param_bounds: OwnedSlice<ast::TyParamBound>,
820+
lifetime: ast::Lifetime,
821+
region_names: &HashSet<ast::Name>)
822+
-> OwnedSlice<ast::TyParamBound> {
823+
ty_param_bounds.map(|tpb| {
824+
match tpb {
825+
&ast::RegionTyParamBound => ast::RegionTyParamBound,
826+
&ast::TraitTyParamBound(ref tr) => {
827+
let last_seg = tr.path.segments.last().unwrap();
828+
let mut insert = Vec::new();
829+
for (i, lt) in last_seg.lifetimes.iter().enumerate() {
830+
if region_names.contains(&lt.name) {
831+
insert.push(i);
832+
}
833+
}
834+
let rebuild_info = RebuildPathInfo {
835+
path: &tr.path,
836+
indexes: insert,
837+
expected: last_seg.lifetimes.len(),
838+
anon_nums: &HashSet::new(),
839+
region_names: region_names
840+
};
841+
let new_path = self.rebuild_path(rebuild_info, lifetime);
842+
ast::TraitTyParamBound(ast::TraitRef {
843+
path: new_path,
844+
ref_id: tr.ref_id,
845+
})
846+
}
847+
}
848+
})
849+
}
850+
785851
fn rebuild_generics(&self,
786852
generics: &ast::Generics,
787853
add: Vec<ast::Lifetime>,
788-
remove: &HashSet<ast::Name>)
854+
remove: &HashSet<ast::Name>,
855+
ty_params: OwnedSlice<ast::TyParam>)
789856
-> ast::Generics {
790857
let mut lifetimes = Vec::new();
791858
for lt in add.iter() {
@@ -798,7 +865,7 @@ impl<'a> Rebuilder<'a> {
798865
}
799866
ast::Generics {
800867
lifetimes: lifetimes,
801-
ty_params: generics.ty_params.clone()
868+
ty_params: ty_params
802869
}
803870
}
804871

@@ -886,11 +953,16 @@ impl<'a> Rebuilder<'a> {
886953
}
887954
}
888955
}
889-
for i in insert.iter() {
890-
new_ty = self.rebuild_ty(new_ty, cur_ty,
891-
lifetime,
892-
Some((*i, expected)));
893-
}
956+
let rebuild_info = RebuildPathInfo {
957+
path: path,
958+
indexes: insert,
959+
expected: expected,
960+
anon_nums: anon_nums,
961+
region_names: region_names
962+
};
963+
new_ty = self.rebuild_ty(new_ty, cur_ty,
964+
lifetime,
965+
Some(rebuild_info));
894966
}
895967
_ => ()
896968
}
@@ -906,7 +978,7 @@ impl<'a> Rebuilder<'a> {
906978
from: ast::P<ast::Ty>,
907979
to: ast::P<ast::Ty>,
908980
lifetime: ast::Lifetime,
909-
index_opt: Option<(uint, uint)>)
981+
rebuild_path_info: Option<RebuildPathInfo>)
910982
-> ast::P<ast::Ty> {
911983

912984
fn build_to(from: ast::P<ast::Ty>,
@@ -950,13 +1022,12 @@ impl<'a> Rebuilder<'a> {
9501022

9511023
let new_ty_node = match to.node {
9521024
ast::TyRptr(_, mut_ty) => ast::TyRptr(Some(lifetime), mut_ty),
953-
ast::TyPath(ref path, ref bounds, id) => {
954-
let (index, expected) = match index_opt {
955-
Some((i, e)) => (i, e),
1025+
ast::TyPath(_, ref bounds, id) => {
1026+
let rebuild_info = match rebuild_path_info {
1027+
Some(ri) => ri,
9561028
None => fail!("expect index_opt in rebuild_ty/ast::TyPath")
9571029
};
958-
let new_path = self.rebuild_path(path, index,
959-
expected, lifetime);
1030+
let new_path = self.rebuild_path(rebuild_info, lifetime);
9601031
ast::TyPath(new_path, bounds.clone(), id)
9611032
}
9621033
_ => fail!("expect ast::TyRptr or ast::TyPath")
@@ -970,34 +1041,49 @@ impl<'a> Rebuilder<'a> {
9701041
}
9711042

9721043
fn rebuild_path(&self,
973-
path: &ast::Path,
974-
index: uint,
975-
expected: uint,
1044+
rebuild_info: RebuildPathInfo,
9761045
lifetime: ast::Lifetime)
9771046
-> ast::Path {
1047+
let RebuildPathInfo {
1048+
path: path,
1049+
indexes: indexes,
1050+
expected: expected,
1051+
anon_nums: anon_nums,
1052+
region_names: region_names,
1053+
} = rebuild_info;
1054+
9781055
let last_seg = path.segments.last().unwrap();
9791056
let mut new_lts = Vec::new();
9801057
if last_seg.lifetimes.len() == 0 {
981-
for i in range(0, expected) {
982-
if i == index {
983-
new_lts.push(lifetime);
984-
} else {
985-
new_lts.push(self.life_giver.give_lifetime());
1058+
// traverse once to see if there's a need to insert lifetime
1059+
let need_insert = range(0, expected).any(|i| {
1060+
indexes.contains(&i)
1061+
});
1062+
if need_insert {
1063+
for i in range(0, expected) {
1064+
if indexes.contains(&i) {
1065+
new_lts.push(lifetime);
1066+
} else {
1067+
new_lts.push(self.life_giver.give_lifetime());
1068+
}
9861069
}
9871070
}
9881071
} else {
9891072
for (i, lt) in last_seg.lifetimes.iter().enumerate() {
990-
if i == index {
1073+
if indexes.contains(&i) {
9911074
new_lts.push(lifetime);
9921075
} else {
9931076
new_lts.push(*lt);
9941077
}
9951078
}
9961079
}
1080+
let new_types = last_seg.types.map(|&t| {
1081+
self.rebuild_arg_ty_or_output(t, lifetime, anon_nums, region_names)
1082+
});
9971083
let new_seg = ast::PathSegment {
9981084
identifier: last_seg.identifier,
9991085
lifetimes: new_lts,
1000-
types: last_seg.types.clone(),
1086+
types: new_types,
10011087
};
10021088
let mut new_segs = Vec::new();
10031089
new_segs.push_all(path.segments.init());
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// ignore-tidy-linelength
12+
13+
use std::iter::{Range,range};
14+
15+
trait Itble<'r, T, I: Iterator<T>> { fn iter(&'r self) -> I; }
16+
17+
impl<'r> Itble<'r, uint, Range<uint>> for (uint, uint) {
18+
fn iter(&'r self) -> Range<uint> {
19+
let &(min, max) = self;
20+
range(min, max)
21+
}
22+
}
23+
24+
fn check<'r, I: Iterator<uint>, T: Itble<'r, uint, I>>(cont: &T) -> bool {
25+
//~^ NOTE: consider using an explicit lifetime parameter as shown: fn check<'a, I: Iterator<uint>, T: Itble<'a, uint, I>>(cont: &'a T) -> bool
26+
let cont_iter = cont.iter(); //~ ERROR: cannot infer
27+
let result = cont_iter.fold(Some(0u16), |state, val| {
28+
state.map_or(None, |mask| {
29+
let bit = 1 << val;
30+
if mask & bit == 0 {Some(mask|bit)} else {None}
31+
})
32+
});
33+
result.is_some()
34+
}
35+
36+
fn main() {}

src/test/compile-fail/lifetime-inference-give-expl-lifetime-param.rs

+11
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,16 @@ fn bar2<'a, 'b, 'c>(x: &Bar<'a, 'b, 'c>) -> (&int, &int, &int) {
5454
//~^^ ERROR: cannot infer
5555
}
5656

57+
struct Cat<'x, T> { cat: &'x int, t: T }
58+
struct Dog<'y> { dog: &'y int }
59+
fn cat<'x>(x: Cat<'x, Dog>) -> &int {
60+
//~^ NOTE: consider using an explicit lifetime parameter as shown: fn cat<'a, 'x>(x: Cat<'x, Dog<'a>>) -> &'a int
61+
x.t.dog //~ ERROR: mismatched types
62+
}
63+
64+
fn cat2<'x, 'y>(x: Cat<'x, Dog<'y>>) -> &int {
65+
//~^ NOTE: consider using an explicit lifetime parameter as shown: fn cat2<'a, 'x>(x: Cat<'x, Dog<'a>>) -> &'a int
66+
x.t.dog //~ ERROR: mismatched types
67+
}
5768

5869
fn main() {}

0 commit comments

Comments
 (0)