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.

expr

Returns true if rl provides a result pointer and the expression consumes it.

AstRlAnnotate.expr
fn expr(astrl: *AstRlAnnotate, node: Ast.Node.Index, block: ?*Block, ri: ResultInfo) Allocator.Error!bool

File

lib/std/zig/AstRlAnnotate.zig:130

Code

fn expr(astrl: *AstRlAnnotate, node: Ast.Node.Index, block: ?*Block, ri: ResultInfo) Allocator.Error!bool {
    const tree = astrl.tree;
    switch (tree.nodeTag(node)) {
        .root,
        .switch_case_one,
        .switch_case_inline_one,
        .switch_case,
        .switch_case_inline,
        .switch_range,
        .for_range,
        .asm_output,
        .asm_input,
        => unreachable,

        .@"defer", .@"errdefer" => {
            _ = try astrl.expr(tree.nodeData(node).node, block, ResultInfo.none);
            return false;
        },

        .container_field_init,
        .container_field_align,
        .container_field,
        => {
            const full = tree.fullContainerField(node).?;
            const type_expr = full.ast.type_expr.unwrap().?;
            _ = try astrl.expr(type_expr, block, ResultInfo.type_only);
            if (full.ast.align_expr.unwrap()) |align_expr| {
                _ = try astrl.expr(align_expr, block, ResultInfo.type_only);
            }
            if (full.ast.value_expr.unwrap()) |value_expr| {
                _ = try astrl.expr(value_expr, block, ResultInfo.type_only);
            }
            return false;
        },
        .test_decl => {
            _ = try astrl.expr(tree.nodeData(node).opt_token_and_node[1], block, ResultInfo.none);
            return false;
        },
        .global_var_decl,
        .local_var_decl,
        .simple_var_decl,
        .aligned_var_decl,
        => {
            const full = tree.fullVarDecl(node).?;
            const init_ri = if (full.ast.type_node.unwrap()) |type_node| init_ri: {
                _ = try astrl.expr(type_node, block, ResultInfo.type_only);
                break :init_ri ResultInfo.typed_ptr;
            } else ResultInfo.inferred_ptr;
            const init_node = full.ast.init_node.unwrap() orelse {
                // No init node, so we're done.
                return false;
            };
            switch (tree.tokenTag(full.ast.mut_token)) {
                .keyword_const => {
                    const init_consumes_rl = try astrl.expr(init_node, block, init_ri);
                    if (init_consumes_rl) {
                        try astrl.nodes_need_rl.putNoClobber(astrl.gpa, node, {});
                    }
                    return false;
                },
                .keyword_var => {
                    // We'll create an alloc either way, so don't care if the
                    // result pointer is consumed.
                    _ = try astrl.expr(init_node, block, init_ri);
                    return false;
                },
                else => unreachable,
            }
        },
        .assign_destructure => {
            const full = tree.assignDestructure(node);
            for (full.ast.variables) |variable_node| {
                _ = try astrl.expr(variable_node, block, ResultInfo.none);
            }
            // We don't need to gather any meaningful data here, because destructures always use RLS
            _ = try astrl.expr(full.ast.value_expr, block, ResultInfo.none);
            return false;
        },
        .assign => {
            const lhs, const rhs = tree.nodeData(node).node_and_node;
            _ = try astrl.expr(lhs, block, ResultInfo.none);
            _ = try astrl.expr(rhs, block, ResultInfo.typed_ptr);
            return false;
        },
        .assign_shl,
        .assign_shl_sat,
        .assign_shr,
        .assign_bit_and,
        .assign_bit_or,
        .assign_bit_xor,
        .assign_div,
        .assign_sub,
        .assign_sub_wrap,
        .assign_sub_sat,
        .assign_mod,
        .assign_add,
        .assign_add_wrap,
        .assign_add_sat,
        .assign_mul,
        .assign_mul_wrap,
        .assign_mul_sat,
        => {
            const lhs, const rhs = tree.nodeData(node).node_and_node;
            _ = try astrl.expr(lhs, block, ResultInfo.none);
            _ = try astrl.expr(rhs, block, ResultInfo.none);
            return false;
        },
        .shl, .shr => {
            const lhs, const rhs = tree.nodeData(node).node_and_node;
            _ = try astrl.expr(lhs, block, ResultInfo.none);
            _ = try astrl.expr(rhs, block, ResultInfo.type_only);
            return false;
        },
        .add,
        .add_wrap,
        .add_sat,
        .sub,
        .sub_wrap,
        .sub_sat,
        .mul,
        .mul_wrap,
        .mul_sat,
        .div,
        .mod,
        .shl_sat,
        .bit_and,
        .bit_or,
        .bit_xor,
        .bang_equal,
        .equal_equal,
        .greater_than,
        .greater_or_equal,
        .less_than,
        .less_or_equal,
        .array_cat,
        => {
            const lhs, const rhs = tree.nodeData(node).node_and_node;
            _ = try astrl.expr(lhs, block, ResultInfo.none);
            _ = try astrl.expr(rhs, block, ResultInfo.none);
            return false;
        },

        .error_union, .merge_error_sets => {
            const lhs, const rhs = tree.nodeData(node).node_and_node;
            _ = try astrl.expr(lhs, block, ResultInfo.none);
            _ = try astrl.expr(rhs, block, ResultInfo.none);
            return false;
        },
        .bool_and,
        .bool_or,
        => {
            const lhs, const rhs = tree.nodeData(node).node_and_node;
            _ = try astrl.expr(lhs, block, ResultInfo.type_only);
            _ = try astrl.expr(rhs, block, ResultInfo.type_only);
            return false;
        },
        .bool_not => {
            _ = try astrl.expr(tree.nodeData(node).node, block, ResultInfo.type_only);
            return false;
        },
        .bit_not, .negation, .negation_wrap => {
            _ = try astrl.expr(tree.nodeData(node).node, block, ResultInfo.none);
            return false;
        },

        // These nodes are leaves and never consume a result location.
        .identifier,
        .string_literal,
        .multiline_string_literal,
        .number_literal,
        .unreachable_literal,
        .asm_simple,
        .@"asm",
        .enum_literal,
        .error_value,
        .anyframe_literal,
        .@"continue",
        .char_literal,
        .error_set_decl,
        => return false,

        .builtin_call_two,
        .builtin_call_two_comma,
        .builtin_call,
        .builtin_call_comma,
        => {
            var buf: [2]Ast.Node.Index = undefined;
            const params = tree.builtinCallParams(&buf, node).?;
            return astrl.builtinCall(block, ri, node, params);
        },

        .call_one,
        .call_one_comma,
        .call,
        .call_comma,
        => {
            var buf: [1]Ast.Node.Index = undefined;
            const full = tree.fullCall(&buf, node).?;
            _ = try astrl.expr(full.ast.fn_expr, block, ResultInfo.none);
            for (full.ast.params) |param_node| {
                _ = try astrl.expr(param_node, block, ResultInfo.type_only);
            }
            return false; // TODO: once function calls are passed result locations this will change
        },

        .@"return" => {
            if (tree.nodeData(node).opt_node.unwrap()) |lhs| {
                const ret_val_consumes_rl = try astrl.expr(lhs, block, ResultInfo.typed_ptr);
                if (ret_val_consumes_rl) {
                    try astrl.nodes_need_rl.putNoClobber(astrl.gpa, node, {});
                }
            }
            return false;
        },

        .field_access => {
            const lhs, _ = tree.nodeData(node).node_and_token;
            _ = try astrl.expr(lhs, block, ResultInfo.none);
            return false;
        },

        .if_simple, .@"if" => {
            const full = tree.fullIf(node).?;
            if (full.error_token != null or full.payload_token != null) {
                _ = try astrl.expr(full.ast.cond_expr, block, ResultInfo.none);
            } else {
                _ = try astrl.expr(full.ast.cond_expr, block, ResultInfo.type_only); // bool
            }

            if (full.ast.else_expr.unwrap()) |else_expr| {
                const then_uses_rl = try astrl.expr(full.ast.then_expr, block, ri);
                const else_uses_rl = try astrl.expr(else_expr, block, ri);
                const uses_rl = then_uses_rl or else_uses_rl;
                if (uses_rl) try astrl.nodes_need_rl.putNoClobber(astrl.gpa, node, {});
                return uses_rl;
            } else {
                _ = try astrl.expr(full.ast.then_expr, block, ResultInfo.none);
                return false;
            }
        },

        .while_simple, .while_cont, .@"while" => {
            const full = tree.fullWhile(node).?;
            const label: ?[]const u8 = if (full.label_token) |label_token| label: {
                break :label try astrl.identString(label_token);
            } else null;
            if (full.error_token != null or full.payload_token != null) {
                _ = try astrl.expr(full.ast.cond_expr, block, ResultInfo.none);
            } else {
                _ = try astrl.expr(full.ast.cond_expr, block, ResultInfo.type_only); // bool
            }
            var new_block: Block = .{
                .parent = block,
                .label = label,
                .is_loop = true,
                .ri = ri,
                .consumes_res_ptr = false,
            };
            if (full.ast.cont_expr.unwrap()) |cont_expr| {
                _ = try astrl.expr(cont_expr, &new_block, ResultInfo.none);
            }
            _ = try astrl.expr(full.ast.then_expr, &new_block, ResultInfo.none);
            const else_consumes_rl = if (full.ast.else_expr.unwrap()) |else_expr| else_rl: {
                break :else_rl try astrl.expr(else_expr, block, ri);
            } else false;
            if (new_block.consumes_res_ptr or else_consumes_rl) {
                try astrl.nodes_need_rl.putNoClobber(astrl.gpa, node, {});
                return true;
            } else {
                return false;
            }
        },

        .for_simple, .@"for" => {
            const full = tree.fullFor(node).?;
            const label: ?[]const u8 = if (full.label_token) |label_token| label: {
                break :label try astrl.identString(label_token);
            } else null;
            for (full.ast.inputs) |input| {
                if (tree.nodeTag(input) == .for_range) {
                    const lhs, const opt_rhs = tree.nodeData(input).node_and_opt_node;
                    _ = try astrl.expr(lhs, block, ResultInfo.type_only);
                    if (opt_rhs.unwrap()) |rhs| {
                        _ = try astrl.expr(rhs, block, ResultInfo.type_only);
                    }
                } else {
                    _ = try astrl.expr(input, block, ResultInfo.none);
                }
            }
            var new_block: Block = .{
                .parent = block,
                .label = label,
                .is_loop = true,
                .ri = ri,
                .consumes_res_ptr = false,
            };
            _ = try astrl.expr(full.ast.then_expr, &new_block, ResultInfo.none);
            const else_consumes_rl = if (full.ast.else_expr.unwrap()) |else_expr| else_rl: {
                break :else_rl try astrl.expr(else_expr, block, ri);
            } else false;
            if (new_block.consumes_res_ptr or else_consumes_rl) {
                try astrl.nodes_need_rl.putNoClobber(astrl.gpa, node, {});
                return true;
            } else {
                return false;
            }
        },

        .slice_open => {
            const sliced, const start = tree.nodeData(node).node_and_node;
            _ = try astrl.expr(sliced, block, ResultInfo.none);
            _ = try astrl.expr(start, block, ResultInfo.type_only);
            return false;
        },
        .slice => {
            const sliced, const extra_index = tree.nodeData(node).node_and_extra;
            const extra = tree.extraData(extra_index, Ast.Node.Slice);
            _ = try astrl.expr(sliced, block, ResultInfo.none);
            _ = try astrl.expr(extra.start, block, ResultInfo.type_only);
            _ = try astrl.expr(extra.end, block, ResultInfo.type_only);
            return false;
        },
        .slice_sentinel => {
            const sliced, const extra_index = tree.nodeData(node).node_and_extra;
            const extra = tree.extraData(extra_index, Ast.Node.SliceSentinel);
            _ = try astrl.expr(sliced, block, ResultInfo.none);
            _ = try astrl.expr(extra.start, block, ResultInfo.type_only);
            if (extra.end.unwrap()) |end| {
                _ = try astrl.expr(end, block, ResultInfo.type_only);
            }
            _ = try astrl.expr(extra.sentinel, block, ResultInfo.none);
            return false;
        },
        .deref => {
            _ = try astrl.expr(tree.nodeData(node).node, block, ResultInfo.none);
            return false;
        },
        .address_of => {
            _ = try astrl.expr(tree.nodeData(node).node, block, ResultInfo.none);
            return false;
        },
        .optional_type => {
            _ = try astrl.expr(tree.nodeData(node).node, block, ResultInfo.type_only);
            return false;
        },
        .@"try",
        .@"nosuspend",
        => return astrl.expr(tree.nodeData(node).node, block, ri),
        .grouped_expression,
        .unwrap_optional,
        => return astrl.expr(tree.nodeData(node).node_and_token[0], block, ri),

        .block_two,
        .block_two_semicolon,
        .block,
        .block_semicolon,
        => {
            var buf: [2]Ast.Node.Index = undefined;
            const statements = tree.blockStatements(&buf, node).?;
            return astrl.blockExpr(block, ri, node, statements);
        },
        .anyframe_type => {
            _, const child_type = tree.nodeData(node).token_and_node;
            _ = try astrl.expr(child_type, block, ResultInfo.type_only);
            return false;
        },
        .@"catch", .@"orelse" => {
            const lhs, const rhs = tree.nodeData(node).node_and_node;
            _ = try astrl.expr(lhs, block, ResultInfo.none);
            const rhs_consumes_rl = try astrl.expr(rhs, block, ri);
            if (rhs_consumes_rl) {
                try astrl.nodes_need_rl.putNoClobber(astrl.gpa, node, {});
            }
            return rhs_consumes_rl;
        },

        .ptr_type_aligned,
        .ptr_type_sentinel,
        .ptr_type,
        .ptr_type_bit_range,
        => {
            const full = tree.fullPtrType(node).?;
            _ = try astrl.expr(full.ast.child_type, block, ResultInfo.type_only);
            if (full.ast.sentinel.unwrap()) |sentinel| {
                _ = try astrl.expr(sentinel, block, ResultInfo.type_only);
            }
            if (full.ast.addrspace_node.unwrap()) |addrspace_node| {
                _ = try astrl.expr(addrspace_node, block, ResultInfo.type_only);
            }
            if (full.ast.align_node.unwrap()) |align_node| {
                _ = try astrl.expr(align_node, block, ResultInfo.type_only);
            }
            if (full.ast.bit_range_start.unwrap()) |bit_range_start| {
                const bit_range_end = full.ast.bit_range_end.unwrap().?;
                _ = try astrl.expr(bit_range_start, block, ResultInfo.type_only);
                _ = try astrl.expr(bit_range_end, block, ResultInfo.type_only);
            }
            return false;
        },

        .container_decl,
        .container_decl_trailing,
        .container_decl_arg,
        .container_decl_arg_trailing,
        .container_decl_two,
        .container_decl_two_trailing,
        .tagged_union,
        .tagged_union_trailing,
        .tagged_union_enum_tag,
        .tagged_union_enum_tag_trailing,
        .tagged_union_two,
        .tagged_union_two_trailing,
        => {
            var buf: [2]Ast.Node.Index = undefined;
            try astrl.containerDecl(block, tree.fullContainerDecl(&buf, node).?);
            return false;
        },

        .@"break" => {
            const opt_label, const opt_rhs = tree.nodeData(node).opt_token_and_opt_node;
            const rhs = opt_rhs.unwrap() orelse {
                // Breaks with void are not interesting
                return false;
            };

            var opt_cur_block = block;
            if (opt_label.unwrap()) |label_token| {
                const break_label = try astrl.identString(label_token);
                while (opt_cur_block) |cur_block| : (opt_cur_block = cur_block.parent) {
                    const block_label = cur_block.label orelse continue;
                    if (std.mem.eql(u8, block_label, break_label)) break;
                }
            } else {
                // No label - we're breaking from a loop.
                while (opt_cur_block) |cur_block| : (opt_cur_block = cur_block.parent) {
                    if (cur_block.is_loop) break;
                }
            }

            if (opt_cur_block) |target_block| {
                const consumes_break_rl = try astrl.expr(rhs, block, target_block.ri);
                if (consumes_break_rl) target_block.consumes_res_ptr = true;
            } else {
                // No corresponding scope to break from - AstGen will emit an error.
                _ = try astrl.expr(rhs, block, ResultInfo.none);
            }

            return false;
        },

        .array_type => {
            const lhs, const rhs = tree.nodeData(node).node_and_node;
            _ = try astrl.expr(lhs, block, ResultInfo.type_only);
            _ = try astrl.expr(rhs, block, ResultInfo.type_only);
            return false;
        },
        .array_type_sentinel => {
            const len_expr, const extra_index = tree.nodeData(node).node_and_extra;
            const extra = tree.extraData(extra_index, Ast.Node.ArrayTypeSentinel);
            _ = try astrl.expr(len_expr, block, ResultInfo.type_only);
            _ = try astrl.expr(extra.elem_type, block, ResultInfo.type_only);
            _ = try astrl.expr(extra.sentinel, block, ResultInfo.type_only);
            return false;
        },
        .array_access => {
            const lhs, const rhs = tree.nodeData(node).node_and_node;
            _ = try astrl.expr(lhs, block, ResultInfo.none);
            _ = try astrl.expr(rhs, block, ResultInfo.type_only);
            return false;
        },
        .@"comptime" => {
            // AstGen will emit an error if the scope is already comptime, so we can assume it is
            // not. This means the result location is not forwarded.
            _ = try astrl.expr(tree.nodeData(node).node, block, ResultInfo.none);
            return false;
        },
        .@"switch", .switch_comma => {
            const operand_node, const extra_index = tree.nodeData(node).node_and_extra;
            const case_nodes = tree.extraDataSlice(tree.extraData(extra_index, Ast.Node.SubRange), Ast.Node.Index);

            _ = try astrl.expr(operand_node, block, ResultInfo.none);

            var any_prong_consumed_rl = false;
            for (case_nodes) |case_node| {
                const case = tree.fullSwitchCase(case_node).?;
                for (case.ast.values) |item_node| {
                    if (tree.nodeTag(item_node) == .switch_range) {
                        const lhs, const rhs = tree.nodeData(item_node).node_and_node;
                        _ = try astrl.expr(lhs, block, ResultInfo.none);
                        _ = try astrl.expr(rhs, block, ResultInfo.none);
                    } else {
                        _ = try astrl.expr(item_node, block, ResultInfo.none);
                    }
                }
                if (try astrl.expr(case.ast.target_expr, block, ri)) {
                    any_prong_consumed_rl = true;
                }
            }
            if (any_prong_consumed_rl) {
                try astrl.nodes_need_rl.putNoClobber(astrl.gpa, node, {});
            }
            return any_prong_consumed_rl;
        },
        .@"suspend" => {
            _ = try astrl.expr(tree.nodeData(node).node, block, ResultInfo.none);
            return false;
        },
        .@"resume" => {
            _ = try astrl.expr(tree.nodeData(node).node, block, ResultInfo.none);
            return false;
        },

        .array_init_one,
        .array_init_one_comma,
        .array_init_dot_two,
        .array_init_dot_two_comma,
        .array_init_dot,
        .array_init_dot_comma,
        .array_init,
        .array_init_comma,
        => {
            var buf: [2]Ast.Node.Index = undefined;
            const full = tree.fullArrayInit(&buf, node).?;

            if (full.ast.type_expr.unwrap()) |type_expr| {
                // Explicitly typed init does not participate in RLS
                _ = try astrl.expr(type_expr, block, ResultInfo.none);
                for (full.ast.elements) |elem_init| {
                    _ = try astrl.expr(elem_init, block, ResultInfo.type_only);
                }
                return false;
            }

            if (ri.have_type) {
                // Always forward type information
                // If we have a result pointer, we use and forward it
                for (full.ast.elements) |elem_init| {
                    _ = try astrl.expr(elem_init, block, ri);
                }
                return ri.have_ptr;
            } else {
                // Untyped init does not consume result location
                for (full.ast.elements) |elem_init| {
                    _ = try astrl.expr(elem_init, block, ResultInfo.none);
                }
                return false;
            }
        },

        .struct_init_one,
        .struct_init_one_comma,
        .struct_init_dot_two,
        .struct_init_dot_two_comma,
        .struct_init_dot,
        .struct_init_dot_comma,
        .struct_init,
        .struct_init_comma,
        => {
            var buf: [2]Ast.Node.Index = undefined;
            const full = tree.fullStructInit(&buf, node).?;

            if (full.ast.type_expr.unwrap()) |type_expr| {
                // Explicitly typed init does not participate in RLS
                _ = try astrl.expr(type_expr, block, ResultInfo.none);
                for (full.ast.fields) |field_init| {
                    _ = try astrl.expr(field_init, block, ResultInfo.type_only);
                }
                return false;
            }

            if (ri.have_type) {
                // Always forward type information
                // If we have a result pointer, we use and forward it
                for (full.ast.fields) |field_init| {
                    _ = try astrl.expr(field_init, block, ri);
                }
                return ri.have_ptr;
            } else {
                // Untyped init does not consume result location
                for (full.ast.fields) |field_init| {
                    _ = try astrl.expr(field_init, block, ResultInfo.none);
                }
                return false;
            }
        },

        .fn_proto_simple,
        .fn_proto_multi,
        .fn_proto_one,
        .fn_proto,
        .fn_decl,
        => |tag| {
            var buf: [1]Ast.Node.Index = undefined;
            const full = tree.fullFnProto(&buf, node).?;
            const body_node = if (tag == .fn_decl) tree.nodeData(node).node_and_node[1].toOptional() else .none;
            {
                var it = full.iterate(tree);
                while (it.next()) |param| {
                    if (param.anytype_ellipsis3 == null) {
                        const type_expr = param.type_expr.?;
                        _ = try astrl.expr(type_expr, block, ResultInfo.type_only);
                    }
                }
            }
            if (full.ast.align_expr.unwrap()) |align_expr| {
                _ = try astrl.expr(align_expr, block, ResultInfo.type_only);
            }
            if (full.ast.addrspace_expr.unwrap()) |addrspace_expr| {
                _ = try astrl.expr(addrspace_expr, block, ResultInfo.type_only);
            }
            if (full.ast.section_expr.unwrap()) |section_expr| {
                _ = try astrl.expr(section_expr, block, ResultInfo.type_only);
            }
            if (full.ast.callconv_expr.unwrap()) |callconv_expr| {
                _ = try astrl.expr(callconv_expr, block, ResultInfo.type_only);
            }
            const return_type = full.ast.return_type.unwrap().?;
            _ = try astrl.expr(return_type, block, ResultInfo.type_only);
            if (body_node.unwrap()) |body| {
                _ = try astrl.expr(body, block, ResultInfo.none);
            }
            return false;
        },
    }
}