1
Fork 0

libsyntax: extend generic deriving code to handle almost all possible traits.

This adds support for static methods, and arguments of most types, traits with
type parameters, methods with type parameters (and lifetimes for both), as well
as making the code more robust to support deriving on types with lifetimes (i.e.
'self).
This commit is contained in:
Huon Wilson 2013-05-07 01:23:51 +10:00
parent 6e6a4be19d
commit b20eb970e1
11 changed files with 945 additions and 563 deletions

View file

@ -54,43 +54,52 @@ pub fn mk_binary(cx: @ext_ctxt, sp: span, op: ast::binop,
cx.next_id(); // see ast_util::op_expr_callee_id cx.next_id(); // see ast_util::op_expr_callee_id
mk_expr(cx, sp, ast::expr_binary(op, lhs, rhs)) mk_expr(cx, sp, ast::expr_binary(op, lhs, rhs))
} }
pub fn mk_deref(cx: @ext_ctxt, sp: span, e: @ast::expr) -> @ast::expr {
mk_unary(cx, sp, ast::deref, e)
}
pub fn mk_unary(cx: @ext_ctxt, sp: span, op: ast::unop, e: @ast::expr) pub fn mk_unary(cx: @ext_ctxt, sp: span, op: ast::unop, e: @ast::expr)
-> @ast::expr { -> @ast::expr {
cx.next_id(); // see ast_util::op_expr_callee_id cx.next_id(); // see ast_util::op_expr_callee_id
mk_expr(cx, sp, ast::expr_unary(op, e)) mk_expr(cx, sp, ast::expr_unary(op, e))
} }
pub fn mk_raw_path(sp: span, idents: ~[ast::ident]) -> @ast::Path { pub fn mk_raw_path(sp: span, idents: ~[ast::ident]) -> @ast::Path {
mk_raw_path_(sp, idents, ~[]) mk_raw_path_(sp, idents, None, ~[])
} }
pub fn mk_raw_path_(sp: span, pub fn mk_raw_path_(sp: span,
idents: ~[ast::ident], idents: ~[ast::ident],
rp: Option<@ast::Lifetime>,
types: ~[@ast::Ty]) types: ~[@ast::Ty])
-> @ast::Path { -> @ast::Path {
@ast::Path { span: sp, @ast::Path { span: sp,
global: false, global: false,
idents: idents, idents: idents,
rp: None, rp: rp,
types: types } types: types }
} }
pub fn mk_raw_path_global(sp: span, idents: ~[ast::ident]) -> @ast::Path { pub fn mk_raw_path_global(sp: span, idents: ~[ast::ident]) -> @ast::Path {
mk_raw_path_global_(sp, idents, ~[]) mk_raw_path_global_(sp, idents, None, ~[])
} }
pub fn mk_raw_path_global_(sp: span, pub fn mk_raw_path_global_(sp: span,
idents: ~[ast::ident], idents: ~[ast::ident],
rp: Option<@ast::Lifetime>,
types: ~[@ast::Ty]) -> @ast::Path { types: ~[@ast::Ty]) -> @ast::Path {
@ast::Path { span: sp, @ast::Path { span: sp,
global: true, global: true,
idents: idents, idents: idents,
rp: None, rp: rp,
types: types } types: types }
} }
pub fn mk_path_raw(cx: @ext_ctxt, sp: span, path: @ast::Path)-> @ast::expr {
mk_expr(cx, sp, ast::expr_path(path))
}
pub fn mk_path(cx: @ext_ctxt, sp: span, idents: ~[ast::ident]) pub fn mk_path(cx: @ext_ctxt, sp: span, idents: ~[ast::ident])
-> @ast::expr { -> @ast::expr {
mk_expr(cx, sp, ast::expr_path(mk_raw_path(sp, idents))) mk_path_raw(cx, sp, mk_raw_path(sp, idents))
} }
pub fn mk_path_global(cx: @ext_ctxt, sp: span, idents: ~[ast::ident]) pub fn mk_path_global(cx: @ext_ctxt, sp: span, idents: ~[ast::ident])
-> @ast::expr { -> @ast::expr {
mk_expr(cx, sp, ast::expr_path(mk_raw_path_global(sp, idents))) mk_path_raw(cx, sp, mk_raw_path_global(sp, idents))
} }
pub fn mk_access_(cx: @ext_ctxt, sp: span, p: @ast::expr, m: ast::ident) pub fn mk_access_(cx: @ext_ctxt, sp: span, p: @ast::expr, m: ast::ident)
-> @ast::expr { -> @ast::expr {
@ -354,44 +363,69 @@ pub fn mk_stmt(cx: @ext_ctxt, span: span, expr: @ast::expr) -> @ast::stmt {
let stmt_ = ast::stmt_semi(expr, cx.next_id()); let stmt_ = ast::stmt_semi(expr, cx.next_id());
@codemap::spanned { node: stmt_, span: span } @codemap::spanned { node: stmt_, span: span }
} }
pub fn mk_ty_mt(ty: @ast::Ty, mutbl: ast::mutability) -> ast::mt {
ast::mt {
ty: ty,
mutbl: mutbl
}
}
pub fn mk_ty(cx: @ext_ctxt,
span: span,
ty: ast::ty_) -> @ast::Ty {
@ast::Ty {
id: cx.next_id(),
span: span,
node: ty
}
}
pub fn mk_ty_path(cx: @ext_ctxt, pub fn mk_ty_path(cx: @ext_ctxt,
span: span, span: span,
idents: ~[ ast::ident ]) idents: ~[ ast::ident ])
-> @ast::Ty { -> @ast::Ty {
let ty = build::mk_raw_path(span, idents); let ty = build::mk_raw_path(span, idents);
let ty = ast::ty_path(ty, cx.next_id()); mk_ty_path_path(cx, span, ty)
let ty = @ast::Ty { id: cx.next_id(), node: ty, span: span };
ty
} }
pub fn mk_ty_path_global(cx: @ext_ctxt, pub fn mk_ty_path_global(cx: @ext_ctxt,
span: span, span: span,
idents: ~[ ast::ident ]) idents: ~[ ast::ident ])
-> @ast::Ty { -> @ast::Ty {
let ty = build::mk_raw_path_global(span, idents); let ty = build::mk_raw_path_global(span, idents);
let ty = ast::ty_path(ty, cx.next_id()); mk_ty_path_path(cx, span, ty)
let ty = @ast::Ty { id: cx.next_id(), node: ty, span: span };
ty
} }
pub fn mk_ty_path_path(cx: @ext_ctxt,
span: span,
path: @ast::Path)
-> @ast::Ty {
let ty = ast::ty_path(path, cx.next_id());
mk_ty(cx, span, ty)
}
pub fn mk_ty_rptr(cx: @ext_ctxt, pub fn mk_ty_rptr(cx: @ext_ctxt,
span: span, span: span,
ty: @ast::Ty, ty: @ast::Ty,
lifetime: Option<@ast::Lifetime>,
mutbl: ast::mutability) mutbl: ast::mutability)
-> @ast::Ty { -> @ast::Ty {
@ast::Ty { mk_ty(cx, span,
id: cx.next_id(), ast::ty_rptr(lifetime, mk_ty_mt(ty, mutbl)))
span: span,
node: ast::ty_rptr(
None,
ast::mt { ty: ty, mutbl: mutbl }
),
}
} }
pub fn mk_ty_uniq(cx: @ext_ctxt, span: span, ty: @ast::Ty) -> @ast::Ty {
mk_ty(cx, span, ast::ty_uniq(mk_ty_mt(ty, ast::m_imm)))
}
pub fn mk_ty_box(cx: @ext_ctxt, span: span,
ty: @ast::Ty, mutbl: ast::mutability) -> @ast::Ty {
mk_ty(cx, span, ast::ty_box(mk_ty_mt(ty, mutbl)))
}
pub fn mk_ty_infer(cx: @ext_ctxt, span: span) -> @ast::Ty { pub fn mk_ty_infer(cx: @ext_ctxt, span: span) -> @ast::Ty {
@ast::Ty { mk_ty(cx, span, ast::ty_infer)
id: cx.next_id(),
node: ast::ty_infer,
span: span,
}
} }
pub fn mk_trait_ref_global(cx: @ext_ctxt, pub fn mk_trait_ref_global(cx: @ext_ctxt,
span: span, span: span,

View file

@ -13,7 +13,6 @@ use codemap::span;
use ext::base::ext_ctxt; use ext::base::ext_ctxt;
use ext::build; use ext::build;
use ext::deriving::generic::*; use ext::deriving::generic::*;
use core::option::{None,Some};
pub fn expand_deriving_clone(cx: @ext_ctxt, pub fn expand_deriving_clone(cx: @ext_ctxt,
@ -22,13 +21,16 @@ pub fn expand_deriving_clone(cx: @ext_ctxt,
in_items: ~[@item]) in_items: ~[@item])
-> ~[@item] { -> ~[@item] {
let trait_def = TraitDef { let trait_def = TraitDef {
path: ~[~"core", ~"clone", ~"Clone"], path: Path::new(~[~"core", ~"clone", ~"Clone"]),
additional_bounds: ~[], additional_bounds: ~[],
generics: LifetimeBounds::empty(),
methods: ~[ methods: ~[
MethodDef { MethodDef {
name: ~"clone", name: ~"clone",
nargs: 0, generics: LifetimeBounds::empty(),
output_type: None, // return Self self_ty: borrowed_explicit_self(),
args: ~[],
ret_ty: Self,
const_nonmatching: false, const_nonmatching: false,
combine_substructure: cs_clone combine_substructure: cs_clone
} }
@ -66,7 +68,8 @@ fn cs_clone(cx: @ext_ctxt, span: span,
ctor_ident = ~[ variant.node.name ]; ctor_ident = ~[ variant.node.name ];
all_fields = af; all_fields = af;
}, },
EnumNonMatching(*) => cx.bug("Non-matching enum variants in `deriving(Clone)`") EnumNonMatching(*) => cx.span_bug(span, "Non-matching enum variants in `deriving(Clone)`"),
StaticEnum(*) | StaticStruct(*) => cx.span_bug(span, "Static method in `deriving(Clone)`")
} }
match all_fields { match all_fields {

View file

@ -8,15 +8,12 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
use ast::{meta_item, item, expr}; use ast::{meta_item, item, expr};
use codemap::span; use codemap::span;
use ext::base::ext_ctxt; use ext::base::ext_ctxt;
use ext::build; use ext::build;
use ext::deriving::generic::*; use ext::deriving::generic::*;
use core::option::Some;
pub fn expand_deriving_eq(cx: @ext_ctxt, pub fn expand_deriving_eq(cx: @ext_ctxt,
span: span, span: span,
mitem: @meta_item, mitem: @meta_item,
@ -24,28 +21,32 @@ pub fn expand_deriving_eq(cx: @ext_ctxt,
// structures are equal if all fields are equal, and non equal, if // structures are equal if all fields are equal, and non equal, if
// any fields are not equal or if the enum variants are different // any fields are not equal or if the enum variants are different
fn cs_eq(cx: @ext_ctxt, span: span, substr: &Substructure) -> @expr { fn cs_eq(cx: @ext_ctxt, span: span, substr: &Substructure) -> @expr {
cs_and(|cx, span, _| build::mk_bool(cx, span, false), cs_and(|cx, span, _, _| build::mk_bool(cx, span, false),
cx, span, substr) cx, span, substr)
} }
fn cs_ne(cx: @ext_ctxt, span: span, substr: &Substructure) -> @expr { fn cs_ne(cx: @ext_ctxt, span: span, substr: &Substructure) -> @expr {
cs_or(|cx, span, _| build::mk_bool(cx, span, true), cs_or(|cx, span, _, _| build::mk_bool(cx, span, true),
cx, span, substr) cx, span, substr)
} }
macro_rules! md ( macro_rules! md (
($name:expr, $f:ident) => { ($name:expr, $f:ident) => {
MethodDef { MethodDef {
name: $name, name: $name,
output_type: Some(~[~"bool"]), generics: LifetimeBounds::empty(),
nargs: 1, self_ty: borrowed_explicit_self(),
args: ~[borrowed_self()],
ret_ty: Literal(Path::new(~[~"bool"])),
const_nonmatching: true, const_nonmatching: true,
combine_substructure: $f combine_substructure: $f
}, },
} }
) );
let trait_def = TraitDef { let trait_def = TraitDef {
path: ~[~"core", ~"cmp", ~"Eq"], path: Path::new(~[~"core", ~"cmp", ~"Eq"]),
additional_bounds: ~[], additional_bounds: ~[],
generics: LifetimeBounds::empty(),
methods: ~[ methods: ~[
md!(~"eq", cs_eq), md!(~"eq", cs_eq),
md!(~"ne", cs_ne) md!(~"ne", cs_ne)

View file

@ -14,29 +14,33 @@ use codemap::span;
use ext::base::ext_ctxt; use ext::base::ext_ctxt;
use ext::build; use ext::build;
use ext::deriving::generic::*; use ext::deriving::generic::*;
use core::option::Some;
macro_rules! md {
($name:expr, $less:expr, $equal:expr) => {
MethodDef {
name: $name,
output_type: Some(~[~"bool"]),
nargs: 1,
const_nonmatching: false,
combine_substructure: |cx, span, substr|
cs_ord($less, $equal, cx, span, substr)
}
}
}
pub fn expand_deriving_ord(cx: @ext_ctxt, pub fn expand_deriving_ord(cx: @ext_ctxt,
span: span, span: span,
mitem: @meta_item, mitem: @meta_item,
in_items: ~[@item]) -> ~[@item] { in_items: ~[@item]) -> ~[@item] {
macro_rules! md (
($name:expr, $less:expr, $equal:expr) => {
MethodDef {
name: $name,
generics: LifetimeBounds::empty(),
self_ty: borrowed_explicit_self(),
args: ~[borrowed_self()],
ret_ty: Literal(Path::new(~[~"bool"])),
const_nonmatching: false,
combine_substructure: |cx, span, substr|
cs_ord($less, $equal, cx, span, substr)
}
}
);
let trait_def = TraitDef { let trait_def = TraitDef {
path: ~[~"core", ~"cmp", ~"Ord"], path: Path::new(~[~"core", ~"cmp", ~"Ord"]),
// XXX: Ord doesn't imply Eq yet // XXX: Ord doesn't imply Eq yet
additional_bounds: ~[~[~"core", ~"cmp", ~"Eq"]], additional_bounds: ~[Literal(Path::new(~[~"core", ~"cmp", ~"Eq"]))],
generics: LifetimeBounds::empty(),
methods: ~[ methods: ~[
md!(~"lt", true, false), md!(~"lt", true, false),
md!(~"le", true, true), md!(~"le", true, true),
@ -97,19 +101,19 @@ fn cs_ord(less: bool, equal: bool,
} }
let cmp = build::mk_method_call(cx, span, let cmp = build::mk_method_call(cx, span,
self_f, cx.ident_of(~"eq"), other_fs); self_f, cx.ident_of(~"eq"), other_fs.to_owned());
let subexpr = build::mk_simple_block(cx, span, subexpr); let subexpr = build::mk_simple_block(cx, span, subexpr);
let elseif = expr_if(cmp, subexpr, Some(false_blk_expr)); let elseif = expr_if(cmp, subexpr, Some(false_blk_expr));
let elseif = build::mk_expr(cx, span, elseif); let elseif = build::mk_expr(cx, span, elseif);
let cmp = build::mk_method_call(cx, span, let cmp = build::mk_method_call(cx, span,
self_f, binop, other_fs); self_f, binop, other_fs.to_owned());
let if_ = expr_if(cmp, true_blk, Some(elseif)); let if_ = expr_if(cmp, true_blk, Some(elseif));
build::mk_expr(cx, span, if_) build::mk_expr(cx, span, if_)
}, },
base, base,
|cx, span, args| { |cx, span, args, _| {
// nonmatching enums, order by the order the variants are // nonmatching enums, order by the order the variants are
// written // written
match args { match args {

View file

@ -15,26 +15,27 @@ use ext::base::ext_ctxt;
use ext::build; use ext::build;
use ext::deriving::generic::*; use ext::deriving::generic::*;
use core::option::Some;
pub fn expand_deriving_totaleq(cx: @ext_ctxt, pub fn expand_deriving_totaleq(cx: @ext_ctxt,
span: span, span: span,
mitem: @meta_item, mitem: @meta_item,
in_items: ~[@item]) -> ~[@item] { in_items: ~[@item]) -> ~[@item] {
fn cs_equals(cx: @ext_ctxt, span: span, substr: &Substructure) -> @expr { fn cs_equals(cx: @ext_ctxt, span: span, substr: &Substructure) -> @expr {
cs_and(|cx, span, _| build::mk_bool(cx, span, false), cs_and(|cx, span, _, _| build::mk_bool(cx, span, false),
cx, span, substr) cx, span, substr)
} }
let trait_def = TraitDef { let trait_def = TraitDef {
path: ~[~"core", ~"cmp", ~"TotalEq"], path: Path::new(~[~"core", ~"cmp", ~"TotalEq"]),
additional_bounds: ~[], additional_bounds: ~[],
generics: LifetimeBounds::empty(),
methods: ~[ methods: ~[
MethodDef { MethodDef {
name: ~"equals", name: ~"equals",
output_type: Some(~[~"bool"]), generics: LifetimeBounds::empty(),
nargs: 1, self_ty: borrowed_explicit_self(),
args: ~[borrowed_self()],
ret_ty: Literal(Path::new(~[~"bool"])),
const_nonmatching: true, const_nonmatching: true,
combine_substructure: cs_equals combine_substructure: cs_equals
} }

View file

@ -14,20 +14,22 @@ use ext::base::ext_ctxt;
use ext::build; use ext::build;
use ext::deriving::generic::*; use ext::deriving::generic::*;
use core::cmp::{Ordering, Equal, Less, Greater}; use core::cmp::{Ordering, Equal, Less, Greater};
use core::option::Some;
pub fn expand_deriving_totalord(cx: @ext_ctxt, pub fn expand_deriving_totalord(cx: @ext_ctxt,
span: span, span: span,
mitem: @meta_item, mitem: @meta_item,
in_items: ~[@item]) -> ~[@item] { in_items: ~[@item]) -> ~[@item] {
let trait_def = TraitDef { let trait_def = TraitDef {
path: ~[~"core", ~"cmp", ~"TotalOrd"], path: Path::new(~[~"core", ~"cmp", ~"TotalOrd"]),
additional_bounds: ~[], additional_bounds: ~[],
generics: LifetimeBounds::empty(),
methods: ~[ methods: ~[
MethodDef { MethodDef {
name: ~"cmp", name: ~"cmp",
output_type: Some(~[~"core", ~"cmp", ~"Ordering"]), generics: LifetimeBounds::empty(),
nargs: 1, self_ty: borrowed_explicit_self(),
args: ~[borrowed_self()],
ret_ty: Literal(Path::new(~[~"core", ~"cmp", ~"Ordering"])),
const_nonmatching: false, const_nonmatching: false,
combine_substructure: cs_cmp combine_substructure: cs_cmp
} }
@ -64,7 +66,7 @@ pub fn cs_cmp(cx: @ext_ctxt, span: span,
build::mk_call_global(cx, span, lexical_ord, ~[old, new]) build::mk_call_global(cx, span, lexical_ord, ~[old, new])
}, },
ordering_const(cx, span, Equal), ordering_const(cx, span, Equal),
|cx, span, list| { |cx, span, list, _| {
match list { match list {
// an earlier nonmatching variant is Less than a // an earlier nonmatching variant is Less than a
// later one // later one

View file

@ -66,6 +66,7 @@ fn create_derived_decodable_impl(
cx.ident_of(~"serialize"), cx.ident_of(~"serialize"),
cx.ident_of(~"Decodable") cx.ident_of(~"Decodable")
], ],
None,
~[ ~[
build::mk_simple_ty_path(cx, span, cx.ident_of(~"__D")) build::mk_simple_ty_path(cx, span, cx.ident_of(~"__D"))
] ]
@ -77,7 +78,7 @@ fn create_derived_decodable_impl(
generics, generics,
methods, methods,
trait_path, trait_path,
generic_ty_params, Generics { ty_params: generic_ty_params, lifetimes: opt_vec::Empty },
opt_vec::Empty opt_vec::Empty
) )
} }
@ -96,6 +97,7 @@ fn create_decode_method(
cx, cx,
span, span,
build::mk_simple_ty_path(cx, span, cx.ident_of(~"__D")), build::mk_simple_ty_path(cx, span, cx.ident_of(~"__D")),
None,
ast::m_mutbl ast::m_mutbl
); );
let d_ident = cx.ident_of(~"__d"); let d_ident = cx.ident_of(~"__d");

View file

@ -66,6 +66,7 @@ fn create_derived_encodable_impl(
cx.ident_of(~"serialize"), cx.ident_of(~"serialize"),
cx.ident_of(~"Encodable") cx.ident_of(~"Encodable")
], ],
None,
~[ ~[
build::mk_simple_ty_path(cx, span, cx.ident_of(~"__E")) build::mk_simple_ty_path(cx, span, cx.ident_of(~"__E"))
] ]
@ -77,7 +78,7 @@ fn create_derived_encodable_impl(
generics, generics,
methods, methods,
trait_path, trait_path,
generic_ty_params, Generics { ty_params: generic_ty_params, lifetimes: opt_vec::Empty },
opt_vec::Empty opt_vec::Empty
) )
} }
@ -94,6 +95,7 @@ fn create_encode_method(
cx, cx,
span, span,
build::mk_simple_ty_path(cx, span, cx.ident_of(~"__E")), build::mk_simple_ty_path(cx, span, cx.ident_of(~"__E")),
None,
ast::m_mutbl ast::m_mutbl
); );
let e_arg = build::mk_arg(cx, span, cx.ident_of(~"__e"), e_arg_type); let e_arg = build::mk_arg(cx, span, cx.ident_of(~"__e"), e_arg_type);
@ -303,7 +305,7 @@ fn expand_deriving_encodable_enum_method(
// Create the arms of the match in the method body. // Create the arms of the match in the method body.
let arms = do enum_definition.variants.mapi |i, variant| { let arms = do enum_definition.variants.mapi |i, variant| {
// Create the matching pattern. // Create the matching pattern.
let pat = create_enum_variant_pattern(cx, span, variant, ~"__self"); let (pat, fields) = create_enum_variant_pattern(cx, span, variant, ~"__self", ast::m_imm);
// Feed the discriminant to the encode function. // Feed the discriminant to the encode function.
let mut stmts = ~[]; let mut stmts = ~[];
@ -311,11 +313,7 @@ fn expand_deriving_encodable_enum_method(
// Feed each argument in this variant to the encode function // Feed each argument in this variant to the encode function
// as well. // as well.
let variant_arg_len = variant_arg_count(cx, span, variant); let variant_arg_len = variant_arg_count(cx, span, variant);
for uint::range(0, variant_arg_len) |j| { for fields.eachi |j, &(_, field)| {
// 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. // Call the substructure method.
let expr = call_substructure_encode_method(cx, span, field); let expr = call_substructure_encode_method(cx, span, field);

File diff suppressed because it is too large Load diff

View file

@ -12,14 +12,7 @@
/// #[deriving(IterBytes)] extensions. /// #[deriving(IterBytes)] extensions.
use ast; use ast;
use ast::{Ty, bind_by_ref, deref, enum_def}; use ast::{Ty, enum_def, expr, ident, item, Generics, meta_item, struct_def};
use ast::{expr, expr_match, ident, item, item_};
use ast::{item_enum, item_impl, item_struct, Generics};
use ast::{m_imm, meta_item, method};
use ast::{named_field, pat, pat_ident, public};
use ast::{struct_def, struct_variant_kind};
use ast::{tuple_variant_kind};
use ast::{ty_path, unnamed_field, variant};
use ext::base::ext_ctxt; use ext::base::ext_ctxt;
use ext::build; use ext::build;
use codemap::{span, respan}; use codemap::{span, respan};
@ -78,23 +71,21 @@ pub fn expand_meta_deriving(cx: @ext_ctxt,
meta_name_value(tname, _) | meta_name_value(tname, _) |
meta_list(tname, _) | meta_list(tname, _) |
meta_word(tname) => { meta_word(tname) => {
macro_rules! expand(($func:path) => ($func(cx, titem.span,
titem, in_items)));
match *tname { match *tname {
~"Clone" => clone::expand_deriving_clone(cx, ~"Clone" => expand!(clone::expand_deriving_clone),
titem.span, titem, in_items),
~"IterBytes" => iter_bytes::expand_deriving_iter_bytes(cx, ~"IterBytes" => expand!(iter_bytes::expand_deriving_iter_bytes),
titem.span, titem, in_items),
~"Encodable" => encodable::expand_deriving_encodable(cx, ~"Encodable" => expand!(encodable::expand_deriving_encodable),
titem.span, titem, in_items), ~"Decodable" => expand!(decodable::expand_deriving_decodable),
~"Decodable" => decodable::expand_deriving_decodable(cx,
titem.span, titem, in_items), ~"Eq" => expand!(eq::expand_deriving_eq),
~"Eq" => eq::expand_deriving_eq(cx, titem.span, ~"TotalEq" => expand!(totaleq::expand_deriving_totaleq),
titem, in_items), ~"Ord" => expand!(ord::expand_deriving_ord),
~"TotalEq" => totaleq::expand_deriving_totaleq(cx, titem.span, ~"TotalOrd" => expand!(totalord::expand_deriving_totalord),
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 => { tname => {
cx.span_err(titem.span, fmt!("unknown \ cx.span_err(titem.span, fmt!("unknown \
`deriving` trait: `%s`", tname)); `deriving` trait: `%s`", tname));
@ -118,14 +109,14 @@ pub fn expand_deriving(cx: @ext_ctxt,
for in_items.each |item| { for in_items.each |item| {
result.push(copy *item); result.push(copy *item);
match item.node { match item.node {
item_struct(struct_def, ref generics) => { ast::item_struct(struct_def, ref generics) => {
result.push(expand_deriving_struct_def(cx, result.push(expand_deriving_struct_def(cx,
span, span,
struct_def, struct_def,
item.ident, item.ident,
generics)); generics));
} }
item_enum(ref enum_definition, ref generics) => { ast::item_enum(ref enum_definition, ref generics) => {
result.push(expand_deriving_enum_def(cx, result.push(expand_deriving_enum_def(cx,
span, span,
enum_definition, enum_definition,
@ -138,7 +129,7 @@ pub fn expand_deriving(cx: @ext_ctxt,
result result
} }
fn create_impl_item(cx: @ext_ctxt, span: span, item: item_) -> @item { fn create_impl_item(cx: @ext_ctxt, span: span, item: ast::item_) -> @item {
let doc_attr = respan(span, let doc_attr = respan(span,
ast::lit_str(@~"Automatically derived.")); ast::lit_str(@~"Automatically derived."));
let doc_attr = respan(span, ast::meta_name_value(@~"doc", doc_attr)); let doc_attr = respan(span, ast::meta_name_value(@~"doc", doc_attr));
@ -154,7 +145,7 @@ fn create_impl_item(cx: @ext_ctxt, span: span, item: item_) -> @item {
attrs: ~[doc_attr], attrs: ~[doc_attr],
id: cx.next_id(), id: cx.next_id(),
node: item, node: item,
vis: public, vis: ast::public,
span: span, span: span,
} }
} }
@ -173,22 +164,29 @@ pub fn create_self_type_with_params(cx: @ext_ctxt,
self_ty_params.push(self_ty_param); self_ty_params.push(self_ty_param);
} }
let lifetime = if generics.lifetimes.is_empty() {
None
} else {
Some(@*generics.lifetimes.get(0))
};
// Create the type of `self`. // Create the type of `self`.
let self_type = build::mk_raw_path_(span, let self_type = build::mk_raw_path_(span,
~[ type_ident ], ~[ type_ident ],
lifetime,
self_ty_params); self_ty_params);
let self_type = ty_path(self_type, cx.next_id()); build::mk_ty_path_path(cx, span, self_type)
@ast::Ty { id: cx.next_id(), node: self_type, span: span }
} }
pub fn create_derived_impl(cx: @ext_ctxt, pub fn create_derived_impl(cx: @ext_ctxt,
span: span, span: span,
type_ident: ident, type_ident: ident,
generics: &Generics, generics: &Generics,
methods: &[@method], methods: &[@ast::method],
trait_path: @ast::Path, trait_path: @ast::Path,
mut impl_ty_params: opt_vec::OptVec<ast::TyParam>, mut impl_generics: Generics,
bounds_paths: opt_vec::OptVec<~[ident]>) bounds_paths: opt_vec::OptVec<@ast::Path>)
-> @item { -> @item {
/*! /*!
* *
@ -204,21 +202,22 @@ pub fn create_derived_impl(cx: @ext_ctxt,
*/ */
// Copy the lifetimes // Copy the lifetimes
let impl_lifetimes = generics.lifetimes.map(|l| { for generics.lifetimes.each |l| {
build::mk_lifetime(cx, l.span, l.ident) impl_generics.lifetimes.push(copy *l)
}); };
// Create the type parameters. // Create the type parameters.
for generics.ty_params.each |ty_param| { for generics.ty_params.each |ty_param| {
// extra restrictions on the generics parameters to the type being derived upon
let mut bounds = do bounds_paths.map |&bound_path| { let mut bounds = do bounds_paths.map |&bound_path| {
build::mk_trait_ty_param_bound_global(cx, span, bound_path) build::mk_trait_ty_param_bound_(cx, bound_path)
}; };
let this_trait_bound = let this_trait_bound =
build::mk_trait_ty_param_bound_(cx, trait_path); build::mk_trait_ty_param_bound_(cx, trait_path);
bounds.push(this_trait_bound); bounds.push(this_trait_bound);
impl_ty_params.push(build::mk_ty_param(cx, ty_param.ident, @bounds)); impl_generics.ty_params.push(build::mk_ty_param(cx, ty_param.ident, @bounds));
} }
// Create the reference to the trait. // Create the reference to the trait.
@ -231,8 +230,7 @@ pub fn create_derived_impl(cx: @ext_ctxt,
generics); generics);
// Create the impl item. // Create the impl item.
let impl_item = item_impl(Generics {lifetimes: impl_lifetimes, let impl_item = ast::item_impl(impl_generics,
ty_params: impl_ty_params},
Some(trait_ref), Some(trait_ref),
self_type, self_type,
methods.map(|x| *x)); methods.map(|x| *x));
@ -240,106 +238,128 @@ pub fn create_derived_impl(cx: @ext_ctxt,
} }
pub fn create_subpatterns(cx: @ext_ctxt, pub fn create_subpatterns(cx: @ext_ctxt,
span: span, span: span,
prefix: ~str, field_paths: ~[@ast::Path],
n: uint) mutbl: ast::mutability)
-> ~[@pat] { -> ~[@ast::pat] {
let mut subpats = ~[]; do field_paths.map |&path| {
for uint::range(0, n) |_i| { build::mk_pat(cx, span,
// Create the subidentifier. ast::pat_ident(ast::bind_by_ref(mutbl), path, None))
let index = subpats.len();
let ident = cx.ident_of(fmt!("%s_%u", prefix, index));
// Create the subpattern.
let subpath = build::mk_raw_path(span, ~[ ident ]);
let subpat = pat_ident(bind_by_ref(m_imm), subpath, None);
let subpat = build::mk_pat(cx, span, subpat);
subpats.push(subpat);
} }
return subpats;
} }
pub fn is_struct_tuple(struct_def: &struct_def) -> bool { #[deriving(Eq)] // dogfooding!
struct_def.fields.len() > 0 && struct_def.fields.all(|f| { enum StructType {
match f.node.kind { Unknown, Record, Tuple
named_field(*) => false, }
unnamed_field => true
} pub fn create_struct_pattern(cx: @ext_ctxt,
}) span: span,
struct_ident: ident,
struct_def: &struct_def,
prefix: ~str,
mutbl: ast::mutability)
-> (@ast::pat, ~[(Option<ident>, @expr)]) {
if struct_def.fields.is_empty() {
return (
build::mk_pat_ident_with_binding_mode(
cx, span, struct_ident, ast::bind_infer),
~[]);
}
let matching_path = build::mk_raw_path(span, ~[ struct_ident ]);
let mut paths = ~[], ident_expr = ~[];
let mut struct_type = Unknown;
for struct_def.fields.eachi |i, struct_field| {
let opt_id = match struct_field.node.kind {
ast::named_field(ident, _, _) if (struct_type == Unknown ||
struct_type == Record) => {
struct_type = Record;
Some(ident)
}
ast::unnamed_field if (struct_type == Unknown ||
struct_type == Tuple) => {
struct_type = Tuple;
None
}
_ => {
cx.span_bug(span, "A struct with named and unnamed fields in `deriving`");
}
};
let path = build::mk_raw_path(span,
~[ cx.ident_of(fmt!("%s_%u", prefix, i)) ]);
paths.push(path);
ident_expr.push((opt_id, build::mk_path_raw(cx, span, path)));
}
let subpats = create_subpatterns(cx, span, paths, mutbl);
// struct_type is definitely not Unknown, since struct_def.fields
// must be nonempty to reach here
let pattern = if struct_type == Record {
let field_pats = do vec::build |push| {
for vec::each2(subpats, ident_expr) |&pat, &(id, _)| {
// id is guaranteed to be Some
push(ast::field_pat { ident: id.get(), pat: pat })
}
};
build::mk_pat_struct(cx, span, matching_path, field_pats)
} else {
build::mk_pat_enum(cx, span, matching_path, subpats)
};
(pattern, ident_expr)
} }
pub fn create_enum_variant_pattern(cx: @ext_ctxt, pub fn create_enum_variant_pattern(cx: @ext_ctxt,
span: span, span: span,
variant: &variant, variant: &ast::variant,
prefix: ~str) prefix: ~str,
-> @pat { mutbl: ast::mutability)
-> (@ast::pat, ~[(Option<ident>, @expr)]) {
let variant_ident = variant.node.name; let variant_ident = variant.node.name;
match variant.node.kind { match variant.node.kind {
tuple_variant_kind(ref variant_args) => { ast::tuple_variant_kind(ref variant_args) => {
if variant_args.len() == 0 { if variant_args.is_empty() {
return build::mk_pat_ident_with_binding_mode( return (build::mk_pat_ident_with_binding_mode(
cx, span, variant_ident, ast::bind_infer); cx, span, variant_ident, ast::bind_infer), ~[]);
} }
let matching_path = build::mk_raw_path(span, ~[ variant_ident ]); let matching_path = build::mk_raw_path(span, ~[ variant_ident ]);
let subpats = create_subpatterns(cx,
span,
prefix,
variant_args.len());
return build::mk_pat_enum(cx, span, matching_path, subpats); let mut paths = ~[], ident_expr = ~[];
} for uint::range(0, variant_args.len()) |i| {
struct_variant_kind(struct_def) => { let path = build::mk_raw_path(span,
let matching_path = build::mk_raw_path(span, ~[ variant_ident ]); ~[ cx.ident_of(fmt!("%s_%u", prefix, i)) ]);
let subpats = create_subpatterns(cx,
span,
prefix,
struct_def.fields.len());
let field_pats = do struct_def.fields.mapi |i, struct_field| { paths.push(path);
let ident = match struct_field.node.kind { ident_expr.push((None, build::mk_path_raw(cx, span, path)));
named_field(ident, _, _) => ident,
unnamed_field => {
cx.span_bug(span, ~"unexpected unnamed field");
}
};
ast::field_pat { ident: ident, pat: subpats[i] }
};
build::mk_pat_struct(cx, span, matching_path, field_pats)
}
}
}
pub fn variant_arg_count(_cx: @ext_ctxt, _span: span, variant: &variant) -> uint {
match variant.node.kind {
tuple_variant_kind(ref args) => args.len(),
struct_variant_kind(ref struct_def) => struct_def.fields.len(),
}
}
/// Iterate through the idents of the variant arguments. The field is
/// unnamed (i.e. it's not a struct-like enum), then `None`.
pub fn each_variant_arg_ident(_cx: @ext_ctxt, _span: span,
variant: &variant, it: &fn(uint, Option<ident>) -> bool) {
match variant.node.kind {
tuple_variant_kind(ref args) => {
for uint::range(0, args.len()) |i| {
if !it(i, None) { break }
} }
let subpats = create_subpatterns(cx, span, paths, mutbl);
(build::mk_pat_enum(cx, span, matching_path, subpats),
ident_expr)
} }
struct_variant_kind(ref struct_def) => { ast::struct_variant_kind(struct_def) => {
for struct_def.fields.eachi |i, f| { create_struct_pattern(cx, span,
let id = match f.node.kind { variant_ident, struct_def,
named_field(ident, _, _) => Some(ident), prefix,
unnamed_field => None mutbl)
};
if !it(i, id) { break }
}
} }
} }
} }
pub fn variant_arg_count(_cx: @ext_ctxt, _span: span, variant: &ast::variant) -> uint {
match variant.node.kind {
ast::tuple_variant_kind(ref args) => args.len(),
ast::struct_variant_kind(ref struct_def) => struct_def.fields.len(),
}
}
pub fn expand_enum_or_struct_match(cx: @ext_ctxt, pub fn expand_enum_or_struct_match(cx: @ext_ctxt,
span: span, span: span,
@ -347,7 +367,7 @@ pub fn expand_enum_or_struct_match(cx: @ext_ctxt,
-> @expr { -> @expr {
let self_ident = cx.ident_of(~"self"); let self_ident = cx.ident_of(~"self");
let self_expr = build::mk_path(cx, span, ~[ self_ident ]); let self_expr = build::mk_path(cx, span, ~[ self_ident ]);
let self_expr = build::mk_unary(cx, span, deref, self_expr); let self_expr = build::mk_unary(cx, span, ast::deref, self_expr);
let self_match_expr = expr_match(self_expr, arms); let self_match_expr = ast::expr_match(self_expr, arms);
build::mk_expr(cx, span, self_match_expr) build::mk_expr(cx, span, self_match_expr)
} }

View file

@ -0,0 +1,242 @@
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <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.
/*!
A mini version of ast::Ty, which is easier to use, and features an
explicit `Self` type to use when specifying impls to be derived.
*/
use ast;
use ast::{expr,Generics,ident};
use ext::base::ext_ctxt;
use ext::build;
use codemap::{span,respan};
use opt_vec;
/// The types of pointers
#[deriving(Eq)]
pub enum PtrTy {
Owned, // ~
Managed(ast::mutability), // @[mut]
Borrowed(Option<~str>, ast::mutability), // &['lifetime] [mut]
}
/// A path, e.g. `::core::option::Option::<int>` (global). Has support
/// for type parameters and a lifetime.
#[deriving(Eq)]
pub struct Path {
path: ~[~str],
lifetime: Option<~str>,
params: ~[~Ty],
global: bool
}
pub impl Path {
fn new(path: ~[~str]) -> Path {
Path::new_(path, None, ~[], true)
}
fn new_local(path: ~str) -> Path {
Path::new_(~[ path ], None, ~[], false)
}
fn new_(path: ~[~str], lifetime: Option<~str>, params: ~[~Ty], global: bool) -> Path {
Path {
path: path,
lifetime: lifetime,
params: params,
global: global
}
}
fn to_ty(&self, cx: @ext_ctxt, span: span,
self_ty: ident, self_generics: &Generics) -> @ast::Ty {
build::mk_ty_path_path(cx, span,
self.to_path(cx, span,
self_ty, self_generics))
}
fn to_path(&self, cx: @ext_ctxt, span: span,
self_ty: ident, self_generics: &Generics) -> @ast::Path {
let idents = self.path.map(|s| cx.ident_of(*s) );
let lt = mk_lifetime(cx, span, self.lifetime);
let tys = self.params.map(|t| t.to_ty(cx, span, self_ty, self_generics));
if self.global {
build::mk_raw_path_global_(span, idents, lt, tys)
} else {
build::mk_raw_path_(span, idents, lt, tys)
}
}
}
/// A type. Supports pointers (except for *), Self, and literals
#[deriving(Eq)]
pub enum Ty {
Self,
// &/~/@ Ty
Ptr(~Ty, PtrTy),
// mod::mod::Type<[lifetime], [Params...]>, including a plain type
// parameter, and things like `int`
Literal(Path),
// includes nil
Tuple(~[Ty])
}
pub fn borrowed_ptrty() -> PtrTy {
Borrowed(None, ast::m_imm)
}
pub fn borrowed(ty: ~Ty) -> Ty {
Ptr(ty, borrowed_ptrty())
}
pub fn borrowed_explicit_self() -> Option<Option<PtrTy>> {
Some(Some(borrowed_ptrty()))
}
pub fn borrowed_self() -> Ty {
borrowed(~Self)
}
pub fn nil_ty() -> Ty {
Tuple(~[])
}
fn mk_lifetime(cx: @ext_ctxt, span: span, lt: Option<~str>) -> Option<@ast::Lifetime> {
match lt {
Some(s) => Some(@build::mk_lifetime(cx, span, cx.ident_of(s))),
None => None
}
}
pub impl Ty {
fn to_ty(&self, cx: @ext_ctxt, span: span,
self_ty: ident, self_generics: &Generics) -> @ast::Ty {
match *self {
Ptr(ref ty, ref ptr) => {
let raw_ty = ty.to_ty(cx, span, self_ty, self_generics);
match *ptr {
Owned => {
build::mk_ty_uniq(cx, span, raw_ty)
}
Managed(copy mutbl) => {
build::mk_ty_box(cx, span, raw_ty, mutbl)
}
Borrowed(copy lt, copy mutbl) => {
let lt = mk_lifetime(cx, span, lt);
build::mk_ty_rptr(cx, span, raw_ty, lt, mutbl)
}
}
}
Literal(ref p) => { p.to_ty(cx, span, self_ty, self_generics) }
Self => {
build::mk_ty_path_path(cx, span, self.to_path(cx, span, self_ty, self_generics))
}
Tuple(ref fields) => {
let ty = if fields.is_empty() {
ast::ty_nil
} else {
ast::ty_tup(fields.map(|f| f.to_ty(cx, span, self_ty, self_generics)))
};
build::mk_ty(cx, span, ty)
}
}
}
fn to_path(&self, cx: @ext_ctxt, span: span,
self_ty: ident, self_generics: &Generics) -> @ast::Path {
match *self {
Self => {
let self_params = do self_generics.ty_params.map |ty_param| {
build::mk_ty_path(cx, span, ~[ ty_param.ident ])
};
let lifetime = if self_generics.lifetimes.is_empty() {
None
} else {
Some(@*self_generics.lifetimes.get(0))
};
build::mk_raw_path_(span, ~[self_ty], lifetime,
opt_vec::take_vec(self_params))
}
Literal(ref p) => {
p.to_path(cx, span, self_ty, self_generics)
}
Ptr(*) => { cx.span_bug(span, ~"Pointer in a path in generic `deriving`") }
Tuple(*) => { cx.span_bug(span, ~"Tuple in a path in generic `deriving`") }
}
}
}
fn mk_ty_param(cx: @ext_ctxt, span: span, name: ~str, bounds: ~[Path],
self_ident: ident, self_generics: &Generics) -> ast::TyParam {
let bounds = opt_vec::from(
do bounds.map |b| {
let path = b.to_path(cx, span, self_ident, self_generics);
build::mk_trait_ty_param_bound_(cx, path)
});
build::mk_ty_param(cx, cx.ident_of(name), @bounds)
}
fn mk_generics(lifetimes: ~[ast::Lifetime], ty_params: ~[ast::TyParam]) -> Generics {
Generics {
lifetimes: opt_vec::from(lifetimes),
ty_params: opt_vec::from(ty_params)
}
}
/// Lifetimes and bounds on type paramers
pub struct LifetimeBounds {
lifetimes: ~[~str],
bounds: ~[(~str, ~[Path])]
}
pub impl LifetimeBounds {
fn empty() -> LifetimeBounds {
LifetimeBounds {
lifetimes: ~[], bounds: ~[]
}
}
fn to_generics(&self, cx: @ext_ctxt, span: span,
self_ty: ident, self_generics: &Generics) -> Generics {
let lifetimes = do self.lifetimes.map |&lt| {
build::mk_lifetime(cx, span, cx.ident_of(lt))
};
let ty_params = do self.bounds.map |&(name, bounds)| {
mk_ty_param(cx, span, name, bounds, self_ty, self_generics)
};
mk_generics(lifetimes, ty_params)
}
}
pub fn get_explicit_self(cx: @ext_ctxt, span: span, self_ptr: Option<PtrTy>)
-> (@expr, ast::self_ty) {
let self_path = build::mk_path(cx, span, ~[cx.ident_of(~"self")]);
match self_ptr {
None => {
(self_path, respan(span, ast::sty_value))
}
Some(ptr) => {
let self_ty = respan(
span,
match ptr {
Owned => ast::sty_uniq(ast::m_imm),
Managed(mutbl) => ast::sty_box(mutbl),
Borrowed(lt, mutbl) => {
let lt = lt.map(|s| @build::mk_lifetime(cx, span,
cx.ident_of(*s)));
ast::sty_region(lt, mutbl)
}
});
let self_expr = build::mk_deref(cx, span, self_path);
(self_expr, self_ty)
}
}
}