/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenCheckedTests.cs
2 955 строк
80 KB
Julien Couvreur
Bind switch expression with checked-aware binder (#61890)
17 июн 2022, 03:50
Не верифицирован
17 июн 2022, 03:50
0730214
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. #nullable disable using System.Linq; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen { public class CodeGenCheckedTests : CSharpTestBase { [Fact] public void CheckedExpression_Signed() { var source = @" class C { public static int Add(int a, int b) { return checked(a+b); } public static int Sub(int a, int b) { return checked(a-b); } public static int Mul(int a, int b) { return checked(a*b); } public static int Minus(int a) { return checked(-a); } } "; var verifier = CompileAndVerify(source); verifier.VerifyIL("C.Add", @" { // Code size 4 (0x4) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: add.ovf IL_0003: ret } "); verifier.VerifyIL("C.Sub", @" { // Code size 4 (0x4) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: sub.ovf IL_0003: ret } "); verifier.VerifyIL("C.Mul", @" { // Code size 4 (0x4) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: mul.ovf IL_0003: ret } "); verifier.VerifyIL("C.Minus", @" { // Code size 4 (0x4) .maxstack 2 IL_0000: ldc.i4.0 IL_0001: ldarg.0 IL_0002: sub.ovf IL_0003: ret } "); } [Fact] public void CheckedExpression_Unsigned() { var source = @" class C { public static long Add(uint a, uint b) { return checked(a+b); } public static long Sub(uint a, uint b) { return checked(a-b); } public static long Mul(uint a, uint b) { return checked(a*b); } public static long Minus(uint a) { return checked(-a); } } "; var verifier = CompileAndVerify(source); verifier.VerifyIL("C.Add", @" { // Code size 5 (0x5) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: add.ovf.un IL_0003: conv.u8 IL_0004: ret } "); verifier.VerifyIL("C.Sub", @" { // Code size 5 (0x5) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: sub.ovf.un IL_0003: conv.u8 IL_0004: ret } "); verifier.VerifyIL("C.Mul", @" { // Code size 5 (0x5) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: mul.ovf.un IL_0003: conv.u8 IL_0004: ret } "); verifier.VerifyIL("C.Minus", @" { // Code size 6 (0x6) .maxstack 2 IL_0000: ldc.i4.0 IL_0001: conv.i8 IL_0002: ldarg.0 IL_0003: conv.u8 IL_0004: sub.ovf IL_0005: ret } "); } [Fact] public void CheckedExpression_Enums() { var source = @" enum E { A } class C { public static E Add1(E a, int b) { return checked(a+b); } public static E Add2(int a, E b) { return checked(a+b); } public static E Sub(E a, E b) { return (E)checked(a-b); } public static E PostInc(E e) { return checked(e++); } public static E PreInc(E e) { return checked(--e); } public static E PostDec(E e) { return checked(e--); } public static E PreDec(E e) { return checked(--e); } } "; var verifier = CompileAndVerify(source); verifier.VerifyIL("C.Add1", @" { // Code size 4 (0x4) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: add.ovf IL_0003: ret } "); verifier.VerifyIL("C.Add2", @" { // Code size 4 (0x4) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: add.ovf IL_0003: ret } "); verifier.VerifyIL("C.Sub", @" { // Code size 4 (0x4) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: sub.ovf IL_0003: ret } "); verifier.VerifyIL("C.PostInc", @" { // Code size 7 (0x7) .maxstack 3 IL_0000: ldarg.0 IL_0001: dup IL_0002: ldc.i4.1 IL_0003: add.ovf IL_0004: starg.s V_0 IL_0006: ret } "); verifier.VerifyIL("C.PostDec", @" { // Code size 7 (0x7) .maxstack 3 IL_0000: ldarg.0 IL_0001: dup IL_0002: ldc.i4.1 IL_0003: sub.ovf IL_0004: starg.s V_0 IL_0006: ret } "); verifier.VerifyIL("C.PreInc", @" { // Code size 7 (0x7) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldc.i4.1 IL_0002: sub.ovf IL_0003: dup IL_0004: starg.s V_0 IL_0006: ret } "); verifier.VerifyIL("C.PreDec", @" { // Code size 7 (0x7) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldc.i4.1 IL_0002: sub.ovf IL_0003: dup IL_0004: starg.s V_0 IL_0006: ret } "); } [Fact] public void CheckedExpression_Pointers() { var source = @" enum E { A } unsafe struct C { public static C* Add_Int1(C* a, int b) { return checked(a+b); } public static C* Add_Int2(C* a, int b) { return checked(b+a); } public static C* Add_UInt1(C* a, uint b) { return checked(a+b); } public static C* Add_UInt2(C* a, uint b) { return checked(b+a); } public static C* Add_Long1(C* a, long b) { return checked(a+b); } public static C* Add_Long2(C* a, long b) { return checked(b+a); } public static C* Add_ULong1(C* a, ulong b) { return checked(a+b); } public static C* Add_ULong2(C* a, ulong b) { return checked(b+a); } public static C* Sub_Int(C* a, int b) { return checked(a-b); } public static C* Sub_UInt(C* a, uint b) { return checked(a-b); } public static C* Sub_Long(C* a, long b) { return checked(a-b); } public static C* Sub_ULong(C* a, ulong b) { return checked(a-b); } public static long Sub_Ptr(C* a, C* b) { return checked(a-b); } public static C* PostInc(C* a) { return checked(a++); } public static C* PostDec(C* a) { return checked(a--); } } "; var verifier = CompileAndVerify(source, options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails); // NOTE: unsigned addition verifier.VerifyIL("C.Add_Int1", @" { // Code size 12 (0xc) .maxstack 3 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: conv.i IL_0003: sizeof ""C"" IL_0009: mul.ovf IL_000a: add.ovf.un IL_000b: ret }"); // NOTE: signed addition verifier.VerifyIL("C.Add_Int2", @" { // Code size 12 (0xc) .maxstack 2 IL_0000: ldarg.1 IL_0001: conv.i IL_0002: sizeof ""C"" IL_0008: mul.ovf IL_0009: ldarg.0 IL_000a: add.ovf IL_000b: ret }"); // NOTE: unsigned addition verifier.VerifyIL("C.Add_UInt1", @"{ // Code size 14 (0xe) .maxstack 3 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: conv.u8 IL_0003: sizeof ""C"" IL_0009: conv.i8 IL_000a: mul.ovf IL_000b: conv.i IL_000c: add.ovf.un IL_000d: ret }"); // NOTE: signed addition verifier.VerifyIL("C.Add_UInt2", @" { // Code size 14 (0xe) .maxstack 2 IL_0000: ldarg.1 IL_0001: conv.u8 IL_0002: sizeof ""C"" IL_0008: conv.i8 IL_0009: mul.ovf IL_000a: conv.i IL_000b: ldarg.0 IL_000c: add.ovf IL_000d: ret }"); // NOTE: unsigned addition verifier.VerifyIL("C.Add_Long1", @" { // Code size 13 (0xd) .maxstack 3 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: sizeof ""C"" IL_0008: conv.i8 IL_0009: mul.ovf IL_000a: conv.i IL_000b: add.ovf.un IL_000c: ret }"); // NOTE: signed addition verifier.VerifyIL("C.Add_Long2", @" { // Code size 13 (0xd) .maxstack 2 IL_0000: ldarg.1 IL_0001: sizeof ""C"" IL_0007: conv.i8 IL_0008: mul.ovf IL_0009: conv.i IL_000a: ldarg.0 IL_000b: add.ovf IL_000c: ret }"); // NOTE: unsigned addition verifier.VerifyIL("C.Add_ULong1", @" { // Code size 13 (0xd) .maxstack 3 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: sizeof ""C"" IL_0008: conv.ovf.u8 IL_0009: mul.ovf.un IL_000a: conv.u IL_000b: add.ovf.un IL_000c: ret }"); // NOTE: unsigned addition (differs from previous Add_*2's) verifier.VerifyIL("C.Add_ULong2", @" { // Code size 13 (0xd) .maxstack 2 IL_0000: ldarg.1 IL_0001: sizeof ""C"" IL_0007: conv.ovf.u8 IL_0008: mul.ovf.un IL_0009: conv.u IL_000a: ldarg.0 IL_000b: add.ovf.un IL_000c: ret }"); verifier.VerifyIL("C.Sub_Int", @" { // Code size 12 (0xc) .maxstack 3 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: conv.i IL_0003: sizeof ""C"" IL_0009: mul.ovf IL_000a: sub.ovf.un IL_000b: ret }"); verifier.VerifyIL("C.Sub_UInt", @" { // Code size 14 (0xe) .maxstack 3 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: conv.u8 IL_0003: sizeof ""C"" IL_0009: conv.i8 IL_000a: mul.ovf IL_000b: conv.i IL_000c: sub.ovf.un IL_000d: ret }"); verifier.VerifyIL("C.Sub_Long", @" { // Code size 13 (0xd) .maxstack 3 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: sizeof ""C"" IL_0008: conv.i8 IL_0009: mul.ovf IL_000a: conv.i IL_000b: sub.ovf.un IL_000c: ret }"); verifier.VerifyIL("C.Sub_ULong", @" { // Code size 13 (0xd) .maxstack 3 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: sizeof ""C"" IL_0008: conv.ovf.u8 IL_0009: mul.ovf.un IL_000a: conv.u IL_000b: sub.ovf.un IL_000c: ret }"); verifier.VerifyIL("C.Sub_Ptr", @" { // Code size 12 (0xc) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: sub IL_0003: sizeof ""C"" IL_0009: div IL_000a: conv.i8 IL_000b: ret }"); verifier.VerifyIL("C.PostInc", @" { // Code size 12 (0xc) .maxstack 3 IL_0000: ldarg.0 IL_0001: dup IL_0002: sizeof ""C"" IL_0008: add.ovf.un IL_0009: starg.s V_0 IL_000b: ret }"); verifier.VerifyIL("C.PostDec", @" { // Code size 12 (0xc) .maxstack 3 IL_0000: ldarg.0 IL_0001: dup IL_0002: sizeof ""C"" IL_0008: sub.ovf.un IL_0009: starg.s V_0 IL_000b: ret }"); } [Fact] public void CheckedExpression_Optimizer() { var source = @" class C { static bool Local() { int a = 1; return checked(-a) != -1; } static bool LocalInc() { int a = 1; return checked(-(a++)) != -1; } } "; var verifier = CompileAndVerify(source); verifier.VerifyIL("C.Local", @" { // Code size 12 (0xc) .maxstack 2 .locals init (int V_0) //a IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldc.i4.0 IL_0003: ldloc.0 IL_0004: sub.ovf IL_0005: ldc.i4.m1 IL_0006: ceq IL_0008: ldc.i4.0 IL_0009: ceq IL_000b: ret } "); verifier.VerifyIL("C.LocalInc", @" { // Code size 16 (0x10) .maxstack 4 .locals init (int V_0) //a IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldc.i4.0 IL_0003: ldloc.0 IL_0004: dup IL_0005: ldc.i4.1 IL_0006: add.ovf IL_0007: stloc.0 IL_0008: sub.ovf IL_0009: ldc.i4.m1 IL_000a: ceq IL_000c: ldc.i4.0 IL_000d: ceq IL_000f: ret } "); } [Fact] public void CheckedExpression_IncDec() { var source = @" class C { public static int PostInc(int a) { return checked(a++); } public static int PreInc(int a) { return checked(++a); } public static int PostDec(int a) { return checked(a--); } public static int PreDec(int a) { return checked(--a); } } "; var verifier = CompileAndVerify(source); verifier.VerifyIL("C.PostInc", @" { // Code size 7 (0x7) .maxstack 3 IL_0000: ldarg.0 IL_0001: dup IL_0002: ldc.i4.1 IL_0003: add.ovf IL_0004: starg.s V_0 IL_0006: ret } "); verifier.VerifyIL("C.PreInc", @" { // Code size 7 (0x7) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldc.i4.1 IL_0002: add.ovf IL_0003: dup IL_0004: starg.s V_0 IL_0006: ret } "); verifier.VerifyIL("C.PostDec", @" { // Code size 7 (0x7) .maxstack 3 IL_0000: ldarg.0 IL_0001: dup IL_0002: ldc.i4.1 IL_0003: sub.ovf IL_0004: starg.s V_0 IL_0006: ret } "); verifier.VerifyIL("C.PreDec", @" { // Code size 7 (0x7) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldc.i4.1 IL_0002: sub.ovf IL_0003: dup IL_0004: starg.s V_0 IL_0006: ret } "); } [Fact] public void CheckedExpression_CompoundAssignment() { var source = @" class C { public static int Add(int a, int b) { return checked(a+=b); } public static int Sub(int a, int b) { return checked(a-=b); } public static int Mul(int a, int b) { return checked(a*=b); } public static int Div(int a, int b) { return checked(a/=b); } public static int Rem(int a, int b) { return checked(a%=b); } } "; var verifier = CompileAndVerify(source); verifier.VerifyIL("C.Add", @" { // Code size 7 (0x7) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: add.ovf IL_0003: dup IL_0004: starg.s V_0 IL_0006: ret } "); verifier.VerifyIL("C.Sub", @" { // Code size 7 (0x7) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: sub.ovf IL_0003: dup IL_0004: starg.s V_0 IL_0006: ret } "); verifier.VerifyIL("C.Mul", @" { // Code size 7 (0x7) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: mul.ovf IL_0003: dup IL_0004: starg.s V_0 IL_0006: ret } "); // both checked and unchecked generate the same instruction that checks overflow verifier.VerifyIL("C.Div", @" { // Code size 7 (0x7) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: div IL_0003: dup IL_0004: starg.s V_0 IL_0006: ret } "); verifier.VerifyIL("C.Rem", @" { // Code size 7 (0x7) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: rem IL_0003: dup IL_0004: starg.s V_0 IL_0006: ret } "); } [Fact] public void Checked_ImplicitConversions_CompoundAssignment() { var source = @" class C { public static int Add(short a) { return checked(a+=1000); } }"; var verifier = CompileAndVerify(source); verifier.VerifyIL("C.Add", @" { // Code size 12 (0xc) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldc.i4 0x3e8 IL_0006: add.ovf IL_0007: conv.ovf.i2 IL_0008: dup IL_0009: starg.s V_0 IL_000b: ret } "); } [Fact] public void Checked_ImplicitConversions_IncDec() { var source = @" class C { class X { public static implicit operator short(X a) { return 1; } public static implicit operator X(short a) { return new X(); } } static void PostIncUserDefined(X x) { checked { x++; } } public static int PostInc(short a) { return checked(a++); } public static int PreInc(short a) { return checked(++a); } static short? s = 0; static short? PostIncNullable() { checked { return s++; } } }"; var verifier = CompileAndVerify(source); verifier.VerifyIL("C.PostIncUserDefined", @" { // Code size 17 (0x11) .maxstack 2 IL_0000: ldarg.0 IL_0001: call ""short C.X.op_Implicit(C.X)"" IL_0006: ldc.i4.1 IL_0007: add.ovf IL_0008: conv.ovf.i2 IL_0009: call ""C.X C.X.op_Implicit(short)"" IL_000e: starg.s V_0 IL_0010: ret } "); verifier.VerifyIL("C.PostInc", @" { // Code size 8 (0x8) .maxstack 3 IL_0000: ldarg.0 IL_0001: dup IL_0002: ldc.i4.1 IL_0003: add.ovf IL_0004: conv.ovf.i2 IL_0005: starg.s V_0 IL_0007: ret } "); verifier.VerifyIL("C.PreInc", @" { // Code size 8 (0x8) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldc.i4.1 IL_0002: add.ovf IL_0003: conv.ovf.i2 IL_0004: dup IL_0005: starg.s V_0 IL_0007: ret } "); verifier.VerifyIL("C.PostIncNullable", @"{ // Code size 48 (0x30) .maxstack 3 .locals init (short? V_0, short? V_1) IL_0000: ldsfld ""short? C.s"" IL_0005: dup IL_0006: stloc.0 IL_0007: ldloca.s V_0 IL_0009: call ""bool short?.HasValue.get"" IL_000e: brtrue.s IL_001b IL_0010: ldloca.s V_1 IL_0012: initobj ""short?"" IL_0018: ldloc.1 IL_0019: br.s IL_002a IL_001b: ldloca.s V_0 IL_001d: call ""short short?.GetValueOrDefault()"" IL_0022: ldc.i4.1 IL_0023: add.ovf IL_0024: conv.ovf.i2 IL_0025: newobj ""short?..ctor(short)"" IL_002a: stsfld ""short? C.s"" IL_002f: ret } "); } [Fact] public void Checked_ImplicitConversions_ArraySize() { var source = @" class C { public static int[] ArraySize(long size) { checked { return new int[size]; } } } "; var verifier = CompileAndVerify(source); verifier.VerifyIL("C.ArraySize", @" { // Code size 8 (0x8) .maxstack 1 IL_0000: ldarg.0 IL_0001: conv.ovf.i IL_0002: newarr ""int"" IL_0007: ret } "); } [Fact] public void Checked_ImplicitConversions_ForEach() { var source = @" class C { static void ForEachString() { checked { foreach (byte b in ""hello"") { } } } static void ForEachVector() { checked { foreach (short b in new int[] {1}) { } } } static void ForEachMultiDimArray() { checked { foreach (short b in new int[,] {{1}}) { } } } } "; var verifier = CompileAndVerify(source); verifier.VerifyIL("C.ForEachString", @" { // Code size 33 (0x21) .maxstack 2 .locals init (string V_0, int V_1) IL_0000: ldstr ""hello"" IL_0005: stloc.0 IL_0006: ldc.i4.0 IL_0007: stloc.1 IL_0008: br.s IL_0017 IL_000a: ldloc.0 IL_000b: ldloc.1 IL_000c: callvirt ""char string.this[int].get"" IL_0011: conv.ovf.u1.un IL_0012: pop IL_0013: ldloc.1 IL_0014: ldc.i4.1 IL_0015: add IL_0016: stloc.1 IL_0017: ldloc.1 IL_0018: ldloc.0 IL_0019: callvirt ""int string.Length.get"" IL_001e: blt.s IL_000a IL_0020: ret } "); verifier.VerifyIL("C.ForEachVector", @" { // Code size 31 (0x1f) .maxstack 4 .locals init (int[] V_0, int V_1) IL_0000: ldc.i4.1 IL_0001: newarr ""int"" IL_0006: dup IL_0007: ldc.i4.0 IL_0008: ldc.i4.1 IL_0009: stelem.i4 IL_000a: stloc.0 IL_000b: ldc.i4.0 IL_000c: stloc.1 IL_000d: br.s IL_0018 IL_000f: ldloc.0 IL_0010: ldloc.1 IL_0011: ldelem.i4 IL_0012: conv.ovf.i2 IL_0013: pop IL_0014: ldloc.1 IL_0015: ldc.i4.1 IL_0016: add IL_0017: stloc.1 IL_0018: ldloc.1 IL_0019: ldloc.0 IL_001a: ldlen IL_001b: conv.i4 IL_001c: blt.s IL_000f IL_001e: ret } "); verifier.VerifyIL("C.ForEachMultiDimArray", @" { // Code size 85 (0x55) .maxstack 5 .locals init (int[,] V_0, int V_1, int V_2, int V_3, int V_4) IL_0000: ldc.i4.1 IL_0001: ldc.i4.1 IL_0002: newobj ""int[*,*]..ctor"" IL_0007: dup IL_0008: ldc.i4.0 IL_0009: ldc.i4.0 IL_000a: ldc.i4.1 IL_000b: call ""int[*,*].Set"" IL_0010: stloc.0 IL_0011: ldloc.0 IL_0012: ldc.i4.0 IL_0013: callvirt ""int System.Array.GetUpperBound(int)"" IL_0018: stloc.1 IL_0019: ldloc.0 IL_001a: ldc.i4.1 IL_001b: callvirt ""int System.Array.GetUpperBound(int)"" IL_0020: stloc.2 IL_0021: ldloc.0 IL_0022: ldc.i4.0 IL_0023: callvirt ""int System.Array.GetLowerBound(int)"" IL_0028: stloc.3 IL_0029: br.s IL_0050 IL_002b: ldloc.0 IL_002c: ldc.i4.1 IL_002d: callvirt ""int System.Array.GetLowerBound(int)"" IL_0032: stloc.s V_4 IL_0034: br.s IL_0047 IL_0036: ldloc.0 IL_0037: ldloc.3 IL_0038: ldloc.s V_4 IL_003a: call ""int[*,*].Get"" IL_003f: conv.ovf.i2 IL_0040: pop IL_0041: ldloc.s V_4 IL_0043: ldc.i4.1 IL_0044: add IL_0045: stloc.s V_4 IL_0047: ldloc.s V_4 IL_0049: ldloc.2 IL_004a: ble.s IL_0036 IL_004c: ldloc.3 IL_004d: ldc.i4.1 IL_004e: add IL_004f: stloc.3 IL_0050: ldloc.3 IL_0051: ldloc.1 IL_0052: ble.s IL_002b IL_0054: ret }"); } [Fact] public void UncheckedExpression_CompoundAssignment() { var source = @" class C { public static int Add(int a, int b) { return unchecked(a+=b); } public static int Sub(int a, int b) { return unchecked(a-=b); } public static int Mul(int a, int b) { return unchecked(a*=b); } public static int Div(int a, int b) { return unchecked(a/=b); } public static int Rem(int a, int b) { return unchecked(a%=b); } } "; var verifier = CompileAndVerify(source); verifier.VerifyIL("C.Add", @" { // Code size 7 (0x7) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: add IL_0003: dup IL_0004: starg.s V_0 IL_0006: ret } "); verifier.VerifyIL("C.Sub", @" { // Code size 7 (0x7) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: sub IL_0003: dup IL_0004: starg.s V_0 IL_0006: ret } "); verifier.VerifyIL("C.Mul", @" { // Code size 7 (0x7) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: mul IL_0003: dup IL_0004: starg.s V_0 IL_0006: ret } "); // both checked and unchecked generate the same instruction that checks overflow verifier.VerifyIL("C.Div", @" { // Code size 7 (0x7) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: div IL_0003: dup IL_0004: starg.s V_0 IL_0006: ret } "); verifier.VerifyIL("C.Rem", @" { // Code size 7 (0x7) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: rem IL_0003: dup IL_0004: starg.s V_0 IL_0006: ret } "); } [Fact] public void CheckedBlock_Conversions() { var source = @" enum E { A = 1 } class C { public static uint SByte_UInt(sbyte a) { checked { return (uint)a; } } public static int UInt_Int(uint a) { checked { return (int)a; } } public static short Enum_Short(E a) { checked { return (short)a; } } } "; var verifier = CompileAndVerify(source); verifier.VerifyIL("C.SByte_UInt", @" { // Code size 3 (0x3) .maxstack 1 IL_0000: ldarg.0 IL_0001: conv.ovf.u4 IL_0002: ret } "); verifier.VerifyIL("C.UInt_Int", @" { // Code size 3 (0x3) .maxstack 1 IL_0000: ldarg.0 IL_0001: conv.ovf.i4.un IL_0002: ret } "); verifier.VerifyIL("C.Enum_Short", @" { // Code size 3 (0x3) .maxstack 1 IL_0000: ldarg.0 IL_0001: conv.ovf.i2 IL_0002: ret } "); } // Although C# 4.0 specification says that checked context never flows in a lambda, // the Dev10 compiler implementation always flows the context in, except for // when the lambda is directly a "parameter" of the checked/unchecked expression. // For Roslyn we decided to change the spec and always flow the context in. [Fact] public void Lambda_Statement() { var verifier = CompileAndVerify(@" class C { static void F() { checked { System.Func<int, int> d1 = delegate(int i) { return i + 1; // add_ovf }; } } } ").VerifyIL("C.<>c.<F>b__0_0(int)", @" { // Code size 4 (0x4) .maxstack 2 IL_0000: ldarg.1 IL_0001: ldc.i4.1 IL_0002: add.ovf IL_0003: ret } "); } [Fact] public void Lambda_QueryStmt() { var verifier = CompileAndVerify(@" using System.Linq; class C { static void F() { checked { var a = from x in new[] { 1 } select x * 2; // mul_ovf } } } ").VerifyIL("C.<>c.<F>b__0_0(int)", @" { // Code size 4 (0x4) .maxstack 2 IL_0000: ldarg.1 IL_0001: ldc.i4.2 IL_0002: mul.ovf IL_0003: ret } "); } [Fact] public void Lambda_QueryExpr() { var verifier = CompileAndVerify(@" using System.Linq; class C { static void F() { var a = checked(from x in new[] { 1 } select x * 2); // mul_ovf } } ").VerifyIL("C.<>c.<F>b__0_0(int)", @" { // Code size 4 (0x4) .maxstack 2 IL_0000: ldarg.1 IL_0001: ldc.i4.2 IL_0002: mul.ovf IL_0003: ret } "); } [Fact] public void Lambda_AddOvfAssignment() { var verifier = CompileAndVerify(@" class C { static void F() { System.Func<int, int> d2; System.Func<int, int> d1 = checked(d2 = delegate(int i) { return i + 1; // add_ovf }); } } ").VerifyIL("C.<>c.<F>b__0_0(int)", @" { // Code size 4 (0x4) .maxstack 2 IL_0000: ldarg.1 IL_0001: ldc.i4.1 IL_0002: add.ovf IL_0003: ret } "); } [Fact] public void Lambda_Add() { var verifier = CompileAndVerify(@" class C { static void F() { System.Func<int, int> d1 = checked(delegate (int i) { return i + 1; // Dev10: add, Roslyn: add_ovf }); } } ").VerifyIL("C.<>c.<F>b__0_0(int)", @" { // Code size 4 (0x4) .maxstack 2 IL_0000: ldarg.1 IL_0001: ldc.i4.1 IL_0002: add.ovf IL_0003: ret } "); } [Fact] public void Lambda_Cast() { var verifier = CompileAndVerify(@" class C { static void F() { var d1 = checked((System.Func<int, int>)delegate (int i) { return i + 1; // add_ovf }); } } ").VerifyIL("C.<>c.<F>b__0_0(int)", @" { // Code size 4 (0x4) .maxstack 2 IL_0000: ldarg.1 IL_0001: ldc.i4.1 IL_0002: add.ovf IL_0003: ret } "); } [Fact] public void Lambda_AddOvfCompoundAssignment() { var verifier = CompileAndVerify(@" class C { static void F() { System.Func<int, int> d2 = null; System.Func<int, int> d1 = checked(d2 += delegate(int i) { return i + 1; // add_ovf }); } } ").VerifyIL("C.<>c.<F>b__0_0(int)", @" { // Code size 4 (0x4) .maxstack 2 IL_0000: ldarg.1 IL_0001: ldc.i4.1 IL_0002: add.ovf IL_0003: ret } "); } [Fact] public void Lambda_AddOvfArgument() { var verifier = CompileAndVerify(@" class C { static System.Func<int, int> Id(System.Func<int, int> x) { return x; } static void F() { System.Func<int, int> d1 = checked(Id(delegate(int i) { return i + 1; // add_ovf })); } } ").VerifyIL("C.<>c.<F>b__1_0(int)", @" { // Code size 4 (0x4) .maxstack 2 IL_0000: ldarg.1 IL_0001: ldc.i4.1 IL_0002: add.ovf IL_0003: ret } "); } [Fact] public void Lambda_AddOvfArgument2() { var verifier = CompileAndVerify(@" class C { static System.Func<int, int> Id(System.Func<int, int> x) { return x; } static void F() { System.Func<int, int> d1 = checked(Id(checked(delegate(int i) { return i + 1; // add_ovf }))); } } ").VerifyIL("C.<>c.<F>b__1_0(int)", @" { // Code size 4 (0x4) .maxstack 2 IL_0000: ldarg.1 IL_0001: ldc.i4.1 IL_0002: add.ovf IL_0003: ret } "); } [Fact] public void Lambda_AddArgument3() { var verifier = CompileAndVerify(@" class C { static System.Func<int, int> Id(System.Func<int, int> x) { return x; } static void F() { System.Func<int, int> d1 = unchecked(Id(checked(delegate(int i) { return i + 1; // add }))); } } ").VerifyIL("C.<>c.<F>b__1_0(int)", @" { // Code size 4 (0x4) .maxstack 2 IL_0000: ldarg.1 IL_0001: ldc.i4.1 IL_0002: add.ovf IL_0003: ret } "); } [Fact] public void Lambda_AddOvfArgument4() { var verifier = CompileAndVerify(@" class C { static System.Func<int, int> Id(System.Func<int, int> x) { return x; } static void F() { System.Func<int, int> d2; System.Func<int, int> d1 = unchecked(Id(checked(d2 = checked(delegate(int i) { return i + 1; // add_ovf })))); } } ").VerifyIL("C.<>c.<F>b__1_0(int)", @" { // Code size 4 (0x4) .maxstack 2 IL_0000: ldarg.1 IL_0001: ldc.i4.1 IL_0002: add.ovf IL_0003: ret } "); } [Fact] public void Lambda_AddOvfArgument5() { var verifier = CompileAndVerify(@" class C { static System.Func<int, int> Id(System.Func<int, int> x) { return x; } static void F() { System.Func<int, int> d1 = checked(Id(unchecked(delegate(int i) { return i + 1; // add_ovf }))); } } ").VerifyIL("C.<>c.<F>b__1_0(int)", @" { // Code size 4 (0x4) .maxstack 2 IL_0000: ldarg.1 IL_0001: ldc.i4.1 IL_0002: add IL_0003: ret } "); } [Fact] public void Lambda_AddArgument6() { var verifier = CompileAndVerify(@" class C { static System.Func<int, int> Id(System.Func<int, int> x) { return x; } static void F() { System.Func<int, int> d1 = checked(unchecked(Id(delegate(int i) { return i + 1; // add }))); } } ").VerifyIL("C.<>c.<F>b__1_0(int)", @" { // Code size 4 (0x4) .maxstack 2 IL_0000: ldarg.1 IL_0001: ldc.i4.1 IL_0002: add IL_0003: ret } "); } [Fact] public void Lambda_AddArgument7() { var verifier = CompileAndVerify(@" class C { static System.Func<int, int> Id(System.Func<int, int> x) { return x; } static void F() { System.Func<int, int> d1 = checked(Id(unchecked(Id(delegate(int i) { return i + 1; // add })))); } } ").VerifyIL("C.<>c.<F>b__1_0(int)", @" { // Code size 4 (0x4) .maxstack 2 IL_0000: ldarg.1 IL_0001: ldc.i4.1 IL_0002: add IL_0003: ret } "); } [Fact] public void Lambda_AddOvfArgument8() { var verifier = CompileAndVerify(@" class C { static System.Func<int, int> Id(System.Func<int, int> x) { return x; } static void F() { System.Func<int, int> d1 = checked(Id(unchecked(Id(delegate(int i) { return i + 1; // add })))); } } ").VerifyIL("C.<>c.<F>b__1_0(int)", @" { // Code size 4 (0x4) .maxstack 2 IL_0000: ldarg.1 IL_0001: ldc.i4.1 IL_0002: add IL_0003: ret } "); } [Fact] public void Lambda_LambdaVsDelegate1() { var verifier = CompileAndVerify(@" class C { static void F() { System.Func<int, int> d1 = checked(delegate(int i) { return i + 1; }); // Dev10: add, Roslyn: add_ovf } } ").VerifyIL("C.<>c.<F>b__0_0(int)", @" { // Code size 4 (0x4) .maxstack 2 IL_0000: ldarg.1 IL_0001: ldc.i4.1 IL_0002: add.ovf IL_0003: ret } "); } [Fact] public void Lambda_LambdaVsDelegate2() { var verifier = CompileAndVerify(@" class C { static void F() { System.Func<int, int> d1 = checked(i => { return i + 1; }); // Dev10: add, Roslyn: add_ovf } } ").VerifyIL("C.<>c.<F>b__0_0(int)", @" { // Code size 4 (0x4) .maxstack 2 IL_0000: ldarg.1 IL_0001: ldc.i4.1 IL_0002: add.ovf IL_0003: ret } "); } [Fact] public void Lambda_LambdaVsDelegate3() { var verifier = CompileAndVerify(@" class C { static void F() { System.Func<int, int> d1 = checked(i => i + 1); // Dev10: add, Roslyn: add_ovf } } ").VerifyIL("C.<>c.<F>b__0_0(int)", @" { // Code size 4 (0x4) .maxstack 2 IL_0000: ldarg.1 IL_0001: ldc.i4.1 IL_0002: add.ovf IL_0003: ret } "); } [Fact] public void Lambda_NewDelegate1() { var verifier = CompileAndVerify(@" class C { static System.Func<int, int> Id(System.Func<int, int> x) { return x; } static void F() { Id(new System.Func<int, int>(i => i + 1)); } } ").VerifyIL("C.<>c.<F>b__1_0(int)", @" { // Code size 4 (0x4) .maxstack 2 IL_0000: ldarg.1 IL_0001: ldc.i4.1 IL_0002: add IL_0003: ret } "); } [Fact] public void Lambda_NewDelegate2() { var verifier = CompileAndVerify(@" class C { static System.Func<int, int> Id(System.Func<int, int> x) { return x; } static void F() { Id(checked(new System.Func<int, int>(i => i + 1))); } } ").VerifyIL("C.<>c.<F>b__1_0(int)", @" { // Code size 4 (0x4) .maxstack 2 IL_0000: ldarg.1 IL_0001: ldc.i4.1 IL_0002: add.ovf IL_0003: ret } "); } [Fact] public void CheckedOption1() { var source = @" class C { public static uint ULong_UInt(ulong a) { return (uint)a; } } "; CompileAndVerify(source, options: TestOptions.ReleaseDll.WithOverflowChecks(true)).VerifyIL("C.ULong_UInt", @" { // Code size 3 (0x3) .maxstack 1 IL_0000: ldarg.0 IL_0001: conv.ovf.u4.un IL_0002: ret } "); CompileAndVerify(source, options: TestOptions.ReleaseDll.WithOverflowChecks(false)).VerifyIL("C.ULong_UInt", @" { // Code size 3 (0x3) .maxstack 1 IL_0000: ldarg.0 IL_0001: conv.u4 IL_0002: ret } "); } [Fact] public void CheckedOption2() { var source = @" class C { public static uint ULong_UInt(ulong a) { return unchecked((uint)a); } } "; CompileAndVerify(source, options: TestOptions.ReleaseDll.WithOverflowChecks(true)).VerifyIL("C.ULong_UInt", @" { // Code size 3 (0x3) .maxstack 1 IL_0000: ldarg.0 IL_0001: conv.u4 IL_0002: ret } "); } [Fact] public void CheckedUncheckedNesting() { var source = @" class C { public static uint ULong_UInt(ulong a) { uint b = (uint)a; unchecked { return b + checked((uint)a - unchecked((uint)a)); } } } "; CompileAndVerify(source, options: TestOptions.ReleaseDll.WithOverflowChecks(true)).VerifyIL("C.ULong_UInt", @" { // Code size 9 (0x9) .maxstack 3 IL_0000: ldarg.0 IL_0001: conv.ovf.u4.un IL_0002: ldarg.0 IL_0003: conv.ovf.u4.un IL_0004: ldarg.0 IL_0005: conv.u4 IL_0006: sub.ovf.un IL_0007: add IL_0008: ret } "); } [Fact] public void UncheckedOperatorWithConstantsOfTheSignedIntegralTypes() { var source = @" class Test { const int a = unchecked((int)0xFFFFFFFF); const int b = unchecked((int)0x80000000); } "; CompileAndVerify(source); } [Fact] public void UncheckedOperatorInCheckedStatement() { // Overflow checking context with use unchecked operator in checked statement var source = @" class Program { static void Main() { int r = 0; r = int.MaxValue + 1; checked { r = int.MaxValue + 1; r = unchecked(int.MaxValue + 1); }; } } "; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (7,13): error CS0220: The operation overflows at compile time in checked mode // r = int.MaxValue + 1; Diagnostic(ErrorCode.ERR_CheckedOverflow, "int.MaxValue + 1"), // (10,17): error CS0220: The operation overflows at compile time in checked mode // r = int.MaxValue + 1; Diagnostic(ErrorCode.ERR_CheckedOverflow, "int.MaxValue + 1")); } [Fact] public void ExpressionsInUncheckedStatementAreInExplicitlyUncheckedContext() { // Expressions which are in unchecked statement are in explicitly unchecked context. var source = @" using System; class Program { static void Main() { int s2 = int.MaxValue; int r = 0; unchecked { r = s2 + 1; }; if (r != int.MinValue) { Console.Write(""FAIL""); } unchecked { r = int.MaxValue + 1; }; if (r != int.MinValue) { Console.Write(""FAIL""); } } } "; var verifier = CompileAndVerify(source); verifier.VerifyIL("Program.Main", @" { // Code size 47 (0x2f) .maxstack 2 IL_0000: ldc.i4 0x7fffffff IL_0005: ldc.i4.1 IL_0006: add IL_0007: ldc.i4 0x80000000 IL_000c: beq.s IL_0018 IL_000e: ldstr ""FAIL"" IL_0013: call ""void System.Console.Write(string)"" IL_0018: ldc.i4 0x80000000 IL_001d: ldc.i4 0x80000000 IL_0022: beq.s IL_002e IL_0024: ldstr ""FAIL"" IL_0029: call ""void System.Console.Write(string)"" IL_002e: ret } "); } [Fact] public void CheckOnUnaryOperator() { var source = @" class Program { static void Main() { long test1 = long.MinValue; long test2 = 0; try { checked { test2 = -test1; } } catch (System.OverflowException) { System.Console.Write(test2); } } } "; CompileAndVerify(source, expectedOutput: "0"); } [Fact] public void Test_024_16BitSignedInteger() { var source = @" using System; public class MyClass { public static void Main() { Int16 f = 0; var r = checked(f += 32000); Console.Write(r); } } "; CompileAndVerify( source, expectedOutput: "32000"); } [Fact] public void AnonymousFunctionExpressionInUncheckedOperator() { // Overflow checking context with use anonymous function expression in unchecked operator var source = @" class Program { delegate int D1(int i); static void Main() { D1 d1; int r1 = 0; d1 = unchecked(delegate (int i) { r1 = int.MaxValue + 1; r1++; return checked(int.MaxValue + 1); }); d1 = unchecked(i => int.MaxValue + 1 + checked(0 + 0)); d1 = unchecked(i => 0 + 0 + checked(int.MaxValue + 1)); d1 = unchecked(d1 = delegate (int i) { r1 = int.MaxValue + 1; return checked(int.MaxValue + 1); }); d1 = unchecked(d1 = i => int.MaxValue + 1 + checked(0 + 0)); d1 = unchecked(d1 = i => 0 + 0 + checked(int.MaxValue + 1)); d1 = unchecked(new D1(delegate (int i) { r1 = int.MaxValue + 1; return checked(int.MaxValue + 1); })); d1 = unchecked(new D1(i => int.MaxValue + 1 + checked(0 + 0))); d1 = unchecked(new D1(i => 0 + 0 + checked(int.MaxValue + 1))); } } "; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (9,81): error CS0220: The operation overflows at compile time in checked mode // d1 = unchecked(delegate (int i) { r1 = int.MaxValue + 1; return checked(int.MaxValue + 1); }); Diagnostic(ErrorCode.ERR_CheckedOverflow, "int.MaxValue + 1"), // (11,45): error CS0220: The operation overflows at compile time in checked mode // d1 = unchecked(i => 0 + 0 + checked(int.MaxValue + 1)); Diagnostic(ErrorCode.ERR_CheckedOverflow, "int.MaxValue + 1"), // (12,86): error CS0220: The operation overflows at compile time in checked mode // d1 = unchecked(d1 = delegate (int i) { r1 = int.MaxValue + 1; return checked(int.MaxValue + 1); }); Diagnostic(ErrorCode.ERR_CheckedOverflow, "int.MaxValue + 1"), // (14,50): error CS0220: The operation overflows at compile time in checked mode // d1 = unchecked(d1 = i => 0 + 0 + checked(int.MaxValue + 1)); Diagnostic(ErrorCode.ERR_CheckedOverflow, "int.MaxValue + 1"), // (15,88): error CS0220: The operation overflows at compile time in checked mode // d1 = unchecked(new D1(delegate (int i) { r1 = int.MaxValue + 1; return checked(int.MaxValue + 1); })); Diagnostic(ErrorCode.ERR_CheckedOverflow, "int.MaxValue + 1"), // (17,52): error CS0220: The operation overflows at compile time in checked mode // d1 = unchecked(new D1(i => 0 + 0 + checked(int.MaxValue + 1))); Diagnostic(ErrorCode.ERR_CheckedOverflow, "int.MaxValue + 1")); } [WorkItem(648109, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/648109")] [Fact] public void CheckedExpressionWithDecimal() { var source = @" class M { void F() { var r = checked(decimal.MaxValue + 1); // error should be reported regardless checked or not // decimal math has not concept of ""checked"" var r1 = decimal.MaxValue + 1; } }"; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (6,25): error CS0463: Evaluation of the decimal constant expression failed // var r = checked(decimal.MaxValue + 1); Diagnostic(ErrorCode.ERR_DecConstError, "decimal.MaxValue + 1").WithLocation(6, 25), // (10,18): error CS0463: Evaluation of the decimal constant expression failed // var r1 = decimal.MaxValue + 1; Diagnostic(ErrorCode.ERR_DecConstError, "decimal.MaxValue + 1").WithLocation(10, 18) ); } [WorkItem(543894, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543894"), WorkItem(543924, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543924")] [Fact] public void CheckedOperatorOnEnumOverflow() { var source = @" using System; class Test { enum Esb { max = int.MaxValue } static void Main() { Esb e = Esb.max; try { var i = checked(e++); Console.WriteLine(""FAIL""); } catch (OverflowException) { Console.WriteLine(""PASS""); } } } "; CompileAndVerify( source, expectedOutput: "PASS"); } [WorkItem(529263, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/529263")] [Fact] public void CheckedOperatorOnLambdaExpr() { var source = @" using System; class Program { static void Main() { int max = int.MaxValue; try { Func<int> f1 = checked(() => max + 1); Console.WriteLine(f1()); Console.WriteLine(""FAIL""); } catch (OverflowException) { Console.WriteLine(""PASS""); } } } "; CompileAndVerify( source, expectedOutput: "PASS"); } [Fact, WorkItem(543981, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543981")] public void CheckedOperatorOnUnaryExpression() { var source = @" using System; class Program { static int Main() { int result = 0; int normi = 10; if (checked(-normi) != -10) { result += 1; } int maxi = int.MaxValue; if (checked(-maxi) != int.MinValue + 1) { result += 1; } try { int mini = int.MinValue; var x = checked(-mini); result += 1; } catch (OverflowException) { Console.Write(""OV-""); } Console.WriteLine(result); return result; } } "; CompileAndVerify( source, expectedOutput: "OV-0"); } [Fact, WorkItem(543983, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543983")] public void CheckedStatementWithCompoundAssignment() { var source = @" using System; public class MyClass { public static void Main() { short goo = 0; try { for (int i = 0; i < 2; i++) { checked { goo += 32000; } Console.Write(goo); } } catch (OverflowException) { Console.WriteLine(""OV""); } } } "; CompileAndVerify( source, expectedOutput: "32000OV"); } [Fact, WorkItem(546872, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546872")] public void CheckPostIncrementOnBaseProtectedClassMember() { var source = @" using System; class Base1 { protected int memberee=0; protected int xyz { get; set; } protected int this[int number] { get { return 0; } set { } } } class Derived2 : Base1 { public void inc() { base.memberee++; base.xyz++; base[0]++; } } "; var verifier = CompileAndVerify(source); verifier.VerifyIL("Derived2.inc", @" { // Code size 49 (0x31) .maxstack 4 .locals init (int V_0) IL_0000: ldarg.0 IL_0001: ldarg.0 IL_0002: ldfld ""int Base1.memberee"" IL_0007: ldc.i4.1 IL_0008: add IL_0009: stfld ""int Base1.memberee"" IL_000e: ldarg.0 IL_000f: call ""int Base1.xyz.get"" IL_0014: stloc.0 IL_0015: ldarg.0 IL_0016: ldloc.0 IL_0017: ldc.i4.1 IL_0018: add IL_0019: call ""void Base1.xyz.set"" IL_001e: ldarg.0 IL_001f: ldc.i4.0 IL_0020: call ""int Base1.this[int].get"" IL_0025: stloc.0 IL_0026: ldarg.0 IL_0027: ldc.i4.0 IL_0028: ldloc.0 IL_0029: ldc.i4.1 IL_002a: add IL_002b: call ""void Base1.this[int].set"" IL_0030: ret } "); } [ConditionalFact(typeof(WindowsDesktopOnly), Reason = "https://github.com/dotnet/roslyn/issues/30160")] public void CheckedConversionsInExpressionTrees_Implicit() { // char CheckedConversionInExpressionTree_Implicit("char", "char", ConvertMethod.None); CheckedConversionInExpressionTree_Implicit("char", "ushort", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Implicit("char", "int", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Implicit("char", "uint", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Implicit("char", "long", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Implicit("char", "ulong", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Implicit("char", "decimal", ConvertMethod.Convert); CheckedConversionInExpressionTree_Implicit("char", "float", ConvertMethod.Convert); CheckedConversionInExpressionTree_Implicit("char", "double", ConvertMethod.Convert); // sbyte CheckedConversionInExpressionTree_Implicit("sbyte", "sbyte", ConvertMethod.None); CheckedConversionInExpressionTree_Implicit("sbyte", "short", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Implicit("sbyte", "int", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Implicit("sbyte", "long", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Implicit("sbyte", "decimal", ConvertMethod.Convert); CheckedConversionInExpressionTree_Implicit("sbyte", "float", ConvertMethod.Convert); CheckedConversionInExpressionTree_Implicit("sbyte", "double", ConvertMethod.Convert); // byte CheckedConversionInExpressionTree_Implicit("byte", "byte", ConvertMethod.None); CheckedConversionInExpressionTree_Implicit("byte", "short", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Implicit("byte", "ushort", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Implicit("byte", "int", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Implicit("byte", "uint", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Implicit("byte", "long", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Implicit("byte", "ulong", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Implicit("byte", "decimal", ConvertMethod.Convert); CheckedConversionInExpressionTree_Implicit("byte", "float", ConvertMethod.Convert); CheckedConversionInExpressionTree_Implicit("byte", "double", ConvertMethod.Convert); // short CheckedConversionInExpressionTree_Implicit("short", "short", ConvertMethod.None); CheckedConversionInExpressionTree_Implicit("short", "int", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Implicit("short", "long", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Implicit("short", "decimal", ConvertMethod.Convert); CheckedConversionInExpressionTree_Implicit("short", "float", ConvertMethod.Convert); CheckedConversionInExpressionTree_Implicit("short", "double", ConvertMethod.Convert); // ushort CheckedConversionInExpressionTree_Implicit("ushort", "ushort", ConvertMethod.None); CheckedConversionInExpressionTree_Implicit("ushort", "int", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Implicit("ushort", "uint", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Implicit("ushort", "long", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Implicit("ushort", "ulong", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Implicit("ushort", "decimal", ConvertMethod.Convert); CheckedConversionInExpressionTree_Implicit("ushort", "float", ConvertMethod.Convert); CheckedConversionInExpressionTree_Implicit("ushort", "double", ConvertMethod.Convert); // int CheckedConversionInExpressionTree_Implicit("int", "int", ConvertMethod.None); CheckedConversionInExpressionTree_Implicit("int", "long", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Implicit("int", "decimal", ConvertMethod.Convert); CheckedConversionInExpressionTree_Implicit("int", "float", ConvertMethod.Convert); CheckedConversionInExpressionTree_Implicit("int", "double", ConvertMethod.Convert); // uint CheckedConversionInExpressionTree_Implicit("uint", "uint", ConvertMethod.None); CheckedConversionInExpressionTree_Implicit("uint", "long", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Implicit("uint", "ulong", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Implicit("uint", "decimal", ConvertMethod.Convert); CheckedConversionInExpressionTree_Implicit("uint", "float", ConvertMethod.Convert); CheckedConversionInExpressionTree_Implicit("uint", "double", ConvertMethod.Convert); // long CheckedConversionInExpressionTree_Implicit("long", "long", ConvertMethod.None); CheckedConversionInExpressionTree_Implicit("long", "decimal", ConvertMethod.Convert); CheckedConversionInExpressionTree_Implicit("long", "float", ConvertMethod.Convert); CheckedConversionInExpressionTree_Implicit("long", "double", ConvertMethod.Convert); // ulong CheckedConversionInExpressionTree_Implicit("ulong", "ulong", ConvertMethod.None); CheckedConversionInExpressionTree_Implicit("ulong", "decimal", ConvertMethod.Convert); CheckedConversionInExpressionTree_Implicit("ulong", "float", ConvertMethod.Convert); CheckedConversionInExpressionTree_Implicit("ulong", "double", ConvertMethod.Convert); // decimal CheckedConversionInExpressionTree_Implicit("decimal", "decimal", ConvertMethod.None); // float CheckedConversionInExpressionTree_Implicit("float", "float", ConvertMethod.None); CheckedConversionInExpressionTree_Implicit("float", "double", ConvertMethod.Convert); // double CheckedConversionInExpressionTree_Implicit("double", "double", ConvertMethod.None); // object CheckedConversionInExpressionTree_Implicit("int", "object", ConvertMethod.Convert); CheckedConversionInExpressionTree_Implicit("string", "object", ConvertMethod.None); // Nullable<> CheckedConversionInExpressionTree_Implicit("int", "int?", "arg => F(Convert(arg))"); CheckedConversionInExpressionTree_Implicit("int", "long?", "arg => F(ConvertChecked(ConvertChecked(arg)))"); } [ConditionalFact(typeof(WindowsDesktopOnly), Reason = "https://github.com/dotnet/roslyn/issues/30160")] [WorkItem(18459, "https://github.com/dotnet/roslyn/issues/18459")] public void CheckedConversionsInExpressionTrees_ImplicitTuple() { CheckedConversionInExpressionTree_Implicit("(int, int)", "(int, int)?", ConvertMethod.Convert); } [ConditionalFact(typeof(WindowsDesktopOnly), Reason = "https://github.com/dotnet/roslyn/issues/30160")] public void CheckedConversionsInExpressionTrees_Explicit() { // char CheckedConversionInExpressionTree_Explicit("char", "char", ConvertMethod.Convert); CheckedConversionInExpressionTree_Explicit("char", "sbyte", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("char", "byte", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("char", "short", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("char", "ushort", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("char", "int", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("char", "uint", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("char", "long", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("char", "ulong", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("char", "decimal", ConvertMethod.Convert); CheckedConversionInExpressionTree_Explicit("char", "float", ConvertMethod.Convert); CheckedConversionInExpressionTree_Explicit("char", "double", ConvertMethod.Convert); // sbyte CheckedConversionInExpressionTree_Explicit("sbyte", "char", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("sbyte", "sbyte", ConvertMethod.Convert); CheckedConversionInExpressionTree_Explicit("sbyte", "byte", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("sbyte", "short", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("sbyte", "ushort", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("sbyte", "int", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("sbyte", "uint", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("sbyte", "long", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("sbyte", "ulong", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("sbyte", "decimal", ConvertMethod.Convert); CheckedConversionInExpressionTree_Explicit("sbyte", "float", ConvertMethod.Convert); CheckedConversionInExpressionTree_Explicit("sbyte", "double", ConvertMethod.Convert); // byte CheckedConversionInExpressionTree_Explicit("byte", "char", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("byte", "sbyte", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("byte", "byte", ConvertMethod.Convert); CheckedConversionInExpressionTree_Explicit("byte", "short", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("byte", "ushort", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("byte", "int", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("byte", "uint", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("byte", "long", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("byte", "ulong", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("byte", "decimal", ConvertMethod.Convert); CheckedConversionInExpressionTree_Explicit("byte", "float", ConvertMethod.Convert); CheckedConversionInExpressionTree_Explicit("byte", "double", ConvertMethod.Convert); // short CheckedConversionInExpressionTree_Explicit("short", "char", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("short", "sbyte", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("short", "byte", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("short", "short", ConvertMethod.Convert); CheckedConversionInExpressionTree_Explicit("short", "ushort", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("short", "int", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("short", "uint", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("short", "long", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("short", "ulong", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("short", "decimal", ConvertMethod.Convert); CheckedConversionInExpressionTree_Explicit("short", "float", ConvertMethod.Convert); CheckedConversionInExpressionTree_Explicit("short", "double", ConvertMethod.Convert); // ushort CheckedConversionInExpressionTree_Explicit("ushort", "char", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("ushort", "sbyte", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("ushort", "byte", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("ushort", "short", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("ushort", "ushort", ConvertMethod.Convert); CheckedConversionInExpressionTree_Explicit("ushort", "int", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("ushort", "uint", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("ushort", "long", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("ushort", "ulong", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("ushort", "decimal", ConvertMethod.Convert); CheckedConversionInExpressionTree_Explicit("ushort", "float", ConvertMethod.Convert); CheckedConversionInExpressionTree_Explicit("ushort", "double", ConvertMethod.Convert); // int CheckedConversionInExpressionTree_Explicit("int", "char", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("int", "sbyte", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("int", "byte", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("int", "short", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("int", "ushort", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("int", "int", ConvertMethod.Convert); CheckedConversionInExpressionTree_Explicit("int", "uint", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("int", "long", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("int", "ulong", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("int", "decimal", ConvertMethod.Convert); CheckedConversionInExpressionTree_Explicit("int", "float", ConvertMethod.Convert); CheckedConversionInExpressionTree_Explicit("int", "double", ConvertMethod.Convert); // uint CheckedConversionInExpressionTree_Explicit("uint", "char", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("uint", "sbyte", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("uint", "byte", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("uint", "short", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("uint", "ushort", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("uint", "int", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("uint", "uint", ConvertMethod.Convert); CheckedConversionInExpressionTree_Explicit("uint", "long", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("uint", "ulong", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("uint", "decimal", ConvertMethod.Convert); CheckedConversionInExpressionTree_Explicit("uint", "float", ConvertMethod.Convert); CheckedConversionInExpressionTree_Explicit("uint", "double", ConvertMethod.Convert); // long CheckedConversionInExpressionTree_Explicit("long", "char", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("long", "sbyte", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("long", "byte", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("long", "short", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("long", "ushort", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("long", "int", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("long", "uint", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("long", "long", ConvertMethod.Convert); CheckedConversionInExpressionTree_Explicit("long", "ulong", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("long", "decimal", ConvertMethod.Convert); CheckedConversionInExpressionTree_Explicit("long", "float", ConvertMethod.Convert); CheckedConversionInExpressionTree_Explicit("long", "double", ConvertMethod.Convert); // ulong CheckedConversionInExpressionTree_Explicit("ulong", "char", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("ulong", "sbyte", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("ulong", "byte", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("ulong", "short", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("ulong", "ushort", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("ulong", "int", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("ulong", "uint", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("ulong", "long", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("ulong", "ulong", ConvertMethod.Convert); CheckedConversionInExpressionTree_Explicit("ulong", "decimal", ConvertMethod.Convert); CheckedConversionInExpressionTree_Explicit("ulong", "float", ConvertMethod.Convert); CheckedConversionInExpressionTree_Explicit("ulong", "double", ConvertMethod.Convert); // decimal CheckedConversionInExpressionTree_Explicit("decimal", "char", ConvertMethod.Convert); CheckedConversionInExpressionTree_Explicit("decimal", "sbyte", ConvertMethod.Convert); CheckedConversionInExpressionTree_Explicit("decimal", "byte", ConvertMethod.Convert); CheckedConversionInExpressionTree_Explicit("decimal", "short", ConvertMethod.Convert); CheckedConversionInExpressionTree_Explicit("decimal", "ushort", ConvertMethod.Convert); CheckedConversionInExpressionTree_Explicit("decimal", "int", ConvertMethod.Convert); CheckedConversionInExpressionTree_Explicit("decimal", "uint", ConvertMethod.Convert); CheckedConversionInExpressionTree_Explicit("decimal", "long", ConvertMethod.Convert); CheckedConversionInExpressionTree_Explicit("decimal", "ulong", ConvertMethod.Convert); CheckedConversionInExpressionTree_Explicit("decimal", "decimal", ConvertMethod.Convert); CheckedConversionInExpressionTree_Explicit("decimal", "float", ConvertMethod.Convert); CheckedConversionInExpressionTree_Explicit("decimal", "double", ConvertMethod.Convert); // float CheckedConversionInExpressionTree_Explicit("float", "char", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("float", "sbyte", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("float", "byte", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("float", "short", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("float", "ushort", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("float", "int", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("float", "uint", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("float", "long", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("float", "ulong", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("float", "decimal", ConvertMethod.Convert); CheckedConversionInExpressionTree_Explicit("float", "float", ConvertMethod.Convert); CheckedConversionInExpressionTree_Explicit("float", "double", ConvertMethod.Convert); // double CheckedConversionInExpressionTree_Explicit("double", "char", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("double", "sbyte", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("double", "byte", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("double", "short", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("double", "ushort", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("double", "int", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("double", "uint", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("double", "long", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("double", "ulong", ConvertMethod.ConvertChecked); CheckedConversionInExpressionTree_Explicit("double", "decimal", ConvertMethod.Convert); CheckedConversionInExpressionTree_Explicit("double", "float", ConvertMethod.Convert); CheckedConversionInExpressionTree_Explicit("double", "double", ConvertMethod.Convert); // enum CheckedConversionInExpressionTree_Explicit("E", "int", ConvertMethod.ConvertChecked, "enum E { }"); CheckedConversionInExpressionTree_Explicit("int", "E", ConvertMethod.ConvertChecked, "enum E { }"); CheckedConversionInExpressionTree_Explicit("E", "int", ConvertMethod.ConvertChecked, "enum E : short { }"); CheckedConversionInExpressionTree_Explicit("int", "E", ConvertMethod.ConvertChecked, "enum E : short { }"); // object CheckedConversionInExpressionTree_Explicit("int", "object", ConvertMethod.Convert); CheckedConversionInExpressionTree_Explicit("object", "int", ConvertMethod.Convert); CheckedConversionInExpressionTree_Explicit("string", "object", ConvertMethod.Convert); CheckedConversionInExpressionTree_Explicit("object", "string", ConvertMethod.Convert); // Nullable<> CheckedConversionInExpressionTree_Explicit("int", "byte?", "arg => ConvertChecked(arg)"); CheckedConversionInExpressionTree_Explicit("int", "int?", "arg => ConvertChecked(arg)"); CheckedConversionInExpressionTree_Explicit("int", "long?", "arg => ConvertChecked(ConvertChecked(arg))"); } [ConditionalFact(typeof(WindowsDesktopOnly), Reason = "https://github.com/dotnet/roslyn/issues/30160")] public void CheckedConversionsInExpressionTrees_ExplicitTuple() { CheckedConversionInExpressionTree_Explicit("(int, int)", "(int, int)?", ConvertMethod.Convert); } private enum ConvertMethod { None, Convert, ConvertChecked, } private void CheckedConversionInExpressionTree_Implicit(string fromType, string toType, ConvertMethod expectedMethod, string additionalTypes = "") { var source = CheckedConversionInExpressionTree_ImplicitSource(fromType, toType, additionalTypes); var compilation = CreateCompilationWithMscorlib40AndSystemCore(source, options: TestOptions.ReleaseExe, references: new[] { ValueTupleRef, SystemRuntimeFacadeRef }); string expectedOutput; switch (expectedMethod) { default: expectedOutput = "arg => F(arg)"; break; case ConvertMethod.Convert: expectedOutput = "arg => F(Convert(arg))"; break; case ConvertMethod.ConvertChecked: expectedOutput = "arg => F(ConvertChecked(arg))"; break; } var verifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); // Since Expression.ConvertChecked can generate a Checked result // (rather than ConvertChecked), verify the correct method was called. VerifyConversionInExpressionTreeIL(verifier.TestData.GetMethodData("C.Main").GetMethodIL(), expectedMethod); } private void CheckedConversionInExpressionTree_Implicit(string fromType, string toType, string expectedOutput) { var source = CheckedConversionInExpressionTree_ImplicitSource(fromType, toType, additionalTypes: ""); var compilation = CreateCompilationWithMscorlib40AndSystemCore(source, options: TestOptions.ReleaseExe); CompileAndVerify(compilation, expectedOutput: expectedOutput); } private static string CheckedConversionInExpressionTree_ImplicitSource(string fromType, string toType, string additionalTypes) { return $@"using System; using System.Linq.Expressions; {additionalTypes} class C {{ static {toType} F({toType} arg) => arg; static void Main() {{ Expression<Func<{fromType}, {toType}>> e = arg => checked(F(arg)); Console.WriteLine(e); }} }}"; } private void CheckedConversionInExpressionTree_Explicit(string fromType, string toType, ConvertMethod expectedMethod, string additionalTypes = "") { var source = CheckedConversionInExpressionTree_ExplicitSource(fromType, toType, additionalTypes); var compilation = CreateCompilationWithMscorlib40AndSystemCore(source, options: TestOptions.ReleaseExe, references: new[] { ValueTupleRef, SystemRuntimeFacadeRef }); string expectedOutput; switch (expectedMethod) { default: expectedOutput = "arg => arg"; break; case ConvertMethod.Convert: expectedOutput = "arg => Convert(arg)"; break; case ConvertMethod.ConvertChecked: expectedOutput = "arg => ConvertChecked(arg)"; break; } var verifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); // Since Expression.ConvertChecked can generate a Checked result // (rather than ConvertChecked), verify the correct method was called. VerifyConversionInExpressionTreeIL(verifier.TestData.GetMethodData("C.Main").GetMethodIL(), expectedMethod); } private void CheckedConversionInExpressionTree_Explicit(string fromType, string toType, string expectedOutput) { var source = CheckedConversionInExpressionTree_ExplicitSource(fromType, toType, additionalTypes: ""); var compilation = CreateCompilationWithMscorlib40AndSystemCore(source, options: TestOptions.ReleaseExe); CompileAndVerify(compilation, expectedOutput: expectedOutput); } private static string CheckedConversionInExpressionTree_ExplicitSource(string fromType, string toType, string additionalTypes) { return $@"using System; using System.Linq.Expressions; {additionalTypes} class C {{ static void Main() {{ Expression<Func<{fromType}, {toType}>> e = arg => checked(({toType})arg); Console.WriteLine(e); }} }}"; } private static void VerifyConversionInExpressionTreeIL(string actualIL, ConvertMethod expectedMethod) { Assert.Equal( actualIL.Contains($"System.Linq.Expressions.Expression.Convert(System.Linq.Expressions.Expression, "), expectedMethod == ConvertMethod.Convert); Assert.Equal( actualIL.Contains($"System.Linq.Expressions.Expression.ConvertChecked(System.Linq.Expressions.Expression, "), expectedMethod == ConvertMethod.ConvertChecked); } [Fact, WorkItem(61843, "https://github.com/dotnet/roslyn/issues/61843")] public void SwitchExpressionInCheckedExpression() { var source = """ using System; int x = int.MaxValue; int y = int.MaxValue; try { C.AddInSwitchExpression(x, y, true); } catch (OverflowException) { Console.Write("RAN1 "); } try { C.AddInSwitchExpression2(x, y, true); } catch (OverflowException) { Console.Write("RAN2 "); } try { C.Add(x, y); } catch (OverflowException) { Console.Write("RAN3 "); } public static class C { public static int AddInSwitchExpression(int x, int y, bool condition) => checked ( condition switch { true => x + y, _ => throw null } ); public static int AddInSwitchExpression2(int x, int y, bool condition) { checked { return condition switch { true => x + y, _ => throw null }; } } public static int Add(int x, int y) => checked(x + y); } """; var comp = CreateCompilation(source); var verifier = CompileAndVerify(comp, expectedOutput: "RAN1 RAN2 RAN3"); verifier.VerifyIL("C.AddInSwitchExpression", @" { // Code size 13 (0xd) .maxstack 2 .locals init (int V_0) IL_0000: ldarg.2 IL_0001: brfalse.s IL_0009 IL_0003: ldarg.0 IL_0004: ldarg.1 IL_0005: add.ovf IL_0006: stloc.0 IL_0007: br.s IL_000b IL_0009: ldnull IL_000a: throw IL_000b: ldloc.0 IL_000c: ret } "); verifier.VerifyIL("C.AddInSwitchExpression2", @" { // Code size 13 (0xd) .maxstack 2 .locals init (int V_0) IL_0000: ldarg.2 IL_0001: brfalse.s IL_0009 IL_0003: ldarg.0 IL_0004: ldarg.1 IL_0005: add.ovf IL_0006: stloc.0 IL_0007: br.s IL_000b IL_0009: ldnull IL_000a: throw IL_000b: ldloc.0 IL_000c: ret } "); verifier.VerifyIL("C.Add", @" { // Code size 4 (0x4) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: add.ovf IL_0003: ret } "); } [Fact, WorkItem(61843, "https://github.com/dotnet/roslyn/issues/61843")] public void SpeculationInCheckedExpression() { var source = """ class C { int M(int x, int y) => checked(x); } """; var comp = CreateCompilation(source); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); var xNode = tree.GetRoot().DescendantNodes().OfType<CheckedExpressionSyntax>().Single().Expression; Assert.Equal("x", xNode.ToString()); var nodeToSpeculate = SyntaxFactory.ParseExpression("x + y"); Assert.Equal("System.Int32 System.Int32.op_CheckedAddition(System.Int32 left, System.Int32 right)", model.GetSpeculativeSymbolInfo(xNode.Position, nodeToSpeculate, SpeculativeBindingOption.BindAsExpression).Symbol.ToTestDisplayString()); var checkedNode = tree.GetRoot().DescendantNodes().OfType<CheckedExpressionSyntax>().Single(); Assert.Equal("checked(x)", checkedNode.ToString()); Assert.Equal("System.Int32 System.Int32.op_Addition(System.Int32 left, System.Int32 right)", model.GetSpeculativeSymbolInfo(checkedNode.Position + 2, nodeToSpeculate, SpeculativeBindingOption.BindAsExpression).Symbol.ToTestDisplayString()); } [Fact, WorkItem(61843, "https://github.com/dotnet/roslyn/issues/61843")] public void SpeculationInUncheckedExpression() { var source = """ class C { int M(int x, int y) { checked { return unchecked(x); } } } """; var comp = CreateCompilation(source); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); var xNode = tree.GetRoot().DescendantNodes().OfType<CheckedExpressionSyntax>().Single().Expression; Assert.Equal("x", xNode.ToString()); var nodeToSpeculate = SyntaxFactory.ParseExpression("x + y"); Assert.Equal("System.Int32 System.Int32.op_Addition(System.Int32 left, System.Int32 right)", model.GetSpeculativeSymbolInfo(xNode.Position, nodeToSpeculate, SpeculativeBindingOption.BindAsExpression).Symbol.ToTestDisplayString()); } } }