Skip to content

Commit 8e46b7a

Browse files
committed
Sema: validate packed struct field types
1 parent f1768b4 commit 8e46b7a

File tree

6 files changed

+215
-131
lines changed

6 files changed

+215
-131
lines changed

src/Sema.zig

Lines changed: 125 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5049,13 +5049,13 @@ pub fn analyzeExport(
50495049
try mod.ensureDeclAnalyzed(exported_decl_index);
50505050
const exported_decl = mod.declPtr(exported_decl_index);
50515051

5052-
if (!(try sema.validateExternType(exported_decl.ty, .other))) {
5052+
if (!sema.validateExternType(exported_decl.ty, .other)) {
50535053
const msg = msg: {
50545054
const msg = try sema.errMsg(block, src, "unable to export type '{}'", .{exported_decl.ty.fmt(sema.mod)});
50555055
errdefer msg.destroy(sema.gpa);
50565056

50575057
const src_decl = sema.mod.declPtr(block.src_decl);
5058-
try sema.explainWhyTypeIsNotExtern(block, src, msg, src.toSrcLoc(src_decl), exported_decl.ty, .other);
5058+
try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl), exported_decl.ty, .other);
50595059

50605060
try sema.addDeclaredHereNote(msg, exported_decl.ty);
50615061
break :msg msg;
@@ -7634,15 +7634,15 @@ fn funcCommon(
76347634
};
76357635
return sema.failWithOwnedErrorMsg(block, msg);
76367636
}
7637-
if (!Type.fnCallingConventionAllowsZigTypes(cc_workaround) and !(try sema.validateExternType(return_type, .ret_ty))) {
7637+
if (!Type.fnCallingConventionAllowsZigTypes(cc_workaround) and !sema.validateExternType(return_type, .ret_ty)) {
76387638
const msg = msg: {
76397639
const msg = try sema.errMsg(block, ret_ty_src, "return type '{}' not allowed in function with calling convention '{s}'", .{
76407640
return_type.fmt(sema.mod), @tagName(cc_workaround),
76417641
});
76427642
errdefer msg.destroy(sema.gpa);
76437643

76447644
const src_decl = sema.mod.declPtr(block.src_decl);
7645-
try sema.explainWhyTypeIsNotExtern(block, ret_ty_src, msg, ret_ty_src.toSrcLoc(src_decl), return_type, .ret_ty);
7645+
try sema.explainWhyTypeIsNotExtern(msg, ret_ty_src.toSrcLoc(src_decl), return_type, .ret_ty);
76467646

76477647
try sema.addDeclaredHereNote(msg, return_type);
76487648
break :msg msg;
@@ -7830,15 +7830,15 @@ fn analyzeParameter(
78307830
};
78317831
return sema.failWithOwnedErrorMsg(block, msg);
78327832
}
7833-
if (!Type.fnCallingConventionAllowsZigTypes(cc) and !(try sema.validateExternType(param.ty, .param_ty))) {
7833+
if (!Type.fnCallingConventionAllowsZigTypes(cc) and !sema.validateExternType(param.ty, .param_ty)) {
78347834
const msg = msg: {
78357835
const msg = try sema.errMsg(block, param_src, "parameter of type '{}' not allowed in function with calling convention '{s}'", .{
78367836
param.ty.fmt(sema.mod), @tagName(cc),
78377837
});
78387838
errdefer msg.destroy(sema.gpa);
78397839

78407840
const src_decl = sema.mod.declPtr(block.src_decl);
7841-
try sema.explainWhyTypeIsNotExtern(block, param_src, msg, param_src.toSrcLoc(src_decl), param.ty, .param_ty);
7841+
try sema.explainWhyTypeIsNotExtern(msg, param_src.toSrcLoc(src_decl), param.ty, .param_ty);
78427842

78437843
try sema.addDeclaredHereNote(msg, param.ty);
78447844
break :msg msg;
@@ -14866,13 +14866,13 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
1486614866
} else if (inst_data.size == .Many and elem_ty.zigTypeTag() == .Opaque) {
1486714867
return sema.fail(block, elem_ty_src, "unknown-length pointer to opaque not allowed", .{});
1486814868
} else if (inst_data.size == .C) {
14869-
if (!(try sema.validateExternType(elem_ty, .other))) {
14869+
if (!sema.validateExternType(elem_ty, .other)) {
1487014870
const msg = msg: {
1487114871
const msg = try sema.errMsg(block, elem_ty_src, "C pointers cannot point to non-C-ABI-compatible type '{}'", .{elem_ty.fmt(sema.mod)});
1487214872
errdefer msg.destroy(sema.gpa);
1487314873

1487414874
const src_decl = sema.mod.declPtr(block.src_decl);
14875-
try sema.explainWhyTypeIsNotExtern(block, elem_ty_src, msg, elem_ty_src.toSrcLoc(src_decl), elem_ty, .other);
14875+
try sema.explainWhyTypeIsNotExtern(msg, elem_ty_src.toSrcLoc(src_decl), elem_ty, .other);
1487614876

1487714877
try sema.addDeclaredHereNote(msg, elem_ty);
1487814878
break :msg msg;
@@ -19736,7 +19736,7 @@ const ExternPosition = enum {
1973619736

1973719737
/// Returns true if `ty` is allowed in extern types.
1973819738
/// Does *NOT* require `ty` to be resolved in any way.
19739-
fn validateExternType(sema: *Sema, ty: Type, position: ExternPosition) CompileError!bool {
19739+
fn validateExternType(sema: *Sema, ty: Type, position: ExternPosition) bool {
1974019740
switch (ty.zigTypeTag()) {
1974119741
.Type,
1974219742
.ComptimeFloat,
@@ -19781,8 +19781,6 @@ fn validateExternType(sema: *Sema, ty: Type, position: ExternPosition) CompileEr
1978119781

1978219782
fn explainWhyTypeIsNotExtern(
1978319783
sema: *Sema,
19784-
block: *Block,
19785-
src: LazySrcLoc,
1978619784
msg: *Module.ErrorMsg,
1978719785
src_loc: Module.SrcLoc,
1978819786
ty: Type,
@@ -19826,7 +19824,7 @@ fn explainWhyTypeIsNotExtern(
1982619824
var buf: Type.Payload.Bits = undefined;
1982719825
const tag_ty = ty.intTagType(&buf);
1982819826
try mod.errNoteNonLazy(src_loc, msg, "enum tag type '{}' is not extern compatible", .{tag_ty.fmt(sema.mod)});
19829-
try sema.explainWhyTypeIsNotExtern(block, src, msg, src_loc, tag_ty, position);
19827+
try sema.explainWhyTypeIsNotExtern(msg, src_loc, tag_ty, position);
1983019828
},
1983119829
.Struct => try mod.errNoteNonLazy(src_loc, msg, "only structs with packed or extern layout are extern compatible", .{}),
1983219830
.Union => try mod.errNoteNonLazy(src_loc, msg, "only unions with packed or extern layout are extern compatible", .{}),
@@ -19836,13 +19834,87 @@ fn explainWhyTypeIsNotExtern(
1983619834
} else if (position == .param_ty) {
1983719835
return mod.errNoteNonLazy(src_loc, msg, "arrays are not allowed as a parameter type", .{});
1983819836
}
19839-
try sema.explainWhyTypeIsNotExtern(block, src, msg, src_loc, ty.elemType2(), position);
19837+
try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.elemType2(), position);
1984019838
},
19841-
.Vector => try sema.explainWhyTypeIsNotExtern(block, src, msg, src_loc, ty.elemType2(), position),
19839+
.Vector => try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.elemType2(), position),
1984219840
.Optional => try mod.errNoteNonLazy(src_loc, msg, "only pointer like optionals are extern compatible", .{}),
1984319841
}
1984419842
}
1984519843

19844+
/// Returns true if `ty` is allowed in packed types.
19845+
/// Does *NOT* require `ty` to be resolved in any way.
19846+
fn validatePackedType(ty: Type) bool {
19847+
switch (ty.zigTypeTag()) {
19848+
.Type,
19849+
.ComptimeFloat,
19850+
.ComptimeInt,
19851+
.EnumLiteral,
19852+
.Undefined,
19853+
.Null,
19854+
.ErrorUnion,
19855+
.ErrorSet,
19856+
.BoundFn,
19857+
.Frame,
19858+
.NoReturn,
19859+
.Opaque,
19860+
.AnyFrame,
19861+
.Fn,
19862+
.Array,
19863+
.Optional,
19864+
=> return false,
19865+
.Void,
19866+
.Bool,
19867+
.Float,
19868+
.Pointer,
19869+
.Int,
19870+
.Vector,
19871+
.Enum,
19872+
=> return true,
19873+
.Struct, .Union => return ty.containerLayout() == .Packed,
19874+
}
19875+
}
19876+
19877+
fn explainWhyTypeIsNotPacked(
19878+
sema: *Sema,
19879+
msg: *Module.ErrorMsg,
19880+
src_loc: Module.SrcLoc,
19881+
ty: Type,
19882+
) CompileError!void {
19883+
const mod = sema.mod;
19884+
switch (ty.zigTypeTag()) {
19885+
.Void,
19886+
.Bool,
19887+
.Float,
19888+
.Pointer,
19889+
.Int,
19890+
.Vector,
19891+
.Enum,
19892+
=> return,
19893+
.Type,
19894+
.ComptimeFloat,
19895+
.ComptimeInt,
19896+
.EnumLiteral,
19897+
.Undefined,
19898+
.Null,
19899+
.BoundFn,
19900+
.Frame,
19901+
.NoReturn,
19902+
.Opaque,
19903+
.ErrorUnion,
19904+
.ErrorSet,
19905+
.AnyFrame,
19906+
.Optional,
19907+
.Array,
19908+
=> try mod.errNoteNonLazy(src_loc, msg, "type has no guaranteed in-memory representation", .{}),
19909+
.Fn => {
19910+
try mod.errNoteNonLazy(src_loc, msg, "type has no guaranteed in-memory representation", .{});
19911+
try mod.errNoteNonLazy(src_loc, msg, "use '*const ' to make a function pointer type", .{});
19912+
},
19913+
.Struct => try mod.errNoteNonLazy(src_loc, msg, "only packed structs layout are allowed in packed types", .{}),
19914+
.Union => try mod.errNoteNonLazy(src_loc, msg, "only packed unions layout are allowed in packed types", .{}),
19915+
}
19916+
}
19917+
1984619918
pub const PanicId = enum {
1984719919
unreach,
1984819920
unwrap_null,
@@ -26919,28 +26991,41 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void
2691926991
const field = &struct_obj.fields.values()[i];
2692026992
field.ty = try field_ty.copy(decl_arena_allocator);
2692126993

26922-
if (struct_obj.layout == .Extern and !(try sema.validateExternType(field.ty, .other))) {
26994+
if (field_ty.zigTypeTag() == .Opaque) {
26995+
const msg = msg: {
26996+
const tree = try sema.getAstTree(&block_scope);
26997+
const field_src = enumFieldSrcLoc(decl, tree.*, struct_obj.node_offset, i);
26998+
const msg = try sema.errMsg(&block_scope, field_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{});
26999+
errdefer msg.destroy(sema.gpa);
27000+
27001+
try sema.addDeclaredHereNote(msg, field_ty);
27002+
break :msg msg;
27003+
};
27004+
return sema.failWithOwnedErrorMsg(&block_scope, msg);
27005+
}
27006+
if (struct_obj.layout == .Extern and !sema.validateExternType(field.ty, .other)) {
2692327007
const msg = msg: {
2692427008
const tree = try sema.getAstTree(&block_scope);
2692527009
const fields_src = enumFieldSrcLoc(decl, tree.*, struct_obj.node_offset, i);
2692627010
const msg = try sema.errMsg(&block_scope, fields_src, "extern structs cannot contain fields of type '{}'", .{field.ty.fmt(sema.mod)});
2692727011
errdefer msg.destroy(sema.gpa);
2692827012

26929-
try sema.explainWhyTypeIsNotExtern(&block_scope, fields_src, msg, fields_src.toSrcLoc(decl), field.ty, .other);
27013+
try sema.explainWhyTypeIsNotExtern(msg, fields_src.toSrcLoc(decl), field.ty, .other);
2693027014

2693127015
try sema.addDeclaredHereNote(msg, field.ty);
2693227016
break :msg msg;
2693327017
};
2693427018
return sema.failWithOwnedErrorMsg(&block_scope, msg);
26935-
}
26936-
if (field_ty.zigTypeTag() == .Opaque) {
27019+
} else if (struct_obj.layout == .Packed and !(validatePackedType(field.ty))) {
2693727020
const msg = msg: {
2693827021
const tree = try sema.getAstTree(&block_scope);
26939-
const field_src = enumFieldSrcLoc(decl, tree.*, struct_obj.node_offset, i);
26940-
const msg = try sema.errMsg(&block_scope, field_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{});
27022+
const fields_src = enumFieldSrcLoc(decl, tree.*, struct_obj.node_offset, i);
27023+
const msg = try sema.errMsg(&block_scope, fields_src, "packed structs cannot contain fields of type '{}'", .{field.ty.fmt(sema.mod)});
2694127024
errdefer msg.destroy(sema.gpa);
2694227025

26943-
try sema.addDeclaredHereNote(msg, field_ty);
27026+
try sema.explainWhyTypeIsNotPacked(msg, fields_src.toSrcLoc(decl), field.ty);
27027+
27028+
try sema.addDeclaredHereNote(msg, field.ty);
2694427029
break :msg msg;
2694527030
};
2694627031
return sema.failWithOwnedErrorMsg(&block_scope, msg);
@@ -27243,27 +27328,40 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
2724327328
}
2724427329
}
2724527330

27246-
if (union_obj.layout == .Extern and !(try sema.validateExternType(field_ty, .union_field))) {
27331+
if (field_ty.zigTypeTag() == .Opaque) {
2724727332
const msg = msg: {
2724827333
const tree = try sema.getAstTree(&block_scope);
2724927334
const field_src = enumFieldSrcLoc(decl, tree.*, union_obj.node_offset, field_i);
27250-
const msg = try sema.errMsg(&block_scope, field_src, "extern unions cannot contain fields of type '{}'", .{field_ty.fmt(sema.mod)});
27335+
const msg = try sema.errMsg(&block_scope, field_src, "opaque types have unknown size and therefore cannot be directly embedded in unions", .{});
2725127336
errdefer msg.destroy(sema.gpa);
2725227337

27253-
try sema.explainWhyTypeIsNotExtern(&block_scope, field_src, msg, field_src.toSrcLoc(decl), field_ty, .union_field);
27254-
2725527338
try sema.addDeclaredHereNote(msg, field_ty);
2725627339
break :msg msg;
2725727340
};
2725827341
return sema.failWithOwnedErrorMsg(&block_scope, msg);
2725927342
}
27260-
if (field_ty.zigTypeTag() == .Opaque) {
27343+
if (union_obj.layout == .Extern and !sema.validateExternType(field_ty, .union_field)) {
2726127344
const msg = msg: {
2726227345
const tree = try sema.getAstTree(&block_scope);
2726327346
const field_src = enumFieldSrcLoc(decl, tree.*, union_obj.node_offset, field_i);
27264-
const msg = try sema.errMsg(&block_scope, field_src, "opaque types have unknown size and therefore cannot be directly embedded in unions", .{});
27347+
const msg = try sema.errMsg(&block_scope, field_src, "extern unions cannot contain fields of type '{}'", .{field_ty.fmt(sema.mod)});
2726527348
errdefer msg.destroy(sema.gpa);
2726627349

27350+
try sema.explainWhyTypeIsNotExtern(msg, field_src.toSrcLoc(decl), field_ty, .union_field);
27351+
27352+
try sema.addDeclaredHereNote(msg, field_ty);
27353+
break :msg msg;
27354+
};
27355+
return sema.failWithOwnedErrorMsg(&block_scope, msg);
27356+
} else if (union_obj.layout == .Packed and !(validatePackedType(field_ty))) {
27357+
const msg = msg: {
27358+
const tree = try sema.getAstTree(&block_scope);
27359+
const fields_src = enumFieldSrcLoc(decl, tree.*, union_obj.node_offset, field_i);
27360+
const msg = try sema.errMsg(&block_scope, fields_src, "packed unions cannot contain fields of type '{}'", .{field_ty.fmt(sema.mod)});
27361+
errdefer msg.destroy(sema.gpa);
27362+
27363+
try sema.explainWhyTypeIsNotPacked(msg, fields_src.toSrcLoc(decl), field_ty);
27364+
2726727365
try sema.addDeclaredHereNote(msg, field_ty);
2726827366
break :msg msg;
2726927367
};

test/behavior/packed-struct.zig

Lines changed: 2 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ const expectEqual = std.testing.expectEqual;
66
const native_endian = builtin.cpu.arch.endian();
77

88
test "correct size of packed structs" {
9+
// Stage2 has different packed struct semantics.
10+
if (builtin.zig_backend != .stage1) return error.SkipZigTest;
911
const T1 = packed struct { one: u8, three: [3]u8 };
1012

1113
try expectEqual(4, @sizeOf(T1));
@@ -118,18 +120,6 @@ test "flags in packed structs" {
118120
try expectEqual(32, @bitSizeOf(Flags3));
119121
}
120122

121-
test "arrays in packed structs" {
122-
if (builtin.zig_backend == .stage1) return error.SkipZigTest;
123-
124-
const T1 = packed struct { array: [3][3]u8 };
125-
const T2 = packed struct { array: [9]u8 };
126-
127-
try expectEqual(@sizeOf(u72), @sizeOf(T1));
128-
try expectEqual(72, @bitSizeOf(T1));
129-
try expectEqual(@sizeOf(u72), @sizeOf(T2));
130-
try expectEqual(72, @bitSizeOf(T2));
131-
}
132-
133123
test "consistent size of packed structs" {
134124
if (builtin.zig_backend == .stage1) return error.SkipZigTest;
135125

@@ -145,23 +135,15 @@ test "consistent size of packed structs" {
145135
try expectEqual(register_size_bits, @bitSizeOf(TxData2));
146136
try expectEqual(register_size_bytes, @sizeOf(TxData2));
147137

148-
const TxData3 = packed struct { a: u32, b: [3]u8 };
149138
const TxData4 = packed struct { a: u32, b: u24 };
150-
const TxData5 = packed struct { a: [3]u8, b: u32 };
151139
const TxData6 = packed struct { a: u24, b: u32 };
152140

153141
const expectedBitSize = 56;
154142
const expectedByteSize = @sizeOf(u56);
155143

156-
try expectEqual(expectedBitSize, @bitSizeOf(TxData3));
157-
try expectEqual(expectedByteSize, @sizeOf(TxData3));
158-
159144
try expectEqual(expectedBitSize, @bitSizeOf(TxData4));
160145
try expectEqual(expectedByteSize, @sizeOf(TxData4));
161146

162-
try expectEqual(expectedBitSize, @bitSizeOf(TxData5));
163-
try expectEqual(expectedByteSize, @sizeOf(TxData5));
164-
165147
try expectEqual(expectedBitSize, @bitSizeOf(TxData6));
166148
try expectEqual(expectedByteSize, @sizeOf(TxData6));
167149
}
@@ -234,12 +216,6 @@ test "correct sizeOf and offsets in packed structs" {
234216
try expectEqual(@as(u7, 0b1111010), s2.y);
235217
try expectEqual(@as(u24, 0xd5c71f), s2.z);
236218
}
237-
238-
const S = packed struct { a: u32, pad: [3]u32, b: u32 };
239-
240-
try expectEqual(16, @offsetOf(S, "b"));
241-
try expectEqual(128, @bitOffsetOf(S, "b"));
242-
try expectEqual(@sizeOf(u160), @sizeOf(S));
243219
}
244220

245221
test "nested packed structs" {

test/behavior/sizeof_and_typeof.zig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ test "@offsetOf" {
105105
}
106106

107107
test "@offsetOf packed struct, array length not power of 2 or multiple of native pointer width in bytes" {
108+
// Stage2 has different packed struct semantics.
109+
if (builtin.zig_backend != .stage1) return error.SkipZigTest;
108110
const p3a_len = 3;
109111
const P3 = packed struct {
110112
a: [p3a_len]u8,

test/behavior/struct.zig

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -704,10 +704,8 @@ const FooArray24Bits = packed struct {
704704
};
705705

706706
test "aligned array of packed struct" {
707-
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
708-
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
709-
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
710-
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
707+
// Stage2 has different packed struct semantics.
708+
if (builtin.zig_backend != .stage1) return error.SkipZigTest;
711709

712710
comptime {
713711
try expect(@sizeOf(FooStructAligned) == 2);

0 commit comments

Comments
 (0)