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.

Block

Represents an in-progress Node.Block. This struct is stack-allocated. When it is deinitialized, it produces an Node.Block which is allocated into the main arena.

Scope.Block
pub const Block = struct

File

Code

pub const Block = struct {
    base: Scope,
    translator: *Translator,
    statements: std.ArrayList(ast.Node),
    variables: AliasList,
    mangle_count: u32 = 0,
    label: ?[]const u8 = null,

    /// By default all variables are discarded, since we do not know in advance if they
    /// will be used. This maps the variable's name to the Discard payload, so that if
    /// the variable is subsequently referenced we can indicate that the discard should
    /// be skipped during the intermediate AST -> Zig AST render step.
    variable_discards: std.array_hash_map.String(*ast.Payload.Discard),

    /// When the block corresponds to a function, keep track of the return type
    /// so that the return expression can be cast, if necessary
    return_type: ?aro.QualType = null,

    /// C static local variables are wrapped in a block-local struct. The struct
    /// is named `mangle(static_local_ + name)` and the Zig variable within the
    /// struct keeps the name of the C variable.
    pub const static_local_prefix = "static_local";

    /// C extern local variables are wrapped in a block-local struct. The struct
    /// is named `mangle(extern_local + name)` and the Zig variable within the
    /// struct keeps the name of the C variable.
    pub const extern_local_prefix = "extern_local";

    pub fn init(t: *Translator, parent: *Scope, labeled: bool) !Block {
        var blk: Block = .{
            .base = .{
                .id = .block,
                .parent = parent,
            },
            .translator = t,
            .statements = .empty,
            .variables = .empty,
            .variable_discards = .empty,
        };
        if (labeled) {
            blk.label = try blk.makeMangledName("blk");
        }
        return blk;
    }

    pub fn deinit(block: *Block) void {
        block.statements.deinit(block.translator.gpa);
        block.variables.deinit(block.translator.gpa);
        block.variable_discards.deinit(block.translator.gpa);
        block.* = undefined;
    }

    pub fn complete(block: *Block) !ast.Node {
        const arena = block.translator.arena;
        if (block.base.parent.?.id == .do_loop) {
            // We reserve 1 extra statement if the parent is a do_loop. This is in case of
            // do while, we want to put `if (cond) break;` at the end.
            const alloc_len = block.statements.items.len + @intFromBool(block.base.parent.?.id == .do_loop);
            var stmts = try arena.alloc(ast.Node, alloc_len);
            stmts.len = block.statements.items.len;
            @memcpy(stmts[0..block.statements.items.len], block.statements.items);
            return ast.Node.Tag.block.create(arena, .{
                .label = block.label,
                .stmts = stmts,
            });
        }
        if (block.statements.items.len == 0) return ast.Node.Tag.empty_block.init();
        return ast.Node.Tag.block.create(arena, .{
            .label = block.label,
            .stmts = try arena.dupe(ast.Node, block.statements.items),
        });
    }

    /// Given the desired name, return a name that does not shadow anything from outer scopes.
    /// Inserts the returned name into the scope.
    /// The name will not be visible to callers of getAlias.
    pub fn reserveMangledName(block: *Block, name: []const u8) ![]const u8 {
        return block.createMangledName(name, true, null);
    }

    /// Same as reserveMangledName, but enables the alias immediately.
    pub fn makeMangledName(block: *Block, name: []const u8) ![]const u8 {
        return block.createMangledName(name, false, null);
    }

    pub fn createMangledName(block: *Block, name: []const u8, reservation: bool, prefix_opt: ?[]const u8) ![]const u8 {
        const arena = block.translator.arena;
        const name_copy = try arena.dupe(u8, name);
        const alias_base = if (prefix_opt) |prefix|
            try std.fmt.allocPrint(arena, "{s}_{s}", .{ prefix, name })
        else
            name;
        var proposed_name = alias_base;
        while (block.contains(proposed_name)) {
            block.mangle_count += 1;
            proposed_name = try std.fmt.allocPrint(arena, "{s}_{d}", .{ alias_base, block.mangle_count });
        }
        const new_mangle = try block.variables.addOne(block.translator.gpa);
        if (reservation) {
            new_mangle.* = .{ .name = name_copy, .alias = name_copy };
        } else {
            new_mangle.* = .{ .name = name_copy, .alias = proposed_name };
        }
        return proposed_name;
    }

    fn getAlias(block: *Block, name: []const u8) ?[]const u8 {
        for (block.variables.items) |p| {
            if (std.mem.eql(u8, p.name, name))
                return p.alias;
        }
        return block.base.parent.?.getAlias(name);
    }

    fn localContains(block: *Block, name: []const u8) bool {
        for (block.variables.items) |p| {
            if (std.mem.eql(u8, p.alias, name))
                return true;
        }
        return false;
    }

    fn contains(block: *Block, name: []const u8) bool {
        if (block.localContains(name))
            return true;
        return block.base.parent.?.contains(name);
    }

    pub fn discardVariable(block: *Block, name: []const u8) Translator.Error!void {
        const gpa = block.translator.gpa;
        const arena = block.translator.arena;
        const name_node = try ast.Node.Tag.identifier.create(arena, name);
        const discard = try ast.Node.Tag.discard.create(arena, .{ .should_skip = false, .value = name_node });
        try block.statements.append(gpa, discard);
        try block.variable_discards.putNoClobber(gpa, name, discard.castTag(.discard).?);
    }
}