Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Operators.typeof<'T> does not seems work with IsByRefLike types #6607

Closed
zpodlovics opened this issue Apr 22, 2019 · 8 comments
Closed

Operators.typeof<'T> does not seems work with IsByRefLike types #6607

zpodlovics opened this issue Apr 22, 2019 · 8 comments

Comments

@zpodlovics
Copy link

It seems that typeof operator does not seems work with IsByRefLike types.

Repro steps

Provide the steps required to reproduce the problem

  1. Consider the following example program in C#
using System;

namespace typeofspan
{
    class Program
    {
        static void Main(string[] args)
        {
            var t = typeof(System.Span<byte>);
            Console.WriteLine($"Span.IsValueType: {t.IsValueType}");
        }
    }
}

typeofspan-csharp.zip

  1. Consider roughly the same program in F#
open System

[<EntryPoint>]
let main argv =
    let t = typeof<System.Span<byte>>
    printfn "Span.IsValueType: %b" (t.IsValueType)
    0 // return an integer exit code

typeofspan-fsharp.zip

Expected behavior

The typeof operator works with IsByRefLike types.

Actual behavior

error FS0412: A type instantiation involves a byref type. This is not permitted by the rules of Common IL. [/tmp/typeofspan/typeofspan.fsproj]

The typeof operator does not seems works with IsByRefLike types.

Known workarounds

None.

Related information

  • Operating system: Ubuntu 18.04
  • .NET Runtime, CoreCLR or Mono Version
ii  dotnet-host                                                 2.2.4-1                                                     amd64        Microsoft .NET Core Host - 2.2.4
ii  dotnet-hostfxr-2.0.7                                        2.0.7-1                                                     amd64        Microsoft .NET Core Host FX Resolver - 2.0.7 2.0.7
ii  dotnet-hostfxr-2.1                                          2.1.10-1                                                    amd64        Microsoft .NET Core Host FX Resolver - 2.1.10 2.1.10
ii  dotnet-hostfxr-2.2                                          2.2.4-1                                                     amd64        Microsoft .NET Core Host FX Resolver - 2.2.4 2.2.4
ii  dotnet-runtime-2.0.7                                        2.0.7-1                                                     amd64        Microsoft .NET Core Runtime - 2.0.7 Microsoft.NETCore.App 2.0.7
ii  dotnet-runtime-2.1                                          2.1.10-1                                                    amd64        Microsoft .NET Core Runtime - 2.1.10 Microsoft.NETCore.App 2.1.10
ii  dotnet-runtime-2.2                                          2.2.4-1                                                     amd64        Microsoft .NET Core Runtime - 2.2.4 Microsoft.NETCore.App 2.2.4
ii  dotnet-runtime-deps-2.1                                     2.1.10-1                                                    amd64        dotnet-runtime-deps-2.1 2.1.10
ii  dotnet-runtime-deps-2.2                                     2.2.4-1                                                     amd64        dotnet-runtime-deps-2.2 2.2.4
ii  dotnet-sdk-2.1                                              2.1.603-1                                                   amd64        Microsoft .NET Core SDK 2.1.603
ii  dotnet-sdk-2.2                                              2.2.203-1                                                   amd64        Microsoft .NET Core SDK 2.2.203

@cartermp
Copy link
Contributor

This is considered by design as typeof is a generic function, and parameterizing byref-like types is unsupported. This is an unfortunate consequence of how typeof is implemented.

@zpodlovics
Copy link
Author

zpodlovics commented Apr 22, 2019

@cartermp How am I supposed to have type information about IsByRefLike types in F# then? How the writing a helper method for every type typeof in C# solve the problem? How am we supposed to explain to the auditors the F# language choice pros when we have to use C# for these basic functionality? How the C# typeof operator could work without generic?

@cartermp
Copy link
Contributor

This scenario isn't supported right now. We may consider some kind of special case here in the future, but typeof as implemented is a generic function can you cannot use a byref-like struct as a generic parameter. This would imply heap allocation which contradicts the feature.

