Skip to content

c-variadic: error when we can't guarantee that the backend does the right thing#152935

Open
folkertdev wants to merge 1 commit intorust-lang:mainfrom
folkertdev:c-variadic-disallow
Open

c-variadic: error when we can't guarantee that the backend does the right thing#152935
folkertdev wants to merge 1 commit intorust-lang:mainfrom
folkertdev:c-variadic-disallow

Conversation

@folkertdev
Copy link
Contributor

tracking issue: #44930
r? workingjubilee

as discussed in #t-lang > stabilizing `c_variadic`, display an error when we can't guarantee that the codegen backend (only LLVM is supported at the moment) does the right thing.

@folkertdev folkertdev added the F-c_variadic `#![feature(c_variadic)]` label Feb 21, 2026
@rustbot rustbot added A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. 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 Feb 21, 2026
@rustbot
Copy link
Collaborator

rustbot commented Feb 21, 2026

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

@rust-log-analyzer

This comment has been minimized.

@@ -1186,9 +1186,10 @@ pub(super) fn emit_va_arg<'ll, 'tcx>(
// Clang uses the LLVM implementation for these architectures.
Copy link
Member

Choose a reason for hiding this comment

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

Is this "clang does it so it's probably fine" or "people familiar with the target have confirmed it's fine"?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It is "clang does this so we're matching the de-facto standard C compiler that rust code would interact with". We could pull that implementation into rustc as well if you prefer. I've traced how this implementation is picked here https://github.com/rust-lang/rust/pull/150831/changes#r2673777819.

The mips case right above will be handled by #152576.

Copy link
Member

Choose a reason for hiding this comment

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

We could pull that implementation into rustc as well if you prefer.

Happy to leave that choice to you.^^ It is very strange to me that clang mostly does not use the LLVM impl here, given that they are part of the same project, so this is all a bit confusing.

Copy link
Contributor Author

@folkertdev folkertdev Feb 21, 2026

Choose a reason for hiding this comment

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

Pinging some target maintainers:

@jonathanpallant @Patryk27 @glaubitz @ricky26

apparently msp430-none-elf does not have a target maintainer. For m68k I can't get programs to link (some incorrect relocation issue, might be an LLVM problem).

Can you confirm that this test (with your target) works?

./x test tests/run-make/c-link-to-rust-va-list-fn --target mipsel-unknown-linux-gnu

this likely needs some sort of config in bootstrap.toml, I have some variations on

[target.mips64-unknown-linux-gnuabi64]
runner = "qemu-mips64 -L /usr/mips64-linux-gnuabi64"

[target.mipsel-unknown-linux-gnu]
runner = "qemu-mipsel -cpu 24Kf -L /usr/mipsel-linux-gnu"

[target.mips-unknown-linux-gnu]
runner = "qemu-mips -L /usr/mips-linux-gnu"

If we don't get any response here, it's probably safer to exclude these targets from stabilization for now, because looking at it again, I think this implementation is also a fallback in clang/llvm so there is a good chance that it's actually untested.

Copy link
Contributor

Choose a reason for hiding this comment

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

Will check tomorrow!

Copy link
Contributor

Choose a reason for hiding this comment

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

For m68k I can't get programs to link (some incorrect relocation issue, might be an LLVM problem).

Yes, it's a known bug, see: llvm/llvm-project#181481

Copy link
Contributor

@Patryk27 Patryk27 Feb 22, 2026

Choose a reason for hiding this comment

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

It seems that that test is not applicable to AVR (it's a non-std target), but in general the AVR backend for LLVM implements variadics:

https://github.com/llvm/llvm-project/blob/8701cfc0008c4756f04f9839fd4afbb3f112bdad/llvm/lib/Target/AVR/AVRCallingConv.td#L28

... and we strive to be compatible with avr-gcc, so I'd expect things to just-work.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We're hesitant with accepting targets because the LLVM va_arg implementation is known to miscompile programs on many platforms. In large part that is because a correct implementation would require layout information that LLVM just won't have. Hence, for many targets the actual logic is implemented in clang and we replicate that logic in rustc.

I just tried this locally, compilation still fails but it gets far enough along to emit these errors:

error[E0277]: the trait bound `i16: VaArgSafe` is not satisfied
   --> checkrust.rs:123:27
    |
123 |     continue_if!(ap.arg::<c_int>() == 4);
    |                     ---   ^^^^^ the nightly-only, unstable trait `VaArgSafe` is not implemented for `i16`
    |                     |
    |                     required by a bound introduced by this call
    |
    = help: the following other types implement trait `VaArgSafe`:
              f64
              i32
              i64
              isize
              u32
              u64
              usize

I suppose this really should work, and we're currently implicitly assuming that c_int is i32.

What types implement VaArgSafe is based on section 7.16.1 of the C23 spec

If type is not compatible with the type of the actual next argument (as promoted according to the default argument promotions), the behavior is undefined

Our reading is that, on most targets, it is UB to read an i16 because it is incompatible with what was passed (even if an i16 was passed, it has been promoted to c_int (we implicitly assume this is i32) through the default argument promotions).

I'll make a separate PR to fix that and hopefully we can figure out some approach to testing avr-none there too.

// > 2×XLEN-bit alignment and size at most 2×XLEN bits like `long long`,
// > `unsigned long long` and `double` to have 4-byte alignment. This
// > behavior may be changed when RV32E/ILP32E is ratified.
RiscV32 if *abi == Abi::Ilp32e => false,
Copy link
Member

Choose a reason for hiding this comment

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

It's concerning to use the target.abi field here which is usually just informational -- it controls cfg(target_abi), it doesn't control how we configure LLVM.

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

Labels

A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. F-c_variadic `#![feature(c_variadic)]` 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.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants