Skip to content

Commit 79d1e5d

Browse files
committed
Support lifetime suggestion for method
This includes a change to the way lifetime names are generated. Say we figure that `[#0, 'a, 'b]` have to be the same lifetimes, then instead of just generating a new lifetime `'c` like before to replace them, we would reuse `'a`. This is done so that when the lifetime name comes from an impl, we don't give something that's completely off, and we don't have to do much work to figure out where the name came from. For example, for the following code snippet: ```rust struct Baz<'x> { bar: &'x int } impl<'x> Baz<'x> { fn baz1(&self) -> &int { self.bar } } ``` `[#1, 'x]` (where `#1` is BrAnon(1) and refers to lifetime of `&int`) have to be marked the same lifetime. With the old method, we would generate a new lifetime `'a` and suggest `fn baz1(&self) -> &'a int` or `fn baz1<'a>(&self) -> &'a int`, both of which are wrong.
1 parent 189584e commit 79d1e5d

File tree

3 files changed

+199
-57
lines changed

3 files changed

+199
-57
lines changed

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

+174-52
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ use syntax::ast_map;
8282
use syntax::ast_util;
8383
use syntax::ast_util::name_to_dummy_lifetime;
8484
use syntax::owned_slice::OwnedSlice;
85+
use syntax::codemap;
8586
use syntax::parse::token;
8687
use syntax::print::pprust;
8788
use util::ppaux::UserString;
@@ -143,10 +144,12 @@ trait ErrorReportingHelpers {
143144
origin: SubregionOrigin);
144145

145146
fn give_expl_lifetime_param(&self,
146-
inputs: Vec<ast::Arg>,
147-
output: ast::P<ast::Ty>,
148-
item: ast::P<ast::Item>,
149-
generics: ast::Generics);
147+
decl: &ast::FnDecl,
148+
fn_style: ast::FnStyle,
149+
ident: ast::Ident,
150+
opt_explicit_self: Option<ast::ExplicitSelf_>,
151+
generics: &ast::Generics,
152+
span: codemap::Span);
150153
}
151154

