private void GetEnumOperation(BinaryOperatorKind kind, TypeSymbol enumType, ArrayBuilder<BinaryOperatorSignature> operators)
        {
            Debug.Assert(enumType != null);

            if (!enumType.IsValidEnumType())
            {
                return;
            }

            var underlying = enumType.GetEnumUnderlyingType();
            Debug.Assert(underlying != null);
            Debug.Assert(underlying.SpecialType != SpecialType.None);

            var nullable = Compilation.GetSpecialType(SpecialType.System_Nullable_T);
            var nullableEnum = nullable.Construct(enumType);
            var nullableUnderlying = nullable.Construct(underlying);

            switch (kind)
            {
                case BinaryOperatorKind.Addition:
                    operators.Add(new BinaryOperatorSignature(BinaryOperatorKind.EnumAndUnderlyingAddition, enumType, underlying, enumType));
                    operators.Add(new BinaryOperatorSignature(BinaryOperatorKind.UnderlyingAndEnumAddition, underlying, enumType, enumType));
                    operators.Add(new BinaryOperatorSignature(BinaryOperatorKind.LiftedEnumAndUnderlyingAddition, nullableEnum, nullableUnderlying, nullableEnum));
                    operators.Add(new BinaryOperatorSignature(BinaryOperatorKind.LiftedUnderlyingAndEnumAddition, nullableUnderlying, nullableEnum, nullableEnum));
                    break;
                case BinaryOperatorKind.Subtraction:
                    operators.Add(new BinaryOperatorSignature(BinaryOperatorKind.EnumSubtraction, enumType, enumType, underlying));
                    operators.Add(new BinaryOperatorSignature(BinaryOperatorKind.EnumAndUnderlyingSubtraction, enumType, underlying, enumType));
                    operators.Add(new BinaryOperatorSignature(BinaryOperatorKind.LiftedEnumSubtraction, nullableEnum, nullableEnum, nullableUnderlying));
                    operators.Add(new BinaryOperatorSignature(BinaryOperatorKind.LiftedEnumAndUnderlyingSubtraction, nullableEnum, nullableUnderlying, nullableEnum));
                    break;
                case BinaryOperatorKind.Equal:
                case BinaryOperatorKind.NotEqual:
                case BinaryOperatorKind.GreaterThan:
                case BinaryOperatorKind.LessThan:
                case BinaryOperatorKind.GreaterThanOrEqual:
                case BinaryOperatorKind.LessThanOrEqual:
                    var boolean = Compilation.GetSpecialType(SpecialType.System_Boolean);
                    operators.Add(new BinaryOperatorSignature(kind | BinaryOperatorKind.Enum, enumType, enumType, boolean));
                    operators.Add(new BinaryOperatorSignature(kind | BinaryOperatorKind.Lifted | BinaryOperatorKind.Enum, nullableEnum, nullableEnum, boolean));
                    break;
                case BinaryOperatorKind.And:
                case BinaryOperatorKind.Or:
                case BinaryOperatorKind.Xor:
                    operators.Add(new BinaryOperatorSignature(kind | BinaryOperatorKind.Enum, enumType, enumType, enumType));
                    operators.Add(new BinaryOperatorSignature(kind | BinaryOperatorKind.Lifted | BinaryOperatorKind.Enum, nullableEnum, nullableEnum, nullableEnum));
                    break;
            }
        }
        private void GetEnumOperation(BinaryOperatorKind kind, TypeSymbol enumType, BoundExpression left, BoundExpression right, ArrayBuilder<BinaryOperatorSignature> operators)
        {
            Debug.Assert((object)enumType != null);
            AssertNotChecked(kind);

            if (!enumType.IsValidEnumType())
            {
                return;
            }

            var underlying = enumType.GetEnumUnderlyingType();
            Debug.Assert((object)underlying != null);
            Debug.Assert(underlying.SpecialType != SpecialType.None);

            NamedTypeSymbol nullableEnum = null;
            NamedTypeSymbol nullableUnderlying = null;

            // PERF: avoid instantiating nullable types in common simple cases.
            var leftType = left.Type;
            var rightType = right.Type;
            var simpleCase = leftType?.IsValueType == true &&
                             rightType?.IsValueType == true &&
                             leftType.IsNullableType() == false &&
                             rightType.IsNullableType() == false;

            if (!simpleCase)
            {
                var nullable = Compilation.GetSpecialType(SpecialType.System_Nullable_T);
                nullableEnum = nullable.Construct(enumType);
                nullableUnderlying = nullable.Construct(underlying);
            }

            switch (kind)
            {
                case BinaryOperatorKind.Addition:
                    operators.Add(new BinaryOperatorSignature(BinaryOperatorKind.EnumAndUnderlyingAddition, enumType, underlying, enumType));
                    operators.Add(new BinaryOperatorSignature(BinaryOperatorKind.UnderlyingAndEnumAddition, underlying, enumType, enumType));
                    if (!simpleCase)
                    {
                        operators.Add(new BinaryOperatorSignature(BinaryOperatorKind.LiftedEnumAndUnderlyingAddition, nullableEnum, nullableUnderlying, nullableEnum));
                        operators.Add(new BinaryOperatorSignature(BinaryOperatorKind.LiftedUnderlyingAndEnumAddition, nullableUnderlying, nullableEnum, nullableEnum));
                    }
                    break;
                case BinaryOperatorKind.Subtraction:
                    if (Strict)
                    {
                        operators.Add(new BinaryOperatorSignature(BinaryOperatorKind.EnumSubtraction, enumType, enumType, underlying));
                        operators.Add(new BinaryOperatorSignature(BinaryOperatorKind.EnumAndUnderlyingSubtraction, enumType, underlying, enumType));
                        if (!simpleCase)
                        {
                            operators.Add(new BinaryOperatorSignature(BinaryOperatorKind.LiftedEnumSubtraction, nullableEnum, nullableEnum, nullableUnderlying));
                            operators.Add(new BinaryOperatorSignature(BinaryOperatorKind.LiftedEnumAndUnderlyingSubtraction, nullableEnum, nullableUnderlying, nullableEnum));
                        }
                    }
                    else
                    {
                        // SPEC VIOLATION:
                        // The native compiler has bugs in overload resolution involving binary operator- for enums,
                        // which we duplicate by hardcoding Priority values among the operators. When present on both
                        // methods being compared during overload resolution, Priority values are used to decide between
                        // two candidates (instead of the usual language-specified rules).
                        bool isExactSubtraction = right.Type?.StrippedType() == underlying;
                        operators.Add(new BinaryOperatorSignature(BinaryOperatorKind.EnumSubtraction, enumType, enumType, underlying)
                        { Priority = 2 });
                        operators.Add(new BinaryOperatorSignature(BinaryOperatorKind.EnumAndUnderlyingSubtraction, enumType, underlying, enumType)
                        { Priority = isExactSubtraction ? 1 : 3 });
                        if (!simpleCase)
                        {
                            operators.Add(new BinaryOperatorSignature(BinaryOperatorKind.LiftedEnumSubtraction, nullableEnum, nullableEnum, nullableUnderlying)
                            { Priority = 12 });
                            operators.Add(new BinaryOperatorSignature(BinaryOperatorKind.LiftedEnumAndUnderlyingSubtraction, nullableEnum, nullableUnderlying, nullableEnum)
                            { Priority = isExactSubtraction ? 11 : 13 });
                        }

                        // Due to a bug, the native compiler allows "underlying - enum", so Roslyn does as well.
                        operators.Add(new BinaryOperatorSignature(BinaryOperatorKind.UnderlyingAndEnumSubtraction, underlying, enumType, enumType)
                        { Priority = 4 });
                        if (!simpleCase)
                        {
                            operators.Add(new BinaryOperatorSignature(BinaryOperatorKind.LiftedUnderlyingAndEnumSubtraction, nullableUnderlying, nullableEnum, nullableEnum)
                            { Priority = 14 });
                        }
                    }
                    break;
                case BinaryOperatorKind.Equal:
                case BinaryOperatorKind.NotEqual:
                case BinaryOperatorKind.GreaterThan:
                case BinaryOperatorKind.LessThan:
                case BinaryOperatorKind.GreaterThanOrEqual:
                case BinaryOperatorKind.LessThanOrEqual:
                    var boolean = Compilation.GetSpecialType(SpecialType.System_Boolean);
                    operators.Add(new BinaryOperatorSignature(kind | BinaryOperatorKind.Enum, enumType, enumType, boolean));
                    if (!simpleCase)
                    {
                        operators.Add(new BinaryOperatorSignature(kind | BinaryOperatorKind.Lifted | BinaryOperatorKind.Enum, nullableEnum, nullableEnum, boolean));
                    }
                    break;
                case BinaryOperatorKind.And:
                case BinaryOperatorKind.Or:
                case BinaryOperatorKind.Xor:
                    operators.Add(new BinaryOperatorSignature(kind | BinaryOperatorKind.Enum, enumType, enumType, enumType));
                    if (!simpleCase)
                    {
                        operators.Add(new BinaryOperatorSignature(kind | BinaryOperatorKind.Lifted | BinaryOperatorKind.Enum, nullableEnum, nullableEnum, nullableEnum));
                    }
                    break;
            }
        }
        private void GetEnumOperation(BinaryOperatorKind kind, TypeSymbol enumType, ArrayBuilder<BinaryOperatorSignature> operators)
        {
            Debug.Assert((object)enumType != null);
            AssertNotChecked(kind);

            if (!enumType.IsValidEnumType())
            {
                return;
            }

            var underlying = enumType.GetEnumUnderlyingType();
            Debug.Assert((object)underlying != null);
            Debug.Assert(underlying.SpecialType != SpecialType.None);

            var nullable = Compilation.GetSpecialType(SpecialType.System_Nullable_T);
            var nullableEnum = nullable.Construct(enumType);
            var nullableUnderlying = nullable.Construct(underlying);

            switch (kind)
            {
                case BinaryOperatorKind.Addition:
                    operators.Add(new BinaryOperatorSignature(BinaryOperatorKind.EnumAndUnderlyingAddition, enumType, underlying, enumType));
                    operators.Add(new BinaryOperatorSignature(BinaryOperatorKind.UnderlyingAndEnumAddition, underlying, enumType, enumType));
                    operators.Add(new BinaryOperatorSignature(BinaryOperatorKind.LiftedEnumAndUnderlyingAddition, nullableEnum, nullableUnderlying, nullableEnum));
                    operators.Add(new BinaryOperatorSignature(BinaryOperatorKind.LiftedUnderlyingAndEnumAddition, nullableUnderlying, nullableEnum, nullableEnum));
                    break;
                case BinaryOperatorKind.Subtraction:
                    operators.Add(new BinaryOperatorSignature(BinaryOperatorKind.EnumSubtraction, enumType, enumType, underlying));
                    operators.Add(new BinaryOperatorSignature(BinaryOperatorKind.EnumAndUnderlyingSubtraction, enumType, underlying, enumType));
                    operators.Add(new BinaryOperatorSignature(BinaryOperatorKind.LiftedEnumSubtraction, nullableEnum, nullableEnum, nullableUnderlying));
                    operators.Add(new BinaryOperatorSignature(BinaryOperatorKind.LiftedEnumAndUnderlyingSubtraction, nullableEnum, nullableUnderlying, nullableEnum));

                    // Due to a bug, the native compiler allows "underlying - enum", so Roslyn does as well.
                    // (In the native compiler codebase look at GetEnumBinOpSigs; the last call to method
                    // "ValidForEnumAndUnderlyingType" is wrong; it should be a call to "ValidForUnderlyingTypeAndEnum".
                    // The net result of the bug is that everything that is supposed to work is fine, and we accidentally
                    // allow underlying - enum because enum - underlying is valid.)

                    operators.Add(new BinaryOperatorSignature(BinaryOperatorKind.UnderlyingAndEnumSubtraction, underlying, enumType, enumType));
                    operators.Add(new BinaryOperatorSignature(BinaryOperatorKind.LiftedUnderlyingAndEnumSubtraction, nullableUnderlying, nullableEnum, nullableEnum));
                    break;
                case BinaryOperatorKind.Equal:
                case BinaryOperatorKind.NotEqual:
                case BinaryOperatorKind.GreaterThan:
                case BinaryOperatorKind.LessThan:
                case BinaryOperatorKind.GreaterThanOrEqual:
                case BinaryOperatorKind.LessThanOrEqual:
                    var boolean = Compilation.GetSpecialType(SpecialType.System_Boolean);
                    operators.Add(new BinaryOperatorSignature(kind | BinaryOperatorKind.Enum, enumType, enumType, boolean));
                    operators.Add(new BinaryOperatorSignature(kind | BinaryOperatorKind.Lifted | BinaryOperatorKind.Enum, nullableEnum, nullableEnum, boolean));
                    break;
                case BinaryOperatorKind.And:
                case BinaryOperatorKind.Or:
                case BinaryOperatorKind.Xor:
                    operators.Add(new BinaryOperatorSignature(kind | BinaryOperatorKind.Enum, enumType, enumType, enumType));
                    operators.Add(new BinaryOperatorSignature(kind | BinaryOperatorKind.Lifted | BinaryOperatorKind.Enum, nullableEnum, nullableEnum, nullableEnum));
                    break;
            }
        }