Unable to create a byref-like DU holding a ref type
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)
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)
How does a function like ToString() violate byref rules?
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);
}