From 369ca4f54c7aa8b24d91fbb2912b5fa49f8e8f2c Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Sun, 7 Aug 2022 05:18:16 -0400 Subject: [PATCH 01/32] Add range operator --- standard/lexical-structure.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/lexical-structure.md b/standard/lexical-structure.md index 6f718ff1f..acfb0ae86 100644 --- a/standard/lexical-structure.md +++ b/standard/lexical-structure.md @@ -1003,7 +1003,7 @@ operator_or_punctuator | '+' | '-' | ASTERISK | SLASH | '%' | '&' | '|' | '^' | '!' | '~' | '=' | '<' | '>' | '?' | '??' | '::' | '++' | '--' | '&&' | '||' | '->' | '==' | '!=' | '<=' | '>=' | '+=' | '-=' | '*=' | '/=' | '%=' - | '&=' | '|=' | '^=' | '<<' | '<<=' | '=>' + | '&=' | '|=' | '^=' | '<<' | '<<=' | '=>' | '..' ; right_shift From d77d14724c44bb870d4246f251098635365f2c6d Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Sun, 7 Aug 2022 06:08:50 -0400 Subject: [PATCH 02/32] add support for indexers and ranges --- standard/expressions.md | 102 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 93 insertions(+), 9 deletions(-) diff --git a/standard/expressions.md b/standard/expressions.md index d2c0df118..4fd314410 100644 --- a/standard/expressions.md +++ b/standard/expressions.md @@ -147,8 +147,9 @@ The precedence of an operator is established by the definition of its associated > > | **Subclause** | **Category** | **Operators** | > | ----------------- | ------------------------------- | -------------------------------------------------------| +<<<<<<< HEAD > | [§12.8](expressions.md#128-primary-expressions) | Primary | `x.y` `x?.y` `f(x)` `a[x]` `a?[x]` `x++` `x--` `new` `typeof` `default` `checked` `unchecked` `delegate` `stackalloc` | -> | [§12.9](expressions.md#129-unary-operators) | Unary | `+` `-` `!` `~` `++x` `--x` `(T)x` `await x` | +> | [§12.9](expressions.md#129-unary-operators) | Unary | `+` `-` `!` `~` `^` `++x` `--x` `(T)x` `await x` | > | [§12.10](expressions.md#1210-arithmetic-operators) | Multiplicative | `*` `/` `%` | > | [§12.10](expressions.md#1210-arithmetic-operators) | Additive | `+` `-` | > | [§12.11](expressions.md#1211-shift-operators) | Shift | `<<` `>>` | @@ -162,6 +163,24 @@ The precedence of an operator is established by the definition of its associated > | [§12.15](expressions.md#1215-the-null-coalescing-operator) and [§12.16](expressions.md#1216-the-throw-expression-operator) | Null coalescing and throw expression | `??` `throw x` | > | [§12.18](expressions.md#1218-conditional-operator) | Conditional | `?:` | > | [§12.21](expressions.md#1221-assignment-operators) and [§12.19](expressions.md#1219-anonymous-function-expressions) | Assignment and lambda expression | `=` `= ref` `*=` `/=` `%=` `+=` `-=` `<<=` `>>=` `&=` `^=` `\|=` `=>` | +======= +> | [§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` | +> | [§11.8](expressions.md#118-unary-operators) | Unary | `+` `-` `!` `~` `^` `++x` `--x` `(T)x` `await x` | +> | §range-operator | Range | `..` +> | [§11.9](expressions.md#119-arithmetic-operators) | Multiplicative | `*` `/` `%` | +> | [§11.9](expressions.md#119-arithmetic-operators) | Additive | `+` `-` | +> | [§11.10](expressions.md#1110-shift-operators) | Shift | `<<` `>>` | +> | [§11.11](expressions.md#1111-relational-and-type-testing-operators) | Relational and type-testing | `<` `>` `<=` `>=` `is` `as` | +> | [§11.11](expressions.md#1111-relational-and-type-testing-operators) | Equality | `==` `!=` | +> | [§11.12](expressions.md#1112-logical-operators) | Logical AND | `&` | +> | [§11.12](expressions.md#1112-logical-operators) | Logical XOR | `^` | +> | [§11.12](expressions.md#1112-logical-operators) | Logical OR | `\|` | +> | [§11.13](expressions.md#1113-conditional-logical-operators) | Conditional AND | `&&` | +> | [§11.13](expressions.md#1113-conditional-logical-operators) | Conditional OR | `\|\|` | +> | [§11.14](expressions.md#1114-the-null-coalescing-operator) and [§11.15](expressions.md#1115-the-throw-expression-operator) | Null coalescing and throw expression | `??` `throw x` | +> | [§11.16](expressions.md#1116-conditional-operator) | Conditional | `?:` | +> | [§11.19](expressions.md#1119-assignment-operators) and [§11.17](expressions.md#1117-anonymous-function-expressions) | Assignment and lambda expression | `=` `*=` `/=` `%=` `+=` `-=` `<<=` `>>=` `&=` `^=` `\|=` `=>` | +>>>>>>> 954d3c3 (add support for indexers and ranges) > > *end note* @@ -346,8 +365,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 `+`, `++`, `-`, `--`, `!`, 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 `+`, `++`, `-`, `--`, `!`, `~`, 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. @@ -2083,17 +2102,78 @@ If the *primary_no_array_creation_expression* of an *element_access* is a value #### 12.8.11.2 Array access -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. +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. -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*. +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*. + +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. 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: - `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 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 expression(s) is computed, and this location becomes the result of the array access. +- 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`. + +- For an index expression not having type `System.Range`: + - 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. + - 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 expression(s) is computed, and this location becomes the result of the array access. +- Else, for an index expression having type `System.Range`: + - 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 Range is transformed into the corresponding slice using `System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray`. If this transformation causes an exception, no further steps are executed. + - The result of the transformation becomes the result of the array access. + +> *Example*: Given the following one-dimensional array and Index: +> ```csharp +> string[] words = new string[] { "red", "green", "blue" }; +> Index idx = 1; +> ``` +> `words[idx]` is transformed by the implementation to +> ```csharp +> words[idx.GetOffset(words.Length)] +> ``` +> 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* + +> *Example*: Given the following jagged array and Indexes: +> ```csharp +> int[][] values = new int[][] { … }; +> Index idx1 = 1; +> Index idx2 = ^1; +> ``` +> ` values[idx1][idx2]` is transformed by the implementation to +> ```csharp +> values[idx1.GetOffset(values.Length)][idx2.GetOffset(values[idx1].Length)] +> ``` +> *end example* + +> *Example*: Given the following multidimensional array and Indexes: +> ```csharp +> int[,] values2D = { … }; +> Index idx3 = 1; +> Index idx4 = ^1; +> ``` +> 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 +> ```csharp +> values2D[idx3.GetOffset(values2D.GetUpperBound(0) + 1), idx4.GetOffset(values2D.GetUpperBound(1) + 1)] +> ``` +> *end example* + +> *Example*: Given the following one-dimensional array: +> ```csharp +> string[] seasons = new string[] { "Summer", "Autumn", "Winter", "Spring" }; +> ``` +> `seasons[0..2]` is transformed by the implementation to +> ```csharp +> System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray(seasons, 0..2) +> ``` +> which returns the `string[]` slice containing `"Summer"` and `"Autumn"`. *end example* + +> *Example*: Given the following jagged array: +> ```csharp +> int[][] values = new int[][] { new int[] { 10, 9, 5 }, +> new int[] { 6, 12, 17, 32 }, new int[] { 28, 42 } }; +> ``` +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* #### 12.8.11.3 Indexer access @@ -2112,6 +2192,10 @@ The binding-time processing of an indexer access of the form `P[A]`, where `P` i 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)). +> *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* + +> *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* + ### 12.8.12 Null Conditional Element Access 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. From 1f97b8e1e071a49b7257fbea4cda940220839313 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Sun, 7 Aug 2022 09:45:02 -0400 Subject: [PATCH 03/32] add indexers and ranges --- standard/classes.md | 127 ++++++++++++++++++++++++++++++++++++++++ standard/expressions.md | 19 ------ 2 files changed, 127 insertions(+), 19 deletions(-) diff --git a/standard/classes.md b/standard/classes.md index 6d81a34ae..3241a3930 100644 --- a/standard/classes.md +++ b/standard/classes.md @@ -3182,6 +3182,8 @@ In a *ref_property_body* an expression body consisting of `=>` followed by `ref` When a property declaration includes an `extern` modifier, the property is said to be an ***external property***. Because an external property declaration provides no actual implementation, each of its *accessor_declarations* consists of a semicolon. +A type is ***countable*** if it has a property named `Length` or `Count` with an accessible `get` accessor ([§14.7.3]( classes.md#1473-accessors)) and a return type of `int`. + ### 15.7.2 Static and instance properties When a property declaration includes a `static` modifier, the property is said to be a ***static property***. When no `static` modifier is present, the property is said to be an ***instance property***. @@ -5416,3 +5418,128 @@ When the body of the async function terminates, the return task is moved out of If the return type of the async function is `void`, evaluation differs from the above in the following way: Because no task is returned, the function instead communicates completion and exceptions to the current thread’s ***synchronization context***. The exact definition of synchronization context is implementation-dependent, but is a representation of “where” the current thread is running. The synchronization context is notified when evaluation of a `void`-returning async function commences, completes successfully, or causes an uncaught exception to be thrown. This allows the context to keep track of how many `void`-returning async functions are running under it, and to decide how to propagate exceptions coming out of them. + +## §indexable-sequence Indexable sequences + +### §indexable-sequence-general General + +> Note to TG2 reviewers: Rationale for the choice of the name of the term *indexable sequence*: Various MS-hosted on-line pages use the term *sequence*. This word is already used quite a bit in the C# spec, in both a general sense as well as being defined in the context of query expressions. [§11.17 Query expressions|§11.17.1 General states: A query expression begins with a `from` clause and ends with either a `select` or `group` clause. The initial `from` clause may be followed by zero or more `from`, `let`, `where`, `join` or `orderby` clauses. Each `from` clause is a generator introducing a ***range variable*** that ranges over the elements of a ***sequence***. Each `let` clause introduces a range variable representing a value computed by means of previous range variables. …]. That definition is *not* applicable to indexes and ranges! The MS-provided proposal uses *collection*; however, that implies enumerable support, which is *not* required by indexes and ranges. (BTW, although it is used a lot in the C# spec, the term *collection* is *not* defined!) As such, rather than overload an existing term or invent a completely different one, I came up with *indexable sequence*. + +An ***indexable sequence*** is an ordered set of zero or more elements having the same type. Any given element can be accessed via an index, and a contiguous subset of elements—referred to as a ***slice***—can be denoted via a range. + +An index is represented by a read-only variable of the value type `System.Index`. A range is represented by a read-only variable of value type `System.Range`, which contains a start and end index. A slice of an array is represented by a (possibly empty) array. The representation of a slice of a user-defined type is determined by the implementer of that type. + +For an indexable sequence of length *N*, elements can be accessed using indexes 0 through *N-1*, which are relative to the start. Elements can also be accessed relative to the end via the `^` index-from-end operator (§index-from-end-operator). `^0` denotes the (non-existent) element just beyond the end. + +A slice can be obtained using the `..` range operator (§range-operator). A range of the form `s..e` starts at element `s` and ends with the element immediately prior to element `e`. + +All single-dimensional and jagged arrays ([§16.1](arrays.md#161-general)) are indexable sequences; multi-dimensional arrays are not! The use of indexes and ranges with arrays is described in [§11.7.10.2](expressions.md#117102-array-access). + +An object of type `string` is an indexable sequence. + +A user-defined type can provide explicit support for indexer access ([§11.7.10.3](expressions.md#117103-indexer-access)) using `System.Index` and `System.Range`. (See §indexable-sequence-expl-support-for-index and §indexable-sequence-expl-support-for-range.) If various criteria are met, an existing user-defined type that does *not* have such explicit support, shall have provided for it by the implementation implicit support for such indexer and range access. (See §indexable-sequence-impl-support-for-index and §indexable-sequence-impl-support-for-range.) In both cases, the type is recognized as being an indexable sequence type. + +### §indexable-sequence-support-for-index Providing support for Index + +#### §indexable-sequence-expl-support-for-index Explicit Index support + +A type having an instance indexer taking a single argument of type `System.Index`, or a first argument of that type followed by optional arguments, may be indexed as described by [§11.7.10.3](expressions.md#117103-indexer-access). + +> *Example*: In [§14.9](classes.md#149-indexers), there is an example defining type `BitArray`, which stores bits in an array of `int`. Individual bits are accessed for read/write via an `int` indexer. Adding an `Index` indexer that simply interprets the `Index` argument as an `int`, is simple: +> ```csharp +> class BitArray +> { +> … +> public int Length { … } +> public bool this[int index] { … } +> public bool this[Index idx] +> { +> get +> { +> return this[idx.GetOffset(Length)]; // use the [int] indexer +> } +> set +> { +> this[idx.GetOffset(Length)] = value; // use the [int] indexer +> } +> } +> } +> ``` +> *end example* + +#### §indexable-sequence-impl-support-for-index Implicit Index support + +An implementation shall behave as if it provides an instance indexer member with a single parameter of type `System.Index` for any type that meets the following criteria: +- The type is countable [§14.7.1](classes.md#1471-general). +- The type has an accessible instance indexer taking an argument of type `int` as its only argument, or as its first argument with the remaining arguments being optional. +- The type does not have an accessible instance indexer taking a `System.Index` as its only argument, or as its first argument with the remaining arguments being optional. + +The provided instance indexer shall have the same get and set members with matching accessibility as the `int` indexer. + +The provided instance indexer shall take the given `System.Index` and use that to call the instance indexer taking an `int`. If both the `Length` and `Count` properties exist and are accessible, `Length` is used. + +> *Note*: See §indexable-sequence-expl-support-for-index for an example of an explicitly provided `Index` indexer. If that were not defined, its equivalent would be provided by the implementation. *end note* + +### §indexable-sequence-support-for-range Providing support for Range + +#### §indexable-sequence-expl-support-for-range Explicit Range support + +A type having an instance indexer taking a single argument of type `System.Range`, or a first argument of that type followed by optional arguments, may be indexed as described by [§11.7.10.3](expressions.md#117103-indexer-access). + +> *Example*: In [§14.9](classes.md#149-indexers), there is an example defining type `BitArray`, which stores bits in an array of `int`. Adding a `Range` indexer that returns a `BitArray` representing the bit slice designated by the Range, is simple: +> ```csharp +> class BitArray +> { +> … +> public BitArray(int length) { … } +> public int Length { … } +> public bool this[int index] { … } +> public BitArray this[Range range] // note the return type +> { +> get +> { +> int startIdx = range.Start.GetOffset(Length); +> int endIdx = range.End.GetOffset(Length); +> int rangeLength = endIdx - startIdx; +> BitArray newBitArray = new BitArray(rangeLength); +> for (int i = startIdx; i < endIdx; ++i) +> { +> newBitArray[i - startIdx] = this[i]; +> } +> return newBitArray; +> } +> } +> } +> ``` +> *end example* + +#### §indexable-sequence-impl-support-for-range Implicit Range support + +An implementation shall behave as if it provides an instance indexer member with a single parameter of type `System.Range` for any type that meets the following criteria: +- The type is countable [§14.7.1](classes.md#1471-general). +- The type has an accessible instance method named `Slice` taking two arguments of type `int` as the only arguments. For type `string`, the method `Substring` is used instead of `Slice`. + > *Note*: As specified in [§11.7.10.2](expressions.md#117102-array-access), for array access, the method `System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray` is used instead of `Slice`. *end note* + - The type does not have an accessible instance indexer taking a `System.Range` as its only argument, or as its first argument with the remaining arguments being optional. + +The provided instance indexer shall have the same accessibility and return type, including `ref` if present, as `Slice`. + +When the type is indexed with a `System.Range`, the provided instance indexer shall take the given range and pass its start index and length as `int`s to `Slice` (or in the case of `string`, to method `Substring`). + +> Note to TG2 reviewers: The MS proposal, section “Implicit Range support,” provided a very detailed discussion of how to transform a pair of Indexes into a call to `Slice` depending on the form of the range used. Rex did *not* retain this in the final proposal, as he saw no point in doing so. Given a start and end index, it is a simple matter to compute the length **in all cases regardless of range format!**, as he shows in his range indexer implementation in “Explicit range support” above. + +> *Note*: See §indexable-sequence-expl-support-for-range for an example of an explicitly provided `Range` indexer. If that were not defined, its equivalent would be provided by the implementation, except that the provided indexer would call `Slice` to create and copy the slice. For type `BitArray`, `Slice` might be defined, as follows: +> ```csharp +> public BitArray Slice(int startIdx, int rangeLength) +> { +> int endIdx = startIdx + rangeLength; +> BitArray newBitArray = new BitArray(rangeLength); +> for (int i = startIdx; i < endIdx; ++i) +> { +> newBitArray[i - startIdx] = this[i]; +> } +> return newBitArray; +> } +> ``` +*end note* + +> Note to TG2 reviewers: Setter: What if anything should we say about implicit and explicit setter for a Range indexer? Certainly, one can define a setter for a user-defined type; however, it is not obvious as to what such a setter would do, especially since it must be used on the left-hand side of assignment taking a right-hand side of the same type as the index returns. In the case of type `BitArray` that would mean something like `ba1[range1] = ba2`, or perhaps `ba1[range1] = ba2[range2]`. As far as Rex could determine, the operations one might like to implement using such a setter are probably best implemented via a named method. In any event, for a compiler-generated Range indexer, attempting to use its setter results in the error message “CS0131 The left-hand side of an assignment must be a variable, property or indexer,” which suggests the generated indexer **has no setter**. If that is the case, we should say that in the previous section. diff --git a/standard/expressions.md b/standard/expressions.md index 4fd314410..964a0d8cf 100644 --- a/standard/expressions.md +++ b/standard/expressions.md @@ -147,7 +147,6 @@ The precedence of an operator is established by the definition of its associated > > | **Subclause** | **Category** | **Operators** | > | ----------------- | ------------------------------- | -------------------------------------------------------| -<<<<<<< HEAD > | [§12.8](expressions.md#128-primary-expressions) | Primary | `x.y` `x?.y` `f(x)` `a[x]` `a?[x]` `x++` `x--` `new` `typeof` `default` `checked` `unchecked` `delegate` `stackalloc` | > | [§12.9](expressions.md#129-unary-operators) | Unary | `+` `-` `!` `~` `^` `++x` `--x` `(T)x` `await x` | > | [§12.10](expressions.md#1210-arithmetic-operators) | Multiplicative | `*` `/` `%` | @@ -163,24 +162,6 @@ The precedence of an operator is established by the definition of its associated > | [§12.15](expressions.md#1215-the-null-coalescing-operator) and [§12.16](expressions.md#1216-the-throw-expression-operator) | Null coalescing and throw expression | `??` `throw x` | > | [§12.18](expressions.md#1218-conditional-operator) | Conditional | `?:` | > | [§12.21](expressions.md#1221-assignment-operators) and [§12.19](expressions.md#1219-anonymous-function-expressions) | Assignment and lambda expression | `=` `= ref` `*=` `/=` `%=` `+=` `-=` `<<=` `>>=` `&=` `^=` `\|=` `=>` | -======= -> | [§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` | -> | [§11.8](expressions.md#118-unary-operators) | Unary | `+` `-` `!` `~` `^` `++x` `--x` `(T)x` `await x` | -> | §range-operator | Range | `..` -> | [§11.9](expressions.md#119-arithmetic-operators) | Multiplicative | `*` `/` `%` | -> | [§11.9](expressions.md#119-arithmetic-operators) | Additive | `+` `-` | -> | [§11.10](expressions.md#1110-shift-operators) | Shift | `<<` `>>` | -> | [§11.11](expressions.md#1111-relational-and-type-testing-operators) | Relational and type-testing | `<` `>` `<=` `>=` `is` `as` | -> | [§11.11](expressions.md#1111-relational-and-type-testing-operators) | Equality | `==` `!=` | -> | [§11.12](expressions.md#1112-logical-operators) | Logical AND | `&` | -> | [§11.12](expressions.md#1112-logical-operators) | Logical XOR | `^` | -> | [§11.12](expressions.md#1112-logical-operators) | Logical OR | `\|` | -> | [§11.13](expressions.md#1113-conditional-logical-operators) | Conditional AND | `&&` | -> | [§11.13](expressions.md#1113-conditional-logical-operators) | Conditional OR | `\|\|` | -> | [§11.14](expressions.md#1114-the-null-coalescing-operator) and [§11.15](expressions.md#1115-the-throw-expression-operator) | Null coalescing and throw expression | `??` `throw x` | -> | [§11.16](expressions.md#1116-conditional-operator) | Conditional | `?:` | -> | [§11.19](expressions.md#1119-assignment-operators) and [§11.17](expressions.md#1117-anonymous-function-expressions) | Assignment and lambda expression | `=` `*=` `/=` `%=` `+=` `-=` `<<=` `>>=` `&=` `^=` `\|=` `=>` | ->>>>>>> 954d3c3 (add support for indexers and ranges) > > *end note* From c3047bec7531016fc4dbcb44b6d0f216c1629cf6 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Sun, 7 Aug 2022 09:54:26 -0400 Subject: [PATCH 04/32] add indexers and ranges --- standard/standard-library.md | 41 +++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/standard/standard-library.md b/standard/standard-library.md index 79a6d3c3c..b770ed336 100644 --- a/standard/standard-library.md +++ b/standard/standard-library.md @@ -453,10 +453,44 @@ The following types, including the members listed, must be defined in a conformi A conforming implementation may provide `Task.GetAwaiter()` and `Task.GetAwaiter()` as extension methods. +> Note to TG2 reviewers: Required vs. Optional library type members: We need to indicate which members of `Index` and `Range` are required and which are optional. + ```csharp namespace System { public class FormattableString : IFormattable { } + + public readonly struct Index : IEquatable + { + public Index(int value, bool fromEnd = false); + public static Index End { get; } + public static Index Start { get; } + public bool IsFromEnd { get; } + public int Value { get; } + public static Index FromEnd(int value); + public static Index FromStart(int value); + public bool Equals(Index other); + public override bool Equals(object? value); + public override int GetHashCode(); + public int GetOffset(int length); + public override string ToString(); + public static implicit operator Index(int value); + } + + public struct Range : IEquatable + { + public Range (Index start, Index end); + public static Range All { get; } + public Index End { get; } + public Index Start { get; } + public static Range EndAt (Index end); + public override bool Equals (object? value); + public bool Equals (Range other); + public override int GetHashCode (); + public (int,int) GetOffsetAndLength (int length); + public static Range StartAt (Index start); + public override string ToString (); + } } namespace System.Linq.Expressions @@ -512,7 +546,12 @@ namespace System.Runtime.CompilerServices void OnCompleted(Action continuation); } - public readonly struct TaskAwaiter : ICriticalNotifyCompletion, + public static class RuntimeHelpers + { + public static T[] GetSubArray(T[] array, System.Range range); + } + + public struct TaskAwaiter : ICriticalNotifyCompletion, INotifyCompletion { public bool IsCompleted { get; } From 3813628e641f7fbde689552a3c9fe15be79e5323 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Sun, 7 Aug 2022 10:48:34 -0400 Subject: [PATCH 05/32] fix formatting --- standard/expressions.md | 108 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 102 insertions(+), 6 deletions(-) diff --git a/standard/expressions.md b/standard/expressions.md index 964a0d8cf..065b43def 100644 --- a/standard/expressions.md +++ b/standard/expressions.md @@ -2086,7 +2086,7 @@ If the *primary_no_array_creation_expression* of an *element_access* is a value 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. 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*. - + 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. 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: @@ -2094,7 +2094,7 @@ The run-time processing of an array access of the form `P[A]`, where `P` is a *p - `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, 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`. -- For an index expression not having type `System.Range`: +- For an index expression not having type `System.Range`: - 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. - 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. @@ -2105,38 +2105,48 @@ The run-time processing of an array access of the form `P[A]`, where `P` is a *p - The result of the transformation becomes the result of the array access. > *Example*: Given the following one-dimensional array and Index: +> > ```csharp > string[] words = new string[] { "red", "green", "blue" }; > Index idx = 1; > ``` +> > `words[idx]` is transformed by the implementation to +> > ```csharp > words[idx.GetOffset(words.Length)] > ``` +> > 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* > *Example*: Given the following jagged array and Indexes: +> > ```csharp > int[][] values = new int[][] { … }; > Index idx1 = 1; > Index idx2 = ^1; > ``` -> ` values[idx1][idx2]` is transformed by the implementation to +> +> `values[idx1][idx2]` is transformed by the implementation to > ```csharp > values[idx1.GetOffset(values.Length)][idx2.GetOffset(values[idx1].Length)] > ``` > *end example* > *Example*: Given the following multidimensional array and Indexes: +> > ```csharp > int[,] values2D = { … }; > Index idx3 = 1; > Index idx4 = ^1; > ``` +> > 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 +> > ```csharp > values2D[idx3.GetOffset(values2D.GetUpperBound(0) + 1), idx4.GetOffset(values2D.GetUpperBound(1) + 1)] > ``` +> > *end example* > *Example*: Given the following one-dimensional array: @@ -2144,16 +2154,20 @@ The run-time processing of an array access of the form `P[A]`, where `P` is a *p > string[] seasons = new string[] { "Summer", "Autumn", "Winter", "Spring" }; > ``` > `seasons[0..2]` is transformed by the implementation to +> > ```csharp > System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray(seasons, 0..2) > ``` +> > which returns the `string[]` slice containing `"Summer"` and `"Autumn"`. *end example* > *Example*: Given the following jagged array: +> > ```csharp > int[][] values = new int[][] { new int[] { 10, 9, 5 }, > new int[] { 6, 12, 17, 32 }, new int[] { 28, 42 } }; > ``` +> 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* #### 12.8.11.3 Indexer access @@ -2173,9 +2187,7 @@ The binding-time processing of an indexer access of the form `P[A]`, where `P` i 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)). -> *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* - -> *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* +> *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. 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* ### 12.8.12 Null Conditional Element Access @@ -3487,6 +3499,44 @@ 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. +### §index-from-end-operator Index-from-end operator + +This operator provides a succinct syntax for denoting the position of an element in an indexable sequence (§indexable-sequence) relative to the end of that sequence. + +For an operation of the form `^x`, unary operator overload resolution ([§11.4.4](expressions.md#1144-unary-operator-overload-resolution)) is applied to select a specific operator implementation. The operand is converted to the parameter type of the selected operator, and the type of the result is the return type of the operator. Only one predefined index-from-end operator exists: + +```csharp +System.Index operator ^(int fromEnd); +``` + +For this operator, an object of (the immutable struct) type `System.Index` is returned that denotes element number `fromEnd` from the end of any indexable sequence. `^n` is shorthand for `new System.Index(n, fromEnd: true)`. + +If after implicit conversion to `int` the operand has a negative value, an exception of type `System.ArgumentOutOfRangeException` is thrown. + +Lifted ([§11.4.8](expressions.md#1148-lifted-operators)) forms of the unlifted predefined index-from-end operator defined above are also predefined. + +> *Example*: The following example uses array and string indexable sequences: +> +> ```csharp +> string[] words = new string[] { "red", "green", "blue" }; +> words[^1] // OK: "blue" +> words[^3] // OK: "red" +> words[^0] // refers to the (non-existent) element beyond the end +> +> Index idx = ^0; // OK; no attempt made to access any non-existent element +> int i = -1; +> idx = ^(ushort)i; // OK; 65535 (0x0000FFFF) from the end +> idx = ^(short)i; // System.ArgumentOutOfRangeException: +> +> string s = "Hello!"; +> int? iN = 5; +> Index? idx4 = ^iN; // OK: non-null, ^5 +> s[^idx4.Value.Value] // OK: "e" +> ``` +> +> `^idx4.Value.Value` is the `int` position from the end of the sequence designated by the `Index` wrapped in the `Index?` designated by `idx4`. (`System.Nullable`and `System.Index` both have public read-only properties called `Value`.) +> *end example* + ### 12.9.6 Prefix increment and decrement operators ```ANTLR @@ -3619,6 +3669,52 @@ 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 + +This operator provides a succinct syntax for specifying a (possibly empty) element range suitable for use in denoting a slice of an indexable sequence (§indexable-sequence). + +```ANTLR +range_expression + : unary_expression + | range_expression? '..' range_expression? + ; +``` + +For an operation of the form `s .. e`, binary operator overload resolution ([§11.4.5](expressions.md#1145-binary-operator-overload-resolution)) is applied to select a specific operator implementation. The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator. Only one predefined range operator exists: + +```csharp +System.Range operator ..(System.Index start = 0, System.Index end = ^0); +``` + +The left and right operands denote, respectively, a start and end Index. For this operator, an object of (the immutable struct) type `System.Range` is returned that contains those Indexes. If the left operand is omitted, an Index of `0` is used. If the right operand is omitted, an Index of `^0` is used. As such, + +- `s .. e` is transformed by the implementation to `new System.Range(s, e)`. +- `s ..` is transformed by the implementation to `new System.Range(s, ^0)` (or to `System.Range.StartAt(s)` if that method exists, is accessible, and is declared as follows: `public static Range StartAt (Index start);`). +- `.. e` is transformed by the implementation to `new System.Range(0, e)` (or to `System.Range.EndAt(e)` if that method exists, is accessible, and is declared as follows: `public static Range EndAt (Index end);`). +- `..` is transformed by the implementation to `new System.Range(0, ^0)` (or instead to `System.Range.All` if that property exists, is accessible, and is declared as follows: `public static Range All { get; }`). + +> *Note*: While a Range can be created with a start Index greater than the end Index, any attempt to use that Range to denote a slice from an indexable sequence will result in `System.ArgumentOutOfRangeException`. *end note* + +Lifted ([§11.4.8](expressions.md#1148-lifted-operators)) forms of the unlifted predefined range operator defined above are also predefined. + +> *Example*: The following example uses array and string indexable sequences: +> + > ```csharp +> string[] seasons = new string[] { "Summer", "Autumn", "Winter", "Spring" }; +> seasons[1..3] // string[2] "Autumn", "Winter" +> seasons[^2..^1] // string[1] "Winter" +> seasons[2..] // string[2] "Winter", "Spring" +> seasons[1..1] // string[0] +> +> string s2 = "Hello!"; +> Index? startN = 1; +> Index? endN = ^2; +> Range? r = startN .. endN; +> s2[r.Value] // OK: "ell" +> ``` +> +> *end example* + ## 12.10 Arithmetic operators ### 12.10.1 General From 54c4a558236374b1732f1d80a65f8d8faf8a76c7 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Sun, 7 Aug 2022 10:53:20 -0400 Subject: [PATCH 06/32] fix formatting --- standard/classes.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/standard/classes.md b/standard/classes.md index 3241a3930..7730e3e80 100644 --- a/standard/classes.md +++ b/standard/classes.md @@ -5446,6 +5446,7 @@ A user-defined type can provide explicit support for indexer access ([§11.7.10. A type having an instance indexer taking a single argument of type `System.Index`, or a first argument of that type followed by optional arguments, may be indexed as described by [§11.7.10.3](expressions.md#117103-indexer-access). > *Example*: In [§14.9](classes.md#149-indexers), there is an example defining type `BitArray`, which stores bits in an array of `int`. Individual bits are accessed for read/write via an `int` indexer. Adding an `Index` indexer that simply interprets the `Index` argument as an `int`, is simple: +> > ```csharp > class BitArray > { @@ -5465,14 +5466,15 @@ A type having an instance indexer taking a single argument of type `System.Index > } > } > ``` +> > *end example* #### §indexable-sequence-impl-support-for-index Implicit Index support An implementation shall behave as if it provides an instance indexer member with a single parameter of type `System.Index` for any type that meets the following criteria: -- The type is countable [§14.7.1](classes.md#1471-general). -- The type has an accessible instance indexer taking an argument of type `int` as its only argument, or as its first argument with the remaining arguments being optional. -- The type does not have an accessible instance indexer taking a `System.Index` as its only argument, or as its first argument with the remaining arguments being optional. +- The type is countable [§14.7.1](classes.md#1471-general). +- The type has an accessible instance indexer taking an argument of type `int` as its only argument, or as its first argument with the remaining arguments being optional. +- The type does not have an accessible instance indexer taking a `System.Index` as its only argument, or as its first argument with the remaining arguments being optional. The provided instance indexer shall have the same get and set members with matching accessibility as the `int` indexer. @@ -5487,6 +5489,7 @@ The provided instance indexer shall take the given `System.Index` and use that t A type having an instance indexer taking a single argument of type `System.Range`, or a first argument of that type followed by optional arguments, may be indexed as described by [§11.7.10.3](expressions.md#117103-indexer-access). > *Example*: In [§14.9](classes.md#149-indexers), there is an example defining type `BitArray`, which stores bits in an array of `int`. Adding a `Range` indexer that returns a `BitArray` representing the bit slice designated by the Range, is simple: +> > ```csharp > class BitArray > { @@ -5511,15 +5514,16 @@ A type having an instance indexer taking a single argument of type `System.Range > } > } > ``` +> > *end example* #### §indexable-sequence-impl-support-for-range Implicit Range support An implementation shall behave as if it provides an instance indexer member with a single parameter of type `System.Range` for any type that meets the following criteria: -- The type is countable [§14.7.1](classes.md#1471-general). -- The type has an accessible instance method named `Slice` taking two arguments of type `int` as the only arguments. For type `string`, the method `Substring` is used instead of `Slice`. +- The type is countable [§14.7.1](classes.md#1471-general). +- The type has an accessible instance method named `Slice` taking two arguments of type `int` as the only arguments. For type `string`, the method `Substring` is used instead of `Slice`. > *Note*: As specified in [§11.7.10.2](expressions.md#117102-array-access), for array access, the method `System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray` is used instead of `Slice`. *end note* - - The type does not have an accessible instance indexer taking a `System.Range` as its only argument, or as its first argument with the remaining arguments being optional. + - The type does not have an accessible instance indexer taking a `System.Range` as its only argument, or as its first argument with the remaining arguments being optional. The provided instance indexer shall have the same accessibility and return type, including `ref` if present, as `Slice`. @@ -5528,6 +5532,7 @@ When the type is indexed with a `System.Range`, the provided instance indexer sh > Note to TG2 reviewers: The MS proposal, section “Implicit Range support,” provided a very detailed discussion of how to transform a pair of Indexes into a call to `Slice` depending on the form of the range used. Rex did *not* retain this in the final proposal, as he saw no point in doing so. Given a start and end index, it is a simple matter to compute the length **in all cases regardless of range format!**, as he shows in his range indexer implementation in “Explicit range support” above. > *Note*: See §indexable-sequence-expl-support-for-range for an example of an explicitly provided `Range` indexer. If that were not defined, its equivalent would be provided by the implementation, except that the provided indexer would call `Slice` to create and copy the slice. For type `BitArray`, `Slice` might be defined, as follows: +> > ```csharp > public BitArray Slice(int startIdx, int rangeLength) > { @@ -5540,6 +5545,7 @@ When the type is indexed with a `System.Range`, the provided instance indexer sh > return newBitArray; > } > ``` +> *end note* > Note to TG2 reviewers: Setter: What if anything should we say about implicit and explicit setter for a Range indexer? Certainly, one can define a setter for a user-defined type; however, it is not obvious as to what such a setter would do, especially since it must be used on the left-hand side of assignment taking a right-hand side of the same type as the index returns. In the case of type `BitArray` that would mean something like `ba1[range1] = ba2`, or perhaps `ba1[range1] = ba2[range2]`. As far as Rex could determine, the operations one might like to implement using such a setter are probably best implemented via a named method. In any event, for a compiler-generated Range indexer, attempting to use its setter results in the error message “CS0131 The left-hand side of an assignment must be a variable, property or indexer,” which suggests the generated indexer **has no setter**. If that is the case, we should say that in the previous section. From 56e03227c32389705ae30de99ec8f54c3d77e065 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Sun, 7 Aug 2022 11:04:33 -0400 Subject: [PATCH 07/32] fix formatting --- standard/classes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/classes.md b/standard/classes.md index 7730e3e80..cffb77a61 100644 --- a/standard/classes.md +++ b/standard/classes.md @@ -5423,7 +5423,7 @@ This allows the context to keep track of how many `void`-returning async functio ### §indexable-sequence-general General -> Note to TG2 reviewers: Rationale for the choice of the name of the term *indexable sequence*: Various MS-hosted on-line pages use the term *sequence*. This word is already used quite a bit in the C# spec, in both a general sense as well as being defined in the context of query expressions. [§11.17 Query expressions|§11.17.1 General states: A query expression begins with a `from` clause and ends with either a `select` or `group` clause. The initial `from` clause may be followed by zero or more `from`, `let`, `where`, `join` or `orderby` clauses. Each `from` clause is a generator introducing a ***range variable*** that ranges over the elements of a ***sequence***. Each `let` clause introduces a range variable representing a value computed by means of previous range variables. …]. That definition is *not* applicable to indexes and ranges! The MS-provided proposal uses *collection*; however, that implies enumerable support, which is *not* required by indexes and ranges. (BTW, although it is used a lot in the C# spec, the term *collection* is *not* defined!) As such, rather than overload an existing term or invent a completely different one, I came up with *indexable sequence*. +> Note to TG2 reviewers: Rationale for the choice of the name of the term *indexable sequence*: Various MS-hosted on-line pages use the term *sequence*. This word is already used quite a bit in the C# spec, in both a general sense as well as being defined in the context of query expressions. "§11.17 Query expressions|§11.17.1 General states: A query expression begins with a `from` clause and ends with either a `select` or `group` clause. The initial `from` clause may be followed by zero or more `from`, `let`, `where`, `join` or `orderby` clauses. Each `from` clause is a generator introducing a ***range variable*** that ranges over the elements of a ***sequence***. Each `let` clause introduces a range variable representing a value computed by means of previous range variables. …". That definition is *not* applicable to indexes and ranges! The MS-provided proposal uses *collection*; however, that implies enumerable support, which is *not* required by indexes and ranges. (BTW, although it is used a lot in the C# spec, the term *collection* is *not* defined!) As such, rather than overload an existing term or invent a completely different one, I came up with *indexable sequence*. An ***indexable sequence*** is an ordered set of zero or more elements having the same type. Any given element can be accessed via an index, and a contiguous subset of elements—referred to as a ***slice***—can be denoted via a range. From 1ef3ab2596f846107469a6fe1754a51d7f7cb906 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Sun, 7 Aug 2022 11:14:24 -0400 Subject: [PATCH 08/32] fix formatting --- standard/expressions.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/standard/expressions.md b/standard/expressions.md index 065b43def..ff21df281 100644 --- a/standard/expressions.md +++ b/standard/expressions.md @@ -2128,9 +2128,11 @@ The run-time processing of an array access of the form `P[A]`, where `P` is a *p > ``` > > `values[idx1][idx2]` is transformed by the implementation to +> > ```csharp > values[idx1.GetOffset(values.Length)][idx2.GetOffset(values[idx1].Length)] > ``` +> > *end example* > *Example*: Given the following multidimensional array and Indexes: @@ -2150,9 +2152,11 @@ The run-time processing of an array access of the form `P[A]`, where `P` is a *p > *end example* > *Example*: Given the following one-dimensional array: +> > ```csharp > string[] seasons = new string[] { "Summer", "Autumn", "Winter", "Spring" }; > ``` +> > `seasons[0..2]` is transformed by the implementation to > > ```csharp From c051a5d9b7d68d8d8c864504b6e1a29f0feabb0c Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Sun, 7 Aug 2022 11:17:35 -0400 Subject: [PATCH 09/32] fix formatting --- standard/classes.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/standard/classes.md b/standard/classes.md index cffb77a61..2391cb54a 100644 --- a/standard/classes.md +++ b/standard/classes.md @@ -5472,6 +5472,7 @@ A type having an instance indexer taking a single argument of type `System.Index #### §indexable-sequence-impl-support-for-index Implicit Index support An implementation shall behave as if it provides an instance indexer member with a single parameter of type `System.Index` for any type that meets the following criteria: + - The type is countable [§14.7.1](classes.md#1471-general). - The type has an accessible instance indexer taking an argument of type `int` as its only argument, or as its first argument with the remaining arguments being optional. - The type does not have an accessible instance indexer taking a `System.Index` as its only argument, or as its first argument with the remaining arguments being optional. @@ -5520,10 +5521,11 @@ A type having an instance indexer taking a single argument of type `System.Range #### §indexable-sequence-impl-support-for-range Implicit Range support An implementation shall behave as if it provides an instance indexer member with a single parameter of type `System.Range` for any type that meets the following criteria: + - The type is countable [§14.7.1](classes.md#1471-general). - The type has an accessible instance method named `Slice` taking two arguments of type `int` as the only arguments. For type `string`, the method `Substring` is used instead of `Slice`. - > *Note*: As specified in [§11.7.10.2](expressions.md#117102-array-access), for array access, the method `System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray` is used instead of `Slice`. *end note* - - The type does not have an accessible instance indexer taking a `System.Range` as its only argument, or as its first argument with the remaining arguments being optional. + > *Note*: As specified in [§11.7.10.2](expressions.md#117102-array-access), for array access, the method `System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray` is used instead of `Slice`. *end note* +- The type does not have an accessible instance indexer taking a `System.Range` as its only argument, or as its first argument with the remaining arguments being optional. The provided instance indexer shall have the same accessibility and return type, including `ref` if present, as `Slice`. From 8ba0cfa8d35ade590f626dbe511b2b23457967f9 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Sun, 7 Aug 2022 11:27:23 -0400 Subject: [PATCH 10/32] fix formatting --- standard/expressions.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/standard/expressions.md b/standard/expressions.md index ff21df281..509ad0c45 100644 --- a/standard/expressions.md +++ b/standard/expressions.md @@ -2146,7 +2146,8 @@ The run-time processing of an array access of the form `P[A]`, where `P` is a *p > 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 > > ```csharp -> values2D[idx3.GetOffset(values2D.GetUpperBound(0) + 1), idx4.GetOffset(values2D.GetUpperBound(1) + 1)] +> values2D[idx3.GetOffset(values2D.GetUpperBound(0) + 1), + idx4.GetOffset(values2D.GetUpperBound(1) + 1)] > ``` > > *end example* @@ -2160,7 +2161,8 @@ The run-time processing of an array access of the form `P[A]`, where `P` is a *p > `seasons[0..2]` is transformed by the implementation to > > ```csharp -> System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray(seasons, 0..2) +> System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray( + seasons, 0..2) > ``` > > which returns the `string[]` slice containing `"Summer"` and `"Autumn"`. *end example* @@ -3703,13 +3705,13 @@ Lifted ([§11.4.8](expressions.md#1148-lifted-operators)) forms of the unlifted > *Example*: The following example uses array and string indexable sequences: > - > ```csharp +> ```csharp > string[] seasons = new string[] { "Summer", "Autumn", "Winter", "Spring" }; > seasons[1..3] // string[2] "Autumn", "Winter" > seasons[^2..^1] // string[1] "Winter" > seasons[2..] // string[2] "Winter", "Spring" > seasons[1..1] // string[0] -> +> > string s2 = "Hello!"; > Index? startN = 1; > Index? endN = ^2; From 18319e5d97621e4639a60ec5cafc5980c7a2427b Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Sun, 7 Aug 2022 11:28:40 -0400 Subject: [PATCH 11/32] fix formatting --- standard/classes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/classes.md b/standard/classes.md index 2391cb54a..036c9f817 100644 --- a/standard/classes.md +++ b/standard/classes.md @@ -5423,7 +5423,7 @@ This allows the context to keep track of how many `void`-returning async functio ### §indexable-sequence-general General -> Note to TG2 reviewers: Rationale for the choice of the name of the term *indexable sequence*: Various MS-hosted on-line pages use the term *sequence*. This word is already used quite a bit in the C# spec, in both a general sense as well as being defined in the context of query expressions. "§11.17 Query expressions|§11.17.1 General states: A query expression begins with a `from` clause and ends with either a `select` or `group` clause. The initial `from` clause may be followed by zero or more `from`, `let`, `where`, `join` or `orderby` clauses. Each `from` clause is a generator introducing a ***range variable*** that ranges over the elements of a ***sequence***. Each `let` clause introduces a range variable representing a value computed by means of previous range variables. …". That definition is *not* applicable to indexes and ranges! The MS-provided proposal uses *collection*; however, that implies enumerable support, which is *not* required by indexes and ranges. (BTW, although it is used a lot in the C# spec, the term *collection* is *not* defined!) As such, rather than overload an existing term or invent a completely different one, I came up with *indexable sequence*. +> Note to TG2 reviewers: Rationale for the choice of the name of the term *indexable sequence*: Various MS-hosted on-line pages use the term *sequence*. This word is already used quite a bit in the C# spec, in both a general sense as well as being defined in the context of query expressions. "§11.17 Query expressions|§11.17.1 General states: A query expression begins with a `from` clause and ends with either a `select` or `group` clause. The initial `from` clause may be followed by zero or more `from`, `let`, `where`, `join` or `orderby` clauses. Each `from` clause is a generator introducing a range variable that ranges over the elements of a sequence. Each `let` clause introduces a range variable representing a value computed by means of previous range variables. …". That definition is *not* applicable to indexes and ranges! The MS-provided proposal uses *collection*; however, that implies enumerable support, which is *not* required by indexes and ranges. (BTW, although it is used a lot in the C# spec, the term *collection* is *not* defined!) As such, rather than overload an existing term or invent a completely different one, I came up with *indexable sequence*. An ***indexable sequence*** is an ordered set of zero or more elements having the same type. Any given element can be accessed via an index, and a contiguous subset of elements—referred to as a ***slice***—can be denoted via a range. From 7cb2cd53bc9d7c58a59c55f80abbfd3107202f38 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Sun, 7 Aug 2022 06:27:54 -0400 Subject: [PATCH 12/32] add indexers and ranges --- standard/expressions.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/standard/expressions.md b/standard/expressions.md index 509ad0c45..7a71b99dd 100644 --- a/standard/expressions.md +++ b/standard/expressions.md @@ -3396,7 +3396,7 @@ An *anonymous_method_expression* is one of two ways of defining an anonymous fun ### 12.9.1 General -The `+`, `-`, `!`, `~`, `++`, `--`, cast, and `await` operators are called the unary operators. +The `+`, `-`, `!`, `~`, `^`, `++`, `--`, cast, and `await` operators are called the unary operators. ```ANTLR unary_expression @@ -3405,6 +3405,7 @@ unary_expression | '-' unary_expression | '!' unary_expression | '~' unary_expression + | '^' unary_expression | pre_increment_expression | pre_decrement_expression | cast_expression @@ -3704,7 +3705,6 @@ The left and right operands denote, respectively, a start and end Index. For thi Lifted ([§11.4.8](expressions.md#1148-lifted-operators)) forms of the unlifted predefined range operator defined above are also predefined. > *Example*: The following example uses array and string indexable sequences: -> > ```csharp > string[] seasons = new string[] { "Summer", "Autumn", "Winter", "Spring" }; > seasons[1..3] // string[2] "Autumn", "Winter" @@ -3729,10 +3729,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 From 32336096a12f193cf844b06cabf5d94f386677dd Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Mon, 6 Feb 2023 10:17:44 -0500 Subject: [PATCH 13/32] fix md formatting --- standard/classes.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/standard/classes.md b/standard/classes.md index 036c9f817..67f044723 100644 --- a/standard/classes.md +++ b/standard/classes.md @@ -5423,7 +5423,7 @@ This allows the context to keep track of how many `void`-returning async functio ### §indexable-sequence-general General -> Note to TG2 reviewers: Rationale for the choice of the name of the term *indexable sequence*: Various MS-hosted on-line pages use the term *sequence*. This word is already used quite a bit in the C# spec, in both a general sense as well as being defined in the context of query expressions. "§11.17 Query expressions|§11.17.1 General states: A query expression begins with a `from` clause and ends with either a `select` or `group` clause. The initial `from` clause may be followed by zero or more `from`, `let`, `where`, `join` or `orderby` clauses. Each `from` clause is a generator introducing a range variable that ranges over the elements of a sequence. Each `let` clause introduces a range variable representing a value computed by means of previous range variables. …". That definition is *not* applicable to indexes and ranges! The MS-provided proposal uses *collection*; however, that implies enumerable support, which is *not* required by indexes and ranges. (BTW, although it is used a lot in the C# spec, the term *collection* is *not* defined!) As such, rather than overload an existing term or invent a completely different one, I came up with *indexable sequence*. +> **Note to TG2 reviewers:** Rationale for the choice of the name of the term *indexable sequence*: Various MS-hosted on-line pages use the term *sequence*. This word is already used quite a bit in the C# spec, in both a general sense as well as being defined in the context of query expressions. "§11.17 Query expressions|§11.17.1 General states: A query expression begins with a `from` clause and ends with either a `select` or `group` clause. The initial `from` clause may be followed by zero or more `from`, `let`, `where`, `join` or `orderby` clauses. Each `from` clause is a generator introducing a range variable that ranges over the elements of a sequence. Each `let` clause introduces a range variable representing a value computed by means of previous range variables. …". That definition is *not* applicable to indexes and ranges! The MS-provided proposal uses *collection*; however, that implies enumerable support, which is *not* required by indexes and ranges. (BTW, although it is used a lot in the C# spec, the term *collection* is *not* defined!) As such, rather than overload an existing term or invent a completely different one, I came up with *indexable sequence*. An ***indexable sequence*** is an ordered set of zero or more elements having the same type. Any given element can be accessed via an index, and a contiguous subset of elements—referred to as a ***slice***—can be denoted via a range. @@ -5531,7 +5531,7 @@ The provided instance indexer shall have the same accessibility and return type, When the type is indexed with a `System.Range`, the provided instance indexer shall take the given range and pass its start index and length as `int`s to `Slice` (or in the case of `string`, to method `Substring`). -> Note to TG2 reviewers: The MS proposal, section “Implicit Range support,” provided a very detailed discussion of how to transform a pair of Indexes into a call to `Slice` depending on the form of the range used. Rex did *not* retain this in the final proposal, as he saw no point in doing so. Given a start and end index, it is a simple matter to compute the length **in all cases regardless of range format!**, as he shows in his range indexer implementation in “Explicit range support” above. +> **Note to TG2 reviewers:** The MS proposal, section “Implicit Range support,” provided a very detailed discussion of how to transform a pair of Indexes into a call to `Slice` depending on the form of the range used. Rex did *not* retain this in the final proposal, as he saw no point in doing so. Given a start and end index, it is a simple matter to compute the length **in all cases regardless of range format!**, as he shows in his range indexer implementation in “Explicit range support” above. > *Note*: See §indexable-sequence-expl-support-for-range for an example of an explicitly provided `Range` indexer. If that were not defined, its equivalent would be provided by the implementation, except that the provided indexer would call `Slice` to create and copy the slice. For type `BitArray`, `Slice` might be defined, as follows: > @@ -5548,6 +5548,6 @@ When the type is indexed with a `System.Range`, the provided instance indexer sh > } > ``` > -*end note* +> *end note* -> Note to TG2 reviewers: Setter: What if anything should we say about implicit and explicit setter for a Range indexer? Certainly, one can define a setter for a user-defined type; however, it is not obvious as to what such a setter would do, especially since it must be used on the left-hand side of assignment taking a right-hand side of the same type as the index returns. In the case of type `BitArray` that would mean something like `ba1[range1] = ba2`, or perhaps `ba1[range1] = ba2[range2]`. As far as Rex could determine, the operations one might like to implement using such a setter are probably best implemented via a named method. In any event, for a compiler-generated Range indexer, attempting to use its setter results in the error message “CS0131 The left-hand side of an assignment must be a variable, property or indexer,” which suggests the generated indexer **has no setter**. If that is the case, we should say that in the previous section. +> **Note to TG2 reviewers:** Setter: What if anything should we say about implicit and explicit setter for a Range indexer? Certainly, one can define a setter for a user-defined type; however, it is not obvious as to what such a setter would do, especially since it must be used on the left-hand side of assignment taking a right-hand side of the same type as the index returns. In the case of type `BitArray` that would mean something like `ba1[range1] = ba2`, or perhaps `ba1[range1] = ba2[range2]`. As far as Rex could determine, the operations one might like to implement using such a setter are probably best implemented via a named method. In any event, for a compiler-generated Range indexer, attempting to use its setter results in the error message “CS0131 The left-hand side of an assignment must be a variable, property or indexer,” which suggests the generated indexer **has no setter**. If that is the case, we should say that in the previous section. From d699f4fb05d1e923a434ad2b540bf09ddb0b09f6 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Mon, 6 Feb 2023 10:21:46 -0500 Subject: [PATCH 14/32] fix md formatting --- standard/classes.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/standard/classes.md b/standard/classes.md index 67f044723..c4f68c34b 100644 --- a/standard/classes.md +++ b/standard/classes.md @@ -5423,7 +5423,7 @@ This allows the context to keep track of how many `void`-returning async functio ### §indexable-sequence-general General -> **Note to TG2 reviewers:** Rationale for the choice of the name of the term *indexable sequence*: Various MS-hosted on-line pages use the term *sequence*. This word is already used quite a bit in the C# spec, in both a general sense as well as being defined in the context of query expressions. "§11.17 Query expressions|§11.17.1 General states: A query expression begins with a `from` clause and ends with either a `select` or `group` clause. The initial `from` clause may be followed by zero or more `from`, `let`, `where`, `join` or `orderby` clauses. Each `from` clause is a generator introducing a range variable that ranges over the elements of a sequence. Each `let` clause introduces a range variable representing a value computed by means of previous range variables. …". That definition is *not* applicable to indexes and ranges! The MS-provided proposal uses *collection*; however, that implies enumerable support, which is *not* required by indexes and ranges. (BTW, although it is used a lot in the C# spec, the term *collection* is *not* defined!) As such, rather than overload an existing term or invent a completely different one, I came up with *indexable sequence*. +**Note to TG2 reviewers:** Rationale for the choice of the name of the term *indexable sequence*: Various MS-hosted on-line pages use the term *sequence*. This word is already used quite a bit in the C# spec, in both a general sense as well as being defined in the context of query expressions. "§11.17 Query expressions|§11.17.1 General states: A query expression begins with a `from` clause and ends with either a `select` or `group` clause. The initial `from` clause may be followed by zero or more `from`, `let`, `where`, `join` or `orderby` clauses. Each `from` clause is a generator introducing a range variable that ranges over the elements of a sequence. Each `let` clause introduces a range variable representing a value computed by means of previous range variables. …". That definition is *not* applicable to indexes and ranges! The MS-provided proposal uses *collection*; however, that implies enumerable support, which is *not* required by indexes and ranges. (BTW, although it is used a lot in the C# spec, the term *collection* is *not* defined!) As such, rather than overload an existing term or invent a completely different one, I came up with *indexable sequence*. An ***indexable sequence*** is an ordered set of zero or more elements having the same type. Any given element can be accessed via an index, and a contiguous subset of elements—referred to as a ***slice***—can be denoted via a range. @@ -5531,7 +5531,7 @@ The provided instance indexer shall have the same accessibility and return type, When the type is indexed with a `System.Range`, the provided instance indexer shall take the given range and pass its start index and length as `int`s to `Slice` (or in the case of `string`, to method `Substring`). -> **Note to TG2 reviewers:** The MS proposal, section “Implicit Range support,” provided a very detailed discussion of how to transform a pair of Indexes into a call to `Slice` depending on the form of the range used. Rex did *not* retain this in the final proposal, as he saw no point in doing so. Given a start and end index, it is a simple matter to compute the length **in all cases regardless of range format!**, as he shows in his range indexer implementation in “Explicit range support” above. +**Note to TG2 reviewers:** The MS proposal, section “Implicit Range support,” provided a very detailed discussion of how to transform a pair of Indexes into a call to `Slice` depending on the form of the range used. Rex did *not* retain this in the final proposal, as he saw no point in doing so. Given a start and end index, it is a simple matter to compute the length **in all cases regardless of range format!**, as he shows in his range indexer implementation in “Explicit range support” above. > *Note*: See §indexable-sequence-expl-support-for-range for an example of an explicitly provided `Range` indexer. If that were not defined, its equivalent would be provided by the implementation, except that the provided indexer would call `Slice` to create and copy the slice. For type `BitArray`, `Slice` might be defined, as follows: > @@ -5550,4 +5550,4 @@ When the type is indexed with a `System.Range`, the provided instance indexer sh > > *end note* -> **Note to TG2 reviewers:** Setter: What if anything should we say about implicit and explicit setter for a Range indexer? Certainly, one can define a setter for a user-defined type; however, it is not obvious as to what such a setter would do, especially since it must be used on the left-hand side of assignment taking a right-hand side of the same type as the index returns. In the case of type `BitArray` that would mean something like `ba1[range1] = ba2`, or perhaps `ba1[range1] = ba2[range2]`. As far as Rex could determine, the operations one might like to implement using such a setter are probably best implemented via a named method. In any event, for a compiler-generated Range indexer, attempting to use its setter results in the error message “CS0131 The left-hand side of an assignment must be a variable, property or indexer,” which suggests the generated indexer **has no setter**. If that is the case, we should say that in the previous section. +**Note to TG2 reviewers:** Setter: What if anything should we say about implicit and explicit setter for a Range indexer? Certainly, one can define a setter for a user-defined type; however, it is not obvious as to what such a setter would do, especially since it must be used on the left-hand side of assignment taking a right-hand side of the same type as the index returns. In the case of type `BitArray` that would mean something like `ba1[range1] = ba2`, or perhaps `ba1[range1] = ba2[range2]`. As far as Rex could determine, the operations one might like to implement using such a setter are probably best implemented via a named method. In any event, for a compiler-generated Range indexer, attempting to use its setter results in the error message “CS0131 The left-hand side of an assignment must be a variable, property or indexer,” which suggests the generated indexer **has no setter**. If that is the case, we should say that in the previous section. From 0b81b47e2a4845c74c018b9248333c8bd75014e4 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Mon, 6 Feb 2023 10:27:50 -0500 Subject: [PATCH 15/32] fix md formatting --- standard/expressions.md | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/standard/expressions.md b/standard/expressions.md index 7a71b99dd..71c2da1bd 100644 --- a/standard/expressions.md +++ b/standard/expressions.md @@ -2117,9 +2117,9 @@ The run-time processing of an array access of the form `P[A]`, where `P` is a *p > words[idx.GetOffset(words.Length)] > ``` > -> 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* - -> *Example*: Given the following jagged array and Indexes: +> which results in `"green"`. Similarly, `words[^0]` is transformed by the implementation to `words[(^0).GetOffset(words.Length)]`, which results in `System.IndexOutOfRangeException`. +> +> Given the following jagged array and Indexes: > > ```csharp > int[][] values = new int[][] { … }; @@ -2133,9 +2133,7 @@ The run-time processing of an array access of the form `P[A]`, where `P` is a *p > values[idx1.GetOffset(values.Length)][idx2.GetOffset(values[idx1].Length)] > ``` > -> *end example* - -> *Example*: Given the following multidimensional array and Indexes: +> Given the following multidimensional array and Indexes: > > ```csharp > int[,] values2D = { … }; @@ -2150,9 +2148,7 @@ The run-time processing of an array access of the form `P[A]`, where `P` is a *p idx4.GetOffset(values2D.GetUpperBound(1) + 1)] > ``` > -> *end example* - -> *Example*: Given the following one-dimensional array: +> Given the following one-dimensional array: > > ```csharp > string[] seasons = new string[] { "Summer", "Autumn", "Winter", "Spring" }; @@ -2165,9 +2161,9 @@ The run-time processing of an array access of the form `P[A]`, where `P` is a *p seasons, 0..2) > ``` > -> which returns the `string[]` slice containing `"Summer"` and `"Autumn"`. *end example* - -> *Example*: Given the following jagged array: +> which returns the `string[]` slice containing `"Summer"` and `"Autumn"`. +> +> Given the following jagged array: > > ```csharp > int[][] values = new int[][] { new int[] { 10, 9, 5 }, From a007699bcf01a83f98e5a00988c0360a7e72b4cf Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Mon, 6 Feb 2023 10:32:28 -0500 Subject: [PATCH 16/32] fix md formatting --- standard/expressions.md | 1 + 1 file changed, 1 insertion(+) diff --git a/standard/expressions.md b/standard/expressions.md index 71c2da1bd..5e7041f62 100644 --- a/standard/expressions.md +++ b/standard/expressions.md @@ -3701,6 +3701,7 @@ The left and right operands denote, respectively, a start and end Index. For thi Lifted ([§11.4.8](expressions.md#1148-lifted-operators)) forms of the unlifted predefined range operator defined above are also predefined. > *Example*: The following example uses array and string indexable sequences: +> > ```csharp > string[] seasons = new string[] { "Summer", "Autumn", "Winter", "Spring" }; > seasons[1..3] // string[2] "Autumn", "Winter" From 8895e783fa546f692f46e5121ee74512e4380da3 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Mon, 6 Feb 2023 10:35:04 -0500 Subject: [PATCH 17/32] fix md formatting --- standard/expressions.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/standard/expressions.md b/standard/expressions.md index 5e7041f62..94900563c 100644 --- a/standard/expressions.md +++ b/standard/expressions.md @@ -2145,7 +2145,7 @@ The run-time processing of an array access of the form `P[A]`, where `P` is a *p > > ```csharp > values2D[idx3.GetOffset(values2D.GetUpperBound(0) + 1), - idx4.GetOffset(values2D.GetUpperBound(1) + 1)] +> idx4.GetOffset(values2D.GetUpperBound(1) + 1)] > ``` > > Given the following one-dimensional array: @@ -2157,8 +2157,7 @@ The run-time processing of an array access of the form `P[A]`, where `P` is a *p > `seasons[0..2]` is transformed by the implementation to > > ```csharp -> System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray( - seasons, 0..2) +> System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray(seasons, 0..2) > ``` > > which returns the `string[]` slice containing `"Summer"` and `"Autumn"`. From d8b297d90802ae604c7d66f2778afb39ccd70a89 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Mon, 6 Feb 2023 14:08:26 -0500 Subject: [PATCH 18/32] Add annotation to examples --- standard/classes.md | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/standard/classes.md b/standard/classes.md index c4f68c34b..9bcee302e 100644 --- a/standard/classes.md +++ b/standard/classes.md @@ -5447,12 +5447,10 @@ A type having an instance indexer taking a single argument of type `System.Index > *Example*: In [§14.9](classes.md#149-indexers), there is an example defining type `BitArray`, which stores bits in an array of `int`. Individual bits are accessed for read/write via an `int` indexer. Adding an `Index` indexer that simply interprets the `Index` argument as an `int`, is simple: > +> > ```csharp -> class BitArray +> partial class BitArray > { -> … -> public int Length { … } -> public bool this[int index] { … } > public bool this[Index idx] > { > get @@ -5461,7 +5459,7 @@ A type having an instance indexer taking a single argument of type `System.Index > } > set > { -> this[idx.GetOffset(Length)] = value; // use the [int] indexer +> this[idx.GetOffset(Length)] = value; // use the [int] indexer > } > } > } @@ -5491,13 +5489,10 @@ A type having an instance indexer taking a single argument of type `System.Range > *Example*: In [§14.9](classes.md#149-indexers), there is an example defining type `BitArray`, which stores bits in an array of `int`. Adding a `Range` indexer that returns a `BitArray` representing the bit slice designated by the Range, is simple: > +> > ```csharp -> class BitArray +> partial class BitArray > { -> … -> public BitArray(int length) { … } -> public int Length { … } -> public bool this[int index] { … } > public BitArray this[Range range] // note the return type > { > get @@ -5535,16 +5530,20 @@ When the type is indexed with a `System.Range`, the provided instance indexer sh > *Note*: See §indexable-sequence-expl-support-for-range for an example of an explicitly provided `Range` indexer. If that were not defined, its equivalent would be provided by the implementation, except that the provided indexer would call `Slice` to create and copy the slice. For type `BitArray`, `Slice` might be defined, as follows: > +> > ```csharp -> public BitArray Slice(int startIdx, int rangeLength) +> partial class BitArray > { -> int endIdx = startIdx + rangeLength; -> BitArray newBitArray = new BitArray(rangeLength); -> for (int i = startIdx; i < endIdx; ++i) +> public BitArray Slice(int startIdx, int rangeLength) > { -> newBitArray[i - startIdx] = this[i]; +> int endIdx = startIdx + rangeLength; +> BitArray newBitArray = new BitArray(rangeLength); +> for (int i = startIdx; i < endIdx; ++i) +> { +> newBitArray[i - startIdx] = this[i]; +> } +> return newBitArray; > } -> return newBitArray; > } > ``` > From 53d51fd17f3a95684321092a4188322dd6265445 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Mon, 6 Feb 2023 14:10:16 -0500 Subject: [PATCH 19/32] Create BitArrayPartial1.cs --- .../additional-files/BitArrayPartial1.cs | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 tools/example-templates/additional-files/BitArrayPartial1.cs diff --git a/tools/example-templates/additional-files/BitArrayPartial1.cs b/tools/example-templates/additional-files/BitArrayPartial1.cs new file mode 100644 index 000000000..c82b8ddfd --- /dev/null +++ b/tools/example-templates/additional-files/BitArrayPartial1.cs @@ -0,0 +1,66 @@ +class Test +{ + static void Main() + { + BitArray ba1 = new BitArray(100); + ba1[0] = true; + ba1[^98] = true; + ba1[99] = true; + Console.WriteLine("ba1[0] = {0}", ba1[0]); // True + Console.WriteLine("ba1[98] = {0}", ba1[98]); // False + Console.WriteLine("ba1[Index 0] = {0}", ba1[new Index(0)]); // True + Console.WriteLine("ba1[^1] = {0}", ba1[^1]); // True + } +} + +partial class BitArray +{ + int[] bits; + int length; + + public BitArray(int length) + { + if (length < 0) + { + throw new ArgumentException(); + } + else if (length == 0) + { + bits = new int[1]; + } + else + { + bits = new int[((length - 1) >> 5) + 1]; + } + this.length = length; + } + + public int Length => length; + + public bool this[int index] + { + get + { + if (index < 0 || index >= length) + { + throw new IndexOutOfRangeException(); + } + return (bits[index >> 5] & 1 << index) != 0; + } + set + { + if (index < 0 || index >= length) + { + throw new IndexOutOfRangeException(); + } + if (value) + { + bits[index >> 5] |= 1 << index; + } + else + { + bits[index >> 5] &= ~(1 << index); + } + } + } +} From 12b4632fdc8ba72a2e13bf40bb1f614654dc4011 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Mon, 6 Feb 2023 14:11:21 -0500 Subject: [PATCH 20/32] Create BitArrayPartial2.cs --- .../additional-files/BitArrayPartial2.cs | 115 ++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 tools/example-templates/additional-files/BitArrayPartial2.cs diff --git a/tools/example-templates/additional-files/BitArrayPartial2.cs b/tools/example-templates/additional-files/BitArrayPartial2.cs new file mode 100644 index 000000000..1e634efee --- /dev/null +++ b/tools/example-templates/additional-files/BitArrayPartial2.cs @@ -0,0 +1,115 @@ +class Test +{ + static void Main() + { + BitArray ba = new BitArray(5); + ba[0] = true; + ba[3] = true; + ba[^1] = true; + Console.WriteLine("ba = >{0}<", ba); + + Range[] testRange = { +// all elements + /*0..5,*/ 0.., ..5, //.., 0..^0, ..^0, ^5..5, ^5.., ^5..^0, + +// trailing part +// 1..5, 1.., 1..^0, ^4..5, ^4.., ^4..^0, + +// leading part +// 0..4, ..4, ^5..4, ^5..^1, + +// middle part: +// 1..4, ^4..4, ^4..^1, 1..^1, +// 2..4, ^3..4, ^3..^1, 2..^1, + 3..4, ^2..4, //^2..^1, 3..^1, + +// empty range + 0..0//, 1..1, 2..2, 3..3, 4..4, 5..5 + }; + + foreach (Range r in testRange) + { + Console.WriteLine($"BitArray is >{ba[r]}<"); + } + } +} + +partial class BitArray +{ + int[] bits; + int length; + + public BitArray(int length) + { + if (length < 0) + { + throw new ArgumentException(); + } + else if (length == 0) + { + bits = new int[1]; + } + else + { + bits = new int[((length - 1) >> 5) + 1]; + } + this.length = length; + } + + public int Length => length; + + public bool this[int index] + { + get + { + if (index < 0 || index >= length) + { + throw new IndexOutOfRangeException(); + } + return (bits[index >> 5] & 1 << index) != 0; + } + set + { + if (index < 0 || index >= length) + { + throw new IndexOutOfRangeException(); + } + if (value) + { + bits[index >> 5] |= 1 << index; + } + else + { + bits[index >> 5] &= ~(1 << index); + } + } + } + public override string ToString() + { + string retstr = ""; + int bitsWord; + int upBound = bits.GetUpperBound(0); + int bitCounter = Length; + + if (Length == 0) + { + return retstr; + } + + for (int i = 0; i <= upBound; ++i) + { + bitsWord = bits[i]; + for (int j = 0; j < 32; ++j) + { + if (bitCounter-- == 0) + { + break; + } + retstr += ((bitsWord & 1) == 1) ? "1" : "0"; + bitsWord >>= 1; + } + } + + return retstr; + } +} From f62b30fe00bfad7eb23e435b209998bedd468271 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Mon, 6 Feb 2023 14:12:21 -0500 Subject: [PATCH 21/32] Create BitArrayPartial3.cs --- .../additional-files/BitArrayPartial3.cs | 116 ++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 tools/example-templates/additional-files/BitArrayPartial3.cs diff --git a/tools/example-templates/additional-files/BitArrayPartial3.cs b/tools/example-templates/additional-files/BitArrayPartial3.cs new file mode 100644 index 000000000..95c2e09c3 --- /dev/null +++ b/tools/example-templates/additional-files/BitArrayPartial3.cs @@ -0,0 +1,116 @@ +class Test +{ + static void Main() + { + BitArray ba = new BitArray(5); + ba[0] = true; + ba[3] = true; + ba[^1] = true; + Console.WriteLine("ba = >{0}<", ba); + + Range[] testRange = { +// all elements + /*0..5,*/ 0.., ..5, //.., 0..^0, ..^0, ^5..5, ^5.., ^5..^0, + +// trailing part +// 1..5, 1.., 1..^0, ^4..5, ^4.., ^4..^0, + +// leading part +// 0..4, ..4, ^5..4, ^5..^1, + +// middle part: +// 1..4, ^4..4, ^4..^1, 1..^1, +// 2..4, ^3..4, ^3..^1, 2..^1, + 3..4, ^2..4, //^2..^1, 3..^1, + +// empty range + 0..0//, 1..1, 2..2, 3..3, 4..4, 5..5 + }; + + foreach (Range r in testRange) + { + Console.WriteLine($"BitArray is >{ba[r]}<"); + } + } +} + +partial class BitArray +{ + int[] bits; + int length; + + public BitArray(int length) + { + if (length < 0) + { + throw new ArgumentException(); + } + else if (length == 0) + { + bits = new int[1]; + } + else + { + bits = new int[((length - 1) >> 5) + 1]; + } + this.length = length; + } + + public int Length => length; + + public override string ToString() + { + string retstr = ""; + int bitsWord; + int upBound = bits.GetUpperBound(0); + int bitCounter = Length; + + if (Length == 0) + { + return retstr; + } + + for (int i = 0; i <= upBound; ++i) + { + bitsWord = bits[i]; + for (int j = 0; j < 32; ++j) + { + if (bitCounter-- == 0) + { + break; + } + retstr += ((bitsWord & 1) == 1) ? "1" : "0"; + bitsWord >>= 1; + } + } + + return retstr; + } + + public bool this[int index] + { + get + { + if (index < 0 || index >= length) + { + throw new IndexOutOfRangeException(); + } + return (bits[index >> 5] & 1 << index) != 0; + } + set + { + if (index < 0 || index >= length) + { + throw new IndexOutOfRangeException(); + } + if (value) + { + bits[index >> 5] |= 1 << index; + } + else + { + bits[index >> 5] &= ~(1 << index); + } + } + } +} From cf03cbd49c07d742f78bee55a9101a50266669ba Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Mon, 6 Feb 2023 14:51:33 -0500 Subject: [PATCH 22/32] added annotation to examples --- standard/expressions.md | 73 ++++++++++++++++++++++++----------------- 1 file changed, 43 insertions(+), 30 deletions(-) diff --git a/standard/expressions.md b/standard/expressions.md index 94900563c..3eadfbcc6 100644 --- a/standard/expressions.md +++ b/standard/expressions.md @@ -2106,25 +2106,24 @@ The run-time processing of an array access of the form `P[A]`, where `P` is a *p > *Example*: Given the following one-dimensional array and Index: > +> > ```csharp > string[] words = new string[] { "red", "green", "blue" }; > Index idx = 1; +> Console.WriteLine(words[idx]); // green +> Console.WriteLine(words[^0]); // IndexOutOfRangeException > ``` > -> `words[idx]` is transformed by the implementation to -> -> ```csharp -> words[idx.GetOffset(words.Length)] -> ``` -> -> which results in `"green"`. Similarly, `words[^0]` is transformed by the implementation to `words[(^0).GetOffset(words.Length)]`, which results in `System.IndexOutOfRangeException`. +> `words[idx]` is transformed by the implementation to `words[idx.GetOffset(words.Length)]`. Similarly, `words[^0]` is transformed by the implementation to `words[(^0).GetOffset(words.Length)]`, which results in an exception. > > Given the following jagged array and Indexes: > +> > ```csharp -> int[][] values = new int[][] { … }; +> int[][] values = new int[][] { new int[] { 10, 9 }, new int[] { 6, 12, 17 }}; > Index idx1 = 1; > Index idx2 = ^1; +> Console.WriteLine(values[idx1][idx2]); // 17 (values[1][2]) > ``` > > `values[idx1][idx2]` is transformed by the implementation to @@ -2135,41 +2134,49 @@ The run-time processing of an array access of the form `P[A]`, where `P` is a *p > > Given the following multidimensional array and Indexes: > +> > ```csharp -> int[,] values2D = { … }; +> int[,] values2D = {{10, 5, 7, 1}, {34, 13, 6, 2}}; > Index idx3 = 1; > Index idx4 = ^1; +> Console.WriteLine(values2D[idx3, idx4]); // won't compile! > ``` > -> 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 +> as there is no implicit conversion from `System.Index` to `int, what one might like to express simply as `values2D[idx3, idx4]` must instead be written explicitly as > > ```csharp -> values2D[idx3.GetOffset(values2D.GetUpperBound(0) + 1), -> idx4.GetOffset(values2D.GetUpperBound(1) + 1)] +> values2D[idx3.GetOffset(values2D.GetUpperBound(0) + 1), idx4.GetOffset(values2D.GetUpperBound(1) + 1)] +> > ``` > > Given the following one-dimensional array: > +> +> > ```csharp > string[] seasons = new string[] { "Summer", "Autumn", "Winter", "Spring" }; +> string[] names = seasons[0..2]; // slice containing "Summer" and "Autumn" > ``` > > `seasons[0..2]` is transformed by the implementation to > +> > ```csharp > System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray(seasons, 0..2) > ``` > -> which returns the `string[]` slice containing `"Summer"` and `"Autumn"`. +> which returns a `string[]` slice. > > Given the following jagged array: > +> > ```csharp > int[][] values = new int[][] { new int[] { 10, 9, 5 }, -> new int[] { 6, 12, 17, 32 }, new int[] { 28, 42 } }; +> new int[] { 6, 12, 17, 32 }, new int[] { 28, 42 } }; +> Console.WriteLine(values[1..3][^1][..2][^1]); // 42 > ``` > -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* +> 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* #### 12.8.11.3 Indexer access @@ -3507,6 +3514,7 @@ This operator provides a succinct syntax for denoting the position of an element For an operation of the form `^x`, unary operator overload resolution ([§11.4.4](expressions.md#1144-unary-operator-overload-resolution)) is applied to select a specific operator implementation. The operand is converted to the parameter type of the selected operator, and the type of the result is the return type of the operator. Only one predefined index-from-end operator exists: +> ```csharp System.Index operator ^(int fromEnd); ``` @@ -3519,24 +3527,26 @@ Lifted ([§11.4.8](expressions.md#1148-lifted-operators)) forms of the unlifted > *Example*: The following example uses array and string indexable sequences: > +> > ```csharp > string[] words = new string[] { "red", "green", "blue" }; -> words[^1] // OK: "blue" -> words[^3] // OK: "red" -> words[^0] // refers to the (non-existent) element beyond the end +> string str; +> str = words[^1]; // OK: "blue" +> str = words[^3]; // OK: "red" +> //str = words[^0]; // refers to the (non-existent) element beyond the end > > Index idx = ^0; // OK; no attempt made to access any non-existent element > int i = -1; -> idx = ^(ushort)i; // OK; 65535 (0x0000FFFF) from the end -> idx = ^(short)i; // System.ArgumentOutOfRangeException: +> idx = ^(ushort)i; // OK; ^65535 (0xFFFF) +> //idx = ^(short)i; // System.ArgumentOutOfRangeException > > string s = "Hello!"; > int? iN = 5; -> Index? idx4 = ^iN; // OK: non-null, ^5 -> s[^idx4.Value.Value] // OK: "e" +> Index? idx4 = ^iN; // OK: non-null, ^5 +> char c = s[^idx4.Value.Value]; // OK: "e" > ``` > -> `^idx4.Value.Value` is the `int` position from the end of the sequence designated by the `Index` wrapped in the `Index?` designated by `idx4`. (`System.Nullable`and `System.Index` both have public read-only properties called `Value`.) +> `^idx4.Value.Value` is the `int` position from the end of the sequence designated by the `Index` wrapped in the `Index?` designated by `idx4`. (`System.Nullable `and `System.Index` both have public read-only properties called `Value`.) > *end example* ### 12.9.6 Prefix increment and decrement operators @@ -3684,6 +3694,7 @@ range_expression For an operation of the form `s .. e`, binary operator overload resolution ([§11.4.5](expressions.md#1145-binary-operator-overload-resolution)) is applied to select a specific operator implementation. The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator. Only one predefined range operator exists: +> ```csharp System.Range operator ..(System.Index start = 0, System.Index end = ^0); ``` @@ -3701,18 +3712,20 @@ Lifted ([§11.4.8](expressions.md#1148-lifted-operators)) forms of the unlifted > *Example*: The following example uses array and string indexable sequences: > +> > ```csharp > string[] seasons = new string[] { "Summer", "Autumn", "Winter", "Spring" }; -> seasons[1..3] // string[2] "Autumn", "Winter" -> seasons[^2..^1] // string[1] "Winter" -> seasons[2..] // string[2] "Winter", "Spring" -> seasons[1..1] // string[0] -> -> string s2 = "Hello!"; +> string[] slice; +> slice = seasons[1..3]; // string[2] "Autumn", "Winter" +> slice = seasons[^2..^1]; // string[1] "Winter" +> slice = seasons[2..]; // string[2] "Winter", "Spring" +> slice = seasons[1..1]; // string[0] +> +> string s1 = "Hello!"; > Index? startN = 1; > Index? endN = ^2; > Range? r = startN .. endN; -> s2[r.Value] // OK: "ell" +> string s2 = s1[r.Value]; // "ell" > ``` > > *end example* From 9e74541c22105685a45ee945e0a73cfaa086008f Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Mon, 6 Feb 2023 15:14:17 -0500 Subject: [PATCH 23/32] fix md formatting --- standard/expressions.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/standard/expressions.md b/standard/expressions.md index 3eadfbcc6..5708cd015 100644 --- a/standard/expressions.md +++ b/standard/expressions.md @@ -2142,7 +2142,7 @@ The run-time processing of an array access of the form `P[A]`, where `P` is a *p > Console.WriteLine(values2D[idx3, idx4]); // won't compile! > ``` > -> as there is no implicit conversion from `System.Index` to `int, what one might like to express simply as `values2D[idx3, idx4]` must instead be written explicitly as +> as there is no implicit conversion from `System.Index` to `int`, what one might like to express simply as `values2D[idx3, idx4]` must instead be written explicitly as > > ```csharp > values2D[idx3.GetOffset(values2D.GetUpperBound(0) + 1), idx4.GetOffset(values2D.GetUpperBound(1) + 1)] @@ -3546,7 +3546,7 @@ Lifted ([§11.4.8](expressions.md#1148-lifted-operators)) forms of the unlifted > char c = s[^idx4.Value.Value]; // OK: "e" > ``` > -> `^idx4.Value.Value` is the `int` position from the end of the sequence designated by the `Index` wrapped in the `Index?` designated by `idx4`. (`System.Nullable `and `System.Index` both have public read-only properties called `Value`.) +> `^idx4.Value.Value` is the `int` position from the end of the sequence designated by the `Index` wrapped in the `Index?` designated by `idx4`. (`System.Nullable`and `System.Index` both have public read-only properties called `Value`.) > *end example* ### 12.9.6 Prefix increment and decrement operators From 5157c57ddff57724f54335f275ef4073f6b0ff48 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Tue, 26 Sep 2023 13:37:55 -0400 Subject: [PATCH 24/32] update links --- standard/classes.md | 20 ++++++++++---------- standard/expressions.md | 8 ++++---- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/standard/classes.md b/standard/classes.md index 9bcee302e..f5cd75868 100644 --- a/standard/classes.md +++ b/standard/classes.md @@ -3182,7 +3182,7 @@ In a *ref_property_body* an expression body consisting of `=>` followed by `ref` When a property declaration includes an `extern` modifier, the property is said to be an ***external property***. Because an external property declaration provides no actual implementation, each of its *accessor_declarations* consists of a semicolon. -A type is ***countable*** if it has a property named `Length` or `Count` with an accessible `get` accessor ([§14.7.3]( classes.md#1473-accessors)) and a return type of `int`. +A type is ***countable*** if it has a property named `Length` or `Count` with an accessible `get` accessor ([§15.7.3]( classes.md#1573-accessors)) and a return type of `int`. ### 15.7.2 Static and instance properties @@ -5423,7 +5423,7 @@ This allows the context to keep track of how many `void`-returning async functio ### §indexable-sequence-general General -**Note to TG2 reviewers:** Rationale for the choice of the name of the term *indexable sequence*: Various MS-hosted on-line pages use the term *sequence*. This word is already used quite a bit in the C# spec, in both a general sense as well as being defined in the context of query expressions. "§11.17 Query expressions|§11.17.1 General states: A query expression begins with a `from` clause and ends with either a `select` or `group` clause. The initial `from` clause may be followed by zero or more `from`, `let`, `where`, `join` or `orderby` clauses. Each `from` clause is a generator introducing a range variable that ranges over the elements of a sequence. Each `let` clause introduces a range variable representing a value computed by means of previous range variables. …". That definition is *not* applicable to indexes and ranges! The MS-provided proposal uses *collection*; however, that implies enumerable support, which is *not* required by indexes and ranges. (BTW, although it is used a lot in the C# spec, the term *collection* is *not* defined!) As such, rather than overload an existing term or invent a completely different one, I came up with *indexable sequence*. +**Note to TG2 reviewers:** Rationale for the choice of the name of the term *indexable sequence*: Various MS-hosted on-line pages use the term *sequence*. This word is already used quite a bit in the C# spec, in both a general sense as well as being defined in the context of query expressions. "§12.17 Query expressions|§12.17.1 General states: A query expression begins with a `from` clause and ends with either a `select` or `group` clause. The initial `from` clause may be followed by zero or more `from`, `let`, `where`, `join` or `orderby` clauses. Each `from` clause is a generator introducing a range variable that ranges over the elements of a sequence. Each `let` clause introduces a range variable representing a value computed by means of previous range variables. …". That definition is *not* applicable to indexes and ranges! The MS-provided proposal uses *collection*; however, that implies enumerable support, which is *not* required by indexes and ranges. (BTW, although it is used a lot in the C# spec, the term *collection* is *not* defined!) As such, rather than overload an existing term or invent a completely different one, I came up with *indexable sequence*. An ***indexable sequence*** is an ordered set of zero or more elements having the same type. Any given element can be accessed via an index, and a contiguous subset of elements—referred to as a ***slice***—can be denoted via a range. @@ -5433,17 +5433,17 @@ For an indexable sequence of length *N*, elements can be accessed using indexes A slice can be obtained using the `..` range operator (§range-operator). A range of the form `s..e` starts at element `s` and ends with the element immediately prior to element `e`. -All single-dimensional and jagged arrays ([§16.1](arrays.md#161-general)) are indexable sequences; multi-dimensional arrays are not! The use of indexes and ranges with arrays is described in [§11.7.10.2](expressions.md#117102-array-access). +All single-dimensional and jagged arrays ([§17.1](arrays.md#171-general)) are indexable sequences; multi-dimensional arrays are not! The use of indexes and ranges with arrays is described in [§12.7.10.2](expressions.md#127102-array-access). An object of type `string` is an indexable sequence. -A user-defined type can provide explicit support for indexer access ([§11.7.10.3](expressions.md#117103-indexer-access)) using `System.Index` and `System.Range`. (See §indexable-sequence-expl-support-for-index and §indexable-sequence-expl-support-for-range.) If various criteria are met, an existing user-defined type that does *not* have such explicit support, shall have provided for it by the implementation implicit support for such indexer and range access. (See §indexable-sequence-impl-support-for-index and §indexable-sequence-impl-support-for-range.) In both cases, the type is recognized as being an indexable sequence type. +A user-defined type can provide explicit support for indexer access ([§12.7.10.3](expressions.md#127103-indexer-access)) using `System.Index` and `System.Range`. (See §indexable-sequence-expl-support-for-index and §indexable-sequence-expl-support-for-range.) If various criteria are met, an existing user-defined type that does *not* have such explicit support, shall have provided for it by the implementation implicit support for such indexer and range access. (See §indexable-sequence-impl-support-for-index and §indexable-sequence-impl-support-for-range.) In both cases, the type is recognized as being an indexable sequence type. ### §indexable-sequence-support-for-index Providing support for Index #### §indexable-sequence-expl-support-for-index Explicit Index support -A type having an instance indexer taking a single argument of type `System.Index`, or a first argument of that type followed by optional arguments, may be indexed as described by [§11.7.10.3](expressions.md#117103-indexer-access). +A type having an instance indexer taking a single argument of type `System.Index`, or a first argument of that type followed by optional arguments, may be indexed as described by [§12.7.10.3](expressions.md#127103-indexer-access). > *Example*: In [§14.9](classes.md#149-indexers), there is an example defining type `BitArray`, which stores bits in an array of `int`. Individual bits are accessed for read/write via an `int` indexer. Adding an `Index` indexer that simply interprets the `Index` argument as an `int`, is simple: > @@ -5471,7 +5471,7 @@ A type having an instance indexer taking a single argument of type `System.Index An implementation shall behave as if it provides an instance indexer member with a single parameter of type `System.Index` for any type that meets the following criteria: -- The type is countable [§14.7.1](classes.md#1471-general). +- The type is countable [§15.7.1](classes.md#1571-general). - The type has an accessible instance indexer taking an argument of type `int` as its only argument, or as its first argument with the remaining arguments being optional. - The type does not have an accessible instance indexer taking a `System.Index` as its only argument, or as its first argument with the remaining arguments being optional. @@ -5485,9 +5485,9 @@ The provided instance indexer shall take the given `System.Index` and use that t #### §indexable-sequence-expl-support-for-range Explicit Range support -A type having an instance indexer taking a single argument of type `System.Range`, or a first argument of that type followed by optional arguments, may be indexed as described by [§11.7.10.3](expressions.md#117103-indexer-access). +A type having an instance indexer taking a single argument of type `System.Range`, or a first argument of that type followed by optional arguments, may be indexed as described by [§12.7.10.3](expressions.md#127103-indexer-access). -> *Example*: In [§14.9](classes.md#149-indexers), there is an example defining type `BitArray`, which stores bits in an array of `int`. Adding a `Range` indexer that returns a `BitArray` representing the bit slice designated by the Range, is simple: +> *Example*: In [§15.9](classes.md#159-indexers), there is an example defining type `BitArray`, which stores bits in an array of `int`. Adding a `Range` indexer that returns a `BitArray` representing the bit slice designated by the Range, is simple: > > > ```csharp @@ -5517,9 +5517,9 @@ A type having an instance indexer taking a single argument of type `System.Range An implementation shall behave as if it provides an instance indexer member with a single parameter of type `System.Range` for any type that meets the following criteria: -- The type is countable [§14.7.1](classes.md#1471-general). +- The type is countable [§15.7.1](classes.md#1571-general). - The type has an accessible instance method named `Slice` taking two arguments of type `int` as the only arguments. For type `string`, the method `Substring` is used instead of `Slice`. - > *Note*: As specified in [§11.7.10.2](expressions.md#117102-array-access), for array access, the method `System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray` is used instead of `Slice`. *end note* + > *Note*: As specified in [§12.7.10.2](expressions.md#127102-array-access), for array access, the method `System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray` is used instead of `Slice`. *end note* - The type does not have an accessible instance indexer taking a `System.Range` as its only argument, or as its first argument with the remaining arguments being optional. The provided instance indexer shall have the same accessibility and return type, including `ref` if present, as `Slice`. diff --git a/standard/expressions.md b/standard/expressions.md index 5708cd015..86c0e0e9e 100644 --- a/standard/expressions.md +++ b/standard/expressions.md @@ -3512,7 +3512,7 @@ Lifted ([§12.4.8](expressions.md#1248-lifted-operators)) forms of the unlifted This operator provides a succinct syntax for denoting the position of an element in an indexable sequence (§indexable-sequence) relative to the end of that sequence. -For an operation of the form `^x`, unary operator overload resolution ([§11.4.4](expressions.md#1144-unary-operator-overload-resolution)) is applied to select a specific operator implementation. The operand is converted to the parameter type of the selected operator, and the type of the result is the return type of the operator. Only one predefined index-from-end operator exists: +For an operation of the form `^x`, unary operator overload resolution ([§12.4.4](expressions.md#1244-unary-operator-overload-resolution)) is applied to select a specific operator implementation. The operand is converted to the parameter type of the selected operator, and the type of the result is the return type of the operator. Only one predefined index-from-end operator exists: > ```csharp @@ -3523,7 +3523,7 @@ For this operator, an object of (the immutable struct) type `System.Index` is re If after implicit conversion to `int` the operand has a negative value, an exception of type `System.ArgumentOutOfRangeException` is thrown. -Lifted ([§11.4.8](expressions.md#1148-lifted-operators)) forms of the unlifted predefined index-from-end operator defined above are also predefined. +Lifted ([§12.4.8](expressions.md#1248-lifted-operators)) forms of the unlifted predefined index-from-end operator defined above are also predefined. > *Example*: The following example uses array and string indexable sequences: > @@ -3692,7 +3692,7 @@ range_expression ; ``` -For an operation of the form `s .. e`, binary operator overload resolution ([§11.4.5](expressions.md#1145-binary-operator-overload-resolution)) is applied to select a specific operator implementation. The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator. Only one predefined range operator exists: +For an operation of the form `s .. e`, binary operator overload resolution ([§12.4.5](expressions.md#1245-binary-operator-overload-resolution)) is applied to select a specific operator implementation. The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator. Only one predefined range operator exists: > ```csharp @@ -3708,7 +3708,7 @@ The left and right operands denote, respectively, a start and end Index. For thi > *Note*: While a Range can be created with a start Index greater than the end Index, any attempt to use that Range to denote a slice from an indexable sequence will result in `System.ArgumentOutOfRangeException`. *end note* -Lifted ([§11.4.8](expressions.md#1148-lifted-operators)) forms of the unlifted predefined range operator defined above are also predefined. +Lifted ([§12.4.8](expressions.md#1248-lifted-operators)) forms of the unlifted predefined range operator defined above are also predefined. > *Example*: The following example uses array and string indexable sequences: > From 39a0fddd5cbd1354d0a3d055f6294d4571260cf9 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Tue, 26 Sep 2023 13:58:08 -0400 Subject: [PATCH 25/32] more section references --- standard/classes.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/standard/classes.md b/standard/classes.md index f5cd75868..5d8c1dd3d 100644 --- a/standard/classes.md +++ b/standard/classes.md @@ -5423,7 +5423,7 @@ This allows the context to keep track of how many `void`-returning async functio ### §indexable-sequence-general General -**Note to TG2 reviewers:** Rationale for the choice of the name of the term *indexable sequence*: Various MS-hosted on-line pages use the term *sequence*. This word is already used quite a bit in the C# spec, in both a general sense as well as being defined in the context of query expressions. "§12.17 Query expressions|§12.17.1 General states: A query expression begins with a `from` clause and ends with either a `select` or `group` clause. The initial `from` clause may be followed by zero or more `from`, `let`, `where`, `join` or `orderby` clauses. Each `from` clause is a generator introducing a range variable that ranges over the elements of a sequence. Each `let` clause introduces a range variable representing a value computed by means of previous range variables. …". That definition is *not* applicable to indexes and ranges! The MS-provided proposal uses *collection*; however, that implies enumerable support, which is *not* required by indexes and ranges. (BTW, although it is used a lot in the C# spec, the term *collection* is *not* defined!) As such, rather than overload an existing term or invent a completely different one, I came up with *indexable sequence*. +**Note to TG2 reviewers:** Rationale for the choice of the name of the term *indexable sequence*: Various MS-hosted on-line pages use the term *sequence*. This word is already used quite a bit in the C# spec, in both a general sense as well as being defined in the context of query expressions. "§12.17 Query expressions|§13.17.1 General states: A query expression begins with a `from` clause and ends with either a `select` or `group` clause. The initial `from` clause may be followed by zero or more `from`, `let`, `where`, `join` or `orderby` clauses. Each `from` clause is a generator introducing a range variable that ranges over the elements of a sequence. Each `let` clause introduces a range variable representing a value computed by means of previous range variables. …". That definition is *not* applicable to indexes and ranges! The MS-provided proposal uses *collection*; however, that implies enumerable support, which is *not* required by indexes and ranges. (BTW, although it is used a lot in the C# spec, the term *collection* is *not* defined!) As such, rather than overload an existing term or invent a completely different one, I came up with *indexable sequence*. An ***indexable sequence*** is an ordered set of zero or more elements having the same type. Any given element can be accessed via an index, and a contiguous subset of elements—referred to as a ***slice***—can be denoted via a range. @@ -5433,19 +5433,19 @@ For an indexable sequence of length *N*, elements can be accessed using indexes A slice can be obtained using the `..` range operator (§range-operator). A range of the form `s..e` starts at element `s` and ends with the element immediately prior to element `e`. -All single-dimensional and jagged arrays ([§17.1](arrays.md#171-general)) are indexable sequences; multi-dimensional arrays are not! The use of indexes and ranges with arrays is described in [§12.7.10.2](expressions.md#127102-array-access). +All single-dimensional and jagged arrays ([§17.1](arrays.md#171-general)) are indexable sequences; multi-dimensional arrays are not! The use of indexes and ranges with arrays is described in [§12.8.11.2](expressions.md#128112-array-access). An object of type `string` is an indexable sequence. -A user-defined type can provide explicit support for indexer access ([§12.7.10.3](expressions.md#127103-indexer-access)) using `System.Index` and `System.Range`. (See §indexable-sequence-expl-support-for-index and §indexable-sequence-expl-support-for-range.) If various criteria are met, an existing user-defined type that does *not* have such explicit support, shall have provided for it by the implementation implicit support for such indexer and range access. (See §indexable-sequence-impl-support-for-index and §indexable-sequence-impl-support-for-range.) In both cases, the type is recognized as being an indexable sequence type. +A user-defined type can provide explicit support for indexer access ([§12.8.11.3](expressions.md#128113-indexer-access)) using `System.Index` and `System.Range`. (See §indexable-sequence-expl-support-for-index and §indexable-sequence-expl-support-for-range.) If various criteria are met, an existing user-defined type that does *not* have such explicit support, shall have provided for it by the implementation implicit support for such indexer and range access. (See §indexable-sequence-impl-support-for-index and §indexable-sequence-impl-support-for-range.) In both cases, the type is recognized as being an indexable sequence type. ### §indexable-sequence-support-for-index Providing support for Index #### §indexable-sequence-expl-support-for-index Explicit Index support -A type having an instance indexer taking a single argument of type `System.Index`, or a first argument of that type followed by optional arguments, may be indexed as described by [§12.7.10.3](expressions.md#127103-indexer-access). +A type having an instance indexer taking a single argument of type `System.Index`, or a first argument of that type followed by optional arguments, may be indexed as described by [§12.8.11.3](expressions.md#128113-indexer-access). -> *Example*: In [§14.9](classes.md#149-indexers), there is an example defining type `BitArray`, which stores bits in an array of `int`. Individual bits are accessed for read/write via an `int` indexer. Adding an `Index` indexer that simply interprets the `Index` argument as an `int`, is simple: +> *Example*: In [§15.9](classes.md#159-indexers), there is an example defining type `BitArray`, which stores bits in an array of `int`. Individual bits are accessed for read/write via an `int` indexer. Adding an `Index` indexer that simply interprets the `Index` argument as an `int`, is simple: > > > ```csharp @@ -5485,7 +5485,7 @@ The provided instance indexer shall take the given `System.Index` and use that t #### §indexable-sequence-expl-support-for-range Explicit Range support -A type having an instance indexer taking a single argument of type `System.Range`, or a first argument of that type followed by optional arguments, may be indexed as described by [§12.7.10.3](expressions.md#127103-indexer-access). +A type having an instance indexer taking a single argument of type `System.Range`, or a first argument of that type followed by optional arguments, may be indexed as described by [§12.8.11.3](expressions.md#128113-indexer-access). > *Example*: In [§15.9](classes.md#159-indexers), there is an example defining type `BitArray`, which stores bits in an array of `int`. Adding a `Range` indexer that returns a `BitArray` representing the bit slice designated by the Range, is simple: > @@ -5519,7 +5519,7 @@ An implementation shall behave as if it provides an instance indexer member with - The type is countable [§15.7.1](classes.md#1571-general). - The type has an accessible instance method named `Slice` taking two arguments of type `int` as the only arguments. For type `string`, the method `Substring` is used instead of `Slice`. - > *Note*: As specified in [§12.7.10.2](expressions.md#127102-array-access), for array access, the method `System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray` is used instead of `Slice`. *end note* + > *Note*: As specified in [§12.7.10.2](expressions.md#128112-array-access), for array access, the method `System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray` is used instead of `Slice`. *end note* - The type does not have an accessible instance indexer taking a `System.Range` as its only argument, or as its first argument with the remaining arguments being optional. The provided instance indexer shall have the same accessibility and return type, including `ref` if present, as `Slice`. From 7c672701df6786eeeb52ad6632a210d01a5d57f1 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Tue, 26 Sep 2023 14:01:16 -0400 Subject: [PATCH 26/32] more section numbers --- standard/classes.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/standard/classes.md b/standard/classes.md index 5d8c1dd3d..37fe04211 100644 --- a/standard/classes.md +++ b/standard/classes.md @@ -5423,7 +5423,7 @@ This allows the context to keep track of how many `void`-returning async functio ### §indexable-sequence-general General -**Note to TG2 reviewers:** Rationale for the choice of the name of the term *indexable sequence*: Various MS-hosted on-line pages use the term *sequence*. This word is already used quite a bit in the C# spec, in both a general sense as well as being defined in the context of query expressions. "§12.17 Query expressions|§13.17.1 General states: A query expression begins with a `from` clause and ends with either a `select` or `group` clause. The initial `from` clause may be followed by zero or more `from`, `let`, `where`, `join` or `orderby` clauses. Each `from` clause is a generator introducing a range variable that ranges over the elements of a sequence. Each `let` clause introduces a range variable representing a value computed by means of previous range variables. …". That definition is *not* applicable to indexes and ranges! The MS-provided proposal uses *collection*; however, that implies enumerable support, which is *not* required by indexes and ranges. (BTW, although it is used a lot in the C# spec, the term *collection* is *not* defined!) As such, rather than overload an existing term or invent a completely different one, I came up with *indexable sequence*. +**Note to TG2 reviewers:** Rationale for the choice of the name of the term *indexable sequence*: Various MS-hosted on-line pages use the term *sequence*. This word is already used quite a bit in the C# spec, in both a general sense as well as being defined in the context of query expressions. "§12.20 Query expressions|§12.20.1 General states: A query expression begins with a `from` clause and ends with either a `select` or `group` clause. The initial `from` clause may be followed by zero or more `from`, `let`, `where`, `join` or `orderby` clauses. Each `from` clause is a generator introducing a range variable that ranges over the elements of a sequence. Each `let` clause introduces a range variable representing a value computed by means of previous range variables. …". That definition is *not* applicable to indexes and ranges! The MS-provided proposal uses *collection*; however, that implies enumerable support, which is *not* required by indexes and ranges. (BTW, although it is used a lot in the C# spec, the term *collection* is *not* defined!) As such, rather than overload an existing term or invent a completely different one, I came up with *indexable sequence*. An ***indexable sequence*** is an ordered set of zero or more elements having the same type. Any given element can be accessed via an index, and a contiguous subset of elements—referred to as a ***slice***—can be denoted via a range. @@ -5519,7 +5519,7 @@ An implementation shall behave as if it provides an instance indexer member with - The type is countable [§15.7.1](classes.md#1571-general). - The type has an accessible instance method named `Slice` taking two arguments of type `int` as the only arguments. For type `string`, the method `Substring` is used instead of `Slice`. - > *Note*: As specified in [§12.7.10.2](expressions.md#128112-array-access), for array access, the method `System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray` is used instead of `Slice`. *end note* + > *Note*: As specified in [§12.8.11.2](expressions.md#128112-array-access), for array access, the method `System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray` is used instead of `Slice`. *end note* - The type does not have an accessible instance indexer taking a `System.Range` as its only argument, or as its first argument with the remaining arguments being optional. The provided instance indexer shall have the same accessibility and return type, including `ref` if present, as `Slice`. From fb5703cfe9c5127674d6c8e72cbba4dbe3714292 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Sun, 25 Aug 2024 11:29:19 -0400 Subject: [PATCH 27/32] Small bits of cleanup --- standard/expressions.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/standard/expressions.md b/standard/expressions.md index 86c0e0e9e..a980859d4 100644 --- a/standard/expressions.md +++ b/standard/expressions.md @@ -148,6 +148,7 @@ 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--` `new` `typeof` `default` `checked` `unchecked` `delegate` `stackalloc` | +> | §range-operator | Range | `..` | > | [§12.9](expressions.md#129-unary-operators) | Unary | `+` `-` `!` `~` `^` `++x` `--x` `(T)x` `await x` | > | [§12.10](expressions.md#1210-arithmetic-operators) | Multiplicative | `*` `/` `%` | > | [§12.10](expressions.md#1210-arithmetic-operators) | Additive | `+` `-` | @@ -2087,7 +2088,7 @@ For an array access, the *primary_no_array_creation_expression* of the *element_ 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*. -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. +The result of evaluating an array access that involves `System.Range` is a slice (§indexable-sequence-general) 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. 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: @@ -2152,7 +2153,6 @@ The run-time processing of an array access of the form `P[A]`, where `P` is a *p > Given the following one-dimensional array: > > -> > ```csharp > string[] seasons = new string[] { "Summer", "Autumn", "Winter", "Spring" }; > string[] names = seasons[0..2]; // slice containing "Summer" and "Autumn" @@ -3692,7 +3692,7 @@ range_expression ; ``` -For an operation of the form `s .. e`, binary operator overload resolution ([§12.4.5](expressions.md#1245-binary-operator-overload-resolution)) is applied to select a specific operator implementation. The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator. Only one predefined range operator exists: +For an operation of the form `s .. e`, binary operator overload resolution ([§12.4.5](expressions.md#1245-binary-operator-overload-resolution)) is applied to select a specific operator implementation. The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator. *range_expression* shall have type `System.Index` or a type that can be converted implicitly to that type. Only one predefined range operator exists: > ```csharp @@ -3702,9 +3702,9 @@ System.Range operator ..(System.Index start = 0, System.Index end = ^0); The left and right operands denote, respectively, a start and end Index. For this operator, an object of (the immutable struct) type `System.Range` is returned that contains those Indexes. If the left operand is omitted, an Index of `0` is used. If the right operand is omitted, an Index of `^0` is used. As such, - `s .. e` is transformed by the implementation to `new System.Range(s, e)`. -- `s ..` is transformed by the implementation to `new System.Range(s, ^0)` (or to `System.Range.StartAt(s)` if that method exists, is accessible, and is declared as follows: `public static Range StartAt (Index start);`). -- `.. e` is transformed by the implementation to `new System.Range(0, e)` (or to `System.Range.EndAt(e)` if that method exists, is accessible, and is declared as follows: `public static Range EndAt (Index end);`). -- `..` is transformed by the implementation to `new System.Range(0, ^0)` (or instead to `System.Range.All` if that property exists, is accessible, and is declared as follows: `public static Range All { get; }`). +- `s ..` is transformed by the implementation to `new System.Range(s, ^0)`. +- `.. e` is transformed by the implementation to `new System.Range(0, e)`. +- `..` is transformed by the implementation to `new System.Range(0, ^0)`. > *Note*: While a Range can be created with a start Index greater than the end Index, any attempt to use that Range to denote a slice from an indexable sequence will result in `System.ArgumentOutOfRangeException`. *end note* From 39819446bb3f95afb21f7668b7d5b459b8af06ef Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Sun, 25 Aug 2024 11:34:03 -0400 Subject: [PATCH 28/32] Make some small improvements --- standard/classes.md | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/standard/classes.md b/standard/classes.md index 37fe04211..148d95d4d 100644 --- a/standard/classes.md +++ b/standard/classes.md @@ -3182,7 +3182,7 @@ In a *ref_property_body* an expression body consisting of `=>` followed by `ref` When a property declaration includes an `extern` modifier, the property is said to be an ***external property***. Because an external property declaration provides no actual implementation, each of its *accessor_declarations* consists of a semicolon. -A type is ***countable*** if it has a property named `Length` or `Count` with an accessible `get` accessor ([§15.7.3]( classes.md#1573-accessors)) and a return type of `int`. +A type is ***countable*** if it has an instance property named `Length` or `Count` with an accessible `get` accessor ([§15.7.3]( classes.md#1573-accessors)) and a return type of `int`. ### 15.7.2 Static and instance properties @@ -5423,8 +5423,6 @@ This allows the context to keep track of how many `void`-returning async functio ### §indexable-sequence-general General -**Note to TG2 reviewers:** Rationale for the choice of the name of the term *indexable sequence*: Various MS-hosted on-line pages use the term *sequence*. This word is already used quite a bit in the C# spec, in both a general sense as well as being defined in the context of query expressions. "§12.20 Query expressions|§12.20.1 General states: A query expression begins with a `from` clause and ends with either a `select` or `group` clause. The initial `from` clause may be followed by zero or more `from`, `let`, `where`, `join` or `orderby` clauses. Each `from` clause is a generator introducing a range variable that ranges over the elements of a sequence. Each `let` clause introduces a range variable representing a value computed by means of previous range variables. …". That definition is *not* applicable to indexes and ranges! The MS-provided proposal uses *collection*; however, that implies enumerable support, which is *not* required by indexes and ranges. (BTW, although it is used a lot in the C# spec, the term *collection* is *not* defined!) As such, rather than overload an existing term or invent a completely different one, I came up with *indexable sequence*. - An ***indexable sequence*** is an ordered set of zero or more elements having the same type. Any given element can be accessed via an index, and a contiguous subset of elements—referred to as a ***slice***—can be denoted via a range. An index is represented by a read-only variable of the value type `System.Index`. A range is represented by a read-only variable of value type `System.Range`, which contains a start and end index. A slice of an array is represented by a (possibly empty) array. The representation of a slice of a user-defined type is determined by the implementer of that type. @@ -5471,7 +5469,7 @@ A type having an instance indexer taking a single argument of type `System.Index An implementation shall behave as if it provides an instance indexer member with a single parameter of type `System.Index` for any type that meets the following criteria: -- The type is countable [§15.7.1](classes.md#1571-general). +- The type is countable ([§15.7.1](classes.md#1571-general)). - The type has an accessible instance indexer taking an argument of type `int` as its only argument, or as its first argument with the remaining arguments being optional. - The type does not have an accessible instance indexer taking a `System.Index` as its only argument, or as its first argument with the remaining arguments being optional. @@ -5517,7 +5515,7 @@ A type having an instance indexer taking a single argument of type `System.Range An implementation shall behave as if it provides an instance indexer member with a single parameter of type `System.Range` for any type that meets the following criteria: -- The type is countable [§15.7.1](classes.md#1571-general). +- The type is countable ([§15.7.1](classes.md#1571-general)). - The type has an accessible instance method named `Slice` taking two arguments of type `int` as the only arguments. For type `string`, the method `Substring` is used instead of `Slice`. > *Note*: As specified in [§12.8.11.2](expressions.md#128112-array-access), for array access, the method `System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray` is used instead of `Slice`. *end note* - The type does not have an accessible instance indexer taking a `System.Range` as its only argument, or as its first argument with the remaining arguments being optional. @@ -5526,8 +5524,6 @@ The provided instance indexer shall have the same accessibility and return type, When the type is indexed with a `System.Range`, the provided instance indexer shall take the given range and pass its start index and length as `int`s to `Slice` (or in the case of `string`, to method `Substring`). -**Note to TG2 reviewers:** The MS proposal, section “Implicit Range support,” provided a very detailed discussion of how to transform a pair of Indexes into a call to `Slice` depending on the form of the range used. Rex did *not* retain this in the final proposal, as he saw no point in doing so. Given a start and end index, it is a simple matter to compute the length **in all cases regardless of range format!**, as he shows in his range indexer implementation in “Explicit range support” above. - > *Note*: See §indexable-sequence-expl-support-for-range for an example of an explicitly provided `Range` indexer. If that were not defined, its equivalent would be provided by the implementation, except that the provided indexer would call `Slice` to create and copy the slice. For type `BitArray`, `Slice` might be defined, as follows: > > @@ -5548,5 +5544,3 @@ When the type is indexed with a `System.Range`, the provided instance indexer sh > ``` > > *end note* - -**Note to TG2 reviewers:** Setter: What if anything should we say about implicit and explicit setter for a Range indexer? Certainly, one can define a setter for a user-defined type; however, it is not obvious as to what such a setter would do, especially since it must be used on the left-hand side of assignment taking a right-hand side of the same type as the index returns. In the case of type `BitArray` that would mean something like `ba1[range1] = ba2`, or perhaps `ba1[range1] = ba2[range2]`. As far as Rex could determine, the operations one might like to implement using such a setter are probably best implemented via a named method. In any event, for a compiler-generated Range indexer, attempting to use its setter results in the error message “CS0131 The left-hand side of an assignment must be a variable, property or indexer,” which suggests the generated indexer **has no setter**. If that is the case, we should say that in the previous section. From 7d53e7b8fcfffeb1fd437f763f98b734bb6be3dc Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Sun, 25 Aug 2024 11:37:45 -0400 Subject: [PATCH 29/32] Remove non-required members --- standard/standard-library.md | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/standard/standard-library.md b/standard/standard-library.md index b770ed336..9e169c051 100644 --- a/standard/standard-library.md +++ b/standard/standard-library.md @@ -463,12 +463,7 @@ namespace System public readonly struct Index : IEquatable { public Index(int value, bool fromEnd = false); - public static Index End { get; } - public static Index Start { get; } - public bool IsFromEnd { get; } public int Value { get; } - public static Index FromEnd(int value); - public static Index FromStart(int value); public bool Equals(Index other); public override bool Equals(object? value); public override int GetHashCode(); @@ -480,16 +475,13 @@ namespace System public struct Range : IEquatable { public Range (Index start, Index end); - public static Range All { get; } public Index End { get; } public Index Start { get; } - public static Range EndAt (Index end); - public override bool Equals (object? value); - public bool Equals (Range other); - public override int GetHashCode (); - public (int,int) GetOffsetAndLength (int length); - public static Range StartAt (Index start); - public override string ToString (); + public override bool Equals(object? value); + public bool Equal (Range other); + public override int GetHashCode(); + public (int,int) GetOffsetAndLength(int length); + public override string ToString(); } } From d1a8feccd8007f19998f77804ee2739947914e40 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Fri, 30 Aug 2024 09:57:27 -0400 Subject: [PATCH 30/32] fix type name ordering --- standard/standard-library.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/standard/standard-library.md b/standard/standard-library.md index f68c13009..9b9c0e7ad 100644 --- a/standard/standard-library.md +++ b/standard/standard-library.md @@ -401,6 +401,13 @@ namespace System public static implicit operator Index(int value); } + public class OperationCanceledException : Exception + { + public OperationCanceledException(); + public OperationCanceledException(string message); + public OperationCanceledException(string message, Exception innerException); + } + public struct Range : IEquatable { public Range (Index start, Index end); @@ -413,13 +420,6 @@ namespace System public override string ToString(); } - public class OperationCanceledException : Exception - { - public OperationCanceledException(); - public OperationCanceledException(string message); - public OperationCanceledException(string message, Exception innerException); - } - public readonly ref struct ReadOnlySpan { public int Length { get; } From faf2eacea8111bb5311e778334498e4acf36da35 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Mon, 11 Nov 2024 13:48:53 -0500 Subject: [PATCH 31/32] describe GetOffset --- standard/expressions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/expressions.md b/standard/expressions.md index 57072f8e0..cc4f92f27 100644 --- a/standard/expressions.md +++ b/standard/expressions.md @@ -2098,7 +2098,7 @@ The result of evaluating an array access that involves `System.Range` is a slice 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: - `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, 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`. +- 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`, which calculates the offset from the start of the collection using the specified collection length. - For an index expression not having type `System.Range`: - 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. From 0b88e742cf49bb122d9c11db76f94f0cde1fedab Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Mon, 11 Nov 2024 13:56:22 -0500 Subject: [PATCH 32/32] some tweaks - added non-virtual requirement to 2 x imp-generated indexers. - described `GetOffset` semantics. - removed erroneous text w.r.t optional trailing params on `this[int]`. --- standard/classes.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/standard/classes.md b/standard/classes.md index a342f50d9..cda81b671 100644 --- a/standard/classes.md +++ b/standard/classes.md @@ -5528,10 +5528,10 @@ A type having an instance indexer taking a single argument of type `System.Index #### §indexable-sequence-impl-support-for-index Implicit Index support -An implementation shall behave as if it provides an instance indexer member with a single parameter of type `System.Index` for any type that meets the following criteria: +An implementation shall behave as if it provides a non-virtual instance indexer member with a single parameter of type `System.Index` for any type that meets the following criteria: - The type is countable ([§15.7.1](classes.md#1571-general)). -- The type has an accessible instance indexer taking an argument of type `int` as its only argument, or as its first argument with the remaining arguments being optional. +- The type has an accessible instance indexer taking an argument of type `int` as its only argument. - The type does not have an accessible instance indexer taking a `System.Index` as its only argument, or as its first argument with the remaining arguments being optional. The provided instance indexer shall have the same get and set members with matching accessibility as the `int` indexer. @@ -5574,7 +5574,7 @@ A type having an instance indexer taking a single argument of type `System.Range #### §indexable-sequence-impl-support-for-range Implicit Range support -An implementation shall behave as if it provides an instance indexer member with a single parameter of type `System.Range` for any type that meets the following criteria: +An implementation shall behave as if it provides a non-virtual instance indexer member with a single parameter of type `System.Range` for any type that meets the following criteria: - The type is countable ([§15.7.1](classes.md#1571-general)). - The type has an accessible instance method named `Slice` taking two arguments of type `int` as the only arguments. For type `string`, the method `Substring` is used instead of `Slice`.