@@ -718,17 +718,118 @@ void register(int (*f)(int (*)(int), int)) {
718
718
719
719
No `transmute` required!
720
720
721
- ## FFI and panics
721
+ ## FFI and unwinding
722
722
723
- It’s important to be mindful of `panic!`s when working with FFI. A `panic!`
724
- across an FFI boundary is undefined behavior. If you’re writing code that may
725
- panic, you should run it in a closure with [`catch_unwind`]:
723
+ It’s important to be mindful of unwinding when working with FFI. Each
724
+ non-`Rust` ABI comes in two variants, one with `-unwind` suffix and one without. If
725
+ you expect Rust `panic`s or foreign (e.g. C++) exceptions to cross an FFI
726
+ boundary, that boundary must use the appropriate `-unwind` ABI string (note
727
+ that compiling with `panic=abort` will still cause `panic!` to immediately
728
+ abort the process, regardless of which ABI is specified by the function that
729
+ `panic`s).
730
+
731
+ Conversely, if you do not expect unwinding to cross an ABI boundary, use one of
732
+ the non-`unwind` ABI strings (other than `Rust`, which always permits
733
+ unwinding). If an unwinding operation does encounter an ABI boundary that is
734
+ not permitted to unwind, the behavior depends on the source of the unwinding
735
+ (Rust `panic` or a foreign exception):
736
+
737
+ * `panic` will cause the process to safely abort.
738
+ * A foreign exception entering Rust will cause undefined behavior.
739
+
740
+ Note that the interaction of `catch_unwind` with foreign exceptions **is
741
+ undefined**, as is the interaction of `panic` with foreign exception-catching
742
+ mechanisms (notably C++'s `try`/`catch`).
743
+
744
+ ### Rust `panic` with `"C-unwind"`
745
+
746
+ <!-- ignore: using unstable feature -->
747
+ ```rust,ignore
748
+ #[no_mangle]
749
+ extern "C-unwind" fn example() {
750
+ panic!("Uh oh");
751
+ }
752
+ ```
753
+
754
+ This function (when compiled with ` panic=unwind ` ) is permitted to unwind C++
755
+ stack frames.
756
+
757
+ ``` text
758
+ [Rust function with `catch_unwind`, which stops the unwinding]
759
+ |
760
+ ...
761
+ |
762
+ [C++ frames]
763
+ | ^
764
+ | (calls) | (unwinding
765
+ v | goes this
766
+ [Rust function `example`] | way)
767
+ | |
768
+ +--- rust function panics --+
769
+ ```
770
+
771
+ If the C++ frames have objects, their destructors will be called.
772
+
773
+ ### C++ ` throw ` with ` "C-unwind" `
774
+
775
+ <!-- ignore: using unstable feature -->
776
+ ``` rust,ignore
777
+ #[link(...)]
778
+ extern "C-unwind" {
779
+ // A C++ function that may throw an exception
780
+ fn may_throw();
781
+ }
782
+
783
+ #[no_mangle]
784
+ extern "C-unwind" fn rust_passthrough() {
785
+ let b = Box::new(5);
786
+ unsafe { may_throw(); }
787
+ println!("{:?}", &b);
788
+ }
789
+ ```
790
+
791
+ A C++ function with a ` try ` block may invoke ` rust_passthrough ` and ` catch ` an
792
+ exception thrown by ` may_throw ` .
793
+
794
+ ``` text
795
+ [C++ function with `try` block that invokes `rust_passthrough`]
796
+ |
797
+ ...
798
+ |
799
+ [Rust function `rust_passthrough`]
800
+ | ^
801
+ | (calls) | (unwinding
802
+ v | goes this
803
+ [C++ function `may_throw`] | way)
804
+ | |
805
+ +--- C++ function throws ----+
806
+ ```
807
+
808
+ If ` may_throw ` does throw an exception, ` b ` will be dropped. Otherwise, ` 5 `
809
+ will be printed.
810
+
811
+ ### ` panic ` can be stopped at an ABI boundary
812
+
813
+ ``` rust
814
+ #[no_mangle]
815
+ extern " C" fn assert_nonzero (input : u32 ) {
816
+ assert! (input != 0 )
817
+ }
818
+ ```
819
+
820
+ If ` assert_nonzero ` is called with the argument ` 0 ` , the runtime is guaranteed
821
+ to (safely) abort the process, whether or not compiled with ` panic=abort ` .
822
+
823
+ ### Catching ` panic ` preemptively
824
+
825
+ If you are writing Rust code that may panic, and you don't wish to abort the
826
+ process if it panics, you must use [ ` catch_unwind ` ] :
726
827
727
828
``` rust
728
829
use std :: panic :: catch_unwind;
729
830
730
831
#[no_mangle]
731
- pub extern fn oh_no() -> i32 {
832
+ pub extern " C " fn oh_no () -> i32 {
732
833
let result = catch_unwind (|| {
733
834
panic! (" Oops!" );
734
835
});
@@ -742,7 +843,7 @@ fn main() {}
742
843
```
743
844
744
845
Please note that [ ` catch_unwind ` ] will only catch unwinding panics, not
745
- those who abort the process. See the documentation of [ ` catch_unwind ` ]
846
+ those that abort the process. See the documentation of [ ` catch_unwind ` ]
746
847
for more information.
747
848
748
849
[ `catch_unwind` ] : ../std/panic/fn.catch_unwind.html
0 commit comments