From e44ad61a2d8e3bac1d2cbf2467a7202250b8a77e Mon Sep 17 00:00:00 2001
From: Jorge Aparicio <jorge@japaric.io>
Date: Mon, 30 Apr 2018 10:55:24 +0200
Subject: [PATCH 01/12] implement #[panic_implementation]

---
 src/libcore/macros.rs                         | 22 +++++
 src/libcore/panic.rs                          |  6 +-
 src/libcore/panicking.rs                      | 40 ++++++++
 src/librustc/middle/dead.rs                   |  2 +-
 src/librustc/middle/lang_items.rs             |  5 +-
 src/librustc/middle/weak_lang_items.rs        |  2 +-
 src/librustc_typeck/check/mod.rs              | 44 ++++++++-
 src/libstd/lib.rs                             |  2 +
 src/libstd/panicking.rs                       | 97 ++++++++++++++++++-
 src/libsyntax/feature_gate.rs                 |  9 ++
 .../compile-fail/duplicate_entry_error.rs     |  8 +-
 .../feature-gate-panic-implementation.rs      | 21 ++++
 .../compile-fail/no_owned_box_lang_item.rs    |  2 +-
 .../panic-implementation-bad-signature-1.rs   | 24 +++++
 .../panic-implementation-bad-signature-2.rs   | 25 +++++
 .../panic-implementation-bad-signature-3.rs   | 22 +++++
 .../panic-implementation-duplicate.rs         | 28 ++++++
 ...anic-implementation-requires-panic-info.rs | 26 +++++
 .../auxiliary/panic-runtime-lang-items.rs     |  6 +-
 src/test/compile-fail/weak-lang-item.rs       |  2 +-
 src/test/ui/error-codes/E0152.rs              |  2 +-
 src/test/ui/error-codes/E0152.stderr          |  2 +-
 22 files changed, 379 insertions(+), 18 deletions(-)
 create mode 100644 src/test/compile-fail/feature-gate-panic-implementation.rs
 create mode 100644 src/test/compile-fail/panic-implementation-bad-signature-1.rs
 create mode 100644 src/test/compile-fail/panic-implementation-bad-signature-2.rs
 create mode 100644 src/test/compile-fail/panic-implementation-bad-signature-3.rs
 create mode 100644 src/test/compile-fail/panic-implementation-duplicate.rs
 create mode 100644 src/test/compile-fail/panic-implementation-requires-panic-info.rs

diff --git a/src/libcore/macros.rs b/src/libcore/macros.rs
index c830c22ee5f50..f98626d939d10 100644
--- a/src/libcore/macros.rs
+++ b/src/libcore/macros.rs
@@ -9,6 +9,7 @@
 // except according to those terms.
 
 /// Entry point of thread panic, for details, see std::macros
+#[cfg(stage0)]
 #[macro_export]
 #[allow_internal_unstable]
 #[stable(feature = "core", since = "1.6.0")]
@@ -28,6 +29,27 @@ macro_rules! panic {
     });
 }
 
+/// Entry point of thread panic, for details, see std::macros
+#[cfg(not(stage0))]
+#[macro_export]
+#[allow_internal_unstable]
+#[stable(feature = "core", since = "1.6.0")]
+macro_rules! panic {
+    () => (
+        panic!("explicit panic")
+    );
+    ($msg:expr) => ({
+        $crate::panicking::panic_payload($msg, &(file!(), line!(), __rust_unstable_column!()))
+    });
+    ($msg:expr,) => (
+        panic!($msg)
+    );
+    ($fmt:expr, $($arg:tt)+) => ({
+        $crate::panicking::panic_fmt(format_args!($fmt, $($arg)*),
+                                     &(file!(), line!(), __rust_unstable_column!()))
+    });
+}
+
 /// Asserts that two expressions are equal to each other (using [`PartialEq`]).
 ///
 /// On panic, this macro will print the values of the expressions with their
diff --git a/src/libcore/panic.rs b/src/libcore/panic.rs
index 27ec4aaac75de..37ae05309af1d 100644
--- a/src/libcore/panic.rs
+++ b/src/libcore/panic.rs
@@ -35,6 +35,7 @@ use fmt;
 ///
 /// panic!("Normal panic");
 /// ```
+#[cfg_attr(not(stage0), lang = "panic_info")]
 #[stable(feature = "panic_hooks", since = "1.10.0")]
 #[derive(Debug)]
 pub struct PanicInfo<'a> {
@@ -53,7 +54,8 @@ impl<'a> PanicInfo<'a> {
     pub fn internal_constructor(message: Option<&'a fmt::Arguments<'a>>,
                                 location: Location<'a>)
                                 -> Self {
-        PanicInfo { payload: &(), location, message }
+        struct NoPayload;
+        PanicInfo { payload: &NoPayload, location, message }
     }
 
     #[doc(hidden)]
@@ -121,7 +123,7 @@ impl<'a> PanicInfo<'a> {
     #[stable(feature = "panic_hooks", since = "1.10.0")]
     pub fn location(&self) -> Option<&Location> {
         // NOTE: If this is changed to sometimes return None,
-        // deal with that case in std::panicking::default_hook.
+        // deal with that case in std::panicking::default_hook and std::panicking::begin_panic_fmt.
         Some(&self.location)
     }
 }
diff --git a/src/libcore/panicking.rs b/src/libcore/panicking.rs
index 6b3dc75af4633..1470a01e0e6dd 100644
--- a/src/libcore/panicking.rs
+++ b/src/libcore/panicking.rs
@@ -36,7 +36,33 @@
                       and related macros",
             issue = "0")]
 
+#[cfg(not(stage0))]
+use any::Any;
 use fmt;
