From c80045cbe77c9cd044ab07b189003726169e0ace Mon Sep 17 00:00:00 2001 From: Techatrix Date: Mon, 3 Aug 2026 18:17:09 +0200 Subject: [PATCH 1/5] c_abi: include array sentinel in length calculation --- src/codegen/loongarch/abi.zig | 2 +- src/codegen/s390x/abi.zig | 2 +- src/codegen/x86_64/abi.zig | 2 +- test/c_abi/cfuncs.c | 100 ++++++++++++++++++ test/c_abi/main.zig | 187 ++++++++++++++++++++++++++++++++++ 5 files changed, 290 insertions(+), 3 deletions(-) diff --git a/src/codegen/loongarch/abi.zig b/src/codegen/loongarch/abi.zig index 09e42a7cb96731dbf0ee44b37ad74f0548a08c93..ba9b79d39d5be3d10e20176bc5574c9205df5ad7 100644 --- a/src/codegen/loongarch/abi.zig +++ b/src/codegen/loongarch/abi.zig @@ -95,7 +95,7 @@ const Classifier = struct { var class: Class = .ignored; const elem_ty = ty.childType(c.zcu); const elem_class = c.classifyType(elem_ty); - for (0..std.math.lossyCast(usize, ty.arrayLen(c.zcu))) |_| { + for (0..std.math.lossyCast(usize, ty.arrayLenIncludingSentinel(c.zcu))) |_| { class = class.combineMember(elem_class, elem_ty); if (class == .address) break; } diff --git a/src/codegen/s390x/abi.zig b/src/codegen/s390x/abi.zig index 7b35245fdad37be5e062bbcc873c116a78a99152..f8c7864a4a313f54085b357a93f6d943ab72524e 100644 --- a/src/codegen/s390x/abi.zig +++ b/src/codegen/s390x/abi.zig @@ -45,7 +45,7 @@ pub fn classifyType(ty: Type, context: Context, zcu: *Zcu) Class { 128 => return .pointer, }, .pointer, .optional => return .simple, - .array => switch (ty.arrayLen(zcu)) { + .array => switch (ty.arrayLenIncludingSentinel(zcu)) { 0 => return .none, 1 => switch (context) { .ret => {}, diff --git a/src/codegen/x86_64/abi.zig b/src/codegen/x86_64/abi.zig index 3ebad4dee2a9235f641e12be27684d247bfd2de1..471c09974d6bd29a29fb0fd37bd1b7e7612088b7 100644 --- a/src/codegen/x86_64/abi.zig +++ b/src/codegen/x86_64/abi.zig @@ -443,7 +443,7 @@ fn classifySystemVArray( const field_classes = std.mem.sliceTo(&classifySystemV(array_ty.childType(zcu), zcu, target, .other), .none); var byte_offset = starting_byte_offset; const elem_size = array_ty.childType(zcu).abiSize(zcu); - for (0..@intCast(array_ty.arrayLen(zcu))) |_| { + for (0..@intCast(array_ty.arrayLenIncludingSentinel(zcu))) |_| { for (result[@intCast(byte_offset / 8)..][0..field_classes.len], field_classes) |*result_class, field_class| result_class.* = result_class.combineSystemV(field_class); byte_offset += elem_size; diff --git a/test/c_abi/cfuncs.c b/test/c_abi/cfuncs.c index b74ea9ffef180944eb00c919dd5a6513fa6d32ad..a259e23413a131b4e73b5f34329e1b2e6093f44d 100644 --- a/test/c_abi/cfuncs.c +++ b/test/c_abi/cfuncs.c @@ -15314,6 +15314,106 @@ void c_test_struct_array_5_f32(void) { zig_struct_array_5_f32((struct Struct_array_5_f32){ .a = { 6, 7, 8, 9, 10 } }, 11); } +struct Struct_array_1_f32 zig_ret_struct_array_0_sentinel_f32(void); +void zig_struct_array_0_sentinel_f32(struct Struct_array_1_f32, size_t); + +struct Struct_array_1_f32 c_ret_struct_array_0_sentinel_f32(void) { + return (struct Struct_array_1_f32){ .a = { 0x1e1 } }; +} +void c_struct_array_0_sentinel_f32(struct Struct_array_1_f32 s, size_t i) { + assert_or_panic(s.a[0] == 0x1e1); + assert_or_panic(i == 2); +} +void c_test_struct_array_0_sentinel_f32(void) { + struct Struct_array_1_f32 s = zig_ret_struct_array_0_sentinel_f32(); + assert_or_panic(s.a[0] == 0x1e1); + zig_struct_array_0_sentinel_f32((struct Struct_array_1_f32){ .a = { 0x1e1 } }, 1); +} + +struct Struct_array_2_f32 zig_ret_struct_array_1_sentinel_f32(void); +void zig_struct_array_1_sentinel_f32(struct Struct_array_2_f32, size_t); + +struct Struct_array_2_f32 c_ret_struct_array_1_sentinel_f32(void) { + return (struct Struct_array_2_f32){ .a = { 4, 0x1e1 } }; +} +void c_struct_array_1_sentinel_f32(struct Struct_array_2_f32 s, size_t i) { + assert_or_panic(s.a[0] == 5); + assert_or_panic(s.a[1] == 0x1e1); + assert_or_panic(i == 6); +} +void c_test_struct_array_1_sentinel_f32(void) { + struct Struct_array_2_f32 s = zig_ret_struct_array_1_sentinel_f32(); + assert_or_panic(s.a[0] == 1); + assert_or_panic(s.a[1] == 0x1e1); + zig_struct_array_1_sentinel_f32((struct Struct_array_2_f32){ .a = { 2, 0x1e1 } }, 3); +} + +struct Struct_array_3_f32 zig_ret_struct_array_2_sentinel_f32(void); +void zig_struct_array_2_sentinel_f32(struct Struct_array_3_f32, size_t); + +struct Struct_array_3_f32 c_ret_struct_array_2_sentinel_f32(void) { + return (struct Struct_array_3_f32){ .a = { 6, 7, 0x1e1 } }; +} +void c_struct_array_2_sentinel_f32(struct Struct_array_3_f32 s, size_t i) { + assert_or_panic(s.a[0] == 8); + assert_or_panic(s.a[1] == 9); + assert_or_panic(s.a[2] == 0x1e1); + assert_or_panic(i == 10); +} +void c_test_struct_array_2_sentinel_f32(void) { + struct Struct_array_3_f32 s = zig_ret_struct_array_2_sentinel_f32(); + assert_or_panic(s.a[0] == 1); + assert_or_panic(s.a[1] == 2); + assert_or_panic(s.a[2] == 0x1e1); + zig_struct_array_2_sentinel_f32((struct Struct_array_3_f32){ .a = { 3, 4, 0x1e1 } }, 5); +} + +struct Struct_array_4_f32 zig_ret_struct_array_3_sentinel_f32(void); +void zig_struct_array_3_sentinel_f32(struct Struct_array_4_f32, size_t); + +struct Struct_array_4_f32 c_ret_struct_array_3_sentinel_f32(void) { + return (struct Struct_array_4_f32){ .a = { 8, 9, 10, 0x1e1 } }; +} +void c_struct_array_3_sentinel_f32(struct Struct_array_4_f32 s, size_t i) { + assert_or_panic(s.a[0] == 11); + assert_or_panic(s.a[1] == 12); + assert_or_panic(s.a[2] == 13); + assert_or_panic(s.a[3] == 0x1e1); + assert_or_panic(i == 14); +} +void c_test_struct_array_3_sentinel_f32(void) { + struct Struct_array_4_f32 s = zig_ret_struct_array_3_sentinel_f32(); + assert_or_panic(s.a[0] == 1); + assert_or_panic(s.a[1] == 2); + assert_or_panic(s.a[2] == 3); + assert_or_panic(s.a[3] == 0x1e1); + zig_struct_array_3_sentinel_f32((struct Struct_array_4_f32){ .a = { 4, 5, 6, 0x1e1 } }, 7); +} + +struct Struct_array_5_f32 zig_ret_struct_array_4_sentinel_f32(void); +void zig_struct_array_4_sentinel_f32(struct Struct_array_5_f32, size_t); + +struct Struct_array_5_f32 c_ret_struct_array_4_sentinel_f32(void) { + return (struct Struct_array_5_f32){ .a = { 10, 11, 12, 13, 0x1e1 } }; +} +void c_struct_array_4_sentinel_f32(struct Struct_array_5_f32 s, size_t i) { + assert_or_panic(s.a[0] == 14); + assert_or_panic(s.a[1] == 15); + assert_or_panic(s.a[2] == 16); + assert_or_panic(s.a[3] == 17); + assert_or_panic(s.a[4] == 0x1e1); + assert_or_panic(i == 18); +} +void c_test_struct_array_4_sentinel_f32(void) { + struct Struct_array_5_f32 s = zig_ret_struct_array_4_sentinel_f32(); + assert_or_panic(s.a[0] == 1); + assert_or_panic(s.a[1] == 2); + assert_or_panic(s.a[2] == 3); + assert_or_panic(s.a[3] == 4); + assert_or_panic(s.a[4] == 0x1e1); + zig_struct_array_4_sentinel_f32((struct Struct_array_5_f32){ .a = { 5, 6, 7, 8, 0x1e1 } }, 9); +} + struct Struct_f32a8 { alignas(8) float a; }; diff --git a/test/c_abi/main.zig b/test/c_abi/main.zig index 1fd267402d74dbb38e524c8e232240ff97ef5217..15cb889467a794aaadd7ce08eb33523b4ec65885 100644 --- a/test/c_abi/main.zig +++ b/test/c_abi/main.zig @@ -16278,6 +16278,193 @@ test "struct [5]f32" { c_test_struct_array_5_f32(); } +const Struct_array_0_sentinel_f32 = extern struct { + a: [0:0x1e1]f32, +}; + +export fn zig_ret_struct_array_0_sentinel_f32() Struct_array_0_sentinel_f32 { + return .{ .a = .{} }; +} +export fn zig_struct_array_0_sentinel_f32(s: Struct_array_0_sentinel_f32, i: usize) void { + var sentinel_index: usize = 0; + _ = &sentinel_index; + expect(s.a[sentinel_index] == 0x1e1) catch @panic("test failure"); + expect(i == 1) catch @panic("test failure"); +} + +extern fn c_ret_struct_array_0_sentinel_f32() Struct_array_0_sentinel_f32; +extern fn c_struct_array_0_sentinel_f32(Struct_array_0_sentinel_f32, usize) void; +extern fn c_test_struct_array_0_sentinel_f32() void; + +test "struct [0:sentinel]f32" { + if (builtin.cpu.arch.isAARCH64()) return error.SkipZigTest; + if (builtin.cpu.arch.isArm()) return error.SkipZigTest; + if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest; + if (builtin.cpu.arch.isPowerPC()) return error.SkipZigTest; + if (builtin.cpu.arch.isRISCV()) return error.SkipZigTest; + if (builtin.cpu.arch == .s390x) return error.SkipZigTest; + if (builtin.cpu.arch.isWasm()) return error.SkipZigTest; + + var sentinel_index: usize = 0; + _ = &sentinel_index; + const s = c_ret_struct_array_0_sentinel_f32(); + try expect(s.a[sentinel_index] == 0x1e1); + c_struct_array_0_sentinel_f32(.{ .a = .{} }, 2); + c_test_struct_array_0_sentinel_f32(); +} + +const Struct_array_1_sentinel_f32 = extern struct { + a: [1:0x1e1]f32, +}; + +export fn zig_ret_struct_array_1_sentinel_f32() Struct_array_1_sentinel_f32 { + return .{ .a = .{1} }; +} +export fn zig_struct_array_1_sentinel_f32(s: Struct_array_1_sentinel_f32, i: usize) void { + var sentinel_index: usize = 1; + _ = &sentinel_index; + expect(s.a[0] == 2) catch @panic("test failure"); + expect(s.a[sentinel_index] == 0x1e1) catch @panic("test failure"); + expect(i == 3) catch @panic("test failure"); +} + +extern fn c_ret_struct_array_1_sentinel_f32() Struct_array_1_sentinel_f32; +extern fn c_struct_array_1_sentinel_f32(Struct_array_1_sentinel_f32, usize) void; +extern fn c_test_struct_array_1_sentinel_f32() void; + +test "struct [1:sentinel]f32" { + if (builtin.cpu.arch.isAARCH64()) return error.SkipZigTest; + if (builtin.cpu.arch.isArm()) return error.SkipZigTest; + if (builtin.cpu.arch == .loongarch64 and builtin.abi.float() == .hard) return error.SkipZigTest; + if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest; + if (builtin.cpu.arch.isPowerPC()) return error.SkipZigTest; + if (builtin.cpu.arch.isRISCV()) return error.SkipZigTest; + + var sentinel_index: usize = 1; + _ = &sentinel_index; + const s = c_ret_struct_array_1_sentinel_f32(); + try expect(s.a[0] == 4); + try expect(s.a[sentinel_index] == 0x1e1); + c_struct_array_1_sentinel_f32(.{ .a = .{5} }, 6); + c_test_struct_array_1_sentinel_f32(); +} + +const Struct_array_2_sentinel_f32 = extern struct { + a: [2:0x1e1]f32, +}; + +export fn zig_ret_struct_array_2_sentinel_f32() Struct_array_2_sentinel_f32 { + return .{ .a = .{ 1, 2 } }; +} +export fn zig_struct_array_2_sentinel_f32(s: Struct_array_2_sentinel_f32, i: usize) void { + var sentinel_index: usize = 2; + _ = &sentinel_index; + expect(s.a[0] == 3) catch @panic("test failure"); + expect(s.a[1] == 4) catch @panic("test failure"); + expect(s.a[sentinel_index] == 0x1e1) catch @panic("test failure"); + expect(i == 5) catch @panic("test failure"); +} + +extern fn c_ret_struct_array_2_sentinel_f32() Struct_array_2_sentinel_f32; +extern fn c_struct_array_2_sentinel_f32(Struct_array_2_sentinel_f32, usize) void; +extern fn c_test_struct_array_2_sentinel_f32() void; + +test "struct [2:sentinel]f32" { + if (builtin.cpu.arch.isAARCH64()) return error.SkipZigTest; + if (builtin.cpu.arch.isArm()) return error.SkipZigTest; + if (builtin.cpu.arch == .hexagon) return error.SkipZigTest; + if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest; + if (builtin.cpu.arch.isPowerPC()) return error.SkipZigTest; + + var sentinel_index: usize = 2; + _ = &sentinel_index; + const s = c_ret_struct_array_2_sentinel_f32(); + try expect(s.a[0] == 6); + try expect(s.a[1] == 7); + try expect(s.a[sentinel_index] == 0x1e1); + c_struct_array_2_sentinel_f32(.{ .a = .{ 8, 9 } }, 10); + c_test_struct_array_2_sentinel_f32(); +} + +const Struct_array_3_sentinel_f32 = extern struct { + a: [3:0x1e1]f32, +}; + +export fn zig_ret_struct_array_3_sentinel_f32() Struct_array_3_sentinel_f32 { + return .{ .a = .{ 1, 2, 3 } }; +} +export fn zig_struct_array_3_sentinel_f32(s: Struct_array_3_sentinel_f32, i: usize) void { + var sentinel_index: usize = 3; + _ = &sentinel_index; + expect(s.a[0] == 4) catch @panic("test failure"); + expect(s.a[1] == 5) catch @panic("test failure"); + expect(s.a[2] == 6) catch @panic("test failure"); + expect(s.a[sentinel_index] == 0x1e1) catch @panic("test failure"); + expect(i == 7) catch @panic("test failure"); +} + +extern fn c_ret_struct_array_3_sentinel_f32() Struct_array_3_sentinel_f32; +extern fn c_struct_array_3_sentinel_f32(Struct_array_3_sentinel_f32, usize) void; +extern fn c_test_struct_array_3_sentinel_f32() void; + +test "struct [3:sentinel]f32" { + if (builtin.cpu.arch.isAARCH64()) return error.SkipZigTest; + if (builtin.cpu.arch.isArm()) return error.SkipZigTest; + if (builtin.cpu.arch == .hexagon) return error.SkipZigTest; + if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest; + if (builtin.cpu.arch.isPowerPC()) return error.SkipZigTest; + + var sentinel_index: usize = 3; + _ = &sentinel_index; + const s = c_ret_struct_array_3_sentinel_f32(); + try expect(s.a[0] == 8); + try expect(s.a[1] == 9); + try expect(s.a[2] == 10); + try expect(s.a[sentinel_index] == 0x1e1); + c_struct_array_3_sentinel_f32(.{ .a = .{ 11, 12, 13 } }, 14); + c_test_struct_array_3_sentinel_f32(); +} + +const Struct_array_4_sentinel_f32 = extern struct { + a: [4:0x1e1]f32, +}; + +export fn zig_ret_struct_array_4_sentinel_f32() Struct_array_4_sentinel_f32 { + return .{ .a = .{ 1, 2, 3, 4 } }; +} +export fn zig_struct_array_4_sentinel_f32(s: Struct_array_4_sentinel_f32, i: usize) void { + var sentinel_index: usize = 4; + _ = &sentinel_index; + expect(s.a[0] == 5) catch @panic("test failure"); + expect(s.a[1] == 6) catch @panic("test failure"); + expect(s.a[2] == 7) catch @panic("test failure"); + expect(s.a[3] == 8) catch @panic("test failure"); + expect(s.a[sentinel_index] == 0x1e1) catch @panic("test failure"); + expect(i == 9) catch @panic("test failure"); +} + +extern fn c_ret_struct_array_4_sentinel_f32() Struct_array_4_sentinel_f32; +extern fn c_struct_array_4_sentinel_f32(Struct_array_4_sentinel_f32, usize) void; +extern fn c_test_struct_array_4_sentinel_f32() void; + +test "struct [4:sentinel]f32" { + if (builtin.cpu.arch.isArm()) return error.SkipZigTest; + if (builtin.cpu.arch == .hexagon) return error.SkipZigTest; + if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest; + if (builtin.cpu.arch.isPowerPC()) return error.SkipZigTest; + + var sentinel_index: usize = 4; + _ = &sentinel_index; + const s = c_ret_struct_array_4_sentinel_f32(); + try expect(s.a[0] == 10); + try expect(s.a[1] == 11); + try expect(s.a[2] == 12); + try expect(s.a[3] == 13); + try expect(s.a[sentinel_index] == 0x1e1); + c_struct_array_4_sentinel_f32(.{ .a = .{ 14, 15, 16, 17 } }, 18); + c_test_struct_array_4_sentinel_f32(); +} + const Struct_f32a8 = extern struct { a: f32 align(8), }; -- 2.54.0 From 6f7607c8e23fd8fd5d8fdbc28b119590f092dd0b Mon Sep 17 00:00:00 2001 From: Techatrix Date: Mon, 3 Aug 2026 21:02:56 +0200 Subject: [PATCH 2/5] c_abi: fix struct with single scalar array element on wasm --- src/codegen/wasm/abi.zig | 7 +++++++ test/c_abi/main.zig | 43 +++++++++++----------------------------- 2 files changed, 19 insertions(+), 31 deletions(-) diff --git a/src/codegen/wasm/abi.zig b/src/codegen/wasm/abi.zig index 0c6afdcc500202b4b4dd4b794d28b9fdb813af70..9f0b9b6dc9ebb7715d16839a6f42fd954d7787a2 100644 --- a/src/codegen/wasm/abi.zig +++ b/src/codegen/wasm/abi.zig @@ -84,6 +84,13 @@ pub fn classifyTypeForLlvm(ty: Type, zcu: *const Zcu) LlvmClass { if (explicit_align.compareStrict(.gt, field_ty.abiAlignment(zcu))) return .indirect; } + if (field_ty.zigTypeTag(zcu) == .array) { + switch (field_ty.arrayLenIncludingSentinel(zcu)) { + 0 => unreachable, + 1 => return classifyTypeForLlvm(field_ty.childType(zcu), zcu), + else => {}, + } + } return classifyTypeForLlvm(field_ty, zcu); }, .@"union" => { diff --git a/test/c_abi/main.zig b/test/c_abi/main.zig index 15cb889467a794aaadd7ce08eb33523b4ec65885..4cc383805d755af213f3e84a2cb5447c0a5315f1 100644 --- a/test/c_abi/main.zig +++ b/test/c_abi/main.zig @@ -16107,20 +16107,12 @@ const Struct_array_1_f32 = extern struct { a: [1]f32, }; -comptime { - skip: { - if (builtin.cpu.arch.isWasm()) break :skip; - - _ = struct { - export fn zig_ret_struct_array_1_f32() Struct_array_1_f32 { - return .{ .a = .{1} }; - } - export fn zig_struct_array_1_f32(s: Struct_array_1_f32, i: usize) void { - expect(s.a[0] == 2) catch @panic("test failure"); - expect(i == 3) catch @panic("test failure"); - } - }; - } +export fn zig_ret_struct_array_1_f32() Struct_array_1_f32 { + return .{ .a = .{1} }; +} +export fn zig_struct_array_1_f32(s: Struct_array_1_f32, i: usize) void { + expect(s.a[0] == 2) catch @panic("test failure"); + expect(i == 3) catch @panic("test failure"); } extern fn c_ret_struct_array_1_f32() Struct_array_1_f32; @@ -16134,7 +16126,6 @@ test "struct [1]f32" { if (builtin.cpu.arch.isPowerPC()) return error.SkipZigTest; if (builtin.cpu.arch.isRISCV()) return error.SkipZigTest; if (builtin.cpu.arch == .s390x) return error.SkipZigTest; - if (builtin.cpu.arch.isWasm()) return error.SkipZigTest; const s = c_ret_struct_array_1_f32(); try expect(s.a[0] == 4); @@ -16303,7 +16294,6 @@ test "struct [0:sentinel]f32" { if (builtin.cpu.arch.isPowerPC()) return error.SkipZigTest; if (builtin.cpu.arch.isRISCV()) return error.SkipZigTest; if (builtin.cpu.arch == .s390x) return error.SkipZigTest; - if (builtin.cpu.arch.isWasm()) return error.SkipZigTest; var sentinel_index: usize = 0; _ = &sentinel_index; @@ -16766,20 +16756,12 @@ const Struct_array_1_f64 = extern struct { a: [1]f64, }; -comptime { - skip: { - if (builtin.cpu.arch.isWasm()) break :skip; - - _ = struct { - export fn zig_ret_struct_array_1_f64() Struct_array_1_f64 { - return .{ .a = .{1} }; - } - export fn zig_struct_array_1_f64(s: Struct_array_1_f64, i: usize) void { - expect(s.a[0] == 2) catch @panic("test failure"); - expect(i == 3) catch @panic("test failure"); - } - }; - } +export fn zig_ret_struct_array_1_f64() Struct_array_1_f64 { + return .{ .a = .{1} }; +} +export fn zig_struct_array_1_f64(s: Struct_array_1_f64, i: usize) void { + expect(s.a[0] == 2) catch @panic("test failure"); + expect(i == 3) catch @panic("test failure"); } extern fn c_ret_struct_array_1_f64() Struct_array_1_f64; @@ -16793,7 +16775,6 @@ test "struct [1]f64" { if (builtin.cpu.arch.isPowerPC()) return error.SkipZigTest; if (builtin.cpu.arch.isRISCV()) return error.SkipZigTest; if (builtin.cpu.arch == .s390x) return error.SkipZigTest; - if (builtin.cpu.arch.isWasm()) return error.SkipZigTest; const s = c_ret_struct_array_1_f64(); try expect(s.a[0] == 4); -- 2.54.0 From 0bebd6108e586c5b6e216b4e69c1a06aa07d411d Mon Sep 17 00:00:00 2001 From: Techatrix Date: Tue, 4 Aug 2026 01:08:16 +0200 Subject: [PATCH 3/5] c_abi: fix union with single scalar array element on wasm --- src/codegen/wasm/abi.zig | 7 +++++++ test/c_abi/cfuncs.c | 20 ++++++++++++++++++++ test/c_abi/main.zig | 30 ++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+) diff --git a/src/codegen/wasm/abi.zig b/src/codegen/wasm/abi.zig index 9f0b9b6dc9ebb7715d16839a6f42fd954d7787a2..5c88caddb98a35c6f192c647fb5d53eb032375a5 100644 --- a/src/codegen/wasm/abi.zig +++ b/src/codegen/wasm/abi.zig @@ -102,6 +102,13 @@ pub fn classifyTypeForLlvm(ty: Type, zcu: *const Zcu) LlvmClass { assert(layout.tag_size == 0); if (union_obj.field_types.len > 1) return .indirect; const first_field_ty = Type.fromInterned(union_obj.field_types.get(ip)[0]); + if (first_field_ty.zigTypeTag(zcu) == .array) { + switch (first_field_ty.arrayLenIncludingSentinel(zcu)) { + 0 => unreachable, + 1 => return classifyTypeForLlvm(first_field_ty.childType(zcu), zcu), + else => {}, + } + } return classifyTypeForLlvm(first_field_ty, zcu); }, .error_union, diff --git a/test/c_abi/cfuncs.c b/test/c_abi/cfuncs.c index a259e23413a131b4e73b5f34329e1b2e6093f44d..110e5fd18dd4a5593c7004f439e85eed82c46a1d 100644 --- a/test/c_abi/cfuncs.c +++ b/test/c_abi/cfuncs.c @@ -15749,6 +15749,26 @@ void c_test_struct_array_5_f64(void) { zig_struct_array_5_f64((struct Struct_array_5_f64){ .a = { 6, 7, 8, 9, 10 } }, 11); } +union Union_f64 { + double a; +}; + +union Union_f64 zig_ret_union_f64(void); +void zig_union_f64(union Union_f64, size_t); + +union Union_f64 c_ret_union_f64(void) { + return (union Union_f64){ .a = 4 }; +} +void c_union_f64(union Union_f64 s, size_t i) { + assert_or_panic(s.a == 5); + assert_or_panic(i == 6); +} +void c_test_union_f64(void) { + union Union_f64 s = zig_ret_union_f64(); + assert_or_panic(s.a == 1); + zig_union_f64((union Union_f64){ .a = 2 }, 3); +} + struct Struct_u32_Union_u32_u32u32 { uint32_t a; union { diff --git a/test/c_abi/main.zig b/test/c_abi/main.zig index 4cc383805d755af213f3e84a2cb5447c0a5315f1..2286d838b86402f9e5e03c30e7db3732c2e3c3f4 100644 --- a/test/c_abi/main.zig +++ b/test/c_abi/main.zig @@ -16918,6 +16918,36 @@ test "struct [5]f64" { c_test_struct_array_5_f64(); } +const Union_f64 = extern union { + a: f64, +}; + +export fn zig_ret_union_f64() Union_f64 { + return .{ .a = 1 }; +} +export fn zig_union_f64(s: Union_f64, i: usize) void { + expect(s.a == 2) catch @panic("test failure"); + expect(i == 3) catch @panic("test failure"); +} + +extern fn c_ret_union_f64() Union_f64; +extern fn c_union_f64(Union_f64, usize) void; +extern fn c_test_union_f64() void; + +test "union f64" { + if (builtin.cpu.arch.isArm() and builtin.abi.float() == .soft) return error.SkipZigTest; + if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest; + if (builtin.cpu.arch.isPowerPC()) return error.SkipZigTest; + if (builtin.cpu.arch.isRiscv32()) return error.SkipZigTest; + if (builtin.cpu.arch == .s390x) return error.SkipZigTest; + if (builtin.cpu.arch == .x86 and builtin.os.tag == .windows) return error.SkipZigTest; + + const s = c_ret_union_f64(); + try expect(s.a == 4); + c_union_f64(.{ .a = 5 }, 6); + c_test_union_f64(); +} + const Struct_u32_Union_u32_u32u32 = extern struct { a: u32, b: extern union { -- 2.54.0 From fd13c819264007e8731fdb5ae9af211d18c70e9a Mon Sep 17 00:00:00 2001 From: Techatrix Date: Mon, 3 Aug 2026 21:52:28 +0200 Subject: [PATCH 4/5] c_abi: fix struct with OPV fields on wasm --- src/codegen/wasm/abi.zig | 36 ++++++++++++++++++++++-------------- test/c_abi/cfuncs.c | 16 ++++++++++++++++ test/c_abi/main.zig | 28 ++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 14 deletions(-) diff --git a/src/codegen/wasm/abi.zig b/src/codegen/wasm/abi.zig index 5c88caddb98a35c6f192c647fb5d53eb032375a5..1952c65227e8d57b0c164a62286814265bae0e3c 100644 --- a/src/codegen/wasm/abi.zig +++ b/src/codegen/wasm/abi.zig @@ -71,27 +71,35 @@ pub fn classifyTypeForLlvm(ty: Type, zcu: *const Zcu) LlvmClass { }, .@"struct" => { const struct_type = zcu.typeToStruct(ty).?; - if (struct_type.layout == .@"packed") { - return .{ .direct = ty }; + switch (struct_type.layout) { + .auto => unreachable, + .@"packed" => return .{ .direct = ty }, + .@"extern" => {}, } - if (struct_type.field_types.len > 1) { - // The struct type is non-scalar. - return .indirect; - } - const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[0]); - const explicit_align = struct_type.field_aligns.getOrNone(ip, 0); - if (explicit_align != .none) { - if (explicit_align.compareStrict(.gt, field_ty.abiAlignment(zcu))) + var opt_single_field_ty: ?Type = null; + for (struct_type.field_types.get(ip), 0..) |field_ty_index, field_index| { + const field_ty: Type = .fromInterned(field_ty_index); + if (!field_ty.hasRuntimeBits(zcu)) continue; + + if (opt_single_field_ty != null) { + return .indirect; + } + + const field_align = struct_type.field_aligns.getOrNone(ip, field_index); + if (field_align != .none and field_align.compareStrict(.gt, field_ty.abiAlignment(zcu))) { return .indirect; + } + opt_single_field_ty = field_ty; } - if (field_ty.zigTypeTag(zcu) == .array) { - switch (field_ty.arrayLenIncludingSentinel(zcu)) { + const single_field_ty = opt_single_field_ty.?; + if (single_field_ty.zigTypeTag(zcu) == .array) { + switch (single_field_ty.arrayLenIncludingSentinel(zcu)) { 0 => unreachable, - 1 => return classifyTypeForLlvm(field_ty.childType(zcu), zcu), + 1 => return classifyTypeForLlvm(single_field_ty.childType(zcu), zcu), else => {}, } } - return classifyTypeForLlvm(field_ty, zcu); + return classifyTypeForLlvm(single_field_ty, zcu); }, .@"union" => { const union_obj = zcu.typeToUnion(ty).?; diff --git a/test/c_abi/cfuncs.c b/test/c_abi/cfuncs.c index 110e5fd18dd4a5593c7004f439e85eed82c46a1d..327bc04ceb34148541b994bb894f04bea85bf210 100644 --- a/test/c_abi/cfuncs.c +++ b/test/c_abi/cfuncs.c @@ -15194,6 +15194,22 @@ void c_test_struct_f32_f32_f32_f32_f32(void) { zig_struct_f32_f32_f32_f32_f32((struct Struct_f32_f32_f32_f32_f32){ .a = 6, .b = 7, .c = 8, .d = 9, .e = 10 }, 11); } +struct Struct_f32 zig_ret_struct_void_f32(void); +void zig_struct_void_f32(struct Struct_f32, size_t); + +struct Struct_f32 c_ret_struct_void_f32(void) { + return (struct Struct_f32){ .a = 4 }; +} +void c_struct_void_f32(struct Struct_f32 s, size_t i) { + assert_or_panic(s.a == 5); + assert_or_panic(i == 6); +} +void c_test_struct_void_f32(void) { + struct Struct_f32 s = zig_ret_struct_void_f32(); + assert_or_panic(s.a == 1); + zig_struct_void_f32((struct Struct_f32){ .a = 2 }, 3); +} + struct Struct_array_1_f32 { float a[1]; }; diff --git a/test/c_abi/main.zig b/test/c_abi/main.zig index 2286d838b86402f9e5e03c30e7db3732c2e3c3f4..53e3890f31fa28014d84fc96012d21dbda3699dd 100644 --- a/test/c_abi/main.zig +++ b/test/c_abi/main.zig @@ -16103,6 +16103,34 @@ test "struct f32, f32, f32, f32, f32" { c_test_struct_f32_f32_f32_f32_f32(); } +const Struct_void_f32 = extern struct { + _: void = {}, + a: f32, +}; + +export fn zig_ret_struct_void_f32() Struct_void_f32 { + return .{ .a = 1 }; +} +export fn zig_struct_void_f32(s: Struct_void_f32, i: usize) void { + expect(s.a == 2) catch @panic("test failure"); + expect(i == 3) catch @panic("test failure"); +} + +extern fn c_ret_struct_void_f32() Struct_void_f32; +extern fn c_struct_void_f32(Struct_void_f32, usize) void; +extern fn c_test_struct_void_f32() void; + +test "struct void, f32" { + if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest; + if (builtin.cpu.arch.isPowerPC()) return error.SkipZigTest; + if (builtin.cpu.arch == .x86 and builtin.os.tag == .windows) return error.SkipZigTest; + + const s = c_ret_struct_void_f32(); + try expect(s.a == 4); + c_struct_void_f32(.{ .a = 5 }, 6); + c_test_struct_void_f32(); +} + const Struct_array_1_f32 = extern struct { a: [1]f32, }; -- 2.54.0 From 46f98e366b10e1db59ca776c8cefcf827a66ffde Mon Sep 17 00:00:00 2001 From: Techatrix Date: Mon, 3 Aug 2026 22:02:28 +0200 Subject: [PATCH 5/5] c_abi: fix single element array on s390x --- src/codegen/s390x/abi.zig | 9 +-------- test/c_abi/main.zig | 3 --- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/src/codegen/s390x/abi.zig b/src/codegen/s390x/abi.zig index f8c7864a4a313f54085b357a93f6d943ab72524e..2c3051ffc3139a314cedadf9ec98027d846a7dd3 100644 --- a/src/codegen/s390x/abi.zig +++ b/src/codegen/s390x/abi.zig @@ -45,14 +45,7 @@ pub fn classifyType(ty: Type, context: Context, zcu: *Zcu) Class { 128 => return .pointer, }, .pointer, .optional => return .simple, - .array => switch (ty.arrayLenIncludingSentinel(zcu)) { - 0 => return .none, - 1 => switch (context) { - .ret => {}, - .arg => return classifyType(ty.childType(zcu), context, zcu), - }, - else => {}, - }, + .array => {}, .@"struct", .@"union" => |tag| switch (ty.containerLayout(zcu)) { .auto => unreachable, .@"extern" => switch (context) { diff --git a/test/c_abi/main.zig b/test/c_abi/main.zig index 53e3890f31fa28014d84fc96012d21dbda3699dd..a481a015117276e8abf7c4393f6d85cf60dd20fb 100644 --- a/test/c_abi/main.zig +++ b/test/c_abi/main.zig @@ -16153,7 +16153,6 @@ test "struct [1]f32" { if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest; if (builtin.cpu.arch.isPowerPC()) return error.SkipZigTest; if (builtin.cpu.arch.isRISCV()) return error.SkipZigTest; - if (builtin.cpu.arch == .s390x) return error.SkipZigTest; const s = c_ret_struct_array_1_f32(); try expect(s.a[0] == 4); @@ -16321,7 +16320,6 @@ test "struct [0:sentinel]f32" { if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest; if (builtin.cpu.arch.isPowerPC()) return error.SkipZigTest; if (builtin.cpu.arch.isRISCV()) return error.SkipZigTest; - if (builtin.cpu.arch == .s390x) return error.SkipZigTest; var sentinel_index: usize = 0; _ = &sentinel_index; @@ -16802,7 +16800,6 @@ test "struct [1]f64" { if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest; if (builtin.cpu.arch.isPowerPC()) return error.SkipZigTest; if (builtin.cpu.arch.isRISCV()) return error.SkipZigTest; - if (builtin.cpu.arch == .s390x) return error.SkipZigTest; const s = c_ret_struct_array_1_f64(); try expect(s.a[0] == 4); -- 2.54.0