From a6012560210dee3071f977310deaf62e4254061c Mon Sep 17 00:00:00 2001 From: Andrii Chebukin Date: Thu, 10 Sep 2026 22:59:07 +0200 Subject: [PATCH] Say why CommitDelayedDiagnostics copies the collection The comment said the copy is eager without saying what that protects against, and `errors` named a collection that also holds warnings. Both invite replacing `ToArray()` with a lazy traversal, which would throw `Collection was modified` on the paths where the capturing logger is still the ambient one while it replays: - `fsc.fs` installs `delayForFlagsLogger` as the thread's logger and keeps it there across every commit; the console sink it commits to calls `TcConfig.Create`, which reports through that same logger. - `RunWithBufferedReporting` commits from its exception path inside the `UseDiagnosticsLogger` scope. Co-Authored-By: Claude Opus 5 (1M context) --- src/Compiler/Facilities/DiagnosticsLogger.fs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Compiler/Facilities/DiagnosticsLogger.fs b/src/Compiler/Facilities/DiagnosticsLogger.fs index 7cc44d88366..f0b0ece1cc3 100644 --- a/src/Compiler/Facilities/DiagnosticsLogger.fs +++ b/src/Compiler/Facilities/DiagnosticsLogger.fs @@ -409,9 +409,9 @@ type CapturingDiagnosticsLogger(nm, ?eagerFormat) = member _.Diagnostics = diagnostics |> Seq.toList member _.CommitDelayedDiagnostics(diagnosticsLogger: DiagnosticsLogger) = - // Eagerly grab all the errors and warnings from the mutable collection - let errors = diagnostics.ToArray() - errors |> Array.iter diagnosticsLogger.DiagnosticSink + // A sink can report back into this logger while replaying, so iterate a snapshot. + let snapshot = diagnostics.ToArray() + snapshot |> Array.iter diagnosticsLogger.DiagnosticSink let buildPhase = AsyncLocal() let diagnosticsLogger = AsyncLocal()