Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement #[panic_implementation] #50338

Merged
merged 12 commits into from
Jun 3, 2018
25 changes: 11 additions & 14 deletions src/doc/unstable-book/src/language-features/lang-items.md
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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 () {}
Expand Down Expand Up @@ -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;
Expand All @@ -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() }
}
```
Expand All @@ -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;
Expand All @@ -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() }
}
```
Expand Down Expand Up @@ -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`
Expand Down Expand Up @@ -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`
Expand Down
10 changes: 7 additions & 3 deletions src/doc/unstable-book/src/language-features/used.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 {}
}
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/libcore/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 4 additions & 2 deletions src/libcore/panic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand All @@ -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)]
Expand Down Expand Up @@ -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)
}
}
Expand Down
21 changes: 21 additions & 0 deletions src/libcore/panicking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
issue = "0")]

use fmt;
#[cfg(not(stage0))]
use panic::{Location, PanicInfo};

#[cold] #[inline(never)] // this is the slow path, always
#[lang = "panic"]
Expand All @@ -59,6 +61,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)]
Expand All @@ -70,3 +73,21 @@ 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)) -> ! {
// 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),
Location::internal_constructor(file, line, col),
);
unsafe { panic_impl(&pi) }
}
6 changes: 3 additions & 3 deletions src/librustc/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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();
}
```
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/middle/dead.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
5 changes: 4 additions & 1 deletion src/librustc/middle/lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}
}

Expand Down Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/middle/weak_lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
58 changes: 56 additions & 2 deletions src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -1129,6 +1129,60 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>,
}
}

// Check that a function marked as `#[panic_implementation]` has signature `fn(&PanicInfo) -> !`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm personally more of a fan of generating AST nodes which correspond to the structure we want which avoids reaching into the internals of the typechecker where possible. For example I don't think this disallows something like this:

#[panic_implementation]
fn foo<T>(a: &PanicInfo) -> ! {
}

right?

I was thinking that we'd basically expand #[panic_implementation] to #[lang = "panic_impl"] where the lang item internally dispatches to #[panic_implementation] (automatically typechecking it along the way). That may be slightly more difficult to architect, though, but I'm curious what you think

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The language items are not type-checked at all at the moment, so there is nothing to hand the type-checking over to.

panic_fmt has no benefits over the new language item and IIRC (from when I wrote the instructions) complicates the implementation of this feature unnecessarily.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right yeah, my point is that we generate a language item which internally dispatches to the function annotated #[panic_implementation], which by construction will typecheck the panic implementation and not require typechekcing of lang ites.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alexcrichton you mean something like this?

// this
// (signature is totally wrong)
#[panic_implementation]
fn foo<T>(pi: &i32) {
    // body
}

// expands into
#[lang = "panic_impl"]
fn __secret_name_maybe(pi: &PanicInfo) -> ! {
    // user input
    fn foo<T>(pi: &i32) {
        // body
    }

    let f: fn(&PanicInfo) -> ! = foo;
    f()
}

That would work typechecking wise, but will it report error messages with meaningful spans? (My experience with proc macros says that most errors will wrongly report the whole #[panic_implementation] item as their span)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this disallows something like this:
fn foo(a: &PanicInfo) -> ! {

I can add a check and test to reject that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@japaric yeah that latter idea is what I was thinking, where basically the compiler manufactures the actual lang item which is hooked up to call the #[panic_implementation]. That way the compiler has full control over ABI and whatnot

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sadly in many cases this approach results in kind-of suboptimal errors.

For example

fn foo() {
    fn bar<T>(i: i32) -> ! { loop {} }
    let foo: fn(i32) -> ! = bar;
}

gives

error[E0282]: type annotations needed
 --> src/main.rs:3:29
  |
3 |     let foo: fn(i32) -> ! = bar;
  |                             ^^^ cannot infer type for `T`

but if expanded within a macro it would be something like this instead:

error[E0282]: type annotations needed
 --> src/main.rs:3:29
  |
3 |     #[panic_implementation]
  |     fn panic<T>(...) -> ! { loop {} }
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type for `T`

which is just… incomprehensible.

That being said, I have a long time wish to eventually implement universal checking of the language item signatures (similar to one we do for intrinsics), which would resolve this issue universally. Thus, I’d recommend going for the simplest complete approach possible for now.

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 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_panic_info = match inputs[0].sty {
ty::TyRef(region, 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`",
);
}

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");
}
} else {
fcx.tcx.sess.err("language item required, but not found: `panic_info`");
}
}

}

(fcx, gen_ty)
}

Expand Down
2 changes: 2 additions & 0 deletions src/libstd/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]

Expand Down
Loading