From b555e0f9f588bd1674ea5691d2192c09f69241ec Mon Sep 17 00:00:00 2001
From: Simon Sapin <simon.sapin@exyr.org>
Date: Wed, 20 Jun 2018 00:49:59 +0200
Subject: [PATCH 1/3] Add a test for the default allocation error hook

---
 src/test/run-pass/default-alloc-error-hook.rs | 28 +++++++++++++++++++
 1 file changed, 28 insertions(+)
 create mode 100644 src/test/run-pass/default-alloc-error-hook.rs

diff --git a/src/test/run-pass/default-alloc-error-hook.rs b/src/test/run-pass/default-alloc-error-hook.rs
new file mode 100644
index 0000000000000..5f6b60a5a8d99
--- /dev/null
+++ b/src/test/run-pass/default-alloc-error-hook.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.
+
+// ignore-cloudabi no processes
+// ignore-emscripten no processes
+
+use std::alloc::{Layout, handle_alloc_error};
+use std::env;
+use std::process::Command;
+use std::str;
+
+fn main() {
+    if env::args().len() > 1 {
+        handle_alloc_error(Layout::new::<[u8; 42]>())
+    }
+
+    let me = env::current_exe().unwrap();
+    let output = Command::new(&me).arg("next").output().unwrap();
+    assert!(!output.status.success(), "{:?} is a success", output.status);
+    assert_eq!(str::from_utf8(&output.stderr).unwrap(), "memory allocation of 42 bytes failed");
+}

From 8101344d44a92de04af935788435f66fd1fb3070 Mon Sep 17 00:00:00 2001
From: Simon Sapin <simon.sapin@exyr.org>
Date: Fri, 6 Jul 2018 15:49:52 +0200
Subject: [PATCH 2/3] Implement #[alloc_error_handler]

This to-be-stable attribute is equivalent to `#[lang = "oom"]`.
It is required when using the alloc crate without the std crate.
It is called by `handle_alloc_error`, which is in turned called
by "infallible" allocations APIs such as `Vec::push`.
---
 src/libcore/alloc.rs                          |  1 +
 src/librustc/middle/dead.rs                   | 12 ++++-
 src/librustc/middle/lang_items.rs             |  3 ++
 src/librustc/middle/weak_lang_items.rs        |  3 ++
 src/librustc_typeck/check/mod.rs              | 47 +++++++++++++++++++
 src/libstd/alloc.rs                           |  3 +-
 src/libstd/lib.rs                             |  3 +-
 src/libsyntax/feature_gate.rs                 |  8 ++++
 .../alloc-error-handler-bad-signature-1.rs    | 28 +++++++++++
 .../alloc-error-handler-bad-signature-2.rs    | 27 +++++++++++
 .../alloc-error-handler-bad-signature-3.rs    | 25 ++++++++++
 .../feature-gate-alloc-error-handler.rs       | 21 +++++++++
 src/test/run-make-fulldeps/issue-51671/app.rs |  5 +-
 src/test/ui/missing-alloc_error_handler.rs    | 33 +++++++++++++
 .../ui/missing-alloc_error_handler.stderr     |  4 ++
 src/test/ui/missing-allocator.rs              |  8 ++--
 16 files changed, 224 insertions(+), 7 deletions(-)
 create mode 100644 src/test/compile-fail/alloc-error-handler-bad-signature-1.rs
 create mode 100644 src/test/compile-fail/alloc-error-handler-bad-signature-2.rs
 create mode 100644 src/test/compile-fail/alloc-error-handler-bad-signature-3.rs
 create mode 100644 src/test/compile-fail/feature-gate-alloc-error-handler.rs
 create mode 100644 src/test/ui/missing-alloc_error_handler.rs
 create mode 100644 src/test/ui/missing-alloc_error_handler.stderr

