fsharp icon indicating copy to clipboard operation
fsharp copied to clipboard

Return types in shadowing members are not considered in generic constraint resolution to avoid ambiguity.

Open natalie-o-perret opened this issue 5 years ago • 4 comments

Description

It seems that when a member is shadowing an inherited member with a different return type, the return type is not considered to avoid any ambiguity when the compiler is attempting to resolve the member.

Repro steps

Self-explanatory example:

type Mother() =
    member this.Hello() =
        Unchecked.defaultof<int32>

type Daughter() =
    inherit Mother()
    member this.Hello() =
        Unchecked.defaultof<string>

type SomeoneHolder<'Someone when 'Someone: (member Hello : unit -> string)> =
    { Someone: 'Someone }

let someoneHolder =
    { Someone = Daughter() }

Expected behavior

It should compile.

Actual behavior

It does not, with the error below:

[FS0001] A unique overload for method 'Hello' could not be determined based on type information prior to this program point. A type annotation may be needed. Candidates: member Daughter.Hello : unit -> string, member Mother.Hello : unit -> int32

Known workarounds

None.

Related information

Provide any related information (optional):

The return type

  • Operating system: Windows 10 Enterprise
  • .NET Runtime kind (.NET Core, .NET Framework, Mono): .NET Core 3.1
  • Editing Tools (e.g. Visual Studio Version, Visual Studio): Rider

natalie-o-perret avatar Mar 25 '20 16:03 natalie-o-perret

What was interesting to me wasn't so much that return types aren't considered (this is the default for both C# and F#) but that overload resolution takes shadowed members into account here.

NinoFloris avatar Mar 25 '20 17:03 NinoFloris

@NinoFloris I kinda disagree:

type Daddy() =
    member this.Hello() =
        Unchecked.defaultof<int32>

type SomeoneHolder<'Someone when 'Someone: (member Hello : unit -> string)> =
    { Someone: 'Someone }

let someoneHolder =
    { Someone = Daddy() }

[FS0001] This expression was expected to have type 'string'
but here has type 'int32'

Maybe we're not on the same page.

The return type needs to respect the signature given in the generic constraint.

So... why not the same behavior for a shadowing member?

Also, I don't think it's that surprising that shadowing members are actually processed by the generic constraint resolution process.

=> All in all there is an issue of consistency.

natalie-o-perret avatar Mar 25 '20 17:03 natalie-o-perret

It does surprise me that the shadowed member is being taken into accdount.

Since this involves SRTP constraint resolution I will investigate as part of https://github.com/dotnet/fsharp/pull/6805.

dsyme avatar Sep 03 '20 15:09 dsyme

We (@kerry-perret and I) discussed this briefly over the weekend and found that the code as written is documented as not being allowed. Mainly, member constraints can only be used with SRTP, and cannot be used on types (i.e., even changing ' to ^ won't allow this code to compile):

image

Still, the error is confusing and it appears as if the compiler actually tries to resolve the member constraint here.

abelbraaksma avatar Nov 02 '20 01:11 abelbraaksma