rollup merge of #19589: huonw/unboxed-closure-elision
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.
This commit is contained in:
commit
63ef9e980f
@ -386,20 +386,81 @@ fn convert_angle_bracketed_parameters<'tcx, AC, RS>(this: &AC,
|
||||
(regions, types)
|
||||
}
|
||||
|
||||
/// Returns the appropriate lifetime to use for any output lifetimes
|
||||
/// (if one exists) and a vector of the (pattern, number of lifetimes)
|
||||
/// corresponding to each input type/pattern.
|
||||
fn find_implied_output_region(input_tys: &[Ty], input_pats: Vec<String>)
|
||||
-> (Option<ty::Region>, Vec<(String, uint)>)
|
||||
{
|
||||
let mut lifetimes_for_params: Vec<(String, uint)> = Vec::new();
|
||||
let mut possible_implied_output_region = None;
|
||||
|
||||
for (input_type, input_pat) in input_tys.iter().zip(input_pats.into_iter()) {
|
||||
let mut accumulator = Vec::new();
|
||||
ty::accumulate_lifetimes_in_type(&mut accumulator, *input_type);
|
||||
|
||||
if accumulator.len() == 1 {
|
||||
// there's a chance that the unique lifetime of this
|
||||
// iteration will be the appropriate lifetime for output
|
||||
// parameters, so lets store it.
|
||||
possible_implied_output_region = Some(accumulator[0])
|
||||
}
|
||||
|
||||
lifetimes_for_params.push((input_pat, accumulator.len()));
|
||||
}
|
||||
|
||||
let implied_output_region = if lifetimes_for_params.iter().map(|&(_, n)| n).sum() == 1 {
|
||||
assert!(possible_implied_output_region.is_some());
|
||||
possible_implied_output_region
|
||||
} else {
|
||||
None
|
||||
};
|
||||
(implied_output_region, lifetimes_for_params)
|
||||
}
|
||||
|
||||
fn convert_ty_with_lifetime_elision<'tcx,AC>(this: &AC,
|
||||
implied_output_region: Option<ty::Region>,
|
||||
param_lifetimes: Vec<(String, uint)>,
|
||||
ty: &ast::Ty)
|
||||
-> Ty<'tcx>
|
||||
where AC: AstConv<'tcx>
|
||||
{
|
||||
match implied_output_region {
|
||||
Some(implied_output_region) => {
|
||||
let rb = SpecificRscope::new(implied_output_region);
|
||||
ast_ty_to_ty(this, &rb, ty)
|
||||
}
|
||||
None => {
|
||||
// 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 = UnelidableRscope::new(param_lifetimes);
|
||||
ast_ty_to_ty(this, &rb, ty)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_parenthesized_parameters<'tcx,AC>(this: &AC,
|
||||
data: &ast::ParenthesizedParameterData)
|
||||
-> Vec<Ty<'tcx>>
|
||||
where AC: AstConv<'tcx>
|
||||
{
|
||||
let binding_rscope = BindingRscope::new();
|
||||
|
||||
let inputs = data.inputs.iter()
|
||||
.map(|a_t| ast_ty_to_ty(this, &binding_rscope, &**a_t))
|
||||
.collect();
|
||||
.collect::<Vec<Ty<'tcx>>>();
|
||||
|
||||
let input_params = Vec::from_elem(inputs.len(), String::new());
|
||||
let (implied_output_region,
|
||||
params_lifetimes) = find_implied_output_region(&*inputs, input_params);
|
||||
|
||||
let input_ty = ty::mk_tup(this.tcx(), inputs);
|
||||
|
||||
let output = match data.output {
|
||||
Some(ref output_ty) => ast_ty_to_ty(this, &binding_rscope, &**output_ty),
|
||||
Some(ref output_ty) => convert_ty_with_lifetime_elision(this,
|
||||
implied_output_region,
|
||||
params_lifetimes,
|
||||
&**output_ty),
|
||||
None => ty::mk_nil(this.tcx()),
|
||||
};
|
||||
|
||||
@ -1059,55 +1120,33 @@ fn ty_of_method_or_bare_fn<'a, 'tcx, AC: AstConv<'tcx>>(
|
||||
let self_and_input_tys: Vec<Ty> =
|
||||
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.
|
||||
if implied_output_region.is_none() {
|
||||
let mut self_and_input_tys_iter = self_and_input_tys.iter();
|
||||
if self_ty.is_some() {
|
||||
let lifetimes_for_params = if implied_output_region.is_none() {
|
||||
let input_tys = if self_ty.is_some() {
|
||||
// Skip the first argument if `self` is present.
|
||||
drop(self_and_input_tys_iter.next())
|
||||
}
|
||||
self_and_input_tys.slice_from(1)
|
||||
} else {
|
||||
self_and_input_tys.slice_from(0)
|
||||
};
|
||||
|
||||
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 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()))
|
||||
.filter(|&(_, l)| l != 0)
|
||||
.collect();
|
||||
let (ior, lfp) = find_implied_output_region(input_tys, input_pats);
|
||||
implied_output_region = ior;
|
||||
lfp
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
||||
let output_ty = match decl.output {
|
||||
ast::Return(ref output) if output.node == ast::TyInfer =>
|
||||
ty::FnConverging(this.ty_infer(output.span)),
|
||||
ast::Return(ref output) =>
|
||||
ty::FnConverging(match implied_output_region {
|
||||
Some(implied_output_region) => {
|
||||
let rb = SpecificRscope::new(implied_output_region);
|
||||
ast_ty_to_ty(this, &rb, &**output)
|
||||
}
|
||||
None => {
|
||||
// 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 = UnelidableRscope::new(param_lifetimes);
|
||||
ast_ty_to_ty(this, &rb, &**output)
|
||||
}
|
||||
}),
|
||||
ty::FnConverging(convert_ty_with_lifetime_elision(this,
|
||||
implied_output_region,
|
||||
lifetimes_for_params,
|
||||
&**output)),
|
||||
ast::NoReturn(_) => ty::FnDiverging
|
||||
};
|
||||
|
||||
|
@ -44,9 +44,9 @@ fn test<'a,'b>() {
|
||||
eq::< for<'a,'b> Foo<(&'a int,&'b uint),uint>,
|
||||
Foo(&int,&uint) -> uint >();
|
||||
|
||||
// FIXME(#18992) Test lifetime elision in `()` form:
|
||||
// eq::< for<'a,'b> Foo<(&'a int,), &'a int>,
|
||||
// Foo(&int) -> &int >();
|
||||
// lifetime elision
|
||||
eq::< for<'a,'b> Foo<(&'a int,), &'a int>,
|
||||
Foo(&int) -> &int >();
|
||||
|
||||
// Errors expected:
|
||||
eq::< Foo<(),()>, Foo(char) >();
|
||||
|
@ -0,0 +1,34 @@
|
||||
// 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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Test that the unboxed closure sugar can be used with an arbitrary
|
||||
// struct type and that it is equivalent to the same syntax using
|
||||
// angle brackets. This test covers only simple types and in
|
||||
// particular doesn't test bound regions.
|
||||
|
||||
#![feature(unboxed_closures)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
trait Foo<T,U> {
|
||||
fn dummy(&self, t: T, u: U);
|
||||
}
|
||||
|
||||
trait Eq<Sized? X> for Sized? { }
|
||||
impl<Sized? X> Eq<X> for X { }
|
||||
fn eq<Sized? A,Sized? B:Eq<A>>() { }
|
||||
|
||||
fn main() {
|
||||
eq::< for<'a> Foo<(&'a int,), &'a int>,
|
||||
Foo(&int) -> &int >();
|
||||
eq::< for<'a> Foo<(&'a int,), (&'a int, &'a int)>,
|
||||
Foo(&int) -> (&int, &int) >();
|
||||
|
||||
let _: Foo(&int, &uint) -> &uint; //~ ERROR missing lifetime specifier
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user