Rewrite deriving(Decodable, Encodable)
Now it uses the generic deriving code and should in theory work in all cases.
This commit is contained in:
parent
d01c7d0d42
commit
a25c7045c1
@ -14,410 +14,150 @@
|
||||
*/
|
||||
|
||||
use core::prelude::*;
|
||||
|
||||
use ast::*;
|
||||
use ast;
|
||||
use ast_util;
|
||||
use codemap::{span, spanned};
|
||||
use ext::base::ExtCtxt;
|
||||
use ext::build::AstBuilder;
|
||||
use ext::deriving::*;
|
||||
use opt_vec;
|
||||
|
||||
use core::vec;
|
||||
use core::uint;
|
||||
|
||||
pub fn expand_deriving_decodable(
|
||||
cx: @ExtCtxt,
|
||||
use ast::{meta_item, item, expr, m_mutbl};
|
||||
use codemap::span;
|
||||
use ext::base::ExtCtxt;
|
||||
use ext::build::AstBuilder;
|
||||
use ext::deriving::generic::*;
|
||||
|
||||
pub fn expand_deriving_decodable(cx: @ExtCtxt,
|
||||
span: span,
|
||||
_mitem: @meta_item,
|
||||
in_items: ~[@item]
|
||||
) -> ~[@item] {
|
||||
expand_deriving(
|
||||
cx,
|
||||
span,
|
||||
in_items,
|
||||
expand_deriving_decodable_struct_def,
|
||||
expand_deriving_decodable_enum_def
|
||||
)
|
||||
}
|
||||
|
||||
fn create_derived_decodable_impl(
|
||||
cx: @ExtCtxt,
|
||||
span: span,
|
||||
type_ident: ident,
|
||||
generics: &Generics,
|
||||
method: @method
|
||||
) -> @item {
|
||||
let decoder_ty_param = cx.typaram(
|
||||
cx.ident_of("__D"),
|
||||
@opt_vec::with(
|
||||
cx.typarambound(
|
||||
cx.path_global(
|
||||
span,
|
||||
~[
|
||||
cx.ident_of("extra"),
|
||||
cx.ident_of("serialize"),
|
||||
cx.ident_of("Decoder"),
|
||||
]))));
|
||||
|
||||
// All the type parameters need to bound to the trait.
|
||||
let generic_ty_params = opt_vec::with(decoder_ty_param);
|
||||
|
||||
let methods = [method];
|
||||
let trait_path = cx.path_all(
|
||||
span,
|
||||
true,
|
||||
~[
|
||||
cx.ident_of("extra"),
|
||||
cx.ident_of("serialize"),
|
||||
cx.ident_of("Decodable")
|
||||
],
|
||||
None,
|
||||
~[
|
||||
cx.ty_ident(span, cx.ident_of("__D"))
|
||||
mitem: @meta_item,
|
||||
in_items: ~[@item]) -> ~[@item] {
|
||||
let trait_def = TraitDef {
|
||||
path: Path::new_(~["extra", "serialize", "Decodable"], None,
|
||||
~[~Literal(Path::new_local("__D"))], true),
|
||||
additional_bounds: ~[],
|
||||
generics: LifetimeBounds {
|
||||
lifetimes: ~[],
|
||||
bounds: ~[("__D", ~[Path::new(~["extra", "serialize", "Decoder"])])],
|
||||
},
|
||||
methods: ~[
|
||||
MethodDef {
|
||||
name: "decode",
|
||||
generics: LifetimeBounds::empty(),
|
||||
explicit_self: None,
|
||||
args: ~[Ptr(~Literal(Path::new_local("__D")),
|
||||
Borrowed(None, m_mutbl))],
|
||||
ret_ty: Self,
|
||||
const_nonmatching: true,
|
||||
combine_substructure: decodable_substructure,
|
||||
},
|
||||
]
|
||||
);
|
||||
create_derived_impl(
|
||||
cx,
|
||||
span,
|
||||
type_ident,
|
||||
generics,
|
||||
methods,
|
||||
trait_path,
|
||||
Generics { ty_params: generic_ty_params, lifetimes: opt_vec::Empty },
|
||||
opt_vec::Empty
|
||||
)
|
||||
};
|
||||
|
||||
expand_deriving_generic(cx, span, mitem, in_items,
|
||||
&trait_def)
|
||||
}
|
||||
|
||||
// Creates a method from the given set of statements conforming to the
|
||||
// signature of the `decodable` method.
|
||||
fn create_decode_method(
|
||||
cx: @ExtCtxt,
|
||||
span: span,
|
||||
type_ident: ast::ident,
|
||||
generics: &Generics,
|
||||
expr: @ast::expr
|
||||
) -> @method {
|
||||
// Create the `e` parameter.
|
||||
let d_arg_type = cx.ty_rptr(
|
||||
span,
|
||||
cx.ty_ident(span, cx.ident_of("__D")),
|
||||
None,
|
||||
ast::m_mutbl
|
||||
);
|
||||
let d_ident = cx.ident_of("__d");
|
||||
let d_arg = cx.arg(span, d_ident, d_arg_type);
|
||||
|
||||
// Create the type of the return value.
|
||||
let output_type = create_self_type_with_params(
|
||||
cx,
|
||||
span,
|
||||
type_ident,
|
||||
generics
|
||||
);
|
||||
|
||||
// Create the function declaration.
|
||||
let inputs = ~[d_arg];
|
||||
let fn_decl = cx.fn_decl(inputs, output_type);
|
||||
|
||||
// Create the body block.
|
||||
let body_block = cx.blk_expr(expr);
|
||||
|
||||
// Create the method.
|
||||
let explicit_self = spanned { node: sty_static, span: span };
|
||||
let method_ident = cx.ident_of("decode");
|
||||
@ast::method {
|
||||
ident: method_ident,
|
||||
attrs: ~[],
|
||||
generics: ast_util::empty_generics(),
|
||||
explicit_self: explicit_self,
|
||||
purity: impure_fn,
|
||||
decl: fn_decl,
|
||||
body: body_block,
|
||||
id: cx.next_id(),
|
||||
span: span,
|
||||
self_id: cx.next_id(),
|
||||
vis: public
|
||||
}
|
||||
}
|
||||
|
||||
fn call_substructure_decode_method(
|
||||
cx: @ExtCtxt,
|
||||
span: span
|
||||
) -> @ast::expr {
|
||||
// Call the substructure method.
|
||||
cx.expr_call(
|
||||
span,
|
||||
cx.expr_path(
|
||||
cx.path_global(
|
||||
span,
|
||||
~[
|
||||
cx.ident_of("extra"),
|
||||
fn decodable_substructure(cx: @ExtCtxt, span: span,
|
||||
substr: &Substructure) -> @expr {
|
||||
let decoder = substr.nonself_args[0];
|
||||
let recurse = ~[cx.ident_of("extra"),
|
||||
cx.ident_of("serialize"),
|
||||
cx.ident_of("Decodable"),
|
||||
cx.ident_of("decode"),
|
||||
]
|
||||
)
|
||||
),
|
||||
~[
|
||||
cx.expr_ident(span, cx.ident_of("__d"))
|
||||
]
|
||||
)
|
||||
}
|
||||
cx.ident_of("decode")];
|
||||
// throw an underscore in front to suppress unused variable warnings
|
||||
let blkarg = cx.ident_of("_d");
|
||||
let blkdecoder = cx.expr_ident(span, blkarg);
|
||||
let calldecode = cx.expr_call_global(span, recurse, ~[blkdecoder]);
|
||||
let lambdadecode = cx.lambda_expr_1(span, calldecode, blkarg);
|
||||
|
||||
fn expand_deriving_decodable_struct_def(
|
||||
cx: @ExtCtxt,
|
||||
span: span,
|
||||
struct_def: &struct_def,
|
||||
type_ident: ident,
|
||||
generics: &Generics
|
||||
) -> @item {
|
||||
// Create the method.
|
||||
let method = expand_deriving_decodable_struct_method(
|
||||
cx,
|
||||
span,
|
||||
struct_def,
|
||||
type_ident,
|
||||
generics
|
||||
);
|
||||
return match *substr.fields {
|
||||
StaticStruct(_, ref summary) => {
|
||||
let nfields = match *summary {
|
||||
Left(n) => n, Right(ref fields) => fields.len()
|
||||
};
|
||||
let read_struct_field = cx.ident_of("read_struct_field");
|
||||
|
||||
// Create the implementation.
|
||||
create_derived_decodable_impl(
|
||||
cx,
|
||||
span,
|
||||
type_ident,
|
||||
generics,
|
||||
method
|
||||
)
|
||||
}
|
||||
let getarg = |name: ~str, field: uint| {
|
||||
cx.expr_method_call(span, blkdecoder, read_struct_field,
|
||||
~[cx.expr_str(span, name),
|
||||
cx.expr_uint(span, field),
|
||||
lambdadecode])
|
||||
};
|
||||
|
||||
fn expand_deriving_decodable_enum_def(
|
||||
cx: @ExtCtxt,
|
||||
span: span,
|
||||
enum_definition: &enum_def,
|
||||
type_ident: ident,
|
||||
generics: &Generics
|
||||
) -> @item {
|
||||
// Create the method.
|
||||
let method = expand_deriving_decodable_enum_method(
|
||||
cx,
|
||||
span,
|
||||
enum_definition,
|
||||
type_ident,
|
||||
generics
|
||||
);
|
||||
|
||||
// Create the implementation.
|
||||
create_derived_decodable_impl(
|
||||
cx,
|
||||
span,
|
||||
type_ident,
|
||||
generics,
|
||||
method
|
||||
)
|
||||
}
|
||||
|
||||
fn create_read_struct_field(
|
||||
cx: @ExtCtxt,
|
||||
span: span,
|
||||
idx: uint,
|
||||
ident: ident
|
||||
) -> ast::field {
|
||||
// Call the substructure method.
|
||||
let decode_expr = call_substructure_decode_method(cx, span);
|
||||
|
||||
let d_id = cx.ident_of("__d");
|
||||
|
||||
let call_expr = cx.expr_method_call(
|
||||
span,
|
||||
cx.expr_ident(span, d_id),
|
||||
cx.ident_of("read_struct_field"),
|
||||
~[
|
||||
cx.expr_str(span, cx.str_of(ident)),
|
||||
cx.expr_uint(span, idx),
|
||||
cx.lambda_expr_1(span, decode_expr, d_id)
|
||||
]
|
||||
);
|
||||
|
||||
cx.field_imm(span, ident, call_expr)
|
||||
}
|
||||
|
||||
fn create_read_struct_arg(
|
||||
cx: @ExtCtxt,
|
||||
span: span,
|
||||
idx: uint,
|
||||
ident: ident
|
||||
) -> ast::field {
|
||||
// Call the substructure method.
|
||||
let decode_expr = call_substructure_decode_method(cx, span);
|
||||
|
||||
let call_expr = cx.expr_method_call(
|
||||
span,
|
||||
cx.expr_ident(span, cx.ident_of("__d")),
|
||||
cx.ident_of("read_struct_arg"),
|
||||
~[
|
||||
cx.expr_uint(span, idx),
|
||||
cx.lambda_expr_0(span, decode_expr),
|
||||
]
|
||||
);
|
||||
|
||||
cx.field_imm(span, ident, call_expr)
|
||||
}
|
||||
|
||||
fn expand_deriving_decodable_struct_method(
|
||||
cx: @ExtCtxt,
|
||||
span: span,
|
||||
struct_def: &struct_def,
|
||||
type_ident: ident,
|
||||
generics: &Generics
|
||||
) -> @method {
|
||||
// Create the body of the method.
|
||||
let mut i = 0;
|
||||
let mut fields = ~[];
|
||||
for struct_def.fields.each |struct_field| {
|
||||
match struct_field.node.kind {
|
||||
named_field(ident, _) => {
|
||||
fields.push(create_read_struct_field(cx, span, i, ident));
|
||||
}
|
||||
unnamed_field => {
|
||||
cx.span_unimpl(
|
||||
span,
|
||||
"unnamed fields with `deriving(Decodable)`"
|
||||
);
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
|
||||
let d_id = cx.ident_of("__d");
|
||||
|
||||
let read_struct_expr = cx.expr_method_call(
|
||||
span,
|
||||
cx.expr_ident(span, d_id),
|
||||
cx.ident_of("read_struct"),
|
||||
~[
|
||||
cx.expr_str(span, cx.str_of(type_ident)),
|
||||
cx.expr_uint(span, fields.len()),
|
||||
cx.lambda_expr_1(
|
||||
span,
|
||||
cx.expr_struct_ident(span, type_ident, fields),
|
||||
d_id)
|
||||
]
|
||||
);
|
||||
|
||||
// Create the method itself.
|
||||
create_decode_method(cx, span, type_ident, generics, read_struct_expr)
|
||||
}
|
||||
|
||||
fn create_read_variant_arg(
|
||||
cx: @ExtCtxt,
|
||||
span: span,
|
||||
idx: uint,
|
||||
variant: &ast::variant
|
||||
) -> ast::arm {
|
||||
// Create the matching pattern.
|
||||
let pat = cx.pat_lit(span, cx.expr_uint(span, idx));
|
||||
|
||||
// Feed each argument in this variant to the decode function
|
||||
// as well.
|
||||
let variant_arg_len = variant_arg_count(cx, span, variant);
|
||||
|
||||
let expr = if variant_arg_len == 0 {
|
||||
cx.expr_ident(span, variant.node.name)
|
||||
let result = match *summary {
|
||||
Left(n) => {
|
||||
if n == 0 {
|
||||
cx.expr_ident(span, substr.type_ident)
|
||||
} else {
|
||||
// Feed the discriminant to the decode function.
|
||||
let mut args = ~[];
|
||||
|
||||
for uint::range(0, variant_arg_len) |j| {
|
||||
// Call the substructure method.
|
||||
let expr = call_substructure_decode_method(cx, span);
|
||||
|
||||
let d_id = cx.ident_of("__d");
|
||||
|
||||
let call_expr = cx.expr_method_call(
|
||||
span,
|
||||
cx.expr_ident(span, d_id),
|
||||
cx.ident_of("read_enum_variant_arg"),
|
||||
~[
|
||||
cx.expr_uint(span, j),
|
||||
cx.lambda_expr_1(span, expr, d_id),
|
||||
]
|
||||
);
|
||||
|
||||
args.push(call_expr);
|
||||
let mut fields = vec::with_capacity(n);
|
||||
for uint::range(0, n) |i| {
|
||||
fields.push(getarg(fmt!("_field%u", i), i));
|
||||
}
|
||||
cx.expr_call_ident(span, substr.type_ident, fields)
|
||||
}
|
||||
}
|
||||
Right(ref fields) => {
|
||||
let fields = do fields.mapi |i, f| {
|
||||
cx.field_imm(span, *f, getarg(cx.str_of(*f), i))
|
||||
};
|
||||
cx.expr_struct_ident(span, substr.type_ident, fields)
|
||||
}
|
||||
|
||||
cx.expr_call_ident(span, variant.node.name, args)
|
||||
};
|
||||
|
||||
// Create the arm.
|
||||
cx.arm(span, ~[pat], expr)
|
||||
}
|
||||
|
||||
fn create_read_enum_variant(
|
||||
cx: @ExtCtxt,
|
||||
span: span,
|
||||
enum_definition: &enum_def
|
||||
) -> @expr {
|
||||
// Create a vector that contains all the variant names.
|
||||
let expr_arm_names = cx.expr_vec(
|
||||
span,
|
||||
do enum_definition.variants.map |variant| {
|
||||
cx.expr_str(
|
||||
span,
|
||||
cx.str_of(variant.node.name)
|
||||
)
|
||||
cx.expr_method_call(span, decoder, cx.ident_of("read_struct"),
|
||||
~[cx.expr_str(span, cx.str_of(substr.type_ident)),
|
||||
cx.expr_uint(span, nfields),
|
||||
cx.lambda_expr_1(span, result, blkarg)])
|
||||
}
|
||||
);
|
||||
StaticEnum(_, ref fields) => {
|
||||
let variant = cx.ident_of("i");
|
||||
|
||||
// Create the arms of the match in the method body.
|
||||
let mut arms = do enum_definition.variants.mapi |i, variant| {
|
||||
create_read_variant_arg(cx, span, i, variant)
|
||||
let mut arms = ~[];
|
||||
let mut variants = ~[];
|
||||
let rvariant_arg = cx.ident_of("read_enum_variant_arg");
|
||||
|
||||
for fields.eachi |i, f| {
|
||||
let (name, parts) = match *f { (i, ref p) => (i, p) };
|
||||
variants.push(cx.expr_str(span, cx.str_of(name)));
|
||||
|
||||
let getarg = |field: uint| {
|
||||
cx.expr_method_call(span, blkdecoder, rvariant_arg,
|
||||
~[cx.expr_uint(span, field),
|
||||
lambdadecode])
|
||||
};
|
||||
|
||||
// Add the impossible case arm.
|
||||
let decoded = match *parts {
|
||||
Left(n) => {
|
||||
if n == 0 {
|
||||
cx.expr_ident(span, name)
|
||||
} else {
|
||||
let mut fields = vec::with_capacity(n);
|
||||
for uint::range(0, n) |i| {
|
||||
fields.push(getarg(i));
|
||||
}
|
||||
cx.expr_call_ident(span, name, fields)
|
||||
}
|
||||
}
|
||||
Right(ref fields) => {
|
||||
let fields = do fields.mapi |i, f| {
|
||||
cx.field_imm(span, *f, getarg(i))
|
||||
};
|
||||
cx.expr_struct_ident(span, name, fields)
|
||||
}
|
||||
};
|
||||
arms.push(cx.arm(span,
|
||||
~[cx.pat_lit(span, cx.expr_uint(span, i))],
|
||||
decoded));
|
||||
}
|
||||
|
||||
arms.push(cx.arm_unreachable(span));
|
||||
|
||||
// Create the read_enum_variant expression.
|
||||
cx.expr_method_call(
|
||||
span,
|
||||
cx.expr_ident(span, cx.ident_of("__d")),
|
||||
let result = cx.expr_match(span, cx.expr_ident(span, variant), arms);
|
||||
let lambda = cx.lambda_expr(span, ~[blkarg, variant], result);
|
||||
let variant_vec = cx.expr_vec(span, variants);
|
||||
let result = cx.expr_method_call(span, blkdecoder,
|
||||
cx.ident_of("read_enum_variant"),
|
||||
~[
|
||||
expr_arm_names,
|
||||
cx.lambda_expr(span,
|
||||
~[cx.ident_of("__d"), cx.ident_of("__i")],
|
||||
cx.expr_match(span, cx.expr_ident(span, cx.ident_of("__i")), arms))
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
fn expand_deriving_decodable_enum_method(
|
||||
cx: @ExtCtxt,
|
||||
span: span,
|
||||
enum_definition: &enum_def,
|
||||
type_ident: ast::ident,
|
||||
generics: &Generics
|
||||
) -> @method {
|
||||
let read_enum_variant_expr = create_read_enum_variant(
|
||||
cx,
|
||||
span,
|
||||
enum_definition
|
||||
);
|
||||
|
||||
let d_id = cx.ident_of("__d");
|
||||
|
||||
// Create the read_enum expression
|
||||
let read_enum_expr = cx.expr_method_call(
|
||||
span,
|
||||
cx.expr_ident(span, d_id),
|
||||
cx.ident_of("read_enum"),
|
||||
~[
|
||||
cx.expr_str(span, cx.str_of(type_ident)),
|
||||
cx.lambda_expr_1(span, read_enum_variant_expr, d_id)
|
||||
]
|
||||
);
|
||||
|
||||
// Create the method.
|
||||
create_decode_method(cx, span, type_ident, generics, read_enum_expr)
|
||||
~[variant_vec, lambda]);
|
||||
cx.expr_method_call(span, decoder, cx.ident_of("read_enum"),
|
||||
~[cx.expr_str(span, cx.str_of(substr.type_ident)),
|
||||
cx.lambda_expr_1(span, result, blkarg)])
|
||||
}
|
||||
_ => cx.bug("expected StaticEnum or StaticStruct in deriving(Decodable)")
|
||||
};
|
||||
}
|
||||
|
@ -77,578 +77,113 @@ fn decode(d: &D) -> spanned<T> {
|
||||
|
||||
use core::prelude::*;
|
||||
|
||||
use ast;
|
||||
use ast::*;
|
||||
use ast::{meta_item, item, expr, m_imm, m_mutbl};
|
||||
use codemap::span;
|
||||
use ext::base::ExtCtxt;
|
||||
use ext::build::AstBuilder;
|
||||
use ext::deriving::*;
|
||||
use codemap::{span, spanned};
|
||||
use ast_util;
|
||||
use opt_vec;
|
||||
use ext::deriving::generic::*;
|
||||
|
||||
pub fn expand_deriving_encodable(
|
||||
cx: @ExtCtxt,
|
||||
pub fn expand_deriving_encodable(cx: @ExtCtxt,
|
||||
span: span,
|
||||
_mitem: @meta_item,
|
||||
in_items: ~[@item]
|
||||
) -> ~[@item] {
|
||||
expand_deriving(
|
||||
cx,
|
||||
span,
|
||||
in_items,
|
||||
expand_deriving_encodable_struct_def,
|
||||
expand_deriving_encodable_enum_def
|
||||
)
|
||||
}
|
||||
|
||||
fn create_derived_encodable_impl(
|
||||
cx: @ExtCtxt,
|
||||
span: span,
|
||||
type_ident: ident,
|
||||
generics: &Generics,
|
||||
method: @method
|
||||
) -> @item {
|
||||
let encoder_ty_param = cx.typaram(
|
||||
cx.ident_of("__E"),
|
||||
@opt_vec::with(
|
||||
cx.typarambound(
|
||||
cx.path_global(
|
||||
span,
|
||||
~[
|
||||
cx.ident_of("extra"),
|
||||
cx.ident_of("serialize"),
|
||||
cx.ident_of("Encoder"),
|
||||
]))));
|
||||
|
||||
// All the type parameters need to bound to the trait.
|
||||
let generic_ty_params = opt_vec::with(encoder_ty_param);
|
||||
|
||||
let methods = [method];
|
||||
let trait_path = cx.path_all(
|
||||
span,
|
||||
true,
|
||||
~[
|
||||
cx.ident_of("extra"),
|
||||
cx.ident_of("serialize"),
|
||||
cx.ident_of("Encodable")
|
||||
],
|
||||
None,
|
||||
~[
|
||||
cx.ty_ident(span, cx.ident_of("__E"))
|
||||
mitem: @meta_item,
|
||||
in_items: ~[@item]) -> ~[@item] {
|
||||
let trait_def = TraitDef {
|
||||
path: Path::new_(~["extra", "serialize", "Encodable"], None,
|
||||
~[~Literal(Path::new_local("__E"))], true),
|
||||
additional_bounds: ~[],
|
||||
generics: LifetimeBounds {
|
||||
lifetimes: ~[],
|
||||
bounds: ~[("__E", ~[Path::new(~["extra", "serialize", "Encoder"])])],
|
||||
},
|
||||
methods: ~[
|
||||
MethodDef {
|
||||
name: "encode",
|
||||
generics: LifetimeBounds::empty(),
|
||||
explicit_self: Some(Some(Borrowed(None, m_imm))),
|
||||
args: ~[Ptr(~Literal(Path::new_local("__E")),
|
||||
Borrowed(None, m_mutbl))],
|
||||
ret_ty: nil_ty(),
|
||||
const_nonmatching: true,
|
||||
combine_substructure: encodable_substructure,
|
||||
},
|
||||
]
|
||||
);
|
||||
create_derived_impl(
|
||||
cx,
|
||||
span,
|
||||
type_ident,
|
||||
generics,
|
||||
methods,
|
||||
trait_path,
|
||||
Generics { ty_params: generic_ty_params, lifetimes: opt_vec::Empty },
|
||||
opt_vec::Empty
|
||||
)
|
||||
};
|
||||
|
||||
expand_deriving_generic(cx, span, mitem, in_items,
|
||||
&trait_def)
|
||||
}
|
||||
|
||||
// Creates a method from the given set of statements conforming to the
|
||||
// signature of the `encodable` method.
|
||||
fn create_encode_method(
|
||||
cx: @ExtCtxt,
|
||||
span: span,
|
||||
statements: ~[@stmt]
|
||||
) -> @method {
|
||||
// Create the `e` parameter.
|
||||
let e_arg_type = cx.ty_rptr(
|
||||
span,
|
||||
cx.ty_ident(span, cx.ident_of("__E")),
|
||||
None,
|
||||
ast::m_mutbl
|
||||
);
|
||||
let e_arg = cx.arg(span, cx.ident_of("__e"), e_arg_type);
|
||||
fn encodable_substructure(cx: @ExtCtxt, span: span,
|
||||
substr: &Substructure) -> @expr {
|
||||
let encoder = substr.nonself_args[0];
|
||||
// throw an underscore in front to suppress unused variable warnings
|
||||
let blkarg = cx.ident_of("_e");
|
||||
let blkencoder = cx.expr_ident(span, blkarg);
|
||||
let encode = cx.ident_of("encode");
|
||||
|
||||
// Create the type of the return value.
|
||||
let output_type = cx.ty_nil();
|
||||
|
||||
// Create the function declaration.
|
||||
let inputs = ~[e_arg];
|
||||
let fn_decl = cx.fn_decl(inputs, output_type);
|
||||
|
||||
// Create the body block.
|
||||
let body_block = cx.blk(span, statements, None);
|
||||
|
||||
// Create the method.
|
||||
let explicit_self = spanned { node: sty_region(None, m_imm), span: span };
|
||||
let method_ident = cx.ident_of("encode");
|
||||
@ast::method {
|
||||
ident: method_ident,
|
||||
attrs: ~[],
|
||||
generics: ast_util::empty_generics(),
|
||||
explicit_self: explicit_self,
|
||||
purity: impure_fn,
|
||||
decl: fn_decl,
|
||||
body: body_block,
|
||||
id: cx.next_id(),
|
||||
span: span,
|
||||
self_id: cx.next_id(),
|
||||
vis: public
|
||||
}
|
||||
}
|
||||
|
||||
fn call_substructure_encode_method(
|
||||
cx: @ExtCtxt,
|
||||
span: span,
|
||||
self_field: @expr
|
||||
) -> @ast::expr {
|
||||
// Gather up the parameters we want to chain along.
|
||||
let e_ident = cx.ident_of("__e");
|
||||
let e_expr = cx.expr_ident(span, e_ident);
|
||||
|
||||
// Call the substructure method.
|
||||
let encode_ident = cx.ident_of("encode");
|
||||
cx.expr_method_call(
|
||||
span,
|
||||
self_field,
|
||||
encode_ident,
|
||||
~[e_expr]
|
||||
)
|
||||
}
|
||||
|
||||
fn expand_deriving_encodable_struct_def(
|
||||
cx: @ExtCtxt,
|
||||
span: span,
|
||||
struct_def: &struct_def,
|
||||
type_ident: ident,
|
||||
generics: &Generics
|
||||
) -> @item {
|
||||
// Create the method.
|
||||
let method = expand_deriving_encodable_struct_method(
|
||||
cx,
|
||||
span,
|
||||
type_ident,
|
||||
struct_def
|
||||
);
|
||||
|
||||
// Create the implementation.
|
||||
create_derived_encodable_impl(
|
||||
cx,
|
||||
span,
|
||||
type_ident,
|
||||
generics,
|
||||
method
|
||||
)
|
||||
}
|
||||
|
||||
fn expand_deriving_encodable_enum_def(
|
||||
cx: @ExtCtxt,
|
||||
span: span,
|
||||
enum_definition: &enum_def,
|
||||
type_ident: ident,
|
||||
generics: &Generics
|
||||
) -> @item {
|
||||
// Create the method.
|
||||
let method = expand_deriving_encodable_enum_method(
|
||||
cx,
|
||||
span,
|
||||
type_ident,
|
||||
enum_definition
|
||||
);
|
||||
|
||||
// Create the implementation.
|
||||
create_derived_encodable_impl(
|
||||
cx,
|
||||
span,
|
||||
type_ident,
|
||||
generics,
|
||||
method
|
||||
)
|
||||
}
|
||||
|
||||
fn expand_deriving_encodable_struct_method(
|
||||
cx: @ExtCtxt,
|
||||
span: span,
|
||||
type_ident: ident,
|
||||
struct_def: &struct_def
|
||||
) -> @method {
|
||||
// Create the body of the method.
|
||||
let mut idx = 0;
|
||||
let mut statements = ~[];
|
||||
for struct_def.fields.each |struct_field| {
|
||||
match struct_field.node.kind {
|
||||
named_field(ident, _) => {
|
||||
// Create the accessor for this field.
|
||||
let self_field = cx.expr_field_access(span,
|
||||
cx.expr_self(span),
|
||||
ident);
|
||||
|
||||
// Call the substructure method.
|
||||
let encode_expr = call_substructure_encode_method(
|
||||
cx,
|
||||
span,
|
||||
self_field
|
||||
);
|
||||
|
||||
let e_ident = cx.ident_of("__e");
|
||||
|
||||
let call_expr = cx.expr_method_call(
|
||||
span,
|
||||
cx.expr_ident(span, e_ident),
|
||||
cx.ident_of("emit_struct_field"),
|
||||
~[
|
||||
cx.expr_str(span, cx.str_of(ident)),
|
||||
cx.expr_uint(span, idx),
|
||||
cx.lambda_expr_1(span, encode_expr, e_ident)
|
||||
]
|
||||
);
|
||||
|
||||
statements.push(cx.stmt_expr(call_expr));
|
||||
}
|
||||
unnamed_field => {
|
||||
cx.span_unimpl(
|
||||
span,
|
||||
"unnamed fields with `deriving(Encodable)`"
|
||||
);
|
||||
}
|
||||
}
|
||||
idx += 1;
|
||||
}
|
||||
|
||||
let e_id = cx.ident_of("__e");
|
||||
let emit_struct_stmt = cx.expr_method_call(
|
||||
span,
|
||||
cx.expr_ident(span, e_id),
|
||||
cx.ident_of("emit_struct"),
|
||||
~[
|
||||
cx.expr_str(span, cx.str_of(type_ident)),
|
||||
cx.expr_uint(span, statements.len()),
|
||||
cx.lambda_stmts_1(span, statements, e_id),
|
||||
]
|
||||
);
|
||||
|
||||
let statements = ~[cx.stmt_expr(emit_struct_stmt)];
|
||||
|
||||
// Create the method itself.
|
||||
return create_encode_method(cx, span, statements);
|
||||
}
|
||||
|
||||
fn expand_deriving_encodable_enum_method(
|
||||
cx: @ExtCtxt,
|
||||
span: span,
|
||||
type_ident: ast::ident,
|
||||
enum_definition: &enum_def
|
||||
) -> @method {
|
||||
// Create the arms of the match in the method body.
|
||||
let arms = do enum_definition.variants.mapi |i, variant| {
|
||||
// Create the matching pattern.
|
||||
let (pat, fields) = create_enum_variant_pattern(cx, span, variant, "__self", ast::m_imm);
|
||||
|
||||
// Feed the discriminant to the encode function.
|
||||
return match *substr.fields {
|
||||
Struct(ref fields) => {
|
||||
let emit_struct_field = cx.ident_of("emit_struct_field");
|
||||
let mut stmts = ~[];
|
||||
|
||||
// Feed each argument in this variant to the encode function
|
||||
// as well.
|
||||
let variant_arg_len = variant_arg_count(cx, span, variant);
|
||||
for fields.eachi |j, &(_, field)| {
|
||||
// Call the substructure method.
|
||||
let expr = call_substructure_encode_method(cx, span, field);
|
||||
|
||||
let e_ident = cx.ident_of("__e");
|
||||
let call_expr = cx.expr_method_call(
|
||||
span,
|
||||
cx.expr_ident(span, e_ident),
|
||||
cx.ident_of("emit_enum_variant_arg"),
|
||||
~[
|
||||
cx.expr_uint(span, j),
|
||||
cx.lambda_expr_1(span, expr, e_ident),
|
||||
]
|
||||
);
|
||||
|
||||
stmts.push(cx.stmt_expr(call_expr));
|
||||
}
|
||||
|
||||
// Create the pattern body.
|
||||
let e_id = cx.ident_of("__e");
|
||||
|
||||
let call_expr = cx.expr_method_call(
|
||||
span,
|
||||
cx.expr_ident(span, e_id),
|
||||
cx.ident_of("emit_enum_variant"),
|
||||
~[
|
||||
cx.expr_str(span, cx.str_of(variant.node.name)),
|
||||
for fields.eachi |i, f| {
|
||||
let (name, val) = match *f {
|
||||
(Some(id), e, _) => (cx.str_of(id), e),
|
||||
(None, e, _) => (fmt!("_field%u", i), e)
|
||||
};
|
||||
let enc = cx.expr_method_call(span, val, encode, ~[blkencoder]);
|
||||
let lambda = cx.lambda_expr_1(span, enc, blkarg);
|
||||
let call = cx.expr_method_call(span, blkencoder,
|
||||
emit_struct_field,
|
||||
~[cx.expr_str(span, name),
|
||||
cx.expr_uint(span, i),
|
||||
cx.expr_uint(span, variant_arg_len),
|
||||
cx.lambda_stmts_1(span, stmts, e_id)
|
||||
]
|
||||
);
|
||||
lambda]);
|
||||
stmts.push(cx.stmt_expr(call));
|
||||
}
|
||||
|
||||
//let match_body_block = cx.blk_expr(call_expr);
|
||||
let blk = cx.lambda_stmts_1(span, stmts, blkarg);
|
||||
cx.expr_method_call(span, encoder, cx.ident_of("emit_struct"),
|
||||
~[cx.expr_str(span, cx.str_of(substr.type_ident)),
|
||||
cx.expr_uint(span, fields.len()),
|
||||
blk])
|
||||
}
|
||||
|
||||
// Create the arm.
|
||||
cx.arm(span, ~[pat], call_expr) //match_body_block)
|
||||
};
|
||||
EnumMatching(idx, variant, ref fields) => {
|
||||
// We're not generating an AST that the borrow checker is expecting,
|
||||
// so we need to generate a unique local variable to take the
|
||||
// mutable loan out on, otherwise we get conflicts which don't
|
||||
// actually exist.
|
||||
let me = cx.stmt_let(span, false, blkarg, encoder);
|
||||
let encoder = cx.expr_ident(span, blkarg);
|
||||
let emit_variant_arg = cx.ident_of("emit_enum_variant_arg");
|
||||
let mut stmts = ~[];
|
||||
for fields.eachi |i, f| {
|
||||
let val = match *f { (_, e, _) => e };
|
||||
let enc = cx.expr_method_call(span, val, encode, ~[blkencoder]);
|
||||
let lambda = cx.lambda_expr_1(span, enc, blkarg);
|
||||
let call = cx.expr_method_call(span, blkencoder,
|
||||
emit_variant_arg,
|
||||
~[cx.expr_uint(span, i),
|
||||
lambda]);
|
||||
stmts.push(cx.stmt_expr(call));
|
||||
}
|
||||
|
||||
let e_ident = cx.ident_of("__e");
|
||||
|
||||
// Create the method body.
|
||||
let lambda_expr = cx.lambda_expr_1(
|
||||
span,
|
||||
expand_enum_or_struct_match(cx, span, arms),
|
||||
e_ident);
|
||||
|
||||
let call_expr = cx.expr_method_call(
|
||||
span,
|
||||
cx.expr_ident(span, e_ident),
|
||||
let blk = cx.lambda_stmts_1(span, stmts, blkarg);
|
||||
let name = cx.expr_str(span, cx.str_of(variant.node.name));
|
||||
let call = cx.expr_method_call(span, blkencoder,
|
||||
cx.ident_of("emit_enum_variant"),
|
||||
~[name,
|
||||
cx.expr_uint(span, idx),
|
||||
cx.expr_uint(span, fields.len()),
|
||||
blk]);
|
||||
let blk = cx.lambda_expr_1(span, call, blkarg);
|
||||
let ret = cx.expr_method_call(span, encoder,
|
||||
cx.ident_of("emit_enum"),
|
||||
~[
|
||||
cx.expr_str(span, cx.str_of(type_ident)),
|
||||
lambda_expr,
|
||||
]
|
||||
);
|
||||
|
||||
let stmt = cx.stmt_expr(call_expr);
|
||||
|
||||
// Create the method.
|
||||
create_encode_method(cx, span, ~[stmt])
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
extern mod extra;
|
||||
use core::option::{None, Some};
|
||||
use extra::serialize::Encodable;
|
||||
use extra::serialize::Encoder;
|
||||
|
||||
// just adding the ones I want to test, for now:
|
||||
#[deriving(Eq)]
|
||||
pub enum call {
|
||||
CallToEmitEnum(~str),
|
||||
CallToEmitEnumVariant(~str, uint, uint),
|
||||
CallToEmitEnumVariantArg(uint),
|
||||
CallToEmitUint(uint),
|
||||
CallToEmitNil,
|
||||
CallToEmitStruct(~str,uint),
|
||||
CallToEmitField(~str,uint),
|
||||
CallToEmitOption,
|
||||
CallToEmitOptionNone,
|
||||
CallToEmitOptionSome,
|
||||
// all of the ones I was too lazy to handle:
|
||||
CallToOther
|
||||
}
|
||||
// using `@mut` rather than changing the
|
||||
// type of self in every method of every encoder everywhere.
|
||||
pub struct TestEncoder {call_log : @mut ~[call]}
|
||||
|
||||
pub impl TestEncoder {
|
||||
// these self's should be &mut self's, as well....
|
||||
fn add_to_log (&self, c : call) {
|
||||
self.call_log.push(copy c);
|
||||
}
|
||||
fn add_unknown_to_log (&self) {
|
||||
self.add_to_log (CallToOther)
|
||||
}
|
||||
~[cx.expr_str(span,
|
||||
cx.str_of(substr.type_ident)),
|
||||
blk]);
|
||||
cx.expr_blk(cx.blk(span, ~[me], Some(ret)))
|
||||
}
|
||||
|
||||
impl Encoder for TestEncoder {
|
||||
fn emit_nil(&mut self) { self.add_to_log(CallToEmitNil) }
|
||||
|
||||
fn emit_uint(&mut self, v: uint) {
|
||||
self.add_to_log(CallToEmitUint(v));
|
||||
}
|
||||
fn emit_u64(&mut self, _v: u64) { self.add_unknown_to_log(); }
|
||||
fn emit_u32(&mut self, _v: u32) { self.add_unknown_to_log(); }
|
||||
fn emit_u16(&mut self, _v: u16) { self.add_unknown_to_log(); }
|
||||
fn emit_u8(&mut self, _v: u8) { self.add_unknown_to_log(); }
|
||||
|
||||
fn emit_int(&mut self, _v: int) { self.add_unknown_to_log(); }
|
||||
fn emit_i64(&mut self, _v: i64) { self.add_unknown_to_log(); }
|
||||
fn emit_i32(&mut self, _v: i32) { self.add_unknown_to_log(); }
|
||||
fn emit_i16(&mut self, _v: i16) { self.add_unknown_to_log(); }
|
||||
fn emit_i8(&mut self, _v: i8) { self.add_unknown_to_log(); }
|
||||
|
||||
fn emit_bool(&mut self, _v: bool) { self.add_unknown_to_log(); }
|
||||
|
||||
fn emit_f64(&mut self, _v: f64) { self.add_unknown_to_log(); }
|
||||
fn emit_f32(&mut self, _v: f32) { self.add_unknown_to_log(); }
|
||||
fn emit_float(&mut self, _v: float) { self.add_unknown_to_log(); }
|
||||
|
||||
fn emit_char(&mut self, _v: char) { self.add_unknown_to_log(); }
|
||||
fn emit_str(&mut self, _v: &str) { self.add_unknown_to_log(); }
|
||||
|
||||
fn emit_enum(&mut self, name: &str, f: &fn(&mut TestEncoder)) {
|
||||
self.add_to_log(CallToEmitEnum(name.to_str()));
|
||||
f(self);
|
||||
}
|
||||
|
||||
fn emit_enum_variant(&mut self,
|
||||
name: &str,
|
||||
id: uint,
|
||||
cnt: uint,
|
||||
f: &fn(&mut TestEncoder)) {
|
||||
self.add_to_log(CallToEmitEnumVariant(name.to_str(), id, cnt));
|
||||
f(self);
|
||||
}
|
||||
|
||||
fn emit_enum_variant_arg(&mut self,
|
||||
idx: uint,
|
||||
f: &fn(&mut TestEncoder)) {
|
||||
self.add_to_log(CallToEmitEnumVariantArg(idx));
|
||||
f(self);
|
||||
}
|
||||
|
||||
fn emit_enum_struct_variant(&mut self,
|
||||
name: &str,
|
||||
id: uint,
|
||||
cnt: uint,
|
||||
f: &fn(&mut TestEncoder)) {
|
||||
self.emit_enum_variant(name, id, cnt, f)
|
||||
}
|
||||
|
||||
fn emit_enum_struct_variant_field(&mut self,
|
||||
_name: &str,
|
||||
idx: uint,
|
||||
f: &fn(&mut TestEncoder)) {
|
||||
self.emit_enum_variant_arg(idx, f)
|
||||
}
|
||||
|
||||
fn emit_struct(&mut self,
|
||||
name: &str,
|
||||
len: uint,
|
||||
f: &fn(&mut TestEncoder)) {
|
||||
self.add_to_log(CallToEmitStruct (name.to_str(),len));
|
||||
f(self);
|
||||
}
|
||||
fn emit_struct_field(&mut self,
|
||||
name: &str,
|
||||
idx: uint,
|
||||
f: &fn(&mut TestEncoder)) {
|
||||
self.add_to_log(CallToEmitField (name.to_str(),idx));
|
||||
f(self);
|
||||
}
|
||||
|
||||
fn emit_tuple(&mut self, _len: uint, f: &fn(&mut TestEncoder)) {
|
||||
self.add_unknown_to_log();
|
||||
f(self);
|
||||
}
|
||||
fn emit_tuple_arg(&mut self, _idx: uint, f: &fn(&mut TestEncoder)) {
|
||||
self.add_unknown_to_log();
|
||||
f(self);
|
||||
}
|
||||
|
||||
fn emit_tuple_struct(&mut self,
|
||||
_name: &str,
|
||||
_len: uint,
|
||||
f: &fn(&mut TestEncoder)) {
|
||||
self.add_unknown_to_log();
|
||||
f(self);
|
||||
}
|
||||
|
||||
fn emit_tuple_struct_arg(&mut self,
|
||||
_idx: uint,
|
||||
f: &fn(&mut TestEncoder)) {
|
||||
self.add_unknown_to_log();
|
||||
f(self);
|
||||
}
|
||||
|
||||
fn emit_option(&mut self, f: &fn(&mut TestEncoder)) {
|
||||
self.add_to_log(CallToEmitOption);
|
||||
f(self);
|
||||
}
|
||||
fn emit_option_none(&mut self) {
|
||||
self.add_to_log(CallToEmitOptionNone);
|
||||
}
|
||||
fn emit_option_some(&mut self, f: &fn(&mut TestEncoder)) {
|
||||
self.add_to_log(CallToEmitOptionSome);
|
||||
f(self);
|
||||
}
|
||||
|
||||
fn emit_seq(&mut self, _len: uint, f: &fn(&mut TestEncoder)) {
|
||||
self.add_unknown_to_log();
|
||||
f(self);
|
||||
}
|
||||
fn emit_seq_elt(&mut self, _idx: uint, f: &fn(&mut TestEncoder)) {
|
||||
self.add_unknown_to_log();
|
||||
f(self);
|
||||
}
|
||||
|
||||
fn emit_map(&mut self, _len: uint, f: &fn(&mut TestEncoder)) {
|
||||
self.add_unknown_to_log();
|
||||
f(self);
|
||||
}
|
||||
fn emit_map_elt_key(&mut self, _idx: uint, f: &fn(&mut TestEncoder)) {
|
||||
self.add_unknown_to_log();
|
||||
f(self);
|
||||
}
|
||||
fn emit_map_elt_val(&mut self, _idx: uint, f: &fn(&mut TestEncoder)) {
|
||||
self.add_unknown_to_log();
|
||||
f(self);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn to_call_log<E:Encodable<TestEncoder>>(val: E) -> ~[call] {
|
||||
let mut te = TestEncoder {
|
||||
call_log: @mut ~[]
|
||||
_ => cx.bug("expected Struct or EnumMatching in deriving(Encodable)")
|
||||
};
|
||||
val.encode(&mut te);
|
||||
copy *te.call_log
|
||||
}
|
||||
|
||||
#[deriving(Encodable)]
|
||||
enum Written {
|
||||
Book(uint,uint),
|
||||
Magazine(~str)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_encode_enum() {
|
||||
assert_eq!(
|
||||
to_call_log(Book(34,44)),
|
||||
~[
|
||||
CallToEmitEnum(~"Written"),
|
||||
CallToEmitEnumVariant(~"Book",0,2),
|
||||
CallToEmitEnumVariantArg(0),
|
||||
CallToEmitUint(34),
|
||||
CallToEmitEnumVariantArg(1),
|
||||
CallToEmitUint(44),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
pub struct BPos(uint);
|
||||
|
||||
#[deriving(Encodable)]
|
||||
pub struct HasPos { pos : BPos }
|
||||
|
||||
#[test]
|
||||
fn test_encode_newtype() {
|
||||
assert_eq!(
|
||||
to_call_log(HasPos { pos:BPos(48) }),
|
||||
~[
|
||||
CallToEmitStruct(~"HasPos",1),
|
||||
CallToEmitField(~"pos",0),
|
||||
CallToEmitUint(48),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_encode_option() {
|
||||
let mut v = None;
|
||||
|
||||
assert_eq!(
|
||||
to_call_log(v),
|
||||
~[
|
||||
CallToEmitOption,
|
||||
CallToEmitOptionNone,
|
||||
]
|
||||
);
|
||||
|
||||
v = Some(54u);
|
||||
assert_eq!(
|
||||
to_call_log(v),
|
||||
~[
|
||||
CallToEmitOption,
|
||||
CallToEmitOptionSome,
|
||||
CallToEmitUint(54)
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
75
src/test/run-pass/deriving-encodable-decodable.rs
Normal file
75
src/test/run-pass/deriving-encodable-decodable.rs
Normal file
@ -0,0 +1,75 @@
|
||||
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <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.
|
||||
|
||||
// This actually tests a lot more than just encodable/decodable, but it gets the
|
||||
// job done at least
|
||||
|
||||
extern mod extra;
|
||||
|
||||
use std::io;
|
||||
use std::rand::{random, Rand};
|
||||
use extra::serialize::*;
|
||||
use extra::ebml;
|
||||
use extra::ebml::writer::Encoder;
|
||||
use extra::ebml::reader::Decoder;
|
||||
|
||||
#[deriving(Encodable, Decodable, Eq, Rand)]
|
||||
struct A;
|
||||
#[deriving(Encodable, Decodable, Eq, Rand)]
|
||||
struct B(int);
|
||||
#[deriving(Encodable, Decodable, Eq, Rand)]
|
||||
struct C(int, int, uint);
|
||||
|
||||
#[deriving(Encodable, Decodable, Eq, Rand)]
|
||||
struct D {
|
||||
a: int,
|
||||
b: uint,
|
||||
}
|
||||
|
||||
#[deriving(Encodable, Decodable, Eq, Rand)]
|
||||
enum E {
|
||||
E1,
|
||||
E2(uint),
|
||||
E3(D),
|
||||
E4{ x: uint },
|
||||
}
|
||||
|
||||
#[deriving(Encodable, Decodable, Eq, Rand)]
|
||||
enum F { F1 }
|
||||
|
||||
#[deriving(Encodable, Decodable, Eq, Rand)]
|
||||
struct G<T> {
|
||||
t: T
|
||||
}
|
||||
|
||||
fn roundtrip<T: Rand + Eq + Encodable<Encoder> + Decodable<Decoder>>() {
|
||||
let obj: T = random();
|
||||
let bytes = do io::with_bytes_writer |w| {
|
||||
let mut e = Encoder(w);
|
||||
obj.encode(&mut e);
|
||||
};
|
||||
let doc = ebml::reader::Doc(@bytes);
|
||||
let mut dec = Decoder(doc);
|
||||
let obj2 = Decodable::decode(&mut dec);
|
||||
assert!(obj == obj2);
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
roundtrip::<A>();
|
||||
roundtrip::<B>();
|
||||
roundtrip::<C>();
|
||||
roundtrip::<D>();
|
||||
|
||||
for 20.times {
|
||||
roundtrip::<E>();
|
||||
roundtrip::<F>();
|
||||
roundtrip::<G<int>>();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user