Skip to content

Say why CommitDelayedDiagnostics copies the collection - #20515

Merged
T-Gro merged 1 commit into
dotnet:mainfrom
xperiandri:diagnostics/name-commit-snapshot
Sep 21, 2026
Merged

T-Gro merged 1 commit into
dotnet:mainfrom
xperiandri:diagnostics/name-commit-snapshot

Conversation

@xperiandri

Copy link
Copy Markdown
Contributor

Description

Comment and local-name only change in CapturingDiagnosticsLogger.CommitDelayedDiagnostics. No behaviour change.

member _.CommitDelayedDiagnostics(diagnosticsLogger: DiagnosticsLogger) =
    // A sink can report back into this logger while replaying, so iterate a snapshot.
    let snapshot = diagnostics.ToArray()
    snapshot |> Array.iter diagnosticsLogger.DiagnosticSink

The old comment ("Eagerly grab all the errors and warnings from the mutable collection") said that the copy is eager but not what it protects against, and errors named a collection that also holds warnings. Together they read like a gratuitous copy of a ResizeArray that is consumed in full, immediately, by the very next line — an inviting target for "just iterate it lazily".

It is not gratuitous. A sink can report a diagnostic back into the logger being replayed, and a lazy traversal would then die with Collection was modified; enumeration operation may not execute:

  • fsc.fs installs delayForFlagsLogger as the thread's logger (fsc.fs#L498) and never unwinds it across the commits that follow. Each of those builds a console logger whose DiagnosticSink calls TcConfig.Create(tcConfigB, validate = false) (fsc.fs#L78), which reports through the ambient logger — still the CapturingDiagnosticsLogger mid-replay.
  • RunWithBufferedReporting commits from its exception path while use _restoreLogger = UseDiagnosticsLogger capturingLogger is still in scope (NameResolution.fs#L2594).

With the snapshot, a diagnostic reported during a replay simply stays in the collection for the next commit. Without it, the failure is an InvalidOperationException far from its cause and only on the re-entrant paths.

Fixes # (issue, if applicable) — none.

Checklist

  • Test cases added — not applicable; nothing observable changed. FSharp.Compiler.Service.Tests.BuildGraphTests (15 tests, the existing coverage for CommitDelayedDiagnostics via MultipleDiagnosticsLoggers.Parallel/Sequential, including diagnostic ordering) passes.
  • Performance benchmarks added in case of performance changes — not applicable.
  • Release notes entry updated — not applicable, no user-visible change. Please apply NO_RELEASE_NOTES; I cannot set labels on this repository.

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Sep 10, 2026 •

Copy link
Copy Markdown
Contributor

✅ Release-note check exempted

The NO_RELEASE_NOTES label exempts this pull request.

@github-actions github-actions Bot added the AI-Tooling-Check-Scanned-Clean Tooling check: diff analyzed, no interesting infrastructure files label Sep 10, 2026

@T-Gro T-Gro left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 🕵️ LGTM

@github-project-automation github-project-automation Bot moved this from New to In Progress in F# Compiler and Tooling Sep 11, 2026
@T-Gro T-Gro added the AI-reviewed PR reviewed by AI review council label Sep 11, 2026
@T-Gro
T-Gro self-requested a review September 11, 2026 15:07
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) <noreply@anthropic.com>
@xperiandri
xperiandri force-pushed the diagnostics/name-commit-snapshot branch from b8a4444 to a601256 Compare September 11, 2026 16:16
@T-Gro T-Gro added the NO_RELEASE_NOTES Label for pull requests which signals, that user opted-out of providing release notes label Sep 21, 2026
@T-Gro
T-Gro merged commit e61d3ca into dotnet:main Sep 21, 2026
52 of 53 checks passed
bartelink pushed a commit to bartelink/fsharp that referenced this pull request Oct 7, 2026
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) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI-reviewed PR reviewed by AI review council AI-Tooling-Check-Scanned-Clean Tooling check: diff analyzed, no interesting infrastructure files NO_RELEASE_NOTES Label for pull requests which signals, that user opted-out of providing release notes

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

2 participants