Skip to content

Commit b800ce1

Browse files
committed
Implement lifetime elision for Foo(...) -> ... type sugar.
This means that `Fn(&A) -> (&B, &C)` is equivalent to `for<'a> Fn(&'a A) -> (&'a B, &'a C)` similar to the lifetime elision of lower-case `fn` in types and declarations. Closes #18992.
1 parent 4573da6 commit b800ce1

File tree

3 files changed

+117
-44
lines changed

3 files changed

+117
-44
lines changed

src/librustc_typeck/astconv.rs

+80-41
Original file line numberDiff line numberDiff line change
@@ -386,20 +386,81 @@ fn convert_angle_bracketed_parameters<'tcx, AC, RS>(this: &AC,
386386
(regions, types)
387387
}
388388

389+
/// Returns the appropriate lifetime to use for any output lifetimes
390+
/// (if one exists) and a vector of the (pattern, number of lifetimes)
391+
/// corresponding to each input type/pattern.
392+
fn find_implied_output_region(input_tys: &[Ty], input_pats: Vec<String>)
393+
-> (Option<ty::Region>, Vec<(String, uint)>)
394+
{
395+
let mut lifetimes_for_params: Vec<(String, uint)> = Vec::new();
396+
let mut possible_implied_output_region = None;
397+
398+
for (input_type, input_pat) in input_tys.iter().zip(input_pats.into_iter()) {
399+
let mut accumulator = Vec::new();
400+
ty::accumulate_lifetimes_in_type(&mut accumulator, *input_type);
401+
402+
if accumulator.len() == 1 {
403+
// there's a chance that the unique lifetime of this
404+
// iteration will be the appropriate lifetime for output
405+
// parameters, so lets store it.
406+
possible_implied_output_region = Some(accumulator[0])
407+
}
408+
409+
lifetimes_for_params.push((input_pat, accumulator.len()));
410+
}
411+
412+
let implied_output_region = if lifetimes_for_params.iter().map(|&(_, n)| n).sum() == 1 {
413+
assert!(possible_implied_output_region.is_some());
414+
possible_implied_output_region
415+
} else {
416+
None
417+
};
418+
(implied_output_region, lifetimes_for_params)
419+
}
420+
421+
fn convert_ty_with_lifetime_elision<'tcx,AC>(this: &AC,
422+
implied_output_region: Option<ty::Region>,
423+
param_lifetimes: Vec<(String, uint)>,
424+
ty: &ast::Ty)
425+
-> Ty<'tcx>
426+
where AC: AstConv<'tcx>
427+
{
428+
match implied_output_region {
429+
Some(implied_output_region) => {
430+
let rb = SpecificRscope::new(implied_output_region);
431+
ast_ty_to_ty(this, &rb, ty)
432+
}
433+
None => {
434+
// All regions must be explicitly specified in the output
435+
// if the lifetime elision rules do not apply. This saves
436+
// the user from potentially-confusing errors.
437+
let rb = UnelidableRscope::new(param_lifetimes);
438+
ast_ty_to_ty(this, &rb, ty)
439+
}
440+
}
441+
}
442+
389443
fn convert_parenthesized_parameters<'tcx,AC>(this: &AC,
390444
data: &ast::ParenthesizedParameterData)
391445
-> Vec<Ty<'tcx>>
392446
where AC: AstConv<'tcx>
393447
{
394448
let binding_rscope = BindingRscope::new();
395-
396449
let inputs = data.inputs.iter()
397450
.map(|a_t| ast_ty_to_ty(this, &binding_rscope, &**a_t))
398-
.collect();
451+
.collect::<Vec<Ty<'tcx>>>();
452+
453+
let input_params = Vec::from_elem(inputs.len(), String::new());
454+
let (implied_output_region,
455+
params_lifetimes) = find_implied_output_region(&*inputs, input_params);
456+
399457
let input_ty = ty::mk_tup(this.tcx(), inputs);
400458

401459
let output = match data.output {
402-
Some(ref output_ty) => ast_ty_to_ty(this, &binding_rscope, &**output_ty),
460+
Some(ref output_ty) => convert_ty_with_lifetime_elision(this,
461+
implied_output_region,
462+
params_lifetimes,
463+
&**output_ty),
403464
None => ty::mk_nil(this.tcx()),
404465
};
405466

@@ -1059,55 +1120,33 @@ fn ty_of_method_or_bare_fn<'a, 'tcx, AC: AstConv<'tcx>>(
10591120
let self_and_input_tys: Vec<Ty> =
10601121
self_ty.into_iter().chain(input_tys).collect();
10611122

1062-
let mut lifetimes_for_params: Vec<(String, Vec<ty::Region>)> = Vec::new();
10631123

10641124
// Second, if there was exactly one lifetime (either a substitution or a
10651125
// reference) in the arguments, then any anonymous regions in the output
10661126
// have that lifetime.
1067-
if implied_output_region.is_none() {
1068-
let mut self_and_input_tys_iter = self_and_input_tys.iter();
1069-
if self_ty.is_some() {
1127+
let lifetimes_for_params = if implied_output_region.is_none() {
1128+
let input_tys = if self_ty.is_some() {
10701129
// Skip the first argument if `self` is present.
1071-
drop(self_and_input_tys_iter.next())
1072-
}
1073-
1074-
for (input_type, input_pat) in self_and_input_tys_iter.zip(input_pats.into_iter()) {
1075-
let mut accumulator = Vec::new();
1076-
ty::accumulate_lifetimes_in_type(&mut accumulator, *input_type);
1077-
lifetimes_for_params.push((input_pat, accumulator));
1078-
}
1079-
1080-
if lifetimes_for_params.iter().map(|&(_, ref x)| x.len()).sum() == 1 {
1081-
implied_output_region =
1082-
Some(lifetimes_for_params.iter()
1083-
.filter_map(|&(_, ref x)|
1084-
if x.len() == 1 { Some(x[0]) } else { None })
1085-
.next().unwrap());
1086-
}
1087-
}
1130+
self_and_input_tys.slice_from(1)
1131+
} else {
1132+
self_and_input_tys.slice_from(0)
1133+
};
10881134

1089-
let param_lifetimes: Vec<(String, uint)> = lifetimes_for_params.into_iter()
1090-
.map(|(n, v)| (n, v.len()))
1091-
.filter(|&(_, l)| l != 0)
1092-
.collect();
1135+
let (ior, lfp) = find_implied_output_region(input_tys, input_pats);
1136+
implied_output_region = ior;
1137+
lfp
1138+
} else {
1139+
vec![]
1140+
};
10931141

10941142
let output_ty = match decl.output {
10951143
ast::Return(ref output) if output.node == ast::TyInfer =>
10961144
ty::FnConverging(this.ty_infer(output.span)),
10971145
ast::Return(ref output) =>
1098-
ty::FnConverging(match implied_output_region {
1099-
Some(implied_output_region) => {
1100-
let rb = SpecificRscope::new(implied_output_region);
1101-
ast_ty_to_ty(this, &rb, &**output)
1102-
}
1103-
None => {
1104-
// All regions must be explicitly specified in the output
1105-
// if the lifetime elision rules do not apply. This saves
1106-
// the user from potentially-confusing errors.
1107-
let rb = UnelidableRscope::new(param_lifetimes);
1108-
ast_ty_to_ty(this, &rb, &**output)
1109-
}
1110-
}),
1146+
ty::FnConverging(convert_ty_with_lifetime_elision(this,
1147+
implied_output_region,
1148+
lifetimes_for_params,
1149+
&**output)),
11111150
ast::NoReturn(_) => ty::FnDiverging
11121151
};
11131152

src/test/compile-fail/unboxed-closure-sugar-equiv.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,9 @@ fn test<'a,'b>() {
4444
eq::< for<'a,'b> Foo<(&'a int,&'b uint),uint>,
4545
Foo(&int,&uint) -> uint >();
4646

47-
// FIXME(#18992) Test lifetime elision in `()` form:
48-
// eq::< for<'a,'b> Foo<(&'a int,), &'a int>,
49-
// Foo(&int) -> &int >();
47+
// lifetime elision
48+
eq::< for<'a,'b> Foo<(&'a int,), &'a int>,
49+
Foo(&int) -> &int >();
5050

5151
// Errors expected:
5252
eq::< Foo<(),()>, Foo(char) >();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
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+
// Test that the unboxed closure sugar can be used with an arbitrary
12+
// struct type and that it is equivalent to the same syntax using
13+
// angle brackets. This test covers only simple types and in
14+
// particular doesn't test bound regions.
15+
16+
#![feature(unboxed_closures)]
17+
#![allow(dead_code)]
18+
19+
trait Foo<T,U> {
20+
fn dummy(&self, t: T, u: U);
21+
}
22+
23+
trait Eq<Sized? X> for Sized? { }
24+
impl<Sized? X> Eq<X> for X { }
25+
fn eq<Sized? A,Sized? B:Eq<A>>() { }
26+
27+
fn main() {
28+
eq::< for<'a> Foo<(&'a int,), &'a int>,
29+
Foo(&int) -> &int >();
30+
eq::< for<'a> Foo<(&'a int,), (&'a int, &'a int)>,
31+
Foo(&int) -> (&int, &int) >();
32+
33+
let _: Foo(&int, &uint) -> &uint; //~ ERROR missing lifetime specifier
34+
}

0 commit comments

Comments
 (0)