Skip to content

Commit

Permalink
Allow ref enumerators in async/iterator methods
Browse files Browse the repository at this point in the history
  • Loading branch information
jjonescz committed Aug 19, 2024
1 parent 75b26a2 commit fc2395c
Show file tree
Hide file tree
Showing 6 changed files with 313 additions and 95 deletions.
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -527,7 +527,7 @@ private BoundForEachStatement BindForEachPartsWorker(BindingDiagnosticBag diagno

if (builder.InlineArraySpanType == WellKnownType.Unknown && getEnumeratorType.IsRestrictedType() && (IsDirectlyInIterator || IsInAsyncMethod()))
{
diagnostics.Add(ErrorCode.ERR_BadSpecialByRefIterator, foreachKeyword.GetLocation(), getEnumeratorType);
CheckFeatureAvailability(foreachKeyword, MessageID.IDS_FeatureRefUnsafeInIteratorAsync, diagnostics);
}

diagnostics.Add(_syntax.ForEachKeyword, useSiteInfo);
Expand Down
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1529,7 +1529,7 @@ internal enum ErrorCode
ERR_AutoPropsInRoStruct = 8341,
ERR_FieldlikeEventsInRoStruct = 8342,
// ERR_RefStructInterfaceImpl = 8343,
ERR_BadSpecialByRefIterator = 8344,
// ERR_BadSpecialByRefIterator = 8344,
ERR_FieldAutoPropCantBeByRefLike = 8345,
ERR_StackAllocConversionNotPossible = 8346,

Expand Down
1 change: 0 additions & 1 deletion src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1837,7 +1837,6 @@ or ErrorCode.ERR_InExtensionMustBeValueType
or ErrorCode.ERR_FieldsInRoStruct
or ErrorCode.ERR_AutoPropsInRoStruct
or ErrorCode.ERR_FieldlikeEventsInRoStruct
or ErrorCode.ERR_BadSpecialByRefIterator
or ErrorCode.ERR_FieldAutoPropCantBeByRefLike
or ErrorCode.ERR_StackAllocConversionNotPossible
or ErrorCode.ERR_EscapeCall
Expand Down
79 changes: 56 additions & 23 deletions src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitForeachTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2061,17 +2061,20 @@ public S(int i)
CompileAndVerify(comp, expectedOutput: "1 2 Done");
}

