dotnet / roslyn

The Roslyn .NET compiler provides C# and Visual Basic languages with rich code analysis APIs.
https://docs.microsoft.com/dotnet/csharp/roslyn-sdk/
MIT License
18.88k stars 4.01k forks source link

Erroneous CS0266 on 'byte = byte & byte' #74375

Closed aehancoc closed 1 month ago

aehancoc commented 1 month ago

Version Used:

Steps to Reproduce:

Observe this example.

Diagnostic Id:

CS0266: Cannot implicitly convert type 'int' to 'byte'. An explicit conversion exists (are you missing a cast?)

Expected Behavior:

Because System.Byte implements System.Numerics.IBitwiseOperators<byte,byte,byte> as seen here, this should compile without issue. This occurs even if using a constant, like byte b = a & (byte)2;.

Interestingly, this works fine in VB.NET (example) and in F# (example).

Actual Behavior:

Compilation error is raised, because for whatever reason the compiler elects to cast some part of this to an int. The easiest workaround (and what the VB.NET/F# examples decompile to) is byte b = (byte)(a & a);, but this feels clunky.

aehancoc commented 1 month ago

May be related to https://github.com/dotnet/roslyn/issues/42816

hez2010 commented 1 month ago

IBitwiseOperators<byte,byte,byte> are implemented explicitly for byte, those methods are not public: https://source.dot.net/#System.Private.CoreLib/src/libraries/System.Private.CoreLib/src/System/Byte.cs,422

If you want to call into IBitwiseOperators<byte,byte,byte>.op_And, you need to go through type parameter:

T And<T>(T l, T r) where T : IBitwiseOperators<T, T, T> => l & r;

byte a = 1, b = 2;
byte c = And(a, b);

Note that exposing those methods public on byte is considered as a breaking change, as existing code expect it to be casted to int.

aehancoc commented 1 month ago

I suppose that makes sense. The default behaviour is just a bit counter-intuitive imo. I'm assuming that it stems from the CIL's internal representation of integers as being at least 32 bits?

I'm not expecting anything to change, given the backwards compatibility requirements, so unless you want to reply then feel free to close this. (That said, I'm curious why the default integer promotion rules wouldn't be adequate if the methods were to be made public, but I'm sure there's plenty of edge cases I'm not considering.)

CyrusNajmabadi commented 1 month ago

Consider:

Foo(b1 & b2)
void Foo(byte b) { ... }
void Foo(int i) { ... }

This can't change meaning.