Skip to content

Commit 954d3c3

Browse files
RexJaeschkeBillWagner
authored andcommitted
add support for indexers and ranges
1 parent ed7f980 commit 954d3c3

File tree

1 file changed

+75
-9
lines changed

1 file changed

+75
-9
lines changed

standard/expressions.md

Lines changed: 75 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,8 @@ The precedence of an operator is established by the definition of its associated
144144
> | **Subclause** | **Category** | **Operators** |
145145
> | ----------------- | ------------------------------- | -------------------------------------------------------|
146146
> | [§11.7](expressions.md#117-primary-expressions) | Primary | `x.y` `x?.y` `f(x)` `a[x]` `a?[x]` `x++` `x--` `new` `typeof` `default` `checked` `unchecked` `delegate` |
147-
> | [§11.8](expressions.md#118-unary-operators) | Unary | `+` `-` `!` `~` `++x` `--x` `(T)x` `await x` |
147+
> | [§11.8](expressions.md#118-unary-operators) | Unary | `+` `-` `!` `~` `^` `++x` `--x` `(T)x` `await x` |
148+
> | §range-operator | Range | `..`
148149
> | [§11.9](expressions.md#119-arithmetic-operators) | Multiplicative | `*` `/` `%` |
149150
> | [§11.9](expressions.md#119-arithmetic-operators) | Additive | `+` `-` |
150151
> | [§11.10](expressions.md#1110-shift-operators) | Shift | `<<` `>>` |
@@ -342,8 +343,8 @@ In both of the above cases, a cast expression can be used to explicitly convert
342343
343344
***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:
344345
345-
- For the unary operators `+`, `++`, `-`, `--`, `!`, 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.
346-
- 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 in11.12.5](expressions.md#11125-nullable-boolean--and--operators)). Otherwise, the lifted operator unwraps the operands, applies the underlying operator, and wraps the result.
346+
- For the unary operators `+`, `++`, `-`, `--`, `!`, `~`, 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.
347+
- 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 in11.12.5](expressions.md#11125-nullable-boolean--and--operators)). Otherwise, the lifted operator unwraps the operands, applies the underlying operator, and wraps the result.
347348
- 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.
348349
- 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.
349350
@@ -1895,17 +1896,78 @@ If the *primary_no_array_creation_expression* of an *element_access* is a value
18951896

18961897
#### 11.7.10.2 Array access
18971898

1898-
For an array access, the *primary_no_array_creation_expression* of the *element_access* shall be a value of an *array_type*. Furthermore, the *argument_list* of an array access is not allowed to 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.
1899+
For an array access, the *primary_no_array_creation_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`, `ulong`, `System.Index`, or `System.Range`, or shall be implicitly convertible to one or more of the types `int`, `uint`, `long`, or `ulong`. However, it is a compile-time error for an *argument_list* expression of type `System.Index` or `System.Range` to be used for any dimension of a multi-dimensional array.
18991900

1900-
The result of evaluating an array access is a variable of the element type of the array, namely the array element selected by the value(s) of the expression(s) in the *argument_list*.
1901+
The result of evaluating an array access that does not involve `System.Range` is a variable of the element type of the array, namely the array element selected by the value(s) of the expression(s) in the *argument_list*.
1902+
1903+
The result of evaluating an array access that involves `System.Range` is a slice of the array being accessed, as selected by the value(s) of the expression(s) in the *argument_list*. The resulting slice’s element type is the same as the element type of the array being accessed.
19011904

19021905
The run-time processing of an array access of the form `P[A]`, where `P` is a *primary_no_array_creation_expression* of an *array_type* and `A` is an *argument_list*, consists of the following steps:
19031906

19041907
- `P` is evaluated. If this evaluation causes an exception, no further steps are executed.
1905-
- 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.
1906-
- 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.
1907-
- 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.
1908-
- The location of the array element given by the index expression(s) is computed, and this location becomes the result of the array access.
1908+
- The index expressions of the *argument_list* are evaluated in order, from left to right. Following evaluation of each index expression, for expressions not having type `System.Index` or `System.Range`, 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. For an index expression having type `System.Index`, the Index is transformed into the corresponding `int` index using `System.Index.GetOffset`.
1909+
1910+
- For an index expression not having type `System.Range`:
1911+
- If evaluation of an index expression, the subsequent implicit conversion, or Index transformation causes an exception, then no further index expressions are evaluated, and no further steps are executed.
1912+
- 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.
1913+
- 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.
1914+
- The location of the array element given by the index expression(s) is computed, and this location becomes the result of the array access.
1915+
- Else, for an index expression having type `System.Range`:
1916+
- 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.
1917+
- The Range is transformed into the corresponding slice using `System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray<T>`. If this transformation causes an exception, no further steps are executed.
1918+
- The result of the transformation becomes the result of the array access.
1919+
1920+
> *Example*: Given the following one-dimensional array and Index:
1921+
> ```csharp
1922+
> string[] words = new string[] { "red", "green", "blue" };
1923+
> Index idx = 1;
1924+
> ```
1925+
> `words[idx]` is transformed by the implementation to
1926+
> ```csharp
1927+
> words[idx.GetOffset(words.Length)]
1928+
> ```
1929+
> which results in `"green"`. Similarly, `words[^0]` is transformed by the implementation to `words[(^0).GetOffset(words.Length)]`, which results in `System.IndexOutOfRangeException`. *end example*
1930+
1931+
> *Example*: Given the following jagged array and Indexes:
1932+
> ```csharp
1933+
> int[][] values = new int[][] { … };
1934+
> Index idx1 = 1;
1935+
> Index idx2 = ^1;
1936+
> ```
1937+
> ` values[idx1][idx2]` is transformed by the implementation to
1938+
> ```csharp
1939+
> values[idx1.GetOffset(values.Length)][idx2.GetOffset(values[idx1].Length)]
1940+
> ```
1941+
> *end example*
1942+
1943+
> *Example*: Given the following multidimensional array and Indexes:
1944+
> ```csharp
1945+
> int[,] values2D = { … };
1946+
> Index idx3 = 1;
1947+
> Index idx4 = ^1;
1948+
> ```
1949+
> as `Index`-typed subscripts are not supported in this context, what one might like to express simply as `values2D[idx3, idx4]` must instead be rewritten explicitly by the programmer as
1950+
> ```csharp
1951+
> values2D[idx3.GetOffset(values2D.GetUpperBound(0) + 1), idx4.GetOffset(values2D.GetUpperBound(1) + 1)]
1952+
> ```
1953+
> *end example*
1954+
1955+
> *Example*: Given the following one-dimensional array:
1956+
> ```csharp
1957+
> string[] seasons = new string[] { "Summer", "Autumn", "Winter", "Spring" };
1958+
> ```
1959+
> `seasons[0..2]` is transformed by the implementation to
1960+
> ```csharp
1961+
> System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray<string>(seasons, 0..2)
1962+
> ```
1963+
> which returns the `string[]` slice containing `"Summer"` and `"Autumn"`. *end example*
1964+
1965+
> *Example*: Given the following jagged array:
1966+
> ```csharp
1967+
> int[][] values = new int[][] { new int[] { 10, 9, 5 },
1968+
> new int[] { 6, 12, 17, 32 }, new int[] { 28, 42 } };
1969+
> ```
1970+
all the Range and Index expressions in `values[1..3][^1][..2][^1]` are transformed by the implementation, resulting in `values[2][1]`, which is 42. *end example*
19091971
19101972
#### 11.7.10.3 Indexer access
19111973
@@ -1924,6 +1986,10 @@ The binding-time processing of an indexer access of the form `P[A]`, where `P` i
19241986
19251987
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 ([§11.19.2](expressions.md#11192-simple-assignment)). In all other cases, the *get_accessor* is invoked to obtain the current value ([§11.2.2](expressions.md#1122-values-of-expressions)).
19261988
1989+
> *Note*: An implementation is required to provide an instance indexer member with a single parameter of type `Index` for any type that meets the criteria specified in §indexable-sequence-impl-support-for-index. *end note*
1990+
1991+
> *Note*: An implementation is required to provide an instance indexer member with a single parameter of type `Range` for any type that meets the criteria specified in §indexable-sequence-impl-support-for-range. *end note*
1992+
19271993
### 11.7.11 Null Conditional Element Access
19281994
19291995
A *null_conditional_element_access* consists of a *primary_no_array_creation_expression* followed by the two tokens “`?`” and “`[`”, followed by an *argument_list*, followed by a “`]`” token, followed by zero or more *dependent_access*es.

0 commit comments

Comments
 (0)