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
19.05k stars 4.04k forks source link

Allow `operator==(S, int)` to be lifted #25318

Open jcouv opened 6 years ago

jcouv commented 6 years ago

In PR https://github.com/dotnet/roslyn/pull/24913, I fixed the case where s doesn't have an operator== and I also verified the case where s has operator==(S, S). But while working on tuple equality, I thought of this scenario and it doesn't seem to work.

struct MyType
{
    int i;
    public MyType(int value)
    {
        i = value;
    }
    static void Main()
    {
        MyType? x = null;

        _ = x == 0;
    }
    public static bool operator==(MyType x, int y)
        => x.i == y;
    public static bool operator!=(MyType x, int y)
        => !(x == y);
    public override bool Equals(object o) => throw null;
    public override int GetHashCode() => throw null;
}
jcouv commented 6 years ago

@gafter It looks like this is a known spec violation. I'll go ahead and close by-design.

Here's a repro without default.

        private static LiftingResult UserDefinedBinaryOperatorCanBeLifted(TypeSymbol left, TypeSymbol right, TypeSymbol result, BinaryOperatorKind kind)
        {
            // SPEC: For the binary operators + - * / % & | ^ << >> a lifted form of the
            // SPEC: operator exists if the operand and result types are all non-nullable
            // SPEC: value types. The lifted form is constructed by adding a single ?
            // SPEC: modifier to each operand and result type. 
            //
            // SPEC: For the equality operators == != a lifted form of the operator exists
            // SPEC: if the operand types are both non-nullable value types and if the 
            // SPEC: result type is bool. The lifted form is constructed by adding
            // SPEC: a single ? modifier to each operand type.
            //
            // SPEC: For the relational operators > < >= <= a lifted form of the 
            // SPEC: operator exists if the operand types are both non-nullable value
            // SPEC: types and if the result type is bool. The lifted form is 
            // SPEC: constructed by adding a single ? modifier to each operand type.

            if (!left.IsValueType ||
                left.IsNullableType() ||
                !right.IsValueType ||
                right.IsNullableType())
            {
                return LiftingResult.NotLifted;
            }

            switch (kind)
            {
                case BinaryOperatorKind.Equal:
                case BinaryOperatorKind.NotEqual:
                    // Spec violation: can't lift unless the types match.
                    // The spec doesn't require this, but dev11 does and it reduces ambiguity in some cases.
                    if (left != right) return LiftingResult.NotLifted;
...
jcouv commented 6 years ago

Re-opening and re-tagging @gafter