Skip to content

F# hot reload: Edit-and-Continue delta emission behind --test:HotReloadDeltas - #19941

Open
NatElkins wants to merge 463 commits into
dotnet:mainfrom
NatElkins:hot-reload-v2
Open

NatElkins wants to merge 463 commits into
dotnet:mainfrom
NatElkins:hot-reload-v2

Conversation

@NatElkins

@NatElkins NatElkins commented Jun 12, 2026 •

Copy link
Copy Markdown
Contributor

This adds opt-in F# hot reload through --test:HotReloadDeltas. The compiler captures baselines and emits metadata, IL, and portable PDB deltas. FSharpChecker.CreateHotReloadSession coordinates project baselines and commits or discards pending updates.

The feature supports method edits, member additions, closures, state machines, generic methods, and supported metadata edits. Unsupported edits require a rebuild. --test:HotReloadClassStateMachines enables reference-type state machines for resume-point edits. Both compiler flags remain off by default.

This umbrella branch includes the remaining implementation slices: #20025, #20027, #20030, and #20031. Each PR targets main because the earlier foundations already merged. The September refresh merges main at 4831b381ee941f8bb03a19332c671ac86b9e82c3 and preserves the upstream naming modules.

The review fixes several concrete failures:

  • Abstract slots, field metadata, parameter metadata, and extension receivers now participate in edit classification.
  • Arrays, byrefs, and native pointers retain stable identities across independent compiler instances.
  • Disk sessions retain original metadata row IDs and compiler output bytes. Anonymous-record edits update one user method without new TypeSpec or MemberRef rows.
  • Distinct --out: paths retain independent project baselines.
  • Runtime-async flag transitions require a restart. Body edits with unchanged runtime-async flags pass runtime application.
  • The in-process compiler passes the current trait context. State-machine capture allocates resumption metadata only when capture is active.
  • The demo passes its retained original DLL to mdv, so metadata inspection uses the true baseline across edits.

Verification uses the repository SDK with BUILDING_USING_DOTNET=true. Runtime checks also set DOTNET_MODIFIABLE_ASSEMBLIES=debug and COMPlus_ForceEnc=1. All 11 verification steps pass: solution build, five architecture guards, both HotReload suites, both smoke modes, and direct runtime application. Service tests pass 491/491. Component tests pass 255, with two manual-host skips. The Release EmittedIL suite passes 1,644 tests, with three existing skips. That suite uses -p:FSHARPCORE_USE_PACKAGE=false so that tests load the current local FSharp.Core. Debug and Release builds report zero warnings and errors. Fantomas passes for the reviewed files.

CI passes on final head 74ec9f2c32adaa78a1cf1876fda67d2eed69b061. All 55 GitHub checks and all 51 Azure jobs succeed, including 22 downstream regression jobs. Azure run 1612549 records the complete result.

The original-metadata fix follows Roslyn EmitBaseline, commit eb789e2741f6f22d9e283e2049dc1378871323e0, dated June 9, 2026. The review compares complete mdv output and runtime application for equivalent method and anonymous-type edits. F# reuses existing reference rows where Roslyn revisits containing metadata. The tests preserve those intentional differences.

The SDK provider protocol remains a separate upstream discussion in dotnet/sdk#55464. A standalone fsharp-watch preview packages the existing watch integration while that contract develops.

@github-actions

github-actions Bot commented Jun 12, 2026 •

Copy link
Copy Markdown
Contributor

✅ Release notes checked


✅ Found changes and release notes in following paths:

Change path Release notes path Description
`src/Compiler` docs/release-notes/.FSharp.Compiler.Service/11.0.200.md

@NatElkins NatElkins mentioned this pull request Jun 12, 2026
@xperiandri

Copy link
Copy Markdown
Contributor

Looks like it requires rebase/conficts fix

Comment thread src/Compiler/Driver/fsc.fs Outdated
Comment thread src/Compiler/Generated/CompilerGeneratedNameMapState.fs Outdated
Comment thread src/Compiler/CodeGen/DeltaMetadataTables.fs Outdated
Comment thread src/Compiler/CodeGen/FSharpDeltaMetadataWriter.fs Outdated
Comment thread src/Compiler/TypedTree/CompilerGlobalState.fs
Comment thread src/Compiler/Generated/CompilerGeneratedNameMapState.fs Outdated
Comment thread src/Compiler/HotReload/ActiveStatements.fs
Comment thread src/Compiler/HotReload/HotReloadState.fs Outdated
Comment thread tests/FSharp.Compiler.Service.Tests/HotReload/ThreadSafetyTests.fs
Comment thread src/Compiler/TypedTree/TypedTreeDiff.fs
Comment thread src/Compiler/CodeGen/HotReloadPdb.fs
Comment thread src/Compiler/Service/service.fs Outdated
@WizMe-M

WizMe-M commented Jun 21, 2026 •

Copy link
Copy Markdown

Hi, I gave a try to hot-reload demo. I followed steps 1-5 and failed. What am I doing wrong?

First of all step 4 says:

Run it:
dotnet watch run --non-interactive
You should see ⌚ F# hot reload session prestarted, then the counter ticking once a second.
But my output was:

Missing `F# hot reload session prestarted`
HotReloadDemo> dotnet watch run --non-interactive
dotnet watch 🔥 Hot reload enabled. For a list of supported edits, see https://aka.ms/dotnet/hot-reload.
dotnet watch 💡 Press Ctrl+R to restart.
Restore complete (0,4s)
    info NETSDK1057: You are using a preview version of .NET. See: https://aka.ms/dotnet-support-policy
  HotReloadDemo net11.0 succeeded (2,7s) → bin\Debug\net11.0\HotReloadDemo.dll

Build succeeded in 3,7s
dotnet watch ⌚ Loading projects ...
dotnet watch ⌚ Loaded 1 project(s) in 0,4s.
dotnet watch ⌚ Waiting for changes
hello (count: 1) <--------------------------- missing 'F# hot reload session prestarted'
Looks like SDK/F#-compiler with hot-reload wasn't built.

After that I've tried to modify Program.fs and got expected result (TLDR: not works):

TLDR: not works
HotReloadDemo> dotnet watch run --non-interactive
dotnet watch 🔥 Hot reload enabled. For a list of supported edits, see https://aka.ms/dotnet/hot-reload.
dotnet watch 💡 Press Ctrl+R to restart.
Restore complete (0,4s)
    info NETSDK1057: You are using a preview version of .NET. See: https://aka.ms/dotnet-support-policy
  HotReloadDemo net11.0 succeeded (2,7s) → bin\Debug\net11.0\HotReloadDemo.dll

Build succeeded in 3,7s
dotnet watch ⌚ Loading projects ...
dotnet watch ⌚ Loaded 1 project(s) in 0,4s.
dotnet watch ⌚ Waiting for changes
hello (count: 1)
...
hello (count: 32)
dotnet watch ⌚ File updated: .\Program.fs
hello (count: 33)
...
hello (count: 56)
dotnet watch ⌚ File updated: .\Program.fs
hello (count: 57)
...
hello (count: 68)
dotnet watch 🔄 Restart requested. <------------------- ctrl+R
hello (count: 69)
hello (count: 70)
hello (count: 71)
dotnet watch ⌚ [HotReloadDemo (net11.0)] Exited
dotnet watch 🔄 Restarting.
Restore complete (0,5s)
    info NETSDK1057: You are using a preview version of .NET. See: https://aka.ms/dotnet-support-policy
  HotReloadDemo net11.0 succeeded (0,2s) → bin\Debug\net11.0\HotReloadDemo.dll

Build succeeded in 1,3s
dotnet watch ⌚ Loading projects ...
dotnet watch ⌚ Loaded 1 project(s) in 0,1s.
dotnet watch ⌚ Waiting for changes
HOT-RELOAD works (count: 1)   <------------------- only after ctrl+R
HOT-RELOAD works (count: 2)

Info:

  • OS: Windows 11 Pro (ran all steps from Powershell)
  • All $env variables were set correcly following manual
  • F#-compiler was built with no errors and warnings
  • SDK was built on third try with no errors and warnings
dotnet --info
HotReloadDemo> dotnet --info
.NET SDK:
 Version:           11.0.100-dev
 Commit:            ab1a0a0dbb
 Workload version:  11.0.100-manifests.844758e0
 MSBuild version:   18.8.0-preview-26277-111+6ca055abb

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.26200
 OS Platform: Windows
 RID:         win-x64
 Base Path:   ~\sdk-hotreload\artifacts\bin\redist\Debug\dotnet\sdk\11.0.100-dev\

