From 89f02d1c107a159dc6433863c7d9167872b4c0c8 Mon Sep 17 00:00:00 2001 From: mlugg Date: Fri, 16 Aug 2024 17:34:30 +0100 Subject: [PATCH] std.zig.Zir: fix declaration traversal The old logic here had bitrotted, largely because there were some incorrect `else` cases. This is now implemented correctly for all current ZIR instructions. This prevents instructions being lost in incremental updates, which is important for updates to be minimal. --- lib/std/zig/Zir.zig | 660 ++++++++++++++++++++++++++++++++++++++++---- src/Zcu.zig | 16 +- 2 files changed, 614 insertions(+), 62 deletions(-) diff --git a/lib/std/zig/Zir.zig b/lib/std/zig/Zir.zig index 051a799db6..7349677a22 100644 --- a/lib/std/zig/Zir.zig +++ b/lib/std/zig/Zir.zig @@ -603,7 +603,7 @@ pub const Inst = struct { /// Uses the `un_node` field. typeof, /// Implements `@TypeOf` for one operand. - /// Uses the `pl_node` field. + /// Uses the `pl_node` field. Payload is `Block`. typeof_builtin, /// Given a value, look at the type of it, which must be an integer type. /// Returns the integer type for the RHS of a shift operation. @@ -2727,6 +2727,9 @@ pub const Inst = struct { field_name_start: NullTerminatedString, }; + /// There is a body of instructions at `extra[body_index..][0..body_len]`. + /// Trailing: + /// 0. operand: Ref // for each `operands_len` pub const TypeOfPeer = struct { src_node: i32, body_len: u32, @@ -2844,6 +2847,40 @@ pub const Inst = struct { src_line: u32, }; + /// Trailing: + /// 0. multi_cases_len: u32 // if `has_multi_cases` + /// 1. err_capture_inst: u32 // if `any_uses_err_capture` + /// 2. non_err_body { + /// info: ProngInfo, + /// inst: Index // for every `info.body_len` + /// } + /// 3. else_body { // if `has_else` + /// info: ProngInfo, + /// inst: Index // for every `info.body_len` + /// } + /// 4. scalar_cases: { // for every `scalar_cases_len` + /// item: Ref, + /// info: ProngInfo, + /// inst: Index // for every `info.body_len` + /// } + /// 5. multi_cases: { // for every `multi_cases_len` + /// items_len: u32, + /// ranges_len: u32, + /// info: ProngInfo, + /// item: Ref // for every `items_len` + /// ranges: { // for every `ranges_len` + /// item_first: Ref, + /// item_last: Ref, + /// } + /// inst: Index // for every `info.body_len` + /// } + /// + /// When analyzing a case body, the switch instruction itself refers to the + /// captured error, or to the success value in `non_err_body`. Whether this + /// is captured by reference or by value depends on whether the `byref` bit + /// is set for the corresponding body. `err_capture_inst` refers to the error + /// capture outside of the `switch`, i.e. `err` in + /// `x catch |err| switch (err) { ... }`. pub const SwitchBlockErrUnion = struct { operand: Ref, bits: Bits, @@ -3153,7 +3190,7 @@ pub const Inst = struct { /// 1. captures_len: u32 // if has_captures_len /// 2. body_len: u32, // if has_body_len /// 3. fields_len: u32, // if has_fields_len - /// 4. decls_len: u37, // if has_decls_len + /// 4. decls_len: u32, // if has_decls_len /// 5. capture: Capture // for every captures_len /// 6. decl: Index, // for every decls_len; points to a `declaration` instruction /// 7. inst: Index // for every body_len @@ -3624,33 +3661,492 @@ pub fn declIterator(zir: Zir, decl_inst: Zir.Inst.Index) DeclIterator { } } -/// The iterator would have to allocate memory anyway to iterate. So here we populate -/// an ArrayList as the result. -pub fn findDecls(zir: Zir, list: *std.ArrayList(Inst.Index), decl_inst: Zir.Inst.Index) !void { +/// Find all type declarations, recursively, within a `declaration` instruction. Does not recurse through +/// said type declarations' declarations; to find all declarations, call this function on the declarations +/// of the discovered types recursively. +/// The iterator would have to allocate memory anyway to iterate, so an `ArrayList` is populated as the result. +pub fn findDecls(zir: Zir, gpa: Allocator, list: *std.ArrayListUnmanaged(Inst.Index), decl_inst: Zir.Inst.Index) !void { list.clearRetainingCapacity(); const declaration, const extra_end = zir.getDeclaration(decl_inst); const bodies = declaration.getBodies(extra_end, zir); - try zir.findDeclsBody(list, bodies.value_body); - if (bodies.align_body) |b| try zir.findDeclsBody(list, b); - if (bodies.linksection_body) |b| try zir.findDeclsBody(list, b); - if (bodies.addrspace_body) |b| try zir.findDeclsBody(list, b); + // `defer` instructions duplicate the same body arbitrarily many times, but we only want to traverse + // their contents once per defer. So, we store the extra index of the body here to deduplicate. + var found_defers: std.AutoHashMapUnmanaged(u32, void) = .{}; + defer found_defers.deinit(gpa); + + try zir.findDeclsBody(gpa, list, &found_defers, bodies.value_body); + if (bodies.align_body) |b| try zir.findDeclsBody(gpa, list, &found_defers, b); + if (bodies.linksection_body) |b| try zir.findDeclsBody(gpa, list, &found_defers, b); + if (bodies.addrspace_body) |b| try zir.findDeclsBody(gpa, list, &found_defers, b); } fn findDeclsInner( zir: Zir, - list: *std.ArrayList(Inst.Index), + gpa: Allocator, + list: *std.ArrayListUnmanaged(Inst.Index), + defers: *std.AutoHashMapUnmanaged(u32, void), inst: Inst.Index, ) Allocator.Error!void { const tags = zir.instructions.items(.tag); const datas = zir.instructions.items(.data); switch (tags[@intFromEnum(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_trunc, + .mod, + .rem, + .mod_rem, + .shl, + .shl_exact, + .shl_sat, + .shr, + .shr_exact, + .param_anytype, + .param_anytype_comptime, + .array_cat, + .array_mul, + .array_type, + .array_type_sentinel, + .vector_type, + .elem_type, + .indexable_ptr_elem_type, + .vector_elem_type, + .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, + .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_val_node, + .elem_val, + .elem_val_imm, + .ensure_result_used, + .ensure_result_non_error, + .ensure_err_union_payload_void, + .error_union_type, + .error_value, + .@"export", + .export_value, + .field_ptr, + .field_val, + .field_ptr_named, + .field_val_named, + .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, + .ret_node, + .ret_load, + .ret_implicit, + .ret_err_value, + .ret_err_value_code, + .ret_ptr, + .ret_type, + .ptr_type, + .slice_start, + .slice_end, + .slice_sentinel, + .slice_length, + .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, + .validate_deref, + .validate_destructure, + .field_type_ref, + .opt_eu_base_ptr_init, + .coerce_ptr_elem_ty, + .validate_ref_ty, + .struct_init_empty, + .struct_init_empty_result, + .struct_init_empty_ref_result, + .struct_init_anon, + .struct_init, + .struct_init_ref, + .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, + .frame_size, + .int_from_float, + .float_from_int, + .ptr_from_int, + .enum_from_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, + .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", + .@"await", + .save_err_ret_index, + .restore_err_ret_index_unconditional, + .restore_err_ret_index_fn_entry, + => return, + + .extended => { + const extended = datas[@intFromEnum(inst)].extended; + switch (extended.opcode) { + .value_placeholder => unreachable, + + // Once again, we start with the boring tags. + .variable, + .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, + .c_undef, + .c_include, + .c_define, + .wasm_memory_size, + .wasm_memory_grow, + .prefetch, + .fence, + .set_float_mode, + .set_align_stack, + .set_cold, + .error_cast, + .await_nosuspend, + .breakpoint, + .disable_instrumentation, + .select, + .int_from_error, + .error_from_int, + .builtin_async_call, + .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, + => 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.findDeclsBody(gpa, list, defers, body); + }, + + // Reifications and opaque declarations need tracking, but have no body. + .reify, .opaque_decl => return list.append(gpa, inst), + + // Struct declarations need tracking and have bodies. + .struct_decl => { + try list.append(gpa, inst); + + const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small); + const extra = zir.extraData(Zir.Inst.StructDecl, extended.operand); + var extra_index = extra.end; + const captures_len = if (small.has_captures_len) blk: { + const captures_len = zir.extra[extra_index]; + extra_index += 1; + break :blk captures_len; + } else 0; + const fields_len = if (small.has_fields_len) blk: { + const fields_len = zir.extra[extra_index]; + extra_index += 1; + break :blk fields_len; + } else 0; + const decls_len = if (small.has_decls_len) blk: { + const decls_len = zir.extra[extra_index]; + extra_index += 1; + break :blk decls_len; + } else 0; + extra_index += captures_len; + if (small.has_backing_int) { + const backing_int_body_len = zir.extra[extra_index]; + extra_index += 1; + if (backing_int_body_len == 0) { + extra_index += 1; // backing_int_ref + } else { + const body = zir.bodySlice(extra_index, backing_int_body_len); + extra_index += backing_int_body_len; + try zir.findDeclsBody(gpa, list, defers, body); + } + } + extra_index += decls_len; + + // This ZIR is structured in a slightly awkward way, so we have to split up the iteration. + // `extra_index` iterates `flags` (bags of bits). + // `fields_extra_index` iterates `fields`. + // We accumulate the total length of bodies into `total_bodies_len`. This is sufficient because + // the bodies are packed together in `extra` and we only need to traverse their instructions (we + // don't really care about the structure). + + const bits_per_field = 4; + const fields_per_u32 = 32 / bits_per_field; + const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable; + var cur_bit_bag: u32 = undefined; + + var fields_extra_index = extra_index + bit_bags_count; + var total_bodies_len: u32 = 0; + + for (0..fields_len) |field_i| { + if (field_i % fields_per_u32 == 0) { + cur_bit_bag = zir.extra[extra_index]; + extra_index += 1; + } + + const has_align = @as(u1, @truncate(cur_bit_bag)) != 0; + cur_bit_bag >>= 1; + const has_init = @as(u1, @truncate(cur_bit_bag)) != 0; + cur_bit_bag >>= 2; // also skip `is_comptime`; we don't care + const has_type_body = @as(u1, @truncate(cur_bit_bag)) != 0; + cur_bit_bag >>= 1; + + fields_extra_index += @intFromBool(!small.is_tuple); // field_name + fields_extra_index += 1; // doc_comment + + if (has_type_body) { + const field_type_body_len = zir.extra[fields_extra_index]; + total_bodies_len += field_type_body_len; + } + fields_extra_index += 1; // field_type or field_type_body_len + + if (has_align) { + const align_body_len = zir.extra[fields_extra_index]; + fields_extra_index += 1; + total_bodies_len += align_body_len; + } + + if (has_init) { + const init_body_len = zir.extra[fields_extra_index]; + fields_extra_index += 1; + total_bodies_len += init_body_len; + } + } + + // Now, `fields_extra_index` points to `bodies`. Let's treat this as one big body. + const merged_bodies = zir.bodySlice(fields_extra_index, total_bodies_len); + try zir.findDeclsBody(gpa, list, defers, merged_bodies); + }, + + // Union declarations need tracking and have a body. + .union_decl => { + try list.append(gpa, inst); + + const small: Zir.Inst.UnionDecl.Small = @bitCast(extended.small); + const extra = zir.extraData(Zir.Inst.UnionDecl, extended.operand); + var extra_index = extra.end; + extra_index += @intFromBool(small.has_tag_type); + const captures_len = if (small.has_captures_len) blk: { + const captures_len = zir.extra[extra_index]; + extra_index += 1; + break :blk captures_len; + } else 0; + const body_len = if (small.has_body_len) blk: { + const body_len = zir.extra[extra_index]; + extra_index += 1; + break :blk body_len; + } else 0; + extra_index += @intFromBool(small.has_fields_len); + const decls_len = if (small.has_decls_len) blk: { + const decls_len = zir.extra[extra_index]; + extra_index += 1; + break :blk decls_len; + } else 0; + extra_index += captures_len; + extra_index += decls_len; + const body = zir.bodySlice(extra_index, body_len); + try zir.findDeclsBody(gpa, list, defers, body); + }, + + // Enum declarations need tracking and have a body. + .enum_decl => { + try list.append(gpa, inst); + + const small: Zir.Inst.EnumDecl.Small = @bitCast(extended.small); + const extra = zir.extraData(Zir.Inst.EnumDecl, extended.operand); + var extra_index = extra.end; + extra_index += @intFromBool(small.has_tag_type); + const captures_len = if (small.has_captures_len) blk: { + const captures_len = zir.extra[extra_index]; + extra_index += 1; + break :blk captures_len; + } else 0; + const body_len = if (small.has_body_len) blk: { + const body_len = zir.extra[extra_index]; + extra_index += 1; + break :blk body_len; + } else 0; + extra_index += @intFromBool(small.has_fields_len); + const decls_len = if (small.has_decls_len) blk: { + const decls_len = zir.extra[extra_index]; + extra_index += 1; + break :blk decls_len; + } else 0; + extra_index += captures_len; + extra_index += decls_len; + const body = zir.bodySlice(extra_index, body_len); + try zir.findDeclsBody(gpa, list, defers, body); + }, + } + }, + // Functions instructions are interesting and have a body. .func, .func_inferred, => { - try list.append(inst); + try list.append(gpa, inst); const inst_data = datas[@intFromEnum(inst)].pl_node; const extra = zir.extraData(Inst.Func, inst_data.payload_index); @@ -3661,14 +4157,14 @@ fn findDeclsInner( else => { const body = zir.bodySlice(extra_index, extra.data.ret_body_len); extra_index += body.len; - try zir.findDeclsBody(list, body); + try zir.findDeclsBody(gpa, list, defers, body); }, } const body = zir.bodySlice(extra_index, extra.data.body_len); - return zir.findDeclsBody(list, body); + return zir.findDeclsBody(gpa, list, defers, body); }, .func_fancy => { - try list.append(inst); + try list.append(gpa, inst); const inst_data = datas[@intFromEnum(inst)].pl_node; const extra = zir.extraData(Inst.FuncFancy, inst_data.payload_index); @@ -3679,7 +4175,7 @@ fn findDeclsInner( const body_len = zir.extra[extra_index]; extra_index += 1; const body = zir.bodySlice(extra_index, body_len); - try zir.findDeclsBody(list, body); + try zir.findDeclsBody(gpa, list, defers, body); extra_index += body.len; } else if (extra.data.bits.has_align_ref) { extra_index += 1; @@ -3689,7 +4185,7 @@ fn findDeclsInner( const body_len = zir.extra[extra_index]; extra_index += 1; const body = zir.bodySlice(extra_index, body_len); - try zir.findDeclsBody(list, body); + try zir.findDeclsBody(gpa, list, defers, body); extra_index += body.len; } else if (extra.data.bits.has_addrspace_ref) { extra_index += 1; @@ -3699,7 +4195,7 @@ fn findDeclsInner( const body_len = zir.extra[extra_index]; extra_index += 1; const body = zir.bodySlice(extra_index, body_len); - try zir.findDeclsBody(list, body); + try zir.findDeclsBody(gpa, list, defers, body); extra_index += body.len; } else if (extra.data.bits.has_section_ref) { extra_index += 1; @@ -3709,7 +4205,7 @@ fn findDeclsInner( const body_len = zir.extra[extra_index]; extra_index += 1; const body = zir.bodySlice(extra_index, body_len); - try zir.findDeclsBody(list, body); + try zir.findDeclsBody(gpa, list, defers, body); extra_index += body.len; } else if (extra.data.bits.has_cc_ref) { extra_index += 1; @@ -3719,7 +4215,7 @@ fn findDeclsInner( const body_len = zir.extra[extra_index]; extra_index += 1; const body = zir.bodySlice(extra_index, body_len); - try zir.findDeclsBody(list, body); + try zir.findDeclsBody(gpa, list, defers, body); extra_index += body.len; } else if (extra.data.bits.has_ret_ty_ref) { extra_index += 1; @@ -3728,62 +4224,99 @@ fn findDeclsInner( extra_index += @intFromBool(extra.data.bits.has_any_noalias); const body = zir.bodySlice(extra_index, extra.data.body_len); - return zir.findDeclsBody(list, body); - }, - .extended => { - const extended = datas[@intFromEnum(inst)].extended; - switch (extended.opcode) { - - // Decl instructions are interesting but have no body. - // TODO yes they do have a body actually. recurse over them just like block instructions. - .struct_decl, - .union_decl, - .enum_decl, - .opaque_decl, - .reify, - => return list.append(inst), - - else => return, - } + return zir.findDeclsBody(gpa, list, defers, body); }, // Block instructions, recurse over the bodies. - .block, .block_comptime, .block_inline => { + .block, + .block_comptime, + .block_inline, + .c_import, + .typeof_builtin, + .loop, + => { const inst_data = datas[@intFromEnum(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.findDeclsBody(list, body); + return zir.findDeclsBody(gpa, list, defers, body); }, .condbr, .condbr_inline => { const inst_data = datas[@intFromEnum(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.findDeclsBody(list, then_body); - try zir.findDeclsBody(list, else_body); + try zir.findDeclsBody(gpa, list, defers, then_body); + try zir.findDeclsBody(gpa, list, defers, else_body); }, .@"try", .try_ptr => { const inst_data = datas[@intFromEnum(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.findDeclsBody(list, body); + try zir.findDeclsBody(gpa, list, defers, body); }, - .switch_block => return findDeclsSwitch(zir, list, inst), + .switch_block, .switch_block_ref => return zir.findDeclsSwitch(gpa, list, defers, inst, .normal), + .switch_block_err_union => return zir.findDeclsSwitch(gpa, list, defers, inst, .err_union), .suspend_block => @panic("TODO iterate suspend block"), - else => return, // Regular instruction, not interesting. + .param, .param_comptime => { + const inst_data = datas[@intFromEnum(inst)].pl_tok; + const extra = zir.extraData(Inst.Param, inst_data.payload_index); + const body = zir.bodySlice(extra.end, extra.data.body_len); + try zir.findDeclsBody(gpa, list, defers, body); + }, + + inline .call, .field_call => |tag| { + const inst_data = datas[@intFromEnum(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.findDeclsBody(gpa, list, defers, args_body); + } + }, + .@"defer" => { + const inst_data = datas[@intFromEnum(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.findDeclsBody(gpa, list, defers, body); + } + }, + .defer_err_code => { + const inst_data = datas[@intFromEnum(inst)].defer_err_code; + const extra = zir.extraData(Inst.DeferErrCode, inst_data.payload_index).data; + const gop = try defers.getOrPut(gpa, extra.index); + if (!gop.found_existing) { + const body = zir.bodySlice(extra.index, extra.len); + try zir.findDeclsBody(gpa, list, defers, body); + } + }, } } fn findDeclsSwitch( zir: Zir, - list: *std.ArrayList(Inst.Index), + gpa: Allocator, + list: *std.ArrayListUnmanaged(Inst.Index), + defers: *std.AutoHashMapUnmanaged(u32, void), inst: Inst.Index, + /// Distinguishes between `switch_block[_ref]` and `switch_block_err_union`. + comptime kind: enum { normal, err_union }, ) Allocator.Error!void { const inst_data = zir.instructions.items(.data)[@intFromEnum(inst)].pl_node; - const extra = zir.extraData(Inst.SwitchBlock, inst_data.payload_index); + const extra = zir.extraData(switch (kind) { + .normal => Inst.SwitchBlock, + .err_union => Inst.SwitchBlockErrUnion, + }, inst_data.payload_index); var extra_index: usize = extra.end; @@ -3793,18 +4326,35 @@ fn findDeclsSwitch( break :blk multi_cases_len; } else 0; - if (extra.data.bits.any_has_tag_capture) { + if (switch (kind) { + .normal => extra.data.bits.any_has_tag_capture, + .err_union => extra.data.bits.any_uses_err_capture, + }) { extra_index += 1; } - const special_prong = extra.data.bits.specialProng(); - if (special_prong != .none) { + const has_special = switch (kind) { + .normal => extra.data.bits.specialProng() != .none, + .err_union => has_special: { + // Handle `non_err_body` first. + const prong_info: Inst.SwitchBlock.ProngInfo = @bitCast(zir.extra[extra_index]); + extra_index += 1; + const body = zir.bodySlice(extra_index, prong_info.body_len); + extra_index += body.len; + + try zir.findDeclsBody(gpa, list, defers, body); + + break :has_special extra.data.bits.has_else; + }, + }; + + if (has_special) { const prong_info: Inst.SwitchBlock.ProngInfo = @bitCast(zir.extra[extra_index]); extra_index += 1; const body = zir.bodySlice(extra_index, prong_info.body_len); extra_index += body.len; - try zir.findDeclsBody(list, body); + try zir.findDeclsBody(gpa, list, defers, body); } { @@ -3816,7 +4366,7 @@ fn findDeclsSwitch( const body = zir.bodySlice(extra_index, prong_info.body_len); extra_index += body.len; - try zir.findDeclsBody(list, body); + try zir.findDeclsBody(gpa, list, defers, body); } } { @@ -3833,18 +4383,20 @@ fn findDeclsSwitch( const body = zir.bodySlice(extra_index, prong_info.body_len); extra_index += body.len; - try zir.findDeclsBody(list, body); + try zir.findDeclsBody(gpa, list, defers, body); } } } fn findDeclsBody( zir: Zir, - list: *std.ArrayList(Inst.Index), + gpa: Allocator, + list: *std.ArrayListUnmanaged(Inst.Index), + defers: *std.AutoHashMapUnmanaged(u32, void), body: []const Inst.Index, ) Allocator.Error!void { for (body) |member| { - try zir.findDeclsInner(list, member); + try zir.findDeclsInner(gpa, list, defers, member); } } @@ -4042,7 +4594,7 @@ pub fn getAssociatedSrcHash(zir: Zir, inst: Zir.Inst.Index) ?std.zig.SrcHash { return null; } const extra_index = extra.end + - 1 + + extra.data.ret_body_len + extra.data.body_len + @typeInfo(Inst.Func.SrcLocs).Struct.fields.len; return @bitCast([4]u32{ diff --git a/src/Zcu.zig b/src/Zcu.zig index 63feb2d00c..f191f6a5f1 100644 --- a/src/Zcu.zig +++ b/src/Zcu.zig @@ -2557,10 +2557,10 @@ pub fn mapOldZirToNew( }); // Used as temporary buffers for namespace declaration instructions - var old_decls = std.ArrayList(Zir.Inst.Index).init(gpa); - defer old_decls.deinit(); - var new_decls = std.ArrayList(Zir.Inst.Index).init(gpa); - defer new_decls.deinit(); + var old_decls: std.ArrayListUnmanaged(Zir.Inst.Index) = .{}; + defer old_decls.deinit(gpa); + var new_decls: std.ArrayListUnmanaged(Zir.Inst.Index) = .{}; + defer new_decls.deinit(gpa); while (match_stack.popOrNull()) |match_item| { // Match the namespace declaration itself @@ -2647,11 +2647,11 @@ pub fn mapOldZirToNew( // Match the `declaration` instruction try inst_map.put(gpa, old_decl_inst, new_decl_inst); - // Find namespace declarations within this declaration - try old_zir.findDecls(&old_decls, old_decl_inst); - try new_zir.findDecls(&new_decls, new_decl_inst); + // Find container type declarations within this declaration + try old_zir.findDecls(gpa, &old_decls, old_decl_inst); + try new_zir.findDecls(gpa, &new_decls, new_decl_inst); - // We don't have any smart way of matching up these namespace declarations, so we always + // We don't have any smart way of matching up these type declarations, so we always // correlate them based on source order. const n = @min(old_decls.items.len, new_decls.items.len); try match_stack.ensureUnusedCapacity(gpa, n);