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.

transVarDecl

Translator.transVarDecl
fn transVarDecl(t: *Translator, scope: *Scope, variable: Node.Variable, decl_node: Node.Index) Error!void

File

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

Code

fn transVarDecl(t: *Translator, scope: *Scope, variable: Node.Variable, decl_node: Node.Index) Error!void {
    const base_name = t.tree.tokSlice(variable.name_tok);
    const toplevel = scope.id == .root;
    const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(t) else undefined;
    const name, const use_base_name = blk: {
        if (toplevel) break :blk .{ base_name, false };

        // Local extern and static variables are wrapped in a struct.
        const prefix: ?[]const u8 = switch (variable.storage_class) {
            .@"extern" => Scope.Block.extern_local_prefix,
            .static => Scope.Block.static_local_prefix,
            else => null,
        };
        break :blk .{ try bs.createMangledName(base_name, false, prefix), prefix != null };
    };

    if (t.typeWasDemotedToOpaque(variable.qt)) {
        if (variable.storage_class != .@"extern" and scope.id == .root) {
            return t.failDecl(scope, variable.name_tok, name, "non-extern variable has opaque type", .{});
        } else {
            return t.failDecl(scope, variable.name_tok, name, "local variable has opaque type", .{});
        }
    }

    const type_node = (if (variable.initializer) |init|
        t.transTypeInit(scope, variable.qt, init, variable.name_tok)
    else
        t.transType(scope, variable.qt, variable.name_tok)) catch |err| switch (err) {
        error.UnsupportedType => {
            return t.failDecl(scope, variable.name_tok, name, "unable to translate variable declaration type", .{});
        },
        else => |e| return e,
    };

    const array_ty = variable.qt.get(t.comp, .array);
    var is_const = variable.qt.@"const" or (array_ty != null and array_ty.?.elem.@"const");
    var is_extern = variable.storage_class == .@"extern";

    var self_referential = false;
    const init_node = init: {
        if (variable.initializer) |init| {
            const maybe_literal = init.get(t.tree);
            if (!toplevel) try t.wip_var_inits.putNoClobber(t.gpa, decl_node, {});
            defer _ = t.wip_var_inits.remove(decl_node);

            const init_node = (if (maybe_literal == .string_literal_expr)
                t.transStringLiteralInitializer(init, maybe_literal.string_literal_expr, type_node)
            else
                t.transExprCoercing(scope, init, .used)) catch |err| switch (err) {
                error.SelfReferential => {
                    self_referential = true;
                    break :init ZigTag.undefined_literal.init();
                },
                error.UnsupportedTranslation, error.UnsupportedType => {
                    return t.failDecl(scope, variable.name_tok, name, "unable to resolve var init expr", .{});
                },
                else => |e| return e,
            };

            break :init try t.toNonBool(init_node, variable.qt);
        }
        if (variable.storage_class == .@"extern") {
            if (array_ty != null and array_ty.?.len == .incomplete) {
                // Oh no, an extern array of unknown size! These are really fun because there's no
                // direct equivalent in Zig. To translate correctly, we'll have to create a C-pointer
                // to the data initialized via @extern.

                // Since this is really a pointer to the underlying data, we tweak a few properties.
                is_extern = false;
                is_const = true;

                const name_str = try std.fmt.allocPrint(t.arena, "\"{s}\"", .{base_name});
                break :init try ZigTag.builtin_extern.create(t.arena, .{
                    .type = type_node,
                    .name = try ZigTag.string_literal.create(t.arena, name_str),
                });
            }
            break :init null;
        }
        if (toplevel or variable.storage_class == .static or variable.thread_local) {
            // The C language specification states that variables with static or threadlocal
            // storage without an initializer are initialized to a zero value.
            break :init try t.createZeroValueNode(variable.qt, type_node, .no_as);
        }
        break :init ZigTag.undefined_literal.init();
    };

    const linksection_string = blk: {
        if (variable.qt.getAttribute(t.comp, .section)) |section| {
            break :blk t.comp.interner.get(section.name.ref()).bytes;
        }
        break :blk null;
    };

    // TODO actually set with @export/@extern
    const linkage = variable.qt.linkage(t.comp);
    if (linkage != .strong) {
        try t.warn(scope, variable.name_tok, "TODO {s} linkage ignored", .{@tagName(linkage)});
    }

    const alignment: ?c_uint = variable.qt.requestedAlignment(t.comp) orelse null;
    var node = try ZigTag.var_decl.create(t.arena, .{
        .is_pub = toplevel,
        .is_const = is_const and !self_referential,
        .is_extern = is_extern,
        .is_export = toplevel and variable.storage_class == .auto and linkage == .strong,
        .is_threadlocal = variable.thread_local,
        .linksection_string = linksection_string,
        .alignment = alignment,
        .name = if (use_base_name) base_name else name,
        .type = type_node,
        .init = init_node,
    });

    if (toplevel) {
        try t.addTopLevelDecl(name, node);
    } else {
        if (use_base_name) {
            node = try ZigTag.wrapped_local.create(t.arena, .{ .name = name, .init = node });
        }
        try scope.appendNode(node);
        if (self_referential) {
            const deferred_init = t.transExprCoercing(scope, variable.initializer.?, .used) catch |err| switch (err) {
                error.SelfReferential => unreachable,
                error.UnsupportedTranslation, error.UnsupportedType => {
                    return t.failDecl(scope, variable.name_tok, name, "unable to resolve var init expr", .{});
                },
                else => |e| return e,
            };

            const assign = try ZigTag.assign.create(t.arena, .{
                .lhs = try ZigTag.identifier.create(t.arena, name),
                .rhs = try t.toNonBool(deferred_init, variable.qt),
            });
            try scope.appendNode(assign);
        }
        try bs.discardVariable(name);

        if (variable.qt.getAttribute(t.comp, .cleanup)) |cleanup_attr| {
            const cleanup_fn_name = t.tree.tokSlice(cleanup_attr.function.tok);
            const mangled_fn_name = scope.getAlias(cleanup_fn_name) orelse cleanup_fn_name;
            const fn_id = try ZigTag.identifier.create(t.arena, mangled_fn_name);

            const varname = try ZigTag.identifier.create(t.arena, name);
            const args = try t.arena.alloc(ZigNode, 1);
            args[0] = try ZigTag.address_of.create(t.arena, varname);

            const cleanup_call = try ZigTag.call.create(t.arena, .{ .lhs = fn_id, .args = args });
            const discard = try ZigTag.discard.create(t.arena, .{ .should_skip = false, .value = cleanup_call });
            const deferred_cleanup = try ZigTag.@"defer".create(t.arena, discard);

            try bs.statements.append(t.gpa, deferred_cleanup);
        }
    }
}