Skip to content

Conversation

@folkertdev
Copy link
Contributor

@folkertdev folkertdev commented Sep 11, 2025

tracking issue: #44930

Continuing the work of #146342, allow inherent and trait methods to be c-variadic. However, a trait that contains a c-variadic method is no longer dyn-compatible.

There is, presumably, some way to make c-variadic methods dyn-compatible. However currently, we don't have confidence that it'll work reliably: when methods from a dyn object are cast to a function pointer, a ReifyShim is created. If that shim is c-variadic, it would need to forward the C variable argument list.

That does appear to work, because the va_list is not represented in MIR at all in this case, so the registers from the call site are untouched by the shim and can be read by the actual implementation. That just does not seem like a solid implementation.

Also, intuitively, why would c-variadic function, primarily needed for FFI, need to be used with dyn objects at all? We can revisit this limitation if a need arises.

r? @workingjubilee

but a C-variadic method makes a trait dyn-incompatible. That is because
methods from dyn traits, when cast to a function pointer, create a shim.
That shim can't really forward the c-variadic arguments.
@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Sep 11, 2025
@rustbot
Copy link
Collaborator

rustbot commented Sep 11, 2025

workingjubilee is currently at their maximum review capacity.
They may take a while to respond.

@folkertdev folkertdev added the F-c_variadic `#![feature(c_variadic)]` label Sep 11, 2025
Copy link
Member

@workingjubilee workingjubilee left a comment

Choose a reason for hiding this comment

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

