1
Fork 0

Implement impl Trait lifetime elision

This commit is contained in:
Taylor Cramer 2017-12-09 21:54:58 -08:00
parent 4c2819d326
commit 018c4038c7
3 changed files with 134 additions and 55 deletions

View file

@ -990,8 +990,9 @@ impl<'a> LoweringContext<'a> {
struct ImplTraitLifetimeCollector<'r, 'a: 'r> { struct ImplTraitLifetimeCollector<'r, 'a: 'r> {
context: &'r mut LoweringContext<'a>, context: &'r mut LoweringContext<'a>,
parent: DefIndex, parent: DefIndex,
currently_bound_lifetimes: Vec<Name>, collect_elided_lifetimes: bool,
already_defined_lifetimes: HashSet<Name>, currently_bound_lifetimes: Vec<hir::LifetimeName>,
already_defined_lifetimes: HashSet<hir::LifetimeName>,
output_lifetimes: Vec<hir::Lifetime>, output_lifetimes: Vec<hir::Lifetime>,
output_lifetime_defs: Vec<hir::LifetimeDef>, output_lifetime_defs: Vec<hir::LifetimeDef>,
} }
@ -1002,6 +1003,30 @@ impl<'a> LoweringContext<'a> {
hir::intravisit::NestedVisitorMap::None hir::intravisit::NestedVisitorMap::None
} }
fn visit_path_parameters(&mut self, span: Span, parameters: &'v hir::PathParameters) {
// Don't collect elided lifetimes used inside of `Fn()` syntax.
if parameters.parenthesized {
let old_collect_elided_lifetimes = self.collect_elided_lifetimes;
self.collect_elided_lifetimes = false;
hir::intravisit::walk_path_parameters(self, span, parameters);
self.collect_elided_lifetimes = old_collect_elided_lifetimes;
} else {
hir::intravisit::walk_path_parameters(self, span, parameters);
}
}
fn visit_ty(&mut self, t: &'v hir::Ty) {
// Don't collect elided lifetimes used inside of `fn()` syntax
if let &hir::Ty_::TyBareFn(_) = &t.node {
let old_collect_elided_lifetimes = self.collect_elided_lifetimes;
self.collect_elided_lifetimes = false;
hir::intravisit::walk_ty(self, t);
self.collect_elided_lifetimes = old_collect_elided_lifetimes;
} else {
hir::intravisit::walk_ty(self, t);
}
}
fn visit_poly_trait_ref(&mut self, fn visit_poly_trait_ref(&mut self,
polytr: &'v hir::PolyTraitRef, polytr: &'v hir::PolyTraitRef,
_: hir::TraitBoundModifier) { _: hir::TraitBoundModifier) {
@ -1010,10 +1035,8 @@ impl<'a> LoweringContext<'a> {
// Record the introduction of 'a in `for<'a> ...` // Record the introduction of 'a in `for<'a> ...`
for lt_def in &polytr.bound_lifetimes { for lt_def in &polytr.bound_lifetimes {
// Introduce lifetimes one at a time so that we can handle // Introduce lifetimes one at a time so that we can handle
// cases like `fn foo<'d>() -> impl for<'a, 'b: 'a, 'c: 'b + 'd> ...` // cases like `fn foo<'d>() -> impl for<'a, 'b: 'a, 'c: 'b + 'd>`
if let hir::LifetimeName::Name(name) = lt_def.lifetime.name { self.currently_bound_lifetimes.push(lt_def.lifetime.name);
self.currently_bound_lifetimes.push(name);
}
// Visit the lifetime bounds // Visit the lifetime bounds
for lt_bound in &lt_def.bounds { for lt_bound in &lt_def.bounds {
@ -1027,40 +1050,50 @@ impl<'a> LoweringContext<'a> {
} }
fn visit_lifetime(&mut self, lifetime: &'v hir::Lifetime) { fn visit_lifetime(&mut self, lifetime: &'v hir::Lifetime) {
// Exclude '_, 'static, and elided lifetimes (there should be no elided lifetimes) let name = match lifetime.name {
if let hir::LifetimeName::Name(lifetime_name) = lifetime.name { hir::LifetimeName::Implicit |
if !self.currently_bound_lifetimes.contains(&lifetime_name) && hir::LifetimeName::Underscore =>
!self.already_defined_lifetimes.contains(&lifetime_name) if self.collect_elided_lifetimes {
{ // Use `'_` for both implicit and underscore lifetimes in
self.already_defined_lifetimes.insert(lifetime_name); // `abstract type Foo<'_>: SomeTrait<'_>;`
let name = hir::LifetimeName::Name(lifetime_name); hir::LifetimeName::Underscore
} else {
return
}
name @ hir::LifetimeName::Name(_) => name,
hir::LifetimeName::Static => return,
};
self.output_lifetimes.push(hir::Lifetime { if !self.currently_bound_lifetimes.contains(&name) &&
id: self.context.next_id().node_id, !self.already_defined_lifetimes.contains(&name)
span: lifetime.span, {
name, self.already_defined_lifetimes.insert(name);
});
let def_node_id = self.context.next_id().node_id; self.output_lifetimes.push(hir::Lifetime {
self.context.resolver.definitions().create_def_with_parent( id: self.context.next_id().node_id,
self.parent, span: lifetime.span,
def_node_id, name,
DefPathData::LifetimeDef(lifetime_name.as_str()), });
DefIndexAddressSpace::High,
Mark::root() let def_node_id = self.context.next_id().node_id;
); self.context.resolver.definitions().create_def_with_parent(
let def_lifetime = hir::Lifetime { self.parent,
id: def_node_id, def_node_id,
span: lifetime.span, DefPathData::LifetimeDef(name.name().as_str()),
name, DefIndexAddressSpace::High,
}; Mark::root()
self.output_lifetime_defs.push(hir::LifetimeDef { );
lifetime: def_lifetime, let def_lifetime = hir::Lifetime {
bounds: Vec::new().into(), id: def_node_id,
pure_wrt_drop: false, span: lifetime.span,
in_band: false, name: name,
}); };
} self.output_lifetime_defs.push(hir::LifetimeDef {
lifetime: def_lifetime,
bounds: Vec::new().into(),
pure_wrt_drop: false,
in_band: false,
});
} }
} }
} }
@ -1068,6 +1101,7 @@ impl<'a> LoweringContext<'a> {
let mut lifetime_collector = ImplTraitLifetimeCollector { let mut lifetime_collector = ImplTraitLifetimeCollector {
context: self, context: self,
parent: parent_index, parent: parent_index,
collect_elided_lifetimes: true,
currently_bound_lifetimes: Vec::new(), currently_bound_lifetimes: Vec::new(),
already_defined_lifetimes: HashSet::new(), already_defined_lifetimes: HashSet::new(),
output_lifetimes: Vec::new(), output_lifetimes: Vec::new(),

View file

@ -600,24 +600,45 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
} = *exist_ty; } = *exist_ty;
let mut index = self.next_early_index(); let mut index = self.next_early_index();
debug!("visit_ty: index = {}", index); debug!("visit_ty: index = {}", index);
let lifetimes = generics
.lifetimes let mut elision = None;
.iter() let mut lifetimes = FxHashMap();
.map(|lt_def| Region::early(&self.tcx.hir, &mut index, lt_def)) for lt_def in &generics.lifetimes {
.collect(); let (lt_name, region) = Region::early(&self.tcx.hir, &mut index, &lt_def);
if let hir::LifetimeName::Underscore = lt_name {
// Pick the elided lifetime "definition" if one exists and use it to make an
// elision scope.
elision = Some(region);
} else {
lifetimes.insert(lt_name, region);
}
}
let next_early_index = index + generics.ty_params.len() as u32; let next_early_index = index + generics.ty_params.len() as u32;
let scope = Scope::Binder {
lifetimes, if let Some(elision_region) = elision {
next_early_index, let scope = Scope::Elision {
s: self.scope, elide: Elide::Exact(elision_region),
}; s: self.scope
self.with(scope, |_old_scope, this| { };
this.visit_generics(generics); self.with(scope, |_old_scope, this| {
for bound in bounds { let scope = Scope::Binder { lifetimes, next_early_index, s: this.scope };
this.visit_ty_param_bound(bound); this.with(scope, |_old_scope, this| {
} this.visit_generics(generics);
}); for bound in bounds {
this.visit_ty_param_bound(bound);
}
});
});
} else {
let scope = Scope::Binder { lifetimes, next_early_index, s: self.scope };
self.with(scope, |_old_scope, this| {
this.visit_generics(generics);
for bound in bounds {
this.visit_ty_param_bound(bound);
}
});
}
} }
_ => intravisit::walk_ty(self, ty), _ => intravisit::walk_ty(self, ty),
} }

