Skip to content

Implement trait aliases (RFC 1733) #55101

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

Merged
merged 10 commits into from
Nov 3, 2018
34 changes: 34 additions & 0 deletions src/doc/unstable-book/src/language-features/trait-alias.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# `trait_alias`

The tracking issue for this feature is: [#41517]

[#41417]: https://github.com/rust-lang/rust/issues/41517

------------------------

The `trait_alias` feature adds support for trait aliases. These allow aliases
to be created for one or more traits (currently just a single regular trait plus
any number of auto-traits), and used wherever traits would normally be used as
either bounds or trait objects.

```rust
#![feature(trait_alias)]

trait Foo = std::fmt::Debug + Send;
trait Bar = Foo + Sync;

// Use trait alias as bound on type parameter.
fn foo<T: Foo>(v: &T) {
println!("{:?}", v);
}

pub fn main() {
foo(&1);

// Use trait alias for trait objects.
let a: &Bar = &123;
println!("{:?}", a);
let b = Box::new(456) as Box<dyn Foo>;
println!("{:?}", b);
}
```
33 changes: 17 additions & 16 deletions src/librustc/hir/lowering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4875,23 +4875,24 @@ impl<'a> LoweringContext<'a> {
let node = match qpath {
hir::QPath::Resolved(None, path) => {
// Turn trait object paths into `TyKind::TraitObject` instead.
if let Def::Trait(_) = path.def {
let principal = hir::PolyTraitRef {
bound_generic_params: hir::HirVec::new(),
trait_ref: hir::TraitRef {
path: path.and_then(|path| path),
ref_id: id.node_id,
hir_ref_id: id.hir_id,
},
span,
};
match path.def {
Def::Trait(_) | Def::TraitAlias(_) => {
let principal = hir::PolyTraitRef {
bound_generic_params: hir::HirVec::new(),
trait_ref: hir::TraitRef {
path: path.and_then(|path| path),
ref_id: id.node_id,
hir_ref_id: id.hir_id,
},
span,
};

// The original ID is taken by the `PolyTraitRef`,
// so the `Ty` itself needs a different one.
id = self.next_id();
hir::TyKind::TraitObject(hir_vec![principal], self.elided_dyn_bound(span))
} else {
hir::TyKind::Path(hir::QPath::Resolved(None, path))
// The original ID is taken by the `PolyTraitRef`,
// so the `Ty` itself needs a different one.
id = self.next_id();
hir::TyKind::TraitObject(hir_vec![principal], self.elided_dyn_bound(span))
}
_ => hir::TyKind::Path(hir::QPath::Resolved(None, path)),
}
}
_ => hir::TyKind::Path(qpath),
Expand Down
4 changes: 1 addition & 3 deletions src/librustc/hir/map/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,9 +301,7 @@ impl<'hir> Map<'hir> {
ItemKind::Struct(..) => Some(Def::Struct(def_id())),
ItemKind::Union(..) => Some(Def::Union(def_id())),
ItemKind::Trait(..) => Some(Def::Trait(def_id())),
ItemKind::TraitAlias(..) => {
bug!("trait aliases are not yet implemented (see issue #41517)")
},
ItemKind::TraitAlias(..) => Some(Def::TraitAlias(def_id())),
ItemKind::ExternCrate(_) |
ItemKind::Use(..) |
ItemKind::ForeignMod(..) |
Expand Down
17 changes: 17 additions & 0 deletions src/librustc/ich/impls_ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1119,6 +1119,7 @@ for traits::Vtable<'gcx, N> where N: HashStable<StableHashingContext<'a>> {
&VtableClosure(ref table_closure) => table_closure.hash_stable(hcx, hasher),
&VtableFnPointer(ref table_fn_pointer) => table_fn_pointer.hash_stable(hcx, hasher),
&VtableGenerator(ref table_generator) => table_generator.hash_stable(hcx, hasher),
&VtableTraitAlias(ref table_alias) => table_alias.hash_stable(hcx, hasher),
}
}
}
Expand Down Expand Up @@ -1227,6 +1228,22 @@ for traits::VtableGeneratorData<'gcx, N> where N: HashStable<StableHashingContex
}
}

impl<'a, 'gcx, N> HashStable<StableHashingContext<'a>>
for traits::VtableTraitAliasData<'gcx, N> where N: HashStable<StableHashingContext<'a>> {
fn hash_stable<W: StableHasherResult>(&self,
hcx: &mut StableHashingContext<'a>,
hasher: &mut StableHasher<W>) {
let traits::VtableTraitAliasData {
alias_def_id,
substs,
ref nested,
} = *self;
alias_def_id.hash_stable(hcx, hasher);
substs.hash_stable(hcx, hasher);
nested.hash_stable(hcx, hasher);
}
}

impl_stable_hash_for!(
impl<'tcx, V> for struct infer::canonical::Canonical<'tcx, V> {
max_universe, variables, value
Expand Down
19 changes: 15 additions & 4 deletions src/librustc/middle/resolve_lifetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,17 @@ fn krate<'tcx>(tcx: TyCtxt<'_, 'tcx, 'tcx>) -> NamedRegionMap {
map
}

/// In traits, there is an implicit `Self` type parameter which comes before the generics.
/// We have to account for this when computing the index of the other generic parameters.
/// This function returns whether there is such an implicit parameter defined on the given item.
fn sub_items_have_self_param(node: &hir::ItemKind) -> bool {
match *node {
hir::ItemKind::Trait(..) |
hir::ItemKind::TraitAlias(..) => true,
_ => false,
}
}

impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
NestedVisitorMap::All(&self.tcx.hir)
Expand Down Expand Up @@ -522,8 +533,8 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
hir::ItemKind::Impl(..) => true,
_ => false,
};
// These kinds of items have only early bound lifetime parameters.
let mut index = if let hir::ItemKind::Trait(..) = item.node {
// These kinds of items have only early-bound lifetime parameters.
let mut index = if sub_items_have_self_param(&item.node) {
1 // Self comes before lifetimes
} else {
0
Expand Down Expand Up @@ -1602,8 +1613,8 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
let mut index = 0;
if let Some(parent_id) = parent_id {
let parent = self.tcx.hir.expect_item(parent_id);
if let hir::ItemKind::Trait(..) = parent.node {
index += 1; // Self comes first.
if sub_items_have_self_param(&parent.node) {
index += 1; // Self comes before lifetimes
}
match parent.node {
hir::ItemKind::Trait(_, _, ref generics, ..)
Expand Down
34 changes: 25 additions & 9 deletions src/librustc/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -534,8 +534,11 @@ pub enum Vtable<'tcx, N> {
/// Same as above, but for a fn pointer type with the given signature.
VtableFnPointer(VtableFnPointerData<'tcx, N>),

/// Vtable automatically generated for a generator
/// Vtable automatically generated for a generator.
VtableGenerator(VtableGeneratorData<'tcx, N>),

/// Vtable for a trait alias.
VtableTraitAlias(VtableTraitAliasData<'tcx, N>),
}

/// Identifies a particular impl in the source, along with a set of
Expand Down Expand Up @@ -605,6 +608,13 @@ pub struct VtableFnPointerData<'tcx, N> {
pub nested: Vec<N>
}

#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable)]
pub struct VtableTraitAliasData<'tcx, N> {
pub alias_def_id: DefId,
pub substs: &'tcx Substs<'tcx>,
pub nested: Vec<N>,
}

/// Creates predicate obligations from the generic bounds.
pub fn predicates_for_generics<'tcx>(cause: ObligationCause<'tcx>,
param_env: ty::ParamEnv<'tcx>,
Expand Down Expand Up @@ -1067,6 +1077,7 @@ impl<'tcx, N> Vtable<'tcx, N> {
VtableGenerator(c) => c.nested,
VtableObject(d) => d.nested,
VtableFnPointer(d) => d.nested,
VtableTraitAlias(d) => d.nested,
}
}

Expand All @@ -1090,20 +1101,25 @@ impl<'tcx, N> Vtable<'tcx, N> {
trait_def_id: d.trait_def_id,
nested: d.nested.into_iter().map(f).collect(),
}),
VtableFnPointer(p) => VtableFnPointer(VtableFnPointerData {
fn_ty: p.fn_ty,
nested: p.nested.into_iter().map(f).collect(),
VtableClosure(c) => VtableClosure(VtableClosureData {
closure_def_id: c.closure_def_id,
substs: c.substs,
nested: c.nested.into_iter().map(f).collect(),
}),
VtableGenerator(c) => VtableGenerator(VtableGeneratorData {
generator_def_id: c.generator_def_id,
substs: c.substs,
nested: c.nested.into_iter().map(f).collect(),
}),
VtableClosure(c) => VtableClosure(VtableClosureData {
closure_def_id: c.closure_def_id,
substs: c.substs,
nested: c.nested.into_iter().map(f).collect(),
})
VtableFnPointer(p) => VtableFnPointer(VtableFnPointerData {
fn_ty: p.fn_ty,
nested: p.nested.into_iter().map(f).collect(),
}),
VtableTraitAlias(d) => VtableTraitAlias(VtableTraitAliasData {
alias_def_id: d.alias_def_id,
substs: d.substs,
nested: d.nested.into_iter().map(f).collect(),
}),
}
}
}
Expand Down
13 changes: 6 additions & 7 deletions src/librustc/traits/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,7 @@ use super::PredicateObligation;
use super::Selection;
use super::SelectionContext;
use super::SelectionError;
use super::VtableClosureData;
use super::VtableGeneratorData;
use super::VtableFnPointerData;
use super::VtableImplData;
use super::{VtableImplData, VtableClosureData, VtableGeneratorData, VtableFnPointerData};
use super::util;

use hir::def_id::DefId;
Expand Down Expand Up @@ -1073,7 +1070,8 @@ fn assemble_candidates_from_impls<'cx, 'gcx, 'tcx>(
super::VtableClosure(_) |
super::VtableGenerator(_) |
super::VtableFnPointer(_) |
super::VtableObject(_) => {
super::VtableObject(_) |
super::VtableTraitAlias(_) => {
debug!("assemble_candidates_from_impls: vtable={:?}",
vtable);
true
Expand Down Expand Up @@ -1235,7 +1233,8 @@ fn confirm_select_candidate<'cx, 'gcx, 'tcx>(
confirm_object_candidate(selcx, obligation, obligation_trait_ref),
super::VtableAutoImpl(..) |
super::VtableParam(..) |
super::VtableBuiltin(..) =>
super::VtableBuiltin(..) |
super::VtableTraitAlias(..) =>
// we don't create Select candidates with this kind of resolution
span_bug!(
obligation.cause.span,
Expand Down Expand Up @@ -1486,7 +1485,7 @@ fn confirm_impl_candidate<'cx, 'gcx, 'tcx>(
impl_vtable: VtableImplData<'tcx, PredicateObligation<'tcx>>)
-> Progress<'tcx>
{
let VtableImplData { substs, nested, impl_def_id } = impl_vtable;
let VtableImplData { impl_def_id, substs, nested } = impl_vtable;

let tcx = selcx.tcx();
let param_env = obligation.param_env;
Expand Down
Loading