Stream: wg-ffi-unwind

Topic: no C unwind?


nikomatsakis (Oct 22 2019 at 21:35, on Zulip):

this sounds very surprising to me :)

Kyle Strand (Oct 22 2019 at 21:37, on Zulip):

I am surprised to be saying it!

nikomatsakis (Oct 22 2019 at 21:38, on Zulip):

I'm breaking this out into its own topic, but I'd definitely like to see some links to what is making you feel this way. I was hoping we could open up some kind of RFC this week :)

Kyle Strand (Oct 22 2019 at 21:40, on Zulip):

And, more generically, the possibility that LLVM can generate well-behaved code would, I think, mean that any other Rust implementation could, too

nikomatsakis (Oct 22 2019 at 21:40, on Zulip):

Hmm

nikomatsakis (Oct 22 2019 at 21:41, on Zulip):

Well, the main concern would I think be dead-code elim (or lack thereof)

nikomatsakis (Oct 22 2019 at 21:41, on Zulip):

"C unwind" is to some extent more of an optimization

Kyle Strand (Oct 22 2019 at 21:41, on Zulip):

If I star comments here in Zulip, is that globally visible? I.e. could I quickly go into that thread and star various comments for you to read?

nikomatsakis (Oct 22 2019 at 21:41, on Zulip):

But this sounds like a great thing to try and create a "decision document" around

nikomatsakis (Oct 22 2019 at 21:41, on Zulip):

If I star comments here in Zulip, is that globally visible? I.e. could I quickly go into that thread and star various comments for you to read?

no

nikomatsakis (Oct 22 2019 at 21:41, on Zulip):

I can skim through that thread

Kyle Strand (Oct 22 2019 at 21:41, on Zulip):

Okay. Despite my previous comment, I think that would be worthwhile :)

Kyle Strand (Oct 22 2019 at 21:42, on Zulip):

And, much to my surprise, as far as I can tell, gnzlbg seems to agree w/ Amanieu's suggestion

Kyle Strand (Oct 22 2019 at 21:43, on Zulip):

(surprise b/c I had been under the impression that treating extern "C" that way was not something gnzlbg was at all favorably disposed toward)

Kyle Strand (Oct 22 2019 at 21:43, on Zulip):

One person, I think, who may be able to read the thread and quickly point out issues with the proposal that I'm not seeing, would be Centril

nikomatsakis (Oct 22 2019 at 21:44, on Zulip):

so far from my skimming, there are a lot of details and my eyes are glazing over :P

nikomatsakis (Oct 22 2019 at 21:44, on Zulip):

(specifically it seems like, in the part I'm reading, there is some debate about whether it is or is not UB and under what circumstances)

nikomatsakis (Oct 22 2019 at 21:44, on Zulip):

I'm presuming this gets settled, let me see if I can find the spot

Kyle Strand (Oct 22 2019 at 21:44, on Zulip):

right....

nikomatsakis (Oct 22 2019 at 21:45, on Zulip):

but if we were to try and summarize, that would be an important thing to clarify

Kyle Strand (Oct 22 2019 at 21:46, on Zulip):

my understanding is that "we" (not sure if that would be anyone, or someone on the Rust team with an existing relationship) should reach out to LLVM somehow and ask for an explicit statement to the effect that unwinding across stack frames without landing pads is well defined unless those frames have destructors/RAII/finally

nikomatsakis (Oct 22 2019 at 21:52, on Zulip):

I guess the key question is why we were saying that extern "C" functions should not allow unwinding by default. I'm not sure how well argued this point is. There are presumably a few possible aspects:

nikomatsakis (Oct 22 2019 at 21:52, on Zulip):

I'm a bit surprised to see discussion of #[no_unwind] attributes and not an ABI

nikomatsakis (Oct 22 2019 at 21:52, on Zulip):

Since the same arguments that led us to "C unwind" seem to apply in reverse

Kyle Strand (Oct 22 2019 at 21:58, on Zulip):

The main discussions I remember about not unwinding through extern "C" were several months before I opened RFC 2699, so...quite long ago, and scattered across multiple GitHub and Discourse threads.

Kyle Strand (Oct 22 2019 at 22:01, on Zulip):

My memory is that there was concern that the behavior would be LLVM-undefined in many or most cases. (Arguably even all cases.) Certainly I was given the impression that when an unwind crosses a stack frame with no landing pads, the platform-level behavior is undefined.

Kyle Strand (Oct 22 2019 at 22:03, on Zulip):

So if that's not true, and we can get documentation into LLVM guaranteeing the behavior of unwinding over C frames not compiled with -fexceptions, then I think Rust could officially change this from "undefined behavior" to "implementation defined behavior," and the no_unwind optimization would just need to be made available to users somehow.

Amanieu (Oct 22 2019 at 22:04, on Zulip):

@nikomatsakis The main point is that we should provide a safe default that allows unwinding. If you want dead code elimination then you can explicitly mark certain C APIs that do not unwind. If you look at the libc headers you will see that certain functions are marked noexcept when compiled with a C++ compiler, rather than the other way around.

nikomatsakis (Oct 22 2019 at 22:05, on Zulip):

Can we create a hackmd and start to collect the key points here?

nikomatsakis (Oct 22 2019 at 22:05, on Zulip):

One thing I found very unclear, @Amanieu, was what all the UB considerations were, especially with respect to longjmp

nikomatsakis (Oct 22 2019 at 22:05, on Zulip):

hackmd doc I just created: https://hackmd.io/ymsEL6OpR6OSMoFr1As1rw

nikomatsakis (Oct 22 2019 at 22:05, on Zulip):

unfortunately, I have to run now to make dinner :)

nikomatsakis (Oct 22 2019 at 22:07, on Zulip):

