diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs index 0653d993db96e..55d0cf6970813 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs @@ -2584,6 +2584,18 @@ internal bool IsMoveableVariable(BoundExpression expr, out Symbol accessedLocalO expr = receiver; continue; } + case BoundKind.InlineArrayAccess: + { + var elementAccess = (BoundInlineArrayAccess)expr; + + if (elementAccess.GetItemOrSliceHelper is WellKnownMember.System_Span_T__get_Item or WellKnownMember.System_ReadOnlySpan_T__get_Item) + { + expr = elementAccess.Expression; + continue; + } + + goto default; + } case BoundKind.RangeVariable: { // NOTE: there are cases where you can take the address of a range variable. diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/InlineArrayTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/InlineArrayTests.cs index 624f5531824d8..5544ca44a305e 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/InlineArrayTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/InlineArrayTests.cs @@ -17562,6 +17562,259 @@ struct Buffer ); } + [ConditionalFact(typeof(CoreClrOnly))] + public void ElementPointer_01() + { + var src = @" +unsafe class Program +{ + static void Main() + { + Buffer10 b = default; + b[0] = 1; + + int* p1 = &b[0]; + (*p1)++; + System.Console.Write(b[0]); + + Buffer10* p2 = &b; + } +} +" + Buffer10Definition; + + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.DebugExe.WithAllowUnsafe(true)); + CompileAndVerify(comp, expectedOutput: "2", verify: Verification.Fails).VerifyDiagnostics(); + } + + [ConditionalFact(typeof(CoreClrOnly))] + public void ElementPointer_02() + { + var src = @" +unsafe class Program +{ + static void Main() + { + Test(default); + } + + static void Test(Buffer10 b) + { + b[0] = 1; + + int* p1 = &b[0]; + (*p1)++; + System.Console.Write(b[0]); + + Buffer10* p2 = &b; + } +} +" + Buffer10Definition; + + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.DebugExe.WithAllowUnsafe(true)); + CompileAndVerify(comp, expectedOutput: "2", verify: Verification.Fails).VerifyDiagnostics(); + } + + [Fact] + public void ElementPointer_03() + { + var src = @" +unsafe class Program +{ + static void Main() + { + Buffer10 b = default; + Test(ref b); + } + + static void Test(ref Buffer10 b) + { + b[0] = 1; + + int* p1 = &b[0]; + (*p1)++; + System.Console.Write(b[0]); + + Buffer10* p2 = &b; + } +} +" + Buffer10Definition; + + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.DebugExe.WithAllowUnsafe(true)); + comp.VerifyDiagnostics( + // (14,19): error CS0212: You can only take the address of an unfixed expression inside of a fixed statement initializer + // int* p1 = &b[0]; + Diagnostic(ErrorCode.ERR_FixedNeeded, "&b[0]").WithLocation(14, 19), + // (18,29): error CS0212: You can only take the address of an unfixed expression inside of a fixed statement initializer + // Buffer10* p2 = &b; + Diagnostic(ErrorCode.ERR_FixedNeeded, "&b").WithLocation(18, 29) + ); + } + + [ConditionalFact(typeof(CoreClrOnly))] + public void ElementPointer_04() + { + var src = @" +unsafe class Program +{ + static void Main() + { + Buffer10 b = default; + Test(ref b); + } + + static void Test(ref Buffer10 b) + { + b[0] = 1; + + fixed (int* p1 = &b[0]) + { + (*p1)++; + } + + System.Console.Write(b[0]); + + fixed (Buffer10* p2 = &b) {} + } +} +" + Buffer10Definition; + + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.DebugExe.WithAllowUnsafe(true)); + CompileAndVerify(comp, expectedOutput: "2", verify: Verification.Fails).VerifyDiagnostics(); + } + + [Fact] + public void ElementPointer_05() + { + var src = @" +unsafe class Program +{ + static void Main() + { + Buffer10 b = default; + Test(b); + } + + static void Test(Buffer10 b) + { + b[0] = 1; + + fixed (int* p1 = &b[0]) + { + (*p1)++; + } + + System.Console.Write(b[0]); + + fixed (Buffer10* p2 = &b) {} + } +} +" + Buffer10Definition; + + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.DebugExe.WithAllowUnsafe(true)); + comp.VerifyDiagnostics( + // (14,26): error CS0213: You cannot use the fixed statement to take the address of an already fixed expression + // fixed (int* p1 = &b[0]) + Diagnostic(ErrorCode.ERR_FixedNotNeeded, "&b[0]").WithLocation(14, 26), + // (21,36): error CS0213: You cannot use the fixed statement to take the address of an already fixed expression + // fixed (Buffer10* p2 = &b) {} + Diagnostic(ErrorCode.ERR_FixedNotNeeded, "&b").WithLocation(21, 36) + ); + } + + [Fact] + public void ElementPointer_06() + { + var src = @" +unsafe class Program +{ + static void Main() + { + fixed (int* p1 = &GetBuffer()[0]) + { + (*p1)++; + } + + int* p2 = &GetBuffer()[0]; + } + + static Buffer10 GetBuffer() => default; +} +" + Buffer10Definition; + + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.DebugExe.WithAllowUnsafe(true)); + comp.VerifyDiagnostics( + // (6,27): error CS0211: Cannot take the address of the given expression + // fixed (int* p1 = &GetBuffer()[0]) + Diagnostic(ErrorCode.ERR_InvalidAddrOp, "GetBuffer()[0]").WithLocation(6, 27), + // (11,20): error CS0211: Cannot take the address of the given expression + // int* p2 = &GetBuffer()[0]; + Diagnostic(ErrorCode.ERR_InvalidAddrOp, "GetBuffer()[0]").WithLocation(11, 20) + ); + } + + [Fact] + public void ElementPointer_07() + { + var src = @" +unsafe class Program +{ + static void Main() + { + Buffer10 b = default; + + fixed (void* p1 = &b[..]) + { + } + + void* p2 = &b[..]; + } +} +" + Buffer10Definition; + + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.DebugExe.WithAllowUnsafe(true)); + comp.VerifyDiagnostics( + // (8,28): error CS0211: Cannot take the address of the given expression + // fixed (void* p1 = &b[..]) + Diagnostic(ErrorCode.ERR_InvalidAddrOp, "b[..]").WithLocation(8, 28), + // (12,21): error CS0211: Cannot take the address of the given expression + // void* p2 = &b[..]; + Diagnostic(ErrorCode.ERR_InvalidAddrOp, "b[..]").WithLocation(12, 21) + ); + } + + [ConditionalFact(typeof(CoreClrOnly))] + public void ElementPointer_08() + { + var src = @" +unsafe class Program +{ + static void Main() + { + S s = new S(1); + + int* p1 = &s.b[0]; + (*p1)++; + System.Console.Write(s.b[0]); + + Buffer10* p2 = &s.b; + } +} + +struct S +{ + public readonly Buffer10 b; + + public S(int x) + { + b[0] = x; + } +} +" + Buffer10Definition; + + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.DebugExe.WithAllowUnsafe(true)); + CompileAndVerify(comp, expectedOutput: "2", verify: Verification.Fails).VerifyDiagnostics(); + } + [ConditionalFact(typeof(CoreClrOnly))] public void Foreach_Variable_01() {