.NET workloads installed:
 [maui-windows]
   Installation Source: VS 17.14.37328.6
   Manifest Version:    11.0.0-preview.1.26102.3/11.0.100-preview.1
   Manifest Path:       ~\sdk-hotreload\artifacts\bin\redist\Debug\dotnet\sdk-manifests\11.0.100-preview.1\microsoft.net.sdk.maui\11.0.0-preview.1.26102.3\WorkloadManifest.json
   Install Type:        FileBased

 [maccatalyst]
   Installation Source: VS 17.14.37328.6
   Manifest Version:    26.2.11310-net11-p1/11.0.100-preview.1
   Manifest Path:       ~\sdk-hotreload\artifacts\bin\redist\Debug\dotnet\sdk-manifests\11.0.100-preview.1\microsoft.net.sdk.maccatalyst\26.2.11310-net11-p1\WorkloadManifest.json
   Install Type:        FileBased

 [ios]
   Installation Source: VS 17.14.37328.6
   Manifest Version:    26.2.11310-net11-p1/11.0.100-preview.1
   Manifest Path:       ~\sdk-hotreload\artifacts\bin\redist\Debug\dotnet\sdk-manifests\11.0.100-preview.1\microsoft.net.sdk.ios\26.2.11310-net11-p1\WorkloadManifest.json
   Install Type:        FileBased

 [android]
   Installation Source: VS 17.14.37328.6
   Manifest Version:    36.1.99-preview.1.119/11.0.100-preview.1
   Manifest Path:       ~\sdk-hotreload\artifacts\bin\redist\Debug\dotnet\sdk-manifests\11.0.100-preview.1\microsoft.net.sdk.android\36.1.99-preview.1.119\WorkloadManifest.json
   Install Type:        FileBased

Configured to use workload sets when installing new manifests.
No workload sets are installed. Run "dotnet workload restore" to install a workload set.

Host:
  Version:      11.0.0-preview.6.26277.111
  Architecture: x64
  Commit:       6ca055abbe

.NET SDKs installed:
  11.0.100-dev [~\sdk-hotreload\artifacts\bin\redist\Debug\dotnet\sdk]

.NET runtimes installed:
  Microsoft.AspNetCore.App 11.0.0-preview.5.26227.104 [~\sdk-hotreload\artifacts\bin\redist\Debug\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 11.0.0-preview.6.26277.111 [~\sdk-hotreload\artifacts\bin\redist\Debug\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 6.0.36 [~\sdk-hotreload\artifacts\bin\redist\Debug\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 7.0.20 [~\sdk-hotreload\artifacts\bin\redist\Debug\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 8.0.28 [~\sdk-hotreload\artifacts\bin\redist\Debug\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 9.0.17 [~\sdk-hotreload\artifacts\bin\redist\Debug\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 10.0.9 [~\sdk-hotreload\artifacts\bin\redist\Debug\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 11.0.0-preview.5.26227.104 [~\sdk-hotreload\artifacts\bin\redist\Debug\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 11.0.0-preview.6.26277.111 [~\sdk-hotreload\artifacts\bin\redist\Debug\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.WindowsDesktop.App 11.0.0-preview.5.26227.104 [~\sdk-hotreload\artifacts\bin\redist\Debug\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 11.0.0-preview.6.26277.111 [~\sdk-hotreload\artifacts\bin\redist\Debug\dotnet\shared\Microsoft.WindowsDesktop.App]

Other architectures found:
  x86   [C:\Program Files (x86)\dotnet]
    registered at [HKLM\SOFTWARE\dotnet\Setup\InstalledVersions\x86\InstallLocation]

Environment variables:
  DOTNET_MSBUILD_SDK_RESOLVER_CLI_DIR      [~/sdk-hotreload/artifacts/bin/redist/Debug/dotnet]
  DOTNET_MULTILEVEL_LOOKUP                 [0]
  DOTNET_ROLL_FORWARD_TO_PRERELEASE        [1]
  DOTNET_ROOT                              [~/sdk-hotreload/artifacts/bin/redist/Debug/dotnet]

global.json file:
  Not found

Learn more:
  https://aka.ms/dotnet/info

Download .NET:
  https://aka.ms/dotnet/download

@ijklam

ijklam commented Jun 21, 2026 •

Copy link
Copy Markdown
Contributor

After spending one or two hours debugging this, I found why this cannot work on Windows. The issue is that on the Windows, when the program is running, the program file will be locked. As a result, the "dotnet build" executed by the dotnet-watch's FSharpHotReloadService fails, cause the hot reload cannot work.

I finally have managed to make it work, done some simple test on it, and found some problems:

  1. It seems working only when editing the content in the method body.
type Greeter() =
    let mutable count = 0

    member _.Message() =
        count <- count + 1  // Modifying this number is OK
        task {
           do! Task.Delay 2000 // Modifying this number does not take effect until restart
        }
        |> _.GetAwaiter().GetResult()
        sprintf "hello (count: %d)" count  // Modifying this string is OK

let greeter = Greeter()

while true do
    printfn "%s" (greeter.Message())   // Modifying this string doesn't take effect, until restart the program
    System.Threading.Thread.Sleep(1000)   // Changing this 1000 to 2000 doesn't take effect, until restart the program
  1. Simply adding a do! ... in a task block will need to restart the program.
    member _.Message() =
        count <- count + 1
        task {
          do! Task.Delay 2000
          do! Task.Delay 2000  // add this line
        }
  1. Adding or removing lines around a task needs restart program.
    member _.Message() =
        count <- count + 1  // remove this line
        task {
          do! Task.Delay 2000
        }

NatElkins added a commit to NatElkins/sdk that referenced this pull request Jun 22, 2026
The per-edit dotnet build refreshed the bin output the running process has
loaded. Windows locks that file against writes while the app runs, so the
build failed on every edit and the change fell back to a full restart instead
of applying in place.

Build the Compile target only (the obj intermediate assembly fsc writes) and
point the hot reload session's baseline and emit reads at that intermediate
assembly via FSharpProjectInfo.IntermediateAssemblyPath. The running process
never loads that file, so nothing is locked, and the bin output is left at
generation 0 until the next real restart. macOS and Linux already tolerated
overwriting a mapped file; this lines all three platforms up on the same path.

Reported on dotnet/fsharp#19941.
@NatElkins

NatElkins commented Jun 22, 2026 •

Copy link
Copy Markdown
Contributor Author

@ijklam got it right. The bridge ran a full dotnet build on every edit, and the last step of that build copies the assembly into bin, which is the file the running app has loaded. Windows keeps that locked while the process is alive, so the copy failed, the build failed with it, and the edit fell back to a restart. macOS and Linux let you replace a file that's mapped into a live process, so it only bit on Windows.

The fix builds just the Compile target instead, so fsc refreshes the intermediate assembly under obj and nothing writes to the loaded bin copy. The session reads the delta from that intermediate assembly now. bin stays put until the next real restart, and all three platforms take the same path.

It's on fsharp-hotreload-watch-v2 (fcf9b49). Only the dotnet-watch side changed, so you just need to pull that branch and rebuild the sdk clone, no need to rebuild the compiler or recopy the FCS dll. I don't have a Windows machine handy at the moment, so if one of you can confirm it works there now that would help a lot.

@WizMe-M the missing F# hot reload session prestarted line was a red herring. It only prints with DOTNET_WATCH_TRACE_FSHARP_HOTRELOAD=1 set, so not seeing it didn't mean anything was broken. I've reworded the quickstart to say that, and to point at the counter instead (it keeps climbing across edits when reloads land, and resets to 1 on a restart).

On the other observations: editing the top level while loop, or adding a do! to a task block, or moving lines around one, are mostly the expected limits rather than the Windows bug. The running loop is the active frame, so it can't be swapped while it's sitting on the stack, and inserting an await into a state machine is a rude edit in C# too. The count and string edits applying in place is exactly the case that should work. If the line-shift-around-a-task case still misbehaves after the rebuild, ping me, that one is worth a second look.

EDIT: Correcting myself on the struck-out line. @ijklam is right, C# hot reload does support adding an await/do! into a state machine, I read the EnC rules wrong. Roslyn treats it as a normal method update (gated on the runtime's AddInstanceFieldToExistingType capability), and only makes it a rude edit when the method is suspended at an active statement at the moment you save. So adding a do! to a task is a genuine parity gap on the F# side today, not an expected limit. I'm going to look into closing it.

@ijklam

ijklam commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

I think "adding or removing lines before a task block needs a restart" is something worth to be solved, if we hope the feature can help us in the routine coding, as there may be many task in a file, to make the program running asynchronously.

inserting an await into a state machine is a rude edit in C# too

By the way, C# hot reload supports this already.

@NatElkins NatElkins changed the title F# hot reload: Edit-and-Continue delta emission behind --enable:hotreloaddeltas F# hot reload: Edit-and-Continue delta emission behind --test:HotReloadDeltas Jun 23, 2026
NatElkins added a commit to NatElkins/fsharp that referenced this pull request Jun 23, 2026
… identity

The resumable-code/trait shape digest rendered builder-call type
instantiations through TType.ToString() (tyToString), whose depth-limited
LimitedToString(4) collapses a deeply-solved typar to the literal "True".
That made the digest non-injective: a `task` whose return type is `int`
both sides could render Bind<int,int,int> before an edit and
Bind<int,True,True> after, producing a false StateMachineShapeChange
(FSHRDL013) rude edit.

Render the digest through tryTypeIdentityFromTType -- the same injective
runtime-identity encoder the capture path already stores -- threading the
method's typar->ordinal map into collectLoweredShapeInfo and
traitConstraintShapeDigest, with a structured formatter and a display-string
fallback only for types the encoder cannot represent. Update the architecture
guard for the new traitConstraintShapeDigest signature.

Addresses review feedback on dotnet#19941.
NatElkins added a commit to NatElkins/fsharp that referenced this pull request Jun 23, 2026
FSharpEditAndContinueLanguageService.UpdateActiveStatements had no callers:
the live path is SetActiveStatements (FSharpHotReloadSession.SetActiveStatements
-> SetSessionActiveStatements -> editAndContinueService.SetActiveStatements).
Remove the dead member and its now-orphaned HotReloadSessionStore.UpdateActiveStatements
backing, whose only caller was the dead forwarder.

Addresses review feedback on dotnet#19941.
Comment thread src/Compiler/HotReload/EditAndContinueLanguageService.fs Outdated
Comment thread src/Compiler/HotReload/RudeEditDiagnostics.fs
Comment thread src/Compiler/HotReload/EditAndContinueLanguageService.fs Outdated
Comment thread src/Compiler/FSComp.txt
Comment thread src/Compiler/Driver/CompilerConfig.fs
NatElkins added a commit to NatElkins/fsharp that referenced this pull request Jun 23, 2026
…error channel

Rude-edit reasons were flattened to a single string at the point of failure
(UnsupportedEdit of string), discarding the per-edit Id, Severity and symbol
before they reached the public API and the dotnet-watch bridge. Carry them
structurally instead:

* RudeEditDiagnostic gains a Severity (FSharpDiagnosticSeverity). Every kind is
  Error today; severityOf is the single place to introduce a non-blocking
  Warning later (e.g. a "might not take effect" edit).
* HotReloadError.UnsupportedEdit and the public FSharpHotReloadError.UnsupportedEdit
  now carry a list of structured diagnostics. A new public FSharpHotReloadRudeEdit
  record exposes Id, Severity, Message and SymbolName.
* The active-statement, deleted-symbol, mapping-error and emit-exception paths
  wrap their ad-hoc reason via RudeEditDiagnostics.unsupported so every error
  still carries an id.

The id namespace is unchanged and still owned solely by
RudeEditDiagnostics.diagnosticId, so the channel itself is id-agnostic.

Addresses review feedback on dotnet#19941.
NatElkins added a commit to NatElkins/fsharp that referenced this pull request Jun 23, 2026
…he swap point

Document, at RudeEditDiagnostics.diagnosticId (the single place the rude-edit id
namespace is decided), why F# keeps its own FSHRDL* codes rather than emitting
Roslyn's ENC* codes, and note the closest ENC analogs. Kept as a separate commit
from the channel work so aligning with the ENC codes later is an isolated change.

Addresses review feedback on dotnet#19941.
NatElkins added a commit to NatElkins/sdk that referenced this pull request Jun 23, 2026
…t-watch

The F# bridge collapsed the rude-edit reason to a record/DU ToString() dump and
logged it at Debug only, so an edit that forces a restart gave the user no reason.
Now that FSharpHotReloadError.UnsupportedEdit carries a structured rude-edit list
(Id + Severity + Message), read it via reflection (TryFormatRudeEdits) into a clean
"{Id}: {Message}" reason and report it as a warning instead of at Debug. The
extraction is defensive: any shape mismatch falls back to ToString(), so the bridge
stays correct against older FCS builds (where UnsupportedEdit still carries a string).

Pairs with the FCS-side structured diagnostics channel on dotnet/fsharp#19941.
Addresses review feedback on #1.
Comment thread src/Compiler/HotReload/EditAndContinueLanguageService.fs
Move the umbrella release note to11.0.200 and retain the integrated session documentation. Refresh the guard fingerprints for the intentional CompilerConfig spacing and service XML documentation changes.

Validation: the Debug solution build passed with zero warnings and errors. The signature drift guard passed for all eight allowed files.
Use the compiler instance predicate so extension receivers remain real static parameters. Match the emitted declaring type and recover receiver names from implementation binders when signature argument names are absent.

Validation: service project build has zero warnings or errors. All 59 HotReload and surface-area tests pass, including the failing-first receiver rename and constructor argument regression.
Keep extension receivers in static method parameter identities, names, and counts. Use the actual declaring module and recover unnamed receiver metadata from the implementation binder.

Validation: Debug solution build passes with zero warnings or errors. All 61 service HotReload and surface-area tests pass. Regressions verify receiver parameter updates, emitted parameter identities, and constructor parameter preservation.
Preserve the richer differ parameter capability path and matching receiver regressions from 125e0c9. The emitter layer supports UpdateParameters, while the foundational slice requires a restart.
# Conflicts:
#	tests/FSharp.Compiler.Service.Tests/HotReload/TypedTreeDiffTests.fs
The regression fails before the dependency fix because the matcher selects the receiver type instead of the emitted extension module. It now verifies the existing Param row, required capability, receiver name, explicit parameter, and changed method body after MetadataUpdater.ApplyUpdate.
Treat only a missing executable as an unavailable mdv tool. Other process failures remain test failures.

Validation: component build passes with zero warnings or errors. HotReload and RuntimeAsync filters pass 162 tests with 3 existing skips. The mdv and runtime application tests execute successfully.
# Conflicts:
#	tests/FSharp.Compiler.Service.Tests/HotReload/TypedTreeDiffTests.fs
The interface resides in CompilerGeneratedNameMapState on current main. The guard now checks that dependency while it still rejects a direct SynthesizedTypeMaps dependency.

Validation: all17 architecture checks pass. The integrated HotReload component suite passes248 tests with2 intentional manual-host skips.
Compare the Async implementation bit before baseline flag reuse. Preserve flags for delta-added methods so later generations enforce the same rule.

Six regressions cover both directions, added-method generations, metadata flags, and runtime ApplyUpdate with Task results 42 to 43. All 39 HotReload component tests pass with no skips. Full mdv baseline and delta output reviewed.

Reference: Roslyn eb789e2741f6f22d9e283e2049dc1378871323e0 MetadataWriter and non-custom MethodImplAttribute rules, inspected 2026-09-25. Runtime-async EnC support remains tracked by dotnet/roslyn#77954.
Point mdv at the retained runtime copy. Each edit replaces the build output, so that path no longer identifies generation zero.

Verified two scripted generations with full mdv inspection, both without runtime apply and with MetadataUpdater.ApplyUpdate. Runtime counters advanced from 1 to 3.
@github-actions

Copy link
Copy Markdown
Contributor

🔍 Tooling Safety Check — Affects-Bootstrap, Affects-Build-Infra, Affects-Compiler-Output, Affects-Test-Tooling
Affects-Bootstrap: Changes sources used in the compiler bootstrap chain.
Affects-Build-Infra: Changes project files and executable build/test scripts.
Affects-Compiler-Output: Changes optimizer, IL emission, and metadata serialization paths.
Affects-Test-Tooling: Adds process-launching hot-reload test infrastructure.

Generated by PR Tooling Safety Check · gpt56 405.2K · ◷

WillEhrendreich added a commit to WillEhrendreich/SageFs that referenced this pull request Oct 2, 2026
…ion reviewed by the F# team, not Microsoft's own

A source-checked follow-up found dotnet/fsharp#19941 and its slices are authored by Nat Elkins with T-Gro reviewing;
none is merged and a stock SDK 11 RC1 fsc rejects --test:HotReloadDeltas.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016SwvKtCMh6Dg7JAWDof5LS
WillEhrendreich added a commit to WillEhrendreich/SageFs that referenced this pull request Oct 2, 2026
…ments say why

decisions.md replaces the entry that said a generic function always restarts. It carries what was measured on
net10.0.12 and net11.0.0-rc.1 (the compiled bodies and their sizes, who calls what with which hidden argument, what
each kind of detour does, what aborted the host in the earlier measurement, tiering), the design, where a probe
sits, every case that restarts and why, and what a real fix would need (a metadata delta through
MetadataUpdater.ApplyUpdate, which dotnet/fsharp#19941 is building the compiler side of, or a profiler ReJIT).

hot-reload.md gets the rows that pass and the restarts that remain, with the parity rows that pin each. The how-it-works
page gets the generics paragraph, the comparison row and the two "still rough" bullets, only as far as the rows
prove them. progress.md is a dated changelog linked to a commit and is left alone.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016SwvKtCMh6Dg7JAWDof5LS
WillEhrendreich added a commit to WillEhrendreich/SageFs that referenced this pull request Oct 2, 2026
…embly's method bodies (off, not wired)

Reads the assembly a process has loaded and a fresh build of the same project,
and writes the three things MetadataUpdater.ApplyUpdate takes: a method body
patch for every changed method, and an added method on a type the process
already runs. Pure over System.Reflection.Metadata. Off unless
SAGEFS_METADATA_DELTA is on, and nothing in the product reads it yet.

What decides things is what a token names, never its row number or an IL byte
offset. PeImage gives every token a symbolic text; IlCanon reads a body into
instructions whose operands are those texts, whose branches are instruction
indices, and whose short forms are folded to the long one. MethodDiff then says,
per method, Unchanged, BodyChanged, Added or Rude, with a closed RudeCause (15
cases, one describe and one caseName each). Closure classes are named after the
line they are written on (clo@18-3), so a line added above renames them: the
number is folded out of the name and closures that then share a name are told
apart by order, and a different number of them is refused (ClosureMatchAmbiguous)
instead of guessed. A changed static constructor or startup-class method is
refused, because a delta replaces a body and never runs it again.

DeltaWriter writes the delta: bodies with every token re-expressed in the
baseline's numbering (an existing row when the baseline names the same thing, a
new AssemblyRef, TypeRef, TypeSpec, MemberRef, MethodSpec or StandAloneSig row
when it does not), string literals into the delta's own #US heap, exception
regions, added methods with their parameters, and EncLog and EncMap in token
order. DeltaChain carries what a later delta must know (heap sizes, rows added,
the EncId chain): Prepare does not change it, Commit returns the next one, and a
delta that did not apply is dropped with the chain it came from still right. It
fails closed on calli, a signature element it does not know, a token from a table
it cannot write, and a baseline with no #US heap.

DeltaApply asks what the process can do before the call (runtime support, the
DOTNET_MODIFIABLE_ASSEMBLIES variable, a debugger, an optimized or reflection-emit
module, the runtime's capability names against what the delta needs) and turns
the runtime's answer into a closed outcome. After a delta applies it runs every
MetadataUpdateHandler, which is what makes a method the delta added appear in
reflection; without it GetMethods kept the list it cached.

The baseline is the module the worker loads, which for run_app is the shadow
copy that CoverageInstrumenter rewrote in place. The differ looks through the
instrumenter's probes (ldc.i4 slot, call __SageFsCoverage::Hit), and a test
pins that symbol against the real instrumenter's output.

Checked against a process that applies the delta, 16 tests, 12 in child
processes. A soak of 12x the seeds (432 single, 144 two-generation, 96
three-generation, 192 against an instrumented baseline) is green. The soak is
what found that a Cecil-written module with no string literal has no #US heap
and that a delta adding the first literal reads as "no string associated with
token": that case is now a refusal, and test programs keep one literal.

Prior art: dotnet/fsharp#19941 (Nat Elkins, unmerged) writes these tables from
inside the compiler behind a compiler flag no released SDK has. This reads two
finished assemblies instead. No code is taken from it.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016SwvKtCMh6Dg7JAWDof5LS
WillEhrendreich added a commit to WillEhrendreich/SageFs that referenced this pull request Oct 2, 2026
…eload workers, with a measured cost and the wire to say so

With the variable on, the whole host tier (312 tests, none failed) and the eight
browser journeys passed, a save to a run_app app is served in 2.6 s on .NET 10
and 1.8 s on .NET 11 against about 7.5 s and 5.8 s for the restart it replaces,
and a loop of 50 million calls costs the same with the process editable
(489 against 481 ms, 444 against 448 ms). SAGEFS_METADATA_DELTA=off is the
way back.

The daemon's lastReload and ReloadReported event now carry the worker's
mechanism (detour, metadata-delta, empty for a verdict that is not a patch) as a
closed set, so a client never has to read it from the words. The fixture gets a
call-heavy route, and RunAppDeltaTests prints the cost (DELTA-COST) with a
tripwire at twice.

The docs say what the route takes, what it refuses and why, what it costs, and
what it leaves undone: the REPL and live tests keep the last build's code until a
restart. Prior art credited to dotnet/fsharp#19941 (Nat Elkins).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016SwvKtCMh6Dg7JAWDof5LS
T-Gro added a commit that referenced this pull request Oct 16, 2026
* Avoid leaking a MeterListener per Cache in DEBUG builds (#19995)

* Avoid leaking a MeterListener per Cache in DEBUG builds

In DEBUG builds, every Cache instance created a CacheMetrics.CacheMetricsListener,
which starts a System.Diagnostics.Metrics.MeterListener registered in the
process-global metrics registry. These were never disposed, so they accumulated for
the lifetime of the process. Because every cache hit/miss/add publishes a measurement
to all registered listeners, the per-operation cost grew linearly with the number of
leaked listeners, so workloads that create many caches (for example repeated
ParseAndCheckProject / per-file checks) slowed down steadily.

Track the per-cache totals used by DebugDisplay directly, incrementing a small Stats
object alongside the existing global Meter counters, instead of via a per-cache
MeterListener. No listener is created, so nothing leaks, and DebugDisplay still works.
The now-unused CacheMetrics.Hit/Miss/Add/Update/Eviction/EvictionFail helpers are
replaced by a single recordMetric helper.

* Address review: drop per-cache CacheMetricsListener and cacheId tag

- Remove the CacheMetrics.CacheMetricsListener type. Its only per-cache use
  was the #if DEBUG debugListener each Cache created and never disposed, which
  was the leak this PR set out to fix. (majocha)
- Drop the per-instance cacheId tag (and nextCacheId). Measurements now carry
  only the cache name, shrinking the payload published to any connected
  exporter and removing the per-instance filtering that was cacheId's only
  purpose. (majocha)
- DebugDisplay and the cache tests read the existing name-aggregated stats via
  CacheMetrics.getTotalsByName / getRatioByName, populated by the single
  process-wide ListenToAll listener. No per-cache listener is created and no
  per-operation cost is added in any configuration, so there is no DEBUG-only
  overhead left to gate behind a separate directive. (T-Gro)
- Overload-cache tests enable ListenToAll and snapshot totals before/after to
  stay scoped to their own compilation; FSharpChecker
  .CreateOverloadCacheMetricsListener is removed.

* Update public SurfaceArea baseline after removing CacheMetricsListener

CacheMetricsListener was a public type, so dropping it changes the recorded
public surface. Remove its 10 entries from
FSharp.Compiler.Service.SurfaceArea.netstandard20.bsl; the SurfaceArea test
now passes. Also note the single-listener assumption the cache metric tests
rely on.

* Apply fantomas formatting to Caches.fs

* Document why OverloadCacheTests is not parallelizable (global cache metrics state)

---------

Co-authored-by: Tomas Grosup <Tomas.Grosup@gmail.com>
(cherry picked from commit 7192705)

* Add ResetCompilerGeneratedNameState to compiler-generated name generators (#20017)

Compiler-generated occurrence names (name@line-N) are allocated from process-wide
counters on CompilerGlobalState that accumulate across compilations. When a warm
checker re-emits the same project in-process, an unchanged closure therefore gets a
different occurrence suffix than the previous emit, so consumers that align generated
names across compilations (Edit-and-Continue delta emission, #19941)
cannot match them.

Add an internal ResetCompilerGeneratedNameState to NiceNameGenerator (clears the
per-(name, file) occurrence counters), StableNiceNameGenerator (clears the cached
stable names and the inner counters), and an aggregate on CompilerGlobalState that
resets all three generators, restoring the fresh-process name layout. Callers must
ensure no compilation is concurrently generating names.

No in-tree caller yet; the consumer is the hot reload emit path in #19941.
Covered by unit tests proving drift without reset, exact replay after reset, and that
the stable-name cache itself is cleared.

(cherry picked from commit 60d315a)

* Localized file check-in by OneLocBuild Task: Build definition ID 499: Build ID 3013177 (#20023)

Co-authored-by: Copilot <copilot@github.com>
(cherry picked from commit 9fc230a)

* Tests/source context: support multiple carets (#20077)

(cherry picked from commit 69fca7f)

* Move VS language-service logic tests to FSharp.Compiler.Service.Tests (#20033)

* Move VS language-service logic tests to FSharp.Compiler.Service.Tests

Port completion, quick info, parameter info, go-to-definition, and
diagnostics coverage from the Windows-only VS Salsa suite to the
cross-platform FSharp.Compiler.Service.Tests. The legacy suite keeps only
the tests that genuinely exercise Visual Studio integration.

(cherry picked from commit 2f07589)

* Add Roslyn-format EnC CustomDebugInformation codec and portable PDB method CDI emission (#20018)

* Add Roslyn-format EnC CustomDebugInformation codec and portable PDB method CDI emission

Adds an internal AbstractIL module implementing, byte for byte, the three Portable PDB
CustomDebugInformation blob formats Roslyn persists per method for Edit and Continue
(EnC Local Slot Map, EnC Lambda and Closure Map, EnC State Machine State Map), with
serializers, deserializers, a portable PDB read-back helper, and an occurrence-key
packing helper for deterministic syntax-offset slots.

Plumbs an optional methodCustomDebugInfoRows side channel through the IL binary writer
options into the portable PDB generator so a compilation can attach CDI rows to named
methods. Names that do not identify exactly one method row are dropped. All existing
writer call sites pass an empty map, so emitted PDBs are byte-identical to before.

No in-tree caller populates the map yet; the consumer is the F# hot reload work in
#19941, following the same pattern as #20017 (land isolated, test-covered
infrastructure first, wire the feature later).

Tests: blob round-trips, Roslyn golden-byte encodings, cross-validation against
CDI blobs emitted by a real Roslyn compilation, fail-closed occurrence-key packing
(including an int32-overflow regression where a wrapped negative key previously
escaped the bound check), and end-to-end synthetic PDB emission proving correct
MethodDef parenting, zero rows for an empty map, and no rows for absent or
ambiguous names.

(cherry picked from commit 24d731a)

* Add stable synthesized-name replay infrastructure for hot reload (#20024)

* Extract stable synthesized-name replay layer

Add internal generated-name normalization and synthesized-name map replay support as a standalone slice. The new map state is side-channel based, all new compiler modules remain internal, and CompilerGlobalState preserves the existing no-map counter path while checking an accessor captured once per compiler state.

Route existing IlxGen generated-name allocations through inert helper wrappers, add pure name-map and normalizer tests, add a normal compilation determinism guard over emitted generated names, and document the extracted seams in P5_REPORT.md.

Verification: built FSharp.Compiler.Service, FSharp.Compiler.Service.Tests, FSharp.Compiler.ComponentTests, and FSharpSuite.Tests in Release; ran the migrated service test classes, the component determinism class, FSharpSuite DeterministicTests, and the FCS SurfaceArea class successfully.

* Fix generated-name scope test in stable names slice

* Validate hot reload generated names before classification

* Format hot reload compiler sources

Verified with the repository-wide Fantomas check.

* Retry CI after Linux runner memory exhaustion

* Make synthesized name snapshots deterministic

(cherry picked from commit 2e838bb)

* Fix #19457: lift CE constructs from plain let RHS in computation expressions (#19868)

(cherry picked from commit 4a2749e)

* Fix attribute resolution in recursive module/namespace scopes (#19744)

(cherry picked from commit 1d8dc39)

* Correct StructLayout size emission for data-less struct unions (#19759)

(cherry picked from commit 1dc395a)

* Report FS3888 for generic attribute type abbreviations instead of FS0193 (#19915)

(cherry picked from commit 8c0e444)

* Support NotNullIfNotNullAttribute (#19977)

(cherry picked from commit 0c94e4e)

* Compiled ToStrings under -reflectionfree for DUs and Records (#19976)

* Add a compiler intrinsic for the 'string' operator

Adds string_operator_info / mkCallStringOperator so generated code can call
Operators.string. These lines are duplicated by the interpolated-string PR
(#19971); kept identical there so a future merge resolves cleanly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Generate a match-based ToString for unions under --reflectionfree

Under --reflectionfree the union ToString previously emitted nothing, so
DUs fell back to Object.ToString() (the namespace-qualified type name).
Instead generate a match over the cases that builds "CaseName(f0, f1, ...)"
using the 'string' operator on each field, via a TypedTree expression fed
to CodeGenMethodForExpr. This recurses naturally into nested unions and is
reflection-free. The default (sprintf "%+A") path is unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Extract mkStringConcat helper for arity-dispatched String.Concat

The "concatenate a list of string exprs, picking the cheapest String.Concat
overload by arity" pattern was duplicated in CheckExpressions (interpolation
lowering) and the optimizer, and our new union ToString used the array
overload unconditionally. Extract mkStringConcat into TypedTreeOps.ExprOps
and route all three through it. This also lets single-field union cases emit
Concat3 instead of allocating a string[] (IlxGen runs after the optimizer, so
nothing else would collapse that array form).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Fix generated union ToString for generic unions

The match-based ToString body is a TypedTree expression codegen'd via
CodeGenMethodForExpr, but it was built with `eenv`, which lacks the tycon's
type parameters. For generic unions this produced wrong IL: the wrong case
branch (always the null-as-true-value case) or a NullReferenceException for
single-case unions. Use `eenvinner` (the per-tycon environment) so the
generic method body resolves its type parameters. The old sprintf path was
unaffected because it emits raw IL off the pre-built ilThisTy.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Render union ToString fields like option (null -> "null")

To make a generated union ToString consistent with how option/list format
their contents (LanguagePrimitives.anyToStringShowingNull), format each field
as: if (box field) is non-null then 'string field' else "null". Previously a
null field rendered as "" (the 'string' operator's null behaviour). Generated
inline rather than calling anyToStringShowingNull, which is internal to
FSharp.Core and so not callable from user-compiled code.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Tidy reflection-free union ToString tests

Normalize union declarations to a leading '|', use System.Console.WriteLine
instead of printfn (the printf machinery is what these changes move away
from), and make the null-field test compare the union's rendering directly
against option's rather than asserting a fixed string.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Add reflection-free ToString to Result and Choice

Result and Choice had no ToString override, so they fell back to the
compiler-generated sprintf "%+A" one, which uses reflection. Give them
hand-written overrides mirroring option/list (String.Concat +
anyToStringShowingNull), e.g. Ok 5 -> "Ok(5)", Choice1Of2 7 -> "Choice1Of2(7)".
This is reflection-free / AOT-friendly and consistent with option's "Some(x)"
rendering. Note: this changes the observable ToString of Result/Choice from
the "%A"-style "Ok 5" to "Ok(5)".

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Generate a single-line ToString for records under --reflectionfree

Records previously fell back to Object.ToString() (the namespace-qualified
type name) under --reflectionfree. Generate "{ F1 = v1; F2 = v2 }" on a single
line (no line breaks, unlike sprintf "%+A"), with fields formatted like union
fields (null -> "null", otherwise via 'string'). Factor the shared field
formatter and ToString-method emission out of the union path. The default
(sprintf "%+A") path is unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Update FSharp.Core surface-area baselines for Result/Choice ToString

Result and Choice`2..7 now declare an explicit ToString() override, so they
appear in the public surface area.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Add release notes

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Generate a single-line ToString for anonymous records under --reflectionfree

Drive anonymous-record ToString through the synthetic record tycon (already
built for equality/comparison) rather than sprintf "%A", so under
--reflectionfree it renders "{| Name = value; ... |}" on a single line.
GenRecordToStringMethod now takes open/close brace strings ("{ "/" }" for
records, "{| "/" |}" for anonymous records). The default (non-reflection-free)
codegen path is unchanged and still falls back to sprintf "%+A".

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Test that a hand-written ToString override is kept under --reflectionfree

Addresses review feedback: generation is gated on `not (HasMember "ToString")`,
so a user-defined ToString on a union or record wins over the generated one.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Rename ToString generators for clarity

Addresses review feedback: distinguish the reflective sprintf path from the
structural one. GenPrintingMethod -> GenSprintfPrintingMethod (the sprintf "%+A"
ToString/get_Message), GenToStringMethodFromExpr -> EmitToStringMethodDef.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Restore tabular layout for string_operator_info in TcGlobals

Addresses review feedback: keep the column-aligned layout of the surrounding
intrinsic table. Also makes these two lines byte-identical to the same intrinsic
added by #19971, so a future merge resolves cleanly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Add reflection-free ToString tests for field shapes, structs, anon records and recursion

Covers DU field shapes (multiple fields vs a single tuple field), explicit
vs unnamed field names rendering identically, struct unions/records,
anonymous and struct anonymous records, and finite recursive/nesting types.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Add EmittedIL tests for reflection-free record and union ToString

Locks in the IL emitted under --reflectionfree: each field is boxed and
rendered through Operators.ToString with a null guard, and the parts are
joined with String.Concat (array form for the record, 3-arg form for the
single-field union case). Nullary union cases return the bare case name.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Generate reflection-free ToString in the augmentation phase

The structural ToString for --reflectionfree records and unions was built in
IlxGen, after the optimizer, so its per-field 'string' operator calls were
never inlined: each value-type field was boxed and rendered through the
generic Operators.ToString, behind a null guard that is dead for a value type.

Move the generation into the type-augmentation phase (alongside
Equals/GetHashCode/CompareTo) so the body flows through the optimizer. The
'string' operator is now specialised - a value-type field renders via a direct,
allocation-free invariant-culture ToString with no boxing and no null guard
(reference fields keep the guard so null still renders as "null"). The shared
body builders live in AugmentTypeDefinitions; anonymous record types are
synthesized too late for augmentation, so they keep generating in IlxGen but
reuse the same builder.

Output is unchanged; the EmittedIL baselines are updated to the leaner IL.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Guard generated reflection-free ToString against deep-recursion overflow

The augmentation-generated structural ToString recurses into fields, so a deeply
nested value can exhaust the stack with an uncatchable StackOverflowException.
Emit RuntimeHelpers.EnsureSufficientExecutionStack() at method entry (as C# records
do in PrintMembers) so it throws a catchable InsufficientExecutionStackException
instead, when the runtime provides the method. The guard is skipped for types whose
every field is a flat primitive (integer/float/decimal/string/char/bool/unit/enum),
which cannot recurse.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Test the reflection-free ToString deep-recursion guard

A 1,000,000-deep value's generated ToString throws a catchable
InsufficientExecutionStackException rather than hard-crashing the process.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* revert ToString additions to fsharp.core types

* Remove stale FSharp.Core release note for the reverted Result/Choice ToString

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Fix code formatting in IlxGen.fs (dotnet fantomas)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* don't use quoted name

* int version of reflectionfree-printing doc

* doc tweaks

* Link release note to the printing doc and cover anonymous records

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* test backticks

* Share the ToString recursion guard with anonymous records

The guard lived in MakeBindingsForToStringAugmentation, which anonymous
records bypass: they are synthesized too late for type augmentation and
reach mkRecdToString from IlxGen instead. Deep nesting overflowed the
stack rather than raising InsufficientExecutionStackException.

Move it into mkToStringRecursionGuard, applied inside mkRecdToString and
mkUnionToString, so every caller of the body builders gets it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Add EmittedIL baselines for struct and anonymous record ToString

Struct records and unions read fields off the this pointer and switch on
the tag, and the anonymous record path is generated separately in IlxGen,
so each gets its own baseline.

The anonymous baseline omits the field reads: they name the anonymous
type, whose mangled name is not stable across compilations.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Fix empty anonymous record ToString rendering a doubled space

The open/close braces carry inner spaces ("{| " and " |}"); with no
fields they abut and render "{|  |}". Trim the leading space when the
field list is empty, matching %A's "{| |}".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* tidy comment

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
(cherry picked from commit 17cb503)

* Run ilverify via the tool manifest instead of a hard-coded cache path (#20101)

(cherry picked from commit c00299f)

* Record spreads (#18927)

(cherry picked from commit f4b785f)

* Implement interpolated strings via String.Concat (#19971)

(cherry picked from commit d3403ca)

* Secure release-note checks for fork pull requests (#20081)

* Secure release-note checks for fork pull requests

* Address release-note workflow review feedback

(cherry picked from commit 647548b)

* Update test project to net11 (#20104)

* Update test project to net11

Internal CI was failing since the move to net11 because restoring this
test project had to suddenly be done via network call to nuget.org

* Update target framework and PDB path in tests

(cherry picked from commit 536800c)

* LexFilter: drop non-strict mode (#20106)

(cherry picked from commit ea778bb)

* Implement direct delegates (#19993)

(cherry picked from commit d89529c)

* Fix check_release_notes 403 by restoring pull-requests: write and making the comment non-fatal (#20198)

check_release_notes runs via pull_request_target, so GitHub executes the workflow from the default branch (main). Creating the informational PR comment requires pull-requests: write, but #20081 reduced the token to read, turning the check red with HTTP 403 on any PR that had to create (not update) the comment - e.g. Maestro/darc PR #20133. Restore pull-requests: write so the comment posts, and guard the comment step with continue-on-error plus try/catch so posting can never fail the release-notes verdict. Supersedes #20200.

(cherry picked from commit 92a3700)

* Adopt ordered multi-caret markers in FCS tests (no behaviour change) (#20082)

* Clean up goto-def tests: drop unused open System and redundant caret-anchor comments

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 9d20cd22-45dc-411f-9163-185fd6dd54d9

* Collapse copy-pasted completion sources with ordered {caretN} markers

Uses SourceContext.extractOrderedMarkedSources (multi-caret) to replace whole-source
[<Theory>] copies with one marked source in DotOff.ArraySliceNotation (3 copies) and
CurriedArguments.Regression (5 copies). No behaviour change.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 9d20cd22-45dc-411f-9163-185fd6dd54d9

* Collapse copy-pasted completion sources in PatternMatching and Generics

Bug312557_2 (4 source copies -> 1 {caretN}) and Bug69673_1.CtrlSpaceForThis
(2-row Theory -> 1). No behaviour change.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 9d20cd22-45dc-411f-9163-185fd6dd54d9

* Collapse Symbols 'Nested copy-and-update' 8 copied sources into one {caretN}

The 8 Facts shared an identical source copied per caret; now one {caret1..8}
source + a (field-name, range) cases list. No behaviour change.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 9d20cd22-45dc-411f-9163-185fd6dd54d9

* BreakpointLocation: mark expected ranges with {selstart}/{selend} in source

Replaces magic ((line,col),(line,col)) tuples with {selstart}/{selend} markers
around the breakpoint span; the validation caret is inferred from {selend}
(SourceContext), so no {caret} needed. Compares against context.SelectedRange.
No behaviour change (all 6 tests green).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 9d20cd22-45dc-411f-9163-185fd6dd54d9

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 9d20cd22-45dc-411f-9163-185fd6dd54d9
(cherry picked from commit 2038269)

* Record spreads: off-by-default shadowing warnings (#20206)

(cherry picked from commit 05af9c8)

* Remove always-on PrintfBinaryFormat language feature flag (#20202)

* Add RED tests for unconditional %B and rejected --disableLanguageFeature:PrintfBinaryFormat

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Remove always-on PrintfBinaryFormat language feature flag

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Correct langversion comment in disableLanguageFeature %B guard test

Fixes an inaccurate comment (claimed minimum accepted --langversion is 8.0)
flagged during expert review. %B is now unconditional across all langversions
after removing the PrintfBinaryFormat feature flag. Comment-only change.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Make %B guard test accurate: assert compile+run at supported langversion

The expert-review finding noted the %B guard test compiled under the default
(latest) langversion where %B was already accepted, giving no regression value
for the removed 6.0 gate. A sub-6.0 test is infeasible: the minimum supported
langversion is 8.0 (lower versions error with FS3880), already above the old
6.0 gate, so removing the PrintfBinaryFormat flag is a pure no-op for every
supported langversion. The guard test now clearly asserts the %B code path is
intact (compiles and runs, producing 10011), with the 3881 rejection test
covering removal of the feature name. Comment documents why no sub-6.0 test
exists.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Add release notes for PrintfBinaryFormat flag removal

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Drop release notes and added guard tests: this is cleanup-only

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
(cherry picked from commit 5fa2950)

* Stabilize preview language features into F# 11.0 (#20199)

* Stabilize preview language features into F# 11.0

Move MethodOverloadsCache, ErrorOnMissingSignatureAttribute,
DirectDelegateConstruction, AccessProtectedBaseFieldFromClosure and
RecordSpreads from previewVersion to languageVersion110. FromEndSlicing
stays in preview in its own block. Also relocate the misplaced
ImplicitDIMCoverage into the F# 11.0 block and move the corresponding
release notes from .Language/preview.md into .Language/11.0.md.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* Fill PR number in release note

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
(cherry picked from commit 84eefce)

* Implement `<inheritdoc>` XML documentation support for F# (#19188)

(cherry picked from commit 93659f3)

* Add support for `<include>` XML documentation tag (#19186)

* Add support for <include> XML documentation tag (#19186)

Implement support for expanding <include file="..." path="..."/> elements
in XML doc comments when generating documentation files via --doc.

(cherry picked from commit 5260b68)

* Fix false prompt-injection alarm on PR Tooling Safety Check bypass labels (#20130)

* Fix false prompt-injection alarm on PR Tooling Safety Check bypass labels

The threat-detection job is a separate LLM that only sees the workflow
description plus the agent's output, not the process steps. When the agent
correctly applies AI-Tooling-Check-Bypassed to a non-fork PR, the detector
misreads the bypass label as the agent being manipulated into skipping its
scan and raises a false prompt-injection alarm, aborting the run's label and
memory outputs. Give the detector context via threat-detection.prompt.

(cherry picked from commit 2c245dd)

* feat(Async): RunSynchronouslyImmediate (#19804)

(cherry picked from commit 9357a4f)

* Add ECMA-335 EnC metadata delta writer (#20019)

* Add ECMA-335 EnC metadata delta writer

Adds an internal, standalone ECMA-335 Edit-and-Continue metadata delta writer to
AbstractIL: delta #- table stream and heap construction (DeltaMetadataTables,
DeltaMetadataSerializer, DeltaTableLayout, DeltaIndexSizing), ECMA-335 II.24.2.6
coded-index encoding (DeltaMetadataEncoding), EncLog/EncMap emission, generation GUID
chaining, user-string and standalone-signature token calculators (IlxDeltaStreams),
and the coordinating writer (FSharpDeltaMetadataWriter) over a plain row-description
input model (DeltaMetadataTypes, ILDeltaHandles, ILMetadataHeaps).

The writer's inputs are row records (names, tokens, signatures, RVAs) plus heap
offsets; it has no dependency on any semantic diffing or session machinery. It
compiles with no in-tree consumer by design: the consumer is the F# hot reload work
in #19941, following the same upstreaming pattern as #20017 and #20018
(land isolated, test-covered infrastructure first, wire the feature in a later PR).

One line of ilwrite.fsi is touched to expose the pre-existing markerForUnicodeBytes
so the delta writer reuses the exact string-marker logic of the full writer. No
behavior change for any existing code path.

Tests (130): coded-index encodings asserted against the production definitions and
ECMA-335 II.24.2.6 order, System.Reflection.Metadata reader parity over emitted
deltas, EncLog/EncMap correctness, stream layout, heap and index sizing,
multi-generation heap-offset chaining asserted against computed expected values,
standalone-signature rows asserted at baseline+1 from a real seeded baseline, and
serializer failure paths.

(cherry picked from commit e9fdc37)

* Remove always-on StructActivePattern language feature flag (#20208)

(cherry picked from commit 2def18d)

* Remove always-on OpenTypeDeclaration language feature flag (#20209)

(cherry picked from commit e1ad05c)

* Allow closing '>' of multiline nested type arguments to align with the opener (#15171) (#20003)

(cherry picked from commit 34a3053)

* Record constructors (FS-1073) (#19974)

(cherry picked from commit 7e2cbf1)

* docs: update state-machine.md for changed workflow SHAs (#20234)

Incremental update:
- Fix LPSS safe-output label count (10 → 11)
- Update source-shas footer

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
(cherry picked from commit 4a929f3)

* Localized file check-in by OneLocBuild Task: Build definition ID 499: Build ID 3039021 (#20108)

* Localized file check-in by OneLocBuild Task: Build definition ID 499: Build ID 3036509

* Localized file check-in by OneLocBuild Task: Build definition ID 499: Build ID 3036509

* Localized file check-in by OneLocBuild Task: Build definition ID 499: Build ID 3038596

* Localized file check-in by OneLocBuild Task: Build definition ID 499: Build ID 3038596

* Localized file check-in by OneLocBuild Task: Build definition ID 499: Build ID 3038733

* Localized file check-in by OneLocBuild Task: Build definition ID 499: Build ID 3038733

---------

Co-authored-by: Copilot <copilot@github.com>
(cherry picked from commit ab2e066)

* Enable MSBuild node reuse defaults in eng/Build.ps1 and eng/build.sh (#20220)

Both entry points hard-disabled MSBuild node reuse for every build,
overriding Arcade's default (node reuse on locally, off on CI). The
overrides were added together in March 2019 (7a6448e, f4bd221)
as a workaround for FSharp.Build.dll version conflicts when the proto
compiler and the freshly built one are loaded into reused MSBuild nodes
during F#'s self-bootstrap.

This removes both overrides so Arcade's default applies, restoring node
reuse for the inner loop and unblocking MSBuild Server. Whether the 2019
FSharp.Build.dll conflict still reproduces is validated by full CI,
including the Windows EndToEndBuildTests job, which builds without -ci
and therefore now exercises node reuse end to end.

Fixes #20217

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
(cherry picked from commit 80b7e4e)

* Fix spurious FS0410 for tuple patternInput bindings (#4161) (#19947)

Skip accessibility checks on compiler-generated patternInput module bindings.
These temps are module-init scaffolding whose visibility does not reflect the
enclosing let-binding scope, which caused false FS0410 errors when tuple
deconstruction referenced private types in the same module.

Fixes #4161

(cherry picked from commit 853c500)

* Ignore lines starting with `#:` (RFC-1337) (#20212)

(cherry picked from commit 8111bff)

* feat(Async+Task+ValueTask): consistent helper modules (#19844)

(cherry picked from commit 4890294)

* Add hot reload baseline reading and recorded EnC state (#20026)

* Add Roslyn-format EnC CustomDebugInformation codec and portable PDB method CDI emission

Adds an internal AbstractIL module implementing, byte for byte, the three Portable PDB
CustomDebugInformation blob formats Roslyn persists per method for Edit and Continue
(EnC Local Slot Map, EnC Lambda and Closure Map, EnC State Machine State Map), with
serializers, deserializers, a portable PDB read-back helper, and an occurrence-key
packing helper for deterministic syntax-offset slots.

Plumbs an optional methodCustomDebugInfoRows side channel through the IL binary writer
options into the portable PDB generator so a compilation can attach CDI rows to named
methods. Names that do not identify exactly one method row are dropped. All existing
writer call sites pass an empty map, so emitted PDBs are byte-identical to before.

No in-tree caller populates the map yet; the consumer is the F# hot reload work in
#19941, following the same pattern as #20017 (land isolated, test-covered
infrastructure first, wire the feature later).

Tests: blob round-trips, Roslyn golden-byte encodings, cross-validation against
CDI blobs emitted by a real Roslyn compilation, fail-closed occurrence-key packing
(including an int32-overflow regression where a wrapped negative key previously
escaped the bound check), and end-to-end synthetic PDB emission proving correct
MethodDef parenting, zero rows for an empty map, and no rows for absent or
ambiguous names.

(cherry picked from commit 51dd37e)

* Optimizer: propagate NoDynamicInvocation (#20242)

(cherry picked from commit 23fa8d5)

* Support OverloadResolutionPriority and a most-concrete overload tiebreaker

Two opt-in (`--langversion:preview`) additions to method overload resolution.

- F# now honors `System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute` (.NET 9): among the applicable candidates the highest-priority ones are considered first, matching C#. Priority is applied before betterness and scoped by the declaring type, so a library can steer F# callers to the overload it intends.

- When overload resolution would otherwise be ambiguous, the more concrete candidate is now preferred — `Option<'T>` over `'T`, `Task<'T>` over `'T`, `Result<'T, string>` over `Result<'T, 'E>` — across constructors, static and instance members, extension members, and optional/ParamArray tails. Candidates that are incomparable (more concrete at different positions, or differing only by an SRTP constraint) stay ambiguous rather than being resolved arbitrarily.

The tiebreaker is transparent: opt into `--warnon:3575` to see which concrete overload was selected and `--warnon:3576` for each generic overload it bypassed.

RFC FS-1338 (OverloadResolutionPriority) and RFC FS-1340 (most-concrete tiebreaker).

(cherry picked from commit 089835d)

* Remove always-on UseBindingValueDiscard language feature flag (#20162) (#20218)

Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
(cherry picked from commit c2b38d7)

* Remove always-on WildCardInForLoop language feature flag (#20221)

* Remove always-on WildCardInForLoop language feature flag

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Add release note for WildCardInForLoop flag removal

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Strip release-notes changes (NO_RELEASE_NOTES)

---------

Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
(cherry picked from commit afe45bf)

* Remove always-on AndBang language feature flag (#20223)

* Collapse always-on AndBang enforcement gates in CheckComputationExpressions

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Remove always-on AndBang language feature flag and regenerate xlf

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix Fantomas formatting after AndBang gate removal

The CheckCodeFormatting CI job failed because the collapsed AndBang gate
left a two-line let binding that Fantomas collapses to a single line.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
(cherry picked from commit 4990959)

* Remove always-on ImplicitYield language feature flag (#20230)

* Collapse ImplicitYield enforcement sites to always-enabled path

Remove the dead 'SupportsFeature LanguageFeature.ImplicitYield' guards, whose condition is always true for langversion >= 8.0, across the four Checking/Expressions files. Each guard is collapsed to its enabled branch and the dead branch/binding deleted. Behaviour is unchanged; the flag definition in LanguageFeatures.fs is intentionally left for a later sprint.

The now-unused 'cenv' parameter of YieldFree is renamed to '_cenv' (not removed): FSharp.Profiles.props enables --warnon:1182 with WarningsAsErrors, so an unused named binding is a hard build error, and removing the parameter would cascade through IsSimpleSemicolonSequenceElement, TryGetSimpleSemicolonSequenceOfComprehension and the SimpleSemicolonSequence active pattern (all out of scope). The '_' prefix preserves the exact signature and all positional call sites.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Remove ImplicitYield language feature flag definition

The ImplicitYield feature shipped in F# 4.7 and is permanently enabled
(minimum accepted --langversion is 8.0), making the flag dead configuration.
Remove the LanguageFeature.ImplicitYield union case, its features-map entry,
the GetFeatureString arm, the featureImplicitYield FSComp.txt string, and
regenerate the xlf localisation files. The always-on
SynExpr.SequentialOrImplicitYield AST machinery is untouched.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
(cherry picked from commit 9f19d64)

* Import: Don't walk non-F# assemblies when labelling trait constraint sources (#20090)

* Don't walk non-F# assemblies when labelling trait constraint sources

`addConstraintSources` (added in #16304, so that a failed member constraint
names the member it came from) is applied to every imported assembly, and
recurses through `e.ModuleOrNamespaceType` for every module and namespace
entity it finds.

For an assembly imported from IL there is nothing to find: the walk only
reads `AllValsAndMembers`, and `ImportILTypeDefs` gives every namespace and
type entity an empty val list; only an F# trait constraint produces a
`TyparConstraint.MayResolveMember` to label in the first place. Meanwhile the
recursion forces each namespace entity's `ModuleOrNamespaceType`, which
imports that namespace - so referencing an assembly ends up importing every
namespace in it, and reading every type definition, whether or not the code
touches it.

Skip the CCUs that aren't F#. FSharp.Core and F# references are still walked,
so the error messages are unchanged.

Measured with FSharpChecker.ParseAndCheckProject, keeping the results alive so
the imported assembly structures stay on the heap (averages of 3 runs, one per
process): a 486-reference F# project retains 1319.2 -> 952.1 MB (-27.8%), and
a 168-reference console project 77.7 -> 69.8 MB (-10.2%). Checking
FSharp.Compiler.Service itself (124 references, 397 sources) goes 2301.7 ->
2298.0 MB, i.e. within the noise at that size - what the imports cost there is
dwarfed by the trees of the project's own code.

(cherry picked from commit 623d80d)

* chore(Async.RunSynchronouslyImmediate): Stragglers re #19804 (#20245)

* chore(Async.RunSynchronouslyImmediate): Stragglers from #19804

- rename and sync clone impls withing VisualFSharp.slnx as per previous PR
- update RunImmediateExceptOnUI to delegate and follow naming

(cherry picked from commit 1852df8)

* Parser: recover on unfinished abstract members (#20070)

(cherry picked from commit ce9b62a)

* Remove always-on SingleUnderscorePattern language feature flag (#20222)

* Remove always-on SingleUnderscorePattern language feature flag

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Remove orphaned parsUnexpectedSymbolDot diagnostic resource

Its only two consumers were the parser guards removed when the always-on SingleUnderscorePattern language feature was deleted, leaving the FSComp.txt entry and 13 xlf trans-units unreachable. Regenerated xlf via UpdateXlf.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Tomas Grosup <tomas.grosup@microsoft.com>
(cherry picked from commit 22e1934)

* Add symbol and type highlighting to F# diagnostics (#20097)

(cherry picked from commit 208b7b4)

* Avoid per-instance lock object in InterruptibleLazy and DelayInitArrayMap (#20088)

* Avoid per-instance lock object in InterruptibleLazy and DelayInitArrayMap

Both types allocated a dedicated `syncObj = obj()` for their one-time
initialisation. These instances are internal and never locked externally,
and there are enough of them (one per lazy IL member, per ILTypeDefs /
ILMethodDefs, etc.) that the extra bare System.Object adds up to tens of MB
on a large project. Lock on `this` instead and drop the field.

Measured on a single-file FCS check against a project with ~486 references:
bare System.Object instances dropped from ~1,000,000 to ~29,000 (~-22 MB).

(cherry picked from commit 29718dd)

* Regression matrix: build unmodified repos against local FSharp.Core (#20240)

(cherry picked from commit ffa6ef1)

* IL: add ILPreNamespace, make ILPreTypeDef creation lazy (#20092)

* IL: add ILPreNamespace, make ILPreTypeDef creation lazy

(cherry picked from commit d3fab44)

* feat(Async): Add exception-unwrapping Await (#19785)

(cherry picked from commit 1e94cc5)

* IL: fix leaking binary view (#20250)

(cherry picked from commit 6ac056a)

* IL: use empty tables for members when possible (#20249)

(cherry picked from commit e09605c)

* Add regression test: #14454, IAsyncDisposable use in task CE in FSI (#20251)

(cherry picked from commit 5d6e4dc)

* Teach compiler driver to ignore unknown warning codes (#20246)

(cherry picked from commit 0640920)

* Remove always-on RelaxWhitespace language feature flag (#20226)

(cherry picked from commit d26c842)

* Add nullness regression test for recursive inline SRTP memoization (#20247)

(cherry picked from commit 492d417)

* Ship FSharp.Core with a net10.0 target framework (selective port of #20229)

Adapted from upstream PR #20229 for the 10.0.4xx servicing
packaging model. Ported: FSharpCoreShippedNetTargetFramework=net10.0 knob
(Directory.Build.props/.targets, eng/TargetFrameworks.props), FSharp.Core
fsproj/nuspec net10.0 asset shipment, FSharp.Core/*.fs(i) #if NETSTANDARD2_1
|| NET widening, FSharp.Compiler.fsproj dependent-project TFM wiring,
VerifyNetPackage.fsx package validation, ILVerify net10.0 baselines, and
FSharp.Core/FCS surface-area test updates.

Not ported: nothing was dropped from the upstream patch's actual net10.0
packaging content; the only upstream hunk excluded was an unrelated
Central-Package-Management NU1507 NoWarn block that only applies when
ManagePackageVersionsCentrally is set (introduced by the separately
excluded #20084 CPM migration, not by #20229 itself). No Arcade 11 or
.NET 11 SDK/TFM assumptions are introduced; the destination's existing
SDK pin, Arcade version, and servicing branding are preserved.

(cherry picked from commit 1928203, adapted)

* Flow release/10.0.4xx into rel/d18.11-ltsc for VS insertion (adapted from #20412)

Upstream PR #20412 ("Make release/dev18.11 flow into
rel/insiders") retargeted release/dev18.11's VS insertion branch from
rel/main to rel/insiders, and set FSharpReleaseBranchName from main to
release/dev18.11.

This branch (release/10.0.4xx) already has FSharpReleaseBranchName set
to its own servicing branch (release/10.0.4xx), so that part does not
apply here and is left untouched. The equivalent, required change for
this servicing branch is only the VS insertion target: route VS
insertion to rel/d18.11-ltsc instead of rel/insiders, matching the LTSC
servicing relationship between this F# branch and its VS branch. No
other branch, package, or servicing branding changed.

(cherry picked from commit ac4fecf, adapted)

* Fix ILVerify baseline filenames for net10.0 FSharp.Core asset (follow-up to #20229 adaptation)

The original upstream #20229 commit shipped the ILVerify baseline files
named with a literal 'netcoreapp' TFM segment
(ilverify_FSharp.Core_{Debug,Release}_netcoreapp.bsl), but ilverify.ps1
resolves the baseline filename dynamically from the
FSharpCoreShippedNetTargetFramework MSBuild property, which in this
destination resolves to 'net10.0' (not 'net11.0' as in later upstream
history, and not the literal string 'netcoreapp'). Renamed the two
empty (zero-error) baseline files to ilverify_FSharp.Core_{Debug,Release}_net10.0.bsl
so tests/ILVerify/ilverify.ps1 finds them. Verified directly via
'dotnet ilverify --sanity-checks --tokens' against the built Release
net10.0 FSharp.Core.dll: 'All Classes and Methods ... Verified.' with
zero errors, matching the (empty) baseline.

* Use the 43.13 FCS family for the 18.11 backport

Signal the upstream breaking tokenizer, tooltip and record AST contracts with a new FCS package family. Keep compiler and Core servicing versions unchanged. Add a red-green version guard and package-linked migration notes.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: ee897a29-d64c-4e85-954e-cded7192eea6

---------

Co-authored-by: Nat Elkins <nat@nelknet.com>
Co-authored-by: dotnet bot <dotnet-bot@dotnetfoundation.org>
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Eugene Auduchinok <eugene.auduchinok@jetbrains.com>
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: kerams <kerams@users.noreply.github.com>
Co-authored-by: Charles Roddie <charles.roddie@cantab.net>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Adam Boniecki <20281641+abonie@users.noreply.github.com>
Co-authored-by: Brian Rourke Boll <brianrourkeboll@users.noreply.github.com>
Co-authored-by: Charles Roddie <charles.roddie@summatic.co.uk>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Ruben Bartelink <ruben@bartelink.com>
Co-authored-by: Edgar Gonzalez <egonzalez@totallymoney.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Kirtikumar Anandrao Ramchandani <33368817+KirtiRamchandani@users.noreply.github.com>
Co-authored-by: Martin <29605222+Martin521@users.noreply.github.com>
Co-authored-by: Tomas Grosup <tomas.grosup@microsoft.com>
Copilot-Session: 9d20cd22-45dc-411f-9163-185fd6dd54d9
Copilot-Session: ee897a29-d64c-4e85-954e-cded7192eea6
Copilot-Session: 34374e7f-b8e0-4d82-89f8-d289e6cdd7f6

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

⚠️ Affects-Bootstrap Tooling check: PR touches compiler bootstrap chain ⚠️ Affects-Build-Infra Tooling check: PR touches build infrastructure ⚠️ Affects-Compiler-Output Tooling check: PR touches IL emission or codegen ⚠️ Affects-Test-Tooling Tooling check: PR touches test framework infrastructure

Projects

Status: New

Development

Successfully merging this pull request may close these issues.

5 participants