diff --git a/src/libcore/alloc.rs b/src/libcore/alloc.rs
index 01221aecb6284..b6ac248b79f86 100644
--- a/src/libcore/alloc.rs
+++ b/src/libcore/alloc.rs
@@ -48,6 +48,7 @@ fn size_align<T>() -> (usize, usize) {
 /// use specific allocators with looser requirements.)
 #[stable(feature = "alloc_layout", since = "1.28.0")]
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[cfg_attr(not(stage0), lang = "alloc_layout")]
 pub struct Layout {
     // size of the requested block of memory, measured in bytes.
     size_: usize,
diff --git a/src/librustc/middle/dead.rs b/src/librustc/middle/dead.rs
index 226d19a91240f..42775e3a1837f 100644
--- a/src/librustc/middle/dead.rs
+++ b/src/librustc/middle/dead.rs
@@ -288,7 +288,17 @@ 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") || attr::contains_name(attrs, "panic_implementation") {
+    if attr::contains_name(attrs, "lang") {
+        return true;
+    }
+
+    // (To be) stable attribute for #[lang = "panic_impl"]
+    if attr::contains_name(attrs, "panic_implementation") {
+        return true;
+    }
+
+    // (To be) stable attribute for #[lang = "oom"]
+    if attr::contains_name(attrs, "alloc_error_handler") {
         return true;
     }
 
diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs
index fe676919a7d14..6c1ef851cbeca 100644
--- a/src/librustc/middle/lang_items.rs
+++ b/src/librustc/middle/lang_items.rs
@@ -187,6 +187,8 @@ pub fn extract(attrs: &[ast::Attribute]) -> Option<(Symbol, Span)> {
             }
         } else if attribute.check_name("panic_implementation") {
             return Some((Symbol::intern("panic_impl"), attribute.span))
+        } else if attribute.check_name("alloc_error_handler") {
+            return Some((Symbol::intern("oom"), attribute.span))
         }
     }
 
@@ -308,6 +310,7 @@ language_item_table! {
     BoxFreeFnLangItem,               "box_free",                box_free_fn;
     DropInPlaceFnLangItem,           "drop_in_place",           drop_in_place_fn;
     OomLangItem,                     "oom",                     oom;
+    AllocLayoutLangItem,             "alloc_layout",            alloc_layout;
 
     StartFnLangItem,                 "start",                   start_fn;
 
diff --git a/src/librustc/middle/weak_lang_items.rs b/src/librustc/middle/weak_lang_items.rs
index 180e75df1a66e..d8570b43fbe27 100644
--- a/src/librustc/middle/weak_lang_items.rs
+++ b/src/librustc/middle/weak_lang_items.rs
@@ -115,6 +115,9 @@ fn verify<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
             if lang_items::$item == lang_items::PanicImplLangItem {
                 tcx.sess.err(&format!("`#[panic_implementation]` function required, \
                                         but not found"));
+            } else if lang_items::$item == lang_items::OomLangItem {
+                tcx.sess.err(&format!("`#[alloc_error_handler]` function required, \
+                                        but not found"));
             } else {
                 tcx.sess.err(&format!("language item required, but not found: `{}`",
                                         stringify!($name)));
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 646c4f17568f0..c7ad3398873ea 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -1182,7 +1182,54 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>,
                 fcx.tcx.sess.err("language item required, but not found: `panic_info`");
             }
         }
+    }
+
+    // Check that a function marked as `#[alloc_error_handler]` has signature `fn(Layout) -> !`
+    if let Some(alloc_error_handler_did) = fcx.tcx.lang_items().oom() {
+        if alloc_error_handler_did == fcx.tcx.hir.local_def_id(fn_id) {
+            if let Some(alloc_layout_did) = fcx.tcx.lang_items().alloc_layout() {
+                if declared_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_alloc_layout = match inputs[0].sty {
+                        ty::TyAdt(ref adt, _) => {
+                            adt.did == alloc_layout_did
+                        },
+                        _ => false,
+                    };
+
+                    if !arg_is_alloc_layout {
+                        fcx.tcx.sess.span_err(
+                            decl.inputs[0].span,
+                            "argument should be `Layout`",
+                        );
+                    }
 
+                    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,
+                                    "`#[alloc_error_handler]` function should have no type \
+                                     parameters",
+                                );
+                            }
+                        }
+                    }
+                } else {
+                    fcx.tcx.sess.span_err(span, "function should have one argument");
+                }
+            } else {
+                fcx.tcx.sess.err("language item required, but not found: `alloc_layout`");
+            }
+        }
     }
 
     (fcx, gen_ty)
