Skip to content

Commit

Permalink
Auto merge of rust-lang#21237 - erickt:derive-assoc-types, r=erickt
Browse files Browse the repository at this point in the history
This PR adds support for associated types to the `#[derive(...)]` syntax extension. In order to do this, it switches over to using where predicates to apply the type constraints. So now this:

```rust
type Trait {
    type Type;
}

#[derive(Clone)]
struct Foo<A> where A: Trait {
    a: A,
    b: <A as Trait>::Type,
}
```

Gets expended into this impl:

```rust
impl<A: Clone> Clone for Foo<A> where
    A: Trait,
    <A as Trait>::Type: Clone,
{
    fn clone(&self) -> Foo<T> {
        Foo {
            a: self.a.clone(),
            b: self.b.clone(),
        }
    }
}
```
  • Loading branch information
bors committed Mar 26, 2015
2 parents 1501f33 + 9cabe27 commit 557d434
Show file tree
Hide file tree
Showing 4 changed files with 358 additions and 6 deletions.
124 changes: 118 additions & 6 deletions src/libsyntax/ext/deriving/generic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,46 @@ pub fn combine_substructure<'a>(f: CombineSubstructureFunc<'a>)
RefCell::new(f)
}

/// This method helps to extract all the type parameters referenced from a
/// type. For a type parameter `<T>`, it looks for either a `TyPath` that
/// is not global and starts with `T`, or a `TyQPath`.
fn find_type_parameters(ty: &ast::Ty, ty_param_names: &[ast::Name]) -> Vec<P<ast::Ty>> {
use visit;

struct Visitor<'a> {
ty_param_names: &'a [ast::Name],
types: Vec<P<ast::Ty>>,
}

impl<'a> visit::Visitor<'a> for Visitor<'a> {
fn visit_ty(&mut self, ty: &'a ast::Ty) {
match ty.node {
ast::TyPath(_, ref path) if !path.global => {
match path.segments.first() {
Some(segment) => {
if self.ty_param_names.contains(&segment.identifier.name) {
self.types.push(P(ty.clone()));
}
}
None => {}
}
}
_ => {}
}

visit::walk_ty(self, ty)
}
}

let mut visitor = Visitor {
ty_param_names: ty_param_names,
types: Vec::new(),
};

visit::Visitor::visit_ty(&mut visitor, ty);

visitor.types
}

impl<'a> TraitDef<'a> {
pub fn expand<F>(&self,
Expand Down Expand Up @@ -374,18 +414,42 @@ impl<'a> TraitDef<'a> {
}))
}

/// Given that we are deriving a trait `Tr` for a type `T<'a, ...,
/// 'z, A, ..., Z>`, creates an impl like:
/// Given that we are deriving a trait `DerivedTrait` for a type like:
///
/// ```ignore
/// impl<'a, ..., 'z, A:Tr B1 B2, ..., Z: Tr B1 B2> Tr for T<A, ..., Z> { ... }
/// struct Struct<'a, ..., 'z, A, B: DeclaredTrait, C, ..., Z> where C: WhereTrait {
/// a: A,
/// b: B::Item,
/// b1: <B as DeclaredTrait>::Item,
/// c1: <C as WhereTrait>::Item,
/// c2: Option<<C as WhereTrait>::Item>,
/// ...
/// }
/// ```
///
/// create an impl like:
///
/// ```ignore
/// impl<'a, ..., 'z, A, B: DeclaredTrait, C, ... Z> where
/// C: WhereTrait,
/// A: DerivedTrait + B1 + ... + BN,
/// B: DerivedTrait + B1 + ... + BN,
/// C: DerivedTrait + B1 + ... + BN,
/// B::Item: DerivedTrait + B1 + ... + BN,
/// <C as WhereTrait>::Item: DerivedTrait + B1 + ... + BN,
/// ...
/// {
/// ...
/// }
/// ```
///
/// where B1, B2, ... are the bounds given by `bounds_paths`.'
/// where B1, ..., BN are the bounds given by `bounds_paths`.'. Z is a phantom type, and
/// therefore does not get bound by the derived trait.
fn create_derived_impl(&self,
cx: &mut ExtCtxt,
type_ident: Ident,
generics: &Generics,
field_tys: Vec<P<ast::Ty>>,
methods: Vec<P<ast::ImplItem>>) -> P<ast::Item> {
let trait_path = self.path.to_path(cx, self.span, type_ident, generics);

Expand Down Expand Up @@ -466,6 +530,35 @@ impl<'a> TraitDef<'a> {
}
}));