typeof in C# is not a generic function, it's a special construct that only accepts a namespace or type name. The two aren't very analagous.

@KevinRansom
Copy link
Member

@zpodlovics, I think this is something that needs to be discussed over in the language design forum. It

Here: https://github.com/fsharp/fslang-design.

@dsyme
Copy link
Contributor

dsyme commented Apr 23, 2019

@cartermp How am I supposed to have type information about IsByRefLike types in F# then? How the writing a helper method for every type typeof in C# solve the problem? How should we explain to the auditors the F# language choice pros when we have to use C# for these basic functionality? How the C# typeof operator could work without generic?

I'd recommend using typeof<int>.MakeByRefType() and similar for span

@zpodlovics
Copy link
Author

Found a workaround using type aliases. The IsByRefLike types still cannot be used for type generics as the type loading will fail with TypeLoadException. Please do NOT remove this functionality until different (non-generic?) operator provide the same instruction sequence and functionality for IsByRefLike types (not only for span but als for custom IsByRefLike types too) too. The number of generated IL / ASM instructions does matter for performance critical cases. Maybe inlined and NoDynamicInvocation marked functions could use IsByRefLike types as type parameters.

open System
open System.Runtime.CompilerServices

type SpanAlias<'T> = System.Span<'T>

type [<Struct;IsByRefLike>] S =
    val Value: int32
    new(v:int32) = { Value = v }

type SAlias = S

type ByRefGeneric<'T> = 
    val Value: 'T
    new(v:'T) = { Value=v }

[<EntryPoint>]
let main argv =
    //Note: this will cause the following error:
    //Unhandled Exception: System.TypeLoadException: The generic type 'ByRefGeneric`1' was used with an invalid instantiation in assembly 'typeofspan, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
    //let x = ByRefGeneric<SpanAlias<byte>>(Span<byte>())
    let t = typeof<SpanAlias<byte>>
    let t2 = typeof<SAlias>
    printfn "Span.IsValueType: %b" (t.IsValueType)
    printfn "S.IsValueType: %b" (t2.IsValueType)
    0 // return an integer exit code

This generate the same instruction sequence as C#:

Span typeof:

    IL_0007:  ldtoken    valuetype [System.Runtime]System.Span`1<uint8>
    IL_000c:  call       class [System.Runtime]System.Type [System.Runtime]System.Type::GetTypeFromHandle(valuetype [System.Runtime]System.RuntimeTypeHandle)

S typeof:

    IL_0012:  ldtoken    Program/S
    IL_0017:  call       class [System.Runtime]System.Type [System.Runtime]System.Type::GetTypeFromHandle(valuetype [System.Runtime]System.RuntimeTypeHandle)

C#

https://sharplab.io/#v2:C4LghgzgtgPgAgBgARwIwG4CwAoHcDMKATEgMJIDeOSNtN1tBKqAbCgCxICyYAlgHYAKNAgDaAXSRgATgHMIASkoM6qpADcZSYEgC82gJ4AHAKYB7AGbDUAOgDKRsPwA8AIwPATAPgVZsatTQATkEAEgAiBycbAEkIADUwABsAVxMAFWMTEEpgWITktMzTAF9w3zoVJBKcEqA===

@cartermp
Copy link
Contributor

@zpodlovics We have no plans to disable aliasing like that. There is a bug where you can use an alias to get around the inability to store byref-like types in reference types (#6133) but this doesn't affect your workaround.

@Tarmil
Copy link
Contributor

Tarmil commented Dec 13, 2021

I am hitting this limitation and I can't seem to make the alias trick work with the compiler from SDK 6.0.100. The following code:

module Test

type MySpan<'T> = System.Span<'T>

let x = typeof<MySpan<int>>

yields this error on the typeof line:

[FS0412] A type instantiation involves a byref type. This is not permitted by the rules of Common IL.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants