dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
14.96k stars 4.65k forks source link

Support for 'in' parameters for dynamic binder #26614

Open OmarTawfik opened 6 years ago

OmarTawfik commented 6 years ago

Copied from: https://github.com/dotnet/roslyn/issues/28019

I have accidently stumbled upon an issue which might be a bug in the C# compiler. Apparently the dynamic keyword does not handle well methods / operators with in parameters. Here is a small sample program to reproduce the problem:

class Program
{
    static void Main(string[] args)
    {
        var vectors = new Vector[1000];
        var result = Sum(vectors);
    }

    static T Sum<T>(params T[] items)
    {
        T result = default;

        foreach (var item in items)
        {
            result += (dynamic)item;
        }

        return result;
    }

    readonly struct Vector
    {
        public readonly float X;
        public readonly float Y;
        public readonly float Z;
        public readonly float W;

        public Vector(float x, float y, float z, float w)
        {
            this.X = x;
            this.Y = y;
            this.Z = z;
            this.W = w;
        }

        public static Vector operator+(in Vector left, in Vector right)
        {
            return new Vector(left.X + right.X,
                                left.Y + right.Y,
                                left.Z + right.Z,
                                left.W + right.W);
        }
    }
}

The above program produces the following exception at runtime:

Unhandled Exception: Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: Operator '+=' cannot be applied to operands of type 'ConsoleApp.Program.Vector' and 'ConsoleApp.Program.Vector'
   at CallSite.Target(Closure , CallSite , Vector , Object )
   at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
   at ConsoleApp.Program.Sum[T](T[] items) in D:\Work\ConsoleApp\ConsoleApp\Program.cs:line 44
   at ConsoleApp.Program.Main(String[] args) in D:\Work\ConsoleApp\ConsoleApp\Program.cs:line 12

Removing the in keyword from the parameters in the operator overload fixes the exception. However, the program works correctly with the in parameters and a non-generic Sum method without the dynamic keyword:

static Vector Sum(params Vector[] vectors)
{
    Vector result = default;

    foreach (var vector in vectors)
    {
        result += vector;
    }

    return result;
}

Please, keep in mind the above example is artificial, just to demonstrate the problem. I am well aware there is a better Vector<T> implementation utilizing SIMD in System.Numerics.Vectors.

cc @vanka78bg

OmarTawfik commented 6 years ago

The binder needs some work to be able to support in parameters correctly. Same issue would happen with in extension methods, etc..