diff --git a/mk/platform.mk b/mk/platform.mk index c8c4fef41fd..a647e9cb3c5 100644 --- a/mk/platform.mk +++ b/mk/platform.mk @@ -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 diff --git a/src/comp/back/upcall.rs b/src/comp/back/upcall.rs index 3b1ee9c9cee..021617d1f18 100644 --- a/src/comp/back/upcall.rs +++ b/src/comp/back/upcall.rs @@ -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: diff --git a/src/comp/lib/llvm.rs b/src/comp/lib/llvm.rs index fa7871042b8..8e00c8d63cd 100644 --- a/src/comp/lib/llvm.rs +++ b/src/comp/lib/llvm.rs @@ -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; diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs index 2276d348611..391d56526df 100644 --- a/src/comp/middle/trans.rs +++ b/src/comp/middle/trans.rs @@ -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, 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) { + 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) { - 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; } } diff --git a/src/comp/middle/trans_build.rs b/src/comp/middle/trans_build.rs index ebf1b20c0ca..5c2a763603b 100644 --- a/src/comp/middle/trans_build.rs +++ b/src/comp/middle/trans_build.rs @@ -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 diff --git a/src/etc/combine-tests.py b/src/etc/combine-tests.py index 163004c4605..09b80ba448f 100755 --- a/src/etc/combine-tests.py +++ b/src/etc/combine-tests.py @@ -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 diff --git a/src/lib/run_program.rs b/src/lib/run_program.rs index 43fe4bc3d95..31bccf32037 100644 --- a/src/lib/run_program.rs +++ b/src/lib/run_program.rs @@ -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; diff --git a/src/lib/test.rs b/src/lib/test.rs index 1ad70db6f17..70de4565365 100644 --- a/src/lib/test.rs +++ b/src/lib/test.rs @@ -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: diff --git a/src/rt/memory_region.cpp b/src/rt/memory_region.cpp index ef8a92b427f..a55d073543d 100644 --- a/src/rt/memory_region.cpp +++ b/src/rt/memory_region.cpp @@ -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); diff --git a/src/rt/memory_region.h b/src/rt/memory_region.h index 0197057268c..9d2106c1eaf 100644 --- a/src/rt/memory_region.h +++ b/src/rt/memory_region.h @@ -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); diff --git a/src/rt/rust_builtin.cpp b/src/rt/rust_builtin.cpp index 5e0c9dbf4c2..2499dea0328 100644 --- a/src/rt/rust_builtin.cpp +++ b/src/rt/rust_builtin.cpp @@ -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) diff --git a/src/rt/rust_internal.h b/src/rt/rust_internal.h index d2c8574280b..c01fc5f3127 100644 --- a/src/rt/rust_internal.h +++ b/src/rt/rust_internal.h @@ -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. diff --git a/src/rt/rust_kernel.cpp b/src/rt/rust_kernel.cpp index 54b1cc98d43..e5234b9d6f5 100644 --- a/src/rt/rust_kernel.cpp +++ b/src/rt/rust_kernel.cpp @@ -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]; diff --git a/src/rt/rust_scheduler.cpp b/src/rt/rust_scheduler.cpp index 3a69184d3fe..b127ec77efa 100644 --- a/src/rt/rust_scheduler.cpp +++ b/src/rt/rust_scheduler.cpp @@ -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(); } diff --git a/src/rt/rust_task.cpp b/src/rt/rust_task.cpp index 57b5d16e427..b861c60b521 100644 --- a/src/rt/rust_task.cpp +++ b/src/rt/rust_task.cpp @@ -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) { diff --git a/src/rt/rust_upcall.cpp b/src/rt/rust_upcall.cpp index 2d7791e876f..0f5c11537fe 100644 --- a/src/rt/rust_upcall.cpp +++ b/src/rt/rust_upcall.cpp @@ -1,6 +1,8 @@ #include "rust_gc.h" #include "rust_internal.h" #include "rust_upcall.h" +#include +#include // 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++ diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in index a0e7c972eeb..bf8155b7998 100644 --- a/src/rt/rustrt.def.in +++ b/src/rt/rustrt.def.in @@ -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 diff --git a/src/rustllvm/rustllvm.def.in b/src/rustllvm/rustllvm.def.in index 25185f3cde5..deac70f3495 100644 --- a/src/rustllvm/rustllvm.def.in +++ b/src/rustllvm/rustllvm.def.in @@ -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 diff --git a/src/test/bench/shootout-nbody.rs b/src/test/bench/shootout-nbody.rs index 92da06f186a..aa8786f8d61 100644 --- a/src/test/bench/shootout-nbody.rs +++ b/src/test/bench/shootout-nbody.rs @@ -1,3 +1,4 @@ +// xfail-test // based on: // http://shootout.alioth.debian.org/u32/benchmark.php?test=nbody&lang=java diff --git a/src/test/compiletest/compiletest.rs b/src/test/compiletest/compiletest.rs index 03bc0e72cc7..0961191918b 100644 --- a/src/test/compiletest/compiletest.rs +++ b/src/test/compiletest/compiletest.rs @@ -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 { diff --git a/src/test/compiletest/header.rs b/src/test/compiletest/header.rs index 9f896c48e74..890818d4574 100644 --- a/src/test/compiletest/header.rs +++ b/src/test/compiletest/header.rs @@ -16,10 +16,7 @@ type test_props = { compile_flags: option::t, // If present, the name of a file that this test should match when // pretty-printed - pp_exact: option::t, - // FIXME: no-valgrind is a temporary directive until all of run-fail - // is valgrind-clean - no_valgrind: bool + pp_exact: option::t }; // 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 { diff --git a/src/test/compiletest/procsrv.rs b/src/test/compiletest/procsrv.rs index ae98853976e..6a40aaccaf4 100644 --- a/src/test/compiletest/procsrv.rs +++ b/src/test/compiletest/procsrv.rs @@ -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}; } diff --git a/src/test/compiletest/runtest.rs b/src/test/compiletest/runtest.rs index 8ed938094c9..46b74e799f4 100644 --- a/src/test/compiletest/runtest.rs +++ b/src/test/compiletest/runtest.rs @@ -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))}; diff --git a/src/test/run-fail/explicit-fail-msg.rs b/src/test/run-fail/explicit-fail-msg.rs index b45e443759c..527a103e342 100644 --- a/src/test/run-fail/explicit-fail-msg.rs +++ b/src/test/run-fail/explicit-fail-msg.rs @@ -1,3 +1,2 @@ // error-pattern:wooooo -// no-valgrind fn main() { let a = 1; if 1 == 1 { a = 2; } fail "woooo" + "o"; } diff --git a/src/test/run-fail/fmt-fail.rs b/src/test/run-fail/fmt-fail.rs index 90256271fd2..5ca0ab74147 100644 --- a/src/test/run-fail/fmt-fail.rs +++ b/src/test/run-fail/fmt-fail.rs @@ -1,5 +1,4 @@ // error-pattern:meh -// no-valgrind use std; fn main() { let str_var: str = "meh"; fail #fmt["%s", str_var]; } diff --git a/src/test/run-fail/linked-failure.rs b/src/test/run-fail/linked-failure.rs index 033a3bc9ec2..24cfff6edb5 100644 --- a/src/test/run-fail/linked-failure.rs +++ b/src/test/run-fail/linked-failure.rs @@ -1,8 +1,7 @@ // -*- rust -*- // error-pattern:1 == 2 -// no-valgrind - +// xfail-test use std; import std::task; import std::comm::port; diff --git a/src/test/run-fail/unwind-assert.rs b/src/test/run-fail/unwind-assert.rs new file mode 100644 index 00000000000..e7aeedcf505 --- /dev/null +++ b/src/test/run-fail/unwind-assert.rs @@ -0,0 +1,6 @@ +// error-pattern:fail + +fn main() { + let a = @0; + assert false; +} \ No newline at end of file diff --git a/src/test/run-fail/unwind-box.rs b/src/test/run-fail/unwind-box.rs new file mode 100644 index 00000000000..19906bda0ae --- /dev/null +++ b/src/test/run-fail/unwind-box.rs @@ -0,0 +1,10 @@ +// error-pattern:fail + +fn failfn() { + fail; +} + +fn main() { + @0; + failfn(); +} \ No newline at end of file diff --git a/src/test/run-fail/unwind-check.rs b/src/test/run-fail/unwind-check.rs new file mode 100644 index 00000000000..e01cc969cea --- /dev/null +++ b/src/test/run-fail/unwind-check.rs @@ -0,0 +1,8 @@ +// error-pattern:fail + +pure fn p(a: @int) -> bool { false } + +fn main() { + let a = @0; + check p(a); +} \ No newline at end of file diff --git a/src/test/run-fail/unwind-closure.rs b/src/test/run-fail/unwind-closure.rs new file mode 100644 index 00000000000..216ae054947 --- /dev/null +++ b/src/test/run-fail/unwind-closure.rs @@ -0,0 +1,10 @@ +// error-pattern:fail + +fn f(a: @int) { + fail; +} + +fn main() { + let g = bind f(@0); + g(); +} \ No newline at end of file diff --git a/src/test/run-fail/unwind-fail.rs b/src/test/run-fail/unwind-fail.rs new file mode 100644 index 00000000000..2d4f138e715 --- /dev/null +++ b/src/test/run-fail/unwind-fail.rs @@ -0,0 +1,6 @@ +// error-pattern:fail + +fn main() { + @0; + fail; +} \ No newline at end of file diff --git a/src/test/run-fail/unwind-initializer-indirect.rs b/src/test/run-fail/unwind-initializer-indirect.rs new file mode 100644 index 00000000000..726dc3d9040 --- /dev/null +++ b/src/test/run-fail/unwind-initializer-indirect.rs @@ -0,0 +1,7 @@ +// error-pattern:fail + +fn f() -> @int { fail; } + +fn main() { + let a: @int = f(); +} \ No newline at end of file diff --git a/src/test/run-fail/unwind-initializer.rs b/src/test/run-fail/unwind-initializer.rs new file mode 100644 index 00000000000..b3dc0d3eaeb --- /dev/null +++ b/src/test/run-fail/unwind-initializer.rs @@ -0,0 +1,7 @@ +// error-pattern:fail + +fn main() { + let a: @int = { + fail; + }; +} \ No newline at end of file diff --git a/src/test/run-fail/unwind-iter.rs b/src/test/run-fail/unwind-iter.rs new file mode 100644 index 00000000000..fa64b0ad78c --- /dev/null +++ b/src/test/run-fail/unwind-iter.rs @@ -0,0 +1,12 @@ +// error-pattern:fail + +iter x() -> int { + fail; + put 0; +} + +fn main() { + let a = @0; + for each x in x() { + } +} \ No newline at end of file diff --git a/src/test/run-fail/unwind-iter2.rs b/src/test/run-fail/unwind-iter2.rs new file mode 100644 index 00000000000..a924bb13616 --- /dev/null +++ b/src/test/run-fail/unwind-iter2.rs @@ -0,0 +1,12 @@ +// error-pattern:fail + +iter x() -> int { + let a = @0; + put 1; +} + +fn main() { + for each x in x() { + fail; + } +} \ No newline at end of file diff --git a/src/test/run-fail/unwind-lambda.rs b/src/test/run-fail/unwind-lambda.rs new file mode 100644 index 00000000000..6980cc16bd1 --- /dev/null +++ b/src/test/run-fail/unwind-lambda.rs @@ -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" + } (); + }); +} \ No newline at end of file diff --git a/src/test/run-fail/unwind-misc-1.rs b/src/test/run-fail/unwind-misc-1.rs new file mode 100644 index 00000000000..15d433ae363 --- /dev/null +++ b/src/test/run-fail/unwind-misc-1.rs @@ -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"]); + } +} \ No newline at end of file diff --git a/src/test/run-fail/unwind-nested.rs b/src/test/run-fail/unwind-nested.rs new file mode 100644 index 00000000000..48e3063c6dd --- /dev/null +++ b/src/test/run-fail/unwind-nested.rs @@ -0,0 +1,11 @@ +// error-pattern:fail + +fn main() { + let a = @0; + { + let b = @0; + { + fail; + } + } +} \ No newline at end of file diff --git a/src/test/run-fail/unwind-resource-fail.rs b/src/test/run-fail/unwind-resource-fail.rs new file mode 100644 index 00000000000..4ab8dc95085 --- /dev/null +++ b/src/test/run-fail/unwind-resource-fail.rs @@ -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); +} \ No newline at end of file diff --git a/src/test/run-fail/unwind-stacked.rs b/src/test/run-fail/unwind-stacked.rs new file mode 100644 index 00000000000..bf5258cee7d --- /dev/null +++ b/src/test/run-fail/unwind-stacked.rs @@ -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(); +} \ No newline at end of file diff --git a/src/test/run-fail/unwind-uninitialized.rs b/src/test/run-fail/unwind-uninitialized.rs new file mode 100644 index 00000000000..35269b38714 --- /dev/null +++ b/src/test/run-fail/unwind-uninitialized.rs @@ -0,0 +1,10 @@ +// error-pattern:fail + +fn f() { + fail; +} + +fn main() { + f(); + let a = @0; +} \ No newline at end of file diff --git a/src/test/run-fail/vec-overrun.rs b/src/test/run-fail/vec-overrun.rs index 54329ca63b5..7a429e08cae 100644 --- a/src/test/run-fail/vec-overrun.rs +++ b/src/test/run-fail/vec-overrun.rs @@ -1,7 +1,6 @@ // -*- rust -*- // error-pattern:bounds check -// no-valgrind fn main() { let v: [int] = [10]; let x: int = 0; diff --git a/src/test/run-fail/vec-underrun.rs b/src/test/run-fail/vec-underrun.rs index 87c8295840e..9caf82d1ae0 100644 --- a/src/test/run-fail/vec-underrun.rs +++ b/src/test/run-fail/vec-underrun.rs @@ -1,7 +1,6 @@ // -*- rust -*- // error-pattern:bounds check -// no-valgrind fn main() { let v: [int] = [10, 20]; let x: int = 0; diff --git a/src/test/run-pass/native-llvm.rs b/src/test/run-pass/native-llvm.rs new file mode 100644 index 00000000000..450f052bc62 --- /dev/null +++ b/src/test/run-pass/native-llvm.rs @@ -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; +} \ No newline at end of file diff --git a/src/test/run-pass/task-comm-15.rs b/src/test/run-pass/task-comm-15.rs index 2b89d8fde38..56cf41b50c3 100644 --- a/src/test/run-pass/task-comm-15.rs +++ b/src/test/run-pass/task-comm-15.rs @@ -1,3 +1,4 @@ +// xfail-win32 use std; import std::comm; import std::task; diff --git a/src/test/run-pass/unwind-box.rs b/src/test/run-pass/unwind-box.rs new file mode 100644 index 00000000000..0c4f1e4616c --- /dev/null +++ b/src/test/run-pass/unwind-box.rs @@ -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); +} \ No newline at end of file diff --git a/src/test/run-pass/unwind-resource.rs b/src/test/run-pass/unwind-resource.rs new file mode 100644 index 00000000000..e8cf0bab44f --- /dev/null +++ b/src/test/run-pass/unwind-resource.rs @@ -0,0 +1,21 @@ +// xfail-win32 +use std; +import std::task; +import std::comm; + +resource complainer(c: comm::chan) { + comm::send(c, true); +} + +fn f(-c: comm::chan) { + 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); +} \ No newline at end of file diff --git a/src/test/run-pass/unwind-resource2.rs b/src/test/run-pass/unwind-resource2.rs new file mode 100644 index 00000000000..1d7a46151fc --- /dev/null +++ b/src/test/run-pass/unwind-resource2.rs @@ -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); +} \ No newline at end of file diff --git a/src/test/stdtest/run.rs b/src/test/stdtest/run.rs index 218889c766b..e5c36d37041 100644 --- a/src/test/stdtest/run.rs +++ b/src/test/stdtest/run.rs @@ -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; +}