dotnet / vblang

The home for design of the Visual Basic .NET programming language and runtime library.
290 stars 66 forks source link

Unable to consume C# 7.2 stack-only types in VB 15.5 #297

Open Nukepayload2 opened 6 years ago

Nukepayload2 commented 6 years ago

If I use stack only type in my VB code, error BC30668 will block compilation. I'm using VS2017 15.6.7.

Steps to reproduce:

  1. Create a .NET Core VB Console project.
  2. Edit the .vbproj file, add <LangVersion>Latest</LangVersion> in <PropertyGroup>.
  3. Create a .NET Standard 2.0 C# class library.
  4. Create a new C# code file, and add public ref struct StackOnlyType {} in that .cs file.
  5. Upgrade the C# project to 7.2 .
  6. In the VB console app, add reference to that C# project.
  7. Add Dim errorType As StackOnlyType to Sub Main.
  8. Build the whole solution.

Expected behavior:

The code should compile, because I'm not trying to put a stack-only variable on the heap.

Actual behavior:

Error: BC30668 "Types with embedded references are not supported in this version of your compiler"

Nukepayload2 commented 6 years ago

They have confirmed that this behavior is by design. I don't understand why they are not intend to let stack-only structures consumable in VB when c# is able to define them. Maybe they thought ref structs are similar to unsafe types. So, in most cases, VB developers may not hurry to consume these types. But this assumption does not make sense, because ref structs are not unsafe codes. They can be defined or be used in C# 7.2 outside of the unsafe context, why can't we just consume them in VB?

Both System.Span(Of T) and System.ReadOnlySpan(Of T) are important new APIs for .NET developers who care about both performance and type safety. We should feel natural when using them, rather than seeing the annoying BC30668 compilation error.

AdamSpeight2008 commented 6 years ago

Just throwing out a possible syntax.

Sub M(ReadOnly ByRef parameter As T)
  Dim x As Readonly ByRef T = arg
End Sub

The first Readonly is new Parameter modifier. The second ReadOnly and ByRef as extending the type signature, akin to nullables.

Berrysoft commented 6 years ago

This code won't cause error:

Module Program
    Sub Main(args As String())
        Dim arr As Integer() = {1, 2, 3, 4, 5}
        Dim span = arr.AsSpan()
        Console.WriteLine(span(1))
    End Sub
End Module

It seems that the compiler only doesn't ignore the ObsoleteAttribute.

Nukepayload2 commented 6 years ago

@Berrysoft the VB compiler should verify that all ref structs are not stored on the heap, rather than simply ignore the ObsoleteAttribute. The following code should produce compile-time error, but the current behavior is compilation succeed without any warning.

' Depends on nuget package "System.Memory 4.5.0"
Module Program
    Dim span1 As Object
    Sub Main(args As String())
        Dim arr = New Byte() {1, 2, 3, 4, 5}
        span1 = arr.AsSpan ' No compilation error here. The expected error code is BC31394.
        Console.WriteLine("Hello World!")
    End Sub
End Module
Berrysoft commented 6 years ago

@Nukepayload2 Surely it should, though your code will cause a runtime exception: System.InvalidProgramException:“Cannot create boxed ByRef-like values.” It may be thrown by JIT.

And I find that this code will cause InvalidProgramException too:

Module Program
    Sub Main(args As String())
        Dim rls As RefLikeStruct
        Console.WriteLine(rls.ToString())
    End Sub
End Module

<System.Runtime.CompilerServices.IsByRefLike()>
Structure RefLikeStruct
    Public A As Integer
End Structure

Seems that RefLikeStruct is a real stack-only type!

gafter commented 6 years ago

Aside from the bugs recorded in https://github.com/dotnet/roslyn/issues/28558, this is by design. We'll take this as a feature request to support the use of ref structs in VB.

VBAndCs commented 3 years ago

2.5 years!

InteXX commented 3 years ago

@gafter

This issue threatens a bottleneck in VB.NET going forward, as more uses of ref structs are introduced into .NET 5 as it matures. I'm adding my voice to the feature request.

hamarb123 commented 2 years ago

I'd also like to add my support for byref like types in VB.NET @gafter. The whole point of byref like types is that they are a safe way to achieve something similar to pointers. Also, I'm aware that VB.NET isn't getting new features anymore, but I think it is reasonable to expect VB.NET to be usable with modern .NET and C# features, currently it is really difficult to do a lot of things and it's just a shame and so unnecessary honestly.

With .NET 7's byref fields, a lot of libraries will be making custom byref structs, many of which that will be public, which VB.NET will be able to use absolutely no api containing. This is exactly what happened with the unmanaged constraint, e.g. with ImageSharp, because it uses that constraint internally, suddenly almost none of the library can work in VB.NET anymore.

