Implement impl Trait lifetime elision
This commit is contained in:
parent
4c2819d326
commit
018c4038c7
3 changed files with 134 additions and 55 deletions
|
@ -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 <_def.bounds {
|
for lt_bound in <_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(),
|
||||||
|
|
|
@ -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, <_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),
|
||||||
}
|
}
|
||||||
|
|
|
@ -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() }
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue