2013-03-28 21:50:10 +11:00
|
|
|
// 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):
|
2013-05-07 01:23:51 +10:00
|
|
|
- Methods taking any number of parameters of any type, and returning
|
|
|
|
any type, other than vectors, bottom and closures.
|
|
|
|
- Generating `impl`s for types with type parameters and lifetimes
|
2013-03-28 21:50:10 +11:00
|
|
|
(e.g. `Option<T>`), the parameters are automatically given the
|
2013-05-07 01:23:51 +10:00
|
|
|
current trait as a bound. (This includes separate type parameters
|
|
|
|
and lifetimes for methods.)
|
2013-03-28 21:50:10 +11:00
|
|
|
- Additional bounds on the type parameters, e.g. the `Ord` instance
|
|
|
|
requires an explicit `Eq` bound at the
|
|
|
|
moment. (`TraitDef.additional_bounds`)
|
|
|
|
|
2013-05-07 01:23:51 +10:00
|
|
|
Unsupported: FIXME #6257: calling methods on borrowed pointer fields,
|
|
|
|
e.g. deriving TotalEq/TotalOrd/Clone don't work on `struct A(&int)`,
|
|
|
|
because of how the auto-dereferencing happens.
|
2013-03-28 21:50:10 +11:00
|
|
|
|
|
|
|
The most important thing for implementers is the `Substructure` and
|
2013-05-07 01:23:51 +10:00
|
|
|
`SubstructureFields` objects. The latter groups 5 possibilities of the
|
2013-03-28 21:50:10 +11:00
|
|
|
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
|
2013-04-02 22:02:46 +11:00
|
|
|
the same variant (e.g. `None`, `Some(1)` and `None`). If
|
|
|
|
`const_nonmatching` is true, this will contain an empty list.
|
2013-05-07 01:23:51 +10:00
|
|
|
- `StaticEnum` and `StaticStruct` for static methods, where the type
|
|
|
|
being derived upon is either a enum or struct respectively. (Any
|
|
|
|
argument with type Self is just grouped among the non-self
|
|
|
|
arguments.)
|
2013-03-28 21:50:10 +11:00
|
|
|
|
|
|
|
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
|
2013-05-07 01:23:51 +10:00
|
|
|
fields are grouped by which argument they come from. There are no
|
|
|
|
fields with values in the static cases, so these are treated entirely
|
|
|
|
differently.
|
2013-03-28 21:50:10 +11:00
|
|
|
|
2013-05-07 01:23:51 +10:00
|
|
|
The non-static cases have `Option<ident>` in several places associated
|
2013-03-28 21:50:10 +11:00
|
|
|
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
|
|
|
|
|
2013-05-07 01:23:51 +10:00
|
|
|
~~~
|
|
|
|
struct A { x : int }
|
2013-03-28 21:50:10 +11:00
|
|
|
|
2013-05-07 01:23:51 +10:00
|
|
|
struct B(int);
|
2013-03-28 21:50:10 +11:00
|
|
|
|
2013-05-07 01:23:51 +10:00
|
|
|
enum C {
|
|
|
|
C0(int),
|
|
|
|
C1 { x: int }
|
|
|
|
}
|
2013-03-28 21:50:10 +11:00
|
|
|
|
|
|
|
The `int`s in `B` and `C0` don't have an identifier, so the
|
|
|
|
`Option<ident>`s would be `None` for them.
|
|
|
|
|
2013-05-07 01:23:51 +10:00
|
|
|
In the static cases, the structure is summarised, either into the
|
|
|
|
number of fields or a list of field idents (for tuple structs and
|
|
|
|
record structs, respectively), or a list of these, for enums (one for
|
|
|
|
each variant). For empty struct and empty enum variants, it is
|
|
|
|
represented as a count of 0.
|
|
|
|
|
2013-03-28 21:50:10 +11:00
|
|
|
# Examples
|
|
|
|
|
|
|
|
The following simplified `Eq` is used for in-code examples:
|
|
|
|
|
2013-05-07 01:23:51 +10:00
|
|
|
~~~
|
|
|
|
trait Eq {
|
|
|
|
fn eq(&self, other: &Self);
|
|
|
|
}
|
|
|
|
impl Eq for int {
|
|
|
|
fn eq(&self, other: &int) -> bool {
|
|
|
|
*self == *other
|
2013-03-28 21:50:10 +11:00
|
|
|
}
|
2013-05-07 01:23:51 +10:00
|
|
|
}
|
|
|
|
~~~
|
2013-03-28 21:50:10 +11:00
|
|
|
|
|
|
|
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
|
|
|
|
|
2013-05-07 01:23:51 +10:00
|
|
|
~~~
|
|
|
|
Struct(~[(Some(<ident of x>),
|
|
|
|
<expr for &self.x>,
|
|
|
|
~[<expr for &other.x])])
|
|
|
|
~~~
|
2013-03-28 21:50:10 +11:00
|
|
|
|
|
|
|
For the `B` impl, called with `B(a)` and `B(b)`,
|
|
|
|
|
2013-05-07 01:23:51 +10:00
|
|
|
~~~
|
|
|
|
Struct(~[(None,
|
|
|
|
<expr for &a>
|
|
|
|
~[<expr for &b>])])
|
|
|
|
~~~
|
2013-03-28 21:50:10 +11:00
|
|
|
|
|
|
|
## Enums
|
|
|
|
|
|
|
|
When generating the `expr` for a call with `self == C0(a)` and `other
|
|
|
|
== C0(b)`, the SubstructureFields is
|
|
|
|
|
2013-05-07 01:23:51 +10:00
|
|
|
~~~
|
|
|
|
EnumMatching(0, <ast::variant for C0>,
|
|
|
|
~[None,
|
|
|
|
<expr for &a>,
|
|
|
|
~[<expr for &b>]])
|
|
|
|
~~~
|
2013-03-28 21:50:10 +11:00
|
|
|
|
|
|
|
For `C1 {x}` and `C1 {x}`,
|
|
|
|
|
2013-05-07 01:23:51 +10:00
|
|
|
~~~
|
|
|
|
EnumMatching(1, <ast::variant for C1>,
|
|
|
|
~[Some(<ident of x>),
|
|
|
|
<expr for &self.x>,
|
|
|
|
~[<expr for &other.x>]])
|
|
|
|
~~~
|
2013-03-28 21:50:10 +11:00
|
|
|
|
|
|
|
For `C0(a)` and `C1 {x}` ,
|
|
|
|
|
2013-05-07 01:23:51 +10:00
|
|
|
~~~
|
|
|
|
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 versa, but with the order of the outermost list flipped.)
|
2013-03-28 21:50:10 +11:00
|
|
|
|
2013-05-07 01:23:51 +10:00
|
|
|
## Static
|
|
|
|
|
|
|
|
A static method on the above would result in,
|
|
|
|
|
|
|
|
~~~~
|
|
|
|
StaticStruct(<ast::struct_def of A>, Right(~[<ident of x>]))
|
|
|
|
|
|
|
|
StaticStruct(<ast::struct_def of B>, Left(1))
|
|
|
|
|
|
|
|
StaticEnum(<ast::enum_def of C>, ~[(<ident of C0>, Left(1)),
|
|
|
|
(<ident of C1>, Right(~[<ident of x>]))])
|
|
|
|
~~~
|
2013-03-28 21:50:10 +11:00
|
|
|
|
|
|
|
*/
|
|
|
|
|
2013-05-17 15:28:44 -07:00
|
|
|
use core::prelude::*;
|
|
|
|
|
2013-03-28 21:50:10 +11:00
|
|
|
use ast;
|
2013-05-07 01:23:51 +10:00
|
|
|
use ast::{enum_def, expr, ident, Generics, struct_def};
|
2013-03-28 21:50:10 +11:00
|
|
|
|
2013-05-17 21:27:17 +10:00
|
|
|
use ext::base::ExtCtxt;
|
2013-05-18 00:19:28 +10:00
|
|
|
use ext::build::AstBuilder;
|
2013-03-28 21:50:10 +11:00
|
|
|
use ext::deriving::*;
|
|
|
|
use codemap::{span,respan};
|
|
|
|
use opt_vec;
|
|
|
|
|
2013-05-07 01:23:51 +10:00
|
|
|
pub use self::ty::*;
|
|
|
|
mod ty;
|
|
|
|
|
2013-05-17 21:27:17 +10:00
|
|
|
pub fn expand_deriving_generic(cx: @ExtCtxt,
|
2013-03-28 21:50:10 +11:00
|
|
|
span: span,
|
2013-05-07 01:23:51 +10:00
|
|
|
_mitem: @ast::meta_item,
|
|
|
|
in_items: ~[@ast::item],
|
|
|
|
trait_def: &TraitDef) -> ~[@ast::item] {
|
2013-03-28 21:50:10 +11:00
|
|
|
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> {
|
2013-05-07 01:23:51 +10:00
|
|
|
/// Path of the trait, including any type parameters
|
2013-05-17 18:41:14 +10:00
|
|
|
path: Path<'self>,
|
2013-05-07 01:23:51 +10:00
|
|
|
/// Additional bounds required of any type parameters of the type,
|
|
|
|
/// other than the current trait
|
2013-05-17 18:41:14 +10:00
|
|
|
additional_bounds: ~[Ty<'self>],
|
2013-05-07 01:23:51 +10:00
|
|
|
|
2013-05-18 12:39:17 -07:00
|
|
|
/// Any extra lifetimes and/or bounds, e.g. `D: extra::serialize::Decoder`
|
2013-05-17 18:41:14 +10:00
|
|
|
generics: LifetimeBounds<'self>,
|
2013-05-07 01:23:51 +10:00
|
|
|
|
2013-03-28 21:50:10 +11:00
|
|
|
methods: ~[MethodDef<'self>]
|
|
|
|
}
|
|
|
|
|
2013-05-07 01:23:51 +10:00
|
|
|
|
2013-03-28 21:50:10 +11:00
|
|
|
pub struct MethodDef<'self> {
|
|
|
|
/// name of the method
|
2013-05-17 18:41:14 +10:00
|
|
|
name: &'self str,
|
2013-05-07 01:23:51 +10:00
|
|
|
/// List of generics, e.g. `R: core::rand::Rng`
|
2013-05-17 18:41:14 +10:00
|
|
|
generics: LifetimeBounds<'self>,
|
2013-05-07 01:23:51 +10:00
|
|
|
|
|
|
|
/// Whether there is a self argument (outer Option) i.e. whether
|
|
|
|
/// this is a static function, and whether it is a pointer (inner
|
|
|
|
/// Option)
|
2013-05-17 18:41:14 +10:00
|
|
|
explicit_self: Option<Option<PtrTy<'self>>>,
|
2013-05-07 01:23:51 +10:00
|
|
|
|
|
|
|
/// Arguments other than the self argument
|
2013-05-17 18:41:14 +10:00
|
|
|
args: ~[Ty<'self>],
|
2013-05-07 01:23:51 +10:00
|
|
|
|
|
|
|
/// Return type
|
2013-05-17 18:41:14 +10:00
|
|
|
ret_ty: Ty<'self>,
|
2013-03-28 21:50:10 +11:00
|
|
|
|
2013-04-02 22:02:46 +11:00
|
|
|
/// if the value of the nonmatching enums is independent of the
|
2013-05-07 01:23:51 +10:00
|
|
|
/// actual enum variants, i.e. can use _ => .. match.
|
2013-04-02 22:02:46 +11:00
|
|
|
const_nonmatching: bool,
|
|
|
|
|
2013-03-28 21:50:10 +11:00
|
|
|
combine_substructure: CombineSubstructureFunc<'self>
|
|
|
|
}
|
|
|
|
|
|
|
|
/// All the data about the data structure/method being derived upon.
|
|
|
|
pub struct Substructure<'self> {
|
2013-05-07 01:23:51 +10:00
|
|
|
/// ident of self
|
2013-03-28 21:50:10 +11:00
|
|
|
type_ident: ident,
|
2013-05-07 01:23:51 +10:00
|
|
|
/// ident of the method
|
2013-03-28 21:50:10 +11:00
|
|
|
method_ident: ident,
|
2013-05-07 01:23:51 +10:00
|
|
|
/// dereferenced access to any Self or Ptr(Self, _) arguments
|
|
|
|
self_args: &'self [@expr],
|
|
|
|
/// verbatim access to any other arguments
|
|
|
|
nonself_args: &'self [@expr],
|
|
|
|
fields: &'self SubstructureFields<'self>
|
2013-03-28 21:50:10 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
/// A summary of the possible sets of fields. See above for details
|
|
|
|
/// and examples
|
2013-05-07 01:23:51 +10:00
|
|
|
pub enum SubstructureFields<'self> {
|
2013-03-28 21:50:10 +11:00
|
|
|
/**
|
2013-05-07 01:23:51 +10:00
|
|
|
Vec of `(field ident, self_or_other)` where the field
|
|
|
|
ident is the ident of the current field (`None` for all fields in tuple
|
|
|
|
structs).
|
2013-03-28 21:50:10 +11:00
|
|
|
*/
|
|
|
|
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.
|
|
|
|
*/
|
2013-05-12 00:25:31 -04:00
|
|
|
EnumMatching(uint, &'self ast::variant, ~[(Option<ident>, @expr, ~[@expr])]),
|
2013-03-28 21:50:10 +11:00
|
|
|
|
|
|
|
/**
|
|
|
|
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.)
|
|
|
|
*/
|
2013-05-12 00:25:31 -04:00
|
|
|
EnumNonMatching(&'self [(uint, ast::variant, ~[(Option<ident>, @expr)])]),
|
2013-05-07 01:23:51 +10:00
|
|
|
|
|
|
|
/// A static method where Self is a struct
|
|
|
|
StaticStruct(&'self ast::struct_def, Either<uint, ~[ident]>),
|
|
|
|
/// A static method where Self is an enum
|
|
|
|
StaticEnum(&'self ast::enum_def, ~[(ident, Either<uint, ~[ident]>)])
|
2013-03-28 21:50:10 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-05-07 01:23:51 +10:00
|
|
|
|
2013-03-28 21:50:10 +11:00
|
|
|
/**
|
|
|
|
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> =
|
2013-05-17 21:27:17 +10:00
|
|
|
&'self fn(@ExtCtxt, span, &Substructure) -> @expr;
|
2013-03-28 21:50:10 +11:00
|
|
|
|
|
|
|
/**
|
2013-05-07 01:23:51 +10:00
|
|
|
Deal with non-matching enum variants, the arguments are a list
|
2013-03-28 21:50:10 +11:00
|
|
|
representing each variant: (variant index, ast::variant instance,
|
2013-05-07 01:23:51 +10:00
|
|
|
[variant fields]), and a list of the nonself args of the type
|
2013-03-28 21:50:10 +11:00
|
|
|
*/
|
|
|
|
pub type EnumNonMatchFunc<'self> =
|
2013-05-17 21:27:17 +10:00
|
|
|
&'self fn(@ExtCtxt, span,
|
2013-05-12 00:25:31 -04:00
|
|
|
&[(uint, ast::variant,
|
2013-05-07 01:23:51 +10:00
|
|
|
~[(Option<ident>, @expr)])],
|
|
|
|
&[@expr]) -> @expr;
|
2013-03-28 21:50:10 +11:00
|
|
|
|
|
|
|
|
|
|
|
impl<'self> TraitDef<'self> {
|
2013-05-17 21:27:17 +10:00
|
|
|
fn create_derived_impl(&self, cx: @ExtCtxt, span: span,
|
2013-03-28 21:50:10 +11:00
|
|
|
type_ident: ident, generics: &Generics,
|
2013-05-07 01:23:51 +10:00
|
|
|
methods: ~[@ast::method]) -> @ast::item {
|
|
|
|
let trait_path = self.path.to_path(cx, span, type_ident, generics);
|
|
|
|
|
|
|
|
let trait_generics = self.generics.to_generics(cx, span, type_ident, generics);
|
2013-03-28 21:50:10 +11:00
|
|
|
|
|
|
|
let additional_bounds = opt_vec::from(
|
2013-05-07 01:23:51 +10:00
|
|
|
do self.additional_bounds.map |p| {
|
|
|
|
p.to_path(cx, span, type_ident, generics)
|
2013-03-28 21:50:10 +11:00
|
|
|
});
|
2013-05-07 01:23:51 +10:00
|
|
|
|
2013-03-28 21:50:10 +11:00
|
|
|
create_derived_impl(cx, span,
|
|
|
|
type_ident, generics,
|
|
|
|
methods, trait_path,
|
2013-05-07 01:23:51 +10:00
|
|
|
trait_generics,
|
2013-03-28 21:50:10 +11:00
|
|
|
additional_bounds)
|
|
|
|
}
|
|
|
|
|
2013-05-17 21:27:17 +10:00
|
|
|
fn expand_struct_def(&self, cx: @ExtCtxt,
|
2013-03-28 21:50:10 +11:00
|
|
|
span: span,
|
|
|
|
struct_def: &struct_def,
|
|
|
|
type_ident: ident,
|
2013-05-07 01:23:51 +10:00
|
|
|
generics: &Generics) -> @ast::item {
|
2013-03-28 21:50:10 +11:00
|
|
|
let methods = do self.methods.map |method_def| {
|
2013-04-30 08:49:48 -07:00
|
|
|
let (explicit_self, self_args, nonself_args, tys) =
|
2013-05-07 01:23:51 +10:00
|
|
|
method_def.split_self_nonself_args(cx, span, type_ident, generics);
|
|
|
|
|
|
|
|
let body = if method_def.is_static() {
|
|
|
|
method_def.expand_static_struct_method_body(
|
|
|
|
cx, span,
|
|
|
|
struct_def,
|
|
|
|
type_ident,
|
|
|
|
self_args, nonself_args)
|
2013-03-28 21:50:10 +11:00
|
|
|
} else {
|
|
|
|
method_def.expand_struct_method_body(cx, span,
|
|
|
|
struct_def,
|
2013-05-07 01:23:51 +10:00
|
|
|
type_ident,
|
|
|
|
self_args, nonself_args)
|
2013-03-28 21:50:10 +11:00
|
|
|
};
|
|
|
|
|
2013-05-07 01:23:51 +10:00
|
|
|
method_def.create_method(cx, span,
|
|
|
|
type_ident, generics,
|
2013-04-30 08:49:48 -07:00
|
|
|
explicit_self, tys,
|
2013-05-07 01:23:51 +10:00
|
|
|
body)
|
2013-03-28 21:50:10 +11:00
|
|
|
};
|
|
|
|
|
|
|
|
self.create_derived_impl(cx, span, type_ident, generics, methods)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn expand_enum_def(&self,
|
2013-05-17 21:27:17 +10:00
|
|
|
cx: @ExtCtxt, span: span,
|
2013-03-28 21:50:10 +11:00
|
|
|
enum_def: &enum_def,
|
|
|
|
type_ident: ident,
|
2013-05-07 01:23:51 +10:00
|
|
|
generics: &Generics) -> @ast::item {
|
2013-03-28 21:50:10 +11:00
|
|
|
let methods = do self.methods.map |method_def| {
|
2013-04-30 08:49:48 -07:00
|
|
|
let (explicit_self, self_args, nonself_args, tys) =
|
2013-05-07 01:23:51 +10:00
|
|
|
method_def.split_self_nonself_args(cx, span, type_ident, generics);
|
|
|
|
|
|
|
|
let body = if method_def.is_static() {
|
|
|
|
method_def.expand_static_enum_method_body(
|
|
|
|
cx, span,
|
|
|
|
enum_def,
|
|
|
|
type_ident,
|
|
|
|
self_args, nonself_args)
|
|
|
|
} else {
|
|
|
|
method_def.expand_enum_method_body(cx, span,
|
|
|
|
enum_def,
|
|
|
|
type_ident,
|
|
|
|
self_args, nonself_args)
|
|
|
|
};
|
2013-03-28 21:50:10 +11:00
|
|
|
|
2013-05-07 01:23:51 +10:00
|
|
|
method_def.create_method(cx, span,
|
|
|
|
type_ident, generics,
|
2013-04-30 08:49:48 -07:00
|
|
|
explicit_self, tys,
|
2013-05-07 01:23:51 +10:00
|
|
|
body)
|
2013-03-28 21:50:10 +11:00
|
|
|
};
|
|
|
|
|
|
|
|
self.create_derived_impl(cx, span, type_ident, generics, methods)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'self> MethodDef<'self> {
|
|
|
|
fn call_substructure_method(&self,
|
2013-05-17 21:27:17 +10:00
|
|
|
cx: @ExtCtxt,
|
2013-03-28 21:50:10 +11:00
|
|
|
span: span,
|
|
|
|
type_ident: ident,
|
2013-05-07 01:23:51 +10:00
|
|
|
self_args: &[@expr],
|
|
|
|
nonself_args: &[@expr],
|
2013-03-28 21:50:10 +11:00
|
|
|
fields: &SubstructureFields)
|
|
|
|
-> @expr {
|
|
|
|
let substructure = Substructure {
|
|
|
|
type_ident: type_ident,
|
|
|
|
method_ident: cx.ident_of(self.name),
|
2013-05-07 01:23:51 +10:00
|
|
|
self_args: self_args,
|
|
|
|
nonself_args: nonself_args,
|
2013-03-28 21:50:10 +11:00
|
|
|
fields: fields
|
|
|
|
};
|
|
|
|
(self.combine_substructure)(cx, span,
|
|
|
|
&substructure)
|
|
|
|
}
|
|
|
|
|
2013-05-17 21:27:17 +10:00
|
|
|
fn get_ret_ty(&self, cx: @ExtCtxt, span: span,
|
2013-05-07 01:23:51 +10:00
|
|
|
generics: &Generics, type_ident: ident) -> @ast::Ty {
|
|
|
|
self.ret_ty.to_ty(cx, span, type_ident, generics)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn is_static(&self) -> bool {
|
2013-04-30 08:49:48 -07:00
|
|
|
self.explicit_self.is_none()
|
2013-05-07 01:23:51 +10:00
|
|
|
}
|
2013-03-28 21:50:10 +11:00
|
|
|
|
2013-05-17 21:27:17 +10:00
|
|
|
fn split_self_nonself_args(&self, cx: @ExtCtxt, span: span,
|
2013-05-07 01:23:51 +10:00
|
|
|
type_ident: ident, generics: &Generics)
|
2013-04-30 08:49:48 -07:00
|
|
|
-> (ast::explicit_self, ~[@expr], ~[@expr], ~[(ident, @ast::Ty)]) {
|
2013-05-07 01:23:51 +10:00
|
|
|
|
|
|
|
let mut self_args = ~[], nonself_args = ~[], arg_tys = ~[];
|
|
|
|
let mut nonstatic = false;
|
|
|
|
|
2013-04-30 08:49:48 -07:00
|
|
|
let ast_explicit_self = match self.explicit_self {
|
2013-05-12 00:25:31 -04:00
|
|
|
Some(ref self_ptr) => {
|
2013-04-30 08:49:48 -07:00
|
|
|
let (self_expr, explicit_self) = ty::get_explicit_self(cx, span, self_ptr);
|
2013-05-07 01:23:51 +10:00
|
|
|
|
|
|
|
self_args.push(self_expr);
|
|
|
|
nonstatic = true;
|
2013-04-30 08:49:48 -07:00
|
|
|
|
|
|
|
explicit_self
|
2013-03-28 21:50:10 +11:00
|
|
|
}
|
2013-04-30 08:49:48 -07:00
|
|
|
None => respan(span, ast::sty_static),
|
|
|
|
};
|
2013-05-07 01:23:51 +10:00
|
|
|
|
|
|
|
for self.args.eachi |i, ty| {
|
|
|
|
let ast_ty = ty.to_ty(cx, span, type_ident, generics);
|
|
|
|
let ident = cx.ident_of(fmt!("__arg_%u", i));
|
|
|
|
arg_tys.push((ident, ast_ty));
|
|
|
|
|
2013-05-19 15:53:42 +10:00
|
|
|
let arg_expr = cx.expr_ident(span, ident);
|
2013-05-07 01:23:51 +10:00
|
|
|
|
|
|
|
match *ty {
|
|
|
|
// for static methods, just treat any Self
|
|
|
|
// arguments as a normal arg
|
|
|
|
Self if nonstatic => {
|
|
|
|
self_args.push(arg_expr);
|
|
|
|
}
|
|
|
|
Ptr(~Self, _) if nonstatic => {
|
2013-05-19 15:53:42 +10:00
|
|
|
self_args.push(cx.expr_deref(span, arg_expr))
|
2013-05-07 01:23:51 +10:00
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
nonself_args.push(arg_expr);
|
|
|
|
}
|
2013-03-28 21:50:10 +11:00
|
|
|
}
|
|
|
|
}
|
2013-05-07 01:23:51 +10:00
|
|
|
|
2013-04-30 08:49:48 -07:00
|
|
|
(ast_explicit_self, self_args, nonself_args, arg_tys)
|
2013-03-28 21:50:10 +11:00
|
|
|
}
|
|
|
|
|
2013-05-17 21:27:17 +10:00
|
|
|
fn create_method(&self, cx: @ExtCtxt, span: span,
|
2013-03-28 21:50:10 +11:00
|
|
|
type_ident: ident,
|
2013-05-07 01:23:51 +10:00
|
|
|
generics: &Generics,
|
2013-04-30 08:49:48 -07:00
|
|
|
explicit_self: ast::explicit_self,
|
2013-05-07 01:23:51 +10:00
|
|
|
arg_types: ~[(ident, @ast::Ty)],
|
|
|
|
body: @expr) -> @ast::method {
|
|
|
|
// create the generics that aren't for Self
|
|
|
|
let fn_generics = self.generics.to_generics(cx, span, type_ident, generics);
|
|
|
|
|
|
|
|
let args = do arg_types.map |&(id, ty)| {
|
2013-05-19 15:53:42 +10:00
|
|
|
cx.arg(span, id, ty)
|
2013-03-28 21:50:10 +11:00
|
|
|
};
|
|
|
|
|
2013-05-07 01:23:51 +10:00
|
|
|
let ret_type = self.get_ret_ty(cx, span, generics, type_ident);
|
2013-03-28 21:50:10 +11:00
|
|
|
|
|
|
|
let method_ident = cx.ident_of(self.name);
|
2013-05-19 15:53:42 +10:00
|
|
|
let fn_decl = cx.fn_decl(args, ret_type);
|
|
|
|
let body_block = cx.blk_expr(body);
|
2013-03-28 21:50:10 +11:00
|
|
|
|
2013-05-07 01:23:51 +10:00
|
|
|
|
2013-03-28 21:50:10 +11:00
|
|
|
// Create the method.
|
|
|
|
@ast::method {
|
|
|
|
ident: method_ident,
|
|
|
|
attrs: ~[],
|
2013-05-07 01:23:51 +10:00
|
|
|
generics: fn_generics,
|
2013-04-30 08:49:48 -07:00
|
|
|
explicit_self: explicit_self,
|
2013-05-07 01:23:51 +10:00
|
|
|
purity: ast::impure_fn,
|
2013-03-28 21:50:10 +11:00
|
|
|
decl: fn_decl,
|
|
|
|
body: body_block,
|
|
|
|
id: cx.next_id(),
|
|
|
|
span: span,
|
|
|
|
self_id: cx.next_id(),
|
2013-05-07 01:23:51 +10:00
|
|
|
vis: ast::public
|
2013-03-28 21:50:10 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2013-05-07 01:23:51 +10:00
|
|
|
~~~
|
2013-03-28 21:50:10 +11:00
|
|
|
#[deriving(Eq)]
|
2013-05-07 01:23:51 +10:00
|
|
|
struct A { x: int, y: int }
|
2013-03-28 21:50:10 +11:00
|
|
|
|
|
|
|
// equivalent to:
|
|
|
|
impl Eq for A {
|
2013-05-07 01:23:51 +10:00
|
|
|
fn eq(&self, __arg_1: &A) -> bool {
|
2013-03-28 21:50:10 +11:00
|
|
|
match *self {
|
2013-05-07 01:23:51 +10:00
|
|
|
A {x: ref __self_0_0, y: ref __self_0_1} => {
|
|
|
|
match *__arg_1 {
|
|
|
|
A {x: ref __self_1_0, y: ref __self_1_1} => {
|
|
|
|
__self_0_0.eq(__self_1_0) && __self_0_1.eq(__self_1_1)
|
2013-03-28 21:50:10 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-05-07 01:23:51 +10:00
|
|
|
~~~
|
2013-03-28 21:50:10 +11:00
|
|
|
*/
|
2013-05-07 01:23:51 +10:00
|
|
|
fn expand_struct_method_body(&self,
|
2013-05-17 21:27:17 +10:00
|
|
|
cx: @ExtCtxt,
|
2013-05-07 01:23:51 +10:00
|
|
|
span: span,
|
|
|
|
struct_def: &struct_def,
|
|
|
|
type_ident: ident,
|
|
|
|
self_args: &[@expr],
|
|
|
|
nonself_args: &[@expr])
|
|
|
|
-> @expr {
|
2013-03-28 21:50:10 +11:00
|
|
|
|
2013-05-07 01:23:51 +10:00
|
|
|
let mut raw_fields = ~[], // ~[[fields of self], [fields of next Self arg], [etc]]
|
|
|
|
patterns = ~[];
|
|
|
|
for uint::range(0, self_args.len()) |i| {
|
|
|
|
let (pat, ident_expr) = create_struct_pattern(cx, span,
|
|
|
|
type_ident, struct_def,
|
|
|
|
fmt!("__self_%u", i), ast::m_imm);
|
|
|
|
patterns.push(pat);
|
|
|
|
raw_fields.push(ident_expr);
|
2013-03-28 21:50:10 +11:00
|
|
|
};
|
|
|
|
|
2013-05-07 01:23:51 +10:00
|
|
|
// transpose raw_fields
|
|
|
|
let fields = match raw_fields {
|
|
|
|
[self_arg, .. rest] => {
|
|
|
|
do self_arg.mapi |i, &(opt_id, field)| {
|
|
|
|
let other_fields = do rest.map |l| {
|
|
|
|
match &l[i] {
|
|
|
|
&(_, ex) => ex
|
|
|
|
}
|
|
|
|
};
|
|
|
|
(opt_id, field, other_fields)
|
|
|
|
}
|
|
|
|
}
|
2013-05-19 01:07:44 -04:00
|
|
|
[] => { cx.span_bug(span, "No self arguments to non-static \
|
|
|
|
method in generic `deriving`") }
|
2013-05-07 01:23:51 +10:00
|
|
|
};
|
2013-03-28 21:50:10 +11:00
|
|
|
|
2013-05-07 01:23:51 +10:00
|
|
|
// body of the inner most destructuring match
|
|
|
|
let mut body = self.call_substructure_method(
|
|
|
|
cx, span,
|
|
|
|
type_ident,
|
|
|
|
self_args,
|
|
|
|
nonself_args,
|
|
|
|
&Struct(fields));
|
|
|
|
|
|
|
|
// make a series of nested matches, to destructure the
|
|
|
|
// structs. This is actually right-to-left, but it shoudn't
|
|
|
|
// matter.
|
|
|
|
for vec::each2(self_args, patterns) |&arg_expr, &pat| {
|
2013-05-19 15:53:42 +10:00
|
|
|
body = cx.expr_match(span, arg_expr,
|
|
|
|
~[ cx.arm(span, ~[pat], body) ])
|
2013-03-28 21:50:10 +11:00
|
|
|
}
|
2013-05-07 01:23:51 +10:00
|
|
|
body
|
2013-03-28 21:50:10 +11:00
|
|
|
}
|
|
|
|
|
2013-05-07 01:23:51 +10:00
|
|
|
fn expand_static_struct_method_body(&self,
|
2013-05-17 21:27:17 +10:00
|
|
|
cx: @ExtCtxt,
|
2013-05-07 01:23:51 +10:00
|
|
|
span: span,
|
|
|
|
struct_def: &struct_def,
|
|
|
|
type_ident: ident,
|
|
|
|
self_args: &[@expr],
|
|
|
|
nonself_args: &[@expr])
|
2013-03-28 21:50:10 +11:00
|
|
|
-> @expr {
|
2013-05-07 01:23:51 +10:00
|
|
|
let summary = summarise_struct(cx, span, struct_def);
|
2013-03-28 21:50:10 +11:00
|
|
|
|
2013-05-07 01:23:51 +10:00
|
|
|
self.call_substructure_method(cx, span,
|
|
|
|
type_ident,
|
|
|
|
self_args, nonself_args,
|
|
|
|
&StaticStruct(struct_def, summary))
|
2013-03-28 21:50:10 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2013-05-07 01:23:51 +10:00
|
|
|
~~~
|
2013-03-28 21:50:10 +11:00
|
|
|
#[deriving(Eq)]
|
|
|
|
enum A {
|
|
|
|
A1
|
|
|
|
A2(int)
|
|
|
|
}
|
|
|
|
|
2013-05-07 01:23:51 +10:00
|
|
|
// is equivalent to (with const_nonmatching == false)
|
2013-03-28 21:50:10 +11:00
|
|
|
|
|
|
|
impl Eq for A {
|
2013-05-07 01:23:51 +10:00
|
|
|
fn eq(&self, __arg_1: &A) {
|
2013-03-28 21:50:10 +11:00
|
|
|
match *self {
|
2013-05-07 01:23:51 +10:00
|
|
|
A1 => match *__arg_1 {
|
|
|
|
A1 => true
|
|
|
|
A2(ref __arg_1_1) => false
|
2013-03-28 21:50:10 +11:00
|
|
|
},
|
2013-05-07 01:23:51 +10:00
|
|
|
A2(self_1) => match *__arg_1 {
|
2013-03-28 21:50:10 +11:00
|
|
|
A1 => false,
|
2013-05-07 01:23:51 +10:00
|
|
|
A2(ref __arg_1_1) => self_1.eq(__arg_1_1)
|
2013-03-28 21:50:10 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-05-07 01:23:51 +10:00
|
|
|
~~~
|
2013-03-28 21:50:10 +11:00
|
|
|
*/
|
|
|
|
fn expand_enum_method_body(&self,
|
2013-05-17 21:27:17 +10:00
|
|
|
cx: @ExtCtxt,
|
2013-03-28 21:50:10 +11:00
|
|
|
span: span,
|
|
|
|
enum_def: &enum_def,
|
2013-05-07 01:23:51 +10:00
|
|
|
type_ident: ident,
|
|
|
|
self_args: &[@expr],
|
|
|
|
nonself_args: &[@expr])
|
2013-03-28 21:50:10 +11:00
|
|
|
-> @expr {
|
2013-05-12 00:25:31 -04:00
|
|
|
let mut matches = ~[];
|
2013-04-02 22:02:46 +11:00
|
|
|
self.build_enum_match(cx, span, enum_def, type_ident,
|
2013-05-07 01:23:51 +10:00
|
|
|
self_args, nonself_args,
|
2013-05-12 00:25:31 -04:00
|
|
|
None, &mut matches, 0)
|
2013-03-28 21:50:10 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2013-04-02 22:02:46 +11:00
|
|
|
Creates the nested matches for an enum definition recursively, i.e.
|
2013-03-28 21:50:10 +11:00
|
|
|
|
2013-05-07 01:23:51 +10:00
|
|
|
~~~
|
2013-03-28 21:50:10 +11:00
|
|
|
match self {
|
|
|
|
Variant1 => match other { Variant1 => matching, Variant2 => nonmatching, ... },
|
|
|
|
Variant2 => match other { Variant1 => nonmatching, Variant2 => matching, ... },
|
|
|
|
...
|
|
|
|
}
|
2013-05-07 01:23:51 +10:00
|
|
|
~~~
|
2013-03-28 21:50:10 +11:00
|
|
|
|
|
|
|
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.
|
2013-04-02 22:02:46 +11:00
|
|
|
|
|
|
|
`matching` is Some(n) if all branches in the tree above the
|
|
|
|
current position are variant `n`, `None` otherwise (including on
|
|
|
|
the first call).
|
2013-03-28 21:50:10 +11:00
|
|
|
*/
|
|
|
|
fn build_enum_match(&self,
|
2013-05-17 21:27:17 +10:00
|
|
|
cx: @ExtCtxt, span: span,
|
2013-03-28 21:50:10 +11:00
|
|
|
enum_def: &enum_def,
|
|
|
|
type_ident: ident,
|
2013-05-07 01:23:51 +10:00
|
|
|
self_args: &[@expr],
|
|
|
|
nonself_args: &[@expr],
|
2013-04-02 22:02:46 +11:00
|
|
|
matching: Option<uint>,
|
2013-05-12 00:25:31 -04:00
|
|
|
matches_so_far: &mut ~[(uint, ast::variant,
|
|
|
|
~[(Option<ident>, @expr)])],
|
2013-04-02 22:02:46 +11:00
|
|
|
match_count: uint) -> @expr {
|
2013-05-07 01:23:51 +10:00
|
|
|
if match_count == self_args.len() {
|
2013-03-28 21:50:10 +11:00
|
|
|
// we've matched against all arguments, so make the final
|
|
|
|
// expression at the bottom of the match tree
|
2013-05-12 00:25:31 -04:00
|
|
|
if matches_so_far.len() == 0 {
|
2013-05-19 01:07:44 -04:00
|
|
|
cx.span_bug(span, "no self match on an enum in generic \
|
|
|
|
`deriving`");
|
2013-05-12 00:25:31 -04:00
|
|
|
}
|
|
|
|
// 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.
|
|
|
|
|
|
|
|
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.
|
|
|
|
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] {
|
|
|
|
(_, ref 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);
|
2013-03-28 21:50:10 +11:00
|
|
|
}
|
|
|
|
}
|
2013-05-12 00:25:31 -04:00
|
|
|
let field_tuples =
|
|
|
|
do vec::map_zip(*self_vec,
|
2013-05-19 15:53:42 +10:00
|
|
|
enum_matching_fields) |&(id, self_f), &other| {
|
2013-05-12 00:25:31 -04:00
|
|
|
(id, self_f, other)
|
|
|
|
};
|
|
|
|
substructure = EnumMatching(variant_index, variant, field_tuples);
|
|
|
|
}
|
|
|
|
None => {
|
|
|
|
substructure = EnumNonMatching(*matches_so_far);
|
2013-03-28 21:50:10 +11:00
|
|
|
}
|
|
|
|
}
|
2013-05-12 00:25:31 -04:00
|
|
|
self.call_substructure_method(cx, span, type_ident,
|
|
|
|
self_args, nonself_args,
|
|
|
|
&substructure)
|
2013-03-28 21:50:10 +11:00
|
|
|
|
|
|
|
} else { // there are still matches to create
|
2013-05-07 01:23:51 +10:00
|
|
|
let current_match_str = if match_count == 0 {
|
|
|
|
~"__self"
|
2013-03-28 21:50:10 +11:00
|
|
|
} else {
|
2013-05-07 01:23:51 +10:00
|
|
|
fmt!("__arg_%u", match_count)
|
2013-03-28 21:50:10 +11:00
|
|
|
};
|
|
|
|
|
|
|
|
let mut arms = ~[];
|
|
|
|
|
2013-04-02 22:02:46 +11:00
|
|
|
// 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,
|
2013-05-19 01:07:44 -04:00
|
|
|
None => cx.span_bug(span, "Non-matching variants when required to \
|
|
|
|
be matching in generic `deriving`")
|
2013-04-02 22:02:46 +11:00
|
|
|
};
|
|
|
|
|
|
|
|
// matching-variant match
|
|
|
|
let variant = &enum_def.variants[index];
|
2013-05-07 01:23:51 +10:00
|
|
|
let (pattern, idents) = create_enum_variant_pattern(cx, span,
|
|
|
|
variant,
|
|
|
|
current_match_str,
|
|
|
|
ast::m_imm);
|
2013-03-28 21:50:10 +11:00
|
|
|
|
2013-05-12 00:25:31 -04:00
|
|
|
matches_so_far.push((index, /*bad*/ copy *variant, idents));
|
2013-03-28 21:50:10 +11:00
|
|
|
let arm_expr = self.build_enum_match(cx, span,
|
|
|
|
enum_def,
|
|
|
|
type_ident,
|
2013-05-07 01:23:51 +10:00
|
|
|
self_args, nonself_args,
|
2013-04-02 22:02:46 +11:00
|
|
|
matching,
|
|
|
|
matches_so_far,
|
|
|
|
match_count + 1);
|
2013-03-28 21:50:10 +11:00
|
|
|
matches_so_far.pop();
|
2013-05-19 15:53:42 +10:00
|
|
|
arms.push(cx.arm(span, ~[ pattern ], arm_expr));
|
2013-03-28 21:50:10 +11:00
|
|
|
|
2013-04-02 22:02:46 +11:00
|
|
|
if enum_def.variants.len() > 1 {
|
2013-05-12 00:25:31 -04:00
|
|
|
let e = &EnumNonMatching(&[]);
|
2013-04-02 22:02:46 +11:00
|
|
|
let wild_expr = self.call_substructure_method(cx, span, type_ident,
|
2013-05-07 01:23:51 +10:00
|
|
|
self_args, nonself_args,
|
2013-05-12 00:25:31 -04:00
|
|
|
e);
|
2013-05-19 15:53:42 +10:00
|
|
|
let wild_arm = cx.arm(span,
|
|
|
|
~[ cx.pat_wild(span) ],
|
|
|
|
wild_expr);
|
2013-04-02 22:02:46 +11:00
|
|
|
arms.push(wild_arm);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// create an arm matching on each variant
|
|
|
|
for enum_def.variants.eachi |index, variant| {
|
2013-05-07 01:23:51 +10:00
|
|
|
let (pattern, idents) = create_enum_variant_pattern(cx, span,
|
|
|
|
variant,
|
|
|
|
current_match_str,
|
|
|
|
ast::m_imm);
|
2013-04-02 22:02:46 +11:00
|
|
|
|
2013-05-12 00:25:31 -04:00
|
|
|
matches_so_far.push((index, /*bad*/ copy *variant, idents));
|
2013-04-02 22:02:46 +11:00
|
|
|
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,
|
2013-05-07 01:23:51 +10:00
|
|
|
self_args, nonself_args,
|
2013-04-02 22:02:46 +11:00
|
|
|
new_matching,
|
|
|
|
matches_so_far,
|
|
|
|
match_count + 1);
|
|
|
|
matches_so_far.pop();
|
|
|
|
|
2013-05-19 15:53:42 +10:00
|
|
|
let arm = cx.arm(span, ~[ pattern ], arm_expr);
|
2013-04-02 22:02:46 +11:00
|
|
|
arms.push(arm);
|
|
|
|
}
|
|
|
|
}
|
2013-03-28 21:50:10 +11:00
|
|
|
|
2013-05-07 01:23:51 +10:00
|
|
|
// match foo { arm, arm, arm, ... }
|
2013-05-19 15:53:42 +10:00
|
|
|
cx.expr_match(span, self_args[match_count], arms)
|
2013-03-28 21:50:10 +11:00
|
|
|
}
|
|
|
|
}
|
2013-05-07 01:23:51 +10:00
|
|
|
|
|
|
|
fn expand_static_enum_method_body(&self,
|
2013-05-17 21:27:17 +10:00
|
|
|
cx: @ExtCtxt,
|
2013-05-07 01:23:51 +10:00
|
|
|
span: span,
|
|
|
|
enum_def: &enum_def,
|
|
|
|
type_ident: ident,
|
|
|
|
self_args: &[@expr],
|
|
|
|
nonself_args: &[@expr])
|
|
|
|
-> @expr {
|
|
|
|
let summary = do enum_def.variants.map |v| {
|
|
|
|
let ident = v.node.name;
|
|
|
|
let summary = match v.node.kind {
|
|
|
|
ast::tuple_variant_kind(ref args) => Left(args.len()),
|
|
|
|
ast::struct_variant_kind(struct_def) => {
|
|
|
|
summarise_struct(cx, span, struct_def)
|
|
|
|
}
|
|
|
|
};
|
|
|
|
(ident, summary)
|
|
|
|
};
|
|
|
|
self.call_substructure_method(cx,
|
|
|
|
span, type_ident,
|
|
|
|
self_args, nonself_args,
|
|
|
|
&StaticEnum(enum_def, summary))
|
|
|
|
}
|
2013-03-28 21:50:10 +11:00
|
|
|
}
|
|
|
|
|
2013-05-17 21:27:17 +10:00
|
|
|
fn summarise_struct(cx: @ExtCtxt, span: span,
|
2013-05-07 01:23:51 +10:00
|
|
|
struct_def: &struct_def) -> Either<uint, ~[ident]> {
|
|
|
|
let mut named_idents = ~[];
|
|
|
|
let mut unnamed_count = 0;
|
|
|
|
for struct_def.fields.each |field| {
|
|
|
|
match field.node.kind {
|
2013-05-08 09:03:31 -07:00
|
|
|
ast::named_field(ident, _) => named_idents.push(ident),
|
|
|
|
ast::unnamed_field => unnamed_count += 1,
|
2013-03-28 21:50:10 +11:00
|
|
|
}
|
|
|
|
}
|
2013-05-07 01:23:51 +10:00
|
|
|
|
|
|
|
match (unnamed_count > 0, named_idents.is_empty()) {
|
|
|
|
(true, false) => cx.span_bug(span,
|
|
|
|
"A struct with named and unnamed \
|
|
|
|
fields in generic `deriving`"),
|
|
|
|
// named fields
|
|
|
|
(_, false) => Right(named_idents),
|
|
|
|
// tuple structs (includes empty structs)
|
|
|
|
(_, _) => Left(unnamed_count)
|
2013-03-28 21:50:10 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 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,
|
2013-05-17 21:27:17 +10:00
|
|
|
f: &fn(@ExtCtxt, span,
|
2013-03-28 21:50:10 +11:00
|
|
|
old: @expr,
|
2013-05-07 01:23:51 +10:00
|
|
|
self_f: @expr, other_fs: &[@expr]) -> @expr,
|
2013-03-28 21:50:10 +11:00
|
|
|
base: @expr,
|
|
|
|
enum_nonmatch_f: EnumNonMatchFunc,
|
2013-05-17 21:27:17 +10:00
|
|
|
cx: @ExtCtxt, span: span,
|
2013-03-28 21:50:10 +11:00
|
|
|
substructure: &Substructure) -> @expr {
|
|
|
|
match *substructure.fields {
|
2013-05-12 00:25:31 -04:00
|
|
|
EnumMatching(_, _, ref all_fields) | Struct(ref all_fields) => {
|
2013-03-28 21:50:10 +11:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
2013-05-12 00:25:31 -04:00
|
|
|
EnumNonMatching(ref all_enums) => enum_nonmatch_f(cx, span,
|
|
|
|
*all_enums,
|
|
|
|
substructure.nonself_args),
|
2013-05-07 01:23:51 +10:00
|
|
|
StaticEnum(*) | StaticStruct(*) => {
|
|
|
|
cx.span_bug(span, "Static function in `deriving`")
|
|
|
|
}
|
2013-03-28 21:50:10 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
Call the method that is being derived on all the fields, and then
|
|
|
|
process the collected results. i.e.
|
|
|
|
|
2013-05-07 01:23:51 +10:00
|
|
|
~~~
|
|
|
|
f(cx, span, ~[self_1.method(__arg_1_1, __arg_2_1),
|
|
|
|
self_2.method(__arg_1_2, __arg_2_2)])
|
|
|
|
~~~
|
2013-03-28 21:50:10 +11:00
|
|
|
*/
|
2013-05-07 01:23:51 +10:00
|
|
|
#[inline(always)]
|
2013-05-17 21:27:17 +10:00
|
|
|
pub fn cs_same_method(f: &fn(@ExtCtxt, span, ~[@expr]) -> @expr,
|
2013-03-28 21:50:10 +11:00
|
|
|
enum_nonmatch_f: EnumNonMatchFunc,
|
2013-05-17 21:27:17 +10:00
|
|
|
cx: @ExtCtxt, span: span,
|
2013-03-28 21:50:10 +11:00
|
|
|
substructure: &Substructure) -> @expr {
|
|
|
|
match *substructure.fields {
|
2013-05-12 00:25:31 -04:00
|
|
|
EnumMatching(_, _, ref all_fields) | Struct(ref all_fields) => {
|
2013-03-28 21:50:10 +11:00
|
|
|
// call self_n.method(other_1_n, other_2_n, ...)
|
|
|
|
let called = do all_fields.map |&(_, self_field, other_fields)| {
|
2013-05-19 15:53:42 +10:00
|
|
|
cx.expr_method_call(span,
|
|
|
|
self_field,
|
|
|
|
substructure.method_ident,
|
|
|
|
other_fields)
|
2013-03-28 21:50:10 +11:00
|
|
|
};
|
|
|
|
|
|
|
|
f(cx, span, called)
|
|
|
|
},
|
2013-05-12 00:25:31 -04:00
|
|
|
EnumNonMatching(ref all_enums) => enum_nonmatch_f(cx, span,
|
|
|
|
*all_enums,
|
|
|
|
substructure.nonself_args),
|
2013-05-07 01:23:51 +10:00
|
|
|
StaticEnum(*) | StaticStruct(*) => {
|
|
|
|
cx.span_bug(span, "Static function in `deriving`")
|
|
|
|
}
|
2013-03-28 21:50:10 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
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`).
|
|
|
|
*/
|
2013-05-07 01:23:51 +10:00
|
|
|
#[inline(always)]
|
2013-03-28 21:50:10 +11:00
|
|
|
pub fn cs_same_method_fold(use_foldl: bool,
|
2013-05-17 21:27:17 +10:00
|
|
|
f: &fn(@ExtCtxt, span, @expr, @expr) -> @expr,
|
2013-03-28 21:50:10 +11:00
|
|
|
base: @expr,
|
|
|
|
enum_nonmatch_f: EnumNonMatchFunc,
|
2013-05-17 21:27:17 +10:00
|
|
|
cx: @ExtCtxt, span: span,
|
2013-03-28 21:50:10 +11:00
|
|
|
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.
|
|
|
|
*/
|
2013-05-07 01:23:51 +10:00
|
|
|
#[inline(always)]
|
|
|
|
pub fn cs_binop(binop: ast::binop, base: @expr,
|
2013-03-28 21:50:10 +11:00
|
|
|
enum_nonmatch_f: EnumNonMatchFunc,
|
2013-05-17 21:27:17 +10:00
|
|
|
cx: @ExtCtxt, span: span,
|
2013-03-28 21:50:10 +11:00
|
|
|
substructure: &Substructure) -> @expr {
|
|
|
|
cs_same_method_fold(
|
|
|
|
true, // foldl is good enough
|
|
|
|
|cx, span, old, new| {
|
2013-05-19 15:53:42 +10:00
|
|
|
cx.expr_binary(span,
|
|
|
|
binop,
|
|
|
|
old, new)
|
2013-03-28 21:50:10 +11:00
|
|
|
|
|
|
|
},
|
|
|
|
base,
|
|
|
|
enum_nonmatch_f,
|
|
|
|
cx, span, substructure)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// cs_binop with binop == or
|
2013-05-07 01:23:51 +10:00
|
|
|
#[inline(always)]
|
2013-03-28 21:50:10 +11:00
|
|
|
pub fn cs_or(enum_nonmatch_f: EnumNonMatchFunc,
|
2013-05-17 21:27:17 +10:00
|
|
|
cx: @ExtCtxt, span: span,
|
2013-03-28 21:50:10 +11:00
|
|
|
substructure: &Substructure) -> @expr {
|
2013-05-19 15:53:42 +10:00
|
|
|
cs_binop(ast::or, cx.expr_bool(span, false),
|
2013-03-28 21:50:10 +11:00
|
|
|
enum_nonmatch_f,
|
|
|
|
cx, span, substructure)
|
|
|
|
}
|
|
|
|
/// cs_binop with binop == and
|
2013-05-07 01:23:51 +10:00
|
|
|
#[inline(always)]
|
2013-03-28 21:50:10 +11:00
|
|
|
pub fn cs_and(enum_nonmatch_f: EnumNonMatchFunc,
|
2013-05-17 21:27:17 +10:00
|
|
|
cx: @ExtCtxt, span: span,
|
2013-03-28 21:50:10 +11:00
|
|
|
substructure: &Substructure) -> @expr {
|
2013-05-19 15:53:42 +10:00
|
|
|
cs_binop(ast::and, cx.expr_bool(span, true),
|
2013-03-28 21:50:10 +11:00
|
|
|
enum_nonmatch_f,
|
|
|
|
cx, span, substructure)
|
|
|
|
}
|