From 85edae87561e21e06929123f6f26cd40b9c21b2d Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Thu, 13 Feb 2020 13:31:11 -0800 Subject: [PATCH] Increment and decrement operators (#41380) --- .../Portable/Binder/Binder_Conversions.cs | 62 +- .../Semantics/Operators/OperatorKind.cs | 26 + .../Operators/OperatorKindExtensions.cs | 2 + .../Operators/UnaryOperatorEasyOut.cs | 4 +- .../Portable/Compilation/BuiltInOperators.cs | 26 + .../LocalRewriter_UnaryOperator.cs | 53 +- .../Portable/Symbols/TypeSymbolExtensions.cs | 4 +- .../Test/Semantic/Semantics/NativeIntTests.cs | 2459 +++++++++++++++-- .../Core/Portable/CodeGen/ILBuilderEmit.cs | 24 + src/Compilers/Core/Portable/ConstantValue.cs | 36 +- .../Core/Portable/ConstantValueSpecialized.cs | 48 + 11 files changed, 2524 insertions(+), 220 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs index 93fa8b5bf35..b3a404f6d95 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs @@ -1150,7 +1150,6 @@ private static object DoUncheckedConversion(SpecialType destinationType, Constan // The int is converted to float and stored internally as the double 214783648, even though the // fully precise int would fit into a double. - // PROTOTYPE: Test all cases. unchecked { switch (value.Discriminator) @@ -1168,6 +1167,8 @@ private static object DoUncheckedConversion(SpecialType destinationType, Constan case SpecialType.System_Int16: return (short)byteValue; case SpecialType.System_Int32: return (int)byteValue; case SpecialType.System_Int64: return (long)byteValue; + case SpecialType.System_IntPtr: return (int)byteValue; + case SpecialType.System_UIntPtr: return (uint)byteValue; case SpecialType.System_Single: case SpecialType.System_Double: return (double)byteValue; case SpecialType.System_Decimal: return (decimal)byteValue; @@ -1186,6 +1187,8 @@ private static object DoUncheckedConversion(SpecialType destinationType, Constan case SpecialType.System_Int16: return (short)charValue; case SpecialType.System_Int32: return (int)charValue; case SpecialType.System_Int64: return (long)charValue; + case SpecialType.System_IntPtr: return (int)charValue; + case SpecialType.System_UIntPtr: return (uint)charValue; case SpecialType.System_Single: case SpecialType.System_Double: return (double)charValue; case SpecialType.System_Decimal: return (decimal)charValue; @@ -1204,6 +1207,8 @@ private static object DoUncheckedConversion(SpecialType destinationType, Constan case SpecialType.System_Int16: return (short)uint16Value; case SpecialType.System_Int32: return (int)uint16Value; case SpecialType.System_Int64: return (long)uint16Value; + case SpecialType.System_IntPtr: return (int)uint16Value; + case SpecialType.System_UIntPtr: return (uint)uint16Value; case SpecialType.System_Single: case SpecialType.System_Double: return (double)uint16Value; case SpecialType.System_Decimal: return (decimal)uint16Value; @@ -1222,6 +1227,8 @@ private static object DoUncheckedConversion(SpecialType destinationType, Constan case SpecialType.System_Int16: return (short)uint32Value; case SpecialType.System_Int32: return (int)uint32Value; case SpecialType.System_Int64: return (long)uint32Value; + case SpecialType.System_IntPtr: return (int)uint32Value; + case SpecialType.System_UIntPtr: return (uint)uint32Value; case SpecialType.System_Single: return (double)(float)uint32Value; case SpecialType.System_Double: return (double)uint32Value; case SpecialType.System_Decimal: return (decimal)uint32Value; @@ -1240,11 +1247,32 @@ private static object DoUncheckedConversion(SpecialType destinationType, Constan case SpecialType.System_Int16: return (short)uint64Value; case SpecialType.System_Int32: return (int)uint64Value; case SpecialType.System_Int64: return (long)uint64Value; + case SpecialType.System_IntPtr: return (int)uint64Value; + case SpecialType.System_UIntPtr: return (uint)uint64Value; case SpecialType.System_Single: return (double)(float)uint64Value; case SpecialType.System_Double: return (double)uint64Value; case SpecialType.System_Decimal: return (decimal)uint64Value; default: throw ExceptionUtilities.UnexpectedValue(destinationType); } + case ConstantValueTypeDiscriminator.NUInt: + uint nuintValue = value.UInt32Value; + switch (destinationType) + { + case SpecialType.System_Byte: return (byte)nuintValue; + case SpecialType.System_Char: return (char)nuintValue; + case SpecialType.System_UInt16: return (ushort)nuintValue; + case SpecialType.System_UInt32: return (uint)nuintValue; + case SpecialType.System_UInt64: return (ulong)nuintValue; + case SpecialType.System_SByte: return (sbyte)nuintValue; + case SpecialType.System_Int16: return (short)nuintValue; + case SpecialType.System_Int32: return (int)nuintValue; + case SpecialType.System_Int64: return (long)nuintValue; + case SpecialType.System_IntPtr: return (int)nuintValue; + case SpecialType.System_Single: return (double)(float)nuintValue; + case SpecialType.System_Double: return (double)nuintValue; + case SpecialType.System_Decimal: return (decimal)nuintValue; + default: throw ExceptionUtilities.UnexpectedValue(destinationType); + } case ConstantValueTypeDiscriminator.SByte: sbyte sbyteValue = value.SByteValue; switch (destinationType) @@ -1258,6 +1286,8 @@ private static object DoUncheckedConversion(SpecialType destinationType, Constan case SpecialType.System_Int16: return (short)sbyteValue; case SpecialType.System_Int32: return (int)sbyteValue; case SpecialType.System_Int64: return (long)sbyteValue; + case SpecialType.System_IntPtr: return (int)sbyteValue; + case SpecialType.System_UIntPtr: return (uint)sbyteValue; case SpecialType.System_Single: case SpecialType.System_Double: return (double)sbyteValue; case SpecialType.System_Decimal: return (decimal)sbyteValue; @@ -1276,6 +1306,8 @@ private static object DoUncheckedConversion(SpecialType destinationType, Constan case SpecialType.System_Int16: return (short)int16Value; case SpecialType.System_Int32: return (int)int16Value; case SpecialType.System_Int64: return (long)int16Value; + case SpecialType.System_IntPtr: return (int)int16Value; + case SpecialType.System_UIntPtr: return (uint)int16Value; case SpecialType.System_Single: case SpecialType.System_Double: return (double)int16Value; case SpecialType.System_Decimal: return (decimal)int16Value; @@ -1314,11 +1346,33 @@ private static object DoUncheckedConversion(SpecialType destinationType, Constan case SpecialType.System_Int16: return (short)int64Value; case SpecialType.System_Int32: return (int)int64Value; case SpecialType.System_Int64: return (long)int64Value; + case SpecialType.System_IntPtr: return (int)int64Value; + case SpecialType.System_UIntPtr: return (uint)int64Value; case SpecialType.System_Single: return (double)(float)int64Value; case SpecialType.System_Double: return (double)int64Value; case SpecialType.System_Decimal: return (decimal)int64Value; default: throw ExceptionUtilities.UnexpectedValue(destinationType); } + case ConstantValueTypeDiscriminator.NInt: + int nintValue = value.Int32Value; + switch (destinationType) + { + case SpecialType.System_Byte: return (byte)nintValue; + case SpecialType.System_Char: return (char)nintValue; + case SpecialType.System_UInt16: return (ushort)nintValue; + case SpecialType.System_UInt32: return (uint)nintValue; + case SpecialType.System_UInt64: return (ulong)nintValue; + case SpecialType.System_SByte: return (sbyte)nintValue; + case SpecialType.System_Int16: return (short)nintValue; + case SpecialType.System_Int32: return (int)nintValue; + case SpecialType.System_Int64: return (long)nintValue; + case SpecialType.System_IntPtr: return (int)nintValue; + case SpecialType.System_UIntPtr: return (uint)nintValue; + case SpecialType.System_Single: return (double)(float)nintValue; + case SpecialType.System_Double: return (double)nintValue; + case SpecialType.System_Decimal: return (decimal)nintValue; + default: throw ExceptionUtilities.UnexpectedValue(destinationType); + } case ConstantValueTypeDiscriminator.Single: case ConstantValueTypeDiscriminator.Double: // When converting from a floating-point type to an integral type, if the checked conversion would @@ -1336,6 +1390,8 @@ private static object DoUncheckedConversion(SpecialType destinationType, Constan case SpecialType.System_Int16: return (short)doubleValue; case SpecialType.System_Int32: return (int)doubleValue; case SpecialType.System_Int64: return (long)doubleValue; + case SpecialType.System_IntPtr: return (int)doubleValue; + case SpecialType.System_UIntPtr: return (uint)doubleValue; case SpecialType.System_Single: return (double)(float)doubleValue; case SpecialType.System_Double: return (double)doubleValue; case SpecialType.System_Decimal: return (value.Discriminator == ConstantValueTypeDiscriminator.Single) ? (decimal)(float)doubleValue : (decimal)doubleValue; @@ -1354,6 +1410,8 @@ private static object DoUncheckedConversion(SpecialType destinationType, Constan case SpecialType.System_Int16: return (short)decimalValue; case SpecialType.System_Int32: return (int)decimalValue; case SpecialType.System_Int64: return (long)decimalValue; + case SpecialType.System_IntPtr: return (int)decimalValue; + case SpecialType.System_UIntPtr: return (uint)decimalValue; case SpecialType.System_Single: return (double)(float)decimalValue; case SpecialType.System_Double: return (double)decimalValue; case SpecialType.System_Decimal: return (decimal)decimalValue; @@ -1436,11 +1494,13 @@ private static object CanonicalizeConstant(ConstantValue value) case ConstantValueTypeDiscriminator.Int16: return (decimal)value.Int16Value; case ConstantValueTypeDiscriminator.Int32: return (decimal)value.Int32Value; case ConstantValueTypeDiscriminator.Int64: return (decimal)value.Int64Value; + case ConstantValueTypeDiscriminator.NInt: return (decimal)value.Int32Value; case ConstantValueTypeDiscriminator.Byte: return (decimal)value.ByteValue; case ConstantValueTypeDiscriminator.Char: return (decimal)value.CharValue; case ConstantValueTypeDiscriminator.UInt16: return (decimal)value.UInt16Value; case ConstantValueTypeDiscriminator.UInt32: return (decimal)value.UInt32Value; case ConstantValueTypeDiscriminator.UInt64: return (decimal)value.UInt64Value; + case ConstantValueTypeDiscriminator.NUInt: return (decimal)value.UInt32Value; case ConstantValueTypeDiscriminator.Single: case ConstantValueTypeDiscriminator.Double: return value.DoubleValue; case ConstantValueTypeDiscriminator.Decimal: return value.DecimalValue; diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/OperatorKind.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/OperatorKind.cs index 2cb836aac53..fe023cfbdd0 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/OperatorKind.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/OperatorKind.cs @@ -76,6 +76,8 @@ internal enum UnaryOperatorKind UIntPostfixIncrement = UInt | PostfixIncrement, LongPostfixIncrement = Long | PostfixIncrement, ULongPostfixIncrement = ULong | PostfixIncrement, + NIntPostfixIncrement = NInt | PostfixIncrement, + NUIntPostfixIncrement = NUInt | PostfixIncrement, CharPostfixIncrement = Char | PostfixIncrement, FloatPostfixIncrement = Float | PostfixIncrement, DoublePostfixIncrement = Double | PostfixIncrement, @@ -90,6 +92,8 @@ internal enum UnaryOperatorKind LiftedUIntPostfixIncrement = Lifted | UInt | PostfixIncrement, LiftedLongPostfixIncrement = Lifted | Long | PostfixIncrement, LiftedULongPostfixIncrement = Lifted | ULong | PostfixIncrement, + LiftedNIntPostfixIncrement = Lifted | NInt | PostfixIncrement, + LiftedNUIntPostfixIncrement = Lifted | NUInt | PostfixIncrement, LiftedCharPostfixIncrement = Lifted | Char | PostfixIncrement, LiftedFloatPostfixIncrement = Lifted | Float | PostfixIncrement, LiftedDoublePostfixIncrement = Lifted | Double | PostfixIncrement, @@ -107,6 +111,8 @@ internal enum UnaryOperatorKind UIntPrefixIncrement = UInt | PrefixIncrement, LongPrefixIncrement = Long | PrefixIncrement, ULongPrefixIncrement = ULong | PrefixIncrement, + NIntPrefixIncrement = NInt | PrefixIncrement, + NUIntPrefixIncrement = NUInt | PrefixIncrement, CharPrefixIncrement = Char | PrefixIncrement, FloatPrefixIncrement = Float | PrefixIncrement, DoublePrefixIncrement = Double | PrefixIncrement, @@ -121,6 +127,8 @@ internal enum UnaryOperatorKind LiftedUIntPrefixIncrement = Lifted | UInt | PrefixIncrement, LiftedLongPrefixIncrement = Lifted | Long | PrefixIncrement, LiftedULongPrefixIncrement = Lifted | ULong | PrefixIncrement, + LiftedNIntPrefixIncrement = Lifted | NInt | PrefixIncrement, + LiftedNUIntPrefixIncrement = Lifted | NUInt | PrefixIncrement, LiftedCharPrefixIncrement = Lifted | Char | PrefixIncrement, LiftedFloatPrefixIncrement = Lifted | Float | PrefixIncrement, LiftedDoublePrefixIncrement = Lifted | Double | PrefixIncrement, @@ -138,6 +146,8 @@ internal enum UnaryOperatorKind UIntPostfixDecrement = UInt | PostfixDecrement, LongPostfixDecrement = Long | PostfixDecrement, ULongPostfixDecrement = ULong | PostfixDecrement, + NIntPostfixDecrement = NInt | PostfixDecrement, + NUIntPostfixDecrement = NUInt | PostfixDecrement, CharPostfixDecrement = Char | PostfixDecrement, FloatPostfixDecrement = Float | PostfixDecrement, DoublePostfixDecrement = Double | PostfixDecrement, @@ -152,6 +162,8 @@ internal enum UnaryOperatorKind LiftedUIntPostfixDecrement = Lifted | UInt | PostfixDecrement, LiftedLongPostfixDecrement = Lifted | Long | PostfixDecrement, LiftedULongPostfixDecrement = Lifted | ULong | PostfixDecrement, + LiftedNIntPostfixDecrement = Lifted | NInt | PostfixDecrement, + LiftedNUIntPostfixDecrement = Lifted | NUInt | PostfixDecrement, LiftedCharPostfixDecrement = Lifted | Char | PostfixDecrement, LiftedFloatPostfixDecrement = Lifted | Float | PostfixDecrement, LiftedDoublePostfixDecrement = Lifted | Double | PostfixDecrement, @@ -169,6 +181,8 @@ internal enum UnaryOperatorKind UIntPrefixDecrement = UInt | PrefixDecrement, LongPrefixDecrement = Long | PrefixDecrement, ULongPrefixDecrement = ULong | PrefixDecrement, + NIntPrefixDecrement = NInt | PrefixDecrement, + NUIntPrefixDecrement = NUInt | PrefixDecrement, CharPrefixDecrement = Char | PrefixDecrement, FloatPrefixDecrement = Float | PrefixDecrement, DoublePrefixDecrement = Double | PrefixDecrement, @@ -183,6 +197,8 @@ internal enum UnaryOperatorKind LiftedUIntPrefixDecrement = Lifted | UInt | PrefixDecrement, LiftedLongPrefixDecrement = Lifted | Long | PrefixDecrement, LiftedULongPrefixDecrement = Lifted | ULong | PrefixDecrement, + LiftedNIntPrefixDecrement = Lifted | NInt | PrefixDecrement, + LiftedNUIntPrefixDecrement = Lifted | NUInt | PrefixDecrement, LiftedCharPrefixDecrement = Lifted | Char | PrefixDecrement, LiftedFloatPrefixDecrement = Lifted | Float | PrefixDecrement, LiftedDoublePrefixDecrement = Lifted | Double | PrefixDecrement, @@ -196,6 +212,8 @@ internal enum UnaryOperatorKind UIntUnaryPlus = UInt | UnaryPlus, LongUnaryPlus = Long | UnaryPlus, ULongUnaryPlus = ULong | UnaryPlus, + NIntUnaryPlus = NInt | UnaryPlus, + NUIntUnaryPlus = NUInt | UnaryPlus, FloatUnaryPlus = Float | UnaryPlus, DoubleUnaryPlus = Double | UnaryPlus, DecimalUnaryPlus = Decimal | UnaryPlus, @@ -204,6 +222,8 @@ internal enum UnaryOperatorKind LiftedUIntUnaryPlus = Lifted | UInt | UnaryPlus, LiftedLongUnaryPlus = Lifted | Long | UnaryPlus, LiftedULongUnaryPlus = Lifted | ULong | UnaryPlus, + LiftedNIntUnaryPlus = Lifted | NInt | UnaryPlus, + LiftedNUIntUnaryPlus = Lifted | NUInt | UnaryPlus, LiftedFloatUnaryPlus = Lifted | Float | UnaryPlus, LiftedDoubleUnaryPlus = Lifted | Double | UnaryPlus, LiftedDecimalUnaryPlus = Lifted | Decimal | UnaryPlus, @@ -212,12 +232,14 @@ internal enum UnaryOperatorKind IntUnaryMinus = Int | UnaryMinus, LongUnaryMinus = Long | UnaryMinus, + NIntUnaryMinus = NInt | UnaryMinus, FloatUnaryMinus = Float | UnaryMinus, DoubleUnaryMinus = Double | UnaryMinus, DecimalUnaryMinus = Decimal | UnaryMinus, UserDefinedUnaryMinus = UserDefined | UnaryMinus, LiftedIntUnaryMinus = Lifted | Int | UnaryMinus, LiftedLongUnaryMinus = Lifted | Long | UnaryMinus, + LiftedNIntUnaryMinus = Lifted | NInt | UnaryMinus, LiftedFloatUnaryMinus = Lifted | Float | UnaryMinus, LiftedDoubleUnaryMinus = Lifted | Double | UnaryMinus, LiftedDecimalUnaryMinus = Lifted | Decimal | UnaryMinus, @@ -234,12 +256,16 @@ internal enum UnaryOperatorKind UIntBitwiseComplement = UInt | BitwiseComplement, LongBitwiseComplement = Long | BitwiseComplement, ULongBitwiseComplement = ULong | BitwiseComplement, + NIntBitwiseComplement = NInt | BitwiseComplement, + NUIntBitwiseComplement = NUInt | BitwiseComplement, EnumBitwiseComplement = Enum | BitwiseComplement, UserDefinedBitwiseComplement = UserDefined | BitwiseComplement, LiftedIntBitwiseComplement = Lifted | Int | BitwiseComplement, LiftedUIntBitwiseComplement = Lifted | UInt | BitwiseComplement, LiftedLongBitwiseComplement = Lifted | Long | BitwiseComplement, LiftedULongBitwiseComplement = Lifted | ULong | BitwiseComplement, + LiftedNIntBitwiseComplement = Lifted | NInt | BitwiseComplement, + LiftedNUIntBitwiseComplement = Lifted | NUInt | BitwiseComplement, LiftedEnumBitwiseComplement = Lifted | Enum | BitwiseComplement, LiftedUserDefinedBitwiseComplement = Lifted | UserDefined | BitwiseComplement, DynamicBitwiseComplement = Dynamic | BitwiseComplement, diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/OperatorKindExtensions.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/OperatorKindExtensions.cs index 0d72eb4574d..444f8321e83 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/OperatorKindExtensions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/OperatorKindExtensions.cs @@ -104,6 +104,8 @@ public static bool IsIntegral(this UnaryOperatorKind kind) case UnaryOperatorKind.UInt: case UnaryOperatorKind.Long: case UnaryOperatorKind.ULong: + case UnaryOperatorKind.NInt: + case UnaryOperatorKind.NUInt: case UnaryOperatorKind.Char: case UnaryOperatorKind.Enum: case UnaryOperatorKind.Pointer: diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorEasyOut.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorEasyOut.cs index 753dc5a4f93..cc6e000cca6 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorEasyOut.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorEasyOut.cs @@ -46,8 +46,8 @@ internal static class UnopEasyOut private static readonly UnaryOperatorKind[] s_increment = //obj str bool chr i08 i16 i32 i64 u08 u16 u32 u64 nint nuint r32 r64 dec - { ERR, ERR, ERR, CHR, I08, I16, I32, I64, U08, U16, U32, U64, ERR, ERR, R32, R64, DEC, - /* lifted */ ERR, LCHR, LI08, LI16, LI32, LI64, LU08, LU16, LU32, LU64, ERR, ERR, LR32, LR64, LDEC }; + { ERR, ERR, ERR, CHR, I08, I16, I32, I64, U08, U16, U32, U64, NIN, NUI, R32, R64, DEC, + /* lifted */ ERR, LCHR, LI08, LI16, LI32, LI64, LU08, LU16, LU32, LU64, LNI, LNU, LR32, LR64, LDEC }; private static readonly UnaryOperatorKind[] s_plus = //obj str bool chr i08 i16 i32 i64 u08 u16 u32 u64 nint nuint r32 r64 dec diff --git a/src/Compilers/CSharp/Portable/Compilation/BuiltInOperators.cs b/src/Compilers/CSharp/Portable/Compilation/BuiltInOperators.cs index 71461e1fd4d..d55c1c88baa 100644 --- a/src/Compilers/CSharp/Portable/Compilation/BuiltInOperators.cs +++ b/src/Compilers/CSharp/Portable/Compilation/BuiltInOperators.cs @@ -58,6 +58,8 @@ internal void GetSimpleBuiltInOperators(UnaryOperatorKind kind, ArrayBuilder.Empty, diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_UnaryOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_UnaryOperator.cs index 6e921b39d65..161b191ddee 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_UnaryOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_UnaryOperator.cs @@ -687,16 +687,15 @@ private BoundExpression MakeBuiltInIncrementOperator(BoundIncrementOperator node BinaryOperatorKind binaryOperatorKind = GetCorrespondingBinaryOperator(node); binaryOperatorKind |= IsIncrement(node) ? BinaryOperatorKind.Addition : BinaryOperatorKind.Subtraction; + // The input/output type of the binary operand. "int" in the example. // The "1" in the example above. - ConstantValue constantOne = GetConstantOneForBinOp(binaryOperatorKind); + (TypeSymbol binaryOperandType, ConstantValue constantOne) = GetConstantOneForIncrement(_compilation, binaryOperatorKind); Debug.Assert(constantOne != null); Debug.Assert(constantOne.SpecialType != SpecialType.None); + Debug.Assert(binaryOperandType.SpecialType != SpecialType.None); Debug.Assert(binaryOperatorKind.OperandTypes() != 0); - // The input/output type of the binary operand. "int" in the example. - TypeSymbol binaryOperandType = _compilation.GetSpecialType(constantOne.SpecialType); - // 1 BoundExpression boundOne = MakeLiteral( syntax: node.Syntax, @@ -900,6 +899,12 @@ private TypeSymbol GetUnaryOperatorType(BoundIncrementOperator node) case UnaryOperatorKind.ULong: specialType = SpecialType.System_UInt64; break; + case UnaryOperatorKind.NInt: + specialType = SpecialType.System_IntPtr; + break; + case UnaryOperatorKind.NUInt: + specialType = SpecialType.System_UIntPtr; + break; case UnaryOperatorKind.Float: specialType = SpecialType.System_Single; break; @@ -955,6 +960,12 @@ private static BinaryOperatorKind GetCorrespondingBinaryOperator(BoundIncrementO case UnaryOperatorKind.ULong: result = BinaryOperatorKind.ULong; break; + case UnaryOperatorKind.NInt: + result = BinaryOperatorKind.NInt; + break; + case UnaryOperatorKind.NUInt: + result = BinaryOperatorKind.NUInt; + break; case UnaryOperatorKind.Float: result = BinaryOperatorKind.Float; break; @@ -1015,6 +1026,8 @@ private static BinaryOperatorKind GetCorrespondingBinaryOperator(BoundIncrementO case BinaryOperatorKind.Int: case BinaryOperatorKind.ULong: case BinaryOperatorKind.Long: + case BinaryOperatorKind.NUInt: + case BinaryOperatorKind.NInt: case BinaryOperatorKind.PointerAndInt: result |= (BinaryOperatorKind)unaryOperatorKind.OverflowChecks(); break; @@ -1028,29 +1041,45 @@ private static BinaryOperatorKind GetCorrespondingBinaryOperator(BoundIncrementO return result; } - private static ConstantValue GetConstantOneForBinOp( + private static (TypeSymbol, ConstantValue) GetConstantOneForIncrement( + CSharpCompilation compilation, BinaryOperatorKind binaryOperatorKind) { + ConstantValue constantOne; switch (binaryOperatorKind.OperandTypes()) { case BinaryOperatorKind.PointerAndInt: case BinaryOperatorKind.Int: - return ConstantValue.Create(1); + constantOne = ConstantValue.Create(1); + break; case BinaryOperatorKind.UInt: - return ConstantValue.Create(1U); + constantOne = ConstantValue.Create(1U); + break; case BinaryOperatorKind.Long: - return ConstantValue.Create(1L); + constantOne = ConstantValue.Create(1L); + break; case BinaryOperatorKind.ULong: - return ConstantValue.Create(1LU); + constantOne = ConstantValue.Create(1LU); + break; + case BinaryOperatorKind.NInt: + constantOne = ConstantValue.Create(1); + return (compilation.GetSpecialType(SpecialType.System_IntPtr).AsNativeInt(true), constantOne); + case BinaryOperatorKind.NUInt: + constantOne = ConstantValue.Create(1U); + return (compilation.GetSpecialType(SpecialType.System_UIntPtr).AsNativeInt(true), constantOne); case BinaryOperatorKind.Float: - return ConstantValue.Create(1f); + constantOne = ConstantValue.Create(1f); + break; case BinaryOperatorKind.Double: - return ConstantValue.Create(1.0); + constantOne = ConstantValue.Create(1.0); + break; case BinaryOperatorKind.Decimal: - return ConstantValue.Create(1m); + constantOne = ConstantValue.Create(1m); + break; default: throw ExceptionUtilities.UnexpectedValue(binaryOperatorKind.OperandTypes()); } + return (compilation.GetSpecialType(constantOne.SpecialType), constantOne); } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs index bb36eb9bc05..a01c97bdc93 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs @@ -41,7 +41,7 @@ public static bool CanBeConst(this TypeSymbol typeSymbol) { RoslynDebug.Assert((object)typeSymbol != null); - return typeSymbol.IsReferenceType || typeSymbol.IsEnumType() || typeSymbol.SpecialType.CanBeConst(); + return typeSymbol.IsReferenceType || typeSymbol.IsEnumType() || typeSymbol.SpecialType.CanBeConst() || (typeSymbol as NamedTypeSymbol)?.IsNativeInt == true; } /// @@ -476,6 +476,8 @@ public static MethodSymbol DelegateInvokeMethod(this TypeSymbol type) case SpecialType.System_UInt32: case SpecialType.System_Int64: case SpecialType.System_UInt64: + case SpecialType.System_IntPtr when ((NamedTypeSymbol)type).IsNativeInt: + case SpecialType.System_UIntPtr when ((NamedTypeSymbol)type).IsNativeInt: case SpecialType.System_Char: case SpecialType.System_Boolean: case SpecialType.System_Single: diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NativeIntTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NativeIntTests.cs index 90c4d88659f..d3130ba0d3f 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NativeIntTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NativeIntTests.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -548,12 +549,48 @@ public void SizeOf_04() Diagnostic(ErrorCode.ERR_NotConstantExpression, "sizeof(nuint)").WithArguments("Program.D").WithLocation(6, 19)); } + // PROTOTYPE: PEVerify: "[ : MyInt::ToString][mdToken=0x6000005][offset 0x00000001] Cannot change initonly field outside its .ctor." + // CodeGenerator.EmitCallExpression() is not expecting a System.ValueType without an overload of ToString(). + [Fact(Skip = "PEVerify failure")] + public void ReadOnlyField_VirtualMethods() + { + string source = +@"class MyInt +{ + private readonly nint _i; + internal MyInt(nint i) => _i = i; + public override string ToString() => _i.ToString(); +} +class Program +{ + static void Main() + { + var m = new MyInt(42); + System.Console.WriteLine(m); + } +}"; + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.RegularPreview); + var verifier = CompileAndVerify(comp, expectedOutput: "42"); + verifier.VerifyIL("MyInt.ToString", +@"{ + // Code size 15 (0xf) + .maxstack 1 + .locals init (System.IntPtr V_0) + IL_0000: ldarg.0 + IL_0001: ldfld ""System.IntPtr MyInt._i"" + IL_0006: stloc.0 + IL_0007: ldloca.s V_0 + IL_0009: call ""string System.IntPtr.ToString()"" + IL_000e: ret +}"); + } + /// - /// Verify there is exactly one built in operator for { nint, nuint, nint?, nuint? } + /// Verify there is the number of built in operators for { nint, nuint, nint?, nuint? } /// for each operator kind. /// [Fact] - public void BinaryOperators_BuiltInOperators() + public void BuiltInOperators() { var source = ""; @@ -567,7 +604,18 @@ public void BinaryOperators_BuiltInOperators() static void verifyOperators(CSharpCompilation comp) { - var operatorKinds = new[] + var unaryOperators = new[] + { + UnaryOperatorKind.PostfixIncrement, + UnaryOperatorKind.PostfixDecrement, + UnaryOperatorKind.PrefixIncrement, + UnaryOperatorKind.PrefixDecrement, + UnaryOperatorKind.UnaryPlus, + UnaryOperatorKind.UnaryMinus, + UnaryOperatorKind.BitwiseComplement, + }; + + var binaryOperators = new[] { BinaryOperatorKind.Addition, BinaryOperatorKind.Subtraction, @@ -587,15 +635,29 @@ static void verifyOperators(CSharpCompilation comp) BinaryOperatorKind.Xor, }; - foreach (var operatorKind in operatorKinds) + foreach (var operatorKind in unaryOperators) + { + var builder = ArrayBuilder.GetInstance(); + comp.builtInOperators.GetSimpleBuiltInOperators(operatorKind, builder); + var operators = builder.ToImmutableAndFree(); + int expectedUnsigned = (operatorKind == UnaryOperatorKind.UnaryMinus) ? 0 : 1; + verifyOperators(operators, (op, signed) => isNativeInt(op.OperandType, signed), 1, expectedUnsigned); + verifyOperators(operators, (op, signed) => isNullableNativeInt(op.OperandType, signed), 1, expectedUnsigned); + } + + foreach (var operatorKind in binaryOperators) { var builder = ArrayBuilder.GetInstance(); comp.builtInOperators.GetSimpleBuiltInOperators(operatorKind, builder); var operators = builder.ToImmutableAndFree(); - _ = operators.Single(op => isNativeInt(op.LeftType, signed: true)); - _ = operators.Single(op => isNativeInt(op.LeftType, signed: false)); - _ = operators.Single(op => isNullableNativeInt(op.LeftType, signed: true)); - _ = operators.Single(op => isNullableNativeInt(op.LeftType, signed: false)); + verifyOperators(operators, (op, signed) => isNativeInt(op.LeftType, signed), 1, 1); + verifyOperators(operators, (op, signed) => isNullableNativeInt(op.LeftType, signed), 1, 1); + } + + static void verifyOperators(ImmutableArray operators, Func predicate, int expectedSigned, int expectedUnsigned) + { + Assert.Equal(expectedSigned, operators.Count(op => predicate(op, true))); + Assert.Equal(expectedUnsigned, operators.Count(op => predicate(op, false))); } static bool isNativeInt(TypeSymbol underlyingType, bool signed) @@ -611,6 +673,628 @@ static bool isNullableNativeInt(TypeSymbol underlyingType, bool signed) } } + [Theory] + [InlineData("")] + [InlineData("unchecked")] + [InlineData("checked")] + public void ConstantConversions_ToNativeInt(string context) + { + var source = +$@"#pragma warning disable 219 +class Program +{{ + static void F1() + {{ + nint i; + {context} + {{ + i = sbyte.MaxValue; + i = byte.MaxValue; + i = char.MaxValue; + i = short.MaxValue; + i = ushort.MaxValue; + i = int.MaxValue; + i = uint.MaxValue; + i = long.MaxValue; + i = ulong.MaxValue; + i = float.MaxValue; + i = double.MaxValue; + i = (decimal)int.MaxValue; + i = (nint)int.MaxValue; + i = (nuint)uint.MaxValue; + }} + }} + static void F2() + {{ + nuint u; + {context} + {{ + u = sbyte.MaxValue; + u = byte.MaxValue; + u = char.MaxValue; + u = short.MaxValue; + u = ushort.MaxValue; + u = int.MaxValue; + u = uint.MaxValue; + u = long.MaxValue; + u = ulong.MaxValue; + u = float.MaxValue; + u = double.MaxValue; + u = (decimal)uint.MaxValue; + u = (nint)int.MaxValue; + u = (nuint)uint.MaxValue; + }} + }} +}}"; + var comp = CreateCompilation(source, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics( + // (15,17): error CS0266: Cannot implicitly convert type 'uint' to 'nint'. An explicit conversion exists (are you missing a cast?) + // i = uint.MaxValue; + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "uint.MaxValue").WithArguments("uint", "nint").WithLocation(15, 17), + // (16,17): error CS0266: Cannot implicitly convert type 'long' to 'nint'. An explicit conversion exists (are you missing a cast?) + // i = long.MaxValue; + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "long.MaxValue").WithArguments("long", "nint").WithLocation(16, 17), + // (17,17): error CS0266: Cannot implicitly convert type 'ulong' to 'nint'. An explicit conversion exists (are you missing a cast?) + // i = ulong.MaxValue; + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "ulong.MaxValue").WithArguments("ulong", "nint").WithLocation(17, 17), + // (18,17): error CS0266: Cannot implicitly convert type 'float' to 'nint'. An explicit conversion exists (are you missing a cast?) + // i = float.MaxValue; + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "float.MaxValue").WithArguments("float", "nint").WithLocation(18, 17), + // (19,17): error CS0266: Cannot implicitly convert type 'double' to 'nint'. An explicit conversion exists (are you missing a cast?) + // i = double.MaxValue; + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "double.MaxValue").WithArguments("double", "nint").WithLocation(19, 17), + // (20,17): error CS0266: Cannot implicitly convert type 'decimal' to 'nint'. An explicit conversion exists (are you missing a cast?) + // i = (decimal)int.MaxValue; + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "(decimal)int.MaxValue").WithArguments("decimal", "nint").WithLocation(20, 17), + // (22,17): error CS0266: Cannot implicitly convert type 'nuint' to 'nint'. An explicit conversion exists (are you missing a cast?) + // i = (nuint)uint.MaxValue; + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "(nuint)uint.MaxValue").WithArguments("nuint", "nint").WithLocation(22, 17), + // (37,17): error CS0266: Cannot implicitly convert type 'long' to 'nuint'. An explicit conversion exists (are you missing a cast?) + // u = long.MaxValue; + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "long.MaxValue").WithArguments("long", "nuint").WithLocation(37, 17), + // (38,17): error CS0266: Cannot implicitly convert type 'ulong' to 'nuint'. An explicit conversion exists (are you missing a cast?) + // u = ulong.MaxValue; + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "ulong.MaxValue").WithArguments("ulong", "nuint").WithLocation(38, 17), + // (39,17): error CS0266: Cannot implicitly convert type 'float' to 'nuint'. An explicit conversion exists (are you missing a cast?) + // u = float.MaxValue; + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "float.MaxValue").WithArguments("float", "nuint").WithLocation(39, 17), + // (40,17): error CS0266: Cannot implicitly convert type 'double' to 'nuint'. An explicit conversion exists (are you missing a cast?) + // u = double.MaxValue; + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "double.MaxValue").WithArguments("double", "nuint").WithLocation(40, 17), + // (41,17): error CS0266: Cannot implicitly convert type 'decimal' to 'nuint'. An explicit conversion exists (are you missing a cast?) + // u = (decimal)uint.MaxValue; + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "(decimal)uint.MaxValue").WithArguments("decimal", "nuint").WithLocation(41, 17), + // (42,17): error CS0266: Cannot implicitly convert type 'nint' to 'nuint'. An explicit conversion exists (are you missing a cast?) + // u = (nint)int.MaxValue; + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "(nint)int.MaxValue").WithArguments("nint", "nuint").WithLocation(42, 17)); + } + + [Theory] + [InlineData("")] + [InlineData("unchecked")] + [InlineData("checked")] + public void ConstantConversions_FromNativeInt(string context) + { + var source = +$@"#pragma warning disable 219 +class Program +{{ + static void F1() + {{ + const nint n = (nint)int.MaxValue; + {context} + {{ + sbyte sb = n; + byte b = n; + char c = n; + short s = n; + ushort us = n; + int i = n; + uint u = n; + long l = n; + ulong ul = n; + float f = n; + double d = n; + decimal dec = n; + nuint nu = n; + }} + }} + static void F2() + {{ + const nuint nu = (nuint)uint.MaxValue; + {context} + {{ + sbyte sb = nu; + byte b = nu; + char c = nu; + short s = nu; + ushort us = nu; + int i = nu; + uint u = nu; + long l = nu; + ulong ul = nu; + float f = nu; + double d = nu; + decimal dec = nu; + nint n = nu; + }} + }} +}}"; + var comp = CreateCompilation(source, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics( + // (9,24): error CS0266: Cannot implicitly convert type 'nint' to 'sbyte'. An explicit conversion exists (are you missing a cast?) + // sbyte sb = n; + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "n").WithArguments("nint", "sbyte").WithLocation(9, 24), + // (10,22): error CS0266: Cannot implicitly convert type 'nint' to 'byte'. An explicit conversion exists (are you missing a cast?) + // byte b = n; + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "n").WithArguments("nint", "byte").WithLocation(10, 22), + // (11,22): error CS0266: Cannot implicitly convert type 'nint' to 'char'. An explicit conversion exists (are you missing a cast?) + // char c = n; + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "n").WithArguments("nint", "char").WithLocation(11, 22), + // (12,23): error CS0266: Cannot implicitly convert type 'nint' to 'short'. An explicit conversion exists (are you missing a cast?) + // short s = n; + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "n").WithArguments("nint", "short").WithLocation(12, 23), + // (13,25): error CS0266: Cannot implicitly convert type 'nint' to 'ushort'. An explicit conversion exists (are you missing a cast?) + // ushort us = n; + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "n").WithArguments("nint", "ushort").WithLocation(13, 25), + // (14,21): error CS0266: Cannot implicitly convert type 'nint' to 'int'. An explicit conversion exists (are you missing a cast?) + // int i = n; + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "n").WithArguments("nint", "int").WithLocation(14, 21), + // (15,22): error CS0266: Cannot implicitly convert type 'nint' to 'uint'. An explicit conversion exists (are you missing a cast?) + // uint u = n; + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "n").WithArguments("nint", "uint").WithLocation(15, 22), + // (17,24): error CS0266: Cannot implicitly convert type 'nint' to 'ulong'. An explicit conversion exists (are you missing a cast?) + // ulong ul = n; + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "n").WithArguments("nint", "ulong").WithLocation(17, 24), + // (21,24): error CS0266: Cannot implicitly convert type 'nint' to 'nuint'. An explicit conversion exists (are you missing a cast?) + // nuint nu = n; + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "n").WithArguments("nint", "nuint").WithLocation(21, 24), + // (29,24): error CS0266: Cannot implicitly convert type 'nuint' to 'sbyte'. An explicit conversion exists (are you missing a cast?) + // sbyte sb = nu; + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "nu").WithArguments("nuint", "sbyte").WithLocation(29, 24), + // (30,22): error CS0266: Cannot implicitly convert type 'nuint' to 'byte'. An explicit conversion exists (are you missing a cast?) + // byte b = nu; + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "nu").WithArguments("nuint", "byte").WithLocation(30, 22), + // (31,22): error CS0266: Cannot implicitly convert type 'nuint' to 'char'. An explicit conversion exists (are you missing a cast?) + // char c = nu; + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "nu").WithArguments("nuint", "char").WithLocation(31, 22), + // (32,23): error CS0266: Cannot implicitly convert type 'nuint' to 'short'. An explicit conversion exists (are you missing a cast?) + // short s = nu; + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "nu").WithArguments("nuint", "short").WithLocation(32, 23), + // (33,25): error CS0266: Cannot implicitly convert type 'nuint' to 'ushort'. An explicit conversion exists (are you missing a cast?) + // ushort us = nu; + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "nu").WithArguments("nuint", "ushort").WithLocation(33, 25), + // (34,21): error CS0266: Cannot implicitly convert type 'nuint' to 'int'. An explicit conversion exists (are you missing a cast?) + // int i = nu; + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "nu").WithArguments("nuint", "int").WithLocation(34, 21), + // (35,22): error CS0266: Cannot implicitly convert type 'nuint' to 'uint'. An explicit conversion exists (are you missing a cast?) + // uint u = nu; + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "nu").WithArguments("nuint", "uint").WithLocation(35, 22), + // (36,22): error CS0266: Cannot implicitly convert type 'nuint' to 'long'. An explicit conversion exists (are you missing a cast?) + // long l = nu; + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "nu").WithArguments("nuint", "long").WithLocation(36, 22), + // (41,22): error CS0266: Cannot implicitly convert type 'nuint' to 'nint'. An explicit conversion exists (are you missing a cast?) + // nint n = nu; + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "nu").WithArguments("nuint", "nint").WithLocation(41, 22)); + } + + [Fact] + public void Constants_NInt() + { + string source = +$@"class Program +{{ + static void Main() + {{ + F(default); + F(int.MinValue); + F({short.MinValue - 1}); + F(short.MinValue); + F(sbyte.MinValue); + F(-2); + F(-1); + F(0); + F(1); + F(2); + F(3); + F(4); + F(5); + F(6); + F(7); + F(8); + F(9); + F(sbyte.MaxValue); + F(byte.MaxValue); + F(short.MaxValue); + F(char.MaxValue); + F(ushort.MaxValue); + F({ushort.MaxValue + 1}); + F(int.MaxValue); + }} + static void F(nint n) + {{ + System.Console.WriteLine(n); + }} +}}"; + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.RegularPreview); + string expectedOutput = +@"0 +-2147483648 +-32769 +-32768 +-128 +-2 +-1 +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +127 +255 +32767 +65535 +65535 +65536 +2147483647"; + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput); + string expectedIL = +@"{ + // Code size 209 (0xd1) + .maxstack 1 + IL_0000: ldc.i4.0 + IL_0001: conv.i + IL_0002: call ""void Program.F(nint)"" + IL_0007: ldc.i4 0x80000000 + IL_000c: conv.i + IL_000d: call ""void Program.F(nint)"" + IL_0012: ldc.i4 0xffff7fff + IL_0017: conv.i + IL_0018: call ""void Program.F(nint)"" + IL_001d: ldc.i4 0xffff8000 + IL_0022: conv.i + IL_0023: call ""void Program.F(nint)"" + IL_0028: ldc.i4.s -128 + IL_002a: conv.i + IL_002b: call ""void Program.F(nint)"" + IL_0030: ldc.i4.s -2 + IL_0032: conv.i + IL_0033: call ""void Program.F(nint)"" + IL_0038: ldc.i4.m1 + IL_0039: conv.i + IL_003a: call ""void Program.F(nint)"" + IL_003f: ldc.i4.0 + IL_0040: conv.i + IL_0041: call ""void Program.F(nint)"" + IL_0046: ldc.i4.1 + IL_0047: conv.i + IL_0048: call ""void Program.F(nint)"" + IL_004d: ldc.i4.2 + IL_004e: conv.i + IL_004f: call ""void Program.F(nint)"" + IL_0054: ldc.i4.3 + IL_0055: conv.i + IL_0056: call ""void Program.F(nint)"" + IL_005b: ldc.i4.4 + IL_005c: conv.i + IL_005d: call ""void Program.F(nint)"" + IL_0062: ldc.i4.5 + IL_0063: conv.i + IL_0064: call ""void Program.F(nint)"" + IL_0069: ldc.i4.6 + IL_006a: conv.i + IL_006b: call ""void Program.F(nint)"" + IL_0070: ldc.i4.7 + IL_0071: conv.i + IL_0072: call ""void Program.F(nint)"" + IL_0077: ldc.i4.8 + IL_0078: conv.i + IL_0079: call ""void Program.F(nint)"" + IL_007e: ldc.i4.s 9 + IL_0080: conv.i + IL_0081: call ""void Program.F(nint)"" + IL_0086: ldc.i4.s 127 + IL_0088: conv.i + IL_0089: call ""void Program.F(nint)"" + IL_008e: ldc.i4 0xff + IL_0093: conv.i + IL_0094: call ""void Program.F(nint)"" + IL_0099: ldc.i4 0x7fff + IL_009e: conv.i + IL_009f: call ""void Program.F(nint)"" + IL_00a4: ldc.i4 0xffff + IL_00a9: conv.i + IL_00aa: call ""void Program.F(nint)"" + IL_00af: ldc.i4 0xffff + IL_00b4: conv.i + IL_00b5: call ""void Program.F(nint)"" + IL_00ba: ldc.i4 0x10000 + IL_00bf: conv.i + IL_00c0: call ""void Program.F(nint)"" + IL_00c5: ldc.i4 0x7fffffff + IL_00ca: conv.i + IL_00cb: call ""void Program.F(nint)"" + IL_00d0: ret +}"; + verifier.VerifyIL("Program.Main", expectedIL); + } + + [Fact] + public void Constants_NUInt() + { + string source = +$@"class Program +{{ + static void Main() + {{ + F(default); + F(0); + F(1); + F(2); + F(3); + F(4); + F(5); + F(6); + F(7); + F(8); + F(9); + F(sbyte.MaxValue); + F(byte.MaxValue); + F(short.MaxValue); + F(char.MaxValue); + F(ushort.MaxValue); + F(int.MaxValue); + F({(uint)int.MaxValue + 1}); + F(uint.MaxValue); + }} + static void F(nuint n) + {{ + System.Console.WriteLine(n); + }} +}}"; + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.RegularPreview); + string expectedOutput = +@"0 +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +127 +255 +32767 +65535 +65535 +2147483647 +2147483648 +4294967295"; + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput); + string expectedIL = +@"{ + // Code size 160 (0xa0) + .maxstack 1 + IL_0000: ldc.i4.0 + IL_0001: conv.i + IL_0002: call ""void Program.F(nuint)"" + IL_0007: ldc.i4.0 + IL_0008: conv.i + IL_0009: call ""void Program.F(nuint)"" + IL_000e: ldc.i4.1 + IL_000f: conv.i + IL_0010: call ""void Program.F(nuint)"" + IL_0015: ldc.i4.2 + IL_0016: conv.i + IL_0017: call ""void Program.F(nuint)"" + IL_001c: ldc.i4.3 + IL_001d: conv.i + IL_001e: call ""void Program.F(nuint)"" + IL_0023: ldc.i4.4 + IL_0024: conv.i + IL_0025: call ""void Program.F(nuint)"" + IL_002a: ldc.i4.5 + IL_002b: conv.i + IL_002c: call ""void Program.F(nuint)"" + IL_0031: ldc.i4.6 + IL_0032: conv.i + IL_0033: call ""void Program.F(nuint)"" + IL_0038: ldc.i4.7 + IL_0039: conv.i + IL_003a: call ""void Program.F(nuint)"" + IL_003f: ldc.i4.8 + IL_0040: conv.i + IL_0041: call ""void Program.F(nuint)"" + IL_0046: ldc.i4.s 9 + IL_0048: conv.i + IL_0049: call ""void Program.F(nuint)"" + IL_004e: ldc.i4.s 127 + IL_0050: conv.i + IL_0051: call ""void Program.F(nuint)"" + IL_0056: ldc.i4 0xff + IL_005b: conv.i + IL_005c: call ""void Program.F(nuint)"" + IL_0061: ldc.i4 0x7fff + IL_0066: conv.i + IL_0067: call ""void Program.F(nuint)"" + IL_006c: ldc.i4 0xffff + IL_0071: conv.i + IL_0072: call ""void Program.F(nuint)"" + IL_0077: ldc.i4 0xffff + IL_007c: conv.i + IL_007d: call ""void Program.F(nuint)"" + IL_0082: ldc.i4 0x7fffffff + IL_0087: conv.i + IL_0088: call ""void Program.F(nuint)"" + IL_008d: ldc.i4 0x80000000 + IL_0092: conv.u + IL_0093: call ""void Program.F(nuint)"" + IL_0098: ldc.i4.m1 + IL_0099: conv.u + IL_009a: call ""void Program.F(nuint)"" + IL_009f: ret +}"; + verifier.VerifyIL("Program.Main", expectedIL); + } + + [Fact] + public void Constants_Locals() + { + var source = +@"#pragma warning disable 219 +class Program +{ + static void Main() + { + const System.IntPtr a = default; + const nint b = default; + const System.UIntPtr c = default; + const nuint d = default; + } +}"; + var comp = CreateCompilation(source, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics( + // (6,15): error CS0283: The type 'IntPtr' cannot be declared const + // const System.IntPtr a = default; + Diagnostic(ErrorCode.ERR_BadConstType, "System.IntPtr").WithArguments("System.IntPtr").WithLocation(6, 15), + // (8,15): error CS0283: The type 'UIntPtr' cannot be declared const + // const System.UIntPtr c = default; + Diagnostic(ErrorCode.ERR_BadConstType, "System.UIntPtr").WithArguments("System.UIntPtr").WithLocation(8, 15)); + } + + [Fact] + public void Constants_Fields_01() + { + var source = +@"class Program +{ + const System.IntPtr A = default(System.IntPtr); + const nint B = default(nint); + const System.UIntPtr C = default(System.UIntPtr); + const nuint D = default(nuint); +}"; + var comp = CreateCompilation(source, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics( + // (3,5): error CS0283: The type 'IntPtr' cannot be declared const + // const System.IntPtr A = default(System.IntPtr); + Diagnostic(ErrorCode.ERR_BadConstType, "const").WithArguments("System.IntPtr").WithLocation(3, 5), + // (3,29): error CS0133: The expression being assigned to 'Program.A' must be constant + // const System.IntPtr A = default(System.IntPtr); + Diagnostic(ErrorCode.ERR_NotConstantExpression, "default(System.IntPtr)").WithArguments("Program.A").WithLocation(3, 29), + // (5,5): error CS0283: The type 'UIntPtr' cannot be declared const + // const System.UIntPtr C = default(System.UIntPtr); + Diagnostic(ErrorCode.ERR_BadConstType, "const").WithArguments("System.UIntPtr").WithLocation(5, 5), + // (5,30): error CS0133: The expression being assigned to 'Program.C' must be constant + // const System.UIntPtr C = default(System.UIntPtr); + Diagnostic(ErrorCode.ERR_NotConstantExpression, "default(System.UIntPtr)").WithArguments("Program.C").WithLocation(5, 30)); + } + + [Fact] + public void Constants_Fields_02() + { + var source0 = +@"public class A +{ + public const nint C1 = -42; + public const nuint C2 = 42; +}"; + var comp = CreateCompilation(source0, parseOptions: TestOptions.RegularPreview); + var ref0 = comp.EmitToImageReference(); + var source1 = +@"using System; +class B +{ + static void Main() + { + Console.WriteLine(A.C1); + Console.WriteLine(A.C2); + } +}"; + comp = CreateCompilation(source1, references: new[] { ref0 }, parseOptions: TestOptions.RegularPreview, options: TestOptions.ReleaseExe); + CompileAndVerify(comp, expectedOutput: +@"-42 +42"); + } + + [Fact] + public void Constants_ParameterDefaults() + { + var source0 = +@"public class A +{ + public static System.IntPtr F1(System.IntPtr i = default) => i; + public static nint F2(nint i = -42) => i; + public static System.UIntPtr F3(System.UIntPtr u = default) => u; + public static nuint F4(nuint u = 42) => u; +}"; + var comp = CreateCompilation(source0, parseOptions: TestOptions.RegularPreview); + var ref0 = comp.EmitToImageReference(); + var source1 = +@"using System; +class B +{ + static void Main() + { + Console.WriteLine(A.F1()); + Console.WriteLine(A.F2()); + Console.WriteLine(A.F3()); + Console.WriteLine(A.F4()); + } +}"; + comp = CreateCompilation(source1, references: new[] { ref0 }, parseOptions: TestOptions.RegularPreview, options: TestOptions.ReleaseExe); + CompileAndVerify(comp, expectedOutput: +@"0 +-42 +0 +42"); + } + + [Fact] + public void Constants_FromMetadata() + { + var source0 = +@"public class Constants +{ + public const nint NIntMin = int.MinValue; + public const nint NIntMax = int.MaxValue; + public const nuint NUIntMin = uint.MinValue; + public const nuint NUIntMax = uint.MaxValue; +}"; + var comp = CreateCompilation(source0, parseOptions: TestOptions.RegularPreview); + var ref0 = comp.EmitToImageReference(); + + var source1 = +@"using System; +class Program +{ + static void Main() + { + const nint nintMin = Constants.NIntMin; + const nint nintMax = Constants.NIntMax; + const nuint nuintMin = Constants.NUIntMin; + const nuint nuintMax = Constants.NUIntMax; + Console.WriteLine(nintMin); + Console.WriteLine(nintMax); + Console.WriteLine(nuintMin); + Console.WriteLine(nuintMax); + } +}"; + comp = CreateCompilation(source1, references: new[] { ref0 }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.RegularPreview); + CompileAndVerify(comp, expectedOutput: +@"-2147483648 +2147483647 +0 +4294967295"); + } + [Fact] public void Conversions() { @@ -670,7 +1354,7 @@ .maxstack 1 }}"; void conversions(string sourceType, string destType, string expectedImplicitIL, string expectedExplicitIL, string expectedCheckedIL = null) { - Convert( + convert( sourceType, destType, expectedImplicitIL, @@ -680,7 +1364,7 @@ void conversions(string sourceType, string destType, string expectedImplicitIL, expectedImplicitIL is null ? expectedExplicitIL is null ? ErrorCode.ERR_NoImplicitConv : ErrorCode.ERR_NoImplicitConvCast : 0); - Convert( + convert( sourceType, destType, expectedExplicitIL, @@ -689,7 +1373,7 @@ void conversions(string sourceType, string destType, string expectedImplicitIL, useChecked: false, expectedExplicitIL is null ? ErrorCode.ERR_NoExplicitConv : 0); expectedCheckedIL ??= expectedExplicitIL; - Convert( + convert( sourceType, destType, expectedCheckedIL, @@ -1617,62 +2301,60 @@ .maxstack 1 }"); conversions(sourceType: "nuint?", destType: "System.IntPtr?", expectedImplicitIL: null, expectedExplicitIL: null); conversions(sourceType: "nuint?", destType: "System.UIntPtr?", expectedImplicitIL: convNone, expectedExplicitIL: convNone); - } - private void Convert(string sourceType, - string destType, - string expectedIL, - bool skipTypeChecks, - bool useExplicitCast, - bool useChecked, - ErrorCode expectedErrorCode) - { - bool useUnsafeContext = useUnsafe(sourceType) || useUnsafe(destType); - string value = "value"; - if (useExplicitCast) - { - value = $"({destType})value"; - } - var expectedDiagnostics = expectedErrorCode == 0 ? - Array.Empty() : - new[] { Diagnostic(expectedErrorCode, value).WithArguments(sourceType, destType) }; - if (useChecked) + void convert(string sourceType, + string destType, + string expectedIL, + bool skipTypeChecks, + bool useExplicitCast, + bool useChecked, + ErrorCode expectedErrorCode) { - value = $"checked({value})"; - } - string source = -$@"class Program + bool useUnsafeContext = useUnsafe(sourceType) || useUnsafe(destType); + string value = "value"; + if (useExplicitCast) + { + value = $"({destType})value"; + } + var expectedDiagnostics = expectedErrorCode == 0 ? + Array.Empty() : + new[] { Diagnostic(expectedErrorCode, value).WithArguments(sourceType, destType) }; + if (useChecked) + { + value = $"checked({value})"; + } + string source = + $@"class Program {{ static {(useUnsafeContext ? "unsafe " : "")}{destType} Convert({sourceType} value) {{ return {value}; }} }}"; - var comp = CreateCompilation(source, options: TestOptions.ReleaseDll.WithAllowUnsafe(useUnsafeContext), parseOptions: TestOptions.RegularPreview); - comp.VerifyDiagnostics(expectedDiagnostics); + var comp = CreateCompilation(source, options: TestOptions.ReleaseDll.WithAllowUnsafe(useUnsafeContext), parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics(expectedDiagnostics); - var tree = comp.SyntaxTrees[0]; - var model = comp.GetSemanticModel(tree); - var expr = tree.GetRoot().DescendantNodes().OfType().Single().Expression; - var underlyingTypeInfo = model.GetTypeInfo(expr); + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var expr = tree.GetRoot().DescendantNodes().OfType().Single().Expression; + var typeInfo = model.GetTypeInfo(expr); - if (!skipTypeChecks) - { - Assert.Equal(sourceType, underlyingTypeInfo.Type.ToString()); - Assert.Equal(destType, underlyingTypeInfo.ConvertedType.ToString()); - } + if (!skipTypeChecks) + { + Assert.Equal(sourceType, typeInfo.Type.ToString()); + Assert.Equal(destType, typeInfo.ConvertedType.ToString()); + } - if (expectedIL != null) - { - var verifier = CompileAndVerify(comp, verify: useUnsafeContext ? Verification.Skipped : Verification.Passes); - verifier.VerifyIL("Program.Convert", expectedIL); - } + if (expectedIL != null) + { + var verifier = CompileAndVerify(comp, verify: useUnsafeContext ? Verification.Skipped : Verification.Passes); + verifier.VerifyIL("Program.Convert", expectedIL); + } - static bool useUnsafe(string underlyingType) => underlyingType == "void*"; + static bool useUnsafe(string type) => type == "void*"; + } } - // PROTOTYPE:Test pre- and postfix increment and decrement. See UnopEasyOut.s_increment. - // PROTOTYPE: Test unary operator- with `static IntPtr operator-(IntPtr)` defined on System.IntPtr. (Should be ignored for `nint`.) [Fact] @@ -1694,7 +2376,7 @@ void unaryOp(string op, string opType, string expectedSymbol = null, string oper diagnostic = Diagnostic(ErrorCode.ERR_BadUnaryOp, $"{op}operand").WithArguments(op, opType); } - UnaryOperator(op, opType, opType, expectedSymbol, operand, expectedResult, expectedIL, diagnostic != null ? new[] { diagnostic } : Array.Empty()); + unaryOperator(op, opType, opType, expectedSymbol, operand, expectedResult, expectedIL, diagnostic != null ? new[] { diagnostic } : Array.Empty()); } unaryOp("+", "nint", "nint nint.op_UnaryPlus(nint value)", "3", "3", @@ -1711,8 +2393,8 @@ .maxstack 1 IL_0000: ldarg.0 IL_0001: ret }"); - unaryOp("+", "System.IntPtr"); - unaryOp("+", "System.UIntPtr"); + //unaryOp("+", "System.IntPtr"); // PROTOTYPE: Not handled. + //unaryOp("+", "System.UIntPtr"); // PROTOTYPE: Not handled. unaryOp("-", "nint", "nint nint.op_UnaryNegation(nint value)", "3", "-3", @"{ // Code size 3 (0x3) @@ -1722,7 +2404,7 @@ .maxstack 1 IL_0002: ret }"); unaryOp("-", "nuint", null, null, null, null, Diagnostic(ErrorCode.ERR_AmbigUnaryOp, "-operand").WithArguments("-", "nuint")); // PROTOTYPE: Should report ERR_BadUnaryOp. - unaryOp("-", "System.IntPtr"); + //unaryOp("-", "System.IntPtr"); // PROTOTYPE: Not handled. unaryOp("-", "System.UIntPtr"); unaryOp("!", "nint"); unaryOp("!", "nuint"); @@ -1744,8 +2426,8 @@ .maxstack 1 IL_0001: not IL_0002: ret }"); - unaryOp("~", "System.IntPtr"); - unaryOp("~", "System.UIntPtr"); + //unaryOp("~", "System.IntPtr"); // PROTOTYPE: Not handled. + //unaryOp("~", "System.UIntPtr"); // PROTOTYPE: Not handled. unaryOp("+", "nint?", "nint nint.op_UnaryPlus(nint value)", "3", "3", @"{ @@ -1787,8 +2469,8 @@ .maxstack 1 IL_001c: newobj ""nuint?..ctor(nuint)"" IL_0021: ret }"); - unaryOp("+", "System.IntPtr?"); - unaryOp("+", "System.UIntPtr?"); + //unaryOp("+", "System.IntPtr?"); // PROTOTYPE: Not handled. + //unaryOp("+", "System.UIntPtr?"); // PROTOTYPE: Not handled. unaryOp("-", "nint?", "nint nint.op_UnaryNegation(nint value)", "3", "-3", @"{ // Code size 35 (0x23) @@ -1811,7 +2493,7 @@ .maxstack 1 IL_0022: ret }"); unaryOp("-", "nuint?", null, null, null, null, Diagnostic(ErrorCode.ERR_AmbigUnaryOp, "-operand").WithArguments("-", "nuint?")); // PROTOTYPE: Should report ERR_BadUnaryOp. - unaryOp("-", "System.IntPtr?"); + //unaryOp("-", "System.IntPtr?"); // PROTOTYPE: Not handled. unaryOp("-", "System.UIntPtr?"); unaryOp("!", "nint?"); unaryOp("!", "nuint?"); @@ -1859,14 +2541,13 @@ .maxstack 1 IL_001d: newobj ""nuint?..ctor(nuint)"" IL_0022: ret }"); - unaryOp("~", "System.IntPtr?"); - unaryOp("~", "System.UIntPtr?"); - } + //unaryOp("~", "System.IntPtr?"); // PROTOTYPE: Not handled. + //unaryOp("~", "System.UIntPtr?"); // PROTOTYPE: Not handled. - private void UnaryOperator(string op, string opType, string resultType, string expectedSymbol, string operand, string expectedResult, string expectedIL, DiagnosticDescription[] expectedDiagnostics) - { - string source = -$@"class Program + void unaryOperator(string op, string opType, string resultType, string expectedSymbol, string operand, string expectedResult, string expectedIL, DiagnosticDescription[] expectedDiagnostics) + { + string source = + $@"class Program {{ static {resultType} Evaluate({opType} operand) {{ @@ -1877,97 +2558,1471 @@ static void Main() System.Console.WriteLine(Evaluate({operand})); }} }}"; - var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.RegularPreview); - comp.VerifyDiagnostics(expectedDiagnostics); + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics(expectedDiagnostics); - var tree = comp.SyntaxTrees[0]; - var model = comp.GetSemanticModel(tree); - var expr = tree.GetRoot().DescendantNodes().OfType().Single(); - var symbolInfo = model.GetSymbolInfo(expr); - Assert.Equal(expectedSymbol, symbolInfo.Symbol?.ToDisplayString(SymbolDisplayFormat.TestFormat.WithMiscellaneousOptions(SymbolDisplayMiscellaneousOptions.UseSpecialTypes))); + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var expr = tree.GetRoot().DescendantNodes().OfType().Single(); + var symbolInfo = model.GetSymbolInfo(expr); + Assert.Equal(expectedSymbol, symbolInfo.Symbol?.ToDisplayString(SymbolDisplayFormat.TestFormat.WithMiscellaneousOptions(SymbolDisplayMiscellaneousOptions.UseSpecialTypes))); - if (expectedDiagnostics.Length == 0) - { - var verifier = CompileAndVerify(comp, expectedOutput: expectedResult); - verifier.VerifyIL("Program.Evaluate", expectedIL); + if (expectedDiagnostics.Length == 0) + { + var verifier = CompileAndVerify(comp, expectedOutput: expectedResult); + verifier.VerifyIL("Program.Evaluate", expectedIL); + } } } [Fact] - public void BinaryOperators() + public void IncrementOperators() { - void binaryOps(string op, string leftType, string rightType, string expectedSymbol1 = null, string expectedSymbol2 = "", DiagnosticDescription[] diagnostics1 = null, DiagnosticDescription[] diagnostics2 = null) + void incrementOps(string op, string opType, string expectedSymbol = null, bool useChecked = false, string values = null, string expectedResult = null, string expectedIL = "", string expectedLiftedIL = "", DiagnosticDescription diagnostic = null) { - binaryOp(op, leftType, rightType, expectedSymbol1, diagnostics1); - binaryOp(op, rightType, leftType, expectedSymbol2 == "" ? expectedSymbol1 : expectedSymbol2, diagnostics2 ?? diagnostics1); - } + incrementOperator(op, opType, isPrefix: true, expectedSymbol, useChecked, values, expectedResult, expectedIL, getDiagnostics(opType, isPrefix: true, diagnostic)); + incrementOperator(op, opType, isPrefix: false, expectedSymbol, useChecked, values, expectedResult, expectedIL, getDiagnostics(opType, isPrefix: false, diagnostic)); + opType += "?"; + incrementOperator(op, opType, isPrefix: true, expectedSymbol, useChecked, values, expectedResult, expectedLiftedIL, getDiagnostics(opType, isPrefix: true, diagnostic)); + incrementOperator(op, opType, isPrefix: false, expectedSymbol, useChecked, values, expectedResult, expectedLiftedIL, getDiagnostics(opType, isPrefix: false, diagnostic)); - void binaryOp(string op, string leftType, string rightType, string expectedSymbol, DiagnosticDescription[] diagnostics) - { - if (expectedSymbol == null && diagnostics == null) + DiagnosticDescription[] getDiagnostics(string opType, bool isPrefix, DiagnosticDescription diagnostic) { - diagnostics = new[] { Diagnostic(ErrorCode.ERR_BadBinaryOps, $"x {op} y").WithArguments(op, leftType, rightType) }; + if (expectedSymbol == null && diagnostic == null) + { + diagnostic = Diagnostic(ErrorCode.ERR_BadUnaryOp, isPrefix ? op + "operand" : "operand" + op).WithArguments(op, opType); + } + return diagnostic != null ? new[] { diagnostic } : Array.Empty(); } - BinaryOperator(op, leftType, rightType, expectedSymbol, diagnostics ?? Array.Empty()); } - var arithmeticOperators = new[] - { - ("-", "op_Subtraction"), - ("*", "op_Multiply"), - ("/", "op_Division"), - ("%", "op_Modulus"), - }; - var additionOperators = new[] - { - ("+", "op_Addition"), - }; - var comparisonOperators = new[] - { - ("<", "op_LessThan"), - ("<=", "op_LessThanOrEqual"), - (">", "op_GreaterThan"), - (">=", "op_GreaterThanOrEqual"), - }; - var shiftOperators = new[] - { - ("<<", "op_LeftShift"), - (">>", "op_RightShift"), - }; - var equalityOperators = new[] - { - ("==", "op_Equality"), - ("!=", "op_Inequality"), - }; - var logicalOperators = new[] - { - ("&", "op_BitwiseAnd"), - ("|", "op_BitwiseOr"), - ("^", "op_ExclusiveOr"), - }; - - foreach ((string symbol, string name) in arithmeticOperators) - { - binaryOps(symbol, "nint", "object"); - binaryOps(symbol, "nint", "string"); - // PROTOTYPE: Test all: - if (symbol == "*") binaryOps(symbol, "nint", "void*", null, null, new[] { Diagnostic(ErrorCode.ERR_BadBinaryOps, $"x {symbol} y").WithArguments(symbol, "nint", "void*"), Diagnostic(ErrorCode.ERR_VoidError, $"x {symbol} y") }, new[] { Diagnostic(ErrorCode.ERR_BadBinaryOps, $"x {symbol} y").WithArguments(symbol, "void*", "nint"), Diagnostic(ErrorCode.ERR_VoidError, $"x {symbol} y") }); - binaryOps(symbol, "nint", "bool"); - binaryOps(symbol, "nint", "char", $"nint nint.{name}(nint left, nint right)"); - binaryOps(symbol, "nint", "sbyte", $"nint nint.{name}(nint left, nint right)"); - binaryOps(symbol, "nint", "byte", $"nint nint.{name}(nint left, nint right)"); - binaryOps(symbol, "nint", "short", $"nint nint.{name}(nint left, nint right)"); - binaryOps(symbol, "nint", "ushort", $"nint nint.{name}(nint left, nint right)"); - binaryOps(symbol, "nint", "int", $"nint nint.{name}(nint left, nint right)"); - binaryOps(symbol, "nint", "uint", $"long long.{name}(long left, long right)"); - binaryOps(symbol, "nint", "nint", $"nint nint.{name}(nint left, nint right)"); - binaryOps(symbol, "nint", "nuint", null, null, new[] { Diagnostic(ErrorCode.ERR_AmbigBinaryOps, $"x {symbol} y").WithArguments(symbol, "nint", "nuint") }, new[] { Diagnostic(ErrorCode.ERR_AmbigBinaryOps, $"x {symbol} y").WithArguments(symbol, "nuint", "nint") }); - binaryOps(symbol, "nint", "long", $"long long.{name}(long left, long right)"); - binaryOps(symbol, "nint", "ulong", null, null, new[] { Diagnostic(ErrorCode.ERR_AmbigBinaryOps, $"x {symbol} y").WithArguments(symbol, "nint", "ulong") }, new[] { Diagnostic(ErrorCode.ERR_AmbigBinaryOps, $"x {symbol} y").WithArguments(symbol, "ulong", "nint") }); - binaryOps(symbol, "nint", "float", $"float float.{name}(float left, float right)"); + incrementOps("++", "nint", "nint nint.op_Increment(nint value)", useChecked: false, + values: $"{int.MinValue}, -1, 0, {int.MaxValue - 1}, {int.MaxValue}", + expectedResult: $"-2147483647, 0, 1, 2147483647, {(IntPtr.Size == 4 ? "-2147483648" : "2147483648")}", +@"{ + // Code size 7 (0x7) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldc.i4.1 + IL_0002: add + IL_0003: starg.s V_0 + IL_0005: ldarg.0 + IL_0006: ret +}", +@"{ + // Code size 40 (0x28) + .maxstack 2 + .locals init (nint? V_0, + nint? V_1) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: call ""bool nint?.HasValue.get"" + IL_0009: brtrue.s IL_0016 + IL_000b: ldloca.s V_1 + IL_000d: initobj ""nint?"" + IL_0013: ldloc.1 + IL_0014: br.s IL_0024 + IL_0016: ldloca.s V_0 + IL_0018: call ""nint nint?.GetValueOrDefault()"" + IL_001d: ldc.i4.1 + IL_001e: add + IL_001f: newobj ""nint?..ctor(nint)"" + IL_0024: starg.s V_0 + IL_0026: ldarg.0 + IL_0027: ret +}"); + incrementOps("++", "nuint", "nuint nuint.op_Increment(nuint value)", useChecked: false, + values: $"0, {int.MaxValue}, {uint.MaxValue - 1}, {uint.MaxValue}", + expectedResult: $"1, 2147483648, 4294967295, {(IntPtr.Size == 4 ? "0" : "4294967296")}", +@"{ + // Code size 7 (0x7) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldc.i4.1 + IL_0002: add + IL_0003: starg.s V_0 + IL_0005: ldarg.0 + IL_0006: ret +}", +@"{ + // Code size 40 (0x28) + .maxstack 2 + .locals init (nuint? V_0, + nuint? V_1) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: call ""bool nuint?.HasValue.get"" + IL_0009: brtrue.s IL_0016 + IL_000b: ldloca.s V_1 + IL_000d: initobj ""nuint?"" + IL_0013: ldloc.1 + IL_0014: br.s IL_0024 + IL_0016: ldloca.s V_0 + IL_0018: call ""nuint nuint?.GetValueOrDefault()"" + IL_001d: ldc.i4.1 + IL_001e: add + IL_001f: newobj ""nuint?..ctor(nuint)"" + IL_0024: starg.s V_0 + IL_0026: ldarg.0 + IL_0027: ret +}"); + //incrementOps("++", "System.IntPtr"); // PROTOTYPE: Not handled. + //incrementOps("++", "System.UIntPtr"); // PROTOTYPE: Not handled. + incrementOps("--", "nint", "nint nint.op_Decrement(nint value)", useChecked: false, + values: $"{int.MinValue}, {int.MinValue + 1}, 0, 1, {int.MaxValue}", + expectedResult: $"{(IntPtr.Size == 4 ? "2147483647" : "-2147483649")}, -2147483648, -1, 0, 2147483646", +@"{ + // Code size 7 (0x7) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldc.i4.1 + IL_0002: sub + IL_0003: starg.s V_0 + IL_0005: ldarg.0 + IL_0006: ret +}", +@"{ + // Code size 40 (0x28) + .maxstack 2 + .locals init (nint? V_0, + nint? V_1) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: call ""bool nint?.HasValue.get"" + IL_0009: brtrue.s IL_0016 + IL_000b: ldloca.s V_1 + IL_000d: initobj ""nint?"" + IL_0013: ldloc.1 + IL_0014: br.s IL_0024 + IL_0016: ldloca.s V_0 + IL_0018: call ""nint nint?.GetValueOrDefault()"" + IL_001d: ldc.i4.1 + IL_001e: sub + IL_001f: newobj ""nint?..ctor(nint)"" + IL_0024: starg.s V_0 + IL_0026: ldarg.0 + IL_0027: ret +}"); + incrementOps("--", "nuint", "nuint nuint.op_Decrement(nuint value)", useChecked: false, + values: $"0, 1, {uint.MaxValue}", + expectedResult: $"{(IntPtr.Size == 4 ? uint.MaxValue.ToString() : ulong.MaxValue.ToString())}, 0, 4294967294", +@"{ + // Code size 7 (0x7) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldc.i4.1 + IL_0002: sub + IL_0003: starg.s V_0 + IL_0005: ldarg.0 + IL_0006: ret +}", +@"{ + // Code size 40 (0x28) + .maxstack 2 + .locals init (nuint? V_0, + nuint? V_1) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: call ""bool nuint?.HasValue.get"" + IL_0009: brtrue.s IL_0016 + IL_000b: ldloca.s V_1 + IL_000d: initobj ""nuint?"" + IL_0013: ldloc.1 + IL_0014: br.s IL_0024 + IL_0016: ldloca.s V_0 + IL_0018: call ""nuint nuint?.GetValueOrDefault()"" + IL_001d: ldc.i4.1 + IL_001e: sub + IL_001f: newobj ""nuint?..ctor(nuint)"" + IL_0024: starg.s V_0 + IL_0026: ldarg.0 + IL_0027: ret +}"); + //incrementOps("--", "System.IntPtr"); // PROTOTYPE: Not handled. + //incrementOps("--", "System.UIntPtr"); // PROTOTYPE: Not handled. + + incrementOps("++", "nint", "nint nint.op_Increment(nint value)", useChecked: true, + values: $"{int.MinValue}, -1, 0, {int.MaxValue - 1}, {int.MaxValue}", + expectedResult: $"-2147483647, 0, 1, 2147483647, {(IntPtr.Size == 4 ? "System.OverflowException" : "2147483648")}", +@"{ + // Code size 7 (0x7) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldc.i4.1 + IL_0002: add.ovf + IL_0003: starg.s V_0 + IL_0005: ldarg.0 + IL_0006: ret +}", +@"{ + // Code size 40 (0x28) + .maxstack 2 + .locals init (nint? V_0, + nint? V_1) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: call ""bool nint?.HasValue.get"" + IL_0009: brtrue.s IL_0016 + IL_000b: ldloca.s V_1 + IL_000d: initobj ""nint?"" + IL_0013: ldloc.1 + IL_0014: br.s IL_0024 + IL_0016: ldloca.s V_0 + IL_0018: call ""nint nint?.GetValueOrDefault()"" + IL_001d: ldc.i4.1 + IL_001e: add.ovf + IL_001f: newobj ""nint?..ctor(nint)"" + IL_0024: starg.s V_0 + IL_0026: ldarg.0 + IL_0027: ret +}"); + incrementOps("++", "nuint", "nuint nuint.op_Increment(nuint value)", useChecked: true, + values: $"0, {int.MaxValue}, {uint.MaxValue - 1}, {uint.MaxValue}", + expectedResult: $"1, 2147483648, 4294967295, {(IntPtr.Size == 4 ? "System.OverflowException" : "4294967296")}", +@"{ + // Code size 7 (0x7) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldc.i4.1 + IL_0002: add.ovf.un + IL_0003: starg.s V_0 + IL_0005: ldarg.0 + IL_0006: ret +}", +@"{ + // Code size 40 (0x28) + .maxstack 2 + .locals init (nuint? V_0, + nuint? V_1) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: call ""bool nuint?.HasValue.get"" + IL_0009: brtrue.s IL_0016 + IL_000b: ldloca.s V_1 + IL_000d: initobj ""nuint?"" + IL_0013: ldloc.1 + IL_0014: br.s IL_0024 + IL_0016: ldloca.s V_0 + IL_0018: call ""nuint nuint?.GetValueOrDefault()"" + IL_001d: ldc.i4.1 + IL_001e: add.ovf.un + IL_001f: newobj ""nuint?..ctor(nuint)"" + IL_0024: starg.s V_0 + IL_0026: ldarg.0 + IL_0027: ret +}"); + incrementOps("--", "nint", "nint nint.op_Decrement(nint value)", useChecked: true, + values: $"{int.MinValue}, {int.MinValue + 1}, 0, 1, {int.MaxValue}", + expectedResult: $"{(IntPtr.Size == 4 ? "System.OverflowException" : "-2147483649")}, -2147483648, -1, 0, 2147483646", +@"{ + // Code size 7 (0x7) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldc.i4.1 + IL_0002: sub.ovf + IL_0003: starg.s V_0 + IL_0005: ldarg.0 + IL_0006: ret +}", +@"{ + // Code size 40 (0x28) + .maxstack 2 + .locals init (nint? V_0, + nint? V_1) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: call ""bool nint?.HasValue.get"" + IL_0009: brtrue.s IL_0016 + IL_000b: ldloca.s V_1 + IL_000d: initobj ""nint?"" + IL_0013: ldloc.1 + IL_0014: br.s IL_0024 + IL_0016: ldloca.s V_0 + IL_0018: call ""nint nint?.GetValueOrDefault()"" + IL_001d: ldc.i4.1 + IL_001e: sub.ovf + IL_001f: newobj ""nint?..ctor(nint)"" + IL_0024: starg.s V_0 + IL_0026: ldarg.0 + IL_0027: ret +}"); + incrementOps("--", "nuint", "nuint nuint.op_Decrement(nuint value)", useChecked: true, + values: $"0, 1, {uint.MaxValue}", + expectedResult: $"System.OverflowException, 0, 4294967294", +@"{ + // Code size 7 (0x7) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldc.i4.1 + IL_0002: sub.ovf.un + IL_0003: starg.s V_0 + IL_0005: ldarg.0 + IL_0006: ret +}", +@"{ + // Code size 40 (0x28) + .maxstack 2 + .locals init (nuint? V_0, + nuint? V_1) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: call ""bool nuint?.HasValue.get"" + IL_0009: brtrue.s IL_0016 + IL_000b: ldloca.s V_1 + IL_000d: initobj ""nuint?"" + IL_0013: ldloc.1 + IL_0014: br.s IL_0024 + IL_0016: ldloca.s V_0 + IL_0018: call ""nuint nuint?.GetValueOrDefault()"" + IL_001d: ldc.i4.1 + IL_001e: sub.ovf.un + IL_001f: newobj ""nuint?..ctor(nuint)"" + IL_0024: starg.s V_0 + IL_0026: ldarg.0 + IL_0027: ret +}"); + + void incrementOperator(string op, string opType, bool isPrefix, string expectedSymbol, bool useChecked, string values, string expectedResult, string expectedIL, DiagnosticDescription[] expectedDiagnostics) + { + var source = +$@"using System; +class Program +{{ + static {opType} Evaluate({opType} operand) + {{ + {(useChecked ? "checked" : "unchecked")} + {{ + {(isPrefix ? op + "operand" : "operand" + op)}; + return operand; + }} + }} + static void EvaluateAndReport({opType} operand) + {{ + object result; + try + {{ + result = Evaluate(operand); + }} + catch (Exception e) + {{ + result = e.GetType(); + }} + Console.Write(result); + }} + static void Main() + {{ + bool separator = false; + foreach (var value in new {opType}[] {{ {values} }}) + {{ + if (separator) Console.Write("", ""); + separator = true; + EvaluateAndReport(value); + }} + }} +}}"; + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics(expectedDiagnostics); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var kind = (op == "++") ? + isPrefix ? SyntaxKind.PreIncrementExpression : SyntaxKind.PostIncrementExpression : + isPrefix ? SyntaxKind.PreDecrementExpression : SyntaxKind.PostDecrementExpression; + var expr = tree.GetRoot().DescendantNodes().Single(n => n.Kind() == kind); + var symbolInfo = model.GetSymbolInfo(expr); + Assert.Equal(expectedSymbol, symbolInfo.Symbol?.ToDisplayString(SymbolDisplayFormat.TestFormat.WithMiscellaneousOptions(SymbolDisplayMiscellaneousOptions.UseSpecialTypes))); + + if (expectedDiagnostics.Length == 0) + { + var verifier = CompileAndVerify(comp, expectedOutput: expectedResult); + verifier.VerifyIL("Program.Evaluate", expectedIL); + } + } + } + + [Fact] + public void IncrementOperators_RefOperand() + { + void incrementOps(string op, string opType, string expectedSymbol = null, string values = null, string expectedResult = null, string expectedIL = "", string expectedLiftedIL = "", DiagnosticDescription diagnostic = null) + { + incrementOperator(op, opType, expectedSymbol, values, expectedResult, expectedIL, getDiagnostics(opType, diagnostic)); + opType += "?"; + incrementOperator(op, opType, expectedSymbol, values, expectedResult, expectedLiftedIL, getDiagnostics(opType, diagnostic)); + + DiagnosticDescription[] getDiagnostics(string opType, DiagnosticDescription diagnostic) + { + if (expectedSymbol == null && diagnostic == null) + { + diagnostic = Diagnostic(ErrorCode.ERR_BadUnaryOp, op + "operand").WithArguments(op, opType); + } + return diagnostic != null ? new[] { diagnostic } : Array.Empty(); + } + } + + incrementOps("++", "nint", "nint nint.op_Increment(nint value)", + values: $"{int.MinValue}, -1, 0, {int.MaxValue - 1}, {int.MaxValue}", + expectedResult: $"-2147483647, 0, 1, 2147483647, {(IntPtr.Size == 4 ? "-2147483648" : "2147483648")}", +@"{ + // Code size 7 (0x7) + .maxstack 3 + IL_0000: ldarg.0 + IL_0001: ldarg.0 + IL_0002: ldind.i + IL_0003: ldc.i4.1 + IL_0004: add + IL_0005: stind.i + IL_0006: ret +}", +@"{ + // Code size 48 (0x30) + .maxstack 3 + .locals init (nint? V_0, + nint? V_1) + IL_0000: ldarg.0 + IL_0001: ldarg.0 + IL_0002: ldobj ""nint?"" + IL_0007: stloc.0 + IL_0008: ldloca.s V_0 + IL_000a: call ""bool nint?.HasValue.get"" + IL_000f: brtrue.s IL_001c + IL_0011: ldloca.s V_1 + IL_0013: initobj ""nint?"" + IL_0019: ldloc.1 + IL_001a: br.s IL_002a + IL_001c: ldloca.s V_0 + IL_001e: call ""nint nint?.GetValueOrDefault()"" + IL_0023: ldc.i4.1 + IL_0024: add + IL_0025: newobj ""nint?..ctor(nint)"" + IL_002a: stobj ""nint?"" + IL_002f: ret +}"); + incrementOps("++", "nuint", "nuint nuint.op_Increment(nuint value)", + values: $"0, {int.MaxValue}, {uint.MaxValue - 1}, {uint.MaxValue}", + expectedResult: $"1, 2147483648, 4294967295, {(IntPtr.Size == 4 ? "0" : "4294967296")}", +@"{ + // Code size 7 (0x7) + .maxstack 3 + IL_0000: ldarg.0 + IL_0001: ldarg.0 + IL_0002: ldind.i + IL_0003: ldc.i4.1 + IL_0004: add + IL_0005: stind.i + IL_0006: ret +}", +@"{ + // Code size 48 (0x30) + .maxstack 3 + .locals init (nuint? V_0, + nuint? V_1) + IL_0000: ldarg.0 + IL_0001: ldarg.0 + IL_0002: ldobj ""nuint?"" + IL_0007: stloc.0 + IL_0008: ldloca.s V_0 + IL_000a: call ""bool nuint?.HasValue.get"" + IL_000f: brtrue.s IL_001c + IL_0011: ldloca.s V_1 + IL_0013: initobj ""nuint?"" + IL_0019: ldloc.1 + IL_001a: br.s IL_002a + IL_001c: ldloca.s V_0 + IL_001e: call ""nuint nuint?.GetValueOrDefault()"" + IL_0023: ldc.i4.1 + IL_0024: add + IL_0025: newobj ""nuint?..ctor(nuint)"" + IL_002a: stobj ""nuint?"" + IL_002f: ret +}"); + incrementOps("--", "nint", "nint nint.op_Decrement(nint value)", + values: $"{int.MinValue}, {int.MinValue + 1}, 0, 1, {int.MaxValue}", + expectedResult: $"{(IntPtr.Size == 4 ? "2147483647" : "-2147483649")}, -2147483648, -1, 0, 2147483646", +@"{ + // Code size 7 (0x7) + .maxstack 3 + IL_0000: ldarg.0 + IL_0001: ldarg.0 + IL_0002: ldind.i + IL_0003: ldc.i4.1 + IL_0004: sub + IL_0005: stind.i + IL_0006: ret +}", +@"{ + // Code size 48 (0x30) + .maxstack 3 + .locals init (nint? V_0, + nint? V_1) + IL_0000: ldarg.0 + IL_0001: ldarg.0 + IL_0002: ldobj ""nint?"" + IL_0007: stloc.0 + IL_0008: ldloca.s V_0 + IL_000a: call ""bool nint?.HasValue.get"" + IL_000f: brtrue.s IL_001c + IL_0011: ldloca.s V_1 + IL_0013: initobj ""nint?"" + IL_0019: ldloc.1 + IL_001a: br.s IL_002a + IL_001c: ldloca.s V_0 + IL_001e: call ""nint nint?.GetValueOrDefault()"" + IL_0023: ldc.i4.1 + IL_0024: sub + IL_0025: newobj ""nint?..ctor(nint)"" + IL_002a: stobj ""nint?"" + IL_002f: ret +}"); + incrementOps("--", "nuint", "nuint nuint.op_Decrement(nuint value)", + values: $"0, 1, {uint.MaxValue}", + expectedResult: $"{(IntPtr.Size == 4 ? uint.MaxValue.ToString() : ulong.MaxValue.ToString())}, 0, 4294967294", +@"{ + // Code size 7 (0x7) + .maxstack 3 + IL_0000: ldarg.0 + IL_0001: ldarg.0 + IL_0002: ldind.i + IL_0003: ldc.i4.1 + IL_0004: sub + IL_0005: stind.i + IL_0006: ret +}", +@"{ + // Code size 48 (0x30) + .maxstack 3 + .locals init (nuint? V_0, + nuint? V_1) + IL_0000: ldarg.0 + IL_0001: ldarg.0 + IL_0002: ldobj ""nuint?"" + IL_0007: stloc.0 + IL_0008: ldloca.s V_0 + IL_000a: call ""bool nuint?.HasValue.get"" + IL_000f: brtrue.s IL_001c + IL_0011: ldloca.s V_1 + IL_0013: initobj ""nuint?"" + IL_0019: ldloc.1 + IL_001a: br.s IL_002a + IL_001c: ldloca.s V_0 + IL_001e: call ""nuint nuint?.GetValueOrDefault()"" + IL_0023: ldc.i4.1 + IL_0024: sub + IL_0025: newobj ""nuint?..ctor(nuint)"" + IL_002a: stobj ""nuint?"" + IL_002f: ret +}"); + + void incrementOperator(string op, string opType, string expectedSymbol, string values, string expectedResult, string expectedIL, DiagnosticDescription[] expectedDiagnostics) + { + string source = + $@"using System; +class Program +{{ + static void Evaluate(ref {opType} operand) + {{ + {op}operand; + }} + static void EvaluateAndReport({opType} operand) + {{ + object result; + try + {{ + Evaluate(ref operand); + result = operand; + }} + catch (Exception e) + {{ + result = e.GetType(); + }} + Console.Write(result); + }} + static void Main() + {{ + bool separator = false; + foreach (var value in new {opType}[] {{ {values} }}) + {{ + if (separator) Console.Write("", ""); + separator = true; + EvaluateAndReport(value); + }} + }} +}}"; + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics(expectedDiagnostics); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var kind = (op == "++") ? SyntaxKind.PreIncrementExpression : SyntaxKind.PreDecrementExpression; + var expr = tree.GetRoot().DescendantNodes().Single(n => n.Kind() == kind); + var symbolInfo = model.GetSymbolInfo(expr); + Assert.Equal(expectedSymbol, symbolInfo.Symbol?.ToDisplayString(SymbolDisplayFormat.TestFormat.WithMiscellaneousOptions(SymbolDisplayMiscellaneousOptions.UseSpecialTypes))); + + if (expectedDiagnostics.Length == 0) + { + var verifier = CompileAndVerify(comp, expectedOutput: expectedResult); + verifier.VerifyIL("Program.Evaluate", expectedIL); + } + } + } + + [Fact] + public void UnaryOperators_UserDefinedConversions_NInt() + { + // PROTOTYPE: Declare _i readonly. See ReadOnlyField_VirtualMethods(). + string source = +@"using System; +class MyInt +{ + private nint _i; + internal MyInt(nint i) => _i = i; + public static implicit operator nint(MyInt i) => i._i; + public static implicit operator MyInt(nint i) => new MyInt(i); + public override string ToString() => _i.ToString(); +} +class Program +{ + static void Main() + { + // ++i; + Evaluate(int.MinValue, PrefixIncrement); + Evaluate(-1, PrefixIncrement); + Evaluate(0, PrefixIncrement); + // i++; + Evaluate(int.MinValue, PostfixIncrement); + Evaluate(-1, PostfixIncrement); + Evaluate(0, PostfixIncrement); + // --i; + Evaluate(int.MaxValue, PrefixDecrement); + Evaluate(1, PrefixDecrement); + Evaluate(0, PrefixDecrement); + // i--; + Evaluate(int.MaxValue, PostfixDecrement); + Evaluate(1, PostfixDecrement); + Evaluate(0, PostfixDecrement); + // +i; + Evaluate(int.MinValue, Plus); + Evaluate(0, Plus); + Evaluate(int.MaxValue, Plus); + // -i; + Evaluate(int.MinValue, Minus); + Evaluate(0, Minus); + Evaluate(int.MaxValue, Minus); + // ~i; + Evaluate(int.MinValue, Complement); + Evaluate(0, Complement); + Evaluate(int.MaxValue, Complement); + } + static void Evaluate(nint i, Func f) + { + MyInt m = f(new MyInt(i)); + Console.WriteLine(m); + } + static MyInt PrefixIncrement(MyInt i) => ++i; + static MyInt PostfixIncrement(MyInt i) { i++; return i; } + static MyInt PrefixDecrement(MyInt i) => --i; + static MyInt PostfixDecrement(MyInt i) { i--; return i; } + static MyInt Plus(MyInt i) => +i; + static MyInt Minus(MyInt i) => -i; + static MyInt Complement(MyInt i) => ~i; +}"; + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.RegularPreview); + string expectedOutput = +$@"-2147483647 +0 +1 +-2147483647 +0 +1 +2147483646 +0 +-1 +2147483646 +0 +-1 +-2147483648 +0 +2147483647 +{(IntPtr.Size == 4 ? "-2147483648" : "2147483648")} +0 +-2147483647 +2147483647 +-1 +-2147483648"; + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput); + verifier.VerifyIL("Program.PrefixIncrement", +@"{ + // Code size 17 (0x11) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: call ""nint MyInt.op_Implicit(MyInt)"" + IL_0006: ldc.i4.1 + IL_0007: add + IL_0008: call ""MyInt MyInt.op_Implicit(nint)"" + IL_000d: dup + IL_000e: starg.s V_0 + IL_0010: ret +}"); + verifier.VerifyIL("Program.PostfixIncrement", +@"{ + // Code size 17 (0x11) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: call ""nint MyInt.op_Implicit(MyInt)"" + IL_0006: ldc.i4.1 + IL_0007: add + IL_0008: call ""MyInt MyInt.op_Implicit(nint)"" + IL_000d: starg.s V_0 + IL_000f: ldarg.0 + IL_0010: ret +}"); + verifier.VerifyIL("Program.PrefixDecrement", +@"{ + // Code size 17 (0x11) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: call ""nint MyInt.op_Implicit(MyInt)"" + IL_0006: ldc.i4.1 + IL_0007: sub + IL_0008: call ""MyInt MyInt.op_Implicit(nint)"" + IL_000d: dup + IL_000e: starg.s V_0 + IL_0010: ret +}"); + verifier.VerifyIL("Program.PostfixDecrement", +@"{ + // Code size 17 (0x11) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: call ""nint MyInt.op_Implicit(MyInt)"" + IL_0006: ldc.i4.1 + IL_0007: sub + IL_0008: call ""MyInt MyInt.op_Implicit(nint)"" + IL_000d: starg.s V_0 + IL_000f: ldarg.0 + IL_0010: ret +}"); + verifier.VerifyIL("Program.Plus", +@"{ + // Code size 12 (0xc) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call ""nint MyInt.op_Implicit(MyInt)"" + IL_0006: call ""MyInt MyInt.op_Implicit(nint)"" + IL_000b: ret +}"); + verifier.VerifyIL("Program.Minus", +@"{ + // Code size 13 (0xd) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call ""nint MyInt.op_Implicit(MyInt)"" + IL_0006: neg + IL_0007: call ""MyInt MyInt.op_Implicit(nint)"" + IL_000c: ret +}"); + verifier.VerifyIL("Program.Complement", +@"{ + // Code size 13 (0xd) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call ""nint MyInt.op_Implicit(MyInt)"" + IL_0006: not + IL_0007: call ""MyInt MyInt.op_Implicit(nint)"" + IL_000c: ret +}"); + } + + [Fact] + public void UnaryOperators_UserDefinedConversions_NUInt() + { + // PROTOTYPE: Declare _i readonly. See ReadOnlyField_VirtualMethods(). + string source = +@"using System; +class MyInt +{ + private nuint _i; + internal MyInt(nuint i) => _i = i; + public static implicit operator nuint(MyInt i) => i._i; + public static implicit operator MyInt(nuint i) => new MyInt(i); + public override string ToString() => _i.ToString(); +} +class Program +{ + static void Main() + { + // ++i; + Evaluate(0, PrefixIncrement); + Evaluate(uint.MaxValue - 1, PrefixIncrement); + // i++; + Evaluate(0, PostfixIncrement); + Evaluate(uint.MaxValue - 1, PostfixIncrement); + // --i; + Evaluate(1, PrefixDecrement); + Evaluate(uint.MaxValue, PrefixDecrement); + // i--; + Evaluate(1, PostfixDecrement); + Evaluate(uint.MaxValue, PostfixDecrement); + // +i; + Evaluate(0, Plus); + Evaluate(uint.MaxValue, Plus); + // ~i; + Evaluate(0, Complement); + Evaluate(uint.MaxValue, Complement); + } + static void Evaluate(nuint i, Func f) + { + MyInt m = f(new MyInt(i)); + Console.WriteLine(m); + } + static MyInt PrefixIncrement(MyInt i) => ++i; + static MyInt PostfixIncrement(MyInt i) { i++; return i; } + static MyInt PrefixDecrement(MyInt i) => --i; + static MyInt PostfixDecrement(MyInt i) { i--; return i; } + static MyInt Plus(MyInt i) => +i; + static MyInt Complement(MyInt i) => ~i; +}"; + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.RegularPreview); + string expectedOutput = +$@"1 +4294967295 +1 +4294967295 +0 +4294967294 +0 +4294967294 +0 +4294967295 +{(IntPtr.Size == 4 ? "4294967295" : "18446744073709551615")} +{(IntPtr.Size == 4 ? "0" : "18446744069414584320")}"; + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput); + verifier.VerifyIL("Program.PrefixIncrement", +@"{ + // Code size 17 (0x11) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: call ""nuint MyInt.op_Implicit(MyInt)"" + IL_0006: ldc.i4.1 + IL_0007: add + IL_0008: call ""MyInt MyInt.op_Implicit(nuint)"" + IL_000d: dup + IL_000e: starg.s V_0 + IL_0010: ret +}"); + verifier.VerifyIL("Program.PostfixIncrement", +@"{ + // Code size 17 (0x11) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: call ""nuint MyInt.op_Implicit(MyInt)"" + IL_0006: ldc.i4.1 + IL_0007: add + IL_0008: call ""MyInt MyInt.op_Implicit(nuint)"" + IL_000d: starg.s V_0 + IL_000f: ldarg.0 + IL_0010: ret +}"); + verifier.VerifyIL("Program.PrefixDecrement", +@"{ + // Code size 17 (0x11) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: call ""nuint MyInt.op_Implicit(MyInt)"" + IL_0006: ldc.i4.1 + IL_0007: sub + IL_0008: call ""MyInt MyInt.op_Implicit(nuint)"" + IL_000d: dup + IL_000e: starg.s V_0 + IL_0010: ret +}"); + verifier.VerifyIL("Program.PostfixDecrement", +@"{ + // Code size 17 (0x11) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: call ""nuint MyInt.op_Implicit(MyInt)"" + IL_0006: ldc.i4.1 + IL_0007: sub + IL_0008: call ""MyInt MyInt.op_Implicit(nuint)"" + IL_000d: starg.s V_0 + IL_000f: ldarg.0 + IL_0010: ret +}"); + verifier.VerifyIL("Program.Plus", +@"{ + // Code size 12 (0xc) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call ""nuint MyInt.op_Implicit(MyInt)"" + IL_0006: call ""MyInt MyInt.op_Implicit(nuint)"" + IL_000b: ret +}"); + verifier.VerifyIL("Program.Complement", +@"{ + // Code size 13 (0xd) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call ""nuint MyInt.op_Implicit(MyInt)"" + IL_0006: not + IL_0007: call ""MyInt MyInt.op_Implicit(nuint)"" + IL_000c: ret +}"); + } + + [Fact] + public void UnaryOperators_UserDefinedConversions_LiftedNInt() + { + string source = +@"using System; +class MyInt +{ + private readonly nint? _i; + internal MyInt(nint? i) => _i = i; + public static implicit operator nint?(MyInt i) => i._i; + public static implicit operator MyInt(nint? i) => new MyInt(i); + public override string ToString() => _i.ToString(); +} +class Program +{ + static void Main() + { + // ++i; + Evaluate(int.MinValue, PrefixIncrement); + Evaluate(-1, PrefixIncrement); + Evaluate(0, PrefixIncrement); + // i++; + Evaluate(int.MinValue, PostfixIncrement); + Evaluate(-1, PostfixIncrement); + Evaluate(0, PostfixIncrement); + // --i; + Evaluate(int.MaxValue, PrefixDecrement); + Evaluate(1, PrefixDecrement); + Evaluate(0, PrefixDecrement); + // i--; + Evaluate(int.MaxValue, PostfixDecrement); + Evaluate(1, PostfixDecrement); + Evaluate(0, PostfixDecrement); + // +i; + Evaluate(int.MinValue, Plus); + Evaluate(0, Plus); + Evaluate(int.MaxValue, Plus); + // -i; + Evaluate(int.MinValue, Minus); + Evaluate(0, Minus); + Evaluate(int.MaxValue, Minus); + // ~i; + Evaluate(int.MinValue, Complement); + Evaluate(0, Complement); + Evaluate(int.MaxValue, Complement); + } + static void Evaluate(nint i, Func f) + { + MyInt m = f(new MyInt(i)); + Console.WriteLine(m); + } + static MyInt PrefixIncrement(MyInt i) => ++i; + static MyInt PostfixIncrement(MyInt i) { i++; return i; } + static MyInt PrefixDecrement(MyInt i) => --i; + static MyInt PostfixDecrement(MyInt i) { i--; return i; } + static MyInt Plus(MyInt i) => +i; + static MyInt Minus(MyInt i) => -i; + static MyInt Complement(MyInt i) => ~i; +}"; + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.RegularPreview); + string expectedOutput = +$@"-2147483647 +0 +1 +-2147483647 +0 +1 +2147483646 +0 +-1 +2147483646 +0 +-1 +-2147483648 +0 +2147483647 +{(IntPtr.Size == 4 ? "-2147483648" : "2147483648")} +0 +-2147483647 +2147483647 +-1 +-2147483648"; + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput); + verifier.VerifyIL("Program.PrefixIncrement", +@"{ + // Code size 50 (0x32) + .maxstack 2 + .locals init (nint? V_0, + nint? V_1) + IL_0000: ldarg.0 + IL_0001: call ""nint? MyInt.op_Implicit(MyInt)"" + IL_0006: stloc.0 + IL_0007: ldloca.s V_0 + IL_0009: call ""bool nint?.HasValue.get"" + IL_000e: brtrue.s IL_001b + IL_0010: ldloca.s V_1 + IL_0012: initobj ""nint?"" + IL_0018: ldloc.1 + IL_0019: br.s IL_0029 + IL_001b: ldloca.s V_0 + IL_001d: call ""nint nint?.GetValueOrDefault()"" + IL_0022: ldc.i4.1 + IL_0023: add + IL_0024: newobj ""nint?..ctor(nint)"" + IL_0029: call ""MyInt MyInt.op_Implicit(nint?)"" + IL_002e: dup + IL_002f: starg.s V_0 + IL_0031: ret +}"); + verifier.VerifyIL("Program.PostfixIncrement", +@"{ + // Code size 50 (0x32) + .maxstack 2 + .locals init (nint? V_0, + nint? V_1) + IL_0000: ldarg.0 + IL_0001: call ""nint? MyInt.op_Implicit(MyInt)"" + IL_0006: stloc.0 + IL_0007: ldloca.s V_0 + IL_0009: call ""bool nint?.HasValue.get"" + IL_000e: brtrue.s IL_001b + IL_0010: ldloca.s V_1 + IL_0012: initobj ""nint?"" + IL_0018: ldloc.1 + IL_0019: br.s IL_0029 + IL_001b: ldloca.s V_0 + IL_001d: call ""nint nint?.GetValueOrDefault()"" + IL_0022: ldc.i4.1 + IL_0023: add + IL_0024: newobj ""nint?..ctor(nint)"" + IL_0029: call ""MyInt MyInt.op_Implicit(nint?)"" + IL_002e: starg.s V_0 + IL_0030: ldarg.0 + IL_0031: ret +}"); + verifier.VerifyIL("Program.PrefixDecrement", +@"{ + // Code size 50 (0x32) + .maxstack 2 + .locals init (nint? V_0, + nint? V_1) + IL_0000: ldarg.0 + IL_0001: call ""nint? MyInt.op_Implicit(MyInt)"" + IL_0006: stloc.0 + IL_0007: ldloca.s V_0 + IL_0009: call ""bool nint?.HasValue.get"" + IL_000e: brtrue.s IL_001b + IL_0010: ldloca.s V_1 + IL_0012: initobj ""nint?"" + IL_0018: ldloc.1 + IL_0019: br.s IL_0029 + IL_001b: ldloca.s V_0 + IL_001d: call ""nint nint?.GetValueOrDefault()"" + IL_0022: ldc.i4.1 + IL_0023: sub + IL_0024: newobj ""nint?..ctor(nint)"" + IL_0029: call ""MyInt MyInt.op_Implicit(nint?)"" + IL_002e: dup + IL_002f: starg.s V_0 + IL_0031: ret +}"); + verifier.VerifyIL("Program.PostfixDecrement", +@"{ + // Code size 50 (0x32) + .maxstack 2 + .locals init (nint? V_0, + nint? V_1) + IL_0000: ldarg.0 + IL_0001: call ""nint? MyInt.op_Implicit(MyInt)"" + IL_0006: stloc.0 + IL_0007: ldloca.s V_0 + IL_0009: call ""bool nint?.HasValue.get"" + IL_000e: brtrue.s IL_001b + IL_0010: ldloca.s V_1 + IL_0012: initobj ""nint?"" + IL_0018: ldloc.1 + IL_0019: br.s IL_0029 + IL_001b: ldloca.s V_0 + IL_001d: call ""nint nint?.GetValueOrDefault()"" + IL_0022: ldc.i4.1 + IL_0023: sub + IL_0024: newobj ""nint?..ctor(nint)"" + IL_0029: call ""MyInt MyInt.op_Implicit(nint?)"" + IL_002e: starg.s V_0 + IL_0030: ldarg.0 + IL_0031: ret +}"); + verifier.VerifyIL("Program.Plus", +@"{ + // Code size 45 (0x2d) + .maxstack 1 + .locals init (nint? V_0, + nint? V_1) + IL_0000: ldarg.0 + IL_0001: call ""nint? MyInt.op_Implicit(MyInt)"" + IL_0006: stloc.0 + IL_0007: ldloca.s V_0 + IL_0009: call ""bool nint?.HasValue.get"" + IL_000e: brtrue.s IL_001b + IL_0010: ldloca.s V_1 + IL_0012: initobj ""nint?"" + IL_0018: ldloc.1 + IL_0019: br.s IL_0027 + IL_001b: ldloca.s V_0 + IL_001d: call ""nint nint?.GetValueOrDefault()"" + IL_0022: newobj ""nint?..ctor(nint)"" + IL_0027: call ""MyInt MyInt.op_Implicit(nint?)"" + IL_002c: ret +}"); + verifier.VerifyIL("Program.Minus", +@"{ + // Code size 46 (0x2e) + .maxstack 1 + .locals init (nint? V_0, + nint? V_1) + IL_0000: ldarg.0 + IL_0001: call ""nint? MyInt.op_Implicit(MyInt)"" + IL_0006: stloc.0 + IL_0007: ldloca.s V_0 + IL_0009: call ""bool nint?.HasValue.get"" + IL_000e: brtrue.s IL_001b + IL_0010: ldloca.s V_1 + IL_0012: initobj ""nint?"" + IL_0018: ldloc.1 + IL_0019: br.s IL_0028 + IL_001b: ldloca.s V_0 + IL_001d: call ""nint nint?.GetValueOrDefault()"" + IL_0022: neg + IL_0023: newobj ""nint?..ctor(nint)"" + IL_0028: call ""MyInt MyInt.op_Implicit(nint?)"" + IL_002d: ret +}"); + verifier.VerifyIL("Program.Complement", +@"{ + // Code size 46 (0x2e) + .maxstack 1 + .locals init (nint? V_0, + nint? V_1) + IL_0000: ldarg.0 + IL_0001: call ""nint? MyInt.op_Implicit(MyInt)"" + IL_0006: stloc.0 + IL_0007: ldloca.s V_0 + IL_0009: call ""bool nint?.HasValue.get"" + IL_000e: brtrue.s IL_001b + IL_0010: ldloca.s V_1 + IL_0012: initobj ""nint?"" + IL_0018: ldloc.1 + IL_0019: br.s IL_0028 + IL_001b: ldloca.s V_0 + IL_001d: call ""nint nint?.GetValueOrDefault()"" + IL_0022: not + IL_0023: newobj ""nint?..ctor(nint)"" + IL_0028: call ""MyInt MyInt.op_Implicit(nint?)"" + IL_002d: ret +}"); + } + + [Fact] + public void UnaryOperators_UserDefinedConversions_LiftedNUInt() + { + string source = +@"using System; +class MyInt +{ + private readonly nuint? _i; + internal MyInt(nuint? i) => _i = i; + public static implicit operator nuint?(MyInt i) => i._i; + public static implicit operator MyInt(nuint? i) => new MyInt(i); + public override string ToString() => _i.ToString(); +} +class Program +{ + static void Main() + { + // ++i; + Evaluate(0, PrefixIncrement); + Evaluate(uint.MaxValue - 1, PrefixIncrement); + // i++; + Evaluate(0, PostfixIncrement); + Evaluate(uint.MaxValue - 1, PostfixIncrement); + // --i; + Evaluate(1, PrefixDecrement); + Evaluate(uint.MaxValue, PrefixDecrement); + // i--; + Evaluate(1, PostfixDecrement); + Evaluate(uint.MaxValue, PostfixDecrement); + // +i; + Evaluate(0, Plus); + Evaluate(uint.MaxValue, Plus); + // ~i; + Evaluate(0, Complement); + Evaluate(uint.MaxValue, Complement); + } + static void Evaluate(nuint i, Func f) + { + MyInt m = f(new MyInt(i)); + Console.WriteLine(m); + } + static MyInt PrefixIncrement(MyInt i) => ++i; + static MyInt PostfixIncrement(MyInt i) { i++; return i; } + static MyInt PrefixDecrement(MyInt i) => --i; + static MyInt PostfixDecrement(MyInt i) { i--; return i; } + static MyInt Plus(MyInt i) => +i; + static MyInt Complement(MyInt i) => ~i; +}"; + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.RegularPreview); + string expectedOutput = +$@"1 +4294967295 +1 +4294967295 +0 +4294967294 +0 +4294967294 +0 +4294967295 +{(IntPtr.Size == 4 ? "4294967295" : "18446744073709551615")} +{(IntPtr.Size == 4 ? "0" : "18446744069414584320")}"; + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput); + verifier.VerifyIL("Program.PrefixIncrement", +@"{ + // Code size 50 (0x32) + .maxstack 2 + .locals init (nuint? V_0, + nuint? V_1) + IL_0000: ldarg.0 + IL_0001: call ""nuint? MyInt.op_Implicit(MyInt)"" + IL_0006: stloc.0 + IL_0007: ldloca.s V_0 + IL_0009: call ""bool nuint?.HasValue.get"" + IL_000e: brtrue.s IL_001b + IL_0010: ldloca.s V_1 + IL_0012: initobj ""nuint?"" + IL_0018: ldloc.1 + IL_0019: br.s IL_0029 + IL_001b: ldloca.s V_0 + IL_001d: call ""nuint nuint?.GetValueOrDefault()"" + IL_0022: ldc.i4.1 + IL_0023: add + IL_0024: newobj ""nuint?..ctor(nuint)"" + IL_0029: call ""MyInt MyInt.op_Implicit(nuint?)"" + IL_002e: dup + IL_002f: starg.s V_0 + IL_0031: ret +}"); + verifier.VerifyIL("Program.PostfixIncrement", +@"{ + // Code size 50 (0x32) + .maxstack 2 + .locals init (nuint? V_0, + nuint? V_1) + IL_0000: ldarg.0 + IL_0001: call ""nuint? MyInt.op_Implicit(MyInt)"" + IL_0006: stloc.0 + IL_0007: ldloca.s V_0 + IL_0009: call ""bool nuint?.HasValue.get"" + IL_000e: brtrue.s IL_001b + IL_0010: ldloca.s V_1 + IL_0012: initobj ""nuint?"" + IL_0018: ldloc.1 + IL_0019: br.s IL_0029 + IL_001b: ldloca.s V_0 + IL_001d: call ""nuint nuint?.GetValueOrDefault()"" + IL_0022: ldc.i4.1 + IL_0023: add + IL_0024: newobj ""nuint?..ctor(nuint)"" + IL_0029: call ""MyInt MyInt.op_Implicit(nuint?)"" + IL_002e: starg.s V_0 + IL_0030: ldarg.0 + IL_0031: ret +}"); + verifier.VerifyIL("Program.PrefixDecrement", +@"{ + // Code size 50 (0x32) + .maxstack 2 + .locals init (nuint? V_0, + nuint? V_1) + IL_0000: ldarg.0 + IL_0001: call ""nuint? MyInt.op_Implicit(MyInt)"" + IL_0006: stloc.0 + IL_0007: ldloca.s V_0 + IL_0009: call ""bool nuint?.HasValue.get"" + IL_000e: brtrue.s IL_001b + IL_0010: ldloca.s V_1 + IL_0012: initobj ""nuint?"" + IL_0018: ldloc.1 + IL_0019: br.s IL_0029 + IL_001b: ldloca.s V_0 + IL_001d: call ""nuint nuint?.GetValueOrDefault()"" + IL_0022: ldc.i4.1 + IL_0023: sub + IL_0024: newobj ""nuint?..ctor(nuint)"" + IL_0029: call ""MyInt MyInt.op_Implicit(nuint?)"" + IL_002e: dup + IL_002f: starg.s V_0 + IL_0031: ret +}"); + verifier.VerifyIL("Program.PostfixDecrement", +@"{ + // Code size 50 (0x32) + .maxstack 2 + .locals init (nuint? V_0, + nuint? V_1) + IL_0000: ldarg.0 + IL_0001: call ""nuint? MyInt.op_Implicit(MyInt)"" + IL_0006: stloc.0 + IL_0007: ldloca.s V_0 + IL_0009: call ""bool nuint?.HasValue.get"" + IL_000e: brtrue.s IL_001b + IL_0010: ldloca.s V_1 + IL_0012: initobj ""nuint?"" + IL_0018: ldloc.1 + IL_0019: br.s IL_0029 + IL_001b: ldloca.s V_0 + IL_001d: call ""nuint nuint?.GetValueOrDefault()"" + IL_0022: ldc.i4.1 + IL_0023: sub + IL_0024: newobj ""nuint?..ctor(nuint)"" + IL_0029: call ""MyInt MyInt.op_Implicit(nuint?)"" + IL_002e: starg.s V_0 + IL_0030: ldarg.0 + IL_0031: ret +}"); + verifier.VerifyIL("Program.Plus", +@"{ + // Code size 45 (0x2d) + .maxstack 1 + .locals init (nuint? V_0, + nuint? V_1) + IL_0000: ldarg.0 + IL_0001: call ""nuint? MyInt.op_Implicit(MyInt)"" + IL_0006: stloc.0 + IL_0007: ldloca.s V_0 + IL_0009: call ""bool nuint?.HasValue.get"" + IL_000e: brtrue.s IL_001b + IL_0010: ldloca.s V_1 + IL_0012: initobj ""nuint?"" + IL_0018: ldloc.1 + IL_0019: br.s IL_0027 + IL_001b: ldloca.s V_0 + IL_001d: call ""nuint nuint?.GetValueOrDefault()"" + IL_0022: newobj ""nuint?..ctor(nuint)"" + IL_0027: call ""MyInt MyInt.op_Implicit(nuint?)"" + IL_002c: ret +}"); + verifier.VerifyIL("Program.Complement", +@"{ + // Code size 46 (0x2e) + .maxstack 1 + .locals init (nuint? V_0, + nuint? V_1) + IL_0000: ldarg.0 + IL_0001: call ""nuint? MyInt.op_Implicit(MyInt)"" + IL_0006: stloc.0 + IL_0007: ldloca.s V_0 + IL_0009: call ""bool nuint?.HasValue.get"" + IL_000e: brtrue.s IL_001b + IL_0010: ldloca.s V_1 + IL_0012: initobj ""nuint?"" + IL_0018: ldloc.1 + IL_0019: br.s IL_0028 + IL_001b: ldloca.s V_0 + IL_001d: call ""nuint nuint?.GetValueOrDefault()"" + IL_0022: not + IL_0023: newobj ""nuint?..ctor(nuint)"" + IL_0028: call ""MyInt MyInt.op_Implicit(nuint?)"" + IL_002d: ret +}"); + } + + [Fact] + public void BinaryOperators() + { + void binaryOps(string op, string leftType, string rightType, string expectedSymbol1 = null, string expectedSymbol2 = "", DiagnosticDescription[] diagnostics1 = null, DiagnosticDescription[] diagnostics2 = null) + { + binaryOp(op, leftType, rightType, expectedSymbol1, diagnostics1); + binaryOp(op, rightType, leftType, expectedSymbol2 == "" ? expectedSymbol1 : expectedSymbol2, diagnostics2 ?? diagnostics1); + } + + void binaryOp(string op, string leftType, string rightType, string expectedSymbol, DiagnosticDescription[] diagnostics) + { + if (expectedSymbol == null && diagnostics == null) + { + diagnostics = new[] { Diagnostic(ErrorCode.ERR_BadBinaryOps, $"x {op} y").WithArguments(op, leftType, rightType) }; + } + binaryOperator(op, leftType, rightType, expectedSymbol, diagnostics ?? Array.Empty()); + } + + var arithmeticOperators = new[] + { + ("-", "op_Subtraction"), + ("*", "op_Multiply"), + ("/", "op_Division"), + ("%", "op_Modulus"), + }; + var additionOperators = new[] + { + ("+", "op_Addition"), + }; + var comparisonOperators = new[] + { + ("<", "op_LessThan"), + ("<=", "op_LessThanOrEqual"), + (">", "op_GreaterThan"), + (">=", "op_GreaterThanOrEqual"), + }; + var shiftOperators = new[] + { + ("<<", "op_LeftShift"), + (">>", "op_RightShift"), + }; + var equalityOperators = new[] + { + ("==", "op_Equality"), + ("!=", "op_Inequality"), + }; + var logicalOperators = new[] + { + ("&", "op_BitwiseAnd"), + ("|", "op_BitwiseOr"), + ("^", "op_ExclusiveOr"), + }; + + foreach ((string symbol, string name) in arithmeticOperators) + { + binaryOps(symbol, "nint", "object"); + binaryOps(symbol, "nint", "string"); + // PROTOTYPE: Test all: + if (symbol == "*") binaryOps(symbol, "nint", "void*", null, null, new[] { Diagnostic(ErrorCode.ERR_BadBinaryOps, $"x {symbol} y").WithArguments(symbol, "nint", "void*"), Diagnostic(ErrorCode.ERR_VoidError, $"x {symbol} y") }, new[] { Diagnostic(ErrorCode.ERR_BadBinaryOps, $"x {symbol} y").WithArguments(symbol, "void*", "nint"), Diagnostic(ErrorCode.ERR_VoidError, $"x {symbol} y") }); + binaryOps(symbol, "nint", "bool"); + binaryOps(symbol, "nint", "char", $"nint nint.{name}(nint left, nint right)"); + binaryOps(symbol, "nint", "sbyte", $"nint nint.{name}(nint left, nint right)"); + binaryOps(symbol, "nint", "byte", $"nint nint.{name}(nint left, nint right)"); + binaryOps(symbol, "nint", "short", $"nint nint.{name}(nint left, nint right)"); + binaryOps(symbol, "nint", "ushort", $"nint nint.{name}(nint left, nint right)"); + binaryOps(symbol, "nint", "int", $"nint nint.{name}(nint left, nint right)"); + binaryOps(symbol, "nint", "uint", $"long long.{name}(long left, long right)"); + binaryOps(symbol, "nint", "nint", $"nint nint.{name}(nint left, nint right)"); + binaryOps(symbol, "nint", "nuint", null, null, new[] { Diagnostic(ErrorCode.ERR_AmbigBinaryOps, $"x {symbol} y").WithArguments(symbol, "nint", "nuint") }, new[] { Diagnostic(ErrorCode.ERR_AmbigBinaryOps, $"x {symbol} y").WithArguments(symbol, "nuint", "nint") }); + binaryOps(symbol, "nint", "long", $"long long.{name}(long left, long right)"); + binaryOps(symbol, "nint", "ulong", null, null, new[] { Diagnostic(ErrorCode.ERR_AmbigBinaryOps, $"x {symbol} y").WithArguments(symbol, "nint", "ulong") }, new[] { Diagnostic(ErrorCode.ERR_AmbigBinaryOps, $"x {symbol} y").WithArguments(symbol, "ulong", "nint") }); + binaryOps(symbol, "nint", "float", $"float float.{name}(float left, float right)"); binaryOps(symbol, "nint", "double", $"double double.{name}(double left, double right)"); binaryOps(symbol, "nint", "decimal", $"decimal decimal.{name}(decimal left, decimal right)"); - //getArgs(builder, symbol, "nint", "System.IntPtr"); // PROTOTYPE: Not handled. + //binaryOps(symbol, "nint", "System.IntPtr"); // PROTOTYPE: Not handled. binaryOps(symbol, "nint", "System.UIntPtr"); binaryOps(symbol, "nint", "bool?"); binaryOps(symbol, "nint", "char?", $"nint nint.{name}(nint left, nint right)"); @@ -1984,7 +4039,7 @@ void binaryOp(string op, string leftType, string rightType, string expectedSymbo binaryOps(symbol, "nint", "float?", $"float float.{name}(float left, float right)"); binaryOps(symbol, "nint", "double?", $"double double.{name}(double left, double right)"); binaryOps(symbol, "nint", "decimal?", $"decimal decimal.{name}(decimal left, decimal right)"); - //getArgs(builder, symbol, "nint", "System.IntPtr?"); // PROTOTYPE: Not handled. + //binaryOps(symbol, "nint", "System.IntPtr?"); // PROTOTYPE: Not handled. binaryOps(symbol, "nint", "System.UIntPtr?"); binaryOps(symbol, "nint", "object"); binaryOps(symbol, "nint?", "string"); @@ -2005,7 +4060,7 @@ void binaryOp(string op, string leftType, string rightType, string expectedSymbo binaryOps(symbol, "nint?", "float", $"float float.{name}(float left, float right)"); binaryOps(symbol, "nint?", "double", $"double double.{name}(double left, double right)"); binaryOps(symbol, "nint?", "decimal", $"decimal decimal.{name}(decimal left, decimal right)"); - //getArgs(builder, symbol, "nint?", "System.IntPtr"); // PROTOTYPE: Not handled. + //binaryOps(symbol, "nint?", "System.IntPtr"); // PROTOTYPE: Not handled. binaryOps(symbol, "nint?", "System.UIntPtr"); binaryOps(symbol, "nint?", "bool?"); binaryOps(symbol, "nint?", "char?", $"nint nint.{name}(nint left, nint right)"); @@ -2022,7 +4077,7 @@ void binaryOp(string op, string leftType, string rightType, string expectedSymbo binaryOps(symbol, "nint?", "float?", $"float float.{name}(float left, float right)"); binaryOps(symbol, "nint?", "double?", $"double double.{name}(double left, double right)"); binaryOps(symbol, "nint?", "decimal?", $"decimal decimal.{name}(decimal left, decimal right)"); - //getArgs(builder, symbol, "nint?", "System.IntPtr?"); // PROTOTYPE: Not handled. + //binaryOps(symbol, "nint?", "System.IntPtr?"); // PROTOTYPE: Not handled. binaryOps(symbol, "nint?", "System.UIntPtr?"); binaryOps(symbol, "nuint", "object"); binaryOps(symbol, "nuint", "string"); @@ -2044,7 +4099,7 @@ void binaryOp(string op, string leftType, string rightType, string expectedSymbo binaryOps(symbol, "nuint", "double", $"double double.{name}(double left, double right)"); binaryOps(symbol, "nuint", "decimal", $"decimal decimal.{name}(decimal left, decimal right)"); binaryOps(symbol, "nuint", "System.IntPtr"); - //getArgs(builder, symbol, "nuint", "System.UIntPtr"); // PROTOTYPE: Not handled. + //binaryOps(symbol, "nuint", "System.UIntPtr"); // PROTOTYPE: Not handled. binaryOps(symbol, "nuint", "bool?"); binaryOps(symbol, "nuint", "char?", $"nuint nuint.{name}(nuint left, nuint right)"); binaryOps(symbol, "nuint", "sbyte?", $"nuint nuint.{name}(nuint left, nuint right)"); @@ -2061,7 +4116,7 @@ void binaryOp(string op, string leftType, string rightType, string expectedSymbo binaryOps(symbol, "nuint", "double?", $"double double.{name}(double left, double right)"); binaryOps(symbol, "nuint", "decimal?", $"decimal decimal.{name}(decimal left, decimal right)"); binaryOps(symbol, "nuint", "System.IntPtr?"); - //getArgs(builder, symbol, "nuint", "System.UIntPtr?"); // PROTOTYPE: Not handled. + //binaryOps(symbol, "nuint", "System.UIntPtr?"); // PROTOTYPE: Not handled. binaryOps(symbol, "nuint?", "object"); binaryOps(symbol, "nuint?", "string"); // PROTOTYPE: Test all: @@ -2082,7 +4137,7 @@ void binaryOp(string op, string leftType, string rightType, string expectedSymbo binaryOps(symbol, "nuint?", "double", $"double double.{name}(double left, double right)"); binaryOps(symbol, "nuint?", "decimal", $"decimal decimal.{name}(decimal left, decimal right)"); binaryOps(symbol, "nuint?", "System.IntPtr"); - //getArgs(builder, symbol, "nuint?", "System.UIntPtr"); // PROTOTYPE: Not handled. + //binaryOps(symbol, "nuint?", "System.UIntPtr"); // PROTOTYPE: Not handled. binaryOps(symbol, "nuint?", "bool?"); binaryOps(symbol, "nuint?", "char?", $"nuint nuint.{name}(nuint left, nuint right)"); binaryOps(symbol, "nuint?", "sbyte?", $"nuint nuint.{name}(nuint left, nuint right)"); @@ -2099,7 +4154,7 @@ void binaryOp(string op, string leftType, string rightType, string expectedSymbo binaryOps(symbol, "nuint?", "double?", $"double double.{name}(double left, double right)"); binaryOps(symbol, "nuint?", "decimal?", $"decimal decimal.{name}(decimal left, decimal right)"); binaryOps(symbol, "nuint?", "System.IntPtr?"); - //getArgs(builder, symbol, "nuint?", "System.UIntPtr?"); // PROTOTYPE: Not handled. + //binaryOps(symbol, "nuint?", "System.UIntPtr?"); // PROTOTYPE: Not handled. } foreach ((string symbol, string name) in comparisonOperators) @@ -2122,7 +4177,7 @@ void binaryOp(string op, string leftType, string rightType, string expectedSymbo binaryOps(symbol, "nint", "float", $"bool float.{name}(float left, float right)"); binaryOps(symbol, "nint", "double", $"bool double.{name}(double left, double right)"); binaryOps(symbol, "nint", "decimal", $"bool decimal.{name}(decimal left, decimal right)"); - //getArgs(builder, symbol, "nint", "System.IntPtr"); // PROTOTYPE: Not handled. + //binaryOps(symbol, "nint", "System.IntPtr"); // PROTOTYPE: Not handled. binaryOps(symbol, "nint", "System.UIntPtr"); binaryOps(symbol, "nint", "bool?"); binaryOps(symbol, "nint", "char?", $"bool nint.{name}(nint left, nint right)"); @@ -2139,7 +4194,7 @@ void binaryOp(string op, string leftType, string rightType, string expectedSymbo binaryOps(symbol, "nint", "float?", $"bool float.{name}(float left, float right)"); binaryOps(symbol, "nint", "double?", $"bool double.{name}(double left, double right)"); binaryOps(symbol, "nint", "decimal?", $"bool decimal.{name}(decimal left, decimal right)"); - //getArgs(builder, symbol, "nint", "System.IntPtr?"); // PROTOTYPE: Not handled. + //binaryOps(symbol, "nint", "System.IntPtr?"); // PROTOTYPE: Not handled. binaryOps(symbol, "nint", "System.UIntPtr?"); binaryOps(symbol, "nint", "object"); binaryOps(symbol, "nint?", "string"); @@ -2159,7 +4214,7 @@ void binaryOp(string op, string leftType, string rightType, string expectedSymbo binaryOps(symbol, "nint?", "float", $"bool float.{name}(float left, float right)"); binaryOps(symbol, "nint?", "double", $"bool double.{name}(double left, double right)"); binaryOps(symbol, "nint?", "decimal", $"bool decimal.{name}(decimal left, decimal right)"); - //getArgs(builder, symbol, "nint?", "System.IntPtr"); // PROTOTYPE: Not handled. + //binaryOps(symbol, "nint?", "System.IntPtr"); // PROTOTYPE: Not handled. binaryOps(symbol, "nint?", "System.UIntPtr"); binaryOps(symbol, "nint?", "bool?"); binaryOps(symbol, "nint?", "char?", $"bool nint.{name}(nint left, nint right)"); @@ -2176,7 +4231,7 @@ void binaryOp(string op, string leftType, string rightType, string expectedSymbo binaryOps(symbol, "nint?", "float?", $"bool float.{name}(float left, float right)"); binaryOps(symbol, "nint?", "double?", $"bool double.{name}(double left, double right)"); binaryOps(symbol, "nint?", "decimal?", $"bool decimal.{name}(decimal left, decimal right)"); - //getArgs(builder, symbol, "nint?", "System.IntPtr?"); // PROTOTYPE: Not handled. + //binaryOps(symbol, "nint?", "System.IntPtr?"); // PROTOTYPE: Not handled. binaryOps(symbol, "nint?", "System.UIntPtr?"); binaryOps(symbol, "nuint", "object"); binaryOps(symbol, "nuint", "string"); @@ -2197,7 +4252,7 @@ void binaryOp(string op, string leftType, string rightType, string expectedSymbo binaryOps(symbol, "nuint", "double", $"bool double.{name}(double left, double right)"); binaryOps(symbol, "nuint", "decimal", $"bool decimal.{name}(decimal left, decimal right)"); binaryOps(symbol, "nuint", "System.IntPtr"); - //getArgs(builder, symbol, "nuint", "System.UIntPtr"); // PROTOTYPE: Not handled. + //binaryOps(symbol, "nuint", "System.UIntPtr"); // PROTOTYPE: Not handled. binaryOps(symbol, "nuint", "bool?"); binaryOps(symbol, "nuint", "char?", $"bool nuint.{name}(nuint left, nuint right)"); binaryOps(symbol, "nuint", "sbyte?", $"bool nuint.{name}(nuint left, nuint right)"); @@ -2214,7 +4269,7 @@ void binaryOp(string op, string leftType, string rightType, string expectedSymbo binaryOps(symbol, "nuint", "double?", $"bool double.{name}(double left, double right)"); binaryOps(symbol, "nuint", "decimal?", $"bool decimal.{name}(decimal left, decimal right)"); binaryOps(symbol, "nuint", "System.IntPtr?"); - //getArgs(builder, symbol, "nuint", "System.UIntPtr?"); // PROTOTYPE: Not handled. + //binaryOps(symbol, "nuint", "System.UIntPtr?"); // PROTOTYPE: Not handled. binaryOps(symbol, "nuint?", "object"); binaryOps(symbol, "nuint?", "string"); binaryOps(symbol, "nuint?", "void*", null, null, new[] { Diagnostic(ErrorCode.ERR_BadBinaryOps, $"x {symbol} y").WithArguments(symbol, "nuint?", "void*") }, new[] { Diagnostic(ErrorCode.ERR_BadBinaryOps, $"x {symbol} y").WithArguments(symbol, "void*", "nuint?") }); @@ -2234,7 +4289,7 @@ void binaryOp(string op, string leftType, string rightType, string expectedSymbo binaryOps(symbol, "nuint?", "double", $"bool double.{name}(double left, double right)"); binaryOps(symbol, "nuint?", "decimal", $"bool decimal.{name}(decimal left, decimal right)"); binaryOps(symbol, "nuint?", "System.IntPtr"); - //getArgs(builder, symbol, "nuint?", "System.UIntPtr"); // PROTOTYPE: Not handled. + //binaryOps(symbol, "nuint?", "System.UIntPtr"); // PROTOTYPE: Not handled. binaryOps(symbol, "nuint?", "bool?"); binaryOps(symbol, "nuint?", "char?", $"bool nuint.{name}(nuint left, nuint right)"); binaryOps(symbol, "nuint?", "sbyte?", $"bool nuint.{name}(nuint left, nuint right)"); @@ -2251,7 +4306,7 @@ void binaryOp(string op, string leftType, string rightType, string expectedSymbo binaryOps(symbol, "nuint?", "double?", $"bool double.{name}(double left, double right)"); binaryOps(symbol, "nuint?", "decimal?", $"bool decimal.{name}(decimal left, decimal right)"); binaryOps(symbol, "nuint?", "System.IntPtr?"); - //getArgs(builder, symbol, "nuint?", "System.UIntPtr?"); // PROTOTYPE: Not handled. + //binaryOps(symbol, "nuint?", "System.UIntPtr?"); // PROTOTYPE: Not handled. } foreach ((string symbol, string name) in additionOperators) @@ -2274,7 +4329,7 @@ void binaryOp(string op, string leftType, string rightType, string expectedSymbo binaryOps(symbol, "nint", "float", $"float float.{name}(float left, float right)"); binaryOps(symbol, "nint", "double", $"double double.{name}(double left, double right)"); binaryOps(symbol, "nint", "decimal", $"decimal decimal.{name}(decimal left, decimal right)"); - //getArgs(builder, symbol, "nint", "System.IntPtr"); // PROTOTYPE: Not handled. + //binaryOps(symbol, "nint", "System.IntPtr"); // PROTOTYPE: Not handled. binaryOps(symbol, "nint", "System.UIntPtr"); binaryOps(symbol, "nint", "bool?"); binaryOps(symbol, "nint", "char?", $"nint nint.{name}(nint left, nint right)"); @@ -2291,7 +4346,7 @@ void binaryOp(string op, string leftType, string rightType, string expectedSymbo binaryOps(symbol, "nint", "float?", $"float float.{name}(float left, float right)"); binaryOps(symbol, "nint", "double?", $"double double.{name}(double left, double right)"); binaryOps(symbol, "nint", "decimal?", $"decimal decimal.{name}(decimal left, decimal right)"); - //getArgs(builder, symbol, "nint", "System.IntPtr?"); // PROTOTYPE: Not handled. + //binaryOps(symbol, "nint", "System.IntPtr?"); // PROTOTYPE: Not handled. binaryOps(symbol, "nint", "System.UIntPtr?"); binaryOps(symbol, "nint", "object"); binaryOps(symbol, "nint?", "string", $"string string.{name}(object left, string right)", $"string string.{name}(string left, object right)"); @@ -2311,7 +4366,7 @@ void binaryOp(string op, string leftType, string rightType, string expectedSymbo binaryOps(symbol, "nint?", "float", $"float float.{name}(float left, float right)"); binaryOps(symbol, "nint?", "double", $"double double.{name}(double left, double right)"); binaryOps(symbol, "nint?", "decimal", $"decimal decimal.{name}(decimal left, decimal right)"); - //getArgs(builder, symbol, "nint?", "System.IntPtr"); // PROTOTYPE: Not handled. + //binaryOps(symbol, "nint?", "System.IntPtr"); // PROTOTYPE: Not handled. binaryOps(symbol, "nint?", "System.UIntPtr"); binaryOps(symbol, "nint?", "bool?"); binaryOps(symbol, "nint?", "char?", $"nint nint.{name}(nint left, nint right)"); @@ -2328,7 +4383,7 @@ void binaryOp(string op, string leftType, string rightType, string expectedSymbo binaryOps(symbol, "nint?", "float?", $"float float.{name}(float left, float right)"); binaryOps(symbol, "nint?", "double?", $"double double.{name}(double left, double right)"); binaryOps(symbol, "nint?", "decimal?", $"decimal decimal.{name}(decimal left, decimal right)"); - //getArgs(builder, symbol, "nint?", "System.IntPtr?"); // PROTOTYPE: Not handled. + //binaryOps(symbol, "nint?", "System.IntPtr?"); // PROTOTYPE: Not handled. binaryOps(symbol, "nint?", "System.UIntPtr?"); binaryOps(symbol, "nuint", "object"); binaryOps(symbol, "nuint", "string", $"string string.{name}(object left, string right)", $"string string.{name}(string left, object right)"); @@ -2349,7 +4404,7 @@ void binaryOp(string op, string leftType, string rightType, string expectedSymbo binaryOps(symbol, "nuint", "double", $"double double.{name}(double left, double right)"); binaryOps(symbol, "nuint", "decimal", $"decimal decimal.{name}(decimal left, decimal right)"); binaryOps(symbol, "nuint", "System.IntPtr"); - //getArgs(builder, symbol, "nuint", "System.UIntPtr"); // PROTOTYPE: Not handled. + //binaryOps(symbol, "nuint", "System.UIntPtr"); // PROTOTYPE: Not handled. binaryOps(symbol, "nuint", "bool?"); binaryOps(symbol, "nuint", "char?", $"nuint nuint.{name}(nuint left, nuint right)"); binaryOps(symbol, "nuint", "sbyte?", $"nuint nuint.{name}(nuint left, nuint right)"); @@ -2366,7 +4421,7 @@ void binaryOp(string op, string leftType, string rightType, string expectedSymbo binaryOps(symbol, "nuint", "double?", $"double double.{name}(double left, double right)"); binaryOps(symbol, "nuint", "decimal?", $"decimal decimal.{name}(decimal left, decimal right)"); binaryOps(symbol, "nuint", "System.IntPtr?"); - //getArgs(builder, symbol, "nuint", "System.UIntPtr?"); // PROTOTYPE: Not handled. + //binaryOps(symbol, "nuint", "System.UIntPtr?"); // PROTOTYPE: Not handled. binaryOps(symbol, "nuint?", "object"); binaryOps(symbol, "nuint?", "string", $"string string.{name}(object left, string right)", $"string string.{name}(string left, object right)"); binaryOps(symbol, "nuint?", "void*", null, null, new[] { Diagnostic(ErrorCode.ERR_BadBinaryOps, "x + y").WithArguments(symbol, "nuint?", "void*"), Diagnostic(ErrorCode.ERR_VoidError, "x + y") }, new[] { Diagnostic(ErrorCode.ERR_BadBinaryOps, "x + y").WithArguments(symbol, "void*", "nuint?"), Diagnostic(ErrorCode.ERR_VoidError, "x + y") }); @@ -2386,7 +4441,7 @@ void binaryOp(string op, string leftType, string rightType, string expectedSymbo binaryOps(symbol, "nuint?", "double", $"double double.{name}(double left, double right)"); binaryOps(symbol, "nuint?", "decimal", $"decimal decimal.{name}(decimal left, decimal right)"); binaryOps(symbol, "nuint?", "System.IntPtr"); - //getArgs(builder, symbol, "nuint?", "System.UIntPtr"); // PROTOTYPE: Not handled. + //binaryOps(symbol, "nuint?", "System.UIntPtr"); // PROTOTYPE: Not handled. binaryOps(symbol, "nuint?", "bool?"); binaryOps(symbol, "nuint?", "char?", $"nuint nuint.{name}(nuint left, nuint right)"); binaryOps(symbol, "nuint?", "sbyte?", $"nuint nuint.{name}(nuint left, nuint right)"); @@ -2403,7 +4458,7 @@ void binaryOp(string op, string leftType, string rightType, string expectedSymbo binaryOps(symbol, "nuint?", "double?", $"double double.{name}(double left, double right)"); binaryOps(symbol, "nuint?", "decimal?", $"decimal decimal.{name}(decimal left, decimal right)"); binaryOps(symbol, "nuint?", "System.IntPtr?"); - //getArgs(builder, symbol, "nuint?", "System.UIntPtr?"); // PROTOTYPE: Not handled. + //binaryOps(symbol, "nuint?", "System.UIntPtr?"); // PROTOTYPE: Not handled. } foreach ((string symbol, string name) in shiftOperators) @@ -2578,7 +4633,7 @@ void binaryOp(string op, string leftType, string rightType, string expectedSymbo binaryOps(symbol, "nint", "float", $"bool float.{name}(float left, float right)"); binaryOps(symbol, "nint", "double", $"bool double.{name}(double left, double right)"); binaryOps(symbol, "nint", "decimal", $"bool decimal.{name}(decimal left, decimal right)"); - //getArgs(builder, symbol, "nint", "System.IntPtr"); // PROTOTYPE: Not handled. + //binaryOps(symbol, "nint", "System.IntPtr"); // PROTOTYPE: Not handled. binaryOps(symbol, "nint", "System.UIntPtr"); binaryOps(symbol, "nint", "bool?"); binaryOps(symbol, "nint", "char?", $"bool nint.{name}(nint left, nint right)"); @@ -2595,7 +4650,7 @@ void binaryOp(string op, string leftType, string rightType, string expectedSymbo binaryOps(symbol, "nint", "float?", $"bool float.{name}(float left, float right)"); binaryOps(symbol, "nint", "double?", $"bool double.{name}(double left, double right)"); binaryOps(symbol, "nint", "decimal?", $"bool decimal.{name}(decimal left, decimal right)"); - //getArgs(builder, symbol, "nint", "System.IntPtr?"); // PROTOTYPE: Not handled. + //binaryOps(symbol, "nint", "System.IntPtr?"); // PROTOTYPE: Not handled. binaryOps(symbol, "nint", "System.UIntPtr?"); binaryOps(symbol, "nint", "object"); binaryOps(symbol, "nint?", "string"); @@ -2615,7 +4670,7 @@ void binaryOp(string op, string leftType, string rightType, string expectedSymbo binaryOps(symbol, "nint?", "float", $"bool float.{name}(float left, float right)"); binaryOps(symbol, "nint?", "double", $"bool double.{name}(double left, double right)"); binaryOps(symbol, "nint?", "decimal", $"bool decimal.{name}(decimal left, decimal right)"); - //getArgs(builder, symbol, "nint?", "System.IntPtr"); // PROTOTYPE: Not handled. + //binaryOps(symbol, "nint?", "System.IntPtr"); // PROTOTYPE: Not handled. binaryOps(symbol, "nint?", "System.UIntPtr"); binaryOps(symbol, "nint?", "bool?"); binaryOps(symbol, "nint?", "char?", $"bool nint.{name}(nint left, nint right)"); @@ -2632,7 +4687,7 @@ void binaryOp(string op, string leftType, string rightType, string expectedSymbo binaryOps(symbol, "nint?", "float?", $"bool float.{name}(float left, float right)"); binaryOps(symbol, "nint?", "double?", $"bool double.{name}(double left, double right)"); binaryOps(symbol, "nint?", "decimal?", $"bool decimal.{name}(decimal left, decimal right)"); - //getArgs(builder, symbol, "nint?", "System.IntPtr?"); // PROTOTYPE: Not handled. + //binaryOps(symbol, "nint?", "System.IntPtr?"); // PROTOTYPE: Not handled. binaryOps(symbol, "nint?", "System.UIntPtr?"); binaryOps(symbol, "nuint", "object"); binaryOps(symbol, "nuint", "string"); @@ -2653,7 +4708,7 @@ void binaryOp(string op, string leftType, string rightType, string expectedSymbo binaryOps(symbol, "nuint", "double", $"bool double.{name}(double left, double right)"); binaryOps(symbol, "nuint", "decimal", $"bool decimal.{name}(decimal left, decimal right)"); binaryOps(symbol, "nuint", "System.IntPtr"); - //getArgs(builder, symbol, "nuint", "System.UIntPtr"); // PROTOTYPE: Not handled. + //binaryOps(symbol, "nuint", "System.UIntPtr"); // PROTOTYPE: Not handled. binaryOps(symbol, "nuint", "bool?"); binaryOps(symbol, "nuint", "char?", $"bool nuint.{name}(nuint left, nuint right)"); binaryOps(symbol, "nuint", "sbyte?", $"bool nuint.{name}(nuint left, nuint right)"); @@ -2670,7 +4725,7 @@ void binaryOp(string op, string leftType, string rightType, string expectedSymbo binaryOps(symbol, "nuint", "double?", $"bool double.{name}(double left, double right)"); binaryOps(symbol, "nuint", "decimal?", $"bool decimal.{name}(decimal left, decimal right)"); binaryOps(symbol, "nuint", "System.IntPtr?"); - //getArgs(builder, symbol, "nuint", "System.UIntPtr?"); // PROTOTYPE: Not handled. + //binaryOps(symbol, "nuint", "System.UIntPtr?"); // PROTOTYPE: Not handled. binaryOps(symbol, "nuint?", "object"); binaryOps(symbol, "nuint?", "string"); binaryOps(symbol, "nuint?", "void*", null, null, new[] { Diagnostic(ErrorCode.ERR_BadBinaryOps, $"x {symbol} y").WithArguments(symbol, "nuint?", "void*") }, new[] { Diagnostic(ErrorCode.ERR_BadBinaryOps, $"x {symbol} y").WithArguments(symbol, "void*", "nuint?") }); @@ -2690,7 +4745,7 @@ void binaryOp(string op, string leftType, string rightType, string expectedSymbo binaryOps(symbol, "nuint?", "double", $"bool double.{name}(double left, double right)"); binaryOps(symbol, "nuint?", "decimal", $"bool decimal.{name}(decimal left, decimal right)"); binaryOps(symbol, "nuint?", "System.IntPtr"); - //getArgs(builder, symbol, "nuint?", "System.UIntPtr"); // PROTOTYPE: Not handled. + //binaryOps(symbol, "nuint?", "System.UIntPtr"); // PROTOTYPE: Not handled. binaryOps(symbol, "nuint?", "bool?"); binaryOps(symbol, "nuint?", "char?", $"bool nuint.{name}(nuint left, nuint right)"); binaryOps(symbol, "nuint?", "sbyte?", $"bool nuint.{name}(nuint left, nuint right)"); @@ -2707,7 +4762,7 @@ void binaryOp(string op, string leftType, string rightType, string expectedSymbo binaryOps(symbol, "nuint?", "double?", $"bool double.{name}(double left, double right)"); binaryOps(symbol, "nuint?", "decimal?", $"bool decimal.{name}(decimal left, decimal right)"); binaryOps(symbol, "nuint?", "System.IntPtr?"); - //getArgs(builder, symbol, "nuint?", "System.UIntPtr?"); // PROTOTYPE: Not handled. + //binaryOps(symbol, "nuint?", "System.UIntPtr?"); // PROTOTYPE: Not handled. } foreach ((string symbol, string name) in logicalOperators) @@ -2730,7 +4785,7 @@ void binaryOp(string op, string leftType, string rightType, string expectedSymbo binaryOps(symbol, "nint", "float"); binaryOps(symbol, "nint", "double"); binaryOps(symbol, "nint", "decimal"); - //getArgs(builder, symbol, "nint", "System.IntPtr"); // PROTOTYPE: Not handled. + //binaryOps(symbol, "nint", "System.IntPtr"); // PROTOTYPE: Not handled. binaryOps(symbol, "nint", "System.UIntPtr"); binaryOps(symbol, "nint", "bool?"); binaryOps(symbol, "nint", "char?", $"nint nint.{name}(nint left, nint right)"); @@ -2747,7 +4802,7 @@ void binaryOp(string op, string leftType, string rightType, string expectedSymbo binaryOps(symbol, "nint", "float?"); binaryOps(symbol, "nint", "double?"); binaryOps(symbol, "nint", "decimal?"); - //getArgs(builder, symbol, "nint", "System.IntPtr?"); // PROTOTYPE: Not handled. + //binaryOps(symbol, "nint", "System.IntPtr?"); // PROTOTYPE: Not handled. binaryOps(symbol, "nint", "System.UIntPtr?"); binaryOps(symbol, "nint", "object"); binaryOps(symbol, "nint?", "string"); @@ -2767,7 +4822,7 @@ void binaryOp(string op, string leftType, string rightType, string expectedSymbo binaryOps(symbol, "nint?", "float"); binaryOps(symbol, "nint?", "double"); binaryOps(symbol, "nint?", "decimal"); - //getArgs(builder, symbol, "nint?", "System.IntPtr"); // PROTOTYPE: Not handled. + //binaryOps(symbol, "nint?", "System.IntPtr"); // PROTOTYPE: Not handled. binaryOps(symbol, "nint?", "System.UIntPtr"); binaryOps(symbol, "nint?", "bool?"); binaryOps(symbol, "nint?", "char?", $"nint nint.{name}(nint left, nint right)"); @@ -2784,7 +4839,7 @@ void binaryOp(string op, string leftType, string rightType, string expectedSymbo binaryOps(symbol, "nint?", "float?"); binaryOps(symbol, "nint?", "double?"); binaryOps(symbol, "nint?", "decimal?"); - //getArgs(builder, symbol, "nint?", "System.IntPtr?"); // PROTOTYPE: Not handled. + //binaryOps(symbol, "nint?", "System.IntPtr?"); // PROTOTYPE: Not handled. binaryOps(symbol, "nint?", "System.UIntPtr?"); binaryOps(symbol, "nuint", "object"); binaryOps(symbol, "nuint", "string"); @@ -2805,7 +4860,7 @@ void binaryOp(string op, string leftType, string rightType, string expectedSymbo binaryOps(symbol, "nuint", "double"); binaryOps(symbol, "nuint", "decimal"); binaryOps(symbol, "nuint", "System.IntPtr"); - //getArgs(builder, symbol, "nuint", "System.UIntPtr"); // PROTOTYPE: Not handled. + //binaryOps(symbol, "nuint", "System.UIntPtr"); // PROTOTYPE: Not handled. binaryOps(symbol, "nuint", "bool?"); binaryOps(symbol, "nuint", "char?", $"nuint nuint.{name}(nuint left, nuint right)"); binaryOps(symbol, "nuint", "sbyte?", $"nuint nuint.{name}(nuint left, nuint right)"); @@ -2822,7 +4877,7 @@ void binaryOp(string op, string leftType, string rightType, string expectedSymbo binaryOps(symbol, "nuint", "double?"); binaryOps(symbol, "nuint", "decimal?"); binaryOps(symbol, "nuint", "System.IntPtr?"); - //getArgs(builder, symbol, "nuint", "System.UIntPtr?"); // PROTOTYPE: Not handled. + //binaryOps(symbol, "nuint", "System.UIntPtr?"); // PROTOTYPE: Not handled. binaryOps(symbol, "nuint?", "object"); binaryOps(symbol, "nuint?", "string"); binaryOps(symbol, "nuint?", "void*", null, null, new[] { Diagnostic(ErrorCode.ERR_BadBinaryOps, $"x {symbol} y").WithArguments(symbol, "nuint?", "void*"), Diagnostic(ErrorCode.ERR_VoidError, $"x {symbol} y") }, new[] { Diagnostic(ErrorCode.ERR_BadBinaryOps, $"x {symbol} y").WithArguments(symbol, "void*", "nuint?"), Diagnostic(ErrorCode.ERR_VoidError, $"x {symbol} y") }); @@ -2842,7 +4897,7 @@ void binaryOp(string op, string leftType, string rightType, string expectedSymbo binaryOps(symbol, "nuint?", "double"); binaryOps(symbol, "nuint?", "decimal"); binaryOps(symbol, "nuint?", "System.IntPtr"); - //getArgs(builder, symbol, "nuint?", "System.UIntPtr"); // PROTOTYPE: Not handled. + //binaryOps(symbol, "nuint?", "System.UIntPtr"); // PROTOTYPE: Not handled. binaryOps(symbol, "nuint?", "bool?"); binaryOps(symbol, "nuint?", "char?", $"nuint nuint.{name}(nuint left, nuint right)"); binaryOps(symbol, "nuint?", "sbyte?", $"nuint nuint.{name}(nuint left, nuint right)"); @@ -2859,36 +4914,36 @@ void binaryOp(string op, string leftType, string rightType, string expectedSymbo binaryOps(symbol, "nuint?", "double?"); binaryOps(symbol, "nuint?", "decimal?"); binaryOps(symbol, "nuint?", "System.IntPtr?"); - //getArgs(builder, symbol, "nuint?", "System.UIntPtr?"); // PROTOTYPE: Not handled. + //binaryOps(symbol, "nuint?", "System.UIntPtr?"); // PROTOTYPE: Not handled. } - } - private void BinaryOperator(string op, string leftType, string rightType, string expectedSymbol, DiagnosticDescription[] expectedDiagnostics) - { - bool useUnsafeContext = useUnsafe(leftType) || useUnsafe(rightType); - string source = -$@"class Program + void binaryOperator(string op, string leftType, string rightType, string expectedSymbol, DiagnosticDescription[] expectedDiagnostics) + { + bool useUnsafeContext = useUnsafe(leftType) || useUnsafe(rightType); + string source = + $@"class Program {{ static {(useUnsafeContext ? "unsafe " : "")}object Evaluate({leftType} x, {rightType} y) {{ return x {op} y; }} }}"; - var comp = CreateCompilation(source, options: TestOptions.ReleaseDll.WithAllowUnsafe(useUnsafeContext), parseOptions: TestOptions.RegularPreview); - comp.VerifyDiagnostics(expectedDiagnostics); + var comp = CreateCompilation(source, options: TestOptions.ReleaseDll.WithAllowUnsafe(useUnsafeContext), parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics(expectedDiagnostics); - var tree = comp.SyntaxTrees[0]; - var model = comp.GetSemanticModel(tree); - var expr = tree.GetRoot().DescendantNodes().OfType().Single(); - var symbolInfo = model.GetSymbolInfo(expr); - Assert.Equal(expectedSymbol, symbolInfo.Symbol?.ToDisplayString(SymbolDisplayFormat.TestFormat.WithMiscellaneousOptions(SymbolDisplayMiscellaneousOptions.UseSpecialTypes))); + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var expr = tree.GetRoot().DescendantNodes().OfType().Single(); + var symbolInfo = model.GetSymbolInfo(expr); + Assert.Equal(expectedSymbol, symbolInfo.Symbol?.ToDisplayString(SymbolDisplayFormat.TestFormat.WithMiscellaneousOptions(SymbolDisplayMiscellaneousOptions.UseSpecialTypes))); - if (expectedDiagnostics.Length == 0) - { - CompileAndVerify(comp); - } + if (expectedDiagnostics.Length == 0) + { + CompileAndVerify(comp); + } - static bool useUnsafe(string underlyingType) => underlyingType == "void*"; + static bool useUnsafe(string type) => type == "void*"; + } } [Fact] diff --git a/src/Compilers/Core/Portable/CodeGen/ILBuilderEmit.cs b/src/Compilers/Core/Portable/CodeGen/ILBuilderEmit.cs index 6d500cffaee..d7548de5404 100644 --- a/src/Compilers/Core/Portable/CodeGen/ILBuilderEmit.cs +++ b/src/Compilers/Core/Portable/CodeGen/ILBuilderEmit.cs @@ -597,6 +597,12 @@ internal void EmitConstantValue(ConstantValue value) case ConstantValueTypeDiscriminator.UInt64: EmitLongConstant(value.Int64Value); break; + case ConstantValueTypeDiscriminator.NInt: + EmitNativeIntConstant(value.Int32Value); + break; + case ConstantValueTypeDiscriminator.NUInt: + EmitNativeIntConstant(value.UInt32Value); + break; case ConstantValueTypeDiscriminator.Single: EmitSingleConstant(value.SingleValue); break; @@ -695,6 +701,24 @@ internal void EmitLongConstant(long value) } } + internal void EmitNativeIntConstant(long value) + { + if (value >= int.MinValue && value <= int.MaxValue) + { + EmitIntConstant((int)value); + EmitOpCode(ILOpCode.Conv_i); + } + else if (value >= uint.MinValue && value <= uint.MaxValue) + { + EmitIntConstant(unchecked((int)value)); + EmitOpCode(ILOpCode.Conv_u); + } + else + { + throw ExceptionUtilities.UnexpectedValue(value); + } + } + internal void EmitSingleConstant(float value) { EmitOpCode(ILOpCode.Ldc_r4); diff --git a/src/Compilers/Core/Portable/ConstantValue.cs b/src/Compilers/Core/Portable/ConstantValue.cs index 917ae0b8db1..3b649ccc48d 100644 --- a/src/Compilers/Core/Portable/ConstantValue.cs +++ b/src/Compilers/Core/Portable/ConstantValue.cs @@ -233,6 +233,34 @@ public static ConstantValue Create(UInt64 value) return new ConstantValueI64(value); } + public static ConstantValue CreateNativeInt(Int32 value) + { + if (value == 0) + { + return ConstantValueDefault.NInt; + } + else if (value == 1) + { + return ConstantValueOne.NInt; + } + + return new ConstantValueNativeInt(value); + } + + public static ConstantValue CreateNativeUInt(UInt32 value) + { + if (value == 0) + { + return ConstantValueDefault.NUInt; + } + else if (value == 1) + { + return ConstantValueOne.NUInt; + } + + return new ConstantValueNativeInt(value); + } + public static ConstantValue Create(bool value) { if (value) @@ -345,8 +373,8 @@ public static ConstantValue Create(object value, ConstantValueTypeDiscriminator case ConstantValueTypeDiscriminator.UInt32: return Create((uint)value); case ConstantValueTypeDiscriminator.Int64: return Create((long)value); case ConstantValueTypeDiscriminator.UInt64: return Create((ulong)value); - case ConstantValueTypeDiscriminator.NInt: return Create((int)value); - case ConstantValueTypeDiscriminator.NUInt: return Create((uint)value); + case ConstantValueTypeDiscriminator.NInt: return CreateNativeInt((int)value); + case ConstantValueTypeDiscriminator.NUInt: return CreateNativeUInt((uint)value); case ConstantValueTypeDiscriminator.Char: return Create((char)value); case ConstantValueTypeDiscriminator.Boolean: return Create((bool)value); case ConstantValueTypeDiscriminator.Single: @@ -384,6 +412,8 @@ public static ConstantValue Default(ConstantValueTypeDiscriminator discriminator case ConstantValueTypeDiscriminator.UInt32: return ConstantValueDefault.UInt32; case ConstantValueTypeDiscriminator.Int64: return ConstantValueDefault.Int64; case ConstantValueTypeDiscriminator.UInt64: return ConstantValueDefault.UInt64; + case ConstantValueTypeDiscriminator.NInt: return ConstantValueDefault.NInt; + case ConstantValueTypeDiscriminator.NUInt: return ConstantValueDefault.NUInt; case ConstantValueTypeDiscriminator.Char: return ConstantValueDefault.Char; case ConstantValueTypeDiscriminator.Boolean: return ConstantValueDefault.Boolean; case ConstantValueTypeDiscriminator.Single: return ConstantValueDefault.Single; @@ -464,6 +494,8 @@ private static SpecialType GetSpecialType(ConstantValueTypeDiscriminator discrim case ConstantValueTypeDiscriminator.UInt32: return Boxes.Box(UInt32Value); case ConstantValueTypeDiscriminator.Int64: return Boxes.Box(Int64Value); case ConstantValueTypeDiscriminator.UInt64: return Boxes.Box(UInt64Value); + case ConstantValueTypeDiscriminator.NInt: return Boxes.Box(Int32Value); + case ConstantValueTypeDiscriminator.NUInt: return Boxes.Box(UInt32Value); case ConstantValueTypeDiscriminator.Char: return Boxes.Box(CharValue); case ConstantValueTypeDiscriminator.Boolean: return Boxes.Box(BooleanValue); case ConstantValueTypeDiscriminator.Single: return Boxes.Box(SingleValue); diff --git a/src/Compilers/Core/Portable/ConstantValueSpecialized.cs b/src/Compilers/Core/Portable/ConstantValueSpecialized.cs index 01f70b7dbbe..59a6abb3e24 100644 --- a/src/Compilers/Core/Portable/ConstantValueSpecialized.cs +++ b/src/Compilers/Core/Portable/ConstantValueSpecialized.cs @@ -303,6 +303,8 @@ private class ConstantValueDefault : ConstantValueDiscriminated public static readonly ConstantValueDefault UInt32 = new ConstantValueDefault(ConstantValueTypeDiscriminator.UInt32); public static readonly ConstantValueDefault Int64 = new ConstantValueDefault(ConstantValueTypeDiscriminator.Int64); public static readonly ConstantValueDefault UInt64 = new ConstantValueDefault(ConstantValueTypeDiscriminator.UInt64); + public static readonly ConstantValueDefault NInt = new ConstantValueDefault(ConstantValueTypeDiscriminator.NInt); + public static readonly ConstantValueDefault NUInt = new ConstantValueDefault(ConstantValueTypeDiscriminator.NUInt); public static readonly ConstantValueDefault Char = new ConstantValueDefault(ConstantValueTypeDiscriminator.Char); public static readonly ConstantValueDefault Single = new ConstantValueSingleZero(); public static readonly ConstantValueDefault Double = new ConstantValueDoubleZero(); @@ -475,6 +477,8 @@ private class ConstantValueOne : ConstantValueDiscriminated public static readonly ConstantValueOne UInt32 = new ConstantValueOne(ConstantValueTypeDiscriminator.UInt32); public static readonly ConstantValueOne Int64 = new ConstantValueOne(ConstantValueTypeDiscriminator.Int64); public static readonly ConstantValueOne UInt64 = new ConstantValueOne(ConstantValueTypeDiscriminator.UInt64); + public static readonly ConstantValueOne NInt = new ConstantValueOne(ConstantValueTypeDiscriminator.NInt); + public static readonly ConstantValueOne NUInt = new ConstantValueOne(ConstantValueTypeDiscriminator.NUInt); public static readonly ConstantValueOne Single = new ConstantValueOne(ConstantValueTypeDiscriminator.Single); public static readonly ConstantValueOne Double = new ConstantValueOne(ConstantValueTypeDiscriminator.Double); public static readonly ConstantValueOne Decimal = new ConstantValueDecimalOne(); @@ -754,6 +758,50 @@ public override bool Equals(ConstantValue? other) } } + private sealed class ConstantValueNativeInt : ConstantValueDiscriminated + { + // Constants are limited to 32-bit for portability. + private readonly int _value; + + public ConstantValueNativeInt(int value) + : base(ConstantValueTypeDiscriminator.NInt) + { + _value = value; + } + + public ConstantValueNativeInt(uint value) + : base(ConstantValueTypeDiscriminator.NUInt) + { + _value = unchecked((int)value); + } + + public override int Int32Value + { + get + { + return _value; + } + } + + public override uint UInt32Value + { + get + { + return unchecked((uint)_value); + } + } + + public override int GetHashCode() + { + return Hash.Combine(base.GetHashCode(), _value.GetHashCode()); + } + + public override bool Equals(ConstantValue? other) + { + return base.Equals(other) && _value == other.Int32Value; + } + } + private sealed class ConstantValueDouble : ConstantValueDiscriminated { private readonly double _value; -- GitLab