mirror of
https://github.com/rust-lang/rust.git
synced 2025-06-04 19:29:07 +00:00
827 lines
28 KiB
Rust
827 lines
28 KiB
Rust
![]() |
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
|
||
|
// file at the top-level directory of this distribution and at
|
||
|
// http://rust-lang.org/COPYRIGHT.
|
||
|
//
|
||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||
|
// option. This file may not be copied, modified, or distributed
|
||
|
// except according to those terms.
|
||
|
|
||
|
/*!
|
||
|
|
||
|
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<T>`), 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<ident>` in several places associated
|
||
|
with field `expr`s. This represents the name of the field it is
|
||
|
associated with. It is only not `None` when the associated field has
|
||
|
an identifier in the source code. For example, the `x`s in the
|
||
|
following snippet
|
||
|
|
||
|
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<ident>`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(<ident of x>),
|
||
|
<expr for self.x>,
|
||
|
~[<expr for other.x])])
|
||
|
|
||
|
For the `B` impl, called with `B(a)` and `B(b)`,
|
||
|
|
||
|
Struct(~[(None,
|
||
|
<expr for a>
|
||
|
~[<expr for b>])])
|
||
|
|
||
|
## Enums
|
||
|
|
||
|
When generating the `expr` for a call with `self == C0(a)` and `other
|
||
|
== C0(b)`, the SubstructureFields is
|
||
|
|
||
|
EnumMatching(0, <ast::variant for C0>,
|
||
|
~[None,
|
||
|
<expr for a>,
|
||
|
~[<expr for b>]])
|
||
|
|
||
|
For `C1 {x}` and `C1 {x}`,
|
||
|
|
||
|
EnumMatching(1, <ast::variant for C1>,
|
||
|
~[Some(<ident of x>),
|
||
|
<expr for self.x>,
|
||
|
~[<expr for other.x>]])
|
||
|
|
||
|
For `C0(a)` and `C1 {x}` ,
|
||
|
|
||
|
EnumNonMatching(~[(0, <ast::variant for B0>,
|
||
|
~[(None, <expr for a>)]),
|
||
|
(1, <ast::variant for B1>,
|
||
|
~[(Some(<ident of x>),
|
||
|
<expr for other.x>)])])
|
||
|
|
||
|
(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<ident>, @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<ident>, @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<ident>, @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<ident>, @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<ident>, @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)
|
||
|
}
|