dotnet / fsharp

The F# compiler, F# core library, F# language service, and F# tooling integration for Visual Studio
https://dotnet.microsoft.com/languages/fsharp
MIT License
3.89k stars 782 forks source link

Support 'protected abstract' #12281

Open kostya9 opened 3 years ago

kostya9 commented 3 years ago

Repro steps

  1. Create new fsharp project
  2. Add "MarkdigExtensions.SyntaxHighlighting" package
  3. Add the following code
    
    open Markdig.Renderers
    open Markdig.Renderers.Html
    open Markdig.Syntax
    open MarkdigExtensions.SyntaxHighlighting

type SyntaxHighlightingRendererWrapper() = inherit CodeBlockRenderer() with let highlightingRenderer = HighlightedCodeBlockRenderer()

    override _.Write(renderer: HtmlRenderer, codeBlock: CodeBlock): unit =
        highlightingRenderer.Write(renderer, codeBlock)

ZIP archive of the reproduction:
[Repro_fsharp_accessibility.zip](https://github.com/dotnet/fsharp/files/7375873/Repro_fsharp_accessibility.zip)

**Expected behavior**

No compilation errors

**Actual behavior**

Error 

Program.fs(12, 13): [FS0629] Method 'Write' is not accessible from this code location


**Known workarounds**

Extract Write method into a function:
```f#
open Markdig.Renderers
open Markdig.Renderers.Html
open Markdig.Syntax
open MarkdigExtensions.SyntaxHighlighting

let render (highlightingRenderer: HighlightedCodeBlockRenderer) (renderer: HtmlRenderer) (codeBlock: CodeBlock) =
    highlightingRenderer.Write(renderer, codeBlock)

type SyntaxHighlightingRendererWrapper() =
    inherit CodeBlockRenderer() with
        let highlightingRenderer = HighlightedCodeBlockRenderer()

        override _.Write(renderer: HtmlRenderer, codeBlock: CodeBlock): unit =
            render highlightingRenderer renderer codeBlock

Related information


I can only guess that this has something to do with overload resolution. Type HighlightedCodeBlockRenderer inherits from MarkdownObjectRenderer that has a similar protected method:

protected abstract void Write(TRenderer renderer, TObject obj);
// TRenderer resolves into HtmlRenderer
// TObject resolves into CodeBlock
dsyme commented 3 years ago

That use of with is unusual. Could you try this?

type SyntaxHighlightingRendererWrapper() =
    inherit CodeBlockRenderer()
    let highlightingRenderer = HighlightedCodeBlockRenderer()

    override _.Write(renderer: HtmlRenderer, codeBlock: CodeBlock): unit =
        highlightingRenderer.Write(renderer, codeBlock)

I think the issue is related to protected - Could you link the exact definitions of the virtual Write method please?

kostya9 commented 3 years ago

Sure, tried with the syntax without with, the problem unfortunately persists.

Overload resolution

Did some additional investigation.

With the Write inlined, the overload taken is

override HighlightedCodeBlockRenderer.Write : renderer:HtmlRenderer * cb:CodeBlock -> unit

With the Write in a separate render function, the overload is:

MarkdownObjectRenderer.Write(renderer: RendererBase, obj: MarkdownObject) : unit

Seems like the overload resolution somehow changes depending on the context?

Method in inheritance hierarchy

Here are the exact definitions of the Write method in the inheritance hierarchy:

HighlightedCodeBlockRenderer

override __.Write(renderer : HtmlRenderer, cb : CodeBlock)

inherits from CodeBlockRenderer

protected override void Write(HtmlRenderer renderer, CodeBlock obj)

inherits from HtmlObjectRenderer<CodeBlock> inherits from MarkdownObjectRenderer<HtmlRenderer, TObject> where TObject : MarkdownObject

public virtual void Write(RendererBase renderer, MarkdownObject obj);
protected abstract void Write(TRenderer renderer, TObject obj);