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; } }