Skip to content

Commit dcf3db4

Browse files
committed
Auto merge of #46616 - cramertj:impl-trait-elision, r=nikomatsakis
Implement impl Trait lifetime elision Fixes #43396. There's one weird ICE in the interaction with argument-position `impl Trait`. I'm still debugging it-- I've left a test for it commented out with a FIXME. Also included a FIXME to ensure that `impl Trait` traits are caught under the lint in #45992. r? @nikomatsakis
2 parents 691f022 + 018c403 commit dcf3db4

File tree

3 files changed

+135
-56
lines changed

3 files changed

+135
-56
lines changed

src/librustc/hir/lowering.rs

+73-39
Original file line numberDiff line numberDiff line change
@@ -990,8 +990,9 @@ impl<'a> LoweringContext<'a> {
990990
struct ImplTraitLifetimeCollector<'r, 'a: 'r> {
991991
context: &'r mut LoweringContext<'a>,
992992
parent: DefIndex,
993-
currently_bound_lifetimes: Vec<Name>,
994-
already_defined_lifetimes: HashSet<Name>,
993+
collect_elided_lifetimes: bool,
994+
currently_bound_lifetimes: Vec<hir::LifetimeName>,
995+
already_defined_lifetimes: HashSet<hir::LifetimeName>,
995996
output_lifetimes: Vec<hir::Lifetime>,
996997
output_lifetime_defs: Vec<hir::LifetimeDef>,
997998
}
@@ -1002,6 +1003,30 @@ impl<'a> LoweringContext<'a> {
10021003
hir::intravisit::NestedVisitorMap::None
10031004
}
10041005

