Skip to content

Commit b1acd4a

Browse files
committed
stage2: Implement explicit backing integers for packed structs
Now the backing integer of a packed struct type may be explicitly specified with e.g. `packed struct(u32) { ... }`.
1 parent b8330ea commit b1acd4a

16 files changed

+449
-71
lines changed

lib/std/builtin.zig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,8 @@ pub const Type = union(enum) {
294294
/// therefore must be kept in sync with the compiler implementation.
295295
pub const Struct = struct {
296296
layout: ContainerLayout,
297+
/// Only valid if layout is .Packed
298+
backing_integer: ?type = null,
297299
fields: []const StructField,
298300
decls: []const Declaration,
299301
is_tuple: bool,

lib/std/zig/Ast.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2967,7 +2967,7 @@ pub const Node = struct {
29672967
/// Same as ContainerDeclTwo except there is known to be a trailing comma
29682968
/// or semicolon before the rbrace.
29692969
container_decl_two_trailing,
2970-
/// `union(lhs)` / `enum(lhs)`. `SubRange[rhs]`.
2970+
/// `struct(lhs)` / `union(lhs)` / `enum(lhs)`. `SubRange[rhs]`.
29712971
container_decl_arg,
29722972
/// Same as container_decl_arg but there is known to be a trailing
29732973
/// comma or semicolon before the rbrace.

lib/std/zig/parse.zig

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3356,16 +3356,18 @@ const Parser = struct {
33563356
}
33573357

33583358
/// Caller must have already verified the first token.
3359+
/// ContainerDeclAuto <- ContainerDeclType LBRACE container_doc_comment? ContainerMembers RBRACE
3360+
///
33593361
/// ContainerDeclType
3360-
/// <- KEYWORD_struct
3362+
/// <- KEYWORD_struct (LPAREN Expr RPAREN)?
3363+
/// / KEYWORD_opaque
33613364
/// / KEYWORD_enum (LPAREN Expr RPAREN)?
33623365
/// / KEYWORD_union (LPAREN (KEYWORD_enum (LPAREN Expr RPAREN)? / Expr) RPAREN)?
3363-
/// / KEYWORD_opaque
33643366
fn parseContainerDeclAuto(p: *Parser) !Node.Index {
33653367
const main_token = p.nextToken();
33663368
const arg_expr = switch (p.token_tags[main_token]) {
3367-
.keyword_struct, .keyword_opaque => null_node,
3368-
.keyword_enum => blk: {
3369+
.keyword_opaque => null_node,
3370+
.keyword_struct, .keyword_enum => blk: {
33693371
if (p.eatToken(.l_paren)) |_| {
33703372
const expr = try p.expectExpr();
33713373
_ = try p.expectToken(.r_paren);

lib/std/zig/parser_test.zig

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3064,6 +3064,13 @@ test "zig fmt: struct declaration" {
30643064
\\ c: u8,
30653065
\\};
30663066
\\
3067+
\\const Ps = packed struct(u32) {
3068+
\\ a: u1,
3069+
\\ b: u2,
3070+
\\
3071+
\\ c: u29,
3072+
\\};
3073+
\\
30673074
\\const Es = extern struct {
30683075
\\ a: u8,
30693076
\\ b: u8,

src/AstGen.zig

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir {
152152
0,
153153
tree.containerDeclRoot(),
154154
.Auto,
155+
0,
155156
)) |struct_decl_ref| {
156157
assert(refToIndex(struct_decl_ref).? == 0);
157158
} else |err| switch (err) {
@@ -4224,15 +4225,18 @@ fn structDeclInner(
42244225
node: Ast.Node.Index,
42254226
container_decl: Ast.full.ContainerDecl,
42264227
layout: std.builtin.Type.ContainerLayout,
4228+
backing_int_node: Ast.Node.Index,
42274229
) InnerError!Zir.Inst.Ref {
42284230
const decl_inst = try gz.reserveInstructionIndex();
42294231

4230-
if (container_decl.ast.members.len == 0) {
4232+
if (container_decl.ast.members.len == 0 and backing_int_node == 0) {
42314233
try gz.setStruct(decl_inst, .{
42324234
.src_node = node,
42334235
.layout = layout,
42344236
.fields_len = 0,
42354237
.decls_len = 0,
4238+
.backing_int_ref = .none,
4239+
.backing_int_body_len = 0,
42364240
.known_non_opv = false,
42374241
.known_comptime_only = false,
42384242
});
@@ -4267,6 +4271,34 @@ fn structDeclInner(
42674271
};
42684272
defer block_scope.unstack();
42694273

4274+
const scratch_top = astgen.scratch.items.len;
4275+
var backing_int_body_inst: []Zir.Inst.Index = &[0]Zir.Inst.Index{};
4276+
defer astgen.scratch.items.len = scratch_top;
4277+
const backing_int_ref: Zir.Inst.Ref = blk: {
4278+
if (backing_int_node != 0) {
4279+
if (layout != .Packed) {
4280+
return astgen.failNode(backing_int_node, "non-packed struct does not support backing integer type", .{});
4281+
} else {
4282+
const backing_int_ref = try typeExpr(&block_scope, &namespace.base, backing_int_node);
4283+
if (!block_scope.isEmpty()) {
4284+
if (!block_scope.endsWithNoReturn()) {
4285+
_ = try block_scope.addBreak(.break_inline, decl_inst, backing_int_ref);
4286+
}
4287+
4288+
const body = block_scope.instructionsSlice();
4289+
const old_scratch_len = astgen.scratch.items.len;
4290+
try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body));
4291+
appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body);
4292+
backing_int_body_inst = astgen.scratch.items[old_scratch_len..];
4293+
block_scope.instructions.items.len = block_scope.instructions_top;
4294+
}
4295+
break :blk backing_int_ref;
4296+
}
4297+
} else {
4298+
break :blk .none;
4299+
}
4300+
};
4301+
42704302
const decl_count = try astgen.scanDecls(&namespace, container_decl.ast.members);
42714303
const field_count = @intCast(u32, container_decl.ast.members.len - decl_count);
42724304

@@ -4379,6 +4411,8 @@ fn structDeclInner(
43794411
.layout = layout,
43804412
.fields_len = field_count,
43814413
.decls_len = decl_count,
4414+
.backing_int_ref = backing_int_ref,
4415+
.backing_int_body_len = @intCast(u32, backing_int_body_inst.len),
43824416
.known_non_opv = known_non_opv,
43834417
.known_comptime_only = known_comptime_only,
43844418
});
@@ -4387,7 +4421,9 @@ fn structDeclInner(
43874421
const decls_slice = wip_members.declsSlice();
43884422
const fields_slice = wip_members.fieldsSlice();
43894423
const bodies_slice = astgen.scratch.items[bodies_start..];
4390-
try astgen.extra.ensureUnusedCapacity(gpa, decls_slice.len + fields_slice.len + bodies_slice.len);
4424+
try astgen.extra.ensureUnusedCapacity(gpa, backing_int_body_inst.len +
4425+
decls_slice.len + fields_slice.len + bodies_slice.len);
4426+
astgen.extra.appendSliceAssumeCapacity(backing_int_body_inst);
43914427
astgen.extra.appendSliceAssumeCapacity(decls_slice);
43924428
astgen.extra.appendSliceAssumeCapacity(fields_slice);
43934429
astgen.extra.appendSliceAssumeCapacity(bodies_slice);
@@ -4583,9 +4619,7 @@ fn containerDecl(
45834619
else => unreachable,
45844620
} else std.builtin.Type.ContainerLayout.Auto;
45854621

4586-
assert(container_decl.ast.arg == 0);
4587-
4588-
const result = try structDeclInner(gz, scope, node, container_decl, layout);
4622+
const result = try structDeclInner(gz, scope, node, container_decl, layout, container_decl.ast.arg);
45894623
return rvalue(gz, rl, result, node);
45904624
},
45914625
.keyword_union => {
@@ -11234,14 +11268,16 @@ const GenZir = struct {
1123411268
src_node: Ast.Node.Index,
1123511269
fields_len: u32,
1123611270
decls_len: u32,
11271+
backing_int_ref: Zir.Inst.Ref,
11272+
backing_int_body_len: u32,
1123711273
layout: std.builtin.Type.ContainerLayout,
1123811274
known_non_opv: bool,
1123911275
known_comptime_only: bool,
1124011276
}) !void {
1124111277
const astgen = gz.astgen;
1124211278
const gpa = astgen.gpa;
1124311279

11244-
try astgen.extra.ensureUnusedCapacity(gpa, 4);
11280+
try astgen.extra.ensureUnusedCapacity(gpa, 6);
1124511281
const payload_index = @intCast(u32, astgen.extra.items.len);
1124611282

1124711283
if (args.src_node != 0) {
@@ -11254,6 +11290,12 @@ const GenZir = struct {
1125411290
if (args.decls_len != 0) {
1125511291
astgen.extra.appendAssumeCapacity(args.decls_len);
1125611292
}
11293+
if (args.backing_int_ref != .none) {
11294+
astgen.extra.appendAssumeCapacity(args.backing_int_body_len);
11295+
if (args.backing_int_body_len == 0) {
11296+
astgen.extra.appendAssumeCapacity(@enumToInt(args.backing_int_ref));
11297+
}
11298+
}
1125711299
astgen.instructions.set(inst, .{
1125811300
.tag = .extended,
1125911301
.data = .{ .extended = .{
@@ -11262,6 +11304,7 @@ const GenZir = struct {
1126211304
.has_src_node = args.src_node != 0,
1126311305
.has_fields_len = args.fields_len != 0,
1126411306
.has_decls_len = args.decls_len != 0,
11307+
.has_backing_int = args.backing_int_ref != .none,
1126511308
.known_non_opv = args.known_non_opv,
1126611309
.known_comptime_only = args.known_comptime_only,
1126711310
.name_strategy = gz.anon_name_strategy,

src/Module.zig

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -895,6 +895,11 @@ pub const Struct = struct {
895895
zir_index: Zir.Inst.Index,
896896

897897
layout: std.builtin.Type.ContainerLayout,
898+
/// If the layout is not packed, this is the noreturn type.
899+
/// If the layout is packed, this is the backing integer type of the packed struct.
900+
/// Whether zig chooses this type or the user specifies it, it is stored here.
901+
/// This will be set to the noreturn type until status is `have_layout`.
902+
backing_int_ty: Type = Type.initTag(.noreturn),
898903
status: enum {
899904
none,
900905
field_types_wip,
@@ -1025,27 +1030,15 @@ pub const Struct = struct {
10251030

10261031
pub fn packedFieldBitOffset(s: Struct, target: Target, index: usize) u16 {
10271032
assert(s.layout == .Packed);
1028-
assert(s.haveFieldTypes());
1033+
assert(s.haveLayout());
10291034
var bit_sum: u64 = 0;
10301035
for (s.fields.values()) |field, i| {
10311036
if (i == index) {
10321037
return @intCast(u16, bit_sum);
10331038
}
10341039
bit_sum += field.ty.bitSize(target);
10351040
}
1036-
return @intCast(u16, bit_sum);
1037-
}
1038-
1039-
pub fn packedIntegerBits(s: Struct, target: Target) u16 {
1040-
return s.packedFieldBitOffset(target, s.fields.count());
1041-
}
1042-
1043-
pub fn packedIntegerType(s: Struct, target: Target, buf: *Type.Payload.Bits) Type {
1044-
buf.* = .{
1045-
.base = .{ .tag = .int_unsigned },
1046-
.data = s.packedIntegerBits(target),
1047-
};
1048-
return Type.initPayload(&buf.base);
1041+
unreachable; // index out of bounds
10491042
}
10501043
};
10511044

0 commit comments

Comments
 (0)