Skip to content

Commit f72e4b2

Browse files
committed
Allow ref enumerators in async/iterator methods
1 parent 75b26a2 commit f72e4b2

File tree

6 files changed

+284
-107
lines changed

6 files changed

+284
-107
lines changed

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

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -525,11 +525,6 @@ private BoundForEachStatement BindForEachPartsWorker(BindingDiagnosticBag diagno
525525

526526
TypeSymbol getEnumeratorType = getEnumeratorMethod.ReturnType;
527527

528-
if (builder.InlineArraySpanType == WellKnownType.Unknown && getEnumeratorType.IsRestrictedType() && (IsDirectlyInIterator || IsInAsyncMethod()))
529-
{
530-
diagnostics.Add(ErrorCode.ERR_BadSpecialByRefIterator, foreachKeyword.GetLocation(), getEnumeratorType);
531-
}
532-
533528
diagnostics.Add(_syntax.ForEachKeyword, useSiteInfo);
534529

535530
// Due to the way we extracted the various types, these conversions should always be possible.

src/Compilers/CSharp/Portable/Errors/ErrorCode.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1529,7 +1529,7 @@ internal enum ErrorCode
15291529
ERR_AutoPropsInRoStruct = 8341,
15301530
ERR_FieldlikeEventsInRoStruct = 8342,
15311531
// ERR_RefStructInterfaceImpl = 8343,
1532-
ERR_BadSpecialByRefIterator = 8344,
1532+
// ERR_BadSpecialByRefIterator = 8344,
15331533
ERR_FieldAutoPropCantBeByRefLike = 8345,
15341534
ERR_StackAllocConversionNotPossible = 8346,
15351535

src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1837,7 +1837,6 @@ or ErrorCode.ERR_InExtensionMustBeValueType
18371837
or ErrorCode.ERR_FieldsInRoStruct
18381838
or ErrorCode.ERR_AutoPropsInRoStruct
18391839
or ErrorCode.ERR_FieldlikeEventsInRoStruct
1840-
or ErrorCode.ERR_BadSpecialByRefIterator
18411840
or ErrorCode.ERR_FieldAutoPropCantBeByRefLike
18421841
or ErrorCode.ERR_StackAllocConversionNotPossible
18431842
or ErrorCode.ERR_EscapeCall

src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitForeachTests.cs

Lines changed: 41 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2061,17 +2061,20 @@ public S(int i)
20612061
CompileAndVerify(comp, expectedOutput: "1 2 Done");
20622062
}
20632063

