Skip to content

Commit

Permalink
Add existential type definitons
Browse files Browse the repository at this point in the history
  • Loading branch information
oli-obk committed Jun 7, 2018
1 parent c131bdc commit 9b1bd94
Show file tree
Hide file tree
Showing 44 changed files with 582 additions and 287 deletions.
3 changes: 3 additions & 0 deletions src/librustc/hir/def.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ pub enum Def {
Enum(DefId),
Variant(DefId),
Trait(DefId),
Existential(DefId),
TyAlias(DefId),
TyForeign(DefId),
TraitAlias(DefId),
Expand Down Expand Up @@ -162,6 +163,7 @@ impl Def {
Def::AssociatedTy(id) | Def::TyParam(id) | Def::Struct(id) | Def::StructCtor(id, ..) |
Def::Union(id) | Def::Trait(id) | Def::Method(id) | Def::Const(id) |
Def::AssociatedConst(id) | Def::Macro(id, ..) |
Def::Existential(id) |
Def::GlobalAsm(id) | Def::TyForeign(id) => {
id
}
Expand All @@ -188,6 +190,7 @@ impl Def {
Def::VariantCtor(.., CtorKind::Const) => "unit variant",
Def::VariantCtor(.., CtorKind::Fictive) => "struct variant",
Def::Enum(..) => "enum",
Def::Existential(..) => "existential type",
Def::TyAlias(..) => "type alias",
Def::TraitAlias(..) => "trait alias",
Def::AssociatedTy(..) => "associated type",
Expand Down
15 changes: 11 additions & 4 deletions src/librustc/hir/intravisit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,14 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item) {
visitor.visit_ty(typ);
visitor.visit_generics(type_parameters)
}
ItemExistential(ExistTy {ref generics, ref bounds, impl_trait_fn}) => {
visitor.visit_id(item.id);
walk_generics(visitor, generics);
walk_list!(visitor, visit_ty_param_bound, bounds);
if let Some(impl_trait_fn) = impl_trait_fn {
visitor.visit_def_mention(Def::Fn(impl_trait_fn))
}
}
ItemEnum(ref enum_definition, ref type_parameters) => {
visitor.visit_generics(type_parameters);
// visit_enum_def() takes care of visiting the Item's NodeId
Expand Down Expand Up @@ -596,10 +604,9 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty) {
}
visitor.visit_lifetime(lifetime);
}
TyImplTraitExistential(ref existty, ref lifetimes) => {
let ExistTy { ref generics, ref bounds } = *existty;
walk_generics(visitor, generics);
walk_list!(visitor, visit_ty_param_bound, bounds);
TyImplTraitExistential(item_id, def_id, ref lifetimes) => {
visitor.visit_def_mention(Def::Existential(def_id));
visitor.visit_nested_item(item_id);
walk_list!(visitor, visit_lifetime, lifetimes);
}
TyTypeof(ref expression) => {
Expand Down
130 changes: 108 additions & 22 deletions src/librustc/hir/lowering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,9 @@ enum ImplTraitContext {
/// Treat `impl Trait` as shorthand for a new universal existential parameter.
/// Example: `fn foo() -> impl Debug`, where `impl Debug` is conceptually
/// equivalent to a fresh existential parameter like `abstract type T; fn foo() -> T`.
Existential,
///
/// We store a DefId here so we can look up necessary information later
Existential(DefId),

/// `impl Trait` is not accepted in this position.
Disallowed,
Expand Down Expand Up @@ -235,6 +237,7 @@ enum ParamMode {
Optional,
}

#[derive(Debug)]
struct LoweredNodeId {
node_id: NodeId,
hir_id: hir::HirId,
Expand Down Expand Up @@ -485,16 +488,16 @@ impl<'a> LoweringContext<'a> {
}
}

fn with_hir_id_owner<F>(&mut self, owner: NodeId, f: F)
fn with_hir_id_owner<F, T>(&mut self, owner: NodeId, f: F) -> T
where
F: FnOnce(&mut Self),
F: FnOnce(&mut Self) -> T,
{
let counter = self.item_local_id_counters
.insert(owner, HIR_ID_COUNTER_LOCKED)
.unwrap();
let def_index = self.resolver.definitions().opt_def_index(owner).unwrap();
self.current_hir_id_owner.push((def_index, counter));
f(self);
let ret = f(self);
let (new_def_index, new_counter) = self.current_hir_id_owner.pop().unwrap();

debug_assert!(def_index == new_def_index);
Expand All @@ -504,6 +507,7 @@ impl<'a> LoweringContext<'a> {
.insert(owner, new_counter)
.unwrap();
debug_assert!(prev == HIR_ID_COUNTER_LOCKED);
ret
}

/// This method allocates a new HirId for the given NodeId and stores it in
Expand All @@ -527,7 +531,10 @@ impl<'a> LoweringContext<'a> {

fn lower_node_id_with_owner(&mut self, ast_node_id: NodeId, owner: NodeId) -> LoweredNodeId {
self.lower_node_id_generic(ast_node_id, |this| {
let local_id_counter = this.item_local_id_counters.get_mut(&owner).unwrap();
let local_id_counter = this
.item_local_id_counters
.get_mut(&owner)
.expect("called lower_node_id_with_owner before allocate_hir_id_counter");
let local_id = *local_id_counter;

// We want to be sure not to modify the counter in the map while it
Expand All @@ -536,7 +543,12 @@ impl<'a> LoweringContext<'a> {
debug_assert!(local_id != HIR_ID_COUNTER_LOCKED);

*local_id_counter += 1;
let def_index = this.resolver.definitions().opt_def_index(owner).unwrap();
let def_index = this
.resolver
.definitions()
.opt_def_index(owner)
.expect("You forgot to call `create_def_with_parent` or are lowering node ids \
that do not belong to the current owner");

hir::HirId {
owner: def_index,
Expand Down Expand Up @@ -1108,26 +1120,93 @@ impl<'a> LoweringContext<'a> {
TyKind::ImplTrait(ref bounds) => {
let span = t.span;
match itctx {
ImplTraitContext::Existential => {
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);
ImplTraitContext::Existential(fn_def_id) => {

// We need to manually repeat the code of `next_id` because the lowering
// needs to happen while the owner_id is pointing to the item itself,
// because items are their own owners
let exist_ty_node_id = self.sess.next_node_id();

// Make sure we know that some funky desugaring has been going on here.
// This is a first: there is code in other places like for loop
// desugaring that explicitly states that we don't want to track that.
// Not tracking it makes lints in rustc and clippy very fragile as
// frequently opened issues show.
let exist_ty_span = self.allow_internal_unstable(
CompilerDesugaringKind::ExistentialReturnType,
t.span,
);

hir::TyImplTraitExistential(
hir::ExistTy {
// Pull a new definition from the ether
let exist_ty_def_index = self
.resolver
.definitions()
.create_def_with_parent(
fn_def_id.index,
exist_ty_node_id,
DefPathData::ExistentialImplTrait,
DefIndexAddressSpace::High,
Mark::root(),
exist_ty_span,
);

// the `t` is just for printing debug messages
self.allocate_hir_id_counter(exist_ty_node_id, t);

let hir_bounds = self.with_hir_id_owner(exist_ty_node_id, |lctx| {
lctx.lower_bounds(bounds, itctx)
});

let (lifetimes, lifetime_defs) = self.lifetimes_from_impl_trait_bounds(
exist_ty_node_id,
exist_ty_def_index,
&hir_bounds,
);

self.with_hir_id_owner(exist_ty_node_id, |lctx| {
let exist_ty_item_kind = hir::ItemExistential(hir::ExistTy {
generics: hir::Generics {
params: lifetime_defs,
where_clause: hir::WhereClause {
id: self.next_id().node_id,
id: lctx.next_id().node_id,
predicates: Vec::new().into(),
},
span,
},
bounds: hir_bounds,
},
lifetimes,
)
impl_trait_fn: Some(fn_def_id),
});
let exist_ty_id = lctx.lower_node_id(exist_ty_node_id);
// Generate an `existential type Foo: Trait;` declaration
trace!("creating existential type with id {:#?}", exist_ty_id);
// Set the name to `impl Bound1 + Bound2`
let exist_ty_name = Symbol::intern(&pprust::ty_to_string(t));

trace!("exist ty def index: {:#?}", exist_ty_def_index);
let exist_ty_item = hir::Item {
id: exist_ty_id.node_id,
hir_id: exist_ty_id.hir_id,
name: exist_ty_name,
attrs: Default::default(),
node: exist_ty_item_kind,
vis: hir::Visibility::Inherited,
span: exist_ty_span,
};

// Insert the item into the global list. This usually happens
// automatically for all AST items. But this existential type item
// does not actually exist in the AST.
lctx.items.insert(exist_ty_id.node_id, exist_ty_item);

// `impl Trait` now just becomes `Foo<'a, 'b, ..>`
hir::TyImplTraitExistential(
hir::ItemId {
id: exist_ty_id.node_id
},
DefId::local(exist_ty_def_index),
lifetimes,
)
})
}
ImplTraitContext::Universal(def_id) => {
let def_node_id = self.next_id().node_id;
Expand All @@ -1136,7 +1215,7 @@ impl<'a> LoweringContext<'a> {
let def_index = self.resolver.definitions().create_def_with_parent(
def_id.index,
def_node_id,
DefPathData::ImplTrait,
DefPathData::UniversalImplTrait,
DefIndexAddressSpace::High,
Mark::root(),
span,
Expand Down Expand Up @@ -1191,6 +1270,7 @@ impl<'a> LoweringContext<'a> {

fn lifetimes_from_impl_trait_bounds(
&mut self,
exist_ty_id: NodeId,
parent_index: DefIndex,
bounds: &hir::TyParamBounds,
) -> (HirVec<hir::Lifetime>, HirVec<hir::GenericParam>) {
Expand All @@ -1200,6 +1280,7 @@ impl<'a> LoweringContext<'a> {
struct ImplTraitLifetimeCollector<'r, 'a: 'r> {
context: &'r mut LoweringContext<'a>,
parent: DefIndex,
exist_ty_id: NodeId,
collect_elided_lifetimes: bool,
currently_bound_lifetimes: Vec<hir::LifetimeName>,
already_defined_lifetimes: HashSet<hir::LifetimeName>,
Expand Down Expand Up @@ -1294,7 +1375,11 @@ impl<'a> LoweringContext<'a> {
name,
});

let def_node_id = self.context.next_id().node_id;
// We need to manually create the ids here, because the
// definitions will go into the explicit `existential type`
// declaration and thus need to have their owner set to that item
let def_node_id = self.context.sess.next_node_id();
let _ = self.context.lower_node_id_with_owner(def_node_id, self.exist_ty_id);
self.context.resolver.definitions().create_def_with_parent(
self.parent,
def_node_id,
Expand All @@ -1306,7 +1391,7 @@ impl<'a> LoweringContext<'a> {
let def_lifetime = hir::Lifetime {
id: def_node_id,
span: lifetime.span,
name: name,
name,
};
self.output_lifetime_params
.push(hir::GenericParam::Lifetime(hir::LifetimeDef {
Expand All @@ -1322,6 +1407,7 @@ impl<'a> LoweringContext<'a> {
let mut lifetime_collector = ImplTraitLifetimeCollector {
context: self,
parent: parent_index,
exist_ty_id,
collect_elided_lifetimes: true,
currently_bound_lifetimes: Vec::new(),
already_defined_lifetimes: HashSet::new(),
Expand Down Expand Up @@ -1759,8 +1845,8 @@ impl<'a> LoweringContext<'a> {
.collect(),
output: match decl.output {
FunctionRetTy::Ty(ref ty) => match fn_def_id {
Some(_) if impl_trait_return_allow => {
hir::Return(self.lower_ty(ty, ImplTraitContext::Existential))
Some(def_id) if impl_trait_return_allow => {
hir::Return(self.lower_ty(ty, ImplTraitContext::Existential(def_id)))
}
_ => hir::Return(self.lower_ty(ty, ImplTraitContext::Disallowed)),
},
Expand Down
5 changes: 1 addition & 4 deletions src/librustc/hir/map/def_collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> {
debug!("visit_item: {:?}", i);

// Pick the def data. This need not be unique, but the more
// information we encapsulate into
// information we encapsulate into, the better
let def_data = match i.node {
ItemKind::Impl(..) => DefPathData::Impl,
ItemKind::Trait(..) => DefPathData::Trait(i.ident.name.as_interned_str()),
Expand Down Expand Up @@ -256,9 +256,6 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> {
fn visit_ty(&mut self, ty: &'a Ty) {
match ty.node {
TyKind::Mac(..) => return self.visit_macro_invoc(ty.id),
TyKind::ImplTrait(..) => {
self.create_def(ty.id, DefPathData::ImplTrait, REGULAR_SPACE, ty.span);
}
_ => {}
}
visit::walk_ty(self, ty);
Expand Down
39 changes: 11 additions & 28 deletions src/librustc/hir/map/definitions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,30 +210,9 @@ impl DefKey {
} = self.disambiguated_data;

::std::mem::discriminant(data).hash(&mut hasher);
match *data {
DefPathData::TypeNs(name) |
DefPathData::Trait(name) |
DefPathData::AssocTypeInTrait(name) |
DefPathData::AssocTypeInImpl(name) |
DefPathData::ValueNs(name) |
DefPathData::Module(name) |
DefPathData::MacroDef(name) |
DefPathData::TypeParam(name) |
DefPathData::LifetimeDef(name) |
DefPathData::EnumVariant(name) |
DefPathData::Field(name) |
DefPathData::GlobalMetaData(name) => {
name.hash(&mut hasher);
}

DefPathData::Impl |
DefPathData::CrateRoot |
DefPathData::Misc |
DefPathData::ClosureExpr |
DefPathData::StructCtor |
DefPathData::AnonConst |
DefPathData::ImplTrait => {}
};
if let Some(name) = data.get_opt_name() {
name.hash(&mut hasher);
}

disambiguator.hash(&mut hasher);

Expand Down Expand Up @@ -390,8 +369,10 @@ pub enum DefPathData {
StructCtor,
/// A constant expression (see {ast,hir}::AnonConst).
AnonConst,
/// An `impl Trait` type node.
ImplTrait,
/// An `impl Trait` type node in argument position.
UniversalImplTrait,
/// An `impl Trait` type node in return position.
ExistentialImplTrait,

/// GlobalMetaData identifies a piece of crate metadata that is global to
/// a whole crate (as opposed to just one item). GlobalMetaData components
Expand Down Expand Up @@ -655,7 +636,8 @@ impl DefPathData {
ClosureExpr |
StructCtor |
AnonConst |
ImplTrait => None
ExistentialImplTrait |
UniversalImplTrait => None
}
}

Expand Down Expand Up @@ -685,7 +667,8 @@ impl DefPathData {
ClosureExpr => "{{closure}}",
StructCtor => "{{constructor}}",
AnonConst => "{{constant}}",
ImplTrait => "{{impl-Trait}}",
ExistentialImplTrait => "{{exist-impl-Trait}}",
UniversalImplTrait => "{{univ-impl-Trait}}",
};

Symbol::intern(s).as_interned_str()
Expand Down
Loading

0 comments on commit 9b1bd94

Please sign in to comment.