Auto merge of #70212 - Amanieu:catch_foreign, r=Mark-Simulacrum

Abort when foreign exceptions are caught by catch_unwind

Prior to this PR, foreign exceptions were not caught by catch_unwind, and instead passed through invisibly. This represented a painful soundness hole in some libraries ([take_mut](https://github.com/Sgeo/take_mut/blob/master/src/lib.rs#L37)), which relied on `catch_unwind` to handle all possible exit paths from a closure.

With this PR, foreign exceptions are now caught by `catch_unwind` and will trigger an abort since catching foreign exceptions is currently UB according to the latest proposals by the FFI unwind project group.

cc @rust-lang/wg-ffi-unwind
This commit is contained in:
bors 2020-08-28 01:20:17 +00:00
commit 41aaa90c67
26 changed files with 274 additions and 104 deletions

View File

@ -117,6 +117,17 @@ pub mod personalities {
1 // `ExceptionContinueSearch`
}
// Similar to above, this corresponds to the `eh_catch_typeinfo` lang item
// that's only used on Emscripten currently.
//
// Since panics don't generate exceptions and foreign exceptions are
// currently UB with -C panic=abort (although this may be subject to
// change), any catch_unwind calls will never use this typeinfo.
#[rustc_std_internal_symbol]
#[allow(non_upper_case_globals)]
#[cfg(target_os = "emscripten")]
static rust_eh_catch_typeinfo: [usize; 2] = [0; 2];
// 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.
#[rustc_std_internal_symbol]

View File

@ -51,11 +51,7 @@ pub enum EHAction {
pub const USING_SJLJ_EXCEPTIONS: bool = cfg!(all(target_os = "ios", target_arch = "arm"));
pub unsafe fn find_eh_action(
lsda: *const u8,
context: &EHContext<'_>,
foreign_exception: bool,
) -> Result<EHAction, ()> {
pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext<'_>) -> Result<EHAction, ()> {
if lsda.is_null() {
return Ok(EHAction::None);
}
@ -98,7 +94,7 @@ pub unsafe fn find_eh_action(
return Ok(EHAction::None);
} else {
let lpad = lpad_base + cs_lpad;
return Ok(interpret_cs_action(cs_action, lpad, foreign_exception));
return Ok(interpret_cs_action(cs_action, lpad));
}
}
}
@ -123,21 +119,17 @@ pub unsafe fn find_eh_action(
// Can never have null landing pad for sjlj -- that would have
// been indicated by a -1 call site index.
let lpad = (cs_lpad + 1) as usize;
return Ok(interpret_cs_action(cs_action, lpad, foreign_exception));
return Ok(interpret_cs_action(cs_action, lpad));
}
}
}
}
fn interpret_cs_action(cs_action: u64, lpad: usize, foreign_exception: bool) -> EHAction {
fn interpret_cs_action(cs_action: u64, lpad: usize) -> EHAction {
if cs_action == 0 {
// If cs_action is 0 then this is a cleanup (Drop::drop). We run these
// for both Rust panics and foreign exceptions.
EHAction::Cleanup(lpad)
} else if foreign_exception {
// catch_unwind should not catch foreign exceptions, only Rust panics.
// Instead just continue unwinding.
EHAction::None
} else {
// Stop unwinding Rust panics at catch_unwind.
EHAction::Catch(lpad)

View File

@ -8,8 +8,10 @@
use alloc::boxed::Box;
use core::any::Any;
use core::intrinsics;
use core::mem;
use core::ptr;
use core::sync::atomic::{AtomicBool, Ordering};
use libc::{self, c_int};
use unwind as uw;
@ -47,6 +49,11 @@ static EXCEPTION_TYPE_INFO: TypeInfo = TypeInfo {
};
struct Exception {
// This is necessary because C++ code can capture our execption with
// std::exception_ptr and rethrow it multiple times, possibly even in
// another thread.
caught: AtomicBool,
// 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
@ -55,11 +62,27 @@ struct Exception {
}
pub unsafe fn cleanup(ptr: *mut u8) -> Box<dyn Any + Send> {
assert!(!ptr.is_null());
let adjusted_ptr = __cxa_begin_catch(ptr as *mut libc::c_void) as *mut Exception;
let ex = (*adjusted_ptr).data.take();
// intrinsics::try actually gives us a pointer to this structure.
#[repr(C)]
struct CatchData {
ptr: *mut u8,
is_rust_panic: bool,
}
let catch_data = &*(ptr as *mut CatchData);
let adjusted_ptr = __cxa_begin_catch(catch_data.ptr as *mut libc::c_void) as *mut Exception;
let out = if catch_data.is_rust_panic {
let was_caught = (*adjusted_ptr).caught.swap(true, Ordering::SeqCst);
if was_caught {
// Since cleanup() isn't allowed to panic, we just abort instead.
intrinsics::abort();
}
(*adjusted_ptr).data.take().unwrap()
} else {
super::__rust_foreign_exception();
};
__cxa_end_catch();
ex.unwrap()
out
}
pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
@ -68,25 +91,16 @@ pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
if exception.is_null() {
return uw::_URC_FATAL_PHASE1_ERROR as u32;
}
ptr::write(exception, Exception { data: Some(data) });
ptr::write(exception, Exception { caught: AtomicBool::new(false), data: Some(data) });
__cxa_throw(exception as *mut _, &EXCEPTION_TYPE_INFO, exception_cleanup);
}
// On WASM and ARM, the destructor returns the pointer to the object.
cfg_if::cfg_if! {
if #[cfg(any(target_arch = "arm", target_arch = "wasm32"))] {
type DestructorRet = *mut libc::c_void;
} else {
type DestructorRet = ();
}
}
extern "C" fn exception_cleanup(ptr: *mut libc::c_void) -> DestructorRet {
extern "C" fn exception_cleanup(ptr: *mut libc::c_void) -> *mut libc::c_void {
unsafe {
if let Some(b) = (ptr as *mut Exception).read().data {
drop(b);
super::__rust_drop_panic();
}
#[cfg(any(target_arch = "arm", target_arch = "wasm32"))]
ptr
}
}
@ -109,7 +123,7 @@ extern "C" {
fn __cxa_throw(
thrown_exception: *mut libc::c_void,
tinfo: *const TypeInfo,
dest: extern "C" fn(*mut libc::c_void) -> DestructorRet,
dest: extern "C" fn(*mut libc::c_void) -> *mut libc::c_void,
) -> !;
fn __gxx_personality_v0(
version: c_int,

View File

@ -73,8 +73,14 @@ pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
}
pub unsafe fn cleanup(ptr: *mut u8) -> Box<dyn Any + Send> {
let exception = Box::from_raw(ptr as *mut Exception);
exception.cause
let exception = ptr as *mut uw::_Unwind_Exception;
if (*exception).exception_class != rust_exception_class() {
uw::_Unwind_DeleteException(exception);
super::__rust_foreign_exception();
} else {
let exception = Box::from_raw(exception as *mut Exception);
exception.cause
}
}
// Rust's exception class identifier. This is used by personality routines to
@ -164,9 +170,7 @@ cfg_if::cfg_if! {
// _Unwind_Context in our libunwind bindings and fetch the required data from there
// directly, bypassing DWARF compatibility functions.
let exception_class = (*exception_object).exception_class;
let foreign_exception = exception_class != rust_exception_class();
let eh_action = match find_eh_action(context, foreign_exception) {
let eh_action = match find_eh_action(context) {
Ok(action) => action,
Err(_) => return uw::_URC_FAILURE,
};
@ -221,15 +225,14 @@ cfg_if::cfg_if! {
// and indirectly on Windows x86_64 via SEH.
unsafe extern "C" fn rust_eh_personality_impl(version: c_int,
actions: uw::_Unwind_Action,
exception_class: uw::_Unwind_Exception_Class,
_exception_class: uw::_Unwind_Exception_Class,
exception_object: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context)
-> uw::_Unwind_Reason_Code {
if version != 1 {
return uw::_URC_FATAL_PHASE1_ERROR;
}
let foreign_exception = exception_class != rust_exception_class();
let eh_action = match find_eh_action(context, foreign_exception) {
let eh_action = match find_eh_action(context) {
Ok(action) => action,
Err(_) => return uw::_URC_FATAL_PHASE1_ERROR,
};
@ -293,10 +296,7 @@ cfg_if::cfg_if! {
}
}
unsafe fn find_eh_action(
context: *mut uw::_Unwind_Context,
foreign_exception: bool,
) -> Result<EHAction, ()> {
unsafe fn find_eh_action(context: *mut uw::_Unwind_Context) -> Result<EHAction, ()> {
let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8;
let mut ip_before_instr: c_int = 0;
let ip = uw::_Unwind_GetIPInfo(context, &mut ip_before_instr);
@ -308,7 +308,7 @@ unsafe fn find_eh_action(
get_text_start: &|| uw::_Unwind_GetTextRelBase(context),
get_data_start: &|| uw::_Unwind_GetDataRelBase(context),
};
eh::find_eh_action(lsda, &eh_context, foreign_exception)
eh::find_eh_action(lsda, &eh_context)
}
// Frame unwind info registration

View File

@ -88,6 +88,9 @@ extern "C" {
/// Handler in libstd called when a panic object is dropped outside of
/// `catch_unwind`.
fn __rust_drop_panic() -> !;
/// Handler in libstd called when a foreign exception is caught.
fn __rust_foreign_exception() -> !;
}
mod dwarf;

View File

@ -309,15 +309,21 @@ pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
extern "system" {
#[unwind(allowed)]
pub fn _CxxThrowException(pExceptionObject: *mut c_void, pThrowInfo: *mut u8) -> !;
fn _CxxThrowException(pExceptionObject: *mut c_void, pThrowInfo: *mut u8) -> !;
}
_CxxThrowException(throw_ptr, &mut THROW_INFO as *mut _ as *mut _);
}
pub unsafe fn cleanup(payload: *mut u8) -> Box<dyn Any + Send> {
let exception = &mut *(payload as *mut Exception);
exception.data.take().unwrap()
// A NULL payload here means that we got here from the catch (...) of
// __rust_try. This happens when a non-Rust foreign exception is caught.
if payload.is_null() {
super::__rust_foreign_exception();
} else {
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

View File

@ -359,6 +359,9 @@ impl<F: Future> Future for AssertUnwindSafe<F> {
/// aborting the process as well. This function *only* catches unwinding panics,
/// not those that abort the process.
///
/// Also note that unwinding into Rust code with a foreign exception (e.g. a
/// an exception thrown from C++ code) is undefined behavior.
///
/// # Examples
///
/// ```

View File

@ -60,6 +60,14 @@ extern "C" fn __rust_drop_panic() -> ! {
rtabort!("Rust panics must be rethrown");
}
/// This function is called by the panic runtime if it catches an exception
/// object which does not correspond to a Rust panic.
#[cfg(not(test))]
#[rustc_std_internal_symbol]
extern "C" fn __rust_foreign_exception() -> ! {
rtabort!("Rust cannot catch foreign exceptions");
}
#[derive(Copy, Clone)]
enum Hook {
Default,

View File

@ -82,6 +82,7 @@ pub struct CodegenCx<'ll, 'tcx> {
pub dbg_cx: Option<debuginfo::CrateDebugContext<'ll, 'tcx>>,
eh_personality: Cell<Option<&'ll Value>>,
eh_catch_typeinfo: Cell<Option<&'ll Value>>,
pub rust_try_fn: Cell<Option<&'ll Value>>,
intrinsics: RefCell<FxHashMap<&'static str, &'ll Value>>,
@ -311,6 +312,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
coverage_cx,
dbg_cx,
eh_personality: Cell::new(None),
eh_catch_typeinfo: Cell::new(None),
rust_try_fn: Cell::new(None),
intrinsics: Default::default(),
local_gen_sym_counter: Cell::new(0),
@ -819,6 +821,25 @@ impl CodegenCx<'b, 'tcx> {
}
None
}
crate fn eh_catch_typeinfo(&self) -> &'b Value {
if let Some(eh_catch_typeinfo) = self.eh_catch_typeinfo.get() {
return eh_catch_typeinfo;
}
let tcx = self.tcx;
assert!(self.sess().target.target.options.is_like_emscripten);
let eh_catch_typeinfo = match tcx.lang_items().eh_catch_typeinfo() {
Some(def_id) => self.get_static(def_id),
_ => {
let ty = self
.type_struct(&[self.type_ptr_to(self.type_isize()), self.type_i8p()], false);
self.declare_global("rust_eh_catch_typeinfo", ty)
}
};
let eh_catch_typeinfo = self.const_bitcast(eh_catch_typeinfo, self.type_i8p());
self.eh_catch_typeinfo.set(Some(eh_catch_typeinfo));
eh_catch_typeinfo
}
}
impl<'b, 'tcx> CodegenCx<'b, 'tcx> {

View File

@ -855,6 +855,8 @@ fn try_intrinsic(
bx.store(bx.const_i32(0), dest, ret_align);
} else if wants_msvc_seh(bx.sess()) {
codegen_msvc_try(bx, try_func, data, catch_func, dest);
} else if bx.sess().target.target.options.is_like_emscripten {
codegen_emcc_try(bx, try_func, data, catch_func, dest);
} else {
codegen_gnu_try(bx, try_func, data, catch_func, dest);
}
@ -880,7 +882,8 @@ fn codegen_msvc_try(
let mut normal = bx.build_sibling_block("normal");
let mut catchswitch = bx.build_sibling_block("catchswitch");
let mut catchpad = bx.build_sibling_block("catchpad");
let mut catchpad_rust = bx.build_sibling_block("catchpad_rust");
let mut catchpad_foreign = bx.build_sibling_block("catchpad_foreign");
let mut caught = bx.build_sibling_block("caught");
let try_func = llvm::get_param(bx.llfn(), 0);
@ -890,21 +893,26 @@ fn codegen_msvc_try(
// We're generating an IR snippet that looks like:
//
// declare i32 @rust_try(%try_func, %data, %catch_func) {
// %slot = alloca u8*
// %slot = alloca i8*
// invoke %try_func(%data) to label %normal unwind label %catchswitch
//
// normal:
// ret i32 0
//
// catchswitch:
// %cs = catchswitch within none [%catchpad] unwind to caller
// %cs = catchswitch within none [%catchpad_rust, %catchpad_foreign] unwind to caller
//
// catchpad:
// %tok = catchpad within %cs [%type_descriptor, 0, %slot]
// catchpad_rust:
// %tok = catchpad within %cs [%type_descriptor, 8, %slot]
// %ptr = load %slot
// call %catch_func(%data, %ptr)
// catchret from %tok to label %caught
//
// catchpad_foreign:
// %tok = catchpad within %cs [null, 64, null]
// call %catch_func(%data, null)
// catchret from %tok to label %caught
//
// caught:
// ret i32 1
// }
@ -912,13 +920,11 @@ fn codegen_msvc_try(
// This structure follows the basic usage of throw/try/catch in LLVM.
// For example, compile this C++ snippet to see what LLVM generates:
//
// #include <stdint.h>
//
// struct rust_panic {
// rust_panic(const rust_panic&);
// ~rust_panic();
//
// uint64_t x[2];
// void* x[2];
// };
//
// int __rust_try(
@ -932,6 +938,9 @@ fn codegen_msvc_try(
// } catch(rust_panic& a) {
// catch_func(data, &a);
// return 1;
// } catch(...) {
// catch_func(data, NULL);
// return 1;
// }
// }
//
@ -942,8 +951,9 @@ fn codegen_msvc_try(
normal.ret(bx.const_i32(0));
let cs = catchswitch.catch_switch(None, None, 1);
catchswitch.add_handler(cs, catchpad.llbb());
let cs = catchswitch.catch_switch(None, None, 2);
catchswitch.add_handler(cs, catchpad_rust.llbb());
catchswitch.add_handler(cs, catchpad_foreign.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
@ -977,11 +987,17 @@ fn codegen_msvc_try(
//
// Source: MicrosoftCXXABI::getAddrOfCXXCatchHandlerType in clang
let flags = bx.const_i32(8);
let funclet = catchpad.catch_pad(cs, &[tydesc, flags, slot]);
let ptr = catchpad.load(slot, ptr_align);
catchpad.call(catch_func, &[data, ptr], Some(&funclet));
let funclet = catchpad_rust.catch_pad(cs, &[tydesc, flags, slot]);
let ptr = catchpad_rust.load(slot, ptr_align);
catchpad_rust.call(catch_func, &[data, ptr], Some(&funclet));
catchpad_rust.catch_ret(&funclet, caught.llbb());
catchpad.catch_ret(&funclet, caught.llbb());
// The flag value of 64 indicates a "catch-all".
let flags = bx.const_i32(64);
let null = bx.const_null(bx.type_i8p());
let funclet = catchpad_foreign.catch_pad(cs, &[null, flags, null]);
catchpad_foreign.call(catch_func, &[data, null], Some(&funclet));
catchpad_foreign.catch_ret(&funclet, caught.llbb());
caught.ret(bx.const_i32(1));
});
@ -1044,13 +1060,7 @@ fn codegen_gnu_try(
// rust_try ignores the selector.
let lpad_ty = bx.type_struct(&[bx.type_i8p(), bx.type_i32()], false);
let vals = catch.landing_pad(lpad_ty, bx.eh_personality(), 1);
let tydesc = match bx.tcx().lang_items().eh_catch_typeinfo() {
Some(tydesc) => {
let tydesc = bx.get_static(tydesc);
bx.bitcast(tydesc, bx.type_i8p())
}
None => bx.const_null(bx.type_i8p()),
};
let tydesc = bx.const_null(bx.type_i8p());
catch.add_clause(vals, tydesc);
let ptr = catch.extract_value(vals, 0);
catch.call(catch_func, &[data, ptr], None);
@ -1064,6 +1074,88 @@ fn codegen_gnu_try(
bx.store(ret, dest, i32_align);
}
// Variant of codegen_gnu_try used for emscripten where Rust panics are
// implemented using C++ exceptions. Here we use exceptions of a specific type
// (`struct rust_panic`) to represent Rust panics.
fn codegen_emcc_try(
bx: &mut Builder<'a, 'll, 'tcx>,
try_func: &'ll Value,
data: &'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 %try_func(%data) normal %normal unwind %catch
//
// normal:
// ret 0
//
// catch:
// (%ptr, %selector) = landingpad
// %rust_typeid = @llvm.eh.typeid.for(@_ZTI10rust_panic)
// %is_rust_panic = %selector == %rust_typeid
// %catch_data = alloca { i8*, i8 }
// %catch_data[0] = %ptr
// %catch_data[1] = %is_rust_panic
// call %catch_func(%data, %catch_data)
// ret 1
bx.sideeffect();
let mut then = bx.build_sibling_block("then");
let mut catch = bx.build_sibling_block("catch");
let try_func = llvm::get_param(bx.llfn(), 0);
let data = llvm::get_param(bx.llfn(), 1);
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.
//
// The first value in this tuple is a pointer to the exception object
// being thrown. The second value is a "selector" indicating which of
// the landing pad clauses the exception's type had been matched to.
let tydesc = bx.eh_catch_typeinfo();
let lpad_ty = bx.type_struct(&[bx.type_i8p(), bx.type_i32()], false);
let vals = catch.landing_pad(lpad_ty, bx.eh_personality(), 2);
catch.add_clause(vals, tydesc);
catch.add_clause(vals, bx.const_null(bx.type_i8p()));
let ptr = catch.extract_value(vals, 0);
let selector = catch.extract_value(vals, 1);
// Check if the typeid we got is the one for a Rust panic.
let llvm_eh_typeid_for = bx.get_intrinsic("llvm.eh.typeid.for");
let rust_typeid = catch.call(llvm_eh_typeid_for, &[tydesc], None);
let is_rust_panic = catch.icmp(IntPredicate::IntEQ, selector, rust_typeid);
let is_rust_panic = catch.zext(is_rust_panic, bx.type_bool());
// We need to pass two values to catch_func (ptr and is_rust_panic), so
// create an alloca and pass a pointer to that.
let ptr_align = bx.tcx().data_layout.pointer_align.abi;
let i8_align = bx.tcx().data_layout.i8_align.abi;
let catch_data =
catch.alloca(bx.type_struct(&[bx.type_i8p(), bx.type_bool()], false), ptr_align);
let catch_data_0 = catch.inbounds_gep(catch_data, &[bx.const_usize(0), bx.const_usize(0)]);
catch.store(ptr, catch_data_0, ptr_align);
let catch_data_1 = catch.inbounds_gep(catch_data, &[bx.const_usize(0), bx.const_usize(1)]);
catch.store(is_rust_panic, catch_data_1, i8_align);
let catch_data = catch.bitcast(catch_data, bx.type_i8p());
catch.call(catch_func, &[data, catch_data], 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, &[try_func, data, catch_func], None);
let i32_align = bx.tcx().data_layout.i32_align.abi;
bx.store(ret, dest, i32_align);
}
// Helper function to give a Block to a closure to codegen a shim function.
// This is currently primarily used for the `try` intrinsic functions above.
fn gen_fn<'ll, 'tcx>(

View File

@ -48,5 +48,6 @@ impl LanguageItems {
weak_lang_items! {
panic_impl, PanicImpl, rust_begin_unwind;
eh_personality, EhPersonality, rust_eh_personality;
eh_catch_typeinfo, EhCatchTypeinfo, rust_eh_catch_typeinfo;
oom, Oom, rust_oom;
}

View File

@ -53,7 +53,9 @@ pub fn required(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.
match tcx.sess.panic_strategy() {
PanicStrategy::Abort => lang_item != LangItem::EhPersonality,
PanicStrategy::Abort => {
lang_item != LangItem::EhPersonality && lang_item != LangItem::EhCatchTypeinfo
}
PanicStrategy::Unwind => true,
}
}

View File

@ -26,6 +26,9 @@ pub fn check_crate<'tcx>(tcx: TyCtxt<'tcx>, items: &mut lang_items::LanguageItem
if items.eh_personality().is_none() {
items.missing.push(LangItem::EhPersonality);
}
if tcx.sess.target.target.options.is_like_emscripten && items.eh_catch_typeinfo().is_none() {
items.missing.push(LangItem::EhCatchTypeinfo);
}
{
let mut cx = Context { tcx, items };

View File

@ -876,6 +876,7 @@ symbols! {
rust_2015_preview,
rust_2018_preview,
rust_begin_unwind,
rust_eh_catch_typeinfo,
rust_eh_personality,
rust_eh_register_frames,
rust_eh_unregister_frames,

View File

@ -11,3 +11,5 @@ use core::panic::PanicInfo;
fn panic_impl(info: &PanicInfo) -> ! { loop {} }
#[lang = "eh_personality"]
fn eh_personality() {}
#[lang = "eh_catch_typeinfo"]
static EH_CATCH_TYPEINFO: u8 = 0;

View File

@ -23,15 +23,15 @@ struct drop_check {
extern "C" {
void rust_catch_callback(void (*cb)(), bool* rust_ok);
static void callback() {
void throw_cxx_exception() {
println("throwing C++ exception");
throw exception();
}
void throw_cxx_exception() {
void test_cxx_exception() {
bool rust_ok = false;
try {
rust_catch_callback(callback, &rust_ok);
rust_catch_callback(throw_cxx_exception, &rust_ok);
assert(false && "unreachable");
} catch (exception e) {
println("caught C++ exception");

View File

@ -1,5 +1,5 @@
// Tests that C++ exceptions can unwind through Rust code, run destructors and
// are ignored by catch_unwind. Also tests that Rust panics can unwind through
// Tests that C++ exceptions can unwind through Rust code run destructors and
// are caught by catch_unwind. Also tests that Rust panics can unwind through
// C++ code.
// For linking libstdc++ on MinGW
@ -17,7 +17,7 @@ impl<'a> Drop for DropCheck<'a> {
}
extern "C" {
fn throw_cxx_exception();
fn test_cxx_exception();
#[unwind(allowed)]
fn cxx_catch_callback(cb: extern "C" fn(), ok: *mut bool);
@ -26,15 +26,12 @@ extern "C" {
#[no_mangle]
#[unwind(allowed)]
extern "C" fn rust_catch_callback(cb: extern "C" fn(), rust_ok: &mut bool) {
let _caught_unwind = catch_unwind(AssertUnwindSafe(|| {
let _drop = DropCheck(rust_ok);
cb();
unreachable!("should have unwound instead of returned");
}));
unreachable!("catch_unwind should not have caught foreign exception");
let _drop = DropCheck(rust_ok);
cb();
unreachable!("should have unwound instead of returned");
}
fn throw_rust_panic() {
fn test_rust_panic() {
#[unwind(allowed)]
extern "C" fn callback() {
println!("throwing rust panic");
@ -60,6 +57,6 @@ fn throw_rust_panic() {
}
fn main() {
unsafe { throw_cxx_exception() };
throw_rust_panic();
unsafe { test_cxx_exception() };
test_rust_panic();
}

View File

@ -14,3 +14,8 @@ pub fn panic_handler(_: &core::panic::PanicInfo) -> ! {
extern "C" fn __rust_drop_panic() -> ! {
loop {}
}
#[no_mangle]
extern "C" fn __rust_foreign_exception() -> ! {
loop {}
}

View File

@ -17,6 +17,8 @@ const X: () = unimplemented!();
#[lang = "eh_personality"]
fn eh() {}
#[lang = "eh_catch_typeinfo"]
static EH_CATCH_TYPEINFO: u8 = 0;
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {

View File

@ -1,41 +1,41 @@
error: 1 positional argument in format string, but no arguments were given
--> $DIR/macro-comma-behavior.rs:20:23
--> $DIR/macro-comma-behavior.rs:21:23
|
LL | assert_eq!(1, 1, "{}",);
| ^^
error: 1 positional argument in format string, but no arguments were given
--> $DIR/macro-comma-behavior.rs:23:23
--> $DIR/macro-comma-behavior.rs:24:23
|
LL | assert_ne!(1, 2, "{}",);
| ^^
error: 1 positional argument in format string, but no arguments were given
--> $DIR/macro-comma-behavior.rs:29:29
--> $DIR/macro-comma-behavior.rs:30:29
|
LL | debug_assert_eq!(1, 1, "{}",);
| ^^
error: 1 positional argument in format string, but no arguments were given
--> $DIR/macro-comma-behavior.rs:32:29
--> $DIR/macro-comma-behavior.rs:33:29
|
LL | debug_assert_ne!(1, 2, "{}",);
| ^^
error: 1 positional argument in format string, but no arguments were given
--> $DIR/macro-comma-behavior.rs:53:19
--> $DIR/macro-comma-behavior.rs:54:19
|
LL | format_args!("{}",);
| ^^
error: 1 positional argument in format string, but no arguments were given
--> $DIR/macro-comma-behavior.rs:71:21
--> $DIR/macro-comma-behavior.rs:72:21
|
LL | unimplemented!("{}",);
| ^^
error: 1 positional argument in format string, but no arguments were given
--> $DIR/macro-comma-behavior.rs:80:24
--> $DIR/macro-comma-behavior.rs:81:24
|
LL | write!(f, "{}",)?;
| ^^

View File

@ -9,6 +9,7 @@
#[cfg(std)] use std::fmt;
#[cfg(core)] use core::fmt;
#[cfg(core)] #[lang = "eh_personality"] fn eh_personality() {}
#[cfg(core)] #[lang = "eh_catch_typeinfo"] static EH_CATCH_TYPEINFO: u8 = 0;
#[cfg(core)] #[lang = "panic_impl"] fn panic_impl(panic: &core::panic::PanicInfo) -> ! { loop {} }
// (see documentation of the similarly-named test in run-pass)

View File

@ -1,59 +1,59 @@
error: 1 positional argument in format string, but no arguments were given
--> $DIR/macro-comma-behavior.rs:20:23
--> $DIR/macro-comma-behavior.rs:21:23
|
LL | assert_eq!(1, 1, "{}",);
| ^^
error: 1 positional argument in format string, but no arguments were given
--> $DIR/macro-comma-behavior.rs:23:23
--> $DIR/macro-comma-behavior.rs:24:23
|
LL | assert_ne!(1, 2, "{}",);
| ^^
error: 1 positional argument in format string, but no arguments were given
--> $DIR/macro-comma-behavior.rs:29:29
--> $DIR/macro-comma-behavior.rs:30:29
|
LL | debug_assert_eq!(1, 1, "{}",);
| ^^
error: 1 positional argument in format string, but no arguments were given
--> $DIR/macro-comma-behavior.rs:32:29
--> $DIR/macro-comma-behavior.rs:33:29
|
LL | debug_assert_ne!(1, 2, "{}",);
| ^^
error: 1 positional argument in format string, but no arguments were given
--> $DIR/macro-comma-behavior.rs:37:18
--> $DIR/macro-comma-behavior.rs:38:18
|
LL | eprint!("{}",);
| ^^
error: 1 positional argument in format string, but no arguments were given
--> $DIR/macro-comma-behavior.rs:49:18
--> $DIR/macro-comma-behavior.rs:50:18
|
LL | format!("{}",);
| ^^
error: 1 positional argument in format string, but no arguments were given
--> $DIR/macro-comma-behavior.rs:53:19
--> $DIR/macro-comma-behavior.rs:54:19
|
LL | format_args!("{}",);
| ^^
error: 1 positional argument in format string, but no arguments were given
--> $DIR/macro-comma-behavior.rs:60:17
--> $DIR/macro-comma-behavior.rs:61:17
|
LL | print!("{}",);
| ^^
error: 1 positional argument in format string, but no arguments were given
--> $DIR/macro-comma-behavior.rs:71:21
--> $DIR/macro-comma-behavior.rs:72:21
|
LL | unimplemented!("{}",);
| ^^
error: 1 positional argument in format string, but no arguments were given
--> $DIR/macro-comma-behavior.rs:80:24
--> $DIR/macro-comma-behavior.rs:81:24
|
LL | write!(f, "{}",)?;
| ^^

View File

@ -12,4 +12,5 @@ fn main() {
}
#[lang = "eh_personality"] extern fn eh_personality() {}
#[lang = "eh_catch_typeinfo"] static EH_CATCH_TYPEINFO: u8 = 0;
#[lang = "panic_impl"] fn panic_impl(panic: &PanicInfo) -> ! { loop {} }

View File

@ -11,3 +11,5 @@ use core::panic::PanicInfo;
fn panic_impl(info: &PanicInfo) -> ! { loop {} }
#[lang = "eh_personality"]
fn eh_personality() {}
#[lang = "eh_catch_typeinfo"]
static EH_CATCH_TYPEINFO: u8 = 0;

View File

@ -14,6 +14,9 @@ use core::ops::RangeBounds;
#[cfg(any(not(target_arch = "wasm32"), target_os = "emscripten"))]
#[lang = "eh_personality"]
extern fn eh_personality() {}
#[cfg(target_os = "emscripten")]
#[lang = "eh_catch_typeinfo"]
static EH_CATCH_TYPEINFO: u8 = 0;
// take a reference to any built-in range

View File

@ -1,7 +1,7 @@
error: `#[panic_handler]` function required, but not found
error[E0308]: mismatched types
--> $DIR/issue-54505-no-std.rs:24:16
--> $DIR/issue-54505-no-std.rs:27: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:29:16
--> $DIR/issue-54505-no-std.rs:32: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:34:16
--> $DIR/issue-54505-no-std.rs:37: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:39:16
--> $DIR/issue-54505-no-std.rs:42: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:44:16
--> $DIR/issue-54505-no-std.rs:47: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:49:16
--> $DIR/issue-54505-no-std.rs:52:16
|
LL | take_range(..=42);
| ^^^^^