Skip to content

Tracking issue for RFC 2137: Support defining C-compatible variadic functions in Rust #44930

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

Open
1 of 3 tasks
Tracked by #1568
aturon opened this issue Sep 29, 2017 · 131 comments
Open
1 of 3 tasks
Tracked by #1568
Labels
A-FFI Area: Foreign function interface (FFI) B-RFC-approved Blocker: Approved by a merged RFC but not yet implemented. B-unstable Blocker: Implemented in the nightly compiler and unstable. C-tracking-issue Category: An issue tracking the progress of sth. like the implementation of an RFC F-c_variadic `#![feature(c_variadic)]` S-tracking-ready-to-stabilize Status: This is ready to stabilize; it may need a stabilization report and a PR T-lang Relevant to the language team

Comments

@aturon
Copy link
Member

aturon commented Sep 29, 2017

This is a tracking issue for the RFC "Support defining C-compatible variadic functions in Rust" (rust-lang/rfcs#2137).

Steps:

Unresolved questions:

@aturon aturon added B-RFC-approved Blocker: Approved by a merged RFC but not yet implemented. C-tracking-issue Category: An issue tracking the progress of sth. like the implementation of an RFC T-lang Relevant to the language team labels Sep 29, 2017
@plietar
Copy link
Contributor

plietar commented Sep 29, 2017

I'd like to work on this, I already have some prototype

@aturon
Copy link
Member Author

aturon commented Sep 29, 2017

Awesome @plietar! It'd probably be good to bring this up in the "middle-end" compiler working group channel, which would also be a good place to get any help you might need.

@joshtriplett
Copy link
Member

@plietar How goes the implementation? I remember you showing a mostly complete prototype on IRC.

@thedataking
Copy link

@plietar any news to share? This is a blocker for teams working on C to Rust transpilers, so this addition would be very welcome. (I'm part of one such team).

@plietar
Copy link
Contributor

plietar commented Feb 22, 2018

Hey,
Sorry I've been busy and then forgot about this. I'll get my prototype back in shape, hopefully by this weekend.

@harpocrates
Copy link
Contributor

@plietar Any update on this? Do you have a WIP branch I can check out to try / fiddle with this?

@dlrobertson
Copy link
Contributor

Looks like I'm a little late to the party 😄 ... sorry about that

A few questions:

  1. How would functions that use a va_list multiple times work without the ability to explicitly use va_start and va_end? Are they expected to use copy? E.g. execl typically loops through the arguments to to get argc, creats a array argv of size argc, loops through the list again populating argv, and finally call execv.

  2. The structure of a va_list varies greatly between the architectures. The intrinsic functions work with the architecture specific structure bitcast to an i8*, but AFAIK we'll still need to define the structure. Which architectures will be expected to be supported in the first iteration? Or am I mistaken that we'll have to define the structure?

@plietar if you don't have the time to work on this any more or if there is any way I could help out, I'd be more than happy to do so. I haven't worked on rustc much, but I'd be happy to help however I can with the implementation of this.

@nikomatsakis
Copy link
Contributor

@dlrobertson

Seems likely that @plietar doesn't have much time, though they can speak for themselves.

I've not really looked closely at what would be needed to implement this, but if you need any help, please ping me, or reach out on gitter/IRC.

@dlrobertson
Copy link
Contributor

dlrobertson commented Apr 4, 2018

I've been working on this for the past two week and have

  • Added va_list_kind to the target specification which closely mirrors clangs TargetInfo::BuiltinVaListKind
  • Added Type::va_list which builds the correct structure based on the targets va_list_kind

I'm struggling a bit with understanding how to write something in libcore and link that to Type::va_list in trans. I'm currently attempting to add #[lang = "va_list"] so that I can check the def.did against the lang_items().va_list_impl() id. Does this seem correct?

Also how would you like me to break this up into PRs? My current plan was to submit a PR once I got VaList implemented (meaning functions like vprintf could be defined and tested). Then submit a second PR for implementing support for functions like printf.

bors added a commit that referenced this issue Sep 28, 2018
libcore: Add VaList and variadic arg handling intrinsics

## Summary

 - Add intrinsics for `va_start`, `va_end`, `va_copy`, and `va_arg`.
 - Add `core::va_list::VaList` to `libcore`.

Part 1 of (at least) 3 for #44930

Comments and critiques are very much welcomed 😄
bors added a commit that referenced this issue Nov 29, 2018
libcore: Add VaList and variadic arg handling intrinsics

## Summary

 - Add intrinsics for `va_start`, `va_end`, `va_copy`, and `va_arg`.
 - Add `core::va_list::VaList` to `libcore`.

Part 1 of (at least) 3 for #44930

Comments and critiques are very much welcomed 😄
@dlrobertson
Copy link
Contributor

dlrobertson commented Nov 29, 2018

Now that the VaList structure is implemented and merged into master, I'm moving on to implementing variadic functions.

When any issues with the implementation of VaList are found, please ping me.

@alexreg
Copy link
Contributor

alexreg commented Dec 9, 2018

@dlrobertson That's super. Are you implementing the ... syntax given that seems to be the consensus so far, and bikeshedding hasn't revealed a better one? (Unless I'm behind on things.)

matthiaskrgr added a commit to matthiaskrgr/rust that referenced this issue May 21, 2025

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
…r=workingjubilee

limit impls of `VaArgSafe` to just types that are actually safe

tracking issue: rust-lang#44930

Retrieving 8- or 16-bit integer arguments from a `VaList` is not safe, because such types are subject to upcasting. See rust-lang#61275 (comment) for more detail.

This PR also makes the instances of `VaArgSafe` visible in the documentation, and uses a private sealed trait to make sure users cannot create additional impls of `VaArgSafe`, which would almost certainly cause UB.

r? `@workingjubilee`
rust-timer added a commit to rust-lang-ci/rust that referenced this issue May 22, 2025

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
Rollup merge of rust-lang#141341 - folkertdev:limit-VaArgSafe-impls, r=workingjubilee

limit impls of `VaArgSafe` to just types that are actually safe

tracking issue: rust-lang#44930

Retrieving 8- or 16-bit integer arguments from a `VaList` is not safe, because such types are subject to upcasting. See rust-lang#61275 (comment) for more detail.

This PR also makes the instances of `VaArgSafe` visible in the documentation, and uses a private sealed trait to make sure users cannot create additional impls of `VaArgSafe`, which would almost certainly cause UB.

r? `@workingjubilee`
@folkertdev
Copy link
Contributor

folkertdev commented May 22, 2025

We need c_variadics on stable for zlib-rs, so I'm going to try and move this forward. A draft for a modified API is at

https://gist.github.com/folkertdev/47c79e2f5b03f1138db9d53be5e51ed1

Some highlights

  • VaListImpl is private!
  • VaList just has a single lifetime parameter now, which makes a lot more sense
  • The VaList::with_copy function and a new va_copy! macro can be used to duplicate a VaList

This all relies on a different desugaring of ..., roughly

fn foo(placeholder: i32, mut args: ...) {
    // stuff
}

// --->

fn foo(placeholder: i32) {
    let tag = core::mem::MaybeUninit::<VaListImpl>::uninit();
    unsafe { core::instrinsics::va_start(tag.as_mut_ptr()) }
    let mut args = VaList(tag.assume_init_mut())

    // stuff
}

It seems possible to make that work internally.


I think this new API is nicer, because it leaks fewer implementation details. But before we start implementation work, does anyone see issues with this direction?

pinging specifically the c2rust folks @thedataking @ahomescu, you've probably worked with the existing c_variadics most.

@traviscross
Copy link
Contributor

@rustbot labels -I-lang-nominated

Let's fork discussion of that proposal out to:

We'll take the nomination there.

@rustbot rustbot removed the I-lang-nominated Nominated for discussion during a lang team meeting. label May 24, 2025
jhpratt added a commit to jhpratt/rust that referenced this issue May 25, 2025

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
…ubilee

use `cfg_select!` to select the right `VaListImpl` definition

tracking issue: rust-lang#44930

Just a bit of cleanup really.

We could use `PhantomInvariantLifetime<'f>` (rust-lang#135806) to make it more precise what that `PhantomData<&'f mut &'f c_void>` marker is doing. I'm not sure how ready that feature is though, `@jhpratt` are these types good to use internally?

---

Some research into the lifetimes of `VaList` and `VaListImpl`:

It's easy to see why the lifetime of these types should not be extended, a `VaList` or `VaListImpl` escaping its function is a bad idea. I don't currently see why coercing the lifetime to a shorter lifetime is problematic though, but probably I just don't understand variance well enough to see it. The history does not provide much explanation:

- immunant@0814087 original implementation
- immunant@b9ea653 adds `VaListImpl<'f>`, but it is only covariant in `'f`
- rust-lang#62639 makes `VaListImpl<'f>` invariant over `'f` (because `VaList<'a, 'f>` is already invariant over `'f`, but I think that is just an implementation detail?)

Beyond that I don't see how the lifetime situation can be simplified significantly, e.g. this function really needs `'copy` to be unconstrained.

```rust
/// Copies the `va_list` at the current location.
pub unsafe fn with_copy<F, R>(&self, f: F) -> R
where
    F: for<'copy> FnOnce(VaList<'copy, 'f>) -> R,
{
    let mut ap = self.clone();
    let ret = f(ap.as_va_list());
    // SAFETY: the caller must uphold the safety contract for `va_end`.
    unsafe {
        va_end(&mut ap);
    }
    ret
}
```

`@rustbot` label +F-c_variadic
r? `@workingjubilee`
rust-timer added a commit that referenced this issue May 25, 2025

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
Rollup merge of #141361 - folkertdev:varargs-cfg, r=workingjubilee

use `cfg_select!` to select the right `VaListImpl` definition

tracking issue: #44930

Just a bit of cleanup really.

We could use `PhantomInvariantLifetime<'f>` (#135806) to make it more precise what that `PhantomData<&'f mut &'f c_void>` marker is doing. I'm not sure how ready that feature is though, `@jhpratt` are these types good to use internally?

---

Some research into the lifetimes of `VaList` and `VaListImpl`:

It's easy to see why the lifetime of these types should not be extended, a `VaList` or `VaListImpl` escaping its function is a bad idea. I don't currently see why coercing the lifetime to a shorter lifetime is problematic though, but probably I just don't understand variance well enough to see it. The history does not provide much explanation:

- immunant@0814087 original implementation
- immunant@b9ea653 adds `VaListImpl<'f>`, but it is only covariant in `'f`
- #62639 makes `VaListImpl<'f>` invariant over `'f` (because `VaList<'a, 'f>` is already invariant over `'f`, but I think that is just an implementation detail?)

Beyond that I don't see how the lifetime situation can be simplified significantly, e.g. this function really needs `'copy` to be unconstrained.

```rust
/// Copies the `va_list` at the current location.
pub unsafe fn with_copy<F, R>(&self, f: F) -> R
where
    F: for<'copy> FnOnce(VaList<'copy, 'f>) -> R,
{
    let mut ap = self.clone();
    let ret = f(ap.as_va_list());
    // SAFETY: the caller must uphold the safety contract for `va_end`.
    unsafe {
        va_end(&mut ap);
    }
    ret
}
```

`@rustbot` label +F-c_variadic
r? `@workingjubilee`
GuillaumeGomez added a commit to GuillaumeGomez/rust that referenced this issue May 25, 2025

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
…workingjubilee

make teach_help message for cast-before-pass-to-variadic more precise

r? `@workingjubilee`
based on your comment [here](rust-lang#44930 (comment))
rust-timer added a commit that referenced this issue May 25, 2025

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
Rollup merge of #141443 - RalfJung:c-variadic-teach-help, r=workingjubilee

make teach_help message for cast-before-pass-to-variadic more precise

r? `@workingjubilee`
based on your comment [here](#44930 (comment))
github-actions bot pushed a commit to model-checking/verify-rust-std that referenced this issue May 26, 2025
…r=workingjubilee

limit impls of `VaArgSafe` to just types that are actually safe

tracking issue: rust-lang#44930

Retrieving 8- or 16-bit integer arguments from a `VaList` is not safe, because such types are subject to upcasting. See rust-lang#61275 (comment) for more detail.

This PR also makes the instances of `VaArgSafe` visible in the documentation, and uses a private sealed trait to make sure users cannot create additional impls of `VaArgSafe`, which would almost certainly cause UB.

r? `@workingjubilee`
github-actions bot pushed a commit to model-checking/verify-rust-std that referenced this issue May 26, 2025

Unverified

This commit is not signed, but one or more authors requires that any commit attributed to them is signed.
…ubilee

use `cfg_select!` to select the right `VaListImpl` definition

tracking issue: rust-lang#44930

Just a bit of cleanup really.

We could use `PhantomInvariantLifetime<'f>` (rust-lang#135806) to make it more precise what that `PhantomData<&'f mut &'f c_void>` marker is doing. I'm not sure how ready that feature is though, `@jhpratt` are these types good to use internally?

---

Some research into the lifetimes of `VaList` and `VaListImpl`:

It's easy to see why the lifetime of these types should not be extended, a `VaList` or `VaListImpl` escaping its function is a bad idea. I don't currently see why coercing the lifetime to a shorter lifetime is problematic though, but probably I just don't understand variance well enough to see it. The history does not provide much explanation:

- immunant@0814087 original implementation
- immunant@b9ea653 adds `VaListImpl<'f>`, but it is only covariant in `'f`
- rust-lang#62639 makes `VaListImpl<'f>` invariant over `'f` (because `VaList<'a, 'f>` is already invariant over `'f`, but I think that is just an implementation detail?)

Beyond that I don't see how the lifetime situation can be simplified significantly, e.g. this function really needs `'copy` to be unconstrained.

```rust
/// Copies the `va_list` at the current location.
pub unsafe fn with_copy<F, R>(&self, f: F) -> R
where
    F: for<'copy> FnOnce(VaList<'copy, 'f>) -> R,
{
    let mut ap = self.clone();
    let ret = f(ap.as_va_list());
    // SAFETY: the caller must uphold the safety contract for `va_end`.
    unsafe {
        va_end(&mut ap);
    }
    ret
}
```

`@rustbot` label +F-c_variadic
r? `@workingjubilee`
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this issue May 27, 2025

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
…r=workingjubilee

use custom types to clarify arguments to `emit_ptr_va_arg`

tracking issue: rust-lang#44930

split out of rust-lang#141622

r? `@workingjubilee`
`@rustbot` label: +F-c_variadic
compiler-errors added a commit to compiler-errors/rust that referenced this issue May 27, 2025

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
…r=workingjubilee

use custom types to clarify arguments to `emit_ptr_va_arg`

tracking issue: rust-lang#44930

split out of rust-lang#141622

r? ``@workingjubilee``
``@rustbot`` label: +F-c_variadic
rust-timer added a commit that referenced this issue May 27, 2025

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
Rollup merge of #141623 - folkertdev:va-arg-explicit-types, r=workingjubilee

use custom types to clarify arguments to `emit_ptr_va_arg`

tracking issue: #44930

split out of #141622

r? ``@workingjubilee``
``@rustbot`` label: +F-c_variadic
bors added a commit that referenced this issue May 28, 2025
implement `va_arg` for x86_64 systemv

tracking issue: #44930

Turns out LLVM's `va_arg` is also unreliable for this target.

llvm/llvm-project#141361

So, like clang, we implement our own. I used

- the spec at https://gitlab.com/x86-psABIs/x86-64-ABI
- the clang implementation at https://github.com/llvm/llvm-project/blob/9a440f84773c56d3803f330774acb2b4f471d5b4/clang/lib/CodeGen/Targets/X86.cpp#L3041

We can take a bunch of shortcuts because the return type of `va_list` must implement `VaArgSafe`. I also extended some of the tests, because up to 11 floats can be stored in the `reg_safe_area` for this calling convention.

r? `@workingjubilee`
`@rustbot` label +F-c_variadic

try-job: x86_64-apple-1
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this issue May 30, 2025

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
…r=workingjubilee

implement `va_arg` for x86_64 systemv

tracking issue: rust-lang#44930

Turns out LLVM's `va_arg` is also unreliable for this target.

llvm/llvm-project#141361

So, like clang, we implement our own. I used

- the spec at https://gitlab.com/x86-psABIs/x86-64-ABI
- the clang implementation at https://github.com/llvm/llvm-project/blob/9a440f84773c56d3803f330774acb2b4f471d5b4/clang/lib/CodeGen/Targets/X86.cpp#L3041

We can take a bunch of shortcuts because the return type of `va_list` must implement `VaArgSafe`. I also extended some of the tests, because up to 11 floats can be stored in the `reg_safe_area` for this calling convention.

r? `@workingjubilee`
`@rustbot` label +F-c_variadic

try-job: x86_64-apple-1
rust-timer added a commit that referenced this issue May 30, 2025

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
Rollup merge of #141538 - folkertdev:systemv-x86_64-va_arg, r=workingjubilee

implement `va_arg` for x86_64 systemv

tracking issue: #44930

Turns out LLVM's `va_arg` is also unreliable for this target.

llvm/llvm-project#141361

So, like clang, we implement our own. I used

- the spec at https://gitlab.com/x86-psABIs/x86-64-ABI
- the clang implementation at https://github.com/llvm/llvm-project/blob/9a440f84773c56d3803f330774acb2b4f471d5b4/clang/lib/CodeGen/Targets/X86.cpp#L3041

We can take a bunch of shortcuts because the return type of `va_list` must implement `VaArgSafe`. I also extended some of the tests, because up to 11 floats can be stored in the `reg_safe_area` for this calling convention.

r? `@workingjubilee`
`@rustbot` label +F-c_variadic

try-job: x86_64-apple-1
github-actions bot pushed a commit to rust-lang/miri that referenced this issue May 31, 2025

Unverified

The signature in this commit could not be verified. Someone may be trying to trick you.
…jubilee

implement `va_arg` for x86_64 systemv

tracking issue: rust-lang/rust#44930

Turns out LLVM's `va_arg` is also unreliable for this target.

llvm/llvm-project#141361

So, like clang, we implement our own. I used

- the spec at https://gitlab.com/x86-psABIs/x86-64-ABI
- the clang implementation at https://github.com/llvm/llvm-project/blob/9a440f84773c56d3803f330774acb2b4f471d5b4/clang/lib/CodeGen/Targets/X86.cpp#L3041

We can take a bunch of shortcuts because the return type of `va_list` must implement `VaArgSafe`. I also extended some of the tests, because up to 11 floats can be stored in the `reg_safe_area` for this calling convention.

r? `@workingjubilee`
`@rustbot` label +F-c_variadic

try-job: x86_64-apple-1
GuillaumeGomez added a commit to GuillaumeGomez/rust that referenced this issue Jun 1, 2025

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
…ngjubilee

implement `va_arg` for `powerpc`

tracking issue: rust-lang#44930

The llvm `va_arg` implementation is well-known to have serious limitations. Some planned changes to rust's `VaList` make it much more likely that LLVM miscompiles `va_arg`, so this PR adds support for the various powerpc targets. Now at least the targets that `core` has explicit support for will continue to work.

For `powerpc` (the 32-bit variant) this implementation also fixes a bug where only up to 20 variadic arguments were supported.

Locally (with qemu), these targets now pass the tests in https://github.com/rust-lang/rust/blob/master/tests/run-make/c-link-to-rust-va-list-fn/checkrust.rs. That test does not actually run for the powerpc targets in CI though.

The implementation is based on clang:

- handling of big endian architectures https://github.com/llvm/llvm-project/blob/3c8089d1ea53232d5a7cdc33f0cb43ef7d6f723b/clang/lib/CodeGen/ABIInfoImpl.cpp#L191-L193
- 64-bit https://github.com/llvm/llvm-project/blob/3c8089d1ea53232d5a7cdc33f0cb43ef7d6f723b/clang/lib/CodeGen/Targets/PPC.cpp#L969
- 32-bit https://github.com/llvm/llvm-project/blob/3c8089d1ea53232d5a7cdc33f0cb43ef7d6f723b/clang/lib/CodeGen/Targets/PPC.cpp#L430

cc `@daltenty` (target maintainer)
r? `@workingjubilee`
`@rustbot` label: +F-c_variadic
rust-timer added a commit that referenced this issue Jun 1, 2025

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
Rollup merge of #141622 - folkertdev:powerpc-va_arg, r=workingjubilee

implement `va_arg` for `powerpc`

tracking issue: #44930

The llvm `va_arg` implementation is well-known to have serious limitations. Some planned changes to rust's `VaList` make it much more likely that LLVM miscompiles `va_arg`, so this PR adds support for the various powerpc targets. Now at least the targets that `core` has explicit support for will continue to work.

For `powerpc` (the 32-bit variant) this implementation also fixes a bug where only up to 20 variadic arguments were supported.

Locally (with qemu), these targets now pass the tests in https://github.com/rust-lang/rust/blob/master/tests/run-make/c-link-to-rust-va-list-fn/checkrust.rs. That test does not actually run for the powerpc targets in CI though.

The implementation is based on clang:

- handling of big endian architectures https://github.com/llvm/llvm-project/blob/3c8089d1ea53232d5a7cdc33f0cb43ef7d6f723b/clang/lib/CodeGen/ABIInfoImpl.cpp#L191-L193
- 64-bit https://github.com/llvm/llvm-project/blob/3c8089d1ea53232d5a7cdc33f0cb43ef7d6f723b/clang/lib/CodeGen/Targets/PPC.cpp#L969
- 32-bit https://github.com/llvm/llvm-project/blob/3c8089d1ea53232d5a7cdc33f0cb43ef7d6f723b/clang/lib/CodeGen/Targets/PPC.cpp#L430

cc `@daltenty` (target maintainer)
r? `@workingjubilee`
`@rustbot` label: +F-c_variadic
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this issue Jun 2, 2025

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
…y-speed, r=nnethercote

C-variadic functions must be unsafe

tracking issue: rust-lang#44930

A function that uses `...` is always unsafe to call, because it is UB to provide the wrong number of arguments, or arguments of an unexpected type. Hence, an `unsafe extern "C" { /* ... */ }` block should not be able to declare a `safe fn` that uses `...`.

cc `@joshtriplett` `@workingjubilee`

I'm not really sure who'd be a good reviewer for the actual parser code.

`@rustbot` label: +F-c_variadic
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-FFI Area: Foreign function interface (FFI) B-RFC-approved Blocker: Approved by a merged RFC but not yet implemented. B-unstable Blocker: Implemented in the nightly compiler and unstable. C-tracking-issue Category: An issue tracking the progress of sth. like the implementation of an RFC F-c_variadic `#![feature(c_variadic)]` S-tracking-ready-to-stabilize Status: This is ready to stabilize; it may need a stabilization report and a PR T-lang Relevant to the language team
Projects
None yet
Development

No branches or pull requests