Skip to content

Commit 48c5358

Browse files
committed
std.mem.zeroes: Improve handling of extern struct and extern union
`extern struct` will now be recursively initialized, same as `struct`. The only difference is that the `extern` variant is initially zeroed out to ensure its padding is also set correctly. `extern union` is still initialized to its first named member, but its padding (if any) is also zeroed out. Fixes #17258
1 parent 8348f08 commit 48c5358

File tree

1 file changed

+24
-17
lines changed

1 file changed

+24
-17
lines changed

lib/std/mem.zig

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -246,19 +246,16 @@ pub fn zeroes(comptime T: type) T {
246246
},
247247
.Struct => |struct_info| {
248248
if (@sizeOf(T) == 0) return undefined;
249+
var structure: T = undefined;
249250
if (struct_info.layout == .Extern) {
250-
var item: T = undefined;
251-
@memset(asBytes(&item), 0);
252-
return item;
253-
} else {
254-
var structure: T = undefined;
255-
inline for (struct_info.fields) |field| {
256-
if (!field.is_comptime) {
257-
@field(structure, field.name) = zeroes(@TypeOf(@field(structure, field.name)));
258-
}
251+
@memset(asBytes(&structure), 0); // Zero out padding.
252+
}
253+
inline for (struct_info.fields) |field| {
254+
if (!field.is_comptime) {
255+
@field(structure, field.name) = zeroes(field.type);
259256
}
260-
return structure;
261257
}
258+
return structure;
262259
},
263260
.Pointer => |ptr_info| {
264261
switch (ptr_info.size) {
@@ -292,13 +289,17 @@ pub fn zeroes(comptime T: type) T {
292289
return @splat(zeroes(info.child));
293290
},
294291
.Union => |info| {
295-
if (comptime meta.containerLayout(T) == .Extern) {
296-
// The C language specification states that (global) unions
297-
// should be zero initialized to the first named member.
298-
return @unionInit(T, info.fields[0].name, zeroes(info.fields[0].type));
292+
if (info.layout != .Extern) {
293+
@compileError("Can't set a " ++ @typeName(T) ++ " to zero.");
299294
}
300-
301-
@compileError("Can't set a " ++ @typeName(T) ++ " to zero.");
295+
// The C language specification states that (global) unions
296+
// have the first named member initialized (recursively),
297+
// and any padding is initialized to zero bits.
298+
const field = info.fields[0];
299+
var u = @unionInit(T, field.name, zeroes(field.type));
300+
const padding = asBytes(&u)[@sizeOf(field.type)..];
301+
if (padding.len > 0) @memset(padding, 0);
302+
return u;
302303
},
303304
.ErrorUnion,
304305
.ErrorSet,
@@ -318,10 +319,14 @@ pub fn zeroes(comptime T: type) T {
318319
test "zeroes" {
319320
const C_struct = extern struct {
320321
x: u32,
321-
y: u32,
322+
y: u32 align(128),
322323
};
323324

324325
var a = zeroes(C_struct);
326+
327+
// Extern structs should have padding zeroed out.
328+
try testing.expectEqualSlices(u8, &[_]u8{0} ** @sizeOf(@TypeOf(a)), asBytes(&a));
329+
325330
a.y += 10;
326331

327332
try testing.expect(a.x == 0);
@@ -402,9 +407,11 @@ test "zeroes" {
402407

403408
var c = zeroes(C_union);
404409
try testing.expectEqual(@as(u8, 0), c.a);
410+
try testing.expectEqual(@as(u32, 0), c.b);
405411

406412
comptime var comptime_union = zeroes(C_union);
407413
try testing.expectEqual(@as(u8, 0), comptime_union.a);
414+
try testing.expectEqual(@as(u32, 0), comptime_union.b);
408415

409416
// Ensure zero sized struct with fields is initialized correctly.
410417
_ = zeroes(struct { handle: void });

0 commit comments

Comments
 (0)