From 019dff446975301596a1bf98521f00cce952f551 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sat, 2 Sep 2017 17:21:25 -0700 Subject: [PATCH 01/25] Support defining C-compatible variadic functions in Rust --- text/0000-variadic.md | 262 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 262 insertions(+) create mode 100644 text/0000-variadic.md diff --git a/text/0000-variadic.md b/text/0000-variadic.md new file mode 100644 index 00000000000..8829305f76c --- /dev/null +++ b/text/0000-variadic.md @@ -0,0 +1,262 @@ +- Feature Name: variadic +- Start Date: 2017-08-21 +- RFC PR: (leave this empty) +- Rust Issue: (leave this empty) + +# Summary +[summary]: #summary + +Support defining C-compatible variadic functions in Rust, via new intrinsics. +Rust currently supports declaring external variadic functions and calling them +from unsafe code, but does not support writing such functions directly in Rust. +Adding such support will allow Rust to replace a larger variety of C libraries, +avoid requiring C stubs and error-prone reimplementation of platform-specific +code, improve incremental translation of C codebases to Rust, and allow +implementation of variadic callbacks. + +# Motivation +[motivation]: #motivation + +Rust can currently call any possible C interface, and export *almost* any +interface for C to call. Variadic functions represent one of the last remaining +gaps in the latter. Currently, providing a variadic function callable from C +requires writing a stub function in C, linking that function into the Rust +program, and arranging for that stub to subsequently call into Rust. +Furthermore, even with the arguments packaged into a `va_list` structure by C +code, extracting arguments from that structure requires exceptionally +error-prone, platform-specific code, for which the crates.io ecosystem provides +only partial solutions for a few target architectures. + +This RFC does not propose an interface intended for native Rust code to pass +variable numbers of arguments to a native Rust function, nor an interface that +provides any kind of type safety. This proposal exists primarily to allow Rust +to provide interfaces callable from C code. + +# Guide-level explanation +[guide-level-explanation]: #guide-level-explanation + +C code allows declaring a function callable with a variable number of +arguments, using an ellipsis (`...`) at the end of the argument list. For +compatibility, unsafe Rust code may export a function compatible with this +mechanism. + +Such a declaration looks like this: + +```rust +pub unsafe extern "C" fn func(arg: T, arg2: T2, ...) { + // implementation +} +``` + +The `...` at the end of the argument list declares the function as variadic. +The function must use `extern "C"`, and must use `unsafe`. To expose +such a function as a symbol for C code to call directly, the function may want +to use `#[no_mangle]` as well; however, Rust code may also pass the function to +C code expecting a function pointer to a variadic function. + +Unsafe Rust code can also define a variadic closure: + +```rust +let closure = |arg, arg2, ...| { + // implementation +}; +``` + +Rust code may pass a variadic closure to C code expecting a pointer to a +variadic function. + +To access the arguments, Rust provides the following public interfaces in +`core::intrinsics` (also available via `std::intrinsics`): + +```rust +/// The argument list of a C-compatible variadic function, corresponding to the +/// underlying C `va_list`. Opaque. +pub struct VaList<'a>; + +impl<'a> VaList<'a> { + /// Obtain the variable arguments of the current function. Produces a + /// compile-time error if called from a non-variadic function. The compiler + /// will supply the appropriate lifetime when called, and prevent that + /// lifetime from outliving the variadic function. + pub unsafe fn start() -> VaList<'a>; + + /// Extract the next argument from the argument list. + pub unsafe fn arg(&mut self) -> T; +} + +impl<'a> Clone for VaList<'a>; +impl<'a> Drop for VaList<'a>; + +/// The type of arguments extractable from VaList +trait VaArg; + +impl VaArg for i8; +impl VaArg for i16; +impl VaArg for i32; +impl VaArg for i64; +impl VaArg for isize; +impl VaArg for u8; +impl VaArg for u16; +impl VaArg for u32; +impl VaArg for u64; +impl VaArg for usize; +impl VaArg for f32; +impl VaArg for f64; +impl VaArg for *const T; +impl VaArg for *mut T; +``` + +The `libc` crate additionally provides implementations of the `VaArg` trait for +the raw C types corresponding to the Rust integer and float types above: + +```rust +impl VaArg for c_char; +impl VaArg for c_schar; +impl VaArg for c_uchar; +impl VaArg for c_short; +impl VaArg for c_int; +impl VaArg for c_long; +impl VaArg for c_longlong; +impl VaArg for c_ushort; +impl VaArg for c_uint; +impl VaArg for c_ulong; +impl VaArg for c_ulonglong; +impl VaArg for c_float; +impl VaArg for c_double; +impl VaArg for int8_t; +impl VaArg for int16_t; +impl VaArg for int32_t; +impl VaArg for int64_t; +impl VaArg for uint8_t; +impl VaArg for uint16_t; +impl VaArg for uint32_t; +impl VaArg for uint64_t; +``` + +Note that extracting an argument from a `VaList` follows the platform-specific +rules for argument passing and promotion. In particular, many platforms promote +any argument smaller than a C `int` to an `int`. On such platforms, extracting +the corresponding type will extract an `int` and convert appropriately. + +Like the underlying platform `va_list` structure in C, `VaList` has an opaque, +platform-specific representation. + +A variadic function may call `VaList::start` more than once, and traverse the +argument list more than once. + +A variadic function may pass the `VaList` to another function. However, it may +not return the `VaList` or otherwise allow it to outlive that call to the +variadic function. + +Defining a variadic function, or calling any of these new functions, requires a +feature-gate, `c_variadic`. + +Sample Rust code exposing a variadic function: + +```rust +#![feature(c_variadic)] +use std::intrinsics::{VaArg, VaList}; + +#[no_mangle] +pub unsafe extern "C" fn func(fixed: u32, ...) { + let args = VaList::start(); + let x: u8 = args::arg(); + let y: u16 = args::arg(); + let z: u32 = args::arg(); + println!("{} {} {} {}", fixed, x, y, z); +} +``` + +Sample C code calling that function: + +```c +#include + +void func(uint32_t fixed, ...); + +int main(void) +{ + uint8_t x = 10; + uint16_t y = 15; + uint32_t z = 20; + func(5, x, y, z); + return 0; +} +``` + +Compiling and linking these two together will produce a program that prints: + +```text +5 10 15 20 +``` + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + +LLVM already provides a set of intrinsics, implementing `va_start`, `va_arg`, +`va_end`, and `va_copy`. The implementation of `VaList::start` will call the +`va_start` intrinsic. The implementation of `VaList::arg` will call `va_arg`. +The implementation of `Clone` for `VaList` wil call `va_copy`. The +implementation of `Drop` for `VaList` wil call `va_end`. + +This RFC intentionally does not specify the mechanism used to implement the +`VaArg` trait, as the compiler may need to natively implement `VaList::arg` +with appropriate understanding of platform-specific conventions. Code outside +of `core`, `std`, and `libc` may not implement this trait for any other type. + +Note that on some platforms, these LLVM intrinsics do not fully implement the +necessary functionality, expecting the invoker of the intrinsic to provide +additional LLVM IR code. On such platforms, rustc will need to provide the +appropriate additional code, just as clang does. + +# Drawbacks +[drawbacks]: #drawbacks + +This feature is highly unsafe, and requires carefully written code to extract +the appropriate argument types provided by the caller, based on whatever +arbitrary runtime information determines those types. However, in this regard, +this feature provides no more unsafety than the equivalent C code, and in fact +provides several additional safety mechanisms, such as automatic handling of +type promotions, lifetimes, copies, and destruction. + +# Rationale and Alternatives +[alternatives]: #alternatives + +This represents one of the few C-compatible interfaces that Rust does not +provide. Currently, Rust code wishing to interoperate with C has no alternative +to this mechanism, other than hand-written C stubs. This also limits the +ability to incrementally translate C to Rust, or to bind to C interfaces that +expect variadic callbacks. + +Rather than having the compiler invent an appropriate lifetime parameter, we +could simply require the unsafe code implementing a variadic function to avoid +ever allowing the `VaList` structure to outlive it. However, if we can provide +an appropriate compile-time lifetime check, doing would make it easier to +correctly write the appropriate unsafe code. + +Rather than defining a `VaList::start` function, we could require specifying a +name along with the `...`: + +```rust +pub unsafe extern "C" fn func(fixed: u32, ...args) { + // implementation +} +``` + +This might simplify the provision of an appropriate lifetime, and would avoid +the need to provide a `VaList::start` function and only allow calling it from +within a variadic function. + +However, such an approach would not expose any means of calling `va_start` +multiple times in the same variadic function. Note that doing so has a +different semantic than calling `va_copy`, as calling `va_start` again iterates +over the arguments from the beginning rather than the current point. Given that +this mechanism exists for the sole purpose of interoperability with C, more +closely matching the underlying C interface seems appropriate. + +# Unresolved questions +[unresolved]: #unresolved-questions + +When implementing this feature, we will need to determine whether the compiler +can provide an appropriate lifetime that prevents a `VaList` from outliving its +corresponding variadic function. From 3039f17281d67d4a90e4f53a5f0251351e6b6457 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sat, 2 Sep 2017 17:53:09 -0700 Subject: [PATCH 02/25] Add VaArg for size_t and ssize_t; sort and group the VaArg impls better --- text/0000-variadic.md | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/text/0000-variadic.md b/text/0000-variadic.md index 8829305f76c..2c4d9106275 100644 --- a/text/0000-variadic.md +++ b/text/0000-variadic.md @@ -95,13 +95,16 @@ impl VaArg for i16; impl VaArg for i32; impl VaArg for i64; impl VaArg for isize; + impl VaArg for u8; impl VaArg for u16; impl VaArg for u32; impl VaArg for u64; impl VaArg for usize; + impl VaArg for f32; impl VaArg for f64; + impl VaArg for *const T; impl VaArg for *mut T; ``` @@ -113,24 +116,34 @@ the raw C types corresponding to the Rust integer and float types above: impl VaArg for c_char; impl VaArg for c_schar; impl VaArg for c_uchar; + impl VaArg for c_short; -impl VaArg for c_int; -impl VaArg for c_long; -impl VaArg for c_longlong; impl VaArg for c_ushort; + +impl VaArg for c_int; impl VaArg for c_uint; + +impl VaArg for c_long; impl VaArg for c_ulong; + +impl VaArg for c_longlong; impl VaArg for c_ulonglong; + impl VaArg for c_float; impl VaArg for c_double; + impl VaArg for int8_t; impl VaArg for int16_t; impl VaArg for int32_t; impl VaArg for int64_t; + impl VaArg for uint8_t; impl VaArg for uint16_t; impl VaArg for uint32_t; impl VaArg for uint64_t; + +impl VaArg for size_t; +impl VaArg for ssize_t; ``` Note that extracting an argument from a `VaList` follows the platform-specific From 12eb69fc3b5c67d4424593cfe89b5d024864a4b1 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sat, 2 Sep 2017 17:54:06 -0700 Subject: [PATCH 03/25] Allow calling extern "C" functions that accept a `va_list` --- text/0000-variadic.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/text/0000-variadic.md b/text/0000-variadic.md index 2c4d9106275..e676b85b4dd 100644 --- a/text/0000-variadic.md +++ b/text/0000-variadic.md @@ -161,6 +161,18 @@ A variadic function may pass the `VaList` to another function. However, it may not return the `VaList` or otherwise allow it to outlive that call to the variadic function. +A function declared with `extern "C"` may accept a `VaList` parameter, +corresponding to a `va_list` parameter in the corresponding C function. For +instance, the `libc` crate could define the `va_list` variants of `printf` as +follows: + +```rust +pub unsafe extern "C" fn vprintf(format: *const c_char, ap: VaList) -> c_int; +pub unsafe extern "C" fn vfprintf(stream: *mut FILE, format: *const c_char, ap: VaList) -> c_int; +pub unsafe extern "C" fn vsprintf(s: *mut c_char, format: *const c_char, ap: VaList) -> c_int; +pub unsafe extern "C" fn vsnprintf(s: *mut c_char, n: size_t, format: *const c_char, ap: VaList) -> c_int; +``` + Defining a variadic function, or calling any of these new functions, requires a feature-gate, `c_variadic`. From e6b5cfb3846a9f8b8a9bfc172b399b1e0cb4bac1 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 3 Sep 2017 09:12:08 -0700 Subject: [PATCH 04/25] Drop variadic closures Rust doesn't support passing a closure as an `extern "C" fn`, so these wouldn't serve any purpose yet. --- text/0000-variadic.md | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/text/0000-variadic.md b/text/0000-variadic.md index e676b85b4dd..034f75d1817 100644 --- a/text/0000-variadic.md +++ b/text/0000-variadic.md @@ -54,17 +54,6 @@ such a function as a symbol for C code to call directly, the function may want to use `#[no_mangle]` as well; however, Rust code may also pass the function to C code expecting a function pointer to a variadic function. -Unsafe Rust code can also define a variadic closure: - -```rust -let closure = |arg, arg2, ...| { - // implementation -}; -``` - -Rust code may pass a variadic closure to C code expecting a pointer to a -variadic function. - To access the arguments, Rust provides the following public interfaces in `core::intrinsics` (also available via `std::intrinsics`): @@ -285,3 +274,7 @@ closely matching the underlying C interface seems appropriate. When implementing this feature, we will need to determine whether the compiler can provide an appropriate lifetime that prevents a `VaList` from outliving its corresponding variadic function. + +Currently, Rust does not allow passing a closure to C code expecting a pointer +to an `extern "C"` function. If this becomes possible in the future, then +variadic closures would become useful, and we should add them at that time. From e9fe291b6f5c8fd86c802278f576bdc289c20f52 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 3 Sep 2017 09:13:34 -0700 Subject: [PATCH 05/25] Fix typo in sample code --- text/0000-variadic.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/text/0000-variadic.md b/text/0000-variadic.md index 034f75d1817..2c209ed21e1 100644 --- a/text/0000-variadic.md +++ b/text/0000-variadic.md @@ -174,9 +174,9 @@ use std::intrinsics::{VaArg, VaList}; #[no_mangle] pub unsafe extern "C" fn func(fixed: u32, ...) { let args = VaList::start(); - let x: u8 = args::arg(); - let y: u16 = args::arg(); - let z: u32 = args::arg(); + let x: u8 = args.arg(); + let y: u16 = args.arg(); + let z: u32 = args.arg(); println!("{} {} {} {}", fixed, x, y, z); } ``` From 247991e47b14fb1200dfbab6960168b0507234e4 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 3 Sep 2017 09:34:48 -0700 Subject: [PATCH 06/25] Drop impls for libc, mention that type aliases cover this --- text/0000-variadic.md | 40 ++++------------------------------------ 1 file changed, 4 insertions(+), 36 deletions(-) diff --git a/text/0000-variadic.md b/text/0000-variadic.md index 2c209ed21e1..35ea4b7fab9 100644 --- a/text/0000-variadic.md +++ b/text/0000-variadic.md @@ -98,42 +98,10 @@ impl VaArg for *const T; impl VaArg for *mut T; ``` -The `libc` crate additionally provides implementations of the `VaArg` trait for -the raw C types corresponding to the Rust integer and float types above: - -```rust -impl VaArg for c_char; -impl VaArg for c_schar; -impl VaArg for c_uchar; - -impl VaArg for c_short; -impl VaArg for c_ushort; - -impl VaArg for c_int; -impl VaArg for c_uint; - -impl VaArg for c_long; -impl VaArg for c_ulong; - -impl VaArg for c_longlong; -impl VaArg for c_ulonglong; - -impl VaArg for c_float; -impl VaArg for c_double; - -impl VaArg for int8_t; -impl VaArg for int16_t; -impl VaArg for int32_t; -impl VaArg for int64_t; - -impl VaArg for uint8_t; -impl VaArg for uint16_t; -impl VaArg for uint32_t; -impl VaArg for uint64_t; - -impl VaArg for size_t; -impl VaArg for ssize_t; -``` +All of the corresponding C integer and float types defined in the `libc` crate +consist of aliases for the underlying Rust types, making it unnecessary for +`libc` to provide additional implementations of the `VaArg` trait. Nothing +outside of `core` should define any implementation of `VaArg`. Note that extracting an argument from a `VaList` follows the platform-specific rules for argument passing and promotion. In particular, many platforms promote From 0cca2ce2874b805c762126dff24835b3a8d31731 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 3 Sep 2017 09:35:00 -0700 Subject: [PATCH 07/25] Make VaArg an unsafe trait --- text/0000-variadic.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/text/0000-variadic.md b/text/0000-variadic.md index 35ea4b7fab9..75e6a2ed10a 100644 --- a/text/0000-variadic.md +++ b/text/0000-variadic.md @@ -77,25 +77,25 @@ impl<'a> Clone for VaList<'a>; impl<'a> Drop for VaList<'a>; /// The type of arguments extractable from VaList -trait VaArg; +unsafe trait VaArg; -impl VaArg for i8; -impl VaArg for i16; -impl VaArg for i32; -impl VaArg for i64; -impl VaArg for isize; +unsafe impl VaArg for i8; +unsafe impl VaArg for i16; +unsafe impl VaArg for i32; +unsafe impl VaArg for i64; +unsafe impl VaArg for isize; -impl VaArg for u8; -impl VaArg for u16; -impl VaArg for u32; -impl VaArg for u64; -impl VaArg for usize; +unsafe impl VaArg for u8; +unsafe impl VaArg for u16; +unsafe impl VaArg for u32; +unsafe impl VaArg for u64; +unsafe impl VaArg for usize; -impl VaArg for f32; -impl VaArg for f64; +unsafe impl VaArg for f32; +unsafe impl VaArg for f64; -impl VaArg for *const T; -impl VaArg for *mut T; +unsafe impl VaArg for *const T; +unsafe impl VaArg for *mut T; ``` All of the corresponding C integer and float types defined in the `libc` crate From 3161c452d63f070773796c7d4cd104ec15f32446 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 3 Sep 2017 09:47:57 -0700 Subject: [PATCH 08/25] Fix typo in sample code --- text/0000-variadic.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-variadic.md b/text/0000-variadic.md index 75e6a2ed10a..9d4ff01d6b6 100644 --- a/text/0000-variadic.md +++ b/text/0000-variadic.md @@ -70,7 +70,7 @@ impl<'a> VaList<'a> { pub unsafe fn start() -> VaList<'a>; /// Extract the next argument from the argument list. - pub unsafe fn arg(&mut self) -> T; + pub unsafe fn arg(&mut self) -> T; } impl<'a> Clone for VaList<'a>; From 7b7b3ff358d2f7d4577093c3bee9896d0e6e3e70 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 3 Sep 2017 09:50:09 -0700 Subject: [PATCH 09/25] Drop VaList::start; name the VaList argument instead, to improve lifetimes This allows the compiler to pass an appropriate lifetime without as much magic. --- text/0000-variadic.md | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/text/0000-variadic.md b/text/0000-variadic.md index 9d4ff01d6b6..d11d01eb64f 100644 --- a/text/0000-variadic.md +++ b/text/0000-variadic.md @@ -43,17 +43,22 @@ mechanism. Such a declaration looks like this: ```rust -pub unsafe extern "C" fn func(arg: T, arg2: T2, ...) { +pub unsafe extern "C" fn func(arg: T, arg2: T2, args: ...) { // implementation } ``` -The `...` at the end of the argument list declares the function as variadic. -The function must use `extern "C"`, and must use `unsafe`. To expose +The use of `...` as the type of `args` at the end of the argument list declares +the function as variadic. This must appear as the last argument of the +function. The function must use `extern "C"`, and must use `unsafe`. To expose such a function as a symbol for C code to call directly, the function may want to use `#[no_mangle]` as well; however, Rust code may also pass the function to C code expecting a function pointer to a variadic function. +The `args` named in the function declaration has the type +`core::intrinsics::VaList<'a>`, where the compiler supplies a lifetime `'a` +that prevents the arguments from outliving the variadic function. + To access the arguments, Rust provides the following public interfaces in `core::intrinsics` (also available via `std::intrinsics`): @@ -63,12 +68,6 @@ To access the arguments, Rust provides the following public interfaces in pub struct VaList<'a>; impl<'a> VaList<'a> { - /// Obtain the variable arguments of the current function. Produces a - /// compile-time error if called from a non-variadic function. The compiler - /// will supply the appropriate lifetime when called, and prevent that - /// lifetime from outliving the variadic function. - pub unsafe fn start() -> VaList<'a>; - /// Extract the next argument from the argument list. pub unsafe fn arg(&mut self) -> T; } @@ -111,11 +110,9 @@ the corresponding type will extract an `int` and convert appropriately. Like the underlying platform `va_list` structure in C, `VaList` has an opaque, platform-specific representation. -A variadic function may call `VaList::start` more than once, and traverse the -argument list more than once. - -A variadic function may pass the `VaList` to another function. However, it may -not return the `VaList` or otherwise allow it to outlive that call to the +A variadic function may pass the `VaList` to another function. However, the +lifetime attached to the `VaList` will prevent the variadic function from +returning the `VaList` or otherwise allowing it to outlive that call to the variadic function. A function declared with `extern "C"` may accept a `VaList` parameter, @@ -137,11 +134,10 @@ Sample Rust code exposing a variadic function: ```rust #![feature(c_variadic)] -use std::intrinsics::{VaArg, VaList}; +use std::intrinsics::VaArg; #[no_mangle] -pub unsafe extern "C" fn func(fixed: u32, ...) { - let args = VaList::start(); +pub unsafe extern "C" fn func(fixed: u32, args: ...) { let x: u8 = args.arg(); let y: u16 = args.arg(); let z: u32 = args.arg(); From 9d060c41409035d1903ab85d9962a811ff1c646c Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 3 Sep 2017 10:04:40 -0700 Subject: [PATCH 10/25] Declare args as `mut` --- text/0000-variadic.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/0000-variadic.md b/text/0000-variadic.md index d11d01eb64f..f51da0f25cc 100644 --- a/text/0000-variadic.md +++ b/text/0000-variadic.md @@ -43,7 +43,7 @@ mechanism. Such a declaration looks like this: ```rust -pub unsafe extern "C" fn func(arg: T, arg2: T2, args: ...) { +pub unsafe extern "C" fn func(arg: T, arg2: T2, mut args: ...) { // implementation } ``` @@ -137,7 +137,7 @@ Sample Rust code exposing a variadic function: use std::intrinsics::VaArg; #[no_mangle] -pub unsafe extern "C" fn func(fixed: u32, args: ...) { +pub unsafe extern "C" fn func(fixed: u32, mut args: ...) { let x: u8 = args.arg(); let y: u16 = args.arg(); let z: u32 = args.arg(); From 376967039cef8494e87acbcb0cd9704327c1a94b Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 3 Sep 2017 11:08:02 -0700 Subject: [PATCH 11/25] Rework the alternatives; the new syntax isn't the "alternative" anymore --- text/0000-variadic.md | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/text/0000-variadic.md b/text/0000-variadic.md index f51da0f25cc..f6a9f638c3d 100644 --- a/text/0000-variadic.md +++ b/text/0000-variadic.md @@ -212,25 +212,16 @@ ever allowing the `VaList` structure to outlive it. However, if we can provide an appropriate compile-time lifetime check, doing would make it easier to correctly write the appropriate unsafe code. -Rather than defining a `VaList::start` function, we could require specifying a -name along with the `...`: - -```rust -pub unsafe extern "C" fn func(fixed: u32, ...args) { - // implementation -} -``` - -This might simplify the provision of an appropriate lifetime, and would avoid -the need to provide a `VaList::start` function and only allow calling it from -within a variadic function. - -However, such an approach would not expose any means of calling `va_start` -multiple times in the same variadic function. Note that doing so has a -different semantic than calling `va_copy`, as calling `va_start` again iterates -over the arguments from the beginning rather than the current point. Given that -this mechanism exists for the sole purpose of interoperability with C, more -closely matching the underlying C interface seems appropriate. +Rather than naming the argument in the variadic function signature, we could +provide a `VaList::start` function to return one. This would also allow calling +`start` more than once. However, this would complicate the lifetime handling +required to ensure that the `VaList` does not outlive the call to the variadic +function. + +We could use several alternative syntaxes to declare the argument in the +signature, including `...args`, or listing the `VaList` or `VaList<'a>` type +explicitly. The latter, however, would require care to ensure that code could +not reference or alias the lifetime. # Unresolved questions [unresolved]: #unresolved-questions From 63b5545b31a8bd6b28c9df70308833e8561c4eda Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 3 Sep 2017 11:54:51 -0700 Subject: [PATCH 12/25] Fix another reference to VaList::start --- text/0000-variadic.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/text/0000-variadic.md b/text/0000-variadic.md index f6a9f638c3d..5e297937dee 100644 --- a/text/0000-variadic.md +++ b/text/0000-variadic.md @@ -172,10 +172,11 @@ Compiling and linking these two together will produce a program that prints: [reference-level-explanation]: #reference-level-explanation LLVM already provides a set of intrinsics, implementing `va_start`, `va_arg`, -`va_end`, and `va_copy`. The implementation of `VaList::start` will call the -`va_start` intrinsic. The implementation of `VaList::arg` will call `va_arg`. -The implementation of `Clone` for `VaList` wil call `va_copy`. The -implementation of `Drop` for `VaList` wil call `va_end`. +`va_end`, and `va_copy`. The compiler will insert a call to the `va_start` +intrinsic at the start of the function to provide the `VaList` argument (if +used). The implementation of `VaList::arg` will call `va_arg`. The +implementation of `Clone` for `VaList` wil call `va_copy`. The implementation +of `Drop` for `VaList` wil call `va_end`. This RFC intentionally does not specify the mechanism used to implement the `VaArg` trait, as the compiler may need to natively implement `VaList::arg` From 5437ab2dbe43bcb61966a9ae2da618373f639122 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 5 Sep 2017 15:04:28 -0700 Subject: [PATCH 13/25] Clarify the description of argument promotion --- text/0000-variadic.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/text/0000-variadic.md b/text/0000-variadic.md index 5e297937dee..d6823508b06 100644 --- a/text/0000-variadic.md +++ b/text/0000-variadic.md @@ -102,10 +102,11 @@ consist of aliases for the underlying Rust types, making it unnecessary for `libc` to provide additional implementations of the `VaArg` trait. Nothing outside of `core` should define any implementation of `VaArg`. -Note that extracting an argument from a `VaList` follows the platform-specific -rules for argument passing and promotion. In particular, many platforms promote -any argument smaller than a C `int` to an `int`. On such platforms, extracting -the corresponding type will extract an `int` and convert appropriately. +Note that extracting an argument from a `VaList` follows the C rules for +argument passing and promotion. In particular, C code will promote any argument +smaller than a C `int` to an `int`, and promote `float` to `double`. Thus, +Rust's argument extractions for the corresponding types will extract an `int` +or `double` as appropriate, and convert appropriately. Like the underlying platform `va_list` structure in C, `VaList` has an opaque, platform-specific representation. From 7bd8d7df5a57b42e6f25fbf99680a124295d85b2 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 5 Sep 2017 16:45:32 -0700 Subject: [PATCH 14/25] Get rid of the VaArg trait Just specify that the compiler must ensure that VaList::arg gets called only for types allowed in FFI calls, including `#[repr(C)]` structs and non-value-carrying enums with a `repr`-specified discriminant type. --- text/0000-variadic.md | 47 ++++++++++++++----------------------------- 1 file changed, 15 insertions(+), 32 deletions(-) diff --git a/text/0000-variadic.md b/text/0000-variadic.md index d6823508b06..2b4711f7fbd 100644 --- a/text/0000-variadic.md +++ b/text/0000-variadic.md @@ -68,39 +68,23 @@ To access the arguments, Rust provides the following public interfaces in pub struct VaList<'a>; impl<'a> VaList<'a> { - /// Extract the next argument from the argument list. - pub unsafe fn arg(&mut self) -> T; + /// Extract the next argument from the argument list. T must have a type + /// usable in an FFI interface. + pub unsafe fn arg(&mut self) -> T; } impl<'a> Clone for VaList<'a>; impl<'a> Drop for VaList<'a>; - -/// The type of arguments extractable from VaList -unsafe trait VaArg; - -unsafe impl VaArg for i8; -unsafe impl VaArg for i16; -unsafe impl VaArg for i32; -unsafe impl VaArg for i64; -unsafe impl VaArg for isize; - -unsafe impl VaArg for u8; -unsafe impl VaArg for u16; -unsafe impl VaArg for u32; -unsafe impl VaArg for u64; -unsafe impl VaArg for usize; - -unsafe impl VaArg for f32; -unsafe impl VaArg for f64; - -unsafe impl VaArg for *const T; -unsafe impl VaArg for *mut T; ``` +The type returned from `VaList::arg` must have a type usable in an FFI +interface: `i8`, `i16`, `i32`, `i64`, `isize`, `u8`, `u16`, `u32`, `u64`, +`usize`, `f32`, `f64`, `*const _`, `*mut _`, a `#[repr(C)]` struct or union, or +a non-value-carrying enum with a `repr`-specified discriminant type. + All of the corresponding C integer and float types defined in the `libc` crate -consist of aliases for the underlying Rust types, making it unnecessary for -`libc` to provide additional implementations of the `VaArg` trait. Nothing -outside of `core` should define any implementation of `VaArg`. +consist of aliases for the underlying Rust types, so `VaList::arg` can also +extract those types. Note that extracting an argument from a `VaList` follows the C rules for argument passing and promotion. In particular, C code will promote any argument @@ -135,7 +119,6 @@ Sample Rust code exposing a variadic function: ```rust #![feature(c_variadic)] -use std::intrinsics::VaArg; #[no_mangle] pub unsafe extern "C" fn func(fixed: u32, mut args: ...) { @@ -179,16 +162,16 @@ used). The implementation of `VaList::arg` will call `va_arg`. The implementation of `Clone` for `VaList` wil call `va_copy`. The implementation of `Drop` for `VaList` wil call `va_end`. -This RFC intentionally does not specify the mechanism used to implement the -`VaArg` trait, as the compiler may need to natively implement `VaList::arg` -with appropriate understanding of platform-specific conventions. Code outside -of `core`, `std`, and `libc` may not implement this trait for any other type. - Note that on some platforms, these LLVM intrinsics do not fully implement the necessary functionality, expecting the invoker of the intrinsic to provide additional LLVM IR code. On such platforms, rustc will need to provide the appropriate additional code, just as clang does. +This RFC intentionally does not specify or expose the mechanism used to limit +the use of `VaList::arg` only to specific types. The compiler should provide +errors similar to those associated with passing types through FFI function +calls. + # Drawbacks [drawbacks]: #drawbacks From 96f80ace9eb8f1d4643a27177fc332e1221599f3 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 5 Sep 2017 16:47:45 -0700 Subject: [PATCH 15/25] Don't impl `Drop` directly; just talk about the necessary drop semantics --- text/0000-variadic.md | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/text/0000-variadic.md b/text/0000-variadic.md index 2b4711f7fbd..b6c949cee10 100644 --- a/text/0000-variadic.md +++ b/text/0000-variadic.md @@ -74,7 +74,6 @@ impl<'a> VaList<'a> { } impl<'a> Clone for VaList<'a>; -impl<'a> Drop for VaList<'a>; ``` The type returned from `VaList::arg` must have a type usable in an FFI @@ -112,6 +111,15 @@ pub unsafe extern "C" fn vsprintf(s: *mut c_char, format: *const c_char, ap: VaL pub unsafe extern "C" fn vsnprintf(s: *mut c_char, n: size_t, format: *const c_char, ap: VaList) -> c_int; ``` +Note that, per the C semantics, after passing `VaList` to these functions, the +caller can no longer use it, hence the use of the `VaList` type to take +ownership of the object. To continue using the object after a call to these +functions, pass a clone of it instead. + +Conversely, an `unsafe extern "C"` function written in Rust may accept a +`VaList` parameter, to allow implementing the `v` variants of such functions in +Rust. Such a function must not specify the lifetime. + Defining a variadic function, or calling any of these new functions, requires a feature-gate, `c_variadic`. @@ -159,8 +167,25 @@ LLVM already provides a set of intrinsics, implementing `va_start`, `va_arg`, `va_end`, and `va_copy`. The compiler will insert a call to the `va_start` intrinsic at the start of the function to provide the `VaList` argument (if used). The implementation of `VaList::arg` will call `va_arg`. The -implementation of `Clone` for `VaList` wil call `va_copy`. The implementation -of `Drop` for `VaList` wil call `va_end`. +implementation of `Clone` for `VaList` wil call `va_copy`. The compiler will +ensure that a call to `va_end` occurs exactly once on every `VaList` at an +appropriate time, similar to drop semantics. (This may occur via an +implementation of `Drop`, but this must take into account the semantics of +passing a `VaList` to or from an `extern "C"` function.) + +The compiler may need to handle the type `VaList` specially, in order to +provide the desired drop semantics at FFI boundaries. In particular, some +platforms pass `va_list` by value, and others pass it by pointer; the standard +leaves unspecified whether changes made in the called function appear in the +caller's copy. Rust must match the underlying C semantics, to allow passing +VaList values between C and Rust. To avoid forcing variadic functions to cope +with these platform-specific differences, the compiler should ensure that the +type behaves as if it has `Drop` semantics, and has `va_end` called on the +original `VaList` as well as any clones of it. For instance, passing a `VaList` +to a `vprintf` function as declared in this RFC semantically passes ownership +of that `VaList`, and prevents calling the `arg` function again in the caller, +but it remains the caller's responsibility to call `va_end`, so the compiler +needs to insert the appropriate drop glue at the FFI boundary. Note that on some platforms, these LLVM intrinsics do not fully implement the necessary functionality, expecting the invoker of the intrinsic to provide From 18a8b36f76b18c811ed28a4ba1414469d11bf9de Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 6 Sep 2017 11:29:55 -0700 Subject: [PATCH 16/25] Fix `extern "C"` function declarations --- text/0000-variadic.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/text/0000-variadic.md b/text/0000-variadic.md index b6c949cee10..35c0ee323bf 100644 --- a/text/0000-variadic.md +++ b/text/0000-variadic.md @@ -105,10 +105,12 @@ instance, the `libc` crate could define the `va_list` variants of `printf` as follows: ```rust -pub unsafe extern "C" fn vprintf(format: *const c_char, ap: VaList) -> c_int; -pub unsafe extern "C" fn vfprintf(stream: *mut FILE, format: *const c_char, ap: VaList) -> c_int; -pub unsafe extern "C" fn vsprintf(s: *mut c_char, format: *const c_char, ap: VaList) -> c_int; -pub unsafe extern "C" fn vsnprintf(s: *mut c_char, n: size_t, format: *const c_char, ap: VaList) -> c_int; +extern "C" { + pub unsafe fn vprintf(format: *const c_char, ap: VaList) -> c_int; + pub unsafe fn vfprintf(stream: *mut FILE, format: *const c_char, ap: VaList) -> c_int; + pub unsafe fn vsprintf(s: *mut c_char, format: *const c_char, ap: VaList) -> c_int; + pub unsafe fn vsnprintf(s: *mut c_char, n: size_t, format: *const c_char, ap: VaList) -> c_int; +} ``` Note that, per the C semantics, after passing `VaList` to these functions, the From 7e5698ea5982280660c3d6ae406413bbd84c3bff Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 6 Sep 2017 11:30:23 -0700 Subject: [PATCH 17/25] Allow `VaList::arg` to return any type usable in an `extern "C" fn` signature --- text/0000-variadic.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/text/0000-variadic.md b/text/0000-variadic.md index 35c0ee323bf..42bc64eea28 100644 --- a/text/0000-variadic.md +++ b/text/0000-variadic.md @@ -76,10 +76,10 @@ impl<'a> VaList<'a> { impl<'a> Clone for VaList<'a>; ``` -The type returned from `VaList::arg` must have a type usable in an FFI -interface: `i8`, `i16`, `i32`, `i64`, `isize`, `u8`, `u16`, `u32`, `u64`, -`usize`, `f32`, `f64`, `*const _`, `*mut _`, a `#[repr(C)]` struct or union, or -a non-value-carrying enum with a `repr`-specified discriminant type. +The type returned from `VaList::arg` must have a type usable in an `extern "C"` +FFI interface; the compiler allows all the same types returned from +`VaList::arg` that it allows in the function signature of an `extern "C"` +function. All of the corresponding C integer and float types defined in the `libc` crate consist of aliases for the underlying Rust types, so `VaList::arg` can also From 105f76485b7eaa9403e33195b512da877618b801 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Thu, 7 Sep 2017 12:18:13 -0700 Subject: [PATCH 18/25] Use `extern type` to declare `VaList` (see RFC 1861) --- text/0000-variadic.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/text/0000-variadic.md b/text/0000-variadic.md index 42bc64eea28..4867e210fab 100644 --- a/text/0000-variadic.md +++ b/text/0000-variadic.md @@ -65,7 +65,7 @@ To access the arguments, Rust provides the following public interfaces in ```rust /// The argument list of a C-compatible variadic function, corresponding to the /// underlying C `va_list`. Opaque. -pub struct VaList<'a>; +pub extern type VaList<'a>; impl<'a> VaList<'a> { /// Extract the next argument from the argument list. T must have a type @@ -76,6 +76,9 @@ impl<'a> VaList<'a> { impl<'a> Clone for VaList<'a>; ``` +`VaList` may become a language item (`#[lang="VaList"]`) to attach the +appropriate compiler handling. + The type returned from `VaList::arg` must have a type usable in an `extern "C"` FFI interface; the compiler allows all the same types returned from `VaList::arg` that it allows in the function signature of an `extern "C"` From 6bdb95c314e0b588da2294ca6097a677d656fc89 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Thu, 7 Sep 2017 12:37:24 -0700 Subject: [PATCH 19/25] Move the mention of language items to the reference-level explanation --- text/0000-variadic.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/text/0000-variadic.md b/text/0000-variadic.md index 4867e210fab..07b8f237cff 100644 --- a/text/0000-variadic.md +++ b/text/0000-variadic.md @@ -76,9 +76,6 @@ impl<'a> VaList<'a> { impl<'a> Clone for VaList<'a>; ``` -`VaList` may become a language item (`#[lang="VaList"]`) to attach the -appropriate compiler handling. - The type returned from `VaList::arg` must have a type usable in an `extern "C"` FFI interface; the compiler allows all the same types returned from `VaList::arg` that it allows in the function signature of an `extern "C"` @@ -178,6 +175,9 @@ appropriate time, similar to drop semantics. (This may occur via an implementation of `Drop`, but this must take into account the semantics of passing a `VaList` to or from an `extern "C"` function.) +`VaList` may become a language item (`#[lang="VaList"]`) to attach the +appropriate compiler handling. + The compiler may need to handle the type `VaList` specially, in order to provide the desired drop semantics at FFI boundaries. In particular, some platforms pass `va_list` by value, and others pass it by pointer; the standard From 1546c827d728d9a90cc94cc56de98ecf5e25f220 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Thu, 7 Sep 2017 13:56:31 -0700 Subject: [PATCH 20/25] Use the rustdoc-style `/* fields omitted */` syntax to declare VaList `extern type` doesn't quite have the right semantics. --- text/0000-variadic.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-variadic.md b/text/0000-variadic.md index 07b8f237cff..f39b083feac 100644 --- a/text/0000-variadic.md +++ b/text/0000-variadic.md @@ -65,7 +65,7 @@ To access the arguments, Rust provides the following public interfaces in ```rust /// The argument list of a C-compatible variadic function, corresponding to the /// underlying C `va_list`. Opaque. -pub extern type VaList<'a>; +pub struct VaList<'a> { /* fields omitted */ } impl<'a> VaList<'a> { /// Extract the next argument from the argument list. T must have a type From eca3eaec3e94bf832d34d739ab5689300b143c31 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Thu, 7 Sep 2017 18:01:24 -0700 Subject: [PATCH 21/25] Require that the function have at least one non-variadic argument --- text/0000-variadic.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/text/0000-variadic.md b/text/0000-variadic.md index f39b083feac..c5dd12049de 100644 --- a/text/0000-variadic.md +++ b/text/0000-variadic.md @@ -50,10 +50,11 @@ pub unsafe extern "C" fn func(arg: T, arg2: T2, mut args: ...) { The use of `...` as the type of `args` at the end of the argument list declares the function as variadic. This must appear as the last argument of the -function. The function must use `extern "C"`, and must use `unsafe`. To expose -such a function as a symbol for C code to call directly, the function may want -to use `#[no_mangle]` as well; however, Rust code may also pass the function to -C code expecting a function pointer to a variadic function. +function, and the function must have at least one argument before it. The +function must use `extern "C"`, and must use `unsafe`. To expose such a +function as a symbol for C code to call directly, the function may want to use +`#[no_mangle]` as well; however, Rust code may also pass the function to C code +expecting a function pointer to a variadic function. The `args` named in the function declaration has the type `core::intrinsics::VaList<'a>`, where the compiler supplies a lifetime `'a` From e3d1f5c8a45f7c847280902eef80a4e755cc1fc4 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Thu, 7 Sep 2017 18:13:25 -0700 Subject: [PATCH 22/25] Add some clarifications on drop handling, and non-standard behavior --- text/0000-variadic.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/text/0000-variadic.md b/text/0000-variadic.md index c5dd12049de..540af3e33fe 100644 --- a/text/0000-variadic.md +++ b/text/0000-variadic.md @@ -191,7 +191,15 @@ original `VaList` as well as any clones of it. For instance, passing a `VaList` to a `vprintf` function as declared in this RFC semantically passes ownership of that `VaList`, and prevents calling the `arg` function again in the caller, but it remains the caller's responsibility to call `va_end`, so the compiler -needs to insert the appropriate drop glue at the FFI boundary. +needs to insert the appropriate drop glue at the FFI boundary. Conversely, +functions accepting a `VaList` argument must not drop it themselves, since the +caller will drop it instead. + +The C standard requires that the call to `va_end` for a `va_list` occur in the +same function as the matching `va_start` or `va_copy` for that `va_list`. Some +C implementations do not enforce this requirement, allowing for functions that +call `va_end` on a passed-in `va_list` that they did not create. This RFC does +not define a means of implementing or calling non-standard functions like these. Note that on some platforms, these LLVM intrinsics do not fully implement the necessary functionality, expecting the invoker of the intrinsic to provide From b2acc33c3bb7c2a575647ca109338b281fc32af2 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 12 Sep 2017 17:51:01 -0700 Subject: [PATCH 23/25] Stop using Clone; implement copy safely via a closure Huge thanks to Without Boats for help working out the details here. --- text/0000-variadic.md | 45 +++++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/text/0000-variadic.md b/text/0000-variadic.md index 540af3e33fe..acd46852944 100644 --- a/text/0000-variadic.md +++ b/text/0000-variadic.md @@ -68,13 +68,17 @@ To access the arguments, Rust provides the following public interfaces in /// underlying C `va_list`. Opaque. pub struct VaList<'a> { /* fields omitted */ } +// Note: the lifetime on VaList is invariant impl<'a> VaList<'a> { /// Extract the next argument from the argument list. T must have a type /// usable in an FFI interface. pub unsafe fn arg(&mut self) -> T; -} -impl<'a> Clone for VaList<'a>; + /// Copy the argument list. Destroys the copy after the closure returns. + pub fn copy<'ret, F, T>(&self, F) -> T + where + F: for<'copy> FnOnce(VaList<'copy>) -> T, T: 'ret; +} ``` The type returned from `VaList::arg` must have a type usable in an `extern "C"` @@ -98,7 +102,8 @@ platform-specific representation. A variadic function may pass the `VaList` to another function. However, the lifetime attached to the `VaList` will prevent the variadic function from returning the `VaList` or otherwise allowing it to outlive that call to the -variadic function. +variadic function. Similarly, the closure called by `copy` cannot return the +`VaList` passed to it or otherwise allow it to outlive the closure. A function declared with `extern "C"` may accept a `VaList` parameter, corresponding to a `va_list` parameter in the corresponding C function. For @@ -117,7 +122,7 @@ extern "C" { Note that, per the C semantics, after passing `VaList` to these functions, the caller can no longer use it, hence the use of the `VaList` type to take ownership of the object. To continue using the object after a call to these -functions, pass a clone of it instead. +functions, use `VaList::copy` to pass a copy of it instead. Conversely, an `unsafe extern "C"` function written in Rust may accept a `VaList` parameter, to allow implementing the `v` variants of such functions in @@ -169,31 +174,21 @@ Compiling and linking these two together will produce a program that prints: LLVM already provides a set of intrinsics, implementing `va_start`, `va_arg`, `va_end`, and `va_copy`. The compiler will insert a call to the `va_start` intrinsic at the start of the function to provide the `VaList` argument (if -used). The implementation of `VaList::arg` will call `va_arg`. The -implementation of `Clone` for `VaList` wil call `va_copy`. The compiler will -ensure that a call to `va_end` occurs exactly once on every `VaList` at an -appropriate time, similar to drop semantics. (This may occur via an -implementation of `Drop`, but this must take into account the semantics of -passing a `VaList` to or from an `extern "C"` function.) +used), and a matching call to the `va_end` intrinsic on any exit from the +function. The implementation of `VaList::arg` will call `va_arg`. The +implementation of `VaList::copy` wil call `va_copy`, and then `va_end` after +the closure exits. `VaList` may become a language item (`#[lang="VaList"]`) to attach the appropriate compiler handling. The compiler may need to handle the type `VaList` specially, in order to -provide the desired drop semantics at FFI boundaries. In particular, some -platforms pass `va_list` by value, and others pass it by pointer; the standard -leaves unspecified whether changes made in the called function appear in the -caller's copy. Rust must match the underlying C semantics, to allow passing -VaList values between C and Rust. To avoid forcing variadic functions to cope -with these platform-specific differences, the compiler should ensure that the -type behaves as if it has `Drop` semantics, and has `va_end` called on the -original `VaList` as well as any clones of it. For instance, passing a `VaList` -to a `vprintf` function as declared in this RFC semantically passes ownership -of that `VaList`, and prevents calling the `arg` function again in the caller, -but it remains the caller's responsibility to call `va_end`, so the compiler -needs to insert the appropriate drop glue at the FFI boundary. Conversely, -functions accepting a `VaList` argument must not drop it themselves, since the -caller will drop it instead. +provide the desired parameter-passing semantics at FFI boundaries. In +particular, some platforms define `va_list` as a single-element array, such +that declaring a `va_list` allocates storage, but passing a `va_list` as a +function parameter occurs by pointer. The compiler must arrange to handle both +receiving and passing `VaList` parameters in a manner compatible with the C +ABI. The C standard requires that the call to `va_end` for a `va_list` occur in the same function as the matching `va_start` or `va_copy` for that `va_list`. Some @@ -219,7 +214,7 @@ the appropriate argument types provided by the caller, based on whatever arbitrary runtime information determines those types. However, in this regard, this feature provides no more unsafety than the equivalent C code, and in fact provides several additional safety mechanisms, such as automatic handling of -type promotions, lifetimes, copies, and destruction. +type promotions, lifetimes, copies, and cleanup. # Rationale and Alternatives [alternatives]: #alternatives From 404283425f11f4bc79ae87576fd104e1573fe0ca Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Fri, 15 Sep 2017 13:11:05 -0700 Subject: [PATCH 24/25] Add an unresolved question on support for non-native ABIs --- text/0000-variadic.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/text/0000-variadic.md b/text/0000-variadic.md index acd46852944..e41008c866f 100644 --- a/text/0000-variadic.md +++ b/text/0000-variadic.md @@ -252,3 +252,14 @@ corresponding variadic function. Currently, Rust does not allow passing a closure to C code expecting a pointer to an `extern "C"` function. If this becomes possible in the future, then variadic closures would become useful, and we should add them at that time. + +This RFC only supports the platform's native `"C"` ABI, not any other ABI. Code +may wish to define variadic functions for another ABI, and potentially more +than one such ABI in the same program. However, such support should not +complicate the common case. LLVM has extremely limited support for this, for +only a specific pair of platforms (supporting the Windows ABI on platforms that +use the System V ABI), with no generalized support in the underlying +intrinsics. The LLVM intrinsics only support using the ABI of the containing +function. Given the current state of the ecosystem, this RFC only proposes +supporting the native `"C"` ABI for now. Doing so will not prevent the +introduction of support for non-native ABIs in the future. From eb9c392c772075f39584b31037db249b16d6d6bc Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Fri, 29 Sep 2017 10:50:30 -0700 Subject: [PATCH 25/25] RFC 2137: Support defining C-compatible variadic functions in Rust --- text/{0000-variadic.md => 2137-variadic.md} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename text/{0000-variadic.md => 2137-variadic.md} (99%) diff --git a/text/0000-variadic.md b/text/2137-variadic.md similarity index 99% rename from text/0000-variadic.md rename to text/2137-variadic.md index e41008c866f..77f60ece65d 100644 --- a/text/0000-variadic.md +++ b/text/2137-variadic.md @@ -1,7 +1,7 @@ - Feature Name: variadic - Start Date: 2017-08-21 -- RFC PR: (leave this empty) -- Rust Issue: (leave this empty) +- RFC PR: https://github.com/rust-lang/rfcs/pull/2137 +- Rust Issue: https://github.com/rust-lang/rust/issues/44930 # Summary [summary]: #summary