diff --git a/.github/workflows/dependencies/GrammarTestingEnv.tgz b/.github/workflows/dependencies/GrammarTestingEnv.tgz
index aa65ce27b..932ae59a9 100644
Binary files a/.github/workflows/dependencies/GrammarTestingEnv.tgz and b/.github/workflows/dependencies/GrammarTestingEnv.tgz differ
diff --git a/standard/README.md b/standard/README.md
index 453433a13..43ddec457 100644
--- a/standard/README.md
+++ b/standard/README.md
@@ -784,6 +784,10 @@
- [§23.8.3](unsafe-code.md#2383-fixed-size-buffers-in-expressions) Fixed-size buffers in expressions
- [§23.8.4](unsafe-code.md#2384-definite-assignment-checking) Definite assignment checking
- [§23.9](unsafe-code.md#239-stack-allocation) Stack allocation
+- [§24](ranges.md#24-ranges-and-slicing) Ranges and Slicing
+ - [§24.1](ranges.md#241-general) General
+ - [§24.2](ranges.md#242-the-index-type) The Index type
+ - [§24.3](ranges.md#243-the-range-type) The Range type
- [§A](grammar.md#annex-a-grammar) Grammar
- [§A.1](grammar.md#a1-general) General
- [§A.2](grammar.md#a2-lexical-grammar) Lexical grammar
diff --git a/standard/arrays.md b/standard/arrays.md
index ccfcfe278..1fda69889 100644
--- a/standard/arrays.md
+++ b/standard/arrays.md
@@ -112,7 +112,9 @@ Elements of arrays created by *array_creation_expression*s are always initialize
## 17.4 Array element access
-Array elements are accessed using *element_access* expressions ([§12.8.12.2](expressions.md#128122-array-access)) of the form `A[I₁, I₂, ..., Iₓ]`, where `A` is an expression of an array type and each `Iₑ` is an expression of type `int`, `uint`, `long`, `ulong`, or can be implicitly converted to one or more of these types. The result of an array element access is a variable, namely the array element selected by the indices.
+Array elements are accessed using the *array access* variant of *element_access* expressions ([§12.8.12.2](expressions.md#128122-array-access)) of the form `A[I₁, I₂, ..., Iₓ]`, where `A` is an expression of an array type and each `Iₑ` is an expression of type `int`, `uint`, `long`, `ulong`, or can be implicitly converted to one or more of these types. The result of an array access is a variable reference (§9.5) to the array element selected by the indices.
+
+Array elements of single-dimensional arrays can also be accessed using an array access expression where the sole index, `I₁`, is an expression of type `Index`, `Range`, or can be implicitly converted to one or both of these types. If `I₁` is of type `Index`, or has been implicitly converted to it, then the result of the array access is a variable reference to the array element selected by the index value. If `I₁` is of type `Range`, or has been implicitly converted to it, then the result of the element access is a new array formed from a shallow copy of the array elements with indices in the `Range`, maintaining the element order.
The elements of an array can be enumerated using a `foreach` statement ([§13.9.5](statements.md#1395-the-foreach-statement)).
diff --git a/standard/clauses.json b/standard/clauses.json
index acff96b61..740c816da 100644
--- a/standard/clauses.json
+++ b/standard/clauses.json
@@ -32,7 +32,8 @@
"attributes.md"
],
"UnsafeClauses": [
- "unsafe-code.md"
+ "unsafe-code.md",
+ "ranges.md"
],
"Annexes": [
"grammar.md",
diff --git a/standard/expressions.md b/standard/expressions.md
index 15e98f08e..6a62a93e5 100644
--- a/standard/expressions.md
+++ b/standard/expressions.md
@@ -148,7 +148,8 @@ The precedence of an operator is established by the definition of its associated
> | **Subclause** | **Category** | **Operators** |
> | ----------------- | ------------------------------- | -------------------------------------------------------|
> | [§12.8](expressions.md#128-primary-expressions) | Primary | `x.y` `x?.y` `f(x)` `a[x]` `a?[x]` `x++` `x--` `x!` `new` `typeof` `default` `checked` `unchecked` `delegate` `stackalloc` |
-> | [§12.9](expressions.md#129-unary-operators) | Unary | `+` `-` `!x` `~` `++x` `--x` `(T)x` `await x` |
+> | [§12.9](expressions.md#129-unary-operators) | Unary | `+` `-` `!x` `~` `^` `++x` `--x` `(T)x` `await x` |
+> | §range-operator | Range | `..` |
> | [§12.10](expressions.md#1210-arithmetic-operators) | Multiplicative | `*` `/` `%` |
> | [§12.10](expressions.md#1210-arithmetic-operators) | Additive | `+` `-` |
> | [§12.11](expressions.md#1211-shift-operators) | Shift | `<<` `>>` |
@@ -167,10 +168,12 @@ The precedence of an operator is established by the definition of its associated
When an operand occurs between two operators with the same precedence, the ***associativity*** of the operators controls the order in which the operations are performed:
-- Except for the assignment operators and the null coalescing operator, all binary operators are ***left-associative***, meaning that operations are performed from left to right.
+- Except for the assignment operators, the range operator, and the null coalescing operator, all binary operators are ***left-associative***, meaning that operations are performed from left to right.
> *Example*: `x + y + z` is evaluated as `(x + y) + z`. *end example*
- The assignment operators, the null coalescing operator and the conditional operator (`?:`) are ***right-associative***, meaning that operations are performed from right to left.
> *Example*: `x = y = z` is evaluated as `x = (y = z)`. *end example*
+- The range operator is ***non-associative***, meaning that neither the left nor right operand of a range operator may be a *range_expression*.
+ > *Example*: Both `x..y..z` and `x..(y..z)` are invalid as `..` is non-associative. *end example*
Precedence and associativity can be controlled using parentheses.
@@ -182,21 +185,17 @@ All unary and binary operators have predefined implementations. In addition, use
The ***overloadable unary operators*** are:
-`+ - !` (logical negation only) `~ ++ -- true false`
+> `+ - !` (logical negation only) `~ ++ -- true false`
-> *Note*: Although `true` and `false` are not used explicitly in expressions (and therefore are not included in the precedence table in [§12.4.2](expressions.md#1242-operator-precedence-and-associativity)), they are considered operators because they are invoked in several expression contexts: Boolean expressions ([§12.24](expressions.md#1224-boolean-expressions)) and expressions involving the conditional ([§12.18](expressions.md#1218-conditional-operator)) and conditional logical operators ([§12.14](expressions.md#1214-conditional-logical-operators)). *end note*
-
+Only the operators listed above can be overloaded. In particular, it is not possible to overload the null-forgiving operator (postfix `!`, [§12.8.9](expressions.md#1289-null-forgiving-expressions)) or the unary hat operator (prefix `^`, (§hat-operator)).
-
-> *Note*: The null-forgiving operator (postfix `!`, [§12.8.9](expressions.md#1289-null-forgiving-expressions)) is not an overloadable operator. *end note*
+> *Note*: Although `true` and `false` are not used explicitly in expressions (and therefore are not included in the precedence table in [§12.4.2](expressions.md#1242-operator-precedence-and-associativity)), they are considered operators because they are invoked in several expression contexts: Boolean expressions ([§12.24](expressions.md#1224-boolean-expressions)) and expressions involving the conditional ([§12.18](expressions.md#1218-conditional-operator)) and conditional logical operators ([§12.14](expressions.md#1214-conditional-logical-operators)). *end note*
The ***overloadable binary operators*** are:
-```csharp
-+ - * / % & | ^ << >> == != > < <= >=
-```
+> `+ - * / % & | ^ << >> == != > < <= >=`
-Only the operators listed above can be overloaded. In particular, it is not possible to overload member access, method invocation, or the `=`, `&&`, `||`, `??`, `?:`, `=>`, `checked`, `unchecked`, `new`, `typeof`, `default`, `as`, and `is` operators.
+Only the operators listed above can be overloaded. In particular, it is not possible to overload member access, method invocation, or the `..`, `=`, `&&`, `||`, `??`, `?:`, `=>`, `checked`, `unchecked`, `new`, `typeof`, `default`, `as`, and `is` operators.
When a binary operator is overloaded, the corresponding compound assignment operator, if any, is also implicitly overloaded.
@@ -348,8 +347,8 @@ In both of the above cases, a cast expression can be used to explicitly convert
***Lifted operators*** permit predefined and user-defined operators that operate on non-nullable value types to also be used with nullable forms of those types. Lifted operators are constructed from predefined and user-defined operators that meet certain requirements, as described in the following:
-- For the unary operators `+`, `++`, `-`, `--`, `!`(logical negation), and `~`, a lifted form of an operator exists if the operand and result types are both non-nullable value types. The lifted form is constructed by adding a single `?` modifier to the operand and result types. The lifted operator produces a `null` value if the operand is `null`. Otherwise, the lifted operator unwraps the operand, applies the underlying operator, and wraps the result.
-- For the binary operators `+`, `-`, `*`, `/`, `%`, `&`, `|`, `^`, `<<`, and `>>`, a lifted form of an operator exists if the operand and result types are all non-nullable value types. The lifted form is constructed by adding a single `?` modifier to each operand and result type. The lifted operator produces a `null` value if one or both operands are `null` (an exception being the `&` and `|` operators of the `bool?` type, as described in [§12.13.5](expressions.md#12135-nullable-boolean--and--operators)). Otherwise, the lifted operator unwraps the operands, applies the underlying operator, and wraps the result.
+- For the unary operators `+`, `++`, `-`, `--`, `!` (logical negation), `^`, and `~`, a lifted form of an operator exists if the operand and result types are both non-nullable value types. The lifted form is constructed by adding a single `?` modifier to the operand and result types. The lifted operator produces a `null` value if the operand is `null`. Otherwise, the lifted operator unwraps the operand, applies the underlying operator, and wraps the result.
+- For the binary operators `+`, `-`, `*`, `/`, `%`, `&`, `|`, `^`, `..`, `<<`, and `>>`, a lifted form of an operator exists if the operand and result types are all non-nullable value types. The lifted form is constructed by adding a single `?` modifier to each operand and result type. The lifted operator produces a `null` value if one or both operands are `null` (an exception being the `&` and `|` operators of the `bool?` type, as described in [§12.13.5](expressions.md#12135-nullable-boolean--and--operators)). Otherwise, the lifted operator unwraps the operands, applies the underlying operator, and wraps the result.
- For the equality operators `==` and `!=`, a lifted form of an operator exists if the operand types are both non-nullable value types and if the result type is `bool`. The lifted form is constructed by adding a single `?` modifier to each operand type. The lifted operator considers two `null` values equal, and a `null` value unequal to any non-`null` value. If both operands are non-`null`, the lifted operator unwraps the operands and applies the underlying operator to produce the `bool` result.
- For the relational operators `<`, `>`, `<=`, and `>=`, a lifted form of an operator exists if the operand types are both non-nullable value types and if the result type is `bool`. The lifted form is constructed by adding a single `?` modifier to each operand type. The lifted operator produces the value `false` if one or both operands are `null`. Otherwise, the lifted operator unwraps the operands and applies the underlying operator to produce the `bool` result.
@@ -1657,7 +1656,7 @@ The *qualified_alias_member* production is defined in [§14.8](namespaces.md#148
A *member_access* is either of the form `E.I` or of the form `E.I`, where `E` is a *primary_expression*, *predefined_type* or *qualified_alias_member,* `I` is a single identifier, and `` is an optional *type_argument_list*. When no *type_argument_list* is specified, consider `e` to be zero.
-A *member_access* with a *primary_expression* of type `dynamic` is dynamically bound ([§12.3.3](expressions.md#1233-dynamic-binding)). In this case, the compiler classifies the member access as a property access of type `dynamic`. The rules below to determine the meaning of the *member_access* are then applied at run-time, using the run-time type instead of the compile-time type of the *primary_expression*. If this run-time classification leads to a method group, then the member access shall be the *primary_expression* of an *invocation_expression*.
+A *member_access* with a *primary_expression* of type `dynamic` is dynamically bound ([§12.3.3](expressions.md#1233-dynamic-binding)). In this case, the member access is classified as a property access of type `dynamic`. The rules below to determine the meaning of the *member_access* are then applied at run-time, using the run-time type instead of the compile-time type of the *primary_expression*. If this run-time classification leads to a method group, then the member access shall be the *primary_expression* of an *invocation_expression*.
The *member_access* is evaluated and classified as follows:
@@ -1733,7 +1732,7 @@ In a member access of the form `E.I`, if `E` is a single identifier, and if the
A *null_conditional_member_access* is a conditional version of *member_access* ([§12.8.7](expressions.md#1287-member-access)) and it is a binding time error if the result type is `void`. For a null conditional expression where the result type may be `void` see ([§12.8.11](expressions.md#12811-null-conditional-invocation-expression)).
-A *null_conditional_member_access* consists of a *primary_expression* followed by the two tokens “`?`” and “`.`”, followed by an *identifier* with an optional *type_argument_list*, followed by zero or more *dependent_access*es any of which can be preceeded by a *null_forgiving_operator*.
+A *null_conditional_member_access* consists of a *primary_expression* followed by the two tokens “`?`” and “`.`”, followed by an *identifier* with an optional *type_argument_list*, followed by zero or more *dependent_access*es any of which can be preceded by a *null_forgiving_operator*.
```ANTLR
null_conditional_member_access
@@ -1823,7 +1822,7 @@ null_forgiving_operator
;
```
-*Note*: The postfix null-forgiving and prefix logical negation operators ([§12.9.4](expressions.md#1294-logical-negation-operator)), while represented by the same lexical token (`!`), are distinct. Only the latter may be overriden ([§15.10](classes.md#1510-operators)), the definition of the null-forgiving operator is fixed. *end note*
+*Note*: The postfix null-forgiving and prefix logical negation operators ([§12.9.4](expressions.md#1294-logical-negation-operator)), while represented by the same lexical token (`!`), are distinct. Only the latter may be overridden ([§15.10](classes.md#1510-operators)), the definition of the null-forgiving operator is fixed. *end note*
It is a compile-time error to apply the null-forgiving operator more than once to the same expression, intervening parentheses notwithstanding.
@@ -1952,7 +1951,7 @@ An *invocation_expression* is dynamically bound ([§12.3.3](expressions.md#1233-
- The *primary_expression* has compile-time type `dynamic`.
- At least one argument of the optional *argument_list* has compile-time type `dynamic`.
-In this case, the compiler classifies the *invocation_expression* as a value of type `dynamic`. The rules below to determine the meaning of the *invocation_expression* are then applied at run-time, using the run-time type instead of the compile-time type of those of the *primary_expression* and arguments that have the compile-time type `dynamic`. If the *primary_expression* does not have compile-time type `dynamic`, then the method invocation undergoes a limited compile-time check as described in [§12.6.5](expressions.md#1265-compile-time-checking-of-dynamic-member-invocation).
+In this case, the *invocation_expression* is classified as a value of type `dynamic`. The rules below to determine the meaning of the *invocation_expression* are then applied at run-time, using the run-time type instead of the compile-time type of those of the *primary_expression* and arguments that have the compile-time type `dynamic`. If the *primary_expression* does not have compile-time type `dynamic`, then the method invocation undergoes a limited compile-time check as described in [§12.6.5](expressions.md#1265-compile-time-checking-of-dynamic-member-invocation).
The *primary_expression* of an *invocation_expression* shall be a method group or a value of a *delegate_type*. If the *primary_expression* is a method group, the *invocation_expression* is a method invocation ([§12.8.10.2](expressions.md#128102-method-invocations)). If the *primary_expression* is a value of a *delegate_type*, the *invocation_expression* is a delegate invocation ([§12.8.10.4](expressions.md#128104-delegate-invocations)). If the *primary_expression* is neither a method group nor a value of a *delegate_type*, a binding-time error occurs.
@@ -2118,7 +2117,7 @@ The preceding rules mean that instance methods take precedence over extension me
> C.H(3)
> ```
>
-> `D.G` takes precendece over `C.G`, and `E.F` takes precedence over both `D.F` and `C.F`.
+> `D.G` takes precedence over `C.G`, and `E.F` takes precedence over both `D.F` and `C.F`.
>
> *end example*
@@ -2157,7 +2156,7 @@ in terms of what is included the text will probably be fine but this will need t
The optional *null_forgiving_operator* may be included if and only if the *null_conditional_member_access* or
*null_conditional_element_access* has a *delegate_type*.
-A *null_conditional_invocation_expression* expression `E` is of the form `P?A`; where `A` is the remainder of the syntactically equivalent *null_conditional_member_access* or *null_conditional_element_access*, `A` will therefore start with `.` or `[`. Let `PA` signify the concatention of `P` and `A`.
+A *null_conditional_invocation_expression* expression `E` is of the form `P?A`; where `A` is the remainder of the syntactically equivalent *null_conditional_member_access* or *null_conditional_element_access*, `A` will therefore start with `.` or `[`. Let `PA` signify the concatenation of `P` and `A`.
When `E` occurs as a *statement_expression* the meaning of `E` is the same as the meaning of the *statement*:
@@ -2198,8 +2197,6 @@ element_access
When recognising a *primary_expression* if both the *element_access* and *pointer_element_access* ([§23.6.4](unsafe-code.md#2364-pointer-element-access)) alternatives are applicable then the latter shall be chosen if the embedded *primary_expression* is of pointer type ([§23.3](unsafe-code.md#233-pointer-types)).
-The *argument_list* of an *element_access* shall not contain `out` or `ref` arguments.
-
The *primary_expression* of an *element_access* shall not be an *array_creation_expression* unless it includes an *array_initializer*, or a *stackalloc_expression* unless it includes a *stackalloc_initializer*.
> *Note*: This restriction exists to disallow potentially confusing code such as:
@@ -2221,29 +2218,94 @@ The *primary_expression* of an *element_access* shall not be an *array_creation_
An *element_access* is dynamically bound ([§12.3.3](expressions.md#1233-dynamic-binding)) if at least one of the following holds:
- The *primary_expression* has compile-time type `dynamic`.
-- At least one expression of the *argument_list* has compile-time type `dynamic` and the *primary_expression* does not have an array type.
+- At least one expression of the *argument_list* has compile-time type `dynamic`.
-In this case, the compiler classifies the *element_access* as a value of type `dynamic`. The rules below to determine the meaning of the *element_access* are then applied at run-time, using the run-time type instead of the compile-time type of those of the *primary_expression* and *argument_list* expressions which have the compile-time type `dynamic`. If the *primary_expression* does not have compile-time type `dynamic`, then the element access undergoes a limited compile-time check as described in [§12.6.5](expressions.md#1265-compile-time-checking-of-dynamic-member-invocation).
+In this case the compile-time type of the *element_access* depends on the compile-time type of its *primary_expression*: if it has an array type then the compile-time type is the element type of that array type; otherwise the compile-time type is `dynamic` and the *element_access* is classified as a value of type `dynamic`. The rules below to determine the meaning of the *element_access* are then applied at run-time, using the run-time type instead of the compile-time type of those of the *primary_expression* and *argument_list* expressions which have the compile-time type `dynamic`. If the *primary_expression* does not have compile-time type `dynamic`, then the element access undergoes a limited compile-time check as described in [§12.6.5](expressions.md#1265-compile-time-checking-of-dynamic-member-invocation).
-If the *primary_expression* of an *element_access* is a value of an *array_type*, the *element_access* is an array access ([§12.8.12.2](expressions.md#128122-array-access)). Otherwise, the *primary_expression* shall be a variable or value of a class, struct, or interface type that has one or more indexer members, in which case the *element_access* is an indexer access ([§12.8.12.3](expressions.md#128123-indexer-access)).
+> *Example*:
+>
+> ```csharp
+> var index = (dynamic)1; // index has compile-time type dynamic
+> int[] a = {0, 1, 2};
+> var a_elem = a[index]; // dynamically bound, a_elem has compile-time type int
+> string s = "012";
+> var s_elem = s[index]; // dynamcially bound, s_elem has compile-time type dynamic
+> ```
+>
+> *end example*
+
+If the *primary_expression* of an *element_access* is:
+
+- a value of *array_type*, the *element_access* is an array access ([§12.8.12.2](expressions.md#128122-array-access));
+- a value of `string` type, the *element_access* is a string access (§string-access);
+- otherwise, the *primary_expression* shall be a variable or value of a class, struct, or interface type that has one or more indexer members, in which case the *element_access* is an indexer access ([§12.8.12.3](expressions.md#128123-indexer-access)).
#### 12.8.12.2 Array access
-For an array access, the *primary_expression* of the *element_access* shall be a value of an *array_type*. Furthermore, the *argument_list* of an array access shall not contain named arguments. The number of expressions in the *argument_list* shall be the same as the rank of the *array_type*, and each expression shall be of type `int`, `uint`, `long`, or `ulong,` or shall be implicitly convertible to one or more of these types.
+For an array access the *argument_list* shall not contain named arguments or by-reference arguments (§15.6.2.3).
+
+The number of expressions in the *argument_list* shall be the same as the rank of the *array_type*, and each expression shall be:
+
+- of type `int`, `uint`, `long`, or `ulong`; or
+- for single rank array access only, of type `Index` or `Range`; or
+- be implicitly convertible to one or more of the above types.
-The result of evaluating an array access is a variable of the element type of the array, namely the array element selected by the values of the expressions in the *argument_list*.
+The run-time processing of an array access of the form `P[A]`, where `P` is a *primary_expression* of an *array_type* and `A` is an *argument_list* of index expressions, consists of the following steps:
-The run-time processing of an array access of the form `P[A]`, where `P` is a *primary_expression* of an *array_type* and `A` is an *argument_list*, consists of the following steps:
+- `P` is evaluated. If this evaluation causes an exception, no further steps are executed.
+- For each index expression in the *argument_list* in order, from left to right:
+ - The index expression is evaluated, let the type of the resultant value be *T*;
+ - This value is then converted to the first of the types: `int`, `uint`, `long`, `ulong`, or for single rank array access only, `Index` or `Range`; for which an implicit conversion ([§10.2](conversions.md#102-implicit-conversions)) from *T* exists.
+ - If evaluation of an index expression or the subsequent implicit conversion causes an exception, then no further index expressions are evaluated and no further steps are executed.
+- The value of `P` is checked to be valid. If the value of `P` is `null`, a `System.NullReferenceException` is thrown and no further steps are executed.
+- If the preceding steps have produced a single index value of type `Range` then:
+ - Let *L* be the length of the array referenced by `P`.
+ - `A` is checked to be valid with respect to *L* (§24.3), if it is not then a `System.ArgumentOutOfRangeException` is thrown and no further steps are executed.
+ - The starting offset, *S*, and number of items, *N*, for `A` with respect to *L* are determined as described for `GetOffsetAndLength` (§24.3).
+ - A new array is created from a shallow copy of the *N* elements of `P` starting at index *S*, if *N* is zero the new array has zero elements. This array becomes the result of the array access.
+
+> > > *Note:* Both *S* and *N* may be zero ($24.3). Indexing an empty array is usually invalid, however indexing with an empty range starting at zero is valid and returns an empty array. The defintion also allows *S* to be *L*, the past-end index (§24.1), in which case *N* will be zero and an empty array returned. *end note*
+
+
+
+> > > *Note:* A range of elements of an array cannot be assigned to using an array access. This differs from indexer accesses (§12.8.12.3) which may, but need not, support assignment to a range of indices specified by a `Range` value. *end note*
+
+- Otherwise:
+ - The result of evaluating the array access is a variable reference (§9.5) of the element type of the array.
+ - The value of each expression in the *argument_list* is checked against the actual bounds of each dimension of the array instance referenced by `P`. If one or more values are out of range, a `System.IndexOutOfRangeException` is thrown and no further steps are executed.
+ - The variable reference of the array element given by the index expressions is computed, and this becomes the result of the array access.
+
+#### §string-access String access
+
+For a string access the *argument_list* of the *element_access* shall contain a single unnamed value argument (§15.6.2.2) which shall be:
+
+- of type `int`, `Index` or `Range`; or
+- implicitly convertible to one or more of the above types.
+
+The run-time processing of a string access of the form `P[A]`, where `P` is a *primary_expression* of `string` type and `A` is a single expression, consists of the following steps:
- `P` is evaluated. If this evaluation causes an exception, no further steps are executed.
-- The index expressions of the *argument_list* are evaluated in order, from left to right. Following evaluation of each index expression, an implicit conversion ([§10.2](conversions.md#102-implicit-conversions)) to one of the following types is performed: `int`, `uint`, `long`, `ulong`. The first type in this list for which an implicit conversion exists is chosen. For instance, if the index expression is of type `short` then an implicit conversion to `int` is performed, since implicit conversions from `short` to `int` and from `short` to `long` are possible. If evaluation of an index expression or the subsequent implicit conversion causes an exception, then no further index expressions are evaluated and no further steps are executed.
+- The index expression is evaluated, let the type of the resultant value be *T*;
+- This value is then converted to the first of the types: `int`, `Index` or `Range`; for which an implicit conversion ([§10.2](conversions.md#102-implicit-conversions)) from *T* exists.
+- If evaluation of an index expression or the subsequent implicit conversion causes an exception, then no further index expressions are evaluated and no further steps are executed.
- The value of `P` is checked to be valid. If the value of `P` is `null`, a `System.NullReferenceException` is thrown and no further steps are executed.
-- The value of each expression in the *argument_list* is checked against the actual bounds of each dimension of the array instance referenced by `P`. If one or more values are out of range, a `System.IndexOutOfRangeException` is thrown and no further steps are executed.
-- The location of the array element given by the index expressions is computed, and this location becomes the result of the array access.
+- If the preceding steps have produced an index value of type `Range` then:
+ - The result of evaluating the string access is a value of `string` type.
+ - Let *L* be the length of the string referenced by `P`.
+ - `A` is checked to be valid with respect to *L* (§24.3), if it is not then a `System.ArgumentOutOfRangeException` is thrown and no further steps are executed.
+ - The starting offset, *S*, and number of items, *N*, for `A` with respect to *L* are determined as described for `GetOffsetAndLength` (§24.3).
+ - The result of the string access is a new string formed by copying the *N* characters of `P` starting from *S*, if *N* is zero the new string is empty.
+
+> > > *Note:* Both *S* and *N* may be zero (§24.3). Indexing an empty string is usually invalid, however indexing with an empty range starting at zero is valid and returns an empty string. The defintion also allows *S* to be *L*, the past-end index (§24.1), in which case *N* will be zero and an empty string returned. *end note*
+
+- Otherwise:
+ - The result of evaluating the string access is a value of `char` type.
+ - The value of the converted index expression is checked against the actual bounds of the string instance referenced by `P`. If the value is out of range, a `System.IndexOutOfRangeException` is thrown and no further steps are executed.
+ - The value of character at the offset of the converted index expression with the string `P` becomes the result of the string access.
#### 12.8.12.3 Indexer access
-For an indexer access, the *primary_expression* of the *element_access* shall be a variable or value of a class, struct, or interface type, and this type shall implement one or more indexers that are applicable with respect to the *argument_list* of the *element_access*.
+For an indexer access, the *primary_expression* of the *element_access* shall be a variable or value of a class, struct, or interface type, and this type shall implement one or more indexers that are applicable with respect to the *argument_list* of the *element_access*. The *argument_list* shall not cannot contain `out` or `ref` arguments.
The binding-time processing of an indexer access of the form `P[A]`, where `P` is a *primary_expression* of a class, struct, or interface type `T`, and `A` is an *argument_list*, consists of the following steps:
@@ -2254,9 +2316,17 @@ The binding-time processing of an indexer access of the form `P[A]`, where `P` i
- If `I` is applicable with respect to `A` ([§12.6.4.2](expressions.md#12642-applicable-function-member)) and `S` is a class type other than `object`, all indexers declared in an interface are removed from the set.
- If the resulting set of candidate indexers is empty, then no applicable indexers exist, and a binding-time error occurs.
- The best indexer of the set of candidate indexers is identified using the overload resolution rules of [§12.6.4](expressions.md#1264-overload-resolution). If a single best indexer cannot be identified, the indexer access is ambiguous, and a binding-time error occurs.
-- The index expressions of the *argument_list* are evaluated in order, from left to right. The result of processing the indexer access is an expression classified as an indexer access. The indexer access expression references the indexer determined in the step above, and has an associated instance expression of `P` and an associated argument list of `A`, and an associated type that is the type of the indexer. If `T` is a class type, the associated type is picked from the first declaration or override of the indexer found when starting with `T` and searching through its base classes.
+- The accessors of the best indexer are checked:
+ - If the indexer access is the target of an assignment then the indexer shall have a set or ref get accessor, if not a binding-time error occurs;
+ - Otherwise the indexer shall have a get or ref get accessor, otherwise a binding-time error occurs.
-Depending on the context in which it is used, an indexer access causes invocation of either the get accessor or the set accessor of the indexer. If the indexer access is the target of an assignment, the set accessor is invoked to assign a new value ([§12.21.2](expressions.md#12212-simple-assignment)). In all other cases, the get accessor is invoked to obtain the current value ([§12.2.2](expressions.md#1222-values-of-expressions)).
+The runtime processing of the indexer access consists of the following steps:
+
+- The target *primary_expression* `P` is evaluated.
+- The index expressions of the *argument_list* `A` are evaluated in order, from left to right.
+- Using the best indexer determined at binding-time:
+ - If the indexer access is the target of an assignment, the set accessor or ref get accessor is invoked to assign a new value ([§12.21.2](expressions.md#12212-simple-assignment)).
+ - In all other cases, the get accessor or ref get accessor is invoked to obtain the current value ([§12.2.2](expressions.md#1222-values-of-expressions)).
### 12.8.13 Null Conditional Element Access
@@ -2518,6 +2588,8 @@ An object initializer consists of a sequence of member initializers, enclosed by
Each *initializer_target* is followed by an equals sign and either an expression, an object initializer or a collection initializer. It is not possible for expressions within the object initializer to refer to the newly created object it is initializing.
+In the *argument_list* of an *initializer_target* there is no implicit support for arguments of type `Index` (§24.4.2) or `Range` (§24.4.3).
+
A member initializer that specifies an expression after the equals sign is processed in the same way as an assignment ([§12.21.2](expressions.md#12212-simple-assignment)) to the target.
A member initializer that specifies an object initializer after the equals sign is a ***nested object initializer***, i.e., an initialization of an embedded object. Instead of assigning a new value to the field or property, the assignments in the nested object initializer are treated as assignments to members of the field or property. Nested object initializers cannot be applied to properties with a value type, or to read-only fields with a value type.
@@ -3462,7 +3534,7 @@ An *anonymous_method_expression* is one of two ways of defining an anonymous fun
### 12.9.1 General
-The `+`, `-`, `!` (logical negation [§12.9.4](expressions.md#1294-logical-negation-operator) only), `~`, `++`, `--`, cast, and `await` operators are called the unary operators.
+The `+`, `-`, `!` (logical negation [§12.9.4](expressions.md#1294-logical-negation-operator) only), `~`, `^`, `++`, `--`, cast, and `await` operators are called the unary operators.
> *Note*: The postfix null-forgiving operator ([§12.8.9](expressions.md#1289-null-forgiving-expressions)), `!`, due to its compile-time and non-overloadable only nature, is excluded from the above list. *end note*
@@ -3473,6 +3545,7 @@ unary_expression
| '-' unary_expression
| logical_negation_operator unary_expression
| '~' unary_expression
+ | '^' unary_expression
| pre_increment_expression
| pre_decrement_expression
| cast_expression
@@ -3484,7 +3557,12 @@ unary_expression
*pointer_indirection_expression* ([§23.6.2](unsafe-code.md#2362-pointer-indirection)) and *addressof_expression* ([§23.6.5](unsafe-code.md#2365-the-address-of-operator)) are available only in unsafe code ([§23](unsafe-code.md#23-unsafe-code)).
-If the operand of a *unary_expression* has the compile-time type `dynamic`, it is dynamically bound ([§12.3.3](expressions.md#1233-dynamic-binding)). In this case, the compile-time type of the *unary_expression* is `dynamic`, and the resolution described below will take place at run-time using the run-time type of the operand.
+If the operand of a *unary_expression* has the compile-time type `dynamic`, it is dynamically bound ([§12.3.3](expressions.md#1233-dynamic-binding)). In this case:
+
+- the compile-time type of the *unary_expression* is:
+ - `Index` for the `^` hat operator (§hat-operator)
+ - `dynamic` for all other unary operators; and
+- the resolution described below will take place at run-time using the run-time type of the operand.
### 12.9.2 Unary plus operator
@@ -3575,6 +3653,24 @@ The result of evaluating `~x`, where `X` is an expression of an enumeration ty
Lifted ([§12.4.8](expressions.md#1248-lifted-operators)) forms of the unlifted predefined bitwise complement operators defined above are also predefined.
+### §hat-operator Hat operator
+
+The unary `^` operator is called the *hat* operator. The hat operator is not overloadable (§12.4.3) and there is a single predefined hat operator:
+
+```csharp
+Index operator ^(int x);
+```
+
+The result of an operation of the form `^x` is a from-end `Index` (§24.2) value equivalent to the result of the expression:
+
+```csharp
+new Index(x, true)
+```
+
+As with the other *unary_expression*s the operand may have a compile-time type of `dynamic` (§12.9.1) and be dynamically bound (§12.3.3). The compile-time type of the result is always `Index`.
+
+A lifted ([§12.4.8](expressions.md#1248-lifted-operators)) form of the hat operator is also predefined.
+
### 12.9.6 Prefix increment and decrement operators
```ANTLR
@@ -3706,6 +3802,42 @@ At run-time, the expression `await t` is evaluated as follows:
An awaiter’s implementation of the interface methods `INotifyCompletion.OnCompleted` and `ICriticalNotifyCompletion.UnsafeOnCompleted` should cause the delegate `r` to be invoked at most once. Otherwise, the behavior of the enclosing async function is undefined.
+## §range-operator Range operator
+
+The `..` operator is called the *range* operator.
+
+```ANTLR
+range_expression
+ : unary_expression
+ | unary_expression? '..' unary_expression?
+ ;
+```
+
+The predefined range operator is:
+
+```csharp
+Range operator ..(Index x, Index y);
+```
+
+The range operator is not overloadable (§12.4.3).
+
+All range expressions are treated as having the form `x..y`, where:
+
+- `x` is the left operand if present, otherwise the expression `0`; and
+- `y` is the right operand if present, otherwise the expression `^0`.
+
+The result of the operation is a `Range` (§24.3) value equivalent to the result of the expression:
+
+```csharp
+new Range(x, y)
+```
+
+If either or both operands in a range expression have the compile-time type `dynamic`, then the expression is dynamically bound ([§12.3.3](expressions.md#1233-dynamic-binding)). The compile-time type of the result is always `Range`.
+
+A lifted ([§12.4.8](expressions.md#1248-lifted-operators)) form of the range operator is also predefined.
+
+The range operator is non-associative (§12.4.2).
+
## 12.10 Arithmetic operators
### 12.10.1 General
@@ -3714,10 +3846,10 @@ The `*`, `/`, `%`, `+`, and `-` operators are called the arithmetic operators.
```ANTLR
multiplicative_expression
- : unary_expression
- | multiplicative_expression '*' unary_expression
- | multiplicative_expression '/' unary_expression
- | multiplicative_expression '%' unary_expression
+ : range_expression
+ | multiplicative_expression '*' range_expression
+ | multiplicative_expression '/' range_expression
+ | multiplicative_expression '%' range_expression
;
additive_expression
@@ -4574,7 +4706,7 @@ User defined conversions are not considered by the `is` operator.
> - If the compile-time type of `e` is the same as `T`, or if an implicit reference conversion ([§10.2.8](conversions.md#1028-implicit-reference-conversions)), boxing conversion ([§10.2.9](conversions.md#1029-boxing-conversions)), wrapping conversion ([§10.6](conversions.md#106-conversions-involving-nullable-types)), or an explicit unwrapping conversion ([§10.6](conversions.md#106-conversions-involving-nullable-types)) exists from the compile-time type of `E` to `T`:
> - If `C` is of a non-nullable value type, the result of the operation is `true`.
> - Otherwise, the result of the operation is equivalent to evaluating `E != null`.
-> - Otherwise, if an explicit reference conversion ([§10.3.5](conversions.md#1035-explicit-reference-conversions)) or unboxing conversion ([§10.3.7](conversions.md#1037-unboxing-conversions)) exists from `C` to `T`, or if `C` or `T` is an open type ([§8.4.3](types.md#843-open-and-closed-types)), then runtime checks as above shall be peformed.
+> - Otherwise, if an explicit reference conversion ([§10.3.5](conversions.md#1035-explicit-reference-conversions)) or unboxing conversion ([§10.3.7](conversions.md#1037-unboxing-conversions)) exists from `C` to `T`, or if `C` or `T` is an open type ([§8.4.3](types.md#843-open-and-closed-types)), then runtime checks as above shall be performed.
> - Otherwise, no reference, boxing, wrapping, or unwrapping conversion of `E` to type `T` is possible, and the result of the operation is `false`.
> A compiler may implement optimisations based on the compile-time type.
>
@@ -5318,7 +5450,7 @@ A local variable is considered to be ***instantiated*** when execution enters th
>
> *end example*
-When not captured, there is no way to observe exactly how often a local variable is instantiated—because the lifetimes of the instantiations are disjoint, it is possible for each instantation to simply use the same storage location. However, when an anonymous function captures a local variable, the effects of instantiation become apparent.
+When not captured, there is no way to observe exactly how often a local variable is instantiated—because the lifetimes of the instantiations are disjoint, it is possible for each instantiation to simply use the same storage location. However, when an anonymous function captures a local variable, the effects of instantiation become apparent.
> *Example*: The example
>
@@ -6416,7 +6548,7 @@ In the translation steps described above, transparent identifiers are always int
The ***Query-expression pattern*** establishes a pattern of methods that types can implement to support query expressions.
-A generic type `C` supports the query-expression-pattern if its public member methods and the publicly accessible extension methods could be replaced by the following class definition. The members and accessible extenson methods is referred to as the “shape” of a generic type `C`. A generic type is used in order to illustrate the proper relationships between parameter and return types, but it is possible to implement the pattern for non-generic types as well.
+A generic type `C` supports the query-expression-pattern if its public member methods and the publicly accessible extension methods could be replaced by the following class definition. The members and accessible extension methods is referred to as the “shape” of a generic type `C`. A generic type is used in order to illustrate the proper relationships between parameter and return types, but it is possible to implement the pattern for non-generic types as well.
```csharp
diff --git a/standard/lexical-structure.md b/standard/lexical-structure.md
index 3e466b29c..f440da104 100644
--- a/standard/lexical-structure.md
+++ b/standard/lexical-structure.md
@@ -1010,11 +1010,11 @@ Punctuators are for grouping and separating.
```ANTLR
operator_or_punctuator
- : '{' | '}' | '[' | ']' | '(' | ')' | '.' | ',' | ':' | ';'
- | '+' | '-' | ASTERISK | SLASH | '%' | '&' | '|' | '^' | '!' | '~'
- | '=' | '<' | '>' | '?' | '??' | '::' | '++' | '--' | '&&' | '||'
- | '->' | '==' | '!=' | '<=' | '>=' | '+=' | '-=' | '*=' | '/=' | '%='
- | '&=' | '|=' | '^=' | '<<' | '<<=' | '=>' | '??='
+ : '{' | '}' | '[' | ']' | '(' | ')' | '.' | ',' | ':' | ';'
+ | '+' | '-' | '*' | '/' | '%' | '&' | '|' | '^' | '!' | '~'
+ | '=' | '<' | '>' | '?' | '??' | '::' | '++' | '--' | '&&' | '||'
+ | '->' | '==' | '!=' | '<=' | '>=' | '+=' | '-=' | '*=' | '/=' | '%='
+ | '&=' | '|=' | '^=' | '<<' | '<<=' | '=>' | '??=' | '..'
;
right_shift
diff --git a/standard/ranges.md b/standard/ranges.md
new file mode 100644
index 000000000..538852b9f
--- /dev/null
+++ b/standard/ranges.md
@@ -0,0 +1,309 @@
+# 24 Extended Indexing and Slicing
+
+> **Review Note:** This new clause, currently (§24), is placed here temporarily to avoid text changes due to renumbering occurring in chapters & clauses otherwise unaffected by the PR. Its final placement is not yet determined, however between the Arrays ([§17](arrays.md#17-arrays)) and Interfaces ([§18](interfaces.md#18-interfaces)) chapters might be suitable – other placements can be suggested during review. It can be relocated later with just a simple edit to `clauses.json`.
+
+## 24.1 General
+
+This clause introduces a model for *extended indexable* and *sliceable* *collection* types built on:
+
+- The types introduced in this clause, `System.Index` (§24.2) and `System.Range` (§24.3);
+- The pre-defined unary `^` (§hat-operator) and binary `..` (§range-operator) operators; and
+- The *element_access* expression.
+
+Under the model a type is classified as:
+
+- a *collection* if it represents a group of *element*s
+- an *extended indexable* collection if it supports an *element_access* expression which has a single argument expression of type `Index` which returns and/or sets a single element of the type, either by value or by reference; and
+- an *extended sliceable* collection if it supports an *element_access* expression which has a single argument expression of type `Range` which returns a *slice* of the elements of the type by value.
+
+> *Note*: The model does not require that a slice, unlike an element, of the type can be set, but a type may support it as an extension of the model. *end note*
+
+The model is supported for single-dimensional arrays (§12.8.12.2) and strings (§string-access).
+
+The model can be supported by any class, struct or interface type which provides appropriate indexers (§15.9) which implement the model semantics.
+
+Implicit support for the model is provided for types which do not directly support it but which provide a certain *pattern* of members (§24.4). This support is pattern-based, rather than semantic-based, as the semantics of the type members upon which it is based are *assumed* – the language does not enforce, or check, the semantics of these type members.
+
+For the purposes of this clause the following terms are defined:
+
+- A ***collection*** is a type which represents a group of ***element***s.
+- A ***countable collection*** is one which provides a ***countable property*** an `int` valued instance property whose value is the number of elements currently in the group. This property shall be named either `Length` or `Count`, the former is chosen if both exist.
+- A ***sequence*** or ***indexable type*** is a collection:
+ - which is countable;
+ - where every element can be accessed using an *element_access* expression with a single required `int` argument, the ***from-start index***, additional optional arguments are allowed;
+ - a sequence is ***modifiable*** if every element can also be set using an *element_access* expression;
+ - an element’s from-start index is the number of elements before it in the sequence, for a sequence containing *N* elements:
+ - the first and last elements have indices of 0 and *N*-1 respectively, and
+ - the ***past-end index***, an index which represents a hypothetical element after the last one, has the value *N*.
+- A ***from-end index*** represents an element’s position within a sequence relative to the last element. For a sequence containing *N* elements the first, last and past-end indices are *N*, 1 and 0 respectively.
+- A ***range*** is a contiguous run of zero or more indices starting at any index within a sequence.
+- A ***slice*** is the collection of elements within a range.
+- A ***sliceable collection*** is one which:
+ - is countable;
+ - provides a method `Slice` which takes two `int` parameters specifying a range, being a starting index and a count of elements respectively, and returns a new slice constructed from the elements in the range.
+
+The above definitions are extended for uses of `Index` and `Range` as follows:
+
+- A type is also a *sequence* if an *element_access* expression taking a single required `Index` argument, rather than an `int` argument, is supported. Where a distinction is required the type is termed ***extended indexable***.
+- A type is also *sliceable* if an *element_access* expression taking a single required `Range` argument, rather a `Slice` method, is supported. Where a distinction is required the type is termed ***extended sliceable***.
+
+Whether a type is classified as countable, indexable, or sliceable is subject to the constraints of member accessibility (§7.5) and therefore dependent on where the type is being used.
+
+> *Example*: A type where the countable property and/or the indexer are `protected` is only a sequence to members of itself and any derived types. *end example*
+
+The required members for a type to qualify as a sequence or sliceable may be inherited.
+
+> *Example*: In the following code
+>
+> ```csharp
+> public class A
+> {
+> public int Length { get { … } }
+> }
+>
+> public class B : A
+> {
+> public int this(int index) { … }
+> }
+>
+> public class C : B
+> {
+> public int[] Slice(int index, int count) { … }
+> }
+> ```
+>
+> The type `A` is countable, `B` is a sequence, and `C` is sliceable and a sequence.
+>
+> *end example*
+
+
+
+> *Note*:
+>
+> - A type can be sliceable without being indexable due to the lack of an (accessible) indexer.
+> - For a type to be sliceable and/or indexable requires the type to be countable.
+> - While the elements of a sequence are ordered by *position* within the sequence the elements themselves need not be ordered by their value, or even orderable.
+>
+> *end note*
+
+## 24.2 The Index type
+
+The `System.Index` type represents an *abstract* index which is either a *from-start index* or a *from-end index*.
+
+```csharp
+ public readonly struct Index : IEquatable
+ {
+ public int Value { get; }
+ public bool IsFromEnd { get; }
+
+ public Index(int value, bool fromEnd = false);
+
+ public static implicit operator Index(int value);
+ public int GetOffset(int length);
+ public bool Equals(Index other);
+ }
+```
+
+`Index` values are constructed from an `int`, specifying the non-negative offset, and a `bool`, indicating whether the offset is from the end (`true`) or start (`false`). If the specified offset is negative an `ArgumentOutOfRangeException` is thrown.
+
+> *Example*
+>
+> ```csharp
+> Index first = new Index(0, false); // first element index
+> var last = new Index(1, true); // last element index
+> var past = new Index(0, true); // past-end index
+>
+> Index invalid = new Index(-1); // throws ArgumentOutOfRangeException
+> ```
+>
+> *end example*
+
+There is an implicit conversion from `int` to `Index` which produces from-start indices, and a language-defined unary operator `^` (§hat-operator) from `int` to `Index` which produces from-end indices.
+
+> *Example*
+>
+> Using implicit conversions and the unary `^` operator the above examples may be written:
+>
+> ```csharp
+> Index first = 0; // first element index
+> var last = ^1; // last element index
+> var past = ^0; // past-end index
+> ```
+>
+> *end example*
+
+The method `GetOffset` converts from an abstract `Index` value to a concrete `int` index value for a sequence of the specified `length`. If the `Index` value, `I`, is from-end this method returns the same value as `length - I.Value`, otherwise it returns the same value as `I.Value`.
+
+This method does **not** check that the return value is in the valid range of `0` through `length-1` inclusive.
+
+> *Note:* No checking is specified as the expected use of the result is to index into a sequence with `length` elements, and that indexing operation is expected to perform the appropriate checks. *end note*
+
+`Index` implements `IEquatable` and values may be compared for equality based on the abstract value; two `Index` values are equal if and only if the respective `Value` and `IsFromEnd` properties are equal. However `Index` values are not ordered and no other comparison operations are provided.
+
+> *Note:* `Index` values are unordered as they are abstract indices, it is in general impossible to determine whether a from-end index comes before or after a from start index without reference to a sequence length. Once converted to concrete indices, e.g. by `GetOffset`, those concrete indices are comparable. *end note*
+
+`Index` values may be directly used in the *argument_list* of an *element_access* expression (§12.8.12) which is:
+
+- an array access and the target is a single-dimensional array (§12.8.12.2);
+- a string access (§string-access)
+- an indexer access and the target type has an indexer with corresponding parameters of either `Index` type (§12.8.12.3) or of a type to which `Index` values are implicitly convertible; or
+- an indexer access and the target type conforms to a sequence pattern for which implicit `Index` support is specified (§24.4.2).
+
+## 24.3 The Range type
+
+The `System.Range` type represents the abstract range of `Index`es from a `Start` index up to, but not including, an `End` index.
+
+```csharp
+ public readonly struct Range : IEquatable
+ {
+ public Index Start { get; }
+ public Index End { get; }
+
+ public Range(Index start, Index end);
+
+ public (int Offset, int Length) GetOffsetAndLength(int length);
+ public bool Equals(Range other);
+ }
+```
+
+`Range` values are constructed from two `Index` values.
+
+> *Example*
+>
+> The following examples use the implicit conversion from `int` to `Index` (§24.2) and the `^` (§hat-operator) operator to create the `Index` values for each `Range`:
+>
+> ```csharp
+> var firstQuad = new Range(0, 4); // the indices from `0` to `3`
+> // int values impicitly convert to `Index`
+> var nextQuad = new Range(4, 8); // the indices from `4` to `7`
+> var wholeSeq = new Range(0, ^0); // the indices from `0` to `N-1` where `N` is the
+> // length of the sequence wholeSeq is used with
+> var dropFirst = new Range(1, ^0); // the indices from `1` to `N-1`
+> var dropLast = new Range(0, ^1); // the indices from `0` to `N-2`
+> var maybeLast = new Range(^1, 6); // the indices from `N-1` to 5
+> var lastTwo = new Range(^2, ^0); // the indices from `N-2` to `N-1`
+> ```
+>
+> *end example*
+
+The language-defined operator `..` (§range-operator) creates a `Range` value from `Index` values.
+
+> *Example*
+>
+> Using the `..` operator the above examples may be written:
+>
+> ```csharp
+> var firstQuad = 0..4; // the indices from `0` to `3`
+> var nextQuad = 4..8; // the indices from `4` to `7`
+> var wholeSeq = 0..^0; // the indices from `0` to `N-1`
+> var dropFirst = 1..^0; // the indices from `1` to `N-1`
+> var dropLast = 0..^1; // the indices from `0` to `N-2`
+> var maybeLast = ^1..6; // the indices from `N-1` to 5
+> var lastTwo = ^2..^0; // the indices from `N-2` to `N-1`
+> ```
+>
+> *end example*
+
+The operands of `..` are optional, the first defaults to `0`, the second defaults to `^0`.
+
+> *Example*
+>
+> Five of the above examples can be shortened by relying on default values for operands:
+>
+> ```csharp
+> var firstQuad = ..4; // the indices from `0` to `3`
+> var wholeSeq = ..; // the indices from `0` to `N-1`
+> var dropFirst = 1..; // the indices from `1` to `N-1`
+> var dropLast = ..^1; // the indices from `0` to `N-2`
+> var lastTwo = ^2..; // the indices from `N-2` to `N-1`
+> ```
+>
+> *end example*
+
+A `Range` value is *valid* with respect to a length *L* if:
+
+- the concrete indices with respect to *L* of the `Range` properties `Start` and `End` are in the range 0 to *L*; and
+- the concrete index for `Start` is not greater than the concrete index for `End`
+
+The method `GetOffsetAndLength` with an argument `length` converts an abstract `Range` value to a concrete `Range` value represented by tuple. If the `Range` is not valid with respect to `length` the method throws `ArgumentOutOfRangeException`.
+
+The returned concrete `Range` tuple is a pair of the form `(S, N)` where:
+
+- `S` is the starting offset of the range, being the concrete index for the `Start` property of the `Range`; and
+- `N` is the number of items in the range, being the difference between the concrete indices for the `End` and `Start` properties;
+- both values being calculated with respect to `length`.
+
+A concrete range value is *empty* if `N` is zero. An empty concrete range may have an `S` value equal to concrete past-end index (§24.1), a non-empty range may not. When a `Range` which is used to slice (§24.1) a collection is valid and empty with respect to that collection then the resulting slice is an empty collection.
+
+> *Note:* A consequence of the above is that a `Range` value which is valid and empty with respect to a `length` of zero may be used to slice an empty collection and results in an empty slice. This differs from indexing which throws an exception if the collection is empty. *end note**
+
+
+
+> *Example*
+>
+> Using the variables defined above with `GetOffSetAndLength(6)`:
+>
+> ```csharp
+> var (ix0, len0) = firstQuad.GetOffsetAndLength(6); // ix0 = 0, len0 = 4
+> var (ix1, len1) = nextQuad.GetOffsetAndLength(6); // throws ArgumentOutOfRangeException
+> // as range crosses sequence end
+> var (ix2, len2) = wholeSeq.GetOffsetAndLength(6); // ix2 = 0, len2 = 6
+> var (ix3, len3) = dropFirst.GetOffsetAndLength(6); // ix3 = 1, len3 = 5
+> var (ix4, len4) = dropLast.GetOffsetAndLength(6); // ix4 = 0, len4 = 5
+> var (ix5, len5) = maybeLast.GetOffsetAndLength(6); // ix5 = 5, len5 = 1
+> var (ix6, len6) = lastTwo.GetOffsetAndLength(6); // ix6 = 4, len6 = 2
+> ```
+
+`Range` implements `IEquatable` and values may be compared for equality based on the abstract value; two `Range` values are equal if and only if the abstract values of the respective `Start` and `End` properties are equal (§24.2). However `Range` values are not ordered and no other comparison operations are provided.
+
+> *Note:* `Range` values are unordered both as they are abstract and there is no unique ordering relation. Once converted to a concrete start and length, e.g. by `GetOffsetAndLength`, an ordering relation could be defined. *end note*
+
+`Range` values can be directly used in the *argument_list* of an *element_access* expression (§12.8.12) which is:
+
+- an array access and the target is a single-dimensional array (§12.8.12.2);
+- a string access (§string-access);
+- an indexer access and the target type has an indexer with corresponding parameters of either `Range` type (§12.8.12.3) or of a type to which `Range` values are implicitly convertible; or
+- an indexer access (§12.8.12.3) and the target type conforms to a sequence pattern for which implicit `Range` support is specified (§24.4.3).
+
+## 24.4 Pattern-based implicit support for Index and Range
+
+### 24.4.1 General
+
+If an *element_access* expression (§12.8.12) of the form `E[A]`; where `E` has type `T` and `A` is a single expression implicitly convertible to `Index` or `Range`; fails to be identified as:
+
+- an array access (§12.8.12.2),
+- a string access (§string-access), or
+- an indexer access (§12.8.12.3) as `T` provides no suitable accessible indexer
+
+then implicit support for the expression is provided if `T` conforms to a particular pattern. If `T` does not conform to this pattern then a compile-time error occurs.
+
+### 24.4.2 Implicit Index support
+
+If in any context an *element_access* expression (§12.8.12) of the form `E[A]`; where `E` has type `T` and `A` is a single expression implicitly convertible to `Index`; is not valid (§24.4.1) then if in the same context:
+
+- `T` provides accessible members qualifying it as a *sequence* (§24.1); and
+- the expression `E[0]` is valid and uses the same indexer that qualifies `T` as a sequence
+
+then the expression `E[A]` shall be implicitly supported.
+
+Without otherwise constraining implementations of this Standard the order of evaluation of the expression shall be equivalent to:
+
+1. `E` is evaluated;
+2. `A` is evaluated;
+3. the countable property of `T` is evaluated, if required by the implementation;
+4. the get or set accessor of the `int` based indexer of `T` that would be used by `E[0]` in the same context is invoked.
+
+### 24.4.3 Implicit Range support
+
+If in any context an *element_access* expression (§12.8.12) of the form `E[A]`; where `E` has type `T` and `A` is a single expression implicitly convertible to `Range`; is not valid (§24.4.1) then if in the same context:
+
+- `T` provides accessible members qualifying it as both *countable* and *sliceable* (§24.1)
+
+then the expression `E[A]` shall be implicitly supported.
+
+Without otherwise constraining implementations of this Standard the order of evaluation of the expression shall be equivalent to:
+
+1. `E` is evaluated;
+2. `A` is evaluated;
+3. the countable property of `T` is evaluated, if required by the implementation;
+4. the `Slice` method of `T` is invoked.
diff --git a/standard/standard-library.md b/standard/standard-library.md
index 9e8efdac0..fc0dc2b44 100644
--- a/standard/standard-library.md
+++ b/standard/standard-library.md
@@ -36,6 +36,12 @@ namespace System
public ArgumentException(string? message, Exception? innerException);
}
+ public class ArgumentOutOfRangeException : ArgumentException
+ {
+ public ArgumentOutOfRangeException(string? paramName);
+ public ArgumentOutOfRangeException(string? paramName, string? message);
+ }
+
public class ArithmeticException : Exception
{
public ArithmeticException();
@@ -130,6 +136,11 @@ namespace System
void Dispose();
}
+ public interface IEquatable
+ {
+ bool Equals(T? other);
+ }
+
public interface IFormattable { }
public sealed class IndexOutOfRangeException : Exception
@@ -396,6 +407,164 @@ namespace System
public OperationCanceledException(string? message, Exception? innerException);
}
+ ///
+ /// A read-only value type which represents an abstract
+ /// index to be used with collections.
+ /// - The Index can be relative to the start or end of a
+ /// collection.
+ /// - An Index can be converted to a zero-based concrete
+ /// from-start index to be used with a collection
+ /// of some specified length.
+ /// - Equality between Index values is provided, however
+ /// unlike concrete indices they are not ordered.
+ /// - Array and String element access support indexing
+ /// with Index values.
+ ///
+ public readonly struct Index : IEquatable
+ {
+ ///
+ /// Construct an Index from an integer value and a
+ /// boolean indicating whether the value is relative
+ /// to the end (true) or start (false).
+ ///
+ ///
+ /// The value, must be ≥ 0.
+ ///
+ ///
+ /// Optional boolean indicating whether the Index is
+ /// relative to the end (true) or start (false).
+ /// The default value is false.
+ ///
+ ///
+ /// Thrown if value < 0.
+ ///
+ ///
+ /// If the Index is relative to the start then:
+ /// - the value 0 refers to the first element.
+ /// If the Index is relative to the end then:
+ /// - the value 1 refers to the last element; and
+ /// - the value 0 refers to beyond last element.
+ ///
+ public Index(int value, bool fromEnd = false);
+
+ ///
+ /// Implicit conversion from integer to a
+ /// from-start Index.
+ ///
+ ///
+ /// The predefined operator:
+ /// Index operator ^(int value);
+ /// is provided to convert from integer to a
+ /// from-end Index.
+ ///
+ public static implicit operator Index(int value);
+
+ ///
+ /// Return the value.
+ ///
+ public int Value { get; }
+
+ ///
+ /// Return whether the Index is relative to
+ /// the end (true) or start (false).
+ ///
+ public bool IsFromEnd { get; }
+
+ ///
+ /// Return a concrete from-start index for a
+ /// given collection length.
+ ///
+ ///
+ /// The length of the collection that the index
+ /// will be used with.
+ ///
+ ///
+ /// This method performs no sanity checking and
+ /// will never throw an IndexOutOfRangeException.
+ /// It is expected that the returned index will be
+ /// used with a collection which will do validation.
+ ///
+ public int GetOffset(int length);
+
+ ///
+ /// Indicates whether the current Index value is
+ /// equal to another Index value.
+ ///
+ ///
+ /// The value to compare with this Index.
+ ///
+ public bool Equals(Index other);
+ }
+
+ ///
+ /// A read-only value type which represents a range of
+ /// abstract indices to be used with collections.
+ /// - The Range has two Index properties, Start and End.
+ /// - A Range can be converted to a concrete index from
+ /// the start and a length value to be used with a
+ /// collection of some specified length.
+ /// - Equality between Range values is provided,
+ /// however they are not ordered.
+ /// - Array and String element access supports indexing
+ /// with Range values, returning a sub-array/substring
+ /// of the indexed value respectively.
+ ///
+ public readonly struct Range : IEquatable
+ {
+ ///
+ /// Construct a Range from two Index values.
+ ///
+ ///
+ /// The inclusive Index value for the start
+ /// of the range.
+ ///
+ ///
+ /// The exclusive Index value for the end
+ /// of the range.
+ ///
+ /// As Index values represent unordered abstract
+ /// indices no sanity checking can be performed
+ /// on the resultant Range value,
+ /// ".
+ ///
+ /// The predefined operator:
+ /// Range operator ..(Index start, Index end);
+ /// also exists to create a Range value.
+ ///
+ public Range(Index start, Index end);
+
+ /// Return the starting Index.
+ public Index Start { get; }
+
+ /// Return the ending Index.
+ public Index End { get; }
+
+ ///
+ /// Return a concrete from-start index and the
+ /// range length for a given collection length.
+ ///
+ ///
+ /// The length of the collection that the result
+ /// will be used with.
+ ///
+ ///
+ /// Thrown if the range is not valid wrt length.
+ ///
+ ///
+ /// A tuple consisting of an index value and range length
+ ///
+ public (int Offset, int Length) GetOffsetAndLength(int length);
+
+ ///
+ /// Indicates whether the current Range value is equal
+ /// to another Range value.
+ ///
+ ///
+ /// The value to compare with this Range.
+ ///
+ public bool Equals(Range other);
+ }
+
public readonly ref struct ReadOnlySpan
{
public int Length { get; }
@@ -1105,6 +1274,7 @@ The following library types are referenced in this specification. The full names
- `global::System.Action`
- `global::System.ArgumentException`
+- `global::System.ArgumentOutOfRangeException`
- `global::System.ArithmeticException`
- `global::System.Array`
- `global::System.ArrayTypeMisMatchException`
@@ -1124,7 +1294,9 @@ The following library types are referenced in this specification. The full names
- `global::System.GC`
- `global::System.IAsyncDisposable`
- `global::System.IDisposable`
+- `global::System.IEquatable`
- `global::System.IFormattable`
+- `global::System.Index`
- `global::System.IndexOutOfRangeException`
- `global::System.Int16`
- `global::System.Int32`
@@ -1140,6 +1312,7 @@ The following library types are referenced in this specification. The full names
- `global::System.OperationCanceledException`
- `global::System.OutOfMemoryException`
- `global::System.OverflowException`
+- `global::System.Range`
- `global::System.ReadOnlySpan`
- `global::System.SByte`
- `global::System.Single`
diff --git a/tools/GrammarTesting/Tests/Parsing/Samples/v6/AllInOneNoPreprocessor-v6-split/part-I/Reference/sample.gruntree.red.txt b/tools/GrammarTesting/Tests/Parsing/Samples/v6/AllInOneNoPreprocessor-v6-split/part-I/Reference/sample.gruntree.red.txt
index 2b6bc6914..1f0eb4e20 100644
--- a/tools/GrammarTesting/Tests/Parsing/Samples/v6/AllInOneNoPreprocessor-v6-split/part-I/Reference/sample.gruntree.red.txt
+++ b/tools/GrammarTesting/Tests/Parsing/Samples/v6/AllInOneNoPreprocessor-v6-split/part-I/Reference/sample.gruntree.red.txt
@@ -598,7 +598,7 @@
⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎝
⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ *
⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎛
-⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ unary_expression
+⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ range_expression
⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎛
⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ identifier
⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ number
diff --git a/tools/GrammarTesting/Tests/Parsing/Samples/v6/AllInOneNoPreprocessor-v6-split/part-I/Reference/sample.tree.red.txt b/tools/GrammarTesting/Tests/Parsing/Samples/v6/AllInOneNoPreprocessor-v6-split/part-I/Reference/sample.tree.red.txt
index b2451249c..0faf8849b 100644
--- a/tools/GrammarTesting/Tests/Parsing/Samples/v6/AllInOneNoPreprocessor-v6-split/part-I/Reference/sample.tree.red.txt
+++ b/tools/GrammarTesting/Tests/Parsing/Samples/v6/AllInOneNoPreprocessor-v6-split/part-I/Reference/sample.tree.red.txt
@@ -1 +1 @@
-(prog (compilation_unit (namespace_declaration namespace (qualified_identifier (identifier My)) (namespace_body { (namespace_member_declaration (enum_declaration (attributes (attribute_section [ (attribute_target_specifier (attribute_target (identifier type)) :) (attribute_list (identifier Flags)) ])) (enum_modifier public) enum (identifier E) (enum_body { (enum_member_declarations (enum_member_declaration (identifier A)) , (enum_member_declaration (identifier B) = (constant_expression (identifier A))) , (enum_member_declaration (identifier C) = (constant_expression (additive_expression (additive_expression (literal 2)) + (multiplicative_expression (identifier A))))) , (enum_member_declaration (identifier D))) , }))) (namespace_member_declaration (delegate_declaration (delegate_modifier public) delegate (return_type void) (delegate_header (identifier Delegate) ( (parameter_list (fixed_parameter (type (class_type object)) (identifier P))) ) ;))) (namespace_member_declaration (namespace_declaration namespace (qualified_identifier (identifier Test)) (namespace_body { (using_directive (using_namespace_directive using (namespace_name (identifier System)) ;)) (using_directive (using_namespace_directive using (namespace_name (namespace_or_type_name (identifier System) . (identifier Collections))) ;)) (namespace_member_declaration (class_declaration (class_modifier public) class (identifier Список) (class_body { (class_member_declaration (method_declaration (method_modifiers (method_modifier (ref_method_modifier public)) (method_modifier (ref_method_modifier static))) (return_type (identifier IEnumerable)) (method_header (member_name (identifier Power)) ( (parameter_list (fixed_parameters (fixed_parameter (type (integral_type int)) (identifier number)) , (fixed_parameter (type (integral_type int)) (identifier exponent)))) )) (method_body (block { (statement_list (statement (declaration_statement (local_variable_declaration (explicitly_typed_local_variable_declaration (type (identifier Список)) (explicitly_typed_local_variable_declarators (explicitly_typed_local_variable_declarator (identifier Список) = (local_variable_initializer (object_creation_expression new (type (identifier Список)) ( ))))))) ;)) (statement (expression_statement (statement_expression (invocation_expression (primary_expression (member_access (primary_expression (identifier Список)) . (identifier Main))) ( ))) ;)) (statement (declaration_statement (local_variable_declaration (explicitly_typed_local_variable_declaration (type (integral_type int)) (explicitly_typed_local_variable_declarators (explicitly_typed_local_variable_declarator (identifier counter) = (local_variable_initializer (parenthesized_expression ( (expression (additive_expression (additive_expression (literal 0)) + (multiplicative_expression (literal 0)))) ))))))) ;)) (statement (declaration_statement (local_variable_declaration (explicitly_typed_local_variable_declaration (type (integral_type int)) (explicitly_typed_local_variable_declarators (explicitly_typed_local_variable_declarator (identifier אתר) = (local_variable_initializer (literal 0)))))) ;)) (statement (while_statement while ( (boolean_expression (relational_expression (relational_expression (pre_increment_expression ++ (unary_expression (post_increment_expression (primary_expression (identifier counter)) ++)))) < (shift_expression (pre_decrement_expression -- (unary_expression (post_decrement_expression (primary_expression (identifier exponent)) --)))))) ) (embedded_statement (block { (statement_list (statement (expression_statement (statement_expression (assignment (unary_expression (identifier result)) (assignment_operator =) (expression (additive_expression (additive_expression (additive_expression (multiplicative_expression (multiplicative_expression (identifier result)) * (unary_expression (identifier number)))) + (multiplicative_expression (unary_expression + (unary_expression (post_increment_expression (primary_expression (post_increment_expression (primary_expression (identifier number)) ++)) ++))))) + (multiplicative_expression (identifier number)))))) ;)) (statement (yield_statement yield return (expression (identifier result)) ;))) }))))) })))) (class_member_declaration (method_declaration (method_modifiers (ref_method_modifier static)) (return_type void) (method_header (member_name (identifier Main)) ( )) (method_body (block { (statement_list (foreach_statement foreach ( (local_variable_type (integral_type int)) (identifier i) in (expression (invocation_expression (primary_expression (identifier Power)) ( (argument_list (argument (literal 2)) , (argument (literal 8))) ))) ) (embedded_statement (block { (statement_list (expression_statement (statement_expression (invocation_expression (primary_expression (member_access (primary_expression (identifier Console)) . (identifier Write))) ( (argument_list (argument (literal "{0} ")) , (argument (identifier i))) ))) ;)) })))) })))) (class_member_declaration (method_declaration (method_modifiers (method_modifier async)) (return_type void) (method_header (member_name (identifier Wait)) ( )) (method_body (block { (statement_list (expression_statement (statement_expression (await_expression await (unary_expression (invocation_expression (primary_expression (member_access (primary_expression (member_access (primary_expression (member_access (primary_expression (member_access (primary_expression (identifier System)) . (identifier Threading))) . (identifier Tasks))) . (identifier Task))) . (identifier Delay))) ( (argument_list (literal 0)) ))))) ;)) })))) (class_member_declaration (method_declaration method_modifiers (return_type void) (method_header (member_name (identifier AsyncAnonymous)) ( )) (method_body (block { (statement_list (declaration_statement (local_variable_declaration (implicitly_typed_local_variable_declaration var (implicitly_typed_local_variable_declarator (identifier task) = (expression (invocation_expression (primary_expression (member_access (primary_expression (member_access (primary_expression (identifier Task)) . (identifier Factory))) . (identifier StartNew))) ( (argument_list (lambda_expression async (anonymous_function_signature (explicit_anonymous_function_signature ( ))) => (anonymous_function_body (block { (statement_list (return_statement return (expression (await_expression await (unary_expression (invocation_expression (primary_expression (member_access (primary_expression (object_creation_expression new (type (identifier WebClient)) ( ))) . (identifier DownloadStringTaskAsync))) ( (argument_list (literal "http://example.com")) ))))) ;)) })))) )))))) ;)) })))) }))) }))) }))))
+(prog (compilation_unit (namespace_declaration namespace (qualified_identifier (identifier My)) (namespace_body { (namespace_member_declaration (enum_declaration (attributes (attribute_section [ (attribute_target_specifier (attribute_target (identifier type)) :) (attribute_list (identifier Flags)) ])) (enum_modifier public) enum (identifier E) (enum_body { (enum_member_declarations (enum_member_declaration (identifier A)) , (enum_member_declaration (identifier B) = (constant_expression (identifier A))) , (enum_member_declaration (identifier C) = (constant_expression (additive_expression (additive_expression (literal 2)) + (multiplicative_expression (identifier A))))) , (enum_member_declaration (identifier D))) , }))) (namespace_member_declaration (delegate_declaration (delegate_modifier public) delegate (return_type void) (delegate_header (identifier Delegate) ( (parameter_list (fixed_parameter (type (class_type object)) (identifier P))) ) ;))) (namespace_member_declaration (namespace_declaration namespace (qualified_identifier (identifier Test)) (namespace_body { (using_directive (using_namespace_directive using (namespace_name (identifier System)) ;)) (using_directive (using_namespace_directive using (namespace_name (namespace_or_type_name (identifier System) . (identifier Collections))) ;)) (namespace_member_declaration (class_declaration (class_modifier public) class (identifier Список) (class_body { (class_member_declaration (method_declaration (method_modifiers (method_modifier (ref_method_modifier public)) (method_modifier (ref_method_modifier static))) (return_type (identifier IEnumerable)) (method_header (member_name (identifier Power)) ( (parameter_list (fixed_parameters (fixed_parameter (type (integral_type int)) (identifier number)) , (fixed_parameter (type (integral_type int)) (identifier exponent)))) )) (method_body (block { (statement_list (statement (declaration_statement (local_variable_declaration (explicitly_typed_local_variable_declaration (type (identifier Список)) (explicitly_typed_local_variable_declarators (explicitly_typed_local_variable_declarator (identifier Список) = (local_variable_initializer (object_creation_expression new (type (identifier Список)) ( ))))))) ;)) (statement (expression_statement (statement_expression (invocation_expression (primary_expression (member_access (primary_expression (identifier Список)) . (identifier Main))) ( ))) ;)) (statement (declaration_statement (local_variable_declaration (explicitly_typed_local_variable_declaration (type (integral_type int)) (explicitly_typed_local_variable_declarators (explicitly_typed_local_variable_declarator (identifier counter) = (local_variable_initializer (parenthesized_expression ( (expression (additive_expression (additive_expression (literal 0)) + (multiplicative_expression (literal 0)))) ))))))) ;)) (statement (declaration_statement (local_variable_declaration (explicitly_typed_local_variable_declaration (type (integral_type int)) (explicitly_typed_local_variable_declarators (explicitly_typed_local_variable_declarator (identifier אתר) = (local_variable_initializer (literal 0)))))) ;)) (statement (while_statement while ( (boolean_expression (relational_expression (relational_expression (pre_increment_expression ++ (unary_expression (post_increment_expression (primary_expression (identifier counter)) ++)))) < (shift_expression (pre_decrement_expression -- (unary_expression (post_decrement_expression (primary_expression (identifier exponent)) --)))))) ) (embedded_statement (block { (statement_list (statement (expression_statement (statement_expression (assignment (unary_expression (identifier result)) (assignment_operator =) (expression (additive_expression (additive_expression (additive_expression (multiplicative_expression (multiplicative_expression (identifier result)) * (range_expression (identifier number)))) + (multiplicative_expression (unary_expression + (unary_expression (post_increment_expression (primary_expression (post_increment_expression (primary_expression (identifier number)) ++)) ++))))) + (multiplicative_expression (identifier number)))))) ;)) (statement (yield_statement yield return (expression (identifier result)) ;))) }))))) })))) (class_member_declaration (method_declaration (method_modifiers (ref_method_modifier static)) (return_type void) (method_header (member_name (identifier Main)) ( )) (method_body (block { (statement_list (foreach_statement foreach ( (local_variable_type (integral_type int)) (identifier i) in (expression (invocation_expression (primary_expression (identifier Power)) ( (argument_list (argument (literal 2)) , (argument (literal 8))) ))) ) (embedded_statement (block { (statement_list (expression_statement (statement_expression (invocation_expression (primary_expression (member_access (primary_expression (identifier Console)) . (identifier Write))) ( (argument_list (argument (literal "{0} ")) , (argument (identifier i))) ))) ;)) })))) })))) (class_member_declaration (method_declaration (method_modifiers (method_modifier async)) (return_type void) (method_header (member_name (identifier Wait)) ( )) (method_body (block { (statement_list (expression_statement (statement_expression (await_expression await (unary_expression (invocation_expression (primary_expression (member_access (primary_expression (member_access (primary_expression (member_access (primary_expression (member_access (primary_expression (identifier System)) . (identifier Threading))) . (identifier Tasks))) . (identifier Task))) . (identifier Delay))) ( (argument_list (literal 0)) ))))) ;)) })))) (class_member_declaration (method_declaration method_modifiers (return_type void) (method_header (member_name (identifier AsyncAnonymous)) ( )) (method_body (block { (statement_list (declaration_statement (local_variable_declaration (implicitly_typed_local_variable_declaration var (implicitly_typed_local_variable_declarator (identifier task) = (expression (invocation_expression (primary_expression (member_access (primary_expression (member_access (primary_expression (identifier Task)) . (identifier Factory))) . (identifier StartNew))) ( (argument_list (lambda_expression async (anonymous_function_signature (explicit_anonymous_function_signature ( ))) => (anonymous_function_body (block { (statement_list (return_statement return (expression (await_expression await (unary_expression (invocation_expression (primary_expression (member_access (primary_expression (object_creation_expression new (type (identifier WebClient)) ( ))) . (identifier DownloadStringTaskAsync))) ( (argument_list (literal "http://example.com")) ))))) ;)) })))) )))))) ;)) })))) }))) }))) }))))
diff --git a/tools/GrammarTesting/Tests/Parsing/Samples/v6/AllInOneNoPreprocessor-v6-split/part-I/Reference/sample.tree.svg b/tools/GrammarTesting/Tests/Parsing/Samples/v6/AllInOneNoPreprocessor-v6-split/part-I/Reference/sample.tree.svg
index 994d268a4..bc4e09f5e 100644
--- a/tools/GrammarTesting/Tests/Parsing/Samples/v6/AllInOneNoPreprocessor-v6-split/part-I/Reference/sample.tree.svg
+++ b/tools/GrammarTesting/Tests/Parsing/Samples/v6/AllInOneNoPreprocessor-v6-split/part-I/Reference/sample.tree.svg
@@ -515,2076 +515,2076 @@
-
-
-block
-
-
-
-delegate_header
-
-
-
-explicitly_typed_local_variable_declaration
-
-
-
-+
-
-
-
-primary_expression
+
+
+int
-
-
-0
+
+
+.
-
-
-expression
+
+
+)
-
-
-primary_expression
+
+
+}
-
-
-ref_method_modifier
+
+
+)
-
-
-Factory
+
+
+explicitly_typed_local_variable_declarator
-
-
-{
+
+
+argument_list
-
-
-Main
+
+
+System
-
-
+
+
;
-
-
-Список
-
-
-
-delegate_declaration
+
+
+identifier
-
-
-additive_expression
+
+
+Test
-
-
+
+
declaration_statement
-
-
-identifier
+
+
+void
-
-
-prog
+
+
+object_creation_expression
-
-
-statement_list
+
+
+System
-
-
-identifier
+
+
+method_declaration
-
-
-{
+
+
+public
-
-
-statement
+
+
++
-
-
-async
+
+
+*
-
-
-await
+
+
+primary_expression
-
-
-;
+
+
+primary_expression
-
-
-A
+
+
+member_access
-
-
-identifier
+
+
+local_variable_initializer
-
-
-identifier
+
+
+argument_list
-
-
-,
+
+
+statement_expression
-
-
+
+
identifier
-
-
-type
-
-
-
-attribute_list
-
-
-
-+
+
+
+statement
-
-
-unary_expression
+
+
+literal
-
-
-identifier
+
+
+argument_list
-
-
+
+
type
-
-
-)
-
-
-
+
+
identifier
-
-
-namespace_name
+
+
+enum_modifier
-
-
-local_variable_declaration
+
+
+constant_expression
-
-
+
+
method_body
-
-
-class_type
-
-
-
-identifier
-
-
-
-return_type
-
-
-
-invocation_expression
-
-
-
-}
+
+
+method_modifiers
-
-
-method_declaration
+
+
+;
P
-
-
-method_header
-
-
-
-attribute_target_specifier
+
+
+explicitly_typed_local_variable_declarators
-
-
-Wait
+
+
+unary_expression
-
-
-primary_expression
+
+
+.
-
-
-;
+
+
+class_member_declaration
-
-
-expression
+
+
+block
-
-
-literal
+
+
+public
-
-
-;
+
+
+member_access
-
-
-Collections
+
+
+identifier
-
-
-literal
+
+
+local_variable_declaration
-
-
-explicitly_typed_local_variable_declarators
+
+
+type
-
-
-return_type
+
+
+identifier
-
-
-delegate
+
+
+invocation_expression
+
+
+
+assignment
+
+
+
+member_name
+
+
+
+identifier
+
+
+
+argument
+
+
+
+identifier
+
+
+
+C
+
+
+
+,
+
+
+
+.
+
+
+
+ref_method_modifier
+
+
+
+class_type
+
+
+
+Список
+
+
+
+identifier
parameter_list
-
-
-expression_statement
+
+
+explicitly_typed_local_variable_declarators
-
-
-System
+
+
+method_body
-
-
-)
+
+
+attribute_target
-
-
+
+
+fixed_parameter
+
+
+
+literal
+
+
+
+await_expression
+
+
+
+class_member_declaration
+
+
+
block
-
-
-identifier
+
+
+0
-
-
-identifier
+
+
+multiplicative_expression
-
-
-class_modifier
+
+
+public
-
-
-method_modifiers
+
+
+in
-
-
-)
+
+
+A
-
-
-.
+
+
+Tasks
-
-
-<
+
+
+statement
-
-
-pre_increment_expression
+
+
+local_variable_initializer
-
-
-)
+
+
+namespace_member_declaration
-
-
-boolean_expression
+
+
+argument
-
-
-block
+
+
+fixed_parameters
-
-
+
+
+=>
+
+
+
+static
+
+
+
+statement_list
+
+
+
+identifier
+
+
+
primary_expression
-
-
-type
+
+
+int
-
-
-foreach_statement
+
+
+;
-
-
-method_declaration
+
+
+argument
-
-
-method_declaration
+
+
+delegate_modifier
-
-
-return_statement
+
+
+method_modifiers
-
-
-invocation_expression
+
+
+block
-
-
-invocation_expression
+
+
+namespace_declaration
-
-
-}
+
+
+statement_expression
-
-
-using_directive
+
+
+identifier
-
-
-primary_expression
+
+
+local_variable_initializer
-
-
-namespace_body
+
+
+identifier
+
+
+
+declaration_statement
+
+
+
+++
+
+
+
+multiplicative_expression
+
+
+
+await
+
+
+
+(
+
+
+
+statement_list
+
+
+
+expression
+
+
+
+,
method_modifier
-
-
-Main
+
+
+attribute_section
-
-
-class_member_declaration
+
+
+qualified_identifier
-
-
-(
+
+
+++
-
-
-identifier
+
+
+)
-
-
-identifier
+
+
+namespace_body
-
-
-additive_expression
+
+
+pre_increment_expression
-
-
-block
+
+
+.
-
-
-parenthesized_expression
+
+
+Delegate
-
-
-)
+
+
+delegate
-
-
-method_header
+
+
+identifier
+
+
+
+qualified_identifier
{
-
-
-literal
-
-
-
-embedded_statement
-
-
-
-local_variable_declaration
+
+
+Console
-
-
+
+
identifier
-
-
-statement
+
+
+method_body
-
-
-primary_expression
+
+
+Task
-
-
-.
+
+
+int
-
-
-public
+
+
+class_declaration
-
-
-primary_expression
+
+
+expression_statement
-
-
-primary_expression
+
+
+=
-
-
-local_variable_type
+
+
+class_member_declaration
-
-
-namespace
+
+
+identifier
-
-
-}
+
+
+additive_expression
-
-
-method_modifier
+
+
+(
-
-
-identifier
+
+
+expression_statement
-
-
-embedded_statement
+
+
+(
-
-
+
+
+attribute_target_specifier
+
+
+
ref_method_modifier
-
-
-unary_expression
+
+
+using_namespace_directive
-
-
-enum_member_declaration
+
+
+result
-
-
-)
+
+
+argument_list
-
-
-static
+
+
+member_name
-
-
+
+
type
-
-
-post_increment_expression
-
-
-
-;
+
+
+additive_expression
-
-
-pre_decrement_expression
+
+
+identifier
-
-
-type
+
+
+embedded_statement
-
-
-+
+
+
+expression_statement
-
-
-0
+
+
+invocation_expression
-
-
-method_modifiers
+
+
+method_modifier
-
-
-namespace_or_type_name
+
+
+A
-
-
-,
+
+
+}
-
-
-identifier
+
+
+boolean_expression
-
-
-number
+
+
+result
-
-
-statement_list
+
+
+int
-
-
+
+
identifier
-
-
-statement
-
-
-
-)
+
+
+0
-
-
-member_access
+
+
+{
-
-
-+
+
+
+"{0}·"
-
-
-explicitly_typed_local_variable_declarator
+
+
+additive_expression
-
-
-multiplicative_expression
+
+
+number
-
-
-method_body
+
+
+}
-
-
-fixed_parameter
+
+
+E
-
-
-Delay
+
+
+explicitly_typed_local_variable_declarators
-
-
-identifier
+
+
+{
-
-
-implicitly_typed_local_variable_declarator
+
+
+explicitly_typed_local_variable_declaration
-
-
-enum_member_declarations
+
+
+,
-
-
-assignment
+
+
+return_type
-
-
+
+
primary_expression
-
-
+
+
identifier
-
-
-namespace_body
-
-
-
-block
-
-
-
-method_header
-
-
-
-using_directive
-
-
-
+
+
enum_member_declaration
-
-
-void
+
+
+method_modifier
-
-
-local_variable_initializer
+
+
+shift_expression
-
-
-D
+
+
+expression_statement
-
-
+
+
identifier
-
-
-)
-
-
-
-int
+
+
+exponent
-
-
-statement_expression
+
+
+identifier
-
-
-type
+
+
+2
-
-
+
+
(
-
-
-System
+
+
+primary_expression
-
-
-namespace_declaration
+
+
+identifier
-
-
+
+
integral_type
-
-
-declaration_statement
+
+
+identifier
-
-
-primary_expression
+
+
+8
-
-
-local_variable_declaration
+
+
+pre_decrement_expression
-
-
-explicitly_typed_local_variable_declarators
+
+
+2
-
-
-argument
+
+
+anonymous_function_body
-
-
+
+
+,
+
+
+
(
-
-
-argument
+
+
+statement_list
-
-
-using_namespace_directive
+
+
+yield_statement
-
-
-integral_type
+
+
+expression
-
-
-exponent
+
+
+"http://example.com"
-
-
-unary_expression
+
+
+statement_list
-
-
-result
+
+
+additive_expression
-
-
-primary_expression
+
+
+counter
-
-
-(
+
+
+relational_expression
-
-
-IEnumerable
+
+
+)
-
-
-Console
+
+
+0
-
-
-}
+
+
+{
-
-
-identifier
+
+
+explicit_anonymous_function_signature
-
-
-number
+
+
+identifier
-
-
-A
+
+
+(
-
-
-}
+
+
+identifier
-
-
-)
+
+
+await
-
-
-;
+
+
+type
-
-
-foreach
+
+
+declaration_statement
-
-
-explicitly_typed_local_variable_declaration
+
+
+type
-
-
-{
+
+
+++
-
-
-identifier
+
+
+number
-
-
-expression_statement
+
+
+using_directive
-
-
+
+
primary_expression
-
-
-A
+
+
+unary_expression
-
-
-)
+
+
+method_declaration
-
-
+
+
identifier
-
-
-expression_statement
+
+
+return_type
+
+
+
+member_name
+
+
+
+,
+
+
+
+;
+
+
+
+(
-
-
-class
+
+
+member_name
-
-
-new
+
+
+;
-
-
-(
+
+
+literal
-
-
-result
+
+
+void
-
-
+
+
identifier
-
-
-(
+
+
+;
-
-
+
+
+argument_list
+
+
+
identifier
-
-
-literal
+
+
+invocation_expression
{
-
-
-integral_type
-
-
-
-int
+
+
+{
-
-
-namespace_member_declaration
+
+
+.
-
-
-i
+
+
+invocation_expression
-
-
-expression
+
+
+multiplicative_expression
-
-
-)
+
+
+Main
-
-
-literal
+
+
+anonymous_function_signature
-
-
+
+
identifier
-
-
-compilation_unit
+
+
+(
-
-
-=
+
+
+await_expression
-
-
-statement
+
+
+(
-
-
-integral_type
+
+
+=
-
-
-unary_expression
+
+
+expression
-
-
-[
+
+
+method_header
-
-
-counter
+
+
+statement_expression
-
-
-2
+
+
+compilation_unit
-
-
-.
+
+
+namespace_member_declaration
-
-
-enum_modifier
+
+
+primary_expression
-
-
-)
+
+
+namespace_name
-
-
-0
+
+
+identifier
-
-
-StartNew
+
+
+range_expression
-
-
-(
+
+
+using
-
-
-enum_declaration
+
+
+)
-
-
-explicitly_typed_local_variable_declarators
+
+
+while_statement
-
-
-literal
+
+
+DownloadStringTaskAsync
-
-
-multiplicative_expression
+
+
+=
-
-
-class_member_declaration
+
+
+block
-
-
-}
+
+
+System
-
-
-method_body
+
+
+)
-
-
---
+
+
+)
-
-
-method_declaration
+
+
+(
-
-
+
+
=
-
-
-,
-
-
-
-member_access
-
-
-
-.
-
-
-
-argument_list
-
-
-
-post_increment_expression
+
+
+return_statement
-
-
-async
+
+
+literal
-
-
-class_member_declaration
+
+
+embedded_statement
-
-
+
+
return_type
-
-
-identifier
+
+
+method_declaration
-
-
-Tasks
+
+
+primary_expression
-
-
-Task
+
+
+IEnumerable
-
-
-Threading
+
+
+local_variable_declaration
-
-
-enum
+
+
+additive_expression
-
-
-explicitly_typed_local_variable_declaration
+
+
+{
-
-
-enum_member_declaration
+
+
+namespace
-
-
-)
+
+
+literal
-
-
+
+
identifier
-
-
-{
-
-
-
-invocation_expression
-
-
-
-statement_list
+
+
+local_variable_declaration
-
-
-statement_expression
+
+
+int
-
-
-local_variable_initializer
+
+
+primary_expression
-
-
-exponent
+
+
+A
-
-
-public
+
+
+--
-
-
-member_access
+
+
+attributes
-
-
-multiplicative_expression
+
+
+namespace_or_type_name
-
-
-qualified_identifier
+
+
+result
-
-
-invocation_expression
+
+
+explicitly_typed_local_variable_declaration
-
-
-fixed_parameters
+
+
+primary_expression
-
-
-var
+
+
+statement
-
-
-literal
+