implement #[panic_implementation]

This commit is contained in:
Jorge Aparicio 2018-04-30 10:55:24 +02:00
parent 3575be60ea
commit e44ad61a2d
22 changed files with 379 additions and 18 deletions

View File

@ -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

View File

@ -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)
}
}

View File

@ -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) }
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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)
}

View File

@ -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]

View File

@ -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);

View File

@ -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),

View File

@ -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 {}
}

View File

@ -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 {}
}

View File

@ -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 {} }

View File

@ -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 `!`
{
}

View File

@ -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 {}
}

View File

@ -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 {}
}

View File

@ -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 {}
}

View File

@ -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 {}

View File

@ -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"]

View File

@ -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

View File

@ -10,7 +10,7 @@
#![feature(lang_items)]
#[lang = "panic_fmt"]
#[lang = "panic_impl"]
struct Foo; //~ ERROR E0152
fn main() {

View File

@ -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