1
Fork 0

Only SwitchInt over integers, not all consts

Also use a Cow to avoid full Vec for all SwitchInts
This commit is contained in:
Simonas Kazlauskas 2017-02-02 06:44:30 +02:00
parent aac82d9b13
commit a00a0adc79
10 changed files with 154 additions and 101 deletions

View file

@ -11,6 +11,7 @@
use syntax::symbol::InternedString; use syntax::symbol::InternedString;
use syntax::ast; use syntax::ast;
use std::rc::Rc; use std::rc::Rc;
use std::borrow::Cow;
use hir::def_id::DefId; use hir::def_id::DefId;
use rustc_const_math::*; use rustc_const_math::*;
use self::ConstVal::*; use self::ConstVal::*;
@ -18,6 +19,8 @@ pub use rustc_const_math::ConstInt;
use std::collections::BTreeMap; use std::collections::BTreeMap;
pub static BOOL_SWITCH_TRUE: Cow<'static, [ConstInt]> = Cow::Borrowed(&[ConstInt::Infer(1)]);
#[derive(Clone, Debug, Hash, RustcEncodable, RustcDecodable, Eq, PartialEq)] #[derive(Clone, Debug, Hash, RustcEncodable, RustcDecodable, Eq, PartialEq)]
pub enum ConstVal { pub enum ConstVal {
Float(ConstFloat), Float(ConstFloat),
@ -49,4 +52,14 @@ impl ConstVal {
Char(..) => "char", Char(..) => "char",
} }
} }
pub fn to_const_int(&self) -> Option<ConstInt> {
match *self {
ConstVal::Integral(i) => Some(i),
ConstVal::Bool(true) => Some(ConstInt::Infer(1)),
ConstVal::Bool(false) => Some(ConstInt::Infer(0)),
ConstVal::Char(ch) => Some(ConstInt::U32(ch as u32)),
_ => None
}
}
} }

View file

