Skip to content

Commit

Permalink
Auto merge of #52650 - oli-obk:associated_existential_types, r=nikoma…
Browse files Browse the repository at this point in the history
…tsakis

Implement associated existential types

r? @nikomatsakis

no idea if these work with generic traits. I'm going home for the day 🤣
  • Loading branch information
bors committed Jul 27, 2018
2 parents 3d0e933 + 33712a8 commit 7c2aeb9
Show file tree
Hide file tree
Showing 8 changed files with 186 additions and 30 deletions.
52 changes: 34 additions & 18 deletions src/librustc/infer/anon_types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -691,25 +691,41 @@ impl<'a, 'gcx, 'tcx> Instantiator<'a, 'gcx, 'tcx> {
// }
// ```
if let Some(anon_node_id) = tcx.hir.as_local_node_id(def_id) {
let in_definition_scope = match tcx.hir.expect_item(anon_node_id).node {
// impl trait
hir::ItemKind::Existential(hir::ExistTy {
impl_trait_fn: Some(parent),
..
}) => parent == self.parent_def_id,
// named existential types
hir::ItemKind::Existential(hir::ExistTy {
impl_trait_fn: None,
..
}) => may_define_existential_type(
tcx,
self.parent_def_id,
anon_node_id,
),
_ => {
let anon_parent_node_id = tcx.hir.get_parent(anon_node_id);
self.parent_def_id == tcx.hir.local_def_id(anon_parent_node_id)
let parent_def_id = self.parent_def_id;
let def_scope_default = || {
let anon_parent_node_id = tcx.hir.get_parent(anon_node_id);
parent_def_id == tcx.hir.local_def_id(anon_parent_node_id)
};
let in_definition_scope = match tcx.hir.find(anon_node_id) {
Some(hir::map::NodeItem(item)) => match item.node {
// impl trait
hir::ItemKind::Existential(hir::ExistTy {
impl_trait_fn: Some(parent),
..
}) => parent == self.parent_def_id,
// named existential types
hir::ItemKind::Existential(hir::ExistTy {
impl_trait_fn: None,
..
}) => may_define_existential_type(
tcx,
self.parent_def_id,
anon_node_id,
),
_ => def_scope_default(),
},
Some(hir::map::NodeImplItem(item)) => match item.node {
hir::ImplItemKind::Existential(_) => may_define_existential_type(
tcx,
self.parent_def_id,
anon_node_id,
),
_ => def_scope_default(),
},
_ => bug!(
"expected (impl) item, found {}",
tcx.hir.node_to_string(anon_node_id),
),
};
if in_definition_scope {
return self.fold_anon_ty(ty, def_id, substs);
Expand Down
13 changes: 10 additions & 3 deletions src/librustc/traits/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1502,19 +1502,26 @@ fn confirm_impl_candidate<'cx, 'gcx, 'tcx>(
let param_env = obligation.param_env;
let assoc_ty = assoc_ty_def(selcx, impl_def_id, obligation.predicate.item_def_id);

let ty = if !assoc_ty.item.defaultness.has_value() {
if !assoc_ty.item.defaultness.has_value() {
// This means that the impl is missing a definition for the
// associated type. This error will be reported by the type
// checker method `check_impl_items_against_trait`, so here we
// just return TyError.
debug!("confirm_impl_candidate: no associated type {:?} for {:?}",
assoc_ty.item.ident,
obligation.predicate);
tcx.types.err
return Progress {
ty: tcx.types.err,
obligations: nested,
};
}
let substs = translate_substs(selcx.infcx(), param_env, impl_def_id, substs, assoc_ty.node);
let ty = if let ty::AssociatedKind::Existential = assoc_ty.item.kind {
let item_substs = Substs::identity_for_item(tcx, assoc_ty.item.def_id);
tcx.mk_anon(assoc_ty.item.def_id, item_substs)
} else {
tcx.type_of(assoc_ty.item.def_id)
};
let substs = translate_substs(selcx.infcx(), param_env, impl_def_id, substs, assoc_ty.node);
Progress {
ty: ty.subst(tcx, substs),
obligations: nested,
Expand Down
16 changes: 13 additions & 3 deletions src/librustc/traits/specialize/specialization_graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -377,9 +377,19 @@ impl<'a, 'gcx, 'tcx> Ancestors {
trait_def_id: DefId,
) -> impl Iterator<Item = NodeItem<ty::AssociatedItem>> + Captures<'gcx> + Captures<'tcx> + 'a {
self.flat_map(move |node| {
node.items(tcx).filter(move |impl_item| {
impl_item.kind == trait_item_kind &&
tcx.hygienic_eq(impl_item.ident, trait_item_name, trait_def_id)
use ty::AssociatedKind::*;
node.items(tcx).filter(move |impl_item| match (trait_item_kind, impl_item.kind) {
| (Const, Const)
| (Method, Method)
| (Type, Type)
| (Type, Existential)
=> tcx.hygienic_eq(impl_item.ident, trait_item_name, trait_def_id),

| (Const, _)
| (Method, _)
| (Type, _)
| (Existential, _)
=> false,
}).map(move |item| NodeItem { node: node, item: item })
})
}
Expand Down
3 changes: 1 addition & 2 deletions src/librustc_typeck/check/wfcheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,7 @@ fn check_associated_item<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
}
}
ty::AssociatedKind::Existential => {
// FIXME(oli-obk) implement existential types in trait impls
unimplemented!()
// do nothing, existential types check themselves
}
}

Expand Down
28 changes: 24 additions & 4 deletions src/librustc_typeck/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1046,12 +1046,12 @@ fn type_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
tcx.mk_fn_def(def_id, substs)
}
ImplItemKind::Const(ref ty, _) => icx.to_ty(ty),
ImplItemKind::Existential(ref _bounds) => {
ImplItemKind::Existential(_) => {
if tcx.impl_trait_ref(tcx.hir.get_parent_did(node_id)).is_none() {
report_assoc_ty_on_inherent_impl(tcx, item.span);
}
// FIXME(oli-obk) implement existential types in trait impls
unimplemented!()

find_existential_constraints(tcx, def_id)
}
ImplItemKind::Type(ref ty) => {
if tcx.impl_trait_ref(tcx.hir.get_parent_did(node_id)).is_none() {
Expand Down Expand Up @@ -1186,8 +1186,10 @@ fn find_existential_constraints<'a, 'tcx>(
}
impl<'a, 'tcx> ConstraintLocator<'a, 'tcx> {
fn check(&mut self, def_id: DefId) {
trace!("checking {:?}", def_id);
// don't try to check items that cannot possibly constrain the type
if !self.tcx.has_typeck_tables(def_id) {
trace!("no typeck tables for {:?}", def_id);
return;
}
let ty = self
Expand Down Expand Up @@ -1244,9 +1246,11 @@ fn find_existential_constraints<'a, 'tcx>(
let mut locator = ConstraintLocator { def_id, tcx, found: None };
let node_id = tcx.hir.as_local_node_id(def_id).unwrap();
let parent = tcx.hir.get_parent(node_id);
trace!("parent_id: {:?}", parent);
if parent == ast::CRATE_NODE_ID {
intravisit::walk_crate(&mut locator, tcx.hir.krate());
} else {
trace!("parent: {:?}", tcx.hir.get(parent));
match tcx.hir.get(parent) {
NodeItem(ref it) => intravisit::walk_item(&mut locator, it),
NodeImplItem(ref it) => intravisit::walk_impl_item(&mut locator, it),
Expand Down Expand Up @@ -1485,7 +1489,23 @@ fn explicit_predicates_of<'a, 'tcx>(
&item.generics
}

NodeImplItem(item) => &item.generics,
NodeImplItem(item) => match item.node {
ImplItemKind::Existential(ref bounds) => {
let substs = Substs::identity_for_item(tcx, def_id);
let anon_ty = tcx.mk_anon(def_id, substs);

// Collect the bounds, i.e. the `A+B+'c` in `impl A+B+'c`.
let bounds = compute_bounds(&icx,
anon_ty,
bounds,
SizedByDefault::Yes,
tcx.def_span(def_id));

predicates.extend(bounds.predicates(tcx, anon_ty));
&item.generics
},
_ => &item.generics,
}

NodeItem(item) => {
match item.node {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(existential_type)]
// compile-pass

trait Bar {}
struct Dummy<U>(U);
impl<V> Bar for Dummy<V> {}

trait Foo<T> {
type Assoc: Bar;
fn foo(t: T) -> Self::Assoc;
}

impl<W> Foo<W> for i32 {
existential type Assoc: Bar;
fn foo(w: W) -> Self::Assoc {
Dummy(w)
}
}

struct NonGeneric;
impl Bar for NonGeneric {}

impl<W> Foo<W> for u32 {
existential type Assoc: Bar;
fn foo(_: W) -> Self::Assoc {
NonGeneric
}
}

fn main() {}
30 changes: 30 additions & 0 deletions src/test/ui/impl-trait/associated-existential-type-trivial.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(existential_type)]
// compile-pass

trait Bar {}
struct Dummy;
impl Bar for Dummy {}

trait Foo {
type Assoc: Bar;
fn foo() -> Self::Assoc;
}

impl Foo for i32 {
existential type Assoc: Bar;
fn foo() -> Self::Assoc {
Dummy
}
}

fn main() {}
34 changes: 34 additions & 0 deletions src/test/ui/impl-trait/associated-existential-type.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(existential_type)]
// compile-pass

trait Bar {}
struct Dummy;
impl Bar for Dummy {}

trait Foo {
type Assoc: Bar;
fn foo() -> Self::Assoc;
fn bar() -> Self::Assoc;
}

impl Foo for i32 {
existential type Assoc: Bar;
fn foo() -> Self::Assoc {
Dummy
}
fn bar() -> Self::Assoc {
Dummy
}
}

fn main() {}

0 comments on commit 7c2aeb9

Please sign in to comment.