Skip to content

Commit f400b6a

Browse files
BillWagneradegeo
andauthored
Add information for C# 13 feature ref locals and unsafe contexts in iterators and async methods (#41614)
* publish the feature spec Add the feature spec for allowing ref locals and unsafe blocks in iterators and async methods. * Remove restrictions on iterators Remove or annotate any restrictions on iterator methods for `ref` and `unsafe` sections of the language reference. * Remove restrictions on async methods Where applicable, remove restrictions on async methods and await statements. * update any restrictions on `await` * Add feature description for `ref` unsafe * Update csharp-13.md Add note on unsafe * Proofread * fix warnings * typo * Apply suggestions from code review * Apply suggestions from code review Co-authored-by: Andy (Steve) De George <[email protected]> --------- Co-authored-by: Andy (Steve) De George <[email protected]>
1 parent 46785ff commit f400b6a

File tree

14 files changed

+110
-59
lines changed

14 files changed

+110
-59
lines changed

docfx.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -491,7 +491,7 @@
491491
"_csharplang/proposals/csharp-10.0/*.md": "08/07/2021",
492492
"_csharplang/proposals/csharp-11.0/*.md": "09/30/2022",
493493
"_csharplang/proposals/csharp-12.0/*.md": "08/15/2023",
494-
"_csharplang/proposals/*.md": "05/15/2024",
494+
"_csharplang/proposals/*.md": "06/28/2024",
495495
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md": "11/08/2022",
496496
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md": "09/26/2023",
497497
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md": "06/26/2024",
@@ -662,6 +662,7 @@
662662
"_csharplang/proposals/lock-object.md": "Obey lock object semantics for lock statements",
663663
"_csharplang/proposals/method-group-natural-type-improvements.md": "Method group natural type improvements",
664664
"_csharplang/proposals/params-collections.md": "Params collections",
665+
"_csharplang/proposals/ref-unsafe-in-terators-async.md": "Allow ref and unsafe in iterators and async methods",
665666
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md": "C# compiler breaking changes since C# 10",
666667
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md": "C# compiler breaking changes since C# 11",
667668
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md": "C# compiler breaking changes since C# 12",
@@ -778,6 +779,7 @@
778779
"_csharplang/proposals/lock-object.md": "Special-case how `System.Threading.Lock` interacts with the `lock` keyword by calling its `EnterScope` method. Add static analysis warnings to prevent accidental misuse of the type where possible.",
779780
"_csharplang/proposals/method-group-natural-type-improvements.md": "This proposal refines the determination of the natural type of a method group by considering candidates scope-by-scope and pruning at each scope.",
780781
"_csharplang/proposals/params-collections.md": "Allow the `params` modifier on collection types beyond arrays, including `IEnumerable` types.",
782+
"_csharplang/proposals/ref-unsafe-in-terators-async.md": "This proposal modifies restrictions to enable ref local variables and unsafe blocks in iterators and async methods",
781783
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md": "Learn about any breaking changes since the initial release of C# 10",
782784
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md": "Learn about any breaking changes since the initial release of C# 11",
783785
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md": "Learn about any breaking changes since the initial release of C# 12",

docs/csharp/language-reference/builtin-types/ref-struct.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: "ref struct types"
33
description: Learn about the ref struct type in C#
4-
ms.date: 10/12/2022
4+
ms.date: 06/28/2024
55
---
66
# `ref` structure types (C# reference)
77

