Skip to content

Commit e44ad61

Browse files
committed
implement #[panic_implementation]
1 parent 3575be6 commit e44ad61

22 files changed

+379
-18
lines changed

src/libcore/macros.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
// except according to those terms.
1010

1111
/// Entry point of thread panic, for details, see std::macros
12+
#[cfg(stage0)]
1213
#[macro_export]
1314
#[allow_internal_unstable]
1415
#[stable(feature = "core", since = "1.6.0")]
@@ -28,6 +29,27 @@ macro_rules! panic {
2829
});
2930
}
3031

32+
/// Entry point of thread panic, for details, see std::macros
33+
#[cfg(not(stage0))]
34+
#[macro_export]
35+
#[allow_internal_unstable]
36+
#[stable(feature = "core", since = "1.6.0")]
37+
macro_rules! panic {
38+
() => (
39+
panic!("explicit panic")
40+
);
41+
($msg:expr) => ({
42+
$crate::panicking::panic_payload($msg, &(file!(), line!(), __rust_unstable_column!()))
43+
});
44+
($msg:expr,) => (
45+
panic!($msg)
46+
);
47+
($fmt:expr, $($arg:tt)+) => ({
48+
$crate::panicking::panic_fmt(format_args!($fmt, $($arg)*),
49+
&(file!(), line!(), __rust_unstable_column!()))
50+
});
51+
}
52+
3153
/// Asserts that two expressions are equal to each other (using [`PartialEq`]).
3254
///
3355
/// On panic, this macro will print the values of the expressions with their

src/libcore/panic.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ use fmt;
3535
///
3636
/// panic!("Normal panic");
3737
/// ```
38+
#[cfg_attr(not(stage0), lang = "panic_info")]
3839
#[stable(feature = "panic_hooks", since = "1.10.0")]
3940
#[derive(Debug)]
4041
pub struct PanicInfo<'a> {
@@ -53,7 +54,8 @@ impl<'a> PanicInfo<'a> {
5354
pub fn internal_constructor(message: Option<&'a fmt::Arguments<'a>>,
5455
location: Location<'a>)
5556
-> Self {
56-
PanicInfo { payload: &(), location, message }
57+
struct NoPayload;
58+
PanicInfo { payload: &NoPayload, location, message }
5759
}
5860

5961
#[doc(hidden)]
@@ -121,7 +123,7 @@ impl<'a> PanicInfo<'a> {
121123
#[stable(feature = "panic_hooks", since = "1.10.0")]
122124
pub fn location(&self) -> Option<&Location> {
123125
// NOTE: If this is changed to sometimes return None,
124-
// deal with that case in std::panicking::default_hook.
126+
// deal with that case in std::panicking::default_hook and std::panicking::begin_panic_fmt.
125127
Some(&self.location)
126128
}
127129
}

src/libcore/panicking.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,33 @@
3636
and related macros",
3737
issue = "0")]
3838