To be clear, I am definitely sympathetic to that argument, @Amanieu -- and also to the point that it behooves us to be analogous with C and C++

Kyle Strand (Oct 22 2019 at 22:09, on Zulip):

To be clear, I am definitely sympathetic to that argument, Amanieu -- and also to the point that it behooves us to be analogous with C and C++

Same here.

Amanieu (Oct 22 2019 at 22:10, on Zulip):

longjmp on Windows uses unwinding, but it is somewhat magic: it calls destructors but it is ignored by noexcept.

nikomatsakis (Oct 22 2019 at 22:31, on Zulip):

hackmd doc I just created: https://hackmd.io/ymsEL6OpR6OSMoFr1As1rw

@Amanieu or @gnzlbg -- one thing I think would be super helpful would be if you could enumerate in this document some of the key scenarios you are curious about. For example, I saw some talk about a longjmp that unwinds over a "noexcept" frame. It seems like you two had a few such scenarios in mind.

Kyle Strand (Oct 22 2019 at 22:36, on Zulip):

@Amanieu I believe longjmp w/ MSVC does _not_ call destructors; I think gnzlbg said that LLVM's difference in behavior here seems to be a Clang bug

gnzlbg (Oct 23 2019 at 09:17, on Zulip):

@nikomatsakis this is essentially the opposite proposal, e.g., instead of making C++ noexcept(true) the default for C APIs, it makes noexcept(false) the default, and adds some way to mark some APIs as noexcept(true)

gnzlbg (Oct 23 2019 at 09:17, on Zulip):

(e.g. the #[no_unwind] attribute)

gnzlbg (Oct 23 2019 at 09:18, on Zulip):

That opens the design of a large language feature, e.g., should #[no_unwind] be part of the type system ? (C++ didn't do that at first in C++11 but had to fix that in C++17)

gnzlbg (Oct 23 2019 at 09:20, on Zulip):

If its part of the type system its technically an effect, and that opens the question of how does one write code that's generic over noexcept (C++ provides a way to query whether an expression is noexcept, noexcept overloading, allows using noexcept in const contexts for constraining impls similar to where clauses, etc.)

gnzlbg (Oct 23 2019 at 09:21, on Zulip):

it also has implications for libstd, e.g., how to make libstd "#[no_unwind] correct", etc.

gnzlbg (Oct 23 2019 at 09:22, on Zulip):

the application of such a feature goes way beyond C FFI as well, e.g., lots of code wants to make sure that certain operations cannot unwind, and #[no_unwind] would achieve that, so the use cases fan out a lot

gnzlbg (Oct 23 2019 at 09:43, on Zulip):

Usually, #[no_unwind] would also be able to make some currently incorrect uses of AssertUnwindSafe correct, e.g., by being able to express that some part of the code never unwinds, and therefore, cannot violate any panic safety invariants.

gnzlbg (Oct 23 2019 at 09:44, on Zulip):

And well then there is also the issue of allowing #[no_unwind] in trait methods, for example. I don't think I've ever written a Drop implementation where allowing Drop::drop to unwind would be something I want.

gnzlbg (Oct 23 2019 at 09:45, on Zulip):

e.g. for something as simple as dropping a [T], if one T::drop panics, then [T]::drop tries to drop all other elements, and then it panics (and if a second T::drop panics, then the program aborts)

gnzlbg (Oct 23 2019 at 09:47, on Zulip):

That code could be much simpler if T::drop is known not to panic, and well, if you are building a Vec<T> on top of it, there is one element for which T::drop failed, but for which the Vec<T> deallocates its memory

gnzlbg (Oct 23 2019 at 09:49, on Zulip):

so there is a T in your program for which the destructor did not succeed and whose memory was deallocated

Amanieu (Oct 23 2019 at 09:59, on Zulip):

Yes, allowing drops to panic was a mistake, but I think it's outside the scope of this WG. Let's stay on topic.

Amanieu (Oct 23 2019 at 10:00, on Zulip):

While I mentioned a #[no_unwind] attribute, my proposal would just as well work with extern "C nounwind". The key point is just allowing unwinding by default in extern "C".

gnzlbg (Oct 23 2019 at 10:00, on Zulip):

The points I tried to make is that a #[no_unwind] attribute that you can stamp on a function is a large language change

gnzlbg (Oct 23 2019 at 10:01, on Zulip):

which impacts many parts of the language and the library long term

gnzlbg (Oct 23 2019 at 10:01, on Zulip):

an extern "C nounwind" ABI feels much restricted in scope

gnzlbg (Oct 23 2019 at 10:02, on Zulip):

it still allows many things that a noexcept-like feature would allow though

gnzlbg (Oct 23 2019 at 10:04, on Zulip):

e.g. you can implement traits for function types with that ABI, which lets you query whether a function call might panic in an adhoc way:

trait CanUnwind { const VALUE: bool }
impl<Args..., Ret> CanUnwind for extern "C nounwind" fn(Args...) -> Ret { const VALUE: bool = false; }
impl<Args...,Ret> CanUnwind for extern "C" fn(Args...)-> Ret { const VALUE: bool = true; }
gnzlbg (Oct 23 2019 at 10:05, on Zulip):

(or using specialization you can do better)

gnzlbg (Oct 23 2019 at 10:05, on Zulip):

A "C unwind" feature also allows this, I don't think we mentioned that anywhere

gnzlbg (Oct 23 2019 at 10:08, on Zulip):

An "ABI" string restricts the ABIs that can use such a noexcept-like feature, but that's essentially it.

gnzlbg (Oct 23 2019 at 10:13, on Zulip):

I've branched that to the "noexcept-like feature" thread

Last update: Nov 15 2019 at 10:50UTC