Skip to content
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

#[ffi_returns_twice] #2633

Closed
wants to merge 5 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 115 additions & 0 deletions text/0000-returns-twice.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
- Feature Name: ffi-returns-twice
- Start Date: 2019.02.08
- RFC PR: (leave this empty)
- Rust Issue: (leave this empty)

# Summary
[summary]: #summary

This RFC adds a new function attribute, `#[ffi_returns_twice]`, which indicates
that a foreign function can return multiple times.

# Motivation
[motivation]: #motivation

Rust assumes that function calls like:

```rust
let x = foo();
```

return only once. That is, when the execution arrives at the function call, the
function is called, it returns a single value, and that's it. This assumption
allows Rust to perform many optimizations.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add an example? I know there was one in some prior discussion that showed the need for this attribute quite convincingly.


However, some foreign functions like [`setjmp`] and [`vfork`] can return
multiple times.

The `#[ffi_returns_twice]` attribute specifies that a foreign function might
returns multiple times, inhibiting optimizations that assume that this is never
the case.

[`setjmp`]: https://en.cppreference.com/w/cpp/utility/program/setjmp
[`longjmp`]: https://en.cppreference.com/w/cpp/utility/program/longjmp
[`vfork`]: http://man7.org/linux/man-pages/man2/vfork.2.html

# Guide-level and reference-level explanation
[guide-level-explanation]: #guide-level-explanation

The `#[ffi_returns_twice]` function attribute specifies that a foreign function
might return multiple times, disabling optimizations that are incorrect for such
functions. Two examples of such functions are [`setjmp`] and [`vfork`].

# Drawbacks
[drawbacks]: #drawbacks

This complicates the language creating a new kind of functions.

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

This attribute is provided in both [LLVM] and [GCC].

[LLVM]: https://llvm.org/docs/LangRef.html#id979
[GCC]: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html

# Rationale and alternatives
[rationale-and-alternatives]: #rationale-and-alternatives

In some platforms, efficient implementations of standard library APIs have to
call low-level platform APIs that return multiple times. For example, the
widely-used [`musl`] library implements [`posix_spawn`] on Linux by calling
[`vfork`] (see: [`musl` blog post]). Rust implementations of such libraries (for
example, see: [`steed`]) should be able to call these platform interfaces
without invoking undefined behavior.

This RFC introduces a function attribute that disables incorrect optimizations
only for those functions for which they would be incorrect.

One alternative could be to remove the assumption that Rust functions return at
most once, eliminating the need for this attribute. This would have negative
performance implications for most Rust functions, which do not return multiple
times, making Rust functions a non-zero-cost abstraction.

Another alternative could be to not support interfacing with this type of
platform APIs, requiring users to use a different programming language (C,
Assembly, etc.) to do so.

[`posix_spawn`]: http://man7.org/linux/man-pages/man3/posix_spawn.3.html
[`musl`]: https://www.musl-libc.org/
[`musl` blog post]: https://ewontfix.com/7/
[`steed`]: https://github.com/japaric/steed

# Unresolved questions
[unresolved-questions]: #unresolved-questions

* Is this attribute sufficient to make write programs with functions that return
multiple times possible?

* Should we give this attribute a different name, e.g.,
`#[might_return_multiple_times]` or similar? Currently, this attribute is
called `returns_twice` because that's how the C attribute is called. Using the
same name as C here makes life easier for them.

* Should we namespace `ffi`-only attributes somehow (e.g. `#[ffi(returns_twice,
foo, bar)]` ? See: https://github.com/rust-lang/rfcs/issues/2637

# Future possibilities
[future-possibilities]: #future-possibilities

This RFC attempts to make writing programs that interface with foreign functions
that return multiple times possible, but doing so is very challenging because it
is trivial to introduce undefined behavior in those programs.

Future RFCs could try to make it easier to avoid certain types of undefined
behavior. For example, we could extend the borrow checker to take
`#[ffi_returns_twice]` into account and reject programs that would have
undefined behavior of the type "use-after-move".

In the presence of types that implement `Drop`, usage of APIs that return
multiple times requires extreme care to avoid deallocating memory without
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be worth saying "deallocating pinned memory" here. At least to my knowledge that is the only case where we really care in terms of type system guarantees.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't Pin just a normal Rust library ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is. So what?

Unsound thread spawning and Rc are also both just normal Rust libraries, and yet having one makes the other unsound. The same goes for Pin and functions that let you deallocate memory without running drop.

invoking destructors - this is critical for using [`Pin`] safely. We could also
explore diagnosing these cases.

[`Drop`]: https://doc.rust-lang.org/std/ops/trait.Drop.html
[`Pin`]: https://doc.rust-lang.org/std/pin/struct.Pin.html
gnzlbg marked this conversation as resolved.
Show resolved Hide resolved