Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add existential type definitions #51414

Merged
merged 7 commits into from
Jun 18, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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