diff --git a/docs/release-notes/.FSharp.Compiler.Service/8.0.400.md b/docs/release-notes/.FSharp.Compiler.Service/8.0.400.md index b378403abd8..c18d969fa4c 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/8.0.400.md +++ b/docs/release-notes/.FSharp.Compiler.Service/8.0.400.md @@ -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)) diff --git a/src/Compiler/Checking/MethodCalls.fs b/src/Compiler/Checking/MethodCalls.fs index 5f60bfce120..47342714394 100644 --- a/src/Compiler/Checking/MethodCalls.fs +++ b/src/Compiler/Checking/MethodCalls.fs @@ -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 diff --git a/tests/FSharp.Compiler.ComponentTests/Interop/StaticsInInterfaces.fs b/tests/FSharp.Compiler.ComponentTests/Interop/StaticsInInterfaces.fs index b16f787ba23..4eb6c169a6f 100644 --- a/tests/FSharp.Compiler.ComponentTests/Interop/StaticsInInterfaces.fs +++ b/tests/FSharp.Compiler.ComponentTests/Interop/StaticsInInterfaces.fs @@ -606,6 +606,49 @@ module Test = #endif ] + [] + 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 "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 "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 + [] let ``C# can call constrained method defined in F#`` () = let FSharpLib = diff --git a/tests/FSharp.Compiler.ComponentTests/Language/InterfaceTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/InterfaceTests.fs index 261a8d9ef4c..645bcfd800b 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/InterfaceTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/InterfaceTests.fs @@ -4,8 +4,8 @@ open Xunit open FSharp.Test.Compiler [] -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 """ [] type I = member _.X () = 1 @@ -18,8 +18,8 @@ type I = ] [] -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 """ [] type I = member _.Prop = "x" @@ -32,8 +32,8 @@ type I = ] [] -let ``Concrete static members are allowed in interfaces in lang preview``() = - FSharp $""" +let ``Concrete static members are allowed in interfaces in lang version80``() = + FSharp """ [] type I<'T> = static member Echo (x: 'T) = x @@ -49,7 +49,7 @@ if I.Echo 42 <> 42 || I.Prop <> 0 || not (isNull I.Prop) then [] let ``Concrete static members are not allowed in interfaces in lang version70``() = - FSharp $""" + FSharp """ [] type I<'T> = static member Echo (x: 'T) = x @@ -63,8 +63,8 @@ type I<'T> = ] [] -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 """ [] type I<'T> = static member Prop = Unchecked.defaultof<'T> @@ -81,8 +81,8 @@ if I.Echo 42 <> 42 || I.Prop <> 0 || not (isNull I.Prop) then [] -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 """ [] type I = static member Echo (x: string) = x @@ -92,7 +92,7 @@ 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" @@ -100,4 +100,4 @@ if I.Echo "yup" <> "yup" || (Imp() :> I).Blah <> 3 || o.Blah <> 4 then |> withLangVersion80 |> asExe |> compileAndRun - |> shouldSucceed \ No newline at end of file + |> shouldSucceed