diff --git a/src/libstd/alloc.rs b/src/libstd/alloc.rs
index f6cecbea11f8d..37ec5bf1e40a0 100644
--- a/src/libstd/alloc.rs
+++ b/src/libstd/alloc.rs
@@ -125,7 +125,8 @@ fn default_alloc_error_hook(layout: Layout) {
 
 #[cfg(not(test))]
 #[doc(hidden)]
-#[lang = "oom"]
+#[cfg_attr(stage0, lang = "oom")]
+#[cfg_attr(not(stage0), alloc_error_handler)]
 #[unstable(feature = "alloc_internals", issue = "0")]
 pub extern fn rust_oom(layout: Layout) -> ! {
     let hook = HOOK.load(Ordering::SeqCst);
diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs
index d73cb1f8349a6..fec14b8d67d35 100644
--- a/src/libstd/lib.rs
+++ b/src/libstd/lib.rs
@@ -233,8 +233,9 @@
 // std is implemented with unstable features, many of which are internal
 // compiler details that will never be stable
 #![feature(alloc)]
-#![feature(allocator_api)]
+#![feature(alloc_error_handler)]
 #![feature(alloc_system)]
+#![feature(allocator_api)]
 #![feature(allocator_internals)]
 #![feature(allow_internal_unsafe)]
 #![feature(allow_internal_unstable)]
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index 4c88325378793..eb95a2436998f 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -479,6 +479,9 @@ declare_features! (
 
     // Allows async and await syntax
     (active, async_await, "1.28.0", Some(50547), None),
+
+    // #[alloc_error_handler]
+    (active, alloc_error_handler, "1.29.0", Some(51540), None),
 );
 
 declare_features! (
@@ -1081,6 +1084,11 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG
                            "#[panic_implementation] is an unstable feature",
                            cfg_fn!(panic_implementation))),
 
+    ("alloc_error_handler", Normal, Gated(Stability::Unstable,
+                           "alloc_error_handler",
+                           "#[alloc_error_handler] is an unstable feature",
+                           cfg_fn!(alloc_error_handler))),
+
     // Crate level attributes
     ("crate_name", CrateLevel, Ungated),
     ("crate_type", CrateLevel, Ungated),
diff --git a/src/test/compile-fail/alloc-error-handler-bad-signature-1.rs b/src/test/compile-fail/alloc-error-handler-bad-signature-1.rs
new file mode 100644
index 0000000000000..e398f16a065bd
--- /dev/null
+++ b/src/test/compile-fail/alloc-error-handler-bad-signature-1.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(alloc_error_handler, panic_implementation)]
+#![no_std]
+#![no_main]
+
+use core::alloc::Layout;
+
+#[alloc_error_handler]
+fn oom(
+    info: &Layout, //~ ERROR argument should be `Layout`
+) -> () //~ ERROR return type should be `!`
+{
+    loop {}
+}
+
+#[panic_implementation]
+fn panic(_: &core::panic::PanicInfo) -> ! { loop {} }
diff --git a/src/test/compile-fail/alloc-error-handler-bad-signature-2.rs b/src/test/compile-fail/alloc-error-handler-bad-signature-2.rs
new file mode 100644
index 0000000000000..4fee9d27e5175
--- /dev/null
+++ b/src/test/compile-fail/alloc-error-handler-bad-signature-2.rs
@@ -0,0 +1,27 @@
+// 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(alloc_error_handler, panic_implementation)]
+#![no_std]
+#![no_main]
+
+struct Layout;
+
+#[alloc_error_handler]
+fn oom(
+    info: Layout, //~ ERROR argument should be `Layout`
+) { //~ ERROR return type should be `!`
+    loop {}
+}
+
+#[panic_implementation]
+fn panic(_: &core::panic::PanicInfo) -> ! { loop {} }
diff --git a/src/test/compile-fail/alloc-error-handler-bad-signature-3.rs b/src/test/compile-fail/alloc-error-handler-bad-signature-3.rs
new file mode 100644
index 0000000000000..828a78055d5f2
--- /dev/null
+++ b/src/test/compile-fail/alloc-error-handler-bad-signature-3.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(alloc_error_handler, panic_implementation)]
+#![no_std]
+#![no_main]
+
+struct Layout;
+
+#[alloc_error_handler]
+fn oom() -> ! { //~ ERROR function should have one argument
+    loop {}
+}
+
+#[panic_implementation]
+fn panic(_: &core::panic::PanicInfo) -> ! { loop {} }
diff --git a/src/test/compile-fail/feature-gate-alloc-error-handler.rs b/src/test/compile-fail/feature-gate-alloc-error-handler.rs
new file mode 100644
index 0000000000000..66691af2d0325
--- /dev/null
+++ b/src/test/compile-fail/feature-gate-alloc-error-handler.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::alloc::Layout;
+
+#[alloc_error_handler] //~ ERROR #[alloc_error_handler] is an unstable feature (see issue #51540)
+fn oom(info: Layout) -> ! {
+    loop {}
+}
diff --git a/src/test/run-make-fulldeps/issue-51671/app.rs b/src/test/run-make-fulldeps/issue-51671/app.rs
index 720ce1512f26a..453602b800b5e 100644
--- a/src/test/run-make-fulldeps/issue-51671/app.rs
+++ b/src/test/run-make-fulldeps/issue-51671/app.rs
@@ -14,6 +14,7 @@
 #![no_main]
 #![no_std]
 
