about summary refs log tree commit diff
path: root/src/libsyntax/ext
diff options
context:
space:
mode:
authorErick Tryzelaar <erick.tryzelaar@gmail.com>2014-02-21 21:33:23 -0800
committerErick Tryzelaar <erick.tryzelaar@gmail.com>2014-02-21 21:33:23 -0800
commitd223dd1e57cc412aa2eff28e6604f86b9f013083 (patch)
treeaa8d3f01f77b0a74c024d1632df29d5a95f4724e /src/libsyntax/ext
parent0135b521ad38615e9a07aac54d9c22627af57ca4 (diff)
downloadrust-d223dd1e57cc412aa2eff28e6604f86b9f013083.tar.gz
rust-d223dd1e57cc412aa2eff28e6604f86b9f013083.zip
std: rewrite Hash to make it more generic
This patch merges IterBytes and Hash traits, which clears up the
confusion of using `#[deriving(IterBytes)]` to support hashing.
Instead, it now is much easier to use the new `#[deriving(Hash)]`
for making a type hashable with a stream hash.

Furthermore, it supports custom non-stream-based hashers, such as
if a value's hash was cached in a database.

This does not yet replace the old IterBytes-hash with this new
version.
Diffstat (limited to 'src/libsyntax/ext')
-rw-r--r--src/libsyntax/ext/deriving/generic.rs12
-rw-r--r--src/libsyntax/ext/deriving/hash.rs97
-rw-r--r--src/libsyntax/ext/deriving/mod.rs2
3 files changed, 105 insertions, 6 deletions
diff --git a/src/libsyntax/ext/deriving/generic.rs b/src/libsyntax/ext/deriving/generic.rs
index a3bef1b5edd..99ac07ba4d7 100644
--- a/src/libsyntax/ext/deriving/generic.rs
+++ b/src/libsyntax/ext/deriving/generic.rs
@@ -416,11 +416,11 @@ impl<'a> TraitDef<'a> {
                           self_type, methods.map(|x| *x)))
     }
 
-    pub fn expand_struct_def(&self,
-                             cx: &mut ExtCtxt,
-                             struct_def: &StructDef,
-                             type_ident: Ident,
-                             generics: &Generics) -> @ast::Item {
+    fn expand_struct_def(&self,
+                         cx: &mut ExtCtxt,
+                         struct_def: &StructDef,
+                         type_ident: Ident,
+                         generics: &Generics) -> @ast::Item {
         let methods = self.methods.map(|method_def| {
             let (explicit_self, self_args, nonself_args, tys) =
                 method_def.split_self_nonself_args(
@@ -450,7 +450,7 @@ impl<'a> TraitDef<'a> {
         self.create_derived_impl(cx, type_ident, generics, methods)
     }
 
-    pub fn expand_enum_def(&self,
+    fn expand_enum_def(&self,
                        cx: &mut ExtCtxt,
                        enum_def: &EnumDef,
                        type_ident: Ident,
diff --git a/src/libsyntax/ext/deriving/hash.rs b/src/libsyntax/ext/deriving/hash.rs
new file mode 100644
index 00000000000..0b9158547ed
--- /dev/null
+++ b/src/libsyntax/ext/deriving/hash.rs
@@ -0,0 +1,97 @@
+// Copyright 2014 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 ast::{MetaItem, Item, Expr, MutMutable};
+use codemap::Span;
+use ext::base::ExtCtxt;
+use ext::build::AstBuilder;
+use ext::deriving::generic::*;
+use parse::token::InternedString;
+
+pub fn expand_deriving_hash(cx: &mut ExtCtxt,
+                            span: Span,
+                            mitem: @MetaItem,
+                            item: @Item,
+                            push: |@Item|) {
+
+    let allow_default_type_param_usage = cx.attribute(
+        span,
+        cx.meta_list(
+            span,
+            InternedString::new("allow"),
+            ~[cx.meta_word(span, InternedString::new("default_type_param_usage"))]));
+
+    let hash_trait_def = TraitDef {
+        span: span,
+        attributes: ~[allow_default_type_param_usage],
+        path: Path::new_(~["std", "hash", "Hash"], None,
+                         ~[~Literal(Path::new_local("__H"))], true),
+        additional_bounds: ~[],
+        generics: LifetimeBounds {
+            lifetimes: ~[],
+            bounds: ~[("__H", ~[Path::new(~["std", "io", "Writer"])])],
+        },
+        methods: ~[
+            MethodDef {
+                name: "hash",
+                generics: LifetimeBounds::empty(),
+                explicit_self: borrowed_explicit_self(),
+                args: ~[Ptr(~Literal(Path::new_local("__H")),
+                            Borrowed(None, MutMutable))],
+                ret_ty: nil_ty(),
+                inline: true,
+                const_nonmatching: false,
+                combine_substructure: hash_substructure
+            }
+        ]
+    };
+
+    hash_trait_def.expand(cx, mitem, item, push);
+}
+
+fn hash_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) -> @Expr {
+    let state_expr = match substr.nonself_args {
+        [state_expr] => state_expr,
+        _ => cx.span_bug(trait_span, "incorrect number of arguments in `deriving(Hash)`")
+    };
+    let hash_ident = substr.method_ident;
+    let call_hash = |span, thing_expr| {
+        let expr = cx.expr_method_call(span, thing_expr, hash_ident, ~[state_expr]);
+        cx.stmt_expr(expr)
+    };
+    let mut stmts = ~[];
+
+    let fields = match *substr.fields {
+        Struct(ref fs) => fs,
+        EnumMatching(index, variant, ref fs) => {
+            // Determine the discriminant. We will feed this value to the byte
+            // iteration function.
+            let discriminant = match variant.node.disr_expr {
+                Some(d) => d,
+                None => cx.expr_uint(trait_span, index)
+            };
+
+            stmts.push(call_hash(trait_span, discriminant));
+
+            fs
+        }
+        _ => cx.span_bug(trait_span, "impossible substructure in `deriving(Hash)`")
+    };
+
+    for &FieldInfo { self_, span, .. } in fields.iter() {
+        stmts.push(call_hash(span, self_));
+    }
+
+    if stmts.len() == 0 {
+        cx.span_bug(trait_span, "#[deriving(Hash)] needs at least one field");
+    }
+
+    cx.expr_block(cx.block(trait_span, stmts, None))
+}
diff --git a/src/libsyntax/ext/deriving/mod.rs b/src/libsyntax/ext/deriving/mod.rs
index 7c686e5cd67..31ee99fbd26 100644
--- a/src/libsyntax/ext/deriving/mod.rs
+++ b/src/libsyntax/ext/deriving/mod.rs
@@ -26,6 +26,7 @@ pub mod clone;
 pub mod iter_bytes;
 pub mod encodable;
 pub mod decodable;
+pub mod hash;
 pub mod rand;
 pub mod to_str;
 pub mod show;
@@ -74,6 +75,7 @@ pub fn expand_meta_deriving(cx: &mut ExtCtxt,
                             "DeepClone" => expand!(clone::expand_deriving_deep_clone),
 
                             "IterBytes" => expand!(iter_bytes::expand_deriving_iter_bytes),
+                            "Hash" => expand!(hash::expand_deriving_hash),
 
                             "Encodable" => expand!(encodable::expand_deriving_encodable),
                             "Decodable" => expand!(decodable::expand_deriving_decodable),