about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--library/panic_abort/src/lib.rs11
-rw-r--r--library/panic_unwind/src/dwarf/eh.rs16
-rw-r--r--library/panic_unwind/src/emcc.rs46
-rw-r--r--library/panic_unwind/src/gcc.rs26
-rw-r--r--library/panic_unwind/src/lib.rs3
-rw-r--r--library/panic_unwind/src/seh.rs12
-rw-r--r--library/std/src/panic.rs3
-rw-r--r--library/std/src/panicking.rs8
-rw-r--r--src/librustc_codegen_llvm/context.rs21
-rw-r--r--src/librustc_codegen_llvm/intrinsic.rs136
-rw-r--r--src/librustc_hir/weak_lang_items.rs1
-rw-r--r--src/librustc_middle/middle/lang_items.rs4
-rw-r--r--src/librustc_passes/weak_lang_items.rs3
-rw-r--r--src/librustc_span/symbol.rs1
-rw-r--r--src/test/compile-fail/auxiliary/panic-runtime-lang-items.rs2
-rw-r--r--src/test/run-make-fulldeps/foreign-exceptions/foo.cpp6
-rw-r--r--src/test/run-make-fulldeps/foreign-exceptions/foo.rs21
-rw-r--r--src/test/run-make-fulldeps/issue-69368/a.rs5
-rw-r--r--src/test/ui/consts/const-eval/const_panic_libcore_main.rs2
-rw-r--r--src/test/ui/macros/macro-comma-behavior.core.stderr14
-rw-r--r--src/test/ui/macros/macro-comma-behavior.rs1
-rw-r--r--src/test/ui/macros/macro-comma-behavior.std.stderr20
-rw-r--r--src/test/ui/no_owned_box_lang_item.rs1
-rw-r--r--src/test/ui/panic-runtime/auxiliary/panic-runtime-lang-items.rs2
-rw-r--r--src/test/ui/range/issue-54505-no-std.rs3
-rw-r--r--src/test/ui/range/issue-54505-no-std.stderr12
26 files changed, 275 insertions, 105 deletions
diff --git a/library/panic_abort/src/lib.rs b/library/panic_abort/src/lib.rs
index ccc067a3c94..8ca25da54a6 100644
--- a/library/panic_abort/src/lib.rs
+++ b/library/panic_abort/src/lib.rs
@@ -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]
diff --git a/library/panic_unwind/src/dwarf/eh.rs b/library/panic_unwind/src/dwarf/eh.rs
index 302478cfac8..8ce4dcd2acd 100644
--- a/library/panic_unwind/src/dwarf/eh.rs
+++ b/library/panic_unwind/src/dwarf/eh.rs
@@ -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)
diff --git a/library/panic_unwind/src/emcc.rs b/library/panic_unwind/src/emcc.rs
index a0bdb1481c6..e428f2fdaaa 100644
--- a/library/panic_unwind/src/emcc.rs
+++ b/library/panic_unwind/src/emcc.rs
@@ -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,
diff --git a/library/panic_unwind/src/gcc.rs b/library/panic_unwind/src/gcc.rs
index f5d83c21da0..85a2a18947d 100644
--- a/library/panic_unwind/src/gcc.rs
+++ b/library/panic_unwind/src/gcc.rs
@@ -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
diff --git a/library/panic_unwind/src/lib.rs b/library/panic_unwind/src/lib.rs
index 7d14893c4cc..ea296ee0bc3 100644
--- a/library/panic_unwind/src/lib.rs
+++ b/library/panic_unwind/src/lib.rs
@@ -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;
diff --git a/library/panic_unwind/src/seh.rs b/library/panic_unwind/src/seh.rs
index 1f812f8df61..eca169373f3 100644
--- a/library/panic_unwind/src/seh.rs
+++ b/library/panic_unwind/src/seh.rs
@@ -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
diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs
index 8fcb24033b1..87493945db6 100644
--- a/library/std/src/panic.rs
+++ b/library/std/src/panic.rs
@@ -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
 ///
 /// ```
diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs
index 08d363a9a29..8dceb12de87 100644
--- a/library/std/src/panicking.rs
+++ b/library/std/src/panicking.rs
@@ -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,
diff --git a/src/librustc_codegen_llvm/context.rs b/src/librustc_codegen_llvm/context.rs
index 26707fdf839..1c51a9df5d8 100644
--- a/src/librustc_codegen_llvm/context.rs
+++ b/src/librustc_codegen_llvm/context.rs
@@ -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> {
diff --git a/src/librustc_codegen_llvm/intrinsic.rs b/src/librustc_codegen_llvm/intrinsic.rs
index bb79a52dcf9..c1dfb83b135 100644
--- a/src/librustc_codegen_llvm/intrinsic.rs
+++ b/src/librustc_codegen_llvm/intrinsic.rs
@@ -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));
-
-        catchpad.catch_ret(&funclet, caught.llbb());
+        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());
+
+        // 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>(
diff --git a/src/librustc_hir/weak_lang_items.rs b/src/librustc_hir/weak_lang_items.rs
index 74e2a90262c..129eec7d29e 100644
--- a/src/librustc_hir/weak_lang_items.rs
+++ b/src/librustc_hir/weak_lang_items.rs
@@ -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;
 }
diff --git a/src/librustc_middle/middle/lang_items.rs b/src/librustc_middle/middle/lang_items.rs
index 3e1caa3b549..7194a035e89 100644
--- a/src/librustc_middle/middle/lang_items.rs
+++ b/src/librustc_middle/middle/lang_items.rs
@@ -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,
     }
 }
