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.

breakExpr

AstGen.breakExpr
fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref

File

lib/std/zig/AstGen.zig:2161

Code

fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
    const astgen = parent_gz.astgen;
    const tree = astgen.tree;
    const opt_break_label, const opt_rhs = tree.nodeData(node).opt_token_and_opt_node;

    // Look for the label in the scope.
    find_scope: switch (parent_scope.unwrap()) {
        .gen_zir => |gen_zir| {
            const scope = &gen_zir.base;

            if (gen_zir.cur_defer_node.unwrap()) |cur_defer_node| {
                // We are breaking out of a `defer` block.
                return astgen.failNodeNotes(node, "cannot break out of defer expression", .{}, &.{
                    try astgen.errNoteNode(
                        cur_defer_node,
                        "defer expression here",
                        .{},
                    ),
                });
            }

            if (opt_break_label.unwrap()) |break_label| labeled: {
                if (gen_zir.label) |*label| {
                    if (try astgen.tokenIdentEql(label.token, break_label)) {
                        label.used = true;
                        break :labeled;
                    }
                }
                // gz without or with different label, continue to parent scopes.
                continue :find_scope gen_zir.parent.unwrap();
            } else if (!gen_zir.allow_unlabeled_control_flow) {
                // This `break` is unlabeled and the gz we've found doesn't allow
                // unlabeled control flow. Continue to parent scopes.
                continue :find_scope gen_zir.parent.unwrap();
            }

            const break_tag: Zir.Inst.Tag = if (gen_zir.is_inline)
                .break_inline
            else
                .@"break";

            if (opt_rhs.unwrap()) |rhs| {
                // We have a `break` operand.
                const operand = try reachableExpr(parent_gz, parent_scope, gen_zir.break_result_info, rhs, node);

                try genDefers(parent_gz, scope, parent_scope, .normal_only);

                // As our last action before the break, "pop" the error trace if needed
                if (!gen_zir.is_comptime) {
                    try restoreErrRetIndex(parent_gz, .{ .block = gen_zir.break_target }, gen_zir.break_result_info, rhs, operand);
                }
                switch (gen_zir.break_result_info.rl) {
                    .ptr => {
                        // In this case we don't have any mechanism to intercept it;
                        // we assume the result location is written, and we break with void.
                        _ = try parent_gz.addBreak(break_tag, gen_zir.break_target, .void_value);
                    },
                    .discard => {
                        _ = try parent_gz.addBreak(break_tag, gen_zir.break_target, .void_value);
                    },
                    else => {
                        _ = try parent_gz.addBreakWithSrcNode(break_tag, gen_zir.break_target, operand, rhs);
                    },
                }
                return .unreachable_value;
            } else {
                _ = try rvalue(parent_gz, gen_zir.break_result_info, .void_value, node);

                try genDefers(parent_gz, scope, parent_scope, .normal_only);

                // As our last action before the break, "pop" the error trace if needed
                if (!gen_zir.is_comptime)
                    _ = try parent_gz.addRestoreErrRetIndex(.{ .block = gen_zir.break_target }, .always, node);

                _ = try parent_gz.addBreak(break_tag, gen_zir.break_target, .void_value);
                return .unreachable_value;
            }
        },
        .local_val => |local_val| continue :find_scope local_val.parent.unwrap(),
        .local_ptr => |local_ptr| continue :find_scope local_ptr.parent.unwrap(),
        .defer_normal, .defer_error => |defer_scope| continue :find_scope defer_scope.parent.unwrap(),
        .namespace => {
            if (opt_break_label.unwrap()) |break_label| {
                const label_name = try astgen.identifierTokenString(break_label);
                return astgen.failTok(break_label, "label not found: '{s}'", .{label_name});
            } else {
                return astgen.failNode(node, "break expression outside loop", .{});
            }
        },
        .top => unreachable,
    }
}