diff --git a/release-notes/11.0/preview/rc2/README.md b/release-notes/11.0/preview/rc2/README.md index f17bf30e79..e8051f51b8 100644 --- a/release-notes/11.0/preview/rc2/README.md +++ b/release-notes/11.0/preview/rc2/README.md @@ -11,7 +11,7 @@ Browse the .NET 11 RC 2 release notes by component: ## Languages - C# -- F# +- [F#](./fsharp.md) ## Workloads, Libraries, & More diff --git a/release-notes/11.0/preview/rc2/fsharp-memory-savings.md b/release-notes/11.0/preview/rc2/fsharp-memory-savings.md new file mode 100644 index 0000000000..e250c0956f --- /dev/null +++ b/release-notes/11.0/preview/rc2/fsharp-memory-savings.md @@ -0,0 +1,200 @@ +# Performance improvements in the F# 11 compiler + + +F# 11 brings **roughly 20-50% less allocation during compilation** across six real-world projects and **about 30% less memory retained after IDE project checks**. **And this is a win for your F# code, too!** Better [inlining of `FSharp.Core` calls](https://github.com/dotnet/fsharp/pull/20422) eliminates closure allocations in everyday functional code. Recompile with the new compiler and `FSharp.Core`, and these optimizations reach your applications and libraries, not just the compiler. + +Behind these gains are **49 performance and supporting PRs**, merged between **Aug 12 and Sep 21, 2026**. **12 are already in RC1**; the remaining **37 are in the RC2 source**, headed for .NET 11 and F# 11 GA. + + +These are author-provided measurements of a source-built compiler, not an official .NET 11 RC2 SDK benchmark. +The **New** payload uses [F# source commit `9cd6167a7`](https://github.com/dotnet/fsharp/commit/9cd6167a7265ce7264b22719503b7dfa9eb8f83c), the production source used by the pinned RC2 build. +The SDK comparison also changes runtime and packaging: Old runs on .NET 10, while RC1 and New run on .NET 11 RC1. +The .NET Framework comparison is separate and keeps the runtime family fixed. + +## Less allocation during compilation + +**Old:** SDK 10.0.100. **RC1:** SDK 11 RC1. **New:** optimized, source-built F# 11 from the RC2/main snapshot. Old runs on .NET 10; RC1 and new run on .NET 11 RC1. + + +| Compilation workload | Old GB | RC1 GB | New GB | RC1 vs old: less allocation | New vs old: less allocation | +| --- | ---: | ---: | ---: | ---: | ---: | +| FSharp.Core | 8.127 | 8.064 | 5.995 | 0.8% | 26.2% | +| FSharp.Compiler.Service | 37.799 | 34.946 | 29.300 | 7.5% | 22.5% | +| FsToolkit.ErrorHandling | 2.378 | 2.100 | 1.847 | 11.7% | 22.3% | +| Oxpecker | 1.230 | 0.759 | 0.634 | 38.3% | 48.5% | +| Nu | 15.853 | 14.871 | 12.276 | 6.2% | 22.6% | +| FsAutoComplete | 6.258 | 5.676 | 4.879 | 9.3% | 22.0% | + + +![Actual compiler allocation in GB for six workloads: the released SDK 10 compiler, RC1, and source-built F# 11. Each project's bar widths use their own zero-based scale.](media/fsharp-memory-savings-compiler-allocation.svg) + + +Peak compilation RAM rose in 4 of six SDK workloads, and FCS used more CPU time. + + +## The allocation gains also reach .NET Framework + +FCS-hosted compilation on **x64 .NET Framework 4.8.1**, comparing released and source-built compiler packages. + + +| Compilation workload | Old GB | New GB | New vs old: less allocation | +| --- | ---: | ---: | ---: | +| FSharp.Core | 8.305 | 6.160 | 25.8% | +| FSharp.Compiler.Service | 40.120 | 30.246 | 24.6% | +| FsToolkit.ErrorHandling | 2.414 | 1.889 | 21.7% | +| Oxpecker | 1.240 | 0.636 | 48.7% | +| Nu | 16.663 | 12.858 | 22.8% | +| FsAutoComplete | 6.429 | 5.002 | 22.2% | + + +![Actual allocation in GB on .NET Framework, comparing the released compiler generation with source-built F# 11 for all six workloads.](media/fsharp-memory-savings-runtime-allocation.svg) + +FCS peak RAM also increased on Framework. + +## Less memory held after IDE project checks + +FCS now [shares imported assemblies](https://github.com/dotnet/fsharp/pull/20296) across projects instead of keeping a separate copy for each one. + + +| Project graph | Old MB | RC1 MB | New MB | New vs old: less retained memory | +| --- | ---: | ---: | ---: | ---: | +| FsAutoComplete | 571.1 | 488.7 | 398.6 | 30.2% | +| Oxpecker | 113.2 | 88.5 | 78.5 | 30.6% | + + +*Managed memory retained after typechecking the project graphs.* + +![Retained managed heap in MB after a full GC: released SDK 10, RC1 and source-built F# 11. Both project graphs retain less memory. All bars share a zero-based MB axis.](media/fsharp-memory-savings-ide-retained-heap.svg) + + +On .NET Framework, retained memory falls by **30.1%-30.6%** too. + + +Editors and language servers pick up these gains by updating FCS. + +## Fewer closure allocations in your F# code + +A **closure** combines a function with values from surrounding code, such as `discount`, which the compiler used to store in a new object for this fold. +[Inlining](https://github.com/dotnet/fsharp/pull/20422) replaces a function call with its body, and `[]` tells the compiler to inline known lambda arguments too. + +```fsharp +let discountedTotal discount prices = + prices |> List.fold (fun total price -> total + price * (100 - discount) / 100) 0 +``` + +For operations such as `List.fold` that otherwise allocate nothing, the closure can be their entire allocation cost. +`List.map` and other collection builders allocate a new collection anyway, so removing a closure usually saves a smaller share. + + + +| F# code (simplified) | Old B/op | New B/op | +| --- | ---: | ---: | +| `List.fold (fun s struct (p, q) -> s + p * q * (100 - d) / 100) 0 items` | 24 | 0 | +| `let any = List.exists (fun x -> x > lo) xs`
`List.forall (fun x -> x < hi) xs && any` | 48 | 0 | +| `Array.fold (fun s x -> s + x * k) 0 xs + Array.fold2 (fun s x w -> s + x * w * k) 0 xs ws` | 48 | 0 | +| `Option.defaultValue 0 (Option.map (addOffset k) opt)` | 0 | 0 | +| `List.fold (fun s xs -> s + List.fold (fun t x -> t + x * k) 0 xs) 0 groups` | 48 | 0 | +| `List.sum (List.map (fun x -> x + k) (List.filter (fun x -> x > lo) xs))` | 146 | 146 | +| `saved <- fun x -> x + offset`
`saved n` | 24 | 24 | +| `List.fold (+) seed xs` | 0 | 0 | + + + +*List and array operations use four-element collections.* + +### Which functions benefit? + +`Option` and `ValueOption` (for `voption`) already inline lambda arguments in functions such as `map`, `bind`, and `fold`; this is not new in F# 11. + + +These existing functions gained explicit lambda inlining in [#20422](https://github.com/dotnet/fsharp/pull/20422) and the earlier [`Array.init` change](https://github.com/dotnet/fsharp/pull/19869): + +| Module | Functions gaining explicit lambda inlining | +| --- | --- | +| `List` (14) | `exists`, `exists2`, `find`, `findIndex`, `fold`, `fold2`, `forall`, `forall2`, `iter2`, `iteri2`, `pick`, `reduce`, `skipWhile`, `tryPick` | +| `Array` (19) | `exists2`, `find`, `findBack`, `findIndex`, `findIndexBack`, `fold`, `fold2`, `foldBack`, `foldBack2`, `forall`, `forall2`, `init`, `iter2`, `iteri`, `iteri2`, `pick`, `reduce`, `reduceBack`, `tryPick` | + + +### Partially applied functions, too + +Previously, `InlineIfLambda` handled explicit lambdas, but some partial applications still left a closure behind. + +```fsharp +type Settings(offset: int) = + member _.Offset = offset + +let add offset x = x + offset + +let adjust (settings: Settings) value = + value |> Option.map (add settings.Offset) +``` + +`add settings.Offset` supplies only the first argument and captures the current offset inside `adjust`, leaving a function waiting for `x`. +The [new compiler](https://github.com/dotnet/fsharp/pull/20487) handles this partial application like a lambda and no longer generates the extra closure object. + +Recompile with the new compiler and FSharp.Core to bring these improvements to your own code. + +## Contributing changes + + +### Already in RC1 + +- **Aug 12** - [#20090](https://github.com/dotnet/fsharp/pull/20090): Import: Don't walk non-F# assemblies when labelling trait constraint sources. +- **Aug 12** - [#20088](https://github.com/dotnet/fsharp/pull/20088): Avoid per-instance lock object in InterruptibleLazy and DelayInitArrayMap. +- **Aug 12** - [#20092](https://github.com/dotnet/fsharp/pull/20092): IL: add ILPreNamespace, make ILPreTypeDef creation lazy. +- **Aug 13** - [#20250](https://github.com/dotnet/fsharp/pull/20250): IL: fix leaking binary view. +- **Aug 13** - [#20249](https://github.com/dotnet/fsharp/pull/20249): IL: use empty tables for members when possible. +- **Aug 19** - [#20254](https://github.com/dotnet/fsharp/pull/20254): IL: share ILCallingConv instances. +- **Aug 19** - [#20256](https://github.com/dotnet/fsharp/pull/20256): IL: cache C# extension methods per CCU. +- **Aug 19** - [#20244](https://github.com/dotnet/fsharp/pull/20244): Fix super-linear compilation of guarded shared-or active-pattern matches. +- **Aug 20** - [#20286](https://github.com/dotnet/fsharp/pull/20286): Make Entity's adhoc members list lazy. +- **Aug 24** - [#20301](https://github.com/dotnet/fsharp/pull/20301): IL: share the pickled references. +- **Aug 24** - [#20298](https://github.com/dotnet/fsharp/pull/20298): Name resolution: group C#-style extension members per 'open' and extended type. +- **Aug 24** - [#20259](https://github.com/dotnet/fsharp/pull/20259): IL: cache the ILTypeRef of a type def. + +--- + +### In the RC2 source, headed for GA + +- **Aug 26** - [#20285](https://github.com/dotnet/fsharp/pull/20285): Calculate Entity.PublicPath instead of storing. +- **Aug 26** - [#20337](https://github.com/dotnet/fsharp/pull/20337): Replace the stringified pattern-match memo key with a typed one. +- **Aug 27** - [#20255](https://github.com/dotnet/fsharp/pull/20255): IL: reuse the cached ILTypeRef in ILTypeInfo.FromType. +- **Aug 27** - [#20364](https://github.com/dotnet/fsharp/pull/20364): Unpickling: share one EntityRef per non-local reference row. +- **Aug 27** - [#20296](https://github.com/dotnet/fsharp/pull/20296): Import: share assembly CCUs between projects. +- **Aug 28** - [#20348](https://github.com/dotnet/fsharp/pull/20348): Four profiler-guided hot-path wins from self-build tracing. +- **Aug 28** - [#20350](https://github.com/dotnet/fsharp/pull/20350): Avoid Choice allocation in fslib entity/val-ref equality. +- **Aug 28** - [#20351](https://github.com/dotnet/fsharp/pull/20351): Reduce Detuple usage-analysis allocation with a mutable Dictionary. +- **Aug 31** - [#20354](https://github.com/dotnet/fsharp/pull/20354): Avoid redundant FreeVars record allocation for local vals. +- **Aug 31** - [#20363](https://github.com/dotnet/fsharp/pull/20363): Fuse the optimizer inlining copy + type-instantiation passes. +- **Aug 31** - [#20367](https://github.com/dotnet/fsharp/pull/20367): Inline TryD to remove constraint-solver closure allocations. +- **Aug 31** - [#20368](https://github.com/dotnet/fsharp/pull/20368): eliminate per-call closure in StackGuard.Guard via InlineIfLambda. +- **Sep 1** - [#20287](https://github.com/dotnet/fsharp/pull/20287): IL: hold custom attributes in fields rather than a union case. +- **Sep 4** - [#20353](https://github.com/dotnet/fsharp/pull/20353): Cache IL method parameter attributes during overload resolution. +- **Sep 4** - [#20384](https://github.com/dotnet/fsharp/pull/20384): Avoid per-call closure allocation in type-hierarchy traversal. +- **Sep 8** - [#20385](https://github.com/dotnet/fsharp/pull/20385): Inline the free-variable typar foldBacks. +- **Sep 9** - [#20447](https://github.com/dotnet/fsharp/pull/20447): Remove per-call closure allocations in post-inference checks. +- **Sep 9** - [#20423](https://github.com/dotnet/fsharp/pull/20423): Eliminate closure allocations in well-known-attribute queries. +- **Sep 9** - [#20415](https://github.com/dotnet/fsharp/pull/20415): Make List.vMapFold inline to drop the nullness-import closure. +- **Sep 9** - [#20372](https://github.com/dotnet/fsharp/pull/20372): Eliminate closure allocations in List.mapq and List.lengthsEqAndForall2. +- **Sep 9** - [#20349](https://github.com/dotnet/fsharp/pull/20349): Reverse instead of sort already-ordered branch fixups in IL writer. +- **Sep 10** - [#20374](https://github.com/dotnet/fsharp/pull/20374): Inline the static-abstract interface-constraint predicate. +- **Sep 10** - [#20437](https://github.com/dotnet/fsharp/pull/20437): Drop the per-call closure in generic type-argument codegen. +- **Sep 10** - [#20421](https://github.com/dotnet/fsharp/pull/20421): Drop accessibility and attribute-scan closures via ListInline. +- **Sep 10** - [#20426](https://github.com/dotnet/fsharp/pull/20426): Avoid per-call FSharpFunc closure in remapVal member-info remap. +- **Sep 11** - [#20487](https://github.com/dotnet/fsharp/pull/20487): Eliminate per-call closure for InlineIfLambda partial applications. +- **Sep 16** - [#20422](https://github.com/dotnet/fsharp/pull/20422): Inline List/Array higher-order functions and adapt function arguments. +- **Sep 16** - [#20388](https://github.com/dotnet/fsharp/pull/20388): Share the empty-array singleton for zero-length Array results. +- **Sep 17** - [#20490](https://github.com/dotnet/fsharp/pull/20490): Name resolution: CheckIWSAM only needs the intrinsic methods. +- **Sep 17** - [#20489](https://github.com/dotnet/fsharp/pull/20489): IL: map the short-lived metadata-only PE reader. +- **Sep 17** - [#20486](https://github.com/dotnet/fsharp/pull/20486): IL: intern the attributes and type references read from metadata. +- **Sep 17** - [#20481](https://github.com/dotnet/fsharp/pull/20481): FCS: fix races that made the background builder repeat work. +- **Sep 17** - [#20494](https://github.com/dotnet/fsharp/pull/20494): Typed tree: create a type's augmentation on first use. +- **Sep 17** - [#20261](https://github.com/dotnet/fsharp/pull/20261): IL: fix the per-reader string cache sizing. +- **Sep 18** - [#20571](https://github.com/dotnet/fsharp/pull/20571): Stabilize OptimizeClosureIfNotInlined for F# 11. +- **Sep 18** - [#20555](https://github.com/dotnet/fsharp/pull/20555): Ship net10.0 FSharp.Core with the SDK tools. +- **Sep 21** - [#20506](https://github.com/dotnet/fsharp/pull/20506): Support multithreaded MSBuild in F# build tasks (concurrency benefit not measured here). + + +Special thanks to [Eugene](https://github.com/auduchinok) for his substantial work reducing allocations during cold compiles and IDE typechecks, and reducing the memory retained per project. + +We hope you enjoy the lower allocations, smaller IDE memory footprint, and more room for your own F# code. Keep enjoying F#! diff --git a/release-notes/11.0/preview/rc2/fsharp.md b/release-notes/11.0/preview/rc2/fsharp.md new file mode 100644 index 0000000000..59abf6a7a5 --- /dev/null +++ b/release-notes/11.0/preview/rc2/fsharp.md @@ -0,0 +1,264 @@ +# F# in .NET 11 RC 2 - Release Notes + +.NET 11 RC 2 includes the following F# updates: + +- [SRTP for extension members and operators](#srtp-for-extension-members-and-operators) +- [Runtime async support](#runtime-async-support) +- [reraise in computation expressions](#reraise-in-computation-expressions) +- [Require named arguments for selected APIs](#require-named-arguments-for-selected-apis) +- [Lower allocations in FSharp.Core collections](#lower-allocations-in-fsharpcore-collections) +- [Compiler memory usage improvements](#compiler-memory-usage-improvements) - [Read the full performance evaluation](./fsharp-memory-savings.md) +- [Bound concurrent asynchronous work](#bound-concurrent-asynchronous-work) +- [Trimming and Native AOT improvements](#trimming-and-native-aot-improvements) +- [Breaking changes from .NET 10](#breaking-changes-from-net-10) +- [Bug fixes and other improvements](#bug-fixes-and-other-improvements) +- [Community contributors](#community-contributors) + +Features marked **preview** require this setting in your project: + +```xml + + preview + +``` + +For F# Interactive, use `dotnet fsi --langversion:preview`. Other updates use the default F# 11 language version. + +## SRTP for extension members and operators + +> **Preview.** + +Bring existing .NET and third-party types into generic F# algorithms without changing those types or introducing wrappers. +Statically resolved type parameter (SRTP) constraints can select extension members, including operators, supplied by your code or a library. +An algorithm no longer needs every operation to be declared inside the original type. + +```fsharp +open System + +type MyOffset = { Hours: float } + +type System.DateTime with + static member (+) (date: DateTime, offset: MyOffset) = + date.AddHours(offset.Hours) + +let inline shift (date: DateTime) offset = date + offset + +let oneHourLater = shift DateTime.MinValue (TimeSpan.FromHours 1.0) +let twoHoursLater = shift DateTime.MinValue { Hours = 2.0 } +``` + +The same `shift` function accepts the built-in `TimeSpan` and a domain-specific offset added through an extension. +Inline overload resolution stays open until the call site instead of fixing the offset type to `TimeSpan` at the function definition. +Libraries can use this mechanism to adapt existing types to generic operations. + +Only public extensions can satisfy these constraints. +When using extensions from another assembly, open their defining module at the call site. +Built-in operations still take precedence when they already apply. +Ordinary extension-member calls do not require preview. + +This capability was already available in RC 1 +([dotnet/fsharp#19602](https://github.com/dotnet/fsharp/pull/19602)). +Thank you [@gusty](https://github.com/gusty) for this contribution! + +## Runtime async support + +> **Preview.** + +Library authors can build custom computation-expression builders on .NET's runtime-async execution model. +RC 2 supplies compiler-recognized return and await intrinsics for generating runtime-async methods +([dotnet/fsharp#20235](https://github.com/dotnet/fsharp/pull/20235)). +The runtime manages asynchronous suspension and continuation instead of requiring a compiler-generated await state machine. + +The [compiler's prototype builder](https://github.com/dotnet/fsharp/blob/9cd6167a7265ce7264b22719503b7dfa9eb8f83c/tests/FSharp.Compiler.ComponentTests/Language/RuntimeAsync/RuntimeTaskBuilder.fs) demonstrates familiar `let!`, `return`, loops, exception handling, and resource management on these mechanics: + +```fsharp +let calculation () = + runtimeTask { + let captured = 40 + let! delta = delayed 2 + return captured + delta + } +``` + +Here `runtimeTask` is the custom builder defined by the tests, not a shipped FSharp.Core symbol. +Its `Run` method passes the delayed body to `StateMachineHelpers.__runtimeAsyncReturn`. +Its `Source` methods use `AsyncHelpers.Await` to await tasks before handing their results to the continuation. + +This release gives library authors the building blocks to create their own computation-expression APIs. +FSharp.Core does not yet ship a builder using these mechanics. +The existing `task {}` and `async {}` builders are unchanged. +The example targets `net11.0` and runs on the .NET 11 RC 2 runtime. +The intrinsics come from FSharp.Core's `net10.0` asset, not its `netstandard2.0` asset; the target and executing runtime must support runtime-async. + +## reraise in computation expressions + +> **Preview.** + +`reraise ()` now works inside the `with` handler of an `async`, `task`, or custom computation expression. +It propagates the original exception and preserves its stack trace, unlike rethrowing with `raise e` +([dotnet/fsharp#20405](https://github.com/dotnet/fsharp/pull/20405)). + +```fsharp +let operation = async { + try + failwith "operation failed" + with _ -> + reraise () +} +``` + +## Require named arguments for selected APIs + +> **Preview.** + +API authors can apply `System.Diagnostics.CodeAnalysis.RequireNamedArgumentsAttribute` to methods and constructors. +With preview enabled, positional calls report `FS3923`, requiring callers to name the affected arguments +([dotnet/fsharp#20340](https://github.com/dotnet/fsharp/pull/20340)). + +The RC 2 BCL does not include the attribute. +Define it in your project or use a library that provides it before marking a member: + +```fsharp +open System.Diagnostics.CodeAnalysis + +type Calculator() = + [] + member _.Double(value: int) = value * 2 + +let answer = Calculator().Double(value = 21) // 42 +``` + +`Calculator().Double(21)` reports `FS3923` under preview. +The default language version does not enforce this attribute. + +## Lower allocations in FSharp.Core collections + +More `List` and `Array` traversal functions, including `fold`, `exists`, and `tryPick`, can now inline their callback lambdas. +This removes callback closures from common collection operations in your applications +([dotnet/fsharp#20422](https://github.com/dotnet/fsharp/pull/20422)). + +```fsharp +let sumWithOffset k xs = + List.fold (fun total value -> total + value + k) 0 xs + +let total = sumWithOffset 1 [ 1; 2; 3 ] // 9 +``` + +The author's capturing-lambda microbenchmarks report 24 bytes per call with FSharp.Core 9.0.100 versus zero with the updated Core. +This applies to the measured `List.fold`, `List.exists`, `Array.fold`, and `Array.fold2` calls, not every collection function. +`OptimizeClosureIfNotInlined` is also enabled by default in F# 11. +It lets library authors adapt non-literal callbacks outside an inlined loop +([dotnet/fsharp#20571](https://github.com/dotnet/fsharp/pull/20571)). + +## Compiler memory usage improvements + +Compilation and project analysis create fewer temporary objects and retain less duplicated assembly metadata. +RC 2 combines expression copying and type instantiation during inlining, removes constraint-solver closures, and reuses metadata objects +([dotnet/fsharp#20363](https://github.com/dotnet/fsharp/pull/20363), +[dotnet/fsharp#20367](https://github.com/dotnet/fsharp/pull/20367), +[dotnet/fsharp#20255](https://github.com/dotnet/fsharp/pull/20255), +[dotnet/fsharp#20489](https://github.com/dotnet/fsharp/pull/20489)). + +The PE-reader reuse PR's FSharp.Common build benchmark reports allocated bytes decreasing from 2,912 MB to 2,606 MB (10.5%). +These are cumulative allocations during compilation, separate from the application runtime allocations described above. + +FSharp.Compiler.Service also shares imported assembly data between projects by default and creates referenced-type metadata only when needed +([dotnet/fsharp#20296](https://github.com/dotnet/fsharp/pull/20296), +[dotnet/fsharp#20494](https://github.com/dotnet/fsharp/pull/20494)). +The sharing PR's 10-project ReSharper.FSharp benchmark reports retained memory decreasing from 1,547 MB to 1,002 MB (35.2%). +Tool authors can opt out with `shareImportedAssemblies = false` on `FSharpChecker.Create`. + +These measurements describe their specific workloads, not peak process memory or a universal build-time speedup. +The compiler improvements are enabled by default. +Thank you [@auduchinok](https://github.com/auduchinok) for this contribution! + +For the measured combined impact of F# 11 compiler and FSharp.Core improvements, see [the full performance evaluation](./fsharp-memory-savings.md). + +## Bound concurrent asynchronous work + +`Async.parallelLimit` runs asynchronous computations with a maximum number in flight and returns results in input order. +Use it to limit concurrent work in a batch without writing a separate concurrency limiter. +Related helpers include `Async.parallelDoLimit`, `Task.parallelLimit`, and sequential variants +([dotnet/fsharp#20294](https://github.com/dotnet/fsharp/pull/20294)). +The task helpers accept functions that start tasks, so the limit applies before the work starts. + +```fsharp +let results = + [ for i in 1..5 -> async { return i * i } ] + |> Async.parallelLimit 2 + |> Async.RunSynchronously +// [| 1; 4; 9; 16; 25 |] +``` + +Thank you [@bartelink](https://github.com/bartelink) for this contribution! + +## Trimming and Native AOT improvements + +`Array2D.create`, `init`, `rebase`, `map`, `mapi`, and `copy` no longer report `IL3050` when targeting .NET 11. +Non-zero-based `*Based` operations still have Native AOT restrictions, now reported at the call site +([dotnet/fsharp#20338](https://github.com/dotnet/fsharp/pull/20338)). + +```fsharp +let matrix = Array2D.init 2 3 (fun row column -> row * 10 + column) +let value = matrix[1, 2] // 12 +``` + +Compiled F# assemblies now embed their metadata-trimming rules, including when assemblies are linked +([dotnet/fsharp#20527](https://github.com/dotnet/fsharp/pull/20527)). + +## Breaking changes from .NET 10 + +With F# 11, `|||`, `&&&`, and `^^^` on enums with non-integer underlying types, such as `char`, now report `FS0001`. +These expressions previously compiled but threw `NotSupportedException` at runtime. +Use an integer-backed enum for bit flags +([dotnet/fsharp#20322](https://github.com/dotnet/fsharp/pull/20322)). +This check is enabled by default, not restricted to preview. +Thank you [@edgarfgp](https://github.com/edgarfgp) for this contribution! + + + +## Bug fixes and other improvements + +### Compiler + +- [Avoid stack overflow when checking long sequence expressions](https://github.com/dotnet/fsharp/pull/20480) +- [Check active-pattern let bindings like equivalent match expressions](https://github.com/dotnet/fsharp/pull/20383) +- [Resolve generic arguments in constraint-dependency order](https://github.com/dotnet/fsharp/pull/20342) +- [Fix final do! expressions being treated as return! or yield!](https://github.com/dotnet/fsharp/pull/20449) +- [Handle optional indexer setter arguments and caller information](https://github.com/dotnet/fsharp/pull/20570) +- [Avoid false nullness warnings for recursive union types](https://github.com/dotnet/fsharp/pull/20562) +- [Resolve recursive inline members in dependency order](https://github.com/dotnet/fsharp/pull/20111) + +### FSharp.Core + +- [Reuse empty arrays in collection operations and conversions](https://github.com/dotnet/fsharp/pull/20388) +- [Return correctly shaped empty slices for extreme reversed bounds](https://github.com/dotnet/fsharp/pull/20557) + +### Tooling and F# Interactive + +- [Avoid repeated background type checking during concurrent requests](https://github.com/dotnet/fsharp/pull/20481) +- [Preserve tuple-pattern parentheses in generated signatures](https://github.com/dotnet/fsharp/pull/20589) +- [Support IAsyncDisposable use bindings in F# Interactive task expressions](https://github.com/dotnet/fsharp/pull/20555) +- [Avoid unnecessary PDB generation with --multiemit+ --debug-](https://github.com/dotnet/fsharp/pull/20394) + +## Community contributors + +Thank you contributors! + +- [@auduchinok](https://github.com/dotnet/fsharp/pulls?q=is%3Apr+is%3Amerged+author%3Aauduchinok) +- [@bartelink](https://github.com/dotnet/fsharp/pulls?q=is%3Apr+is%3Amerged+author%3Abartelink) +- [@edgarfgp](https://github.com/dotnet/fsharp/pulls?q=is%3Apr+is%3Amerged+author%3Aedgarfgp) +- [@gusty](https://github.com/dotnet/fsharp/pulls?q=is%3Apr+is%3Amerged+author%3Agusty) +- [@Happypig375](https://github.com/dotnet/fsharp/pulls?q=is%3Apr+is%3Amerged+author%3AHappypig375) +- [@majocha](https://github.com/dotnet/fsharp/pulls?q=is%3Apr+is%3Amerged+author%3Amajocha) +- [@nojaf](https://github.com/dotnet/fsharp/pulls?q=is%3Apr+is%3Amerged+author%3Anojaf) +- [@xperiandri](https://github.com/dotnet/fsharp/pulls?q=is%3Apr+is%3Amerged+author%3Axperiandri) + +F# updates: + +- [F# release notes](https://fsharp.github.io/fsharp-compiler-docs/release-notes/About.html) +- [dotnet/fsharp repository](https://github.com/dotnet/fsharp) diff --git a/release-notes/11.0/preview/rc2/media/fsharp-memory-savings-compiler-allocation.svg b/release-notes/11.0/preview/rc2/media/fsharp-memory-savings-compiler-allocation.svg new file mode 100644 index 0000000000..fe88fbab5c --- /dev/null +++ b/release-notes/11.0/preview/rc2/media/fsharp-memory-savings-compiler-allocation.svg @@ -0,0 +1,81 @@ + +Less compiler allocation in every workloadLess compiler allocation in every workload. Actual GB; zero baseline. FSharp.Core: Old (SDK 10) 8.127 GB, SDK 11 RC1 8.064 GB, New (source) 5.995 GB. FSharp.Compiler.Service: Old (SDK 10) 37.799 GB, SDK 11 RC1 34.946 GB, New (source) 29.300 GB. FsToolkit.ErrorHandling: Old (SDK 10) 2.378 GB, SDK 11 RC1 2.100 GB, New (source) 1.847 GB. Oxpecker: Old (SDK 10) 1.230 GB, SDK 11 RC1 0.759 GB, New (source) 0.634 GB. Nu: Old (SDK 10) 15.853 GB, SDK 11 RC1 14.871 GB, New (source) 12.276 GB. FsAutoComplete: Old (SDK 10) 6.258 GB, SDK 11 RC1 5.676 GB, New (source) 4.879 GB. + + +Less compiler allocation in every workload +Actual allocation in GB. Bar widths are scaled separately for each project. +FSharp.Core +0 + +Old (SDK 10) + +8.127 GB +SDK 11 RC1 + +8.064 GB +New (source) + +5.995 GB +FSharp.Compiler.Service +0 + +Old (SDK 10) + +37.799 GB +SDK 11 RC1 + +34.946 GB +New (source) + +29.300 GB +FsToolkit.ErrorHandling +0 + +Old (SDK 10) + +2.378 GB +SDK 11 RC1 + +2.100 GB +New (source) + +1.847 GB +Oxpecker +0 + +Old (SDK 10) + +1.230 GB +SDK 11 RC1 + +0.759 GB +New (source) + +0.634 GB +Nu +0 + +Old (SDK 10) + +15.853 GB +SDK 11 RC1 + +14.871 GB +New (source) + +12.276 GB +FsAutoComplete +0 + +Old (SDK 10) + +6.258 GB +SDK 11 RC1 + +5.676 GB +New (source) + +4.879 GB +Compare versions within each project; different projects use different bar scales. Every scale starts at zero. +Old = released SDK 10 generation. New = source-built F# 11. Medians of 12 measured runs. + diff --git a/release-notes/11.0/preview/rc2/media/fsharp-memory-savings-ide-retained-heap.svg b/release-notes/11.0/preview/rc2/media/fsharp-memory-savings-ide-retained-heap.svg new file mode 100644 index 0000000000..f1ff13312f --- /dev/null +++ b/release-notes/11.0/preview/rc2/media/fsharp-memory-savings-ide-retained-heap.svg @@ -0,0 +1,43 @@ + +Less managed heap retained after checkingLess managed heap retained after checking. Actual MB; zero baseline. FsAutoComplete: Old (SDK 10) 571.1 MB, SDK 11 RC1 488.7 MB, New (source) 398.6 MB. Oxpecker: Old (SDK 10) 113.2 MB, SDK 11 RC1 88.5 MB, New (source) 78.5 MB. + + +Less managed heap retained after checking +Live managed heap after full GC, in MB. All projects share the same scale. + +0 + +100 + +200 + +300 + +400 + +500 + +600 +FsAutoComplete +Old (SDK 10) + +571.1 MB +SDK 11 RC1 + +488.7 MB +New (source) + +398.6 MB +Oxpecker +Old (SDK 10) + +113.2 MB +SDK 11 RC1 + +88.5 MB +New (source) + +78.5 MB +Retained managed heap (MB), measured while the checker, options and results remain alive. +Old = released SDK 10 generation. New = source-built F# 11. Medians of 12 measured runs. + diff --git a/release-notes/11.0/preview/rc2/media/fsharp-memory-savings-runtime-allocation.svg b/release-notes/11.0/preview/rc2/media/fsharp-memory-savings-runtime-allocation.svg new file mode 100644 index 0000000000..f4ce9bc02e --- /dev/null +++ b/release-notes/11.0/preview/rc2/media/fsharp-memory-savings-runtime-allocation.svg @@ -0,0 +1,63 @@ + +The allocation gains also reach .NET FrameworkThe allocation gains also reach .NET Framework. Actual GB; zero baseline. FSharp.Core: Old (released) 8.305 GB, New (source) 6.160 GB. FSharp.Compiler.Service: Old (released) 40.120 GB, New (source) 30.246 GB. FsToolkit.ErrorHandling: Old (released) 2.414 GB, New (source) 1.889 GB. Oxpecker: Old (released) 1.240 GB, New (source) 0.636 GB. Nu: Old (released) 16.663 GB, New (source) 12.858 GB. FsAutoComplete: Old (released) 6.429 GB, New (source) 5.002 GB. + + +The allocation gains also reach .NET Framework +Actual allocation in GB. Bar widths are scaled separately for each project. +FSharp.Core +0 + +Old (released) + +8.305 GB +New (source) + +6.160 GB +FSharp.Compiler.Service +0 + +Old (released) + +40.120 GB +New (source) + +30.246 GB +FsToolkit.ErrorHandling +0 + +Old (released) + +2.414 GB +New (source) + +1.889 GB +Oxpecker +0 + +Old (released) + +1.240 GB +New (source) + +0.636 GB +Nu +0 + +Old (released) + +16.663 GB +New (source) + +12.858 GB +FsAutoComplete +0 + +Old (released) + +6.429 GB +New (source) + +5.002 GB +Compare versions within each project; different projects use different bar scales. Every scale starts at zero. +Old = released SDK 10 generation. New = source-built F# 11. Medians of 12 measured runs. +