Skip to content

Commit

Permalink
Fix impl Trait Lifetime Handling
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
cramertj committed Nov 17, 2017
1 parent d0f8e29 commit bd0f2c7
Show file tree
Hide file tree
Showing 24 changed files with 883 additions and 133 deletions.
1 change: 1 addition & 0 deletions src/librustc/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2019,4 +2019,5 @@ register_diagnostics! {
E0628, // generators cannot have explicit arguments
E0631, // type mismatch in closure arguments
E0637, // "'_" is not a valid lifetime bound
E0657, // `impl Trait` can only capture lifetimes bound at the fn level
}
5 changes: 4 additions & 1 deletion src/librustc/hir/intravisit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -591,8 +591,11 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty) {
}
visitor.visit_lifetime(lifetime);
}
TyImplTraitExistential(ref bounds) => {
TyImplTraitExistential(ref existty, ref lifetimes) => {
let ExistTy { ref generics, ref bounds } = *existty;
walk_generics(visitor, generics);
walk_list!(visitor, visit_ty_param_bound, bounds);
walk_list!(visitor, visit_lifetime, lifetimes);
}
TyImplTraitUniversal(_, ref bounds) => {
walk_list!(visitor, visit_ty_param_bound, bounds);
Expand Down
131 changes: 127 additions & 4 deletions src/librustc/hir/lowering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,9 @@
use dep_graph::DepGraph;
use hir;
use hir::map::{Definitions, DefKey};
use hir::def_id::{DefIndex, DefId, CRATE_DEF_INDEX};
use hir::HirVec;
use hir::map::{Definitions, DefKey, DefPathData};
use hir::def_id::{DefIndex, DefId, CRATE_DEF_INDEX, DefIndexAddressSpace};
use hir::def::{Def, PathResolution};
use lint::builtin::PARENTHESIZED_PARAMS_IN_TYPES_AND_MODULES;
use middle::cstore::CrateStore;
Expand All @@ -52,7 +53,7 @@ use session::Session;
use util::common::FN_OUTPUT_NAME;
use util::nodemap::{DefIdMap, FxHashMap, NodeMap};

use std::collections::BTreeMap;
use std::collections::{BTreeMap, HashSet};
use std::fmt::Debug;
use std::iter;
use std::mem;
Expand Down Expand Up @@ -777,7 +778,24 @@ impl<'a> LoweringContext<'a> {
t.span, GateIssue::Language,
"`impl Trait` in return position is experimental");
}
hir::TyImplTraitExistential(self.lower_bounds(bounds, itctx))
let def_index = self.resolver.definitions().opt_def_index(t.id).unwrap();
let hir_bounds = self.lower_bounds(bounds, itctx);
let (lifetimes, lifetime_defs) =
self.lifetimes_from_impl_trait_bounds(def_index, &hir_bounds);

hir::TyImplTraitExistential(hir::ExistTy {
generics: hir::Generics {
lifetimes: lifetime_defs,
// Type parameters are taken from environment:
ty_params: Vec::new().into(),
where_clause: hir::WhereClause {
id: self.next_id().node_id,
predicates: Vec::new().into(),
},
span: t.span,
},
bounds: hir_bounds,
}, lifetimes)
},
ImplTraitContext::Universal(def_id) => {
let has_feature = self.sess.features.borrow().universal_impl_trait;
Expand Down Expand Up @@ -808,6 +826,111 @@ impl<'a> LoweringContext<'a> {
})
}

fn lifetimes_from_impl_trait_bounds(
&mut self,
parent_index: DefIndex,
bounds: &hir::TyParamBounds
) -> (HirVec<hir::Lifetime>, HirVec<hir::LifetimeDef>) {

// This visitor walks over impl trait bounds and creates defs for all lifetimes which
// appear in the bounds, excluding lifetimes that are created within the bounds.
// e.g. 'a, 'b, but not 'c in `impl for<'c> SomeTrait<'a, 'b, 'c>`
struct ImplTraitLifetimeCollector<'r, 'a: 'r> {
context: &'r mut LoweringContext<'a>,
parent: DefIndex,
currently_bound_lifetimes: Vec<Name>,
already_defined_lifetimes: HashSet<Name>,
output_lifetimes: Vec<hir::Lifetime>,
output_lifetime_defs: Vec<hir::LifetimeDef>,
}

