Skip to content

Commit 23f23ac

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 396eb73 commit 23f23ac

16 files changed

+322
-76
lines changed

lib/std/builtin.zig

+2
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

+1-1
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

+9-2
Original file line numberDiff line numberDiff line change
@@ -3356,6 +3356,13 @@ const Parser = struct {
33563356
}
33573357

33583358
/// Caller must have already verified the first token.
3359+
/// ContainerDeclAuto <- ContainerDeclType LBRACE container_doc_comment? ContainerMembers RBRACE
3360+
///
3361+
/// ContainerDeclType
3362+
/// <- KEYWORD_struct (LPAREN Expr RPAREN)?
3363+
/// / KEYWORD_opaque
3364+
/// / KEYWORD_enum (LPAREN Expr RPAREN)?
3365+
/// / KEYWORD_union (LPAREN (KEYWORD_enum (LPAREN Expr RPAREN)? / Expr) RPAREN)?
33593366
/// ContainerDeclType
33603367
/// <- KEYWORD_struct
33613368
/// / KEYWORD_enum (LPAREN Expr RPAREN)?
@@ -3364,8 +3371,8 @@ const Parser = struct {
33643371
fn parseContainerDeclAuto(p: *Parser) !Node.Index {
33653372
const main_token = p.nextToken();
33663373
const arg_expr = switch (p.token_tags[main_token]) {
3367-
.keyword_struct, .keyword_opaque => null_node,
3368-
.keyword_enum => blk: {
3374+
.keyword_opaque => null_node,
3375+
.keyword_struct, .keyword_enum => blk: {
33693376
if (p.eatToken(.l_paren)) |_| {
33703377
const expr = try p.expectExpr();
33713378
_ = try p.expectToken(.r_paren);

lib/std/zig/parser_test.zig

+7
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

+23-4
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) {
@@ -4212,12 +4213,14 @@ fn structDeclInner(
42124213
node: Ast.Node.Index,
42134214
container_decl: Ast.full.ContainerDecl,
42144215
layout: std.builtin.Type.ContainerLayout,
4216+
backing_int_node: Ast.Node.Index,
42154217
) InnerError!Zir.Inst.Ref {
42164218
const decl_inst = try gz.reserveInstructionIndex();
42174219

4218-
if (container_decl.ast.members.len == 0) {
4220+
if (container_decl.ast.members.len == 0 and backing_int_node == 0) {
42194221
try gz.setStruct(decl_inst, .{
42204222
.src_node = node,
4223+
.backing_int = .none,
42214224
.layout = layout,
42224225
.fields_len = 0,
42234226
.decls_len = 0,
@@ -4255,6 +4258,18 @@ fn structDeclInner(
42554258
};
42564259
defer block_scope.unstack();
42574260

4261+
const backing_int_ref: Zir.Inst.Ref = blk: {
4262+
if (backing_int_node != 0) {
4263+
if (layout != .Packed) {
4264+
return astgen.failNode(backing_int_node, "non-packed struct does not support backing integer type", .{});
4265+
} else {
4266+
break :blk try typeExpr(&block_scope, &namespace.base, backing_int_node);
4267+
}
4268+
} else {
4269+
break :blk .none;
4270+
}
4271+
};
4272+
42584273
const decl_count = try astgen.scanDecls(&namespace, container_decl.ast.members);
42594274
const field_count = @intCast(u32, container_decl.ast.members.len - decl_count);
42604275

@@ -4364,6 +4379,7 @@ fn structDeclInner(
43644379

43654380
try gz.setStruct(decl_inst, .{
43664381
.src_node = node,
4382+
.backing_int = backing_int_ref,
43674383
.layout = layout,
43684384
.fields_len = field_count,
43694385
.decls_len = decl_count,
@@ -4571,9 +4587,7 @@ fn containerDecl(
45714587
else => unreachable,
45724588
} else std.builtin.Type.ContainerLayout.Auto;
45734589

4574-
assert(container_decl.ast.arg == 0);
4575-
4576-
const result = try structDeclInner(gz, scope, node, container_decl, layout);
4590+
const result = try structDeclInner(gz, scope, node, container_decl, layout, container_decl.ast.arg);
45774591
return rvalue(gz, rl, result, node);
45784592
},
45794593
.keyword_union => {
@@ -11156,6 +11170,7 @@ const GenZir = struct {
1115611170

1115711171
fn setStruct(gz: *GenZir, inst: Zir.Inst.Index, args: struct {
1115811172
src_node: Ast.Node.Index,
11173+
backing_int: Zir.Inst.Ref,
1115911174
fields_len: u32,
1116011175
decls_len: u32,
1116111176
layout: std.builtin.Type.ContainerLayout,
@@ -11172,6 +11187,9 @@ const GenZir = struct {
1117211187
const node_offset = gz.nodeIndexToRelative(args.src_node);
1117311188
astgen.extra.appendAssumeCapacity(@bitCast(u32, node_offset));
1117411189
}
11190+
if (args.backing_int != .none) {
11191+
astgen.extra.appendAssumeCapacity(@enumToInt(args.backing_int));
11192+
}
1117511193
if (args.fields_len != 0) {
1117611194
astgen.extra.appendAssumeCapacity(args.fields_len);
1117711195
}
@@ -11184,6 +11202,7 @@ const GenZir = struct {
1118411202
.opcode = .struct_decl,
1118511203
.small = @bitCast(u16, Zir.Inst.StructDecl.Small{
1118611204
.has_src_node = args.src_node != 0,
11205+
.has_backing_int = args.backing_int != .none,
1118711206
.has_fields_len = args.fields_len != 0,
1118811207
.has_decls_len = args.decls_len != 0,
1118911208
.known_non_opv = args.known_non_opv,

src/Module.zig

+6-13
Original file line numberDiff line numberDiff line change
@@ -899,6 +899,11 @@ pub const Struct = struct {
899899
zir_index: Zir.Inst.Index,
900900

901901
layout: std.builtin.Type.ContainerLayout,
902+
/// If the layout is not packed, this is the noreturn type.
903+
/// If the layout is packed, this is the backing integer type of the packed struct.
904+
/// Whether zig chooses this type or the user specifies it, it is stored here.
905+
/// This will be set to the noreturn type until status is `have_field_types`.
906+
backing_int_ty: Type = Type.initTag(.noreturn),
902907
status: enum {
903908
none,
904909
field_types_wip,
@@ -1037,19 +1042,7 @@ pub const Struct = struct {
10371042
}
10381043
bit_sum += field.ty.bitSize(target);
10391044
}
1040-
return @intCast(u16, bit_sum);
1041-
}
1042-
1043-
pub fn packedIntegerBits(s: Struct, target: Target) u16 {
1044-
return s.packedFieldBitOffset(target, s.fields.count());
1045-
}
1046-
1047-
pub fn packedIntegerType(s: Struct, target: Target, buf: *Type.Payload.Bits) Type {
1048-
buf.* = .{
1049-
.base = .{ .tag = .int_unsigned },
1050-
.data = s.packedIntegerBits(target),
1051-
};
1052-
return Type.initPayload(&buf.base);
1045+
unreachable; // index out of bounds
10531046
}
10541047
};
10551048

0 commit comments

Comments
 (0)