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.

continueExpr

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

File

lib/std/zig/AstGen.zig:2254

Code

fn continueExpr(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;

    if (opt_break_label == .none and opt_rhs != .none) {
        return astgen.failNode(node, "cannot continue with operand without label", .{});
    }

    // 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| {
                return astgen.failNodeNotes(node, "cannot continue 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)) {
                        switch (gen_zir.continue_target) {
                            .none => {
                                return astgen.failNode(node, "continue outside of loop or labeled switch expression", .{});
                            },
                            .@"break" => if (opt_rhs != .none) {
                                return astgen.failNode(node, "cannot continue loop with operand", .{});
                            },
                            .switch_continue => if (opt_rhs == .none) {
                                return astgen.failNode(node, "cannot continue switch without operand", .{});
                            },
                        }
                        label.used = true;
                        label.used_for_continue = 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 `continue` is unlabeled. If the gz we've found doesn't
                // provide a `continue` target or corresponds to a labeled
                // `switch`, ignore it and continue to parent scopes.
                switch (gen_zir.continue_target) {
                    .none, .switch_continue => {
                        continue :find_scope gen_zir.parent.unwrap();
                    },
                    .@"break" => {},
                }
            } else {
                // We don't have a break label and the gz we found doesn't allow
                // unlabeled control flow, so we continue to its parent scopes.
                continue :find_scope gen_zir.parent.unwrap();
            }

            switch (gen_zir.continue_target) {
                .none => unreachable, // should have failed or continued to parent scopes by now
                .@"break" => |block| {
                    try genDefers(parent_gz, scope, parent_scope, .normal_only);

                    const break_tag: Zir.Inst.Tag = if (gen_zir.is_inline)
                        .break_inline
                    else
                        .@"break";
                    if (break_tag == .break_inline) {
                        _ = try parent_gz.addUnNode(.check_comptime_control_flow, block.toRef(), node);
                    }

                    // As our last action before the continue, "pop" the error trace if needed
                    if (!gen_zir.is_comptime) {
                        _ = try parent_gz.addRestoreErrRetIndex(.{ .block = block }, .always, node);
                    }
                    _ = try parent_gz.addBreak(break_tag, block, .void_value);
                    return .unreachable_value;
                },
                .switch_continue => |switch_block| {
                    const rhs = opt_rhs.unwrap().?; // checked above
                    const operand = try reachableExpr(parent_gz, parent_scope, gen_zir.continue_result_info, rhs, node);

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

                    // As our last action before the continue, "pop" the error trace if needed
                    if (!gen_zir.is_comptime) {
                        _ = try parent_gz.addRestoreErrRetIndex(.{ .block = switch_block }, .always, node);
                    }
                    _ = try parent_gz.addBreakWithSrcNode(.switch_continue, switch_block, operand, rhs);
                    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, "continue expression outside loop", .{});
            }
        },
        .top => unreachable,
    }
}