Merge branch 'unwind'
Conflicts: src/comp/middle/trans.rs src/comp/middle/trans_build.rs src/lib/run_program.rs src/test/compiletest/runtest.rs
This commit is contained in:
commit
393deeb06f
49 changed files with 491 additions and 106 deletions
|
@ -114,7 +114,7 @@ ifdef CFG_UNIXY
|
|||
endif
|
||||
ifdef CFG_VALGRIND
|
||||
CFG_VALGRIND += --leak-check=full \
|
||||
--error-exitcode=1 \
|
||||
--error-exitcode=100 \
|
||||
--quiet --suppressions=$(CFG_SRC_DIR)src/etc/x86.supp
|
||||
endif
|
||||
endif
|
||||
|
|
|
@ -37,7 +37,8 @@ type upcalls =
|
|||
log_type: ValueRef,
|
||||
dynastack_mark: ValueRef,
|
||||
dynastack_alloc: ValueRef,
|
||||
dynastack_free: ValueRef};
|
||||
dynastack_free: ValueRef,
|
||||
rust_personality: ValueRef};
|
||||
|
||||
fn declare_upcalls(_tn: type_names, tydesc_type: TypeRef,
|
||||
taskptr_type: TypeRef, llmod: ModuleRef) -> @upcalls {
|
||||
|
@ -89,7 +90,9 @@ fn declare_upcalls(_tn: type_names, tydesc_type: TypeRef,
|
|||
dynastack_alloc:
|
||||
d("dynastack_alloc_2", [T_size_t(), T_ptr(tydesc_type)],
|
||||
T_ptr(T_i8())),
|
||||
dynastack_free: d("dynastack_free", [T_ptr(T_i8())], T_void())};
|
||||
dynastack_free: d("dynastack_free", [T_ptr(T_i8())], T_void()),
|
||||
rust_personality: dr("rust_personality", [], T_i32())
|
||||
};
|
||||
}
|
||||
//
|
||||
// Local Variables:
|
||||
|
|
|
@ -572,6 +572,9 @@ native "cdecl" mod llvm = "rustllvm" {
|
|||
fn LLVMBuildInvoke(B: BuilderRef, Fn: ValueRef, Args: *ValueRef,
|
||||
NumArgs: uint, Then: BasicBlockRef,
|
||||
Catch: BasicBlockRef, Name: sbuf) -> ValueRef;
|
||||
fn LLVMBuildLandingPad(B: BuilderRef, Ty: TypeRef, PersFn: ValueRef,
|
||||
NumClauses: uint, Name: sbuf) -> ValueRef;
|
||||
fn LLVMBuildResume(B: BuilderRef, Exn: ValueRef) -> ValueRef;
|
||||
fn LLVMBuildUnreachable(B: BuilderRef) -> ValueRef;
|
||||
|
||||
/* Add a case to the switch instruction */
|
||||
|
@ -580,6 +583,12 @@ native "cdecl" mod llvm = "rustllvm" {
|
|||
/* Add a destination to the indirectbr instruction */
|
||||
fn LLVMAddDestination(IndirectBr: ValueRef, Dest: BasicBlockRef);
|
||||
|
||||
/* Add a clause to the landing pad instruction */
|
||||
fn LLVMAddClause(LandingPad: ValueRef, ClauseVal: ValueRef);
|
||||
|
||||
/* Set the cleanup on a landing pad instruction */
|
||||
fn LLVMSetCleanup(LandingPad: ValueRef, Val: Bool);
|
||||
|
||||
/* Arithmetic */
|
||||
fn LLVMBuildAdd(B: BuilderRef, LHS: ValueRef, RHS: ValueRef, Name: sbuf)
|
||||
-> ValueRef;
|
||||
|
|
|
@ -3715,7 +3715,7 @@ fn trans_call(in_cx: @block_ctxt, f: @ast::expr,
|
|||
for the call itself is unreachable. */
|
||||
let retval = C_nil();
|
||||
if !is_terminated(bcx) {
|
||||
FastCall(bcx, faddr, llargs);
|
||||
bcx = invoke_fastcall(bcx, faddr, llargs).bcx;
|
||||
alt lliterbody {
|
||||
none. {
|
||||
if !ty::type_is_nil(bcx_tcx(cx), ret_ty) {
|
||||
|
@ -3748,6 +3748,67 @@ fn trans_call(in_cx: @block_ctxt, f: @ast::expr,
|
|||
ret rslt(bcx, retval);
|
||||
}
|
||||
|
||||
fn invoke(bcx: @block_ctxt, llfn: ValueRef,
|
||||
llargs: [ValueRef]) -> result {
|
||||
ret invoke_(bcx, llfn, llargs, Invoke);
|
||||
}
|
||||
|
||||
fn invoke_fastcall(bcx: @block_ctxt, llfn: ValueRef,
|
||||
llargs: [ValueRef]) -> result {
|
||||
ret invoke_(bcx, llfn, llargs, FastInvoke);
|
||||
}
|
||||
|
||||
fn invoke_(bcx: @block_ctxt, llfn: ValueRef,
|
||||
llargs: [ValueRef],
|
||||
invoker: fn(@block_ctxt, ValueRef, [ValueRef],
|
||||
BasicBlockRef, BasicBlockRef) -> ValueRef) -> result {
|
||||
// FIXME: May be worth turning this into a plain call when there are no
|
||||
// cleanups to run
|
||||
let normal_bcx = new_sub_block_ctxt(bcx, "normal return");
|
||||
let unwind_bcx = new_sub_block_ctxt(bcx, "unwind");
|
||||
let retval = invoker(bcx, llfn, llargs,
|
||||
normal_bcx.llbb,
|
||||
unwind_bcx.llbb);
|
||||
trans_landing_pad(unwind_bcx);
|
||||
ret rslt(normal_bcx, retval);
|
||||
}
|
||||
|
||||
fn trans_landing_pad(bcx: @block_ctxt) {
|
||||
// The landing pad return type (the type being propagated). Not sure what
|
||||
// this represents but it's determined by the personality function and
|
||||
// this is what the EH proposal example uses.
|
||||
let llretty = T_struct([T_ptr(T_i8()), T_i32()]);
|
||||
// The exception handling personality function. This is the C++
|
||||
// personality function __gxx_personality_v0, wrapped in our naming
|
||||
// convention.
|
||||
let personality = bcx_ccx(bcx).upcalls.rust_personality;
|
||||
// The only landing pad clause will be 'cleanup'
|
||||
let clauses = 1u;
|
||||
let llpad = LandingPad(bcx, llretty, personality, clauses);
|
||||
// The landing pad result is used both for modifying the landing pad
|
||||
// in the C API and as the exception value
|
||||
let llretval = llpad;
|
||||
// The landing pad block is a cleanup
|
||||
SetCleanup(bcx, llpad);
|
||||
|
||||
// FIXME: This seems like a very naive and redundant way to generate the
|
||||
// landing pads, as we're re-generating all in-scope cleanups for each
|
||||
// function call. Probably good optimization opportunities here.
|
||||
let bcx = bcx;
|
||||
let scope_cx = bcx;
|
||||
while true {
|
||||
scope_cx = find_scope_cx(scope_cx);
|
||||
bcx = trans_block_cleanups(bcx, scope_cx);
|
||||
scope_cx = alt scope_cx.parent {
|
||||
parent_some(b) { b }
|
||||
parent_none. { break; }
|
||||
};
|
||||
}
|
||||
|
||||
// Continue unwinding
|
||||
Resume(bcx, llretval);
|
||||
}
|
||||
|
||||
fn trans_tup(cx: @block_ctxt, elts: [@ast::expr], id: ast::node_id) ->
|
||||
result {
|
||||
let bcx = cx;
|
||||
|
@ -4211,7 +4272,7 @@ fn trans_fail_value(cx: @block_ctxt, sp_opt: option::t<span>,
|
|||
let V_str = PointerCast(cx, V_fail_str, T_ptr(T_i8()));
|
||||
V_filename = PointerCast(cx, V_filename, T_ptr(T_i8()));
|
||||
let args = [cx.fcx.lltaskptr, V_str, V_filename, C_int(V_line)];
|
||||
Call(cx, bcx_ccx(cx).upcalls._fail, args);
|
||||
let cx = invoke(cx, bcx_ccx(cx).upcalls._fail, args).bcx;
|
||||
Unreachable(cx);
|
||||
ret rslt(cx, C_nil());
|
||||
}
|
||||
|
@ -4247,7 +4308,7 @@ fn trans_put(in_cx: @block_ctxt, e: option::t<@ast::expr>) -> result {
|
|||
llargs += [r.val];
|
||||
}
|
||||
}
|
||||
FastCall(bcx, llcallee, llargs);
|
||||
bcx = invoke_fastcall(bcx, llcallee, llargs).bcx;
|
||||
bcx = trans_block_cleanups(bcx, cx);
|
||||
let next_cx = new_sub_block_ctxt(in_cx, "next");
|
||||
Br(bcx, next_cx.llbb);
|
||||
|
@ -4379,7 +4440,9 @@ fn init_local(bcx: @block_ctxt, local: @ast::local) -> result {
|
|||
// Make a note to drop this slot on the way out.
|
||||
add_clean(bcx, llptr, ty);
|
||||
|
||||
if must_zero(local) { bcx = zero_alloca(bcx, llptr, ty).bcx; }
|
||||
if must_zero(bcx_ccx(bcx), local) {
|
||||
bcx = zero_alloca(bcx, llptr, ty).bcx;
|
||||
}
|
||||
|
||||
alt local.node.init {
|
||||
some(init) {
|
||||
|
@ -4405,35 +4468,38 @@ fn init_local(bcx: @block_ctxt, local: @ast::local) -> result {
|
|||
bcx.fcx.lllocals, false);
|
||||
ret rslt(bcx, llptr);
|
||||
|
||||
fn must_zero(local: @ast::local) -> bool {
|
||||
fn must_zero(ccx: @crate_ctxt, local: @ast::local) -> bool {
|
||||
alt local.node.init {
|
||||
some(init) { might_not_init(init.expr) }
|
||||
some(init) { might_not_init(ccx, init.expr) }
|
||||
none. { true }
|
||||
}
|
||||
}
|
||||
|
||||
fn might_not_init(expr: @ast::expr) -> bool {
|
||||
type env = @mutable bool;
|
||||
let e = @mutable false;
|
||||
// FIXME: Probably also need to account for expressions that
|
||||
// fail but since we don't unwind yet, it doesn't seem to be a
|
||||
// problem
|
||||
fn might_not_init(ccx: @crate_ctxt, expr: @ast::expr) -> bool {
|
||||
type env = {mutable mightnt: bool,
|
||||
ccx: @crate_ctxt};
|
||||
let e = {mutable mightnt: false,
|
||||
ccx: ccx};
|
||||
fn visit_expr(ex: @ast::expr, e: env, v: vt<env>) {
|
||||
let might_not_init = alt ex.node {
|
||||
ast::expr_ret(_) { true }
|
||||
ast::expr_break. { true }
|
||||
ast::expr_cont. { true }
|
||||
ast::expr_call(_, _) { true }
|
||||
_ {
|
||||
let ex_ty = ty::expr_ty(e.ccx.tcx, ex);
|
||||
ty::type_is_bot(e.ccx.tcx, ex_ty)
|
||||
}
|
||||
};
|
||||
if might_not_init {
|
||||
e.mightnt = true;
|
||||
} else { visit::visit_expr(ex, e, v); }
|
||||
}
|
||||
let visitor =
|
||||
visit::mk_vt(@{visit_expr:
|
||||
fn (ex: @ast::expr, e: env, v: vt<env>) {
|
||||
let might_not_init =
|
||||
alt ex.node {
|
||||
ast::expr_ret(_) { true }
|
||||
ast::expr_break. { true }
|
||||
ast::expr_cont. { true }
|
||||
_ { false }
|
||||
};
|
||||
if might_not_init {
|
||||
*e = true;
|
||||
} else { visit::visit_expr(ex, e, v); }
|
||||
} with *visit::default_visitor()});
|
||||
visit::mk_vt(@{visit_expr: visit_expr
|
||||
with *visit::default_visitor()});
|
||||
visitor.visit_expr(expr, e, visitor);
|
||||
ret *e;
|
||||
ret e.mightnt;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -68,6 +68,21 @@ fn Invoke(cx: @block_ctxt, Fn: ValueRef, Args: [ValueRef],
|
|||
});
|
||||
}
|
||||
|
||||
fn FastInvoke(cx: @block_ctxt, Fn: ValueRef, Args: [ValueRef],
|
||||
Then: BasicBlockRef, Catch: BasicBlockRef) -> ValueRef {
|
||||
assert (!cx.terminated);
|
||||
cx.terminated = true;
|
||||
let v = str::as_buf("",
|
||||
{|buf|
|
||||
llvm::LLVMBuildInvoke(B(cx), Fn,
|
||||
vec::to_ptr(Args),
|
||||
vec::len(Args), Then,
|
||||
Catch, buf)
|
||||
});
|
||||
llvm::LLVMSetInstructionCallConv(v, lib::llvm::LLVMFastCallConv);
|
||||
ret v;
|
||||
}
|
||||
|
||||
fn Unreachable(cx: @block_ctxt) -> ValueRef {
|
||||
assert (!cx.terminated);
|
||||
cx.terminated = true;
|
||||
|
@ -527,6 +542,29 @@ fn Trap(cx: @block_ctxt) -> ValueRef {
|
|||
});
|
||||
}
|
||||
|
||||
fn LandingPad(cx: @block_ctxt, Ty: TypeRef, PersFn: ValueRef,
|
||||
NumClauses: uint) -> ValueRef {
|
||||
assert (!cx.terminated);
|
||||
ret str::as_buf("",
|
||||
{|buf|
|
||||
llvm::LLVMBuildLandingPad(B(cx),
|
||||
Ty,
|
||||
PersFn,
|
||||
NumClauses,
|
||||
buf)
|
||||
});
|
||||
}
|
||||
|
||||
fn SetCleanup(_cx: @block_ctxt, LandingPad: ValueRef) {
|
||||
llvm::LLVMSetCleanup(LandingPad, lib::llvm::True);
|
||||
}
|
||||
|
||||
fn Resume(cx: @block_ctxt, Exn: ValueRef) -> ValueRef {
|
||||
assert (!cx.terminated);
|
||||
cx.terminated = true;
|
||||
ret llvm::LLVMBuildResume(B(cx), Exn);
|
||||
}
|
||||
|
||||
//
|
||||
// Local Variables:
|
||||
// mode: rust
|
||||
|
|
|
@ -27,7 +27,8 @@ for t in os.listdir(run_pass):
|
|||
f = codecs.open(os.path.join(run_pass, t), "r", "utf8")
|
||||
s = f.read()
|
||||
if not ("xfail-test" in s or
|
||||
"xfail-fast" in s):
|
||||
"xfail-fast" in s or
|
||||
"xfail-win32" in s):
|
||||
stage2_tests.append(t)
|
||||
if "main(args: [str])" in s:
|
||||
take_args[t] = True
|
||||
|
|
|
@ -6,6 +6,7 @@ export run_program;
|
|||
export start_program;
|
||||
export program_output;
|
||||
export spawn_process;
|
||||
export waitpid;
|
||||
|
||||
native "rust" mod rustrt {
|
||||
fn rust_run_program(argv: *sbuf, in_fd: int, out_fd: int, err_fd: int) ->
|
||||
|
@ -33,7 +34,7 @@ fn spawn_process(prog: str, args: [str], in_fd: int, out_fd: int, err_fd: int)
|
|||
}
|
||||
|
||||
fn run_program(prog: str, args: [str]) -> int {
|
||||
ret os::waitpid(spawn_process(prog, args, 0, 0, 0));
|
||||
ret waitpid(spawn_process(prog, args, 0, 0, 0));
|
||||
}
|
||||
|
||||
type program =
|
||||
|
@ -87,7 +88,7 @@ fn start_program(prog: str, args: [str]) -> @program_res {
|
|||
if finished { ret 0; }
|
||||
finished = true;
|
||||
self.close_input();
|
||||
ret os::waitpid(pid);
|
||||
ret waitpid(pid);
|
||||
}
|
||||
fn destroy() {
|
||||
self.finish();
|
||||
|
@ -117,6 +118,44 @@ fn program_output(prog: str, args: [str]) ->
|
|||
out: read_all(pr.output()),
|
||||
err: read_all(pr.err())};
|
||||
}
|
||||
|
||||
/* Returns an exit status */
|
||||
#[cfg(target_os = "win32")]
|
||||
fn waitpid(pid: int) -> int {
|
||||
os::waitpid(pid)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg(target_os = "macos")]
|
||||
fn waitpid(pid: int) -> int {
|
||||
#[cfg(target_os = "linux")]
|
||||
fn WIFEXITED(status: int) -> bool {
|
||||
(status & 0xff) == 0
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn WIFEXITED(status: int) -> bool {
|
||||
(status & 0x7f) == 0
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn WEXITSTATUS(status: int) -> int {
|
||||
(status >> 8) & 0xff
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn WEXITSTATUS(status: int) -> int {
|
||||
status >> 8
|
||||
}
|
||||
|
||||
let status = os::waitpid(pid);
|
||||
ret if WIFEXITED(status) {
|
||||
WEXITSTATUS(status)
|
||||
} else {
|
||||
1
|
||||
};
|
||||
}
|
||||
|
||||
// Local Variables:
|
||||
// mode: rust
|
||||
// fill-column: 78;
|
||||
|
|
|
@ -26,7 +26,6 @@ export configure_test_task;
|
|||
export joinable;
|
||||
|
||||
native "rust" mod rustrt {
|
||||
fn hack_allow_leaks();
|
||||
fn sched_threads() -> uint;
|
||||
}
|
||||
|
||||
|
@ -324,12 +323,6 @@ fn configure_test_task() {
|
|||
// If this task fails we don't want that failure to propagate to the
|
||||
// test runner or else we couldn't keep running tests
|
||||
task::unsupervise();
|
||||
|
||||
// FIXME (236): Hack supreme - unwinding doesn't work yet so if this
|
||||
// task fails memory will not be freed correctly. This turns off the
|
||||
// sanity checks in the runtime's memory region for the task, so that
|
||||
// the test runner can continue.
|
||||
rustrt::hack_allow_leaks();
|
||||
}
|
||||
|
||||
// Local Variables:
|
||||
|
|
|
@ -15,13 +15,13 @@ memory_region::alloc_header *memory_region::get_header(void *mem) {
|
|||
memory_region::memory_region(rust_srv *srv, bool synchronized) :
|
||||
_srv(srv), _parent(NULL), _live_allocations(0),
|
||||
_detailed_leaks(srv->env->detailed_leaks),
|
||||
_synchronized(synchronized), _hack_allow_leaks(false) {
|
||||
_synchronized(synchronized) {
|
||||
}
|
||||
|
||||
memory_region::memory_region(memory_region *parent) :
|
||||
_srv(parent->_srv), _parent(parent), _live_allocations(0),
|
||||
_detailed_leaks(parent->_detailed_leaks),
|
||||
_synchronized(parent->_synchronized), _hack_allow_leaks(false) {
|
||||
_synchronized(parent->_synchronized) {
|
||||
}
|
||||
|
||||
void memory_region::add_alloc() {
|
||||
|
@ -127,18 +127,13 @@ memory_region::~memory_region() {
|
|||
assert(leak_count == _live_allocations);
|
||||
}
|
||||
#endif
|
||||
if (!_hack_allow_leaks && _live_allocations > 0) {
|
||||
if (_live_allocations > 0) {
|
||||
_srv->fatal(msg, __FILE__, __LINE__,
|
||||
"%d objects", _live_allocations);
|
||||
}
|
||||
if (_synchronized) { _lock.unlock(); }
|
||||
}
|
||||
|
||||
void
|
||||
memory_region::hack_allow_leaks() {
|
||||
_hack_allow_leaks = true;
|
||||
}
|
||||
|
||||
void
|
||||
memory_region::release_alloc(void *mem) {
|
||||
alloc_header *alloc = get_header(mem);
|
||||
|
|
|
@ -32,7 +32,6 @@ private:
|
|||
const bool _detailed_leaks;
|
||||
const bool _synchronized;
|
||||
lock_and_signal _lock;
|
||||
bool _hack_allow_leaks;
|
||||
|
||||
void add_alloc();
|
||||
void dec_alloc();
|
||||
|
@ -46,10 +45,6 @@ public:
|
|||
void *realloc(void *mem, size_t size);
|
||||
void free(void *mem);
|
||||
virtual ~memory_region();
|
||||
// FIXME (236: This is a temporary hack to allow failing tasks that leak
|
||||
// to not kill the entire process, which the test runner needs. Please
|
||||
// kill with prejudice once unwinding works.
|
||||
void hack_allow_leaks();
|
||||
|
||||
void release_alloc(void *mem);
|
||||
void claim_alloc(void *mem);
|
||||
|
|
|
@ -224,13 +224,6 @@ debug_opaque(rust_task *task, type_desc *t, uint8_t *front)
|
|||
}
|
||||
}
|
||||
|
||||
extern "C" CDECL void
|
||||
hack_allow_leaks(rust_task *task)
|
||||
{
|
||||
LOG(task, stdlib, "hack_allow_leaks");
|
||||
task->local_region.hack_allow_leaks();
|
||||
}
|
||||
|
||||
struct rust_box {
|
||||
RUST_REFCOUNTED(rust_box)
|
||||
|
||||
|
|
|
@ -99,6 +99,9 @@ static size_t const TIME_SLICE_IN_MS = 10;
|
|||
|
||||
static size_t const BUF_BYTES = 2048;
|
||||
|
||||
// The error status to use when the process fails
|
||||
#define PROC_FAIL_CODE 101;
|
||||
|
||||
// Every reference counted object should use this macro and initialize
|
||||
// ref_count.
|
||||
|
||||
|
|
|
@ -140,7 +140,7 @@ rust_kernel::fail() {
|
|||
// Runtime to terminate it in an unusual way" when trying to shutdown
|
||||
// cleanly.
|
||||
#if defined(__WIN32__)
|
||||
exit(1);
|
||||
exit(rval);
|
||||
#endif
|
||||
for(size_t i = 0; i < num_threads; ++i) {
|
||||
rust_scheduler *thread = threads[i];
|
||||
|
|
|
@ -71,7 +71,7 @@ rust_scheduler::fail() {
|
|||
log(NULL, log_err, "domain %s @0x%" PRIxPTR " root task failed",
|
||||
name, this);
|
||||
I(this, kernel->rval == 0);
|
||||
kernel->rval = 1;
|
||||
kernel->rval = PROC_FAIL_CODE;
|
||||
kernel->fail();
|
||||
}
|
||||
|
||||
|
|
|
@ -137,18 +137,6 @@ struct rust_closure_env {
|
|||
type_desc *td;
|
||||
};
|
||||
|
||||
extern "C" CDECL
|
||||
void task_exit(rust_closure_env *env, int rval, rust_task *task) {
|
||||
LOG(task, task, "task exited with value %d", rval);
|
||||
if(env) {
|
||||
// free the environment.
|
||||
I(task->sched, 1 == env->ref_count); // the ref count better be 1
|
||||
//env->td->drop_glue(NULL, task, NULL, env->td->first_param, env);
|
||||
//env->td->free_glue(NULL, task, NULL, env->td->first_param, env);
|
||||
task->free(env);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" CDECL
|
||||
void task_start_wrapper(spawn_args *a)
|
||||
{
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#include "rust_gc.h"
|
||||
#include "rust_internal.h"
|
||||
#include "rust_upcall.h"
|
||||
#include <stdint.h>
|
||||
#include <unwind.h>
|
||||
|
||||
// Upcalls.
|
||||
|
||||
|
@ -190,6 +192,26 @@ upcall_dynastack_free(rust_task *task, void *ptr) {
|
|||
return task->dynastack.free(ptr);
|
||||
}
|
||||
|
||||
extern "C" _Unwind_Reason_Code
|
||||
__gxx_personality_v0(int version,
|
||||
_Unwind_Action actions,
|
||||
uint64_t exception_class,
|
||||
_Unwind_Exception *ue_header,
|
||||
_Unwind_Context *context);
|
||||
|
||||
extern "C" _Unwind_Reason_Code
|
||||
upcall_rust_personality(int version,
|
||||
_Unwind_Action actions,
|
||||
uint64_t exception_class,
|
||||
_Unwind_Exception *ue_header,
|
||||
_Unwind_Context *context) {
|
||||
return __gxx_personality_v0(version,
|
||||
actions,
|
||||
exception_class,
|
||||
ue_header,
|
||||
context);
|
||||
}
|
||||
|
||||
//
|
||||
// Local Variables:
|
||||
// mode: C++
|
||||
|
|
|
@ -28,7 +28,6 @@ get_task_id
|
|||
get_task_pointer
|
||||
get_task_trampoline
|
||||
get_time
|
||||
hack_allow_leaks
|
||||
last_os_error
|
||||
leak
|
||||
migrate_alloc
|
||||
|
@ -78,5 +77,6 @@ upcall_vec_grow
|
|||
upcall_vec_push
|
||||
upcall_log_type
|
||||
upcall_malloc
|
||||
upcall_rust_personality
|
||||
upcall_shared_malloc
|
||||
upcall_shared_free
|
||||
|
|
|
@ -25,6 +25,7 @@ LLVMAddAttribute
|
|||
LLVMAddBasicAliasAnalysisPass
|
||||
LLVMAddCFGSimplificationPass
|
||||
LLVMAddCase
|
||||
LLVMAddClause
|
||||
LLVMAddConstantMergePass
|
||||
LLVMAddConstantPropagationPass
|
||||
LLVMAddCorrelatedValuePropagationPass
|
||||
|
@ -122,6 +123,7 @@ LLVMBuildIntToPtr
|
|||
LLVMBuildInvoke
|
||||
LLVMBuildIsNotNull
|
||||
LLVMBuildIsNull
|
||||
LLVMBuildLandingPad
|
||||
LLVMBuildLShr
|
||||
LLVMBuildLoad
|
||||
LLVMBuildMalloc
|
||||
|
@ -141,6 +143,7 @@ LLVMBuildPhi
|
|||
LLVMBuildPointerCast
|
||||
LLVMBuildPtrDiff
|
||||
LLVMBuildPtrToInt
|
||||
LLVMBuildResume
|
||||
LLVMBuildRet
|
||||
LLVMBuildRetVoid
|
||||
LLVMBuildSDiv
|
||||
|
@ -548,6 +551,7 @@ LLVMRunPassManager
|
|||
LLVMRunStaticConstructors
|
||||
LLVMRunStaticDestructors
|
||||
LLVMSetAlignment
|
||||
LLVMSetCleanup
|
||||
LLVMSetCurrentDebugLocation
|
||||
LLVMSetDataLayout
|
||||
LLVMSetFunctionCallConv
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// xfail-test
|
||||
// based on:
|
||||
// http://shootout.alioth.debian.org/u32/benchmark.php?test=nbody&lang=java
|
||||
|
||||
|
|
|
@ -110,8 +110,9 @@ fn run_tests(config: config) {
|
|||
let opts = test_opts(config);
|
||||
let cx = {config: config, procsrv: procsrv::mk()};
|
||||
let tests = make_tests(cx);
|
||||
test::run_tests_console_(opts, tests.tests, tests.to_task);
|
||||
let res = test::run_tests_console_(opts, tests.tests, tests.to_task);
|
||||
procsrv::close(cx.procsrv);
|
||||
if !res { fail "Some tests failed"; }
|
||||
}
|
||||
|
||||
fn test_opts(config: config) -> test::test_opts {
|
||||
|
|
|
@ -16,10 +16,7 @@ type test_props = {
|
|||
compile_flags: option::t<str>,
|
||||
// If present, the name of a file that this test should match when
|
||||
// pretty-printed
|
||||
pp_exact: option::t<str>,
|
||||
// FIXME: no-valgrind is a temporary directive until all of run-fail
|
||||
// is valgrind-clean
|
||||
no_valgrind: bool
|
||||
pp_exact: option::t<str>
|
||||
};
|
||||
|
||||
// Load any test directives embedded in the file
|
||||
|
@ -27,7 +24,6 @@ fn load_props(testfile: str) -> test_props {
|
|||
let error_patterns = [];
|
||||
let compile_flags = option::none;
|
||||
let pp_exact = option::none;
|
||||
let no_valgrind = false;
|
||||
for each ln: str in iter_header(testfile) {
|
||||
alt parse_error_pattern(ln) {
|
||||
option::some(ep) { error_patterns += [ep]; }
|
||||
|
@ -41,16 +37,11 @@ fn load_props(testfile: str) -> test_props {
|
|||
if option::is_none(pp_exact) {
|
||||
pp_exact = parse_pp_exact(ln, testfile);
|
||||
}
|
||||
|
||||
if no_valgrind == false {
|
||||
no_valgrind = parse_name_directive(ln, "no-valgrind");
|
||||
}
|
||||
}
|
||||
ret {
|
||||
error_patterns: error_patterns,
|
||||
compile_flags: compile_flags,
|
||||
pp_exact: pp_exact,
|
||||
no_valgrind: no_valgrind
|
||||
pp_exact: pp_exact
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -59,11 +50,16 @@ fn is_test_ignored(config: config, testfile: str) -> bool {
|
|||
for each ln: str in iter_header(testfile) {
|
||||
// FIXME: Can't return or break from iterator
|
||||
found = found || parse_name_directive(ln, "xfail-test");
|
||||
found = found || parse_name_directive(ln, xfail_target());
|
||||
if (config.mode == common::mode_pretty) {
|
||||
found = found || parse_name_directive(ln, "xfail-pretty");
|
||||
}
|
||||
}
|
||||
ret found;
|
||||
|
||||
fn xfail_target() -> str {
|
||||
"xfail-" + std::os::target_os()
|
||||
}
|
||||
}
|
||||
|
||||
iter iter_header(testfile: str) -> str {
|
||||
|
|
|
@ -66,7 +66,7 @@ fn run(handle: handle, lib_path: str, prog: str, args: [str],
|
|||
writeclose(resp.infd, input);
|
||||
let output = readclose(resp.outfd);
|
||||
let errput = readclose(resp.errfd);
|
||||
let status = os::waitpid(resp.pid);
|
||||
let status = run::waitpid(resp.pid);
|
||||
ret {status: status, out: output, err: errput};
|
||||
}
|
||||
|
||||
|
|
|
@ -51,19 +51,21 @@ fn run_rfail_test(cx: cx, props: test_props, testfile: str) {
|
|||
|
||||
procres = exec_compiled_test(cx, props, testfile);
|
||||
|
||||
if procres.status == 0 {
|
||||
fatal_procres("run-fail test didn't produce an error!", procres);
|
||||
}
|
||||
|
||||
// This is the value valgrind returns on failure
|
||||
// FIXME: Why is this value neither the value we pass to
|
||||
// valgrind as --error-exitcode (1), nor the value we see as the
|
||||
// exit code on the command-line (137)?
|
||||
const valgrind_err: int = 9;
|
||||
// The value our Makefile configures valgrind to return on failure
|
||||
const valgrind_err: int = 100;
|
||||
if procres.status == valgrind_err {
|
||||
fatal_procres("run-fail test isn't valgrind-clean!", procres);
|
||||
}
|
||||
|
||||
// The value the rust runtime returns on failure
|
||||
const rust_err: int = 101;
|
||||
if procres.status != rust_err {
|
||||
fatal_procres(
|
||||
#fmt("run-fail test produced the wrong error code: %d",
|
||||
procres.status),
|
||||
procres);
|
||||
}
|
||||
|
||||
check_error_patterns(props, testfile, procres);
|
||||
}
|
||||
|
||||
|
@ -251,10 +253,9 @@ fn make_exe_name(config: config, testfile: str) -> str {
|
|||
output_base_name(config, testfile) + os::exec_suffix()
|
||||
}
|
||||
|
||||
fn make_run_args(config: config, props: test_props, testfile: str) ->
|
||||
fn make_run_args(config: config, _props: test_props, testfile: str) ->
|
||||
procargs {
|
||||
let toolargs =
|
||||
if !props.no_valgrind {
|
||||
let toolargs = {
|
||||
// If we've got another tool to run under (valgrind),
|
||||
// then split apart its command
|
||||
let runtool =
|
||||
|
@ -263,7 +264,7 @@ fn make_run_args(config: config, props: test_props, testfile: str) ->
|
|||
option::none. { option::none }
|
||||
};
|
||||
split_maybe_args(runtool)
|
||||
} else { [] };
|
||||
};
|
||||
|
||||
let args = toolargs + [make_exe_name(config, testfile)];
|
||||
ret {prog: args[0], args: vec::slice(args, 1u, vec::len(args))};
|
||||
|
|
|
@ -1,3 +1,2 @@
|
|||
// error-pattern:wooooo
|
||||
// no-valgrind
|
||||
fn main() { let a = 1; if 1 == 1 { a = 2; } fail "woooo" + "o"; }
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
// error-pattern:meh
|
||||
// no-valgrind
|
||||
use std;
|
||||
|
||||
fn main() { let str_var: str = "meh"; fail #fmt["%s", str_var]; }
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
// -*- rust -*-
|
||||
|
||||
// error-pattern:1 == 2
|
||||
// no-valgrind
|
||||
|
||||
// xfail-test
|
||||
use std;
|
||||
import std::task;
|
||||
import std::comm::port;
|
||||
|
|
6
src/test/run-fail/unwind-assert.rs
Normal file
6
src/test/run-fail/unwind-assert.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
// error-pattern:fail
|
||||
|
||||
fn main() {
|
||||
let a = @0;
|
||||
assert false;
|
||||
}
|
10
src/test/run-fail/unwind-box.rs
Normal file
10
src/test/run-fail/unwind-box.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
// error-pattern:fail
|
||||
|
||||
fn failfn() {
|
||||
fail;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
@0;
|
||||
failfn();
|
||||
}
|
8
src/test/run-fail/unwind-check.rs
Normal file
8
src/test/run-fail/unwind-check.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
// error-pattern:fail
|
||||
|
||||
pure fn p(a: @int) -> bool { false }
|
||||
|
||||
fn main() {
|
||||
let a = @0;
|
||||
check p(a);
|
||||
}
|
10
src/test/run-fail/unwind-closure.rs
Normal file
10
src/test/run-fail/unwind-closure.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
// error-pattern:fail
|
||||
|
||||
fn f(a: @int) {
|
||||
fail;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let g = bind f(@0);
|
||||
g();
|
||||
}
|
6
src/test/run-fail/unwind-fail.rs
Normal file
6
src/test/run-fail/unwind-fail.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
// error-pattern:fail
|
||||
|
||||
fn main() {
|
||||
@0;
|
||||
fail;
|
||||
}
|
7
src/test/run-fail/unwind-initializer-indirect.rs
Normal file
7
src/test/run-fail/unwind-initializer-indirect.rs
Normal file
|
@ -0,0 +1,7 @@
|
|||
// error-pattern:fail
|
||||
|
||||
fn f() -> @int { fail; }
|
||||
|
||||
fn main() {
|
||||
let a: @int = f();
|
||||
}
|
7
src/test/run-fail/unwind-initializer.rs
Normal file
7
src/test/run-fail/unwind-initializer.rs
Normal file
|
@ -0,0 +1,7 @@
|
|||
// error-pattern:fail
|
||||
|
||||
fn main() {
|
||||
let a: @int = {
|
||||
fail;
|
||||
};
|
||||
}
|
12
src/test/run-fail/unwind-iter.rs
Normal file
12
src/test/run-fail/unwind-iter.rs
Normal file
|
@ -0,0 +1,12 @@
|
|||
// error-pattern:fail
|
||||
|
||||
iter x() -> int {
|
||||
fail;
|
||||
put 0;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let a = @0;
|
||||
for each x in x() {
|
||||
}
|
||||
}
|
12
src/test/run-fail/unwind-iter2.rs
Normal file
12
src/test/run-fail/unwind-iter2.rs
Normal file
|
@ -0,0 +1,12 @@
|
|||
// error-pattern:fail
|
||||
|
||||
iter x() -> int {
|
||||
let a = @0;
|
||||
put 1;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
for each x in x() {
|
||||
fail;
|
||||
}
|
||||
}
|
16
src/test/run-fail/unwind-lambda.rs
Normal file
16
src/test/run-fail/unwind-lambda.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
// error-pattern:fail
|
||||
|
||||
fn main() {
|
||||
let cheese = "roquefort";
|
||||
let carrots = @"crunchy";
|
||||
|
||||
fn (tasties: @str, macerate: block(str)) {
|
||||
macerate(*tasties);
|
||||
} (carrots, { |food|
|
||||
let mush = food + cheese;
|
||||
lambda() {
|
||||
let chew = mush + cheese;
|
||||
fail "so yummy"
|
||||
} ();
|
||||
});
|
||||
}
|
28
src/test/run-fail/unwind-misc-1.rs
Normal file
28
src/test/run-fail/unwind-misc-1.rs
Normal file
|
@ -0,0 +1,28 @@
|
|||
// error-pattern:fail
|
||||
|
||||
use std;
|
||||
import std::map;
|
||||
import std::uint;
|
||||
|
||||
fn main() {
|
||||
let count = @mutable 0u;
|
||||
let hash = bind fn (_s: [@str], count: @mutable uint) -> uint {
|
||||
*count += 1u;
|
||||
if *count == 10u {
|
||||
fail;
|
||||
} else {
|
||||
ret *count;
|
||||
}
|
||||
} (_, count);
|
||||
|
||||
fn eq(s: [@str], t: [@str]) -> bool {
|
||||
ret s == t;
|
||||
}
|
||||
|
||||
let map = map::mk_hashmap(hash, eq);
|
||||
let arr = [];
|
||||
for each i in uint::range(0u, 10u) {
|
||||
arr += [@"key stuff"];
|
||||
map.insert(arr, arr + [@"value stuff"]);
|
||||
}
|
||||
}
|
11
src/test/run-fail/unwind-nested.rs
Normal file
11
src/test/run-fail/unwind-nested.rs
Normal file
|
@ -0,0 +1,11 @@
|
|||
// error-pattern:fail
|
||||
|
||||
fn main() {
|
||||
let a = @0;
|
||||
{
|
||||
let b = @0;
|
||||
{
|
||||
fail;
|
||||
}
|
||||
}
|
||||
}
|
12
src/test/run-fail/unwind-resource-fail.rs
Normal file
12
src/test/run-fail/unwind-resource-fail.rs
Normal file
|
@ -0,0 +1,12 @@
|
|||
// error-pattern:fail
|
||||
// xfail-test
|
||||
|
||||
resource r(i: int) {
|
||||
// What happens when destructors throw?
|
||||
fail;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
@0;
|
||||
let r <- r(0);
|
||||
}
|
16
src/test/run-fail/unwind-stacked.rs
Normal file
16
src/test/run-fail/unwind-stacked.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
// error-pattern:fail
|
||||
|
||||
fn f() {
|
||||
let a = @0;
|
||||
fail;
|
||||
}
|
||||
|
||||
fn g() {
|
||||
let b = @0;
|
||||
f();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let a = @0;
|
||||
g();
|
||||
}
|
10
src/test/run-fail/unwind-uninitialized.rs
Normal file
10
src/test/run-fail/unwind-uninitialized.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
// error-pattern:fail
|
||||
|
||||
fn f() {
|
||||
fail;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
f();
|
||||
let a = @0;
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
// -*- rust -*-
|
||||
|
||||
// error-pattern:bounds check
|
||||
// no-valgrind
|
||||
fn main() {
|
||||
let v: [int] = [10];
|
||||
let x: int = 0;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// -*- rust -*-
|
||||
|
||||
// error-pattern:bounds check
|
||||
// no-valgrind
|
||||
fn main() {
|
||||
let v: [int] = [10, 20];
|
||||
let x: int = 0;
|
||||
|
|
10
src/test/run-pass/native-llvm.rs
Normal file
10
src/test/run-pass/native-llvm.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
// xfail-test
|
||||
|
||||
native "llvm" mod llvm {
|
||||
fn thesqrt(n: float) -> float = "sqrt.f64";
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let s = llvm::thesqrt(4.0);
|
||||
assert 1.9 < s && s < 2.1;
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
// xfail-win32
|
||||
use std;
|
||||
import std::comm;
|
||||
import std::task;
|
||||
|
|
14
src/test/run-pass/unwind-box.rs
Normal file
14
src/test/run-pass/unwind-box.rs
Normal file
|
@ -0,0 +1,14 @@
|
|||
// xfail-win32
|
||||
use std;
|
||||
import std::task;
|
||||
|
||||
fn f() {
|
||||
task::unsupervise();
|
||||
let a = @0;
|
||||
fail;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let g = f;
|
||||
task::spawn(g);
|
||||
}
|
21
src/test/run-pass/unwind-resource.rs
Normal file
21
src/test/run-pass/unwind-resource.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
// xfail-win32
|
||||
use std;
|
||||
import std::task;
|
||||
import std::comm;
|
||||
|
||||
resource complainer(c: comm::chan<bool>) {
|
||||
comm::send(c, true);
|
||||
}
|
||||
|
||||
fn f(-c: comm::chan<bool>) {
|
||||
task::unsupervise();
|
||||
let c <- complainer(c);
|
||||
fail;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let p = comm::port();
|
||||
let c = comm::chan(p);
|
||||
task::spawn(bind f(c));
|
||||
assert comm::recv(p);
|
||||
}
|
18
src/test/run-pass/unwind-resource2.rs
Normal file
18
src/test/run-pass/unwind-resource2.rs
Normal file
|
@ -0,0 +1,18 @@
|
|||
// xfail-win32
|
||||
use std;
|
||||
import std::task;
|
||||
import std::comm;
|
||||
|
||||
resource complainer(c: @int) {
|
||||
}
|
||||
|
||||
fn f() {
|
||||
task::unsupervise();
|
||||
let c <- complainer(@0);
|
||||
fail;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let g = f;
|
||||
task::spawn(g);
|
||||
}
|
|
@ -65,3 +65,10 @@ fn test_pipes() {
|
|||
ret buf;
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn waitpid() {
|
||||
let pid = run::spawn_process("false", [], 0, 0, 0);
|
||||
let status = run::waitpid(pid);
|
||||
assert status == 1;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue