librustc: Implement fast-ffi and use it in various places
This commit is contained in:
parent
1a36b0f17e
commit
f903ae9e72
@ -1109,8 +1109,10 @@ pub mod funcs {
|
||||
// Omitted: putc, putchar (might be macros).
|
||||
unsafe fn puts(s: *c_char) -> c_int;
|
||||
unsafe fn ungetc(c: c_int, stream: *FILE) -> c_int;
|
||||
#[fast_ffi]
|
||||
unsafe fn fread(ptr: *mut c_void, size: size_t,
|
||||
nobj: size_t, stream: *FILE) -> size_t;
|
||||
#[fast_ffi]
|
||||
unsafe fn fwrite(ptr: *c_void, size: size_t,
|
||||
nobj: size_t, stream: *FILE) -> size_t;
|
||||
unsafe fn fseek(stream: *FILE, offset: c_long,
|
||||
@ -1144,9 +1146,13 @@ pub mod funcs {
|
||||
-> c_long;
|
||||
unsafe fn strtoul(s: *c_char, endp: **c_char, base: c_int)
|
||||
-> c_ulong;
|
||||
#[fast_ffi]
|
||||
unsafe fn calloc(nobj: size_t, size: size_t) -> *c_void;
|
||||
#[fast_ffi]
|
||||
unsafe fn malloc(size: size_t) -> *c_void;
|
||||
#[fast_ffi]
|
||||
unsafe fn realloc(p: *c_void, size: size_t) -> *c_void;
|
||||
#[fast_ffi]
|
||||
unsafe fn free(p: *c_void);
|
||||
unsafe fn abort() -> !;
|
||||
unsafe fn exit(status: c_int) -> !;
|
||||
@ -1340,6 +1346,7 @@ pub mod funcs {
|
||||
textmode: c_int) -> c_int;
|
||||
|
||||
#[link_name = "_read"]
|
||||
#[fast_ffi]
|
||||
unsafe fn read(fd: c_int, buf: *mut c_void, count: c_uint)
|
||||
-> c_int;
|
||||
|
||||
@ -1350,6 +1357,7 @@ pub mod funcs {
|
||||
unsafe fn unlink(c: *c_char) -> c_int;
|
||||
|
||||
#[link_name = "_write"]
|
||||
#[fast_ffi]
|
||||
unsafe fn write(fd: c_int, buf: *c_void, count: c_uint)
|
||||
-> c_int;
|
||||
}
|
||||
@ -1502,6 +1510,7 @@ pub mod funcs {
|
||||
unsafe fn pathconf(path: *c_char, name: c_int) -> c_long;
|
||||
unsafe fn pause() -> c_int;
|
||||
unsafe fn pipe(fds: *mut c_int) -> c_int;
|
||||
#[fast_ffi]
|
||||
unsafe fn read(fd: c_int, buf: *mut c_void,
|
||||
count: size_t) -> ssize_t;
|
||||
unsafe fn rmdir(path: *c_char) -> c_int;
|
||||
@ -1514,6 +1523,7 @@ pub mod funcs {
|
||||
unsafe fn tcgetpgrp(fd: c_int) -> pid_t;
|
||||
unsafe fn ttyname(fd: c_int) -> *c_char;
|
||||
unsafe fn unlink(c: *c_char) -> c_int;
|
||||
#[fast_ffi]
|
||||
unsafe fn write(fd: c_int, buf: *c_void, count: size_t)
|
||||
-> ssize_t;
|
||||
}
|
||||
|
@ -35,6 +35,14 @@ pub mod rustrt {
|
||||
|
||||
#[rust_stack]
|
||||
unsafe fn rust_upcall_free(ptr: *c_char);
|
||||
|
||||
#[fast_ffi]
|
||||
unsafe fn rust_upcall_malloc_noswitch(td: *c_char,
|
||||
size: uintptr_t)
|
||||
-> *c_char;
|
||||
|
||||
#[fast_ffi]
|
||||
unsafe fn rust_upcall_free_noswitch(ptr: *c_char);
|
||||
}
|
||||
}
|
||||
|
||||
@ -81,7 +89,7 @@ pub unsafe fn exchange_free(ptr: *c_char) {
|
||||
#[lang="malloc"]
|
||||
#[inline(always)]
|
||||
pub unsafe fn local_malloc(td: *c_char, size: uintptr_t) -> *c_char {
|
||||
return rustrt::rust_upcall_malloc(td, size);
|
||||
return rustrt::rust_upcall_malloc_noswitch(td, size);
|
||||
}
|
||||
|
||||
// NB: Calls to free CANNOT be allowed to fail, as throwing an exception from
|
||||
@ -90,7 +98,7 @@ pub unsafe fn local_malloc(td: *c_char, size: uintptr_t) -> *c_char {
|
||||
#[lang="free"]
|
||||
#[inline(always)]
|
||||
pub unsafe fn local_free(ptr: *c_char) {
|
||||
rustrt::rust_upcall_free(ptr);
|
||||
rustrt::rust_upcall_free_noswitch(ptr);
|
||||
}
|
||||
|
||||
#[lang="borrow_as_imm"]
|
||||
|
@ -43,9 +43,11 @@ pub mod rustrt {
|
||||
pub extern {
|
||||
// These names are terrible. reserve_shared applies
|
||||
// to ~[] and reserve_shared_actual applies to @[].
|
||||
#[fast_ffi]
|
||||
unsafe fn vec_reserve_shared(++t: *sys::TypeDesc,
|
||||
++v: **raw::VecRepr,
|
||||
++n: libc::size_t);
|
||||
#[fast_ffi]
|
||||
unsafe fn vec_reserve_shared_actual(++t: *sys::TypeDesc,
|
||||
++v: **raw::VecRepr,
|
||||
++n: libc::size_t);
|
||||
|
@ -172,10 +172,13 @@ pub enum compile_upto {
|
||||
|
||||
// For continuing compilation after a parsed crate has been
|
||||
// modified
|
||||
pub fn compile_rest(sess: Session, cfg: ast::crate_cfg,
|
||||
upto: compile_upto, outputs: Option<@OutputFilenames>,
|
||||
#[fixed_stack_segment]
|
||||
pub fn compile_rest(sess: Session,
|
||||
cfg: ast::crate_cfg,
|
||||
upto: compile_upto,
|
||||
outputs: Option<@OutputFilenames>,
|
||||
curr: Option<@ast::crate>)
|
||||
-> (@ast::crate, Option<ty::ctxt>) {
|
||||
-> (@ast::crate, Option<ty::ctxt>) {
|
||||
let time_passes = sess.time_passes();
|
||||
let mut crate = curr.get();
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1336,17 +1336,7 @@ fn roundtrip(in_item: Option<@ast::item>) {
|
||||
let ebml_doc = reader::Doc(@bytes);
|
||||
let out_item = decode_item_ast(ebml_doc);
|
||||
|
||||
let exp_str = do io::with_str_writer |w| {
|
||||
in_item.encode(&prettyprint::Serializer(w))
|
||||
};
|
||||
let out_str = do io::with_str_writer |w| {
|
||||
out_item.encode(&prettyprint::Serializer(w))
|
||||
};
|
||||
|
||||
debug!("expected string: %s", exp_str);
|
||||
debug!("actual string : %s", out_str);
|
||||
|
||||
assert!(exp_str == out_str);
|
||||
assert_eq!(in_item, out_item);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -399,24 +399,24 @@ pub fn set_optimize_for_size(f: ValueRef) {
|
||||
unsafe {
|
||||
llvm::LLVMAddFunctionAttr(f,
|
||||
lib::llvm::OptimizeForSizeAttribute
|
||||
as c_ulonglong,
|
||||
0u as c_ulonglong);
|
||||
as c_uint,
|
||||
0);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_no_inline(f: ValueRef) {
|
||||
unsafe {
|
||||
llvm::LLVMAddFunctionAttr(f,
|
||||
lib::llvm::NoInlineAttribute as c_ulonglong,
|
||||
0u as c_ulonglong);
|
||||
lib::llvm::NoInlineAttribute as c_uint,
|
||||
0);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_no_unwind(f: ValueRef) {
|
||||
unsafe {
|
||||
llvm::LLVMAddFunctionAttr(f,
|
||||
lib::llvm::NoUnwindAttribute as c_ulonglong,
|
||||
0u as c_ulonglong);
|
||||
lib::llvm::NoUnwindAttribute as c_uint,
|
||||
0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -425,15 +425,16 @@ pub fn set_no_unwind(f: ValueRef) {
|
||||
pub fn set_uwtable(f: ValueRef) {
|
||||
unsafe {
|
||||
llvm::LLVMAddFunctionAttr(f,
|
||||
lib::llvm::UWTableAttribute as c_ulonglong,
|
||||
0u as c_ulonglong);
|
||||
lib::llvm::UWTableAttribute as c_uint,
|
||||
0);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_inline_hint(f: ValueRef) {
|
||||
unsafe {
|
||||
llvm::LLVMAddFunctionAttr(f, lib::llvm::InlineHintAttribute
|
||||
as c_ulonglong, 0u as c_ulonglong);
|
||||
llvm::LLVMAddFunctionAttr(f,
|
||||
lib::llvm::InlineHintAttribute as c_uint,
|
||||
0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -449,14 +450,15 @@ pub fn set_inline_hint_if_appr(attrs: &[ast::attribute],
|
||||
|
||||
pub fn set_always_inline(f: ValueRef) {
|
||||
unsafe {
|
||||
llvm::LLVMAddFunctionAttr(f, lib::llvm::AlwaysInlineAttribute
|
||||
as c_ulonglong, 0u as c_ulonglong);
|
||||
llvm::LLVMAddFunctionAttr(f,
|
||||
lib::llvm::AlwaysInlineAttribute as c_uint,
|
||||
0);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_custom_stack_growth_fn(f: ValueRef) {
|
||||
pub fn set_fixed_stack_segment(f: ValueRef) {
|
||||
unsafe {
|
||||
llvm::LLVMAddFunctionAttr(f, 0u as c_ulonglong, 1u as c_ulonglong);
|
||||
llvm::LLVMAddFunctionAttr(f, 0, 1 << (39 - 32));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1774,6 +1776,7 @@ pub fn trans_closure(ccx: @CrateContext,
|
||||
param_substs: Option<@param_substs>,
|
||||
id: ast::node_id,
|
||||
impl_id: Option<ast::def_id>,
|
||||
attributes: &[ast::attribute],
|
||||
maybe_load_env: &fn(fn_ctxt),
|
||||
finish: &fn(block)) {
|
||||
ccx.stats.n_closures += 1;
|
||||
@ -1784,10 +1787,20 @@ pub fn trans_closure(ccx: @CrateContext,
|
||||
param_substs.repr(ccx.tcx));
|
||||
|
||||
// Set up arguments to the function.
|
||||
let fcx = new_fn_ctxt_w_id(ccx, path, llfndecl, id, impl_id, param_substs,
|
||||
Some(body.span));
|
||||
let raw_llargs = create_llargs_for_fn_args(fcx, ty_self,
|
||||
decl.inputs);
|
||||
let fcx = new_fn_ctxt_w_id(ccx,
|
||||
path,
|
||||
llfndecl,
|
||||
id,
|
||||
impl_id,
|
||||
param_substs,
|
||||
Some(body.span));
|
||||
let raw_llargs = create_llargs_for_fn_args(fcx, ty_self, decl.inputs);
|
||||
|
||||
// Set the fixed stack segment flag if necessary.
|
||||
if attr::attrs_contains_name(attributes, "fixed_stack_segment") {
|
||||
set_no_inline(fcx.llfn);
|
||||
set_fixed_stack_segment(fcx.llfn);
|
||||
}
|
||||
|
||||
// Set GC for function.
|
||||
if ccx.sess.opts.gc {
|
||||
@ -1840,7 +1853,8 @@ pub fn trans_fn(ccx: @CrateContext,
|
||||
ty_self: self_arg,
|
||||
param_substs: Option<@param_substs>,
|
||||
id: ast::node_id,
|
||||
impl_id: Option<ast::def_id>) {
|
||||
impl_id: Option<ast::def_id>,
|
||||
attrs: &[ast::attribute]) {
|
||||
let do_time = ccx.sess.trans_stats();
|
||||
let start = if do_time { time::get_time() }
|
||||
else { time::Timespec::new(0, 0) };
|
||||
@ -1850,8 +1864,16 @@ pub fn trans_fn(ccx: @CrateContext,
|
||||
let _icx = ccx.insn_ctxt("trans_fn");
|
||||
ccx.stats.n_fns += 1;
|
||||
let the_path_str = path_str(ccx.sess, path);
|
||||
trans_closure(ccx, path, decl, body, llfndecl, ty_self,
|
||||
param_substs, id, impl_id,
|
||||
trans_closure(ccx,
|
||||
path,
|
||||
decl,
|
||||
body,
|
||||
llfndecl,
|
||||
ty_self,
|
||||
param_substs,
|
||||
id,
|
||||
impl_id,
|
||||
attrs,
|
||||
|fcx| {
|
||||
if ccx.sess.opts.extra_debuginfo {
|
||||
debuginfo::create_function(fcx);
|
||||
@ -2023,8 +2045,16 @@ pub fn trans_struct_dtor(ccx: @CrateContext,
|
||||
}
|
||||
/* Translate the dtor body */
|
||||
let decl = ast_util::dtor_dec();
|
||||
trans_fn(ccx, path, &decl, body, lldecl,
|
||||
impl_self(class_ty), psubsts, dtor_id, None);
|
||||
trans_fn(ccx,
|
||||
path,
|
||||
&decl,
|
||||
body,
|
||||
lldecl,
|
||||
impl_self(class_ty),
|
||||
psubsts,
|
||||
dtor_id,
|
||||
None,
|
||||
[]);
|
||||
lldecl
|
||||
}
|
||||
|
||||
@ -2073,7 +2103,14 @@ pub fn trans_item(ccx: @CrateContext, item: ast::item) {
|
||||
let llfndecl = get_item_val(ccx, item.id);
|
||||
trans_fn(ccx,
|
||||
vec::append(/*bad*/copy *path, ~[path_name(item.ident)]),
|
||||
decl, body, llfndecl, no_self, None, item.id, None);
|
||||
decl,
|
||||
body,
|
||||
llfndecl,
|
||||
no_self,
|
||||
None,
|
||||
item.id,
|
||||
None,
|
||||
item.attrs);
|
||||
} else {
|
||||
for body.node.stmts.each |stmt| {
|
||||
match stmt.node {
|
||||
|
@ -416,9 +416,16 @@ pub fn trans_expr_fn(bcx: block,
|
||||
None => None};
|
||||
let ClosureResult {llbox, cdata_ty, bcx}
|
||||
= build_closure(bcx, cap_vars, sigil, ret_handle);
|
||||
trans_closure(ccx, sub_path, decl,
|
||||
body, llfn, no_self,
|
||||
/*bad*/ copy bcx.fcx.param_substs, user_id, None,
|
||||
trans_closure(ccx,
|
||||
sub_path,
|
||||
decl,
|
||||
body,
|
||||
llfn,
|
||||
no_self,
|
||||
/*bad*/ copy bcx.fcx.param_substs,
|
||||
user_id,
|
||||
None,
|
||||
[],
|
||||
|fcx| load_environment(fcx, cdata_ty, cap_vars,
|
||||
ret_handle.is_some(), sigil),
|
||||
|bcx| {
|
||||
|
@ -316,11 +316,11 @@ pub fn trans_foreign_mod(ccx: @CrateContext,
|
||||
{
|
||||
let llwrapfn = get_item_val(ccx, id);
|
||||
let tys = shim_types(ccx, id);
|
||||
if attr::attrs_contains_name(
|
||||
foreign_item.attrs, "rust_stack")
|
||||
{
|
||||
if attr::attrs_contains_name(foreign_item.attrs, "rust_stack") {
|
||||
build_direct_fn(ccx, llwrapfn, foreign_item,
|
||||
&tys, cc);
|
||||
} else if attr::attrs_contains_name(foreign_item.attrs, "fast_ffi") {
|
||||
build_fast_ffi_fn(ccx, llwrapfn, foreign_item, &tys, cc);
|
||||
} else {
|
||||
let llshimfn = build_shim_fn(ccx, foreign_item,
|
||||
&tys, cc);
|
||||
@ -380,6 +380,8 @@ pub fn trans_foreign_mod(ccx: @CrateContext,
|
||||
fn build_direct_fn(ccx: @CrateContext, decl: ValueRef,
|
||||
item: @ast::foreign_item, tys: &ShimTypes,
|
||||
cc: lib::llvm::CallConv) {
|
||||
debug!("build_direct_fn(%s)", *link_name(ccx, item));
|
||||
|
||||
let fcx = new_fn_ctxt(ccx, ~[], decl, None);
|
||||
let bcx = top_scope_block(fcx, None), lltop = bcx.llbb;
|
||||
let llbasefn = base_fn(ccx, *link_name(ccx, item), tys, cc);
|
||||
@ -389,7 +391,36 @@ pub fn trans_foreign_mod(ccx: @CrateContext,
|
||||
get_param(decl, i + first_real_arg)
|
||||
});
|
||||
let retval = Call(bcx, llbasefn, args);
|
||||
if !ty::type_is_nil(ty::ty_fn_ret(ty)) {
|
||||
let ret_ty = ty::ty_fn_ret(ty);
|
||||
if !ty::type_is_nil(ret_ty) && !ty::type_is_bot(ret_ty) {
|
||||
Store(bcx, retval, fcx.llretptr);
|
||||
}
|
||||
build_return(bcx);
|
||||
finish_fn(fcx, lltop);
|
||||
}
|
||||
|
||||
// FIXME (#2535): this is very shaky and probably gets ABIs wrong all
|
||||
// over the place
|
||||
fn build_fast_ffi_fn(ccx: @CrateContext,
|
||||
decl: ValueRef,
|
||||
item: @ast::foreign_item,
|
||||
tys: &ShimTypes,
|
||||
cc: lib::llvm::CallConv) {
|
||||
debug!("build_fast_ffi_fn(%s)", *link_name(ccx, item));
|
||||
|
||||
let fcx = new_fn_ctxt(ccx, ~[], decl, None);
|
||||
let bcx = top_scope_block(fcx, None), lltop = bcx.llbb;
|
||||
let llbasefn = base_fn(ccx, *link_name(ccx, item), tys, cc);
|
||||
set_no_inline(fcx.llfn);
|
||||
set_fixed_stack_segment(fcx.llfn);
|
||||
let ty = ty::lookup_item_type(ccx.tcx,
|
||||
ast_util::local_def(item.id)).ty;
|
||||
let args = vec::from_fn(ty::ty_fn_args(ty).len(), |i| {
|
||||
get_param(decl, i + first_real_arg)
|
||||
});
|
||||
let retval = Call(bcx, llbasefn, args);
|
||||
let ret_ty = ty::ty_fn_ret(ty);
|
||||
if !ty::type_is_nil(ret_ty) && !ty::type_is_bot(ret_ty) {
|
||||
Store(bcx, retval, fcx.llretptr);
|
||||
}
|
||||
build_return(bcx);
|
||||
@ -1006,7 +1037,16 @@ pub fn trans_foreign_fn(ccx: @CrateContext,
|
||||
)));
|
||||
let llty = type_of_fn_from_ty(ccx, t);
|
||||
let llfndecl = decl_internal_cdecl_fn(ccx.llmod, ps, llty);
|
||||
trans_fn(ccx, path, decl, body, llfndecl, no_self, None, id, None);
|
||||
trans_fn(ccx,
|
||||
path,
|
||||
decl,
|
||||
body,
|
||||
llfndecl,
|
||||
no_self,
|
||||
None,
|
||||
id,
|
||||
None,
|
||||
[]);
|
||||
return llfndecl;
|
||||
}
|
||||
|
||||
|
@ -116,7 +116,8 @@ pub fn maybe_instantiate_inline(ccx: @CrateContext, fn_id: ast::def_id,
|
||||
self_kind,
|
||||
None,
|
||||
mth.id,
|
||||
Some(impl_did));
|
||||
Some(impl_did),
|
||||
[]);
|
||||
}
|
||||
local_def(mth.id)
|
||||
}
|
||||
|
@ -137,7 +137,8 @@ pub fn trans_method(ccx: @CrateContext,
|
||||
self_arg,
|
||||
param_substs,
|
||||
method.id,
|
||||
Some(impl_id));
|
||||
Some(impl_id),
|
||||
[]);
|
||||
}
|
||||
|
||||
pub fn trans_self_arg(bcx: block,
|
||||
|
@ -195,7 +195,16 @@ pub fn monomorphic_fn(ccx: @CrateContext,
|
||||
}, _) => {
|
||||
let d = mk_lldecl();
|
||||
set_inline_hint_if_appr(/*bad*/copy i.attrs, d);
|
||||
trans_fn(ccx, pt, decl, body, d, no_self, psubsts, fn_id.node, None);
|
||||
trans_fn(ccx,
|
||||
pt,
|
||||
decl,
|
||||
body,
|
||||
d,
|
||||
no_self,
|
||||
psubsts,
|
||||
fn_id.node,
|
||||
None,
|
||||
[]);
|
||||
d
|
||||
}
|
||||
ast_map::node_item(*) => {
|
||||
|
@ -457,8 +457,9 @@ rust_task::get_next_stack_size(size_t min, size_t current, size_t requested) {
|
||||
"min: %" PRIdPTR " current: %" PRIdPTR " requested: %" PRIdPTR,
|
||||
min, current, requested);
|
||||
|
||||
// Allocate at least enough to accomodate the next frame
|
||||
size_t sz = std::max(min, requested);
|
||||
// Allocate at least enough to accomodate the next frame, plus a little
|
||||
// slack to avoid thrashing
|
||||
size_t sz = std::max(min, requested + (requested / 2));
|
||||
|
||||
// And double the stack size each allocation
|
||||
const size_t max = 1024 * 1024;
|
||||
|
@ -191,6 +191,14 @@ rust_upcall_malloc(type_desc *td, uintptr_t size) {
|
||||
return upcall_malloc(td, size);
|
||||
}
|
||||
|
||||
extern "C" CDECL uintptr_t
|
||||
rust_upcall_malloc_noswitch(type_desc *td, uintptr_t size) {
|
||||
rust_task *task = rust_get_current_task();
|
||||
s_malloc_args args = {task, 0, td, size};
|
||||
upcall_s_malloc(&args);
|
||||
return args.retval;
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
* Called whenever an object in the task-local heap is freed.
|
||||
*/
|
||||
@ -231,6 +239,13 @@ rust_upcall_free(void* ptr) {
|
||||
upcall_free(ptr);
|
||||
}
|
||||
|
||||
extern "C" CDECL void
|
||||
rust_upcall_free_noswitch(void* ptr) {
|
||||
rust_task *task = rust_get_current_task();
|
||||
s_free_args args = {task,ptr};
|
||||
upcall_s_free(&args);
|
||||
}
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
extern "C" _Unwind_Reason_Code
|
||||
|
@ -66,7 +66,9 @@ upcall_del_stack
|
||||
upcall_reset_stack_limit
|
||||
rust_upcall_fail
|
||||
rust_upcall_free
|
||||
rust_upcall_free_noswitch
|
||||
rust_upcall_malloc
|
||||
rust_upcall_malloc_noswitch
|
||||
rust_uv_loop_new
|
||||
rust_uv_loop_delete
|
||||
rust_uv_walk
|
||||
|
@ -451,6 +451,7 @@ LLVMRustWriteOutputFile(LLVMPassManagerRef PMR,
|
||||
TargetOptions Options;
|
||||
Options.NoFramePointerElim = true;
|
||||
Options.EnableSegmentedStacks = EnableSegmentedStacks;
|
||||
Options.FixedStackSegmentSize = 2 * 1024 * 1024; // XXX: This is too big.
|
||||
|
||||
PassManager *PM = unwrap<PassManager>(PMR);
|
||||
|
||||
|
@ -25,20 +25,6 @@ use std::ebml;
|
||||
use std::serialize::{Decodable, Encodable};
|
||||
use std::time;
|
||||
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
fn test_prettyprint<A:Encodable<prettyprint::Serializer>>(
|
||||
a: &A,
|
||||
expected: &~str
|
||||
) {
|
||||
let s = do io::with_str_writer |w| {
|
||||
a.encode(&prettyprint::Serializer(w))
|
||||
};
|
||||
debug!("s == %?", s);
|
||||
assert!(s == *expected);
|
||||
}
|
||||
|
||||
>>>>>>> librustc: Remove `fail_unless!`
|
||||
fn test_ebml<A:
|
||||
Eq +
|
||||
Encodable<EBWriter::Encoder> +
|
||||
|
Loading…
x
Reference in New Issue
Block a user