From 911294116d5df0db3b431f9117f42bf8074a3b83 Mon Sep 17 00:00:00 2001 From: Matthew Lugg Date: Tue, 27 Jan 2026 17:13:14 +0000 Subject: [PATCH] compiler: make type resolution lazy ...and rework some of the incremental reference tracking. Almost all kinds of AnalUnit have one property in common: they might never be referenced in any update despite conceptually "existing", in which case we don't want to waste time semantically analyzing them. As of the lazy type resolution introduced in this commit, the only units to which this does not apply are `memoized_state` and `@"comptime"`. Previously, I had a somewhat hacky system in `Zcu` for dealing with this, but I now have a better understanding of the design incremental compilation is converging on, so can implement a better solution. By finding a few unused bits lying around (...or making them), we can represent a single bit of state indicating whether something's corresponding units have ever been referenced. This is akin to the units being in `Zcu.outdated`, with the key difference being that the compiler will *not* attempt to analyze units which are in this state. Once they are first referenced or depended on, the flag is set to true and the unit is added to `outdated` so that it can participate in the normal dependency resolution logic. --- src/InternPool.zig | 412 ++++++++++++++++++++++++++++------- src/Sema.zig | 204 ++++++----------- src/Sema/LowerZon.zig | 11 +- src/Sema/bitcast.zig | 6 +- src/Sema/type_resolution.zig | 68 +++--- src/Type.zig | 15 +- src/Value.zig | 2 +- src/Zcu.zig | 97 ++------- src/Zcu/PerThread.zig | 67 ++---- 9 files changed, 515 insertions(+), 367 deletions(-) diff --git a/src/InternPool.zig b/src/InternPool.zig index ce9622e54d57bc0703d9151a5bbd35e3ef57298c..230ef78d63dd4190c712a6b955b5ea594d89ddb6 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -546,6 +546,8 @@ pub const Nav = struct { analysis: ?struct { namespace: NamespaceIndex, zir_index: TrackedInst.Index, + /// Initially `false`. Set to `true` by `setWantNavAnalysis`. + wanted: bool, }, status: union(enum) { /// This `Nav` is pending semantic analysis. @@ -743,7 +745,7 @@ pub const Nav = struct { const Repr = struct { name: NullTerminatedString, fqn: NullTerminatedString, - // The following 1 fields are either both populated, or both `.none`. + // The following 2 fields are either both populated, or both `.none`. analysis_namespace: OptionalNamespaceIndex, analysis_zir_index: TrackedInst.Index.Optional, /// Populated only if `bits.status != .unresolved`. @@ -762,7 +764,7 @@ pub const Nav = struct { @"addrspace": std.builtin.AddressSpace, /// Populated only if `bits.status == .type_resolved`. is_threadlocal: bool, - _: u1 = 0, + want_analysis: bool, }; fn unpack(repr: Repr) Nav { @@ -772,6 +774,7 @@ pub const Nav = struct { .analysis = if (repr.analysis_namespace.unwrap()) |namespace| .{ .namespace = namespace, .zir_index = repr.analysis_zir_index.unwrap().?, + .wanted = repr.bits.want_analysis, } else a: { assert(repr.analysis_zir_index == .none); break :a null; @@ -824,6 +827,7 @@ pub const Nav = struct { .alignment = .none, .@"addrspace" = .generic, .is_threadlocal = false, + .want_analysis = if (nav.analysis) |a| a.wanted else false, }, .type_resolved => |r| .{ .status = if (r.is_extern_decl) .type_resolved_extern_decl else .type_resolved, @@ -831,6 +835,7 @@ pub const Nav = struct { .alignment = r.alignment, .@"addrspace" = r.@"addrspace", .is_threadlocal = r.is_threadlocal, + .want_analysis = if (nav.analysis) |a| a.wanted else false, }, .fully_resolved => |r| .{ .status = .fully_resolved, @@ -838,6 +843,7 @@ pub const Nav = struct { .alignment = r.alignment, .@"addrspace" = r.@"addrspace", .is_threadlocal = false, + .want_analysis = if (nav.analysis) |a| a.wanted else false, }, }, }; @@ -2412,17 +2418,6 @@ pub const Key = union(enum) { @atomicStore(FuncAnalysis, analysis_ptr, analysis, .release); } - pub fn setAnalyzed(func: Func, ip: *InternPool, io: Io) void { - const extra_mutex = &ip.getLocal(func.tid).mutate.extra.mutex; - extra_mutex.lockUncancelable(io); - defer extra_mutex.unlock(io); - - const analysis_ptr = func.analysisPtr(ip); - var analysis = analysis_ptr.*; - analysis.is_analyzed = true; - @atomicStore(FuncAnalysis, analysis_ptr, analysis, .release); - } - /// Returns a pointer that becomes invalid after any additions to the `InternPool`. fn zirBodyInstPtr(func: Func, ip: *const InternPool) *TrackedInst.Index { const extra = ip.getLocalShared(func.tid).extra.acquire(); @@ -3314,6 +3309,25 @@ pub const LoadedStructType = struct { /// May be `undefined` if `layout != .@"packed"`. packed_backing_mode: BackingTypeMode, + /// Initially `false`, and set to `true` once any dependency on or reference to the struct's + /// layout is encountered, after which it is never reset to `false`, even across incremental + /// updates. + /// + /// This field is purely an optimization to avoid resolving the layout of types whose layouts + /// are never demanded. If this field is `true` but the layout is not actually needed, the + /// compiler frontend resolves this by traversing the reference graph at the end of each update + /// with `Zcu.resolveReferences` and hiding compile errors which arise from this analysis. + want_layout: bool, + /// Initially `false`, and set to `true` once any dependency on or reference to the struct's + /// default field values is encountered, after which it is never reset to `false`, even across + /// incremental updates. + /// + /// This field is purely an optimization to avoid resolving the layout of types whose layouts + /// are never demanded. If this field is `true` but the layout is not actually needed, the + /// compiler frontend resolves this by traversing the reference graph at the end of each update + /// with `Zcu.resolveReferences` and hiding compile errors which arise from this analysis. + want_defaults: bool, + // The remaining fields are only valid once the struct's layout is resolved. field_name_map: MapIndex, field_names: NullTerminatedString.Slice, @@ -3490,6 +3504,16 @@ pub const LoadedUnionType = struct { /// or populate `enum_tag_type`. reified_field_names: NullTerminatedString.Slice, + /// Initially `false`, and set to `true` once any dependency on or reference to the struct's + /// layout is encountered, after which it is never reset to `false`, even across incremental + /// updates. + /// + /// This field is purely an optimization to avoid resolving the layout of types whose layouts + /// are never demanded. If this field is `true` but the layout is not actually needed, the + /// compiler frontend resolves this by traversing the reference graph at the end of each update + /// with `Zcu.resolveReferences` and hiding compile errors which arise from this analysis. + want_layout: bool, + // The remaining fields are only valid once the union's layout is resolved. field_types: Index.Slice, field_aligns: Alignment.Slice, @@ -3532,6 +3556,16 @@ pub const LoadedEnumType = struct { int_tag_mode: BackingTypeMode, nonexhaustive: bool, + /// Initially `false`, and set to `true` once any dependency on or reference to the struct's + /// layout is encountered, after which it is never reset to `false`, even across incremental + /// updates. + /// + /// This field is purely an optimization to avoid resolving the layout of types whose layouts + /// are never demanded. If this field is `true` but the layout is not actually needed, the + /// compiler frontend resolves this by traversing the reference graph at the end of each update + /// with `Zcu.resolveReferences` and hiding compile errors which arise from this analysis. + want_layout: bool, + // The remaining fields are only valid once the enum's layout is resolved. int_tag_type: Index, field_name_map: MapIndex, @@ -3669,6 +3703,9 @@ pub fn loadStructType(ip: *const InternPool, index: Index) LoadedStructType { }, .packed_backing_mode = undefined, + .want_layout = extra.data.flags.want_layout, + .want_defaults = extra.data.flags.want_defaults, + .field_name_map = extra.data.field_name_map, .field_names = field_names, .field_types = field_types, @@ -3690,15 +3727,15 @@ pub fn loadStructType(ip: *const InternPool, index: Index) LoadedStructType { }; const extra = extraDataTrail(extra_list, Tag.TypeStructPacked, item.data); var extra_index = extra.end; - const captures: CaptureValue.Slice = switch (extra.data.captures_len) { + const captures: CaptureValue.Slice = switch (extra.data.bits.captures_len) { .reified => captures: { extra_index += 2; // type_hash: PackedU64 break :captures .empty; }, - _ => .{ + _ => |n| .{ .tid = unwrapped_index.tid, .start = extra_index, - .len = @intFromEnum(extra.data.captures_len), + .len = @intFromEnum(n), }, }; extra_index += captures.len; @@ -3723,13 +3760,16 @@ pub fn loadStructType(ip: *const InternPool, index: Index) LoadedStructType { return .{ .zir_index = extra.data.zir_index, .captures = captures, - .is_reified = extra.data.captures_len == .reified, + .is_reified = extra.data.bits.captures_len == .reified, .name = extra.data.name, .name_nav = extra.data.name_nav, .namespace = extra.data.namespace, .layout = .@"packed", .packed_backing_mode = backing_mode, + .want_layout = extra.data.bits.want_layout, + .want_defaults = extra.data.bits.want_defaults, + .field_name_map = extra.data.field_name_map, .field_names = field_names, .field_types = field_types, @@ -3813,6 +3853,7 @@ pub fn loadUnionType(ip: *const InternPool, index: Index) LoadedUnionType { .packed_backing_mode = undefined, .packed_backing_int_type = undefined, .reified_field_names = reified_field_names, + .want_layout = extra.data.flags.want_layout, .field_types = field_types, .field_aligns = field_aligns, .has_no_possible_value = extra.data.flags.has_no_possible_value, @@ -3828,19 +3869,19 @@ pub fn loadUnionType(ip: *const InternPool, index: Index) LoadedUnionType { }; const extra = extraDataTrail(extra_list, Tag.TypeUnionPacked, item.data); var extra_index = extra.end; - const captures: CaptureValue.Slice = switch (extra.data.captures_len) { + const captures: CaptureValue.Slice = switch (extra.data.bits.captures_len) { .reified => captures: { extra_index += 2; // type_hash: PackedU64 break :captures .empty; }, - _ => .{ + _ => |n| .{ .tid = unwrapped_index.tid, .start = extra_index, - .len = @intFromEnum(extra.data.captures_len), + .len = @intFromEnum(n), }, }; extra_index += captures.len; - const reified_field_names: NullTerminatedString.Slice = if (extra.data.captures_len == .reified) .{ + const reified_field_names: NullTerminatedString.Slice = if (extra.data.bits.captures_len == .reified) .{ .tid = unwrapped_index.tid, .start = extra_index, .len = extra.data.fields_len, @@ -3855,7 +3896,7 @@ pub fn loadUnionType(ip: *const InternPool, index: Index) LoadedUnionType { return .{ .zir_index = extra.data.zir_index, .captures = captures, - .is_reified = extra.data.captures_len == .reified, + .is_reified = extra.data.bits.captures_len == .reified, .name = extra.data.name, .name_nav = extra.data.name_nav, .namespace = extra.data.namespace, @@ -3866,6 +3907,7 @@ pub fn loadUnionType(ip: *const InternPool, index: Index) LoadedUnionType { .packed_backing_mode = backing_mode, .packed_backing_int_type = extra.data.backing_int_type, .reified_field_names = reified_field_names, + .want_layout = extra.data.bits.want_layout, .field_types = field_types, .field_aligns = .empty, .has_no_possible_value = undefined, @@ -3891,7 +3933,7 @@ pub fn loadEnumType(ip: *const InternPool, index: Index) LoadedEnumType { }; const extra = extraDataTrail(extra_list, Tag.TypeEnum, item.data); var extra_index: u32 = @intCast(extra.end); - const zir_index: TrackedInst.Index.Optional, const captures: CaptureValue.Slice, const owner_union: Index = switch (extra.data.captures_len) { + const zir_index: TrackedInst.Index.Optional, const captures: CaptureValue.Slice, const owner_union: Index = switch (extra.data.bits.captures_len) { .reified => info: { const zir_index: TrackedInst.Index = @enumFromInt(extra_items[extra_index]); extra_index += 1; @@ -3903,13 +3945,13 @@ pub fn loadEnumType(ip: *const InternPool, index: Index) LoadedEnumType { extra_index += 1; break :info .{ .none, .empty, owner_union }; }, - _ => info: { + _ => |n| info: { const zir_index: TrackedInst.Index = @enumFromInt(extra_items[extra_index]); extra_index += 1; const captures: CaptureValue.Slice = .{ .tid = unwrapped_index.tid, .start = extra_index, - .len = @intFromEnum(extra.data.captures_len), + .len = @intFromEnum(n), }; extra_index += captures.len; break :info .{ zir_index.toOptional(), captures, .none }; @@ -3935,7 +3977,7 @@ pub fn loadEnumType(ip: *const InternPool, index: Index) LoadedEnumType { return .{ .zir_index = zir_index, .captures = captures, - .is_reified = extra.data.captures_len == .reified, + .is_reified = extra.data.bits.captures_len == .reified, .owner_union = owner_union, .name = extra.data.name, .name_nav = extra.data.name_nav, @@ -3943,6 +3985,7 @@ pub fn loadEnumType(ip: *const InternPool, index: Index) LoadedEnumType { .int_tag_type = extra.data.int_tag_type, .int_tag_mode = if (explicit_int_tag) .explicit else .auto, .nonexhaustive = nonexhaustive, + .want_layout = extra.data.bits.want_layout, .field_name_map = extra.data.field_name_map, .field_value_map = field_value_map, .field_names = field_names, @@ -5629,7 +5672,10 @@ pub const Tag = enum(u8) { /// Alignment of the whole struct. Always `.none` until layout resolved. alignment: Alignment, - _: u16 = 0, + want_layout: bool, + want_defaults: bool, + + _: u14 = 0, }; }; @@ -5641,10 +5687,7 @@ pub const Tag = enum(u8) { /// 4. field_default: Index // if item tag implies field defaults; for each `fields_len` pub const TypeStructPacked = struct { zir_index: TrackedInst.Index, - captures_len: enum(u32) { - reified = std.math.maxInt(u32), - _, - }, + bits: Bits, name: NullTerminatedString, name_nav: Nav.Index.Optional, @@ -5655,6 +5698,15 @@ pub const Tag = enum(u8) { fields_len: u32, field_name_map: MapIndex, + + const Bits = packed struct(u32) { + captures_len: enum(u30) { + reified = std.math.maxInt(u30), + _, + }, + want_layout: bool, + want_defaults: bool, + }; }; /// For declared unions, field names are intentionally omitted because they are available in @@ -5718,7 +5770,9 @@ pub const Tag = enum(u8) { /// Alignment of the whole union. Always `.none` until layout resolved. alignment: Alignment, - _: u15 = 0, + want_layout: bool, + + _: u14 = 0, }; }; @@ -5734,10 +5788,7 @@ pub const Tag = enum(u8) { /// 3. field_type: Index // for each `fields_len` pub const TypeUnionPacked = struct { zir_index: TrackedInst.Index, - captures_len: enum(u32) { - reified = std.math.maxInt(u32), - _, - }, + bits: Bits, name: NullTerminatedString, name_nav: Nav.Index.Optional, @@ -5753,6 +5804,14 @@ pub const Tag = enum(u8) { /// to store it directly. This is also necessary for `dumpStatsFallible` to /// work on unresolved types. fields_len: u32, + + const Bits = packed struct(u32) { + captures_len: enum(u31) { + reified = std.math.maxInt(u31), + _, + }, + want_layout: bool, + }; }; /// Trailing: @@ -5764,11 +5823,7 @@ pub const Tag = enum(u8) { /// 5. field_name: NullTerminatedString // for each `fields_len` /// 6. field_value: Index // if tag is not `.type_enum_auto`; for each `fields_len` pub const TypeEnum = struct { - captures_len: enum(u32) { - reified = std.math.maxInt(u32), - generated_union_tag = std.math.maxInt(u32) - 1, - _, - }, + bits: Bits, name: NullTerminatedString, name_nav: Nav.Index.Optional, @@ -5780,6 +5835,15 @@ pub const Tag = enum(u8) { fields_len: u32, field_name_map: MapIndex, + + const Bits = packed struct(u32) { + captures_len: enum(u31) { + reified = std.math.maxInt(u31), + generated_union_tag = std.math.maxInt(u31) - 1, + _, + }, + want_layout: bool, + }; }; /// Trailing: @@ -5812,7 +5876,7 @@ pub const BackingTypeMode = enum(u1) { /// equality or hashing, except for `inferred_error_set` which is considered /// to be part of the type of the function. pub const FuncAnalysis = packed struct(u32) { - is_analyzed: bool, + want_runtime_analysis: bool, branch_hint: std.builtin.BranchHint, is_noinline: bool, has_error_trace: bool, @@ -6597,17 +6661,17 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key { => .{ .struct_type = ns: { const extra_list = unwrapped_index.getExtra(ip); const extra = extraDataTrail(extra_list, Tag.TypeStructPacked, data); - break :ns switch (extra.data.captures_len) { + break :ns switch (extra.data.bits.captures_len) { .reified => .{ .reified = .{ .zir_index = extra.data.zir_index, .type_hash = extraData(extra_list, PackedU64, extra.end).get(), } }, - _ => .{ .declared = .{ + _ => |len| .{ .declared = .{ .zir_index = extra.data.zir_index, .captures = .{ .owned = .{ .tid = unwrapped_index.tid, .start = extra.end, - .len = @intFromEnum(extra.data.captures_len), + .len = @intFromEnum(len), } }, } }, }; @@ -6637,17 +6701,17 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key { .type_union_packed_auto, .type_union_packed_explicit => .{ .union_type = ns: { const extra_list = unwrapped_index.getExtra(ip); const extra = extraDataTrail(extra_list, Tag.TypeUnionPacked, data); - break :ns switch (extra.data.captures_len) { + break :ns switch (extra.data.bits.captures_len) { .reified => .{ .reified = .{ .zir_index = extra.data.zir_index, .type_hash = extraData(extra_list, PackedU64, extra.end).get(), } }, - _ => .{ .declared = .{ + _ => |len| .{ .declared = .{ .zir_index = extra.data.zir_index, .captures = .{ .owned = .{ .tid = unwrapped_index.tid, .start = extra.end, - .len = @intFromEnum(extra.data.captures_len), + .len = @intFromEnum(len), } }, } }, }; @@ -6655,7 +6719,7 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key { .type_enum_auto, .type_enum_explicit, .type_enum_nonexhaustive => .{ .enum_type = ns: { const extra_list = unwrapped_index.getExtra(ip); const extra = extraDataTrail(extra_list, Tag.TypeEnum, data); - break :ns switch (extra.data.captures_len) { + break :ns switch (extra.data.bits.captures_len) { .reified => .{ .reified = .{ .zir_index = @enumFromInt(extra_list.view().items(.@"0")[extra.end]), .type_hash = extraData(extra_list, PackedU64, extra.end + 1).get(), @@ -6663,12 +6727,12 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key { .generated_union_tag => .{ .generated_union_tag = owner_union: { break :owner_union @enumFromInt(extra_list.view().items(.@"0")[extra.end]); } }, - _ => .{ .declared = .{ + _ => |len| .{ .declared = .{ .zir_index = @enumFromInt(extra_list.view().items(.@"0")[extra.end]), .captures = .{ .owned = .{ .tid = unwrapped_index.tid, .start = extra.end + 1, - .len = @intFromEnum(extra.data.captures_len), + .len = @intFromEnum(len), } }, } }, }; @@ -8138,7 +8202,11 @@ pub fn getDeclaredStructType( const extra_index = addExtraAssumeCapacity(extra, Tag.TypeStructPacked{ .zir_index = ini.zir_index, - .captures_len = @enumFromInt(ini.captures.len), + .bits = .{ + .captures_len = @enumFromInt(ini.captures.len), + .want_layout = false, + .want_defaults = false, + }, .name = undefined, // set by `finish` .name_nav = undefined, // set by `finish` .namespace = undefined, // set by `finish` @@ -8204,6 +8272,8 @@ pub fn getDeclaredStructType( .comptime_only = false, .has_runtime_bits = false, .alignment = .none, + .want_layout = false, + .want_defaults = false, }, }); if (ini.captures.len != 0) { @@ -8281,7 +8351,11 @@ pub fn getReifiedStructType(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.Pe const extra_index = addExtraAssumeCapacity(extra, Tag.TypeStructPacked{ .zir_index = ini.zir_index, - .captures_len = .reified, + .bits = .{ + .captures_len = .reified, + .want_layout = false, + .want_defaults = false, + }, .name = undefined, // set by `finish` .name_nav = undefined, // set by `finish` .namespace = undefined, // set by `finish` @@ -8352,6 +8426,8 @@ pub fn getReifiedStructType(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.Pe .comptime_only = false, .has_runtime_bits = false, .alignment = .none, + .want_layout = false, + .want_defaults = false, }, }); _ = addExtraAssumeCapacity(extra, PackedU64.init(ini.type_hash)); // type_hash @@ -8451,7 +8527,10 @@ pub fn getDeclaredUnionType( const extra_index = addExtraAssumeCapacity(extra, Tag.TypeUnionPacked{ .zir_index = ini.zir_index, - .captures_len = @enumFromInt(ini.captures.len), + .bits = .{ + .captures_len = @enumFromInt(ini.captures.len), + .want_layout = false, + }, .name = undefined, // set by `finish` .name_nav = undefined, // set by `finish` .namespace = undefined, // set by `finish` @@ -8509,6 +8588,7 @@ pub fn getDeclaredUnionType( .comptime_only = false, .has_runtime_bits = false, .alignment = .none, + .want_layout = false, }, }); if (ini.captures.len > 0) { @@ -8572,7 +8652,10 @@ pub fn getReifiedUnionType(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.Per const extra_index = addExtraAssumeCapacity(extra, Tag.TypeUnionPacked{ .zir_index = ini.zir_index, - .captures_len = .reified, + .bits = .{ + .captures_len = .reified, + .want_layout = false, + }, .name = undefined, // set by `finish` .name_nav = undefined, // set by `finish` .namespace = undefined, // set by `finish` @@ -8633,6 +8716,7 @@ pub fn getReifiedUnionType(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.Per .comptime_only = false, .has_runtime_bits = false, .alignment = .none, + .want_layout = false, }, }); _ = addExtraAssumeCapacity(extra, PackedU64.init(ini.type_hash)); @@ -8723,7 +8807,10 @@ pub fn getDeclaredEnumType( (if (have_values) ini.fields_len else 0)); // field_value const extra_index = addExtraAssumeCapacity(extra, Tag.TypeEnum{ - .captures_len = @enumFromInt(ini.captures.len), + .bits = .{ + .captures_len = @enumFromInt(ini.captures.len), + .want_layout = false, + }, .name = undefined, // set by `finish` .name_nav = undefined, // set by `finish` .namespace = undefined, // set by `finish` @@ -8795,7 +8882,10 @@ pub fn getReifiedEnumType(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerT (if (have_values) ini.fields_len else 0)); // field_value const extra_index = addExtraAssumeCapacity(extra, Tag.TypeEnum{ - .captures_len = .reified, + .bits = .{ + .captures_len = .reified, + .want_layout = false, + }, .name = undefined, // set by `finish` .name_nav = undefined, // set by `finish` .namespace = undefined, // set by `finish` @@ -8865,7 +8955,10 @@ pub fn getGeneratedEnumTagType(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu (if (have_values) ini.fields_len else 0)); // field_value const extra_index = addExtraAssumeCapacity(extra, Tag.TypeEnum{ - .captures_len = .generated_union_tag, + .bits = .{ + .captures_len = .generated_union_tag, + .want_layout = false, + }, .name = undefined, // set by `finish` .name_nav = undefined, // set by `finish` .namespace = undefined, // set by `finish` @@ -9249,7 +9342,7 @@ pub fn getFuncDecl( const func_decl_extra_index = addExtraAssumeCapacity(extra, Tag.FuncDecl{ .analysis = .{ - .is_analyzed = false, + .want_runtime_analysis = false, .branch_hint = .none, .is_noinline = key.is_noinline, .has_error_trace = false, @@ -9359,7 +9452,7 @@ pub fn getFuncDeclIes( const func_decl_extra_index = addExtraAssumeCapacity(extra, Tag.FuncDecl{ .analysis = .{ - .is_analyzed = false, + .want_runtime_analysis = false, .branch_hint = .none, .is_noinline = key.is_noinline, .has_error_trace = false, @@ -9557,7 +9650,7 @@ pub fn getFuncInstance( const func_extra_index = addExtraAssumeCapacity(extra, Tag.FuncInstance{ .analysis = .{ - .is_analyzed = false, + .want_runtime_analysis = false, .branch_hint = .none, .is_noinline = arg.is_noinline, .has_error_trace = false, @@ -9658,7 +9751,7 @@ fn getFuncInstanceIes( const func_extra_index = addExtraAssumeCapacity(extra, Tag.FuncInstance{ .analysis = .{ - .is_analyzed = false, + .want_runtime_analysis = false, .branch_hint = .none, .is_noinline = arg.is_noinline, .has_error_trace = false, @@ -9902,9 +9995,6 @@ fn addExtraAssumeCapacity(extra: Local.Extra.Mutable, item: anytype) u32 { TrackedInst.Index, TrackedInst.Index.Optional, ComptimeAllocIndex, - @FieldType(Tag.TypeStructPacked, "captures_len"), - @FieldType(Tag.TypeUnionPacked, "captures_len"), - @FieldType(Tag.TypeEnum, "captures_len"), => @intFromEnum(@field(item, field.name)), u32, @@ -9916,6 +10006,9 @@ fn addExtraAssumeCapacity(extra: Local.Extra.Mutable, item: anytype) u32 { Tag.TypePointer.PackedOffset, Tag.TypeUnion.Flags, Tag.TypeStruct.Flags, + Tag.TypeStructPacked.Bits, + Tag.TypeUnionPacked.Bits, + Tag.TypeEnum.Bits, => @bitCast(@field(item, field.name)), else => @compileError("bad field type: " ++ @typeName(field.type)), @@ -9967,9 +10060,6 @@ fn extraDataTrail(extra: Local.Extra, comptime T: type, index: u32) struct { dat TrackedInst.Index, TrackedInst.Index.Optional, ComptimeAllocIndex, - @FieldType(Tag.TypeStructPacked, "captures_len"), - @FieldType(Tag.TypeUnionPacked, "captures_len"), - @FieldType(Tag.TypeEnum, "captures_len"), => @enumFromInt(extra_item), u32, @@ -9981,6 +10071,9 @@ fn extraDataTrail(extra: Local.Extra, comptime T: type, index: u32) struct { dat Tag.TypeUnion.Flags, Tag.TypeStruct.Flags, FuncAnalysis, + Tag.TypeStructPacked.Bits, + Tag.TypeUnionPacked.Bits, + Tag.TypeEnum.Bits, => @bitCast(extra_item), else => @compileError("bad field type: " ++ @typeName(field.type)), @@ -10750,7 +10843,7 @@ fn dumpStatsFallible(ip: *const InternPool, w: *Io.Writer, arena: Allocator) !vo .type_struct_packed_auto, .type_struct_packed_explicit => b: { var n: usize = @typeInfo(Tag.TypeStructPacked).@"struct".fields.len; const extra = extraDataTrail(extra_list, Tag.TypeStructPacked, data); - switch (extra.data.captures_len) { + switch (extra.data.bits.captures_len) { .reified => n += 2, // type_hash: PackedU64 _ => |len| n += @intFromEnum(len), // capture: CaptureValue } @@ -10761,7 +10854,7 @@ fn dumpStatsFallible(ip: *const InternPool, w: *Io.Writer, arena: Allocator) !vo .type_struct_packed_auto_defaults, .type_struct_packed_explicit_defaults => b: { var n: usize = @typeInfo(Tag.TypeStructPacked).@"struct".fields.len; const extra = extraDataTrail(extra_list, Tag.TypeStructPacked, data); - switch (extra.data.captures_len) { + switch (extra.data.bits.captures_len) { .reified => n += 2, // type_hash: PackedU64 _ => |len| n += @intFromEnum(len), // capture: CaptureValue } @@ -10790,7 +10883,7 @@ fn dumpStatsFallible(ip: *const InternPool, w: *Io.Writer, arena: Allocator) !vo .type_union_packed_auto, .type_union_packed_explicit => b: { var n: usize = @typeInfo(Tag.TypeUnionPacked).@"struct".fields.len; const extra = extraDataTrail(extra_list, Tag.TypeUnionPacked, data); - switch (extra.data.captures_len) { + switch (extra.data.bits.captures_len) { .reified => n += 2, // type_hash: PackedU64 _ => |len| n += @intFromEnum(len), // capture: CaptureValue } @@ -10800,7 +10893,7 @@ fn dumpStatsFallible(ip: *const InternPool, w: *Io.Writer, arena: Allocator) !vo .type_enum_auto => b: { var n: usize = @typeInfo(Tag.TypeEnum).@"struct".fields.len; const extra = extraData(extra_list, Tag.TypeEnum, data); - switch (extra.captures_len) { + switch (extra.bits.captures_len) { .generated_union_tag => n += 1, // owner_union: Index .reified => { n += 1; // zir_index: TrackedInst.Index, @@ -10817,7 +10910,7 @@ fn dumpStatsFallible(ip: *const InternPool, w: *Io.Writer, arena: Allocator) !vo .type_enum_explicit, .type_enum_nonexhaustive => b: { var n: usize = @typeInfo(Tag.TypeEnum).@"struct".fields.len; const extra = extraData(extra_list, Tag.TypeEnum, data); - switch (extra.captures_len) { + switch (extra.bits.captures_len) { .generated_union_tag => n += 1, // owner_union: Index .reified => { n += 1; // zir_index: TrackedInst.Index, @@ -11204,6 +11297,7 @@ pub fn createDeclNav( .analysis = .{ .namespace = namespace, .zir_index = zir_index, + .wanted = false, }, .status = .unresolved, })); @@ -12829,3 +12923,175 @@ pub fn resolveEnumLayout( extra_items[item.data + std.meta.fieldIndex(Tag.TypeEnum, "int_tag_type").?] = @intFromEnum(int_tag_type); } + +/// Sets the "want_layout" flag on the given struct, union, or enum type. Returns true if the flag +/// was *not* already set, meaning we have just discovered the first reference to this type's +/// layout. This flag is never reset to false, and exists purely as an optimization; for details, +/// see doc comments in `LoadedStructType`. +pub fn setWantTypeLayout(ip: *InternPool, io: Io, container_type: Index) bool { + const unwrapped_index = container_type.unwrap(ip); + + const local = ip.getLocal(unwrapped_index.tid); + local.mutate.extra.mutex.lockUncancelable(io); + defer local.mutate.extra.mutex.unlock(io); + + const extra_items = local.shared.extra.view().items(.@"0"); + const item = unwrapped_index.getItem(ip); + switch (item.tag) { + .type_struct_packed_auto, + .type_struct_packed_explicit, + .type_struct_packed_auto_defaults, + .type_struct_packed_explicit_defaults, + => { + const bits: *Tag.TypeStructPacked.Bits = @ptrCast(&extra_items[ + item.data + std.meta.fieldIndex(Tag.TypeStructPacked, "bits").? + ]); + if (bits.want_layout) { + return false; + } else { + bits.want_layout = true; + return true; + } + }, + + .type_struct => { + const flags: *Tag.TypeStruct.Flags = @ptrCast(&extra_items[ + item.data + std.meta.fieldIndex(Tag.TypeStruct, "flags").? + ]); + if (flags.want_layout) { + return false; + } else { + flags.want_layout = true; + return true; + } + }, + + .type_union_packed_auto, + .type_union_packed_explicit, + => { + const bits: *Tag.TypeUnionPacked.Bits = @ptrCast(&extra_items[ + item.data + std.meta.fieldIndex(Tag.TypeUnionPacked, "bits").? + ]); + if (bits.want_layout) { + return false; + } else { + bits.want_layout = true; + return true; + } + }, + + .type_union => { + const flags: *Tag.TypeUnion.Flags = @ptrCast(&extra_items[ + item.data + std.meta.fieldIndex(Tag.TypeUnion, "flags").? + ]); + if (flags.want_layout) { + return false; + } else { + flags.want_layout = true; + return true; + } + }, + + .type_enum_auto, + .type_enum_explicit, + .type_enum_nonexhaustive, + => { + const bits: *Tag.TypeEnum.Bits = @ptrCast(&extra_items[ + item.data + std.meta.fieldIndex(Tag.TypeEnum, "bits").? + ]); + if (bits.want_layout) { + return false; + } else { + bits.want_layout = true; + return true; + } + }, + + else => unreachable, + } +} + +/// Like `setWantTypeLayout`, but for the default field values of a struct (so this sets the +/// `want_defaults` flag rather than the `want_layout` flag). +pub fn setWantStructDefaults(ip: *InternPool, io: Io, struct_type: Index) bool { + const unwrapped_index = struct_type.unwrap(ip); + + const local = ip.getLocal(unwrapped_index.tid); + local.mutate.extra.mutex.lockUncancelable(io); + defer local.mutate.extra.mutex.unlock(io); + + const extra_items = local.shared.extra.view().items(.@"0"); + const item = unwrapped_index.getItem(ip); + switch (item.tag) { + .type_struct_packed_auto, + .type_struct_packed_explicit, + .type_struct_packed_auto_defaults, + .type_struct_packed_explicit_defaults, + => { + const bits: *Tag.TypeStructPacked.Bits = @ptrCast(&extra_items[ + item.data + std.meta.fieldIndex(Tag.TypeStructPacked, "bits").? + ]); + if (bits.want_defaults) { + return false; + } else { + bits.want_defaults = true; + return true; + } + }, + + .type_struct => { + const flags: *Tag.TypeStruct.Flags = @ptrCast(&extra_items[ + item.data + std.meta.fieldIndex(Tag.TypeStruct, "flags").? + ]); + if (flags.want_defaults) { + return false; + } else { + flags.want_defaults = true; + return true; + } + }, + + else => unreachable, + } +} + +/// Like `setWantTypeLayout`, but for runtime analysis of a function body, using the +/// `FuncAnalysis.want_runtime_analysis` flag. +pub fn setWantRuntimeFnAnalysis(ip: *InternPool, io: Io, func_index: Index) bool { + const unwrapped_index = func_index.unwrap(ip); + + const local = ip.getLocal(unwrapped_index.tid); + local.mutate.extra.mutex.lockUncancelable(io); + defer local.mutate.extra.mutex.unlock(io); + + const a = funcAnalysisPtr(ip, func_index); + if (a.want_runtime_analysis) { + return false; + } else { + a.want_runtime_analysis = true; + return true; + } +} + +/// Like `setWantTypeLayout`, but for runtime analysis of a `Nav`, using the `Nav.analysis.wanted` flag. +pub fn setWantNavAnalysis(ip: *InternPool, io: Io, nav_index: Nav.Index) bool { + const unwrapped = nav_index.unwrap(ip); + + const local = ip.getLocal(unwrapped.tid); + local.mutate.extra.mutex.lockUncancelable(io); + defer local.mutate.extra.mutex.unlock(io); + + const navs = local.shared.navs.view(); + + if (navs.items(.analysis_namespace)[unwrapped.index] == .none) { + return false; + } + + const bits = &navs.items(.bits)[unwrapped.index]; + if (bits.want_analysis) { + return false; + } else { + bits.want_analysis = true; + return true; + } +} diff --git a/src/Sema.zig b/src/Sema.zig index 2fffe8ae546af74951980c583506ba82647b0d60..a506a7e6bbc369bf6a9906d2cda4dd6b41450317 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -3258,7 +3258,7 @@ fn zirAllocExtended( } else .none; if (small.has_type) { - try sema.ensureLayoutResolved(var_ty); + try sema.ensureLayoutResolved(var_ty, ty_src); if (block.isComptime() or small.is_comptime or var_ty.comptimeOnly(zcu)) { return sema.analyzeComptimeAlloc(block, var_src, var_ty, alignment); } @@ -3322,7 +3322,7 @@ fn zirAllocComptime(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr const ty_src = block.src(.{ .node_offset_var_decl_ty = inst_data.src_node }); const var_src = block.nodeOffset(inst_data.src_node); const var_ty = try sema.resolveType(block, ty_src, inst_data.operand); - try sema.ensureLayoutResolved(var_ty); + try sema.ensureLayoutResolved(var_ty, ty_src); return sema.analyzeComptimeAlloc(block, var_src, var_ty, .none); } @@ -3743,7 +3743,7 @@ fn zirAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I const var_src = block.nodeOffset(inst_data.src_node); const var_ty = try sema.resolveType(block, ty_src, inst_data.operand); - try sema.ensureLayoutResolved(var_ty); + try sema.ensureLayoutResolved(var_ty, ty_src); if (block.isComptime() or var_ty.comptimeOnly(zcu)) { return sema.analyzeComptimeAlloc(block, var_src, var_ty, .none); } @@ -3775,7 +3775,7 @@ fn zirAllocMut(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const var_src = block.nodeOffset(inst_data.src_node); const var_ty = try sema.resolveType(block, ty_src, inst_data.operand); - try sema.ensureLayoutResolved(var_ty); + try sema.ensureLayoutResolved(var_ty, ty_src); if (block.isComptime()) { return sema.analyzeComptimeAlloc(block, var_src, var_ty, .none); } @@ -4132,8 +4132,9 @@ fn optEuBasePtrInit(sema: *Sema, block: *Block, ptr: Air.Inst.Ref, src: LazySrcL fn zirOptEuBasePtrInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; const ptr = sema.resolveInst(un_node.operand); - try sema.ensureLayoutResolved(sema.typeOf(ptr).childType(sema.pt.zcu)); - return sema.optEuBasePtrInit(block, ptr, block.nodeOffset(un_node.src_node)); + const src = block.nodeOffset(un_node.src_node); + try sema.ensureLayoutResolved(sema.typeOf(ptr).childType(sema.pt.zcu), src); + return sema.optEuBasePtrInit(block, ptr, src); } fn zirCoercePtrElemTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -4518,7 +4519,7 @@ fn validateStructInit( if (struct_ty.structFieldIsComptime(i, zcu)) continue; if (!struct_ty.isTuple(zcu)) { - try sema.ensureStructDefaultsResolved(struct_ty); + try sema.ensureStructDefaultsResolved(struct_ty, init_src); } const default_val = struct_ty.structFieldDefaultValue(i, zcu) orelse { @@ -4642,7 +4643,7 @@ fn zirValidateDeref(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr } const elem_ty = operand_ty.childType(zcu); - try sema.ensureLayoutResolved(elem_ty); + try sema.ensureLayoutResolved(elem_ty, src); if (try elem_ty.onePossibleValue(pt) != null) { // No need to validate the actual pointer value, we don't need it! @@ -7025,7 +7026,7 @@ fn analyzeCall( break :ret_ty full_ty; }; - try sema.ensureLayoutResolved(resolved_ret_ty); + try sema.ensureLayoutResolved(resolved_ret_ty, func_ret_ty_src); // If we've discovered after evaluating arguments that a generic function instantiation is // comptime-only, then we can mark the block as comptime *now*. @@ -7122,7 +7123,7 @@ fn analyzeCall( .generic_owner = func_val.?.toIntern(), .comptime_args = comptime_args, }); - try sema.ensureLayoutResolved(.fromInterned(ip.typeOf(func_instance))); + try sema.ensureLayoutResolved(.fromInterned(ip.typeOf(func_instance)), call_src); if (zcu.comp.debugIncremental()) { const nav = ip.indexToKey(func_instance).func.owner_nav; const gop = try zcu.incremental_debug_state.navs.getOrPut(gpa, nav); @@ -7196,7 +7197,7 @@ fn analyzeCall( return .unreachable_value; } - try sema.ensureLayoutResolved(sema.typeOf(maybe_opv)); + try sema.ensureLayoutResolved(sema.typeOf(maybe_opv), func_ret_ty_src); if (try sema.typeOf(maybe_opv).onePossibleValue(pt)) |opv| { return .fromValue(opv); } else { @@ -7270,7 +7271,7 @@ fn analyzeCall( // We're about to do an inline call; if the return type expression was generic, the return type // may not be resolved yet. It's correct to resolve it because the function is going to return a // value of this type. - try sema.ensureLayoutResolved(resolved_ret_ty); + try sema.ensureLayoutResolved(resolved_ret_ty, func_ret_ty_src); // For an inline call, we depend on the source code of the whole function definition. try sema.declareDependency(.{ .src_hash = fn_nav.analysis.?.zir_index }); @@ -7532,7 +7533,6 @@ fn zirArrayInitElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compil const zcu = pt.zcu; const bin = sema.code.instructions.items(.data)[@intFromEnum(inst)].bin; const maybe_wrapped_indexable_ty = try sema.resolveTypeOrPoison(block, LazySrcLoc.unneeded, bin.lhs) orelse return .generic_poison_type; - try sema.ensureLayoutResolved(maybe_wrapped_indexable_ty); const indexable_ty = maybe_wrapped_indexable_ty.optEuBaseType(zcu); assert(indexable_ty.isIndexable(zcu)); // validated by a previous instruction const elem_ty = switch (indexable_ty.zigTypeTag(zcu)) { @@ -8040,7 +8040,7 @@ fn zirEnumFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError if (dest_ty.zigTypeTag(zcu) != .@"enum") { return sema.fail(block, src, "expected enum, found '{f}'", .{dest_ty.fmt(pt)}); } - try sema.ensureLayoutResolved(dest_ty); + try sema.ensureLayoutResolved(dest_ty, src); _ = try sema.checkIntType(block, operand_src, operand_ty); if (sema.resolveValue(operand)) |int_val| { @@ -8103,7 +8103,7 @@ fn zirOptionalPayloadPtr( const ptr_ty = sema.typeOf(optional_ptr); assert(ptr_ty.zigTypeTag(sema.pt.zcu) == .pointer); - try sema.ensureLayoutResolved(ptr_ty.childType(sema.pt.zcu)); + try sema.ensureLayoutResolved(ptr_ty.childType(sema.pt.zcu), src); return sema.analyzeOptionalPayloadPtr(block, src, optional_ptr, safety_check, false); } @@ -8313,7 +8313,7 @@ fn zirErrUnionPayloadPtr( const ptr_ty = sema.typeOf(operand); assert(ptr_ty.zigTypeTag(sema.pt.zcu) == .pointer); - try sema.ensureLayoutResolved(ptr_ty.childType(sema.pt.zcu)); + try sema.ensureLayoutResolved(ptr_ty.childType(sema.pt.zcu), src); return sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false); } @@ -9022,6 +9022,7 @@ fn funcCommon( const io = comp.io; const ip = &zcu.intern_pool; + const src = block.nodeOffset(src_node_offset); const ret_ty_src = block.src(.{ .node_offset_fn_type_ret_ty = src_node_offset }); const cc_src = block.src(.{ .node_offset_fn_type_cc = src_node_offset }); @@ -9091,7 +9092,7 @@ fn funcCommon( .lbrace_column = @as(u16, @truncate(src_locs.columns)), .rbrace_column = @as(u16, @truncate(src_locs.columns >> 16)), })); - try sema.ensureLayoutResolved(func_val.typeOf(zcu)); + try sema.ensureLayoutResolved(func_val.typeOf(zcu), src); return .fromValue(func_val); } @@ -9106,7 +9107,7 @@ fn funcCommon( }); if (has_body) { - try sema.ensureLayoutResolved(.fromInterned(func_ty)); + try sema.ensureLayoutResolved(.fromInterned(func_ty), src); return .fromIntern(try ip.getFuncDecl(gpa, io, pt.tid, .{ .owner_nav = sema.owner.unwrap().nav_val, .ty = func_ty, @@ -9762,7 +9763,7 @@ fn zirElemPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air return sema.failWithOwnedErrorMsg(block, msg); } try sema.checkIndexable(block, src, indexable_ty); - try sema.ensureLayoutResolved(indexable_ty.childType(zcu)); + try sema.ensureLayoutResolved(indexable_ty.childType(zcu), src); return sema.elemPtrOneLayerOnly(block, src, array_ptr, elem_index, src, false, false); } @@ -9983,7 +9984,7 @@ fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp err_union_ty.fmt(pt), }); } - try sema.ensureLayoutResolved(err_union_ty); + try sema.ensureLayoutResolved(err_union_ty, operand_src); const non_err_cond = if (non_err_case.operand_is_ref) try sema.analyzePtrIsNonErr(block, operand_src, eu_maybe_ptr) @@ -11287,7 +11288,7 @@ fn validateSwitchBlock( } break :operand_ty raw_operand_ty; }; - try sema.ensureLayoutResolved(operand_ty); + try sema.ensureLayoutResolved(operand_ty, operand_src); const item_ty: Type = item_ty: { switch (operand_ty.zigTypeTag(zcu)) { @@ -12870,7 +12871,7 @@ fn zirHasField(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const name_src = block.builtinCallArgSrc(inst_data.src_node, 1); const ty = try sema.resolveType(block, ty_src, extra.lhs); const field_name = try sema.resolveConstStringIntern(block, name_src, extra.rhs, .{ .simple = .field_name }); - try sema.ensureLayoutResolved(ty); + try sema.ensureLayoutResolved(ty, ty_src); const ip = &zcu.intern_pool; const has_field = hf: { @@ -15270,7 +15271,7 @@ fn analyzeArithmetic( else => return sema.failWithInvalidPtrArithmetic(block, src, "pointer-integer", "addition and subtraction"), }; - try sema.ensureLayoutResolved(lhs_ty.childType(zcu)); + try sema.ensureLayoutResolved(lhs_ty.childType(zcu), src); return sema.analyzePtrArithmetic(block, src, lhs, rhs, air_tag, lhs_src, rhs_src); }, } @@ -15964,7 +15965,7 @@ fn zirSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. .@"anyframe", => {}, } - try sema.ensureLayoutResolved(ty); + try sema.ensureLayoutResolved(ty, operand_src); return .fromValue(try pt.intValue(.comptime_int, ty.abiSize(zcu))); } @@ -16005,7 +16006,7 @@ fn zirBitSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A .@"anyframe", => {}, } - try sema.ensureLayoutResolved(operand_ty); + try sema.ensureLayoutResolved(operand_ty, operand_src); return .fromValue(try pt.intValue(.comptime_int, operand_ty.bitSize(zcu))); } @@ -16251,7 +16252,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const type_info_ty = try sema.getBuiltinType(src, .Type); const type_info_tag_ty = type_info_ty.unionTagType(zcu).?; - try sema.ensureLayoutResolved(ty); + try sema.ensureLayoutResolved(ty, src); if (ty.typeDeclInst(zcu)) |type_decl_inst| { try sema.declareDependency(.{ .namespace = type_decl_inst }); @@ -16412,7 +16413,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai if (info.flags.alignment.toByteUnits()) |b| break :bytes b; const elem_ty: Type = .fromInterned(info.child); // MLUGG TODO: this resolution is sus, but i doubt i'll solve it in this branch - try sema.ensureLayoutResolved(elem_ty); + try sema.ensureLayoutResolved(elem_ty, src); break :bytes elem_ty.abiAlignment(zcu).toByteUnits().?; }); @@ -16873,7 +16874,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai .struct_type => ip.loadStructType(ty.toIntern()), else => unreachable, }; - try sema.ensureStructDefaultsResolved(ty); // can't do this sooner, since it's not allowed on tuples + try sema.ensureStructDefaultsResolved(ty, src); // can't do this sooner, since it's not allowed on tuples struct_field_vals = try gpa.alloc(InternPool.Index, struct_type.field_types.len); for (struct_field_vals, 0..) |*field_val, field_index| { @@ -18294,7 +18295,7 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air elem_ty.fmt(pt), bit_offset, bit_offset - host_size * 8, host_size, }); } - try sema.ensureLayoutResolved(elem_ty); + try sema.ensureLayoutResolved(elem_ty, elem_ty_src); const elem_bit_size = elem_ty.bitSize(zcu); if (elem_bit_size > host_size * 8 - bit_offset) { return sema.fail(block, bitoffset_src, "packed type '{f}' at bit offset {d} ends {d} bits after the end of a {d} byte host integer", .{ @@ -18363,7 +18364,7 @@ fn zirStructInitEmpty(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE const pt = sema.pt; const zcu = pt.zcu; - try sema.ensureLayoutResolved(obj_ty); + try sema.ensureLayoutResolved(obj_ty, ty_src); switch (obj_ty.zigTypeTag(zcu)) { .@"struct" => return sema.structInitEmpty(block, obj_ty, src, src), @@ -18428,7 +18429,7 @@ fn zirStructInitEmptyResult(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is }); } else ty_operand; - try sema.ensureLayoutResolved(init_ty); + try sema.ensureLayoutResolved(init_ty, src); const obj_ty = init_ty.optEuBaseType(zcu); @@ -18544,7 +18545,7 @@ fn zirStructInit( // The type wasn't actually known, so treat this as an anon struct init. return sema.structInitAnon(block, src, inst, .typed_init, extra.data, extra.end, is_ref); }; - try sema.ensureLayoutResolved(result_ty); + try sema.ensureLayoutResolved(result_ty, src); const resolved_ty = result_ty.optEuBaseType(zcu); if (resolved_ty.zigTypeTag(zcu) == .@"struct") { @@ -18751,7 +18752,7 @@ fn finishStructInit( continue; } - try sema.ensureStructDefaultsResolved(struct_ty); + try sema.ensureStructDefaultsResolved(struct_ty, init_src); const field_default: InternPool.Index = d: { if (struct_type.field_defaults.len == 0) break :d .none; @@ -18979,17 +18980,11 @@ fn structInitAnon( }); if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index); - try zcu.outdated.ensureUnusedCapacity(gpa, 1); - try zcu.outdated_ready.ensureUnusedCapacity(gpa, 1); - errdefer comptime unreachable; // because we don't remove the `outdated` entries - zcu.outdated.putAssumeCapacityNoClobber(.wrap(.{ .type_layout = wip.index }), 0); - zcu.outdated_ready.putAssumeCapacityNoClobber(.wrap(.{ .type_layout = wip.index }), {}); - break :ty .fromInterned(wip.finish(ip, new_namespace_index)); }, }; try sema.addTypeReferenceEntry(src, struct_ty); - try sema.ensureLayoutResolved(struct_ty); + try sema.ensureLayoutResolved(struct_ty, src); _ = opt_runtime_index orelse { const struct_val = try pt.aggregateValue(struct_ty, values); @@ -19308,7 +19303,7 @@ fn zirFieldTypeRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro const field_src = block.builtinCallArgSrc(inst_data.src_node, 1); const aggregate_ty = try sema.resolveType(block, ty_src, extra.container_type); const field_name = try sema.resolveConstStringIntern(block, field_src, extra.field_name, .{ .simple = .field_name }); - try sema.ensureLayoutResolved(aggregate_ty); + try sema.ensureLayoutResolved(aggregate_ty, ty_src); return sema.fieldType(block, aggregate_ty, field_name, field_src, ty_src); } @@ -19328,7 +19323,7 @@ fn zirStructInitFieldType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp const aggregate_ty = wrapped_aggregate_ty.optEuBaseType(zcu); const zir_field_name = sema.code.nullTerminatedString(extra.name_start); const field_name = try ip.getOrPutString(gpa, io, pt.tid, zir_field_name, .no_embedded_nulls); - try sema.ensureLayoutResolved(aggregate_ty); + try sema.ensureLayoutResolved(aggregate_ty, ty_src); return sema.fieldType(block, aggregate_ty, field_name, field_name_src, ty_src); } @@ -19431,7 +19426,7 @@ fn zirAlignOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air if (ty.isNoReturn(zcu)) { return sema.fail(block, operand_src, "no align available for type '{f}'", .{ty.fmt(sema.pt)}); } - try sema.ensureLayoutResolved(ty); + try sema.ensureLayoutResolved(ty, operand_src); return .fromValue(try pt.intValue(.comptime_int, ty.abiAlignment(zcu).toByteUnits().?)); } @@ -19912,7 +19907,7 @@ fn zirReifyFn( const param_attrs_arr = try sema.derefSliceAsArray(block, param_attrs_src, param_attrs_slice, .{ .simple = .fn_param_attrs }); const ret_ty = try sema.resolveType(block, ret_ty_src, extra.ret_ty); - try sema.ensureLayoutResolved(ret_ty); + try sema.ensureLayoutResolved(ret_ty, ret_ty_src); const fn_attrs_uncoerced = sema.resolveInst(extra.fn_attrs); const fn_attrs_coerced = try sema.coerce(block, fn_attrs_ty, fn_attrs_uncoerced, fn_attrs_src); @@ -19937,7 +19932,7 @@ fn zirReifyFn( param_types_src, fn_attrs.@"callconv", ); - try sema.ensureLayoutResolved(param_ty); + try sema.ensureLayoutResolved(param_ty, param_types_src); if (param_ty.comptimeOnly(zcu)) { return sema.fail(block, param_attrs_src, "cannot reify function type with comptime-only parameter type '{f}'", .{param_ty.fmt(pt)}); } @@ -20253,14 +20248,6 @@ fn zirReifyStruct( }); try sema.addTypeReferenceEntry(src, .fromInterned(wip.index)); if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index); - // MLUGG TODO: we could potentially revert this language change if we wanted? don't mind - try zcu.comp.queueJob(.{ .analyze_unit = .wrap(.{ .type_layout = wip.index }) }); - - try zcu.outdated.ensureUnusedCapacity(gpa, 1); - try zcu.outdated_ready.ensureUnusedCapacity(gpa, 1); - errdefer comptime unreachable; // because we don't remove the `outdated` entries - zcu.outdated.putAssumeCapacityNoClobber(.wrap(.{ .type_layout = wip.index }), 0); - zcu.outdated_ready.putAssumeCapacityNoClobber(.wrap(.{ .type_layout = wip.index }), {}); return .fromIntern(wip.finish(ip, new_namespace_index)); }, @@ -20482,15 +20469,6 @@ fn zirReifyUnion( if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index); try sema.addTypeReferenceEntry(src, .fromInterned(wip.index)); - // MLUGG TODO: we could potentially revert this language change if we wanted? don't mind - try zcu.comp.queueJob(.{ .analyze_unit = .wrap(.{ .type_layout = wip.index }) }); - - try zcu.outdated.ensureUnusedCapacity(gpa, 1); - try zcu.outdated_ready.ensureUnusedCapacity(gpa, 1); - errdefer comptime unreachable; // because we don't remove the `outdated` entry - zcu.outdated.putAssumeCapacityNoClobber(.wrap(.{ .type_layout = wip.index }), 0); - zcu.outdated_ready.putAssumeCapacityNoClobber(.wrap(.{ .type_layout = wip.index }), {}); - return .fromIntern(wip.finish(ip, new_namespace_index)); }, } @@ -20643,15 +20621,6 @@ fn zirReifyEnum( try sema.addTypeReferenceEntry(src, .fromInterned(wip.index)); if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index); - // MLUGG TODO: we could potentially revert this language change if we wanted? don't mind - try zcu.comp.queueJob(.{ .analyze_unit = .wrap(.{ .type_layout = wip.index }) }); - - try zcu.outdated.ensureUnusedCapacity(gpa, 1); - try zcu.outdated_ready.ensureUnusedCapacity(gpa, 1); - errdefer comptime unreachable; // because we don't remove the `outdated` entry - zcu.outdated.putAssumeCapacityNoClobber(.wrap(.{ .type_layout = wip.index }), 0); - zcu.outdated_ready.putAssumeCapacityNoClobber(.wrap(.{ .type_layout = wip.index }), {}); - return .fromIntern(wip.finish(ip, new_namespace_index)); }, } @@ -20874,7 +20843,7 @@ fn zirPtrFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! const elem_ty = ptr_ty.nullablePtrElem(zcu); // We'll need to validate the pointer alignment. - try sema.ensureLayoutResolved(elem_ty); + try sema.ensureLayoutResolved(elem_ty, src); const ptr_align = ptr_ty.ptrAlignment(zcu); if (ptr_ty.isSlice(zcu)) { @@ -21217,8 +21186,8 @@ fn ptrCastFull( const src_info = operand_ty.ptrInfo(zcu); const dest_info = dest_ty.ptrInfo(zcu); - try sema.ensureLayoutResolved(.fromInterned(src_info.child)); - try sema.ensureLayoutResolved(.fromInterned(dest_info.child)); + try sema.ensureLayoutResolved(.fromInterned(src_info.child), operand_src); + try sema.ensureLayoutResolved(.fromInterned(dest_info.child), src); const DestSliceLen = union(enum) { undef, @@ -21989,7 +21958,7 @@ fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u6 const ty = try sema.resolveType(block, ty_src, extra.lhs); const field_name = try sema.resolveConstStringIntern(block, field_name_src, extra.rhs, .{ .simple = .field_name }); - try sema.ensureLayoutResolved(ty); + try sema.ensureLayoutResolved(ty, ty_src); const pt = sema.pt; const zcu = pt.zcu; @@ -23072,7 +23041,7 @@ fn zirAtomicLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, true); const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering, .{ .simple = .atomic_order }); - try sema.ensureLayoutResolved(elem_ty); + try sema.ensureLayoutResolved(elem_ty, elem_ty_src); switch (order) { .release, .acq_rel => { @@ -23392,7 +23361,7 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.Ins return sema.fail(block, inst_src, "expected single pointer type, found '{f}'", .{parent_ptr_ty.fmt(pt)}); } const parent_ty: Type = .fromInterned(parent_ptr_info.child); - try sema.ensureLayoutResolved(parent_ty); + try sema.ensureLayoutResolved(parent_ty, inst_src); switch (parent_ty.zigTypeTag(zcu)) { .@"struct", .@"union" => {}, else => return sema.fail(block, inst_src, "expected pointer to struct or union type, found '{f}'", .{parent_ptr_ty.fmt(pt)}), @@ -24002,8 +23971,8 @@ fn zirMemcpy( const dest_elem_ty = dest_ty.indexableElem(zcu); const src_elem_ty = src_ty.indexableElem(zcu); - try sema.ensureLayoutResolved(dest_elem_ty); - try sema.ensureLayoutResolved(src_elem_ty); + try sema.ensureLayoutResolved(dest_elem_ty, dest_src); + try sema.ensureLayoutResolved(src_elem_ty, src_src); const imc = try sema.coerceInMemoryAllowed( block, @@ -25518,7 +25487,7 @@ fn fieldPtrLoad( const zcu = pt.zcu; const object_ptr_ty = sema.typeOf(object_ptr); const pointee_ty = object_ptr_ty.childType(zcu); - try sema.ensureLayoutResolved(pointee_ty); // MLUGG TODO + try sema.ensureLayoutResolved(pointee_ty, src); // MLUGG TODO if (try pointee_ty.onePossibleValue(pt)) |opv| { const object: Air.Inst.Ref = .fromValue(opv); return fieldVal(sema, block, src, object, field_name, field_name_src); @@ -25654,7 +25623,7 @@ fn fieldVal( if (try sema.namespaceLookupVal(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| { return inst; } - try sema.ensureLayoutResolved(child_type); + try sema.ensureLayoutResolved(child_type, src); if (child_type.unionTagType(zcu)) |enum_ty| { if (enum_ty.enumFieldIndex(field_name, zcu)) |field_index_usize| { const field_index: u32 = @intCast(field_index_usize); @@ -25667,7 +25636,7 @@ fn fieldVal( if (try sema.namespaceLookupVal(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| { return inst; } - try sema.ensureLayoutResolved(child_type); + try sema.ensureLayoutResolved(child_type, src); const field_index_usize = child_type.enumFieldIndex(field_name, zcu) orelse return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name); const field_index: u32 = @intCast(field_index_usize); @@ -25693,7 +25662,7 @@ fn fieldVal( }, .@"struct" => if (is_pointer_to) { // Avoid loading the entire struct by fetching a pointer and loading that - try sema.ensureLayoutResolved(inner_ty); + try sema.ensureLayoutResolved(inner_ty, src); const field_ptr = try sema.structFieldPtr(block, src, object, field_name, field_name_src, inner_ty, false); return sema.analyzeLoad(block, src, field_ptr, object_src); } else { @@ -25701,7 +25670,7 @@ fn fieldVal( }, .@"union" => if (is_pointer_to) { // Avoid loading the entire union by fetching a pointer and loading that - try sema.ensureLayoutResolved(inner_ty); + try sema.ensureLayoutResolved(inner_ty, src); const field_ptr = try sema.unionFieldPtr(block, src, object, field_name, field_name_src, inner_ty, false); return sema.analyzeLoad(block, src, field_ptr, object_src); } else { @@ -25884,7 +25853,7 @@ fn fieldPtr( if (try sema.namespaceLookupRef(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| { return inst; } - try sema.ensureLayoutResolved(child_type); + try sema.ensureLayoutResolved(child_type, src); if (child_type.unionTagType(zcu)) |enum_ty| { if (enum_ty.enumFieldIndex(field_name, zcu)) |field_index| { const field_index_u32: u32 = @intCast(field_index); @@ -25898,7 +25867,7 @@ fn fieldPtr( if (try sema.namespaceLookupRef(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| { return inst; } - try sema.ensureLayoutResolved(child_type); + try sema.ensureLayoutResolved(child_type, src); const field_index = child_type.enumFieldIndex(field_name, zcu) orelse { return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name); }; @@ -25920,7 +25889,7 @@ fn fieldPtr( try sema.analyzeLoad(block, src, object_ptr, object_ptr_src) else object_ptr; - try sema.ensureLayoutResolved(inner_ty); + try sema.ensureLayoutResolved(inner_ty, src); const field_ptr = try sema.structFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty, initializing); try sema.checkKnownAllocPtr(block, inner_ptr, field_ptr); return field_ptr; @@ -25930,7 +25899,7 @@ fn fieldPtr( try sema.analyzeLoad(block, src, object_ptr, object_ptr_src) else object_ptr; - try sema.ensureLayoutResolved(inner_ty); + try sema.ensureLayoutResolved(inner_ty, src); const field_ptr = try sema.unionFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty, initializing); try sema.checkKnownAllocPtr(block, inner_ptr, field_ptr); return field_ptr; @@ -25974,7 +25943,7 @@ fn fieldCallBind( // Optionally dereference a second pointer to get the concrete type. const is_double_ptr = inner_ty.zigTypeTag(zcu) == .pointer and inner_ty.ptrSize(zcu) == .one; const concrete_ty = if (is_double_ptr) inner_ty.childType(zcu) else inner_ty; - try sema.ensureLayoutResolved(concrete_ty); + try sema.ensureLayoutResolved(concrete_ty, src); const ptr_ty = if (is_double_ptr) inner_ty else raw_ptr_ty; const object_ptr = if (is_double_ptr) try sema.analyzeLoad(block, src, raw_ptr, src) @@ -26661,7 +26630,7 @@ fn elemPtr( else => return sema.fail(block, indexable_ptr_src, "expected pointer, found '{f}'", .{indexable_ptr_ty.fmt(pt)}), }; try sema.checkIndexable(block, src, indexable_ty); - try sema.ensureLayoutResolved(indexable_ty); + try sema.ensureLayoutResolved(indexable_ty, src); const elem_ptr = switch (indexable_ty.zigTypeTag(zcu)) { .array, .vector => try sema.elemPtrArray(block, src, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init, oob_safety), @@ -26673,7 +26642,7 @@ fn elemPtr( }, else => { const indexable = try sema.analyzeLoad(block, indexable_ptr_src, indexable_ptr, indexable_ptr_src); - try sema.ensureLayoutResolved(sema.typeOf(indexable).childType(zcu)); + try sema.ensureLayoutResolved(sema.typeOf(indexable).childType(zcu), src); return elemPtrOneLayerOnly(sema, block, src, indexable, elem_index, elem_index_src, init, oob_safety); }, }; @@ -26769,7 +26738,7 @@ fn elemVal( switch (indexable_ty.zigTypeTag(zcu)) { .pointer => { const child_ty = indexable_ty.childType(zcu); - try sema.ensureLayoutResolved(child_ty); + try sema.ensureLayoutResolved(child_ty, src); switch (indexable_ty.ptrSize(zcu)) { .slice => return sema.elemValSlice(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety), .many, .c => { @@ -27293,7 +27262,7 @@ fn coerceExtra( const target = zcu.getTarget(); inst_ty.assertHasLayout(zcu); - try sema.ensureLayoutResolved(dest_ty); + try sema.ensureLayoutResolved(dest_ty, inst_src); // If the types are the same, we can return the operand. if (dest_ty.eql(inst_ty, zcu)) @@ -28657,8 +28626,8 @@ fn coerceInMemoryAllowedFns( } }; } - try sema.ensureLayoutResolved(src_ty); - try sema.ensureLayoutResolved(dest_ty); + try sema.ensureLayoutResolved(src_ty, src_src); + try sema.ensureLayoutResolved(dest_ty, dest_src); const src_is_runtime = src_ty.fnHasRuntimeBits(zcu); const dest_is_runtime = dest_ty.fnHasRuntimeBits(zcu); if (src_is_runtime != dest_is_runtime) return .{ .fn_generic = !dest_is_runtime }; @@ -28712,7 +28681,7 @@ fn coerceInMemoryAllowedFns( const src_is_comptime = src_info.paramIsComptime(@intCast(param_i)); const dest_is_comptime = dest_info.paramIsComptime(@intCast(param_i)); if (src_is_comptime == dest_is_comptime) break :comptime_param; - try sema.ensureLayoutResolved(dest_param_ty); + try sema.ensureLayoutResolved(dest_param_ty, dest_src); if (!dest_is_mut and src_is_comptime and !dest_is_comptime and dest_param_ty.comptimeOnly(zcu)) { // A parameter which is marked `comptime` can drop that annotation if the type is comptime-only. // The function remains generic, and the parameter is going to be comptime-resolved either way, @@ -28940,11 +28909,11 @@ fn coerceInMemoryAllowedPtrs( dest_info.child != src_info.child) { const src_align = if (src_info.flags.alignment == .none) a: { - try sema.ensureLayoutResolved(src_child); + try sema.ensureLayoutResolved(src_child, src_src); break :a src_child.abiAlignment(zcu); } else src_info.flags.alignment; const dest_align = if (dest_info.flags.alignment == .none) a: { - try sema.ensureLayoutResolved(dest_child); + try sema.ensureLayoutResolved(dest_child, dest_src); break :a dest_child.abiAlignment(zcu); } else dest_info.flags.alignment; if (dest_align.compare(if (dest_is_mut) .neq else .gt, src_align)) { @@ -29294,7 +29263,7 @@ fn bitCast( const old_ty = sema.typeOf(inst); old_ty.assertHasLayout(zcu); - try sema.ensureLayoutResolved(dest_ty); + try sema.ensureLayoutResolved(dest_ty, inst_src); const dest_bits = dest_ty.bitSize(zcu); const old_bits = old_ty.bitSize(zcu); @@ -29908,7 +29877,7 @@ fn analyzeNavVal( return sema.analyzeLoad(block, src, ref, src); } -fn addReferenceEntry( +pub fn addReferenceEntry( sema: *Sema, opt_block: ?*Block, src: LazySrcLoc, @@ -30176,7 +30145,7 @@ fn analyzeLoad( return sema.fail(block, ptr_src, "cannot load opaque type '{f}'", .{elem_ty.fmt(pt)}); } - try sema.ensureLayoutResolved(elem_ty); + try sema.ensureLayoutResolved(elem_ty, src); if (try elem_ty.onePossibleValue(pt)) |opv| return .fromValue(opv); if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| { @@ -30561,7 +30530,7 @@ fn analyzeSlice( else => return sema.fail(block, src, "slice of non-array type '{f}'", .{ptr_ptr_child_ty.fmt(pt)}), } - try sema.ensureLayoutResolved(elem_ty); + try sema.ensureLayoutResolved(elem_ty, src); const ptr = if (slice_ty.isSlice(zcu)) try sema.analyzeSlicePtr(block, ptr_src, ptr_or_slice, slice_ty) @@ -34024,7 +33993,7 @@ pub fn analyzeMemoizedState(sema: *Sema, block: *Block, simple_src: LazySrcLoc, .type => if (uncoerced_val.typeOf(zcu).zigTypeTag(zcu) != .type) { return sema.fail(block, src, "{s}.{s} is not a type", .{ parent_name, name }); } else val: { - try sema.ensureLayoutResolved(uncoerced_val.toType()); + try sema.ensureLayoutResolved(uncoerced_val.toType(), src); break :val uncoerced_val; }, .func => val: { @@ -34271,20 +34240,9 @@ fn zirStructDecl( }); errdefer pt.destroyNamespace(new_namespace_index); try pt.scanNamespace(new_namespace_index, struct_decl.decls); - // MLUGG TODO: we could potentially revert this language change if we wanted? don't mind - try zcu.comp.queueJob(.{ .analyze_unit = .wrap(.{ .type_layout = wip.index }) }); - try zcu.comp.queueJob(.{ .analyze_unit = .wrap(.{ .struct_defaults = wip.index }) }); if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index); - try zcu.outdated.ensureUnusedCapacity(gpa, 2); - try zcu.outdated_ready.ensureUnusedCapacity(gpa, 2); - errdefer comptime unreachable; // because we don't remove the `outdated` entries - zcu.outdated.putAssumeCapacityNoClobber(.wrap(.{ .type_layout = wip.index }), 0); - zcu.outdated.putAssumeCapacityNoClobber(.wrap(.{ .struct_defaults = wip.index }), 0); - zcu.outdated_ready.putAssumeCapacityNoClobber(.wrap(.{ .type_layout = wip.index }), {}); - zcu.outdated_ready.putAssumeCapacityNoClobber(.wrap(.{ .struct_defaults = wip.index }), {}); - break :ty .fromInterned(wip.finish(ip, new_namespace_index)); }, }; @@ -34364,17 +34322,8 @@ fn zirUnionDecl( try pt.scanNamespace(new_namespace_index, union_decl.decls); - // MLUGG TODO: we could potentially revert this language change if we wanted? don't mind - try zcu.comp.queueJob(.{ .analyze_unit = .wrap(.{ .type_layout = wip.index }) }); - if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index); - try zcu.outdated.ensureUnusedCapacity(gpa, 1); - try zcu.outdated_ready.ensureUnusedCapacity(gpa, 1); - errdefer comptime unreachable; // because we don't remove the `outdated` entry - zcu.outdated.putAssumeCapacityNoClobber(.wrap(.{ .type_layout = wip.index }), 0); - zcu.outdated_ready.putAssumeCapacityNoClobber(.wrap(.{ .type_layout = wip.index }), {}); - break :ty .fromInterned(wip.finish(ip, new_namespace_index)); }, }; @@ -34434,17 +34383,8 @@ fn zirEnumDecl( try pt.scanNamespace(new_namespace_index, enum_decl.decls); - // MLUGG TODO: we could potentially revert this language change if we wanted? don't mind - try zcu.comp.queueJob(.{ .analyze_unit = .wrap(.{ .type_layout = wip.index }) }); - if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index); - try zcu.outdated.ensureUnusedCapacity(gpa, 1); - try zcu.outdated_ready.ensureUnusedCapacity(gpa, 1); - errdefer comptime unreachable; // because we don't remove the `outdated` entry - zcu.outdated.putAssumeCapacityNoClobber(.wrap(.{ .type_layout = wip.index }), 0); - zcu.outdated_ready.putAssumeCapacityNoClobber(.wrap(.{ .type_layout = wip.index }), {}); - break :ty .fromInterned(wip.finish(ip, new_namespace_index)); }, }; diff --git a/src/Sema/LowerZon.zig b/src/Sema/LowerZon.zig index bb10a39729ad41d3e74af62428b671985c13a0cf..71256ae44ddc4e6283171172bfb0cdda3f8eb50e 100644 --- a/src/Sema/LowerZon.zig +++ b/src/Sema/LowerZon.zig @@ -300,7 +300,7 @@ fn checkTypeInner( } else { const gop = try visited.getOrPut(sema.arena, ty.toIntern()); if (gop.found_existing) return; - try sema.ensureLayoutResolved(ty); + try sema.ensureLayoutResolved(ty, self.import_loc); const struct_info = zcu.typeToStruct(ty).?; for (struct_info.field_types.get(ip)) |field_type| { try self.checkTypeInner(.fromInterned(field_type), null, visited); @@ -309,7 +309,7 @@ fn checkTypeInner( .@"union" => { const gop = try visited.getOrPut(sema.arena, ty.toIntern()); if (gop.found_existing) return; - try sema.ensureLayoutResolved(ty); + try sema.ensureLayoutResolved(ty, self.import_loc); const union_info = zcu.typeToUnion(ty).?; for (union_info.field_types.get(ip)) |field_type| { if (field_type != .void_type) { @@ -646,6 +646,7 @@ fn lowerEnum(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.I const gpa = comp.gpa; const io = comp.io; const ip = &pt.zcu.intern_pool; + try self.sema.ensureLayoutResolved(res_ty, self.import_loc); switch (node.get(self.file.zoir.?)) { .enum_literal => |field_name| { const field_name_interned = try ip.getOrPutString( @@ -768,8 +769,8 @@ fn lowerStruct(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool const io = comp.io; const ip = &pt.zcu.intern_pool; - try self.sema.ensureLayoutResolved(res_ty); - try self.sema.ensureStructDefaultsResolved(res_ty); + try self.sema.ensureLayoutResolved(res_ty, self.import_loc); + try self.sema.ensureStructDefaultsResolved(res_ty, self.import_loc); const struct_info = self.sema.pt.zcu.typeToStruct(res_ty).?; const fields: @FieldType(Zoir.Node, "struct_literal") = switch (node.get(self.file.zoir.?)) { @@ -919,7 +920,7 @@ fn lowerUnion(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool. const gpa = comp.gpa; const io = comp.io; const ip = &pt.zcu.intern_pool; - try self.sema.ensureLayoutResolved(res_ty); + try self.sema.ensureLayoutResolved(res_ty, self.import_loc); const union_info = pt.zcu.typeToUnion(res_ty).?; const enum_tag_info = ip.loadEnumType(union_info.enum_tag_type); diff --git a/src/Sema/bitcast.zig b/src/Sema/bitcast.zig index ee70bd1746466aaf57c8812083f37892e69dac59..b496db1ce0e167aad6cacf51568ecc9bb91cde97 100644 --- a/src/Sema/bitcast.zig +++ b/src/Sema/bitcast.zig @@ -80,7 +80,7 @@ fn bitCastInner( const val_ty = val.typeOf(zcu); val_ty.assertHasLayout(zcu); - try sema.ensureLayoutResolved(dest_ty); + dest_ty.assertHasLayout(zcu); assert(val_ty.hasWellDefinedLayout(zcu)); @@ -138,8 +138,8 @@ fn bitCastSpliceInner( const val_ty = val.typeOf(zcu); const splice_val_ty = splice_val.typeOf(zcu); - try sema.ensureLayoutResolved(val_ty); - try sema.ensureLayoutResolved(splice_val_ty); + val_ty.assertHasLayout(zcu); + splice_val_ty.assertHasLayout(zcu); const splice_bits = splice_val_ty.bitSize(zcu); diff --git a/src/Sema/type_resolution.zig b/src/Sema/type_resolution.zig index f18cf9aaaeae1557d1a69799516d0f7687096dd0..970cf4f0116328dc6f84fe4c1e755eb71a4a915a 100644 --- a/src/Sema/type_resolution.zig +++ b/src/Sema/type_resolution.zig @@ -19,7 +19,7 @@ const arith = @import("arith.zig"); /// Adds incremental dependencies tracking any required type resolution. /// MLUGG TODO: to make the langspec non-stupid, we need to call this from WAY fewer places (the conditions need to be less specific). /// MLUGG TODO: to be clear, i should audit EVERY use of this before PRing -pub fn ensureLayoutResolved(sema: *Sema, ty: Type) SemaError!void { +pub fn ensureLayoutResolved(sema: *Sema, ty: Type, src: LazySrcLoc) SemaError!void { const pt = sema.pt; const zcu = pt.zcu; const ip = &zcu.intern_pool; @@ -35,20 +35,21 @@ pub fn ensureLayoutResolved(sema: *Sema, ty: Type) SemaError!void { .func_type => |func_type| { for (func_type.param_types.get(ip)) |param_ty| { - try ensureLayoutResolved(sema, .fromInterned(param_ty)); + try ensureLayoutResolved(sema, .fromInterned(param_ty), src); } - try ensureLayoutResolved(sema, .fromInterned(func_type.return_type)); + try ensureLayoutResolved(sema, .fromInterned(func_type.return_type), src); }, - .array_type => |arr| return ensureLayoutResolved(sema, .fromInterned(arr.child)), - .vector_type => |vec| return ensureLayoutResolved(sema, .fromInterned(vec.child)), - .opt_type => |child| return ensureLayoutResolved(sema, .fromInterned(child)), - .error_union_type => |eu| return ensureLayoutResolved(sema, .fromInterned(eu.payload_type)), + .array_type => |arr| return ensureLayoutResolved(sema, .fromInterned(arr.child), src), + .vector_type => |vec| return ensureLayoutResolved(sema, .fromInterned(vec.child), src), + .opt_type => |child| return ensureLayoutResolved(sema, .fromInterned(child), src), + .error_union_type => |eu| return ensureLayoutResolved(sema, .fromInterned(eu.payload_type), src), .tuple_type => |tuple| for (tuple.types.get(ip)) |field_ty| { - try ensureLayoutResolved(sema, .fromInterned(field_ty)); + try ensureLayoutResolved(sema, .fromInterned(field_ty), src); }, .struct_type, .union_type, .enum_type => { try sema.declareDependency(.{ .type_layout = ty.toIntern() }); + try sema.addReferenceEntry(null, src, .wrap(.{ .type_layout = ty.toIntern() })); if (zcu.analysis_in_progress.contains(.wrap(.{ .type_layout = ty.toIntern() }))) { // TODO: better error message return sema.failWithOwnedErrorMsg(null, try sema.errMsg( @@ -89,13 +90,14 @@ pub fn ensureLayoutResolved(sema: *Sema, ty: Type) SemaError!void { /// /// It is not necessary to call this function to query the values of comptime fields: those values /// are available from type *layout* resolution, see `ensureLayoutResolved`. -pub fn ensureStructDefaultsResolved(sema: *Sema, ty: Type) SemaError!void { +pub fn ensureStructDefaultsResolved(sema: *Sema, ty: Type, src: LazySrcLoc) SemaError!void { const pt = sema.pt; const zcu = pt.zcu; const ip = &zcu.intern_pool; assert(ip.indexToKey(ty.toIntern()) == .struct_type); try sema.declareDependency(.{ .struct_defaults = ty.toIntern() }); + try sema.addReferenceEntry(null, src, .wrap(.{ .struct_defaults = ty.toIntern() })); if (zcu.analysis_in_progress.contains(.wrap(.{ .struct_defaults = ty.toIntern() }))) { // TODO: better error message return sema.failWithOwnedErrorMsg(null, try sema.errMsg( @@ -120,7 +122,8 @@ pub fn resolveStructLayout(sema: *Sema, struct_ty: Type) CompileError!void { assert(sema.owner.unwrap().type_layout == struct_ty.toIntern()); const struct_obj = ip.loadStructType(struct_ty.toIntern()); - const zir_index = struct_obj.zir_index.resolve(ip).?; + assert(struct_obj.want_layout); + const zir_index = struct_obj.zir_index.resolve(ip) orelse return error.AnalysisFail; var block: Block = .{ .parent = null, @@ -219,7 +222,7 @@ pub fn resolveStructLayout(sema: *Sema, struct_ty: Type) CompileError!void { const field_ty: Type = .fromInterned(field_ty_ip); assert(!field_ty.isGenericPoison()); const field_ty_src = block.src(.{ .container_field_type = @intCast(field_index) }); - try sema.ensureLayoutResolved(field_ty); + try sema.ensureLayoutResolved(field_ty, field_ty_src); if (field_ty.zigTypeTag(zcu) == .@"opaque") { return sema.failWithOwnedErrorMsg(&block, msg: { @@ -368,7 +371,7 @@ fn resolvePackedStructLayout( const field_ty: Type = .fromInterned(field_ty_ip); assert(!field_ty.isGenericPoison()); const field_ty_src = block.src(.{ .container_field_type = @intCast(field_index) }); - try sema.ensureLayoutResolved(field_ty); + try sema.ensureLayoutResolved(field_ty, field_ty_src); if (field_ty.zigTypeTag(zcu) == .@"opaque") { return sema.failWithOwnedErrorMsg(block, msg: { const msg = try sema.errMsg(field_ty_src, "cannot directly embed opaque type '{f}' in struct", .{field_ty.fmt(pt)}); @@ -458,17 +461,20 @@ pub fn resolveStructDefaults(sema: *Sema, struct_ty: Type) CompileError!void { assert(sema.owner.unwrap().struct_defaults == struct_ty.toIntern()); - try sema.ensureLayoutResolved(struct_ty); + try sema.ensureLayoutResolved(struct_ty, struct_ty.srcLoc(zcu)); const struct_obj = ip.loadStructType(struct_ty.toIntern()); + assert(struct_obj.want_defaults); + + if (struct_obj.is_reified) { + // `Sema.zirReifyStruct` has already populated the default field values *and* (by loading + // the default values from pointers) validated their types, so we have nothing to do. We + // don't even need to mark any dependencies. + return; + } try sema.declareDependency(.{ .src_hash = struct_obj.zir_index }); - // This logic isn't used for reified structs, because the signature of `@Struct` requires that - // default values are populated and correctly typed from the moment the struct type is interned - // (because `Sema.zirReifyStruct` had to dereference the default value from a pointer). - assert(!struct_obj.is_reified); - if (struct_obj.field_defaults.len == 0) { // The struct has no default field values, so the slice has been omitted. return; @@ -509,7 +515,7 @@ fn resolveStructDefaultsInner( const ip = &zcu.intern_pool; // We'll need to map the struct decl instruction to provide result types - const zir_index = struct_obj.zir_index.resolve(ip).?; + const zir_index = struct_obj.zir_index.resolve(ip) orelse return error.AnalysisFail; try sema.inst_map.ensureSpaceForInstructions(gpa, &.{zir_index}); const field_types = struct_obj.field_types.get(ip); @@ -555,7 +561,8 @@ pub fn resolveUnionLayout(sema: *Sema, union_ty: Type) CompileError!void { assert(sema.owner.unwrap().type_layout == union_ty.toIntern()); const union_obj = ip.loadUnionType(union_ty.toIntern()); - const zir_index = union_obj.zir_index.resolve(ip).?; + assert(union_obj.want_layout); + const zir_index = union_obj.zir_index.resolve(ip) orelse return error.AnalysisFail; var block: Block = .{ .parent = null, @@ -627,16 +634,11 @@ pub fn resolveUnionLayout(sema: *Sema, union_ty: Type) CompileError!void { .generation = zcu.generation, }); if (comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index); - try zcu.outdated.ensureUnusedCapacity(gpa, 1); - try zcu.outdated_ready.ensureUnusedCapacity(gpa, 1); - errdefer comptime unreachable; // because we don't remove the `outdated` entry - zcu.outdated.putAssumeCapacityNoClobber(.wrap(.{ .type_layout = wip.index }), 0); - zcu.outdated_ready.putAssumeCapacityNoClobber(.wrap(.{ .type_layout = wip.index }), {}); break :tag_ty .fromInterned(wip.finish(ip, new_namespace_index)); }, }; - try sema.ensureLayoutResolved(enum_tag_ty); + try sema.ensureLayoutResolved(enum_tag_ty, block.src(.container_arg)); const enum_obj = ip.loadEnumType(enum_tag_ty.toIntern()); if (union_obj.is_reified) { @@ -731,7 +733,7 @@ pub fn resolveUnionLayout(sema: *Sema, union_ty: Type) CompileError!void { const field_ty: Type = .fromInterned(field_ty_ip); assert(!field_ty.isGenericPoison()); const field_ty_src = block.src(.{ .container_field_type = @intCast(field_index) }); - try sema.ensureLayoutResolved(field_ty); + try sema.ensureLayoutResolved(field_ty, field_ty_src); if (field_ty.zigTypeTag(zcu) == .@"opaque") { return sema.failWithOwnedErrorMsg(&block, msg: { const msg = try sema.errMsg(field_ty_src, "cannot directly embed opaque type '{f}' in union", .{field_ty.fmt(pt)}); @@ -889,7 +891,7 @@ fn resolvePackedUnionLayout( const field_ty: Type = .fromInterned(field_ty_ip); assert(!field_ty.isGenericPoison()); const field_ty_src = block.src(.{ .container_field_type = @intCast(field_index) }); - try sema.ensureLayoutResolved(field_ty); + try sema.ensureLayoutResolved(field_ty, field_ty_src); if (field_ty.zigTypeTag(zcu) == .@"opaque") { return sema.failWithOwnedErrorMsg(block, msg: { const msg = try sema.errMsg(field_ty_src, "cannot directly embed opaque type '{f}' in union", .{field_ty.fmt(pt)}); @@ -995,6 +997,7 @@ pub fn resolveEnumLayout(sema: *Sema, enum_ty: Type) CompileError!void { assert(sema.owner.unwrap().type_layout == enum_ty.toIntern()); const enum_obj = ip.loadEnumType(enum_ty.toIntern()); + assert(enum_obj.want_layout); const maybe_parent_union_obj: ?InternPool.LoadedUnionType = un: { if (enum_obj.owner_union == .none) break :un null; @@ -1002,6 +1005,7 @@ pub fn resolveEnumLayout(sema: *Sema, enum_ty: Type) CompileError!void { }; const tracked_inst = enum_obj.zir_index.unwrap() orelse maybe_parent_union_obj.?.zir_index; + const zir_index = tracked_inst.resolve(ip) orelse return error.AnalysisFail; var block: Block = .{ .parent = null, @@ -1040,7 +1044,7 @@ pub fn resolveEnumLayout(sema: *Sema, enum_ty: Type) CompileError!void { // Generated tag enums for declared unions do not yet have field names populated. It is // our job to populate them now. try sema.declareDependency(.{ .src_hash = union_obj.zir_index }); - const zir_union = sema.code.getUnionDecl(union_obj.zir_index.resolve(ip).?); + const zir_union = sema.code.getUnionDecl(zir_index); for (zir_union.field_names) |zir_field_name| { const name_slice = sema.code.nullTerminatedString(zir_field_name); const name = try ip.getOrPutString(gpa, io, pt.tid, name_slice, .no_embedded_nulls); @@ -1065,7 +1069,7 @@ pub fn resolveEnumLayout(sema: *Sema, enum_ty: Type) CompileError!void { } else { // Declared enums do not yet have field names populated. It is our job to populate them now. try sema.declareDependency(.{ .src_hash = enum_obj.zir_index.unwrap().? }); - const zir_enum = sema.code.getEnumDecl(enum_obj.zir_index.unwrap().?.resolve(ip).?); + const zir_enum = sema.code.getEnumDecl(zir_index); for (zir_enum.field_names) |zir_field_name| { const name_slice = sema.code.nullTerminatedString(zir_field_name); const name = try ip.getOrPutString(gpa, io, pt.tid, name_slice, .no_embedded_nulls); @@ -1087,7 +1091,6 @@ pub fn resolveEnumLayout(sema: *Sema, enum_ty: Type) CompileError!void { // Reification has no equivalent of 'union(enum(T))'. break :ty null; } - const zir_index = union_obj.zir_index.resolve(ip).?; const zir_union = sema.code.getUnionDecl(zir_index); if (zir_union.kind != .tagged_enum_explicit) { break :ty null; // int tag type will be inferred @@ -1102,7 +1105,6 @@ pub fn resolveEnumLayout(sema: *Sema, enum_ty: Type) CompileError!void { const type_ref = try sema.resolveInlineBody(&block, tag_type_body, zir_index); break :ty try sema.analyzeAsType(&block, tag_type_src, .enum_int_tag_type, type_ref); } else ty: { - const zir_index = enum_obj.zir_index.unwrap().?.resolve(ip).?; const zir_enum = sema.code.getEnumDecl(zir_index); const tag_type_body = zir_enum.tag_type_body orelse { break :ty null; // int tag type will be inferred @@ -1147,8 +1149,6 @@ pub fn resolveEnumLayout(sema: *Sema, enum_ty: Type) CompileError!void { // There may be old field values in here from a previous update. field_value_map.get(ip).clearRetainingCapacity(); - const zir_index = tracked_inst.resolve(ip).?; - // Map the enum (or union) decl instruction to provide the tag type as the result type try sema.inst_map.ensureSpaceForInstructions(gpa, &.{zir_index}); sema.inst_map.putAssumeCapacity(zir_index, .fromIntern(int_tag_ty.toIntern())); diff --git a/src/Type.zig b/src/Type.zig index ca94c09bf04fc00503dc414541a0861194cf4256..5b5839cf6f40e5953e182162289aa2e7b05ba96d 100644 --- a/src/Type.zig +++ b/src/Type.zig @@ -3044,7 +3044,20 @@ pub fn assertHasLayout(ty: Type, zcu: *const Zcu) void { .tuple_type => |tuple| for (tuple.types.get(&zcu.intern_pool)) |field_ty| { assertHasLayout(.fromInterned(field_ty), zcu); }, - .struct_type, .union_type, .enum_type => { + .struct_type => { + assert(zcu.intern_pool.loadStructType(ty.toIntern()).want_layout); + const unit: InternPool.AnalUnit = .wrap(.{ .type_layout = ty.toIntern() }); + assert(!zcu.outdated.contains(unit)); + assert(!zcu.potentially_outdated.contains(unit)); + }, + .union_type => { + assert(zcu.intern_pool.loadUnionType(ty.toIntern()).want_layout); + const unit: InternPool.AnalUnit = .wrap(.{ .type_layout = ty.toIntern() }); + assert(!zcu.outdated.contains(unit)); + assert(!zcu.potentially_outdated.contains(unit)); + }, + .enum_type => { + assert(zcu.intern_pool.loadEnumType(ty.toIntern()).want_layout); const unit: InternPool.AnalUnit = .wrap(.{ .type_layout = ty.toIntern() }); assert(!zcu.outdated.contains(unit)); assert(!zcu.potentially_outdated.contains(unit)); diff --git a/src/Value.zig b/src/Value.zig index de7aacd1e1f2c84737acb243aa1565ac5fc0a94b..d513e5d07d9d07311a7d15b106e57795b54e92be 100644 --- a/src/Value.zig +++ b/src/Value.zig @@ -2149,7 +2149,7 @@ pub fn pointerDerivationAdvanced(ptr_val: Value, arena: Allocator, pt: Zcu.PerTh const base_ptr = Value.fromInterned(field.base); const base_ptr_ty = base_ptr.typeOf(zcu); const agg_ty = base_ptr_ty.childType(zcu); - if (resolve_types) try opt_sema.?.ensureLayoutResolved(agg_ty); + if (resolve_types) try opt_sema.?.ensureLayoutResolved(agg_ty, .unneeded); // MLUGG TODO: unneeded is a hack const field_ty: Type, const field_align: InternPool.Alignment = switch (agg_ty.zigTypeTag(zcu)) { .@"struct", .@"union" => .{ agg_ty.fieldType(@intCast(field.index), zcu), agg_ty.resolvedFieldAlignment(@intCast(field.index), pt.zcu) }, .pointer => switch (field.index) { diff --git a/src/Zcu.zig b/src/Zcu.zig index 3b17bc1c0981d1e19f57db43412f4b309484faf4..aedbf7043c32aa83d8e7988d8bd3bb2304476632 100644 --- a/src/Zcu.zig +++ b/src/Zcu.zig @@ -266,9 +266,6 @@ outdated_ready: std.AutoArrayHashMapUnmanaged(AnalUnit, void) = .empty, /// it as outdated. retryable_failures: std.ArrayList(AnalUnit) = .empty, -func_body_analysis_queued: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .empty, -nav_val_analysis_queued: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void) = .empty, - /// These are the modules which we initially queue for analysis in `Compilation.update`. /// `resolveReferences` will use these as the root of its reachability traversal. analysis_roots_buffer: [5]*Package.Module, @@ -2814,9 +2811,6 @@ pub fn deinit(zcu: *Zcu) void { zcu.outdated_ready.deinit(gpa); zcu.retryable_failures.deinit(gpa); - zcu.func_body_analysis_queued.deinit(gpa); - zcu.nav_val_analysis_queued.deinit(gpa); - zcu.test_functions.deinit(gpa); for (zcu.global_assembly.values()) |s| { @@ -3179,8 +3173,6 @@ fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: AnalUni /// recursive analysis (all of its previously-marked dependencies are already up-to-date), because /// recursive analysis can cause over-analysis on incremental updates. pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?AnalUnit { - if (!zcu.comp.config.incremental) return null; - if (zcu.outdated_ready.count() > 0) { const unit = zcu.outdated_ready.keys()[0]; log.debug("findOutdatedToAnalyze: {f}", .{zcu.fmtAnalUnit(unit)}); @@ -3458,47 +3450,35 @@ pub fn mapOldZirToNew( /// The caller is responsible for ensuring the function decl itself is already /// analyzed, and for ensuring it can exist at runtime (see /// `Type.fnHasRuntimeBitsSema`). This function does *not* guarantee that the body -/// will be analyzed when it returns: for that, see `ensureFuncBodyAnalyzed`. -pub fn ensureFuncBodyAnalysisQueued(zcu: *Zcu, func_index: InternPool.Index) !void { +/// will be analyzed when it returns: for that, see `PerThread.ensureFuncBodyUpToDate`. +pub fn ensureFuncBodyAnalysisQueued(zcu: *Zcu, func: InternPool.Index) !void { + const comp = zcu.comp; + const gpa = comp.gpa; + const io = comp.io; const ip = &zcu.intern_pool; - - const func = zcu.funcInfo(func_index); - - assert(func.ty == func.uncoerced_ty); // analyze the body of the original function, not a coerced one - - if (zcu.func_body_analysis_queued.contains(func_index)) return; - - if (func.analysisUnordered(ip).is_analyzed) { - if (!zcu.outdated.contains(.wrap(.{ .func = func_index })) and - !zcu.potentially_outdated.contains(.wrap(.{ .func = func_index }))) - { - // This function has been analyzed before and is definitely up-to-date. - return; - } + assert(func == ip.unwrapCoercedFunc(func)); // analyze the body of the original function, not a coerced one + if (ip.setWantRuntimeFnAnalysis(io, func)) { + // This is the first reference to this function, so we must ensure it will be analyzed. + const unit: AnalUnit = .wrap(.{ .func = func }); + try zcu.outdated.putNoClobber(gpa, unit, 0); + try zcu.outdated_ready.putNoClobber(gpa, unit, {}); } - - try zcu.func_body_analysis_queued.ensureUnusedCapacity(zcu.gpa, 1); - try zcu.comp.queueJob(.{ .analyze_unit = .wrap(.{ .func = func_index }) }); - zcu.func_body_analysis_queued.putAssumeCapacityNoClobber(func_index, {}); } -pub fn ensureNavValAnalysisQueued(zcu: *Zcu, nav_id: InternPool.Nav.Index) !void { +pub fn ensureNavValAnalysisQueued(zcu: *Zcu, nav: InternPool.Nav.Index) !void { + const comp = zcu.comp; + const gpa = comp.gpa; + const io = comp.io; const ip = &zcu.intern_pool; - - if (zcu.nav_val_analysis_queued.contains(nav_id)) return; - - if (ip.getNav(nav_id).status == .fully_resolved) { - if (!zcu.outdated.contains(.wrap(.{ .nav_val = nav_id })) and - !zcu.potentially_outdated.contains(.wrap(.{ .nav_val = nav_id }))) - { - // This `Nav` has been analyzed before and is definitely up-to-date. - return; - } + if (ip.setWantNavAnalysis(io, nav)) { + // This is the first reference to this function, so we must ensure it will be analyzed. + try zcu.outdated.ensureUnusedCapacity(gpa, 2); + try zcu.outdated_ready.ensureUnusedCapacity(gpa, 2); + zcu.outdated.putAssumeCapacityNoClobber(.wrap(.{ .nav_val = nav }), 0); + zcu.outdated.putAssumeCapacityNoClobber(.wrap(.{ .nav_ty = nav }), 0); + zcu.outdated_ready.putAssumeCapacityNoClobber(.wrap(.{ .nav_val = nav }), {}); + zcu.outdated_ready.putAssumeCapacityNoClobber(.wrap(.{ .nav_ty = nav }), {}); } - - try zcu.nav_val_analysis_queued.ensureUnusedCapacity(zcu.gpa, 1); - try zcu.comp.queueJob(.{ .analyze_unit = .wrap(.{ .nav_val = nav_id }) }); - zcu.nav_val_analysis_queued.putAssumeCapacityNoClobber(nav_id, {}); } pub const ImportResult = struct { @@ -4035,37 +4015,6 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoArrayHashMapUnmanaged(AnalUnit, ?R refs_log.debug("handle type '{f}'", .{Type.fromInterned(ty).containerTypeName(ip).fmt(ip)}); - // If this type undergoes type resolution, the corresponding `AnalUnit`s are automatically referenced. - const has_layout: bool, const has_inits: bool = switch (ip.indexToKey(ty)) { - .struct_type => .{ true, true }, - .union_type => .{ true, false }, - .enum_type => .{ false, true }, - .opaque_type => .{ false, false }, - else => unreachable, - }; - if (has_layout) { - // this should only be referenced by the type - const unit: AnalUnit = .wrap(.{ .type_layout = ty }); - try units.putNoClobber(gpa, unit, referencer); - } - if (has_inits) { - // this should only be referenced by the type - const unit: AnalUnit = .wrap(.{ .struct_defaults = ty }); - try units.putNoClobber(gpa, unit, referencer); - } - - // If this is a union with a generated tag, its tag type is automatically referenced. - // We don't add this reference for non-generated tags, as those will already be referenced via the union's type resolution, with a better source location. - implicit_tag: { - const loaded_union = zcu.typeToUnion(.fromInterned(ty)) orelse break :implicit_tag; - const tag_ty = loaded_union.enum_tag_type; - if (tag_ty == .none) break :implicit_tag; - if (ip.indexToKey(tag_ty).enum_type != .generated_union_tag) break :implicit_tag; - const gop = try types.getOrPut(gpa, tag_ty); - if (gop.found_existing) break :implicit_tag; - gop.value_ptr.* = referencer; - } - // Queue any decls within this type which would be automatically analyzed. // Keep in sync with analysis queueing logic in `Zcu.PerThread.ScanDeclIter.scanDecl`. const ns = Type.fromInterned(ty).getNamespace(zcu).unwrap().?; diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index d937367c4ac871c61e7bd285e9ddf04f7df175c7..00c911caf4e67e3a7dcf5691ebe7cf45d53ff002 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -719,20 +719,9 @@ pub fn ensureFileAnalyzed(pt: Zcu.PerThread, file_index: Zcu.File.Index) (Alloca }); errdefer pt.destroyNamespace(new_namespace_index); try pt.scanNamespace(new_namespace_index, struct_decl.decls); - // MLUGG TODO: we could potentially revert this language change if we wanted? don't mind - try zcu.comp.queueJob(.{ .analyze_unit = .wrap(.{ .type_layout = wip.index }) }); - try zcu.comp.queueJob(.{ .analyze_unit = .wrap(.{ .struct_defaults = wip.index }) }); if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index); - try zcu.outdated.ensureUnusedCapacity(gpa, 2); - try zcu.outdated_ready.ensureUnusedCapacity(gpa, 2); - errdefer comptime unreachable; // because we don't remove the `outdated` entries - zcu.outdated.putAssumeCapacityNoClobber(.wrap(.{ .type_layout = wip.index }), 0); - zcu.outdated.putAssumeCapacityNoClobber(.wrap(.{ .struct_defaults = wip.index }), 0); - zcu.outdated_ready.putAssumeCapacityNoClobber(.wrap(.{ .type_layout = wip.index }), {}); - zcu.outdated_ready.putAssumeCapacityNoClobber(.wrap(.{ .struct_defaults = wip.index }), {}); - const file_root_type: Type = .fromInterned(wip.finish(ip, new_namespace_index)); zcu.setFileRootType(file_index, file_root_type.toIntern()); @@ -1075,11 +1064,12 @@ pub fn ensureTypeLayoutUpToDate(pt: Zcu.PerThread, ty: Type) Zcu.SemaError!void assert(!zcu.analysis_in_progress.contains(anal_unit)); const was_outdated = zcu.outdated.swapRemove(anal_unit) or - zcu.potentially_outdated.swapRemove(anal_unit); + zcu.potentially_outdated.swapRemove(anal_unit) or + zcu.intern_pool.setWantTypeLayout(zcu.comp.io, ty.toIntern()); if (was_outdated) { _ = zcu.outdated_ready.swapRemove(anal_unit); - // `was_outdated` can be true in the initial update for comptime units, so this isn't a `dev.check`. + // `was_outdated` is true in the initial update, so this isn't a `dev.check`. if (dev.env.supports(.incremental)) { zcu.deleteUnitExports(anal_unit); zcu.deleteUnitReferences(anal_unit); @@ -1182,16 +1172,13 @@ pub fn ensureStructDefaultsUpToDate(pt: Zcu.PerThread, ty: Type) Zcu.SemaError!v assert(!zcu.analysis_in_progress.contains(anal_unit)); - // Determine whether or not this type is outdated. For this kind of `AnalUnit`, that's - // the only indicator as to whether or not analysis is required; when a struct/enum is - // first created, it's marked as outdated. - const was_outdated = zcu.outdated.swapRemove(anal_unit) or - zcu.potentially_outdated.swapRemove(anal_unit); + zcu.potentially_outdated.swapRemove(anal_unit) or + zcu.intern_pool.setWantStructDefaults(zcu.comp.io, ty.toIntern()); if (was_outdated) { _ = zcu.outdated_ready.swapRemove(anal_unit); - // `was_outdated` can be true in the initial update for comptime units, so this isn't a `dev.check`. + // `was_outdated` is true in the initial update, so this isn't a `dev.check`. if (dev.env.supports(.incremental)) { zcu.deleteUnitExports(anal_unit); zcu.deleteUnitReferences(anal_unit); @@ -1279,8 +1266,6 @@ pub fn ensureNavValUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu const gpa = zcu.gpa; const ip = &zcu.intern_pool; - _ = zcu.nav_val_analysis_queued.swapRemove(nav_id); - const anal_unit: AnalUnit = .wrap(.{ .nav_val = nav_id }); const nav = ip.getNav(nav_id); @@ -1288,6 +1273,8 @@ pub fn ensureNavValUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu assert(!zcu.analysis_in_progress.contains(anal_unit)); + try zcu.ensureNavValAnalysisQueued(nav_id); + // Determine whether or not this `Nav`'s value is outdated. This also includes checking if the // status is `.unresolved`, which indicates that the value is outdated because it has *never* // been analyzed so far. @@ -1317,10 +1304,8 @@ pub fn ensureNavValUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu } else { // We can trust the current information about this unit. if (prev_failed) return error.AnalysisFail; - switch (nav.status) { - .unresolved, .type_resolved => {}, - .fully_resolved => return, - } + assert(nav.status == .fully_resolved); + return; } if (zcu.comp.debugIncremental()) { @@ -1488,9 +1473,7 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr const maybe_ty: ?Type = if (zir_decl.type_body != null) ty: { // Since we have a type body, the type is resolved separately! - // Of course, we need to make sure we depend on it properly. - try sema.declareDependency(.{ .nav_ty = nav_id }); - try pt.ensureNavTypeUpToDate(nav_id); + try sema.ensureNavResolved(&block, init_src, nav_id, .type); break :ty .fromInterned(ip.getNav(nav_id).typeOf(ip)); } else null; @@ -1602,7 +1585,7 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr // This resolves the type of the resolved value, not that value itself. If `nav_val` is a struct type, // this resolves the type `type` (which needs no resolution), not the struct itself. - try sema.ensureLayoutResolved(nav_ty); + try sema.ensureLayoutResolved(nav_ty, init_src); const queue_linker_work, const is_owned_fn = switch (ip.indexToKey(nav_val.toIntern())) { .func => |f| .{ true, f.owner_nav == nav_id }, // note that this lets function aliases reach codegen @@ -1692,6 +1675,8 @@ pub fn ensureNavTypeUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zc assert(!zcu.analysis_in_progress.contains(anal_unit)); + try zcu.ensureNavValAnalysisQueued(nav_id); + const type_resolved_by_value: bool = from_val: { const analysis = nav.analysis orelse break :from_val false; const inst_resolved = analysis.zir_index.resolveFull(ip) orelse break :from_val false; @@ -1733,10 +1718,8 @@ pub fn ensureNavTypeUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zc } else { // We can trust the current information about this unit. if (prev_failed) return error.AnalysisFail; - switch (nav.status) { - .unresolved => {}, - .type_resolved, .fully_resolved => return, - } + assert(nav.status != .unresolved); + return; } if (zcu.comp.debugIncremental()) { @@ -1869,7 +1852,7 @@ fn analyzeNavType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileEr break :ty .fromInterned(type_ref.toInterned().?); }; - try sema.ensureLayoutResolved(resolved_ty); + try sema.ensureLayoutResolved(resolved_ty, ty_src); // In the case where the type is specified, this function is also responsible for resolving // the pointer modifiers, i.e. alignment, linksection, addrspace. @@ -1929,8 +1912,6 @@ pub fn ensureFuncBodyUpToDate(pt: Zcu.PerThread, func_index: InternPool.Index) Z const gpa = zcu.gpa; const ip = &zcu.intern_pool; - _ = zcu.func_body_analysis_queued.swapRemove(func_index); - const anal_unit: AnalUnit = .wrap(.{ .func = func_index }); log.debug("ensureFuncBodyUpToDate {f}", .{zcu.fmtAnalUnit(anal_unit)}); @@ -1942,7 +1923,8 @@ pub fn ensureFuncBodyUpToDate(pt: Zcu.PerThread, func_index: InternPool.Index) Z assert(func.ty == func.uncoerced_ty); // analyze the body of the original function, not a coerced one const was_outdated = zcu.outdated.swapRemove(anal_unit) or - zcu.potentially_outdated.swapRemove(anal_unit); + zcu.potentially_outdated.swapRemove(anal_unit) or + ip.setWantRuntimeFnAnalysis(zcu.comp.io, func_index); const prev_failed = zcu.failed_analysis.contains(anal_unit) or zcu.transitive_failed_analysis.contains(anal_unit); @@ -1958,10 +1940,8 @@ pub fn ensureFuncBodyUpToDate(pt: Zcu.PerThread, func_index: InternPool.Index) Z _ = zcu.transitive_failed_analysis.swapRemove(anal_unit); } else { // We can trust the current information about this function. - if (prev_failed) { - return error.AnalysisFail; - } - if (func.analysisUnordered(ip).is_analyzed) return; + if (prev_failed) return error.AnalysisFail; + return; } if (zcu.comp.debugIncremental()) { @@ -3026,7 +3006,6 @@ fn analyzeFuncBodyInner(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.Sem try zcu.analysis_in_progress.putNoClobber(gpa, anal_unit, {}); defer assert(zcu.analysis_in_progress.swapRemove(anal_unit)); - func.setAnalyzed(ip, io); if (func.analysisUnordered(ip).inferred_error_set) { func.setResolvedErrorSet(ip, io, .none); } @@ -3144,7 +3123,7 @@ fn analyzeFuncBodyInner(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.Sem const param_ty: Type = .fromInterned(fn_ty_info.param_types.get(ip)[runtime_param_index]); runtime_param_index += 1; - try sema.ensureLayoutResolved(param_ty); + try sema.ensureLayoutResolved(param_ty, inner_block.src(.{ .func_decl_param_ty = @intCast(zir_param_index) })); if (try param_ty.onePossibleValue(pt)) |opv| { gop.value_ptr.* = .fromValue(opv); continue; @@ -3161,7 +3140,7 @@ fn analyzeFuncBodyInner(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.Sem }); } - try sema.ensureLayoutResolved(sema.fn_ret_ty); + try sema.ensureLayoutResolved(sema.fn_ret_ty, inner_block.src(.{ .node_offset_fn_type_ret_ty = .zero })); const last_arg_index = inner_block.instructions.items.len; -- 2.54.0