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.

transRecordDecl

Translator.transRecordDecl
fn transRecordDecl(t: *Translator, scope: *Scope, record_qt: QualType) Error!void

File

lib/compiler/translate-c/Translator.zig:550

Code

fn transRecordDecl(t: *Translator, scope: *Scope, record_qt: QualType) Error!void {
    const base = record_qt.base(t.comp);
    const record_ty = switch (base.type) {
        .@"struct", .@"union" => |record_ty| record_ty,
        else => unreachable,
    };

    if (t.type_decls.get(record_ty.decl_node)) |_|
        return; // Avoid processing this decl twice

    const toplevel = scope.id == .root;
    const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(t) else undefined;

    const container_kind: ZigTag = if (base.type == .@"union") .@"union" else .@"struct";
    const container_kind_name = @tagName(container_kind);

    var bare_name = record_ty.name.lookup(t.comp);
    var is_unnamed = false;
    var name = bare_name;

    if (t.unnamed_typedefs.get(base.qt)) |typedef_name| {
        bare_name = typedef_name;
        name = typedef_name;
    } else {
        if (record_ty.isAnonymous(t.comp)) {
            bare_name = try std.fmt.allocPrint(t.arena, "unnamed_{d}", .{t.getMangle()});
            is_unnamed = true;
        }
        name = try std.fmt.allocPrint(t.arena, "{s}_{s}", .{ container_kind_name, bare_name });
        if (toplevel and !is_unnamed) {
            name = try t.mangleWeakGlobalName(name);
        }
    }
    if (!toplevel) name = try bs.makeMangledName(name);
    try t.type_decls.putNoClobber(t.gpa, record_ty.decl_node, name);

    const is_pub = toplevel and !is_unnamed;
    const init_node = init: {
        if (record_ty.layout == null) {
            try t.opaque_demotes.put(t.gpa, base.qt, {});
            break :init ZigTag.opaque_literal.init();
        }

        var fields: std.ArrayList(ast.Payload.Container.Field) = .empty;
        defer fields.deinit(t.gpa);
        try fields.ensureUnusedCapacity(t.gpa, record_ty.fields.len);

        var functions: std.ArrayList(ZigNode) = .empty;
        defer functions.deinit(t.gpa);

        var unnamed_field_count: u32 = 0;

        // If a record doesn't have any attributes that would affect the alignment and
        // layout, then we can just use a simple `extern` type. If it does have attributes,
        // then we need to inspect the layout and assign an `align` value for each field.
        const has_alignment_attributes = aligned: {
            if (record_qt.hasAttribute(t.comp, .@"packed")) break :aligned true;
            if (record_qt.hasAttribute(t.comp, .aligned)) break :aligned true;
            for (record_ty.fields) |field| {
                const field_attrs = field.attributes(t.comp);
                for (field_attrs) |field_attr| {
                    switch (field_attr.tag) {
                        .@"packed", .aligned => break :aligned true,
                        else => {},
                    }
                }
            }
            break :aligned false;
        };
        const head_field_alignment: ?c_uint = if (has_alignment_attributes) t.headFieldAlignment(record_ty) else null;

        for (record_ty.fields, 0..) |field, field_index| {
            const field_loc = field.name_tok;

            // Demote record to opaque if it contains a bitfield
            if (field.bit_width != .null) {
                try t.opaque_demotes.put(t.gpa, base.qt, {});
                try t.warn(scope, field_loc, "{s} demoted to opaque type - has bitfield", .{container_kind_name});
                break :init ZigTag.opaque_literal.init();
            }

            var field_name = field.name.lookup(t.comp);
            if (field.name_tok == 0) {
                field_name = try std.fmt.allocPrint(t.arena, "unnamed_{d}", .{unnamed_field_count});
                unnamed_field_count += 1;
                try t.anonymous_record_field_names.put(t.gpa, .{
                    .parent = base.qt,
                    .field = field.qt,
                }, field_name);
            }

            const field_type = field_type: {
                // Check if this is a flexible array member.
                flexible: {
                    if (field_index != record_ty.fields.len - 1 and container_kind != .@"union") break :flexible;
                    const array_ty = field.qt.get(t.comp, .array) orelse break :flexible;
                    if (!t.isFlexibleArrayLen(array_ty.len)) break :flexible;

                    const elem_type = t.transType(scope, array_ty.elem, field_loc) catch |err| switch (err) {
                        error.UnsupportedType => break :flexible,
                        else => |e| return e,
                    };
                    const backing_array_len: usize = switch (array_ty.len) {
                        .fixed => |n| @intCast(n),
                        else => 0,
                    };
                    const backing_array = try ZigTag.array_type.create(t.arena, .{ .len = backing_array_len, .elem_type = elem_type });

                    const member_name = field_name;
                    field_name = try std.fmt.allocPrint(t.arena, "_{s}", .{field_name});

                    const member = try t.createFlexibleMemberFn(member_name, field_name);
                    try functions.append(t.gpa, member);

                    break :field_type backing_array;
                }

                break :field_type t.transType(scope, field.qt, field_loc) catch |err| switch (err) {
                    error.UnsupportedType => {
                        try t.opaque_demotes.put(t.gpa, base.qt, {});
                        try t.warn(scope, field.name_tok, "{s} demoted to opaque type - unable to translate type of field {s}", .{
                            container_kind_name,
                            field_name,
                        });
                        break :init ZigTag.opaque_literal.init();
                    },
                    else => |e| return e,
                };
            };

            // Demote record to opaque if it contains an opaque field
            if (t.typeWasDemotedToOpaque(field.qt)) {
                try t.opaque_demotes.put(t.gpa, base.qt, {});
                try t.warn(scope, field_loc, "{s} demoted to opaque type - has opaque field", .{container_kind_name});
                break :init ZigTag.opaque_literal.init();
            }

            const field_alignment = if (has_alignment_attributes)
                t.alignmentForField(record_ty, head_field_alignment, field_index)
            else
                null;

            // C99 introduced designated initializers for structs. Omitted fields are implicitly
            // initialized to zero. Some C APIs are designed with this in mind. Defaulting to zero
            // values for translated struct fields permits Zig code to comfortably use such an API.
            const default_value = if (t.default_init and container_kind == .@"struct")
                try t.createZeroValueNode(field.qt, field_type, .no_as)
            else
                null;

            fields.appendAssumeCapacity(.{
                .name = field_name,
                .type = field_type,
                .alignment = field_alignment,
                .default_value = default_value,
            });
        }

        // A record is empty if it has no fields or only flexible array fields.
        if (record_ty.fields.len == functions.items.len and
            t.comp.target.os.tag == .windows and t.comp.target.abi == .msvc)
        {
            // In MSVC empty records have the same size as their alignment.
            const padding_bits = record_ty.layout.?.size_bits;
            const alignment_bits = record_ty.layout.?.field_alignment_bits;

            try fields.append(t.gpa, .{
                .name = "_padding",
                .type = try ZigTag.type.create(t.arena, try std.fmt.allocPrint(t.arena, "u{d}", .{padding_bits})),
                .alignment = @divExact(alignment_bits, 8),
                .default_value = if (t.default_init and container_kind == .@"struct")
                    ZigTag.zero_literal.init()
                else
                    null,
            });
        }

        const container_payload = try t.arena.create(ast.Payload.Container);
        container_payload.* = .{
            .base = .{ .tag = container_kind },
            .data = .{
                .layout = .@"extern",
                .fields = try t.arena.dupe(ast.Payload.Container.Field, fields.items),
                .decls = try t.arena.dupe(ZigNode, functions.items),
            },
        };
        break :init ZigNode.initPayload(&container_payload.base);
    };

    const payload = try t.arena.create(ast.Payload.SimpleVarDecl);
    payload.* = .{
        .base = .{ .tag = if (is_pub) .pub_var_simple else .var_simple },
        .data = .{
            .name = name,
            .init = init_node,
        },
    };
    const node = ZigNode.initPayload(&payload.base);
    if (toplevel) {
        try t.addTopLevelDecl(name, node);
        // Only add the alias if the name is available *and* it was caught by
        // name detection. Don't bother performing a weak mangle, since a
        // mangled name is of no real use here.
        if (!is_unnamed and !t.global_names.contains(bare_name) and t.weak_global_names.contains(bare_name))
            try t.alias_list.append(t.gpa, .{ .alias = bare_name, .name = name });
        try t.global_scope.container_member_fns_map.put(t.gpa, record_qt, .{
            .container_decl_ptr = &payload.data.init,
        });
    } else {
        try scope.appendNode(node);
        try bs.discardVariable(name);
    }
}