diff options
| author | P1start <rewi-github@whanau.org> | 2014-09-13 14:01:17 +1200 |
|---|---|---|
| committer | P1start <rewi-github@whanau.org> | 2014-10-02 11:09:29 +1300 |
| commit | a8577be6f4e2d92b4f397aa8191e4ac48e2b4c6d (patch) | |
| tree | 5f84143453fb6e5c040648326ca6a7a4d093ae32 /src | |
| parent | 60e7317345f246a8169bbfe721473f693d54cade (diff) | |
| download | rust-a8577be6f4e2d92b4f397aa8191e4ac48e2b4c6d.tar.gz rust-a8577be6f4e2d92b4f397aa8191e4ac48e2b4c6d.zip | |
Output a note when lifetimes cannot be elided from functions
Diffstat (limited to 'src')
| -rw-r--r-- | src/librustc/middle/typeck/astconv.rs | 83 | ||||
| -rw-r--r-- | src/librustc/middle/typeck/check/mod.rs | 2 | ||||
| -rw-r--r-- | src/librustc/middle/typeck/rscope.rs | 33 | ||||
| -rw-r--r-- | src/test/compile-fail/lifetime-elision-return-type-requires-explicit-lifetime.rs | 7 |
4 files changed, 103 insertions, 22 deletions
diff --git a/src/librustc/middle/typeck/astconv.rs b/src/librustc/middle/typeck/astconv.rs index 2503fb2541b..fa68814ea16 100644 --- a/src/librustc/middle/typeck/astconv.rs +++ b/src/librustc/middle/typeck/astconv.rs @@ -59,7 +59,7 @@ use middle::subst::{VecPerParamSpace}; use middle::ty; use middle::typeck::lookup_def_tcx; use middle::typeck::infer; -use middle::typeck::rscope::{ExplicitRscope, RegionScope, SpecificRscope}; +use middle::typeck::rscope::{UnelidableRscope, RegionScope, SpecificRscope}; use middle::typeck::rscope; use middle::typeck::TypeAndSubsts; use middle::typeck; @@ -67,10 +67,11 @@ use util::ppaux::{Repr, UserString}; use std::collections::HashMap; use std::rc::Rc; -use syntax::abi; -use syntax::{ast, ast_util}; +use std::iter::AdditiveIterator; +use syntax::{abi, ast, ast_util}; use syntax::codemap::Span; use syntax::parse::token; +use syntax::print::pprust; pub trait AstConv<'tcx> { fn tcx<'a>(&'a self) -> &'a ty::ctxt<'tcx>; @@ -147,10 +148,49 @@ pub fn opt_ast_region_to_region<'tcx, AC: AstConv<'tcx>, RS: RegionScope>( None => { match rscope.anon_regions(default_span, 1) { - Err(()) => { + Err(v) => { debug!("optional region in illegal location"); span_err!(this.tcx().sess, default_span, E0106, "missing lifetime specifier"); + match v { + Some(v) => { + let mut m = String::new(); + let len = v.len(); + for (i, (name, n)) in v.move_iter().enumerate() { + m.push_str(if n == 1 { + format!("`{}`", name) + } else { + format!("one of `{}`'s {} elided lifetimes", name, n) + }.as_slice()); + + if len == 2 && i == 0 { + m.push_str(" or "); + } else if i == len - 2 { + m.push_str(", or "); + } else if i != len - 1 { + m.push_str(", "); + } + } + if len == 1 { + span_note!(this.tcx().sess, default_span, + "this function's return type contains a borrowed value, but \ + the signature does not say which {} it is borrowed from", + m); + } else if len == 0 { + span_note!(this.tcx().sess, default_span, + "this function's return type contains a borrowed value, but \ + there is no value for it to be borrowed from"); + span_note!(this.tcx().sess, default_span, + "consider giving it a 'static lifetime"); + } else { + span_note!(this.tcx().sess, default_span, + "this function's return type contains a borrowed value, but \ + the signature does not say whether it is borrowed from {}", + m); + } + } + None => {}, + } ty::ReStatic } @@ -217,7 +257,7 @@ fn ast_path_substs<'tcx,AC,RS>( match anon_regions { Ok(v) => v.into_iter().collect(), - Err(()) => Vec::from_fn(expected_num_region_params, + Err(_) => Vec::from_fn(expected_num_region_params, |_| ty::ReStatic) // hokey } }; @@ -1153,15 +1193,20 @@ fn ty_of_method_or_bare_fn<'tcx, AC: AstConv<'tcx>>( }; // HACK(eddyb) replace the fake self type in the AST with the actual type. - let input_tys = if self_ty.is_some() { + let input_params = if self_ty.is_some() { decl.inputs.slice_from(1) } else { decl.inputs.as_slice() }; - let input_tys = input_tys.iter().map(|a| ty_of_arg(this, &rb, a, None)); - let self_and_input_tys: Vec<_> = + let input_tys = input_params.iter().map(|a| ty_of_arg(this, &rb, a, None)); + let input_pats: Vec<String> = input_params.iter() + .map(|a| pprust::pat_to_string(&*a.pat)) + .collect(); + let self_and_input_tys: Vec<ty::t> = self_ty.into_iter().chain(input_tys).collect(); + let mut lifetimes_for_params: Vec<(String, Vec<ty::Region>)> = Vec::new(); + // Second, if there was exactly one lifetime (either a substitution or a // reference) in the arguments, then any anonymous regions in the output // have that lifetime. @@ -1172,15 +1217,25 @@ fn ty_of_method_or_bare_fn<'tcx, AC: AstConv<'tcx>>( drop(self_and_input_tys_iter.next()) } - let mut accumulator = Vec::new(); - for input_type in self_and_input_tys_iter { - ty::accumulate_lifetimes_in_type(&mut accumulator, *input_type) + for (input_type, input_pat) in self_and_input_tys_iter.zip(input_pats.into_iter()) { + let mut accumulator = Vec::new(); + ty::accumulate_lifetimes_in_type(&mut accumulator, *input_type); + lifetimes_for_params.push((input_pat, accumulator)); } - if accumulator.len() == 1 { - implied_output_region = Some(*accumulator.get(0)); + + if lifetimes_for_params.iter().map(|&(_, ref x)| x.len()).sum() == 1 { + implied_output_region = + Some(lifetimes_for_params.iter() + .filter_map(|&(_, ref x)| + if x.len() == 1 { Some(x[0]) } else { None }) + .next().unwrap()); } } + let param_lifetimes: Vec<(String, uint)> = lifetimes_for_params.into_iter() + .map(|(n, v)| (n, v.len())) + .collect(); + let output_ty = match decl.output.node { ast::TyInfer => this.ty_infer(decl.output.span), _ => { @@ -1193,7 +1248,7 @@ fn ty_of_method_or_bare_fn<'tcx, AC: AstConv<'tcx>>( // All regions must be explicitly specified in the output // if the lifetime elision rules do not apply. This saves // the user from potentially-confusing errors. - let rb = ExplicitRscope; + let rb = UnelidableRscope::new(param_lifetimes); ast_ty_to_ty(this, &rb, &*decl.output) } } diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index e49936f5a4c..9b3bf46b94c 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -1601,7 +1601,7 @@ impl<'a, 'tcx> RegionScope for infer::InferCtxt<'a, 'tcx> { } fn anon_regions(&self, span: Span, count: uint) - -> Result<Vec<ty::Region> , ()> { + -> Result<Vec<ty::Region>, Option<Vec<(String, uint)>>> { Ok(Vec::from_fn(count, |_| { self.next_region_var(infer::MiscVariable(span)) })) diff --git a/src/librustc/middle/typeck/rscope.rs b/src/librustc/middle/typeck/rscope.rs index 530f65855d4..2845e3954b5 100644 --- a/src/librustc/middle/typeck/rscope.rs +++ b/src/librustc/middle/typeck/rscope.rs @@ -29,7 +29,7 @@ pub trait RegionScope { fn anon_regions(&self, span: Span, count: uint) - -> Result<Vec<ty::Region> , ()>; + -> Result<Vec<ty::Region>, Option<Vec<(String, uint)>>>; fn default_region_bound(&self, span: Span) -> Option<ty::Region>; } @@ -46,8 +46,31 @@ impl RegionScope for ExplicitRscope { fn anon_regions(&self, _span: Span, _count: uint) - -> Result<Vec<ty::Region> , ()> { - Err(()) + -> Result<Vec<ty::Region>, Option<Vec<(String, uint)>>> { + Err(None) + } +} + +// Same as `ExplicitRscope`, but provides some extra information for diagnostics +pub struct UnelidableRscope(Vec<(String, uint)>); + +impl UnelidableRscope { + pub fn new(v: Vec<(String, uint)>) -> UnelidableRscope { + UnelidableRscope(v) + } +} + +impl RegionScope for UnelidableRscope { + fn default_region_bound(&self, _span: Span) -> Option<ty::Region> { + None + } + + fn anon_regions(&self, + _span: Span, + _count: uint) + -> Result<Vec<ty::Region>, Option<Vec<(String, uint)>>> { + let UnelidableRscope(ref v) = *self; + Err(Some(v.clone())) } } @@ -72,7 +95,7 @@ impl RegionScope for SpecificRscope { fn anon_regions(&self, _span: Span, count: uint) - -> Result<Vec<ty::Region> , ()> + -> Result<Vec<ty::Region>, Option<Vec<(String, uint)>>> { Ok(Vec::from_elem(count, self.default)) } @@ -109,7 +132,7 @@ impl RegionScope for BindingRscope { fn anon_regions(&self, _: Span, count: uint) - -> Result<Vec<ty::Region> , ()> + -> Result<Vec<ty::Region>, Option<Vec<(String, uint)>>> { Ok(Vec::from_fn(count, |_| self.next_region())) } diff --git a/src/test/compile-fail/lifetime-elision-return-type-requires-explicit-lifetime.rs b/src/test/compile-fail/lifetime-elision-return-type-requires-explicit-lifetime.rs index aef3f7a40b5..5fa8c5db5b0 100644 --- a/src/test/compile-fail/lifetime-elision-return-type-requires-explicit-lifetime.rs +++ b/src/test/compile-fail/lifetime-elision-return-type-requires-explicit-lifetime.rs @@ -10,11 +10,13 @@ // Lifetime annotation needed because we have no arguments. fn f() -> &int { //~ ERROR missing lifetime specifier +//~^ NOTE there is no value for it to be borrowed from fail!() } // Lifetime annotation needed because we have two by-reference parameters. -fn g(_: &int, _: &int) -> &int { //~ ERROR missing lifetime specifier +fn g(_x: &int, _y: &int) -> &int { //~ ERROR missing lifetime specifier +//~^ NOTE the signature does not say whether it is borrowed from `_x` or `_y` fail!() } @@ -24,7 +26,8 @@ struct Foo<'a> { // Lifetime annotation needed because we have two lifetimes: one as a parameter // and one on the reference. -fn h(_: &Foo) -> &int { //~ ERROR missing lifetime specifier +fn h(_x: &Foo) -> &int { //~ ERROR missing lifetime specifier +//~^ NOTE the signature does not say which one of `_x`'s 2 elided lifetimes it is borrowed from fail!() } |
