diff --git a/src/doc/book/syntax-index.md b/src/doc/book/syntax-index.md index 01f06f718f857..f7e32943c638e 100644 --- a/src/doc/book/syntax-index.md +++ b/src/doc/book/syntax-index.md @@ -41,6 +41,7 @@ * `!` (`ident!(…)`, `ident!{…}`, `ident![…]`): denotes macro expansion. See [Macros]. * `!` (`!expr`): bitwise or logical complement. Overloadable (`Not`). +* `!=` (`var != expr`): nonequality comparison. Overloadable (`PartialEq`). * `%` (`expr % expr`): arithmetic remainder. Overloadable (`Rem`). * `%=` (`var %= expr`): arithmetic remainder & assignment. * `&` (`expr & expr`): bitwise and. Overloadable (`BitAnd`). @@ -75,13 +76,13 @@ * `;` (`[…; len]`): part of fixed-size array syntax. See [Primitive Types (Arrays)]. * `<<` (`expr << expr`): left-shift. Overloadable (`Shl`). * `<<=` (`var <<= expr`): left-shift & assignment. -* `<` (`expr < expr`): less-than comparison. Overloadable (`Cmp`, `PartialCmp`). -* `<=` (`var <= expr`): less-than or equal-to comparison. Overloadable (`Cmp`, `PartialCmp`). +* `<` (`expr < expr`): less-than comparison. Overloadable (`PartialOrd`). +* `<=` (`var <= expr`): less-than or equal-to comparison. Overloadable (`PartialOrd`). * `=` (`var = expr`, `ident = type`): assignment/equivalence. See [Variable Bindings], [`type` Aliases], generic parameter defaults. -* `==` (`var == expr`): comparison. Overloadable (`Eq`, `PartialEq`). +* `==` (`var == expr`): equality comparison. Overloadable (`PartialEq`). * `=>` (`pat => expr`): part of match arm syntax. See [Match]. -* `>` (`expr > expr`): greater-than comparison. Overloadable (`Cmp`, `PartialCmp`). -* `>=` (`var >= expr`): greater-than or equal-to comparison. Overloadable (`Cmp`, `PartialCmp`). +* `>` (`expr > expr`): greater-than comparison. Overloadable (`PartialOrd`). +* `>=` (`var >= expr`): greater-than or equal-to comparison. Overloadable (`PartialOrd`). * `>>` (`expr >> expr`): right-shift. Overloadable (`Shr`). * `>>=` (`var >>= expr`): right-shift & assignment. * `@` (`ident @ pat`): pattern binding. See [Patterns (Bindings)]. diff --git a/src/grammar/parser-lalr.y b/src/grammar/parser-lalr.y index b310b2b3351c5..3aa76d168df07 100644 --- a/src/grammar/parser-lalr.y +++ b/src/grammar/parser-lalr.y @@ -1822,8 +1822,8 @@ unpaired_token | LIT_FLOAT { $$ = mk_atom(yytext); } | LIT_STR { $$ = mk_atom(yytext); } | LIT_STR_RAW { $$ = mk_atom(yytext); } -| LIT_BYTE_STR { $$ = mk_atom(yytext); } -| LIT_BYTE_STR_RAW { $$ = mk_atom(yytext); } +| LIT_BYTE_STR { $$ = mk_atom(yytext); } +| LIT_BYTE_STR_RAW { $$ = mk_atom(yytext); } | IDENT { $$ = mk_atom(yytext); } | UNDERSCORE { $$ = mk_atom(yytext); } | LIFETIME { $$ = mk_atom(yytext); } diff --git a/src/librustc/front/map/mod.rs b/src/librustc/front/map/mod.rs index cbda1e8880b66..7de6099544525 100644 --- a/src/librustc/front/map/mod.rs +++ b/src/librustc/front/map/mod.rs @@ -839,11 +839,10 @@ pub fn map_crate<'ast>(forest: &'ast mut Forest) -> Map<'ast> { } /// Used for items loaded from external crate that are being inlined into this -/// crate. The `path` should be the path to the item but should not include -/// the item itself. +/// crate. pub fn map_decoded_item<'ast, F: FoldOps>(map: &Map<'ast>, - path: Vec, - def_path: DefPath, + parent_path: Vec, + parent_def_path: DefPath, ii: InlinedItem, fold_ops: F) -> &'ast InlinedItem { @@ -862,7 +861,7 @@ pub fn map_decoded_item<'ast, F: FoldOps>(map: &Map<'ast>, }; let ii_parent = map.forest.inlined_items.alloc(InlinedParent { - path: path, + path: parent_path, ii: ii }); @@ -872,7 +871,7 @@ pub fn map_decoded_item<'ast, F: FoldOps>(map: &Map<'ast>, map.krate(), ii_parent, ii_parent_id, - def_path, + parent_def_path, mem::replace(&mut *map.map.borrow_mut(), vec![]), mem::replace(&mut *map.definitions.borrow_mut(), Definitions::new())); ii_parent.ii.visit(&mut collector); diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index e83fe60f419a6..be6bb8689eefa 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -701,9 +701,9 @@ pub struct Constant<'tcx> { #[derive(Clone, Copy, Debug, PartialEq, RustcEncodable, RustcDecodable)] pub enum ItemKind { Constant, + /// This is any sort of callable (usually those that have a type of `fn(…) -> …`). This + /// includes functions, constructors, but not methods which have their own ItemKind. Function, - Struct, - Variant, Method, } diff --git a/src/librustc_metadata/astencode.rs b/src/librustc_metadata/astencode.rs index 714f0a4350b3d..8f74acd9ebdf6 100644 --- a/src/librustc_metadata/astencode.rs +++ b/src/librustc_metadata/astencode.rs @@ -124,20 +124,20 @@ impl<'a, 'b, 'c, 'tcx> ast_map::FoldOps for &'a DecodeContext<'b, 'c, 'tcx> { /// ast-map. pub fn decode_inlined_item<'tcx>(cdata: &cstore::crate_metadata, tcx: &ty::ctxt<'tcx>, - path: Vec, - def_path: ast_map::DefPath, + parent_path: Vec, + parent_def_path: ast_map::DefPath, par_doc: rbml::Doc, orig_did: DefId) -> Result<&'tcx InlinedItem, (Vec, ast_map::DefPath)> { match par_doc.opt_child(c::tag_ast) { - None => Err((path, def_path)), + None => Err((parent_path, parent_def_path)), Some(ast_doc) => { let mut path_as_str = None; debug!("> Decoding inlined fn: {:?}::?", { // Do an Option dance to use the path after it is moved below. - let s = ast_map::path_to_string(path.iter().cloned()); + let s = ast_map::path_to_string(parent_path.iter().cloned()); path_as_str = Some(s); path_as_str.as_ref().map(|x| &x[..]) }); @@ -152,8 +152,11 @@ pub fn decode_inlined_item<'tcx>(cdata: &cstore::crate_metadata, last_filemap_index: Cell::new(0) }; let raw_ii = decode_ast(ast_doc); - let ii = ast_map::map_decoded_item(&dcx.tcx.map, path, def_path, raw_ii, dcx); - + let ii = ast_map::map_decoded_item(&dcx.tcx.map, + parent_path, + parent_def_path, + raw_ii, + dcx); let name = match *ii { InlinedItem::Item(ref i) => i.name, InlinedItem::Foreign(ref i) => i.name, diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index 357158c24bacd..8126970759e6d 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -763,29 +763,54 @@ pub fn get_item_name(intr: &IdentInterner, cdata: Cmd, id: DefIndex) -> ast::Nam pub type DecodeInlinedItem<'a> = Box FnMut(Cmd, &ty::ctxt<'tcx>, - Vec, - hir_map::DefPath, + Vec, // parent_path + hir_map::DefPath, // parent_def_path rbml::Doc, DefId) -> Result<&'tcx InlinedItem, (Vec, hir_map::DefPath)> + 'a>; -pub fn maybe_get_item_ast<'tcx>(cdata: Cmd, tcx: &ty::ctxt<'tcx>, id: DefIndex, +pub fn maybe_get_item_ast<'tcx>(cdata: Cmd, + tcx: &ty::ctxt<'tcx>, + id: DefIndex, mut decode_inlined_item: DecodeInlinedItem) -> FoundAst<'tcx> { debug!("Looking up item: {:?}", id); let item_doc = cdata.lookup_item(id); let item_did = item_def_id(item_doc, cdata); - let path = item_path(item_doc).split_last().unwrap().1.to_vec(); - let def_path = def_path(cdata, id); - match decode_inlined_item(cdata, tcx, path, def_path, item_doc, item_did) { + let parent_path = { + let mut path = item_path(item_doc); + path.pop(); + path + }; + let parent_def_path = { + let mut def_path = def_path(cdata, id); + def_path.pop(); + def_path + }; + match decode_inlined_item(cdata, + tcx, + parent_path, + parent_def_path, + item_doc, + item_did) { Ok(ii) => FoundAst::Found(ii), - Err((path, def_path)) => { + Err((mut parent_path, mut parent_def_path)) => { match item_parent_item(cdata, item_doc) { - Some(did) => { - let parent_item = cdata.lookup_item(did.index); - match decode_inlined_item(cdata, tcx, path, def_path, parent_item, did) { - Ok(ii) => FoundAst::FoundParent(did, ii), + Some(parent_did) => { + // Remove the last element from the paths, since we are now + // trying to inline the parent. + parent_path.pop(); + parent_def_path.pop(); + + let parent_item = cdata.lookup_item(parent_did.index); + match decode_inlined_item(cdata, + tcx, + parent_path, + parent_def_path, + parent_item, + parent_did) { + Ok(ii) => FoundAst::FoundParent(parent_did, ii), Err(_) => FoundAst::NotFound } } diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs index 5cb12627d6ba8..46e6243c8bf30 100644 --- a/src/librustc_mir/hair/cx/expr.rs +++ b/src/librustc_mir/hair/cx/expr.rs @@ -519,51 +519,51 @@ fn convert_path_expr<'a, 'tcx: 'a>(cx: &mut Cx<'a, 'tcx>, expr: &'tcx hir::Expr) let substs = cx.tcx.mk_substs(cx.tcx.node_id_item_substs(expr.id).substs); // Otherwise there may be def_map borrow conflicts let def = cx.tcx.def_map.borrow()[&expr.id].full_def(); - match def { - def::DefVariant(_, def_id, false) | - def::DefStruct(def_id) | - def::DefFn(def_id, _) | - def::DefMethod(def_id) => { - let kind = match def { - def::DefVariant(..) => ItemKind::Variant, - def::DefStruct(..) => ItemKind::Struct, - def::DefFn(..) => ItemKind::Function, - def::DefMethod(..) => ItemKind::Method, - _ => panic!() - }; - ExprKind::Literal { - literal: Literal::Item { def_id: def_id, kind: kind, substs: substs } + let (def_id, kind) = match def { + // A variant constructor. + def::DefVariant(_, def_id, false) => (def_id, ItemKind::Function), + // A regular function. + def::DefFn(def_id, _) => (def_id, ItemKind::Function), + def::DefMethod(def_id) => (def_id, ItemKind::Method), + def::DefStruct(def_id) => { + match cx.tcx.node_id_to_type(expr.id).sty { + // A tuple-struct constructor. + ty::TyBareFn(..) => (def_id, ItemKind::Function), + // This is a special case: a unit struct which is used as a value. We return a + // completely different ExprKind here to account for this special case. + ty::TyStruct(adt_def, substs) => return ExprKind::Adt { + adt_def: adt_def, + variant_index: 0, + substs: substs, + fields: vec![], + base: None + }, + ref sty => panic!("unexpected sty: {:?}", sty) } }, def::DefConst(def_id) | def::DefAssociatedConst(def_id) => { if let Some(v) = cx.try_const_eval_literal(expr) { - ExprKind::Literal { literal: v } + return ExprKind::Literal { literal: v }; } else { - ExprKind::Literal { - literal: Literal::Item { - def_id: def_id, - kind: ItemKind::Constant, - substs: substs - } - } + (def_id, ItemKind::Constant) } } - - def::DefStatic(node_id, _) => - ExprKind::StaticRef { - id: node_id, - }, + def::DefStatic(node_id, _) => return ExprKind::StaticRef { + id: node_id, + }, def @ def::DefLocal(..) | - def @ def::DefUpvar(..) => - convert_var(cx, expr, def), + def @ def::DefUpvar(..) => return convert_var(cx, expr, def), def => cx.tcx.sess.span_bug( expr.span, &format!("def `{:?}` not yet implemented", def)), + }; + ExprKind::Literal { + literal: Literal::Item { def_id: def_id, kind: kind, substs: substs } } } diff --git a/src/librustc_trans/trans/mir/did.rs b/src/librustc_trans/trans/mir/did.rs index 368708d470bef..d4a7a7c8b48ff 100644 --- a/src/librustc_trans/trans/mir/did.rs +++ b/src/librustc_trans/trans/mir/did.rs @@ -39,9 +39,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { did: DefId) -> OperandRef<'tcx> { match kind { - ItemKind::Function | - ItemKind::Struct | - ItemKind::Variant => self.trans_fn_ref(bcx, ty, substs, did), + ItemKind::Function => self.trans_fn_ref(bcx, ty, substs, did), ItemKind::Method => match bcx.tcx().impl_or_trait_item(did).container() { ty::ImplContainer(_) => self.trans_fn_ref(bcx, ty, substs, did), ty::TraitContainer(tdid) => self.trans_static_method(bcx, ty, did, tdid, substs) diff --git a/src/librustc_trans/trans/mir/rvalue.rs b/src/librustc_trans/trans/mir/rvalue.rs index 529e65dace04c..55a41201b2ba1 100644 --- a/src/librustc_trans/trans/mir/rvalue.rs +++ b/src/librustc_trans/trans/mir/rvalue.rs @@ -19,6 +19,7 @@ use trans::common::{self, Block, Result}; use trans::debuginfo::DebugLoc; use trans::declare; use trans::expr; +use trans::adt; use trans::machine; use trans::type_::Type; use trans::type_of; @@ -26,21 +27,22 @@ use trans::tvec; use super::MirContext; use super::operand::{OperandRef, OperandValue}; +use super::lvalue::LvalueRef; impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { pub fn trans_rvalue(&mut self, bcx: Block<'bcx, 'tcx>, - lldest: ValueRef, + dest: LvalueRef<'tcx>, rvalue: &mir::Rvalue<'tcx>) -> Block<'bcx, 'tcx> { - debug!("trans_rvalue(lldest={}, rvalue={:?})", - bcx.val_to_string(lldest), + debug!("trans_rvalue(dest.llval={}, rvalue={:?})", + bcx.val_to_string(dest.llval), rvalue); match *rvalue { mir::Rvalue::Use(ref operand) => { - self.trans_operand_into(bcx, lldest, operand); + self.trans_operand_into(bcx, dest.llval, operand); bcx } @@ -49,7 +51,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { // into-coerce of a thin pointer to a fat pointer - just // use the operand path. let (bcx, temp) = self.trans_rvalue_operand(bcx, rvalue); - self.store_operand(bcx, lldest, temp); + self.store_operand(bcx, dest.llval, temp); return bcx; } @@ -72,12 +74,12 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { base::store_ty(bcx, llval, lltemp, operand.ty); base::coerce_unsized_into(bcx, lltemp, operand.ty, - lldest, cast_ty); + dest.llval, cast_ty); } OperandValue::Ref(llref) => { base::coerce_unsized_into(bcx, llref, operand.ty, - lldest, cast_ty); + dest.llval, cast_ty); } } bcx @@ -86,20 +88,31 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { mir::Rvalue::Repeat(ref elem, ref count) => { let elem = self.trans_operand(bcx, elem); let size = self.trans_constant(bcx, count).immediate(); - let base = expr::get_dataptr(bcx, lldest); + let base = expr::get_dataptr(bcx, dest.llval); tvec::iter_vec_raw(bcx, base, elem.ty, size, |bcx, llslot, _| { self.store_operand(bcx, llslot, elem); bcx }) } - mir::Rvalue::Aggregate(_, ref operands) => { - for (i, operand) in operands.iter().enumerate() { - // Note: perhaps this should be StructGep, but - // note that in some cases the values here will - // not be structs but arrays. - let lldest_i = build::GEPi(bcx, lldest, &[0, i]); - self.trans_operand_into(bcx, lldest_i, operand); + mir::Rvalue::Aggregate(ref kind, ref operands) => { + match *kind { + // Unit struct, which is translated very differently compared to any other + // aggregate + mir::AggregateKind::Adt(adt_def, 0, _) + if adt_def.struct_variant().kind() == ty::VariantKind::Unit => { + let repr = adt::represent_type(bcx.ccx(), dest.ty.to_ty(bcx.tcx())); + adt::trans_set_discr(bcx, &*repr, dest.llval, 0); + }, + _ => { + for (i, operand) in operands.iter().enumerate() { + // Note: perhaps this should be StructGep, but + // note that in some cases the values here will + // not be structs but arrays. + let lldest_i = build::GEPi(bcx, dest.llval, &[0, i]); + self.trans_operand_into(bcx, lldest_i, operand); + } + } } bcx } @@ -113,9 +126,9 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { let llbase1 = build::GEPi(bcx, llbase, &[from_start]); let adj = common::C_uint(ccx, from_start + from_end); let lllen1 = build::Sub(bcx, lllen, adj, DebugLoc::None); - let lladdrdest = expr::get_dataptr(bcx, lldest); + let lladdrdest = expr::get_dataptr(bcx, dest.llval); build::Store(bcx, llbase1, lladdrdest); - let llmetadest = expr::get_meta(bcx, lldest); + let llmetadest = expr::get_meta(bcx, dest.llval); build::Store(bcx, lllen1, llmetadest); bcx } @@ -127,7 +140,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { _ => { assert!(rvalue_creates_operand(rvalue)); let (bcx, temp) = self.trans_rvalue_operand(bcx, rvalue); - self.store_operand(bcx, lldest, temp); + self.store_operand(bcx, dest.llval, temp); bcx } } diff --git a/src/librustc_trans/trans/mir/statement.rs b/src/librustc_trans/trans/mir/statement.rs index 9894626e284a2..dae0d3b55c0ba 100644 --- a/src/librustc_trans/trans/mir/statement.rs +++ b/src/librustc_trans/trans/mir/statement.rs @@ -31,7 +31,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { let index = index as usize; match self.temps[index as usize] { TempRef::Lvalue(tr_dest) => { - self.trans_rvalue(bcx, tr_dest.llval, rvalue) + self.trans_rvalue(bcx, tr_dest, rvalue) } TempRef::Operand(None) => { let (bcx, operand) = self.trans_rvalue_operand(bcx, rvalue); @@ -47,7 +47,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } _ => { let tr_dest = self.trans_lvalue(bcx, lvalue); - self.trans_rvalue(bcx, tr_dest.llval, rvalue) + self.trans_rvalue(bcx, tr_dest, rvalue) } } } diff --git a/src/libstd/panic.rs b/src/libstd/panic.rs index 6e4ba337b08ee..b42d1d1b8d4ce 100644 --- a/src/libstd/panic.rs +++ b/src/libstd/panic.rs @@ -21,6 +21,8 @@ use sync::{Arc, Mutex, RwLock}; use sys_common::unwind; use thread::Result; +pub use panicking::{take_handler, set_handler, PanicInfo, Location}; + /// A marker trait which represents "panic safe" types in Rust. /// /// This trait is implemented by default for many types and behaves similarly in @@ -99,8 +101,11 @@ use thread::Result; across a recover boundary"] pub trait RecoverSafe {} -/// A marker trait representing types which do not contain an `UnsafeCell` by -/// value internally. +/// A marker trait representing types where a shared reference is considered +/// recover safe. +/// +/// This trait is namely not implemented by `UnsafeCell`, the root of all +/// interior mutability. /// /// This is a "helper marker trait" used to provide impl blocks for the /// `RecoverSafe` trait, for more information see that documentation. @@ -108,7 +113,7 @@ pub trait RecoverSafe {} #[rustc_on_unimplemented = "the type {Self} contains interior mutability \ and a reference may not be safely transferrable \ across a recover boundary"] -pub trait NoUnsafeCell {} +pub trait RefRecoverSafe {} /// A simple wrapper around a type to assert that it is panic safe. /// @@ -157,11 +162,11 @@ pub struct AssertRecoverSafe(T); // * Our custom AssertRecoverSafe wrapper is indeed recover safe impl RecoverSafe for .. {} impl<'a, T: ?Sized> !RecoverSafe for &'a mut T {} -impl<'a, T: NoUnsafeCell + ?Sized> RecoverSafe for &'a T {} -impl RecoverSafe for *const T {} -impl RecoverSafe for *mut T {} +impl<'a, T: RefRecoverSafe + ?Sized> RecoverSafe for &'a T {} +impl RecoverSafe for *const T {} +impl RecoverSafe for *mut T {} impl RecoverSafe for Unique {} -impl RecoverSafe for Shared {} +impl RecoverSafe for Shared {} impl RecoverSafe for Mutex {} impl RecoverSafe for RwLock {} impl RecoverSafe for AssertRecoverSafe {} @@ -169,15 +174,16 @@ impl RecoverSafe for AssertRecoverSafe {} // not covered via the Shared impl above b/c the inner contents use // Cell/AtomicUsize, but the usage here is recover safe so we can lift the // impl up one level to Arc/Rc itself -impl RecoverSafe for Rc {} -impl RecoverSafe for Arc {} +impl RecoverSafe for Rc {} +impl RecoverSafe for Arc {} -// Pretty simple implementations for the `NoUnsafeCell` marker trait, basically -// just saying that this is a marker trait and `UnsafeCell` is the only thing -// which doesn't implement it (which then transitively applies to everything -// else. -impl NoUnsafeCell for .. {} -impl !NoUnsafeCell for UnsafeCell {} +// Pretty simple implementations for the `RefRecoverSafe` marker trait, +// basically just saying that this is a marker trait and `UnsafeCell` is the +// only thing which doesn't implement it (which then transitively applies to +// everything else. +impl RefRecoverSafe for .. {} +impl !RefRecoverSafe for UnsafeCell {} +impl RefRecoverSafe for AssertRecoverSafe {} impl AssertRecoverSafe { /// Creates a new `AssertRecoverSafe` wrapper around the provided type. diff --git a/src/libstd/panicking.rs b/src/libstd/panicking.rs index 2b2af350c992c..3f9a1c30ef493 100644 --- a/src/libstd/panicking.rs +++ b/src/libstd/panicking.rs @@ -15,10 +15,12 @@ use any::Any; use cell::Cell; use cell::RefCell; use intrinsics; +use sync::StaticRwLock; use sys::stdio::Stderr; use sys_common::backtrace; use sys_common::thread_info; use sys_common::util; +use thread; thread_local! { pub static PANIC_COUNT: Cell = Cell::new(0) } @@ -28,11 +30,138 @@ thread_local! { } } -fn log_panic(obj: &(Any+Send), file: &'static str, line: u32, - log_backtrace: bool) { - let msg = match obj.downcast_ref::<&'static str>() { +#[derive(Copy, Clone)] +enum Handler { + Default, + Custom(*mut (Fn(&PanicInfo) + 'static + Sync + Send)), +} + +static HANDLER_LOCK: StaticRwLock = StaticRwLock::new(); +static mut HANDLER: Handler = Handler::Default; + +/// Registers a custom panic handler, replacing any that was previously +/// registered. +/// +/// The panic handler is invoked when a thread panics, but before it begins +/// unwinding the stack. The default handler prints a message to standard error +/// and generates a backtrace if requested, but this behavior can be customized +/// with the `set_handler` and `take_handler` functions. +/// +/// The handler is provided with a `PanicInfo` struct which contains information +/// about the origin of the panic, including the payload passed to `panic!` and +/// the source code location from which the panic originated. +/// +/// The panic handler is a global resource. +/// +/// # Panics +/// +/// Panics if called from a panicking thread. +#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")] +pub fn set_handler(handler: F) where F: Fn(&PanicInfo) + 'static + Sync + Send { + if thread::panicking() { + panic!("cannot modify the panic handler from a panicking thread"); + } + + let handler = Box::new(handler); + unsafe { + let lock = HANDLER_LOCK.write(); + let old_handler = HANDLER; + HANDLER = Handler::Custom(Box::into_raw(handler)); + drop(lock); + + if let Handler::Custom(ptr) = old_handler { + Box::from_raw(ptr); + } + } +} + +/// Unregisters the current panic handler, returning it. +/// +/// If no custom handler is registered, the default handler will be returned. +/// +/// # Panics +/// +/// Panics if called from a panicking thread. +#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")] +pub fn take_handler() -> Box { + if thread::panicking() { + panic!("cannot modify the panic handler from a panicking thread"); + } + + unsafe { + let lock = HANDLER_LOCK.write(); + let handler = HANDLER; + HANDLER = Handler::Default; + drop(lock); + + match handler { + Handler::Default => Box::new(default_handler), + Handler::Custom(ptr) => {Box::from_raw(ptr)} // FIXME #30530 + } + } +} + +/// A struct providing information about a panic. +#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")] +pub struct PanicInfo<'a> { + payload: &'a (Any + Send), + location: Location<'a>, +} + +impl<'a> PanicInfo<'a> { + /// Returns the payload associated with the panic. + /// + /// This will commonly, but not always, be a `&'static str` or `String`. + #[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")] + pub fn payload(&self) -> &(Any + Send) { + self.payload + } + + /// Returns information about the location from which the panic originated, + /// if available. + /// + /// This method will currently always return `Some`, but this may change + /// in future versions. + #[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")] + pub fn location(&self) -> Option<&Location> { + Some(&self.location) + } +} + +/// A struct containing information about the location of a panic. +#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")] +pub struct Location<'a> { + file: &'a str, + line: u32, +} + +impl<'a> Location<'a> { + /// Returns the name of the source file from which the panic originated. + #[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")] + pub fn file(&self) -> &str { + self.file + } + + /// Returns the line number from which the panic originated. + #[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")] + pub fn line(&self) -> u32 { + self.line + } +} + +fn default_handler(info: &PanicInfo) { + let panics = PANIC_COUNT.with(|s| s.get()); + + // If this is a double panic, make sure that we print a backtrace + // for this panic. Otherwise only print it if logging is enabled. + let log_backtrace = panics >= 2 || backtrace::log_enabled(); + + let file = info.location.file; + let line = info.location.line; + + let msg = match info.payload.downcast_ref::<&'static str>() { Some(s) => *s, - None => match obj.downcast_ref::() { + None => match info.payload.downcast_ref::() { Some(s) => &s[..], None => "Box", } @@ -81,10 +210,21 @@ pub fn on_panic(obj: &(Any+Send), file: &'static str, line: u32) { unsafe { intrinsics::abort() } } - // If this is a double panic, make sure that we print a backtrace - // for this panic. Otherwise only print it if logging is enabled. - let log_backtrace = panics >= 2 || backtrace::log_enabled(); - log_panic(obj, file, line, log_backtrace); + let info = PanicInfo { + payload: obj, + location: Location { + file: file, + line: line, + }, + }; + + unsafe { + let _lock = HANDLER_LOCK.read(); + match HANDLER { + Handler::Default => default_handler(&info), + Handler::Custom(ptr) => (*ptr)(&info), + } + } if panics >= 2 { // If a thread panics while it's already unwinding then we diff --git a/src/snapshots.txt b/src/snapshots.txt index 81ff9b051721d..34773e1657c2d 100644 --- a/src/snapshots.txt +++ b/src/snapshots.txt @@ -3,6 +3,7 @@ S 2015-12-18 3391630 linux-x86_64 97e2a5eb8904962df8596e95d6e5d9b574d73bf4 macos-i386 ca52d2d3ba6497ed007705ee3401cf7efc136ca1 macos-x86_64 3c44ffa18f89567c2b81f8d695e711c86d81ffc7 + openbsd-x86_64 6c8aab2c8a169274942f9a15e460069a3ff64be9 winnt-i386 f9056ebd3db9611d31c2dc6dc5f96c7208d5d227 winnt-x86_64 a85a40e535d828016181d3aa40afe34c3e36ab8c diff --git a/src/test/run-fail/panic-set-handler.rs b/src/test/run-fail/panic-set-handler.rs new file mode 100644 index 0000000000000..bfeb407dd25a4 --- /dev/null +++ b/src/test/run-fail/panic-set-handler.rs @@ -0,0 +1,22 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// error-pattern:greetings from the panic handler + +#![feature(std_panic, panic_handler)] +use std::panic; +use std::io::{self, Write}; + +fn main() { + panic::set_handler(|i| { + write!(io::stderr(), "greetings from the panic handler"); + }); + panic!("foobar"); +} diff --git a/src/test/run-fail/panic-set-unset-handler.rs b/src/test/run-fail/panic-set-unset-handler.rs new file mode 100644 index 0000000000000..6999aa715e791 --- /dev/null +++ b/src/test/run-fail/panic-set-unset-handler.rs @@ -0,0 +1,23 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// error-pattern:thread '
' panicked at 'foobar' + +#![feature(std_panic, panic_handler)] +use std::panic; +use std::io::{self, Write}; + +fn main() { + panic::set_handler(|i| { + write!(io::stderr(), "greetings from the panic handler"); + }); + panic::take_handler(); + panic!("foobar"); +} diff --git a/src/test/run-fail/panic-take-handler-nop.rs b/src/test/run-fail/panic-take-handler-nop.rs new file mode 100644 index 0000000000000..fec1db24adf09 --- /dev/null +++ b/src/test/run-fail/panic-take-handler-nop.rs @@ -0,0 +1,19 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// error-pattern:thread '
' panicked at 'foobar' + +#![feature(std_panic, panic_handler)] +use std::panic; + +fn main() { + panic::take_handler(); + panic!("foobar"); +} diff --git a/src/test/run-pass/mir_refs_correct.rs b/src/test/run-pass/mir_refs_correct.rs index 2da1a75870964..9b349c0e4e21b 100644 --- a/src/test/run-pass/mir_refs_correct.rs +++ b/src/test/run-pass/mir_refs_correct.rs @@ -14,6 +14,8 @@ extern crate mir_external_refs as ext; struct S(u8); +#[derive(Debug, PartialEq, Eq)] +struct Unit; impl S { fn hey() -> u8 { 42 } @@ -175,6 +177,11 @@ fn t20() -> fn(u64, u32)->(u64, u32) { >::staticmeth } +#[rustc_mir] +fn t21() -> Unit { + Unit +} + fn main(){ unsafe { assert_eq!(t1()(), regular()); @@ -214,5 +221,6 @@ fn main(){ assert_eq!(t18()(50u64, 5u64), F::f(50u64, 5u64)); assert_eq!(t19()(322u64, 2u32), F::f(322u64, 2u32)); assert_eq!(t20()(123u64, 38u32), >::staticmeth(123, 38)); + assert_eq!(t21(), Unit); } } diff --git a/src/test/run-pass/panic-handler-chain.rs b/src/test/run-pass/panic-handler-chain.rs new file mode 100644 index 0000000000000..1ed592d3d6b92 --- /dev/null +++ b/src/test/run-pass/panic-handler-chain.rs @@ -0,0 +1,33 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![feature(panic_handler, const_fn, std_panic)] + +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::panic; +use std::thread; + +static A: AtomicUsize = AtomicUsize::new(0); +static B: AtomicUsize = AtomicUsize::new(0); + +fn main() { + panic::set_handler(|_| { A.fetch_add(1, Ordering::SeqCst); }); + let handler = panic::take_handler(); + panic::set_handler(move |info| { + B.fetch_add(1, Ordering::SeqCst); + handler(info); + }); + + let _ = thread::spawn(|| { + panic!(); + }).join(); + + assert_eq!(1, A.load(Ordering::SeqCst)); + assert_eq!(1, B.load(Ordering::SeqCst)); +} diff --git a/src/test/run-pass/panic-handler-flail-wildly.rs b/src/test/run-pass/panic-handler-flail-wildly.rs new file mode 100644 index 0000000000000..783a44beaf36a --- /dev/null +++ b/src/test/run-pass/panic-handler-flail-wildly.rs @@ -0,0 +1,57 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![feature(panic_handler, std_panic)] + +use std::panic; +use std::thread; + +fn a() { + panic::set_handler(|_| println!("hello yes this is a")); + panic::take_handler(); + panic::set_handler(|_| println!("hello yes this is a part 2")); + panic::take_handler(); +} + +fn b() { + panic::take_handler(); + panic::take_handler(); + panic::take_handler(); + panic::take_handler(); + panic::take_handler(); + panic!(); +} + +fn c() { + panic::set_handler(|_| ()); + panic::set_handler(|_| ()); + panic::set_handler(|_| ()); + panic::set_handler(|_| ()); + panic::set_handler(|_| ()); + panic::set_handler(|_| ()); + panic!(); +} + +fn main() { + for _ in 0..10 { + let mut handles = vec![]; + for _ in 0..10 { + handles.push(thread::spawn(a)); + } + for _ in 0..10 { + handles.push(thread::spawn(b)); + } + for _ in 0..10 { + handles.push(thread::spawn(c)); + } + for handle in handles { + let _ = handle.join(); + } + } +} diff --git a/src/test/run-pass/panic-handler-set-twice.rs b/src/test/run-pass/panic-handler-set-twice.rs new file mode 100644 index 0000000000000..edf65e8e2aa69 --- /dev/null +++ b/src/test/run-pass/panic-handler-set-twice.rs @@ -0,0 +1,27 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![feature(panic_handler, const_fn, std_panic)] + +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::panic; +use std::thread; + +static A: AtomicUsize = AtomicUsize::new(0); + +fn main() { + panic::set_handler(|_| ()); + panic::set_handler(|info| { A.fetch_add(1, Ordering::SeqCst); }); + + let _ = thread::spawn(|| { + panic!(); + }).join(); + + assert_eq!(1, A.load(Ordering::SeqCst)); +} diff --git a/src/test/run-pass/panic-safe.rs b/src/test/run-pass/panic-safe.rs index cd2457e8a52f7..9949b79278c11 100644 --- a/src/test/run-pass/panic-safe.rs +++ b/src/test/run-pass/panic-safe.rs @@ -11,7 +11,7 @@ #![allow(dead_code)] #![feature(recover)] -use std::panic::RecoverSafe; +use std::panic::{RecoverSafe, AssertRecoverSafe}; use std::cell::RefCell; use std::sync::{Mutex, RwLock, Arc}; use std::rc::Rc; @@ -47,5 +47,9 @@ fn main() { assert::>(); assert::>(); assert::>(); + assert::>(); + assert::<&AssertRecoverSafe>(); + assert::>>(); + assert::>>(); } }