diff --git a/standard/basic-concepts.md b/standard/basic-concepts.md index 2d042bba5..40aaa8ac3 100644 --- a/standard/basic-concepts.md +++ b/standard/basic-concepts.md @@ -731,21 +731,22 @@ Signatures are the enabling mechanism for ***overloading*** of members in classe - Overloading of indexers permits a class, struct, or interface to declare multiple indexers, provided their signatures are unique within that class, struct, or interface. - Overloading of operators permits a class or struct to declare multiple operators with the same name, provided their signatures are unique within that class or struct. -Although `in`, `out`, and `ref` parameter modifiers are considered part of a signature, members declared in a single type cannot differ in signature solely by `in`, `out`, and `ref`. A compile-time error occurs if two members are declared in the same type with signatures that would be the same if all parameters in both methods with `out` or `in` modifiers were changed to `ref` modifiers. For other purposes of signature matching (e.g., hiding or overriding), `in`, `out`, and `ref` are considered part of the signature and do not match each other. +Although *parameter_mode_modifier*s are considered part of a signature, members declared in a single type cannot differ in signature solely by those modifiers. A compile-time error occurs if two members are declared in the same type with signatures that would be the same if all parameters in both methods with `out` or `in` modifiers were changed to `ref` modifiers. For other purposes of signature matching (e.g., hiding or overriding), *parameter_mode_modifier*s are considered part of the signature and do not match each other. -> *Note*: This restriction is to allow C# programs to be easily translated to run on a platform that does not provide a way to define methods that differ solely in `in`, `out`, and `ref`. *end note* +> *Note*: This restriction is to allow C# programs to be easily translated to run on a platform that does not provide a way to define methods that differ solely in their *parameter_mode_modifier*s. *end note* The types `object` and `dynamic` are not distinguished when comparing signatures. Therefore members declared in a single type whose signatures differ only by replacing `object` with `dynamic` are not allowed. > *Example*: The following example shows a set of overloaded method declarations along with their signatures. > -> +> > ```csharp > interface ITest > { > void F(); // F() > void F(int x); // F(int) > void F(ref int x); // F(ref int) +> void F(ref readonly int x); // F(ref int) error > void F(out int x); // F(out int) error > void F(object o); // F(object) > void F(dynamic d); // error. @@ -761,7 +762,7 @@ The types `object` and `dynamic` are not distinguished when comparing signatures > } > ``` > -> Note that any `in`, `out`, and `ref` parameter modifiers ([§15.6.2](classes.md#1562-method-parameters)) are part of a signature. Thus, `F(int)`, `F(in int)`, `F(out int)` , and `F(ref int)` are all unique signatures. However, `F(in int)`, `F(out int)` , and `F(ref int)` cannot be declared within the same interface because their signatures differ solely by `in`, `out`, and `ref`. Also, note that the return type and the `params` modifier are not part of a signature, so it is not possible to overload solely based on return type or on the inclusion or exclusion of the `params` modifier. As such, the declarations of the methods `F(int)` and `F(params string[])` identified above, result in a compile-time error. *end example* +> Note that any *parameter_mode_modifier*s ([§15.6.2](classes.md#1562-method-parameters)) are part of a signature. Thus, `F(int)`, `F(in int)`, `F(out int)` , `F(ref int)`, and `F(ref readonly int)` are all unique signatures. However, `F(in int)`, `F(out int)`, `F(ref int)`, and `F(ref readonly int)` cannot be declared within the same interface because their signatures differ solely by their *parameter_mode_modifier*s. Also, note that the return type and the `params` modifier are not part of a signature, so it is not possible to overload solely based on return type or on the inclusion or exclusion of the `params` modifier. As such, the declarations of the methods `F(int)` and `F(params string[])` identified above, result in a compile-time error. *end example* ## 7.7 Scopes diff --git a/standard/classes.md b/standard/classes.md index 2bda78459..a12dad249 100644 --- a/standard/classes.md +++ b/standard/classes.md @@ -2296,7 +2296,7 @@ parameter_modifier ; parameter_mode_modifier - : 'ref' + : ref_kind | 'out' | 'in' ; @@ -2308,13 +2308,13 @@ parameter_array The parameter list consists of one or more comma-separated parameters of which only the last may be a *parameter_array*. -A *fixed_parameter* consists of an optional set of *attributes* ([§23](attributes.md#23-attributes)); an optional `this` modifier; an optional `scoped` modifier; an optional `in`, `out`, `ref` modifier; a *type*; an *identifier*; and an optional *default_argument*. Each *fixed_parameter* declares a parameter of the given type with the given name. The `this` modifier designates the method as an extension method and is only allowed on the first parameter of a static method in a non-generic, non-nested static class. If the parameter is a `struct` type or a type parameter constrained to a `struct`, the `this` modifier may be combined with either the `ref` or `in` modifier, but not the `out` modifier. Extension methods are further described in [§15.6.10](classes.md#15610-extension-methods). A *fixed_parameter* with a *default_argument* is known as an ***optional parameter***, whereas a *fixed_parameter* without a *default_argument* is a ***required parameter***. A required parameter shall not appear after an optional parameter in a *parameter_list*. +A *fixed_parameter* consists of an optional set of *attributes* ([§23](attributes.md#23-attributes)); an optional `this` modifier; an optional `scoped` modifier; an optional `in`, `out`, `ref` modifier, or `ref readonly`; a *type*; an *identifier*; and an optional *default_argument*. Each *fixed_parameter* declares a parameter of the given type with the given name. The `this` modifier designates the method as an extension method and is only allowed on the first parameter of a static method in a non-generic, non-nested static class. If the parameter is a `struct` type or a type parameter constrained to a `struct`, the `this` modifier may be combined with the `ref`, `ref readonly`, or `in` modifier, but not the `out` modifier. Extension methods are further described in [§15.6.10](classes.md#15610-extension-methods). A *fixed_parameter* with a *default_argument* is known as an ***optional parameter***, whereas a *fixed_parameter* without a *default_argument* is a ***required parameter***. A required parameter shall not appear after an optional parameter in a *parameter_list*. An output parameter implicitly has the `scoped` modifier. For a discussion of `scoped`, see [§9.7.3](variables.md#973-the-scoped-modifier). -A parameter with a `ref`, `out` or `this` modifier cannot have a *default_argument*. An input parameter may have a *default_argument*. The *expression* in a *default_argument* shall be one of the following: +A parameter with a `ref`, `out` or `this` modifier cannot have a *default_argument*. A parameter with an `ref readonly` or `in` modifier may have a *default_argument*; however, in the `ref readonly` case, a warning shall be issued. The *expression* in a *default_argument* shall be one of the following: - a *constant_expression* - an expression of the form `new S()` where `S` is a value type @@ -2358,9 +2358,10 @@ The following kinds of parameters exist: - Input parameters ([§15.6.2.3.2](classes.md#156232-input-parameters)). - Output parameters ([§15.6.2.3.4](classes.md#156234-output-parameters)). - Reference parameters ([§15.6.2.3.3](classes.md#156233-reference-parameters)). +- Reference readonly parameters, which are reference parameters that also have the `readonly` modifier. - Parameter arrays ([§15.6.2.4](classes.md#15624-parameter-arrays)). -> *Note*: As described in [§7.6](basic-concepts.md#76-signatures-and-overloading), the `in`, `out`, and `ref` modifiers are part of a method’s signature, but the `params` and `scoped` modifiers are not. *end note* +> *Note*: As described in [§7.6](basic-concepts.md#76-signatures-and-overloading), the `in`, `out`, `ref`, and `ref readonly` modifiers are part of a method’s signature, but the `params` and `scoped` modifiers are not. *end note* #### 15.6.2.2 Value parameters @@ -2380,11 +2381,11 @@ Input, output, and reference parameters are ***by-reference parameter***s. A by- > *Note*: The referent of a by-reference parameter can be changed using the ref assignment (`= ref`) operator. -When a parameter is a by-reference parameter, the corresponding argument in a method invocation shall consist of the corresponding keyword, `in`, `ref`, or `out`, followed by a *variable_reference* ([§9.5](variables.md#95-variable-references)) of the same type as the parameter. However, when the parameter is an `in` parameter, the argument may be an *expression* for which an implicit conversion ([§10.2](conversions.md#102-implicit-conversions)) exists from that argument expression to the type of the corresponding parameter. +When a parameter is a by-reference parameter, the corresponding argument in a method invocation shall consist of the corresponding keyword, `in`, `ref`, or `out`, followed by a *variable_reference* ([§9.5](variables.md#95-variable-references)) of the same type as the parameter. However, when the parameter is a `ref readonly` or `in` parameter, the argument may be an *expression* for which an implicit conversion ([§10.2](conversions.md#102-implicit-conversions)) exists from that argument expression to the type of the corresponding parameter. By-reference parameters are not allowed on functions declared as an iterator ([§15.15](classes.md#1515-synchronous-and-asynchronous-iterators)) or async function ([§15.14](classes.md#1514-async-functions)). -In a method that takes multiple by-reference parameters, it is possible for multiple names to represent the same storage location. +In a method that has multiple by-reference parameters, it is possible for multiple parameter names to represent the same storage location. ##### 15.6.2.3.2 Input parameters @@ -2396,7 +2397,11 @@ It is a compile-time error to modify the value of an input parameter. ##### 15.6.2.3.3 Reference parameters -A parameter declared with a `ref` modifier is a ***reference parameter***. For definite-assignment rules, see [§9.2.6](variables.md#926-reference-parameters). +A parameter declared with a `ref` or `ref readonly` modifier is a ***reference parameter***. For definite-assignment rules, see [§9.2.6](variables.md#926-reference-parameters). + +It is a compile-time error to modify the value of a `ref readonly` parameter. + +The argument corresponding to a `ref readonly` parameter may be a value, in which case, a variable having that value is created by the implementation ([§12.6.2.3](expressions.md#12623-run-time-evaluation-of-argument-lists)) in the method invocation. > *Example*: The example > @@ -2456,6 +2461,20 @@ A parameter declared with a `ref` modifier is a ***reference parameter***. For d > the invocation of `F` in `G` passes a reference to `s` for both `a` and `b`. Thus, for that invocation, the names `s`, `a`, and `b` all refer to the same storage location, and the three assignments all modify the instance field `s`. > > *end example* + + + +> *Example*: The following example +> +> +> ```csharp +> LargeStruct ls /* init somehow */; +> M(ref ls); +> static void M(ref readonly LargeStruct p) { /* ... */ } +> struct LargeStruct { /* ... */ } +> ``` +> +> shows a large struct being passed by reference for efficiency, but without the called method having the ability to modify that that struct. *end example* For a `struct` type, within an instance method, instance accessor ([§12.2.1](expressions.md#1221-general)), or instance constructor with a constructor initializer, the `this` keyword behaves exactly as a reference parameter of the struct type ([§12.8.14](expressions.md#12814-this-access)). diff --git a/standard/expressions.md b/standard/expressions.md index 0ae84f22b..9c1ed5384 100644 --- a/standard/expressions.md +++ b/standard/expressions.md @@ -553,7 +553,7 @@ Every function member and delegate invocation includes an argument list, which p - For events, the argument list consists of the expression specified as the right operand of the `+=` or `-=` operator. - For user-defined operators, the argument list consists of the single operand of the unary operator or the two operands of the binary operator. -The arguments of properties ([§15.7](classes.md#157-properties)) and events ([§15.8](classes.md#158-events)) are always passed as value parameters ([§15.6.2.2](classes.md#15622-value-parameters)). The arguments of user-defined operators ([§15.10](classes.md#1510-operators)) are always passed as value parameters ([§15.6.2.2](classes.md#15622-value-parameters)) or input parameters ([§9.2.8](variables.md#928-input-parameters)). The arguments of indexers ([§15.9](classes.md#159-indexers)) are always passed as value parameters ([§15.6.2.2](classes.md#15622-value-parameters)), input parameters ([§9.2.8](variables.md#928-input-parameters)), or parameter arrays ([§15.6.2.4](classes.md#15624-parameter-arrays)). Output and reference parameters are not supported for these categories of function members. +The arguments of properties ([§15.7](classes.md#157-properties)) and events ([§15.8](classes.md#158-events)) are always passed as value parameters ([§15.6.2.2](classes.md#15622-value-parameters)). The arguments of user-defined operators ([§15.10](classes.md#1510-operators)) are always passed as value parameters ([§15.6.2.2](classes.md#15622-value-parameters)) or input parameters ([§9.2.8](variables.md#928-input-parameters)). The arguments of indexers ([§15.9](classes.md#159-indexers)) are always passed as value parameters ([§15.6.2.2](classes.md#15622-value-parameters)), input parameters ([§9.2.8](variables.md#928-input-parameters)), reference parameters of kind `ref readonly` ([§15.6.2.3.3](classes.md#156233-reference-parameters)), or parameter arrays ([§15.6.2.4](classes.md#15624-parameter-arrays)). Output and reference parameters of kind `ref` are not supported for these categories of function members. The arguments of an instance constructor, method, indexer, or delegate invocation are specified as an *argument_list*: @@ -582,12 +582,12 @@ An *argument_list* consists of one or more *argument*s, separated by commas. Eac The *argument_value* can take one of the following forms: -- An *expression*, indicating that the argument is passed as a value parameter or is transformed into an input parameter and then passed as that, as determined by ([§12.6.4.2](expressions.md#12642-applicable-function-member) and described in [§12.6.2.3](expressions.md#12623-run-time-evaluation-of-argument-lists). -- The keyword `in` optionally followed by `scoped`, followed by a *variable_reference* ([§9.5](variables.md#95-variable-references)), indicating that the argument is passed as an input parameter ([§15.6.2.3.2](classes.md#156232-input-parameters)). A variable shall be definitely assigned ([§9.4](variables.md#94-definite-assignment)) before it can be passed as an input parameter. For a discussion of `scoped`, see [§9.7.3](variables.md#973-the-scoped-modifier). +- An *expression*, indicating that the argument is passed as a value parameter or is transformed into an input or `ref readonly` reference parameter and then passed as that, as determined by ([§12.6.4.2](expressions.md#12642-applicable-function-member) and described in [§12.6.2.3](expressions.md#12623-run-time-evaluation-of-argument-lists). +- The keyword `in` optionally followed by `scoped`, followed by a *variable_reference* ([§9.5](variables.md#95-variable-references)), indicating that the argument is passed as an input parameter ([§15.6.2.3.2](classes.md#156232-input-parameters)) or is transformed into a `ref readonly` reference parameter ([§15.6.2.3.3](classes.md#156233-reference-parameters)) and then passed as that. A variable shall be definitely assigned ([§9.4](variables.md#94-definite-assignment)) before it can be passed as an input parameter. For a discussion of `scoped`, see [§9.7.3](variables.md#973-the-scoped-modifier). - The keyword `ref` optionally followed by `scoped`, followed by a *variable_reference* ([§9.5](variables.md#95-variable-references)), indicating that the argument is passed as a reference parameter ([§15.6.2.3.3](classes.md#156233-reference-parameters)). A variable shall be definitely assigned ([§9.4](variables.md#94-definite-assignment)) before it can be passed as a reference parameter. For a discussion of `scoped`, see [§9.7.3](variables.md#973-the-scoped-modifier). - The keyword `out` optionally followed by `scoped`, optionally followed by a *variable_reference* ([§9.5](variables.md#95-variable-references)), indicating that the argument is passed as an output parameter ([§15.6.2.3.4](classes.md#156234-output-parameters)). A variable is considered definitely assigned ([§9.4](variables.md#94-definite-assignment)) following a function member invocation in which the variable is passed as an output parameter. For a discussion of `scoped`, see [§9.7.3](variables.md#973-the-scoped-modifier). -The form determines the ***parameter-passing mode*** of the argument: *value*, *input*, *reference*, or *output*, respectively. However, as mentioned above, an argument with value passing mode, might be transformed into one with input passing mode. +The form determines the ***parameter-passing mode*** of the argument: *value*, *input*, *reference*, or *output*, respectively. However, as mentioned above, an argument with value passing mode, might be transformed into one with input or `ref readonly` reference passing mode. Passing a volatile field ([§15.5.4](classes.md#1554-volatile-fields)) as an input, output, or reference parameter causes a warning, since the field cannot be treated as volatile by the invoked method. @@ -622,19 +622,19 @@ During the run-time processing of a function member invocation ([§12.6.6](expre - For a value argument, if the parameter’s passing mode is value - the argument expression is evaluated and an implicit conversion ([§10.2](conversions.md#102-implicit-conversions)) to the corresponding parameter type is performed. The resulting value becomes the initial value of the value parameter in the function member invocation. - - otherwise, the parameter’s passing mode is input. If the argument is a variable reference and there exists an identity conversion ([§10.2.2](conversions.md#1022-identity-conversion)) between the argument’s type and the parameter’s type, the resulting storage location becomes the storage location represented by the parameter in the function member invocation. Otherwise, a storage location is created with the same type as that of the corresponding parameter. The argument expression is evaluated and an implicit conversion ([§10.2](conversions.md#102-implicit-conversions)) to the corresponding parameter type is performed. The resulting value is stored within that storage location. That storage location is represented by the input parameter in the function member invocation. + - otherwise, the parameter’s passing mode is input or ref readonly. If the argument is a variable reference and there exists an identity conversion ([§10.2.2](conversions.md#1022-identity-conversion)) between the argument’s type and the parameter’s type, the resulting storage location becomes the storage location represented by the parameter in the function member invocation. Otherwise, a storage location is created with the same type as that of the corresponding parameter. The argument expression is evaluated and an implicit conversion ([§10.2](conversions.md#102-implicit-conversions)) to the corresponding parameter type is performed. The resulting value is stored within that storage location. That storage location is represented by the input parameter in the function member invocation. > *Example*: Given the following declarations and method calls: > - > + > > ```csharp - > static void M1(in int p1) { ... } + > static void M1(in int p1, ref readonly int p2) { ... } > int i = 10; - > M1(i); // i is passed as an input argument - > M1(i + 5); // transformed to a temporary input argument + > M1(i, i); // i is passed as input and reference arguments + > M1(i + 5, i + 7); // both transformed to temporary arguments > ``` > - > In the `M1(i)` method call, `i` itself is passed as an input argument, because it is classified as a variable and has the same type `int` as the input parameter. In the `M1(i + 5)` method call, an unnamed `int` variable is created, initialized with the argument’s value, and then passed as an input argument. See [§12.6.4.2](expressions.md#12642-applicable-function-member) and [§12.6.4.4](expressions.md#12644-better-parameter-passing-mode). + > In the `M1(i, i)` method call, `i` itself is passed as an input and a reference argument, respectively, because it is classified as a variable and has the same type `int` as the corresponding input and reference parameters. In the `M1(i + 5, i + 7)` method call, two unnamed `int` variables are created, initialized with the corresponding argument’s value, and then passed as an input and reference argument, respectively. See [§12.6.4.2](expressions.md#12642-applicable-function-member) and [§12.6.4.4](expressions.md#12644-better-parameter-passing-mode). > > *end example* @@ -1076,6 +1076,7 @@ A function member is said to be an ***applicable function member*** with respect - for a reference or output parameter, there is an identity conversion between the type of the argument expression (if any) and the type of the corresponding parameter, or - for an input parameter when the corresponding argument has the `in` modifier, there is an identity conversion between the type of the argument expression (if any) and the type of the corresponding parameter, or - for an input parameter when the corresponding argument omits the `in` modifier, an implicit conversion ([§10.2](conversions.md#102-implicit-conversions)) exists from the argument expression to the type of the corresponding parameter. + - for a `ref readonly` parameter when the corresponding argument omits the `ref` modifier, an implicit conversion ([§10.2](conversions.md#102-implicit-conversions)) exists from the argument expression to the type of the corresponding parameter. For a function member that includes a parameter array, if the function member is applicable by the above rules, it is said to be applicable in its ***normal form***. If a function member that includes a parameter array is not applicable in its normal form, the function member might instead be applicable in its ***expanded form***: @@ -1084,7 +1085,7 @@ For a function member that includes a parameter array, if the function member is - the parameter-passing mode of the argument is identical to the parameter-passing mode of the corresponding parameter, and: - for a fixed value parameter or a value parameter created by the expansion, an implicit conversion ([§10.2](conversions.md#102-implicit-conversions)) exists from the argument expression to the type of the corresponding parameter; or - for a by-reference parameter, the type of the argument expression is identical to the type of the corresponding parameter. - - the parameter-passing mode of the argument is value, and the parameter-passing mode of the corresponding parameter is input, and an implicit conversion ([§10.2](conversions.md#102-implicit-conversions)) exists from the argument expression to the type of the corresponding parameter. + - the parameter-passing mode of the argument is value, and the parameter-passing mode of the corresponding parameter is input or `ref readonly`, and an implicit conversion ([§10.2](conversions.md#102-implicit-conversions)) exists from the argument expression to the type of the corresponding parameter. When the implicit conversion from the argument type to the parameter type of an input parameter is a dynamic implicit conversion ([§10.2.10](conversions.md#10210-implicit-dynamic-conversions)), the results are undefined. diff --git a/standard/variables.md b/standard/variables.md index e953e5440..314c4372c 100644 --- a/standard/variables.md +++ b/standard/variables.md @@ -696,7 +696,7 @@ new «type» ( «arg₁», «arg₂», … , «argₓ» ) - For an invocation expression, the definite assignment state of *v* before *primary_expression* is the same as the state of *v* before *expr*. - For an invocation expression, the definite assignment state of *v* before *arg₁* is the same as the state of *v* after *primary_expression*. - For an object creation expression, the definite assignment state of *v* before *arg₁* is the same as the state of *v* before *expr*. -- For each argument *argᵢ*, the definite assignment state of *v* after *argᵢ* is determined by the normal expression rules, ignoring any `in`, `out`, or `ref` modifiers. +- For each argument *argᵢ*, the definite assignment state of *v* after *argᵢ* is determined by the normal expression rules, ignoring any *parameter_mode_modifier*s. - For each argument *argᵢ* for any *i* greater than one, the definite assignment state of *v* before *argᵢ* is the same as the state of *v* after *argᵢ₋₁*. - If the variable *v* is passed as an `out` argument (i.e., an argument of the form “out *v*”) in any of the arguments, then the state of *v* after *expr* is definitely assigned. Otherwise, the state of *v* after *expr* is the same as the state of *v* after *argₓ*. - For array initializers ([§12.8.17.5](expressions.md#128175-array-creation-expressions)), object initializers ([§12.8.17.3](expressions.md#128173-object-initializers)), collection initializers ([§12.8.17.3.1](expressions.md#1281731-collection-initializers)) and anonymous object initializers ([§12.8.17.4](expressions.md#128174-anonymous-object-creation-expressions)), the definite-assignment state is determined by the expansion that these constructs are defined in terms of. @@ -1424,7 +1424,7 @@ For a variable `c` resulting from a ref-returning function invocation, `ref e1.M - The caller-context. - The safe-context ([§16.5.15](structs.md#16515-safe-context-constraint)) contributed by all argument expressions (including the receiver), excluding arguments corresponding to `scoped` parameters and excluding `out` arguments. -- The ref-safe-context contributed by all `ref` arguments, excluding those corresponding to `scoped ref` parameters and excluding `out` arguments. +- The ref-safe-context contributed by all `ref` and `ref readonly` arguments, excluding those corresponding to `scoped ref` parameters and excluding `out` arguments. If `M()` does return ref-to-ref-struct, the ref-safe-context is the narrowest ref-safe-context contributed by all arguments which are ref-to-ref-struct.