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 4a6110cf514..a20e7204a0d 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md +++ b/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md @@ -54,6 +54,7 @@ * Fix parallel compilation of scripts ([PR #19649](https://github.com/dotnet/fsharp/pull/19649)) * Fix parser recovery, name resolution, and code completion for unfinished enum patterns ([PR #19708](https://github.com/dotnet/fsharp/pull/19708)) * Parser: fix unexpected diagnostics in debug builds, improve error messages ([PR #19730](https://github.com/dotnet/fsharp/pull/19730)) +* Fix overload resolution of static member extension on generic types when the call uses explicit type arguments and an intrinsic overload of the same name exists. ([Issue #19664](https://github.com/dotnet/fsharp/issues/19664), [PR #19736](https://github.com/dotnet/fsharp/pull/19736)) ### Added diff --git a/src/Compiler/Checking/NameResolution.fs b/src/Compiler/Checking/NameResolution.fs index 54290c21bd0..0650a43d300 100644 --- a/src/Compiler/Checking/NameResolution.fs +++ b/src/Compiler/Checking/NameResolution.fs @@ -2874,6 +2874,20 @@ let rec ResolveLongIdentInTypePrim (ncenv: NameResolver) nenv lookupKind (resInf | Some(MethodItem msets) when isLookUpExpr -> let minfos = msets |> ExcludeHiddenOfMethInfos g ncenv.amap m + // If the intrinsic candidate set already disagrees with the strict + // instance/static filter (e.g. expression-form `Type.Member` is + // resolved as a dot-expression with LookupIsInstance.Yes but yields + // static intrinsic members), relax the filter for the extension lookup + // so static extension members are also considered. See issue #19664. + let isInstanceFilter = + match isInstanceFilter with + | LookupIsInstance.Ambivalent -> isInstanceFilter + | LookupIsInstance.Yes when minfos |> List.exists (fun m -> not m.IsInstance) -> + LookupIsInstance.Ambivalent + | LookupIsInstance.No when minfos |> List.exists (fun m -> m.IsInstance) -> + LookupIsInstance.Ambivalent + | _ -> isInstanceFilter + // fold the available extension members into the overload resolution let extensionMethInfos = ExtensionMethInfosOfTypeInScope ResultCollectionSettings.AllResults ncenv.InfoReader nenv ad optFilter isInstanceFilter m ty diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/MethodResolution/StaticMethodResolution.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/MethodResolution/StaticMethodResolution.fs new file mode 100644 index 00000000000..59250d79e8a --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/MethodResolution/StaticMethodResolution.fs @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace Conformance.BasicGrammarElements + +open FSharp.Test.Compiler +open Xunit + +module StaticMethodResolution = + + // Regression test for https://github.com/dotnet/fsharp/issues/19664 + // + // When a static extension method is defined in a *different* [] module than + // the generic type it extends, and shares its name with an intrinsic static member, + // resolving the call via the explicit-type-argument syntax `Type.Member(...)` + // previously failed with FS0505. The non-generic dotted form `Type.Member(...)` + // resolved correctly, so any regression test that omits the explicit type arguments + // does not actually exercise the bug. See the discussion at + // https://github.com/dotnet/fsharp/issues/19675#issuecomment-4373059900. + [] + let ``Static extension on generic type resolves with explicit type arguments (issue 19664)`` () = + Fsx """ +module Extensions = + + type StaticGeneric<'T> = + static member Bar() = () + static member Bar(_: int, _: int) = () + + [] + module StaticGenericExtensions = + type StaticGeneric<'T> with + static member Bar(_: int) = () + +module Program = + open Extensions + + StaticGeneric.Bar() // intrinsic, 0 args + StaticGeneric.Bar(42) // regressed: extension, 1 arg, see issue 19664 (FS0505) + StaticGeneric.Bar(42, 0) // intrinsic, 2 args + """ + |> withOptions ["--nowarn:1125"] + |> typecheck + |> shouldSucceed diff --git a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj index 50937f9945b..bdbca65675a 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj +++ b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj @@ -65,6 +65,7 @@ +