diff --git a/src/doc/book/syntax-index.md b/src/doc/book/syntax-index.md
index 01f06f718f8..f7e32943c63 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 b310b2b3351..3aa76d168df 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 cbda1e8880b..7de60995445 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<PathElem>,
-                                          def_path: DefPath,
+                                          parent_path: Vec<PathElem>,
+                                          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 e83fe60f419..be6bb8689ee 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 714f0a4350b..8f74acd9ebd 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<ast_map::PathElem>,
-                                 def_path: ast_map::DefPath,
+                                 parent_path: Vec<ast_map::PathElem>,
+                                 parent_def_path: ast_map::DefPath,
                                  par_doc: rbml::Doc,
                                  orig_did: DefId)
                                  -> Result<&'tcx InlinedItem, (Vec<ast_map::PathElem>,
                                                                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 357158c24ba..8126970759e 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<for<'tcx> FnMut(Cmd,
                         &ty::ctxt<'tcx>,
-                        Vec<hir_map::PathElem>,
-                        hir_map::DefPath,
+                        Vec<hir_map::PathElem>, // parent_path
+                        hir_map::DefPath,       // parent_def_path
                         rbml::Doc,
                         DefId)
                         -> Result<&'tcx InlinedItem, (Vec<hir_map::PathElem>,
                                                       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 5cb12627d6b..46e6243c8bf 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 368708d470b..d4a7a7c8b48 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 529e65dace0..55a41201b2b 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 9894626e284..dae0d3b55c0 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 6e4ba337b08..b42d1d1b8d4 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>(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<T: NoUnsafeCell + ?Sized> RecoverSafe for *const T {}
-impl<T: NoUnsafeCell + ?Sized> RecoverSafe for *mut T {}
+impl<'a, T: RefRecoverSafe + ?Sized> RecoverSafe for &'a T {}
+impl<T: RefRecoverSafe + ?Sized> RecoverSafe for *const T {}
+impl<T: RefRecoverSafe + ?Sized> RecoverSafe for *mut T {}
 impl<T: RecoverSafe> RecoverSafe for Unique<T> {}
-impl<T: NoUnsafeCell + ?Sized> RecoverSafe for Shared<T> {}
+impl<T: RefRecoverSafe + ?Sized> RecoverSafe for Shared<T> {}
 impl<T: ?Sized> RecoverSafe for Mutex<T> {}
 impl<T: ?Sized> RecoverSafe for RwLock<T> {}
 impl<T> RecoverSafe for AssertRecoverSafe<T> {}
@@ -169,15 +174,16 @@ impl<T> RecoverSafe for AssertRecoverSafe<T> {}
 // 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<T: NoUnsafeCell + ?Sized> RecoverSafe for Rc<T> {}
-impl<T: NoUnsafeCell + ?Sized> RecoverSafe for Arc<T> {}
+impl<T: RefRecoverSafe + ?Sized> RecoverSafe for Rc<T> {}
+impl<T: RefRecoverSafe + ?Sized> RecoverSafe for Arc<T> {}
 
-// 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<T: ?Sized> !NoUnsafeCell for UnsafeCell<T> {}
+// 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<T: ?Sized> !RefRecoverSafe for UnsafeCell<T> {}
+impl<T> RefRecoverSafe for AssertRecoverSafe<T> {}
 
 impl<T> AssertRecoverSafe<T> {
     /// Creates a new `AssertRecoverSafe` wrapper around the provided type.
diff --git a/src/libstd/panicking.rs b/src/libstd/panicking.rs
index 2b2af350c99..3f9a1c30ef4 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<usize> = 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<F>(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<Fn(&PanicInfo) + 'static + Sync + Send> {
+    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::<String>() {
+        None => match info.payload.downcast_ref::<String>() {
             Some(s) => &s[..],
             None => "Box<Any>",
         }
@@ -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 81ff9b05172..34773e1657c 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 00000000000..bfeb407dd25
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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 00000000000..6999aa715e7
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// error-pattern:thread '<main>' 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 00000000000..fec1db24adf
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// error-pattern:thread '<main>' 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 2da1a758709..9b349c0e4e2 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) {
     <u32 as T<_, _>>::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), <u32 as T<_, _>>::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 00000000000..1ed592d3d6b
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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 00000000000..783a44beaf3
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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 00000000000..edf65e8e2aa
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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 cd2457e8a52..9949b79278c 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::<Box<T>>();
         assert::<Vec<T>>();
         assert::<RefCell<T>>();
+        assert::<AssertRecoverSafe<T>>();
+        assert::<&AssertRecoverSafe<T>>();
+        assert::<Rc<AssertRecoverSafe<T>>>();
+        assert::<Arc<AssertRecoverSafe<T>>>();
     }
 }