Skip to content

Commit 97ff51d

Browse files
committed
Rollup merge of rust-lang#21237 - erickt:derive-assoc-types, r=erickt
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(), } } } ```
2 parents d4ba1ca + 9cabe27 commit 97ff51d

File tree

4 files changed

+358
-6
lines changed

4 files changed

+358
-6
lines changed

src/libsyntax/ext/deriving/generic/mod.rs

+118-6
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,46 @@ pub fn combine_substructure<'a>(f: CombineSubstructureFunc<'a>)
332332
RefCell::new(f)
333333
}
334334

335+
/// This method helps to extract all the type parameters referenced from a
336+
/// type. For a type parameter `<T>`, it looks for either a `TyPath` that
337+
/// is not global and starts with `T`, or a `TyQPath`.
338+
fn find_type_parameters(ty: &ast::Ty, ty_param_names: &[ast::Name]) -> Vec<P<ast::Ty>> {
339+
use visit;
340+
341+
struct Visitor<'a> {
342+
ty_param_names: &'a [ast::Name],
343+
types: Vec<P<ast::Ty>>,
344+
}
345+
346+
impl<'a> visit::Visitor<'a> for Visitor<'a> {
347+
fn visit_ty(&mut self, ty: &'a ast::Ty) {
348+
match ty.node {
349+
ast::TyPath(_, ref path) if !path.global => {
350+
match path.segments.first() {
351+
Some(segment) => {
352+
if self.ty_param_names.contains(&segment.identifier.name) {
353+
self.types.push(P(ty.clone()));
354+
}
355+
}
356+
None => {}
357+
}
358+
}
359+
_ => {}
360+
}
361+
362+
visit::walk_ty(self, ty)
363+
}
364+
}
365+
366+
let mut visitor = Visitor {
367+
ty_param_names: ty_param_names,
368+
types: Vec::new(),
369+
};
370+
371+
visit::Visitor::visit_ty(&mut visitor, ty);
372+
373+
visitor.types
374+
}
335375

336376
impl<'a> TraitDef<'a> {
337377
pub fn expand<F>(&self,
@@ -374,18 +414,42 @@ impl<'a> TraitDef<'a> {
374414
}))
375415
}
376416

377-
/// Given that we are deriving a trait `Tr` for a type `T<'a, ...,
378-
/// 'z, A, ..., Z>`, creates an impl like:
417+
/// Given that we are deriving a trait `DerivedTrait` for a type like:
379418
///
380419
/// ```ignore
381-
/// impl<'a, ..., 'z, A:Tr B1 B2, ..., Z: Tr B1 B2> Tr for T<A, ..., Z> { ... }
420+
/// struct Struct<'a, ..., 'z, A, B: DeclaredTrait, C, ..., Z> where C: WhereTrait {
421+
/// a: A,
422+
/// b: B::Item,
423+
/// b1: <B as DeclaredTrait>::Item,
424+
/// c1: <C as WhereTrait>::Item,
425+
/// c2: Option<<C as WhereTrait>::Item>,
426+
/// ...
427+
/// }
428+
/// ```
429+
///
430+
/// create an impl like:
431+
///
432+
/// ```ignore
433+
/// impl<'a, ..., 'z, A, B: DeclaredTrait, C, ... Z> where
434+
/// C: WhereTrait,
435+
/// A: DerivedTrait + B1 + ... + BN,
436+
/// B: DerivedTrait + B1 + ... + BN,
437+
/// C: DerivedTrait + B1 + ... + BN,
438+
/// B::Item: DerivedTrait + B1 + ... + BN,
439+
/// <C as WhereTrait>::Item: DerivedTrait + B1 + ... + BN,
440+
/// ...
441+
/// {
442+
/// ...
443+
/// }
382444
/// ```
383445
///
384-
/// where B1, B2, ... are the bounds given by `bounds_paths`.'
446+
/// where B1, ..., BN are the bounds given by `bounds_paths`.'. Z is a phantom type, and
447+
/// therefore does not get bound by the derived trait.
385448
fn create_derived_impl(&self,
386449
cx: &mut ExtCtxt,
387450
type_ident: Ident,
388451
generics: &Generics,
452+
field_tys: Vec<P<ast::Ty>>,
389453
methods: Vec<P<ast::ImplItem>>) -> P<ast::Item> {
390454
let trait_path = self.path.to_path(cx, self.span, type_ident, generics);
391455

@@ -466,6 +530,35 @@ impl<'a> TraitDef<'a> {
466530
}
467531
}));
468532

