Abort when catch_unwind catches a foreign exception
This commit is contained in:
parent
118860a7e7
commit
239f833ed1
26 changed files with 274 additions and 104 deletions
|
@ -117,6 +117,17 @@ pub mod personalities {
|
||||||
1 // `ExceptionContinueSearch`
|
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
|
// 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.
|
// they don't need to do anything so the bodies are nops.
|
||||||
#[rustc_std_internal_symbol]
|
#[rustc_std_internal_symbol]
|
||||||
|
|
|
@ -51,11 +51,7 @@ pub enum EHAction {
|
||||||
|
|
||||||
pub const USING_SJLJ_EXCEPTIONS: bool = cfg!(all(target_os = "ios", target_arch = "arm"));
|
pub const USING_SJLJ_EXCEPTIONS: bool = cfg!(all(target_os = "ios", target_arch = "arm"));
|
||||||
|
|
||||||
pub unsafe fn find_eh_action(
|
pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext<'_>) -> Result<EHAction, ()> {
|
||||||
lsda: *const u8,
|
|
||||||
context: &EHContext<'_>,
|
|
||||||
foreign_exception: bool,
|
|
||||||
) -> Result<EHAction, ()> {
|
|
||||||
if lsda.is_null() {
|
if lsda.is_null() {
|
||||||
return Ok(EHAction::None);
|
return Ok(EHAction::None);
|
||||||
}
|
}
|
||||||
|
@ -98,7 +94,7 @@ pub unsafe fn find_eh_action(
|
||||||
return Ok(EHAction::None);
|
return Ok(EHAction::None);
|
||||||
} else {
|
} else {
|
||||||
let lpad = lpad_base + cs_lpad;
|
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
|
// Can never have null landing pad for sjlj -- that would have
|
||||||
// been indicated by a -1 call site index.
|
// been indicated by a -1 call site index.
|
||||||
let lpad = (cs_lpad + 1) as usize;
|
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 == 0 {
|
||||||
// If cs_action is 0 then this is a cleanup (Drop::drop). We run these
|
// If cs_action is 0 then this is a cleanup (Drop::drop). We run these
|
||||||
// for both Rust panics and foreign exceptions.
|
// for both Rust panics and foreign exceptions.
|
||||||
EHAction::Cleanup(lpad)
|
EHAction::Cleanup(lpad)
|
||||||
} else if foreign_exception {
|
|
||||||
// catch_unwind should not catch foreign exceptions, only Rust panics.
|
|
||||||
// Instead just continue unwinding.
|
|
||||||
EHAction::None
|
|
||||||
} else {
|
} else {
|
||||||
// Stop unwinding Rust panics at catch_unwind.
|
// Stop unwinding Rust panics at catch_unwind.
|
||||||
EHAction::Catch(lpad)
|
EHAction::Catch(lpad)
|
||||||
|
|
|
@ -8,8 +8,10 @@
|
||||||
|
|
||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
use core::any::Any;
|
use core::any::Any;
|
||||||
|
use core::intrinsics;
|
||||||
use core::mem;
|
use core::mem;
|
||||||
use core::ptr;
|
use core::ptr;
|
||||||
|
use core::sync::atomic::{AtomicBool, Ordering};
|
||||||
use libc::{self, c_int};
|
use libc::{self, c_int};
|
||||||
use unwind as uw;
|
use unwind as uw;
|
||||||
|
|
||||||
|
@ -47,6 +49,11 @@ static EXCEPTION_TYPE_INFO: TypeInfo = TypeInfo {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Exception {
|
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++
|
// 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
|
// 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
|
// 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> {
|
pub unsafe fn cleanup(ptr: *mut u8) -> Box<dyn Any + Send> {
|
||||||
assert!(!ptr.is_null());
|
// intrinsics::try actually gives us a pointer to this structure.
|
||||||
let adjusted_ptr = __cxa_begin_catch(ptr as *mut libc::c_void) as *mut Exception;
|
#[repr(C)]
|
||||||
let ex = (*adjusted_ptr).data.take();
|
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();
|
__cxa_end_catch();
|
||||||
ex.unwrap()
|
out
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
|
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() {
|
if exception.is_null() {
|
||||||
return uw::_URC_FATAL_PHASE1_ERROR as u32;
|
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);
|
__cxa_throw(exception as *mut _, &EXCEPTION_TYPE_INFO, exception_cleanup);
|
||||||
}
|
}
|
||||||
|
|
||||||
// On WASM and ARM, the destructor returns the pointer to the object.
|
extern "C" fn exception_cleanup(ptr: *mut libc::c_void) -> *mut libc::c_void {
|
||||||
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 {
|
|
||||||
unsafe {
|
unsafe {
|
||||||
if let Some(b) = (ptr as *mut Exception).read().data {
|
if let Some(b) = (ptr as *mut Exception).read().data {
|
||||||
drop(b);
|
drop(b);
|
||||||
super::__rust_drop_panic();
|
super::__rust_drop_panic();
|
||||||
}
|
}
|
||||||
#[cfg(any(target_arch = "arm", target_arch = "wasm32"))]
|
|
||||||
ptr
|
ptr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,7 +123,7 @@ extern "C" {
|
||||||
fn __cxa_throw(
|
fn __cxa_throw(
|
||||||
thrown_exception: *mut libc::c_void,
|
thrown_exception: *mut libc::c_void,
|
||||||
tinfo: *const TypeInfo,
|
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(
|
fn __gxx_personality_v0(
|
||||||
version: c_int,
|
version: c_int,
|
||||||
|
|
|
@ -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> {
|
pub unsafe fn cleanup(ptr: *mut u8) -> Box<dyn Any + Send> {
|
||||||
let exception = Box::from_raw(ptr as *mut Exception);
|
let exception = ptr as *mut uw::_Unwind_Exception;
|
||||||
exception.cause
|
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
|
// 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
|
// _Unwind_Context in our libunwind bindings and fetch the required data from there
|
||||||
// directly, bypassing DWARF compatibility functions.
|
// directly, bypassing DWARF compatibility functions.
|
||||||
|
|
||||||
let exception_class = (*exception_object).exception_class;
|
let eh_action = match find_eh_action(context) {
|
||||||
let foreign_exception = exception_class != rust_exception_class();
|
|
||||||
let eh_action = match find_eh_action(context, foreign_exception) {
|
|
||||||
Ok(action) => action,
|
Ok(action) => action,
|
||||||
Err(_) => return uw::_URC_FAILURE,
|
Err(_) => return uw::_URC_FAILURE,
|
||||||
};
|
};
|
||||||
|
@ -221,15 +225,14 @@ cfg_if::cfg_if! {
|
||||||
// and indirectly on Windows x86_64 via SEH.
|
// and indirectly on Windows x86_64 via SEH.
|
||||||
unsafe extern "C" fn rust_eh_personality_impl(version: c_int,
|
unsafe extern "C" fn rust_eh_personality_impl(version: c_int,
|
||||||
actions: uw::_Unwind_Action,
|
actions: uw::_Unwind_Action,
|
||||||
exception_class: uw::_Unwind_Exception_Class,
|
_exception_class: uw::_Unwind_Exception_Class,
|
||||||
exception_object: *mut uw::_Unwind_Exception,
|
exception_object: *mut uw::_Unwind_Exception,
|
||||||
context: *mut uw::_Unwind_Context)
|
context: *mut uw::_Unwind_Context)
|
||||||
-> uw::_Unwind_Reason_Code {
|
-> uw::_Unwind_Reason_Code {
|
||||||
if version != 1 {
|
if version != 1 {
|
||||||
return uw::_URC_FATAL_PHASE1_ERROR;
|
return uw::_URC_FATAL_PHASE1_ERROR;
|
||||||
}
|
}
|
||||||
let foreign_exception = exception_class != rust_exception_class();
|
let eh_action = match find_eh_action(context) {
|
||||||
let eh_action = match find_eh_action(context, foreign_exception) {
|
|
||||||
Ok(action) => action,
|
Ok(action) => action,
|
||||||
Err(_) => return uw::_URC_FATAL_PHASE1_ERROR,
|
Err(_) => return uw::_URC_FATAL_PHASE1_ERROR,
|
||||||
};
|
};
|
||||||
|
@ -293,10 +296,7 @@ cfg_if::cfg_if! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn find_eh_action(
|
unsafe fn find_eh_action(context: *mut uw::_Unwind_Context) -> Result<EHAction, ()> {
|
||||||
context: *mut uw::_Unwind_Context,
|
|
||||||
foreign_exception: bool,
|
|
||||||
) -> Result<EHAction, ()> {
|
|
||||||
let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8;
|
let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8;
|
||||||
let mut ip_before_instr: c_int = 0;
|
let mut ip_before_instr: c_int = 0;
|
||||||
let ip = uw::_Unwind_GetIPInfo(context, &mut ip_before_instr);
|
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_text_start: &|| uw::_Unwind_GetTextRelBase(context),
|
||||||
get_data_start: &|| uw::_Unwind_GetDataRelBase(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
|
// Frame unwind info registration
|
||||||
|
|
|
@ -88,6 +88,9 @@ extern "C" {
|
||||||
/// Handler in libstd called when a panic object is dropped outside of
|
/// Handler in libstd called when a panic object is dropped outside of
|
||||||
/// `catch_unwind`.
|
/// `catch_unwind`.
|
||||||
fn __rust_drop_panic() -> !;
|
fn __rust_drop_panic() -> !;
|
||||||
|
|
||||||
|
/// Handler in libstd called when a foreign exception is caught.
|
||||||
|
fn __rust_foreign_exception() -> !;
|
||||||
}
|
}
|
||||||
|
|
||||||
mod dwarf;
|
mod dwarf;
|
||||||
|
|
|
@ -309,15 +309,21 @@ pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
|
||||||
|
|
||||||
extern "system" {
|
extern "system" {
|
||||||
#[unwind(allowed)]
|
#[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 _);
|
_CxxThrowException(throw_ptr, &mut THROW_INFO as *mut _ as *mut _);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn cleanup(payload: *mut u8) -> Box<dyn Any + Send> {
|
pub unsafe fn cleanup(payload: *mut u8) -> Box<dyn Any + Send> {
|
||||||
let exception = &mut *(payload as *mut Exception);
|
// A NULL payload here means that we got here from the catch (...) of
|
||||||
exception.data.take().unwrap()
|
// __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
|
// This is required by the compiler to exist (e.g., it's a lang item), but
|
||||||
|
|
|
@ -359,6 +359,9 @@ impl<F: Future> Future for AssertUnwindSafe<F> {
|
||||||
/// aborting the process as well. This function *only* catches unwinding panics,
|
/// aborting the process as well. This function *only* catches unwinding panics,
|
||||||
/// not those that abort the process.
|
/// 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
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
|
|
|
@ -60,6 +60,14 @@ extern "C" fn __rust_drop_panic() -> ! {
|
||||||
rtabort!("Rust panics must be rethrown");
|
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)]
|
#[derive(Copy, Clone)]
|
||||||
enum Hook {
|
enum Hook {
|
||||||
Default,
|
Default,
|
||||||
|
|
|
@ -82,6 +82,7 @@ pub struct CodegenCx<'ll, 'tcx> {
|
||||||
pub dbg_cx: Option<debuginfo::CrateDebugContext<'ll, 'tcx>>,
|
pub dbg_cx: Option<debuginfo::CrateDebugContext<'ll, 'tcx>>,
|
||||||
|
|
||||||
eh_personality: Cell<Option<&'ll Value>>,
|
eh_personality: Cell<Option<&'ll Value>>,
|
||||||
|
eh_catch_typeinfo: Cell<Option<&'ll Value>>,
|
||||||
pub rust_try_fn: Cell<Option<&'ll Value>>,
|
pub rust_try_fn: Cell<Option<&'ll Value>>,
|
||||||
|
|
||||||
intrinsics: RefCell<FxHashMap<&'static str, &'ll Value>>,
|
intrinsics: RefCell<FxHashMap<&'static str, &'ll Value>>,
|
||||||
|
@ -311,6 +312,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
|
||||||
coverage_cx,
|
coverage_cx,
|
||||||
dbg_cx,
|
dbg_cx,
|
||||||
eh_personality: Cell::new(None),
|
eh_personality: Cell::new(None),
|
||||||
|
eh_catch_typeinfo: Cell::new(None),
|
||||||
rust_try_fn: Cell::new(None),
|
rust_try_fn: Cell::new(None),
|
||||||
intrinsics: Default::default(),
|
intrinsics: Default::default(),
|
||||||
local_gen_sym_counter: Cell::new(0),
|
local_gen_sym_counter: Cell::new(0),
|
||||||
|
@ -819,6 +821,25 @@ impl CodegenCx<'b, 'tcx> {
|
||||||
}
|
}
|
||||||
None
|
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> {
|
impl<'b, 'tcx> CodegenCx<'b, 'tcx> {
|
||||||
|
|
|
@ -855,6 +855,8 @@ fn try_intrinsic(
|
||||||
bx.store(bx.const_i32(0), dest, ret_align);
|
bx.store(bx.const_i32(0), dest, ret_align);
|
||||||
} else if wants_msvc_seh(bx.sess()) {
|
} else if wants_msvc_seh(bx.sess()) {
|
||||||
codegen_msvc_try(bx, try_func, data, catch_func, dest);
|
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 {
|
} else {
|
||||||
codegen_gnu_try(bx, try_func, data, catch_func, dest);
|
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 normal = bx.build_sibling_block("normal");
|
||||||
let mut catchswitch = bx.build_sibling_block("catchswitch");
|
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 mut caught = bx.build_sibling_block("caught");
|
||||||
|
|
||||||
let try_func = llvm::get_param(bx.llfn(), 0);
|
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:
|
// We're generating an IR snippet that looks like:
|
||||||
//
|
//
|
||||||
// declare i32 @rust_try(%try_func, %data, %catch_func) {
|
// 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
|
// invoke %try_func(%data) to label %normal unwind label %catchswitch
|
||||||
//
|
//
|
||||||
// normal:
|
// normal:
|
||||||
// ret i32 0
|
// ret i32 0
|
||||||
//
|
//
|
||||||
// catchswitch:
|
// catchswitch:
|
||||||
// %cs = catchswitch within none [%catchpad] unwind to caller
|
// %cs = catchswitch within none [%catchpad_rust, %catchpad_foreign] unwind to caller
|
||||||
//
|
//
|
||||||
// catchpad:
|
// catchpad_rust:
|
||||||
// %tok = catchpad within %cs [%type_descriptor, 0, %slot]
|
// %tok = catchpad within %cs [%type_descriptor, 8, %slot]
|
||||||
// %ptr = load %slot
|
// %ptr = load %slot
|
||||||
// call %catch_func(%data, %ptr)
|
// call %catch_func(%data, %ptr)
|
||||||
// catchret from %tok to label %caught
|
// 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:
|
// caught:
|
||||||
// ret i32 1
|
// ret i32 1
|
||||||
// }
|
// }
|
||||||
|
@ -912,13 +920,11 @@ fn codegen_msvc_try(
|
||||||
// This structure follows the basic usage of throw/try/catch in LLVM.
|
// This structure follows the basic usage of throw/try/catch in LLVM.
|
||||||
// For example, compile this C++ snippet to see what LLVM generates:
|
// For example, compile this C++ snippet to see what LLVM generates:
|
||||||
//
|
//
|
||||||
// #include <stdint.h>
|
|
||||||
//
|
|
||||||
// struct rust_panic {
|
// struct rust_panic {
|
||||||
// rust_panic(const rust_panic&);
|
// rust_panic(const rust_panic&);
|
||||||
// ~rust_panic();
|
// ~rust_panic();
|
||||||
//
|
//
|
||||||
// uint64_t x[2];
|
// void* x[2];
|
||||||
// };
|
// };
|
||||||
//
|
//
|
||||||
// int __rust_try(
|
// int __rust_try(
|
||||||
|
@ -932,6 +938,9 @@ fn codegen_msvc_try(
|
||||||
// } catch(rust_panic& a) {
|
// } catch(rust_panic& a) {
|
||||||
// catch_func(data, &a);
|
// catch_func(data, &a);
|
||||||
// return 1;
|
// return 1;
|
||||||
|
// } catch(...) {
|
||||||
|
// catch_func(data, NULL);
|
||||||
|
// return 1;
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
|
@ -942,8 +951,9 @@ fn codegen_msvc_try(
|
||||||
|
|
||||||
normal.ret(bx.const_i32(0));
|
normal.ret(bx.const_i32(0));
|
||||||
|
|
||||||
let cs = catchswitch.catch_switch(None, None, 1);
|
let cs = catchswitch.catch_switch(None, None, 2);
|
||||||
catchswitch.add_handler(cs, catchpad.llbb());
|
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
|
// We can't use the TypeDescriptor defined in libpanic_unwind because it
|
||||||
// might be in another DLL and the SEH encoding only supports specifying
|
// 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
|
// Source: MicrosoftCXXABI::getAddrOfCXXCatchHandlerType in clang
|
||||||
let flags = bx.const_i32(8);
|
let flags = bx.const_i32(8);
|
||||||
let funclet = catchpad.catch_pad(cs, &[tydesc, flags, slot]);
|
let funclet = catchpad_rust.catch_pad(cs, &[tydesc, flags, slot]);
|
||||||
let ptr = catchpad.load(slot, ptr_align);
|
let ptr = catchpad_rust.load(slot, ptr_align);
|
||||||
catchpad.call(catch_func, &[data, ptr], Some(&funclet));
|
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));
|
caught.ret(bx.const_i32(1));
|
||||||
});
|
});
|
||||||
|
@ -1044,13 +1060,7 @@ fn codegen_gnu_try(
|
||||||
// rust_try ignores the selector.
|
// rust_try ignores the selector.
|
||||||
let lpad_ty = bx.type_struct(&[bx.type_i8p(), bx.type_i32()], false);
|
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 vals = catch.landing_pad(lpad_ty, bx.eh_personality(), 1);
|
||||||
let tydesc = match bx.tcx().lang_items().eh_catch_typeinfo() {
|
let tydesc = bx.const_null(bx.type_i8p());
|
||||||
Some(tydesc) => {
|
|
||||||
let tydesc = bx.get_static(tydesc);
|
|
||||||
bx.bitcast(tydesc, bx.type_i8p())
|
|
||||||
}
|
|
||||||
None => bx.const_null(bx.type_i8p()),
|
|
||||||
};
|
|
||||||
catch.add_clause(vals, tydesc);
|
catch.add_clause(vals, tydesc);
|
||||||
let ptr = catch.extract_value(vals, 0);
|
let ptr = catch.extract_value(vals, 0);
|
||||||
catch.call(catch_func, &[data, ptr], None);
|
catch.call(catch_func, &[data, ptr], None);
|
||||||
|
@ -1064,6 +1074,88 @@ fn codegen_gnu_try(
|
||||||
bx.store(ret, dest, i32_align);
|
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.
|
// 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.
|
// This is currently primarily used for the `try` intrinsic functions above.
|
||||||
fn gen_fn<'ll, 'tcx>(
|
fn gen_fn<'ll, 'tcx>(
|
||||||
|
|
|
@ -48,5 +48,6 @@ impl LanguageItems {
|
||||||
weak_lang_items! {
|
weak_lang_items! {
|
||||||
panic_impl, PanicImpl, rust_begin_unwind;
|
panic_impl, PanicImpl, rust_begin_unwind;
|
||||||
eh_personality, EhPersonality, rust_eh_personality;
|
eh_personality, EhPersonality, rust_eh_personality;
|
||||||
|
eh_catch_typeinfo, EhCatchTypeinfo, rust_eh_catch_typeinfo;
|
||||||
oom, Oom, rust_oom;
|
oom, Oom, rust_oom;
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,9 @@ pub fn required(tcx: TyCtxt<'_>, lang_item: LangItem) -> bool {
|
||||||
// symbols. Other panic runtimes ensure that the relevant symbols are
|
// symbols. Other panic runtimes ensure that the relevant symbols are
|
||||||
// available to link things together, but they're never exercised.
|
// available to link things together, but they're never exercised.
|
||||||
match tcx.sess.panic_strategy() {
|
match tcx.sess.panic_strategy() {
|
||||||
PanicStrategy::Abort => lang_item != LangItem::EhPersonality,
|
PanicStrategy::Abort => {
|
||||||
|
lang_item != LangItem::EhPersonality && lang_item != LangItem::EhCatchTypeinfo
|
||||||
|
}
|
||||||
PanicStrategy::Unwind => true,
|
PanicStrategy::Unwind => true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,9 @@ pub fn check_crate<'tcx>(tcx: TyCtxt<'tcx>, items: &mut lang_items::LanguageItem
|
||||||
if items.eh_personality().is_none() {
|
if items.eh_personality().is_none() {
|
||||||
items.missing.push(LangItem::EhPersonality);
|
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 };
|
let mut cx = Context { tcx, items };
|
||||||
|
|
|
@ -876,6 +876,7 @@ symbols! {
|
||||||
rust_2015_preview,
|
rust_2015_preview,
|
||||||
rust_2018_preview,
|
rust_2018_preview,
|
||||||
rust_begin_unwind,
|
rust_begin_unwind,
|
||||||
|
rust_eh_catch_typeinfo,
|
||||||
rust_eh_personality,
|
rust_eh_personality,
|
||||||
rust_eh_register_frames,
|
rust_eh_register_frames,
|
||||||
rust_eh_unregister_frames,
|
rust_eh_unregister_frames,
|
||||||
|
|
|
@ -11,3 +11,5 @@ use core::panic::PanicInfo;
|
||||||
fn panic_impl(info: &PanicInfo) -> ! { loop {} }
|
fn panic_impl(info: &PanicInfo) -> ! { loop {} }
|
||||||
#[lang = "eh_personality"]
|
#[lang = "eh_personality"]
|
||||||
fn eh_personality() {}
|
fn eh_personality() {}
|
||||||
|
#[lang = "eh_catch_typeinfo"]
|
||||||
|
static EH_CATCH_TYPEINFO: u8 = 0;
|
||||||
|
|
|
@ -23,15 +23,15 @@ struct drop_check {
|
||||||
extern "C" {
|
extern "C" {
|
||||||
void rust_catch_callback(void (*cb)(), bool* rust_ok);
|
void rust_catch_callback(void (*cb)(), bool* rust_ok);
|
||||||
|
|
||||||
static void callback() {
|
void throw_cxx_exception() {
|
||||||
println("throwing C++ exception");
|
println("throwing C++ exception");
|
||||||
throw exception();
|
throw exception();
|
||||||
}
|
}
|
||||||
|
|
||||||
void throw_cxx_exception() {
|
void test_cxx_exception() {
|
||||||
bool rust_ok = false;
|
bool rust_ok = false;
|
||||||
try {
|
try {
|
||||||
rust_catch_callback(callback, &rust_ok);
|
rust_catch_callback(throw_cxx_exception, &rust_ok);
|
||||||
assert(false && "unreachable");
|
assert(false && "unreachable");
|
||||||
} catch (exception e) {
|
} catch (exception e) {
|
||||||
println("caught C++ exception");
|
println("caught C++ exception");
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Tests that C++ exceptions can unwind through Rust code, run destructors and
|
// 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
|
// are caught by catch_unwind. Also tests that Rust panics can unwind through
|
||||||
// C++ code.
|
// C++ code.
|
||||||
|
|
||||||
// For linking libstdc++ on MinGW
|
// For linking libstdc++ on MinGW
|
||||||
|
@ -17,7 +17,7 @@ impl<'a> Drop for DropCheck<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
fn throw_cxx_exception();
|
fn test_cxx_exception();
|
||||||
|
|
||||||
#[unwind(allowed)]
|
#[unwind(allowed)]
|
||||||
fn cxx_catch_callback(cb: extern "C" fn(), ok: *mut bool);
|
fn cxx_catch_callback(cb: extern "C" fn(), ok: *mut bool);
|
||||||
|
@ -26,15 +26,12 @@ extern "C" {
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
#[unwind(allowed)]
|
#[unwind(allowed)]
|
||||||
extern "C" fn rust_catch_callback(cb: extern "C" fn(), rust_ok: &mut bool) {
|
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);
|
||||||
let _drop = DropCheck(rust_ok);
|
cb();
|
||||||
cb();
|
unreachable!("should have unwound instead of returned");
|
||||||
unreachable!("should have unwound instead of returned");
|
|
||||||
}));
|
|
||||||
unreachable!("catch_unwind should not have caught foreign exception");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn throw_rust_panic() {
|
fn test_rust_panic() {
|
||||||
#[unwind(allowed)]
|
#[unwind(allowed)]
|
||||||
extern "C" fn callback() {
|
extern "C" fn callback() {
|
||||||
println!("throwing rust panic");
|
println!("throwing rust panic");
|
||||||
|
@ -60,6 +57,6 @@ fn throw_rust_panic() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
unsafe { throw_cxx_exception() };
|
unsafe { test_cxx_exception() };
|
||||||
throw_rust_panic();
|
test_rust_panic();
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,3 +14,8 @@ pub fn panic_handler(_: &core::panic::PanicInfo) -> ! {
|
||||||
extern "C" fn __rust_drop_panic() -> ! {
|
extern "C" fn __rust_drop_panic() -> ! {
|
||||||
loop {}
|
loop {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
extern "C" fn __rust_foreign_exception() -> ! {
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
|
|
|
@ -17,6 +17,8 @@ const X: () = unimplemented!();
|
||||||
|
|
||||||
#[lang = "eh_personality"]
|
#[lang = "eh_personality"]
|
||||||
fn eh() {}
|
fn eh() {}
|
||||||
|
#[lang = "eh_catch_typeinfo"]
|
||||||
|
static EH_CATCH_TYPEINFO: u8 = 0;
|
||||||
|
|
||||||
#[panic_handler]
|
#[panic_handler]
|
||||||
fn panic(_info: &PanicInfo) -> ! {
|
fn panic(_info: &PanicInfo) -> ! {
|
||||||
|
|
|
@ -1,41 +1,41 @@
|
||||||
error: 1 positional argument in format string, but no arguments were given
|
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, "{}",);
|
LL | assert_eq!(1, 1, "{}",);
|
||||||
| ^^
|
| ^^
|
||||||
|
|
||||||
error: 1 positional argument in format string, but no arguments were given
|
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, "{}",);
|
LL | assert_ne!(1, 2, "{}",);
|
||||||
| ^^
|
| ^^
|
||||||
|
|
||||||
error: 1 positional argument in format string, but no arguments were given
|
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, "{}",);
|
LL | debug_assert_eq!(1, 1, "{}",);
|
||||||
| ^^
|
| ^^
|
||||||
|
|
||||||
error: 1 positional argument in format string, but no arguments were given
|
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, "{}",);
|
LL | debug_assert_ne!(1, 2, "{}",);
|
||||||
| ^^
|
| ^^
|
||||||
|
|
||||||
error: 1 positional argument in format string, but no arguments were given
|
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!("{}",);
|
LL | format_args!("{}",);
|
||||||
| ^^
|
| ^^
|
||||||
|
|
||||||
error: 1 positional argument in format string, but no arguments were given
|
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!("{}",);
|
LL | unimplemented!("{}",);
|
||||||
| ^^
|
| ^^
|
||||||
|
|
||||||
error: 1 positional argument in format string, but no arguments were given
|
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, "{}",)?;
|
LL | write!(f, "{}",)?;
|
||||||
| ^^
|
| ^^
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#[cfg(std)] use std::fmt;
|
#[cfg(std)] use std::fmt;
|
||||||
#[cfg(core)] use core::fmt;
|
#[cfg(core)] use core::fmt;
|
||||||
#[cfg(core)] #[lang = "eh_personality"] fn eh_personality() {}
|
#[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 {} }
|
#[cfg(core)] #[lang = "panic_impl"] fn panic_impl(panic: &core::panic::PanicInfo) -> ! { loop {} }
|
||||||
|
|
||||||
// (see documentation of the similarly-named test in run-pass)
|
// (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
|
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, "{}",);
|
LL | assert_eq!(1, 1, "{}",);
|
||||||
| ^^
|
| ^^
|
||||||
|
|
||||||
error: 1 positional argument in format string, but no arguments were given
|
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, "{}",);
|
LL | assert_ne!(1, 2, "{}",);
|
||||||
| ^^
|
| ^^
|
||||||
|
|
||||||
error: 1 positional argument in format string, but no arguments were given
|
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, "{}",);
|
LL | debug_assert_eq!(1, 1, "{}",);
|
||||||
| ^^
|
| ^^
|
||||||
|
|
||||||
error: 1 positional argument in format string, but no arguments were given
|
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, "{}",);
|
LL | debug_assert_ne!(1, 2, "{}",);
|
||||||
| ^^
|
| ^^
|
||||||
|
|
||||||
error: 1 positional argument in format string, but no arguments were given
|
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!("{}",);
|
LL | eprint!("{}",);
|
||||||
| ^^
|
| ^^
|
||||||
|
|
||||||
error: 1 positional argument in format string, but no arguments were given
|
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!("{}",);
|
LL | format!("{}",);
|
||||||
| ^^
|
| ^^
|
||||||
|
|
||||||
error: 1 positional argument in format string, but no arguments were given
|
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!("{}",);
|
LL | format_args!("{}",);
|
||||||
| ^^
|
| ^^
|
||||||
|
|
||||||
error: 1 positional argument in format string, but no arguments were given
|
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!("{}",);
|
LL | print!("{}",);
|
||||||
| ^^
|
| ^^
|
||||||
|
|
||||||
error: 1 positional argument in format string, but no arguments were given
|
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!("{}",);
|
LL | unimplemented!("{}",);
|
||||||
| ^^
|
| ^^
|
||||||
|
|
||||||
error: 1 positional argument in format string, but no arguments were given
|
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, "{}",)?;
|
LL | write!(f, "{}",)?;
|
||||||
| ^^
|
| ^^
|
||||||
|
|
|
@ -12,4 +12,5 @@ fn main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[lang = "eh_personality"] extern fn eh_personality() {}
|
#[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 {} }
|
#[lang = "panic_impl"] fn panic_impl(panic: &PanicInfo) -> ! { loop {} }
|
||||||
|
|
|
@ -11,3 +11,5 @@ use core::panic::PanicInfo;
|
||||||
fn panic_impl(info: &PanicInfo) -> ! { loop {} }
|
fn panic_impl(info: &PanicInfo) -> ! { loop {} }
|
||||||
#[lang = "eh_personality"]
|
#[lang = "eh_personality"]
|
||||||
fn eh_personality() {}
|
fn eh_personality() {}
|
||||||
|
#[lang = "eh_catch_typeinfo"]
|
||||||
|
static EH_CATCH_TYPEINFO: u8 = 0;
|
||||||
|
|
|
@ -14,6 +14,9 @@ use core::ops::RangeBounds;
|
||||||
#[cfg(any(not(target_arch = "wasm32"), target_os = "emscripten"))]
|
#[cfg(any(not(target_arch = "wasm32"), target_os = "emscripten"))]
|
||||||
#[lang = "eh_personality"]
|
#[lang = "eh_personality"]
|
||||||
extern fn 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
|
// take a reference to any built-in range
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
error: `#[panic_handler]` function required, but not found
|
error: `#[panic_handler]` function required, but not found
|
||||||
|
|
||||||
error[E0308]: mismatched types
|
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);
|
LL | take_range(0..1);
|
||||||
| ^^^^
|
| ^^^^
|
||||||
|
@ -13,7 +13,7 @@ LL | take_range(0..1);
|
||||||
found struct `core::ops::Range<{integer}>`
|
found struct `core::ops::Range<{integer}>`
|
||||||
|
|
||||||
error[E0308]: mismatched types
|
error[E0308]: mismatched types
|
||||||
--> $DIR/issue-54505-no-std.rs:29:16
|
--> $DIR/issue-54505-no-std.rs:32:16
|
||||||
|
|
|
|
||||||
LL | take_range(1..);
|
LL | take_range(1..);
|
||||||
| ^^^
|
| ^^^
|
||||||
|
@ -25,7 +25,7 @@ LL | take_range(1..);
|
||||||
found struct `core::ops::RangeFrom<{integer}>`
|
found struct `core::ops::RangeFrom<{integer}>`
|
||||||
|
|
||||||
error[E0308]: mismatched types
|
error[E0308]: mismatched types
|
||||||
--> $DIR/issue-54505-no-std.rs:34:16
|
--> $DIR/issue-54505-no-std.rs:37:16
|
||||||
|
|
|
|
||||||
LL | take_range(..);
|
LL | take_range(..);
|
||||||
| ^^
|
| ^^
|
||||||
|
@ -37,7 +37,7 @@ LL | take_range(..);
|
||||||
found struct `core::ops::RangeFull`
|
found struct `core::ops::RangeFull`
|
||||||
|
|
||||||
error[E0308]: mismatched types
|
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);
|
LL | take_range(0..=1);
|
||||||
| ^^^^^
|
| ^^^^^
|
||||||
|
@ -49,7 +49,7 @@ LL | take_range(0..=1);
|
||||||
found struct `core::ops::RangeInclusive<{integer}>`
|
found struct `core::ops::RangeInclusive<{integer}>`
|
||||||
|
|
||||||
error[E0308]: mismatched types
|
error[E0308]: mismatched types
|
||||||
--> $DIR/issue-54505-no-std.rs:44:16
|
--> $DIR/issue-54505-no-std.rs:47:16
|
||||||
|
|
|
|
||||||
LL | take_range(..5);
|
LL | take_range(..5);
|
||||||
| ^^^
|
| ^^^
|
||||||
|
@ -61,7 +61,7 @@ LL | take_range(..5);
|
||||||
found struct `core::ops::RangeTo<{integer}>`
|
found struct `core::ops::RangeTo<{integer}>`
|
||||||
|
|
||||||
error[E0308]: mismatched types
|
error[E0308]: mismatched types
|
||||||
--> $DIR/issue-54505-no-std.rs:49:16
|
--> $DIR/issue-54505-no-std.rs:52:16
|
||||||
|
|
|
|
||||||
LL | take_range(..=42);
|
LL | take_range(..=42);
|
||||||
| ^^^^^
|
| ^^^^^
|
||||||
|
|
Loading…
Add table
Reference in a new issue