From 814491f59921600b8a0137733b48d4b0f6b99d78 Mon Sep 17 00:00:00 2001 From: mlugg Date: Fri, 3 Jan 2025 23:47:57 +0000 Subject: [PATCH 1/5] Zcu: fix crash when incremental re-analysis of type annotation yields same result --- src/Zcu/PerThread.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index d304ee3533cb..4175223f14f4 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -909,7 +909,7 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr // Of course, we need to make sure we depend on it properly. try sema.declareDependency(.{ .nav_ty = nav_id }); try pt.ensureNavTypeUpToDate(nav_id); - break :ty .fromInterned(ip.getNav(nav_id).status.type_resolved.type); + break :ty .fromInterned(ip.getNav(nav_id).typeOf(ip)); } else null; const final_val: ?Value = if (zir_decl.value_body) |value_body| val: { From f818098971fdafdaaa6af4b927f2cf47332ed5f7 Mon Sep 17 00:00:00 2001 From: mlugg Date: Fri, 3 Jan 2025 23:48:27 +0000 Subject: [PATCH 2/5] incremental: correctly return `error.AnalysisFail` when type structure changes `Zcu.PerThead.ensureTypeUpToDate` is set up in such a way that it only returns the updated type the first time it is called. In general, that's okay; however, the exception is that we want the function to continue returning `error.AnalysisFail` when the type has been lost, or its number of captures changed. Therefore, the check for this case now happens before the up-to-date success return. For simplicity, the number of captures is now handled by intentionally losing the instruction in `Zcu.mapOldZirToNew`, since there is nothing to gain from tracking a type when old instances of it can never be reused. --- lib/std/zig/Zir.zig | 32 +++++++++++++++++ src/InternPool.zig | 20 ++++++----- src/Sema.zig | 2 ++ src/Zcu.zig | 21 +++++------ src/Zcu/PerThread.zig | 83 ++++++++++++++++++++++--------------------- 5 files changed, 96 insertions(+), 62 deletions(-) diff --git a/lib/std/zig/Zir.zig b/lib/std/zig/Zir.zig index c8937094b532..21f59f8e8398 100644 --- a/lib/std/zig/Zir.zig +++ b/lib/std/zig/Zir.zig @@ -5066,3 +5066,35 @@ pub fn assertTrackable(zir: Zir, inst_idx: Zir.Inst.Index) void { else => unreachable, // assertion failure; not trackable } } + +pub fn typeCapturesLen(zir: Zir, type_decl: Inst.Index) u32 { + const inst = zir.instructions.get(@intFromEnum(type_decl)); + assert(inst.tag == .extended); + switch (inst.data.extended.opcode) { + .struct_decl => { + const small: Inst.StructDecl.Small = @bitCast(inst.data.extended.small); + if (!small.has_captures_len) return 0; + const extra = zir.extraData(Inst.StructDecl, inst.data.extended.operand); + return zir.extra[extra.end]; + }, + .union_decl => { + const small: Inst.UnionDecl.Small = @bitCast(inst.data.extended.small); + if (!small.has_captures_len) return 0; + const extra = zir.extraData(Inst.UnionDecl, inst.data.extended.operand); + return zir.extra[extra.end + @intFromBool(small.has_tag_type)]; + }, + .enum_decl => { + const small: Inst.EnumDecl.Small = @bitCast(inst.data.extended.small); + if (!small.has_captures_len) return 0; + const extra = zir.extraData(Inst.EnumDecl, inst.data.extended.operand); + return zir.extra[extra.end + @intFromBool(small.has_tag_type)]; + }, + .opaque_decl => { + const small: Inst.OpaqueDecl.Small = @bitCast(inst.data.extended.small); + if (!small.has_captures_len) return 0; + const extra = zir.extraData(Inst.OpaqueDecl, inst.data.extended.operand); + return zir.extra[extra.end]; + }, + else => unreachable, + } +} diff --git a/src/InternPool.zig b/src/InternPool.zig index fdeddcfa2fb2..c0b4d2f4462a 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -2029,15 +2029,7 @@ pub const Key = union(enum) { pub const NamespaceType = union(enum) { /// This type corresponds to an actual source declaration, e.g. `struct { ... }`. /// It is hashed based on its ZIR instruction index and set of captures. - declared: struct { - /// A `struct_decl`, `union_decl`, `enum_decl`, or `opaque_decl` instruction. - zir_index: TrackedInst.Index, - /// The captured values of this type. These values must be fully resolved per the language spec. - captures: union(enum) { - owned: CaptureValue.Slice, - external: []const CaptureValue, - }, - }, + declared: Declared, /// This type is an automatically-generated enum tag type for a union. /// It is hashed based on the index of the union type it corresponds to. generated_tag: struct { @@ -2053,6 +2045,16 @@ pub const Key = union(enum) { /// A hash of this type's attributes, fields, etc, generated by Sema. type_hash: u64, }, + + pub const Declared = struct { + /// A `struct_decl`, `union_decl`, `enum_decl`, or `opaque_decl` instruction. + zir_index: TrackedInst.Index, + /// The captured values of this type. These values must be fully resolved per the language spec. + captures: union(enum) { + owned: CaptureValue.Slice, + external: []const CaptureValue, + }, + }; }; pub const FuncType = struct { diff --git a/src/Sema.zig b/src/Sema.zig index 0ec1a2493904..4a977664eff4 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -18172,7 +18172,9 @@ fn zirThis( _ = extended; const pt = sema.pt; const namespace = pt.zcu.namespacePtr(block.namespace); + const new_ty = try pt.ensureTypeUpToDate(namespace.owner_type); + switch (pt.zcu.intern_pool.indexToKey(new_ty)) { .struct_type, .union_type, .enum_type => try sema.declareDependency(.{ .interned = new_ty }), .opaque_type => {}, diff --git a/src/Zcu.zig b/src/Zcu.zig index fc6052a47c3c..31e1d8dbd23b 100644 --- a/src/Zcu.zig +++ b/src/Zcu.zig @@ -2563,19 +2563,6 @@ pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?AnalUnit { } } - if (chosen_unit == null) { - for (zcu.outdated.keys(), zcu.outdated.values()) |o, opod| { - const func = o.unwrap().func; - const nav = zcu.funcInfo(func).owner_nav; - std.io.getStdErr().writer().print("outdated: func {}, nav {}, name '{}', [p]o deps {}\n", .{ func, nav, ip.getNav(nav).fqn.fmt(ip), opod }) catch {}; - } - for (zcu.potentially_outdated.keys(), zcu.potentially_outdated.values()) |o, opod| { - const func = o.unwrap().func; - const nav = zcu.funcInfo(func).owner_nav; - std.io.getStdErr().writer().print("po: func {}, nav {}, name '{}', [p]o deps {}\n", .{ func, nav, ip.getNav(nav).fqn.fmt(ip), opod }) catch {}; - } - } - log.debug("findOutdatedToAnalyze: heuristic returned '{}' ({d} dependers)", .{ zcu.fmtAnalUnit(chosen_unit.?), chosen_unit_dependers, @@ -2661,6 +2648,14 @@ pub fn mapOldZirToNew( } while (match_stack.popOrNull()) |match_item| { + // First, a check: if the number of captures of this type has changed, we can't map it, because + // we wouldn't know how to correlate type information with the last update. + // Synchronizes with logic in `Zcu.PerThread.recreateStructType` etc. + if (old_zir.typeCapturesLen(match_item.old_inst) != new_zir.typeCapturesLen(match_item.new_inst)) { + // Don't map this type or anything within it. + continue; + } + // Match the namespace declaration itself try inst_map.put(gpa, match_item.old_inst, match_item.new_inst); diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index 4175223f14f4..393d3ed837c1 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -3524,9 +3524,11 @@ pub fn navAlignment(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) InternPo return ty.abiAlignment(zcu); } -/// Given a container type requiring resolution, ensures that it is up-to-date. -/// If not, the type is recreated at a new `InternPool.Index`. -/// The new index is returned. This is the same as the old index if the fields were up-to-date. +/// `ty` is a container type requiring resolution (struct, union, or enum). +/// If `ty` is outdated, it is recreated at a new `InternPool.Index`, which is returned. +/// If the type cannot be recreated because it has been lost, `error.AnalysisFail` is returned. +/// If `ty` is not outdated, that same `InternPool.Index` is returned. +/// If `ty` has already been replaced by this function, the new index will not be returned again. pub fn ensureTypeUpToDate(pt: Zcu.PerThread, ty: InternPool.Index) Zcu.SemaError!InternPool.Index { const zcu = pt.zcu; const gpa = zcu.gpa; @@ -3536,13 +3538,30 @@ pub fn ensureTypeUpToDate(pt: Zcu.PerThread, ty: InternPool.Index) Zcu.SemaError const outdated = zcu.outdated.swapRemove(anal_unit) or zcu.potentially_outdated.swapRemove(anal_unit); + if (outdated) { + _ = zcu.outdated_ready.swapRemove(anal_unit); + try zcu.markDependeeOutdated(.marked_po, .{ .interned = ty }); + } + + const ty_key = switch (ip.indexToKey(ty)) { + .struct_type, .union_type, .enum_type => |key| key, + else => unreachable, + }; + const declared_ty_key = switch (ty_key) { + .reified => unreachable, // never outdated + .generated_tag => unreachable, // never outdated + .declared => |d| d, + }; + + if (declared_ty_key.zir_index.resolve(ip) == null) { + // The instruction has been lost -- this type is dead. + return error.AnalysisFail; + } + if (!outdated) return ty; // We will recreate the type at a new `InternPool.Index`. - _ = zcu.outdated_ready.swapRemove(anal_unit); - try zcu.markDependeeOutdated(.marked_po, .{ .interned = ty }); - // Delete old state which is no longer in use. Technically, this is not necessary: these exports, // references, etc, will be ignored because the type itself is unreferenced. However, it allows // reusing the memory which is currently being used to track this state. @@ -3555,9 +3574,9 @@ pub fn ensureTypeUpToDate(pt: Zcu.PerThread, ty: InternPool.Index) Zcu.SemaError zcu.intern_pool.removeDependenciesForDepender(gpa, anal_unit); switch (ip.indexToKey(ty)) { - .struct_type => |key| return pt.recreateStructType(ty, key), - .union_type => |key| return pt.recreateUnionType(ty, key), - .enum_type => |key| return pt.recreateEnumType(ty, key), + .struct_type => return pt.recreateStructType(ty, declared_ty_key), + .union_type => return pt.recreateUnionType(ty, declared_ty_key), + .enum_type => return pt.recreateEnumType(ty, declared_ty_key), else => unreachable, } } @@ -3565,21 +3584,15 @@ pub fn ensureTypeUpToDate(pt: Zcu.PerThread, ty: InternPool.Index) Zcu.SemaError fn recreateStructType( pt: Zcu.PerThread, old_ty: InternPool.Index, - full_key: InternPool.Key.NamespaceType, -) Zcu.SemaError!InternPool.Index { + key: InternPool.Key.NamespaceType.Declared, +) Allocator.Error!InternPool.Index { const zcu = pt.zcu; const gpa = zcu.gpa; const ip = &zcu.intern_pool; - const key = switch (full_key) { - .reified => unreachable, // never outdated - .generated_tag => unreachable, // not a struct - .declared => |d| d, - }; - - const inst_info = key.zir_index.resolveFull(ip) orelse return error.AnalysisFail; + const inst_info = key.zir_index.resolveFull(ip).?; const file = zcu.fileByIndex(inst_info.file); - if (file.status != .success_zir) return error.AnalysisFail; + assert(file.status == .success_zir); // otherwise inst tracking failed const zir = file.zir; assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended); @@ -3600,7 +3613,7 @@ fn recreateStructType( break :blk fields_len; } else 0; - if (captures_len != key.captures.owned.len) return error.AnalysisFail; + assert(captures_len == key.captures.owned.len); // synchronises with logic in `Zcu.mapOldZirToNew` const struct_obj = ip.loadStructType(old_ty); @@ -3644,21 +3657,15 @@ fn recreateStructType( fn recreateUnionType( pt: Zcu.PerThread, old_ty: InternPool.Index, - full_key: InternPool.Key.NamespaceType, -) Zcu.SemaError!InternPool.Index { + key: InternPool.Key.NamespaceType.Declared, +) Allocator.Error!InternPool.Index { const zcu = pt.zcu; const gpa = zcu.gpa; const ip = &zcu.intern_pool; - const key = switch (full_key) { - .reified => unreachable, // never outdated - .generated_tag => unreachable, // not a union - .declared => |d| d, - }; - - const inst_info = key.zir_index.resolveFull(ip) orelse return error.AnalysisFail; + const inst_info = key.zir_index.resolveFull(ip).?; const file = zcu.fileByIndex(inst_info.file); - if (file.status != .success_zir) return error.AnalysisFail; + assert(file.status == .success_zir); // otherwise inst tracking failed const zir = file.zir; assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended); @@ -3681,7 +3688,7 @@ fn recreateUnionType( break :blk fields_len; } else 0; - if (captures_len != key.captures.owned.len) return error.AnalysisFail; + assert(captures_len == key.captures.owned.len); // synchronises with logic in `Zcu.mapOldZirToNew` const union_obj = ip.loadUnionType(old_ty); @@ -3731,23 +3738,19 @@ fn recreateUnionType( return wip_ty.finish(ip, namespace_index); } +// TODO: is it safe for this to return `SemaError`? enum type resolution is a bit weird... fn recreateEnumType( pt: Zcu.PerThread, old_ty: InternPool.Index, - full_key: InternPool.Key.NamespaceType, + key: InternPool.Key.NamespaceType.Declared, ) Zcu.SemaError!InternPool.Index { const zcu = pt.zcu; const gpa = zcu.gpa; const ip = &zcu.intern_pool; - const key = switch (full_key) { - .reified, .generated_tag => unreachable, // never outdated - .declared => |d| d, - }; - - const inst_info = key.zir_index.resolveFull(ip) orelse return error.AnalysisFail; + const inst_info = key.zir_index.resolveFull(ip).?; const file = zcu.fileByIndex(inst_info.file); - if (file.status != .success_zir) return error.AnalysisFail; + assert(file.status == .success_zir); // otherwise inst tracking failed const zir = file.zir; assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended); @@ -3787,7 +3790,7 @@ fn recreateEnumType( break :blk decls_len; } else 0; - if (captures_len != key.captures.owned.len) return error.AnalysisFail; + assert(captures_len == key.captures.owned.len); // synchronises with logic in `Zcu.mapOldZirToNew` extra_index += captures_len; extra_index += decls_len; From fd62912787ee4f26b06ba86560e9aa605095ae04 Mon Sep 17 00:00:00 2001 From: mlugg Date: Sat, 4 Jan 2025 00:06:24 +0000 Subject: [PATCH 3/5] incremental: correctly handle losing file root `struct_decl` inst --- src/Zcu/PerThread.zig | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index 393d3ed837c1..c382d78efa59 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -545,10 +545,19 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void { pub fn ensureFileAnalyzed(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError!void { const file_root_type = pt.zcu.fileRootType(file_index); if (file_root_type != .none) { - _ = try pt.ensureTypeUpToDate(file_root_type); - } else { - return pt.semaFile(file_index); + if (pt.ensureTypeUpToDate(file_root_type)) |_| { + return; + } else |err| switch (err) { + error.AnalysisFail => { + // The file's root `struct_decl` has, at some point, been lost, because the file failed AstGen. + // Clear `file_root_type`, and try the `semaFile` call below, in case the instruction has since + // been discovered under a new `TrackedInst.Index`. + pt.zcu.setFileRootType(file_index, .none); + }, + else => |e| return e, + } } + return pt.semaFile(file_index); } /// Ensures that the state of the given `ComptimeUnit` is fully up-to-date, performing re-analysis From f01029c4af7d3015c1c3f7d36bb62f7d5afb53a4 Mon Sep 17 00:00:00 2001 From: mlugg Date: Sat, 4 Jan 2025 05:09:02 +0000 Subject: [PATCH 4/5] incremental: new `AnalUnit` to group dependencies on `std.builtin` decls This commit reworks how values like the panic handler function are memoized during a compiler invocation. Previously, the value was resolved by whichever analysis requested it first, and cached on `Zcu`. This is problematic for incremental compilation, as after the initial resolution, no dependencies are marked by users of this memoized state. This is arguably acceptable for `std.builtin`, but it's definitely not acceptable for the panic handler/messages, because those can be set by the user (`std.builtin.Panic` checks `@import("root").Panic`). So, here we introduce a new kind of `AnalUnit`, called `memoized_state`. There are 3 such units: * `.{ .memoized_state = .va_list }` resolves the type `std.builtin.VaList` * `.{ .memoized_state = .panic }` resolves `std.Panic` * `.{ .memoized_state = .main }` resolves everything else we want These units essentially "bundle" the resolution of their corresponding declarations, storing the results into fields on `Zcu`. This way, when, for instance, a function wants to call the panic handler, it simply runs `ensureMemoizedStateResolved`, registering one dependency, and pulls the values from the `Zcu`. This "bundling" minimizes dependency edges. The 3 units are separated to allow them to act independently: for instance, the panic handler can use `std.builtin.Type` without triggering a dependency loop. --- src/Compilation.zig | 26 ++- src/InternPool.zig | 55 ++++++ src/Sema.zig | 423 +++++++++++++++++++++--------------------- src/Zcu.zig | 217 +++++++++++++++++++++- src/Zcu/PerThread.zig | 160 ++++++++++++++-- src/codegen/llvm.zig | 12 +- 6 files changed, 639 insertions(+), 254 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 929ed586f65b..4a32b3c45726 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -3158,16 +3158,19 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { if (!refs.contains(anal_unit)) continue; } - const file_index = switch (anal_unit.unwrap()) { - .@"comptime" => |cu| ip.getComptimeUnit(cu).zir_index.resolveFile(ip), - .nav_val, .nav_ty => |nav| ip.getNav(nav).analysis.?.zir_index.resolveFile(ip), - .type => |ty| Type.fromInterned(ty).typeDeclInst(zcu).?.resolveFile(ip), - .func => |ip_index| zcu.funcInfo(ip_index).zir_body_inst.resolveFile(ip), - }; + report_ok: { + const file_index = switch (anal_unit.unwrap()) { + .@"comptime" => |cu| ip.getComptimeUnit(cu).zir_index.resolveFile(ip), + .nav_val, .nav_ty => |nav| ip.getNav(nav).analysis.?.zir_index.resolveFile(ip), + .type => |ty| Type.fromInterned(ty).typeDeclInst(zcu).?.resolveFile(ip), + .func => |ip_index| zcu.funcInfo(ip_index).zir_body_inst.resolveFile(ip), + .memoized_state => break :report_ok, // always report std.builtin errors + }; - // Skip errors for AnalUnits within files that had a parse failure. - // We'll try again once parsing succeeds. - if (!zcu.fileByIndex(file_index).okToReportErrors()) continue; + // Skip errors for AnalUnits within files that had a parse failure. + // We'll try again once parsing succeeds. + if (!zcu.fileByIndex(file_index).okToReportErrors()) continue; + } std.log.scoped(.zcu).debug("analysis error '{s}' reported from unit '{}'", .{ error_msg.msg, @@ -3391,7 +3394,7 @@ pub fn addModuleErrorMsg( const ref = maybe_ref orelse break; const gop = try seen.getOrPut(gpa, ref.referencer); if (gop.found_existing) break; - if (ref_traces.items.len < max_references) { + if (ref_traces.items.len < max_references) skip: { const src = ref.src.upgrade(zcu); const source = try src.file_scope.getSource(gpa); const span = try src.span(gpa); @@ -3403,6 +3406,7 @@ pub fn addModuleErrorMsg( .nav_val, .nav_ty => |nav| ip.getNav(nav).name.toSlice(ip), .type => |ty| Type.fromInterned(ty).containerTypeName(ip).toSlice(ip), .func => |f| ip.getNav(zcu.funcInfo(f).owner_nav).name.toSlice(ip), + .memoized_state => break :skip, }; try ref_traces.append(gpa, .{ .decl_name = try eb.addString(name), @@ -3670,6 +3674,7 @@ fn performAllTheWorkInner( if (try zcu.findOutdatedToAnalyze()) |outdated| { try comp.queueJob(switch (outdated.unwrap()) { .func => |f| .{ .analyze_func = f }, + .memoized_state, .@"comptime", .nav_ty, .nav_val, @@ -3737,6 +3742,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre .nav_ty => |nav| pt.ensureNavTypeUpToDate(nav), .nav_val => |nav| pt.ensureNavValUpToDate(nav), .type => |ty| if (pt.ensureTypeUpToDate(ty)) |_| {} else |err| err, + .memoized_state => |stage| pt.ensureMemoizedStateUpToDate(stage), .func => unreachable, }; maybe_err catch |err| switch (err) { diff --git a/src/InternPool.zig b/src/InternPool.zig index c0b4d2f4462a..c34e21a4d345 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -49,6 +49,11 @@ namespace_deps: std.AutoArrayHashMapUnmanaged(TrackedInst.Index, DepEntry.Index) /// Dependencies on the (non-)existence of some name in a namespace. /// Value is index into `dep_entries` of the first dependency on this name. namespace_name_deps: std.AutoArrayHashMapUnmanaged(NamespaceNameKey, DepEntry.Index), +// Dependencies on the value of fields memoized on `Zcu` (`panic_messages` etc). +// If set, these are indices into `dep_entries` of the first dependency on this state. +memoized_state_main_deps: DepEntry.Index.Optional, +memoized_state_panic_deps: DepEntry.Index.Optional, +memoized_state_va_list_deps: DepEntry.Index.Optional, /// Given a `Depender`, points to an entry in `dep_entries` whose `depender` /// matches. The `next_dependee` field can be used to iterate all such entries @@ -87,6 +92,9 @@ pub const empty: InternPool = .{ .interned_deps = .empty, .namespace_deps = .empty, .namespace_name_deps = .empty, + .memoized_state_main_deps = .none, + .memoized_state_panic_deps = .none, + .memoized_state_va_list_deps = .none, .first_dependency = .empty, .dep_entries = .empty, .free_dep_entries = .empty, @@ -385,6 +393,7 @@ pub const AnalUnit = packed struct(u64) { nav_ty, type, func, + memoized_state, }; pub const Unwrapped = union(Kind) { @@ -399,6 +408,8 @@ pub const AnalUnit = packed struct(u64) { type: InternPool.Index, /// This `AnalUnit` analyzes the body of the given runtime function. func: InternPool.Index, + /// This `AnalUnit` resolves all state which is memoized in fields on `Zcu`. + memoized_state: MemoizedStateStage, }; pub fn unwrap(au: AnalUnit) Unwrapped { @@ -434,6 +445,16 @@ pub const AnalUnit = packed struct(u64) { }; }; +pub const MemoizedStateStage = enum(u32) { + /// Everything other than panics and `VaList`. + main, + /// Everything within `std.builtin.Panic`. + /// Since the panic handler is user-provided, this must be able to reference the other memoized state. + panic, + /// Specifically `std.builtin.VaList`. See `Zcu.BuiltinDecl.stage`. + va_list, +}; + pub const ComptimeUnit = extern struct { zir_index: TrackedInst.Index, namespace: NamespaceIndex, @@ -769,6 +790,7 @@ pub const Dependee = union(enum) { interned: Index, namespace: TrackedInst.Index, namespace_name: NamespaceNameKey, + memoized_state: MemoizedStateStage, }; pub fn removeDependenciesForDepender(ip: *InternPool, gpa: Allocator, depender: AnalUnit) void { @@ -819,6 +841,11 @@ pub fn dependencyIterator(ip: *const InternPool, dependee: Dependee) DependencyI .interned => |x| ip.interned_deps.get(x), .namespace => |x| ip.namespace_deps.get(x), .namespace_name => |x| ip.namespace_name_deps.get(x), + .memoized_state => |stage| switch (stage) { + .main => ip.memoized_state_main_deps.unwrap(), + .panic => ip.memoized_state_panic_deps.unwrap(), + .va_list => ip.memoized_state_va_list_deps.unwrap(), + }, } orelse return .{ .ip = ip, .next_entry = .none, @@ -848,6 +875,33 @@ pub fn addDependency(ip: *InternPool, gpa: Allocator, depender: AnalUnit, depend // This block should allocate an entry and prepend it to the relevant `*_deps` list. // The `next` field should be correctly initialized; all other fields may be undefined. const new_index: DepEntry.Index = switch (dependee) { + .memoized_state => |stage| new_index: { + const deps = switch (stage) { + .main => &ip.memoized_state_main_deps, + .panic => &ip.memoized_state_panic_deps, + .va_list => &ip.memoized_state_va_list_deps, + }; + + if (deps.unwrap()) |first| { + if (ip.dep_entries.items[@intFromEnum(first)].depender == .none) { + // Dummy entry, so we can reuse it rather than allocating a new one! + break :new_index first; + } + } + + // Prepend a new dependency. + const new_index: DepEntry.Index, const ptr = if (ip.free_dep_entries.popOrNull()) |new_index| new: { + break :new .{ new_index, &ip.dep_entries.items[@intFromEnum(new_index)] }; + } else .{ @enumFromInt(ip.dep_entries.items.len), ip.dep_entries.addOneAssumeCapacity() }; + if (deps.unwrap()) |old_first| { + ptr.next = old_first.toOptional(); + ip.dep_entries.items[@intFromEnum(old_first)].prev = new_index.toOptional(); + } else { + ptr.next = .none; + } + deps.* = new_index.toOptional(); + break :new_index new_index; + }, inline else => |dependee_payload, tag| new_index: { const gop = try switch (tag) { .file => ip.file_deps, @@ -857,6 +911,7 @@ pub fn addDependency(ip: *InternPool, gpa: Allocator, depender: AnalUnit, depend .interned => ip.interned_deps, .namespace => ip.namespace_deps, .namespace_name => ip.namespace_name_deps, + .memoized_state => comptime unreachable, }.getOrPut(gpa, dependee_payload); if (gop.found_existing and ip.dep_entries.items[@intFromEnum(gop.value_ptr.*)].depender == .none) { diff --git a/src/Sema.zig b/src/Sema.zig index 4a977664eff4..a937687761c9 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -428,7 +428,7 @@ pub const Block = struct { } }); } - fn nodeOffset(block: Block, node_offset: i32) LazySrcLoc { + pub fn nodeOffset(block: Block, node_offset: i32) LazySrcLoc { return block.src(LazySrcLoc.Offset.nodeOffset(node_offset)); } @@ -2149,7 +2149,7 @@ pub fn setupErrorReturnTrace(sema: *Sema, block: *Block, last_arg_index: usize) const addrs_ptr = try err_trace_block.addTy(.alloc, try pt.singleMutPtrType(addr_arr_ty)); // var st: StackTrace = undefined; - const stack_trace_ty = try sema.getBuiltinType("StackTrace"); + const stack_trace_ty = try sema.getBuiltinType(block.nodeOffset(0), .StackTrace); try stack_trace_ty.resolveFields(pt); const st_ptr = try err_trace_block.addTy(.alloc, try pt.singleMutPtrType(stack_trace_ty)); @@ -6600,6 +6600,7 @@ fn zirDisableInstrumentation(sema: *Sema) CompileError!void { .nav_val, .nav_ty, .type, + .memoized_state, => return, // does nothing outside a function }; ip.funcSetDisableInstrumentation(func); @@ -6609,7 +6610,7 @@ fn zirDisableInstrumentation(sema: *Sema) CompileError!void { fn zirSetFloatMode(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void { const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; const src = block.builtinCallArgSrc(extra.node, 0); - block.float_mode = try sema.resolveBuiltinEnum(block, src, extra.operand, "FloatMode", .{ .simple = .operand_setFloatMode }); + block.float_mode = try sema.resolveBuiltinEnum(block, src, extra.operand, .FloatMode, .{ .simple = .operand_setFloatMode }); } fn zirSetRuntimeSafety(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { @@ -6917,7 +6918,7 @@ fn lookupInNamespace( ignore_self: { const skip_nav = switch (sema.owner.unwrap()) { - .@"comptime", .type, .func => break :ignore_self, + .@"comptime", .type, .func, .memoized_state => break :ignore_self, .nav_ty, .nav_val => |nav| nav, }; var i: usize = 0; @@ -6990,7 +6991,7 @@ pub fn analyzeSaveErrRetIndex(sema: *Sema, block: *Block) SemaError!Air.Inst.Ref if (!block.ownerModule().error_tracing) return .none; - const stack_trace_ty = try sema.getBuiltinType("StackTrace"); + const stack_trace_ty = try sema.getBuiltinType(block.nodeOffset(0), .StackTrace); try stack_trace_ty.resolveFields(pt); const field_name = try zcu.intern_pool.getOrPutString(gpa, pt.tid, "index", .no_embedded_nulls); const field_index = sema.structFieldIndex(block, stack_trace_ty, field_name, LazySrcLoc.unneeded) catch |err| switch (err) { @@ -7032,7 +7033,7 @@ fn popErrorReturnTrace( // AstGen determined this result does not go to an error-handling expr (try/catch/return etc.), or // the result is comptime-known to be a non-error. Either way, pop unconditionally. - const stack_trace_ty = try sema.getBuiltinType("StackTrace"); + const stack_trace_ty = try sema.getBuiltinType(src, .StackTrace); try stack_trace_ty.resolveFields(pt); const ptr_stack_trace_ty = try pt.singleMutPtrType(stack_trace_ty); const err_return_trace = try block.addTy(.err_return_trace, ptr_stack_trace_ty); @@ -7058,7 +7059,7 @@ fn popErrorReturnTrace( defer then_block.instructions.deinit(gpa); // If non-error, then pop the error return trace by restoring the index. - const stack_trace_ty = try sema.getBuiltinType("StackTrace"); + const stack_trace_ty = try sema.getBuiltinType(src, .StackTrace); try stack_trace_ty.resolveFields(pt); const ptr_stack_trace_ty = try pt.singleMutPtrType(stack_trace_ty); const err_return_trace = try then_block.addTy(.err_return_trace, ptr_stack_trace_ty); @@ -7178,7 +7179,7 @@ fn zirCall( const call_inst = try sema.analyzeCall(block, func, func_ty, callee_src, call_src, modifier, ensure_result_used, args_info, call_dbg_node, .call); switch (sema.owner.unwrap()) { - .@"comptime", .type, .nav_ty, .nav_val => input_is_error = false, + .@"comptime", .type, .memoized_state, .nav_ty, .nav_val => input_is_error = false, .func => |owner_func| if (!zcu.intern_pool.funcAnalysisUnordered(owner_func).calls_or_awaits_errorable_fn) { // No errorable fn actually called; we have no error return trace input_is_error = false; @@ -7201,7 +7202,7 @@ fn zirCall( // If any input is an error-type, we might need to pop any trace it generated. Otherwise, we only // need to clean-up our own trace if we were passed to a non-error-handling expression. if (input_is_error or (pop_error_return_trace and return_ty.isError(zcu))) { - const stack_trace_ty = try sema.getBuiltinType("StackTrace"); + const stack_trace_ty = try sema.getBuiltinType(call_src, .StackTrace); try stack_trace_ty.resolveFields(pt); const field_name = try zcu.intern_pool.getOrPutString(sema.gpa, pt.tid, "index", .no_embedded_nulls); const field_index = try sema.structFieldIndex(block, stack_trace_ty, field_name, call_src); @@ -8091,7 +8092,7 @@ fn analyzeCall( if (call_dbg_node) |some| try sema.zirDbgStmt(block, some); switch (sema.owner.unwrap()) { - .@"comptime", .nav_ty, .nav_val, .type => {}, + .@"comptime", .nav_ty, .nav_val, .type, .memoized_state => {}, .func => |owner_func| if (Type.fromInterned(func_ty_info.return_type).isError(zcu)) { ip.funcSetCallsOrAwaitsErrorableFn(owner_func); }, @@ -8557,7 +8558,7 @@ fn instantiateGenericCall( if (call_dbg_node) |some| try sema.zirDbgStmt(block, some); switch (sema.owner.unwrap()) { - .@"comptime", .nav_ty, .nav_val, .type => {}, + .@"comptime", .nav_ty, .nav_val, .type, .memoized_state => {}, .func => |owner_func| if (Type.fromInterned(func_ty_info.return_type).isError(zcu)) { ip.funcSetCallsOrAwaitsErrorableFn(owner_func); }, @@ -9537,6 +9538,7 @@ fn zirFunc( const extra = sema.code.extraData(Zir.Inst.Func, inst_data.payload_index); const target = zcu.getTarget(); const ret_ty_src = block.src(.{ .node_offset_fn_type_ret_ty = inst_data.src_node }); + const src = block.nodeOffset(inst_data.src_node); var extra_index = extra.end; @@ -9588,7 +9590,7 @@ fn zirFunc( // error by trying to evaluate `std.builtin.CallingConvention.c`, so for consistency, // let's eval that now and just get the transitive error. (It's guaranteed to error // because it does the exact `cCallingConvention` call we just did.) - const cc_type = try sema.getBuiltinType("CallingConvention"); + const cc_type = try sema.getBuiltinType(src, .CallingConvention); _ = try sema.namespaceLookupVal( block, LazySrcLoc.unneeded, @@ -10302,7 +10304,7 @@ fn finishFunc( if (!final_is_generic and sema.wantErrorReturnTracing(return_type)) { // Make sure that StackTrace's fields are resolved so that the backend can // lower this fn type. - const unresolved_stack_trace_ty = try sema.getBuiltinType("StackTrace"); + const unresolved_stack_trace_ty = try sema.getBuiltinType(block.nodeOffset(0), .StackTrace); try unresolved_stack_trace_ty.resolveFields(pt); } @@ -14283,7 +14285,7 @@ fn maybeErrorUnwrap( const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; const msg_inst = try sema.resolveInst(inst_data.operand); - const panic_fn = try getPanicInnerFn(sema, block, operand_src, "call"); + const panic_fn = try getBuiltin(sema, operand_src, .@"Panic.call"); const err_return_trace = try sema.getErrorReturnTrace(block); const args: [3]Air.Inst.Ref = .{ msg_inst, err_return_trace, .null_value }; try sema.callBuiltin(block, operand_src, Air.internedToRef(panic_fn), .auto, &args, .@"safety check"); @@ -17477,7 +17479,7 @@ fn analyzeArithmetic( if (block.wantSafety() and want_safety and scalar_tag == .int) { if (zcu.backendSupportsFeature(.safety_checked_instructions)) { if (air_tag != air_tag_safe) { - _ = try sema.preparePanicId(block, src, .integer_overflow); + _ = try sema.preparePanicId(src, .integer_overflow); } return block.addBinOp(air_tag_safe, casted_lhs, casted_rhs); } else { @@ -18381,7 +18383,7 @@ fn zirBuiltinSrc( } }); }; - const src_loc_ty = try sema.getBuiltinType("SourceLocation"); + const src_loc_ty = try sema.getBuiltinType(block.nodeOffset(0), .SourceLocation); const fields = .{ // module: [:0]const u8, module_name_val, @@ -18408,7 +18410,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; const src = block.nodeOffset(inst_data.src_node); const ty = try sema.resolveType(block, src, inst_data.operand); - const type_info_ty = try sema.getBuiltinType("Type"); + const type_info_ty = try sema.getBuiltinType(src, .Type); const type_info_tag_ty = type_info_ty.unionTagType(zcu).?; if (ty.typeDeclInst(zcu)) |type_decl_inst| { @@ -18428,8 +18430,8 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai => |type_info_tag| return unionInitFromEnumTag(sema, block, src, type_info_ty, @intFromEnum(type_info_tag), .void_value), .@"fn" => { - const fn_info_ty = try getBuiltinInnerType(sema, block, src, type_info_ty, "Type", "Fn"); - const param_info_ty = try getBuiltinInnerType(sema, block, src, fn_info_ty, "Type.Fn", "Param"); + const fn_info_ty = try sema.getBuiltinType(src, .@"Type.Fn"); + const param_info_ty = try sema.getBuiltinType(src, .@"Type.Fn.Param"); const func_ty_info = zcu.typeToFunc(ty).?; const param_vals = try sema.arena.alloc(InternPool.Index, func_ty_info.param_types.len); @@ -18499,7 +18501,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai func_ty_info.return_type, } }); - const callconv_ty = try sema.getBuiltinType("CallingConvention"); + const callconv_ty = try sema.getBuiltinType(src, .CallingConvention); const callconv_val = Value.uninterpret(func_ty_info.cc, callconv_ty, pt) catch |err| switch (err) { error.TypeMismatch => @panic("std.builtin is corrupt"), error.OutOfMemory => |e| return e, @@ -18527,8 +18529,8 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai }))); }, .int => { - const int_info_ty = try getBuiltinInnerType(sema, block, src, type_info_ty, "Type", "Int"); - const signedness_ty = try sema.getBuiltinType("Signedness"); + const int_info_ty = try sema.getBuiltinType(src, .@"Type.Int"); + const signedness_ty = try sema.getBuiltinType(src, .Signedness); const info = ty.intInfo(zcu); const field_values = .{ // signedness: Signedness, @@ -18546,7 +18548,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai }))); }, .float => { - const float_info_ty = try getBuiltinInnerType(sema, block, src, type_info_ty, "Type", "Float"); + const float_info_ty = try sema.getBuiltinType(src, .@"Type.Float"); const field_vals = .{ // bits: u16, @@ -18568,9 +18570,9 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai else try Type.fromInterned(info.child).lazyAbiAlignment(pt); - const addrspace_ty = try sema.getBuiltinType("AddressSpace"); - const pointer_ty = try getBuiltinInnerType(sema, block, src, type_info_ty, "Type", "Pointer"); - const ptr_size_ty = try getBuiltinInnerType(sema, block, src, pointer_ty, "Type.Pointer", "Size"); + const addrspace_ty = try sema.getBuiltinType(src, .AddressSpace); + const pointer_ty = try sema.getBuiltinType(src, .@"Type.Pointer"); + const ptr_size_ty = try sema.getBuiltinType(src, .@"Type.Pointer.Size"); const field_values = .{ // size: Size, @@ -18603,7 +18605,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai }))); }, .array => { - const array_field_ty = try getBuiltinInnerType(sema, block, src, type_info_ty, "Type", "Array"); + const array_field_ty = try sema.getBuiltinType(src, .@"Type.Array"); const info = ty.arrayInfo(zcu); const field_values = .{ @@ -18624,7 +18626,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai }))); }, .vector => { - const vector_field_ty = try getBuiltinInnerType(sema, block, src, type_info_ty, "Type", "Vector"); + const vector_field_ty = try sema.getBuiltinType(src, .@"Type.Vector"); const info = ty.arrayInfo(zcu); const field_values = .{ @@ -18643,7 +18645,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai }))); }, .optional => { - const optional_field_ty = try getBuiltinInnerType(sema, block, src, type_info_ty, "Type", "Optional"); + const optional_field_ty = try sema.getBuiltinType(src, .@"Type.Optional"); const field_values = .{ // child: type, @@ -18660,7 +18662,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai }, .error_set => { // Get the Error type - const error_field_ty = try getBuiltinInnerType(sema, block, src, type_info_ty, "Type", "Error"); + const error_field_ty = try sema.getBuiltinType(src, .@"Type.Error"); // Build our list of Error values // Optional value is only null if anyerror @@ -18756,7 +18758,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai }))); }, .error_union => { - const error_union_field_ty = try getBuiltinInnerType(sema, block, src, type_info_ty, "Type", "ErrorUnion"); + const error_union_field_ty = try sema.getBuiltinType(src, .@"Type.ErrorUnion"); const field_values = .{ // error_set: type, @@ -18776,7 +18778,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai .@"enum" => { const is_exhaustive = Value.makeBool(ip.loadEnumType(ty.toIntern()).tag_mode != .nonexhaustive); - const enum_field_ty = try getBuiltinInnerType(sema, block, src, type_info_ty, "Type", "EnumField"); + const enum_field_ty = try sema.getBuiltinType(src, .@"Type.EnumField"); const enum_field_vals = try sema.arena.alloc(InternPool.Index, ip.loadEnumType(ty.toIntern()).names.len); for (enum_field_vals, 0..) |*field_val, tag_index| { @@ -18861,9 +18863,9 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai } }); }; - const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, ip.loadEnumType(ty.toIntern()).namespace.toOptional()); + const decls_val = try sema.typeInfoDecls(block, src, ip.loadEnumType(ty.toIntern()).namespace.toOptional()); - const type_enum_ty = try getBuiltinInnerType(sema, block, src, type_info_ty, "Type", "Enum"); + const type_enum_ty = try sema.getBuiltinType(src, .@"Type.Enum"); const field_values = .{ // tag_type: type, @@ -18885,8 +18887,8 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai }))); }, .@"union" => { - const type_union_ty = try getBuiltinInnerType(sema, block, src, type_info_ty, "Type", "Union"); - const union_field_ty = try getBuiltinInnerType(sema, block, src, type_info_ty, "Type", "UnionField"); + const type_union_ty = try sema.getBuiltinType(src, .@"Type.Union"); + const union_field_ty = try sema.getBuiltinType(src, .@"Type.UnionField"); try ty.resolveLayout(pt); // Getting alignment requires type layout const union_obj = zcu.typeToUnion(ty).?; @@ -18974,14 +18976,14 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai } }); }; - const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, ty.getNamespaceIndex(zcu).toOptional()); + const decls_val = try sema.typeInfoDecls(block, src, ty.getNamespaceIndex(zcu).toOptional()); const enum_tag_ty_val = try pt.intern(.{ .opt = .{ .ty = (try pt.optionalType(.type_type)).toIntern(), .val = if (ty.unionTagType(zcu)) |tag_ty| tag_ty.toIntern() else .none, } }); - const container_layout_ty = try getBuiltinInnerType(sema, block, src, type_info_ty, "Type", "ContainerLayout"); + const container_layout_ty = try sema.getBuiltinType(src, .@"Type.ContainerLayout"); const field_values = .{ // layout: ContainerLayout, @@ -19004,8 +19006,8 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai }))); }, .@"struct" => { - const type_struct_ty = try getBuiltinInnerType(sema, block, src, type_info_ty, "Type", "Struct"); - const struct_field_ty = try getBuiltinInnerType(sema, block, src, type_info_ty, "Type", "StructField"); + const type_struct_ty = try sema.getBuiltinType(src, .@"Type.Struct"); + const struct_field_ty = try sema.getBuiltinType(src, .@"Type.StructField"); try ty.resolveLayout(pt); // Getting alignment requires type layout @@ -19169,7 +19171,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai } }); }; - const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, ty.getNamespace(zcu)); + const decls_val = try sema.typeInfoDecls(block, src, ty.getNamespace(zcu)); const backing_integer_val = try pt.intern(.{ .opt = .{ .ty = (try pt.optionalType(.type_type)).toIntern(), @@ -19179,7 +19181,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai } else .none, } }); - const container_layout_ty = try getBuiltinInnerType(sema, block, src, type_info_ty, "Type", "ContainerLayout"); + const container_layout_ty = try sema.getBuiltinType(src, .@"Type.ContainerLayout"); const layout = ty.containerLayout(zcu); @@ -19205,10 +19207,10 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai }))); }, .@"opaque" => { - const type_opaque_ty = try getBuiltinInnerType(sema, block, src, type_info_ty, "Type", "Opaque"); + const type_opaque_ty = try sema.getBuiltinType(src, .@"Type.Opaque"); try ty.resolveFields(pt); - const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, ty.getNamespace(zcu)); + const decls_val = try sema.typeInfoDecls(block, src, ty.getNamespace(zcu)); const field_values = .{ // decls: []const Declaration, @@ -19232,14 +19234,13 @@ fn typeInfoDecls( sema: *Sema, block: *Block, src: LazySrcLoc, - type_info_ty: Type, opt_namespace: InternPool.OptionalNamespaceIndex, ) CompileError!InternPool.Index { const pt = sema.pt; const zcu = pt.zcu; const gpa = sema.gpa; - const declaration_ty = try getBuiltinInnerType(sema, block, src, type_info_ty, "Type", "Declaration"); + const declaration_ty = try sema.getBuiltinType(src, .@"Type.Declaration"); var decl_vals = std.ArrayList(InternPool.Index).init(gpa); defer decl_vals.deinit(); @@ -20181,11 +20182,11 @@ fn retWithErrTracing( else => true, }; const gpa = sema.gpa; - const stack_trace_ty = try sema.getBuiltinType("StackTrace"); + const stack_trace_ty = try sema.getBuiltinType(src, .StackTrace); try stack_trace_ty.resolveFields(pt); const ptr_stack_trace_ty = try pt.singleMutPtrType(stack_trace_ty); const err_return_trace = try block.addTy(.err_return_trace, ptr_stack_trace_ty); - const return_err_fn = try sema.getBuiltin("returnError"); + const return_err_fn = Air.internedToRef(try sema.getBuiltin(src, .returnError)); const args: [1]Air.Inst.Ref = .{err_return_trace}; if (!need_check) { @@ -21607,7 +21608,7 @@ fn getErrorReturnTrace(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref { const pt = sema.pt; const zcu = pt.zcu; const ip = &zcu.intern_pool; - const stack_trace_ty = try sema.getBuiltinType("StackTrace"); + const stack_trace_ty = try sema.getBuiltinType(block.nodeOffset(0), .StackTrace); try stack_trace_ty.resolveFields(pt); const ptr_stack_trace_ty = try pt.singleMutPtrType(stack_trace_ty); const opt_ptr_stack_trace_ty = try pt.optionalType(ptr_stack_trace_ty.toIntern()); @@ -21616,7 +21617,7 @@ fn getErrorReturnTrace(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref { .func => |func| if (ip.funcAnalysisUnordered(func).calls_or_awaits_errorable_fn and block.ownerModule().error_tracing) { return block.addTy(.err_return_trace, opt_ptr_stack_trace_ty); }, - .@"comptime", .nav_ty, .nav_val, .type => {}, + .@"comptime", .nav_ty, .nav_val, .type, .memoized_state => {}, } return Air.internedToRef(try pt.intern(.{ .opt = .{ .ty = opt_ptr_stack_trace_ty.toIntern(), @@ -21896,7 +21897,7 @@ fn zirReify( }, }, }; - const type_info_ty = try sema.getBuiltinType("Type"); + const type_info_ty = try sema.getBuiltinType(src, .Type); const uncasted_operand = try sema.resolveInst(extra.operand); const type_info = try sema.coerce(block, type_info_ty, uncasted_operand, operand_src); const val = try sema.resolveConstDefinedValue(block, operand_src, type_info, .{ .simple = .operand_Type }); @@ -23156,7 +23157,7 @@ fn reifyStruct( fn resolveVaListRef(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) CompileError!Air.Inst.Ref { const pt = sema.pt; - const va_list_ty = try sema.getBuiltinType("VaList"); + const va_list_ty = try sema.getBuiltinType(src, .VaList); const va_list_ptr = try pt.singleMutPtrType(va_list_ty); const inst = try sema.resolveInst(zir_ref); @@ -23195,7 +23196,7 @@ fn zirCVaCopy(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) const va_list_src = block.builtinCallArgSrc(extra.node, 0); const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.operand); - const va_list_ty = try sema.getBuiltinType("VaList"); + const va_list_ty = try sema.getBuiltinType(src, .VaList); try sema.requireRuntimeBlock(block, src, null); return block.addTyOp(.c_va_copy, va_list_ty, va_list_ref); @@ -23215,7 +23216,7 @@ fn zirCVaEnd(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) C fn zirCVaStart(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { const src = block.nodeOffset(@bitCast(extended.operand)); - const va_list_ty = try sema.getBuiltinType("VaList"); + const va_list_ty = try sema.getBuiltinType(src, .VaList); try sema.requireRuntimeBlock(block, src, null); return block.addInst(.{ .tag = .c_va_start, @@ -24821,7 +24822,7 @@ fn resolveExportOptions( const zcu = pt.zcu; const gpa = sema.gpa; const ip = &zcu.intern_pool; - const export_options_ty = try sema.getBuiltinType("ExportOptions"); + const export_options_ty = try sema.getBuiltinType(src, .ExportOptions); const air_ref = try sema.resolveInst(zir_ref); const options = try sema.coerce(block, export_options_ty, air_ref, src); @@ -24871,15 +24872,15 @@ fn resolveBuiltinEnum( block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref, - comptime name: []const u8, + comptime name: Zcu.BuiltinDecl, reason: ComptimeReason, -) CompileError!@field(std.builtin, name) { +) CompileError!@field(std.builtin, @tagName(name)) { const pt = sema.pt; - const ty = try sema.getBuiltinType(name); + const ty = try sema.getBuiltinType(src, name); const air_ref = try sema.resolveInst(zir_ref); const coerced = try sema.coerce(block, ty, air_ref, src); const val = try sema.resolveConstDefinedValue(block, src, coerced, reason); - return pt.zcu.toEnum(@field(std.builtin, name), val); + return pt.zcu.toEnum(@field(std.builtin, @tagName(name)), val); } fn resolveAtomicOrder( @@ -24889,7 +24890,7 @@ fn resolveAtomicOrder( zir_ref: Zir.Inst.Ref, reason: ComptimeReason, ) CompileError!std.builtin.AtomicOrder { - return sema.resolveBuiltinEnum(block, src, zir_ref, "AtomicOrder", reason); + return sema.resolveBuiltinEnum(block, src, zir_ref, .AtomicOrder, reason); } fn resolveAtomicRmwOp( @@ -24898,7 +24899,7 @@ fn resolveAtomicRmwOp( src: LazySrcLoc, zir_ref: Zir.Inst.Ref, ) CompileError!std.builtin.AtomicRmwOp { - return sema.resolveBuiltinEnum(block, src, zir_ref, "AtomicRmwOp", .{ .simple = .operand_atomicRmw_operation }); + return sema.resolveBuiltinEnum(block, src, zir_ref, .AtomicRmwOp, .{ .simple = .operand_atomicRmw_operation }); } fn zirCmpxchg( @@ -25078,7 +25079,7 @@ fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; const op_src = block.builtinCallArgSrc(inst_data.src_node, 0); const operand_src = block.builtinCallArgSrc(inst_data.src_node, 1); - const operation = try sema.resolveBuiltinEnum(block, op_src, extra.lhs, "ReduceOp", .{ .simple = .operand_reduce_operation }); + const operation = try sema.resolveBuiltinEnum(block, op_src, extra.lhs, .ReduceOp, .{ .simple = .operand_reduce_operation }); const operand = try sema.resolveInst(extra.rhs); const operand_ty = sema.typeOf(operand); const pt = sema.pt; @@ -25668,7 +25669,7 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const extra = sema.code.extraData(Zir.Inst.BuiltinCall, inst_data.payload_index).data; const func = try sema.resolveInst(extra.callee); - const modifier_ty = try sema.getBuiltinType("CallModifier"); + const modifier_ty = try sema.getBuiltinType(call_src, .CallModifier); const air_ref = try sema.resolveInst(extra.modifier); const modifier_ref = try sema.coerce(block, modifier_ty, air_ref, modifier_src); const modifier_val = try sema.resolveConstDefinedValue(block, modifier_src, modifier_ref, .{ .simple = .call_modifier }); @@ -26630,13 +26631,13 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const body = sema.code.bodySlice(extra_index, body_len); extra_index += body.len; - const cc_ty = try sema.getBuiltinType("CallingConvention"); + const cc_ty = try sema.getBuiltinType(cc_src, .CallingConvention); const val = try sema.resolveGenericBody(block, cc_src, body, inst, cc_ty, .{ .simple = .@"callconv" }); break :blk try sema.analyzeValueAsCallconv(block, cc_src, val); } else if (extra.data.bits.has_cc_ref) blk: { const cc_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); extra_index += 1; - const cc_ty = try sema.getBuiltinType("CallingConvention"); + const cc_ty = try sema.getBuiltinType(cc_src, .CallingConvention); const uncoerced_cc = try sema.resolveInst(cc_ref); const coerced_cc = try sema.coerce(block, cc_ty, uncoerced_cc, cc_src); const cc_val = try sema.resolveConstDefinedValue(block, cc_src, coerced_cc, .{ .simple = .@"callconv" }); @@ -26656,7 +26657,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A // error by trying to evaluate `std.builtin.CallingConvention.c`, so for consistency, // let's eval that now and just get the transitive error. (It's guaranteed to error // because it does the exact `cCallingConvention` call we just did.) - const cc_type = try sema.getBuiltinType("CallingConvention"); + const cc_type = try sema.getBuiltinType(cc_src, .CallingConvention); _ = try sema.namespaceLookupVal( block, LazySrcLoc.unneeded, @@ -26834,7 +26835,7 @@ fn resolvePrefetchOptions( const zcu = pt.zcu; const gpa = sema.gpa; const ip = &zcu.intern_pool; - const options_ty = try sema.getBuiltinType("PrefetchOptions"); + const options_ty = try sema.getBuiltinType(src, .PrefetchOptions); const options = try sema.coerce(block, options_ty, try sema.resolveInst(zir_ref), src); const rw_src = block.src(.{ .init_field_rw = src.offset.node_offset_builtin_call_arg.builtin_call_node }); @@ -26902,7 +26903,7 @@ fn resolveExternOptions( const gpa = sema.gpa; const ip = &zcu.intern_pool; const options_inst = try sema.resolveInst(zir_ref); - const extern_options_ty = try sema.getBuiltinType("ExternOptions"); + const extern_options_ty = try sema.getBuiltinType(src, .ExternOptions); const options = try sema.coerce(block, extern_options_ty, options_inst, src); const name_src = block.src(.{ .init_field_name = src.offset.node_offset_builtin_call_arg.builtin_call_node }); @@ -27004,6 +27005,7 @@ fn zirBuiltinExtern( .zir_index = switch (sema.owner.unwrap()) { .@"comptime" => |cu| ip.getComptimeUnit(cu).zir_index, .type => |owner_ty| Type.fromInterned(owner_ty).typeDeclInst(zcu).?, + .memoized_state => unreachable, .nav_ty, .nav_val => |nav| ip.getNav(nav).analysis.?.zir_index, .func => |func| zir_index: { const func_info = zcu.funcInfo(func); @@ -27081,23 +27083,25 @@ fn zirBuiltinValue(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstD const src = block.nodeOffset(@bitCast(extended.operand)); const value: Zir.Inst.BuiltinValue = @enumFromInt(extended.small); - const type_name = switch (value) { - .atomic_order => "AtomicOrder", - .atomic_rmw_op => "AtomicRmwOp", - .calling_convention => "CallingConvention", - .address_space => "AddressSpace", - .float_mode => "FloatMode", - .reduce_op => "ReduceOp", - .call_modifier => "CallModifier", - .prefetch_options => "PrefetchOptions", - .export_options => "ExportOptions", - .extern_options => "ExternOptions", - .type_info => "Type", - .branch_hint => "BranchHint", + const ty = switch (value) { + // zig fmt: off + .atomic_order => try sema.getBuiltinType(src, .AtomicOrder), + .atomic_rmw_op => try sema.getBuiltinType(src, .AtomicRmwOp), + .calling_convention => try sema.getBuiltinType(src, .CallingConvention), + .address_space => try sema.getBuiltinType(src, .AddressSpace), + .float_mode => try sema.getBuiltinType(src, .FloatMode), + .reduce_op => try sema.getBuiltinType(src, .ReduceOp), + .call_modifier => try sema.getBuiltinType(src, .CallModifier), + .prefetch_options => try sema.getBuiltinType(src, .PrefetchOptions), + .export_options => try sema.getBuiltinType(src, .ExportOptions), + .extern_options => try sema.getBuiltinType(src, .ExternOptions), + .type_info => try sema.getBuiltinType(src, .Type), + .branch_hint => try sema.getBuiltinType(src, .BranchHint), + // zig fmt: on // Values are handled here. .calling_convention_c => { - const callconv_ty = try sema.getBuiltinType("CallingConvention"); + const callconv_ty = try sema.getBuiltinType(src, .CallingConvention); return try sema.namespaceLookupVal( block, src, @@ -27107,7 +27111,7 @@ fn zirBuiltinValue(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstD }, .calling_convention_inline => { comptime assert(@typeInfo(std.builtin.CallingConvention.Tag).@"enum".tag_type == u8); - const callconv_ty = try sema.getBuiltinType("CallingConvention"); + const callconv_ty = try sema.getBuiltinType(src, .CallingConvention); const callconv_tag_ty = callconv_ty.unionTagType(zcu) orelse @panic("std.builtin is corrupt"); const inline_tag_val = try pt.enumValue( callconv_tag_ty, @@ -27119,7 +27123,6 @@ fn zirBuiltinValue(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstD return sema.coerce(block, callconv_ty, Air.internedToRef(inline_tag_val.toIntern()), src); }, }; - const ty = try sema.getBuiltinType(type_name); return Air.internedToRef(ty.toIntern()); } @@ -27158,7 +27161,7 @@ fn zirBranchHint(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat const uncoerced_hint = try sema.resolveInst(extra.operand); const operand_src = block.builtinCallArgSrc(extra.node, 0); - const hint_ty = try sema.getBuiltinType("BranchHint"); + const hint_ty = try sema.getBuiltinType(operand_src, .BranchHint); const coerced_hint = try sema.coerce(block, hint_ty, uncoerced_hint, operand_src); const hint_val = try sema.resolveConstDefinedValue(block, operand_src, coerced_hint, .{ .simple = .operand_branchHint }); @@ -27603,61 +27606,19 @@ fn explainWhyTypeIsNotPacked( } } -fn prepareSimplePanic(sema: *Sema, block: *Block, src: LazySrcLoc) !void { - const pt = sema.pt; - const zcu = pt.zcu; - - if (zcu.panic_func_index == .none) { - zcu.panic_func_index = try sema.getPanicInnerFn(block, src, "call"); - // Here, function body analysis must be queued up so that backends can - // make calls to this function. - try zcu.ensureFuncBodyAnalysisQueued(zcu.panic_func_index); - } - - if (zcu.null_stack_trace == .none) { - const stack_trace_ty = try sema.getBuiltinType("StackTrace"); - try stack_trace_ty.resolveFields(pt); - const target = zcu.getTarget(); - const ptr_stack_trace_ty = try pt.ptrTypeSema(.{ - .child = stack_trace_ty.toIntern(), - .flags = .{ - .address_space = target_util.defaultAddressSpace(target, .global_constant), - }, - }); - const opt_ptr_stack_trace_ty = try pt.optionalType(ptr_stack_trace_ty.toIntern()); - zcu.null_stack_trace = try pt.intern(.{ .opt = .{ - .ty = opt_ptr_stack_trace_ty.toIntern(), - .val = .none, - } }); - } -} - /// Backends depend on panic decls being available when lowering safety-checked /// instructions. This function ensures the panic function will be available to /// be called during that time. -fn preparePanicId(sema: *Sema, block: *Block, src: LazySrcLoc, panic_id: Zcu.PanicId) !InternPool.Nav.Index { - const pt = sema.pt; - const zcu = pt.zcu; - const gpa = sema.gpa; - if (zcu.panic_messages[@intFromEnum(panic_id)].unwrap()) |x| return x; - - try sema.prepareSimplePanic(block, src); - - const panic_ty = try sema.getBuiltinType("Panic"); - const panic_messages_ty = try sema.getBuiltinInnerType(block, src, panic_ty, "Panic", "messages"); - const msg_nav_index = (sema.namespaceLookup( - block, - LazySrcLoc.unneeded, - panic_messages_ty.getNamespaceIndex(zcu), - try zcu.intern_pool.getOrPutString(gpa, pt.tid, @tagName(panic_id), .no_embedded_nulls), - ) catch |err| switch (err) { - error.AnalysisFail => return error.AnalysisFail, - error.GenericPoison, error.ComptimeReturn, error.ComptimeBreak => unreachable, - error.OutOfMemory => |e| return e, - }).?; - try sema.ensureNavResolved(src, msg_nav_index, .fully); - zcu.panic_messages[@intFromEnum(panic_id)] = msg_nav_index.toOptional(); - return msg_nav_index; +fn preparePanicId(sema: *Sema, src: LazySrcLoc, panic_id: Zcu.PanicId) !InternPool.Index { + const zcu = sema.pt.zcu; + try sema.ensureMemoizedStateResolved(src, .panic); + try zcu.ensureFuncBodyAnalysisQueued(zcu.builtin_decl_values.@"Panic.call"); + switch (panic_id) { + inline else => |ct_panic_id| { + const name = "Panic.messages." ++ @tagName(ct_panic_id); + return @field(zcu.builtin_decl_values, name); + }, + } } fn addSafetyCheck( @@ -27761,10 +27722,10 @@ fn panicWithMsg(sema: *Sema, block: *Block, src: LazySrcLoc, msg_inst: Air.Inst. return; } - try sema.prepareSimplePanic(block, src); + try sema.ensureMemoizedStateResolved(src, .panic); + try zcu.ensureFuncBodyAnalysisQueued(zcu.builtin_decl_values.@"Panic.call"); - const panic_func = zcu.funcInfo(zcu.panic_func_index); - const panic_fn = try sema.analyzeNavVal(block, src, panic_func.owner_nav); + const panic_fn = Air.internedToRef(zcu.builtin_decl_values.@"Panic.call"); const null_stack_trace = Air.internedToRef(zcu.null_stack_trace); const opt_usize_ty = try pt.optionalType(.usize_type); @@ -27812,7 +27773,7 @@ fn safetyPanicUnwrapError(sema: *Sema, block: *Block, src: LazySrcLoc, err: Air. if (!zcu.backendSupportsFeature(.panic_fn)) { _ = try block.addNoOp(.trap); } else { - const panic_fn = try getPanicInnerFn(sema, block, src, "unwrapError"); + const panic_fn = try getBuiltin(sema, src, .@"Panic.unwrapError"); const err_return_trace = try sema.getErrorReturnTrace(block); const args: [2]Air.Inst.Ref = .{ err_return_trace, err }; try sema.callBuiltin(block, src, Air.internedToRef(panic_fn), .auto, &args, .@"safety check"); @@ -27829,7 +27790,7 @@ fn addSafetyCheckIndexOob( ) !void { assert(!parent_block.isComptime()); const ok = try parent_block.addBinOp(cmp_op, index, len); - return addSafetyCheckCall(sema, parent_block, src, ok, "outOfBounds", &.{ index, len }); + return addSafetyCheckCall(sema, parent_block, src, ok, .@"Panic.outOfBounds", &.{ index, len }); } fn addSafetyCheckInactiveUnionField( @@ -27841,7 +27802,7 @@ fn addSafetyCheckInactiveUnionField( ) !void { assert(!parent_block.isComptime()); const ok = try parent_block.addBinOp(.cmp_eq, active_tag, wanted_tag); - return addSafetyCheckCall(sema, parent_block, src, ok, "inactiveUnionField", &.{ active_tag, wanted_tag }); + return addSafetyCheckCall(sema, parent_block, src, ok, .@"Panic.inactiveUnionField", &.{ active_tag, wanted_tag }); } fn addSafetyCheckSentinelMismatch( @@ -27882,7 +27843,7 @@ fn addSafetyCheckSentinelMismatch( break :ok try parent_block.addBinOp(.cmp_eq, expected_sentinel, actual_sentinel); }; - return addSafetyCheckCall(sema, parent_block, src, ok, "sentinelMismatch", &.{ + return addSafetyCheckCall(sema, parent_block, src, ok, .@"Panic.sentinelMismatch", &.{ expected_sentinel, actual_sentinel, }); } @@ -27892,7 +27853,7 @@ fn addSafetyCheckCall( parent_block: *Block, src: LazySrcLoc, ok: Air.Inst.Ref, - func_name: []const u8, + comptime func_decl: Zcu.BuiltinDecl, args: []const Air.Inst.Ref, ) !void { assert(!parent_block.isComptime()); @@ -27916,7 +27877,7 @@ fn addSafetyCheckCall( if (!zcu.backendSupportsFeature(.panic_fn)) { _ = try fail_block.addNoOp(.trap); } else { - const panic_fn = try getPanicInnerFn(sema, &fail_block, src, func_name); + const panic_fn = try getBuiltin(sema, src, func_decl); try sema.callBuiltin(&fail_block, src, Air.internedToRef(panic_fn), .auto, args, .@"safety check"); } @@ -27925,9 +27886,8 @@ fn addSafetyCheckCall( /// This does not set `sema.branch_hint`. fn safetyPanic(sema: *Sema, block: *Block, src: LazySrcLoc, panic_id: Zcu.PanicId) CompileError!void { - const msg_nav_index = try sema.preparePanicId(block, src, panic_id); - const msg_inst = try sema.analyzeNavVal(block, src, msg_nav_index); - try sema.panicWithMsg(block, src, msg_inst, .@"safety check"); + const msg_val = try sema.preparePanicId(src, panic_id); + try sema.panicWithMsg(block, src, Air.internedToRef(msg_val), .@"safety check"); } fn emitBackwardBranch(sema: *Sema, block: *Block, src: LazySrcLoc) !void { @@ -32524,6 +32484,19 @@ fn addTypeReferenceEntry( try zcu.addTypeReference(sema.owner, referenced_type, src); } +fn ensureMemoizedStateResolved(sema: *Sema, src: LazySrcLoc, stage: InternPool.MemoizedStateStage) SemaError!void { + const pt = sema.pt; + + const unit: AnalUnit = .wrap(.{ .memoized_state = stage }); + try sema.addReferenceEntry(src, unit); + try sema.declareDependency(.{ .memoized_state = stage }); + + if (pt.zcu.analysis_in_progress.contains(unit)) { + return sema.failWithOwnedErrorMsg(null, try sema.errMsg(src, "dependency loop detected", .{})); + } + try pt.ensureMemoizedStateUpToDate(stage); +} + pub fn ensureNavResolved(sema: *Sema, src: LazySrcLoc, nav_index: InternPool.Nav.Index, kind: enum { type, fully }) CompileError!void { const pt = sema.pt; const zcu = pt.zcu; @@ -33373,7 +33346,7 @@ fn analyzeSlice( assert(!block.isComptime()); try sema.requireRuntimeBlock(block, src, runtime_src.?); const ok = try block.addBinOp(.cmp_lte, start, end); - try sema.addSafetyCheckCall(block, src, ok, "startGreaterThanEnd", &.{ start, end }); + try sema.addSafetyCheckCall(block, src, ok, .@"Panic.startGreaterThanEnd", &.{ start, end }); } const new_len = if (by_length) try sema.coerce(block, Type.usize, uncasted_end_opt, end_src) @@ -35493,7 +35466,7 @@ pub fn resolveIes(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError!void } } -pub fn resolveFnTypes(sema: *Sema, fn_ty: Type) CompileError!void { +pub fn resolveFnTypes(sema: *Sema, fn_ty: Type, src: LazySrcLoc) CompileError!void { const pt = sema.pt; const zcu = pt.zcu; const ip = &zcu.intern_pool; @@ -35505,7 +35478,7 @@ pub fn resolveFnTypes(sema: *Sema, fn_ty: Type) CompileError!void { Type.fromInterned(fn_ty_info.return_type).isError(zcu)) { // Ensure the type exists so that backends can assume that. - _ = try sema.getBuiltinType("StackTrace"); + _ = try sema.getBuiltinType(src, .StackTrace); } for (0..fn_ty_info.param_types.len) |i| { @@ -37550,7 +37523,7 @@ pub fn analyzeAsAddressSpace( ) !std.builtin.AddressSpace { const pt = sema.pt; const zcu = pt.zcu; - const addrspace_ty = try sema.getBuiltinType("AddressSpace"); + const addrspace_ty = try sema.getBuiltinType(src, .AddressSpace); const coerced = try sema.coerce(block, addrspace_ty, air_ref, src); const addrspace_val = try sema.resolveConstDefinedValue(block, src, coerced, .{ .simple = .@"addrspace" }); const address_space = zcu.toEnum(std.builtin.AddressSpace, addrspace_val); @@ -38747,69 +38720,15 @@ const ComptimeLoadResult = @import("Sema/comptime_ptr_access.zig").ComptimeLoadR const storeComptimePtr = @import("Sema/comptime_ptr_access.zig").storeComptimePtr; const ComptimeStoreResult = @import("Sema/comptime_ptr_access.zig").ComptimeStoreResult; -fn getPanicInnerFn( - sema: *Sema, - block: *Block, - src: LazySrcLoc, - inner_name: []const u8, -) !InternPool.Index { - const gpa = sema.gpa; - const pt = sema.pt; - const zcu = pt.zcu; - const ip = &zcu.intern_pool; - const outer_ty = try sema.getBuiltinType("Panic"); - const inner_name_ip = try ip.getOrPutString(gpa, pt.tid, inner_name, .no_embedded_nulls); - const opt_fn_ref = try namespaceLookupVal(sema, block, src, outer_ty.getNamespaceIndex(zcu), inner_name_ip); - const fn_ref = opt_fn_ref orelse return sema.fail(block, src, "std.builtin.Panic missing {s}", .{inner_name}); - const fn_val = try sema.resolveConstValue(block, src, fn_ref, .{ .simple = .panic_handler }); - if (fn_val.typeOf(zcu).zigTypeTag(zcu) != .@"fn") { - return sema.fail(block, src, "std.builtin.Panic.{s} is not a function", .{inner_name}); - } - // Better not to queue up function body analysis because the function might be generic, and - // the semantic analysis for the call will already queue if necessary. - return fn_val.toIntern(); -} - -fn getBuiltinType(sema: *Sema, name: []const u8) SemaError!Type { - const pt = sema.pt; - const ty_inst = try sema.getBuiltin(name); - const ty = Type.fromInterned(ty_inst.toInterned() orelse @panic("std.builtin is corrupt")); - try ty.resolveFully(pt); - return ty; -} - -fn getBuiltinInnerType( - sema: *Sema, - block: *Block, - src: LazySrcLoc, - outer_ty: Type, - /// Relative to "std.builtin". - compile_error_parent_name: []const u8, - inner_name: []const u8, -) !Type { - const pt = sema.pt; - const zcu = pt.zcu; - const ip = &zcu.intern_pool; - const gpa = sema.gpa; - const inner_name_ip = try ip.getOrPutString(gpa, pt.tid, inner_name, .no_embedded_nulls); - const opt_nav = try sema.namespaceLookup(block, src, outer_ty.getNamespaceIndex(zcu), inner_name_ip); - const nav = opt_nav orelse return sema.fail(block, src, "std.builtin.{s} missing {s}", .{ - compile_error_parent_name, inner_name, - }); - try sema.ensureNavResolved(src, nav, .fully); - const val = Value.fromInterned(ip.getNav(nav).status.fully_resolved.val); - const ty = val.toType(); - try ty.resolveFully(pt); - return ty; +pub fn getBuiltinType(sema: *Sema, src: LazySrcLoc, comptime decl: Zcu.BuiltinDecl) SemaError!Type { + comptime assert(decl.kind() == .type); + try sema.ensureMemoizedStateResolved(src, decl.stage()); + return .fromInterned(@field(sema.pt.zcu.builtin_decl_values, @tagName(decl))); } - -fn getBuiltin(sema: *Sema, name: []const u8) SemaError!Air.Inst.Ref { - const pt = sema.pt; - const zcu = pt.zcu; - const ip = &zcu.intern_pool; - const nav = try pt.getBuiltinNav(name); - try pt.ensureNavValUpToDate(nav); - return Air.internedToRef(ip.getNav(nav).status.fully_resolved.val); +pub fn getBuiltin(sema: *Sema, src: LazySrcLoc, comptime decl: Zcu.BuiltinDecl) SemaError!InternPool.Index { + comptime assert(decl.kind() != .type); + try sema.ensureMemoizedStateResolved(src, decl.stage()); + return @field(sema.pt.zcu.builtin_decl_values, @tagName(decl)); } pub const NavPtrModifiers = struct { @@ -38877,3 +38796,77 @@ pub fn resolveNavPtrModifiers( .@"addrspace" = @"addrspace", }; } + +pub fn analyzeMemoizedState(sema: *Sema, block: *Block, src: LazySrcLoc, builtin_namespace: InternPool.NamespaceIndex, stage: InternPool.MemoizedStateStage) CompileError!bool { + const pt = sema.pt; + const zcu = pt.zcu; + const ip = &zcu.intern_pool; + const gpa = zcu.gpa; + + var any_changed = false; + + inline for (comptime std.enums.values(Zcu.BuiltinDecl)) |builtin_decl| { + if (stage == comptime builtin_decl.stage()) { + const parent_ns: Zcu.Namespace.Index, const parent_name: []const u8, const name: []const u8 = switch (comptime builtin_decl.access()) { + .direct => |name| .{ builtin_namespace, "std.builtin", name }, + .nested => |nested| access: { + const parent_ty: Type = .fromInterned(@field(zcu.builtin_decl_values, @tagName(nested[0]))); + const parent_ns = parent_ty.getNamespace(zcu).unwrap() orelse { + return sema.fail(block, src, "std.builtin.{s} is not a container type", .{@tagName(nested[0])}); + }; + break :access .{ parent_ns, "std.builtin." ++ @tagName(nested[0]), nested[1] }; + }, + }; + + const name_nts = try ip.getOrPutString(gpa, pt.tid, name, .no_embedded_nulls); + const result = try sema.namespaceLookupVal(block, src, parent_ns, name_nts) orelse + return sema.fail(block, src, "{s} missing {s}", .{ parent_name, name }); + + const val = try sema.resolveConstDefinedValue(block, src, result, null); + + switch (builtin_decl.kind()) { + .type => if (val.typeOf(zcu).zigTypeTag(zcu) != .type) { + return sema.fail(block, src, "{s}.{s} is not a type", .{ parent_name, name }); + } else { + try val.toType().resolveFully(pt); + }, + .func => if (val.typeOf(zcu).zigTypeTag(zcu) != .@"fn") { + return sema.fail(block, src, "{s}.{s} is not a function", .{ parent_name, name }); + }, + .string => { + const ty = val.typeOf(zcu); + if (!ty.isSinglePointer(zcu) or + !ty.isConstPtr(zcu) or + ty.childType(zcu).zigTypeTag(zcu) != .array or + ty.childType(zcu).childType(zcu).toIntern() != .u8_type) + { + return sema.fail(block, src, "{s}.{s} is not a valid string", .{ parent_name, name }); + } + }, + } + + const prev = @field(zcu.builtin_decl_values, @tagName(builtin_decl)); + if (val.toIntern() != prev) { + @field(zcu.builtin_decl_values, @tagName(builtin_decl)) = val.toIntern(); + any_changed = true; + } + } + } + + if (stage == .panic) { + // We use `getBuiltinType` because this is from an earlier stage. + const stack_trace_ty = try sema.getBuiltinType(src, .StackTrace); + const ptr_stack_trace_ty = try pt.singleMutPtrType(stack_trace_ty); + const opt_ptr_stack_trace_ty = try pt.optionalType(ptr_stack_trace_ty.toIntern()); + const null_stack_trace = try pt.intern(.{ .opt = .{ + .ty = opt_ptr_stack_trace_ty.toIntern(), + .val = .none, + } }); + if (null_stack_trace != zcu.null_stack_trace) { + zcu.null_stack_trace = null_stack_trace; + any_changed = true; + } + } + + return any_changed; +} diff --git a/src/Zcu.zig b/src/Zcu.zig index 31e1d8dbd23b..fcd519e2ed34 100644 --- a/src/Zcu.zig +++ b/src/Zcu.zig @@ -217,15 +217,212 @@ all_type_references: std.ArrayListUnmanaged(TypeReference) = .empty, /// Freelist of indices in `all_type_references`. free_type_references: std.ArrayListUnmanaged(u32) = .empty, -panic_messages: [PanicId.len]InternPool.Nav.Index.Optional = .{.none} ** PanicId.len, -/// The panic function body. -panic_func_index: InternPool.Index = .none, +/// Populated by analysis of `AnalUnit.wrap(.{ .memoized_state = s })`, where `s` depends on the field. +builtin_decl_values: BuiltinDecl.Memoized = .{}, +/// Populated by analysis of `AnalUnit.wrap(.{ .memoized_state = .panic })`. null_stack_trace: InternPool.Index = .none, generation: u32 = 0, pub const PerThread = @import("Zcu/PerThread.zig"); +/// Names of declarations in `std.builtin` whose values are memoized in a `BuiltinDecl.Memoized`. +/// The name must exactly match the declaration name, as comptime logic is used to compute the namespace accesses. +/// Parent namespaces must be before their children in this enum. For instance, `.Type` must be before `.@"Type.Fn"`. +/// Additionally, parent namespaces must be resolved in the same stage as their children; see `BuiltinDecl.stage`. +pub const BuiltinDecl = enum { + Signedness, + AddressSpace, + CallingConvention, + returnError, + StackTrace, + SourceLocation, + CallModifier, + AtomicOrder, + AtomicRmwOp, + ReduceOp, + FloatMode, + PrefetchOptions, + ExportOptions, + ExternOptions, + BranchHint, + + Type, + @"Type.Fn", + @"Type.Fn.Param", + @"Type.Int", + @"Type.Float", + @"Type.Pointer", + @"Type.Pointer.Size", + @"Type.Array", + @"Type.Vector", + @"Type.Optional", + @"Type.Error", + @"Type.ErrorUnion", + @"Type.EnumField", + @"Type.Enum", + @"Type.Union", + @"Type.UnionField", + @"Type.Struct", + @"Type.StructField", + @"Type.ContainerLayout", + @"Type.Opaque", + @"Type.Declaration", + + Panic, + @"Panic.call", + @"Panic.sentinelMismatch", + @"Panic.unwrapError", + @"Panic.outOfBounds", + @"Panic.startGreaterThanEnd", + @"Panic.inactiveUnionField", + @"Panic.messages", + @"Panic.messages.reached_unreachable", + @"Panic.messages.unwrap_null", + @"Panic.messages.cast_to_null", + @"Panic.messages.incorrect_alignment", + @"Panic.messages.invalid_error_code", + @"Panic.messages.cast_truncated_data", + @"Panic.messages.negative_to_unsigned", + @"Panic.messages.integer_overflow", + @"Panic.messages.shl_overflow", + @"Panic.messages.shr_overflow", + @"Panic.messages.divide_by_zero", + @"Panic.messages.exact_division_remainder", + @"Panic.messages.integer_part_out_of_bounds", + @"Panic.messages.corrupt_switch", + @"Panic.messages.shift_rhs_too_big", + @"Panic.messages.invalid_enum_value", + @"Panic.messages.for_len_mismatch", + @"Panic.messages.memcpy_len_mismatch", + @"Panic.messages.memcpy_alias", + @"Panic.messages.noreturn_returned", + + VaList, + + /// Determines what kind of validation will be done to the decl's value. + pub fn kind(decl: BuiltinDecl) enum { type, func, string } { + return switch (decl) { + .returnError => .func, + + .StackTrace, + .CallingConvention, + .SourceLocation, + .Signedness, + .AddressSpace, + .VaList, + .CallModifier, + .AtomicOrder, + .AtomicRmwOp, + .ReduceOp, + .FloatMode, + .PrefetchOptions, + .ExportOptions, + .ExternOptions, + .BranchHint, + => .type, + + .Type, + .@"Type.Fn", + .@"Type.Fn.Param", + .@"Type.Int", + .@"Type.Float", + .@"Type.Pointer", + .@"Type.Pointer.Size", + .@"Type.Array", + .@"Type.Vector", + .@"Type.Optional", + .@"Type.Error", + .@"Type.ErrorUnion", + .@"Type.EnumField", + .@"Type.Enum", + .@"Type.Union", + .@"Type.UnionField", + .@"Type.Struct", + .@"Type.StructField", + .@"Type.ContainerLayout", + .@"Type.Opaque", + .@"Type.Declaration", + => .type, + + .Panic => .type, + + .@"Panic.call", + .@"Panic.sentinelMismatch", + .@"Panic.unwrapError", + .@"Panic.outOfBounds", + .@"Panic.startGreaterThanEnd", + .@"Panic.inactiveUnionField", + => .func, + + .@"Panic.messages" => .type, + + .@"Panic.messages.reached_unreachable", + .@"Panic.messages.unwrap_null", + .@"Panic.messages.cast_to_null", + .@"Panic.messages.incorrect_alignment", + .@"Panic.messages.invalid_error_code", + .@"Panic.messages.cast_truncated_data", + .@"Panic.messages.negative_to_unsigned", + .@"Panic.messages.integer_overflow", + .@"Panic.messages.shl_overflow", + .@"Panic.messages.shr_overflow", + .@"Panic.messages.divide_by_zero", + .@"Panic.messages.exact_division_remainder", + .@"Panic.messages.integer_part_out_of_bounds", + .@"Panic.messages.corrupt_switch", + .@"Panic.messages.shift_rhs_too_big", + .@"Panic.messages.invalid_enum_value", + .@"Panic.messages.for_len_mismatch", + .@"Panic.messages.memcpy_len_mismatch", + .@"Panic.messages.memcpy_alias", + .@"Panic.messages.noreturn_returned", + => .string, + }; + } + + /// Resolution of these values is done in three distinct stages: + /// * Resolution of `std.builtin.Panic` and everything under it + /// * Resolution of `VaList` + /// * Everything else + /// + /// Panics are separated because they are provided by the user, so must be able to use + /// things like reification. + /// + /// `VaList` is separate because its value depends on the target, so it needs some reflection + /// machinery to work; additionally, it is `@compileError` on some targets, so must be referenced + /// by itself. + pub fn stage(decl: BuiltinDecl) InternPool.MemoizedStateStage { + if (decl == .VaList) return .va_list; + + if (@intFromEnum(decl) <= @intFromEnum(BuiltinDecl.@"Type.Declaration")) { + return .main; + } else { + return .panic; + } + } + + /// Based on the tag name, determines how to access this decl; either as a direct child of the + /// `std.builtin` namespace, or as a child of some preceding `BuiltinDecl` value. + pub fn access(decl: BuiltinDecl) union(enum) { + direct: []const u8, + nested: struct { BuiltinDecl, []const u8 }, + } { + @setEvalBranchQuota(2000); + return switch (decl) { + inline else => |tag| { + const name = @tagName(tag); + const split = (comptime std.mem.lastIndexOfScalar(u8, name, '.')) orelse return .{ .direct = name }; + const parent = @field(BuiltinDecl, name[0..split]); + comptime assert(@intFromEnum(parent) < @intFromEnum(tag)); // dependencies ordered correctly + return .{ .nested = .{ parent, name[split + 1 ..] } }; + }, + }; + } + + const Memoized = std.enums.EnumFieldStruct(BuiltinDecl, InternPool.Index, .none); +}; + pub const PanicId = enum { reached_unreachable, unwrap_null, @@ -247,8 +444,6 @@ pub const PanicId = enum { memcpy_len_mismatch, memcpy_alias, noreturn_returned, - - pub const len = @typeInfo(PanicId).@"enum".fields.len; }; pub const GlobalErrorSet = std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, void); @@ -2454,6 +2649,7 @@ pub fn markPoDependeeUpToDate(zcu: *Zcu, dependee: InternPool.Dependee) !void { .nav_ty => |nav| try zcu.markPoDependeeUpToDate(.{ .nav_ty = nav }), .type => |ty| try zcu.markPoDependeeUpToDate(.{ .interned = ty }), .func => |func| try zcu.markPoDependeeUpToDate(.{ .interned = func }), + .memoized_state => |stage| try zcu.markPoDependeeUpToDate(.{ .memoized_state = stage }), } } } @@ -2468,6 +2664,7 @@ fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: AnalUni .nav_ty => |nav| .{ .nav_ty = nav }, .type => |ty| .{ .interned = ty }, .func => |func_index| .{ .interned = func_index }, // IES + .memoized_state => |stage| .{ .memoized_state = stage }, }; log.debug("potentially outdated dependee: {}", .{zcu.fmtDependee(dependee)}); var it = ip.dependencyIterator(dependee); @@ -2553,6 +2750,12 @@ pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?AnalUnit { .type => |ty| .{ .interned = ty }, .nav_val => |nav| .{ .nav_val = nav }, .nav_ty => |nav| .{ .nav_ty = nav }, + .memoized_state => { + // If we've hit a loop and some `.memoized_state` is outdated, we should make that choice eagerly. + // In general, it's good to resolve this early on, since -- for instance -- almost every function + // references the panic handler. + return unit; + }, }); while (it.next()) |_| n += 1; @@ -3462,7 +3665,7 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv const other: AnalUnit = .wrap(switch (unit.unwrap()) { .nav_val => |n| .{ .nav_ty = n }, .nav_ty => |n| .{ .nav_val = n }, - .@"comptime", .type, .func => break :queue_paired, + .@"comptime", .type, .func, .memoized_state => break :queue_paired, }); if (result.contains(other)) break :queue_paired; try unit_queue.put(gpa, other, kv.value); // same reference location @@ -3597,6 +3800,7 @@ fn formatAnalUnit(data: struct { unit: AnalUnit, zcu: *Zcu }, comptime fmt: []co const nav = zcu.funcInfo(func).owner_nav; return writer.print("func('{}' [{}])", .{ ip.getNav(nav).fqn.fmt(ip), @intFromEnum(func) }); }, + .memoized_state => return writer.writeAll("memoized_state"), } } fn formatDependee(data: struct { dependee: InternPool.Dependee, zcu: *Zcu }, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { @@ -3642,6 +3846,7 @@ fn formatDependee(data: struct { dependee: InternPool.Dependee, zcu: *Zcu }, com const file_path = zcu.fileByIndex(info.file).sub_file_path; return writer.print("namespace('{s}', %{d}, '{}')", .{ file_path, @intFromEnum(info.inst), k.name.fmt(ip) }); }, + .memoized_state => return writer.writeAll("memoized_state"), } } diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index c382d78efa59..854e27ae9bba 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -560,6 +560,147 @@ pub fn ensureFileAnalyzed(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.Sem return pt.semaFile(file_index); } +/// Ensures that all memoized state on `Zcu` is up-to-date, performing re-analysis if necessary. +/// Returns `error.AnalysisFail` if an analysis error is encountered; the caller is free to ignore +/// this, since the error is already registered, but it must not use the value of memoized fields. +pub fn ensureMemoizedStateUpToDate(pt: Zcu.PerThread, stage: InternPool.MemoizedStateStage) Zcu.SemaError!void { + const tracy = trace(@src()); + defer tracy.end(); + + const zcu = pt.zcu; + const gpa = zcu.gpa; + + const unit: AnalUnit = .wrap(.{ .memoized_state = stage }); + + log.debug("ensureMemoizedStateUpToDate", .{}); + + assert(!zcu.analysis_in_progress.contains(unit)); + + const was_outdated = zcu.outdated.swapRemove(unit) or zcu.potentially_outdated.swapRemove(unit); + const prev_failed = zcu.failed_analysis.contains(unit) or zcu.transitive_failed_analysis.contains(unit); + + if (was_outdated) { + dev.check(.incremental); + _ = zcu.outdated_ready.swapRemove(unit); + // No need for `deleteUnitExports` because we never export anything. + zcu.deleteUnitReferences(unit); + if (zcu.failed_analysis.fetchSwapRemove(unit)) |kv| { + kv.value.destroy(gpa); + } + _ = zcu.transitive_failed_analysis.swapRemove(unit); + } else { + if (prev_failed) return error.AnalysisFail; + // We use an arbitrary field to check if the state has been resolved yet. + const val = switch (stage) { + .main => zcu.builtin_decl_values.Type, + .panic => zcu.builtin_decl_values.Panic, + .va_list => zcu.builtin_decl_values.VaList, + }; + if (val != .none) return; + } + + const any_changed: bool, const new_failed: bool = if (pt.analyzeMemoizedState(stage)) |any_changed| + .{ any_changed or prev_failed, false } + else |err| switch (err) { + error.AnalysisFail => res: { + if (!zcu.failed_analysis.contains(unit)) { + // If this unit caused the error, it would have an entry in `failed_analysis`. + // Since it does not, this must be a transitive failure. + try zcu.transitive_failed_analysis.put(gpa, unit, {}); + log.debug("mark transitive analysis failure for {}", .{zcu.fmtAnalUnit(unit)}); + } + break :res .{ !prev_failed, true }; + }, + error.OutOfMemory => { + // TODO: same as for `ensureComptimeUnitUpToDate` etc + return error.OutOfMemory; + }, + error.GenericPoison => unreachable, + error.ComptimeReturn => unreachable, + error.ComptimeBreak => unreachable, + }; + + if (was_outdated) { + const dependee: InternPool.Dependee = .{ .memoized_state = stage }; + if (any_changed) { + try zcu.markDependeeOutdated(.marked_po, dependee); + } else { + try zcu.markPoDependeeUpToDate(dependee); + } + } + + if (new_failed) return error.AnalysisFail; +} + +fn analyzeMemoizedState(pt: Zcu.PerThread, stage: InternPool.MemoizedStateStage) Zcu.CompileError!bool { + const zcu = pt.zcu; + const ip = &zcu.intern_pool; + const gpa = zcu.gpa; + + const unit: AnalUnit = .wrap(.{ .memoized_state = stage }); + + try zcu.analysis_in_progress.put(gpa, unit, {}); + defer assert(zcu.analysis_in_progress.swapRemove(unit)); + + // Before we begin, collect: + // * The type `std`, and its namespace + // * The type `std.builtin`, and its namespace + // * A semi-reasonable source location + const std_file_imported = pt.importPkg(zcu.std_mod) catch return error.AnalysisFail; + try pt.ensureFileAnalyzed(std_file_imported.file_index); + const std_type: Type = .fromInterned(zcu.fileRootType(std_file_imported.file_index)); + const std_namespace = std_type.getNamespaceIndex(zcu); + try pt.ensureNamespaceUpToDate(std_namespace); + const builtin_str = try ip.getOrPutString(gpa, pt.tid, "builtin", .no_embedded_nulls); + const builtin_nav = zcu.namespacePtr(std_namespace).pub_decls.getKeyAdapted(builtin_str, Zcu.Namespace.NameAdapter{ .zcu = zcu }) orelse + @panic("lib/std.zig is corrupt and missing 'builtin'"); + try pt.ensureNavValUpToDate(builtin_nav); + const builtin_type: Type = .fromInterned(ip.getNav(builtin_nav).status.fully_resolved.val); + const builtin_namespace = builtin_type.getNamespaceIndex(zcu); + try pt.ensureNamespaceUpToDate(builtin_namespace); + const src: Zcu.LazySrcLoc = .{ + .base_node_inst = builtin_type.typeDeclInst(zcu).?, + .offset = .entire_file, + }; + + var analysis_arena: std.heap.ArenaAllocator = .init(gpa); + defer analysis_arena.deinit(); + + var comptime_err_ret_trace: std.ArrayList(Zcu.LazySrcLoc) = .init(gpa); + defer comptime_err_ret_trace.deinit(); + + var sema: Sema = .{ + .pt = pt, + .gpa = gpa, + .arena = analysis_arena.allocator(), + .code = .{ .instructions = .empty, .string_bytes = &.{}, .extra = &.{} }, + .owner = unit, + .func_index = .none, + .func_is_naked = false, + .fn_ret_ty = .void, + .fn_ret_ty_ies = null, + .comptime_err_ret_trace = &comptime_err_ret_trace, + }; + defer sema.deinit(); + + var block: Sema.Block = .{ + .parent = null, + .sema = &sema, + .namespace = std_namespace, + .instructions = .{}, + .inlining = null, + .comptime_reason = .{ .reason = .{ + .src = src, + .r = .{ .simple = .type }, + } }, + .src_base_inst = src.base_node_inst, + .type_name_ctx = .empty, + }; + defer block.instructions.deinit(gpa); + + return sema.analyzeMemoizedState(&block, src, builtin_namespace, stage); +} + /// Ensures that the state of the given `ComptimeUnit` is fully up-to-date, performing re-analysis /// if necessary. Returns `error.AnalysisFail` if an analysis error is encountered; the caller is /// free to ignore this, since the error is already registered. @@ -2615,7 +2756,7 @@ fn analyzeFnBodyInner(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaE // result in circular dependency errors. // TODO: this can go away once we fix backends having to resolve `StackTrace`. // The codegen timing guarantees that the parameter types will be populated. - sema.resolveFnTypes(fn_ty) catch |err| switch (err) { + sema.resolveFnTypes(fn_ty, inner_block.nodeOffset(0)) catch |err| switch (err) { error.GenericPoison => unreachable, error.ComptimeReturn => unreachable, error.ComptimeBreak => unreachable, @@ -3471,23 +3612,6 @@ pub fn structPackedFieldBitOffset( unreachable; // index out of bounds } -pub fn getBuiltinNav(pt: Zcu.PerThread, name: []const u8) Allocator.Error!InternPool.Nav.Index { - const zcu = pt.zcu; - const gpa = zcu.gpa; - const ip = &zcu.intern_pool; - const std_file_imported = pt.importPkg(zcu.std_mod) catch @panic("failed to import lib/std.zig"); - const std_type = Type.fromInterned(zcu.fileRootType(std_file_imported.file_index)); - const std_namespace = zcu.namespacePtr(std_type.getNamespace(zcu).unwrap().?); - const builtin_str = try ip.getOrPutString(gpa, pt.tid, "builtin", .no_embedded_nulls); - const builtin_nav = std_namespace.pub_decls.getKeyAdapted(builtin_str, Zcu.Namespace.NameAdapter{ .zcu = zcu }) orelse - @panic("lib/std.zig is corrupt and missing 'builtin'"); - pt.ensureNavValUpToDate(builtin_nav) catch @panic("std.builtin is corrupt"); - const builtin_type = Type.fromInterned(ip.getNav(builtin_nav).status.fully_resolved.val); - const builtin_namespace = zcu.namespacePtr(builtin_type.getNamespace(zcu).unwrap() orelse @panic("std.builtin is corrupt")); - const name_str = try ip.getOrPutString(gpa, pt.tid, name, .no_embedded_nulls); - return builtin_namespace.pub_decls.getKeyAdapted(name_str, Zcu.Namespace.NameAdapter{ .zcu = zcu }) orelse @panic("lib/std/builtin.zig is corrupt"); -} - pub fn navPtrType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Allocator.Error!Type { const zcu = pt.zcu; const ip = &zcu.intern_pool; diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 5b36644019c8..63fc7b7dcc3c 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -5754,10 +5754,12 @@ pub const FuncGen = struct { const o = fg.ng.object; const zcu = o.pt.zcu; const ip = &zcu.intern_pool; - const msg_nav_index = zcu.panic_messages[@intFromEnum(panic_id)].unwrap().?; - const msg_nav = ip.getNav(msg_nav_index); - const msg_len = Type.fromInterned(msg_nav.typeOf(ip)).childType(zcu).arrayLen(zcu); - const msg_ptr = try o.lowerValue(msg_nav.status.fully_resolved.val); + const panic_msg_val: InternPool.Index = switch (panic_id) { + inline else => |ct_panic_id| @field(zcu.builtin_decl_values, "Panic.messages." ++ @tagName(ct_panic_id)), + }; + assert(panic_msg_val != .none); + const msg_len = Value.fromInterned(panic_msg_val).typeOf(zcu).childType(zcu).arrayLen(zcu); + const msg_ptr = try o.lowerValue(panic_msg_val); const null_opt_addr_global = try fg.resolveNullOptUsize(); const target = zcu.getTarget(); const llvm_usize = try o.lowerType(Type.usize); @@ -5768,7 +5770,7 @@ pub const FuncGen = struct { // ptr null, ; stack trace // ptr @2, ; addr (null ?usize) // ) - const panic_func = zcu.funcInfo(zcu.panic_func_index); + const panic_func = zcu.funcInfo(zcu.builtin_decl_values.@"Panic.call"); const panic_nav = ip.getNav(panic_func.owner_nav); const fn_info = zcu.typeToFunc(Type.fromInterned(panic_nav.typeOf(ip))).?; const panic_global = try o.resolveLlvmFunction(panic_func.owner_nav); From 6cc848e9f6c86184030fd4c9bc7c09ce90dd5cf9 Mon Sep 17 00:00:00 2001 From: mlugg Date: Sat, 4 Jan 2025 05:16:16 +0000 Subject: [PATCH 5/5] incremental: add new test cases These cover the fixes from the last few commits. --- test/incremental/change_panic_handler | 74 ++++++++++++++++++++++++++ test/incremental/temporary_parse_error | 18 +++++++ 2 files changed, 92 insertions(+) create mode 100644 test/incremental/change_panic_handler create mode 100644 test/incremental/temporary_parse_error diff --git a/test/incremental/change_panic_handler b/test/incremental/change_panic_handler new file mode 100644 index 000000000000..bab6eab793dc --- /dev/null +++ b/test/incremental/change_panic_handler @@ -0,0 +1,74 @@ +#target=x86_64-linux-selfhosted +#target=x86_64-linux-cbe +#target=x86_64-windows-cbe +#update=initial version +#file=main.zig +pub fn main() !u8 { + var a: u8 = undefined; + a = 255; + _ = a + 1; + return 1; +} +pub const Panic = struct { + pub const call = myPanic; + pub const sentinelMismatch = std.debug.FormattedPanic.sentinelMismatch; + pub const unwrapError = std.debug.FormattedPanic.unwrapError; + pub const outOfBounds = std.debug.FormattedPanic.outOfBounds; + pub const startGreaterThanEnd = std.debug.FormattedPanic.startGreaterThanEnd; + pub const inactiveUnionField = std.debug.FormattedPanic.inactiveUnionField; + pub const messages = std.debug.FormattedPanic.messages; +}; +fn myPanic(msg: []const u8, _: ?*std.builtin.StackTrace, _: ?usize) noreturn { + std.io.getStdOut().writer().print("panic message: {s}\n", .{msg}) catch {}; + std.process.exit(0); +} +const std = @import("std"); +#expect_stdout="panic message: integer overflow\n" + +#update=change the panic handler body +#file=main.zig +pub fn main() !u8 { + var a: u8 = undefined; + a = 255; + _ = a + 1; + return 1; +} +pub const Panic = struct { + pub const call = myPanic; + pub const sentinelMismatch = std.debug.FormattedPanic.sentinelMismatch; + pub const unwrapError = std.debug.FormattedPanic.unwrapError; + pub const outOfBounds = std.debug.FormattedPanic.outOfBounds; + pub const startGreaterThanEnd = std.debug.FormattedPanic.startGreaterThanEnd; + pub const inactiveUnionField = std.debug.FormattedPanic.inactiveUnionField; + pub const messages = std.debug.FormattedPanic.messages; +}; +fn myPanic(msg: []const u8, _: ?*std.builtin.StackTrace, _: ?usize) noreturn { + std.io.getStdOut().writer().print("new panic message: {s}\n", .{msg}) catch {}; + std.process.exit(0); +} +const std = @import("std"); +#expect_stdout="new panic message: integer overflow\n" + +#update=change the panic handler function value +#file=main.zig +pub fn main() !u8 { + var a: u8 = undefined; + a = 255; + _ = a + 1; + return 1; +} +pub const Panic = struct { + pub const call = myPanicNew; + pub const sentinelMismatch = std.debug.FormattedPanic.sentinelMismatch; + pub const unwrapError = std.debug.FormattedPanic.unwrapError; + pub const outOfBounds = std.debug.FormattedPanic.outOfBounds; + pub const startGreaterThanEnd = std.debug.FormattedPanic.startGreaterThanEnd; + pub const inactiveUnionField = std.debug.FormattedPanic.inactiveUnionField; + pub const messages = std.debug.FormattedPanic.messages; +}; +fn myPanicNew(msg: []const u8, _: ?*std.builtin.StackTrace, _: ?usize) noreturn { + std.io.getStdOut().writer().print("third panic message: {s}\n", .{msg}) catch {}; + std.process.exit(0); +} +const std = @import("std"); +#expect_stdout="third panic message: integer overflow\n" diff --git a/test/incremental/temporary_parse_error b/test/incremental/temporary_parse_error new file mode 100644 index 000000000000..4668c53b32f0 --- /dev/null +++ b/test/incremental/temporary_parse_error @@ -0,0 +1,18 @@ +#target=x86_64-linux-selfhosted +#target=x86_64-linux-cbe +#target=x86_64-windows-cbe +#update=initial version +#file=main.zig +const std = @import("std"); +pub fn main() !void {} +#expect_stdout="" + +#update=introduce parse error +#file=main.zig +pub fn main() !void { +#expect_error=ignored + +#update=fix parse error +#file=main.zig +pub fn main() !void {} +#expect_stdout=""