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.

tunnelThroughClosure

Access a ZIR instruction through closure. May tunnel through arbitrarily many namespaces, adding closure captures as required. Returns the index of the closure_get instruction added to gz.

AstGen.tunnelThroughClosure
fn tunnelThroughClosure(
    gz: *GenZir,
    /// The node which references the value to be captured.
    inner_ref_node: Ast.Node.Index,
    /// The number of namespaces being tunnelled through. At least 1.
    num_tunnels: u32,
    /// The value being captured.
    value: union(enum)

File

lib/std/zig/AstGen.zig:8302

Code

fn tunnelThroughClosure(
    gz: *GenZir,
    /// The node which references the value to be captured.
    inner_ref_node: Ast.Node.Index,
    /// The number of namespaces being tunnelled through. At least 1.
    num_tunnels: u32,
    /// The value being captured.
    value: union(enum) {
        ref: Zir.Inst.Ref,
        ref_load: Zir.Inst.Ref,
        decl_val: Zir.NullTerminatedString,
        decl_ref: Zir.NullTerminatedString,
    },
    /// The location of the value's declaration.
    decl_src: union(enum) {
        token: Ast.TokenIndex,
        node: Ast.Node.Index,
    },
    name_str_index: Zir.NullTerminatedString,
) !Zir.Inst.Ref {
    switch (value) {
        .ref => |v| if (v.toIndex() == null) return v, // trivial value; do not need tunnel
        .ref_load => |v| assert(v.toIndex() != null), // there are no constant pointer refs
        .decl_val, .decl_ref => {},
    }

    const astgen = gz.astgen;
    const gpa = astgen.gpa;

    // Otherwise we need a tunnel. First, figure out the path of namespaces we
    // are tunneling through. This is usually only going to be one or two, so
    // use an BFA to optimize for the common case.
    var bfa_buf: [2]usize = undefined;
    var bfa: std.heap.BufferFirstAllocator = .init(@ptrCast(&bfa_buf), astgen.arena);
    var intermediate_tunnels = try bfa.allocator().alloc(*Scope.Namespace, num_tunnels - 1);

    const root_ns = ns: {
        var i: usize = num_tunnels - 1;
        var scope: *Scope = gz.parent;
        while (i > 0) {
            if (scope.cast(Scope.Namespace)) |mid_ns| {
                i -= 1;
                intermediate_tunnels[i] = mid_ns;
            }
            scope = scope.parent().?;
        }
        while (true) {
            if (scope.cast(Scope.Namespace)) |ns| break :ns ns;
            scope = scope.parent().?;
        }
    };

    // Now that we know the scopes we're tunneling through, begin adding
    // captures as required, starting with the outermost namespace.
    const root_capture: Zir.Inst.Capture = .wrap(switch (value) {
        .ref => |v| .{ .instruction = v.toIndex().? },
        .ref_load => |v| .{ .instruction_load = v.toIndex().? },
        .decl_val => |str| .{ .decl_val = str },
        .decl_ref => |str| .{ .decl_ref = str },
    });

    const root_gop = try root_ns.captures.getOrPut(gpa, root_capture);
    root_gop.value_ptr.* = name_str_index;
    var cur_capture_index = std.math.cast(u16, root_gop.index) orelse return astgen.failNodeNotes(
        root_ns.node,
        "this compiler implementation only supports up to 65536 captures per namespace",
        .{},
        &.{
            switch (decl_src) {
                .token => |t| try astgen.errNoteTok(t, "captured value here", .{}),
                .node => |n| try astgen.errNoteNode(n, "captured value here", .{}),
            },
            try astgen.errNoteNode(inner_ref_node, "value used here", .{}),
        },
    );

    for (intermediate_tunnels) |tunnel_ns| {
        const tunnel_gop = try tunnel_ns.captures.getOrPut(gpa, .wrap(.{ .nested = cur_capture_index }));
        tunnel_gop.value_ptr.* = name_str_index;
        cur_capture_index = std.math.cast(u16, tunnel_gop.index) orelse return astgen.failNodeNotes(
            tunnel_ns.node,
            "this compiler implementation only supports up to 65536 captures per namespace",
            .{},
            &.{
                switch (decl_src) {
                    .token => |t| try astgen.errNoteTok(t, "captured value here", .{}),
                    .node => |n| try astgen.errNoteNode(n, "captured value here", .{}),
                },
                try astgen.errNoteNode(inner_ref_node, "value used here", .{}),
            },
        );
    }

    // Incorporate the capture index into the source hash, so that changes in
    // the order of captures cause suitable re-analysis.
    astgen.src_hasher.update(std.mem.asBytes(&cur_capture_index));

    // Add an instruction to get the value from the closure.
    return gz.addExtendedNodeSmall(.closure_get, inner_ref_node, cur_capture_index);
}