533+
if !ty_params.is_empty() {
534+
let ty_param_names: Vec<ast::Name> = ty_params.iter()
535+
.map(|ty_param| ty_param.ident.name)
536+
.collect();
537+
538+
for field_ty in field_tys.into_iter() {
539+
let tys = find_type_parameters(&*field_ty, &ty_param_names);
540+
541+
for ty in tys.into_iter() {
542+
let mut bounds: Vec<_> = self.additional_bounds.iter().map(|p| {
543+
cx.typarambound(p.to_path(cx, self.span, type_ident, generics))
544+
}).collect();
545+
546+
// require the current trait
547+
bounds.push(cx.typarambound(trait_path.clone()));
548+
549+
let predicate = ast::WhereBoundPredicate {
550+
span: self.span,
551+
bound_lifetimes: vec![],
552+
bounded_ty: ty,
553+
bounds: OwnedSlice::from_vec(bounds),
554+
};
555+
556+
let predicate = ast::WherePredicate::BoundPredicate(predicate);
557+
where_clause.predicates.push(predicate);
558+
}
559+
}
560+
}
561+
469562
let trait_generics = Generics {
470563
lifetimes: lifetimes,
471564
ty_params: OwnedSlice::from_vec(ty_params),
@@ -518,6 +611,10 @@ impl<'a> TraitDef<'a> {
518611
struct_def: &StructDef,
519612
type_ident: Ident,
520613
generics: &Generics) -> P<ast::Item> {
614+
let field_tys: Vec<P<ast::Ty>> = struct_def.fields.iter()
615+
.map(|field| field.node.ty.clone())
616+
.collect();
617+
521618
let methods = self.methods.iter().map(|method_def| {
522619
let (explicit_self, self_args, nonself_args, tys) =
523620
method_def.split_self_nonself_args(
@@ -550,14 +647,29 @@ impl<'a> TraitDef<'a> {
550647
body)
551648
}).collect();
552649

553-
self.create_derived_impl(cx, type_ident, generics, methods)
650+
self.create_derived_impl(cx, type_ident, generics, field_tys, methods)
554651
}
555652

556653
fn expand_enum_def(&self,
557654
cx: &mut ExtCtxt,
558655
enum_def: &EnumDef,
559656
type_ident: Ident,
560657
generics: &Generics) -> P<ast::Item> {
658+
let mut field_tys = Vec::new();
659+
660+
for variant in enum_def.variants.iter() {
661+
match variant.node.kind {
662+
ast::VariantKind::TupleVariantKind(ref args) => {
663+
field_tys.extend(args.iter()
664+
.map(|arg| arg.ty.clone()));
665+
}
666+
ast::VariantKind::StructVariantKind(ref args) => {
667+
field_tys.extend(args.fields.iter()
668+
.map(|field| field.node.ty.clone()));
669+
}
670+
}
671+
}
672+
561673
let methods = self.methods.iter().map(|method_def| {
562674
let (explicit_self, self_args, nonself_args, tys) =
563675
method_def.split_self_nonself_args(cx, self,
@@ -590,7 +702,7 @@ impl<'a> TraitDef<'a> {
590702
body)
591703
}).collect();
592704

593-
self.create_derived_impl(cx, type_ident, generics, methods)
705+
self.create_derived_impl(cx, type_ident, generics, field_tys, methods)
594706
}
595707
}
596708

src/test/auxiliary/struct_variant_xc_aux.rs

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#![crate_name="struct_variant_xc_aux"]
1212
#![crate_type = "lib"]
1313

14+
#[derive(Copy)]
1415
pub enum Enum {
1516
Variant(u8),
1617
StructVariant { arg: u8 }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
trait Foo {
12+
type X;
13+
fn method(&self) {}
14+
}
15+
16+
#[derive(Clone)]
17+
struct Bar<T: Foo> {
18+
x: T::X,
19+
}
20+
21+
struct NotClone;
22+
23+
impl Foo for NotClone {
24+
type X = i8;
25+
}
26+
27+
fn main() {
28+
Bar::<NotClone> { x: 1 }.clone(); //~ ERROR
29+
}

0 commit comments

Comments
 (0)