@@ -16,6 +16,10 @@ Additionally, we define the behavior for a limited number of
16
16
previously-undefined cases when an unwind operation reaches a Rust function
17
17
boundary with a non-` "Rust" ` , non-` "C unwind" ` ABI.
18
18
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
+
19
23
This RFC does not define the behavior of ` catch_unwind ` in a Rust frame being
20
24
unwound by a foreign exception. This is something the [ project
21
25
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.
36
40
Additionally, there are libraries such as ` rlua ` that rely on ` longjmp ` across
37
41
Rust frames; on Windows, ` longjmp ` is implemented via [ forced
38
42
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.
41
45
42
46
The desire for this feature has been previously discussed on other RFCs,
43
47
including [ #2699 ] [ rfc-2699 ] and [ #2753 ] [ rfc-2753 ] .
@@ -74,8 +78,8 @@ how well the current design satisfies these constraints.
74
78
* ** Enable error handling with ` longjmp ` :** As mentioned above, some existing
75
79
Rust libraries use ` longjmp ` . Despite the fact that ` longjmp ` on Windows is
76
80
[ 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.
79
83
* ** Do not change the ABI of functions in the ` libc ` crate:** Some ` libc `
80
84
functions may invoke ` pthread_exit ` , which uses [ a form of
81
85
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
126
130
unwind the C++ frames, if the Rust code declares both the C++ entrypoint and
127
131
the Rust entrypoint using ` "C unwind" ` .
128
132
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
+
129
155
## Forced unwinding
130
156
[ forced-unwinding ] : #forced-unwinding
131
157
@@ -143,16 +169,13 @@ deallocate Rust frames without true unwinding on other platforms.
143
169
This RFC specifies that, regardless of the platform or the ABI string (` "C" ` or
144
170
` "C unwind" ` ), any platform features that may rely on forced unwinding are:
145
171
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
149
174
150
175
As an example, this means that Rust code can (indirectly, via C) invoke
151
176
` 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).
156
179
157
180
[ inside-rust-forced ] : https://blog.rust-lang.org/inside-rust/2020/02/27/ffi-unwind-design-meeting.html#forced-unwinding
158
181
@@ -167,8 +190,8 @@ This RFC retains most of that undefined behavior, with two exceptions:
167
190
168
191
* With the ` panic=unwind ` runtime, ` panic! ` will cause an ` abort ` if it would
169
192
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
172
195
` pthread_exit ` and ` longjmp ` consistent across platforms.
173
196
174
197
## Interaction with ` panic=abort `
@@ -182,7 +205,7 @@ to abort with `panic=abort`, though:
182
205
183
206
* Forced unwinding: Rust provides no mechanism to catch this type of unwinding.
184
207
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.
186
209
* Unwinding from another language into Rust if the entrypoint to that language
187
210
is declared with ` extern "C" ` (contrary to the guidelines above): this is
188
211
always undefined behavior.
@@ -206,24 +229,25 @@ Regardless of the panic runtime, ABI, or platform, the interaction of Rust
206
229
frames with C functions that deallocate frames (i.e. functions that may use
207
230
forced unwinding on specific platforms) is specified as follows:
208
231
209
- * ** When deallocating Rust frames without destructors :** frames are safely
232
+ * ** When deallocating Rust [ POFs ] [ POF-definition ] :** frames are safely
210
233
deallocated; no undefined behavior
211
- * ** When deallocating Rust frames with destructors :** undefined behavior
234
+ * ** When deallocating Rust non-POFs :** undefined behavior
212
235
213
236
No subtype relationship is defined between functions or function pointers using
214
237
different ABIs. This RFC also does not define coercions between ` "C" ` and
215
238
` "C unwind" ` .
216
239
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.
219
243
220
244
# Drawbacks
221
245
[ drawbacks ] : #drawbacks
222
246
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.
227
251
228
252
This design imposes some burden on existing codebases (mentioned
229
253
[ 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.
249
273
behavior of a forced unwind across a ` "C unwind" ` boundary under ` panic=abort ` .
250
274
Under the current proposal, this type of unwind is permitted, allowing
251
275
` 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.
258
282
259
283
The other proposal in the blog post, "option 3", is dramatically different. In
260
284
that proposal, foreign exceptions are permitted to cross ` extern "C" `
@@ -335,15 +359,13 @@ future work.
335
359
### Enable error handling with ` longjmp `
336
360
337
361
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.
340
363
341
364
### Do not change the ABI of functions in the ` libc ` crate
342
365
343
366
This constraint is met: ` libc ` functions will continue to use the ` "C" ` ABI.
344
367
` 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.
347
369
348
370
# Prior art
349
371
[ prior-art ] : #prior-art
0 commit comments