From f40348aa480890250b2d67b42bbee25641c210c4 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Sat, 18 Apr 2026 11:19:44 +0200 Subject: [PATCH 1/6] Fix #19596: overloaded member with unit parameter conformance check SignatureConformance.fs: when matching overloaded members, add relaxed fallback for unmatched pairs using type equivalence. This handles member M(()) (argInfos=[[]]) vs sig member M: unit->unit (argInfos=[[unit_arg]]) where types are equivalent but TotalArgCount differs. Also relax checkValInfo arg group check: empty impl group is compatible with singleton sig group for unit-parameter members. Tests: roundtrip test, handwritten sig+impl+consumer (both directions). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/Compiler/Checking/SignatureConformance.fs | 49 +++++++++-- .../Signatures/TypeTests.fs | 88 ++++++++++++++++++- 2 files changed, 127 insertions(+), 10 deletions(-) diff --git a/src/Compiler/Checking/SignatureConformance.fs b/src/Compiler/Checking/SignatureConformance.fs index 5c947bda13c..9bb322f62d5 100644 --- a/src/Compiler/Checking/SignatureConformance.fs +++ b/src/Compiler/Checking/SignatureConformance.fs @@ -295,11 +295,24 @@ type Checker(g, amap, denv, remapInfo: SignatureRepackageInfo, checkingSig) = err(fun(x, y, z) -> FSComp.SR.ValueNotContainedMutabilityGenericParametersDiffer(x, y, z, string mtps, string ntps)) elif implValInfo.KindsOfTypars <> sigValInfo.KindsOfTypars then err(FSComp.SR.ValueNotContainedMutabilityGenericParametersAreDifferentKinds) - elif not (nSigArgInfos <= implArgInfos.Length && List.forall2 (fun x y -> List.length x <= List.length y) sigArgInfos (fst (List.splitAt nSigArgInfos implArgInfos))) then + else + // Check arg group arities. An empty impl group [] is compatible with + // a singleton sig group [_] when the member takes unit (e.g. member M(()) vs member M: unit -> unit) + let argGroupsCompatible = + nSigArgInfos <= implArgInfos.Length && + List.forall2 + (fun (sigGroup: ArgReprInfo list) (implGroup: ArgReprInfo list) -> + List.length sigGroup <= List.length implGroup + || (List.length implGroup = 0 && List.length sigGroup = 1)) + sigArgInfos + (fst (List.splitAt nSigArgInfos implArgInfos)) + + if not argGroupsCompatible then err(fun(x, y, z) -> FSComp.SR.ValueNotContainedMutabilityAritiesDiffer(x, y, z, id.idText, string nSigArgInfos, id.idText, id.idText)) else let implArgInfos = implArgInfos |> List.truncate nSigArgInfos - let implArgInfos = (implArgInfos, sigArgInfos) ||> List.map2 (fun l1 l2 -> l1 |> List.take l2.Length) + // When impl has empty group [] and sig has [unit_arg], use min to avoid taking more than available + let implArgInfos = (implArgInfos, sigArgInfos) ||> List.map2 (fun l1 l2 -> l1 |> List.take (min l1.Length l2.Length)) // Propagate some information signature to implementation. // Check the attributes on each argument, and update the ValReprInfo for @@ -307,7 +320,11 @@ type Checker(g, amap, denv, remapInfo: SignatureRepackageInfo, checkingSig) = // This ensures that the compiled form of the value matches the signature rather than // the implementation. This also propagates argument names from signature to implementation let res = - (implArgInfos, sigArgInfos) ||> List.forall2 (List.forall2 (fun implArgInfo sigArgInfo -> + (implArgInfos, sigArgInfos) ||> List.forall2 (fun (implGroup: ArgReprInfo list) (sigGroup: ArgReprInfo list) -> + // When impl group is empty (unit param like member M(())), skip arg-level checks + if implGroup.IsEmpty then true + else + (implGroup, sigGroup) ||> List.forall2 (fun implArgInfo sigArgInfo -> checkAttribs aenv (implArgInfo.Attribs.AsList()) (sigArgInfo.Attribs.AsList()) (fun attribs -> match implArgInfo.Name, sigArgInfo.Name with | Some iname, Some sname when sname.idText <> iname.idText -> @@ -661,7 +678,7 @@ type Checker(g, amap, denv, remapInfo: SignatureRepackageInfo, checkingSig) = let fkey = fv.GetLinkagePartialKey() (akey.MemberParentMangledName = fkey.MemberParentMangledName) && (akey.LogicalName = fkey.LogicalName) && - (akey.TotalArgCount = fkey.TotalArgCount) + (akey.TotalArgCount = fkey.TotalArgCount) (implModType.AllValsAndMembersByLogicalNameUncached, signModType.AllValsAndMembersByLogicalNameUncached) ||> NameMap.suball2 @@ -683,9 +700,29 @@ type Checker(g, amap, denv, remapInfo: SignatureRepackageInfo, checkingSig) = | None -> None | Some av -> Some(fv, av)) + // For unmatched sig vals, try relaxed matching for unit-parameter equivalence: + // member M(()) has TotalArgCount 1, sig member M: unit -> unit has TotalArgCount 2, + // but their types are both unit -> unit. + let matchedAvs = matchingPairs |> List.map snd + let matchedFvs = matchingPairs |> List.map fst + let unmatchedFvs = fvs |> List.filter (fun fv -> not (List.exists (fun fv2 -> System.Object.ReferenceEquals(fv, fv2)) matchedFvs)) + let unmatchedAvs = avs |> List.filter (fun av -> not (List.exists (fun av2 -> System.Object.ReferenceEquals(av, av2)) matchedAvs)) + let relaxedPairs = + unmatchedFvs |> List.choose (fun fv -> + let fkey = fv.GetLinkagePartialKey() + match unmatchedAvs |> List.tryFind (fun av -> + let akey = av.GetLinkagePartialKey() + akey.MemberParentMangledName = fkey.MemberParentMangledName && + akey.LogicalName = fkey.LogicalName && + av.IsMember && fv.IsMember && + typeAEquivAux EraseAll g aenv av.Type fv.Type) with + | None -> None + | Some av -> Some(fv, av)) + let allMatchingPairs = matchingPairs @ relaxedPairs + // Check the ones with matching linkage - let allPairsOk = matchingPairs |> List.map (fun (fv, av) -> checkVal implModRef aenv infoReader av fv) |> List.forall id - let someNotOk = matchingPairs.Length < fvs.Length + let allPairsOk = allMatchingPairs |> List.map (fun (fv, av) -> checkVal implModRef aenv infoReader av fv) |> List.forall id + let someNotOk = allMatchingPairs.Length < fvs.Length // Report an error for those that don't. Try pairing up by enclosing-type/name if someNotOk then let noMatches, partialMatchingPairs = diff --git a/tests/FSharp.Compiler.ComponentTests/Signatures/TypeTests.fs b/tests/FSharp.Compiler.ComponentTests/Signatures/TypeTests.fs index d4b4815e086..2c776682ed6 100644 --- a/tests/FSharp.Compiler.ComponentTests/Signatures/TypeTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Signatures/TypeTests.fs @@ -762,10 +762,90 @@ let (|A|B|) (x: int) = if x > 0 then A else B """ // Sweep: overloaded member with unit parameter (FS0193) — #19596 -// Roundtrip fails: member M(()) generates sig 'member M: unit -> unit' but -// conformance checker can't match it when M is overloaded. The sig syntax -// is correct but the conformance check for unit-parameter overloads is broken. -[ unit fails when overloaded - FS0193")>] +// Testing both directions and consumer access to understand conformance boundaries. + +// Direction 1: handwritten sig says "member M: unit -> unit", impl has "member M(()) = ()" +[] +let ``Unit param overload - sig with consumer compiles`` () = + let sigSource = """ +module Lib + +type R1 = + { f1: int } + +type D = + new: unit -> D + member M: unit -> unit + member M: y: R1 -> unit + member N: unit +""" + let implSource = """ +module Lib +type R1 = { f1 : int } +type D() = + member x.N = x.M { f1 = 3 } + member x.M((y: R1)) = () + member x.M(()) = () +""" + let consumerSource = """ +module Consumer +open Lib +let test() = + let d = D() + d.M(()) + d.M { f1 = 42 } + d.N +""" + Fsi sigSource + |> withAdditionalSourceFile (FsSourceWithFileName "Lib.fs" implSource) + |> withAdditionalSourceFile (FsSourceWithFileName "Consumer.fs" consumerSource) + |> ignoreWarnings + |> compile + |> shouldSucceed + |> ignore + +// Direction 2: impl without explicit unit parens "member x.M() = ()" +[] +let ``Unit param overload - non-paren impl with sig and consumer`` () = + let sigSource = """ +module Lib + +type R1 = + { f1: int } + +type D = + new: unit -> D + member M: unit -> unit + member M: y: R1 -> unit + member N: unit +""" + let implSource = """ +module Lib +type R1 = { f1 : int } +type D() = + member x.N = x.M { f1 = 3 } + member x.M((y: R1)) = () + member x.M() = () +""" + let consumerSource = """ +module Consumer +open Lib +let test() = + let d = D() + d.M() + d.M { f1 = 42 } + d.N +""" + Fsi sigSource + |> withAdditionalSourceFile (FsSourceWithFileName "Lib.fs" implSource) + |> withAdditionalSourceFile (FsSourceWithFileName "Consumer.fs" consumerSource) + |> ignoreWarnings + |> compile + |> shouldSucceed + |> ignore + +// Roundtrip: generated sig from impl, then compile sig+impl+consumer +[] let ``Sweep - overloaded member with unit param roundtrips`` () = assertSignatureRoundtrip """ module Repro From 4e7a8b1ce26561c30cab30ffda18519ccda17545 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Sat, 18 Apr 2026 11:23:04 +0200 Subject: [PATCH 2/6] Update release notes: add #19596 fix, fix PR numbers Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- 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 5c87eb88b88..952ac3cfb02 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md +++ b/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md @@ -49,6 +49,7 @@ * Fix signature generation: type params with special characters missing backtick escaping. ([Issue #19595](https://github.com/dotnet/fsharp/issues/19595), [PR #19609](https://github.com/dotnet/fsharp/pull/19609)) * Fix internal error when using custom attribute with `[]` value type parameter and no `[]`. ([Issue #8353](https://github.com/dotnet/fsharp/issues/8353), [PR #19484](https://github.com/dotnet/fsharp/pull/19484)) * Fix parallel compilation of scripts ([PR #19649](https://github.com/dotnet/fsharp/pull/19649)) +* Fix signature conformance: overloaded member with unit parameter `M(())` now matches sig `member M: unit -> unit`. ([Issue #19596](https://github.com/dotnet/fsharp/issues/19596), [PR #19609](https://github.com/dotnet/fsharp/pull/19609)) ### Added From 5e4aec48350af90140615c6e9eb318060e4eb54f Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Sun, 19 Apr 2026 09:58:28 +0200 Subject: [PATCH 3/6] Add IL verification: M(()) and M() produce identical IL method signatures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Proves the conformance relaxation is safe at IL level — both member M(()) and member M() compile to '.method public hidebysig instance int32 M()', confirming the representation difference is compile-time only. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Signatures/TypeTests.fs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/tests/FSharp.Compiler.ComponentTests/Signatures/TypeTests.fs b/tests/FSharp.Compiler.ComponentTests/Signatures/TypeTests.fs index 2c776682ed6..4eb0eee3f2a 100644 --- a/tests/FSharp.Compiler.ComponentTests/Signatures/TypeTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Signatures/TypeTests.fs @@ -854,4 +854,21 @@ type D() = member x.N = x.M { f1 = 3 } member x.M((y: R1)) = () member x.M(()) = () -""" \ No newline at end of file +""" + +// Verify M(()) and M() produce identical IL method signatures +[] +let ``Unit param - M(()) and M() produce same IL method signature`` () = + FSharp """ +module Test +type D() = + member x.M(()) = 1 + member x.M(y: int) = y +""" + |> compile + |> shouldSucceed + |> verifyILContains [ + ".method public hidebysig instance int32 M() cil managed" + ".method public hidebysig instance int32 M(int32 y) cil managed" + ] + |> ignore \ No newline at end of file From 32fc9db1dde405d32e6d6d66f52226836c6a8ee3 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Sun, 19 Apr 2026 10:03:48 +0200 Subject: [PATCH 4/6] Add inverse conformance tests for unit param overload MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Consumer cannot call d.M() when impl is M(()) with overloads (FS0503 expected) - Consumer can call d.M() when impl is M() with sig M: unit -> unit Covers both directions of sig↔impl conformance with consumer validation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Signatures/TypeTests.fs | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/tests/FSharp.Compiler.ComponentTests/Signatures/TypeTests.fs b/tests/FSharp.Compiler.ComponentTests/Signatures/TypeTests.fs index 4eb0eee3f2a..eea44464179 100644 --- a/tests/FSharp.Compiler.ComponentTests/Signatures/TypeTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Signatures/TypeTests.fs @@ -856,6 +856,87 @@ type D() = member x.M(()) = () """ +// Inverse direction 1: impl M(()) but consumer calls d.M() (no parens) — must fail with FS0503 +[] +let ``Unit param overload - consumer cannot call M() when impl is M(()) with overloads`` () = + let sigSource = """ +module Lib + +type R1 = + { f1: int } + +type D = + new: unit -> D + member M: unit -> unit + member M: y: R1 -> unit + member N: unit +""" + let implSource = """ +module Lib +type R1 = { f1 : int } +type D() = + member x.N = x.M { f1 = 3 } + member x.M((y: R1)) = () + member x.M(()) = () +""" + let consumerSource = """ +module Consumer +open Lib +let test() = + let d = D() + d.M() + d.M { f1 = 42 } + d.N +""" + Fsi sigSource + |> withAdditionalSourceFile (FsSourceWithFileName "Lib.fs" implSource) + |> withAdditionalSourceFile (FsSourceWithFileName "Consumer.fs" consumerSource) + |> ignoreWarnings + |> compile + |> shouldFail + |> withErrorCode 503 + |> ignore + +// Inverse direction 2: impl M() (no explicit unit), sig M: unit -> unit, consumer calls d.M() +[] +let ``Unit param overload - consumer calls M() with normal impl and sig`` () = + let sigSource = """ +module Lib + +type R1 = + { f1: int } + +type D = + new: unit -> D + member M: unit -> unit + member M: y: R1 -> unit + member N: unit +""" + let implSource = """ +module Lib +type R1 = { f1 : int } +type D() = + member x.N = x.M { f1 = 3 } + member x.M((y: R1)) = () + member x.M() = () +""" + let consumerSource = """ +module Consumer +open Lib +let test() = + let d = D() + d.M() + d.M { f1 = 42 } + d.N +""" + Fsi sigSource + |> withAdditionalSourceFile (FsSourceWithFileName "Lib.fs" implSource) + |> withAdditionalSourceFile (FsSourceWithFileName "Consumer.fs" consumerSource) + |> ignoreWarnings + |> compile + |> shouldSucceed + |> ignore + // Verify M(()) and M() produce identical IL method signatures [] let ``Unit param - M(()) and M() produce same IL method signature`` () = From c94466e088447dec124a060baab982a022fc3f03 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Wed, 13 May 2026 09:55:19 +0200 Subject: [PATCH 5/6] Address review feedback: fix non-overloaded unit-param conformance - Add type-equivalence fallback in [av],[fv] single-val path so non-overloaded member M(()) conforms to sig member M: unit -> unit (addresses abonie + T-Gro feedback) - Use idiomatic implGroup.IsEmpty && sigGroup.Length = 1 (T-Gro nit) - Add test for non-overloaded unit-param member conformance Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/Compiler/Checking/SignatureConformance.fs | 4 +++- .../Signatures/TypeTests.fs | 22 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/Compiler/Checking/SignatureConformance.fs b/src/Compiler/Checking/SignatureConformance.fs index 9bb322f62d5..0069d2c95b3 100644 --- a/src/Compiler/Checking/SignatureConformance.fs +++ b/src/Compiler/Checking/SignatureConformance.fs @@ -303,7 +303,7 @@ type Checker(g, amap, denv, remapInfo: SignatureRepackageInfo, checkingSig) = List.forall2 (fun (sigGroup: ArgReprInfo list) (implGroup: ArgReprInfo list) -> List.length sigGroup <= List.length implGroup - || (List.length implGroup = 0 && List.length sigGroup = 1)) + || (implGroup.IsEmpty && sigGroup.Length = 1)) sigArgInfos (fst (List.splitAt nSigArgInfos implArgInfos)) @@ -689,6 +689,8 @@ type Checker(g, amap, denv, remapInfo: SignatureRepackageInfo, checkingSig) = | [av], [fv] -> if valuesPartiallyMatch av fv then checkVal implModRef aenv infoReader av fv + elif av.IsMember && fv.IsMember && typeAEquivAux EraseAll g aenv av.Type fv.Type then + checkVal implModRef aenv infoReader av fv else sigValHadNoMatchingImplementation fv None false diff --git a/tests/FSharp.Compiler.ComponentTests/Signatures/TypeTests.fs b/tests/FSharp.Compiler.ComponentTests/Signatures/TypeTests.fs index eea44464179..76a5643ee5f 100644 --- a/tests/FSharp.Compiler.ComponentTests/Signatures/TypeTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Signatures/TypeTests.fs @@ -937,6 +937,28 @@ let test() = |> shouldSucceed |> ignore +// Non-overloaded: member M(()) with sig member M: unit -> unit (no other overloads) +[] +let ``Unit param - non-overloaded member M(()) conforms to sig member M: unit -> unit`` () = + let sigSource = """ +module Lib + +type D = + new: unit -> D + member M: unit -> unit +""" + let implSource = """ +module Lib +type D() = + member _.M(()) = () +""" + Fsi sigSource + |> withAdditionalSourceFile (FsSourceWithFileName "Lib.fs" implSource) + |> ignoreWarnings + |> compile + |> shouldSucceed + |> ignore + // Verify M(()) and M() produce identical IL method signatures [] let ``Unit param - M(()) and M() produce same IL method signature`` () = From 7ac3c7094d8daa40c5e4ab7405ec28f289c2a21d Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Wed, 13 May 2026 10:10:00 +0200 Subject: [PATCH 6/6] Address expert review: fix PR number, prevent double-consume, style fixes - Fix release notes PR number: #19609 -> #19615 - Prevent double-consume of impl vals in relaxed matching by using List.fold to track remaining available avs - Use obj.ReferenceEquals instead of System.Object.ReferenceEquals for consistency with codebase style - Add trailing newline to TypeTests.fs Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../.FSharp.Compiler.Service/11.0.100.md | 2 +- src/Compiler/Checking/SignatureConformance.fs | 20 +++++++++++-------- .../Signatures/TypeTests.fs | 2 +- 3 files changed, 14 insertions(+), 10 deletions(-) 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 952ac3cfb02..f0b8259b54b 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md +++ b/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md @@ -49,7 +49,7 @@ * Fix signature generation: type params with special characters missing backtick escaping. ([Issue #19595](https://github.com/dotnet/fsharp/issues/19595), [PR #19609](https://github.com/dotnet/fsharp/pull/19609)) * Fix internal error when using custom attribute with `[]` value type parameter and no `[]`. ([Issue #8353](https://github.com/dotnet/fsharp/issues/8353), [PR #19484](https://github.com/dotnet/fsharp/pull/19484)) * Fix parallel compilation of scripts ([PR #19649](https://github.com/dotnet/fsharp/pull/19649)) -* Fix signature conformance: overloaded member with unit parameter `M(())` now matches sig `member M: unit -> unit`. ([Issue #19596](https://github.com/dotnet/fsharp/issues/19596), [PR #19609](https://github.com/dotnet/fsharp/pull/19609)) +* Fix signature conformance: overloaded member with unit parameter `M(())` now matches sig `member M: unit -> unit`. ([Issue #19596](https://github.com/dotnet/fsharp/issues/19596), [PR #19615](https://github.com/dotnet/fsharp/pull/19615)) ### Added diff --git a/src/Compiler/Checking/SignatureConformance.fs b/src/Compiler/Checking/SignatureConformance.fs index 0069d2c95b3..afea753688a 100644 --- a/src/Compiler/Checking/SignatureConformance.fs +++ b/src/Compiler/Checking/SignatureConformance.fs @@ -689,7 +689,9 @@ type Checker(g, amap, denv, remapInfo: SignatureRepackageInfo, checkingSig) = | [av], [fv] -> if valuesPartiallyMatch av fv then checkVal implModRef aenv infoReader av fv - elif av.IsMember && fv.IsMember && typeAEquivAux EraseAll g aenv av.Type fv.Type then + elif av.IsMember && fv.IsMember + && av.LogicalName <> ".ctor" + && typeAEquivAux EraseAll g aenv av.Type fv.Type then checkVal implModRef aenv infoReader av fv else sigValHadNoMatchingImplementation fv None @@ -707,19 +709,21 @@ type Checker(g, amap, denv, remapInfo: SignatureRepackageInfo, checkingSig) = // but their types are both unit -> unit. let matchedAvs = matchingPairs |> List.map snd let matchedFvs = matchingPairs |> List.map fst - let unmatchedFvs = fvs |> List.filter (fun fv -> not (List.exists (fun fv2 -> System.Object.ReferenceEquals(fv, fv2)) matchedFvs)) - let unmatchedAvs = avs |> List.filter (fun av -> not (List.exists (fun av2 -> System.Object.ReferenceEquals(av, av2)) matchedAvs)) - let relaxedPairs = - unmatchedFvs |> List.choose (fun fv -> + let unmatchedFvs = fvs |> List.filter (fun fv -> not (List.exists (fun fv2 -> obj.ReferenceEquals(fv, fv2)) matchedFvs)) + let unmatchedAvs = avs |> List.filter (fun av -> not (List.exists (fun av2 -> obj.ReferenceEquals(av, av2)) matchedAvs)) + let relaxedPairs, _ = + (([], unmatchedAvs), unmatchedFvs) + ||> List.fold (fun (pairs, remainingAvs) fv -> let fkey = fv.GetLinkagePartialKey() - match unmatchedAvs |> List.tryFind (fun av -> + match remainingAvs |> List.tryFind (fun av -> let akey = av.GetLinkagePartialKey() akey.MemberParentMangledName = fkey.MemberParentMangledName && akey.LogicalName = fkey.LogicalName && av.IsMember && fv.IsMember && + av.LogicalName <> ".ctor" && typeAEquivAux EraseAll g aenv av.Type fv.Type) with - | None -> None - | Some av -> Some(fv, av)) + | None -> (pairs, remainingAvs) + | Some av -> ((fv, av) :: pairs, remainingAvs |> List.filter (fun a -> not (obj.ReferenceEquals(a, av))))) let allMatchingPairs = matchingPairs @ relaxedPairs // Check the ones with matching linkage diff --git a/tests/FSharp.Compiler.ComponentTests/Signatures/TypeTests.fs b/tests/FSharp.Compiler.ComponentTests/Signatures/TypeTests.fs index 76a5643ee5f..dc70a6391a9 100644 --- a/tests/FSharp.Compiler.ComponentTests/Signatures/TypeTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Signatures/TypeTests.fs @@ -974,4 +974,4 @@ type D() = ".method public hidebysig instance int32 M() cil managed" ".method public hidebysig instance int32 M(int32 y) cil managed" ] - |> ignore \ No newline at end of file + |> ignore