about summary refs log tree commit diff
diff options
context:
space:
mode:
authorPatrick Walton <pcwalton@mimiga.net>2013-03-04 15:33:05 -0800
committerPatrick Walton <pcwalton@mimiga.net>2013-03-04 16:13:15 -0800
commit0643df28a33f122af9cfbdf49970ab55ae08f106 (patch)
tree23b04bf3b4b35b486efcfb6793951f54deefd975
parentc639a78dc4a9be4e5db0265cf8e73be28ce922f6 (diff)
downloadrust-0643df28a33f122af9cfbdf49970ab55ae08f106.tar.gz
rust-0643df28a33f122af9cfbdf49970ab55ae08f106.zip
libsyntax: Implement `#[deriving_clone]`
-rw-r--r--src/libsyntax/ext/base.rs3
-rw-r--r--src/libsyntax/ext/deriving.rs301
-rw-r--r--src/test/run-pass/deriving-clone-enum.rs9
-rw-r--r--src/test/run-pass/deriving-clone-generic-enum.rs9
-rw-r--r--src/test/run-pass/deriving-clone-generic-struct.rs9
-rw-r--r--src/test/run-pass/deriving-clone-generic-tuple-struct.rs5
-rw-r--r--src/test/run-pass/deriving-clone-struct.rs8
-rw-r--r--src/test/run-pass/deriving-clone-tuple-struct.rs5
8 files changed, 333 insertions, 16 deletions
diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs
index b3d3358e586..01439fb40fb 100644
--- a/src/libsyntax/ext/base.rs
+++ b/src/libsyntax/ext/base.rs
@@ -155,6 +155,9 @@ pub fn syntax_expander_table() -> SyntaxEnv {
     syntax_expanders.insert(@~"deriving_iter_bytes",
                             @SE(ItemDecorator(
                                 ext::deriving::expand_deriving_iter_bytes)));
+    syntax_expanders.insert(@~"deriving_clone",
+                            @SE(ItemDecorator(
+                                ext::deriving::expand_deriving_clone)));
 
     // Quasi-quoting expanders
     syntax_expanders.insert(@~"quote_tokens",
diff --git a/src/libsyntax/ext/deriving.rs b/src/libsyntax/ext/deriving.rs
index 50047d2ce41..f5dbf269138 100644
--- a/src/libsyntax/ext/deriving.rs
+++ b/src/libsyntax/ext/deriving.rs
@@ -15,7 +15,7 @@ use core::prelude::*;
 
 use ast;
 use ast::{TraitTyParamBound, Ty, and, bind_by_ref, binop, deref, enum_def};
-use ast::{enum_variant_kind, expr, expr_match, ident, item, item_};
+use ast::{enum_variant_kind, expr, expr_match, ident, impure_fn, item, item_};
 use ast::{item_enum, item_impl, item_struct, Generics};
 use ast::{m_imm, meta_item, method};
 use ast::{named_field, or, pat, pat_ident, pat_wild, public, pure_fn};
@@ -84,6 +84,18 @@ pub fn expand_deriving_iter_bytes(cx: ext_ctxt,
                     expand_deriving_iter_bytes_enum_def)
 }
 
+pub fn expand_deriving_clone(cx: ext_ctxt,
+                             span: span,
+                             _: @meta_item,
+                             in_items: ~[@item])
+                          -> ~[@item] {
+    expand_deriving(cx,
+                    span,
+                    in_items,
+                    expand_deriving_clone_struct_def,
+                    expand_deriving_clone_enum_def)
+}
+
 fn expand_deriving(cx: ext_ctxt,
                    span: span,
                    in_items: ~[@item],
@@ -303,6 +315,21 @@ fn create_derived_iter_bytes_impl(cx: ext_ctxt,
     create_derived_impl(cx, span, type_ident, generics, methods, trait_path)
 }
 
+fn create_derived_clone_impl(cx: ext_ctxt,
+                             span: span,
+                             type_ident: ident,
+                             generics: &Generics,
+                             method: @method)
+                          -> @item {
+    let methods = [ method ];
+    let trait_path = [
+        cx.ident_of(~"core"),
+        cx.ident_of(~"clone"),
+        cx.ident_of(~"Clone"),
+    ];
+    create_derived_impl(cx, span, type_ident, generics, methods, trait_path)
+}
+
 // Creates a method from the given set of statements conforming to the
 // signature of the `iter_bytes` method.
 fn create_iter_bytes_method(cx: ext_ctxt,
@@ -352,6 +379,58 @@ fn create_iter_bytes_method(cx: ext_ctxt,
     }
 }
 
+// Creates a method from the given expression conforming to the signature of
+// the `clone` method.
+fn create_clone_method(cx: ext_ctxt,
+                       span: span,
+                       +type_ident: ast::ident,
+                       generics: &Generics,
+                       expr: @ast::expr)
+                    -> @method {
+    // Create the type parameters of the return value.
+    let mut output_ty_params = ~[];
+    for generics.ty_params.each |ty_param| {
+        let path = build::mk_ty_path(cx, span, ~[ ty_param.ident ]);
+        output_ty_params.push(path);
+    }
+
+    // Create the type of the return value.
+    let output_type_path = build::mk_raw_path_(span,
+                                               ~[ type_ident ],
+                                               output_ty_params);
+    let output_type = ast::ty_path(output_type_path, cx.next_id());
+    let output_type = @ast::Ty {
+        id: cx.next_id(),
+        node: output_type,
+        span: span
+    };
+
+    // Create the function declaration.
+    let fn_decl = build::mk_fn_decl(~[], output_type);
+
+    // Create the body block.
+    let body_block = build::mk_simple_block(cx, span, expr);
+
+    // Create the self type and method identifier.
+    let self_ty = spanned { node: sty_region(m_imm), span: span };
+    let method_ident = cx.ident_of(~"clone");
+
+    // Create the method.
+    @ast::method {
+        ident: method_ident,
+        attrs: ~[],
+        generics: ast_util::empty_generics(),
+        self_ty: self_ty,
+        purity: impure_fn,
+        decl: fn_decl,
+        body: body_block,
+        id: cx.next_id(),
+        span: span,
+        self_id: cx.next_id(),
+        vis: public,
+    }
+}
+
 fn create_subpatterns(cx: ext_ctxt,
                       span: span,
                       prefix: ~str,
@@ -372,6 +451,15 @@ fn create_subpatterns(cx: ext_ctxt,
     return dvec::unwrap(subpats);
 }
 
+fn is_struct_tuple(struct_def: &struct_def) -> bool {
+    struct_def.fields.len() > 0 && struct_def.fields.all(|f| {
+        match f.node.kind {
+            named_field(*) => false,
+            unnamed_field => true
+        }
+    })
+}
+
 fn create_enum_variant_pattern(cx: ext_ctxt,
                                span: span,
                                variant: &variant,
@@ -488,6 +576,16 @@ fn call_substructure_iter_bytes_method(cx: ext_ctxt,
     build::mk_stmt(cx, span, self_call)
 }
 
+fn call_substructure_clone_method(cx: ext_ctxt,
+                                  span: span,
+                                  self_field: @expr)
+                               -> @expr {
+    // Call the substructure method.
+    let clone_ident = cx.ident_of(~"clone");
+    let self_method = build::mk_access_(cx, span, self_field, clone_ident);
+    build::mk_call_(cx, span, self_method, ~[])
+}
+
 fn variant_arg_count(cx: ext_ctxt, span: span, variant: &variant) -> uint {
     match variant.node.kind {
         tuple_variant_kind(ref args) => args.len(),
@@ -508,21 +606,12 @@ fn expand_deriving_eq_struct_def(cx: ext_ctxt,
     let eq_ident = cx.ident_of(~"eq");
     let ne_ident = cx.ident_of(~"ne");
 
-    let is_struct_tuple =
-    struct_def.fields.len() > 0 && struct_def.fields.all(|f| {
-        match f.node.kind {
-            named_field(*) => false,
-            unnamed_field => true
-        }
-    });
-
-    let derive_struct_fn = if is_struct_tuple {
+    let derive_struct_fn = if is_struct_tuple(struct_def) {
         expand_deriving_eq_struct_tuple_method
     } else {
         expand_deriving_eq_struct_method
     };
 
-
     let eq_method = derive_struct_fn(cx,
                                      span,
                                      struct_def,
@@ -618,6 +707,48 @@ fn expand_deriving_iter_bytes_enum_def(cx: ext_ctxt,
                                           method);
 }
 
+fn expand_deriving_clone_struct_def(cx: ext_ctxt,
+                                    span: span,
+                                    struct_def: &struct_def,
+                                    type_ident: ident,
+                                    generics: &Generics)
+                                 -> @item {
+    // Create the method.
+    let method = if !is_struct_tuple(struct_def) {
+        expand_deriving_clone_struct_method(cx,
+                                            span,
+                                            struct_def,
+                                            type_ident,
+                                            generics)
+    } else {
+        expand_deriving_clone_tuple_struct_method(cx,
+                                                  span,
+                                                  struct_def,
+                                                  type_ident,
+                                                  generics)
+    };
+
+    // Create the implementation.
+    create_derived_clone_impl(cx, span, type_ident, generics, method)
+}
+
+fn expand_deriving_clone_enum_def(cx: ext_ctxt,
+                                  span: span,
+                                  enum_definition: &enum_def,
+                                  type_ident: ident,
+                                  generics: &Generics)
+                               -> @item {
+    // Create the method.
+    let method = expand_deriving_clone_enum_method(cx,
+                                                   span,
+                                                   enum_definition,
+                                                   type_ident,
+                                                   generics);
+
+    // Create the implementation.
+    create_derived_clone_impl(cx, span, type_ident, generics, method)
+}
+
 fn expand_deriving_eq_struct_method(cx: ext_ctxt,
                                     span: span,
                                     struct_def: &struct_def,
@@ -709,6 +840,93 @@ fn expand_deriving_iter_bytes_struct_method(cx: ext_ctxt,
     return create_iter_bytes_method(cx, span, statements);
 }
 
+fn expand_deriving_clone_struct_method(cx: ext_ctxt,
+                                       span: span,
+                                       struct_def: &struct_def,
+                                       type_ident: ident,
+                                       generics: &Generics)
+                                    -> @method {
+    let self_ident = cx.ident_of(~"self");
+
+    // Create the new fields.
+    let mut fields = ~[];
+    for struct_def.fields.each |struct_field| {
+        match struct_field.node.kind {
+            named_field(ident, _, _) => {
+                // Create the accessor for this field.
+                let self_field = build::mk_access(cx,
+                                                  span,
+                                                  ~[ self_ident ],
+                                                  ident);
+
+                // Call the substructure method.
+                let call = call_substructure_clone_method(cx,
+                                                          span,
+                                                          self_field);
+
+                let field = build::Field { ident: ident, ex: call };
+                fields.push(field);
+            }
+            unnamed_field => {
+                cx.span_bug(span,
+                            ~"unnamed fields in \
+                              expand_deriving_clone_struct_method");
+            }
+        }
+    }
+
+    // Create the struct literal.
+    let struct_literal = build::mk_struct_e(cx,
+                                            span,
+                                            ~[ type_ident ],
+                                            fields);
+    create_clone_method(cx, span, type_ident, generics, struct_literal)
+}
+
+fn expand_deriving_clone_tuple_struct_method(cx: ext_ctxt,
+                                             span: span,
+                                             struct_def: &struct_def,
+                                             type_ident: ident,
+                                             generics: &Generics)
+                                          -> @method {
+    // Create the pattern for the match.
+    let matching_path = build::mk_raw_path(span, ~[ type_ident ]);
+    let field_count = struct_def.fields.len();
+    let subpats = create_subpatterns(cx, span, ~"__self", field_count);
+    let pat = build::mk_pat_enum(cx, span, matching_path, subpats);
+
+    // Create the new fields.
+    let mut subcalls = ~[];
+    for uint::range(0, struct_def.fields.len()) |i| {
+        // Create the expression for this field.
+        let field_ident = cx.ident_of(~"__self" + i.to_str());
+        let field = build::mk_path(cx, span, ~[ field_ident ]);
+
+        // Call the substructure method.
+        let subcall = call_substructure_clone_method(cx, span, field);
+        subcalls.push(subcall);
+    }
+
+    // Create the call to the struct constructor.
+    let call = build::mk_call(cx, span, ~[ type_ident ], subcalls);
+
+    // Create the pattern body.
+    let match_body_block = build::mk_simple_block(cx, span, call);
+
+    // Create the arm.
+    let arm = ast::arm {
+        pats: ~[ pat ],
+        guard: None,
+        body: match_body_block
+    };
+
+    // Create the method body.
+    let self_match_expr = expand_enum_or_struct_match(cx, span, ~[ arm ]);
+
+    // Create the method.
+    create_clone_method(cx, span, type_ident, generics, self_match_expr)
+}
+
 fn expand_deriving_eq_enum_method(cx: ext_ctxt,
                                   span: span,
                                   enum_definition: &enum_def,
@@ -904,6 +1122,17 @@ fn expand_deriving_eq_struct_tuple_method(cx: ext_ctxt,
         type_ident, generics, self_match_expr)
 }
 
+fn expand_enum_or_struct_match(cx: ext_ctxt,
+                               span: span,
+                               arms: ~[ ast::arm ])
+                            -> @expr {
+    let self_ident = cx.ident_of(~"self");
+    let self_expr = build::mk_path(cx, span, ~[ self_ident ]);
+    let self_expr = build::mk_unary(cx, span, deref, self_expr);
+    let self_match_expr = expr_match(self_expr, arms);
+    build::mk_expr(cx, span, self_match_expr)
+}
+
 fn expand_deriving_iter_bytes_enum_method(cx: ext_ctxt,
                                           span: span,
                                           enum_definition: &enum_def)
@@ -953,14 +1182,54 @@ fn expand_deriving_iter_bytes_enum_method(cx: ext_ctxt,
     };
 
     // Create the method body.
-    let self_ident = cx.ident_of(~"self");
-    let self_expr = build::mk_path(cx, span, ~[ self_ident ]);
-    let self_expr = build::mk_unary(cx, span, deref, self_expr);
-    let self_match_expr = expr_match(self_expr, arms);
-    let self_match_expr = build::mk_expr(cx, span, self_match_expr);
+    let self_match_expr = expand_enum_or_struct_match(cx, span, arms);
     let self_match_stmt = build::mk_stmt(cx, span, self_match_expr);
 
     // Create the method.
     create_iter_bytes_method(cx, span, ~[ self_match_stmt ])
 }
 
+fn expand_deriving_clone_enum_method(cx: ext_ctxt,
+                                     span: span,
+                                     enum_definition: &enum_def,
+                                     type_ident: ident,
+                                     generics: &Generics)
+                                  -> @method {
+    // Create the arms of the match in the method body.
+    let arms = do enum_definition.variants.map |variant| {
+        // Create the matching pattern.
+        let pat = create_enum_variant_pattern(cx, span, variant, ~"__self");
+
+        // Iterate over the variant arguments, creating the subcalls.
+        let mut subcalls = ~[];
+        for uint::range(0, variant_arg_count(cx, span, variant)) |j| {
+            // Create the expression for this field.
+            let field_ident = cx.ident_of(~"__self" + j.to_str());
+            let field = build::mk_path(cx, span, ~[ field_ident ]);
+
+            // Call the substructure method.
+            let subcall = call_substructure_clone_method(cx, span, field);
+            subcalls.push(subcall);
+        }
+
+        // Create the call to the enum variant (if necessary).
+        let call = if subcalls.len() > 0 {
+            build::mk_call(cx, span, ~[ variant.node.name ], subcalls)
+        } else {
+            build::mk_path(cx, span, ~[ variant.node.name ])
+        };
+
+        // Create the pattern body.
+        let match_body_block = build::mk_simple_block(cx, span, call);
+
+        // Create the arm.
+        ast::arm { pats: ~[ pat ], guard: None, body: match_body_block }
+    };
+
+    // Create the method body.
+    let self_match_expr = expand_enum_or_struct_match(cx, span, arms);
+
+    // Create the method.
+    create_clone_method(cx, span, type_ident, generics, self_match_expr)
+}
+
diff --git a/src/test/run-pass/deriving-clone-enum.rs b/src/test/run-pass/deriving-clone-enum.rs
new file mode 100644
index 00000000000..bad83f41bac
--- /dev/null
+++ b/src/test/run-pass/deriving-clone-enum.rs
@@ -0,0 +1,9 @@
+#[deriving_clone]
+enum E {
+    A,
+    B(()),
+    C
+}
+
+fn main() {}
+
diff --git a/src/test/run-pass/deriving-clone-generic-enum.rs b/src/test/run-pass/deriving-clone-generic-enum.rs
new file mode 100644
index 00000000000..c70e644e2a8
--- /dev/null
+++ b/src/test/run-pass/deriving-clone-generic-enum.rs
@@ -0,0 +1,9 @@
+#[deriving_clone]
+enum E<T,U> {
+    A(T),
+    B(T,U),
+    C
+}
+
+fn main() {}
+
diff --git a/src/test/run-pass/deriving-clone-generic-struct.rs b/src/test/run-pass/deriving-clone-generic-struct.rs
new file mode 100644
index 00000000000..73fb3ad8d64
--- /dev/null
+++ b/src/test/run-pass/deriving-clone-generic-struct.rs
@@ -0,0 +1,9 @@
+#[deriving_clone]
+struct S<T> {
+    foo: (),
+    bar: (),
+    baz: T,
+}
+
+fn main() {}
+
diff --git a/src/test/run-pass/deriving-clone-generic-tuple-struct.rs b/src/test/run-pass/deriving-clone-generic-tuple-struct.rs
new file mode 100644
index 00000000000..d7b15d63280
--- /dev/null
+++ b/src/test/run-pass/deriving-clone-generic-tuple-struct.rs
@@ -0,0 +1,5 @@
+#[deriving_clone]
+struct S<T>(T, ());
+
+fn main() {}
+
diff --git a/src/test/run-pass/deriving-clone-struct.rs b/src/test/run-pass/deriving-clone-struct.rs
new file mode 100644
index 00000000000..2f371c8920b
--- /dev/null
+++ b/src/test/run-pass/deriving-clone-struct.rs
@@ -0,0 +1,8 @@
+#[deriving_clone]
+struct S {
+    foo: (),
+    bar: ()
+}
+
+fn main() {}
+
diff --git a/src/test/run-pass/deriving-clone-tuple-struct.rs b/src/test/run-pass/deriving-clone-tuple-struct.rs
new file mode 100644
index 00000000000..a1a79613d40
--- /dev/null
+++ b/src/test/run-pass/deriving-clone-tuple-struct.rs
@@ -0,0 +1,5 @@
+#[deriving_clone]
+struct S((), ());
+
+fn main() {}
+