| author | |
| committer | |
| log | 804c284d2a4ece85aec64d25c241a48b4e8b11ad |
| tree | 401d9620e5b1a798cbabf3ea7ebfdb75524a3219 |
| parent | f134f4345cf8484b82c46278074eb45af0efaf2e |
| signature |
When analyzing the body of a generic function instance, we were making a
dependency on the owner NAV of the *instance*, rather than that of our
generic owner. Aside from being nonsensical (because the instance's
owner NAV does not undergo semantic analysis), this meant that if the
generic owner's owner NAV had a compile error (due to e.g. an error in
the function signature), then analysis of the generic instance's body
would fail, but it would not register a dependency on that NAV's value,
so would not be re-analyzed if the NAV suceeded in a future update.
Based on descriptions of when people had been hitting the dreaded
"referenced transitive analysis errors, but none actually emitted"
error, I *think* this was by far the most serious remaining incremental
compilation bug in the frontend---every description I've had of such a
crash occurring seems to more-or-less line up with this bug. So fingers
crossed this is another big jump in incremental compilation stability!2 files changed, 48 insertions(+), 2 deletions(-)
src/Zcu/PerThread.zig+2-2| ... | @@ -3262,9 +3262,7 @@ fn analyzeFuncBodyInner( | ... | @@ -3262,9 +3262,7 @@ fn analyzeFuncBodyInner( |
| 3262 | defer sema.deinit(); | 3262 | defer sema.deinit(); |
| 3263 | 3263 | ||
| 3264 | // Every runtime function has a dependency on the source of the Decl it originates from. | 3264 | // Every runtime function has a dependency on the source of the Decl it originates from. |
| 3265 | // It also depends on the value of its owner Decl. | ||
| 3266 | try sema.declareDependency(.{ .src_hash = decl_analysis.zir_index }); | 3265 | try sema.declareDependency(.{ .src_hash = decl_analysis.zir_index }); |
| 3267 | try sema.declareDependency(.{ .nav_val = func.owner_nav }); | ||
| 3268 | 3266 | ||
| 3269 | // Make sure that the declaration `Nav` still refers to this function (or its generic owner). | 3267 | // Make sure that the declaration `Nav` still refers to this function (or its generic owner). |
| 3270 | // This will not be the case if the incremental update has changed a function type or turned a | 3268 | // This will not be the case if the incremental update has changed a function type or turned a |
| ... | @@ -3275,6 +3273,7 @@ fn analyzeFuncBodyInner( | ... | @@ -3275,6 +3273,7 @@ fn analyzeFuncBodyInner( |
| 3275 | // If we *are* still owned by the right NAV, this analysis updates `zir_body_inst` if necessary. | 3273 | // If we *are* still owned by the right NAV, this analysis updates `zir_body_inst` if necessary. |
| 3276 | 3274 | ||
| 3277 | if (func.generic_owner == .none) { | 3275 | if (func.generic_owner == .none) { |
| 3276 | try sema.declareDependency(.{ .nav_val = func.owner_nav }); | ||
| 3278 | pt.ensureNavValUpToDate(func.owner_nav, reason) catch |err| switch (err) { | 3277 | pt.ensureNavValUpToDate(func.owner_nav, reason) catch |err| switch (err) { |
| 3279 | error.AnalysisFail => return sema.failTransitive(.{ .failed_unit = .wrap(.{ .nav_val = func.owner_nav }) }), | 3278 | error.AnalysisFail => return sema.failTransitive(.{ .failed_unit = .wrap(.{ .nav_val = func.owner_nav }) }), |
| 3280 | else => |e| return e, | 3279 | else => |e| return e, |
| ... | @@ -3284,6 +3283,7 @@ fn analyzeFuncBodyInner( | ... | @@ -3284,6 +3283,7 @@ fn analyzeFuncBodyInner( |
| 3284 | } | 3283 | } |
| 3285 | } else { | 3284 | } else { |
| 3286 | const go_nav = zcu.funcInfo(func.generic_owner).owner_nav; | 3285 | const go_nav = zcu.funcInfo(func.generic_owner).owner_nav; |
| 3286 | try sema.declareDependency(.{ .nav_val = go_nav }); | ||
| 3287 | pt.ensureNavValUpToDate(go_nav, reason) catch |err| switch (err) { | 3287 | pt.ensureNavValUpToDate(go_nav, reason) catch |err| switch (err) { |
| 3288 | error.AnalysisFail => return sema.failTransitive(.{ .failed_unit = .wrap(.{ .nav_val = go_nav }) }), | 3288 | error.AnalysisFail => return sema.failTransitive(.{ .failed_unit = .wrap(.{ .nav_val = go_nav }) }), |
| 3289 | else => |e| return e, | 3289 | else => |e| return e, |
test/incremental/temporary_analysis_error_in_generic_signature created+46| ... | @@ -0,0 +1,46 @@ | ||
| 1 | #update=initial version | ||
| 2 | #file=main.zig | ||
| 3 | //! The original repro here depends on re-analysis order, which depends on | ||
| 4 | //! declaration order, so this exact declaration order must be used. | ||
| 5 | const Foo = struct { x: u8 }; | ||
| 6 | pub fn main(init: std.process.Init) !void { | ||
| 7 | const c = bar('Z').x; | ||
| 8 | try std.Io.File.stdout().writeStreamingAll(init.io, &.{ c, '\n' }); | ||
| 9 | } | ||
| 10 | fn bar(comptime x: u8) @This().Foo { | ||
| 11 | return .{ .x = x }; | ||
| 12 | } | ||
| 13 | const std = @import("std"); | ||
| 14 | #expect_stdout="Z\n" | ||
| 15 | |||
| 16 | #update=change generic signature to use non-existent member | ||
| 17 | #file=main.zig | ||
| 18 | //! The original repro here depends on re-analysis order, which depends on | ||
| 19 | //! declaration order, so this exact declaration order must be used. | ||
| 20 | const Foo = struct { x: u8 }; | ||
| 21 | pub fn main(init: std.process.Init) !void { | ||
| 22 | const c = bar('Z').x; | ||
| 23 | try std.Io.File.stdout().writeStreamingAll(init.io, &.{ c, '\n' }); | ||
| 24 | } | ||
| 25 | fn bar(comptime x: u8) @This().FooAlias { | ||
| 26 | return .{ .x = x }; | ||
| 27 | } | ||
| 28 | const std = @import("std"); | ||
| 29 | #expect_error=main.zig:8:31: error: root source file struct 'main' has no member named 'FooAlias' | ||
| 30 | #expect_error=main.zig:1:1: note: struct declared here | ||
| 31 | |||
| 32 | #update=add that member, fixing the error | ||
| 33 | #file=main.zig | ||
| 34 | //! The original repro here depends on re-analysis order, which depends on | ||
| 35 | //! declaration order, so this exact declaration order must be used. | ||
| 36 | const Foo = struct { x: u8 }; | ||
| 37 | const FooAlias = Foo; | ||
| 38 | pub fn main(init: std.process.Init) !void { | ||
| 39 | const c = bar('Z').x; | ||
| 40 | try std.Io.File.stdout().writeStreamingAll(init.io, &.{ c, '\n' }); | ||
| 41 | } | ||
| 42 | fn bar(comptime x: u8) @This().FooAlias { | ||
| 43 | return .{ .x = x }; | ||
| 44 | } | ||
| 45 | const std = @import("std"); | ||
| 46 | #expect_stdout="Z\n" | ||