39+
#[cfg(not(stage0))]
40+
use any::Any;
3941
use fmt;
42+
#[cfg(not(stage0))]
43+
use panic::{Location, PanicInfo};
44+
45+
#[cfg(not(stage0))]
46+
#[allow(improper_ctypes)] // PanicInfo contains a trait object which is not FFI safe
47+
extern "C" {
48+
#[lang = "panic_impl"]
49+
fn panic_impl(pi: &PanicInfo) -> !;
50+
}
51+
52+
#[cfg(not(stage0))]
53+
#[cold] #[inline(never)]
54+
pub fn panic_payload<M>(msg: M, file_line_col: &(&'static str, u32, u32)) -> !
55+
where
56+
M: Any + Send,
57+
{
58+
let (file, line, col) = *file_line_col;
59+
let mut pi = PanicInfo::internal_constructor(
60+
None,
61+
Location::internal_constructor(file, line, col),
62+
);
63+
pi.set_payload(&msg);
64+
unsafe { panic_impl(&pi) }
65+
}
4066

4167
#[cold] #[inline(never)] // this is the slow path, always
4268
#[lang = "panic"]
@@ -59,6 +85,7 @@ fn panic_bounds_check(file_line_col: &(&'static str, u32, u32),
5985
len, index), file_line_col)
6086
}
6187

88+
#[cfg(stage0)]
6289
#[cold] #[inline(never)]
6390
pub fn panic_fmt(fmt: fmt::Arguments, file_line_col: &(&'static str, u32, u32)) -> ! {
6491
#[allow(improper_ctypes)]
@@ -70,3 +97,16 @@ pub fn panic_fmt(fmt: fmt::Arguments, file_line_col: &(&'static str, u32, u32))
7097
let (file, line, col) = *file_line_col;
7198
unsafe { panic_impl(fmt, file, line, col) }
7299
}
100+
101+
#[cfg(not(stage0))]
102+
#[cold] #[inline(never)]
103+
pub fn panic_fmt(fmt: fmt::Arguments, file_line_col: &(&'static str, u32, u32)) -> ! {
104+
struct NoPayload;
105+
106+
let (file, line, col) = *file_line_col;
107+
let pi = PanicInfo::internal_constructor(
108+
Some(&fmt),
109+
Location::internal_constructor(file, line, col),
110+
);
111+
unsafe { panic_impl(&pi) }
112+
}

src/librustc/middle/dead.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ impl<'a, 'tcx> Visitor<'tcx> for MarkSymbolVisitor<'a, 'tcx> {
284284
fn has_allow_dead_code_or_lang_attr(tcx: TyCtxt,
285285
id: ast::NodeId,
286286
attrs: &[ast::Attribute]) -> bool {
287-
if attr::contains_name(attrs, "lang") {
287+
if attr::contains_name(attrs, "lang") || attr::contains_name(attrs, "panic_implementation") {
288288
return true;
289289
}
290290

src/librustc/middle/lang_items.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,8 @@ pub fn extract(attrs: &[ast::Attribute]) -> Option<(Symbol, Span)> {
185185
if let Some(value) = attribute.value_str() {
186186
return Some((value, attribute.span));
187187
}
188+
} else if attribute.check_name("panic_implementation") {
189+
return Some((Symbol::intern("panic_impl"), attribute.span))
188190
}
189191
}
190192

@@ -299,7 +301,8 @@ language_item_table! {
299301
// lang item, but do not have it defined.
300302
PanicFnLangItem, "panic", panic_fn;
301303
PanicBoundsCheckFnLangItem, "panic_bounds_check", panic_bounds_check_fn;
302-
PanicFmtLangItem, "panic_fmt", panic_fmt;
304+
PanicInfoLangItem, "panic_info", panic_info;
305+
PanicImplLangItem, "panic_impl", panic_impl;
303306

304307
ExchangeMallocFnLangItem, "exchange_malloc", exchange_malloc_fn;
305308
BoxFreeFnLangItem, "box_free", box_free_fn;

src/librustc/middle/weak_lang_items.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for Context<'a, 'tcx> {
148148
) }
149149

150150
weak_lang_items! {
151-
panic_fmt, PanicFmtLangItem, rust_begin_unwind;
151+
panic_impl, PanicImplLangItem, rust_begin_unwind;
152152
eh_personality, EhPersonalityLangItem, rust_eh_personality;
153153
eh_unwind_resume, EhUnwindResumeLangItem, rust_eh_unwind_resume;
154154
oom, OomLangItem, rust_oom;

src/librustc_typeck/check/mod.rs

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ use rustc::middle::region;
9696
use rustc::mir::interpret::{GlobalId};
9797
use rustc::ty::subst::{UnpackedKind, Subst, Substs};
9898
use rustc::traits::{self, ObligationCause, ObligationCauseCode, TraitEngine};
99-
use rustc::ty::{self, Ty, TyCtxt, GenericParamDefKind, Visibility, ToPredicate};
99+
use rustc::ty::{self, Ty, TyCtxt, GenericParamDefKind, Visibility, ToPredicate, RegionKind};
100100
use rustc::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability};
101101
use rustc::ty::fold::TypeFoldable;
102102
use rustc::ty::maps::Providers;
@@ -1129,6 +1129,48 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>,
11291129
}
11301130
}
11311131

1132+
// Check that a function marked as `#[panic_implementation]` has signature `fn(&PanicInfo) -> !`
1133+
if let Some(panic_impl_did) = fcx.tcx.lang_items().panic_impl() {
1134+
if panic_impl_did == fn_hir_id.owner_def_id() {
1135+
if let Some(panic_info_did) = fcx.tcx.lang_items().panic_info() {
1136+
if ret_ty.sty != ty::TyNever {
1137+
fcx.tcx.sess.span_err(
1138+
decl.output.span(),
1139+
"return type should be `!`",
1140+
);
1141+
}
1142+
1143+
let inputs = fn_sig.inputs();
1144+
let span = fcx.tcx.hir.span(fn_id);
1145+
if inputs.len() == 1 {
1146+
let arg_is_panic_info = match inputs[0].sty {
1147+
ty::TyRef(region, ty::TypeAndMut { ty, mutbl }) => match ty.sty {
1148+
ty::TyAdt(ref adt, _) => {
1149+
adt.did == panic_info_did &&
1150+
mutbl == hir::Mutability::MutImmutable &&
1151+
*region != RegionKind::ReStatic
1152+
},
1153+
_ => false,
1154+
},
1155+
_ => false,
1156+
};
1157+
1158+
if !arg_is_panic_info {
1159+
fcx.tcx.sess.span_err(
1160+
decl.inputs[0].span,
1161+
"argument should be `&PanicInfo`",
1162+
);
1163+
}
1164+
} else {
1165+
fcx.tcx.sess.span_err(span, "function should have one argument");
1166+
}
1167+
} else {
1168+
fcx.tcx.sess.err("language item required, but not found: `panic_info`");
1169+
}
1170+
}
1171+
1172+
}
1173+
11321174
(fcx, gen_ty)
11331175
}
11341176

src/libstd/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,8 @@
317317
#![cfg_attr(windows, feature(used))]
318318
#![feature(doc_alias)]
319319
#![feature(float_internals)]
320+
#![feature(panic_info_message)]
321+
#![cfg_attr(not(stage0), feature(panic_implementation))]
320322

321323
#![default_lib_allocator]
322324

src/libstd/panicking.rs

Lines changed: 94 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ fn default_hook(info: &PanicInfo) {
186186

187187
let location = info.location().unwrap(); // The current implementation always returns Some
188188

189-
let msg = match info.payload().downcast_ref::<&'static str>() {
189+
let msg = match info.payload().downcast_ref::<&str>() {
190190
Some(s) => *s,
191191
None => match info.payload().downcast_ref::<String>() {
192192
Some(s) => &s[..],
@@ -319,6 +319,7 @@ pub fn panicking() -> bool {
319319

320320
/// Entry point of panic from the libcore crate.
321321
#[cfg(not(test))]
322+
#[cfg(stage0)]
322323
#[lang = "panic_fmt"]
323324
#[unwind(allowed)]
324325
pub extern fn rust_begin_panic(msg: fmt::Arguments,
@@ -328,12 +329,22 @@ pub extern fn rust_begin_panic(msg: fmt::Arguments,
328329
begin_panic_fmt(&msg, &(file, line, col))
329330
}
330331

332+
/// Entry point of panic from the libcore crate.
333+
#[cfg(not(test))]
334+
#[cfg(not(stage0))]
335+
#[panic_implementation]
336+
#[unwind(allowed)]
337+
pub fn rust_begin_panic(info: &PanicInfo) -> ! {
338+
continue_panic_fmt(&info)
339+
}
340+
331341
/// The entry point for panicking with a formatted message.
332342
///
333343
/// This is designed to reduce the amount of code required at the call
334344
/// site as much as possible (so that `panic!()` has as low an impact
335345
/// on (e.g.) the inlining of other functions as possible), by moving
336346
/// the actual formatting into this shared place.
347+
#[cfg(stage0)]
337348
#[unstable(feature = "libstd_sys_internals",
338349
reason = "used by the panic! macro",
339350
issue = "0")]
@@ -381,12 +392,92 @@ pub fn begin_panic_fmt(msg: &fmt::Arguments,
381392
}
382393
}
383394

395+
/// The entry point for panicking with a formatted message.
396+
///
397+
/// This is designed to reduce the amount of code required at the call
398+
/// site as much as possible (so that `panic!()` has as low an impact
399+
/// on (e.g.) the inlining of other functions as possible), by moving
400+
/// the actual formatting into this shared place.
401+
#[cfg(not(stage0))]
402+
#[unstable(feature = "libstd_sys_internals",
403+
reason = "used by the panic! macro",
404+
issue = "0")]
405+
#[inline(never)] #[cold]
406+
pub fn begin_panic_fmt(msg: &fmt::Arguments,
407+
file_line_col: &(&'static str, u32, u32)) -> ! {
408+
let (file, line, col) = *file_line_col;
409+
let info = PanicInfo::internal_constructor(
410+
Some(msg),
411+
Location::internal_constructor(file, line, col),
412+
);
413+
continue_panic_fmt(&info)
414+
}
415+
416+
#[cfg(not(stage0))]
417+
fn continue_panic_fmt(info: &PanicInfo) -> ! {
418+
use fmt::Write;
419+
420+
// We do two allocations here, unfortunately. But (a) they're
421+
// required with the current scheme, and (b) we don't handle
422+
// panic + OOM properly anyway (see comment in begin_panic
423+
// below).
424+
425+
let loc = info.location().unwrap(); // The current implementation always returns Some
426+
let file_line_col = (loc.file(), loc.line(), loc.column());
427+
rust_panic_with_hook(
428+
&mut PanicPayload::new(info.payload(), info.message()),
429+
info.message(),
430+
&file_line_col);
431+
432+
struct PanicPayload<'a> {
433+
payload: &'a (Any + Send),
434+
msg: Option<&'a fmt::Arguments<'a>>,
435+
string: Option<String>,
436+
}
437+
438+
impl<'a> PanicPayload<'a> {
439+
fn new(payload: &'a (Any + Send), msg: Option<&'a fmt::Arguments<'a>>) -> PanicPayload<'a> {
440+
PanicPayload { payload, msg, string: None }
441+
}
442+
443+
444+
fn fill(&mut self) -> Option<&mut String> {
445+
if let Some(msg) = self.msg.take() {
446+
Some(self.string.get_or_insert_with(|| {
447+
let mut s = String::new();
448+
drop(s.write_fmt(*msg));
449+
s
450+
}))
451+
} else {
452+
None
453+
}
454+
}
455+
}
456+
457+
unsafe impl<'a> BoxMeUp for PanicPayload<'a> {
458+
fn box_me_up(&mut self) -> *mut (Any + Send) {
459+
if let Some(string) = self.fill() {
460+
let contents = mem::replace(string, String::new());
461+
Box::into_raw(Box::new(contents))
462+
} else {
463+
// We can't go from &(Any+Send) to Box<Any+Send> so the payload is lost here
464+
struct NoPayload;
465+
Box::into_raw(Box::new(NoPayload))
466+
}
467+
}
468+
469+
fn get(&mut self) -> &(Any + Send) {
470+
self.payload
471+
}
472+
}
473+
}
474+
384475
/// This is the entry point of panicking for panic!() and assert!().
385476
#[unstable(feature = "libstd_sys_internals",
386477
reason = "used by the panic! macro",
387478
issue = "0")]
388479
#[inline(never)] #[cold] // avoid code bloat at the call sites as much as possible
389-
pub fn begin_panic<M: Any + Send>(msg: M, file_line_col: &(&'static str, u32, u32)) -> ! {
480+
pub fn begin_panic<M: Any + Send>(msg: M, file_line_col: &(&str, u32, u32)) -> ! {
390481
// Note that this should be the only allocation performed in this code path.
391482
// Currently this means that panic!() on OOM will invoke this code path,
392483
// 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
431522
/// abort or unwind.
432523
fn rust_panic_with_hook(payload: &mut BoxMeUp,
433524
message: Option<&fmt::Arguments>,
434-
file_line_col: &(&'static str, u32, u32)) -> ! {
525+
file_line_col: &(&str, u32, u32)) -> ! {
435526
let (file, line, col) = *file_line_col;
436527

437528
let panics = update_panic_count(1);

src/libsyntax/feature_gate.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,9 @@ declare_features! (
475475

476476
// 'a: { break 'a; }
477477
(active, label_break_value, "1.28.0", Some(48594), None),
478+
479+
// #[panic_implementation]
480+
(active, panic_implementation, "1.28.0", Some(44489), None),
478481
);
479482

480483
declare_features! (
@@ -1069,6 +1072,12 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG
10691072
"attribute is currently unstable",
10701073
cfg_fn!(wasm_custom_section))),
10711074

1075+
// RFC 2070
1076+
("panic_implementation", Normal, Gated(Stability::Unstable,
1077+
"panic_implementation",
1078+
"#[panic_implementation] is an unstable feature",
1079+
cfg_fn!(panic_implementation))),
1080+
10721081
// Crate level attributes
10731082
("crate_name", CrateLevel, Ungated),
10741083
("crate_type", CrateLevel, Ungated),

0 commit comments

Comments
 (0)