From 82f35db4b49e3d22b5d23306f64ca351328ea5a9 Mon Sep 17 00:00:00 2001 From: Eugene Auduchinok Date: Mon, 24 Aug 2026 19:12:23 +0200 Subject: [PATCH 1/3] IL: map the short-lived metadata-only PE reader This view exists only to parse the PE headers and copy the resources out, and is disposed immediately after, so reading the whole assembly into a managed byte array materialises megabytes to touch a few pages. Mapping it is safe because neither pectxtEager nor pevEager may be captured by the results. Co-Authored-By: Claude Opus 5 --- src/Compiler/AbstractIL/ilread.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler/AbstractIL/ilread.fs b/src/Compiler/AbstractIL/ilread.fs index d1728dc36d2..a9a052cb092 100644 --- a/src/Compiler/AbstractIL/ilread.fs +++ b/src/Compiler/AbstractIL/ilread.fs @@ -5168,7 +5168,7 @@ let OpenILModuleReader fileName opts = // For metadata-only, always use a temporary, short-lived PE file reader, preferably over a memory mapped file. // Then use the metadata blob as the long-lived memory resource. - let disposer, pefileEager = getBinaryFile fullPath false + let disposer, pefileEager = getBinaryFile fullPath true use _disposer = disposer let metadataPhysLoc, metadataSize, peinfo, pectxtEager, pevEager = From 94ee8635c02924c13b9f85ae8873dd3143312c65 Mon Sep 17 00:00:00 2001 From: Eugene Auduchinok Date: Tue, 8 Sep 2026 18:46:06 +0200 Subject: [PATCH 2/3] IO: dispose MemoryMappedStream through Stream.Dispose(bool) The cleanup lived in an explicit IDisposable implementation, which Stream.Dispose() never reaches, so getBinaryFile's disposer left the file mapped until the safe handles were finalized. Co-Authored-By: Claude Opus 5 --- src/Compiler/Utilities/FileSystem.fs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/Compiler/Utilities/FileSystem.fs b/src/Compiler/Utilities/FileSystem.fs index 5fd055b525d..b73650db326 100644 --- a/src/Compiler/Utilities/FileSystem.fs +++ b/src/Compiler/Utilities/FileSystem.fs @@ -199,13 +199,10 @@ type internal MemoryMappedStream(mmf: MemoryMappedFile, length: int64) = override x.Write(buffer, offset, count) = viewStream.Write(buffer, offset, count) override x.Read(buffer, offset, count) = viewStream.Read(buffer, offset, count) - override x.Finalize() = x.Dispose() - - interface IDisposable with - override x.Dispose() = - GC.SuppressFinalize x - mmf.Dispose() - viewStream.Dispose() + override _.Dispose disposing = + base.Dispose disposing + viewStream.Dispose() + mmf.Dispose() [] type RawByteMemory(addr: nativeptr, length: int, holder: obj) = From 57c504f6738e57d4ac9c1e6d00044d3911e76110 Mon Sep 17 00:00:00 2001 From: Eugene Auduchinok Date: Wed, 9 Sep 2026 22:48:02 +0200 Subject: [PATCH 3/3] Release notes Co-Authored-By: Claude Fable 5.1 --- docs/release-notes/.FSharp.Compiler.Service/11.0.100.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md b/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md index ed1e382d6c7..50ddd78a27d 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md +++ b/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md @@ -169,6 +169,7 @@ * Checker: recover on checking language version ([PR ##19970](https://github.com/dotnet/fsharp/pull/19970)) * Implied argument names for function-to-delegate coercions now fall back to the delegate's `Invoke` parameter names when the function has no recoverable names (e.g. a partial application like `System.Func((+) 1)`), instead of synthetic `delegateArg0`, `delegateArg1`, … names. ([PR #20001](https://github.com/dotnet/fsharp/pull/20001)) * Add internal `ResetCompilerGeneratedNameState` to `CompilerGlobalState` name generators so warm-checker re-compilation can produce fresh-process-identical generated names. ([PR #20017](https://github.com/dotnet/fsharp/pull/20017)) +* IL: map the short-lived metadata-only PE reader ([PR #20489](https://github.com/dotnet/fsharp/pull/20489)) * Add internal ECMA-335 Edit-and-Continue metadata delta writer to AbstractIL. ([PR #20019](https://github.com/dotnet/fsharp/pull/20019)) * Add Roslyn-format EnC CustomDebugInformation codec and portable PDB method CDI emission support to AbstractIL. ([PR #20018](https://github.com/dotnet/fsharp/pull/20018)) * Extension members (including operators) now participate in SRTP (statically-resolved type parameter) constraint resolution, so member constraints on `inline` code can be satisfied by extension members — including operators defined on types you don't own via type extensions. Intrinsic members keep priority over extension members, and extension members solve SRTP constraints without satisfying nominal static-abstract interface (IWSAM) constraints. Also adds the `[]` attribute for overloads that differ only by return type, and tuple type extensions (`type ('T1 * 'T2) with` / `type struct ('T1 * 'T2) with`). Requires `--langversion:preview`. ([RFC FS-1043](https://github.com/fsharp/fslang-design/blob/main/RFCs/FS-1043-extension-members-for-operators-and-srtp-constraints.md), [fslang-suggestions#230](https://github.com/fsharp/fslang-suggestions/issues/230), [PR #19602](https://github.com/dotnet/fsharp/pull/19602))