-
Notifications
You must be signed in to change notification settings - Fork 486
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
panic runtime and C-unwind documentation #1226
base: master
Are you sure you want to change the base?
Conversation
Hm... not sure how to fix the links to the newly-introduced page. Is there an index page I need to edit? Edit: I think I found it |
src/items/functions.md
Outdated
| panic runtime | ABI | `panic`-unwind | Unforced foreign unwind | | ||
| -------------- | ------------ | ------------------------------------- | ----------------------- | | ||
| `panic=unwind` | `"C-unwind"` | unwind | unwind | | ||
| `panic=unwind` | `"C"` | abort | UB | | ||
| `panic=abort` | `"C-unwind"` | `panic!` aborts | abort | | ||
| `panic=abort` | `"C"` | `panic!` aborts (no unwinding occurs) | UB | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| panic runtime | ABI | `panic`-unwind | Unforced foreign unwind | | |
| -------------- | ------------ | ------------------------------------- | ----------------------- | | |
| `panic=unwind` | `"C-unwind"` | unwind | unwind | | |
| `panic=unwind` | `"C"` | abort | UB | | |
| `panic=abort` | `"C-unwind"` | `panic!` aborts | abort | | |
| `panic=abort` | `"C"` | `panic!` aborts (no unwinding occurs) | UB | | |
| panic runtime | ABI | `panic`-unwind | Unforced foreign unwind | | |
| -------------- | ------------ | ------------------------------------- | ----------------------- | | |
| `panic=unwind` | `"C-unwind"` | unwind | unwind | | |
| `panic=unwind` | `"C"` | abort if unwinding reaches the function | UB if unwinding reaches the function | | |
| `panic=abort` | `"C-unwind"` | aborts immediately (no unwinding occurs) | abort if unwinding reaches the function | | |
| `panic=abort` | `"C"` | aborts immediately (no unwinding occurs) | UB if unwinding reaches the function | |
I found this a bit confusing. I believe there are subtle differences in terms of where the aborts occur and so forth. I have tried to clarify above, but I think it may be worth further clarifying.
It may also be worth adding some (perhaps non-normative) discussion of implementation:
- When compiling a function F with
panic=unwind
andextern "C"
, the compiler inserts unwinding guards for Rust panics that trigger an abort when unwinding reaches F.
I am also be misunderstanding what's going on. I was a bit surprised to see "UB" for unforced-foreign-unwind with C=unwind. I guess that this table is combining two scenarios:
- what happens when you call a C++ function declared as extern "C", and it unwinds (UB, we haven't compiled any guards)
- what happens when an
extern "C"
Rust function invokes some C++ function that throws (probably, in practice, an abort, but perhaps we have simplified to call it UB?)
Is that right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's only UB for a foreign function declared as extern "C"
to unwind.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nbdd0121 what happens when an extern "C"
Rust function unwinds? I believe we insert an abort guard, but this table doesn't clarify that, right? Or maybe I don't understand what it's trying to convey. I'm imagining a scenario like
extern "C-unwind" fn throws();
extern "C" fn rust_fn() {
throws(); // unwinds
}
In this case, I presume you get an abort -- and I think we guarantee that? But the way I read this table, it would be listed as UB.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm....I don't know if the panic
abort guard would currently catch and abort in that case, or if it relies on the personality function to only abort on true Rust panic
s. I agree that the behavior in the table as-written is UB.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nbdd0121 what happens when an
extern "C"
Rust function unwinds? I believe we insert an abort guard, but this table doesn't clarify that, right? Or maybe I don't understand what it's trying to convey. I'm imagining a scenario likeextern "C-unwind" fn throws(); extern "C" fn rust_fn() { throws(); // unwinds }In this case, I presume you get an abort -- and I think we guarantee that? But the way I read this table, it would be listed as UB.
Unwinding out from extern "C"
functions (defined in either Rust or foreign language) is UB.
In the case you listed, we insert guard to prevent unwinding from actually leaving a Rust extern "C"
functions, therefore the function does not unwind, so UB is prevented; in this case we never unwinds out from a extern "C"
Rust functions.
If you define a extern "C-unwind"
Rust function and transmute it to extern "C"
and then call it, it's not UB if unwinding does not happen, and it's UB if unwinding happens.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nikomatsakis With the change to the verbiage above, explaining that the table entries are specifically describing behavior at function boundaries, do you still want to make a change here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please check whether the notes I suggested to add under the table are correct.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nikomatsakis Can I resolve this comment thread now?
Sorry for the delay; I think I've addressed all comments. |
@tmandry @nikomatsakis I'm not sure you saw my comments & changes last week, but I think this is ready for re-review. |
Could you squash the commits? |
@nbdd0121 Can that be done on merge? I've heard that GitHub sometimes has trouble with PR branches that receive force-pushes. |
I think I've resolved all open questions and concerns. Is there anything else needed from me at the moment? |
This really needs rebasing now. |
e8c62b4
to
6e83797
Compare
@nbdd0121 Done! |
@tmandry two changes since your review:
|
This looks reasonable to me, though I'm also relying on the strength of the many earlier reviews. Last call for comments. Otherwise we'll get this merged in the coming days. |
6b624fe
to
e57a0cd
Compare
2c341e6
to
e8fd24d
Compare
No crate may be linked with [the `panic=abort` runtime][panic-runtime] if it has | ||
both of the following characteristics: | ||
|
||
* It contains a call to an `-unwind` foreign function or function pointer |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This includes "Rust" foreign function calls, right? Like a separate copy of the Rust runtime, built with the same compiler and hence ABI compatible.
In the other file you carefully define unwinding and non-unwinding ABIs; can that be reused here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I'm not sure what you're asking with regard to runtimes. This is talking about the ABI strings C-unwind
, system-unwind
, etc; yes, the code being invoked may be Rust code.
What verbiage are you suggesting be reused? I don't think listing all the ABI strings seems necessary, but I can add a link to the functions.md
section explaining unwinding vs non-unwinding ABIs, with the caveat that the Rust ABI itself doesn't prevent linking crates with different panic strategies.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm saying that this needs to also include extern "Rust" fn
foreign function calls, but the current wording does not seem to cover those functions. I'm not sure what is the best way to fix that, but functions.md
introduces the concept of an ""unwinding ABI" and that seems to also be the right concept here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, Rust calls are specifically excluded here; fn
is entirely equivalent to extern "Rust" fn
. So including extern "Rust"
here would effectively mean "it's never legal to link panic=abort
crates against panic=unwind
crates," which is clearly not true.
... possibly I am still misunderstanding the question?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I see. What makes "Rust" different from the other unwinding APIs?
IIRC the argument was that if we link in the abort runtime, Rust itself will never trigger unwinding, so if your program has no other "*-unwind" functions anywhere (meaning there's no foreign-language unwinding) then there can be no unwinding in general?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's not guaranteed to work, but yes, in theory you can do it, and it may even work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, if people do it and not making sure panic agrees on both side, then it's not any different than using extern "Rust"
but doesn't make sure ABI on both side match, e.g. different target feature. Also, this was unsound forever and not anything new due to c_unwind
. -Cpanic=abort
will make compiler assume extern "Rust"
won't panic as early as Rust 1.23.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, this was unsound forever and not anything new due to c_unwind. -Cpanic=abort will make compiler assume extern "Rust" won't panic as early as Rust 1.23.
Absolutely, but now we're finally writing docs for all this so it seems odd to skip this one?
But okay, in the interest of unblocking this PR, maybe let's make this an issue then: #1642.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The text here certainly shouldn't be modified to add extern "Rust"
, because the ffi_unwind_calls
lint (in the paragraph below) works as described and does not prevent using the extern "Rust"
ABI.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would be okay with adding this sentence to the beginning of the "prohibited linkage scenarios" section, but I'm not 100% confident that it is accurate:
It is never valid to link code against both [panic-runtimes][panic-runtime]; linkers will often reject attempts to do so by emitting a multiple-definitions error, but if a binary is successfully produced, any panic will result in undefined behavior.
In particular, I don't know what the right terminology is to distinguish these two types of linking:
- the same code is "linked against" both panic runtimes, in the sense that they both provide candidate definitions for the same symbol lookup at link-time (i.e., the situation I'm saying is never allowed, and which will tend to trigger multiple-definition errors)
- the situation @RalfJung described above: "a dylib...with -Cpanic=abort [loaded] into a Rust binary that was built with -Cpanic=unwind... The dylib statically contains a Rust runtime, the main binary statically contains another independent Rust runtime."
78717a6
to
d53e2c1
Compare
… r=Mark-Simulacrum Update `catch_unwind` doc comments for `c_unwind` Updates `catch_unwind` doc comments to indicate that catching a foreign exception _will no longer_ be UB. Instead, there are two possible behaviors, though it is not specified which one an implementation will choose. Nominated for t-lang to confirm that they are okay with making such a promise based on t-opsem FCP, or whether they would like to be included in the FCP. Related: rust-lang#74990, rust-lang#115285, rust-lang/reference#1226
…=Mark-Simulacrum Update `catch_unwind` doc comments for `c_unwind` Updates `catch_unwind` doc comments to indicate that catching a foreign exception _will no longer_ be UB. Instead, there are two possible behaviors, though it is not specified which one an implementation will choose. Nominated for t-lang to confirm that they are okay with making such a promise based on t-opsem FCP, or whether they would like to be included in the FCP. Related: rust-lang#74990, rust-lang#115285, rust-lang/reference#1226
45da62b
to
aa9a282
Compare
Links should be fixed now, and everything has been rebased/squashed. I'm not sure if any of the still-open threads should be considered blockers. |
…ulacrum Update `catch_unwind` doc comments for `c_unwind` Updates `catch_unwind` doc comments to indicate that catching a foreign exception _will no longer_ be UB. Instead, there are two possible behaviors, though it is not specified which one an implementation will choose. Nominated for t-lang to confirm that they are okay with making such a promise based on t-opsem FCP, or whether they would like to be included in the FCP. Related: rust-lang/rust#74990, rust-lang/rust#115285, rust-lang/reference#1226
aa9a282
to
e740cec
Compare
> Note: With `panic=unwind`, when a `panic` is turned into an abort by a | ||
> non-unwinding ABI boundary, either no destructors (`Drop` calls) will run, or | ||
> all destructors up until the ABI boundary will run. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This note (and the similar wording in src/destructors.md
) address the uncertainty I had in a previous review about what code can potentially run before an abort, thankyou.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're welcome, but be careful, because the behavior in 1.81 is different from what we intend to ship in 1.82. When you asked before, this had been left intentionally undefined, but the lang team later signed off on the guarantee given in the text here. I had forgotten that the linked PR didn't actually make it into 1.81, though.
…ulacrum Update `catch_unwind` doc comments for `c_unwind` Updates `catch_unwind` doc comments to indicate that catching a foreign exception _will no longer_ be UB. Instead, there are two possible behaviors, though it is not specified which one an implementation will choose. Nominated for t-lang to confirm that they are okay with making such a promise based on t-opsem FCP, or whether they would like to be included in the FCP. Related: rust-lang/rust#74990, rust-lang/rust#115285, rust-lang/reference#1226
e740cec
to
3a2921d
Compare
@traviscross I think this is ready for review again. |
Tracking issue: rust-lang/rust#74990