From f8bd977868dcc81066be7fc4eb56adfba0ad6cde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20L=C3=B6bel?= Date: Sat, 21 Sep 2013 03:18:53 +0200 Subject: [PATCH] Added Copy trait and deriving mode --- src/libstd/copy.rs | 255 +++++++++++++++++++++++++++++ src/libstd/prelude.rs | 1 + src/libstd/std.rs | 1 + src/libsyntax/ext/deriving/copy.rs | 122 ++++++++++++++ src/libsyntax/ext/deriving/mod.rs | 4 + 5 files changed, 383 insertions(+) create mode 100644 src/libstd/copy.rs create mode 100644 src/libsyntax/ext/deriving/copy.rs diff --git a/src/libstd/copy.rs b/src/libstd/copy.rs new file mode 100644 index 0000000000000..509662f66b2d2 --- /dev/null +++ b/src/libstd/copy.rs @@ -0,0 +1,255 @@ +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/*! The Copy trait for types that cannot be "implicitly copied" + +In Rust, some simple types are "implicitly copyable" and when you +assign them or pass them as arguments, the receiver will get a copy, +leaving the original value in place. These types do not require +allocation to copy and do not have finalizers (i.e. they do not +contain owned boxes or implement `Drop`), so the compiler considers +them cheap and safe to copy. For other types copies must be made +explicitly, by convention implementing the `Copy` trait and calling +the `copy` method. + +*/ + +use std::kinds::Freeze; + +/// A common trait for copying an object. +pub trait Copy { + /// Returns a copy of the value. The contents of owned pointers + /// are copied to maintain uniqueness, while the contents of + /// managed pointers are not copied. + fn copy(&self) -> Self; +} + +impl Copy for ~T { + /// Return a deep copy of the owned box. + #[inline] + fn copy(&self) -> ~T { ~(**self).copy() } +} + +impl Copy for @T { + /// Return a shallow copy of the managed box. + #[inline] + fn copy(&self) -> @T { *self } +} + +impl Copy for @mut T { + /// Return a shallow copy of the managed box. + #[inline] + fn copy(&self) -> @mut T { *self } +} + +impl<'self, T> Copy for &'self T { + /// Return a shallow copy of the borrowed pointer. + #[inline] + fn copy(&self) -> &'self T { *self } +} + +impl<'self, T> Copy for &'self [T] { + /// Return a shallow copy of the slice. + #[inline] + fn copy(&self) -> &'self [T] { *self } +} + +impl<'self> Copy for &'self str { + /// Return a shallow copy of the slice. + #[inline] + fn copy(&self) -> &'self str { *self } +} + +macro_rules! copy_impl( + ($t:ty) => { + impl Copy for $t { + /// Return a deep copy of the value. + #[inline] + fn copy(&self) -> $t { *self } + } + } +) + +copy_impl!(int) +copy_impl!(i8) +copy_impl!(i16) +copy_impl!(i32) +copy_impl!(i64) + +copy_impl!(uint) +copy_impl!(u8) +copy_impl!(u16) +copy_impl!(u32) +copy_impl!(u64) + +copy_impl!(float) +copy_impl!(f32) +copy_impl!(f64) + +copy_impl!(()) +copy_impl!(bool) +copy_impl!(char) + +macro_rules! extern_fn_copy( + ($($A:ident),*) => ( + impl<$($A,)* ReturnType> Copy for extern "Rust" fn($($A),*) -> ReturnType { + /// Return a copy of a function pointer + #[inline] + fn copy(&self) -> extern "Rust" fn($($A),*) -> ReturnType { *self } + } + ) +) + +extern_fn_copy!() +extern_fn_copy!(A) +extern_fn_copy!(A, B) +extern_fn_copy!(A, B, C) +extern_fn_copy!(A, B, C, D) +extern_fn_copy!(A, B, C, D, E) +extern_fn_copy!(A, B, C, D, E, F) +extern_fn_copy!(A, B, C, D, E, F, G) +extern_fn_copy!(A, B, C, D, E, F, G, H) + +/// A trait distinct from `Copy` which represents "deep copies" of things like +/// managed boxes which would otherwise not be copied. +pub trait DeepCopy { + /// Return a deep copy of the value. Unlike `Copy`, the contents of shared pointer types + /// *are* copied. + fn deep_copy(&self) -> Self; +} + +impl DeepCopy for ~T { + /// Return a deep copy of the owned box. + #[inline] + fn deep_copy(&self) -> ~T { ~(**self).deep_copy() } +} + +// FIXME: #6525: should also be implemented for `T: Send + DeepCopy` +impl DeepCopy for @T { + /// Return a deep copy of the managed box. The `Freeze` trait is required to prevent performing + /// a deep copy of a potentially cyclical type. + #[inline] + fn deep_copy(&self) -> @T { @(**self).deep_copy() } +} + +// FIXME: #6525: should also be implemented for `T: Send + DeepCopy` +impl DeepCopy for @mut T { + /// Return a deep copy of the managed box. The `Freeze` trait is required to prevent performing + /// a deep copy of a potentially cyclical type. + #[inline] + fn deep_copy(&self) -> @mut T { @mut (**self).deep_copy() } +} + +macro_rules! deep_copy_impl( + ($t:ty) => { + impl DeepCopy for $t { + /// Return a deep copy of the value. + #[inline] + fn deep_copy(&self) -> $t { *self } + } + } +) + +deep_copy_impl!(int) +deep_copy_impl!(i8) +deep_copy_impl!(i16) +deep_copy_impl!(i32) +deep_copy_impl!(i64) + +deep_copy_impl!(uint) +deep_copy_impl!(u8) +deep_copy_impl!(u16) +deep_copy_impl!(u32) +deep_copy_impl!(u64) + +deep_copy_impl!(float) +deep_copy_impl!(f32) +deep_copy_impl!(f64) + +deep_copy_impl!(()) +deep_copy_impl!(bool) +deep_copy_impl!(char) + +macro_rules! extern_fn_deep_copy( + ($($A:ident),*) => ( + impl<$($A,)* ReturnType> DeepCopy for extern "Rust" fn($($A),*) -> ReturnType { + /// Return a copy of a function pointer + #[inline] + fn deep_copy(&self) -> extern "Rust" fn($($A),*) -> ReturnType { *self } + } + ) +) + +extern_fn_deep_copy!() +extern_fn_deep_copy!(A) +extern_fn_deep_copy!(A, B) +extern_fn_deep_copy!(A, B, C) +extern_fn_deep_copy!(A, B, C, D) +extern_fn_deep_copy!(A, B, C, D, E) +extern_fn_deep_copy!(A, B, C, D, E, F) +extern_fn_deep_copy!(A, B, C, D, E, F, G) +extern_fn_deep_copy!(A, B, C, D, E, F, G, H) + +#[test] +fn test_owned_copy() { + let a = ~5i; + let b: ~int = a.copy(); + assert_eq!(a, b); +} + +#[test] +fn test_managed_copy() { + let a = @5i; + let b: @int = a.copy(); + assert_eq!(a, b); +} + +#[test] +fn test_managed_mut_deep_copy() { + let x = @mut 5i; + let y: @mut int = x.deep_copy(); + *x = 20; + assert_eq!(*y, 5); +} + +#[test] +fn test_managed_mut_copy() { + let a = @mut 5i; + let b: @mut int = a.copy(); + assert_eq!(a, b); + *b = 10; + assert_eq!(a, b); +} + +#[test] +fn test_borrowed_copy() { + let x = 5i; + let y: &int = &x; + let z: &int = (&y).copy(); + assert_eq!(*z, 5); +} + +#[test] +fn test_extern_fn_copy() { + trait Empty {} + impl Empty for int {} + + fn test_fn_a() -> float { 1.0 } + fn test_fn_b(x: T) -> T { x } + fn test_fn_c(_: int, _: float, _: ~[int], _: int, _: int, _: int) {} + + let _ = test_fn_a.copy(); + let _ = test_fn_b::.copy(); + let _ = test_fn_c.copy(); + + let _ = test_fn_a.deep_copy(); + let _ = test_fn_b::.deep_copy(); + let _ = test_fn_c.deep_copy(); +} diff --git a/src/libstd/prelude.rs b/src/libstd/prelude.rs index 1672f0a902e71..87c8a499fccbb 100644 --- a/src/libstd/prelude.rs +++ b/src/libstd/prelude.rs @@ -46,6 +46,7 @@ pub use from_str::from_str; // Reexported types and traits pub use c_str::ToCStr; pub use clone::{Clone, DeepClone}; +pub use copy::{Copy, DeepCopy}; pub use cmp::{Eq, ApproxEq, Ord, TotalEq, TotalOrd, Ordering, Less, Equal, Greater, Equiv}; pub use char::Char; pub use container::{Container, Mutable, Map, MutableMap, Set, MutableSet}; diff --git a/src/libstd/std.rs b/src/libstd/std.rs index e9d5dd416ade3..9792d2fa33c1a 100644 --- a/src/libstd/std.rs +++ b/src/libstd/std.rs @@ -145,6 +145,7 @@ pub mod iter; pub mod to_str; pub mod to_bytes; pub mod clone; +pub mod copy; pub mod io; pub mod hash; pub mod container; diff --git a/src/libsyntax/ext/deriving/copy.rs b/src/libsyntax/ext/deriving/copy.rs new file mode 100644 index 0000000000000..2a32e0bbee76d --- /dev/null +++ b/src/libsyntax/ext/deriving/copy.rs @@ -0,0 +1,122 @@ +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use ast::{MetaItem, item, Expr}; +use codemap::Span; +use ext::base::ExtCtxt; +use ext::build::AstBuilder; +use ext::deriving::generic::*; + +pub fn expand_deriving_copy(cx: @ExtCtxt, + span: Span, + mitem: @MetaItem, + in_items: ~[@item]) + -> ~[@item] { + let trait_def = TraitDef { + path: Path::new(~["std", "copy", "Copy"]), + additional_bounds: ~[], + generics: LifetimeBounds::empty(), + methods: ~[ + MethodDef { + name: "copy", + generics: LifetimeBounds::empty(), + explicit_self: borrowed_explicit_self(), + args: ~[], + ret_ty: Self, + const_nonmatching: false, + combine_substructure: |c, s, sub| cs_copy("Copy", c, s, sub) + } + ] + }; + + trait_def.expand(cx, span, mitem, in_items) +} + +pub fn expand_deriving_deep_copy(cx: @ExtCtxt, + span: Span, + mitem: @MetaItem, + in_items: ~[@item]) + -> ~[@item] { + let trait_def = TraitDef { + path: Path::new(~["std", "copy", "DeepCopy"]), + additional_bounds: ~[], + generics: LifetimeBounds::empty(), + methods: ~[ + MethodDef { + name: "deep_copy", + generics: LifetimeBounds::empty(), + explicit_self: borrowed_explicit_self(), + args: ~[], + ret_ty: Self, + const_nonmatching: false, + // cs_copy uses the ident passed to it, i.e. it will + // call deep_copy (not copy) here. + combine_substructure: |c, s, sub| cs_copy("DeepCopy", c, s, sub) + } + ] + }; + + trait_def.expand(cx, span, mitem, in_items) +} + +fn cs_copy( + name: &str, + cx: @ExtCtxt, span: Span, + substr: &Substructure) -> @Expr { + let copy_ident = substr.method_ident; + let ctor_ident; + let all_fields; + let subcall = |field| + cx.expr_method_call(span, field, copy_ident, ~[]); + + match *substr.fields { + Struct(ref af) => { + ctor_ident = substr.type_ident; + all_fields = af; + } + EnumMatching(_, variant, ref af) => { + ctor_ident = variant.node.name; + all_fields = af; + }, + EnumNonMatching(*) => cx.span_bug(span, + fmt!("Non-matching enum variants in `deriving(%s)`", + name)), + StaticEnum(*) | StaticStruct(*) => cx.span_bug(span, + fmt!("Static method in `deriving(%s)`", + name)) + } + + match *all_fields { + [(None, _, _), .. _] => { + // enum-like + let subcalls = all_fields.map(|&(_, self_f, _)| subcall(self_f)); + cx.expr_call_ident(span, ctor_ident, subcalls) + }, + _ => { + // struct-like + let fields = do all_fields.map |&(o_id, self_f, _)| { + let ident = match o_id { + Some(i) => i, + None => cx.span_bug(span, + fmt!("unnamed field in normal struct in `deriving(%s)`", + name)) + }; + cx.field_imm(span, ident, subcall(self_f)) + }; + + if fields.is_empty() { + // no fields, so construct like `None` + cx.expr_ident(span, ctor_ident) + } else { + cx.expr_struct_ident(span, ctor_ident, fields) + } + } + } +} diff --git a/src/libsyntax/ext/deriving/mod.rs b/src/libsyntax/ext/deriving/mod.rs index dfd4f79cd9e3e..c517311aeadc7 100644 --- a/src/libsyntax/ext/deriving/mod.rs +++ b/src/libsyntax/ext/deriving/mod.rs @@ -25,6 +25,7 @@ use ext::build::AstBuilder; use codemap::Span; pub mod clone; +pub mod copy; pub mod iter_bytes; pub mod encodable; pub mod decodable; @@ -84,6 +85,9 @@ pub fn expand_meta_deriving(cx: @ExtCtxt, "Clone" => expand!(clone::expand_deriving_clone), "DeepClone" => expand!(clone::expand_deriving_deep_clone), + "Copy" => expand!(copy::expand_deriving_copy), + "DeepCopy" => expand!(copy::expand_deriving_deep_copy), + "IterBytes" => expand!(iter_bytes::expand_deriving_iter_bytes), "Encodable" => expand!(encodable::expand_deriving_encodable),