authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-02-09 11:23:46+00:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-03-10 10:26:11+00:00
logb00ef1aea1a456ad8b175534add8bb324cb39bba
tree4f6f4eb841d745b33239e80d6e66850ff82ff603
parent09d0b1f87a740e968ef739e75729204ff470c98a
signaturelock-open Commit is signed but in an unrecognized format.

Zcu: prevent data races from `Type.assertHasLayout`


3 files changed, 91 insertions(+), 42 deletions(-)

src/Type.zig+3-9
...@@ -3175,21 +3175,15 @@ pub fn assertHasLayout(ty: Type, zcu: *const Zcu) void {...@@ -3175,21 +3175,15 @@ pub fn assertHasLayout(ty: Type, zcu: *const Zcu) void {
3175 },3175 },
3176 .struct_type => {3176 .struct_type => {
3177 assert(zcu.intern_pool.loadStructType(ty.toIntern()).want_layout);3177 assert(zcu.intern_pool.loadStructType(ty.toIntern()).want_layout);
3178 const unit: InternPool.AnalUnit = .wrap(.{ .type_layout = ty.toIntern() });3178 zcu.assertUpToDate(.wrap(.{ .type_layout = ty.toIntern() }));
3179 assert(!zcu.outdated.contains(unit));
3180 assert(!zcu.potentially_outdated.contains(unit));
3181 },3179 },
3182 .union_type => {3180 .union_type => {
3183 assert(zcu.intern_pool.loadUnionType(ty.toIntern()).want_layout);3181 assert(zcu.intern_pool.loadUnionType(ty.toIntern()).want_layout);
3184 const unit: InternPool.AnalUnit = .wrap(.{ .type_layout = ty.toIntern() });3182 zcu.assertUpToDate(.wrap(.{ .type_layout = ty.toIntern() }));
3185 assert(!zcu.outdated.contains(unit));
3186 assert(!zcu.potentially_outdated.contains(unit));
3187 },3183 },
3188 .enum_type => {3184 .enum_type => {
3189 assert(zcu.intern_pool.loadEnumType(ty.toIntern()).want_layout);3185 assert(zcu.intern_pool.loadEnumType(ty.toIntern()).want_layout);
3190 const unit: InternPool.AnalUnit = .wrap(.{ .type_layout = ty.toIntern() });3186 zcu.assertUpToDate(.wrap(.{ .type_layout = ty.toIntern() }));
3191 assert(!zcu.outdated.contains(unit));
3192 assert(!zcu.potentially_outdated.contains(unit));
3193 },3187 },
31943188
3195 // values, not types3189 // values, not types
src/Zcu.zig+79-8
...@@ -264,6 +264,10 @@ cimport_errors: std.AutoArrayHashMapUnmanaged(AnalUnit, std.zig.ErrorBundle) = ....@@ -264,6 +264,10 @@ cimport_errors: std.AutoArrayHashMapUnmanaged(AnalUnit, std.zig.ErrorBundle) = .
264/// Maximum amount of distinct error values, set by --error-limit264/// Maximum amount of distinct error values, set by --error-limit
265error_limit: ErrorInt,265error_limit: ErrorInt,
266266
267/// In safe builds, `Type.assertHasLayout` may be called cross-thread, so this lock
268/// guards accesses to `outdated` and `potentially_outdated`. In unsafe builds, the
269/// lock is not needed and is compiled out.
270outdated_lock: if (std.debug.runtime_safety) std.Io.RwLock else void = if (std.debug.runtime_safety) .init,
267/// Value is the number of PO dependencies of this AnalUnit.271/// Value is the number of PO dependencies of this AnalUnit.
268/// This value will decrease as we perform semantic analysis to learn what is outdated.272/// This value will decrease as we perform semantic analysis to learn what is outdated.
269/// If any of these PO deps is outdated, this value will be moved to `outdated`.273/// If any of these PO deps is outdated, this value will be moved to `outdated`.
...@@ -3063,6 +3067,8 @@ pub fn markDependeeOutdated(...@@ -3063,6 +3067,8 @@ pub fn markDependeeOutdated(
3063) !void {3067) !void {
3064 deps_log.debug("outdated dependee: {f}", .{zcu.fmtDependee(dependee)});3068 deps_log.debug("outdated dependee: {f}", .{zcu.fmtDependee(dependee)});
3065 var it = zcu.intern_pool.dependencyIterator(dependee);3069 var it = zcu.intern_pool.dependencyIterator(dependee);
3070 if (std.debug.runtime_safety) zcu.outdated_lock.lockUncancelable(zcu.comp.io);
3071 defer if (std.debug.runtime_safety) zcu.outdated_lock.unlock(zcu.comp.io);
3066 while (it.next()) |depender| {3072 while (it.next()) |depender| {
3067 if (zcu.outdated.getPtr(depender)) |po_dep_count| {3073 if (zcu.outdated.getPtr(depender)) |po_dep_count| {
3068 switch (marked_po) {3074 switch (marked_po) {
...@@ -3107,6 +3113,12 @@ pub fn markDependeeOutdated(...@@ -3107,6 +3113,12 @@ pub fn markDependeeOutdated(
3107}3113}
31083114
3109pub fn markPoDependeeUpToDate(zcu: *Zcu, dependee: InternPool.Dependee) !void {3115pub fn markPoDependeeUpToDate(zcu: *Zcu, dependee: InternPool.Dependee) !void {
3116 if (std.debug.runtime_safety) zcu.outdated_lock.lockUncancelable(zcu.comp.io);
3117 defer if (std.debug.runtime_safety) zcu.outdated_lock.unlock(zcu.comp.io);
3118 return markPoDependeeUpToDateInner(zcu, dependee);
3119}
3120/// Assumes that `zcu.outdated_lock` is already held exclusively.
3121fn markPoDependeeUpToDateInner(zcu: *Zcu, dependee: InternPool.Dependee) !void {
3110 deps_log.debug("up-to-date dependee: {f}", .{zcu.fmtDependee(dependee)});3122 deps_log.debug("up-to-date dependee: {f}", .{zcu.fmtDependee(dependee)});
3111 var it = zcu.intern_pool.dependencyIterator(dependee);3123 var it = zcu.intern_pool.dependencyIterator(dependee);
3112 while (it.next()) |depender| {3124 while (it.next()) |depender| {
...@@ -3142,17 +3154,19 @@ pub fn markPoDependeeUpToDate(zcu: *Zcu, dependee: InternPool.Dependee) !void {...@@ -3142,17 +3154,19 @@ pub fn markPoDependeeUpToDate(zcu: *Zcu, dependee: InternPool.Dependee) !void {
3142 // as no longer PO.3154 // as no longer PO.
3143 switch (depender.unwrap()) {3155 switch (depender.unwrap()) {
3144 .@"comptime" => {},3156 .@"comptime" => {},
3145 .nav_val => |nav| try zcu.markPoDependeeUpToDate(.{ .nav_val = nav }),3157 .nav_val => |nav| try zcu.markPoDependeeUpToDateInner(.{ .nav_val = nav }),
3146 .nav_ty => |nav| try zcu.markPoDependeeUpToDate(.{ .nav_ty = nav }),3158 .nav_ty => |nav| try zcu.markPoDependeeUpToDateInner(.{ .nav_ty = nav }),
3147 .type_layout => |ty| try zcu.markPoDependeeUpToDate(.{ .type_layout = ty }),3159 .type_layout => |ty| try zcu.markPoDependeeUpToDateInner(.{ .type_layout = ty }),
3148 .func => |func| try zcu.markPoDependeeUpToDate(.{ .func_ies = func }),3160 .func => |func| try zcu.markPoDependeeUpToDateInner(.{ .func_ies = func }),
3149 .memoized_state => |stage| try zcu.markPoDependeeUpToDate(.{ .memoized_state = stage }),3161 .memoized_state => |stage| try zcu.markPoDependeeUpToDateInner(.{ .memoized_state = stage }),
3150 }3162 }
3151 }3163 }
3152}3164}
31533165
3154/// Given a AnalUnit which is newly outdated or PO, mark all AnalUnits which may3166/// Given a AnalUnit which is newly outdated or PO, mark all AnalUnits which may
3155/// in turn be PO, due to a dependency on the original AnalUnit's tyval or IES.3167/// in turn be PO, due to a dependency on the original AnalUnit's tyval or IES.
3168///
3169/// Assumes that `zcu.outdated_lock` is already held exclusively.
3156fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: AnalUnit) !void {3170fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: AnalUnit) !void {
3157 const ip = &zcu.intern_pool;3171 const ip = &zcu.intern_pool;
3158 const dependee: InternPool.Dependee = switch (maybe_outdated.unwrap()) {3172 const dependee: InternPool.Dependee = switch (maybe_outdated.unwrap()) {
...@@ -3211,6 +3225,9 @@ pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?AnalUnit {...@@ -3211,6 +3225,9 @@ pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?AnalUnit {
3211 // possible situation is a cycle where everything is actually up-to-date, so we can clear out3225 // possible situation is a cycle where everything is actually up-to-date, so we can clear out
3212 // `zcu.potentially_outdated` and we are done.3226 // `zcu.potentially_outdated` and we are done.
32133227
3228 if (std.debug.runtime_safety) zcu.outdated_lock.lockUncancelable(zcu.comp.io);
3229 defer if (std.debug.runtime_safety) zcu.outdated_lock.unlock(zcu.comp.io);
3230
3214 if (zcu.outdated.count() == 0) {3231 if (zcu.outdated.count() == 0) {
3215 // Everything is up-to-date. There could be lingering entries in `zcu.potentially_outdated`3232 // Everything is up-to-date. There could be lingering entries in `zcu.potentially_outdated`
3216 // from a dependency loop on a previous update.3233 // from a dependency loop on a previous update.
...@@ -3230,7 +3247,10 @@ pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?AnalUnit {...@@ -3230,7 +3247,10 @@ pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?AnalUnit {
3230/// During an incremental update, before semantic analysis, call this to flush all values from3247/// During an incremental update, before semantic analysis, call this to flush all values from
3231/// `retryable_failures` and mark them as outdated so they get re-analyzed.3248/// `retryable_failures` and mark them as outdated so they get re-analyzed.
3232pub fn flushRetryableFailures(zcu: *Zcu) !void {3249pub fn flushRetryableFailures(zcu: *Zcu) !void {
3233 const gpa = zcu.gpa;3250 const comp = zcu.comp;
3251 const gpa = comp.gpa;
3252 if (std.debug.runtime_safety) zcu.outdated_lock.lockUncancelable(comp.io);
3253 defer if (std.debug.runtime_safety) zcu.outdated_lock.unlock(comp.io);
3234 for (zcu.retryable_failures.items) |depender| {3254 for (zcu.retryable_failures.items) |depender| {
3235 if (zcu.outdated.contains(depender)) continue;3255 if (zcu.outdated.contains(depender)) continue;
3236 if (zcu.potentially_outdated.fetchSwapRemove(depender)) |kv| {3256 if (zcu.potentially_outdated.fetchSwapRemove(depender)) |kv| {
...@@ -3481,8 +3501,12 @@ pub fn ensureFuncBodyAnalysisQueued(zcu: *Zcu, func: InternPool.Index) !void {...@@ -3481,8 +3501,12 @@ pub fn ensureFuncBodyAnalysisQueued(zcu: *Zcu, func: InternPool.Index) !void {
3481 if (ip.setWantRuntimeFnAnalysis(io, func)) {3501 if (ip.setWantRuntimeFnAnalysis(io, func)) {
3482 // This is the first reference to this function, so we must ensure it will be analyzed.3502 // This is the first reference to this function, so we must ensure it will be analyzed.
3483 const unit: AnalUnit = .wrap(.{ .func = func });3503 const unit: AnalUnit = .wrap(.{ .func = func });
3484 try zcu.outdated.putNoClobber(gpa, unit, 0);3504 if (std.debug.runtime_safety) zcu.outdated_lock.lockUncancelable(zcu.comp.io);
3485 try zcu.outdated_ready.putNoClobber(gpa, unit, {});3505 defer if (std.debug.runtime_safety) zcu.outdated_lock.unlock(zcu.comp.io);
3506 try zcu.outdated.ensureUnusedCapacity(gpa, 1);
3507 try zcu.outdated_ready.ensureUnusedCapacity(gpa, 1);
3508 zcu.outdated.putAssumeCapacityNoClobber(unit, 0);
3509 zcu.outdated_ready.putAssumeCapacityNoClobber(unit, {});
3486 }3510 }
3487}3511}
34883512
...@@ -3493,6 +3517,8 @@ pub fn ensureNavValAnalysisQueued(zcu: *Zcu, nav: InternPool.Nav.Index) !void {...@@ -3493,6 +3517,8 @@ pub fn ensureNavValAnalysisQueued(zcu: *Zcu, nav: InternPool.Nav.Index) !void {
3493 const ip = &zcu.intern_pool;3517 const ip = &zcu.intern_pool;
3494 if (ip.setWantNavAnalysis(io, nav)) {3518 if (ip.setWantNavAnalysis(io, nav)) {
3495 // This is the first reference to this function, so we must ensure it will be analyzed.3519 // This is the first reference to this function, so we must ensure it will be analyzed.
3520 if (std.debug.runtime_safety) zcu.outdated_lock.lockUncancelable(zcu.comp.io);
3521 defer if (std.debug.runtime_safety) zcu.outdated_lock.unlock(zcu.comp.io);
3496 try zcu.outdated.ensureUnusedCapacity(gpa, 2);3522 try zcu.outdated.ensureUnusedCapacity(gpa, 2);
3497 try zcu.outdated_ready.ensureUnusedCapacity(gpa, 2);3523 try zcu.outdated_ready.ensureUnusedCapacity(gpa, 2);
3498 zcu.outdated.putAssumeCapacityNoClobber(.wrap(.{ .nav_val = nav }), 0);3524 zcu.outdated.putAssumeCapacityNoClobber(.wrap(.{ .nav_val = nav }), 0);
...@@ -3502,6 +3528,51 @@ pub fn ensureNavValAnalysisQueued(zcu: *Zcu, nav: InternPool.Nav.Index) !void {...@@ -3502,6 +3528,51 @@ pub fn ensureNavValAnalysisQueued(zcu: *Zcu, nav: InternPool.Nav.Index) !void {
3502 }3528 }
3503}3529}
35043530
3531/// Called when an `InternPool.ComptimeUnit` is first created to mark it as outdated so that it will
3532/// be semantically analyzed.
3533pub fn queueComptimeUnitAnalysis(zcu: *Zcu, cu: InternPool.ComptimeUnit.Id) Allocator.Error!void {
3534 const comp = zcu.comp;
3535 const gpa = comp.gpa;
3536 const io = comp.io;
3537 const unit: AnalUnit = .wrap(.{ .@"comptime" = cu });
3538 if (std.debug.runtime_safety) zcu.outdated_lock.lockUncancelable(io);
3539 defer if (std.debug.runtime_safety) zcu.outdated_lock.unlock(io);
3540 try zcu.outdated.ensureUnusedCapacity(gpa, 1);
3541 try zcu.outdated_ready.ensureUnusedCapacity(gpa, 1);
3542 zcu.outdated.putAssumeCapacityNoClobber(unit, 0);
3543 zcu.outdated_ready.putAssumeCapacityNoClobber(unit, {});
3544}
3545
3546/// If `unit` was marked as outdated or porentially outdated, clears that status and returns `true`.
3547/// Otherwise, returns `false`.
3548pub fn clearOutdatedState(zcu: *Zcu, unit: AnalUnit) bool {
3549 const io = zcu.comp.io;
3550 if (std.debug.runtime_safety) zcu.outdated_lock.lockUncancelable(io);
3551 defer if (std.debug.runtime_safety) zcu.outdated_lock.unlock(io);
3552 if (zcu.outdated.fetchSwapRemove(unit)) |kv| {
3553 if (kv.value == 0) assert(zcu.outdated_ready.swapRemove(unit));
3554 return true;
3555 } else if (zcu.potentially_outdated.swapRemove(unit)) {
3556 return true;
3557 } else {
3558 return false;
3559 }
3560}
3561
3562/// This function takes a `*const Zcu` and `@constCast`s it so that it can be called from functions
3563/// in `Type` which otherwise do not modify the `Zcu`.
3564pub fn assertUpToDate(zcu: *const Zcu, unit: AnalUnit) void {
3565 if (!std.debug.runtime_safety) return;
3566
3567 const io = zcu.comp.io;
3568
3569 @constCast(zcu).outdated_lock.lockSharedUncancelable(io);
3570 defer @constCast(zcu).outdated_lock.unlockShared(io);
3571
3572 assert(!zcu.outdated.contains(unit));
3573 assert(!zcu.potentially_outdated.contains(unit));
3574}
3575
3505pub const ImportResult = struct {3576pub const ImportResult = struct {
3506 /// Whether `file` has been newly created; in other words, whether this is the first import of3577 /// Whether `file` has been newly created; in other words, whether this is the first import of
3507 /// this file. This should only be `true` when importing files during AstGen. After that, all3578 /// this file. This should only be `true` when importing files during AstGen. After that, all
src/Zcu/PerThread.zig+9-25
...@@ -746,12 +746,11 @@ pub fn ensureMemoizedStateUpToDate(...@@ -746,12 +746,11 @@ pub fn ensureMemoizedStateUpToDate(
746746
747 assert(!zcu.analysis_in_progress.contains(unit));747 assert(!zcu.analysis_in_progress.contains(unit));
748748
749 const was_outdated = zcu.outdated.swapRemove(unit) or zcu.potentially_outdated.swapRemove(unit);749 const was_outdated = zcu.clearOutdatedState(unit);
750 const prev_failed = zcu.failed_analysis.contains(unit) or zcu.transitive_failed_analysis.contains(unit);750 const prev_failed = zcu.failed_analysis.contains(unit) or zcu.transitive_failed_analysis.contains(unit);
751751
752 if (was_outdated) {752 if (was_outdated) {
753 dev.check(.incremental);753 dev.check(.incremental);
754 _ = zcu.outdated_ready.swapRemove(unit);
755 zcu.resetUnit(unit);754 zcu.resetUnit(unit);
756 } else {755 } else {
757 if (prev_failed) return error.AnalysisFail;756 if (prev_failed) return error.AnalysisFail;
...@@ -866,11 +865,9 @@ pub fn ensureComptimeUnitUpToDate(pt: Zcu.PerThread, cu_id: InternPool.ComptimeU...@@ -866,11 +865,9 @@ pub fn ensureComptimeUnitUpToDate(pt: Zcu.PerThread, cu_id: InternPool.ComptimeU
866 // result in over-analysis if analysis occurs in a poor order; we do our best to avoid this by865 // result in over-analysis if analysis occurs in a poor order; we do our best to avoid this by
867 // carefully choosing which units to re-analyze. See `Zcu.findOutdatedToAnalyze`.866 // carefully choosing which units to re-analyze. See `Zcu.findOutdatedToAnalyze`.
868867
869 const was_outdated = zcu.outdated.swapRemove(anal_unit) or868 const was_outdated = zcu.clearOutdatedState(anal_unit);
870 zcu.potentially_outdated.swapRemove(anal_unit);
871869
872 if (was_outdated) {870 if (was_outdated) {
873 _ = zcu.outdated_ready.swapRemove(anal_unit);
874 // `was_outdated` can be true in the initial update for comptime units, so this isn't a `dev.check`.871 // `was_outdated` can be true in the initial update for comptime units, so this isn't a `dev.check`.
875 if (dev.env.supports(.incremental)) {872 if (dev.env.supports(.incremental)) {
876 zcu.resetUnit(anal_unit);873 zcu.resetUnit(anal_unit);
...@@ -1023,12 +1020,10 @@ pub fn ensureTypeLayoutUpToDate(...@@ -1023,12 +1020,10 @@ pub fn ensureTypeLayoutUpToDate(
10231020
1024 assert(!zcu.analysis_in_progress.contains(anal_unit));1021 assert(!zcu.analysis_in_progress.contains(anal_unit));
10251022
1026 const was_outdated = zcu.outdated.swapRemove(anal_unit) or1023 const was_outdated = zcu.clearOutdatedState(anal_unit) or
1027 zcu.potentially_outdated.swapRemove(anal_unit) or
1028 zcu.intern_pool.setWantTypeLayout(zcu.comp.io, ty.toIntern());1024 zcu.intern_pool.setWantTypeLayout(zcu.comp.io, ty.toIntern());
10291025
1030 if (was_outdated) {1026 if (was_outdated) {
1031 _ = zcu.outdated_ready.swapRemove(anal_unit);
1032 // `was_outdated` is true in the initial update, so this isn't a `dev.check`.1027 // `was_outdated` is true in the initial update, so this isn't a `dev.check`.
1033 if (dev.env.supports(.incremental)) {1028 if (dev.env.supports(.incremental)) {
1034 zcu.resetUnit(anal_unit);1029 zcu.resetUnit(anal_unit);
...@@ -1139,15 +1134,13 @@ pub fn ensureNavValUpToDate(...@@ -1139,15 +1134,13 @@ pub fn ensureNavValUpToDate(
1139 // result in over-analysis if analysis occurs in a poor order; we do our best to avoid this by1134 // result in over-analysis if analysis occurs in a poor order; we do our best to avoid this by
1140 // carefully choosing which units to re-analyze. See `Zcu.findOutdatedToAnalyze`.1135 // carefully choosing which units to re-analyze. See `Zcu.findOutdatedToAnalyze`.
11411136
1142 const was_outdated = zcu.outdated.swapRemove(anal_unit) or1137 const was_outdated = zcu.clearOutdatedState(anal_unit);
1143 zcu.potentially_outdated.swapRemove(anal_unit);
11441138
1145 const prev_failed = zcu.failed_analysis.contains(anal_unit) or1139 const prev_failed = zcu.failed_analysis.contains(anal_unit) or
1146 zcu.transitive_failed_analysis.contains(anal_unit);1140 zcu.transitive_failed_analysis.contains(anal_unit);
11471141
1148 if (was_outdated) {1142 if (was_outdated) {
1149 dev.check(.incremental);1143 dev.check(.incremental);
1150 _ = zcu.outdated_ready.swapRemove(anal_unit);
1151 zcu.resetUnit(anal_unit);1144 zcu.resetUnit(anal_unit);
1152 } else {1145 } else {
1153 // We can trust the current information about this unit.1146 // We can trust the current information about this unit.
...@@ -1497,15 +1490,13 @@ pub fn ensureNavTypeUpToDate(...@@ -1497,15 +1490,13 @@ pub fn ensureNavTypeUpToDate(
1497 // result in over-analysis if analysis occurs in a poor order; we do our best to avoid this by1490 // result in over-analysis if analysis occurs in a poor order; we do our best to avoid this by
1498 // carefully choosing which units to re-analyze. See `Zcu.findOutdatedToAnalyze`.1491 // carefully choosing which units to re-analyze. See `Zcu.findOutdatedToAnalyze`.
14991492
1500 const was_outdated = zcu.outdated.swapRemove(anal_unit) or1493 const was_outdated = zcu.clearOutdatedState(anal_unit);
1501 zcu.potentially_outdated.swapRemove(anal_unit);
15021494
1503 const prev_failed = zcu.failed_analysis.contains(anal_unit) or1495 const prev_failed = zcu.failed_analysis.contains(anal_unit) or
1504 zcu.transitive_failed_analysis.contains(anal_unit);1496 zcu.transitive_failed_analysis.contains(anal_unit);
15051497
1506 if (was_outdated) {1498 if (was_outdated) {
1507 dev.check(.incremental);1499 dev.check(.incremental);
1508 _ = zcu.outdated_ready.swapRemove(anal_unit);
1509 zcu.resetUnit(anal_unit);1500 zcu.resetUnit(anal_unit);
1510 } else {1501 } else {
1511 // We can trust the current information about this unit.1502 // We can trust the current information about this unit.
...@@ -1733,15 +1724,13 @@ pub fn ensureFuncBodyUpToDate(...@@ -1733,15 +1724,13 @@ pub fn ensureFuncBodyUpToDate(
17331724
1734 assert(func.ty == func.uncoerced_ty); // analyze the body of the original function, not a coerced one1725 assert(func.ty == func.uncoerced_ty); // analyze the body of the original function, not a coerced one
17351726
1736 const was_outdated = zcu.outdated.swapRemove(anal_unit) or1727 const was_outdated = zcu.clearOutdatedState(anal_unit) or
1737 zcu.potentially_outdated.swapRemove(anal_unit) or
1738 ip.setWantRuntimeFnAnalysis(zcu.comp.io, func_index);1728 ip.setWantRuntimeFnAnalysis(zcu.comp.io, func_index);
17391729
1740 const prev_failed = zcu.failed_analysis.contains(anal_unit) or zcu.transitive_failed_analysis.contains(anal_unit);1730 const prev_failed = zcu.failed_analysis.contains(anal_unit) or zcu.transitive_failed_analysis.contains(anal_unit);
17411731
1742 if (was_outdated) {1732 if (was_outdated) {
1743 dev.check(.incremental);1733 dev.check(.incremental);
1744 _ = zcu.outdated_ready.swapRemove(anal_unit);
1745 zcu.resetUnit(anal_unit);1734 zcu.resetUnit(anal_unit);
1746 } else {1735 } else {
1747 // We can trust the current information about this function.1736 // We can trust the current information about this function.
...@@ -2712,27 +2701,22 @@ const ScanDeclIter = struct {...@@ -2712,27 +2701,22 @@ const ScanDeclIter = struct {
27122701
2713 const existing_unit = iter.existing_by_inst.get(tracked_inst);2702 const existing_unit = iter.existing_by_inst.get(tracked_inst);
27142703
2715 const unit, const want_analysis = switch (decl.kind) {2704 const unit: AnalUnit, const want_analysis = switch (decl.kind) {
2716 .@"comptime" => unit: {2705 .@"comptime" => unit: {
2717 const cu = if (existing_unit) |eu|2706 const cu = if (existing_unit) |eu|
2718 eu.unwrap().@"comptime"2707 eu.unwrap().@"comptime"
2719 else2708 else
2720 try ip.createComptimeUnit(gpa, io, pt.tid, tracked_inst, namespace_index);2709 try ip.createComptimeUnit(gpa, io, pt.tid, tracked_inst, namespace_index);
27212710
2722 const unit: AnalUnit = .wrap(.{ .@"comptime" = cu });
2723
2724 try namespace.comptime_decls.append(gpa, cu);2711 try namespace.comptime_decls.append(gpa, cu);
27252712
2726 if (existing_unit == null) {2713 if (existing_unit == null) {
2727 // For a `comptime` declaration, whether to analyze is based solely on whether the unit2714 // For a `comptime` declaration, whether to analyze is based solely on whether the unit
2728 // is outdated. So, add this fresh one to `outdated` and `outdated_ready`.2715 // is outdated. So, add this fresh one to `outdated` and `outdated_ready`.
2729 try zcu.outdated.ensureUnusedCapacity(gpa, 1);2716 try zcu.queueComptimeUnitAnalysis(cu);
2730 try zcu.outdated_ready.ensureUnusedCapacity(gpa, 1);
2731 zcu.outdated.putAssumeCapacityNoClobber(unit, 0);
2732 zcu.outdated_ready.putAssumeCapacityNoClobber(unit, {});
2733 }2717 }
27342718
2735 break :unit .{ unit, true };2719 break :unit .{ .wrap(.{ .@"comptime" = cu }), true };
2736 },2720 },
2737 else => unit: {2721 else => unit: {
2738 const name = maybe_name.unwrap().?;2722 const name = maybe_name.unwrap().?;