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
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
@@ -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
@@ -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
Original file line number Diff line number Diff line change
@@ -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 =
26 changes: 13 additions & 13 deletions tests/FSharp.Compiler.ComponentTests/Language/InterfaceTests.fs
Original file line number Diff line number Diff line change
@@ -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``() =
FSharp """
[<Interface>]
type I =
member _.X () = 1
@@ -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"
@@ -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
@@ -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
@@ -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>
@@ -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
@@ -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