Skip to content

Commit 2b31115

Browse files
committed
Sema: validate packed struct field types
1 parent 571c83b commit 2b31115

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
@@ -5048,13 +5048,13 @@ pub fn analyzeExport(
50485048
try mod.ensureDeclAnalyzed(exported_decl_index);
50495049
const exported_decl = mod.declPtr(exported_decl_index);
50505050

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

50565056
const src_decl = sema.mod.declPtr(block.src_decl);
5057-
try sema.explainWhyTypeIsNotExtern(block, src, msg, src.toSrcLoc(src_decl), exported_decl.ty, .other);
5057+
try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl), exported_decl.ty, .other);
50585058

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

76437643
const src_decl = sema.mod.declPtr(block.src_decl);
7644-
try sema.explainWhyTypeIsNotExtern(block, ret_ty_src, msg, ret_ty_src.toSrcLoc(src_decl), return_type, .ret_ty);
7644+
try sema.explainWhyTypeIsNotExtern(msg, ret_ty_src.toSrcLoc(src_decl), return_type, .ret_ty);
76457645

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

78397839
const src_decl = sema.mod.declPtr(block.src_decl);
7840-
try sema.explainWhyTypeIsNotExtern(block, param_src, msg, param_src.toSrcLoc(src_decl), param.ty, .param_ty);
7840+
try sema.explainWhyTypeIsNotExtern(msg, param_src.toSrcLoc(src_decl), param.ty, .param_ty);
78417841

