Skip to content

Commit a727b49

Browse files
author
Kyle Strand
committed
POF example
1 parent e89faee commit a727b49

File tree

1 file changed

+31
-5
lines changed

1 file changed

+31
-5
lines changed

rfcs/0000-c-unwind-abi.md

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ called non-POFs.
151151

152152
Note that a non-POF may _become_ a POF, for instance if all `Drop` objects are
153153
moved out of scope, or if its only `catch_unwind` call is in a code path that
154-
will not be executed.
154+
will not be executed. The next section provides an example.
155155

156156
[cpp-POD-definition]: https://en.cppreference.com/w/cpp/named_req/PODType
157157

@@ -175,10 +175,36 @@ This RFC specifies that, regardless of the platform or the ABI string (`"C"` or
175175
* _undefined behavior_ if they cross non-[POFs][POF-definition]
176176
* _defined behavior_ when all unwound frames are POFs
177177

178-
As an example, this means that Rust code can (indirectly, via C) invoke
179-
`longjmp` using the "C" ABI, and that `longjmp` can unwind or otherwise cross
180-
Rust [POFs][POF-definition]. If those Rust frames are not POFs, then invoking
181-
`longjmp` would be undefined behavior (and hence a bug).
178+
As an example:
179+
180+
```rust
181+
fn foo<D: Drop>(c: bool, d: D) {
182+
if c {
183+
drop(d);
184+
}
185+
longjmp_if_true(c);
186+
}
187+
188+
/// Calls `longjmp` if `c` is true; otherwise returns normally.
189+
extern "C" fn longjmp_if_true(c: bool);
190+
```
191+
192+
If a `longjmp` occurs, it can safely traverse the `foo` frame, which will be a
193+
POF because `d` has already been dropped.
194+
195+
Since `longjmp_if_true` function is using the `"C"` rather than the `"C
196+
unwind"` ABI, the optimizer may assume that it cannot unwind; on LLVM, this is
197+
represented by the `nounwind` attribute. On most platforms, `longjmp` is not a
198+
form of unwinding: the `foo` frame is simply discarded. On Windows, `longjmp`
199+
is implemented as a forced unwind, which is permitted to traverse `nounwind`
200+
frames. Since `foo` contains a `Drop` type the forced unwind will include a
201+
call to the frame's cleanup logic, but that logic will not produce any
202+
observable effect; in particular, `D::drop()` will not be called again. The
203+
observable behavior should therefore be the same on all platforms.
204+
205+
Conversely, if, due to a bug, `longjmp` were called unconditionally, then this
206+
code would have undefined behavior on all platforms when `c` is false, because
207+
`foo` would not be a POF.
182208

183209
[inside-rust-forced]: https://blog.rust-lang.org/inside-rust/2020/02/27/ffi-unwind-design-meeting.html#forced-unwinding
184210

0 commit comments

Comments
 (0)