Skip to content

Commit 317b01d

Browse files
author
Kyle Strand
committed
POF definition & links
1 parent 86b4697 commit 317b01d

File tree

1 file changed

+54
-32
lines changed

1 file changed

+54
-32
lines changed

rfcs/0000-c-unwind-abi.md

Lines changed: 54 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ Additionally, we define the behavior for a limited number of
1616
previously-undefined cases when an unwind operation reaches a Rust function
1717
boundary with a non-`"Rust"`, non-`"C unwind"` ABI.
1818

19+
As part of this specification, we introduce the term ["Plain Old Frame"
20+
(POF)][POF-definition]. These are frames that may be safely deallocated with
21+
`longjmp`.
22+
1923
This RFC does not define the behavior of `catch_unwind` in a Rust frame being
2024
unwound by a foreign exception. This is something the [project
2125
group][project-group] would like to specify in a future RFC; as such, it is
@@ -36,8 +40,8 @@ native exception mechanisms in GCC, LLVM, and MSVC.
3640
Additionally, there are libraries such as `rlua` that rely on `longjmp` across
3741
Rust frames; on Windows, `longjmp` is implemented via [forced
3842
unwinding][forced-unwinding]. The current `rustc` implementation makes it safe
39-
to `longjmp` across Rust frames without `Drop` types, but this is not formally
40-
specified in an RFC or by the Reference.
43+
to `longjmp` across Rust [POFs][POF-definition] (frames without `Drop` types),
44+
but this is not formally specified in an RFC or by the Reference.
4145

