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

Fix virtual static call #17013

Merged
merged 16 commits into from
Apr 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/release-notes/.FSharp.Compiler.Service/8.0.400.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@

* Various parenthesization API fixes. ([PR #16977](https://github.com/dotnet/fsharp/pull/16977))
* Fix bug in optimization of for-loops over integral ranges with steps and units of measure. ([Issue #17025](https://github.com/dotnet/fsharp/issues/17025), [PR #17040](https://github.com/dotnet/fsharp/pull/17040))
* Fix calling an overridden virtual static method via the interface ([PR #17013](https://github.com/dotnet/fsharp/pull/17013))
4 changes: 2 additions & 2 deletions src/Compiler/Checking/MethodCalls.fs
Original file line number Diff line number Diff line change
Expand Up @@ -906,8 +906,8 @@ let IsBaseCall objArgs =
/// For example, when calling an interface method on a struct, or a method on a constrained
/// variable type.
let ComputeConstrainedCallInfo g amap m staticTyOpt args (minfo: MethInfo) =
match args, staticTyOpt with
| _, Some staticTy when not minfo.IsExtensionMember && not minfo.IsInstance && minfo.IsAbstract -> Some staticTy
match args, staticTyOpt with
| _, Some staticTy when not minfo.IsExtensionMember && not minfo.IsInstance && (minfo.IsAbstract || minfo.IsVirtual) -> Some staticTy

| (objArgExpr :: _), _ when minfo.IsInstance && not minfo.IsExtensionMember ->
let methObjTy = minfo.ApparentEnclosingType
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -606,6 +606,49 @@ module Test =
#endif
]

[<FactForNETCOREAPP>]
let ``F# can call overwritten static virtual member from interface``() =
let CSharpLib =
CSharp """
namespace Test;

public interface I
{
static virtual string Echo(string x) => $"I.Echo: {x}";
}
"""
|> withCSharpLanguageVersion CSharpLanguageVersion.CSharp11
|> withName "CsLibAssembly"

FSharp """
type Imp() =
interface Test.I with
static member Echo (x: string) = $"Imp.I.Echo: {x}"

static member Echo (x: string) = $"Imp.Echo: {x}"

let echo<'T when 'T :> Test.I> x = 'T.Echo(x)

let inline echo_srtp<'T when 'T : (static member Echo: string -> string)> x = 'T.Echo(x)

match echo<Imp> "a" with
| "Imp.I.Echo: a" -> printfn "success"
| "Imp.Echo: a" -> failwith "incorrectly invoked the class 'Echo'"
| "I.Echo: a" -> failwith "incorrectly invoked the base interface 'Echo'"
| _ -> failwith "incorrect value"

match echo_srtp<Imp> "a" with
| "Imp.Echo: a" -> printfn "success"
| "Imp.I.Echo: a" -> failwith "incorrectly invoked the interface 'Echo'"
| "I.Echo: a" -> failwith "incorrectly invoked the base interface 'Echo'"
| _ -> failwith "incorrect value"
"""
|> withReferences [CSharpLib]
|> withLangVersion80
|> asExe
|> compileAndRun
|> shouldSucceed

[<FactForNETCOREAPP>]
let ``C# can call constrained method defined in F#`` () =
let FSharpLib =
Expand Down
26 changes: 13 additions & 13 deletions tests/FSharp.Compiler.ComponentTests/Language/InterfaceTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ open Xunit
open FSharp.Test.Compiler

[<Fact>]
let ``Concrete instance method is not allowed in interfaces in lang preview``() =
FSharp $"""
let ``Concrete instance method is not allowed in interfaces in lang version80``() =
DragonSA marked this conversation as resolved.
Show resolved Hide resolved
FSharp """
[<Interface>]
type I =
member _.X () = 1
Expand All @@ -18,8 +18,8 @@ type I =
]

[<Fact>]
let ``Concrete instance property is not allowed in interfaces in lang preview``() =
FSharp $"""
let ``Concrete instance property is not allowed in interfaces in lang version80``() =
FSharp """
[<Interface>]
type I =
member _.Prop = "x"
Expand All @@ -32,8 +32,8 @@ type I =
]

[<Fact>]
let ``Concrete static members are allowed in interfaces in lang preview``() =
FSharp $"""
let ``Concrete static members are allowed in interfaces in lang version80``() =
FSharp """
[<Interface>]
type I<'T> =
static member Echo (x: 'T) = x
Expand All @@ -49,7 +49,7 @@ if I<int>.Echo 42 <> 42 || I<int>.Prop <> 0 || not (isNull I<string>.Prop) then

[<Fact>]
let ``Concrete static members are not allowed in interfaces in lang version70``() =
FSharp $"""
FSharp """
[<Interface>]
type I<'T> =
static member Echo (x: 'T) = x
Expand All @@ -63,8 +63,8 @@ type I<'T> =
]

[<Fact>]
let ``Concrete static members are allowed in interfaces as intrinsics in lang preview``() =
FSharp $"""
let ``Concrete static members are allowed in interfaces as intrinsics in lang version80``() =
FSharp """
[<Interface>]
type I<'T> =
static member Prop = Unchecked.defaultof<'T>
Expand All @@ -81,8 +81,8 @@ if I<int>.Echo 42 <> 42 || I<int>.Prop <> 0 || not (isNull I<string>.Prop) then


[<Fact>]
let ``Interface with concrete static members can be implemented in lang preview``() =
FSharp $"""
let ``Interface with concrete static members can be implemented in lang version80``() =
FSharp """
[<Interface>]
type I =
static member Echo (x: string) = x
Expand All @@ -92,12 +92,12 @@ type Imp () =
interface I with
member _.Blah = 3

let o = {{ new I with member _.Blah = 4 }}
let o = { new I with member _.Blah = 4 }

if I.Echo "yup" <> "yup" || (Imp() :> I).Blah <> 3 || o.Blah <> 4 then
failwith "failed"
"""
|> withLangVersion80
|> asExe
|> compileAndRun
|> shouldSucceed
|> shouldSucceed
Loading