about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2013-06-16 01:52:09 -0700
committerbors <bors@rust-lang.org>2013-06-16 01:52:09 -0700
commit08c1155a223eb79960e7a5dbb0ea6276ef9754ea (patch)
treefdbb9cbe16c75d9cb615fc651a3946fb6391ed17 /src
parentd1927d295013e19e57a9773c37ded698e89392eb (diff)
parent893c70d7bc670054ef646b71d4d503298cc50d76 (diff)
downloadrust-08c1155a223eb79960e7a5dbb0ea6276ef9754ea.tar.gz
rust-08c1155a223eb79960e7a5dbb0ea6276ef9754ea.zip
auto merge of #7142 : alexcrichton/rust/deriving-zero, r=pcwalton
This allows mass-initialization of large structs without having to specify all the fields.

I'm a bit hesitant, but I wanted to get this out there. I don't really like using the `Zero` trait, because it doesn't really make sense for a type like `HashMap` to use `Zero` as the 'blank allocation' trait. In theory there'd be a new trait, but then that's adding cruft to the language which may not necessarily need to be there.

I do think that this can be useful, but I only implemented `Zero` on the basic types where I thought it made sense, so it may not be all that usable yet. (opinions?)
Diffstat (limited to 'src')
-rw-r--r--src/libstd/char.rs10
-rw-r--r--src/libstd/num/num.rs15
-rw-r--r--src/libstd/option.rs5
-rw-r--r--src/libstd/str.rs11
-rw-r--r--src/libstd/tuple.rs13
-rw-r--r--src/libstd/vec.rs11
-rw-r--r--src/libsyntax/ext/deriving/mod.rs2
-rw-r--r--src/libsyntax/ext/deriving/zero.rs96
-rw-r--r--src/test/run-pass/deriving-zero.rs40
9 files changed, 201 insertions, 2 deletions
diff --git a/src/libstd/char.rs b/src/libstd/char.rs
index 073ced8988a..7bfac2927c8 100644
--- a/src/libstd/char.rs
+++ b/src/libstd/char.rs
@@ -17,8 +17,8 @@ use u32;
 use uint;
 use unicode::{derived_property, general_category};
 
