Zig 0.17.0-dev (Split by item)

This is an example of documentation generated by ZigDoc, an alternative to Zig's built-in Auto Doc feature. See also examples in other modes/formats. The project being documented here (as the example) is the Zig library itself.

structDeclInner

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

lib/std/zig/AstGen.zig:4820

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);

    // The struct_decl instruction introduces a scope in which the decls of the struct
    // 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();

    // Replicate the structure of the ZIR trailing data in `scratch`
    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(.{});

    // Before any field bodies comes the backing int type, if specified.
    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();
}