| author | bors <bors@rust-lang.org> 2024-10-17 04:34:51 UTC |
| committer | bors <bors@rust-lang.org> 2024-10-17 04:34:51 UTC |
| log | 06d261daf62620e3449ba451898c7ad9ae750f41 |
| tree | 35b821155357b77c05b8bb049a9384b0f59addf1 |
| parent | dd5127615ad626741a1116d022cf784637ac05df |
| parent | bb531083cc0301d2152dc59746eb30472564948d |
Make destructors on `extern "C"` frames to be executed
This would make the example in #123231 print "Noisy Drop". I didn't mark this as fixing the issue because the behaviour is yet to be spec'ed.
Tracking:
- https://github.com/rust-lang/rust/issues/749908 files changed, 99 insertions(+), 17 deletions(-)
compiler/rustc_mir_transform/src/abort_unwinding_calls.rs+19-6| ... | ... | @@ -51,11 +51,20 @@ impl<'tcx> crate::MirPass<'tcx> for AbortUnwindingCalls { |
| 51 | 51 | // This will filter to functions with `extern "C-unwind"` ABIs, for |
| 52 | 52 | // example. |
| 53 | 53 | for block in body.basic_blocks.as_mut() { |
| 54 | let Some(terminator) = &mut block.terminator else { continue }; | |
| 55 | let span = terminator.source_info.span; | |
| 56 | ||
| 57 | // If we see an `UnwindResume` terminator inside a function that cannot unwind, we need | |
| 58 | // to replace it with `UnwindTerminate`. | |
| 59 | if let TerminatorKind::UnwindResume = &terminator.kind | |
| 60 | && !body_can_unwind | |
| 61 | { | |
| 62 | terminator.kind = TerminatorKind::UnwindTerminate(UnwindTerminateReason::Abi); | |
| 63 | } | |
| 64 | ||
| 54 | 65 | if block.is_cleanup { |
| 55 | 66 | continue; |
| 56 | 67 | } |
| 57 | let Some(terminator) = &block.terminator else { continue }; | |
| 58 | let span = terminator.source_info.span; | |
| 59 | 68 | |
| 60 | 69 | let call_can_unwind = match &terminator.kind { |
| 61 | 70 | TerminatorKind::Call { func, .. } => { |
| ... | ... | @@ -87,14 +96,18 @@ impl<'tcx> crate::MirPass<'tcx> for AbortUnwindingCalls { |
| 87 | 96 | if !call_can_unwind { |
| 88 | 97 | // If this function call can't unwind, then there's no need for it |
| 89 | 98 | // to have a landing pad. This means that we can remove any cleanup |
| 90 | // registered for it. | |
| 99 | // registered for it (and turn it into `UnwindAction::Unreachable`). | |
| 91 | 100 | let cleanup = block.terminator_mut().unwind_mut().unwrap(); |
| 92 | 101 | *cleanup = UnwindAction::Unreachable; |
| 93 | } else if !body_can_unwind { | |
| 102 | } else if !body_can_unwind | |
| 103 | && matches!(terminator.unwind(), Some(UnwindAction::Continue)) | |
| 104 | { | |
| 94 | 105 | // Otherwise if this function can unwind, then if the outer function |
| 95 | 106 | // can also unwind there's nothing to do. If the outer function |
| 96 | // can't unwind, however, we need to change the landing pad for this | |
| 97 | // function call to one that aborts. | |
| 107 | // can't unwind, however, we need to ensure that any `UnwindAction::Continue` | |
| 108 | // is replaced with terminate. For those with `UnwindAction::Cleanup`, | |
| 109 | // cleanup will still happen, and terminate will happen afterwards handled by | |
| 110 | // the `UnwindResume` -> `UnwindTerminate` terminator replacement. | |
| 98 | 111 | let cleanup = block.terminator_mut().unwind_mut().unwrap(); |
| 99 | 112 | *cleanup = UnwindAction::Terminate(UnwindTerminateReason::Abi); |
| 100 | 113 | } |
tests/mir-opt/asm_unwind_panic_abort.main.AbortUnwindingCalls.after.mir+5-1| ... | ... | @@ -7,7 +7,7 @@ fn main() -> () { |
| 7 | 7 | bb0: { |
| 8 | 8 | StorageLive(_1); |
| 9 | 9 | _1 = const (); |
| 10 | asm!("", options(MAY_UNWIND)) -> [return: bb1, unwind terminate(abi)]; | |
| 10 | asm!("", options(MAY_UNWIND)) -> [return: bb1, unwind: bb2]; | |
| 11 | 11 | } |
| 12 | 12 | |
| 13 | 13 | bb1: { |
| ... | ... | @@ -15,4 +15,8 @@ fn main() -> () { |
| 15 | 15 | _0 = const (); |
| 16 | 16 | return; |
| 17 | 17 | } |
| 18 | ||
| 19 | bb2 (cleanup): { | |
| 20 | terminate(abi); | |
| 21 | } | |
| 18 | 22 | } |
tests/mir-opt/asm_unwind_panic_abort.rs+3-1| ... | ... | @@ -10,7 +10,9 @@ |
| 10 | 10 | fn main() { |
| 11 | 11 | // CHECK-LABEL: fn main( |
| 12 | 12 | // CHECK: asm!( |
| 13 | // CHECK-SAME: unwind terminate(abi) | |
| 13 | // CHECK-SAME: unwind: [[unwind:bb.*]]] | |
| 14 | // CHECK: [[unwind]] (cleanup) | |
| 15 | // CHECK-NEXT: terminate(abi) | |
| 14 | 16 | unsafe { |
| 15 | 17 | std::arch::asm!("", options(may_unwind)); |
| 16 | 18 | } |
tests/mir-opt/c_unwind_terminate.rs created+25| ... | ... | @@ -0,0 +1,25 @@ |
| 1 | //@ needs-unwind | |
| 2 | ||
| 3 | struct Noise; | |
| 4 | impl Drop for Noise { | |
| 5 | fn drop(&mut self) { | |
| 6 | eprintln!("Noisy Drop"); | |
| 7 | } | |
| 8 | } | |
| 9 | ||
| 10 | fn panic() { | |
| 11 | panic!(); | |
| 12 | } | |
| 13 | ||
| 14 | // EMIT_MIR c_unwind_terminate.test.AbortUnwindingCalls.after.mir | |
| 15 | extern "C" fn test() { | |
| 16 | // CHECK-LABEL: fn test( | |
| 17 | // CHECK: drop | |
| 18 | // CHECK-SAME: unwind: [[unwind:bb.*]]] | |
| 19 | // CHECK: [[unwind]] (cleanup) | |
| 20 | // CHECK-NEXT: terminate(abi) | |
| 21 | let _val = Noise; | |
| 22 | panic(); | |
| 23 | } | |
| 24 | ||
| 25 | fn main() {} |
tests/mir-opt/c_unwind_terminate.test.AbortUnwindingCalls.after.mir created+36| ... | ... | @@ -0,0 +1,36 @@ |
| 1 | // MIR for `test` after AbortUnwindingCalls | |
| 2 | ||
| 3 | fn test() -> () { | |
| 4 | let mut _0: (); | |
| 5 | let _1: Noise; | |
| 6 | let _2: (); | |
| 7 | scope 1 { | |
| 8 | debug _val => _1; | |
| 9 | } | |
| 10 | ||
| 11 | bb0: { | |
| 12 | StorageLive(_1); | |
| 13 | _1 = Noise; | |
| 14 | StorageLive(_2); | |
| 15 | _2 = panic() -> [return: bb1, unwind: bb3]; | |
| 16 | } | |
| 17 | ||
| 18 | bb1: { | |
| 19 | StorageDead(_2); | |
| 20 | _0 = const (); | |
| 21 | drop(_1) -> [return: bb2, unwind: bb4]; | |
| 22 | } | |
| 23 | ||
| 24 | bb2: { | |
| 25 | StorageDead(_1); | |
| 26 | return; | |
| 27 | } | |
| 28 | ||
| 29 | bb3 (cleanup): { | |
| 30 | drop(_1) -> [return: bb4, unwind terminate(cleanup)]; | |
| 31 | } | |
| 32 | ||
| 33 | bb4 (cleanup): { | |
| 34 | terminate(abi); | |
| 35 | } | |
| 36 | } |
tests/run-make/longjmp-across-rust/main.rs-8| ... | ... | @@ -10,19 +10,11 @@ fn main() { |
| 10 | 10 | } |
| 11 | 11 | } |
| 12 | 12 | |
| 13 | struct A; | |
| 14 | ||
| 15 | impl Drop for A { | |
| 16 | fn drop(&mut self) {} | |
| 17 | } | |
| 18 | ||
| 19 | 13 | extern "C" fn test_middle() { |
| 20 | let _a = A; | |
| 21 | 14 | foo(); |
| 22 | 15 | } |
| 23 | 16 | |
| 24 | 17 | fn foo() { |
| 25 | let _a = A; | |
| 26 | 18 | unsafe { |
| 27 | 19 | test_end(); |
| 28 | 20 | } |
tests/ui/panics/panic-in-ffi.rs+9| ... | ... | @@ -2,13 +2,22 @@ |
| 2 | 2 | //@ exec-env:RUST_BACKTRACE=0 |
| 3 | 3 | //@ check-run-results |
| 4 | 4 | //@ error-pattern: panic in a function that cannot unwind |
| 5 | //@ error-pattern: Noisy Drop | |
| 5 | 6 | //@ normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "" |
| 6 | 7 | //@ normalize-stderr-test: "\n +at [^\n]+" -> "" |
| 7 | 8 | //@ normalize-stderr-test: "(core/src/panicking\.rs):[0-9]+:[0-9]+" -> "$1:$$LINE:$$COL" |
| 8 | 9 | //@ needs-unwind |
| 9 | 10 | //@ ignore-emscripten "RuntimeError" junk in output |
| 10 | 11 | |
| 12 | struct Noise; | |
| 13 | impl Drop for Noise { | |
| 14 | fn drop(&mut self) { | |
| 15 | eprintln!("Noisy Drop"); | |
| 16 | } | |
| 17 | } | |
| 18 | ||
| 11 | 19 | extern "C" fn panic_in_ffi() { |
| 20 | let _val = Noise; | |
| 12 | 21 | panic!("Test"); |
| 13 | 22 | } |
| 14 | 23 |
tests/ui/panics/panic-in-ffi.run.stderr+2-1| ... | ... | @@ -1,6 +1,7 @@ |
| 1 | thread 'main' panicked at $DIR/panic-in-ffi.rs:12:5: | |
| 1 | thread 'main' panicked at $DIR/panic-in-ffi.rs:21:5: | |
| 2 | 2 | Test |
| 3 | 3 | note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace |
| 4 | Noisy Drop | |
| 4 | 5 | thread 'main' panicked at core/src/panicking.rs:$LINE:$COL: |
| 5 | 6 | panic in a function that cannot unwind |
| 6 | 7 | stack backtrace: |