mirror of
https://github.com/ziglang/zig.git
synced 2026-02-13 12:59:04 +00:00
Merge pull request #22754 from mlugg/files-and-stuff
ZON and incremental bits
This commit is contained in:
commit
f01f1e33c9
@ -10,6 +10,31 @@ string_bytes: []u8,
|
||||
compile_errors: []Zoir.CompileError,
|
||||
error_notes: []Zoir.CompileError.Note,
|
||||
|
||||
/// The data stored at byte offset 0 when ZOIR is stored in a file.
|
||||
pub const Header = extern struct {
|
||||
nodes_len: u32,
|
||||
extra_len: u32,
|
||||
limbs_len: u32,
|
||||
string_bytes_len: u32,
|
||||
compile_errors_len: u32,
|
||||
error_notes_len: u32,
|
||||
|
||||
/// We could leave this as padding, however it triggers a Valgrind warning because
|
||||
/// we read and write undefined bytes to the file system. This is harmless, but
|
||||
/// it's essentially free to have a zero field here and makes the warning go away,
|
||||
/// making it more likely that following Valgrind warnings will be taken seriously.
|
||||
unused: u64 = 0,
|
||||
|
||||
stat_inode: std.fs.File.INode,
|
||||
stat_size: u64,
|
||||
stat_mtime: i128,
|
||||
|
||||
comptime {
|
||||
// Check that `unused` is working as expected
|
||||
assert(std.meta.hasUniqueRepresentation(Header));
|
||||
}
|
||||
};
|
||||
|
||||
pub fn hasCompileErrors(zoir: Zoir) bool {
|
||||
if (zoir.compile_errors.len > 0) {
|
||||
assert(zoir.nodes.len == 0);
|
||||
|
||||
@ -264,14 +264,12 @@ pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void {
|
||||
}
|
||||
|
||||
pub fn populateFile(comp: *Compilation, mod: *Module, file: *File) !void {
|
||||
assert(file.source_loaded == true);
|
||||
|
||||
if (mod.root.statFile(mod.root_src_path)) |stat| {
|
||||
if (stat.size != file.source.len) {
|
||||
if (stat.size != file.source.?.len) {
|
||||
std.log.warn(
|
||||
"the cached file '{}{s}' had the wrong size. Expected {d}, found {d}. " ++
|
||||
"Overwriting with correct file contents now",
|
||||
.{ mod.root, mod.root_src_path, file.source.len, stat.size },
|
||||
.{ mod.root, mod.root_src_path, file.source.?.len, stat.size },
|
||||
);
|
||||
|
||||
try writeFile(file, mod);
|
||||
@ -296,15 +294,13 @@ pub fn populateFile(comp: *Compilation, mod: *Module, file: *File) !void {
|
||||
|
||||
log.debug("parsing and generating '{s}'", .{mod.root_src_path});
|
||||
|
||||
file.tree = try std.zig.Ast.parse(comp.gpa, file.source, .zig);
|
||||
assert(file.tree.errors.len == 0); // builtin.zig must parse
|
||||
file.tree_loaded = true;
|
||||
file.tree = try std.zig.Ast.parse(comp.gpa, file.source.?, .zig);
|
||||
assert(file.tree.?.errors.len == 0); // builtin.zig must parse
|
||||
|
||||
file.zir = try AstGen.generate(comp.gpa, file.tree);
|
||||
assert(!file.zir.hasCompileErrors()); // builtin.zig must not have astgen errors
|
||||
file.zir_loaded = true;
|
||||
file.status = .success_zir;
|
||||
// Note that whilst we set `zir_loaded` here, we populated `path_digest`
|
||||
file.zir = try AstGen.generate(comp.gpa, file.tree.?);
|
||||
assert(!file.zir.?.hasCompileErrors()); // builtin.zig must not have astgen errors
|
||||
file.status = .success;
|
||||
// Note that whilst we set `zir` here, we populated `path_digest`
|
||||
// all the way back in `Package.Module.create`.
|
||||
}
|
||||
|
||||
@ -312,7 +308,7 @@ fn writeFile(file: *File, mod: *Module) !void {
|
||||
var buf: [std.fs.max_path_bytes]u8 = undefined;
|
||||
var af = try mod.root.atomicFile(mod.root_src_path, .{ .make_path = true }, &buf);
|
||||
defer af.deinit();
|
||||
try af.file.writeAll(file.source);
|
||||
try af.file.writeAll(file.source.?);
|
||||
af.finish() catch |err| switch (err) {
|
||||
error.AccessDenied => switch (builtin.os.tag) {
|
||||
.windows => {
|
||||
@ -326,7 +322,7 @@ fn writeFile(file: *File, mod: *Module) !void {
|
||||
};
|
||||
|
||||
file.stat = .{
|
||||
.size = file.source.len,
|
||||
.size = file.source.?.len,
|
||||
.inode = 0, // dummy value
|
||||
.mtime = 0, // dummy value
|
||||
};
|
||||
|
||||
@ -2220,10 +2220,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
|
||||
try comp.astgen_work_queue.ensureUnusedCapacity(zcu.import_table.count());
|
||||
for (zcu.import_table.values()) |file_index| {
|
||||
if (zcu.fileByIndex(file_index).mod.isBuiltin()) continue;
|
||||
const file = zcu.fileByIndex(file_index);
|
||||
if (file.getMode() == .zig) {
|
||||
comp.astgen_work_queue.writeItemAssumeCapacity(file_index);
|
||||
}
|
||||
comp.astgen_work_queue.writeItemAssumeCapacity(file_index);
|
||||
}
|
||||
if (comp.file_system_inputs) |fsi| {
|
||||
for (zcu.import_table.values()) |file_index| {
|
||||
@ -2906,10 +2903,12 @@ pub fn makeBinFileWritable(comp: *Compilation) !void {
|
||||
const Header = extern struct {
|
||||
intern_pool: extern struct {
|
||||
thread_count: u32,
|
||||
file_deps_len: u32,
|
||||
src_hash_deps_len: u32,
|
||||
nav_val_deps_len: u32,
|
||||
nav_ty_deps_len: u32,
|
||||
interned_deps_len: u32,
|
||||
zon_file_deps_len: u32,
|
||||
embed_file_deps_len: u32,
|
||||
namespace_deps_len: u32,
|
||||
namespace_name_deps_len: u32,
|
||||
first_dependency_len: u32,
|
||||
@ -2950,10 +2949,12 @@ pub fn saveState(comp: *Compilation) !void {
|
||||
const header: Header = .{
|
||||
.intern_pool = .{
|
||||
.thread_count = @intCast(ip.locals.len),
|
||||
.file_deps_len = @intCast(ip.file_deps.count()),
|
||||
.src_hash_deps_len = @intCast(ip.src_hash_deps.count()),
|
||||
.nav_val_deps_len = @intCast(ip.nav_val_deps.count()),
|
||||
.nav_ty_deps_len = @intCast(ip.nav_ty_deps.count()),
|
||||
.interned_deps_len = @intCast(ip.interned_deps.count()),
|
||||
.zon_file_deps_len = @intCast(ip.zon_file_deps.count()),
|
||||
.embed_file_deps_len = @intCast(ip.embed_file_deps.count()),
|
||||
.namespace_deps_len = @intCast(ip.namespace_deps.count()),
|
||||
.namespace_name_deps_len = @intCast(ip.namespace_name_deps.count()),
|
||||
.first_dependency_len = @intCast(ip.first_dependency.count()),
|
||||
@ -2978,14 +2979,18 @@ pub fn saveState(comp: *Compilation) !void {
|
||||
addBuf(&bufs, mem.asBytes(&header));
|
||||
addBuf(&bufs, mem.sliceAsBytes(pt_headers.items));
|
||||
|
||||
addBuf(&bufs, mem.sliceAsBytes(ip.file_deps.keys()));
|
||||
addBuf(&bufs, mem.sliceAsBytes(ip.file_deps.values()));
|
||||
addBuf(&bufs, mem.sliceAsBytes(ip.src_hash_deps.keys()));
|
||||
addBuf(&bufs, mem.sliceAsBytes(ip.src_hash_deps.values()));
|
||||
addBuf(&bufs, mem.sliceAsBytes(ip.nav_val_deps.keys()));
|
||||
addBuf(&bufs, mem.sliceAsBytes(ip.nav_val_deps.values()));
|
||||
addBuf(&bufs, mem.sliceAsBytes(ip.nav_ty_deps.keys()));
|
||||
addBuf(&bufs, mem.sliceAsBytes(ip.nav_ty_deps.values()));
|
||||
addBuf(&bufs, mem.sliceAsBytes(ip.interned_deps.keys()));
|
||||
addBuf(&bufs, mem.sliceAsBytes(ip.interned_deps.values()));
|
||||
addBuf(&bufs, mem.sliceAsBytes(ip.zon_file_deps.keys()));
|
||||
addBuf(&bufs, mem.sliceAsBytes(ip.zon_file_deps.values()));
|
||||
addBuf(&bufs, mem.sliceAsBytes(ip.embed_file_deps.keys()));
|
||||
addBuf(&bufs, mem.sliceAsBytes(ip.embed_file_deps.values()));
|
||||
addBuf(&bufs, mem.sliceAsBytes(ip.namespace_deps.keys()));
|
||||
addBuf(&bufs, mem.sliceAsBytes(ip.namespace_deps.values()));
|
||||
addBuf(&bufs, mem.sliceAsBytes(ip.namespace_name_deps.keys()));
|
||||
@ -3203,15 +3208,13 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
|
||||
}
|
||||
|
||||
if (comp.zcu) |zcu| {
|
||||
const ip = &zcu.intern_pool;
|
||||
|
||||
for (zcu.failed_files.keys(), zcu.failed_files.values()) |file, error_msg| {
|
||||
if (error_msg) |msg| {
|
||||
try addModuleErrorMsg(zcu, &bundle, msg.*);
|
||||
} else {
|
||||
// Must be ZIR or Zoir errors. Note that this may include AST errors.
|
||||
_ = try file.getTree(gpa); // Tree must be loaded.
|
||||
if (file.zir_loaded) {
|
||||
if (file.zir != null) {
|
||||
try addZirErrorMessages(&bundle, file);
|
||||
} else if (file.zoir != null) {
|
||||
try addZoirErrorMessages(&bundle, file);
|
||||
@ -3277,20 +3280,6 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
|
||||
if (!refs.contains(anal_unit)) continue;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
std.log.scoped(.zcu).debug("analysis error '{s}' reported from unit '{}'", .{
|
||||
error_msg.msg,
|
||||
zcu.fmtAnalUnit(anal_unit),
|
||||
@ -3318,12 +3307,10 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
|
||||
}
|
||||
}
|
||||
}
|
||||
for (zcu.failed_codegen.keys(), zcu.failed_codegen.values()) |nav, error_msg| {
|
||||
if (!zcu.navFileScope(nav).okToReportErrors()) continue;
|
||||
for (zcu.failed_codegen.values()) |error_msg| {
|
||||
try addModuleErrorMsg(zcu, &bundle, error_msg.*);
|
||||
}
|
||||
for (zcu.failed_types.keys(), zcu.failed_types.values()) |ty_index, error_msg| {
|
||||
if (!zcu.typeFileScope(ty_index).okToReportErrors()) continue;
|
||||
for (zcu.failed_types.values()) |error_msg| {
|
||||
try addModuleErrorMsg(zcu, &bundle, error_msg.*);
|
||||
}
|
||||
for (zcu.failed_exports.values()) |value| {
|
||||
@ -3623,22 +3610,17 @@ pub fn addModuleErrorMsg(
|
||||
}
|
||||
|
||||
pub fn addZirErrorMessages(eb: *ErrorBundle.Wip, file: *Zcu.File) !void {
|
||||
assert(file.zir_loaded);
|
||||
assert(file.tree_loaded);
|
||||
assert(file.source_loaded);
|
||||
const gpa = eb.gpa;
|
||||
const src_path = try file.fullPath(gpa);
|
||||
defer gpa.free(src_path);
|
||||
return eb.addZirErrorMessages(file.zir, file.tree, file.source, src_path);
|
||||
return eb.addZirErrorMessages(file.zir.?, file.tree.?, file.source.?, src_path);
|
||||
}
|
||||
|
||||
pub fn addZoirErrorMessages(eb: *ErrorBundle.Wip, file: *Zcu.File) !void {
|
||||
assert(file.source_loaded);
|
||||
assert(file.tree_loaded);
|
||||
const gpa = eb.gpa;
|
||||
const src_path = try file.fullPath(gpa);
|
||||
defer gpa.free(src_path);
|
||||
return eb.addZoirErrorMessages(file.zoir.?, file.tree, file.source, src_path);
|
||||
return eb.addZoirErrorMessages(file.zoir.?, file.tree.?, file.source.?, src_path);
|
||||
}
|
||||
|
||||
pub fn performAllTheWork(
|
||||
@ -3802,7 +3784,7 @@ fn performAllTheWorkInner(
|
||||
// will be needed by the worker threads.
|
||||
const path_digest = zcu.filePathDigest(file_index);
|
||||
const file = zcu.fileByIndex(file_index);
|
||||
comp.thread_pool.spawnWgId(&astgen_wait_group, workerAstGenFile, .{
|
||||
comp.thread_pool.spawnWgId(&astgen_wait_group, workerUpdateFile, .{
|
||||
comp, file, file_index, path_digest, zir_prog_node, &astgen_wait_group, .root,
|
||||
});
|
||||
}
|
||||
@ -3810,7 +3792,7 @@ fn performAllTheWorkInner(
|
||||
|
||||
for (0.., zcu.embed_table.values()) |ef_index_usize, ef| {
|
||||
const ef_index: Zcu.EmbedFile.Index = @enumFromInt(ef_index_usize);
|
||||
comp.thread_pool.spawnWgId(&astgen_wait_group, workerCheckEmbedFile, .{
|
||||
comp.thread_pool.spawnWgId(&astgen_wait_group, workerUpdateEmbedFile, .{
|
||||
comp, ef_index, ef,
|
||||
});
|
||||
}
|
||||
@ -3832,12 +3814,64 @@ fn performAllTheWorkInner(
|
||||
if (comp.zcu) |zcu| {
|
||||
const pt: Zcu.PerThread = .activate(zcu, .main);
|
||||
defer pt.deactivate();
|
||||
|
||||
// If the cache mode is `whole`, then add every source file to the cache manifest.
|
||||
switch (comp.cache_use) {
|
||||
.whole => |whole| if (whole.cache_manifest) |man| {
|
||||
const gpa = zcu.gpa;
|
||||
for (zcu.import_table.values()) |file_index| {
|
||||
const file = zcu.fileByIndex(file_index);
|
||||
const source = file.getSource(gpa) catch |err| {
|
||||
try pt.reportRetryableFileError(file_index, "unable to load source: {s}", .{@errorName(err)});
|
||||
continue;
|
||||
};
|
||||
const resolved_path = try std.fs.path.resolve(gpa, &.{
|
||||
file.mod.root.root_dir.path orelse ".",
|
||||
file.mod.root.sub_path,
|
||||
file.sub_file_path,
|
||||
});
|
||||
errdefer gpa.free(resolved_path);
|
||||
whole.cache_manifest_mutex.lock();
|
||||
defer whole.cache_manifest_mutex.unlock();
|
||||
man.addFilePostContents(resolved_path, source.bytes, source.stat) catch |err| switch (err) {
|
||||
error.OutOfMemory => |e| return e,
|
||||
else => {
|
||||
try pt.reportRetryableFileError(file_index, "unable to update cache: {s}", .{@errorName(err)});
|
||||
continue;
|
||||
},
|
||||
};
|
||||
}
|
||||
},
|
||||
.incremental => {},
|
||||
}
|
||||
|
||||
try reportMultiModuleErrors(pt);
|
||||
|
||||
const any_fatal_files = for (zcu.import_table.values()) |file_index| {
|
||||
const file = zcu.fileByIndex(file_index);
|
||||
switch (file.status) {
|
||||
.never_loaded => unreachable, // everything is loaded by the workers
|
||||
.retryable_failure, .astgen_failure => break true,
|
||||
.success => {},
|
||||
}
|
||||
} else false;
|
||||
|
||||
if (any_fatal_files or comp.alloc_failure_occurred) {
|
||||
// We give up right now! No updating of ZIR refs, no nothing. The idea is that this prevents
|
||||
// us from invalidating lots of incremental dependencies due to files with e.g. parse errors.
|
||||
// However, this means our analysis data is invalid, so we want to omit all analysis errors.
|
||||
// To do that, let's just clear the analysis roots!
|
||||
|
||||
assert(zcu.failed_files.count() > 0); // we will get an error
|
||||
zcu.analysis_roots.clear(); // no analysis happened
|
||||
return;
|
||||
}
|
||||
|
||||
if (comp.incremental) {
|
||||
const update_zir_refs_node = main_progress_node.start("Update ZIR References", 0);
|
||||
defer update_zir_refs_node.end();
|
||||
try pt.updateZirRefs();
|
||||
}
|
||||
try reportMultiModuleErrors(pt);
|
||||
try zcu.flushRetryableFailures();
|
||||
|
||||
zcu.sema_prog_node = main_progress_node.start("Semantic Analysis", 0);
|
||||
@ -4280,7 +4314,7 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) anye
|
||||
};
|
||||
}
|
||||
|
||||
fn workerAstGenFile(
|
||||
fn workerUpdateFile(
|
||||
tid: usize,
|
||||
comp: *Compilation,
|
||||
file: *Zcu.File,
|
||||
@ -4290,40 +4324,44 @@ fn workerAstGenFile(
|
||||
wg: *WaitGroup,
|
||||
src: Zcu.AstGenSrc,
|
||||
) void {
|
||||
assert(file.getMode() == .zig);
|
||||
const child_prog_node = prog_node.start(file.sub_file_path, 0);
|
||||
defer child_prog_node.end();
|
||||
|
||||
const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid));
|
||||
defer pt.deactivate();
|
||||
pt.astGenFile(file, path_digest) catch |err| switch (err) {
|
||||
pt.updateFile(file, path_digest) catch |err| switch (err) {
|
||||
error.AnalysisFail => return,
|
||||
else => {
|
||||
file.status = .retryable_failure;
|
||||
pt.reportRetryableAstGenError(src, file_index, err) catch |oom| switch (oom) {
|
||||
// Swallowing this error is OK because it's implied to be OOM when
|
||||
// there is a missing `failed_files` error message.
|
||||
error.OutOfMemory => {},
|
||||
error.OutOfMemory => {
|
||||
comp.mutex.lock();
|
||||
defer comp.mutex.unlock();
|
||||
comp.setAllocFailure();
|
||||
},
|
||||
};
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
switch (file.getMode()) {
|
||||
.zig => {}, // continue to logic below
|
||||
.zon => return, // ZON can't import anything so we're done
|
||||
}
|
||||
|
||||
// Pre-emptively look for `@import` paths and queue them up.
|
||||
// If we experience an error preemptively fetching the
|
||||
// file, just ignore it and let it happen again later during Sema.
|
||||
assert(file.zir_loaded);
|
||||
const imports_index = file.zir.extra[@intFromEnum(Zir.ExtraIndex.imports)];
|
||||
const imports_index = file.zir.?.extra[@intFromEnum(Zir.ExtraIndex.imports)];
|
||||
if (imports_index != 0) {
|
||||
const extra = file.zir.extraData(Zir.Inst.Imports, imports_index);
|
||||
const extra = file.zir.?.extraData(Zir.Inst.Imports, imports_index);
|
||||
var import_i: u32 = 0;
|
||||
var extra_index = extra.end;
|
||||
|
||||
while (import_i < extra.data.imports_len) : (import_i += 1) {
|
||||
const item = file.zir.extraData(Zir.Inst.Imports.Item, extra_index);
|
||||
const item = file.zir.?.extraData(Zir.Inst.Imports.Item, extra_index);
|
||||
extra_index = item.end;
|
||||
|
||||
const import_path = file.zir.nullTerminatedString(item.data.name);
|
||||
const import_path = file.zir.?.nullTerminatedString(item.data.name);
|
||||
// `@import("builtin")` is handled specially.
|
||||
if (mem.eql(u8, import_path, "builtin")) continue;
|
||||
|
||||
@ -4344,7 +4382,7 @@ fn workerAstGenFile(
|
||||
const imported_path_digest = pt.zcu.filePathDigest(res.file_index);
|
||||
break :blk .{ res, imported_path_digest };
|
||||
};
|
||||
if (import_result.is_new and import_result.file.getMode() == .zig) {
|
||||
if (import_result.is_new) {
|
||||
log.debug("AstGen of {s} has import '{s}'; queuing AstGen of {s}", .{
|
||||
file.sub_file_path, import_path, import_result.file.sub_file_path,
|
||||
});
|
||||
@ -4352,7 +4390,7 @@ fn workerAstGenFile(
|
||||
.importing_file = file_index,
|
||||
.import_tok = item.data.token,
|
||||
} };
|
||||
comp.thread_pool.spawnWgId(wg, workerAstGenFile, .{
|
||||
comp.thread_pool.spawnWgId(wg, workerUpdateFile, .{
|
||||
comp, import_result.file, import_result.file_index, imported_path_digest, prog_node, wg, sub_src,
|
||||
});
|
||||
}
|
||||
@ -4375,7 +4413,7 @@ fn workerUpdateBuiltinZigFile(
|
||||
};
|
||||
}
|
||||
|
||||
fn workerCheckEmbedFile(tid: usize, comp: *Compilation, ef_index: Zcu.EmbedFile.Index, ef: *Zcu.EmbedFile) void {
|
||||
fn workerUpdateEmbedFile(tid: usize, comp: *Compilation, ef_index: Zcu.EmbedFile.Index, ef: *Zcu.EmbedFile) void {
|
||||
comp.detectEmbedFileUpdate(@enumFromInt(tid), ef_index, ef) catch |err| switch (err) {
|
||||
error.OutOfMemory => {
|
||||
comp.mutex.lock();
|
||||
|
||||
@ -17,13 +17,6 @@ tid_shift_31: if (single_threaded) u0 else std.math.Log2Int(u32),
|
||||
/// Cached shift amount to put a `tid` in the top bits of a 32-bit value.
|
||||
tid_shift_32: if (single_threaded) u0 else std.math.Log2Int(u32),
|
||||
|
||||
/// Dependencies on whether an entire file gets past AstGen.
|
||||
/// These are triggered by `@import`, so that:
|
||||
/// * if a file initially fails AstGen, triggering a transitive failure, when a future update
|
||||
/// causes it to succeed AstGen, the `@import` is re-analyzed, allowing analysis to proceed
|
||||
/// * if a file initially succeds AstGen, but a future update causes the file to fail it,
|
||||
/// the `@import` is re-analyzed, registering a transitive failure
|
||||
file_deps: std.AutoArrayHashMapUnmanaged(FileIndex, DepEntry.Index),
|
||||
/// Dependencies on the source code hash associated with a ZIR instruction.
|
||||
/// * For a `declaration`, this is the entire declaration body.
|
||||
/// * For a `struct_decl`, `union_decl`, etc, this is the source of the fields (but not declarations).
|
||||
@ -42,6 +35,9 @@ nav_ty_deps: std.AutoArrayHashMapUnmanaged(Nav.Index, DepEntry.Index),
|
||||
/// * a container type requiring resolution (invalidated when the type must be recreated at a new index)
|
||||
/// Value is index into `dep_entries` of the first dependency on this interned value.
|
||||
interned_deps: std.AutoArrayHashMapUnmanaged(Index, DepEntry.Index),
|
||||
/// Dependencies on a ZON file. Triggered by `@import` of ZON.
|
||||
/// Value is index into `dep_entries` of the first dependency on this ZON file.
|
||||
zon_file_deps: std.AutoArrayHashMapUnmanaged(FileIndex, DepEntry.Index),
|
||||
/// Dependencies on an embedded file.
|
||||
/// Introduced by `@embedFile`; invalidated when the file changes.
|
||||
/// Value is index into `dep_entries` of the first dependency on this `Zcu.EmbedFile`.
|
||||
@ -89,11 +85,11 @@ pub const empty: InternPool = .{
|
||||
.tid_shift_30 = if (single_threaded) 0 else 31,
|
||||
.tid_shift_31 = if (single_threaded) 0 else 31,
|
||||
.tid_shift_32 = if (single_threaded) 0 else 31,
|
||||
.file_deps = .empty,
|
||||
.src_hash_deps = .empty,
|
||||
.nav_val_deps = .empty,
|
||||
.nav_ty_deps = .empty,
|
||||
.interned_deps = .empty,
|
||||
.zon_file_deps = .empty,
|
||||
.embed_file_deps = .empty,
|
||||
.namespace_deps = .empty,
|
||||
.namespace_name_deps = .empty,
|
||||
@ -824,11 +820,11 @@ pub const Nav = struct {
|
||||
};
|
||||
|
||||
pub const Dependee = union(enum) {
|
||||
file: FileIndex,
|
||||
src_hash: TrackedInst.Index,
|
||||
nav_val: Nav.Index,
|
||||
nav_ty: Nav.Index,
|
||||
interned: Index,
|
||||
zon_file: FileIndex,
|
||||
embed_file: Zcu.EmbedFile.Index,
|
||||
namespace: TrackedInst.Index,
|
||||
namespace_name: NamespaceNameKey,
|
||||
@ -876,11 +872,11 @@ pub const DependencyIterator = struct {
|
||||
|
||||
pub fn dependencyIterator(ip: *const InternPool, dependee: Dependee) DependencyIterator {
|
||||
const first_entry = switch (dependee) {
|
||||
.file => |x| ip.file_deps.get(x),
|
||||
.src_hash => |x| ip.src_hash_deps.get(x),
|
||||
.nav_val => |x| ip.nav_val_deps.get(x),
|
||||
.nav_ty => |x| ip.nav_ty_deps.get(x),
|
||||
.interned => |x| ip.interned_deps.get(x),
|
||||
.zon_file => |x| ip.zon_file_deps.get(x),
|
||||
.embed_file => |x| ip.embed_file_deps.get(x),
|
||||
.namespace => |x| ip.namespace_deps.get(x),
|
||||
.namespace_name => |x| ip.namespace_name_deps.get(x),
|
||||
@ -947,11 +943,11 @@ pub fn addDependency(ip: *InternPool, gpa: Allocator, depender: AnalUnit, depend
|
||||
},
|
||||
inline else => |dependee_payload, tag| new_index: {
|
||||
const gop = try switch (tag) {
|
||||
.file => ip.file_deps,
|
||||
.src_hash => ip.src_hash_deps,
|
||||
.nav_val => ip.nav_val_deps,
|
||||
.nav_ty => ip.nav_ty_deps,
|
||||
.interned => ip.interned_deps,
|
||||
.zon_file => ip.zon_file_deps,
|
||||
.embed_file => ip.embed_file_deps,
|
||||
.namespace => ip.namespace_deps,
|
||||
.namespace_name => ip.namespace_name_deps,
|
||||
@ -6688,11 +6684,11 @@ pub fn init(ip: *InternPool, gpa: Allocator, available_threads: usize) !void {
|
||||
pub fn deinit(ip: *InternPool, gpa: Allocator) void {
|
||||
if (debug_state.enable_checks) std.debug.assert(debug_state.intern_pool == null);
|
||||
|
||||
ip.file_deps.deinit(gpa);
|
||||
ip.src_hash_deps.deinit(gpa);
|
||||
ip.nav_val_deps.deinit(gpa);
|
||||
ip.nav_ty_deps.deinit(gpa);
|
||||
ip.interned_deps.deinit(gpa);
|
||||
ip.zon_file_deps.deinit(gpa);
|
||||
ip.embed_file_deps.deinit(gpa);
|
||||
ip.namespace_deps.deinit(gpa);
|
||||
ip.namespace_name_deps.deinit(gpa);
|
||||
|
||||
@ -482,15 +482,12 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {
|
||||
};
|
||||
new_file.* = .{
|
||||
.sub_file_path = "builtin.zig",
|
||||
.source = generated_builtin_source,
|
||||
.source_loaded = true,
|
||||
.tree_loaded = false,
|
||||
.zir_loaded = false,
|
||||
.stat = undefined,
|
||||
.tree = undefined,
|
||||
.zir = undefined,
|
||||
.source = generated_builtin_source,
|
||||
.tree = null,
|
||||
.zir = null,
|
||||
.zoir = null,
|
||||
.status = .never_loaded,
|
||||
.prev_status = .never_loaded,
|
||||
.mod = new,
|
||||
};
|
||||
break :b new;
|
||||
|
||||
24
src/Sema.zig
24
src/Sema.zig
@ -6140,10 +6140,9 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr
|
||||
return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)});
|
||||
|
||||
const path_digest = zcu.filePathDigest(result.file_index);
|
||||
pt.astGenFile(result.file, path_digest) catch |err|
|
||||
pt.updateFile(result.file, path_digest) catch |err|
|
||||
return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)});
|
||||
|
||||
try sema.declareDependency(.{ .file = result.file_index });
|
||||
try pt.ensureFileAnalyzed(result.file_index);
|
||||
const ty = zcu.fileRootType(result.file_index);
|
||||
try sema.declareDependency(.{ .interned = ty });
|
||||
@ -7649,9 +7648,8 @@ fn analyzeCall(
|
||||
const nav = ip.getNav(info.owner_nav);
|
||||
const resolved_func_inst = info.zir_body_inst.resolveFull(ip) orelse return error.AnalysisFail;
|
||||
const file = zcu.fileByIndex(resolved_func_inst.file);
|
||||
assert(file.zir_loaded);
|
||||
const zir_info = file.zir.getFnInfo(resolved_func_inst.inst);
|
||||
break :b .{ nav, file.zir, info.zir_body_inst, resolved_func_inst.inst, zir_info };
|
||||
const zir_info = file.zir.?.getFnInfo(resolved_func_inst.inst);
|
||||
break :b .{ nav, file.zir.?, info.zir_body_inst, resolved_func_inst.inst, zir_info };
|
||||
} else .{ undefined, undefined, undefined, undefined, undefined };
|
||||
|
||||
// This is the `inst_map` used when evaluating generic parameters and return types.
|
||||
@ -13987,7 +13985,6 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
|
||||
};
|
||||
switch (result.file.getMode()) {
|
||||
.zig => {
|
||||
try sema.declareDependency(.{ .file = result.file_index });
|
||||
try pt.ensureFileAnalyzed(result.file_index);
|
||||
const ty = zcu.fileRootType(result.file_index);
|
||||
try sema.declareDependency(.{ .interned = ty });
|
||||
@ -13995,12 +13992,6 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
|
||||
return Air.internedToRef(ty);
|
||||
},
|
||||
.zon => {
|
||||
_ = result.file.getTree(zcu.gpa) catch |err| {
|
||||
// TODO: these errors are file system errors; make sure an update() will
|
||||
// retry this and not cache the file system error, which may be transient.
|
||||
return sema.fail(block, operand_src, "unable to open '{s}': {s}", .{ result.file.sub_file_path, @errorName(err) });
|
||||
};
|
||||
|
||||
if (extra.res_ty == .none) {
|
||||
return sema.fail(block, operand_src, "'@import' of ZON must have a known result type", .{});
|
||||
}
|
||||
@ -14010,6 +14001,7 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
|
||||
return sema.fail(block, operand_src, "'@import' of ZON must have a known result type", .{});
|
||||
}
|
||||
|
||||
try sema.declareDependency(.{ .zon_file = result.file_index });
|
||||
const interned = try LowerZon.run(
|
||||
sema,
|
||||
result.file,
|
||||
@ -35328,7 +35320,7 @@ fn backingIntType(
|
||||
break :blk accumulator;
|
||||
};
|
||||
|
||||
const zir = zcu.namespacePtr(struct_type.namespace).fileScope(zcu).zir;
|
||||
const zir = zcu.namespacePtr(struct_type.namespace).fileScope(zcu).zir.?;
|
||||
const zir_index = struct_type.zir_index.resolve(ip) orelse return error.AnalysisFail;
|
||||
const extended = zir.instructions.items(.data)[@intFromEnum(zir_index)].extended;
|
||||
assert(extended.opcode == .struct_decl);
|
||||
@ -35948,7 +35940,7 @@ fn structFields(
|
||||
const gpa = zcu.gpa;
|
||||
const ip = &zcu.intern_pool;
|
||||
const namespace_index = struct_type.namespace;
|
||||
const zir = zcu.namespacePtr(namespace_index).fileScope(zcu).zir;
|
||||
const zir = zcu.namespacePtr(namespace_index).fileScope(zcu).zir.?;
|
||||
const zir_index = struct_type.zir_index.resolve(ip) orelse return error.AnalysisFail;
|
||||
|
||||
const fields_len, _, var extra_index = structZirInfo(zir, zir_index);
|
||||
@ -36149,7 +36141,7 @@ fn structFieldInits(
|
||||
assert(!struct_type.haveFieldInits(ip));
|
||||
|
||||
const namespace_index = struct_type.namespace;
|
||||
const zir = zcu.namespacePtr(namespace_index).fileScope(zcu).zir;
|
||||
const zir = zcu.namespacePtr(namespace_index).fileScope(zcu).zir.?;
|
||||
const zir_index = struct_type.zir_index.resolve(ip) orelse return error.AnalysisFail;
|
||||
const fields_len, _, var extra_index = structZirInfo(zir, zir_index);
|
||||
|
||||
@ -36268,7 +36260,7 @@ fn unionFields(
|
||||
const zcu = pt.zcu;
|
||||
const gpa = zcu.gpa;
|
||||
const ip = &zcu.intern_pool;
|
||||
const zir = zcu.namespacePtr(union_type.namespace).fileScope(zcu).zir;
|
||||
const zir = zcu.namespacePtr(union_type.namespace).fileScope(zcu).zir.?;
|
||||
const zir_index = union_type.zir_index.resolve(ip) orelse return error.AnalysisFail;
|
||||
const extended = zir.instructions.items(.data)[@intFromEnum(zir_index)].extended;
|
||||
assert(extended.opcode == .union_decl);
|
||||
|
||||
@ -39,8 +39,6 @@ pub fn run(
|
||||
) CompileError!InternPool.Index {
|
||||
const pt = sema.pt;
|
||||
|
||||
_ = try file.getZoir(pt.zcu);
|
||||
|
||||
const tracked_inst = try pt.zcu.intern_pool.trackZir(pt.zcu.gpa, pt.tid, .{
|
||||
.file = file_index,
|
||||
.inst = .main_struct_inst, // this is the only trackable instruction in a ZON file
|
||||
|
||||
@ -3587,8 +3587,7 @@ pub fn typeDeclSrcLine(ty: Type, zcu: *Zcu) ?u32 {
|
||||
};
|
||||
const info = tracked.resolveFull(&zcu.intern_pool) orelse return null;
|
||||
const file = zcu.fileByIndex(info.file);
|
||||
assert(file.zir_loaded);
|
||||
const zir = file.zir;
|
||||
const zir = file.zir.?;
|
||||
const inst = zir.instructions.get(@intFromEnum(info.inst));
|
||||
return switch (inst.tag) {
|
||||
.struct_init, .struct_init_ref => zir.extraData(Zir.Inst.StructInit, inst.data.pl_node.payload_index).data.abs_line,
|
||||
@ -3905,7 +3904,7 @@ fn resolveStructInner(
|
||||
var comptime_err_ret_trace = std.ArrayList(Zcu.LazySrcLoc).init(gpa);
|
||||
defer comptime_err_ret_trace.deinit();
|
||||
|
||||
const zir = zcu.namespacePtr(struct_obj.namespace).fileScope(zcu).zir;
|
||||
const zir = zcu.namespacePtr(struct_obj.namespace).fileScope(zcu).zir.?;
|
||||
var sema: Sema = .{
|
||||
.pt = pt,
|
||||
.gpa = gpa,
|
||||
@ -3959,7 +3958,7 @@ fn resolveUnionInner(
|
||||
var comptime_err_ret_trace = std.ArrayList(Zcu.LazySrcLoc).init(gpa);
|
||||
defer comptime_err_ret_trace.deinit();
|
||||
|
||||
const zir = zcu.namespacePtr(union_obj.namespace).fileScope(zcu).zir;
|
||||
const zir = zcu.namespacePtr(union_obj.namespace).fileScope(zcu).zir.?;
|
||||
var sema: Sema = .{
|
||||
.pt = pt,
|
||||
.gpa = gpa,
|
||||
|
||||
352
src/Zcu.zig
352
src/Zcu.zig
@ -658,24 +658,35 @@ pub const Namespace = struct {
|
||||
};
|
||||
|
||||
pub const File = struct {
|
||||
status: Status,
|
||||
prev_status: Status,
|
||||
source_loaded: bool,
|
||||
tree_loaded: bool,
|
||||
zir_loaded: bool,
|
||||
/// Relative to the owning package's root source directory.
|
||||
/// Memory is stored in gpa, owned by File.
|
||||
sub_file_path: []const u8,
|
||||
/// Whether this is populated depends on `source_loaded`.
|
||||
source: [:0]const u8,
|
||||
|
||||
status: enum {
|
||||
/// We have not yet attempted to load this file.
|
||||
/// `stat` is not populated and may be `undefined`.
|
||||
never_loaded,
|
||||
/// A filesystem access failed. It should be retried on the next update.
|
||||
/// There is a `failed_files` entry containing a non-`null` message.
|
||||
/// `stat` is not populated and may be `undefined`.
|
||||
retryable_failure,
|
||||
/// Parsing/AstGen/ZonGen of this file has failed.
|
||||
/// There is an error in `zir` or `zoir`.
|
||||
/// There is a `failed_files` entry (with a `null` message).
|
||||
/// `stat` is populated.
|
||||
astgen_failure,
|
||||
/// Parsing and AstGen/ZonGen of this file has succeeded.
|
||||
/// `stat` is populated.
|
||||
success,
|
||||
},
|
||||
/// Whether this is populated depends on `status`.
|
||||
stat: Cache.File.Stat,
|
||||
/// Whether this is populated or not depends on `tree_loaded`.
|
||||
tree: Ast,
|
||||
/// Whether this is populated or not depends on `zir_loaded`.
|
||||
zir: Zir,
|
||||
/// Cached Zoir, generated lazily.
|
||||
zoir: ?Zoir = null,
|
||||
|
||||
source: ?[:0]const u8,
|
||||
tree: ?Ast,
|
||||
zir: ?Zir,
|
||||
zoir: ?Zoir,
|
||||
|
||||
/// Module that this file is a part of, managed externally.
|
||||
mod: *Package.Module,
|
||||
/// Whether this file is a part of multiple packages. This is an error condition which will be reported after AstGen.
|
||||
@ -683,19 +694,24 @@ pub const File = struct {
|
||||
/// List of references to this file, used for multi-package errors.
|
||||
references: std.ArrayListUnmanaged(File.Reference) = .empty,
|
||||
|
||||
/// The most recent successful ZIR for this file, with no errors.
|
||||
/// This is only populated when a previously successful ZIR
|
||||
/// newly introduces compile errors during an update. When ZIR is
|
||||
/// successful, this field is unloaded.
|
||||
/// The ZIR for this file from the last update with no file failures. As such, this ZIR is never
|
||||
/// failed (although it may have compile errors).
|
||||
///
|
||||
/// Because updates with file failures do not perform ZIR mapping or semantic analysis, we keep
|
||||
/// this around so we have the "old" ZIR to map when an update is ready to do so. Once such an
|
||||
/// update occurs, this field is unloaded, since it is no longer necessary.
|
||||
///
|
||||
/// In other words, if `TrackedInst`s are tied to ZIR other than what's in the `zir` field, this
|
||||
/// field is populated with that old ZIR.
|
||||
prev_zir: ?*Zir = null,
|
||||
|
||||
pub const Status = enum {
|
||||
never_loaded,
|
||||
retryable_failure,
|
||||
parse_failure,
|
||||
astgen_failure,
|
||||
success_zir,
|
||||
};
|
||||
/// This field serves a similar purpose to `prev_zir`, but for ZOIR. However, since we do not
|
||||
/// need to map old ZOIR to new ZOIR -- instead only invalidating dependencies if the ZOIR
|
||||
/// changed -- this field is just a simple boolean.
|
||||
///
|
||||
/// When `zoir` is updated, this field is set to `true`. In `updateZirRefs`, if this is `true`,
|
||||
/// we invalidate the corresponding `zon_file` dependency, and reset it to `false`.
|
||||
zoir_invalidated: bool = false,
|
||||
|
||||
/// A single reference to a file.
|
||||
pub const Reference = union(enum) {
|
||||
@ -727,23 +743,23 @@ pub const File = struct {
|
||||
}
|
||||
|
||||
pub fn unloadTree(file: *File, gpa: Allocator) void {
|
||||
if (file.tree_loaded) {
|
||||
file.tree_loaded = false;
|
||||
file.tree.deinit(gpa);
|
||||
if (file.tree) |*tree| {
|
||||
tree.deinit(gpa);
|
||||
file.tree = null;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unloadSource(file: *File, gpa: Allocator) void {
|
||||
if (file.source_loaded) {
|
||||
file.source_loaded = false;
|
||||
gpa.free(file.source);
|
||||
if (file.source) |source| {
|
||||
gpa.free(source);
|
||||
file.source = null;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unloadZir(file: *File, gpa: Allocator) void {
|
||||
if (file.zir_loaded) {
|
||||
file.zir_loaded = false;
|
||||
file.zir.deinit(gpa);
|
||||
if (file.zir) |*zir| {
|
||||
zir.deinit(gpa);
|
||||
file.zir = null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -753,8 +769,8 @@ pub const File = struct {
|
||||
};
|
||||
|
||||
pub fn getSource(file: *File, gpa: Allocator) !Source {
|
||||
if (file.source_loaded) return Source{
|
||||
.bytes = file.source,
|
||||
if (file.source) |source| return .{
|
||||
.bytes = source,
|
||||
.stat = file.stat,
|
||||
};
|
||||
|
||||
@ -769,18 +785,20 @@ pub const File = struct {
|
||||
return error.FileTooBig;
|
||||
|
||||
const source = try gpa.allocSentinel(u8, @as(usize, @intCast(stat.size)), 0);
|
||||
defer if (!file.source_loaded) gpa.free(source);
|
||||
errdefer gpa.free(source);
|
||||
|
||||
const amt = try f.readAll(source);
|
||||
if (amt != stat.size)
|
||||
return error.UnexpectedEndOfFile;
|
||||
|
||||
// Here we do not modify stat fields because this function is the one
|
||||
// used for error reporting. We need to keep the stat fields stale so that
|
||||
// astGenFile can know to regenerate ZIR.
|
||||
// updateFile can know to regenerate ZIR.
|
||||
|
||||
file.source = source;
|
||||
file.source_loaded = true;
|
||||
return Source{
|
||||
errdefer comptime unreachable; // don't error after populating `source`
|
||||
|
||||
return .{
|
||||
.bytes = source,
|
||||
.stat = .{
|
||||
.size = stat.size,
|
||||
@ -791,20 +809,20 @@ pub const File = struct {
|
||||
}
|
||||
|
||||
pub fn getTree(file: *File, gpa: Allocator) !*const Ast {
|
||||
if (file.tree_loaded) return &file.tree;
|
||||
if (file.tree) |*tree| return tree;
|
||||
|
||||
const source = try file.getSource(gpa);
|
||||
file.tree = try Ast.parse(gpa, source.bytes, file.getMode());
|
||||
file.tree_loaded = true;
|
||||
return &file.tree;
|
||||
file.tree = try .parse(gpa, source.bytes, file.getMode());
|
||||
return &file.tree.?;
|
||||
}
|
||||
|
||||
pub fn getZoir(file: *File, zcu: *Zcu) !*const Zoir {
|
||||
if (file.zoir) |*zoir| return zoir;
|
||||
|
||||
assert(file.tree_loaded);
|
||||
assert(file.tree.mode == .zon);
|
||||
file.zoir = try ZonGen.generate(zcu.gpa, file.tree, .{});
|
||||
const tree = file.tree.?;
|
||||
assert(tree.mode == .zon);
|
||||
|
||||
file.zoir = try ZonGen.generate(zcu.gpa, tree, .{});
|
||||
if (file.zoir.?.hasCompileErrors()) {
|
||||
try zcu.failed_files.putNoClobber(zcu.gpa, file, null);
|
||||
return error.AnalysisFail;
|
||||
@ -854,13 +872,6 @@ pub const File = struct {
|
||||
std.debug.print("{s}:{d}:{d}\n", .{ file.sub_file_path, loc.line + 1, loc.column + 1 });
|
||||
}
|
||||
|
||||
pub fn okToReportErrors(file: File) bool {
|
||||
return switch (file.status) {
|
||||
.parse_failure, .astgen_failure => false,
|
||||
else => true,
|
||||
};
|
||||
}
|
||||
|
||||
/// Add a reference to this file during AstGen.
|
||||
pub fn addReference(file: *File, zcu: *Zcu, ref: File.Reference) !void {
|
||||
// Don't add the same module root twice. Note that since we always add module roots at the
|
||||
@ -900,18 +911,18 @@ pub const File = struct {
|
||||
|
||||
// We can only mark children as failed if the ZIR is loaded, which may not
|
||||
// be the case if there were other astgen failures in this file
|
||||
if (!file.zir_loaded) return;
|
||||
if (file.zir == null) return;
|
||||
|
||||
const imports_index = file.zir.extra[@intFromEnum(Zir.ExtraIndex.imports)];
|
||||
const imports_index = file.zir.?.extra[@intFromEnum(Zir.ExtraIndex.imports)];
|
||||
if (imports_index == 0) return;
|
||||
const extra = file.zir.extraData(Zir.Inst.Imports, imports_index);
|
||||
const extra = file.zir.?.extraData(Zir.Inst.Imports, imports_index);
|
||||
|
||||
var extra_index = extra.end;
|
||||
for (0..extra.data.imports_len) |_| {
|
||||
const item = file.zir.extraData(Zir.Inst.Imports.Item, extra_index);
|
||||
const item = file.zir.?.extraData(Zir.Inst.Imports.Item, extra_index);
|
||||
extra_index = item.end;
|
||||
|
||||
const import_path = file.zir.nullTerminatedString(item.data.name);
|
||||
const import_path = file.zir.?.nullTerminatedString(item.data.name);
|
||||
if (mem.eql(u8, import_path, "builtin")) continue;
|
||||
|
||||
const res = pt.importFile(file, import_path) catch continue;
|
||||
@ -1012,7 +1023,7 @@ pub const SrcLoc = struct {
|
||||
lazy: LazySrcLoc.Offset,
|
||||
|
||||
pub fn baseSrcToken(src_loc: SrcLoc) Ast.TokenIndex {
|
||||
const tree = src_loc.file_scope.tree;
|
||||
const tree = src_loc.file_scope.tree.?;
|
||||
return tree.firstToken(src_loc.base_node);
|
||||
}
|
||||
|
||||
@ -1057,7 +1068,6 @@ pub const SrcLoc = struct {
|
||||
const node_off = traced_off.x;
|
||||
const tree = try src_loc.file_scope.getTree(gpa);
|
||||
const node = src_loc.relativeToNodeIndex(node_off);
|
||||
assert(src_loc.file_scope.tree_loaded);
|
||||
return tree.nodeToSpan(node);
|
||||
},
|
||||
.node_offset_main_token => |node_off| {
|
||||
@ -1069,7 +1079,6 @@ pub const SrcLoc = struct {
|
||||
.node_offset_bin_op => |node_off| {
|
||||
const tree = try src_loc.file_scope.getTree(gpa);
|
||||
const node = src_loc.relativeToNodeIndex(node_off);
|
||||
assert(src_loc.file_scope.tree_loaded);
|
||||
return tree.nodeToSpan(node);
|
||||
},
|
||||
.node_offset_initializer => |node_off| {
|
||||
@ -2408,9 +2417,8 @@ pub const LazySrcLoc = struct {
|
||||
if (zir_inst == .main_struct_inst) return .{ file, 0 };
|
||||
|
||||
// Otherwise, make sure ZIR is loaded.
|
||||
assert(file.zir_loaded);
|
||||
const zir = file.zir.?;
|
||||
|
||||
const zir = file.zir;
|
||||
const inst = zir.instructions.get(@intFromEnum(zir_inst));
|
||||
const base_node: Ast.Node.Index = switch (inst.tag) {
|
||||
.declaration => inst.data.declaration.src_node,
|
||||
@ -2643,6 +2651,189 @@ pub fn loadZirCacheBody(gpa: Allocator, header: Zir.Header, cache_file: std.fs.F
|
||||
return zir;
|
||||
}
|
||||
|
||||
pub fn saveZirCache(gpa: Allocator, cache_file: std.fs.File, stat: std.fs.File.Stat, zir: Zir) (std.fs.File.WriteError || Allocator.Error)!void {
|
||||
const safety_buffer = if (data_has_safety_tag)
|
||||
try gpa.alloc([8]u8, zir.instructions.len)
|
||||
else
|
||||
undefined;
|
||||
defer if (data_has_safety_tag) gpa.free(safety_buffer);
|
||||
|
||||
const data_ptr: [*]const u8 = if (data_has_safety_tag)
|
||||
if (zir.instructions.len == 0)
|
||||
undefined
|
||||
else
|
||||
@ptrCast(safety_buffer.ptr)
|
||||
else
|
||||
@ptrCast(zir.instructions.items(.data).ptr);
|
||||
|
||||
if (data_has_safety_tag) {
|
||||
// The `Data` union has a safety tag but in the file format we store it without.
|
||||
for (zir.instructions.items(.data), 0..) |*data, i| {
|
||||
const as_struct: *const HackDataLayout = @ptrCast(data);
|
||||
safety_buffer[i] = as_struct.data;
|
||||
}
|
||||
}
|
||||
|
||||
const header: Zir.Header = .{
|
||||
.instructions_len = @intCast(zir.instructions.len),
|
||||
.string_bytes_len = @intCast(zir.string_bytes.len),
|
||||
.extra_len = @intCast(zir.extra.len),
|
||||
|
||||
.stat_size = stat.size,
|
||||
.stat_inode = stat.inode,
|
||||
.stat_mtime = stat.mtime,
|
||||
};
|
||||
var iovecs: [5]std.posix.iovec_const = .{
|
||||
.{
|
||||
.base = @ptrCast(&header),
|
||||
.len = @sizeOf(Zir.Header),
|
||||
},
|
||||
.{
|
||||
.base = @ptrCast(zir.instructions.items(.tag).ptr),
|
||||
.len = zir.instructions.len,
|
||||
},
|
||||
.{
|
||||
.base = data_ptr,
|
||||
.len = zir.instructions.len * 8,
|
||||
},
|
||||
.{
|
||||
.base = zir.string_bytes.ptr,
|
||||
.len = zir.string_bytes.len,
|
||||
},
|
||||
.{
|
||||
.base = @ptrCast(zir.extra.ptr),
|
||||
.len = zir.extra.len * 4,
|
||||
},
|
||||
};
|
||||
try cache_file.writevAll(&iovecs);
|
||||
}
|
||||
|
||||
pub fn saveZoirCache(cache_file: std.fs.File, stat: std.fs.File.Stat, zoir: Zoir) std.fs.File.WriteError!void {
|
||||
const header: Zoir.Header = .{
|
||||
.nodes_len = @intCast(zoir.nodes.len),
|
||||
.extra_len = @intCast(zoir.extra.len),
|
||||
.limbs_len = @intCast(zoir.limbs.len),
|
||||
.string_bytes_len = @intCast(zoir.string_bytes.len),
|
||||
.compile_errors_len = @intCast(zoir.compile_errors.len),
|
||||
.error_notes_len = @intCast(zoir.error_notes.len),
|
||||
|
||||
.stat_size = stat.size,
|
||||
.stat_inode = stat.inode,
|
||||
.stat_mtime = stat.mtime,
|
||||
};
|
||||
var iovecs: [9]std.posix.iovec_const = .{
|
||||
.{
|
||||
.base = @ptrCast(&header),
|
||||
.len = @sizeOf(Zoir.Header),
|
||||
},
|
||||
.{
|
||||
.base = @ptrCast(zoir.nodes.items(.tag)),
|
||||
.len = zoir.nodes.len * @sizeOf(Zoir.Node.Repr.Tag),
|
||||
},
|
||||
.{
|
||||
.base = @ptrCast(zoir.nodes.items(.data)),
|
||||
.len = zoir.nodes.len * 4,
|
||||
},
|
||||
.{
|
||||
.base = @ptrCast(zoir.nodes.items(.ast_node)),
|
||||
.len = zoir.nodes.len * 4,
|
||||
},
|
||||
.{
|
||||
.base = @ptrCast(zoir.extra),
|
||||
.len = zoir.extra.len * 4,
|
||||
},
|
||||
.{
|
||||
.base = @ptrCast(zoir.limbs),
|
||||
.len = zoir.limbs.len * 4,
|
||||
},
|
||||
.{
|
||||
.base = zoir.string_bytes.ptr,
|
||||
.len = zoir.string_bytes.len,
|
||||
},
|
||||
.{
|
||||
.base = @ptrCast(zoir.compile_errors),
|
||||
.len = zoir.compile_errors.len * @sizeOf(Zoir.CompileError),
|
||||
},
|
||||
.{
|
||||
.base = @ptrCast(zoir.error_notes),
|
||||
.len = zoir.error_notes.len * @sizeOf(Zoir.CompileError.Note),
|
||||
},
|
||||
};
|
||||
try cache_file.writevAll(&iovecs);
|
||||
}
|
||||
|
||||
pub fn loadZoirCacheBody(gpa: Allocator, header: Zoir.Header, cache_file: std.fs.File) !Zoir {
|
||||
var zoir: Zoir = .{
|
||||
.nodes = .empty,
|
||||
.extra = &.{},
|
||||
.limbs = &.{},
|
||||
.string_bytes = &.{},
|
||||
.compile_errors = &.{},
|
||||
.error_notes = &.{},
|
||||
};
|
||||
errdefer zoir.deinit(gpa);
|
||||
|
||||
zoir.nodes = nodes: {
|
||||
var nodes: std.MultiArrayList(Zoir.Node.Repr) = .empty;
|
||||
defer nodes.deinit(gpa);
|
||||
try nodes.setCapacity(gpa, header.nodes_len);
|
||||
nodes.len = header.nodes_len;
|
||||
break :nodes nodes.toOwnedSlice();
|
||||
};
|
||||
|
||||
zoir.extra = try gpa.alloc(u32, header.extra_len);
|
||||
zoir.limbs = try gpa.alloc(std.math.big.Limb, header.limbs_len);
|
||||
zoir.string_bytes = try gpa.alloc(u8, header.string_bytes_len);
|
||||
|
||||
zoir.compile_errors = try gpa.alloc(Zoir.CompileError, header.compile_errors_len);
|
||||
zoir.error_notes = try gpa.alloc(Zoir.CompileError.Note, header.error_notes_len);
|
||||
|
||||
var iovecs: [8]std.posix.iovec = .{
|
||||
.{
|
||||
.base = @ptrCast(zoir.nodes.items(.tag)),
|
||||
.len = header.nodes_len * @sizeOf(Zoir.Node.Repr.Tag),
|
||||
},
|
||||
.{
|
||||
.base = @ptrCast(zoir.nodes.items(.data)),
|
||||
.len = header.nodes_len * 4,
|
||||
},
|
||||
.{
|
||||
.base = @ptrCast(zoir.nodes.items(.ast_node)),
|
||||
.len = header.nodes_len * 4,
|
||||
},
|
||||
.{
|
||||
.base = @ptrCast(zoir.extra),
|
||||
.len = header.extra_len * 4,
|
||||
},
|
||||
.{
|
||||
.base = @ptrCast(zoir.limbs),
|
||||
.len = header.limbs_len * @sizeOf(std.math.big.Limb),
|
||||
},
|
||||
.{
|
||||
.base = zoir.string_bytes.ptr,
|
||||
.len = header.string_bytes_len,
|
||||
},
|
||||
.{
|
||||
.base = @ptrCast(zoir.compile_errors),
|
||||
.len = header.compile_errors_len * @sizeOf(Zoir.CompileError),
|
||||
},
|
||||
.{
|
||||
.base = @ptrCast(zoir.error_notes),
|
||||
.len = header.error_notes_len * @sizeOf(Zoir.CompileError.Note),
|
||||
},
|
||||
};
|
||||
|
||||
const bytes_expected = expected: {
|
||||
var n: usize = 0;
|
||||
for (iovecs) |v| n += v.len;
|
||||
break :expected n;
|
||||
};
|
||||
|
||||
const bytes_read = try cache_file.readvAll(&iovecs);
|
||||
if (bytes_read != bytes_expected) return error.UnexpectedFileSize;
|
||||
return zoir;
|
||||
}
|
||||
|
||||
pub fn markDependeeOutdated(
|
||||
zcu: *Zcu,
|
||||
/// When we are diffing ZIR and marking things as outdated, we won't yet have marked the dependencies as PO.
|
||||
@ -3303,19 +3494,6 @@ pub fn optimizeMode(zcu: *const Zcu) std.builtin.OptimizeMode {
|
||||
return zcu.root_mod.optimize_mode;
|
||||
}
|
||||
|
||||
fn lockAndClearFileCompileError(zcu: *Zcu, file: *File) void {
|
||||
switch (file.status) {
|
||||
.success_zir, .retryable_failure => {},
|
||||
.never_loaded, .parse_failure, .astgen_failure => {
|
||||
zcu.comp.mutex.lock();
|
||||
defer zcu.comp.mutex.unlock();
|
||||
if (zcu.failed_files.fetchSwapRemove(file)) |kv| {
|
||||
if (kv.value) |msg| msg.destroy(zcu.gpa); // Delete previous error message.
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handleUpdateExports(
|
||||
zcu: *Zcu,
|
||||
export_indices: []const Export.Index,
|
||||
@ -3670,9 +3848,7 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv
|
||||
// `test` declarations are analyzed depending on the test filter.
|
||||
const inst_info = nav.analysis.?.zir_index.resolveFull(ip) orelse continue;
|
||||
const file = zcu.fileByIndex(inst_info.file);
|
||||
// If the file failed AstGen, the TrackedInst refers to the old ZIR.
|
||||
const zir = if (file.status == .success_zir) file.zir else file.prev_zir.?.*;
|
||||
const decl = zir.getDeclaration(inst_info.inst);
|
||||
const decl = file.zir.?.getDeclaration(inst_info.inst);
|
||||
|
||||
if (!comp.config.is_test or file.mod != zcu.main_mod) continue;
|
||||
|
||||
@ -3702,9 +3878,7 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv
|
||||
// These are named declarations. They are analyzed only if marked `export`.
|
||||
const inst_info = ip.getNav(nav).analysis.?.zir_index.resolveFull(ip) orelse continue;
|
||||
const file = zcu.fileByIndex(inst_info.file);
|
||||
// If the file failed AstGen, the TrackedInst refers to the old ZIR.
|
||||
const zir = if (file.status == .success_zir) file.zir else file.prev_zir.?.*;
|
||||
const decl = zir.getDeclaration(inst_info.inst);
|
||||
const decl = file.zir.?.getDeclaration(inst_info.inst);
|
||||
if (decl.linkage == .@"export") {
|
||||
const unit: AnalUnit = .wrap(.{ .nav_val = nav });
|
||||
if (!result.contains(unit)) {
|
||||
@ -3720,9 +3894,7 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv
|
||||
// These are named declarations. They are analyzed only if marked `export`.
|
||||
const inst_info = ip.getNav(nav).analysis.?.zir_index.resolveFull(ip) orelse continue;
|
||||
const file = zcu.fileByIndex(inst_info.file);
|
||||
// If the file failed AstGen, the TrackedInst refers to the old ZIR.
|
||||
const zir = if (file.status == .success_zir) file.zir else file.prev_zir.?.*;
|
||||
const decl = zir.getDeclaration(inst_info.inst);
|
||||
const decl = file.zir.?.getDeclaration(inst_info.inst);
|
||||
if (decl.linkage == .@"export") {
|
||||
const unit: AnalUnit = .wrap(.{ .nav_val = nav });
|
||||
if (!result.contains(unit)) {
|
||||
@ -3858,7 +4030,7 @@ pub fn navSrcLine(zcu: *Zcu, nav_index: InternPool.Nav.Index) u32 {
|
||||
const ip = &zcu.intern_pool;
|
||||
const inst_info = ip.getNav(nav_index).srcInst(ip).resolveFull(ip).?;
|
||||
const zir = zcu.fileByIndex(inst_info.file).zir;
|
||||
return zir.getDeclaration(inst_info.inst).src_line;
|
||||
return zir.?.getDeclaration(inst_info.inst).src_line;
|
||||
}
|
||||
|
||||
pub fn navValue(zcu: *const Zcu, nav_index: InternPool.Nav.Index) Value {
|
||||
@ -3910,10 +4082,6 @@ fn formatDependee(data: struct { dependee: InternPool.Dependee, zcu: *Zcu }, com
|
||||
const zcu = data.zcu;
|
||||
const ip = &zcu.intern_pool;
|
||||
switch (data.dependee) {
|
||||
.file => |file| {
|
||||
const file_path = zcu.fileByIndex(file).sub_file_path;
|
||||
return writer.print("file('{s}')", .{file_path});
|
||||
},
|
||||
.src_hash => |ti| {
|
||||
const info = ti.resolveFull(ip) orelse {
|
||||
return writer.writeAll("inst(<lost>)");
|
||||
@ -3934,6 +4102,10 @@ fn formatDependee(data: struct { dependee: InternPool.Dependee, zcu: *Zcu }, com
|
||||
.func => |f| return writer.print("ies('{}')", .{ip.getNav(f.owner_nav).fqn.fmt(ip)}),
|
||||
else => unreachable,
|
||||
},
|
||||
.zon_file => |file| {
|
||||
const file_path = zcu.fileByIndex(file).sub_file_path;
|
||||
return writer.print("zon_file('{s}')", .{file_path});
|
||||
},
|
||||
.embed_file => |ef_idx| {
|
||||
const ef = ef_idx.get(zcu);
|
||||
return writer.print("embed_file('{s}')", .{std.fs.path.fmtJoin(&.{
|
||||
|
||||
@ -26,6 +26,8 @@ const Type = @import("../Type.zig");
|
||||
const Value = @import("../Value.zig");
|
||||
const Zcu = @import("../Zcu.zig");
|
||||
const Zir = std.zig.Zir;
|
||||
const Zoir = std.zig.Zoir;
|
||||
const ZonGen = std.zig.ZonGen;
|
||||
|
||||
zcu: *Zcu,
|
||||
|
||||
@ -73,7 +75,9 @@ pub fn destroyFile(pt: Zcu.PerThread, file_index: Zcu.File.Index) void {
|
||||
if (!is_builtin) gpa.destroy(file);
|
||||
}
|
||||
|
||||
pub fn astGenFile(
|
||||
/// Ensures that `file` has up-to-date ZIR. If not, loads the ZIR cache or runs
|
||||
/// AstGen as needed. Also updates `file.status`.
|
||||
pub fn updateFile(
|
||||
pt: Zcu.PerThread,
|
||||
file: *Zcu.File,
|
||||
path_digest: Cache.BinDigest,
|
||||
@ -109,7 +113,7 @@ pub fn astGenFile(
|
||||
|
||||
break :lock .shared;
|
||||
},
|
||||
.parse_failure, .astgen_failure, .success_zir => lock: {
|
||||
.astgen_failure, .success => lock: {
|
||||
const unchanged_metadata =
|
||||
stat.size == file.stat.size and
|
||||
stat.mtime == file.stat.mtime and
|
||||
@ -126,6 +130,27 @@ pub fn astGenFile(
|
||||
},
|
||||
};
|
||||
|
||||
// The old compile error, if any, is no longer relevant.
|
||||
pt.lockAndClearFileCompileError(file);
|
||||
|
||||
// If `zir` is not null, and `prev_zir` is null, then `TrackedInst`s are associated with `zir`.
|
||||
// We need to keep it around!
|
||||
// As an optimization, also check `loweringFailed`; if true, but `prev_zir == null`, then this
|
||||
// file has never passed AstGen, so we actually need not cache the old ZIR.
|
||||
if (file.zir != null and file.prev_zir == null and !file.zir.?.loweringFailed()) {
|
||||
assert(file.prev_zir == null);
|
||||
const prev_zir_ptr = try gpa.create(Zir);
|
||||
file.prev_zir = prev_zir_ptr;
|
||||
prev_zir_ptr.* = file.zir.?;
|
||||
file.zir = null;
|
||||
}
|
||||
|
||||
// If ZOIR is changing, then we need to invalidate dependencies on it
|
||||
if (file.zoir != null) file.zoir_invalidated = true;
|
||||
|
||||
// We're going to re-load everything, so unload source, AST, ZIR, ZOIR.
|
||||
file.unload(gpa);
|
||||
|
||||
// We ask for a lock in order to coordinate with other zig processes.
|
||||
// If another process is already working on this file, we will get the cached
|
||||
// version. Likewise if we're working on AstGen and another process asks for
|
||||
@ -180,190 +205,164 @@ pub fn astGenFile(
|
||||
};
|
||||
defer cache_file.close();
|
||||
|
||||
while (true) {
|
||||
update: {
|
||||
// First we read the header to determine the lengths of arrays.
|
||||
const header = cache_file.reader().readStruct(Zir.Header) catch |err| switch (err) {
|
||||
// This can happen if Zig bails out of this function between creating
|
||||
// the cached file and writing it.
|
||||
error.EndOfStream => break :update,
|
||||
else => |e| return e,
|
||||
};
|
||||
const unchanged_metadata =
|
||||
stat.size == header.stat_size and
|
||||
stat.mtime == header.stat_mtime and
|
||||
stat.inode == header.stat_inode;
|
||||
|
||||
if (!unchanged_metadata) {
|
||||
log.debug("AstGen cache stale: {s}", .{file.sub_file_path});
|
||||
break :update;
|
||||
}
|
||||
log.debug("AstGen cache hit: {s} instructions_len={d}", .{
|
||||
file.sub_file_path, header.instructions_len,
|
||||
});
|
||||
|
||||
file.zir = Zcu.loadZirCacheBody(gpa, header, cache_file) catch |err| switch (err) {
|
||||
error.UnexpectedFileSize => {
|
||||
log.warn("unexpected EOF reading cached ZIR for {s}", .{file.sub_file_path});
|
||||
break :update;
|
||||
},
|
||||
else => |e| return e,
|
||||
};
|
||||
file.zir_loaded = true;
|
||||
file.stat = .{
|
||||
.size = header.stat_size,
|
||||
.inode = header.stat_inode,
|
||||
.mtime = header.stat_mtime,
|
||||
};
|
||||
file.prev_status = file.status;
|
||||
file.status = .success_zir;
|
||||
log.debug("AstGen cached success: {s}", .{file.sub_file_path});
|
||||
|
||||
if (file.zir.hasCompileErrors()) {
|
||||
comp.mutex.lock();
|
||||
defer comp.mutex.unlock();
|
||||
try zcu.failed_files.putNoClobber(gpa, file, null);
|
||||
}
|
||||
if (file.zir.loweringFailed()) {
|
||||
file.status = .astgen_failure;
|
||||
return error.AnalysisFail;
|
||||
}
|
||||
return;
|
||||
const need_update = while (true) {
|
||||
const result = switch (file.getMode()) {
|
||||
inline else => |mode| try loadZirZoirCache(zcu, cache_file, stat, file, mode),
|
||||
};
|
||||
switch (result) {
|
||||
.success => {
|
||||
log.debug("AstGen cached success: {s}", .{file.sub_file_path});
|
||||
break false;
|
||||
},
|
||||
.invalid => {},
|
||||
.truncated => log.warn("unexpected EOF reading cached ZIR for {s}", .{file.sub_file_path}),
|
||||
.stale => log.debug("AstGen cache stale: {s}", .{file.sub_file_path}),
|
||||
}
|
||||
|
||||
// If we already have the exclusive lock then it is our job to update.
|
||||
if (builtin.os.tag == .wasi or lock == .exclusive) break;
|
||||
if (builtin.os.tag == .wasi or lock == .exclusive) break true;
|
||||
// Otherwise, unlock to give someone a chance to get the exclusive lock
|
||||
// and then upgrade to an exclusive lock.
|
||||
cache_file.unlock();
|
||||
lock = .exclusive;
|
||||
try cache_file.lock(lock);
|
||||
}
|
||||
|
||||
// The cache is definitely stale so delete the contents to avoid an underwrite later.
|
||||
cache_file.setEndPos(0) catch |err| switch (err) {
|
||||
error.FileTooBig => unreachable, // 0 is not too big
|
||||
|
||||
else => |e| return e,
|
||||
};
|
||||
|
||||
pt.lockAndClearFileCompileError(file);
|
||||
if (need_update) {
|
||||
// The cache is definitely stale so delete the contents to avoid an underwrite later.
|
||||
cache_file.setEndPos(0) catch |err| switch (err) {
|
||||
error.FileTooBig => unreachable, // 0 is not too big
|
||||
else => |e| return e,
|
||||
};
|
||||
|
||||
// Previous ZIR is kept for two reasons:
|
||||
//
|
||||
// 1. In case an update to the file causes a Parse or AstGen failure, we
|
||||
// need to compare two successful ZIR files in order to proceed with an
|
||||
// incremental update. This avoids needlessly tossing out semantic
|
||||
// analysis work when an error is temporarily introduced.
|
||||
//
|
||||
// 2. In order to detect updates, we need to iterate over the intern pool
|
||||
// values while comparing old ZIR to new ZIR. This is better done in a
|
||||
// single-threaded context, so we need to keep both versions around
|
||||
// until that point in the pipeline. Previous ZIR data is freed after
|
||||
// that.
|
||||
if (file.zir_loaded and !file.zir.loweringFailed()) {
|
||||
assert(file.prev_zir == null);
|
||||
const prev_zir_ptr = try gpa.create(Zir);
|
||||
file.prev_zir = prev_zir_ptr;
|
||||
prev_zir_ptr.* = file.zir;
|
||||
file.zir = undefined;
|
||||
file.zir_loaded = false;
|
||||
if (stat.size > std.math.maxInt(u32))
|
||||
return error.FileTooBig;
|
||||
|
||||
const source = try gpa.allocSentinel(u8, @as(usize, @intCast(stat.size)), 0);
|
||||
defer if (file.source == null) gpa.free(source);
|
||||
const amt = try source_file.readAll(source);
|
||||
if (amt != stat.size)
|
||||
return error.UnexpectedEndOfFile;
|
||||
|
||||
file.source = source;
|
||||
|
||||
// Any potential AST errors are converted to ZIR errors when we run AstGen/ZonGen.
|
||||
file.tree = try Ast.parse(gpa, source, file.getMode());
|
||||
|
||||
switch (file.getMode()) {
|
||||
.zig => {
|
||||
file.zir = try AstGen.generate(gpa, file.tree.?);
|
||||
Zcu.saveZirCache(gpa, cache_file, stat, file.zir.?) catch |err| switch (err) {
|
||||
error.OutOfMemory => |e| return e,
|
||||
else => log.warn("unable to write cached ZIR code for {}{s} to {}{s}: {s}", .{
|
||||
file.mod.root, file.sub_file_path, cache_directory, &hex_digest, @errorName(err),
|
||||
}),
|
||||
};
|
||||
},
|
||||
.zon => {
|
||||
file.zoir = try ZonGen.generate(gpa, file.tree.?, .{});
|
||||
Zcu.saveZoirCache(cache_file, stat, file.zoir.?) catch |err| {
|
||||
log.warn("unable to write cached ZOIR code for {}{s} to {}{s}: {s}", .{
|
||||
file.mod.root, file.sub_file_path, cache_directory, &hex_digest, @errorName(err),
|
||||
});
|
||||
};
|
||||
},
|
||||
}
|
||||
|
||||
log.debug("AstGen fresh success: {s}", .{file.sub_file_path});
|
||||
}
|
||||
file.unload(gpa);
|
||||
|
||||
if (stat.size > std.math.maxInt(u32))
|
||||
return error.FileTooBig;
|
||||
|
||||
const source = try gpa.allocSentinel(u8, @as(usize, @intCast(stat.size)), 0);
|
||||
defer if (!file.source_loaded) gpa.free(source);
|
||||
const amt = try source_file.readAll(source);
|
||||
if (amt != stat.size)
|
||||
return error.UnexpectedEndOfFile;
|
||||
|
||||
file.stat = .{
|
||||
.size = stat.size,
|
||||
.inode = stat.inode,
|
||||
.mtime = stat.mtime,
|
||||
};
|
||||
file.source = source;
|
||||
file.source_loaded = true;
|
||||
|
||||
file.tree = try Ast.parse(gpa, source, .zig);
|
||||
file.tree_loaded = true;
|
||||
// Now, `zir` or `zoir` is definitely populated and up-to-date.
|
||||
// Mark file successes/failures as needed.
|
||||
|
||||
// Any potential AST errors are converted to ZIR errors here.
|
||||
file.zir = try AstGen.generate(gpa, file.tree);
|
||||
file.zir_loaded = true;
|
||||
file.prev_status = file.status;
|
||||
file.status = .success_zir;
|
||||
log.debug("AstGen fresh success: {s}", .{file.sub_file_path});
|
||||
|
||||
const safety_buffer = if (Zcu.data_has_safety_tag)
|
||||
try gpa.alloc([8]u8, file.zir.instructions.len)
|
||||
else
|
||||
undefined;
|
||||
defer if (Zcu.data_has_safety_tag) gpa.free(safety_buffer);
|
||||
const data_ptr = if (Zcu.data_has_safety_tag)
|
||||
if (file.zir.instructions.len == 0)
|
||||
@as([*]const u8, undefined)
|
||||
else
|
||||
@as([*]const u8, @ptrCast(safety_buffer.ptr))
|
||||
else
|
||||
@as([*]const u8, @ptrCast(file.zir.instructions.items(.data).ptr));
|
||||
if (Zcu.data_has_safety_tag) {
|
||||
// The `Data` union has a safety tag but in the file format we store it without.
|
||||
for (file.zir.instructions.items(.data), 0..) |*data, i| {
|
||||
const as_struct: *const Zcu.HackDataLayout = @ptrCast(data);
|
||||
safety_buffer[i] = as_struct.data;
|
||||
}
|
||||
switch (file.getMode()) {
|
||||
.zig => {
|
||||
if (file.zir.?.hasCompileErrors()) {
|
||||
comp.mutex.lock();
|
||||
defer comp.mutex.unlock();
|
||||
try zcu.failed_files.putNoClobber(gpa, file, null);
|
||||
}
|
||||
if (file.zir.?.loweringFailed()) {
|
||||
file.status = .astgen_failure;
|
||||
} else {
|
||||
file.status = .success;
|
||||
}
|
||||
},
|
||||
.zon => {
|
||||
if (file.zoir.?.hasCompileErrors()) {
|
||||
file.status = .astgen_failure;
|
||||
comp.mutex.lock();
|
||||
defer comp.mutex.unlock();
|
||||
try zcu.failed_files.putNoClobber(gpa, file, null);
|
||||
} else {
|
||||
file.status = .success;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
const header: Zir.Header = .{
|
||||
.instructions_len = @as(u32, @intCast(file.zir.instructions.len)),
|
||||
.string_bytes_len = @as(u32, @intCast(file.zir.string_bytes.len)),
|
||||
.extra_len = @as(u32, @intCast(file.zir.extra.len)),
|
||||
switch (file.status) {
|
||||
.never_loaded => unreachable,
|
||||
.retryable_failure => unreachable,
|
||||
.astgen_failure => return error.AnalysisFail,
|
||||
.success => return,
|
||||
}
|
||||
}
|
||||
|
||||
.stat_size = stat.size,
|
||||
.stat_inode = stat.inode,
|
||||
.stat_mtime = stat.mtime,
|
||||
};
|
||||
var iovecs = [_]std.posix.iovec_const{
|
||||
.{
|
||||
.base = @as([*]const u8, @ptrCast(&header)),
|
||||
.len = @sizeOf(Zir.Header),
|
||||
},
|
||||
.{
|
||||
.base = @as([*]const u8, @ptrCast(file.zir.instructions.items(.tag).ptr)),
|
||||
.len = file.zir.instructions.len,
|
||||
},
|
||||
.{
|
||||
.base = data_ptr,
|
||||
.len = file.zir.instructions.len * 8,
|
||||
},
|
||||
.{
|
||||
.base = file.zir.string_bytes.ptr,
|
||||
.len = file.zir.string_bytes.len,
|
||||
},
|
||||
.{
|
||||
.base = @as([*]const u8, @ptrCast(file.zir.extra.ptr)),
|
||||
.len = file.zir.extra.len * 4,
|
||||
},
|
||||
};
|
||||
cache_file.writevAll(&iovecs) catch |err| {
|
||||
log.warn("unable to write cached ZIR code for {}{s} to {}{s}: {s}", .{
|
||||
file.mod.root, file.sub_file_path, cache_directory, &hex_digest, @errorName(err),
|
||||
});
|
||||
fn loadZirZoirCache(
|
||||
zcu: *Zcu,
|
||||
cache_file: std.fs.File,
|
||||
stat: std.fs.File.Stat,
|
||||
file: *Zcu.File,
|
||||
comptime mode: Ast.Mode,
|
||||
) !enum { success, invalid, truncated, stale } {
|
||||
assert(file.getMode() == mode);
|
||||
|
||||
const gpa = zcu.gpa;
|
||||
|
||||
const Header = switch (mode) {
|
||||
.zig => Zir.Header,
|
||||
.zon => Zoir.Header,
|
||||
};
|
||||
|
||||
if (file.zir.hasCompileErrors()) {
|
||||
comp.mutex.lock();
|
||||
defer comp.mutex.unlock();
|
||||
try zcu.failed_files.putNoClobber(gpa, file, null);
|
||||
// First we read the header to determine the lengths of arrays.
|
||||
const header = cache_file.reader().readStruct(Header) catch |err| switch (err) {
|
||||
// This can happen if Zig bails out of this function between creating
|
||||
// the cached file and writing it.
|
||||
error.EndOfStream => return .invalid,
|
||||
else => |e| return e,
|
||||
};
|
||||
|
||||
const unchanged_metadata =
|
||||
stat.size == header.stat_size and
|
||||
stat.mtime == header.stat_mtime and
|
||||
stat.inode == header.stat_inode;
|
||||
|
||||
if (!unchanged_metadata) {
|
||||
return .stale;
|
||||
}
|
||||
if (file.zir.loweringFailed()) {
|
||||
file.status = .astgen_failure;
|
||||
return error.AnalysisFail;
|
||||
|
||||
switch (mode) {
|
||||
.zig => {
|
||||
file.zir = Zcu.loadZirCacheBody(gpa, header, cache_file) catch |err| switch (err) {
|
||||
error.UnexpectedFileSize => return .truncated,
|
||||
else => |e| return e,
|
||||
};
|
||||
},
|
||||
.zon => {
|
||||
file.zoir = Zcu.loadZoirCacheBody(gpa, header, cache_file) catch |err| switch (err) {
|
||||
error.UnexpectedFileSize => return .truncated,
|
||||
else => |e| return e,
|
||||
};
|
||||
},
|
||||
}
|
||||
|
||||
return .success;
|
||||
}
|
||||
|
||||
const UpdatedFile = struct {
|
||||
@ -384,24 +383,32 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {
|
||||
const gpa = zcu.gpa;
|
||||
|
||||
// We need to visit every updated File for every TrackedInst in InternPool.
|
||||
// This only includes Zig files; ZON files are omitted.
|
||||
var updated_files: std.AutoArrayHashMapUnmanaged(Zcu.File.Index, UpdatedFile) = .empty;
|
||||
defer cleanupUpdatedFiles(gpa, &updated_files);
|
||||
|
||||
for (zcu.import_table.values()) |file_index| {
|
||||
const file = zcu.fileByIndex(file_index);
|
||||
if (file.prev_status != file.status and file.prev_status != .never_loaded) {
|
||||
try zcu.markDependeeOutdated(.not_marked_po, .{ .file = file_index });
|
||||
assert(file.status == .success);
|
||||
switch (file.getMode()) {
|
||||
.zig => {}, // logic below
|
||||
.zon => {
|
||||
if (file.zoir_invalidated) {
|
||||
try zcu.markDependeeOutdated(.not_marked_po, .{ .zon_file = file_index });
|
||||
file.zoir_invalidated = false;
|
||||
}
|
||||
continue;
|
||||
},
|
||||
}
|
||||
const old_zir = file.prev_zir orelse continue;
|
||||
const new_zir = file.zir;
|
||||
const new_zir = file.zir.?;
|
||||
const gop = try updated_files.getOrPut(gpa, file_index);
|
||||
assert(!gop.found_existing);
|
||||
gop.value_ptr.* = .{
|
||||
.file = file,
|
||||
.inst_map = .{},
|
||||
};
|
||||
if (!new_zir.loweringFailed()) {
|
||||
try Zcu.mapOldZirToNew(gpa, old_zir.*, file.zir, &gop.value_ptr.inst_map);
|
||||
}
|
||||
try Zcu.mapOldZirToNew(gpa, old_zir.*, new_zir, &gop.value_ptr.inst_map);
|
||||
}
|
||||
|
||||
if (updated_files.count() == 0)
|
||||
@ -421,13 +428,9 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {
|
||||
.index = @intCast(tracked_inst_unwrapped_index),
|
||||
}).wrap(ip);
|
||||
const new_inst = updated_file.inst_map.get(old_inst) orelse {
|
||||
// Tracking failed for this instruction.
|
||||
// This may be due to changes in the ZIR, or AstGen might have failed due to a very broken file.
|
||||
// Either way, invalidate associated `src_hash` deps.
|
||||
log.debug("tracking failed for %{d}{s}", .{
|
||||
old_inst,
|
||||
if (file.zir.loweringFailed()) " due to AstGen failure" else "",
|
||||
});
|
||||
// Tracking failed for this instruction due to changes in the ZIR.
|
||||
// Invalidate associated `src_hash` deps.
|
||||
log.debug("tracking failed for %{d}", .{old_inst});
|
||||
tracked_inst.inst = .lost;
|
||||
try zcu.markDependeeOutdated(.not_marked_po, .{ .src_hash = tracked_inst_index });
|
||||
continue;
|
||||
@ -435,7 +438,7 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {
|
||||
tracked_inst.inst = InternPool.TrackedInst.MaybeLost.ZirIndex.wrap(new_inst);
|
||||
|
||||
const old_zir = file.prev_zir.?.*;
|
||||
const new_zir = file.zir;
|
||||
const new_zir = file.zir.?;
|
||||
const old_tag = old_zir.instructions.items(.tag)[@intFromEnum(old_inst)];
|
||||
const old_data = old_zir.instructions.items(.data)[@intFromEnum(old_inst)];
|
||||
|
||||
@ -532,23 +535,19 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {
|
||||
|
||||
for (updated_files.keys(), updated_files.values()) |file_index, updated_file| {
|
||||
const file = updated_file.file;
|
||||
if (file.zir.loweringFailed()) {
|
||||
// Keep `prev_zir` around: it's the last usable ZIR.
|
||||
// Don't update the namespace, as we have no new data to update *to*.
|
||||
} else {
|
||||
const prev_zir = file.prev_zir.?;
|
||||
file.prev_zir = null;
|
||||
prev_zir.deinit(gpa);
|
||||
gpa.destroy(prev_zir);
|
||||
|
||||
// For every file which has changed, re-scan the namespace of the file's root struct type.
|
||||
// These types are special-cased because they don't have an enclosing declaration which will
|
||||
// be re-analyzed (causing the struct's namespace to be re-scanned). It's fine to do this
|
||||
// now because this work is fast (no actual Sema work is happening, we're just updating the
|
||||
// namespace contents). We must do this after updating ZIR refs above, since `scanNamespace`
|
||||
// will track some instructions.
|
||||
try pt.updateFileNamespace(file_index);
|
||||
}
|
||||
const prev_zir = file.prev_zir.?;
|
||||
file.prev_zir = null;
|
||||
prev_zir.deinit(gpa);
|
||||
gpa.destroy(prev_zir);
|
||||
|
||||
// For every file which has changed, re-scan the namespace of the file's root struct type.
|
||||
// These types are special-cased because they don't have an enclosing declaration which will
|
||||
// be re-analyzed (causing the struct's namespace to be re-scanned). It's fine to do this
|
||||
// now because this work is fast (no actual Sema work is happening, we're just updating the
|
||||
// namespace contents). We must do this after updating ZIR refs above, since `scanNamespace`
|
||||
// will track some instructions.
|
||||
try pt.updateFileNamespace(file_index);
|
||||
}
|
||||
}
|
||||
|
||||
@ -750,6 +749,7 @@ pub fn ensureComptimeUnitUpToDate(pt: Zcu.PerThread, cu_id: InternPool.ComptimeU
|
||||
kv.value.destroy(gpa);
|
||||
}
|
||||
_ = zcu.transitive_failed_analysis.swapRemove(anal_unit);
|
||||
zcu.intern_pool.removeDependenciesForDepender(gpa, anal_unit);
|
||||
}
|
||||
} else {
|
||||
// We can trust the current information about this unit.
|
||||
@ -801,14 +801,7 @@ fn analyzeComptimeUnit(pt: Zcu.PerThread, cu_id: InternPool.ComptimeUnit.Id) Zcu
|
||||
|
||||
const inst_resolved = comptime_unit.zir_index.resolveFull(ip) orelse return error.AnalysisFail;
|
||||
const file = zcu.fileByIndex(inst_resolved.file);
|
||||
// TODO: stop the compiler ever reaching Sema if there are failed files. That way, this check is
|
||||
// unnecessary, and we can move the below `removeDependenciesForDepender` call up with its friends
|
||||
// in `ensureComptimeUnitUpToDate`.
|
||||
if (file.status != .success_zir) return error.AnalysisFail;
|
||||
const zir = file.zir;
|
||||
|
||||
// We are about to re-analyze this unit; drop its depenndencies.
|
||||
zcu.intern_pool.removeDependenciesForDepender(gpa, anal_unit);
|
||||
const zir = file.zir.?;
|
||||
|
||||
try zcu.analysis_in_progress.put(gpa, anal_unit, {});
|
||||
defer assert(zcu.analysis_in_progress.swapRemove(anal_unit));
|
||||
@ -928,6 +921,7 @@ pub fn ensureNavValUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu
|
||||
kv.value.destroy(gpa);
|
||||
}
|
||||
_ = zcu.transitive_failed_analysis.swapRemove(anal_unit);
|
||||
ip.removeDependenciesForDepender(gpa, anal_unit);
|
||||
} else {
|
||||
// We can trust the current information about this unit.
|
||||
if (prev_failed) return error.AnalysisFail;
|
||||
@ -998,14 +992,7 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr
|
||||
|
||||
const inst_resolved = old_nav.analysis.?.zir_index.resolveFull(ip) orelse return error.AnalysisFail;
|
||||
const file = zcu.fileByIndex(inst_resolved.file);
|
||||
// TODO: stop the compiler ever reaching Sema if there are failed files. That way, this check is
|
||||
// unnecessary, and we can move the below `removeDependenciesForDepender` call up with its friends
|
||||
// in `ensureComptimeUnitUpToDate`.
|
||||
if (file.status != .success_zir) return error.AnalysisFail;
|
||||
const zir = file.zir;
|
||||
|
||||
// We are about to re-analyze this unit; drop its depenndencies.
|
||||
zcu.intern_pool.removeDependenciesForDepender(gpa, anal_unit);
|
||||
const zir = file.zir.?;
|
||||
|
||||
try zcu.analysis_in_progress.put(gpa, anal_unit, {});
|
||||
errdefer _ = zcu.analysis_in_progress.swapRemove(anal_unit);
|
||||
@ -1306,6 +1293,7 @@ pub fn ensureNavTypeUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zc
|
||||
kv.value.destroy(gpa);
|
||||
}
|
||||
_ = zcu.transitive_failed_analysis.swapRemove(anal_unit);
|
||||
ip.removeDependenciesForDepender(gpa, anal_unit);
|
||||
} else {
|
||||
// We can trust the current information about this unit.
|
||||
if (prev_failed) return error.AnalysisFail;
|
||||
@ -1376,14 +1364,7 @@ fn analyzeNavType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileEr
|
||||
|
||||
const inst_resolved = old_nav.analysis.?.zir_index.resolveFull(ip) orelse return error.AnalysisFail;
|
||||
const file = zcu.fileByIndex(inst_resolved.file);
|
||||
// TODO: stop the compiler ever reaching Sema if there are failed files. That way, this check is
|
||||
// unnecessary, and we can move the below `removeDependenciesForDepender` call up with its friends
|
||||
// in `ensureComptimeUnitUpToDate`.
|
||||
if (file.status != .success_zir) return error.AnalysisFail;
|
||||
const zir = file.zir;
|
||||
|
||||
// We are about to re-analyze this unit; drop its depenndencies.
|
||||
zcu.intern_pool.removeDependenciesForDepender(gpa, anal_unit);
|
||||
const zir = file.zir.?;
|
||||
|
||||
try zcu.analysis_in_progress.put(gpa, anal_unit, {});
|
||||
defer _ = zcu.analysis_in_progress.swapRemove(anal_unit);
|
||||
@ -1758,7 +1739,7 @@ fn createFileRootStruct(
|
||||
const gpa = zcu.gpa;
|
||||
const ip = &zcu.intern_pool;
|
||||
const file = zcu.fileByIndex(file_index);
|
||||
const extended = file.zir.instructions.items(.data)[@intFromEnum(Zir.Inst.Index.main_struct_inst)].extended;
|
||||
const extended = file.zir.?.instructions.items(.data)[@intFromEnum(Zir.Inst.Index.main_struct_inst)].extended;
|
||||
assert(extended.opcode == .struct_decl);
|
||||
const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
|
||||
assert(!small.has_captures_len);
|
||||
@ -1766,16 +1747,16 @@ fn createFileRootStruct(
|
||||
assert(small.layout == .auto);
|
||||
var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).@"struct".fields.len;
|
||||
const fields_len = if (small.has_fields_len) blk: {
|
||||
const fields_len = file.zir.extra[extra_index];
|
||||
const fields_len = file.zir.?.extra[extra_index];
|
||||
extra_index += 1;
|
||||
break :blk fields_len;
|
||||
} else 0;
|
||||
const decls_len = if (small.has_decls_len) blk: {
|
||||
const decls_len = file.zir.extra[extra_index];
|
||||
const decls_len = file.zir.?.extra[extra_index];
|
||||
extra_index += 1;
|
||||
break :blk decls_len;
|
||||
} else 0;
|
||||
const decls = file.zir.bodySlice(extra_index, decls_len);
|
||||
const decls = file.zir.?.bodySlice(extra_index, decls_len);
|
||||
extra_index += decls_len;
|
||||
|
||||
const tracked_inst = try ip.trackZir(gpa, pt.tid, .{
|
||||
@ -1833,7 +1814,6 @@ fn updateFileNamespace(pt: Zcu.PerThread, file_index: Zcu.File.Index) Allocator.
|
||||
const zcu = pt.zcu;
|
||||
|
||||
const file = zcu.fileByIndex(file_index);
|
||||
assert(file.status == .success_zir);
|
||||
const file_root_type = zcu.fileRootType(file_index);
|
||||
if (file_root_type == .none) return;
|
||||
|
||||
@ -1844,17 +1824,17 @@ fn updateFileNamespace(pt: Zcu.PerThread, file_index: Zcu.File.Index) Allocator.
|
||||
|
||||
const namespace_index = Type.fromInterned(file_root_type).getNamespaceIndex(zcu);
|
||||
const decls = decls: {
|
||||
const extended = file.zir.instructions.items(.data)[@intFromEnum(Zir.Inst.Index.main_struct_inst)].extended;
|
||||
const extended = file.zir.?.instructions.items(.data)[@intFromEnum(Zir.Inst.Index.main_struct_inst)].extended;
|
||||
const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
|
||||
|
||||
var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).@"struct".fields.len;
|
||||
extra_index += @intFromBool(small.has_fields_len);
|
||||
const decls_len = if (small.has_decls_len) blk: {
|
||||
const decls_len = file.zir.extra[extra_index];
|
||||
const decls_len = file.zir.?.extra[extra_index];
|
||||
extra_index += 1;
|
||||
break :blk decls_len;
|
||||
} else 0;
|
||||
break :decls file.zir.bodySlice(extra_index, decls_len);
|
||||
break :decls file.zir.?.bodySlice(extra_index, decls_len);
|
||||
};
|
||||
try pt.scanNamespace(namespace_index, decls);
|
||||
zcu.namespacePtr(namespace_index).generation = zcu.generation;
|
||||
@ -1865,15 +1845,11 @@ fn semaFile(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError!void {
|
||||
defer tracy.end();
|
||||
|
||||
const zcu = pt.zcu;
|
||||
const gpa = zcu.gpa;
|
||||
const file = zcu.fileByIndex(file_index);
|
||||
assert(file.getMode() == .zig);
|
||||
assert(zcu.fileRootType(file_index) == .none);
|
||||
|
||||
if (file.status != .success_zir) {
|
||||
return error.AnalysisFail;
|
||||
}
|
||||
assert(file.zir_loaded);
|
||||
assert(file.zir != null);
|
||||
|
||||
const new_namespace_index = try pt.createNamespace(.{
|
||||
.parent = .none,
|
||||
@ -1883,39 +1859,9 @@ fn semaFile(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError!void {
|
||||
});
|
||||
const struct_ty = try pt.createFileRootStruct(file_index, new_namespace_index, false);
|
||||
errdefer zcu.intern_pool.remove(pt.tid, struct_ty);
|
||||
|
||||
switch (zcu.comp.cache_use) {
|
||||
.whole => |whole| if (whole.cache_manifest) |man| {
|
||||
const source = file.getSource(gpa) catch |err| {
|
||||
try pt.reportRetryableFileError(file_index, "unable to load source: {s}", .{@errorName(err)});
|
||||
return error.AnalysisFail;
|
||||
};
|
||||
|
||||
const resolved_path = std.fs.path.resolve(gpa, &.{
|
||||
file.mod.root.root_dir.path orelse ".",
|
||||
file.mod.root.sub_path,
|
||||
file.sub_file_path,
|
||||
}) catch |err| {
|
||||
try pt.reportRetryableFileError(file_index, "unable to resolve path: {s}", .{@errorName(err)});
|
||||
return error.AnalysisFail;
|
||||
};
|
||||
errdefer gpa.free(resolved_path);
|
||||
|
||||
whole.cache_manifest_mutex.lock();
|
||||
defer whole.cache_manifest_mutex.unlock();
|
||||
man.addFilePostContents(resolved_path, source.bytes, source.stat) catch |err| switch (err) {
|
||||
error.OutOfMemory => |e| return e,
|
||||
else => {
|
||||
try pt.reportRetryableFileError(file_index, "unable to update cache: {s}", .{@errorName(err)});
|
||||
return error.AnalysisFail;
|
||||
},
|
||||
};
|
||||
},
|
||||
.incremental => {},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn importPkg(pt: Zcu.PerThread, mod: *Module) !Zcu.ImportFileResult {
|
||||
pub fn importPkg(pt: Zcu.PerThread, mod: *Module) Allocator.Error!Zcu.ImportFileResult {
|
||||
const zcu = pt.zcu;
|
||||
const gpa = zcu.gpa;
|
||||
|
||||
@ -1983,15 +1929,12 @@ pub fn importPkg(pt: Zcu.PerThread, mod: *Module) !Zcu.ImportFileResult {
|
||||
gop.value_ptr.* = new_file_index;
|
||||
new_file.* = .{
|
||||
.sub_file_path = sub_file_path,
|
||||
.source = undefined,
|
||||
.source_loaded = false,
|
||||
.tree_loaded = false,
|
||||
.zir_loaded = false,
|
||||
.stat = undefined,
|
||||
.tree = undefined,
|
||||
.zir = undefined,
|
||||
.source = null,
|
||||
.tree = null,
|
||||
.zir = null,
|
||||
.zoir = null,
|
||||
.status = .never_loaded,
|
||||
.prev_status = .never_loaded,
|
||||
.mod = mod,
|
||||
};
|
||||
|
||||
@ -2004,13 +1947,19 @@ pub fn importPkg(pt: Zcu.PerThread, mod: *Module) !Zcu.ImportFileResult {
|
||||
};
|
||||
}
|
||||
|
||||
/// Called from a worker thread during AstGen.
|
||||
/// Called from a worker thread during AstGen (with the Compilation mutex held).
|
||||
/// Also called from Sema during semantic analysis.
|
||||
/// Does not attempt to load the file from disk; just returns a corresponding `*Zcu.File`.
|
||||
pub fn importFile(
|
||||
pt: Zcu.PerThread,
|
||||
cur_file: *Zcu.File,
|
||||
import_string: []const u8,
|
||||
) !Zcu.ImportFileResult {
|
||||
) error{
|
||||
OutOfMemory,
|
||||
ModuleNotFound,
|
||||
ImportOutsideModulePath,
|
||||
CurrentWorkingDirectoryUnlinked,
|
||||
}!Zcu.ImportFileResult {
|
||||
const zcu = pt.zcu;
|
||||
const mod = cur_file.mod;
|
||||
|
||||
@ -2068,7 +2017,10 @@ pub fn importFile(
|
||||
defer gpa.free(resolved_root_path);
|
||||
|
||||
const sub_file_path = p: {
|
||||
const relative = try std.fs.path.relative(gpa, resolved_root_path, resolved_path);
|
||||
const relative = std.fs.path.relative(gpa, resolved_root_path, resolved_path) catch |err| switch (err) {
|
||||
error.Unexpected => unreachable,
|
||||
else => |e| return e,
|
||||
};
|
||||
errdefer gpa.free(relative);
|
||||
|
||||
if (!isUpDir(relative) and !std.fs.path.isAbsolute(relative)) {
|
||||
@ -2096,15 +2048,15 @@ pub fn importFile(
|
||||
gop.value_ptr.* = new_file_index;
|
||||
new_file.* = .{
|
||||
.sub_file_path = sub_file_path,
|
||||
.source = undefined,
|
||||
.source_loaded = false,
|
||||
.tree_loaded = false,
|
||||
.zir_loaded = false,
|
||||
.stat = undefined,
|
||||
.tree = undefined,
|
||||
.zir = undefined,
|
||||
|
||||
.status = .never_loaded,
|
||||
.prev_status = .never_loaded,
|
||||
.stat = undefined,
|
||||
|
||||
.source = null,
|
||||
.tree = null,
|
||||
.zir = null,
|
||||
.zoir = null,
|
||||
|
||||
.mod = mod,
|
||||
};
|
||||
|
||||
@ -2441,7 +2393,7 @@ const ScanDeclIter = struct {
|
||||
const namespace = zcu.namespacePtr(namespace_index);
|
||||
const gpa = zcu.gpa;
|
||||
const file = namespace.fileScope(zcu);
|
||||
const zir = file.zir;
|
||||
const zir = file.zir.?;
|
||||
const ip = &zcu.intern_pool;
|
||||
|
||||
const decl = zir.getDeclaration(decl_inst);
|
||||
@ -2591,7 +2543,7 @@ fn analyzeFnBodyInner(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaE
|
||||
const func = zcu.funcInfo(func_index);
|
||||
const inst_info = func.zir_body_inst.resolveFull(ip) orelse return error.AnalysisFail;
|
||||
const file = zcu.fileByIndex(inst_info.file);
|
||||
const zir = file.zir;
|
||||
const zir = file.zir.?;
|
||||
|
||||
try zcu.analysis_in_progress.put(gpa, anal_unit, {});
|
||||
errdefer _ = zcu.analysis_in_progress.swapRemove(anal_unit);
|
||||
@ -2843,11 +2795,32 @@ pub fn getErrorValueFromSlice(pt: Zcu.PerThread, name: []const u8) Allocator.Err
|
||||
/// Removes any entry from `Zcu.failed_files` associated with `file`. Acquires `Compilation.mutex` as needed.
|
||||
/// `file.zir` must be unchanged from the last update, as it is used to determine if there is such an entry.
|
||||
fn lockAndClearFileCompileError(pt: Zcu.PerThread, file: *Zcu.File) void {
|
||||
if (!file.zir_loaded or !file.zir.hasCompileErrors()) return;
|
||||
const maybe_has_error = switch (file.status) {
|
||||
.never_loaded => false,
|
||||
.retryable_failure => true,
|
||||
.astgen_failure => true,
|
||||
.success => switch (file.getMode()) {
|
||||
.zig => has_error: {
|
||||
const zir = file.zir orelse break :has_error false;
|
||||
break :has_error zir.hasCompileErrors();
|
||||
},
|
||||
.zon => has_error: {
|
||||
const zoir = file.zoir orelse break :has_error false;
|
||||
break :has_error zoir.hasCompileErrors();
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// If runtime safety is on, let's quickly lock the mutex and check anyway.
|
||||
if (!maybe_has_error and !std.debug.runtime_safety) {
|
||||
return;
|
||||
}
|
||||
|
||||
pt.zcu.comp.mutex.lock();
|
||||
defer pt.zcu.comp.mutex.unlock();
|
||||
if (pt.zcu.failed_files.fetchSwapRemove(file)) |kv| {
|
||||
if (kv.value) |msg| msg.destroy(pt.zcu.gpa); // Delete previous error message.
|
||||
assert(maybe_has_error); // the runtime safety case above
|
||||
if (kv.value) |msg| msg.destroy(pt.zcu.gpa); // delete previous error message
|
||||
}
|
||||
}
|
||||
|
||||
@ -3203,6 +3176,7 @@ pub fn linkerUpdateLineNumber(pt: Zcu.PerThread, ti: InternPool.TrackedInst.Inde
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets `File.status` of `file_index` to `retryable_failure`, and stores an error in `pt.zcu.failed_files`.
|
||||
pub fn reportRetryableAstGenError(
|
||||
pt: Zcu.PerThread,
|
||||
src: Zcu.AstGenSrc,
|
||||
@ -3238,13 +3212,18 @@ pub fn reportRetryableAstGenError(
|
||||
});
|
||||
errdefer err_msg.destroy(gpa);
|
||||
|
||||
{
|
||||
zcu.comp.mutex.lock();
|
||||
defer zcu.comp.mutex.unlock();
|
||||
try zcu.failed_files.putNoClobber(gpa, file, err_msg);
|
||||
zcu.comp.mutex.lock();
|
||||
defer zcu.comp.mutex.unlock();
|
||||
const gop = try zcu.failed_files.getOrPut(gpa, file);
|
||||
if (gop.found_existing) {
|
||||
if (gop.value_ptr.*) |old_err_msg| {
|
||||
old_err_msg.destroy(gpa);
|
||||
}
|
||||
}
|
||||
gop.value_ptr.* = err_msg;
|
||||
}
|
||||
|
||||
/// Sets `File.status` of `file_index` to `retryable_failure`, and stores an error in `pt.zcu.failed_files`.
|
||||
pub fn reportRetryableFileError(
|
||||
pt: Zcu.PerThread,
|
||||
file_index: Zcu.File.Index,
|
||||
@ -3778,8 +3757,7 @@ fn recreateStructType(
|
||||
|
||||
const inst_info = key.zir_index.resolveFull(ip).?;
|
||||
const file = zcu.fileByIndex(inst_info.file);
|
||||
assert(file.status == .success_zir); // otherwise inst tracking failed
|
||||
const zir = file.zir;
|
||||
const zir = file.zir.?;
|
||||
|
||||
assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended);
|
||||
const extended = zir.instructions.items(.data)[@intFromEnum(inst_info.inst)].extended;
|
||||
@ -3851,8 +3829,7 @@ fn recreateUnionType(
|
||||
|
||||
const inst_info = key.zir_index.resolveFull(ip).?;
|
||||
const file = zcu.fileByIndex(inst_info.file);
|
||||
assert(file.status == .success_zir); // otherwise inst tracking failed
|
||||
const zir = file.zir;
|
||||
const zir = file.zir.?;
|
||||
|
||||
assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended);
|
||||
const extended = zir.instructions.items(.data)[@intFromEnum(inst_info.inst)].extended;
|
||||
@ -3938,8 +3915,7 @@ fn recreateEnumType(
|
||||
|
||||
const inst_info = key.zir_index.resolveFull(ip).?;
|
||||
const file = zcu.fileByIndex(inst_info.file);
|
||||
assert(file.status == .success_zir); // otherwise inst tracking failed
|
||||
const zir = file.zir;
|
||||
const zir = file.zir.?;
|
||||
|
||||
assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended);
|
||||
const extended = zir.instructions.items(.data)[@intFromEnum(inst_info.inst)].extended;
|
||||
@ -4082,8 +4058,7 @@ pub fn ensureNamespaceUpToDate(pt: Zcu.PerThread, namespace_index: Zcu.Namespace
|
||||
|
||||
const inst_info = key.zir_index.resolveFull(ip) orelse return error.AnalysisFail;
|
||||
const file = zcu.fileByIndex(inst_info.file);
|
||||
if (file.status != .success_zir) return error.AnalysisFail;
|
||||
const zir = file.zir;
|
||||
const zir = file.zir.?;
|
||||
|
||||
assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended);
|
||||
const extended = zir.instructions.items(.data)[@intFromEnum(inst_info.inst)].extended;
|
||||
|
||||
@ -750,8 +750,7 @@ pub const File = struct {
|
||||
{
|
||||
const ti = ti_id.resolveFull(&pt.zcu.intern_pool).?;
|
||||
const file = pt.zcu.fileByIndex(ti.file);
|
||||
assert(file.zir_loaded);
|
||||
const inst = file.zir.instructions.get(@intFromEnum(ti.inst));
|
||||
const inst = file.zir.?.instructions.get(@intFromEnum(ti.inst));
|
||||
assert(inst.tag == .declaration);
|
||||
}
|
||||
|
||||
|
||||
@ -2358,8 +2358,7 @@ fn initWipNavInner(
|
||||
const nav = ip.getNav(nav_index);
|
||||
const inst_info = nav.srcInst(ip).resolveFull(ip).?;
|
||||
const file = zcu.fileByIndex(inst_info.file);
|
||||
assert(file.zir_loaded);
|
||||
const decl = file.zir.getDeclaration(inst_info.inst);
|
||||
const decl = file.zir.?.getDeclaration(inst_info.inst);
|
||||
log.debug("initWipNav({s}:{d}:{d} %{d} = {})", .{
|
||||
file.sub_file_path,
|
||||
decl.src_line + 1,
|
||||
@ -2373,7 +2372,7 @@ fn initWipNavInner(
|
||||
switch (nav_key) {
|
||||
// Ignore @extern
|
||||
.@"extern" => |@"extern"| if (decl.linkage != .@"extern" or
|
||||
!@"extern".name.eqlSlice(file.zir.nullTerminatedString(decl.name), ip)) return null,
|
||||
!@"extern".name.eqlSlice(file.zir.?.nullTerminatedString(decl.name), ip)) return null,
|
||||
else => {},
|
||||
}
|
||||
|
||||
@ -2696,8 +2695,7 @@ fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPoo
|
||||
const nav = ip.getNav(nav_index);
|
||||
const inst_info = nav.srcInst(ip).resolveFull(ip).?;
|
||||
const file = zcu.fileByIndex(inst_info.file);
|
||||
assert(file.zir_loaded);
|
||||
const decl = file.zir.getDeclaration(inst_info.inst);
|
||||
const decl = file.zir.?.getDeclaration(inst_info.inst);
|
||||
log.debug("updateComptimeNav({s}:{d}:{d} %{d} = {})", .{
|
||||
file.sub_file_path,
|
||||
decl.src_line + 1,
|
||||
@ -4097,7 +4095,7 @@ pub fn updateContainerType(dwarf: *Dwarf, pt: Zcu.PerThread, type_index: InternP
|
||||
// if a newly-tracked instruction can be a type's owner `zir_index`.
|
||||
comptime assert(Zir.inst_tracking_version == 0);
|
||||
|
||||
const decl_inst = file.zir.instructions.get(@intFromEnum(inst_info.inst));
|
||||
const decl_inst = file.zir.?.instructions.get(@intFromEnum(inst_info.inst));
|
||||
const name_strat: Zir.Inst.NameStrategy = switch (decl_inst.tag) {
|
||||
.struct_init, .struct_init_ref, .struct_init_anon => .anon,
|
||||
.extended => switch (decl_inst.data.extended.opcode) {
|
||||
@ -4301,14 +4299,13 @@ pub fn updateLineNumber(dwarf: *Dwarf, zcu: *Zcu, zir_index: InternPool.TrackedI
|
||||
const inst_info = zir_index.resolveFull(ip).?;
|
||||
assert(inst_info.inst != .main_struct_inst);
|
||||
const file = zcu.fileByIndex(inst_info.file);
|
||||
assert(file.zir_loaded);
|
||||
const decl = file.zir.getDeclaration(inst_info.inst);
|
||||
const decl = file.zir.?.getDeclaration(inst_info.inst);
|
||||
log.debug("updateLineNumber({s}:{d}:{d} %{d} = {s})", .{
|
||||
file.sub_file_path,
|
||||
decl.src_line + 1,
|
||||
decl.src_column + 1,
|
||||
@intFromEnum(inst_info.inst),
|
||||
file.zir.nullTerminatedString(decl.name),
|
||||
file.zir.?.nullTerminatedString(decl.name),
|
||||
});
|
||||
|
||||
var line_buf: [4]u8 = undefined;
|
||||
@ -4661,7 +4658,7 @@ pub fn flushModule(dwarf: *Dwarf, pt: Zcu.PerThread) FlushError!void {
|
||||
.target_unit = StringSection.unit,
|
||||
.target_entry = (try dwarf.debug_line_str.addString(
|
||||
dwarf,
|
||||
if (file.mod.builtin_file == file) file.source else "",
|
||||
if (file.mod.builtin_file == file) file.source.? else "",
|
||||
)).toOptional(),
|
||||
});
|
||||
header.appendNTimesAssumeCapacity(0, dwarf.sectionOffsetBytes());
|
||||
|
||||
115
src/main.zig
115
src/main.zig
@ -3636,7 +3636,7 @@ fn buildOutputType(
|
||||
|
||||
if (show_builtin) {
|
||||
const builtin_mod = comp.root_mod.getBuiltinDependency();
|
||||
const source = builtin_mod.builtin_file.?.source;
|
||||
const source = builtin_mod.builtin_file.?.source.?;
|
||||
return std.io.getStdOut().writeAll(source);
|
||||
}
|
||||
switch (listen) {
|
||||
@ -6134,15 +6134,12 @@ fn cmdAstCheck(
|
||||
|
||||
var file: Zcu.File = .{
|
||||
.status = .never_loaded,
|
||||
.prev_status = .never_loaded,
|
||||
.source_loaded = false,
|
||||
.tree_loaded = false,
|
||||
.zir_loaded = false,
|
||||
.sub_file_path = undefined,
|
||||
.source = undefined,
|
||||
.stat = undefined,
|
||||
.tree = undefined,
|
||||
.zir = undefined,
|
||||
.source = null,
|
||||
.tree = null,
|
||||
.zir = null,
|
||||
.zoir = null,
|
||||
.mod = undefined,
|
||||
};
|
||||
if (zig_source_file) |file_name| {
|
||||
@ -6163,7 +6160,6 @@ fn cmdAstCheck(
|
||||
|
||||
file.sub_file_path = file_name;
|
||||
file.source = source;
|
||||
file.source_loaded = true;
|
||||
file.stat = .{
|
||||
.size = stat.size,
|
||||
.inode = stat.inode,
|
||||
@ -6176,7 +6172,6 @@ fn cmdAstCheck(
|
||||
};
|
||||
file.sub_file_path = "<stdin>";
|
||||
file.source = source;
|
||||
file.source_loaded = true;
|
||||
file.stat.size = source.len;
|
||||
}
|
||||
|
||||
@ -6196,17 +6191,15 @@ fn cmdAstCheck(
|
||||
.fully_qualified_name = "root",
|
||||
});
|
||||
|
||||
file.tree = try Ast.parse(gpa, file.source, mode);
|
||||
file.tree_loaded = true;
|
||||
defer file.tree.deinit(gpa);
|
||||
file.tree = try Ast.parse(gpa, file.source.?, mode);
|
||||
defer file.tree.?.deinit(gpa);
|
||||
|
||||
switch (mode) {
|
||||
.zig => {
|
||||
file.zir = try AstGen.generate(gpa, file.tree);
|
||||
file.zir_loaded = true;
|
||||
defer file.zir.deinit(gpa);
|
||||
file.zir = try AstGen.generate(gpa, file.tree.?);
|
||||
defer file.zir.?.deinit(gpa);
|
||||
|
||||
if (file.zir.hasCompileErrors()) {
|
||||
if (file.zir.?.hasCompileErrors()) {
|
||||
var wip_errors: std.zig.ErrorBundle.Wip = undefined;
|
||||
try wip_errors.init(gpa);
|
||||
defer wip_errors.deinit();
|
||||
@ -6215,13 +6208,13 @@ fn cmdAstCheck(
|
||||
defer error_bundle.deinit(gpa);
|
||||
error_bundle.renderToStdErr(color.renderOptions());
|
||||
|
||||
if (file.zir.loweringFailed()) {
|
||||
if (file.zir.?.loweringFailed()) {
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (!want_output_text) {
|
||||
if (file.zir.hasCompileErrors()) {
|
||||
if (file.zir.?.hasCompileErrors()) {
|
||||
process.exit(1);
|
||||
} else {
|
||||
return cleanExit();
|
||||
@ -6233,18 +6226,18 @@ fn cmdAstCheck(
|
||||
|
||||
{
|
||||
const token_bytes = @sizeOf(Ast.TokenList) +
|
||||
file.tree.tokens.len * (@sizeOf(std.zig.Token.Tag) + @sizeOf(Ast.ByteOffset));
|
||||
const tree_bytes = @sizeOf(Ast) + file.tree.nodes.len *
|
||||
file.tree.?.tokens.len * (@sizeOf(std.zig.Token.Tag) + @sizeOf(Ast.ByteOffset));
|
||||
const tree_bytes = @sizeOf(Ast) + file.tree.?.nodes.len *
|
||||
(@sizeOf(Ast.Node.Tag) +
|
||||
@sizeOf(Ast.Node.Data) +
|
||||
@sizeOf(Ast.TokenIndex));
|
||||
const instruction_bytes = file.zir.instructions.len *
|
||||
const instruction_bytes = file.zir.?.instructions.len *
|
||||
// Here we don't use @sizeOf(Zir.Inst.Data) because it would include
|
||||
// the debug safety tag but we want to measure release size.
|
||||
(@sizeOf(Zir.Inst.Tag) + 8);
|
||||
const extra_bytes = file.zir.extra.len * @sizeOf(u32);
|
||||
const extra_bytes = file.zir.?.extra.len * @sizeOf(u32);
|
||||
const total_bytes = @sizeOf(Zir) + instruction_bytes + extra_bytes +
|
||||
file.zir.string_bytes.len * @sizeOf(u8);
|
||||
file.zir.?.string_bytes.len * @sizeOf(u8);
|
||||
const stdout = io.getStdOut();
|
||||
const fmtIntSizeBin = std.fmt.fmtIntSizeBin;
|
||||
// zig fmt: off
|
||||
@ -6258,27 +6251,27 @@ fn cmdAstCheck(
|
||||
\\# Extra Data Items: {d} ({})
|
||||
\\
|
||||
, .{
|
||||
fmtIntSizeBin(file.source.len),
|
||||
file.tree.tokens.len, fmtIntSizeBin(token_bytes),
|
||||
file.tree.nodes.len, fmtIntSizeBin(tree_bytes),
|
||||
fmtIntSizeBin(file.source.?.len),
|
||||
file.tree.?.tokens.len, fmtIntSizeBin(token_bytes),
|
||||
file.tree.?.nodes.len, fmtIntSizeBin(tree_bytes),
|
||||
fmtIntSizeBin(total_bytes),
|
||||
file.zir.instructions.len, fmtIntSizeBin(instruction_bytes),
|
||||
fmtIntSizeBin(file.zir.string_bytes.len),
|
||||
file.zir.extra.len, fmtIntSizeBin(extra_bytes),
|
||||
file.zir.?.instructions.len, fmtIntSizeBin(instruction_bytes),
|
||||
fmtIntSizeBin(file.zir.?.string_bytes.len),
|
||||
file.zir.?.extra.len, fmtIntSizeBin(extra_bytes),
|
||||
});
|
||||
// zig fmt: on
|
||||
}
|
||||
|
||||
try @import("print_zir.zig").renderAsTextToFile(gpa, &file, io.getStdOut());
|
||||
|
||||
if (file.zir.hasCompileErrors()) {
|
||||
if (file.zir.?.hasCompileErrors()) {
|
||||
process.exit(1);
|
||||
} else {
|
||||
return cleanExit();
|
||||
}
|
||||
},
|
||||
.zon => {
|
||||
const zoir = try ZonGen.generate(gpa, file.tree, .{});
|
||||
const zoir = try ZonGen.generate(gpa, file.tree.?, .{});
|
||||
defer zoir.deinit(gpa);
|
||||
|
||||
if (zoir.hasCompileErrors()) {
|
||||
@ -6289,7 +6282,7 @@ fn cmdAstCheck(
|
||||
{
|
||||
const src_path = try file.fullPath(gpa);
|
||||
defer gpa.free(src_path);
|
||||
try wip_errors.addZoirErrorMessages(zoir, file.tree, file.source, src_path);
|
||||
try wip_errors.addZoirErrorMessages(zoir, file.tree.?, file.source.?, src_path);
|
||||
}
|
||||
|
||||
var error_bundle = try wip_errors.toOwnedBundle("");
|
||||
@ -6518,27 +6511,24 @@ fn cmdDumpZir(
|
||||
|
||||
var file: Zcu.File = .{
|
||||
.status = .never_loaded,
|
||||
.prev_status = .never_loaded,
|
||||
.source_loaded = false,
|
||||
.tree_loaded = false,
|
||||
.zir_loaded = true,
|
||||
.sub_file_path = undefined,
|
||||
.source = undefined,
|
||||
.stat = undefined,
|
||||
.tree = undefined,
|
||||
.source = null,
|
||||
.tree = null,
|
||||
.zir = try Zcu.loadZirCache(gpa, f),
|
||||
.zoir = null,
|
||||
.mod = undefined,
|
||||
};
|
||||
defer file.zir.deinit(gpa);
|
||||
defer file.zir.?.deinit(gpa);
|
||||
|
||||
{
|
||||
const instruction_bytes = file.zir.instructions.len *
|
||||
const instruction_bytes = file.zir.?.instructions.len *
|
||||
// Here we don't use @sizeOf(Zir.Inst.Data) because it would include
|
||||
// the debug safety tag but we want to measure release size.
|
||||
(@sizeOf(Zir.Inst.Tag) + 8);
|
||||
const extra_bytes = file.zir.extra.len * @sizeOf(u32);
|
||||
const extra_bytes = file.zir.?.extra.len * @sizeOf(u32);
|
||||
const total_bytes = @sizeOf(Zir) + instruction_bytes + extra_bytes +
|
||||
file.zir.string_bytes.len * @sizeOf(u8);
|
||||
file.zir.?.string_bytes.len * @sizeOf(u8);
|
||||
const stdout = io.getStdOut();
|
||||
const fmtIntSizeBin = std.fmt.fmtIntSizeBin;
|
||||
// zig fmt: off
|
||||
@ -6550,9 +6540,9 @@ fn cmdDumpZir(
|
||||
\\
|
||||
, .{
|
||||
fmtIntSizeBin(total_bytes),
|
||||
file.zir.instructions.len, fmtIntSizeBin(instruction_bytes),
|
||||
fmtIntSizeBin(file.zir.string_bytes.len),
|
||||
file.zir.extra.len, fmtIntSizeBin(extra_bytes),
|
||||
file.zir.?.instructions.len, fmtIntSizeBin(instruction_bytes),
|
||||
fmtIntSizeBin(file.zir.?.string_bytes.len),
|
||||
file.zir.?.extra.len, fmtIntSizeBin(extra_bytes),
|
||||
});
|
||||
// zig fmt: on
|
||||
}
|
||||
@ -6586,19 +6576,16 @@ fn cmdChangelist(
|
||||
|
||||
var file: Zcu.File = .{
|
||||
.status = .never_loaded,
|
||||
.prev_status = .never_loaded,
|
||||
.source_loaded = false,
|
||||
.tree_loaded = false,
|
||||
.zir_loaded = false,
|
||||
.sub_file_path = old_source_file,
|
||||
.source = undefined,
|
||||
.stat = .{
|
||||
.size = stat.size,
|
||||
.inode = stat.inode,
|
||||
.mtime = stat.mtime,
|
||||
},
|
||||
.tree = undefined,
|
||||
.zir = undefined,
|
||||
.source = null,
|
||||
.tree = null,
|
||||
.zir = null,
|
||||
.zoir = null,
|
||||
.mod = undefined,
|
||||
};
|
||||
|
||||
@ -6613,17 +6600,14 @@ fn cmdChangelist(
|
||||
if (amt != stat.size)
|
||||
return error.UnexpectedEndOfFile;
|
||||
file.source = source;
|
||||
file.source_loaded = true;
|
||||
|
||||
file.tree = try Ast.parse(gpa, file.source, .zig);
|
||||
file.tree_loaded = true;
|
||||
defer file.tree.deinit(gpa);
|
||||
file.tree = try Ast.parse(gpa, file.source.?, .zig);
|
||||
defer file.tree.?.deinit(gpa);
|
||||
|
||||
file.zir = try AstGen.generate(gpa, file.tree);
|
||||
file.zir_loaded = true;
|
||||
defer file.zir.deinit(gpa);
|
||||
file.zir = try AstGen.generate(gpa, file.tree.?);
|
||||
defer file.zir.?.deinit(gpa);
|
||||
|
||||
if (file.zir.loweringFailed()) {
|
||||
if (file.zir.?.loweringFailed()) {
|
||||
var wip_errors: std.zig.ErrorBundle.Wip = undefined;
|
||||
try wip_errors.init(gpa);
|
||||
defer wip_errors.deinit();
|
||||
@ -6652,13 +6636,12 @@ fn cmdChangelist(
|
||||
var new_tree = try Ast.parse(gpa, new_source, .zig);
|
||||
defer new_tree.deinit(gpa);
|
||||
|
||||
var old_zir = file.zir;
|
||||
var old_zir = file.zir.?;
|
||||
defer old_zir.deinit(gpa);
|
||||
file.zir_loaded = false;
|
||||
file.zir = null;
|
||||
file.zir = try AstGen.generate(gpa, new_tree);
|
||||
file.zir_loaded = true;
|
||||
|
||||
if (file.zir.loweringFailed()) {
|
||||
if (file.zir.?.loweringFailed()) {
|
||||
var wip_errors: std.zig.ErrorBundle.Wip = undefined;
|
||||
try wip_errors.init(gpa);
|
||||
defer wip_errors.deinit();
|
||||
@ -6672,7 +6655,7 @@ fn cmdChangelist(
|
||||
var inst_map: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .empty;
|
||||
defer inst_map.deinit(gpa);
|
||||
|
||||
try Zcu.mapOldZirToNew(gpa, old_zir, file.zir, &inst_map);
|
||||
try Zcu.mapOldZirToNew(gpa, old_zir, file.zir.?, &inst_map);
|
||||
|
||||
var bw = io.bufferedWriter(io.getStdOut().writer());
|
||||
const stdout = bw.writer();
|
||||
|
||||
@ -22,7 +22,7 @@ pub fn renderAsTextToFile(
|
||||
.gpa = gpa,
|
||||
.arena = arena.allocator(),
|
||||
.file = scope_file,
|
||||
.code = scope_file.zir,
|
||||
.code = scope_file.zir.?,
|
||||
.indent = 0,
|
||||
.parent_decl_node = 0,
|
||||
.recurse_decls = true,
|
||||
@ -36,18 +36,18 @@ pub fn renderAsTextToFile(
|
||||
try stream.print("%{d} ", .{@intFromEnum(main_struct_inst)});
|
||||
try writer.writeInstToStream(stream, main_struct_inst);
|
||||
try stream.writeAll("\n");
|
||||
const imports_index = scope_file.zir.extra[@intFromEnum(Zir.ExtraIndex.imports)];
|
||||
const imports_index = scope_file.zir.?.extra[@intFromEnum(Zir.ExtraIndex.imports)];
|
||||
if (imports_index != 0) {
|
||||
try stream.writeAll("Imports:\n");
|
||||
|
||||
const extra = scope_file.zir.extraData(Zir.Inst.Imports, imports_index);
|
||||
const extra = scope_file.zir.?.extraData(Zir.Inst.Imports, imports_index);
|
||||
var extra_index = extra.end;
|
||||
|
||||
for (0..extra.data.imports_len) |_| {
|
||||
const item = scope_file.zir.extraData(Zir.Inst.Imports.Item, extra_index);
|
||||
const item = scope_file.zir.?.extraData(Zir.Inst.Imports.Item, extra_index);
|
||||
extra_index = item.end;
|
||||
|
||||
const import_path = scope_file.zir.nullTerminatedString(item.data.name);
|
||||
const import_path = scope_file.zir.?.nullTerminatedString(item.data.name);
|
||||
try stream.print(" @import(\"{}\") ", .{
|
||||
std.zig.fmtEscapes(import_path),
|
||||
});
|
||||
@ -75,7 +75,7 @@ pub fn renderInstructionContext(
|
||||
.gpa = gpa,
|
||||
.arena = arena.allocator(),
|
||||
.file = scope_file,
|
||||
.code = scope_file.zir,
|
||||
.code = scope_file.zir.?,
|
||||
.indent = if (indent < 2) 2 else indent,
|
||||
.parent_decl_node = parent_decl_node,
|
||||
.recurse_decls = false,
|
||||
@ -107,7 +107,7 @@ pub fn renderSingleInstruction(
|
||||
.gpa = gpa,
|
||||
.arena = arena.allocator(),
|
||||
.file = scope_file,
|
||||
.code = scope_file.zir,
|
||||
.code = scope_file.zir.?,
|
||||
.indent = indent,
|
||||
.parent_decl_node = parent_decl_node,
|
||||
.recurse_decls = false,
|
||||
@ -2759,8 +2759,7 @@ const Writer = struct {
|
||||
}
|
||||
|
||||
fn writeSrcNode(self: *Writer, stream: anytype, src_node: i32) !void {
|
||||
if (!self.file.tree_loaded) return;
|
||||
const tree = self.file.tree;
|
||||
const tree = self.file.tree orelse return;
|
||||
const abs_node = self.relativeToNodeIndex(src_node);
|
||||
const src_span = tree.nodeToSpan(abs_node);
|
||||
const start = self.line_col_cursor.find(tree.source, src_span.start);
|
||||
@ -2772,8 +2771,7 @@ const Writer = struct {
|
||||
}
|
||||
|
||||
fn writeSrcTok(self: *Writer, stream: anytype, src_tok: u32) !void {
|
||||
if (!self.file.tree_loaded) return;
|
||||
const tree = self.file.tree;
|
||||
const tree = self.file.tree orelse return;
|
||||
const abs_tok = tree.firstToken(self.parent_decl_node) + src_tok;
|
||||
const span_start = tree.tokens.items(.start)[abs_tok];
|
||||
const span_end = span_start + @as(u32, @intCast(tree.tokenSlice(abs_tok).len));
|
||||
@ -2786,8 +2784,7 @@ const Writer = struct {
|
||||
}
|
||||
|
||||
fn writeSrcTokAbs(self: *Writer, stream: anytype, src_tok: u32) !void {
|
||||
if (!self.file.tree_loaded) return;
|
||||
const tree = self.file.tree;
|
||||
const tree = self.file.tree orelse return;
|
||||
const span_start = tree.tokens.items(.start)[src_tok];
|
||||
const span_end = span_start + @as(u32, @intCast(tree.tokenSlice(src_tok).len));
|
||||
const start = self.line_col_cursor.find(tree.source, span_start);
|
||||
|
||||
@ -1,9 +0,0 @@
|
||||
export fn entry() void {
|
||||
_ = @import(
|
||||
"bogus-does-not-exist.zon",
|
||||
);
|
||||
}
|
||||
|
||||
// error
|
||||
//
|
||||
// :3:9: error: unable to open 'bogus-does-not-exist.zon': FileNotFound
|
||||
46
test/incremental/change_zon_file
Normal file
46
test/incremental/change_zon_file
Normal file
@ -0,0 +1,46 @@
|
||||
#target=x86_64-linux-selfhosted
|
||||
#target=x86_64-linux-cbe
|
||||
#target=x86_64-windows-cbe
|
||||
//#target=wasm32-wasi-selfhosted
|
||||
#update=initial version
|
||||
#file=main.zig
|
||||
const std = @import("std");
|
||||
const message: []const u8 = @import("message.zon");
|
||||
pub fn main() !void {
|
||||
try std.io.getStdOut().writeAll(message);
|
||||
}
|
||||
#file=message.zon
|
||||
"Hello, World!\n"
|
||||
#expect_stdout="Hello, World!\n"
|
||||
|
||||
#update=change ZON file contents
|
||||
#file=message.zon
|
||||
"Hello again, World!\n"
|
||||
#expect_stdout="Hello again, World!\n"
|
||||
|
||||
#update=delete file
|
||||
#rm_file=message.zon
|
||||
#expect_error=message.zon:1:1: error: unable to load './message.zon': FileNotFound
|
||||
|
||||
#update=remove reference to ZON file
|
||||
#file=main.zig
|
||||
const std = @import("std");
|
||||
const message: []const u8 = @import("message.zon");
|
||||
pub fn main() !void {
|
||||
try std.io.getStdOut().writeAll("a hardcoded string\n");
|
||||
}
|
||||
#expect_error=message.zon:1:1: error: unable to load './message.zon': FileNotFound
|
||||
|
||||
#update=recreate ZON file
|
||||
#file=message.zon
|
||||
"We're back, World!\n"
|
||||
#expect_stdout="a hardcoded string\n"
|
||||
|
||||
#update=re-introduce reference to ZON file
|
||||
#file=main.zig
|
||||
const std = @import("std");
|
||||
const message: []const u8 = @import("message.zon");
|
||||
pub fn main() !void {
|
||||
try std.io.getStdOut().writeAll(message);
|
||||
}
|
||||
#expect_stdout="We're back, World!\n"
|
||||
Loading…
x
Reference in New Issue
Block a user