| author | |
| committer | |
| log | 22539783ad15be3028ed07983eb8e23910171f11 |
| tree | 0ace6676df00397e4ca4df098013220f95e85bf7 |
| parent | c6842b58d488c236aca74dea82082eec365eb117 |
| signature |
The re-analysis here is a little coarse; it'd be nice in the future to
have a way for an AstGen failure to preserve *all* analysis which
depends on the last success, and just hide the compile errors which
depend on it somehow. But I'm not sure how we'd achieve that, so this
works fine for now.
Resolves: #212238 files changed, 97 insertions(+), 23 deletions(-)
src/Compilation.zig+4| ... | @@ -2901,6 +2901,7 @@ pub fn makeBinFileWritable(comp: *Compilation) !void { | ... | @@ -2901,6 +2901,7 @@ pub fn makeBinFileWritable(comp: *Compilation) !void { |
| 2901 | const Header = extern struct { | 2901 | const Header = extern struct { |
| 2902 | intern_pool: extern struct { | 2902 | intern_pool: extern struct { |
| 2903 | thread_count: u32, | 2903 | thread_count: u32, |
| 2904 | file_deps_len: u32, | ||
| 2904 | src_hash_deps_len: u32, | 2905 | src_hash_deps_len: u32, |
| 2905 | nav_val_deps_len: u32, | 2906 | nav_val_deps_len: u32, |
| 2906 | namespace_deps_len: u32, | 2907 | namespace_deps_len: u32, |
| ... | @@ -2943,6 +2944,7 @@ pub fn saveState(comp: *Compilation) !void { | ... | @@ -2943,6 +2944,7 @@ pub fn saveState(comp: *Compilation) !void { |
| 2943 | const header: Header = .{ | 2944 | const header: Header = .{ |
| 2944 | .intern_pool = .{ | 2945 | .intern_pool = .{ |
| 2945 | .thread_count = @intCast(ip.locals.len), | 2946 | .thread_count = @intCast(ip.locals.len), |
| 2947 | .file_deps_len = @intCast(ip.file_deps.count()), | ||
| 2946 | .src_hash_deps_len = @intCast(ip.src_hash_deps.count()), | 2948 | .src_hash_deps_len = @intCast(ip.src_hash_deps.count()), |
| 2947 | .nav_val_deps_len = @intCast(ip.nav_val_deps.count()), | 2949 | .nav_val_deps_len = @intCast(ip.nav_val_deps.count()), |
| 2948 | .namespace_deps_len = @intCast(ip.namespace_deps.count()), | 2950 | .namespace_deps_len = @intCast(ip.namespace_deps.count()), |
| ... | @@ -2969,6 +2971,8 @@ pub fn saveState(comp: *Compilation) !void { | ... | @@ -2969,6 +2971,8 @@ pub fn saveState(comp: *Compilation) !void { |
| 2969 | addBuf(&bufs, mem.asBytes(&header)); | 2971 | addBuf(&bufs, mem.asBytes(&header)); |
| 2970 | addBuf(&bufs, mem.sliceAsBytes(pt_headers.items)); | 2972 | addBuf(&bufs, mem.sliceAsBytes(pt_headers.items)); |
| 2971 | 2973 | ||
| 2974 | addBuf(&bufs, mem.sliceAsBytes(ip.file_deps.keys())); | ||
| 2975 | addBuf(&bufs, mem.sliceAsBytes(ip.file_deps.values())); | ||
| 2972 | addBuf(&bufs, mem.sliceAsBytes(ip.src_hash_deps.keys())); | 2976 | addBuf(&bufs, mem.sliceAsBytes(ip.src_hash_deps.keys())); |
| 2973 | addBuf(&bufs, mem.sliceAsBytes(ip.src_hash_deps.values())); | 2977 | addBuf(&bufs, mem.sliceAsBytes(ip.src_hash_deps.values())); |
| 2974 | addBuf(&bufs, mem.sliceAsBytes(ip.nav_val_deps.keys())); | 2978 | addBuf(&bufs, mem.sliceAsBytes(ip.nav_val_deps.keys())); |
src/InternPool.zig+12| ... | @@ -17,6 +17,13 @@ tid_shift_31: if (single_threaded) u0 else std.math.Log2Int(u32), | ... | @@ -17,6 +17,13 @@ tid_shift_31: if (single_threaded) u0 else std.math.Log2Int(u32), |
| 17 | /// Cached shift amount to put a `tid` in the top bits of a 32-bit value. | 17 | /// Cached shift amount to put a `tid` in the top bits of a 32-bit value. |
| 18 | tid_shift_32: if (single_threaded) u0 else std.math.Log2Int(u32), | 18 | tid_shift_32: if (single_threaded) u0 else std.math.Log2Int(u32), |
| 19 | 19 | ||
| 20 | /// Dependencies on whether an entire file gets past AstGen. | ||
| 21 | /// These are triggered by `@import`, so that: | ||
| 22 | /// * if a file initially fails AstGen, triggering a transitive failure, when a future update | ||
| 23 | /// causes it to succeed AstGen, the `@import` is re-analyzed, allowing analysis to proceed | ||
| 24 | /// * if a file initially succeds AstGen, but a future update causes the file to fail it, | ||
| 25 | /// the `@import` is re-analyzed, registering a transitive failure | ||
| 26 | file_deps: std.AutoArrayHashMapUnmanaged(FileIndex, DepEntry.Index), | ||
| 20 | /// Dependencies on the source code hash associated with a ZIR instruction. | 27 | /// Dependencies on the source code hash associated with a ZIR instruction. |
| 21 | /// * For a `declaration`, this is the entire declaration body. | 28 | /// * For a `declaration`, this is the entire declaration body. |
| 22 | /// * For a `struct_decl`, `union_decl`, etc, this is the source of the fields (but not declarations). | 29 | /// * For a `struct_decl`, `union_decl`, etc, this is the source of the fields (but not declarations). |
| ... | @@ -70,6 +77,7 @@ pub const empty: InternPool = .{ | ... | @@ -70,6 +77,7 @@ pub const empty: InternPool = .{ |
| 70 | .tid_shift_30 = if (single_threaded) 0 else 31, | 77 | .tid_shift_30 = if (single_threaded) 0 else 31, |
| 71 | .tid_shift_31 = if (single_threaded) 0 else 31, | 78 | .tid_shift_31 = if (single_threaded) 0 else 31, |
| 72 | .tid_shift_32 = if (single_threaded) 0 else 31, | 79 | .tid_shift_32 = if (single_threaded) 0 else 31, |
| 80 | .file_deps = .empty, | ||
| 73 | .src_hash_deps = .empty, | 81 | .src_hash_deps = .empty, |
| 74 | .nav_val_deps = .empty, | 82 | .nav_val_deps = .empty, |
| 75 | .interned_deps = .empty, | 83 | .interned_deps = .empty, |
| ... | @@ -656,6 +664,7 @@ pub const Nav = struct { | ... | @@ -656,6 +664,7 @@ pub const Nav = struct { |
| 656 | }; | 664 | }; |
| 657 | 665 | ||
| 658 | pub const Dependee = union(enum) { | 666 | pub const Dependee = union(enum) { |
| 667 | file: FileIndex, | ||
| 659 | src_hash: TrackedInst.Index, | 668 | src_hash: TrackedInst.Index, |
| 660 | nav_val: Nav.Index, | 669 | nav_val: Nav.Index, |
| 661 | interned: Index, | 670 | interned: Index, |
| ... | @@ -704,6 +713,7 @@ pub const DependencyIterator = struct { | ... | @@ -704,6 +713,7 @@ pub const DependencyIterator = struct { |
| 704 | 713 | ||
| 705 | pub fn dependencyIterator(ip: *const InternPool, dependee: Dependee) DependencyIterator { | 714 | pub fn dependencyIterator(ip: *const InternPool, dependee: Dependee) DependencyIterator { |
| 706 | const first_entry = switch (dependee) { | 715 | const first_entry = switch (dependee) { |
| 716 | .file => |x| ip.file_deps.get(x), | ||
| 707 | .src_hash => |x| ip.src_hash_deps.get(x), | 717 | .src_hash => |x| ip.src_hash_deps.get(x), |
| 708 | .nav_val => |x| ip.nav_val_deps.get(x), | 718 | .nav_val => |x| ip.nav_val_deps.get(x), |
| 709 | .interned => |x| ip.interned_deps.get(x), | 719 | .interned => |x| ip.interned_deps.get(x), |
| ... | @@ -740,6 +750,7 @@ pub fn addDependency(ip: *InternPool, gpa: Allocator, depender: AnalUnit, depend | ... | @@ -740,6 +750,7 @@ pub fn addDependency(ip: *InternPool, gpa: Allocator, depender: AnalUnit, depend |
| 740 | const new_index: DepEntry.Index = switch (dependee) { | 750 | const new_index: DepEntry.Index = switch (dependee) { |
| 741 | inline else => |dependee_payload, tag| new_index: { | 751 | inline else => |dependee_payload, tag| new_index: { |
| 742 | const gop = try switch (tag) { | 752 | const gop = try switch (tag) { |
| 753 | .file => ip.file_deps, | ||
| 743 | .src_hash => ip.src_hash_deps, | 754 | .src_hash => ip.src_hash_deps, |
| 744 | .nav_val => ip.nav_val_deps, | 755 | .nav_val => ip.nav_val_deps, |
| 745 | .interned => ip.interned_deps, | 756 | .interned => ip.interned_deps, |
| ... | @@ -6268,6 +6279,7 @@ pub fn init(ip: *InternPool, gpa: Allocator, available_threads: usize) !void { | ... | @@ -6268,6 +6279,7 @@ pub fn init(ip: *InternPool, gpa: Allocator, available_threads: usize) !void { |
| 6268 | } | 6279 | } |
| 6269 | 6280 | ||
| 6270 | pub fn deinit(ip: *InternPool, gpa: Allocator) void { | 6281 | pub fn deinit(ip: *InternPool, gpa: Allocator) void { |
| 6282 | ip.file_deps.deinit(gpa); | ||
| 6271 | ip.src_hash_deps.deinit(gpa); | 6283 | ip.src_hash_deps.deinit(gpa); |
| 6272 | ip.nav_val_deps.deinit(gpa); | 6284 | ip.nav_val_deps.deinit(gpa); |
| 6273 | ip.interned_deps.deinit(gpa); | 6285 | ip.interned_deps.deinit(gpa); |
src/Package/Module.zig+1| ... | @@ -454,6 +454,7 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module { | ... | @@ -454,6 +454,7 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module { |
| 454 | .tree = undefined, | 454 | .tree = undefined, |
| 455 | .zir = undefined, | 455 | .zir = undefined, |
| 456 | .status = .never_loaded, | 456 | .status = .never_loaded, |
| 457 | .prev_status = .never_loaded, | ||
| 457 | .mod = new, | 458 | .mod = new, |
| 458 | }; | 459 | }; |
| 459 | break :b new; | 460 | break :b new; |
src/Sema.zig+2-6| ... | @@ -6024,9 +6024,7 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr | ... | @@ -6024,9 +6024,7 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr |
| 6024 | pt.astGenFile(result.file, path_digest) catch |err| | 6024 | pt.astGenFile(result.file, path_digest) catch |err| |
| 6025 | return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)}); | 6025 | return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)}); |
| 6026 | 6026 | ||
| 6027 | // TODO: register some kind of dependency on the file. | 6027 | try sema.declareDependency(.{ .file = result.file_index }); |
| 6028 | // That way, if this returns `error.AnalysisFail`, we have the dependency banked ready to | ||
| 6029 | // trigger re-analysis later. | ||
| 6030 | try pt.ensureFileAnalyzed(result.file_index); | 6028 | try pt.ensureFileAnalyzed(result.file_index); |
| 6031 | const ty = zcu.fileRootType(result.file_index); | 6029 | const ty = zcu.fileRootType(result.file_index); |
| 6032 | try sema.declareDependency(.{ .interned = ty }); | 6030 | try sema.declareDependency(.{ .interned = ty }); |
| ... | @@ -14347,9 +14345,7 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. | ... | @@ -14347,9 +14345,7 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. |
| 14347 | return sema.fail(block, operand_src, "unable to open '{s}': {s}", .{ operand, @errorName(err) }); | 14345 | return sema.fail(block, operand_src, "unable to open '{s}': {s}", .{ operand, @errorName(err) }); |
| 14348 | }, | 14346 | }, |
| 14349 | }; | 14347 | }; |
| 14350 | // TODO: register some kind of dependency on the file. | 14348 | try sema.declareDependency(.{ .file = result.file_index }); |
| 14351 | // That way, if this returns `error.AnalysisFail`, we have the dependency banked ready to | ||
| 14352 | // trigger re-analysis later. | ||
| 14353 | try pt.ensureFileAnalyzed(result.file_index); | 14349 | try pt.ensureFileAnalyzed(result.file_index); |
| 14354 | const ty = zcu.fileRootType(result.file_index); | 14350 | const ty = zcu.fileRootType(result.file_index); |
| 14355 | try sema.declareDependency(.{ .interned = ty }); | 14351 | try sema.declareDependency(.{ .interned = ty }); |
src/Zcu.zig+14-7| ... | @@ -424,13 +424,8 @@ pub const Namespace = struct { | ... | @@ -424,13 +424,8 @@ pub const Namespace = struct { |
| 424 | }; | 424 | }; |
| 425 | 425 | ||
| 426 | pub const File = struct { | 426 | pub const File = struct { |
| 427 | status: enum { | 427 | status: Status, |
| 428 | never_loaded, | 428 | prev_status: Status, |
| 429 | retryable_failure, | ||
| 430 | parse_failure, | ||
| 431 | astgen_failure, | ||
| 432 | success_zir, | ||
| 433 | }, | ||
| 434 | source_loaded: bool, | 429 | source_loaded: bool, |
| 435 | tree_loaded: bool, | 430 | tree_loaded: bool, |
| 436 | zir_loaded: bool, | 431 | zir_loaded: bool, |
| ... | @@ -458,6 +453,14 @@ pub const File = struct { | ... | @@ -458,6 +453,14 @@ pub const File = struct { |
| 458 | /// successful, this field is unloaded. | 453 | /// successful, this field is unloaded. |
| 459 | prev_zir: ?*Zir = null, | 454 | prev_zir: ?*Zir = null, |
| 460 | 455 | ||
| 456 | pub const Status = enum { | ||
| 457 | never_loaded, | ||
| 458 | retryable_failure, | ||
| 459 | parse_failure, | ||
| 460 | astgen_failure, | ||
| 461 | success_zir, | ||
| 462 | }; | ||
| 463 | |||
| 461 | /// A single reference to a file. | 464 | /// A single reference to a file. |
| 462 | pub const Reference = union(enum) { | 465 | pub const Reference = union(enum) { |
| 463 | /// The file is imported directly (i.e. not as a package) with @import. | 466 | /// The file is imported directly (i.e. not as a package) with @import. |
| ... | @@ -3474,6 +3477,10 @@ fn formatDependee(data: struct { dependee: InternPool.Dependee, zcu: *Zcu }, com | ... | @@ -3474,6 +3477,10 @@ fn formatDependee(data: struct { dependee: InternPool.Dependee, zcu: *Zcu }, com |
| 3474 | const zcu = data.zcu; | 3477 | const zcu = data.zcu; |
| 3475 | const ip = &zcu.intern_pool; | 3478 | const ip = &zcu.intern_pool; |
| 3476 | switch (data.dependee) { | 3479 | switch (data.dependee) { |
| 3480 | .file => |file| { | ||
| 3481 | const file_path = zcu.fileByIndex(file).sub_file_path; | ||
| 3482 | return writer.print("file('{s}')", .{file_path}); | ||
| 3483 | }, | ||
| 3477 | .src_hash => |ti| { | 3484 | .src_hash => |ti| { |
| 3478 | const info = ti.resolveFull(ip) orelse { | 3485 | const info = ti.resolveFull(ip) orelse { |
| 3479 | return writer.writeAll("inst(<lost>)"); | 3486 | return writer.writeAll("inst(<lost>)"); |
src/Zcu/PerThread.zig+26-10| ... | @@ -179,10 +179,10 @@ pub fn astGenFile( | ... | @@ -179,10 +179,10 @@ pub fn astGenFile( |
| 179 | .inode = header.stat_inode, | 179 | .inode = header.stat_inode, |
| 180 | .mtime = header.stat_mtime, | 180 | .mtime = header.stat_mtime, |
| 181 | }; | 181 | }; |
| 182 | file.prev_status = file.status; | ||
| 182 | file.status = .success_zir; | 183 | file.status = .success_zir; |
| 183 | log.debug("AstGen cached success: {s}", .{file.sub_file_path}); | 184 | log.debug("AstGen cached success: {s}", .{file.sub_file_path}); |
| 184 | 185 | ||
| 185 | // TODO don't report compile errors until Sema @importFile | ||
| 186 | if (file.zir.hasCompileErrors()) { | 186 | if (file.zir.hasCompileErrors()) { |
| 187 | { | 187 | { |
| 188 | comp.mutex.lock(); | 188 | comp.mutex.lock(); |
| ... | @@ -258,6 +258,7 @@ pub fn astGenFile( | ... | @@ -258,6 +258,7 @@ pub fn astGenFile( |
| 258 | // Any potential AST errors are converted to ZIR errors here. | 258 | // Any potential AST errors are converted to ZIR errors here. |
| 259 | file.zir = try AstGen.generate(gpa, file.tree); | 259 | file.zir = try AstGen.generate(gpa, file.tree); |
| 260 | file.zir_loaded = true; | 260 | file.zir_loaded = true; |
| 261 | file.prev_status = file.status; | ||
| 261 | file.status = .success_zir; | 262 | file.status = .success_zir; |
| 262 | log.debug("AstGen fresh success: {s}", .{file.sub_file_path}); | 263 | log.debug("AstGen fresh success: {s}", .{file.sub_file_path}); |
| 263 | 264 | ||
| ... | @@ -350,6 +351,9 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void { | ... | @@ -350,6 +351,9 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void { |
| 350 | defer cleanupUpdatedFiles(gpa, &updated_files); | 351 | defer cleanupUpdatedFiles(gpa, &updated_files); |
| 351 | for (zcu.import_table.values()) |file_index| { | 352 | for (zcu.import_table.values()) |file_index| { |
| 352 | const file = zcu.fileByIndex(file_index); | 353 | const file = zcu.fileByIndex(file_index); |
| 354 | if (file.prev_status != file.status and file.prev_status != .never_loaded) { | ||
| 355 | try zcu.markDependeeOutdated(.not_marked_po, .{ .file = file_index }); | ||
| 356 | } | ||
| 353 | const old_zir = file.prev_zir orelse continue; | 357 | const old_zir = file.prev_zir orelse continue; |
| 354 | const new_zir = file.zir; | 358 | const new_zir = file.zir; |
| 355 | const gop = try updated_files.getOrPut(gpa, file_index); | 359 | const gop = try updated_files.getOrPut(gpa, file_index); |
| ... | @@ -551,11 +555,13 @@ pub fn ensureCauAnalyzed(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) Zcu | ... | @@ -551,11 +555,13 @@ pub fn ensureCauAnalyzed(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) Zcu |
| 551 | const cau_outdated = zcu.outdated.swapRemove(anal_unit) or | 555 | const cau_outdated = zcu.outdated.swapRemove(anal_unit) or |
| 552 | zcu.potentially_outdated.swapRemove(anal_unit); | 556 | zcu.potentially_outdated.swapRemove(anal_unit); |
| 553 | 557 | ||
| 558 | const prev_failed = zcu.failed_analysis.contains(anal_unit) or zcu.transitive_failed_analysis.contains(anal_unit); | ||
| 559 | |||
| 554 | if (cau_outdated) { | 560 | if (cau_outdated) { |
| 555 | _ = zcu.outdated_ready.swapRemove(anal_unit); | 561 | _ = zcu.outdated_ready.swapRemove(anal_unit); |
| 556 | } else { | 562 | } else { |
| 557 | // We can trust the current information about this `Cau`. | 563 | // We can trust the current information about this `Cau`. |
| 558 | if (zcu.failed_analysis.contains(anal_unit) or zcu.transitive_failed_analysis.contains(anal_unit)) { | 564 | if (prev_failed) { |
| 559 | return error.AnalysisFail; | 565 | return error.AnalysisFail; |
| 560 | } | 566 | } |
| 561 | // If it wasn't failed and wasn't marked outdated, then either... | 567 | // If it wasn't failed and wasn't marked outdated, then either... |
| ... | @@ -578,9 +584,13 @@ pub fn ensureCauAnalyzed(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) Zcu | ... | @@ -578,9 +584,13 @@ pub fn ensureCauAnalyzed(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) Zcu |
| 578 | // Since it does not, this must be a transitive failure. | 584 | // Since it does not, this must be a transitive failure. |
| 579 | try zcu.transitive_failed_analysis.put(gpa, anal_unit, {}); | 585 | try zcu.transitive_failed_analysis.put(gpa, anal_unit, {}); |
| 580 | } | 586 | } |
| 581 | // We treat errors as up-to-date, since those uses would just trigger a transitive error. | 587 | // We consider this `Cau` to be outdated if: |
| 582 | // The exception is types, since type declarations may require re-analysis if the type, e.g. its captures, changed. | 588 | // * Previous analysis succeeded; in this case, we need to re-analyze dependants to ensure |
| 583 | const outdated = cau.owner.unwrap() == .type; | 589 | // they hit a transitive error here, rather than reporting a different error later (which |
| 590 | // may now be invalid). | ||
| 591 | // * The `Cau` is a type; in this case, the declaration site may require re-analysis to | ||
| 592 | // construct a valid type. | ||
| 593 | const outdated = !prev_failed or cau.owner.unwrap() == .type; | ||
| 584 | break :res .{ .{ | 594 | break :res .{ .{ |
| 585 | .invalidate_decl_val = outdated, | 595 | .invalidate_decl_val = outdated, |
| 586 | .invalidate_decl_ref = outdated, | 596 | .invalidate_decl_ref = outdated, |
| ... | @@ -597,10 +607,9 @@ pub fn ensureCauAnalyzed(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) Zcu | ... | @@ -597,10 +607,9 @@ pub fn ensureCauAnalyzed(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) Zcu |
| 597 | ); | 607 | ); |
| 598 | zcu.retryable_failures.appendAssumeCapacity(anal_unit); | 608 | zcu.retryable_failures.appendAssumeCapacity(anal_unit); |
| 599 | zcu.failed_analysis.putAssumeCapacityNoClobber(anal_unit, msg); | 609 | zcu.failed_analysis.putAssumeCapacityNoClobber(anal_unit, msg); |
| 600 | // We treat errors as up-to-date, since those uses would just trigger a transitive error | ||
| 601 | break :res .{ .{ | 610 | break :res .{ .{ |
| 602 | .invalidate_decl_val = false, | 611 | .invalidate_decl_val = true, |
| 603 | .invalidate_decl_ref = false, | 612 | .invalidate_decl_ref = true, |
| 604 | }, true }; | 613 | }, true }; |
| 605 | }, | 614 | }, |
| 606 | }; | 615 | }; |
| ... | @@ -707,11 +716,13 @@ pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: Inter | ... | @@ -707,11 +716,13 @@ pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: Inter |
| 707 | const func_outdated = zcu.outdated.swapRemove(anal_unit) or | 716 | const func_outdated = zcu.outdated.swapRemove(anal_unit) or |
| 708 | zcu.potentially_outdated.swapRemove(anal_unit); | 717 | zcu.potentially_outdated.swapRemove(anal_unit); |
| 709 | 718 | ||
| 719 | const prev_failed = zcu.failed_analysis.contains(anal_unit) or zcu.transitive_failed_analysis.contains(anal_unit); | ||
| 720 | |||
| 710 | if (func_outdated) { | 721 | if (func_outdated) { |
| 711 | _ = zcu.outdated_ready.swapRemove(anal_unit); | 722 | _ = zcu.outdated_ready.swapRemove(anal_unit); |
| 712 | } else { | 723 | } else { |
| 713 | // We can trust the current information about this function. | 724 | // We can trust the current information about this function. |
| 714 | if (zcu.failed_analysis.contains(anal_unit) or zcu.transitive_failed_analysis.contains(anal_unit)) { | 725 | if (prev_failed) { |
| 715 | return error.AnalysisFail; | 726 | return error.AnalysisFail; |
| 716 | } | 727 | } |
| 717 | switch (func.analysisUnordered(ip).state) { | 728 | switch (func.analysisUnordered(ip).state) { |
| ... | @@ -730,7 +741,10 @@ pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: Inter | ... | @@ -730,7 +741,10 @@ pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: Inter |
| 730 | // Since it does not, this must be a transitive failure. | 741 | // Since it does not, this must be a transitive failure. |
| 731 | try zcu.transitive_failed_analysis.put(gpa, anal_unit, {}); | 742 | try zcu.transitive_failed_analysis.put(gpa, anal_unit, {}); |
| 732 | } | 743 | } |
| 733 | break :res .{ false, true }; // we treat errors as up-to-date IES, since those uses would just trigger a transitive error | 744 | // We consider the IES to be outdated if the function previously succeeded analysis; in this case, |
| 745 | // we need to re-analyze dependants to ensure they hit a transitive error here, rather than reporting | ||
| 746 | // a different error later (which may now be invalid). | ||
| 747 | break :res .{ !prev_failed, true }; | ||
| 734 | }, | 748 | }, |
| 735 | error.OutOfMemory => return error.OutOfMemory, // TODO: graceful handling like `ensureCauAnalyzed` | 749 | error.OutOfMemory => return error.OutOfMemory, // TODO: graceful handling like `ensureCauAnalyzed` |
| 736 | }; | 750 | }; |
| ... | @@ -1445,6 +1459,7 @@ pub fn importPkg(pt: Zcu.PerThread, mod: *Module) !Zcu.ImportFileResult { | ... | @@ -1445,6 +1459,7 @@ pub fn importPkg(pt: Zcu.PerThread, mod: *Module) !Zcu.ImportFileResult { |
| 1445 | .tree = undefined, | 1459 | .tree = undefined, |
| 1446 | .zir = undefined, | 1460 | .zir = undefined, |
| 1447 | .status = .never_loaded, | 1461 | .status = .never_loaded, |
| 1462 | .prev_status = .never_loaded, | ||
| 1448 | .mod = mod, | 1463 | .mod = mod, |
| 1449 | }; | 1464 | }; |
| 1450 | 1465 | ||
| ... | @@ -1555,6 +1570,7 @@ pub fn importFile( | ... | @@ -1555,6 +1570,7 @@ pub fn importFile( |
| 1555 | .tree = undefined, | 1570 | .tree = undefined, |
| 1556 | .zir = undefined, | 1571 | .zir = undefined, |
| 1557 | .status = .never_loaded, | 1572 | .status = .never_loaded, |
| 1573 | .prev_status = .never_loaded, | ||
| 1558 | .mod = mod, | 1574 | .mod = mod, |
| 1559 | }; | 1575 | }; |
| 1560 | 1576 |
src/main.zig+3| ... | @@ -6118,6 +6118,7 @@ fn cmdAstCheck( | ... | @@ -6118,6 +6118,7 @@ fn cmdAstCheck( |
| 6118 | 6118 | ||
| 6119 | var file: Zcu.File = .{ | 6119 | var file: Zcu.File = .{ |
| 6120 | .status = .never_loaded, | 6120 | .status = .never_loaded, |
| 6121 | .prev_status = .never_loaded, | ||
| 6121 | .source_loaded = false, | 6122 | .source_loaded = false, |
| 6122 | .tree_loaded = false, | 6123 | .tree_loaded = false, |
| 6123 | .zir_loaded = false, | 6124 | .zir_loaded = false, |
| ... | @@ -6441,6 +6442,7 @@ fn cmdDumpZir( | ... | @@ -6441,6 +6442,7 @@ fn cmdDumpZir( |
| 6441 | 6442 | ||
| 6442 | var file: Zcu.File = .{ | 6443 | var file: Zcu.File = .{ |
| 6443 | .status = .never_loaded, | 6444 | .status = .never_loaded, |
| 6445 | .prev_status = .never_loaded, | ||
| 6444 | .source_loaded = false, | 6446 | .source_loaded = false, |
| 6445 | .tree_loaded = false, | 6447 | .tree_loaded = false, |
| 6446 | .zir_loaded = true, | 6448 | .zir_loaded = true, |
| ... | @@ -6508,6 +6510,7 @@ fn cmdChangelist( | ... | @@ -6508,6 +6510,7 @@ fn cmdChangelist( |
| 6508 | 6510 | ||
| 6509 | var file: Zcu.File = .{ | 6511 | var file: Zcu.File = .{ |
| 6510 | .status = .never_loaded, | 6512 | .status = .never_loaded, |
| 6513 | .prev_status = .never_loaded, | ||
| 6511 | .source_loaded = false, | 6514 | .source_loaded = false, |
| 6512 | .tree_loaded = false, | 6515 | .tree_loaded = false, |
| 6513 | .zir_loaded = false, | 6516 | .zir_loaded = false, |
test/incremental/fix_astgen_failure created+35| ... | @@ -0,0 +1,35 @@ | ||
| 1 | #target=x86_64-linux-selfhosted | ||
| 2 | #target=x86_64-linux-cbe | ||
| 3 | #target=x86_64-windows-cbe | ||
| 4 | #update=initial version with error | ||
| 5 | #file=main.zig | ||
| 6 | pub fn main() !void { | ||
| 7 | try @import("foo.zig").hello(); | ||
| 8 | } | ||
| 9 | #file=foo.zig | ||
| 10 | pub fn hello() !void { | ||
| 11 | try std.io.getStdOut().writeAll("Hello, World!\n"); | ||
| 12 | } | ||
| 13 | #expect_error=ignored | ||
| 14 | #update=fix the error | ||
| 15 | #file=foo.zig | ||
| 16 | const std = @import("std"); | ||
| 17 | pub fn hello() !void { | ||
| 18 | try std.io.getStdOut().writeAll("Hello, World!\n"); | ||
| 19 | } | ||
| 20 | #expect_stdout="Hello, World!\n" | ||
| 21 | #update=add new error | ||
| 22 | #file=foo.zig | ||
| 23 | const std = @import("std"); | ||
| 24 | pub fn hello() !void { | ||
| 25 | try std.io.getStdOut().writeAll(hello_str); | ||
| 26 | } | ||
| 27 | #expect_error=ignored | ||
| 28 | #update=fix the new error | ||
| 29 | #file=foo.zig | ||
| 30 | const std = @import("std"); | ||
| 31 | const hello_str = "Hello, World! Again!\n"; | ||
| 32 | pub fn hello() !void { | ||
| 33 | try std.io.getStdOut().writeAll(hello_str); | ||
| 34 | } | ||
| 35 | #expect_stdout="Hello, World! Again!\n" | ||