5

Why I cannot have generic unmanaged struct in F#? May be Cell<'T when 'T: unmanaged> is not unmanaged, then how I can fix that?

type FloatCell =
    struct
        val x: float
        val y: nativeptr<FloatCell>
    end
    
[<Struct>]
[<StructLayout(LayoutKind.Sequential)>]
type Cell<'T when 'T: unmanaged> =
    struct
        val x: 'T
        val y: nativeptr<Cell<'T>>
    end   

Gives

error FS0001: A generic construct requires that the type 'Cell<'T>' is an unmanaged type [E:\dzmitry\src\uncorefx\src\uncorefx\uncorefx.fsproj]

UPDATE:

C# has same.

   unsafe struct FloatCell
    {
        public float val;
        public FloatCell* next;
    }

    [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
    unsafe struct Cell<T> where T: unmanaged
    {
        public float val;
        public Cell<T>* next;
    }

With error:

error CS0208: Cannot take the address of, get the size of, or declare a pointer to a managed type ('Program.Cell')

I do not think it is managed.

UPDATE2:

I tried attributes. Did not help. I have used extension property for cast. Possible solution. But question why I cannot do that natively? Or I can do? Or should I raise C#/F# issue?

[<Struct>]
[<NativeCppClass>]
[<System.Runtime.CompilerServices.UnsafeValueType>]
[<StructLayout(LayoutKind.Sequential)>]
type Cell<'T when 'T: unmanaged> =
    struct
        val element: 'T
        val next:  voidptr
    end    

type Cell<'T when 'T: unmanaged> with
    member  x.Next = x.next |> NativePtr.ofVoidPtr<'T> 

UPDATE3:

I have tried to wrap up pointers, and got into the issue without pointers.

    public struct UnmanagedStruct
    {
    }

    public struct UnmanagedStructWithSpecifiedGenerics
    {
        public EmptyCell<float> cell;
    }

    public ref struct RefUnmanagedStruct
    {
        public EmptyCell<float> cell;
    }

    public  struct EmptyCell<T> where T : unmanaged
    {
    }

And then instantiate:

        var compiles1 = new UnmanagedStructWithSpecifiedGenerics();
        var compiles2 = new EmptyCell<UnmanagedStruct>();
        var CS8377_1 = new EmptyCell<EmptyCell<float>>();
        var CS8377_1 = new EmptyCell<UnmanagedStructWithSpecifiedGenerics>();
        var CS0306 = new EmptyCell<RefUnmanagedStruct>();

Leads to:

error CS8377: The type 'EmptyCell' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'EmptyCell'

error CS8377: The type 'UnmanagedStructWithSpecifiedGenerics' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'EmptyCell'

error CS0306: The type 'RefUnmanagedStruct' may not be used as a type argument

Wrong error message? Should I raise issue onto Roslyn compiler?

1 Answer 1

5

It appears to be by design, though I'm not sure of the reason for the limitation. Here's a quote from the F# spec:

5.2.9 Unmanaged Constraints

An unmanaged constraint has the following form: typar : unmanaged

During constraint solving (§14.5), the constraint type : unmanaged is met if type is unmanaged as specified below:

  • Types sbyte, byte, char, nativeint, unativeint, float32, float, int16, uint16, int32, uint32, int64, uint64, decimal are unmanaged.
  • Type nativeptr<type> is unmanaged.
  • A non-generic struct type whose fields are all unmanaged types is unmanaged.

Note non-generic struct types are explicitly mentioned in the last bullet.

1
  • I will try to compile pure IL class to see if this is limitation of C#/F# or of CLR. Commented Sep 8, 2018 at 14:36

Not the answer you're looking for? Browse other questions tagged or ask your own question.