Skip to content

Commit ebda766

Browse files
committed
Auto merge of #45701 - cramertj:impl-trait-this-time, r=eddyb
impl Trait Lifetime Handling This PR implements the updated strategy for handling `impl Trait` lifetimes, as described in [RFC 1951](https://github.com/rust-lang/rfcs/blob/master/text/1951-expand-impl-trait.md) (cc #42183). With this PR, the `impl Trait` desugaring works as follows: ```rust fn foo<T, 'a, 'b, 'c>(...) -> impl Foo<'a, 'b> { ... } // desugars to exists type MyFoo<ParentT, 'parent_a, 'parent_b, 'parent_c, 'a, 'b>: Foo<'a, 'b>; fn foo<T, 'a, 'b, 'c>(...) -> MyFoo<T, 'static, 'static, 'static, 'a, 'b> { ... } ``` All of the in-scope (parent) generics are listed as parent generics of the anonymous type, with parent regions being replaced by `'static`. Parent regions referenced in the `impl Trait` return type are duplicated into the anonymous type's generics and mapped appropriately. One case came up that wasn't specified in the RFC: it's possible to write a return type that contains multiple regions, neither of which outlives the other. In that case, it's not clear what the required lifetime of the output type should be, so we generate an error. There's one remaining FIXME in one of the tests: `-> impl Foo<'a, 'b> + 'c` should be able to outlive both `'a` and `'b`, but not `'c`. Currently, it can't outlive any of them. @nikomatsakis and I have discussed this, and there are some complex interactions here if we ever allow `impl<'a, 'b> SomeTrait for AnonType<'a, 'b> { ... }`, so the plan is to hold off on this until we've got a better idea of what the interactions are here. cc #34511. Fixes #44727.
2 parents bac7c53 + 450bbc5 commit ebda766

24 files changed

+884
-133
lines changed

src/librustc/diagnostics.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2019,4 +2019,5 @@ register_diagnostics! {
20192019
E0628, // generators cannot have explicit arguments
20202020
E0631, // type mismatch in closure arguments
20212021
E0637, // "'_" is not a valid lifetime bound
2022+
E0657, // `impl Trait` can only capture lifetimes bound at the fn level
20222023
}

src/librustc/hir/intravisit.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -591,8 +591,11 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty) {
591591
}
592592
visitor.visit_lifetime(lifetime);
593593
}
594-
TyImplTraitExistential(ref bounds) => {
594+
TyImplTraitExistential(ref existty, ref lifetimes) => {
595+
let ExistTy { ref generics, ref bounds } = *existty;
596+
walk_generics(visitor, generics);
595597
walk_list!(visitor, visit_ty_param_bound, bounds);
598+
walk_list!(visitor, visit_lifetime, lifetimes);
596599
}
597600
TyImplTraitUniversal(_, ref bounds) => {
598601
walk_list!(visitor, visit_ty_param_bound, bounds);

src/librustc/hir/lowering.rs

+127-4
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,9 @@
4242
4343
use dep_graph::DepGraph;
4444
use hir;
45-
use hir::map::{Definitions, DefKey};
46-
use hir::def_id::{DefIndex, DefId, CRATE_DEF_INDEX};
45+
use hir::HirVec;
46+
use hir::map::{Definitions, DefKey, DefPathData};
47+
use hir::def_id::{DefIndex, DefId, CRATE_DEF_INDEX, DefIndexAddressSpace};
4748
use hir::def::{Def, PathResolution};
4849
use lint::builtin::PARENTHESIZED_PARAMS_IN_TYPES_AND_MODULES;
4950
use middle::cstore::CrateStore;
@@ -52,7 +53,7 @@ use session::Session;
5253
use util::common::FN_OUTPUT_NAME;
5354
use util::nodemap::{DefIdMap, FxHashMap, NodeMap};
5455

55-
use std::collections::BTreeMap;
56+
use std::collections::{BTreeMap, HashSet};
5657
use std::fmt::Debug;
5758
use std::iter;
5859
use std::mem;
@@ -777,7 +778,24 @@ impl<'a> LoweringContext<'a> {
777778
t.span, GateIssue::Language,
778779
"`impl Trait` in return position is experimental");
779780
}
780-
hir::TyImplTraitExistential(self.lower_bounds(bounds, itctx))
781+
let def_index = self.resolver.definitions().opt_def_index(t.id).unwrap();
782+
let hir_bounds = self.lower_bounds(bounds, itctx);
783+
let (lifetimes, lifetime_defs) =
784+
self.lifetimes_from_impl_trait_bounds(def_index, &hir_bounds);
785+
786+
hir::TyImplTraitExistential(hir::ExistTy {
787+
generics: hir::Generics {
788+
lifetimes: lifetime_defs,
789+
// Type parameters are taken from environment:
790+
ty_params: Vec::new().into(),
791+
where_clause: hir::WhereClause {
792+
id: self.next_id().node_id,
793+
predicates: Vec::new().into(),
794+
},
795+
span: t.span,
796+
},
797+
bounds: hir_bounds,
798+
}, lifetimes)
781799
},
782800
ImplTraitContext::Universal(def_id) => {
783801
let has_feature = self.sess.features.borrow().universal_impl_trait;
@@ -808,6 +826,111 @@ impl<'a> LoweringContext<'a> {
808826
})
809827
}
810828