diff --git a/src/librustc_passes/weak_lang_items.rs b/src/librustc_passes/weak_lang_items.rs
index f559d66587b..6bc2110bfb3 100644
--- a/src/librustc_passes/weak_lang_items.rs
+++ b/src/librustc_passes/weak_lang_items.rs
@@ -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 };
diff --git a/src/librustc_span/symbol.rs b/src/librustc_span/symbol.rs
index 46612145bf0..5092b945f72 100644
--- a/src/librustc_span/symbol.rs
+++ b/src/librustc_span/symbol.rs
@@ -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,
diff --git a/src/test/compile-fail/auxiliary/panic-runtime-lang-items.rs b/src/test/compile-fail/auxiliary/panic-runtime-lang-items.rs
index 3e5cdad7ab9..b9ef2f32941 100644
--- a/src/test/compile-fail/auxiliary/panic-runtime-lang-items.rs
+++ b/src/test/compile-fail/auxiliary/panic-runtime-lang-items.rs
@@ -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;
diff --git a/src/test/run-make-fulldeps/foreign-exceptions/foo.cpp b/src/test/run-make-fulldeps/foreign-exceptions/foo.cpp
index b0fd65f88e7..8182021a2cc 100644
--- a/src/test/run-make-fulldeps/foreign-exceptions/foo.cpp
+++ b/src/test/run-make-fulldeps/foreign-exceptions/foo.cpp
@@ -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");
diff --git a/src/test/run-make-fulldeps/foreign-exceptions/foo.rs b/src/test/run-make-fulldeps/foreign-exceptions/foo.rs
index 9c2045c8c89..b5c8c1962a8 100644
--- a/src/test/run-make-fulldeps/foreign-exceptions/foo.rs
+++ b/src/test/run-make-fulldeps/foreign-exceptions/foo.rs
@@ -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();
 }
diff --git a/src/test/run-make-fulldeps/issue-69368/a.rs b/src/test/run-make-fulldeps/issue-69368/a.rs
index 726db874637..7d339c5a5da 100644
--- a/src/test/run-make-fulldeps/issue-69368/a.rs
+++ b/src/test/run-make-fulldeps/issue-69368/a.rs
@@ -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 {}
+}
diff --git a/src/test/ui/consts/const-eval/const_panic_libcore_main.rs b/src/test/ui/consts/const-eval/const_panic_libcore_main.rs
index 6b86feb5921..6b03e847def 100644
--- a/src/test/ui/consts/const-eval/const_panic_libcore_main.rs
+++ b/src/test/ui/consts/const-eval/const_panic_libcore_main.rs
@@ -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) -> ! {
diff --git a/src/test/ui/macros/macro-comma-behavior.core.stderr b/src/test/ui/macros/macro-comma-behavior.core.stderr
index 83a88ab3bd9..dd0cac659fd 100644
--- a/src/test/ui/macros/macro-comma-behavior.core.stderr
+++ b/src/test/ui/macros/macro-comma-behavior.core.stderr
@@ -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, "{}",)?;
    |                        ^^
diff --git a/src/test/ui/macros/macro-comma-behavior.rs b/src/test/ui/macros/macro-comma-behavior.rs
index 04714c65b5c..0bfe0683078 100644
--- a/src/test/ui/macros/macro-comma-behavior.rs
+++ b/src/test/ui/macros/macro-comma-behavior.rs
@@ -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)
diff --git a/src/test/ui/macros/macro-comma-behavior.std.stderr b/src/test/ui/macros/macro-comma-behavior.std.stderr
index 26445f2c5c5..4372d89fbf5 100644
--- a/src/test/ui/macros/macro-comma-behavior.std.stderr
+++ b/src/test/ui/macros/macro-comma-behavior.std.stderr
@@ -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, "{}",)?;
    |                        ^^
diff --git a/src/test/ui/no_owned_box_lang_item.rs b/src/test/ui/no_owned_box_lang_item.rs
index 58e45ff73a5..bef630d826c 100644
--- a/src/test/ui/no_owned_box_lang_item.rs
+++ b/src/test/ui/no_owned_box_lang_item.rs
@@ -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 {} }
diff --git a/src/test/ui/panic-runtime/auxiliary/panic-runtime-lang-items.rs b/src/test/ui/panic-runtime/auxiliary/panic-runtime-lang-items.rs
index 3e5cdad7ab9..b9ef2f32941 100644
--- a/src/test/ui/panic-runtime/auxiliary/panic-runtime-lang-items.rs
+++ b/src/test/ui/panic-runtime/auxiliary/panic-runtime-lang-items.rs
@@ -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;
diff --git a/src/test/ui/range/issue-54505-no-std.rs b/src/test/ui/range/issue-54505-no-std.rs
index c6a3cc346fc..f5d5823e468 100644
--- a/src/test/ui/range/issue-54505-no-std.rs
+++ b/src/test/ui/range/issue-54505-no-std.rs
@@ -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
diff --git a/src/test/ui/range/issue-54505-no-std.stderr b/src/test/ui/range/issue-54505-no-std.stderr
index 90934061132..5537ed45767 100644
--- a/src/test/ui/range/issue-54505-no-std.stderr
+++ b/src/test/ui/range/issue-54505-no-std.stderr
@@ -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);
    |                ^^^^^