Skip to content
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/11.0.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
* Fix insertion context for modules with multiline attributes. ([Issue #18671](https://github.com/dotnet/fsharp/issues/18671))
* Fix `--typecheck-only` for scripts stopping after processing `#load`-ed script ([PR #19048](https://github.com/dotnet/fsharp/pull/19048))
* Fix object expressions in struct types generating invalid IL with byref fields causing TypeLoadException at runtime. ([Issue #19068](https://github.com/dotnet/fsharp/issues/19068), [PR #19070](https://github.com/dotnet/fsharp/pull/19070))
* Reject direct invocation of static abstract interface members on F#-defined interface types, which caused BadImageFormatException at runtime. ([Issue #19231](https://github.com/dotnet/fsharp/issues/19231), [PR #19232](https://github.com/dotnet/fsharp/pull/19232))

### Added

Expand Down
5 changes: 4 additions & 1 deletion src/Compiler/Checking/ConstraintSolver.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3485,9 +3485,12 @@ and ResolveOverloading
| Some ttype -> isTyparTy g ttype
| None -> false

match calledMeth.Method with
let minfo = calledMeth.Method
match minfo with
| ILMeth(ilMethInfo= ilMethInfo) when not isStaticConstrainedCall && ilMethInfo.IsStatic && ilMethInfo.IsAbstract ->
None, ErrorD (Error (FSComp.SR.chkStaticAbstractInterfaceMembers(ilMethInfo.ILName), m)), NoTrace
| FSMeth(g, _, vref, _) when not isStaticConstrainedCall && not minfo.IsInstance && isInterfaceTy g minfo.ApparentEnclosingType && vref.IsDispatchSlotMember ->
None, ErrorD (Error (FSComp.SR.chkStaticAbstractInterfaceMembers(minfo.LogicalName), m)), NoTrace
| _ -> Some calledMeth, CompleteD, NoTrace

| [], _ when not isOpConversion ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1752,3 +1752,46 @@ printfn "Success: %d" result
|> compileAndRun
|> shouldSucceed

// Tests for issue #19231: Invoking static abstract member on interface type should be rejected
let private iwsamWarnings = [ "--nowarn:3536" ; "--nowarn:3535" ]

[<TheoryForNETCOREAPP>]
[<InlineData("static abstract Name : string", "ITest.Name", "get_Name", 3, 9, 3, 19)>]
[<InlineData("static abstract Parse : string -> int", "ITest.Parse \"42\"", "Parse", 3, 9, 3, 25)>]
let ``Direct call to static abstract on interface produces error 3866`` (memberDef: string, call: string, memberName: string, l1, c1, l2, c2) =
Fsx $"type ITest =\n {memberDef}\nlet x = {call}"
|> withOptions iwsamWarnings |> typecheck |> shouldFail
|> withSingleDiagnostic (Error 3866, Line l1, Col c1, Line l2, Col c2, $"A static abstract non-virtual interface member should only be called via type parameter (for example: 'T.{memberName}).")

[<FactForNETCOREAPP>]
let ``SRTP call to static abstract via type parameter succeeds`` () =
Fsx "type IP = static abstract Parse : string -> int\ntype P() = interface IP with static member Parse s = int s\nlet inline p<'T when 'T :> IP> s = 'T.Parse s\nif p<P> \"42\" <> 42 then failwith \"fail\""
|> withOptions iwsamWarnings |> compileAndRun |> shouldSucceed

[<TheoryForNETCOREAPP>]
[<InlineData("op_CheckedAddition", true)>] // DIM - succeeds
[<InlineData("op_Addition", false)>] // Pure abstract - fails
let ``BCL IAdditionOperators static member`` (op: string, shouldPass: bool) =
let code = Fsx $"open System.Numerics\nlet _ = IAdditionOperators.{op} (5, 7)"
if shouldPass then code |> withOptions iwsamWarnings |> compileAndRun |> shouldSucceed |> ignore
else code |> withOptions iwsamWarnings |> typecheck |> shouldFail |> withErrorCode 3866 |> ignore

[<FactForNETCOREAPP>]
let ``Inherited static abstract without impl produces error 3866`` () =
Fsx "type IBase = static abstract Value : int\ntype IDerived = inherit IBase\nlet _ = IDerived.Value"
|> withOptions iwsamWarnings |> typecheck |> shouldFail |> withErrorCode 3866 |> ignore

[<FactForNETCOREAPP>]
let ``C# static abstract consumed by F# produces error 3866`` () =
let csLib = CSharp "namespace CsLib { public interface IP { static abstract string Get(); } }"
|> withCSharpLanguageVersion CSharpLanguageVersion.Preview |> withName "csLib"
FSharp "module T\nopen CsLib\nlet _ = IP.Get()" |> asExe |> withOptions iwsamWarnings |> withReferences [csLib]
|> compile |> shouldFail |> withErrorCode 3866 |> ignore

[<FactForNETCOREAPP>]
let ``C# static virtual DIM called on interface succeeds`` () =
let csLib = CSharp "namespace CsLib { public interface IP { static virtual string Get() => \"x\"; } }"
|> withCSharpLanguageVersion CSharpLanguageVersion.Preview |> withName "csLib"
FSharp "module T\nopen CsLib\nlet _ = IP.Get()" |> asExe |> withOptions iwsamWarnings |> withReferences [csLib]
|> compileAndRun |> shouldSucceed

Loading