/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/CSharp/Test/Emit/CodeGen/UnsafeTests.cs
13 113 строк
354 KB
AlekseyTs
Adjust IL used for a null check in the compiler-synthesized inline-array helpers (#84523)
20 июл 2026, 18:31
Не верифицирован
20 июл 2026, 18:31
4d33ccf
Код
Авторство
О чём код?
// 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; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen { [CompilerTrait(CompilerFeature.Unsafe)] public class UnsafeTests : EmitMetadataTestBase { #region AddressOf tests [Fact] public void AddressOfLocal_Unused() { var text = @" unsafe class C { void M() { int x; int* p = &x; } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.Passes); compVerifier.VerifyIL("C.M", @" { // Code size 4 (0x4) .maxstack 1 .locals init (int V_0) //x IL_0000: ldloca.s V_0 IL_0002: pop IL_0003: ret } "); } [Fact] public void AddressOfLocal_Used() { var text = @" unsafe class C { void M(int* param) { int x; int* p = &x; M(p); } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails); compVerifier.VerifyIL("C.M", @" { // Code size 12 (0xc) .maxstack 2 .locals init (int V_0, //x int* V_1) //p IL_0000: ldloca.s V_0 IL_0002: conv.u IL_0003: stloc.1 IL_0004: ldarg.0 IL_0005: ldloc.1 IL_0006: call ""void C.M(int*)"" IL_000b: ret } "); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67330")] public void Invocation_Pointer() { var text = @" class C { void M(int* param) { M(param); } } "; var comp = CreateCompilation(text, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeDebugDll); comp.VerifyDiagnostics( // (4,12): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // void M(int* param) Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(4, 12), // (6,9): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // M(param); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "M(param)").WithLocation(6, 9), // (6,11): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // M(param); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "param").WithLocation(6, 11) ); var expectedDiagnostics = new[] { // (6,9): error CS9363: 'C.M(int*)' must be used in an unsafe context because it has pointers in its signature // M(param); Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "M(param)").WithArguments("C.M(int*)").WithLocation(6, 9), }; CreateCompilation(text, options: TestOptions.UnsafeDebugDll).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(text, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeDebugDll).VerifyDiagnostics(expectedDiagnostics); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67330")] public void Invocation_PointerArray() { var text = @" class C { void M(int*[] param) { M(param); } } "; var comp = CreateCompilation(text, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeDebugDll); comp.VerifyDiagnostics( // (4,12): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // void M(int*[] param) Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(4, 12), // (6,9): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // M(param); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "M(param)").WithLocation(6, 9), // (6,11): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // M(param); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "param").WithLocation(6, 11) ); var expectedDiagnostics = new[] { // (6,9): error CS9363: 'C.M(int*[])' must be used in an unsafe context because it has pointers in its signature // M(param); Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "M(param)").WithArguments("C.M(int*[])").WithLocation(6, 9), }; CreateCompilation(text, options: TestOptions.UnsafeDebugDll).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(text, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeDebugDll).VerifyDiagnostics(expectedDiagnostics); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67330")] public void Invocation_PointerArray_Nested() { var text = @" class C<T> { void M(C<int*[]>[] param) { M(param); } } "; var comp = CreateCompilation(text, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeDebugDll); comp.VerifyDiagnostics( // (4,14): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // void M(C<int*[]>[] param) Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(4, 14), // (6,9): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // M(param); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "M(param)").WithLocation(6, 9), // (6,11): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // M(param); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "param").WithLocation(6, 11) ); var expectedDiagnostics = new[] { // (6,9): error CS9363: 'C<T>.M(C<int*[]>[])' must be used in an unsafe context because it has pointers in its signature // M(param); Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "M(param)").WithArguments("C<T>.M(C<int*[]>[])").WithLocation(6, 9), }; CreateCompilation(text, options: TestOptions.UnsafeDebugDll).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(text, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeDebugDll).VerifyDiagnostics(expectedDiagnostics); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67330")] public void ClassCreation_Pointer() { var text = @" class C { C(int* param) { new C(param); } } "; var comp = CreateCompilation(text, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeDebugDll); comp.VerifyDiagnostics( // (4,7): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // C(int* param) Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(4, 7), // (6,9): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // new C(param); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "new C(param)").WithLocation(6, 9), // (6,15): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // new C(param); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "param").WithLocation(6, 15) ); var expectedDiagnostics = new[] { // (6,9): error CS9363: 'C.C(int*)' must be used in an unsafe context because it has pointers in its signature // new C(param); Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "new C(param)").WithArguments("C.C(int*)").WithLocation(6, 9), }; CreateCompilation(text, options: TestOptions.UnsafeDebugDll).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(text, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeDebugDll).VerifyDiagnostics(expectedDiagnostics); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67330")] public void ClassCreation_PointerArray() { var text = @" class C { C(int*[] param) { new C(param); } } "; var comp = CreateCompilation(text, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeDebugDll); comp.VerifyDiagnostics( // (4,7): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // C(int*[] param) Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(4, 7), // (6,9): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // new C(param); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "new C(param)").WithLocation(6, 9), // (6,15): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // new C(param); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "param").WithLocation(6, 15) ); var expectedDiagnostics = new[] { // (6,9): error CS9363: 'C.C(int*[])' must be used in an unsafe context because it has pointers in its signature // new C(param); Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "new C(param)").WithArguments("C.C(int*[])").WithLocation(6, 9), }; CreateCompilation(text, options: TestOptions.UnsafeDebugDll).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(text, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeDebugDll).VerifyDiagnostics(expectedDiagnostics); } [Fact] public void AddressOfParameter_Unused() { var text = @" unsafe class C { void M(int x) { int* p = &x; } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.Passes); compVerifier.VerifyIL("C.M", @" { // Code size 4 (0x4) .maxstack 1 IL_0000: ldarga.s V_1 IL_0002: pop IL_0003: ret } "); } [Fact] public void AddressOfParameter_Used() { var text = @" unsafe class C { void M(int x, int* param) { int* p = &x; M(x, p); } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails); compVerifier.VerifyIL("C.M", @" { // Code size 13 (0xd) .maxstack 3 .locals init (int* V_0) //p IL_0000: ldarga.s V_1 IL_0002: conv.u IL_0003: stloc.0 IL_0004: ldarg.0 IL_0005: ldarg.1 IL_0006: ldloc.0 IL_0007: call ""void C.M(int, int*)"" IL_000c: ret } "); } [Fact] public void AddressOfStructField() { var text = @" unsafe class C { void M() { S1 s; S1* p1 = &s; S2* p2 = &s.s; int* p3 = &s.s.x; Goo(s, p1, p2, p3); } void Goo(S1 s, S1* p1, S2* p2, int* p3) { } } struct S1 { public S2 s; } struct S2 { public int x; } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails); compVerifier.VerifyIL("C.M", @" { // Code size 38 (0x26) .maxstack 5 .locals init (S1 V_0, //s S1* V_1, //p1 S2* V_2, //p2 int* V_3) //p3 IL_0000: ldloca.s V_0 IL_0002: conv.u IL_0003: stloc.1 IL_0004: ldloca.s V_0 IL_0006: ldflda ""S2 S1.s"" IL_000b: conv.u IL_000c: stloc.2 IL_000d: ldloca.s V_0 IL_000f: ldflda ""S2 S1.s"" IL_0014: ldflda ""int S2.x"" IL_0019: conv.u IL_001a: stloc.3 IL_001b: ldarg.0 IL_001c: ldloc.0 IL_001d: ldloc.1 IL_001e: ldloc.2 IL_001f: ldloc.3 IL_0020: call ""void C.Goo(S1, S1*, S2*, int*)"" IL_0025: ret } "); } [Fact] public void AddressOfSuppressOptimization() { var text = @" unsafe class C { static void M() { int x = 123; Goo(&x); // should not optimize into 'Goo(&123)' } static void Goo(int* p) { } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails); compVerifier.VerifyIL("C.M", @" { // Code size 12 (0xc) .maxstack 1 .locals init (int V_0) //x IL_0000: ldc.i4.s 123 IL_0002: stloc.0 IL_0003: ldloca.s V_0 IL_0005: conv.u IL_0006: call ""void C.Goo(int*)"" IL_000b: ret } "); } #endregion AddressOf tests #region Dereference tests [Fact] public void DereferenceLocal() { var text = @" unsafe class C { static void Main() { int x = 123; int* p = &x; System.Console.WriteLine(*p); } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: "123", verify: Verification.Fails); // NOTE: p is optimized away, but & and * aren't. compVerifier.VerifyIL("C.Main", @" { // Code size 13 (0xd) .maxstack 1 .locals init (int V_0) //x IL_0000: ldc.i4.s 123 IL_0002: stloc.0 IL_0003: ldloca.s V_0 IL_0005: conv.u IL_0006: ldind.i4 IL_0007: call ""void System.Console.WriteLine(int)"" IL_000c: ret } "); } [Fact] public void DereferenceParameter() { var text = @" unsafe class C { static void Main() { long x = 456; System.Console.WriteLine(Dereference(&x)); } static long Dereference(long* p) { return *p; } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: "456", verify: Verification.Fails); compVerifier.VerifyIL("C.Dereference", @" { // Code size 3 (0x3) .maxstack 1 IL_0000: ldarg.0 IL_0001: ldind.i8 IL_0002: ret } "); } [Fact] public void DereferenceWrite() { var text = @" unsafe class C { static void Main() { int x = 1; int* p = &x; *p = 2; System.Console.WriteLine(x); } } "; var compVerifierOptimized = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: "2", verify: Verification.Fails); // NOTE: p is optimized away, but & and * aren't. compVerifierOptimized.VerifyIL("C.Main", @" { // Code size 14 (0xe) .maxstack 2 .locals init (int V_0) //x IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldloca.s V_0 IL_0004: conv.u IL_0005: ldc.i4.2 IL_0006: stind.i4 IL_0007: ldloc.0 IL_0008: call ""void System.Console.WriteLine(int)"" IL_000d: ret } "); var compVerifierUnoptimized = CompileAndVerify(text, options: TestOptions.UnsafeDebugExe, expectedOutput: "2", verify: Verification.Fails); compVerifierUnoptimized.VerifyIL("C.Main", @" { // Code size 18 (0x12) .maxstack 2 .locals init (int V_0, //x int* V_1) //p IL_0000: nop IL_0001: ldc.i4.1 IL_0002: stloc.0 IL_0003: ldloca.s V_0 IL_0005: conv.u IL_0006: stloc.1 IL_0007: ldloc.1 IL_0008: ldc.i4.2 IL_0009: stind.i4 IL_000a: ldloc.0 IL_000b: call ""void System.Console.WriteLine(int)"" IL_0010: nop IL_0011: ret } "); } [Fact] public void DereferenceStruct() { var text = @" unsafe struct S { S* p; byte x; static void Main() { S s; S* sp = &s; (*sp).p = sp; (*sp).x = 1; System.Console.WriteLine((*(s.p)).x); } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: "1", verify: Verification.Fails); compVerifier.VerifyIL("S.Main", @" { // Code size 35 (0x23) .maxstack 2 .locals init (S V_0, //s S* V_1) //sp IL_0000: ldloca.s V_0 IL_0002: conv.u IL_0003: stloc.1 IL_0004: ldloc.1 IL_0005: ldloc.1 IL_0006: stfld ""S* S.p"" IL_000b: ldloc.1 IL_000c: ldc.i4.1 IL_000d: stfld ""byte S.x"" IL_0012: ldloc.0 IL_0013: ldfld ""S* S.p"" IL_0018: ldfld ""byte S.x"" IL_001d: call ""void System.Console.WriteLine(int)"" IL_0022: ret } "); } [Fact] public void DereferenceSwap() { var text = @" using System; unsafe class C { static void Main() { byte b1 = 2; byte b2 = 7; Console.WriteLine(""Before: {0} {1}"", b1, b2); Swap(&b1, &b2); Console.WriteLine(""After: {0} {1}"", b1, b2); } static void Swap(byte* p1, byte* p2) { byte tmp = *p1; *p1 = *p2; *p2 = tmp; } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: @"Before: 2 7 After: 7 2", verify: Verification.Fails); compVerifier.VerifyIL("C.Swap", @" { // Code size 11 (0xb) .maxstack 2 .locals init (byte V_0) //tmp IL_0000: ldarg.0 IL_0001: ldind.u1 IL_0002: stloc.0 IL_0003: ldarg.0 IL_0004: ldarg.1 IL_0005: ldind.u1 IL_0006: stind.i1 IL_0007: ldarg.1 IL_0008: ldloc.0 IL_0009: stind.i1 IL_000a: ret } "); } [Fact] public void DereferenceIsLValue1() { var text = @" using System; unsafe class C { static void Main() { char c = 'a'; char* p = &c; Console.Write(c); Incr(ref *p); Console.Write(c); } static void Incr(ref char c) { c++; } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: @"ab", verify: Verification.Fails); compVerifier.VerifyIL("C.Main", @" { // Code size 24 (0x18) .maxstack 2 .locals init (char V_0) //c IL_0000: ldc.i4.s 97 IL_0002: stloc.0 IL_0003: ldloca.s V_0 IL_0005: conv.u IL_0006: ldloc.0 IL_0007: call ""void System.Console.Write(char)"" IL_000c: call ""void C.Incr(ref char)"" IL_0011: ldloc.0 IL_0012: call ""void System.Console.Write(char)"" IL_0017: ret } "); } [Fact] public void DereferenceIsLValue2() { var text = @" using System; unsafe struct S { int x; static void Main() { S s; s.x = 1; S* p = &s; Console.Write(s.x); (*p).Mutate(); Console.Write(s.x); } void Mutate() { x++; } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: @"12", verify: Verification.Fails); compVerifier.VerifyIL("S.Main", @" { // Code size 39 (0x27) .maxstack 2 .locals init (S V_0) //s IL_0000: ldloca.s V_0 IL_0002: ldc.i4.1 IL_0003: stfld ""int S.x"" IL_0008: ldloca.s V_0 IL_000a: conv.u IL_000b: ldloc.0 IL_000c: ldfld ""int S.x"" IL_0011: call ""void System.Console.Write(int)"" IL_0016: call ""void S.Mutate()"" IL_001b: ldloc.0 IL_001c: ldfld ""int S.x"" IL_0021: call ""void System.Console.Write(int)"" IL_0026: ret } "); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79051")] public void Retrack_PointerToRefLocal() { // When converting from a pointer to a ref, we must avoid eliding the ref local in IL, so the GC knows to track the ref. var source = """ class C { unsafe void M(byte* p) { ref byte b = ref *p; b.ToString(); } } """; CompileAndVerify(source, verify: Verification.Fails, options: TestOptions.UnsafeDebugDll).VerifyIL("C.M", """ { // Code size 11 (0xb) .maxstack 1 .locals init (byte& V_0) //b IL_0000: nop IL_0001: ldarg.1 IL_0002: stloc.0 IL_0003: ldloc.0 IL_0004: call "string byte.ToString()" IL_0009: pop IL_000a: ret } """); CompileAndVerify(source, verify: Verification.Fails, options: TestOptions.UnsafeReleaseDll).VerifyIL("C.M", """ { // Code size 10 (0xa) .maxstack 1 .locals init (byte& V_0) //b IL_0000: ldarg.1 IL_0001: stloc.0 IL_0002: ldloc.0 IL_0003: call "string byte.ToString()" IL_0008: pop IL_0009: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79051")] public void Retrack_PointerToRefLocal_Nested() { var source = """ class C { unsafe void M(ref byte b1, byte* p) { ref byte local = ref b1; { ref byte b2 = ref *p; local = ref b2; } local.ToString(); } } """; CompileAndVerify(source, verify: Verification.Fails, options: TestOptions.UnsafeDebugDll).VerifyIL("C.M", """ { // Code size 17 (0x11) .maxstack 1 .locals init (byte& V_0, //local byte& V_1) //b2 IL_0000: nop IL_0001: ldarg.1 IL_0002: stloc.0 IL_0003: nop IL_0004: ldarg.2 IL_0005: stloc.1 IL_0006: ldloc.1 IL_0007: stloc.0 IL_0008: nop IL_0009: ldloc.0 IL_000a: call "string byte.ToString()" IL_000f: pop IL_0010: ret } """); CompileAndVerify(source, verify: Verification.Fails, options: TestOptions.UnsafeReleaseDll).VerifyIL("C.M", """ { // Code size 13 (0xd) .maxstack 1 .locals init (byte& V_0) //b2 IL_0000: ldarg.1 IL_0001: ldind.u1 IL_0002: pop IL_0003: ldarg.2 IL_0004: stloc.0 IL_0005: ldloc.0 IL_0006: call "string byte.ToString()" IL_000b: pop IL_000c: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79051")] public void Retrack_PointerToRefLocal_Arg() { var source = """ class C { unsafe void M1(byte* p) { ref byte b = ref M2(ref *p); b.ToString(); } ref byte M2(ref byte b) => ref b; } """; CompileAndVerify(source, verify: Verification.Fails, options: TestOptions.UnsafeDebugDll).VerifyIL("C.M1", """ { // Code size 17 (0x11) .maxstack 2 .locals init (byte& V_0) //b IL_0000: nop IL_0001: ldarg.0 IL_0002: ldarg.1 IL_0003: call "ref byte C.M2(ref byte)" IL_0008: stloc.0 IL_0009: ldloc.0 IL_000a: call "string byte.ToString()" IL_000f: pop IL_0010: ret } """); CompileAndVerify(source, verify: Verification.Fails, options: TestOptions.UnsafeReleaseDll).VerifyIL("C.M1", """ { // Code size 14 (0xe) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: call "ref byte C.M2(ref byte)" IL_0007: call "string byte.ToString()" IL_000c: pop IL_000d: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79051")] public void Retrack_PointerToRefLocal_Arg_EvaluationOrder() { // Perhaps the pointer-to-ref conversion should be preserved in the IL (via a ref local) // so GC can re-track it before evaulating other arguments (which can have side effects), // but there is no ref local in the C# source, so the current behavior might be expected. var source = """ class C { unsafe void M1(byte* p) { ref byte b = ref M2(ref *p, M3()); b.ToString(); } ref byte M2(ref byte b, int i) => ref b; int M3() => 42; } """; CompileAndVerify(source, verify: Verification.Fails, options: TestOptions.UnsafeDebugDll).VerifyIL("C.M1", """ { // Code size 23 (0x17) .maxstack 3 .locals init (byte& V_0) //b IL_0000: nop IL_0001: ldarg.0 IL_0002: ldarg.1 IL_0003: ldarg.0 IL_0004: call "int C.M3()" IL_0009: call "ref byte C.M2(ref byte, int)" IL_000e: stloc.0 IL_000f: ldloc.0 IL_0010: call "string byte.ToString()" IL_0015: pop IL_0016: ret } """); CompileAndVerify(source, verify: Verification.Fails, options: TestOptions.UnsafeReleaseDll).VerifyIL("C.M1", """ { // Code size 20 (0x14) .maxstack 3 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: ldarg.0 IL_0003: call "int C.M3()" IL_0008: call "ref byte C.M2(ref byte, int)" IL_000d: call "string byte.ToString()" IL_0012: pop IL_0013: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79051")] public void Retrack_PointerToRefLocal_Arg_FunctionPointer_ReturnsRef() { var source = """ class C { unsafe void M(byte* p, delegate*<ref byte, ref byte> f) { ref byte b = ref f(ref *p); b.ToString(); } } """; CompileAndVerify(source, verify: Verification.Fails, options: TestOptions.UnsafeDebugDll).VerifyIL("C.M", """ { // Code size 19 (0x13) .maxstack 2 .locals init (byte& V_0, //b delegate*<ref byte, ref byte> V_1) IL_0000: nop IL_0001: ldarg.2 IL_0002: stloc.1 IL_0003: ldarg.1 IL_0004: ldloc.1 IL_0005: calli "delegate*<ref byte, ref byte>" IL_000a: stloc.0 IL_000b: ldloc.0 IL_000c: call "string byte.ToString()" IL_0011: pop IL_0012: ret } """); CompileAndVerify(source, verify: Verification.Fails, options: TestOptions.UnsafeReleaseDll).VerifyIL("C.M", """ { // Code size 16 (0x10) .maxstack 2 .locals init (delegate*<ref byte, ref byte> V_0) IL_0000: ldarg.2 IL_0001: stloc.0 IL_0002: ldarg.1 IL_0003: ldloc.0 IL_0004: calli "delegate*<ref byte, ref byte>" IL_0009: call "string byte.ToString()" IL_000e: pop IL_000f: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79051")] public void Retrack_PointerToRefLocal_Arg_FunctionPointer_ReturnsPointer() { var source = """ class C { unsafe void M(byte* p, delegate*<ref byte, byte*> f) { ref byte b = ref *f(ref *p); b.ToString(); } } """; CompileAndVerify(source, verify: Verification.Fails, options: TestOptions.UnsafeDebugDll).VerifyIL("C.M", """ { // Code size 19 (0x13) .maxstack 2 .locals init (byte& V_0, //b delegate*<ref byte, byte*> V_1) IL_0000: nop IL_0001: ldarg.2 IL_0002: stloc.1 IL_0003: ldarg.1 IL_0004: ldloc.1 IL_0005: calli "delegate*<ref byte, byte*>" IL_000a: stloc.0 IL_000b: ldloc.0 IL_000c: call "string byte.ToString()" IL_0011: pop IL_0012: ret } """); CompileAndVerify(source, verify: Verification.Fails, options: TestOptions.UnsafeReleaseDll).VerifyIL("C.M", """ { // Code size 18 (0x12) .maxstack 2 .locals init (byte& V_0, //b delegate*<ref byte, byte*> V_1) IL_0000: ldarg.2 IL_0001: stloc.1 IL_0002: ldarg.1 IL_0003: ldloc.1 IL_0004: calli "delegate*<ref byte, byte*>" IL_0009: stloc.0 IL_000a: ldloc.0 IL_000b: call "string byte.ToString()" IL_0010: pop IL_0011: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79051")] public void Retrack_PointerToRefLocal_FunctionPointer_ReturnsRef() { var source = """ class C { unsafe void M(delegate*<ref byte> f) { ref byte b = ref f(); b.ToString(); } } """; CompileAndVerify(source, verify: Verification.Fails, options: TestOptions.UnsafeDebugDll).VerifyIL("C.M", """ { // Code size 16 (0x10) .maxstack 1 .locals init (byte& V_0) //b IL_0000: nop IL_0001: ldarg.1 IL_0002: calli "delegate*<ref byte>" IL_0007: stloc.0 IL_0008: ldloc.0 IL_0009: call "string byte.ToString()" IL_000e: pop IL_000f: ret } """); CompileAndVerify(source, verify: Verification.Fails, options: TestOptions.UnsafeReleaseDll).VerifyIL("C.M", """ { // Code size 13 (0xd) .maxstack 1 IL_0000: ldarg.1 IL_0001: calli "delegate*<ref byte>" IL_0006: call "string byte.ToString()" IL_000b: pop IL_000c: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79051")] public void Retrack_PointerToRefLocal_FunctionPointer_ReturnsPointer() { var source = """ class C { unsafe void M(delegate*<byte*> f) { ref byte b = ref *f(); b.ToString(); } } """; CompileAndVerify(source, verify: Verification.Fails, options: TestOptions.UnsafeDebugDll).VerifyIL("C.M", """ { // Code size 16 (0x10) .maxstack 1 .locals init (byte& V_0) //b IL_0000: nop IL_0001: ldarg.1 IL_0002: calli "delegate*<byte*>" IL_0007: stloc.0 IL_0008: ldloc.0 IL_0009: call "string byte.ToString()" IL_000e: pop IL_000f: ret } """); CompileAndVerify(source, verify: Verification.Fails, options: TestOptions.UnsafeReleaseDll).VerifyIL("C.M", """ { // Code size 15 (0xf) .maxstack 1 .locals init (byte& V_0) //b IL_0000: ldarg.1 IL_0001: calli "delegate*<byte*>" IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: call "string byte.ToString()" IL_000d: pop IL_000e: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79051")] public void Retrack_PointerToRefLocal_Synthesized() { // Implicit (synthesized) ref locals can be elided. var source = """ class C { unsafe void M(byte* p) { (*p)++; } } """; CompileAndVerify(source, verify: Verification.Fails, options: TestOptions.UnsafeDebugDll).VerifyIL("C.M", """ { // Code size 9 (0x9) .maxstack 3 IL_0000: nop IL_0001: ldarg.1 IL_0002: dup IL_0003: ldind.u1 IL_0004: ldc.i4.1 IL_0005: add IL_0006: conv.u1 IL_0007: stind.i1 IL_0008: ret } """); CompileAndVerify(source, verify: Verification.Fails, options: TestOptions.UnsafeReleaseDll).VerifyIL("C.M", """ { // Code size 8 (0x8) .maxstack 3 IL_0000: ldarg.1 IL_0001: dup IL_0002: ldind.u1 IL_0003: ldc.i4.1 IL_0004: add IL_0005: conv.u1 IL_0006: stind.i1 IL_0007: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79051")] public void Retrack_RefLocalToRefLocal() { // There is no pointer-to-ref conversion, so the ref local b2 can be elided in Release mode. var source = """ class C { void M(ref byte b1) { ref byte b2 = ref b1; b2.ToString(); } } """; CompileAndVerify(source, options: TestOptions.DebugDll).VerifyIL("C.M", """ { // Code size 11 (0xb) .maxstack 1 .locals init (byte& V_0) //b2 IL_0000: nop IL_0001: ldarg.1 IL_0002: stloc.0 IL_0003: ldloc.0 IL_0004: call "string byte.ToString()" IL_0009: pop IL_000a: ret } """); CompileAndVerify(source, options: TestOptions.ReleaseDll).VerifyIL("C.M", """ { // Code size 8 (0x8) .maxstack 1 IL_0000: ldarg.1 IL_0001: call "string byte.ToString()" IL_0006: pop IL_0007: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/82137")] public void Retrack_PointerToRefLocal_NestedAssignment() { var source = """ class C { unsafe void M(byte* p) { ref byte b1 = ref F(); ref byte b2 = ref (b1 = ref *p); b2.ToString(); } ref byte F() => throw null; } """; CompileAndVerify(source, verify: Verification.Fails, options: TestOptions.UnsafeDebugDll).VerifyIL("C.M", """ { // Code size 20 (0x14) .maxstack 2 .locals init (byte& V_0, //b1 byte& V_1) //b2 IL_0000: nop IL_0001: ldarg.0 IL_0002: call "ref byte C.F()" IL_0007: stloc.0 IL_0008: ldarg.1 IL_0009: dup IL_000a: stloc.0 IL_000b: stloc.1 IL_000c: ldloc.1 IL_000d: call "string byte.ToString()" IL_0012: pop IL_0013: ret } """); CompileAndVerify(source, verify: Verification.Fails, options: TestOptions.UnsafeReleaseDll).VerifyIL("C.M", """ { // Code size 19 (0x13) .maxstack 2 .locals init (byte& V_0, //b1 byte& V_1) //b2 IL_0000: ldarg.0 IL_0001: call "ref byte C.F()" IL_0006: stloc.0 IL_0007: ldarg.1 IL_0008: dup IL_0009: stloc.0 IL_000a: stloc.1 IL_000b: ldloc.1 IL_000c: call "string byte.ToString()" IL_0011: pop IL_0012: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/82137")] public void Retrack_PointerToRefLocal_Field() { var source = """ class C { unsafe void M(System.ValueTuple<byte>* p) { ref byte b = ref p->Item1; b.ToString(); } } """; CompileAndVerify(source, verify: Verification.Fails, options: TestOptions.UnsafeDebugDll).VerifyIL("C.M", """ { // Code size 16 (0x10) .maxstack 1 .locals init (byte& V_0) //b IL_0000: nop IL_0001: ldarg.1 IL_0002: ldflda "byte System.ValueTuple<byte>.Item1" IL_0007: stloc.0 IL_0008: ldloc.0 IL_0009: call "string byte.ToString()" IL_000e: pop IL_000f: ret } """); CompileAndVerify(source, verify: Verification.Fails, options: TestOptions.UnsafeReleaseDll).VerifyIL("C.M", """ { // Code size 15 (0xf) .maxstack 1 .locals init (byte& V_0) //b IL_0000: ldarg.1 IL_0001: ldflda "byte System.ValueTuple<byte>.Item1" IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: call "string byte.ToString()" IL_000d: pop IL_000e: ret } """); } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/82137")] [InlineData("s->F")] [InlineData("(*s).F")] [InlineData("s[0].F")] public void Retrack_PointerToRefLocal_Field_Field(string expr) { var source = $$""" struct S { System.ValueTuple<byte> F; unsafe void M(S* s) { ref byte b = ref {{expr}}.Item1; b.ToString(); } } """; CompileAndVerify(source, verify: Verification.Fails, options: TestOptions.UnsafeDebugDll).VerifyIL("S.M", """ { // Code size 21 (0x15) .maxstack 1 .locals init (byte& V_0) //b IL_0000: nop IL_0001: ldarg.1 IL_0002: ldflda "System.ValueTuple<byte> S.F" IL_0007: ldflda "byte System.ValueTuple<byte>.Item1" IL_000c: stloc.0 IL_000d: ldloc.0 IL_000e: call "string byte.ToString()" IL_0013: pop IL_0014: ret } """); CompileAndVerify(source, verify: Verification.Fails, options: TestOptions.UnsafeReleaseDll).VerifyIL("S.M", """ { // Code size 20 (0x14) .maxstack 1 .locals init (byte& V_0) //b IL_0000: ldarg.1 IL_0001: ldflda "System.ValueTuple<byte> S.F" IL_0006: ldflda "byte System.ValueTuple<byte>.Item1" IL_000b: stloc.0 IL_000c: ldloc.0 IL_000d: call "string byte.ToString()" IL_0012: pop IL_0013: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/82137")] public void Retrack_PointerToRefLocal_FieldPointer_Field() { var source = """ struct S { unsafe System.ValueTuple<byte>* F; unsafe void M(S* s) { ref byte b = ref s->F->Item1; b.ToString(); } } """; CompileAndVerify(source, verify: Verification.Fails, options: TestOptions.UnsafeDebugDll).VerifyIL("S.M", """ { // Code size 21 (0x15) .maxstack 1 .locals init (byte& V_0) //b IL_0000: nop IL_0001: ldarg.1 IL_0002: ldfld "System.ValueTuple<byte>* S.F" IL_0007: ldflda "byte System.ValueTuple<byte>.Item1" IL_000c: stloc.0 IL_000d: ldloc.0 IL_000e: call "string byte.ToString()" IL_0013: pop IL_0014: ret } """); CompileAndVerify(source, verify: Verification.Fails, options: TestOptions.UnsafeReleaseDll).VerifyIL("S.M", """ { // Code size 20 (0x14) .maxstack 1 .locals init (byte& V_0) //b IL_0000: ldarg.1 IL_0001: ldfld "System.ValueTuple<byte>* S.F" IL_0006: ldflda "byte System.ValueTuple<byte>.Item1" IL_000b: stloc.0 IL_000c: ldloc.0 IL_000d: call "string byte.ToString()" IL_0012: pop IL_0013: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/82137")] public void Retrack_PointerToRefLocal_Function() { var source = """ struct S { unsafe byte* F() => null; unsafe void M(S* s) { ref byte b = ref *s->F(); b.ToString(); } } """; CompileAndVerify(source, verify: Verification.Fails, options: TestOptions.UnsafeDebugDll).VerifyIL("S.M", """ { // Code size 16 (0x10) .maxstack 1 .locals init (byte& V_0) //b IL_0000: nop IL_0001: ldarg.1 IL_0002: call "byte* S.F()" IL_0007: stloc.0 IL_0008: ldloc.0 IL_0009: call "string byte.ToString()" IL_000e: pop IL_000f: ret } """); CompileAndVerify(source, verify: Verification.Fails, options: TestOptions.UnsafeReleaseDll).VerifyIL("S.M", """ { // Code size 15 (0xf) .maxstack 1 .locals init (byte& V_0) //b IL_0000: ldarg.1 IL_0001: call "byte* S.F()" IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: call "string byte.ToString()" IL_000d: pop IL_000e: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/82137")] public void Retrack_PointerToRefLocal_Function_Field() { var source = """ struct S { unsafe System.ValueTuple<byte>* F() => null; unsafe void M(S* s) { ref byte b = ref s->F()->Item1; b.ToString(); } } """; CompileAndVerify(source, verify: Verification.Fails, options: TestOptions.UnsafeDebugDll).VerifyIL("S.M", """ { // Code size 21 (0x15) .maxstack 1 .locals init (byte& V_0) //b IL_0000: nop IL_0001: ldarg.1 IL_0002: call "System.ValueTuple<byte>* S.F()" IL_0007: ldflda "byte System.ValueTuple<byte>.Item1" IL_000c: stloc.0 IL_000d: ldloc.0 IL_000e: call "string byte.ToString()" IL_0013: pop IL_0014: ret } """); CompileAndVerify(source, verify: Verification.Fails, options: TestOptions.UnsafeReleaseDll).VerifyIL("S.M", """ { // Code size 20 (0x14) .maxstack 1 .locals init (byte& V_0) //b IL_0000: ldarg.1 IL_0001: call "System.ValueTuple<byte>* S.F()" IL_0006: ldflda "byte System.ValueTuple<byte>.Item1" IL_000b: stloc.0 IL_000c: ldloc.0 IL_000d: call "string byte.ToString()" IL_0012: pop IL_0013: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/82137")] public void Retrack_PointerToRefLocal_Conditional_Both() { var source = """ class C { unsafe void M(bool c, byte* p1, byte* p2) { ref byte b = ref c ? ref *p1 : ref *p2; b.ToString(); } } """; CompileAndVerify(source, verify: Verification.Fails, options: TestOptions.UnsafeDebugDll).VerifyIL("C.M", """ { // Code size 17 (0x11) .maxstack 1 .locals init (byte& V_0) //b IL_0000: nop IL_0001: ldarg.1 IL_0002: brtrue.s IL_0007 IL_0004: ldarg.3 IL_0005: br.s IL_0008 IL_0007: ldarg.2 IL_0008: stloc.0 IL_0009: ldloc.0 IL_000a: call "string byte.ToString()" IL_000f: pop IL_0010: ret } """); CompileAndVerify(source, verify: Verification.Fails, options: TestOptions.UnsafeReleaseDll).VerifyIL("C.M", """ { // Code size 16 (0x10) .maxstack 1 .locals init (byte& V_0) //b IL_0000: ldarg.1 IL_0001: brtrue.s IL_0006 IL_0003: ldarg.3 IL_0004: br.s IL_0007 IL_0006: ldarg.2 IL_0007: stloc.0 IL_0008: ldloc.0 IL_0009: call "string byte.ToString()" IL_000e: pop IL_000f: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/82137")] public void Retrack_PointerToRefLocal_Conditional_One() { var source = """ class C { unsafe void M(bool c, ref byte p1, byte* p2) { ref byte b = ref c ? ref p1 : ref *p2; b.ToString(); } } """; CompileAndVerify(source, verify: Verification.Fails, options: TestOptions.UnsafeDebugDll).VerifyIL("C.M", """ { // Code size 17 (0x11) .maxstack 1 .locals init (byte& V_0) //b IL_0000: nop IL_0001: ldarg.1 IL_0002: brtrue.s IL_0007 IL_0004: ldarg.3 IL_0005: br.s IL_0008 IL_0007: ldarg.2 IL_0008: stloc.0 IL_0009: ldloc.0 IL_000a: call "string byte.ToString()" IL_000f: pop IL_0010: ret } """); CompileAndVerify(source, verify: Verification.Fails, options: TestOptions.UnsafeReleaseDll).VerifyIL("C.M", """ { // Code size 16 (0x10) .maxstack 1 .locals init (byte& V_0) //b IL_0000: ldarg.1 IL_0001: brtrue.s IL_0006 IL_0003: ldarg.3 IL_0004: br.s IL_0007 IL_0006: ldarg.2 IL_0007: stloc.0 IL_0008: ldloc.0 IL_0009: call "string byte.ToString()" IL_000e: pop IL_000f: ret } """); } [Fact] public void Retrack_PointerToRefLocal_Conditional_None() { var source = """ class C { unsafe void M(bool c, ref byte p1, ref byte p2) { ref byte b = ref c ? ref p1 : ref p2; b.ToString(); } } """; CompileAndVerify(source, options: TestOptions.UnsafeDebugDll).VerifyIL("C.M", """ { // Code size 17 (0x11) .maxstack 1 .locals init (byte& V_0) //b IL_0000: nop IL_0001: ldarg.1 IL_0002: brtrue.s IL_0007 IL_0004: ldarg.3 IL_0005: br.s IL_0008 IL_0007: ldarg.2 IL_0008: stloc.0 IL_0009: ldloc.0 IL_000a: call "string byte.ToString()" IL_000f: pop IL_0010: ret } """); CompileAndVerify(source, options: TestOptions.UnsafeReleaseDll).VerifyIL("C.M", """ { // Code size 14 (0xe) .maxstack 1 IL_0000: ldarg.1 IL_0001: brtrue.s IL_0006 IL_0003: ldarg.3 IL_0004: br.s IL_0007 IL_0006: ldarg.2 IL_0007: call "string byte.ToString()" IL_000c: pop IL_000d: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/82137")] public void Retrack_PointerToRefLocal_Conditional_Field_01() { var source = """ class C { unsafe void M(bool c, System.ValueTuple<byte, byte>* t) { ref byte b = ref c ? ref t->Item1 : ref t->Item2; b.ToString(); } } """; CompileAndVerify(source, verify: Verification.Fails, options: TestOptions.UnsafeDebugDll).VerifyIL("C.M", """ { // Code size 27 (0x1b) .maxstack 1 .locals init (byte& V_0) //b IL_0000: nop IL_0001: ldarg.1 IL_0002: brtrue.s IL_000c IL_0004: ldarg.2 IL_0005: ldflda "byte System.ValueTuple<byte, byte>.Item2" IL_000a: br.s IL_0012 IL_000c: ldarg.2 IL_000d: ldflda "byte System.ValueTuple<byte, byte>.Item1" IL_0012: stloc.0 IL_0013: ldloc.0 IL_0014: call "string byte.ToString()" IL_0019: pop IL_001a: ret } """); CompileAndVerify(source, verify: Verification.Fails, options: TestOptions.UnsafeReleaseDll).VerifyIL("C.M", """ { // Code size 26 (0x1a) .maxstack 1 .locals init (byte& V_0) //b IL_0000: ldarg.1 IL_0001: brtrue.s IL_000b IL_0003: ldarg.2 IL_0004: ldflda "byte System.ValueTuple<byte, byte>.Item2" IL_0009: br.s IL_0011 IL_000b: ldarg.2 IL_000c: ldflda "byte System.ValueTuple<byte, byte>.Item1" IL_0011: stloc.0 IL_0012: ldloc.0 IL_0013: call "string byte.ToString()" IL_0018: pop IL_0019: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/82137")] public void Retrack_PointerToRefLocal_Conditional_Field_02() { var source = """ class C { unsafe void M(bool c, System.ValueTuple<byte>* t1, System.ValueTuple<byte>* t2) { ref byte b = ref (c ? ref *t1 : ref *t2).Item1; b.ToString(); } } """; CompileAndVerify(source, verify: Verification.Fails, options: TestOptions.UnsafeDebugDll).VerifyIL("C.M", """ { // Code size 22 (0x16) .maxstack 1 .locals init (byte& V_0) //b IL_0000: nop IL_0001: ldarg.1 IL_0002: brtrue.s IL_0007 IL_0004: ldarg.3 IL_0005: br.s IL_0008 IL_0007: ldarg.2 IL_0008: ldflda "byte System.ValueTuple<byte>.Item1" IL_000d: stloc.0 IL_000e: ldloc.0 IL_000f: call "string byte.ToString()" IL_0014: pop IL_0015: ret } """); CompileAndVerify(source, verify: Verification.Fails, options: TestOptions.UnsafeReleaseDll).VerifyIL("C.M", """ { // Code size 21 (0x15) .maxstack 1 .locals init (byte& V_0) //b IL_0000: ldarg.1 IL_0001: brtrue.s IL_0006 IL_0003: ldarg.3 IL_0004: br.s IL_0007 IL_0006: ldarg.2 IL_0007: ldflda "byte System.ValueTuple<byte>.Item1" IL_000c: stloc.0 IL_000d: ldloc.0 IL_000e: call "string byte.ToString()" IL_0013: pop IL_0014: ret } """); } #endregion Dereference tests #region Pointer member access tests [Fact] public void PointerMemberAccessRead() { var text = @" using System; unsafe struct S { int x; static void Main() { S s; s.x = 3; S* p = &s; Console.Write(p->x); } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: @"3", verify: Verification.Fails); compVerifier.VerifyIL("S.Main", @" { // Code size 22 (0x16) .maxstack 2 .locals init (S V_0) //s IL_0000: ldloca.s V_0 IL_0002: ldc.i4.3 IL_0003: stfld ""int S.x"" IL_0008: ldloca.s V_0 IL_000a: conv.u IL_000b: ldfld ""int S.x"" IL_0010: call ""void System.Console.Write(int)"" IL_0015: ret } "); compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: @"3", verify: Verification.Fails); compVerifier.VerifyIL("S.Main", @" { // Code size 22 (0x16) .maxstack 2 .locals init (S V_0) //s IL_0000: ldloca.s V_0 IL_0002: ldc.i4.3 IL_0003: stfld ""int S.x"" IL_0008: ldloca.s V_0 IL_000a: conv.u IL_000b: ldfld ""int S.x"" IL_0010: call ""void System.Console.Write(int)"" IL_0015: ret } "); } [Fact] public void PointerMemberAccessWrite() { var text = @" using System; unsafe struct S { int x; static void Main() { S s; s.x = 3; S* p = &s; Console.Write(s.x); p->x = 4; Console.Write(s.x); } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: @"34", verify: Verification.Fails); compVerifier.VerifyIL("S.Main", @" { // Code size 40 (0x28) .maxstack 2 .locals init (S V_0) //s IL_0000: ldloca.s V_0 IL_0002: ldc.i4.3 IL_0003: stfld ""int S.x"" IL_0008: ldloca.s V_0 IL_000a: conv.u IL_000b: ldloc.0 IL_000c: ldfld ""int S.x"" IL_0011: call ""void System.Console.Write(int)"" IL_0016: ldc.i4.4 IL_0017: stfld ""int S.x"" IL_001c: ldloc.0 IL_001d: ldfld ""int S.x"" IL_0022: call ""void System.Console.Write(int)"" IL_0027: ret } "); compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: @"34", verify: Verification.Fails); compVerifier.VerifyIL("S.Main", @" { // Code size 40 (0x28) .maxstack 2 .locals init (S V_0) //s IL_0000: ldloca.s V_0 IL_0002: ldc.i4.3 IL_0003: stfld ""int S.x"" IL_0008: ldloca.s V_0 IL_000a: conv.u IL_000b: ldloc.0 IL_000c: ldfld ""int S.x"" IL_0011: call ""void System.Console.Write(int)"" IL_0016: ldc.i4.4 IL_0017: stfld ""int S.x"" IL_001c: ldloc.0 IL_001d: ldfld ""int S.x"" IL_0022: call ""void System.Console.Write(int)"" IL_0027: ret } "); } [Fact] public void PointerMemberAccessInvoke() { var text = @" using System; unsafe struct S { static void Main() { S s; S* p = &s; p->M(); p->M(1); p->M(1, 2); } void M() { Console.Write(1); } void M(int x) { Console.Write(2); } } static class Extensions { public static void M(this S s, int x, int y) { Console.Write(3); } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: @"123", verify: Verification.Fails); compVerifier.VerifyIL("S.Main", @" { // Code size 29 (0x1d) .maxstack 3 .locals init (S V_0) //s IL_0000: ldloca.s V_0 IL_0002: conv.u IL_0003: dup IL_0004: call ""void S.M()"" IL_0009: dup IL_000a: ldc.i4.1 IL_000b: call ""void S.M(int)"" IL_0010: ldobj ""S"" IL_0015: ldc.i4.1 IL_0016: ldc.i4.2 IL_0017: call ""void Extensions.M(S, int, int)"" IL_001c: ret } "); compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: @"123", verify: Verification.Fails); compVerifier.VerifyIL("S.Main", @" { // Code size 29 (0x1d) .maxstack 3 .locals init (S V_0) //s IL_0000: ldloca.s V_0 IL_0002: conv.u IL_0003: dup IL_0004: call ""void S.M()"" IL_0009: dup IL_000a: ldc.i4.1 IL_000b: call ""void S.M(int)"" IL_0010: ldobj ""S"" IL_0015: ldc.i4.1 IL_0016: ldc.i4.2 IL_0017: call ""void Extensions.M(S, int, int)"" IL_001c: ret } "); } [Fact] public void PointerMemberAccessInvoke001() { var text = @" using System; unsafe struct S { static void Main() { S s; S* p = &s; Test(ref p); } static void Test(ref S* p) { p->M(); p->M(1); p->M(1, 2); } void M() { Console.Write(1); } void M(int x) { Console.Write(2); } } static class Extensions { public static void M(this S s, int x, int y) { Console.Write(3); } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: @"123", verify: Verification.Fails); compVerifier.VerifyIL("S.Test(ref S*)", @" { // Code size 30 (0x1e) .maxstack 3 IL_0000: ldarg.0 IL_0001: ldind.i IL_0002: call ""void S.M()"" IL_0007: ldarg.0 IL_0008: ldind.i IL_0009: ldc.i4.1 IL_000a: call ""void S.M(int)"" IL_000f: ldarg.0 IL_0010: ldind.i IL_0011: ldobj ""S"" IL_0016: ldc.i4.1 IL_0017: ldc.i4.2 IL_0018: call ""void Extensions.M(S, int, int)"" IL_001d: ret } "); compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: @"123", verify: Verification.Fails); compVerifier.VerifyIL("S.Test(ref S*)", @" { // Code size 30 (0x1e) .maxstack 3 IL_0000: ldarg.0 IL_0001: ldind.i IL_0002: call ""void S.M()"" IL_0007: ldarg.0 IL_0008: ldind.i IL_0009: ldc.i4.1 IL_000a: call ""void S.M(int)"" IL_000f: ldarg.0 IL_0010: ldind.i IL_0011: ldobj ""S"" IL_0016: ldc.i4.1 IL_0017: ldc.i4.2 IL_0018: call ""void Extensions.M(S, int, int)"" IL_001d: ret } "); } [Fact] public void PointerMemberAccessMutate() { var text = @" using System; unsafe struct S { int x; static void Main() { S s; s.x = 3; S* p = &s; Console.Write((p->x)++); Console.Write((p->x)++); } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: @"34", verify: Verification.Fails); compVerifier.VerifyIL("S.Main", @" { // Code size 49 (0x31) .maxstack 4 .locals init (S V_0, //s int V_1) IL_0000: ldloca.s V_0 IL_0002: ldc.i4.3 IL_0003: stfld ""int S.x"" IL_0008: ldloca.s V_0 IL_000a: conv.u IL_000b: dup IL_000c: ldflda ""int S.x"" IL_0011: dup IL_0012: ldind.i4 IL_0013: stloc.1 IL_0014: ldloc.1 IL_0015: ldc.i4.1 IL_0016: add IL_0017: stind.i4 IL_0018: ldloc.1 IL_0019: call ""void System.Console.Write(int)"" IL_001e: ldflda ""int S.x"" IL_0023: dup IL_0024: ldind.i4 IL_0025: stloc.1 IL_0026: ldloc.1 IL_0027: ldc.i4.1 IL_0028: add IL_0029: stind.i4 IL_002a: ldloc.1 IL_002b: call ""void System.Console.Write(int)"" IL_0030: ret } "); compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: @"34", verify: Verification.Fails); compVerifier.VerifyIL("S.Main", @" { // Code size 49 (0x31) .maxstack 4 .locals init (S V_0, //s int V_1) IL_0000: ldloca.s V_0 IL_0002: ldc.i4.3 IL_0003: stfld ""int S.x"" IL_0008: ldloca.s V_0 IL_000a: conv.u IL_000b: dup IL_000c: ldflda ""int S.x"" IL_0011: dup IL_0012: ldind.i4 IL_0013: stloc.1 IL_0014: ldloc.1 IL_0015: ldc.i4.1 IL_0016: add IL_0017: stind.i4 IL_0018: ldloc.1 IL_0019: call ""void System.Console.Write(int)"" IL_001e: ldflda ""int S.x"" IL_0023: dup IL_0024: ldind.i4 IL_0025: stloc.1 IL_0026: ldloc.1 IL_0027: ldc.i4.1 IL_0028: add IL_0029: stind.i4 IL_002a: ldloc.1 IL_002b: call ""void System.Console.Write(int)"" IL_0030: ret } "); } #endregion Pointer member access tests #region Pointer element access tests [Fact] public void PointerElementAccessCheckedAndUnchecked() { var text = @" using System; unsafe struct S { static void Main() { S s = new S(); S* p = &s; int i = (int)p; uint ui = (uint)p; long l = (long)p; ulong ul = (ulong)p; checked { s = p[i]; s = p[ui]; s = p[l]; s = p[ul]; } unchecked { s = p[i]; s = p[ui]; s = p[l]; s = p[ul]; } } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, verify: Verification.Fails); // The conversions differ from dev10 in the same way as for numeric addition. // Note that, unlike for numeric addition, the add operation is never checked. compVerifier.VerifyIL("S.Main", @" { // Code size 170 (0xaa) .maxstack 4 .locals init (S V_0, //s int V_1, //i uint V_2, //ui long V_3, //l ulong V_4) //ul IL_0000: ldloca.s V_0 IL_0002: initobj ""S"" IL_0008: ldloca.s V_0 IL_000a: conv.u IL_000b: dup IL_000c: conv.i4 IL_000d: stloc.1 IL_000e: dup IL_000f: conv.u4 IL_0010: stloc.2 IL_0011: dup IL_0012: conv.u8 IL_0013: stloc.3 IL_0014: dup IL_0015: conv.u8 IL_0016: stloc.s V_4 IL_0018: dup IL_0019: ldloc.1 IL_001a: conv.i IL_001b: sizeof ""S"" IL_0021: mul.ovf IL_0022: add IL_0023: ldobj ""S"" IL_0028: stloc.0 IL_0029: dup IL_002a: ldloc.2 IL_002b: conv.u8 IL_002c: sizeof ""S"" IL_0032: conv.i8 IL_0033: mul.ovf IL_0034: conv.i IL_0035: add IL_0036: ldobj ""S"" IL_003b: stloc.0 IL_003c: dup IL_003d: ldloc.3 IL_003e: sizeof ""S"" IL_0044: conv.i8 IL_0045: mul.ovf IL_0046: conv.i IL_0047: add IL_0048: ldobj ""S"" IL_004d: stloc.0 IL_004e: dup IL_004f: ldloc.s V_4 IL_0051: sizeof ""S"" IL_0057: conv.ovf.u8 IL_0058: mul.ovf.un IL_0059: conv.u IL_005a: add IL_005b: ldobj ""S"" IL_0060: stloc.0 IL_0061: dup IL_0062: ldloc.1 IL_0063: conv.i IL_0064: sizeof ""S"" IL_006a: mul IL_006b: add IL_006c: ldobj ""S"" IL_0071: stloc.0 IL_0072: dup IL_0073: ldloc.2 IL_0074: conv.u8 IL_0075: sizeof ""S"" IL_007b: conv.i8 IL_007c: mul IL_007d: conv.i IL_007e: add IL_007f: ldobj ""S"" IL_0084: stloc.0 IL_0085: dup IL_0086: ldloc.3 IL_0087: sizeof ""S"" IL_008d: conv.i8 IL_008e: mul IL_008f: conv.i IL_0090: add IL_0091: ldobj ""S"" IL_0096: stloc.0 IL_0097: ldloc.s V_4 IL_0099: sizeof ""S"" IL_009f: conv.i8 IL_00a0: mul IL_00a1: conv.u IL_00a2: add IL_00a3: ldobj ""S"" IL_00a8: stloc.0 IL_00a9: ret } "); } [Fact] public void PointerElementAccessWrite() { var text = @" using System; unsafe struct S { static void Main() { int* p = null; p[1] = 2; } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, verify: Verification.Fails); compVerifier.VerifyIL("S.Main", @" { // Code size 9 (0x9) .maxstack 2 .locals init (int* V_0) //p IL_0000: ldc.i4.0 IL_0001: conv.u IL_0002: stloc.0 IL_0003: ldloc.0 IL_0004: ldc.i4.4 IL_0005: add IL_0006: ldc.i4.2 IL_0007: stind.i4 IL_0008: ret } "); } [Fact] public void PointerElementAccessMutate() { var text = @" using System; unsafe struct S { static void Main() { int[] array = new int[3]; fixed (int* p = array) { p[1] += ++p[0]; p[2] -= p[1]--; } foreach (int element in array) { Console.WriteLine(element); } } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: @" 1 0 -1", verify: Verification.Fails); } [Fact] public void PointerElementAccessNested() { var text = @" using System; unsafe class C { static void Main() { fixed (int* q = new int[3]) { q[0] = 2; q[1] = 0; q[2] = 1; Console.Write(q[q[q[q[q[q[*q]]]]]]); Console.Write(q[q[q[q[q[q[q[*q]]]]]]]); Console.Write(q[q[q[q[q[q[q[q[*q]]]]]]]]); } } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: "210", verify: Verification.Fails); } [Fact] public void PointerElementAccessZero() { var text = @" using System; unsafe class C { static void Main() { int x = 1; int* p = &x; Console.WriteLine(p[0]); } } "; // NOTE: no pointer arithmetic - just dereference p. var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: "1", verify: Verification.Fails).VerifyIL("C.Main", @" { // Code size 12 (0xc) .maxstack 1 .locals init (int V_0) //x IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldloca.s V_0 IL_0004: conv.u IL_0005: ldind.i4 IL_0006: call ""void System.Console.WriteLine(int)"" IL_000b: ret } "); } #endregion Pointer element access tests #region Fixed statement tests [Fact] public void FixedStatementField() { var text = @" using System; unsafe class C { int x; static void Main() { C c = new C(); fixed (int* p = &c.x) { *p = 1; } Console.WriteLine(c.x); } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: @"1", verify: Verification.Fails); compVerifier.VerifyIL("C.Main", @" { // Code size 30 (0x1e) .maxstack 3 .locals init (pinned int& V_0) IL_0000: newobj ""C..ctor()"" IL_0005: dup IL_0006: ldflda ""int C.x"" IL_000b: stloc.0 IL_000c: ldloc.0 IL_000d: conv.u IL_000e: ldc.i4.1 IL_000f: stind.i4 IL_0010: ldc.i4.0 IL_0011: conv.u IL_0012: stloc.0 IL_0013: ldfld ""int C.x"" IL_0018: call ""void System.Console.WriteLine(int)"" IL_001d: ret } "); } [Fact] public void FixedStatementThis() { var text = @" public class Program { public static void Main() { S1 s = default; s.Test(); } unsafe readonly struct S1 { readonly int x; public void Test() { fixed(void* p = &this) { *(int*)p = 123; } ref readonly S1 r = ref this; fixed (S1* p = &r) { System.Console.WriteLine(p->x); } } } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: @"123", verify: Verification.Fails); compVerifier.VerifyIL("Program.S1.Test()", @" { // Code size 30 (0x1e) .maxstack 2 .locals init (void* V_0, //p pinned Program.S1& V_1) IL_0000: ldarg.0 IL_0001: stloc.1 IL_0002: ldloc.1 IL_0003: conv.u IL_0004: stloc.0 IL_0005: ldloc.0 IL_0006: ldc.i4.s 123 IL_0008: stind.i4 IL_0009: ldc.i4.0 IL_000a: conv.u IL_000b: stloc.1 IL_000c: ldarg.0 IL_000d: stloc.1 IL_000e: ldloc.1 IL_000f: conv.u IL_0010: ldfld ""int Program.S1.x"" IL_0015: call ""void System.Console.WriteLine(int)"" IL_001a: ldc.i4.0 IL_001b: conv.u IL_001c: stloc.1 IL_001d: ret } "); } [WorkItem(22306, "https://github.com/dotnet/roslyn/issues/22306")] [Fact] public void FixedStatementMultipleFields() { var text = @" using System; unsafe class C { int x; readonly int y; static void Main() { C c = new C(); fixed (int* p = &c.x, q = &c.y) { *p = 1; *q = 2; } Console.Write(c.x); Console.Write(c.y); } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: @"12", verify: Verification.Fails); compVerifier.VerifyIL("C.Main", @" { // Code size 57 (0x39) .maxstack 4 .locals init (int* V_0, //p pinned int& V_1, pinned int& V_2) IL_0000: newobj ""C..ctor()"" IL_0005: dup IL_0006: ldflda ""int C.x"" IL_000b: stloc.1 IL_000c: ldloc.1 IL_000d: conv.u IL_000e: stloc.0 IL_000f: dup IL_0010: ldflda ""int C.y"" IL_0015: stloc.2 IL_0016: ldloc.2 IL_0017: conv.u IL_0018: ldloc.0 IL_0019: ldc.i4.1 IL_001a: stind.i4 IL_001b: ldc.i4.2 IL_001c: stind.i4 IL_001d: ldc.i4.0 IL_001e: conv.u IL_001f: stloc.1 IL_0020: ldc.i4.0 IL_0021: conv.u IL_0022: stloc.2 IL_0023: dup IL_0024: ldfld ""int C.x"" IL_0029: call ""void System.Console.Write(int)"" IL_002e: ldfld ""int C.y"" IL_0033: call ""void System.Console.Write(int)"" IL_0038: ret } "); } [WorkItem(22306, "https://github.com/dotnet/roslyn/issues/22306")] [Fact] public void FixedStatementMultipleMethods() { var text = @" using System; unsafe class C { int x; readonly int y; ref int X()=>ref x; ref readonly int this[int i]=>ref y; static void Main() { C c = new C(); fixed (int* p = &c.X(), q = &c[3]) { *p = 1; *q = 2; } Console.Write(c.x); Console.Write(c.y); } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: @"12", verify: Verification.Fails); compVerifier.VerifyIL("C.Main", @" { // Code size 58 (0x3a) .maxstack 4 .locals init (int* V_0, //p pinned int& V_1, pinned int& V_2) IL_0000: newobj ""C..ctor()"" IL_0005: dup IL_0006: callvirt ""ref int C.X()"" IL_000b: stloc.1 IL_000c: ldloc.1 IL_000d: conv.u IL_000e: stloc.0 IL_000f: dup IL_0010: ldc.i4.3 IL_0011: callvirt ""ref readonly int C.this[int].get"" IL_0016: stloc.2 IL_0017: ldloc.2 IL_0018: conv.u IL_0019: ldloc.0 IL_001a: ldc.i4.1 IL_001b: stind.i4 IL_001c: ldc.i4.2 IL_001d: stind.i4 IL_001e: ldc.i4.0 IL_001f: conv.u IL_0020: stloc.1 IL_0021: ldc.i4.0 IL_0022: conv.u IL_0023: stloc.2 IL_0024: dup IL_0025: ldfld ""int C.x"" IL_002a: call ""void System.Console.Write(int)"" IL_002f: ldfld ""int C.y"" IL_0034: call ""void System.Console.Write(int)"" IL_0039: ret } "); } [WorkItem(546866, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546866")] [Fact] public void FixedStatementProperty() { var text = @"class C { string P { get { return null; } } char[] Q { get { return null; } } unsafe static void M(C c) { fixed (char* o = c.P) { } fixed (char* o = c.Q) { } } }"; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails); compVerifier.VerifyIL("C.M(C)", @" { // Code size 55 (0x37) .maxstack 2 .locals init (char* V_0, //o pinned string V_1, char* V_2, //o pinned char[] V_3) IL_0000: ldarg.0 IL_0001: callvirt ""string C.P.get"" IL_0006: stloc.1 IL_0007: ldloc.1 IL_0008: conv.u IL_0009: stloc.0 IL_000a: ldloc.0 IL_000b: brfalse.s IL_0015 IL_000d: ldloc.0 IL_000e: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_0013: add IL_0014: stloc.0 IL_0015: ldnull IL_0016: stloc.1 IL_0017: ldarg.0 IL_0018: callvirt ""char[] C.Q.get"" IL_001d: dup IL_001e: stloc.3 IL_001f: brfalse.s IL_0026 IL_0021: ldloc.3 IL_0022: ldlen IL_0023: conv.i4 IL_0024: brtrue.s IL_002b IL_0026: ldc.i4.0 IL_0027: conv.u IL_0028: stloc.2 IL_0029: br.s IL_0034 IL_002b: ldloc.3 IL_002c: ldc.i4.0 IL_002d: ldelema ""char"" IL_0032: conv.u IL_0033: stloc.2 IL_0034: ldnull IL_0035: stloc.3 IL_0036: ret } "); } [Fact] public void FixedStatementMultipleOptimized() { var text = @" using System; unsafe class C { int x; int y; static void Main() { C c = new C(); fixed (int* p = &c.x, q = &c.y) { *p = 1; *q = 2; } Console.Write(c.x); Console.Write(c.y); } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: @"12", verify: Verification.Fails); compVerifier.VerifyIL("C.Main", @" { // Code size 57 (0x39) .maxstack 4 .locals init (int* V_0, //p pinned int& V_1, pinned int& V_2) IL_0000: newobj ""C..ctor()"" IL_0005: dup IL_0006: ldflda ""int C.x"" IL_000b: stloc.1 IL_000c: ldloc.1 IL_000d: conv.u IL_000e: stloc.0 IL_000f: dup IL_0010: ldflda ""int C.y"" IL_0015: stloc.2 IL_0016: ldloc.2 IL_0017: conv.u IL_0018: ldloc.0 IL_0019: ldc.i4.1 IL_001a: stind.i4 IL_001b: ldc.i4.2 IL_001c: stind.i4 IL_001d: ldc.i4.0 IL_001e: conv.u IL_001f: stloc.1 IL_0020: ldc.i4.0 IL_0021: conv.u IL_0022: stloc.2 IL_0023: dup IL_0024: ldfld ""int C.x"" IL_0029: call ""void System.Console.Write(int)"" IL_002e: ldfld ""int C.y"" IL_0033: call ""void System.Console.Write(int)"" IL_0038: ret } "); } [Fact] public void FixedStatementReferenceParameter() { var text = @" using System; class C { static void Main() { char ch; M(out ch); Console.WriteLine(ch); } unsafe static void M(out char ch) { fixed (char* p = &ch) { *p = 'a'; } } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: @"a", verify: Verification.Fails); compVerifier.VerifyIL("C.M", @" { // Code size 11 (0xb) .maxstack 2 .locals init (pinned char& V_0) IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloc.0 IL_0003: conv.u IL_0004: ldc.i4.s 97 IL_0006: stind.i2 IL_0007: ldc.i4.0 IL_0008: conv.u IL_0009: stloc.0 IL_000a: ret } "); } [Fact] public void FixedStatementReferenceParameterDebug() { var text = @" using System; class C { static void Main() { char ch; M(out ch); Console.WriteLine(ch); } unsafe static void M(out char ch) { fixed (char* p = &ch) { *p = 'a'; } } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeDebugExe, expectedOutput: @"a", verify: Verification.Fails); compVerifier.VerifyIL("C.M", @" { // Code size 16 (0x10) .maxstack 2 .locals init (char* V_0, //p pinned char& V_1) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.1 IL_0003: ldloc.1 IL_0004: conv.u IL_0005: stloc.0 IL_0006: nop IL_0007: ldloc.0 IL_0008: ldc.i4.s 97 IL_000a: stind.i2 IL_000b: nop IL_000c: ldc.i4.0 IL_000d: conv.u IL_000e: stloc.1 IL_000f: ret } "); } [Fact] public void FixedStatementStringLiteral() { var text = @" using System; unsafe class C { static void Main() { fixed (char* p = ""hello"") { Console.WriteLine(*p); } } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeDebugExe, expectedOutput: @"h", verify: Verification.Fails); compVerifier.VerifyIL("C.Main", @" { // Code size 34 (0x22) .maxstack 2 .locals init (char* V_0, //p pinned string V_1) -IL_0000: nop IL_0001: ldstr ""hello"" IL_0006: stloc.1 -IL_0007: ldloc.1 IL_0008: conv.u IL_0009: stloc.0 IL_000a: ldloc.0 IL_000b: brfalse.s IL_0015 IL_000d: ldloc.0 IL_000e: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_0013: add IL_0014: stloc.0 -IL_0015: nop -IL_0016: ldloc.0 IL_0017: ldind.u2 IL_0018: call ""void System.Console.WriteLine(char)"" IL_001d: nop -IL_001e: nop ~IL_001f: ldnull IL_0020: stloc.1 -IL_0021: ret } ", sequencePointDisplay: SequencePointDisplayMode.Minimal); } [Fact] public void FixedStatementStringVariable() { var text = @" using System; unsafe class C { static void Main() { string s = ""hello""; fixed (char* p = s) { Console.Write(*p); } s = null; fixed (char* p = s) { Console.Write(p == null); } } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeDebugExe, expectedOutput: @"hTrue", verify: Verification.Fails); compVerifier.VerifyIL("C.Main", @" { // Code size 72 (0x48) .maxstack 2 .locals init (string V_0, //s char* V_1, //p pinned string V_2, char* V_3, //p pinned string V_4) -IL_0000: nop -IL_0001: ldstr ""hello"" IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: stloc.2 -IL_0009: ldloc.2 IL_000a: conv.u IL_000b: stloc.1 IL_000c: ldloc.1 IL_000d: brfalse.s IL_0017 IL_000f: ldloc.1 IL_0010: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_0015: add IL_0016: stloc.1 -IL_0017: nop -IL_0018: ldloc.1 IL_0019: ldind.u2 IL_001a: call ""void System.Console.Write(char)"" IL_001f: nop -IL_0020: nop ~IL_0021: ldnull IL_0022: stloc.2 -IL_0023: ldnull IL_0024: stloc.0 IL_0025: ldloc.0 IL_0026: stloc.s V_4 -IL_0028: ldloc.s V_4 IL_002a: conv.u IL_002b: stloc.3 IL_002c: ldloc.3 IL_002d: brfalse.s IL_0037 IL_002f: ldloc.3 IL_0030: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_0035: add IL_0036: stloc.3 -IL_0037: nop -IL_0038: ldloc.3 IL_0039: ldc.i4.0 IL_003a: conv.u IL_003b: ceq IL_003d: call ""void System.Console.Write(bool)"" IL_0042: nop -IL_0043: nop ~IL_0044: ldnull IL_0045: stloc.s V_4 -IL_0047: ret } ", sequencePointDisplay: SequencePointDisplayMode.Minimal); } [Fact] public void FixedStatementStringVariableOptimized() { var text = @" using System; unsafe class C { static void Main() { string s = ""hello""; fixed (char* p = s) { Console.Write(*p); } s = null; fixed (char* p = s) { Console.Write(p == null); } } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: @"hTrue", verify: Verification.Fails); // Null checks and branches are much simpler, but string temps are NOT optimized away. compVerifier.VerifyIL("C.Main", @" { // Code size 58 (0x3a) .maxstack 2 .locals init (char* V_0, //p pinned string V_1, char* V_2) //p IL_0000: ldstr ""hello"" IL_0005: stloc.1 IL_0006: ldloc.1 IL_0007: conv.u IL_0008: stloc.0 IL_0009: ldloc.0 IL_000a: brfalse.s IL_0014 IL_000c: ldloc.0 IL_000d: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_0012: add IL_0013: stloc.0 IL_0014: ldloc.0 IL_0015: ldind.u2 IL_0016: call ""void System.Console.Write(char)"" IL_001b: ldnull IL_001c: stloc.1 IL_001d: ldnull IL_001e: stloc.1 IL_001f: ldloc.1 IL_0020: conv.u IL_0021: stloc.2 IL_0022: ldloc.2 IL_0023: brfalse.s IL_002d IL_0025: ldloc.2 IL_0026: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_002b: add IL_002c: stloc.2 IL_002d: ldloc.2 IL_002e: ldc.i4.0 IL_002f: conv.u IL_0030: ceq IL_0032: call ""void System.Console.Write(bool)"" IL_0037: ldnull IL_0038: stloc.1 IL_0039: ret } "); } [Fact] public void FixedStatementOneDimensionalArray() { var text = @" using System; unsafe class C { int[] a = new int[1]; static void Main() { C c = new C(); Console.Write(c.a[0]); fixed (int* p = c.a) { *p = 1; } Console.Write(c.a[0]); } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: @"01", verify: Verification.Fails); compVerifier.VerifyIL("C.Main", @" { // Code size 65 (0x41) .maxstack 3 .locals init (int* V_0, //p pinned int[] V_1) IL_0000: newobj ""C..ctor()"" IL_0005: dup IL_0006: ldfld ""int[] C.a"" IL_000b: ldc.i4.0 IL_000c: ldelem.i4 IL_000d: call ""void System.Console.Write(int)"" IL_0012: dup IL_0013: ldfld ""int[] C.a"" IL_0018: dup IL_0019: stloc.1 IL_001a: brfalse.s IL_0021 IL_001c: ldloc.1 IL_001d: ldlen IL_001e: conv.i4 IL_001f: brtrue.s IL_0026 IL_0021: ldc.i4.0 IL_0022: conv.u IL_0023: stloc.0 IL_0024: br.s IL_002f IL_0026: ldloc.1 IL_0027: ldc.i4.0 IL_0028: ldelema ""int"" IL_002d: conv.u IL_002e: stloc.0 IL_002f: ldloc.0 IL_0030: ldc.i4.1 IL_0031: stind.i4 IL_0032: ldnull IL_0033: stloc.1 IL_0034: ldfld ""int[] C.a"" IL_0039: ldc.i4.0 IL_003a: ldelem.i4 IL_003b: call ""void System.Console.Write(int)"" IL_0040: ret } "); } [Fact] public void FixedStatementOneDimensionalArrayOptimized() { var text = @" using System; unsafe class C { int[] a = new int[1]; static void Main() { C c = new C(); Console.Write(c.a[0]); fixed (int* p = c.a) { *p = 1; } Console.Write(c.a[0]); } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: @"01", verify: Verification.Fails); compVerifier.VerifyIL("C.Main", @" { // Code size 65 (0x41) .maxstack 3 .locals init (int* V_0, //p pinned int[] V_1) IL_0000: newobj ""C..ctor()"" IL_0005: dup IL_0006: ldfld ""int[] C.a"" IL_000b: ldc.i4.0 IL_000c: ldelem.i4 IL_000d: call ""void System.Console.Write(int)"" IL_0012: dup IL_0013: ldfld ""int[] C.a"" IL_0018: dup IL_0019: stloc.1 IL_001a: brfalse.s IL_0021 IL_001c: ldloc.1 IL_001d: ldlen IL_001e: conv.i4 IL_001f: brtrue.s IL_0026 IL_0021: ldc.i4.0 IL_0022: conv.u IL_0023: stloc.0 IL_0024: br.s IL_002f IL_0026: ldloc.1 IL_0027: ldc.i4.0 IL_0028: ldelema ""int"" IL_002d: conv.u IL_002e: stloc.0 IL_002f: ldloc.0 IL_0030: ldc.i4.1 IL_0031: stind.i4 IL_0032: ldnull IL_0033: stloc.1 IL_0034: ldfld ""int[] C.a"" IL_0039: ldc.i4.0 IL_003a: ldelem.i4 IL_003b: call ""void System.Console.Write(int)"" IL_0040: ret } "); } [Fact] public void FixedStatementMultiDimensionalArrayOptimized() { var text = @" using System; unsafe class C { int[,] a = new int[1,1]; static void Main() { C c = new C(); Console.Write(c.a[0, 0]); fixed (int* p = c.a) { *p = 1; } Console.Write(c.a[0, 0]); } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: @"01", verify: Verification.Fails); compVerifier.VerifyIL("C.Main", @" { // Code size 79 (0x4f) .maxstack 4 .locals init (int* V_0, //p pinned int[,] V_1) IL_0000: newobj ""C..ctor()"" IL_0005: dup IL_0006: ldfld ""int[,] C.a"" IL_000b: ldc.i4.0 IL_000c: ldc.i4.0 IL_000d: call ""int[*,*].Get"" IL_0012: call ""void System.Console.Write(int)"" IL_0017: dup IL_0018: ldfld ""int[,] C.a"" IL_001d: dup IL_001e: stloc.1 IL_001f: brfalse.s IL_0029 IL_0021: ldloc.1 IL_0022: callvirt ""int System.Array.Length.get"" IL_0027: brtrue.s IL_002e IL_0029: ldc.i4.0 IL_002a: conv.u IL_002b: stloc.0 IL_002c: br.s IL_0038 IL_002e: ldloc.1 IL_002f: ldc.i4.0 IL_0030: ldc.i4.0 IL_0031: call ""int[*,*].Address"" IL_0036: conv.u IL_0037: stloc.0 IL_0038: ldloc.0 IL_0039: ldc.i4.1 IL_003a: stind.i4 IL_003b: ldnull IL_003c: stloc.1 IL_003d: ldfld ""int[,] C.a"" IL_0042: ldc.i4.0 IL_0043: ldc.i4.0 IL_0044: call ""int[*,*].Get"" IL_0049: call ""void System.Console.Write(int)"" IL_004e: ret } "); } [Fact] public void FixedStatementMixed() { var text = @" using System; unsafe class C { char c = 'a'; char[] a = new char[1]; static void Main() { C c = new C(); fixed (char* p = &c.c, q = c.a, r = ""hello"") { Console.Write((int)*p); Console.Write((int)*q); Console.Write((int)*r); } } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: @"970104", verify: Verification.Fails); compVerifier.VerifyIL("C.Main", @" { // Code size 99 (0x63) .maxstack 2 .locals init (char* V_0, //p char* V_1, //q char* V_2, //r pinned char& V_3, pinned char[] V_4, pinned string V_5) IL_0000: newobj ""C..ctor()"" IL_0005: dup IL_0006: ldflda ""char C.c"" IL_000b: stloc.3 IL_000c: ldloc.3 IL_000d: conv.u IL_000e: stloc.0 IL_000f: ldfld ""char[] C.a"" IL_0014: dup IL_0015: stloc.s V_4 IL_0017: brfalse.s IL_001f IL_0019: ldloc.s V_4 IL_001b: ldlen IL_001c: conv.i4 IL_001d: brtrue.s IL_0024 IL_001f: ldc.i4.0 IL_0020: conv.u IL_0021: stloc.1 IL_0022: br.s IL_002e IL_0024: ldloc.s V_4 IL_0026: ldc.i4.0 IL_0027: ldelema ""char"" IL_002c: conv.u IL_002d: stloc.1 IL_002e: ldstr ""hello"" IL_0033: stloc.s V_5 IL_0035: ldloc.s V_5 IL_0037: conv.u IL_0038: stloc.2 IL_0039: ldloc.2 IL_003a: brfalse.s IL_0044 IL_003c: ldloc.2 IL_003d: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_0042: add IL_0043: stloc.2 IL_0044: ldloc.0 IL_0045: ldind.u2 IL_0046: call ""void System.Console.Write(int)"" IL_004b: ldloc.1 IL_004c: ldind.u2 IL_004d: call ""void System.Console.Write(int)"" IL_0052: ldloc.2 IL_0053: ldind.u2 IL_0054: call ""void System.Console.Write(int)"" IL_0059: ldc.i4.0 IL_005a: conv.u IL_005b: stloc.3 IL_005c: ldnull IL_005d: stloc.s V_4 IL_005f: ldnull IL_0060: stloc.s V_5 IL_0062: ret } "); } [Fact] public void FixedStatementInTryOfTryFinally() { var text = @" unsafe class C { static void nop() { } void Test() { try { fixed (char* p = ""hello"") { } } finally { nop(); } } } "; // Cleanup in finally. CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails). VerifyIL("C.Test", @" { // Code size 32 (0x20) .maxstack 2 .locals init (char* V_0, //p pinned string V_1) .try { .try { IL_0000: ldstr ""hello"" IL_0005: stloc.1 IL_0006: ldloc.1 IL_0007: conv.u IL_0008: stloc.0 IL_0009: ldloc.0 IL_000a: brfalse.s IL_0014 IL_000c: ldloc.0 IL_000d: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_0012: add IL_0013: stloc.0 IL_0014: leave.s IL_001f } finally { IL_0016: ldnull IL_0017: stloc.1 IL_0018: endfinally } } finally { IL_0019: call ""void C.nop()"" IL_001e: endfinally } IL_001f: ret } "); } [Fact] public void FixedStatementInTryOfTryCatch() { var text = @" unsafe class C { void Test() { try { fixed (char* p = ""hello"") { } } catch { } } } "; // Cleanup in finally. CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails). VerifyIL("C.Test", @" { // Code size 31 (0x1f) .maxstack 2 .locals init (char* V_0, //p pinned string V_1) .try { .try { IL_0000: ldstr ""hello"" IL_0005: stloc.1 IL_0006: ldloc.1 IL_0007: conv.u IL_0008: stloc.0 IL_0009: ldloc.0 IL_000a: brfalse.s IL_0014 IL_000c: ldloc.0 IL_000d: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_0012: add IL_0013: stloc.0 IL_0014: leave.s IL_0019 } finally { IL_0016: ldnull IL_0017: stloc.1 IL_0018: endfinally } IL_0019: leave.s IL_001e } catch object { IL_001b: pop IL_001c: leave.s IL_001e } IL_001e: ret } "); } [Fact] public void FixedStatementInFinally() { var text = @" unsafe class C { void Test() { try { } finally { fixed (char* p = ""hello"") { } } } } "; // Cleanup not in finally. CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails). VerifyIL("C.Test", @" { // Code size 26 (0x1a) .maxstack 2 .locals init (char* V_0, //p pinned string V_1) .try { IL_0000: leave.s IL_0019 } finally { IL_0002: ldstr ""hello"" IL_0007: stloc.1 IL_0008: ldloc.1 IL_0009: conv.u IL_000a: stloc.0 IL_000b: ldloc.0 IL_000c: brfalse.s IL_0016 IL_000e: ldloc.0 IL_000f: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_0014: add IL_0015: stloc.0 IL_0016: ldnull IL_0017: stloc.1 IL_0018: endfinally } IL_0019: ret } "); } [Fact] public void FixedStatementInCatchOfTryCatch() { var text = @" unsafe class C { void nop() { } void Test() { try { nop(); } catch { fixed (char* p = ""hello"") { } } } } "; // Cleanup not in finally. CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails). VerifyIL("C.Test", @"{ // Code size 34 (0x22) .maxstack 2 .locals init (char* V_0, //p pinned string V_1) .try { IL_0000: ldarg.0 IL_0001: call ""void C.nop()"" IL_0006: leave.s IL_0021 } catch object { IL_0008: pop IL_0009: ldstr ""hello"" IL_000e: stloc.1 IL_000f: ldloc.1 IL_0010: conv.u IL_0011: stloc.0 IL_0012: ldloc.0 IL_0013: brfalse.s IL_001d IL_0015: ldloc.0 IL_0016: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_001b: add IL_001c: stloc.0 IL_001d: ldnull IL_001e: stloc.1 IL_001f: leave.s IL_0021 } IL_0021: ret }"); } [Fact] public void FixedStatementInCatchOfTryCatchFinally() { var text = @" unsafe class C { static void nop() { } void Test() { try { nop(); } catch { fixed (char* p = ""hello"") { } } finally { } } } "; // Cleanup in finally. CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails). VerifyIL("C.Test", @" { // Code size 36 (0x24) .maxstack 2 .locals init (char* V_0, //p pinned string V_1) .try { IL_0000: call ""void C.nop()"" IL_0005: leave.s IL_0023 } catch object { IL_0007: pop .try { IL_0008: ldstr ""hello"" IL_000d: stloc.1 IL_000e: ldloc.1 IL_000f: conv.u IL_0010: stloc.0 IL_0011: ldloc.0 IL_0012: brfalse.s IL_001c IL_0014: ldloc.0 IL_0015: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_001a: add IL_001b: stloc.0 IL_001c: leave.s IL_0021 } finally { IL_001e: ldnull IL_001f: stloc.1 IL_0020: endfinally } IL_0021: leave.s IL_0023 } IL_0023: ret } "); } [Fact] public void FixedStatementInFixed_NoBranch() { var text = @" unsafe class C { void Test() { fixed (char* q = ""goodbye"") { fixed (char* p = ""hello"") { } } } } "; // Neither inner nor outer has finally. CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails). VerifyIL("C.Test", @" { // Code size 45 (0x2d) .maxstack 2 .locals init (char* V_0, //q pinned string V_1, char* V_2, //p pinned string V_3) IL_0000: ldstr ""goodbye"" IL_0005: stloc.1 IL_0006: ldloc.1 IL_0007: conv.u IL_0008: stloc.0 IL_0009: ldloc.0 IL_000a: brfalse.s IL_0014 IL_000c: ldloc.0 IL_000d: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_0012: add IL_0013: stloc.0 IL_0014: ldstr ""hello"" IL_0019: stloc.3 IL_001a: ldloc.3 IL_001b: conv.u IL_001c: stloc.2 IL_001d: ldloc.2 IL_001e: brfalse.s IL_0028 IL_0020: ldloc.2 IL_0021: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_0026: add IL_0027: stloc.2 IL_0028: ldnull IL_0029: stloc.3 IL_002a: ldnull IL_002b: stloc.1 IL_002c: ret } "); } [Fact] public void FixedStatementInFixed_InnerBranch() { var text = @" unsafe class C { void Test() { fixed (char* q = ""goodbye"") { fixed (char* p = ""hello"") { goto label; } } label: ; } } "; // Inner and outer both have finally blocks. CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails). VerifyIL("C.Test", @" { // Code size 50 (0x32) .maxstack 2 .locals init (char* V_0, //q pinned string V_1, char* V_2, //p pinned string V_3) .try { IL_0000: ldstr ""goodbye"" IL_0005: stloc.1 IL_0006: ldloc.1 IL_0007: conv.u IL_0008: stloc.0 IL_0009: ldloc.0 IL_000a: brfalse.s IL_0014 IL_000c: ldloc.0 IL_000d: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_0012: add IL_0013: stloc.0 IL_0014: nop .try { IL_0015: ldstr ""hello"" IL_001a: stloc.3 IL_001b: ldloc.3 IL_001c: conv.u IL_001d: stloc.2 IL_001e: ldloc.2 IL_001f: brfalse.s IL_0029 IL_0021: ldloc.2 IL_0022: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_0027: add IL_0028: stloc.2 IL_0029: leave.s IL_0031 } finally { IL_002b: ldnull IL_002c: stloc.3 IL_002d: endfinally } } finally { IL_002e: ldnull IL_002f: stloc.1 IL_0030: endfinally } IL_0031: ret } "); } [Fact] public void FixedStatementInFixed_OuterBranch() { var text = @" unsafe class C { void Test() { fixed (char* q = ""goodbye"") { fixed (char* p = ""hello"") { } goto label; } label: ; } } "; // Outer has finally, inner does not. CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails). VerifyIL("C.Test", @" { // Code size 48 (0x30) .maxstack 2 .locals init (char* V_0, //q pinned string V_1, char* V_2, //p pinned string V_3) .try { IL_0000: ldstr ""goodbye"" IL_0005: stloc.1 IL_0006: ldloc.1 IL_0007: conv.u IL_0008: stloc.0 IL_0009: ldloc.0 IL_000a: brfalse.s IL_0014 IL_000c: ldloc.0 IL_000d: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_0012: add IL_0013: stloc.0 IL_0014: ldstr ""hello"" IL_0019: stloc.3 IL_001a: ldloc.3 IL_001b: conv.u IL_001c: stloc.2 IL_001d: ldloc.2 IL_001e: brfalse.s IL_0028 IL_0020: ldloc.2 IL_0021: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_0026: add IL_0027: stloc.2 IL_0028: ldnull IL_0029: stloc.3 IL_002a: leave.s IL_002f } finally { IL_002c: ldnull IL_002d: stloc.1 IL_002e: endfinally } IL_002f: ret } "); } [Fact] public void FixedStatementInFixed_Nesting() { var text = @" unsafe class C { void Test() { fixed (char* p1 = ""A"") { fixed (char* p2 = ""B"") { fixed (char* p3 = ""C"") { } fixed (char* p4 = ""D"") { } } fixed (char* p5 = ""E"") { fixed (char* p6 = ""F"") { } fixed (char* p7 = ""G"") { } } } } } "; // This test checks two things: // 1) nothing blows up with triple-nesting, and // 2) none of the fixed statements has a try-finally. // CONSIDER: Shorter test that performs the same checks. CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails). VerifyIL("C.Test", @" { // Code size 187 (0xbb) .maxstack 2 .locals init (char* V_0, //p1 pinned string V_1, char* V_2, //p2 pinned string V_3, char* V_4, //p3 pinned string V_5, char* V_6, //p4 char* V_7, //p5 char* V_8, //p6 char* V_9) //p7 IL_0000: ldstr ""A"" IL_0005: stloc.1 IL_0006: ldloc.1 IL_0007: conv.u IL_0008: stloc.0 IL_0009: ldloc.0 IL_000a: brfalse.s IL_0014 IL_000c: ldloc.0 IL_000d: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_0012: add IL_0013: stloc.0 IL_0014: ldstr ""B"" IL_0019: stloc.3 IL_001a: ldloc.3 IL_001b: conv.u IL_001c: stloc.2 IL_001d: ldloc.2 IL_001e: brfalse.s IL_0028 IL_0020: ldloc.2 IL_0021: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_0026: add IL_0027: stloc.2 IL_0028: ldstr ""C"" IL_002d: stloc.s V_5 IL_002f: ldloc.s V_5 IL_0031: conv.u IL_0032: stloc.s V_4 IL_0034: ldloc.s V_4 IL_0036: brfalse.s IL_0042 IL_0038: ldloc.s V_4 IL_003a: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_003f: add IL_0040: stloc.s V_4 IL_0042: ldnull IL_0043: stloc.s V_5 IL_0045: ldstr ""D"" IL_004a: stloc.s V_5 IL_004c: ldloc.s V_5 IL_004e: conv.u IL_004f: stloc.s V_6 IL_0051: ldloc.s V_6 IL_0053: brfalse.s IL_005f IL_0055: ldloc.s V_6 IL_0057: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_005c: add IL_005d: stloc.s V_6 IL_005f: ldnull IL_0060: stloc.s V_5 IL_0062: ldnull IL_0063: stloc.3 IL_0064: ldstr ""E"" IL_0069: stloc.3 IL_006a: ldloc.3 IL_006b: conv.u IL_006c: stloc.s V_7 IL_006e: ldloc.s V_7 IL_0070: brfalse.s IL_007c IL_0072: ldloc.s V_7 IL_0074: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_0079: add IL_007a: stloc.s V_7 IL_007c: ldstr ""F"" IL_0081: stloc.s V_5 IL_0083: ldloc.s V_5 IL_0085: conv.u IL_0086: stloc.s V_8 IL_0088: ldloc.s V_8 IL_008a: brfalse.s IL_0096 IL_008c: ldloc.s V_8 IL_008e: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_0093: add IL_0094: stloc.s V_8 IL_0096: ldnull IL_0097: stloc.s V_5 IL_0099: ldstr ""G"" IL_009e: stloc.s V_5 IL_00a0: ldloc.s V_5 IL_00a2: conv.u IL_00a3: stloc.s V_9 IL_00a5: ldloc.s V_9 IL_00a7: brfalse.s IL_00b3 IL_00a9: ldloc.s V_9 IL_00ab: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_00b0: add IL_00b1: stloc.s V_9 IL_00b3: ldnull IL_00b4: stloc.s V_5 IL_00b6: ldnull IL_00b7: stloc.3 IL_00b8: ldnull IL_00b9: stloc.1 IL_00ba: ret } "); } [Fact] public void FixedStatementInUsing() { var text = @" unsafe class C { void Test() { using (System.IDisposable d = null) { fixed (char* p = ""hello"") { } } } } "; // CONSIDER: This is sort of silly since the using is optimized away. // Cleanup in finally. CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails). VerifyIL("C.Test", @" { // Code size 26 (0x1a) .maxstack 2 .locals init (char* V_0, //p pinned string V_1) .try { IL_0000: ldstr ""hello"" IL_0005: stloc.1 IL_0006: ldloc.1 IL_0007: conv.u IL_0008: stloc.0 IL_0009: ldloc.0 IL_000a: brfalse.s IL_0014 IL_000c: ldloc.0 IL_000d: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_0012: add IL_0013: stloc.0 IL_0014: leave.s IL_0019 } finally { IL_0016: ldnull IL_0017: stloc.1 IL_0018: endfinally } IL_0019: ret } "); } [Fact] public void FixedStatementInLock() { var text = @" unsafe class C { void Test() { lock (this) { fixed (char* p = ""hello"") { } } } } "; // Cleanup not in finally (matches dev11, but not clear why). CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails). VerifyIL("C.Test", @" { // Code size 47 (0x2f) .maxstack 2 .locals init (C V_0, bool V_1, char* V_2, //p pinned string V_3) IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldc.i4.0 IL_0003: stloc.1 .try { IL_0004: ldloc.0 IL_0005: ldloca.s V_1 IL_0007: call ""void System.Threading.Monitor.Enter(object, ref bool)"" IL_000c: ldstr ""hello"" IL_0011: stloc.3 IL_0012: ldloc.3 IL_0013: conv.u IL_0014: stloc.2 IL_0015: ldloc.2 IL_0016: brfalse.s IL_0020 IL_0018: ldloc.2 IL_0019: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_001e: add IL_001f: stloc.2 IL_0020: ldnull IL_0021: stloc.3 IL_0022: leave.s IL_002e } finally { IL_0024: ldloc.1 IL_0025: brfalse.s IL_002d IL_0027: ldloc.0 IL_0028: call ""void System.Threading.Monitor.Exit(object)"" IL_002d: endfinally } IL_002e: ret } "); } [Fact] public void FixedStatementInForEach_NoDispose() { var text = @" unsafe class C { void Test(int[] array) { foreach (int i in array) { fixed (char* p = ""hello"") { } } } } "; // Cleanup in finally. // CONSIDER: dev11 is smarter and skips the try-finally. CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails). VerifyIL("C.Test", @" { // Code size 46 (0x2e) .maxstack 2 .locals init (int[] V_0, int V_1, char* V_2, //p pinned string V_3) IL_0000: ldarg.1 IL_0001: stloc.0 IL_0002: ldc.i4.0 IL_0003: stloc.1 IL_0004: br.s IL_0027 IL_0006: ldloc.0 IL_0007: ldloc.1 IL_0008: ldelem.i4 IL_0009: pop .try { IL_000a: ldstr ""hello"" IL_000f: stloc.3 IL_0010: ldloc.3 IL_0011: conv.u IL_0012: stloc.2 IL_0013: ldloc.2 IL_0014: brfalse.s IL_001e IL_0016: ldloc.2 IL_0017: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_001c: add IL_001d: stloc.2 IL_001e: leave.s IL_0023 } finally { IL_0020: ldnull IL_0021: stloc.3 IL_0022: endfinally } IL_0023: ldloc.1 IL_0024: ldc.i4.1 IL_0025: add IL_0026: stloc.1 IL_0027: ldloc.1 IL_0028: ldloc.0 IL_0029: ldlen IL_002a: conv.i4 IL_002b: blt.s IL_0006 IL_002d: ret } "); } [Fact] public void FixedStatementInForEach_Dispose() { var text = @" unsafe class C { void Test(Enumerable e) { foreach (var x in e) { fixed (char* p = ""hello"") { } } } } class Enumerable { public Enumerator GetEnumerator() { return new Enumerator(); } } class Enumerator : System.IDisposable { int x; public int Current { get { return x; } } public bool MoveNext() { return ++x < 4; } void System.IDisposable.Dispose() { } } "; // Cleanup in finally. CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails). VerifyIL("C.Test", @" { // Code size 62 (0x3e) .maxstack 2 .locals init (Enumerator V_0, char* V_1, //p pinned string V_2) IL_0000: ldarg.1 IL_0001: callvirt ""Enumerator Enumerable.GetEnumerator()"" IL_0006: stloc.0 .try { IL_0007: br.s IL_0029 IL_0009: ldloc.0 IL_000a: callvirt ""int Enumerator.Current.get"" IL_000f: pop .try { IL_0010: ldstr ""hello"" IL_0015: stloc.2 IL_0016: ldloc.2 IL_0017: conv.u IL_0018: stloc.1 IL_0019: ldloc.1 IL_001a: brfalse.s IL_0024 IL_001c: ldloc.1 IL_001d: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_0022: add IL_0023: stloc.1 IL_0024: leave.s IL_0029 } finally { IL_0026: ldnull IL_0027: stloc.2 IL_0028: endfinally } IL_0029: ldloc.0 IL_002a: callvirt ""bool Enumerator.MoveNext()"" IL_002f: brtrue.s IL_0009 IL_0031: leave.s IL_003d } finally { IL_0033: ldloc.0 IL_0034: brfalse.s IL_003c IL_0036: ldloc.0 IL_0037: callvirt ""void System.IDisposable.Dispose()"" IL_003c: endfinally } IL_003d: ret } "); } [Fact] public void FixedStatementInLambda1() { var text = @" unsafe class C { void Test() { System.Action a = () => { try { fixed (char* p = ""hello"") { } } finally { } }; } } "; // Cleanup in finally. CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails). VerifyIL("C.<>c.<Test>b__0_0()", @" { // Code size 26 (0x1a) .maxstack 2 .locals init (char* V_0, //p pinned string V_1) .try { IL_0000: ldstr ""hello"" IL_0005: stloc.1 IL_0006: ldloc.1 IL_0007: conv.u IL_0008: stloc.0 IL_0009: ldloc.0 IL_000a: brfalse.s IL_0014 IL_000c: ldloc.0 IL_000d: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_0012: add IL_0013: stloc.0 IL_0014: leave.s IL_0019 } finally { IL_0016: ldnull IL_0017: stloc.1 IL_0018: endfinally } IL_0019: ret }"); } [Fact] public void FixedStatementInLambda2() { var text = @" unsafe class C { void Test() { try { System.Action a = () => { fixed (char* p = ""hello"") { } }; } finally { } } } "; // Cleanup not in finally. CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails). VerifyIL("C.<>c.<Test>b__0_0()", @" { // Code size 23 (0x17) .maxstack 2 .locals init (char* V_0, //p pinned string V_1) IL_0000: ldstr ""hello"" IL_0005: stloc.1 IL_0006: ldloc.1 IL_0007: conv.u IL_0008: stloc.0 IL_0009: ldloc.0 IL_000a: brfalse.s IL_0014 IL_000c: ldloc.0 IL_000d: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_0012: add IL_0013: stloc.0 IL_0014: ldnull IL_0015: stloc.1 IL_0016: ret } "); } [Fact] public void FixedStatementInLambda3() { var text = @" unsafe class C { void Test() { System.Action a = () => { fixed (char* p = ""hello"") { goto label; } label: ; }; } } "; // Cleanup in finally. CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails). VerifyIL("C.<>c.<Test>b__0_0()", @" { // Code size 26 (0x1a) .maxstack 2 .locals init (char* V_0, //p pinned string V_1) .try { IL_0000: ldstr ""hello"" IL_0005: stloc.1 IL_0006: ldloc.1 IL_0007: conv.u IL_0008: stloc.0 IL_0009: ldloc.0 IL_000a: brfalse.s IL_0014 IL_000c: ldloc.0 IL_000d: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_0012: add IL_0013: stloc.0 IL_0014: leave.s IL_0019 } finally { IL_0016: ldnull IL_0017: stloc.1 IL_0018: endfinally } IL_0019: ret } "); } [Fact] public void FixedStatementInFieldInitializer1() { var text = @" unsafe class C { System.Action a = () => { try { fixed (char* p = ""hello"") { } } finally { } }; } "; // Cleanup in finally. CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails). VerifyIL("C.<>c.<.ctor>b__1_0()", @" { // Code size 26 (0x1a) .maxstack 2 .locals init (char* V_0, //p pinned string V_1) .try { IL_0000: ldstr ""hello"" IL_0005: stloc.1 IL_0006: ldloc.1 IL_0007: conv.u IL_0008: stloc.0 IL_0009: ldloc.0 IL_000a: brfalse.s IL_0014 IL_000c: ldloc.0 IL_000d: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_0012: add IL_0013: stloc.0 IL_0014: leave.s IL_0019 } finally { IL_0016: ldnull IL_0017: stloc.1 IL_0018: endfinally } IL_0019: ret } "); } [Fact] public void FixedStatementInFieldInitializer2() { var text = @" unsafe class C { System.Action a = () => { fixed (char* p = ""hello"") { goto label; } label: ; }; } "; // Cleanup in finally. CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails). VerifyIL("C.<>c.<.ctor>b__1_0()", @" { // Code size 26 (0x1a) .maxstack 2 .locals init (char* V_0, //p pinned string V_1) .try { IL_0000: ldstr ""hello"" IL_0005: stloc.1 IL_0006: ldloc.1 IL_0007: conv.u IL_0008: stloc.0 IL_0009: ldloc.0 IL_000a: brfalse.s IL_0014 IL_000c: ldloc.0 IL_000d: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_0012: add IL_0013: stloc.0 IL_0014: leave.s IL_0019 } finally { IL_0016: ldnull IL_0017: stloc.1 IL_0018: endfinally } IL_0019: ret } "); } [Fact] public void FixedStatementWithBranchOut_LoopBreak() { var text = @" unsafe class C { void Test() { while(true) { fixed (char* p = ""hello"") { break; } } } } "; // Cleanup in finally. CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails). VerifyIL("C.Test", @" { // Code size 27 (0x1b) .maxstack 2 .locals init (char* V_0, //p pinned string V_1) IL_0000: nop .try { IL_0001: ldstr ""hello"" IL_0006: stloc.1 IL_0007: ldloc.1 IL_0008: conv.u IL_0009: stloc.0 IL_000a: ldloc.0 IL_000b: brfalse.s IL_0015 IL_000d: ldloc.0 IL_000e: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_0013: add IL_0014: stloc.0 IL_0015: leave.s IL_001a } finally { IL_0017: ldnull IL_0018: stloc.1 IL_0019: endfinally } IL_001a: ret } "); } [Fact] public void FixedStatementWithBranchOut_LoopContinue() { var text = @" unsafe class C { void Test() { while(true) { fixed (char* p = ""hello"") { continue; } } } } "; // Cleanup in finally. // CONSIDER: dev11 doesn't have a finally here, but that seems incorrect. CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails). VerifyIL("C.Test", @" { // Code size 26 (0x1a) .maxstack 2 .locals init (char* V_0, //p pinned string V_1) IL_0000: nop .try { IL_0001: ldstr ""hello"" IL_0006: stloc.1 IL_0007: ldloc.1 IL_0008: conv.u IL_0009: stloc.0 IL_000a: ldloc.0 IL_000b: brfalse.s IL_0015 IL_000d: ldloc.0 IL_000e: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_0013: add IL_0014: stloc.0 IL_0015: leave.s IL_0000 } finally { IL_0017: ldnull IL_0018: stloc.1 IL_0019: endfinally } } "); } [Fact] public void FixedStatementWithBranchOut_SwitchBreak() { var text = @" unsafe class C { void Test() { switch (1) { case 1: fixed (char* p = ""hello"") { break; } } } } "; // Cleanup in finally. CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails). VerifyIL("C.Test", @" { // Code size 27 (0x1b) .maxstack 2 .locals init (char* V_0, //p pinned string V_1) IL_0000: nop .try { IL_0001: ldstr ""hello"" IL_0006: stloc.1 IL_0007: ldloc.1 IL_0008: conv.u IL_0009: stloc.0 IL_000a: ldloc.0 IL_000b: brfalse.s IL_0015 IL_000d: ldloc.0 IL_000e: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_0013: add IL_0014: stloc.0 IL_0015: leave.s IL_001a } finally { IL_0017: ldnull IL_0018: stloc.1 IL_0019: endfinally } IL_001a: ret } "); } [Fact] public void FixedStatementWithBranchOut_SwitchGoto() { var text = @" unsafe class C { void Test() { switch (1) { case 1: fixed (char* p = ""hello"") { goto case 1; } } } } "; // Cleanup in finally. CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails). VerifyIL("C.Test", @" { // Code size 26 (0x1a) .maxstack 2 .locals init (char* V_0, //p pinned string V_1) IL_0000: nop .try { IL_0001: ldstr ""hello"" IL_0006: stloc.1 IL_0007: ldloc.1 IL_0008: conv.u IL_0009: stloc.0 IL_000a: ldloc.0 IL_000b: brfalse.s IL_0015 IL_000d: ldloc.0 IL_000e: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_0013: add IL_0014: stloc.0 IL_0015: leave.s IL_0000 } finally { IL_0017: ldnull IL_0018: stloc.1 IL_0019: endfinally } } "); } [Fact] public void FixedStatementWithBranchOut_BackwardGoto() { var text = @" unsafe class C { void Test() { label: fixed (char* p = ""hello"") { goto label; } } } "; // Cleanup in finally. CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails). VerifyIL("C.Test", @" { // Code size 26 (0x1a) .maxstack 2 .locals init (char* V_0, //p pinned string V_1) IL_0000: nop .try { IL_0001: ldstr ""hello"" IL_0006: stloc.1 IL_0007: ldloc.1 IL_0008: conv.u IL_0009: stloc.0 IL_000a: ldloc.0 IL_000b: brfalse.s IL_0015 IL_000d: ldloc.0 IL_000e: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_0013: add IL_0014: stloc.0 IL_0015: leave.s IL_0000 } finally { IL_0017: ldnull IL_0018: stloc.1 IL_0019: endfinally } } "); } [Fact] public void FixedStatementWithBranchOut_ForwardGoto() { var text = @" unsafe class C { void Test() { fixed (char* p = ""hello"") { goto label; } label: ; } } "; // Cleanup in finally. CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails). VerifyIL("C.Test", @" { // Code size 26 (0x1a) .maxstack 2 .locals init (char* V_0, //p pinned string V_1) .try { IL_0000: ldstr ""hello"" IL_0005: stloc.1 IL_0006: ldloc.1 IL_0007: conv.u IL_0008: stloc.0 IL_0009: ldloc.0 IL_000a: brfalse.s IL_0014 IL_000c: ldloc.0 IL_000d: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_0012: add IL_0013: stloc.0 IL_0014: leave.s IL_0019 } finally { IL_0016: ldnull IL_0017: stloc.1 IL_0018: endfinally } IL_0019: ret } "); } [Fact] public void FixedStatementWithBranchOut_Throw() { var text = @" unsafe class C { void Test() { fixed (char* p = ""hello"") { throw null; } } } "; // Cleanup not in finally. CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails). VerifyIL("C.Test", @" { // Code size 22 (0x16) .maxstack 2 .locals init (char* V_0, //p pinned string V_1) IL_0000: ldstr ""hello"" IL_0005: stloc.1 IL_0006: ldloc.1 IL_0007: conv.u IL_0008: stloc.0 IL_0009: ldloc.0 IL_000a: brfalse.s IL_0014 IL_000c: ldloc.0 IL_000d: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_0012: add IL_0013: stloc.0 IL_0014: ldnull IL_0015: throw } "); } [Fact] public void FixedStatementWithBranchOut_Return() { var text = @" unsafe class C { void Test() { fixed (char* p = ""hello"") { return; } } } "; // Cleanup not in finally. CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails). VerifyIL("C.Test", @" { // Code size 21 (0x15) .maxstack 2 .locals init (char* V_0, //p pinned string V_1) IL_0000: ldstr ""hello"" IL_0005: stloc.1 IL_0006: ldloc.1 IL_0007: conv.u IL_0008: stloc.0 IL_0009: ldloc.0 IL_000a: brfalse.s IL_0014 IL_000c: ldloc.0 IL_000d: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_0012: add IL_0013: stloc.0 IL_0014: ret } "); } [Fact] public void FixedStatementWithNoBranchOut_Loop() { var text = @" unsafe class C { void Test() { fixed (char* p = ""hello"") { for (int i = 0; i < 10; i++) { } } } } "; // Cleanup not in finally. CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails). VerifyIL("C.Test", @" { // Code size 36 (0x24) .maxstack 2 .locals init (char* V_0, //p pinned string V_1, int V_2) //i IL_0000: ldstr ""hello"" IL_0005: stloc.1 IL_0006: ldloc.1 IL_0007: conv.u IL_0008: stloc.0 IL_0009: ldloc.0 IL_000a: brfalse.s IL_0014 IL_000c: ldloc.0 IL_000d: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_0012: add IL_0013: stloc.0 IL_0014: ldc.i4.0 IL_0015: stloc.2 IL_0016: br.s IL_001c IL_0018: ldloc.2 IL_0019: ldc.i4.1 IL_001a: add IL_001b: stloc.2 IL_001c: ldloc.2 IL_001d: ldc.i4.s 10 IL_001f: blt.s IL_0018 IL_0021: ldnull IL_0022: stloc.1 IL_0023: ret } "); } [Fact] public void FixedStatementWithNoBranchOut_InternalGoto() { var text = @" unsafe class C { void Test() { fixed (char* p = ""hello"") { goto label; label: ; } } } "; // NOTE: Dev11 uses a finally here, but it's unnecessary. // From GotoChecker::VisitGOTO: // We have an unrealized goto, so we do not know whether it // branches out or not. We should be conservative and assume that // it does. // Cleanup not in finally. CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails). VerifyIL("C.Test", @" { // Code size 23 (0x17) .maxstack 2 .locals init (char* V_0, //p pinned string V_1) IL_0000: ldstr ""hello"" IL_0005: stloc.1 IL_0006: ldloc.1 IL_0007: conv.u IL_0008: stloc.0 IL_0009: ldloc.0 IL_000a: brfalse.s IL_0014 IL_000c: ldloc.0 IL_000d: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_0012: add IL_0013: stloc.0 IL_0014: ldnull IL_0015: stloc.1 IL_0016: ret } "); } [Fact] public void FixedStatementWithNoBranchOut_Switch() { var text = @" unsafe class C { void Test() { fixed (char* p = ""hello"") { switch(*p) { case 'a': Test(); goto case 'b'; case 'b': Test(); goto case 'c'; case 'c': Test(); goto case 'd'; case 'd': Test(); goto case 'e'; case 'e': Test(); goto case 'f'; case 'f': Test(); goto default; default: Test(); break; } } } } "; // Cleanup not in finally. CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails). VerifyIL("C.Test", @" { // Code size 103 (0x67) .maxstack 2 .locals init (char* V_0, //p pinned string V_1, char V_2) IL_0000: ldstr ""hello"" IL_0005: stloc.1 IL_0006: ldloc.1 IL_0007: conv.u IL_0008: stloc.0 IL_0009: ldloc.0 IL_000a: brfalse.s IL_0014 IL_000c: ldloc.0 IL_000d: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_0012: add IL_0013: stloc.0 IL_0014: ldloc.0 IL_0015: ldind.u2 IL_0016: stloc.2 IL_0017: ldloc.2 IL_0018: ldc.i4.s 97 IL_001a: sub IL_001b: switch ( IL_003a, IL_0040, IL_0046, IL_004c, IL_0052, IL_0058) IL_0038: br.s IL_005e IL_003a: ldarg.0 IL_003b: call ""void C.Test()"" IL_0040: ldarg.0 IL_0041: call ""void C.Test()"" IL_0046: ldarg.0 IL_0047: call ""void C.Test()"" IL_004c: ldarg.0 IL_004d: call ""void C.Test()"" IL_0052: ldarg.0 IL_0053: call ""void C.Test()"" IL_0058: ldarg.0 IL_0059: call ""void C.Test()"" IL_005e: ldarg.0 IL_005f: call ""void C.Test()"" IL_0064: ldnull IL_0065: stloc.1 IL_0066: ret } "); } [Fact] public void FixedStatementWithParenthesizedStringExpression() { var text = @" unsafe class C { void Test() { fixed (char* p = ((""hello""))) { } } }"; CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails). VerifyIL("C.Test", @" { // Code size 23 (0x17) .maxstack 2 .locals init (char* V_0, //p pinned string V_1) IL_0000: ldstr ""hello"" IL_0005: stloc.1 IL_0006: ldloc.1 IL_0007: conv.u IL_0008: stloc.0 IL_0009: ldloc.0 IL_000a: brfalse.s IL_0014 IL_000c: ldloc.0 IL_000d: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_0012: add IL_0013: stloc.0 IL_0014: ldnull IL_0015: stloc.1 IL_0016: ret } "); } #endregion Fixed statement tests #region Custom fixed statement tests [Fact] public void SimpleCaseOfCustomFixed() { var text = @" unsafe class C { public static void Main() { fixed (int* p = new Fixable()) { System.Console.WriteLine(p[1]); } } } class Fixable { public ref int GetPinnableReference() { return ref (new int[]{1,2,3})[0]; } } static class FixableExt { public static ref int GetPinnableReference(this Fixable self) { return ref (new int[]{1,2,3})[0]; } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: @"2", verify: Verification.Fails); compVerifier.VerifyIL("C.Main", @" { // Code size 33 (0x21) .maxstack 2 .locals init (pinned int& V_0) IL_0000: newobj ""Fixable..ctor()"" IL_0005: dup IL_0006: brtrue.s IL_000d IL_0008: pop IL_0009: ldc.i4.0 IL_000a: conv.u IL_000b: br.s IL_0015 IL_000d: call ""ref int Fixable.GetPinnableReference()"" IL_0012: stloc.0 IL_0013: ldloc.0 IL_0014: conv.u IL_0015: ldc.i4.4 IL_0016: add IL_0017: ldind.i4 IL_0018: call ""void System.Console.WriteLine(int)"" IL_001d: ldc.i4.0 IL_001e: conv.u IL_001f: stloc.0 IL_0020: ret } "); } [Fact] public void SimpleCaseOfCustomFixedExt() { var text = @" unsafe class C { public static void Main() { fixed (int* p = new Fixable()) { System.Console.WriteLine(p[1]); } } } class Fixable { public ref int GetPinnableReference<T>() => throw null; } static class FixableExt { public static ref int GetPinnableReference<T>(this T self) { return ref (new int[]{1,2,3})[0]; } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: @"2", verify: Verification.Fails); compVerifier.VerifyIL("C.Main", @" { // Code size 33 (0x21) .maxstack 2 .locals init (pinned int& V_0) IL_0000: newobj ""Fixable..ctor()"" IL_0005: dup IL_0006: brtrue.s IL_000d IL_0008: pop IL_0009: ldc.i4.0 IL_000a: conv.u IL_000b: br.s IL_0015 IL_000d: call ""ref int FixableExt.GetPinnableReference<Fixable>(Fixable)"" IL_0012: stloc.0 IL_0013: ldloc.0 IL_0014: conv.u IL_0015: ldc.i4.4 IL_0016: add IL_0017: ldind.i4 IL_0018: call ""void System.Console.WriteLine(int)"" IL_001d: ldc.i4.0 IL_001e: conv.u IL_001f: stloc.0 IL_0020: ret } "); } [Fact] public void SimpleCaseOfCustomFixed_oldVersion() { var text = @" unsafe class C { public static void Main() { fixed (int* p = new Fixable()) { System.Console.WriteLine(p[1]); } } class Fixable { public ref int GetPinnableReference() { return ref (new int[]{1,2,3})[0]; } } } "; var compVerifier = CreateCompilationWithMscorlib46(text, options: TestOptions.UnsafeReleaseExe, parseOptions: TestOptions.Regular7_2); compVerifier.VerifyDiagnostics( // (6,25): error CS8320: Feature 'extensible fixed statement' is not available in C# 7.2. Please use language version 7.3 or greater. // fixed (int* p = new Fixable()) Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_2, "new Fixable()").WithArguments("extensible fixed statement", "7.3").WithLocation(6, 25) ); } [Fact] public void SimpleCaseOfCustomFixedNull() { var text = @" unsafe class C { public static void Main() { fixed (int* p = (Fixable)null) { System.Console.WriteLine((int)p); } } class Fixable { public ref int GetPinnableReference() { return ref (new int[]{1,2,3})[0]; } } }"; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: @"0", verify: Verification.Fails); compVerifier.VerifyIL("C.Main", @" { // Code size 26 (0x1a) .maxstack 1 .locals init (pinned int& V_0) IL_0000: ldnull IL_0001: brtrue.s IL_0007 IL_0003: ldc.i4.0 IL_0004: conv.u IL_0005: br.s IL_0010 IL_0007: ldnull IL_0008: call ""ref int C.Fixable.GetPinnableReference()"" IL_000d: stloc.0 IL_000e: ldloc.0 IL_000f: conv.u IL_0010: conv.i4 IL_0011: call ""void System.Console.WriteLine(int)"" IL_0016: ldc.i4.0 IL_0017: conv.u IL_0018: stloc.0 IL_0019: ret } "); } [Fact] public void SimpleCaseOfCustomFixedStruct() { var text = @" unsafe class C { public static void Main() { fixed (int* p = new Fixable()) { System.Console.WriteLine(p[1]); } } struct Fixable { public ref int GetPinnableReference() { return ref (new int[]{1,2,3})[0]; } } }"; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: @"2", verify: Verification.Fails); compVerifier.VerifyIL("C.Main", @" { // Code size 29 (0x1d) .maxstack 2 .locals init (pinned int& V_0, C.Fixable V_1) IL_0000: ldloca.s V_1 IL_0002: dup IL_0003: initobj ""C.Fixable"" IL_0009: call ""ref int C.Fixable.GetPinnableReference()"" IL_000e: stloc.0 IL_000f: ldloc.0 IL_0010: conv.u IL_0011: ldc.i4.4 IL_0012: add IL_0013: ldind.i4 IL_0014: call ""void System.Console.WriteLine(int)"" IL_0019: ldc.i4.0 IL_001a: conv.u IL_001b: stloc.0 IL_001c: ret } "); } [Fact] public void CustomFixedStructNullable() { var text = @" unsafe class C { public static void Main() { Fixable? f = new Fixable(); fixed (int* p = f) { System.Console.WriteLine(p[1]); } } } public struct Fixable { public ref int GetPinnableReference() { return ref (new int[]{1,2,3})[0]; } } public static class FixableExt { public static ref int GetPinnableReference(this Fixable? f) { return ref f.Value.GetPinnableReference(); } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: @"2", verify: Verification.Fails); compVerifier.VerifyIL("C.Main", @" { // Code size 49 (0x31) .maxstack 2 .locals init (Fixable V_0, pinned int& V_1, Fixable? V_2) IL_0000: ldloca.s V_0 IL_0002: initobj ""Fixable"" IL_0008: ldloc.0 IL_0009: newobj ""Fixable?..ctor(Fixable)"" IL_000e: stloc.2 IL_000f: ldloca.s V_2 IL_0011: call ""bool Fixable?.HasValue.get"" IL_0016: brtrue.s IL_001c IL_0018: ldc.i4.0 IL_0019: conv.u IL_001a: br.s IL_0025 IL_001c: ldloc.2 IL_001d: call ""ref int FixableExt.GetPinnableReference(Fixable?)"" IL_0022: stloc.1 IL_0023: ldloc.1 IL_0024: conv.u IL_0025: ldc.i4.4 IL_0026: add IL_0027: ldind.i4 IL_0028: call ""void System.Console.WriteLine(int)"" IL_002d: ldc.i4.0 IL_002e: conv.u IL_002f: stloc.1 IL_0030: ret } "); } [Fact] public void CustomFixedStructNullableErr() { var text = @" unsafe class C { public static void Main() { Fixable? f = new Fixable(); fixed (int* p = f) { System.Console.WriteLine(p[1]); } } } public struct Fixable { public ref int GetPinnableReference() { return ref (new int[]{1,2,3})[0]; } } "; var compVerifier = CreateCompilationWithMscorlib46(text, options: TestOptions.UnsafeReleaseExe); compVerifier.VerifyDiagnostics( // (8,25): error CS9385: The given expression cannot be used in a fixed statement // fixed (int* p = f) Diagnostic(ErrorCode.ERR_ExprCannotBeFixed, "f").WithLocation(8, 25) ); } [Fact] public void CustomFixedErrAmbiguous() { var text = @" unsafe class C { public static void Main() { fixed (int* p = new Fixable(1)) { System.Console.Write(p[1]); } var f = new Fixable(1); fixed (int* p = f) { System.Console.Write(p[2]); } } } public struct Fixable { public Fixable(int arg){} } public static class FixableExt { public static ref readonly int GetPinnableReference(in this Fixable f) { return ref (new int[]{1,2,3})[0]; } } public static class FixableExt1 { public static ref readonly int GetPinnableReference(in this Fixable f) { return ref (new int[]{1,2,3})[0]; } } "; var compVerifier = CreateCompilationWithMscorlib46(text, options: TestOptions.UnsafeReleaseExe); compVerifier.VerifyDiagnostics( // (6,25): error CS0121: The call is ambiguous between the following methods or properties: 'FixableExt.GetPinnableReference(in Fixable)' and 'FixableExt1.GetPinnableReference(in Fixable)' // fixed (int* p = new Fixable(1)) Diagnostic(ErrorCode.ERR_AmbigCall, "new Fixable(1)").WithArguments("FixableExt.GetPinnableReference(in Fixable)", "FixableExt1.GetPinnableReference(in Fixable)").WithLocation(6, 25), // (6,25): error CS9385: The given expression cannot be used in a fixed statement // fixed (int* p = new Fixable(1)) Diagnostic(ErrorCode.ERR_ExprCannotBeFixed, "new Fixable(1)").WithLocation(6, 25), // (12,25): error CS0121: The call is ambiguous between the following methods or properties: 'FixableExt.GetPinnableReference(in Fixable)' and 'FixableExt1.GetPinnableReference(in Fixable)' // fixed (int* p = f) Diagnostic(ErrorCode.ERR_AmbigCall, "f").WithArguments("FixableExt.GetPinnableReference(in Fixable)", "FixableExt1.GetPinnableReference(in Fixable)").WithLocation(12, 25), // (12,25): error CS9385: The given expression cannot be used in a fixed statement // fixed (int* p = f) Diagnostic(ErrorCode.ERR_ExprCannotBeFixed, "f").WithLocation(12, 25) ); } [Fact] public void CustomFixedErrDynamic() { var text = @" unsafe class C { public static void Main() { fixed (int* p = (dynamic)(new Fixable(1))) { System.Console.Write(p[1]); } } } public struct Fixable { public Fixable(int arg){} } public static class FixableExt { public static ref readonly int GetPinnableReference(in this Fixable f) { return ref (new int[]{1,2,3})[0]; } } "; var compVerifier = CreateCompilationWithMscorlib46(text, options: TestOptions.UnsafeReleaseExe); compVerifier.VerifyDiagnostics( // (6,25): error CS9385: The given expression cannot be used in a fixed statement // fixed (int* p = (dynamic)(new Fixable(1))) Diagnostic(ErrorCode.ERR_ExprCannotBeFixed, "(dynamic)(new Fixable(1))").WithLocation(6, 25) ); } [Fact] public void CustomFixedErrBad() { var text = @" unsafe class C { public static void Main() { fixed (int* p = (HocusPocus)(new Fixable(1))) { System.Console.Write(p[1]); } } } public struct Fixable { public Fixable(int arg){} } public static class FixableExt { public static ref readonly int GetPinnableReference(in this Fixable f) { return ref (new int[]{1,2,3})[0]; } } "; var compVerifier = CreateCompilationWithMscorlib46(text, options: TestOptions.UnsafeReleaseExe); compVerifier.VerifyDiagnostics( // (6,26): error CS0246: The type or namespace name 'HocusPocus' could not be found (are you missing a using directive or an assembly reference?) // fixed (int* p = (HocusPocus)(new Fixable(1))) Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "HocusPocus").WithArguments("HocusPocus").WithLocation(6, 26) ); } [Fact] public void SimpleCaseOfCustomFixedGeneric() { var text = @" unsafe class C { public static void Main() { Test(42); Test((object)null); } public static void Test<T>(T arg) { fixed (int* p = arg) { System.Console.Write(p == null? 0: p[1]); } } } static class FixAllExt { public static ref int GetPinnableReference<T>(this T dummy) { return ref (new int[]{1,2,3})[0]; } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: @"20", verify: Verification.Fails); compVerifier.VerifyIL("C.Test<T>(T)", @" { // Code size 49 (0x31) .maxstack 2 .locals init (int* V_0, //p pinned int& V_1) IL_0000: ldarg.0 IL_0001: box ""T"" IL_0006: brtrue.s IL_000c IL_0008: ldc.i4.0 IL_0009: conv.u IL_000a: br.s IL_001b IL_000c: ldarga.s V_0 IL_000e: ldobj ""T"" IL_0013: call ""ref int FixAllExt.GetPinnableReference<T>(T)"" IL_0018: stloc.1 IL_0019: ldloc.1 IL_001a: conv.u IL_001b: stloc.0 IL_001c: ldloc.0 IL_001d: ldc.i4.0 IL_001e: conv.u IL_001f: beq.s IL_0027 IL_0021: ldloc.0 IL_0022: ldc.i4.4 IL_0023: add IL_0024: ldind.i4 IL_0025: br.s IL_0028 IL_0027: ldc.i4.0 IL_0028: call ""void System.Console.Write(int)"" IL_002d: ldc.i4.0 IL_002e: conv.u IL_002f: stloc.1 IL_0030: ret } "); } [Fact] public void CustomFixedStructSideeffects() { var text = @" unsafe class C { public static void Main() { var b = new FixableStruct(); Test(ref b); System.Console.WriteLine(b.x); } public static void Test(ref FixableStruct arg) { fixed (int* p = arg) { System.Console.Write(p[1]); } } } struct FixableStruct { public int x; public ref int GetPinnableReference() { x = 456; return ref (new int[] { 4, 5, 6 })[0]; } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, verify: Verification.Fails, expectedOutput: @"5456"); compVerifier.VerifyIL("C.Test(ref FixableStruct)", @" { // Code size 21 (0x15) .maxstack 2 .locals init (pinned int& V_0) IL_0000: ldarg.0 IL_0001: call ""ref int FixableStruct.GetPinnableReference()"" IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: conv.u IL_0009: ldc.i4.4 IL_000a: add IL_000b: ldind.i4 IL_000c: call ""void System.Console.Write(int)"" IL_0011: ldc.i4.0 IL_0012: conv.u IL_0013: stloc.0 IL_0014: ret } "); } [Fact] public void CustomFixedClassSideeffects() { var text = @" using System; unsafe class C { public static void Main() { var b = new FixableClass(); Test(ref b); System.Console.WriteLine(b.x); } public static void Test(ref FixableClass arg) { fixed (int* p = arg) { System.Console.Write(p[1]); } } } class FixableClass { public int x; [Obsolete] public ref int GetPinnableReference() { x = 456; return ref (new int[] { 4, 5, 6 })[0]; } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, verify: Verification.Fails, expectedOutput: @"5456"); compVerifier.VerifyDiagnostics( // (14,29): warning CS0612: 'FixableClass.GetPinnableReference()' is obsolete // fixed (int* p = arg) Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "arg").WithArguments("FixableClass.GetPinnableReference()").WithLocation(14, 29) ); // note that defensive copy is created compVerifier.VerifyIL("C.Test(ref FixableClass)", @" { // Code size 30 (0x1e) .maxstack 2 .locals init (pinned int& V_0) IL_0000: ldarg.0 IL_0001: ldind.ref IL_0002: dup IL_0003: brtrue.s IL_000a IL_0005: pop IL_0006: ldc.i4.0 IL_0007: conv.u IL_0008: br.s IL_0012 IL_000a: call ""ref int FixableClass.GetPinnableReference()"" IL_000f: stloc.0 IL_0010: ldloc.0 IL_0011: conv.u IL_0012: ldc.i4.4 IL_0013: add IL_0014: ldind.i4 IL_0015: call ""void System.Console.Write(int)"" IL_001a: ldc.i4.0 IL_001b: conv.u IL_001c: stloc.0 IL_001d: ret } "); } [Fact] public void CustomFixedGenericSideeffects() { var text = @" unsafe class C { public static void Main() { var a = new FixableClass(); Test(ref a); System.Console.WriteLine(a.x); var b = new FixableStruct(); Test(ref b); System.Console.WriteLine(b.x); } public static void Test<T>(ref T arg) where T: IFixable { fixed (int* p = arg) { System.Console.Write(p[1]); } } } interface IFixable { ref int GetPinnableReference(); } class FixableClass : IFixable { public int x; public ref int GetPinnableReference() { x = 123; return ref (new int[] { 1, 2, 3 })[0]; } } struct FixableStruct : IFixable { public int x; public ref int GetPinnableReference() { x = 456; return ref (new int[] { 4, 5, 6 })[0]; } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, verify: Verification.Fails, expectedOutput: @"2123 5456"); compVerifier.VerifyIL("C.Test<T>(ref T)", @" { // Code size 64 (0x40) .maxstack 2 .locals init (pinned int& V_0, T V_1) IL_0000: ldarg.0 IL_0001: ldloca.s V_1 IL_0003: initobj ""T"" IL_0009: ldloc.1 IL_000a: box ""T"" IL_000f: brtrue.s IL_0026 IL_0011: ldobj ""T"" IL_0016: stloc.1 IL_0017: ldloca.s V_1 IL_0019: ldloc.1 IL_001a: box ""T"" IL_001f: brtrue.s IL_0026 IL_0021: pop IL_0022: ldc.i4.0 IL_0023: conv.u IL_0024: br.s IL_0034 IL_0026: constrained. ""T"" IL_002c: callvirt ""ref int IFixable.GetPinnableReference()"" IL_0031: stloc.0 IL_0032: ldloc.0 IL_0033: conv.u IL_0034: ldc.i4.4 IL_0035: add IL_0036: ldind.i4 IL_0037: call ""void System.Console.Write(int)"" IL_003c: ldc.i4.0 IL_003d: conv.u IL_003e: stloc.0 IL_003f: ret } "); } [Fact] public void CustomFixedGenericRefExtension() { var text = @" unsafe class C { public static void Main() { var b = new FixableStruct(); Test(ref b); System.Console.WriteLine(b.x); } public static void Test<T>(ref T arg) where T: struct, IFixable { fixed (int* p = arg) { System.Console.Write(p[1]); } } } public interface IFixable { ref int GetPinnableReferenceImpl(); } public struct FixableStruct : IFixable { public int x; public ref int GetPinnableReferenceImpl() { x = 456; return ref (new int[] { 4, 5, 6 })[0]; } } public static class FixableExt { public static ref int GetPinnableReference<T>(ref this T f) where T: struct, IFixable { return ref f.GetPinnableReferenceImpl(); } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, verify: Verification.Fails, expectedOutput: @"5456"); compVerifier.VerifyIL("C.Test<T>(ref T)", @" { // Code size 21 (0x15) .maxstack 2 .locals init (pinned int& V_0) IL_0000: ldarg.0 IL_0001: call ""ref int FixableExt.GetPinnableReference<T>(ref T)"" IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: conv.u IL_0009: ldc.i4.4 IL_000a: add IL_000b: ldind.i4 IL_000c: call ""void System.Console.Write(int)"" IL_0011: ldc.i4.0 IL_0012: conv.u IL_0013: stloc.0 IL_0014: ret } "); } [Fact] public void CustomFixedStructInExtension() { var text = @" unsafe class C { public static void Main() { fixed (int* p = new Fixable(1)) { System.Console.Write(p[1]); } var f = new Fixable(1); fixed (int* p = f) { System.Console.Write(p[2]); } } } public struct Fixable { public Fixable(int arg){} } public static class FixableExt { public static ref readonly int GetPinnableReference(in this Fixable f) { return ref (new int[]{1,2,3})[0]; } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: @"23", verify: Verification.Fails); compVerifier.VerifyIL("C.Main", @" { // Code size 61 (0x3d) .maxstack 3 .locals init (Fixable V_0, //f pinned int& V_1, Fixable V_2) IL_0000: ldc.i4.1 IL_0001: newobj ""Fixable..ctor(int)"" IL_0006: stloc.2 IL_0007: ldloca.s V_2 IL_0009: call ""ref readonly int FixableExt.GetPinnableReference(in Fixable)"" IL_000e: stloc.1 IL_000f: ldloc.1 IL_0010: conv.u IL_0011: ldc.i4.4 IL_0012: add IL_0013: ldind.i4 IL_0014: call ""void System.Console.Write(int)"" IL_0019: ldc.i4.0 IL_001a: conv.u IL_001b: stloc.1 IL_001c: ldloca.s V_0 IL_001e: ldc.i4.1 IL_001f: call ""Fixable..ctor(int)"" IL_0024: ldloca.s V_0 IL_0026: call ""ref readonly int FixableExt.GetPinnableReference(in Fixable)"" IL_002b: stloc.1 IL_002c: ldloc.1 IL_002d: conv.u IL_002e: ldc.i4.2 IL_002f: conv.i IL_0030: ldc.i4.4 IL_0031: mul IL_0032: add IL_0033: ldind.i4 IL_0034: call ""void System.Console.Write(int)"" IL_0039: ldc.i4.0 IL_003a: conv.u IL_003b: stloc.1 IL_003c: ret } "); } [Fact] public void CustomFixedStructObjectExtension_01() { var text = @" unsafe class C { public static void Main() { fixed (int* p = new Fixable(1)) { System.Console.Write(p[1]); } var f = new Fixable(1); fixed (int* p = f) { System.Console.Write(p[2]); } } } public struct Fixable { public Fixable(int arg){} } public static class FixableExt { public static ref readonly int GetPinnableReference(this object f) { return ref (new int[]{1,2,3})[0]; } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: @"23", verify: Verification.Fails); compVerifier.VerifyIL("C.Main", @" { // Code size 64 (0x40) .maxstack 3 .locals init (pinned int& V_0) IL_0000: ldc.i4.1 IL_0001: newobj ""Fixable..ctor(int)"" IL_0006: box ""Fixable"" IL_000b: call ""ref readonly int FixableExt.GetPinnableReference(object)"" IL_0010: stloc.0 IL_0011: ldloc.0 IL_0012: conv.u IL_0013: ldc.i4.4 IL_0014: add IL_0015: ldind.i4 IL_0016: call ""void System.Console.Write(int)"" IL_001b: ldc.i4.0 IL_001c: conv.u IL_001d: stloc.0 IL_001e: ldc.i4.1 IL_001f: newobj ""Fixable..ctor(int)"" IL_0024: box ""Fixable"" IL_0029: call ""ref readonly int FixableExt.GetPinnableReference(object)"" IL_002e: stloc.0 IL_002f: ldloc.0 IL_0030: conv.u IL_0031: ldc.i4.2 IL_0032: conv.i IL_0033: ldc.i4.4 IL_0034: mul IL_0035: add IL_0036: ldind.i4 IL_0037: call ""void System.Console.Write(int)"" IL_003c: ldc.i4.0 IL_003d: conv.u IL_003e: stloc.0 IL_003f: ret } "); } [Fact] public void CustomFixedStructObjectExtension_02() { // We check conversion during initial binding var text = """ #pragma warning disable CS0436 // The type 'ReadOnlySpan<T>' in '' conflicts with the imported type 'ReadOnlySpan<T>' unsafe class C { public static void M() { System.ReadOnlySpan<string> x = default; fixed (long* p = x) { } } } static class E { public static ref long GetPinnableReference(this System.ReadOnlySpan<object> s) => throw null; } namespace System { public ref struct ReadOnlySpan<T> { } } """; var comp = CreateCompilation(text, options: TestOptions.UnsafeDebugDll, targetFramework: TargetFramework.Net90); comp.VerifyDiagnostics( // (8,26): error CS0656: Missing compiler required member 'ReadOnlySpan<T>.CastUp' // fixed (long* p = x) Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "x").WithArguments("System.ReadOnlySpan<T>", "CastUp").WithLocation(8, 26) ); } [Fact] public void CustomFixedStructRefExtension() { var text = @" unsafe class C { public static void Main() { var f = new Fixable(1); fixed (int* p = f) { System.Console.Write(p[2]); } } } public struct Fixable { public Fixable(int arg){} } public static class FixableExt { public static ref int GetPinnableReference(ref this Fixable f) { return ref (new int[]{1,2,3})[0]; } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: @"3", verify: Verification.Fails); compVerifier.VerifyIL("C.Main", @" { // Code size 33 (0x21) .maxstack 3 .locals init (Fixable V_0, //f pinned int& V_1) IL_0000: ldloca.s V_0 IL_0002: ldc.i4.1 IL_0003: call ""Fixable..ctor(int)"" IL_0008: ldloca.s V_0 IL_000a: call ""ref int FixableExt.GetPinnableReference(ref Fixable)"" IL_000f: stloc.1 IL_0010: ldloc.1 IL_0011: conv.u IL_0012: ldc.i4.2 IL_0013: conv.i IL_0014: ldc.i4.4 IL_0015: mul IL_0016: add IL_0017: ldind.i4 IL_0018: call ""void System.Console.Write(int)"" IL_001d: ldc.i4.0 IL_001e: conv.u IL_001f: stloc.1 IL_0020: ret } "); } [Fact] public void CustomFixedStructRefExtensionErr() { var text = @" unsafe class C { public static void Main() { fixed (int* p = new Fixable(1)) { System.Console.Write(p[1]); } } } public struct Fixable { public Fixable(int arg){} } public static class FixableExt { public static ref int GetPinnableReference(ref this Fixable f) { return ref (new int[]{1,2,3})[0]; } } "; var compVerifier = CreateCompilationWithMscorlib46(text, options: TestOptions.UnsafeReleaseExe); compVerifier.VerifyDiagnostics( // (6,25): error CS1510: A ref or out value must be an assignable variable // fixed (int* p = new Fixable(1)) Diagnostic(ErrorCode.ERR_RefLvalueExpected, "new Fixable(1)").WithLocation(6, 25), // (6,25): error CS9385: The given expression cannot be used in a fixed statement // fixed (int* p = new Fixable(1)) Diagnostic(ErrorCode.ERR_ExprCannotBeFixed, "new Fixable(1)").WithLocation(6, 25) ); } [Fact] public void CustomFixedStructVariousErr01() { var text = @" unsafe class C { public static void Main() { fixed (int* p = new Fixable(1)) { System.Console.Write(p[1]); } } } public struct Fixable { public Fixable(int arg){} } public static class FixableExt { private static ref int GetPinnableReference(this Fixable f) { return ref (new int[]{1,2,3})[0]; } } "; var compVerifier = CreateCompilationWithMscorlib46(text, options: TestOptions.UnsafeReleaseExe); compVerifier.VerifyDiagnostics( // (6,25): error CS8385: The given expression cannot be used in a fixed statement // fixed (int* p = new Fixable(1)) Diagnostic(ErrorCode.ERR_ExprCannotBeFixed, "new Fixable(1)").WithLocation(6, 25) ); } [Fact] public void CustomFixedStructVariousErr01_oldVersion() { var text = @" unsafe class C { public static void Main() { fixed (int* p = new Fixable(1)) { System.Console.Write(p[1]); } } } public struct Fixable { public Fixable(int arg){} } public static class FixableExt { private static ref int GetPinnableReference(this Fixable f) { return ref (new int[]{1,2,3})[0]; } } "; var compVerifier = CreateCompilationWithMscorlib46(text, options: TestOptions.UnsafeReleaseExe, parseOptions: TestOptions.Regular7_2); compVerifier.VerifyDiagnostics( // (6,25): error CS9385: The given expression cannot be used in a fixed statement // fixed (int* p = new Fixable(1)) Diagnostic(ErrorCode.ERR_ExprCannotBeFixed, "new Fixable(1)").WithLocation(6, 25) ); } [Fact] public void CustomFixedStructVariousErr02() { var text = @" unsafe class C { public static void Main() { fixed (int* p = new Fixable(1)) { System.Console.Write(p[1]); } } } public struct Fixable { public Fixable(int arg){} public static ref int GetPinnableReference() { return ref (new int[]{1,2,3})[0]; } } "; var compVerifier = CreateCompilationWithMscorlib46(text, options: TestOptions.UnsafeReleaseExe); compVerifier.VerifyDiagnostics( // (6,25): error CS9385: The given expression cannot be used in a fixed statement // fixed (int* p = new Fixable(1)) Diagnostic(ErrorCode.ERR_ExprCannotBeFixed, "new Fixable(1)").WithLocation(6, 25), // (6,25): error CS0176: Member 'Fixable.GetPinnableReference()' cannot be accessed with an instance reference; qualify it with a type name instead // fixed (int* p = new Fixable(1)) Diagnostic(ErrorCode.ERR_ObjectProhibited, "new Fixable(1)").WithArguments("Fixable.GetPinnableReference()").WithLocation(6, 25) ); } [Fact] public void CustomFixedStructVariousErr03() { var text = @" unsafe class C { public static void Main() { fixed (int* p = new Fixable(1)) { System.Console.Write(p[1]); } } } public struct Fixable { public Fixable(int arg){} public ref int GetPinnableReference => ref (new int[]{1,2,3})[0]; } "; var compVerifier = CreateCompilationWithMscorlib46(text, options: TestOptions.UnsafeReleaseExe); compVerifier.VerifyDiagnostics( // (6,25): error CS1955: Non-invocable member 'Fixable.GetPinnableReference' cannot be used like a method. // fixed (int* p = new Fixable(1)) Diagnostic(ErrorCode.ERR_NonInvocableMemberCalled, "new Fixable(1)").WithArguments("Fixable.GetPinnableReference").WithLocation(6, 25), // (6,25): error CS9385: The given expression cannot be used in a fixed statement // fixed (int* p = new Fixable(1)) Diagnostic(ErrorCode.ERR_ExprCannotBeFixed, "new Fixable(1)").WithLocation(6, 25) ); } [Fact] public void CustomFixedStructVariousErr04() { var text = @" unsafe class C { public static void Main() { fixed (int* p = new Fixable(1)) { System.Console.Write(p[1]); } } } public struct Fixable { public Fixable(int arg){} public ref int GetPinnableReference<T>() => ref (new int[]{1,2,3})[0]; } "; var compVerifier = CreateCompilationWithMscorlib46(text, options: TestOptions.UnsafeReleaseExe); compVerifier.VerifyDiagnostics( // (6,25): error CS0411: The type arguments for method 'Fixable.GetPinnableReference<T>()' cannot be inferred from the usage. Try specifying the type arguments explicitly. // fixed (int* p = new Fixable(1)) Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "new Fixable(1)").WithArguments("Fixable.GetPinnableReference<T>()").WithLocation(6, 25), // (6,25): error CS9385: The given expression cannot be used in a fixed statement // fixed (int* p = new Fixable(1)) Diagnostic(ErrorCode.ERR_ExprCannotBeFixed, "new Fixable(1)").WithLocation(6, 25) ); } [Fact] public void CustomFixedStructVariousErr05_Obsolete() { var text = @" using System; unsafe class C { public static void Main() { fixed (int* p = new Fixable(1)) { System.Console.Write(p[1]); } } } public struct Fixable { public Fixable(int arg){} [Obsolete(""hi"", true)] public ref int GetPinnableReference() => ref (new int[]{1,2,3})[0]; } "; var compVerifier = CreateCompilationWithMscorlib46(text, options: TestOptions.UnsafeReleaseExe); compVerifier.VerifyDiagnostics( // (8,25): error CS0619: 'Fixable.GetPinnableReference()' is obsolete: 'hi' // fixed (int* p = new Fixable(1)) Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "new Fixable(1)").WithArguments("Fixable.GetPinnableReference()", "hi").WithLocation(8, 25) ); } [Fact] public void CustomFixedStructVariousErr06_UseSite() { var missing_cs = "public struct Missing { }"; var missing = CreateCompilationWithMscorlib461(missing_cs, options: TestOptions.DebugDll, assemblyName: "missing"); var lib_cs = @" public struct Fixable { public Fixable(int arg){} public ref Missing GetPinnableReference() => throw null; } "; var lib = CreateCompilationWithMscorlib461(lib_cs, references: new[] { missing.EmitToImageReference() }, options: TestOptions.DebugDll); var source = @" unsafe class C { public static void Main() { fixed (void* p = new Fixable(1)) { } } } "; var comp = CreateCompilationWithMscorlib461(source, references: new[] { lib.EmitToImageReference() }, options: TestOptions.UnsafeDebugDll); comp.VerifyDiagnostics( // (6,26): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // fixed (void* p = new Fixable(1)) Diagnostic(ErrorCode.ERR_NoTypeDef, "new Fixable(1)").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(6, 26), // (6,26): error CS9385: The given expression cannot be used in a fixed statement // fixed (void* p = new Fixable(1)) Diagnostic(ErrorCode.ERR_ExprCannotBeFixed, "new Fixable(1)").WithLocation(6, 26) ); } [Fact] public void CustomFixedStructVariousErr07_Optional() { var text = @" unsafe class C { public static void Main() { fixed (int* p = new Fixable(1)) { System.Console.Write(p[1]); } } } public struct Fixable { public Fixable(int arg){} public ref int GetPinnableReference(int x = 0) => ref (new int[]{1,2,3})[0]; } "; var compVerifier = CreateCompilationWithMscorlib46(text, options: TestOptions.UnsafeReleaseExe); compVerifier.VerifyDiagnostics( // (6,25): warning CS0280: 'Fixable' does not implement the 'fixed' pattern. 'Fixable.GetPinnableReference(int)' has the wrong signature. // fixed (int* p = new Fixable(1)) Diagnostic(ErrorCode.WRN_PatternBadSignature, "new Fixable(1)").WithArguments("Fixable", "fixed", "Fixable.GetPinnableReference(int)").WithLocation(6, 25), // (6,25): error CS9385: The given expression cannot be used in a fixed statement // fixed (int* p = new Fixable(1)) Diagnostic(ErrorCode.ERR_ExprCannotBeFixed, "new Fixable(1)").WithLocation(6, 25) ); } [Fact] public void FixStringMissingAllHelpers() { var text = @" unsafe class C { public static void Main() { fixed (char* p = string.Empty) { } } } "; var comp = CreateCompilationWithMscorlib46(text, options: TestOptions.UnsafeReleaseExe); comp.MakeMemberMissing(WellKnownMember.System_Runtime_CompilerServices_RuntimeHelpers__get_OffsetToStringData); comp.VerifyEmitDiagnostics( // (6,26): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.RuntimeHelpers.get_OffsetToStringData' // fixed (char* p = string.Empty) Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "string.Empty").WithArguments("System.Runtime.CompilerServices.RuntimeHelpers", "get_OffsetToStringData").WithLocation(6, 26) ); } [Fact] public void FixStringArrayExtensionHelpersIgnored() { var text = @" unsafe class C { public static void Main() { fixed (char* p = ""A"") { *p = default; } fixed (char* p = new char[1]) { *p = default; } } } public static class FixableExt { public static ref char GetPinnableReference(this string self) => throw null; public static ref char GetPinnableReference(this char[] self) => throw null; } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, verify: Verification.Fails, expectedOutput: @""); compVerifier.VerifyIL("C.Main()", @" { // Code size 60 (0x3c) .maxstack 2 .locals init (char* V_0, //p pinned string V_1, char* V_2, //p pinned char[] V_3) IL_0000: ldstr ""A"" IL_0005: stloc.1 IL_0006: ldloc.1 IL_0007: conv.u IL_0008: stloc.0 IL_0009: ldloc.0 IL_000a: brfalse.s IL_0014 IL_000c: ldloc.0 IL_000d: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_0012: add IL_0013: stloc.0 IL_0014: ldloc.0 IL_0015: ldc.i4.0 IL_0016: stind.i2 IL_0017: ldnull IL_0018: stloc.1 IL_0019: ldc.i4.1 IL_001a: newarr ""char"" IL_001f: dup IL_0020: stloc.3 IL_0021: brfalse.s IL_0028 IL_0023: ldloc.3 IL_0024: ldlen IL_0025: conv.i4 IL_0026: brtrue.s IL_002d IL_0028: ldc.i4.0 IL_0029: conv.u IL_002a: stloc.2 IL_002b: br.s IL_0036 IL_002d: ldloc.3 IL_002e: ldc.i4.0 IL_002f: ldelema ""char"" IL_0034: conv.u IL_0035: stloc.2 IL_0036: ldloc.2 IL_0037: ldc.i4.0 IL_0038: stind.i2 IL_0039: ldnull IL_003a: stloc.3 IL_003b: ret } "); } [Fact] public void CustomFixedDelegateErr() { var text = @" unsafe class C { public static void Main() { fixed (int* p = new Fixable()) { System.Console.Write(p[1]); } } } public delegate ref int ReturnsRef(); public struct Fixable { public Fixable(int arg){} public ReturnsRef GetPinnableReference => null; } "; var compVerifier = CreateCompilationWithMscorlib46(text, options: TestOptions.UnsafeReleaseExe); compVerifier.VerifyDiagnostics( // (6,25): error CS9385: The given expression cannot be used in a fixed statement // fixed (int* p = new Fixable()) Diagnostic(ErrorCode.ERR_ExprCannotBeFixed, "new Fixable()").WithLocation(6, 25) ); } [Fact] [WorkItem("https://github.com/dotnet/roslyn/issues/66167")] public void Issue66167_01() { var text = @" unsafe class C { public static void Main() { Test((int?)null); Test2((int?)null); } public static void Test<T>(T arg) { fixed (int* p = arg) { System.Console.Write(p == null? 0: p[1]); } } public static void Test2(int? arg) { fixed (int* p = arg) { System.Console.Write(p == null? 0: p[1]); } } } static class FixAllExt { public static ref int GetPinnableReference<T>(this T dummy) { return ref (new int[]{1,2,3})[0]; } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: @"00", verify: Verification.Fails); compVerifier.VerifyIL("C.Test<T>(T)", @" { // Code size 49 (0x31) .maxstack 2 .locals init (int* V_0, //p pinned int& V_1) IL_0000: ldarg.0 IL_0001: box ""T"" IL_0006: brtrue.s IL_000c IL_0008: ldc.i4.0 IL_0009: conv.u IL_000a: br.s IL_001b IL_000c: ldarga.s V_0 IL_000e: ldobj ""T"" IL_0013: call ""ref int FixAllExt.GetPinnableReference<T>(T)"" IL_0018: stloc.1 IL_0019: ldloc.1 IL_001a: conv.u IL_001b: stloc.0 IL_001c: ldloc.0 IL_001d: ldc.i4.0 IL_001e: conv.u IL_001f: beq.s IL_0027 IL_0021: ldloc.0 IL_0022: ldc.i4.4 IL_0023: add IL_0024: ldind.i4 IL_0025: br.s IL_0028 IL_0027: ldc.i4.0 IL_0028: call ""void System.Console.Write(int)"" IL_002d: ldc.i4.0 IL_002e: conv.u IL_002f: stloc.1 IL_0030: ret } "); compVerifier.VerifyIL("C.Test2(int?)", @" { // Code size 46 (0x2e) .maxstack 2 .locals init (int* V_0, //p pinned int& V_1, int? V_2) IL_0000: ldarg.0 IL_0001: stloc.2 IL_0002: ldloca.s V_2 IL_0004: call ""bool int?.HasValue.get"" IL_0009: brtrue.s IL_000f IL_000b: ldc.i4.0 IL_000c: conv.u IL_000d: br.s IL_0018 IL_000f: ldloc.2 IL_0010: call ""ref int FixAllExt.GetPinnableReference<int?>(int?)"" IL_0015: stloc.1 IL_0016: ldloc.1 IL_0017: conv.u IL_0018: stloc.0 IL_0019: ldloc.0 IL_001a: ldc.i4.0 IL_001b: conv.u IL_001c: beq.s IL_0024 IL_001e: ldloc.0 IL_001f: ldc.i4.4 IL_0020: add IL_0021: ldind.i4 IL_0022: br.s IL_0025 IL_0024: ldc.i4.0 IL_0025: call ""void System.Console.Write(int)"" IL_002a: ldc.i4.0 IL_002b: conv.u IL_002c: stloc.1 IL_002d: ret } "); } [Fact] [WorkItem("https://github.com/dotnet/roslyn/issues/66167")] public void Issue66167_02() { var text = @" unsafe class C { public static void Main() { fixed (int* p = new S1()) { } fixed (int* p = new S1?()) { } fixed (int* p = new S2()) { } fixed (int* p = new S2?()) { } } } struct S1 { public ref int GetPinnableReference() { return ref (new int[]{1,2,3})[0]; } } struct S2 { } static class FixAllExt { public static ref int GetPinnableReference(this S2 dummy) { return ref (new int[]{1,2,3})[0]; } } "; var comp = CreateCompilation(text, options: TestOptions.UnsafeReleaseExe); comp.VerifyDiagnostics( // (10,25): error CS1929: 'S1?' does not contain a definition for 'GetPinnableReference' and the best extension method overload 'FixAllExt.GetPinnableReference(S2)' requires a receiver of type 'S2' // fixed (int* p = new S1?()) Diagnostic(ErrorCode.ERR_BadInstanceArgType, "new S1?()").WithArguments("S1?", "GetPinnableReference", "FixAllExt.GetPinnableReference(S2)", "S2").WithLocation(10, 25), // (10,25): error CS8385: The given expression cannot be used in a fixed statement // fixed (int* p = new S1?()) Diagnostic(ErrorCode.ERR_ExprCannotBeFixed, "new S1?()").WithLocation(10, 25), // (18,25): error CS1929: 'S2?' does not contain a definition for 'GetPinnableReference' and the best extension method overload 'FixAllExt.GetPinnableReference(S2)' requires a receiver of type 'S2' // fixed (int* p = new S2?()) Diagnostic(ErrorCode.ERR_BadInstanceArgType, "new S2?()").WithArguments("S2?", "GetPinnableReference", "FixAllExt.GetPinnableReference(S2)", "S2").WithLocation(18, 25), // (18,25): error CS8385: The given expression cannot be used in a fixed statement // fixed (int* p = new S2?()) Diagnostic(ErrorCode.ERR_ExprCannotBeFixed, "new S2?()").WithLocation(18, 25) ); } #endregion Custom fixed statement tests #region Pointer conversion tests [Fact] public void ConvertNullToPointer() { var template = @" using System; unsafe class C {{ static void Main() {{ {0} {{ char ch = 'a'; char* p = &ch; Console.WriteLine(p == null); p = null; Console.WriteLine(p == null); }} }} }} "; var expectedIL = @" { // Code size 31 (0x1f) .maxstack 2 .locals init (char V_0, //ch char* V_1) //p IL_0000: ldc.i4.s 97 IL_0002: stloc.0 IL_0003: ldloca.s V_0 IL_0005: conv.u IL_0006: stloc.1 IL_0007: ldloc.1 IL_0008: ldc.i4.0 IL_0009: conv.u IL_000a: ceq IL_000c: call ""void System.Console.WriteLine(bool)"" IL_0011: ldc.i4.0 IL_0012: conv.u IL_0013: stloc.1 IL_0014: ldloc.1 IL_0015: ldc.i4.0 IL_0016: conv.u IL_0017: ceq IL_0019: call ""void System.Console.WriteLine(bool)"" IL_001e: ret } "; var expectedOutput = @"False True"; CompileAndVerify(string.Format(template, "unchecked"), options: TestOptions.UnsafeReleaseExe, expectedOutput: expectedOutput, verify: Verification.Fails).VerifyIL("C.Main", expectedIL); CompileAndVerify(string.Format(template, "checked"), options: TestOptions.UnsafeReleaseExe, expectedOutput: expectedOutput, verify: Verification.Fails).VerifyIL("C.Main", expectedIL); } [Fact] public void ConvertPointerToPointerOrVoid() { var template = @" using System; unsafe class C {{ static void Main() {{ {0} {{ char ch = 'a'; char* c1 = &ch; void* v1 = c1; void* v2 = (void**)v1; char* c2 = (char*)v2; Console.WriteLine(*c2); }} }} }} "; var expectedIL = @" { // Code size 19 (0x13) .maxstack 1 .locals init (char V_0, //ch void* V_1, //v1 void* V_2, //v2 char* V_3) //c2 IL_0000: ldc.i4.s 97 IL_0002: stloc.0 IL_0003: ldloca.s V_0 IL_0005: conv.u IL_0006: stloc.1 IL_0007: ldloc.1 IL_0008: stloc.2 IL_0009: ldloc.2 IL_000a: stloc.3 IL_000b: ldloc.3 IL_000c: ldind.u2 IL_000d: call ""void System.Console.WriteLine(char)"" IL_0012: ret } "; var expectedOutput = @"a"; CompileAndVerify(string.Format(template, "unchecked"), options: TestOptions.UnsafeReleaseExe, expectedOutput: expectedOutput, verify: Verification.Fails).VerifyIL("C.Main", expectedIL); CompileAndVerify(string.Format(template, "checked"), options: TestOptions.UnsafeReleaseExe, expectedOutput: expectedOutput, verify: Verification.Fails).VerifyIL("C.Main", expectedIL); } [Fact] public void ConvertPointerToNumericUnchecked() { var text = @" using System; unsafe class C { void M(int* pi, void* pv, sbyte sb, byte b, short s, ushort us, int i, uint ui, long l, ulong ul) { unchecked { sb = (sbyte)pi; b = (byte)pi; s = (short)pi; us = (ushort)pi; i = (int)pi; ui = (uint)pi; l = (long)pi; ul = (ulong)pi; sb = (sbyte)pv; b = (byte)pv; s = (short)pv; us = (ushort)pv; i = (int)pv; ui = (uint)pv; l = (long)pv; ul = (ulong)pv; } } } "; CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails).VerifyIL("C.M", @" { // Code size 65 (0x41) .maxstack 1 IL_0000: ldarg.1 IL_0001: conv.i1 IL_0002: starg.s V_3 IL_0004: ldarg.1 IL_0005: conv.u1 IL_0006: starg.s V_4 IL_0008: ldarg.1 IL_0009: conv.i2 IL_000a: starg.s V_5 IL_000c: ldarg.1 IL_000d: conv.u2 IL_000e: starg.s V_6 IL_0010: ldarg.1 IL_0011: conv.i4 IL_0012: starg.s V_7 IL_0014: ldarg.1 IL_0015: conv.u4 IL_0016: starg.s V_8 IL_0018: ldarg.1 IL_0019: conv.u8 IL_001a: starg.s V_9 IL_001c: ldarg.1 IL_001d: conv.u8 IL_001e: starg.s V_10 IL_0020: ldarg.2 IL_0021: conv.i1 IL_0022: starg.s V_3 IL_0024: ldarg.2 IL_0025: conv.u1 IL_0026: starg.s V_4 IL_0028: ldarg.2 IL_0029: conv.i2 IL_002a: starg.s V_5 IL_002c: ldarg.2 IL_002d: conv.u2 IL_002e: starg.s V_6 IL_0030: ldarg.2 IL_0031: conv.i4 IL_0032: starg.s V_7 IL_0034: ldarg.2 IL_0035: conv.u4 IL_0036: starg.s V_8 IL_0038: ldarg.2 IL_0039: conv.u8 IL_003a: starg.s V_9 IL_003c: ldarg.2 IL_003d: conv.u8 IL_003e: starg.s V_10 IL_0040: ret } "); } [Fact] public void ConvertPointerToNumericChecked() { var text = @" using System; unsafe class C { void M(int* pi, void* pv, sbyte sb, byte b, short s, ushort us, int i, uint ui, long l, ulong ul) { checked { sb = (sbyte)pi; b = (byte)pi; s = (short)pi; us = (ushort)pi; i = (int)pi; ui = (uint)pi; l = (long)pi; ul = (ulong)pi; sb = (sbyte)pv; b = (byte)pv; s = (short)pv; us = (ushort)pv; i = (int)pv; ui = (uint)pv; l = (long)pv; ul = (ulong)pv; } } } "; CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails).VerifyIL("C.M", @" { // Code size 65 (0x41) .maxstack 1 IL_0000: ldarg.1 IL_0001: conv.ovf.i1.un IL_0002: starg.s V_3 IL_0004: ldarg.1 IL_0005: conv.ovf.u1.un IL_0006: starg.s V_4 IL_0008: ldarg.1 IL_0009: conv.ovf.i2.un IL_000a: starg.s V_5 IL_000c: ldarg.1 IL_000d: conv.ovf.u2.un IL_000e: starg.s V_6 IL_0010: ldarg.1 IL_0011: conv.ovf.i4.un IL_0012: starg.s V_7 IL_0014: ldarg.1 IL_0015: conv.ovf.u4.un IL_0016: starg.s V_8 IL_0018: ldarg.1 IL_0019: conv.ovf.i8.un IL_001a: starg.s V_9 IL_001c: ldarg.1 IL_001d: conv.u8 IL_001e: starg.s V_10 IL_0020: ldarg.2 IL_0021: conv.ovf.i1.un IL_0022: starg.s V_3 IL_0024: ldarg.2 IL_0025: conv.ovf.u1.un IL_0026: starg.s V_4 IL_0028: ldarg.2 IL_0029: conv.ovf.i2.un IL_002a: starg.s V_5 IL_002c: ldarg.2 IL_002d: conv.ovf.u2.un IL_002e: starg.s V_6 IL_0030: ldarg.2 IL_0031: conv.ovf.i4.un IL_0032: starg.s V_7 IL_0034: ldarg.2 IL_0035: conv.ovf.u4.un IL_0036: starg.s V_8 IL_0038: ldarg.2 IL_0039: conv.ovf.i8.un IL_003a: starg.s V_9 IL_003c: ldarg.2 IL_003d: conv.u8 IL_003e: starg.s V_10 IL_0040: ret } "); } [Fact] public void ConvertNumericToPointerUnchecked() { var text = @" using System; unsafe class C { void M(int* pi, void* pv, sbyte sb, byte b, short s, ushort us, int i, uint ui, long l, ulong ul) { unchecked { pi = (int*)sb; pi = (int*)b; pi = (int*)s; pi = (int*)us; pi = (int*)i; pi = (int*)ui; pi = (int*)l; pi = (int*)ul; pv = (void*)sb; pv = (void*)b; pv = (void*)s; pv = (void*)us; pv = (void*)i; pv = (void*)ui; pv = (void*)l; pv = (void*)ul; } } } "; CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.FailsPEVerify).VerifyIL("C.M", @" { // Code size 79 (0x4f) .maxstack 1 IL_0000: ldarg.3 IL_0001: conv.i IL_0002: starg.s V_1 IL_0004: ldarg.s V_4 IL_0006: conv.u IL_0007: starg.s V_1 IL_0009: ldarg.s V_5 IL_000b: conv.i IL_000c: starg.s V_1 IL_000e: ldarg.s V_6 IL_0010: conv.u IL_0011: starg.s V_1 IL_0013: ldarg.s V_7 IL_0015: conv.i IL_0016: starg.s V_1 IL_0018: ldarg.s V_8 IL_001a: conv.u IL_001b: starg.s V_1 IL_001d: ldarg.s V_9 IL_001f: conv.u IL_0020: starg.s V_1 IL_0022: ldarg.s V_10 IL_0024: conv.u IL_0025: starg.s V_1 IL_0027: ldarg.3 IL_0028: conv.i IL_0029: starg.s V_2 IL_002b: ldarg.s V_4 IL_002d: conv.u IL_002e: starg.s V_2 IL_0030: ldarg.s V_5 IL_0032: conv.i IL_0033: starg.s V_2 IL_0035: ldarg.s V_6 IL_0037: conv.u IL_0038: starg.s V_2 IL_003a: ldarg.s V_7 IL_003c: conv.i IL_003d: starg.s V_2 IL_003f: ldarg.s V_8 IL_0041: conv.u IL_0042: starg.s V_2 IL_0044: ldarg.s V_9 IL_0046: conv.u IL_0047: starg.s V_2 IL_0049: ldarg.s V_10 IL_004b: conv.u IL_004c: starg.s V_2 IL_004e: ret } "); } [Fact] public void ConvertNumericToPointerChecked() { var text = @" using System; unsafe class C { void M(int* pi, void* pv, sbyte sb, byte b, short s, ushort us, int i, uint ui, long l, ulong ul) { checked { pi = (int*)sb; pi = (int*)b; pi = (int*)s; pi = (int*)us; pi = (int*)i; pi = (int*)ui; pi = (int*)l; pi = (int*)ul; pv = (void*)sb; pv = (void*)b; pv = (void*)s; pv = (void*)us; pv = (void*)i; pv = (void*)ui; pv = (void*)l; pv = (void*)ul; } } } "; CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.FailsPEVerify).VerifyIL("C.M", @" { // Code size 79 (0x4f) .maxstack 1 IL_0000: ldarg.3 IL_0001: conv.ovf.u IL_0002: starg.s V_1 IL_0004: ldarg.s V_4 IL_0006: conv.u IL_0007: starg.s V_1 IL_0009: ldarg.s V_5 IL_000b: conv.ovf.u IL_000c: starg.s V_1 IL_000e: ldarg.s V_6 IL_0010: conv.u IL_0011: starg.s V_1 IL_0013: ldarg.s V_7 IL_0015: conv.ovf.u IL_0016: starg.s V_1 IL_0018: ldarg.s V_8 IL_001a: conv.u IL_001b: starg.s V_1 IL_001d: ldarg.s V_9 IL_001f: conv.ovf.u IL_0020: starg.s V_1 IL_0022: ldarg.s V_10 IL_0024: conv.ovf.u.un IL_0025: starg.s V_1 IL_0027: ldarg.3 IL_0028: conv.ovf.u IL_0029: starg.s V_2 IL_002b: ldarg.s V_4 IL_002d: conv.u IL_002e: starg.s V_2 IL_0030: ldarg.s V_5 IL_0032: conv.ovf.u IL_0033: starg.s V_2 IL_0035: ldarg.s V_6 IL_0037: conv.u IL_0038: starg.s V_2 IL_003a: ldarg.s V_7 IL_003c: conv.ovf.u IL_003d: starg.s V_2 IL_003f: ldarg.s V_8 IL_0041: conv.u IL_0042: starg.s V_2 IL_0044: ldarg.s V_9 IL_0046: conv.ovf.u IL_0047: starg.s V_2 IL_0049: ldarg.s V_10 IL_004b: conv.ovf.u.un IL_004c: starg.s V_2 IL_004e: ret } "); } [Fact] public void ConvertClassToPointerUDC() { var template = @" using System; unsafe class C {{ void M(int* pi, void* pv, Explicit e, Implicit i) {{ {0} {{ e = (Explicit)pi; e = (Explicit)pv; i = pi; i = pv; pi = (int*)e; pv = (int*)e; pi = i; pv = i; }} }} }} unsafe class Explicit {{ public static explicit operator Explicit(void* p) {{ return null; }} public static explicit operator int*(Explicit e) {{ return null; }} }} unsafe class Implicit {{ public static implicit operator Implicit(void* p) {{ return null; }} public static implicit operator int*(Implicit e) {{ return null; }} }} "; var expectedIL = @" { // Code size 67 (0x43) .maxstack 1 IL_0000: ldarg.1 IL_0001: call ""Explicit Explicit.op_Explicit(void*)"" IL_0006: starg.s V_3 IL_0008: ldarg.2 IL_0009: call ""Explicit Explicit.op_Explicit(void*)"" IL_000e: starg.s V_3 IL_0010: ldarg.1 IL_0011: call ""Implicit Implicit.op_Implicit(void*)"" IL_0016: starg.s V_4 IL_0018: ldarg.2 IL_0019: call ""Implicit Implicit.op_Implicit(void*)"" IL_001e: starg.s V_4 IL_0020: ldarg.3 IL_0021: call ""int* Explicit.op_Explicit(Explicit)"" IL_0026: starg.s V_1 IL_0028: ldarg.3 IL_0029: call ""int* Explicit.op_Explicit(Explicit)"" IL_002e: starg.s V_2 IL_0030: ldarg.s V_4 IL_0032: call ""int* Implicit.op_Implicit(Implicit)"" IL_0037: starg.s V_1 IL_0039: ldarg.s V_4 IL_003b: call ""int* Implicit.op_Implicit(Implicit)"" IL_0040: starg.s V_2 IL_0042: ret } "; CompileAndVerify(string.Format(template, "unchecked"), options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails).VerifyIL("C.M", expectedIL); CompileAndVerify(string.Format(template, "checked"), options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails).VerifyIL("C.M", expectedIL); } [Fact] public void ConvertIntPtrToPointer() { var template = @" using System; unsafe class C {{ void M(int* pi, void* pv, IntPtr i, UIntPtr u) {{ {0} {{ i = (IntPtr)pi; i = (IntPtr)pv; u = (UIntPtr)pi; u = (UIntPtr)pv; pi = (int*)i; pv = (int*)i; pi = (int*)u; pv = (int*)u; }} }} }} "; // Nothing special here - just more UDCs. var expectedIL = @" { // Code size 67 (0x43) .maxstack 1 IL_0000: ldarg.1 IL_0001: call ""System.IntPtr System.IntPtr.op_Explicit(void*)"" IL_0006: starg.s V_3 IL_0008: ldarg.2 IL_0009: call ""System.IntPtr System.IntPtr.op_Explicit(void*)"" IL_000e: starg.s V_3 IL_0010: ldarg.1 IL_0011: call ""System.UIntPtr System.UIntPtr.op_Explicit(void*)"" IL_0016: starg.s V_4 IL_0018: ldarg.2 IL_0019: call ""System.UIntPtr System.UIntPtr.op_Explicit(void*)"" IL_001e: starg.s V_4 IL_0020: ldarg.3 IL_0021: call ""void* System.IntPtr.op_Explicit(System.IntPtr)"" IL_0026: starg.s V_1 IL_0028: ldarg.3 IL_0029: call ""void* System.IntPtr.op_Explicit(System.IntPtr)"" IL_002e: starg.s V_2 IL_0030: ldarg.s V_4 IL_0032: call ""void* System.UIntPtr.op_Explicit(System.UIntPtr)"" IL_0037: starg.s V_1 IL_0039: ldarg.s V_4 IL_003b: call ""void* System.UIntPtr.op_Explicit(System.UIntPtr)"" IL_0040: starg.s V_2 IL_0042: ret } "; CompileAndVerify(string.Format(template, "unchecked"), options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails).VerifyIL("C.M", expectedIL); CompileAndVerify(string.Format(template, "checked"), options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails).VerifyIL("C.M", expectedIL); } [Fact] public void FixedStatementConversion() { var template = @" using System; unsafe class C {{ char c = 'a'; char[] a = new char[1]; static void Main() {{ {0} {{ C c = new C(); fixed (void* p = &c.c, q = c.a, r = ""hello"") {{ Console.Write((int)*(char*)p); Console.Write((int)*(char*)q); Console.Write((int)*(char*)r); }} }} }} }} "; // NB: "pinned System.IntPtr&" (which ildasm displays as "pinned native int&"), not void. var expectedIL = @" { // Code size 112 (0x70) .maxstack 2 .locals init (C V_0, //c void* V_1, //p void* V_2, //q void* V_3, //r pinned char& V_4, pinned char[] V_5, pinned string V_6) -IL_0000: nop -IL_0001: nop -IL_0002: newobj ""C..ctor()"" IL_0007: stloc.0 IL_0008: ldloc.0 IL_0009: ldflda ""char C.c"" IL_000e: stloc.s V_4 -IL_0010: ldloc.s V_4 IL_0012: conv.u IL_0013: stloc.1 -IL_0014: ldloc.0 IL_0015: ldfld ""char[] C.a"" IL_001a: dup IL_001b: stloc.s V_5 IL_001d: brfalse.s IL_0025 IL_001f: ldloc.s V_5 IL_0021: ldlen IL_0022: conv.i4 IL_0023: brtrue.s IL_002a IL_0025: ldc.i4.0 IL_0026: conv.u IL_0027: stloc.2 IL_0028: br.s IL_0034 IL_002a: ldloc.s V_5 IL_002c: ldc.i4.0 IL_002d: ldelema ""char"" IL_0032: conv.u IL_0033: stloc.2 IL_0034: ldstr ""hello"" IL_0039: stloc.s V_6 -IL_003b: ldloc.s V_6 IL_003d: conv.u IL_003e: stloc.3 IL_003f: ldloc.3 IL_0040: brfalse.s IL_004a IL_0042: ldloc.3 IL_0043: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_0048: add IL_0049: stloc.3 -IL_004a: nop -IL_004b: ldloc.1 IL_004c: ldind.u2 IL_004d: call ""void System.Console.Write(int)"" IL_0052: nop -IL_0053: ldloc.2 IL_0054: ldind.u2 IL_0055: call ""void System.Console.Write(int)"" IL_005a: nop -IL_005b: ldloc.3 IL_005c: ldind.u2 IL_005d: call ""void System.Console.Write(int)"" IL_0062: nop -IL_0063: nop ~IL_0064: ldc.i4.0 IL_0065: conv.u IL_0066: stloc.s V_4 IL_0068: ldnull IL_0069: stloc.s V_5 IL_006b: ldnull IL_006c: stloc.s V_6 -IL_006e: nop -IL_006f: ret } "; var expectedOutput = @"970104"; CompileAndVerify(string.Format(template, "unchecked"), options: TestOptions.UnsafeDebugExe, expectedOutput: expectedOutput, verify: Verification.Fails).VerifyIL("C.Main", expectedIL, sequencePointDisplay: SequencePointDisplayMode.Minimal); CompileAndVerify(string.Format(template, "checked "), options: TestOptions.UnsafeDebugExe, expectedOutput: expectedOutput, verify: Verification.Fails).VerifyIL("C.Main", expectedIL, sequencePointDisplay: SequencePointDisplayMode.Minimal); } [Fact] public void FixedStatementVoidPointerPointer() { var template = @" using System; unsafe class C {{ void* v; static void Main() {{ {0} {{ char ch = 'a'; C c = new C(); c.v = &ch; fixed (void** p = &c.v) {{ Console.Write(*(char*)*p); }} }} }} }} "; // NB: "pinned void*&", as in Dev10. var expectedIL = @" { // Code size 36 (0x24) .maxstack 3 .locals init (char V_0, //ch pinned void*& V_1) IL_0000: ldc.i4.s 97 IL_0002: stloc.0 IL_0003: newobj ""C..ctor()"" IL_0008: dup IL_0009: ldloca.s V_0 IL_000b: conv.u IL_000c: stfld ""void* C.v"" IL_0011: ldflda ""void* C.v"" IL_0016: stloc.1 IL_0017: ldloc.1 IL_0018: conv.u IL_0019: ldind.i IL_001a: ldind.u2 IL_001b: call ""void System.Console.Write(char)"" IL_0020: ldc.i4.0 IL_0021: conv.u IL_0022: stloc.1 IL_0023: ret } "; var expectedOutput = @"a"; CompileAndVerify(string.Format(template, "unchecked"), options: TestOptions.UnsafeReleaseExe, expectedOutput: expectedOutput, verify: Verification.Fails).VerifyIL("C.Main", expectedIL); CompileAndVerify(string.Format(template, "checked"), options: TestOptions.UnsafeReleaseExe, expectedOutput: expectedOutput, verify: Verification.Fails).VerifyIL("C.Main", expectedIL); } [ConditionalFact(typeof(DesktopOnly))] public void PointerArrayConversion() { var template = @" using System; unsafe class C {{ void M(int*[] api, void*[] apv, Array a) {{ {0} {{ a = api; a = apv; api = (int*[])a; apv = (void*[])a; }} }} }} "; var expectedIL = @" { // Code size 23 (0x17) .maxstack 1 IL_0000: ldarg.1 IL_0001: starg.s V_3 IL_0003: ldarg.2 IL_0004: starg.s V_3 IL_0006: ldarg.3 IL_0007: castclass ""int*[]"" IL_000c: starg.s V_1 IL_000e: ldarg.3 IL_000f: castclass ""void*[]"" IL_0014: starg.s V_2 IL_0016: ret } "; CompileAndVerify(string.Format(template, "unchecked"), options: TestOptions.UnsafeReleaseDll, verify: Verification.Passes).VerifyIL("C.M", expectedIL); CompileAndVerify(string.Format(template, "checked"), options: TestOptions.UnsafeReleaseDll, verify: Verification.Passes).VerifyIL("C.M", expectedIL); } [ConditionalFact(typeof(DesktopOnly))] public void PointerArrayConversionRuntimeError() { var text = @" unsafe class C { static void Main() { System.Globalization.CultureInfo saveUICulture = System.Threading.Thread.CurrentThread.CurrentUICulture; System.Threading.Thread.CurrentThread.CurrentUICulture = System.Globalization.CultureInfo.InvariantCulture; try { int*[] api = new int*[1]; System.Array a = api; a.GetValue(0); } finally { System.Threading.Thread.CurrentThread.CurrentUICulture = saveUICulture; } } } "; CompileAndVerifyException<NotSupportedException>(text, "Type is not supported.", allowUnsafe: true, verify: Verification.Fails); } [ConditionalFact(typeof(DesktopOnly))] public void PointerArrayEnumerableConversion() { var template = @" using System.Collections; unsafe class C {{ void M(int*[] api, void*[] apv, IEnumerable e) {{ {0} {{ e = api; e = apv; api = (int*[])e; apv = (void*[])e; }} }} }} "; var expectedIL = @" { // Code size 23 (0x17) .maxstack 1 IL_0000: ldarg.1 IL_0001: starg.s V_3 IL_0003: ldarg.2 IL_0004: starg.s V_3 IL_0006: ldarg.3 IL_0007: castclass ""int*[]"" IL_000c: starg.s V_1 IL_000e: ldarg.3 IL_000f: castclass ""void*[]"" IL_0014: starg.s V_2 IL_0016: ret } "; CompileAndVerify(string.Format(template, "unchecked"), options: TestOptions.UnsafeReleaseDll, verify: Verification.Passes).VerifyIL("C.M", expectedIL); CompileAndVerify(string.Format(template, "checked"), options: TestOptions.UnsafeReleaseDll, verify: Verification.Passes).VerifyIL("C.M", expectedIL); } [ConditionalFact(typeof(DesktopOnly))] public void PointerArrayEnumerableConversionRuntimeError() { var text = @" unsafe class C { static void Main() { System.Globalization.CultureInfo saveUICulture = System.Threading.Thread.CurrentThread.CurrentUICulture; System.Threading.Thread.CurrentThread.CurrentUICulture = System.Globalization.CultureInfo.InvariantCulture; try { int*[] api = new int*[1]; System.Collections.IEnumerable e = api; var enumerator = e.GetEnumerator(); enumerator.MoveNext(); var current = enumerator.Current; } finally { System.Threading.Thread.CurrentThread.CurrentUICulture = saveUICulture; } } } "; CompileAndVerifyException<NotSupportedException>(text, "Type is not supported.", allowUnsafe: true, verify: Verification.Fails); } [Fact] public void PointerArrayForeachSingle() { var text = @" using System; unsafe class C { static void Main() { int*[] array = new [] { (int*)1, (int*)2, }; foreach (var element in array) { Console.Write((int)element); } } } "; CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: "12", verify: Verification.Fails).VerifyIL("C.Main", @" { // Code size 41 (0x29) .maxstack 4 .locals init (int*[] V_0, int V_1) IL_0000: ldc.i4.2 IL_0001: newarr ""int*"" IL_0006: dup IL_0007: ldc.i4.0 IL_0008: ldc.i4.1 IL_0009: conv.i IL_000a: stelem.i IL_000b: dup IL_000c: ldc.i4.1 IL_000d: ldc.i4.2 IL_000e: conv.i IL_000f: stelem.i IL_0010: stloc.0 IL_0011: ldc.i4.0 IL_0012: stloc.1 IL_0013: br.s IL_0022 IL_0015: ldloc.0 IL_0016: ldloc.1 IL_0017: ldelem.i IL_0018: conv.i4 IL_0019: call ""void System.Console.Write(int)"" IL_001e: ldloc.1 IL_001f: ldc.i4.1 IL_0020: add IL_0021: stloc.1 IL_0022: ldloc.1 IL_0023: ldloc.0 IL_0024: ldlen IL_0025: conv.i4 IL_0026: blt.s IL_0015 IL_0028: ret } "); } [Fact] public void PointerArrayForeachMultiple() { var text = @" using System; unsafe class C { static void Main() { int*[,] array = new [,] { { (int*)1, (int*)2, }, { (int*)3, (int*)4, }, }; foreach (var element in array) { Console.Write((int)element); } } } "; CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: "1234", verify: Verification.FailsPEVerify).VerifyIL("C.Main", @" { // Code size 120 (0x78) .maxstack 5 .locals init (int*[,] V_0, int V_1, int V_2, int V_3, int V_4) IL_0000: ldc.i4.2 IL_0001: ldc.i4.2 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: conv.i IL_000c: call ""int*[*,*].Set"" IL_0011: dup IL_0012: ldc.i4.0 IL_0013: ldc.i4.1 IL_0014: ldc.i4.2 IL_0015: conv.i IL_0016: call ""int*[*,*].Set"" IL_001b: dup IL_001c: ldc.i4.1 IL_001d: ldc.i4.0 IL_001e: ldc.i4.3 IL_001f: conv.i IL_0020: call ""int*[*,*].Set"" IL_0025: dup IL_0026: ldc.i4.1 IL_0027: ldc.i4.1 IL_0028: ldc.i4.4 IL_0029: conv.i IL_002a: call ""int*[*,*].Set"" IL_002f: stloc.0 IL_0030: ldloc.0 IL_0031: ldc.i4.0 IL_0032: callvirt ""int System.Array.GetUpperBound(int)"" IL_0037: stloc.1 IL_0038: ldloc.0 IL_0039: ldc.i4.1 IL_003a: callvirt ""int System.Array.GetUpperBound(int)"" IL_003f: stloc.2 IL_0040: ldloc.0 IL_0041: ldc.i4.0 IL_0042: callvirt ""int System.Array.GetLowerBound(int)"" IL_0047: stloc.3 IL_0048: br.s IL_0073 IL_004a: ldloc.0 IL_004b: ldc.i4.1 IL_004c: callvirt ""int System.Array.GetLowerBound(int)"" IL_0051: stloc.s V_4 IL_0053: br.s IL_006a IL_0055: ldloc.0 IL_0056: ldloc.3 IL_0057: ldloc.s V_4 IL_0059: call ""int*[*,*].Get"" IL_005e: conv.i4 IL_005f: call ""void System.Console.Write(int)"" IL_0064: ldloc.s V_4 IL_0066: ldc.i4.1 IL_0067: add IL_0068: stloc.s V_4 IL_006a: ldloc.s V_4 IL_006c: ldloc.2 IL_006d: ble.s IL_0055 IL_006f: ldloc.3 IL_0070: ldc.i4.1 IL_0071: add IL_0072: stloc.3 IL_0073: ldloc.3 IL_0074: ldloc.1 IL_0075: ble.s IL_004a IL_0077: ret } "); } [ConditionalFact(typeof(DesktopOnly))] public void PointerArrayForeachEnumerable() { var text = @" using System; using System.Collections; unsafe class C { static void Main() { System.Globalization.CultureInfo saveUICulture = System.Threading.Thread.CurrentThread.CurrentUICulture; System.Threading.Thread.CurrentThread.CurrentUICulture = System.Globalization.CultureInfo.InvariantCulture; try { int*[] array = new [] { (int*)1, (int*)2, }; foreach (var element in (IEnumerable)array) { Console.Write((int)element); } } finally { System.Threading.Thread.CurrentThread.CurrentUICulture = saveUICulture; } } } "; CompileAndVerifyException<NotSupportedException>(text, "Type is not supported.", allowUnsafe: true, verify: Verification.Fails); } #endregion Pointer conversion tests #region sizeof tests [Fact] public void SizeOfConstant() { var text = @" using System; class C { static void Main() { Console.WriteLine(sizeof(sbyte)); Console.WriteLine(sizeof(byte)); Console.WriteLine(sizeof(short)); Console.WriteLine(sizeof(ushort)); Console.WriteLine(sizeof(int)); Console.WriteLine(sizeof(uint)); Console.WriteLine(sizeof(long)); Console.WriteLine(sizeof(ulong)); Console.WriteLine(sizeof(char)); Console.WriteLine(sizeof(float)); Console.WriteLine(sizeof(double)); Console.WriteLine(sizeof(bool)); Console.WriteLine(sizeof(decimal)); //Supported by dev10, but not spec. } } "; var expectedOutput = @" 1 1 2 2 4 4 8 8 2 4 8 1 16 ".Trim(); CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: expectedOutput, verify: Verification.Passes).VerifyIL("C.Main", @" { // Code size 80 (0x50) .maxstack 1 IL_0000: ldc.i4.1 IL_0001: call ""void System.Console.WriteLine(int)"" IL_0006: ldc.i4.1 IL_0007: call ""void System.Console.WriteLine(int)"" IL_000c: ldc.i4.2 IL_000d: call ""void System.Console.WriteLine(int)"" IL_0012: ldc.i4.2 IL_0013: call ""void System.Console.WriteLine(int)"" IL_0018: ldc.i4.4 IL_0019: call ""void System.Console.WriteLine(int)"" IL_001e: ldc.i4.4 IL_001f: call ""void System.Console.WriteLine(int)"" IL_0024: ldc.i4.8 IL_0025: call ""void System.Console.WriteLine(int)"" IL_002a: ldc.i4.8 IL_002b: call ""void System.Console.WriteLine(int)"" IL_0030: ldc.i4.2 IL_0031: call ""void System.Console.WriteLine(int)"" IL_0036: ldc.i4.4 IL_0037: call ""void System.Console.WriteLine(int)"" IL_003c: ldc.i4.8 IL_003d: call ""void System.Console.WriteLine(int)"" IL_0042: ldc.i4.1 IL_0043: call ""void System.Console.WriteLine(int)"" IL_0048: ldc.i4.s 16 IL_004a: call ""void System.Console.WriteLine(int)"" IL_004f: ret } "); } [Fact] public void SizeOfNonConstant() { var text = @" using System; unsafe class C { static void Main() { Console.WriteLine(sizeof(S)); Console.WriteLine(sizeof(Outer.Inner)); Console.WriteLine(sizeof(int*)); Console.WriteLine(sizeof(void*)); } } struct S { public byte b; } class Outer { public struct Inner { public char c; } } "; bool isx86 = (IntPtr.Size == 4); string expectedOutput; if (isx86) { expectedOutput = @" 1 2 4 4 ".Trim(); } else { expectedOutput = @" 1 2 8 8 ".Trim(); } CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: expectedOutput, verify: Verification.Passes).VerifyIL("C.Main", @" { // Code size 45 (0x2d) .maxstack 1 IL_0000: sizeof ""S"" IL_0006: call ""void System.Console.WriteLine(int)"" IL_000b: sizeof ""Outer.Inner"" IL_0011: call ""void System.Console.WriteLine(int)"" IL_0016: sizeof ""int*"" IL_001c: call ""void System.Console.WriteLine(int)"" IL_0021: sizeof ""void*"" IL_0027: call ""void System.Console.WriteLine(int)"" IL_002c: ret } "); } [Fact] public void SizeOfEnum() { var text = @" using System; unsafe class C { static void Main() { Console.WriteLine(sizeof(E1)); Console.WriteLine(sizeof(E2)); Console.WriteLine(sizeof(E3)); } } enum E1 { A } enum E2 : byte { A } enum E3 : long { A } "; var expectedOutput = @" 4 1 8 ".Trim(); CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: expectedOutput, verify: Verification.Passes).VerifyIL("C.Main", @" { // Code size 19 (0x13) .maxstack 1 IL_0000: ldc.i4.4 IL_0001: call ""void System.Console.WriteLine(int)"" IL_0006: ldc.i4.1 IL_0007: call ""void System.Console.WriteLine(int)"" IL_000c: ldc.i4.8 IL_000d: call ""void System.Console.WriteLine(int)"" IL_0012: ret } "); } #endregion sizeof tests #region Pointer arithmetic tests [Fact] public void NumericAdditionChecked() { var text = @" using System; unsafe struct S { static void Main() { checked { S s = new S(); S* p = &s; p = p + 2; p = p + 3u; p = p + 4l; p = p + 5ul; } } } "; // Dev10 has conv.u after IL_000d and conv.i8 in place of conv.u8 at IL_0017. CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails).VerifyIL("S.Main", @" { // Code size 59 (0x3b) .maxstack 3 .locals init (S V_0) //s IL_0000: ldloca.s V_0 IL_0002: initobj ""S"" IL_0008: ldloca.s V_0 IL_000a: conv.u IL_000b: ldc.i4.2 IL_000c: conv.i IL_000d: sizeof ""S"" IL_0013: mul.ovf IL_0014: add.ovf.un IL_0015: ldc.i4.3 IL_0016: conv.u8 IL_0017: sizeof ""S"" IL_001d: conv.i8 IL_001e: mul.ovf IL_001f: conv.i IL_0020: add.ovf.un IL_0021: ldc.i4.4 IL_0022: conv.i8 IL_0023: sizeof ""S"" IL_0029: conv.i8 IL_002a: mul.ovf IL_002b: conv.i IL_002c: add.ovf.un IL_002d: ldc.i4.5 IL_002e: conv.i8 IL_002f: sizeof ""S"" IL_0035: conv.ovf.u8 IL_0036: mul.ovf.un IL_0037: conv.u IL_0038: add.ovf.un IL_0039: pop IL_003a: ret } "); } [Fact] public void NumericAdditionUnchecked() { var text = @" using System; unsafe struct S { static void Main() { unchecked { S s = new S(); S* p = &s; p = p + 2; p = p + 3u; p = p + 4l; p = p + 5ul; } } } "; // Dev10 has conv.u after IL_000d and conv.i8 in place of conv.u8 at IL_0017. CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails).VerifyIL("S.Main", @" { // Code size 59 (0x3b) .maxstack 3 .locals init (S V_0) //s IL_0000: ldloca.s V_0 IL_0002: initobj ""S"" IL_0008: ldloca.s V_0 IL_000a: conv.u IL_000b: ldc.i4.2 IL_000c: conv.i IL_000d: sizeof ""S"" IL_0013: mul IL_0014: add IL_0015: ldc.i4.3 IL_0016: conv.u8 IL_0017: sizeof ""S"" IL_001d: conv.i8 IL_001e: mul IL_001f: conv.i IL_0020: add IL_0021: ldc.i4.4 IL_0022: conv.i8 IL_0023: sizeof ""S"" IL_0029: conv.i8 IL_002a: mul IL_002b: conv.i IL_002c: add IL_002d: pop IL_002e: ldc.i4.5 IL_002f: conv.i8 IL_0030: sizeof ""S"" IL_0036: conv.i8 IL_0037: mul IL_0038: conv.u IL_0039: pop IL_003a: ret } "); } [Fact] public void NumericSubtractionChecked() { var text = @" using System; unsafe struct S { static void Main() { checked { S s = new S(); S* p = &s; p = p - 2; p = p - 3u; p = p - 4l; p = p - 5ul; } } } "; // Dev10 has conv.u after IL_000d and conv.i8 in place of conv.u8 at IL_0017. CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails).VerifyIL("S.Main", @" { // Code size 59 (0x3b) .maxstack 3 .locals init (S V_0) //s IL_0000: ldloca.s V_0 IL_0002: initobj ""S"" IL_0008: ldloca.s V_0 IL_000a: conv.u IL_000b: ldc.i4.2 IL_000c: conv.i IL_000d: sizeof ""S"" IL_0013: mul.ovf IL_0014: sub.ovf.un IL_0015: ldc.i4.3 IL_0016: conv.u8 IL_0017: sizeof ""S"" IL_001d: conv.i8 IL_001e: mul.ovf IL_001f: conv.i IL_0020: sub.ovf.un IL_0021: ldc.i4.4 IL_0022: conv.i8 IL_0023: sizeof ""S"" IL_0029: conv.i8 IL_002a: mul.ovf IL_002b: conv.i IL_002c: sub.ovf.un IL_002d: ldc.i4.5 IL_002e: conv.i8 IL_002f: sizeof ""S"" IL_0035: conv.ovf.u8 IL_0036: mul.ovf.un IL_0037: conv.u IL_0038: sub.ovf.un IL_0039: pop IL_003a: ret } "); } [Fact] public void NumericSubtractionUnchecked() { var text = @" using System; unsafe struct S { static void Main() { unchecked { S s = new S(); S* p = &s; p = p - 2; p = p - 3u; p = p - 4l; p = p - 5ul; } } } "; // Dev10 has conv.u after IL_000d and conv.i8 in place of conv.u8 at IL_0017. CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails).VerifyIL("S.Main", @" { // Code size 59 (0x3b) .maxstack 3 .locals init (S V_0) //s IL_0000: ldloca.s V_0 IL_0002: initobj ""S"" IL_0008: ldloca.s V_0 IL_000a: conv.u IL_000b: ldc.i4.2 IL_000c: conv.i IL_000d: sizeof ""S"" IL_0013: mul IL_0014: sub IL_0015: ldc.i4.3 IL_0016: conv.u8 IL_0017: sizeof ""S"" IL_001d: conv.i8 IL_001e: mul IL_001f: conv.i IL_0020: sub IL_0021: ldc.i4.4 IL_0022: conv.i8 IL_0023: sizeof ""S"" IL_0029: conv.i8 IL_002a: mul IL_002b: conv.i IL_002c: sub IL_002d: pop IL_002e: ldc.i4.5 IL_002f: conv.i8 IL_0030: sizeof ""S"" IL_0036: conv.i8 IL_0037: mul IL_0038: conv.u IL_0039: pop IL_003a: ret } "); } [WorkItem(546750, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546750")] [Fact] public void NumericAdditionUnchecked_SizeOne() { var text = @" using System; unsafe class C { void Test(int i, uint u, long l, ulong ul) { unchecked { byte b = 3; byte* p = &b; p = p + 2; p = p + 3u; p = p + 4l; p = p + 5ul; p = p + i; p = p + u; p = p + l; p = p + ul; } } } "; // NOTE: even when not optimized. // NOTE: additional conversions applied to constants of type int and uint. CompileAndVerify(text, options: TestOptions.UnsafeDebugDll, verify: Verification.Fails).VerifyIL("C.Test", @" { // Code size 50 (0x32) .maxstack 2 .locals init (byte V_0, //b byte* V_1) //p IL_0000: nop IL_0001: nop IL_0002: ldc.i4.3 IL_0003: stloc.0 IL_0004: ldloca.s V_0 IL_0006: conv.u IL_0007: stloc.1 IL_0008: ldloc.1 IL_0009: ldc.i4.2 IL_000a: add IL_000b: stloc.1 IL_000c: ldloc.1 IL_000d: ldc.i4.3 IL_000e: add IL_000f: stloc.1 IL_0010: ldloc.1 IL_0011: ldc.i4.4 IL_0012: conv.i8 IL_0013: conv.i IL_0014: add IL_0015: stloc.1 IL_0016: ldloc.1 IL_0017: ldc.i4.5 IL_0018: conv.i8 IL_0019: conv.u IL_001a: add IL_001b: stloc.1 IL_001c: ldloc.1 IL_001d: ldarg.1 IL_001e: add IL_001f: stloc.1 IL_0020: ldloc.1 IL_0021: ldarg.2 IL_0022: conv.u IL_0023: add IL_0024: stloc.1 IL_0025: ldloc.1 IL_0026: ldarg.3 IL_0027: conv.i IL_0028: add IL_0029: stloc.1 IL_002a: ldloc.1 IL_002b: ldarg.s V_4 IL_002d: conv.u IL_002e: add IL_002f: stloc.1 IL_0030: nop IL_0031: ret } "); } [WorkItem(18871, "https://github.com/dotnet/roslyn/issues/18871")] [WorkItem(546750, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546750")] [Fact] public void NumericAdditionChecked_SizeOne() { var text = @" using System; unsafe class C { void Test(int i, uint u, long l, ulong ul) { checked { byte b = 3; byte* p = &b; p = p + 2; p = p + 3u; p = p + 4l; p = p + 5ul; p = p + i; p = p + u; p = p + l; p = p + ul; p = p + (-2); } } void Test1(int i, uint u, long l, ulong ul) { checked { byte b = 3; byte* p = &b; p = p - 2; p = p - 3u; p = p - 4l; p = p - 5ul; p = p - i; p = p - u; p = p - l; p = p - ul; p = p - (-1); } } } "; // NOTE: even when not optimized. // NOTE: additional conversions applied to constants of type int and uint. // NOTE: identical to unchecked except "add" becomes "add.ovf.un". var comp = CompileAndVerify(text, options: TestOptions.UnsafeDebugDll, verify: Verification.Fails); comp.VerifyIL("C.Test", @" { // Code size 57 (0x39) .maxstack 2 .locals init (byte V_0, //b byte* V_1) //p IL_0000: nop IL_0001: nop IL_0002: ldc.i4.3 IL_0003: stloc.0 IL_0004: ldloca.s V_0 IL_0006: conv.u IL_0007: stloc.1 IL_0008: ldloc.1 IL_0009: ldc.i4.2 IL_000a: add.ovf.un IL_000b: stloc.1 IL_000c: ldloc.1 IL_000d: ldc.i4.3 IL_000e: add.ovf.un IL_000f: stloc.1 IL_0010: ldloc.1 IL_0011: ldc.i4.4 IL_0012: conv.i8 IL_0013: conv.i IL_0014: add.ovf.un IL_0015: stloc.1 IL_0016: ldloc.1 IL_0017: ldc.i4.5 IL_0018: conv.i8 IL_0019: conv.u IL_001a: add.ovf.un IL_001b: stloc.1 IL_001c: ldloc.1 IL_001d: ldarg.1 IL_001e: conv.i IL_001f: add.ovf.un IL_0020: stloc.1 IL_0021: ldloc.1 IL_0022: ldarg.2 IL_0023: conv.u IL_0024: add.ovf.un IL_0025: stloc.1 IL_0026: ldloc.1 IL_0027: ldarg.3 IL_0028: conv.i IL_0029: add.ovf.un IL_002a: stloc.1 IL_002b: ldloc.1 IL_002c: ldarg.s V_4 IL_002e: conv.u IL_002f: add.ovf.un IL_0030: stloc.1 IL_0031: ldloc.1 IL_0032: ldc.i4.s -2 IL_0034: conv.i IL_0035: add.ovf.un IL_0036: stloc.1 IL_0037: nop IL_0038: ret }"); comp.VerifyIL("C.Test1", @" { // Code size 56 (0x38) .maxstack 2 .locals init (byte V_0, //b byte* V_1) //p IL_0000: nop IL_0001: nop IL_0002: ldc.i4.3 IL_0003: stloc.0 IL_0004: ldloca.s V_0 IL_0006: conv.u IL_0007: stloc.1 IL_0008: ldloc.1 IL_0009: ldc.i4.2 IL_000a: sub.ovf.un IL_000b: stloc.1 IL_000c: ldloc.1 IL_000d: ldc.i4.3 IL_000e: sub.ovf.un IL_000f: stloc.1 IL_0010: ldloc.1 IL_0011: ldc.i4.4 IL_0012: conv.i8 IL_0013: conv.i IL_0014: sub.ovf.un IL_0015: stloc.1 IL_0016: ldloc.1 IL_0017: ldc.i4.5 IL_0018: conv.i8 IL_0019: conv.u IL_001a: sub.ovf.un IL_001b: stloc.1 IL_001c: ldloc.1 IL_001d: ldarg.1 IL_001e: conv.i IL_001f: sub.ovf.un IL_0020: stloc.1 IL_0021: ldloc.1 IL_0022: ldarg.2 IL_0023: conv.u IL_0024: sub.ovf.un IL_0025: stloc.1 IL_0026: ldloc.1 IL_0027: ldarg.3 IL_0028: conv.i IL_0029: sub.ovf.un IL_002a: stloc.1 IL_002b: ldloc.1 IL_002c: ldarg.s V_4 IL_002e: conv.u IL_002f: sub.ovf.un IL_0030: stloc.1 IL_0031: ldloc.1 IL_0032: ldc.i4.m1 IL_0033: conv.i IL_0034: sub.ovf.un IL_0035: stloc.1 IL_0036: nop IL_0037: ret }"); } [Fact] public void CheckedSignExtend() { var text = @" using System; unsafe struct S { static void Main() { byte* ptr1 = default(byte*); ptr1 = (byte*)2; checked { // should not overflow regardless of 32/64 bit ptr1 = ptr1 + 2147483649; } Console.WriteLine((long)ptr1); byte* ptr = (byte*)2; try { checked { int i = -1; // should overflow regardless of 32/64 bit ptr = ptr + i; } Console.WriteLine((long)ptr); } catch (OverflowException) { Console.WriteLine(""overflow""); Console.WriteLine((long)ptr); } } } "; CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: @"2147483651 overflow 2", verify: Verification.Fails).VerifyIL("S.Main", @" { // Code size 67 (0x43) .maxstack 2 .locals init (byte* V_0, //ptr1 byte* V_1, //ptr int V_2) //i IL_0000: ldloca.s V_0 IL_0002: initobj ""byte*"" IL_0008: ldc.i4.2 IL_0009: conv.i IL_000a: stloc.0 IL_000b: ldloc.0 IL_000c: ldc.i4 0x80000001 IL_0011: conv.u IL_0012: add.ovf.un IL_0013: stloc.0 IL_0014: ldloc.0 IL_0015: conv.u8 IL_0016: call ""void System.Console.WriteLine(long)"" IL_001b: ldc.i4.2 IL_001c: conv.i IL_001d: stloc.1 .try { IL_001e: ldc.i4.m1 IL_001f: stloc.2 IL_0020: ldloc.1 IL_0021: ldloc.2 IL_0022: conv.i IL_0023: add.ovf.un IL_0024: stloc.1 IL_0025: ldloc.1 IL_0026: conv.u8 IL_0027: call ""void System.Console.WriteLine(long)"" IL_002c: leave.s IL_0042 } catch System.OverflowException { IL_002e: pop IL_002f: ldstr ""overflow"" IL_0034: call ""void System.Console.WriteLine(string)"" IL_0039: ldloc.1 IL_003a: conv.u8 IL_003b: call ""void System.Console.WriteLine(long)"" IL_0040: leave.s IL_0042 } IL_0042: ret } "); } [Fact] public void Increment() { var text = @" using System; unsafe struct S { static void Main() { S* p = (S*)0; checked { p++; } checked { ++p; } unchecked { p++; } unchecked { ++p; } Console.WriteLine((int)p); } } "; CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: "4", verify: Verification.Fails).VerifyIL("S.Main", @" { // Code size 47 (0x2f) .maxstack 2 .locals init (S* V_0) //p IL_0000: ldc.i4.0 IL_0001: conv.i IL_0002: stloc.0 IL_0003: ldloc.0 IL_0004: sizeof ""S"" IL_000a: add.ovf.un IL_000b: stloc.0 IL_000c: ldloc.0 IL_000d: sizeof ""S"" IL_0013: add.ovf.un IL_0014: stloc.0 IL_0015: ldloc.0 IL_0016: sizeof ""S"" IL_001c: add IL_001d: stloc.0 IL_001e: ldloc.0 IL_001f: sizeof ""S"" IL_0025: add IL_0026: stloc.0 IL_0027: ldloc.0 IL_0028: conv.i4 IL_0029: call ""void System.Console.WriteLine(int)"" IL_002e: ret } "); } [Fact] public void Decrement() { var text = @" using System; unsafe struct S { static void Main() { S* p = (S*)8; checked { p--; } checked { --p; } unchecked { p--; } unchecked { --p; } Console.WriteLine((int)p); } } "; CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: "4", verify: Verification.Fails).VerifyIL("S.Main", @" { // Code size 47 (0x2f) .maxstack 2 .locals init (S* V_0) //p IL_0000: ldc.i4.8 IL_0001: conv.i IL_0002: stloc.0 IL_0003: ldloc.0 IL_0004: sizeof ""S"" IL_000a: sub.ovf.un IL_000b: stloc.0 IL_000c: ldloc.0 IL_000d: sizeof ""S"" IL_0013: sub.ovf.un IL_0014: stloc.0 IL_0015: ldloc.0 IL_0016: sizeof ""S"" IL_001c: sub IL_001d: stloc.0 IL_001e: ldloc.0 IL_001f: sizeof ""S"" IL_0025: sub IL_0026: stloc.0 IL_0027: ldloc.0 IL_0028: conv.i4 IL_0029: call ""void System.Console.WriteLine(int)"" IL_002e: ret } "); } [Fact] public void IncrementProperty() { var text = @" using System; unsafe struct S { S* P { get; set; } S* this[int x] { get { return P; } set { P = value; } } static void Main() { S s = new S(); s.P++; --s[GetIndex()]; Console.Write((int)s.P); } static int GetIndex() { Console.Write(""I""); return 1; } } "; CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: "I0", verify: Verification.Fails).VerifyIL("S.Main", @" { // Code size 74 (0x4a) .maxstack 3 .locals init (S V_0, //s S* V_1, int V_2) IL_0000: ldloca.s V_0 IL_0002: initobj ""S"" IL_0008: ldloca.s V_0 IL_000a: dup IL_000b: call ""readonly S* S.P.get"" IL_0010: stloc.1 IL_0011: ldloc.1 IL_0012: sizeof ""S"" IL_0018: add IL_0019: call ""void S.P.set"" IL_001e: ldloca.s V_0 IL_0020: call ""int S.GetIndex()"" IL_0025: stloc.2 IL_0026: dup IL_0027: ldloc.2 IL_0028: call ""S* S.this[int].get"" IL_002d: sizeof ""S"" IL_0033: sub IL_0034: stloc.1 IL_0035: ldloc.2 IL_0036: ldloc.1 IL_0037: call ""void S.this[int].set"" IL_003c: ldloca.s V_0 IL_003e: call ""readonly S* S.P.get"" IL_0043: conv.i4 IL_0044: call ""void System.Console.Write(int)"" IL_0049: ret } "); } [Fact] public void CompoundAssignment() { var text = @" using System; unsafe struct S { static void Main() { S* p = (S*)8; checked { p += 1; p += 2U; p -= 1L; p -= 2UL; } unchecked { p += 1; p += 2U; p -= 1L; p -= 2UL; } Console.WriteLine((int)p); } } "; CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: "8", verify: Verification.Fails).VerifyIL("S.Main", @" { // Code size 103 (0x67) .maxstack 3 .locals init (S* V_0) //p IL_0000: ldc.i4.8 IL_0001: conv.i IL_0002: stloc.0 IL_0003: ldloc.0 IL_0004: sizeof ""S"" IL_000a: add.ovf.un IL_000b: stloc.0 IL_000c: ldloc.0 IL_000d: ldc.i4.2 IL_000e: conv.u8 IL_000f: sizeof ""S"" IL_0015: conv.i8 IL_0016: mul.ovf IL_0017: conv.i IL_0018: add.ovf.un IL_0019: stloc.0 IL_001a: ldloc.0 IL_001b: sizeof ""S"" IL_0021: sub.ovf.un IL_0022: stloc.0 IL_0023: ldloc.0 IL_0024: ldc.i4.2 IL_0025: conv.i8 IL_0026: sizeof ""S"" IL_002c: conv.ovf.u8 IL_002d: mul.ovf.un IL_002e: conv.u IL_002f: sub.ovf.un IL_0030: stloc.0 IL_0031: ldloc.0 IL_0032: sizeof ""S"" IL_0038: add IL_0039: stloc.0 IL_003a: ldloc.0 IL_003b: ldc.i4.2 IL_003c: conv.u8 IL_003d: sizeof ""S"" IL_0043: conv.i8 IL_0044: mul IL_0045: conv.i IL_0046: add IL_0047: stloc.0 IL_0048: ldloc.0 IL_0049: sizeof ""S"" IL_004f: sub IL_0050: stloc.0 IL_0051: ldloc.0 IL_0052: ldc.i4.2 IL_0053: conv.i8 IL_0054: sizeof ""S"" IL_005a: conv.i8 IL_005b: mul IL_005c: conv.u IL_005d: sub IL_005e: stloc.0 IL_005f: ldloc.0 IL_0060: conv.i4 IL_0061: call ""void System.Console.WriteLine(int)"" IL_0066: ret } "); } [Fact] public void CompoundAssignProperty() { var text = @" using System; unsafe struct S { S* P { get; set; } S* this[int x] { get { return P; } set { P = value; } } static void Main() { S s = new S(); s.P += 3; s[GetIndex()] -= 2; Console.Write((int)s.P); } static int GetIndex() { Console.Write(""I""); return 1; } } "; bool isx86 = (IntPtr.Size == 4); string expectedOutput; if (isx86) { expectedOutput = @"I4"; } else { expectedOutput = @"I8"; } CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: expectedOutput, verify: Verification.Fails).VerifyIL("S.Main", @" { // Code size 78 (0x4e) .maxstack 5 .locals init (S V_0, //s S& V_1, int V_2) IL_0000: ldloca.s V_0 IL_0002: initobj ""S"" IL_0008: ldloca.s V_0 IL_000a: dup IL_000b: call ""readonly S* S.P.get"" IL_0010: ldc.i4.3 IL_0011: conv.i IL_0012: sizeof ""S"" IL_0018: mul IL_0019: add IL_001a: call ""void S.P.set"" IL_001f: ldloca.s V_0 IL_0021: stloc.1 IL_0022: call ""int S.GetIndex()"" IL_0027: stloc.2 IL_0028: ldloc.1 IL_0029: ldloc.2 IL_002a: ldloc.1 IL_002b: ldloc.2 IL_002c: call ""S* S.this[int].get"" IL_0031: ldc.i4.2 IL_0032: conv.i IL_0033: sizeof ""S"" IL_0039: mul IL_003a: sub IL_003b: call ""void S.this[int].set"" IL_0040: ldloca.s V_0 IL_0042: call ""readonly S* S.P.get"" IL_0047: conv.i4 IL_0048: call ""void System.Console.Write(int)"" IL_004d: ret } "); } [Fact] public void PointerSubtraction_EmptyStruct() { var text = @" using System; unsafe struct S { static void Main() { S* p = (S*)8; S* q = (S*)4; checked { Console.Write(p - q); } unchecked { Console.Write(p - q); } } } "; // NOTE: don't use checked subtraction or division in either case (matches dev10). CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: "44", verify: Verification.Fails).VerifyIL("S.Main", @" { // Code size 39 (0x27) .maxstack 2 .locals init (S* V_0, //p S* V_1) //q IL_0000: ldc.i4.8 IL_0001: conv.i IL_0002: stloc.0 IL_0003: ldc.i4.4 IL_0004: conv.i IL_0005: stloc.1 IL_0006: ldloc.0 IL_0007: ldloc.1 IL_0008: sub IL_0009: sizeof ""S"" IL_000f: div IL_0010: conv.i8 IL_0011: call ""void System.Console.Write(long)"" IL_0016: ldloc.0 IL_0017: ldloc.1 IL_0018: sub IL_0019: sizeof ""S"" IL_001f: div IL_0020: conv.i8 IL_0021: call ""void System.Console.Write(long)"" IL_0026: ret } "); } [Fact] public void PointerSubtraction_NonEmptyStruct() { var text = @" using System; unsafe struct S { int x; //non-empty struct static void Main() { S* p = (S*)8; S* q = (S*)4; checked { Console.Write(p - q); } unchecked { Console.Write(p - q); } } } "; // NOTE: don't use checked subtraction or division in either case (matches dev10). CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: "11", verify: Verification.Fails).VerifyIL("S.Main", @" { // Code size 39 (0x27) .maxstack 2 .locals init (S* V_0, //p S* V_1) //q IL_0000: ldc.i4.8 IL_0001: conv.i IL_0002: stloc.0 IL_0003: ldc.i4.4 IL_0004: conv.i IL_0005: stloc.1 IL_0006: ldloc.0 IL_0007: ldloc.1 IL_0008: sub IL_0009: sizeof ""S"" IL_000f: div IL_0010: conv.i8 IL_0011: call ""void System.Console.Write(long)"" IL_0016: ldloc.0 IL_0017: ldloc.1 IL_0018: sub IL_0019: sizeof ""S"" IL_001f: div IL_0020: conv.i8 IL_0021: call ""void System.Console.Write(long)"" IL_0026: ret } "); } [Fact] public void PointerSubtraction_ConstantSize() { var text = @" using System; unsafe struct S { static void Main() { int* p = (int*)8; //size is known at compile-time int* q = (int*)4; checked { Console.Write(p - q); } unchecked { Console.Write(p - q); } } } "; // NOTE: don't use checked subtraction or division in either case (matches dev10). CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: "11", verify: Verification.Fails).VerifyIL("S.Main", @" { // Code size 29 (0x1d) .maxstack 2 .locals init (int* V_0, //p int* V_1) //q IL_0000: ldc.i4.8 IL_0001: conv.i IL_0002: stloc.0 IL_0003: ldc.i4.4 IL_0004: conv.i IL_0005: stloc.1 IL_0006: ldloc.0 IL_0007: ldloc.1 IL_0008: sub IL_0009: ldc.i4.4 IL_000a: div IL_000b: conv.i8 IL_000c: call ""void System.Console.Write(long)"" IL_0011: ldloc.0 IL_0012: ldloc.1 IL_0013: sub IL_0014: ldc.i4.4 IL_0015: div IL_0016: conv.i8 IL_0017: call ""void System.Console.Write(long)"" IL_001c: ret } "); } [Fact] public void PointerSubtraction_IntegerDivision() { var text = @" using System; unsafe struct S { int x; //size = 4 static void Main() { S* p1 = (S*)7; //size is known at compile-time S* p2 = (S*)9; //size is known at compile-time S* q = (S*)4; checked { Console.Write(p1 - q); } unchecked { Console.Write(p2 - q); } } } "; // NOTE: don't use checked subtraction or division in either case (matches dev10). CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: "01", verify: Verification.Fails).VerifyIL("S.Main", @" { // Code size 43 (0x2b) .maxstack 2 .locals init (S* V_0, //p1 S* V_1, //p2 S* V_2) //q IL_0000: ldc.i4.7 IL_0001: conv.i IL_0002: stloc.0 IL_0003: ldc.i4.s 9 IL_0005: conv.i IL_0006: stloc.1 IL_0007: ldc.i4.4 IL_0008: conv.i IL_0009: stloc.2 IL_000a: ldloc.0 IL_000b: ldloc.2 IL_000c: sub IL_000d: sizeof ""S"" IL_0013: div IL_0014: conv.i8 IL_0015: call ""void System.Console.Write(long)"" IL_001a: ldloc.1 IL_001b: ldloc.2 IL_001c: sub IL_001d: sizeof ""S"" IL_0023: div IL_0024: conv.i8 IL_0025: call ""void System.Console.Write(long)"" IL_002a: ret } "); } [WorkItem(544155, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544155")] [Fact] public void SubtractPointerTypes() { var text = @" using System; class PointerArithmetic { static unsafe void Main() { short ia1 = 10; short* ptr = &ia1; short* newPtr; newPtr = ptr - 2; Console.WriteLine((int)(ptr - newPtr)); } } "; CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: "2", verify: Verification.Fails); } #endregion Pointer arithmetic tests #region Checked pointer arithmetic overflow tests // 0 - operation name (e.g. "Add") // 1 - pointed at type name (e.g. "S") // 2 - operator (e.g. "+") // 3 - checked/unchecked private const string CheckedNumericHelperTemplate = @" unsafe static class Helper {{ public static void {0}Int({1}* p, int num, string description) {{ {3} {{ try {{ p = p {2} num; Console.WriteLine(""{0}Int: No exception at {{0}} (value = {{1}})"", description, (ulong)p); }} catch (OverflowException) {{ Console.WriteLine(""{0}Int: Exception at {{0}}"", description); }} }} }} public static void {0}UInt({1}* p, uint num, string description) {{ {3} {{ try {{ p = p {2} num; Console.WriteLine(""{0}UInt: No exception at {{0}} (value = {{1}})"", description, (ulong)p); }} catch (OverflowException) {{ Console.WriteLine(""{0}UInt: Exception at {{0}}"", description); }} }} }} public static void {0}Long({1}* p, long num, string description) {{ {3} {{ try {{ p = p {2} num; Console.WriteLine(""{0}Long: No exception at {{0}} (value = {{1}})"", description, (ulong)p); }} catch (OverflowException) {{ Console.WriteLine(""{0}Long: Exception at {{0}}"", description); }} }} }} public static void {0}ULong({1}* p, ulong num, string description) {{ {3} {{ try {{ p = p {2} num; Console.WriteLine(""{0}ULong: No exception at {{0}} (value = {{1}})"", description, (ulong)p); }} catch (OverflowException) {{ Console.WriteLine(""{0}ULong: Exception at {{0}}"", description); }} }} }} }} "; private const string SizedStructs = @" //sizeof SXX is 2 ^ XX struct S00 { } struct S01 { S00 a, b; } struct S02 { S01 a, b; } struct S03 { S02 a, b; } struct S04 { S03 a, b; } struct S05 { S04 a, b; } struct S06 { S05 a, b; } struct S07 { S06 a, b; } struct S08 { S07 a, b; } struct S09 { S08 a, b; } struct S10 { S09 a, b; } struct S11 { S10 a, b; } struct S12 { S11 a, b; } struct S13 { S12 a, b; } struct S14 { S13 a, b; } struct S15 { S14 a, b; } struct S16 { S15 a, b; } struct S17 { S16 a, b; } struct S18 { S17 a, b; } struct S19 { S18 a, b; } struct S20 { S19 a, b; } struct S21 { S20 a, b; } struct S22 { S21 a, b; } struct S23 { S22 a, b; } struct S24 { S23 a, b; } struct S25 { S24 a, b; } struct S26 { S25 a, b; } struct S27 { S26 a, b; } //struct S28 { S27 a, b; } //Can't load type //struct S29 { S28 a, b; } //Can't load type //struct S30 { S29 a, b; } //Can't load type //struct S31 { S30 a, b; } //Can't load type "; // 0 - pointed-at type private const string PositiveNumericAdditionCasesTemplate = @" Helper.AddInt(({0}*)0, int.MaxValue, ""0 + int.MaxValue""); Helper.AddInt(({0}*)1, int.MaxValue, ""1 + int.MaxValue""); Helper.AddInt(({0}*)int.MaxValue, 0, ""int.MaxValue + 0""); Helper.AddInt(({0}*)int.MaxValue, 1, ""int.MaxValue + 1""); //Helper.AddInt(({0}*)0, uint.MaxValue, ""0 + uint.MaxValue""); //Helper.AddInt(({0}*)1, uint.MaxValue, ""1 + uint.MaxValue""); Helper.AddInt(({0}*)uint.MaxValue, 0, ""uint.MaxValue + 0""); Helper.AddInt(({0}*)uint.MaxValue, 1, ""uint.MaxValue + 1""); //Helper.AddInt(({0}*)0, long.MaxValue, ""0 + long.MaxValue""); //Helper.AddInt(({0}*)1, long.MaxValue, ""1 + long.MaxValue""); //Helper.AddInt(({0}*)long.MaxValue, 0, ""long.MaxValue + 0""); //Helper.AddInt(({0}*)long.MaxValue, 1, ""long.MaxValue + 1""); //Helper.AddInt(({0}*)0, ulong.MaxValue, ""0 + ulong.MaxValue""); //Helper.AddInt(({0}*)1, ulong.MaxValue, ""1 + ulong.MaxValue""); //Helper.AddInt(({0}*)ulong.MaxValue, 0, ""ulong.MaxValue + 0""); //Helper.AddInt(({0}*)ulong.MaxValue, 1, ""ulong.MaxValue + 1""); Console.WriteLine(); Helper.AddUInt(({0}*)0, int.MaxValue, ""0 + int.MaxValue""); Helper.AddUInt(({0}*)1, int.MaxValue, ""1 + int.MaxValue""); Helper.AddUInt(({0}*)int.MaxValue, 0, ""int.MaxValue + 0""); Helper.AddUInt(({0}*)int.MaxValue, 1, ""int.MaxValue + 1""); Helper.AddUInt(({0}*)0, uint.MaxValue, ""0 + uint.MaxValue""); Helper.AddUInt(({0}*)1, uint.MaxValue, ""1 + uint.MaxValue""); Helper.AddUInt(({0}*)uint.MaxValue, 0, ""uint.MaxValue + 0""); Helper.AddUInt(({0}*)uint.MaxValue, 1, ""uint.MaxValue + 1""); //Helper.AddUInt(({0}*)0, long.MaxValue, ""0 + long.MaxValue""); //Helper.AddUInt(({0}*)1, long.MaxValue, ""1 + long.MaxValue""); //Helper.AddUInt(({0}*)long.MaxValue, 0, ""long.MaxValue + 0""); //Helper.AddUInt(({0}*)long.MaxValue, 1, ""long.MaxValue + 1""); //Helper.AddUInt(({0}*)0, ulong.MaxValue, ""0 + ulong.MaxValue""); //Helper.AddUInt(({0}*)1, ulong.MaxValue, ""1 + ulong.MaxValue""); //Helper.AddUInt(({0}*)ulong.MaxValue, 0, ""ulong.MaxValue + 0""); //Helper.AddUInt(({0}*)ulong.MaxValue, 1, ""ulong.MaxValue + 1""); Console.WriteLine(); Helper.AddLong(({0}*)0, int.MaxValue, ""0 + int.MaxValue""); Helper.AddLong(({0}*)1, int.MaxValue, ""1 + int.MaxValue""); Helper.AddLong(({0}*)int.MaxValue, 0, ""int.MaxValue + 0""); Helper.AddLong(({0}*)int.MaxValue, 1, ""int.MaxValue + 1""); Helper.AddLong(({0}*)0, uint.MaxValue, ""0 + uint.MaxValue""); Helper.AddLong(({0}*)1, uint.MaxValue, ""1 + uint.MaxValue""); Helper.AddLong(({0}*)uint.MaxValue, 0, ""uint.MaxValue + 0""); Helper.AddLong(({0}*)uint.MaxValue, 1, ""uint.MaxValue + 1""); Helper.AddLong(({0}*)0, long.MaxValue, ""0 + long.MaxValue""); Helper.AddLong(({0}*)1, long.MaxValue, ""1 + long.MaxValue""); //Helper.AddLong(({0}*)long.MaxValue, 0, ""long.MaxValue + 0""); //Helper.AddLong(({0}*)long.MaxValue, 1, ""long.MaxValue + 1""); //Helper.AddLong(({0}*)0, ulong.MaxValue, ""0 + ulong.MaxValue""); //Helper.AddLong(({0}*)1, ulong.MaxValue, ""1 + ulong.MaxValue""); //Helper.AddLong(({0}*)ulong.MaxValue, 0, ""ulong.MaxValue + 0""); //Helper.AddLong(({0}*)ulong.MaxValue, 1, ""ulong.MaxValue + 1""); Console.WriteLine(); Helper.AddULong(({0}*)0, int.MaxValue, ""0 + int.MaxValue""); Helper.AddULong(({0}*)1, int.MaxValue, ""1 + int.MaxValue""); Helper.AddULong(({0}*)int.MaxValue, 0, ""int.MaxValue + 0""); Helper.AddULong(({0}*)int.MaxValue, 1, ""int.MaxValue + 1""); Helper.AddULong(({0}*)0, uint.MaxValue, ""0 + uint.MaxValue""); Helper.AddULong(({0}*)1, uint.MaxValue, ""1 + uint.MaxValue""); Helper.AddULong(({0}*)uint.MaxValue, 0, ""uint.MaxValue + 0""); Helper.AddULong(({0}*)uint.MaxValue, 1, ""uint.MaxValue + 1""); Helper.AddULong(({0}*)0, long.MaxValue, ""0 + long.MaxValue""); Helper.AddULong(({0}*)1, long.MaxValue, ""1 + long.MaxValue""); //Helper.AddULong(({0}*)long.MaxValue, 0, ""long.MaxValue + 0""); //Helper.AddULong(({0}*)long.MaxValue, 1, ""long.MaxValue + 1""); Helper.AddULong(({0}*)0, ulong.MaxValue, ""0 + ulong.MaxValue""); Helper.AddULong(({0}*)1, ulong.MaxValue, ""1 + ulong.MaxValue""); //Helper.AddULong(({0}*)ulong.MaxValue, 0, ""ulong.MaxValue + 0""); //Helper.AddULong(({0}*)ulong.MaxValue, 1, ""ulong.MaxValue + 1""); "; // 0 - pointed-at type private const string NegativeNumericAdditionCasesTemplate = @" Helper.AddInt(({0}*)0, -1, ""0 + (-1)""); Helper.AddInt(({0}*)0, int.MinValue, ""0 + int.MinValue""); //Helper.AddInt(({0}*)0, long.MinValue, ""0 + long.MinValue""); Console.WriteLine(); Helper.AddLong(({0}*)0, -1, ""0 + (-1)""); Helper.AddLong(({0}*)0, int.MinValue, ""0 + int.MinValue""); Helper.AddLong(({0}*)0, long.MinValue, ""0 + long.MinValue""); "; // 0 - pointed-at type private const string PositiveNumericSubtractionCasesTemplate = @" Helper.SubInt(({0}*)0, 1, ""0 - 1""); Helper.SubInt(({0}*)0, int.MaxValue, ""0 - int.MaxValue""); //Helper.SubInt(({0}*)0, uint.MaxValue, ""0 - uint.MaxValue""); //Helper.SubInt(({0}*)0, long.MaxValue, ""0 - long.MaxValue""); //Helper.SubInt(({0}*)0, ulong.MaxValue, ""0 - ulong.MaxValue""); Console.WriteLine(); Helper.SubUInt(({0}*)0, 1, ""0 - 1""); Helper.SubUInt(({0}*)0, int.MaxValue, ""0 - int.MaxValue""); Helper.SubUInt(({0}*)0, uint.MaxValue, ""0 - uint.MaxValue""); //Helper.SubUInt(({0}*)0, long.MaxValue, ""0 - long.MaxValue""); //Helper.SubUInt(({0}*)0, ulong.MaxValue, ""0 - ulong.MaxValue""); Console.WriteLine(); Helper.SubLong(({0}*)0, 1, ""0 - 1""); Helper.SubLong(({0}*)0, int.MaxValue, ""0 - int.MaxValue""); Helper.SubLong(({0}*)0, uint.MaxValue, ""0 - uint.MaxValue""); Helper.SubLong(({0}*)0, long.MaxValue, ""0 - long.MaxValue""); //Helper.SubLong(({0}*)0, ulong.MaxValue, ""0 - ulong.MaxValue""); Console.WriteLine(); Helper.SubULong(({0}*)0, 1, ""0 - 1""); Helper.SubULong(({0}*)0, int.MaxValue, ""0 - int.MaxValue""); Helper.SubULong(({0}*)0, uint.MaxValue, ""0 - uint.MaxValue""); Helper.SubULong(({0}*)0, long.MaxValue, ""0 - long.MaxValue""); Helper.SubULong(({0}*)0, ulong.MaxValue, ""0 - ulong.MaxValue""); "; // 0 - pointed-at type private const string NegativeNumericSubtractionCasesTemplate = @" Helper.SubInt(({0}*)0, -1, ""0 - -1""); Helper.SubInt(({0}*)0, int.MinValue, ""0 - int.MinValue""); Helper.SubInt(({0}*)0, -1 * int.MaxValue, ""0 - -int.MaxValue""); Console.WriteLine(); Helper.SubLong(({0}*)0, -1L, ""0 - -1""); Helper.SubLong(({0}*)0, int.MinValue, ""0 - int.MinValue""); Helper.SubLong(({0}*)0, long.MinValue, ""0 - long.MinValue""); Helper.SubLong(({0}*)0, -1L * int.MaxValue, ""0 - -int.MaxValue""); Helper.SubLong(({0}*)0, -1L * uint.MaxValue, ""0 - -uint.MaxValue""); Helper.SubLong(({0}*)0, -1L * long.MaxValue, ""0 - -long.MaxValue""); Helper.SubLong(({0}*)0, -1L * long.MaxValue, ""0 - -ulong.MaxValue""); Helper.SubLong(({0}*)0, -1L * int.MinValue, ""0 - -int.MinValue""); //Helper.SubLong(({0}*)0, -1L * long.MinValue, ""0 - -long.MinValue""); "; private static string MakeNumericOverflowTest(string casesTemplate, string pointedAtType, string operationName, string @operator, string checkedness) { const string mainClassTemplate = @" using System; unsafe class C {{ static void Main() {{ {0} {{ {1} }} }} }} {2} {3} "; return string.Format(mainClassTemplate, checkedness, string.Format(casesTemplate, pointedAtType), string.Format(CheckedNumericHelperTemplate, operationName, pointedAtType, @operator, checkedness), SizedStructs); } // Positive numbers, size = 1 [Fact] public void CheckedNumericAdditionOverflow1() { var text = MakeNumericOverflowTest(PositiveNumericAdditionCasesTemplate, "S00", "Add", "+", "checked"); bool isx86 = (IntPtr.Size == 4); string expectedOutput; if (isx86) { expectedOutput = @" AddInt: No exception at 0 + int.MaxValue (value = 2147483647) AddInt: No exception at 1 + int.MaxValue (value = 2147483648) AddInt: No exception at int.MaxValue + 0 (value = 2147483647) AddInt: No exception at int.MaxValue + 1 (value = 2147483648) AddInt: No exception at uint.MaxValue + 0 (value = 4294967295) AddInt: Exception at uint.MaxValue + 1 AddUInt: No exception at 0 + int.MaxValue (value = 2147483647) AddUInt: No exception at 1 + int.MaxValue (value = 2147483648) AddUInt: No exception at int.MaxValue + 0 (value = 2147483647) AddUInt: No exception at int.MaxValue + 1 (value = 2147483648) AddUInt: No exception at 0 + uint.MaxValue (value = 4294967295) AddUInt: Exception at 1 + uint.MaxValue AddUInt: No exception at uint.MaxValue + 0 (value = 4294967295) AddUInt: Exception at uint.MaxValue + 1 AddLong: No exception at 0 + int.MaxValue (value = 2147483647) AddLong: No exception at 1 + int.MaxValue (value = 2147483648) AddLong: No exception at int.MaxValue + 0 (value = 2147483647) AddLong: No exception at int.MaxValue + 1 (value = 2147483648) AddLong: No exception at 0 + uint.MaxValue (value = 4294967295) AddLong: Exception at 1 + uint.MaxValue AddLong: No exception at uint.MaxValue + 0 (value = 4294967295) AddLong: Exception at uint.MaxValue + 1 AddLong: No exception at 0 + long.MaxValue (value = 4294967295) AddLong: Exception at 1 + long.MaxValue AddULong: No exception at 0 + int.MaxValue (value = 2147483647) AddULong: No exception at 1 + int.MaxValue (value = 2147483648) AddULong: No exception at int.MaxValue + 0 (value = 2147483647) AddULong: No exception at int.MaxValue + 1 (value = 2147483648) AddULong: No exception at 0 + uint.MaxValue (value = 4294967295) AddULong: Exception at 1 + uint.MaxValue AddULong: No exception at uint.MaxValue + 0 (value = 4294967295) AddULong: Exception at uint.MaxValue + 1 AddULong: No exception at 0 + long.MaxValue (value = 4294967295) AddULong: Exception at 1 + long.MaxValue AddULong: No exception at 0 + ulong.MaxValue (value = 4294967295) AddULong: Exception at 1 + ulong.MaxValue "; } else { expectedOutput = @" AddInt: No exception at 0 + int.MaxValue (value = 2147483647) AddInt: No exception at 1 + int.MaxValue (value = 2147483648) AddInt: No exception at int.MaxValue + 0 (value = 2147483647) AddInt: No exception at int.MaxValue + 1 (value = 2147483648) AddInt: No exception at uint.MaxValue + 0 (value = 4294967295) AddInt: No exception at uint.MaxValue + 1 (value = 4294967296) AddUInt: No exception at 0 + int.MaxValue (value = 2147483647) AddUInt: No exception at 1 + int.MaxValue (value = 2147483648) AddUInt: No exception at int.MaxValue + 0 (value = 2147483647) AddUInt: No exception at int.MaxValue + 1 (value = 2147483648) AddUInt: No exception at 0 + uint.MaxValue (value = 4294967295) AddUInt: No exception at 1 + uint.MaxValue (value = 4294967296) AddUInt: No exception at uint.MaxValue + 0 (value = 4294967295) AddUInt: No exception at uint.MaxValue + 1 (value = 4294967296) AddLong: No exception at 0 + int.MaxValue (value = 2147483647) AddLong: No exception at 1 + int.MaxValue (value = 2147483648) AddLong: No exception at int.MaxValue + 0 (value = 2147483647) AddLong: No exception at int.MaxValue + 1 (value = 2147483648) AddLong: No exception at 0 + uint.MaxValue (value = 4294967295) AddLong: No exception at 1 + uint.MaxValue (value = 4294967296) AddLong: No exception at uint.MaxValue + 0 (value = 4294967295) AddLong: No exception at uint.MaxValue + 1 (value = 4294967296) AddLong: No exception at 0 + long.MaxValue (value = 9223372036854775807) AddLong: No exception at 1 + long.MaxValue (value = 9223372036854775808) AddULong: No exception at 0 + int.MaxValue (value = 2147483647) AddULong: No exception at 1 + int.MaxValue (value = 2147483648) AddULong: No exception at int.MaxValue + 0 (value = 2147483647) AddULong: No exception at int.MaxValue + 1 (value = 2147483648) AddULong: No exception at 0 + uint.MaxValue (value = 4294967295) AddULong: No exception at 1 + uint.MaxValue (value = 4294967296) AddULong: No exception at uint.MaxValue + 0 (value = 4294967295) AddULong: No exception at uint.MaxValue + 1 (value = 4294967296) AddULong: No exception at 0 + long.MaxValue (value = 9223372036854775807) AddULong: No exception at 1 + long.MaxValue (value = 9223372036854775808) AddULong: No exception at 0 + ulong.MaxValue (value = 18446744073709551615) AddULong: Exception at 1 + ulong.MaxValue "; } CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: expectedOutput, verify: Verification.Fails); } // Positive numbers, size = 4 [Fact] public void CheckedNumericAdditionOverflow2() { var text = MakeNumericOverflowTest(PositiveNumericAdditionCasesTemplate, "S02", "Add", "+", "checked"); bool isx86 = (IntPtr.Size == 4); string expectedOutput; if (isx86) { expectedOutput = @" AddInt: Exception at 0 + int.MaxValue AddInt: Exception at 1 + int.MaxValue AddInt: No exception at int.MaxValue + 0 (value = 2147483647) AddInt: No exception at int.MaxValue + 1 (value = 2147483651) AddInt: No exception at uint.MaxValue + 0 (value = 4294967295) AddInt: Exception at uint.MaxValue + 1 AddUInt: No exception at 0 + int.MaxValue (value = 4294967292) AddUInt: No exception at 1 + int.MaxValue (value = 4294967293) AddUInt: No exception at int.MaxValue + 0 (value = 2147483647) AddUInt: No exception at int.MaxValue + 1 (value = 2147483651) AddUInt: No exception at 0 + uint.MaxValue (value = 4294967292) AddUInt: No exception at 1 + uint.MaxValue (value = 4294967293) AddUInt: No exception at uint.MaxValue + 0 (value = 4294967295) AddUInt: Exception at uint.MaxValue + 1 AddLong: No exception at 0 + int.MaxValue (value = 4294967292) AddLong: No exception at 1 + int.MaxValue (value = 4294967293) AddLong: No exception at int.MaxValue + 0 (value = 2147483647) AddLong: No exception at int.MaxValue + 1 (value = 2147483651) AddLong: No exception at 0 + uint.MaxValue (value = 4294967292) AddLong: No exception at 1 + uint.MaxValue (value = 4294967293) AddLong: No exception at uint.MaxValue + 0 (value = 4294967295) AddLong: Exception at uint.MaxValue + 1 AddLong: Exception at 0 + long.MaxValue AddLong: Exception at 1 + long.MaxValue AddULong: No exception at 0 + int.MaxValue (value = 4294967292) AddULong: No exception at 1 + int.MaxValue (value = 4294967293) AddULong: No exception at int.MaxValue + 0 (value = 2147483647) AddULong: No exception at int.MaxValue + 1 (value = 2147483651) AddULong: No exception at 0 + uint.MaxValue (value = 4294967292) AddULong: No exception at 1 + uint.MaxValue (value = 4294967293) AddULong: No exception at uint.MaxValue + 0 (value = 4294967295) AddULong: Exception at uint.MaxValue + 1 AddULong: Exception at 0 + long.MaxValue AddULong: Exception at 1 + long.MaxValue AddULong: Exception at 0 + ulong.MaxValue AddULong: Exception at 1 + ulong.MaxValue "; } else { expectedOutput = @" AddInt: No exception at 0 + int.MaxValue (value = 8589934588) AddInt: No exception at 1 + int.MaxValue (value = 8589934589) AddInt: No exception at int.MaxValue + 0 (value = 2147483647) AddInt: No exception at int.MaxValue + 1 (value = 2147483651) AddInt: No exception at uint.MaxValue + 0 (value = 4294967295) AddInt: No exception at uint.MaxValue + 1 (value = 4294967299) AddUInt: No exception at 0 + int.MaxValue (value = 8589934588) AddUInt: No exception at 1 + int.MaxValue (value = 8589934589) AddUInt: No exception at int.MaxValue + 0 (value = 2147483647) AddUInt: No exception at int.MaxValue + 1 (value = 2147483651) AddUInt: No exception at 0 + uint.MaxValue (value = 17179869180) AddUInt: No exception at 1 + uint.MaxValue (value = 17179869181) AddUInt: No exception at uint.MaxValue + 0 (value = 4294967295) AddUInt: No exception at uint.MaxValue + 1 (value = 4294967299) AddLong: No exception at 0 + int.MaxValue (value = 8589934588) AddLong: No exception at 1 + int.MaxValue (value = 8589934589) AddLong: No exception at int.MaxValue + 0 (value = 2147483647) AddLong: No exception at int.MaxValue + 1 (value = 2147483651) AddLong: No exception at 0 + uint.MaxValue (value = 17179869180) AddLong: No exception at 1 + uint.MaxValue (value = 17179869181) AddLong: No exception at uint.MaxValue + 0 (value = 4294967295) AddLong: No exception at uint.MaxValue + 1 (value = 4294967299) AddLong: Exception at 0 + long.MaxValue AddLong: Exception at 1 + long.MaxValue AddULong: No exception at 0 + int.MaxValue (value = 8589934588) AddULong: No exception at 1 + int.MaxValue (value = 8589934589) AddULong: No exception at int.MaxValue + 0 (value = 2147483647) AddULong: No exception at int.MaxValue + 1 (value = 2147483651) AddULong: No exception at 0 + uint.MaxValue (value = 17179869180) AddULong: No exception at 1 + uint.MaxValue (value = 17179869181) AddULong: No exception at uint.MaxValue + 0 (value = 4294967295) AddULong: No exception at uint.MaxValue + 1 (value = 4294967299) AddULong: Exception at 0 + long.MaxValue AddULong: Exception at 1 + long.MaxValue AddULong: Exception at 0 + ulong.MaxValue AddULong: Exception at 1 + ulong.MaxValue "; } CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: expectedOutput, verify: Verification.Fails); } // Negative numbers, size = 1 [Fact] public void CheckedNumericAdditionOverflow3() { var text = MakeNumericOverflowTest(NegativeNumericAdditionCasesTemplate, "S00", "Add", "+", "checked"); bool isx86 = (IntPtr.Size == 4); string expectedOutput; if (isx86) { expectedOutput = @" AddInt: No exception at 0 + (-1) (value = 4294967295) AddInt: No exception at 0 + int.MinValue (value = 2147483648) AddLong: No exception at 0 + (-1) (value = 4294967295) AddLong: No exception at 0 + int.MinValue (value = 2147483648) AddLong: No exception at 0 + long.MinValue (value = 0) "; } else { expectedOutput = @" AddInt: No exception at 0 + (-1) (value = 18446744073709551615) AddInt: No exception at 0 + int.MinValue (value = 18446744071562067968) AddLong: No exception at 0 + (-1) (value = 18446744073709551615) AddLong: No exception at 0 + int.MinValue (value = 18446744071562067968) AddLong: No exception at 0 + long.MinValue (value = 9223372036854775808) "; } CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, verify: Verification.Fails, expectedOutput: expectedOutput); } // Negative numbers, size = 4 [Fact] public void CheckedNumericAdditionOverflow4() { var text = MakeNumericOverflowTest(NegativeNumericAdditionCasesTemplate, "S02", "Add", "+", "checked"); bool isx86 = (IntPtr.Size == 4); string expectedOutput; if (isx86) { expectedOutput = @" AddInt: No exception at 0 + (-1) (value = 4294967292) AddInt: Exception at 0 + int.MinValue AddLong: No exception at 0 + (-1) (value = 4294967292) AddLong: No exception at 0 + int.MinValue (value = 0) AddLong: Exception at 0 + long.MinValue "; } else { expectedOutput = @" AddInt: No exception at 0 + (-1) (value = 18446744073709551612) AddInt: No exception at 0 + int.MinValue (value = 18446744065119617024) AddLong: No exception at 0 + (-1) (value = 18446744073709551612) AddLong: No exception at 0 + int.MinValue (value = 18446744065119617024) AddLong: Exception at 0 + long.MinValue "; } CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: expectedOutput, verify: Verification.Fails); } // Positive numbers, size = 1 [Fact] public void CheckedNumericSubtractionOverflow1() { var text = MakeNumericOverflowTest(PositiveNumericSubtractionCasesTemplate, "S00", "Sub", "-", "checked"); CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, verify: Verification.Fails, expectedOutput: @" SubInt: Exception at 0 - 1 SubInt: Exception at 0 - int.MaxValue SubUInt: Exception at 0 - 1 SubUInt: Exception at 0 - int.MaxValue SubUInt: Exception at 0 - uint.MaxValue SubLong: Exception at 0 - 1 SubLong: Exception at 0 - int.MaxValue SubLong: Exception at 0 - uint.MaxValue SubLong: Exception at 0 - long.MaxValue SubULong: Exception at 0 - 1 SubULong: Exception at 0 - int.MaxValue SubULong: Exception at 0 - uint.MaxValue SubULong: Exception at 0 - long.MaxValue SubULong: Exception at 0 - ulong.MaxValue "); } // Positive numbers, size = 4 [Fact] public void CheckedNumericSubtractionOverflow2() { var text = MakeNumericOverflowTest(PositiveNumericSubtractionCasesTemplate, "S02", "Sub", "-", "checked"); CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, verify: Verification.Fails, expectedOutput: @" SubInt: Exception at 0 - 1 SubInt: Exception at 0 - int.MaxValue SubUInt: Exception at 0 - 1 SubUInt: Exception at 0 - int.MaxValue SubUInt: Exception at 0 - uint.MaxValue SubLong: Exception at 0 - 1 SubLong: Exception at 0 - int.MaxValue SubLong: Exception at 0 - uint.MaxValue SubLong: Exception at 0 - long.MaxValue SubULong: Exception at 0 - 1 SubULong: Exception at 0 - int.MaxValue SubULong: Exception at 0 - uint.MaxValue SubULong: Exception at 0 - long.MaxValue SubULong: Exception at 0 - ulong.MaxValue "); } // Negative numbers, size = 1 [Fact] public void CheckedNumericSubtractionOverflow3() { var text = MakeNumericOverflowTest(NegativeNumericSubtractionCasesTemplate, "S00", "Sub", "-", "checked"); bool isx86 = (IntPtr.Size == 4); string expectedOutput; if (isx86) { expectedOutput = @" SubInt: Exception at 0 - -1 SubInt: Exception at 0 - int.MinValue SubInt: Exception at 0 - -int.MaxValue SubLong: Exception at 0 - -1 SubLong: Exception at 0 - int.MinValue SubLong: No exception at 0 - long.MinValue (value = 0) SubLong: Exception at 0 - -int.MaxValue SubLong: Exception at 0 - -uint.MaxValue SubLong: Exception at 0 - -long.MaxValue SubLong: Exception at 0 - -ulong.MaxValue SubLong: Exception at 0 - -int.MinValue "; } else { expectedOutput = @" SubInt: Exception at 0 - -1 SubInt: Exception at 0 - int.MinValue SubInt: Exception at 0 - -int.MaxValue SubLong: Exception at 0 - -1 SubLong: Exception at 0 - int.MinValue SubLong: Exception at 0 - long.MinValue SubLong: Exception at 0 - -int.MaxValue SubLong: Exception at 0 - -uint.MaxValue SubLong: Exception at 0 - -long.MaxValue SubLong: Exception at 0 - -ulong.MaxValue SubLong: Exception at 0 - -int.MinValue "; } CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: expectedOutput, verify: Verification.Fails); } // Negative numbers, size = 4 [Fact] public void CheckedNumericSubtractionOverflow4() { var text = MakeNumericOverflowTest(NegativeNumericSubtractionCasesTemplate, "S02", "Sub", "-", "checked"); bool isx86 = (IntPtr.Size == 4); string expectedOutput; if (isx86) { expectedOutput = @" SubInt: Exception at 0 - -1 SubInt: Exception at 0 - int.MinValue SubInt: Exception at 0 - -int.MaxValue SubLong: Exception at 0 - -1 SubLong: No exception at 0 - int.MinValue (value = 0) SubLong: Exception at 0 - long.MinValue SubLong: Exception at 0 - -int.MaxValue SubLong: Exception at 0 - -uint.MaxValue SubLong: Exception at 0 - -long.MaxValue SubLong: Exception at 0 - -ulong.MaxValue SubLong: No exception at 0 - -int.MinValue (value = 0) "; } else { expectedOutput = @" SubInt: Exception at 0 - -1 SubInt: Exception at 0 - int.MinValue SubInt: Exception at 0 - -int.MaxValue SubLong: Exception at 0 - -1 SubLong: Exception at 0 - int.MinValue SubLong: Exception at 0 - long.MinValue SubLong: Exception at 0 - -int.MaxValue SubLong: Exception at 0 - -uint.MaxValue SubLong: Exception at 0 - -long.MaxValue SubLong: Exception at 0 - -ulong.MaxValue SubLong: Exception at 0 - -int.MinValue "; } CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: expectedOutput, verify: Verification.Fails); } [Fact] public void CheckedNumericSubtractionQuirk() { var text = @" using System; unsafe struct S { static void Main() { checked { S* p; p = (S*)0 + (-1); System.Console.WriteLine(""No exception from addition""); try { p = (S*)0 - 1; } catch (OverflowException) { System.Console.WriteLine(""Exception from subtraction""); } } } } "; CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, verify: Verification.Passes, expectedOutput: @" No exception from addition Exception from subtraction "); } [Fact] public void CheckedNumericAdditionQuirk() { var text = @" using System; unsafe struct S { static void Main() { checked { S* p; p = (S*)1 + int.MaxValue; System.Console.WriteLine(""No exception for pointer + int""); try { p = int.MaxValue + (S*)1; } catch (OverflowException) { System.Console.WriteLine(""Exception for int + pointer""); } } } } "; bool isx86 = (IntPtr.Size == 4); string expectedOutput; if (isx86) { expectedOutput = @" No exception for pointer + int Exception for int + pointer "; } else { expectedOutput = @" No exception for pointer + int "; } CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: expectedOutput, verify: Verification.Passes); } [Fact] public void CheckedPointerSubtractionQuirk() { var text = @" using System; unsafe struct S { static void Main() { S* p = (S*)uint.MinValue; S* q = (S*)uint.MaxValue; checked { Console.Write(p - q); } unchecked { Console.Write(p - q); } } } "; bool isx86 = (IntPtr.Size == 4); string expectedOutput; if (isx86) { expectedOutput = @"11"; } else { expectedOutput = @"-4294967295-4294967295"; } CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: expectedOutput, verify: Verification.Fails); } [Fact] public void CheckedPointerElementAccessQuirk() { var text = @" using System; unsafe struct S { static void Main() { fixed (byte* p = new byte[2]) { p[0] = 12; // Take a pointer to the second element of the array. byte* q = p + 1; // Compute the offset that will wrap around all the way to the preceding byte of memory. // We do this so that we can overflow, but still end up in valid memory. ulong offset = sizeof(IntPtr) == sizeof(int) ? uint.MaxValue : ulong.MaxValue; checked { Console.WriteLine(q[offset]); System.Console.WriteLine(""No exception for element access""); try { Console.WriteLine(*(q + offset)); } catch (OverflowException) { System.Console.WriteLine(""Exception for add-then-dereference""); } } } } } "; CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, verify: Verification.Fails, expectedOutput: @" 12 No exception for element access Exception for add-then-dereference "); } #endregion Checked pointer arithmetic overflow tests #region Unchecked pointer arithmetic overflow tests // Positive numbers, size = 1 [Fact] public void UncheckedNumericAdditionOverflow1() { var text = MakeNumericOverflowTest(PositiveNumericAdditionCasesTemplate, "S00", "Add", "+", "unchecked"); bool isx86 = (IntPtr.Size == 4); string expectedOutput; if (isx86) { expectedOutput = @" AddInt: No exception at 0 + int.MaxValue (value = 2147483647) AddInt: No exception at 1 + int.MaxValue (value = 2147483648) AddInt: No exception at int.MaxValue + 0 (value = 2147483647) AddInt: No exception at int.MaxValue + 1 (value = 2147483648) AddInt: No exception at uint.MaxValue + 0 (value = 4294967295) AddInt: No exception at uint.MaxValue + 1 (value = 0) AddUInt: No exception at 0 + int.MaxValue (value = 2147483647) AddUInt: No exception at 1 + int.MaxValue (value = 2147483648) AddUInt: No exception at int.MaxValue + 0 (value = 2147483647) AddUInt: No exception at int.MaxValue + 1 (value = 2147483648) AddUInt: No exception at 0 + uint.MaxValue (value = 4294967295) AddUInt: No exception at 1 + uint.MaxValue (value = 0) AddUInt: No exception at uint.MaxValue + 0 (value = 4294967295) AddUInt: No exception at uint.MaxValue + 1 (value = 0) AddLong: No exception at 0 + int.MaxValue (value = 2147483647) AddLong: No exception at 1 + int.MaxValue (value = 2147483648) AddLong: No exception at int.MaxValue + 0 (value = 2147483647) AddLong: No exception at int.MaxValue + 1 (value = 2147483648) AddLong: No exception at 0 + uint.MaxValue (value = 4294967295) AddLong: No exception at 1 + uint.MaxValue (value = 0) AddLong: No exception at uint.MaxValue + 0 (value = 4294967295) AddLong: No exception at uint.MaxValue + 1 (value = 0) AddLong: No exception at 0 + long.MaxValue (value = 4294967295) AddLong: No exception at 1 + long.MaxValue (value = 0) AddULong: No exception at 0 + int.MaxValue (value = 2147483647) AddULong: No exception at 1 + int.MaxValue (value = 2147483648) AddULong: No exception at int.MaxValue + 0 (value = 2147483647) AddULong: No exception at int.MaxValue + 1 (value = 2147483648) AddULong: No exception at 0 + uint.MaxValue (value = 4294967295) AddULong: No exception at 1 + uint.MaxValue (value = 0) AddULong: No exception at uint.MaxValue + 0 (value = 4294967295) AddULong: No exception at uint.MaxValue + 1 (value = 0) AddULong: No exception at 0 + long.MaxValue (value = 4294967295) AddULong: No exception at 1 + long.MaxValue (value = 0) AddULong: No exception at 0 + ulong.MaxValue (value = 4294967295) AddULong: No exception at 1 + ulong.MaxValue (value = 0) "; } else { expectedOutput = @" AddInt: No exception at 0 + int.MaxValue (value = 2147483647) AddInt: No exception at 1 + int.MaxValue (value = 2147483648) AddInt: No exception at int.MaxValue + 0 (value = 2147483647) AddInt: No exception at int.MaxValue + 1 (value = 2147483648) AddInt: No exception at uint.MaxValue + 0 (value = 4294967295) AddInt: No exception at uint.MaxValue + 1 (value = 4294967296) AddUInt: No exception at 0 + int.MaxValue (value = 2147483647) AddUInt: No exception at 1 + int.MaxValue (value = 2147483648) AddUInt: No exception at int.MaxValue + 0 (value = 2147483647) AddUInt: No exception at int.MaxValue + 1 (value = 2147483648) AddUInt: No exception at 0 + uint.MaxValue (value = 4294967295) AddUInt: No exception at 1 + uint.MaxValue (value = 4294967296) AddUInt: No exception at uint.MaxValue + 0 (value = 4294967295) AddUInt: No exception at uint.MaxValue + 1 (value = 4294967296) AddLong: No exception at 0 + int.MaxValue (value = 2147483647) AddLong: No exception at 1 + int.MaxValue (value = 2147483648) AddLong: No exception at int.MaxValue + 0 (value = 2147483647) AddLong: No exception at int.MaxValue + 1 (value = 2147483648) AddLong: No exception at 0 + uint.MaxValue (value = 4294967295) AddLong: No exception at 1 + uint.MaxValue (value = 4294967296) AddLong: No exception at uint.MaxValue + 0 (value = 4294967295) AddLong: No exception at uint.MaxValue + 1 (value = 4294967296) AddLong: No exception at 0 + long.MaxValue (value = 9223372036854775807) AddLong: No exception at 1 + long.MaxValue (value = 9223372036854775808) AddULong: No exception at 0 + int.MaxValue (value = 2147483647) AddULong: No exception at 1 + int.MaxValue (value = 2147483648) AddULong: No exception at int.MaxValue + 0 (value = 2147483647) AddULong: No exception at int.MaxValue + 1 (value = 2147483648) AddULong: No exception at 0 + uint.MaxValue (value = 4294967295) AddULong: No exception at 1 + uint.MaxValue (value = 4294967296) AddULong: No exception at uint.MaxValue + 0 (value = 4294967295) AddULong: No exception at uint.MaxValue + 1 (value = 4294967296) AddULong: No exception at 0 + long.MaxValue (value = 9223372036854775807) AddULong: No exception at 1 + long.MaxValue (value = 9223372036854775808) AddULong: No exception at 0 + ulong.MaxValue (value = 18446744073709551615) AddULong: No exception at 1 + ulong.MaxValue (value = 0) "; } CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: expectedOutput, verify: Verification.Fails); } // Positive numbers, size = 4 [Fact] public void UncheckedNumericAdditionOverflow2() { var text = MakeNumericOverflowTest(PositiveNumericAdditionCasesTemplate, "S02", "Add", "+", "unchecked"); bool isx86 = (IntPtr.Size == 4); string expectedOutput; if (isx86) { expectedOutput = @" AddInt: No exception at 0 + int.MaxValue (value = 4294967292) AddInt: No exception at 1 + int.MaxValue (value = 4294967293) AddInt: No exception at int.MaxValue + 0 (value = 2147483647) AddInt: No exception at int.MaxValue + 1 (value = 2147483651) AddInt: No exception at uint.MaxValue + 0 (value = 4294967295) AddInt: No exception at uint.MaxValue + 1 (value = 3) AddUInt: No exception at 0 + int.MaxValue (value = 4294967292) AddUInt: No exception at 1 + int.MaxValue (value = 4294967293) AddUInt: No exception at int.MaxValue + 0 (value = 2147483647) AddUInt: No exception at int.MaxValue + 1 (value = 2147483651) AddUInt: No exception at 0 + uint.MaxValue (value = 4294967292) AddUInt: No exception at 1 + uint.MaxValue (value = 4294967293) AddUInt: No exception at uint.MaxValue + 0 (value = 4294967295) AddUInt: No exception at uint.MaxValue + 1 (value = 3) AddLong: No exception at 0 + int.MaxValue (value = 4294967292) AddLong: No exception at 1 + int.MaxValue (value = 4294967293) AddLong: No exception at int.MaxValue + 0 (value = 2147483647) AddLong: No exception at int.MaxValue + 1 (value = 2147483651) AddLong: No exception at 0 + uint.MaxValue (value = 4294967292) AddLong: No exception at 1 + uint.MaxValue (value = 4294967293) AddLong: No exception at uint.MaxValue + 0 (value = 4294967295) AddLong: No exception at uint.MaxValue + 1 (value = 3) AddLong: No exception at 0 + long.MaxValue (value = 4294967292) AddLong: No exception at 1 + long.MaxValue (value = 4294967293) AddULong: No exception at 0 + int.MaxValue (value = 4294967292) AddULong: No exception at 1 + int.MaxValue (value = 4294967293) AddULong: No exception at int.MaxValue + 0 (value = 2147483647) AddULong: No exception at int.MaxValue + 1 (value = 2147483651) AddULong: No exception at 0 + uint.MaxValue (value = 4294967292) AddULong: No exception at 1 + uint.MaxValue (value = 4294967293) AddULong: No exception at uint.MaxValue + 0 (value = 4294967295) AddULong: No exception at uint.MaxValue + 1 (value = 3) AddULong: No exception at 0 + long.MaxValue (value = 4294967292) AddULong: No exception at 1 + long.MaxValue (value = 4294967293) AddULong: No exception at 0 + ulong.MaxValue (value = 4294967292) AddULong: No exception at 1 + ulong.MaxValue (value = 4294967293) "; } else { expectedOutput = @" AddInt: No exception at 0 + int.MaxValue (value = 8589934588) AddInt: No exception at 1 + int.MaxValue (value = 8589934589) AddInt: No exception at int.MaxValue + 0 (value = 2147483647) AddInt: No exception at int.MaxValue + 1 (value = 2147483651) AddInt: No exception at uint.MaxValue + 0 (value = 4294967295) AddInt: No exception at uint.MaxValue + 1 (value = 4294967299) AddUInt: No exception at 0 + int.MaxValue (value = 8589934588) AddUInt: No exception at 1 + int.MaxValue (value = 8589934589) AddUInt: No exception at int.MaxValue + 0 (value = 2147483647) AddUInt: No exception at int.MaxValue + 1 (value = 2147483651) AddUInt: No exception at 0 + uint.MaxValue (value = 17179869180) AddUInt: No exception at 1 + uint.MaxValue (value = 17179869181) AddUInt: No exception at uint.MaxValue + 0 (value = 4294967295) AddUInt: No exception at uint.MaxValue + 1 (value = 4294967299) AddLong: No exception at 0 + int.MaxValue (value = 8589934588) AddLong: No exception at 1 + int.MaxValue (value = 8589934589) AddLong: No exception at int.MaxValue + 0 (value = 2147483647) AddLong: No exception at int.MaxValue + 1 (value = 2147483651) AddLong: No exception at 0 + uint.MaxValue (value = 17179869180) AddLong: No exception at 1 + uint.MaxValue (value = 17179869181) AddLong: No exception at uint.MaxValue + 0 (value = 4294967295) AddLong: No exception at uint.MaxValue + 1 (value = 4294967299) AddLong: No exception at 0 + long.MaxValue (value = 18446744073709551612) AddLong: No exception at 1 + long.MaxValue (value = 18446744073709551613) AddULong: No exception at 0 + int.MaxValue (value = 8589934588) AddULong: No exception at 1 + int.MaxValue (value = 8589934589) AddULong: No exception at int.MaxValue + 0 (value = 2147483647) AddULong: No exception at int.MaxValue + 1 (value = 2147483651) AddULong: No exception at 0 + uint.MaxValue (value = 17179869180) AddULong: No exception at 1 + uint.MaxValue (value = 17179869181) AddULong: No exception at uint.MaxValue + 0 (value = 4294967295) AddULong: No exception at uint.MaxValue + 1 (value = 4294967299) AddULong: No exception at 0 + long.MaxValue (value = 18446744073709551612) AddULong: No exception at 1 + long.MaxValue (value = 18446744073709551613) AddULong: No exception at 0 + ulong.MaxValue (value = 18446744073709551612) AddULong: No exception at 1 + ulong.MaxValue (value = 18446744073709551613) "; } CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: expectedOutput, verify: Verification.Fails); } // Negative numbers, size = 1 [Fact] public void UncheckedNumericAdditionOverflow3() { var text = MakeNumericOverflowTest(NegativeNumericAdditionCasesTemplate, "S00", "Add", "+", "unchecked"); bool isx86 = (IntPtr.Size == 4); string expectedOutput; if (isx86) { expectedOutput = @" AddInt: No exception at 0 + (-1) (value = 4294967295) AddInt: No exception at 0 + int.MinValue (value = 2147483648) AddLong: No exception at 0 + (-1) (value = 4294967295) AddLong: No exception at 0 + int.MinValue (value = 2147483648) AddLong: No exception at 0 + long.MinValue (value = 0) "; } else { expectedOutput = @" AddInt: No exception at 0 + (-1) (value = 18446744073709551615) AddInt: No exception at 0 + int.MinValue (value = 18446744071562067968) AddLong: No exception at 0 + (-1) (value = 18446744073709551615) AddLong: No exception at 0 + int.MinValue (value = 18446744071562067968) AddLong: No exception at 0 + long.MinValue (value = 9223372036854775808) "; } CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: expectedOutput, verify: Verification.Fails); } // Negative numbers, size = 4 [Fact] public void UncheckedNumericAdditionOverflow4() { var text = MakeNumericOverflowTest(NegativeNumericAdditionCasesTemplate, "S02", "Add", "+", "unchecked"); bool isx86 = (IntPtr.Size == 4); string expectedOutput; if (isx86) { expectedOutput = @" AddInt: No exception at 0 + (-1) (value = 4294967292) AddInt: No exception at 0 + int.MinValue (value = 0) AddLong: No exception at 0 + (-1) (value = 4294967292) AddLong: No exception at 0 + int.MinValue (value = 0) AddLong: No exception at 0 + long.MinValue (value = 0) "; } else { expectedOutput = @" AddInt: No exception at 0 + (-1) (value = 18446744073709551612) AddInt: No exception at 0 + int.MinValue (value = 18446744065119617024) AddLong: No exception at 0 + (-1) (value = 18446744073709551612) AddLong: No exception at 0 + int.MinValue (value = 18446744065119617024) AddLong: No exception at 0 + long.MinValue (value = 0) "; } CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: expectedOutput, verify: Verification.Fails); } // Positive numbers, size = 1 [Fact] public void UncheckedNumericSubtractionOverflow1() { var text = MakeNumericOverflowTest(PositiveNumericSubtractionCasesTemplate, "S00", "Sub", "-", "unchecked"); bool isx86 = (IntPtr.Size == 4); string expectedOutput; if (isx86) { expectedOutput = @" SubInt: No exception at 0 - 1 (value = 4294967295) SubInt: No exception at 0 - int.MaxValue (value = 2147483649) SubUInt: No exception at 0 - 1 (value = 4294967295) SubUInt: No exception at 0 - int.MaxValue (value = 2147483649) SubUInt: No exception at 0 - uint.MaxValue (value = 1) SubLong: No exception at 0 - 1 (value = 4294967295) SubLong: No exception at 0 - int.MaxValue (value = 2147483649) SubLong: No exception at 0 - uint.MaxValue (value = 1) SubLong: No exception at 0 - long.MaxValue (value = 1) SubULong: No exception at 0 - 1 (value = 4294967295) SubULong: No exception at 0 - int.MaxValue (value = 2147483649) SubULong: No exception at 0 - uint.MaxValue (value = 1) SubULong: No exception at 0 - long.MaxValue (value = 1) SubULong: No exception at 0 - ulong.MaxValue (value = 1) "; } else { expectedOutput = @" SubInt: No exception at 0 - 1 (value = 18446744073709551615) SubInt: No exception at 0 - int.MaxValue (value = 18446744071562067969) SubUInt: No exception at 0 - 1 (value = 18446744073709551615) SubUInt: No exception at 0 - int.MaxValue (value = 18446744071562067969) SubUInt: No exception at 0 - uint.MaxValue (value = 18446744069414584321) SubLong: No exception at 0 - 1 (value = 18446744073709551615) SubLong: No exception at 0 - int.MaxValue (value = 18446744071562067969) SubLong: No exception at 0 - uint.MaxValue (value = 18446744069414584321) SubLong: No exception at 0 - long.MaxValue (value = 9223372036854775809) SubULong: No exception at 0 - 1 (value = 18446744073709551615) SubULong: No exception at 0 - int.MaxValue (value = 18446744071562067969) SubULong: No exception at 0 - uint.MaxValue (value = 18446744069414584321) SubULong: No exception at 0 - long.MaxValue (value = 9223372036854775809) SubULong: No exception at 0 - ulong.MaxValue (value = 1) "; } CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: expectedOutput, verify: Verification.Fails); } // Positive numbers, size = 4 [Fact] public void UncheckedNumericSubtractionOverflow2() { var text = MakeNumericOverflowTest(PositiveNumericSubtractionCasesTemplate, "S02", "Sub", "-", "unchecked"); bool isx86 = (IntPtr.Size == 4); string expectedOutput; if (isx86) { expectedOutput = @" SubInt: No exception at 0 - 1 (value = 4294967292) SubInt: No exception at 0 - int.MaxValue (value = 4) SubUInt: No exception at 0 - 1 (value = 4294967292) SubUInt: No exception at 0 - int.MaxValue (value = 4) SubUInt: No exception at 0 - uint.MaxValue (value = 4) SubLong: No exception at 0 - 1 (value = 4294967292) SubLong: No exception at 0 - int.MaxValue (value = 4) SubLong: No exception at 0 - uint.MaxValue (value = 4) SubLong: No exception at 0 - long.MaxValue (value = 4) SubULong: No exception at 0 - 1 (value = 4294967292) SubULong: No exception at 0 - int.MaxValue (value = 4) SubULong: No exception at 0 - uint.MaxValue (value = 4) SubULong: No exception at 0 - long.MaxValue (value = 4) SubULong: No exception at 0 - ulong.MaxValue (value = 4) "; } else { expectedOutput = @" SubInt: No exception at 0 - 1 (value = 18446744073709551612) SubInt: No exception at 0 - int.MaxValue (value = 18446744065119617028) SubUInt: No exception at 0 - 1 (value = 18446744073709551612) SubUInt: No exception at 0 - int.MaxValue (value = 18446744065119617028) SubUInt: No exception at 0 - uint.MaxValue (value = 18446744056529682436) SubLong: No exception at 0 - 1 (value = 18446744073709551612) SubLong: No exception at 0 - int.MaxValue (value = 18446744065119617028) SubLong: No exception at 0 - uint.MaxValue (value = 18446744056529682436) SubLong: No exception at 0 - long.MaxValue (value = 4) SubULong: No exception at 0 - 1 (value = 18446744073709551612) SubULong: No exception at 0 - int.MaxValue (value = 18446744065119617028) SubULong: No exception at 0 - uint.MaxValue (value = 18446744056529682436) SubULong: No exception at 0 - long.MaxValue (value = 4) SubULong: No exception at 0 - ulong.MaxValue (value = 4) "; } CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: expectedOutput, verify: Verification.Fails); } // Negative numbers, size = 1 [Fact] public void UncheckedNumericSubtractionOverflow3() { var text = MakeNumericOverflowTest(NegativeNumericSubtractionCasesTemplate, "S00", "Sub", "-", "unchecked"); bool isx86 = (IntPtr.Size == 4); string expectedOutput; if (isx86) { expectedOutput = @" SubInt: No exception at 0 - -1 (value = 1) SubInt: No exception at 0 - int.MinValue (value = 2147483648) SubInt: No exception at 0 - -int.MaxValue (value = 2147483647) SubLong: No exception at 0 - -1 (value = 1) SubLong: No exception at 0 - int.MinValue (value = 2147483648) SubLong: No exception at 0 - long.MinValue (value = 0) SubLong: No exception at 0 - -int.MaxValue (value = 2147483647) SubLong: No exception at 0 - -uint.MaxValue (value = 4294967295) SubLong: No exception at 0 - -long.MaxValue (value = 4294967295) SubLong: No exception at 0 - -ulong.MaxValue (value = 4294967295) SubLong: No exception at 0 - -int.MinValue (value = 2147483648) "; } else { expectedOutput = @" SubInt: No exception at 0 - -1 (value = 1) SubInt: No exception at 0 - int.MinValue (value = 2147483648) SubInt: No exception at 0 - -int.MaxValue (value = 2147483647) SubLong: No exception at 0 - -1 (value = 1) SubLong: No exception at 0 - int.MinValue (value = 2147483648) SubLong: No exception at 0 - long.MinValue (value = 9223372036854775808) SubLong: No exception at 0 - -int.MaxValue (value = 2147483647) SubLong: No exception at 0 - -uint.MaxValue (value = 4294967295) SubLong: No exception at 0 - -long.MaxValue (value = 9223372036854775807) SubLong: No exception at 0 - -ulong.MaxValue (value = 9223372036854775807) SubLong: No exception at 0 - -int.MinValue (value = 18446744071562067968) "; } CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: expectedOutput, verify: Verification.Fails); } // Negative numbers, size = 4 [Fact] public void UncheckedNumericSubtractionOverflow4() { var text = MakeNumericOverflowTest(NegativeNumericSubtractionCasesTemplate, "S02", "Sub", "-", "unchecked"); bool isx86 = (IntPtr.Size == 4); string expectedOutput; if (isx86) { expectedOutput = @" SubInt: No exception at 0 - -1 (value = 4) SubInt: No exception at 0 - int.MinValue (value = 0) SubInt: No exception at 0 - -int.MaxValue (value = 4294967292) SubLong: No exception at 0 - -1 (value = 4) SubLong: No exception at 0 - int.MinValue (value = 0) SubLong: No exception at 0 - long.MinValue (value = 0) SubLong: No exception at 0 - -int.MaxValue (value = 4294967292) SubLong: No exception at 0 - -uint.MaxValue (value = 4294967292) SubLong: No exception at 0 - -long.MaxValue (value = 4294967292) SubLong: No exception at 0 - -ulong.MaxValue (value = 4294967292) SubLong: No exception at 0 - -int.MinValue (value = 0)"; } else { expectedOutput = @" SubInt: No exception at 0 - -1 (value = 4) SubInt: No exception at 0 - int.MinValue (value = 8589934592) SubInt: No exception at 0 - -int.MaxValue (value = 8589934588) SubLong: No exception at 0 - -1 (value = 4) SubLong: No exception at 0 - int.MinValue (value = 8589934592) SubLong: No exception at 0 - long.MinValue (value = 0) SubLong: No exception at 0 - -int.MaxValue (value = 8589934588) SubLong: No exception at 0 - -uint.MaxValue (value = 17179869180) SubLong: No exception at 0 - -long.MaxValue (value = 18446744073709551612) SubLong: No exception at 0 - -ulong.MaxValue (value = 18446744073709551612) SubLong: No exception at 0 - -int.MinValue (value = 18446744065119617024)"; } CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: expectedOutput, verify: Verification.Fails); } #endregion Unchecked pointer arithmetic overflow tests #region Pointer comparison tests [Fact] public void PointerComparisonSameType() { var text = @" using System; unsafe struct S { static void Main() { S* p = (S*)0; S* q = (S*)1; unchecked { Write(p == q); Write(p != q); Write(p <= q); Write(p >= q); Write(p < q); Write(p > q); } checked { Write(p == q); Write(p != q); Write(p <= q); Write(p >= q); Write(p < q); Write(p > q); } } static void Write(bool b) { Console.Write(b ? 1 : 0); } } "; // NOTE: all comparisons unsigned. CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: "011010011010", verify: Verification.Fails).VerifyIL("S.Main", @" { // Code size 133 (0x85) .maxstack 2 .locals init (S* V_0, //p S* V_1) //q IL_0000: ldc.i4.0 IL_0001: conv.i IL_0002: stloc.0 IL_0003: ldc.i4.1 IL_0004: conv.i IL_0005: stloc.1 IL_0006: ldloc.0 IL_0007: ldloc.1 IL_0008: ceq IL_000a: call ""void S.Write(bool)"" IL_000f: ldloc.0 IL_0010: ldloc.1 IL_0011: ceq IL_0013: ldc.i4.0 IL_0014: ceq IL_0016: call ""void S.Write(bool)"" IL_001b: ldloc.0 IL_001c: ldloc.1 IL_001d: cgt.un IL_001f: ldc.i4.0 IL_0020: ceq IL_0022: call ""void S.Write(bool)"" IL_0027: ldloc.0 IL_0028: ldloc.1 IL_0029: clt.un IL_002b: ldc.i4.0 IL_002c: ceq IL_002e: call ""void S.Write(bool)"" IL_0033: ldloc.0 IL_0034: ldloc.1 IL_0035: clt.un IL_0037: call ""void S.Write(bool)"" IL_003c: ldloc.0 IL_003d: ldloc.1 IL_003e: cgt.un IL_0040: call ""void S.Write(bool)"" IL_0045: ldloc.0 IL_0046: ldloc.1 IL_0047: ceq IL_0049: call ""void S.Write(bool)"" IL_004e: ldloc.0 IL_004f: ldloc.1 IL_0050: ceq IL_0052: ldc.i4.0 IL_0053: ceq IL_0055: call ""void S.Write(bool)"" IL_005a: ldloc.0 IL_005b: ldloc.1 IL_005c: cgt.un IL_005e: ldc.i4.0 IL_005f: ceq IL_0061: call ""void S.Write(bool)"" IL_0066: ldloc.0 IL_0067: ldloc.1 IL_0068: clt.un IL_006a: ldc.i4.0 IL_006b: ceq IL_006d: call ""void S.Write(bool)"" IL_0072: ldloc.0 IL_0073: ldloc.1 IL_0074: clt.un IL_0076: call ""void S.Write(bool)"" IL_007b: ldloc.0 IL_007c: ldloc.1 IL_007d: cgt.un IL_007f: call ""void S.Write(bool)"" IL_0084: ret } "); } [Fact, WorkItem(49639, "https://github.com/dotnet/roslyn/issues/49639")] public void CompareToNullWithNestedUnconstrainedTypeParameter() { var verifier = CompileAndVerify(@" using System; unsafe { test<int>(null); S<int> s = default; test<int>(&s); static void test<T>(S<T>* s) { Console.WriteLine(s == null); Console.WriteLine(s is null); } } struct S<T> {} ", options: TestOptions.UnsafeReleaseExe, expectedOutput: @" True True False False", verify: Verification.Skipped); verifier.VerifyIL("Program.<<Main>$>g__test|0_0<T>(S<T>*)", @" { // Code size 21 (0x15) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldc.i4.0 IL_0002: conv.u IL_0003: ceq IL_0005: call ""void System.Console.WriteLine(bool)"" IL_000a: ldarg.0 IL_000b: ldc.i4.0 IL_000c: conv.u IL_000d: ceq IL_000f: call ""void System.Console.WriteLine(bool)"" IL_0014: ret } "); } [Fact, WorkItem(49639, "https://github.com/dotnet/roslyn/issues/49639")] public void CompareToNullWithPointerToUnmanagedTypeParameter() { var verifier = CompileAndVerify(@" using System; unsafe { test<int>(null); int i = 0; test<int>(&i); static void test<T>(T* t) where T : unmanaged { Console.WriteLine(t == null); Console.WriteLine(t is null); } } ", options: TestOptions.UnsafeReleaseExe, expectedOutput: @" True True False False", verify: Verification.Skipped); verifier.VerifyIL("Program.<<Main>$>g__test|0_0<T>(T*)", @" { // Code size 21 (0x15) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldc.i4.0 IL_0002: conv.u IL_0003: ceq IL_0005: call ""void System.Console.WriteLine(bool)"" IL_000a: ldarg.0 IL_000b: ldc.i4.0 IL_000c: conv.u IL_000d: ceq IL_000f: call ""void System.Console.WriteLine(bool)"" IL_0014: ret } "); } [Theory] [InlineData("int*")] [InlineData("int*[]")] [InlineData("delegate*<void>")] [InlineData("T*")] [InlineData("delegate*<T>")] public void CompareToNullInPatternOutsideUnsafe(string pointerType) { var source = $@" var c = default(S<int>); _ = c.Field is null; unsafe struct S<T> where T : unmanaged {{ #pragma warning disable CS0649 // Field is unassigned public {pointerType} Field; }} "; CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics( // (3,5): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // _ = c.Field is null; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "c.Field").WithLocation(3, 5) ); var expectedDiagnostics = new[] { // (3,5): error CS9363: 'S<int>.Field' must be used in an unsafe context because it has pointers in its signature // _ = c.Field is null; Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "c.Field").WithArguments("S<int>.Field").WithLocation(3, 5), }; CreateCompilation(source, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); } #endregion Pointer comparison tests #region stackalloc tests [Fact] public void SimpleStackAlloc() { var text = @" unsafe class C { void M() { int count = 1; checked { int* p = stackalloc int[2]; char* q = stackalloc char[count]; Use(p); Use(q); } unchecked { int* p = stackalloc int[2]; char* q = stackalloc char[count]; Use(p); Use(q); } } static void Use(int * ptr) { } static void Use(char * ptr) { } } "; // NOTE: conversion is always unchecked, multiplication is always checked. CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails).VerifyIL("C.M", @" { // Code size 47 (0x2f) .maxstack 2 .locals init (int V_0, //count int* V_1, //p int* V_2) //p IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldc.i4.8 IL_0003: conv.u IL_0004: localloc IL_0006: stloc.1 IL_0007: ldloc.0 IL_0008: conv.u IL_0009: ldc.i4.2 IL_000a: mul.ovf.un IL_000b: localloc IL_000d: ldloc.1 IL_000e: call ""void C.Use(int*)"" IL_0013: call ""void C.Use(char*)"" IL_0018: ldc.i4.8 IL_0019: conv.u IL_001a: localloc IL_001c: stloc.2 IL_001d: ldloc.0 IL_001e: conv.u IL_001f: ldc.i4.2 IL_0020: mul.ovf.un IL_0021: localloc IL_0023: ldloc.2 IL_0024: call ""void C.Use(int*)"" IL_0029: call ""void C.Use(char*)"" IL_002e: ret } "); } [Fact] public void StackAllocConversion() { var text = @" unsafe class C { void M() { void* p = stackalloc int[2]; C q = stackalloc int[2]; } public static implicit operator C(int* p) { return null; } } "; CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails).VerifyIL("C.M", @" { // Code size 16 (0x10) .maxstack 1 .locals init (void* V_0) //p IL_0000: ldc.i4.8 IL_0001: conv.u IL_0002: localloc IL_0004: stloc.0 IL_0005: ldc.i4.8 IL_0006: conv.u IL_0007: localloc IL_0009: call ""C C.op_Implicit(int*)"" IL_000e: pop IL_000f: ret } "); } [Fact] public void StackAllocConversionZero() { var text = @" unsafe class C { void M() { void* p = stackalloc int[0]; C q = stackalloc int[0]; } public static implicit operator C(int* p) { return null; } } "; CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.FailsPEVerify).VerifyIL("C.M", @" { // Code size 12 (0xc) .maxstack 1 .locals init (void* V_0) //p IL_0000: ldc.i4.0 IL_0001: conv.u IL_0002: stloc.0 IL_0003: ldc.i4.0 IL_0004: conv.u IL_0005: call ""C C.op_Implicit(int*)"" IL_000a: pop IL_000b: ret } "); } [Fact] public void StackAllocSpecExample() //Section 18.8 { var text = @" using System; unsafe class C { static void Main() { Console.WriteLine(IntToString(123)); Console.WriteLine(IntToString(-456)); } static string IntToString(int value) { int n = value >= 0? value: -value; unsafe { char* buffer = stackalloc char[16]; char* p = buffer + 16; do { *--p = (char)(n % 10 + '0'); n /= 10; } while (n != 0); if (value < 0) *--p = '-'; return new string(p, 0, (int)(buffer + 16 - p)); } } } "; CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, verify: Verification.Fails, expectedOutput: @"123 -456 "); } // See MethodToClassRewriter.VisitAssignmentOperator for an explanation. [Fact] public void StackAllocIntoHoistedLocal1() { var text = @" using System; unsafe class C { static void Main() { var p = stackalloc int[2]; var q = stackalloc int[2]; Action a = () => { var r = stackalloc int[2]; var s = stackalloc int[2]; Action b = () => { p = null; //capture p r = null; //capture r }; Use(s); }; Use(q); } static void Use(int * ptr) { } } "; var verifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, verify: Verification.Fails); // Note that the stackalloc for p is written into a temp *before* the receiver (i.e. "this") // for C.<>c__DisplayClass0.p is pushed onto the stack. verifier.VerifyIL("C.Main", @" { // Code size 28 (0x1c) .maxstack 2 .locals init (C.<>c__DisplayClass0_0 V_0, //CS$<>8__locals0 int* V_1) IL_0000: newobj ""C.<>c__DisplayClass0_0..ctor()"" IL_0005: stloc.0 IL_0006: ldc.i4.8 IL_0007: conv.u IL_0008: localloc IL_000a: stloc.1 IL_000b: ldloc.0 IL_000c: ldloc.1 IL_000d: stfld ""int* C.<>c__DisplayClass0_0.p"" IL_0012: ldc.i4.8 IL_0013: conv.u IL_0014: localloc IL_0016: call ""void C.Use(int*)"" IL_001b: ret } "); // Check that the same thing works inside a lambda. verifier.VerifyIL("C.<>c__DisplayClass0_0.<Main>b__0", @" { // Code size 35 (0x23) .maxstack 2 .locals init (C.<>c__DisplayClass0_1 V_0, //CS$<>8__locals0 int* V_1) IL_0000: newobj ""C.<>c__DisplayClass0_1..ctor()"" IL_0005: stloc.0 IL_0006: ldloc.0 IL_0007: ldarg.0 IL_0008: stfld ""C.<>c__DisplayClass0_0 C.<>c__DisplayClass0_1.CS$<>8__locals1"" IL_000d: ldc.i4.8 IL_000e: conv.u IL_000f: localloc IL_0011: stloc.1 IL_0012: ldloc.0 IL_0013: ldloc.1 IL_0014: stfld ""int* C.<>c__DisplayClass0_1.r"" IL_0019: ldc.i4.8 IL_001a: conv.u IL_001b: localloc IL_001d: call ""void C.Use(int*)"" IL_0022: ret } "); } // See MethodToClassRewriter.VisitAssignmentOperator for an explanation. [Fact] public void StackAllocIntoHoistedLocal2() { // From native bug #59454 (in DevDiv collection) var text = @" unsafe class T { delegate int D(); static void Main() { int* v = stackalloc int[1]; D d = delegate { return *v; }; System.Console.WriteLine(d()); } } "; CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: "0", verify: Verification.Fails).VerifyIL("T.Main", @" { // Code size 41 (0x29) .maxstack 2 .locals init (T.<>c__DisplayClass1_0 V_0, //CS$<>8__locals0 int* V_1) IL_0000: newobj ""T.<>c__DisplayClass1_0..ctor()"" IL_0005: stloc.0 IL_0006: ldc.i4.4 IL_0007: conv.u IL_0008: localloc IL_000a: stloc.1 IL_000b: ldloc.0 IL_000c: ldloc.1 IL_000d: stfld ""int* T.<>c__DisplayClass1_0.v"" IL_0012: ldloc.0 IL_0013: ldftn ""int T.<>c__DisplayClass1_0.<Main>b__0()"" IL_0019: newobj ""T.D..ctor(object, System.IntPtr)"" IL_001e: callvirt ""int T.D.Invoke()"" IL_0023: call ""void System.Console.WriteLine(int)"" IL_0028: ret } "); CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: "0", verify: Verification.Fails).VerifyIL("T.Main", @" { // Code size 41 (0x29) .maxstack 2 .locals init (T.<>c__DisplayClass1_0 V_0, //CS$<>8__locals0 int* V_1) IL_0000: newobj ""T.<>c__DisplayClass1_0..ctor()"" IL_0005: stloc.0 IL_0006: ldc.i4.4 IL_0007: conv.u IL_0008: localloc IL_000a: stloc.1 IL_000b: ldloc.0 IL_000c: ldloc.1 IL_000d: stfld ""int* T.<>c__DisplayClass1_0.v"" IL_0012: ldloc.0 IL_0013: ldftn ""int T.<>c__DisplayClass1_0.<Main>b__0()"" IL_0019: newobj ""T.D..ctor(object, System.IntPtr)"" IL_001e: callvirt ""int T.D.Invoke()"" IL_0023: call ""void System.Console.WriteLine(int)"" IL_0028: ret } "); } [Fact] public void CSLegacyStackallocUse32bitChecked() { // This is from C# Legacy test where it uses Perl script to call ildasm and check 'mul.ovf' emitted // $Roslyn\Main\LegacyTest\CSharp\Source\csharp\Source\Conformance\unsafecode\stackalloc\regr001.cs var text = @"// <Title>Should checked affect stackalloc?</Title> // <Description> // The lower level localloc MSIL instruction takes an unsigned native int as input; however the higher level // stackalloc uses only 32-bits. The example shows the operation overflowing the 32-bit multiply which leads to // a curious edge condition. // If compile with /checked we insert a mul.ovf instruction, and this causes a system overflow exception at runtime. // </Description> // <RelatedBugs>VSW:489857</RelatedBugs> using System; public class C { private static unsafe int Main() { Int64* intArray = stackalloc Int64[0x7fffffff]; return (int)intArray[0]; } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails); compVerifier.VerifyIL("C.Main", @" { // Code size 13 (0xd) .maxstack 2 IL_0000: ldc.i4 0x7fffffff IL_0005: conv.u IL_0006: ldc.i4.8 IL_0007: mul.ovf.un IL_0008: localloc IL_000a: ldind.i8 IL_000b: conv.i4 IL_000c: ret } "); } #endregion stackalloc tests #region Functional tests [Fact] public void BubbleSort() { var text = @" using System; unsafe class C { static void Main() { BubbleSort(); BubbleSort(1); BubbleSort(2, 1); BubbleSort(3, 1, 2); BubbleSort(3, 1, 4, 2); } static void BubbleSort(params int[] array) { if (array == null) { return; } fixed (int* begin = array) { BubbleSort(begin, end: begin + array.Length); } Console.WriteLine(string.Join("", "", array)); } private static void BubbleSort(int* begin, int* end) { for (int* firstUnsorted = begin; firstUnsorted < end; firstUnsorted++) { for (int* current = firstUnsorted; current + 1 < end; current++) { if (current[0] > current[1]) { SwapWithNext(current); } } } } static void SwapWithNext(int* p) { int temp = *p; p[0] = p[1]; p[1] = temp; } } "; CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, verify: Verification.Fails, expectedOutput: @" 1 1, 2 1, 2, 3 1, 2, 3, 4"); } [Fact] public void BigStructs() { var text = @" unsafe class C { static void Main() { void* v; CheckOverflow(""(S15*)0 + sizeof(S15)"", () => v = checked((S15*)0 + sizeof(S15))); CheckOverflow(""(S15*)0 + sizeof(S16)"", () => v = checked((S15*)0 + sizeof(S16))); CheckOverflow(""(S16*)0 + sizeof(S15)"", () => v = checked((S16*)0 + sizeof(S15))); } static void CheckOverflow(string description, System.Action operation) { try { operation(); System.Console.WriteLine(""No overflow from {0}"", description); } catch (System.OverflowException) { System.Console.WriteLine(""Overflow from {0}"", description); } } } " + SizedStructs; bool isx86 = (IntPtr.Size == 4); string expectedOutput; if (isx86) { expectedOutput = @" No overflow from (S15*)0 + sizeof(S15) Overflow from (S15*)0 + sizeof(S16) Overflow from (S16*)0 + sizeof(S15)"; } else { expectedOutput = @" No overflow from (S15*)0 + sizeof(S15) No overflow from (S15*)0 + sizeof(S16) No overflow from (S16*)0 + sizeof(S15)"; } // PEVerify: // [ : C+<>c__DisplayClass0_0::<Main>b__0][mdToken=0x6000005][offset 0x00000012][found Native Int][expected unmanaged pointer] Unexpected type on the stack. // [ : C+<> c__DisplayClass0_0::< Main > b__1][mdToken= 0x6000006][offset 0x00000012][found Native Int][expected unmanaged pointer] Unexpected type on the stack. // [ : C +<> c__DisplayClass0_0::< Main > b__2][mdToken = 0x6000007][offset 0x00000012][found Native Int][expected unmanaged pointer] Unexpected type on the stack. CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: expectedOutput, verify: Verification.FailsPEVerify); } [Fact] public void LambdaConversion() { var text = @" using System; class Program { static void Main() { Goo(x => { }); } static void Goo(F1 f) { Console.WriteLine(1); } static void Goo(F2 f) { Console.WriteLine(2); } } unsafe delegate void F1(int* x); delegate void F2(int x); "; CompileAndVerify(text, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe, expectedOutput: @"2", verify: Verification.Passes); var expectedPreviewDiagnostics = new[] { // (8,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.Goo(F1)' and 'Program.Goo(F2)' // Goo(x => { }); Diagnostic(ErrorCode.ERR_AmbigCall, "Goo").WithArguments("Program.Goo(F1)", "Program.Goo(F2)").WithLocation(8, 9) }; CreateCompilation(text, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedPreviewDiagnostics); CreateCompilation(text, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedPreviewDiagnostics); } [Fact] public void LambdaConversion_PointerArray() { var text = @" using System; class C<T> { } class Program { static void Main() { M(x => { }); } static void M(F1 f) { throw null; } static void M(F2 f) { Console.WriteLine(2); } } unsafe delegate void F1(C<int*[]> x); delegate void F2(int x); "; var comp = CreateCompilation(text, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeDebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "2"); var expectedPreviewDiagnostics = new[] { // (10,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.M(F1)' and 'Program.M(F2)' // M(x => { }); Diagnostic(ErrorCode.ERR_AmbigCall, "M").WithArguments("Program.M(F1)", "Program.M(F2)").WithLocation(10, 9) }; CreateCompilation(text, options: TestOptions.UnsafeDebugExe).VerifyDiagnostics(expectedPreviewDiagnostics); CreateCompilation(text, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeDebugExe).VerifyDiagnostics(expectedPreviewDiagnostics); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67330")] public void ParameterContainsPointer() { var source = """ class C<T> { } class D { public static void M1() { var lam1 = (int* ptr) => ptr; // 1 } public static void M2() { var lam2 = (int*[] a) => a; // 2 } public static void M3() { var lam3 = (delegate*<void> ptr) => ptr; // 3 } public static void M4() { var lam4 = (C<delegate*<void>[]> a) => a; // 4 } } """; var comp = CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeDebugDll); comp.VerifyDiagnostics( // (6,21): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // var lam1 = (int* ptr) => ptr; // 1 Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(6, 21), // (6,26): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // var lam1 = (int* ptr) => ptr; // 1 Diagnostic(ErrorCode.ERR_UnsafeNeeded, "ptr").WithLocation(6, 26), // (6,34): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // var lam1 = (int* ptr) => ptr; // 1 Diagnostic(ErrorCode.ERR_UnsafeNeeded, "ptr").WithLocation(6, 34), // (10,21): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // var lam2 = (int*[] a) => a; // 2 Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(10, 21), // (10,28): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // var lam2 = (int*[] a) => a; // 2 Diagnostic(ErrorCode.ERR_UnsafeNeeded, "a").WithLocation(10, 28), // (10,34): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // var lam2 = (int*[] a) => a; // 2 Diagnostic(ErrorCode.ERR_UnsafeNeeded, "a").WithLocation(10, 34), // (14,21): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // var lam3 = (delegate*<void> ptr) => ptr; // 3 Diagnostic(ErrorCode.ERR_UnsafeNeeded, "delegate*").WithLocation(14, 21), // (14,37): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // var lam3 = (delegate*<void> ptr) => ptr; // 3 Diagnostic(ErrorCode.ERR_UnsafeNeeded, "ptr").WithLocation(14, 37), // (14,45): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // var lam3 = (delegate*<void> ptr) => ptr; // 3 Diagnostic(ErrorCode.ERR_UnsafeNeeded, "ptr").WithLocation(14, 45), // (18,23): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // var lam4 = (C<delegate*<void>[]> a) => a; // 4 Diagnostic(ErrorCode.ERR_UnsafeNeeded, "delegate*").WithLocation(18, 23), // (18,42): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // var lam4 = (C<delegate*<void>[]> a) => a; // 4 Diagnostic(ErrorCode.ERR_UnsafeNeeded, "a").WithLocation(18, 42), // (18,48): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // var lam4 = (C<delegate*<void>[]> a) => a; // 4 Diagnostic(ErrorCode.ERR_UnsafeNeeded, "a").WithLocation(18, 48) ); CreateCompilation(source, options: TestOptions.UnsafeDebugDll).VerifyDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeDebugDll).VerifyDiagnostics(); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67330")] public void DelegateConversionContainsPointer() { var source = """ class C<T> { } unsafe delegate int* D1(int* ptr); unsafe delegate int*[] D2(int*[] a); unsafe delegate delegate*<void> D3(delegate*<void> ptr); unsafe delegate C<delegate*<void>[]> D4(C<delegate*<void>[]> a); class D { public static D1 M1() { return (ptr) => ptr; // 1 } public static D2 M2() { return (a) => a; // 2 } public static D3 M3() { return (ptr) => ptr; // 3 } public static D4 M4() { return (a) => a; // 4 } } """; var comp = CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeDebugDll); comp.VerifyDiagnostics( // (11,17): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // return (ptr) => ptr; // 1 Diagnostic(ErrorCode.ERR_UnsafeNeeded, "ptr").WithLocation(11, 17), // (11,25): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // return (ptr) => ptr; // 1 Diagnostic(ErrorCode.ERR_UnsafeNeeded, "ptr").WithLocation(11, 25), // (15,17): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // return (a) => a; // 2 Diagnostic(ErrorCode.ERR_UnsafeNeeded, "a").WithLocation(15, 17), // (15,23): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // return (a) => a; // 2 Diagnostic(ErrorCode.ERR_UnsafeNeeded, "a").WithLocation(15, 23), // (19,17): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // return (ptr) => ptr; // 3 Diagnostic(ErrorCode.ERR_UnsafeNeeded, "ptr").WithLocation(19, 17), // (19,25): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // return (ptr) => ptr; // 3 Diagnostic(ErrorCode.ERR_UnsafeNeeded, "ptr").WithLocation(19, 25), // (23,17): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // return (a) => a; // 4 Diagnostic(ErrorCode.ERR_UnsafeNeeded, "a").WithLocation(23, 17), // (23,23): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // return (a) => a; // 4 Diagnostic(ErrorCode.ERR_UnsafeNeeded, "a").WithLocation(23, 23) ); CreateCompilation(source, options: TestOptions.UnsafeDebugDll).VerifyDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeDebugDll).VerifyDiagnostics(); } [Fact] public void LocalVariableReuse() { var text = @" unsafe class C { int this[string s] { get { return 0; } set { } } void Test() { { this[""not pinned"".ToString()] += 2; //creates an unpinned string local (for the argument) } fixed (char* p = ""pinned"") //creates a pinned string local { } { this[""not pinned"".ToString()] += 2; //reuses the unpinned string local } fixed (char* p = ""pinned"") //reuses the pinned string local { } } } "; // NOTE: one pinned string temp and one unpinned string temp. // That is, pinned temps are reused in by other pinned temps // but not by unpinned temps and vice versa. CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails).VerifyIL("C.Test", @" { // Code size 99 (0x63) .maxstack 4 .locals init (string V_0, char* V_1, //p pinned string V_2, char* V_3) //p IL_0000: ldstr ""not pinned"" IL_0005: callvirt ""string object.ToString()"" IL_000a: stloc.0 IL_000b: ldarg.0 IL_000c: ldloc.0 IL_000d: ldarg.0 IL_000e: ldloc.0 IL_000f: call ""int C.this[string].get"" IL_0014: ldc.i4.2 IL_0015: add IL_0016: call ""void C.this[string].set"" IL_001b: ldstr ""pinned"" IL_0020: stloc.2 IL_0021: ldloc.2 IL_0022: conv.u IL_0023: stloc.1 IL_0024: ldloc.1 IL_0025: brfalse.s IL_002f IL_0027: ldloc.1 IL_0028: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_002d: add IL_002e: stloc.1 IL_002f: ldnull IL_0030: stloc.2 IL_0031: ldstr ""not pinned"" IL_0036: callvirt ""string object.ToString()"" IL_003b: stloc.0 IL_003c: ldarg.0 IL_003d: ldloc.0 IL_003e: ldarg.0 IL_003f: ldloc.0 IL_0040: call ""int C.this[string].get"" IL_0045: ldc.i4.2 IL_0046: add IL_0047: call ""void C.this[string].set"" IL_004c: ldstr ""pinned"" IL_0051: stloc.2 IL_0052: ldloc.2 IL_0053: conv.u IL_0054: stloc.3 IL_0055: ldloc.3 IL_0056: brfalse.s IL_0060 IL_0058: ldloc.3 IL_0059: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get"" IL_005e: add IL_005f: stloc.3 IL_0060: ldnull IL_0061: stloc.2 IL_0062: ret }"); } [WorkItem(544229, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544229")] [Fact] public void UnsafeTypeAsAttributeArgument() { var template = @" using System; namespace System {{ class Int32 {{ }} }} [A(Type = typeof({0}))] class A : Attribute {{ public Type Type; static void Main() {{ var a = (A)typeof(A).GetCustomAttributes(false)[0]; Console.WriteLine(a.Type == typeof({0})); }} }} "; CompileAndVerify(string.Format(template, "int"), options: TestOptions.UnsafeReleaseExe, expectedOutput: @"True", verify: Verification.Passes); CompileAndVerify(string.Format(template, "int*"), options: TestOptions.UnsafeReleaseExe, expectedOutput: @"True", verify: Verification.Passes); CompileAndVerify(string.Format(template, "int**"), options: TestOptions.UnsafeReleaseExe, expectedOutput: @"True", verify: Verification.Passes); CompileAndVerify(string.Format(template, "int[]"), options: TestOptions.UnsafeReleaseExe, expectedOutput: @"True", verify: Verification.Passes); CompileAndVerify(string.Format(template, "int[][]"), options: TestOptions.UnsafeReleaseExe, expectedOutput: @"True", verify: Verification.Passes); CompileAndVerify(string.Format(template, "int*[]"), options: TestOptions.UnsafeReleaseExe, expectedOutput: @"True", verify: Verification.Passes); } #endregion Functional tests #region Regression tests [WorkItem(545026, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545026")] [Fact] public void MixedSafeAndUnsafeFields() { var text = @"struct Perf_Contexts { int data; private int SuppressUnused(int x) { data = x; return data; } } public sealed class ChannelServices { static unsafe Perf_Contexts* GetPrivateContextsPerfCounters() { return null; } private static int I1 = 12; unsafe private static Perf_Contexts* perf_Contexts = GetPrivateContextsPerfCounters(); private static int I2 = 13; private static int SuppressUnused(int x) { return I1 + I2; } } public class Test { public static void Main() { } } "; CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, verify: Verification.FailsPEVerify with { PEVerifyMessage = """ [ : ChannelServices::.cctor][offset 0x0000000C][found unmanaged pointer][expected unmanaged pointer] Unexpected type on the stack. [ : ChannelServices::GetPrivateContextsPerfCounters][offset 0x00000002][found Native Int][expected unmanaged pointer] Unexpected type on the stack. """, }).VerifyDiagnostics(); } [WorkItem(545026, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545026")] [Fact] public void SafeFieldBeforeUnsafeField() { var text = @" class C { int x = 1; unsafe int* p = (int*)2; } "; var c = CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.FailsPEVerify with { PEVerifyMessage = "[ : C::.ctor][offset 0x0000000A][found Native Int][expected unmanaged pointer] Unexpected type on the stack." }); c.VerifyDiagnostics( // (4,9): warning CS0414: The field 'C.x' is assigned but its value is never used // int x = 1; Diagnostic(ErrorCode.WRN_UnreferencedFieldAssg, "x").WithArguments("C.x")); } [WorkItem(545026, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545026")] [Fact] public void SafeFieldAfterUnsafeField() { var text = @" class C { unsafe int* p = (int*)2; int x = 1; } "; var c = CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.FailsPEVerify with { PEVerifyMessage = "[ : C::.ctor][offset 0x00000003][found Native Int][expected unmanaged pointer] Unexpected type on the stack." }); c.VerifyDiagnostics( // (5,9): warning CS0414: The field 'C.x' is assigned but its value is never used // int x = 1; Diagnostic(ErrorCode.WRN_UnreferencedFieldAssg, "x").WithArguments("C.x")); } [WorkItem(545026, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545026"), WorkItem(598170, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/598170")] [Fact] public void FixedPassByRef() { var text = @" class Test { unsafe static int printAddress(out int* pI) { pI = null; System.Console.WriteLine((ulong)pI); return 1; } unsafe static int printAddress1(ref int* pI) { pI = null; System.Console.WriteLine((ulong)pI); return 1; } static int Main() { int retval = 0; S s = new S(); unsafe { retval = Test.printAddress(out s.i); retval = Test.printAddress1(ref s.i); } if (retval == 0) System.Console.WriteLine(""Failed.""); return retval; } } unsafe struct S { public fixed int i[1]; } "; var comp = CreateCompilation(text, options: TestOptions.UnsafeReleaseDll); comp.VerifyDiagnostics( // (24,44): error CS1510: A ref or out argument must be an assignable variable // retval = Test.printAddress(out s.i); Diagnostic(ErrorCode.ERR_RefLvalueExpected, "s.i"), // (25,45): error CS1510: A ref or out argument must be an assignable variable // retval = Test.printAddress1(ref s.i); Diagnostic(ErrorCode.ERR_RefLvalueExpected, "s.i")); } [Fact, WorkItem(545293, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545293"), WorkItem(881188, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/881188")] public void EmptyAndFixedBufferStructIsInitialized() { var text = @" public struct EmptyStruct { } unsafe public struct FixedStruct { fixed char c[10]; } public struct OuterStruct { EmptyStruct ES; FixedStruct FS; override public string ToString() { return (ES.ToString() + FS.ToString()); } } "; CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.Passes).VerifyDiagnostics( // (8,17): warning CS0649: Field 'OuterStruct.FS' is never assigned to, and will always have its default value // FixedStruct FS; Diagnostic(ErrorCode.WRN_UnassignedInternalField, "FS").WithArguments("OuterStruct.FS", "").WithLocation(8, 17), // (7,17): warning CS0649: Field 'OuterStruct.ES' is never assigned to, and will always have its default value // EmptyStruct ES; Diagnostic(ErrorCode.WRN_UnassignedInternalField, "ES").WithArguments("OuterStruct.ES", "").WithLocation(7, 17) ); } [Fact, WorkItem(545296, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545296"), WorkItem(545999, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545999")] public void FixedBufferAndStatementWithFixedArrayElementAsInitializer() { var text = @" unsafe public struct FixedStruct { fixed int i[1]; fixed char c[10]; override public string ToString() { fixed (char* pc = this.c) { return pc[0].ToString(); } } } "; var comp = CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails).VerifyDiagnostics(); comp.VerifyIL("FixedStruct.ToString", @" { // Code size 20 (0x14) .maxstack 1 .locals init (pinned char& V_0) IL_0000: ldarg.0 IL_0001: ldflda ""char* FixedStruct.c"" IL_0006: ldflda ""char FixedStruct.<c>e__FixedBuffer.FixedElementField"" IL_000b: stloc.0 IL_000c: ldloc.0 IL_000d: conv.u IL_000e: call ""string char.ToString()"" IL_0013: ret } "); } [Fact] public void FixedBufferAndStatementWithFixedArrayElementAsInitializerExe() { var text = @" class Program { unsafe static void Main(string[] args) { FixedStruct s = new FixedStruct(); s.c[0] = 'A'; s.c[1] = 'B'; s.c[2] = 'C'; FixedStruct[] arr = { s }; System.Console.Write(arr[0].ToString()); } } unsafe public struct FixedStruct { public fixed char c[10]; override public string ToString() { fixed (char* pc = this.c) { System.Console.Write(pc[0]); System.Console.Write(pc[1].ToString()); return pc[2].ToString(); } } }"; var comp = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: "ABC", verify: Verification.Fails).VerifyDiagnostics(); comp.VerifyIL("FixedStruct.ToString", @" { // Code size 45 (0x2d) .maxstack 3 .locals init (pinned char& V_0) IL_0000: ldarg.0 IL_0001: ldflda ""char* FixedStruct.c"" IL_0006: ldflda ""char FixedStruct.<c>e__FixedBuffer.FixedElementField"" IL_000b: stloc.0 IL_000c: ldloc.0 IL_000d: conv.u IL_000e: dup IL_000f: ldind.u2 IL_0010: call ""void System.Console.Write(char)"" IL_0015: dup IL_0016: ldc.i4.2 IL_0017: add IL_0018: call ""string char.ToString()"" IL_001d: call ""void System.Console.Write(string)"" IL_0022: ldc.i4.2 IL_0023: conv.i IL_0024: ldc.i4.2 IL_0025: mul IL_0026: add IL_0027: call ""string char.ToString()"" IL_002c: ret } "); } [Fact, WorkItem(545299, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545299")] public void FixedStatementInlambda() { var text = @" using System; using System.Diagnostics; using System.Collections; using System.Collections.Generic; unsafe class C<T> where T : struct { public void Goo() { Func<T, char> d = delegate { fixed (char* p = ""blah"") { for (char* pp = p; pp != null; pp++) return *pp; } return 'c'; }; Console.WriteLine(d(default(T))); } } class A { static void Main() { new C<int>().Goo(); } } "; CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: "b", verify: Verification.Fails); } [Fact, WorkItem(546865, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546865")] public void DontStackScheduleLocalPerformingPointerConversion() { var text = @" using System; unsafe struct S1 { public char* charPointer; } unsafe class Test { static void Main() { S1 s1 = new S1(); fixed (char* p = ""hello"") { s1.charPointer = p; ulong UserData = (ulong)&s1; Test1(UserData); } } static void Test1(ulong number) { S1* structPointer = (S1*)number; Console.WriteLine(new string(structPointer->charPointer)); } static ulong Test2() { S1* structPointer = (S1*)null; // null to pointer int* intPointer = (int*)structPointer; // pointer to pointer void* voidPointer = (void*)intPointer; // pointer to void ulong number = (ulong)voidPointer; // pointer to integer return number; } } "; var verifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: "hello", verify: Verification.Fails); // Note that the pointer local is not scheduled on the stack. verifier.VerifyIL("Test.Test1", @" { // Code size 20 (0x14) .maxstack 1 .locals init (S1* V_0) //structPointer IL_0000: ldarg.0 IL_0001: conv.u IL_0002: stloc.0 IL_0003: ldloc.0 IL_0004: ldfld ""char* S1.charPointer"" IL_0009: newobj ""string..ctor(char*)"" IL_000e: call ""void System.Console.WriteLine(string)"" IL_0013: ret }"); // All locals retained. verifier.VerifyIL("Test.Test2", @" { // Code size 10 (0xa) .maxstack 1 .locals init (S1* V_0, //structPointer int* V_1, //intPointer void* V_2) //voidPointer IL_0000: ldc.i4.0 IL_0001: conv.u IL_0002: stloc.0 IL_0003: ldloc.0 IL_0004: stloc.1 IL_0005: ldloc.1 IL_0006: stloc.2 IL_0007: ldloc.2 IL_0008: conv.u8 IL_0009: ret }"); } [Fact, WorkItem(546807, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546807")] public void PointerMemberAccessReadonlyField() { var text = @" using System; unsafe class C { public S1* S1; } unsafe struct S1 { public readonly int* X; public int* Y; } unsafe class Test { static void Main() { S1 s1 = new S1(); C c = new C(); c.S1 = &s1; Console.WriteLine(null == c.S1->X); Console.WriteLine(null == c.S1->Y); } } "; var verifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, verify: Verification.Fails, expectedOutput: @" True True"); // NOTE: ldobj before ldfld S1.X, but not before ldfld S1.Y. verifier.VerifyIL("Test.Main", @" { // Code size 64 (0x40) .maxstack 2 .locals init (S1 V_0, //s1 C V_1) //c IL_0000: ldloca.s V_0 IL_0002: initobj ""S1"" IL_0008: newobj ""C..ctor()"" IL_000d: stloc.1 IL_000e: ldloc.1 IL_000f: ldloca.s V_0 IL_0011: conv.u IL_0012: stfld ""S1* C.S1"" IL_0017: ldc.i4.0 IL_0018: conv.u IL_0019: ldloc.1 IL_001a: ldfld ""S1* C.S1"" IL_001f: ldfld ""int* S1.X"" IL_0024: ceq IL_0026: call ""void System.Console.WriteLine(bool)"" IL_002b: ldc.i4.0 IL_002c: conv.u IL_002d: ldloc.1 IL_002e: ldfld ""S1* C.S1"" IL_0033: ldfld ""int* S1.Y"" IL_0038: ceq IL_003a: call ""void System.Console.WriteLine(bool)"" IL_003f: ret } "); } [Fact, WorkItem(546807, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546807")] public void PointerMemberAccessCall() { var text = @" using System; unsafe class C { public S1* S1; } unsafe struct S1 { public int X; public void Instance() { Console.WriteLine(this.X); } } static class Extensions { public static void Extension(this S1 s1) { Console.WriteLine(s1.X); } } unsafe class Test { static void Main() { S1 s1 = new S1 { X = 2 }; C c = new C(); c.S1 = &s1; c.S1->Instance(); c.S1->Extension(); } } "; var verifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, verify: Verification.Fails, expectedOutput: @" 2 2"); // NOTE: ldobj before extension call, but not before instance call. verifier.VerifyIL("Test.Main", @" { // Code size 59 (0x3b) .maxstack 3 .locals init (S1 V_0, //s1 S1 V_1) IL_0000: ldloca.s V_1 IL_0002: initobj ""S1"" IL_0008: ldloca.s V_1 IL_000a: ldc.i4.2 IL_000b: stfld ""int S1.X"" IL_0010: ldloc.1 IL_0011: stloc.0 IL_0012: newobj ""C..ctor()"" IL_0017: dup IL_0018: ldloca.s V_0 IL_001a: conv.u IL_001b: stfld ""S1* C.S1"" IL_0020: dup IL_0021: ldfld ""S1* C.S1"" IL_0026: call ""void S1.Instance()"" IL_002b: ldfld ""S1* C.S1"" IL_0030: ldobj ""S1"" IL_0035: call ""void Extensions.Extension(S1)"" IL_003a: ret }"); } [Fact, WorkItem(531327, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/531327")] public void PointerParameter() { var text = @" using System; unsafe struct S1 { static void M(N.S2* ps2){} } namespace N { public struct S2 { public int F; } } "; var verifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll.WithConcurrentBuild(false), verify: Verification.Passes); } [Fact, WorkItem(531327, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/531327")] public void PointerReturn() { var text = @" using System; namespace N { public struct S2 { public int F; } } unsafe struct S1 { static N.S2* M(int ps2){return null;} } "; var verifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseDll.WithConcurrentBuild(false), verify: Verification.FailsPEVerify); } [Fact, WorkItem(748530, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/748530")] public void Repro748530() { var text = @" unsafe class A { public unsafe struct ListNode { internal ListNode(int data, ListNode* pNext) { } } } "; var comp = CreateCompilation(text, options: TestOptions.UnsafeReleaseDll); comp.VerifyDiagnostics(); } [WorkItem(682584, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/682584")] [Fact] public void UnsafeMathConv() { var text = @" using System; unsafe class C { static void Main(string[] args) { byte* data = (byte*)0x76543210; uint offset = 0x80000000; byte* wrong = data + offset; Console.WriteLine(""{0:X}"", (ulong)wrong); } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: "F6543210", verify: Verification.Fails); compVerifier.VerifyIL("C.Main", @" { // Code size 36 (0x24) .maxstack 2 .locals init (byte* V_0, //data uint V_1, //offset byte* V_2) //wrong IL_0000: ldc.i4 0x76543210 IL_0005: conv.i IL_0006: stloc.0 IL_0007: ldc.i4 0x80000000 IL_000c: stloc.1 IL_000d: ldloc.0 IL_000e: ldloc.1 IL_000f: conv.u IL_0010: add IL_0011: stloc.2 IL_0012: ldstr ""{0:X}"" IL_0017: ldloc.2 IL_0018: conv.u8 IL_0019: box ""ulong"" IL_001e: call ""void System.Console.WriteLine(string, object)"" IL_0023: ret } "); } [WorkItem(682584, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/682584")] [Fact] public void UnsafeMathConv001() { var text = @" using System; unsafe class C { static void Main(string[] args) { short* data = (short*)0x76543210; uint offset = 0x40000000; short* wrong = data + offset; Console.WriteLine(""{0:X}"", (ulong)wrong); } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: "F6543210", verify: Verification.Fails); compVerifier.VerifyIL("C.Main", @" { // Code size 40 (0x28) .maxstack 3 .locals init (short* V_0, //data uint V_1, //offset short* V_2) //wrong IL_0000: ldc.i4 0x76543210 IL_0005: conv.i IL_0006: stloc.0 IL_0007: ldc.i4 0x40000000 IL_000c: stloc.1 IL_000d: ldloc.0 IL_000e: ldloc.1 IL_000f: conv.u8 IL_0010: ldc.i4.2 IL_0011: conv.i8 IL_0012: mul IL_0013: conv.i IL_0014: add IL_0015: stloc.2 IL_0016: ldstr ""{0:X}"" IL_001b: ldloc.2 IL_001c: conv.u8 IL_001d: box ""ulong"" IL_0022: call ""void System.Console.WriteLine(string, object)"" IL_0027: ret } "); } [WorkItem(682584, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/682584")] [Fact] public void UnsafeMathConv002() { var text = @" using System; unsafe class C { static void Main(string[] args) { byte* data = (byte*)0x76543210; byte* wrong = data + 0x80000000u; Console.WriteLine(""{0:X}"", (ulong)wrong); } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: "F6543210", verify: Verification.Fails); compVerifier.VerifyIL("C.Main", @" { // Code size 34 (0x22) .maxstack 2 .locals init (byte* V_0, //data byte* V_1) //wrong IL_0000: ldc.i4 0x76543210 IL_0005: conv.i IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: ldc.i4 0x80000000 IL_000d: conv.u IL_000e: add IL_000f: stloc.1 IL_0010: ldstr ""{0:X}"" IL_0015: ldloc.1 IL_0016: conv.u8 IL_0017: box ""ulong"" IL_001c: call ""void System.Console.WriteLine(string, object)"" IL_0021: ret } "); } [WorkItem(682584, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/682584")] [Fact] public void UnsafeMathConv002a() { var text = @" using System; unsafe class C { static void Main(string[] args) { byte* data = (byte*)0x76543210; byte* wrong = data + 0x7FFFFFFFu; Console.WriteLine(""{0:X}"", (ulong)wrong); } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: "F654320F", verify: Verification.Fails); compVerifier.VerifyIL("C.Main", @" { // Code size 33 (0x21) .maxstack 2 .locals init (byte* V_0, //data byte* V_1) //wrong IL_0000: ldc.i4 0x76543210 IL_0005: conv.i IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: ldc.i4 0x7fffffff IL_000d: add IL_000e: stloc.1 IL_000f: ldstr ""{0:X}"" IL_0014: ldloc.1 IL_0015: conv.u8 IL_0016: box ""ulong"" IL_001b: call ""void System.Console.WriteLine(string, object)"" IL_0020: ret } "); } [WorkItem(857598, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/857598")] [Fact] public void VoidToNullable() { var text = @" unsafe class C { public int? x = (int?)(void*)0; } class c1 { public static void Main() { var x = new C(); System.Console.WriteLine(x.x); } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: "0", verify: Verification.Passes); compVerifier.VerifyIL("C..ctor", @" { // Code size 21 (0x15) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldc.i4.0 IL_0002: conv.i IL_0003: conv.i4 IL_0004: newobj ""int?..ctor(int)"" IL_0009: stfld ""int? C.x"" IL_000e: ldarg.0 IL_000f: call ""object..ctor()"" IL_0014: ret } "); } [WorkItem(907771, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/907771")] [Fact] public void UnsafeBeforeReturn001() { var text = @" using System; public unsafe class C { private static readonly byte[] _emptyArray = new byte[0]; public static void Main() { System.Console.WriteLine(ToManagedByteArray(2)); } public static byte[] ToManagedByteArray(uint byteCount) { if (byteCount == 0) { return _emptyArray; // degenerate case } else { byte[] bytes = new byte[byteCount]; fixed (byte* pBytes = bytes) { } return bytes; } } } "; var compVerifier = CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: "System.Byte[]", verify: Verification.Fails); compVerifier.VerifyIL("C.ToManagedByteArray", @" { // Code size 42 (0x2a) .maxstack 3 .locals init (byte* V_0, //pBytes pinned byte[] V_1) IL_0000: ldarg.0 IL_0001: brtrue.s IL_0009 IL_0003: ldsfld ""byte[] C._emptyArray"" IL_0008: ret IL_0009: ldarg.0 IL_000a: newarr ""byte"" IL_000f: dup IL_0010: dup IL_0011: stloc.1 IL_0012: brfalse.s IL_0019 IL_0014: ldloc.1 IL_0015: ldlen IL_0016: conv.i4 IL_0017: brtrue.s IL_001e IL_0019: ldc.i4.0 IL_001a: conv.u IL_001b: stloc.0 IL_001c: br.s IL_0027 IL_001e: ldloc.1 IL_001f: ldc.i4.0 IL_0020: ldelema ""byte"" IL_0025: conv.u IL_0026: stloc.0 IL_0027: ldnull IL_0028: stloc.1 IL_0029: ret } "); } [WorkItem(907771, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/907771")] [Fact] public void UnsafeBeforeReturn002() { var text = @" using System; public unsafe class C { private static readonly byte[] _emptyArray = new byte[0]; public static void Main() { System.Console.WriteLine(ToManagedByteArray(2)); } public static byte[] ToManagedByteArray(uint byteCount) { if (byteCount == 0) { return _emptyArray; // degenerate case } else { byte[] bytes = new byte[byteCount]; fixed (byte* pBytes = bytes) { } return bytes; } } } "; var v = CompileAndVerify(text, options: TestOptions.UnsafeDebugExe, expectedOutput: "System.Byte[]", verify: Verification.Fails); v.VerifyIL("C.ToManagedByteArray", @" { // Code size 64 (0x40) .maxstack 2 .locals init (bool V_0, byte[] V_1, byte[] V_2, //bytes byte* V_3, //pBytes pinned byte[] V_4) -IL_0000: nop -IL_0001: ldarg.0 IL_0002: ldc.i4.0 IL_0003: ceq IL_0005: stloc.0 ~IL_0006: ldloc.0 IL_0007: brfalse.s IL_0012 -IL_0009: nop -IL_000a: ldsfld ""byte[] C._emptyArray"" IL_000f: stloc.1 IL_0010: br.s IL_003e -IL_0012: nop -IL_0013: ldarg.0 IL_0014: newarr ""byte"" IL_0019: stloc.2 -IL_001a: ldloc.2 IL_001b: dup IL_001c: stloc.s V_4 IL_001e: brfalse.s IL_0026 IL_0020: ldloc.s V_4 IL_0022: ldlen IL_0023: conv.i4 IL_0024: brtrue.s IL_002b IL_0026: ldc.i4.0 IL_0027: conv.u IL_0028: stloc.3 IL_0029: br.s IL_0035 IL_002b: ldloc.s V_4 IL_002d: ldc.i4.0 IL_002e: ldelema ""byte"" IL_0033: conv.u IL_0034: stloc.3 -IL_0035: nop -IL_0036: nop ~IL_0037: ldnull IL_0038: stloc.s V_4 -IL_003a: ldloc.2 IL_003b: stloc.1 IL_003c: br.s IL_003e -IL_003e: ldloc.1 IL_003f: ret } ", sequencePointDisplay: SequencePointDisplayMode.Minimal); } [ClrOnlyFact(ClrOnlyReason.Ilasm)] public void SystemIntPtrInSignature_BreakingChange() { // NOTE: the IL is intentionally not compliant with ECMA spec // in particular Metadata spec II.23.2.16 (Short form signatures) says that // [mscorlib]System.IntPtr is not supposed to be used in metadata // and short-version 'native int' is supposed to be used instead. var ilSource = @" .class public AddressHelper{ .method public hidebysig static valuetype [mscorlib]System.IntPtr AddressOf<T>(!!0& t){ ldarg 0 ldind.i ret } } "; var csharpSource = @" class Program { static void Main(string[] args) { var s = string.Empty; var i = AddressHelper.AddressOf(ref s); System.Console.WriteLine(i); } } "; var cscomp = CreateCompilationWithILAndMscorlib40(csharpSource, ilSource); var expected = new[] { // (7,35): error CS0570: 'AddressHelper.AddressOf<T>(?)' is not supported by the language // var i = AddressHelper.AddressOf(ref s); Diagnostic(ErrorCode.ERR_BindToBogus, "AddressOf").WithArguments("AddressHelper.AddressOf<T>(?)").WithLocation(7, 35) }; cscomp.VerifyDiagnostics(expected); } [ClrOnlyFact(ClrOnlyReason.Ilasm)] public void SystemIntPtrInSignature_BreakingChange_001() { var ilSource = @" .class public AddressHelper{ .method public hidebysig static native int AddressOf<T>(!!0& t){ ldc.i4.5 conv.u ret } } "; var csharpSource = @" class Program { static void Main(string[] args) { var s = string.Empty; var i = AddressHelper.AddressOf(ref s); System.Console.WriteLine(i); } } "; var compilation = CreateCompilationWithILAndMscorlib40(csharpSource, ilSource, targetFramework: TargetFramework.Mscorlib40, options: TestOptions.ReleaseExe); compilation.VerifyDiagnostics(); var result = CompileAndVerify(compilation, expectedOutput: "5"); } [Fact, WorkItem(7550, "https://github.com/dotnet/roslyn/issues/7550")] public void EnsureNullPointerIsPoppedIfUnused() { string source = @" public class A { public unsafe byte* Ptr; static void Main() { unsafe { var x = new A(); byte* ptr = (x == null) ? null : x.Ptr; } System.Console.WriteLine(""OK""); } } "; CompileAndVerify(source, options: TestOptions.UnsafeReleaseExe, expectedOutput: "OK", verify: Verification.Passes); } [Fact, WorkItem(40768, "https://github.com/dotnet/roslyn/issues/40768")] public void DoesNotEmitArrayDotEmptyForEmptyPointerArrayParams() { var source = @" using System; public static class Program { public static unsafe void Main() { Console.WriteLine(Test()); } public static unsafe int Test(params int*[] types) { return types.Length; } }"; // PEVerify: // [ : Program::Main][mdToken= 0x6000001][offset 0x00000001] Unmanaged pointers are not a verifiable type. // [ : Program::Main][mdToken = 0x6000001][offset 0x00000001] Unable to resolve token. var comp = CompileAndVerify(source, options: TestOptions.UnsafeReleaseExe, expectedOutput: "0", verify: Verification.FailsPEVerify); comp.VerifyIL("Program.Main", @" { // Code size 17 (0x11) .maxstack 1 IL_0000: ldc.i4.0 IL_0001: newarr ""int*"" IL_0006: call ""int Program.Test(params int*[])"" IL_000b: call ""void System.Console.WriteLine(int)"" IL_0010: ret }"); } [Fact] public void DoesEmitArrayDotEmptyForEmptyPointerArrayArrayParams() { var source = @" using System; public static class Program { public static unsafe void Main() { Console.WriteLine(Test()); } public static unsafe int Test(params int*[][] types) { return types.Length; } }"; var comp = CompileAndVerify(source, options: TestOptions.UnsafeReleaseExe, expectedOutput: "0"); comp.VerifyIL("Program.Main", @" { // Code size 16 (0x10) .maxstack 1 IL_0000: call ""int*[][] System.Array.Empty<int*[]>()"" IL_0005: call ""int Program.Test(params int*[][])"" IL_000a: call ""void System.Console.WriteLine(int)"" IL_000f: ret }"); } #endregion } }