Skip to content

Commit bc4810d

Browse files
committed
Fix impl Trait Lifetime Handling
After this change, impl Trait existentials are desugared to a new `abstract type` definition paired with a set of lifetimes to apply. In-scope generics are included as parents of the `abstract type` generics. Parent regions are replaced with static, and parent regions referenced in the `impl Trait` type are duplicated at the end of the `abstract type`'s generics.
1 parent d0f8e29 commit bc4810d

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
@@ -1436,6 +1436,12 @@ pub struct BareFnTy {
14361436
pub arg_names: HirVec<Spanned<Name>>,
14371437
}
14381438

1439+
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
1440+
pub struct ExistTy {
1441+
pub generics: Generics,
1442+
pub bounds: TyParamBounds,
1443+
}
1444+
14391445
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
14401446
/// The different kinds of types recognized by the compiler
14411447
pub enum Ty_ {
@@ -1463,7 +1469,16 @@ pub enum Ty_ {
14631469
TyTraitObject(HirVec<PolyTraitRef>, Lifetime),
14641470
/// An exsitentially quantified (there exists a type satisfying) `impl
14651471
/// Bound1 + Bound2 + Bound3` type where `Bound` is a trait or a lifetime.
1466-
TyImplTraitExistential(TyParamBounds),
1472+
///
1473+
/// The `ExistTy` structure emulates an
1474+
/// `abstract type Foo<'a, 'b>: MyTrait<'a, 'b>;`.
1475+
///
1476+
/// The `HirVec<Lifetime>` is the list of lifetimes applied as parameters
1477+
/// to the `abstract type`, e.g. the `'c` and `'d` in `-> Foo<'c, 'd>`.
1478+
/// This list is only a list of lifetimes and not type parameters
1479+
/// because all in-scope type parameters are captured by `impl Trait`,
1480+
/// so they are resolved directly through the parent `Generics`.
1481+
TyImplTraitExistential(ExistTy, HirVec<Lifetime>),
14671482
/// An universally quantified (for all types satisfying) `impl
14681483
/// Bound1 + Bound2 + Bound3` type where `Bound` is a trait or a lifetime.
14691484
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
@@ -277,6 +277,11 @@ impl_stable_hash_for!(struct hir::BareFnTy {
277277
arg_names
278278
});
279279

280+
impl_stable_hash_for!(struct hir::ExistTy {
281+
generics,
282+
bounds
283+
});
284+
280285
impl_stable_hash_for!(enum hir::Ty_ {
281286
TySlice(t),
282287
TyArray(t, body_id),
@@ -287,7 +292,7 @@ impl_stable_hash_for!(enum hir::Ty_ {
287292
TyTup(ts),
288293
TyPath(qpath),
289294
TyTraitObject(trait_refs, lifetime),
290-
TyImplTraitExistential(bounds),
295+
TyImplTraitExistential(existty, lifetimes),
291296
TyImplTraitUniversal(def_id, bounds),
292297
TyTypeof(body_id),
293298
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)