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

SRTP doesn't handle calling member hiding inherited members #5531

Closed
vbfox opened this issue Aug 17, 2018 · 5 comments
Closed

SRTP doesn't handle calling member hiding inherited members #5531

vbfox opened this issue Aug 17, 2018 · 5 comments
Milestone

Comments

@vbfox
Copy link

vbfox commented Aug 17, 2018

When SRTP is used to call a member that is hiding an inherited member (C# new modifier) it fail to compile with the following error:

A unique overload for method 'Foo' could not be determined based on type information prior to this program point.
A type annotation may be needed. Candidates:

abstract member Base.Foo : unit -> unit,
member Derived.Foo : unit -> unit

Repro steps

The last line of this program should compile as the type is fully known and the correct Foo method can be chosen (The compiler does it correctly when the method is called directly without SRTP)

type Base() =
    abstract member Foo : unit -> unit
    default this.Foo() = printfn "Base"
    
type Derived() =
    inherit Base()
    member this.Foo() = printfn "Derived"
    
let inline callFoo< ^T when ^T : (member Foo: unit -> unit) > (t: ^T) =
    (^T : (member Foo: unit -> unit) (t))
    
let b = Base()
let d = Derived()
let bd = d :> Base

b.Foo()
bd.Foo()
d.Foo()

callFoo<Base> b
callFoo<Base> bd
callFoo<Base> d
// A unique overload for method 'Foo' could not be determined based on type information prior to this program point.
// A type annotation may be needed. Candidates:
//   abstract member Base.Foo : unit -> unit,
//   member Derived.Foo : unit -> unit
callFoo<Derived> d

Known workarounds

None AFAIK, if a library you use suddenly does it (Like Roslyn just did between 2.8.2 and 2.9.0) you must rewrite everything without SRTP.

Related information

Tested on SDK 2.1.300 & 2.1.400

@vbfox vbfox changed the title SRTP doesn't handle hidden inherited members SRTP doesn't handle calling member hidding inherited members Aug 17, 2018
@vbfox vbfox changed the title SRTP doesn't handle calling member hidding inherited members SRTP doesn't handle calling member hiding inherited members Aug 17, 2018
@cartermp cartermp added this to the Unknown milestone Sep 15, 2018
@realvictorprm
Copy link
Contributor

I've successfully looked into this and apparently there's definitely no check prior to filter out any methods which are higher in the object hirachy (if they're matching in signature).

realvictorprm added a commit to realvictorprm/visualfsharp that referenced this issue Oct 16, 2018
This is a working fix for the example of dotnet#5531.
However there are still some points open before I think it's a good idea to merge this:
- Discuss the change of this proposal, is it OK to prefer overrides of methods and so ignore base implementations?
- Extend the check to the complete inheritance graph instead of a single "look-back"
- Only ignore a methinfo if the signature of both match (so respect different overloads)

@dsyme It would be great if you could have a look whether this check is allowed at this location or should appear earlier.
@realvictorprm realvictorprm mentioned this issue Oct 16, 2018
3 tasks
@realvictorprm
Copy link
Contributor

How should the compiler act for such an example:

type Base() =
    abstract member Foo : unit -> unit
    default this.Foo() = printfn "Base"
    
type Derived() =
    inherit Base()
    member this.Foo() = printfn "Derived"
    
type DerivedDerived() =
    inherit Derived()
    member this.Foo() = printfn "DerivedDerived"
    
let inline callFoo< ^T when ^T : (member Foo: unit -> unit) > (t: ^T) =
    (^T : (member Foo: unit -> unit) (t))
    
let b = Base()
let d = Derived()
let dd = DerivedDerived()
let bd = d :> Base
callFoo<Derived> d
callFoo<DerivedDerived> dd

Should callFoo<DerivedDerived> dd call Foo of DerivedDerived?

@zpodlovics
Copy link

zpodlovics commented Oct 20, 2018

If you are looking for overload resolution / late binding semantics corner cases, take a look at here and combine it with SRTPs:

eg.:

let inline callUpCv< ^T when ^T : (member Cv: Top -> unit) > (t: ^T) =
    (^T : (member Cv: Top -> unit) (t))

#3526

@vbfox
Copy link
Author

vbfox commented Oct 20, 2018

@realvictorprm Compiler should have one single behavior. It's currently behaving one way when these methods are called without SRTP and should act exactly the same when SRTP is involved.

type Base() =
    abstract member Foo : unit -> unit
    default this.Foo() = printfn "Base"
    
type Derived() =
    inherit Base()
    member this.Foo() = printfn "Derived"
    
type DerivedDerived() =
    inherit Derived()
    member this.Foo() = printfn "DerivedDerived"
    
let inline callFoo< ^T when ^T : (member Foo: unit -> unit) > (t: ^T) =
    (^T : (member Foo: unit -> unit) (t))
    
let b = Base()
let d = Derived()
let dd = DerivedDerived()
let bd = d :> Base
d.Foo() //callFoo<Derived> d
dd.Foo() //callFoo<DerivedDerived> dd
bd.Foo() //callFoo<Base> bd

Prints:

Derived
DerivedDerived
Base

@realvictorprm
Copy link
Contributor

OK great, I have the same opinion on this.

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

No branches or pull requests

4 participants