From 0643df28a33f122af9cfbdf49970ab55ae08f106 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Mon, 4 Mar 2013 15:33:05 -0800 Subject: [PATCH] libsyntax: Implement `#[deriving_clone]` --- src/libsyntax/ext/base.rs | 3 + src/libsyntax/ext/deriving.rs | 301 +++++++++++++++++- src/test/run-pass/deriving-clone-enum.rs | 9 + .../run-pass/deriving-clone-generic-enum.rs | 9 + .../run-pass/deriving-clone-generic-struct.rs | 9 + .../deriving-clone-generic-tuple-struct.rs | 5 + src/test/run-pass/deriving-clone-struct.rs | 8 + .../run-pass/deriving-clone-tuple-struct.rs | 5 + 8 files changed, 333 insertions(+), 16 deletions(-) create mode 100644 src/test/run-pass/deriving-clone-enum.rs create mode 100644 src/test/run-pass/deriving-clone-generic-enum.rs create mode 100644 src/test/run-pass/deriving-clone-generic-struct.rs create mode 100644 src/test/run-pass/deriving-clone-generic-tuple-struct.rs create mode 100644 src/test/run-pass/deriving-clone-struct.rs create mode 100644 src/test/run-pass/deriving-clone-tuple-struct.rs diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index b3d3358e5861e..01439fb40fb37 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 50047d2ce4166..f5dbf26913834 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 0000000000000..bad83f41bac65 --- /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 0000000000000..c70e644e2a860 --- /dev/null +++ b/src/test/run-pass/deriving-clone-generic-enum.rs @@ -0,0 +1,9 @@ +#[deriving_clone] +enum E { + 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 0000000000000..73fb3ad8d6477 --- /dev/null +++ b/src/test/run-pass/deriving-clone-generic-struct.rs @@ -0,0 +1,9 @@ +#[deriving_clone] +struct S { + 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 0000000000000..d7b15d63280d0 --- /dev/null +++ b/src/test/run-pass/deriving-clone-generic-tuple-struct.rs @@ -0,0 +1,5 @@ +#[deriving_clone] +struct S(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 0000000000000..2f371c8920be5 --- /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 0000000000000..a1a79613d401f --- /dev/null +++ b/src/test/run-pass/deriving-clone-tuple-struct.rs @@ -0,0 +1,5 @@ +#[deriving_clone] +struct S((), ()); + +fn main() {} +