Inline methods break in types with self identifier with misleading error message
Repro steps
type Test() as _self =
member inline this.InlineMethod() = ()
Expected behavior
The above compiles or emits a useful error message.
Actual behavior
The following error is emitted The value 'InlineMethod' was marked inline but its implementation makes use of an internal or private function which is not sufficiently accessible
Related information .NET 9.0
type Test() as _self =
class end
type Test with
member inline this.InlineMethod() = ()
The above functions as a workaround
This is the same as https://github.com/dotnet/fsharp/issues/17899. There are a number of related issues, e.g., https://github.com/dotnet/fsharp/issues/8349, etc.
Explanation
It looks like the cause is that the self-identifier causes the compiler to emit an initialization check that uses an internal field (SharpLab):
type Test() as _self =
member this.InlineMethod() = ()
using System;
using System.Reflection;
using Microsoft.FSharp.Core;
[assembly: FSharpInterfaceDataVersion(2, 0, 0)]
[assembly: AssemblyVersion("0.0.0.0")]
[CompilationMapping(SourceConstructFlags.Module)]
public static class @_
{
[Serializable]
[CompilationMapping(SourceConstructFlags.ObjectType)]
public class Test
{
internal int init@1;
public Test()
{
FSharpRef<Test> fSharpRef = new FSharpRef<Test>(null);
base..ctor();
fSharpRef.contents = this;
init@1 = 1;
}
public void InlineMethod()
{
if (init@1 < 1)
{
LanguagePrimitives.IntrinsicFunctions.FailInit();
}
}
}
}
namespace <StartupCode$_>
{
internal static class $_
{
public static void main@()
{
}
}
}
This is done to support this behavior outlined in the spec:
Object References in Primary Constructors
For types that have a primary constructor, the name of the object parameter can be bound and used in the non-static function, value, and member definitions of the type definition as follows:
type X(a:int) as x = let mutable currentA = a let mutable currentB = 0 do x.B <- x.A + 3 member self.GetResult()= currentA + currentB member self.A with get() = currentA and set v = currentA <- v member self.B with get() = currentB and set v = currentB <- vDuring construction, no member on the type may be called before the last value or function definition in the type has completed; such a call results in an
InvalidOperationException. For example, the following code raises this exception:type C() as self = let f = (fun (x:C) -> x.F()) let y = f self do printfn "construct" member this.F() = printfn "hi, y = %A" y let r = new C() // raises InvalidOperationExceptionThe exception is raised because an attempt may be made to access the value of the field
ybefore initialization is complete.
Action?
At the very least, it does seem like the compiler should (and could?) emit a better error message when the internal/private value is itself compiler-generated and invisible to the end-user.