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.

containerDecl

AstGen.containerDecl
fn containerDecl(
    gz: *GenZir,
    scope: *Scope,
    ri: ResultInfo,
    node: Ast.Node.Index,
    container_decl: Ast.full.ContainerDecl,
    name_strat: Zir.Inst.NameStrategy,
) InnerError!Zir.Inst.Ref

File

lib/std/zig/AstGen.zig:5313

Code

fn containerDecl(
    gz: *GenZir,
    scope: *Scope,
    ri: ResultInfo,
    node: Ast.Node.Index,
    container_decl: Ast.full.ContainerDecl,
    name_strat: Zir.Inst.NameStrategy,
) InnerError!Zir.Inst.Ref {
    const astgen = gz.astgen;
    const gpa = astgen.gpa;
    const tree = astgen.tree;

    const prev_fn_block = astgen.fn_block;
    astgen.fn_block = null;
    defer astgen.fn_block = prev_fn_block;

    // We must not create any types until Sema. Here the goal is only to generate
    // ZIR for all the field types, alignments, and default value expressions.

    switch (tree.tokenTag(container_decl.ast.main_token)) {
        .keyword_struct => {
            const layout: std.lang.Type.ContainerLayout = if (container_decl.layout_token) |t| switch (tree.tokenTag(t)) {
                .keyword_packed => .@"packed",
                .keyword_extern => .@"extern",
                else => unreachable,
            } else .auto;

            const result = try structDeclInner(gz, scope, node, container_decl, layout, container_decl.ast.arg, name_strat);
            return rvalue(gz, ri, result, node);
        },
        .keyword_union => {
            const layout: std.lang.Type.ContainerLayout = if (container_decl.layout_token) |t| switch (tree.tokenTag(t)) {
                .keyword_packed => .@"packed",
                .keyword_extern => .@"extern",
                else => unreachable,
            } else .auto;

            const result = try unionDeclInner(gz, scope, node, container_decl.ast.members, layout, container_decl.ast.arg, container_decl.ast.enum_token, name_strat);
            return rvalue(gz, ri, result, node);
        },
        .keyword_enum => {
            if (container_decl.layout_token) |t| {
                return astgen.failTok(t, "enums do not support 'packed' or 'extern'; instead provide an explicit integer tag type", .{});
            }

            astgen.advanceSourceCursorToNode(node);

            const decl_inst = try gz.reserveInstructionIndex();

            var namespace: Scope.Namespace = .{
                .parent = scope,
                .node = node,
                .inst = decl_inst,
                .declaring_gz = gz,
                .maybe_generic = astgen.within_fn,
            };
            defer namespace.deinit(gpa);

            // The enum_decl instruction introduces a scope in which the decls of the enum
            // are in scope, so that tag values can refer to decls within the enum 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, .@"enum");
            // The name `_` is not actually a field; it marks a non-exhaustive enum.
            const fields_len: u32 = scan_result.fields_len - @intFromBool(scan_result.has_underscore_field);

            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(fields_len);
            const field_value_body_lens = try scratch.addOptionalSlice(scan_result.any_field_values, fields_len);

            const old_hasher = astgen.src_hasher;
            defer astgen.src_hasher = old_hasher;
            astgen.src_hasher = .init(.{});

            // Before any field bodies comes the tag type, if specified.
            const tag_type_body_len: ?u32 = if (container_decl.ast.arg.unwrap()) |tag_type_node| len: {
                astgen.src_hasher.update(astgen.tree.getNodeSource(tag_type_node));
                const type_ref = try typeExpr(&block_scope, &namespace.base, tag_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());
                block_scope.instructions.items.len = block_scope.instructions_top;
                break :len body_len;
            } else null;

            var next_field_idx: u32 = 0;
            var opt_nonexhaustive_node: Ast.Node.OptionalIndex = .none;
            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,
                };
                member.convertToNonTupleLike(astgen.tree);
                if (member.ast.tuple_like) return astgen.failTok(member.ast.main_token, "enum field missing name", .{});
                if (member.comptime_token) |t| return astgen.failTok(t, "enum fields cannot be marked comptime", .{});
                if (member.ast.type_expr.unwrap()) |type_node| {
                    return astgen.failNodeNotes(type_node, "enum fields do not have types", .{}, &.{
                        try astgen.errNoteNode(node, "consider 'union(enum)' here to make it a tagged union", .{}),
                    });
                }
                if (member.ast.align_expr.unwrap()) |n| return astgen.failNode(n, "enum fields cannot be aligned", .{});
                if (mem.eql(u8, tree.tokenSlice(member.ast.main_token), "_")) {
                    // non-exhaustive mark
                    assert(scan_result.has_underscore_field);
                    if (opt_nonexhaustive_node.unwrap()) |prev_node| {
                        return astgen.failNodeNotes(member_node, "redundant non-exhaustive enum mark", .{}, &.{
                            try astgen.errNoteNode(prev_node, "other mark here", .{}),
                        });
                    }
                    if (member.ast.value_expr.unwrap()) |value_node| {
                        return astgen.failNode(value_node, "'_' is used to mark an enum as non-exhaustive and cannot be assigned a value", .{});
                    }
                    if (next_field_idx != fields_len) {
                        return astgen.failNode(member_node, "'_' field of non-exhaustive enum must be last", .{});
                    }
                    if (tag_type_body_len == null) {
                        return astgen.failNodeNotes(node, "non-exhaustive enum missing integer tag type", .{}, &.{
                            try astgen.errNoteNode(member_node, "marked non-exhaustive here", .{}),
                        });
                    }
                    opt_nonexhaustive_node = member_node.toOptional();
                    continue;
                }

                // This is a real field rather than a non-exhaustive mark.
                const field_idx = next_field_idx;
                next_field_idx += 1;

                astgen.src_hasher.update(tree.getNodeSource(member_node));

                field_names.get(astgen)[field_idx] = @backingInt(try astgen.identAsString(member.ast.main_token));

                if (member.ast.value_expr.unwrap()) |value_node| {
                    if (tag_type_body_len == null) {
                        return astgen.failNodeNotes(node, "explicitly valued enum missing integer tag type", .{}, &.{
                            try astgen.errNoteNode(value_node, "tag value specified here", .{}),
                        });
                    }
                    const val_ri: ResultInfo = .{ .rl = .{ .coerced_ty = decl_inst.toRef() } };
                    const value_ref = try expr(&block_scope, &namespace.base, val_ri, value_node);
                    if (!block_scope.endsWithNoReturn()) {
                        _ = try block_scope.addBreak(.break_inline, decl_inst, value_ref);
                    }
                    const body_len = try scratch.appendBodyWithFixups(block_scope.instructionsSlice());
                    field_value_body_lens.?.get(astgen)[field_idx] = body_len;
                    block_scope.instructions.items.len = block_scope.instructions_top;
                } else if (field_value_body_lens) |lens| {
                    lens.get(astgen)[field_idx] = 0;
                }
            }
            assert(scan_result.has_underscore_field == (opt_nonexhaustive_node != .none));
            assert(next_field_idx == fields_len);
            wip_decls.finish();

            var fields_hash: std.zig.SrcHash = undefined;
            astgen.src_hasher.final(&fields_hash);

            try gz.setEnum(decl_inst, .{
                .src_node = node,
                .name_strat = name_strat,
                .tag_type_body_len = tag_type_body_len,
                .nonexhaustive = scan_result.has_underscore_field,
                .decls_len = scan_result.decls_len,
                .fields_len = fields_len,
                .any_field_values = scan_result.any_field_values,
                .fields_hash = fields_hash,
                .captures = namespace.captures.keys(),
                .capture_names = namespace.captures.values(),
                .remaining = scratch.all().get(astgen),
            });

            block_scope.unstack();
            return rvalue(gz, ri, decl_inst.toRef(), node);
        },
        .keyword_opaque => {
            assert(container_decl.ast.arg == .none);

            astgen.advanceSourceCursorToNode(node);

            const decl_inst = try gz.reserveInstructionIndex();

            var namespace: Scope.Namespace = .{
                .parent = scope,
                .node = node,
                .inst = decl_inst,
                .declaring_gz = gz,
                .maybe_generic = astgen.within_fn,
            };
            defer namespace.deinit(gpa);

            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, .@"opaque");

            var scratch: Scratch = .init(astgen);
            defer scratch.reset();
            var wip_decls: WipDecls = try .init(&scratch, scan_result.decls_len);

            if (container_decl.layout_token) |layout_token| {
                return astgen.failTok(layout_token, "opaque types do not support 'packed' or 'extern'", .{});
            }

            for (container_decl.ast.members) |member_node| {
                switch (try containerMember(&block_scope, &namespace.base, &wip_decls, member_node)) {
                    .decl => {},
                    .field => return astgen.failNode(member_node, "opaque types cannot have fields", .{}),
                }
            }

            wip_decls.finish();

            try gz.setOpaque(decl_inst, .{
                .src_node = node,
                .name_strat = name_strat,
                .decls_len = scan_result.decls_len,
                .captures = namespace.captures.keys(),
                .capture_names = namespace.captures.values(),
                .decls = @ptrCast(scratch.all().get(astgen)),
            });

            block_scope.unstack();
            return rvalue(gz, ri, decl_inst.toRef(), node);
        },
        else => unreachable,
    }
}