|
| 1 | +- Feature Name: ffi-returns-twice |
| 2 | +- Start Date: 2019.02.08 |
| 3 | +- RFC PR: (leave this empty) |
| 4 | +- Rust Issue: (leave this empty) |
| 5 | + |
| 6 | +# Summary |
| 7 | +[summary]: #summary |
| 8 | + |
| 9 | +This RFC adds a new function attribute, `#[ffi_returns_twice]`, which indicates |
| 10 | +that an `extern` function can return multiple times. |
| 11 | + |
| 12 | +# Motivation |
| 13 | +[motivation]: #motivation |
| 14 | + |
| 15 | +Rust assumes that function calls like: |
| 16 | + |
| 17 | +```rust |
| 18 | +let x = foo(); |
| 19 | +``` |
| 20 | + |
| 21 | +return only once. That is, when the execution arrives at the function call, the |
| 22 | +function is called, it returns a single value, and that's it. This assumption |
| 23 | +allows Rust to perform many optimizations. |
| 24 | + |
| 25 | +However, some `extern` functions like [`setjmp`] and [`vfork`] can return |
| 26 | +multiple times. |
| 27 | + |
| 28 | +The `#[ffi_returns_twice]` attribute specifies that an `extern` function might |
| 29 | +returns multiple times, inhibiting optimizations that assume that this is never |
| 30 | +the case. |
| 31 | + |
| 32 | +[`setjmp`]: https://en.cppreference.com/w/cpp/utility/program/setjmp |
| 33 | +[`longjmp`]: https://en.cppreference.com/w/cpp/utility/program/longjmp |
| 34 | +[`vfork`]: http://man7.org/linux/man-pages/man2/vfork.2.html |
| 35 | + |
| 36 | +# Guide-level and reference-level explanation |
| 37 | +[guide-level-explanation]: #guide-level-explanation |
| 38 | + |
| 39 | +The `#[ffi_returns_twice]` function attribute specifies that an `extern` function |
| 40 | +might return multiple times, disabling optimizations that are incorrect for such |
| 41 | +functions. Two examples of such functions are [`setjmp`] and [`vfork`]. |
| 42 | + |
| 43 | +# Drawbacks |
| 44 | +[drawbacks]: #drawbacks |
| 45 | + |
| 46 | +This complicates the language creating a new kind of functions. |
| 47 | + |
| 48 | +# Prior art |
| 49 | +[prior-art]: #prior-art |
| 50 | + |
| 51 | +This attribute is provided in both [LLVM] and [GCC]. |
| 52 | + |
| 53 | +[LLVM]: https://llvm.org/docs/LangRef.html#id979 |
| 54 | +[GCC]: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html |
| 55 | + |
| 56 | +# Rationale and alternatives |
| 57 | +[rationale-and-alternatives]: #rationale-and-alternatives |
| 58 | + |
| 59 | +In some platforms, efficient implementations of standard library APIs have to |
| 60 | +call low-level platform APIs that return multiple times. For example, the |
| 61 | +widely-used [`musl`] library implements [`posix_spawn`] on Linux by calling |
| 62 | +[`vfork`] (see: [`musl` blog post]). Rust implementations of such libraries (for |
| 63 | +example, see: [`steed`]) should be able to call these platform interfaces |
| 64 | +without invoking undefined behavior. |
| 65 | + |
| 66 | +This RFC introduces a function attribute that disables incorrect optimizations |
| 67 | +only for those functions for which they would be incorrect. |
| 68 | + |
| 69 | +One alternative could be to remove the assumption that Rust functions return at |
| 70 | +most once, eliminating the need for this attribute. This would have negative |
| 71 | +performance implications for most Rust functions, which do not return multiple |
| 72 | +times, making Rust functions a non-zero-cost abstraction. |
| 73 | + |
| 74 | +Another alternative could be to not support interfacing with this type of |
| 75 | +platform APIs, requiring users to use a different programming language (C, |
| 76 | +Assembly, etc.) to do so. |
| 77 | + |
| 78 | +[`posix_spawn`]: http://man7.org/linux/man-pages/man3/posix_spawn.3.html |
| 79 | +[`musl`]: https://www.musl-libc.org/ |
| 80 | +[`musl` blog post]: https://ewontfix.com/7/ |
| 81 | +[`steed`]: https://github.com/japaric/steed |
| 82 | + |
| 83 | +# Unresolved questions |
| 84 | +[unresolved-questions]: #unresolved-questions |
| 85 | + |
| 86 | +Is this attribute sufficient to make write programs with functions that return |
| 87 | +multiple times possible? |
| 88 | + |
| 89 | +# Future possibilities |
| 90 | +[future-possibilities]: #future-possibilities |
| 91 | + |
| 92 | +This RFC attempts to make writing programs that interface with extern functions |
| 93 | +that return multiple times possible, but doing so is very challenging because it |
| 94 | +is trivial to introduce undefined behavior in those programs. |
| 95 | + |
| 96 | +Future RFCs could try to make it easier to avoid certain types of undefined |
| 97 | +behavior. For example, we could extend the borrow checker to take |
| 98 | +`#[ffi_returns_twice]` into account and reject programs that would have |
| 99 | +undefined behavior of the type "use-after-move". |
| 100 | + |
| 101 | +In the presence of types that implement `Drop`, usage of APIs that return |
| 102 | +multiple times requires extreme care to avoid deallocating [`Drop`] types |
| 103 | +without calling `Drop::drop`. Calling `Drop::drop` before freeing the memory of |
| 104 | +a `Drop` type is required for the correctness of APIs like [`Pin`]. Future RFCs |
| 105 | +could pursue warning when it is clear that this might occur. |
| 106 | + |
| 107 | +`Drop`]: https://doc.rust-lang.org/std/ops/trait.Drop.html |
| 108 | +[`Pin`]: https://doc.rust-lang.org/std/pin/struct.Pin.html |
0 commit comments