From 7561f1e004f760510bd9af5e2d032df501cff808 Mon Sep 17 00:00:00 2001 From: Matthew Lugg Date: Mon, 2 Mar 2026 17:35:02 +0000 Subject: [PATCH] compiler,std: various little fixes --- lib/std/hash_map.zig | 6 +-- src/InternPool.zig | 16 +++++- src/Sema.zig | 75 ++++++++++++++-------------- src/Value.zig | 4 +- src/Zcu.zig | 5 ++ src/Zcu/PerThread.zig | 21 ++------ src/codegen/c.zig | 6 +-- src/codegen/llvm.zig | 2 +- src/link.zig | 5 +- src/link/Dwarf.zig | 110 ++++++++++++++++++++++++++++++++++++++---- 10 files changed, 174 insertions(+), 76 deletions(-) diff --git a/lib/std/hash_map.zig b/lib/std/hash_map.zig index 58c9397a6b..cb74cbc08b 100644 --- a/lib/std/hash_map.zig +++ b/lib/std/hash_map.zig @@ -1526,9 +1526,9 @@ pub fn HashMapUnmanaged( } comptime { - if (!builtin.strip_debug_info) _ = switch (builtin.zig_backend) { - .stage2_llvm => &dbHelper, - .stage2_x86_64 => KV, + if (!builtin.strip_debug_info) switch (builtin.zig_backend) { + .stage2_llvm => _ = &dbHelper, + .stage2_x86_64 => _ = @as(KV, undefined), else => {}, }; } diff --git a/src/InternPool.zig b/src/InternPool.zig index da95ed6cc7..7a561d6ca2 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -3334,11 +3334,17 @@ pub const LoadedStructType = struct { field_defaults: Index.Slice, field_aligns: Alignment.Slice, field_is_comptime_bits: ComptimeBits, + /// If `layout` is `.@"packed"`, this is `.empty`. field_runtime_order: RuntimeOrder.Slice, + /// If `layout` is `.@"packed"`, this is `.empty`. field_offsets: Offsets, + /// Only valid if `layout` is `.@"packed"`. packed_backing_int_type: Index, + /// Only valid if `layout` is *not* `.@"packed"`. class: TypeClass, + /// Only valid if `layout` is *not* `.@"packed"`. size: u32, + /// Only valid if `layout` is *not* `.@"packed"`. alignment: Alignment, pub const ComptimeBits = struct { @@ -3516,15 +3522,21 @@ pub const LoadedUnionType = struct { tag_usage: TagUsage, /// While `tag_usage` indicates whether the union should logically contain a tag, it may be /// omitted if the union layout is resolved as OPV or NPV. This field is `true` iff there is an - /// actual runtime tag in the union layout. + /// actual runtime tag, with one or more runtime bits, in the union layout. It is always `false` + /// if `layout` is not `.auto`. has_runtime_tag: bool, /// Even if `tag_usage == .none` and `has_runtime_tag == false`, this is still populated with /// the union's "hypothetical" tag type. enum_tag_type: Index, + /// Only valid if `layout` is `.@"packed"`. packed_backing_int_type: Index, + /// Not valid if `layout` is `.@"packed"`. class: TypeClass, + /// Not valid if `layout` is `.@"packed"`. size: u32, + /// Not valid if `layout` is `.@"packed"`. padding: u32, + /// Not valid if `layout` is `.@"packed"`. alignment: Alignment, pub const TagUsage = enum(u2) { @@ -3898,7 +3910,7 @@ pub fn loadUnionType(ip: *const InternPool, index: Index) LoadedUnionType { .want_layout = extra.data.bits.want_layout, .field_types = field_types, .field_aligns = .empty, - .has_runtime_tag = undefined, + .has_runtime_tag = false, .class = undefined, .size = undefined, .padding = undefined, diff --git a/src/Sema.zig b/src/Sema.zig index 644bad5dca..53fc776481 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -13777,45 +13777,44 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const many_alloc = try block.addBitCast(many_ty, mutable_alloc); // lhs_dest_slice = dest[0..lhs.len] - const slice_ty_ref = Air.internedToRef(slice_ty.toIntern()); - const lhs_len_ref = try pt.intRef(.usize, lhs_len); - const lhs_dest_slice = try block.addInst(.{ - .tag = .slice, - .data = .{ .ty_pl = .{ - .ty = slice_ty_ref, - .payload = try sema.addExtra(Air.Bin{ - .lhs = many_alloc, - .rhs = lhs_len_ref, - }), - } }, - }); - - _ = try block.addBinOp(.memcpy, lhs_dest_slice, lhs); + if (lhs_len > 0) { + const lhs_dest_slice = try block.addInst(.{ + .tag = .slice, + .data = .{ .ty_pl = .{ + .ty = .fromType(slice_ty), + .payload = try sema.addExtra(Air.Bin{ + .lhs = many_alloc, + .rhs = try pt.intRef(.usize, lhs_len), + }), + } }, + }); + _ = try block.addBinOp(.memcpy, lhs_dest_slice, lhs); + } // rhs_dest_slice = dest[lhs.len..][0..rhs.len] - const rhs_len_ref = try pt.intRef(.usize, rhs_len); - const rhs_dest_offset = try block.addInst(.{ - .tag = .ptr_add, - .data = .{ .ty_pl = .{ - .ty = Air.internedToRef(many_ty.toIntern()), - .payload = try sema.addExtra(Air.Bin{ - .lhs = many_alloc, - .rhs = lhs_len_ref, - }), - } }, - }); - const rhs_dest_slice = try block.addInst(.{ - .tag = .slice, - .data = .{ .ty_pl = .{ - .ty = slice_ty_ref, - .payload = try sema.addExtra(Air.Bin{ - .lhs = rhs_dest_offset, - .rhs = rhs_len_ref, - }), - } }, - }); - - _ = try block.addBinOp(.memcpy, rhs_dest_slice, rhs); + if (rhs_len > 0) { + const rhs_dest_offset = try block.addInst(.{ + .tag = .ptr_add, + .data = .{ .ty_pl = .{ + .ty = Air.internedToRef(many_ty.toIntern()), + .payload = try sema.addExtra(Air.Bin{ + .lhs = many_alloc, + .rhs = try pt.intRef(.usize, lhs_len), + }), + } }, + }); + const rhs_dest_slice = try block.addInst(.{ + .tag = .slice, + .data = .{ .ty_pl = .{ + .ty = .fromType(slice_ty), + .payload = try sema.addExtra(Air.Bin{ + .lhs = rhs_dest_offset, + .rhs = try pt.intRef(.usize, rhs_len), + }), + } }, + }); + _ = try block.addBinOp(.memcpy, rhs_dest_slice, rhs); + } if (res_sent_val) |sent_val| { const elem_index = try pt.intRef(.usize, result_len); @@ -18829,7 +18828,7 @@ fn finishStructInit( return sema.addConstantMaybeRef(sema.resolveValue(final_val_ref).?, is_ref); }, .@"packed" => { - const buf = try sema.arena.alloc(u8, (struct_ty.bitSize(zcu) + 7) / 8); + const buf = try sema.arena.alloc(u8, @intCast((struct_ty.bitSize(zcu) + 7) / 8)); var bit_offset: u16 = 0; for (field_inits) |field_init| { const field_val = sema.resolveValue(field_init).?; diff --git a/src/Value.zig b/src/Value.zig index 4cd7995b71..3176520d36 100644 --- a/src/Value.zig +++ b/src/Value.zig @@ -895,7 +895,7 @@ pub fn fieldValue(val: Value, pt: Zcu.PerThread, index: usize) !Value { // Avoid hitting gpa for accesses to small packed structs var sfba_state = std.heap.stackFallback(128, zcu.comp.gpa); const sfba = sfba_state.get(); - const buf = try sfba.alloc(u8, (ty.bitSize(zcu) + 7) / 8); + const buf = try sfba.alloc(u8, @intCast((ty.bitSize(zcu) + 7) / 8)); defer sfba.free(buf); int_val.writeToPackedMemory(pt, buf, 0) catch |err| switch (err) { error.ReinterpretDeclRef => unreachable, // it's an integer @@ -2419,7 +2419,7 @@ pub fn uninterpret(val: anytype, ty: Type, pt: Zcu.PerThread) error{ OutOfMemory } for (field_vals, 0..) |*field_val, field_idx| { if (field_val.* == .none) { - const default_init = struct_obj.field_inits.get(ip)[field_idx]; + const default_init = struct_obj.field_defaults.get(ip)[field_idx]; if (default_init == .none) return error.TypeMismatch; field_val.* = default_init; } diff --git a/src/Zcu.zig b/src/Zcu.zig index 78144bac16..d5704a84d3 100644 --- a/src/Zcu.zig +++ b/src/Zcu.zig @@ -3686,6 +3686,11 @@ pub const ImportResult = struct { pub fn resetUnit(zcu: *Zcu, unit: AnalUnit) void { const gpa = zcu.comp.gpa; + if (!dev.env.supports(.incremental)) { + // This is the first time `unit` is being analyzed, so there is no stale data to clear. + return; + } + // Compile errors if (zcu.failed_analysis.fetchSwapRemove(unit)) |kv| { kv.value.destroy(gpa); diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index 6a7054246e..76c1dd8795 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -1075,7 +1075,6 @@ pub fn ensureMemoizedStateUpToDate( const prev_failed = zcu.failed_analysis.contains(unit) or zcu.transitive_failed_analysis.contains(unit); if (was_outdated) { - dev.check(.incremental); zcu.resetUnit(unit); } else { if (prev_failed) return error.AnalysisFail; @@ -1193,10 +1192,7 @@ pub fn ensureComptimeUnitUpToDate(pt: Zcu.PerThread, cu_id: InternPool.ComptimeU const was_outdated = zcu.clearOutdatedState(anal_unit); if (was_outdated) { - // `was_outdated` can be true in the initial update for comptime units, so this isn't a `dev.check`. - if (dev.env.supports(.incremental)) { - zcu.resetUnit(anal_unit); - } + zcu.resetUnit(anal_unit); } else { // We can trust the current information about this unit. if (zcu.failed_analysis.contains(anal_unit)) return error.AnalysisFail; @@ -1366,10 +1362,7 @@ pub fn ensureTypeLayoutUpToDate( }; if (was_outdated) { - // `was_outdated` is true in the initial update, so this isn't a `dev.check`. - if (dev.env.supports(.incremental)) { - zcu.resetUnit(anal_unit); - } + zcu.resetUnit(anal_unit); // For types, we already know that we have to invalidate all dependees. // TODO: we actually *could* detect whether everything was the same. should we bother? try zcu.markDependeeOutdated(.marked_po, .{ .type_layout = ty.toIntern() }); @@ -1492,10 +1485,7 @@ pub fn ensureStructDefaultsUpToDate( }; if (was_outdated) { - // `was_outdated` is true in the initial update, so this isn't a `dev.check`. - if (dev.env.supports(.incremental)) { - zcu.resetUnit(anal_unit); - } + zcu.resetUnit(anal_unit); // For types, we already know that we have to invalidate all dependees. // TODO: we actually *could* detect whether everything was the same. should we bother? try zcu.markDependeeOutdated(.marked_po, .{ .struct_defaults = ty.toIntern() }); @@ -1609,7 +1599,6 @@ pub fn ensureNavValUpToDate( zcu.transitive_failed_analysis.contains(anal_unit); if (was_outdated) { - dev.check(.incremental); zcu.resetUnit(anal_unit); } else { // We can trust the current information about this unit. @@ -1973,7 +1962,6 @@ pub fn ensureNavTypeUpToDate( zcu.transitive_failed_analysis.contains(anal_unit); if (was_outdated) { - dev.check(.incremental); zcu.resetUnit(anal_unit); } else { // We can trust the current information about this unit. @@ -2210,7 +2198,6 @@ pub fn ensureFuncBodyUpToDate( const prev_failed = zcu.failed_analysis.contains(anal_unit) or zcu.transitive_failed_analysis.contains(anal_unit); if (was_outdated) { - dev.check(.incremental); zcu.resetUnit(anal_unit); } else { // We can trust the current information about this function. @@ -2292,7 +2279,7 @@ fn analyzeFuncBody( var air = try pt.analyzeFuncBodyInner(func_index, reason); var air_owned = true; - errdefer if (air_owned) air.deinit(gpa); + defer if (air_owned) air.deinit(gpa); const ies_outdated = !func.analysisUnordered(ip).inferred_error_set or func.resolvedErrorSetUnordered(ip) != old_resolved_ies; diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 13e07dfcd0..97e5ca2249 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -7132,9 +7132,9 @@ fn formatIntLiteral(data: FormatIntLiteralContext, w: *Writer) Writer.Error!void var limb_buf: [std.math.big.int.calcTwosCompLimbCount(65535)]std.math.big.Limb = undefined; for (0..big.limbs_len) |limb_index| { if (limb_index != 0) try w.writeAll(", "); - const limb_bit_offset: u64 = switch (target.cpu.arch.endian()) { - .little => limb_index * big.limb_size.bits(), - .big => (big.limbs_len - limb_index - 1) * big.limb_size.bits(), + const limb_bit_offset: u16 = switch (target.cpu.arch.endian()) { + .little => @intCast(limb_index * big.limb_size.bits()), + .big => @intCast((big.limbs_len - limb_index - 1) * big.limb_size.bits()), }; var limb_bigint: std.math.big.int.Mutable = .{ .limbs = &limb_buf, diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 938706a42b..7bcd60d8a7 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -2432,7 +2432,7 @@ pub const Object = struct { const debug_payload_type = try o.builder.debugUnionType( payload_name: { if (layout.tag_size == 0) break :payload_name name; - break :payload_name try o.builder.metadataStringFmt("{s}:Payload", .{name.slice(&o.builder)}); + break :payload_name try o.builder.metadataStringFmt("{f}:Payload", .{ty.fmt(pt)}); }, file, scope, diff --git a/src/link.zig b/src/link.zig index 3eb4add772..80141676d4 100644 --- a/src/link.zig +++ b/src/link.zig @@ -1557,7 +1557,10 @@ pub fn doZcuTask(comp: *Compilation, tid: Zcu.PerThread.Id, task: ZcuTask) void .link_func => |codegen_task| nav: { timer.pause(io); const func, var mir = codegen_task.wait(&zcu.codegen_task_pool, io) catch |err| switch (err) { - error.Canceled, error.AlreadyReported => return, + error.Canceled, error.AlreadyReported => { + comp.link_prog_node.completeOne(); + return; + }, }; defer mir.deinit(zcu); timer.@"resume"(io); diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index 97fb0294eb..fac71faf56 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -3344,6 +3344,7 @@ pub fn updateConstIncomplete(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index } fn updateConstIncompleteInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.ConstPool.Index, value_index: InternPool.Index) !void { const zcu = pt.zcu; + const ip = &zcu.intern_pool; const val: Value = .fromInterned(value_index); @@ -3383,20 +3384,109 @@ fn updateConstIncompleteInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_inde .debug_loclists = .init(dwarf.gpa), }; defer wip_nav.deinit(); - switch (val.typeOf(zcu).toIntern()) { - .type_type => { - try wip_nav.abbrevCode(.generated_empty_struct_type); - try wip_nav.strpFmt("{f}", .{val.toType().fmt(pt)}); - try wip_nav.debug_info.writer.writeByte(@intFromBool(true)); + + switch (ip.indexToKey(value_index)) { + // Container types still need to be valid namespaces. + .struct_type => { + const loaded_struct = ip.loadStructType(value_index); + const root_of_file: ?Zcu.File.Index = if (loaded_struct.zir_index.resolveFull(ip)) |r| f: { + if (r.inst != .main_struct_inst) break :f null; + break :f r.file; + } else null; + if (root_of_file) |file_index| { + assert(loaded_struct.name_nav == .none); + const file_gop = try dwarf.getModInfo(unit).files.getOrPut(dwarf.gpa, file_index); + try wip_nav.abbrevCode(.empty_file); + try wip_nav.debug_info.writer.writeUleb128(file_gop.index); + try wip_nav.strp(loaded_struct.name.toSlice(ip)); + } else { + try dwarf.emitIncompleteContainerType( + &wip_nav, + loaded_struct.zir_index, + loaded_struct.name, + loaded_struct.name_nav, + ); + } }, - else => |ty| { - try wip_nav.abbrevCode(.undefined_comptime_value); - try wip_nav.refType(.fromInterned(ty)); + .union_type => { + const loaded_union = ip.loadUnionType(value_index); + try dwarf.emitIncompleteContainerType( + &wip_nav, + loaded_union.zir_index, + loaded_union.name, + loaded_union.name_nav, + ); + }, + .enum_type => { + const loaded_enum = ip.loadEnumType(value_index); + if (loaded_enum.zir_index.unwrap()) |zir_index| { + try dwarf.emitIncompleteContainerType( + &wip_nav, + zir_index, + loaded_enum.name, + loaded_enum.name_nav, + ); + } else { + try wip_nav.abbrevCode(.generated_empty_struct_type); + try wip_nav.strp(loaded_enum.name.toSlice(ip)); + try wip_nav.debug_info.writer.writeByte(@intFromBool(true)); + } + }, + .opaque_type => { + const loaded_opaque = ip.loadOpaqueType(value_index); + try dwarf.emitIncompleteContainerType( + &wip_nav, + loaded_opaque.zir_index, + loaded_opaque.name, + loaded_opaque.name_nav, + ); + }, + // Not a container type, so just emit a dummy entry. If `val` happens to be a type, we'll + // emit it as if it were an opaque type so that we can name it. + else => |val_key| switch (val_key.typeOf()) { + .type_type => { + try wip_nav.abbrevCode(.generated_empty_struct_type); + try wip_nav.strpFmt("{f}", .{val.toType().fmt(pt)}); + try wip_nav.debug_info.writer.writeByte(@intFromBool(true)); + }, + else => |ty| { + try wip_nav.abbrevCode(.undefined_comptime_value); + try wip_nav.refType(.fromInterned(ty)); + }, }, } try dwarf.debug_info.section.replaceEntry(unit, entry, dwarf, wip_nav.debug_info.written()); try dwarf.debug_loclists.section.replaceEntry(unit, entry, dwarf, wip_nav.debug_loclists.written()); } +fn emitIncompleteContainerType( + dwarf: *Dwarf, + wip_nav: *WipNav, + zir_index: InternPool.TrackedInst.Index, + name: InternPool.NullTerminatedString, + name_nav: InternPool.Nav.Index.Optional, +) !void { + const zcu = wip_nav.pt.zcu; + const ip = &zcu.intern_pool; + const file = zir_index.resolveFile(ip); + if (name_nav.unwrap()) |nav_index| { + const nav = ip.getNav(nav_index); + const decl_inst = nav.srcInst(ip).resolve(ip).?; + const decl = zcu.fileByIndex(file).zir.?.getDeclaration(decl_inst); + try wip_nav.declCommon(.{ + .decl = .decl_namespace_struct, + .generic_decl = .generic_decl_const, + .decl_instance = .decl_instance_namespace_struct, + }, &nav, file, &decl); + try wip_nav.debug_info.writer.writeByte(@intFromBool(true)); + } else { + const diw = &wip_nav.debug_info.writer; + const file_gop = try dwarf.getModInfo(wip_nav.unit).files.getOrPut(dwarf.gpa, file); + try wip_nav.abbrevCode(.empty_struct_type); + try diw.writeUleb128(file_gop.index); + try wip_nav.strp(name.toSlice(ip)); + try diw.writeByte(@intFromBool(true)); + } +} /// Should only be called by the `link.ConstPool` implementation. /// /// Emits a DIE for the given comptime-only value (which may be a type). @@ -3418,6 +3508,8 @@ fn updateConstInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.Co val.typeOf(zcu).assertHasLayout(zcu); } + if (value_index == .anyerror_type) return; // handled in `flush` instead + const value_ip_key = ip.indexToKey(value_index); switch (value_ip_key) { .func => return, // populated by the Nav instead (`updateComptimeNav` or `initWipNav`) @@ -3746,7 +3838,7 @@ fn updateConstInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.Co try wip_nav.abbrevCode(.void_type); try wip_nav.strpFmt("{f}", .{val.toType().fmt(pt)}); }, - .anyerror => return, // delay until flush + .anyerror => unreachable, // already did early return above .adhoc_inferred_error_set => unreachable, }, .tuple_type => |tuple_type| if (tuple_type.types.len == 0) {