| ... | ... | @@ -264,6 +264,10 @@ cimport_errors: std.AutoArrayHashMapUnmanaged(AnalUnit, std.zig.ErrorBundle) = . |
| 264 | 264 | /// Maximum amount of distinct error values, set by --error-limit |
| 265 | 265 | error_limit: ErrorInt, |
| 266 | 266 | |
| 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. |
| 270 | outdated_lock: if (std.debug.runtime_safety) std.Io.RwLock else void = if (std.debug.runtime_safety) .init, |
| 267 | 271 | /// Value is the number of PO dependencies of this AnalUnit. |
| 268 | 272 | /// This value will decrease as we perform semantic analysis to learn what is outdated. |
| 269 | 273 | /// If any of these PO deps is outdated, this value will be moved to `outdated`. |
| ... | ... | @@ -3063,6 +3067,8 @@ pub fn markDependeeOutdated( |
| 3063 | 3067 | ) !void { |
| 3064 | 3068 | deps_log.debug("outdated dependee: {f}", .{zcu.fmtDependee(dependee)}); |
| 3065 | 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 | 3072 | while (it.next()) |depender| { |
| 3067 | 3073 | if (zcu.outdated.getPtr(depender)) |po_dep_count| { |
| 3068 | 3074 | switch (marked_po) { |
| ... | ... | @@ -3107,6 +3113,12 @@ pub fn markDependeeOutdated( |
| 3107 | 3113 | } |
| 3108 | 3114 | |
| 3109 | 3115 | pub 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. |
| 3121 | fn markPoDependeeUpToDateInner(zcu: *Zcu, dependee: InternPool.Dependee) !void { |
| 3110 | 3122 | deps_log.debug("up-to-date dependee: {f}", .{zcu.fmtDependee(dependee)}); |
| 3111 | 3123 | var it = zcu.intern_pool.dependencyIterator(dependee); |
| 3112 | 3124 | while (it.next()) |depender| { |
| ... | ... | @@ -3142,17 +3154,19 @@ pub fn markPoDependeeUpToDate(zcu: *Zcu, dependee: InternPool.Dependee) !void { |
| 3142 | 3154 | // as no longer PO. |
| 3143 | 3155 | switch (depender.unwrap()) { |
| 3144 | 3156 | .@"comptime" => {}, |
| 3145 | | .nav_val => |nav| try zcu.markPoDependeeUpToDate(.{ .nav_val = nav }), |
| 3146 | | .nav_ty => |nav| try zcu.markPoDependeeUpToDate(.{ .nav_ty = nav }), |
| 3147 | | .type_layout => |ty| try zcu.markPoDependeeUpToDate(.{ .type_layout = ty }), |
| 3148 | | .func => |func| try zcu.markPoDependeeUpToDate(.{ .func_ies = func }), |
| 3149 | | .memoized_state => |stage| try zcu.markPoDependeeUpToDate(.{ .memoized_state = stage }), |
| 3157 | .nav_val => |nav| try zcu.markPoDependeeUpToDateInner(.{ .nav_val = nav }), |
| 3158 | .nav_ty => |nav| try zcu.markPoDependeeUpToDateInner(.{ .nav_ty = nav }), |
| 3159 | .type_layout => |ty| try zcu.markPoDependeeUpToDateInner(.{ .type_layout = ty }), |
| 3160 | .func => |func| try zcu.markPoDependeeUpToDateInner(.{ .func_ies = func }), |
| 3161 | .memoized_state => |stage| try zcu.markPoDependeeUpToDateInner(.{ .memoized_state = stage }), |
| 3150 | 3162 | } |
| 3151 | 3163 | } |
| 3152 | 3164 | } |
| 3153 | 3165 | |
| 3154 | 3166 | /// Given a AnalUnit which is newly outdated or PO, mark all AnalUnits which may |
| 3155 | 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. |
| 3156 | 3170 | fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: AnalUnit) !void { |
| 3157 | 3171 | const ip = &zcu.intern_pool; |
| 3158 | 3172 | const dependee: InternPool.Dependee = switch (maybe_outdated.unwrap()) { |
| ... | ... | @@ -3211,6 +3225,9 @@ pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?AnalUnit { |
| 3211 | 3225 | // possible situation is a cycle where everything is actually up-to-date, so we can clear out |
| 3212 | 3226 | // `zcu.potentially_outdated` and we are done. |
| 3213 | 3227 | |
| 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 | 3231 | if (zcu.outdated.count() == 0) { |
| 3215 | 3232 | // Everything is up-to-date. There could be lingering entries in `zcu.potentially_outdated` |
| 3216 | 3233 | // from a dependency loop on a previous update. |
| ... | ... | @@ -3230,7 +3247,10 @@ pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?AnalUnit { |
| 3230 | 3247 | /// During an incremental update, before semantic analysis, call this to flush all values from |
| 3231 | 3248 | /// `retryable_failures` and mark them as outdated so they get re-analyzed. |
| 3232 | 3249 | pub 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 | 3254 | for (zcu.retryable_failures.items) |depender| { |
| 3235 | 3255 | if (zcu.outdated.contains(depender)) continue; |
| 3236 | 3256 | if (zcu.potentially_outdated.fetchSwapRemove(depender)) |kv| { |
| ... | ... | @@ -3481,8 +3501,12 @@ pub fn ensureFuncBodyAnalysisQueued(zcu: *Zcu, func: InternPool.Index) !void { |
| 3481 | 3501 | if (ip.setWantRuntimeFnAnalysis(io, func)) { |
| 3482 | 3502 | // This is the first reference to this function, so we must ensure it will be analyzed. |
| 3483 | 3503 | const unit: AnalUnit = .wrap(.{ .func = func }); |
| 3484 | | try zcu.outdated.putNoClobber(gpa, unit, 0); |
| 3485 | | try zcu.outdated_ready.putNoClobber(gpa, unit, {}); |
| 3504 | if (std.debug.runtime_safety) zcu.outdated_lock.lockUncancelable(zcu.comp.io); |
| 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 | } |
| 3488 | 3512 | |
| ... | ... | @@ -3493,6 +3517,8 @@ pub fn ensureNavValAnalysisQueued(zcu: *Zcu, nav: InternPool.Nav.Index) !void { |
| 3493 | 3517 | const ip = &zcu.intern_pool; |
| 3494 | 3518 | if (ip.setWantNavAnalysis(io, nav)) { |
| 3495 | 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 | 3522 | try zcu.outdated.ensureUnusedCapacity(gpa, 2); |
| 3497 | 3523 | try zcu.outdated_ready.ensureUnusedCapacity(gpa, 2); |
| 3498 | 3524 | zcu.outdated.putAssumeCapacityNoClobber(.wrap(.{ .nav_val = nav }), 0); |
| ... | ... | @@ -3502,6 +3528,51 @@ pub fn ensureNavValAnalysisQueued(zcu: *Zcu, nav: InternPool.Nav.Index) !void { |
| 3502 | 3528 | } |
| 3503 | 3529 | } |
| 3504 | 3530 | |
| 3531 | /// Called when an `InternPool.ComptimeUnit` is first created to mark it as outdated so that it will |
| 3532 | /// be semantically analyzed. |
| 3533 | pub 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`. |
| 3548 | pub 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`. |
| 3564 | pub 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 | |
| 3505 | 3576 | pub const ImportResult = struct { |
| 3506 | 3577 | /// Whether `file` has been newly created; in other words, whether this is the first import of |
| 3507 | 3578 | /// this file. This should only be `true` when importing files during AstGen. After that, all |