1006+
fn visit_path_parameters(&mut self, span: Span, parameters: &'v hir::PathParameters) {
1007+
// Don't collect elided lifetimes used inside of `Fn()` syntax.
1008+
if parameters.parenthesized {
1009+
let old_collect_elided_lifetimes = self.collect_elided_lifetimes;
1010+
self.collect_elided_lifetimes = false;
1011+
hir::intravisit::walk_path_parameters(self, span, parameters);
1012+
self.collect_elided_lifetimes = old_collect_elided_lifetimes;
1013+
} else {
1014+
hir::intravisit::walk_path_parameters(self, span, parameters);
1015+
}
1016+
}
1017+
1018+
fn visit_ty(&mut self, t: &'v hir::Ty) {
1019+
// Don't collect elided lifetimes used inside of `fn()` syntax
1020+
if let &hir::Ty_::TyBareFn(_) = &t.node {
1021+
let old_collect_elided_lifetimes = self.collect_elided_lifetimes;
1022+
self.collect_elided_lifetimes = false;
1023+
hir::intravisit::walk_ty(self, t);
1024+
self.collect_elided_lifetimes = old_collect_elided_lifetimes;
1025+
} else {
1026+
hir::intravisit::walk_ty(self, t);
1027+
}
1028+
}
1029+
10051030
fn visit_poly_trait_ref(&mut self,
10061031
polytr: &'v hir::PolyTraitRef,
10071032
_: hir::TraitBoundModifier) {
@@ -1010,10 +1035,8 @@ impl<'a> LoweringContext<'a> {
10101035
// Record the introduction of 'a in `for<'a> ...`
10111036
for lt_def in &polytr.bound_lifetimes {
10121037
// Introduce lifetimes one at a time so that we can handle
1013-
// cases like `fn foo<'d>() -> impl for<'a, 'b: 'a, 'c: 'b + 'd> ...`
1014-
if let hir::LifetimeName::Name(name) = lt_def.lifetime.name {
1015-
self.currently_bound_lifetimes.push(name);
1016-
}
1038+
// cases like `fn foo<'d>() -> impl for<'a, 'b: 'a, 'c: 'b + 'd>`
1039+
self.currently_bound_lifetimes.push(lt_def.lifetime.name);
10171040

10181041
// Visit the lifetime bounds
10191042
for lt_bound in &lt_def.bounds {
@@ -1027,47 +1050,58 @@ impl<'a> LoweringContext<'a> {
10271050
}
10281051

10291052
fn visit_lifetime(&mut self, lifetime: &'v hir::Lifetime) {
1030-
// Exclude '_, 'static, and elided lifetimes (there should be no elided lifetimes)
1031-
if let hir::LifetimeName::Name(lifetime_name) = lifetime.name {
1032-
if !self.currently_bound_lifetimes.contains(&lifetime_name) &&
1033-
!self.already_defined_lifetimes.contains(&lifetime_name)
1034-
{
1035-
self.already_defined_lifetimes.insert(lifetime_name);
1036-
let name = hir::LifetimeName::Name(lifetime_name);
1037-
1038-
self.output_lifetimes.push(hir::Lifetime {
1039-
id: self.context.next_id().node_id,
1040-
span: lifetime.span,
1041-
name,
1042-
});
1053+
let name = match lifetime.name {
1054+
hir::LifetimeName::Implicit |
1055+
hir::LifetimeName::Underscore =>
1056+
if self.collect_elided_lifetimes {
1057+
// Use `'_` for both implicit and underscore lifetimes in
1058+
// `abstract type Foo<'_>: SomeTrait<'_>;`
1059+
hir::LifetimeName::Underscore
1060+
} else {
1061+
return
1062+
}
1063+
name @ hir::LifetimeName::Name(_) => name,
1064+
hir::LifetimeName::Static => return,
1065+
};
10431066

1044-
let def_node_id = self.context.next_id().node_id;
1045-
self.context.resolver.definitions().create_def_with_parent(
1046-
self.parent,
1047-
def_node_id,
1048-
DefPathData::LifetimeDef(lifetime_name.as_str()),
1049-
DefIndexAddressSpace::High,
1050-
Mark::root()
1051-
);
1052-
let def_lifetime = hir::Lifetime {
1053-
id: def_node_id,
1054-
span: lifetime.span,
1055-
name,
1056-
};
1057-
self.output_lifetime_defs.push(hir::LifetimeDef {
1058-
lifetime: def_lifetime,
1059-
bounds: Vec::new().into(),
1060-
pure_wrt_drop: false,
1061-
in_band: false,
1062-
});
1063-
}
1067+
if !self.currently_bound_lifetimes.contains(&name) &&
1068+
!self.already_defined_lifetimes.contains(&name)
1069+
{
1070+
self.already_defined_lifetimes.insert(name);
1071+
1072+
self.output_lifetimes.push(hir::Lifetime {
1073+
id: self.context.next_id().node_id,
1074+
span: lifetime.span,
1075+
name,
1076+
});
1077+
1078+
let def_node_id = self.context.next_id().node_id;
1079+
self.context.resolver.definitions().create_def_with_parent(
1080+
self.parent,
1081+
def_node_id,
1082+
DefPathData::LifetimeDef(name.name().as_str()),
1083+
DefIndexAddressSpace::High,
1084+
Mark::root()
1085+
);
1086+
let def_lifetime = hir::Lifetime {
1087+
id: def_node_id,
1088+
span: lifetime.span,
1089+
name: name,
1090+
};
1091+
self.output_lifetime_defs.push(hir::LifetimeDef {
1092+
lifetime: def_lifetime,
1093+
bounds: Vec::new().into(),
1094+
pure_wrt_drop: false,
1095+
in_band: false,
1096+
});
10641097
}
10651098
}
10661099
}
10671100

10681101
let mut lifetime_collector = ImplTraitLifetimeCollector {
10691102
context: self,
10701103
parent: parent_index,
1104+
collect_elided_lifetimes: true,
10711105
currently_bound_lifetimes: Vec::new(),
10721106
already_defined_lifetimes: HashSet::new(),
10731107
output_lifetimes: Vec::new(),

src/librustc/middle/resolve_lifetime.rs

+37-16
Original file line numberDiff line numberDiff line change
@@ -600,24 +600,45 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
600600
} = *exist_ty;
601601
let mut index = self.next_early_index();
602602
debug!("visit_ty: index = {}", index);
603-
let lifetimes = generics
604-
.lifetimes
605-
.iter()
606-
.map(|lt_def| Region::early(&self.tcx.hir, &mut index, lt_def))
607-
.collect();
608603

609-
let next_early_index = index + generics.ty_params.len() as u32;
610-
let scope = Scope::Binder {
611-
lifetimes,
612-
next_early_index,
613-
s: self.scope,
614-
};
615-
self.with(scope, |_old_scope, this| {
616-
this.visit_generics(generics);
617-
for bound in bounds {
618-
this.visit_ty_param_bound(bound);
604+
let mut elision = None;
605+
let mut lifetimes = FxHashMap();
606+
for lt_def in &generics.lifetimes {
607+
let (lt_name, region) = Region::early(&self.tcx.hir, &mut index, &lt_def);
608+
if let hir::LifetimeName::Underscore = lt_name {
609+
// Pick the elided lifetime "definition" if one exists and use it to make an
610+
// elision scope.
611+
elision = Some(region);
612+
} else {
613+
lifetimes.insert(lt_name, region);
619614
}
620-
});
615+
}
616+
617+
let next_early_index = index + generics.ty_params.len() as u32;
618+
619+
if let Some(elision_region) = elision {
620+
let scope = Scope::Elision {
621+
elide: Elide::Exact(elision_region),
622+
s: self.scope
623+
};
624+
self.with(scope, |_old_scope, this| {
625+
let scope = Scope::Binder { lifetimes, next_early_index, s: this.scope };
626+
this.with(scope, |_old_scope, this| {
627+
this.visit_generics(generics);
628+
for bound in bounds {
629+
this.visit_ty_param_bound(bound);
630+
}
631+
});
632+
});
633+
} else {
634+
let scope = Scope::Binder { lifetimes, next_early_index, s: self.scope };
635+
self.with(scope, |_old_scope, this| {
636+
this.visit_generics(generics);
637+
for bound in bounds {
638+
this.visit_ty_param_bound(bound);
639+
}
640+
});
641+
}
621642
}
622643
_ => intravisit::walk_ty(self, ty),
623644
}

src/test/run-pass/impl-trait/lifetimes.rs

+25-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
#![feature(conservative_impl_trait)]
11+
#![feature(conservative_impl_trait, underscore_lifetimes, universal_impl_trait)]
1212
#![allow(warnings)]
1313

1414
use std::fmt::Debug;
@@ -32,12 +32,36 @@ fn no_params_or_lifetimes_is_static() -> impl Debug + 'static {
3232
fn static_input_type_is_static<T: Debug + 'static>(x: T) -> impl Debug + 'static { x }
3333

3434
fn type_outlives_reference_lifetime<'a, T: Debug>(x: &'a T) -> impl Debug + 'a { x }
35+
fn type_outlives_reference_lifetime_elided<T: Debug>(x: &T) -> impl Debug + '_ { x }
3536

3637
trait SingleRegionTrait<'a> {}
3738
impl<'a> SingleRegionTrait<'a> for u32 {}
39+
impl<'a> SingleRegionTrait<'a> for &'a u32 {}
40+
struct SingleRegionStruct<'a>(&'a u32);
3841

3942
fn simple_type_hrtb<'b>() -> impl for<'a> SingleRegionTrait<'a> { 5 }
43+
// FIXME(cramertj) add test after #45992 lands to ensure lint is triggered
44+
fn elision_single_region_trait(x: &u32) -> impl SingleRegionTrait { x }
45+
fn elision_single_region_struct(x: SingleRegionStruct) -> impl Into<SingleRegionStruct> { x }
46+
4047
fn closure_hrtb() -> impl for<'a> Fn(&'a u32) { |_| () }
48+
fn closure_hr_elided() -> impl Fn(&u32) { |_| () }
49+
fn closure_hr_elided_return() -> impl Fn(&u32) -> &u32 { |x| x }
50+
fn closure_pass_through_elided_return(x: impl Fn(&u32) -> &u32) -> impl Fn(&u32) -> &u32 { x }
51+
fn closure_pass_through_reference_elided(x: &impl Fn(&u32) -> &u32) -> &impl Fn(&u32) -> &u32 { x }
52+
53+
fn pass_through_elision(x: &u32) -> impl Into<&u32> { x }
54+
fn pass_through_elision_with_fn_ptr(x: &fn(&u32) -> &u32) -> impl Into<&fn(&u32) -> &u32> { x }
55+
56+
fn pass_through_elision_with_fn_path<T: Fn(&u32) -> &u32>(
57+
x: &T
58+
) -> impl Into<&impl Fn(&u32) -> &u32> { x }
59+
60+
// FIXME(cramertj) Currently ICEing, part of issue #46685:
61+
// fn foo(x: &impl Debug) -> impl Into<&impl Debug> { x }
62+
// Works:
63+
fn foo_no_outer_impl(x: &impl Debug) -> &impl Debug { x }
64+
fn foo_explicit_arg<T: Debug>(x: &T) -> impl Into<&impl Debug> { x }
4165

4266
fn mixed_lifetimes<'a>() -> impl for<'b: 'a> Fn(&'b u32) { |_| () }
4367
fn mixed_as_static() -> impl Fn(&'static u32) { mixed_lifetimes() }

0 commit comments

Comments
 (0)