Skip to content

Commit fc2395c

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

File tree

6 files changed

+313
-95
lines changed

6 files changed

+313
-95
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -527,7 +527,7 @@ private BoundForEachStatement BindForEachPartsWorker(BindingDiagnosticBag diagno
527527

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

533533
diagnostics.Add(_syntax.ForEachKeyword, useSiteInfo);

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: 56 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();
@@ -2083,22 +2086,35 @@ public ref struct Enumerator
20832086
}
20842087
""";
20852088

2089+
// https://github.com/dotnet/roslyn/issues/73280 - should not be a langversion error since this remains an error in C# 13
2090+
CreateCompilation(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics(
2091+
// (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.
2092+
// await foreach (var s in new C())
2093+
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "foreach").WithArguments("ref and unsafe in async and iterator methods", "13.0").WithLocation(6, 15));
2094+
20862095
var expectedDiagnostics = new[]
20872096
{
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.
2097+
// (6,9): error CS4007: Instance of type 'C.Enumerator' cannot be preserved across 'await' or 'yield' boundary.
20892098
// await foreach (var s in new C())
2090-
Diagnostic(ErrorCode.ERR_BadSpecialByRefIterator, "foreach").WithArguments("C.Enumerator").WithLocation(6, 15)
2099+
Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, @"await foreach (var s in new C())
2100+
{
2101+
" + body + @"
2102+
}").WithArguments("C.Enumerator").WithLocation(6, 9)
20912103
};
20922104

2093-
CreateCompilation(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics(expectedDiagnostics);
2094-
CreateCompilation(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics(expectedDiagnostics);
2095-
CreateCompilation(source).VerifyDiagnostics(expectedDiagnostics);
2105+
CreateCompilation(source, parseOptions: TestOptions.Regular13).VerifyEmitDiagnostics(expectedDiagnostics);
2106+
CreateCompilation(source).VerifyEmitDiagnostics(expectedDiagnostics);
20962107
}
20972108

2098-
[Fact]
2099-
public void TestWithPattern_RefStructEnumerator_AsyncIterator()
2109+
[Theory]
2110+
[InlineData("")]
2111+
[InlineData("await Task.Yield();")]
2112+
[InlineData("yield return x;")]
2113+
[InlineData("yield return x; await Task.Yield();")]
2114+
[InlineData("await Task.Yield(); yield return x;")]
2115+
public void TestWithPattern_RefStructEnumerator_AsyncIterator(string body)
21002116
{
2101-
var source = """
2117+
var source = $$"""
21022118
using System.Collections.Generic;
21032119
using System.Threading.Tasks;
21042120
public class C
@@ -2107,8 +2123,9 @@ public static async IAsyncEnumerable<int> M()
21072123
{
21082124
await foreach (var x in new C())
21092125
{
2110-
yield return x;
2126+
{{body}}
21112127
}
2128+
yield return -1;
21122129
}
21132130
public Enumerator GetAsyncEnumerator() => new Enumerator();
21142131
public ref struct Enumerator
@@ -2117,18 +2134,26 @@ public ref struct Enumerator
21172134
public Task<bool> MoveNextAsync() => throw null;
21182135
}
21192136
}
2120-
""" + s_IAsyncEnumerable;
2137+
""" + AsyncStreamsTypes;
2138+
2139+
// https://github.com/dotnet/roslyn/issues/73280 - should not be a langversion error since this remains an error in C# 13
2140+
CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics(
2141+
// (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.
2142+
// await foreach (var x in new C())
2143+
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "foreach").WithArguments("ref and unsafe in async and iterator methods", "13.0").WithLocation(7, 15));
21212144

21222145
var expectedDiagnostics = new[]
21232146
{
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.
2147+
// (7,9): error CS4007: Instance of type 'C.Enumerator' cannot be preserved across 'await' or 'yield' boundary.
21252148
// await foreach (var x in new C())
2126-
Diagnostic(ErrorCode.ERR_BadSpecialByRefIterator, "foreach").WithArguments("C.Enumerator").WithLocation(7, 15)
2149+
Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, @"await foreach (var x in new C())
2150+
{
2151+
" + body + @"
2152+
}").WithArguments("C.Enumerator").WithLocation(7, 9)
21272153
};
21282154

2129-
CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics(expectedDiagnostics);
2130-
CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics(expectedDiagnostics);
2131-
CreateCompilationWithTasksExtensions(source).VerifyDiagnostics(expectedDiagnostics);
2155+
CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.Regular13).VerifyEmitDiagnostics(expectedDiagnostics);
2156+
CreateCompilationWithTasksExtensions(source).VerifyEmitDiagnostics(expectedDiagnostics);
21322157
}
21332158

21342159
[Fact]
@@ -2154,16 +2179,24 @@ public ref struct Enumerator
21542179
}
21552180
""";
21562181

2182+
// https://github.com/dotnet/roslyn/issues/73280 - should not be a langversion error since this remains an error in C# 13
2183+
CreateCompilation(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics(
2184+
// (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.
2185+
// foreach (var x in new C())
2186+
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "foreach").WithArguments("ref and unsafe in async and iterator methods", "13.0").WithLocation(6, 9));
2187+
21572188
var expectedDiagnostics = new[]
21582189
{
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.
2190+
// (6,9): error CS4007: Instance of type 'C.Enumerator' cannot be preserved across 'await' or 'yield' boundary.
21602191
// foreach (var x in new C())
2161-
Diagnostic(ErrorCode.ERR_BadSpecialByRefIterator, "foreach").WithArguments("C.Enumerator").WithLocation(6, 9)
2192+
Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, @"foreach (var x in new C())
2193+
{
2194+
yield return x;
2195+
}").WithArguments("C.Enumerator").WithLocation(6, 9)
21622196
};
21632197

2164-
CreateCompilation(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics(expectedDiagnostics);
2165-
CreateCompilation(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics(expectedDiagnostics);
2166-
CreateCompilation(source).VerifyDiagnostics(expectedDiagnostics);
2198+
CreateCompilation(source, parseOptions: TestOptions.Regular13).VerifyEmitDiagnostics(expectedDiagnostics);
2199+
CreateCompilation(source).VerifyEmitDiagnostics(expectedDiagnostics);
21672200
}
21682201

21692202
[Fact]

0 commit comments

Comments
 (0)