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/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 diff --git a/src/libsyntax/ext/deriving/mod.rs b/src/libsyntax/ext/deriving/mod.rs index 5aeeef2b17a..d48ff98be06 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}; @@ -30,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; @@ -78,23 +73,25 @@ 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), + + ~"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)); @@ -118,14 +115,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 +135,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 +151,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 +170,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 +208,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 +236,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 +244,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 +373,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/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 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) + } + } +} diff --git a/src/test/run-pass/deriving-rand.rs b/src/test/run-pass/deriving-rand.rs new file mode 100644 index 00000000000..dd4664e7446 --- /dev/null +++ b/src/test/run-pass/deriving-rand.rs @@ -0,0 +1,39 @@ +// 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. +// +// 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..4b98f9a73c5 --- /dev/null +++ b/src/test/run-pass/deriving-to-str.rs @@ -0,0 +1,45 @@ +// 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. +// +// 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