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.

findTrackableInner

Zir.findTrackableInner
fn findTrackableInner(
    zir: Zir,
    gpa: Allocator,
    contents: *DeclContents,
    defers: *std.AutoHashMapUnmanaged(u32, void),
    inst: Inst.Index,
) Allocator.Error!void

File

lib/std/zig/Zir.zig:4119

Code

fn findTrackableInner(
    zir: Zir,
    gpa: Allocator,
    contents: *DeclContents,
    defers: *std.AutoHashMapUnmanaged(u32, void),
    inst: Inst.Index,
) Allocator.Error!void {
    comptime assert(Zir.inst_tracking_version == 0);

    const tags = zir.instructions.items(.tag);
    const datas = zir.instructions.items(.data);

    switch (tags[@backingInt(inst)]) {
        .declaration => unreachable,

        // Boring instruction tags first. These have no body and are not declarations or type declarations.
        .add,
        .addwrap,
        .add_sat,
        .add_unsafe,
        .sub,
        .subwrap,
        .sub_sat,
        .mul,
        .mulwrap,
        .mul_sat,
        .div_exact,
        .div_floor,
        .div_ceil,
        .div_trunc,
        .mod,
        .rem,
        .mod_rem,
        .shl,
        .shl_exact,
        .shl_sat,
        .shr,
        .shr_exact,
        .param_anytype,
        .param_anytype_comptime,
        .array_cat,
        .array_type,
        .array_type_sentinel,
        .reify_int,
        .vector_type,
        .elem_type,
        .indexable_ptr_elem_type,
        .splat_op_result_ty,
        .from_backing_int_arg_ty,
        .indexable_ptr_len,
        .anyframe_type,
        .as_node,
        .as_shift_operand,
        .bit_and,
        .bitcast,
        .bit_not,
        .bit_or,
        .bool_not,
        .bool_br_and,
        .bool_br_or,
        .@"break",
        .break_inline,
        .switch_continue,
        .check_comptime_control_flow,
        .builtin_call,
        .cmp_lt,
        .cmp_lte,
        .cmp_eq,
        .cmp_gte,
        .cmp_gt,
        .cmp_neq,
        .error_set_decl,
        .dbg_stmt,
        .dbg_var_ptr,
        .dbg_var_val,
        .decl_ref,
        .decl_val,
        .load,
        .div,
        .elem_ptr_node,
        .elem_ptr,
        .elem_ptr_load,
        .elem_val,
        .elem_val_imm,
        .ensure_result_used,
        .ensure_result_non_error,
        .ensure_err_union_payload_void,
        .error_union_type,
        .error_value,
        .@"export",
        .field_ptr,
        .field_ptr_load,
        .field_ptr_named,
        .field_ptr_named_load,
        .import,
        .int,
        .int_big,
        .float,
        .float128,
        .int_type,
        .is_non_null,
        .is_non_null_ptr,
        .is_non_err,
        .is_non_err_ptr,
        .ret_is_non_err,
        .repeat,
        .repeat_inline,
        .for_len,
        .merge_error_sets,
        .ref,
        .deref,
        .ref_deref,
        .ret_node,
        .ret_load,
        .ret_implicit,
        .ret_err_value,
        .ret_ptr,
        .ret_type,
        .ptr_type,
        .slice_start,
        .slice_end,
        .slice_sentinel,
        .slice_length,
        .slice_sentinel_ty,
        .store_node,
        .store_to_inferred_ptr,
        .str,
        .negate,
        .negate_wrap,
        .typeof,
        .typeof_log2_int_type,
        .@"unreachable",
        .xor,
        .optional_type,
        .optional_payload_safe,
        .optional_payload_unsafe,
        .optional_payload_safe_ptr,
        .optional_payload_unsafe_ptr,
        .err_union_payload_unsafe,
        .err_union_payload_unsafe_ptr,
        .err_union_code,
        .err_union_code_ptr,
        .enum_literal,
        .decl_literal,
        .decl_literal_no_coerce,
        .validate_destructure,
        .field_type_ref,
        .opt_eu_base_ptr_init,
        .coerce_ptr_elem_ty,
        .validate_ref_ty,
        .validate_const,
        .struct_init_empty,
        .struct_init_empty_result,
        .struct_init_empty_ref_result,
        .validate_struct_init_ty,
        .validate_struct_init_result_ty,
        .validate_ptr_struct_init,
        .struct_init_field_type,
        .struct_init_field_ptr,
        .array_init_anon,
        .array_init,
        .array_init_ref,
        .validate_array_init_ty,
        .validate_array_init_result_ty,
        .validate_array_init_ref_ty,
        .validate_ptr_array_init,
        .array_init_elem_type,
        .array_init_elem_ptr,
        .union_init,
        .type_info,
        .size_of,
        .bit_size_of,
        .int_from_ptr,
        .compile_error,
        .set_eval_branch_quota,
        .int_from_enum,
        .align_of,
        .int_from_bool,
        .embed_file,
        .error_name,
        .panic,
        .trap,
        .set_runtime_safety,
        .sqrt,
        .sin,
        .cos,
        .tan,
        .exp,
        .exp2,
        .log,
        .log2,
        .log10,
        .abs,
        .floor,
        .ceil,
        .trunc,
        .round,
        .tag_name,
        .type_name,
        .frame_type,
        .int_from_float,
        .float_from_int,
        .ptr_from_int,
        .enum_from_int,
        .backing_int,
        .from_backing_int,
        .float_cast,
        .int_cast,
        .ptr_cast,
        .truncate,
        .has_decl,
        .has_field,
        .clz,
        .ctz,
        .pop_count,
        .byte_swap,
        .bit_reverse,
        .bit_offset_of,
        .offset_of,
        .splat,
        .reduce,
        .shuffle,
        .atomic_load,
        .atomic_rmw,
        .atomic_store,
        .mul_add,
        .memcpy,
        .memset,
        .memmove,
        .min,
        .max,
        .alloc,
        .alloc_mut,
        .alloc_comptime_mut,
        .alloc_inferred,
        .alloc_inferred_mut,
        .alloc_inferred_comptime,
        .alloc_inferred_comptime_mut,
        .resolve_inferred_alloc,
        .make_ptr_const,
        .@"resume",
        .save_err_ret_index,
        .restore_err_ret_index_unconditional,
        .restore_err_ret_index_fn_entry,
        => return,

        // Struct initializations need tracking, as they may create anonymous struct types.
        .struct_init,
        .struct_init_ref,
        .struct_init_anon,
        => return contents.other.append(gpa, inst),

        .extended => {
            const extended = datas[@backingInt(inst)].extended;
            switch (extended.opcode) {
                .value_placeholder => unreachable,

                // Once again, we start with the boring tags.
                .this,
                .ret_addr,
                .builtin_src,
                .error_return_trace,
                .frame,
                .frame_address,
                .alloc,
                .builtin_extern,
                .@"asm",
                .asm_expr,
                .compile_log,
                .min_multi,
                .max_multi,
                .add_with_overflow,
                .sub_with_overflow,
                .mul_with_overflow,
                .shl_with_overflow,
                .round_op,
                .wasm_memory_size,
                .wasm_memory_grow,
                .prefetch,
                .set_float_mode,
                .error_cast,
                .breakpoint,
                .disable_instrumentation,
                .disable_intrinsics,
                .select,
                .int_from_error,
                .error_from_int,
                .reify_slice_arg_ty,
                .reify_enum_value_slice_ty,
                .reify_pointer_sentinel_ty,
                .reify_tuple,
                .reify_pointer,
                .reify_fn,
                .cmpxchg,
                .c_va_arg,
                .c_va_copy,
                .c_va_end,
                .c_va_start,
                .ptr_cast_full,
                .ptr_cast_no_dest,
                .work_item_id,
                .work_group_size,
                .work_group_id,
                .in_comptime,
                .restore_err_ret_index,
                .closure_get,
                .field_parent_ptr,
                .std_lang_value,
                .branch_hint,
                .inplace_arith_result_ty,
                .tuple_decl,
                .dbg_empty_stmt,
                .astgen_error,
                .float_op_result_ty,
                .round_op_ty,
                => return,

                // `@TypeOf` has a body.
                .typeof_peer => {
                    const extra = zir.extraData(Zir.Inst.TypeOfPeer, extended.operand);
                    const body = zir.bodySlice(extra.data.body_index, extra.data.body_len);
                    try zir.findTrackableBody(gpa, contents, defers, body);
                },

                // Reifications need tracking.
                .reify_enum,
                .reify_struct,
                .reify_union,
                .reify_spirv_type,
                => return contents.other.append(gpa, inst),

                // Type declarations need tracking.
                .struct_decl,
                .union_decl,
                .enum_decl,
                .opaque_decl,
                => return contents.type_decls.append(gpa, inst),
            }
        },

        // Functions instructions are interesting and have a body.
        .func,
        .func_inferred,
        => {
            const inst_data = datas[@backingInt(inst)].pl_node;
            const extra = zir.extraData(Inst.Func, inst_data.payload_index);

            if (extra.data.body_len == 0) {
                // This is just a prototype. No need to track.
                assert(extra.data.ret_ty.body_len < 2);
                return;
            }

            assert(contents.func_decl == null);
            contents.func_decl = inst;

            var extra_index: usize = extra.end;
            switch (extra.data.ret_ty.body_len) {
                0 => {},
                1 => extra_index += 1,
                else => {
                    const body = zir.bodySlice(extra_index, extra.data.ret_ty.body_len);
                    extra_index += body.len;
                    try zir.findTrackableBody(gpa, contents, defers, body);
                },
            }
            const body = zir.bodySlice(extra_index, extra.data.body_len);
            return zir.findTrackableBody(gpa, contents, defers, body);
        },
        .func_fancy => {
            const inst_data = datas[@backingInt(inst)].pl_node;
            const extra = zir.extraData(Inst.FuncFancy, inst_data.payload_index);

            if (extra.data.body_len == 0) {
                // This is just a prototype. No need to track.
                assert(!extra.data.bits.has_cc_body);
                assert(!extra.data.bits.has_ret_ty_body);
                return;
            }

            assert(contents.func_decl == null);
            contents.func_decl = inst;

            var extra_index: usize = extra.end;

            if (extra.data.bits.has_cc_body) {
                const body_len = zir.extra[extra_index];
                extra_index += 1;
                const body = zir.bodySlice(extra_index, body_len);
                try zir.findTrackableBody(gpa, contents, defers, body);
                extra_index += body.len;
            } else if (extra.data.bits.has_cc_ref) {
                extra_index += 1;
            }

            if (extra.data.bits.has_ret_ty_body) {
                const body_len = zir.extra[extra_index];
                extra_index += 1;
                const body = zir.bodySlice(extra_index, body_len);
                try zir.findTrackableBody(gpa, contents, defers, body);
                extra_index += body.len;
            } else if (extra.data.bits.has_ret_ty_ref) {
                extra_index += 1;
            }

            extra_index += @intFromBool(extra.data.bits.has_any_noalias);

            const body = zir.bodySlice(extra_index, extra.data.body_len);
            return zir.findTrackableBody(gpa, contents, defers, body);
        },

        // Block instructions, recurse over the bodies.

        .block,
        .block_inline,
        .typeof_builtin,
        .loop,
        => {
            const inst_data = datas[@backingInt(inst)].pl_node;
            const extra = zir.extraData(Inst.Block, inst_data.payload_index);
            const body = zir.bodySlice(extra.end, extra.data.body_len);
            return zir.findTrackableBody(gpa, contents, defers, body);
        },
        .block_comptime => {
            const inst_data = datas[@backingInt(inst)].pl_node;
            const extra = zir.extraData(Inst.BlockComptime, inst_data.payload_index);
            const body = zir.bodySlice(extra.end, extra.data.body_len);
            return zir.findTrackableBody(gpa, contents, defers, body);
        },
        .condbr, .condbr_inline => {
            const inst_data = datas[@backingInt(inst)].pl_node;
            const extra = zir.extraData(Inst.CondBr, inst_data.payload_index);
            const then_body = zir.bodySlice(extra.end, extra.data.then_body_len);
            const else_body = zir.bodySlice(extra.end + then_body.len, extra.data.else_body_len);
            try zir.findTrackableBody(gpa, contents, defers, then_body);
            try zir.findTrackableBody(gpa, contents, defers, else_body);
        },
        .@"try", .try_ptr => {
            const inst_data = datas[@backingInt(inst)].pl_node;
            const extra = zir.extraData(Inst.Try, inst_data.payload_index);
            const body = zir.bodySlice(extra.end, extra.data.body_len);
            try zir.findTrackableBody(gpa, contents, defers, body);
        },

        .switch_block,
        .switch_block_ref,
        .switch_block_err_union,
        => {
            const zir_switch = zir.getSwitchBlock(inst);
            if (zir_switch.non_err_case) |non_err_case| {
                try zir.findTrackableBody(gpa, contents, defers, non_err_case.body);
            }
            if (zir_switch.else_case) |else_case| {
                try zir.findTrackableBody(gpa, contents, defers, else_case.body);
            }
            var extra_index = zir_switch.end;
            var case_it = zir_switch.iterateCases();
            while (case_it.next()) |case| {
                const prong_body = zir.bodySlice(extra_index, case.prong_info.body_len);
                extra_index += prong_body.len;
                try zir.findTrackableBody(gpa, contents, defers, prong_body);
                for (case.item_infos) |item_info| {
                    if (item_info.bodyLen()) |body_len| {
                        const item_body = zir.bodySlice(extra_index, body_len);
                        extra_index += item_body.len;
                        try zir.findTrackableBody(gpa, contents, defers, item_body);
                    }
                }
                for (case.range_infos) |range_info| {
                    if (range_info[0].bodyLen()) |body_len| {
                        const first_body = zir.bodySlice(extra_index, body_len);
                        extra_index += first_body.len;
                        try zir.findTrackableBody(gpa, contents, defers, first_body);
                    }
                    if (range_info[1].bodyLen()) |body_len| {
                        const last_body = zir.bodySlice(extra_index, body_len);
                        extra_index += last_body.len;
                        try zir.findTrackableBody(gpa, contents, defers, last_body);
                    }
                }
            }
        },

        .suspend_block => @panic("TODO iterate suspend block"),

        .param, .param_comptime => {
            const inst_data = datas[@backingInt(inst)].pl_tok;
            const extra = zir.extraData(Inst.Param, inst_data.payload_index);
            const body = zir.bodySlice(extra.end, extra.data.type.body_len);
            try zir.findTrackableBody(gpa, contents, defers, body);
        },

        inline .call, .field_call => |tag| {
            const inst_data = datas[@backingInt(inst)].pl_node;
            const extra = zir.extraData(switch (tag) {
                .call => Inst.Call,
                .field_call => Inst.FieldCall,
                else => unreachable,
            }, inst_data.payload_index);
            // It's easiest to just combine all the arg bodies into one body, like we do above for `struct_decl`.
            const args_len = extra.data.flags.args_len;
            if (args_len > 0) {
                const first_arg_start_off = args_len;
                const final_arg_end_off = zir.extra[extra.end + args_len - 1];
                const args_body = zir.bodySlice(extra.end + first_arg_start_off, final_arg_end_off - first_arg_start_off);
                try zir.findTrackableBody(gpa, contents, defers, args_body);
            }
        },
        .@"defer" => {
            const inst_data = datas[@backingInt(inst)].@"defer";
            const gop = try defers.getOrPut(gpa, inst_data.index);
            if (!gop.found_existing) {
                const body = zir.bodySlice(inst_data.index, inst_data.len);
                try zir.findTrackableBody(gpa, contents, defers, body);
            }
        },
    }
}