829+
fn lifetimes_from_impl_trait_bounds(
830+
&mut self,
831+
parent_index: DefIndex,
832+
bounds: &hir::TyParamBounds
833+
) -> (HirVec<hir::Lifetime>, HirVec<hir::LifetimeDef>) {
834+
835+
// This visitor walks over impl trait bounds and creates defs for all lifetimes which
836+
// appear in the bounds, excluding lifetimes that are created within the bounds.
837+
// e.g. 'a, 'b, but not 'c in `impl for<'c> SomeTrait<'a, 'b, 'c>`
838+
struct ImplTraitLifetimeCollector<'r, 'a: 'r> {
839+
context: &'r mut LoweringContext<'a>,
840+
parent: DefIndex,
841+
currently_bound_lifetimes: Vec<Name>,
842+
already_defined_lifetimes: HashSet<Name>,
843+
output_lifetimes: Vec<hir::Lifetime>,
844+
output_lifetime_defs: Vec<hir::LifetimeDef>,
845+
}
846+
847+
impl<'r, 'a: 'r, 'v> hir::intravisit::Visitor<'v> for ImplTraitLifetimeCollector<'r, 'a> {
848+
fn nested_visit_map<'this>(&'this mut self)
849+
-> hir::intravisit::NestedVisitorMap<'this, 'v> {
850+
hir::intravisit::NestedVisitorMap::None
851+
}
852+
853+
fn visit_poly_trait_ref(&mut self,
854+
polytr: &'v hir::PolyTraitRef,
855+
_: hir::TraitBoundModifier) {
856+
let old_len = self.currently_bound_lifetimes.len();
857+
858+
// Record the introduction of 'a in `for<'a> ...`
859+
for lt_def in &polytr.bound_lifetimes {
860+
// Introduce lifetimes one at a time so that we can handle
861+
// cases like `fn foo<'d>() -> impl for<'a, 'b: 'a, 'c: 'b + 'd> ...`
862+
if let hir::LifetimeName::Name(name) = lt_def.lifetime.name {
863+
self.currently_bound_lifetimes.push(name);
864+
}
865+
866+
// Visit the lifetime bounds
867+
for lt_bound in &lt_def.bounds {
868+
self.visit_lifetime(&lt_bound);
869+
}
870+
}
871+
872+
hir::intravisit::walk_trait_ref(self, &polytr.trait_ref);
873+
874+
self.currently_bound_lifetimes.truncate(old_len);
875+
}
876+
877+
fn visit_lifetime(&mut self, lifetime: &'v hir::Lifetime) {
878+
// Exclude '_, 'static, and elided lifetimes (there should be no elided lifetimes)
879+
if let hir::LifetimeName::Name(lifetime_name) = lifetime.name {
880+
if !self.currently_bound_lifetimes.contains(&lifetime_name) &&
881+
!self.already_defined_lifetimes.contains(&lifetime_name)
882+
{
883+
self.already_defined_lifetimes.insert(lifetime_name);
884+
let name = hir::LifetimeName::Name(lifetime_name);
885+
886+
self.output_lifetimes.push(hir::Lifetime {
887+
id: self.context.next_id().node_id,
888+
span: lifetime.span,
889+
name,
890+
});
891+
892+
let def_node_id = self.context.next_id().node_id;
893+
self.context.resolver.definitions().create_def_with_parent(
894+
self.parent,
895+
def_node_id,
896+
DefPathData::LifetimeDef(lifetime_name.as_str()),
897+
DefIndexAddressSpace::High,
898+
Mark::root()
899+
);
900+
let def_lifetime = hir::Lifetime {
901+
id: def_node_id,
902+
span: lifetime.span,
903+
name,
904+
};
905+
self.output_lifetime_defs.push(hir::LifetimeDef {
906+
lifetime: def_lifetime,
907+
bounds: Vec::new().into(),
908+
pure_wrt_drop: false,
909+
});
910+
}
911+
}
912+
}
913+
}
914+
915+
let mut lifetime_collector = ImplTraitLifetimeCollector {
916+
context: self,
917+
parent: parent_index,
918+
currently_bound_lifetimes: Vec::new(),
919+
already_defined_lifetimes: HashSet::new(),
920+
output_lifetimes: Vec::new(),
921+
output_lifetime_defs: Vec::new(),
922+
};
923+
924+
for bound in bounds {
925+
hir::intravisit::walk_ty_param_bound(&mut lifetime_collector, &bound);
926+
}
927+
928+
(
929+
lifetime_collector.output_lifetimes.into(),
930+
lifetime_collector.output_lifetime_defs.into()
931+
)
932+
}
933+
811934
fn lower_foreign_mod(&mut self, fm: &ForeignMod) -> hir::ForeignMod {
812935
hir::ForeignMod {
813936
abi: fm.abi,

src/librustc/hir/mod.rs

+16-1
Original file line numberDiff line numberDiff line change
@@ -1461,6 +1461,12 @@ pub struct BareFnTy {
14611461
pub arg_names: HirVec<Spanned<Name>>,
14621462
}
14631463

1464+
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
1465+
pub struct ExistTy {
1466+
pub generics: Generics,
1467+
pub bounds: TyParamBounds,
1468+
}
1469+
14641470
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
14651471
/// The different kinds of types recognized by the compiler
14661472
pub enum Ty_ {
@@ -1488,7 +1494,16 @@ pub enum Ty_ {
14881494
TyTraitObject(HirVec<PolyTraitRef>, Lifetime),
14891495
/// An exsitentially quantified (there exists a type satisfying) `impl
14901496
/// Bound1 + Bound2 + Bound3` type where `Bound` is a trait or a lifetime.
1491-
TyImplTraitExistential(TyParamBounds),
1497+
///
1498+
/// The `ExistTy` structure emulates an
1499+
/// `abstract type Foo<'a, 'b>: MyTrait<'a, 'b>;`.
1500+
///
1501+
/// The `HirVec<Lifetime>` is the list of lifetimes applied as parameters
1502+
/// to the `abstract type`, e.g. the `'c` and `'d` in `-> Foo<'c, 'd>`.
1503+
/// This list is only a list of lifetimes and not type parameters
1504+
/// because all in-scope type parameters are captured by `impl Trait`,
1505+
/// so they are resolved directly through the parent `Generics`.
1506+
TyImplTraitExistential(ExistTy, HirVec<Lifetime>),
14921507
/// An universally quantified (for all types satisfying) `impl
14931508
/// Bound1 + Bound2 + Bound3` type where `Bound` is a trait or a lifetime.
14941509
TyImplTraitUniversal(DefId, TyParamBounds),

src/librustc/hir/print.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -421,8 +421,10 @@ impl<'a> State<'a> {
421421
self.print_lifetime(lifetime)?;
422422
}
423423
}
424-
hir::TyImplTraitExistential(ref bounds) |
425-
hir::TyImplTraitUniversal(_, ref bounds) => {
424+
hir::TyImplTraitExistential(ref existty, ref _lifetimes) => {
425+
self.print_bounds("impl", &existty.bounds[..])?;
426+
}
427+
hir::TyImplTraitUniversal(_, ref bounds) => {
426428
self.print_bounds("impl", &bounds[..])?;
427429
}
428430
hir::TyArray(ref ty, v) => {

src/librustc/ich/impls_hir.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,11 @@ impl_stable_hash_for!(struct hir::BareFnTy {
295295
arg_names
296296
});
297297

298+
impl_stable_hash_for!(struct hir::ExistTy {
299+
generics,
300+
bounds
301+
});
302+
298303
impl_stable_hash_for!(enum hir::Ty_ {
299304
TySlice(t),
300305
TyArray(t, body_id),
@@ -305,7 +310,7 @@ impl_stable_hash_for!(enum hir::Ty_ {
305310
TyTup(ts),
306311
TyPath(qpath),
307312
TyTraitObject(trait_refs, lifetime),
308-
TyImplTraitExistential(bounds),
313+
TyImplTraitExistential(existty, lifetimes),
309314
TyImplTraitUniversal(def_id, bounds),
310315
TyTypeof(body_id),
311316
TyErr,

src/librustc/middle/free_region.rs

+23-3
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ impl<'a, 'gcx, 'tcx> RegionRelations<'a, 'gcx, 'tcx> {
8484
(&ty::ReFree(_), &ty::ReEarlyBound(_)) |
8585
(&ty::ReEarlyBound(_), &ty::ReFree(_)) |
8686
(&ty::ReFree(_), &ty::ReFree(_)) =>
87-
self.free_regions.relation.contains(&sub_region, &super_region),
87+
self.free_regions.sub_free_regions(&sub_region, &super_region),
8888

8989
_ =>
9090
false,
@@ -158,19 +158,39 @@ impl<'tcx> FreeRegionMap<'tcx> {
158158
}
159159
}
160160

161-
// Record that `'sup:'sub`. Or, put another way, `'sub <= 'sup`.
162-
// (with the exception that `'static: 'x` is not notable)
161+
/// Record that `'sup:'sub`. Or, put another way, `'sub <= 'sup`.
162+
/// (with the exception that `'static: 'x` is not notable)
163163
pub fn relate_regions(&mut self, sub: Region<'tcx>, sup: Region<'tcx>) {
164+
debug!("relate_regions(sub={:?}, sup={:?})", sub, sup);
164165
if (is_free(sub) || *sub == ty::ReStatic) && is_free(sup) {
165166
self.relation.add(sub, sup)
166167
}
167168
}
168169

170+
/// True if `r_a <= r_b` is known to hold. Both `r_a` and `r_b`
171+
/// must be free regions from the function header.
172+
pub fn sub_free_regions<'a, 'gcx>(&self,
173+
r_a: Region<'tcx>,
174+
r_b: Region<'tcx>)
175+
-> bool {
176+
debug!("sub_free_regions(r_a={:?}, r_b={:?})", r_a, r_b);
177+
assert!(is_free(r_a));
178+
assert!(is_free(r_b));
179+
let result = r_a == r_b || self.relation.contains(&r_a, &r_b);
180+
debug!("sub_free_regions: result={}", result);
181+
result
182+
}
183+
184+
/// Compute the least-upper-bound of two free regions. In some
185+
/// cases, this is more conservative than necessary, in order to
186+
/// avoid making arbitrary choices. See
187+
/// `TransitiveRelation::postdom_upper_bound` for more details.
169188
pub fn lub_free_regions<'a, 'gcx>(&self,
170189
tcx: TyCtxt<'a, 'gcx, 'tcx>,
171190
r_a: Region<'tcx>,
172191
r_b: Region<'tcx>)
173192
-> Region<'tcx> {
193+
debug!("lub_free_regions(r_a={:?}, r_b={:?})", r_a, r_b);
174194
assert!(is_free(r_a));
175195
assert!(is_free(r_b));
176196
let result = if r_a == r_b { r_a } else {

0 commit comments

Comments
 (0)