feature. See also
. The project being documented here (as the example) is the Zig library itself.
AstGen.structDeclInner
fn structDeclInner(
gz: *GenZir,
scope: *Scope,
node: Ast.Node.Index,
container_decl: Ast.full.ContainerDecl,
layout: std.lang.Type.ContainerLayout,
maybe_backing_int_node: Ast.Node.OptionalIndex,
name_strat: Zir.Inst.NameStrategy,
) InnerError!Zir.Inst.Ref
File
Code
fn structDeclInner(
gz: *GenZir,
scope: *Scope,
node: Ast.Node.Index,
container_decl: Ast.full.ContainerDecl,
layout: std.lang.Type.ContainerLayout,
maybe_backing_int_node: Ast.Node.OptionalIndex,
name_strat: Zir.Inst.NameStrategy,
) InnerError!Zir.Inst.Ref {
const astgen = gz.astgen;
const gpa = astgen.gpa;
const tree = astgen.tree;
is_tuple: {
const tuple_field_node = for (container_decl.ast.members) |member_node| {
const container_field = tree.fullContainerField(member_node) orelse continue;
if (container_field.ast.tuple_like) break member_node;
} else break :is_tuple;
if (node == .root) {
return astgen.failNode(tuple_field_node, "file cannot be a tuple", .{});
} else {
return tupleDecl(gz, scope, node, container_decl, layout, maybe_backing_int_node);
}
}
astgen.advanceSourceCursorToNode(node);
const decl_inst = try gz.reserveInstructionIndex();
if (container_decl.ast.members.len == 0 and maybe_backing_int_node == .none) {
try gz.setStruct(decl_inst, .{
.src_node = node,
.name_strat = name_strat,
.layout = layout,
.backing_int_type_body_len = null,
.decls_len = 0,
.fields_len = 0,
.any_field_aligns = false,
.any_field_defaults = false,
.any_comptime_fields = false,
.fields_hash = @splat(0),
.captures = &.{},
.capture_names = &.{},
.remaining = &.{},
});
return decl_inst.toRef();
}
var namespace: Scope.Namespace = .{
.parent = scope,
.node = node,
.inst = decl_inst,
.declaring_gz = gz,
.maybe_generic = astgen.within_fn,
};
defer namespace.deinit(gpa);
// are in scope, so that field types, alignments, and default value expressions
// can refer to decls within the struct itself.
var block_scope: GenZir = .{
.parent = &namespace.base,
.decl_node_index = node,
.decl_line = gz.decl_line,
.astgen = astgen,
.is_comptime = true,
.instructions = gz.instructions,
.instructions_top = gz.instructions.items.len,
};
defer block_scope.unstack();
const scan_result = try astgen.scanContainer(&namespace, container_decl.ast.members, .@"struct");
var scratch: Scratch = .init(astgen);
defer scratch.reset();
var wip_decls: WipDecls = try .init(&scratch, scan_result.decls_len);
const field_names = try scratch.addSlice(scan_result.fields_len);
const field_type_body_lens = try scratch.addSlice(scan_result.fields_len);
const field_align_body_lens = try scratch.addOptionalSlice(scan_result.any_field_aligns, scan_result.fields_len);
const field_default_body_lens = try scratch.addOptionalSlice(scan_result.any_field_values, scan_result.fields_len);
const field_comptime_bits = try scratch.addOptionalSlice(
scan_result.any_comptime_fields,
@divCeil(scan_result.fields_len, 32),
);
if (field_comptime_bits) |bits| @memset(bits.get(astgen), 0);
const old_hasher = astgen.src_hasher;
defer astgen.src_hasher = old_hasher;
astgen.src_hasher = .init(.{});
const backing_int_type_body_len: ?u32 = if (maybe_backing_int_node.unwrap()) |backing_int_node| len: {
if (layout != .@"packed") return astgen.failNode(
backing_int_node,
"non-packed struct does not support backing integer type",
.{},
);
astgen.src_hasher.update(astgen.tree.getNodeSource(backing_int_node));
const type_ref = try typeExpr(&block_scope, &namespace.base, backing_int_node);
if (!block_scope.endsWithNoReturn()) {
_ = try block_scope.addBreak(.break_inline, decl_inst, type_ref);
}
const body_len = try scratch.appendBodyWithFixups(block_scope.instructionsSlice());
block_scope.instructions.items.len = block_scope.instructions_top;
break :len body_len;
} else null;
var next_field_idx: u32 = 0;
for (container_decl.ast.members) |member_node| {
var member = switch (try containerMember(&block_scope, &namespace.base, &wip_decls, member_node)) {
.decl => continue,
.field => |field| field,
};
const field_idx = next_field_idx;
next_field_idx += 1;
astgen.src_hasher.update(tree.getNodeSource(member_node));
member.convertToNonTupleLike(astgen.tree);
assert(!member.ast.tuple_like);
field_names.get(astgen)[field_idx] = @backingInt(try astgen.identAsString(member.ast.main_token));
{
const type_node = member.ast.type_expr.unwrap() orelse {
return astgen.failTok(member.ast.main_token, "struct field missing type", .{});
};
const type_ref = try typeExpr(&block_scope, &namespace.base, type_node);
if (!block_scope.endsWithNoReturn()) {
_ = try block_scope.addBreak(.break_inline, decl_inst, type_ref);
}
const body_len = try scratch.appendBodyWithFixups(block_scope.instructionsSlice());
field_type_body_lens.get(astgen)[field_idx] = body_len;
block_scope.instructions.items.len = block_scope.instructions_top;
}
if (member.ast.align_expr.unwrap()) |align_node| {
if (layout == .@"packed") {
return astgen.failNode(align_node, "unable to override alignment of packed struct fields", .{});
}
const align_ref = try expr(&block_scope, &namespace.base, coerced_align_ri, align_node);
if (!block_scope.endsWithNoReturn()) {
_ = try block_scope.addBreak(.break_inline, decl_inst, align_ref);
}
const body_len = try scratch.appendBodyWithFixups(block_scope.instructionsSlice());
field_align_body_lens.?.get(astgen)[field_idx] = body_len;
block_scope.instructions.items.len = block_scope.instructions_top;
} else if (field_align_body_lens) |lens| {
lens.get(astgen)[field_idx] = 0;
}
if (member.ast.value_expr.unwrap()) |default_node| {
const ri: ResultInfo = .{ .rl = .{ .coerced_ty = decl_inst.toRef() } };
const default_ref = try expr(&block_scope, &namespace.base, ri, default_node);
if (!block_scope.endsWithNoReturn()) {
_ = try block_scope.addBreak(.break_inline, decl_inst, default_ref);
}
const body_len = try scratch.appendBodyWithFixups(block_scope.instructionsSlice());
field_default_body_lens.?.get(astgen)[field_idx] = body_len;
block_scope.instructions.items.len = block_scope.instructions_top;
} else if (field_default_body_lens) |lens| {
lens.get(astgen)[field_idx] = 0;
}
if (member.comptime_token) |comptime_token| {
switch (layout) {
.@"packed", .@"extern" => return astgen.failTok(comptime_token, "{s} struct fields cannot be marked comptime", .{@tagName(layout)}),
.auto => {},
}
if (member.ast.value_expr == .none) {
return astgen.failTok(comptime_token, "comptime field without default initialization value", .{});
}
const mask = @as(u32, 1) << @intCast(field_idx % 32);
field_comptime_bits.?.get(astgen)[field_idx / 32] |= mask;
}
}
assert(next_field_idx == scan_result.fields_len);
wip_decls.finish();
var fields_hash: std.zig.SrcHash = undefined;
astgen.src_hasher.final(&fields_hash);
try gz.setStruct(decl_inst, .{
.src_node = node,
.name_strat = name_strat,
.layout = layout,
.backing_int_type_body_len = backing_int_type_body_len,
.decls_len = scan_result.decls_len,
.fields_len = scan_result.fields_len,
.any_field_aligns = scan_result.any_field_aligns,
.any_field_defaults = scan_result.any_field_values,
.any_comptime_fields = scan_result.any_comptime_fields,
.fields_hash = fields_hash,
.captures = namespace.captures.keys(),
.capture_names = namespace.captures.values(),
.remaining = scratch.all().get(astgen),
});
block_scope.unstack();
return decl_inst.toRef();
}