From 07755a8de3bdf1317f2cdd4e6ee698179320484d Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Tue, 2 Jul 2024 14:23:05 -0400 Subject: [PATCH 1/5] Add stubs for new warnings and errors. --- .../compiler-messages/iterator-yield.md | 41 +++++++++++++++++++ docs/csharp/language-reference/toc.yml | 3 ++ 2 files changed, 44 insertions(+) create mode 100644 docs/csharp/language-reference/compiler-messages/iterator-yield.md diff --git a/docs/csharp/language-reference/compiler-messages/iterator-yield.md b/docs/csharp/language-reference/compiler-messages/iterator-yield.md new file mode 100644 index 0000000000000..ab659d56b3b20 --- /dev/null +++ b/docs/csharp/language-reference/compiler-messages/iterator-yield.md @@ -0,0 +1,41 @@ +--- +title: Errors and warnings for iterator methods and `yield return` +description: Use article to diagnose and correct compiler errors and warnings when you write iterator methods that use `yield return` to enumerate a sequence of elements. +f1_keywords: + - "CS9237" + - "CS9238" + - "CS9239" +helpviewer_keywords: + - "CS9237" + - "CS9238" + - "CS9239" +ms.date: 07/02/2024 +--- +# Errors and warnings related to the `yield return` statement and iterator methods + +There are numerous *errors* related to the `yield return` statement and iterator methods: + + +- [**CS9237**](#): *'yield return' should not be used in the body of a lock statement* +- [**CS9238**](#): *Cannot use 'yield return' in an 'unsafe' block* +- [**CS9239**](#): *The `&` operator cannot be used on parameters or local variables in iterator methods.* + +In addition, the compiler might produce the following *warning* related to `lock` statements and thread synchronization: + +- [**CS9216**](#lock-warning): *A value of type `System.Threading.Lock` converted to a different type will use likely unintended monitor-based locking in `lock` statement.* + +## New errors + +- **CS9237**: *''yield return' should not be used in the body of a lock statement* +- **CS9238**: *Cannot use 'yield return' in an 'unsafe' block* +- **CS9239**: *The `&` operator cannot be used on parameters or local variables in iterator methods.* + +These errors indicate that your code violates safety rules because an iterator returns an element and resumes to generate the next element: + +- You can't `yield return` from inside a `lock` statement block. Doing so can cause deadlocks. +- You can't `yield return` from an `unsafe` block. The context for an iterator creates a nested `safe` block within the enclosing `unsafe` block. +- You can't use the `&` operator to take the address of a variable in an iterator method. + +You must update your code to remove the constructs that aren't allowed. diff --git a/docs/csharp/language-reference/toc.yml b/docs/csharp/language-reference/toc.yml index fad57332f76bd..6aeed9dc1e596 100644 --- a/docs/csharp/language-reference/toc.yml +++ b/docs/csharp/language-reference/toc.yml @@ -464,6 +464,9 @@ items: CS8351, CS8373, CS8374, CS8388, CS8977, CS9072, CS9077, CS9078, CS9079, CS9085, CS9086, CS9087, CS9089, CS9091, CS9092, CS9093, CS9094, CS9095, CS9096, CS9097, CS9101, CS9102, CS9104, CS9190, CS9191, CS9192, CS9193, CS9195, CS9196, CS9197, CS9198, CS9199, CS9200, CS9201 + - name: Iterator methods + href: ./iterator-yield.md + displayName: yield return, yield break, CS9237, CS9238, CS9239 - name: Params modifier href: ./compiler-messages/params-arrays.md displayName: > From e2fa5d9c873303104fe5fdbf6685ce03f0b99fa3 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Tue, 2 Jul 2024 15:39:44 -0400 Subject: [PATCH 2/5] Combine existing errors --- .openpublishing.redirection.csharp.json | 48 +- .../compiler-messages/cs4013.md | 103 ---- .../compiler-messages/cs8154.md | 71 --- .../compiler-messages/cs8176.md | 51 -- .../compiler-messages/iterator-yield.md | 450 ++++++++++++++++++ docs/csharp/language-reference/toc.yml | 24 +- docs/csharp/misc/cs1622.md | 32 -- docs/csharp/misc/cs1624.md | 38 -- docs/csharp/misc/cs1625.md | 41 -- docs/csharp/misc/cs1626.md | 46 -- docs/csharp/misc/cs1627.md | 37 -- docs/csharp/misc/cs1629.md | 34 -- docs/csharp/misc/cs1631.md | 41 -- docs/csharp/misc/cs1637.md | 33 -- 14 files changed, 497 insertions(+), 552 deletions(-) delete mode 100644 docs/csharp/language-reference/compiler-messages/cs4013.md delete mode 100644 docs/csharp/language-reference/compiler-messages/cs8154.md delete mode 100644 docs/csharp/language-reference/compiler-messages/cs8176.md delete mode 100644 docs/csharp/misc/cs1622.md delete mode 100644 docs/csharp/misc/cs1624.md delete mode 100644 docs/csharp/misc/cs1625.md delete mode 100644 docs/csharp/misc/cs1626.md delete mode 100644 docs/csharp/misc/cs1627.md delete mode 100644 docs/csharp/misc/cs1629.md delete mode 100644 docs/csharp/misc/cs1631.md delete mode 100644 docs/csharp/misc/cs1637.md diff --git a/.openpublishing.redirection.csharp.json b/.openpublishing.redirection.csharp.json index 643dee94558de..618370f57a13c 100644 --- a/.openpublishing.redirection.csharp.json +++ b/.openpublishing.redirection.csharp.json @@ -400,17 +400,21 @@ "redirect_url": "/dotnet/csharp/language-reference/compiler-messages/array-declaration-errors" }, { - "source_path_from_root": "/docs/csharp/language-reference/compiler-messages/cs7000.md", - "redirect_url": "/dotnet/csharp/language-reference/compiler-messages/using-directive-errors" + "source_path_from_root": "/docs/csharp/language-reference/compiler-messages/cs4013.md", + "redirect_url": "/dotnet/csharp/language-reference/compiler-messages/iterator-yield" }, { - "source_path_from_root": "/docs/csharp/language-reference/compiler-messages/cs8400.md", - "redirect_url": "/dotnet/csharp/language-reference/compiler-messages/feature-version-errors" + "source_path_from_root": "/docs/csharp/language-reference/compiler-messages/cs7000.md", + "redirect_url": "/dotnet/csharp/language-reference/compiler-messages/using-directive-errors" }, { "source_path_from_root": "/docs/csharp/language-reference/compiler-messages/cs8153.md", "redirect_url": "/dotnet/csharp/language-reference/compiler-messages/lambda-expression-errors#conversion-to-expression-trees" }, + { + "source_path_from_root": "/docs/csharp/language-reference/compiler-messages/cs8154.md", + "redirect_url": "/dotnet/csharp/language-reference/compiler-messages/iterator-yield" + }, { "source_path_from_root": "/docs/csharp/language-reference/compiler-messages/cs8155.md", "redirect_url": "/dotnet/csharp/language-reference/compiler-messages/lambda-expression-errors#conversion-to-expression-trees" @@ -435,6 +439,10 @@ "source_path_from_root": "/docs/csharp/language-reference/compiler-messages/cs8175.md", "redirect_url": "/dotnet/csharp/language-reference/compiler-messages/lambda-expression-errors#syntax-limitations-in-lambda-expressions" }, + { + "source_path_from_root": "/docs/csharp/language-reference/compiler-messages/cs8176.md", + "redirect_url": "/dotnet/csharp/language-reference/compiler-messages/iterator-yield" + }, { "source_path_from_root": "/docs/csharp/language-reference/compiler-messages/cs8373.md", "redirect_url": "/dotnet/csharp/language-reference/compiler-messages/ref-modifiers-errors" @@ -443,6 +451,10 @@ "source_path_from_root": "/docs/csharp/language-reference/compiler-messages/cs8374.md", "redirect_url": "/dotnet/csharp/language-reference/compiler-messages/ref-modifiers-errors" }, + { + "source_path_from_root": "/docs/csharp/language-reference/compiler-messages/cs8400.md", + "redirect_url": "/dotnet/csharp/language-reference/compiler-messages/feature-version-errors" + }, { "source_path_from_root": "/docs/csharp/language-reference/compiler-messages/cs8401.md", "redirect_url": "/dotnet/csharp/language-reference/compiler-messages/feature-version-errors" @@ -1668,18 +1680,46 @@ "source_path_from_root": "/docs/csharp/misc/cs1621.md", "redirect_url": "/dotnet/csharp/language-reference/compiler-messages/lambda-expression-errors#syntax-limitations-in-lambda-expressions" }, + { + "source_path_from_root": "/docs/csharp/misc/cs1622.md", + "redirect_url": "/dotnet/csharp/language-reference/compiler-messages/iterator-yield" + }, { "source_path_from_root": "/docs/csharp/misc/cs1623.md", "redirect_url": "/dotnet/csharp/language-reference/compiler-messages/ref-modifiers-errors" }, + { + "source_path_from_root": "/docs/csharp/misc/cs1624.md", + "redirect_url": "/dotnet/csharp/language-reference/compiler-messages/iterator-yield" + }, + { + "source_path_from_root": "/docs/csharp/misc/cs1625.md", + "redirect_url": "/dotnet/csharp/language-reference/compiler-messages/iterator-yield" + }, + { + "source_path_from_root": "/docs/csharp/misc/cs1626.md", + "redirect_url": "/dotnet/csharp/language-reference/compiler-messages/iterator-yield" + }, + { + "source_path_from_root": "/docs/csharp/misc/cs1627.md", + "redirect_url": "/dotnet/csharp/language-reference/compiler-messages/iterator-yield" + }, { "source_path_from_root": "/docs/csharp/misc/cs1628.md", "redirect_url": "/dotnet/csharp/language-reference/compiler-messages/lambda-expression-errors#syntax-limitations-in-lambda-expressions" }, + { + "source_path_from_root": "/docs/csharp/misc/cs1629.md", + "redirect_url": "/dotnet/csharp/language-reference/compiler-messages/iterator-yield" + }, { "source_path_from_root": "/docs/csharp/misc/cs1632.md", "redirect_url": "/dotnet/csharp/language-reference/compiler-messages/lambda-expression-errors#syntax-limitations-in-lambda-expressions" }, + { + "source_path_from_root": "/docs/csharp/misc/cs1637.md", + "redirect_url": "/dotnet/csharp/language-reference/compiler-messages/iterator-yield" + }, { "source_path_from_root": "/docs/csharp/misc/cs1649.md", "redirect_url": "/dotnet/csharp/language-reference/compiler-messages/ref-modifiers-errors" diff --git a/docs/csharp/language-reference/compiler-messages/cs4013.md b/docs/csharp/language-reference/compiler-messages/cs4013.md deleted file mode 100644 index 71d12c75fe017..0000000000000 --- a/docs/csharp/language-reference/compiler-messages/cs4013.md +++ /dev/null @@ -1,103 +0,0 @@ ---- -description: "Compiler Error CS4013" -title: "Compiler Error CS4013" -ms.date: 06/28/2024 -f1_keywords: - - "CS4013" -helpviewer_keywords: - - "CS4013" ---- -# Compiler Error CS4013 - -Instance of type cannot be used inside a nested function, query expression, iterator block or async method - -Beginning with C# 13, `ref struct` types can be used in iterator methods, if they aren't accessed across `yield return` statement. - -## Example - - The following sample generates CS4013: - -```csharp -public class C -{ - public static IEnumerable Lines(char[] text) - { - ReadOnlySpan chars = text; - var index = chars.IndexOf('\n'); - while (index > 0) - { - yield return chars[..index].ToString(); - chars = chars[(index + 1)..]; - index = chars.IndexOf('\n'); - } - - yield return chars.ToString(); - } -} -``` - -This enumerator method extracts lines of text from a character array. It naively tries to use `ReadOnlySpan` 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. - -## To correct this error - -`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` can't be stored in the heap, the CS4013 error is raised. To continue to use a `ReadOnlySpan`, to correct this error, the method must be reimplemented as a noniterator function, for example: - -```csharp - public static IEnumerable Lines2(char[] text) - { - ReadOnlySpan chars = text; - - var lines = new List(); - var index = chars.IndexOf('\n'); - while (index > 0) - { - lines.Add(chars[..index].ToString()); - chars = chars[(index+1)..]; - index = chars.IndexOf('\n'); - } - - lines.Add(chars.ToString()); - return lines; - } -``` - -To continue to use an iterator function, to correct this error, the method must be reimplemented to avoid using `ReadOnlySpan`, for example: - -```csharp - public static IEnumerable Lines2(char[] chars) - { - var startIndex = 0; - var index = Array.IndexOf(chars,'\n'); - while (index > 0) - { - yield return new string(chars, startIndex, index); - startIndex = index+1; - index = Array.IndexOf(chars, '\n', startIndex); - } - yield return new string(chars, startIndex, chars.Length - startIndex); - } -``` - -In C# 13, a `ReadOnlySpan` can be used, but can only be used in code segments without a `yield return`: - -```csharp -static IEnumerable Lines2(char[] text) -{ - ReadOnlySpan chars = text; - - var lines = new List(); - var index = chars.IndexOf('\n'); - while (index > 0) - { - lines.Add(chars[..index].ToString()); - chars = chars[(index + 1)..]; - index = chars.IndexOf('\n'); - } - - lines.Add(chars.ToString()); - foreach(var line in lines) - { - yield return line; - } -} -``` diff --git a/docs/csharp/language-reference/compiler-messages/cs8154.md b/docs/csharp/language-reference/compiler-messages/cs8154.md deleted file mode 100644 index dbd5e8ae64258..0000000000000 --- a/docs/csharp/language-reference/compiler-messages/cs8154.md +++ /dev/null @@ -1,71 +0,0 @@ ---- -description: "Compiler Error CS8154" -title: "Compiler Error CS8154" -ms.date: 9/19/2022 -f1_keywords: - - "CS8154" -helpviewer_keywords: - - "CS8154" ---- -# Compiler Error CS8154 - -The body cannot be an iterator block because it returns by reference - -## Example - - The following sample generates CS8154: - -```csharp -// CS8154.cs (12,17) - -class TestClass -{ - int x = 0; - ref int TestFunction() - { - if (true) - { - yield return x; - } - - ref int localFunction() - { - if (true) - { - yield return x; - } - } - - yield return localFunction(); - } -} -``` - -## To correct this error - -Iterator methods cannot return by reference, refactoring to return by value corrects this error: - -```csharp -class TestClass -{ - int x = 0; - IEnumerable TestFunction() - { - if (true) - { - yield return x; - } - - IEnumerable localFunction() - { - if (true) - { - yield return x; - } - } - - foreach (var item in localFunction()) - yield return item; - } -} -``` diff --git a/docs/csharp/language-reference/compiler-messages/cs8176.md b/docs/csharp/language-reference/compiler-messages/cs8176.md deleted file mode 100644 index 21a94a76614c2..0000000000000 --- a/docs/csharp/language-reference/compiler-messages/cs8176.md +++ /dev/null @@ -1,51 +0,0 @@ ---- -description: "Compiler Error CS8176" -title: "Compiler Error CS8176" -ms.date: 06/28/2024 -f1_keywords: - - "CS8176" -helpviewer_keywords: - - "CS8176" ---- -# Compiler Error CS8176 - -Iterators cannot have by-reference locals - -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. - -Beginning with C# 13, this restriction was removed. - -## Example - - The following sample generates CS8176: - -```csharp -// CS8176.cs (7,26) - -using System.Collections.Generic; -class C -{ - IEnumerable M() - { - ref readonly int x = ref (new int[1])[0]; - int i = x; - yield return i; - } -} -``` - -## To correct this error - -Removing use of by-reference corrects this error: - -```csharp -class C -{ - IEnumerable M() - { - int x = (new int[1])[0]; - int i = x; - yield return i; - } -} -``` diff --git a/docs/csharp/language-reference/compiler-messages/iterator-yield.md b/docs/csharp/language-reference/compiler-messages/iterator-yield.md index ab659d56b3b20..4c0ebe314965c 100644 --- a/docs/csharp/language-reference/compiler-messages/iterator-yield.md +++ b/docs/csharp/language-reference/compiler-messages/iterator-yield.md @@ -2,10 +2,32 @@ title: Errors and warnings for iterator methods and `yield return` description: Use article to diagnose and correct compiler errors and warnings when you write iterator methods that use `yield return` to enumerate a sequence of elements. f1_keywords: + - "CS1622" + - "CS1624" + - "CS1625" + - "CS1626" + - "CS1627" + - "CS1629" + - "CS1631" + - "CS1637" + - "CS4013" + - "CS8154" + - "CS8176" - "CS9237" - "CS9238" - "CS9239" helpviewer_keywords: + - "CS1622" + - "CS1624" + - "CS1625" + - "CS1626" + - "CS1627" + - "CS1629" + - "CS1631" + - "CS1637" + - "CS4013" + - "CS8154" + - "CS8176" - "CS9237" - "CS9238" - "CS9239" @@ -18,6 +40,17 @@ There are numerous *errors* related to the `yield return` statement and iterator +- [**CS1622**](#): *Cannot return a value from an iterator. Use the yield return statement to return a value, or yield break to end the iteration.* +- [**CS1624**](#): *The body of 'accessor' cannot be an iterator block because 'type' is not an iterator interface type* +- [**CS1625**](#): *Cannot yield in the body of a finally clause* +- [**CS1626**](#): *Cannot yield a value in the body of a try block with a catch clause* +- [**CS1627**](#): *Expression expected after yield return* +- [**CS1629**](#): *Unsafe code may not appear in iterators* +- [**CS1631**](#): *Cannot yield a value in the body of a catch clause* +- [**CS1637**](#): *Iterators cannot have unsafe parameters or yield types* +- [**CS4013**](#): *Instance of type cannot be used inside a nested function, query expression, iterator block or async method* +- [**CS8154**](#): *The body cannot be an iterator block because it returns by reference* +- [**CS8176**](#): *Iterators cannot have by-reference locals* - [**CS9237**](#): *'yield return' should not be used in the body of a lock statement* - [**CS9238**](#): *Cannot use 'yield return' in an 'unsafe' block* - [**CS9239**](#): *The `&` operator cannot be used on parameters or local variables in iterator methods.* @@ -26,6 +59,423 @@ In addition, the compiler might produce the following *warning* related to `lock - [**CS9216**](#lock-warning): *A value of type `System.Threading.Lock` converted to a different type will use likely unintended monitor-based locking in `lock` statement.* +## Compiler Error CS1622 + +Cannot return a value from an iterator. Use the yield return statement to return a value, or yield break to end the iteration. + +An iterator is a special function that returns a value via the yield statement rather than the return statement. For more information, see **iterators**. + +The following sample generates CS1622: + +```csharp +// CS1622.cs +// compile with: /target:library +using System.Collections; + +class C : IEnumerable +{ + public IEnumerator GetEnumerator() + { + return (IEnumerator) this; // CS1622 + yield return this; // OK + } +} +``` + +## Compiler Error CS1624 + +The body of 'accessor' cannot be an iterator block because 'type' is not an iterator interface type + +This error occurs if an iterator accessor is used but the return type is not one of the iterator interface types: , , , . To avoid this error, use one of the iterator interface types as a return type. + +The following sample generates CS1624: + +```csharp +// CS1624.cs +using System; +using System.Collections; + +class C +{ + public int Iterator + // Try this instead: + // public IEnumerable Iterator + { + get // CS1624 + { + yield return 1; + } + } +} +``` + +## Compiler Error CS1625 + +Cannot yield in the body of a finally clause + +A yield statement is not allowed in the body of a finally clause. To avoid this error, move the yield statement out of the finally clause. + +The following sample generates CS1625: + +```csharp +// CS1625.cs +using System.Collections; + +class C : IEnumerable +{ + public IEnumerator GetEnumerator() + { + try + { + } + finally + { + yield return this; // CS1625 + } + } +} + +public class CMain +{ + public static void Main() { } +} +``` + +## Compiler Error CS1626 + +Cannot yield a value in the body of a try block with a catch clause + +A yield statement is not allowed in a try block if there is a catch clause associated with the try block. To avoid this error, either move the yield statement out of the try/catch/finally block, or remove the catch block. + +The following sample generates CS1626: + +```csharp +// CS1626.cs +using System.Collections; + +class C : IEnumerable +{ + public IEnumerator GetEnumerator() + { + try + { + yield return this; // CS1626 + } + catch + { + + } + finally + { + + } + } +} + +public class CMain +{ + public static void Main() { } +} +``` + +## Compiler Error CS1627 + +Expression expected after yield return + +This error occurs if `yield` is used without an expression. To avoid this error, insert the appropriate expression in the statement. + +The following sample generates CS1627: + +```csharp +// CS1627.cs +using System.Collections; + +class C : IEnumerable +{ + public IEnumerator GetEnumerator() + { + yield return; // CS1627 + // To resolve, add the following line: + // yield return 0; + } +} + +public class CMain +{ + public static void Main() { } +} +``` + +## Compiler Error CS1629 + +Unsafe code may not appear in iterators + +The C# language specification doesn't allow unsafe code in iterators. This restriction is relaxed in C# 13. You can use `unsafe` blocks, but the `yield return` statement can't be used in an `unsafe` block. + +The following sample generates CS1629: + +```csharp +// CS1629.cs +// compile with: /unsafe +using System.Collections.Generic; +class C +{ + IEnumerator IteratorMethod() + { + int i; + unsafe // CS1629 + { + int *p = &i; + yield return *p; + } + } +} +``` + +# Compiler Error CS1631 + +Cannot yield a value in the body of a catch clause + +The yield statement is not allowed from within the body of a catch clause. To avoid this error, move the yield statement outside the body of the catch clause. + +The following sample generates CS1631: + +```csharp +// CS1631.cs +using System; +using System.Collections; + +public class C : IEnumerable +{ + public IEnumerator GetEnumerator() + { + try + { + } + catch(Exception e) + { + yield return this; // CS1631 + } + } + + public static void Main() + { + } +} +``` + +## Compiler Error CS1637 + +Iterators cannot have unsafe parameters or yield types + +Check the argument list of the iterator and the type of any yield statements to verify that you are not using any unsafe types. + +The following sample generates CS1637: + +```csharp +// CS1637.cs +// compile with: /unsafe +using System.Collections; + +public unsafe class C +{ + public IEnumerator Iterator1(int* p) // CS1637 + { + yield return null; + } +} +``` + +## Compiler Error CS4013 + +Instance of type cannot be used inside a nested function, query expression, iterator block or async method + +Beginning with C# 13, `ref struct` types can be used in iterator methods, if they aren't accessed across `yield return` statement. + +The following sample generates CS4013: + +```csharp +public class C +{ + public static IEnumerable Lines(char[] text) + { + ReadOnlySpan chars = text; + var index = chars.IndexOf('\n'); + while (index > 0) + { + yield return chars[..index].ToString(); + chars = chars[(index + 1)..]; + index = chars.IndexOf('\n'); + } + + yield return chars.ToString(); + } +} +``` + +This enumerator method extracts lines of text from a character array. It naively tries to use `ReadOnlySpan` 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. + +To correct this error: + +`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` can't be stored in the heap, the CS4013 error is raised. To continue to use a `ReadOnlySpan`, to correct this error, the method must be reimplemented as a noniterator function, for example: + +```csharp + public static IEnumerable Lines2(char[] text) + { + ReadOnlySpan chars = text; + + var lines = new List(); + var index = chars.IndexOf('\n'); + while (index > 0) + { + lines.Add(chars[..index].ToString()); + chars = chars[(index+1)..]; + index = chars.IndexOf('\n'); + } + + lines.Add(chars.ToString()); + return lines; + } +``` + +To continue to use an iterator function, to correct this error, the method must be reimplemented to avoid using `ReadOnlySpan`, for example: + +```csharp + public static IEnumerable Lines2(char[] chars) + { + var startIndex = 0; + var index = Array.IndexOf(chars,'\n'); + while (index > 0) + { + yield return new string(chars, startIndex, index); + startIndex = index+1; + index = Array.IndexOf(chars, '\n', startIndex); + } + yield return new string(chars, startIndex, chars.Length - startIndex); + } +``` + +In C# 13, a `ReadOnlySpan` can be used, but can only be used in code segments without a `yield return`: + +```csharp +static IEnumerable Lines2(char[] text) +{ + ReadOnlySpan chars = text; + + var lines = new List(); + var index = chars.IndexOf('\n'); + while (index > 0) + { + lines.Add(chars[..index].ToString()); + chars = chars[(index + 1)..]; + index = chars.IndexOf('\n'); + } + + lines.Add(chars.ToString()); + foreach(var line in lines) + { + yield return line; + } +} +``` + +## Compiler Error CS8154 + +The body cannot be an iterator block because it returns by reference + +The following sample generates CS8154: + +```csharp +// CS8154.cs (12,17) + +class TestClass +{ + int x = 0; + ref int TestFunction() + { + if (true) + { + yield return x; + } + + ref int localFunction() + { + if (true) + { + yield return x; + } + } + + yield return localFunction(); + } +} +``` + +Iterator methods cannot return by reference, refactoring to return by value corrects this error: + +```csharp +class TestClass +{ + int x = 0; + IEnumerable TestFunction() + { + if (true) + { + yield return x; + } + + IEnumerable localFunction() + { + if (true) + { + yield return x; + } + } + + foreach (var item in localFunction()) + yield return item; + } +} +``` + +## Compiler Error CS8176 + +Iterators cannot have by-reference locals + +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. + +Beginning with C# 13, this restriction was removed. + +The following sample generates CS8176: + +```csharp +// CS8176.cs (7,26) + +using System.Collections.Generic; +class C +{ + IEnumerable M() + { + ref readonly int x = ref (new int[1])[0]; + int i = x; + yield return i; + } +} +``` + +Removing use of by-reference corrects this error: + +```csharp +class C +{ + IEnumerable M() + { + int x = (new int[1])[0]; + int i = x; + yield return i; + } +} +``` ## New errors - **CS9237**: *''yield return' should not be used in the body of a lock statement* diff --git a/docs/csharp/language-reference/toc.yml b/docs/csharp/language-reference/toc.yml index 6aeed9dc1e596..dc4863ffa74f0 100644 --- a/docs/csharp/language-reference/toc.yml +++ b/docs/csharp/language-reference/toc.yml @@ -466,7 +466,9 @@ items: CS9198, CS9199, CS9200, CS9201 - name: Iterator methods href: ./iterator-yield.md - displayName: yield return, yield break, CS9237, CS9238, CS9239 + displayName: > + yield return, yield break, + CS1622, CS1624, CS1625, CS1626, CS1627, CS1629, CS1631, CS1637, CS4013, CS8154, CS8176, CS9237, CS9238, CS9239 - name: Params modifier href: ./compiler-messages/params-arrays.md displayName: > @@ -1652,22 +1654,8 @@ items: href: ../misc/cs1619.md - name: CS1620 href: ../misc/cs1620.md - - name: CS1622 - href: ../misc/cs1622.md - - name: CS1624 - href: ../misc/cs1624.md - - name: CS1625 - href: ../misc/cs1625.md - - name: CS1626 - href: ../misc/cs1626.md - - name: CS1627 - href: ../misc/cs1627.md - - name: CS1629 - href: ../misc/cs1629.md - name: CS1630 href: ../misc/cs1630.md - - name: CS1631 - href: ../misc/cs1631.md - name: CS1637 href: ../misc/cs1637.md - name: CS1638 @@ -1916,8 +1904,6 @@ items: href: ./compiler-messages/cs4008.md - name: CS4009 href: ../misc/CS4009.md - - name: CS4013 - href: ./compiler-messages/cs4013.md - name: CS4032 href: ./compiler-messages/cs4032.md - name: CS4033 @@ -1962,8 +1948,6 @@ items: href: ./compiler-messages/cs8151.md - name: CS8152 href: ./compiler-messages/cs8152.md - - name: CS8154 - href: ./compiler-messages/cs8154.md - name: CS8156 href: ./compiler-messages/cs8156.md - name: CS8157 @@ -1990,8 +1974,6 @@ items: href: ./compiler-messages/cs8173.md - name: CS8174 href: ./compiler-messages/cs8174.md - - name: CS8176 - href: ./compiler-messages/cs8176.md - name: CS8177 href: ./compiler-messages/cs8177.md - name: CS8178 diff --git a/docs/csharp/misc/cs1622.md b/docs/csharp/misc/cs1622.md deleted file mode 100644 index b25da53b5fb37..0000000000000 --- a/docs/csharp/misc/cs1622.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -description: "Compiler Error CS1622" -title: "Compiler Error CS1622" -ms.date: 07/20/2015 -f1_keywords: - - "CS1622" -helpviewer_keywords: - - "CS1622" -ms.assetid: 6b53a777-4cd8-423a-84ff-22ff588044d3 ---- -# Compiler Error CS1622 - -Cannot return a value from an iterator. Use the yield return statement to return a value, or yield break to end the iteration. - - An iterator is a special function that returns a value via the yield statement rather than the return statement. For more information, see **iterators**. - - The following sample generates CS1622: - -```csharp -// CS1622.cs -// compile with: /target:library -using System.Collections; - -class C : IEnumerable -{ - public IEnumerator GetEnumerator() - { - return (IEnumerator) this; // CS1622 - yield return this; // OK - } -} -``` diff --git a/docs/csharp/misc/cs1624.md b/docs/csharp/misc/cs1624.md deleted file mode 100644 index 0da881f4f26ac..0000000000000 --- a/docs/csharp/misc/cs1624.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -description: "Compiler Error CS1624" -title: "Compiler Error CS1624" -ms.date: 07/20/2015 -f1_keywords: - - "CS1624" -helpviewer_keywords: - - "CS1624" -ms.assetid: af7d049d-27e2-4ce1-973c-5c2cb3e56a63 ---- -# Compiler Error CS1624 - -The body of 'accessor' cannot be an iterator block because 'type' is not an iterator interface type - - This error occurs if an iterator accessor is used but the return type is not one of the iterator interface types: , , , . To avoid this error, use one of the iterator interface types as a return type. - -## Example - - The following sample generates CS1624: - -```csharp -// CS1624.cs -using System; -using System.Collections; - -class C -{ - public int Iterator - // Try this instead: - // public IEnumerable Iterator - { - get // CS1624 - { - yield return 1; - } - } -} -``` diff --git a/docs/csharp/misc/cs1625.md b/docs/csharp/misc/cs1625.md deleted file mode 100644 index 6408b0c97fea5..0000000000000 --- a/docs/csharp/misc/cs1625.md +++ /dev/null @@ -1,41 +0,0 @@ ---- -description: "Compiler Error CS1625" -title: "Compiler Error CS1625" -ms.date: 07/20/2015 -f1_keywords: - - "CS1625" -helpviewer_keywords: - - "CS1625" -ms.assetid: 0b25b7f9-a585-49b0-9ee6-4384e87fcea6 ---- -# Compiler Error CS1625 - -Cannot yield in the body of a finally clause - - A yield statement is not allowed in the body of a finally clause. To avoid this error, move the yield statement out of the finally clause. - - The following sample generates CS1625: - -```csharp -// CS1625.cs -using System.Collections; - -class C : IEnumerable -{ - public IEnumerator GetEnumerator() - { - try - { - } - finally - { - yield return this; // CS1625 - } - } -} - -public class CMain -{ - public static void Main() { } -} -``` diff --git a/docs/csharp/misc/cs1626.md b/docs/csharp/misc/cs1626.md deleted file mode 100644 index 0d2d22e10da50..0000000000000 --- a/docs/csharp/misc/cs1626.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -description: "Compiler Error CS1626" -title: "Compiler Error CS1626" -ms.date: 07/20/2015 -f1_keywords: - - "CS1626" -helpviewer_keywords: - - "CS1626" -ms.assetid: 3ba03383-eb24-4fd8-bf40-8b0f7d6baf0d ---- -# Compiler Error CS1626 - -Cannot yield a value in the body of a try block with a catch clause - - A yield statement is not allowed in a try block if there is a catch clause associated with the try block. To avoid this error, either move the yield statement out of the try/catch/finally block, or remove the catch block. - - The following sample generates CS1626: - -```csharp -// CS1626.cs -using System.Collections; - -class C : IEnumerable -{ - public IEnumerator GetEnumerator() - { - try - { - yield return this; // CS1626 - } - catch - { - - } - finally - { - - } - } -} - -public class CMain -{ - public static void Main() { } -} -``` diff --git a/docs/csharp/misc/cs1627.md b/docs/csharp/misc/cs1627.md deleted file mode 100644 index ddc77233f6631..0000000000000 --- a/docs/csharp/misc/cs1627.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -description: "Compiler Error CS1627" -title: "Compiler Error CS1627" -ms.date: 07/20/2015 -f1_keywords: - - "CS1627" -helpviewer_keywords: - - "CS1627" -ms.assetid: 58dd6e22-e9ed-4e5c-ae04-ce255f07064e ---- -# Compiler Error CS1627 - -Expression expected after yield return - - This error occurs if `yield` is used without an expression. To avoid this error, insert the appropriate expression in the statement. - - The following sample generates CS1627: - -```csharp -// CS1627.cs -using System.Collections; - -class C : IEnumerable -{ - public IEnumerator GetEnumerator() - { - yield return; // CS1627 - // To resolve, add the following line: - // yield return 0; - } -} - -public class CMain -{ - public static void Main() { } -} -``` diff --git a/docs/csharp/misc/cs1629.md b/docs/csharp/misc/cs1629.md deleted file mode 100644 index 5f404a55550c8..0000000000000 --- a/docs/csharp/misc/cs1629.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -description: "Compiler Error CS1629" -title: "Compiler Error CS1629" -ms.date: 06/28/2024 -f1_keywords: - - "CS1629" -helpviewer_keywords: - - "CS1629" ---- -# Compiler Error CS1629 - -Unsafe code may not appear in iterators - -The C# language specification doesn't allow unsafe code in iterators. This restriction is relaxed in C# 13. You can use `unsafe` blocks, but the `yield return` statement can't be used in an `unsafe` block. - -The following sample generates CS1629: - -```csharp -// CS1629.cs -// compile with: /unsafe -using System.Collections.Generic; -class C -{ - IEnumerator IteratorMethod() - { - int i; - unsafe // CS1629 - { - int *p = &i; - yield return *p; - } - } -} -``` diff --git a/docs/csharp/misc/cs1631.md b/docs/csharp/misc/cs1631.md deleted file mode 100644 index 91822185e6bc3..0000000000000 --- a/docs/csharp/misc/cs1631.md +++ /dev/null @@ -1,41 +0,0 @@ ---- -description: "Compiler Error CS1631" -title: "Compiler Error CS1631" -ms.date: 07/20/2015 -f1_keywords: - - "CS1631" -helpviewer_keywords: - - "CS1631" -ms.assetid: bf0c5ff9-90a3-4db6-b4ee-0b93e31614e0 ---- -# Compiler Error CS1631 - -Cannot yield a value in the body of a catch clause - - The yield statement is not allowed from within the body of a catch clause. To avoid this error, move the yield statement outside the body of the catch clause. - - The following sample generates CS1631: - -```csharp -// CS1631.cs -using System; -using System.Collections; - -public class C : IEnumerable -{ - public IEnumerator GetEnumerator() - { - try - { - } - catch(Exception e) - { - yield return this; // CS1631 - } - } - - public static void Main() - { - } -} -``` diff --git a/docs/csharp/misc/cs1637.md b/docs/csharp/misc/cs1637.md deleted file mode 100644 index 14f9812a1cb60..0000000000000 --- a/docs/csharp/misc/cs1637.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -description: "Compiler Error CS1637" -title: "Compiler Error CS1637" -ms.date: 07/20/2015 -f1_keywords: - - "CS1637" -helpviewer_keywords: - - "CS1637" -ms.assetid: 95aa82ab-bd52-4def-b5f3-d65e6dcb3855 ---- -# Compiler Error CS1637 - -Iterators cannot have unsafe parameters or yield types - - Check the argument list of the iterator and the type of any yield statements to verify that you are not using any unsafe types. - -## Example - - The following sample generates CS1637: - -```csharp -// CS1637.cs -// compile with: /unsafe -using System.Collections; - -public unsafe class C -{ - public IEnumerator Iterator1(int* p) // CS1637 - { - yield return null; - } -} -``` From 1874c7812863d29514179e2f11b7c62c1a74022a Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Tue, 2 Jul 2024 16:01:08 -0400 Subject: [PATCH 3/5] fix build warnings --- .openpublishing.redirection.csharp.json | 4 +++ .../compiler-messages/iterator-yield.md | 35 +++++++++---------- docs/csharp/language-reference/toc.yml | 2 +- 3 files changed, 21 insertions(+), 20 deletions(-) diff --git a/.openpublishing.redirection.csharp.json b/.openpublishing.redirection.csharp.json index 618370f57a13c..43036829fd283 100644 --- a/.openpublishing.redirection.csharp.json +++ b/.openpublishing.redirection.csharp.json @@ -1712,6 +1712,10 @@ "source_path_from_root": "/docs/csharp/misc/cs1629.md", "redirect_url": "/dotnet/csharp/language-reference/compiler-messages/iterator-yield" }, + { + "source_path_from_root": "/docs/csharp/misc/cs1631.md", + "redirect_url": "/dotnet/csharp/language-reference/compiler-messages/iterator-yield" + }, { "source_path_from_root": "/docs/csharp/misc/cs1632.md", "redirect_url": "/dotnet/csharp/language-reference/compiler-messages/lambda-expression-errors#syntax-limitations-in-lambda-expressions" diff --git a/docs/csharp/language-reference/compiler-messages/iterator-yield.md b/docs/csharp/language-reference/compiler-messages/iterator-yield.md index 4c0ebe314965c..503eb007ecdb4 100644 --- a/docs/csharp/language-reference/compiler-messages/iterator-yield.md +++ b/docs/csharp/language-reference/compiler-messages/iterator-yield.md @@ -40,24 +40,20 @@ There are numerous *errors* related to the `yield return` statement and iterator -- [**CS1622**](#): *Cannot return a value from an iterator. Use the yield return statement to return a value, or yield break to end the iteration.* -- [**CS1624**](#): *The body of 'accessor' cannot be an iterator block because 'type' is not an iterator interface type* -- [**CS1625**](#): *Cannot yield in the body of a finally clause* -- [**CS1626**](#): *Cannot yield a value in the body of a try block with a catch clause* -- [**CS1627**](#): *Expression expected after yield return* -- [**CS1629**](#): *Unsafe code may not appear in iterators* -- [**CS1631**](#): *Cannot yield a value in the body of a catch clause* -- [**CS1637**](#): *Iterators cannot have unsafe parameters or yield types* -- [**CS4013**](#): *Instance of type cannot be used inside a nested function, query expression, iterator block or async method* -- [**CS8154**](#): *The body cannot be an iterator block because it returns by reference* -- [**CS8176**](#): *Iterators cannot have by-reference locals* -- [**CS9237**](#): *'yield return' should not be used in the body of a lock statement* -- [**CS9238**](#): *Cannot use 'yield return' in an 'unsafe' block* -- [**CS9239**](#): *The `&` operator cannot be used on parameters or local variables in iterator methods.* - -In addition, the compiler might produce the following *warning* related to `lock` statements and thread synchronization: - -- [**CS9216**](#lock-warning): *A value of type `System.Threading.Lock` converted to a different type will use likely unintended monitor-based locking in `lock` statement.* +- [**CS1622**](#compiler-error-cs1622): *Cannot return a value from an iterator. Use the yield return statement to return a value, or yield break to end the iteration.* +- [**CS1624**](#compiler-error-cs1624): *The body of 'accessor' cannot be an iterator block because 'type' is not an iterator interface type* +- [**CS1625**](#compiler-error-cs1625): *Cannot yield in the body of a finally clause* +- [**CS1626**](#compiler-error-cs1626): *Cannot yield a value in the body of a try block with a catch clause* +- [**CS1627**](#compiler-error-cs1627): *Expression expected after yield return* +- [**CS1629**](#compiler-error-cs1629): *Unsafe code may not appear in iterators* +- [**CS1631**](#compiler-error-cs1631): *Cannot yield a value in the body of a catch clause* +- [**CS1637**](#compiler-error-cs1637): *Iterators cannot have unsafe parameters or yield types* +- [**CS4013**](#compiler-error-cs4013): *Instance of type cannot be used inside a nested function, query expression, iterator block or async method* +- [**CS8154**](#compiler-error-cs8154): *The body cannot be an iterator block because it returns by reference* +- [**CS8176**](#compiler-error-cs8176): *Iterators cannot have by-reference locals* +- [**CS9237**](#new-errors): *'yield return' should not be used in the body of a lock statement* +- [**CS9238**](#new-errors): *Cannot use 'yield return' in an 'unsafe' block* +- [**CS9239**](#new-errors): *The `&` operator cannot be used on parameters or local variables in iterator methods.* ## Compiler Error CS1622 @@ -232,7 +228,7 @@ class C } ``` -# Compiler Error CS1631 +## Compiler Error CS1631 Cannot yield a value in the body of a catch clause @@ -476,6 +472,7 @@ class C } } ``` + ## New errors - **CS9237**: *''yield return' should not be used in the body of a lock statement* diff --git a/docs/csharp/language-reference/toc.yml b/docs/csharp/language-reference/toc.yml index dc4863ffa74f0..57a0061e10f59 100644 --- a/docs/csharp/language-reference/toc.yml +++ b/docs/csharp/language-reference/toc.yml @@ -465,7 +465,7 @@ items: CS9093, CS9094, CS9095, CS9096, CS9097, CS9101, CS9102, CS9104, CS9190, CS9191, CS9192, CS9193, CS9195, CS9196, CS9197, CS9198, CS9199, CS9200, CS9201 - name: Iterator methods - href: ./iterator-yield.md + href: ./compiler-messages/iterator-yield.md displayName: > yield return, yield break, CS1622, CS1624, CS1625, CS1626, CS1627, CS1629, CS1631, CS1637, CS4013, CS8154, CS8176, CS9237, CS9238, CS9239 From cd6498c090f8f718f1b54917f80b74f38bb8fbb9 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Wed, 3 Jul 2024 15:39:59 -0400 Subject: [PATCH 4/5] Edit pass --- .../compiler-messages/iterator-yield.md | 492 ++---------------- docs/csharp/whats-new/csharp-13.md | 2 +- 2 files changed, 57 insertions(+), 437 deletions(-) diff --git a/docs/csharp/language-reference/compiler-messages/iterator-yield.md b/docs/csharp/language-reference/compiler-messages/iterator-yield.md index 503eb007ecdb4..873f6387f57a0 100644 --- a/docs/csharp/language-reference/compiler-messages/iterator-yield.md +++ b/docs/csharp/language-reference/compiler-messages/iterator-yield.md @@ -40,449 +40,69 @@ There are numerous *errors* related to the `yield return` statement and iterator -- [**CS1622**](#compiler-error-cs1622): *Cannot return a value from an iterator. Use the yield return statement to return a value, or yield break to end the iteration.* -- [**CS1624**](#compiler-error-cs1624): *The body of 'accessor' cannot be an iterator block because 'type' is not an iterator interface type* -- [**CS1625**](#compiler-error-cs1625): *Cannot yield in the body of a finally clause* -- [**CS1626**](#compiler-error-cs1626): *Cannot yield a value in the body of a try block with a catch clause* -- [**CS1627**](#compiler-error-cs1627): *Expression expected after yield return* -- [**CS1629**](#compiler-error-cs1629): *Unsafe code may not appear in iterators* -- [**CS1631**](#compiler-error-cs1631): *Cannot yield a value in the body of a catch clause* -- [**CS1637**](#compiler-error-cs1637): *Iterators cannot have unsafe parameters or yield types* -- [**CS4013**](#compiler-error-cs4013): *Instance of type cannot be used inside a nested function, query expression, iterator block or async method* -- [**CS8154**](#compiler-error-cs8154): *The body cannot be an iterator block because it returns by reference* -- [**CS8176**](#compiler-error-cs8176): *Iterators cannot have by-reference locals* -- [**CS9237**](#new-errors): *'yield return' should not be used in the body of a lock statement* -- [**CS9238**](#new-errors): *Cannot use 'yield return' in an 'unsafe' block* -- [**CS9239**](#new-errors): *The `&` operator cannot be used on parameters or local variables in iterator methods.* - -## Compiler Error CS1622 - -Cannot return a value from an iterator. Use the yield return statement to return a value, or yield break to end the iteration. - -An iterator is a special function that returns a value via the yield statement rather than the return statement. For more information, see **iterators**. - -The following sample generates CS1622: - -```csharp -// CS1622.cs -// compile with: /target:library -using System.Collections; - -class C : IEnumerable -{ - public IEnumerator GetEnumerator() - { - return (IEnumerator) this; // CS1622 - yield return this; // OK - } -} -``` - -## Compiler Error CS1624 - -The body of 'accessor' cannot be an iterator block because 'type' is not an iterator interface type - -This error occurs if an iterator accessor is used but the return type is not one of the iterator interface types: , , , . To avoid this error, use one of the iterator interface types as a return type. - -The following sample generates CS1624: - -```csharp -// CS1624.cs -using System; -using System.Collections; - -class C -{ - public int Iterator - // Try this instead: - // public IEnumerable Iterator - { - get // CS1624 - { - yield return 1; - } - } -} -``` - -## Compiler Error CS1625 - -Cannot yield in the body of a finally clause - -A yield statement is not allowed in the body of a finally clause. To avoid this error, move the yield statement out of the finally clause. - -The following sample generates CS1625: - -```csharp -// CS1625.cs -using System.Collections; - -class C : IEnumerable -{ - public IEnumerator GetEnumerator() - { - try - { - } - finally - { - yield return this; // CS1625 - } - } -} - -public class CMain -{ - public static void Main() { } -} -``` - -## Compiler Error CS1626 - -Cannot yield a value in the body of a try block with a catch clause - -A yield statement is not allowed in a try block if there is a catch clause associated with the try block. To avoid this error, either move the yield statement out of the try/catch/finally block, or remove the catch block. - -The following sample generates CS1626: - -```csharp -// CS1626.cs -using System.Collections; - -class C : IEnumerable -{ - public IEnumerator GetEnumerator() - { - try - { - yield return this; // CS1626 - } - catch - { - - } - finally - { - - } - } -} - -public class CMain -{ - public static void Main() { } -} -``` - -## Compiler Error CS1627 - -Expression expected after yield return - -This error occurs if `yield` is used without an expression. To avoid this error, insert the appropriate expression in the statement. - -The following sample generates CS1627: - -```csharp -// CS1627.cs -using System.Collections; - -class C : IEnumerable -{ - public IEnumerator GetEnumerator() - { - yield return; // CS1627 - // To resolve, add the following line: - // yield return 0; - } -} - -public class CMain -{ - public static void Main() { } -} -``` - -## Compiler Error CS1629 - -Unsafe code may not appear in iterators - -The C# language specification doesn't allow unsafe code in iterators. This restriction is relaxed in C# 13. You can use `unsafe` blocks, but the `yield return` statement can't be used in an `unsafe` block. - -The following sample generates CS1629: - -```csharp -// CS1629.cs -// compile with: /unsafe -using System.Collections.Generic; -class C -{ - IEnumerator IteratorMethod() - { - int i; - unsafe // CS1629 - { - int *p = &i; - yield return *p; - } - } -} -``` - -## Compiler Error CS1631 - -Cannot yield a value in the body of a catch clause - -The yield statement is not allowed from within the body of a catch clause. To avoid this error, move the yield statement outside the body of the catch clause. - -The following sample generates CS1631: - -```csharp -// CS1631.cs -using System; -using System.Collections; - -public class C : IEnumerable -{ - public IEnumerator GetEnumerator() - { - try - { - } - catch(Exception e) - { - yield return this; // CS1631 - } - } - - public static void Main() - { - } -} -``` - -## Compiler Error CS1637 - -Iterators cannot have unsafe parameters or yield types - -Check the argument list of the iterator and the type of any yield statements to verify that you are not using any unsafe types. - -The following sample generates CS1637: - -```csharp -// CS1637.cs -// compile with: /unsafe -using System.Collections; - -public unsafe class C -{ - public IEnumerator Iterator1(int* p) // CS1637 - { - yield return null; - } -} -``` - -## Compiler Error CS4013 - -Instance of type cannot be used inside a nested function, query expression, iterator block or async method - -Beginning with C# 13, `ref struct` types can be used in iterator methods, if they aren't accessed across `yield return` statement. - -The following sample generates CS4013: - -```csharp -public class C -{ - public static IEnumerable Lines(char[] text) - { - ReadOnlySpan chars = text; - var index = chars.IndexOf('\n'); - while (index > 0) - { - yield return chars[..index].ToString(); - chars = chars[(index + 1)..]; - index = chars.IndexOf('\n'); - } - - yield return chars.ToString(); - } -} -``` - -This enumerator method extracts lines of text from a character array. It naively tries to use `ReadOnlySpan` 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. - -To correct this error: - -`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` can't be stored in the heap, the CS4013 error is raised. To continue to use a `ReadOnlySpan`, to correct this error, the method must be reimplemented as a noniterator function, for example: - -```csharp - public static IEnumerable Lines2(char[] text) - { - ReadOnlySpan chars = text; - - var lines = new List(); - var index = chars.IndexOf('\n'); - while (index > 0) - { - lines.Add(chars[..index].ToString()); - chars = chars[(index+1)..]; - index = chars.IndexOf('\n'); - } - - lines.Add(chars.ToString()); - return lines; - } -``` - -To continue to use an iterator function, to correct this error, the method must be reimplemented to avoid using `ReadOnlySpan`, for example: - -```csharp - public static IEnumerable Lines2(char[] chars) - { - var startIndex = 0; - var index = Array.IndexOf(chars,'\n'); - while (index > 0) - { - yield return new string(chars, startIndex, index); - startIndex = index+1; - index = Array.IndexOf(chars, '\n', startIndex); - } - yield return new string(chars, startIndex, chars.Length - startIndex); - } -``` - -In C# 13, a `ReadOnlySpan` can be used, but can only be used in code segments without a `yield return`: - -```csharp -static IEnumerable Lines2(char[] text) -{ - ReadOnlySpan chars = text; - - var lines = new List(); - var index = chars.IndexOf('\n'); - while (index > 0) - { - lines.Add(chars[..index].ToString()); - chars = chars[(index + 1)..]; - index = chars.IndexOf('\n'); - } - - lines.Add(chars.ToString()); - foreach(var line in lines) - { - yield return line; - } -} -``` - -## Compiler Error CS8154 - -The body cannot be an iterator block because it returns by reference - -The following sample generates CS8154: - -```csharp -// CS8154.cs (12,17) - -class TestClass -{ - int x = 0; - ref int TestFunction() - { - if (true) - { - yield return x; - } - - ref int localFunction() - { - if (true) - { - yield return x; - } - } - - yield return localFunction(); - } -} -``` - -Iterator methods cannot return by reference, refactoring to return by value corrects this error: - -```csharp -class TestClass -{ - int x = 0; - IEnumerable TestFunction() - { - if (true) - { - yield return x; - } - - IEnumerable localFunction() - { - if (true) - { - yield return x; - } - } - - foreach (var item in localFunction()) - yield return item; - } -} -``` - -## Compiler Error CS8176 - -Iterators cannot have by-reference locals - -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. - -Beginning with C# 13, this restriction was removed. - -The following sample generates CS8176: - -```csharp -// CS8176.cs (7,26) - -using System.Collections.Generic; -class C -{ - IEnumerable M() - { - ref readonly int x = ref (new int[1])[0]; - int i = x; - yield return i; - } -} -``` - -Removing use of by-reference corrects this error: - -```csharp -class C -{ - IEnumerable M() - { - int x = (new int[1])[0]; - int i = x; - yield return i; - } -} -``` - -## New errors - +- [**CS1622**](#structure-of-an-iterator-method): *Cannot return a value from an iterator. Use the yield return statement to return a value, or yield break to end the iteration.* +- [**CS1624**](#structure-of-an-iterator-method): *The body of 'accessor' cannot be an iterator block because 'type' is not an iterator interface type* +- [**CS1625**](#restrictions-on-iterator-methods): *Cannot yield in the body of a finally clause* +- [**CS1626**](#restrictions-on-iterator-methods): *Cannot yield a value in the body of a try block with a catch clause* +- [**CS1627**](#structure-of-an-iterator-method): *Expression expected after yield return* +- [**CS1629**](#restrictions-on-iterator-methods): *Unsafe code may not appear in iterators* +- [**CS1631**](#restrictions-on-iterator-methods): *Cannot yield a value in the body of a catch clause* +- [**CS1637**](#structure-of-an-iterator-method): *Iterators cannot have unsafe parameters or yield types* +- [**CS4013**](#ref-safety-in-iterator-methods): *Instance of type cannot be used inside a nested function, query expression, iterator block or async method* +- [**CS8154**](#structure-of-an-iterator-method): *The body cannot be an iterator block because it returns by reference* +- [**CS8176**](#ref-safety-in-iterator-methods): *Iterators cannot have by-reference locals* +- [**CS9237**](#restrictions-on-iterator-methods): *'yield return' should not be used in the body of a lock statement* +- [**CS9238**](#restrictions-on-iterator-methods): *Cannot use 'yield return' in an 'unsafe' block* +- [**CS9239**](#restrictions-on-iterator-methods): *The `&` operator cannot be used on parameters or local variables in iterator methods.* + +## Structure of an iterator method + +An iterator method must conform to several rules in C#. The compiler issues the following errors when your iterator method violates one or more of those rules: + +- **CS1622**: *Cannot return a value from an iterator. Use the yield return statement to return a value, or yield break to end the iteration.* +- **CS1624**: *The body of 'accessor' cannot be an iterator block because 'type' is not an iterator interface type* +- **CS1627**: *Expression expected after yield return* +- **CS1637**: *Iterators cannot have unsafe parameters or yield types* +- **CS8154**: *The body cannot be an iterator block because it returns by reference* + +Your iterator method must follow the following rules: + +- An iterator method (using `yield return` and optionally `yield break`) can't also use a `return` statement to return a sequence. +- An iterator method must declare an *iterator interface type* as the return type. The *iterator interface types* are: , , , . +- A `yield return` statement must include an expression to return as part of a sequence. `yield return;` isn't valid. +- An iterator method can't use unsafe types as parameters, such as pointers. +- An iterator method can't `yield return` unsafe type, such as pointers. +- An iterator method can't `yield return` by `ref`. You must return by value. + +## Restrictions on iterator methods + +The body of an iterator method must conform to restrictions on the `yield return` statement and its context. The compiler issues the following errors when your iterator violates one of these restrictions: + +- **CS1625**: *Cannot yield in the body of a finally clause* +- **CS1626**: *Cannot yield a value in the body of a try block with a catch clause* +- **CS1631**: *Cannot yield a value in the body of a catch clause* +- **CS1629**: *Unsafe code may not appear in iterators* - **CS9237**: *''yield return' should not be used in the body of a lock statement* - **CS9238**: *Cannot use 'yield return' in an 'unsafe' block* - **CS9239**: *The `&` operator cannot be used on parameters or local variables in iterator methods.* These errors indicate that your code violates safety rules because an iterator returns an element and resumes to generate the next element: +- You can't `yield return` from a `catch` or `finally` clause. +- You can't `yield return` from a `try` block with a catch clause. - You can't `yield return` from inside a `lock` statement block. Doing so can cause deadlocks. - You can't `yield return` from an `unsafe` block. The context for an iterator creates a nested `safe` block within the enclosing `unsafe` block. - You can't use the `&` operator to take the address of a variable in an iterator method. -You must update your code to remove the constructs that aren't allowed. +Before C# 13, iterators can't contain `unsafe` code (CS1629). Beginning with C# 13, this restriction is relaxed. All `yield return` statements must be in a safe context, but an iterator method can contain `unsafe` code. + +## ref safety in iterator methods + +Iterator methods have special ref safety restrictions. These rules are relaxed in C# 13: + +- **CS4013**: *Instance of type cannot be used inside a nested function, query expression, iterator block or async method* +- **CS8176**: *Iterators cannot have by-reference locals* + +Before C# 13, iterators couldn't declare `ref` local variables. They could not declare any variables of a `ref struct` type. + +Beginning with C# 13, `ref struct` types can be used in iterator methods, if they aren't accessed across `yield return` statement. Beginning with C# 13, iterator methods can declare `ref` local variables. diff --git a/docs/csharp/whats-new/csharp-13.md b/docs/csharp/whats-new/csharp-13.md index c076bc3ebeccb..bdc7ad3933082 100644 --- a/docs/csharp/whats-new/csharp-13.md +++ b/docs/csharp/whats-new/csharp-13.md @@ -82,7 +82,7 @@ In C# 13, `async` methods can declare `ref` local variables, or local variables This relaxed restriction enables the compiler to allow verifiably safe use of `ref` local variables and `ref struct` types in more places. You can safely use types like in these methods. The compiler tells you if you violate safety rules. -In the same fashion, C# 13 allows `unsafe` contexts in iterator methods, provided no unsafe code appears in the same context as the `yield return` statements. +In the same fashion, C# 13 allows `unsafe` contexts in iterator methods. However, all `yield return` and `yield break` statements must be in safe contexts. ## See also From 948307f4b265921de2aa084ec5fb296a71838015 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Wed, 3 Jul 2024 16:00:00 -0400 Subject: [PATCH 5/5] build warnings --- .../language-reference/compiler-messages/iterator-yield.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/csharp/language-reference/compiler-messages/iterator-yield.md b/docs/csharp/language-reference/compiler-messages/iterator-yield.md index 873f6387f57a0..306a689a7f730 100644 --- a/docs/csharp/language-reference/compiler-messages/iterator-yield.md +++ b/docs/csharp/language-reference/compiler-messages/iterator-yield.md @@ -67,9 +67,9 @@ An iterator method must conform to several rules in C#. The compiler issues the Your iterator method must follow the following rules: -- An iterator method (using `yield return` and optionally `yield break`) can't also use a `return` statement to return a sequence. +- An iterator method (using `yield return` and optionally `yield break`) can't also use a `return` statement to return a sequence. - An iterator method must declare an *iterator interface type* as the return type. The *iterator interface types* are: , , , . -- A `yield return` statement must include an expression to return as part of a sequence. `yield return;` isn't valid. +- A `yield return` statement must include an expression to return as part of a sequence. `yield return;` isn't valid. - An iterator method can't use unsafe types as parameters, such as pointers. - An iterator method can't `yield return` unsafe type, such as pointers. - An iterator method can't `yield return` by `ref`. You must return by value.