4246
The desire for this feature has been previously discussed on other RFCs,
4347
including [#2699][rfc-2699] and [#2753][rfc-2753].
@@ -74,8 +78,8 @@ how well the current design satisfies these constraints.
7478
* **Enable error handling with `longjmp`:** As mentioned above, some existing
7579
Rust libraries use `longjmp`. Despite the fact that `longjmp` on Windows is
7680
[technically a form of unwinding][forced-unwinding], using `longjmp` across
77-
Rust frames [is safe][longjmp-pr] with the current implementation of `rustc`,
78-
and we want to specify that this will remain safe.
81+
Rust [POFs][POF-definition] [is safe][longjmp-pr] with the current
82+
implementation of `rustc`, and we want to specify that this will remain safe.
7983
* **Do not change the ABI of functions in the `libc` crate:** Some `libc`
8084
functions may invoke `pthread_exit`, which uses [a form of
8185
unwinding][forced-unwinding] in the GNU libc implementation. Such functions
@@ -126,6 +130,28 @@ may be "sandwiched" between Rust frames, so that Rust `panic`s may safely
126130
unwind the C++ frames, if the Rust code declares both the C++ entrypoint and
127131
the Rust entrypoint using `"C unwind"`.
128132

133+
## "Plain Old Frames"
134+
[POF-definition]: #plain-old-frames
135+
136+
A "POF", or "Plain Old Frame", is defined as a frame that can be trivially
137+
deallocated; that is, returning from or unwinding a POF cannot cause any
138+
observable effects. This means that POFs do not contain any pending destructors
139+
(live `Drop` objects) or `catch_unwind` calls.
140+
141+
The terminology is intentionally akin to [C++'s "Plain Old Data"
142+
types][cpp-POD-definition], which are types that, among other requirements, are
143+
trivially destructible (their destructors do not cause any observable effects,
144+
and may be elided as an optimization).
145+
146+
Rust frames that do contain pending destructors or `catch_unwind` calls are
147+
called non-POFs.
148+
149+
Note that a non-POF may _become_ a POF, for instance if all `Drop` objects are
150+
moved out of scope, or if its only `catch_unwind` call is in a code path that
151+
will not be executed.
152+
153+
[cpp-POD-definition]: https://en.cppreference.com/w/cpp/named_req/PODType
154+
129155
## Forced unwinding
130156
[forced-unwinding]: #forced-unwinding
131157

@@ -143,16 +169,13 @@ deallocate Rust frames without true unwinding on other platforms.
143169
This RFC specifies that, regardless of the platform or the ABI string (`"C"` or
144170
`"C unwind"`), any platform features that may rely on forced unwinding are:
145171

146-
* _undefined behavior_ if they cross frames with destructors or a
147-
`catch_unwind` call
148-
* _defined behavior_ otherwise
172+
* _undefined behavior_ if they cross non-[POFs][POF-definition]
173+
* _defined behavior_ when all unwound frames are POFs
149174

150175
As an example, this means that Rust code can (indirectly, via C) invoke
151176
`longjmp` using the "C" ABI, and that `longjmp` can unwind or otherwise cross
152-
Rust frames, so long as those frames do not contain any pending destructors or
153-
make use of `catch_unwind`. If those Rust frames do contain pending
154-
destructors, then invoking `longjmp` would be undefined behavior (and hence a
155-
bug).
177+
Rust [POFs][POF-definition]. If those Rust frames are not POFs, then invoking
178+
`longjmp` would be undefined behavior (and hence a bug).
156179

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

@@ -167,8 +190,8 @@ This RFC retains most of that undefined behavior, with two exceptions:
167190

168191
* With the `panic=unwind` runtime, `panic!` will cause an `abort` if it would
169192
otherwise "escape" from a function defined with `extern "C"`.
170-
* Forced unwinding is safe with `extern "C"` as long as no frames with
171-
destructors (i.e. `Drop` types) are unwound. This is to keep behavior of
193+
* Forced unwinding is safe with `extern "C"` as long as only
194+
* [POFs][POF-definition] are unwound. This is to keep behavior of
172195
`pthread_exit` and `longjmp` consistent across platforms.
173196

174197
## Interaction with `panic=abort`
@@ -182,7 +205,7 @@ to abort with `panic=abort`, though:
182205

183206
* Forced unwinding: Rust provides no mechanism to catch this type of unwinding.
184207
This is safe with either the `"C"` ABI or the new `"C unwind"` ABI, as long
185-
as none of the unwound Rust frames contain destructors.
208+
as only [POFs][POF-definition] are unwound.
186209
* Unwinding from another language into Rust if the entrypoint to that language
187210
is declared with `extern "C"` (contrary to the guidelines above): this is
188211
always undefined behavior.
@@ -206,24 +229,25 @@ Regardless of the panic runtime, ABI, or platform, the interaction of Rust
206229
frames with C functions that deallocate frames (i.e. functions that may use
207230
forced unwinding on specific platforms) is specified as follows:
208231

209-
* **When deallocating Rust frames without destructors:** frames are safely
232+
* **When deallocating Rust [POFs][POF-definition]:** frames are safely
210233
deallocated; no undefined behavior
211-
* **When deallocating Rust frames with destructors:** undefined behavior
234+
* **When deallocating Rust non-POFs:** undefined behavior
212235

213236
No subtype relationship is defined between functions or function pointers using
214237
different ABIs. This RFC also does not define coercions between `"C"` and
215238
`"C unwind"`.
216239

217-
As noted in the [summary][summary], if a Rust frame containing `catch_unwind` is unwound by a
218-
foreign exception, the behavior is undefined for now.
240+
As noted in the [summary][summary], if a Rust frame containing a pending
241+
`catch_unwind` call is unwound by a foreign exception, the behavior is
242+
undefined for now.
219243

220244
# Drawbacks
221245
[drawbacks]: #drawbacks
222246

223-
Forced unwinding is treated as universally unsafe across frames with
224-
destructors, but on some platforms it could theoretically be well-defined. As
225-
noted [above](forced-unwind), however, this would make the UB inconsistent
226-
across platforms, which is not desirable.
247+
Forced unwinding is treated as universally unsafe across
248+
[non-POFs][POF-definition], but on some platforms it could theoretically be
249+
well-defined. As noted [above](forced-unwind), however, this would make the UB
250+
inconsistent across platforms, which is not desirable.
227251

228252
This design imposes some burden on existing codebases (mentioned
229253
[above][motivation]) to change their `extern` annotations to use the new ABI.
@@ -249,12 +273,12 @@ RFC is referred to as "option 2" in that post.
249273
behavior of a forced unwind across a `"C unwind"` boundary under `panic=abort`.
250274
Under the current proposal, this type of unwind is permitted, allowing
251275
`longjmp` and `pthread_exit` to behave "normally" with both the `"C"` and the
252-
`"C unwind"` ABI across all platforms regardless of panic runtime. If there are
253-
destructors in the unwound frames, this results in undefined behavior. Under
254-
"option 1", however, all foreign unwinding, forced or unforced, is caught at
255-
`"C unwind"` boundaries under `panic=abort`, and the process is aborted. This
256-
gives `longjmp` and `pthread_exit` surprising behavior on some platforms, but
257-
avoids that cause of undefined behavior in the current proposal.
276+
`"C unwind"` ABI across all platforms regardless of panic runtime. If
277+
[non-POFs][POF-definition] are unwound, this results in undefined behavior.
278+
Under "option 1", however, all foreign unwinding, forced or unforced, is caught
279+
at `"C unwind"` boundaries under `panic=abort`, and the process is aborted.
280+
This gives `longjmp` and `pthread_exit` surprising behavior on some platforms,
281+
but avoids that cause of undefined behavior in the current proposal.
258282

259283
The other proposal in the blog post, "option 3", is dramatically different. In
260284
that proposal, foreign exceptions are permitted to cross `extern "C"`
@@ -335,15 +359,13 @@ future work.
335359
### Enable error handling with `longjmp`
336360

337361
This constraint is met: `longjmp` is treated the same across all platforms, and
338-
is safe as long as the deallocated Rust frames do not contain pending `drop` or
339-
`catch_unwind` calls.
362+
is safe as long as only [POFs][POF-definition] are deallocated.
340363

341364
### Do not change the ABI of functions in the `libc` crate
342365

343366
This constraint is met: `libc` functions will continue to use the `"C"` ABI.
344367
`pthread_exit` will be treated the same across all platforms, and will be safe
345-
as long as the deallocated Rust frames do not contain pending `drop` or
346-
`catch_unwind` calls.
368+
as long as only [POFs][POF-definition] are deallocated.
347369

348370
# Prior art
349371
[prior-art]: #prior-art

0 commit comments

Comments
 (0)