Skip to content

Member call resolves to generic overload even when matching non-generic overload is available #19890

Description

@brianberns

I'm not calling this a bug, because I think it functions as designed, but I found it very surprising when I ran into it the wild:

namespace One

type Foo() = class end

[<AutoOpen>]
module FooExt =
    type Foo with
        member _.GetString() = "Non-generic"

namespace Two

[<AutoOpen>]
module FooExt =
    type One.Foo with
        member _.GetString<'t>() = $"Generic: {typeof<'t>.Name}"

namespace Three

open One
open Two

module Program =

    let foo = Foo()
    printfn $"{foo.GetString()}"   // output: "Generic: Object"

My expectation was that foo.GetString() would resolve to the non-generic version of GetString, but instead the compiler chose GetString<obj> with nary a peep. If I reverse the order of the open statements, then it resolves to the non-generic version, so I guess it's a case of "last namespace wins".

Suggestion: If the compiler can match a non-generic call with a non-generic member, that should probably take precedence over matching a generic member with an implicit obj type parameter, even if the generic member is in a more recent namespace. Silently filling that type parameter with obj when a non-generic match is available is at least worth a warning, I would think.

Note: In the real world, I was simply the consumer of the Foo type (which lives in an external NuGet package), so I had no visibility into exactly how it was defined across namespaces. Thus, the example code above ruins the surprise that I actually experienced. (IntelliSense gives no hint that they are in different namespaces.)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    Status
    New

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions