Skip to content

Make parallel and sequential code generation byte-identical#19929

Merged
T-Gro merged 192 commits into
mainfrom
fix/determinism-seq-vs-para
Jul 7, 2026
Merged

Make parallel and sequential code generation byte-identical#19929
T-Gro merged 192 commits into
mainfrom
fix/determinism-seq-vs-para

Conversation

@T-Gro

@T-Gro T-Gro commented Jun 10, 2026

Copy link
Copy Markdown
Member

Fixes #19928

--parallelcompilation+ now produces byte-identical output to --parallelcompilation-, so --deterministic Release builds are reproducible regardless of parallelism and FSharp.Compiler.Service.dll is restored to the determinism gate (now also checked sequential-vs-parallel).

Both modes run the same deferred per-file code-generation drain — differing only in Array.iter vs ArrayParallel.iter — with every type/member/field emit-order key and generated name derived from the file being emitted rather than thread-scheduling order.

T-Gro and others added 30 commits May 26, 2026 11:35
…19732)

Optimize/DetupleArgs.determineTransforms and Optimize/InnerLambdasToTopLevelFuncs.CreateNewValuesForTLR walked Val sets in Val.Stamp order. Stamps are race-assigned during parallel parse / type-check, so the contained NiceNameGenerator counter calls happen in different orders per build, producing names like `func1@1-30` vs `func1@1-20` for the same source.

Sort by (FileIndex, line, col, LogicalName) before name generation so the call sequence is stable regardless of stamp assignment race.

Also drops the stale OptimizeInputs.fs:514 comment - PR #19028 removed the deterministic-mode gate it described.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Address multi-model review consensus:
- Add Val.Stamp as final sort-key component to make the order total
  within a single compilation run (stamps are consistent per-process)
- Fix release note: Vals are created during type-check, not parse

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

- Extract valSourceOrderKey into TypedTreeOps.ExprConstruction (.fs + .fsi)
  and reuse from DetupleArgs / InnerLambdasToTopLevelFuncs, so the invariant
  lives in one place near valOrder.
- Trim the long block comments at the two sort sites to a single line that
  links the issue; the helper docstring carries the WHY.
- Restore a brief note in OptimizeInputs.fs above the parallel branch so
  future readers know which sort sites guard determinism.
- azure-pipelines-PR.yml: run eng/test-determinism.cmd in Release config.
  DetupleArgs and InnerLambdasToTopLevelFuncs only run when --optimize+ is
  on (set by SetOptimizeOn for Release), so the Debug job never exercised
  the race this PR fixes. Rename job to Determinism_Release.
- Release note: add PR link.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Revert Determinism CI job back to Debug: Release exposes pre-existing
  TypeDefsBuilder races unrelated to this fix, causing flaky failures.
  Release coverage belongs in a follow-up when all races are fixed.