could we have some tests in relevant files for functions which are neither extern "C" nor Rust functions, to demonstrate how the principles here extend (or don't)?

In particular I'd like them to cover an ABI which in principle doesn't support varargs and an ABI which in principle does.

View changes since this review

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Sep 11, 2025
@folkertdev folkertdev force-pushed the c-variadic-inherent-methods branch from 1b99d56 to 321a76b Compare September 11, 2025 21:56
@folkertdev
Copy link
Contributor Author

folkertdev commented Sep 11, 2025

Sure, I added tests for these calling conventions in various contexts:

unsafe extern "cdecl" fn cdecl_free(_: ...) {}
unsafe extern "cdecl-unwind" fn cdecl_unwind_free(_: ...) {}
unsafe extern "stdcall" fn stdcall_free(_: ...) {}
unsafe extern "stdcall-unwind" fn stdcall_unwind_free(_: ...) {}
unsafe extern "thiscall" fn thiscall_free(_: ...) {}
unsafe extern "thiscall-unwind" fn thiscall_unwind_free(_: ...) {}

@rustbot ready

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Sep 11, 2025
@workingjubilee
Copy link
Member

Shims are also generated when #[track_caller] is involved, right?

@folkertdev
Copy link
Contributor Author

Correct, but #[track_caller] is only allowed on normal extern "Rust" functions, and ... is disallowed for that calling convention, so these features never interact.

@workingjubilee
Copy link
Member

Oh right.

Good.

@workingjubilee
Copy link
Member

Will try to get to this on Monday. You will probably see many more questions in the shape of "what about ${thing_invalid_for_other_reasons}?" so my apologies if that is the sort of thing that you find frustrating. When I focus on "problem generation" like that it rarely goes well if I then try to cull possible issues too early.

@workingjubilee
Copy link
Member

So we parse ... as a type, but reject its nesting in anything, including even something like type VarArgs = ...;, preventing the type aliasing issues that we've run into with other AST-based checks. This means that while a type alias may be defined for a function signature with ..., it must use an entire function signature.

And we didn't encounter any shim-generation in codegen for any sort of inherent methods or trait methods that aren't dynamic?

I think I'm feeling positive about this but want to sleep on it and review the codegen shims a bit maybe.

@folkertdev
Copy link
Contributor Author

No, I think only this is relevant

pub enum ReifyReason {
    /// The `ReifyShim` was created to produce a function pointer. This happens when:
    /// * A vtable entry is directly converted to a function call (e.g. creating a fn ptr from a
    ///   method on a `dyn` object).
    /// * A function with `#[track_caller]` is converted to a function pointer
    /// * If KCFI is enabled, creating a function pointer from a method on a dyn-compatible trait.
    /// This includes the case of converting `::call`-like methods on closure-likes to function
    /// pointers.
    FnPtr,
    /// This `ReifyShim` was created to populate a vtable. Currently, this happens when a
    /// `#[track_caller]` mismatch occurs between the implementation of a method and the method.
    /// This includes the case of `::call`-like methods in closure-likes' vtables.
    Vtable,
}

#[track_caller] is only allowed on extern "Rust", and a c-variadic method makes the trait no longer dyn-compatible. I don't see any other variants of InstanceKind that would apply here.

@workingjubilee
Copy link
Member

    /// * If KCFI is enabled, creating a function pointer from a method on a dyn-compatible trait.
    /// This includes the case of converting `::call`-like methods on closure-likes to function
    /// pointers.

Thanks for pulling that out, that was one of my big questions. Okay, so that happens when we decay a "closure-like", so a

pub fn is_closure_like(self, def_id: DefId) -> bool {
which is to say "any kind of closure, even if it's some kind of coroutine or whatever".

I am somewhat worried about that, because I think this might actually be something that "normal" closure decay is at risk of incurring. But closures shouldn't have varargs, of course, since they're extern "Rust", and I suspect if I discussed how varargs fit into KCFI with @maurer and @rcvalle at length, I am guessing they would say something like, "The entire point of varargs is to bypass typechecking, so no, we don't have to worry about that because CFI basically doesn't apply here and it's kind of incompatible without using one of the escape hatches in CFI".

Or maybe I'm wrong? I hope I'm wrong in a direction that still aligns with not caring about these possible interactions, at least.

Either way, since it's an interaction between two unstable things, I am willing to defer having a decisive answer to this until later.

This should enable more code only, so:

@bors r+ rollup

@bors
Copy link
Collaborator

bors commented Sep 17, 2025

📌 Commit 321a76b has been approved by workingjubilee

It is now in the queue for this repository.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Sep 17, 2025
@workingjubilee
Copy link
Member

Also I found out after a brief chat that apparently KCFI does get used in the case of varargs! It still shouldn't matter for the other reasons, but, you know,

fmease added a commit to fmease/rust that referenced this pull request Sep 18, 2025
…hods, r=workingjubilee

c-variadic: allow c-variadic inherent and trait methods

tracking issue: rust-lang#44930

Continuing the work of rust-lang#146342, allow inherent and trait methods to be c-variadic. However, a trait that contains a c-variadic method is no longer dyn-compatible.

There is, presumably, some way to make c-variadic methods dyn-compatible. However currently, we don't have confidence that it'll work reliably: when methods from a `dyn` object are cast to a function pointer, a `ReifyShim` is created. If that shim is c-variadic, it would need to forward the C variable argument list.

That does appear to work, because the `va_list` is not represented in MIR at all in this case, so the registers from the call site are untouched by the shim and can be read by the actual implementation. That just does not seem like a solid implementation.

Also, intuitively, why would c-variadic function, primarily needed for FFI, need to be used with `dyn` objects at all? We can revisit this limitation if a need arises.

r? `@workingjubilee`
bors added a commit that referenced this pull request Sep 18, 2025
…kingjubilee

c-variadic: allow c-variadic inherent and trait methods

tracking issue: #44930

Continuing the work of #146342, allow inherent and trait methods to be c-variadic. However, a trait that contains a c-variadic method is no longer dyn-compatible.

There is, presumably, some way to make c-variadic methods dyn-compatible. However currently, we don't have confidence that it'll work reliably: when methods from a `dyn` object are cast to a function pointer, a `ReifyShim` is created. If that shim is c-variadic, it would need to forward the C variable argument list.

That does appear to work, because the `va_list` is not represented in MIR at all in this case, so the registers from the call site are untouched by the shim and can be read by the actual implementation. That just does not seem like a solid implementation.

Also, intuitively, why would c-variadic function, primarily needed for FFI, need to be used with `dyn` objects at all? We can revisit this limitation if a need arises.

r? `@workingjubilee`
@bors
Copy link
Collaborator

bors commented Sep 18, 2025

⌛ Testing commit 321a76b with merge 20aa0ef...

@bors
Copy link
Collaborator

bors commented Sep 18, 2025

💔 Test failed - checks-actions

@bors bors added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. labels Sep 18, 2025
@folkertdev
Copy link
Contributor Author

It looks like the i686-msvc-1 action timed out (after almost 4 hours)? Its logs just sort of stop mid-test, but there is no backtrace or any indication that a test actually failed.

@fmease
Copy link
Member

fmease commented Sep 18, 2025

@bors retry

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Sep 18, 2025
bors added a commit that referenced this pull request Sep 18, 2025
Rollup of 6 pull requests

Successful merges:

 - #146434 (c-variadic: allow c-variadic inherent and trait methods)
 - #146487 (Improve `core::num` coverage)
 - #146597 (Add span for struct tail recursion limit error)
 - #146622 (Add regression test for issue #91831)
 - #146717 (Clean up universe evaluation during type test evaluation)
 - #146723 (Include patch in release notes)

r? `@ghost`
`@rustbot` modify labels: rollup
@bors bors merged commit 185926c into rust-lang:master Sep 18, 2025
10 of 11 checks passed
@rustbot rustbot added this to the 1.92.0 milestone Sep 18, 2025
rust-timer added a commit that referenced this pull request Sep 18, 2025
Rollup merge of #146434 - folkertdev:c-variadic-inherent-methods, r=workingjubilee

c-variadic: allow c-variadic inherent and trait methods

tracking issue: #44930

Continuing the work of #146342, allow inherent and trait methods to be c-variadic. However, a trait that contains a c-variadic method is no longer dyn-compatible.

There is, presumably, some way to make c-variadic methods dyn-compatible. However currently, we don't have confidence that it'll work reliably: when methods from a `dyn` object are cast to a function pointer, a `ReifyShim` is created. If that shim is c-variadic, it would need to forward the C variable argument list.

That does appear to work, because the `va_list` is not represented in MIR at all in this case, so the registers from the call site are untouched by the shim and can be read by the actual implementation. That just does not seem like a solid implementation.

Also, intuitively, why would c-variadic function, primarily needed for FFI, need to be used with `dyn` objects at all? We can revisit this limitation if a need arises.

r? `@workingjubilee`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

F-c_variadic `#![feature(c_variadic)]` S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants