From fe5871e87013c16181fa016400daed1cc5666abf Mon Sep 17 00:00:00 2001 From: Copilot <223556219+Copilot@users.noreply.github.com> Date: Sat, 1 Aug 2026 19:51:39 -0700 Subject: [PATCH 1/2] [release-notes] C# in .NET 11 Preview 7 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: a851ab96-8780-407d-97d0-50eed422ead5 --- release-notes/11.0/preview/preview7/csharp.md | 137 ++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 release-notes/11.0/preview/preview7/csharp.md diff --git a/release-notes/11.0/preview/preview7/csharp.md b/release-notes/11.0/preview/preview7/csharp.md new file mode 100644 index 0000000000..4769b500da --- /dev/null +++ b/release-notes/11.0/preview/preview7/csharp.md @@ -0,0 +1,137 @@ +# C# in .NET 11 Preview 7 - Release Notes + +.NET 11 Preview 7 includes the following C# language and compiler updates: + +- [Labeled `break` and `continue`](#labeled-break-and-continue) +- [Union patterns match the union or its value](#union-patterns-match-the-union-or-its-value) +- [Exhaustiveness for type parameters constrained to a closed type](#exhaustiveness-for-type-parameters-constrained-to-a-closed-type) +- [Unsafe Evolution: compat mode and `nameof`](#unsafe-evolution-compat-mode-and-nameof) +- [Null check in compiler-synthesized inline-array helpers](#null-check-in-compiler-synthesized-inline-array-helpers) +- [Clearer runtime-async unsupported-feature diagnostic](#clearer-runtime-async-unsupported-feature-diagnostic) + +C# updates: + +- [What's new in C# 15](https://learn.microsoft.com/dotnet/csharp/whats-new/csharp-15) + +## Labeled `break` and `continue` + +`break` and `continue` can now name an enclosing loop or `switch`, so you can exit or continue an outer construct directly from an inner one without threading state through a flag or resorting to `goto` ([dotnet/roslyn #84281](https://github.com/dotnet/roslyn/pull/84281)). The corresponding language proposal lives at [dotnet/csharplang labeled-break-continue](https://github.com/dotnet/csharplang/blob/main/proposals/labeled-break-continue.md). + +Prefix the target loop with a label, then reference it from `break` or `continue`: + +```csharp +string? foundValue = null; +outer: +for (int x = 0; x < xMax; x++) +{ + for (int y = 0; y < yMax; y++) + { + if (GetValue(x, y) is { } value && value == target) + { + foundValue = value; + break outer; + } + } +} +``` + +The same label can drive `continue`: + +```csharp +row: +for (int i = 0; i < rows; i++) +{ + for (int j = 0; j < cols; j++) + { + if (ShouldSkipRestOfRow(i, j)) continue row; + Process(i, j); + } +} +``` + +The label must be attached directly to a `for`, `foreach`, `while`, `do`, or `switch` statement, and the labeled `break`/`continue` must appear inside that statement. + +## Union patterns match the union or its value + +`union` types now use the **Try-Both** matching approach: when a pattern is applied to a union value, the compiler first tests the pattern against the union instance itself, and — if that test fails — against the union's contained `Value`. This aligns matching semantics with the [updated `unions` speclet](https://github.com/dotnet/csharplang/blob/main/proposals/unions.md#union-matching) and applies to type, `var`, declaration, list, and recursive patterns ([dotnet/roslyn #84323](https://github.com/dotnet/roslyn/pull/84323), [dotnet/roslyn #84365](https://github.com/dotnet/roslyn/pull/84365), [dotnet/roslyn #84418](https://github.com/dotnet/roslyn/pull/84418), [dotnet/roslyn #84531](https://github.com/dotnet/roslyn/pull/84531)). + +```csharp +public record class Dog(string Name); +public record class Cat(int Lives); + +public union Pet(Dog, Cat); + +Pet pet = new Cat(9); + +// Match against the union instance itself +if (pet is Pet) // true — the union type is a valid match target + Console.WriteLine("got a pet"); + +// Match against the contained value +if (pet is Cat { Lives: > 0 } cat) + Console.WriteLine($"cat has {cat.Lives} lives"); +``` + +Alongside this shift, type-checking during binding stops propagating union type information as soon as the output value is narrowed to a specific case type, aligning behavior with non-union scenarios ([dotnet/roslyn #84248](https://github.com/dotnet/roslyn/pull/84248)). Custom union declarations use a new `UnionMatchingMode` property to control how patterns are lowered, replacing earlier ad-hoc flags ([dotnet/roslyn #84436](https://github.com/dotnet/roslyn/pull/84436), [dotnet/roslyn #84499](https://github.com/dotnet/roslyn/pull/84499)). Unions remain a preview feature; enable `preview` to use them, and expect the surface to keep evolving. + +## Exhaustiveness for type parameters constrained to a closed type + +Switch-expression exhaustiveness now understands generic parameters constrained to a `closed` type ([dotnet/roslyn #83979](https://github.com/dotnet/roslyn/pull/83979), tracking [dotnet/roslyn #81039](https://github.com/dotnet/roslyn/issues/81039)). When every direct subtype of the closed base is handled, the compiler no longer warns that the switch is non-exhaustive — even when the input is typed as the type parameter rather than the base type itself. + +```csharp +public closed record class Shape; +public record class Circle(double Radius) : Shape; +public record class Square(double Side) : Shape; + +static double Area(T shape) where T : Shape => shape switch +{ + Circle(var r) => Math.PI * r * r, + Square(var s) => s * s +}; +``` + +The compiler decides `T` cannot introduce any additional case, because every derived type of `Shape` is already covered. The closed-hierarchies metadata format also stabilizes in this preview: `IsClosedTypeAttribute` now carries a `DerivedTypes` property, matching the format decided in [dotnet/runtime #129009](https://github.com/dotnet/runtime/issues/129009) ([dotnet/roslyn #84350](https://github.com/dotnet/roslyn/pull/84350)). + +## Unsafe Evolution: compat mode and `nameof` + +> This is a preview feature for .NET 11. + +Unsafe Evolution continues to refine the boundary between "code that mentions pointers" and "code that dereferences unmanaged memory". This preview delivers two follow-up rules from the [Unsafe Evolution speclet](https://github.com/dotnet/csharplang/blob/main/proposals/unsafe-evolution.md): + +- **Compat mode extends to legacy callers.** Members marked as *requires-unsafe* under the updated memory-safety rules now require an `unsafe` context even when they are called from code that hasn't opted into those rules ([dotnet/roslyn #83660](https://github.com/dotnet/roslyn/pull/83660), fixes [dotnet/roslyn #81967](https://github.com/dotnet/roslyn/issues/81967)). Without this, projects that only bumped `LangVersion` — but not their memory-safety rules version — could end up *less* protected than before by silently calling requires-unsafe members from safe contexts. +- **`nameof` no longer reports requires-unsafe errors.** Referencing a requires-unsafe member inside `nameof(...)` no longer reports an unsafe-context error, matching how `nameof` already handled most other member kinds ([dotnet/roslyn #84325](https://github.com/dotnet/roslyn/pull/84325)). + +Unsafe Evolution remains a preview feature; the exact rules and diagnostics can still change. Follow the [test plan](https://github.com/dotnet/roslyn/issues/81207) for status. + +## Null check in compiler-synthesized inline-array helpers + +Element access and `Span` conversion for an `[InlineArray]` value are lowered to calls into synthesized helpers in `` (`InlineArrayElementRef`, `InlineArrayElementRefReadOnly`, `InlineArrayAsSpan`, `InlineArrayAsReadOnlySpan`). Those helpers previously accepted any byref — including a null byref — and returned a ref or `Span` at a caller-controlled offset. Reading or writing through the result did not reliably fault; instead it produced arbitrary reads and writes, and this was reachable from safe code (for example, via `Unsafe.NullRef()`). + +The helpers now null-check the incoming buffer, so forming a ref or span from a null inline-array reference throws `NullReferenceException` deterministically ([dotnet/roslyn #84488](https://github.com/dotnet/roslyn/pull/84488), [dotnet/roslyn #84523](https://github.com/dotnet/roslyn/pull/84523), closes [dotnet/roslyn #84344](https://github.com/dotnet/roslyn/issues/84344)). In the common case where the JIT can prove the base is non-null, the check is elided. No source change is needed to pick up the fix — rebuild against the Preview 7 compiler. + +## Clearer runtime-async unsupported-feature diagnostic + +The runtime-async diagnostic that reports "this method uses a feature that runtime-async doesn't support yet" no longer implies that support will eventually arrive for `__arglist` ([dotnet/roslyn #84263](https://github.com/dotnet/roslyn/pull/84263)). `__arglist` cannot be lowered by the runtime-async transform, so the updated message states that plainly. The diagnostic is also removed from the build-only list, since the remaining reporting site runs during initial binding. + + + +## Community contributors + +Thank you contributors! ❤️ + +- [@corentingallet](https://github.com/dotnet/roslyn/pulls?q=is%3Apr+is%3Amerged+author%3Acorentingallet) +- [@DoctorKrolic](https://github.com/dotnet/roslyn/pulls?q=is%3Apr+is%3Amerged+author%3ADoctorKrolic) +- [@neoGeneva](https://github.com/dotnet/roslyn/pulls?q=is%3Apr+is%3Amerged+author%3AneoGeneva) +- [@seblyng](https://github.com/dotnet/roslyn/pulls?q=is%3Apr+is%3Amerged+author%3Aseblyng) +- [@teo-tsirpanis](https://github.com/dotnet/roslyn/pulls?q=is%3Apr+is%3Amerged+author%3Ateo-tsirpanis) + + From 8edfa402636f838ffba40950613bd113c413f204 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Wed, 5 Aug 2026 16:31:39 -0400 Subject: [PATCH 2/2] General edit pass Clean up some syntax and explanations. --- release-notes/11.0/preview/preview7/csharp.md | 39 ++++++------------- 1 file changed, 12 insertions(+), 27 deletions(-) diff --git a/release-notes/11.0/preview/preview7/csharp.md b/release-notes/11.0/preview/preview7/csharp.md index 4769b500da..69847b626d 100644 --- a/release-notes/11.0/preview/preview7/csharp.md +++ b/release-notes/11.0/preview/preview7/csharp.md @@ -15,14 +15,13 @@ C# updates: ## Labeled `break` and `continue` -`break` and `continue` can now name an enclosing loop or `switch`, so you can exit or continue an outer construct directly from an inner one without threading state through a flag or resorting to `goto` ([dotnet/roslyn #84281](https://github.com/dotnet/roslyn/pull/84281)). The corresponding language proposal lives at [dotnet/csharplang labeled-break-continue](https://github.com/dotnet/csharplang/blob/main/proposals/labeled-break-continue.md). +`break` and `continue` can now name an enclosing loop or `switch`, so you can exit or continue an outer construct directly from an inner one without threading state through a flag or resorting to `goto` ([dotnet/roslyn#84281](https://github.com/dotnet/roslyn/pull/84281)). The corresponding language proposal lives at [dotnet/csharplang labeled-break-continue](https://github.com/dotnet/csharplang/blob/main/proposals/labeled-break-continue.md). Prefix the target loop with a label, then reference it from `break` or `continue`: ```csharp string? foundValue = null; -outer: -for (int x = 0; x < xMax; x++) +outer: for (int x = 0; x < xMax; x++) { for (int y = 0; y < yMax; y++) { @@ -38,8 +37,7 @@ for (int x = 0; x < xMax; x++) The same label can drive `continue`: ```csharp -row: -for (int i = 0; i < rows; i++) +row: for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { @@ -53,7 +51,7 @@ The label must be attached directly to a `for`, `foreach`, `while`, `do`, or `sw ## Union patterns match the union or its value -`union` types now use the **Try-Both** matching approach: when a pattern is applied to a union value, the compiler first tests the pattern against the union instance itself, and — if that test fails — against the union's contained `Value`. This aligns matching semantics with the [updated `unions` speclet](https://github.com/dotnet/csharplang/blob/main/proposals/unions.md#union-matching) and applies to type, `var`, declaration, list, and recursive patterns ([dotnet/roslyn #84323](https://github.com/dotnet/roslyn/pull/84323), [dotnet/roslyn #84365](https://github.com/dotnet/roslyn/pull/84365), [dotnet/roslyn #84418](https://github.com/dotnet/roslyn/pull/84418), [dotnet/roslyn #84531](https://github.com/dotnet/roslyn/pull/84531)). +`union` types now use the **Try-Both** matching approach: when a pattern is applied to a union value, the compiler first tests the pattern against the union instance itself, and — if that test fails — against the union's contained `Value`. This aligns matching semantics with the [updated `unions` speclet](https://github.com/dotnet/csharplang/blob/main/proposals/unions.md#union-matching) and applies to type, `var`, declaration, list, and recursive patterns ([dotnet/roslyn#84323](https://github.com/dotnet/roslyn/pull/84323), [dotnet/roslyn#84365](https://github.com/dotnet/roslyn/pull/84365), [dotnet/roslyn#84418](https://github.com/dotnet/roslyn/pull/84418), [dotnet/roslyn#84531](https://github.com/dotnet/roslyn/pull/84531)). ```csharp public record class Dog(string Name); @@ -72,11 +70,11 @@ if (pet is Cat { Lives: > 0 } cat) Console.WriteLine($"cat has {cat.Lives} lives"); ``` -Alongside this shift, type-checking during binding stops propagating union type information as soon as the output value is narrowed to a specific case type, aligning behavior with non-union scenarios ([dotnet/roslyn #84248](https://github.com/dotnet/roslyn/pull/84248)). Custom union declarations use a new `UnionMatchingMode` property to control how patterns are lowered, replacing earlier ad-hoc flags ([dotnet/roslyn #84436](https://github.com/dotnet/roslyn/pull/84436), [dotnet/roslyn #84499](https://github.com/dotnet/roslyn/pull/84499)). Unions remain a preview feature; enable `preview` to use them, and expect the surface to keep evolving. +Alongside this shift, type-checking during binding stops propagating union type information as soon as the output value is narrowed to a specific case type, aligning behavior with non-union scenarios ([dotnet/roslyn#84248](https://github.com/dotnet/roslyn/pull/84248)). Custom union declarations use a new `UnionMatchingMode` property to control how patterns are lowered, replacing earlier ad-hoc flags ([dotnet/roslyn#84436](https://github.com/dotnet/roslyn/pull/84436), [dotnet/roslyn#84499](https://github.com/dotnet/roslyn/pull/84499)). Unions remain a preview feature; enable `preview` to use them, and expect the surface to keep evolving. ## Exhaustiveness for type parameters constrained to a closed type -Switch-expression exhaustiveness now understands generic parameters constrained to a `closed` type ([dotnet/roslyn #83979](https://github.com/dotnet/roslyn/pull/83979), tracking [dotnet/roslyn #81039](https://github.com/dotnet/roslyn/issues/81039)). When every direct subtype of the closed base is handled, the compiler no longer warns that the switch is non-exhaustive — even when the input is typed as the type parameter rather than the base type itself. +Switch-expression exhaustiveness now understands generic parameters constrained to a `closed` type ([dotnet/roslyn#83979](https://github.com/dotnet/roslyn/pull/83979), tracking [dotnet/roslyn#81039](https://github.com/dotnet/roslyn/issues/81039)). When every direct subtype of the closed base is handled, the compiler no longer warns that the switch is non-exhaustive — even when the input is typed as the type parameter rather than the base type itself. ```csharp public closed record class Shape; @@ -90,7 +88,7 @@ static double Area(T shape) where T : Shape => shape switch }; ``` -The compiler decides `T` cannot introduce any additional case, because every derived type of `Shape` is already covered. The closed-hierarchies metadata format also stabilizes in this preview: `IsClosedTypeAttribute` now carries a `DerivedTypes` property, matching the format decided in [dotnet/runtime #129009](https://github.com/dotnet/runtime/issues/129009) ([dotnet/roslyn #84350](https://github.com/dotnet/roslyn/pull/84350)). +The compiler decides `T` cannot introduce any additional case, because every derived type of `Shape` is already covered. The closed-hierarchies metadata format also stabilizes in this preview: `IsClosedTypeAttribute` now carries a `DerivedTypes` property, matching the format decided in [dotnet/runtime#129009](https://github.com/dotnet/runtime/issues/129009) ([dotnet/roslyn#84350](https://github.com/dotnet/roslyn/pull/84350)). ## Unsafe Evolution: compat mode and `nameof` @@ -98,31 +96,20 @@ The compiler decides `T` cannot introduce any additional case, because every der Unsafe Evolution continues to refine the boundary between "code that mentions pointers" and "code that dereferences unmanaged memory". This preview delivers two follow-up rules from the [Unsafe Evolution speclet](https://github.com/dotnet/csharplang/blob/main/proposals/unsafe-evolution.md): -- **Compat mode extends to legacy callers.** Members marked as *requires-unsafe* under the updated memory-safety rules now require an `unsafe` context even when they are called from code that hasn't opted into those rules ([dotnet/roslyn #83660](https://github.com/dotnet/roslyn/pull/83660), fixes [dotnet/roslyn #81967](https://github.com/dotnet/roslyn/issues/81967)). Without this, projects that only bumped `LangVersion` — but not their memory-safety rules version — could end up *less* protected than before by silently calling requires-unsafe members from safe contexts. -- **`nameof` no longer reports requires-unsafe errors.** Referencing a requires-unsafe member inside `nameof(...)` no longer reports an unsafe-context error, matching how `nameof` already handled most other member kinds ([dotnet/roslyn #84325](https://github.com/dotnet/roslyn/pull/84325)). +- **Compat mode extends to legacy callers.** Members marked as *requires-unsafe* under the updated memory-safety rules now require an `unsafe` context even when they are called from code that hasn't opted into those rules ([dotnet/roslyn#83660](https://github.com/dotnet/roslyn/pull/83660), fixes [dotnet/roslyn#81967](https://github.com/dotnet/roslyn/issues/81967)). Without this, projects that only bumped `LangVersion` — but not their memory-safety rules version — could end up *less* protected than before by silently calling requires-unsafe members from safe contexts. +- **`nameof` no longer reports requires-unsafe errors.** Referencing a requires-unsafe member inside `nameof(...)` no longer reports an unsafe-context error, matching how `nameof` already handled most other member kinds ([dotnet/roslyn#84325](https://github.com/dotnet/roslyn/pull/84325)). Unsafe Evolution remains a preview feature; the exact rules and diagnostics can still change. Follow the [test plan](https://github.com/dotnet/roslyn/issues/81207) for status. ## Null check in compiler-synthesized inline-array helpers -Element access and `Span` conversion for an `[InlineArray]` value are lowered to calls into synthesized helpers in `` (`InlineArrayElementRef`, `InlineArrayElementRefReadOnly`, `InlineArrayAsSpan`, `InlineArrayAsReadOnlySpan`). Those helpers previously accepted any byref — including a null byref — and returned a ref or `Span` at a caller-controlled offset. Reading or writing through the result did not reliably fault; instead it produced arbitrary reads and writes, and this was reachable from safe code (for example, via `Unsafe.NullRef()`). +Element access and `Span` conversion for an `[InlineArray]` value are lowered to calls into synthesized helpers in `` (`InlineArrayElementRef`, `InlineArrayElementRefReadOnly`, `InlineArrayAsSpan`, `InlineArrayAsReadOnlySpan`). Those helpers previously accepted any byref — including a null byref — and returned a `ref` or `Span` at a caller-controlled offset. Reading or writing through the result did not reliably fault; instead it produced arbitrary reads and writes, and this was reachable from safe code (for example, via `Unsafe.NullRef()`). -The helpers now null-check the incoming buffer, so forming a ref or span from a null inline-array reference throws `NullReferenceException` deterministically ([dotnet/roslyn #84488](https://github.com/dotnet/roslyn/pull/84488), [dotnet/roslyn #84523](https://github.com/dotnet/roslyn/pull/84523), closes [dotnet/roslyn #84344](https://github.com/dotnet/roslyn/issues/84344)). In the common case where the JIT can prove the base is non-null, the check is elided. No source change is needed to pick up the fix — rebuild against the Preview 7 compiler. +The helpers now null-check the incoming buffer, so forming a `ref` or span from a null inline-array reference throws `NullReferenceException` deterministically ([dotnet/roslyn#84488](https://github.com/dotnet/roslyn/pull/84488), [dotnet/roslyn#84523](https://github.com/dotnet/roslyn/pull/84523), closes [dotnet/roslyn#84344](https://github.com/dotnet/roslyn/issues/84344)). In the common case where the JIT can prove the base is non-null, the check is elided. No source change is needed to pick up the fix — rebuild against the Preview 7 compiler. ## Clearer runtime-async unsupported-feature diagnostic -The runtime-async diagnostic that reports "this method uses a feature that runtime-async doesn't support yet" no longer implies that support will eventually arrive for `__arglist` ([dotnet/roslyn #84263](https://github.com/dotnet/roslyn/pull/84263)). `__arglist` cannot be lowered by the runtime-async transform, so the updated message states that plainly. The diagnostic is also removed from the build-only list, since the remaining reporting site runs during initial binding. - - +The runtime-async diagnostic that reports "this method uses a feature that runtime-async doesn't support yet" no longer implies that support will eventually arrive for `__arglist` ([dotnet/roslyn#84263](https://github.com/dotnet/roslyn/pull/84263)). `__arglist` cannot be lowered by the runtime-async transform, so the updated message states that plainly. The diagnostic is also removed from the build-only list, since the remaining reporting site runs during initial binding. ## Community contributors @@ -133,5 +120,3 @@ Thank you contributors! ❤️ - [@neoGeneva](https://github.com/dotnet/roslyn/pulls?q=is%3Apr+is%3Amerged+author%3AneoGeneva) - [@seblyng](https://github.com/dotnet/roslyn/pulls?q=is%3Apr+is%3Amerged+author%3Aseblyng) - [@teo-tsirpanis](https://github.com/dotnet/roslyn/pulls?q=is%3Apr+is%3Amerged+author%3Ateo-tsirpanis) - -