@ -446,6 +446,9 @@ pub struct Terminator<'tcx> {
pub kind: TerminatorKind<'tcx> pub kind: TerminatorKind<'tcx>
} }
/// For use in SwitchInt, for switching on bools.
pub static BOOL_SWITCH_TRUE: Cow<'static, [ConstInt]> = Cow::Borrowed(&[ConstInt::Infer(1)]);
#[derive(Clone, RustcEncodable, RustcDecodable)] #[derive(Clone, RustcEncodable, RustcDecodable)]
pub enum TerminatorKind<'tcx> { pub enum TerminatorKind<'tcx> {
/// block should have one successor in the graph; we jump there /// block should have one successor in the graph; we jump there
@ -464,8 +467,7 @@ pub enum TerminatorKind<'tcx> {
/// Possible values. The locations to branch to in each case /// Possible values. The locations to branch to in each case
/// are found in the corresponding indices from the `targets` vector. /// are found in the corresponding indices from the `targets` vector.
// FIXME: ConstVal doesnt quite make any sense here? Its a Switch*Int*. values: Cow<'tcx, [ConstInt]>,
values: Vec<ConstVal>,
/// Possible branch sites. The length of this vector should be /// Possible branch sites. The length of this vector should be
/// equal to the length of the `values` vector plus 1 -- the /// equal to the length of the `values` vector plus 1 -- the
@ -696,7 +698,7 @@ impl<'tcx> TerminatorKind<'tcx> {
values.iter() values.iter()
.map(|const_val| { .map(|const_val| {
let mut buf = String::new(); let mut buf = String::new();
fmt_const_val(&mut buf, const_val).unwrap(); fmt_const_val(&mut buf, &ConstVal::Integral(*const_val)).unwrap();
buf.into() buf.into()
}) })
.chain(iter::once(String::from("otherwise").into())) .chain(iter::once(String::from("otherwise").into()))

View file

@ -223,6 +223,12 @@ macro_rules! make_mir_visitor {
self.super_const_val(const_val); self.super_const_val(const_val);
} }
fn visit_const_int(&mut self,
const_int: &ConstInt,
_: Location) {
self.super_const_int(const_int);
}
fn visit_const_usize(&mut self, fn visit_const_usize(&mut self,
const_usize: & $($mutability)* ConstUsize, const_usize: & $($mutability)* ConstUsize,
_: Location) { _: Location) {
@ -364,12 +370,12 @@ macro_rules! make_mir_visitor {
TerminatorKind::SwitchInt { ref $($mutability)* discr, TerminatorKind::SwitchInt { ref $($mutability)* discr,
ref $($mutability)* switch_ty, ref $($mutability)* switch_ty,
ref $($mutability)* values, ref values,
ref targets } => { ref targets } => {
self.visit_operand(discr, source_location); self.visit_operand(discr, source_location);
self.visit_ty(switch_ty); self.visit_ty(switch_ty);
for value in values { for value in &values[..] {
self.visit_const_val(value, source_location); self.visit_const_int(value, source_location);
} }
for &target in targets { for &target in targets {
self.visit_branch(block, target); self.visit_branch(block, target);
@ -698,10 +704,13 @@ macro_rules! make_mir_visitor {
_substs: & $($mutability)* ClosureSubsts<'tcx>) { _substs: & $($mutability)* ClosureSubsts<'tcx>) {
} }
fn super_const_val(&mut self, _substs: & $($mutability)* ConstVal) { fn super_const_val(&mut self, _const_val: & $($mutability)* ConstVal) {
} }
fn super_const_usize(&mut self, _substs: & $($mutability)* ConstUsize) { fn super_const_int(&mut self, _const_int: &ConstInt) {
}
fn super_const_usize(&mut self, _const_usize: & $($mutability)* ConstUsize) {
} }
// Convenience methods // Convenience methods

View file

@ -679,7 +679,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
let discr = ConstInt::new_inttype(variant.disr_val, adt.discr_ty, let discr = ConstInt::new_inttype(variant.disr_val, adt.discr_ty,
self.tcx.sess.target.uint_type, self.tcx.sess.target.uint_type,
self.tcx.sess.target.int_type).unwrap(); self.tcx.sess.target.int_type).unwrap();
values.push(ConstVal::Integral(discr)); values.push(discr);
blocks.push(self.open_drop_for_variant(c, &mut drop_block, adt, substs, idx)); blocks.push(self.open_drop_for_variant(c, &mut drop_block, adt, substs, idx));
} }
// If there are multiple variants, then if something // If there are multiple variants, then if something
@ -704,7 +704,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
kind: TerminatorKind::SwitchInt { kind: TerminatorKind::SwitchInt {
discr: Operand::Consume(discr), discr: Operand::Consume(discr),
switch_ty: discr_ty, switch_ty: discr_ty,
values: values, values: From::from(values),
targets: blocks, targets: blocks,
// adt_def: adt, // adt_def: adt,
// targets: variant_drops // targets: variant_drops
@ -836,7 +836,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
self.new_block(c, is_cleanup, TerminatorKind::SwitchInt { self.new_block(c, is_cleanup, TerminatorKind::SwitchInt {
discr: Operand::Consume(flag), discr: Operand::Consume(flag),
switch_ty: boolty, switch_ty: boolty,
values: vec![ConstVal::Bool(true)], values: BOOL_SWITCH_TRUE.clone(),
targets: vec![on_set, on_unset], targets: vec![on_set, on_unset],
}) })
} }

View file

@ -15,7 +15,6 @@ use build::expr::category::{Category, RvalueFunc};
use hair::*; use hair::*;
use rustc::ty; use rustc::ty;
use rustc::mir::*; use rustc::mir::*;
use rustc::middle::const_val::ConstVal;
impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
/// Compile `expr`, storing the result into `destination`, which /// Compile `expr`, storing the result into `destination`, which
@ -73,7 +72,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
this.cfg.terminate(block, source_info, TerminatorKind::SwitchInt { this.cfg.terminate(block, source_info, TerminatorKind::SwitchInt {
discr: operand, discr: operand,
switch_ty: this.hir.bool_ty(), switch_ty: this.hir.bool_ty(),
values: vec![ConstVal::Bool(true)], values: BOOL_SWITCH_TRUE.clone(),
targets: vec![then_block, else_block], targets: vec![then_block, else_block],
}); });
@ -120,7 +119,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
this.cfg.terminate(block, source_info, TerminatorKind::SwitchInt { this.cfg.terminate(block, source_info, TerminatorKind::SwitchInt {
discr: lhs, discr: lhs,
switch_ty: this.hir.bool_ty(), switch_ty: this.hir.bool_ty(),
values: vec![ConstVal::Bool(true)], values: BOOL_SWITCH_TRUE.clone(),
targets: blocks, targets: blocks,
}); });
@ -128,7 +127,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
this.cfg.terminate(else_block, source_info, TerminatorKind::SwitchInt { this.cfg.terminate(else_block, source_info, TerminatorKind::SwitchInt {
discr: rhs, discr: rhs,
switch_ty: this.hir.bool_ty(), switch_ty: this.hir.bool_ty(),
values: vec![ConstVal::Bool(true)], values: BOOL_SWITCH_TRUE.clone(),
targets: vec![true_block, false_block], targets: vec![true_block, false_block],
}); });
@ -192,7 +191,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
TerminatorKind::SwitchInt { TerminatorKind::SwitchInt {
discr: cond, discr: cond,
switch_ty: this.hir.bool_ty(), switch_ty: this.hir.bool_ty(),
values: vec![ConstVal::Bool(true)], values: BOOL_SWITCH_TRUE.clone(),
targets: vec![body_block, exit_block], targets: vec![body_block, exit_block],
}); });

View file

@ -675,7 +675,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
self.cfg.terminate(block, source_info, TerminatorKind::SwitchInt { self.cfg.terminate(block, source_info, TerminatorKind::SwitchInt {
discr: cond, discr: cond,
switch_ty: self.hir.bool_ty(), switch_ty: self.hir.bool_ty(),
values: vec![ConstVal::Bool(true)], values: BOOL_SWITCH_TRUE.clone(),
targets: vec![arm_block, otherwise], targets: vec![arm_block, otherwise],
}); });
Some(otherwise) Some(otherwise)

View file

@ -196,7 +196,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
let discr = ConstInt::new_inttype(variant.disr_val, adt_def.discr_ty, let discr = ConstInt::new_inttype(variant.disr_val, adt_def.discr_ty,
tcx.sess.target.uint_type, tcx.sess.target.uint_type,
tcx.sess.target.int_type).unwrap(); tcx.sess.target.int_type).unwrap();
values.push(ConstVal::Integral(discr)); values.push(discr);
*(targets.place_back() <- self.cfg.start_new_block()) *(targets.place_back() <- self.cfg.start_new_block())
} else { } else {
if otherwise_block.is_none() { if otherwise_block.is_none() {
@ -226,59 +226,45 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
self.cfg.terminate(block, source_info, TerminatorKind::SwitchInt { self.cfg.terminate(block, source_info, TerminatorKind::SwitchInt {
discr: Operand::Consume(discr), discr: Operand::Consume(discr),
switch_ty: discr_ty, switch_ty: discr_ty,
values: values, values: From::from(values),
targets: targets targets: targets
}); });
target_blocks target_blocks
} }
TestKind::SwitchInt { switch_ty, ref options, indices: _ } => { TestKind::SwitchInt { switch_ty, ref options, indices: _ } => {
let (targets, term) = match switch_ty.sty { let (values, targets, ret) = if switch_ty.sty == ty::TyBool {
// If we're matching on boolean we can assert!(options.len() > 0 && options.len() <= 2);
// use the If TerminatorKind instead let (true_bb, false_bb) = (self.cfg.start_new_block(),
ty::TyBool => { self.cfg.start_new_block());
assert!(options.len() > 0 && options.len() <= 2); let ret = match &options[0] {
&ConstVal::Bool(true) => vec![true_bb, false_bb],
let (true_bb, else_bb) = &ConstVal::Bool(false) => vec![false_bb, true_bb],
(self.cfg.start_new_block(), v => span_bug!(test.span, "expected boolean value but got {:?}", v)
self.cfg.start_new_block()); };
(BOOL_SWITCH_TRUE.clone(), vec![true_bb, false_bb], ret)
let targets = match &options[0] { } else {
&ConstVal::Bool(true) => vec![true_bb, else_bb], // The switch may be inexhaustive so we
&ConstVal::Bool(false) => vec![else_bb, true_bb], // add a catch all block
v => span_bug!(test.span, "expected boolean value but got {:?}", v) let otherwise = self.cfg.start_new_block();
}; let targets: Vec<_> =
options.iter()
(targets, TerminatorKind::SwitchInt { .map(|_| self.cfg.start_new_block())
discr: Operand::Consume(lvalue.clone()), .chain(Some(otherwise))
switch_ty: self.hir.bool_ty(), .collect();
values: vec![ConstVal::Bool(true)], let values: Vec<_> = options.iter().map(|v|
targets: vec![true_bb, else_bb] v.to_const_int().expect("switching on integral")
}) ).collect();
(From::from(values), targets.clone(), targets)
}
_ => {
// The switch may be inexhaustive so we
// add a catch all block
let otherwise = self.cfg.start_new_block();
let targets: Vec<_> =
options.iter()
.map(|_| self.cfg.start_new_block())
.chain(Some(otherwise))
.collect();
(targets.clone(),
TerminatorKind::SwitchInt {
discr: Operand::Consume(lvalue.clone()),
switch_ty: switch_ty,
values: options.clone(),
targets: targets
})
}
}; };
self.cfg.terminate(block, source_info, term); self.cfg.terminate(block, source_info, TerminatorKind::SwitchInt {
targets discr: Operand::Consume(lvalue.clone()),
switch_ty: switch_ty,
values: values,
targets: targets.clone(),
});
ret
} }
TestKind::Eq { ref value, mut ty } => { TestKind::Eq { ref value, mut ty } => {
@ -346,10 +332,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
self.cfg.terminate(eq_block, source_info, TerminatorKind::SwitchInt { self.cfg.terminate(eq_block, source_info, TerminatorKind::SwitchInt {
discr: Operand::Consume(eq_result), discr: Operand::Consume(eq_result),
switch_ty: self.hir.bool_ty(), switch_ty: self.hir.bool_ty(),
values: vec![ConstVal::Bool(true)], values: BOOL_SWITCH_TRUE.clone(),
targets: vec![block, fail], targets: vec![block, fail],
}); });
vec![block, fail] vec![block, fail]
} else { } else {
let block = self.compare(block, fail, test.span, BinOp::Eq, expect, val); let block = self.compare(block, fail, test.span, BinOp::Eq, expect, val);
@ -391,16 +376,15 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
Operand::Consume(expected))); Operand::Consume(expected)));
// branch based on result // branch based on result
let target_blocks: Vec<_> = vec![self.cfg.start_new_block(), let (false_bb, true_bb) = (self.cfg.start_new_block(),
self.cfg.start_new_block()]; self.cfg.start_new_block());
self.cfg.terminate(block, source_info, TerminatorKind::SwitchInt { self.cfg.terminate(block, source_info, TerminatorKind::SwitchInt {
discr: Operand::Consume(result), discr: Operand::Consume(result),
switch_ty: self.hir.bool_ty(), switch_ty: self.hir.bool_ty(),
values: vec![ConstVal::Bool(true)], values: BOOL_SWITCH_TRUE.clone(),
targets: target_blocks.clone(), targets: vec![true_bb, false_bb],
}); });
vec![true_bb, false_bb]
target_blocks
} }
} }
} }
@ -425,10 +409,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
self.cfg.terminate(block, source_info, TerminatorKind::SwitchInt { self.cfg.terminate(block, source_info, TerminatorKind::SwitchInt {
discr: Operand::Consume(result), discr: Operand::Consume(result),
switch_ty: self.hir.bool_ty(), switch_ty: self.hir.bool_ty(),
values: vec![ConstVal::Bool(true)], values: BOOL_SWITCH_TRUE.clone(),
targets: vec![target_block, fail_block] targets: vec![target_block, fail_block]
}); });
target_block target_block
} }

