Skip to content

Commit 5fae448

Browse files
authored
Fix 'GetRefEscape' for invocation using old rules (#80261)
1 parent b8eec28 commit 5fae448

File tree

3 files changed

+145
-1
lines changed

3 files changed

+145
-1
lines changed

src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3056,7 +3056,13 @@ private void GetEscapeValuesForOldRules(
30563056
escapeValues.Add(new EscapeValue(parameter, argument, EscapeLevel.CallingMethod, isRefEscape: false));
30573057
}
30583058

3059-
if (parameter.RefKind != RefKind.None)
3059+
// https://github.com/dotnet/csharpstandard/blob/0ad29bf615b18ae463d92ef64f557eeb007b76f1/standard/variables.md#9723-parameter-ref-safe-context
3060+
// For a parameter `p`:
3061+
// - If `p` is a reference or input parameter, its ref-safe-context is the caller-context. If `p` is an input parameter, it can’t be returned as a writable `ref` but can be returned as `ref readonly`.
3062+
// - If `p` is an output parameter, its ref-safe-context is the caller-context.
3063+
// - Otherwise, if `p` is the `this` parameter of a struct type, its ref-safe-context is the function-member.
3064+
// - Otherwise, the parameter is a value parameter, and its ref-safe-context is the function-member.
3065+
if (parameter.RefKind != RefKind.None && !parameter.IsThis)
30603066
{
30613067
escapeValues.Add(new EscapeValue(parameter, argument, EscapeLevel.CallingMethod, isRefEscape: true));
30623068
}

src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31991,5 +31991,135 @@ static void Test()
3199131991
// M(GetValue().F);
3199231992
Diagnostic(ErrorCode.ERR_BadArgRef, "GetValue().F").WithArguments("1", "ref").WithLocation(11, 11));
3199331993
}
31994+
31995+
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80244")]
31996+
public void Repro_80244_NetCoreApp()
31997+
{
31998+
var comp = CreateCompilation("""
31999+
using System;
32000+
using System.Runtime.CompilerServices;
32001+
using System.Runtime.InteropServices;
32002+
32003+
ref struct SpanReader
32004+
{
32005+
long _spanEndStreamOffset;
32006+
ReadOnlySpan<byte> _buffer;
32007+
public SpanReader(ReadOnlySpan<byte> buffer, long spanStartStreamOffset)
32008+
{
32009+
_buffer = buffer;
32010+
_spanEndStreamOffset = spanStartStreamOffset + buffer.Length;
32011+
}
32012+
32013+
public ref readonly T ReadRef<T>() where T : struct
32014+
{
32015+
if (_buffer.Length >= Unsafe.SizeOf<T>())
32016+
{
32017+
ref readonly T ret = ref MemoryMarshal.Cast<byte, T>(_buffer)[0];
32018+
_buffer = _buffer.Slice(Unsafe.SizeOf<T>());
32019+
return ref ret;
32020+
}
32021+
else
32022+
{
32023+
throw new Exception();
32024+
}
32025+
}
32026+
}
32027+
""",
32028+
targetFramework: TargetFramework.NetCoreApp,
32029+
parseOptions: TestOptions.Regular14);
32030+
comp.VerifyEmitDiagnostics();
32031+
}
32032+
32033+
[Theory, WorkItem("https://github.com/dotnet/roslyn/issues/80244")]
32034+
[InlineData(LanguageVersion.CSharp8), InlineData(LanguageVersion.CSharp14)]
32035+
public void Repro_80244_NetStandard(LanguageVersion consumerLanguageVersion)
32036+
{
32037+
var spanCompilation = CreateCompilation(TestSources.Span, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular8);
32038+
var spanReference = spanCompilation.EmitToImageReference();
32039+
var source0 = """
32040+
namespace System.Runtime.CompilerServices
32041+
{
32042+
public static class Unsafe
32043+
{
32044+
public static int SizeOf<T>() => throw null!;
32045+
}
32046+
}
32047+
32048+
namespace System.Runtime.InteropServices
32049+
{
32050+
public static class MemoryMarshal
32051+
{
32052+
public static ReadOnlySpan<TTo> Cast<TFrom, TTo>(ReadOnlySpan<TFrom> span)
32053+
where TFrom : struct
32054+
=> throw null!;
32055+
}
32056+
}
32057+
""";
32058+
var source1 = """
32059+
using System;
32060+
using System.Runtime.CompilerServices;
32061+
using System.Runtime.InteropServices;
32062+
32063+
ref struct SpanReader
32064+
{
32065+
long _spanEndStreamOffset;
32066+
ReadOnlySpan<byte> _buffer;
32067+
public SpanReader(ReadOnlySpan<byte> buffer, long spanStartStreamOffset)
32068+
{
32069+
_buffer = buffer;
32070+
_spanEndStreamOffset = spanStartStreamOffset + buffer.Length;
32071+
}
32072+
32073+
public ref readonly T ReadRef<T>() where T : struct
32074+
{
32075+
if (_buffer.Length >= Unsafe.SizeOf<T>())
32076+
{
32077+
ref readonly T ret = ref MemoryMarshal.Cast<byte, T>(_buffer)[0];
32078+
_buffer = _buffer.Slice(Unsafe.SizeOf<T>());
32079+
return ref ret;
32080+
}
32081+
else
32082+
{
32083+
throw new Exception();
32084+
}
32085+
}
32086+
}
32087+
""";
32088+
var comp = CreateCompilation([source0, source1],
32089+
references: [spanReference],
32090+
parseOptions: TestOptions.Regular.WithLanguageVersion(consumerLanguageVersion),
32091+
targetFramework: TargetFramework.NetStandard20);
32092+
comp.VerifyEmitDiagnostics();
32093+
}
32094+
32095+
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80244")]
32096+
public void Repro_80244_Simple()
32097+
{
32098+
var source0 = """
32099+
public ref struct RS
32100+
{
32101+
public ref byte this[int i] => throw null!;
32102+
}
32103+
""";
32104+
32105+
var reference = CreateCompilation(source0, parseOptions: TestOptions.Regular8).EmitToImageReference();
32106+
var source1 = """
32107+
class Program
32108+
{
32109+
static ref byte M1(RS rs)
32110+
{
32111+
ref byte ret = ref rs[1];
32112+
return ref ret;
32113+
}
32114+
32115+
static ref byte M2(RS rs)
32116+
{
32117+
return ref rs[1];
32118+
}
32119+
}
32120+
""";
32121+
var comp = CreateCompilation(source1, references: [reference], parseOptions: TestOptions.Regular8);
32122+
comp.VerifyEmitDiagnostics();
32123+
}
3199432124
}
3199532125
}

src/Compilers/Test/Utilities/CSharp/TestSources.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,14 @@ public readonly ref struct Span<T>
2424
unsafe public Span(void* pointer, int length)
2525
{
2626
this.arr = Helpers.ToArray<T>(pointer, length);
27+
this.start = 0;
2728
this.Length = length;
2829
}
2930
3031
public Span(T[] arr)
3132
{
3233
this.arr = arr;
34+
this.start = 0;
3335
this.Length = arr is null ? 0 : arr.Length;
3436
}
3537
@@ -96,6 +98,8 @@ public ref T Current
9698
public static implicit operator ReadOnlySpan<T>(Span<T> span) => new ReadOnlySpan<T>(span.arr);
9799
98100
public Span<T> Slice(int offset, int length) => new Span<T>(this.arr, offset, length);
101+
102+
public Span<T> Slice(int offset) => new Span<T>(this.arr, offset, Length - offset);
99103
}
100104
101105
public readonly ref struct ReadOnlySpan<T>
@@ -111,12 +115,14 @@ public readonly ref struct ReadOnlySpan<T>
111115
unsafe public ReadOnlySpan(void* pointer, int length)
112116
{
113117
this.arr = Helpers.ToArray<T>(pointer, length);
118+
this.start = 0;
114119
this.Length = length;
115120
}
116121
117122
public ReadOnlySpan(T[] arr)
118123
{
119124
this.arr = arr;
125+
this.start = 0;
120126
this.Length = arr is null ? 0 : arr.Length;
121127
}
122128
@@ -185,6 +191,8 @@ public ref readonly T Current
185191
186192
public ReadOnlySpan<T> Slice(int offset, int length) => new ReadOnlySpan<T>(this.arr, offset, length);
187193
194+
public ReadOnlySpan<T> Slice(int offset) => new ReadOnlySpan<T>(this.arr, offset, offset - Length);
195+
188196
#nullable enable
189197
public static ReadOnlySpan<T> CastUp<TDerived>(ReadOnlySpan<TDerived> items) where TDerived : class?, T
190198
{

0 commit comments

Comments
 (0)