Skip to content

Commit a8577be

Browse files
committed
Output a note when lifetimes cannot be elided from functions
1 parent 60e7317 commit a8577be

File tree

4 files changed

+103
-22
lines changed

4 files changed

+103
-22
lines changed

src/librustc/middle/typeck/astconv.rs

Lines changed: 69 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -59,18 +59,19 @@ use middle::subst::{VecPerParamSpace};
5959
use middle::ty;
6060
use middle::typeck::lookup_def_tcx;
6161
use middle::typeck::infer;
62-
use middle::typeck::rscope::{ExplicitRscope, RegionScope, SpecificRscope};
62+
use middle::typeck::rscope::{UnelidableRscope, RegionScope, SpecificRscope};
6363
use middle::typeck::rscope;
6464
use middle::typeck::TypeAndSubsts;
6565
use middle::typeck;
6666
use util::ppaux::{Repr, UserString};
6767

6868
use std::collections::HashMap;
6969
use std::rc::Rc;
70-
use syntax::abi;
71-
use syntax::{ast, ast_util};
70+
use std::iter::AdditiveIterator;
71+
use syntax::{abi, ast, ast_util};
7272
use syntax::codemap::Span;
7373
use syntax::parse::token;
74+
use syntax::print::pprust;
7475

7576
pub trait AstConv<'tcx> {
7677
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>(
147148

148149
None => {
149150
match rscope.anon_regions(default_span, 1) {
150-
Err(()) => {
151+
Err(v) => {
151152
debug!("optional region in illegal location");
152153
span_err!(this.tcx().sess, default_span, E0106,
153154
"missing lifetime specifier");
155+
match v {
156+
Some(v) => {
157+
let mut m = String::new();
158+
let len = v.len();
159+
for (i, (name, n)) in v.move_iter().enumerate() {
160+
m.push_str(if n == 1 {
161+
format!("`{}`", name)
162+
} else {
163+
format!("one of `{}`'s {} elided lifetimes", name, n)
164+
}.as_slice());
165+
166+
if len == 2 && i == 0 {
167+
m.push_str(" or ");
168+
} else if i == len - 2 {
169+
m.push_str(", or ");
170+
} else if i != len - 1 {
171+
m.push_str(", ");
172+
}
173+
}
174+
if len == 1 {
175+
span_note!(this.tcx().sess, default_span,
176+
"this function's return type contains a borrowed value, but \
177+
the signature does not say which {} it is borrowed from",
178+
m);
179+
} else if len == 0 {
180+
span_note!(this.tcx().sess, default_span,
181+
"this function's return type contains a borrowed value, but \
182+
there is no value for it to be borrowed from");
183+
span_note!(this.tcx().sess, default_span,
184+
"consider giving it a 'static lifetime");
185+
} else {
186+
span_note!(this.tcx().sess, default_span,
187+
"this function's return type contains a borrowed value, but \
188+
the signature does not say whether it is borrowed from {}",
189+
m);
190+
}
191+
}
192+
None => {},
193+
}
154194
ty::ReStatic
155195
}
156196

@@ -217,7 +257,7 @@ fn ast_path_substs<'tcx,AC,RS>(
217257

218258
match anon_regions {
219259
Ok(v) => v.into_iter().collect(),
220-
Err(()) => Vec::from_fn(expected_num_region_params,
260+
Err(_) => Vec::from_fn(expected_num_region_params,
221261
|_| ty::ReStatic) // hokey
222262
}
223263
};
@@ -1153,15 +1193,20 @@ fn ty_of_method_or_bare_fn<'tcx, AC: AstConv<'tcx>>(
11531193
};
11541194

11551195
// HACK(eddyb) replace the fake self type in the AST with the actual type.
1156-
let input_tys = if self_ty.is_some() {
1196+
let input_params = if self_ty.is_some() {
11571197
decl.inputs.slice_from(1)
11581198
} else {
11591199
decl.inputs.as_slice()
11601200
};
1161-
let input_tys = input_tys.iter().map(|a| ty_of_arg(this, &rb, a, None));
1162-
let self_and_input_tys: Vec<_> =
1201+
let input_tys = input_params.iter().map(|a| ty_of_arg(this, &rb, a, None));
1202+
let input_pats: Vec<String> = input_params.iter()
1203+
.map(|a| pprust::pat_to_string(&*a.pat))
1204+
.collect();
1205+
let self_and_input_tys: Vec<ty::t> =
11631206
self_ty.into_iter().chain(input_tys).collect();
11641207

1208+
let mut lifetimes_for_params: Vec<(String, Vec<ty::Region>)> = Vec::new();
1209+
11651210
// Second, if there was exactly one lifetime (either a substitution or a
11661211
// reference) in the arguments, then any anonymous regions in the output
11671212
// have that lifetime.
@@ -1172,15 +1217,25 @@ fn ty_of_method_or_bare_fn<'tcx, AC: AstConv<'tcx>>(
11721217
drop(self_and_input_tys_iter.next())
11731218
}
11741219

1175-
let mut accumulator = Vec::new();
1176-
for input_type in self_and_input_tys_iter {
1177-
ty::accumulate_lifetimes_in_type(&mut accumulator, *input_type)
1220+
for (input_type, input_pat) in self_and_input_tys_iter.zip(input_pats.into_iter()) {
1221+
let mut accumulator = Vec::new();
1222+
ty::accumulate_lifetimes_in_type(&mut accumulator, *input_type);
1223+
lifetimes_for_params.push((input_pat, accumulator));
11781224
}
1179-
if accumulator.len() == 1 {
1180-
implied_output_region = Some(*accumulator.get(0));
1225+
1226+
if lifetimes_for_params.iter().map(|&(_, ref x)| x.len()).sum() == 1 {
1227+
implied_output_region =
1228+
Some(lifetimes_for_params.iter()
1229+
.filter_map(|&(_, ref x)|
1230+
if x.len() == 1 { Some(x[0]) } else { None })
1231+
.next().unwrap());
11811232
}
11821233
}
11831234

1235+
let param_lifetimes: Vec<(String, uint)> = lifetimes_for_params.into_iter()
1236+
.map(|(n, v)| (n, v.len()))
1237+
.collect();
1238+
11841239
let output_ty = match decl.output.node {
11851240
ast::TyInfer => this.ty_infer(decl.output.span),
11861241
_ => {
@@ -1193,7 +1248,7 @@ fn ty_of_method_or_bare_fn<'tcx, AC: AstConv<'tcx>>(
11931248
// All regions must be explicitly specified in the output
11941249
// if the lifetime elision rules do not apply. This saves
11951250
// the user from potentially-confusing errors.
1196-
let rb = ExplicitRscope;
1251+
let rb = UnelidableRscope::new(param_lifetimes);
11971252
ast_ty_to_ty(this, &rb, &*decl.output)
11981253
}
11991254
}

src/librustc/middle/typeck/check/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1601,7 +1601,7 @@ impl<'a, 'tcx> RegionScope for infer::InferCtxt<'a, 'tcx> {
16011601
}
16021602

16031603
fn anon_regions(&self, span: Span, count: uint)
1604-
-> Result<Vec<ty::Region> , ()> {
1604+
-> Result<Vec<ty::Region>, Option<Vec<(String, uint)>>> {
16051605
Ok(Vec::from_fn(count, |_| {
16061606
self.next_region_var(infer::MiscVariable(span))
16071607
}))

src/librustc/middle/typeck/rscope.rs

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ pub trait RegionScope {
2929
fn anon_regions(&self,
3030
span: Span,
3131
count: uint)
32-
-> Result<Vec<ty::Region> , ()>;
32+
-> Result<Vec<ty::Region>, Option<Vec<(String, uint)>>>;
3333

3434
fn default_region_bound(&self, span: Span) -> Option<ty::Region>;
3535
}
@@ -46,8 +46,31 @@ impl RegionScope for ExplicitRscope {
4646
fn anon_regions(&self,
4747
_span: Span,
4848
_count: uint)
49-
-> Result<Vec<ty::Region> , ()> {
50-
Err(())
49+
-> Result<Vec<ty::Region>, Option<Vec<(String, uint)>>> {
50+
Err(None)
51+
}
52+
}
53+
54+
// Same as `ExplicitRscope`, but provides some extra information for diagnostics
55+
pub struct UnelidableRscope(Vec<(String, uint)>);
56+
57+
impl UnelidableRscope {
58+
pub fn new(v: Vec<(String, uint)>) -> UnelidableRscope {
59+
UnelidableRscope(v)
60+
}
61+
}
62+
63+
impl RegionScope for UnelidableRscope {
64+
fn default_region_bound(&self, _span: Span) -> Option<ty::Region> {
65+
None
66+
}
67+
68+
fn anon_regions(&self,
69+
_span: Span,
70+
_count: uint)
71+
-> Result<Vec<ty::Region>, Option<Vec<(String, uint)>>> {
72+
let UnelidableRscope(ref v) = *self;
73+
Err(Some(v.clone()))
5174
}
5275
}
5376

@@ -72,7 +95,7 @@ impl RegionScope for SpecificRscope {
7295
fn anon_regions(&self,
7396
_span: Span,
7497
count: uint)
75-
-> Result<Vec<ty::Region> , ()>
98+
-> Result<Vec<ty::Region>, Option<Vec<(String, uint)>>>
7699
{
77100
Ok(Vec::from_elem(count, self.default))
78101
}
@@ -109,7 +132,7 @@ impl RegionScope for BindingRscope {
109132
fn anon_regions(&self,
110133
_: Span,
111134
count: uint)
112-
-> Result<Vec<ty::Region> , ()>
135+
-> Result<Vec<ty::Region>, Option<Vec<(String, uint)>>>
113136
{
114137
Ok(Vec::from_fn(count, |_| self.next_region()))
115138
}

src/test/compile-fail/lifetime-elision-return-type-requires-explicit-lifetime.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@
1010

1111
// Lifetime annotation needed because we have no arguments.
1212
fn f() -> &int { //~ ERROR missing lifetime specifier
13+
//~^ NOTE there is no value for it to be borrowed from
1314
fail!()
1415
}
1516

1617
// Lifetime annotation needed because we have two by-reference parameters.
17-
fn g(_: &int, _: &int) -> &int { //~ ERROR missing lifetime specifier
18+
fn g(_x: &int, _y: &int) -> &int { //~ ERROR missing lifetime specifier
19+
//~^ NOTE the signature does not say whether it is borrowed from `_x` or `_y`
1820
fail!()
1921
}
2022

@@ -24,7 +26,8 @@ struct Foo<'a> {
2426

2527
// Lifetime annotation needed because we have two lifetimes: one as a parameter
2628
// and one on the reference.
27-
fn h(_: &Foo) -> &int { //~ ERROR missing lifetime specifier
29+
fn h(_x: &Foo) -> &int { //~ ERROR missing lifetime specifier
30+
//~^ NOTE the signature does not say which one of `_x`'s 2 elided lifetimes it is borrowed from
2831
fail!()
2932
}
3033

0 commit comments

Comments
 (0)