From b2bf220f50536ef6a1b2414d85a116c785a235f0 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Mon, 2 Mar 2026 14:00:41 -0500 Subject: [PATCH 1/8] support list and slice patterns --- standard/patterns.md | 132 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) diff --git a/standard/patterns.md b/standard/patterns.md index 2fa664aaa..d097ca169 100644 --- a/standard/patterns.md +++ b/standard/patterns.md @@ -40,6 +40,8 @@ primary_pattern | discard_pattern | type_pattern | relational_pattern + | list_pattern + | slice_pattern ; parenthesized_pattern @@ -701,3 +703,133 @@ When a *pattern* appears on the right-hand-side of `is`, the extent of the patte > > > *end example* + + +### §list-pattern-new-clause List pattern + +A *list_pattern* matches a sequence of elements in a list or an array. + +```ANTLR +list_pattern + : list_pattern_clause simple_designation? + ; + +list_pattern_clause + : '[' (pattern (',' pattern)* ','?)? ']' + ; +``` + +A *list_pattern* is compatible with any type that is *countable* ([§14.7.1](classes.md#1471-general[rcj1.1])) as well as *indexable* ([§xxx](classes.md#xxx))[rcj2.1]—it has an accessible indexer that takes an `Index` as an argument, or an accessible indexer with a single `int` parameter. If both indexers are present, the former is preferred. (See ([§xxx](classes.md#xxx-implicit-index-support))[rcj3.1] for details of implicit index support.) + +A pattern of the form `expr is [1, 2, 3]` is equivalent to the following code: + +```csharp +expr.Length is 3 +&& expr[new Index(0, fromEnd: false)] is 1 +&& expr[new Index(1, fromEnd: false)] is 2 +&& expr[new Index(2, fromEnd: false)] is 3 +``` + +> *Example*: +> +> +> ```csharp +> int[] numbers = { 1, 2, 3 }; +> +> Console.WriteLine(numbers is [1, 2, 3]); // True +> Console.WriteLine(numbers is [1, 2, 4]); // False +> Console.WriteLine(numbers is [1, 2, 3, 4]); // False +> Console.WriteLine(numbers is [0 or 1, <= 2, >= 3 and not 7]); // True +> ``` +> +> *end example* + +The discard pattern ([§11.2.7](patterns.md#1127-discard-pattern)) matches any single element. + +> *Example*: +> +> +> ```csharp +> List numbers = new() { 1, 2, 3 }; +> +> if (numbers is [_, var second, _]) +> { +> Console.WriteLine($"The second element is {second}."); +> } +> ``` +> +> *end example* + +### §slice-pattern-new-clause Slice pattern + +A *slice_pattern* discards zero or more elements. It shall only be used directly in a *list_pattern_clause*, and then only once at most in that clause. + +```ANTLR +slice_pattern + : '..' pattern? + ; +``` + +A *slice_pattern* without a subpattern is compatible with any type that is compatible with a *list_pattern*. A *slice_pattern* with a subpattern is compatible with any type that is *countable* ([§14.7.1](classes.md#1471-general[rcj4.1])) as well as *sliceable* ([§xxx](classes.md#xxx))[rcj5.1]—it has an accessible indexer that takes a `Range` as an argument, or an accessible `Slice` method with two `int` parameters. If both are present, the former is preferred. (See ([§xxx](classes.md#xxx-implicit-index-support))[rcj6.1] for details of implicit index support.) + +A *slice_pattern* acts like a proper discard; that is, no tests shall be made for such pattern. Rather, it only affects other nodes, namely the length and indexer. For instance, a pattern of the form `expr is [1, .. var s, 3]` is equivalent to the following code (if compatible via explicit `Index` and `Range` support): + +```csharp +expr.Length is >= 2 +&& expr[new Index(0, fromEnd: false)] is 1 +&& expr[new Range(new Index(1, fromEnd: false), new Index(1, fromEnd: true))] is var s +&& expr[new Index(1, fromEnd: true)] is 3 +``` + +The input type for a *slice_pattern* is the return type of the underlying `this[Range]` or `Slice` method with two exceptions: For `string`s and arrays, `string.Substring` and `RuntimeHelpers.GetSubArray`, respectively, shall be used. + +> *Example*: A slice pattern can be used to match elements only at the start or/and the end of an input sequence. +> +> +> ```csharp +> Console.WriteLine(new[] { 1, 2, 3, 4, 5 } is [> 0, > 0, ..]); // True +> Console.WriteLine(new[] { 1, 1 } is [_, _, ..]); // True +> Console.WriteLine(new[] { 0, 1, 2, 3, 4 } is [> 0, > 0, ..]); // False +> Console.WriteLine(new[] { 1 } is [1, 2, ..]); // False +> +> Console.WriteLine(new[] { 1, 2, 3, 4 } is [.., > 0, > 0]); // True +> Console.WriteLine(new[] { 2, 4 } is [.., > 0, 2, 4]); // False +> Console.WriteLine(new[] { 2, 4 } is [.., 2, 4]); // True +> +> Console.WriteLine(new[] { 1, 2, 3, 4 } is [>= 0, .., 2 or 4]); // True +> Console.WriteLine(new[] { 1, 0, 0, 1 } is [1, 0, .., 0, 1]); // True +> Console.WriteLine(new[] { 1, 0, 1 } is [1, 0, .., 0, 1]); // False +> ``` +> +> *end example* + + + +> *Example*: A subpattern can be nested within a slice pattern: +> +> +> ```csharp +> MatchMessage("aBBA"); // output: Message aBBA matches; inner part is BB. +> MatchMessage("apron"); // output: Message apron doesn't match. +> +> void MatchMessage(string message) +> { +> var result = message is ['a' or 'A', .. var s, 'a' or 'A'] +> ? $"Message {message} matches; inner part is {s}." +> : $"Message {message} doesn't match."; +> Console.WriteLine(result); +> } +> +> Validate(new[] { -1, 0, 1 }); // output: not valid +> Validate(new[] { -1, 0, 0, 1 }); // output: valid +> +> void Validate(int[] numbers) +> { +> var result = numbers is [< 0, .. { Length: 2 or 4 }, > 0] +> ? "valid" : "not valid"; +> Console.WriteLine(result); +> } +> ``` +> +> *end example* + From ef36fd4a16922972fabbfeb51e3e5b1f71eafacd Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Mon, 2 Mar 2026 16:51:33 -0500 Subject: [PATCH 2/8] support list and slice patterns --- standard/portability-issues.md | 1 + 1 file changed, 1 insertion(+) diff --git a/standard/portability-issues.md b/standard/portability-issues.md index 52b21840a..7bc732364 100644 --- a/standard/portability-issues.md +++ b/standard/portability-issues.md @@ -10,6 +10,7 @@ This annex collects some information about portability that appears in this spec The behavior is undefined in the following circumstances: +1. If certain assumptions re pattern subsumption do not hold ([§11.3](patterns.md#113-pattern-assumptions)). 1. The behavior of the enclosing async function when an awaiter’s implementation of the interface methods `INotifyCompletion.OnCompleted` and `ICriticalNotifyCompletion.UnsafeOnCompleted` does not cause the resumption delegate to be invoked at most once ([§12.9.9.4](expressions.md#12994-run-time-evaluation-of-await-expressions)). 1. Passing pointers as `ref` or `out` parameters ([§24.3.2](unsafe-code.md#2432-data-pointers)). 1. When dereferencing the result of converting one pointer type to another and the resulting pointer is not correctly aligned for the pointed-to type. ([§24.5.1](unsafe-code.md#2451-general)). From 0f9ce4aad08edf069234b32e4f64dd3653d4112a Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Mon, 2 Mar 2026 17:18:25 -0500 Subject: [PATCH 3/8] support list and slice patterns --- standard/patterns.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/standard/patterns.md b/standard/patterns.md index d097ca169..0f9e3432e 100644 --- a/standard/patterns.md +++ b/standard/patterns.md @@ -719,7 +719,7 @@ list_pattern_clause ; ``` -A *list_pattern* is compatible with any type that is *countable* ([§14.7.1](classes.md#1471-general[rcj1.1])) as well as *indexable* ([§xxx](classes.md#xxx))[rcj2.1]—it has an accessible indexer that takes an `Index` as an argument, or an accessible indexer with a single `int` parameter. If both indexers are present, the former is preferred. (See ([§xxx](classes.md#xxx-implicit-index-support))[rcj3.1] for details of implicit index support.) +A *list_pattern* is compatible with any type that is *countable* ([§18.1](ranges.md#181-general)) as well as *indexable* ([§18.1](ranges.md#181-general))—it has an accessible indexer that takes an `Index` as an argument, or an accessible indexer with a single `int` parameter. If both indexers are present, the former is preferred. (See [§18.4.2](ranges.md#1842-implicit-index-support) for details of implicit index support.) A pattern of the form `expr is [1, 2, 3]` is equivalent to the following code: @@ -770,7 +770,7 @@ slice_pattern ; ``` -A *slice_pattern* without a subpattern is compatible with any type that is compatible with a *list_pattern*. A *slice_pattern* with a subpattern is compatible with any type that is *countable* ([§14.7.1](classes.md#1471-general[rcj4.1])) as well as *sliceable* ([§xxx](classes.md#xxx))[rcj5.1]—it has an accessible indexer that takes a `Range` as an argument, or an accessible `Slice` method with two `int` parameters. If both are present, the former is preferred. (See ([§xxx](classes.md#xxx-implicit-index-support))[rcj6.1] for details of implicit index support.) +A *slice_pattern* without a subpattern is compatible with any type that is compatible with a *list_pattern*. A *slice_pattern* with a subpattern is compatible with any type that is *countable* ([§18.1](ranges.md#181-general)) as well as *sliceable* ([§18.1](ranges.md#181-general))—it has an accessible indexer that takes a `Range` as an argument, or an accessible `Slice` method with two `int` parameters. If both are present, the former is preferred. (See [§18.4.2](ranges.md#1842-implicit-index-support) for details of implicit index support.) A *slice_pattern* acts like a proper discard; that is, no tests shall be made for such pattern. Rather, it only affects other nodes, namely the length and indexer. For instance, a pattern of the form `expr is [1, .. var s, 3]` is equivalent to the following code (if compatible via explicit `Index` and `Range` support): From bf19198228beca1b0a5a5289a09ce9df63f935fc Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Mon, 2 Mar 2026 17:34:06 -0500 Subject: [PATCH 4/8] fix link --- standard/portability-issues.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/portability-issues.md b/standard/portability-issues.md index 7bc732364..9c9720ad4 100644 --- a/standard/portability-issues.md +++ b/standard/portability-issues.md @@ -10,7 +10,7 @@ This annex collects some information about portability that appears in this spec The behavior is undefined in the following circumstances: -1. If certain assumptions re pattern subsumption do not hold ([§11.3](patterns.md#113-pattern-assumptions)). +1. If certain assumptions re pattern subsumption do not hold ([§11.3](patterns.md#113-pattern-subsumption)). 1. The behavior of the enclosing async function when an awaiter’s implementation of the interface methods `INotifyCompletion.OnCompleted` and `ICriticalNotifyCompletion.UnsafeOnCompleted` does not cause the resumption delegate to be invoked at most once ([§12.9.9.4](expressions.md#12994-run-time-evaluation-of-await-expressions)). 1. Passing pointers as `ref` or `out` parameters ([§24.3.2](unsafe-code.md#2432-data-pointers)). 1. When dereferencing the result of converting one pointer type to another and the resulting pointer is not correctly aligned for the pointed-to type. ([§24.5.1](unsafe-code.md#2451-general)). From 6b8c0bbe982c75c7da7015f8ecca4cafa7f3d813 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Mon, 4 May 2026 11:18:07 -0400 Subject: [PATCH 5/8] review and minor fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix four issues in patterns.md: Remove the stray [ at end of the subsumption "assumptions" block (line ~748: …doesn't hold.[). Remove the leading space in the SlicePattern2 example annotation: name:" SlicePattern2" → name:"SlicePattern2" (would otherwise break ExampleExtractor lookups). Collapse three double-spaces in the new sections: …1842-implicit-index-support) for details, A slice pattern can be used, expr is [1, .. var s, 3] is equivalent. Fix one issue in portability-issues.md: Reword "re pattern subsumption" → "regarding pattern subsumption" to match the rest of that file's prose. --- standard/portability-issues.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/portability-issues.md b/standard/portability-issues.md index 9c9720ad4..6241d429d 100644 --- a/standard/portability-issues.md +++ b/standard/portability-issues.md @@ -10,7 +10,7 @@ This annex collects some information about portability that appears in this spec The behavior is undefined in the following circumstances: -1. If certain assumptions re pattern subsumption do not hold ([§11.3](patterns.md#113-pattern-subsumption)). +1. If certain assumptions regarding pattern subsumption do not hold ([§11.3](patterns.md#113-pattern-subsumption)). 1. The behavior of the enclosing async function when an awaiter’s implementation of the interface methods `INotifyCompletion.OnCompleted` and `ICriticalNotifyCompletion.UnsafeOnCompleted` does not cause the resumption delegate to be invoked at most once ([§12.9.9.4](expressions.md#12994-run-time-evaluation-of-await-expressions)). 1. Passing pointers as `ref` or `out` parameters ([§24.3.2](unsafe-code.md#2432-data-pointers)). 1. When dereferencing the result of converting one pointer type to another and the resulting pointer is not correctly aligned for the pointed-to type. ([§24.5.1](unsafe-code.md#2451-general)). From 5047068693ac80b22d7ede66431bc211d9ab0cd5 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 18 Sep 2026 16:58:07 -0400 Subject: [PATCH 6/8] Remove retired pattern subsumption reference --- standard/portability-issues.md | 1 - 1 file changed, 1 deletion(-) diff --git a/standard/portability-issues.md b/standard/portability-issues.md index 6241d429d..52b21840a 100644 --- a/standard/portability-issues.md +++ b/standard/portability-issues.md @@ -10,7 +10,6 @@ This annex collects some information about portability that appears in this spec The behavior is undefined in the following circumstances: -1. If certain assumptions regarding pattern subsumption do not hold ([§11.3](patterns.md#113-pattern-subsumption)). 1. The behavior of the enclosing async function when an awaiter’s implementation of the interface methods `INotifyCompletion.OnCompleted` and `ICriticalNotifyCompletion.UnsafeOnCompleted` does not cause the resumption delegate to be invoked at most once ([§12.9.9.4](expressions.md#12994-run-time-evaluation-of-await-expressions)). 1. Passing pointers as `ref` or `out` parameters ([§24.3.2](unsafe-code.md#2432-data-pointers)). 1. When dereferencing the result of converting one pointer type to another and the resulting pointer is not correctly aligned for the pointed-to type. ([§24.5.1](unsafe-code.md#2451-general)). From d9ac7cdf96091889bd6a3df5366e6e44dcb9a435 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 18 Sep 2026 17:13:24 -0400 Subject: [PATCH 7/8] Remove extra list-pattern blank lines --- standard/patterns.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/standard/patterns.md b/standard/patterns.md index 0f9e3432e..595cc5bf3 100644 --- a/standard/patterns.md +++ b/standard/patterns.md @@ -704,7 +704,6 @@ When a *pattern* appears on the right-hand-side of `is`, the extent of the patte > > *end example* - ### §list-pattern-new-clause List pattern A *list_pattern* matches a sequence of elements in a list or an array. @@ -832,4 +831,3 @@ The input type for a *slice_pattern* is the return type of the underlying `this[ > ``` > > *end example* - From 54789d91bcd445b0bba94cb81fd5a36efed5fe75 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Mon, 21 Sep 2026 14:59:52 -0400 Subject: [PATCH 8/8] Fix slice pattern example project name Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: bce5e82a-89fd-4655-bc08-6d6ba97f0cc6 --- standard/patterns.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/patterns.md b/standard/patterns.md index 595cc5bf3..6cde6d292 100644 --- a/standard/patterns.md +++ b/standard/patterns.md @@ -806,7 +806,7 @@ The input type for a *slice_pattern* is the return type of the underlying `this[ > *Example*: A subpattern can be nested within a slice pattern: > -> +> > ```csharp > MatchMessage("aBBA"); // output: Message aBBA matches; inner part is BB. > MatchMessage("apron"); // output: Message apron doesn't match.