diff options
| author | Alex Burka <aburka@seas.upenn.edu> | 2016-02-03 19:40:59 -0500 |
|---|---|---|
| committer | Alex Burka <aburka@seas.upenn.edu> | 2016-04-26 13:49:29 -0400 |
| commit | 9249e6a1e2ef57bb6e329e6477beed31647236b1 (patch) | |
| tree | 7c95b5476a6fa184d8fac7afaf11084762a1e719 /src/libsyntax_ext | |
| parent | 42ea682fc495b6ec362db0aafba69e56c7aa0a9c (diff) | |
| download | rust-9249e6a1e2ef57bb6e329e6477beed31647236b1.tar.gz rust-9249e6a1e2ef57bb6e329e6477beed31647236b1.zip | |
shallow Clone for #[derive(Copy,Clone)]
Changes #[derive(Copy, Clone)] to use a faster impl of Clone when both derives are present, and there are no generics in the type. The faster impl is simply returning *self (which works because the type is also Copy). See the comments in libsyntax_ext/deriving/clone.rs for more details. There are a few types which are Copy but not Clone, in violation of the definition of Copy. These include large arrays and tuples. The very existence of these types is arguably a bug, but in order for this optimization not to change the applicability of #[derive(Copy, Clone)], the faster Clone impl also injects calls to a new function, core::clone::assert_receiver_is_clone, to verify that all members are actually Clone. This is not a breaking change, because pursuant to RFC 1521, any type that implements Copy should not do any observable work in its Clone impl.
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 a05bd7869b2..daa352275d8 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 b8ba1a58f21..56df0ff0e5b 100644 --- a/src/libsyntax_ext/deriving/generic/mod.rs +++ b/src/libsyntax_ext/deriving/generic/mod.rs @@ -858,6 +858,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); @@ -991,6 +992,7 @@ impl<'a> MethodDef<'a> { body = cx.expr_match(trait_.span, arg_expr.clone(), vec!( cx.arm(trait_.span, vec!(pat.clone()), body) )) } + body } |