View file

@ -11,6 +11,7 @@
use llvm::{self, ValueRef, BasicBlockRef}; use llvm::{self, ValueRef, BasicBlockRef};
use rustc_const_eval::{ErrKind, ConstEvalErr, note_const_eval_err}; use rustc_const_eval::{ErrKind, ConstEvalErr, note_const_eval_err};
use rustc::middle::lang_items; use rustc::middle::lang_items;
use rustc::middle::const_val::ConstInt;
use rustc::ty::{self, layout, TypeFoldable}; use rustc::ty::{self, layout, TypeFoldable};
use rustc::mir; use rustc::mir;
use abi::{Abi, FnType, ArgType}; use abi::{Abi, FnType, ArgType};
@ -134,14 +135,24 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
} }
mir::TerminatorKind::SwitchInt { ref discr, switch_ty, ref values, ref targets } => { mir::TerminatorKind::SwitchInt { ref discr, switch_ty, ref values, ref targets } => {
// TODO: cond_br if only 1 value let discr = self.trans_operand(&bcx, discr);
let (otherwise, targets) = targets.split_last().unwrap(); if switch_ty == bcx.tcx().types.bool {
let discr = self.trans_operand(&bcx, discr).immediate(); let lltrue = llblock(self, targets[0]);
let switch = bcx.switch(discr, llblock(self, *otherwise), values.len()); let llfalse = llblock(self, targets[1]);
for (value, target) in values.iter().zip(targets) { if let [ConstInt::Infer(0)] = values[..] {
let val = Const::from_constval(bcx.ccx, value.clone(), switch_ty); bcx.cond_br(discr.immediate(), llfalse, lltrue);
let llbb = llblock(self, *target); } else {
bcx.add_case(switch, val.llval, llbb) bcx.cond_br(discr.immediate(), lltrue, llfalse);
}
} else {
let (otherwise, targets) = targets.split_last().unwrap();
let switch = bcx.switch(discr.immediate(),
llblock(self, *otherwise), values.len());
for (value, target) in values.iter().zip(targets) {
let val = Const::from_constint(bcx.ccx, value);
let llbb = llblock(self, *target);
bcx.add_case(switch, val.llval, llbb)
}
} }
} }

