From 4b348b54c57e2e821fcbe79b214be0a725d2960a Mon Sep 17 00:00:00 2001 From: Eugene Auduchinok Date: Tue, 28 Jul 2026 17:04:56 +0200 Subject: [PATCH 1/2] 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. Co-Authored-By: Claude Opus 5 --- src/Compiler/Driver/CompilerImports.fs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Compiler/Driver/CompilerImports.fs b/src/Compiler/Driver/CompilerImports.fs index f0868919ad0..46a3b83b604 100644 --- a/src/Compiler/Driver/CompilerImports.fs +++ b/src/Compiler/Driver/CompilerImports.fs @@ -2341,6 +2341,12 @@ and [] TcImports let! ccuinfos = phase2s |> runMethod if importsBase.IsSome then + let addConstraintSources (ia: ImportedAssembly) = + // Only an F# assembly can carry a trait constraint to label. + // Prevent force-reading of the whole assembly namespace tree for other assemblies. + if ia.FSharpViewOfMetadata.IsFSharp then + addConstraintSources ia + importsBase.Value.CcuTable.Values |> Seq.iter addConstraintSources ccuTable.Values |> Seq.iter addConstraintSources From 0f5a9f3302a34a2243b6c0c019a59a9bbf5f55e1 Mon Sep 17 00:00:00 2001 From: Eugene Auduchinok Date: Mon, 10 Aug 2026 12:26:09 +0200 Subject: [PATCH 2/2] Release notes --- 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 ab65df29a6e..ac6ae840deb 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md +++ b/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md @@ -130,6 +130,7 @@ * Fix dot-completion after indexed expressions (`a.[0].Data.`, `a[0].Data.`, `[1;2].Length.`) returning unrelated global completions instead of expression-typings members. ([Issue #4966](https://github.com/dotnet/fsharp/issues/4966), [PR #19934](https://github.com/dotnet/fsharp/pull/19934)) * Quotations of `match s with "" -> _` no longer leak the `s <> null && s.Length = 0` lowering; the empty-string optimization moved from pattern-match compilation to the optimizer so quoted expressions keep `op_Equality(s, "")`. ([Issue #19873](https://github.com/dotnet/fsharp/issues/19873)) * Fix #5795: Allow attributes defined in a `module rec` / `namespace rec` scope to be used on union cases, record fields, and generic type parameters of types in the same recursive scope. ([Issue #5795](https://github.com/dotnet/fsharp/issues/5795), [PR #19744](https://github.com/dotnet/fsharp/pull/19744)) +* Import: Don't walk non-F# assemblies when labelling trait constraint sources (PR [#20090](https://github.com/dotnet/fsharp/pull/20090)) ### Added