From 3698ea7e54c46cfde377f95782710ee5e19876d3 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Thu, 28 Mar 2013 21:50:10 +1100 Subject: [PATCH 1/7] libsyntax: abstract most of the deriving boilerplate into a simpler(r) interface. Pulls out many of the common patterns from the Eq and Clone deriving code (and invents a few of its own), so that deriving instances are very easy to write for a certain class of traits. (Basically, those which don't have parameters and where all methods only take arguments of type `&Self` and return either `Self` or types with no parameters.) --- src/libsyntax/ext/deriving/clone.rs | 7 +- src/libsyntax/ext/deriving/decodable.rs | 3 +- src/libsyntax/ext/deriving/encodable.rs | 5 +- src/libsyntax/ext/deriving/eq.rs | 10 +- src/libsyntax/ext/deriving/generic.rs | 826 +++++++++++++++++++++++ src/libsyntax/ext/deriving/iter_bytes.rs | 5 +- src/libsyntax/ext/deriving/mod.rs | 101 ++- 7 files changed, 915 insertions(+), 42 deletions(-) create mode 100644 src/libsyntax/ext/deriving/generic.rs diff --git a/src/libsyntax/ext/deriving/clone.rs b/src/libsyntax/ext/deriving/clone.rs index c8ba6b990e47a..390b72da3314b 100644 --- a/src/libsyntax/ext/deriving/clone.rs +++ b/src/libsyntax/ext/deriving/clone.rs @@ -55,7 +55,8 @@ fn create_derived_clone_impl(cx: @ext_ctxt, cx.ident_of(~"Clone"), ]; 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) + create_derived_impl(cx, span, type_ident, generics, methods, trait_path, + opt_vec::Empty, opt_vec::Empty) } // Creates a method from the given expression conforming to the signature of // the `clone` method. @@ -219,7 +220,7 @@ fn expand_deriving_clone_tuple_struct_method(cx: @ext_ctxt, let mut subcalls = ~[]; for uint::range(0, struct_def.fields.len()) |i| { // Create the expression for this field. - let field_ident = cx.ident_of(~"__self" + i.to_str()); + let field_ident = cx.ident_of(~"__self_" + i.to_str()); let field = build::mk_path(cx, span, ~[ field_ident ]); // Call the substructure method. @@ -262,7 +263,7 @@ fn expand_deriving_clone_enum_method(cx: @ext_ctxt, let mut subcalls = ~[]; 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_ident = cx.ident_of(~"__self_" + j.to_str()); let field = build::mk_path(cx, span, ~[ field_ident ]); // Call the substructure method. diff --git a/src/libsyntax/ext/deriving/decodable.rs b/src/libsyntax/ext/deriving/decodable.rs index 11f492316e281..df3536a3caef7 100644 --- a/src/libsyntax/ext/deriving/decodable.rs +++ b/src/libsyntax/ext/deriving/decodable.rs @@ -81,7 +81,8 @@ fn create_derived_decodable_impl( generics, methods, trait_path, - generic_ty_params + generic_ty_params, + opt_vec::Empty ) } diff --git a/src/libsyntax/ext/deriving/encodable.rs b/src/libsyntax/ext/deriving/encodable.rs index 81bfb03724f30..9776f484818cc 100644 --- a/src/libsyntax/ext/deriving/encodable.rs +++ b/src/libsyntax/ext/deriving/encodable.rs @@ -81,7 +81,8 @@ fn create_derived_encodable_impl( generics, methods, trait_path, - generic_ty_params + generic_ty_params, + opt_vec::Empty ) } @@ -306,7 +307,7 @@ fn expand_deriving_encodable_enum_method( 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_ident = cx.ident_of(~"__self_" + j.to_str()); let field = build::mk_path(cx, span, ~[ field_ident ]); // Call the substructure method. diff --git a/src/libsyntax/ext/deriving/eq.rs b/src/libsyntax/ext/deriving/eq.rs index c427a206c2e32..0afb667c69ab9 100644 --- a/src/libsyntax/ext/deriving/eq.rs +++ b/src/libsyntax/ext/deriving/eq.rs @@ -131,7 +131,7 @@ fn create_derived_eq_impl(cx: @ext_ctxt, cx.ident_of(~"Eq") ]; 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) + create_derived_impl(cx, span, type_ident, generics, methods, trait_path, opt_vec::Empty, []) } fn call_substructure_eq_method(cx: @ext_ctxt, @@ -338,13 +338,13 @@ fn expand_deriving_eq_enum_method(cx: @ext_ctxt, let mut matching_body_expr = None; for uint::range(0, variant_arg_count(cx, span, self_variant)) |i| { // Create the expression for the other field. - let other_field_ident = cx.ident_of(~"__other" + i.to_str()); + let other_field_ident = cx.ident_of(~"__other_" + i.to_str()); let other_field = build::mk_path(cx, span, ~[ other_field_ident ]); // Create the expression for this field. - let self_field_ident = cx.ident_of(~"__self" + i.to_str()); + let self_field_ident = cx.ident_of(~"__self_" + i.to_str()); let self_field = build::mk_path(cx, span, ~[ self_field_ident ]); // Call the substructure method. @@ -456,10 +456,10 @@ fn expand_deriving_eq_struct_tuple_method(cx: @ext_ctxt, // Create comparison expression, comparing each of the fields let mut match_body = None; for fields.eachi |i, _| { - let other_field_ident = cx.ident_of(other_str + i.to_str()); + let other_field_ident = cx.ident_of(fmt!("%s_%u", other_str, i)); let other_field = build::mk_path(cx, span, ~[ other_field_ident ]); - let self_field_ident = cx.ident_of(self_str + i.to_str()); + let self_field_ident = cx.ident_of(fmt!("%s_%u", self_str, i)); let self_field = build::mk_path(cx, span, ~[ self_field_ident ]); call_substructure_eq_method(cx, span, self_field, other_field, diff --git a/src/libsyntax/ext/deriving/generic.rs b/src/libsyntax/ext/deriving/generic.rs new file mode 100644 index 0000000000000..23a075ef001f6 --- /dev/null +++ b/src/libsyntax/ext/deriving/generic.rs @@ -0,0 +1,826 @@ +// 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. + +/*! + +Some code that abstracts away much of the boilerplate of writing +`deriving` instances for traits. Among other things it manages getting +access to the fields of the 4 different sorts of structs and enum +variants, as well as creating the method and impl ast instances. + +Supported features (fairly exhaustive): +- Methods taking any number of parameters of 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 + (e.g. `Option`), the parameters are automatically given the + current trait as a bound. +- 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.) + +The most important thing for implementers is the `Substructure` and +`SubstructureFields` objects. The latter groups 3 possibilities of the +arguments: + +- `Struct`, when `Self` is a struct (including tuple structs, e.g + `struct T(int, char)`). +- `EnumMatching`, when `Self` is an enum and all the arguments are the + same variant of the enum (e.g. `Some(1)`, `Some(3)` and `Some(4)`) +- `EnumNonMatching` when `Self` is an enum and the arguments are not + the same variant (e.g. `None`, `Some(1)` and `None`) + +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. + +All of the 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 B(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. + +# 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 + } + } + +Some examples of the values of `SubstructureFields` follow, using the +above `Eq`, `A`, `B` and `C`. + +## Structs + +When generating the `expr` for the `A` impl, the `SubstructureFields` is + + Struct(~[(Some(), + , + ~[ + ~[])]) + +## Enums + +When generating the `expr` for a call with `self == C0(a)` and `other +== C0(b)`, the SubstructureFields is + + EnumMatching(0, , + ~[None, + , + ~[]]) + +For `C1 {x}` and `C1 {x}`, + + EnumMatching(1, , + ~[Some(), + , + ~[]]) + +For `C0(a)` and `C1 {x}` , + + EnumNonMatching(~[(0, , + ~[(None, )]), + (1, , + ~[(Some(), + )])]) + +(and vice verse, but with the order of the outermost list flipped.) + +*/ + +use core::prelude::*; + +use ast; + +use ast::{ + and, binop, deref, enum_def, expr, expr_match, ident, impure_fn, + item, Generics, m_imm, meta_item, method, named_field, or, 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 fn expand_deriving_generic(cx: @ext_ctxt, + span: span, + _mitem: @meta_item, + in_items: ~[@item], + trait_def: &TraitDef) -> ~[@item] { + let expand_enum: ExpandDerivingEnumDefFn = + |cx, span, enum_def, type_ident, generics| { + trait_def.expand_enum_def(cx, span, enum_def, type_ident, generics) + }; + let expand_struct: ExpandDerivingStructDefFn = + |cx, span, struct_def, type_ident, generics| { + trait_def.expand_struct_def(cx, span, struct_def, type_ident, generics) + }; + + expand_deriving(cx, span, in_items, + expand_struct, + expand_enum) +} + +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]], + 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, + + combine_substructure: CombineSubstructureFunc<'self> +} + +/// All the data about the data structure/method being derived upon. +pub struct Substructure<'self> { + type_ident: ident, + method_ident: ident, + fields: &'self SubstructureFields +} + +/// A summary of the possible sets of fields. See above for details +/// and examples +pub enum SubstructureFields { + /** + Vec of `(field ident, self, [others])` where the field ident is + the ident of the current field (`None` for all fields in tuple + structs) + */ + Struct(~[(Option, @expr, ~[@expr])]), + + /** + Matching variants of the enum: variant index, ast::variant, + 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])]), + + /** + 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)])]) +} + + +/** +Combine the values of all the fields together. The last argument is +all the fields of all the structures, see above for details. +*/ +pub type CombineSubstructureFunc<'self> = + &'self fn(@ext_ctxt, span, &Substructure) -> @expr; + +/** +Deal with non-matching enum variants, the argument is a list +representing each variant: (variant index, ast::variant instance, +[variant fields]) +*/ +pub type EnumNonMatchFunc<'self> = + &'self fn(@ext_ctxt, span, ~[(uint, variant, ~[(Option, @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) }); + + let additional_bounds = opt_vec::from( + do self.additional_bounds.map |v| { + do v.map |&s| { cx.ident_of(s) } + }); + create_derived_impl(cx, span, + type_ident, generics, + methods, trait_path, + opt_vec::Empty, + additional_bounds) + } + + fn expand_struct_def(&self, cx: @ext_ctxt, + span: span, + struct_def: &struct_def, + type_ident: ident, + generics: &Generics) + -> @item { + let is_tuple = is_struct_tuple(struct_def); + + 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) + } else { + method_def.expand_struct_method_body(cx, span, + struct_def, + type_ident) + }; + + method_def.create_method(cx, span, type_ident, generics, body) + }; + + self.create_derived_impl(cx, span, type_ident, generics, methods) + } + + fn expand_enum_def(&self, + cx: @ext_ctxt, span: span, + enum_def: &enum_def, + type_ident: ident, + generics: &Generics) -> @item { + let methods = do self.methods.map |method_def| { + let body = method_def.expand_enum_method_body(cx, span, + enum_def, + type_ident); + + method_def.create_method(cx, span, type_ident, generics, body) + }; + + self.create_derived_impl(cx, span, type_ident, generics, methods) + } +} + +impl<'self> MethodDef<'self> { + fn call_substructure_method(&self, + cx: @ext_ctxt, + span: span, + type_ident: ident, + fields: &SubstructureFields) + -> @expr { + let substructure = Substructure { + type_ident: type_ident, + method_ident: cx.ident_of(self.name), + 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 ])); + } + }; + + build::mk_raw_path_(span, ~[ type_ident ], out_ty_params) + } + Some(str_path) => { + let p = do str_path.map |&s| { cx.ident_of(s) }; + build::mk_raw_path(span, p) + } + } + } + + 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, + }; + + // 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 method_ident = cx.ident_of(self.name); + let fn_decl = build::mk_fn_decl(args, output_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(), + 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 + } + } + + /** + ``` + #[deriving(Eq)] + struct A(int, int); + + // equivalent to: + + impl Eq for A { + fn eq(&self, __other_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) + } + } + } + } + } + } + ``` + */ + 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) + -> @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`"); + } + } + }; + + self.call_substructure_method(cx, span, type_ident, &Struct(fields)) + } + + /** + ``` + #[deriving(Eq)] + enum A { + A1 + A2(int) + } + + // is equivalent to + + impl Eq for A { + fn eq(&self, __other_1: &A) { + match *self { + A1 => match *__other_1 { + A1 => true, + A2(ref __other_1_1) => false + }, + A2(self_1) => match *__other_1 { + A1 => false, + A2(ref __other_1_1) => self_1.eq(__other_1_1) + } + } + } + } + ``` + */ + fn expand_enum_method_body(&self, + cx: @ext_ctxt, + span: span, + enum_def: &enum_def, + type_ident: ident) + -> @expr { + self.build_enum_match(cx, span, enum_def, type_ident, ~[]) + } + + + /** + Creates the nested matches for an enum definition, 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 + the tree are the same. Hopefully the optimisers get rid of any + repetition, otherwise derived methods with many Self arguments will be + exponentially large. + */ + fn build_enum_match(&self, + cx: @ext_ctxt, span: span, + enum_def: &enum_def, + type_ident: ident, + matches_so_far: ~[(uint, variant, + ~[(Option, @expr)])]) -> @expr { + if matches_so_far.len() == self.nargs + 1 { + // 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`"), + _ => { + // we currently have a vec of vecs, where each + // subvec is the fields of one of the arguments, + // but if the variants all match, we want this as + // vec of tuples, where each tuple represents a + // field. + + // `ref` inside let matches is buggy. Causes havoc wih rusc. + // let (variant_index, ref self_vec) = matches_so_far[0]; + let (variant_index, variant, self_vec) = match matches_so_far[0] { + (i, v, ref s) => (i, v, s) + }; + + let substructure; + + // most arms don't have matching variants, so do a + // quick check to see if they match (even though + // this means iterating twice) instead of being + // optimistic and doing a pile of allocations etc. + if matches_so_far.all(|&(v_i, _, _)| v_i == variant_index) { + let mut enum_matching_fields = vec::from_elem(self_vec.len(), ~[]); + + for matches_so_far.tail().each |&(_, _, other_fields)| { + for other_fields.eachi |i, &(_, other_field)| { + enum_matching_fields[i].push(other_field); + } + } + let field_tuples = + do vec::map2(*self_vec, + enum_matching_fields) |&(id, self_f), &other| { + (id, self_f, other) + }; + substructure = EnumMatching(variant_index, variant, field_tuples); + } else { + substructure = EnumNonMatching(matches_so_far); + } + self.call_substructure_method(cx, span, type_ident, &substructure) + } + } + + } else { // there are still matches to create + let (current_match_ident, current_match_str) = if matches_so_far.is_empty() { + (cx.ident_of(~"self"), ~"__self") + } else { + let s = fmt!("__other_%u", matches_so_far.len() - 1); + (cx.ident_of(s), s) + }; + + let mut arms = ~[]; + + // this is used as a stack + let mut matches_so_far = matches_so_far; + + // 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 ]))); + } + }; + + + matches_so_far.push((index, *variant, idents)); + let arm_expr = self.build_enum_match(cx, span, + enum_def, + type_ident, + matches_so_far); + matches_so_far.pop(); + + let arm_block = build::mk_simple_block(cx, span, arm_expr); + let arm = ast::arm { + pats: ~[ pattern ], + guard: None, + body: arm_block + }; + 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 + } + } +} + +/// 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)); + } + } +} +/// 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) + } +} + + + +/* helpful premade recipes */ + +/** +Fold the fields. `use_foldl` controls whether this is done +left-to-right (`true`) or right-to-left (`false`). +*/ +pub fn cs_fold(use_foldl: bool, + f: &fn(@ext_ctxt, span, + old: @expr, + self_f: @expr, other_fs: ~[@expr]) -> @expr, + base: @expr, + enum_nonmatch_f: EnumNonMatchFunc, + cx: @ext_ctxt, span: span, + substructure: &Substructure) -> @expr { + match *substructure.fields { + EnumMatching(_, _, all_fields) | Struct(all_fields) => { + if use_foldl { + do all_fields.foldl(base) |&old, &(_, self_f, other_fs)| { + f(cx, span, old, self_f, other_fs) + } + } else { + do all_fields.foldr(base) |&(_, self_f, other_fs), old| { + f(cx, span, old, self_f, other_fs) + } + } + }, + EnumNonMatching(all_enums) => enum_nonmatch_f(cx, span, all_enums) + } +} + + +/** +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)]) +``` +*/ +pub fn cs_same_method(f: &fn(@ext_ctxt, span, ~[@expr]) -> @expr, + enum_nonmatch_f: EnumNonMatchFunc, + cx: @ext_ctxt, span: span, + substructure: &Substructure) -> @expr { + match *substructure.fields { + EnumMatching(_, _, all_fields) | Struct(all_fields) => { + // call self_n.method(other_1_n, other_2_n, ...) + let called = do all_fields.map |&(_, self_field, other_fields)| { + build::mk_method_call(cx, span, + self_field, + substructure.method_ident, + other_fields) + }; + + f(cx, span, called) + }, + EnumNonMatching(all_enums) => enum_nonmatch_f(cx, span, all_enums) + } +} + +/** +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`). +*/ +pub fn cs_same_method_fold(use_foldl: bool, + f: &fn(@ext_ctxt, span, @expr, @expr) -> @expr, + base: @expr, + enum_nonmatch_f: EnumNonMatchFunc, + cx: @ext_ctxt, span: span, + substructure: &Substructure) -> @expr { + cs_same_method( + |cx, span, vals| { + if use_foldl { + do vals.foldl(base) |&old, &new| { + f(cx, span, old, new) + } + } else { + do vals.foldr(base) |&new, old| { + f(cx, span, old, new) + } + } + }, + enum_nonmatch_f, + cx, span, substructure) + +} + +/** +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, + enum_nonmatch_f: EnumNonMatchFunc, + cx: @ext_ctxt, span: span, + substructure: &Substructure) -> @expr { + cs_same_method_fold( + true, // foldl is good enough + |cx, span, old, new| { + build::mk_binary(cx, span, + binop, + old, new) + + }, + base, + enum_nonmatch_f, + cx, span, substructure) +} + +/// cs_binop with binop == or +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), + enum_nonmatch_f, + cx, span, substructure) +} +/// cs_binop with binop == and +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), + 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 4124e6ee6c165..75215b90eb0da 100644 --- a/src/libsyntax/ext/deriving/iter_bytes.rs +++ b/src/libsyntax/ext/deriving/iter_bytes.rs @@ -56,7 +56,8 @@ fn create_derived_iter_bytes_impl(cx: @ext_ctxt, 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) + 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 @@ -230,7 +231,7 @@ fn expand_deriving_iter_bytes_enum_method(cx: @ext_ctxt, // 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_ident = cx.ident_of(~"__self_" + j.to_str()); let field = build::mk_path(cx, span, ~[ field_ident ]); // Call the substructure method. diff --git a/src/libsyntax/ext/deriving/mod.rs b/src/libsyntax/ext/deriving/mod.rs index 63106eae48ae8..1241d4fa71139 100644 --- a/src/libsyntax/ext/deriving/mod.rs +++ b/src/libsyntax/ext/deriving/mod.rs @@ -24,30 +24,32 @@ use ast::{tuple_variant_kind}; use ast::{ty_path, unnamed_field, variant}; use ext::base::ext_ctxt; use ext::build; -use codemap::span; +use codemap::{span, respan}; use parse::token::special_idents::clownshoes_extensions; use opt_vec; use core::uint; -pub mod clone; pub mod eq; +pub mod clone; pub mod iter_bytes; pub mod encodable; pub mod decodable; -type ExpandDerivingStructDefFn<'self> = &'self fn(@ext_ctxt, - span, - x: &struct_def, - ident, - y: &Generics) - -> @item; -type ExpandDerivingEnumDefFn<'self> = &'self fn(@ext_ctxt, - span, - x: &enum_def, - ident, - y: &Generics) - -> @item; +pub mod generic; + +pub type ExpandDerivingStructDefFn<'self> = &'self fn(@ext_ctxt, + span, + x: &struct_def, + ident, + y: &Generics) + -> @item; +pub type ExpandDerivingEnumDefFn<'self> = &'self fn(@ext_ctxt, + span, + x: &enum_def, + ident, + y: &Generics) + -> @item; pub fn expand_meta_deriving(cx: @ext_ctxt, _span: span, @@ -72,10 +74,10 @@ pub fn expand_meta_deriving(cx: @ext_ctxt, meta_list(tname, _) | meta_word(tname) => { match *tname { + ~"Eq" => eq::expand_deriving_eq(cx, titem.span, + titem, in_items), ~"Clone" => clone::expand_deriving_clone(cx, titem.span, titem, in_items), - ~"Eq" => eq::expand_deriving_eq(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, @@ -126,9 +128,19 @@ pub fn expand_deriving(cx: @ext_ctxt, } fn create_impl_item(cx: @ext_ctxt, span: span, +item: item_) -> @item { + let doc_attr = respan(span, + ast::lit_str(@~"Automatically derived.")); + let doc_attr = respan(span, ast::meta_name_value(@~"doc", doc_attr)); + let doc_attr = ast::attribute_ { + style: ast::attr_outer, + value: @doc_attr, + is_sugared_doc: false + }; + let doc_attr = respan(span, doc_attr); + @ast::item { ident: clownshoes_extensions, - attrs: ~[], + attrs: ~[doc_attr], id: cx.next_id(), node: item, vis: public, @@ -164,14 +176,17 @@ pub fn create_derived_impl(cx: @ext_ctxt, generics: &Generics, methods: &[@method], trait_path: @ast::Path, - mut impl_ty_params: opt_vec::OptVec) + mut impl_ty_params: opt_vec::OptVec, + bounds_paths: opt_vec::OptVec<~[ident]>) -> @item { /*! * * Given that we are deriving a trait `Tr` for a type `T<'a, ..., * 'z, A, ..., Z>`, creates an impl like: * - * impl<'a, ..., 'z, A:Tr, ..., Z: Tr> Tr for T { ... } + * impl<'a, ..., 'z, A:Tr B1 B2, ..., Z: Tr B1 B2> Tr for T { ... } + * + * where B1, B2, ... are the bounds given by `bounds_paths`. * * FIXME(#5090): Remove code duplication between this and the * code in auto_encode.rs @@ -182,16 +197,21 @@ pub fn create_derived_impl(cx: @ext_ctxt, build::mk_lifetime(cx, l.span, l.ident) }); - // Create the reference to the trait. - let trait_ref = build::mk_trait_ref_(cx, trait_path); - // Create the type parameters. for generics.ty_params.each |ty_param| { - let bounds = @opt_vec::with( - build::mk_trait_ty_param_bound_(cx, trait_path) - ); - impl_ty_params.push(build::mk_ty_param(cx, ty_param.ident, bounds)); - }; + let mut bounds = do bounds_paths.map |&bound_path| { + build::mk_trait_ty_param_bound_global(cx, span, 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)); + } + + // Create the reference to the trait. + let trait_ref = build::mk_trait_ref_(cx, trait_path); // Create the type of `self`. let self_type = create_self_type_with_params(cx, @@ -216,8 +236,8 @@ pub fn create_subpatterns(cx: @ext_ctxt, let mut subpats = ~[]; for uint::range(0, n) |_i| { // Create the subidentifier. - let index = subpats.len().to_str(); - let ident = cx.ident_of(prefix + index); + 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 ]); @@ -287,6 +307,29 @@ pub fn variant_arg_count(_cx: @ext_ctxt, _span: span, variant: &variant) -> uint } } +/// 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 } + } + } + 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 } + } + } + } +} + + pub fn expand_enum_or_struct_match(cx: @ext_ctxt, span: span, arms: ~[ ast::arm ]) From 85b82c763bfbfd5de59f4c6b026dca58f3ba4687 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Sun, 31 Mar 2013 01:34:37 +1100 Subject: [PATCH 2/7] libcore: combine cmp::Ordering instances in lexical order. --- src/libcore/cmp.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/libcore/cmp.rs b/src/libcore/cmp.rs index f96575aaf414f..2c2b7f40f318f 100644 --- a/src/libcore/cmp.rs +++ b/src/libcore/cmp.rs @@ -116,6 +116,19 @@ totalord_impl!(i64) totalord_impl!(int) totalord_impl!(uint) +/** +Return `o1` if it is not `Equal`, otherwise `o2`. Simulates the +lexical ordering on a type `(int, int)`. +*/ +// used in deriving code in libsyntax +#[inline(always)] +pub fn lexical_ordering(o1: Ordering, o2: Ordering) -> Ordering { + match o1 { + Equal => o2, + _ => o1 + } +} + /** * Trait for values that can be compared for a sort-order. * @@ -184,6 +197,8 @@ pub fn max(v1: T, v2: T) -> T { #[cfg(test)] mod test { + use super::lexical_ordering; + #[test] fn test_int_totalord() { assert_eq!(5.cmp(&10), Less); @@ -204,4 +219,16 @@ mod test { assert!(Less < Equal); assert_eq!(Greater.cmp(&Less), Greater); } + + #[test] + fn test_lexical_ordering() { + fn t(o1: Ordering, o2: Ordering, e: Ordering) { + assert_eq!(lexical_ordering(o1, o2), e); + } + for [Less, Equal, Greater].each |&o| { + t(Less, o, Less); + t(Equal, o, o); + t(Greater, o, Greater); + } + } } From 7906c5572a8c4c5c0f6aa6e69bb63d64de50d697 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Sun, 31 Mar 2013 01:58:05 +1100 Subject: [PATCH 3/7] libsyntax: derive Clone, Eq, TotalEq, Ord, TotalOrd with the new generic deriving code. Closes #4269, #5588 and #5589. --- src/libsyntax/ext/deriving/clone.rs | 322 +++---------- src/libsyntax/ext/deriving/cmp/eq.rs | 65 +++ src/libsyntax/ext/deriving/cmp/ord.rs | 142 ++++++ src/libsyntax/ext/deriving/cmp/totaleq.rs | 45 ++ src/libsyntax/ext/deriving/cmp/totalord.rs | 77 ++++ src/libsyntax/ext/deriving/eq.rs | 500 --------------------- src/libsyntax/ext/deriving/mod.rs | 21 +- 7 files changed, 411 insertions(+), 761 deletions(-) create mode 100644 src/libsyntax/ext/deriving/cmp/eq.rs create mode 100644 src/libsyntax/ext/deriving/cmp/ord.rs create mode 100644 src/libsyntax/ext/deriving/cmp/totaleq.rs create mode 100644 src/libsyntax/ext/deriving/cmp/totalord.rs delete mode 100644 src/libsyntax/ext/deriving/eq.rs diff --git a/src/libsyntax/ext/deriving/clone.rs b/src/libsyntax/ext/deriving/clone.rs index 390b72da3314b..0c62566702d90 100644 --- a/src/libsyntax/ext/deriving/clone.rs +++ b/src/libsyntax/ext/deriving/clone.rs @@ -8,29 +8,35 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use core::prelude::*; - -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::*; +use core::option::{None,Some}; -use core::uint; pub fn expand_deriving_clone(cx: @ext_ctxt, span: span, - _: @meta_item, + mitem: @meta_item, in_items: ~[@item]) -> ~[@item] { - expand_deriving(cx, - span, - in_items, - expand_deriving_clone_struct_def, - expand_deriving_clone_enum_def) + let trait_def = TraitDef { + path: ~[~"core", ~"clone", ~"Clone"], + additional_bounds: ~[], + methods: ~[ + MethodDef { + name: ~"clone", + nargs: 0, + output_type: None, // return Self + combine_substructure: cs_clone + } + ] + }; + + expand_deriving_generic(cx, span, + mitem, in_items, + &trait_def) } pub fn expand_deriving_obsolete(cx: @ext_ctxt, @@ -42,252 +48,52 @@ pub fn expand_deriving_obsolete(cx: @ext_ctxt, in_items } -fn create_derived_clone_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(~"clone"), - cx.ident_of(~"Clone"), - ]; - 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 expression conforming to the signature of -// the `clone` method. -fn create_clone_method(cx: @ext_ctxt, - span: span, - +type_ident: ast::ident, - generics: &Generics, - expr: @ast::expr) - -> @method { - // Create the type parameters of the return value. - let mut output_ty_params = ~[]; - for generics.ty_params.each |ty_param| { - let path = build::mk_ty_path(cx, span, ~[ ty_param.ident ]); - output_ty_params.push(path); - } - - // Create the type of the return value. - let output_type_path = build::mk_raw_path_(span, - ~[ type_ident ], - output_ty_params); - let output_type = ast::ty_path(output_type_path, cx.next_id()); - let output_type = @ast::Ty { - id: cx.next_id(), - node: output_type, - span: span - }; - - // Create the function declaration. - let fn_decl = build::mk_fn_decl(~[], output_type); - - // Create the body block. - let body_block = build::mk_simple_block(cx, span, expr); - - // Create the self type and method identifier. - let self_ty = spanned { node: sty_region(None, m_imm), span: span }; - let method_ident = cx.ident_of(~"clone"); - - // Create the method. - @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 cs_clone(cx: @ext_ctxt, span: span, + substr: &Substructure) -> @expr { + let clone_ident = substr.method_ident; + let ctor_ident; + let all_fields; + let subcall = |field| + build::mk_method_call(cx, span, field, clone_ident, ~[]); + + match *substr.fields { + Struct(af) => { + ctor_ident = ~[ substr.type_ident ]; + all_fields = af; + } + EnumMatching(_, variant, af) => { + ctor_ident = ~[ variant.node.name ]; + all_fields = af; + }, + EnumNonMatching(*) => cx.bug("Non-matching enum variants in `deriving(Clone)`") } -} - -fn call_substructure_clone_method(cx: @ext_ctxt, - span: span, - self_field: @expr) - -> @expr { - // Call the substructure method. - let clone_ident = cx.ident_of(~"clone"); - build::mk_method_call(cx, span, - self_field, clone_ident, - ~[]) -} - -fn expand_deriving_clone_struct_def(cx: @ext_ctxt, - span: span, - struct_def: &struct_def, - type_ident: ident, - generics: &Generics) - -> @item { - // Create the method. - let method = if !is_struct_tuple(struct_def) { - expand_deriving_clone_struct_method(cx, - span, - struct_def, - type_ident, - generics) - } else { - expand_deriving_clone_tuple_struct_method(cx, - span, - struct_def, - type_ident, - generics) - }; - - // Create the implementation. - create_derived_clone_impl(cx, span, type_ident, generics, method) -} - -fn expand_deriving_clone_enum_def(cx: @ext_ctxt, - span: span, - enum_definition: &enum_def, - type_ident: ident, - generics: &Generics) - -> @item { - // Create the method. - let method = expand_deriving_clone_enum_method(cx, - span, - enum_definition, - type_ident, - generics); - - // Create the implementation. - create_derived_clone_impl(cx, span, type_ident, generics, method) -} - -fn expand_deriving_clone_struct_method(cx: @ext_ctxt, - span: span, - struct_def: &struct_def, - type_ident: ident, - generics: &Generics) - -> @method { - let self_ident = cx.ident_of(~"self"); - - // Create the new fields. - let mut fields = ~[]; - 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 call = call_substructure_clone_method(cx, - span, - self_field); - - let field = build::Field { ident: ident, ex: call }; - fields.push(field); - } - unnamed_field => { - cx.span_bug(span, ~"unnamed fields in `deriving(Clone)`"); + match all_fields { + [(None, _, _), .. _] => { + // enum-like + let subcalls = all_fields.map(|&(_, self_f, _)| subcall(self_f)); + build::mk_call(cx, span, ctor_ident, subcalls) + }, + _ => { + // struct-like + let fields = do all_fields.map |&(o_id, self_f, _)| { + let ident = match o_id { + Some(i) => i, + None => cx.span_bug(span, + ~"unnamed field in normal struct \ + in `deriving(Clone)`") + }; + build::Field { ident: ident, ex: subcall(self_f) } + }; + + if fields.is_empty() { + // no fields, so construct like `None` + build::mk_path(cx, span, ctor_ident) + } else { + build::mk_struct_e(cx, span, + ctor_ident, + fields) } } } - - // Create the struct literal. - let struct_literal = build::mk_struct_e(cx, - span, - ~[ type_ident ], - fields); - create_clone_method(cx, span, type_ident, generics, struct_literal) -} - -fn expand_deriving_clone_tuple_struct_method(cx: @ext_ctxt, - span: span, - struct_def: &struct_def, - type_ident: ident, - generics: &Generics) - -> @method { - // Create the pattern for the match. - let matching_path = build::mk_raw_path(span, ~[ type_ident ]); - let field_count = struct_def.fields.len(); - let subpats = create_subpatterns(cx, span, ~"__self", field_count); - let pat = build::mk_pat_enum(cx, span, matching_path, subpats); - - // Create the new fields. - let mut subcalls = ~[]; - for uint::range(0, struct_def.fields.len()) |i| { - // Create the expression for this field. - let field_ident = cx.ident_of(~"__self_" + i.to_str()); - let field = build::mk_path(cx, span, ~[ field_ident ]); - - // Call the substructure method. - let subcall = call_substructure_clone_method(cx, span, field); - subcalls.push(subcall); - } - - // Create the call to the struct constructor. - let call = build::mk_call(cx, span, ~[ type_ident ], subcalls); - - // Create the pattern body. - let match_body_block = build::mk_simple_block(cx, span, call); - - // Create the arm. - let arm = ast::arm { - pats: ~[ pat ], - guard: None, - body: match_body_block - }; - - // Create the method body. - let self_match_expr = expand_enum_or_struct_match(cx, span, ~[ arm ]); - - // Create the method. - create_clone_method(cx, span, type_ident, generics, self_match_expr) -} - -fn expand_deriving_clone_enum_method(cx: @ext_ctxt, - span: span, - enum_definition: &enum_def, - type_ident: ident, - generics: &Generics) - -> @method { - // Create the arms of the match in the method body. - let arms = do enum_definition.variants.map |variant| { - // Create the matching pattern. - let pat = create_enum_variant_pattern(cx, span, variant, ~"__self"); - - // Iterate over the variant arguments, creating the subcalls. - let mut subcalls = ~[]; - 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 subcall = call_substructure_clone_method(cx, span, field); - subcalls.push(subcall); - } - - // Create the call to the enum variant (if necessary). - let call = if subcalls.len() > 0 { - build::mk_call(cx, span, ~[ variant.node.name ], subcalls) - } else { - build::mk_path(cx, span, ~[ variant.node.name ]) - }; - - // Create the pattern body. - let match_body_block = build::mk_simple_block(cx, span, call); - - // Create the arm. - ast::arm { pats: ~[ pat ], guard: None, body: match_body_block } - }; - - // Create the method body. - let self_match_expr = expand_enum_or_struct_match(cx, span, arms); - - // Create the method. - create_clone_method(cx, span, type_ident, generics, self_match_expr) } diff --git a/src/libsyntax/ext/deriving/cmp/eq.rs b/src/libsyntax/ext/deriving/cmp/eq.rs new file mode 100644 index 0000000000000..142f0565e149e --- /dev/null +++ b/src/libsyntax/ext/deriving/cmp/eq.rs @@ -0,0 +1,65 @@ +// 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 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, + in_items: ~[@item]) -> ~[@item] { + // 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), + cx, span, substr) + } + fn cs_ne(cx: @ext_ctxt, span: span, substr: &Substructure) -> @expr { + cs_or(|cx, span, _| build::mk_bool(cx, span, true), + cx, span, substr) + } + + + let trait_def = TraitDef { + path: ~[~"core", ~"cmp", ~"Eq"], + additional_bounds: ~[], + methods: ~[ + MethodDef { + name: ~"ne", + output_type: Some(~[~"bool"]), + nargs: 1, + combine_substructure: cs_ne + }, + MethodDef { + name: ~"eq", + output_type: Some(~[~"bool"]), + nargs: 1, + combine_substructure: cs_eq + } + ] + }; + + expand_deriving_generic(cx, span, mitem, in_items, + &trait_def) +} + +pub fn expand_deriving_obsolete(cx: @ext_ctxt, + span: span, + _mitem: @meta_item, + in_items: ~[@item]) -> ~[@item] { + cx.span_err(span, ~"`#[deriving_eq]` is obsolete; use `#[deriving(Eq)]` instead"); + in_items +} diff --git a/src/libsyntax/ext/deriving/cmp/ord.rs b/src/libsyntax/ext/deriving/cmp/ord.rs new file mode 100644 index 0000000000000..7f7babab45cc4 --- /dev/null +++ b/src/libsyntax/ext/deriving/cmp/ord.rs @@ -0,0 +1,142 @@ +// 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 ast::{meta_item, item, expr_if, expr}; +use codemap::span; +use ext::base::ext_ctxt; +use ext::build; +use ext::deriving::generic::*; +use core::option::Some; + +macro_rules! mk_cso { + ($less:expr, $equal:expr) => { + |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] { + let trait_def = TraitDef { + path: ~[~"core", ~"cmp", ~"Ord"], + // XXX: Ord doesn't imply Eq yet + additional_bounds: ~[~[~"core", ~"cmp", ~"Eq"]], + methods: ~[ + MethodDef { + name: ~"lt", + output_type: Some(~[~"bool"]), + nargs: 1, + combine_substructure: mk_cso!(true, false) + }, + MethodDef { + name: ~"le", + output_type: Some(~[~"bool"]), + nargs: 1, + combine_substructure: mk_cso!(true, true) + }, + MethodDef { + name: ~"gt", + output_type: Some(~[~"bool"]), + nargs: 1, + combine_substructure: mk_cso!(false, false) + }, + MethodDef { + name: ~"ge", + output_type: Some(~[~"bool"]), + nargs: 1, + combine_substructure: mk_cso!(false, true) + }, + ] + }; + + expand_deriving_generic(cx, span, mitem, in_items, + &trait_def) +} + +/// `less`: is this `lt` or `le`? `equal`: is this `le` or `ge`? +fn cs_ord(less: bool, equal: bool, + cx: @ext_ctxt, span: span, + substr: &Substructure) -> @expr { + let binop = if less { + cx.ident_of(~"lt") + } else { + cx.ident_of(~"gt") + }; + let false_blk_expr = build::mk_block(cx, span, + ~[], ~[], + Some(build::mk_bool(cx, span, false))); + let true_blk = build::mk_simple_block(cx, span, + build::mk_bool(cx, span, true)); + let base = build::mk_bool(cx, span, equal); + + cs_fold( + false, // need foldr, + |cx, span, subexpr, self_f, other_fs| { + /* + + build up a series of nested ifs from the inside out to get + lexical ordering (hence foldr), i.e. + + ``` + if self.f1 `binop` other.f1 { + true + } else if self.f1 == other.f1 { + if self.f2 `binop` other.f2 { + true + } else if self.f2 == other.f2 { + `equal` + } else { + false + } + } else { + false + } + ``` + + The inner "`equal`" case is only reached if the two + items have all fields equal. + */ + if other_fs.len() != 1 { + cx.span_bug(span, "Not exactly 2 arguments in `deriving(Ord)`"); + } + + let cmp = build::mk_method_call(cx, span, + self_f, cx.ident_of(~"eq"), other_fs); + 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); + let if_ = expr_if(cmp, true_blk, Some(elseif)); + + build::mk_expr(cx, span, if_) + }, + base, + |cx, span, args| { + // nonmatching enums, order by the order the variants are + // written + match args { + [(self_var, _, _), + (other_var, _, _)] => + build::mk_bool(cx, span, + if less { + self_var < other_var + } else { + self_var > other_var + }), + _ => cx.span_bug(span, "Not exactly 2 arguments in `deriving(Ord)`") + } + }, + cx, span, substr) +} diff --git a/src/libsyntax/ext/deriving/cmp/totaleq.rs b/src/libsyntax/ext/deriving/cmp/totaleq.rs new file mode 100644 index 0000000000000..d71db22591dd6 --- /dev/null +++ b/src/libsyntax/ext/deriving/cmp/totaleq.rs @@ -0,0 +1,45 @@ +// 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 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_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), + cx, span, substr) + } + + let trait_def = TraitDef { + path: ~[~"core", ~"cmp", ~"TotalEq"], + additional_bounds: ~[], + methods: ~[ + MethodDef { + name: ~"equals", + output_type: Some(~[~"bool"]), + nargs: 1, + combine_substructure: cs_equals + } + ] + }; + + expand_deriving_generic(cx, span, mitem, in_items, + &trait_def) +} diff --git a/src/libsyntax/ext/deriving/cmp/totalord.rs b/src/libsyntax/ext/deriving/cmp/totalord.rs new file mode 100644 index 0000000000000..d82c63e9dd343 --- /dev/null +++ b/src/libsyntax/ext/deriving/cmp/totalord.rs @@ -0,0 +1,77 @@ +// 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 ast::{meta_item, item, expr}; +use codemap::span; +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"], + additional_bounds: ~[], + methods: ~[ + MethodDef { + name: ~"cmp", + output_type: Some(~[~"core", ~"cmp", ~"Ordering"]), + nargs: 1, + combine_substructure: cs_cmp + } + ] + }; + + expand_deriving_generic(cx, span, mitem, in_items, + &trait_def) +} + + +pub fn ordering_const(cx: @ext_ctxt, span: span, cnst: Ordering) -> @expr { + let cnst = match cnst { + Less => ~"Less", + Equal => ~"Equal", + Greater => ~"Greater" + }; + build::mk_path(cx, span, + ~[cx.ident_of(~"core"), + cx.ident_of(~"cmp"), + cx.ident_of(cnst)]) +} + +pub fn cs_cmp(cx: @ext_ctxt, span: span, + substr: &Substructure) -> @expr { + let lexical_ord = ~[cx.ident_of(~"core"), + cx.ident_of(~"cmp"), + cx.ident_of(~"lexical_ordering")]; + + cs_same_method_fold( + // foldr (possibly) nests the matches in lexical_ordering better + false, + |cx, span, old, new| { + build::mk_call(cx, span, lexical_ord, ~[old, new]) + }, + ordering_const(cx, span, Equal), + |cx, span, list| { + match list { + // an earlier nonmatching variant is Less than a + // later one + [(self_var, _, _), + (other_var, _, _)] => ordering_const(cx, span, + self_var.cmp(&other_var)), + _ => cx.span_bug(span, "Not exactly 2 arguments in `deriving(TotalOrd)`") + } + }, + cx, span, substr) +} diff --git a/src/libsyntax/ext/deriving/eq.rs b/src/libsyntax/ext/deriving/eq.rs deleted file mode 100644 index 0afb667c69ab9..0000000000000 --- a/src/libsyntax/ext/deriving/eq.rs +++ /dev/null @@ -1,500 +0,0 @@ -// 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 core::prelude::*; - -use ast; -use ast::*; -use ext::base::ext_ctxt; -use ext::build; -use ext::deriving::*; -use codemap::{span, spanned}; -use ast_util; -use opt_vec; - -use core::uint; - -enum Junction { - Conjunction, - Disjunction, -} - -pub impl Junction { - fn to_binop(self) -> binop { - match self { - Conjunction => and, - Disjunction => or, - } - } -} - -pub fn expand_deriving_eq(cx: @ext_ctxt, - span: span, - _mitem: @meta_item, - in_items: ~[@item]) - -> ~[@item] { - expand_deriving(cx, - span, - in_items, - expand_deriving_eq_struct_def, - expand_deriving_eq_enum_def) -} - -pub fn expand_deriving_obsolete(cx: @ext_ctxt, - span: span, - _mitem: @meta_item, - in_items: ~[@item]) - -> ~[@item] { - cx.span_err(span, ~"`#[deriving_eq]` is obsolete; use `#[deriving(Eq)]` instead"); - in_items -} - -/// Creates a method from the given expression, the signature of which -/// conforms to the `eq` or `ne` method. -fn create_eq_method(cx: @ext_ctxt, - span: span, - method_ident: ident, - type_ident: ident, - generics: &Generics, - body: @expr) - -> @method { - // Create the type of the `other` parameter. - 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, - }; - - // Create the `other` parameter. - let other_ident = cx.ident_of(~"__other"); - let arg = build::mk_arg(cx, span, other_ident, arg_type); - - // Create the type of the return value. - let bool_ident = cx.ident_of(~"bool"); - let output_type = build::mk_raw_path(span, ~[ bool_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, - }; - - // Create the function declaration. - let fn_decl = build::mk_fn_decl(~[ arg ], output_type); - - // Create the body block. - let body_block = build::mk_simple_block(cx, span, body); - - // Create the method. - let self_ty = spanned { node: sty_region(None, m_imm), span: span }; - @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 create_derived_eq_impl(cx: @ext_ctxt, - span: span, - type_ident: ident, - generics: &Generics, - eq_method: @method, - ne_method: @method) - -> @item { - let methods = [ eq_method, ne_method ]; - let trait_path = ~[ - cx.ident_of(~"core"), - cx.ident_of(~"cmp"), - cx.ident_of(~"Eq") - ]; - 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, []) -} - -fn call_substructure_eq_method(cx: @ext_ctxt, - span: span, - self_field: @expr, - other_field_ref: @expr, - method_ident: ident, - junction: Junction, - chain_expr: &mut Option<@expr>) { - // Call the substructure method. - let self_call = build::mk_method_call(cx, span, - self_field, method_ident, - ~[ other_field_ref ]); - - // Connect to the outer expression if necessary. - *chain_expr = match *chain_expr { - None => Some(self_call), - Some(copy old_outer_expr) => { - let binop = junction.to_binop(); - let chain_expr = build::mk_binary(cx, - span, - binop, - old_outer_expr, - self_call); - Some(chain_expr) - } - }; -} - -fn finish_eq_chain_expr(cx: @ext_ctxt, - span: span, - chain_expr: Option<@expr>, - junction: Junction) - -> @expr { - match chain_expr { - None => { - match junction { - Conjunction => build::mk_bool(cx, span, true), - Disjunction => build::mk_bool(cx, span, false), - } - } - Some(ref outer_expr) => *outer_expr, - } -} - -fn expand_deriving_eq_struct_def(cx: @ext_ctxt, - span: span, - struct_def: &struct_def, - type_ident: ident, - generics: &Generics) - -> @item { - // Create the methods. - let eq_ident = cx.ident_of(~"eq"); - let ne_ident = cx.ident_of(~"ne"); - - let derive_struct_fn = if is_struct_tuple(struct_def) { - expand_deriving_eq_struct_tuple_method - } else { - expand_deriving_eq_struct_method - }; - - let eq_method = derive_struct_fn(cx, - span, - struct_def, - eq_ident, - type_ident, - generics, - Conjunction); - let ne_method = derive_struct_fn(cx, - span, - struct_def, - ne_ident, - type_ident, - generics, - Disjunction); - - // Create the implementation. - return create_derived_eq_impl(cx, - span, - type_ident, - generics, - eq_method, - ne_method); -} - -fn expand_deriving_eq_enum_def(cx: @ext_ctxt, - span: span, - enum_definition: &enum_def, - type_ident: ident, - generics: &Generics) - -> @item { - // Create the methods. - let eq_ident = cx.ident_of(~"eq"); - let ne_ident = cx.ident_of(~"ne"); - let eq_method = expand_deriving_eq_enum_method(cx, - span, - enum_definition, - eq_ident, - type_ident, - generics, - Conjunction); - let ne_method = expand_deriving_eq_enum_method(cx, - span, - enum_definition, - ne_ident, - type_ident, - generics, - Disjunction); - - // Create the implementation. - return create_derived_eq_impl(cx, - span, - type_ident, - generics, - eq_method, - ne_method); -} - -fn expand_deriving_eq_struct_method(cx: @ext_ctxt, - span: span, - struct_def: &struct_def, - method_ident: ident, - type_ident: ident, - generics: &Generics, - junction: Junction) - -> @method { - let self_ident = cx.ident_of(~"self"); - let other_ident = cx.ident_of(~"__other"); - - // Create the body of the method. - let mut outer_expr = None; - for struct_def.fields.each |struct_field| { - match struct_field.node.kind { - named_field(ident, _, _) => { - // Create the accessor for the other field. - let other_field = build::mk_access(cx, - span, - ~[ other_ident ], - ident); - let other_field_ref = build::mk_addr_of(cx, - span, - other_field); - - // Create the accessor for this field. - let self_field = build::mk_access(cx, - span, - ~[ self_ident ], - ident); - - // Call the substructure method. - call_substructure_eq_method(cx, - span, - self_field, - other_field_ref, - method_ident, - junction, - &mut outer_expr); - } - unnamed_field => { - cx.span_unimpl(span, ~"unnamed fields with `deriving(Eq)`"); - } - } - } - - // Create the method itself. - let body = finish_eq_chain_expr(cx, span, outer_expr, junction); - return create_eq_method(cx, - span, - method_ident, - type_ident, - generics, - body); -} - -fn expand_deriving_eq_enum_method(cx: @ext_ctxt, - span: span, - enum_definition: &enum_def, - method_ident: ident, - type_ident: ident, - generics: &Generics, - junction: Junction) - -> @method { - let self_ident = cx.ident_of(~"self"); - let other_ident = cx.ident_of(~"__other"); - - let is_eq; - match junction { - Conjunction => is_eq = true, - Disjunction => is_eq = false, - } - - // Create the arms of the self match in the method body. - let mut self_arms = ~[]; - for enum_definition.variants.each |self_variant| { - let mut other_arms = ~[]; - - // Create the matching pattern. - let matching_pat = create_enum_variant_pattern(cx, - span, - self_variant, - ~"__other"); - - // Create the matching pattern body. - let mut matching_body_expr = None; - for uint::range(0, variant_arg_count(cx, span, self_variant)) |i| { - // Create the expression for the other field. - let other_field_ident = cx.ident_of(~"__other_" + i.to_str()); - let other_field = build::mk_path(cx, - span, - ~[ other_field_ident ]); - - // Create the expression for this field. - let self_field_ident = cx.ident_of(~"__self_" + i.to_str()); - let self_field = build::mk_path(cx, span, ~[ self_field_ident ]); - - // Call the substructure method. - call_substructure_eq_method(cx, - span, - self_field, - other_field, - method_ident, - junction, - &mut matching_body_expr); - } - - let matching_body_expr = finish_eq_chain_expr(cx, - span, - matching_body_expr, - junction); - let matching_body_block = build::mk_simple_block(cx, - span, - matching_body_expr); - - // Create the matching arm. - let matching_arm = ast::arm { - pats: ~[ matching_pat ], - guard: None, - body: matching_body_block - }; - other_arms.push(matching_arm); - - // Maybe generate a non-matching case. If there is only one - // variant then there will always be a match. - if enum_definition.variants.len() > 1 { - // Create the nonmatching pattern. - let nonmatching_pat = @ast::pat { - id: cx.next_id(), - node: pat_wild, - span: span - }; - - // Create the nonmatching pattern body. - let nonmatching_expr = build::mk_bool(cx, span, !is_eq); - let nonmatching_body_block = - build::mk_simple_block(cx, - span, - nonmatching_expr); - - // Create the nonmatching arm. - let nonmatching_arm = ast::arm { - pats: ~[ nonmatching_pat ], - guard: None, - body: nonmatching_body_block, - }; - other_arms.push(nonmatching_arm); - } - - // Create the self pattern. - let self_pat = create_enum_variant_pattern(cx, - span, - self_variant, - ~"__self"); - - // Create the self pattern body. - let other_expr = build::mk_path(cx, span, ~[ other_ident ]); - let other_expr = build::mk_unary(cx, span, deref, other_expr); - let other_match_expr = expr_match(other_expr, other_arms); - let other_match_expr = build::mk_expr(cx, - span, - other_match_expr); - let other_match_body_block = build::mk_simple_block(cx, - span, - other_match_expr); - - // Create the self arm. - let self_arm = ast::arm { - pats: ~[ self_pat ], - guard: None, - body: other_match_body_block, - }; - self_arms.push(self_arm); - } - - // Create the method body. - 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, self_arms); - let self_match_expr = build::mk_expr(cx, span, self_match_expr); - - // Create the method. - return create_eq_method(cx, - span, - method_ident, - type_ident, - generics, - self_match_expr); -} - -fn expand_deriving_eq_struct_tuple_method(cx: @ext_ctxt, - span: span, - struct_def: &struct_def, - method_ident: ident, - type_ident: ident, - generics: &Generics, - junction: Junction) - -> @method { - let self_str = ~"self"; - let other_str = ~"__other"; - let type_path = build::mk_raw_path(span, ~[type_ident]); - let fields = copy struct_def.fields; - - // Create comparison expression, comparing each of the fields - let mut match_body = None; - for fields.eachi |i, _| { - let other_field_ident = cx.ident_of(fmt!("%s_%u", other_str, i)); - let other_field = 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 ]); - - call_substructure_eq_method(cx, span, self_field, other_field, - method_ident, junction, &mut match_body); - } - let match_body = finish_eq_chain_expr(cx, span, match_body, junction); - - // Create arm for the '__other' match, containing the comparison expr - let other_subpats = create_subpatterns(cx, span, other_str, fields.len()); - let other_arm = ast::arm { - pats: ~[ build::mk_pat_enum(cx, span, type_path, other_subpats) ], - guard: None, - body: build::mk_simple_block(cx, span, match_body), - }; - - // Create the match on '__other' - let other_expr = build::mk_path(cx, span, ~[ cx.ident_of(other_str) ]); - let other_expr = build::mk_unary(cx, span, deref, other_expr); - let other_match_expr = expr_match(other_expr, ~[other_arm]); - let other_match_expr = build::mk_expr(cx, span, other_match_expr); - - // Create arm for the 'self' match, which contains the '__other' match - let self_subpats = create_subpatterns(cx, span, self_str, fields.len()); - let self_arm = ast::arm { - pats: ~[build::mk_pat_enum(cx, span, type_path, self_subpats)], - guard: None, - body: build::mk_simple_block(cx, span, other_match_expr), - }; - - // Create the match on 'self' - let self_expr = build::mk_path(cx, span, ~[ cx.ident_of(self_str) ]); - let self_expr = build::mk_unary(cx, span, deref, self_expr); - let self_match_expr = expr_match(self_expr, ~[self_arm]); - let self_match_expr = build::mk_expr(cx, span, self_match_expr); - - create_eq_method(cx, span, method_ident, - type_ident, generics, self_match_expr) -} diff --git a/src/libsyntax/ext/deriving/mod.rs b/src/libsyntax/ext/deriving/mod.rs index 1241d4fa71139..78faf5556b2ce 100644 --- a/src/libsyntax/ext/deriving/mod.rs +++ b/src/libsyntax/ext/deriving/mod.rs @@ -30,12 +30,21 @@ use opt_vec; use core::uint; -pub mod eq; pub mod clone; pub mod iter_bytes; pub mod encodable; pub mod decodable; +#[path="cmp/eq.rs"] +pub mod eq; +#[path="cmp/totaleq.rs"] +pub mod totaleq; +#[path="cmp/ord.rs"] +pub mod ord; +#[path="cmp/totalord.rs"] +pub mod totalord; + + pub mod generic; pub type ExpandDerivingStructDefFn<'self> = &'self fn(@ext_ctxt, @@ -74,8 +83,6 @@ pub fn expand_meta_deriving(cx: @ext_ctxt, meta_list(tname, _) | meta_word(tname) => { match *tname { - ~"Eq" => eq::expand_deriving_eq(cx, titem.span, - titem, in_items), ~"Clone" => clone::expand_deriving_clone(cx, titem.span, titem, in_items), ~"IterBytes" => iter_bytes::expand_deriving_iter_bytes(cx, @@ -84,6 +91,14 @@ pub fn expand_meta_deriving(cx: @ext_ctxt, 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), tname => { cx.span_err(titem.span, fmt!("unknown \ `deriving` trait: `%s`", tname)); From 99492796dcaac41966dc54f7ab4b8e33e641bb73 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Sun, 31 Mar 2013 02:01:44 +1100 Subject: [PATCH 4/7] testsuite: add tests for derived Eq, TotalEq, Ord, TotalOrd. --- src/test/run-pass/deriving-cmp.rs | 75 +++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 src/test/run-pass/deriving-cmp.rs diff --git a/src/test/run-pass/deriving-cmp.rs b/src/test/run-pass/deriving-cmp.rs new file mode 100644 index 0000000000000..56968fc12100f --- /dev/null +++ b/src/test/run-pass/deriving-cmp.rs @@ -0,0 +1,75 @@ +// 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, TotalEq, Ord, TotalOrd)] +struct S { + x: T, + y: T +} + +#[deriving(Eq, TotalEq, Ord, TotalOrd)] +struct TS(T,T); + +#[deriving(Eq, TotalEq, Ord, TotalOrd)] +enum E { + E0, + E1(T), + E2(T,T) +} + +#[deriving(Eq, TotalEq, Ord, TotalOrd)] +enum ES { + ES1 { x: T }, + ES2 { x: T, y: T } +} + + +pub fn main() { + let s1 = S {x: 1, y: 1}, s2 = S {x: 1, y: 2}; + let ts1 = TS(1, 1), ts2 = TS(1,2); + let e0 = E0, e11 = E1(1), e12 = E1(2), e21 = E2(1,1), e22 = E2(1, 2); + let es11 = ES1 {x: 1}, es12 = ES1 {x: 2}, es21 = ES2 {x: 1, y: 1}, es22 = ES2 {x: 1, y: 2}; + + test([s1, s2]); + test([ts1, ts2]); + test([e0, e11, e12, e21, e22]); + test([es11, es12, es21, es22]); +} + +fn test(ts: &[T]) { + // compare each element against all other elements. The list + // should be in sorted order, so that if i < j, then ts[i] < + // ts[j], etc. + for ts.eachi |i, t1| { + for ts.eachi |j, t2| { + let ord = i.cmp(&j); + + let eq = i == j; + let lt = i < j, le = i <= j; + let gt = i > j, ge = i >= j; + + // Eq + assert_eq!(*t1 == *t2, eq); + + // TotalEq + assert_eq!(t1.equals(t2), eq); + + // Ord + assert_eq!(*t1 < *t2, lt); + assert_eq!(*t1 > *t2, gt); + + assert_eq!(*t1 <= *t2, le); + assert_eq!(*t1 >= *t2, ge); + + // TotalOrd + assert_eq!(t1.cmp(t2), ord); + } + } +} \ No newline at end of file From bff374873136bacf8352e05f73cb3252761dc2d6 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Tue, 2 Apr 2013 22:02:46 +1100 Subject: [PATCH 5/7] libsyntax: short-circuit on non-matching variants in deriving code. Allow a deriving instance using the generic code to short-circuit for any non-matching enum variants (grouping them all into a _ match), reducing the number of arms required. Use this to speed up the Eq & TotalEq implementations. --- src/libsyntax/ext/deriving/clone.rs | 1 + src/libsyntax/ext/deriving/cmp/eq.rs | 26 +-- src/libsyntax/ext/deriving/cmp/ord.rs | 42 ++--- src/libsyntax/ext/deriving/cmp/totaleq.rs | 1 + src/libsyntax/ext/deriving/cmp/totalord.rs | 1 + src/libsyntax/ext/deriving/generic.rs | 161 +++++++++++++----- .../run-pass/deriving-cmp-generic-enum.rs | 50 ++++++ .../deriving-cmp-generic-struct-enum.rs | 52 ++++++ .../run-pass/deriving-cmp-generic-struct.rs | 49 ++++++ .../deriving-cmp-generic-tuple-struct.rs | 47 +++++ src/test/run-pass/deriving-cmp.rs | 75 -------- 11 files changed, 349 insertions(+), 156 deletions(-) create mode 100644 src/test/run-pass/deriving-cmp-generic-enum.rs create mode 100644 src/test/run-pass/deriving-cmp-generic-struct-enum.rs create mode 100644 src/test/run-pass/deriving-cmp-generic-struct.rs create mode 100644 src/test/run-pass/deriving-cmp-generic-tuple-struct.rs delete mode 100644 src/test/run-pass/deriving-cmp.rs diff --git a/src/libsyntax/ext/deriving/clone.rs b/src/libsyntax/ext/deriving/clone.rs index 0c62566702d90..d996bca60a367 100644 --- a/src/libsyntax/ext/deriving/clone.rs +++ b/src/libsyntax/ext/deriving/clone.rs @@ -29,6 +29,7 @@ pub fn expand_deriving_clone(cx: @ext_ctxt, name: ~"clone", nargs: 0, output_type: None, // return Self + const_nonmatching: false, combine_substructure: cs_clone } ] diff --git a/src/libsyntax/ext/deriving/cmp/eq.rs b/src/libsyntax/ext/deriving/cmp/eq.rs index 142f0565e149e..c0060cc67dc33 100644 --- a/src/libsyntax/ext/deriving/cmp/eq.rs +++ b/src/libsyntax/ext/deriving/cmp/eq.rs @@ -31,24 +31,24 @@ pub fn expand_deriving_eq(cx: @ext_ctxt, 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, + const_nonmatching: true, + combine_substructure: $f + }, + } + ) let trait_def = TraitDef { path: ~[~"core", ~"cmp", ~"Eq"], additional_bounds: ~[], methods: ~[ - MethodDef { - name: ~"ne", - output_type: Some(~[~"bool"]), - nargs: 1, - combine_substructure: cs_ne - }, - MethodDef { - name: ~"eq", - output_type: Some(~[~"bool"]), - nargs: 1, - combine_substructure: cs_eq - } + 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 7f7babab45cc4..398e27eb3e385 100644 --- a/src/libsyntax/ext/deriving/cmp/ord.rs +++ b/src/libsyntax/ext/deriving/cmp/ord.rs @@ -16,10 +16,16 @@ use ext::build; use ext::deriving::generic::*; use core::option::Some; -macro_rules! mk_cso { - ($less:expr, $equal:expr) => { - |cx, span, substr| - cs_ord($less, $equal, cx, span, substr) +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) + } } } @@ -32,30 +38,10 @@ pub fn expand_deriving_ord(cx: @ext_ctxt, // XXX: Ord doesn't imply Eq yet additional_bounds: ~[~[~"core", ~"cmp", ~"Eq"]], methods: ~[ - MethodDef { - name: ~"lt", - output_type: Some(~[~"bool"]), - nargs: 1, - combine_substructure: mk_cso!(true, false) - }, - MethodDef { - name: ~"le", - output_type: Some(~[~"bool"]), - nargs: 1, - combine_substructure: mk_cso!(true, true) - }, - MethodDef { - name: ~"gt", - output_type: Some(~[~"bool"]), - nargs: 1, - combine_substructure: mk_cso!(false, false) - }, - MethodDef { - name: ~"ge", - output_type: Some(~[~"bool"]), - nargs: 1, - combine_substructure: mk_cso!(false, true) - }, + md!(~"lt", true, false), + md!(~"le", true, true), + md!(~"gt", false, false), + md!(~"ge", false, true) ] }; diff --git a/src/libsyntax/ext/deriving/cmp/totaleq.rs b/src/libsyntax/ext/deriving/cmp/totaleq.rs index d71db22591dd6..fc8ec103a6021 100644 --- a/src/libsyntax/ext/deriving/cmp/totaleq.rs +++ b/src/libsyntax/ext/deriving/cmp/totaleq.rs @@ -35,6 +35,7 @@ pub fn expand_deriving_totaleq(cx: @ext_ctxt, name: ~"equals", output_type: Some(~[~"bool"]), nargs: 1, + 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 d82c63e9dd343..9c20a0be87c24 100644 --- a/src/libsyntax/ext/deriving/cmp/totalord.rs +++ b/src/libsyntax/ext/deriving/cmp/totalord.rs @@ -28,6 +28,7 @@ pub fn expand_deriving_totalord(cx: @ext_ctxt, name: ~"cmp", output_type: Some(~[~"core", ~"cmp", ~"Ordering"]), nargs: 1, + const_nonmatching: false, combine_substructure: cs_cmp } ] diff --git a/src/libsyntax/ext/deriving/generic.rs b/src/libsyntax/ext/deriving/generic.rs index 23a075ef001f6..8fe2ca1a1a107 100644 --- a/src/libsyntax/ext/deriving/generic.rs +++ b/src/libsyntax/ext/deriving/generic.rs @@ -40,7 +40,8 @@ arguments: - `EnumMatching`, when `Self` is an enum and all the arguments are the same variant of the enum (e.g. `Some(1)`, `Some(3)` and `Some(4)`) - `EnumNonMatching` when `Self` is an enum and the arguments are not - the same variant (e.g. `None`, `Some(1)` and `None`) + the same variant (e.g. `None`, `Some(1)` and `None`). If + `const_nonmatching` is true, this will contain an empty list. In the first two cases, the values from the corresponding fields in all the arguments are grouped together. In the `EnumNonMatching` case @@ -129,9 +130,11 @@ use core::prelude::*; use ast; use ast::{ + and, binop, deref, enum_def, expr, expr_match, ident, impure_fn, - item, Generics, m_imm, meta_item, method, named_field, or, public, - struct_def, sty_region, ty_rptr, ty_path, variant}; + 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; @@ -177,6 +180,10 @@ pub struct MethodDef<'self> { /// Number of arguments other than `self` (all of type `&Self`) nargs: uint, + /// if the value of the nonmatching enums is independent of the + /// actual enums, i.e. can use _ => .. match. + const_nonmatching: bool, + combine_substructure: CombineSubstructureFunc<'self> } @@ -555,12 +562,13 @@ impl<'self> MethodDef<'self> { enum_def: &enum_def, type_ident: ident) -> @expr { - self.build_enum_match(cx, span, enum_def, type_ident, ~[]) + self.build_enum_match(cx, span, enum_def, type_ident, + None, ~[], 0) } /** - Creates the nested matches for an enum definition, i.e. + Creates the nested matches for an enum definition recursively, i.e. ``` match self { @@ -575,14 +583,20 @@ impl<'self> MethodDef<'self> { the tree are the same. Hopefully the optimisers get rid of any repetition, otherwise derived methods with many Self arguments will be exponentially large. + + `matching` is Some(n) if all branches in the tree above the + current position are variant `n`, `None` otherwise (including on + the first call). */ fn build_enum_match(&self, cx: @ext_ctxt, span: span, enum_def: &enum_def, type_ident: ident, + matching: Option, matches_so_far: ~[(uint, variant, - ~[(Option, @expr)])]) -> @expr { - if matches_so_far.len() == self.nargs + 1 { + ~[(Option, @expr)])], + match_count: uint) -> @expr { + if match_count == self.nargs + 1 { // we've matched against all arguments, so make the final // expression at the bottom of the match tree match matches_so_far { @@ -594,41 +608,44 @@ impl<'self> MethodDef<'self> { // vec of tuples, where each tuple represents a // field. - // `ref` inside let matches is buggy. Causes havoc wih rusc. - // let (variant_index, ref self_vec) = matches_so_far[0]; - let (variant_index, variant, self_vec) = match matches_so_far[0] { - (i, v, ref s) => (i, v, s) - }; - let substructure; // most arms don't have matching variants, so do a // quick check to see if they match (even though // this means iterating twice) instead of being // optimistic and doing a pile of allocations etc. - if matches_so_far.all(|&(v_i, _, _)| v_i == variant_index) { - let mut enum_matching_fields = vec::from_elem(self_vec.len(), ~[]); - - for matches_so_far.tail().each |&(_, _, other_fields)| { - for other_fields.eachi |i, &(_, other_field)| { - enum_matching_fields[i].push(other_field); + match matching { + Some(variant_index) => { + // `ref` inside let matches is buggy. Causes havoc wih rusc. + // let (variant_index, ref self_vec) = matches_so_far[0]; + let (variant, self_vec) = match matches_so_far[0] { + (_, v, ref s) => (v, s) + }; + + let mut enum_matching_fields = vec::from_elem(self_vec.len(), ~[]); + + for matches_so_far.tail().each |&(_, _, other_fields)| { + for other_fields.eachi |i, &(_, other_field)| { + enum_matching_fields[i].push(other_field); + } } + let field_tuples = + do vec::map2(*self_vec, + enum_matching_fields) |&(id, self_f), &other| { + (id, self_f, other) + }; + substructure = EnumMatching(variant_index, variant, field_tuples); + } + None => { + substructure = EnumNonMatching(matches_so_far); } - let field_tuples = - do vec::map2(*self_vec, - enum_matching_fields) |&(id, self_f), &other| { - (id, self_f, other) - }; - substructure = EnumMatching(variant_index, variant, field_tuples); - } else { - substructure = EnumNonMatching(matches_so_far); } self.call_substructure_method(cx, span, type_ident, &substructure) } } } else { // there are still matches to create - let (current_match_ident, current_match_str) = if matches_so_far.is_empty() { + let (current_match_ident, current_match_str) = if match_count == 0 { (cx.ident_of(~"self"), ~"__self") } else { let s = fmt!("__other_%u", matches_so_far.len() - 1); @@ -640,8 +657,32 @@ impl<'self> MethodDef<'self> { // this is used as a stack let mut matches_so_far = matches_so_far; - // create an arm matching on each variant - for enum_def.variants.eachi |index, variant| { + 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`") + }; + + // matching-variant match + let variant = &enum_def.variants[index]; let pattern = create_enum_variant_pattern(cx, span, variant, current_match_str); @@ -653,23 +694,63 @@ impl<'self> MethodDef<'self> { } }; - matches_so_far.push((index, *variant, idents)); let arm_expr = self.build_enum_match(cx, span, enum_def, type_ident, - matches_so_far); + matching, + matches_so_far, + match_count + 1); matches_so_far.pop(); - - let arm_block = build::mk_simple_block(cx, span, arm_expr); - let arm = ast::arm { - pats: ~[ pattern ], - guard: None, - body: arm_block - }; + let arm = mk_arm!(pattern, arm_expr); arms.push(arm); - } + 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, + &EnumNonMatching(~[])); + let wild_arm = mk_arm!(wild_pat, 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 ]))); + } + }; + + matches_so_far.push((index, *variant, idents)); + let new_matching = + match matching { + _ if match_count == 0 => Some(index), + Some(i) if index == i => Some(i), + _ => None + }; + let arm_expr = self.build_enum_match(cx, span, + enum_def, + type_ident, + new_matching, + matches_so_far, + match_count + 1); + matches_so_far.pop(); + + let arm = mk_arm!(pattern, arm_expr); + arms.push(arm); + } + } let deref_expr = build::mk_unary(cx, span, deref, build::mk_path(cx, span, ~[ current_match_ident ])); diff --git a/src/test/run-pass/deriving-cmp-generic-enum.rs b/src/test/run-pass/deriving-cmp-generic-enum.rs new file mode 100644 index 0000000000000..a2651ddac3d19 --- /dev/null +++ b/src/test/run-pass/deriving-cmp-generic-enum.rs @@ -0,0 +1,50 @@ +// 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, TotalEq, Ord, TotalOrd)] +enum E { + E0, + E1(T), + E2(T,T) +} + +pub fn main() { + let e0 = E0, e11 = E1(1), e12 = E1(2), e21 = E2(1,1), e22 = E2(1, 2); + + // in order for both Ord and TotalOrd + let es = [e0, e11, e12, e21, e22]; + + for es.eachi |i, e1| { + for es.eachi |j, e2| { + let ord = i.cmp(&j); + + let eq = i == j; + let lt = i < j, le = i <= j; + let gt = i > j, ge = i >= j; + + // Eq + assert_eq!(*e1 == *e2, eq); + assert_eq!(*e1 != *e2, !eq); + + // TotalEq + assert_eq!(e1.equals(e2), eq); + + // Ord + assert_eq!(*e1 < *e2, lt); + assert_eq!(*e1 > *e2, gt); + + assert_eq!(*e1 <= *e2, le); + assert_eq!(*e1 >= *e2, ge); + + // TotalOrd + assert_eq!(e1.cmp(e2), ord); + } + } +} diff --git a/src/test/run-pass/deriving-cmp-generic-struct-enum.rs b/src/test/run-pass/deriving-cmp-generic-struct-enum.rs new file mode 100644 index 0000000000000..6f6e8d79d8b92 --- /dev/null +++ b/src/test/run-pass/deriving-cmp-generic-struct-enum.rs @@ -0,0 +1,52 @@ +// xfail-test #5530 + +// 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, TotalEq, Ord, TotalOrd)] +enum ES { + ES1 { x: T }, + ES2 { x: T, y: T } +} + + +pub fn main() { + let es11 = ES1 {x: 1}, es12 = ES1 {x: 2}, es21 = ES2 {x: 1, y: 1}, es22 = ES2 {x: 1, y: 2}; + + // in order for both Ord and TotalOrd + let ess = [es11, es12, es21, es22]; + + for ess.eachi |i, es1| { + for ess.eachi |j, es2| { + let ord = i.cmp(&j); + + let eq = i == j; + let lt = i < j, le = i <= j; + let gt = i > j, ge = i >= j; + + // Eq + assert_eq!(*es1 == *es2, eq); + assert_eq!(*es1 != *es2, !eq); + + // TotalEq + assert_eq!(es1.equals(es2), eq); + + // Ord + assert_eq!(*es1 < *es2, lt); + assert_eq!(*es1 > *es2, gt); + + assert_eq!(*es1 <= *es2, le); + assert_eq!(*es1 >= *es2, ge); + + // TotalOrd + assert_eq!(es1.cmp(es2), ord); + } + } +} \ No newline at end of file diff --git a/src/test/run-pass/deriving-cmp-generic-struct.rs b/src/test/run-pass/deriving-cmp-generic-struct.rs new file mode 100644 index 0000000000000..bd3e02ba29b30 --- /dev/null +++ b/src/test/run-pass/deriving-cmp-generic-struct.rs @@ -0,0 +1,49 @@ +// 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, TotalEq, Ord, TotalOrd)] +struct S { + x: T, + y: T +} + +pub fn main() { + let s1 = S {x: 1, y: 1}, s2 = S {x: 1, y: 2}; + + // in order for both Ord and TotalOrd + let ss = [s1, s2]; + + for ss.eachi |i, s1| { + for ss.eachi |j, s2| { + let ord = i.cmp(&j); + + let eq = i == j; + let lt = i < j, le = i <= j; + let gt = i > j, ge = i >= j; + + // Eq + assert_eq!(*s1 == *s2, eq); + assert_eq!(*s1 != *s2, !eq); + + // TotalEq + assert_eq!(s1.equals(s2), eq); + + // Ord + assert_eq!(*s1 < *s2, lt); + assert_eq!(*s1 > *s2, gt); + + assert_eq!(*s1 <= *s2, le); + assert_eq!(*s1 >= *s2, ge); + + // TotalOrd + assert_eq!(s1.cmp(s2), ord); + } + } +} \ No newline at end of file diff --git a/src/test/run-pass/deriving-cmp-generic-tuple-struct.rs b/src/test/run-pass/deriving-cmp-generic-tuple-struct.rs new file mode 100644 index 0000000000000..733b19a9ae2da --- /dev/null +++ b/src/test/run-pass/deriving-cmp-generic-tuple-struct.rs @@ -0,0 +1,47 @@ +// 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, TotalEq, Ord, TotalOrd)] +struct TS(T,T); + + +pub fn main() { + let ts1 = TS(1, 1), ts2 = TS(1,2); + + // in order for both Ord and TotalOrd + let tss = [ts1, ts2]; + + for tss.eachi |i, ts1| { + for tss.eachi |j, ts2| { + let ord = i.cmp(&j); + + let eq = i == j; + let lt = i < j, le = i <= j; + let gt = i > j, ge = i >= j; + + // Eq + assert_eq!(*ts1 == *ts2, eq); + assert_eq!(*ts1 != *ts2, !eq); + + // TotalEq + assert_eq!(ts1.equals(ts2), eq); + + // Ord + assert_eq!(*ts1 < *ts2, lt); + assert_eq!(*ts1 > *ts2, gt); + + assert_eq!(*ts1 <= *ts2, le); + assert_eq!(*ts1 >= *ts2, ge); + + // TotalOrd + assert_eq!(ts1.cmp(ts2), ord); + } + } +} \ No newline at end of file diff --git a/src/test/run-pass/deriving-cmp.rs b/src/test/run-pass/deriving-cmp.rs deleted file mode 100644 index 56968fc12100f..0000000000000 --- a/src/test/run-pass/deriving-cmp.rs +++ /dev/null @@ -1,75 +0,0 @@ -// 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, TotalEq, Ord, TotalOrd)] -struct S { - x: T, - y: T -} - -#[deriving(Eq, TotalEq, Ord, TotalOrd)] -struct TS(T,T); - -#[deriving(Eq, TotalEq, Ord, TotalOrd)] -enum E { - E0, - E1(T), - E2(T,T) -} - -#[deriving(Eq, TotalEq, Ord, TotalOrd)] -enum ES { - ES1 { x: T }, - ES2 { x: T, y: T } -} - - -pub fn main() { - let s1 = S {x: 1, y: 1}, s2 = S {x: 1, y: 2}; - let ts1 = TS(1, 1), ts2 = TS(1,2); - let e0 = E0, e11 = E1(1), e12 = E1(2), e21 = E2(1,1), e22 = E2(1, 2); - let es11 = ES1 {x: 1}, es12 = ES1 {x: 2}, es21 = ES2 {x: 1, y: 1}, es22 = ES2 {x: 1, y: 2}; - - test([s1, s2]); - test([ts1, ts2]); - test([e0, e11, e12, e21, e22]); - test([es11, es12, es21, es22]); -} - -fn test(ts: &[T]) { - // compare each element against all other elements. The list - // should be in sorted order, so that if i < j, then ts[i] < - // ts[j], etc. - for ts.eachi |i, t1| { - for ts.eachi |j, t2| { - let ord = i.cmp(&j); - - let eq = i == j; - let lt = i < j, le = i <= j; - let gt = i > j, ge = i >= j; - - // Eq - assert_eq!(*t1 == *t2, eq); - - // TotalEq - assert_eq!(t1.equals(t2), eq); - - // Ord - assert_eq!(*t1 < *t2, lt); - assert_eq!(*t1 > *t2, gt); - - assert_eq!(*t1 <= *t2, le); - assert_eq!(*t1 >= *t2, ge); - - // TotalOrd - assert_eq!(t1.cmp(t2), ord); - } - } -} \ No newline at end of file From ea9bdaaed9e4cb91cb8dfc96d0ce2dab10eae1d2 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Fri, 12 Apr 2013 19:00:53 +1000 Subject: [PATCH 6/7] libsyntax: (maybe) fix deriving(TotalOrd) on windows --- src/libsyntax/ext/deriving/cmp/totalord.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libsyntax/ext/deriving/cmp/totalord.rs b/src/libsyntax/ext/deriving/cmp/totalord.rs index 9c20a0be87c24..a098a7463d3e7 100644 --- a/src/libsyntax/ext/deriving/cmp/totalord.rs +++ b/src/libsyntax/ext/deriving/cmp/totalord.rs @@ -45,10 +45,10 @@ pub fn ordering_const(cx: @ext_ctxt, span: span, cnst: Ordering) -> @expr { Equal => ~"Equal", Greater => ~"Greater" }; - build::mk_path(cx, span, - ~[cx.ident_of(~"core"), - cx.ident_of(~"cmp"), - cx.ident_of(cnst)]) + build::mk_path_global(cx, span, + ~[cx.ident_of(~"core"), + cx.ident_of(~"cmp"), + cx.ident_of(cnst)]) } pub fn cs_cmp(cx: @ext_ctxt, span: span, @@ -61,7 +61,7 @@ pub fn cs_cmp(cx: @ext_ctxt, span: span, // foldr (possibly) nests the matches in lexical_ordering better false, |cx, span, old, new| { - build::mk_call(cx, span, lexical_ord, ~[old, new]) + build::mk_call_global(cx, span, lexical_ord, ~[old, new]) }, ordering_const(cx, span, Equal), |cx, span, list| { @@ -70,7 +70,7 @@ pub fn cs_cmp(cx: @ext_ctxt, span: span, // later one [(self_var, _, _), (other_var, _, _)] => ordering_const(cx, span, - self_var.cmp(&other_var)), + self_var.cmp(&other_var)), _ => cx.span_bug(span, "Not exactly 2 arguments in `deriving(TotalOrd)`") } }, From 5c376e53d0febb493bcb7de21564d6f60a6da514 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Fri, 12 Apr 2013 20:19:11 +1000 Subject: [PATCH 7/7] libsyntax: another fix for deriving on windows. --- src/libsyntax/ext/deriving/generic.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libsyntax/ext/deriving/generic.rs b/src/libsyntax/ext/deriving/generic.rs index 8fe2ca1a1a107..dfbb98fa23388 100644 --- a/src/libsyntax/ext/deriving/generic.rs +++ b/src/libsyntax/ext/deriving/generic.rs @@ -130,7 +130,6 @@ use core::prelude::*; use ast; 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, @@ -328,7 +327,7 @@ impl<'self> MethodDef<'self> { } Some(str_path) => { let p = do str_path.map |&s| { cx.ident_of(s) }; - build::mk_raw_path(span, p) + build::mk_raw_path_global(span, p) } } }