Files
ILSpy/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeInIteratorsAndAsync.cs
T
Siegfried Pammer bbe41b841a Add pretty tests for C# 13 ref/unsafe in iterators and async methods
C# 13 allows ref locals, ref-struct (Span) locals, and unsafe blocks
inside iterators and async methods as long as they do not cross a
suspension point. Two suites, both gated on Roslyn 4.14/latest (whose
-langversion:latest is C# 13/14; C# 12 rejects every case with CS9202):

RefLocalsInIteratorsAndAsync is green and pins what already works:
stackalloc and array-backed Span locals in async methods and async
iterators, ReadOnlySpan in iterators, ref locals in iterators, and
local functions (async with stackalloc, iterator with ref local,
static with ref local) nested in such methods. Ref locals whose uses
are all foldable (generic ref, ref readonly, ref reassignment, ref
into a span) decompile to equivalent direct element accesses; those
cases pin the folded form via EXPECTED_OUTPUT.

UnsafeInIteratorsAndAsync is Assert.Ignore'd; the fixture is the
desired output. The decompiler currently emits the raw pinned-local
lowering (int[]/*pinned*/ plus Unsafe.AsPointer, or a GetPinnableReference
sequence for fixed over string) instead of reconstructing fixed
statements inside MoveNext, and marks the methods unsafe instead of
wrapping the pointer region in an unsafe block. That output does not
recompile: unsafe async methods fail with CS4004 (cannot await in an
unsafe context), and pointer code in iterator bodies fails with CS0214
because an iterator body remains a safe context in C# 13 even when the
method is declared unsafe. All four Roslyn 4.14/latest debug/opt
configs compile the fixture and fail only at the output comparison.

Assisted-by: Claude:claude-fable-5:Claude Code
2026-07-29 07:19:30 +02:00

67 lines
1.0 KiB
C#

using System.Collections.Generic;
using System.Threading.Tasks;
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
public class UnsafeInIteratorsAndAsync
{
public async Task<int> UnsafeBlockInAsync(int[] array)
{
await Task.Yield();
int result;
unsafe
{
fixed (int* ptr = array)
{
result = *ptr + ptr[1];
}
}
await Task.Yield();
return result;
}
public async Task<int> PointerStackAllocInAsync()
{
await Task.Yield();
int result;
unsafe
{
int* ptr = stackalloc int[4];
*ptr = 1;
ptr[3] = 4;
result = *ptr + ptr[3];
}
await Task.Yield();
return result;
}
public IEnumerable<int> FixedInIterator(int[] array)
{
yield return 1;
int value;
unsafe
{
fixed (int* ptr = array)
{
value = *ptr;
}
}
yield return value;
}
public IEnumerable<char> FixedStringInIterator(string text)
{
yield return 'x';
char value;
unsafe
{
fixed (char* ptr = text)
{
value = *ptr;
}
}
yield return value;
}
}
}