Skip to content

Commit

Permalink
Record read of ref local even if value is discarded (#60910)
Browse files Browse the repository at this point in the history
  • Loading branch information
cston authored May 10, 2022
1 parent b47d68d commit 4c4df4d
Show file tree
Hide file tree
Showing 2 changed files with 263 additions and 0 deletions.
6 changes: 6 additions & 0 deletions src/Compilers/CSharp/Portable/CodeGen/Optimizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -874,6 +874,12 @@ public override BoundNode VisitLocal(BoundLocal node)
break;

case ExprContext.Sideeffects:
if (node.LocalSymbol.RefKind != RefKind.None)
{
// Reading from a ref has a side effect since the read
// may result in a NullReferenceException.
RecordVarRead(node.LocalSymbol);
}
break;

case ExprContext.Value:
Expand Down
257 changes: 257 additions & 0 deletions src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefLocalTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3710,5 +3710,262 @@ void verify(CSharpCompilationOptions options, Verification verify, string expect
verifier.VerifyIL("<top-level-statements-entry-point>", expectedIL);
}
}

[Fact]
[WorkItem(60905, "https://github.com/dotnet/roslyn/issues/60905")]
public void ReadValueAndDiscard_01()
{
var source =
@"struct S { }
class Program
{
static void Main()
{
F(new S[1]);
}
static void F(S[] a)
{
ref var b = ref a[0];
_ = b;
}
}";
var verifier = CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: "");
verifier.VerifyIL("Program.F",
@"{
// Code size 11 (0xb)
.maxstack 2
.locals init (S& V_0) //b
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldc.i4.0
IL_0003: ldelema ""S""
IL_0008: stloc.0
IL_0009: nop
IL_000a: ret
}");
verifier = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: "");
verifier.VerifyIL("Program.F",
@"{
// Code size 9 (0x9)
.maxstack 2
IL_0000: ldarg.0
IL_0001: ldc.i4.0
IL_0002: ldelema ""S""
IL_0007: pop
IL_0008: ret
}");
}

[Fact]
[WorkItem(60905, "https://github.com/dotnet/roslyn/issues/60905")]
public void ReadValueAndDiscard_02()
{
var source =
@"struct S<T>
{
public T F;
}
class Program
{
static void Main()
{
F(new S<int>());
}
static void F<T>(S<T> s)
{
ref T t = ref s.F;
_ = t;
}
}";
var verifier = CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: "");
verifier.VerifyIL("Program.F<T>",
@"{
// Code size 11 (0xb)
.maxstack 1
.locals init (T& V_0) //t
IL_0000: nop
IL_0001: ldarga.s V_0
IL_0003: ldflda ""T S<T>.F""
IL_0008: stloc.0
IL_0009: nop
IL_000a: ret
}");
verifier = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: "");
verifier.VerifyIL("Program.F<T>",
@"{
// Code size 9 (0x9)
.maxstack 1
IL_0000: ldarga.s V_0
IL_0002: ldflda ""T S<T>.F""
IL_0007: pop
IL_0008: ret
}");
}

[Fact]
[WorkItem(60905, "https://github.com/dotnet/roslyn/issues/60905")]
public void ReadValueAndDiscard_03()
{
var source =
@"struct S<T>
{
public T F;
}
class Program
{
static void Main()
{
var s = new S<int>();
F(ref s);
}
static void F<T>(ref S<T> s)
{
ref T t = ref s.F;
_ = t;
}
}";
var verifier = CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: "");
verifier.VerifyIL("Program.F<T>",
@"{
// Code size 10 (0xa)
.maxstack 1
.locals init (T& V_0) //t
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldflda ""T S<T>.F""
IL_0007: stloc.0
IL_0008: nop
IL_0009: ret
}");
verifier = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: "");
verifier.VerifyIL("Program.F<T>",
@"{
// Code size 8 (0x8)
.maxstack 1
IL_0000: ldarg.0
IL_0001: ldflda ""T S<T>.F""
IL_0006: pop
IL_0007: ret
}");
}

[Fact]
[WorkItem(60905, "https://github.com/dotnet/roslyn/issues/60905")]
public void ReadValueAndDiscard_04()
{
var source =
@"struct S<T>
{
public T F;
}
class Program
{
static void Main()
{
F(new S<int>());
}
static void F<T>(in S<T> s)
{
ref readonly T t = ref s.F;
_ = t;
}
}";
var verifier = CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: "");
verifier.VerifyIL("Program.F<T>",
@"{
// Code size 10 (0xa)
.maxstack 1
.locals init (T& V_0) //t
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldflda ""T S<T>.F""
IL_0007: stloc.0
IL_0008: nop
IL_0009: ret
}");
verifier = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: "");
verifier.VerifyIL("Program.F<T>",
@"{
// Code size 8 (0x8)
.maxstack 1
IL_0000: ldarg.0
IL_0001: ldflda ""T S<T>.F""
IL_0006: pop
IL_0007: ret
}");
}

[Fact]
public void ReadValueAndDiscard_05()
{
var source =
@"struct S<T>
{
}
class Program
{
static void Main()
{
var s = new S<int>();
F(ref s);
}
static void F<T>(ref S<T> s)
{
_ = s;
}
}";
var verifier = CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: "");
verifier.VerifyIL("Program.F<T>",
@"{
// Code size 3 (0x3)
.maxstack 0
IL_0000: nop
IL_0001: nop
IL_0002: ret
}");
verifier = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: "");
verifier.VerifyIL("Program.F<T>",
@"{
// Code size 1 (0x1)
.maxstack 0
IL_0000: ret
}");
}

[Fact]
public void ReadValueAndDiscard_06()
{
var source =
@"struct S<T>
{
}
class Program
{
static void Main()
{
F(new S<int>());
}
static void F<T>(in S<T> s)
{
_ = s;
}
}";
var verifier = CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: "");
verifier.VerifyIL("Program.F<T>",
@"{
// Code size 3 (0x3)
.maxstack 0
IL_0000: nop
IL_0001: nop
IL_0002: ret
}");
verifier = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: "");
verifier.VerifyIL("Program.F<T>",
@"{
// Code size 1 (0x1)
.maxstack 0
IL_0000: ret
}");
}
}
}

0 comments on commit 4c4df4d

Please sign in to comment.