2064-
[Fact]
2065-
public void TestWithPattern_RefStructEnumerator_Async()
2064+
[Theory]
2065+
[InlineData("")]
2066+
[InlineData("await Task.Yield();")]
2067+
public void TestWithPattern_RefStructEnumerator_Async(string body)
20662068
{
2067-
var source = """
2069+
var source = $$"""
20682070
using System.Threading.Tasks;
20692071
public class C
20702072
{
20712073
public static async Task Main()
20722074
{
20732075
await foreach (var s in new C())
20742076
{
2077+
{{body}}
20752078
}
20762079
}
20772080
public Enumerator GetAsyncEnumerator() => new Enumerator();
@@ -2085,20 +2088,28 @@ public ref struct Enumerator
20852088

20862089
var expectedDiagnostics = new[]
20872090
{
2088-
// (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.
2091+
// (6,9): error CS4007: Instance of type 'C.Enumerator' cannot be preserved across 'await' or 'yield' boundary.
20892092
// await foreach (var s in new C())
2090-
Diagnostic(ErrorCode.ERR_BadSpecialByRefIterator, "foreach").WithArguments("C.Enumerator").WithLocation(6, 15)
2093+
Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, @"await foreach (var s in new C())
2094+
{
2095+
" + body + @"
2096+
}").WithArguments("C.Enumerator").WithLocation(6, 9)
20912097
};
20922098

2093-
CreateCompilation(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics(expectedDiagnostics);
2094-
CreateCompilation(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics(expectedDiagnostics);
2095-
CreateCompilation(source).VerifyDiagnostics(expectedDiagnostics);
2099+
CreateCompilation(source, parseOptions: TestOptions.Regular12).VerifyEmitDiagnostics(expectedDiagnostics);
2100+
CreateCompilation(source, parseOptions: TestOptions.Regular13).VerifyEmitDiagnostics(expectedDiagnostics);
2101+
CreateCompilation(source).VerifyEmitDiagnostics(expectedDiagnostics);
20962102
}
20972103

2098-
[Fact]
2099-
public void TestWithPattern_RefStructEnumerator_AsyncIterator()
2104+
[Theory]
2105+
[InlineData("")]
2106+
[InlineData("await Task.Yield();")]
2107+
[InlineData("yield return x;")]
2108+
[InlineData("yield return x; await Task.Yield();")]
2109+
[InlineData("await Task.Yield(); yield return x;")]
2110+
public void TestWithPattern_RefStructEnumerator_AsyncIterator(string body)
21002111
{
2101-
var source = """
2112+
var source = $$"""
21022113
using System.Collections.Generic;
21032114
using System.Threading.Tasks;
21042115
public class C
@@ -2107,8 +2118,9 @@ public static async IAsyncEnumerable<int> M()
21072118
{
21082119
await foreach (var x in new C())
21092120
{
2110-
yield return x;
2121+
{{body}}
21112122
}
2123+
yield return -1;
21122124
}
21132125
public Enumerator GetAsyncEnumerator() => new Enumerator();
21142126
public ref struct Enumerator
@@ -2117,18 +2129,21 @@ public ref struct Enumerator
21172129
public Task<bool> MoveNextAsync() => throw null;
21182130
}
21192131
}
2120-
""" + s_IAsyncEnumerable;
2132+
""" + AsyncStreamsTypes;
21212133

21222134
var expectedDiagnostics = new[]
21232135
{
2124-
// (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.
2136+
// (7,9): error CS4007: Instance of type 'C.Enumerator' cannot be preserved across 'await' or 'yield' boundary.
21252137
// await foreach (var x in new C())
2126-
Diagnostic(ErrorCode.ERR_BadSpecialByRefIterator, "foreach").WithArguments("C.Enumerator").WithLocation(7, 15)
2138+
Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, @"await foreach (var x in new C())
2139+
{
2140+
" + body + @"
2141+
}").WithArguments("C.Enumerator").WithLocation(7, 9)
21272142
};
21282143

2129-
CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics(expectedDiagnostics);
2130-
CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics(expectedDiagnostics);
2131-
CreateCompilationWithTasksExtensions(source).VerifyDiagnostics(expectedDiagnostics);
2144+
CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.Regular12).VerifyEmitDiagnostics(expectedDiagnostics);
2145+
CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.Regular13).VerifyEmitDiagnostics(expectedDiagnostics);
2146+
CreateCompilationWithTasksExtensions(source).VerifyEmitDiagnostics(expectedDiagnostics);
21322147
}
21332148

21342149
[Fact]
@@ -2156,14 +2171,17 @@ public ref struct Enumerator
21562171

21572172
var expectedDiagnostics = new[]
21582173
{
2159-
// (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.
2174+
// (6,9): error CS4007: Instance of type 'C.Enumerator' cannot be preserved across 'await' or 'yield' boundary.
21602175
// foreach (var x in new C())
2161-
Diagnostic(ErrorCode.ERR_BadSpecialByRefIterator, "foreach").WithArguments("C.Enumerator").WithLocation(6, 9)
2176+
Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, @"foreach (var x in new C())
2177+
{
2178+
yield return x;
2179+
}").WithArguments("C.Enumerator").WithLocation(6, 9)
21622180
};
21632181

2164-
CreateCompilation(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics(expectedDiagnostics);
2165-
CreateCompilation(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics(expectedDiagnostics);
2166-
CreateCompilation(source).VerifyDiagnostics(expectedDiagnostics);
2182+
CreateCompilation(source, parseOptions: TestOptions.Regular12).VerifyEmitDiagnostics(expectedDiagnostics);
2183+
CreateCompilation(source, parseOptions: TestOptions.Regular13).VerifyEmitDiagnostics(expectedDiagnostics);
2184+
CreateCompilation(source).VerifyEmitDiagnostics(expectedDiagnostics);
21672185
}
21682186

21692187
[Fact]

0 commit comments

Comments
 (0)