Skip to content

stage2: Implement explicit backing integers for packed structs #12379

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lib/std/builtin.zig
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,8 @@ pub const Type = union(enum) {
/// therefore must be kept in sync with the compiler implementation.
pub const Struct = struct {
layout: ContainerLayout,
/// Only valid if layout is .Packed
backing_integer: ?type = null,
fields: []const StructField,
decls: []const Declaration,
is_tuple: bool,
Expand Down
2 changes: 1 addition & 1 deletion lib/std/zig/Ast.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2967,7 +2967,7 @@ pub const Node = struct {
/// Same as ContainerDeclTwo except there is known to be a trailing comma
/// or semicolon before the rbrace.
container_decl_two_trailing,
/// `union(lhs)` / `enum(lhs)`. `SubRange[rhs]`.
/// `struct(lhs)` / `union(lhs)` / `enum(lhs)`. `SubRange[rhs]`.
container_decl_arg,
/// Same as container_decl_arg but there is known to be a trailing
/// comma or semicolon before the rbrace.
Expand Down
10 changes: 6 additions & 4 deletions lib/std/zig/parse.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3356,16 +3356,18 @@ const Parser = struct {
}

/// Caller must have already verified the first token.
/// ContainerDeclAuto <- ContainerDeclType LBRACE container_doc_comment? ContainerMembers RBRACE
///
/// ContainerDeclType
/// <- KEYWORD_struct
/// <- KEYWORD_struct (LPAREN Expr RPAREN)?
/// / KEYWORD_opaque
/// / KEYWORD_enum (LPAREN Expr RPAREN)?
/// / KEYWORD_union (LPAREN (KEYWORD_enum (LPAREN Expr RPAREN)? / Expr) RPAREN)?
/// / KEYWORD_opaque
fn parseContainerDeclAuto(p: *Parser) !Node.Index {
const main_token = p.nextToken();
const arg_expr = switch (p.token_tags[main_token]) {
.keyword_struct, .keyword_opaque => null_node,
.keyword_enum => blk: {
.keyword_opaque => null_node,
.keyword_struct, .keyword_enum => blk: {
if (p.eatToken(.l_paren)) |_| {
const expr = try p.expectExpr();
_ = try p.expectToken(.r_paren);
Expand Down
7 changes: 7 additions & 0 deletions lib/std/zig/parser_test.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3064,6 +3064,13 @@ test "zig fmt: struct declaration" {
\\ c: u8,
\\};
\\
\\const Ps = packed struct(u32) {
\\ a: u1,
\\ b: u2,
\\
\\ c: u29,
\\};
\\
\\const Es = extern struct {
\\ a: u8,
\\ b: u8,
Expand Down
56 changes: 50 additions & 6 deletions src/AstGen.zig
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir {
0,
tree.containerDeclRoot(),
.Auto,
0,
)) |struct_decl_ref| {
assert(refToIndex(struct_decl_ref).? == 0);
} else |err| switch (err) {
Expand Down Expand Up @@ -4223,15 +4224,18 @@ fn structDeclInner(
node: Ast.Node.Index,
container_decl: Ast.full.ContainerDecl,
layout: std.builtin.Type.ContainerLayout,
backing_int_node: Ast.Node.Index,
) InnerError!Zir.Inst.Ref {
const decl_inst = try gz.reserveInstructionIndex();

if (container_decl.ast.members.len == 0) {
if (container_decl.ast.members.len == 0 and backing_int_node == 0) {
try gz.setStruct(decl_inst, .{
.src_node = node,
.layout = layout,
.fields_len = 0,
.decls_len = 0,
.backing_int_ref = .none,
.backing_int_body_len = 0,
.known_non_opv = false,
.known_comptime_only = false,
});
Expand Down Expand Up @@ -4266,6 +4270,35 @@ fn structDeclInner(
};
defer block_scope.unstack();

const scratch_top = astgen.scratch.items.len;
defer astgen.scratch.items.len = scratch_top;

var backing_int_body_len: usize = 0;
const backing_int_ref: Zir.Inst.Ref = blk: {
if (backing_int_node != 0) {
if (layout != .Packed) {
return astgen.failNode(backing_int_node, "non-packed struct does not support backing integer type", .{});
} else {
const backing_int_ref = try typeExpr(&block_scope, &namespace.base, backing_int_node);
if (!block_scope.isEmpty()) {
if (!block_scope.endsWithNoReturn()) {
_ = try block_scope.addBreak(.break_inline, decl_inst, backing_int_ref);
}

const body = block_scope.instructionsSlice();
const old_scratch_len = astgen.scratch.items.len;
try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body));
appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body);
backing_int_body_len = astgen.scratch.items.len - old_scratch_len;
block_scope.instructions.items.len = block_scope.instructions_top;
}
break :blk backing_int_ref;
}
} else {
break :blk .none;
}
};

const decl_count = try astgen.scanDecls(&namespace, container_decl.ast.members);
const field_count = @intCast(u32, container_decl.ast.members.len - decl_count);

Expand Down Expand Up @@ -4378,6 +4411,8 @@ fn structDeclInner(
.layout = layout,
.fields_len = field_count,
.decls_len = decl_count,
.backing_int_ref = backing_int_ref,
.backing_int_body_len = @intCast(u32, backing_int_body_len),
.known_non_opv = known_non_opv,
.known_comptime_only = known_comptime_only,
});
Expand All @@ -4386,7 +4421,9 @@ fn structDeclInner(
const decls_slice = wip_members.declsSlice();
const fields_slice = wip_members.fieldsSlice();
const bodies_slice = astgen.scratch.items[bodies_start..];
try astgen.extra.ensureUnusedCapacity(gpa, decls_slice.len + fields_slice.len + bodies_slice.len);
try astgen.extra.ensureUnusedCapacity(gpa, backing_int_body_len +
decls_slice.len + fields_slice.len + bodies_slice.len);
astgen.extra.appendSliceAssumeCapacity(astgen.scratch.items[scratch_top..][0..backing_int_body_len]);
astgen.extra.appendSliceAssumeCapacity(decls_slice);
astgen.extra.appendSliceAssumeCapacity(fields_slice);
astgen.extra.appendSliceAssumeCapacity(bodies_slice);
Expand Down Expand Up @@ -4582,9 +4619,7 @@ fn containerDecl(
else => unreachable,
} else std.builtin.Type.ContainerLayout.Auto;

assert(container_decl.ast.arg == 0);

const result = try structDeclInner(gz, scope, node, container_decl, layout);
const result = try structDeclInner(gz, scope, node, container_decl, layout, container_decl.ast.arg);
return rvalue(gz, rl, result, node);
},
.keyword_union => {
Expand Down Expand Up @@ -11254,14 +11289,16 @@ const GenZir = struct {
src_node: Ast.Node.Index,
fields_len: u32,
decls_len: u32,
backing_int_ref: Zir.Inst.Ref,
backing_int_body_len: u32,
layout: std.builtin.Type.ContainerLayout,
known_non_opv: bool,
known_comptime_only: bool,
}) !void {
const astgen = gz.astgen;
const gpa = astgen.gpa;

try astgen.extra.ensureUnusedCapacity(gpa, 4);
try astgen.extra.ensureUnusedCapacity(gpa, 6);
const payload_index = @intCast(u32, astgen.extra.items.len);

if (args.src_node != 0) {
Expand All @@ -11274,6 +11311,12 @@ const GenZir = struct {
if (args.decls_len != 0) {
astgen.extra.appendAssumeCapacity(args.decls_len);
}
if (args.backing_int_ref != .none) {
astgen.extra.appendAssumeCapacity(args.backing_int_body_len);
if (args.backing_int_body_len == 0) {
astgen.extra.appendAssumeCapacity(@enumToInt(args.backing_int_ref));
}
}
astgen.instructions.set(inst, .{
.tag = .extended,
.data = .{ .extended = .{
Expand All @@ -11282,6 +11325,7 @@ const GenZir = struct {
.has_src_node = args.src_node != 0,
.has_fields_len = args.fields_len != 0,
.has_decls_len = args.decls_len != 0,
.has_backing_int = args.backing_int_ref != .none,
.known_non_opv = args.known_non_opv,
.known_comptime_only = args.known_comptime_only,
.name_strategy = gz.anon_name_strategy,
Expand Down
11 changes: 11 additions & 0 deletions src/Autodoc.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2536,6 +2536,17 @@ fn walkInstruction(
break :blk decls_len;
} else 0;

// TODO: Expose explicit backing integer types in some way.
if (small.has_backing_int) {
const backing_int_body_len = file.zir.extra[extra_index];
extra_index += 1; // backing_int_body_len
if (backing_int_body_len == 0) {
extra_index += 1; // backing_int_ref
} else {
extra_index += backing_int_body_len; // backing_int_body_inst
}
}

var decl_indexes: std.ArrayListUnmanaged(usize) = .{};
var priv_decl_indexes: std.ArrayListUnmanaged(usize) = .{};

Expand Down
21 changes: 7 additions & 14 deletions src/Module.zig
Original file line number Diff line number Diff line change
Expand Up @@ -895,6 +895,11 @@ pub const Struct = struct {
zir_index: Zir.Inst.Index,

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

pub fn packedFieldBitOffset(s: Struct, target: Target, index: usize) u16 {
assert(s.layout == .Packed);
assert(s.haveFieldTypes());
assert(s.haveLayout());
var bit_sum: u64 = 0;
for (s.fields.values()) |field, i| {
if (i == index) {
return @intCast(u16, bit_sum);
}
bit_sum += field.ty.bitSize(target);
}
return @intCast(u16, bit_sum);
}

pub fn packedIntegerBits(s: Struct, target: Target) u16 {
return s.packedFieldBitOffset(target, s.fields.count());
}

pub fn packedIntegerType(s: Struct, target: Target, buf: *Type.Payload.Bits) Type {
buf.* = .{
.base = .{ .tag = .int_unsigned },
.data = s.packedIntegerBits(target),
};
return Type.initPayload(&buf.base);
unreachable; // index out of bounds
}
};

Expand Down
Loading