diff options
| author | bors <bors@rust-lang.org> | 2016-04-26 14:54:37 -0700 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2016-04-26 14:54:37 -0700 |
| commit | 8f552181895921a377ab8c235340ea3d80ec7fc0 (patch) | |
| tree | 92395c4d4b72986babdc2b9e53646229c39cccc1 /src/libsyntax_ext | |
| parent | 897199a0fbe01ab6f7e7f8096656603e238e85a2 (diff) | |
| parent | 9249e6a1e2ef57bb6e329e6477beed31647236b1 (diff) | |
| download | rust-8f552181895921a377ab8c235340ea3d80ec7fc0.tar.gz rust-8f552181895921a377ab8c235340ea3d80ec7fc0.zip | |
Auto merge of #31414 - durka:clone-copy, r=alexcrichton
special-case #[derive(Copy, Clone)] with a shallow clone If a type is Copy then its Clone implementation can be a no-op. Currently `#[derive(Clone)]` generates a deep clone anyway. This can lead to lots of code bloat. This PR detects the case where Copy and Clone are both being derived (the general case of "is this type Copy" can't be determined by a syntax extension) and generates the shallow Clone impl. Right now this can only be done if there are no type parameters (see https://github.com/rust-lang/rust/issues/31085#issuecomment-178988663), but this restriction can be removed after specialization. Fixes #31085.
Diffstat (limited to 'src/libsyntax_ext')
| -rw-r--r-- | src/libsyntax_ext/deriving/clone.rs | 122 | ||||
| -rw-r--r-- | src/libsyntax_ext/deriving/decodable.rs | 2 | ||||
| -rw-r--r-- | src/libsyntax_ext/deriving/encodable.rs | 2 | ||||
| -rw-r--r-- | src/libsyntax_ext/deriving/generic/mod.rs | 2 |
4 files changed, 96 insertions, 32 deletions
diff --git a/src/libsyntax_ext/deriving/clone.rs b/src/libsyntax_ext/deriving/clone.rs index 0085182d1ac..c81198d4729 100644 --- a/src/libsyntax_ext/deriving/clone.rs +++ b/src/libsyntax_ext/deriving/clone.rs @@ -11,26 +11,68 @@ use deriving::generic::*; use deriving::generic::ty::*; -use syntax::ast::{MetaItem, Expr, VariantData}; +use syntax::ast::{Expr, ItemKind, Generics, MetaItem, VariantData}; +use syntax::attr::{self, AttrMetaMethods}; use syntax::codemap::Span; use syntax::ext::base::{ExtCtxt, Annotatable}; use syntax::ext::build::AstBuilder; use syntax::parse::token::InternedString; use syntax::ptr::P; +#[derive(PartialEq)] +enum Mode { Deep, Shallow } + pub fn expand_deriving_clone(cx: &mut ExtCtxt, span: Span, mitem: &MetaItem, item: &Annotatable, push: &mut FnMut(Annotatable)) { + // check if we can use a short form + // + // the short form is `fn clone(&self) -> Self { *self }` + // + // we can use the short form if: + // - the item is Copy (unfortunately, all we can check is whether it's also deriving Copy) + // - there are no generic parameters (after specialization this limitation can be removed) + // if we used the short form with generics, we'd have to bound the generics with + // Clone + Copy, and then there'd be no Clone impl at all if the user fills in something + // that is Clone but not Copy. and until specialization we can't write both impls. + let bounds; + let substructure; + match *item { + Annotatable::Item(ref annitem) => { + match annitem.node { + ItemKind::Struct(_, Generics { ref ty_params, .. }) | + ItemKind::Enum(_, Generics { ref ty_params, .. }) + if ty_params.is_empty() + && attr::contains_name(&annitem.attrs, "derive_Copy") => { + + bounds = vec![Literal(path_std!(cx, core::marker::Copy))]; + substructure = combine_substructure(Box::new(|c, s, sub| { + cs_clone("Clone", c, s, sub, Mode::Shallow) + })); + } + + _ => { + bounds = vec![]; + substructure = combine_substructure(Box::new(|c, s, sub| { + cs_clone("Clone", c, s, sub, Mode::Deep) + })); + } + } + } + + _ => cx.span_bug(span, "#[derive(Clone)] on trait item or impl item") + } + let inline = cx.meta_word(span, InternedString::new("inline")); let attrs = vec!(cx.attribute(span, inline)); let trait_def = TraitDef { span: span, attributes: Vec::new(), path: path_std!(cx, core::clone::Clone), - additional_bounds: Vec::new(), + additional_bounds: bounds, generics: LifetimeBounds::empty(), is_unsafe: false, methods: vec!( @@ -42,9 +84,7 @@ pub fn expand_deriving_clone(cx: &mut ExtCtxt, ret_ty: Self_, attributes: attrs, is_unsafe: false, - combine_substructure: combine_substructure(Box::new(|c, s, sub| { - cs_clone("Clone", c, s, sub) - })), + combine_substructure: substructure, } ), associated_types: Vec::new(), @@ -56,14 +96,24 @@ pub fn expand_deriving_clone(cx: &mut ExtCtxt, fn cs_clone( name: &str, cx: &mut ExtCtxt, trait_span: Span, - substr: &Substructure) -> P<Expr> { + substr: &Substructure, + mode: Mode) -> P<Expr> { let ctor_path; let all_fields; - let fn_path = cx.std_path(&["clone", "Clone", "clone"]); + let fn_path = match mode { + Mode::Shallow => cx.std_path(&["clone", "assert_receiver_is_clone"]), + Mode::Deep => cx.std_path(&["clone", "Clone", "clone"]), + }; let subcall = |field: &FieldInfo| { let args = vec![cx.expr_addr_of(field.span, field.self_.clone())]; - cx.expr_call_global(field.span, fn_path.clone(), args) + let span = if mode == Mode::Shallow { + // set the expn ID so we can call the unstable method + Span { expn_id: cx.backtrace(), .. trait_span } + } else { + field.span + }; + cx.expr_call_global(span, fn_path.clone(), args) }; let vdata; @@ -89,29 +139,41 @@ fn cs_clone( } } - match *vdata { - VariantData::Struct(..) => { - let fields = all_fields.iter().map(|field| { - let ident = match field.name { - Some(i) => i, - None => { - cx.span_bug(trait_span, - &format!("unnamed field in normal struct in \ - `derive({})`", name)) - } - }; - cx.field_imm(field.span, ident, subcall(field)) - }).collect::<Vec<_>>(); - - cx.expr_struct(trait_span, ctor_path, fields) + match mode { + Mode::Shallow => { + cx.expr_block(cx.block(trait_span, + all_fields.iter() + .map(subcall) + .map(|e| cx.stmt_expr(e)) + .collect(), + Some(cx.expr_deref(trait_span, cx.expr_self(trait_span))))) } - VariantData::Tuple(..) => { - let subcalls = all_fields.iter().map(subcall).collect(); - let path = cx.expr_path(ctor_path); - cx.expr_call(trait_span, path, subcalls) - } - VariantData::Unit(..) => { - cx.expr_path(ctor_path) + Mode::Deep => { + match *vdata { + VariantData::Struct(..) => { + let fields = all_fields.iter().map(|field| { + let ident = match field.name { + Some(i) => i, + None => { + cx.span_bug(trait_span, + &format!("unnamed field in normal struct in \ + `derive({})`", name)) + } + }; + cx.field_imm(field.span, ident, subcall(field)) + }).collect::<Vec<_>>(); + + cx.expr_struct(trait_span, ctor_path, fields) + } + VariantData::Tuple(..) => { + let subcalls = all_fields.iter().map(subcall).collect(); + let path = cx.expr_path(ctor_path); + cx.expr_call(trait_span, path, subcalls) + } + VariantData::Unit(..) => { + cx.expr_path(ctor_path) + } + } } } } diff --git a/src/libsyntax_ext/deriving/decodable.rs b/src/libsyntax_ext/deriving/decodable.rs index 49f14c937e9..9387cf05ac7 100644 --- a/src/libsyntax_ext/deriving/decodable.rs +++ b/src/libsyntax_ext/deriving/decodable.rs @@ -74,7 +74,7 @@ fn expand_deriving_decodable_imp(cx: &mut ExtCtxt, }, explicit_self: None, args: vec!(Ptr(Box::new(Literal(Path::new_local(typaram))), - Borrowed(None, Mutability::Mutable))), + Borrowed(None, Mutability::Mutable))), ret_ty: Literal(Path::new_( pathvec_std!(cx, core::result::Result), None, diff --git a/src/libsyntax_ext/deriving/encodable.rs b/src/libsyntax_ext/deriving/encodable.rs index 6e47f2dd08f..5b47d8da8b6 100644 --- a/src/libsyntax_ext/deriving/encodable.rs +++ b/src/libsyntax_ext/deriving/encodable.rs @@ -150,7 +150,7 @@ fn expand_deriving_encodable_imp(cx: &mut ExtCtxt, }, explicit_self: borrowed_explicit_self(), args: vec!(Ptr(Box::new(Literal(Path::new_local(typaram))), - Borrowed(None, Mutability::Mutable))), + Borrowed(None, Mutability::Mutable))), ret_ty: Literal(Path::new_( pathvec_std!(cx, core::result::Result), None, diff --git a/src/libsyntax_ext/deriving/generic/mod.rs b/src/libsyntax_ext/deriving/generic/mod.rs index 5251b0d08d4..1d5fc13c720 100644 --- a/src/libsyntax_ext/deriving/generic/mod.rs +++ b/src/libsyntax_ext/deriving/generic/mod.rs @@ -857,6 +857,7 @@ impl<'a> MethodDef<'a> { explicit_self: ast::ExplicitSelf, arg_types: Vec<(Ident, P<ast::Ty>)> , body: P<Expr>) -> ast::ImplItem { + // create the generics that aren't for Self let fn_generics = self.generics.to_generics(cx, trait_.span, type_ident, generics); @@ -990,6 +991,7 @@ impl<'a> MethodDef<'a> { body = cx.expr_match(trait_.span, arg_expr.clone(), vec!( cx.arm(trait_.span, vec!(pat.clone()), body) )) } + body } |