+use core::alloc::Layout;
 use core::panic::PanicInfo;
 
 #[panic_implementation]
@@ -25,4 +26,6 @@ fn panic(_: &PanicInfo) -> ! {
 fn eh() {}
 
 #[lang = "oom"]
-fn oom() {}
+fn oom(_: Layout) -> ! {
+    loop {}
+}
diff --git a/src/test/ui/missing-alloc_error_handler.rs b/src/test/ui/missing-alloc_error_handler.rs
new file mode 100644
index 0000000000000..3842d48f8fab1
--- /dev/null
+++ b/src/test/ui/missing-alloc_error_handler.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.
+
+// compile-flags: -C panic=abort
+// no-prefer-dynamic
+
+#![no_std]
+#![crate_type = "staticlib"]
+#![feature(panic_implementation, alloc_error_handler, alloc)]
+
+#[panic_implementation]
+fn panic(_: &core::panic::PanicInfo) -> ! {
+    loop {}
+}
+
+extern crate alloc;
+
+#[global_allocator]
+static A: MyAlloc = MyAlloc;
+
+struct MyAlloc;
+
+unsafe impl core::alloc::GlobalAlloc for MyAlloc {
+    unsafe fn alloc(&self, _: core::alloc::Layout) -> *mut u8 { 0 as _ }
+    unsafe fn dealloc(&self, _: *mut u8, _: core::alloc::Layout) {}
+}
diff --git a/src/test/ui/missing-alloc_error_handler.stderr b/src/test/ui/missing-alloc_error_handler.stderr
new file mode 100644
index 0000000000000..5489b2cbbfad8
--- /dev/null
+++ b/src/test/ui/missing-alloc_error_handler.stderr
@@ -0,0 +1,4 @@
+error: `#[alloc_error_handler]` function required, but not found
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/missing-allocator.rs b/src/test/ui/missing-allocator.rs
index 24282631b7eea..c949dcb635aad 100644
--- a/src/test/ui/missing-allocator.rs
+++ b/src/test/ui/missing-allocator.rs
@@ -13,14 +13,16 @@
 
 #![no_std]
 #![crate_type = "staticlib"]
-#![feature(panic_implementation, lang_items, alloc)]
+#![feature(panic_implementation, alloc_error_handler, alloc)]
 
 #[panic_implementation]
 fn panic(_: &core::panic::PanicInfo) -> ! {
     loop {}
 }
 
-#[lang = "oom"]
-fn oom() {}
+#[alloc_error_handler]
+fn oom(_: core::alloc::Layout) -> ! {
+    loop {}
+}
 
 extern crate alloc;

From c37a4a03586a76c188d405fdb46e91843ac30858 Mon Sep 17 00:00:00 2001
From: Simon Sapin <simon.sapin@exyr.org>
Date: Sun, 8 Jul 2018 16:07:08 +0200
Subject: [PATCH 3/3] Remove `extern` on the `pub fn rust_oom` lang item in
 libstd, to match ABI of the declaration in liballoc

---
 src/libstd/alloc.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/libstd/alloc.rs b/src/libstd/alloc.rs
index 37ec5bf1e40a0..8db365cd21d67 100644
--- a/src/libstd/alloc.rs
+++ b/src/libstd/alloc.rs
@@ -128,7 +128,7 @@ fn default_alloc_error_hook(layout: Layout) {
 #[cfg_attr(stage0, lang = "oom")]
 #[cfg_attr(not(stage0), alloc_error_handler)]
 #[unstable(feature = "alloc_internals", issue = "0")]
-pub extern fn rust_oom(layout: Layout) -> ! {
+pub fn rust_oom(layout: Layout) -> ! {
     let hook = HOOK.load(Ordering::SeqCst);
     let hook: fn(Layout) = if hook.is_null() {
         default_alloc_error_hook