Skip to content

Commit e017889

Browse files
authored
Merge pull request #12379 from ifreund/packed-struct-explicit-backing-int
stage2: Implement explicit backing integers for packed structs
2 parents 45c444f + 0d32b73 commit e017889

22 files changed

+565
-113
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: 50 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) {
@@ -4223,15 +4224,18 @@ fn structDeclInner(
42234224
node: Ast.Node.Index,
42244225
container_decl: Ast.full.ContainerDecl,
42254226
layout: std.builtin.Type.ContainerLayout,
4227+
backing_int_node: Ast.Node.Index,
42264228
) InnerError!Zir.Inst.Ref {
42274229
const decl_inst = try gz.reserveInstructionIndex();
42284230

4229-
if (container_decl.ast.members.len == 0) {
4231+
if (container_decl.ast.members.len == 0 and backing_int_node == 0) {
42304232
try gz.setStruct(decl_inst, .{
42314233
.src_node = node,
42324234
.layout = layout,
42334235
.fields_len = 0,
42344236
.decls_len = 0,
4237+
.backing_int_ref = .none,
4238+
.backing_int_body_len = 0,
42354239
.known_non_opv = false,
42364240
.known_comptime_only = false,
42374241
});
@@ -4266,6 +4270,35 @@ fn structDeclInner(
42664270
};
42674271
defer block_scope.unstack();
42684272

4273+
const scratch_top = astgen.scratch.items.len;
4274+
defer astgen.scratch.items.len = scratch_top;
4275+
4276+
var backing_int_body_len: usize = 0;
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_len = astgen.scratch.items.len - 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+
42694302
const decl_count = try astgen.scanDecls(&namespace, container_decl.ast.members);
42704303
const field_count = @intCast(u32, container_decl.ast.members.len - decl_count);
42714304

@@ -4378,6 +4411,8 @@ fn structDeclInner(
43784411
.layout = layout,
43794412
.fields_len = field_count,
43804413
.decls_len = decl_count,
4414+
.backing_int_ref = backing_int_ref,
4415+
.backing_int_body_len = @intCast(u32, backing_int_body_len),
43814416
.known_non_opv = known_non_opv,
43824417
.known_comptime_only = known_comptime_only,
43834418
});
@@ -4386,7 +4421,9 @@ fn structDeclInner(
43864421
const decls_slice = wip_members.declsSlice();
43874422
const fields_slice = wip_members.fieldsSlice();
43884423
const bodies_slice = astgen.scratch.items[bodies_start..];
4389-
try astgen.extra.ensureUnusedCapacity(gpa, decls_slice.len + fields_slice.len + bodies_slice.len);
4424+
try astgen.extra.ensureUnusedCapacity(gpa, backing_int_body_len +
4425+
decls_slice.len + fields_slice.len + bodies_slice.len);
4426+
astgen.extra.appendSliceAssumeCapacity(astgen.scratch.items[scratch_top..][0..backing_int_body_len]);
43904427
astgen.extra.appendSliceAssumeCapacity(decls_slice);
43914428
astgen.extra.appendSliceAssumeCapacity(fields_slice);
43924429
astgen.extra.appendSliceAssumeCapacity(bodies_slice);
@@ -4582,9 +4619,7 @@ fn containerDecl(
45824619
else => unreachable,
45834620
} else std.builtin.Type.ContainerLayout.Auto;
45844621

4585-
assert(container_decl.ast.arg == 0);
4586-
4587-
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);
45884623
return rvalue(gz, rl, result, node);
45894624
},
45904625
.keyword_union => {
@@ -11254,14 +11289,16 @@ const GenZir = struct {
1125411289
src_node: Ast.Node.Index,
1125511290
fields_len: u32,
1125611291
decls_len: u32,
11292+
backing_int_ref: Zir.Inst.Ref,
11293+
backing_int_body_len: u32,
1125711294
layout: std.builtin.Type.ContainerLayout,
1125811295
known_non_opv: bool,
1125911296
known_comptime_only: bool,
1126011297
}) !void {
1126111298
const astgen = gz.astgen;
1126211299
const gpa = astgen.gpa;
1126311300

11264-
try astgen.extra.ensureUnusedCapacity(gpa, 4);
11301+
try astgen.extra.ensureUnusedCapacity(gpa, 6);
1126511302
const payload_index = @intCast(u32, astgen.extra.items.len);
1126611303

1126711304
if (args.src_node != 0) {
@@ -11274,6 +11311,12 @@ const GenZir = struct {
1127411311
if (args.decls_len != 0) {
1127511312
astgen.extra.appendAssumeCapacity(args.decls_len);
1127611313
}
11314+
if (args.backing_int_ref != .none) {
11315+
astgen.extra.appendAssumeCapacity(args.backing_int_body_len);
11316+
if (args.backing_int_body_len == 0) {
11317+
astgen.extra.appendAssumeCapacity(@enumToInt(args.backing_int_ref));
11318+
}
11319+
}
1127711320
astgen.instructions.set(inst, .{
1127811321
.tag = .extended,
1127911322
.data = .{ .extended = .{
@@ -11282,6 +11325,7 @@ const GenZir = struct {
1128211325
.has_src_node = args.src_node != 0,
1128311326
.has_fields_len = args.fields_len != 0,
1128411327
.has_decls_len = args.decls_len != 0,
11328+
.has_backing_int = args.backing_int_ref != .none,
1128511329
.known_non_opv = args.known_non_opv,
1128611330
.known_comptime_only = args.known_comptime_only,
1128711331
.name_strategy = gz.anon_name_strategy,

src/Autodoc.zig

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2536,6 +2536,17 @@ fn walkInstruction(
25362536
break :blk decls_len;
25372537
} else 0;
25382538

2539+
// TODO: Expose explicit backing integer types in some way.
2540+
if (small.has_backing_int) {
2541+
const backing_int_body_len = file.zir.extra[extra_index];
2542+
extra_index += 1; // backing_int_body_len
2543+
if (backing_int_body_len == 0) {
2544+
extra_index += 1; // backing_int_ref
2545+
} else {
2546+
extra_index += backing_int_body_len; // backing_int_body_inst
2547+
}
2548+
}
2549+
25392550
var decl_indexes: std.ArrayListUnmanaged(usize) = .{};
25402551
var priv_decl_indexes: std.ArrayListUnmanaged(usize) = .{};
25412552

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)