-
Notifications
You must be signed in to change notification settings - Fork 859
Fix #19664: Static extension overload resolution for generic types #19736
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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<TArg>.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 = | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Type System / Name Resolution - design, medium] This change patches the symptom at the candidate-gathering site, not the root cause. The dot-expression Consider whether the right fix is upstream: either (i) classify |
||
| 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) -> | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Overload Resolution / Test Coverage - Behavioral, medium] This Consequences worth justifying:
The |
||
| LookupIsInstance.Ambivalent | ||
| | _ -> isInstanceFilter | ||
|
|
||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Diagnostic Quality - low] Worth verifying: when no overload matches after this relaxation, does the diagnostic still point at the same set of candidates as before? FS0501 (member does not take N arguments) vs FS0505 (is not a static method) vs FS0041 (overload ambiguity) can swap depending on the meth-group contents. Add a negative test (e.g., |
||
| // fold the available extension members into the overload resolution | ||
| let extensionMethInfos = ExtensionMethInfosOfTypeInScope ResultCollectionSettings.AllResults ncenv.InfoReader nenv ad optFilter isInstanceFilter m ty | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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* [<AutoOpen>] 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<TArg>.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. | ||
| [<Fact>] | ||
| 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) = () | ||
|
|
||
| [<AutoOpen>] | ||
| module StaticGenericExtensions = | ||
| type StaticGeneric<'T> with | ||
| static member Bar(_: int) = () | ||
|
|
||
| module Program = | ||
| open Extensions | ||
|
|
||
| StaticGeneric<int>.Bar() // intrinsic, 0 args | ||
| StaticGeneric<int>.Bar(42) // regressed: extension, 1 arg, see issue 19664 (FS0505) | ||
| StaticGeneric<int>.Bar(42, 0) // intrinsic, 2 args | ||
| """ | ||
| |> withOptions ["--nowarn:1125"] | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Test Coverage - medium] Please:
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In that test case |
||
| |> typecheck | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Test Coverage - medium] Test gaps to fill before merge:
|
||
| |> shouldSucceed | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Type System - low/medium] The trigger condition is computed after
ExcludeHiddenOfMethInfos(line 2875). If an intrinsic overload of the name is hidden by a derived override,minfoswill not reflect what is actually present on the type, and the relaxation can fail to fire (false negative) or fire spuriously (false positive) relative to what overload resolution will see. At minimum, leave a comment explaining why hidden-exclusion is the right input for the trigger check; ideally evaluate against the unfilteredmsetsfor the trigger, while still emittingminfosinto the meth group.