@@ -12,9 +12,9 @@ You can use the `ref` modifier in the declaration of a [structure type](struct.m
1212
- A `ref struct` can't implement interfaces.
1313
- A `ref struct` can't be boxed to <xref:System.ValueType?displayProperty=nameWithType> or <xref:System.Object?displayProperty=nameWithType>.
1414
- A `ref struct` can't be a type argument.
15-
- A `ref struct` variable can't be captured by a [lambda expression](../operators/lambda-expressions.md) or a [local function](../../programming-guide/classes-and-structs/local-functions.md).
16-
- A `ref struct` variable can't be used in an [`async`](../keywords/async.md) method. However, you can use `ref struct` variables in synchronous methods, for example, in methods that return <xref:System.Threading.Tasks.Task> or <xref:System.Threading.Tasks.Task%601>.
17-
- A `ref struct` variable can't be used in [iterators](../../iterators.md).
15+
- A `ref struct` variable can't be captured in a [lambda expression](../operators/lambda-expressions.md) or a [local function](../../programming-guide/classes-and-structs/local-functions.md).
16+
- Before C# 13,`ref struct` variables can't be used in an `async` method. Beginning with C# 13, a `ref struct` variable can't be used in the same block as the [`await`](../operators/await.md) expression in an [`async`](../keywords/async.md) method. However, you can use `ref struct` variables in synchronous methods, for example, in methods that return <xref:System.Threading.Tasks.Task> or <xref:System.Threading.Tasks.Task%601>.
17+
- Before C# 13, a `ref struct` variable can't be used in [iterators](../../iterators.md). Beginning with C# 13, `ref struct` types and `ref` locals can be used in iterators, provided they aren't in code segments with the `yield return` statement.
1818

1919
You can define a disposable `ref struct`. To do that, ensure that a `ref struct` fits the [disposable pattern](~/_csharplang/proposals/csharp-8.0/using.md#pattern-based-using). That is, it has an instance `Dispose` method, which is accessible, parameterless and has a `void` return type. You can use the [using statement or declaration](../statements/using.md) with an instance of a disposable `ref struct`.
2020

@@ -34,13 +34,13 @@ Beginning with C# 11, you can declare a `ref` field in a `ref struct`, as the fo
3434

3535
:::code language="csharp" source="snippets/shared/StructType.cs" id="SnippetRefField":::
3636

37-
A `ref` field may have the `null` value. Use the <xref:System.Runtime.CompilerServices.Unsafe.IsNullRef%60%601(%60%600@)?displayProperty=nameWithType> method to determine if a `ref` field is `null`.
37+
A `ref` field can have the `null` value. Use the <xref:System.Runtime.CompilerServices.Unsafe.IsNullRef%60%601(%60%600@)?displayProperty=nameWithType> method to determine if a `ref` field is `null`.
3838

3939
You can apply the `readonly` modifier to a `ref` field in the following ways:
4040

4141
- `readonly ref`: You can [ref reassign](../operators/assignment-operator.md#ref-assignment) such a field with the `= ref` operator only inside a constructor or an [`init` accessor](../keywords/init.md). You can assign a value with the `=` operator at any point allowed by the field access modifier.
42-
- `ref readonly`: At any point, you cannot assign a value with the `=` operator to such a field. However, you can ref reassign a field with the `= ref` operator.
43-
- `readonly ref readonly`: You can only ref reassign such a field in a constructor or an `init` accessor. At any point, you cannot assign a value to the field.
42+
- `ref readonly`: At any point, you can't assign a value with the `=` operator to such a field. However, you can ref reassign a field with the `= ref` operator.
43+
- `readonly ref readonly`: You can only ref reassign such a field in a constructor or an `init` accessor. At any point, you can't assign a value to the field.
4444

4545
The compiler ensures that a reference stored in a `ref` field doesn't outlive its referent.
4646

docs/csharp/language-reference/compiler-messages/cs1996.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
description: "Compiler Error CS1996"
33
title: "Compiler Error CS1996"
4-
ms.date: 9/12/2022
4+
ms.date: 7/01/2024
55
f1_keywords:
66
- "CS1996"
77
helpviewer_keywords:
@@ -13,7 +13,7 @@ Cannot await in the body of a lock statement
1313

1414
## Example
1515

16-
The following sample generates CS1996:
16+
The following sample generates CS1996:
1717

1818
```csharp
1919
public class C
@@ -33,9 +33,11 @@ public class C
3333
}
3434
```
3535

36+
The preceding code produces the same error with C# 13, as the `await` is in the `lock` statement block.
37+
3638
## To correct this error
3739

38-
Asynchronous code within a `lock` statement block is hard to implement reliably and even harder to implement in a general sense. The C# compiler doesn't support doing this to avoid emitting code that will be prone to deadlocks. Extracting the asynchronous code from the `lock` statement block will correct this error. For example:
40+
Asynchronous code within a `lock` statement block is hard to implement reliably and even harder to implement in a general sense. The C# compiler doesn't support doing this to avoid emitting code prone to deadlocks. Extracting the asynchronous code from the `lock` statement block corrects this error. For example:
3941

4042
```csharp
4143
public class C

docs/csharp/language-reference/compiler-messages/cs4004.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
description: "Compiler Error CS4004"
33
title: "Compiler Error CS4004"
4-
ms.date: 9/12/2022
4+
ms.date: 07/01/2024
55
f1_keywords:
66
- "CS4004"
77
helpviewer_keywords:
@@ -13,7 +13,7 @@ Cannot await in an unsafe context
1313

1414
## Example
1515

16-
The following sample generates CS4004:
16+
The following sample generates CS4004:
1717

1818
```csharp
1919
using System.Threading.Tasks;
@@ -47,11 +47,13 @@ public static class C
4747
}
4848
```
4949

50+
This code generates an error in C# 13 because the `await` is in the `unsafe` block.
51+
5052
The `ReverseText` method naively uses a background task to asynchronously create a new string in reverse order of a given string.
5153

5254
## To correct this error
5355

54-
Separating the unsafe code from the awaitable code will correct this error. One separation technique is creating a new method for the unsafe code and then calling it from the awaitable code. For example:
56+
Separating the unsafe code from the awaitable code corrects this error. One separation technique is creating a new method for the unsafe code and then calling it from the awaitable code. For example:
5557

5658
```csharp
5759
public static class C

docs/csharp/language-reference/compiler-messages/cs4013.md

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
description: "Compiler Error CS4013"
33
title: "Compiler Error CS4013"
4-
ms.date: 9/12/2022
4+
ms.date: 06/28/2024
55
f1_keywords:
66
- "CS4013"
77
helpviewer_keywords:
@@ -11,6 +11,8 @@ helpviewer_keywords:
1111

1212
Instance of type cannot be used inside a nested function, query expression, iterator block or async method
1313

14+
Beginning with C# 13, `ref struct` types can be used in iterator methods, if they aren't accessed across `yield return` statement.
15+
1416
## Example
1517

1618
The following sample generates CS4013:
@@ -34,11 +36,11 @@ public class C
3436
}
3537
```
3638

37-
This enumerator method extracts lines of text from a character array. It naively tries to use `ReadOnlySpan<T>` to improve performance.
39+
This enumerator method extracts lines of text from a character array. It naively tries to use `ReadOnlySpan<T>` to improve performance. The preceding example exhibits the same error in C# 13, because the `ReadOnlySpan` instance `chars` is in scope at the `yield return` statement.
3840

3941
## To correct this error
4042

41-
`Lines(char[] text)` is an enumerator function. An enumerator function compiles the method's body into a state machine that manages the sequence of states the iterator function goes through while processing. That state machine is implemented as a generated class, and the state is implemented as variables within that class. That captured local state is forced from a stack context to a heap context. Since `ref struct`s like `ReadOnlySpan<T>` cannot be stored in the heap, the CS4013 error is raised. To continue to use a `ReadOnlySpan<T>`, to correct this error, the method must be re-implemented as a non-iterator function, for example:
43+
`Lines(char[] text)` is an enumerator function. An enumerator function compiles the method's body into a state machine that manages the sequence of states the iterator function goes through while processing. That state machine is implemented as a generated class, and the state is implemented as variables within that class. That captured local state is forced from a stack context to a heap context. Since `ref struct`s like `ReadOnlySpan<T>` can't be stored in the heap, the CS4013 error is raised. To continue to use a `ReadOnlySpan<T>`, to correct this error, the method must be reimplemented as a noniterator function, for example:
4244

4345
```csharp
4446
public static IEnumerable<string> Lines2(char[] text)
@@ -59,7 +61,7 @@ This enumerator method extracts lines of text from a character array. It naivel
5961
}
6062
```
6163

62-
To continue to use an iterator function, to correct this error, the method must be re-implemented to avoid using `ReadOnlySpan<T>`, for example:
64+
To continue to use an iterator function, to correct this error, the method must be reimplemented to avoid using `ReadOnlySpan<T>`, for example:
6365

6466
```csharp
6567
public static IEnumerable<string> Lines2(char[] chars)
@@ -75,3 +77,27 @@ To continue to use an iterator function, to correct this error, the method must
7577
yield return new string(chars, startIndex, chars.Length - startIndex);
7678
}
7779
```
80+
81+
In C# 13, a `ReadOnlySpan` can be used, but can only be used in code segments without a `yield return`:
82+
83+
```csharp
84+
static IEnumerable<string> Lines2(char[] text)
85+
{
86+
ReadOnlySpan<char> chars = text;
87+
88+
var lines = new List<string>();
89+
var index = chars.IndexOf('\n');
90+
while (index > 0)
91+
{
92+
lines.Add(chars[..index].ToString());
93+
chars = chars[(index + 1)..];
94+
index = chars.IndexOf('\n');
95+
}
96+
97+
lines.Add(chars.ToString());
98+
foreach(var line in lines)
99+
{
100+
yield return line;
101+
}
102+
}
103+
```

docs/csharp/language-reference/compiler-messages/cs8176.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
description: "Compiler Error CS8176"
33
title: "Compiler Error CS8176"
4-
ms.date: 9/19/2022
4+
ms.date: 06/28/2024
55
f1_keywords:
66
- "CS8176"
77
helpviewer_keywords:
@@ -11,7 +11,9 @@ helpviewer_keywords:
1111

1212
Iterators cannot have by-reference locals
1313

14-
Iterator blocks use deferred execution, where the evaluation of an expression is delayed until its realized value is actually required. To manage that deferred execution state, iterator blocks use a state machine, capturing variable state in closures implemented in compiler-generated classes and properties. A local variable reference (on the stack) cannot be captured within the instance of a class in the heap, so the compiler issues an error.
14+
Iterator blocks use deferred execution, where the evaluation of an expression is delayed until its realized value is required. To manage that deferred execution state, iterator blocks use a state machine, capturing variable state in closures implemented in compiler-generated classes and properties. A local variable reference (on the stack) can't be captured within the instance of a class in the heap, so the compiler issues an error.
15+
16+
Beginning with C# 13, this restriction was removed.
1517

1618
## Example
1719

docs/csharp/language-reference/compiler-messages/cs8177.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
description: "Compiler Error CS8177"
33
title: "Compiler Error CS8177"
4-
ms.date: 9/19/2022
4+
ms.date: 7/01/2024
55
f1_keywords:
66
- "CS8177"
77
helpviewer_keywords:
@@ -11,11 +11,11 @@ helpviewer_keywords:
1111

1212
Async methods cannot have by-reference locals
1313

14-
To manage asynchronous state, `async` methods use a state machine, capturing variable state in closures implemented in compiler-generated classes and properties. A local variable reference (on the stack) cannot be captured within the instance of a class in the heap, so the compiler issues an error.
14+
To manage asynchronous state, `async` methods use a state machine, capturing variable state in closures implemented in compiler-generated classes and properties. A local variable reference (on the stack) can't be captured within the instance of a class in the heap, so the compiler issues an error.
1515

1616
## Example
1717

18-
The following sample generates CS8177:
18+
The following sample generates CS8177 before C# 13:
1919

2020
```csharp
2121
// CS8177.cs (20,26)
@@ -49,7 +49,7 @@ class C
4949

5050
## To correct this error
5151

52-
Changing the variable declaration to remove the `ref` modifier corrects this error:
52+
Remove the `ref` modifier. Or, you can upgrade to C# 13, which ships with .NET 9.
5353

5454
```csharp
5555
class C

0 commit comments

Comments
 (0)