78427842
try sema.addDeclaredHereNote(msg, param.ty);
78437843
break :msg msg;
@@ -14574,13 +14574,13 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
1457414574
} else if (inst_data.size == .Many and elem_ty.zigTypeTag() == .Opaque) {
1457514575
return sema.fail(block, elem_ty_src, "unknown-length pointer to opaque not allowed", .{});
1457614576
} else if (inst_data.size == .C) {
14577-
if (!(try sema.validateExternType(elem_ty, .other))) {
14577+
if (!sema.validateExternType(elem_ty, .other)) {
1457814578
const msg = msg: {
1457914579
const msg = try sema.errMsg(block, elem_ty_src, "C pointers cannot point to non-C-ABI-compatible type '{}'", .{elem_ty.fmt(sema.mod)});
1458014580
errdefer msg.destroy(sema.gpa);
1458114581

1458214582
const src_decl = sema.mod.declPtr(block.src_decl);
14583-
try sema.explainWhyTypeIsNotExtern(block, elem_ty_src, msg, elem_ty_src.toSrcLoc(src_decl), elem_ty, .other);
14583+
try sema.explainWhyTypeIsNotExtern(msg, elem_ty_src.toSrcLoc(src_decl), elem_ty, .other);
1458414584

1458514585
try sema.addDeclaredHereNote(msg, elem_ty);
1458614586
break :msg msg;
@@ -19233,7 +19233,7 @@ const ExternPosition = enum {
1923319233

1923419234
/// Returns true if `ty` is allowed in extern types.
1923519235
/// Does *NOT* require `ty` to be resolved in any way.
19236-
fn validateExternType(sema: *Sema, ty: Type, position: ExternPosition) CompileError!bool {
19236+
fn validateExternType(sema: *Sema, ty: Type, position: ExternPosition) bool {
1923719237
switch (ty.zigTypeTag()) {
1923819238
.Type,
1923919239
.ComptimeFloat,
@@ -19278,8 +19278,6 @@ fn validateExternType(sema: *Sema, ty: Type, position: ExternPosition) CompileEr
1927819278

1927919279
fn explainWhyTypeIsNotExtern(
1928019280
sema: *Sema,
19281-
block: *Block,
19282-
src: LazySrcLoc,
1928319281
msg: *Module.ErrorMsg,
1928419282
src_loc: Module.SrcLoc,
1928519283
ty: Type,
@@ -19323,7 +19321,7 @@ fn explainWhyTypeIsNotExtern(
1932319321
var buf: Type.Payload.Bits = undefined;
1932419322
const tag_ty = ty.intTagType(&buf);
1932519323
try mod.errNoteNonLazy(src_loc, msg, "enum tag type '{}' is not extern compatible", .{tag_ty.fmt(sema.mod)});
19326-
try sema.explainWhyTypeIsNotExtern(block, src, msg, src_loc, tag_ty, position);
19324+
try sema.explainWhyTypeIsNotExtern(msg, src_loc, tag_ty, position);
1932719325
},
1932819326
.Struct => try mod.errNoteNonLazy(src_loc, msg, "only structs with packed or extern layout are extern compatible", .{}),
1932919327
.Union => try mod.errNoteNonLazy(src_loc, msg, "only unions with packed or extern layout are extern compatible", .{}),
@@ -19333,13 +19331,87 @@ fn explainWhyTypeIsNotExtern(
1933319331
} else if (position == .param_ty) {
1933419332
return mod.errNoteNonLazy(src_loc, msg, "arrays are not allowed as a parameter type", .{});
1933519333
}
19336-
try sema.explainWhyTypeIsNotExtern(block, src, msg, src_loc, ty.elemType2(), position);
19334+
try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.elemType2(), position);
1933719335
},
19338-
.Vector => try sema.explainWhyTypeIsNotExtern(block, src, msg, src_loc, ty.elemType2(), position),
19336+
.Vector => try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.elemType2(), position),
1933919337
.Optional => try mod.errNoteNonLazy(src_loc, msg, "only pointer like optionals are extern compatible", .{}),
1934019338
}
1934119339
}
1934219340

19341+
/// Returns true if `ty` is allowed in packed types.
19342+
/// Does *NOT* require `ty` to be resolved in any way.
19343+
fn validatePackedType(ty: Type) bool {
19344+
switch (ty.zigTypeTag()) {
19345+
.Type,
19346+
.ComptimeFloat,
19347+
.ComptimeInt,
19348+
.EnumLiteral,
19349+
.Undefined,
19350+
.Null,
19351+
.ErrorUnion,
19352+
.ErrorSet,
19353+
.BoundFn,
19354+
.Frame,
19355+
.NoReturn,
19356+
.Opaque,
19357+
.AnyFrame,
19358+
.Fn,
19359+
.Array,
19360+
.Optional,
19361+
=> return false,
19362+
.Void,
19363+
.Bool,
19364+
.Float,
19365+
.Pointer,
19366+
.Int,
19367+
.Vector,
19368+
.Enum,
19369+
=> return true,
19370+
.Struct, .Union => return ty.containerLayout() == .Packed,
19371+
}
19372+
}
19373+
19374+
fn explainWhyTypeIsNotPacked(
19375+
sema: *Sema,
19376+
msg: *Module.ErrorMsg,
19377+
src_loc: Module.SrcLoc,
19378+
ty: Type,
19379+
) CompileError!void {
19380+
const mod = sema.mod;
19381+
switch (ty.zigTypeTag()) {
19382+
.Void,
19383+
.Bool,
19384+
.Float,
19385+
.Pointer,
19386+
.Int,
19387+
.Vector,
19388+
.Enum,
19389+
=> return,
19390+
.Type,
19391+
.ComptimeFloat,
19392+
.ComptimeInt,
19393+
.EnumLiteral,
19394+
.Undefined,
19395+
.Null,
19396+
.BoundFn,
19397+
.Frame,
19398+
.NoReturn,
19399+
.Opaque,
19400+
.ErrorUnion,
19401+
.ErrorSet,
19402+
.AnyFrame,
19403+
.Optional,
19404+
.Array,
19405+
=> try mod.errNoteNonLazy(src_loc, msg, "type has no guaranteed in-memory representation", .{}),
19406+
.Fn => {
19407+
try mod.errNoteNonLazy(src_loc, msg, "type has no guaranteed in-memory representation", .{});
19408+
try mod.errNoteNonLazy(src_loc, msg, "use '*const ' to make a function pointer type", .{});
19409+
},
19410+
.Struct => try mod.errNoteNonLazy(src_loc, msg, "only packed structs layout are allowed in packed types", .{}),
19411+
.Union => try mod.errNoteNonLazy(src_loc, msg, "only packed unions layout are allowed in packed types", .{}),
19412+
}
19413+
}
19414+
1934319415
pub const PanicId = enum {
1934419416
unreach,
1934519417
unwrap_null,
@@ -26271,28 +26343,41 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void
2627126343
const field = &struct_obj.fields.values()[i];
2627226344
field.ty = try field_ty.copy(decl_arena_allocator);
2627326345

26274-
if (struct_obj.layout == .Extern and !(try sema.validateExternType(field.ty, .other))) {
26346+
if (field_ty.zigTypeTag() == .Opaque) {
26347+
const msg = msg: {
26348+
const tree = try sema.getAstTree(&block_scope);
26349+
const field_src = enumFieldSrcLoc(decl, tree.*, struct_obj.node_offset, i);
26350+
const msg = try sema.errMsg(&block_scope, field_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{});
26351+
errdefer msg.destroy(sema.gpa);
26352+
26353+
try sema.addDeclaredHereNote(msg, field_ty);
26354+
break :msg msg;
26355+
};
26356+
return sema.failWithOwnedErrorMsg(&block_scope, msg);
26357+
}
26358+
if (struct_obj.layout == .Extern and !sema.validateExternType(field.ty, .other)) {
2627526359
const msg = msg: {
2627626360
const tree = try sema.getAstTree(&block_scope);
2627726361
const fields_src = enumFieldSrcLoc(decl, tree.*, struct_obj.node_offset, i);
2627826362
const msg = try sema.errMsg(&block_scope, fields_src, "extern structs cannot contain fields of type '{}'", .{field.ty.fmt(sema.mod)});
2627926363
errdefer msg.destroy(sema.gpa);
2628026364

26281-
try sema.explainWhyTypeIsNotExtern(&block_scope, fields_src, msg, fields_src.toSrcLoc(decl), field.ty, .other);
26365+
try sema.explainWhyTypeIsNotExtern(msg, fields_src.toSrcLoc(decl), field.ty, .other);
2628226366

2628326367
try sema.addDeclaredHereNote(msg, field.ty);
2628426368
break :msg msg;
2628526369
};
2628626370
return sema.failWithOwnedErrorMsg(&block_scope, msg);
26287-
}
26288-
if (field_ty.zigTypeTag() == .Opaque) {
26371+
} else if (struct_obj.layout == .Packed and !(validatePackedType(field.ty))) {
2628926372
const msg = msg: {
2629026373
const tree = try sema.getAstTree(&block_scope);
26291-
const field_src = enumFieldSrcLoc(decl, tree.*, struct_obj.node_offset, i);
26292-
const msg = try sema.errMsg(&block_scope, field_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{});
26374+
const fields_src = enumFieldSrcLoc(decl, tree.*, struct_obj.node_offset, i);
26375+
const msg = try sema.errMsg(&block_scope, fields_src, "packed structs cannot contain fields of type '{}'", .{field.ty.fmt(sema.mod)});
2629326376
errdefer msg.destroy(sema.gpa);
2629426377

26295-
try sema.addDeclaredHereNote(msg, field_ty);
26378+
try sema.explainWhyTypeIsNotPacked(msg, fields_src.toSrcLoc(decl), field.ty);
26379+
26380+
try sema.addDeclaredHereNote(msg, field.ty);
2629626381
break :msg msg;
2629726382
};
2629826383
return sema.failWithOwnedErrorMsg(&block_scope, msg);
@@ -26595,27 +26680,40 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
2659526680
}
2659626681
}
2659726682

26598-
if (union_obj.layout == .Extern and !(try sema.validateExternType(field_ty, .union_field))) {
26683+
if (field_ty.zigTypeTag() == .Opaque) {
2659926684
const msg = msg: {
2660026685
const tree = try sema.getAstTree(&block_scope);
2660126686
const field_src = enumFieldSrcLoc(decl, tree.*, union_obj.node_offset, field_i);
26602-
const msg = try sema.errMsg(&block_scope, field_src, "extern unions cannot contain fields of type '{}'", .{field_ty.fmt(sema.mod)});
26687+
const msg = try sema.errMsg(&block_scope, field_src, "opaque types have unknown size and therefore cannot be directly embedded in unions", .{});
2660326688
errdefer msg.destroy(sema.gpa);
2660426689

26605-
try sema.explainWhyTypeIsNotExtern(&block_scope, field_src, msg, field_src.toSrcLoc(decl), field_ty, .union_field);
26606-
2660726690
try sema.addDeclaredHereNote(msg, field_ty);
2660826691
break :msg msg;
2660926692
};
2661026693
return sema.failWithOwnedErrorMsg(&block_scope, msg);
2661126694
}
26612-
if (field_ty.zigTypeTag() == .Opaque) {
26695+
if (union_obj.layout == .Extern and !sema.validateExternType(field_ty, .union_field)) {
2661326696
const msg = msg: {
2661426697
const tree = try sema.getAstTree(&block_scope);
2661526698
const field_src = enumFieldSrcLoc(decl, tree.*, union_obj.node_offset, field_i);
26616-
const msg = try sema.errMsg(&block_scope, field_src, "opaque types have unknown size and therefore cannot be directly embedded in unions", .{});
26699+
const msg = try sema.errMsg(&block_scope, field_src, "extern unions cannot contain fields of type '{}'", .{field_ty.fmt(sema.mod)});
2661726700
errdefer msg.destroy(sema.gpa);
2661826701

26702+
try sema.explainWhyTypeIsNotExtern(msg, field_src.toSrcLoc(decl), field_ty, .union_field);
26703+
26704+
try sema.addDeclaredHereNote(msg, field_ty);
26705+
break :msg msg;
26706+
};
26707+
return sema.failWithOwnedErrorMsg(&block_scope, msg);
26708+
} else if (union_obj.layout == .Packed and !(validatePackedType(field_ty))) {
26709+
const msg = msg: {
26710+
const tree = try sema.getAstTree(&block_scope);
26711+
const fields_src = enumFieldSrcLoc(decl, tree.*, union_obj.node_offset, field_i);
26712+
const msg = try sema.errMsg(&block_scope, fields_src, "packed unions cannot contain fields of type '{}'", .{field_ty.fmt(sema.mod)});
26713+
errdefer msg.destroy(sema.gpa);
26714+
26715+
try sema.explainWhyTypeIsNotPacked(msg, fields_src.toSrcLoc(decl), field_ty);
26716+
2661926717
try sema.addDeclaredHereNote(msg, field_ty);
2662026718
break :msg msg;
2662126719
};

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)