diff options
| author | Seo Sanghyeon <sanxiyn@gmail.com> | 2015-12-10 23:23:14 +0900 |
|---|---|---|
| committer | Seo Sanghyeon <sanxiyn@gmail.com> | 2015-12-15 15:04:46 +0900 |
| commit | f9ba1078245bc2c023f51d2a63e0ca84863600e7 (patch) | |
| tree | d30ec5a73269a4746e16b561ff54410df808a4dd /src/libsyntax_ext/deriving | |
| parent | 8f031bf96205ed4cb990c2c7aded84d5ac079254 (diff) | |
| download | rust-f9ba1078245bc2c023f51d2a63e0ca84863600e7.tar.gz rust-f9ba1078245bc2c023f51d2a63e0ca84863600e7.zip | |
Move built-in syntax extensions to a separate crate
Diffstat (limited to 'src/libsyntax_ext/deriving')
| -rw-r--r-- | src/libsyntax_ext/deriving/bounds.rs | 50 | ||||
| -rw-r--r-- | src/libsyntax_ext/deriving/clone.rs | 115 | ||||
| -rw-r--r-- | src/libsyntax_ext/deriving/cmp/eq.rs | 73 | ||||
| -rw-r--r-- | src/libsyntax_ext/deriving/cmp/ord.rs | 136 | ||||
| -rw-r--r-- | src/libsyntax_ext/deriving/cmp/partial_eq.rs | 97 | ||||
| -rw-r--r-- | src/libsyntax_ext/deriving/cmp/partial_ord.rs | 233 | ||||
| -rw-r--r-- | src/libsyntax_ext/deriving/debug.rs | 156 | ||||
| -rw-r--r-- | src/libsyntax_ext/deriving/decodable.rs | 224 | ||||
| -rw-r--r-- | src/libsyntax_ext/deriving/default.rs | 85 | ||||
| -rw-r--r-- | src/libsyntax_ext/deriving/encodable.rs | 289 | ||||
| -rw-r--r-- | src/libsyntax_ext/deriving/generic/mod.rs | 1634 | ||||
| -rw-r--r-- | src/libsyntax_ext/deriving/generic/ty.rs | 284 | ||||
| -rw-r--r-- | src/libsyntax_ext/deriving/hash.rs | 100 | ||||
| -rw-r--r-- | src/libsyntax_ext/deriving/mod.rs | 202 | ||||
| -rw-r--r-- | src/libsyntax_ext/deriving/primitive.rs | 142 |
15 files changed, 3820 insertions, 0 deletions
diff --git a/src/libsyntax_ext/deriving/bounds.rs b/src/libsyntax_ext/deriving/bounds.rs new file mode 100644 index 00000000000..9bc0e088110 --- /dev/null +++ b/src/libsyntax_ext/deriving/bounds.rs @@ -0,0 +1,50 @@ +// 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 deriving::generic::*; +use deriving::generic::ty::*; + +use syntax::ast::MetaItem; +use syntax::codemap::Span; +use syntax::ext::base::{ExtCtxt, Annotatable}; + +pub fn expand_deriving_unsafe_bound(cx: &mut ExtCtxt, + span: Span, + _: &MetaItem, + _: &Annotatable, + _: &mut FnMut(Annotatable)) +{ + cx.span_err(span, "this unsafe trait should be implemented explicitly"); +} + +pub fn expand_deriving_copy(cx: &mut ExtCtxt, + span: Span, + mitem: &MetaItem, + item: &Annotatable, + push: &mut FnMut(Annotatable)) +{ + let mut v = cx.crate_root.map(|s| vec![s]).unwrap_or(Vec::new()); + v.push("marker"); + v.push("Copy"); + let path = Path::new(v); + + let trait_def = TraitDef { + span: span, + attributes: Vec::new(), + path: path, + additional_bounds: Vec::new(), + generics: LifetimeBounds::empty(), + is_unsafe: false, + methods: Vec::new(), + associated_types: Vec::new(), + }; + + trait_def.expand(cx, mitem, item, push); +} diff --git a/src/libsyntax_ext/deriving/clone.rs b/src/libsyntax_ext/deriving/clone.rs new file mode 100644 index 00000000000..1825c3d347e --- /dev/null +++ b/src/libsyntax_ext/deriving/clone.rs @@ -0,0 +1,115 @@ +// 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 deriving::generic::*; +use deriving::generic::ty::*; + +use syntax::ast::{MetaItem, Expr}; +use syntax::codemap::Span; +use syntax::ext::base::{ExtCtxt, Annotatable}; +use syntax::ext::build::AstBuilder; +use syntax::parse::token::InternedString; +use syntax::ptr::P; + +pub fn expand_deriving_clone(cx: &mut ExtCtxt, + span: Span, + mitem: &MetaItem, + item: &Annotatable, + push: &mut FnMut(Annotatable)) +{ + 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(), + generics: LifetimeBounds::empty(), + is_unsafe: false, + methods: vec!( + MethodDef { + name: "clone", + generics: LifetimeBounds::empty(), + explicit_self: borrowed_explicit_self(), + args: Vec::new(), + ret_ty: Self_, + attributes: attrs, + is_unsafe: false, + combine_substructure: combine_substructure(Box::new(|c, s, sub| { + cs_clone("Clone", c, s, sub) + })), + } + ), + associated_types: Vec::new(), + }; + + trait_def.expand(cx, mitem, item, push) +} + +fn cs_clone( + name: &str, + cx: &mut ExtCtxt, trait_span: Span, + substr: &Substructure) -> P<Expr> { + let ctor_path; + let all_fields; + let fn_path = 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) + }; + + match *substr.fields { + Struct(ref af) => { + ctor_path = cx.path(trait_span, vec![substr.type_ident]); + all_fields = af; + } + EnumMatching(_, variant, ref af) => { + ctor_path = cx.path(trait_span, vec![substr.type_ident, variant.node.name]); + all_fields = af; + }, + EnumNonMatchingCollapsed (..) => { + cx.span_bug(trait_span, + &format!("non-matching enum variants in \ + `derive({})`", name)) + } + StaticEnum(..) | StaticStruct(..) => { + cx.span_bug(trait_span, + &format!("static method in `derive({})`", name)) + } + } + + if !all_fields.is_empty() && all_fields[0].name.is_none() { + // enum-like + let subcalls = all_fields.iter().map(subcall).collect(); + let path = cx.expr_path(ctor_path); + cx.expr_call(trait_span, path, subcalls) + } else { + // struct-like + 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<_>>(); + + if fields.is_empty() { + // no fields, so construct like `None` + cx.expr_path(ctor_path) + } else { + cx.expr_struct(trait_span, ctor_path, fields) + } + } +} diff --git a/src/libsyntax_ext/deriving/cmp/eq.rs b/src/libsyntax_ext/deriving/cmp/eq.rs new file mode 100644 index 00000000000..1b855c56a48 --- /dev/null +++ b/src/libsyntax_ext/deriving/cmp/eq.rs @@ -0,0 +1,73 @@ +// Copyright 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 deriving::generic::*; +use deriving::generic::ty::*; + +use syntax::ast::{MetaItem, Expr}; +use syntax::codemap::Span; +use syntax::ext::base::{ExtCtxt, Annotatable}; +use syntax::ext::build::AstBuilder; +use syntax::parse::token::InternedString; +use syntax::ptr::P; + +pub fn expand_deriving_eq(cx: &mut ExtCtxt, + span: Span, + mitem: &MetaItem, + item: &Annotatable, + push: &mut FnMut(Annotatable)) +{ + fn cs_total_eq_assert(cx: &mut ExtCtxt, span: Span, substr: &Substructure) -> P<Expr> { + cs_same_method( + |cx, span, exprs| { + // create `a.<method>(); b.<method>(); c.<method>(); ...` + // (where method is `assert_receiver_is_total_eq`) + let stmts = exprs.into_iter().map(|e| cx.stmt_expr(e)).collect(); + let block = cx.block(span, stmts, None); + cx.expr_block(block) + }, + Box::new(|cx, sp, _, _| { + cx.span_bug(sp, "non matching enums in derive(Eq)?") }), + cx, + span, + substr + ) + } + + let inline = cx.meta_word(span, InternedString::new("inline")); + let hidden = cx.meta_word(span, InternedString::new("hidden")); + let doc = cx.meta_list(span, InternedString::new("doc"), vec!(hidden)); + let attrs = vec!(cx.attribute(span, inline), + cx.attribute(span, doc)); + let trait_def = TraitDef { + span: span, + attributes: Vec::new(), + path: path_std!(cx, core::cmp::Eq), + additional_bounds: Vec::new(), + generics: LifetimeBounds::empty(), + is_unsafe: false, + methods: vec!( + MethodDef { + name: "assert_receiver_is_total_eq", + generics: LifetimeBounds::empty(), + explicit_self: borrowed_explicit_self(), + args: vec!(), + ret_ty: nil_ty(), + attributes: attrs, + is_unsafe: false, + combine_substructure: combine_substructure(Box::new(|a, b, c| { + cs_total_eq_assert(a, b, c) + })) + } + ), + associated_types: Vec::new(), + }; + trait_def.expand(cx, mitem, item, push) +} diff --git a/src/libsyntax_ext/deriving/cmp/ord.rs b/src/libsyntax_ext/deriving/cmp/ord.rs new file mode 100644 index 00000000000..95a5d184d0e --- /dev/null +++ b/src/libsyntax_ext/deriving/cmp/ord.rs @@ -0,0 +1,136 @@ +// Copyright 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 deriving::generic::*; +use deriving::generic::ty::*; + +use syntax::ast; +use syntax::ast::{MetaItem, Expr}; +use syntax::codemap::Span; +use syntax::ext::base::{ExtCtxt, Annotatable}; +use syntax::ext::build::AstBuilder; +use syntax::parse::token::InternedString; +use syntax::ptr::P; + +pub fn expand_deriving_ord(cx: &mut ExtCtxt, + span: Span, + mitem: &MetaItem, + item: &Annotatable, + push: &mut FnMut(Annotatable)) +{ + 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::cmp::Ord), + additional_bounds: Vec::new(), + generics: LifetimeBounds::empty(), + is_unsafe: false, + methods: vec!( + MethodDef { + name: "cmp", + generics: LifetimeBounds::empty(), + explicit_self: borrowed_explicit_self(), + args: vec!(borrowed_self()), + ret_ty: Literal(path_std!(cx, core::cmp::Ordering)), + attributes: attrs, + is_unsafe: false, + combine_substructure: combine_substructure(Box::new(|a, b, c| { + cs_cmp(a, b, c) + })), + } + ), + associated_types: Vec::new(), + }; + + trait_def.expand(cx, mitem, item, push) +} + + +pub fn ordering_collapsed(cx: &mut ExtCtxt, + span: Span, + self_arg_tags: &[ast::Ident]) -> P<ast::Expr> { + let lft = cx.expr_ident(span, self_arg_tags[0]); + let rgt = cx.expr_addr_of(span, cx.expr_ident(span, self_arg_tags[1])); + cx.expr_method_call(span, lft, cx.ident_of("cmp"), vec![rgt]) +} + +pub fn cs_cmp(cx: &mut ExtCtxt, span: Span, + substr: &Substructure) -> P<Expr> { + let test_id = cx.ident_of("__test"); + let equals_path = cx.path_global(span, + cx.std_path(&["cmp", "Ordering", "Equal"])); + + let cmp_path = cx.std_path(&["cmp", "Ord", "cmp"]); + + /* + Builds: + + let __test = ::std::cmp::Ord::cmp(&self_field1, &other_field1); + if other == ::std::cmp::Ordering::Equal { + let __test = ::std::cmp::Ord::cmp(&self_field2, &other_field2); + if __test == ::std::cmp::Ordering::Equal { + ... + } else { + __test + } + } else { + __test + } + + FIXME #6449: These `if`s could/should be `match`es. + */ + cs_fold( + // foldr nests the if-elses correctly, leaving the first field + // as the outermost one, and the last as the innermost. + false, + |cx, span, old, self_f, other_fs| { + // let __test = new; + // if __test == ::std::cmp::Ordering::Equal { + // old + // } else { + // __test + // } + + let new = { + let other_f = match (other_fs.len(), other_fs.get(0)) { + (1, Some(o_f)) => o_f, + _ => cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`"), + }; + + let args = vec![ + cx.expr_addr_of(span, self_f), + cx.expr_addr_of(span, other_f.clone()), + ]; + + cx.expr_call_global(span, cmp_path.clone(), args) + }; + + let assign = cx.stmt_let(span, false, test_id, new); + + let cond = cx.expr_binary(span, ast::BiEq, + cx.expr_ident(span, test_id), + cx.expr_path(equals_path.clone())); + let if_ = cx.expr_if(span, + cond, + old, Some(cx.expr_ident(span, test_id))); + cx.expr_block(cx.block(span, vec!(assign), Some(if_))) + }, + cx.expr_path(equals_path.clone()), + Box::new(|cx, span, (self_args, tag_tuple), _non_self_args| { + if self_args.len() != 2 { + cx.span_bug(span, "not exactly 2 arguments in `derives(Ord)`") + } else { + ordering_collapsed(cx, span, tag_tuple) + } + }), + cx, span, substr) +} diff --git a/src/libsyntax_ext/deriving/cmp/partial_eq.rs b/src/libsyntax_ext/deriving/cmp/partial_eq.rs new file mode 100644 index 00000000000..29be5a7ddc3 --- /dev/null +++ b/src/libsyntax_ext/deriving/cmp/partial_eq.rs @@ -0,0 +1,97 @@ +// Copyright 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 deriving::generic::*; +use deriving::generic::ty::*; + +use syntax::ast::{MetaItem, Expr, self}; +use syntax::codemap::Span; +use syntax::ext::base::{ExtCtxt, Annotatable}; +use syntax::ext::build::AstBuilder; +use syntax::parse::token::InternedString; +use syntax::ptr::P; + +pub fn expand_deriving_partial_eq(cx: &mut ExtCtxt, + span: Span, + mitem: &MetaItem, + item: &Annotatable, + push: &mut FnMut(Annotatable)) +{ + // structures are equal if all fields are equal, and non equal, if + // any fields are not equal or if the enum variants are different + fn cs_eq(cx: &mut ExtCtxt, span: Span, substr: &Substructure) -> P<Expr> { + cs_fold( + true, // use foldl + |cx, span, subexpr, self_f, other_fs| { + let other_f = match (other_fs.len(), other_fs.get(0)) { + (1, Some(o_f)) => o_f, + _ => cx.span_bug(span, "not exactly 2 arguments in `derive(PartialEq)`") + }; + + let eq = cx.expr_binary(span, ast::BiEq, self_f, other_f.clone()); + + cx.expr_binary(span, ast::BiAnd, subexpr, eq) + }, + cx.expr_bool(span, true), + Box::new(|cx, span, _, _| cx.expr_bool(span, false)), + cx, span, substr) + } + fn cs_ne(cx: &mut ExtCtxt, span: Span, substr: &Substructure) -> P<Expr> { + cs_fold( + true, // use foldl + |cx, span, subexpr, self_f, other_fs| { + let other_f = match (other_fs.len(), other_fs.get(0)) { + (1, Some(o_f)) => o_f, + _ => cx.span_bug(span, "not exactly 2 arguments in `derive(PartialEq)`") + }; + + let eq = cx.expr_binary(span, ast::BiNe, self_f, other_f.clone()); + + cx.expr_binary(span, ast::BiOr, subexpr, eq) + }, + cx.expr_bool(span, false), + Box::new(|cx, span, _, _| cx.expr_bool(span, true)), + cx, span, substr) + } + + macro_rules! md { + ($name:expr, $f:ident) => { { + let inline = cx.meta_word(span, InternedString::new("inline")); + let attrs = vec!(cx.attribute(span, inline)); + MethodDef { + name: $name, + generics: LifetimeBounds::empty(), + explicit_self: borrowed_explicit_self(), + args: vec!(borrowed_self()), + ret_ty: Literal(path_local!(bool)), + attributes: attrs, + is_unsafe: false, + combine_substructure: combine_substructure(Box::new(|a, b, c| { + $f(a, b, c) + })) + } + } } + } + + let trait_def = TraitDef { + span: span, + attributes: Vec::new(), + path: path_std!(cx, core::cmp::PartialEq), + additional_bounds: Vec::new(), + generics: LifetimeBounds::empty(), + is_unsafe: false, + methods: vec!( + md!("eq", cs_eq), + md!("ne", cs_ne) + ), + associated_types: Vec::new(), + }; + trait_def.expand(cx, mitem, item, push) +} diff --git a/src/libsyntax_ext/deriving/cmp/partial_ord.rs b/src/libsyntax_ext/deriving/cmp/partial_ord.rs new file mode 100644 index 00000000000..bd825e5c8df --- /dev/null +++ b/src/libsyntax_ext/deriving/cmp/partial_ord.rs @@ -0,0 +1,233 @@ +// Copyright 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. + +pub use self::OrderingOp::*; + +use deriving::generic::*; +use deriving::generic::ty::*; + +use syntax::ast; +use syntax::ast::{MetaItem, Expr}; +use syntax::codemap::Span; +use syntax::ext::base::{ExtCtxt, Annotatable}; +use syntax::ext::build::AstBuilder; +use syntax::parse::token::InternedString; +use syntax::ptr::P; + +pub fn expand_deriving_partial_ord(cx: &mut ExtCtxt, + span: Span, + mitem: &MetaItem, + item: &Annotatable, + push: &mut FnMut(Annotatable)) +{ + macro_rules! md { + ($name:expr, $op:expr, $equal:expr) => { { + let inline = cx.meta_word(span, InternedString::new("inline")); + let attrs = vec!(cx.attribute(span, inline)); + MethodDef { + name: $name, + generics: LifetimeBounds::empty(), + explicit_self: borrowed_explicit_self(), + args: vec!(borrowed_self()), + ret_ty: Literal(path_local!(bool)), + attributes: attrs, + is_unsafe: false, + combine_substructure: combine_substructure(Box::new(|cx, span, substr| { + cs_op($op, $equal, cx, span, substr) + })) + } + } } + } + + let ordering_ty = Literal(path_std!(cx, core::cmp::Ordering)); + let ret_ty = Literal(Path::new_(pathvec_std!(cx, core::option::Option), + None, + vec![Box::new(ordering_ty)], + true)); + + let inline = cx.meta_word(span, InternedString::new("inline")); + let attrs = vec!(cx.attribute(span, inline)); + + let partial_cmp_def = MethodDef { + name: "partial_cmp", + generics: LifetimeBounds::empty(), + explicit_self: borrowed_explicit_self(), + args: vec![borrowed_self()], + ret_ty: ret_ty, + attributes: attrs, + is_unsafe: false, + combine_substructure: combine_substructure(Box::new(|cx, span, substr| { + cs_partial_cmp(cx, span, substr) + })) + }; + + let trait_def = TraitDef { + span: span, + attributes: vec![], + path: path_std!(cx, core::cmp::PartialOrd), + additional_bounds: vec![], + generics: LifetimeBounds::empty(), + is_unsafe: false, + methods: vec![ + partial_cmp_def, + md!("lt", true, false), + md!("le", true, true), + md!("gt", false, false), + md!("ge", false, true) + ], + associated_types: Vec::new(), + }; + trait_def.expand(cx, mitem, item, push) +} + +#[derive(Copy, Clone)] +pub enum OrderingOp { + PartialCmpOp, LtOp, LeOp, GtOp, GeOp, +} + +pub fn some_ordering_collapsed(cx: &mut ExtCtxt, + span: Span, + op: OrderingOp, + self_arg_tags: &[ast::Ident]) -> P<ast::Expr> { + let lft = cx.expr_ident(span, self_arg_tags[0]); + let rgt = cx.expr_addr_of(span, cx.expr_ident(span, self_arg_tags[1])); + let op_str = match op { + PartialCmpOp => "partial_cmp", + LtOp => "lt", LeOp => "le", + GtOp => "gt", GeOp => "ge", + }; + cx.expr_method_call(span, lft, cx.ident_of(op_str), vec![rgt]) +} + +pub fn cs_partial_cmp(cx: &mut ExtCtxt, span: Span, + substr: &Substructure) -> P<Expr> { + let test_id = cx.ident_of("__test"); + let ordering = cx.path_global(span, + cx.std_path(&["cmp", "Ordering", "Equal"])); + let ordering = cx.expr_path(ordering); + let equals_expr = cx.expr_some(span, ordering); + + let partial_cmp_path = cx.std_path(&["cmp", "PartialOrd", "partial_cmp"]); + + /* + Builds: + + let __test = ::std::cmp::PartialOrd::partial_cmp(&self_field1, &other_field1); + if __test == ::std::option::Option::Some(::std::cmp::Ordering::Equal) { + let __test = ::std::cmp::PartialOrd::partial_cmp(&self_field2, &other_field2); + if __test == ::std::option::Option::Some(::std::cmp::Ordering::Equal) { + ... + } else { + __test + } + } else { + __test + } + + FIXME #6449: These `if`s could/should be `match`es. + */ + cs_fold( + // foldr nests the if-elses correctly, leaving the first field + // as the outermost one, and the last as the innermost. + false, + |cx, span, old, self_f, other_fs| { + // let __test = new; + // if __test == Some(::std::cmp::Ordering::Equal) { + // old + // } else { + // __test + // } + + let new = { + let other_f = match (other_fs.len(), other_fs.get(0)) { + (1, Some(o_f)) => o_f, + _ => cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`"), + }; + + let args = vec![ + cx.expr_addr_of(span, self_f), + cx.expr_addr_of(span, other_f.clone()), + ]; + + cx.expr_call_global(span, partial_cmp_path.clone(), args) + }; + + let assign = cx.stmt_let(span, false, test_id, new); + + let cond = cx.expr_binary(span, ast::BiEq, + cx.expr_ident(span, test_id), + equals_expr.clone()); + let if_ = cx.expr_if(span, + cond, + old, Some(cx.expr_ident(span, test_id))); + cx.expr_block(cx.block(span, vec!(assign), Some(if_))) + }, + equals_expr.clone(), + Box::new(|cx, span, (self_args, tag_tuple), _non_self_args| { + if self_args.len() != 2 { + cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`") + } else { + some_ordering_collapsed(cx, span, PartialCmpOp, tag_tuple) + } + }), + cx, span, substr) +} + +/// Strict inequality. +fn cs_op(less: bool, equal: bool, cx: &mut ExtCtxt, + span: Span, substr: &Substructure) -> P<Expr> { + let op = if less {ast::BiLt} else {ast::BiGt}; + cs_fold( + false, // need foldr, + |cx, span, subexpr, self_f, other_fs| { + /* + build up a series of chain ||'s and &&'s from the inside + out (hence foldr) to get lexical ordering, i.e. for op == + `ast::lt` + + ``` + self.f1 < other.f1 || (!(other.f1 < self.f1) && + (self.f2 < other.f2 || (!(other.f2 < self.f2) && + (false) + )) + ) + ``` + + The optimiser should remove the redundancy. We explicitly + get use the binops to avoid auto-deref dereferencing too many + layers of pointers, if the type includes pointers. + */ + let other_f = match (other_fs.len(), other_fs.get(0)) { + (1, Some(o_f)) => o_f, + _ => cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`") + }; + + let cmp = cx.expr_binary(span, op, self_f.clone(), other_f.clone()); + + let not_cmp = cx.expr_unary(span, ast::UnNot, + cx.expr_binary(span, op, other_f.clone(), self_f)); + + let and = cx.expr_binary(span, ast::BiAnd, not_cmp, subexpr); + cx.expr_binary(span, ast::BiOr, cmp, and) + }, + cx.expr_bool(span, equal), + Box::new(|cx, span, (self_args, tag_tuple), _non_self_args| { + if self_args.len() != 2 { + cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`") + } else { + let op = match (less, equal) { + (true, true) => LeOp, (true, false) => LtOp, + (false, true) => GeOp, (false, false) => GtOp, + }; + some_ordering_collapsed(cx, span, op, tag_tuple) + } + }), + cx, span, substr) +} diff --git a/src/libsyntax_ext/deriving/debug.rs b/src/libsyntax_ext/deriving/debug.rs new file mode 100644 index 00000000000..ed3f764c1d2 --- /dev/null +++ b/src/libsyntax_ext/deriving/debug.rs @@ -0,0 +1,156 @@ +// 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 deriving::generic::*; +use deriving::generic::ty::*; + +use syntax::ast; +use syntax::ast::{MetaItem, Expr}; +use syntax::codemap::{Span, respan}; +use syntax::ext::base::{ExtCtxt, Annotatable}; +use syntax::ext::build::AstBuilder; +use syntax::parse::token; +use syntax::ptr::P; + +pub fn expand_deriving_debug(cx: &mut ExtCtxt, + span: Span, + mitem: &MetaItem, + item: &Annotatable, + push: &mut FnMut(Annotatable)) +{ + // &mut ::std::fmt::Formatter + let fmtr = Ptr(Box::new(Literal(path_std!(cx, core::fmt::Formatter))), + Borrowed(None, ast::MutMutable)); + + let trait_def = TraitDef { + span: span, + attributes: Vec::new(), + path: path_std!(cx, core::fmt::Debug), + additional_bounds: Vec::new(), + generics: LifetimeBounds::empty(), + is_unsafe: false, + methods: vec![ + MethodDef { + name: "fmt", + generics: LifetimeBounds::empty(), + explicit_self: borrowed_explicit_self(), + args: vec!(fmtr), + ret_ty: Literal(path_std!(cx, core::fmt::Result)), + attributes: Vec::new(), + is_unsafe: false, + combine_substructure: combine_substructure(Box::new(|a, b, c| { + show_substructure(a, b, c) + })) + } + ], + associated_types: Vec::new(), + }; + trait_def.expand(cx, mitem, item, push) +} + +/// We use the debug builders to do the heavy lifting here +fn show_substructure(cx: &mut ExtCtxt, span: Span, + substr: &Substructure) -> P<Expr> { + // build fmt.debug_struct(<name>).field(<fieldname>, &<fieldval>)....build() + // or fmt.debug_tuple(<name>).field(&<fieldval>)....build() + // based on the "shape". + let ident = match *substr.fields { + Struct(_) => substr.type_ident, + EnumMatching(_, v, _) => v.node.name, + EnumNonMatchingCollapsed(..) | StaticStruct(..) | StaticEnum(..) => { + cx.span_bug(span, "nonsensical .fields in `#[derive(Debug)]`") + } + }; + + // We want to make sure we have the expn_id set so that we can use unstable methods + let span = Span { expn_id: cx.backtrace(), .. span }; + let name = cx.expr_lit(span, ast::Lit_::LitStr(ident.name.as_str(), + ast::StrStyle::CookedStr)); + let builder = token::str_to_ident("builder"); + let builder_expr = cx.expr_ident(span, builder.clone()); + + let fmt = substr.nonself_args[0].clone(); + + let stmts = match *substr.fields { + Struct(ref fields) | EnumMatching(_, _, ref fields) => { + let mut stmts = vec![]; + if fields.is_empty() || fields[0].name.is_none() { + // tuple struct/"normal" variant + let expr = cx.expr_method_call(span, + fmt, + token::str_to_ident("debug_tuple"), + vec![name]); + stmts.push(cx.stmt_let(span, true, builder, expr)); + + for field in fields { + // Use double indirection to make sure this works for unsized types + let field = cx.expr_addr_of(field.span, field.self_.clone()); + let field = cx.expr_addr_of(field.span, field); + + let expr = cx.expr_method_call(span, + builder_expr.clone(), + token::str_to_ident("field"), + vec![field]); + + // Use `let _ = expr;` to avoid triggering the + // unused_results lint. + stmts.push(stmt_let_undescore(cx, span, expr)); + } + } else { + // normal struct/struct variant + let expr = cx.expr_method_call(span, + fmt, + token::str_to_ident("debug_struct"), + vec![name]); + stmts.push(cx.stmt_let(span, true, builder, expr)); + + for field in fields { + let name = cx.expr_lit(field.span, ast::Lit_::LitStr( + field.name.unwrap().name.as_str(), + ast::StrStyle::CookedStr)); + + // Use double indirection to make sure this works for unsized types + let field = cx.expr_addr_of(field.span, field.self_.clone()); + let field = cx.expr_addr_of(field.span, field); + let expr = cx.expr_method_call(span, + builder_expr.clone(), + token::str_to_ident("field"), + vec![name, field]); + stmts.push(stmt_let_undescore(cx, span, expr)); + } + } + stmts + } + _ => unreachable!() + }; + + let expr = cx.expr_method_call(span, + builder_expr, + token::str_to_ident("finish"), + vec![]); + + let block = cx.block(span, stmts, Some(expr)); + cx.expr_block(block) +} + +fn stmt_let_undescore(cx: &mut ExtCtxt, + sp: Span, + expr: P<ast::Expr>) -> P<ast::Stmt> { + let local = P(ast::Local { + pat: cx.pat_wild(sp), + ty: None, + init: Some(expr), + id: ast::DUMMY_NODE_ID, + span: sp, + attrs: None, + }); + let decl = respan(sp, ast::DeclLocal(local)); + P(respan(sp, ast::StmtDecl(P(decl), ast::DUMMY_NODE_ID))) +} diff --git a/src/libsyntax_ext/deriving/decodable.rs b/src/libsyntax_ext/deriving/decodable.rs new file mode 100644 index 00000000000..4ea4f04623a --- /dev/null +++ b/src/libsyntax_ext/deriving/decodable.rs @@ -0,0 +1,224 @@ +// 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. + +//! The compiler code necessary for `#[derive(Decodable)]`. See encodable.rs for more. + +use deriving::generic::*; +use deriving::generic::ty::*; + +use syntax::ast; +use syntax::ast::{MetaItem, Expr, MutMutable}; +use syntax::codemap::Span; +use syntax::ext::base::{ExtCtxt, Annotatable}; +use syntax::ext::build::AstBuilder; +use syntax::parse::token::InternedString; +use syntax::parse::token; +use syntax::ptr::P; + +pub fn expand_deriving_rustc_decodable(cx: &mut ExtCtxt, + span: Span, + mitem: &MetaItem, + item: &Annotatable, + push: &mut FnMut(Annotatable)) +{ + expand_deriving_decodable_imp(cx, span, mitem, item, push, "rustc_serialize") +} + +pub fn expand_deriving_decodable(cx: &mut ExtCtxt, + span: Span, + mitem: &MetaItem, + item: &Annotatable, + push: &mut FnMut(Annotatable)) +{ + expand_deriving_decodable_imp(cx, span, mitem, item, push, "serialize") +} + +fn expand_deriving_decodable_imp(cx: &mut ExtCtxt, + span: Span, + mitem: &MetaItem, + item: &Annotatable, + push: &mut FnMut(Annotatable), + krate: &'static str) +{ + if cx.crate_root != Some("std") { + // FIXME(#21880): lift this requirement. + cx.span_err(span, "this trait cannot be derived with #![no_std] \ + or #![no_core]"); + return + } + + let trait_def = TraitDef { + span: span, + attributes: Vec::new(), + path: Path::new_(vec!(krate, "Decodable"), None, vec!(), true), + additional_bounds: Vec::new(), + generics: LifetimeBounds::empty(), + is_unsafe: false, + methods: vec!( + MethodDef { + name: "decode", + generics: LifetimeBounds { + lifetimes: Vec::new(), + bounds: vec!(("__D", vec!(Path::new_( + vec!(krate, "Decoder"), None, + vec!(), true)))) + }, + explicit_self: None, + args: vec!(Ptr(Box::new(Literal(Path::new_local("__D"))), + Borrowed(None, MutMutable))), + ret_ty: Literal(Path::new_( + pathvec_std!(cx, core::result::Result), + None, + vec!(Box::new(Self_), Box::new(Literal(Path::new_( + vec!["__D", "Error"], None, vec![], false + )))), + true + )), + attributes: Vec::new(), + is_unsafe: false, + combine_substructure: combine_substructure(Box::new(|a, b, c| { + decodable_substructure(a, b, c, krate) + })), + } + ), + associated_types: Vec::new(), + }; + + trait_def.expand(cx, mitem, item, push) +} + +fn decodable_substructure(cx: &mut ExtCtxt, trait_span: Span, + substr: &Substructure, + krate: &str) -> P<Expr> { + let decoder = substr.nonself_args[0].clone(); + let recurse = vec!(cx.ident_of(krate), + cx.ident_of("Decodable"), + cx.ident_of("decode")); + let exprdecode = cx.expr_path(cx.path_global(trait_span, recurse)); + // throw an underscore in front to suppress unused variable warnings + let blkarg = cx.ident_of("_d"); + let blkdecoder = cx.expr_ident(trait_span, blkarg); + + return match *substr.fields { + StaticStruct(_, ref summary) => { + let nfields = match *summary { + Unnamed(ref fields) => fields.len(), + Named(ref fields) => fields.len() + }; + let read_struct_field = cx.ident_of("read_struct_field"); + + let path = cx.path_ident(trait_span, substr.type_ident); + let result = decode_static_fields(cx, + trait_span, + path, + summary, + |cx, span, name, field| { + cx.expr_try(span, + cx.expr_method_call(span, blkdecoder.clone(), read_struct_field, + vec!(cx.expr_str(span, name), + cx.expr_usize(span, field), + exprdecode.clone()))) + }); + let result = cx.expr_ok(trait_span, result); + cx.expr_method_call(trait_span, + decoder, + cx.ident_of("read_struct"), + vec!( + cx.expr_str(trait_span, substr.type_ident.name.as_str()), + cx.expr_usize(trait_span, nfields), + cx.lambda_expr_1(trait_span, result, blkarg) + )) + } + StaticEnum(_, ref fields) => { + let variant = cx.ident_of("i"); + + let mut arms = Vec::new(); + let mut variants = Vec::new(); + let rvariant_arg = cx.ident_of("read_enum_variant_arg"); + + for (i, &(ident, v_span, ref parts)) in fields.iter().enumerate() { + variants.push(cx.expr_str(v_span, ident.name.as_str())); + + let path = cx.path(trait_span, vec![substr.type_ident, ident]); + let decoded = decode_static_fields(cx, + v_span, + path, + parts, + |cx, span, _, field| { + let idx = cx.expr_usize(span, field); + cx.expr_try(span, + cx.expr_method_call(span, blkdecoder.clone(), rvariant_arg, + vec!(idx, exprdecode.clone()))) + }); + + arms.push(cx.arm(v_span, + vec!(cx.pat_lit(v_span, cx.expr_usize(v_span, i))), + decoded)); + } + + arms.push(cx.arm_unreachable(trait_span)); + + let result = cx.expr_ok(trait_span, + cx.expr_match(trait_span, + cx.expr_ident(trait_span, variant), arms)); + let lambda = cx.lambda_expr(trait_span, vec!(blkarg, variant), result); + let variant_vec = cx.expr_vec(trait_span, variants); + let variant_vec = cx.expr_addr_of(trait_span, variant_vec); + let result = cx.expr_method_call(trait_span, blkdecoder, + cx.ident_of("read_enum_variant"), + vec!(variant_vec, lambda)); + cx.expr_method_call(trait_span, + decoder, + cx.ident_of("read_enum"), + vec!( + cx.expr_str(trait_span, substr.type_ident.name.as_str()), + cx.lambda_expr_1(trait_span, result, blkarg) + )) + } + _ => cx.bug("expected StaticEnum or StaticStruct in derive(Decodable)") + }; +} + +/// Create a decoder for a single enum variant/struct: +/// - `outer_pat_path` is the path to this enum variant/struct +/// - `getarg` should retrieve the `usize`-th field with name `@str`. +fn decode_static_fields<F>(cx: &mut ExtCtxt, + trait_span: Span, + outer_pat_path: ast::Path, + fields: &StaticFields, + mut getarg: F) + -> P<Expr> where + F: FnMut(&mut ExtCtxt, Span, InternedString, usize) -> P<Expr>, +{ + match *fields { + Unnamed(ref fields) => { + let path_expr = cx.expr_path(outer_pat_path); + if fields.is_empty() { + path_expr + } else { + let fields = fields.iter().enumerate().map(|(i, &span)| { + getarg(cx, span, + token::intern_and_get_ident(&format!("_field{}", i)), + i) + }).collect(); + + cx.expr_call(trait_span, path_expr, fields) + } + } + Named(ref fields) => { + // use the field's span to get nicer error messages. + let fields = fields.iter().enumerate().map(|(i, &(ident, span))| { + let arg = getarg(cx, span, ident.name.as_str(), i); + cx.field_imm(span, ident, arg) + }).collect(); + cx.expr_struct(trait_span, outer_pat_path, fields) + } + } +} diff --git a/src/libsyntax_ext/deriving/default.rs b/src/libsyntax_ext/deriving/default.rs new file mode 100644 index 00000000000..bee63a98c25 --- /dev/null +++ b/src/libsyntax_ext/deriving/default.rs @@ -0,0 +1,85 @@ +// 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 deriving::generic::*; +use deriving::generic::ty::*; + +use syntax::ast::{MetaItem, Expr}; +use syntax::codemap::Span; +use syntax::ext::base::{ExtCtxt, Annotatable}; +use syntax::ext::build::AstBuilder; +use syntax::parse::token::InternedString; +use syntax::ptr::P; + +pub fn expand_deriving_default(cx: &mut ExtCtxt, + span: Span, + mitem: &MetaItem, + item: &Annotatable, + push: &mut FnMut(Annotatable)) +{ + 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::default::Default), + additional_bounds: Vec::new(), + generics: LifetimeBounds::empty(), + is_unsafe: false, + methods: vec!( + MethodDef { + name: "default", + generics: LifetimeBounds::empty(), + explicit_self: None, + args: Vec::new(), + ret_ty: Self_, + attributes: attrs, + is_unsafe: false, + combine_substructure: combine_substructure(Box::new(|a, b, c| { + default_substructure(a, b, c) + })) + } + ), + associated_types: Vec::new(), + }; + trait_def.expand(cx, mitem, item, push) +} + +fn default_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) -> P<Expr> { + let default_ident = cx.std_path(&["default", "Default", "default"]); + let default_call = |span| cx.expr_call_global(span, default_ident.clone(), Vec::new()); + + return match *substr.fields { + StaticStruct(_, ref summary) => { + match *summary { + Unnamed(ref fields) => { + if fields.is_empty() { + cx.expr_ident(trait_span, substr.type_ident) + } else { + let exprs = fields.iter().map(|sp| default_call(*sp)).collect(); + cx.expr_call_ident(trait_span, substr.type_ident, exprs) + } + } + Named(ref fields) => { + let default_fields = fields.iter().map(|&(ident, span)| { + cx.field_imm(span, ident, default_call(span)) + }).collect(); + cx.expr_struct_ident(trait_span, substr.type_ident, default_fields) + } + } + } + StaticEnum(..) => { + cx.span_err(trait_span, "`Default` cannot be derived for enums, only structs"); + // let compilation continue + cx.expr_usize(trait_span, 0) + } + _ => cx.span_bug(trait_span, "Non-static method in `derive(Default)`") + }; +} diff --git a/src/libsyntax_ext/deriving/encodable.rs b/src/libsyntax_ext/deriving/encodable.rs new file mode 100644 index 00000000000..02747d38c00 --- /dev/null +++ b/src/libsyntax_ext/deriving/encodable.rs @@ -0,0 +1,289 @@ +// 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. + +//! The compiler code necessary to implement the `#[derive(Encodable)]` +//! (and `Decodable`, in decodable.rs) extension. The idea here is that +//! type-defining items may be tagged with `#[derive(Encodable, Decodable)]`. +//! +//! For example, a type like: +//! +//! ```ignore +//! #[derive(Encodable, Decodable)] +//! struct Node { id: usize } +//! ``` +//! +//! would generate two implementations like: +//! +//! ```ignore +//! impl<S: Encoder<E>, E> Encodable<S, E> for Node { +//! fn encode(&self, s: &mut S) -> Result<(), E> { +//! s.emit_struct("Node", 1, |this| { +//! this.emit_struct_field("id", 0, |this| { +//! Encodable::encode(&self.id, this) +//! /* this.emit_usize(self.id) can also be used */ +//! }) +//! }) +//! } +//! } +//! +//! impl<D: Decoder<E>, E> Decodable<D, E> for Node { +//! fn decode(d: &mut D) -> Result<Node, E> { +//! d.read_struct("Node", 1, |this| { +//! match this.read_struct_field("id", 0, |this| Decodable::decode(this)) { +//! Ok(id) => Ok(Node { id: id }), +//! Err(e) => Err(e), +//! } +//! }) +//! } +//! } +//! ``` +//! +//! Other interesting scenarios are when the item has type parameters or +//! references other non-built-in types. A type definition like: +//! +//! ```ignore +//! #[derive(Encodable, Decodable)] +//! struct Spanned<T> { node: T, span: Span } +//! ``` +//! +//! would yield functions like: +//! +//! ```ignore +//! impl< +//! S: Encoder<E>, +//! E, +//! T: Encodable<S, E> +//! > Encodable<S, E> for Spanned<T> { +//! fn encode(&self, s: &mut S) -> Result<(), E> { +//! s.emit_struct("Spanned", 2, |this| { +//! this.emit_struct_field("node", 0, |this| self.node.encode(this)) +//! .unwrap(); +//! this.emit_struct_field("span", 1, |this| self.span.encode(this)) +//! }) +//! } +//! } +//! +//! impl< +//! D: Decoder<E>, +//! E, +//! T: Decodable<D, E> +//! > Decodable<D, E> for Spanned<T> { +//! fn decode(d: &mut D) -> Result<Spanned<T>, E> { +//! d.read_struct("Spanned", 2, |this| { +//! Ok(Spanned { +//! node: this.read_struct_field("node", 0, |this| Decodable::decode(this)) +//! .unwrap(), +//! span: this.read_struct_field("span", 1, |this| Decodable::decode(this)) +//! .unwrap(), +//! }) +//! }) +//! } +//! } +//! ``` + +use deriving::generic::*; +use deriving::generic::ty::*; + +use syntax::ast::{MetaItem, Expr, ExprRet, MutMutable}; +use syntax::codemap::Span; +use syntax::ext::base::{ExtCtxt,Annotatable}; +use syntax::ext::build::AstBuilder; +use syntax::parse::token; +use syntax::ptr::P; + +pub fn expand_deriving_rustc_encodable(cx: &mut ExtCtxt, + span: Span, + mitem: &MetaItem, + item: &Annotatable, + push: &mut FnMut(Annotatable)) +{ + expand_deriving_encodable_imp(cx, span, mitem, item, push, "rustc_serialize") +} + +pub fn expand_deriving_encodable(cx: &mut ExtCtxt, + span: Span, + mitem: &MetaItem, + item: &Annotatable, + push: &mut FnMut(Annotatable)) +{ + expand_deriving_encodable_imp(cx, span, mitem, item, push, "serialize") +} + +fn expand_deriving_encodable_imp(cx: &mut ExtCtxt, + span: Span, + mitem: &MetaItem, + item: &Annotatable, + push: &mut FnMut(Annotatable), + krate: &'static str) +{ + if cx.crate_root != Some("std") { + // FIXME(#21880): lift this requirement. + cx.span_err(span, "this trait cannot be derived with #![no_std] \ + or #![no_core]"); + return; + } + + let trait_def = TraitDef { + span: span, + attributes: Vec::new(), + path: Path::new_(vec!(krate, "Encodable"), None, vec!(), true), + additional_bounds: Vec::new(), + generics: LifetimeBounds::empty(), + is_unsafe: false, + methods: vec!( + MethodDef { + name: "encode", + generics: LifetimeBounds { + lifetimes: Vec::new(), + bounds: vec!(("__S", vec!(Path::new_( + vec!(krate, "Encoder"), None, + vec!(), true)))) + }, + explicit_self: borrowed_explicit_self(), + args: vec!(Ptr(Box::new(Literal(Path::new_local("__S"))), + Borrowed(None, MutMutable))), + ret_ty: Literal(Path::new_( + pathvec_std!(cx, core::result::Result), + None, + vec!(Box::new(Tuple(Vec::new())), Box::new(Literal(Path::new_( + vec!["__S", "Error"], None, vec![], false + )))), + true + )), + attributes: Vec::new(), + is_unsafe: false, + combine_substructure: combine_substructure(Box::new(|a, b, c| { + encodable_substructure(a, b, c) + })), + } + ), + associated_types: Vec::new(), + }; + + trait_def.expand(cx, mitem, item, push) +} + +fn encodable_substructure(cx: &mut ExtCtxt, trait_span: Span, + substr: &Substructure) -> P<Expr> { + let encoder = substr.nonself_args[0].clone(); + // throw an underscore in front to suppress unused variable warnings + let blkarg = cx.ident_of("_e"); + let blkencoder = cx.expr_ident(trait_span, blkarg); + let encode = cx.ident_of("encode"); + + return match *substr.fields { + Struct(ref fields) => { + let emit_struct_field = cx.ident_of("emit_struct_field"); + let mut stmts = Vec::new(); + for (i, &FieldInfo { + name, + ref self_, + span, + .. + }) in fields.iter().enumerate() { + let name = match name { + Some(id) => id.name.as_str(), + None => { + token::intern_and_get_ident(&format!("_field{}", i)) + } + }; + let enc = cx.expr_method_call(span, self_.clone(), + encode, vec!(blkencoder.clone())); + let lambda = cx.lambda_expr_1(span, enc, blkarg); + let call = cx.expr_method_call(span, blkencoder.clone(), + emit_struct_field, + vec!(cx.expr_str(span, name), + cx.expr_usize(span, i), + lambda)); + + // last call doesn't need a try! + let last = fields.len() - 1; + let call = if i != last { + cx.expr_try(span, call) + } else { + cx.expr(span, ExprRet(Some(call))) + }; + stmts.push(cx.stmt_expr(call)); + } + + // unit structs have no fields and need to return Ok() + if stmts.is_empty() { + let ret_ok = cx.expr(trait_span, + ExprRet(Some(cx.expr_ok(trait_span, + cx.expr_tuple(trait_span, vec![]))))); + stmts.push(cx.stmt_expr(ret_ok)); + } + + let blk = cx.lambda_stmts_1(trait_span, stmts, blkarg); + cx.expr_method_call(trait_span, + encoder, + cx.ident_of("emit_struct"), + vec!( + cx.expr_str(trait_span, substr.type_ident.name.as_str()), + cx.expr_usize(trait_span, fields.len()), + blk + )) + } + + EnumMatching(idx, variant, ref fields) => { + // We're not generating an AST that the borrow checker is expecting, + // so we need to generate a unique local variable to take the + // mutable loan out on, otherwise we get conflicts which don't + // actually exist. + let me = cx.stmt_let(trait_span, false, blkarg, encoder); + let encoder = cx.expr_ident(trait_span, blkarg); + let emit_variant_arg = cx.ident_of("emit_enum_variant_arg"); + let mut stmts = Vec::new(); + if !fields.is_empty() { + let last = fields.len() - 1; + for (i, &FieldInfo { ref self_, span, .. }) in fields.iter().enumerate() { + let enc = cx.expr_method_call(span, self_.clone(), + encode, vec!(blkencoder.clone())); + let lambda = cx.lambda_expr_1(span, enc, blkarg); + let call = cx.expr_method_call(span, blkencoder.clone(), + emit_variant_arg, + vec!(cx.expr_usize(span, i), + lambda)); + let call = if i != last { + cx.expr_try(span, call) + } else { + cx.expr(span, ExprRet(Some(call))) + }; + stmts.push(cx.stmt_expr(call)); + } + } else { + let ret_ok = cx.expr(trait_span, + ExprRet(Some(cx.expr_ok(trait_span, + cx.expr_tuple(trait_span, vec![]))))); + stmts.push(cx.stmt_expr(ret_ok)); + } + + let blk = cx.lambda_stmts_1(trait_span, stmts, blkarg); + let name = cx.expr_str(trait_span, variant.node.name.name.as_str()); + let call = cx.expr_method_call(trait_span, blkencoder, + cx.ident_of("emit_enum_variant"), + vec!(name, + cx.expr_usize(trait_span, idx), + cx.expr_usize(trait_span, fields.len()), + blk)); + let blk = cx.lambda_expr_1(trait_span, call, blkarg); + let ret = cx.expr_method_call(trait_span, + encoder, + cx.ident_of("emit_enum"), + vec!( + cx.expr_str(trait_span, substr.type_ident.name.as_str()), + blk + )); + cx.expr_block(cx.block(trait_span, vec!(me), Some(ret))) + } + + _ => cx.bug("expected Struct or EnumMatching in derive(Encodable)") + }; +} diff --git a/src/libsyntax_ext/deriving/generic/mod.rs b/src/libsyntax_ext/deriving/generic/mod.rs new file mode 100644 index 00000000000..5977144dae7 --- /dev/null +++ b/src/libsyntax_ext/deriving/generic/mod.rs @@ -0,0 +1,1634 @@ +// Copyright 2013-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. + +//! Some code that abstracts away much of the boilerplate of writing +//! `derive` instances for traits. Among other things it manages getting +//! access to the fields of the 4 different sorts of structs and enum +//! variants, as well as creating the method and impl ast instances. +//! +//! Supported features (fairly exhaustive): +//! +//! - Methods taking any number of parameters of any type, and returning +//! any type, other than vectors, bottom and closures. +//! - Generating `impl`s for types with type parameters and lifetimes +//! (e.g. `Option<T>`), the parameters are automatically given the +//! current trait as a bound. (This includes separate type parameters +//! and lifetimes for methods.) +//! - Additional bounds on the type parameters (`TraitDef.additional_bounds`) +//! +//! The most important thing for implementers is the `Substructure` and +//! `SubstructureFields` objects. The latter groups 5 possibilities of the +//! arguments: +//! +//! - `Struct`, when `Self` is a struct (including tuple structs, e.g +//! `struct T(i32, char)`). +//! - `EnumMatching`, when `Self` is an enum and all the arguments are the +//! same variant of the enum (e.g. `Some(1)`, `Some(3)` and `Some(4)`) +//! - `EnumNonMatchingCollapsed` when `Self` is an enum and the arguments +//! are not the same variant (e.g. `None`, `Some(1)` and `None`). +//! - `StaticEnum` and `StaticStruct` for static methods, where the type +//! being derived upon is either an enum or struct respectively. (Any +//! argument with type Self is just grouped among the non-self +//! arguments.) +//! +//! In the first two cases, the values from the corresponding fields in +//! all the arguments are grouped together. For `EnumNonMatchingCollapsed` +//! this isn't possible (different variants have different fields), so the +//! fields are inaccessible. (Previous versions of the deriving infrastructure +//! had a way to expand into code that could access them, at the cost of +//! generating exponential amounts of code; see issue #15375). There are no +//! fields with values in the static cases, so these are treated entirely +//! differently. +//! +//! The non-static cases have `Option<ident>` in several places associated +//! with field `expr`s. This represents the name of the field it is +//! associated with. It is only not `None` when the associated field has +//! an identifier in the source code. For example, the `x`s in the +//! following snippet +//! +//! ```rust +//! # #![allow(dead_code)] +//! struct A { x : i32 } +//! +//! struct B(i32); +//! +//! enum C { +//! C0(i32), +//! C1 { x: i32 } +//! } +//! ``` +//! +//! The `i32`s in `B` and `C0` don't have an identifier, so the +//! `Option<ident>`s would be `None` for them. +//! +//! In the static cases, the structure is summarised, either into the just +//! spans of the fields or a list of spans and the field idents (for tuple +//! structs and record structs, respectively), or a list of these, for +//! enums (one for each variant). For empty struct and empty enum +//! variants, it is represented as a count of 0. +//! +//! # "`cs`" functions +//! +//! The `cs_...` functions ("combine substructure) are designed to +//! make life easier by providing some pre-made recipes for common +//! threads; mostly calling the function being derived on all the +//! arguments and then combining them back together in some way (or +//! letting the user chose that). They are not meant to be the only +//! way to handle the structures that this code creates. +//! +//! # Examples +//! +//! The following simplified `PartialEq` is used for in-code examples: +//! +//! ```rust +//! trait PartialEq { +//! fn eq(&self, other: &Self) -> bool; +//! } +//! impl PartialEq for i32 { +//! fn eq(&self, other: &i32) -> bool { +//! *self == *other +//! } +//! } +//! ``` +//! +//! Some examples of the values of `SubstructureFields` follow, using the +//! above `PartialEq`, `A`, `B` and `C`. +//! +//! ## Structs +//! +//! When generating the `expr` for the `A` impl, the `SubstructureFields` is +//! +//! ```{.text} +//! Struct(vec![FieldInfo { +//! span: <span of x> +//! name: Some(<ident of x>), +//! self_: <expr for &self.x>, +//! other: vec![<expr for &other.x] +//! }]) +//! ``` +//! +//! For the `B` impl, called with `B(a)` and `B(b)`, +//! +//! ```{.text} +//! Struct(vec![FieldInfo { +//! span: <span of `i32`>, +//! name: None, +//! self_: <expr for &a> +//! other: vec![<expr for &b>] +//! }]) +//! ``` +//! +//! ## Enums +//! +//! When generating the `expr` for a call with `self == C0(a)` and `other +//! == C0(b)`, the SubstructureFields is +//! +//! ```{.text} +//! EnumMatching(0, <ast::Variant for C0>, +//! vec![FieldInfo { +//! span: <span of i32> +//! name: None, +//! self_: <expr for &a>, +//! other: vec![<expr for &b>] +//! }]) +//! ``` +//! +//! For `C1 {x}` and `C1 {x}`, +//! +//! ```{.text} +//! EnumMatching(1, <ast::Variant for C1>, +//! vec![FieldInfo { +//! span: <span of x> +//! name: Some(<ident of x>), +//! self_: <expr for &self.x>, +//! other: vec![<expr for &other.x>] +//! }]) +//! ``` +//! +//! For `C0(a)` and `C1 {x}` , +//! +//! ```{.text} +//! EnumNonMatchingCollapsed( +//! vec![<ident of self>, <ident of __arg_1>], +//! &[<ast::Variant for C0>, <ast::Variant for C1>], +//! &[<ident for self index value>, <ident of __arg_1 index value>]) +//! ``` +//! +//! It is the same for when the arguments are flipped to `C1 {x}` and +//! `C0(a)`; the only difference is what the values of the identifiers +//! <ident for self index value> and <ident of __arg_1 index value> will +//! be in the generated code. +//! +//! `EnumNonMatchingCollapsed` deliberately provides far less information +//! than is generally available for a given pair of variants; see #15375 +//! for discussion. +//! +//! ## Static +//! +//! A static method on the types above would result in, +//! +//! ```{.text} +//! StaticStruct(<ast::VariantData of A>, Named(vec![(<ident of x>, <span of x>)])) +//! +//! StaticStruct(<ast::VariantData of B>, Unnamed(vec![<span of x>])) +//! +//! StaticEnum(<ast::EnumDef of C>, +//! vec![(<ident of C0>, <span of C0>, Unnamed(vec![<span of i32>])), +//! (<ident of C1>, <span of C1>, Named(vec![(<ident of x>, <span of x>)]))]) +//! ``` + +pub use self::StaticFields::*; +pub use self::SubstructureFields::*; +use self::StructType::*; + +use std::cell::RefCell; +use std::collections::HashSet; +use std::vec; + +use syntax::abi::Abi; +use syntax::abi; +use syntax::ast; +use syntax::ast::{EnumDef, Expr, Ident, Generics, VariantData}; +use syntax::ast_util; +use syntax::attr; +use syntax::attr::AttrMetaMethods; +use syntax::ext::base::{ExtCtxt, Annotatable}; +use syntax::ext::build::AstBuilder; +use syntax::codemap::{self, DUMMY_SP}; +use syntax::codemap::Span; +use syntax::diagnostic::SpanHandler; +use syntax::util::move_map::MoveMap; +use syntax::owned_slice::OwnedSlice; +use syntax::parse::token::{intern, InternedString}; +use syntax::parse::token::special_idents; +use syntax::ptr::P; + +use self::ty::{LifetimeBounds, Path, Ptr, PtrTy, Self_, Ty}; + +pub mod ty; + +pub struct TraitDef<'a> { + /// The span for the current #[derive(Foo)] header. + pub span: Span, + + pub attributes: Vec<ast::Attribute>, + + /// Path of the trait, including any type parameters + pub path: Path<'a>, + + /// Additional bounds required of any type parameters of the type, + /// other than the current trait + pub additional_bounds: Vec<Ty<'a>>, + + /// Any extra lifetimes and/or bounds, e.g. `D: serialize::Decoder` + pub generics: LifetimeBounds<'a>, + + /// Is it an `unsafe` trait? + pub is_unsafe: bool, + + pub methods: Vec<MethodDef<'a>>, + + pub associated_types: Vec<(ast::Ident, Ty<'a>)>, +} + + +pub struct MethodDef<'a> { + /// name of the method + pub name: &'a str, + /// List of generics, e.g. `R: rand::Rng` + pub generics: LifetimeBounds<'a>, + + /// Whether there is a self argument (outer Option) i.e. whether + /// this is a static function, and whether it is a pointer (inner + /// Option) + pub explicit_self: Option<Option<PtrTy<'a>>>, + + /// Arguments other than the self argument + pub args: Vec<Ty<'a>>, + + /// Return type + pub ret_ty: Ty<'a>, + + pub attributes: Vec<ast::Attribute>, + + // Is it an `unsafe fn`? + pub is_unsafe: bool, + + pub combine_substructure: RefCell<CombineSubstructureFunc<'a>>, +} + +/// All the data about the data structure/method being derived upon. +pub struct Substructure<'a> { + /// ident of self + pub type_ident: Ident, + /// ident of the method + pub method_ident: Ident, + /// dereferenced access to any `Self_` or `Ptr(Self_, _)` arguments + pub self_args: &'a [P<Expr>], + /// verbatim access to any other arguments + pub nonself_args: &'a [P<Expr>], + pub fields: &'a SubstructureFields<'a> +} + +/// Summary of the relevant parts of a struct/enum field. +pub struct FieldInfo<'a> { + pub span: Span, + /// None for tuple structs/normal enum variants, Some for normal + /// structs/struct enum variants. + pub name: Option<Ident>, + /// The expression corresponding to this field of `self` + /// (specifically, a reference to it). + pub self_: P<Expr>, + /// The expressions corresponding to references to this field in + /// the other `Self` arguments. + pub other: Vec<P<Expr>>, + /// The attributes on the field + pub attrs: &'a [ast::Attribute], +} + +/// Fields for a static method +pub enum StaticFields { + /// Tuple structs/enum variants like this. + Unnamed(Vec<Span>), + /// Normal structs/struct variants. + Named(Vec<(Ident, Span)>), +} + +/// A summary of the possible sets of fields. +pub enum SubstructureFields<'a> { + Struct(Vec<FieldInfo<'a>>), + /// Matching variants of the enum: variant index, ast::Variant, + /// fields: the field name is only non-`None` in the case of a struct + /// variant. + EnumMatching(usize, &'a ast::Variant, Vec<FieldInfo<'a>>), + + /// Non-matching variants of the enum, but with all state hidden from + /// the consequent code. The first component holds `Ident`s for all of + /// the `Self` arguments; the second component is a slice of all of the + /// variants for the enum itself, and the third component is a list of + /// `Ident`s bound to the variant index values for each of the actual + /// input `Self` arguments. + EnumNonMatchingCollapsed(Vec<Ident>, &'a [P<ast::Variant>], &'a [Ident]), + + /// A static method where `Self` is a struct. + StaticStruct(&'a ast::VariantData, StaticFields), + /// A static method where `Self` is an enum. + StaticEnum(&'a ast::EnumDef, Vec<(Ident, Span, StaticFields)>), +} + + + +/// Combine the values of all the fields together. The last argument is +/// all the fields of all the structures. +pub type CombineSubstructureFunc<'a> = + Box<FnMut(&mut ExtCtxt, Span, &Substructure) -> P<Expr> + 'a>; + +/// Deal with non-matching enum variants. The tuple is a list of +/// identifiers (one for each `Self` argument, which could be any of the +/// variants since they have been collapsed together) and the identifiers +/// holding the variant index value for each of the `Self` arguments. The +/// last argument is all the non-`Self` args of the method being derived. +pub type EnumNonMatchCollapsedFunc<'a> = + Box<FnMut(&mut ExtCtxt, Span, (&[Ident], &[Ident]), &[P<Expr>]) -> P<Expr> + 'a>; + +pub fn combine_substructure<'a>(f: CombineSubstructureFunc<'a>) + -> RefCell<CombineSubstructureFunc<'a>> { + RefCell::new(f) +} + +/// This method helps to extract all the type parameters referenced from a +/// type. For a type parameter `<T>`, it looks for either a `TyPath` that +/// is not global and starts with `T`, or a `TyQPath`. +fn find_type_parameters(ty: &ast::Ty, ty_param_names: &[ast::Name]) -> Vec<P<ast::Ty>> { + use syntax::visit; + + struct Visitor<'a> { + ty_param_names: &'a [ast::Name], + types: Vec<P<ast::Ty>>, + } + + impl<'a> visit::Visitor<'a> for Visitor<'a> { + fn visit_ty(&mut self, ty: &'a ast::Ty) { + match ty.node { + ast::TyPath(_, ref path) if !path.global => { + match path.segments.first() { + Some(segment) => { + if self.ty_param_names.contains(&segment.identifier.name) { + self.types.push(P(ty.clone())); + } + } + None => {} + } + } + _ => {} + } + + visit::walk_ty(self, ty) + } + } + + let mut visitor = Visitor { + ty_param_names: ty_param_names, + types: Vec::new(), + }; + + visit::Visitor::visit_ty(&mut visitor, ty); + + visitor.types +} + +impl<'a> TraitDef<'a> { + pub fn expand(&self, + cx: &mut ExtCtxt, + mitem: &ast::MetaItem, + item: &'a Annotatable, + push: &mut FnMut(Annotatable)) + { + match *item { + Annotatable::Item(ref item) => { + let newitem = match item.node { + ast::ItemStruct(ref struct_def, ref generics) => { + self.expand_struct_def(cx, + &struct_def, + item.ident, + generics) + } + ast::ItemEnum(ref enum_def, ref generics) => { + self.expand_enum_def(cx, + enum_def, + &item.attrs, + item.ident, + generics) + } + _ => { + cx.span_err(mitem.span, + "`derive` may only be applied to structs and enums"); + return; + } + }; + // Keep the lint attributes of the previous item to control how the + // generated implementations are linted + let mut attrs = newitem.attrs.clone(); + attrs.extend(item.attrs.iter().filter(|a| { + match &a.name()[..] { + "allow" | "warn" | "deny" | "forbid" | "stable" | "unstable" => true, + _ => false, + } + }).cloned()); + push(Annotatable::Item(P(ast::Item { + attrs: attrs, + ..(*newitem).clone() + }))) + } + _ => { + cx.span_err(mitem.span, "`derive` may only be applied to structs and enums"); + } + } + } + + /// Given that we are deriving a trait `DerivedTrait` for a type like: + /// + /// ```ignore + /// struct Struct<'a, ..., 'z, A, B: DeclaredTrait, C, ..., Z> where C: WhereTrait { + /// a: A, + /// b: B::Item, + /// b1: <B as DeclaredTrait>::Item, + /// c1: <C as WhereTrait>::Item, + /// c2: Option<<C as WhereTrait>::Item>, + /// ... + /// } + /// ``` + /// + /// create an impl like: + /// + /// ```ignore + /// impl<'a, ..., 'z, A, B: DeclaredTrait, C, ... Z> where + /// C: WhereTrait, + /// A: DerivedTrait + B1 + ... + BN, + /// B: DerivedTrait + B1 + ... + BN, + /// C: DerivedTrait + B1 + ... + BN, + /// B::Item: DerivedTrait + B1 + ... + BN, + /// <C as WhereTrait>::Item: DerivedTrait + B1 + ... + BN, + /// ... + /// { + /// ... + /// } + /// ``` + /// + /// where B1, ..., BN are the bounds given by `bounds_paths`.'. Z is a phantom type, and + /// therefore does not get bound by the derived trait. + fn create_derived_impl(&self, + cx: &mut ExtCtxt, + type_ident: Ident, + generics: &Generics, + field_tys: Vec<P<ast::Ty>>, + methods: Vec<P<ast::ImplItem>>) -> P<ast::Item> { + let trait_path = self.path.to_path(cx, self.span, type_ident, generics); + + // Transform associated types from `deriving::ty::Ty` into `ast::ImplItem` + let associated_types = self.associated_types.iter().map(|&(ident, ref type_def)| { + P(ast::ImplItem { + id: ast::DUMMY_NODE_ID, + span: self.span, + ident: ident, + vis: ast::Inherited, + attrs: Vec::new(), + node: ast::ImplItemKind::Type(type_def.to_ty(cx, + self.span, + type_ident, + generics + )), + }) + }); + + let Generics { mut lifetimes, ty_params, mut where_clause } = + self.generics.to_generics(cx, self.span, type_ident, generics); + let mut ty_params = ty_params.into_vec(); + + // Copy the lifetimes + lifetimes.extend(generics.lifetimes.iter().cloned()); + + // Create the type parameters. + ty_params.extend(generics.ty_params.iter().map(|ty_param| { + // I don't think this can be moved out of the loop, since + // a TyParamBound requires an ast id + let mut bounds: Vec<_> = + // extra restrictions on the generics parameters to the type being derived upon + self.additional_bounds.iter().map(|p| { + cx.typarambound(p.to_path(cx, self.span, + type_ident, generics)) + }).collect(); + + // require the current trait + bounds.push(cx.typarambound(trait_path.clone())); + + // also add in any bounds from the declaration + for declared_bound in ty_param.bounds.iter() { + bounds.push((*declared_bound).clone()); + } + + cx.typaram(self.span, + ty_param.ident, + OwnedSlice::from_vec(bounds), + None) + })); + + // and similarly for where clauses + where_clause.predicates.extend(generics.where_clause.predicates.iter().map(|clause| { + match *clause { + ast::WherePredicate::BoundPredicate(ref wb) => { + ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate { + span: self.span, + bound_lifetimes: wb.bound_lifetimes.clone(), + bounded_ty: wb.bounded_ty.clone(), + bounds: OwnedSlice::from_vec(wb.bounds.iter().cloned().collect()) + }) + } + ast::WherePredicate::RegionPredicate(ref rb) => { + ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate { + span: self.span, + lifetime: rb.lifetime, + bounds: rb.bounds.iter().cloned().collect() + }) + } + ast::WherePredicate::EqPredicate(ref we) => { + ast::WherePredicate::EqPredicate(ast::WhereEqPredicate { + id: ast::DUMMY_NODE_ID, + span: self.span, + path: we.path.clone(), + ty: we.ty.clone() + }) + } + } + })); + + if !ty_params.is_empty() { + let ty_param_names: Vec<ast::Name> = ty_params.iter() + .map(|ty_param| ty_param.ident.name) + .collect(); + + let mut processed_field_types = HashSet::new(); + for field_ty in field_tys { + let tys = find_type_parameters(&*field_ty, &ty_param_names); + + for ty in tys { + // if we have already handled this type, skip it + if let ast::TyPath(_, ref p) = ty.node { + if p.segments.len() == 1 + && ty_param_names.contains(&p.segments[0].identifier.name) + || processed_field_types.contains(&p.segments) { + continue; + }; + processed_field_types.insert(p.segments.clone()); + } + let mut bounds: Vec<_> = self.additional_bounds.iter().map(|p| { + cx.typarambound(p.to_path(cx, self.span, type_ident, generics)) + }).collect(); + + // require the current trait + bounds.push(cx.typarambound(trait_path.clone())); + + let predicate = ast::WhereBoundPredicate { + span: self.span, + bound_lifetimes: vec![], + bounded_ty: ty, + bounds: OwnedSlice::from_vec(bounds), + }; + + let predicate = ast::WherePredicate::BoundPredicate(predicate); + where_clause.predicates.push(predicate); + } + } + } + + let trait_generics = Generics { + lifetimes: lifetimes, + ty_params: OwnedSlice::from_vec(ty_params), + where_clause: where_clause + }; + + // Create the reference to the trait. + let trait_ref = cx.trait_ref(trait_path); + + // Create the type parameters on the `self` path. + let self_ty_params = generics.ty_params.map(|ty_param| { + cx.ty_ident(self.span, ty_param.ident) + }); + + let self_lifetimes: Vec<ast::Lifetime> = + generics.lifetimes + .iter() + .map(|ld| ld.lifetime) + .collect(); + + // Create the type of `self`. + let self_type = cx.ty_path( + cx.path_all(self.span, false, vec!( type_ident ), self_lifetimes, + self_ty_params.into_vec(), Vec::new())); + + let attr = cx.attribute( + self.span, + cx.meta_word(self.span, + InternedString::new("automatically_derived"))); + // Just mark it now since we know that it'll end up used downstream + attr::mark_used(&attr); + let opt_trait_ref = Some(trait_ref); + let ident = ast_util::impl_pretty_name(&opt_trait_ref, Some(&*self_type)); + let unused_qual = cx.attribute( + self.span, + cx.meta_list(self.span, + InternedString::new("allow"), + vec![cx.meta_word(self.span, + InternedString::new("unused_qualifications"))])); + let mut a = vec![attr, unused_qual]; + a.extend(self.attributes.iter().cloned()); + + let unsafety = if self.is_unsafe { + ast::Unsafety::Unsafe + } else { + ast::Unsafety::Normal + }; + + cx.item( + self.span, + ident, + a, + ast::ItemImpl(unsafety, + ast::ImplPolarity::Positive, + trait_generics, + opt_trait_ref, + self_type, + methods.into_iter().chain(associated_types).collect())) + } + + fn expand_struct_def(&self, + cx: &mut ExtCtxt, + struct_def: &'a VariantData, + type_ident: Ident, + generics: &Generics) -> P<ast::Item> { + let field_tys: Vec<P<ast::Ty>> = struct_def.fields().iter() + .map(|field| field.node.ty.clone()) + .collect(); + + let methods = self.methods.iter().map(|method_def| { + let (explicit_self, self_args, nonself_args, tys) = + method_def.split_self_nonself_args( + cx, self, type_ident, generics); + + let body = if method_def.is_static() { + method_def.expand_static_struct_method_body( + cx, + self, + struct_def, + type_ident, + &self_args[..], + &nonself_args[..]) + } else { + method_def.expand_struct_method_body(cx, + self, + struct_def, + type_ident, + &self_args[..], + &nonself_args[..]) + }; + + method_def.create_method(cx, + self, + type_ident, + generics, + abi::Rust, + explicit_self, + tys, + body) + }).collect(); + + self.create_derived_impl(cx, type_ident, generics, field_tys, methods) + } + + fn expand_enum_def(&self, + cx: &mut ExtCtxt, + enum_def: &'a EnumDef, + type_attrs: &[ast::Attribute], + type_ident: Ident, + generics: &Generics) -> P<ast::Item> { + let mut field_tys = Vec::new(); + + for variant in &enum_def.variants { + field_tys.extend(variant.node.data.fields().iter() + .map(|field| field.node.ty.clone())); + } + + let methods = self.methods.iter().map(|method_def| { + let (explicit_self, self_args, nonself_args, tys) = + method_def.split_self_nonself_args(cx, self, + type_ident, generics); + + let body = if method_def.is_static() { + method_def.expand_static_enum_method_body( + cx, + self, + enum_def, + type_ident, + &self_args[..], + &nonself_args[..]) + } else { + method_def.expand_enum_method_body(cx, + self, + enum_def, + type_attrs, + type_ident, + self_args, + &nonself_args[..]) + }; + + method_def.create_method(cx, + self, + type_ident, + generics, + abi::Rust, + explicit_self, + tys, + body) + }).collect(); + + self.create_derived_impl(cx, type_ident, generics, field_tys, methods) + } +} + +fn find_repr_type_name(diagnostic: &SpanHandler, + type_attrs: &[ast::Attribute]) -> &'static str { + let mut repr_type_name = "i32"; + for a in type_attrs { + for r in &attr::find_repr_attrs(diagnostic, a) { + repr_type_name = match *r { + attr::ReprAny | attr::ReprPacked | attr::ReprSimd => continue, + attr::ReprExtern => "i32", + + attr::ReprInt(_, attr::SignedInt(ast::TyIs)) => "isize", + attr::ReprInt(_, attr::SignedInt(ast::TyI8)) => "i8", + attr::ReprInt(_, attr::SignedInt(ast::TyI16)) => "i16", + attr::ReprInt(_, attr::SignedInt(ast::TyI32)) => "i32", + attr::ReprInt(_, attr::SignedInt(ast::TyI64)) => "i64", + + attr::ReprInt(_, attr::UnsignedInt(ast::TyUs)) => "usize", + attr::ReprInt(_, attr::UnsignedInt(ast::TyU8)) => "u8", + attr::ReprInt(_, attr::UnsignedInt(ast::TyU16)) => "u16", + attr::ReprInt(_, attr::UnsignedInt(ast::TyU32)) => "u32", + attr::ReprInt(_, attr::UnsignedInt(ast::TyU64)) => "u64", + } + } + } + repr_type_name +} + +impl<'a> MethodDef<'a> { + fn call_substructure_method(&self, + cx: &mut ExtCtxt, + trait_: &TraitDef, + type_ident: Ident, + self_args: &[P<Expr>], + nonself_args: &[P<Expr>], + fields: &SubstructureFields) + -> P<Expr> { + let substructure = Substructure { + type_ident: type_ident, + method_ident: cx.ident_of(self.name), + self_args: self_args, + nonself_args: nonself_args, + fields: fields + }; + let mut f = self.combine_substructure.borrow_mut(); + let f: &mut CombineSubstructureFunc = &mut *f; + f(cx, trait_.span, &substructure) + } + + fn get_ret_ty(&self, + cx: &mut ExtCtxt, + trait_: &TraitDef, + generics: &Generics, + type_ident: Ident) + -> P<ast::Ty> { + self.ret_ty.to_ty(cx, trait_.span, type_ident, generics) + } + + fn is_static(&self) -> bool { + self.explicit_self.is_none() + } + + fn split_self_nonself_args(&self, + cx: &mut ExtCtxt, + trait_: &TraitDef, + type_ident: Ident, + generics: &Generics) + -> (ast::ExplicitSelf, Vec<P<Expr>>, Vec<P<Expr>>, Vec<(Ident, P<ast::Ty>)>) { + + let mut self_args = Vec::new(); + let mut nonself_args = Vec::new(); + let mut arg_tys = Vec::new(); + let mut nonstatic = false; + + let ast_explicit_self = match self.explicit_self { + Some(ref self_ptr) => { + let (self_expr, explicit_self) = + ty::get_explicit_self(cx, trait_.span, self_ptr); + + self_args.push(self_expr); + nonstatic = true; + + explicit_self + } + None => codemap::respan(trait_.span, ast::SelfStatic), + }; + + for (i, ty) in self.args.iter().enumerate() { + let ast_ty = ty.to_ty(cx, trait_.span, type_ident, generics); + let ident = cx.ident_of(&format!("__arg_{}", i)); + arg_tys.push((ident, ast_ty)); + + let arg_expr = cx.expr_ident(trait_.span, ident); + + match *ty { + // for static methods, just treat any Self + // arguments as a normal arg + Self_ if nonstatic => { + self_args.push(arg_expr); + } + Ptr(ref ty, _) if **ty == Self_ && nonstatic => { + self_args.push(cx.expr_deref(trait_.span, arg_expr)) + } + _ => { + nonself_args.push(arg_expr); + } + } + } + + (ast_explicit_self, self_args, nonself_args, arg_tys) + } + + fn create_method(&self, + cx: &mut ExtCtxt, + trait_: &TraitDef, + type_ident: Ident, + generics: &Generics, + abi: Abi, + explicit_self: ast::ExplicitSelf, + arg_types: Vec<(Ident, P<ast::Ty>)> , + body: P<Expr>) -> P<ast::ImplItem> { + // create the generics that aren't for Self + let fn_generics = self.generics.to_generics(cx, trait_.span, type_ident, generics); + + let self_arg = match explicit_self.node { + ast::SelfStatic => None, + // creating fresh self id + _ => Some(ast::Arg::new_self(trait_.span, ast::MutImmutable, special_idents::self_)) + }; + let args = { + let args = arg_types.into_iter().map(|(name, ty)| { + cx.arg(trait_.span, name, ty) + }); + self_arg.into_iter().chain(args).collect() + }; + + let ret_type = self.get_ret_ty(cx, trait_, generics, type_ident); + + let method_ident = cx.ident_of(self.name); + let fn_decl = cx.fn_decl(args, ret_type); + let body_block = cx.block_expr(body); + + let unsafety = if self.is_unsafe { + ast::Unsafety::Unsafe + } else { + ast::Unsafety::Normal + }; + + // Create the method. + P(ast::ImplItem { + id: ast::DUMMY_NODE_ID, + attrs: self.attributes.clone(), + span: trait_.span, + vis: ast::Inherited, + ident: method_ident, + node: ast::ImplItemKind::Method(ast::MethodSig { + generics: fn_generics, + abi: abi, + explicit_self: explicit_self, + unsafety: unsafety, + constness: ast::Constness::NotConst, + decl: fn_decl + }, body_block) + }) + } + + /// ```ignore + /// #[derive(PartialEq)] + /// struct A { x: i32, y: i32 } + /// + /// // equivalent to: + /// impl PartialEq for A { + /// fn eq(&self, __arg_1: &A) -> bool { + /// match *self { + /// A {x: ref __self_0_0, y: ref __self_0_1} => { + /// match *__arg_1 { + /// A {x: ref __self_1_0, y: ref __self_1_1} => { + /// __self_0_0.eq(__self_1_0) && __self_0_1.eq(__self_1_1) + /// } + /// } + /// } + /// } + /// } + /// } + /// ``` + fn expand_struct_method_body<'b>(&self, + cx: &mut ExtCtxt, + trait_: &TraitDef<'b>, + struct_def: &'b VariantData, + type_ident: Ident, + self_args: &[P<Expr>], + nonself_args: &[P<Expr>]) + -> P<Expr> { + + let mut raw_fields = Vec::new(); // Vec<[fields of self], + // [fields of next Self arg], [etc]> + let mut patterns = Vec::new(); + for i in 0..self_args.len() { + let struct_path= cx.path(DUMMY_SP, vec!( type_ident )); + let (pat, ident_expr) = + trait_.create_struct_pattern(cx, + struct_path, + struct_def, + &format!("__self_{}", + i), + ast::MutImmutable); + patterns.push(pat); + raw_fields.push(ident_expr); + } + + // transpose raw_fields + let fields = if !raw_fields.is_empty() { + let mut raw_fields = raw_fields.into_iter().map(|v| v.into_iter()); + let first_field = raw_fields.next().unwrap(); + let mut other_fields: Vec<vec::IntoIter<_>> + = raw_fields.collect(); + first_field.map(|(span, opt_id, field, attrs)| { + FieldInfo { + span: span, + name: opt_id, + self_: field, + other: other_fields.iter_mut().map(|l| { + match l.next().unwrap() { + (_, _, ex, _) => ex + } + }).collect(), + attrs: attrs, + } + }).collect() + } else { + cx.span_bug(trait_.span, + "no self arguments to non-static method in generic \ + `derive`") + }; + + // body of the inner most destructuring match + let mut body = self.call_substructure_method( + cx, + trait_, + type_ident, + self_args, + nonself_args, + &Struct(fields)); + + // make a series of nested matches, to destructure the + // structs. This is actually right-to-left, but it shouldn't + // matter. + for (arg_expr, pat) in self_args.iter().zip(patterns) { + body = cx.expr_match(trait_.span, arg_expr.clone(), + vec!( cx.arm(trait_.span, vec!(pat.clone()), body) )) + } + body + } + + fn expand_static_struct_method_body(&self, + cx: &mut ExtCtxt, + trait_: &TraitDef, + struct_def: &VariantData, + type_ident: Ident, + self_args: &[P<Expr>], + nonself_args: &[P<Expr>]) + -> P<Expr> { + let summary = trait_.summarise_struct(cx, struct_def); + + self.call_substructure_method(cx, + trait_, + type_ident, + self_args, nonself_args, + &StaticStruct(struct_def, summary)) + } + + /// ```ignore + /// #[derive(PartialEq)] + /// enum A { + /// A1, + /// A2(i32) + /// } + /// + /// // is equivalent to + /// + /// impl PartialEq for A { + /// fn eq(&self, __arg_1: &A) -> ::bool { + /// match (&*self, &*__arg_1) { + /// (&A1, &A1) => true, + /// (&A2(ref __self_0), + /// &A2(ref __arg_1_0)) => (*__self_0).eq(&(*__arg_1_0)), + /// _ => { + /// let __self_vi = match *self { A1(..) => 0, A2(..) => 1 }; + /// let __arg_1_vi = match *__arg_1 { A1(..) => 0, A2(..) => 1 }; + /// false + /// } + /// } + /// } + /// } + /// ``` + /// + /// (Of course `__self_vi` and `__arg_1_vi` are unused for + /// `PartialEq`, and those subcomputations will hopefully be removed + /// as their results are unused. The point of `__self_vi` and + /// `__arg_1_vi` is for `PartialOrd`; see #15503.) + fn expand_enum_method_body<'b>(&self, + cx: &mut ExtCtxt, + trait_: &TraitDef<'b>, + enum_def: &'b EnumDef, + type_attrs: &[ast::Attribute], + type_ident: Ident, + self_args: Vec<P<Expr>>, + nonself_args: &[P<Expr>]) + -> P<Expr> { + self.build_enum_match_tuple( + cx, trait_, enum_def, type_attrs, type_ident, self_args, nonself_args) + } + + + /// Creates a match for a tuple of all `self_args`, where either all + /// variants match, or it falls into a catch-all for when one variant + /// does not match. + + /// There are N + 1 cases because is a case for each of the N + /// variants where all of the variants match, and one catch-all for + /// when one does not match. + + /// As an optimization we generate code which checks whether all variants + /// match first which makes llvm see that C-like enums can be compiled into + /// a simple equality check (for PartialEq). + + /// The catch-all handler is provided access the variant index values + /// for each of the self-args, carried in precomputed variables. + + /// ```{.text} + /// let __self0_vi = unsafe { + /// std::intrinsics::discriminant_value(&self) } as i32; + /// let __self1_vi = unsafe { + /// std::intrinsics::discriminant_value(&__arg1) } as i32; + /// let __self2_vi = unsafe { + /// std::intrinsics::discriminant_value(&__arg2) } as i32; + /// + /// if __self0_vi == __self1_vi && __self0_vi == __self2_vi && ... { + /// match (...) { + /// (Variant1, Variant1, ...) => Body1 + /// (Variant2, Variant2, ...) => Body2, + /// ... + /// _ => ::core::intrinsics::unreachable() + /// } + /// } + /// else { + /// ... // catch-all remainder can inspect above variant index values. + /// } + /// ``` + fn build_enum_match_tuple<'b>( + &self, + cx: &mut ExtCtxt, + trait_: &TraitDef<'b>, + enum_def: &'b EnumDef, + type_attrs: &[ast::Attribute], + type_ident: Ident, + self_args: Vec<P<Expr>>, + nonself_args: &[P<Expr>]) -> P<Expr> { + + let sp = trait_.span; + let variants = &enum_def.variants; + + let self_arg_names = self_args.iter().enumerate() + .map(|(arg_count, _self_arg)| { + if arg_count == 0 { + "__self".to_string() + } else { + format!("__arg_{}", arg_count) + } + }) + .collect::<Vec<String>>(); + + let self_arg_idents = self_arg_names.iter() + .map(|name|cx.ident_of(&name[..])) + .collect::<Vec<ast::Ident>>(); + + // The `vi_idents` will be bound, solely in the catch-all, to + // a series of let statements mapping each self_arg to an int + // value corresponding to its discriminant. + let vi_idents: Vec<ast::Ident> = self_arg_names.iter() + .map(|name| { let vi_suffix = format!("{}_vi", &name[..]); + cx.ident_of(&vi_suffix[..]) }) + .collect::<Vec<ast::Ident>>(); + + // Builds, via callback to call_substructure_method, the + // delegated expression that handles the catch-all case, + // using `__variants_tuple` to drive logic if necessary. + let catch_all_substructure = EnumNonMatchingCollapsed( + self_arg_idents, &variants[..], &vi_idents[..]); + + // These arms are of the form: + // (Variant1, Variant1, ...) => Body1 + // (Variant2, Variant2, ...) => Body2 + // ... + // where each tuple has length = self_args.len() + let mut match_arms: Vec<ast::Arm> = variants.iter().enumerate() + .map(|(index, variant)| { + let mk_self_pat = |cx: &mut ExtCtxt, self_arg_name: &str| { + let (p, idents) = trait_.create_enum_variant_pattern(cx, type_ident, + &**variant, + self_arg_name, + ast::MutImmutable); + (cx.pat(sp, ast::PatRegion(p, ast::MutImmutable)), idents) + }; + + // A single arm has form (&VariantK, &VariantK, ...) => BodyK + // (see "Final wrinkle" note below for why.) + let mut subpats = Vec::with_capacity(self_arg_names.len()); + let mut self_pats_idents = Vec::with_capacity(self_arg_names.len() - 1); + let first_self_pat_idents = { + let (p, idents) = mk_self_pat(cx, &self_arg_names[0]); + subpats.push(p); + idents + }; + for self_arg_name in &self_arg_names[1..] { + let (p, idents) = mk_self_pat(cx, &self_arg_name[..]); + subpats.push(p); + self_pats_idents.push(idents); + } + + // Here is the pat = `(&VariantK, &VariantK, ...)` + let single_pat = cx.pat_tuple(sp, subpats); + + // For the BodyK, we need to delegate to our caller, + // passing it an EnumMatching to indicate which case + // we are in. + + // All of the Self args have the same variant in these + // cases. So we transpose the info in self_pats_idents + // to gather the getter expressions together, in the + // form that EnumMatching expects. + + // The transposition is driven by walking across the + // arg fields of the variant for the first self pat. + let field_tuples = first_self_pat_idents.into_iter().enumerate() + // For each arg field of self, pull out its getter expr ... + .map(|(field_index, (sp, opt_ident, self_getter_expr, attrs))| { + // ... but FieldInfo also wants getter expr + // for matching other arguments of Self type; + // so walk across the *other* self_pats_idents + // and pull out getter for same field in each + // of them (using `field_index` tracked above). + // That is the heart of the transposition. + let others = self_pats_idents.iter().map(|fields| { + let (_, _opt_ident, ref other_getter_expr, _) = + fields[field_index]; + + // All Self args have same variant, so + // opt_idents are the same. (Assert + // here to make it self-evident that + // it is okay to ignore `_opt_ident`.) + assert!(opt_ident == _opt_ident); + + other_getter_expr.clone() + }).collect::<Vec<P<Expr>>>(); + + FieldInfo { span: sp, + name: opt_ident, + self_: self_getter_expr, + other: others, + attrs: attrs, + } + }).collect::<Vec<FieldInfo>>(); + + // Now, for some given VariantK, we have built up + // expressions for referencing every field of every + // Self arg, assuming all are instances of VariantK. + // Build up code associated with such a case. + let substructure = EnumMatching(index, + &**variant, + field_tuples); + let arm_expr = self.call_substructure_method( + cx, trait_, type_ident, &self_args[..], nonself_args, + &substructure); + + cx.arm(sp, vec![single_pat], arm_expr) + }).collect(); + // We will usually need the catch-all after matching the + // tuples `(VariantK, VariantK, ...)` for each VariantK of the + // enum. But: + // + // * when there is only one Self arg, the arms above suffice + // (and the deriving we call back into may not be prepared to + // handle EnumNonMatchCollapsed), and, + // + // * when the enum has only one variant, the single arm that + // is already present always suffices. + // + // * In either of the two cases above, if we *did* add a + // catch-all `_` match, it would trigger the + // unreachable-pattern error. + // + if variants.len() > 1 && self_args.len() > 1 { + // Build a series of let statements mapping each self_arg + // to its discriminant value. If this is a C-style enum + // with a specific repr type, then casts the values to + // that type. Otherwise casts to `i32` (the default repr + // type). + // + // i.e. for `enum E<T> { A, B(1), C(T, T) }`, and a deriving + // with three Self args, builds three statements: + // + // ``` + // let __self0_vi = unsafe { + // std::intrinsics::discriminant_value(&self) } as i32; + // let __self1_vi = unsafe { + // std::intrinsics::discriminant_value(&__arg1) } as i32; + // let __self2_vi = unsafe { + // std::intrinsics::discriminant_value(&__arg2) } as i32; + // ``` + let mut index_let_stmts: Vec<P<ast::Stmt>> = Vec::new(); + + //We also build an expression which checks whether all discriminants are equal + // discriminant_test = __self0_vi == __self1_vi && __self0_vi == __self2_vi && ... + let mut discriminant_test = cx.expr_bool(sp, true); + + let target_type_name = + find_repr_type_name(&cx.parse_sess.span_diagnostic, type_attrs); + + let mut first_ident = None; + for (&ident, self_arg) in vi_idents.iter().zip(&self_args) { + let path = cx.std_path(&["intrinsics", "discriminant_value"]); + let call = cx.expr_call_global( + sp, path, vec![cx.expr_addr_of(sp, self_arg.clone())]); + let variant_value = cx.expr_block(P(ast::Block { + stmts: vec![], + expr: Some(call), + id: ast::DUMMY_NODE_ID, + rules: ast::UnsafeBlock(ast::CompilerGenerated), + span: sp })); + + let target_ty = cx.ty_ident(sp, cx.ident_of(target_type_name)); + let variant_disr = cx.expr_cast(sp, variant_value, target_ty); + let let_stmt = cx.stmt_let(sp, false, ident, variant_disr); + index_let_stmts.push(let_stmt); + + match first_ident { + Some(first) => { + let first_expr = cx.expr_ident(sp, first); + let id = cx.expr_ident(sp, ident); + let test = cx.expr_binary(sp, ast::BiEq, first_expr, id); + discriminant_test = cx.expr_binary(sp, ast::BiAnd, discriminant_test, test) + } + None => { + first_ident = Some(ident); + } + } + } + + let arm_expr = self.call_substructure_method( + cx, trait_, type_ident, &self_args[..], nonself_args, + &catch_all_substructure); + + //Since we know that all the arguments will match if we reach the match expression we + //add the unreachable intrinsics as the result of the catch all which should help llvm + //in optimizing it + let path = cx.std_path(&["intrinsics", "unreachable"]); + let call = cx.expr_call_global( + sp, path, vec![]); + let unreachable = cx.expr_block(P(ast::Block { + stmts: vec![], + expr: Some(call), + id: ast::DUMMY_NODE_ID, + rules: ast::UnsafeBlock(ast::CompilerGenerated), + span: sp })); + match_arms.push(cx.arm(sp, vec![cx.pat_wild(sp)], unreachable)); + + // Final wrinkle: the self_args are expressions that deref + // down to desired l-values, but we cannot actually deref + // them when they are fed as r-values into a tuple + // expression; here add a layer of borrowing, turning + // `(*self, *__arg_0, ...)` into `(&*self, &*__arg_0, ...)`. + let borrowed_self_args = self_args.move_map(|self_arg| cx.expr_addr_of(sp, self_arg)); + let match_arg = cx.expr(sp, ast::ExprTup(borrowed_self_args)); + + //Lastly we create an expression which branches on all discriminants being equal + // if discriminant_test { + // match (...) { + // (Variant1, Variant1, ...) => Body1 + // (Variant2, Variant2, ...) => Body2, + // ... + // _ => ::core::intrinsics::unreachable() + // } + // } + // else { + // <delegated expression referring to __self0_vi, et al.> + // } + let all_match = cx.expr_match(sp, match_arg, match_arms); + let arm_expr = cx.expr_if(sp, discriminant_test, all_match, Some(arm_expr)); + cx.expr_block( + cx.block_all(sp, index_let_stmts, Some(arm_expr))) + } else if variants.is_empty() { + // As an additional wrinkle, For a zero-variant enum A, + // currently the compiler + // will accept `fn (a: &Self) { match *a { } }` + // but rejects `fn (a: &Self) { match (&*a,) { } }` + // as well as `fn (a: &Self) { match ( *a,) { } }` + // + // This means that the strategy of building up a tuple of + // all Self arguments fails when Self is a zero variant + // enum: rustc rejects the expanded program, even though + // the actual code tends to be impossible to execute (at + // least safely), according to the type system. + // + // The most expedient fix for this is to just let the + // code fall through to the catch-all. But even this is + // error-prone, since the catch-all as defined above would + // generate code like this: + // + // _ => { let __self0 = match *self { }; + // let __self1 = match *__arg_0 { }; + // <catch-all-expr> } + // + // Which is yields bindings for variables which type + // inference cannot resolve to unique types. + // + // One option to the above might be to add explicit type + // annotations. But the *only* reason to go down that path + // would be to try to make the expanded output consistent + // with the case when the number of enum variants >= 1. + // + // That just isn't worth it. In fact, trying to generate + // sensible code for *any* deriving on a zero-variant enum + // does not make sense. But at the same time, for now, we + // do not want to cause a compile failure just because the + // user happened to attach a deriving to their + // zero-variant enum. + // + // Instead, just generate a failing expression for the + // zero variant case, skipping matches and also skipping + // delegating back to the end user code entirely. + // + // (See also #4499 and #12609; note that some of the + // discussions there influence what choice we make here; + // e.g. if we feature-gate `match x { ... }` when x refers + // to an uninhabited type (e.g. a zero-variant enum or a + // type holding such an enum), but do not feature-gate + // zero-variant enums themselves, then attempting to + // derive Debug on such a type could here generate code + // that needs the feature gate enabled.) + + cx.expr_unreachable(sp) + } + else { + + // Final wrinkle: the self_args are expressions that deref + // down to desired l-values, but we cannot actually deref + // them when they are fed as r-values into a tuple + // expression; here add a layer of borrowing, turning + // `(*self, *__arg_0, ...)` into `(&*self, &*__arg_0, ...)`. + let borrowed_self_args = self_args.move_map(|self_arg| cx.expr_addr_of(sp, self_arg)); + let match_arg = cx.expr(sp, ast::ExprTup(borrowed_self_args)); + cx.expr_match(sp, match_arg, match_arms) + } + } + + fn expand_static_enum_method_body(&self, + cx: &mut ExtCtxt, + trait_: &TraitDef, + enum_def: &EnumDef, + type_ident: Ident, + self_args: &[P<Expr>], + nonself_args: &[P<Expr>]) + -> P<Expr> { + let summary = enum_def.variants.iter().map(|v| { + let ident = v.node.name; + let summary = trait_.summarise_struct(cx, &v.node.data); + (ident, v.span, summary) + }).collect(); + self.call_substructure_method(cx, trait_, type_ident, + self_args, nonself_args, + &StaticEnum(enum_def, summary)) + } +} + +#[derive(PartialEq)] // dogfooding! +enum StructType { + Unknown, Record, Tuple +} + +// general helper methods. +impl<'a> TraitDef<'a> { + fn set_expn_info(&self, + cx: &mut ExtCtxt, + mut to_set: Span) -> Span { + let trait_name = match self.path.path.last() { + None => cx.span_bug(self.span, "trait with empty path in generic `derive`"), + Some(name) => *name + }; + to_set.expn_id = cx.codemap().record_expansion(codemap::ExpnInfo { + call_site: to_set, + callee: codemap::NameAndSpan { + format: codemap::MacroAttribute(intern(&format!("derive({})", trait_name))), + span: Some(self.span), + allow_internal_unstable: false, + } + }); + to_set + } + + fn summarise_struct(&self, + cx: &mut ExtCtxt, + struct_def: &VariantData) -> StaticFields { + let mut named_idents = Vec::new(); + let mut just_spans = Vec::new(); + for field in struct_def.fields(){ + let sp = self.set_expn_info(cx, field.span); + match field.node.kind { + ast::NamedField(ident, _) => named_idents.push((ident, sp)), + ast::UnnamedField(..) => just_spans.push(sp), + } + } + + match (just_spans.is_empty(), named_idents.is_empty()) { + (false, false) => cx.span_bug(self.span, + "a struct with named and unnamed \ + fields in generic `derive`"), + // named fields + (_, false) => Named(named_idents), + // tuple structs (includes empty structs) + (_, _) => Unnamed(just_spans) + } + } + + fn create_subpatterns(&self, + cx: &mut ExtCtxt, + field_paths: Vec<ast::SpannedIdent> , + mutbl: ast::Mutability) + -> Vec<P<ast::Pat>> { + field_paths.iter().map(|path| { + cx.pat(path.span, + ast::PatIdent(ast::BindByRef(mutbl), (*path).clone(), None)) + }).collect() + } + + fn create_struct_pattern(&self, + cx: &mut ExtCtxt, + struct_path: ast::Path, + struct_def: &'a VariantData, + prefix: &str, + mutbl: ast::Mutability) + -> (P<ast::Pat>, Vec<(Span, Option<Ident>, + P<Expr>, + &'a [ast::Attribute])>) { + if struct_def.fields().is_empty() { + return (cx.pat_enum(self.span, struct_path, vec![]), vec![]); + } + + let mut paths = Vec::new(); + let mut ident_expr = Vec::new(); + let mut struct_type = Unknown; + + for (i, struct_field) in struct_def.fields().iter().enumerate() { + let sp = self.set_expn_info(cx, struct_field.span); + let opt_id = match struct_field.node.kind { + ast::NamedField(ident, _) if (struct_type == Unknown || + struct_type == Record) => { + struct_type = Record; + Some(ident) + } + ast::UnnamedField(..) if (struct_type == Unknown || + struct_type == Tuple) => { + struct_type = Tuple; + None + } + _ => { + cx.span_bug(sp, "a struct with named and unnamed fields in `derive`"); + } + }; + let ident = cx.ident_of(&format!("{}_{}", prefix, i)); + paths.push(codemap::Spanned{span: sp, node: ident}); + let val = cx.expr( + sp, ast::ExprParen(cx.expr_deref(sp, cx.expr_path(cx.path_ident(sp,ident))))); + ident_expr.push((sp, opt_id, val, &struct_field.node.attrs[..])); + } + + let subpats = self.create_subpatterns(cx, paths, mutbl); + + // struct_type is definitely not Unknown, since struct_def.fields + // must be nonempty to reach here + let pattern = if struct_type == Record { + let field_pats = subpats.into_iter().zip(&ident_expr) + .map(|(pat, &(_, id, _, _))| { + // id is guaranteed to be Some + codemap::Spanned { + span: pat.span, + node: ast::FieldPat { ident: id.unwrap(), pat: pat, is_shorthand: false }, + } + }).collect(); + cx.pat_struct(self.span, struct_path, field_pats) + } else { + cx.pat_enum(self.span, struct_path, subpats) + }; + + (pattern, ident_expr) + } + + fn create_enum_variant_pattern(&self, + cx: &mut ExtCtxt, + enum_ident: ast::Ident, + variant: &'a ast::Variant, + prefix: &str, + mutbl: ast::Mutability) + -> (P<ast::Pat>, Vec<(Span, Option<Ident>, P<Expr>, &'a [ast::Attribute])>) { + let variant_ident = variant.node.name; + let variant_path = cx.path(variant.span, vec![enum_ident, variant_ident]); + self.create_struct_pattern(cx, variant_path, &variant.node.data, prefix, mutbl) + } +} + +/* helpful premade recipes */ + +/// Fold the fields. `use_foldl` controls whether this is done +/// left-to-right (`true`) or right-to-left (`false`). +pub fn cs_fold<F>(use_foldl: bool, + mut f: F, + base: P<Expr>, + mut enum_nonmatch_f: EnumNonMatchCollapsedFunc, + cx: &mut ExtCtxt, + trait_span: Span, + substructure: &Substructure) + -> P<Expr> where + F: FnMut(&mut ExtCtxt, Span, P<Expr>, P<Expr>, &[P<Expr>]) -> P<Expr>, +{ + match *substructure.fields { + EnumMatching(_, _, ref all_fields) | Struct(ref all_fields) => { + if use_foldl { + all_fields.iter().fold(base, |old, field| { + f(cx, + field.span, + old, + field.self_.clone(), + &field.other) + }) + } else { + all_fields.iter().rev().fold(base, |old, field| { + f(cx, + field.span, + old, + field.self_.clone(), + &field.other) + }) + } + }, + EnumNonMatchingCollapsed(ref all_args, _, tuple) => + enum_nonmatch_f(cx, trait_span, (&all_args[..], tuple), + substructure.nonself_args), + StaticEnum(..) | StaticStruct(..) => { + cx.span_bug(trait_span, "static function in `derive`") + } + } +} + + +/// Call the method that is being derived on all the fields, and then +/// process the collected results. i.e. +/// +/// ```ignore +/// f(cx, span, vec![self_1.method(__arg_1_1, __arg_2_1), +/// self_2.method(__arg_1_2, __arg_2_2)]) +/// ``` +#[inline] +pub fn cs_same_method<F>(f: F, + mut enum_nonmatch_f: EnumNonMatchCollapsedFunc, + cx: &mut ExtCtxt, + trait_span: Span, + substructure: &Substructure) + -> P<Expr> where + F: FnOnce(&mut ExtCtxt, Span, Vec<P<Expr>>) -> P<Expr>, +{ + match *substructure.fields { + EnumMatching(_, _, ref all_fields) | Struct(ref all_fields) => { + // call self_n.method(other_1_n, other_2_n, ...) + let called = all_fields.iter().map(|field| { + cx.expr_method_call(field.span, + field.self_.clone(), + substructure.method_ident, + field.other.iter() + .map(|e| cx.expr_addr_of(field.span, e.clone())) + .collect()) + }).collect(); + + f(cx, trait_span, called) + }, + EnumNonMatchingCollapsed(ref all_self_args, _, tuple) => + enum_nonmatch_f(cx, trait_span, (&all_self_args[..], tuple), + substructure.nonself_args), + StaticEnum(..) | StaticStruct(..) => { + cx.span_bug(trait_span, "static function in `derive`") + } + } +} diff --git a/src/libsyntax_ext/deriving/generic/ty.rs b/src/libsyntax_ext/deriving/generic/ty.rs new file mode 100644 index 00000000000..0c4a81361ae --- /dev/null +++ b/src/libsyntax_ext/deriving/generic/ty.rs @@ -0,0 +1,284 @@ +// Copyright 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. + +//! A mini version of ast::Ty, which is easier to use, and features an explicit `Self` type to use +//! when specifying impls to be derived. + +pub use self::PtrTy::*; +pub use self::Ty::*; + +use syntax::ast; +use syntax::ast::{Expr,Generics,Ident}; +use syntax::ext::base::ExtCtxt; +use syntax::ext::build::AstBuilder; +use syntax::codemap::{Span,respan}; +use syntax::owned_slice::OwnedSlice; +use syntax::parse::token::special_idents; +use syntax::ptr::P; + +/// The types of pointers +#[derive(Clone, Eq, PartialEq)] +#[allow(dead_code)] +pub enum PtrTy<'a> { + /// &'lifetime mut + Borrowed(Option<&'a str>, ast::Mutability), + /// *mut + Raw(ast::Mutability), +} + +/// A path, e.g. `::std::option::Option::<i32>` (global). Has support +/// for type parameters and a lifetime. +#[derive(Clone, Eq, PartialEq)] +pub struct Path<'a> { + pub path: Vec<&'a str> , + pub lifetime: Option<&'a str>, + pub params: Vec<Box<Ty<'a>>>, + pub global: bool, +} + +impl<'a> Path<'a> { + pub fn new<'r>(path: Vec<&'r str> ) -> Path<'r> { + Path::new_(path, None, Vec::new(), true) + } + pub fn new_local<'r>(path: &'r str) -> Path<'r> { + Path::new_(vec!( path ), None, Vec::new(), false) + } + pub fn new_<'r>(path: Vec<&'r str> , + lifetime: Option<&'r str>, + params: Vec<Box<Ty<'r>>>, + global: bool) + -> Path<'r> { + Path { + path: path, + lifetime: lifetime, + params: params, + global: global + } + } + + pub fn to_ty(&self, + cx: &ExtCtxt, + span: Span, + self_ty: Ident, + self_generics: &Generics) + -> P<ast::Ty> { + cx.ty_path(self.to_path(cx, span, self_ty, self_generics)) + } + pub fn to_path(&self, + cx: &ExtCtxt, + span: Span, + self_ty: Ident, + self_generics: &Generics) + -> ast::Path { + let idents = self.path.iter().map(|s| cx.ident_of(*s)).collect(); + let lt = mk_lifetimes(cx, span, &self.lifetime); + let tys = self.params.iter().map(|t| t.to_ty(cx, span, self_ty, self_generics)).collect(); + + cx.path_all(span, self.global, idents, lt, tys, Vec::new()) + } +} + +/// A type. Supports pointers, Self, and literals +#[derive(Clone, Eq, PartialEq)] +pub enum Ty<'a> { + Self_, + /// &/Box/ Ty + Ptr(Box<Ty<'a>>, PtrTy<'a>), + /// mod::mod::Type<[lifetime], [Params...]>, including a plain type + /// parameter, and things like `i32` + Literal(Path<'a>), + /// includes unit + Tuple(Vec<Ty<'a>> ) +} + +pub fn borrowed_ptrty<'r>() -> PtrTy<'r> { + Borrowed(None, ast::MutImmutable) +} +pub fn borrowed<'r>(ty: Box<Ty<'r>>) -> Ty<'r> { + Ptr(ty, borrowed_ptrty()) +} + +pub fn borrowed_explicit_self<'r>() -> Option<Option<PtrTy<'r>>> { + Some(Some(borrowed_ptrty())) +} + +pub fn borrowed_self<'r>() -> Ty<'r> { + borrowed(Box::new(Self_)) +} + +pub fn nil_ty<'r>() -> Ty<'r> { + Tuple(Vec::new()) +} + +fn mk_lifetime(cx: &ExtCtxt, span: Span, lt: &Option<&str>) -> Option<ast::Lifetime> { + match *lt { + Some(ref s) => Some(cx.lifetime(span, cx.ident_of(*s).name)), + None => None + } +} + +fn mk_lifetimes(cx: &ExtCtxt, span: Span, lt: &Option<&str>) -> Vec<ast::Lifetime> { + match *lt { + Some(ref s) => vec!(cx.lifetime(span, cx.ident_of(*s).name)), + None => vec!() + } +} + +impl<'a> Ty<'a> { + pub fn to_ty(&self, + cx: &ExtCtxt, + span: Span, + self_ty: Ident, + self_generics: &Generics) + -> P<ast::Ty> { + match *self { + Ptr(ref ty, ref ptr) => { + let raw_ty = ty.to_ty(cx, span, self_ty, self_generics); + match *ptr { + Borrowed(ref lt, mutbl) => { + let lt = mk_lifetime(cx, span, lt); + cx.ty_rptr(span, raw_ty, lt, mutbl) + } + Raw(mutbl) => cx.ty_ptr(span, raw_ty, mutbl) + } + } + Literal(ref p) => { p.to_ty(cx, span, self_ty, self_generics) } + Self_ => { + cx.ty_path(self.to_path(cx, span, self_ty, self_generics)) + } + Tuple(ref fields) => { + let ty = ast::TyTup(fields.iter() + .map(|f| f.to_ty(cx, span, self_ty, self_generics)) + .collect()); + cx.ty(span, ty) + } + } + } + + pub fn to_path(&self, + cx: &ExtCtxt, + span: Span, + self_ty: Ident, + self_generics: &Generics) + -> ast::Path { + match *self { + Self_ => { + let self_params = self_generics.ty_params.map(|ty_param| { + cx.ty_ident(span, ty_param.ident) + }); + let lifetimes = self_generics.lifetimes.iter() + .map(|d| d.lifetime) + .collect(); + + cx.path_all(span, false, vec!(self_ty), lifetimes, + self_params.into_vec(), Vec::new()) + } + Literal(ref p) => { + p.to_path(cx, span, self_ty, self_generics) + } + Ptr(..) => { cx.span_bug(span, "pointer in a path in generic `derive`") } + Tuple(..) => { cx.span_bug(span, "tuple in a path in generic `derive`") } + } + } +} + + +fn mk_ty_param(cx: &ExtCtxt, + span: Span, + name: &str, + bounds: &[Path], + self_ident: Ident, + self_generics: &Generics) + -> ast::TyParam { + let bounds = + bounds.iter().map(|b| { + let path = b.to_path(cx, span, self_ident, self_generics); + cx.typarambound(path) + }).collect(); + cx.typaram(span, cx.ident_of(name), bounds, None) +} + +fn mk_generics(lifetimes: Vec<ast::LifetimeDef>, ty_params: Vec<ast::TyParam>) + -> Generics { + Generics { + lifetimes: lifetimes, + ty_params: OwnedSlice::from_vec(ty_params), + where_clause: ast::WhereClause { + id: ast::DUMMY_NODE_ID, + predicates: Vec::new(), + }, + } +} + +/// Lifetimes and bounds on type parameters +#[derive(Clone)] +pub struct LifetimeBounds<'a> { + pub lifetimes: Vec<(&'a str, Vec<&'a str>)>, + pub bounds: Vec<(&'a str, Vec<Path<'a>>)>, +} + +impl<'a> LifetimeBounds<'a> { + pub fn empty() -> LifetimeBounds<'a> { + LifetimeBounds { + lifetimes: Vec::new(), bounds: Vec::new() + } + } + pub fn to_generics(&self, + cx: &ExtCtxt, + span: Span, + self_ty: Ident, + self_generics: &Generics) + -> Generics { + let lifetimes = self.lifetimes.iter().map(|&(ref lt, ref bounds)| { + let bounds = + bounds.iter().map( + |b| cx.lifetime(span, cx.ident_of(*b).name)).collect(); + cx.lifetime_def(span, cx.ident_of(*lt).name, bounds) + }).collect(); + let ty_params = self.bounds.iter().map(|t| { + match *t { + (ref name, ref bounds) => { + mk_ty_param(cx, + span, + *name, + bounds, + self_ty, + self_generics) + } + } + }).collect(); + mk_generics(lifetimes, ty_params) + } +} + +pub fn get_explicit_self(cx: &ExtCtxt, span: Span, self_ptr: &Option<PtrTy>) + -> (P<Expr>, ast::ExplicitSelf) { + // this constructs a fresh `self` path, which will match the fresh `self` binding + // created below. + let self_path = cx.expr_self(span); + match *self_ptr { + None => { + (self_path, respan(span, ast::SelfValue(special_idents::self_))) + } + Some(ref ptr) => { + let self_ty = respan( + span, + match *ptr { + Borrowed(ref lt, mutbl) => { + let lt = lt.map(|s| cx.lifetime(span, cx.ident_of(s).name)); + ast::SelfRegion(lt, mutbl, special_idents::self_) + } + Raw(_) => cx.span_bug(span, "attempted to use *self in deriving definition") + }); + let self_expr = cx.expr_deref(span, self_path); + (self_expr, self_ty) + } + } +} diff --git a/src/libsyntax_ext/deriving/hash.rs b/src/libsyntax_ext/deriving/hash.rs new file mode 100644 index 00000000000..6bd21f7c0e0 --- /dev/null +++ b/src/libsyntax_ext/deriving/hash.rs @@ -0,0 +1,100 @@ +// 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 deriving::generic::*; +use deriving::generic::ty::*; + +use syntax::ast::{MetaItem, Expr, MutMutable}; +use syntax::codemap::Span; +use syntax::ext::base::{ExtCtxt, Annotatable}; +use syntax::ext::build::AstBuilder; +use syntax::ptr::P; + +pub fn expand_deriving_hash(cx: &mut ExtCtxt, + span: Span, + mitem: &MetaItem, + item: &Annotatable, + push: &mut FnMut(Annotatable)) +{ + + let path = Path::new_(pathvec_std!(cx, core::hash::Hash), None, + vec!(), true); + let arg = Path::new_local("__H"); + let hash_trait_def = TraitDef { + span: span, + attributes: Vec::new(), + path: path, + additional_bounds: Vec::new(), + generics: LifetimeBounds::empty(), + is_unsafe: false, + methods: vec!( + MethodDef { + name: "hash", + generics: LifetimeBounds { + lifetimes: Vec::new(), + bounds: vec![("__H", + vec![path_std!(cx, core::hash::Hasher)])], + }, + explicit_self: borrowed_explicit_self(), + args: vec!(Ptr(Box::new(Literal(arg)), Borrowed(None, MutMutable))), + ret_ty: nil_ty(), + attributes: vec![], + is_unsafe: false, + combine_substructure: combine_substructure(Box::new(|a, b, c| { + hash_substructure(a, b, c) + })) + } + ), + associated_types: Vec::new(), + }; + + hash_trait_def.expand(cx, mitem, item, push); +} + +fn hash_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) -> P<Expr> { + let state_expr = match (substr.nonself_args.len(), substr.nonself_args.get(0)) { + (1, Some(o_f)) => o_f, + _ => cx.span_bug(trait_span, "incorrect number of arguments in `derive(Hash)`") + }; + let call_hash = |span, thing_expr| { + let hash_path = { + let strs = cx.std_path(&["hash", "Hash", "hash"]); + + cx.expr_path(cx.path_global(span, strs)) + }; + let ref_thing = cx.expr_addr_of(span, thing_expr); + let expr = cx.expr_call(span, hash_path, vec!(ref_thing, state_expr.clone())); + cx.stmt_expr(expr) + }; + let mut stmts = Vec::new(); + + 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(ref d) => d.clone(), + None => cx.expr_usize(trait_span, index) + }; + + stmts.push(call_hash(trait_span, discriminant)); + + fs + } + _ => cx.span_bug(trait_span, "impossible substructure in `derive(Hash)`") + }; + + for &FieldInfo { ref self_, span, .. } in fields { + stmts.push(call_hash(span, self_.clone())); + } + + 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 new file mode 100644 index 00000000000..919540724ca --- /dev/null +++ b/src/libsyntax_ext/deriving/mod.rs @@ -0,0 +1,202 @@ +// Copyright 2012-2015 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. + +//! The compiler code necessary to implement the `#[derive]` extensions. +//! +//! FIXME (#2810): hygiene. Search for "__" strings (in other files too). We also assume "extra" is +//! the standard library, and "std" is the core library. + +use syntax::ast::{MetaItem, MetaWord}; +use syntax::attr::AttrMetaMethods; +use syntax::ext::base::{ExtCtxt, SyntaxEnv, Annotatable}; +use syntax::ext::base::{MultiDecorator, MultiItemDecorator, MultiModifier}; +use syntax::ext::build::AstBuilder; +use syntax::feature_gate; +use syntax::codemap::Span; +use syntax::parse::token::{intern, intern_and_get_ident}; + +macro_rules! pathvec { + ($($x:ident)::+) => ( + vec![ $( stringify!($x) ),+ ] + ) +} + +macro_rules! path { + ($($x:tt)*) => ( + ::ext::deriving::generic::ty::Path::new( pathvec!( $($x)* ) ) + ) +} + +macro_rules! path_local { + ($x:ident) => ( + ::deriving::generic::ty::Path::new_local(stringify!($x)) + ) +} + +macro_rules! pathvec_std { + ($cx:expr, $first:ident :: $($rest:ident)::+) => ({ + let mut v = pathvec!($($rest)::+); + if let Some(s) = $cx.crate_root { + v.insert(0, s); + } + v + }) +} + +macro_rules! path_std { + ($($x:tt)*) => ( + ::deriving::generic::ty::Path::new( pathvec_std!( $($x)* ) ) + ) +} + +pub mod bounds; +pub mod clone; +pub mod encodable; +pub mod decodable; +pub mod hash; +pub mod debug; +pub mod default; +pub mod primitive; + +#[path="cmp/partial_eq.rs"] +pub mod partial_eq; +#[path="cmp/eq.rs"] +pub mod eq; +#[path="cmp/partial_ord.rs"] +pub mod partial_ord; +#[path="cmp/ord.rs"] +pub mod ord; + + +pub mod generic; + +fn expand_derive(cx: &mut ExtCtxt, + span: Span, + mitem: &MetaItem, + annotatable: Annotatable) + -> Annotatable { + annotatable.map_item_or(|item| { + item.map(|mut item| { + if mitem.value_str().is_some() { + cx.span_err(mitem.span, "unexpected value in `derive`"); + } + + let traits = mitem.meta_item_list().unwrap_or(&[]); + if traits.is_empty() { + cx.span_warn(mitem.span, "empty trait list in `derive`"); + } + + for titem in traits.iter().rev() { + let tname = match titem.node { + MetaWord(ref tname) => tname, + _ => { + cx.span_err(titem.span, "malformed `derive` entry"); + continue; + } + }; + + if !(is_builtin_trait(tname) || cx.ecfg.enable_custom_derive()) { + feature_gate::emit_feature_err(&cx.parse_sess.span_diagnostic, + "custom_derive", + titem.span, + feature_gate::GateIssue::Language, + feature_gate::EXPLAIN_CUSTOM_DERIVE); + continue; + } + + // #[derive(Foo, Bar)] expands to #[derive_Foo] #[derive_Bar] + item.attrs.push(cx.attribute(titem.span, cx.meta_word(titem.span, + intern_and_get_ident(&format!("derive_{}", tname))))); + } + + item + }) + }, |a| { + cx.span_err(span, "`derive` can only be applied to items"); + a + }) +} + +macro_rules! derive_traits { + ($( $name:expr => $func:path, )+) => { + pub fn register_all(env: &mut SyntaxEnv) { + // Define the #[derive_*] extensions. + $({ + struct DeriveExtension; + + impl MultiItemDecorator for DeriveExtension { + fn expand(&self, + ecx: &mut ExtCtxt, + sp: Span, + mitem: &MetaItem, + annotatable: &Annotatable, + push: &mut FnMut(Annotatable)) { + warn_if_deprecated(ecx, sp, $name); + $func(ecx, sp, mitem, annotatable, push); + } + } + + env.insert(intern(concat!("derive_", $name)), + MultiDecorator(Box::new(DeriveExtension))); + })+ + + env.insert(intern("derive"), + MultiModifier(Box::new(expand_derive))); + } + + fn is_builtin_trait(name: &str) -> bool { + match name { + $( $name )|+ => true, + _ => false, + } + } + } +} + +derive_traits! { + "Clone" => clone::expand_deriving_clone, + + "Hash" => hash::expand_deriving_hash, + + "RustcEncodable" => encodable::expand_deriving_rustc_encodable, + + "RustcDecodable" => decodable::expand_deriving_rustc_decodable, + + "PartialEq" => partial_eq::expand_deriving_partial_eq, + "Eq" => eq::expand_deriving_eq, + "PartialOrd" => partial_ord::expand_deriving_partial_ord, + "Ord" => ord::expand_deriving_ord, + + "Debug" => debug::expand_deriving_debug, + + "Default" => default::expand_deriving_default, + + "FromPrimitive" => primitive::expand_deriving_from_primitive, + + "Send" => bounds::expand_deriving_unsafe_bound, + "Sync" => bounds::expand_deriving_unsafe_bound, + "Copy" => bounds::expand_deriving_copy, + + // deprecated + "Encodable" => encodable::expand_deriving_encodable, + "Decodable" => decodable::expand_deriving_decodable, +} + +#[inline] // because `name` is a compile-time constant +fn warn_if_deprecated(ecx: &mut ExtCtxt, sp: Span, name: &str) { + if let Some(replacement) = match name { + "Encodable" => Some("RustcEncodable"), + "Decodable" => Some("RustcDecodable"), + _ => None, + } { + ecx.span_warn(sp, &format!("derive({}) is deprecated in favor of derive({})", + name, replacement)); + } +} diff --git a/src/libsyntax_ext/deriving/primitive.rs b/src/libsyntax_ext/deriving/primitive.rs new file mode 100644 index 00000000000..121fe01976e --- /dev/null +++ b/src/libsyntax_ext/deriving/primitive.rs @@ -0,0 +1,142 @@ +// 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 deriving::generic::*; +use deriving::generic::ty::*; + +use syntax::ast::{MetaItem, Expr}; +use syntax::ast; +use syntax::codemap::Span; +use syntax::ext::base::{ExtCtxt, Annotatable}; +use syntax::ext::build::AstBuilder; +use syntax::parse::token::InternedString; +use syntax::ptr::P; + +pub fn expand_deriving_from_primitive(cx: &mut ExtCtxt, + span: Span, + mitem: &MetaItem, + item: &Annotatable, + push: &mut FnMut(Annotatable)) +{ + 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::num::FromPrimitive), + additional_bounds: Vec::new(), + generics: LifetimeBounds::empty(), + is_unsafe: false, + methods: vec!( + MethodDef { + name: "from_i64", + generics: LifetimeBounds::empty(), + explicit_self: None, + args: vec!(Literal(path_local!(i64))), + ret_ty: Literal(Path::new_(pathvec_std!(cx, core::option::Option), + None, + vec!(Box::new(Self_)), + true)), + // #[inline] liable to cause code-bloat + attributes: attrs.clone(), + is_unsafe: false, + combine_substructure: combine_substructure(Box::new(|c, s, sub| { + cs_from("i64", c, s, sub) + })), + }, + MethodDef { + name: "from_u64", + generics: LifetimeBounds::empty(), + explicit_self: None, + args: vec!(Literal(path_local!(u64))), + ret_ty: Literal(Path::new_(pathvec_std!(cx, core::option::Option), + None, + vec!(Box::new(Self_)), + true)), + // #[inline] liable to cause code-bloat + attributes: attrs, + is_unsafe: false, + combine_substructure: combine_substructure(Box::new(|c, s, sub| { + cs_from("u64", c, s, sub) + })), + } + ), + associated_types: Vec::new(), + }; + + trait_def.expand(cx, mitem, item, push) +} + +fn cs_from(name: &str, cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) -> P<Expr> { + let n = match (substr.nonself_args.len(), substr.nonself_args.get(0)) { + (1, Some(o_f)) => o_f, + _ => cx.span_bug(trait_span, "incorrect number of arguments in `derive(FromPrimitive)`") + }; + + match *substr.fields { + StaticStruct(..) => { + cx.span_err(trait_span, "`FromPrimitive` cannot be derived for structs"); + return cx.expr_fail(trait_span, InternedString::new("")); + } + StaticEnum(enum_def, _) => { + if enum_def.variants.is_empty() { + cx.span_err(trait_span, + "`FromPrimitive` cannot be derived for enums with no variants"); + return cx.expr_fail(trait_span, InternedString::new("")); + } + + let mut arms = Vec::new(); + + for variant in &enum_def.variants { + let def = &variant.node.data; + if !def.is_unit() { + cx.span_err(trait_span, "`FromPrimitive` cannot be derived \ + for enums with non-unit variants"); + return cx.expr_fail(trait_span, + InternedString::new("")); + } + + let span = variant.span; + + // expr for `$n == $variant as $name` + let path = cx.path(span, vec![substr.type_ident, variant.node.name]); + let variant = cx.expr_path(path); + let ty = cx.ty_ident(span, cx.ident_of(name)); + let cast = cx.expr_cast(span, variant.clone(), ty); + let guard = cx.expr_binary(span, ast::BiEq, n.clone(), cast); + + // expr for `Some($variant)` + let body = cx.expr_some(span, variant); + + // arm for `_ if $guard => $body` + let arm = ast::Arm { + attrs: vec!(), + pats: vec!(cx.pat_wild(span)), + guard: Some(guard), + body: body, + }; + + arms.push(arm); + } + + // arm for `_ => None` + let arm = ast::Arm { + attrs: vec!(), + pats: vec!(cx.pat_wild(trait_span)), + guard: None, + body: cx.expr_none(trait_span), + }; + arms.push(arm); + + cx.expr_match(trait_span, n.clone(), arms) + } + _ => cx.span_bug(trait_span, "expected StaticEnum in derive(FromPrimitive)") + } +} |
