Skip to content
Open
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
87 changes: 85 additions & 2 deletions src/items/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ r[items.fn.params.self-restriction]
Functions with a self parameter may only appear as an [associated function] in a [trait] or [implementation].

r[items.fn.params.varargs]
A parameter with the `...` token indicates a [variadic function], and may only be used as the last parameter of an [external block] function. The variadic parameter may have an optional identifier, such as `args: ...`.
A parameter with the `...` token indicates a [c-variadic function]. The variadic parameter may have an optional identifier, such as `args: ...`.

r[items.fn.body]
## Function body
Expand Down Expand Up @@ -332,6 +332,88 @@ Note that this behavior is a consequence of the desugaring to a function that re

Unsafe is used on an async function in precisely the same way that it is used on other functions: it indicates that the function imposes some additional obligations on its caller to ensure soundness. As in any other unsafe function, these conditions may extend beyond the initial call itself -- in the snippet above, for example, the `unsafe_example` function took a pointer `x` as argument, and then (when awaited) dereferenced that pointer. This implies that `x` would have to be valid until the future is finished executing, and it is the caller's responsibility to ensure that.

r[items.fn.c-variadic]
## C-variadic functions

r[items.fn.c-variadic.intro]
A *c-variadic* function accepts a variable argument list `pat: ...` as its final parameter.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

for c-variadic definitions only pat: ... is accepted semantically.

plain ... is currently parsed, the varargs_without_pattern lint is meant to eventually disallow it. This syntax can only be used as an input to macros, when a bare ... makes it past macro expansion, that will emit an error.

I didn't mention this here, but maybe we should: we follow C23 in that pat: ... may be the only argument. Earlier versions of C required at least one standard argument before the ....

Copy link
Contributor

Choose a reason for hiding this comment

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

Is anything holding back changing varargs_without_pattern to report_in_deps: true?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not as far as I know. I can make that change an nominate for T-lang.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Based on rust-lang/rust#143619 (comment) there was only one actual impacted crate https://crates.io/crates/binrw. The issue was fixed in jam1garner/binrw#342, however there has not been a release since.

So, if we made this warn in dependencies there would be nothing that users of that crate could do. I'll ping for a release with the fix.


```rust
unsafe extern "C" fn example(ap: ...) -> f64 {
unsafe { ap.arg::<f64>() }
}
```

This parameter stands in for an arbitrary number of arguments that may be passed by the caller.

> [!WARNING]
> Passing an unexpected number of arguments or arguments of unexpected type to a variadic function may lead to [undefined behavior][undefined].
Comment on lines +349 to +350
Copy link
Contributor Author

Choose a reason for hiding this comment

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

copied from

https://doc.rust-lang.org/reference/items/external-blocks.html?highlight=variadic#r-items.extern.variadic

basically, the responsibility for passing valid arguments is on the caller.


r[items.fn.c-variadic.variadic-parameter-type]
The type of `pat` in the function body is [`VaList`].

r[items.fn.c-variadic.desugar-brief]
A c-variadic function definition is roughly equivalent to a function operating on a [`VaList`].
Copy link
Contributor

Choose a reason for hiding this comment

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

What is "roughly" about it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In how arguments are actually passed, these signatures are (very) different

fn foo(ap: ...) { /* ... */ }
fn bar(ap: VaList) { /* ... */ }

Those two functions could have exactly the same body though.




```rust
// Source
unsafe extern "C" fn example(mut ap: ...) -> i32 {
unsafe { ap.arg::<i32>() }
}
```

is roughly equivalent to:

```rust
# use std::ffi::VaList;
# use std::mem::MaybeUninit;
# fn va_start() {}
// Desugared
unsafe extern "C" fn example() -> i32 {
let mut storage = MaybeUninit::<VaList<'_>>::uninit();
va_start(storage.as_mut_ptr()); // Initializes the VaList.
let mut ap: &mut VaList<'_> = ap.assume_init_mut();

unsafe { ap.arg::<i32>() }

va_end(ap)
}
Comment on lines +374 to +382
Copy link
Contributor Author

Choose a reason for hiding this comment

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

va_start and va_end are C functions/concepts. We could instead handwave and say

let mut ap: VaList<'_> = /* ... */; // Initializes the VaList.

the va_end is called by the VaList Drop implementation.

```

r[items.fn.c-variadic.lifetime]
The lifetime of a `VaList` is that of the function that created it. Hence, the `VaList` value can never outlive the function that created it.

r[items.fn.c-variadic.ffi-compatibility]
The rust [`VaList`] is ABI-compatible with the C `va_list` type.

r[items.fn.c-variadic.abi]
Only `extern "C"` and `extern "C-unwind"` functions can accept a variable argument list.

r[items.fn.c-variadic.safety]
Only `unsafe` functions can accept a variable argument list.

r[items.fn.c-variadic.async]
A c-variadic functions cannot be `async`

r[items.fn.c-variadic.const]
A c-variadic functions cannot be `const`

r[items.fn.c-variadic.platform-support]
Some ABIs do not support c-variadic function definitions. The compiler errors in this case.

```
error: the `bpfel` target does not support c-variadic functions
--> $DIR/not-supported.rs:23:31
|
LL | unsafe extern "C" fn variadic(_: ...) {}
| ^^^^^^
```

r[items.fn.c-variadic.dyn-compat]
When a trait method is c-variadic, the trait is no longer dyn-compatible.

r[items.fn.attributes]
## Attributes on functions

Expand Down Expand Up @@ -424,5 +506,6 @@ fn foo_oof(#[some_inert_attribute] arg: u8) {
[associated function]: associated-items.md#associated-functions-and-methods
[implementation]: implementations.md
[value namespace]: ../names/namespaces.md
[variadic function]: external-blocks.md#variadic-functions
[c-variadic function]: external-blocks.md#variadic-functions
[`extern` block]: external-blocks.md
[`VaList`]: std::ffi::VaList
Loading