- Add regression test exercising DetupleArgs + TLR with tuple-arg
  functions and nested lambdas across 8 files (#19732).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Reverting CI to Debug was a hack. The Release determinism job is meant
to fail when non-determinism slips into the compiler; that is exactly
its job. Pre-existing races (TypeDefsBuilder counter, ConcurrentStack
drain, NiceNameGenerator) must be fixed at source, not papered over.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The old code used global Interlocked counters as sort keys, so the emit
order of ILTypeDefs depended on whichever thread won the race during
parallel file gen. Combined with ConcurrentDictionary bucket order
(string GetHashCode is per-process randomized in .NET 6+), this produced
different IL byte sequences across builds and a non-deterministic MVID
for FSharp.Compiler.Service.dll in Release.

Fix: route AddTypeDef through a thread-local batch context. Sequential
adds go to batch 0 (legacy counter order, preserves existing baselines).
Each parallel file gets a deterministic batch index (file index in
delayedFileGenReverse, which is already in source order) with a per-batch
counter, so each file's types form a contiguous, source-ordered block.

All 1172 EmittedIL component tests still pass with no baseline updates;
the 2 unrelated failures (SequenceExpression handler, Thai culture
interpolation) are pre-existing on baseline.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Two additional Release-only determinism races:

1. AssemblyBuilder.GrabExtraBindingsToGenerate (IlxGen.fs):
   Anonymous-record augmentation bindings are pushed onto a ConcurrentStack
   from many parallel file-gen threads, so the drain order is racy. Sort
   the drained bindings by source position using valSourceOrderKey before
   feeding them into CodeGenMethod. The baseline shifts are exactly the
   reorder of anon-record .Equals/.CompareTo/.GetHashCode overloads.

2. ParseInputFilesInParallel (ParseAndCheckInputs.fs):
   FileIndex values are allocated lazily under a lock keyed by parse-time
   first-touch. With parallel parsing this assigns indices in a thread-
   interleaved order. Indices leak into IL via debug info, NiceNameGenerator
   keys ((basicName, FileIndex)), and any downstream sort using FileIndex.
   Pre-register indices in source-file order before kicking off the parallel
   parse so file 0 always gets the first index.

Baseline updates:
  EmittedIL/Misc/AnonRecd.fs.il.netcore.bsl
  EmittedIL/Nullness/AnonRecords.fs.il.netcore.bsl
Both are pure reorderings of overloaded compiler-generated members.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Differential testing (compile same project twice, once with
--parallelcompilation+ and once with --parallelcompilation- + --test:ParallelOff)
revealed that the order of methods within a class diverged between the two
modes for TLR-lifted helpers (e.g. nested 'composed@N' methods).

Root cause: in sequential mode (delayCodeGen = false), method bodies were
generated inline during the sequential file walk, so inner AddMethodDef
calls (for TLR helpers discovered during body codegen) interleaved with
outer ones in source order. In parallel mode (delayCodeGen = true), method
bodies were deferred and forced later, so inner AddMethodDef calls happened
AFTER the outer method def was already registered.

Two complementary fixes:

1. TypeDefBuilder: tag every AddMethodDef / AddFieldDef / AddEventDef
   with (batchIndex, intraIndex) and sort at Close time. Sequential phase
   uses batch 0 with a shared counter; each parallel file batch gets its
   own batchIndex via ParallelCodeGenContext. Adds are now lock-protected
   because multiple parallel batches can target the same TypeDef
   (StartupCode$, AnonymousType$, augmentation types).

2. Always set delayCodeGen = true in GenerateCode, regardless of
   parallelIlxGen. Parallel vs sequential only affects whether the
   deferred file batches are forced via ArrayParallel.iteri or
   Array.iteri. This normalizes AddMethodDef timing across modes.

Component test: 'Parallel and sequential compilation must produce identical
assemblies' (DeterministicTests.fs). 12 files exercising TLR + anon records.
Verified to fail without (2) and pass with it.

All 1172 EmittedIL component tests still pass with no baseline changes.

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

Addresses cross-model consensus from 21-agent adversarial review:

- valSourceOrderKey: document Val.Stamp tiebreaker hazard and pair every
  callsite with assertValSourceOrderKeyUnique (debug-only) so any future
  collision on the build-stable prefix (FileIndex, line, col, LogicalName)
  fires an assertion instead of silently reintroducing #19732.
- IlxGen TypeDefBuilder: extract tagInitial helper, deduplicate triplicated
  List.mapi tagging, rename NextIntra -> NextIntraBatchIndex, replace the
  two hand-rolled while loops in Append/PrependInstructionsToSpecificMethodDef
  with Seq.tryFindIndex, lock-protect gproperties for parity with
  gmethods/gfields/gevents, and lock the gmethods scans in those Append/
  Prepend members instead of relying on an implicit post-join invariant.
- azure-pipelines-PR.yml Determinism_Release: drop the duplicate
  experimental_features matrix leg (both legs set _experimental_flag: '',
  giving identical coverage at double the CI cost).
- DeterministicTests: switch to createTemporaryDirectory(), wrap test body
  in try/finally so artifacts survive on failure, drop sprintf+15-positional
  args in favour of $"""...""" interpolation matching the rest of the file,
  and eliminate the verbatim File1 duplicate by routing the primary source
  through the same fileSource helper.
- Release note: replace the overclaimed 'Release MVID reproducible' with a
  precise description of what the differential test and CI job actually prove.

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

Addresses round-1 cross-model review consensus:

- D8 (PR compactness): drop the lock on gproperties and the locks around
  the gmethods scans in Append/PrependInstructionsToSpecificMethodDef. Those
  members are called only from the main thread after the parallel codegen
  join in CodegenAssembly, so the locks were speculative defensive code
  (their own comment admitted as much). Add a one-line invariant note in
  place of the locks.
- D5 vs D8 tension: drop assertValSourceOrderKeyUnique entirely. Running
  the EmittedIL suite with the assertion promoted from Debug.Assert to
  failwith showed that synthetic Vals at the same source location DO
  legitimately collide on the build-stable prefix (e.g. e1/e2 generic
  compare-augmentation parameters at file 0, line 1, col 0). The collision
  is real but harmless in practice because those Vals are created together
  by a single pass and therefore receive monotonic Stamp values within one
  process. Rely on the differential
  'Parallel and sequential compilation must produce identical assemblies'
  component test as the regression guard instead of an always-failing
  precondition that would block normal compilation.
- D8: trim TypeDefsBuilder.Close (9-line comment -> 3), trim delayCodeGen=true
  rationale (5 lines -> 3), trim the release-note bullet, drop the .fsi/.fs
  duplication on valSourceOrderKey.

All 1172 EmittedIL component tests, 21 DeterministicTests, and the local
/tmp/det-diff seq-vs-par differential all pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Build 1443688 surfaced three deterministic-IL-related failures that the
previous netcore-only baseline updates did not cover:

* WindowsCompressedMetadata_Desktop Batch1 - EmittedIL.RealInternalSignature.Misc.AnonRecd_fs
* WindowsCompressedMetadata_Desktop Batch2 - EmittedIL.NullnessMetadata 'Nullable attr for anon records'
* Build_And_Test_AOT_Windows (classic + compressed) - StaticLinkedFSharpCore trim size

The IlxGen emit-order stabilization changes anon-record method order
identically on .NET Framework and .NET, so mirror the netcore.bsl
reordering into the matching net472.bsl files (CompareTo(obj) before
CompareTo(typed); Equals(obj)/Equals(typed)/Equals(obj,comp)/Equals(typed,comp)
before GetHashCode()/GetHashCode(comp)). Bump the trimmed
StaticLinkedFSharpCore_Trimming_Test.dll expected size from 9168384 to
9177088 bytes to track the new deterministic emit.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The default 'same' mode (build twice with identical flags) only catches
non-determinism that happens to fire between two runs of the same code
path. The new 'seq-vs-par' mode builds the compiler once with
--parallelcompilation- --test:ParallelOff and once with --parallelcompilation+,
then MD5-compares all outputs. Any divergence between the two scheduling
modes is a deterministic 1-shot failure, converting the probabilistic test
of #19732 / PR #19810 into a regression gate without retries.

Threads an AdditionalFscCmdFlags MSBuild property through Run-Build that
flows into the existing OtherFlags wiring; the flag pair is empty in
'same' mode so behaviour is byte-identical to today.

Verified locally on macOS that the in-process equivalent of these flag
pairs produces (a) divergent MVIDs on pre-fix bdb847a and (b) identical
MVIDs on the current head, so the CI signal will fail before the fix lands
and pass after.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The race-detector leg keeps catching schedule-divergent non-determinism on
the same code path. The new seq-vs-par leg deterministically catches any
divergence between --parallelcompilation+ and --parallelcompilation- on the
full compiler self-build in one shot — converting the probabilistic
regression test of #19732 into a hard gate.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
These are local-only investigation harness files from a subagent's working
directory; they should not be in the repo. Adds .scratch/ to .gitignore.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The local 12-file harness shows seq == par with the full PR applied, but
the empirical experiment at full compiler scale (build 1443778, log 268)
revealed that FSharp.Compiler.Service.dll and FSharp.Core.dll still differ
between sequential and parallel compilation at the whole-self-build scale.

There are evidently additional non-determinism sources that only surface at
the ~700-file compiler-self-build size which this PR has not yet identified
and fixed. Rather than block PR merge on a stronger invariant that isn't
fully achieved, mark the new leg as informational (continueOnError: true)
so it provides data without gating. The original race-detector leg
(build-twice-identical) PASSES and is the actual #19732 contract.

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

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

Re-applies the reverted IlxGen emit-order determinism (TypeDefsBuilder/
TypeDefBuilder batch context, extra-binding sort by valSourceOrderKey,
always-delayed codegen) and adds a per-file code-generation naming scope.

The residual non-determinism after restoring emit order was the '-N'
disambiguation suffix on compiler-generated method names (e.g. func1@1-N,
f@284-N from inlined FSharp.Core operators). These flow through
StableNiceNameGenerator during parallel code generation, whose inner
counter was bucketed by m.FileIndex - the inlined *source* location, which
is shared across all files - so parallel file batches raced on one counter.

CodegenNamingScope is a thread-local set by IlxGen around each file's code
generation; StableNiceNameGenerator now buckets its uniqueness counter by
the emitting file rather than by the inlined source location. This mirrors
the optimizer's PerFileNamingScope (Option B) and makes two Release builds
of FSharp.Compiler.Service.dll byte-identical (verified 3x).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Re-applies the reverted IlxGen emit-order determinism (TypeDefsBuilder/
TypeDefBuilder batch context, extra-binding sort by valSourceOrderKey,
always-delayed codegen) and adds a per-file code-generation naming scope.

The residual non-determinism after the optimizer-level fix (PerFileNamingScope
for DetupleArgs and TLR) was in the code generation layer:
1. TypeDefBuilder: methods/fields/events added from parallel threads were
   not ordered deterministically. Now tagged with (batchIndex, intraIndex).
2. TypeDefsBuilder: ConcurrentDictionary iteration order is non-deterministic.
   Now sorted by (batchIndex, intraBatchIndex) at Close.
3. CodegenAssembly: parallel file batches raced on StableNiceNameGenerator
   counters bucketed by m.FileIndex (shared inlined source). CodegenNamingScope
   now buckets by the emitting file.
4. Extra bindings from ConcurrentStack: sorted by valSourceOrderKey.
5. delayCodeGen = true unconditionally so method add order is identical.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
T-Gro added a commit that referenced this pull request Jun 29, 2026
…odegen test

- C4 (abonie): replace AssemblyBuilder.GetCurrentFields():seq<_> with HasFields():bool;
  sole consumer is the cctor-force emptiness check. Drops field-collection exposure and a
  seq alloc; matches #19929.
- C2 (abonie): add ComponentTest for a double-backtick member whose compiled name contains
  '@' (warns FS1104, valid F#). Confirms it compiles/runs; method-sort '@' bucketing stays
  deterministic (total order), so harmless.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread eng/test-determinism.ps1 Outdated
Comment thread src/Compiler/CodeGen/IlxGen.fs
T-Gro and others added 5 commits July 1, 2026 15:59
Resolve conflicts from the stacked-squash of #19810 (fix-deterministic-strings)
plus new main work:
- IlxGen.fs: keep #19929's CodegenFileScope/OrderKey redesign + content-derived
  raw-data naming (supersedes #19810's counter approach); drop main's orphaned
  PrimeRawDataValueTypeCounter/primedRawTypeCounter.
- Thread #19991's importMap/amap through MakeTopLevelRepresentationDecisions
  (OptimizeInputs, InnerLambdasToTopLevelFuncs .fs/.fsi) and keep its
  protected-base-field guard in GenMethodForBinding.
- CompilerGlobalState.fs: drop duplicate FreshCompilerGeneratedNameInScope.
- Keep valSourceOrderKey sort form; keep seq-vs-par CI leg; merge
  DeterministicTests to Guid temp dir + 10 iterations + try/finally.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…S.dll determinism gate

The residual #19928 non-determinism was the '-N' disambiguation suffix on compiler-generated
closure type names: it was allocated from a NiceNameGenerator bucket keyed by the closure's own
range FileIndex. Inlined or synthetic-range closures (e.g. FileIndex 0) share one bucket that the
parallel per-file IlxGen drain increments in thread-scheduling order, so two builds could name the
same closure func@1-17 vs func@1-23.

Route closure naming through StableNiceNameGenerator.GetUniqueCompilerGeneratedNameInScope, bucketing
the suffix by CodegenFileScope.CurrentFileIdx (the file currently being emitted) instead. Each file's
drain runs under its own thread-static scope, so parallel drains touch disjoint buckets and produce
identical names to a sequential drain.

Also wrap the trailing residue drain (anon-record structural-equality augmentations) in a
CodegenFileScope.With past the last file index, so its emitted members keep a positive currentFileIdx
(OrderKey assert + no struct(0,_) hoisting).

Re-enable FSharp.Compiler.Service.dll in eng/test-determinism.ps1 (removed from skipList, restored as a
well-known binary). Verified: seq-vs-par harness now produces byte-identical FSharp.Compiler.Service.dll.

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

The trailing residue drain (anon-record structural-equality augmentations, forced static-init) now
runs in a CodegenFileScope, so its emitted members/types sort deterministically instead of via a
struct(0,_) key. IL is semantically identical (pure member/type reordering); verified byte-identical
seq-vs-par.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The determinism fix stabilizes FSharp.Compiler.Service.dll, so the statically-linked trimming size is
now pinnable. Pin real net9.0 trimmed sizes (SelfContained FSharp.Core 315392, StaticLinked 9178624;
FSharpMetadataResource 7612928 unchanged) - verified locally against the global.json-pinned SDK.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ed file-index locals

Behavior-preserving cleanup of the closure-name fix: extract the shared StableNiceNameGenerator cache
body (GetOrAddStableName) so the plain and scoped variants differ only in the allocator; add
NiceNameGenerator.FreshCompilerGeneratedNameOfBasicNameInScope to avoid recomputing the basic name;
replace the raw CodegenFileScope.CurrentFileIdx read with CurrentFileIdxOr(fallback) so the 0-sentinel
policy is centralized; name the lastImplFileIdx / residueFileIdx codegen scope indices. Verified: FSCS.dll
still byte-identical seq-vs-par.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@T-Gro T-Gro changed the title Determinism: SEQ=PAR byte-identical FSharp.Compiler.Service.dll (fixes #19928) Make parallel and sequential code generation byte-identical Jul 2, 2026
T-Gro and others added 6 commits July 2, 2026 15:03
Main advanced 15 commits; only IlxGen.fs conflicted. #19548 landed an independent fix for the same
closure-name race (bucket the counter by the enclosing type's file via nameRange). Adopt it and drop
this branch's now-redundant CodegenFileScope-scoped name generator (GetUniqueCompilerGeneratedNameInScope /
CurrentFileIdxOr / GetOrAddStableName) - reverting CompilerGlobalState to main. The #19929 IlxGen emit-order
redesign, residue-drain scope, FSCS.dll gate re-enable, seq-vs-par CI leg and AOT pins are unchanged.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Benign method reordering from the newly-merged #19548/#19758 codegen changes combined with the
per-file IlxGen emit order; IL is semantically identical (pure permutation).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The closure-name determinism now comes from #19548's nameRange (bucket by the emitting file), not a
scoped name generator; update the skipList comment and release note to describe the emit-order
determinism this PR delivers without the dropped per-codegen-file-scope wording.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
AOT SelfContained/StaticLinked trimmed sizes differ ~4-5KB local-vs-CI (trimmer/runtime-pack skew); pin the
CI values (311296 / 9173504; FSharpMetadataResource 7612928 unchanged). Regenerate the Conformance
MethodResolution OptionalAndOutParameters baselines (missed earlier - the local regen filtered EmittedIL.*
only). Benign emit-order permutation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
GenericComparison.Equals10-18, Misc/AnonRecd, Nullness/AnonRecords .il.net472.bsl were stale on the Desktop
(net472) legs - their netcore variants were regenerated earlier but net472 was never run locally. Benign
anon-record/structural-equality emit-order permutations; net472 tests now pass 137/137.

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

abonie commented Jul 3, 2026

Copy link
Copy Markdown
Member

NiceNameGenerator.IncrementOnly (added in CompilerGlobalState.fs + .fsi) has zero callers — looks like a leftover from the earlier scoped-bucketing approach that the nameRange closure-naming rework superseded. Can it be removed?

T-Gro and others added 2 commits July 5, 2026 23:09
…sizeArray.Count

IncrementOnly lost its only callers when content-derived raw-data naming replaced the @T/@field counters. The TypeDefBuilder byref counters (methodIdx/fieldIdx/eventIdx) duplicated the ResizeArray.Count they tracked, so OrderKey now reads .Count directly; emit-order keys are byte-identical.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Brings in 7 main commits (through b53b04d). Release-notes union-merge verified: no duplicate headers/bullets, this PR's #19929 entry and main's entries all present. IlxGen auto-merge preserves the OrderKey/.Count cleanup.

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

T-Gro commented Jul 7, 2026

Copy link
Copy Markdown
Member Author

@abonie : ineed, clean now

@github-project-automation github-project-automation Bot moved this from New to In Progress in F# Compiler and Tooling Jul 7, 2026
@T-Gro T-Gro merged commit 5257863 into main Jul 7, 2026
50 checks passed
@T-Gro T-Gro deleted the fix/determinism-seq-vs-para branch July 7, 2026 12:21
@github-project-automation github-project-automation Bot moved this from In Progress to Done in F# Compiler and Tooling Jul 7, 2026
NatElkins added a commit to NatElkins/fsharp that referenced this pull request Jul 7, 2026
Resolve the dotnet#19929 IlxGen raw-data naming overlap by adopting the content/range-derived raw-data path while keeping hot reload generated-name replay wrappers. Update the hot reload guards and docs for the removed IncrementOnly path.

Verification:

- ./.dotnet/dotnet build FSharp.slnx -c Debug -v minimal

- DOTNET_MODIFIABLE_ASSEMBLIES=debug COMPlus_ForceEnc=1 ./.dotnet/dotnet test --project tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.Tests.fsproj -c Debug --no-build -- --filter-class '*HotReload*' (428 passed)

- DOTNET_MODIFIABLE_ASSEMBLIES=debug COMPlus_ForceEnc=1 ./.dotnet/dotnet test --project tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj -c Debug --no-build -- --filter-class '*HotReload*' (236 passed, 2 skipped)

- ./tests/scripts/check-ilxgen-name-path.sh
NatElkins added a commit to NatElkins/fsharp that referenced this pull request Jul 7, 2026
Bring the cumulative in-process hot reload branch to the reviewed post-dotnet#19929 compiler source and test state.

This removes duplicate extracted metadata writer copies, keeps the final module placement, carries the latest in-process compile performance fixes, and includes the HotReloadDemo project referenced by FSharp.slnx.

Verified: ./.dotnet/dotnet build FSharp.slnx -c Debug -v minimal passed; service HotReload tests 428 passed; component HotReload tests 236 passed, 2 skipped.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI-Tooling-Check-Bypassed Tooling check: non-fork PR, not diff-analyzed

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

Determinism: closure type names differ between --parallelcompilation- and --parallelcompilation+

2 participants