summary refs log tree commit diff
path: root/src/libsyntax_ext
diff options
context:
space:
mode:
authorAlex Burka <aburka@seas.upenn.edu>2016-02-03 19:40:59 -0500
committerAlex Burka <aburka@seas.upenn.edu>2016-04-26 13:49:29 -0400
commit9249e6a1e2ef57bb6e329e6477beed31647236b1 (patch)
tree7c95b5476a6fa184d8fac7afaf11084762a1e719 /src/libsyntax_ext
parent42ea682fc495b6ec362db0aafba69e56c7aa0a9c (diff)
downloadrust-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.rs122
-rw-r--r--src/libsyntax_ext/deriving/decodable.rs2
-rw-r--r--src/libsyntax_ext/deriving/encodable.rs2
-rw-r--r--src/libsyntax_ext/deriving/generic/mod.rs2
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
     }