From b20eb970e18377191465cb37f2074f467dfd61b1 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Tue, 7 May 2013 01:23:51 +1000 Subject: [PATCH 1/5] libsyntax: extend generic deriving code to handle almost all possible traits. This adds support for static methods, and arguments of most types, traits with type parameters, methods with type parameters (and lifetimes for both), as well as making the code more robust to support deriving on types with lifetimes (i.e. 'self). --- src/libsyntax/ext/build.rs | 84 ++- src/libsyntax/ext/deriving/clone.rs | 13 +- src/libsyntax/ext/deriving/cmp/eq.rs | 19 +- src/libsyntax/ext/deriving/cmp/ord.rs | 42 +- src/libsyntax/ext/deriving/cmp/totaleq.rs | 13 +- src/libsyntax/ext/deriving/cmp/totalord.rs | 12 +- src/libsyntax/ext/deriving/decodable.rs | 4 +- src/libsyntax/ext/deriving/encodable.rs | 12 +- src/libsyntax/ext/deriving/generic.rs | 801 +++++++++++---------- src/libsyntax/ext/deriving/mod.rs | 266 +++---- src/libsyntax/ext/deriving/ty.rs | 242 +++++++ 11 files changed, 945 insertions(+), 563 deletions(-) create mode 100644 src/libsyntax/ext/deriving/ty.rs diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 4c876669f47..c1163fda844 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -54,43 +54,52 @@ pub fn mk_binary(cx: @ext_ctxt, sp: span, op: ast::binop, cx.next_id(); // see ast_util::op_expr_callee_id mk_expr(cx, sp, ast::expr_binary(op, lhs, rhs)) } + +pub fn mk_deref(cx: @ext_ctxt, sp: span, e: @ast::expr) -> @ast::expr { + mk_unary(cx, sp, ast::deref, e) +} pub fn mk_unary(cx: @ext_ctxt, sp: span, op: ast::unop, e: @ast::expr) -> @ast::expr { cx.next_id(); // see ast_util::op_expr_callee_id mk_expr(cx, sp, ast::expr_unary(op, e)) } pub fn mk_raw_path(sp: span, idents: ~[ast::ident]) -> @ast::Path { - mk_raw_path_(sp, idents, ~[]) + mk_raw_path_(sp, idents, None, ~[]) } pub fn mk_raw_path_(sp: span, idents: ~[ast::ident], + rp: Option<@ast::Lifetime>, types: ~[@ast::Ty]) -> @ast::Path { @ast::Path { span: sp, global: false, idents: idents, - rp: None, + rp: rp, types: types } } pub fn mk_raw_path_global(sp: span, idents: ~[ast::ident]) -> @ast::Path { - mk_raw_path_global_(sp, idents, ~[]) + mk_raw_path_global_(sp, idents, None, ~[]) } pub fn mk_raw_path_global_(sp: span, idents: ~[ast::ident], + rp: Option<@ast::Lifetime>, types: ~[@ast::Ty]) -> @ast::Path { @ast::Path { span: sp, global: true, idents: idents, - rp: None, + rp: rp, types: types } } +pub fn mk_path_raw(cx: @ext_ctxt, sp: span, path: @ast::Path)-> @ast::expr { + mk_expr(cx, sp, ast::expr_path(path)) +} pub fn mk_path(cx: @ext_ctxt, sp: span, idents: ~[ast::ident]) -> @ast::expr { - mk_expr(cx, sp, ast::expr_path(mk_raw_path(sp, idents))) + mk_path_raw(cx, sp, mk_raw_path(sp, idents)) } pub fn mk_path_global(cx: @ext_ctxt, sp: span, idents: ~[ast::ident]) -> @ast::expr { - mk_expr(cx, sp, ast::expr_path(mk_raw_path_global(sp, idents))) + mk_path_raw(cx, sp, mk_raw_path_global(sp, idents)) } pub fn mk_access_(cx: @ext_ctxt, sp: span, p: @ast::expr, m: ast::ident) -> @ast::expr { @@ -354,44 +363,69 @@ pub fn mk_stmt(cx: @ext_ctxt, span: span, expr: @ast::expr) -> @ast::stmt { let stmt_ = ast::stmt_semi(expr, cx.next_id()); @codemap::spanned { node: stmt_, span: span } } + +pub fn mk_ty_mt(ty: @ast::Ty, mutbl: ast::mutability) -> ast::mt { + ast::mt { + ty: ty, + mutbl: mutbl + } +} + +pub fn mk_ty(cx: @ext_ctxt, + span: span, + ty: ast::ty_) -> @ast::Ty { + @ast::Ty { + id: cx.next_id(), + span: span, + node: ty + } +} + pub fn mk_ty_path(cx: @ext_ctxt, span: span, idents: ~[ ast::ident ]) -> @ast::Ty { let ty = build::mk_raw_path(span, idents); - let ty = ast::ty_path(ty, cx.next_id()); - let ty = @ast::Ty { id: cx.next_id(), node: ty, span: span }; - ty + mk_ty_path_path(cx, span, ty) } + pub fn mk_ty_path_global(cx: @ext_ctxt, span: span, idents: ~[ ast::ident ]) -> @ast::Ty { let ty = build::mk_raw_path_global(span, idents); - let ty = ast::ty_path(ty, cx.next_id()); - let ty = @ast::Ty { id: cx.next_id(), node: ty, span: span }; - ty + mk_ty_path_path(cx, span, ty) } + +pub fn mk_ty_path_path(cx: @ext_ctxt, + span: span, + path: @ast::Path) + -> @ast::Ty { + let ty = ast::ty_path(path, cx.next_id()); + mk_ty(cx, span, ty) +} + pub fn mk_ty_rptr(cx: @ext_ctxt, span: span, ty: @ast::Ty, + lifetime: Option<@ast::Lifetime>, mutbl: ast::mutability) -> @ast::Ty { - @ast::Ty { - id: cx.next_id(), - span: span, - node: ast::ty_rptr( - None, - ast::mt { ty: ty, mutbl: mutbl } - ), - } + mk_ty(cx, span, + ast::ty_rptr(lifetime, mk_ty_mt(ty, mutbl))) } +pub fn mk_ty_uniq(cx: @ext_ctxt, span: span, ty: @ast::Ty) -> @ast::Ty { + mk_ty(cx, span, ast::ty_uniq(mk_ty_mt(ty, ast::m_imm))) +} +pub fn mk_ty_box(cx: @ext_ctxt, span: span, + ty: @ast::Ty, mutbl: ast::mutability) -> @ast::Ty { + mk_ty(cx, span, ast::ty_box(mk_ty_mt(ty, mutbl))) +} + + + pub fn mk_ty_infer(cx: @ext_ctxt, span: span) -> @ast::Ty { - @ast::Ty { - id: cx.next_id(), - node: ast::ty_infer, - span: span, - } + mk_ty(cx, span, ast::ty_infer) } pub fn mk_trait_ref_global(cx: @ext_ctxt, span: span, diff --git a/src/libsyntax/ext/deriving/clone.rs b/src/libsyntax/ext/deriving/clone.rs index d996bca60a3..1c33fe35070 100644 --- a/src/libsyntax/ext/deriving/clone.rs +++ b/src/libsyntax/ext/deriving/clone.rs @@ -13,7 +13,6 @@ use codemap::span; use ext::base::ext_ctxt; use ext::build; use ext::deriving::generic::*; -use core::option::{None,Some}; pub fn expand_deriving_clone(cx: @ext_ctxt, @@ -22,13 +21,16 @@ pub fn expand_deriving_clone(cx: @ext_ctxt, in_items: ~[@item]) -> ~[@item] { let trait_def = TraitDef { - path: ~[~"core", ~"clone", ~"Clone"], + path: Path::new(~[~"core", ~"clone", ~"Clone"]), additional_bounds: ~[], + generics: LifetimeBounds::empty(), methods: ~[ MethodDef { name: ~"clone", - nargs: 0, - output_type: None, // return Self + generics: LifetimeBounds::empty(), + self_ty: borrowed_explicit_self(), + args: ~[], + ret_ty: Self, const_nonmatching: false, combine_substructure: cs_clone } @@ -66,7 +68,8 @@ fn cs_clone(cx: @ext_ctxt, span: span, ctor_ident = ~[ variant.node.name ]; all_fields = af; }, - EnumNonMatching(*) => cx.bug("Non-matching enum variants in `deriving(Clone)`") + EnumNonMatching(*) => cx.span_bug(span, "Non-matching enum variants in `deriving(Clone)`"), + StaticEnum(*) | StaticStruct(*) => cx.span_bug(span, "Static method in `deriving(Clone)`") } match all_fields { diff --git a/src/libsyntax/ext/deriving/cmp/eq.rs b/src/libsyntax/ext/deriving/cmp/eq.rs index c0060cc67dc..e431e1f78bf 100644 --- a/src/libsyntax/ext/deriving/cmp/eq.rs +++ b/src/libsyntax/ext/deriving/cmp/eq.rs @@ -8,15 +8,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. - use ast::{meta_item, item, expr}; use codemap::span; use ext::base::ext_ctxt; use ext::build; use ext::deriving::generic::*; -use core::option::Some; - pub fn expand_deriving_eq(cx: @ext_ctxt, span: span, mitem: @meta_item, @@ -24,28 +21,32 @@ pub fn expand_deriving_eq(cx: @ext_ctxt, // 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: @ext_ctxt, span: span, substr: &Substructure) -> @expr { - cs_and(|cx, span, _| build::mk_bool(cx, span, false), + cs_and(|cx, span, _, _| build::mk_bool(cx, span, false), cx, span, substr) } fn cs_ne(cx: @ext_ctxt, span: span, substr: &Substructure) -> @expr { - cs_or(|cx, span, _| build::mk_bool(cx, span, true), + cs_or(|cx, span, _, _| build::mk_bool(cx, span, true), cx, span, substr) } + macro_rules! md ( ($name:expr, $f:ident) => { MethodDef { name: $name, - output_type: Some(~[~"bool"]), - nargs: 1, + generics: LifetimeBounds::empty(), + self_ty: borrowed_explicit_self(), + args: ~[borrowed_self()], + ret_ty: Literal(Path::new(~[~"bool"])), const_nonmatching: true, combine_substructure: $f }, } - ) + ); let trait_def = TraitDef { - path: ~[~"core", ~"cmp", ~"Eq"], + path: Path::new(~[~"core", ~"cmp", ~"Eq"]), additional_bounds: ~[], + generics: LifetimeBounds::empty(), methods: ~[ md!(~"eq", cs_eq), md!(~"ne", cs_ne) diff --git a/src/libsyntax/ext/deriving/cmp/ord.rs b/src/libsyntax/ext/deriving/cmp/ord.rs index 398e27eb3e3..5998fc7145d 100644 --- a/src/libsyntax/ext/deriving/cmp/ord.rs +++ b/src/libsyntax/ext/deriving/cmp/ord.rs @@ -14,29 +14,33 @@ use codemap::span; use ext::base::ext_ctxt; use ext::build; use ext::deriving::generic::*; -use core::option::Some; - -macro_rules! md { - ($name:expr, $less:expr, $equal:expr) => { - MethodDef { - name: $name, - output_type: Some(~[~"bool"]), - nargs: 1, - const_nonmatching: false, - combine_substructure: |cx, span, substr| - cs_ord($less, $equal, cx, span, substr) - } - } -} pub fn expand_deriving_ord(cx: @ext_ctxt, span: span, mitem: @meta_item, in_items: ~[@item]) -> ~[@item] { + macro_rules! md ( + ($name:expr, $less:expr, $equal:expr) => { + MethodDef { + name: $name, + generics: LifetimeBounds::empty(), + self_ty: borrowed_explicit_self(), + args: ~[borrowed_self()], + ret_ty: Literal(Path::new(~[~"bool"])), + const_nonmatching: false, + combine_substructure: |cx, span, substr| + cs_ord($less, $equal, cx, span, substr) + } + } + ); + + + let trait_def = TraitDef { - path: ~[~"core", ~"cmp", ~"Ord"], + path: Path::new(~[~"core", ~"cmp", ~"Ord"]), // XXX: Ord doesn't imply Eq yet - additional_bounds: ~[~[~"core", ~"cmp", ~"Eq"]], + additional_bounds: ~[Literal(Path::new(~[~"core", ~"cmp", ~"Eq"]))], + generics: LifetimeBounds::empty(), methods: ~[ md!(~"lt", true, false), md!(~"le", true, true), @@ -97,19 +101,19 @@ fn cs_ord(less: bool, equal: bool, } let cmp = build::mk_method_call(cx, span, - self_f, cx.ident_of(~"eq"), other_fs); + self_f, cx.ident_of(~"eq"), other_fs.to_owned()); let subexpr = build::mk_simple_block(cx, span, subexpr); let elseif = expr_if(cmp, subexpr, Some(false_blk_expr)); let elseif = build::mk_expr(cx, span, elseif); let cmp = build::mk_method_call(cx, span, - self_f, binop, other_fs); + self_f, binop, other_fs.to_owned()); let if_ = expr_if(cmp, true_blk, Some(elseif)); build::mk_expr(cx, span, if_) }, base, - |cx, span, args| { + |cx, span, args, _| { // nonmatching enums, order by the order the variants are // written match args { diff --git a/src/libsyntax/ext/deriving/cmp/totaleq.rs b/src/libsyntax/ext/deriving/cmp/totaleq.rs index fc8ec103a60..068a7bc06b1 100644 --- a/src/libsyntax/ext/deriving/cmp/totaleq.rs +++ b/src/libsyntax/ext/deriving/cmp/totaleq.rs @@ -15,26 +15,27 @@ use ext::base::ext_ctxt; use ext::build; use ext::deriving::generic::*; -use core::option::Some; - pub fn expand_deriving_totaleq(cx: @ext_ctxt, span: span, mitem: @meta_item, in_items: ~[@item]) -> ~[@item] { fn cs_equals(cx: @ext_ctxt, span: span, substr: &Substructure) -> @expr { - cs_and(|cx, span, _| build::mk_bool(cx, span, false), + cs_and(|cx, span, _, _| build::mk_bool(cx, span, false), cx, span, substr) } let trait_def = TraitDef { - path: ~[~"core", ~"cmp", ~"TotalEq"], + path: Path::new(~[~"core", ~"cmp", ~"TotalEq"]), additional_bounds: ~[], + generics: LifetimeBounds::empty(), methods: ~[ MethodDef { name: ~"equals", - output_type: Some(~[~"bool"]), - nargs: 1, + generics: LifetimeBounds::empty(), + self_ty: borrowed_explicit_self(), + args: ~[borrowed_self()], + ret_ty: Literal(Path::new(~[~"bool"])), const_nonmatching: true, combine_substructure: cs_equals } diff --git a/src/libsyntax/ext/deriving/cmp/totalord.rs b/src/libsyntax/ext/deriving/cmp/totalord.rs index a098a7463d3..ac873c5bd12 100644 --- a/src/libsyntax/ext/deriving/cmp/totalord.rs +++ b/src/libsyntax/ext/deriving/cmp/totalord.rs @@ -14,20 +14,22 @@ use ext::base::ext_ctxt; use ext::build; use ext::deriving::generic::*; use core::cmp::{Ordering, Equal, Less, Greater}; -use core::option::Some; pub fn expand_deriving_totalord(cx: @ext_ctxt, span: span, mitem: @meta_item, in_items: ~[@item]) -> ~[@item] { let trait_def = TraitDef { - path: ~[~"core", ~"cmp", ~"TotalOrd"], + path: Path::new(~[~"core", ~"cmp", ~"TotalOrd"]), additional_bounds: ~[], + generics: LifetimeBounds::empty(), methods: ~[ MethodDef { name: ~"cmp", - output_type: Some(~[~"core", ~"cmp", ~"Ordering"]), - nargs: 1, + generics: LifetimeBounds::empty(), + self_ty: borrowed_explicit_self(), + args: ~[borrowed_self()], + ret_ty: Literal(Path::new(~[~"core", ~"cmp", ~"Ordering"])), const_nonmatching: false, combine_substructure: cs_cmp } @@ -64,7 +66,7 @@ pub fn cs_cmp(cx: @ext_ctxt, span: span, build::mk_call_global(cx, span, lexical_ord, ~[old, new]) }, ordering_const(cx, span, Equal), - |cx, span, list| { + |cx, span, list, _| { match list { // an earlier nonmatching variant is Less than a // later one diff --git a/src/libsyntax/ext/deriving/decodable.rs b/src/libsyntax/ext/deriving/decodable.rs index fe270abc2e4..79cc4a3bda8 100644 --- a/src/libsyntax/ext/deriving/decodable.rs +++ b/src/libsyntax/ext/deriving/decodable.rs @@ -66,6 +66,7 @@ fn create_derived_decodable_impl( cx.ident_of(~"serialize"), cx.ident_of(~"Decodable") ], + None, ~[ build::mk_simple_ty_path(cx, span, cx.ident_of(~"__D")) ] @@ -77,7 +78,7 @@ fn create_derived_decodable_impl( generics, methods, trait_path, - generic_ty_params, + Generics { ty_params: generic_ty_params, lifetimes: opt_vec::Empty }, opt_vec::Empty ) } @@ -96,6 +97,7 @@ fn create_decode_method( cx, span, build::mk_simple_ty_path(cx, span, cx.ident_of(~"__D")), + None, ast::m_mutbl ); let d_ident = cx.ident_of(~"__d"); diff --git a/src/libsyntax/ext/deriving/encodable.rs b/src/libsyntax/ext/deriving/encodable.rs index 8f8139790ad..8b86173dc24 100644 --- a/src/libsyntax/ext/deriving/encodable.rs +++ b/src/libsyntax/ext/deriving/encodable.rs @@ -66,6 +66,7 @@ fn create_derived_encodable_impl( cx.ident_of(~"serialize"), cx.ident_of(~"Encodable") ], + None, ~[ build::mk_simple_ty_path(cx, span, cx.ident_of(~"__E")) ] @@ -77,7 +78,7 @@ fn create_derived_encodable_impl( generics, methods, trait_path, - generic_ty_params, + Generics { ty_params: generic_ty_params, lifetimes: opt_vec::Empty }, opt_vec::Empty ) } @@ -94,6 +95,7 @@ fn create_encode_method( cx, span, build::mk_simple_ty_path(cx, span, cx.ident_of(~"__E")), + None, ast::m_mutbl ); let e_arg = build::mk_arg(cx, span, cx.ident_of(~"__e"), e_arg_type); @@ -303,7 +305,7 @@ fn expand_deriving_encodable_enum_method( // Create the arms of the match in the method body. let arms = do enum_definition.variants.mapi |i, variant| { // Create the matching pattern. - let pat = create_enum_variant_pattern(cx, span, variant, ~"__self"); + let (pat, fields) = create_enum_variant_pattern(cx, span, variant, ~"__self", ast::m_imm); // Feed the discriminant to the encode function. let mut stmts = ~[]; @@ -311,11 +313,7 @@ fn expand_deriving_encodable_enum_method( // Feed each argument in this variant to the encode function // as well. let variant_arg_len = variant_arg_count(cx, span, variant); - for uint::range(0, variant_arg_len) |j| { - // Create the expression for this field. - let field_ident = cx.ident_of(~"__self_" + j.to_str()); - let field = build::mk_path(cx, span, ~[ field_ident ]); - + for fields.eachi |j, &(_, field)| { // Call the substructure method. let expr = call_substructure_encode_method(cx, span, field); diff --git a/src/libsyntax/ext/deriving/generic.rs b/src/libsyntax/ext/deriving/generic.rs index 05941f4cbd6..565d6dd59ba 100644 --- a/src/libsyntax/ext/deriving/generic.rs +++ b/src/libsyntax/ext/deriving/generic.rs @@ -16,23 +16,22 @@ 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 type `&Self`, including - none other than `self`. (`MethodDef.nargs`) -- Methods returning `Self` or a non-parameterised type - (e.g. `bool` or `core::cmp::Ordering`). (`MethodDef.output_type`) -- Generating `impl`s for types with type parameters +- 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`), the parameters are automatically given the - current trait as a bound. + current trait as a bound. (This includes separate type parameters + and lifetimes for methods.) - Additional bounds on the type parameters, e.g. the `Ord` instance requires an explicit `Eq` bound at the moment. (`TraitDef.additional_bounds`) -(Key unsupported things: methods with arguments of non-`&Self` type, -traits with parameters, methods returning parameterised types, static -methods.) +Unsupported: FIXME #6257: calling methods on borrowed pointer fields, +e.g. deriving TotalEq/TotalOrd/Clone don't work on `struct A(&int)`, +because of how the auto-dereferencing happens. The most important thing for implementers is the `Substructure` and -`SubstructureFields` objects. The latter groups 3 possibilities of the +`SubstructureFields` objects. The latter groups 5 possibilities of the arguments: - `Struct`, when `Self` is a struct (including tuple structs, e.g @@ -42,42 +41,57 @@ arguments: - `EnumNonMatching` when `Self` is an enum and the arguments are not the same variant (e.g. `None`, `Some(1)` and `None`). If `const_nonmatching` is true, this will contain an empty list. +- `StaticEnum` and `StaticStruct` for static methods, where the type + being derived upon is either a 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. In the `EnumNonMatching` case this isn't possible (different variants have different fields), so the -fields are grouped by which argument they come from. +fields are grouped by which argument they come from. There are no +fields with values in the static cases, so these are treated entirely +differently. -All of the cases have `Option` in several places associated +The non-static cases have `Option` 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 - struct A { x : int } +~~~ +struct A { x : int } - struct B(int); +struct B(int); - enum C { - C0(int), - C1 { x: int } - } +enum C { + C0(int), + C1 { x: int } +} The `int`s in `B` and `C0` don't have an identifier, so the `Option`s would be `None` for them. +In the static cases, the structure is summarised, either into the +number of fields or a list of 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. + # Examples The following simplified `Eq` is used for in-code examples: - trait Eq { - fn eq(&self, other: &Self); - } - impl Eq for int { - fn eq(&self, other: &int) -> bool { - *self == *other - } +~~~ +trait Eq { + fn eq(&self, other: &Self); +} +impl Eq for int { + fn eq(&self, other: &int) -> bool { + *self == *other } +} +~~~ Some examples of the values of `SubstructureFields` follow, using the above `Eq`, `A`, `B` and `C`. @@ -86,65 +100,85 @@ above `Eq`, `A`, `B` and `C`. When generating the `expr` for the `A` impl, the `SubstructureFields` is - Struct(~[(Some(), - , - ~[), + , + ~[ - ~[])]) +~~~ +Struct(~[(None, + + ~[])]) +~~~ ## Enums When generating the `expr` for a call with `self == C0(a)` and `other == C0(b)`, the SubstructureFields is - EnumMatching(0, , - ~[None, - , - ~[]]) +~~~ +EnumMatching(0, , + ~[None, + , + ~[]]) +~~~ For `C1 {x}` and `C1 {x}`, - EnumMatching(1, , - ~[Some(), - , - ~[]]) +~~~ +EnumMatching(1, , + ~[Some(), + , + ~[]]) +~~~ For `C0(a)` and `C1 {x}` , - EnumNonMatching(~[(0, , - ~[(None, )]), - (1, , - ~[(Some(), - )])]) +~~~ +EnumNonMatching(~[(0, , + ~[(None, )]), + (1, , + ~[(Some(), + )])]) +~~~ -(and vice verse, but with the order of the outermost list flipped.) +(and vice versa, but with the order of the outermost list flipped.) + +## Static + +A static method on the above would result in, + +~~~~ +StaticStruct(, Right(~[])) + +StaticStruct(, Left(1)) + +StaticEnum(, ~[(, Left(1)), + (, Right(~[]))]) +~~~ */ use ast; +use ast::{enum_def, expr, ident, Generics, struct_def}; -use ast::{ - and, binop, deref, enum_def, expr, expr_match, ident, impure_fn, - item, Generics, m_imm, meta_item, method, named_field, or, - pat_wild, public, struct_def, sty_region, ty_rptr, ty_path, - variant}; - -use ast_util; use ext::base::ext_ctxt; use ext::build; use ext::deriving::*; use codemap::{span,respan}; use opt_vec; +pub use self::ty::*; +mod ty; + pub fn expand_deriving_generic(cx: @ext_ctxt, span: span, - _mitem: @meta_item, - in_items: ~[@item], - trait_def: &TraitDef) -> ~[@item] { + _mitem: @ast::meta_item, + in_items: ~[@ast::item], + trait_def: &TraitDef) -> ~[@ast::item] { let expand_enum: ExpandDerivingEnumDefFn = |cx, span, enum_def, type_ident, generics| { trait_def.expand_enum_def(cx, span, enum_def, type_ident, generics) @@ -160,25 +194,38 @@ pub fn expand_deriving_generic(cx: @ext_ctxt, } pub struct TraitDef<'self> { - /// Path of the trait - path: ~[~str], - /// Additional bounds required of any type parameters, other than - /// the current trait - additional_bounds: ~[~[~str]], + /// Path of the trait, including any type parameters + path: Path, + /// Additional bounds required of any type parameters of the type, + /// other than the current trait + additional_bounds: ~[Ty], + + /// Any extra lifetimes and/or bounds, e.g. `D: std::serialize::Decoder` + generics: LifetimeBounds, + methods: ~[MethodDef<'self>] } + pub struct MethodDef<'self> { /// name of the method name: ~str, - /// The path of return type of the method, e.g. `~[~"core", - /// ~"cmp", ~"Eq"]`. `None` for `Self`. - output_type: Option<~[~str]>, - /// Number of arguments other than `self` (all of type `&Self`) - nargs: uint, + /// List of generics, e.g. `R: core::rand::Rng` + generics: LifetimeBounds, + + /// Whether there is a self argument (outer Option) i.e. whether + /// this is a static function, and whether it is a pointer (inner + /// Option) + self_ty: Option>, + + /// Arguments other than the self argument + args: ~[Ty], + + /// Return type + ret_ty: Ty, /// if the value of the nonmatching enums is independent of the - /// actual enums, i.e. can use _ => .. match. + /// actual enum variants, i.e. can use _ => .. match. const_nonmatching: bool, combine_substructure: CombineSubstructureFunc<'self> @@ -186,18 +233,24 @@ pub struct MethodDef<'self> { /// All the data about the data structure/method being derived upon. pub struct Substructure<'self> { + /// ident of self type_ident: ident, + /// ident of the method method_ident: ident, - fields: &'self SubstructureFields + /// dereferenced access to any Self or Ptr(Self, _) arguments + self_args: &'self [@expr], + /// verbatim access to any other arguments + nonself_args: &'self [@expr], + fields: &'self SubstructureFields<'self> } /// A summary of the possible sets of fields. See above for details /// and examples -pub enum SubstructureFields { +pub enum SubstructureFields<'self> { /** - Vec of `(field ident, self, [others])` where the field ident is - the ident of the current field (`None` for all fields in tuple - structs) + Vec of `(field ident, self_or_other)` where the field + ident is the ident of the current field (`None` for all fields in tuple + structs). */ Struct(~[(Option, @expr, ~[@expr])]), @@ -206,17 +259,23 @@ pub enum SubstructureFields { fields: `(field ident, self, [others])`, where the field ident is only non-`None` in the case of a struct variant. */ - EnumMatching(uint, variant, ~[(Option, @expr, ~[@expr])]), + EnumMatching(uint, ast::variant, ~[(Option, @expr, ~[@expr])]), /** non-matching variants of the enum, [(variant index, ast::variant, [field ident, fields])] (i.e. all fields for self are in the first tuple, for other1 are in the second tuple, etc.) */ - EnumNonMatching(~[(uint, variant, ~[(Option, @expr)])]) + EnumNonMatching(~[(uint, ast::variant, ~[(Option, @expr)])]), + + /// A static method where Self is a struct + StaticStruct(&'self ast::struct_def, Either), + /// A static method where Self is an enum + StaticEnum(&'self ast::enum_def, ~[(ident, Either)]) } + /** Combine the values of all the fields together. The last argument is all the fields of all the structures, see above for details. @@ -225,31 +284,34 @@ pub type CombineSubstructureFunc<'self> = &'self fn(@ext_ctxt, span, &Substructure) -> @expr; /** -Deal with non-matching enum variants, the argument is a list +Deal with non-matching enum variants, the arguments are a list representing each variant: (variant index, ast::variant instance, -[variant fields]) +[variant fields]), and a list of the nonself args of the type */ pub type EnumNonMatchFunc<'self> = - &'self fn(@ext_ctxt, span, ~[(uint, variant, ~[(Option, @expr)])]) -> @expr; - + &'self fn(@ext_ctxt, span, + ~[(uint, ast::variant, + ~[(Option, @expr)])], + &[@expr]) -> @expr; impl<'self> TraitDef<'self> { fn create_derived_impl(&self, cx: @ext_ctxt, span: span, type_ident: ident, generics: &Generics, - methods: ~[@method]) -> @item { - let trait_path = build::mk_raw_path_global( - span, - do self.path.map |&s| { cx.ident_of(s) }); + methods: ~[@ast::method]) -> @ast::item { + let trait_path = self.path.to_path(cx, span, type_ident, generics); + + let trait_generics = self.generics.to_generics(cx, span, type_ident, generics); let additional_bounds = opt_vec::from( - do self.additional_bounds.map |v| { - do v.map |&s| { cx.ident_of(s) } + do self.additional_bounds.map |p| { + p.to_path(cx, span, type_ident, generics) }); + create_derived_impl(cx, span, type_ident, generics, methods, trait_path, - opt_vec::Empty, + trait_generics, additional_bounds) } @@ -257,22 +319,28 @@ impl<'self> TraitDef<'self> { span: span, struct_def: &struct_def, type_ident: ident, - generics: &Generics) - -> @item { - let is_tuple = is_struct_tuple(struct_def); - + generics: &Generics) -> @ast::item { let methods = do self.methods.map |method_def| { - let body = if is_tuple { - method_def.expand_struct_tuple_method_body(cx, span, - struct_def, - type_ident) + let (self_ty, self_args, nonself_args, tys) = + method_def.split_self_nonself_args(cx, span, type_ident, generics); + + let body = if method_def.is_static() { + method_def.expand_static_struct_method_body( + cx, span, + struct_def, + type_ident, + self_args, nonself_args) } else { method_def.expand_struct_method_body(cx, span, struct_def, - type_ident) + type_ident, + self_args, nonself_args) }; - method_def.create_method(cx, span, type_ident, generics, body) + method_def.create_method(cx, span, + type_ident, generics, + self_ty, tys, + body) }; self.create_derived_impl(cx, span, type_ident, generics, methods) @@ -282,13 +350,28 @@ impl<'self> TraitDef<'self> { cx: @ext_ctxt, span: span, enum_def: &enum_def, type_ident: ident, - generics: &Generics) -> @item { + generics: &Generics) -> @ast::item { let methods = do self.methods.map |method_def| { - let body = method_def.expand_enum_method_body(cx, span, - enum_def, - type_ident); + let (self_ty, self_args, nonself_args, tys) = + method_def.split_self_nonself_args(cx, span, type_ident, generics); - method_def.create_method(cx, span, type_ident, generics, body) + let body = if method_def.is_static() { + method_def.expand_static_enum_method_body( + cx, span, + enum_def, + type_ident, + self_args, nonself_args) + } else { + method_def.expand_enum_method_body(cx, span, + enum_def, + type_ident, + self_args, nonself_args) + }; + + method_def.create_method(cx, span, + type_ident, generics, + self_ty, tys, + body) }; self.create_derived_impl(cx, span, type_ident, generics, methods) @@ -300,266 +383,241 @@ impl<'self> MethodDef<'self> { cx: @ext_ctxt, span: span, type_ident: ident, + self_args: &[@expr], + nonself_args: &[@expr], fields: &SubstructureFields) -> @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 }; (self.combine_substructure)(cx, span, &substructure) } - fn get_output_type_path(&self, cx: @ext_ctxt, span: span, - generics: &Generics, type_ident: ident) -> @ast::Path { - match self.output_type { - None => { // Self, add any type parameters - let out_ty_params = do vec::build |push| { - for generics.ty_params.each |ty_param| { - push(build::mk_ty_path(cx, span, ~[ ty_param.ident ])); - } - }; + fn get_ret_ty(&self, cx: @ext_ctxt, span: span, + generics: &Generics, type_ident: ident) -> @ast::Ty { + self.ret_ty.to_ty(cx, span, type_ident, generics) + } - build::mk_raw_path_(span, ~[ type_ident ], out_ty_params) + fn is_static(&self) -> bool { + self.self_ty.is_none() + } + + fn split_self_nonself_args(&self, cx: @ext_ctxt, span: span, + type_ident: ident, generics: &Generics) + -> (ast::self_ty, ~[@expr], ~[@expr], ~[(ident, @ast::Ty)]) { + + let mut self_args = ~[], nonself_args = ~[], arg_tys = ~[]; + let mut ast_self_ty = respan(span, ast::sty_static); + let mut nonstatic = false; + + match self.self_ty { + Some(self_ptr) => { + let (self_expr, self_ty) = ty::get_explicit_self(cx, span, self_ptr); + + ast_self_ty = self_ty; + self_args.push(self_expr); + nonstatic = true; } - Some(str_path) => { - let p = do str_path.map |&s| { cx.ident_of(s) }; - build::mk_raw_path_global(span, p) + _ => {} + } + + for self.args.eachi |i, ty| { + let ast_ty = ty.to_ty(cx, span, type_ident, generics); + let ident = cx.ident_of(fmt!("__arg_%u", i)); + arg_tys.push((ident, ast_ty)); + + let arg_expr = build::mk_path(cx, 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(~Self, _) if nonstatic => { + self_args.push(build::mk_deref(cx, span, arg_expr)) + } + _ => { + nonself_args.push(arg_expr); + } } } + + (ast_self_ty, self_args, nonself_args, arg_tys) } fn create_method(&self, cx: @ext_ctxt, span: span, type_ident: ident, - generics: &Generics, body: @expr) -> @method { - // Create the `Self` type of the `other` parameters. - let arg_path_type = create_self_type_with_params(cx, - span, - type_ident, - generics); - let arg_type = ty_rptr( - None, - ast::mt { ty: arg_path_type, mutbl: m_imm } - ); - let arg_type = @ast::Ty { - id: cx.next_id(), - node: arg_type, - span: span, + generics: &Generics, + self_ty: ast::self_ty, + arg_types: ~[(ident, @ast::Ty)], + body: @expr) -> @ast::method { + // create the generics that aren't for Self + let fn_generics = self.generics.to_generics(cx, span, type_ident, generics); + + let args = do arg_types.map |&(id, ty)| { + build::mk_arg(cx, span, id, ty) }; - // create the arguments - let other_idents = create_other_idents(cx, self.nargs); - let args = do other_idents.map |&id| { - build::mk_arg(cx, span, id, arg_type) - }; - - let output_type = self.get_output_type_path(cx, span, generics, type_ident); - let output_type = ty_path(output_type, cx.next_id()); - let output_type = @ast::Ty { - id: cx.next_id(), - node: output_type, - span: span, - }; + let ret_type = self.get_ret_ty(cx, span, generics, type_ident); let method_ident = cx.ident_of(self.name); - let fn_decl = build::mk_fn_decl(args, output_type); + let fn_decl = build::mk_fn_decl(args, ret_type); let body_block = build::mk_simple_block(cx, span, body); + // Create the method. - let self_ty = respan(span, sty_region(None, m_imm)); @ast::method { ident: method_ident, attrs: ~[], - generics: ast_util::empty_generics(), + generics: fn_generics, self_ty: self_ty, - purity: impure_fn, + purity: ast::impure_fn, decl: fn_decl, body: body_block, id: cx.next_id(), span: span, self_id: cx.next_id(), - vis: public + vis: ast::public } } /** - ``` + ~~~ #[deriving(Eq)] - struct A(int, int); + struct A { x: int, y: int } // equivalent to: - impl Eq for A { - fn eq(&self, __other_1: &A) -> bool { + fn eq(&self, __arg_1: &A) -> bool { match *self { - (ref self_1, ref self_2) => { - match *__other_1 { - (ref __other_1_1, ref __other_1_2) => { - self_1.eq(__other_1_1) && self_2.eq(__other_1_2) + 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_tuple_method_body(&self, - cx: @ext_ctxt, - span: span, - struct_def: &struct_def, - type_ident: ident) -> @expr { - let self_str = ~"self"; - let other_strs = create_other_strs(self.nargs); - let num_fields = struct_def.fields.len(); - - - let fields = do struct_def.fields.mapi |i, _| { - let other_fields = do other_strs.map |&other_str| { - let other_field_ident = cx.ident_of(fmt!("%s_%u", other_str, i)); - build::mk_path(cx, span, ~[ other_field_ident ]) - }; - - let self_field_ident = cx.ident_of(fmt!("%s_%u", self_str, i)); - let self_field = build::mk_path(cx, span, ~[ self_field_ident ]); - - (None, self_field, other_fields) - }; - - let mut match_body = self.call_substructure_method(cx, span, type_ident, &Struct(fields)); - - let type_path = build::mk_raw_path(span, ~[type_ident]); - - // create the matches from inside to out (i.e. other_{self.nargs} to other_1) - for other_strs.each_reverse |&other_str| { - match_body = create_deref_match(cx, span, type_path, - other_str, num_fields, - match_body) - } - - // create the match on self - return create_deref_match(cx, span, type_path, - ~"self", num_fields, match_body); - - /** - Creates a match expression against a tuple that needs to - be dereferenced, but nothing else - - ``` - match *`to_match` { - (`to_match`_1, ..., `to_match`_`num_fields`) => `match_body` - } - ``` - */ - fn create_deref_match(cx: @ext_ctxt, - span: span, - type_path: @ast::Path, - to_match: ~str, - num_fields: uint, - match_body: @expr) -> @expr { - let match_subpats = create_subpatterns(cx, span, to_match, num_fields); - let match_arm = ast::arm { - pats: ~[ build::mk_pat_enum(cx, span, type_path, match_subpats) ], - guard: None, - body: build::mk_simple_block(cx, span, match_body), - }; - - let deref_expr = build::mk_unary(cx, span, deref, - build::mk_path(cx, span, - ~[ cx.ident_of(to_match)])); - let match_expr = build::mk_expr(cx, span, expr_match(deref_expr, ~[match_arm])); - - match_expr - } - } - - /** - ``` - #[deriving(Eq)] - struct A { x: int, y: int } - - // equivalent to: - - impl Eq for A { - fn eq(&self, __other_1: &A) -> bool { - self.x.eq(&__other_1.x) && - self.y.eq(&__other_1.y) - } - } - ``` + ~~~ */ fn expand_struct_method_body(&self, - cx: @ext_ctxt, - span: span, - struct_def: &struct_def, - type_ident: ident) + cx: @ext_ctxt, + span: span, + struct_def: &struct_def, + type_ident: ident, + self_args: &[@expr], + nonself_args: &[@expr]) -> @expr { - let self_ident = cx.ident_of(~"self"); - let other_idents = create_other_idents(cx, self.nargs); - let fields = do struct_def.fields.map |struct_field| { - match struct_field.node.kind { - named_field(ident, _, _) => { - // Create the accessor for this field in the other args. - let other_fields = do other_idents.map |&id| { - build::mk_access(cx, span, ~[id], ident) - }; - let other_field_refs = do other_fields.map |&other_field| { - build::mk_addr_of(cx, span, other_field) - }; - - // Create the accessor for this field in self. - let self_field = - build::mk_access( - cx, span, - ~[ self_ident ], - ident); - - (Some(ident), self_field, other_field_refs) - } - unnamed_field => { - cx.span_unimpl(span, ~"unnamed fields with `deriving_generic`"); - } - } + let mut raw_fields = ~[], // ~[[fields of self], [fields of next Self arg], [etc]] + patterns = ~[]; + for uint::range(0, self_args.len()) |i| { + let (pat, ident_expr) = create_struct_pattern(cx, span, + type_ident, struct_def, + fmt!("__self_%u", i), ast::m_imm); + patterns.push(pat); + raw_fields.push(ident_expr); }; - self.call_substructure_method(cx, span, type_ident, &Struct(fields)) + // transpose raw_fields + let fields = match raw_fields { + [self_arg, .. rest] => { + do self_arg.mapi |i, &(opt_id, field)| { + let other_fields = do rest.map |l| { + match &l[i] { + &(_, ex) => ex + } + }; + (opt_id, field, other_fields) + } + } + [] => { cx.span_bug(span, ~"No self arguments to non-static \ + method in generic `deriving`") } + }; + + // body of the inner most destructuring match + let mut body = self.call_substructure_method( + cx, span, + 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 shoudn't + // matter. + for vec::each2(self_args, patterns) |&arg_expr, &pat| { + let match_arm = ast::arm { + pats: ~[ pat ], + guard: None, + body: build::mk_simple_block(cx, span, body) + }; + + body = build::mk_expr(cx, span, ast::expr_match(arg_expr, ~[match_arm])) + } + body + } + + fn expand_static_struct_method_body(&self, + cx: @ext_ctxt, + span: span, + struct_def: &struct_def, + type_ident: ident, + self_args: &[@expr], + nonself_args: &[@expr]) + -> @expr { + let summary = summarise_struct(cx, span, struct_def); + + self.call_substructure_method(cx, span, + type_ident, + self_args, nonself_args, + &StaticStruct(struct_def, summary)) } /** - ``` + ~~~ #[deriving(Eq)] enum A { A1 A2(int) } - // is equivalent to + // is equivalent to (with const_nonmatching == false) impl Eq for A { - fn eq(&self, __other_1: &A) { + fn eq(&self, __arg_1: &A) { match *self { - A1 => match *__other_1 { - A1 => true, - A2(ref __other_1_1) => false + A1 => match *__arg_1 { + A1 => true + A2(ref __arg_1_1) => false }, - A2(self_1) => match *__other_1 { + A2(self_1) => match *__arg_1 { A1 => false, - A2(ref __other_1_1) => self_1.eq(__other_1_1) + A2(ref __arg_1_1) => self_1.eq(__arg_1_1) } } } } - ``` + ~~~ */ fn expand_enum_method_body(&self, cx: @ext_ctxt, span: span, enum_def: &enum_def, - type_ident: ident) + type_ident: ident, + self_args: &[@expr], + nonself_args: &[@expr]) -> @expr { self.build_enum_match(cx, span, enum_def, type_ident, + self_args, nonself_args, None, ~[], 0) } @@ -567,13 +625,13 @@ impl<'self> MethodDef<'self> { /** Creates the nested matches for an enum definition recursively, i.e. - ``` + ~~~ match self { Variant1 => match other { Variant1 => matching, Variant2 => nonmatching, ... }, Variant2 => match other { Variant1 => nonmatching, Variant2 => matching, ... }, ... } - ``` + ~~~ It acts in the most naive way, so every branch (and subbranch, subsubbranch, etc) exists, not just the ones where all the variants in @@ -589,15 +647,17 @@ impl<'self> MethodDef<'self> { cx: @ext_ctxt, span: span, enum_def: &enum_def, type_ident: ident, + self_args: &[@expr], + nonself_args: &[@expr], matching: Option, - matches_so_far: ~[(uint, variant, + matches_so_far: ~[(uint, ast::variant, ~[(Option, @expr)])], match_count: uint) -> @expr { - if match_count == self.nargs + 1 { + if match_count == self_args.len() { // we've matched against all arguments, so make the final // expression at the bottom of the match tree match matches_so_far { - [] => cx.bug(~"no self match on an enum in `deriving_generic`"), + [] => cx.span_bug(span, ~"no self match on an enum in generic `deriving`"), _ => { // we currently have a vec of vecs, where each // subvec is the fields of one of the arguments, @@ -637,16 +697,17 @@ impl<'self> MethodDef<'self> { substructure = EnumNonMatching(matches_so_far); } } - self.call_substructure_method(cx, span, type_ident, &substructure) + self.call_substructure_method(cx, span, type_ident, + self_args, nonself_args, + &substructure) } } } else { // there are still matches to create - let (current_match_ident, current_match_str) = if match_count == 0 { - (cx.ident_of(~"self"), ~"__self") + let current_match_str = if match_count == 0 { + ~"__self" } else { - let s = fmt!("__other_%u", matches_so_far.len() - 1); - (cx.ident_of(s), s) + fmt!("__arg_%u", match_count) }; let mut arms = ~[]; @@ -654,80 +715,50 @@ impl<'self> MethodDef<'self> { // this is used as a stack let mut matches_so_far = matches_so_far; - macro_rules! mk_arm( - ($pat:expr, $expr:expr) => { - { - let blk = build::mk_simple_block(cx, span, $expr); - let arm = ast::arm { - pats: ~[$ pat ], - guard: None, - body: blk - }; - arm - } - } - ) - // the code for nonmatching variants only matters when // we've seen at least one other variant already if self.const_nonmatching && match_count > 0 { // make a matching-variant match, and a _ match. let index = match matching { Some(i) => i, - None => cx.span_bug(span, ~"Non-matching variants when required to\ - be matching in `deriving_generic`") + None => cx.span_bug(span, ~"Non-matching variants when required to \ + be matching in generic `deriving`") }; // matching-variant match let variant = &enum_def.variants[index]; - let pattern = create_enum_variant_pattern(cx, span, - variant, - current_match_str); - - let idents = do vec::build |push| { - for each_variant_arg_ident(cx, span, variant) |i, field_id| { - let id = cx.ident_of(fmt!("%s_%u", current_match_str, i)); - push((field_id, build::mk_path(cx, span, ~[ id ]))); - } - }; + let (pattern, idents) = create_enum_variant_pattern(cx, span, + variant, + current_match_str, + ast::m_imm); matches_so_far.push((index, *variant, idents)); let arm_expr = self.build_enum_match(cx, span, enum_def, type_ident, + self_args, nonself_args, matching, matches_so_far, match_count + 1); matches_so_far.pop(); - let arm = mk_arm!(pattern, arm_expr); - arms.push(arm); + arms.push(build::mk_arm(cx, span, ~[ pattern ], arm_expr)); if enum_def.variants.len() > 1 { - // _ match, if necessary - let wild_pat = @ast::pat { - id: cx.next_id(), - node: pat_wild, - span: span - }; - let wild_expr = self.call_substructure_method(cx, span, type_ident, + self_args, nonself_args, &EnumNonMatching(~[])); - let wild_arm = mk_arm!(wild_pat, wild_expr); + let wild_arm = build::mk_arm(cx, span, + ~[ build::mk_pat_wild(cx, span) ], + wild_expr); arms.push(wild_arm); } } else { // create an arm matching on each variant for enum_def.variants.eachi |index, variant| { - let pattern = create_enum_variant_pattern(cx, span, - variant, - current_match_str); - - let idents = do vec::build |push| { - for each_variant_arg_ident(cx, span, variant) |i, field_id| { - let id = cx.ident_of(fmt!("%s_%u", current_match_str, i)); - push((field_id, build::mk_path(cx, span, ~[ id ]))); - } - }; + let (pattern, idents) = create_enum_variant_pattern(cx, span, + variant, + current_match_str, + ast::m_imm); matches_so_far.push((index, *variant, idents)); let new_matching = @@ -739,44 +770,75 @@ impl<'self> MethodDef<'self> { let arm_expr = self.build_enum_match(cx, span, enum_def, type_ident, + self_args, nonself_args, new_matching, matches_so_far, match_count + 1); matches_so_far.pop(); - let arm = mk_arm!(pattern, arm_expr); + let arm = build::mk_arm(cx, span, ~[ pattern ], arm_expr); arms.push(arm); } } - let deref_expr = build::mk_unary(cx, span, deref, - build::mk_path(cx, span, - ~[ current_match_ident ])); - let match_expr = build::mk_expr(cx, span, - expr_match(deref_expr, arms)); - match_expr + // match foo { arm, arm, arm, ... } + build::mk_expr(cx, span, + ast::expr_match(self_args[match_count], arms)) } } + + fn expand_static_enum_method_body(&self, + cx: @ext_ctxt, + span: span, + enum_def: &enum_def, + type_ident: ident, + self_args: &[@expr], + nonself_args: &[@expr]) + -> @expr { + let summary = do enum_def.variants.map |v| { + let ident = v.node.name; + let summary = match v.node.kind { + ast::tuple_variant_kind(ref args) => Left(args.len()), + ast::struct_variant_kind(struct_def) => { + summarise_struct(cx, span, struct_def) + } + }; + (ident, summary) + }; + self.call_substructure_method(cx, + span, type_ident, + self_args, nonself_args, + &StaticEnum(enum_def, summary)) + } } -/// Create variable names (as strings) to refer to the non-self -/// parameters -fn create_other_strs(n: uint) -> ~[~str] { - do vec::build |push| { - for uint::range(0, n) |i| { - push(fmt!("__other_%u", i)); +fn summarise_struct(cx: @ext_ctxt, span: span, + struct_def: &struct_def) -> Either { + let mut named_idents = ~[]; + let mut unnamed_count = 0; + for struct_def.fields.each |field| { + match field.node.kind { + ast::named_field(ident, _, _) => { + named_idents.push(ident) + } + ast::unnamed_field => { + unnamed_count += 1; + } } } -} -/// Like `create_other_strs`, but returns idents for the strings -fn create_other_idents(cx: @ext_ctxt, n: uint) -> ~[ident] { - do create_other_strs(n).map |&s| { - cx.ident_of(s) + + match (unnamed_count > 0, named_idents.is_empty()) { + (true, false) => cx.span_bug(span, + "A struct with named and unnamed \ + fields in generic `deriving`"), + // named fields + (_, false) => Right(named_idents), + // tuple structs (includes empty structs) + (_, _) => Left(unnamed_count) } } - /* helpful premade recipes */ /** @@ -786,7 +848,7 @@ left-to-right (`true`) or right-to-left (`false`). pub fn cs_fold(use_foldl: bool, f: &fn(@ext_ctxt, span, old: @expr, - self_f: @expr, other_fs: ~[@expr]) -> @expr, + self_f: @expr, other_fs: &[@expr]) -> @expr, base: @expr, enum_nonmatch_f: EnumNonMatchFunc, cx: @ext_ctxt, span: span, @@ -803,7 +865,11 @@ pub fn cs_fold(use_foldl: bool, } } }, - EnumNonMatching(all_enums) => enum_nonmatch_f(cx, span, all_enums) + EnumNonMatching(all_enums) => enum_nonmatch_f(cx, span, + all_enums, substructure.nonself_args), + StaticEnum(*) | StaticStruct(*) => { + cx.span_bug(span, "Static function in `deriving`") + } } } @@ -812,11 +878,12 @@ pub fn cs_fold(use_foldl: bool, Call the method that is being derived on all the fields, and then process the collected results. i.e. -``` -f(cx, span, ~[self_1.method(__other_1_1, __other_2_1), - self_2.method(__other_1_2, __other_2_2)]) -``` +~~~ +f(cx, span, ~[self_1.method(__arg_1_1, __arg_2_1), + self_2.method(__arg_1_2, __arg_2_2)]) +~~~ */ +#[inline(always)] pub fn cs_same_method(f: &fn(@ext_ctxt, span, ~[@expr]) -> @expr, enum_nonmatch_f: EnumNonMatchFunc, cx: @ext_ctxt, span: span, @@ -833,7 +900,11 @@ pub fn cs_same_method(f: &fn(@ext_ctxt, span, ~[@expr]) -> @expr, f(cx, span, called) }, - EnumNonMatching(all_enums) => enum_nonmatch_f(cx, span, all_enums) + EnumNonMatching(all_enums) => enum_nonmatch_f(cx, span, + all_enums, substructure.nonself_args), + StaticEnum(*) | StaticStruct(*) => { + cx.span_bug(span, "Static function in `deriving`") + } } } @@ -842,6 +913,7 @@ Fold together the results of calling the derived method on all the fields. `use_foldl` controls whether this is done left-to-right (`true`) or right-to-left (`false`). */ +#[inline(always)] pub fn cs_same_method_fold(use_foldl: bool, f: &fn(@ext_ctxt, span, @expr, @expr) -> @expr, base: @expr, @@ -869,7 +941,8 @@ pub fn cs_same_method_fold(use_foldl: bool, Use a given binop to combine the result of calling the derived method on all the fields. */ -pub fn cs_binop(binop: binop, base: @expr, +#[inline(always)] +pub fn cs_binop(binop: ast::binop, base: @expr, enum_nonmatch_f: EnumNonMatchFunc, cx: @ext_ctxt, span: span, substructure: &Substructure) -> @expr { @@ -887,18 +960,20 @@ pub fn cs_binop(binop: binop, base: @expr, } /// cs_binop with binop == or +#[inline(always)] pub fn cs_or(enum_nonmatch_f: EnumNonMatchFunc, cx: @ext_ctxt, span: span, substructure: &Substructure) -> @expr { - cs_binop(or, build::mk_bool(cx, span, false), + cs_binop(ast::or, build::mk_bool(cx, span, false), enum_nonmatch_f, cx, span, substructure) } /// cs_binop with binop == and +#[inline(always)] pub fn cs_and(enum_nonmatch_f: EnumNonMatchFunc, cx: @ext_ctxt, span: span, substructure: &Substructure) -> @expr { - cs_binop(and, build::mk_bool(cx, span, true), + cs_binop(ast::and, build::mk_bool(cx, span, true), enum_nonmatch_f, cx, span, substructure) } diff --git a/src/libsyntax/ext/deriving/mod.rs b/src/libsyntax/ext/deriving/mod.rs index 5aeeef2b17a..9f3972e71ac 100644 --- a/src/libsyntax/ext/deriving/mod.rs +++ b/src/libsyntax/ext/deriving/mod.rs @@ -12,14 +12,7 @@ /// #[deriving(IterBytes)] extensions. use ast; -use ast::{Ty, bind_by_ref, deref, enum_def}; -use ast::{expr, expr_match, ident, item, item_}; -use ast::{item_enum, item_impl, item_struct, Generics}; -use ast::{m_imm, meta_item, method}; -use ast::{named_field, pat, pat_ident, public}; -use ast::{struct_def, struct_variant_kind}; -use ast::{tuple_variant_kind}; -use ast::{ty_path, unnamed_field, variant}; +use ast::{Ty, enum_def, expr, ident, item, Generics, meta_item, struct_def}; use ext::base::ext_ctxt; use ext::build; use codemap::{span, respan}; @@ -78,23 +71,21 @@ pub fn expand_meta_deriving(cx: @ext_ctxt, meta_name_value(tname, _) | meta_list(tname, _) | meta_word(tname) => { + macro_rules! expand(($func:path) => ($func(cx, titem.span, + titem, in_items))); match *tname { - ~"Clone" => clone::expand_deriving_clone(cx, - titem.span, titem, in_items), - ~"IterBytes" => iter_bytes::expand_deriving_iter_bytes(cx, - titem.span, titem, in_items), - ~"Encodable" => encodable::expand_deriving_encodable(cx, - titem.span, titem, in_items), - ~"Decodable" => decodable::expand_deriving_decodable(cx, - titem.span, titem, in_items), - ~"Eq" => eq::expand_deriving_eq(cx, titem.span, - titem, in_items), - ~"TotalEq" => totaleq::expand_deriving_totaleq(cx, titem.span, - titem, in_items), - ~"Ord" => ord::expand_deriving_ord(cx, titem.span, - titem, in_items), - ~"TotalOrd" => totalord::expand_deriving_totalord(cx, titem.span, - titem, in_items), + ~"Clone" => expand!(clone::expand_deriving_clone), + + ~"IterBytes" => expand!(iter_bytes::expand_deriving_iter_bytes), + + ~"Encodable" => expand!(encodable::expand_deriving_encodable), + ~"Decodable" => expand!(decodable::expand_deriving_decodable), + + ~"Eq" => expand!(eq::expand_deriving_eq), + ~"TotalEq" => expand!(totaleq::expand_deriving_totaleq), + ~"Ord" => expand!(ord::expand_deriving_ord), + ~"TotalOrd" => expand!(totalord::expand_deriving_totalord), + tname => { cx.span_err(titem.span, fmt!("unknown \ `deriving` trait: `%s`", tname)); @@ -118,14 +109,14 @@ pub fn expand_deriving(cx: @ext_ctxt, for in_items.each |item| { result.push(copy *item); match item.node { - item_struct(struct_def, ref generics) => { + ast::item_struct(struct_def, ref generics) => { result.push(expand_deriving_struct_def(cx, span, struct_def, item.ident, generics)); } - item_enum(ref enum_definition, ref generics) => { + ast::item_enum(ref enum_definition, ref generics) => { result.push(expand_deriving_enum_def(cx, span, enum_definition, @@ -138,7 +129,7 @@ pub fn expand_deriving(cx: @ext_ctxt, result } -fn create_impl_item(cx: @ext_ctxt, span: span, item: item_) -> @item { +fn create_impl_item(cx: @ext_ctxt, span: span, item: ast::item_) -> @item { let doc_attr = respan(span, ast::lit_str(@~"Automatically derived.")); let doc_attr = respan(span, ast::meta_name_value(@~"doc", doc_attr)); @@ -154,7 +145,7 @@ fn create_impl_item(cx: @ext_ctxt, span: span, item: item_) -> @item { attrs: ~[doc_attr], id: cx.next_id(), node: item, - vis: public, + vis: ast::public, span: span, } } @@ -173,22 +164,29 @@ pub fn create_self_type_with_params(cx: @ext_ctxt, self_ty_params.push(self_ty_param); } + let lifetime = if generics.lifetimes.is_empty() { + None + } else { + Some(@*generics.lifetimes.get(0)) + }; + + // Create the type of `self`. let self_type = build::mk_raw_path_(span, ~[ type_ident ], + lifetime, self_ty_params); - let self_type = ty_path(self_type, cx.next_id()); - @ast::Ty { id: cx.next_id(), node: self_type, span: span } + build::mk_ty_path_path(cx, span, self_type) } pub fn create_derived_impl(cx: @ext_ctxt, span: span, type_ident: ident, generics: &Generics, - methods: &[@method], + methods: &[@ast::method], trait_path: @ast::Path, - mut impl_ty_params: opt_vec::OptVec, - bounds_paths: opt_vec::OptVec<~[ident]>) + mut impl_generics: Generics, + bounds_paths: opt_vec::OptVec<@ast::Path>) -> @item { /*! * @@ -204,21 +202,22 @@ pub fn create_derived_impl(cx: @ext_ctxt, */ // Copy the lifetimes - let impl_lifetimes = generics.lifetimes.map(|l| { - build::mk_lifetime(cx, l.span, l.ident) - }); + for generics.lifetimes.each |l| { + impl_generics.lifetimes.push(copy *l) + }; // Create the type parameters. for generics.ty_params.each |ty_param| { + // extra restrictions on the generics parameters to the type being derived upon let mut bounds = do bounds_paths.map |&bound_path| { - build::mk_trait_ty_param_bound_global(cx, span, bound_path) + build::mk_trait_ty_param_bound_(cx, bound_path) }; let this_trait_bound = build::mk_trait_ty_param_bound_(cx, trait_path); bounds.push(this_trait_bound); - impl_ty_params.push(build::mk_ty_param(cx, ty_param.ident, @bounds)); + impl_generics.ty_params.push(build::mk_ty_param(cx, ty_param.ident, @bounds)); } // Create the reference to the trait. @@ -231,8 +230,7 @@ pub fn create_derived_impl(cx: @ext_ctxt, generics); // Create the impl item. - let impl_item = item_impl(Generics {lifetimes: impl_lifetimes, - ty_params: impl_ty_params}, + let impl_item = ast::item_impl(impl_generics, Some(trait_ref), self_type, methods.map(|x| *x)); @@ -240,106 +238,128 @@ pub fn create_derived_impl(cx: @ext_ctxt, } pub fn create_subpatterns(cx: @ext_ctxt, - span: span, - prefix: ~str, - n: uint) - -> ~[@pat] { - let mut subpats = ~[]; - for uint::range(0, n) |_i| { - // Create the subidentifier. - let index = subpats.len(); - let ident = cx.ident_of(fmt!("%s_%u", prefix, index)); - - // Create the subpattern. - let subpath = build::mk_raw_path(span, ~[ ident ]); - let subpat = pat_ident(bind_by_ref(m_imm), subpath, None); - let subpat = build::mk_pat(cx, span, subpat); - subpats.push(subpat); + span: span, + field_paths: ~[@ast::Path], + mutbl: ast::mutability) + -> ~[@ast::pat] { + do field_paths.map |&path| { + build::mk_pat(cx, span, + ast::pat_ident(ast::bind_by_ref(mutbl), path, None)) } - return subpats; } -pub fn is_struct_tuple(struct_def: &struct_def) -> bool { - struct_def.fields.len() > 0 && struct_def.fields.all(|f| { - match f.node.kind { - named_field(*) => false, - unnamed_field => true - } - }) +#[deriving(Eq)] // dogfooding! +enum StructType { + Unknown, Record, Tuple +} + +pub fn create_struct_pattern(cx: @ext_ctxt, + span: span, + struct_ident: ident, + struct_def: &struct_def, + prefix: ~str, + mutbl: ast::mutability) + -> (@ast::pat, ~[(Option, @expr)]) { + if struct_def.fields.is_empty() { + return ( + build::mk_pat_ident_with_binding_mode( + cx, span, struct_ident, ast::bind_infer), + ~[]); + } + + let matching_path = build::mk_raw_path(span, ~[ struct_ident ]); + + let mut paths = ~[], ident_expr = ~[]; + + let mut struct_type = Unknown; + + for struct_def.fields.eachi |i, struct_field| { + let opt_id = match struct_field.node.kind { + ast::named_field(ident, _, _) if (struct_type == Unknown || + struct_type == Record) => { + struct_type = Record; + Some(ident) + } + ast::unnamed_field if (struct_type == Unknown || + struct_type == Tuple) => { + struct_type = Tuple; + None + } + _ => { + cx.span_bug(span, "A struct with named and unnamed fields in `deriving`"); + } + }; + let path = build::mk_raw_path(span, + ~[ cx.ident_of(fmt!("%s_%u", prefix, i)) ]); + paths.push(path); + ident_expr.push((opt_id, build::mk_path_raw(cx, span, path))); + } + + let subpats = create_subpatterns(cx, span, 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 = do vec::build |push| { + for vec::each2(subpats, ident_expr) |&pat, &(id, _)| { + // id is guaranteed to be Some + push(ast::field_pat { ident: id.get(), pat: pat }) + } + }; + build::mk_pat_struct(cx, span, matching_path, field_pats) + } else { + build::mk_pat_enum(cx, span, matching_path, subpats) + }; + + (pattern, ident_expr) } pub fn create_enum_variant_pattern(cx: @ext_ctxt, - span: span, - variant: &variant, - prefix: ~str) - -> @pat { + span: span, + variant: &ast::variant, + prefix: ~str, + mutbl: ast::mutability) + -> (@ast::pat, ~[(Option, @expr)]) { + let variant_ident = variant.node.name; match variant.node.kind { - tuple_variant_kind(ref variant_args) => { - if variant_args.len() == 0 { - return build::mk_pat_ident_with_binding_mode( - cx, span, variant_ident, ast::bind_infer); + ast::tuple_variant_kind(ref variant_args) => { + if variant_args.is_empty() { + return (build::mk_pat_ident_with_binding_mode( + cx, span, variant_ident, ast::bind_infer), ~[]); } let matching_path = build::mk_raw_path(span, ~[ variant_ident ]); - let subpats = create_subpatterns(cx, - span, - prefix, - variant_args.len()); - return build::mk_pat_enum(cx, span, matching_path, subpats); - } - struct_variant_kind(struct_def) => { - let matching_path = build::mk_raw_path(span, ~[ variant_ident ]); - let subpats = create_subpatterns(cx, - span, - prefix, - struct_def.fields.len()); + let mut paths = ~[], ident_expr = ~[]; + for uint::range(0, variant_args.len()) |i| { + let path = build::mk_raw_path(span, + ~[ cx.ident_of(fmt!("%s_%u", prefix, i)) ]); - let field_pats = do struct_def.fields.mapi |i, struct_field| { - let ident = match struct_field.node.kind { - named_field(ident, _, _) => ident, - unnamed_field => { - cx.span_bug(span, ~"unexpected unnamed field"); - } - }; - ast::field_pat { ident: ident, pat: subpats[i] } - }; - - build::mk_pat_struct(cx, span, matching_path, field_pats) - } - } -} - -pub fn variant_arg_count(_cx: @ext_ctxt, _span: span, variant: &variant) -> uint { - match variant.node.kind { - tuple_variant_kind(ref args) => args.len(), - struct_variant_kind(ref struct_def) => struct_def.fields.len(), - } -} - -/// Iterate through the idents of the variant arguments. The field is -/// unnamed (i.e. it's not a struct-like enum), then `None`. -pub fn each_variant_arg_ident(_cx: @ext_ctxt, _span: span, - variant: &variant, it: &fn(uint, Option) -> bool) { - match variant.node.kind { - tuple_variant_kind(ref args) => { - for uint::range(0, args.len()) |i| { - if !it(i, None) { break } + paths.push(path); + ident_expr.push((None, build::mk_path_raw(cx, span, path))); } + + let subpats = create_subpatterns(cx, span, paths, mutbl); + + (build::mk_pat_enum(cx, span, matching_path, subpats), + ident_expr) } - struct_variant_kind(ref struct_def) => { - for struct_def.fields.eachi |i, f| { - let id = match f.node.kind { - named_field(ident, _, _) => Some(ident), - unnamed_field => None - }; - if !it(i, id) { break } - } + ast::struct_variant_kind(struct_def) => { + create_struct_pattern(cx, span, + variant_ident, struct_def, + prefix, + mutbl) } } } +pub fn variant_arg_count(_cx: @ext_ctxt, _span: span, variant: &ast::variant) -> uint { + match variant.node.kind { + ast::tuple_variant_kind(ref args) => args.len(), + ast::struct_variant_kind(ref struct_def) => struct_def.fields.len(), + } +} pub fn expand_enum_or_struct_match(cx: @ext_ctxt, span: span, @@ -347,7 +367,7 @@ pub fn expand_enum_or_struct_match(cx: @ext_ctxt, -> @expr { let self_ident = cx.ident_of(~"self"); let self_expr = build::mk_path(cx, span, ~[ self_ident ]); - let self_expr = build::mk_unary(cx, span, deref, self_expr); - let self_match_expr = expr_match(self_expr, arms); + let self_expr = build::mk_unary(cx, span, ast::deref, self_expr); + let self_match_expr = ast::expr_match(self_expr, arms); build::mk_expr(cx, span, self_match_expr) } diff --git a/src/libsyntax/ext/deriving/ty.rs b/src/libsyntax/ext/deriving/ty.rs new file mode 100644 index 00000000000..6195a3a6424 --- /dev/null +++ b/src/libsyntax/ext/deriving/ty.rs @@ -0,0 +1,242 @@ +// 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 or the MIT license +// , 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. +*/ + +use ast; +use ast::{expr,Generics,ident}; +use ext::base::ext_ctxt; +use ext::build; +use codemap::{span,respan}; +use opt_vec; + +/// The types of pointers +#[deriving(Eq)] +pub enum PtrTy { + Owned, // ~ + Managed(ast::mutability), // @[mut] + Borrowed(Option<~str>, ast::mutability), // &['lifetime] [mut] +} + +/// A path, e.g. `::core::option::Option::` (global). Has support +/// for type parameters and a lifetime. +#[deriving(Eq)] +pub struct Path { + path: ~[~str], + lifetime: Option<~str>, + params: ~[~Ty], + global: bool +} + +pub impl Path { + fn new(path: ~[~str]) -> Path { + Path::new_(path, None, ~[], true) + } + fn new_local(path: ~str) -> Path { + Path::new_(~[ path ], None, ~[], false) + } + fn new_(path: ~[~str], lifetime: Option<~str>, params: ~[~Ty], global: bool) -> Path { + Path { + path: path, + lifetime: lifetime, + params: params, + global: global + } + } + + fn to_ty(&self, cx: @ext_ctxt, span: span, + self_ty: ident, self_generics: &Generics) -> @ast::Ty { + build::mk_ty_path_path(cx, span, + self.to_path(cx, span, + self_ty, self_generics)) + } + fn to_path(&self, cx: @ext_ctxt, span: span, + self_ty: ident, self_generics: &Generics) -> @ast::Path { + let idents = self.path.map(|s| cx.ident_of(*s) ); + let lt = mk_lifetime(cx, span, self.lifetime); + let tys = self.params.map(|t| t.to_ty(cx, span, self_ty, self_generics)); + + if self.global { + build::mk_raw_path_global_(span, idents, lt, tys) + } else { + build::mk_raw_path_(span, idents, lt, tys) + } + } +} + +/// A type. Supports pointers (except for *), Self, and literals +#[deriving(Eq)] +pub enum Ty { + Self, + // &/~/@ Ty + Ptr(~Ty, PtrTy), + // mod::mod::Type<[lifetime], [Params...]>, including a plain type + // parameter, and things like `int` + Literal(Path), + // includes nil + Tuple(~[Ty]) +} + +pub fn borrowed_ptrty() -> PtrTy { + Borrowed(None, ast::m_imm) +} +pub fn borrowed(ty: ~Ty) -> Ty { + Ptr(ty, borrowed_ptrty()) +} + +pub fn borrowed_explicit_self() -> Option> { + Some(Some(borrowed_ptrty())) +} + +pub fn borrowed_self() -> Ty { + borrowed(~Self) +} + +pub fn nil_ty() -> Ty { + Tuple(~[]) +} + +fn mk_lifetime(cx: @ext_ctxt, span: span, lt: Option<~str>) -> Option<@ast::Lifetime> { + match lt { + Some(s) => Some(@build::mk_lifetime(cx, span, cx.ident_of(s))), + None => None + } +} + +pub impl Ty { + fn to_ty(&self, cx: @ext_ctxt, span: span, + self_ty: ident, self_generics: &Generics) -> @ast::Ty { + match *self { + Ptr(ref ty, ref ptr) => { + let raw_ty = ty.to_ty(cx, span, self_ty, self_generics); + match *ptr { + Owned => { + build::mk_ty_uniq(cx, span, raw_ty) + } + Managed(copy mutbl) => { + build::mk_ty_box(cx, span, raw_ty, mutbl) + } + Borrowed(copy lt, copy mutbl) => { + let lt = mk_lifetime(cx, span, lt); + build::mk_ty_rptr(cx, span, raw_ty, lt, mutbl) + } + } + } + Literal(ref p) => { p.to_ty(cx, span, self_ty, self_generics) } + Self => { + build::mk_ty_path_path(cx, span, self.to_path(cx, span, self_ty, self_generics)) + } + Tuple(ref fields) => { + let ty = if fields.is_empty() { + ast::ty_nil + } else { + ast::ty_tup(fields.map(|f| f.to_ty(cx, span, self_ty, self_generics))) + }; + + build::mk_ty(cx, span, ty) + } + } + } + + fn to_path(&self, cx: @ext_ctxt, span: span, + self_ty: ident, self_generics: &Generics) -> @ast::Path { + match *self { + Self => { + let self_params = do self_generics.ty_params.map |ty_param| { + build::mk_ty_path(cx, span, ~[ ty_param.ident ]) + }; + let lifetime = if self_generics.lifetimes.is_empty() { + None + } else { + Some(@*self_generics.lifetimes.get(0)) + }; + + build::mk_raw_path_(span, ~[self_ty], lifetime, + opt_vec::take_vec(self_params)) + } + Literal(ref p) => { + p.to_path(cx, span, self_ty, self_generics) + } + Ptr(*) => { cx.span_bug(span, ~"Pointer in a path in generic `deriving`") } + Tuple(*) => { cx.span_bug(span, ~"Tuple in a path in generic `deriving`") } + } + } +} + + +fn mk_ty_param(cx: @ext_ctxt, span: span, name: ~str, bounds: ~[Path], + self_ident: ident, self_generics: &Generics) -> ast::TyParam { + let bounds = opt_vec::from( + do bounds.map |b| { + let path = b.to_path(cx, span, self_ident, self_generics); + build::mk_trait_ty_param_bound_(cx, path) + }); + build::mk_ty_param(cx, cx.ident_of(name), @bounds) +} + +fn mk_generics(lifetimes: ~[ast::Lifetime], ty_params: ~[ast::TyParam]) -> Generics { + Generics { + lifetimes: opt_vec::from(lifetimes), + ty_params: opt_vec::from(ty_params) + } +} + +/// Lifetimes and bounds on type paramers +pub struct LifetimeBounds { + lifetimes: ~[~str], + bounds: ~[(~str, ~[Path])] +} + +pub impl LifetimeBounds { + fn empty() -> LifetimeBounds { + LifetimeBounds { + lifetimes: ~[], bounds: ~[] + } + } + fn to_generics(&self, cx: @ext_ctxt, span: span, + self_ty: ident, self_generics: &Generics) -> Generics { + let lifetimes = do self.lifetimes.map |<| { + build::mk_lifetime(cx, span, cx.ident_of(lt)) + }; + let ty_params = do self.bounds.map |&(name, bounds)| { + mk_ty_param(cx, span, name, bounds, self_ty, self_generics) + }; + mk_generics(lifetimes, ty_params) + } +} + + +pub fn get_explicit_self(cx: @ext_ctxt, span: span, self_ptr: Option) + -> (@expr, ast::self_ty) { + let self_path = build::mk_path(cx, span, ~[cx.ident_of(~"self")]); + match self_ptr { + None => { + (self_path, respan(span, ast::sty_value)) + } + Some(ptr) => { + let self_ty = respan( + span, + match ptr { + Owned => ast::sty_uniq(ast::m_imm), + Managed(mutbl) => ast::sty_box(mutbl), + Borrowed(lt, mutbl) => { + let lt = lt.map(|s| @build::mk_lifetime(cx, span, + cx.ident_of(*s))); + ast::sty_region(lt, mutbl) + } + }); + let self_expr = build::mk_deref(cx, span, self_path); + (self_expr, self_ty) + } + } +} From 1cf2108a2b09942d6b225700b9d39dc9fa070463 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Tue, 7 May 2013 01:30:00 +1000 Subject: [PATCH 2/5] libsyntax: convert #[deriving(IterBytes)] to generic deriving --- src/libsyntax/ext/deriving/iter_bytes.rs | 290 +++++------------------ 1 file changed, 64 insertions(+), 226 deletions(-) diff --git a/src/libsyntax/ext/deriving/iter_bytes.rs b/src/libsyntax/ext/deriving/iter_bytes.rs index f03306ea07a..c1c34c9a53a 100644 --- a/src/libsyntax/ext/deriving/iter_bytes.rs +++ b/src/libsyntax/ext/deriving/iter_bytes.rs @@ -1,4 +1,4 @@ -// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// 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. // @@ -8,25 +8,37 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use ast; -use ast::*; +use ast::{meta_item, item, expr}; +use codemap::span; use ext::base::ext_ctxt; use ext::build; -use ext::deriving::*; -use codemap::{span, spanned}; -use ast_util; -use opt_vec; +use ext::deriving::generic::*; pub fn expand_deriving_iter_bytes(cx: @ext_ctxt, span: span, - _mitem: @meta_item, - in_items: ~[@item]) - -> ~[@item] { - expand_deriving(cx, - span, - in_items, - expand_deriving_iter_bytes_struct_def, - expand_deriving_iter_bytes_enum_def) + mitem: @meta_item, + in_items: ~[@item]) -> ~[@item] { + let trait_def = TraitDef { + path: Path::new(~[~"core", ~"to_bytes", ~"IterBytes"]), + additional_bounds: ~[], + generics: LifetimeBounds::empty(), + methods: ~[ + MethodDef { + name: ~"iter_bytes", + generics: LifetimeBounds::empty(), + self_ty: borrowed_explicit_self(), + args: ~[ + Literal(Path::new(~[~"bool"])), + Literal(Path::new(~[~"core", ~"to_bytes", ~"Cb"])) + ], + ret_ty: nil_ty(), + const_nonmatching: false, + combine_substructure: iter_bytes_substructure + } + ] + }; + + expand_deriving_generic(cx, span, mitem, in_items, &trait_def) } pub fn expand_deriving_obsolete(cx: @ext_ctxt, @@ -39,217 +51,43 @@ pub fn expand_deriving_obsolete(cx: @ext_ctxt, in_items } -fn create_derived_iter_bytes_impl(cx: @ext_ctxt, - span: span, - type_ident: ident, - generics: &Generics, - method: @method) - -> @item { - let methods = [ method ]; - let trait_path = ~[ - cx.ident_of(~"core"), - cx.ident_of(~"to_bytes"), - cx.ident_of(~"IterBytes") - ]; - let trait_path = build::mk_raw_path_global(span, trait_path); - create_derived_impl(cx, span, type_ident, generics, methods, trait_path, - opt_vec::Empty, opt_vec::Empty) -} - -// Creates a method from the given set of statements conforming to the -// signature of the `iter_bytes` method. -fn create_iter_bytes_method(cx: @ext_ctxt, - span: span, - statements: ~[@stmt]) - -> @method { - // Create the `lsb0` parameter. - let bool_ident = cx.ident_of(~"bool"); - let lsb0_arg_type = build::mk_simple_ty_path(cx, span, bool_ident); - let lsb0_ident = cx.ident_of(~"__lsb0"); - let lsb0_arg = build::mk_arg(cx, span, lsb0_ident, lsb0_arg_type); - - // Create the `f` parameter. - let core_ident = cx.ident_of(~"core"); - let to_bytes_ident = cx.ident_of(~"to_bytes"); - let cb_ident = cx.ident_of(~"Cb"); - let core_to_bytes_cb_ident = ~[ core_ident, to_bytes_ident, cb_ident ]; - let f_arg_type = build::mk_ty_path(cx, span, core_to_bytes_cb_ident); - let f_ident = cx.ident_of(~"__f"); - let f_arg = build::mk_arg(cx, span, f_ident, f_arg_type); - - // Create the type of the return value. - let output_type = @ast::Ty { id: cx.next_id(), node: ty_nil, span: span }; - - // Create the function declaration. - let inputs = ~[ lsb0_arg, f_arg ]; - let fn_decl = build::mk_fn_decl(inputs, output_type); - - // Create the body block. - let body_block = build::mk_block_(cx, span, statements); - - // Create the method. - let self_ty = spanned { node: sty_region(None, m_imm), span: span }; - let method_ident = cx.ident_of(~"iter_bytes"); - @ast::method { - ident: method_ident, - attrs: ~[], - generics: ast_util::empty_generics(), - self_ty: self_ty, - purity: impure_fn, - decl: fn_decl, - body: body_block, - id: cx.next_id(), - span: span, - self_id: cx.next_id(), - vis: public - } -} - -fn call_substructure_iter_bytes_method(cx: @ext_ctxt, - span: span, - self_field: @expr) - -> @stmt { - // Gather up the parameters we want to chain along. - let lsb0_ident = cx.ident_of(~"__lsb0"); - let f_ident = cx.ident_of(~"__f"); - let lsb0_expr = build::mk_path(cx, span, ~[ lsb0_ident ]); - let f_expr = build::mk_path(cx, span, ~[ f_ident ]); - - // Call the substructure method. - let iter_bytes_ident = cx.ident_of(~"iter_bytes"); - let self_call = build::mk_method_call(cx, - span, - self_field, - iter_bytes_ident, - ~[ lsb0_expr, f_expr ]); - - // Create a statement out of this expression. - build::mk_stmt(cx, span, self_call) -} - -fn expand_deriving_iter_bytes_struct_def(cx: @ext_ctxt, - span: span, - struct_def: &struct_def, - type_ident: ident, - generics: &Generics) - -> @item { - // Create the method. - let method = expand_deriving_iter_bytes_struct_method(cx, - span, - struct_def); - - // Create the implementation. - return create_derived_iter_bytes_impl(cx, - span, - type_ident, - generics, - method); -} - -fn expand_deriving_iter_bytes_enum_def(cx: @ext_ctxt, - span: span, - enum_definition: &enum_def, - type_ident: ident, - generics: &Generics) - -> @item { - // Create the method. - let method = expand_deriving_iter_bytes_enum_method(cx, - span, - enum_definition); - - // Create the implementation. - return create_derived_iter_bytes_impl(cx, - span, - type_ident, - generics, - method); -} - -fn expand_deriving_iter_bytes_struct_method(cx: @ext_ctxt, - span: span, - struct_def: &struct_def) - -> @method { - let self_ident = cx.ident_of(~"self"); - - // Create the body of the method. - let mut statements = ~[]; - for struct_def.fields.each |struct_field| { - match struct_field.node.kind { - named_field(ident, _, _) => { - // Create the accessor for this field. - let self_field = build::mk_access(cx, - span, - ~[ self_ident ], - ident); - - // Call the substructure method. - let stmt = call_substructure_iter_bytes_method(cx, - span, - self_field); - statements.push(stmt); - } - unnamed_field => { - cx.span_unimpl(span, - ~"unnamed fields with `deriving(IterBytes)`"); - } - } - } - - // Create the method itself. - return create_iter_bytes_method(cx, span, statements); -} - -fn expand_deriving_iter_bytes_enum_method(cx: @ext_ctxt, - span: span, - enum_definition: &enum_def) - -> @method { - // Create the arms of the match in the method body. - let arms = do enum_definition.variants.mapi |i, variant| { - // Create the matching pattern. - let pat = create_enum_variant_pattern(cx, span, variant, ~"__self"); - - // Determine the discriminant. We will feed this value to the byte - // iteration function. - let discriminant; - match variant.node.disr_expr { - Some(copy disr_expr) => discriminant = disr_expr, - None => discriminant = build::mk_uint(cx, span, i), - } - - // Feed the discriminant to the byte iteration function. - let mut stmts = ~[]; - let discrim_stmt = call_substructure_iter_bytes_method(cx, - span, - discriminant); - stmts.push(discrim_stmt); - - // Feed each argument in this variant to the byte iteration function - // as well. - for uint::range(0, variant_arg_count(cx, span, variant)) |j| { - // Create the expression for this field. - let field_ident = cx.ident_of(~"__self_" + j.to_str()); - let field = build::mk_path(cx, span, ~[ field_ident ]); - - // Call the substructure method. - let stmt = call_substructure_iter_bytes_method(cx, span, field); - stmts.push(stmt); - } - - // Create the pattern body. - let match_body_block = build::mk_block_(cx, span, stmts); - - // Create the arm. - ast::arm { - pats: ~[ pat ], - guard: None, - body: match_body_block, - } +fn iter_bytes_substructure(cx: @ext_ctxt, span: span, substr: &Substructure) -> @expr { + let lsb0_f = match substr.nonself_args { + [l, f] => ~[l, f], + _ => cx.span_bug(span, "Incorrect number of arguments in `deriving(IterBytes)`") }; + let iter_bytes_ident = substr.method_ident; + let call_iterbytes = |thing_expr| { + build::mk_stmt( + cx, span, + build::mk_method_call(cx, span, + thing_expr, iter_bytes_ident, + copy lsb0_f)) + }; + let mut stmts = ~[]; + let fields; + match *substr.fields { + Struct(ref fs) => { + fields = fs + } + EnumMatching(copy index, ref variant, ref fs) => { + // Determine the discriminant. We will feed this value to the byte + // iteration function. + let discriminant = match variant.node.disr_expr { + Some(copy d)=> d, + None => build::mk_uint(cx, span, index) + }; - // Create the method body. - let self_match_expr = expand_enum_or_struct_match(cx, span, arms); - let self_match_stmt = build::mk_stmt(cx, span, self_match_expr); + stmts.push(call_iterbytes(discriminant)); - // Create the method. - create_iter_bytes_method(cx, span, ~[ self_match_stmt ]) -} + fields = fs; + } + _ => cx.span_bug(span, "Impossible substructure in `deriving(IterBytes)`") + } + + for fields.each |&(_, field, _)| { + stmts.push(call_iterbytes(field)); + } + + build::mk_block(cx, span, ~[], stmts, None) +} \ No newline at end of file From 5e1d6c2c8011f466d2a524231e5386df0b5ed841 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Tue, 7 May 2013 01:30:42 +1000 Subject: [PATCH 3/5] libsyntax: add #[deriving(Rand, ToStr)]. The former fills each field of a struct or enum variant with a random value (and picks a random enum variant). The latter makes the .to_str method have the same output as fmt!("%?", ..). --- src/libsyntax/ext/deriving/mod.rs | 6 ++ src/libsyntax/ext/deriving/rand.rs | 136 +++++++++++++++++++++++++++ src/libsyntax/ext/deriving/to_str.rs | 54 +++++++++++ 3 files changed, 196 insertions(+) create mode 100644 src/libsyntax/ext/deriving/rand.rs create mode 100644 src/libsyntax/ext/deriving/to_str.rs diff --git a/src/libsyntax/ext/deriving/mod.rs b/src/libsyntax/ext/deriving/mod.rs index 9f3972e71ac..d48ff98be06 100644 --- a/src/libsyntax/ext/deriving/mod.rs +++ b/src/libsyntax/ext/deriving/mod.rs @@ -23,6 +23,8 @@ pub mod clone; pub mod iter_bytes; pub mod encodable; pub mod decodable; +pub mod rand; +pub mod to_str; #[path="cmp/eq.rs"] pub mod eq; @@ -86,6 +88,10 @@ pub fn expand_meta_deriving(cx: @ext_ctxt, ~"Ord" => expand!(ord::expand_deriving_ord), ~"TotalOrd" => expand!(totalord::expand_deriving_totalord), + ~"Rand" => expand!(rand::expand_deriving_rand), + + ~"ToStr" => expand!(to_str::expand_deriving_to_str), + tname => { cx.span_err(titem.span, fmt!("unknown \ `deriving` trait: `%s`", tname)); diff --git a/src/libsyntax/ext/deriving/rand.rs b/src/libsyntax/ext/deriving/rand.rs new file mode 100644 index 00000000000..03202801d20 --- /dev/null +++ b/src/libsyntax/ext/deriving/rand.rs @@ -0,0 +1,136 @@ +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use ast; +use ast::{meta_item, item, expr, ident}; +use codemap::span; +use ext::base::ext_ctxt; +use ext::build; +use ext::deriving::generic::*; + +pub fn expand_deriving_rand(cx: @ext_ctxt, + span: span, + mitem: @meta_item, + in_items: ~[@item]) + -> ~[@item] { + let trait_def = TraitDef { + path: Path::new(~[~"core", ~"rand", ~"Rand"]), + additional_bounds: ~[], + generics: LifetimeBounds::empty(), + methods: ~[ + MethodDef { + name: ~"rand", + generics: LifetimeBounds { + lifetimes: ~[], + bounds: ~[(~"R", + ~[ Path::new(~[~"core", ~"rand", ~"Rng"]) ])] + }, + self_ty: None, + args: ~[ + Ptr(~Literal(Path::new_local(~"R")), + Borrowed(None, ast::m_imm)) + ], + ret_ty: Self, + const_nonmatching: false, + combine_substructure: rand_substructure + } + ] + }; + + expand_deriving_generic(cx, span, mitem, in_items, &trait_def) +} + +fn rand_substructure(cx: @ext_ctxt, span: span, substr: &Substructure) -> @expr { + let rng = match substr.nonself_args { + [rng] => ~[ rng ], + _ => cx.bug("Incorrect number of arguments to `rand` in `deriving(Rand)`") + }; + let rand_ident = ~[ + cx.ident_of(~"core"), + cx.ident_of(~"rand"), + cx.ident_of(~"Rand"), + cx.ident_of(~"rand") + ]; + let rand_call = || { + build::mk_call_global(cx, span, + copy rand_ident, copy rng) + }; + + return match *substr.fields { + StaticStruct(_, ref summary) => { + rand_thing(cx, span, substr.type_ident, summary, rand_call) + } + StaticEnum(_, ref variants) => { + if variants.is_empty() { + cx.span_fatal(span, "`Rand` cannot be derived for enums with no variants"); + } + + let variant_count = build::mk_uint(cx, span, variants.len()); + + // need to specify the uint-ness of the random number + let u32_ty = build::mk_ty_path(cx, span, ~[cx.ident_of(~"uint")]); + let r_ty = build::mk_ty_path(cx, span, ~[cx.ident_of(~"R")]); + let rand_name = build::mk_raw_path_(span, copy rand_ident, None, ~[ u32_ty, r_ty ]); + let rand_name = build::mk_path_raw(cx, span, rand_name); + + let rv_call = build::mk_call_(cx, span, rand_name, copy rng); + + // rand() % variants.len() + let rand_variant = build::mk_binary(cx, span, ast::rem, + rv_call, variant_count); + + let mut arms = do variants.mapi |i, id_sum| { + let i_expr = build::mk_uint(cx, span, i); + let pat = build::mk_pat_lit(cx, span, i_expr); + + match *id_sum { + (ident, ref summary) => { + build::mk_arm(cx, span, + ~[ pat ], + rand_thing(cx, span, ident, summary, rand_call)) + } + } + }; + + // _ => {} at the end. Should never occur + arms.push(build::mk_unreachable_arm(cx, span)); + + build::mk_expr(cx, span, + ast::expr_match(rand_variant, arms)) + } + _ => cx.bug("Non-static method in `deriving(Rand)`") + }; + + fn rand_thing(cx: @ext_ctxt, span: span, + ctor_ident: ident, + summary: &Either, + rand_call: &fn() -> @expr) -> @expr { + let ctor_ident = ~[ ctor_ident ]; + match *summary { + Left(copy count) => { + if count == 0 { + build::mk_path(cx, span, ctor_ident) + } else { + let exprs = vec::from_fn(count, |_| rand_call()); + build::mk_call(cx, span, ctor_ident, exprs) + } + } + Right(ref fields) => { + let rand_fields = do fields.map |ident| { + build::Field { + ident: *ident, + ex: rand_call() + } + }; + build::mk_struct_e(cx, span, ctor_ident, rand_fields) + } + } + } +} \ No newline at end of file diff --git a/src/libsyntax/ext/deriving/to_str.rs b/src/libsyntax/ext/deriving/to_str.rs new file mode 100644 index 00000000000..2c7d449585f --- /dev/null +++ b/src/libsyntax/ext/deriving/to_str.rs @@ -0,0 +1,54 @@ +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use ast::{meta_item, item, expr}; +use codemap::span; +use ext::base::ext_ctxt; +use ext::build; +use ext::deriving::generic::*; + +pub fn expand_deriving_to_str(cx: @ext_ctxt, + span: span, + mitem: @meta_item, + in_items: ~[@item]) + -> ~[@item] { + let trait_def = TraitDef { + path: Path::new(~[~"core", ~"to_str", ~"ToStr"]), + additional_bounds: ~[], + generics: LifetimeBounds::empty(), + methods: ~[ + MethodDef { + name: ~"to_str", + generics: LifetimeBounds::empty(), + self_ty: borrowed_explicit_self(), + args: ~[], + ret_ty: Ptr(~Literal(Path::new_local(~"str")), Owned), + const_nonmatching: false, + combine_substructure: to_str_substructure + } + ] + }; + + expand_deriving_generic(cx, span, mitem, in_items, &trait_def) +} + +fn to_str_substructure(cx: @ext_ctxt, span: span, substr: &Substructure) -> @expr { + match substr.self_args { + [self_obj] => { + let self_addr = build::mk_addr_of(cx, span, self_obj); + build::mk_call_global(cx, span, + ~[cx.ident_of(~"core"), + cx.ident_of(~"sys"), + cx.ident_of(~"log_str")], + ~[self_addr]) + } + _ => cx.span_bug(span, ~"Invalid number of arguments in `deriving(ToStr)`") + } +} \ No newline at end of file From 4a43c1babbdf453fc4af25cf06aae7c96213d918 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Tue, 7 May 2013 01:32:34 +1000 Subject: [PATCH 4/5] testsuite: tests for deriving changes and additions --- src/test/run-pass/deriving-rand.rs | 38 ++++++++++++++++ ...deriving-self-lifetime-totalord-totaleq.rs | 32 ++++++++++++++ src/test/run-pass/deriving-self-lifetime.rs | 33 ++++++++++++++ src/test/run-pass/deriving-to-str.rs | 44 +++++++++++++++++++ 4 files changed, 147 insertions(+) create mode 100644 src/test/run-pass/deriving-rand.rs create mode 100644 src/test/run-pass/deriving-self-lifetime-totalord-totaleq.rs create mode 100644 src/test/run-pass/deriving-self-lifetime.rs create mode 100644 src/test/run-pass/deriving-to-str.rs diff --git a/src/test/run-pass/deriving-rand.rs b/src/test/run-pass/deriving-rand.rs new file mode 100644 index 00000000000..290dc02dcdb --- /dev/null +++ b/src/test/run-pass/deriving-rand.rs @@ -0,0 +1,38 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[deriving(Rand)] +struct A; + +#[deriving(Rand)] +struct B(int, int); + +#[deriving(Rand)] +struct C { + x: f64, + y: (u8, u8) +} + +#[deriving(Rand)] +enum D { + D0, + D1(uint), + D2 { x: (), y: () } +} + +fn main() { + // check there's no segfaults + for 20.times { + rand::random::(); + rand::random::(); + rand::random::(); + rand::random::(); + } +} \ No newline at end of file diff --git a/src/test/run-pass/deriving-self-lifetime-totalord-totaleq.rs b/src/test/run-pass/deriving-self-lifetime-totalord-totaleq.rs new file mode 100644 index 00000000000..b0b03d8419b --- /dev/null +++ b/src/test/run-pass/deriving-self-lifetime-totalord-totaleq.rs @@ -0,0 +1,32 @@ +// xfail-test FIXME #6257 + +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use core::cmp::{Less,Equal,Greater}; + +#[deriving(TotalEq,TotalOrd)] +struct A<'self> { + x: &'self int +} + +fn main() { + let a = A { x: &1 }, b = A { x: &2 }; + + assert!(a.equals(&a)); + assert!(b.equals(&b)); + + + assert_eq!(a.cmp(&a), Equal); + assert_eq!(b.cmp(&b), Equal); + + assert_eq!(a.cmp(&b), Less); + assert_eq!(b.cmp(&a), Greater); +} diff --git a/src/test/run-pass/deriving-self-lifetime.rs b/src/test/run-pass/deriving-self-lifetime.rs new file mode 100644 index 00000000000..549a9b398a2 --- /dev/null +++ b/src/test/run-pass/deriving-self-lifetime.rs @@ -0,0 +1,33 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[deriving(Eq,Ord)] +struct A<'self> { + x: &'self int +} + +fn main() { + let a = A { x: &1 }, b = A { x: &2 }; + + assert_eq!(a, a); + assert_eq!(b, b); + + + assert!(a < b); + assert!(b > a); + + assert!(a <= b); + assert!(a <= a); + assert!(b <= b); + + assert!(b >= a); + assert!(b >= b); + assert!(a >= a); +} diff --git a/src/test/run-pass/deriving-to-str.rs b/src/test/run-pass/deriving-to-str.rs new file mode 100644 index 00000000000..10f6bcbeff7 --- /dev/null +++ b/src/test/run-pass/deriving-to-str.rs @@ -0,0 +1,44 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[deriving(Rand,ToStr)] +struct A; + +#[deriving(Rand,ToStr)] +struct B(int, int); + +#[deriving(Rand,ToStr)] +struct C { + x: f64, + y: (u8, u8) +} + +#[deriving(Rand,ToStr)] +enum D { + D0, + D1(uint), + D2 { x: (), y: () } +} + +fn main() { + macro_rules! t( + ($ty:ty) => {{ + let x =rand::random::<$ty>(); + assert_eq!(x.to_str(), fmt!("%?", x)); + }} + ); + + for 20.times { + t!(A); + t!(B); + t!(C); + t!(D); + } +} \ No newline at end of file From 08c80544662f53867551e689e47f8c4f363199cd Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Tue, 7 May 2013 22:46:27 +1000 Subject: [PATCH 5/5] testsuite: xfail tests for windows --- src/test/run-pass/deriving-rand.rs | 1 + src/test/run-pass/deriving-to-str.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/test/run-pass/deriving-rand.rs b/src/test/run-pass/deriving-rand.rs index 290dc02dcdb..dd4664e7446 100644 --- a/src/test/run-pass/deriving-rand.rs +++ b/src/test/run-pass/deriving-rand.rs @@ -1,3 +1,4 @@ +// xfail-fast #6330 // 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. diff --git a/src/test/run-pass/deriving-to-str.rs b/src/test/run-pass/deriving-to-str.rs index 10f6bcbeff7..4b98f9a73c5 100644 --- a/src/test/run-pass/deriving-to-str.rs +++ b/src/test/run-pass/deriving-to-str.rs @@ -1,3 +1,4 @@ +// xfail-fast #6330 // 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.