Problem with @cVarArg()

While programming, sometimes you get into a state of mind where you’re somehow incapable of seeing an obvious mistake that you’ve made. I think I’m in that state now. For the life of me I can’t figure out why the following code is doing what it’s doing:

const std = @import("std");

pub fn main() !void {
    const ns = struct {
        fn function(arg0: i64, ...) callconv(.C) f64 {
            var va_list = @cVaStart();
            defer @cVaEnd(&va_list);
            const arg1 = @cVaArg(&va_list, i32);
            const arg2 = @cVaArg(&va_list, f32);
            const arg3 = @cVaArg(&va_list, f32);
            std.debug.print("\n{any} {any} {any} {any}\n", .{ arg0, arg1, arg2, arg3 });
            return @as(f64, @floatFromInt(arg0)) + @as(f64, @floatFromInt(arg1)) + arg2 + arg3;
        }
    };
    _ = ns.function(1000, @as(i32, 7), @as(f32, -5), @as(f32, -2));
}
1000 7 0e0 0e0

I’ve tested this code in ppc64le, riscv64, arm32, and ia32. Not getting the expected result in any of them. I’m pretty sure there’s something wrong with the code but I not seeing it. I’m hoping a fresh pair of eyes can help me here.

C Type Primitives have guaranteed C ABI compatibility.

const std = @import("std");

pub fn main() !void {
    const ns = struct {
        fn function(arg0: c_longlong, ...) callconv(.C) f64 {
            var va_list = @cVaStart();
            defer @cVaEnd(&va_list);
            const arg1 = @cVaArg(&va_list, c_int);
            const arg2 = @cVaArg(&va_list, f64);
            const arg3 = @cVaArg(&va_list, f64);
            std.debug.print("\n{any} {any} {any} {any}\n", .{ arg0, arg1, arg2, arg3 });
            return @as(f64, @floatFromInt(arg0)) + @as(f64, @floatFromInt(arg1)) + arg2 + arg3;
        }
    };
    _ = ns.function(1000, @as(c_int, 7), @as(f64, -5), @as(f64, -2));
}
1000 7 -5e0 -2e0

The code in question is for a test case. I can’t switch the type since that’s the very thing I’m testing. The call to ns.function() is supposed to establish the control for the test. I’m comparing the result to what I get from my dynamic dispatch mechanism. Funny thing is that my stuff seems to produce the right results but I can’t get the control to work.

Before a call to a variadic function, integer arguments undergo integer promotion, and each f32 argument is converted to f64.
https://en.cppreference.com/w/c/language/conversion#Default_argument_promotions
I don’t think it’s connected to using C primitives. After all, C also has fixed width integer types.

Edit: by “not connected to C primitives” I meant the first parameter of a variadic function. But @cVaArg type should be a C non-fixed width integer or f64.