if !ty_params.is_empty() {
let ty_param_names: Vec<ast::Name> = ty_params.iter()
.map(|ty_param| ty_param.ident.name)
.collect();

for field_ty in field_tys.into_iter() {
let tys = find_type_parameters(&*field_ty, &ty_param_names);

for ty in tys.into_iter() {
let mut bounds: Vec<_> = self.additional_bounds.iter().map(|p| {
cx.typarambound(p.to_path(cx, self.span, type_ident, generics))
}).collect();

// require the current trait
bounds.push(cx.typarambound(trait_path.clone()));

let predicate = ast::WhereBoundPredicate {
span: self.span,
bound_lifetimes: vec![],
bounded_ty: ty,
bounds: OwnedSlice::from_vec(bounds),
};

let predicate = ast::WherePredicate::BoundPredicate(predicate);
where_clause.predicates.push(predicate);
}
}
}

let trait_generics = Generics {
lifetimes: lifetimes,
ty_params: OwnedSlice::from_vec(ty_params),
Expand Down Expand Up @@ -518,6 +611,10 @@ impl<'a> TraitDef<'a> {
struct_def: &StructDef,
type_ident: Ident,
generics: &Generics) -> P<ast::Item> {
let field_tys: Vec<P<ast::Ty>> = struct_def.fields.iter()
.map(|field| field.node.ty.clone())
.collect();

let methods = self.methods.iter().map(|method_def| {
let (explicit_self, self_args, nonself_args, tys) =
method_def.split_self_nonself_args(
Expand Down Expand Up @@ -550,14 +647,29 @@ impl<'a> TraitDef<'a> {
body)
}).collect();

self.create_derived_impl(cx, type_ident, generics, methods)
self.create_derived_impl(cx, type_ident, generics, field_tys, methods)
}

fn expand_enum_def(&self,
cx: &mut ExtCtxt,
enum_def: &EnumDef,
type_ident: Ident,
generics: &Generics) -> P<ast::Item> {
let mut field_tys = Vec::new();

for variant in enum_def.variants.iter() {
match variant.node.kind {
ast::VariantKind::TupleVariantKind(ref args) => {
field_tys.extend(args.iter()
.map(|arg| arg.ty.clone()));
}
ast::VariantKind::StructVariantKind(ref args) => {
field_tys.extend(args.fields.iter()
.map(|field| field.node.ty.clone()));
}
}
}

let methods = self.methods.iter().map(|method_def| {
let (explicit_self, self_args, nonself_args, tys) =
method_def.split_self_nonself_args(cx, self,
Expand Down Expand Up @@ -590,7 +702,7 @@ impl<'a> TraitDef<'a> {
body)
}).collect();

self.create_derived_impl(cx, type_ident, generics, methods)
self.create_derived_impl(cx, type_ident, generics, field_tys, methods)
}
}

Expand Down
1 change: 1 addition & 0 deletions src/test/auxiliary/struct_variant_xc_aux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#![crate_name="struct_variant_xc_aux"]
#![crate_type = "lib"]

#[derive(Copy)]
pub enum Enum {
Variant(u8),
StructVariant { arg: u8 }
Expand Down
29 changes: 29 additions & 0 deletions src/test/compile-fail/derive-assoc-type-not-impl.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright 2015 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.

trait Foo {
type X;
fn method(&self) {}
}

#[derive(Clone)]
struct Bar<T: Foo> {
x: T::X,
}

struct NotClone;

impl Foo for NotClone {
type X = i8;
}

fn main() {
Bar::<NotClone> { x: 1 }.clone(); //~ ERROR
}
Loading

0 comments on commit 557d434

Please sign in to comment.