-#[cfg(not(test))]
-use cmp::{Eq, Ord};
+#[cfg(not(test))] use cmp::{Eq, Ord};
+#[cfg(not(test))] use num::Zero;
 
 /*
     Lu  Uppercase_Letter        an uppercase letter
@@ -328,6 +328,12 @@ impl Ord for char {
     fn ge(&self, other: &char) -> bool { *self >= *other }
 }
 
+#[cfg(not(test))]
+impl Zero for char {
+    fn zero() -> char { 0 as char }
+    fn is_zero(&self) -> bool { *self == 0 as char }
+}
+
 #[test]
 fn test_is_lowercase() {
     assert!('a'.is_lowercase());
diff --git a/src/libstd/num/num.rs b/src/libstd/num/num.rs
index a9893579721..4681e4f4f53 100644
--- a/src/libstd/num/num.rs
+++ b/src/libstd/num/num.rs
@@ -418,6 +418,21 @@ pub fn pow_with_uint<T:NumCast+One+Zero+Copy+Div<T,T>+Mul<T,T>>(radix: uint, pow
     total
 }
 
+impl<T: Zero> Zero for @mut T {
+    fn zero() -> @mut T { @mut Zero::zero() }
+    fn is_zero(&self) -> bool { (**self).is_zero() }
+}
+
+impl<T: Zero> Zero for @T {
+    fn zero() -> @T { @Zero::zero() }
+    fn is_zero(&self) -> bool { (**self).is_zero() }
+}
+
+impl<T: Zero> Zero for ~T {
+    fn zero() -> ~T { ~Zero::zero() }
+    fn is_zero(&self) -> bool { (**self).is_zero() }
+}
+
 /// Helper function for testing numeric operations
 #[cfg(test)]
 pub fn test_num<T:Num + NumCast>(ten: T, two: T) {
diff --git a/src/libstd/option.rs b/src/libstd/option.rs
index 80f4fb7643c..76272706445 100644
--- a/src/libstd/option.rs
+++ b/src/libstd/option.rs
@@ -350,6 +350,11 @@ impl<T:Copy + Zero> Option<T> {
     }
 }
 
+impl<T> Zero for Option<T> {
+    fn zero() -> Option<T> { None }
+    fn is_zero(&self) -> bool { self.is_none() }
+}
+
 /// Immutable iterator over an `Option<A>`
 pub struct OptionIterator<'self, A> {
     priv opt: Option<&'self A>
diff --git a/src/libstd/str.rs b/src/libstd/str.rs
index 21f747317f4..aa6b1470b26 100644
--- a/src/libstd/str.rs
+++ b/src/libstd/str.rs
@@ -27,6 +27,7 @@ use container::Container;
 use iter::Times;
 use iterator::{Iterator, IteratorUtil, FilterIterator, AdditiveIterator, MapIterator};
 use libc;
+use num::Zero;
 use option::{None, Option, Some};
 use old_iter::{BaseIter, EqIter};
 use ptr;
@@ -2201,6 +2202,16 @@ impl<'self> Iterator<u8> for StrBytesRevIterator<'self> {
     }
 }
 
+impl Zero for ~str {
+    fn zero() -> ~str { ~"" }
+    fn is_zero(&self) -> bool { self.len() == 0 }
+}
+
+impl Zero for @str {
+    fn zero() -> @str { @"" }
+    fn is_zero(&self) -> bool { self.len() == 0 }
+}
+
 #[cfg(test)]
 mod tests {
     use iterator::IteratorUtil;
diff --git a/src/libstd/tuple.rs b/src/libstd/tuple.rs
index 589c18de0ab..b80f152a5b1 100644
--- a/src/libstd/tuple.rs
+++ b/src/libstd/tuple.rs
@@ -135,6 +135,7 @@ macro_rules! tuple_impls {
         pub mod inner {
             use clone::Clone;
             #[cfg(not(test))] use cmp::*;
+            #[cfg(not(test))] use num::Zero;
 
             $(
                 pub trait $cloneable_trait<$($T),+> {
@@ -210,6 +211,18 @@ macro_rules! tuple_impls {
                         lexical_cmp!($(self.$get_ref_fn(), other.$get_ref_fn()),+)
                     }
                 }
+
+                #[cfg(not(test))]
+                impl<$($T:Zero),+> Zero for ($($T),+) {
+                    #[inline]
+                    fn zero() -> ($($T),+) {
+                        ($(Zero::zero::<$T>()),+)
+                    }
+                    #[inline]
+                    fn is_zero(&self) -> bool {
+                        $(self.$get_ref_fn().is_zero())&&+
+                    }
+                }
             )+
         }
     }
diff --git a/src/libstd/vec.rs b/src/libstd/vec.rs
index f330e13c5bd..1a236a49a32 100644
--- a/src/libstd/vec.rs
+++ b/src/libstd/vec.rs
@@ -23,6 +23,7 @@ use iterator::{Iterator, IteratorUtil};
 use iter::FromIter;
 use kinds::Copy;
 use libc;
+use num::Zero;
 use old_iter::CopyableIter;
 use option::{None, Option, Some};
 use ptr::to_unsafe_ptr;
@@ -2628,6 +2629,16 @@ impl<A:Clone> Clone for ~[A] {
     }
 }
 
+impl<A> Zero for ~[A] {
+    fn zero() -> ~[A] { ~[] }
+    fn is_zero(&self) -> bool { self.len() == 0 }
+}
+
+impl<A> Zero for @[A] {
+    fn zero() -> @[A] { @[] }
+    fn is_zero(&self) -> bool { self.len() == 0 }
+}
+
 macro_rules! iterator {
     /* FIXME: #4375 Cannot attach documentation/attributes to a macro generated struct.
     (struct $name:ident -> $ptr:ty, $elem:ty) => {
diff --git a/src/libsyntax/ext/deriving/mod.rs b/src/libsyntax/ext/deriving/mod.rs
index 0198ca424d7..f5170d284c6 100644
--- a/src/libsyntax/ext/deriving/mod.rs
+++ b/src/libsyntax/ext/deriving/mod.rs
@@ -31,6 +31,7 @@ pub mod encodable;
 pub mod decodable;
 pub mod rand;
 pub mod to_str;
+pub mod zero;
 
 #[path="cmp/eq.rs"]
 pub mod eq;
@@ -98,6 +99,7 @@ pub fn expand_meta_deriving(cx: @ExtCtxt,
                             "Rand" => expand!(rand::expand_deriving_rand),
 
                             "ToStr" => expand!(to_str::expand_deriving_to_str),
+                            "Zero" => expand!(zero::expand_deriving_zero),
 
                             ref tname => {
                                 cx.span_err(titem.span, fmt!("unknown \
diff --git a/src/libsyntax/ext/deriving/zero.rs b/src/libsyntax/ext/deriving/zero.rs
new file mode 100644
index 00000000000..121d8351ee4
--- /dev/null
+++ b/src/libsyntax/ext/deriving/zero.rs
@@ -0,0 +1,96 @@
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use core::prelude::*;
+
+use ast::{meta_item, item, expr};
+use codemap::span;
+use ext::base::ExtCtxt;
+use ext::build::AstBuilder;
+use ext::deriving::generic::*;
+
+use core::vec;
+
+pub fn expand_deriving_zero(cx: @ExtCtxt,
+                            span: span,
+                            mitem: @meta_item,
+                            in_items: ~[@item])
+    -> ~[@item] {
+    let trait_def = TraitDef {
+        path: Path::new(~["std", "num", "Zero"]),
+        additional_bounds: ~[],
+        generics: LifetimeBounds::empty(),
+        methods: ~[
+            MethodDef {
+                name: "zero",
+                generics: LifetimeBounds::empty(),
+                explicit_self: None,
+                args: ~[],
+                ret_ty: Self,
+                const_nonmatching: false,
+                combine_substructure: zero_substructure
+            },
+            MethodDef {
+                name: "is_zero",
+                generics: LifetimeBounds::empty(),
+                explicit_self: borrowed_explicit_self(),
+                args: ~[],
+                ret_ty: Literal(Path::new(~["bool"])),
+                const_nonmatching: false,
+                combine_substructure: |cx, span, substr| {
+                    cs_and(|cx, span, _, _| cx.span_bug(span,
+                                                        "Non-matching enum \
+                                                         variant in \
+                                                         deriving(Zero)"),
+                           cx, span, substr)
+                }
+            }
+        ]
+    };
+    trait_def.expand(cx, span, mitem, in_items)
+}
+
+fn zero_substructure(cx: @ExtCtxt, span: span, substr: &Substructure) -> @expr {
+    let zero_ident = ~[
+        cx.ident_of("std"),
+        cx.ident_of("num"),
+        cx.ident_of("Zero"),
+        cx.ident_of("zero")
+    ];
+    let zero_call = || {
+        cx.expr_call_global(span, copy zero_ident, ~[])
+    };
+
+    return match *substr.fields {
+        StaticStruct(_, ref summary) => {
+            match *summary {
+                Left(count) => {
+                    if count == 0 {
+                        cx.expr_ident(span, substr.type_ident)
+                    } else {
+                        let exprs = vec::from_fn(count, |_| zero_call());
+                        cx.expr_call_ident(span, substr.type_ident, exprs)
+                    }
+                }
+                Right(ref fields) => {
+                    let zero_fields = do fields.map |ident| {
+                        cx.field_imm(span, *ident, zero_call())
+                    };
+                    cx.expr_struct_ident(span, substr.type_ident, zero_fields)
+                }
+            }
+        }
+        StaticEnum(*) => {
+            cx.span_fatal(span, "`Zero` cannot be derived for enums, \
+                                 only structs")
+        }
+        _ => cx.bug("Non-static method in `deriving(Zero)`")
+    };
+}
diff --git a/src/test/run-pass/deriving-zero.rs b/src/test/run-pass/deriving-zero.rs
new file mode 100644
index 00000000000..2ee57624112
--- /dev/null
+++ b/src/test/run-pass/deriving-zero.rs
@@ -0,0 +1,40 @@
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::util;
+use std::num::Zero;
+
+#[deriving(Zero)]
+struct A;
+#[deriving(Zero)]
+struct B(int);
+#[deriving(Zero)]
+struct C(int, int);
+#[deriving(Zero)]
+struct D { a: int }
+#[deriving(Zero)]
+struct E { a: int, b: int }
+
+#[deriving(Zero)]
+struct Lots {
+    a: ~str,
+    b: @str,
+    c: Option<util::NonCopyable>,
+    d: u8,
+    e: char,
+    f: float,
+    g: (f32, char),
+    h: ~[util::NonCopyable],
+    i: @mut (int, int),
+}
+
+fn main() {
+    assert!(Zero::zero::<Lots>().is_zero());
+}