152155
impl<'a> ErrorReporting for InferCtxt<'a> {
@@ -260,6 +263,19 @@ impl<'a> ErrorReporting for InferCtxt<'a> {
260263
scope_id: ast::NodeId
261264
}
262265

266+
impl FreeRegionsFromSameFn {
267+
fn new(sub_fr: ty::FreeRegion,
268+
sup_fr: ty::FreeRegion,
269+
scope_id: ast::NodeId)
270+
-> FreeRegionsFromSameFn {
271+
FreeRegionsFromSameFn {
272+
sub_fr: sub_fr,
273+
sup_fr: sup_fr,
274+
scope_id: scope_id
275+
}
276+
}
277+
}
278+
263279
fn free_regions_from_same_fn(tcx: &ty::ctxt,
264280
sub: Region,
265281
sup: Region)
@@ -280,17 +296,14 @@ impl<'a> ErrorReporting for InferCtxt<'a> {
280296
match parent_node {
281297
Some(node) => match node {
282298
ast_map::NodeItem(item) => match item.node {
283-
// FIXME: handle method
284299
ast::ItemFn(..) => {
285-
let fr_from_same_fn = FreeRegionsFromSameFn {
286-
sub_fr: fr1,
287-
sup_fr: fr2,
288-
scope_id: scope_id
289-
};
290-
Some(fr_from_same_fn)
300+
Some(FreeRegionsFromSameFn::new(fr1, fr2, scope_id))
291301
},
292302
_ => None
293303
},
304+
ast_map::NodeMethod(..) => {
305+
Some(FreeRegionsFromSameFn::new(fr1, fr2, scope_id))
306+
},
294307
_ => None
295308
},
296309
None => {
@@ -662,21 +675,28 @@ impl<'a> ErrorReporting for InferCtxt<'a> {
662675
let node_inner = match parent_node {
663676
Some(node) => match node {
664677
ast_map::NodeItem(item) => match item.node {
665-
// FIXME: handling method
666-
ast::ItemFn(ref fn_decl, _, _, ref gen, _) => {
667-
Some((item, fn_decl, gen))
678+
ast::ItemFn(ref fn_decl, ref pur, _, ref gen, _) => {
679+
Some((fn_decl, gen, *pur, item.ident, None, item.span))
668680
},
669681
_ => None
670682
},
683+
ast_map::NodeMethod(m) => {
684+
Some((&m.decl, &m.generics, m.fn_style,
685+
m.ident, Some(m.explicit_self.node), m.span))
686+
},
671687
_ => None
672688
},
673689
None => None
674690
};
675-
let (item, fn_decl, generics) = node_inner.expect("expect item fn");
676-
let rebuilder = Rebuilder::new(self.tcx, *fn_decl,
677-
generics, same_regions);
678-
let (inputs, output, generics) = rebuilder.rebuild();
679-
self.give_expl_lifetime_param(inputs, output, item, generics);
691+
let (fn_decl, generics, fn_style, ident, expl_self, span)
692+
= node_inner.expect("expect item fn");
693+
let taken = lifetimes_in_scope(self.tcx, scope_id);
694+
let life_giver = LifeGiver::with_taken(taken.as_slice());
695+
let rebuilder = Rebuilder::new(self.tcx, *fn_decl, expl_self,
696+
generics, same_regions, &life_giver);
697+
let (fn_decl, expl_self, generics) = rebuilder.rebuild();
698+
self.give_expl_lifetime_param(&fn_decl, fn_style, ident,
699+
expl_self, &generics, span);
680700
}
681701
}
682702

@@ -694,53 +714,98 @@ struct RebuildPathInfo<'a> {
694714
struct Rebuilder<'a> {
695715
tcx: &'a ty::ctxt,
696716
fn_decl: ast::P<ast::FnDecl>,
717+
expl_self_opt: Option<ast::ExplicitSelf_>,
697718
generics: &'a ast::Generics,
698719
same_regions: &'a [SameRegions],
699-
life_giver: LifeGiver,
720+
life_giver: &'a LifeGiver,
700721
cur_anon: Cell<uint>,
701722
inserted_anons: RefCell<HashSet<uint>>,
702723
}
703724

725+
enum FreshOrKept {
726+
Fresh,
727+
Kept
728+
}
729+
704730
impl<'a> Rebuilder<'a> {
705731
fn new(tcx: &'a ty::ctxt,
706732
fn_decl: ast::P<ast::FnDecl>,
733+
expl_self_opt: Option<ast::ExplicitSelf_>,
707734
generics: &'a ast::Generics,
708-
same_regions: &'a [SameRegions])
735+
same_regions: &'a [SameRegions],
736+
life_giver: &'a LifeGiver)
709737
-> Rebuilder<'a> {
710738
Rebuilder {
711739
tcx: tcx,
712740
fn_decl: fn_decl,
741+
expl_self_opt: expl_self_opt,
713742
generics: generics,
714743
same_regions: same_regions,
715-
life_giver: LifeGiver::with_taken(generics.lifetimes.as_slice()),
744+
life_giver: life_giver,
716745
cur_anon: Cell::new(0),
717746
inserted_anons: RefCell::new(HashSet::new()),
718747
}
719748
}
720749

721-
fn rebuild(&self) -> (Vec<ast::Arg>, ast::P<ast::Ty>, ast::Generics) {
750+
fn rebuild(&self)
751+
-> (ast::FnDecl, Option<ast::ExplicitSelf_>, ast::Generics) {
752+
let mut expl_self_opt = self.expl_self_opt;
722753
let mut inputs = self.fn_decl.inputs.clone();
723754
let mut output = self.fn_decl.output;
724755
let mut ty_params = self.generics.ty_params.clone();
756+
let mut kept_lifetimes = HashSet::new();
725757
for sr in self.same_regions.iter() {
726758
self.cur_anon.set(0);
727759
self.offset_cur_anon();
728760
let (anon_nums, region_names) =
729761
self.extract_anon_nums_and_names(sr);
730-
let lifetime = self.life_giver.give_lifetime();
762+
let (lifetime, fresh_or_kept) = self.pick_lifetime(&region_names);
763+
match fresh_or_kept {
764+
Kept => { kept_lifetimes.insert(lifetime.name); }
765+
_ => ()
766+
}
767+
expl_self_opt = self.rebuild_expl_self(expl_self_opt, lifetime,
768+
&anon_nums, &region_names);
731769
inputs = self.rebuild_args_ty(inputs.as_slice(), lifetime,
732770
&anon_nums, &region_names);
733771
output = self.rebuild_arg_ty_or_output(output, lifetime,
734772
&anon_nums, &region_names);
735773
ty_params = self.rebuild_ty_params(ty_params, lifetime,
736774
&region_names);
737775
}
738-
let generated_lifetimes = self.life_giver.get_generated_lifetimes();
776+
let fresh_lifetimes = self.life_giver.get_generated_lifetimes();
739777
let all_region_names = self.extract_all_region_names();
740778
let generics = self.rebuild_generics(self.generics,
741-
generated_lifetimes,
742-
&all_region_names, ty_params);
743-
(inputs, output, generics)
779+
&fresh_lifetimes,
780+
&kept_lifetimes,
781+
&all_region_names,
782+
ty_params);
783+
let new_fn_decl = ast::FnDecl {
784+
inputs: inputs,
785+
output: output,
786+
cf: self.fn_decl.cf,
787+
variadic: self.fn_decl.variadic
788+
};
789+
(new_fn_decl, expl_self_opt, generics)
790+
}
791+
792+
fn pick_lifetime(&self,
793+
region_names: &HashSet<ast::Name>)
794+
-> (ast::Lifetime, FreshOrKept) {
795+
if region_names.len() > 0 {
796+
// It's not necessary to convert the set of region names to a
797+
// vector of string and then sort them. However, it makes the
798+
// choice of lifetime name deterministic and thus easier to test.
799+
let mut names = Vec::new();
800+
for rn in region_names.iter() {
801+
let lt_name = token::get_name(*rn).get().to_owned();
802+
names.push(lt_name);
803+
}
804+
names.sort();
805+
let name = token::str_to_ident(names.get(0).as_slice()).name;
806+
return (name_to_dummy_lifetime(name), Kept);
807+
}
808+
return (self.life_giver.give_lifetime(), Fresh);
744809
}
745810

746811
fn extract_anon_nums_and_names(&self, same_regions: &SameRegions)
@@ -849,9 +914,38 @@ impl<'a> Rebuilder<'a> {
849914
})
850915
}
851916

917+
fn rebuild_expl_self(&self,
918+
expl_self_opt: Option<ast::ExplicitSelf_>,
919+
lifetime: ast::Lifetime,
920+
anon_nums: &HashSet<uint>,
921+
region_names: &HashSet<ast::Name>)
922+
-> Option<ast::ExplicitSelf_> {
923+
match expl_self_opt {
924+
Some(expl_self) => match expl_self {
925+
ast::SelfRegion(lt_opt, muta) => match lt_opt {
926+
Some(lt) => if region_names.contains(&lt.name) {
927+
return Some(ast::SelfRegion(Some(lifetime), muta));
928+
},
929+
None => {
930+
let anon = self.cur_anon.get();
931+
self.inc_and_offset_cur_anon(1);
932+
if anon_nums.contains(&anon) {
933+
self.track_anon(anon);
934+
return Some(ast::SelfRegion(Some(lifetime), muta));
935+
}
936+
}
937+
},
938+
_ => ()
939+
},
940+
None => ()
941+
}
942+
expl_self_opt
943+
}
944+
852945
fn rebuild_generics(&self,
853946
generics: &ast::Generics,
854-
add: Vec<ast::Lifetime>,
947+
add: &Vec<ast::Lifetime>,
948+
keep: &HashSet<ast::Name>,
855949
remove: &HashSet<ast::Name>,
856950
ty_params: OwnedSlice<ast::TyParam>)
857951
-> ast::Generics {
@@ -860,7 +954,7 @@ impl<'a> Rebuilder<'a> {
860954
lifetimes.push(*lt);
861955
}
862956
for lt in generics.lifetimes.iter() {
863-
if !remove.contains(&lt.name) {
957+
if keep.contains(&lt.name) || !remove.contains(&lt.name) {
864958
lifetimes.push((*lt).clone());
865959
}
866960
}
@@ -1099,29 +1193,17 @@ impl<'a> Rebuilder<'a> {
10991193

11001194
impl<'a> ErrorReportingHelpers for InferCtxt<'a> {
11011195
fn give_expl_lifetime_param(&self,
1102-
inputs: Vec<ast::Arg>,
1103-
output: ast::P<ast::Ty>,
1104-
item: ast::P<ast::Item>,
1105-
generics: ast::Generics) {
1106-
let (fn_decl, fn_style, ident) = match item.node {
1107-
// FIXME: handling method
1108-
ast::ItemFn(ref fn_decl, ref fn_style, _, _, _) => {
1109-
(fn_decl, fn_style, item.ident)
1110-
},
1111-
_ => fail!("Expect function or method")
1112-
1113-
};
1114-
let fd = ast::FnDecl {
1115-
inputs: inputs,
1116-
output: output,
1117-
cf: fn_decl.cf,
1118-
variadic: fn_decl.variadic
1119-
};
1120-
let suggested_fn =
1121-
pprust::fun_to_str(&fd, *fn_style, ident, None, &generics);
1196+
decl: &ast::FnDecl,
1197+
fn_style: ast::FnStyle,
1198+
ident: ast::Ident,
1199+
opt_explicit_self: Option<ast::ExplicitSelf_>,
1200+
generics: &ast::Generics,
1201+
span: codemap::Span) {
1202+
let suggested_fn = pprust::fun_to_str(decl, fn_style, ident,
1203+
opt_explicit_self, generics);
11221204
let msg = format!("consider using an explicit lifetime \
11231205
parameter as shown: {}", suggested_fn);
1124-
self.tcx.sess.span_note(item.span, msg);
1206+
self.tcx.sess.span_note(span, msg);
11251207
}
11261208

11271209
fn report_inference_failure(&self,
@@ -1318,6 +1400,47 @@ impl Resolvable for @ty::TraitRef {
13181400
}
13191401
}
13201402

1403+
fn lifetimes_in_scope(tcx: &ty::ctxt,
1404+
scope_id: ast::NodeId)
1405+
-> Vec<ast::Lifetime> {
1406+
let mut taken = Vec::new();
1407+
let parent = tcx.map.get_parent(scope_id);
1408+
let method_id_opt = match tcx.map.find(parent) {
1409+
Some(node) => match node {
1410+
ast_map::NodeItem(item) => match item.node {
1411+
ast::ItemFn(_, _, _, ref gen, _) => {
1412+
taken.push_all(gen.lifetimes.as_slice());
1413+
None
1414+
},
1415+
_ => None
1416+
},
1417+
ast_map::NodeMethod(m) => {
1418+
taken.push_all(m.generics.lifetimes.as_slice());
1419+
Some(m.id)
1420+
},
1421+
_ => None
1422+
},
1423+
None => None
1424+
};
1425+
if method_id_opt.is_some() {
1426+
let method_id = method_id_opt.unwrap();
1427+
let parent = tcx.map.get_parent(method_id);
1428+
match tcx.map.find(parent) {
1429+
Some(node) => match node {
1430+
ast_map::NodeItem(item) => match item.node {
1431+
ast::ItemImpl(ref gen, _, _, _) => {
1432+
taken.push_all(gen.lifetimes.as_slice());
1433+
}
1434+
_ => ()
1435+
},
1436+
_ => ()
1437+
},
1438+
None => ()
1439+
}
1440+
}
1441+
return taken;
1442+
}
1443+
13211444
// LifeGiver is responsible for generating fresh lifetime names
13221445
struct LifeGiver {
13231446
taken: HashSet<~str>,
@@ -1326,7 +1449,6 @@ struct LifeGiver {
13261449
}
13271450

13281451
impl LifeGiver {
1329-
// FIXME: `taken` needs to include names from higher scope, too
13301452
fn with_taken(taken: &[ast::Lifetime]) -> LifeGiver {
13311453
let mut taken_ = HashSet::new();
13321454
for lt in taken.iter() {

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ impl<'r> Itble<'r, uint, Range<uint>> for (uint, uint) {
2222
}
2323

2424
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
25+
//~^ NOTE: consider using an explicit lifetime parameter as shown: fn check<'r, I: Iterator<uint>, T: Itble<'r, uint, I>>(cont: &'r T) -> bool
2626
let cont_iter = cont.iter(); //~ ERROR: cannot infer
2727
let result = cont_iter.fold(Some(0u16), |state, val| {
2828
state.map_or(None, |mask| {

0 commit comments

Comments
 (0)