View file

@ -61,6 +61,33 @@ impl<'tcx> Const<'tcx> {
} }
} }
pub fn from_constint<'a>(ccx: &CrateContext<'a, 'tcx>, ci: &ConstInt)
-> Const<'tcx> {
let tcx = ccx.tcx();
let (llval, ty) = match *ci {
I8(v) => (C_integral(Type::i8(ccx), v as u64, true), tcx.types.i8),
I16(v) => (C_integral(Type::i16(ccx), v as u64, true), tcx.types.i16),
I32(v) => (C_integral(Type::i32(ccx), v as u64, true), tcx.types.i32),
I64(v) => (C_integral(Type::i64(ccx), v as u64, true), tcx.types.i64),
I128(v) => (C_big_integral(Type::i128(ccx), v as u128), tcx.types.i128),
Isize(v) => {
let i = v.as_i64(ccx.tcx().sess.target.int_type);
(C_integral(Type::int(ccx), i as u64, true), tcx.types.isize)
},
U8(v) => (C_integral(Type::i8(ccx), v as u64, false), tcx.types.u8),
U16(v) => (C_integral(Type::i16(ccx), v as u64, false), tcx.types.u16),
U32(v) => (C_integral(Type::i32(ccx), v as u64, false), tcx.types.u32),
U64(v) => (C_integral(Type::i64(ccx), v, false), tcx.types.u64),
U128(v) => (C_big_integral(Type::i128(ccx), v), tcx.types.u128),
Usize(v) => {
let u = v.as_u64(ccx.tcx().sess.target.uint_type);
(C_integral(Type::int(ccx), u, false), tcx.types.usize)
},
Infer(_) | InferSigned(_) => bug!("MIR must not use `{:?}`", ci),
};
Const { llval: llval, ty: ty }
}
/// Translate ConstVal into a LLVM constant value. /// Translate ConstVal into a LLVM constant value.
pub fn from_constval<'a>(ccx: &CrateContext<'a, 'tcx>, pub fn from_constval<'a>(ccx: &CrateContext<'a, 'tcx>,
cv: ConstVal, cv: ConstVal,
@ -72,26 +99,7 @@ impl<'tcx> Const<'tcx> {
ConstVal::Float(F64(v)) => C_floating_f64(v, llty), ConstVal::Float(F64(v)) => C_floating_f64(v, llty),
ConstVal::Float(FInfer {..}) => bug!("MIR must not use `{:?}`", cv), ConstVal::Float(FInfer {..}) => bug!("MIR must not use `{:?}`", cv),
ConstVal::Bool(v) => C_bool(ccx, v), ConstVal::Bool(v) => C_bool(ccx, v),
ConstVal::Integral(I8(v)) => C_integral(Type::i8(ccx), v as u64, true), ConstVal::Integral(ref i) => return Const::from_constint(ccx, i),
ConstVal::Integral(I16(v)) => C_integral(Type::i16(ccx), v as u64, true),
ConstVal::Integral(I32(v)) => C_integral(Type::i32(ccx), v as u64, true),
ConstVal::Integral(I64(v)) => C_integral(Type::i64(ccx), v as u64, true),
ConstVal::Integral(I128(v)) => C_big_integral(Type::i128(ccx), v as u128),
ConstVal::Integral(Isize(v)) => {
let i = v.as_i64(ccx.tcx().sess.target.int_type);
C_integral(Type::int(ccx), i as u64, true)
},
ConstVal::Integral(U8(v)) => C_integral(Type::i8(ccx), v as u64, false),
ConstVal::Integral(U16(v)) => C_integral(Type::i16(ccx), v as u64, false),
ConstVal::Integral(U32(v)) => C_integral(Type::i32(ccx), v as u64, false),
ConstVal::Integral(U64(v)) => C_integral(Type::i64(ccx), v, false),
ConstVal::Integral(U128(v)) => C_big_integral(Type::i128(ccx), v),
ConstVal::Integral(Usize(v)) => {
let u = v.as_u64(ccx.tcx().sess.target.uint_type);
C_integral(Type::int(ccx), u, false)
},
ConstVal::Integral(Infer(_)) |
ConstVal::Integral(InferSigned(_)) => bug!("MIR must not use `{:?}`", cv),
ConstVal::Str(ref v) => C_str_slice(ccx, v.clone()), ConstVal::Str(ref v) => C_str_slice(ccx, v.clone()),
ConstVal::ByteStr(ref v) => consts::addr_of(ccx, C_bytes(ccx, v), 1, "byte_str"), ConstVal::ByteStr(ref v) => consts::addr_of(ccx, C_bytes(ccx, v), 1, "byte_str"),
ConstVal::Struct(_) | ConstVal::Tuple(_) | ConstVal::Struct(_) | ConstVal::Tuple(_) |

View file

@ -567,6 +567,34 @@ impl<T:Decodable> Decodable for Vec<T> {
} }
} }
impl<'a, T:Encodable> Encodable for Cow<'a, [T]>
where [T]: ToOwned<Owned = Vec<T>>
{
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
s.emit_seq(self.len(), |s| {
for (i, e) in self.iter().enumerate() {
s.emit_seq_elt(i, |s| e.encode(s))?
}
Ok(())
})
}
}
impl<T:Decodable+ToOwned> Decodable for Cow<'static, [T]>
where [T]: ToOwned<Owned = Vec<T>>
{
fn decode<D: Decoder>(d: &mut D) -> Result<Cow<'static, [T]>, D::Error> {
d.read_seq(|d, len| {
let mut v = Vec::with_capacity(len);
for i in 0..len {
v.push(d.read_seq_elt(i, |d| Decodable::decode(d))?);
}
Ok(Cow::Owned(v))
})
}
}
impl<T:Encodable> Encodable for Option<T> { impl<T:Encodable> Encodable for Option<T> {
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> { fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
s.emit_option(|s| { s.emit_option(|s| {