Skip to content
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

Overloaded interface methods/properties in member constraints differing in return type only are rejected #1402

Open
5 of 6 tasks
IS4Code opened this issue Jan 15, 2025 · 3 comments

Comments

@IS4Code
Copy link

IS4Code commented Jan 15, 2025

I propose we have modifications to the overload resolution algorithm used by member constraints in SRTPs that fix the following situation where there are overloaded interface methods differing in return type only:

open System.Collections
open System.Collections.Generic
let a: int seq = List<int>()
let i1 = a.GetEnumerator() // works fine
let i2 = (^T: (member GetEnumerator: unit -> IEnumerator<int>) a) // error FS0043: A unique overload for method 'GetEnumerator' could not be determined based on type information prior to this program point. A type annotation may be needed.
let i3 = (^T: (member GetEnumerator: unit -> #IEnumerator<int>) a) // same error

When the method is called directly, everything works, but with the member constraint, both overloads of GetEnumerator are taken into consideration, even though only one matches the return type.

It should be noted that if I remove : int seq, only the last line starts working, so the issue is not caused by variance (the type must match directly, not via a derived one unless specified by #).

A similar situation happens with properties:

let a: IEnumerator<int> = List<int>().GetEnumerator()
let c1 = a.Current // fine
let c2 = (^T: (member Current: int) a) // error FS0043: A unique overload for method 'get_Current' could not be determined based on type information prior to this program point. A type annotation may be needed.

Removing : IEnumerator<int> once again makes the code compilable, correctly ignoring the get_Current method from interfaces.

The proposed changes to the algorithm are:

  • Methods/properties with return type incompatible with the type specified in the constraint are removed from the set of candidates.
  • In case of multiple candidates, the candidate methods/properties that would normally be shadowed by other candidates when called directly are removed.

The existing way of approaching this problem in F# is not to use member constraints in this situation. I am not aware of a solution using a type annotation, as the error message suggests.

Pros and Cons

The advantages of making this adjustment to F# are parity with normal overload resolution and less surprising behaviour in situations where an overload can be clearly determined.

The disadvantages of making this adjustment to F# are changing potentially complicated code with the risk of introducing silent behaviour changes.

Extra information

Estimated cost (XS, S, M, L, XL, XXL): S

Related suggestions: #820

Affidavit (please submit!)

  • This is not a question (e.g. like one you might ask on StackOverflow) and I have searched StackOverflow for discussions of this issue

  • This is a language change and not purely a tooling change (e.g. compiler bug, editor support, warning/error messages, new warning, non-breaking optimisation) belonging to the compiler and tooling repository

  • This is not something which has obviously "already been decided" in previous versions of F#. If you're questioning a fundamental design decision that has obviously already been taken (e.g. "Make F# untyped") then please don't submit it

  • I have searched both open and closed suggestions on this site and believe this is not a duplicate

  • This is not a breaking change to the F# language design (Probably. The proposed changes could be in effect only when there is already a conflict, to be sure.)

  • I or my company would be willing to help implement and/or test this

For Readers

If you would like to see this issue implemented, please click the 👍 emoji on this issue. These counts are used to generally order the suggestions by engagement.

@vzarytovskii
Copy link

It is a duplicate of #820 and is covered in the RFC (https://github.com/fsharp/fslang-design/blob/main/RFCs/FS-1043-extension-members-for-operators-and-srtp-constraints.md)

It was supposed to be part of SRTP+extensions feature, it is not finished yet. I might try to resurrect it on top of latest main and see if there's anything missing:
dotnet/fsharp#6805 (comment)
https://github.com/dotnet/fsharp/pull/6805/files#diff-9246b98d4e4b65d4b62b040af442919aa03ff2c68d752004be734b8d59514157

@IS4Code
Copy link
Author

IS4Code commented Jan 15, 2025

Thank you! I noticed the linked issue before but did not realize it is connected to this too. I understand there are two ways to solve this; one by taking the return type into account (i.e. what you linked to), the other one being to use shadowing to remove ambiguous candidates. I will be happy to see this working either way.

@vzarytovskii
Copy link

I think shadowing will be a new mechanism for compiler (in a way that we don't do that for methods currently, we don't choose/shadow methods for user in majority of the cases). Taking return into account when resolving might be more straigtforward.

There's an issue for the tiebreakers as well, which might also change how things are getting chosen, theoretically: #905

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants