using System; using System.Diagnostics.CodeAnalysis; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { internal struct Buffer { private int value; [UnscopedRef] public ref int GetRef() { return ref value; } } internal ref struct Holder { public Span Inner; public void Set(Span inner) { Inner = inner; } public readonly void ReadonlyUse(Span inner) { _ = inner.Length; } } internal static class HolderExtensions { public static void SetExtension(this ref Holder holder, Span inner) { holder.Inner = inner; } public static void ReadonlyExtension(this in Holder holder, Span inner) { _ = inner.Length; } } internal class LifetimeTests { private static int staticField; public Span CreateWithoutCapture(scoped ref int value) { // Okay: value is not captured return new Span(ref staticField); } public Span CreateAndCapture(ref int value) { // Okay: value Rule 3 specifies that the safe-to-escape be limited to the ref-safe-to-escape // of the ref argument. That is the *calling method* for value hence this is not allowed. return new Span(ref value); } public Span ScopedRefSpan(scoped ref Span span) { return span; } public Span ScopedSpan(scoped Span span) { return default(Span); } public void OutSpan(out Span span) { span = default(Span); } public void CaptureIntoRef(ref Holder holder, Span inner) { holder.Inner = inner; } public void CaptureIntoOut(out Holder holder, Span inner) { holder = default(Holder); holder.Inner = inner; } public void CaptureRefIntoOut(out Holder holder, ref int value) { holder = default(Holder); holder.Inner = new Span(ref value); } public ref Holder Identity(ref Holder holder) { return ref holder; } public Span CaptureOut([UnscopedRef] out int value) { value = 0; return new Span(ref value); } public Span NoCaptureOut(out int value) { value = 0; return default(Span); } public Span Identity(Span span) { return span; } public void Calls() { int value = 0; scoped Span span = CreateWithoutCapture(ref value); span = CreateAndCapture(ref value); span = ScopedRefSpan(ref span); span = ScopedSpan(span); OutSpan(out span); } public int ReassignScopedRefToLocal(bool b, ref int x) { int num = 42; scoped ref int reference = ref x; if (b) { reference = ref num; } return reference; } public int ReassignScopedSpanFromOut(bool b) { int value = 0; scoped Span span = default(Span); if (b) { span = CaptureOut(out value); } return span[0] + value; } public int ImplicitScopedOutDoesNotCapture() { Span span = default(Span); span = NoCaptureOut(out var value); Console.WriteLine(span.Length); return span.Length + value; } public int ReassignScopedSpanFromReceiver(bool b) { UnscopedRefStruct unscopedRefStruct = default(UnscopedRefStruct); scoped Span span = default(Span); if (b) { span = unscopedRefStruct.AsSpan(); } return span[0]; } public int ReassignScopedSpanFromRefParameter(bool b, ref int value) { scoped Span span = default(Span); if (b) { span = CreateAndCapture(ref value); } return span[0]; } public int ReassignScopedSpanFromStackAlloc(bool b) { scoped Span span = default(Span); if (b) { span = stackalloc int[1]; } return span[0]; } public int ReassignScopedSpanFromScopedValue(bool b, scoped Span value) { scoped Span span = default(Span); if (b) { span = value; } return span[0]; } public int ReassignScopedSpanFromNestedCall(bool b) { int value = 0; scoped Span span = default(Span); if (b) { span = Identity(CreateAndCapture(ref value)); } return span[0]; } public int ReassignScopedSpanFromLocalCopy(bool b) { Span span = stackalloc int[1]; scoped Span span2 = default(Span); if (b) { span2 = span; } return span2[0]; } public int ReassignScopedSpanFromScopedRefValue(bool b) { Span span = stackalloc int[1]; scoped Span span2 = default(Span); if (b) { span2 = ScopedRefSpan(ref span); } return span2[0]; } public int NarrowInitializerDoesNotNeedScoped(bool b) { int num = 1; int num2 = 2; ref int reference = ref num; if (b) { reference = ref num2; } return reference; } public int NarrowThenWideDoesNotNeedScoped(bool b, ref int value) { int num = 1; ref int reference = ref num; if (b) { reference = ref value; } return reference; } public int ClassParameterReceiverDoesNotNarrow(SpanProvider provider, bool b) { Span span = default(Span); if (b) { span = provider.GetBuffer(); } return span.Length; } public int ClassLocalReceiverDoesNotNarrow(bool b) { SpanProvider spanProvider = new SpanProvider(); Span span = default(Span); if (b) { span = spanProvider.GetBuffer(); } return span.Length; } public ref int PlainStructReceiverDoesNotForceScoped(bool b, ref Buffer buffer, ref int other) { ref int result = ref buffer.GetRef(); if (b) { result = ref other; } return ref result; } public int FieldStoreRequiresScoped() { scoped Holder holder = default(Holder); Span inner = stackalloc int[4]; holder.Inner = inner; return holder.Inner.Length; } public int ReceiverCallRequiresScoped() { scoped Holder holder = default(Holder); Span inner = stackalloc int[4]; holder.Set(inner); return holder.Inner.Length; } public int ExtensionReceiverCallRequiresScoped() { scoped Holder holder = default(Holder); Span inner = stackalloc int[4]; holder.SetExtension(inner); return holder.Inner.Length; } public int RefArgumentCallRequiresScoped() { scoped Holder holder = default(Holder); Span inner = stackalloc int[4]; CaptureIntoRef(ref holder, inner); return holder.Inner.Length; } public int OutArgumentCallRequiresScoped() { scoped Holder holder = default(Holder); Span inner = stackalloc int[4]; CaptureIntoOut(out holder, inner); return holder.Inner.Length; } public int OutArgumentCapturesRefRequiresScoped() { int value = 0; scoped Holder holder = default(Holder); CaptureRefIntoOut(out holder, ref value); return holder.Inner[0]; } public Holder RefArgumentWideValueDoesNotRequireScoped(Span wide) { Holder holder = default(Holder); CaptureIntoRef(ref holder, wide); return holder; } public Holder OutArgumentWideValueDoesNotRequireScoped(Span wide) { Holder holder = default(Holder); CaptureIntoOut(out holder, wide); return holder; } public int RefReturnFieldStoreRequiresScoped() { scoped Holder holder = default(Holder); Span inner = stackalloc int[4]; Identity(ref holder).Inner = inner; return holder.Inner.Length; } public Holder RefReturnFieldStoreWideValueDoesNotRequireScoped(Span wide) { Holder holder = default(Holder); Identity(ref holder).Inner = wide; return holder; } public Holder ReadonlyReceiverDoesNotRequireScoped(bool b) { Holder result = default(Holder); if (b) { Span inner = stackalloc int[4]; result.ReadonlyUse(inner); } return result; } public Holder ReadonlyExtensionReceiverDoesNotRequireScoped(bool b) { Holder holder = default(Holder); if (b) { Span inner = stackalloc int[4]; holder.ReadonlyExtension(inner); } return holder; } public ReadOnlyHolder ReadonlyRefStructReceiverDoesNotRequireScoped(bool b, Span wide) { ReadOnlyHolder result = new ReadOnlyHolder(wide); if (b) { Span other = stackalloc int[1]; result.Compare(other); } return result; } public int TryInitThenReassign(bool condition, Span wide) { scoped Span span; try { span = stackalloc int[4]; } finally { Console.WriteLine("cleanup"); } if (condition) { span = wide; } return span[0]; } } internal readonly ref struct ReadOnlyHolder(Span inner) { private readonly Span inner = inner; public void Compare(Span other) { _ = inner.Length; _ = other.Length; } } internal ref struct RefFields { public ref int Field0; public ref readonly int Field1; public readonly ref int Field2; public readonly ref readonly int Field3; public int PropertyAccessingRefFieldByValue { get { return Field0; } set { Field0 = value; } } public ref int PropertyReturningRefFieldByReference => ref Field0; public void Uses(int[] array) { Field1 = ref array[0]; Field2 = array[0]; } public void ReadonlyLocal() { ref readonly int field = ref Field1; Console.WriteLine("No inlining"); field.ToString(); } public RefFields(ref int v) { Field0 = ref v; Field1 = ref v; Field2 = ref v; Field3 = ref v; } } internal sealed class SpanProvider { public Span GetBuffer() { return default(Span); } } internal ref struct UnscopedRefStruct { private int val; [UnscopedRef] public ref int ByRefProperty => ref val; [UnscopedRef] public ref int ByRefAccess() { return ref val; } [UnscopedRef] public Span AsSpan() { return new Span(ref val); } } }