diff --git a/src/libsyntax/ext/deriving/generic/mod.rs b/src/libsyntax/ext/deriving/generic/mod.rs index 58b6d96607df7..0c5e4d67642ac 100644 --- a/src/libsyntax/ext/deriving/generic/mod.rs +++ b/src/libsyntax/ext/deriving/generic/mod.rs @@ -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 ``, 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> { + use visit; + + struct Visitor<'a> { + ty_param_names: &'a [ast::Name], + types: Vec>, + } + + 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(&self, @@ -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 { ... } + /// struct Struct<'a, ..., 'z, A, B: DeclaredTrait, C, ..., Z> where C: WhereTrait { + /// a: A, + /// b: B::Item, + /// b1: ::Item, + /// c1: ::Item, + /// c2: Option<::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, + /// ::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>, methods: Vec>) -> P { let trait_path = self.path.to_path(cx, self.span, type_ident, generics); @@ -466,6 +530,35 @@ impl<'a> TraitDef<'a> { } })); + if !ty_params.is_empty() { + let ty_param_names: Vec = 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), @@ -518,6 +611,10 @@ impl<'a> TraitDef<'a> { struct_def: &StructDef, type_ident: Ident, generics: &Generics) -> P { + let field_tys: Vec> = 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( @@ -550,7 +647,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) } fn expand_enum_def(&self, @@ -558,6 +655,21 @@ impl<'a> TraitDef<'a> { enum_def: &EnumDef, type_ident: Ident, generics: &Generics) -> P { + 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, @@ -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) } } diff --git a/src/test/auxiliary/struct_variant_xc_aux.rs b/src/test/auxiliary/struct_variant_xc_aux.rs index 76fd619f68964..8670cd96fc6de 100644 --- a/src/test/auxiliary/struct_variant_xc_aux.rs +++ b/src/test/auxiliary/struct_variant_xc_aux.rs @@ -11,6 +11,7 @@ #![crate_name="struct_variant_xc_aux"] #![crate_type = "lib"] +#[derive(Copy)] pub enum Enum { Variant(u8), StructVariant { arg: u8 } diff --git a/src/test/compile-fail/derive-assoc-type-not-impl.rs b/src/test/compile-fail/derive-assoc-type-not-impl.rs new file mode 100644 index 0000000000000..3799f2ffba440 --- /dev/null +++ b/src/test/compile-fail/derive-assoc-type-not-impl.rs @@ -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 or the MIT license +// , 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 { + x: T::X, +} + +struct NotClone; + +impl Foo for NotClone { + type X = i8; +} + +fn main() { + Bar:: { x: 1 }.clone(); //~ ERROR +} diff --git a/src/test/run-pass/deriving-associated-types.rs b/src/test/run-pass/deriving-associated-types.rs new file mode 100644 index 0000000000000..59eb5506c45c7 --- /dev/null +++ b/src/test/run-pass/deriving-associated-types.rs @@ -0,0 +1,210 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(core, debug_builders)] + +pub trait DeclaredTrait { + type Type; +} + +impl DeclaredTrait for i32 { + type Type = i32; +} + +pub trait WhereTrait { + type Type; +} + +impl WhereTrait for i32 { + type Type = i32; +} + +// Make sure we don't add a bound that just shares a name with an associated +// type. +pub mod module { + pub type Type = i32; +} + +#[derive(PartialEq, Debug)] +struct PrivateStruct(T); + +#[derive(PartialEq, Debug)] +struct TupleStruct( + module::Type, + Option, + A, + PrivateStruct, + B, + B::Type, + Option, + ::Type, + Option<::Type>, + C, + C::Type, + Option, + ::Type, + Option<::Type>, + ::Type, +) where C: WhereTrait; + +#[derive(PartialEq, Debug)] +pub struct Struct where C: WhereTrait { + m1: module::Type, + m2: Option, + a1: A, + a2: PrivateStruct, + b: B, + b1: B::Type, + b2: Option, + b3: ::Type, + b4: Option<::Type>, + c: C, + c1: C::Type, + c2: Option, + c3: ::Type, + c4: Option<::Type>, + d: ::Type, +} + +#[derive(PartialEq, Debug)] +enum Enum where C: WhereTrait { + Unit, + Seq( + module::Type, + Option, + A, + PrivateStruct, + B, + B::Type, + Option, + ::Type, + Option<::Type>, + C, + C::Type, + Option, + ::Type, + Option<::Type>, + ::Type, + ), + Map { + m1: module::Type, + m2: Option, + a1: A, + a2: PrivateStruct, + b: B, + b1: B::Type, + b2: Option, + b3: ::Type, + b4: Option<::Type>, + c: C, + c1: C::Type, + c2: Option, + c3: ::Type, + c4: Option<::Type>, + d: ::Type, + }, +} + +fn main() { + let e: TupleStruct< + i32, + i32, + i32, + > = TupleStruct( + 0, + None, + 0, + PrivateStruct(0), + 0, + 0, + None, + 0, + None, + 0, + 0, + None, + 0, + None, + 0, + ); + assert_eq!(e, e); + + let e: Struct< + i32, + i32, + i32, + > = Struct { + m1: 0, + m2: None, + a1: 0, + a2: PrivateStruct(0), + b: 0, + b1: 0, + b2: None, + b3: 0, + b4: None, + c: 0, + c1: 0, + c2: None, + c3: 0, + c4: None, + d: 0, + }; + assert_eq!(e, e); + + let e = Enum::Unit::; + assert_eq!(e, e); + + let e: Enum< + i32, + i32, + i32, + > = Enum::Seq( + 0, + None, + 0, + PrivateStruct(0), + 0, + 0, + None, + 0, + None, + 0, + 0, + None, + 0, + None, + 0, + ); + assert_eq!(e, e); + + let e: Enum< + i32, + i32, + i32, + > = Enum::Map { + m1: 0, + m2: None, + a1: 0, + a2: PrivateStruct(0), + b: 0, + b1: 0, + b2: None, + b3: 0, + b4: None, + c: 0, + c1: 0, + c2: None, + c3: 0, + c4: None, + d: 0, + }; + assert_eq!(e, e); +}