Skip to content
Draft
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.100.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
### Fixed

* Fix missing "No implementation was given" error when F# class inherits from a C# class with `abstract override` members without providing an implementation. ([Issue #7776](https://github.com/dotnet/fsharp/issues/7776), [PR #19503](https://github.com/dotnet/fsharp/pull/19503))
* Fix CLIEvent properties to be correctly recognized as events: `IsEvent` returns `true` and `XmlDocSig` uses `E:` prefix instead of `P:`. ([Issue #10273](https://github.com/dotnet/fsharp/issues/10273), [PR #18584](https://github.com/dotnet/fsharp/pull/18584))
* Fix extra sequence point at the end of match expressions. ([Issue #12052](https://github.com/dotnet/fsharp/issues/12052), [PR #19278](https://github.com/dotnet/fsharp/pull/19278))
* Fix wrong sequence point range for `return`/`yield`/`return!`/`yield!` inside computation expressions. ([Issue #19248](https://github.com/dotnet/fsharp/issues/19248), [PR #19278](https://github.com/dotnet/fsharp/pull/19278))
Expand Down
9 changes: 6 additions & 3 deletions src/Compiler/Checking/InfoReader.fs
Original file line number Diff line number Diff line change
Expand Up @@ -571,7 +571,7 @@ type InfoReader(g: TcGlobals, amap: ImportMap) as this =
FilterItemsInSuperTypesBasedOnItemsInSubTypes nmf (fun item1 items -> not (items |> List.exists (fun item2 -> equivTest item1 item2))) itemLists

/// Filter the overrides of methods or properties, either keeping the overrides or keeping the dispatch slots.
static let FilterOverrides findFlag (isVirt:'a->bool, isNewSlot, isDefiniteOverride, isFinal, equivSigs, nmf:'a->string) items =
static let FilterOverrides findFlag (isVirt:'a->bool, isNewSlot, isDefiniteOverride, isFinal, isAbstract, equivSigs, nmf:'a->string) items =
let equivVirts x y = isVirt x && isVirt y && equivSigs x y
let filterDefiniteOverrides = List.filter(isDefiniteOverride >> not)

Expand Down Expand Up @@ -610,9 +610,10 @@ type InfoReader(g: TcGlobals, amap: ImportMap) as this =
// (a) not virtual
// (b) is a new slot or
// (c) not equivalent
// (d) is abstract (e.g. C# 'abstract override' re-abstracting a base virtual method)
// We keep virtual finals around for error detection later on
|> FilterItemsInSubTypesBasedOnItemsInSuperTypes nmf (fun newItem priorItem ->
(isVirt newItem && isFinal newItem) || not (isVirt newItem) || isNewSlot newItem || not (equivVirts newItem priorItem) )
(isVirt newItem && isFinal newItem) || not (isVirt newItem) || isNewSlot newItem || isAbstract newItem || not (equivVirts newItem priorItem) )

// Remove any abstract slots in supertypes that are (a) hidden by another newslot and (b) implemented
// We leave unimplemented ones around to give errors, e.g. for
Expand Down Expand Up @@ -649,6 +650,7 @@ type InfoReader(g: TcGlobals, amap: ImportMap) as this =
(fun minfo -> minfo.IsNewSlot),
(fun minfo -> minfo.IsDefiniteFSharpOverride),
(fun minfo -> minfo.IsFinal),
(fun minfo -> minfo.IsAbstract),
MethInfosEquivByNameAndSig EraseNone true g amap m,
(fun minfo -> minfo.LogicalName))

Expand All @@ -664,7 +666,8 @@ type InfoReader(g: TcGlobals, amap: ImportMap) as this =
((fun (pinfo: PropInfo) -> pinfo.IsVirtualProperty),
(fun pinfo -> pinfo.IsNewSlot),
(fun pinfo -> pinfo.IsDefiniteFSharpOverride),
(fun _ -> false),
(fun _ -> false), // isFinal
(fun _ -> false), // isAbstract
PropsGetterSetterEquiv (PropInfosEquivByNameAndSig EraseNone g amap m),
(fun pinfo -> pinfo.PropertyName))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -291,3 +291,130 @@ let x4 = new TestLib.B<string>()
|> compile
|> shouldFail
|> withErrorCode 759

// Regression tests for https://github.com/dotnet/fsharp/issues/7776

/// C# 'abstract override' re-abstracts a virtual method from a base class.
/// F# classes inheriting from such a class must provide an implementation.
let private csLibWithAbstractOverride =
CSharp """
namespace CSharpLib
{
public abstract class AbstractClass
{
public abstract override string ToString();
}

public abstract class AbstractClassWithCustomMethod
{
public virtual int GetValue() => 42;
}

public abstract class ReAbstractCustomMethod : AbstractClassWithCustomMethod
{
public abstract override int GetValue();
}
}
"""
|> withName "CSharpAbstractOverrideLib"

// https://github.com/dotnet/fsharp/issues/7776
[<Fact>]
let ``Abstract override ToString - missing implementation should error`` () =
FSharp """
module Test

open CSharpLib

type T() =
inherit AbstractClass()
"""
|> asLibrary
|> withReferences [csLibWithAbstractOverride]
|> compile
|> shouldFail
|> withErrorCode 365

// https://github.com/dotnet/fsharp/issues/7776
[<Fact>]
let ``Abstract override ToString - with implementation should succeed`` () =
FSharp """
module Test

open CSharpLib

type T() =
inherit AbstractClass()
override _.ToString() = "T"
"""
|> asLibrary
|> withReferences [csLibWithAbstractOverride]
|> compile
|> shouldSucceed

// https://github.com/dotnet/fsharp/issues/7776
[<Fact>]
let ``Abstract override custom method - missing implementation should error`` () =
FSharp """
module Test

open CSharpLib

type T() =
inherit ReAbstractCustomMethod()
"""
|> asLibrary
|> withReferences [csLibWithAbstractOverride]
|> compile
|> shouldFail
|> withErrorCode 365

// https://github.com/dotnet/fsharp/issues/7776
[<Fact>]
let ``Abstract override custom method - with implementation should succeed`` () =
FSharp """
module Test

open CSharpLib

type T() =
inherit ReAbstractCustomMethod()
override _.GetValue() = 100
"""
|> asLibrary
|> withReferences [csLibWithAbstractOverride]
|> compile
|> shouldSucceed

// https://github.com/dotnet/fsharp/issues/7776
[<Fact>]
let ``Abstract override - F# abstract subclass should be allowed`` () =
FSharp """
module Test

open CSharpLib

[<AbstractClass>]
type T() =
inherit AbstractClass()
"""
|> asLibrary
|> withReferences [csLibWithAbstractOverride]
|> compile
|> shouldSucceed

// https://github.com/dotnet/fsharp/issues/7776
[<Fact>]
let ``Abstract override ToString - object expression must implement`` () =
FSharp """
module Test

open CSharpLib

let x = { new AbstractClass() with
override _.ToString() = "obj" }
"""
|> asLibrary
|> withReferences [csLibWithAbstractOverride]
|> compile
|> shouldSucceed