-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Well-defined unwinding through FFI boundaries #2699
Conversation
This text about how the behavior could change at any time sounds like the very definition of an unstable feature, but it also sounds like this is being proposed for stabilization? That opens a big can of worms around our stability guarantees that ought to be discussed in the RFC. In particular, it seems like we should at least consider the option of making crates that depend on this become unstable, or contain that dependency in an unstable feature or something. I think we're also missing a summary of why any crate would want to rely on this in the first place. I've probably read it somewhere before, but I've completely forgotten it now, and it's obviously critical to this proposal. Is it as simple as " |
Fix link markdown Co-Authored-By: Joe ST <joe@fbstj.net>
@lxrec Re: instability, what can change is the implementation of unwinding, not the observable behavior in Rust code. In the guide-level explanation, I mentioned that Rust-to-Rust unwinding will be well-defined. I should probably expand on that paragraph, but there are real use-cases for this. From a comment by @gnzlbg further down in one of those threads:
As long as the code on both sides of the FFI boundary uses the same compiler, changes to the implementation would not change the observable unwinding behavior. Regarding the three "on the other hand" quotes you've cited:
I found this post pretty confusing at first, but @gnzlbg later clarified:
And later acknowledged:
Yes, that's why I mentioned
The problem with this is two-fold. First, I think it's probably not controversial to say that any solution requiring users to add C++ to a Rust/C project would be undesirable. Second, C++ doesn't actually provide strong guarantees than Rust for this; the only reason to use this as a solution would be if Rust implements the unwind-aborts-at-FFI-boundaries feature without providing an opt-out mechanism. |
The short summary of the state of the affairs:
What this RFC is proposing--in terms of implementation--is basically to provide a mechanism to make an
Okay, with that out of the way, here are my personal thoughts on the RFC: I'm definitely in favor of giving us better tools in Rust for interacting with unwinds and FFI exceptions. This RFC doesn't go all the way there, but it doesn't need to do so to solve the more immediate and pressing problem: the minimal implementation change mentioned above sounds like the right change, so long as we have analyzed to make sure that the different Rust ABI situation can be handled in all cases. But I don't think this gives the right semantics to achieve that change. The property of unwinding to me is a fundamental component of the ABI--not only is there an exception coming via some unwinding side channel, but the mechanics of how that side channel works. This means that it is effectively part of the calling convention, as function pointers also have to know if their target is a Also, exposing the ABI of Rust that would happen with marking |
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 RFC proposes a feature to solve propagating panics in a Rust->X->Rust code chain, where X is written in a programming language that is somehow able to propagate Rust panics properly. The feature proposed does a very poor job at solving this problem.
This feature could be useful to solve other problems, e.g., like allowing those that want to link Rust code dynamically (Rust->Rust with the same toolchain), e.g., in a dll, using the C ABI, to be able to use Rust panics.
text/XXX-annotate-unwind-rust.md
Outdated
# Summary | ||
[summary]: #summary | ||
|
||
A new function annotation, `#[unwind(Rust)]`, will be introduced; it will |
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.
nit: use present tense - this RFC introduces...
text/XXX-annotate-unwind-rust.md
Outdated
|
||
A new function annotation, `#[unwind(Rust)]`, will be introduced; it will | ||
explicitly permit `extern` functions to unwind (`panic`) without aborting. No | ||
guarantees are made regarding the specific unwinding implementation. |
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.
We do make some guarantees though. For example, when using a particular toolchain to compile Rust code, all extern functions and extern function declarations with this attribute are assumed to use the same panic implementation.
text/XXX-annotate-unwind-rust.md
Outdated
[summary]: #summary | ||
|
||
A new function annotation, `#[unwind(Rust)]`, will be introduced; it will | ||
explicitly permit `extern` functions to unwind (`panic`) without aborting. No |
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.
Only extern "ABI"
functions ? (all ABIs ? or which ones). Shouldn't this also be supported on extern function declarations (extern { fn-decl }
) ?
text/XXX-annotate-unwind-rust.md
Outdated
around the `libpng` and `libjpeg` C libraries) that make use of the current | ||
implementation's behavior. The proposed annotation would act as an "opt out" of | ||
the safety guarantee provided by aborting at an FFI boundary. | ||
|
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.
These library authors decided to rely on an implementation detail of undefined behavior, they knew about it, and have been actively blocking the fix of soundness hole in the language without putting in any work towards a solution.
If anything, this is probably the strongest argument against this RFC. It sends the message that it is ok to rely on UB, exploit implementation details, etc. knowingly as long as you complain loud enough when things break because then the language will be bent in your favor.
Calling it an ""opt out" of a safety guarantee" does not help.
Honestly I don't think even mentioning this would do this RFC a favor. From the use cases discussed, the Rust-Rust dynamic linking one felt like the only one that could motivate this. It might be better to focus on that instead.
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 didn't realize you felt this way about the specific impetus for the request and the proposal itself; I had thought, from you last comments in the issue thread, that you were in agreement that this was a reasonable request and a viable proposal.
I disagree with your perspective on the UB issue and am willing to have that conversation, but I'm now wondering if that discussion should be continued back on the internals
thread so that it doesn't make this PR too noisy. If I had understood that we didn't yet have agreement on the way forward, I wouldn't have opened this PR.
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.
(By "the UB issue" I don't mean expecting LLVM not to use nounwind
for optimization; I agree that doing so is incorrect.)
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 didn't realize you felt this way about the specific impetus for the request and the proposal itself; I had thought, from you last comments in the issue thread, that you were in agreement that this was a reasonable request and a viable proposal.
I thought this comment expressed what I believe would be an appropriate motivation for this attribute.
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.
@gnzlbg I agree that FFI Rust->Rust is an appropriate motivation for the attribute, but it felt disingenuous to me to advocate for it on that ground alone since it's not the real motivation for myself or @kornelski to be pushing for an annotation of some sort changing FFI unwind behavior. What I took from your phrasing in that comment about Rust->X->Rust was that you agreed it would be appropriate for @kornelski and anyone else to use the proposed annotation for Rust->X->Rust as long as they realize that they're relying on an implementation detail:
...by accident, it would allow those doing Rust->X->Rust to continue to "work" as long as their assumptions about implementation details still hold.
Knowledge about implementation details doesn't seem like an overly burdensome requirement for unsafe
interactions between languages; that's how the computing world managed to function without a formally defined C ABI for several decades.
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.
What I took from your phrasing in that comment about Rust->X->Rust was that you agreed it would be appropriate for @kornelski and anyone else to use the proposed annotation for Rust->X->Rust as long as they realize that they're relying on an implementation detail:
I don't see where I agreed to that. My comment says that "panics in Rust->Rust FFI" is a simple extension to the language that solves an useful problem, and that such a feature would allow some of the people that are invoking undefined-behavior in Rust today to continue doing so after the soundness fix for panics in Rust FFI lands (which AFAICT is all they wanted).
I never suggested #[unwind(Rust)]
as a solution to "panic in Rust->X", nor say that it would be appropriate for anyone to write code with undefined behavior. If anything, I showed multiple times how to write code without UB in stable Rust today, and argued that because that is possible "panics in Rust->X" might not be a problem worth solving.
text/XXX-annotate-unwind-rust.md
Outdated
than zero will cause the program to be aborted. This is the only way the Rust | ||
compiler can ensure that the function is safe, because there is no way for it | ||
to know whether or not the calling code supports the same implementation of | ||
stack-unwinding used for Rust. |
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 don't think this is the main issue.
The Rust language guarantees that extern "C"
functions do not panic, and these functions are optimized accordingly by LLVM. If they were to actually panic, the optimizations would be incorrect, which results in undefined behavior. Therefore, the language requires these functions to abort if a panic were to escape from them, but due to a bug in the implementation, this does not happen today. This bugs allows safe Rust programs to trigger UB, which makes the implementation unsound.
None of this has anything to do with the unwinding implementation of other languages. In fact, even if Rust code compiled with the exact same toolchain was used to call this function, the unsoundness bug would still be there, because LLVM will still optimize this code under the assumption that it does not panic.
text/XXX-annotate-unwind-rust.md
Outdated
} | ||
assert!(result.is_err()); | ||
} | ||
``` |
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.
See above, this example is unsound.
text/XXX-annotate-unwind-rust.md
Outdated
} | ||
``` | ||
|
||
**PLEASE NOTE:** Using this annotation **does not** provide any guarantees |
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 behavior of extern
functions and extern
function declarations that unwind (in any way - not only via Rust panics) is undefined. #[unwind(Rust)]
allows these functions to let a Rust panic!
escape, which is something that they cannot do without this attribute.
Explaining that does not really need mentioning anything about the panic implementation. If you want to mention that, just mentioning that the Rust panic implementation is unspecified, that is, it can change at any time, should be enough.
text/XXX-annotate-unwind-rust.md
Outdated
Since the behavior may be subject to change without notice as the Rust compiler | ||
is updated, it is recommended that all projects that rely on unwinding from | ||
Rust code into C code lock the project's `rustc` version and only update it | ||
after ensuring that the behavior will remain 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.
If this feature were aimed at solving this problem, then I don't think that having to do this to be able to use this feature correctly would be good enough.
I don't think this feature can solve this problem well without major changes.
text/XXX-annotate-unwind-rust.md
Outdated
|
||
Unwinding for functions marked `#[unwind(Rust)]` is performed as if the | ||
function were not marked `extern`. This is identical to the behavior of `rustc` | ||
for all versions prior to 1.35 except for 1.24.0. |
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 reads a bit like this feature is an opt-out workaround for some language change. I don't think the RFC should frame that this way.
text/XXX-annotate-unwind-rust.md
Outdated
|
||
This annotation has no effect on functions not marked `extern`. It has no | ||
observable effect unless the marked function `panic`s (e.g. it has no | ||
observable effect when a function returns normally or enters an infinite loop). |
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.
Note that the attribute does have an effect from the point-of-view of how the generated code can be optimized, even if the marked function never panic!
s in practice.
I think this feature does solve a problem worth solving for Rust->Rust FFI.
Solving this problem provides a temporary workaround for those wanting to
do Rust->X->Rust FFI, but the RFC frames this as if this were the problem
that this feature actually solves, which isn’t really the case.
So please do not interpret my feedback as combative or anything similar, as
mentioned, I think something like this is worth having. But to properly
address whether this is the best solution to the Rust->Rust FFI issue, the
RFC would need to be reframed around that problem.
|
@gnzlbg Well...I do think that the specific annotation |
Do you think that the feature proposed here is a good solution to the Rust->X->Rust problem?
Something like that is IMO a better solution for Rust->X->Rust than this. |
"Good" is ambiguous, but I do think it would be sufficient, at least for now. But
It seems to me that Looking again at the internals post and previous GitHub discussion, I don't actually see specific objections to |
Usually (always?) RFCs start by specifying the problem they intend to solve, and then they set out to find the "best" solution to that problem, where by "best" one should understand that the "best" solution often involves many trade-offs and constraints. So while this RFC might start at "good"/"sufficient", for it to be merged it will need to be the "best" solution to the problem it intends to solve, given the constraints.
I think both features solve slightly different problems, but these problems are related. I don't really mind much about whether features to solve these problems are proposed in one or multiple RFCs, but I think that each of these problems should be worth solving on its own, and that each feature that solves them should solve their problem well. |
I think
The C++ unwinding is defined for all the target platforms, already supported by LLVM (because it is also a C++ compiler backend) and is not likely to change. Or at least less likely than Rust's per above, and if it does, the libraries will probably become incompatible for more reasons. An In many cases the conversion would be trivial, but it would leave most flexibility on the Rust side while covering the use-cases. At the cost of having to define the conversions as part of the unwinding definition for each platform. |
Also in the long term it would be preferable to define a Rust interface for shared libraries that would be restricted to things that can be fully compiled in the library (i.e. mainly no generic parameters except lifetimes), but otherwise use Rust symbol mangling and not need |
I agree that this makes I suspect, though, that if the Rust exception ABI does change, the transformation to and from the native exception mechanism could be expensive, and so anyone using |
I am now wondering whether it's worth permitting individual function annotations to specify unwinding mechanisms at all, rather than introducing rustc/cargo settings that would specify the unwinding mechanism globally. Then, if/when the default unwinding mechanism changes, users can opt-out (which I suspect will be a necessary feature anyway), and the only per-function annotation we'd introduce would be |
I'd still prefer the per-function annotation on specific FFI functions.
This is the unusual case, and even in a program that wants this, not
every function may want it.
…On Tue, May 28, 2019 at 03:13:09PM -0700, BatmanAoD wrote:
I am now wondering whether it's worth permitting individual function annotations to specify unwinding mechanisms at all, rather than introducing rustc/cargo settings that would specify the unwinding mechanism globally. Then, if/when the default unwinding mechanism changes, users can opt-out (which I suspect will be a necessary feature anyway), and the only per-function annotation we'd introduce would be `#[unwind(allow)]` for FFI functions. (I thought there was an existing RFC to introduce that annotation, but I can't find it.)
--
You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub:
#2699 (comment)
|
Note that for Rust->Rust FFI translating Rust panics to C++ exceptions and re-translate C++ exceptions back into Rust panics isn't a zero-cost abstraction. |
@gnzlbg As I said above, unwinding is not part of Rust's type system, so the documentation's incorrect implication that But just because non- Unwinding through an I don't believe that
The proposal, of course, would change the behavior of this program; but it would change it from undefined behavior that happens to propagate the |
@BatmanAoD you are conflating a couple of orthogonal issues. First, you claim that non-extern Rust functions are not exactly equivalent to extern "Rust" fns. This is incorrect, there is no difference between As a consequence, the other claims being made: "Unwinding through an extern "Rust" fn is currently undefined behavior", "Rust functions without an
They aren't, as opposed to, e.g.,
Do you have a link to that discussion? You also make the orthogonal claim that unwinding is not part of the Rust type system, because it is not part of a function call ABI. Do you have an example of a calling convention specification that does not consider unwinding to be part of the calling convention? |
In case there are any doubts left, let's take it from $ echo 'extern "Rust" fn main() {}' | rustc - --pretty=normal -Z unstable-options
fn main() { } The parser itself treats Was the use of the keyword I propose we completely stop using "
Yes, you can have Rust-ABI FFI imports. And yes we can probably give them slightly different semantics than regular Rust functions, but they still have to be able to coerce to |
Sorry that I'm joining this thread late; I wish I'd found it sooner because it's quite relevant to things we're doing in Lucet, but hopefully this is still helpful input. To provide a bit of context, in Lucet, we compile WebAssembly into native shared objects using Cranelift, and then use a runtime system implemented in Rust to run functions exported from those shared objects. Because those WebAssembly programs can call functions imported from their environment, some of which must call back into WebAssembly, we fairly regularly end up with call chains that look like Given the features that need to be implemented in those import functions, we can't just impose the So, while I agree that it's essential as a first step, I believe we need just a little bit more than what @joshtriplett described above:
In addition, we need to be able to mark unsafe calls to I have some longer term thoughts as well, with the caveat that I've almost exclusively been thinking about System V ABI platforms. I wonder if we should look to the C/C++ compilers'
In case 1, it would be nice to have an option to mark all In case 2, conversely, it would make sense to do the opposite, causing all panics to abort when they reach the entrypoint of any Finally, case 3 gets us back to per-function attributes, so that we can pick and choose whether unwinding is allowed in each place where it might occur. Though this scenario could also work without a new attribute by allowing unwinding by default, and encouraging the use of Where all of these options collide with the choice of panic runtime is if we develop choices other than |
This might be a crazy idea, but we have three stable ABIs that are guaranteed to be supported on all platforms that Rust supports: What if we make Iff the Rust panics are implemented using the exact same mechanism as the system unwinding, then this might be fairly low cost or zero cost. If we ever implement a different panicking mechanism for Rust, then some translation might need to happen here that might add overhead, but the code should still work, and improving that is a problem that we'll only need to solve if that ever happens - and that we could solve by offering some way of preserving the old behavior. EDIT: right now, "system" maps to some other ABI, like |
In this model, then, we wouldn't have an attribute, the unwinding behavior would just be dependent on the ABI string. I believe this was mentioned upthread, and I think it's a good idea, but we'll have to add new ABI strings to account for this. For example, the System V ABI supports unwinding but does not require it, so you can have a perfectly compliant System V ABI binary that nonetheless would have UB if you unwound into it. So we would need |
@acfoltzer said... [link]
It was, here's a brief overview(Apologies, but copy/pasting GitHub comments like this removes markup and there's way to much here to fill it back in)
@BatmanAoD said... [link]
@BatmanAoD said... [link]
@BatmanAoD said... [link]
Here's my opinion: Exception ABI should be specified separately from calling ABI to avoid an N×M problem for ABI strings. However, it would make sense to have a "two-part" ABI string, e.g. When defining an extern fn ( When declaring an extern fn ( This should probably include linting on but allowing "not guaranteed" combinations such as The minimal way forward would be to allow specifying the "exception ABI string" in addition to the "calling ABI string" and allow the combinations |
@CAD97, thank you so much for collecting the previous context about this idea. I also completely agree about the mechanisms for translating between unwinding formats. The N×M problem hadn't occurred to me because we're so focused on Linux, but I could see that getting out of hand quickly. If we have separate specifiers for the unwinding side of the ABI, though, I strongly support making the default choice be to use the ABI-specified unwinding mechanism rather than the minimal (abort) choice. First of all, if Rust is using the ABI unwinding mechanism (which I believe it is, at least on Tier 1 platforms?), calling an extern "$ABI" fn foo() {
panic!()
}
foo(); Second, while it seems at first like the conservative default is to disallow unwinding across FFI boundaries, this is only true for the case where the FFI is calling into Rust, not the other way around. In the Postel's law suggests we might then want to abort by default for Finally, C programs are expected to use As for how users would specify an unwinding mechanism, let's take the example of a program targeting But suppose someone needs to link against a library compiled with SJLJ. For a C++ program, this would mean downloading the SJLJ version of MinGW and recompiling an SJLJ-specific version of the program. As far as I know, there's no way to specify exception mechanism at a more granular level for C++. In Rust we have to at least be slightly more precise than this, because
Having a link-time mechanism like 1 would reduce cases where a library client would need to fork a dependency just to change the unwind mechanism for their particular use case, or where a library author would need to expose an abundance of feature flags. Solution 2 would be for more specialized cases, and could coexist with the link-wide option. For example in Lucet, we have calls between our runtime library and Cranelift-generated code that would need to be precisely specified as System V with DWARF unwinding, but the the mechanism used by the C API we expose for our users need not be fixed. |
I'd argue that this means we would want to abort panics by default going from Rust to FFI on any ABI that doesn't require unwinding support, and collect any reasonable kind of unwind from FFI and convert it to a Rust panic, or the opposite of what you suggest here.
C++ also requires a lot of things for safety by default that Rust doesn't, because it's a good idea for the default direction of the gun to be downrange, not at your foot.
Isn't this just the panic runtime? I think being able to set the unwind implementation used by Rust native panics can be useful, but shifting the default for other ABI strings feels absurdly footgunny. If I write
Personally, I'm partial to the argument that the exception ABI is too important to the function to be relegated to "just" an attribute.
99.9% of Rust crates are statically linked anyway, so it doesn't matter. I'd (maybe naively?) expect the ABI shuffling involved in calling a statically linked function to be optimized out. I think a dylib having a good reason to support multiple unwinding mechanisms unlikely. |
If there is no handler on the thread, I believe all the unwind implementations in question will turn it into a terminate-the-application, and I don't think there's any issue. I haven't verified all the mechanisms yet to see what happens, though, and so erred on the side of caution. Catching the unwind in FFI code is problematic under the current implementation (for Windows MSVC ABI), and needs to remain undefined behavior. |
Sorry, this was a notation confusion; I was using
For the purposes of unwinding, I see the safety obligation being on the caller of the foreign code (modulo contravariance for cases involving callbacks). If we default to allow ABI unwinding, it would be the C compiler that has an unsafe default for calling into Rust, and the obligation of the C side to use the correct flag. Perhaps this is too narrow a view of safety and blame, though?
No, the panic runtime is what determines the behavior of panics within a Rust program. The option I was trying to describe here would determine what would happen to those panics once they reach an FFI boundary, as well as what unwinding mechanism to expect when Rust calls into an FFI function.
If we require the // libfoo/src/lib.rs
#[cfg(libfoo_unwind = seh)]
extern "C" "seh" fn foo() { ... }
#[cfg(libfoo_unwind = sjlj)]
extern "C" "sjlj" fn foo() { ... }
#[cfg(libfoo_unwind = sysv-dwarf)]
extern "C" "sysv-dwarf" fn foo() { ... }
... I'm sympathetic to the argument that compiler versions and flags shouldn't matter for non-Rust ABIs, but this would be a composability nightmare in practice. |
What I don't see is any reason a library would need to expose the same symbol with different unwind mechanisms, and not want to expose multiple at the same time. And it would only result in minimal and very macro-by-example-friendly repetition: extern "C" "unwind(seh)" fn foo_seh() { foo() }
extern "C" "unwind(sysv-dwarf)" fn foo_sysv() { foo() }
fn foo() { ... } And don't forget that my draft includes being able to specify what unwinding mechanism |
This is the case that would arise when a library wants to export a potentially-unwinding function across multiple platforms, but that doesn't depend on a specific mechanism. This is a similar motivation as that of the I think we should have something similar for "C + unwinding"; I would prefer it just be
This is what the |
That level of granularity for describing unwind is very unwarranted: the unwind in each case should be designated as While there is discussion of potentially other unwind systems, it does seem that the current trend for proposals (say, exceptions-lite) is closer to a syntactic sugar for multiple return value ABIs (or returning something like Rust's |
Unwinding ABI is part of the calling convention in any case. The compiler needs to know whether a function can unwind when generating the call to it. The calling convention including whether the function can unwind must also be part of the type, because the information has to be available when calling the function through pointers—that can also enforce that you can't pass a function that can unwind as callback to foreign code that is compiled without support for it. |
Thank you for explaining this. I had no idea it was the case and am actually quite surprised. @eddyb Thanks for confirming this and demonstrating that the distinction is lost at parse time.
Makes sense. I had thought that these were converses of each other; i.e., that Now that I understand this, I think the rule I would ideally have is "by default, any entry point in the global symbol table will not unwind, unless otherwise annotated." I'm sure this would be a breaking change, though, and in any case it wouldn't solve the issue at hand, so for now I'll proceed with the assumption that that's not on the table for this RFC.
I really wasn't considering the function call ABI to be part of the "type system" as such at all. But I suppose it is, insofar as function pointers must maintain their associated ABI information. I kind of wish there were a way around this; for instance, taking a pointer to a non-
I was mistaken. The example I was misremembering used
I actually like this idea at first blush, but I'm not sure what it implies about the other ABIs. I assume |
@CAD97 I'm not fully convinced yet that we could end up with an NxM problem. That would mean that all "unwinding ABIs" can be used with all "non-unwinding-part of call ABIs". Is this the case? I think that making the unwinding ABI part of the function is a good idea, whether we use a single string or multiple strings for that, is more like a detail. One thing that we should think about is how this impact the type system. For example (leaving bikeshedding of the precise syntax and unwinding ABI names apart): extern "Rust" "unwind(abort)" fn foo() { ... }
extern "Rust" "unwind(allow)" fn bar() { ... }
let a: extern "Rust" "unwind(abort)" fn() = foo; // OK
let b: extern "Rust" "unwind(allow)" fn() = foo; // OK abort->unwind coercion ?
let c: extern "Rust" "unwind(abort)" fn() = bar; // ERROR
let d: extern "Rust" "unwind(allow)" fn() = bar; // OK In particular, if we were to compile the crate with |
@gnzlbg
I think it'd be nicer if we could have some decently thin way to wrap function pointers between ABIs, but that's something we don't have for regular ABIs, and would use the same mechanism for unwinding threading. |
Permit unwinding through FFI by default This repeats #62505 for master (Rust 1.38+), as #58794 is not yet resolved. This is a stopgap until a stable alternative is available, like [RFC 2699](rust-lang/rfcs#2699), as long as progress is being made to that end. r? @joshtriplett
I met an ex-LLVM developer who told me that the unwinding mechanism is indeed part of the ABI and that "you don't have to do anything special to be able to propagate an unwind safely though there are things you can do to break it." I'm now wondering if the stated design intent to abort by default for any ABI would in fact be a mistake; the dev I've quoted suggested that it would be preferable to keep the language feature for aborting at an API boundary separate from the ABI specification. So, in that case, the best path would be to stop inserting The problem of introducing new, custom ABIs with nonstandard exception specifications could then be left for another RFC. And in the long run, I think it may be worth discussing deprecating the |
@CAD97 @jan-hudec Here is a simpler RFC that doesn't attempt to tackle as much: #2753 |
I believe there is an existing proposal for an unwind implementation that would be essentially equivalent to syntax sugar over variadic return types (a sort of "invisible With that said, personally, I think that's acceptable; and in fact just because the syntax and the target platforms would support certain combinations does not mean that As I mentioned in #2753, I think the
The As an alternative to the Other unwind specifications we could support initially:
For imports, I think we should keep the
This, to me, seems like the thorniest question. For imported
For an initial implementation, I don't think differently-annotated functions and function pointers should ever be implicitly convertible to each other; even my "equivalent to Some conversions do seem reasonable enough to provide in the long run, e.g. But perhaps it would be preferable to provide something like |
Dear world, I'm writing with a quick update. As I mentioned in my update comment, the plan of record here is as follows:
In fact, we've already created a repository for tracking the progress of this group. Right now that's a personal repository of mine, but it will move to rust-lang once the group is "official". The details there are still a "WIP" but hopefully it gives you some sense of the roadmap and the steps we plan to take. We're also chatting in For the time being, I'm going to go ahead and close this RFC because this discussion thread has gotten long and is quite distracting. Our intention is to open a fresh RFC that declares the above steps. To be clear, I expect that RFC to move fairly quickly through the process -- I think we've had plenty of discussion on the topic and the tradeoffs involved in different strategies. |
Update 28 October 2019: The effort to provide this language feature has been spun into a working group. See the announcement RFC.
Rendered