View file

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![feature(conservative_impl_trait)] #![feature(conservative_impl_trait, underscore_lifetimes, universal_impl_trait)]
#![allow(warnings)] #![allow(warnings)]
use std::fmt::Debug; use std::fmt::Debug;
@ -32,12 +32,36 @@ fn no_params_or_lifetimes_is_static() -> impl Debug + 'static {
fn static_input_type_is_static<T: Debug + 'static>(x: T) -> impl Debug + 'static { x } fn static_input_type_is_static<T: Debug + 'static>(x: T) -> impl Debug + 'static { x }
fn type_outlives_reference_lifetime<'a, T: Debug>(x: &'a T) -> impl Debug + 'a { x } fn type_outlives_reference_lifetime<'a, T: Debug>(x: &'a T) -> impl Debug + 'a { x }
fn type_outlives_reference_lifetime_elided<T: Debug>(x: &T) -> impl Debug + '_ { x }
trait SingleRegionTrait<'a> {} trait SingleRegionTrait<'a> {}
impl<'a> SingleRegionTrait<'a> for u32 {} impl<'a> SingleRegionTrait<'a> for u32 {}
impl<'a> SingleRegionTrait<'a> for &'a u32 {}
struct SingleRegionStruct<'a>(&'a u32);
fn simple_type_hrtb<'b>() -> impl for<'a> SingleRegionTrait<'a> { 5 } fn simple_type_hrtb<'b>() -> impl for<'a> SingleRegionTrait<'a> { 5 }
// FIXME(cramertj) add test after #45992 lands to ensure lint is triggered
fn elision_single_region_trait(x: &u32) -> impl SingleRegionTrait { x }
fn elision_single_region_struct(x: SingleRegionStruct) -> impl Into<SingleRegionStruct> { x }
fn closure_hrtb() -> impl for<'a> Fn(&'a u32) { |_| () } fn closure_hrtb() -> impl for<'a> Fn(&'a u32) { |_| () }
fn closure_hr_elided() -> impl Fn(&u32) { |_| () }
fn closure_hr_elided_return() -> impl Fn(&u32) -> &u32 { |x| x }
fn closure_pass_through_elided_return(x: impl Fn(&u32) -> &u32) -> impl Fn(&u32) -> &u32 { x }
fn closure_pass_through_reference_elided(x: &impl Fn(&u32) -> &u32) -> &impl Fn(&u32) -> &u32 { x }
fn pass_through_elision(x: &u32) -> impl Into<&u32> { x }
fn pass_through_elision_with_fn_ptr(x: &fn(&u32) -> &u32) -> impl Into<&fn(&u32) -> &u32> { x }
fn pass_through_elision_with_fn_path<T: Fn(&u32) -> &u32>(
x: &T
) -> impl Into<&impl Fn(&u32) -> &u32> { x }
// FIXME(cramertj) Currently ICEing, part of issue #46685:
// fn foo(x: &impl Debug) -> impl Into<&impl Debug> { x }
// Works:
fn foo_no_outer_impl(x: &impl Debug) -> &impl Debug { x }
fn foo_explicit_arg<T: Debug>(x: &T) -> impl Into<&impl Debug> { x }
fn mixed_lifetimes<'a>() -> impl for<'b: 'a> Fn(&'b u32) { |_| () } fn mixed_lifetimes<'a>() -> impl for<'b: 'a> Fn(&'b u32) { |_| () }
fn mixed_as_static() -> impl Fn(&'static u32) { mixed_lifetimes() } fn mixed_as_static() -> impl Fn(&'static u32) { mixed_lifetimes() }