fsharp icon indicating copy to clipboard operation
fsharp copied to clipboard

Unable to create a byref-like DU holding a ref type

Open lovelace08 opened this issue 8 months ago • 3 comments

Please provide a succinct description of the issue.

Try to create a byref-like discriminated union holding a byref type (ex. Span)

Provide the steps required to reproduce the problem:

open System
open System.Runtime.CompilerServices

[<Struct; IsByRefLike>]
type Record = { inner: ReadOnlySpan<byte> }

[<Struct; IsByRefLike>]
type Discrim = | Case of ReadOnlySpan<byte>

let record (x: ReadOnlySpan<byte>) = { inner = x } // Works!
let discrim (x: ReadOnlySpan<byte>) = Case(x) // Does not :(

Expected behavior

No errors

Actual behavior

error FS0418: The byref typed value 'x' cannot be used at this point

Known workarounds

None so far.

Related information

Provide any related information (optional):

  • Operating system
  • .NET Runtime kind (.NET Core, .NET Framework, Mono)
  • Editing Tools (e.g. Visual Studio Version, Visual Studio)

lovelace08 avatar Apr 27 '25 13:04 lovelace08

I looked into this, I had no idea this was ever supported.

The RFC FS-1053 https://github.com/fsharp/fslang-design/blob/main/FSharp-4.5/FS-1053-span.md uses the term "struct types" when mentioning features like IsByRefLike support. All the examples and test cases work with regular (non record, non DU) explicitly declared struct types.

Putting aside the need to make DUs work, I think this fits the need of the FS-1053 and could be treated like a bugfix.

There are however things which will not work, neither for DUs nor for records. One example is printing (ToString()) the type. Compilation will suceed, but there will be an error at runtime https://sharplab.io/#v2:DYLgZgzgNALiBOBXAdlAJiA1AHwPYAcBTZAAgGUBPCGQgWwFgAoA48qm2gOgCUUYBLWoU4BhXLXz9gheGRkA3fgGNCEJkwDaAHjIwkSmAG4SASQgAhCt0JgAMvwDWhAHwBdJjApES1pbnhoJAC8JADeJPzIyDIgPoQAhmgA8sjAFGT48chaAEYUNM4kAL5M0jAk8IR+ASQAFAAesdaJKWkZWbn5LgCUwWERUTJ99cUkAPRjJADq/g4QAIRM+PCRMGCkAEQApBAbdbWV1YG1zcmp6ZmkGtgAjACsiBTYrt3dnAAquLoryADmta8gA .

I assume the biggest amount of work will be checking and verifying if the codegen for lowering records/DUs into IL violates the byref rules or not. And if yes, what to do about those violations. (in the case of ToString(), it would have to be a different codegen for ToString, e.g. the default one from .NET)

T-Gro avatar Apr 28 '25 08:04 T-Gro

How does a function like ToString() violate byref rules?

lovelace08 avatar Apr 28 '25 08:04 lovelace08

The decompiled version for ToString looks like this - it passes this to a printing function, which can then box it (cast it to obj directly, or store it somewhere) for it's own internal working:

        [CompilerGenerated]
        public override string ToString()
        {
            return ExtraTopLevelOperators.PrintFormatToString(new PrintfFormat<FSharpFunc<Record, string>, Unit, string, string, Record>("%+A")).Invoke(this);
        }

T-Gro avatar Apr 28 '25 09:04 T-Gro