If VB.NET absolutely must not be able to write unsafe code, it should still be able to consume code that happens to be unsafe that's written in other languages as it always has been able to do (obviously other than constructors which take pointers), so it should be able to use spans since they're safe by design, and the unmanaged constraint so it can consume C# code even if it doesn't allow you to do anything additional in VB.NET.

That's my opinion anyway.

BerndPodhradsky commented 2 years ago

I agree that we need support for ref struct in Visual Basic .NET. I completely understand Microsoft's strategy to not develop Visual Basic .NET any further, but this is essentially making Visual Basic .NET development in .NET 7 (in .NET 6 there are workarounds) impossible. I just tried to upgrade an application we maintained in .NET 6 in Visual Basic .NET and I get hundreds of errors regarding ref struct not being supported by the compiler.

AnthonyDGreen commented 2 years ago

@BerndPodhradsky can you give some examples of ref struct instances you're trying to use? I understand ref structs being on the periphery but are they really a part of the common workflows. I know some of the JsonReader stuff is using them but there are non ref struct alternatives, right?

BerndPodhradsky commented 2 years ago

@AnthonyDGreen I use RSA.ImportPkcs8PrivateKey to create JWTs and if you look at the documentation (https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.rsa.importpkcs8privatekey?view=net-7.0) you can see that there's only one overload that requires a ReadOnlySpan. However, I cannot call AsSpan on an IEnumerable in .NET 7, because I immediately get error messages indicating that the Visual Basic .NET compiler does not support it.

AnthonyDGreen commented 2 years ago

I see. That is pretty silly.

BerndPodhradsky commented 2 years ago

@AnthonyDGreen Funnily enough, in .NET 6 I was able to call that function overload by calling the AsSpan extension method for IEnumerables - do you have any idea why that isn't allowed any longer in .NET 7?

hamarb123 commented 2 years ago

I'd like to add that I find it increasingly difficult to do anything in VB.NET these days particularly due to lack of byref-like structures support (I don't have specific examples on the top of my head), and am gradually moving code to C# as a result of this because there is lots of code I simply cannot write (it's a pain to have to move large amounts of existing code). It would be really, really good if VB.NET would support ref structs natively (at least for consumption).

I doubt VB.NET is in mind for many of the new .NET APIs, and therefore the situation will only get worse as there are likely to be many APIs that cannot be accessed on VB.NET due to lack of ref structs (and a similar situation for unmanaged for low-level libraries like image manipulation libraries e.g. ImageSharp).

Also, I've seen a lot of people saying the reason they shouldn't be supported is because they're 'unsafe', but the whole point of ref structs is that they're designed to be safe.

BerndPodhradsky commented 2 years ago

@hamarb123 To be honest I never really understood that argument in the first place; if stuff shouldn't be used because it's unsafe, by should/can it be used in C#? I fear we will have to move our code base to C# as well but IMHO code editor support for C# is in some cases worse than for Visual Basic .NET and it's a pain once you're used to VB (e.g. indented region content etc.).

hamarb123 commented 2 years ago

I use C# mostly these days, but the only reason I started using it was because I determined that MS doesn't want things to be possible in VB.NET (e.g. unsafe etc), so I started using C#, but I still have many large projects in VB, and VB not supporting many features makes it even more difficult to move the code into C# (because I have to write C# that will work with VB because the rest is still in VB, which also inevitably reduces performance compared to if I could just write the C# I want to write).

VBAndCs commented 2 years ago

@BerndPodhradsky Try mark the method that uses ref structs in VB with the <Obsolete> attr. If they closed this back door as they intended while ago, then you can just change VB compiler version in you project file to 16.9. I think this will make this workaround work.

VBAndCs commented 2 years ago

@hamarb123 VB.NET has other ways to the future like Mercury which supports .NET Framework and .NET core and other non MS frameworks and is cross platform, and ModVB which is Roslyn-based and VS.NET hosted and developed by @AnthonyDGreen. Look at the roadmap

BerndPodhradsky commented 2 years ago

@VBAndCs Unfortunately neither <Obsolete> nor changing the language version fixed the issue.

hamarb123 commented 2 years ago

Haden't heard of ModVB before, is it open source? Can you have ModVB and VB.NET working side-by-side?

VBAndCs commented 2 years ago

@hamarb123 It is a pre-release version. You can setup it as a VS extension, open a VB.NET project, then add a NuGet to make the project use ModVB instead VB.NET. All instruction in this post

VBAndCs commented 2 years ago

@BerndPodhradsky Create a dll that targets .NET 6.0, and add to it all code that uses the ref structs, then use it in you .NET 7 apps.