impl<'r, 'a: 'r, 'v> hir::intravisit::Visitor<'v> for ImplTraitLifetimeCollector<'r, 'a> {
fn nested_visit_map<'this>(&'this mut self)
-> hir::intravisit::NestedVisitorMap<'this, 'v> {
hir::intravisit::NestedVisitorMap::None
}

fn visit_poly_trait_ref(&mut self,
polytr: &'v hir::PolyTraitRef,
_: hir::TraitBoundModifier) {
let old_len = self.currently_bound_lifetimes.len();

// Record the introduction of 'a in `for<'a> ...`
for lt_def in &polytr.bound_lifetimes {
// Introduce lifetimes one at a time so that we can handle
// 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(name);
}

// Visit the lifetime bounds
for lt_bound in &lt_def.bounds {
self.visit_lifetime(&lt_bound);
}
}

hir::intravisit::walk_trait_ref(self, &polytr.trait_ref);

self.currently_bound_lifetimes.truncate(old_len);
}

fn visit_lifetime(&mut self, lifetime: &'v hir::Lifetime) {
// Exclude '_, 'static, and elided lifetimes (there should be no elided lifetimes)
if let hir::LifetimeName::Name(lifetime_name) = lifetime.name {
if !self.currently_bound_lifetimes.contains(&lifetime_name) &&
!self.already_defined_lifetimes.contains(&lifetime_name)
{
self.already_defined_lifetimes.insert(lifetime_name);
let name = hir::LifetimeName::Name(lifetime_name);

self.output_lifetimes.push(hir::Lifetime {
id: self.context.next_id().node_id,
span: lifetime.span,
name,
});

let def_node_id = self.context.next_id().node_id;
self.context.resolver.definitions().create_def_with_parent(
self.parent,
def_node_id,
DefPathData::LifetimeDef(lifetime_name.as_str()),
DefIndexAddressSpace::High,
Mark::root()
);
let def_lifetime = hir::Lifetime {
id: def_node_id,
span: lifetime.span,
name,
};
self.output_lifetime_defs.push(hir::LifetimeDef {
lifetime: def_lifetime,
bounds: Vec::new().into(),
pure_wrt_drop: false,
});
}
}
}
}

let mut lifetime_collector = ImplTraitLifetimeCollector {
context: self,
parent: parent_index,
currently_bound_lifetimes: Vec::new(),
already_defined_lifetimes: HashSet::new(),
output_lifetimes: Vec::new(),
output_lifetime_defs: Vec::new(),
};

for bound in bounds {
hir::intravisit::walk_ty_param_bound(&mut lifetime_collector, &bound);
}

(
lifetime_collector.output_lifetimes.into(),
lifetime_collector.output_lifetime_defs.into()
)
}

fn lower_foreign_mod(&mut self, fm: &ForeignMod) -> hir::ForeignMod {
hir::ForeignMod {
abi: fm.abi,
Expand Down
17 changes: 16 additions & 1 deletion src/librustc/hir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1436,6 +1436,12 @@ pub struct BareFnTy {
pub arg_names: HirVec<Spanned<Name>>,
}

#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
pub struct ExistTy {
pub generics: Generics,
pub bounds: TyParamBounds,
}

#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
/// The different kinds of types recognized by the compiler
pub enum Ty_ {
Expand Down Expand Up @@ -1463,7 +1469,16 @@ pub enum Ty_ {
TyTraitObject(HirVec<PolyTraitRef>, Lifetime),
/// An exsitentially quantified (there exists a type satisfying) `impl
/// Bound1 + Bound2 + Bound3` type where `Bound` is a trait or a lifetime.
TyImplTraitExistential(TyParamBounds),
///
/// The `ExistTy` structure emulates an
/// `abstract type Foo<'a, 'b>: MyTrait<'a, 'b>;`.
///
/// The `HirVec<Lifetime>` is the list of lifetimes applied as parameters
/// to the `abstract type`, e.g. the `'c` and `'d` in `-> Foo<'c, 'd>`.
/// This list is only a list of lifetimes and not type parameters
/// because all in-scope type parameters are captured by `impl Trait`,
/// so they are resolved directly through the parent `Generics`.
TyImplTraitExistential(ExistTy, HirVec<Lifetime>),
/// An universally quantified (for all types satisfying) `impl
/// Bound1 + Bound2 + Bound3` type where `Bound` is a trait or a lifetime.
TyImplTraitUniversal(DefId, TyParamBounds),
Expand Down
6 changes: 4 additions & 2 deletions src/librustc/hir/print.rs
Original file line number Diff line number Diff line change
Expand Up @@ -421,8 +421,10 @@ impl<'a> State<'a> {
self.print_lifetime(lifetime)?;
}
}
hir::TyImplTraitExistential(ref bounds) |
hir::TyImplTraitUniversal(_, ref bounds) => {
hir::TyImplTraitExistential(ref existty, ref _lifetimes) => {
self.print_bounds("impl", &existty.bounds[..])?;
}
hir::TyImplTraitUniversal(_, ref bounds) => {
self.print_bounds("impl", &bounds[..])?;
}
hir::TyArray(ref ty, v) => {
Expand Down
7 changes: 6 additions & 1 deletion src/librustc/ich/impls_hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,11 @@ impl_stable_hash_for!(struct hir::BareFnTy {
arg_names
});

impl_stable_hash_for!(struct hir::ExistTy {
generics,
bounds
});

impl_stable_hash_for!(enum hir::Ty_ {
TySlice(t),
TyArray(t, body_id),
Expand All @@ -287,7 +292,7 @@ impl_stable_hash_for!(enum hir::Ty_ {
TyTup(ts),
TyPath(qpath),
TyTraitObject(trait_refs, lifetime),
TyImplTraitExistential(bounds),
TyImplTraitExistential(existty, lifetimes),
TyImplTraitUniversal(def_id, bounds),
TyTypeof(body_id),
TyErr,
Expand Down
26 changes: 23 additions & 3 deletions src/librustc/middle/free_region.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ impl<'a, 'gcx, 'tcx> RegionRelations<'a, 'gcx, 'tcx> {
(&ty::ReFree(_), &ty::ReEarlyBound(_)) |
(&ty::ReEarlyBound(_), &ty::ReFree(_)) |
(&ty::ReFree(_), &ty::ReFree(_)) =>
self.free_regions.relation.contains(&sub_region, &super_region),
self.free_regions.sub_free_regions(&sub_region, &super_region),

_ =>
false,
Expand Down Expand Up @@ -158,19 +158,39 @@ impl<'tcx> FreeRegionMap<'tcx> {
}
}

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

/// True if `r_a <= r_b` is known to hold. Both `r_a` and `r_b`
/// must be free regions from the function header.
pub fn sub_free_regions<'a, 'gcx>(&self,
r_a: Region<'tcx>,
r_b: Region<'tcx>)
-> bool {
debug!("sub_free_regions(r_a={:?}, r_b={:?})", r_a, r_b);
assert!(is_free(r_a));
assert!(is_free(r_b));
let result = r_a == r_b || self.relation.contains(&r_a, &r_b);
debug!("sub_free_regions: result={}", result);
result
}

/// Compute the least-upper-bound of two free regions. In some
/// cases, this is more conservative than necessary, in order to
/// avoid making arbitrary choices. See
/// `TransitiveRelation::postdom_upper_bound` for more details.
pub fn lub_free_regions<'a, 'gcx>(&self,
tcx: TyCtxt<'a, 'gcx, 'tcx>,
r_a: Region<'tcx>,
r_b: Region<'tcx>)
-> Region<'tcx> {
debug!("lub_free_regions(r_a={:?}, r_b={:?})", r_a, r_b);
assert!(is_free(r_a));
assert!(is_free(r_b));
let result = if r_a == r_b { r_a } else {
Expand Down
Loading

0 comments on commit bd0f2c7

Please sign in to comment.