From 1d99d37f87d4cc46d285e883c2fc28b3e4e5c405 Mon Sep 17 00:00:00 2001 From: Kiet Tran Date: Mon, 24 Mar 2014 19:11:44 -0400 Subject: [PATCH] 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. --- .../middle/typeck/infer/error_reporting.rs | 134 ++++++++++++++---- ...me-inference-give-expl-lifetime-param-2.rs | 36 +++++ ...time-inference-give-expl-lifetime-param.rs | 11 ++ 3 files changed, 157 insertions(+), 24 deletions(-) create mode 100644 src/test/compile-fail/lifetime-inference-give-expl-lifetime-param-2.rs diff --git a/src/librustc/middle/typeck/infer/error_reporting.rs b/src/librustc/middle/typeck/infer/error_reporting.rs index 83ca7ea7dfbd4..25465cc2d6548 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, + // 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, + region_names: &'a HashSet +} + struct Rebuilder<'a> { tcx: &'a ty::ctxt, fn_decl: ast::P, @@ -708,6 +720,7 @@ impl<'a> Rebuilder<'a> { fn rebuild(&self) -> (Vec, ast::P, 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, ®ion_names); output = self.rebuild_arg_ty_or_output(output, lifetime, &anon_nums, ®ion_names); + ty_params = self.rebuild_ty_params(ty_params, lifetime, + ®ion_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, + lifetime: ast::Lifetime, + region_names: &HashSet) + -> OwnedSlice { + 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, + lifetime: ast::Lifetime, + region_names: &HashSet) + -> OwnedSlice { + 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(<.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, - remove: &HashSet) + remove: &HashSet, + ty_params: OwnedSlice) -> 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, to: ast::P, lifetime: ast::Lifetime, - index_opt: Option<(uint, uint)>) + rebuild_path_info: Option) -> ast::P { fn build_to(from: ast::P, @@ -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 0000000000000..a6bf5a4b653d5 --- /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 or the MIT license +// , 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> { fn iter(&'r self) -> I; } + +impl<'r> Itble<'r, uint, Range> for (uint, uint) { + fn iter(&'r self) -> Range { + let &(min, max) = self; + range(min, max) + } +} + +fn check<'r, I: Iterator, T: Itble<'r, uint, I>>(cont: &T) -> bool { +//~^ NOTE: consider using an explicit lifetime parameter as shown: fn check<'a, I: Iterator, 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 709f15d355245..33b849f346a05 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() {}