roslyn
roslyn copied to clipboard
Possibly unintended reuse of `ref` local to `in`/`ref readonly` parameters causes unexpected mutation
Version Used: 8.0.204
and 9.0.100-preview.3.24204.13
.
Steps to Reproduce:
- Copy paste the following code into a REPL, IDE, or SharpLab.
using System;
ReadOnlySpan<bool> a = new(true);
ReadOnlySpan<bool> b = new(false);
Console.WriteLine(a[0]);
Console.WriteLine(b[0]);
- Run the program or decompile the output. In both Debug and Release, observe that both spans resolve to
false
, which causes the program to outputFalse
twice.
private static void <Main>$(string[] args)
{
bool reference = true;
ReadOnlySpan<bool> readOnlySpan = new ReadOnlySpan<bool>(ref reference);
reference = false;
ReadOnlySpan<bool> readOnlySpan2 = new ReadOnlySpan<bool>(ref reference);
Console.WriteLine(readOnlySpan[0]);
Console.WriteLine(readOnlySpan2[0]);
}
This was discovered while I was writing a library, and finally narrowed it down to these two problematic lines.
// 'entries' splits by new lines lazily.
// The reference gets assigned to '\n' at the start.
var entries = mountTable.AsSpan().SplitOn((byte)'\n');
foreach (var entry in entries)
{
if (entry is [(byte)'#', ..])
continue;
var escaped = entry.SplitOn(s_whitespace)[1];
// With the execution of this line, subsequent enumerations
// split by '\' instead due to the shared reference.
var (first, rest) = escaped.SplitOn((byte)'\\');
// ...
Changing this would technically be a breaking change, and I assume that perhaps this is in some way intended for optimization reasons. If altering behavior is not desired, I would strongly suggest there to be a warning when two in
parameters are used within the same scope and of the same type.
Diagnostic Id:
N/A
Expected Behavior:
A reference is generated for every value separately, unless it can be proven that sharing the same reference won't affect the program, such as if both variables live in separate scopes and do not have their lifetimes overlap.
Actual Behavior:
A shared reference is used between multiple seemingly unrelated values, causing unexpected changes when a different in
/ref readonly
method is invoked.