╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ Zcu.zig ──────────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭ find file ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╮ ╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │ │ │http│ ssh │ │ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╯ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
4749 4750 pub const CodegenFailError = Io.Cancelable || error{ 4751 /// Indicates the error message has been already stored at `Zcu.failed_codegen`. 4752 AlreadyReported, 4753 OutOfMemory, 4754 }; 4755 4756 pub fn codegenFail( 4757 zcu: *Zcu, 4758 nav_index: InternPool.Nav.Index, 4759 comptime format: []const u8, 4760 args: anytype, 4761 ) CodegenFailError { 4762 const msg = try Zcu.ErrorMsg.create(zcu.gpa, zcu.navSrcLoc(nav_index), format, args); 4763 return zcu.codegenFailMsg(nav_index, msg); 4764 } 4765 4766 /// Takes ownership of `msg`, even on OOM. 4767 pub fn codegenFailMsg(zcu: *Zcu, nav_index: InternPool.Nav.Index, msg: *ErrorMsg) CodegenFailError { 4768 const comp = zcu.comp; 4769 const gpa = comp.gpa; 4770 const io = comp.io; 4771 { 4772 comp.mutex.lockUncancelable(io); 4773 defer comp.mutex.unlock(io); 4774 errdefer msg.deinit(gpa); 4775 try zcu.failed_codegen.putNoClobber(gpa, nav_index, msg); 4776 } 4777 return error.AlreadyReported; 4778 } 4779 4780 pub fn codegenFailType( 4781 zcu: *Zcu, 4782 ty_index: InternPool.Index, 4783 comptime format: []const u8, 4784 args: anytype, 4785 ) CodegenFailError { 4786 const gpa = zcu.gpa; 4787 try zcu.failed_types.ensureUnusedCapacity(gpa, 1); 4788 const msg = try Zcu.ErrorMsg.create(gpa, zcu.typeSrcLoc(ty_index), format, args); 4789 zcu.failed_types.putAssumeCapacityNoClobber(ty_index, msg); 4790 return error.AlreadyReported; 4791 } 4792 4793 pub fn codegenFailTypeMsg(zcu: *Zcu, ty_index: InternPool.Index, msg: *ErrorMsg) CodegenFailError { 4794 const gpa = zcu.gpa; 4795 { 4796 errdefer msg.deinit(gpa); 4797 try zcu.failed_types.ensureUnusedCapacity(gpa, 1); 4798 } 4799 zcu.failed_types.putAssumeCapacityNoClobber(ty_index, msg); 4800 return error.AlreadyReported; 4801 } 4802 4803 /// Asserts that `zcu.multi_module_err != null`. 4804 pub fn addFileInMultipleModulesError( 4805 zcu: *Zcu, 4806 eb: *std.zig.ErrorBundle.Wip, 4807 ) Allocator.Error!void { 4808 const gpa = zcu.gpa; 4809 4810 const info = zcu.multi_module_err.?; 4811 const file = info.file; 4812 4813 // error: file exists in modules 'root.foo' and 'root.bar' 4814 // note: files must belong to only one module 4815 // note: file is imported here 4816 // note: which is imported here 4817 // note: which is the root of module 'root.foo' imported here 4818 // note: file is the root of module 'root.bar' imported here 4819 4820 const file_src = try zcu.fileByIndex(file).errorBundleWholeFileSrc(zcu, eb); 4821 const root_msg = try eb.printString("file exists in modules '{s}' and '{s}'", .{ 4822 info.modules[0].fully_qualified_name, 4823 info.modules[1].fully_qualified_name, 4824 }); 4825 4826 var notes: std.ArrayList(std.zig.ErrorBundle.MessageIndex) = .empty; 4827 defer notes.deinit(gpa); 4828 4829 try notes.append(gpa, try eb.addErrorMessage(.{ 4830 .msg = try eb.addString("files must belong to only one module"), 4831 .src_loc = file_src, 4832 })); 4833 4834 try zcu.explainWhyFileIsInModule(eb, &notes, file, info.modules[0], info.refs[0]); 4835 try zcu.explainWhyFileIsInModule(eb, &notes, file, info.modules[1], info.refs[1]); 4836 4837 try eb.addRootErrorMessage(.{ 4838 .msg = root_msg, 4839 .src_loc = file_src, 4840 .notes_len = @intCast(notes.items.len), 4841 }); 4842 const notes_start = try eb.reserveNotes(@intCast(notes.items.len)); 4843 const notes_slice: []std.zig.ErrorBundle.MessageIndex = @ptrCast(eb.extra.items[notes_start..]); 4844 @memcpy(notes_slice, notes.items); 4845 } 4846 4847 fn explainWhyFileIsInModule( 4848 zcu: *Zcu, 4849 eb: *std.zig.ErrorBundle.Wip, 4850 notes_out: *std.ArrayList(std.zig.ErrorBundle.MessageIndex), 4851 file: File.Index, 4852 in_module: *Module, 4853 ref: File.Reference, 4854 ) Allocator.Error!void { 4855 const gpa = zcu.gpa; 4856 4857 // error: file is the root of module 'foo' 4858 // 4859 // error: file is imported here by the root of module 'foo' 4860 // 4861 // error: file is imported here 4862 // note: which is imported here 4863 // note: which is imported here by the root of module 'foo' 4864 4865 var import = switch (ref) { 4866 .analysis_root => |mod| { 4867 assert(mod == in_module); 4868 try notes_out.append(gpa, try eb.addErrorMessage(.{ 4869 .msg = try eb.printString("file is the root of module '{s}'", .{mod.fully_qualified_name}), 4870 .src_loc = try zcu.fileByIndex(file).errorBundleWholeFileSrc(zcu, eb), 4871 })); 4872 return; 4873 }, 4874 .import => |import| if (import.module) |mod| { 4875 assert(mod == in_module); 4876 try notes_out.append(gpa, try eb.addErrorMessage(.{ 4877 .msg = try eb.printString("file is the root of module '{s}'", .{mod.fully_qualified_name}), 4878 .src_loc = try zcu.fileByIndex(file).errorBundleWholeFileSrc(zcu, eb), 4879 })); 4880 return; 4881 } else import, 4882 }; 4883 4884 var is_first = true; 4885 while (true) { 4886 const thing: []const u8 = if (is_first) "file" else "which"; 4887 is_first = false; 4888 4889 const importer_file = zcu.fileByIndex(import.importer); 4890 // `errorBundleTokenSrc` expects the tree to be loaded 4891 _ = importer_file.getTree(zcu) catch |err| { 4892 try Compilation.unableToLoadZcuFile(zcu, eb, importer_file, err); 4893 return; // stop the explanation early 4894 }; 4895 const import_src = try importer_file.errorBundleTokenSrc(import.tok, zcu, eb); 4896 4897 const importer_ref = zcu.alive_files.get(import.importer).?; 4898 const importer_root: ?*Module = switch (importer_ref) { 4899 .analysis_root => |mod| mod, 4900 .import => |i| i.module, 4901 }; 4902 4903 if (importer_root) |m| { 4904 try notes_out.append(gpa, try eb.addErrorMessage(.{ 4905 .msg = try eb.printString("{s} is imported here by the root of module '{s}'", .{ thing, m.fully_qualified_name }), 4906 .src_loc = import_src, 4907 })); 4908 return; 4909 } 4910 4911 try notes_out.append(gpa, try eb.addErrorMessage(.{ 4912 .msg = try eb.printString("{s} is imported here", .{thing}), 4913 .src_loc = import_src, 4914 })); 4915 4916 import = importer_ref.import; 4917 } 4918 } 4919 4920 pub fn addDependencyLoopErrors(zcu: *Zcu, eb: *std.zig.ErrorBundle.Wip) Allocator.Error!void { 4921 const gpa = zcu.comp.gpa; 4922 4923 const all_references = try zcu.resolveReferences(); 4924 4925 var units: std.ArrayList(AnalUnit) = .empty; 4926 defer units.deinit(gpa); 4927 4928 // TODO: sort the dependency loops somehow to make the error bundle reproducible 4929 for (zcu.dependency_loops.keys()) |arbitrary_unit| { 4930 units.clearRetainingCapacity(); 4931 4932 var cur = arbitrary_unit; 4933 while (true) { 4934 try units.append(gpa, cur); 4935 cur = zcu.dependency_loop_nodes.get(cur).?.unit; 4936 if (cur == arbitrary_unit) break; 4937 } 4938 4939 // `units` now contains all units in the loop. We need to pick a starting point somewhere 4940 // along that loop to begin. We will pick whichever node has the shortest reference trace, 4941 // because the other units may well just be referenced *by* that one! This is also likely 4942 // to match the user's intuition for where the loop "starts". 4943 var start_index: usize = 0; 4944 var start_depth: u32 = depth: { 4945 var depth: u32 = 0; 4946 var opt_ref = all_references.get(units.items[0]) orelse { 4947 // This dependency loop is actually unreferenced, so we don't need to emit a compile 4948 // error at all! Move onto the next dependency loop. 4949 continue; 4950 }; 4951 while (opt_ref) |ref| : (opt_ref = all_references.get(ref.referencer).?) depth += 1; 4952 break :depth depth; 4953 }; 4954 for (units.items[1..], 1..) |unit, index| { 4955 var depth: u32 = 0; 4956 var opt_ref = all_references.get(unit).?; 4957 while (opt_ref) |ref| : (opt_ref = all_references.get(ref.referencer).?) depth += 1; 4958 if (depth < start_depth) { 4959 start_index = index; 4960 start_depth = depth; 4961 } 4962 } 4963 4964 // Collect a reference trace for the start of the loop. 4965 var ref_trace: std.ArrayList(std.zig.ErrorBundle.ReferenceTrace) = .empty; 4966 defer ref_trace.deinit(gpa); 4967 const frame_limit = zcu.comp.reference_trace orelse 0; 4968 try zcu.populateReferenceTrace(units.items[start_index], frame_limit, eb, &ref_trace); 4969 4970 if (units.items.len == 1) { 4971 // Don't do a complicated message with multiple notes, just do a single error message. 4972 assert(start_index == 0); 4973 const root_msg = addDependencyLoopErrorLine(zcu, eb, units.items[start_index], ref_trace.items) catch |err| switch (err) { 4974 error.AlreadyReported => return, // give up on the dep loop error 4975 error.OutOfMemory => |e| return e, 4976 }; 4977 try eb.root_list.append(eb.gpa, root_msg); 4978 continue; 4979 } 4980 4981 // Collect all notes first so we don't leave an incomplete root error message on `error.AlreadyReported`. 4982 const note_buf = try gpa.alloc(std.zig.ErrorBundle.MessageIndex, units.items.len + 1); 4983 defer gpa.free(note_buf); 4984 note_buf[0] = addDependencyLoopErrorLine(zcu, eb, units.items[start_index], ref_trace.items) catch |err| switch (err) { 4985 error.AlreadyReported => return, // give up on the dep loop error 4986 error.OutOfMemory => |e| return e, 4987 }; 4988 for (units.items[start_index + 1 ..], note_buf[1 .. units.items.len - start_index]) |unit, *note| { 4989 note.* = addDependencyLoopErrorLine(zcu, eb, unit, &.{}) catch |err| switch (err) { 4990 error.AlreadyReported => return, // give up on the dep loop error 4991 error.OutOfMemory => |e| return e, 4992 }; 4993 } 4994 for (units.items[0..start_index], note_buf[units.items.len - start_index .. units.items.len]) |unit, *note| { 4995 note.* = addDependencyLoopErrorLine(zcu, eb, unit, &.{}) catch |err| switch (err) { 4996 error.AlreadyReported => return, // give up on the dep loop error 4997 error.OutOfMemory => |e| return e, 4998 }; 4999 } 5000 note_buf[units.items.len] = try eb.addErrorMessage(.{ 5001 .msg = try eb.addString("eliminate any one of these dependencies to break the loop"), 5002 .src_loc = .none, 5003 }); 5004 5005 try eb.addRootErrorMessage(.{ 5006 .msg = try eb.printString("dependency loop with length {d}", .{units.items.len}), 5007 .src_loc = .none, 5008 .notes_len = @intCast(units.items.len + 1), 5009 }); 5010 const notes_start = try eb.reserveNotes(@intCast(units.items.len + 1)); 5011 const notes: []std.zig.ErrorBundle.MessageIndex = @ptrCast(eb.extra.items[notes_start..]); 5012 @memcpy(notes, note_buf); 5013 } 5014 } 5015 fn addDependencyLoopErrorLine( 5016 zcu: *Zcu, 5017 eb: *std.zig.ErrorBundle.Wip, 5018 source_unit: AnalUnit, 5019 ref_trace: []const std.zig.ErrorBundle.ReferenceTrace, 5020 ) (Allocator.Error || error{AlreadyReported})!std.zig.ErrorBundle.MessageIndex { 5021 const ip = &zcu.intern_pool; 5022 const comp = zcu.comp; 5023 5024 const fmt_source: std.fmt.Alt(FormatAnalUnit, formatDependencyLoopSourceUnit) = .{ .data = .{ 5025 .unit = source_unit, 5026 .zcu = zcu, 5027 } }; 5028 5029 const dep_node = zcu.dependency_loop_nodes.get(source_unit).?; 5030 5031 const msg: std.zig.ErrorBundle.String = if (dep_node.unit == source_unit) switch (source_unit.unwrap()) { 5032 .@"comptime" => unreachable, // cannot be involved in a dependency loop 5033 .nav_ty, .nav_val => try eb.printString("{f} depends on itself here", .{fmt_source}), 5034 .memoized_state => unreachable, // memoized_state definitely does not *directly* depend on itself 5035 .func => try eb.printString("{f} uses its own inferred error set here", .{fmt_source}), 5036 .type_layout => try eb.printString("{f} depends on itself {s}", .{ 5037 fmt_source, 5038 dep_node.reason.type_layout_reason.msg(), 5039 }), 5040 .struct_defaults => |ty| try eb.printString( 5041 "default field values of '{f}' depend on themselves for initialization here", 5042 .{Type.fromInterned(ty).containerTypeName(ip).fqn.fmt(ip)}, 5043 ), 5044 } else switch (dep_node.unit.unwrap()) { 5045 .@"comptime" => unreachable, // cannot be involved in a dependency loop 5046 .nav_val => |nav| try eb.printString("{f} uses value of declaration '{f}' here", .{ 5047 fmt_source, ip.getNav(nav).fqn.fmt(ip), 5048 }), 5049 .nav_ty => |nav| try eb.printString("{f} uses type of declaration '{f}' here", .{ 5050 fmt_source, ip.getNav(nav).fqn.fmt(ip), 5051 }), 5052 .memoized_state => |stage| switch (stage) { 5053 .panic => try eb.printString("{f} requires panic handler for call here", .{fmt_source}), 5054 else => try eb.printString("{f} requires 'std.lang' declarations here", .{fmt_source}), 5055 }, 5056 .func => |func| try eb.printString("{f} uses inferred error set of function '{f}' here", .{ 5057 fmt_source, ip.getNav(zcu.funcInfo(func).owner_nav).fqn.fmt(ip), 5058 }), 5059 .type_layout => |ty| try eb.printString("{f} depends on type '{f}' {s}", .{ 5060 fmt_source, 5061 Type.fromInterned(ty).containerTypeName(ip).fqn.fmt(ip), 5062 dep_node.reason.type_layout_reason.msg(), 5063 }), 5064 .struct_defaults => |ty| try eb.printString( 5065 "{f} uses default field values of '{f}' here", 5066 .{ fmt_source, Type.fromInterned(ty).containerTypeName(ip).fqn.fmt(ip) }, 5067 ), 5068 }; 5069 5070 const src_loc = dep_node.reason.src.upgrade(zcu); 5071 const source = src_loc.file_scope.getSource(zcu) catch |err| { 5072 try Compilation.unableToLoadZcuFile(zcu, eb, src_loc.file_scope, err); 5073 return error.AlreadyReported; 5074 }; 5075 const span = src_loc.span(zcu) catch |err| { 5076 try Compilation.unableToLoadZcuFile(zcu, eb, src_loc.file_scope, err); 5077 return error.AlreadyReported; 5078 }; 5079 const loc = std.zig.findLineColumn(source, span.main); 5080 const eb_src = try eb.addSourceLocation(.{ 5081 .src_path = try eb.printString("{f}", .{src_loc.file_scope.path.fmt(comp)}), 5082 .span_start = span.start, 5083 .span_main = span.main, 5084 .span_end = span.end, 5085 .line = @intCast(loc.line), 5086 .column = @intCast(loc.column), 5087 .source_line = try eb.addString(loc.source_line), 5088 .reference_trace_len = @intCast(ref_trace.len), 5089 }); 5090 for (ref_trace) |rt| try eb.addReferenceTrace(rt); 5091 return eb.addErrorMessage(.{ 5092 .msg = msg, 5093 .src_loc = eb_src, 5094 }); 5095 } 5096 fn formatDependencyLoopSourceUnit(data: FormatAnalUnit, w: *Io.Writer) Io.Writer.Error!void { 5097 const zcu = data.zcu; 5098 const ip = &zcu.intern_pool; 5099 switch (data.unit.unwrap()) { 5100 .@"comptime" => unreachable, // cannot be involved in a dependency loop 5101 .nav_val => |nav| try w.print("value of declaration '{f}'", .{ip.getNav(nav).fqn.fmt(ip)}), 5102 .nav_ty => |nav| try w.print("type of declaration '{f}'", .{ip.getNav(nav).fqn.fmt(ip)}), 5103 .memoized_state => |stage| switch (stage) { 5104 .panic => try w.writeAll("panic handler"), 5105 else => try w.writeAll("'std.lang' declarations"), 5106 }, 5107 .type_layout => |ty| try w.print("type '{f}'", .{ 5108 Type.fromInterned(ty).containerTypeName(ip).fqn.fmt(ip), 5109 }), 5110 .struct_defaults => |ty| try w.print("default field value of '{f}'", .{ 5111 Type.fromInterned(ty).containerTypeName(ip).fqn.fmt(ip), 5112 }), 5113 .func => |func| try w.print("function '{f}'", .{ 5114 ip.getNav(zcu.funcInfo(func).owner_nav).fqn.fmt(ip), 5115 }), 5116 } 5117 } 5118 5119 pub fn populateReferenceTrace( 5120 zcu: *Zcu, 5121 root: AnalUnit, 5122 frame_limit: u32, 5123 eb: *std.zig.ErrorBundle.Wip, 5124 ref_trace: *std.ArrayList(std.zig.ErrorBundle.ReferenceTrace), 5125 ) Allocator.Error!void { 5126 const ip = &zcu.intern_pool; 5127 const gpa = zcu.comp.gpa; 5128 5129 if (frame_limit == 0) return; 5130 5131 const all_references = try zcu.resolveReferences(); 5132 5133 var seen: std.AutoHashMapUnmanaged(InternPool.AnalUnit, void) = .empty; 5134 defer seen.deinit(gpa); 5135 5136 var referenced_by = root; 5137 while (all_references.get(referenced_by)) |maybe_ref| { 5138 const ref = maybe_ref orelse break; 5139 const gop = try seen.getOrPut(gpa, ref.referencer); 5140 if (gop.found_existing) break; 5141 if (ref_trace.items.len < frame_limit) { 5142 var last_call_src = ref.src; 5143 var opt_inline_frame = ref.inline_frame; 5144 while (opt_inline_frame.unwrap()) |inline_frame| { 5145 const f = inline_frame.ptr(zcu).*; 5146 const func_nav = ip.indexToKey(f.callee).func.owner_nav; 5147 const func_name = ip.getNav(func_nav).name.toSlice(ip); 5148 addReferenceTraceFrame(zcu, eb, ref_trace, func_name, last_call_src, true) catch |err| switch (err) { 5149 error.OutOfMemory => |e| return e, 5150 error.AlreadyReported => { 5151 // An incomplete reference trace isn't the end of the world; just cut it off. 5152 return; 5153 }, 5154 }; 5155 last_call_src = f.call_src; 5156 opt_inline_frame = f.parent; 5157 } 5158 const root_name: ?[]const u8 = switch (ref.referencer.unwrap()) { 5159 .@"comptime" => "comptime", 5160 .nav_val, .nav_ty => |nav| ip.getNav(nav).name.toSlice(ip), 5161 .type_layout, .struct_defaults => |ty| Type.fromInterned(ty).containerTypeName(ip).fqn.toSlice(ip), 5162 .func => |f| ip.getNav(zcu.funcInfo(f).owner_nav).name.toSlice(ip), 5163 .memoized_state => null, 5164 }; 5165 if (root_name) |n| { 5166 addReferenceTraceFrame(zcu, eb, ref_trace, n, last_call_src, false) catch |err| switch (err) { 5167 error.OutOfMemory => |e| return e, 5168 error.AlreadyReported => { 5169 // An incomplete reference trace isn't the end of the world; just cut it off. 5170 return; 5171 }, 5172 }; 5173 } 5174 } 5175 referenced_by = ref.referencer; 5176 } 5177 5178 if (seen.count() > ref_trace.items.len) { 5179 try ref_trace.append(gpa, .{ 5180 .decl_name = @intCast(seen.count() - ref_trace.items.len), 5181 .src_loc = .none, 5182 }); 5183 } 5184 } 5185 fn addReferenceTraceFrame( 5186 zcu: *Zcu, 5187 eb: *std.zig.ErrorBundle.Wip, 5188 ref_trace: *std.ArrayList(std.zig.ErrorBundle.ReferenceTrace), 5189 name: []const u8, 5190 lazy_src: Zcu.LazySrcLoc, 5191 inlined: bool, 5192 ) error{ OutOfMemory, AlreadyReported }!void { 5193 const gpa = zcu.gpa; 5194 const src = lazy_src.upgrade(zcu); 5195 const source = src.file_scope.getSource(zcu) catch |err| { 5196 try Compilation.unableToLoadZcuFile(zcu, eb, src.file_scope, err); 5197 return error.AlreadyReported; 5198 }; 5199 const span = src.span(zcu) catch |err| { 5200 try Compilation.unableToLoadZcuFile(zcu, eb, src.file_scope, err); 5201 return error.AlreadyReported; 5202 }; 5203 const loc = std.zig.findLineColumn(source, span.main); 5204 try ref_trace.append(gpa, .{ 5205 .decl_name = try eb.printString("{s}{s}", .{ name, if (inlined) " [inlined]" else "" }), 5206 .src_loc = try eb.addSourceLocation(.{ 5207 .src_path = try eb.printString("{f}", .{src.file_scope.path.fmt(zcu.comp)}), 5208 .span_start = span.start, 5209 .span_main = span.main, 5210 .span_end = span.end, 5211 .line = @intCast(loc.line), 5212 .column = @intCast(loc.column), 5213 .source_line = 0, 5214 }), 5215 }); 5216 } 5217 5218 const TrackedUnitSema = struct { 5219 /// `null` means we created the node, so should end it. 5220 old_name: ?[std.Progress.Node.max_name_len]u8, 5221 old_analysis_timer: ?Compilation.Timer, 5222 analysis_timer_decl: ?InternPool.TrackedInst.Index, 5223 pub fn end(tus: TrackedUnitSema, zcu: *Zcu) void { 5224 const comp = zcu.comp; 5225 const io = comp.io; 5226 if (tus.old_name) |old_name| { 5227 zcu.sema_prog_node.completeOne(); // we're just renaming, but it's effectively completion 5228 zcu.cur_sema_prog_node.setName(&old_name); 5229 } else { 5230 zcu.cur_sema_prog_node.end(); 5231 zcu.cur_sema_prog_node = .none; 5232 } 5233 report_time: { 5234 const sema_ns = zcu.cur_analysis_timer.?.finish(io) orelse break :report_time; 5235 const zir_decl = tus.analysis_timer_decl orelse break :report_time; 5236 comp.mutex.lockUncancelable(io); 5237 defer comp.mutex.unlock(io); 5238 comp.time_report.?.stats.cpu_ns_sema += sema_ns; 5239 const gop = comp.time_report.?.decl_sema_info.getOrPut(comp.gpa, zir_decl) catch |err| switch (err) { 5240 error.OutOfMemory => { 5241 comp.setAllocFailure(); 5242 break :report_time; 5243 }, 5244 }; 5245 if (!gop.found_existing) gop.value_ptr.* = .{ .ns = 0, .count = 0 }; 5246 gop.value_ptr.ns += sema_ns; 5247 gop.value_ptr.count += 1; 5248 } 5249 zcu.cur_analysis_timer = tus.old_analysis_timer; 5250 if (zcu.cur_analysis_timer) |*t| t.@"resume"(io); 5251 } 5252 }; 5253 pub fn trackUnitSema(zcu: *Zcu, name: []const u8, zir_inst: ?InternPool.TrackedInst.Index) TrackedUnitSema { 5254 const comp = zcu.comp; 5255 const io = comp.io; 5256 if (zcu.cur_analysis_timer) |*t| t.pause(io); 5257 const old_analysis_timer = zcu.cur_analysis_timer; 5258 zcu.cur_analysis_timer = zcu.comp.startTimer(); 5259 const old_name: ?[std.Progress.Node.max_name_len]u8 = old_name: { 5260 if (zcu.cur_sema_prog_node.index == .none) { 5261 zcu.cur_sema_prog_node = zcu.sema_prog_node.start(name, 0); 5262 break :old_name null; 5263 } 5264 const old_name = zcu.cur_sema_prog_node.getName(); 5265 zcu.cur_sema_prog_node.setName(name); 5266 break :old_name old_name; 5267 }; 5268 return .{ 5269 .old_name = old_name, 5270 .old_analysis_timer = old_analysis_timer, 5271 .analysis_timer_decl = zir_inst, 5272 }; 5273 } 5274 5275 pub const CodegenTaskPool = struct { 5276 const CodegenResult = PerThread.RunCodegenError!codegen.AnyMir; 5277 5278 /// In the worst observed case, MIR is around 50 times as large as AIR. More typically, the ratio is 5279 /// around 20. Going by that 50x multiplier, and assuming we want to consume no more than 500 MiB of 5280 /// memory on AIR/MIR, we see a limit of around 10 MiB of AIR in-flight. 5281 const max_air_bytes_in_flight = 10 * 1024 * 1024; 5282 5283 const max_funcs_in_flight = link.Queue.buffer_size; 5284 5285 available_air_bytes: u32, 5286 5287 /// Locks the freelist and `available_air_bytes`. 5288 mutex: Io.Mutex, 5289 5290 /// Signaled when an item is added to the freelist. 5291 free_cond: Io.Condition, 5292 /// Pre-allocated with enough capacity for all indices. 5293 free: std.ArrayList(Index), 5294 5295 /// `.none` means this task is in the freelist. The `task_air_bytes` and 5296 /// `task_futures` entries are `undefined`. 5297 task_funcs: []InternPool.Index, 5298 task_air_bytes: []u32, 5299 task_futures: []Io.Future(CodegenResult), 5300 5301 pub fn init(arena: Allocator) Allocator.Error!CodegenTaskPool { 5302 const task_funcs = try arena.alloc(InternPool.Index, max_funcs_in_flight); 5303 const task_air_bytes = try arena.alloc(u32, max_funcs_in_flight); 5304 const task_futures = try arena.alloc(Io.Future(CodegenResult), max_funcs_in_flight); 5305 @memset(task_funcs, .none); 5306 5307 var free: std.ArrayList(Index) = try .initCapacity(arena, max_funcs_in_flight); 5308 for (0..max_funcs_in_flight) |index| free.appendAssumeCapacity(@fromBackingInt(@intCast(index))); 5309 5310 return .{ 5311 .available_air_bytes = max_air_bytes_in_flight, 5312 .mutex = .init, 5313 .free_cond = .init, 5314 .free = free, 5315 .task_funcs = task_funcs, 5316 .task_air_bytes = task_air_bytes, 5317 .task_futures = task_futures, 5318 }; 5319 } 5320 5321 pub fn cancel(pool: *CodegenTaskPool, zcu: *const Zcu) void { 5322 const io = zcu.comp.io; 5323 for ( 5324 pool.task_funcs, 5325 pool.task_air_bytes, 5326 pool.task_futures, 5327 ) |func, effective_air_bytes, *future| { 5328 if (func == .none) continue; 5329 pool.available_air_bytes += effective_air_bytes; 5330 var mir = future.cancel(io) catch continue; 5331 mir.deinit(zcu); 5332 } 5333 assert(pool.available_air_bytes == max_air_bytes_in_flight); 5334 zcu.updateTracyPlot("air_bytes_in_flight", 0); 5335 } 5336 5337 pub fn start( 5338 pool: *CodegenTaskPool, 5339 zcu: *Zcu, 5340 func_index: InternPool.Index, 5341 air: *Air, 5342 /// If `true`, this function will take ownership of `air`, freeing it after codegen 5343 /// completes; it is not assumed that `air` will outlive this function. If `false`, 5344 /// codegen will operate on `air` via the given pointer, which it is assumed will 5345 /// outline the codegen task. 5346 move_air: bool, 5347 ) Io.Cancelable!Index { 5348 const io = zcu.comp.io; 5349 5350 // To avoid consuming an excessive amount of memory, there is a limit on the total number of AIR 5351 // bytes which can be in the codegen/link pipeline at one time. If we exceed this limit, we must 5352 // wait for codegen/link to finish some WIP functions so they catch up with us. 5353 const actual_air_bytes: u32 = @intCast(air.instructions.len * 5 + air.extra.items.len * 4); 5354 // We need to let all AIR through eventually, even if one function exceeds `max_air_bytes_in_flight`. 5355 const effective_air_bytes: u32 = @min(actual_air_bytes, max_air_bytes_in_flight); 5356 assert(effective_air_bytes > 0); 5357 5358 const index: Index = index: { 5359 try pool.mutex.lock(io); 5360 defer pool.mutex.unlock(io); 5361 5362 while (pool.free.items.len == 0 or pool.available_air_bytes < effective_air_bytes) { 5363 // The linker thread needs to catch up! 5364 try pool.free_cond.wait(io, &pool.mutex); 5365 } 5366 5367 pool.available_air_bytes -= effective_air_bytes; 5368 5369 zcu.updateTracyPlot("air_bytes_in_flight", @max( 5370 max_air_bytes_in_flight - pool.available_air_bytes, 5371 actual_air_bytes, 5372 )); 5373 5374 break :index pool.free.pop().?; 5375 }; 5376 5377 // No turning back now: we're incrementing `pending_codegen_jobs` and starting the worker. 5378 errdefer comptime unreachable; 5379 5380 assert(zcu.pending_codegen_jobs.fetchAdd(1, .monotonic) > 0); // the "Code Generation" node is still active 5381 assert(pool.task_funcs[@backingInt(index)] == .none); 5382 pool.task_funcs[@backingInt(index)] = func_index; 5383 pool.task_air_bytes[@backingInt(index)] = actual_air_bytes; 5384 pool.task_futures[@backingInt(index)] = if (move_air) io.async( 5385 workerCodegenOwnedAir, 5386 .{ zcu, func_index, air.* }, 5387 ) else io.async( 5388 workerCodegenExternalAir, 5389 .{ zcu, func_index, air }, 5390 ); 5391 5392 return index; 5393 } 5394 pub const Index = enum(u32) { 5395 _, 5396 5397 /// Blocks until codegen has completed, successfully or otherwise. 5398 /// The returned MIR is owned by the caller. 5399 pub fn wait( 5400 index: Index, 5401 pool: *CodegenTaskPool, 5402 zcu: *const Zcu, 5403 ) PerThread.RunCodegenError!struct { InternPool.Index, codegen.AnyMir } { 5404 const io = zcu.comp.io; 5405 const func = pool.task_funcs[@backingInt(index)]; 5406 assert(func != .none); 5407 const effective_air_bytes = pool.task_air_bytes[@backingInt(index)]; 5408 const result = pool.task_futures[@backingInt(index)].await(io); 5409 5410 pool.task_funcs[@backingInt(index)] = .none; 5411 pool.task_air_bytes[@backingInt(index)] = undefined; 5412 pool.task_futures[@backingInt(index)] = undefined; 5413 5414 { 5415 pool.mutex.lockUncancelable(io); 5416 defer pool.mutex.unlock(io); 5417 pool.available_air_bytes += effective_air_bytes; 5418 pool.free.appendAssumeCapacity(index); 5419 pool.free_cond.signal(io); 5420 zcu.updateTracyPlot("air_bytes_in_flight", max_air_bytes_in_flight - pool.available_air_bytes); 5421 } 5422 5423 return .{ func, try result }; 5424 } 5425 }; 5426 fn workerCodegenOwnedAir( 5427 zcu: *Zcu, 5428 func_index: InternPool.Index, 5429 orig_air: Air, 5430 ) CodegenResult { 5431 // We own `air` now, so we are responsbile for freeing it. 5432 var air = orig_air; 5433 defer air.deinit(zcu.comp.gpa); 5434 const io = zcu.comp.io; 5435 const tid: Zcu.PerThread.Id = .acquire(io); 5436 defer tid.release(io); 5437 const active = zcu.activate(tid); 5438 defer active.deactivate(); 5439 return active.pt.runCodegen(func_index, &air); 5440 } 5441 fn workerCodegenExternalAir( 5442 zcu: *Zcu, 5443 func_index: InternPool.Index, 5444 air: *Air, 5445 ) CodegenResult { 5446 const io = zcu.comp.io; 5447 const tid: Zcu.PerThread.Id = .acquire(io); 5448 defer tid.release(io); 5449 const active = zcu.activate(tid); 5450 defer active.deactivate(); 5451 return active.pt.runCodegen(func_index, air); 5452 } 5453 }; 5454 5455 fn initTracyPlots(zcu: *const Zcu) void { 5456 if (zcu.comp.skip_linker_dependencies) return; 5457 5458 tracy.plotConfig("air_bytes_in_flight", .{ .format = .memory, .mode = .step }); 5459 5460 tracy.plotConfig("outdated + potentially_outdated", .{ .format = .number, .mode = .step, .color = 0xFFFF00 }); 5461 tracy.plotConfig("outdated", .{ .format = .number, .mode = .step, .color = 0xFF0000 }); 5462 tracy.plotConfig("potentially_outdated", .{ .format = .number, .mode = .step, .color = 0xFF7700 }); 5463 tracy.plotConfig("outdated_ready", .{ .format = .number, .mode = .step, .color = 0x00FF00 }); 5464 } 5465 5466 /// Marked `inline` to prevent binary bloat from trivial generic instances, and to ensure there is 5467 /// minimal overhead to this call when Tracy is disabled, even in Debug builds. 5468 inline fn updateTracyPlot(zcu: *const Zcu, comptime name: [*:0]const u8, val: u64) void { 5469 if (zcu.comp.skip_linker_dependencies) return; 5470 tracy.plotInt(name, @intCast(val)); 5471 } 5472 5473 /// Assumes that `zcu.outdated_lock` is already held. 5474 fn updateTracyOutdatedPlots(zcu: *const Zcu) void { 5475 zcu.updateTracyPlot("outdated + potentially_outdated", zcu.outdated.count() + zcu.potentially_outdated.count()); 5476 zcu.updateTracyPlot("outdated", zcu.outdated.count()); 5477 zcu.updateTracyPlot("potentially_outdated", zcu.potentially_outdated.count()); 5478 zcu.updateTracyPlot("outdated_ready", zcu.outdated_ready.funcs.count() + zcu.outdated_ready.other.count()); 5479 } 5480 5481 pub const Active = struct { 5482 pt: Zcu.PerThread, 5483 ip: InternPool.Active, 5484 pub fn deactivate(active: Active) void { 5485 active.ip.deactivate(); 5486 } 5487 pub fn release(active: Active) void { 5488 active.deactivate(); 5489 active.pt.tid.release(active.pt.zcu.comp.io); 5490 } 5491 }; 5492 pub fn activate(zcu: *Zcu, tid: PerThread.Id) Active { 5493 return .{ 5494 .pt = .{ .zcu = zcu, .tid = tid }, 5495 .ip = zcu.intern_pool.activate(), 5496 }; 5497 } 5498 pub fn acquire(zcu: *Zcu) Active { 5499 return zcu.activate(.acquire(zcu.comp.io)); 5500 } 5501