+#[cfg(not(stage0))]
+use panic::{Location, PanicInfo};
+
+#[cfg(not(stage0))]
+#[allow(improper_ctypes)] // PanicInfo contains a trait object which is not FFI safe
+extern "C" {
+    #[lang = "panic_impl"]
+    fn panic_impl(pi: &PanicInfo) -> !;
+}
+
+#[cfg(not(stage0))]
+#[cold] #[inline(never)]
+pub fn panic_payload<M>(msg: M, file_line_col: &(&'static str, u32, u32)) -> !
+where
+    M: Any + Send,
+{
+    let (file, line, col) = *file_line_col;
+    let mut pi = PanicInfo::internal_constructor(
+        None,
+        Location::internal_constructor(file, line, col),
+    );
+    pi.set_payload(&msg);
+    unsafe { panic_impl(&pi) }
+}
 
 #[cold] #[inline(never)] // this is the slow path, always
 #[lang = "panic"]
@@ -59,6 +85,7 @@ fn panic_bounds_check(file_line_col: &(&'static str, u32, u32),
                            len, index), file_line_col)
 }
 
+#[cfg(stage0)]
 #[cold] #[inline(never)]
 pub fn panic_fmt(fmt: fmt::Arguments, file_line_col: &(&'static str, u32, u32)) -> ! {
     #[allow(improper_ctypes)]
@@ -70,3 +97,16 @@ pub fn panic_fmt(fmt: fmt::Arguments, file_line_col: &(&'static str, u32, u32))
     let (file, line, col) = *file_line_col;
     unsafe { panic_impl(fmt, file, line, col) }
 }
+
+#[cfg(not(stage0))]
+#[cold] #[inline(never)]
+pub fn panic_fmt(fmt: fmt::Arguments, file_line_col: &(&'static str, u32, u32)) -> ! {
+    struct NoPayload;
+
+    let (file, line, col) = *file_line_col;
+    let pi = PanicInfo::internal_constructor(
+        Some(&fmt),
+        Location::internal_constructor(file, line, col),
+    );
+    unsafe { panic_impl(&pi) }
+}
diff --git a/src/librustc/middle/dead.rs b/src/librustc/middle/dead.rs
index e3b2078971439..7ebc0d4a4de5a 100644
--- a/src/librustc/middle/dead.rs
+++ b/src/librustc/middle/dead.rs
@@ -284,7 +284,7 @@ impl<'a, 'tcx> Visitor<'tcx> for MarkSymbolVisitor<'a, 'tcx> {
 fn has_allow_dead_code_or_lang_attr(tcx: TyCtxt,
                                     id: ast::NodeId,
                                     attrs: &[ast::Attribute]) -> bool {
-    if attr::contains_name(attrs, "lang") {
+    if attr::contains_name(attrs, "lang") || attr::contains_name(attrs, "panic_implementation") {
         return true;
     }
 
diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs
index d70f994e87ba7..fe676919a7d14 100644
--- a/src/librustc/middle/lang_items.rs
+++ b/src/librustc/middle/lang_items.rs
@@ -185,6 +185,8 @@ pub fn extract(attrs: &[ast::Attribute]) -> Option<(Symbol, Span)> {
             if let Some(value) = attribute.value_str() {
                 return Some((value, attribute.span));
             }
+        } else if attribute.check_name("panic_implementation") {
+            return Some((Symbol::intern("panic_impl"), attribute.span))
         }
     }
 
@@ -299,7 +301,8 @@ language_item_table! {
     // lang item, but do not have it defined.
     PanicFnLangItem,                 "panic",                   panic_fn;
     PanicBoundsCheckFnLangItem,      "panic_bounds_check",      panic_bounds_check_fn;
-    PanicFmtLangItem,                "panic_fmt",               panic_fmt;
+    PanicInfoLangItem,               "panic_info",              panic_info;
+    PanicImplLangItem,               "panic_impl",              panic_impl;
 
     ExchangeMallocFnLangItem,        "exchange_malloc",         exchange_malloc_fn;
     BoxFreeFnLangItem,               "box_free",                box_free_fn;
diff --git a/src/librustc/middle/weak_lang_items.rs b/src/librustc/middle/weak_lang_items.rs
index 42e4d3861bae3..3c2ea047218a7 100644
--- a/src/librustc/middle/weak_lang_items.rs
+++ b/src/librustc/middle/weak_lang_items.rs
@@ -148,7 +148,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for Context<'a, 'tcx> {
 ) }
 
 weak_lang_items! {
-    panic_fmt,          PanicFmtLangItem,           rust_begin_unwind;
+    panic_impl,         PanicImplLangItem,          rust_begin_unwind;
     eh_personality,     EhPersonalityLangItem,      rust_eh_personality;
     eh_unwind_resume,   EhUnwindResumeLangItem,     rust_eh_unwind_resume;
     oom,                OomLangItem,                rust_oom;
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 90b974fb972c0..5c33eb5f0af12 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -96,7 +96,7 @@ use rustc::middle::region;
 use rustc::mir::interpret::{GlobalId};
 use rustc::ty::subst::{UnpackedKind, Subst, Substs};
 use rustc::traits::{self, ObligationCause, ObligationCauseCode, TraitEngine};
-use rustc::ty::{self, Ty, TyCtxt, GenericParamDefKind, Visibility, ToPredicate};
+use rustc::ty::{self, Ty, TyCtxt, GenericParamDefKind, Visibility, ToPredicate, RegionKind};
 use rustc::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability};
 use rustc::ty::fold::TypeFoldable;
 use rustc::ty::maps::Providers;
@@ -1129,6 +1129,48 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>,
         }
     }
 
+    // Check that a function marked as `#[panic_implementation]` has signature `fn(&PanicInfo) -> !`
+    if let Some(panic_impl_did) = fcx.tcx.lang_items().panic_impl() {
+        if panic_impl_did == fn_hir_id.owner_def_id() {
+            if let Some(panic_info_did) = fcx.tcx.lang_items().panic_info() {
+                if ret_ty.sty != ty::TyNever {
+                    fcx.tcx.sess.span_err(
+                        decl.output.span(),
+                        "return type should be `!`",
+                    );
+                }
+
+                let inputs = fn_sig.inputs();
+                let span = fcx.tcx.hir.span(fn_id);
+                if inputs.len() == 1 {
+                    let arg_is_panic_info = match inputs[0].sty {
+                        ty::TyRef(region, ty::TypeAndMut { ty, mutbl }) => match ty.sty {
+                            ty::TyAdt(ref adt, _) => {
+                                adt.did == panic_info_did &&
+                                    mutbl == hir::Mutability::MutImmutable &&
+                                    *region != RegionKind::ReStatic
+                            },
+                            _ => false,
+                        },
+                        _ => false,
+                    };
+
+                    if !arg_is_panic_info {
+                        fcx.tcx.sess.span_err(
+                            decl.inputs[0].span,
+                            "argument should be `&PanicInfo`",
+                        );
+                    }
+                } else {
+                    fcx.tcx.sess.span_err(span, "function should have one argument");
+                }
+            } else {
+                fcx.tcx.sess.err("language item required, but not found: `panic_info`");
+            }
+        }
+
+    }
+
     (fcx, gen_ty)
 }
 
diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs
index f7d06852f2793..c576245edb755 100644
--- a/src/libstd/lib.rs
+++ b/src/libstd/lib.rs
@@ -317,6 +317,8 @@
 #![cfg_attr(windows, feature(used))]
 #![feature(doc_alias)]
 #![feature(float_internals)]
+#![feature(panic_info_message)]
+#![cfg_attr(not(stage0), feature(panic_implementation))]
 
 #![default_lib_allocator]
 
diff --git a/src/libstd/panicking.rs b/src/libstd/panicking.rs
index 403056240bf57..6bb098310de22 100644
--- a/src/libstd/panicking.rs
+++ b/src/libstd/panicking.rs
@@ -186,7 +186,7 @@ fn default_hook(info: &PanicInfo) {
 
     let location = info.location().unwrap();  // The current implementation always returns Some
 
-    let msg = match info.payload().downcast_ref::<&'static str>() {
+    let msg = match info.payload().downcast_ref::<&str>() {
         Some(s) => *s,
         None => match info.payload().downcast_ref::<String>() {
             Some(s) => &s[..],
@@ -319,6 +319,7 @@ pub fn panicking() -> bool {
 
 /// Entry point of panic from the libcore crate.
 #[cfg(not(test))]
+#[cfg(stage0)]
 #[lang = "panic_fmt"]
 #[unwind(allowed)]
 pub extern fn rust_begin_panic(msg: fmt::Arguments,
@@ -328,12 +329,22 @@ pub extern fn rust_begin_panic(msg: fmt::Arguments,
     begin_panic_fmt(&msg, &(file, line, col))
 }
 
+/// Entry point of panic from the libcore crate.
+#[cfg(not(test))]
+#[cfg(not(stage0))]
+#[panic_implementation]
+#[unwind(allowed)]
+pub fn rust_begin_panic(info: &PanicInfo) -> ! {
+    continue_panic_fmt(&info)
+}
+
 /// The entry point for panicking with a formatted message.
 ///
 /// This is designed to reduce the amount of code required at the call
 /// site as much as possible (so that `panic!()` has as low an impact
 /// on (e.g.) the inlining of other functions as possible), by moving
 /// the actual formatting into this shared place.
+#[cfg(stage0)]
 #[unstable(feature = "libstd_sys_internals",
            reason = "used by the panic! macro",
            issue = "0")]
@@ -381,12 +392,92 @@ pub fn begin_panic_fmt(msg: &fmt::Arguments,
     }
 }
 
+/// The entry point for panicking with a formatted message.
+///
+/// This is designed to reduce the amount of code required at the call
+/// site as much as possible (so that `panic!()` has as low an impact
+/// on (e.g.) the inlining of other functions as possible), by moving
+/// the actual formatting into this shared place.
+#[cfg(not(stage0))]
+#[unstable(feature = "libstd_sys_internals",
+           reason = "used by the panic! macro",
+           issue = "0")]
+#[inline(never)] #[cold]
+pub fn begin_panic_fmt(msg: &fmt::Arguments,
+                       file_line_col: &(&'static str, u32, u32)) -> ! {
+    let (file, line, col) = *file_line_col;
+    let info = PanicInfo::internal_constructor(
+        Some(msg),
+        Location::internal_constructor(file, line, col),
+    );
+    continue_panic_fmt(&info)
+}
+
+#[cfg(not(stage0))]
+fn continue_panic_fmt(info: &PanicInfo) -> ! {
+    use fmt::Write;
+
+    // We do two allocations here, unfortunately. But (a) they're
+    // required with the current scheme, and (b) we don't handle
+    // panic + OOM properly anyway (see comment in begin_panic
+    // below).
+
+    let loc = info.location().unwrap(); // The current implementation always returns Some
+    let file_line_col = (loc.file(), loc.line(), loc.column());
+    rust_panic_with_hook(
+        &mut PanicPayload::new(info.payload(), info.message()),
+        info.message(),
+        &file_line_col);
+
+    struct PanicPayload<'a> {
+        payload: &'a (Any + Send),
+        msg: Option<&'a fmt::Arguments<'a>>,
+        string: Option<String>,
+    }
+
+    impl<'a> PanicPayload<'a> {
+        fn new(payload: &'a (Any + Send), msg: Option<&'a fmt::Arguments<'a>>) -> PanicPayload<'a> {
+            PanicPayload { payload, msg, string: None }
+        }
+
+
+        fn fill(&mut self) -> Option<&mut String> {
+            if let Some(msg) = self.msg.take() {
+                Some(self.string.get_or_insert_with(|| {
+                    let mut s = String::new();
+                    drop(s.write_fmt(*msg));
+                    s
+                }))
+            } else {
+                None
+            }
+        }
+    }
+
+    unsafe impl<'a> BoxMeUp for PanicPayload<'a> {
+        fn box_me_up(&mut self) -> *mut (Any + Send) {
+            if let Some(string) = self.fill() {
+                let contents = mem::replace(string, String::new());
+                Box::into_raw(Box::new(contents))
+            } else {
+                // We can't go from &(Any+Send) to Box<Any+Send> so the payload is lost here
+                struct NoPayload;
+                Box::into_raw(Box::new(NoPayload))
+            }
+        }
+
+        fn get(&mut self) -> &(Any + Send) {
+            self.payload
+        }
+    }
+}
+
 /// This is the entry point of panicking for panic!() and assert!().
 #[unstable(feature = "libstd_sys_internals",
            reason = "used by the panic! macro",
            issue = "0")]
 #[inline(never)] #[cold] // avoid code bloat at the call sites as much as possible
-pub fn begin_panic<M: Any + Send>(msg: M, file_line_col: &(&'static str, u32, u32)) -> ! {
+pub fn begin_panic<M: Any + Send>(msg: M, file_line_col: &(&str, u32, u32)) -> ! {
     // Note that this should be the only allocation performed in this code path.
     // Currently this means that panic!() on OOM will invoke this code path,
     // but then again we're not really ready for panic on OOM anyway. If
@@ -431,7 +522,7 @@ pub fn begin_panic<M: Any + Send>(msg: M, file_line_col: &(&'static str, u32, u3
 /// abort or unwind.
 fn rust_panic_with_hook(payload: &mut BoxMeUp,
                         message: Option<&fmt::Arguments>,
-                        file_line_col: &(&'static str, u32, u32)) -> ! {
+                        file_line_col: &(&str, u32, u32)) -> ! {
     let (file, line, col) = *file_line_col;
 
     let panics = update_panic_count(1);
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index 9b84713b0f90f..7349745fefea1 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -475,6 +475,9 @@ declare_features! (
 
     // 'a: { break 'a; }
     (active, label_break_value, "1.28.0", Some(48594), None),
+
+    // #[panic_implementation]
+    (active, panic_implementation, "1.28.0", Some(44489), None),
 );
 
 declare_features! (
@@ -1069,6 +1072,12 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG
                                  "attribute is currently unstable",
                                  cfg_fn!(wasm_custom_section))),
 
+    // RFC 2070
+    ("panic_implementation", Normal, Gated(Stability::Unstable,
+                           "panic_implementation",
+                           "#[panic_implementation] is an unstable feature",
+                           cfg_fn!(panic_implementation))),
+
     // Crate level attributes
     ("crate_name", CrateLevel, Ungated),
     ("crate_type", CrateLevel, Ungated),
diff --git a/src/test/compile-fail/duplicate_entry_error.rs b/src/test/compile-fail/duplicate_entry_error.rs
index 485519e8c3d7e..176aa7cca5357 100644
--- a/src/test/compile-fail/duplicate_entry_error.rs
+++ b/src/test/compile-fail/duplicate_entry_error.rs
@@ -14,9 +14,11 @@
 
 #![feature(lang_items)]
 
-#[lang = "panic_fmt"]
-fn panic_fmt() -> ! {
-//~^ ERROR: duplicate lang item found: `panic_fmt`.
+use std::panic::PanicInfo;
+
+#[lang = "panic_impl"]
+fn panic_impl(info: &PanicInfo) -> ! {
+//~^ ERROR: duplicate lang item found: `panic_impl`.
     loop {}
 }
 
diff --git a/src/test/compile-fail/feature-gate-panic-implementation.rs b/src/test/compile-fail/feature-gate-panic-implementation.rs
new file mode 100644
index 0000000000000..ae9fbc7b13bd5
--- /dev/null
+++ b/src/test/compile-fail/feature-gate-panic-implementation.rs
@@ -0,0 +1,21 @@
+// Copyright 2018 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.
+
+// compile-flags:-C panic=abort
+
+#![no_std]
+#![no_main]
+
+use core::panic::PanicInfo;
+
+#[panic_implementation] //~ ERROR #[panic_implementation] is an unstable feature (see issue #44489)
+fn panic(info: &PanicInfo) -> ! {
+    loop {}
+}
diff --git a/src/test/compile-fail/no_owned_box_lang_item.rs b/src/test/compile-fail/no_owned_box_lang_item.rs
index 72eb687adc602..1c2bf1573dcc1 100644
--- a/src/test/compile-fail/no_owned_box_lang_item.rs
+++ b/src/test/compile-fail/no_owned_box_lang_item.rs
@@ -21,4 +21,4 @@ fn main() {
 
 #[lang = "eh_personality"] extern fn eh_personality() {}
 #[lang = "eh_unwind_resume"] extern fn eh_unwind_resume() {}
-#[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} }
+#[lang = "panic_impl"] fn panic_impl() -> ! { loop {} }
diff --git a/src/test/compile-fail/panic-implementation-bad-signature-1.rs b/src/test/compile-fail/panic-implementation-bad-signature-1.rs
new file mode 100644
index 0000000000000..fec11fdbd7ba1
--- /dev/null
+++ b/src/test/compile-fail/panic-implementation-bad-signature-1.rs
@@ -0,0 +1,24 @@
+// Copyright 2018 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.
+
+// compile-flags:-C panic=abort
+
+#![feature(panic_implementation)]
+#![no_std]
+#![no_main]
+
+use core::panic::PanicInfo;
+
+#[panic_implementation]
+fn panic(
+    info: PanicInfo, //~ ERROR argument should be `&PanicInfo`
+) -> () //~ ERROR return type should be `!`
+{
+}
diff --git a/src/test/compile-fail/panic-implementation-bad-signature-2.rs b/src/test/compile-fail/panic-implementation-bad-signature-2.rs
new file mode 100644
index 0000000000000..2a628c05699cc
--- /dev/null
+++ b/src/test/compile-fail/panic-implementation-bad-signature-2.rs
@@ -0,0 +1,25 @@
+// Copyright 2018 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.
+
+// compile-flags:-C panic=abort
+
+#![feature(panic_implementation)]
+#![no_std]
+#![no_main]
+
+use core::panic::PanicInfo;
+
+#[panic_implementation]
+fn panic(
+    info: &'static PanicInfo, //~ ERROR argument should be `&PanicInfo`
+) -> !
+{
+    loop {}
+}
diff --git a/src/test/compile-fail/panic-implementation-bad-signature-3.rs b/src/test/compile-fail/panic-implementation-bad-signature-3.rs
new file mode 100644
index 0000000000000..29337025b7076
--- /dev/null
+++ b/src/test/compile-fail/panic-implementation-bad-signature-3.rs
@@ -0,0 +1,22 @@
+// Copyright 2018 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.
+
+// compile-flags:-C panic=abort
+
+#![feature(panic_implementation)]
+#![no_std]
+#![no_main]
+
+use core::panic::PanicInfo;
+
+#[panic_implementation]
+fn panic() -> ! { //~ ERROR function should have one argument
+    loop {}
+}
diff --git a/src/test/compile-fail/panic-implementation-duplicate.rs b/src/test/compile-fail/panic-implementation-duplicate.rs
new file mode 100644
index 0000000000000..017113af409bb
--- /dev/null
+++ b/src/test/compile-fail/panic-implementation-duplicate.rs
@@ -0,0 +1,28 @@
+// Copyright 2018 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.
+
+// compile-flags:-C panic=abort
+
+#![feature(lang_items)]
+#![feature(panic_implementation)]
+#![no_std]
+#![no_main]
+
+use core::panic::PanicInfo;
+
+#[panic_implementation]
+fn panic(info: &PanicInfo) -> ! {
+    loop {}
+}
+
+#[lang = "panic_impl"]
+fn panic2(info: &PanicInfo) -> ! { //~ ERROR duplicate lang item found: `panic_impl`.
+    loop {}
+}
diff --git a/src/test/compile-fail/panic-implementation-requires-panic-info.rs b/src/test/compile-fail/panic-implementation-requires-panic-info.rs
new file mode 100644
index 0000000000000..597f44d98326d
--- /dev/null
+++ b/src/test/compile-fail/panic-implementation-requires-panic-info.rs
@@ -0,0 +1,26 @@
+// Copyright 2018 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.
+
+// compile-flags:-C panic=abort
+// error-pattern: language item required, but not found: `panic_info`
+
+#![feature(lang_items)]
+#![feature(no_core)]
+#![feature(panic_implementation)]
+#![no_core]
+#![no_main]
+
+#[panic_implementation]
+fn panic() -> ! {
+    loop {}
+}
+
+#[lang = "sized"]
+trait Sized {}
diff --git a/src/test/compile-fail/panic-runtime/auxiliary/panic-runtime-lang-items.rs b/src/test/compile-fail/panic-runtime/auxiliary/panic-runtime-lang-items.rs
index fbf70b3d3fefe..d9848a554aba5 100644
--- a/src/test/compile-fail/panic-runtime/auxiliary/panic-runtime-lang-items.rs
+++ b/src/test/compile-fail/panic-runtime/auxiliary/panic-runtime-lang-items.rs
@@ -15,8 +15,10 @@
 #![no_std]
 #![feature(lang_items)]
 
-#[lang = "panic_fmt"]
-fn panic_fmt() {}
+use core::panic::PanicInfo;
+
+#[lang = "panic_impl"]
+fn panic_impl(info: &PanicInfo) -> ! { loop {} }
 #[lang = "eh_personality"]
 fn eh_personality() {}
 #[lang = "eh_unwind_resume"]
diff --git a/src/test/compile-fail/weak-lang-item.rs b/src/test/compile-fail/weak-lang-item.rs
index 8579611b9380a..7b988c3595f5e 100644
--- a/src/test/compile-fail/weak-lang-item.rs
+++ b/src/test/compile-fail/weak-lang-item.rs
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 // aux-build:weak-lang-items.rs
-// error-pattern: language item required, but not found: `panic_fmt`
+// error-pattern: language item required, but not found: `panic_impl`
 // error-pattern: language item required, but not found: `eh_personality`
 // ignore-wasm32-bare compiled with panic=abort, personality not required
 
diff --git a/src/test/ui/error-codes/E0152.rs b/src/test/ui/error-codes/E0152.rs
index ae501b94e3f05..8fbad7b3ff301 100644
--- a/src/test/ui/error-codes/E0152.rs
+++ b/src/test/ui/error-codes/E0152.rs
@@ -10,7 +10,7 @@
 
 #![feature(lang_items)]
 
-#[lang = "panic_fmt"]
+#[lang = "panic_impl"]
 struct Foo; //~ ERROR E0152
 
 fn main() {
diff --git a/src/test/ui/error-codes/E0152.stderr b/src/test/ui/error-codes/E0152.stderr
index f67022bd6d3f9..c7f5f362efb28 100644
--- a/src/test/ui/error-codes/E0152.stderr
+++ b/src/test/ui/error-codes/E0152.stderr
@@ -1,4 +1,4 @@
-error[E0152]: duplicate lang item found: `panic_fmt`.
+error[E0152]: duplicate lang item found: `panic_impl`.
   --> $DIR/E0152.rs:14:1
    |
 LL | struct Foo; //~ ERROR E0152

From 63f18e108af98be931465fa0d2e7e998c5542aab Mon Sep 17 00:00:00 2001
From: Jorge Aparicio <jorge@japaric.io>
Date: Mon, 30 Apr 2018 10:57:11 +0200
Subject: [PATCH 02/12] s/panic_fmt/panic_impl/g in docs

---
 .../src/language-features/lang-items.md       | 25 ++++++++-----------
 .../src/language-features/used.md             | 10 +++++---
 src/libcore/lib.rs                            |  2 +-
 src/librustc/diagnostics.rs                   |  6 ++---
 4 files changed, 22 insertions(+), 21 deletions(-)

diff --git a/src/doc/unstable-book/src/language-features/lang-items.md b/src/doc/unstable-book/src/language-features/lang-items.md
index 6a7aea7f1c27e..bac619fd4a30d 100644
--- a/src/doc/unstable-book/src/language-features/lang-items.md
+++ b/src/doc/unstable-book/src/language-features/lang-items.md
@@ -19,6 +19,7 @@ sugar for dynamic allocations via `malloc` and `free`:
 #![feature(lang_items, box_syntax, start, libc, core_intrinsics)]
 #![no_std]
 use core::intrinsics;
+use core::panic::PanicInfo;
 
 extern crate libc;
 
@@ -50,7 +51,7 @@ fn main(_argc: isize, _argv: *const *const u8) -> isize {
 }
 
 #[lang = "eh_personality"] extern fn rust_eh_personality() {}
-#[lang = "panic_fmt"] extern fn rust_begin_panic() -> ! { unsafe { intrinsics::abort() } }
+#[lang = "panic_impl"] extern fn rust_begin_panic(info: &PanicInfo) -> ! { unsafe { intrinsics::abort() } }
 #[lang = "eh_unwind_resume"] extern fn rust_eh_unwind_resume() {}
 #[no_mangle] pub extern fn rust_eh_register_frames () {}
 #[no_mangle] pub extern fn rust_eh_unregister_frames () {}
@@ -110,6 +111,7 @@ in the same format as C:
 #![feature(start)]
 #![no_std]
 use core::intrinsics;
+use core::panic::PanicInfo;
 
 // Pull in the system libc library for what crt0.o likely requires.
 extern crate libc;
@@ -134,12 +136,9 @@ pub extern fn rust_eh_personality() {
 pub extern fn rust_eh_unwind_resume() {
 }
 
-#[lang = "panic_fmt"]
+#[lang = "panic_impl"]
 #[no_mangle]
-pub extern fn rust_begin_panic(_msg: core::fmt::Arguments,
-                               _file: &'static str,
-                               _line: u32,
-                               _column: u32) -> ! {
+pub extern fn rust_begin_panic(info: &PanicInfo) -> ! {
     unsafe { intrinsics::abort() }
 }
 ```
@@ -155,6 +154,7 @@ compiler's name mangling too:
 #![no_std]
 #![no_main]
 use core::intrinsics;
+use core::panic::PanicInfo;
 
 // Pull in the system libc library for what crt0.o likely requires.
 extern crate libc;
@@ -179,12 +179,9 @@ pub extern fn rust_eh_personality() {
 pub extern fn rust_eh_unwind_resume() {
 }
 
-#[lang = "panic_fmt"]
+#[lang = "panic_impl"]
 #[no_mangle]
-pub extern fn rust_begin_panic(_msg: core::fmt::Arguments,
-                               _file: &'static str,
-                               _line: u32,
-                               _column: u32) -> ! {
+pub extern fn rust_begin_panic(info: &PanicInfo) -> ! {
     unsafe { intrinsics::abort() }
 }
 ```
@@ -215,7 +212,7 @@ called. The language item's name is `eh_personality`.
 
 The second function, `rust_begin_panic`, is also used by the failure mechanisms of the
 compiler. When a panic happens, this controls the message that's displayed on
-the screen. While the language item's name is `panic_fmt`, the symbol name is
+the screen. While the language item's name is `panic_impl`, the symbol name is
 `rust_begin_panic`.
 
 A third function, `rust_eh_unwind_resume`, is also needed if the `custom_unwind_resume`
@@ -259,8 +256,8 @@ the source code.
   - `msvc_try_filter`: `libpanic_unwind/seh.rs` (SEH)
   - `panic`: `libcore/panicking.rs`
   - `panic_bounds_check`: `libcore/panicking.rs`
-  - `panic_fmt`: `libcore/panicking.rs`
-  - `panic_fmt`: `libstd/panicking.rs`
+  - `panic_impl`: `libcore/panicking.rs`
+  - `panic_impl`: `libstd/panicking.rs`
 - Allocations
   - `owned_box`: `liballoc/boxed.rs`
   - `exchange_malloc`: `liballoc/heap.rs`
diff --git a/src/doc/unstable-book/src/language-features/used.md b/src/doc/unstable-book/src/language-features/used.md
index 75a8b2774f422..c3b7f2e41e15b 100644
--- a/src/doc/unstable-book/src/language-features/used.md
+++ b/src/doc/unstable-book/src/language-features/used.md
@@ -87,11 +87,13 @@ This condition can be met using `#[used]` and `#[link_section]` plus a linker
 script.
 
 ``` rust,ignore
-#![feature(lang_items)]
+#![feature(panic_implementation)]
 #![feature(used)]
 #![no_main]
 #![no_std]
 
+use core::panic::PanicInfo;
+
 extern "C" fn reset_handler() -> ! {
     loop {}
 }
@@ -100,8 +102,10 @@ extern "C" fn reset_handler() -> ! {
 #[used]
 static RESET_HANDLER: extern "C" fn() -> ! = reset_handler;
 
-#[lang = "panic_fmt"]
-fn panic_fmt() {}
+#[panic_implementation]
+fn panic_impl(info: &PanicInfo) -> ! {
+    loop {}
+}
 ```
 
 ``` text
diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs
index 32cf31231c301..e6ab64a331272 100644
--- a/src/libcore/lib.rs
+++ b/src/libcore/lib.rs
@@ -41,7 +41,7 @@
 //!   dictate the panic message, the file at which panic was invoked, and the
 //!   line and column inside the file. It is up to consumers of this core
 //!   library to define this panic function; it is only required to never
-//!   return. This requires a `lang` attribute named `panic_fmt`.
+//!   return. This requires a `lang` attribute named `panic_impl`.
 //!
 //! * `rust_eh_personality` - is used by the failure mechanisms of the
 //!    compiler. This is often mapped to GCC's personality function, but crates
diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs
index 2662e70999196..61f05ca347358 100644
--- a/src/librustc/diagnostics.rs
+++ b/src/librustc/diagnostics.rs
@@ -637,8 +637,8 @@ Erroneous code example:
 ```compile_fail,E0152
 #![feature(lang_items)]
 
-#[lang = "panic_fmt"]
-struct Foo; // error: duplicate lang item found: `panic_fmt`
+#[lang = "panic_impl"]
+struct Foo; // error: duplicate lang item found: `panic_impl`
 ```
 
 Lang items are already implemented in the standard library. Unless you are
@@ -824,7 +824,7 @@ A list of available external lang items is available in
 #![feature(lang_items)]
 
 extern "C" {
-    #[lang = "panic_fmt"] // ok!
+    #[lang = "panic_impl"] // ok!
     fn cake();
 }
 ```

From eaef110890837283a1504eb200cab1eba03650ea Mon Sep 17 00:00:00 2001
From: Jorge Aparicio <jorge@japaric.io>
Date: Tue, 1 May 2018 03:02:39 +0200
Subject: [PATCH 03/12] format payload if possible instead of returning
 "Box<Any>"

---
 src/libstd/panicking.rs | 15 +++++++++++----
 1 file changed, 11 insertions(+), 4 deletions(-)

diff --git a/src/libstd/panicking.rs b/src/libstd/panicking.rs
index 6bb098310de22..1f80fb6f738bd 100644
--- a/src/libstd/panicking.rs
+++ b/src/libstd/panicking.rs
@@ -440,12 +440,11 @@ fn continue_panic_fmt(info: &PanicInfo) -> ! {
             PanicPayload { payload, msg, string: None }
         }
 
-
         fn fill(&mut self) -> Option<&mut String> {
-            if let Some(msg) = self.msg.take() {
+            if let Some(msg) = self.msg.cloned() {
                 Some(self.string.get_or_insert_with(|| {
                     let mut s = String::new();
-                    drop(s.write_fmt(*msg));
+                    drop(s.write_fmt(msg));
                     s
                 }))
             } else {
@@ -459,6 +458,10 @@ fn continue_panic_fmt(info: &PanicInfo) -> ! {
             if let Some(string) = self.fill() {
                 let contents = mem::replace(string, String::new());
                 Box::into_raw(Box::new(contents))
+            } else if let Some(s) = self.payload.downcast_ref::<&str>() {
+                Box::into_raw(Box::new(s.to_owned()))
+            } else if let Some(s) = self.payload.downcast_ref::<String>() {
+                Box::into_raw(Box::new(s.clone()))
             } else {
                 // We can't go from &(Any+Send) to Box<Any+Send> so the payload is lost here
                 struct NoPayload;
@@ -467,7 +470,11 @@ fn continue_panic_fmt(info: &PanicInfo) -> ! {
         }
 
         fn get(&mut self) -> &(Any + Send) {
-            self.payload
+            if let Some(s) = self.fill() {
+                s
+            } else {
+                self.payload
+            }
         }
     }
 }

From b442348d53ca37b6d22cd6e40c305d89f7782538 Mon Sep 17 00:00:00 2001
From: Jorge Aparicio <jorge@japaric.io>
Date: Tue, 1 May 2018 03:03:20 +0200
Subject: [PATCH 04/12] remove unused `struct NoPayload`

---
 src/libcore/panicking.rs | 2 --
 1 file changed, 2 deletions(-)

diff --git a/src/libcore/panicking.rs b/src/libcore/panicking.rs
index 1470a01e0e6dd..71a1eb2f7a4e6 100644
--- a/src/libcore/panicking.rs
+++ b/src/libcore/panicking.rs
@@ -101,8 +101,6 @@ pub fn panic_fmt(fmt: fmt::Arguments, file_line_col: &(&'static str, u32, u32))
 #[cfg(not(stage0))]
 #[cold] #[inline(never)]
 pub fn panic_fmt(fmt: fmt::Arguments, file_line_col: &(&'static str, u32, u32)) -> ! {
-    struct NoPayload;
-
     let (file, line, col) = *file_line_col;
     let pi = PanicInfo::internal_constructor(
         Some(&fmt),

From eb1936141611afa8ad9034c4093e1539df36548c Mon Sep 17 00:00:00 2001
From: Jorge Aparicio <jorge@japaric.io>
Date: Tue, 1 May 2018 03:04:42 +0200
Subject: [PATCH 05/12] document that `panic_impl` never passes the FFI
 boundary

---
 src/libcore/panicking.rs | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/libcore/panicking.rs b/src/libcore/panicking.rs
index 71a1eb2f7a4e6..4b4fa73c47810 100644
--- a/src/libcore/panicking.rs
+++ b/src/libcore/panicking.rs
@@ -42,9 +42,10 @@ use fmt;
 #[cfg(not(stage0))]
 use panic::{Location, PanicInfo};
 
+// NOTE This function never crosses the FFI boundary; it's a Rust-to-Rust call
 #[cfg(not(stage0))]
 #[allow(improper_ctypes)] // PanicInfo contains a trait object which is not FFI safe
-extern "C" {
+extern "Rust" {
     #[lang = "panic_impl"]
     fn panic_impl(pi: &PanicInfo) -> !;
 }

From 430ad769008c0aaa40949a1d98a6f0e18e35ec65 Mon Sep 17 00:00:00 2001
From: Jorge Aparicio <jorge@japaric.io>
Date: Wed, 16 May 2018 14:07:58 +0200
Subject: [PATCH 06/12] undo payload in core::panic! changes

---
 src/libcore/macros.rs    |  22 --------
 src/libcore/panicking.rs |  32 +++---------
 src/libstd/panicking.rs  | 109 +++++++++++----------------------------
 3 files changed, 37 insertions(+), 126 deletions(-)

diff --git a/src/libcore/macros.rs b/src/libcore/macros.rs
index f98626d939d10..c830c22ee5f50 100644
--- a/src/libcore/macros.rs
+++ b/src/libcore/macros.rs
@@ -9,7 +9,6 @@
 // except according to those terms.
 
 /// Entry point of thread panic, for details, see std::macros
-#[cfg(stage0)]
 #[macro_export]
 #[allow_internal_unstable]
 #[stable(feature = "core", since = "1.6.0")]
@@ -29,27 +28,6 @@ macro_rules! panic {
     });
 }
 
-/// Entry point of thread panic, for details, see std::macros
-#[cfg(not(stage0))]
-#[macro_export]
-#[allow_internal_unstable]
-#[stable(feature = "core", since = "1.6.0")]
-macro_rules! panic {
-    () => (
-        panic!("explicit panic")
-    );
-    ($msg:expr) => ({
-        $crate::panicking::panic_payload($msg, &(file!(), line!(), __rust_unstable_column!()))
-    });
-    ($msg:expr,) => (
-        panic!($msg)
-    );
-    ($fmt:expr, $($arg:tt)+) => ({
-        $crate::panicking::panic_fmt(format_args!($fmt, $($arg)*),
-                                     &(file!(), line!(), __rust_unstable_column!()))
-    });
-}
-
 /// Asserts that two expressions are equal to each other (using [`PartialEq`]).
 ///
 /// On panic, this macro will print the values of the expressions with their
diff --git a/src/libcore/panicking.rs b/src/libcore/panicking.rs
index 4b4fa73c47810..0d4f8d1141e73 100644
--- a/src/libcore/panicking.rs
+++ b/src/libcore/panicking.rs
@@ -36,35 +36,10 @@
                       and related macros",
             issue = "0")]
 
-#[cfg(not(stage0))]
-use any::Any;
 use fmt;
 #[cfg(not(stage0))]
 use panic::{Location, PanicInfo};
 
-// NOTE This function never crosses the FFI boundary; it's a Rust-to-Rust call
-#[cfg(not(stage0))]
-#[allow(improper_ctypes)] // PanicInfo contains a trait object which is not FFI safe
-extern "Rust" {
-    #[lang = "panic_impl"]
-    fn panic_impl(pi: &PanicInfo) -> !;
-}
-
-#[cfg(not(stage0))]
-#[cold] #[inline(never)]
-pub fn panic_payload<M>(msg: M, file_line_col: &(&'static str, u32, u32)) -> !
-where
-    M: Any + Send,
-{
-    let (file, line, col) = *file_line_col;
-    let mut pi = PanicInfo::internal_constructor(
-        None,
-        Location::internal_constructor(file, line, col),
-    );
-    pi.set_payload(&msg);
-    unsafe { panic_impl(&pi) }
-}
-
 #[cold] #[inline(never)] // this is the slow path, always
 #[lang = "panic"]
 pub fn panic(expr_file_line_col: &(&'static str, &'static str, u32, u32)) -> ! {
@@ -102,6 +77,13 @@ pub fn panic_fmt(fmt: fmt::Arguments, file_line_col: &(&'static str, u32, u32))
 #[cfg(not(stage0))]
 #[cold] #[inline(never)]
 pub fn panic_fmt(fmt: fmt::Arguments, file_line_col: &(&'static str, u32, u32)) -> ! {
+    // NOTE This function never crosses the FFI boundary; it's a Rust-to-Rust call
+    #[allow(improper_ctypes)] // PanicInfo contains a trait object which is not FFI safe
+    extern "Rust" {
+        #[lang = "panic_impl"]
+        fn panic_impl(pi: &PanicInfo) -> !;
+    }
+
     let (file, line, col) = *file_line_col;
     let pi = PanicInfo::internal_constructor(
         Some(&fmt),
diff --git a/src/libstd/panicking.rs b/src/libstd/panicking.rs
index 1f80fb6f738bd..9d8052143b98e 100644
--- a/src/libstd/panicking.rs
+++ b/src/libstd/panicking.rs
@@ -186,7 +186,7 @@ fn default_hook(info: &PanicInfo) {
 
     let location = info.location().unwrap();  // The current implementation always returns Some
 
-    let msg = match info.payload().downcast_ref::<&str>() {
+    let msg = match info.payload().downcast_ref::<&'static str>() {
         Some(s) => *s,
         None => match info.payload().downcast_ref::<String>() {
             Some(s) => &s[..],
@@ -351,44 +351,45 @@ pub fn rust_begin_panic(info: &PanicInfo) -> ! {
 #[inline(never)] #[cold]
 pub fn begin_panic_fmt(msg: &fmt::Arguments,
                        file_line_col: &(&'static str, u32, u32)) -> ! {
-    use fmt::Write;
-
     // We do two allocations here, unfortunately. But (a) they're
     // required with the current scheme, and (b) we don't handle
     // panic + OOM properly anyway (see comment in begin_panic
     // below).
 
     rust_panic_with_hook(&mut PanicPayload::new(msg), Some(msg), file_line_col);
+}
+
+// NOTE(stage0) move into `continue_panic_fmt` on next stage0 update
+struct PanicPayload<'a> {
+    inner: &'a fmt::Arguments<'a>,
+    string: Option<String>,
+}
 
-    struct PanicPayload<'a> {
-        inner: &'a fmt::Arguments<'a>,
-        string: Option<String>,
+impl<'a> PanicPayload<'a> {
+    fn new(inner: &'a fmt::Arguments<'a>) -> PanicPayload<'a> {
+        PanicPayload { inner, string: None }
     }
 
-    impl<'a> PanicPayload<'a> {
-        fn new(inner: &'a fmt::Arguments<'a>) -> PanicPayload<'a> {
-            PanicPayload { inner, string: None }
-        }
+    fn fill(&mut self) -> &mut String {
+        use fmt::Write;
 
-        fn fill(&mut self) -> &mut String {
-            let inner = self.inner;
-            self.string.get_or_insert_with(|| {
-                let mut s = String::new();
-                drop(s.write_fmt(*inner));
-                s
-            })
-        }
+        let inner = self.inner;
+        self.string.get_or_insert_with(|| {
+            let mut s = String::new();
+            drop(s.write_fmt(*inner));
+            s
+        })
     }
+}
 
-    unsafe impl<'a> BoxMeUp for PanicPayload<'a> {
-        fn box_me_up(&mut self) -> *mut (Any + Send) {
-            let contents = mem::replace(self.fill(), String::new());
-            Box::into_raw(Box::new(contents))
-        }
+unsafe impl<'a> BoxMeUp for PanicPayload<'a> {
+    fn box_me_up(&mut self) -> *mut (Any + Send) {
+        let contents = mem::replace(self.fill(), String::new());
+        Box::into_raw(Box::new(contents))
+    }
 
-        fn get(&mut self) -> &(Any + Send) {
-            self.fill()
-        }
+    fn get(&mut self) -> &(Any + Send) {
+        self.fill()
     }
 }
 
@@ -415,68 +416,18 @@ pub fn begin_panic_fmt(msg: &fmt::Arguments,
 
 #[cfg(not(stage0))]
 fn continue_panic_fmt(info: &PanicInfo) -> ! {
-    use fmt::Write;
-
     // We do two allocations here, unfortunately. But (a) they're
     // required with the current scheme, and (b) we don't handle
     // panic + OOM properly anyway (see comment in begin_panic
     // below).
 
     let loc = info.location().unwrap(); // The current implementation always returns Some
+    let msg = info.message().unwrap(); // The current implementation always returns Some
     let file_line_col = (loc.file(), loc.line(), loc.column());
     rust_panic_with_hook(
-        &mut PanicPayload::new(info.payload(), info.message()),
+        &mut PanicPayload::new(msg),
         info.message(),
         &file_line_col);
-
-    struct PanicPayload<'a> {
-        payload: &'a (Any + Send),
-        msg: Option<&'a fmt::Arguments<'a>>,
-        string: Option<String>,
-    }
-
-    impl<'a> PanicPayload<'a> {
-        fn new(payload: &'a (Any + Send), msg: Option<&'a fmt::Arguments<'a>>) -> PanicPayload<'a> {
-            PanicPayload { payload, msg, string: None }
-        }
-
-        fn fill(&mut self) -> Option<&mut String> {
-            if let Some(msg) = self.msg.cloned() {
-                Some(self.string.get_or_insert_with(|| {
-                    let mut s = String::new();
-                    drop(s.write_fmt(msg));
-                    s
-                }))
-            } else {
-                None
-            }
-        }
-    }
-
-    unsafe impl<'a> BoxMeUp for PanicPayload<'a> {
-        fn box_me_up(&mut self) -> *mut (Any + Send) {
-            if let Some(string) = self.fill() {
-                let contents = mem::replace(string, String::new());
-                Box::into_raw(Box::new(contents))
-            } else if let Some(s) = self.payload.downcast_ref::<&str>() {
-                Box::into_raw(Box::new(s.to_owned()))
-            } else if let Some(s) = self.payload.downcast_ref::<String>() {
-                Box::into_raw(Box::new(s.clone()))
-            } else {
-                // We can't go from &(Any+Send) to Box<Any+Send> so the payload is lost here
-                struct NoPayload;
-                Box::into_raw(Box::new(NoPayload))
-            }
-        }
-
-        fn get(&mut self) -> &(Any + Send) {
-            if let Some(s) = self.fill() {
-                s
-            } else {
-                self.payload
-            }
-        }
-    }
 }
 
 /// This is the entry point of panicking for panic!() and assert!().
@@ -484,7 +435,7 @@ fn continue_panic_fmt(info: &PanicInfo) -> ! {
            reason = "used by the panic! macro",
            issue = "0")]
 #[inline(never)] #[cold] // avoid code bloat at the call sites as much as possible
-pub fn begin_panic<M: Any + Send>(msg: M, file_line_col: &(&str, u32, u32)) -> ! {
+pub fn begin_panic<M: Any + Send>(msg: M, file_line_col: &(&'static str, u32, u32)) -> ! {
     // Note that this should be the only allocation performed in this code path.
     // Currently this means that panic!() on OOM will invoke this code path,
     // but then again we're not really ready for panic on OOM anyway. If

From 86a07696219fecfe4444aeb727d02ad8a4ebcda0 Mon Sep 17 00:00:00 2001
From: Jorge Aparicio <jorge@japaric.io>
Date: Wed, 16 May 2018 19:55:57 +0200
Subject: [PATCH 07/12] fix after rebase

---
 src/librustc_typeck/check/mod.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 5c33eb5f0af12..23ebf418195f2 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -1144,7 +1144,7 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>,
                 let span = fcx.tcx.hir.span(fn_id);
                 if inputs.len() == 1 {
                     let arg_is_panic_info = match inputs[0].sty {
-                        ty::TyRef(region, ty::TypeAndMut { ty, mutbl }) => match ty.sty {
+                        ty::TyRef(region, ty, mutbl) => match ty.sty {
                             ty::TyAdt(ref adt, _) => {
                                 adt.did == panic_info_did &&
                                     mutbl == hir::Mutability::MutImmutable &&

From 4c84d382ed87521a5b98b30f508366111dbbc7d5 Mon Sep 17 00:00:00 2001
From: Jorge Aparicio <jorge@japaric.io>
Date: Wed, 16 May 2018 19:56:24 +0200
Subject: [PATCH 08/12] remove #[unwind(allowed)]

not required because this is a Rust function
---
 src/libstd/panicking.rs | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/libstd/panicking.rs b/src/libstd/panicking.rs
index 9d8052143b98e..0808efa2ecea5 100644
--- a/src/libstd/panicking.rs
+++ b/src/libstd/panicking.rs
@@ -321,7 +321,6 @@ pub fn panicking() -> bool {
 #[cfg(not(test))]
 #[cfg(stage0)]
 #[lang = "panic_fmt"]
-#[unwind(allowed)]
 pub extern fn rust_begin_panic(msg: fmt::Arguments,
                                file: &'static str,
                                line: u32,

From da2ee5dcb29b3675cb342c9bc240a22eea9cc2b7 Mon Sep 17 00:00:00 2001
From: Jorge Aparicio <jorge@japaric.io>
Date: Tue, 29 May 2018 11:23:00 +0200
Subject: [PATCH 09/12] reject `fn panic_impl<T>(_: &PanicInfo) -> !`

---
 src/librustc_typeck/check/mod.rs              | 15 ++++++++++--
 .../panic-implementation-bad-signature-4.rs   | 23 +++++++++++++++++++
 2 files changed, 36 insertions(+), 2 deletions(-)
 create mode 100644 src/test/compile-fail/panic-implementation-bad-signature-4.rs

diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 23ebf418195f2..d54f29e6b9980 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -130,7 +130,7 @@ use syntax_pos::{self, BytePos, Span, MultiSpan};
 use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap};
 use rustc::hir::itemlikevisit::ItemLikeVisitor;
 use rustc::hir::map::Node;
-use rustc::hir::{self, PatKind};
+use rustc::hir::{self, PatKind, Item_};
 use rustc::middle::lang_items;
 
 mod autoderef;
@@ -1133,7 +1133,7 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>,
     if let Some(panic_impl_did) = fcx.tcx.lang_items().panic_impl() {
         if panic_impl_did == fn_hir_id.owner_def_id() {
             if let Some(panic_info_did) = fcx.tcx.lang_items().panic_info() {
-                if ret_ty.sty != ty::TyNever {
+                if declared_ret_ty.sty != ty::TyNever {
                     fcx.tcx.sess.span_err(
                         decl.output.span(),
                         "return type should be `!`",
@@ -1161,6 +1161,17 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>,
                             "argument should be `&PanicInfo`",
                         );
                     }
+
+                    if let Node::NodeItem(item) = fcx.tcx.hir.get(fn_id) {
+                        if let Item_::ItemFn(_, _, _, _, ref generics, _) = item.node {
+                            if !generics.params.is_empty() {
+                                fcx.tcx.sess.span_err(
+                                    span,
+                                    "`#[panic_implementation]` function should have no type parameters",
+                                );
+                            }
+                        }
+                    }
                 } else {
                     fcx.tcx.sess.span_err(span, "function should have one argument");
                 }
diff --git a/src/test/compile-fail/panic-implementation-bad-signature-4.rs b/src/test/compile-fail/panic-implementation-bad-signature-4.rs
new file mode 100644
index 0000000000000..d5f942ba2d62f
--- /dev/null
+++ b/src/test/compile-fail/panic-implementation-bad-signature-4.rs
@@ -0,0 +1,23 @@
+// Copyright 2018 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.
+
+// compile-flags:-C panic=abort
+
+#![feature(panic_implementation)]
+#![no_std]
+#![no_main]
+
+use core::panic::PanicInfo;
+
+#[panic_implementation]
+fn panic<T>(pi: &PanicInfo) -> ! {
+    //~^ ERROR `#[panic_implementation]` function should have no type parameters
+    loop {}
+}

From a174f2ab7c061ece4529ffa9b897236761298717 Mon Sep 17 00:00:00 2001
From: Jorge Aparicio <jorge@japaric.io>
Date: Tue, 29 May 2018 12:33:11 +0200
Subject: [PATCH 10/12] add more tests

---
 .../compile-fail/auxiliary/some-panic-impl.rs | 22 ++++++++++++++
 .../compile-fail/panic-implementation-std.rs  | 22 ++++++++++++++
 .../panic-implementation-twice.rs             | 29 +++++++++++++++++++
 .../run-make/panic-impl-transitive/Makefile   |  7 +++++
 .../panic-impl-consumer.rs                    | 15 ++++++++++
 .../panic-impl-provider.rs                    | 20 +++++++++++++
 6 files changed, 115 insertions(+)
 create mode 100644 src/test/compile-fail/auxiliary/some-panic-impl.rs
 create mode 100644 src/test/compile-fail/panic-implementation-std.rs
 create mode 100644 src/test/compile-fail/panic-implementation-twice.rs
 create mode 100644 src/test/run-make/panic-impl-transitive/Makefile
 create mode 100644 src/test/run-make/panic-impl-transitive/panic-impl-consumer.rs
 create mode 100644 src/test/run-make/panic-impl-transitive/panic-impl-provider.rs

diff --git a/src/test/compile-fail/auxiliary/some-panic-impl.rs b/src/test/compile-fail/auxiliary/some-panic-impl.rs
new file mode 100644
index 0000000000000..db16ac325ac59
--- /dev/null
+++ b/src/test/compile-fail/auxiliary/some-panic-impl.rs
@@ -0,0 +1,22 @@
+// Copyright 2018 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.
+
+// no-prefer-dynamic
+
+#![crate_type = "rlib"]
+#![feature(panic_implementation)]
+#![no_std]
+
+use core::panic::PanicInfo;
+
+#[panic_implementation]
+fn panic(info: &PanicInfo) -> ! {
+    loop {}
+}
diff --git a/src/test/compile-fail/panic-implementation-std.rs b/src/test/compile-fail/panic-implementation-std.rs
new file mode 100644
index 0000000000000..f25cd3605c1dd
--- /dev/null
+++ b/src/test/compile-fail/panic-implementation-std.rs
@@ -0,0 +1,22 @@
+// Copyright 2018 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: duplicate lang item found: `panic_impl`.
+
+#![feature(panic_implementation)]
+
+use std::panic::PanicInfo;
+
+#[panic_implementation]
+fn panic(info: PanicInfo) -> ! {
+    loop {}
+}
+
+fn main() {}
diff --git a/src/test/compile-fail/panic-implementation-twice.rs b/src/test/compile-fail/panic-implementation-twice.rs
new file mode 100644
index 0000000000000..78dc545c036f8
--- /dev/null
+++ b/src/test/compile-fail/panic-implementation-twice.rs
@@ -0,0 +1,29 @@
+// Copyright 2018 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.
+
+// aux-build:some-panic-impl.rs
+
+#![feature(panic_implementation)]
+#![feature(lang_items)]
+#![no_std]
+#![no_main]
+
+extern crate some_panic_impl;
+
+use core::panic::PanicInfo;
+
+#[panic_implementation]
+fn panic(info: &PanicInfo) -> ! {
+    //~^ error duplicate lang item found: `panic_impl`
+    loop {}
+}
+
+#[lang = "eh_personality"]
+fn eh() {}
diff --git a/src/test/run-make/panic-impl-transitive/Makefile b/src/test/run-make/panic-impl-transitive/Makefile
new file mode 100644
index 0000000000000..1714578b2ba28
--- /dev/null
+++ b/src/test/run-make/panic-impl-transitive/Makefile
@@ -0,0 +1,7 @@
+-include ../../run-make-fulldeps/tools.mk
+
+# NOTE we use --emit=llvm-ir to avoid running the linker (linking will fail because there's no main
+# in this crate)
+all:
+	$(RUSTC) panic-impl-provider.rs
+	$(RUSTC) panic-impl-consumer.rs -C panic=abort --emit=llvm-ir -L $(TMPDIR)
diff --git a/src/test/run-make/panic-impl-transitive/panic-impl-consumer.rs b/src/test/run-make/panic-impl-transitive/panic-impl-consumer.rs
new file mode 100644
index 0000000000000..592fab8be85ec
--- /dev/null
+++ b/src/test/run-make/panic-impl-transitive/panic-impl-consumer.rs
@@ -0,0 +1,15 @@
+// Copyright 2018 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.
+
+#![no_std]
+#![no_main]
+
+// this crate provides the `panic_impl` lang item so we don't need to define it here
+extern crate panic_impl_provider;
diff --git a/src/test/run-make/panic-impl-transitive/panic-impl-provider.rs b/src/test/run-make/panic-impl-transitive/panic-impl-provider.rs
new file mode 100644
index 0000000000000..46cdf2e2fa55e
--- /dev/null
+++ b/src/test/run-make/panic-impl-transitive/panic-impl-provider.rs
@@ -0,0 +1,20 @@
+// Copyright 2018 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.
+
+#![crate_type = "rlib"]
+#![feature(panic_implementation)]
+#![no_std]
+
+use core::panic::PanicInfo;
+
+#[panic_implementation]
+fn panic(info: &PanicInfo) -> ! {
+    loop {}
+}

From 948ed24a091a569595de6a417f54dbb252f34cd7 Mon Sep 17 00:00:00 2001
From: Jorge Aparicio <jorge@japaric.io>
Date: Tue, 29 May 2018 14:25:35 +0200
Subject: [PATCH 11/12] fix tidy error

---
 src/librustc_typeck/check/mod.rs | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index d54f29e6b9980..c2c71d90f0674 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -1167,7 +1167,8 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>,
                             if !generics.params.is_empty() {
                                 fcx.tcx.sess.span_err(
                                     span,
-                                    "`#[panic_implementation]` function should have no type parameters",
+                                    "`#[panic_implementation]` function should have no type \
+                                     parameters",
                                 );
                             }
                         }

From 8ad15dea3f9ac9b0fcfad4a61a70aa47ecc4d938 Mon Sep 17 00:00:00 2001
From: Jorge Aparicio <jorge@japaric.io>
Date: Sun, 3 Jun 2018 19:57:49 +0200
Subject: [PATCH 12/12] turn run-make test into a run-make-fulldeps test

---
 .../panic-impl-transitive/Makefile                                | 0
 .../panic-impl-transitive/panic-impl-consumer.rs                  | 0
 .../panic-impl-transitive/panic-impl-provider.rs                  | 0
 3 files changed, 0 insertions(+), 0 deletions(-)
 rename src/test/{run-make => run-make-fulldeps}/panic-impl-transitive/Makefile (100%)
 rename src/test/{run-make => run-make-fulldeps}/panic-impl-transitive/panic-impl-consumer.rs (100%)
 rename src/test/{run-make => run-make-fulldeps}/panic-impl-transitive/panic-impl-provider.rs (100%)

diff --git a/src/test/run-make/panic-impl-transitive/Makefile b/src/test/run-make-fulldeps/panic-impl-transitive/Makefile
similarity index 100%
rename from src/test/run-make/panic-impl-transitive/Makefile
rename to src/test/run-make-fulldeps/panic-impl-transitive/Makefile
diff --git a/src/test/run-make/panic-impl-transitive/panic-impl-consumer.rs b/src/test/run-make-fulldeps/panic-impl-transitive/panic-impl-consumer.rs
similarity index 100%
rename from src/test/run-make/panic-impl-transitive/panic-impl-consumer.rs
rename to src/test/run-make-fulldeps/panic-impl-transitive/panic-impl-consumer.rs
diff --git a/src/test/run-make/panic-impl-transitive/panic-impl-provider.rs b/src/test/run-make-fulldeps/panic-impl-transitive/panic-impl-provider.rs
similarity index 100%
rename from src/test/run-make/panic-impl-transitive/panic-impl-provider.rs
rename to src/test/run-make-fulldeps/panic-impl-transitive/panic-impl-provider.rs