From cd5c57ca8a4f9f40b5fce93051b955f586982455 Mon Sep 17 00:00:00 2001 From: Graydon Hoare Date: Fri, 21 Dec 2012 09:54:18 -0800 Subject: [PATCH 1/4] rustc: add return-unwind -Z option, does nothing yet except trigger no-landing-pads --- src/librustc/driver/session.rs | 14 ++++++++++++-- src/librustc/middle/trans/base.rs | 5 ++--- src/librustc/middle/trans/glue.rs | 4 ++-- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/librustc/driver/session.rs b/src/librustc/driver/session.rs index 75400c5e3244c..ffffddc59b32b 100644 --- a/src/librustc/driver/session.rs +++ b/src/librustc/driver/session.rs @@ -67,6 +67,7 @@ pub const jit: uint = 1 << 19; pub const debug_info: uint = 1 << 20; pub const extra_debug_info: uint = 1 << 21; pub const static: uint = 1 << 22; +pub const return_unwind: uint = 1 << 23; pub fn debugging_opts_map() -> ~[(~str, ~str, uint)] { ~[(~"verbose", ~"in general, enable more debug printouts", verbose), @@ -100,7 +101,10 @@ pub fn debugging_opts_map() -> ~[(~str, ~str, uint)] { extra_debug_info), (~"debug-info", ~"Produce debug info (experimental)", debug_info), (~"static", ~"Use or produce static libraries or binaries " + - "(experimental)", static) + "(experimental)", static), + (~"return-unwind", + ~"use return codes for unwinding (implies no-landing-pads)", + return_unwind), ] } @@ -240,6 +244,10 @@ pub impl Session_ { } fn verbose(@self) -> bool { self.debugging_opt(verbose) } fn time_passes(@self) -> bool { self.debugging_opt(time_passes) } + fn no_landing_pads(@self) -> bool { + self.debugging_opt(no_landing_pads) || + self.debugging_opt(return_unwind) + } fn count_llvm_insns(@self) -> bool { self.debugging_opt(count_llvm_insns) } @@ -265,7 +273,9 @@ pub impl Session_ { fn no_monomorphic_collapse(@self) -> bool { self.debugging_opt(no_monomorphic_collapse) } - + fn return_unwind(@self) -> bool { + self.debugging_opt(return_unwind) + } fn str_of(@self, id: ast::ident) -> @~str { self.parse_sess.interner.get(id) } diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index a340e6472b8f7..f607eb878976a 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -838,7 +838,7 @@ pub fn invoke(bcx: block, llfn: ValueRef, +llargs: ~[ValueRef]) -> block { } pub fn need_invoke(bcx: block) -> bool { - if (bcx.ccx().sess.opts.debugging_opts & session::no_landing_pads != 0) { + if (bcx.ccx().sess.no_landing_pads()) { return false; } @@ -1258,8 +1258,7 @@ pub fn trans_block_cleanups_(bcx: block, let _icx = bcx.insn_ctxt("trans_block_cleanups"); // NB: Don't short-circuit even if this block is unreachable because // GC-based cleanup needs to the see that the roots are live. - let no_lpads = - bcx.ccx().sess.opts.debugging_opts & session::no_landing_pads != 0; + let no_lpads = bcx.ccx().sess.no_landing_pads(); if bcx.unreachable && !no_lpads { return bcx; } let mut bcx = bcx; for vec::rev_each(cleanups) |cu| { diff --git a/src/librustc/middle/trans/glue.rs b/src/librustc/middle/trans/glue.rs index 8302f46120e66..92831b87a970c 100644 --- a/src/librustc/middle/trans/glue.rs +++ b/src/librustc/middle/trans/glue.rs @@ -328,8 +328,8 @@ pub fn call_tydesc_glue_full(++bcx: block, let ccx = bcx.ccx(); // NB: Don't short-circuit even if this block is unreachable because // GC-based cleanup needs to the see that the roots are live. - let no_lpads = - ccx.sess.opts.debugging_opts & session::no_landing_pads != 0; + let no_lpads = ccx.sess.no_landing_pads(); + if bcx.unreachable && !no_lpads { return; } let static_glue_fn = match static_ti { From 5c6b631f3e636c5c2f90d3330c48c97f2dfd6e99 Mon Sep 17 00:00:00 2001 From: Graydon Hoare Date: Fri, 21 Dec 2012 10:54:43 -0800 Subject: [PATCH 2/4] rustc: return bool from fns, true on success. --- src/librustc/middle/trans/base.rs | 6 +++++- src/librustc/middle/trans/type_of.rs | 8 +++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index f607eb878976a..d550230ff82d0 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -1728,7 +1728,11 @@ pub fn finish_fn(fcx: fn_ctxt, lltop: BasicBlockRef) { let _icx = fcx.insn_ctxt("finish_fn"); tie_up_header_blocks(fcx, lltop); let ret_cx = raw_block(fcx, false, fcx.llreturn); - RetVoid(ret_cx); + if fcx.ccx.sess.return_unwind() { + Ret(ret_cx, C_bool(true)); + } else { + RetVoid(ret_cx); + } } pub fn tie_up_header_blocks(fcx: fn_ctxt, lltop: BasicBlockRef) { diff --git a/src/librustc/middle/trans/type_of.rs b/src/librustc/middle/trans/type_of.rs index 0c3e93885f964..b178d8b1ec21a 100644 --- a/src/librustc/middle/trans/type_of.rs +++ b/src/librustc/middle/trans/type_of.rs @@ -51,7 +51,13 @@ pub fn type_of_fn(cx: @CrateContext, inputs: &[ty::arg], // ... then explicit args. atys.push_all(type_of_explicit_args(cx, inputs)); - return T_fn(atys, llvm::LLVMVoidType()); + + // return-unwind form returns a boolean: true=ok / false=fail. + if cx.sess.return_unwind() { + T_fn(atys, T_bool()) + } else { + T_fn(atys, llvm::LLVMVoidType()) + } } } From b29ef83f551446dcdb1c3c16955495d93c1a6c2a Mon Sep 17 00:00:00 2001 From: Graydon Hoare Date: Mon, 4 Feb 2013 13:43:32 -0800 Subject: [PATCH 3/4] librustc: fix two minor undef-ptr-during-alloca mistranslations. --- src/librustc/middle/trans/base.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index d550230ff82d0..52f6d22d3d565 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -1523,7 +1523,7 @@ pub fn alloca_maybe_zeroed(cx: block, t: TypeRef, zero: bool) -> ValueRef { let _icx = cx.insn_ctxt("alloca"); if cx.unreachable { unsafe { - return llvm::LLVMGetUndef(t); + return llvm::LLVMGetUndef(T_ptr(t)); } } let initcx = base::raw_block(cx.fcx, false, cx.fcx.llstaticallocas); @@ -1536,7 +1536,7 @@ pub fn arrayalloca(cx: block, t: TypeRef, v: ValueRef) -> ValueRef { let _icx = cx.insn_ctxt("arrayalloca"); if cx.unreachable { unsafe { - return llvm::LLVMGetUndef(t); + return llvm::LLVMGetUndef(T_ptr(t)); } } return ArrayAlloca( From 793e02cdbfd3ee5d90f47f0b7328178118ff93d0 Mon Sep 17 00:00:00 2001 From: Graydon Hoare Date: Tue, 5 Feb 2013 12:43:27 -0800 Subject: [PATCH 4/4] librustc: set, return, and propagate false retcode on failure. --- src/libcore/sys.rs | 73 ++++++++++++++++++++---- src/libcore/unstable/lang.rs | 13 ++++- src/librustc/driver/driver.rs | 7 +++ src/librustc/middle/trans/base.rs | 45 ++++++++++----- src/librustc/middle/trans/common.rs | 12 ++-- src/librustc/middle/trans/controlflow.rs | 39 ++++++++++--- src/librustc/middle/trans/foreign.rs | 8 ++- src/librustc/middle/trans/type_of.rs | 17 +++--- src/librustc/middle/trans/type_use.rs | 3 +- src/librustc/middle/typeck/check/mod.rs | 2 + src/libuv | 2 +- src/rt/rust_task.cpp | 48 ++++++++++------ src/rt/rust_task.h | 12 ++-- src/rt/rust_type.h | 3 +- src/rt/rust_upcall.cpp | 16 ++++-- src/test/run-fail/issue-2144.rs | 2 + src/test/run-fail/morestack1.rs | 3 + src/test/run-fail/too-much-recursion.rs | 2 + 18 files changed, 227 insertions(+), 80 deletions(-) diff --git a/src/libcore/sys.rs b/src/libcore/sys.rs index 179a33ae43ea3..bf1bee1c17d04 100644 --- a/src/libcore/sys.rs +++ b/src/libcore/sys.rs @@ -37,9 +37,20 @@ pub struct Closure { env: *(), } +pub mod rustrt { + use libc::{c_char, size_t}; + + pub extern { + #[rust_stack] + unsafe fn rust_upcall_fail(unwind: bool, expr: *c_char, + file: *c_char, line: size_t); + } +} + +#[cfg(stage0)] pub mod rusti { #[abi = "rust-intrinsic"] - pub extern { + extern { fn get_tydesc() -> *(); fn size_of() -> uint; fn pref_align_of() -> uint; @@ -47,14 +58,17 @@ pub mod rusti { } } -pub mod rustrt { - use libc::{c_char, size_t}; - - pub extern { - #[rust_stack] - unsafe fn rust_upcall_fail(expr: *c_char, - file: *c_char, - line: size_t); +#[cfg(stage1)] +#[cfg(stage2)] +#[cfg(stage3)] +pub mod rusti { + #[abi = "rust-intrinsic"] + extern { + fn get_tydesc() -> *(); + fn size_of() -> uint; + fn pref_align_of() -> uint; + fn min_align_of() -> uint; + fn set_retcode_fail(); } } @@ -134,7 +148,17 @@ pub pure fn log_str(t: &T) -> ~str { } } + +#[cfg(return_unwind)] +const do_throw:bool = false; + +#[cfg(stage0)] +#[cfg(throw_unwind)] +const do_throw:bool = true; + /** Initiate task failure */ +#[cfg(stage0)] +#[cfg(throw_unwind)] pub pure fn begin_unwind(msg: ~str, file: ~str, line: uint) -> ! { do str::as_buf(msg) |msg_buf, _msg_len| { do str::as_buf(file) |file_buf, _file_len| { @@ -147,15 +171,19 @@ pub pure fn begin_unwind(msg: ~str, file: ~str, line: uint) -> ! { } } + // FIXME #4427: Temporary until rt::rt_fail_ goes away +#[cfg(stage0)] +#[cfg(throw_unwind)] pub pure fn begin_unwind_(msg: *c_char, file: *c_char, line: size_t) -> ! { unsafe { gc::cleanup_stack_for_failure(); - rustrt::rust_upcall_fail(msg, file, line); + rustrt::rust_upcall_fail(do_throw, msg, file, line); cast::transmute(()) } } + pub pure fn fail_assert(msg: &str, file: &str, line: uint) -> ! { unsafe { let (msg, file) = (msg.to_owned(), file.to_owned()); @@ -163,6 +191,31 @@ pub pure fn fail_assert(msg: &str, file: &str, line: uint) -> ! { } } +/** Initiate task failure */ +#[cfg(return_unwind)] +pub pure fn begin_unwind(msg: ~str, file: ~str, line: uint) { + do str::as_buf(msg) |msg_buf, _msg_len| { + do str::as_buf(file) |file_buf, _file_len| { + unsafe { + let msg_buf = cast::transmute(msg_buf); + let file_buf = cast::transmute(file_buf); + begin_unwind_(msg_buf, file_buf, line as libc::size_t) + } + } + } +} + + +// FIXME #4427: Temporary until rt::rt_fail_ goes away +#[cfg(return_unwind)] +pub pure fn begin_unwind_(msg: *c_char, file: *c_char, line: size_t) { + unsafe { + gc::cleanup_stack_for_failure(); + rustrt::rust_upcall_fail(do_throw, msg, file, line); + rusti::set_retcode_fail(); + } +} + #[cfg(test)] pub mod tests { use cast; diff --git a/src/libcore/unstable/lang.rs b/src/libcore/unstable/lang.rs index db0b1cc33cd67..7fa1c980996fc 100644 --- a/src/libcore/unstable/lang.rs +++ b/src/libcore/unstable/lang.rs @@ -27,7 +27,7 @@ pub const FROZEN_BIT: uint = 0x80000000; pub const FROZEN_BIT: uint = 0x8000000000000000; pub mod rustrt { - use libc::{c_char, uintptr_t}; + use libc::{c_char, uintptr_t, size_t}; pub extern { #[rust_stack] @@ -38,11 +38,22 @@ pub mod rustrt { } } +#[rt(fail_)] #[lang="fail_"] +#[cfg(return_unwind)] +pub fn fail_(expr: *c_char, file: *c_char, line: size_t) { + sys::begin_unwind_(expr, file, line); +} + +#[lang="fail_"] +#[cfg(stage0)] +#[cfg(throw_unwind)] pub fn fail_(expr: *c_char, file: *c_char, line: size_t) -> ! { sys::begin_unwind_(expr, file, line); } + +#[rt(fail_bounds_check)] #[lang="fail_bounds_check"] pub unsafe fn fail_bounds_check(file: *c_char, line: size_t, index: size_t, len: size_t) { diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index 509ef704b070a..ffb1df0e0a268 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -128,6 +128,13 @@ pub fn build_configuration(sess: Session, +argv0: ~str, input: input) -> let user_cfg = append_configuration( user_cfg, if sess.opts.gc { ~"gc" } else { ~"nogc" }); + let user_cfg = append_configuration( + user_cfg, + if sess.return_unwind() { + ~"return_unwind" + } else { + ~"throw_unwind" + }); return vec::append(user_cfg, default_cfg); } diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 52f6d22d3d565..9956265754bc0 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -822,7 +822,16 @@ pub fn invoke(bcx: block, llfn: ValueRef, +llargs: ~[ValueRef]) -> block { } let normal_bcx = sub_block(bcx, ~"normal return"); Invoke(bcx, llfn, llargs, normal_bcx.llbb, get_landing_pad(bcx)); - return normal_bcx; + normal_bcx + } else if bcx.fcx.ccx.sess.return_unwind() { + debug!("calling with return-unwind check"); + let v = Call(bcx, llfn, llargs); + do with_cond(bcx, Not(bcx, v)) |bcx| { + Store(bcx, C_bool(false), controlflow::get_llretcode(bcx)); + cleanup_and_leave(bcx, None, Some(bcx.fcx.llreturn)); + Unreachable(bcx); + bcx + } } else { unsafe { debug!("calling %x at %x", @@ -833,7 +842,7 @@ pub fn invoke(bcx: block, llfn: ValueRef, +llargs: ~[ValueRef]) -> block { } } Call(bcx, llfn, llargs); - return bcx; + bcx } } @@ -1582,6 +1591,7 @@ pub fn new_fn_ctxt_w_id(ccx: @CrateContext, llstaticallocas: llbbs.sa, llloadenv: None, llreturn: llbbs.rt, + llretcode: None, llself: None, personality: None, loop_ret: None, @@ -1728,10 +1738,9 @@ pub fn finish_fn(fcx: fn_ctxt, lltop: BasicBlockRef) { let _icx = fcx.insn_ctxt("finish_fn"); tie_up_header_blocks(fcx, lltop); let ret_cx = raw_block(fcx, false, fcx.llreturn); - if fcx.ccx.sess.return_unwind() { - Ret(ret_cx, C_bool(true)); - } else { - RetVoid(ret_cx); + match fcx.llretcode { + None => Ret(ret_cx, C_bool(true)), + Some(addr) => Ret(ret_cx, Load(ret_cx, addr)) } } @@ -2228,7 +2237,19 @@ pub fn create_main_wrapper(ccx: @CrateContext, let lloutputarg = unsafe { llvm::LLVMGetParam(llfdecl, 0 as c_uint) }; let llenvarg = unsafe { llvm::LLVMGetParam(llfdecl, 1 as c_uint) }; let mut args = ~[lloutputarg, llenvarg]; - Call(bcx, main_llfn, args); + + let bcx = if bcx.fcx.ccx.sess.return_unwind() { + let v = Call(bcx, main_llfn, args); + do with_cond(bcx, Not(bcx, v)) |bcx| { + Store(bcx, C_bool(false), controlflow::get_llretcode(bcx)); + cleanup_and_leave(bcx, None, Some(bcx.fcx.llreturn)); + Unreachable(bcx); + bcx + } + } else { + Call(bcx, main_llfn, args); + bcx + }; build_return(bcx); finish_fn(fcx, lltop); @@ -2463,13 +2484,7 @@ pub fn get_item_val(ccx: @CrateContext, id: ast::node_id) -> ValueRef { let class_ty = ty::lookup_item_type(tcx, parent_id).ty; // This code shouldn't be reached if the class is generic fail_unless!(!ty::type_has_params(class_ty)); - let lldty = unsafe { - T_fn(~[ - T_ptr(type_of(ccx, ty::mk_nil(tcx))), - T_ptr(type_of(ccx, class_ty)) - ], - llvm::LLVMVoidType()) - }; + let lldty = type_of_dtor(ccx, class_ty); let s = get_dtor_symbol(ccx, /*bad*/copy *pt, dt.node.id, None); /* Make the declaration for the dtor */ @@ -3035,7 +3050,7 @@ pub fn trans_crate(sess: session::Session, let task_type = T_task(targ_cfg); let taskptr_type = T_ptr(task_type); lib::llvm::associate_type(tn, @"taskptr", taskptr_type); - let tydesc_type = T_tydesc(targ_cfg); + let tydesc_type = T_tydesc(sess); lib::llvm::associate_type(tn, @"tydesc", tydesc_type); let crate_map = decl_crate_map(sess, link_meta, llmod); let dbg_cx = if sess.opts.debuginfo { diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs index 04e25bdbf21c8..5b4ba8055b8a2 100644 --- a/src/librustc/middle/trans/common.rs +++ b/src/librustc/middle/trans/common.rs @@ -283,6 +283,11 @@ pub struct fn_ctxt_ { // allowing most any operation to be performed on them.) llloadenv: Option, llreturn: BasicBlockRef, + + // An alloca holding the 'true' value we return normally; written to + // 'false' when we're returning failure. + llretcode: Option, + // The 'self' value currently in use in this function, if there // is one. // @@ -917,15 +922,14 @@ pub fn T_generic_glue_fn(cx: @CrateContext) -> TypeRef { return t; } -pub fn T_tydesc(targ_cfg: @session::config) -> TypeRef { +pub fn T_tydesc(sess: session::Session) -> TypeRef { let tydesc = T_named_struct(~"tydesc"); let tydescpp = T_ptr(T_ptr(tydesc)); let pvoid = T_ptr(T_i8()); let glue_fn_ty = T_ptr(T_fn(~[T_ptr(T_nil()), T_ptr(T_nil()), tydescpp, - pvoid], T_void())); - - let int_type = T_int(targ_cfg); + pvoid], T_bool())); + let int_type = T_int(sess.targ_cfg); let elems = ~[int_type, int_type, glue_fn_ty, glue_fn_ty, glue_fn_ty, glue_fn_ty, diff --git a/src/librustc/middle/trans/controlflow.rs b/src/librustc/middle/trans/controlflow.rs index 40394391f1adb..541e217466c0c 100644 --- a/src/librustc/middle/trans/controlflow.rs +++ b/src/librustc/middle/trans/controlflow.rs @@ -321,10 +321,24 @@ pub fn trans_ret(bcx: block, e: Option<@ast::expr>) -> block { return bcx; } + +pub fn get_llretcode(bcx: block) -> ValueRef { + match bcx.fcx.llretcode { + None => { + let initcx = raw_block(bcx.fcx, false, + bcx.fcx.llstaticallocas); + let v = Alloca(initcx, T_bool()); + Store(initcx, C_bool(true), v); + bcx.fcx.llretcode = Some(v); + v + } + Some(v) => v + } +} + pub fn trans_fail_expr(bcx: block, - sp_opt: Option, - fail_expr: Option<@ast::expr>) - -> block { + sp_opt: Option, + fail_expr: Option<@ast::expr>) -> block { let _icx = bcx.insn_ctxt("trans_fail_expr"); let mut bcx = bcx; match fail_expr { @@ -377,10 +391,21 @@ fn trans_fail_value(bcx: block, let V_str = PointerCast(bcx, V_fail_str, T_ptr(T_i8())); let V_filename = PointerCast(bcx, V_filename, T_ptr(T_i8())); let args = ~[V_str, V_filename, C_int(ccx, V_line)]; - let bcx = callee::trans_lang_call( - bcx, bcx.tcx().lang_items.fail_fn(), args, expr::Ignore); - Unreachable(bcx); - return bcx; + + if bcx.sess().return_unwind() { + let bcx = callee::trans_lang_call( + bcx, bcx.tcx().lang_items.fail_fn(), args, expr::Ignore); + Store(bcx, C_bool(false), get_llretcode(bcx)); + cleanup_and_leave(bcx, None, Some(bcx.fcx.llreturn)); + Unreachable(bcx); + return bcx; + + } else { + let bcx = callee::trans_lang_call( + bcx, bcx.tcx().lang_items.fail_fn(), args, expr::Ignore); + Unreachable(bcx); + return bcx; + } } pub fn trans_fail_bounds_check(bcx: block, sp: span, diff --git a/src/librustc/middle/trans/foreign.rs b/src/librustc/middle/trans/foreign.rs index 8dd55c9a37b60..4a351845c3906 100644 --- a/src/librustc/middle/trans/foreign.rs +++ b/src/librustc/middle/trans/foreign.rs @@ -26,6 +26,7 @@ use middle::trans::cabi_mips::*; use middle::trans::build::*; use middle::trans::callee::*; use middle::trans::common::*; +use middle::trans::controlflow; use middle::trans::datum::*; use middle::trans::expr::{Dest, Ignore}; use middle::trans::machine::llsize_of; @@ -120,7 +121,7 @@ fn shim_types(ccx: @CrateContext, id: ast::node_id) -> ShimTypes { llsig: llsig, ret_def: ret_def, bundle_ty: bundle_ty, - shim_fn_ty: T_fn(~[T_ptr(bundle_ty)], T_void()), + shim_fn_ty: T_fn(~[T_ptr(bundle_ty)], T_bool()), fn_ty: fn_ty } } @@ -394,7 +395,7 @@ pub fn trans_foreign_mod(ccx: @CrateContext, fn build_ret(bcx: block, _tys: &ShimTypes, _llargbundle: ValueRef) { let _icx = bcx.insn_ctxt("foreign::wrap::build_ret"); - RetVoid(bcx); + Ret(bcx, C_bool(true)); } } } @@ -597,6 +598,9 @@ pub fn trans_intrinsic(ccx: @CrateContext, C_bool(ty::type_needs_drop(ccx.tcx, tp_ty)), fcx.llretptr); } + ~"set_retcode_fail" => { + Store(bcx, C_bool(false), controlflow::get_llretcode(bcx)); + } ~"visit_tydesc" => { let td = get_param(decl, first_real_arg); let visitor = get_param(decl, first_real_arg + 1u); diff --git a/src/librustc/middle/trans/type_of.rs b/src/librustc/middle/trans/type_of.rs index b178d8b1ec21a..a8607f757d1c8 100644 --- a/src/librustc/middle/trans/type_of.rs +++ b/src/librustc/middle/trans/type_of.rs @@ -52,12 +52,11 @@ pub fn type_of_fn(cx: @CrateContext, inputs: &[ty::arg], // ... then explicit args. atys.push_all(type_of_explicit_args(cx, inputs)); - // return-unwind form returns a boolean: true=ok / false=fail. - if cx.sess.return_unwind() { - T_fn(atys, T_bool()) - } else { - T_fn(atys, llvm::LLVMVoidType()) - } + // Return a boolean: true=ok / false=fail. + // Note false=fail only _happens_ when we're not using + // dwarf or sjlj type unwinding. But it's always latent + // in the type of rust functions. + T_fn(atys, T_bool()) } } @@ -304,7 +303,7 @@ pub fn type_of_dtor(ccx: @CrateContext, self_ty: ty::t) -> TypeRef { unsafe { T_fn(~[T_ptr(type_of(ccx, ty::mk_nil(ccx.tcx))), // output pointer T_ptr(type_of(ccx, self_ty))], // self arg - llvm::LLVMVoidType()) + T_bool()) } } @@ -318,6 +317,6 @@ pub fn type_of_rooted(ccx: @CrateContext, t: ty::t) -> TypeRef { pub fn type_of_glue_fn(ccx: @CrateContext, t: ty::t) -> TypeRef { let tydescpp = T_ptr(T_ptr(ccx.tydesc_type)); let llty = T_ptr(type_of(ccx, t)); - return T_fn(~[T_ptr(T_nil()), T_ptr(T_nil()), tydescpp, llty], - T_void()); + T_fn(~[T_ptr(T_nil()), T_ptr(T_nil()), tydescpp, llty], + T_bool()) } diff --git a/src/librustc/middle/trans/type_use.rs b/src/librustc/middle/trans/type_use.rs index 5279c10d9675a..e4fb19f1211ec 100644 --- a/src/librustc/middle/trans/type_use.rs +++ b/src/librustc/middle/trans/type_use.rs @@ -135,7 +135,8 @@ pub fn type_uses_for(ccx: @CrateContext, fn_id: def_id, n_tps: uint) ~"atomic_xadd_rel" | ~"atomic_xsub_rel" => 0, ~"visit_tydesc" | ~"forget" | ~"addr_of" | - ~"frame_address" | ~"morestack_addr" => 0, + ~"frame_address" | ~"morestack_addr" | + ~"set_retcode_fail" => 0, ~"memmove32" | ~"memmove64" => 0, diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index 240f79ce262cc..828ef260083bf 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -3195,6 +3195,8 @@ pub fn check_intrinsic_type(ccx: @mut CrateCtxt, it: @ast::foreign_item) { } ~"needs_drop" => (1u, ~[], ty::mk_bool(tcx)), + ~"set_retcode_fail" => (0u, ~[], ty::mk_nil(tcx)), + ~"atomic_cxchg" | ~"atomic_cxchg_acq"| ~"atomic_cxchg_rel" => { (0u, ~[arg(ast::by_copy, ty::mk_mut_rptr(tcx, ty::re_bound(ty::br_anon(0)), diff --git a/src/libuv b/src/libuv index 218ab86721eef..da33bba7c04e0 160000 --- a/src/libuv +++ b/src/libuv @@ -1 +1 @@ -Subproject commit 218ab86721eefd7b7e97fa6d9f95a80a1fa8686c +Subproject commit da33bba7c04e0873b457a9a4290bed2adf620154 diff --git a/src/rt/rust_task.cpp b/src/rt/rust_task.cpp index 63dc1c9833e21..08e7f457b5ca3 100644 --- a/src/rt/rust_task.cpp +++ b/src/rt/rust_task.cpp @@ -78,13 +78,17 @@ rust_task::delete_this() } // All failure goes through me. Put your breakpoints here! +// (Note: this function is extern "C" so it has a nice symbol +// you can break on in gdb; rust code does not call this directly, +// but rather calls one of the upcalls.) extern "C" void -rust_task_fail(rust_task *task, +rust_task_fail(bool do_throw, + rust_task *task, char const *expr, char const *file, size_t line) { assert(task != NULL); - task->begin_failure(expr, file, line); + task->begin_failure(do_throw, expr, file, line); } struct spawn_args { @@ -154,20 +158,22 @@ extern "C" CDECL void upcall_exchange_free(void *ptr); void task_start_wrapper(spawn_args *a) { rust_task *task = a->task; + bool ok = true; - bool threw_exception = false; try { // The first argument is the return pointer; as the task fn // must have void return type, we can safely pass 0. - a->f(0, a->envptr, a->argptr); + ok = a->f(0, a->envptr, a->argptr); } catch (rust_task *ex) { - assert(ex == task && "Expected this task to be thrown for unwinding"); - threw_exception = true; + assert(ex == task && + "Expected this task to be thrown for unwinding"); + ok = false; + } + if (!ok) { if (task->c_stack) { task->return_c_stack(); } - // Since we call glue code below we need to make sure we // have the stack limit set up correctly task->reset_stack_limit(); @@ -185,9 +191,8 @@ void task_start_wrapper(spawn_args *a) } // The cleanup work needs lots of stack - cleanup_args ca = {a, threw_exception}; + cleanup_args ca = {a, !ok}; task->call_on_c_stack(&ca, (void*)cleanup_task); - task->ctx.next->swap(task->ctx); } @@ -309,17 +314,19 @@ void rust_task::kill_inner() { void rust_task::fail() { // See note in ::kill() regarding who should call this. - fail(NULL, NULL, 0); + fail(false, NULL, NULL, 0); } void -rust_task::fail(char const *expr, char const *file, size_t line) { - rust_task_fail(this, expr, file, line); +rust_task::fail(bool do_throw, char const *expr, + char const *file, size_t line) { + rust_task_fail(do_throw, this, expr, file, line); } // Called only by rust_task_fail void -rust_task::begin_failure(char const *expr, char const *file, size_t line) { +rust_task::begin_failure(bool do_throw, char const *expr, + char const *file, size_t line) { if (expr) { LOG_ERR(this, task, "task failed at '%s', %s:%" PRIdPTR, @@ -329,13 +336,16 @@ rust_task::begin_failure(char const *expr, char const *file, size_t line) { DLOG(sched_loop, task, "task %s @0x%" PRIxPTR " failing", name, this); backtrace(); unwinding = true; + + if (do_throw) { #ifndef __WIN32__ - throw this; + throw this; #else - die(); - // FIXME (#908): Need unwinding on windows. This will end up aborting - fail_sched_loop(); + die(); + // FIXME (#908): Need unwinding on windows. This will end up aborting + fail_sched_loop(); #endif + } } void rust_task::fail_sched_loop() { @@ -528,11 +538,11 @@ rust_task::new_stack(size_t requested_sz) { // arbitrarily selected as 2x the maximum stack size. if (!unwinding && used_stack > max_stack) { LOG_ERR(this, task, "task %" PRIxPTR " ran out of stack", this); - fail(); + abort(); } else if (unwinding && used_stack > max_stack * 2) { LOG_ERR(this, task, "task %" PRIxPTR " ran out of stack during unwinding", this); - fail(); + abort(); } size_t sz = rust_stk_sz + RED_ZONE_SIZE; diff --git a/src/rt/rust_task.h b/src/rt/rust_task.h index 8c9ec172c45ec..c771685f23170 100644 --- a/src/rt/rust_task.h +++ b/src/rt/rust_task.h @@ -196,7 +196,8 @@ struct task_notification { }; extern "C" void -rust_task_fail(rust_task *task, +rust_task_fail(bool do_throw, + rust_task *task, char const *expr, char const *file, size_t line); @@ -279,7 +280,8 @@ rust_task : public kernel_owned bool must_fail_from_being_killed_inner(); // Called by rust_task_fail to unwind on failure - void begin_failure(char const *expr, + void begin_failure(bool do_throw, + char const *expr, char const *file, size_t line); @@ -287,7 +289,8 @@ rust_task : public kernel_owned friend void cleanup_task(cleanup_args *a); friend void reset_stack_limit_on_c_stack(reset_args *a); friend void new_stack_slow(new_stack_args *a); - friend void rust_task_fail(rust_task *task, + friend void rust_task_fail(bool do_throw, + rust_task *task, char const *expr, char const *file, size_t line); @@ -343,7 +346,8 @@ rust_task : public kernel_owned // Fail self, assuming caller-on-stack is this task. void fail(); - void fail(char const *expr, char const *file, size_t line); + void fail(bool do_throw, + char const *expr, char const *file, size_t line); // Propagate failure to the entire rust runtime. void fail_sched_loop(); diff --git a/src/rt/rust_type.h b/src/rt/rust_type.h index ece0d48c3ae44..fa2e0ea3e27c4 100644 --- a/src/rt/rust_type.h +++ b/src/rt/rust_type.h @@ -21,7 +21,8 @@ struct rust_opaque_box; // - the main function: has a NULL environment, but uses the void* arg // - unique closures of type fn~(): have a non-NULL environment, but // no arguments (and hence the final void*) is harmless -typedef void (*CDECL spawn_fn)(void*, rust_opaque_box*, void *); +// - return type indicates success or failure; false = fail +typedef bool (*CDECL spawn_fn)(void*, rust_opaque_box*, void *); struct type_desc; diff --git a/src/rt/rust_upcall.cpp b/src/rt/rust_upcall.cpp index 9f39e1433fc63..4a40957382902 100644 --- a/src/rt/rust_upcall.cpp +++ b/src/rt/rust_upcall.cpp @@ -101,6 +101,7 @@ upcall_call_shim_on_rust_stack(void *args, void *fn_ptr) { /**********************************************************************/ struct s_fail_args { + bool unwind; rust_task *task; char const *expr; char const *file; @@ -111,20 +112,22 @@ extern "C" CDECL void upcall_s_fail(s_fail_args *args) { rust_task *task = args->task; LOG_UPCALL_ENTRY(task); - task->fail(args->expr, args->file, args->line); + task->fail(args->unwind, args->expr, + args->file, args->line); } extern "C" CDECL void -upcall_fail(char const *expr, +upcall_fail(bool unwind, + char const *expr, char const *file, size_t line) { - rust_task *task = rust_try_get_current_task(); + rust_task *task = rust_get_current_task(); if (task == NULL) { // FIXME #5161: Need to think about what to do here printf("failure outside of a task"); abort(); } - s_fail_args args = {task,expr,file,line}; + s_fail_args args = {unwind,task,expr,file,line}; UPCALL_SWITCH_STACK(task, &args, upcall_s_fail); } @@ -132,10 +135,11 @@ upcall_fail(char const *expr, // autogenerated wrappers for upcall_fail. Remove this when we fully move away // away from the C upcall path. extern "C" CDECL void -rust_upcall_fail(char const *expr, +rust_upcall_fail(bool unwind, + char const *expr, char const *file, size_t line) { - upcall_fail(expr, file, line); + upcall_fail(unwind, expr, file, line); } struct s_trace_args { diff --git a/src/test/run-fail/issue-2144.rs b/src/test/run-fail/issue-2144.rs index 56b7acc7f0f18..e09d034310517 100644 --- a/src/test/run-fail/issue-2144.rs +++ b/src/test/run-fail/issue-2144.rs @@ -13,6 +13,8 @@ // Don't leak when the landing pads need to request more stack // than is allowed during normal execution +// xfail-test + fn useBlock(f: ~fn() -> uint) { useBlock(|| 22u ) } fn main() { useBlock(|| 22u ); diff --git a/src/test/run-fail/morestack1.rs b/src/test/run-fail/morestack1.rs index f4faac0b2aba6..2ce913ec568dd 100644 --- a/src/test/run-fail/morestack1.rs +++ b/src/test/run-fail/morestack1.rs @@ -9,6 +9,9 @@ // except according to those terms. // error-pattern:fail + +// xfail-test + fn getbig(i: int) { if i != 0 { getbig(i - 1); diff --git a/src/test/run-fail/too-much-recursion.rs b/src/test/run-fail/too-much-recursion.rs index 04514b13455fb..26ebaf3ac673d 100644 --- a/src/test/run-fail/too-much-recursion.rs +++ b/src/test/run-fail/too-much-recursion.rs @@ -10,6 +10,8 @@ // error-pattern:ran out of stack +// xfail-test + // Test that the task fails after hiting the recursion limit fn main() {