[Fact]
public void TestWithPattern_RefStructEnumerator_Async()
[Theory]
[InlineData("")]
[InlineData("await Task.Yield();")]
public void TestWithPattern_RefStructEnumerator_Async(string body)
{
var source = """
var source = $$"""
using System.Threading.Tasks;
public class C
{
public static async Task Main()
{
await foreach (var s in new C())
{
{{body}}
}
}
public Enumerator GetAsyncEnumerator() => new Enumerator();
Expand All @@ -2083,22 +2086,35 @@ public ref struct Enumerator
}
""";

// https://github.com/dotnet/roslyn/issues/73280 - should not be a langversion error since this remains an error in C# 13
CreateCompilation(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics(
// (6,15): error CS9202: Feature 'ref and unsafe in async and iterator methods' is not available in C# 12.0. Please use language version 13.0 or greater.
// await foreach (var s in new C())
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "foreach").WithArguments("ref and unsafe in async and iterator methods", "13.0").WithLocation(6, 15));

var expectedDiagnostics = new[]
{
// (6,15): error CS8344: foreach statement cannot operate on enumerators of type 'C.Enumerator' in async or iterator methods because 'C.Enumerator' is a ref struct.
// (6,9): error CS4007: Instance of type 'C.Enumerator' cannot be preserved across 'await' or 'yield' boundary.
// await foreach (var s in new C())
Diagnostic(ErrorCode.ERR_BadSpecialByRefIterator, "foreach").WithArguments("C.Enumerator").WithLocation(6, 15)
Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, @"await foreach (var s in new C())
{
" + body + @"
}").WithArguments("C.Enumerator").WithLocation(6, 9)
};

CreateCompilation(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics(expectedDiagnostics);
CreateCompilation(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics(expectedDiagnostics);
CreateCompilation(source).VerifyDiagnostics(expectedDiagnostics);
CreateCompilation(source, parseOptions: TestOptions.Regular13).VerifyEmitDiagnostics(expectedDiagnostics);
CreateCompilation(source).VerifyEmitDiagnostics(expectedDiagnostics);
}

[Fact]
public void TestWithPattern_RefStructEnumerator_AsyncIterator()
[Theory]
[InlineData("")]
[InlineData("await Task.Yield();")]
[InlineData("yield return x;")]
[InlineData("yield return x; await Task.Yield();")]
[InlineData("await Task.Yield(); yield return x;")]
public void TestWithPattern_RefStructEnumerator_AsyncIterator(string body)
{
var source = """
var source = $$"""
using System.Collections.Generic;
using System.Threading.Tasks;
public class C
Expand All @@ -2107,8 +2123,9 @@ public static async IAsyncEnumerable<int> M()
{
await foreach (var x in new C())
{
yield return x;
{{body}}
}
yield return -1;
}
public Enumerator GetAsyncEnumerator() => new Enumerator();
public ref struct Enumerator
Expand All @@ -2117,18 +2134,26 @@ public ref struct Enumerator
public Task<bool> MoveNextAsync() => throw null;
}
}
""" + s_IAsyncEnumerable;
""" + AsyncStreamsTypes;

// https://github.com/dotnet/roslyn/issues/73280 - should not be a langversion error since this remains an error in C# 13
CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics(
// (7,15): error CS9202: Feature 'ref and unsafe in async and iterator methods' is not available in C# 12.0. Please use language version 13.0 or greater.
// await foreach (var x in new C())
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "foreach").WithArguments("ref and unsafe in async and iterator methods", "13.0").WithLocation(7, 15));

var expectedDiagnostics = new[]
{
// (7,15): error CS8344: foreach statement cannot operate on enumerators of type 'C.Enumerator' in async or iterator methods because 'C.Enumerator' is a ref struct.
// (7,9): error CS4007: Instance of type 'C.Enumerator' cannot be preserved across 'await' or 'yield' boundary.
// await foreach (var x in new C())
Diagnostic(ErrorCode.ERR_BadSpecialByRefIterator, "foreach").WithArguments("C.Enumerator").WithLocation(7, 15)
Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, @"await foreach (var x in new C())
{
" + body + @"
}").WithArguments("C.Enumerator").WithLocation(7, 9)
};

CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics(expectedDiagnostics);
CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics(expectedDiagnostics);
CreateCompilationWithTasksExtensions(source).VerifyDiagnostics(expectedDiagnostics);
CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.Regular13).VerifyEmitDiagnostics(expectedDiagnostics);
CreateCompilationWithTasksExtensions(source).VerifyEmitDiagnostics(expectedDiagnostics);
}

[Fact]
Expand All @@ -2154,16 +2179,24 @@ public ref struct Enumerator
}
""";

// https://github.com/dotnet/roslyn/issues/73280 - should not be a langversion error since this remains an error in C# 13
CreateCompilation(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics(
// (6,9): error CS9202: Feature 'ref and unsafe in async and iterator methods' is not available in C# 12.0. Please use language version 13.0 or greater.
// foreach (var x in new C())
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "foreach").WithArguments("ref and unsafe in async and iterator methods", "13.0").WithLocation(6, 9));

var expectedDiagnostics = new[]
{
// (6,9): error CS8344: foreach statement cannot operate on enumerators of type 'C.Enumerator' in async or iterator methods because 'C.Enumerator' is a ref struct.
// (6,9): error CS4007: Instance of type 'C.Enumerator' cannot be preserved across 'await' or 'yield' boundary.
// foreach (var x in new C())
Diagnostic(ErrorCode.ERR_BadSpecialByRefIterator, "foreach").WithArguments("C.Enumerator").WithLocation(6, 9)
Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, @"foreach (var x in new C())
{
yield return x;
}").WithArguments("C.Enumerator").WithLocation(6, 9)
};

CreateCompilation(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics(expectedDiagnostics);
CreateCompilation(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics(expectedDiagnostics);
CreateCompilation(source).VerifyDiagnostics(expectedDiagnostics);
CreateCompilation(source, parseOptions: TestOptions.Regular13).VerifyEmitDiagnostics(expectedDiagnostics);
CreateCompilation(source).VerifyEmitDiagnostics(expectedDiagnostics);
}

[Fact]
Expand Down
Loading

0 comments on commit fc2395c

Please sign in to comment.