Auto merge of #67502 - Mark-Simulacrum:opt-catch, r=Mark-Simulacrum
Optimize catch_unwind to match C++ try/catch This refactors the implementation of catching unwinds to allow LLVM to inline the "try" closure directly into the happy path, avoiding indirection. This means that the catch_unwind implementation is (after this PR) zero-cost unless a panic is thrown. https://rust.godbolt.org/z/cZcUSB is an example of the current codegen in a simple case. Notably, the codegen is *exactly the same* if `-Cpanic=abort` is passed, which is clearly not great. This PR, on the other hand, generates the following assembly: ```asm # -Cpanic=unwind: push rbx mov ebx,0x2a call QWORD PTR [rip+0x1c53c] # <happy> mov eax,ebx pop rbx ret mov rdi,rax call QWORD PTR [rip+0x1c537] # cleanup function call call QWORD PTR [rip+0x1c539] # <unfortunate> mov ebx,0xd mov eax,ebx pop rbx ret # -Cpanic=abort: push rax call QWORD PTR [rip+0x20a1] # <happy> mov eax,0x2a pop rcx ret ``` Fixes #64224, and resolves #64222.
This commit is contained in:
commit
be055d96c4
@ -52,7 +52,6 @@ fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
||||
|
||||
#[lang = "eh_personality"] extern fn rust_eh_personality() {}
|
||||
#[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 () {}
|
||||
```
|
||||
@ -67,7 +66,7 @@ Other features provided by lang items include:
|
||||
marked with lang items; those specific four are `eq`, `ord`,
|
||||
`deref`, and `add` respectively.
|
||||
- stack unwinding and general failure; the `eh_personality`,
|
||||
`eh_unwind_resume`, `fail` and `fail_bounds_checks` lang items.
|
||||
`panic` and `panic_bounds_checks` lang items.
|
||||
- the traits in `std::marker` used to indicate types of
|
||||
various kinds; lang items `send`, `sync` and `copy`.
|
||||
- the marker types and variance indicators found in
|
||||
@ -130,12 +129,6 @@ fn start(_argc: isize, _argv: *const *const u8) -> isize {
|
||||
pub extern fn rust_eh_personality() {
|
||||
}
|
||||
|
||||
// This function may be needed based on the compilation target.
|
||||
#[lang = "eh_unwind_resume"]
|
||||
#[no_mangle]
|
||||
pub extern fn rust_eh_unwind_resume() {
|
||||
}
|
||||
|
||||
#[lang = "panic_impl"]
|
||||
#[no_mangle]
|
||||
pub extern fn rust_begin_panic(info: &PanicInfo) -> ! {
|
||||
@ -173,12 +166,6 @@ pub extern fn main(_argc: i32, _argv: *const *const u8) -> i32 {
|
||||
pub extern fn rust_eh_personality() {
|
||||
}
|
||||
|
||||
// This function may be needed based on the compilation target.
|
||||
#[lang = "eh_unwind_resume"]
|
||||
#[no_mangle]
|
||||
pub extern fn rust_eh_unwind_resume() {
|
||||
}
|
||||
|
||||
#[lang = "panic_impl"]
|
||||
#[no_mangle]
|
||||
pub extern fn rust_begin_panic(info: &PanicInfo) -> ! {
|
||||
@ -211,10 +198,8 @@ compiler. When a panic happens, this controls the message that's displayed on
|
||||
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`
|
||||
flag is set in the options of the compilation target. It allows customizing the
|
||||
process of resuming unwind at the end of the landing pads. The language item's name
|
||||
is `eh_unwind_resume`.
|
||||
Finally, a `eh_catch_typeinfo` static is needed for certain targets which
|
||||
implement Rust panics on top of C++ exceptions.
|
||||
|
||||
## List of all language items
|
||||
|
||||
@ -247,8 +232,6 @@ the source code.
|
||||
- `eh_personality`: `libpanic_unwind/emcc.rs` (EMCC)
|
||||
- `eh_personality`: `libpanic_unwind/gcc.rs` (GNU)
|
||||
- `eh_personality`: `libpanic_unwind/seh.rs` (SEH)
|
||||
- `eh_unwind_resume`: `libpanic_unwind/gcc.rs` (GCC)
|
||||
- `eh_catch_typeinfo`: `libpanic_unwind/seh.rs` (SEH)
|
||||
- `eh_catch_typeinfo`: `libpanic_unwind/emcc.rs` (EMCC)
|
||||
- `panic`: `libcore/panicking.rs`
|
||||
- `panic_bounds_check`: `libcore/panicking.rs`
|
||||
|
@ -1871,14 +1871,16 @@ extern "rust-intrinsic" {
|
||||
#[rustc_const_unstable(feature = "const_discriminant", issue = "69821")]
|
||||
pub fn discriminant_value<T>(v: &T) -> u64;
|
||||
|
||||
/// Rust's "try catch" construct which invokes the function pointer `f` with
|
||||
/// the data pointer `data`.
|
||||
/// Rust's "try catch" construct which invokes the function pointer `try_fn`
|
||||
/// with the data pointer `data`.
|
||||
///
|
||||
/// The third pointer is a target-specific data pointer which is filled in
|
||||
/// with the specifics of the exception that occurred. For examples on Unix
|
||||
/// platforms this is a `*mut *mut T` which is filled in by the compiler and
|
||||
/// on MSVC it's `*mut [usize; 2]`. For more information see the compiler's
|
||||
/// The third argument is a function called if a panic occurs. This function
|
||||
/// takes the data pointer and a pointer to the target-specific exception
|
||||
/// object that was caught. For more information see the compiler's
|
||||
/// source as well as std's catch implementation.
|
||||
#[cfg(not(bootstrap))]
|
||||
pub fn r#try(try_fn: fn(*mut u8), data: *mut u8, catch_fn: fn(*mut u8, *mut u8)) -> i32;
|
||||
#[cfg(bootstrap)]
|
||||
pub fn r#try(f: fn(*mut u8), data: *mut u8, local_ptr: *mut u8) -> i32;
|
||||
|
||||
/// Emits a `!nontemporal` store according to LLVM (see their docs).
|
||||
|
@ -18,17 +18,11 @@
|
||||
#![feature(staged_api)]
|
||||
#![feature(rustc_attrs)]
|
||||
|
||||
// Rust's "try" function, but if we're aborting on panics we just call the
|
||||
// function as there's nothing else we need to do here.
|
||||
use core::any::Any;
|
||||
|
||||
#[rustc_std_internal_symbol]
|
||||
pub unsafe extern "C" fn __rust_maybe_catch_panic(
|
||||
f: fn(*mut u8),
|
||||
data: *mut u8,
|
||||
_data_ptr: *mut usize,
|
||||
_vtable_ptr: *mut usize,
|
||||
) -> u32 {
|
||||
f(data);
|
||||
0
|
||||
pub unsafe extern "C" fn __rust_panic_cleanup(_: *mut u8) -> *mut (dyn Any + Send + 'static) {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
// "Leak" the payload and shim to the relevant abort on the platform in
|
||||
@ -92,7 +86,7 @@ pub unsafe extern "C" fn __rust_start_panic(_payload: usize) -> u32 {
|
||||
// binaries, but it should never be called as we don't link in an unwinding
|
||||
// runtime at all.
|
||||
pub mod personalities {
|
||||
#[no_mangle]
|
||||
#[rustc_std_internal_symbol]
|
||||
#[cfg(not(any(
|
||||
all(target_arch = "wasm32", not(target_os = "emscripten"),),
|
||||
all(target_os = "windows", target_env = "gnu", target_arch = "x86_64",),
|
||||
@ -101,7 +95,7 @@ pub mod personalities {
|
||||
|
||||
// On x86_64-pc-windows-gnu we use our own personality function that needs
|
||||
// to return `ExceptionContinueSearch` as we're passing on all our frames.
|
||||
#[no_mangle]
|
||||
#[rustc_std_internal_symbol]
|
||||
#[cfg(all(target_os = "windows", target_env = "gnu", target_arch = "x86_64"))]
|
||||
pub extern "C" fn rust_eh_personality(
|
||||
_record: usize,
|
||||
@ -117,16 +111,16 @@ pub mod personalities {
|
||||
//
|
||||
// Note that we don't execute landing pads, so this is never called, so it's
|
||||
// body is empty.
|
||||
#[no_mangle]
|
||||
#[cfg(all(target_os = "windows", target_env = "gnu"))]
|
||||
#[rustc_std_internal_symbol]
|
||||
#[cfg(all(bootstrap, target_os = "windows", target_env = "gnu"))]
|
||||
pub extern "C" fn rust_eh_unwind_resume() {}
|
||||
|
||||
// These two are called by our startup objects on i686-pc-windows-gnu, but
|
||||
// they don't need to do anything so the bodies are nops.
|
||||
#[no_mangle]
|
||||
#[rustc_std_internal_symbol]
|
||||
#[cfg(all(target_os = "windows", target_env = "gnu", target_arch = "x86"))]
|
||||
pub extern "C" fn rust_eh_register_frames() {}
|
||||
#[no_mangle]
|
||||
#[rustc_std_internal_symbol]
|
||||
#[cfg(all(target_os = "windows", target_env = "gnu", target_arch = "x86"))]
|
||||
pub extern "C" fn rust_eh_unregister_frames() {}
|
||||
}
|
||||
|
@ -6,10 +6,6 @@ use alloc::boxed::Box;
|
||||
use core::any::Any;
|
||||
use core::intrinsics;
|
||||
|
||||
pub fn payload() -> *mut u8 {
|
||||
core::ptr::null_mut()
|
||||
}
|
||||
|
||||
pub unsafe fn cleanup(_ptr: *mut u8) -> Box<dyn Any + Send> {
|
||||
intrinsics::abort()
|
||||
}
|
||||
|
@ -48,15 +48,11 @@ static EXCEPTION_TYPE_INFO: TypeInfo = TypeInfo {
|
||||
name: b"rust_panic\0".as_ptr(),
|
||||
};
|
||||
|
||||
pub fn payload() -> *mut u8 {
|
||||
ptr::null_mut()
|
||||
}
|
||||
|
||||
struct Exception {
|
||||
// This needs to be an Option because the object's lifetime follows C++
|
||||
// semantics: when catch_unwind moves the Box out of the exception it must
|
||||
// still leave the exception object in a valid state because its destructor
|
||||
// is still going to be called by __cxa_end_catch..
|
||||
// is still going to be called by __cxa_end_catch.
|
||||
data: Option<Box<dyn Any + Send>>,
|
||||
}
|
||||
|
||||
@ -98,7 +94,6 @@ extern "C" fn exception_cleanup(ptr: *mut libc::c_void) -> DestructorRet {
|
||||
}
|
||||
|
||||
#[lang = "eh_personality"]
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn rust_eh_personality(
|
||||
version: c_int,
|
||||
actions: uw::_Unwind_Action,
|
||||
|
@ -35,20 +35,11 @@
|
||||
//!
|
||||
//! Once stack has been unwound down to the handler frame level, unwinding stops
|
||||
//! and the last personality routine transfers control to the catch block.
|
||||
//!
|
||||
//! ## `eh_personality` and `eh_unwind_resume`
|
||||
//!
|
||||
//! These language items are used by the compiler when generating unwind info.
|
||||
//! The first one is the personality routine described above. The second one
|
||||
//! allows compilation target to customize the process of resuming unwind at the
|
||||
//! end of the landing pads. `eh_unwind_resume` is used only if
|
||||
//! `custom_unwind_resume` flag in the target options is set.
|
||||
|
||||
#![allow(private_no_mangle_fns)]
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use core::any::Any;
|
||||
use core::ptr;
|
||||
|
||||
use crate::dwarf::eh::{self, EHAction, EHContext};
|
||||
use libc::{c_int, uintptr_t};
|
||||
@ -83,10 +74,6 @@ pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn payload() -> *mut u8 {
|
||||
ptr::null_mut()
|
||||
}
|
||||
|
||||
pub unsafe fn cleanup(ptr: *mut u8) -> Box<dyn Any + Send> {
|
||||
let exception = Box::from_raw(ptr as *mut Exception);
|
||||
exception.cause
|
||||
@ -143,7 +130,6 @@ cfg_if::cfg_if! {
|
||||
//
|
||||
// iOS uses the default routine instead since it uses SjLj unwinding.
|
||||
#[lang = "eh_personality"]
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn rust_eh_personality(state: uw::_Unwind_State,
|
||||
exception_object: *mut uw::_Unwind_Exception,
|
||||
context: *mut uw::_Unwind_Context)
|
||||
@ -277,7 +263,6 @@ cfg_if::cfg_if! {
|
||||
// On x86_64 MinGW targets, the unwinding mechanism is SEH however the unwind
|
||||
// handler data (aka LSDA) uses GCC-compatible encoding.
|
||||
#[lang = "eh_personality"]
|
||||
#[no_mangle]
|
||||
#[allow(nonstandard_style)]
|
||||
unsafe extern "C" fn rust_eh_personality(exceptionRecord: *mut uw::EXCEPTION_RECORD,
|
||||
establisherFrame: uw::LPVOID,
|
||||
@ -293,7 +278,6 @@ cfg_if::cfg_if! {
|
||||
} else {
|
||||
// The personality routine for most of our targets.
|
||||
#[lang = "eh_personality"]
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn rust_eh_personality(version: c_int,
|
||||
actions: uw::_Unwind_Action,
|
||||
exception_class: uw::_Unwind_Exception_Class,
|
||||
@ -329,8 +313,8 @@ unsafe fn find_eh_action(
|
||||
eh::find_eh_action(lsda, &eh_context, foreign_exception)
|
||||
}
|
||||
|
||||
// See docs in the `unwind` module.
|
||||
#[cfg(all(
|
||||
bootstrap,
|
||||
target_os = "windows",
|
||||
any(target_arch = "x86", target_arch = "x86_64"),
|
||||
target_env = "gnu"
|
||||
@ -364,12 +348,12 @@ pub mod eh_frame_registry {
|
||||
fn __deregister_frame_info(eh_frame_begin: *const u8, object: *mut u8);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[rustc_std_internal_symbol]
|
||||
pub unsafe extern "C" fn rust_eh_register_frames(eh_frame_begin: *const u8, object: *mut u8) {
|
||||
__register_frame_info(eh_frame_begin, object);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[rustc_std_internal_symbol]
|
||||
pub unsafe extern "C" fn rust_eh_unregister_frames(eh_frame_begin: *const u8, object: *mut u8) {
|
||||
__deregister_frame_info(eh_frame_begin, object);
|
||||
}
|
||||
|
@ -6,10 +6,6 @@ use alloc::boxed::Box;
|
||||
use core::any::Any;
|
||||
use core::ptr;
|
||||
|
||||
pub fn payload() -> *mut u8 {
|
||||
ptr::null_mut()
|
||||
}
|
||||
|
||||
pub unsafe fn cleanup(_ptr: *mut u8) -> Box<dyn Any + Send> {
|
||||
extern "C" {
|
||||
pub fn __rust_abort() -> !;
|
||||
|
@ -22,19 +22,18 @@
|
||||
#![feature(libc)]
|
||||
#![feature(nll)]
|
||||
#![feature(panic_unwind)]
|
||||
#![feature(raw)]
|
||||
#![feature(staged_api)]
|
||||
#![feature(std_internals)]
|
||||
#![feature(unwind_attributes)]
|
||||
#![feature(abi_thiscall)]
|
||||
#![feature(rustc_attrs)]
|
||||
#![feature(raw)]
|
||||
#![panic_runtime]
|
||||
#![feature(panic_runtime)]
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use core::intrinsics;
|
||||
use core::mem;
|
||||
use core::any::Any;
|
||||
use core::panic::BoxMeUp;
|
||||
use core::raw;
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(target_os = "emscripten")] {
|
||||
@ -69,33 +68,14 @@ extern "C" {
|
||||
|
||||
mod dwarf;
|
||||
|
||||
// Entry point for catching an exception, implemented using the `try` intrinsic
|
||||
// in the compiler.
|
||||
//
|
||||
// The interaction between the `payload` function and the compiler is pretty
|
||||
// hairy and tightly coupled, for more information see the compiler's
|
||||
// implementation of this.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn __rust_maybe_catch_panic(
|
||||
f: fn(*mut u8),
|
||||
data: *mut u8,
|
||||
data_ptr: *mut usize,
|
||||
vtable_ptr: *mut usize,
|
||||
) -> u32 {
|
||||
let mut payload = imp::payload();
|
||||
if intrinsics::r#try(f, data, &mut payload as *mut _ as *mut _) == 0 {
|
||||
0
|
||||
} else {
|
||||
let obj = mem::transmute::<_, raw::TraitObject>(imp::cleanup(payload));
|
||||
*data_ptr = obj.data as usize;
|
||||
*vtable_ptr = obj.vtable as usize;
|
||||
1
|
||||
}
|
||||
#[rustc_std_internal_symbol]
|
||||
pub unsafe extern "C" fn __rust_panic_cleanup(payload: *mut u8) -> *mut (dyn Any + Send + 'static) {
|
||||
Box::into_raw(imp::cleanup(payload))
|
||||
}
|
||||
|
||||
// Entry point for raising an exception, just delegates to the platform-specific
|
||||
// implementation.
|
||||
#[no_mangle]
|
||||
#[rustc_std_internal_symbol]
|
||||
#[unwind(allowed)]
|
||||
pub unsafe extern "C" fn __rust_start_panic(payload: usize) -> u32 {
|
||||
let payload = payload as *mut &mut dyn BoxMeUp;
|
||||
|
@ -49,10 +49,17 @@
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use core::any::Any;
|
||||
use core::mem;
|
||||
use core::raw;
|
||||
use core::mem::{self, ManuallyDrop};
|
||||
use libc::{c_int, c_uint, c_void};
|
||||
|
||||
struct Exception {
|
||||
// This needs to be an Option because we catch the exception by reference
|
||||
// and its destructor is executed by the C++ runtime. When we take the Box
|
||||
// out of the exception, we need to leave the exception in a valid state
|
||||
// for its destructor to run without double-dropping the Box.
|
||||
data: Option<Box<dyn Any + Send>>,
|
||||
}
|
||||
|
||||
// First up, a whole bunch of type definitions. There's a few platform-specific
|
||||
// oddities here, and a lot that's just blatantly copied from LLVM. The purpose
|
||||
// of all this is to implement the `panic` function below through a call to
|
||||
@ -167,6 +174,9 @@ pub struct _TypeDescriptor {
|
||||
|
||||
// Note that we intentionally ignore name mangling rules here: we don't want C++
|
||||
// to be able to catch Rust panics by simply declaring a `struct rust_panic`.
|
||||
//
|
||||
// When modifying, make sure that the type name string exactly matches
|
||||
// the one used in src/librustc_codegen_llvm/intrinsic.rs.
|
||||
const TYPE_NAME: [u8; 11] = *b"rust_panic\0";
|
||||
|
||||
static mut THROW_INFO: _ThrowInfo = _ThrowInfo {
|
||||
@ -183,7 +193,7 @@ static mut CATCHABLE_TYPE: _CatchableType = _CatchableType {
|
||||
properties: 0,
|
||||
pType: ptr!(0),
|
||||
thisDisplacement: _PMD { mdisp: 0, pdisp: -1, vdisp: 0 },
|
||||
sizeOrOffset: mem::size_of::<[u64; 2]>() as c_int,
|
||||
sizeOrOffset: mem::size_of::<Exception>() as c_int,
|
||||
copyFunction: ptr!(0),
|
||||
};
|
||||
|
||||
@ -199,12 +209,12 @@ extern "C" {
|
||||
static TYPE_INFO_VTABLE: *const u8;
|
||||
}
|
||||
|
||||
// We use #[lang = "eh_catch_typeinfo"] here as this is the type descriptor which
|
||||
// we'll use in LLVM's `catchpad` instruction which ends up also being passed as
|
||||
// an argument to the C++ personality function.
|
||||
// This type descriptor is only used when throwing an exception. The catch part
|
||||
// is handled by the try intrinsic, which generates its own TypeDescriptor.
|
||||
//
|
||||
// Again, I'm not entirely sure what this is describing, it just seems to work.
|
||||
#[cfg_attr(not(test), lang = "eh_catch_typeinfo")]
|
||||
// This is fine since the MSVC runtime uses string comparison on the type name
|
||||
// to match TypeDescriptors rather than pointer equality.
|
||||
#[cfg_attr(bootstrap, lang = "eh_catch_typeinfo")]
|
||||
static mut TYPE_DESCRIPTOR: _TypeDescriptor = _TypeDescriptor {
|
||||
pVFTable: unsafe { &TYPE_INFO_VTABLE } as *const _ as *const _,
|
||||
spare: core::ptr::null_mut(),
|
||||
@ -226,16 +236,16 @@ static mut TYPE_DESCRIPTOR: _TypeDescriptor = _TypeDescriptor {
|
||||
// because Box<dyn Any> isn't clonable.
|
||||
macro_rules! define_cleanup {
|
||||
($abi:tt) => {
|
||||
unsafe extern $abi fn exception_cleanup(e: *mut [u64; 2]) {
|
||||
if (*e)[0] != 0 {
|
||||
cleanup(*e);
|
||||
unsafe extern $abi fn exception_cleanup(e: *mut Exception) {
|
||||
if let Exception { data: Some(b) } = e.read() {
|
||||
drop(b);
|
||||
super::__rust_drop_panic();
|
||||
}
|
||||
}
|
||||
#[unwind(allowed)]
|
||||
unsafe extern $abi fn exception_copy(_dest: *mut [u64; 2],
|
||||
_src: *mut [u64; 2])
|
||||
-> *mut [u64; 2] {
|
||||
unsafe extern $abi fn exception_copy(_dest: *mut Exception,
|
||||
_src: *mut Exception)
|
||||
-> *mut Exception {
|
||||
panic!("Rust panics cannot be copied");
|
||||
}
|
||||
}
|
||||
@ -255,12 +265,11 @@ pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
|
||||
// need to otherwise transfer `data` to the heap. We just pass a stack
|
||||
// pointer to this function.
|
||||
//
|
||||
// The first argument is the payload being thrown (our two pointers), and
|
||||
// the second argument is the type information object describing the
|
||||
// exception (constructed above).
|
||||
let ptrs = mem::transmute::<_, raw::TraitObject>(data);
|
||||
let mut ptrs = [ptrs.data as u64, ptrs.vtable as u64];
|
||||
let throw_ptr = ptrs.as_mut_ptr() as *mut _;
|
||||
// The ManuallyDrop is needed here since we don't want Exception to be
|
||||
// dropped when unwinding. Instead it will be dropped by exception_cleanup
|
||||
// which is invoked by the C++ runtime.
|
||||
let mut exception = ManuallyDrop::new(Exception { data: Some(data) });
|
||||
let throw_ptr = &mut exception as *mut _ as *mut _;
|
||||
|
||||
// This... may seems surprising, and justifiably so. On 32-bit MSVC the
|
||||
// pointers between these structure are just that, pointers. On 64-bit MSVC,
|
||||
@ -308,12 +317,9 @@ pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
|
||||
_CxxThrowException(throw_ptr, &mut THROW_INFO as *mut _ as *mut _);
|
||||
}
|
||||
|
||||
pub fn payload() -> [u64; 2] {
|
||||
[0; 2]
|
||||
}
|
||||
|
||||
pub unsafe fn cleanup(payload: [u64; 2]) -> Box<dyn Any + Send> {
|
||||
mem::transmute(raw::TraitObject { data: payload[0] as *mut _, vtable: payload[1] as *mut _ })
|
||||
pub unsafe fn cleanup(payload: *mut u8) -> Box<dyn Any + Send> {
|
||||
let exception = &mut *(payload as *mut Exception);
|
||||
exception.data.take().unwrap()
|
||||
}
|
||||
|
||||
// This is required by the compiler to exist (e.g., it's a lang item), but
|
||||
|
@ -57,8 +57,7 @@ pub fn whitelisted(tcx: TyCtxt<'_>, lang_item: LangItem) -> bool {
|
||||
// symbols. Other panic runtimes ensure that the relevant symbols are
|
||||
// available to link things together, but they're never exercised.
|
||||
if tcx.sess.panic_strategy() != PanicStrategy::Unwind {
|
||||
return lang_item == LangItem::EhPersonalityLangItem
|
||||
|| lang_item == LangItem::EhUnwindResumeLangItem;
|
||||
return lang_item == LangItem::EhPersonalityLangItem;
|
||||
}
|
||||
|
||||
false
|
||||
|
@ -1,4 +1,3 @@
|
||||
use crate::abi::FnAbi;
|
||||
use crate::attributes;
|
||||
use crate::debuginfo;
|
||||
use crate::llvm;
|
||||
@ -15,7 +14,7 @@ use rustc::mir::mono::CodegenUnit;
|
||||
use rustc::session::config::{self, CFGuard, DebugInfo};
|
||||
use rustc::session::Session;
|
||||
use rustc::ty::layout::{
|
||||
FnAbiExt, HasParamEnv, LayoutError, LayoutOf, PointeeInfo, Size, TyLayout, VariantIdx,
|
||||
HasParamEnv, LayoutError, LayoutOf, PointeeInfo, Size, TyLayout, VariantIdx,
|
||||
};
|
||||
use rustc::ty::{self, Instance, Ty, TyCtxt};
|
||||
use rustc_codegen_ssa::base::wants_msvc_seh;
|
||||
@ -23,15 +22,12 @@ use rustc_data_structures::base_n;
|
||||
use rustc_data_structures::const_cstr;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::small_c_str::SmallCStr;
|
||||
use rustc_hir::Unsafety;
|
||||
use rustc_target::spec::{HasTargetSpec, Target};
|
||||
|
||||
use crate::abi::Abi;
|
||||
use rustc_span::source_map::{Span, DUMMY_SP};
|
||||
use rustc_span::symbol::Symbol;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::ffi::CStr;
|
||||
use std::iter;
|
||||
use std::str;
|
||||
use std::sync::Arc;
|
||||
|
||||
@ -87,7 +83,6 @@ pub struct CodegenCx<'ll, 'tcx> {
|
||||
pub dbg_cx: Option<debuginfo::CrateDebugContext<'ll, 'tcx>>,
|
||||
|
||||
eh_personality: Cell<Option<&'ll Value>>,
|
||||
eh_unwind_resume: Cell<Option<&'ll Value>>,
|
||||
pub rust_try_fn: Cell<Option<&'ll Value>>,
|
||||
|
||||
intrinsics: RefCell<FxHashMap<&'static str, &'ll Value>>,
|
||||
@ -327,7 +322,6 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
|
||||
isize_ty,
|
||||
dbg_cx,
|
||||
eh_personality: Cell::new(None),
|
||||
eh_unwind_resume: Cell::new(None),
|
||||
rust_try_fn: Cell::new(None),
|
||||
intrinsics: Default::default(),
|
||||
local_gen_sym_counter: Cell::new(0),
|
||||
@ -405,45 +399,6 @@ impl MiscMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
||||
llfn
|
||||
}
|
||||
|
||||
// Returns a Value of the "eh_unwind_resume" lang item if one is defined,
|
||||
// otherwise declares it as an external function.
|
||||
fn eh_unwind_resume(&self) -> &'ll Value {
|
||||
let unwresume = &self.eh_unwind_resume;
|
||||
if let Some(llfn) = unwresume.get() {
|
||||
return llfn;
|
||||
}
|
||||
|
||||
let tcx = self.tcx;
|
||||
assert!(self.sess().target.target.options.custom_unwind_resume);
|
||||
if let Some(def_id) = tcx.lang_items().eh_unwind_resume() {
|
||||
let llfn = self.get_fn_addr(
|
||||
ty::Instance::resolve(
|
||||
tcx,
|
||||
ty::ParamEnv::reveal_all(),
|
||||
def_id,
|
||||
tcx.intern_substs(&[]),
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
unwresume.set(Some(llfn));
|
||||
return llfn;
|
||||
}
|
||||
|
||||
let sig = ty::Binder::bind(tcx.mk_fn_sig(
|
||||
iter::once(tcx.mk_mut_ptr(tcx.types.u8)),
|
||||
tcx.types.never,
|
||||
false,
|
||||
Unsafety::Unsafe,
|
||||
Abi::C,
|
||||
));
|
||||
|
||||
let fn_abi = FnAbi::of_fn_ptr(self, sig, &[]);
|
||||
let llfn = self.declare_fn("rust_eh_unwind_resume", &fn_abi);
|
||||
attributes::apply_target_cpu_attr(self, llfn);
|
||||
unwresume.set(Some(llfn));
|
||||
llfn
|
||||
}
|
||||
|
||||
fn sess(&self) -> &Session {
|
||||
&self.tcx.sess
|
||||
}
|
||||
|
@ -851,19 +851,21 @@ fn memset_intrinsic(
|
||||
|
||||
fn try_intrinsic(
|
||||
bx: &mut Builder<'a, 'll, 'tcx>,
|
||||
func: &'ll Value,
|
||||
try_func: &'ll Value,
|
||||
data: &'ll Value,
|
||||
local_ptr: &'ll Value,
|
||||
catch_func: &'ll Value,
|
||||
dest: &'ll Value,
|
||||
) {
|
||||
if bx.sess().no_landing_pads() {
|
||||
bx.call(func, &[data], None);
|
||||
let ptr_align = bx.tcx().data_layout.pointer_align.abi;
|
||||
bx.store(bx.const_null(bx.type_i8p()), dest, ptr_align);
|
||||
bx.call(try_func, &[data], None);
|
||||
// Return 0 unconditionally from the intrinsic call;
|
||||
// we can never unwind.
|
||||
let ret_align = bx.tcx().data_layout.i32_align.abi;
|
||||
bx.store(bx.const_i32(0), dest, ret_align);
|
||||
} else if wants_msvc_seh(bx.sess()) {
|
||||
codegen_msvc_try(bx, func, data, local_ptr, dest);
|
||||
codegen_msvc_try(bx, try_func, data, catch_func, dest);
|
||||
} else {
|
||||
codegen_gnu_try(bx, func, data, local_ptr, dest);
|
||||
codegen_gnu_try(bx, try_func, data, catch_func, dest);
|
||||
}
|
||||
}
|
||||
|
||||
@ -876,9 +878,9 @@ fn try_intrinsic(
|
||||
// as the old ones are still more optimized.
|
||||
fn codegen_msvc_try(
|
||||
bx: &mut Builder<'a, 'll, 'tcx>,
|
||||
func: &'ll Value,
|
||||
try_func: &'ll Value,
|
||||
data: &'ll Value,
|
||||
local_ptr: &'ll Value,
|
||||
catch_func: &'ll Value,
|
||||
dest: &'ll Value,
|
||||
) {
|
||||
let llfn = get_rust_try_fn(bx, &mut |mut bx| {
|
||||
@ -890,15 +892,15 @@ fn codegen_msvc_try(
|
||||
let mut catchpad = bx.build_sibling_block("catchpad");
|
||||
let mut caught = bx.build_sibling_block("caught");
|
||||
|
||||
let func = llvm::get_param(bx.llfn(), 0);
|
||||
let try_func = llvm::get_param(bx.llfn(), 0);
|
||||
let data = llvm::get_param(bx.llfn(), 1);
|
||||
let local_ptr = llvm::get_param(bx.llfn(), 2);
|
||||
let catch_func = llvm::get_param(bx.llfn(), 2);
|
||||
|
||||
// We're generating an IR snippet that looks like:
|
||||
//
|
||||
// declare i32 @rust_try(%func, %data, %ptr) {
|
||||
// %slot = alloca [2 x i64]
|
||||
// invoke %func(%data) to label %normal unwind label %catchswitch
|
||||
// declare i32 @rust_try(%try_func, %data, %catch_func) {
|
||||
// %slot = alloca u8*
|
||||
// invoke %try_func(%data) to label %normal unwind label %catchswitch
|
||||
//
|
||||
// normal:
|
||||
// ret i32 0
|
||||
@ -908,8 +910,8 @@ fn codegen_msvc_try(
|
||||
//
|
||||
// catchpad:
|
||||
// %tok = catchpad within %cs [%type_descriptor, 0, %slot]
|
||||
// %ptr[0] = %slot[0]
|
||||
// %ptr[1] = %slot[1]
|
||||
// %ptr = load %slot
|
||||
// call %catch_func(%data, %ptr)
|
||||
// catchret from %tok to label %caught
|
||||
//
|
||||
// caught:
|
||||
@ -926,32 +928,57 @@ fn codegen_msvc_try(
|
||||
// ~rust_panic();
|
||||
//
|
||||
// uint64_t x[2];
|
||||
// }
|
||||
// };
|
||||
//
|
||||
// int bar(void (*foo)(void), uint64_t *ret) {
|
||||
// int __rust_try(
|
||||
// void (*try_func)(void*),
|
||||
// void *data,
|
||||
// void (*catch_func)(void*, void*) noexcept
|
||||
// ) {
|
||||
// try {
|
||||
// foo();
|
||||
// try_func(data);
|
||||
// return 0;
|
||||
// } catch(rust_panic& a) {
|
||||
// ret[0] = a.x[0];
|
||||
// ret[1] = a.x[1];
|
||||
// a.x[0] = 0;
|
||||
// catch_func(data, &a);
|
||||
// return 1;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// More information can be found in libstd's seh.rs implementation.
|
||||
let i64_2 = bx.type_array(bx.type_i64(), 2);
|
||||
let i64_2_ptr = bx.type_ptr_to(i64_2);
|
||||
let ptr_align = bx.tcx().data_layout.pointer_align.abi;
|
||||
let slot = bx.alloca(i64_2_ptr, ptr_align);
|
||||
bx.invoke(func, &[data], normal.llbb(), catchswitch.llbb(), None);
|
||||
let slot = bx.alloca(bx.type_i8p(), ptr_align);
|
||||
bx.invoke(try_func, &[data], normal.llbb(), catchswitch.llbb(), None);
|
||||
|
||||
normal.ret(bx.const_i32(0));
|
||||
|
||||
let cs = catchswitch.catch_switch(None, None, 1);
|
||||
catchswitch.add_handler(cs, catchpad.llbb());
|
||||
|
||||
// We can't use the TypeDescriptor defined in libpanic_unwind because it
|
||||
// might be in another DLL and the SEH encoding only supports specifying
|
||||
// a TypeDescriptor from the current module.
|
||||
//
|
||||
// However this isn't an issue since the MSVC runtime uses string
|
||||
// comparison on the type name to match TypeDescriptors rather than
|
||||
// pointer equality.
|
||||
//
|
||||
// So instead we generate a new TypeDescriptor in each module that uses
|
||||
// `try` and let the linker merge duplicate definitions in the same
|
||||
// module.
|
||||
//
|
||||
// When modifying, make sure that the type_name string exactly matches
|
||||
// the one used in src/libpanic_unwind/seh.rs.
|
||||
let type_info_vtable = bx.declare_global("??_7type_info@@6B@", bx.type_i8p());
|
||||
let type_name = bx.const_bytes(b"rust_panic\0");
|
||||
let type_info =
|
||||
bx.const_struct(&[type_info_vtable, bx.const_null(bx.type_i8p()), type_name], false);
|
||||
let tydesc = bx.declare_global("__rust_panic_type_info", bx.val_ty(type_info));
|
||||
unsafe {
|
||||
llvm::LLVMRustSetLinkage(tydesc, llvm::Linkage::LinkOnceODRLinkage);
|
||||
llvm::SetUniqueComdat(bx.llmod, tydesc);
|
||||
llvm::LLVMSetInitializer(tydesc, type_info);
|
||||
}
|
||||
|
||||
// The flag value of 8 indicates that we are catching the exception by
|
||||
// reference instead of by value. We can't use catch by value because
|
||||
// that requires copying the exception object, which we don't support
|
||||
@ -959,23 +986,9 @@ fn codegen_msvc_try(
|
||||
//
|
||||
// Source: MicrosoftCXXABI::getAddrOfCXXCatchHandlerType in clang
|
||||
let flags = bx.const_i32(8);
|
||||
let tydesc = match bx.tcx().lang_items().eh_catch_typeinfo() {
|
||||
Some(did) => bx.get_static(did),
|
||||
None => bug!("eh_catch_typeinfo not defined, but needed for SEH unwinding"),
|
||||
};
|
||||
let funclet = catchpad.catch_pad(cs, &[tydesc, flags, slot]);
|
||||
|
||||
let i64_align = bx.tcx().data_layout.i64_align.abi;
|
||||
let payload_ptr = catchpad.load(slot, ptr_align);
|
||||
let payload = catchpad.load(payload_ptr, i64_align);
|
||||
let local_ptr = catchpad.bitcast(local_ptr, bx.type_ptr_to(i64_2));
|
||||
catchpad.store(payload, local_ptr, i64_align);
|
||||
|
||||
// Clear the first word of the exception so avoid double-dropping it.
|
||||
// This will be read by the destructor which is implicitly called at the
|
||||
// end of the catch block by the runtime.
|
||||
let payload_0_ptr = catchpad.inbounds_gep(payload_ptr, &[bx.const_i32(0), bx.const_i32(0)]);
|
||||
catchpad.store(bx.const_u64(0), payload_0_ptr, i64_align);
|
||||
let ptr = catchpad.load(slot, ptr_align);
|
||||
catchpad.call(catch_func, &[data, ptr], Some(&funclet));
|
||||
|
||||
catchpad.catch_ret(&funclet, caught.llbb());
|
||||
|
||||
@ -984,7 +997,7 @@ fn codegen_msvc_try(
|
||||
|
||||
// Note that no invoke is used here because by definition this function
|
||||
// can't panic (that's what it's catching).
|
||||
let ret = bx.call(llfn, &[func, data, local_ptr], None);
|
||||
let ret = bx.call(llfn, &[try_func, data, catch_func], None);
|
||||
let i32_align = bx.tcx().data_layout.i32_align.abi;
|
||||
bx.store(ret, dest, i32_align);
|
||||
}
|
||||
@ -1002,38 +1015,34 @@ fn codegen_msvc_try(
|
||||
// the right personality function.
|
||||
fn codegen_gnu_try(
|
||||
bx: &mut Builder<'a, 'll, 'tcx>,
|
||||
func: &'ll Value,
|
||||
try_func: &'ll Value,
|
||||
data: &'ll Value,
|
||||
local_ptr: &'ll Value,
|
||||
catch_func: &'ll Value,
|
||||
dest: &'ll Value,
|
||||
) {
|
||||
let llfn = get_rust_try_fn(bx, &mut |mut bx| {
|
||||
// Codegens the shims described above:
|
||||
//
|
||||
// bx:
|
||||
// invoke %func(%args...) normal %normal unwind %catch
|
||||
// invoke %try_func(%data) normal %normal unwind %catch
|
||||
//
|
||||
// normal:
|
||||
// ret 0
|
||||
//
|
||||
// catch:
|
||||
// (ptr, _) = landingpad
|
||||
// store ptr, %local_ptr
|
||||
// (%ptr, _) = landingpad
|
||||
// call %catch_func(%data, %ptr)
|
||||
// ret 1
|
||||
//
|
||||
// Note that the `local_ptr` data passed into the `try` intrinsic is
|
||||
// expected to be `*mut *mut u8` for this to actually work, but that's
|
||||
// managed by the standard library.
|
||||
|
||||
bx.sideeffect();
|
||||
|
||||
let mut then = bx.build_sibling_block("then");
|
||||
let mut catch = bx.build_sibling_block("catch");
|
||||
|
||||
let func = llvm::get_param(bx.llfn(), 0);
|
||||
let try_func = llvm::get_param(bx.llfn(), 0);
|
||||
let data = llvm::get_param(bx.llfn(), 1);
|
||||
let local_ptr = llvm::get_param(bx.llfn(), 2);
|
||||
bx.invoke(func, &[data], then.llbb(), catch.llbb(), None);
|
||||
let catch_func = llvm::get_param(bx.llfn(), 2);
|
||||
bx.invoke(try_func, &[data], then.llbb(), catch.llbb(), None);
|
||||
then.ret(bx.const_i32(0));
|
||||
|
||||
// Type indicator for the exception being thrown.
|
||||
@ -1053,15 +1062,13 @@ fn codegen_gnu_try(
|
||||
};
|
||||
catch.add_clause(vals, tydesc);
|
||||
let ptr = catch.extract_value(vals, 0);
|
||||
let ptr_align = bx.tcx().data_layout.pointer_align.abi;
|
||||
let bitcast = catch.bitcast(local_ptr, bx.type_ptr_to(bx.type_i8p()));
|
||||
catch.store(ptr, bitcast, ptr_align);
|
||||
catch.call(catch_func, &[data, ptr], None);
|
||||
catch.ret(bx.const_i32(1));
|
||||
});
|
||||
|
||||
// Note that no invoke is used here because by definition this function
|
||||
// can't panic (that's what it's catching).
|
||||
let ret = bx.call(llfn, &[func, data, local_ptr], None);
|
||||
let ret = bx.call(llfn, &[try_func, data, catch_func], None);
|
||||
let i32_align = bx.tcx().data_layout.i32_align.abi;
|
||||
bx.store(ret, dest, i32_align);
|
||||
}
|
||||
@ -1084,6 +1091,8 @@ fn gen_fn<'ll, 'tcx>(
|
||||
));
|
||||
let fn_abi = FnAbi::of_fn_ptr(cx, rust_fn_sig, &[]);
|
||||
let llfn = cx.declare_fn(name, &fn_abi);
|
||||
cx.set_frame_pointer_elimination(llfn);
|
||||
cx.apply_target_cpu_attr(llfn);
|
||||
// FIXME(eddyb) find a nicer way to do this.
|
||||
unsafe { llvm::LLVMRustSetLinkage(llfn, llvm::Linkage::InternalLinkage) };
|
||||
let bx = Builder::new_block(cx, llfn, "entry-block");
|
||||
@ -1106,15 +1115,22 @@ fn get_rust_try_fn<'ll, 'tcx>(
|
||||
// Define the type up front for the signature of the rust_try function.
|
||||
let tcx = cx.tcx;
|
||||
let i8p = tcx.mk_mut_ptr(tcx.types.i8);
|
||||
let fn_ty = tcx.mk_fn_ptr(ty::Binder::bind(tcx.mk_fn_sig(
|
||||
let try_fn_ty = tcx.mk_fn_ptr(ty::Binder::bind(tcx.mk_fn_sig(
|
||||
iter::once(i8p),
|
||||
tcx.mk_unit(),
|
||||
false,
|
||||
hir::Unsafety::Unsafe,
|
||||
Abi::Rust,
|
||||
)));
|
||||
let catch_fn_ty = tcx.mk_fn_ptr(ty::Binder::bind(tcx.mk_fn_sig(
|
||||
[i8p, i8p].iter().cloned(),
|
||||
tcx.mk_unit(),
|
||||
false,
|
||||
hir::Unsafety::Unsafe,
|
||||
Abi::Rust,
|
||||
)));
|
||||
let output = tcx.types.i32;
|
||||
let rust_try = gen_fn(cx, "__rust_try", vec![fn_ty, i8p, i8p], output, codegen);
|
||||
let rust_try = gen_fn(cx, "__rust_try", vec![try_fn_ty, i8p, catch_fn_ty], output, codegen);
|
||||
cx.rust_try_fn.set(Some(rust_try));
|
||||
rust_try
|
||||
}
|
||||
|
@ -490,6 +490,11 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>(
|
||||
info!("preparing {:?} to {:?}", crate_type, out_filename);
|
||||
let (linker, flavor) = linker_and_flavor(sess);
|
||||
|
||||
let any_dynamic_crate = crate_type == config::CrateType::Dylib
|
||||
|| codegen_results.crate_info.dependency_formats.iter().any(|(ty, list)| {
|
||||
*ty == crate_type && list.iter().any(|&linkage| linkage == Linkage::Dynamic)
|
||||
});
|
||||
|
||||
// The invocations of cc share some flags across platforms
|
||||
let (pname, mut cmd) = get_linker(sess, &linker, flavor);
|
||||
|
||||
@ -555,6 +560,15 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>(
|
||||
if let Some(args) = sess.target.target.options.late_link_args.get(&flavor) {
|
||||
cmd.args(args);
|
||||
}
|
||||
if any_dynamic_crate {
|
||||
if let Some(args) = sess.target.target.options.late_link_args_dynamic.get(&flavor) {
|
||||
cmd.args(args);
|
||||
}
|
||||
} else {
|
||||
if let Some(args) = sess.target.target.options.late_link_args_static.get(&flavor) {
|
||||
cmd.args(args);
|
||||
}
|
||||
}
|
||||
for obj in &sess.target.target.options.post_link_objects {
|
||||
cmd.arg(get_file_path(sess, obj));
|
||||
}
|
||||
|
@ -178,15 +178,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||
let lp1 = bx.load_operand(lp1).immediate();
|
||||
slot.storage_dead(&mut bx);
|
||||
|
||||
if !bx.sess().target.target.options.custom_unwind_resume {
|
||||
let mut lp = bx.const_undef(self.landing_pad_type());
|
||||
lp = bx.insert_value(lp, lp0, 0);
|
||||
lp = bx.insert_value(lp, lp1, 1);
|
||||
bx.resume(lp);
|
||||
} else {
|
||||
bx.call(bx.eh_unwind_resume(), &[lp0], helper.funclet(self));
|
||||
bx.unreachable();
|
||||
}
|
||||
let mut lp = bx.const_undef(self.landing_pad_type());
|
||||
lp = bx.insert_value(lp, lp0, 0);
|
||||
lp = bx.insert_value(lp, lp1, 1);
|
||||
bx.resume(lp);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,6 @@ pub trait MiscMethods<'tcx>: BackendTypes {
|
||||
fn get_fn(&self, instance: Instance<'tcx>) -> Self::Function;
|
||||
fn get_fn_addr(&self, instance: Instance<'tcx>) -> Self::Value;
|
||||
fn eh_personality(&self) -> Self::Value;
|
||||
fn eh_unwind_resume(&self) -> Self::Value;
|
||||
fn sess(&self) -> &Session;
|
||||
fn codegen_unit(&self) -> &Arc<CodegenUnit<'tcx>>;
|
||||
fn used_statics(&self) -> &RefCell<Vec<Self::Value>>;
|
||||
|
@ -240,7 +240,6 @@ language_item_table! {
|
||||
StartFnLangItem, "start", start_fn, Target::Fn;
|
||||
|
||||
EhPersonalityLangItem, "eh_personality", eh_personality, Target::Fn;
|
||||
EhUnwindResumeLangItem, "eh_unwind_resume", eh_unwind_resume, Target::Fn;
|
||||
EhCatchTypeinfoLangItem, "eh_catch_typeinfo", eh_catch_typeinfo, Target::Static;
|
||||
|
||||
OwnedBoxLangItem, "owned_box", owned_box, Target::Struct;
|
||||
|
@ -43,6 +43,5 @@ impl LanguageItems {
|
||||
weak_lang_items! {
|
||||
panic_impl, PanicImplLangItem, rust_begin_unwind;
|
||||
eh_personality, EhPersonalityLangItem, rust_eh_personality;
|
||||
eh_unwind_resume, EhUnwindResumeLangItem, rust_eh_unwind_resume;
|
||||
oom, OomLangItem, rust_oom;
|
||||
}
|
||||
|
@ -28,9 +28,6 @@ pub fn check_crate<'tcx>(tcx: TyCtxt<'tcx>, items: &mut lang_items::LanguageItem
|
||||
if items.eh_personality().is_none() {
|
||||
items.missing.push(lang_items::EhPersonalityLangItem);
|
||||
}
|
||||
if tcx.sess.target.target.options.custom_unwind_resume & items.eh_unwind_resume().is_none() {
|
||||
items.missing.push(lang_items::EhUnwindResumeLangItem);
|
||||
}
|
||||
|
||||
{
|
||||
let mut cx = Context { tcx, items };
|
||||
|
@ -289,7 +289,6 @@ symbols! {
|
||||
dylib,
|
||||
dyn_trait,
|
||||
eh_personality,
|
||||
eh_unwind_resume,
|
||||
enable,
|
||||
Encodable,
|
||||
env,
|
||||
@ -665,7 +664,6 @@ symbols! {
|
||||
rustc_variance,
|
||||
rustfmt,
|
||||
rust_eh_personality,
|
||||
rust_eh_unwind_resume,
|
||||
rust_oom,
|
||||
rvalue_static_promotion,
|
||||
sanitize,
|
||||
|
@ -581,6 +581,12 @@ pub struct TargetOptions {
|
||||
/// user-defined but before post_link_objects. Standard platform
|
||||
/// libraries that should be always be linked to, usually go here.
|
||||
pub late_link_args: LinkArgs,
|
||||
/// Linker arguments used in addition to `late_link_args` if at least one
|
||||
/// Rust dependency is dynamically linked.
|
||||
pub late_link_args_dynamic: LinkArgs,
|
||||
/// Linker arguments used in addition to `late_link_args` if aall Rust
|
||||
/// dependencies are statically linked.
|
||||
pub late_link_args_static: LinkArgs,
|
||||
/// Objects to link after all others, always found within the
|
||||
/// sysroot folder.
|
||||
pub post_link_objects: Vec<String>, // ... unconditionally
|
||||
@ -694,11 +700,6 @@ pub struct TargetOptions {
|
||||
pub archive_format: String,
|
||||
/// Is asm!() allowed? Defaults to true.
|
||||
pub allow_asm: bool,
|
||||
/// Whether the target uses a custom unwind resumption routine.
|
||||
/// By default LLVM lowers `resume` instructions into calls to `_Unwind_Resume`
|
||||
/// defined in libgcc. If this option is enabled, the target must provide
|
||||
/// `eh_unwind_resume` lang item.
|
||||
pub custom_unwind_resume: bool,
|
||||
/// Whether the runtime startup code requires the `main` function be passed
|
||||
/// `argc` and `argv` values.
|
||||
pub main_needs_argc_argv: bool,
|
||||
@ -865,10 +866,11 @@ impl Default for TargetOptions {
|
||||
post_link_objects: Vec::new(),
|
||||
post_link_objects_crt: Vec::new(),
|
||||
late_link_args: LinkArgs::new(),
|
||||
late_link_args_dynamic: LinkArgs::new(),
|
||||
late_link_args_static: LinkArgs::new(),
|
||||
link_env: Vec::new(),
|
||||
link_env_remove: Vec::new(),
|
||||
archive_format: "gnu".to_string(),
|
||||
custom_unwind_resume: false,
|
||||
main_needs_argc_argv: true,
|
||||
allow_asm: true,
|
||||
has_elf_tls: false,
|
||||
@ -1144,6 +1146,8 @@ impl Target {
|
||||
key!(pre_link_objects_exe_crt, list);
|
||||
key!(pre_link_objects_dll, list);
|
||||
key!(late_link_args, link_args);
|
||||
key!(late_link_args_dynamic, link_args);
|
||||
key!(late_link_args_static, link_args);
|
||||
key!(post_link_objects, list);
|
||||
key!(post_link_objects_crt, list);
|
||||
key!(post_link_args, link_args);
|
||||
@ -1184,7 +1188,6 @@ impl Target {
|
||||
key!(relro_level, RelroLevel)?;
|
||||
key!(archive_format);
|
||||
key!(allow_asm, bool);
|
||||
key!(custom_unwind_resume, bool);
|
||||
key!(main_needs_argc_argv, bool);
|
||||
key!(has_elf_tls, bool);
|
||||
key!(obj_is_bitcode, bool);
|
||||
@ -1372,6 +1375,8 @@ impl ToJson for Target {
|
||||
target_option_val!(pre_link_objects_exe_crt);
|
||||
target_option_val!(pre_link_objects_dll);
|
||||
target_option_val!(link_args - late_link_args);
|
||||
target_option_val!(link_args - late_link_args_dynamic);
|
||||
target_option_val!(link_args - late_link_args_static);
|
||||
target_option_val!(post_link_objects);
|
||||
target_option_val!(post_link_objects_crt);
|
||||
target_option_val!(link_args - post_link_args);
|
||||
@ -1412,7 +1417,6 @@ impl ToJson for Target {
|
||||
target_option_val!(relro_level);
|
||||
target_option_val!(archive_format);
|
||||
target_option_val!(allow_asm);
|
||||
target_option_val!(custom_unwind_resume);
|
||||
target_option_val!(main_needs_argc_argv);
|
||||
target_option_val!(has_elf_tls);
|
||||
target_option_val!(obj_is_bitcode);
|
||||
|
@ -17,12 +17,13 @@ pub fn opts() -> TargetOptions {
|
||||
);
|
||||
|
||||
let mut late_link_args = LinkArgs::new();
|
||||
let mut late_link_args_dynamic = LinkArgs::new();
|
||||
let mut late_link_args_static = LinkArgs::new();
|
||||
late_link_args.insert(
|
||||
LinkerFlavor::Gcc,
|
||||
vec![
|
||||
"-lmingwex".to_string(),
|
||||
"-lmingw32".to_string(),
|
||||
"-lgcc".to_string(), // alas, mingw* libraries above depend on libgcc
|
||||
"-lmsvcrt".to_string(),
|
||||
// mingw's msvcrt is a weird hybrid import library and static library.
|
||||
// And it seems that the linker fails to use import symbols from msvcrt
|
||||
@ -37,6 +38,31 @@ pub fn opts() -> TargetOptions {
|
||||
"-lkernel32".to_string(),
|
||||
],
|
||||
);
|
||||
late_link_args_dynamic.insert(
|
||||
LinkerFlavor::Gcc,
|
||||
vec![
|
||||
// If any of our crates are dynamically linked then we need to use
|
||||
// the shared libgcc_s-dw2-1.dll. This is required to support
|
||||
// unwinding across DLL boundaries.
|
||||
"-lgcc_s".to_string(),
|
||||
"-lgcc".to_string(),
|
||||
"-lkernel32".to_string(),
|
||||
],
|
||||
);
|
||||
late_link_args_static.insert(
|
||||
LinkerFlavor::Gcc,
|
||||
vec![
|
||||
// If all of our crates are statically linked then we can get away
|
||||
// with statically linking the libgcc unwinding code. This allows
|
||||
// binaries to be redistributed without the libgcc_s-dw2-1.dll
|
||||
// dependency, but unfortunately break unwinding across DLL
|
||||
// boundaries when unwinding across FFI boundaries.
|
||||
"-lgcc".to_string(),
|
||||
"-lgcc_eh".to_string(),
|
||||
"-lpthread".to_string(),
|
||||
"-lkernel32".to_string(),
|
||||
],
|
||||
);
|
||||
|
||||
TargetOptions {
|
||||
// FIXME(#13846) this should be enabled for windows
|
||||
@ -63,8 +89,9 @@ pub fn opts() -> TargetOptions {
|
||||
"rsbegin.o".to_string(),
|
||||
],
|
||||
late_link_args,
|
||||
late_link_args_dynamic,
|
||||
late_link_args_static,
|
||||
post_link_objects: vec!["rsend.o".to_string()],
|
||||
custom_unwind_resume: true,
|
||||
abi_return_struct_as_int: true,
|
||||
emit_debug_gdb_scripts: false,
|
||||
requires_uwtable: true,
|
||||
|
@ -54,7 +54,6 @@ pub fn opts() -> TargetOptions {
|
||||
pre_link_objects_dll: vec!["rsbegin.o".to_string()],
|
||||
late_link_args,
|
||||
post_link_objects: vec!["rsend.o".to_string()],
|
||||
custom_unwind_resume: true,
|
||||
abi_return_struct_as_int: true,
|
||||
emit_debug_gdb_scripts: false,
|
||||
requires_uwtable: true,
|
||||
|
@ -299,14 +299,25 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
|
||||
|
||||
"try" => {
|
||||
let mut_u8 = tcx.mk_mut_ptr(tcx.types.u8);
|
||||
let fn_ty = ty::Binder::bind(tcx.mk_fn_sig(
|
||||
let try_fn_ty = ty::Binder::bind(tcx.mk_fn_sig(
|
||||
iter::once(mut_u8),
|
||||
tcx.mk_unit(),
|
||||
false,
|
||||
hir::Unsafety::Normal,
|
||||
Abi::Rust,
|
||||
));
|
||||
(0, vec![tcx.mk_fn_ptr(fn_ty), mut_u8, mut_u8], tcx.types.i32)
|
||||
let catch_fn_ty = ty::Binder::bind(tcx.mk_fn_sig(
|
||||
[mut_u8, mut_u8].iter().cloned(),
|
||||
tcx.mk_unit(),
|
||||
false,
|
||||
hir::Unsafety::Normal,
|
||||
Abi::Rust,
|
||||
));
|
||||
(
|
||||
0,
|
||||
vec![tcx.mk_fn_ptr(try_fn_ty), mut_u8, tcx.mk_fn_ptr(catch_fn_ty)],
|
||||
tcx.types.i32,
|
||||
)
|
||||
}
|
||||
|
||||
"va_start" | "va_end" => match mk_va_list_ty(hir::Mutability::Mut) {
|
||||
|
@ -14,7 +14,6 @@ use crate::fmt;
|
||||
use crate::intrinsics;
|
||||
use crate::mem::{self, ManuallyDrop};
|
||||
use crate::process;
|
||||
use crate::raw;
|
||||
use crate::sync::atomic::{AtomicBool, Ordering};
|
||||
use crate::sys::stdio::panic_output;
|
||||
use crate::sys_common::backtrace::{self, RustBacktrace};
|
||||
@ -41,12 +40,7 @@ use realstd::io::set_panic;
|
||||
// hook up these functions, but it is not this day!
|
||||
#[allow(improper_ctypes)]
|
||||
extern "C" {
|
||||
fn __rust_maybe_catch_panic(
|
||||
f: fn(*mut u8),
|
||||
data: *mut u8,
|
||||
data_ptr: *mut usize,
|
||||
vtable_ptr: *mut usize,
|
||||
) -> u32;
|
||||
fn __rust_panic_cleanup(payload: *mut u8) -> *mut (dyn Any + Send + 'static);
|
||||
|
||||
/// `payload` is actually a `*mut &mut dyn BoxMeUp` but that would cause FFI warnings.
|
||||
/// It cannot be `Box<dyn BoxMeUp>` because the other end of this call does not depend
|
||||
@ -247,12 +241,13 @@ pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>>
|
||||
union Data<F, R> {
|
||||
f: ManuallyDrop<F>,
|
||||
r: ManuallyDrop<R>,
|
||||
p: ManuallyDrop<Box<dyn Any + Send>>,
|
||||
}
|
||||
|
||||
// We do some sketchy operations with ownership here for the sake of
|
||||
// performance. We can only pass pointers down to
|
||||
// `__rust_maybe_catch_panic` (can't pass objects by value), so we do all
|
||||
// the ownership tracking here manually using a union.
|
||||
// performance. We can only pass pointers down to `do_call` (can't pass
|
||||
// objects by value), so we do all the ownership tracking here manually
|
||||
// using a union.
|
||||
//
|
||||
// We go through a transition where:
|
||||
//
|
||||
@ -263,7 +258,7 @@ pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>>
|
||||
// * If the closure successfully returns, we write the return value into the
|
||||
// data's return slot. Note that `ptr::write` is used as it's overwriting
|
||||
// uninitialized data.
|
||||
// * Finally, when we come back out of the `__rust_maybe_catch_panic` we're
|
||||
// * Finally, when we come back out of the `try` intrinsic we're
|
||||
// in one of two states:
|
||||
//
|
||||
// 1. The closure didn't panic, in which case the return value was
|
||||
@ -274,27 +269,59 @@ pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>>
|
||||
//
|
||||
// Once we stack all that together we should have the "most efficient'
|
||||
// method of calling a catch panic whilst juggling ownership.
|
||||
let mut any_data = 0;
|
||||
let mut any_vtable = 0;
|
||||
let mut data = Data { f: ManuallyDrop::new(f) };
|
||||
|
||||
let r = __rust_maybe_catch_panic(
|
||||
do_call::<F, R>,
|
||||
&mut data as *mut _ as *mut u8,
|
||||
&mut any_data,
|
||||
&mut any_vtable,
|
||||
);
|
||||
|
||||
return if r == 0 {
|
||||
let data_ptr = &mut data as *mut _ as *mut u8;
|
||||
return if do_try(do_call::<F, R>, data_ptr, do_catch::<F, R>) == 0 {
|
||||
Ok(ManuallyDrop::into_inner(data.r))
|
||||
} else {
|
||||
update_panic_count(-1);
|
||||
Err(mem::transmute(raw::TraitObject {
|
||||
data: any_data as *mut _,
|
||||
vtable: any_vtable as *mut _,
|
||||
}))
|
||||
Err(ManuallyDrop::into_inner(data.p))
|
||||
};
|
||||
|
||||
// Compatibility wrapper around the try intrinsic for bootstrap
|
||||
#[inline]
|
||||
unsafe fn do_try(try_fn: fn(*mut u8), data: *mut u8, catch_fn: fn(*mut u8, *mut u8)) -> i32 {
|
||||
#[cfg(not(bootstrap))]
|
||||
{
|
||||
intrinsics::r#try(try_fn, data, catch_fn)
|
||||
}
|
||||
#[cfg(bootstrap)]
|
||||
{
|
||||
use crate::mem::MaybeUninit;
|
||||
#[cfg(target_env = "msvc")]
|
||||
type TryPayload = [u64; 2];
|
||||
#[cfg(not(target_env = "msvc"))]
|
||||
type TryPayload = *mut u8;
|
||||
|
||||
let mut payload: MaybeUninit<TryPayload> = MaybeUninit::uninit();
|
||||
let payload_ptr = payload.as_mut_ptr() as *mut u8;
|
||||
let r = intrinsics::r#try(try_fn, data, payload_ptr);
|
||||
if r != 0 {
|
||||
#[cfg(target_env = "msvc")]
|
||||
{
|
||||
catch_fn(data, payload_ptr)
|
||||
}
|
||||
#[cfg(not(target_env = "msvc"))]
|
||||
{
|
||||
catch_fn(data, payload.assume_init())
|
||||
}
|
||||
}
|
||||
r
|
||||
}
|
||||
}
|
||||
|
||||
// We consider unwinding to be rare, so mark this function as cold. However,
|
||||
// do not mark it no-inline -- that decision is best to leave to the
|
||||
// optimizer (in most cases this function is not inlined even as a normal,
|
||||
// non-cold function, though, as of the writing of this comment).
|
||||
#[cold]
|
||||
unsafe fn cleanup(payload: *mut u8) -> Box<dyn Any + Send + 'static> {
|
||||
let obj = Box::from_raw(__rust_panic_cleanup(payload));
|
||||
update_panic_count(-1);
|
||||
obj
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn do_call<F: FnOnce() -> R, R>(data: *mut u8) {
|
||||
unsafe {
|
||||
let data = data as *mut Data<F, R>;
|
||||
@ -303,6 +330,19 @@ pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>>
|
||||
data.r = ManuallyDrop::new(f());
|
||||
}
|
||||
}
|
||||
|
||||
// We *do* want this part of the catch to be inlined: this allows the
|
||||
// compiler to properly track accesses to the Data union and optimize it
|
||||
// away most of the time.
|
||||
#[inline]
|
||||
fn do_catch<F: FnOnce() -> R, R>(data: *mut u8, payload: *mut u8) {
|
||||
unsafe {
|
||||
let data = data as *mut Data<F, R>;
|
||||
let data = &mut (*data);
|
||||
let obj = cleanup(payload);
|
||||
data.p = ManuallyDrop::new(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Determines whether the current thread is unwinding because of panic.
|
||||
|
@ -33,8 +33,13 @@ fn main() {
|
||||
} else if target.contains("dragonfly") {
|
||||
println!("cargo:rustc-link-lib=gcc_pic");
|
||||
} else if target.contains("pc-windows-gnu") {
|
||||
println!("cargo:rustc-link-lib=static-nobundle=gcc_eh");
|
||||
println!("cargo:rustc-link-lib=static-nobundle=pthread");
|
||||
// This is handled in the target spec with late_link_args_[static|dynamic]
|
||||
|
||||
// cfg!(bootstrap) doesn't work in build scripts
|
||||
if env::var("RUSTC_STAGE").ok() == Some("0".to_string()) {
|
||||
println!("cargo:rustc-link-lib=static-nobundle=gcc_eh");
|
||||
println!("cargo:rustc-link-lib=static-nobundle=pthread");
|
||||
}
|
||||
} else if target.contains("uwp-windows-gnu") {
|
||||
println!("cargo:rustc-link-lib=unwind");
|
||||
} else if target.contains("fuchsia") {
|
||||
|
@ -60,37 +60,36 @@ pub mod eh_frames {
|
||||
}
|
||||
|
||||
// Unwind info registration/deregistration routines.
|
||||
// See the docs of `unwind` module in libstd.
|
||||
// See the docs of libpanic_unwind.
|
||||
extern "C" {
|
||||
fn rust_eh_register_frames(eh_frame_begin: *const u8, object: *mut u8);
|
||||
fn rust_eh_unregister_frames(eh_frame_begin: *const u8, object: *mut u8);
|
||||
}
|
||||
|
||||
unsafe fn init() {
|
||||
unsafe extern "C" fn init() {
|
||||
// register unwind info on module startup
|
||||
rust_eh_register_frames(&__EH_FRAME_BEGIN__ as *const u8, &mut OBJ as *mut _ as *mut u8);
|
||||
}
|
||||
|
||||
unsafe fn uninit() {
|
||||
unsafe extern "C" fn uninit() {
|
||||
// unregister on shutdown
|
||||
rust_eh_unregister_frames(&__EH_FRAME_BEGIN__ as *const u8, &mut OBJ as *mut _ as *mut u8);
|
||||
}
|
||||
|
||||
// MSVC-specific init/uninit routine registration
|
||||
pub mod ms_init {
|
||||
// .CRT$X?? sections are roughly analogous to ELF's .init_array and .fini_array,
|
||||
// except that they exploit the fact that linker will sort them alphabitically,
|
||||
// so e.g., sections with names between .CRT$XIA and .CRT$XIZ are guaranteed to be
|
||||
// placed between those two, without requiring any ordering of objects on the linker
|
||||
// command line.
|
||||
// Note that ordering of same-named sections from different objects is not guaranteed.
|
||||
// Since .CRT$XIA contains init array's header symbol, which must always come first,
|
||||
// we place our initialization callback into .CRT$XIB.
|
||||
// MinGW-specific init/uninit routine registration
|
||||
pub mod mingw_init {
|
||||
// MinGW's startup objects (crt0.o / dllcrt0.o) will invoke global constructors in the
|
||||
// .ctors and .dtors sections on startup and exit. In the case of DLLs, this is done when
|
||||
// the DLL is loaded and unloaded.
|
||||
//
|
||||
// The linker will sort the sections, which ensures that our callbacks are located at the
|
||||
// end of the list. Since constructors are run in reverse order, this ensures that our
|
||||
// callbacks are the first and last ones executed.
|
||||
|
||||
#[link_section = ".CRT$XIB"] // .CRT$XI? : C initialization callbacks
|
||||
pub static P_INIT: unsafe fn() = super::init;
|
||||
#[link_section = ".ctors.65535"] // .ctors.* : C initialization callbacks
|
||||
pub static P_INIT: unsafe extern "C" fn() = super::init;
|
||||
|
||||
#[link_section = ".CRT$XTY"] // .CRT$XT? : C termination callbacks
|
||||
pub static P_UNINIT: unsafe fn() = super::uninit;
|
||||
#[link_section = ".dtors.65535"] // .dtors.* : C termination callbacks
|
||||
pub static P_UNINIT: unsafe extern "C" fn() = super::uninit;
|
||||
}
|
||||
}
|
||||
|
19
src/test/codegen/catch-unwind.rs
Normal file
19
src/test/codegen/catch-unwind.rs
Normal file
@ -0,0 +1,19 @@
|
||||
// compile-flags: -O
|
||||
|
||||
#![crate_type = "lib"]
|
||||
|
||||
extern "C" {
|
||||
fn bar();
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @foo
|
||||
#[no_mangle]
|
||||
pub unsafe fn foo() -> i32 {
|
||||
// CHECK: call void @bar
|
||||
// CHECK: ret i32 0
|
||||
std::panic::catch_unwind(|| {
|
||||
bar();
|
||||
0
|
||||
})
|
||||
.unwrap()
|
||||
}
|
20
src/test/codegen/try-panic-abort.rs
Normal file
20
src/test/codegen/try-panic-abort.rs
Normal file
@ -0,0 +1,20 @@
|
||||
// compile-flags: -C panic=abort -O
|
||||
|
||||
#![crate_type = "lib"]
|
||||
#![feature(unwind_attributes, core_intrinsics)]
|
||||
|
||||
extern "C" {
|
||||
#[unwind(allow)]
|
||||
fn bar(data: *mut u8);
|
||||
}
|
||||
extern "Rust" {
|
||||
fn catch(data: *mut u8, exception: *mut u8);
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @foo
|
||||
#[no_mangle]
|
||||
pub unsafe fn foo() -> i32 {
|
||||
// CHECK: call void @bar
|
||||
// CHECK: ret i32 0
|
||||
std::intrinsics::r#try(|x| bar(x), 0 as *mut u8, |x, y| catch(x, y))
|
||||
}
|
@ -11,5 +11,3 @@ use core::panic::PanicInfo;
|
||||
fn panic_impl(info: &PanicInfo) -> ! { loop {} }
|
||||
#[lang = "eh_personality"]
|
||||
fn eh_personality() {}
|
||||
#[lang = "eh_unwind_resume"]
|
||||
fn eh_unwind_resume() {}
|
||||
|
@ -17,8 +17,6 @@ const X: () = unimplemented!();
|
||||
|
||||
#[lang = "eh_personality"]
|
||||
fn eh() {}
|
||||
#[lang = "eh_unwind_resume"]
|
||||
fn eh_unwind_resume() {}
|
||||
|
||||
#[panic_handler]
|
||||
fn panic(_info: &PanicInfo) -> ! {
|
||||
|
@ -1,41 +1,41 @@
|
||||
error: 1 positional argument in format string, but no arguments were given
|
||||
--> $DIR/macro-comma-behavior.rs:21:23
|
||||
--> $DIR/macro-comma-behavior.rs:20:23
|
||||
|
|
||||
LL | assert_eq!(1, 1, "{}",);
|
||||
| ^^
|
||||
|
||||
error: 1 positional argument in format string, but no arguments were given
|
||||
--> $DIR/macro-comma-behavior.rs:24:23
|
||||
--> $DIR/macro-comma-behavior.rs:23:23
|
||||
|
|
||||
LL | assert_ne!(1, 2, "{}",);
|
||||
| ^^
|
||||
|
||||
error: 1 positional argument in format string, but no arguments were given
|
||||
--> $DIR/macro-comma-behavior.rs:30:29
|
||||
--> $DIR/macro-comma-behavior.rs:29:29
|
||||
|
|
||||
LL | debug_assert_eq!(1, 1, "{}",);
|
||||
| ^^
|
||||
|
||||
error: 1 positional argument in format string, but no arguments were given
|
||||
--> $DIR/macro-comma-behavior.rs:33:29
|
||||
--> $DIR/macro-comma-behavior.rs:32:29
|
||||
|
|
||||
LL | debug_assert_ne!(1, 2, "{}",);
|
||||
| ^^
|
||||
|
||||
error: 1 positional argument in format string, but no arguments were given
|
||||
--> $DIR/macro-comma-behavior.rs:54:19
|
||||
--> $DIR/macro-comma-behavior.rs:53:19
|
||||
|
|
||||
LL | format_args!("{}",);
|
||||
| ^^
|
||||
|
||||
error: 1 positional argument in format string, but no arguments were given
|
||||
--> $DIR/macro-comma-behavior.rs:72:21
|
||||
--> $DIR/macro-comma-behavior.rs:71:21
|
||||
|
|
||||
LL | unimplemented!("{}",);
|
||||
| ^^
|
||||
|
||||
error: 1 positional argument in format string, but no arguments were given
|
||||
--> $DIR/macro-comma-behavior.rs:81:24
|
||||
--> $DIR/macro-comma-behavior.rs:80:24
|
||||
|
|
||||
LL | write!(f, "{}",)?;
|
||||
| ^^
|
||||
|
@ -9,7 +9,6 @@
|
||||
#[cfg(std)] use std::fmt;
|
||||
#[cfg(core)] use core::fmt;
|
||||
#[cfg(core)] #[lang = "eh_personality"] fn eh_personality() {}
|
||||
#[cfg(core)] #[lang = "eh_unwind_resume"] fn eh_unwind_resume() {}
|
||||
#[cfg(core)] #[lang = "panic_impl"] fn panic_impl(panic: &core::panic::PanicInfo) -> ! { loop {} }
|
||||
|
||||
// (see documentation of the similarly-named test in run-pass)
|
||||
|
@ -1,59 +1,59 @@
|
||||
error: 1 positional argument in format string, but no arguments were given
|
||||
--> $DIR/macro-comma-behavior.rs:21:23
|
||||
--> $DIR/macro-comma-behavior.rs:20:23
|
||||
|
|
||||
LL | assert_eq!(1, 1, "{}",);
|
||||
| ^^
|
||||
|
||||
error: 1 positional argument in format string, but no arguments were given
|
||||
--> $DIR/macro-comma-behavior.rs:24:23
|
||||
--> $DIR/macro-comma-behavior.rs:23:23
|
||||
|
|
||||
LL | assert_ne!(1, 2, "{}",);
|
||||
| ^^
|
||||
|
||||
error: 1 positional argument in format string, but no arguments were given
|
||||
--> $DIR/macro-comma-behavior.rs:30:29
|
||||
--> $DIR/macro-comma-behavior.rs:29:29
|
||||
|
|
||||
LL | debug_assert_eq!(1, 1, "{}",);
|
||||
| ^^
|
||||
|
||||
error: 1 positional argument in format string, but no arguments were given
|
||||
--> $DIR/macro-comma-behavior.rs:33:29
|
||||
--> $DIR/macro-comma-behavior.rs:32:29
|
||||
|
|
||||
LL | debug_assert_ne!(1, 2, "{}",);
|
||||
| ^^
|
||||
|
||||
error: 1 positional argument in format string, but no arguments were given
|
||||
--> $DIR/macro-comma-behavior.rs:38:18
|
||||
--> $DIR/macro-comma-behavior.rs:37:18
|
||||
|
|
||||
LL | eprint!("{}",);
|
||||
| ^^
|
||||
|
||||
error: 1 positional argument in format string, but no arguments were given
|
||||
--> $DIR/macro-comma-behavior.rs:50:18
|
||||
--> $DIR/macro-comma-behavior.rs:49:18
|
||||
|
|
||||
LL | format!("{}",);
|
||||
| ^^
|
||||
|
||||
error: 1 positional argument in format string, but no arguments were given
|
||||
--> $DIR/macro-comma-behavior.rs:54:19
|
||||
--> $DIR/macro-comma-behavior.rs:53:19
|
||||
|
|
||||
LL | format_args!("{}",);
|
||||
| ^^
|
||||
|
||||
error: 1 positional argument in format string, but no arguments were given
|
||||
--> $DIR/macro-comma-behavior.rs:61:17
|
||||
--> $DIR/macro-comma-behavior.rs:60:17
|
||||
|
|
||||
LL | print!("{}",);
|
||||
| ^^
|
||||
|
||||
error: 1 positional argument in format string, but no arguments were given
|
||||
--> $DIR/macro-comma-behavior.rs:72:21
|
||||
--> $DIR/macro-comma-behavior.rs:71:21
|
||||
|
|
||||
LL | unimplemented!("{}",);
|
||||
| ^^
|
||||
|
||||
error: 1 positional argument in format string, but no arguments were given
|
||||
--> $DIR/macro-comma-behavior.rs:81:24
|
||||
--> $DIR/macro-comma-behavior.rs:80:24
|
||||
|
|
||||
LL | write!(f, "{}",)?;
|
||||
| ^^
|
||||
|
@ -1,23 +0,0 @@
|
||||
// run-pass
|
||||
// compile-flags: -Z no-landing-pads -C codegen-units=1
|
||||
// ignore-emscripten no threads support
|
||||
|
||||
use std::thread;
|
||||
|
||||
static mut HIT: bool = false;
|
||||
|
||||
struct A;
|
||||
|
||||
impl Drop for A {
|
||||
fn drop(&mut self) {
|
||||
unsafe { HIT = true; }
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
thread::spawn(move|| -> () {
|
||||
let _a = A;
|
||||
panic!();
|
||||
}).join().unwrap_err();
|
||||
assert!(unsafe { !HIT });
|
||||
}
|
@ -12,5 +12,4 @@ fn main() {
|
||||
}
|
||||
|
||||
#[lang = "eh_personality"] extern fn eh_personality() {}
|
||||
#[lang = "eh_unwind_resume"] extern fn eh_unwind_resume() {}
|
||||
#[lang = "panic_impl"] fn panic_impl(panic: &PanicInfo) -> ! { loop {} }
|
||||
|
@ -11,5 +11,3 @@ use core::panic::PanicInfo;
|
||||
fn panic_impl(info: &PanicInfo) -> ! { loop {} }
|
||||
#[lang = "eh_personality"]
|
||||
fn eh_personality() {}
|
||||
#[lang = "eh_unwind_resume"]
|
||||
fn eh_unwind_resume() {}
|
||||
|
@ -15,10 +15,6 @@ use core::ops::RangeBounds;
|
||||
#[lang = "eh_personality"]
|
||||
extern fn eh_personality() {}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
#[lang = "eh_unwind_resume"]
|
||||
extern fn eh_unwind_resume() {}
|
||||
|
||||
|
||||
// take a reference to any built-in range
|
||||
fn take_range(_r: &impl RangeBounds<i8>) {}
|
||||
|
@ -1,7 +1,7 @@
|
||||
error: `#[panic_handler]` function required, but not found
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/issue-54505-no-std.rs:28:16
|
||||
--> $DIR/issue-54505-no-std.rs:24:16
|
||||
|
|
||||
LL | take_range(0..1);
|
||||
| ^^^^
|
||||
@ -13,7 +13,7 @@ LL | take_range(0..1);
|
||||
found struct `core::ops::Range<{integer}>`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/issue-54505-no-std.rs:33:16
|
||||
--> $DIR/issue-54505-no-std.rs:29:16
|
||||
|
|
||||
LL | take_range(1..);
|
||||
| ^^^
|
||||
@ -25,7 +25,7 @@ LL | take_range(1..);
|
||||
found struct `core::ops::RangeFrom<{integer}>`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/issue-54505-no-std.rs:38:16
|
||||
--> $DIR/issue-54505-no-std.rs:34:16
|
||||
|
|
||||
LL | take_range(..);
|
||||
| ^^
|
||||
@ -37,7 +37,7 @@ LL | take_range(..);
|
||||
found struct `core::ops::RangeFull`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/issue-54505-no-std.rs:43:16
|
||||
--> $DIR/issue-54505-no-std.rs:39:16
|
||||
|
|
||||
LL | take_range(0..=1);
|
||||
| ^^^^^
|
||||
@ -49,7 +49,7 @@ LL | take_range(0..=1);
|
||||
found struct `core::ops::RangeInclusive<{integer}>`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/issue-54505-no-std.rs:48:16
|
||||
--> $DIR/issue-54505-no-std.rs:44:16
|
||||
|
|
||||
LL | take_range(..5);
|
||||
| ^^^
|
||||
@ -61,7 +61,7 @@ LL | take_range(..5);
|
||||
found struct `core::ops::RangeTo<{integer}>`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/issue-54505-no-std.rs:53:16
|
||||
--> $DIR/issue-54505-no-std.rs:49:16
|
||||
|
|
||||
LL | take_range(..=42);
|
||||
| ^^^^^
|
||||
|
@ -59,6 +59,8 @@ const EXCEPTION_PATHS: &[&str] = &[
|
||||
"src/libstd/sys_common/mod.rs",
|
||||
"src/libstd/sys_common/net.rs",
|
||||
"src/libstd/sys_common/backtrace.rs",
|
||||
// panic_unwind shims
|
||||
"src/libstd/panicking.rs",
|
||||
"src/libterm", // Not sure how to make this crate portable, but test crate needs it.
|
||||
"src/libtest", // Probably should defer to unstable `std::sys` APIs.
|
||||
"src/libstd/sync/mpsc", // some tests are only run on non-emscripten
|
||||
|
Loading…
Reference in New Issue
Block a user