diff --git a/standard/basic-concepts.md b/standard/basic-concepts.md index 199b6f978..cfa46bfb9 100644 --- a/standard/basic-concepts.md +++ b/standard/basic-concepts.md @@ -522,9 +522,8 @@ The following accessibility constraints exist: Methods, instance constructors, indexers, and operators are characterized by their ***signatures***: -- The signature of a method consists of the name of the method, the number of type parameters, and the type and parameter-passing mode (value, reference, or output) of each of its formal parameters, considered in the order left to right. For these purposes, any type parameter of the method that occurs in the type of a formal parameter is identified not by its name, but by its ordinal position in the type parameter list of the method. The signature of a method specifically does not include the return type, parameter names, type parameter names, type parameter constraints, the `params` or `this` parameter modifiers, nor whether parameters are required or optional. - -- The signature of an instance constructor consists of the type and parameter-passing mode (value, reference, or output) of each of its formal parameters, considered in the order left to right. The signature of an instance constructor specifically does not include the `params` modifier that may be specified for the right-most parameter. +- The signature of a method consists of the name of the method, the number of type parameters, and the type and parameter-passing mode of each of its formal parameters, considered in the order left to right. For these purposes, any type parameter of the method that occurs in the type of a formal parameter is identified not by its name, but by its ordinal position in the type parameter list of the method. The signature of a method specifically does not include the return type, parameter names, type parameter names, type parameter constraints, the `params` or `this` parameter modifiers, nor whether parameters are required or optional. +- The signature of an instance constructor consists of the type and parameter-passing mode of each of its formal parameters, considered in the order left to right. The signature of an instance constructor specifically does not include the `params` modifier that may be specified for the right-most parameter. - The signature of an indexer consists of the type of each of its formal parameters, considered in the order left to right. The signature of an indexer specifically does not include the element type, nor does it include the `params` modifier that may be specified for the right-most parameter. - The signature of an operator consists of the name of the operator and the type of each of its formal parameters, considered in the order left to right. The signature of an operator specifically does not include the result type. - The signature of a conversion operator consists of the source type and the target type. The implicit or explicit classification of a conversion operator is not part of the signature. @@ -537,9 +536,9 @@ 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 `out` and `ref` parameter modifiers are considered part of a signature, members declared in a single type cannot differ in signature solely by `ref` and `out`. 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` modifiers were changed to `ref` modifiers. For other purposes of signature matching (e.g., hiding or overriding), `ref` and `out` are considered part of the signature and do not match each other. +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. -> *Note*: This restriction is to allow C# programs to be easily translated to run on the Common Language Infrastructure (CLI), which does not provide a way to define methods that differ solely in `ref` and `out`. *end note* +> *Note*: This restriction is to allow C# programs to be easily translated to run on the Common Language Infrastructure (CLI), which does not provide a way to define methods that differ solely in `in`, `out`, and `ref`. *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. @@ -567,9 +566,7 @@ The types `object` and `dynamic` are not distinguished when comparing signatures > } > ``` > -> Note that any `ref` and `out` parameter modifiers ([§15.6.2](classes.md#1562-method-parameters)) are part of a signature. Thus, `F(int)`, `F(ref int)`, and `F(out int)` are all unique signatures. However, `F(ref int)` and `F(out int)` cannot be declared within the same interface because their signatures differ solely by `ref` and `out`. 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 `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* ## 7.7 Scopes diff --git a/standard/classes.md b/standard/classes.md index 5fca21588..9cf33bcc7 100644 --- a/standard/classes.md +++ b/standard/classes.md @@ -776,8 +776,7 @@ A *class_declaration* creates a new declaration space ([§7.3](basic-concepts.md > *Note*: Since the fully qualified name of a type declaration encodes the number of type parameters, two distinct types may share the same name as long as they have different number of type parameters. *end note* - The name of a constant, field, property, or event shall differ from the names of all other members declared in the same class. - -- The name of a method shall differ from the names of all other non-methods declared in the same class. In addition, the signature ([§7.6](basic-concepts.md#76-signatures-and-overloading)) of a method shall differ from the signatures of all other methods declared in the same class, and two methods declared in the same class shall not have signatures that differ solely by `ref` and `out`. +- The name of a method shall differ from the names of all other non-methods declared in the same class. In addition, the signature ([§7.6](basic-concepts.md#76-signatures-and-overloading)) of a method shall differ from the signatures of all other methods declared in the same class, and two methods declared in the same class shall not have signatures that differ solely by `in`, `out`, and `ref`. - The signature of an instance constructor shall differ from the signatures of all other instance constructors declared in the same class, and two constructors declared in the same class shall not have signatures that differ solely by `ref` and `out`. @@ -1910,9 +1909,9 @@ method_declaration ; method_header - : attributes? method_modifier* 'partial'? return_type member_name - type_parameter_list? '(' formal_parameter_list? ')' - type_parameter_constraints_clause* + : attributes? method_modifier* 'partial'? ('ref' 'readonly'?)? + return_type member_name type_parameter_list? + '(' formal_parameter_list? ')' type_parameter_constraints_clause* ; method_modifier @@ -1969,7 +1968,11 @@ A declaration has a valid combination of modifiers if all of the following are t - If the declaration includes the `sealed` modifier, then the declaration also includes the `override` modifier. - If the declaration includes the `partial` modifier, then it does not include any of the following modifiers: new, `public`, `protected`, `internal`, `private`, `virtual`, `sealed`, `override`, `abstract`, or `extern`. -The *return_type* of a method declaration specifies the type of the value computed and returned by the method. The *return_type* is `void` if the method does not return a value. If the declaration includes the `partial` modifier, then the return type shall be `void` ([§15.6.9](classes.md#1569-partial-methods)). If the declaration includes the `async` modifier then the return type shall be `void` or a task type ([§15.15.1](classes.md#15151-general)). +It is a compile-time error to have both `ref` and a *return_type* of `void`. + +If `ref` is present, the method is ***returns-by-ref***; otherwise, if *return_type* is `void`, the method is ***returns-no-value***; otherwise, the method is ***returns-by-value***. + +The *return_type* of a method declaration specifies the type of the result, if any, returned by the method. A returns-no-value method does not return a value. A returns-by-ref method returns a *variable_reference* (§9.5), that is optionally read-only. A returns-by-value method returns a value. If the declaration includes the `partial` modifier, then *return_type* shall be `void` ([§15.6.9](classes.md#1569-partial-methods)). If the declaration includes the `async` modifier then *return_type* shall be `void` or the method returns-by-value and the return type is a *task type* ([§15.15.1](classes.md#15151-general)). A generic method is a method whose declaration includes a *type_parameter_list*. This specifies the type parameters for the method. The optional *type_parameter_constraints_clause*s specify the constraints for the type parameters. A *method_declaration* shall not have *type_parameter_constraints_clauses* unless it also has a *type_parameter_list*. A *method_declaration* for an explicit interface member implementation shall not have any *type_parameter_constraints_clause*s. A generic *method_declaration* for an explicit interface member implementation inherits any constraints from the constraints on the interface method. Similarly, a method declaration with the `override` modifier shall not have any *type_parameter_constraints_clause*s and the constraints of the method’s type parameters are inherited from the virtual method being overridden.The *member_name* specifies the name of the method. Unless the method is an explicit interface member implementation ([§18.6.2](interfaces.md#1862-explicit-interface-member-implementations)), the *member_name* is simply an *identifier*. For an explicit interface member implementation, the *member_name* consists of an *interface_type* followed by a “`.`” and an *identifier*. In this case, the declaration shall include no modifiers other than (possibly) `extern` or `async`. @@ -1985,7 +1988,7 @@ If the *method_body* consists of a semicolon, the declaration shall not include The name, the number of type parameters, and the formal parameter list of a method define the signature ([§7.6](basic-concepts.md#76-signatures-and-overloading)) of the method. Specifically, the signature of a method consists of its name, the number of its type parameters, and the number, *parameter_mode_modifier*s ([§15.6.2.1](classes.md#15621-general)), and types of its formal parameters. The return type is not part of a method’s signature, nor are the names of the formal parameters, the names of the type parameters, or the constraints. When a formal parameter type references a type parameter of the method, the ordinal position of the type parameter (not the name of the type parameter) is used for type equivalence. -The name of a method shall differ from the names of all other non-methods declared in the same class. In addition, the signature of a method shall differ from the signatures of all other methods declared in the same class, and two methods declared in the same class may not have signatures that differ solely by `ref` and `out`. +The name of a method shall differ from the names of all other non-methods declared in the same class. In addition, the signature of a method shall differ from the signatures of all other methods declared in the same class, and two methods declared in the same class may not have signatures that differ solely by `in`, `out`, and `ref`. The method’s *type_parameter*s are in scope throughout the *method_declaration*, and can be used to form types throughout that scope in *return_type*, *method_body*, and *type_parameter_constraints_clause*s but not in *attributes*. @@ -2024,6 +2027,7 @@ parameter_modifier parameter_mode_modifier : 'ref' | 'out' + | 'in' ; parameter_array @@ -2033,9 +2037,9 @@ parameter_array The formal 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* ([§22](attributes.md#22-attributes)); an optional `ref`, `out`, or `this` 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. 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 may not appear after an optional parameter in a *formal_parameter_list*. +A *fixed_parameter* consists of an optional set of *attributes* ([§22](attributes.md#22-attributes)); an optional `in`, `out`, `ref`, or `this` 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 may not appear after an optional parameter in a *formal_parameter_list*. -A parameter with a `ref`, `out` or `this` modifier cannot 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 `in` modifier may have a *default_argument*. 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 @@ -2073,26 +2077,45 @@ A method declaration creates a separate declaration space ([§7.3](basic-concept A method invocation ([§12.8.9.2](expressions.md#12892-method-invocations)) creates a copy, specific to that invocation, of the formal parameters and local variables of the method, and the argument list of the invocation assigns values or variable references to the newly created formal parameters. Within the *block* of a method, formal parameters can be referenced by their identifiers in *simple_name* expressions ([§12.8.4](expressions.md#1284-simple-names)). -There are four kinds of formal parameters: +The following kinds of formal parameters exist: - Value parameters, which are declared without any modifiers. -- Reference parameters, which are declared with the `ref` modifier. +- Input parameters, which are declared with the `in` modifier. - Output parameters, which are declared with the `out` modifier. +- Reference parameters, which are declared with the `ref` modifier. - Parameter arrays, which are declared with the `params` modifier. -> *Note*: As described in [§7.6](basic-concepts.md#76-signatures-and-overloading), the `ref` and `out` modifiers are part of a method’s signature, but the `params` modifier is not. +> *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` modifier is not. #### 15.6.2.2 Value parameters -A parameter declared with no modifiers is a value parameter. A value parameter corresponds to a local variable that gets its initial value from the corresponding argument supplied in the method invocation. +A parameter declared with no modifiers is a value parameter. A value parameter is a local variable that gets its initial value from the corresponding argument supplied in the method invocation. When a formal parameter is a value parameter, the corresponding argument in a method invocation shall be an expression that is implicitly convertible ([§10.2](conversions.md#102-implicit-conversions)) to the formal parameter type. A method is permitted to assign new values to a value parameter. Such assignments only affect the local storage location represented by the value parameter—they have no effect on the actual argument given in the method invocation. +#### §method-input-parameters-new-clause Input parameters + +A parameter declared with an `in` modifier is an input parameter. An input parameter is a local reference variable (§ref-span-safety) that gets its initial referent from the corresponding argument supplied in the method invocation. That argument is either a variable existing at the point of the method invocation, or one created by the implementation ([§12.6.2.3](expressions.md#12623-run-time-evaluation-of-argument-lists)) in the method invocation. + +*Note*: As with reference variables the referent of an input parameter can be changed using the ref assignment (`= ref`) operator, however the value stored in the referent itself cannot be changed. + +When a formal parameter is an input parameter, the corresponding argument in a method invocation shall consist of either the keyword `in` followed by a *variable_reference* (§input-parameters-new-clause) of the same type as the formal parameter, or 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. A variable shall be definitely assigned before it can be passed as an input parameter. + +It is a compile-time error to modify the value of an input parameter. + +Within a method, an input parameter is always considered definitely assigned. + +Input parameters are not allowed on functions declared as an iterator ([§15.14](classes.md#1514-iterators)) or async function (§15.15). + +In a method that takes input parameters, it is possible for multiple names to represent the same storage location. + #### 15.6.2.3 Reference parameters -A parameter declared with a `ref` modifier is a reference parameter. Unlike a value parameter, a reference parameter does not create a new storage location. Instead, a reference parameter represents the same storage location as the variable given as the argument in the method invocation. +A parameter declared with a `ref` modifier is a reference parameter. A reference parameter is a local reference variable (§ref-span-safety) that gets its initial referent from the corresponding argument supplied in the method invocation. + +*Note*: As with reference variables the referent of a reference parameter can be changed using the ref assignment (`= ref`) operator. When a formal parameter is a reference parameter, the corresponding argument in a method invocation shall consist of the keyword `ref` followed by a *variable_reference* ([§9.5](variables.md#95-variable-references)) of the same type as the formal parameter. A variable shall be definitely assigned before it can be passed as a reference parameter. @@ -2161,7 +2184,7 @@ In a method that takes reference parameters, it is possible for multiple names t #### 15.6.2.4 Output parameters -A parameter declared with an `out` modifier is an output parameter. Similar to a reference parameter, an output parameter does not create a new storage location. Instead, an output parameter represents the same storage location as the variable given as the argument in the method invocation. +A parameter declared with an `out` modifier is an output parameter. An output parameter is a local reference variable (§ref-span-safety) that gets its initial referent from the corresponding argument supplied in the method invocation. When a formal parameter is an output parameter, the corresponding argument in a method invocation shall consist of the keyword `out` followed by a *variable_reference* ([§9.5](variables.md#95-variable-references)) of the same type as the formal parameter. A variable need not be definitely assigned before it can be passed as an output parameter, but following an invocation where a variable was passed as an output parameter, the variable is considered definitely assigned. @@ -2222,7 +2245,7 @@ A parameter declared with a `params` modifier is a parameter array. If a formal > *Example*: The types `string[]` and `string[][]` can be used as the type of a parameter array, but the type `string[,]` can not. *end example* -It is not possible to combine the `params` modifier with the modifiers `ref` and `out`. +*Note*: It is not possible to combine the `params` modifier with the modifiers `in`, `out`, or `ref`. *end note* A parameter array permits arguments to be specified in one of two ways in a method invocation: @@ -2920,7 +2943,11 @@ class Customer ### 15.6.10 Extension methods -When the first parameter of a method includes the `this` modifier, that method is said to be an ***extension method***. Extension methods shall only be declared in non-generic, non-nested static classes. The first parameter of an extension method may have no modifiers other than `this`. +When the first parameter of a method includes the `this` modifier, that method is said to be an ***extension method***. Extension methods shall only be declared in non-generic, non-nested static classes. The first parameter of an extension method is restricted, as follows: + +- It may have the parameter modifier `in` only if the parameter has a value type +- It may have the parameter modifier `ref` only if the parameter has a value type or is a generic type constrained to struct +- It shall not be a pointer type. > *Example*: The following is an example of a static class that declares two extension methods: > @@ -2994,11 +3021,13 @@ The ***effective return type*** of a method is `void` if the return type is `voi When the effective return type of a method is `void` and the method has a block body, `return` statements ([§13.10.5](statements.md#13105-the-return-statement)) in the block shall not specify an expression. If execution of the block of a void method completes normally (that is, control flows off the end of the method body), that method simply returns to its caller. -When the effective return type of a method is `void` and the method has an expression body, the expression `E` shall be a *statement_expression*, and the body is exactly equivalent to a statment body of the form `{ E; }`. +When the effective return type of a method is `void` and the method has an expression body, the expression `E` shall be a *statement_expression*, and the body is exactly equivalent to a block body of the form `{ E; }`. + +For a returns-by-value method ([§15.6.1](classes.md#1561-general)), each return statement in that method's body shall specify an expression that is implicitly convertible to the effective return type. -When the effective return type of a method is not `void` and the method has a block body, each return statement in that method’s body shall specify an expression that is implicitly convertible to the effective return type. The endpoint of the method body of a value-returning method shall not be reachable. In other words, in a value-returning method with a block body, control is not permitted to flow off the end of the method body. +For a returns-by-ref method ([§15.6.1](classes.md#1561-general)), each return statement in that method's body shall specify an expression whose type is that of the effective return type, and has a *ref-safe-context* of *caller-context* (§ref-safe-contexts). -When the effective return type of a method is not `void` and the method has an expression body, `E`, the expression shall be implicitly convertible to the effective return type, and the body is exactly equivalent to a block body of the form `{ return E; }`. +For returns-by-value and returns-by-ref methods the endpoint of the method body shall not be reachable. In other words, control is not permitted to flow off the end of the method body. > *Example*: In the following code > @@ -3043,7 +3072,8 @@ Properties are declared using *property_declaration*s: ```ANTLR property_declaration - : attributes? property_modifier* type member_name property_body + : attributes? property_modifier* ('ref' 'readonly'?)? type + member_name property_body ; property_modifier @@ -3077,7 +3107,9 @@ A *property_declaration* may include a set of *attributes* ([§22](attributes.md Property declarations are subject to the same rules as method declarations ([§15.6](classes.md#156-methods)) with regard to valid combinations of modifiers. -The *type* of a property declaration specifies the type of the property introduced by the declaration, and the *member_name* ([§15.6.1](classes.md#1561-general)) specifies the name of the property. Unless the property is an explicit interface member implementation, the *member_name* is simply an *identifier*. For an explicit interface member implementation ([§18.6.2](interfaces.md#1862-explicit-interface-member-implementations)), the *member_name* consists of an *interface_type* followed by a “`.`” and an *identifier*. +The *type* of a property declaration specifies the type of the property introduced by the declaration. If the `ref` modifier is present, the expression returned is a *variable_reference* (§9.4), that is optionally read-only. The *member_name* ([§15.6.1](classes.md#1561-general)) specifies the name of the property. Unless the property is an explicit interface member implementation, the *member_name* is simply an *identifier*. For an explicit interface member implementation ([§18.6.2](interfaces.md#1862-explicit-interface-member-implementations)), the *member_name* consists of an *interface_type* followed by a “`.`” and an *identifier*. + +A ref-returning property shall not have a `set` accessor. The *type* of a property shall be at least as accessible as the property itself ([§7.5.5](basic-concepts.md#755-accessibility-constraints)). @@ -3087,7 +3119,7 @@ An expression body consisting of `=>` followed by an *expression* `E` and a semi A *property_initializer* may only be given for an automatically implemented property ([§15.7.4](classes.md#1574-automatically-implemented-properties)), and causes the initialization of the underlying field of such properties with the value given by the *expression*. -Even though the syntax for accessing a property is the same as that for a field, a property is not classified as a variable. Thus, it is not possible to pass a property as a `ref` or `out` argument. +*Note*: Even though the syntax for accessing a property is the same as that for a field, a property is not classified as a variable. Thus, it is not possible to pass a property as an `in`, `out`, or `ref` argument unless the property itself returns a reference (§ref-span-safety). *end note* 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. @@ -4018,8 +4050,8 @@ indexer_modifier ; indexer_declarator - : type 'this' '[' formal_parameter_list ']' - | type interface_type '.' 'this' '[' formal_parameter_list ']' + : ('ref' 'readonly'?)? type 'this' '[' formal_parameter_list ']' + | ('ref' 'readonly'?)? type interface_type '.' 'this' '[' formal_parameter_list ']' ; indexer_body @@ -4040,6 +4072,8 @@ The *type* of an indexer declaration specifies the element type of the indexer i > *Note:* As indexers are designed to be used in array element-like contexts, the term *element type* as defined for an array is also used with an indexer. *end note* +The *formal_parameter_list* specifies the parameters of the indexer. The formal parameter list of an indexer corresponds to that of a method ([§15.6.2](classes.md#1562-method-parameters)), except that at least one parameter shall be specified, and that the `this`, `out`, and `ref` parameter modifiers are not permitted. + Unless the indexer is an explicit interface member implementation, the *type* is followed by the keyword `this`. For an explicit interface member implementation, the *type* is followed by an *interface_type*, a “`.`”, and the keyword `this`. Unlike other members, indexers do not have user-defined names. The *formal_parameter_list* specifies the parameters of the indexer. The formal parameter list of an indexer corresponds to that of a method ([§15.6.2](classes.md#1562-method-parameters)), except that at least one parameter shall be specified, and that the `this`, `ref`, and `out` parameter modifiers are not permitted. @@ -4056,7 +4090,7 @@ Based on the presence or absence of get and set accessors, an indexer is classif An expression body consisting of “`=>`” followed by an expression `E` and a semicolon is exactly equivalent to the block body `{ get { return E; } }`, and can therefore only be used to specify read-only indexers where the result of the get accessor is given by a single expression. -Even though the syntax for accessing an indexer element is the same as that for an array element, an indexer element is not classified as a variable. Thus, it is not possible to pass an indexer element as a `ref` or `out` argument. +> *Note*: Even though the syntax for accessing an indexer element is the same as that for an array element, an indexer element is not classified as a variable. Thus, it is not possible to pass an indexer element as an `in`, `out`, or `ref` argument unless the indexer itself returns a reference (§ref-span-safety). *end note* The *formal_parameter_list* of an indexer defines the signature ([§7.6](basic-concepts.md#76-signatures-and-overloading)) of the indexer. Specifically, the signature of an indexer consists of the number and types of its formal parameters. The element type and names of the formal parameters are not part of an indexer’s signature. @@ -5053,7 +5087,7 @@ A function member ([§12.6](expressions.md#126-function-members)) implemented us An iterator block may be used as the body of a function member as long as the return type of the corresponding function member is one of the enumerator interfaces ([§15.14.2](classes.md#15142-enumerator-interfaces)) or one of the enumerable interfaces ([§15.14.3](classes.md#15143-enumerable-interfaces)). It may occur as a *method_body*, *operator_body* or *accessor_body*, whereas events, instance constructors, static constructors and finalizers may not be implemented as iterators. -When a function member is implemented using an iterator block, it is a compile-time error for the formal parameter list of the function member to specify any `ref` or `out` parameters. +When a function member is implemented using an iterator block, it is a compile-time error for the formal parameter list of the function member to specify any `in`, `out`, or `ref` parameters, or an parameter of a `ref struct` type. ### 15.14.2 Enumerator interfaces @@ -5169,7 +5203,7 @@ An enumerable object provides an implementation of the `GetEnumerator` methods o A method ([§15.6](classes.md#156-methods)) or anonymous function ([§12.19](expressions.md#1219-anonymous-function-expressions)) with the `async` modifier is called an ***async function***. In general, the term ***async*** is used to describe any kind of function that has the `async` modifier. -It is a compile-time error for the formal parameter list of an async function to specify any `ref` or `out` parameters. +It is a compile-time error for the formal parameter list of an async function to specify any `in`, `out`, or `ref` parameters, or any parameter of a `ref struct` type. The *return_type* of an async method shall be either `void` or a ***task type***. For an async method that returns a value, a task type shall be generic. For an async method that does not return a value, a task type shall not be generic. Such types are referred to in this specification as `«TaskType»` and `«TaskType»`, respectively. (The Standard library types `System.Threading.Tasks.Task` and types constructed from `System.Threading.Tasks.Task` are task types.) A task type shall be a class or struct type that is associated with a ***task builder type*** via the attribute `System.Runtime.CompilerServices.AsyncMethodBuilder`. Such types are referred to in this specification as `«TaskBuilderType»` and `«TaskBuilderType»`. diff --git a/standard/conversions.md b/standard/conversions.md index 15db2859e..9fde39512 100644 --- a/standard/conversions.md +++ b/standard/conversions.md @@ -919,7 +919,7 @@ An implicit conversion exists from a method group ([§12.2](expressions.md#122-e The compile-time application of the conversion from a method group `E` to a delegate type `D` is described in the following. - A single method `M` is selected corresponding to a method invocation ([§12.8.9.2](expressions.md#12892-method-invocations)) of the form `E(A)`, with the following modifications: - - The argument list `A` is a list of expressions, each classified as a variable and with the type and modifier (`ref` or `out`) of the corresponding parameter in the *formal_parameter_list* of `D` — excepting parameters of type `dynamic`, where the corresponding expression has the type `object` instead of `dynamic`. + - The argument list `A` is a list of expressions, each classified as a variable and with the type and modifier (`in`, `out`, or `ref`) of the corresponding parameter in the *formal_parameter_list* of `D` — excepting parameters of type `dynamic`, where the corresponding expression has the type `object` instead of `dynamic`. - The candidate methods considered are only those methods that are applicable in their normal form and do not omit any optional parameters ([§12.6.4.2](expressions.md#12642-applicable-function-member)). Thus, candidate methods are ignored if they are applicable only in their expanded form, or if one or more of their optional parameters do not have a corresponding parameter in `D`. - A conversion is considered to exist if the algorithm of [§12.8.9.2](expressions.md#12892-method-invocations) produces a single best method `M` which is compatible ([§20.4](delegates.md#204-delegate-compatibility)) with `D`. - If the selected method `M` is an instance method, the instance expression associated with `E` determines the target object of the delegate. diff --git a/standard/delegates.md b/standard/delegates.md index 01d33f8bd..0b53454c4 100644 --- a/standard/delegates.md +++ b/standard/delegates.md @@ -12,9 +12,8 @@ A *delegate_declaration* is a *type_declaration* ([§14.7](namespaces.md#147-typ ```ANTLR delegate_declaration - : attributes? delegate_modifier* 'delegate' return_type identifier - variant_type_parameter_list? '(' formal_parameter_list? ')' - type_parameter_constraints_clause* ';' + : attributes? delegate_modifier* 'delegate' ('ref' 'readonly'?)? return_type identifier variant_type_parameter_list? + '(' formal_parameter_list? ')' type_parameter_constraints_clause* ';' ; delegate_modifier @@ -41,7 +40,11 @@ The `public`, `protected`, `internal`, and `private` modifiers control the acces The delegate’s type name is *identifier*. -The optional *formal_parameter_list* specifies the parameters of the delegate, and *return_type* indicates the return type of the delegate. +As with methods ([§15.6.1](classes.md#1561-general)), if `ref` is present, the delegate returns-by-ref; otherwise, if *return_type* is `void`, the delegate returns-no-value; otherwise, the delegate returns-by-value. It is a compile-time error to have both `ref` and a *return_type* of `void`. + +The optional *formal_parameter_list* specifies the parameters of the delegate. + +*return_type* indicates the return type of the delegate. The optional `ref` indicates that a *variable reference* (§9.5) is returned, with the optional `readonly` indicating that that variable is read-only. The optional *variant_type_parameter_list* ([§18.2.3](interfaces.md#1823-variant-type-parameter-lists)) specifies the type parameters to the delegate itself. @@ -83,10 +86,13 @@ Except for instantiation, any operation that can be applied to a class or class A method or delegate type `M` is ***compatible*** with a delegate type `D` if all of the following are true: -- `D` and `M` have the same number of parameters, and each parameter in `D` has the same `ref` or `out` modifiers as the corresponding parameter in `M`. -- For each value parameter (a parameter with no `ref` or `out` modifier), an identity conversion ([§10.2.2](conversions.md#1022-identity-conversion)) or implicit reference conversion ([§10.2.8](conversions.md#1028-implicit-reference-conversions)) exists from the parameter type in `D` to the corresponding parameter type in `M`. -- For each `ref` or `out` parameter, the parameter type in `D` is the same as the parameter type in `M`. -- An identity or implicit reference conversion exists from the return type of `M` to the return type of `D`. +- `D` and `M` have the same number of parameters, and each parameter in `D` has the same `in`, `out`, or `ref` modifiers as the corresponding parameter in `M`. +- For each value parameter, an identity conversion ([§10.2.2](conversions.md#1022-identity-conversion)) or implicit reference conversion ([§10.2.8](conversions.md#1028-implicit-reference-conversions)) exists from the parameter type in `D` to the corresponding parameter type in `M`. +- For each `in`, `out`, or `ref` parameter, the parameter type in `D` is the same as the parameter type in `M`. +- One of the following is true: + - `D` and `M` are both *returns-no-value* + - `D` and `M` are returns-by-value ([§15.6.1](classes.md#1561-general), [§20.2](delegates.md#202-delegate-declarations)), and an identity or implicit reference conversion exists from the return type of `M` to the return type of `D`. + - `D` and `M` are both returns-by-ref, and an identity conversion exists between the return type of `M` and the return type of `D`, and both have a *return_type* preceded by `ref readonly`, or both have a *return_type* preceded by `ref` only. This definition of compatibility allows covariance in return type and contravariance in parameter types. diff --git a/standard/documentation-comments.md b/standard/documentation-comments.md index 80c29b478..0ecc5a82d 100644 --- a/standard/documentation-comments.md +++ b/standard/documentation-comments.md @@ -695,7 +695,7 @@ The documentation generator observes the following rules when it generates the I - For methods and properties with arguments, the argument list follows, enclosed in parentheses. For those without arguments, the parentheses are omitted. The arguments are separated by commas. The encoding of each argument is the same as a CLI signature, as follows: - Arguments are represented by their documentation name, which is based on their fully qualified name, modified as follows: - Arguments that represent generic types have an appended “`'`” character followed by the number of type parameters - - Arguments having the `out` or `ref` modifier have an `@` following their type name. Arguments passed by value or via `params` have no special notation. + - Arguments having the `in`, `out` or `ref` modifier have an `@` following their type name. Arguments passed by value or via `params` have no special notation. - Arguments that are arrays are represented as `[` *lowerbound* `:` *size* `,` … `,` *lowerbound* `:` *size* `]` where the number of commas is the rank less one, and the lower bounds and size of each dimension, if known, are represented in decimal. If a lower bound or size is not specified, it is omitted. If the lower bound and size for a particular dimension are omitted, the “`:`” is omitted as well. Jagged arrays are represented by one “`[]`” per level. - Arguments that have pointer types other than `void` are represented using a `*` following the type name. A `void` pointer is represented using a type name of `System.Void`. - Arguments that refer to generic type parameters defined on types are encoded using the “`` ` ``” character followed by the zero-based index of the type parameter. @@ -854,7 +854,7 @@ namespace Acme } public static void M0() { ... } - public void M1(char c, out float f, ref ValueType v) { ... } + public void M1(char c, out float f, ref ValueType v, in int i) { ... } public void M2(short[] x1, int[,] x2, long[][] x3) { ... } public void M3(long[][] x3, Widget[][,,] x4) { ... } public unsafe void M4(char *pc, Color **pf) { ... } @@ -881,7 +881,7 @@ IDs: "M:Acme.ValueType.M(System.Int32)" "M:Acme.Widget.NestedClass.M(System.Int32)" "M:Acme.Widget.M0" -"M:Acme.Widget.M1(System.Char,System.Single@,Acme.ValueType@)" +"M:Acme.Widget.M1(System.Char,System.Single@,Acme.ValueType@,System.Int32@)" "M:Acme.Widget.M2(System.Int16[],System.Int32[0:,0:],System.Int64[][])" "M:Acme.Widget.M3(System.Int64[][],Acme.Widget[0:,0:,0:][])" "M:Acme.Widget.M4(System.Char*,Color**)" diff --git a/standard/expressions.md b/standard/expressions.md index c3c2720fa..eb56dddc9 100644 --- a/standard/expressions.md +++ b/standard/expressions.md @@ -61,11 +61,13 @@ The following operations in C# are subject to binding: - Object creation: new `C(e₁,...,eᵥ)` - Overloaded unary operators: `+`, `-`, `!`, `~`, `++`, `--`, `true`, `false` - Overloaded binary operators: `+`, `-`, `*`, `/`, `%`, `&`, `&&`, `|`, `||`, `??`, `^`, `<<`, `>>`, `==`, `!=`, `>`, `<`, `>=`, `<=` -- Assignment operators: `=`, `+=`, `-=`, `*=`, `/=`, `%=`, `&=`, `|=`, `^=`, `<<=`, `>>=` +- Assignment operators: `=`, `= ref`, `+=`, `-=`, `*=`, `/=`, `%=`, `&=`, `|=`, `^=`, `<<=`, `>>=` - Implicit and explicit conversions When no dynamic expressions are involved, C# defaults to static binding, which means that the compile-time types of subexpressions are used in the selection process. However, when one of the subexpressions in the operations listed above is a dynamic expression, the operation is instead dynamically bound. +It is a compile time error if a method invocation is dynamically bound and any of the parameters, including the receiver, has the `in` modifier. + ### 12.3.2 Binding-time Static binding takes place at compile-time, whereas dynamic binding takes place at run-time. In the following subclauses, the term ***binding-time*** refers to either compile-time or run-time, depending on when the binding takes place. @@ -145,7 +147,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` | +> | [§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 | `*` `/` `%` | > | [§12.10](expressions.md#1210-arithmetic-operators) | Additive | `+` `-` | @@ -159,7 +161,7 @@ The precedence of an operator is established by the definition of its associated > | [§12.14](expressions.md#1214-conditional-logical-operators) | Conditional OR | `\|\|` | > | [§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 | `=` `*=` `/=` `%=` `+=` `-=` `<<=` `>>=` `&=` `^=` `\|=` `=>` | +> | [§12.21](expressions.md#1221-assignment-operators) and [§12.19](expressions.md#1219-anonymous-function-expressions) | Assignment and lambda expression | `=` `= ref` `*=` `/=` `%=` `+=` `-=` `<<=` `>>=` `&=` `^=` `\|=` `=>` | > > *end note* @@ -529,7 +531,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)), events ([§15.8](classes.md#158-events)), and user-defined operators ([§15.10](classes.md#1510-operators)) are always passed as value parameters ([§15.6.2.2](classes.md#15622-value-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)) or parameter arrays ([§15.6.2.5](classes.md#15625-parameter-arrays)). Reference and output 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 (§input-parameters-new-clause). 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 (§input-parameters-new-clause), or parameter arrays ([§15.6.2.5](classes.md#15625-parameter-arrays)). Output and reference parameters 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*: @@ -548,6 +550,7 @@ argument_name argument_value : expression + | 'in' variable_reference | 'ref' variable_reference | 'out' variable_reference ; @@ -557,13 +560,14 @@ 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 ([§15.6.2.2](classes.md#15622-value-parameters)). +- 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` followed by a *variable_reference* ([§9.5](variables.md#95-variable-references)), indicating that the argument is passed as an input parameter (§method-input-parameters-new-clause). A variable shall be definitely assigned ([§9.4](variables.md#94-definite-assignment)) before it can be passed as an input parameter. - The keyword `ref` 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](classes.md#15623-reference-parameters)). A variable shall be definitely assigned ([§9.4](variables.md#94-definite-assignment)) before it can be passed as a reference parameter. - The keyword `out` 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.4](classes.md#15624-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. -The form determines the ***parameter-passing mode*** of the argument: *value*, *reference*, or *output*, respectively. +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. -Passing a volatile field ([§15.5.4](classes.md#1554-volatile-fields)) as a reference parameter or output parameter causes a warning, since the field may not be treated as volatile by the invoked method. +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 may not be treated as volatile by the invoked method. #### 12.6.2.2 Corresponding parameters @@ -594,8 +598,24 @@ The corresponding parameters for function member arguments are established as fo During the run-time processing of a function member invocation ([§12.6.6](expressions.md#1266-function-member-invocation)), the expressions or variable references of an argument list are evaluated in order, from left to right, as follows: -- For a value 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 becomes the initial value of the value parameter in the function member invocation. -- For a reference or output parameter, the variable reference is evaluated and the resulting storage location becomes the storage location represented by the parameter in the function member invocation. If the variable reference given as a reference or output parameter is an array element of a *reference_type*, a run-time check is performed to ensure that the element type of the array is identical to the type of the parameter. If this check fails, a `System.ArrayTypeMismatchException` is thrown. +- 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) 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 + > public static void M1(in int p1) { … } + > int i = 10; + > M1(i); // i is passed as an input argument + > M1(i + 5); // transformed to a temporary input argument + > ``` + > + > In each 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 §better-argument-passing-mode-new-clause. + > + > *end example* + +- For an input, output, or reference argument, the variable reference is evaluated and the resulting storage location becomes the storage location represented by the parameter in the function member invocation. For an input or reference argument, the variable must be definitely assigned at the point of the method call. If the variable reference given as an output, or reference is an array element of a *reference_type*, a run-time check is performed to ensure that the element type of the array is identical to the type of the parameter. If this check fails, a `System.ArrayTypeMismatchException` is thrown. Methods, indexers, and instance constructors may declare their right-most parameter to be a parameter array ([§15.6.2.5](classes.md#15625-parameter-arrays)). Such function members are invoked either in their normal form or in their expanded form depending on which is applicable ([§12.6.4.2](expressions.md#12642-applicable-function-member)): @@ -631,7 +651,7 @@ The expressions of an argument list are always evaluated in textual order. > > *end example* -The array co-variance rules ([§17.6](arrays.md#176-array-covariance)) permit a value of an array type `A[]` to be a reference to an instance of an array type `B[]`, provided an implicit reference conversion exists from `B` to `A`. Because of these rules, when an array element of a *reference_type* is passed as a reference or output parameter, a run-time check is required to ensure that the actual element type of the array is *identical* to that of the parameter. +The array co-variance rules ([§17.6](arrays.md#176-array-covariance)) permit a value of an array type `A[]` to be a reference to an instance of an array type `B[]`, provided an implicit reference conversion exists from `B` to `A`. Because of these rules, when an array element of a *reference_type* is passed as an output or reference argument, a run-time check is required to ensure that the actual element type of the array is *identical* to that of the parameter. > *Example*: In the following code > @@ -679,7 +699,7 @@ When a function member with a parameter array is invoked in its expanded form wi > > *end example* -When arguments are omitted from a function member with corresponding optional parameters, the default arguments of the function member declaration are implicitly passed. +When arguments are omitted from a function member with corresponding optional parameters, the default arguments of the function member declaration are implicitly passed. (This can involve the creation of a storage location, as described above.) > *Note*: Because these are always constant, their evaluation will not impact the evaluation of the remaining arguments. *end note* @@ -737,7 +757,9 @@ For each of the method arguments `Eᵢ`: - If `Eᵢ` is an anonymous function, an *explicit parameter type inference* ([§12.6.3.8](expressions.md#12638-explicit-parameter-type-inferences)) is made *from* `Eᵢ` *to* `Tᵢ` - Otherwise, if `Eᵢ` has a type `U` and `xᵢ` is a value parameter ([§15.6.2.2](classes.md#15622-value-parameters)) then a *lower-bound inference* ([§12.6.3.10](expressions.md#126310-lower-bound-inferences)) is made *from* `U` *to* `Tᵢ`. -- Otherwise, if `Eᵢ` has a type `U` and `xᵢ` is a reference ([§15.6.2.3](classes.md#15623-reference-parameters)) or output ([§15.6.2.4](classes.md#15624-output-parameters)) parameter then an *exact inference* ([§12.6.3.9](expressions.md#12639-exact-inferences)) is made *from* `U` *to* `Tᵢ`. +- Otherwise, if `Eᵢ` has a type `U` and `xᵢ` is a reference parameter ([§15.6.2.3](classes.md#15623-reference-parameters)), or output parameter ([§15.6.2.4](classes.md#15624-output-parameters)) then an *exact inference* ([§12.6.3.9](expressions.md#12639-exact-inferences)) is made *from* `U` *to* `Tᵢ`. +- Otherwise, if `Eᵢ` has a type `U` and `xᵢ` is an input parameter (§method-input-parameters-new-clause) and `Ei` is an input argument, then an *exact inference* ([§12.6.3.9](expressions.md#12639-exact-inferences)) is made *from* `U` *to* `Tᵢ`. +- Otherwise, if `Eᵢ` has a type `U` and `xᵢ` is an input parameter (§method-input-parameters-new-clause) then a *lower bound inference* ([§12.6.3.10](expressions.md#126310-lower-bound-inferences)) is made *from* `U` *to* `Tᵢ`. - Otherwise, no inference is made for this argument. #### 12.6.3.3 The second phase @@ -980,16 +1002,40 @@ A function member is said to be an ***applicable function member*** with respect - For each argument in `A`, the parameter-passing mode of the argument is identical to the parameter-passing mode of the corresponding parameter, and - for a value parameter or a parameter array, 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 `ref` or `out` parameter, there is an identity conversion between the type of the argument expression (if any) and the type of the corresponding parameter + - for an `in` 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 + - for an `in` parameter when the corresponding argument omits the `in` modifier, an implicit conversion ([§10.2](conversions.md#102-implicit-conversions)), excluding dynamic implicit conversions (§10.2.10) 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***: - The expanded form is constructed by replacing the parameter array in the function member declaration with zero or more value parameters of the element type of the parameter array such that the number of arguments in the argument list `A` matches the total number of parameters. If `A` has fewer arguments than the number of fixed parameters in the function member declaration, the expanded form of the function member cannot be constructed and is thus not applicable. -- Otherwise, the expanded form is applicable if for each argument in `A` 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 `ref` or `out` parameter, there is an identity conversion between the type of the argument expression (if any) and the type of the corresponding parameter. - -Additional rules determine whether a method is applicable or not based on the context of the expression: +- Otherwise, the expanded form is applicable if for each argument in `A`, one of the following is true: + - 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 an `in`, `out`, or `ref` 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)) excluding dynamic implicit conversions (§10.2.10) exists from the argument expression to the type of the corresponding parameter +> *Example*: Given the following declarations and method calls: +> +> ```csharp +> public static void M1(int p1) { … } +> public static void M1(in int p1) { … } +> int i = 10; uint ui = 34U; +> +> M1(in i); // M1(in int) is applicable +> M1(in ui); // no exact type match, so M1(in int) is not applicable +> M1(i); // M1(int) and M1(in int) are applicable +> M1(i + 5); // M1(int) and M1(in int) are applicable +> M1(100u); // no implicit conversion exists, so M1(int) is not applicable +> +> public static void M2(in int p1) { … } +> +> M2(in i); // M2(in int) is applicable +> M2(i); // M2(in int) is applicable +> M2(i + 5); // M2(in int) is applicable +> ``` +> +> *end example* + - A static method is only applicable if the method group results from a *simple_name* or a *member_access* through a type. - An instance method is only applicable if the method group results from a *simple_name*, a *member_access* through a variable or value, or a *base_access*. - If the method group results from a *simple_name*, an instance method is only applicable if `this` access is permitted [§12.8.13](expressions.md#12813-this-access). @@ -1023,7 +1069,22 @@ In case the parameter type sequences `{P₁, P₂, ..., Pᵥ}` and `{Q₁, Q₂ - Recursively, a constructed type is more specific than another constructed type (with the same number of type arguments) if at least one type argument is more specific and no type argument is less specific than the corresponding type argument in the other. - An array type is more specific than another array type (with the same number of dimensions) if the element type of the first is more specific than the element type of the second. - Otherwise if one member is a non-lifted operator and the other is a lifted operator, the non-lifted one is better. -- If neither function member was found to be better, and all parameters of `Mᵥ` have a corresponding argument whereas default arguments need to be substituted for at least one optional parameter in `Mₓ`, then `Mᵥ` is better than `Mₓ`. Otherwise, no function member is better. +- If neither function member was found to be better, and all parameters of `Mᵥ` have a corresponding argument whereas default arguments need to be substituted for at least one optional parameter in `Mₓ`, then `Mᵥ` is better than `Mₓ`. +- If for at least one parameter `Mᵥ` uses the ***better parameter-passing choice*** (§better-argument-passing-mode-new-clause) than the corresponding parameter in `Mₓ` and none of the parameters in `Mₓ` use the better parameter-passing choice than `Mᵥ`, `Mᵥ` is better than `Mₓ`. +- Otherwise, no function member is better. + +#### §better-argument-passing-mode-new-clause Better parameter-passing mode + +It is permitted to have corresponding parameters in two overloaded methods differ only by parameter-passing mode provided one of the two parameters has value-passing mode, as follows: + +```csharp +public static void M1(int p1) { … } +public static void M1(in int p1) { … } +``` + +Given `int i = 10;`, according to [§12.6.4.2](expressions.md#12642-applicable-function-member), the calls `M1(i)` and `M1(i + 5)` result in both overloads being applicable. In such cases, the method with the parameter-passing mode of value is the ***better parameter-passing mode choice***. + +> *Note*: No such choice need exist for arguments of input, output, or reference passing modes, as those arguments only match the exact same parameter passing modes. *end note* #### 12.6.4.4 Better conversion from expression @@ -1222,6 +1283,7 @@ primary_no_array_creation_expression | anonymous_method_expression | pointer_member_access // unsafe code support | pointer_element_access // unsafe code support + | stackalloc_expression ; ``` @@ -1373,22 +1435,22 @@ Six of the lexical rules defined above are *context sensitive* as follows: | *Interpolated_Regular_String_End* | Only recognised after an *Interpolated_Regular_String_Start* and only if any intervening tokens are either *Interpolated_Regular_String_Mid*s or tokens that can be part of *regular_interpolation*s, including tokens for any *interpolated_regular_string_expression*s contained within such interpolations. | | *Interpolated_Verbatim_String_Mid* *Verbatim_Interpolation_Format* *Interpolated_Verbatim_String_End* | Recognition of these three rules follows that of the corresponding rules above with each mentioned *regular* grammar rule replaced by the corresponding *verbatim* one. | -> *Note:* The above rules are context sensitive as their definitions overlap with those of +> *Note*: The above rules are context sensitive as their definitions overlap with those of other tokens in the language. *end note* -> *Note:* The above grammar is not ANTLR-ready due to the context sensitive lexical rules. As with +> *Note*: The above grammar is not ANTLR-ready due to the context sensitive lexical rules. As with other lexer generators ANTLR supports context sensitive lexical rules, for example using its *lexical modes*, but this is an implementation detail and therefore not part of this Standard. *end note* An *interpolated_string_expression* is classified as a value. If it is immediately converted to `System.IFormattable` or `System.FormattableString` with an implicit interpolated string conversion ([§10.2.5](conversions.md#1025-implicit-interpolated-string-conversions)), the interpolated string expression has that type. Otherwise, it has the type `string`. -> *Note:* The differences between the possible types an *interpolated_string_expression* may be determined from the documentation for `System.String` ([§C.2](standard-library.md#c2-standard-library-types-defined-in-isoiec-23271)) and `System.FormattableString` ([§C.3](standard-library.md#c3-standard-library-types-not-defined-in-isoiec-23271)). *end note* +> *Note*: The differences between the possible types an *interpolated_string_expression* may be determined from the documentation for `System.String` ([§C.2](standard-library.md#c2-standard-library-types-defined-in-isoiec-23271)) and `System.FormattableString` ([§C.3](standard-library.md#c3-standard-library-types-not-defined-in-isoiec-23271)). *end note* The meaning of an interpolation, both *regular_interpolation* and *verbatim_interpolation*, is to format the value of the *expression* as a `string` either according to the format specified by the *Regular_Interpolation_Format* or *Verbatim_Interpolation_Format*, or according to a default format for the type of *expression*. The formatted string is then modified by the *interpolation_minimum_width*, if any, to produce the final `string` to be interpolated into the *interpolated_string_expression*. -> *Note:* How the default format for a type is determined is detailed in the documentation for `System.String` ([§C.2](standard-library.md#c2-standard-library-types-defined-in-isoiec-23271)) and `System.FormattableString` ([§C.3](standard-library.md#c3-standard-library-types-not-defined-in-isoiec-23271)). Descriptions of standard formats, which are identical for *Regular_Interpolation_Format* and *Verbatim_Interpolation_Format*, may be found in the documentation for `System.IFormattable` ([§C.4](standard-library.md#c4-format-specifications)) and in other types in the standard library ([§C](standard-library.md#annex-c-standard-library)). *end note* +> *Note*: How the default format for a type is determined is detailed in the documentation for `System.String` ([§C.2](standard-library.md#c2-standard-library-types-defined-in-isoiec-23271)) and `System.FormattableString` ([§C.3](standard-library.md#c3-standard-library-types-not-defined-in-isoiec-23271)). Descriptions of standard formats, which are identical for *Regular_Interpolation_Format* and *Verbatim_Interpolation_Format*, may be found in the documentation for `System.IFormattable` ([§C.4](standard-library.md#c4-format-specifications)) and in other types in the standard library ([§C](standard-library.md#annex-c-standard-library)). *end note* In an *interpolation_minimum_width* the *constant_expression* shall have an implicit conversion to `int`. Let the *field width* be the absolute value of this *constant_expression* and the *alignment* be the sign (positive or negative) of the value of this *constant_expression*: @@ -1740,8 +1802,9 @@ The optional *argument_list* ([§12.6.2](expressions.md#1262-argument-lists)) pr The result of evaluating an *invocation_expression* is classified as follows: -- If the *invocation_expression* invokes a method or delegate that returns void, the result is nothing. An expression that is classified as nothing is permitted only in the context of a *statement_expression* ([§13.7](statements.md#137-expression-statements)) or as the body of a *lambda_expression* ([§12.19](expressions.md#1219-anonymous-function-expressions)). Otherwise a binding-time error occurs. -- Otherwise, the result is a value, with an associated type of the return type of the method or delegate after any type argument substitutions ([§12.8.9.2](expressions.md#12892-method-invocations)) have been performed. If the invocation is of an instance method, and the receiver is of a class type `T`, the associated type is picked from the first declaration or override of the method found when starting with `T` and searching through its base classes. +- If the *invocation_expression* invokes a returns-no-value method ([§15.6.1](classes.md#1561-general)) or a returns-no-value delegate, the result is nothing. An expression that is classified as nothing is permitted only in the context of a *statement_expression* ([§13.7](statements.md#137-expression-statements)) or as the body of a *lambda_expression* ([§12.19](expressions.md#1219-anonymous-function-expressions)). Otherwise, a binding-time error occurs. +- Otherwise, if the *invocation_expression* invokes a returns-by-ref method ([§15.6.1](classes.md#1561-general)) or a returns-by-ref delegate, the result is a variable with an associated type of the return type of the method or delegate. If the invocation is of an instance method, and the receiver is of a class type `T`, the associated type is picked from the first declaration or override of the method found when starting with `T` and searching through its base classes. +- Otherwise, the *invocation_expression* invokes a returns-by-value method ([§15.6.1](classes.md#1561-general)) or returns-by-value delegate, and the result is a value, with an associated type of the return type of the method or delegate. If the invocation is of an instance method, and the receiver is of a class type `T`, the associated type is picked from the first declaration or override of the method found when starting with `T` and searching through its base classes. #### 12.8.9.2 Method invocations @@ -1968,7 +2031,7 @@ element_access ; ``` -The *argument_list* of an *element_access* is not allowed to contain `ref` or `out` arguments. +The *argument_list* of an *element_access* is not allowed to contain `out` or `ref` arguments. An *element_access* is dynamically bound ([§12.3.3](expressions.md#1233-dynamic-binding)) if at least one of the following holds: @@ -2069,7 +2132,9 @@ A *this_access* is permitted only in the *block* of an instance constructor, an - If the constructor declaration has no constructor initializer, the `this` variable behaves exactly the same as an `out` parameter of the struct type. In particular, this means that the variable shall be definitely assigned in every execution path of the instance constructor. - Otherwise, the `this` variable behaves exactly the same as a `ref` parameter of the struct type. In particular, this means that the variable is considered initially assigned. - When `this` is used in a *primary_expression* within an instance method or instance accessor of a struct, it is classified as a variable. The type of the variable is the instance type ([§15.3.2](classes.md#1532-the-instance-type)) of the struct within which the usage occurs. - - If the method or accessor is not an iterator ([§15.14](classes.md#1514-iterators)) or async function ([§15.15](classes.md#1515-async-functions)), the `this` variable represents the struct for which the method or accessor was invoked, and behaves exactly the same as a `ref` parameter of the struct type. + - If the method or accessor is not an iterator ([§15.14](classes.md#1514-iterators)) or async function ([§15.15](classes.md#1515-async-functions)), the `this` variable represents the struct for which the method or accessor was invoked. + - If the struct is a `readonly struct`, the `this` variable behaves exactly the same as an `in` parameter of the struct type + - Otherwise the `this` variable behaves exactly the same as a `ref` parameter of the struct type - If the method or accessor is an iterator or async function, the `this` variable represents a *copy* of the struct for which the method or accessor was invoked, and behaves exactly the same as a *value* parameter of the struct type. Use of `this` in a *primary_expression* in a context other than the ones listed above is a compile-time error. In particular, it is not possible to refer to `this` in a static method, a static property accessor, or in a *variable_initializer* of a field declaration. @@ -2992,6 +3057,84 @@ A *default_value_expression* is a constant expression ([§12.23](expressions.md# - one of the following value types: `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `char`, `float`, `double`, `decimal`, `bool,`; or - any enumeration type. +### §stack-allocation Stack allocation + +A stack allocation expression allocates a block of memory from the execution stack. The ***execution stack*** is an area of memory where local variables are stored. The execution stack is not part of the managed heap. The memory used for local variable storage is automatically recovered when the current function returns. + +The safe context rules for a stack allocation expression are described in §safe-context-rules-stackalloc. + +```ANTLR +stackalloc_expression + : 'stackalloc' unmanaged_type '[' expression ']' + | 'stackalloc' unmanaged_type? '[' expression? ']' stackalloc_initializer + ; + +stackalloc_initializer + : '{' stackalloc_initializer_element_list '}' + ; + +stackalloc_initializer_element_list + : stackalloc_element_initializer (',' stackalloc_element_initializer)* ','? + ; + +stackalloc_element_initializer + : expression + ; +``` + +The *unmanaged_type* ([§8.8](types.md#88-unmanaged-types)) indicates the type of the items that will be stored in the newly allocated location, and the *expression* indicates the number of these items. Taken together, these specify the required allocation size. As the size of a stack allocation cannot be negative, it is a compile-time error to specify the number of items as a *constant_expression* that evaluates to a negative value. + +If *unmanaged_type* is omitted, it is inferred from the corresponding *stackalloc_initializer*. If *expression* is omitted from *stackalloc_expression*, it is inferred to be the number of *stackalloc_element_initializer*s in the corresponding *stackalloc_initializer* following the rules for best common type (§12.6.3.15) of the *stackalloc_initializer_element_list*. + +When a *stackalloc_expression* includes both *expression* and *stackalloc_initializer*, the *expression* shall be a *constant_expression* and the number of elements in that *stackalloc_initializer* shall match the value of *expression*. + +A stack allocation initializer of the form `stackalloc T[E]` requires `T` to be an *unmanaged_type* and `E` to be an expression implicitly convertible to type `int`. The operator allocates `E * sizeof(T)` bytes from the call stack. The result is a pointer, of type `T*`, to the newly allocated block. For use in safe contexts, a *stackalloc_expression* has an implicit conversion from `T*` to `Span`. As pointer contexts require unsafe code, see §stack-allocation for more information. + +If `E` is a negative value, then the behavior is undefined. If `E` is zero, then no allocation is made, and the value returned is implementation-defined. If there is not enough memory available to allocate a block of the given size, a `System.StackOverflowException` is thrown. + +When *stackalloc_initializer* is present, the *stackalloc_initializer_element_list* shall consist of a sequence of expressions, each having an implicit conversion to *unmanaged_type* ([§10.2](conversions.md#102-implicit-conversions)). The expressions initialize elements in the allocated memory in increasing order, starting with the element at index zero. In the absence of a *stackalloc_initializer*, the content of the newly allocated memory is undefined. + +Access via an instance of `System.Span` to the elements of an allocated block is range checked. + +Stack allocation initializers are not permitted in `catch` or `finally` blocks ([§13.11](statements.md#1311-the-try-statement)). + +> *Note*: There is no way to explicitly free memory allocated using `stackalloc`. *end note* + +All stack-allocated memory blocks created during the execution of a function member are automatically discarded when that function member returns. + +Except for the `stackalloc` operator, C# provides no predefined constructs for managing non-garbage collected memory. Such services are typically provided by supporting class libraries or imported directly from the underlying operating system. + +> *Example*: +> +> ```csharp +> // Memory uninitialized +> Span span1 = stackalloc int[3]; +> // Memory initialized +> Span span2 = stackalloc int[3] { -10, -15, -30 }; +> // Type int is inferred +> Span span3 = stackalloc[] { 11, 12, 13 }; +> // Error; result is int*, not allowed in a safe context +> var span4 = stackalloc[] { 11, 12, 13 }; +> // Error; no conversion from Span to Span +> Span span5 = stackalloc[] { 11, 12, 13 }; +> // Converts 11 and 13, and returns Span +> Span span6 = stackalloc[] { 11, 12L, 13 }; +> // Converts all and returns Span +> Span span7 = stackalloc long[] { 11, 12, 13 }; +> // Implicit conversion of Span +> ReadOnlySpan span8 = stackalloc int[] { 10, 22, 30 }; +> // Implicit conversion of Span +> Widget span9 = stackalloc double[] { 1.2, 5.6 }; +> +> public class Widget +> { +> public static implicit operator Widget(Span sp) { return null; } +> } +> ``` +> +> In the case of `span8`, `stackalloc` results in a `Span`, which is converted by an implicit operator to `ReadOnlySpan`. Similarly, for `span9`, the resulting `Span` is converted to the user-defined type `Widget using the conversion, as shown. +> *end example* + ### 12.8.21 Nameof expressions A *nameof_expression* is used to obtain the name of a program entity as a constant string. @@ -4517,7 +4660,8 @@ A declaration expression with the identifier `_` is a discard ([§9.2.8.1](varia > > var s1 = M(out int i1, "One", out var b1); > Console.WriteLine($"{i1}, {b1}, {s1}"); -> var s2 = M(out var i2, M(out i2, "Two", out bool b2), out b2); // Error: i2 referenced within declaring argument list +> // Error: i2 referenced within declaring argument list +> var s2 = M(out var i2, M(out i2, "Two", out bool b2), out b2); > var s3 = M(out int _, "Three", out var _); > ``` > @@ -4557,9 +4701,12 @@ The `?:` operator is called the conditional operator. It is at times also calle conditional_expression : null_coalescing_expression | null_coalescing_expression '?' expression ':' expression + | null_coalescing_expression '?' 'ref' variable_reference ':' 'ref' variable_reference ; ``` +A throw expression (§12.16) is not allowed in a conditional operator if `ref` is present. + A conditional expression of the form `b ? x : y` first evaluates the condition `b`. Then, if `b` is `true`, `x` is evaluated and becomes the result of the operation. Otherwise, `y` is evaluated and becomes the result of the operation. A conditional expression never evaluates both `x` and `y`. The conditional operator is right-associative, meaning that operations are grouped from right to left. @@ -4568,7 +4715,14 @@ The conditional operator is right-associative, meaning that operations are group The first operand of the `?:` operator shall be an expression that can be implicitly converted to `bool`, or an expression of a type that implements `operator true`. If neither of these requirements is satisfied, a compile-time error occurs. -The second and third operands, `x` and `y`, of the `?:` operator control the type of the conditional expression. If both `x` and `y` are *default_literal*s ([§12.8.20](expressions.md#12820-default-value-expressions)), a compile-time error occurs. +If `ref` is present: + +- An identity conversion must exist between the types of the two *variable_reference*s, and type of the result can be either type. If either type is `dynamic`, type inference prefers `dynamic` (§8.7). +- The result is a variable reference, which is writeable if both *variable_reference*s are writeable. + +> *Note:* When `ref` is present, the *conditional_expression* returns a variable reference, which can be assigned to a reference variable using the `= ref` operator or passed as a reference/input/output parameter. *end note* + +If `ref` is not present, the second and third operands, `x` and `y`, of the `?:` operator control the type of the conditional expression: - If `x` has type `X` and `y` has type `Y` then, - If `X` and `Y` are the same type, then this is the type of the conditional expression. @@ -4580,6 +4734,14 @@ The second and third operands, `x` and `y`, of the `?:` operator control the t - If only one of `x` and `y` has a type, and both `x` and `y` are implicitly convertible to that type, then that is the type of the conditional expression. - Otherwise, no expression type can be determined, and a compile-time error occurs. +The run-time processing of a ref conditional expression of the form `b ? ref x : ref y` consists of the following steps: + +- First, `b` is evaluated, and the `bool` value of `b` is determined: + - If an implicit conversion from the type of `b` to `bool` exists, then this implicit conversion is performed to produce a `bool` value. + - Otherwise, the `operator true` defined by the type of `b` is invoked to produce a `bool` value. +- If the `bool` value produced by the step above is `true`, then `x` is evaluated and the resulting variable reference becomes the result of the conditional expression. +- Otherwise, `y` is evaluated and the resulting variable reference becomes the result of the conditional expression. + The run-time processing of a conditional expression of the form `b ? x : y` consists of the following steps: - First, `b` is evaluated, and the `bool` value of `b` is determined: @@ -4626,6 +4788,7 @@ explicit_anonymous_function_parameter anonymous_function_parameter_modifier : 'ref' | 'out' + | 'in' ; implicit_anonymous_function_signature @@ -4645,6 +4808,7 @@ implicit_anonymous_function_parameter anonymous_function_body : null_conditional_invocation_expression | expression + | 'ref' variable_reference | block ; ``` @@ -4713,7 +4877,8 @@ Note also that conversion to an expression tree type, even if compatible, may st The body (*expression* or *block*) of an anonymous function is subject to the following rules: - If the anonymous function includes a signature, the parameters specified in the signature are available in the body. If the anonymous function has no signature it can be converted to a delegate type or expression type having parameters ([§10.7](conversions.md#107-anonymous-function-conversions)), but the parameters cannot be accessed in the body. -- Except for `ref` or `out` parameters specified in the signature (if any) of the nearest enclosing anonymous function, it is a compile-time error for the body to access a `ref` or `out` parameter. +- Except for `in`, `out`, or `ref` parameters specified in the signature (if any) of the nearest enclosing anonymous function, it is a compile-time error for the body to access an `in`, `out`, or `ref` parameter. +- Except for parameters specified in the signature (if any) of the nearest enclosing anonymous function, it is a compile-time error for the body to access a parameter of a `ref struct` type. - When the type of `this` is a struct type, it is a compile-time error for the body to access `this`. This is true whether the access is explicit (as in `this.x`) or implicit (as in `x` where `x` is an instance member of the struct). This rule simply prohibits such access and does not affect whether member lookup results in a member of the struct. - The body has access to the outer variables ([§12.19.6](expressions.md#12196-outer-variables)) of the anonymous function. Access of an outer variable will reference the instance of the variable that is active at the time the *lambda_expression* or *anonymous_method_expression* is evaluated ([§12.19.7](expressions.md#12197-evaluation-of-anonymous-function-expressions)). - It is a compile-time error for the body to contain a `goto` statement, a `break` statement, or a `continue` statement whose target is outside the body or within the body of a contained anonymous function. @@ -6042,7 +6207,7 @@ The methods above use the generic delegate types `Func` and `Func + { + } +} +namespace System +{ + public ref struct Span + { + public static implicit operator ReadOnlySpan(Span span); + } +} +``` + ## C.4 Format Specifications The meaning of the formats, as used in interpolated string expressions ([§12.8.3](expressions.md#1283-interpolated-string-expressions)), are defined in ISO/IEC 23271:2012. For convenience the following text is copied from the description of `System.IFormatable`. diff --git a/standard/statements.md b/standard/statements.md index e0e6cdf6f..8c0869952 100644 --- a/standard/statements.md +++ b/standard/statements.md @@ -298,7 +298,7 @@ A *local_variable_declaration* declares one or more local variables. ```ANTLR local_variable_declaration - : local_variable_type local_variable_declarators + : ('ref' 'readonly'?)? local_variable_type local_variable_declarators ; local_variable_type @@ -318,20 +318,24 @@ local_variable_declarator local_variable_initializer : expression + | 'ref' variable_reference | array_initializer - | stackalloc_initializer // unsafe code support ; ``` -*stackalloc_initializer* ([§23.9](unsafe-code.md#239-stack-allocation)) is only available in unsafe code ([§23](unsafe-code.md#23-unsafe-code)). +The *local_variable_type* of a *local_variable_declaration* either directly specifies the type of the variables introduced by the declaration, or indicates with the identifier `var` that the type should be inferred based on an initializer. The type is followed by a list of *local_variable_declarator*s, each of which introduces a new variable. A *local_variable_declarator* consists of an *identifier* that names the variable, optionally followed by an “`=`” token and a *local_variable_initializer* that gives the initial value of the variable. However, it is a compile-time error to omit *local_variable_initializer* from a *local_variable_declarator* for a variable declared `ref` or `ref readonly`. -The *local_variable_type* of a *local_variable_declaration* either directly specifies the type of the variables introduced by the declaration, or indicates with the identifier `var` that the type should be inferred based on an initializer. The type is followed by a list of *local_variable_declarator*s, each of which introduces a new variable. A *local_variable_declarator* consists of an *identifier* that names the variable, optionally followed by an “`=`” token and a *local_variable_initializer* that gives the initial value of the variable. +A *local_variable_initializer* for a variable declared `ref` or `ref readonly` shall be of the form “`ref` *variable_reference*”. It is a compile time error if the scope of the local variable is wider than the ref-safe-context of the *variable_reference* (§ref-safe-contexts). + +If *local_variable_declaration* contains `ref readonly`, the *identifier*s being declared are references to variables that are treated as read-only, and their corresponding *local_variable_initializer*s shall each contain `ref`. Otherwise, if *local_variable_declaration* contains `ref` without `readonly`, the *identifier*s being declared are references to variables that shall be writable, and their corresponding *local_variable_initializer* shall each contain `ref`. + +It is a compile-time error to declare a local variable `ref` or `ref readonly` or a variable of a `ref struct` type within a method declared with the *method_modifier* `async`, or an iterator (§15.14). In the context of a local variable declaration, the identifier `var` acts as a contextual keyword ([§6.4.4](lexical-structure.md#644-keywords)).When the *local_variable_type* is specified as `var` and no type named `var` is in scope, the declaration is an ***implicitly typed local variable declaration***, whose type is inferred from the type of the associated initializer expression. Implicitly typed local variable declarations are subject to the following restrictions: - The *local_variable_declaration* cannot include multiple *local_variable_declarator*s. - The *local_variable_declarator* shall include a *local_variable_initializer*. -- The *local_variable_initializer* shall be an *expression*. +- The *local_variable_initializer* shall be an *expression*, optionally preceded by `ref` or `ref readonly`. - The initializer *expression* shall have a compile-time type. - The initializer *expression* cannot refer to the declared variable itself @@ -352,7 +356,7 @@ The value of a local variable is obtained in an expression using a *simple_name* The scope of a local variable declared in a *local_variable_declaration* is the block in which the declaration occurs. It is an error to refer to a local variable in a textual position that precedes the *local_variable_declarator* of the local variable. Within the scope of a local variable, it is a compile-time error to declare another local variable or constant with the same name. -A local variable declaration that declares multiple variables is equivalent to multiple declarations of single variables with the same type. Furthermore, a variable initializer in a local variable declaration corresponds exactly to an assignment statement that is inserted immediately after the declaration. +A local variable declaration that declares multiple variables is equivalent to multiple declarations of single variables with the same type and `ref`/`ref readonly` prefix. Furthermore, a variable initializer in a local variable declaration corresponds exactly to an assignment statement that is inserted immediately after the declaration. > *Example*: The example > @@ -389,6 +393,8 @@ In an implicitly typed local variable declaration, the type of the local variabl > var d = 1.0; > var numbers = new int[] {1, 2, 3}; > var orders = new Dictionary(); +> ref var j = ref i; +> ref readonly var k = ref i; > ``` > > The implicitly typed local variable declarations above are precisely equivalent to the following explicitly typed declarations: @@ -400,6 +406,8 @@ In an implicitly typed local variable declaration, the type of the local variabl > double d = 1.0; > int[] numbers = new int[] {1, 2, 3}; > Dictionary orders = new Dictionary(); +> ref int j = ref i; +> ref int var k = ref i; > ``` > > *end example* @@ -442,7 +450,7 @@ local_function_declaration ; local_function_header - : local_function_modifier* return_type identifier type_parameter_list? + : local_function_modifier* ('ref' 'readonly'?)? return_type identifier type_parameter_list? ( formal_parameter_list? ) type_parameter_constraints_clause* ; local_function_modifier @@ -1029,12 +1037,16 @@ The `foreach` statement enumerates the elements of a collection, executing an em ```ANTLR foreach_statement - : 'foreach' '(' local_variable_type identifier 'in' expression ')' - embedded_statement + : 'foreach' '(' ('ref' 'readonly'?)? local_variable_type identifier 'in' + expression ')' embedded_statement ; ``` -The *local_variable_type* and *identifier* of a `foreach` statement declare the ***iteration variable*** of the statement. If the `var` identifier is given as the *local_variable_type*, and no type named var is in scope, the iteration variable is said to be an ***implicitly typed iteration variable***, and its type is taken to be the iteration type of the `foreach` statement, as specified below. The iteration variable corresponds to a read-only local variable with a scope that extends over the embedded statement. During execution of a `foreach` statement, the iteration variable represents the collection element for which an iteration is currently being performed. A compile-time error occurs if the embedded statement attempts to modify the iteration variable (via assignment or the `++` and `--` operators) or pass the iteration variable as a `ref` or `out` parameter. +The *local_variable_type* and *identifier* of a foreach statement declare the ***iteration variable*** of the statement. If the `var` identifier is given as the *local_variable_type*, and no type named `var` is in scope, the iteration variable is said to be an ***implicitly typed iteration variable***, and its type is taken to be the element type of the `foreach` statement, as specified below. + +If the *foreach_statement* contains both or neither `ref` and `readonly`, the iteration variable denotes a variable that is treated as read-only. Otherwise, if *foreach_statement* contains `ref` without `readonly`, the iteration variable denotes a variable that shall be writable. + +The iteration variable corresponds to a local variable with a scope that extends over the embedded statement. During execution of a `foreach` statement, the iteration variable represents the collection element for which an iteration is currently being performed. If the iteration variable denotes a read-only variable, a compile-time error occurs if the embedded statement attempts to modify it (via assignment or the `++` and `--` operators) or pass it as a `ref` or `out` parameter. In the following, for brevity, `IEnumerable`, `IEnumerator`, `IEnumerable` and `IEnumerator` refer to the corresponding types in the namespaces `System.Collections` and `System.Collections.Generic`. @@ -1386,17 +1398,31 @@ Because a `goto` statement unconditionally transfers control elsewhere, the end ### 13.10.5 The return statement -The `return` statement returns control to the current caller of the function member in which the return statement appears. +The `return` statement returns control to the current caller of the function member in which the return statement appears, optionally returning a value or a *variable_reference* (§10.5). ```ANTLR return_statement - : 'return' expression? ';' + : 'return' ';' + | 'return' expression ';' + | 'return' 'ref' variable_reference ';' ; ``` -A function member is said to ***compute a value*** if it is a method with a non-`void` result type ([§15.6.11](classes.md#15611-method-body)), the get accessor of a property or indexer, or a user-defined operator. Function members that do not compute a value are methods with the effective return type `void`, set accessors of properties and indexers, add and remove accessors of event, instance constructors, static constructors and finalizers. +A *return_statement* without *expression* is called a ***return-no-value***; one containing `ref` *expression* is called a ***return-by-ref***; and one containing only *expression* is called a ***return-by-value***. + +It is a compile-time error to use a return-no-value from a method declared as being returns-by-value or returns-by-ref ([§15.6.1](classes.md#1561-general)). + +It is a compile-time error to use a return-by-ref from a method declared as being returns-no-value or returns-by-value. + +It is a compile-time error to use a return-by-value from a method declared as being returns-no-value or returns-by-ref. + +It is a compile-time error to use a return-by-ref if *expression* is not a *variable_reference* or is a reference to a variable whose ref-safe-context is not caller-context (§ref-safe-contexts). + +It is a compile-time error to use a return-by-ref from a method declared with the *method_modifier* `async`. + +A function member is said to ***compute a value*** if it is a method with a returns-by-value method ([§15.6.11](classes.md#15611-method-body)), a returns-by-value `get` accessor of a property or indexer, or a user-defined operator. Function members that are returns-no-value do not compute a value and are methods with the effective return type `void`, `set` accessors of properties and indexers, `add` and `remove` accessors of event, instance constructors, static constructors and finalizers. Function members that are returns-by-ref do not compute a value. -Within a function member, a `return` statement with no expression can only be used if the function member does not compute a value. Within a function member, a `return` statement with an expression can only be used if the function member computes a value. Where the `return` statement includes an expression, an implicit conversion ([§10.2](conversions.md#102-implicit-conversions)) shall exist from the type of the expression to the effective return type of the containing function member. +For a return-by-value, an implicit conversion ([§10.2](conversions.md#102-implicit-conversions)) shall exist from the type of *expression* to the effective return type ([§15.6.11](classes.md#15611-method-body)) of the containing function member. For a return-by-ref, an identity conversion ([§10.2.2](conversions.md#1022-identity-conversion)) shall exist between the type of *expression* and the effective return type of the containing function member. `return` statements can also be used in the body of anonymous function expressions ([§12.19](expressions.md#1219-anonymous-function-expressions)), and participate in determining which conversions exist for those functions ([§10.7.1](conversions.md#1071-general)). @@ -1404,7 +1430,7 @@ It is a compile-time error for a `return` statement to appear in a `finally` blo A `return` statement is executed as follows: -- If the `return` statement specifies an expression, the expression is evaluated and its value is converted to the effective return type of the containing function by an implicit conversion. The result of the conversion becomes the result value produced by the function. +- For a return-by-value, *expression* is evaluated and its value is converted to the effective return type of the containing function by an implicit conversion. The result of the conversion becomes the result value produced by the function. For a return-by-ref, a reference to the *variable_reference* designated by *expression* becomes the result produced by the function. That result is a variable. If the enclosing method’s return-by-ref includes `readonly`, the resulting variable is read-only. - If the `return` statement is enclosed by one or more `try` or `catch` blocks with associated `finally` blocks, control is initially transferred to the `finally` block of the innermost `try` statement. When and if control reaches the end point of a `finally` block, control is transferred to the `finally` block of the next enclosing `try` statement. This process is repeated until the `finally` blocks of all enclosing `try` statements have been executed. - If the containing function is not an async function, control is returned to the caller of the containing function along with the result value, if any. - If the containing function is an async function, control is returned to the current caller, and the result value, if any, is recorded in the return task as described in ([§15.15.3](classes.md#15153-evaluation-of-a-task-returning-async-function)). diff --git a/standard/structs.md b/standard/structs.md index af144198d..0eb2dc890 100644 --- a/standard/structs.md +++ b/standard/structs.md @@ -16,18 +16,20 @@ A *struct_declaration* is a *type_declaration* ([§14.7](namespaces.md#147-type- ```ANTLR struct_declaration - : attributes? struct_modifier* 'partial'? 'struct' + : attributes? struct_modifier* 'ref'? 'partial'? 'struct' identifier type_parameter_list? struct_interfaces? type_parameter_constraints_clause* struct_body ';'? ; ``` -A *struct_declaration* consists of an optional set of *attributes* ([§22](attributes.md#22-attributes)), followed by an optional set of *struct_modifier*s ([§16.2.2](structs.md#1622-struct-modifiers)), followed by an optional partial modifier ([§15.2.7](classes.md#1527-partial-declarations)), followed by the keyword `struct` and an *identifier* that names the struct, followed by an optional *type_parameter_list* specification ([§15.2.3](classes.md#1523-type-parameters)), followed by an optional *struct_interfaces* specification ([§16.2.4](structs.md#1624-struct-interfaces)), followed by an optional *type_parameter_constraints-clauses* specification ([§15.2.5](classes.md#1525-type-parameter-constraints)), followed by a *struct_body* ([§16.2.5](structs.md#1625-struct-body)), optionally followed by a semicolon. +A *struct_declaration* consists of an optional set of *attributes* ([§22](attributes.md#22-attributes)), followed by an optional set of *struct_modifier*s ([§16.2.2](structs.md#1622-struct-modifiers)), followed by an optional `ref` modifier (§ref-modifier-new-clause), followed by an optional partial modifier ([§15.2.7](classes.md#1527-partial-declarations)), followed by the keyword `struct` and an *identifier* that names the struct, followed by an optional *type_parameter_list* specification ([§15.2.3](classes.md#1523-type-parameters)), followed by an optional *struct_interfaces* specification ([§16.2.4](structs.md#1624-struct-interfaces)), followed by an optional *type_parameter_constraints-clauses* specification ([§15.2.5](classes.md#1525-type-parameter-constraints)), followed by a *struct_body* ([§16.2.5](structs.md#1625-struct-body)), optionally followed by a semicolon. A struct declaration shall not supply a *type_parameter_constraints_clauses* unless it also supplies a *type_parameter_list*. A struct declaration that supplies a *type_parameter_list* is a generic struct declaration. Additionally, any struct nested inside a generic class declaration or a generic struct declaration is itself a generic struct declaration, since type arguments for the containing type shall be supplied to create a constructed type ([§8.4](types.md#84-constructed-types)). +A struct declaration that includes a `ref` keyword shall not have a *struct_interfaces* part. + ### 16.2.2 Struct modifiers A *struct_declaration* may optionally include a sequence of *struct_modifier*s: @@ -60,6 +62,28 @@ A readonly struct has the following constraints: When an instance of a readonly struct is passed to a method, its `this` is treated like an `in` argument/parameter, which disallows write access to any instance fields (except by constructors). +### §ref-modifier-new-clause Ref modifier + +The `ref` modifier indicates that the *struct_declaration* declares a type whose instances are allocated on the execution stack. These types are are called ***ref struct*** types. The `ref` modifier declares that instances may contain ref-like fields, and may not be copied out of its safe-context (§safe-context-rules). The rules for determining the safe context of a ref struct are described in §safe-context-rules. + +It is a compile-time error if a ref struct type is used in any of the following contexts: + +- As the element type of an array. +- As the declared type of a field of a class or a struct that does not have the `ref` modifier. +- Being boxed to `System.ValueType` or `System.Object`: +- As a type argument. +- An async method. +- An iterator. +- There is no conversion from a `ref struct` type to the type `object` or the type `System.ValueType`. +- A `ref struct` type shall not be declared to implement any interface. +- An instance method declared in `object` or in `System.ValueType` but not overridden in a `ref struct` type shall not be called with a receiver of that `ref struct` type. +- An instance method of a `ref struct` type shall not be captured by method group conversion to a delegate type. +- A ref struct shall not be captured by a lambda expression or a local function. + +> *Note*: A `ref struct` shall not declare `async` instance methods nor use a `yield return` or `yield break` statement within an instance method, because the implicit `this` parameter cannot be used in those contexts. *end note* + +These constraints ensure that a variable of `ref struct` type does not refer to stack memory that is no longer valid, or to variables that are no longer valid. + ### 16.2.3 Partial modifier The `partial` modifier indicates that this *struct_declaration* is a partial type declaration. Multiple partial struct declarations with the same name within an enclosing namespace or type declaration combine to form one struct declaration, following the rules specified in [§15.2.7](classes.md#1527-partial-declarations). @@ -160,7 +184,7 @@ A variable of a struct type directly contains the data of the struct, whereas a > > *end example* -With classes, it is possible for two variables to reference the same object, and thus possible for operations on one variable to affect the object referenced by the other variable. With structs, the variables each have their own copy of the data (except in the case of `ref` and `out` parameter variables), and it is not possible for operations on one to affect the other. Furthermore, except when explicitly nullable ([§8.3.12](types.md#8312-nullable-value-types)), it is not possible for values of a struct type to be `null`. +With classes, it is possible for two variables to reference the same object, and thus possible for operations on one variable to affect the object referenced by the other variable. With structs, the variables each have their own copy of the data (except in the case of `in`, `out` and `ref` parameter variables), and it is not possible for operations on one to affect the other. Furthermore, except when explicitly nullable ([§8.3.12](types.md#8312-nullable-value-types)), it is not possible for values of a struct type to be `null`. > *Note*: If a struct contains a field of reference type then the contents of the object referenced can be altered by other operations. However the value of the field itself, i.e., which object it references, cannot be changed through a mutation of a different struct value. *end note* @@ -211,7 +235,7 @@ Function members in a struct cannot be abstract or virtual, and the `override` m Assignment to a variable of a struct type creates a *copy* of the value being assigned. This differs from assignment to a variable of a class type, which copies the reference but not the object identified by the reference. -Similar to an assignment, when a struct is passed as a value parameter or returned as the result of a function member, a copy of the struct is created. A struct may be passed by reference to a function member using a `ref` or `out` parameter. +Similar to an assignment, when a struct is passed as a value parameter or returned as the result of a function member, a copy of the struct is created. A struct may be passed by reference to a function member using an `in`, `out`, or `ref` parameter. When a property or indexer of a struct is the target of an assignment, the instance expression associated with the property or indexer access shall be classified as a variable. If the `instance` expression is classified as a value, a compile-time error occurs. This is described in further detail in [§12.21.2](expressions.md#12212-simple-assignment). @@ -476,3 +500,73 @@ Static constructors for structs follow most of the same rules as for classes. Th Automatically implemented properties ([§15.7.4](classes.md#1574-automatically-implemented-properties)) use hidden backing fields, which are only accessible to the property accessors. > *Note*: This access restriction means that constructors in structs containing automatically implemented properties often need an explicit constructor initializer where they would not otherwise need one, to satisfy the requirement of all fields being definitely assigned before any function member is invoked or the constructor returns. *end note* + +### §safe-context-rules Safe context constraint for ref struct types + +#### §safe-context-rules-general General + +At compile-time, each expression whose type is a ref struct is associated with a context where that instance and all its fields can be safely accessed, its ***safe-context***. The safe-context is a context, enclosing an expression, which it is safe for the value to escape to. + +The safe-context records which context a ref struct may be copied into. Given an assignment from an expression `E1` with a safe-context `S1`, to an expression `E2` with safe-context `S2`, it is an error if `S2` is a wider context than `S1`. + +There are three different safe-context values, the same as the ref-safe-context values defined for reference variables (§ref-safe-contexts): declaration-block, function-member, and caller-context. An expression `e1` of a ref struct type is constrained by its safe-context as follows: + +- For a return statement `return e1`, the safe-context of `e1` must be caller-context. +- For an assignment `e1 = e2` the safe-context of `e2` must be at least as wide a context as the safe-context of `e1`. + +For a method invocation if there is a `ref` or `out` argument of a `ref struct` type (including the receiver unless the type is `readonly`), with safe-context `S1`, then no argument (including the receiver) may have a narrower safe-context than `S1`. + +Any expression whose compile-time type is not a ref struct has a safe-context of caller-context. + +A `default` expression, for any type, has safe-context of caller-context. + +#### §safe-context-rules-parameter Parameter safe context + +A formal parameter of a ref struct type, including the `this` parameter of an instance method, has a safe-context of caller-context. + +#### §safe-context-rules-local Local variable safe context + +A local variable of a ref struct type has a safe-context as follows: + +- If the variable is an iteration variable of a `foreach` loop, then the variable's safe-context is the same as the safe-context of the `foreach` loop's expression. +- Otherwise if the variable's declaration has an initializer then the variable's safe-context is the same as the safe-context of that initializer. +- Otherwise the variable is uninitialized at the point of declaration and has a safe-context of caller-context. + +#### §safe-context-rules-field Field safe context + +A reference to a field `e.F`, where the type of `F` is a ref struct type, has a safe-context that is the same as the safe-context of `e`. + +#### §safe-context-rules-operator Operators + +The application of a user-defined operator is treated as a method invocation (§safe-context-method-invocation). + +For an operator that yields a value, such as `e1 + e2` or `c ? e1 : e2`, the safe-context of the result is the narrowest context among the safe-contexts of the operands of the operator. As a consequence, for a unary operator that yields a value, such as `+e`, the safe-context of the result is the safe-context of the operand. + +> *Note*: The first operand of a conditional operator is a `bool`, so its safe-context is caller-context. It follows that the resulting safe-context is the narrowest safe-context of the second and third operand. *end note* + +#### §safe-context-method-invocation Method and property invocation + +A value resulting from a method invocation `e1.M(e2, ...)` has safe-context of the smallest of the following contexts: + +- calling-method. +- The safe-context of all argument expressions (including the receiver). + +A property invocation (either `get` or `set`) is treated as a method invocation of the underlying method by the above rules. + +#### §safe-context-rules-stackalloc stackalloc + +The result of a stackalloc expression has safe-context of current-method. + +#### §safe-context-rules-constructor Constructor invocations + +A `new` expression that invokes a constructor obeys the same rules as a method invocation that is considered to return the type being constructed. + +In addition the safe-context is the smallest of the safe-contexts of all arguments and operands of all object initializer expressions, recursively, if any initializer is present. + +> *Note*: These rules rely on `Span` not having a constructor of the following form: +> +> ```csharp +> public Span(ref T p) +> ``` +> +> Such a constructor makes instances of `Span` used as fields indistinguishable from a `ref` field. The safety rules described in this document depend on `ref` fields not being a valid construct in C# or .NET. *end note* diff --git a/standard/types.md b/standard/types.md index 9453101d4..b341ab8a5 100644 --- a/standard/types.md +++ b/standard/types.md @@ -412,7 +412,8 @@ Tuple elements are public fields with the names `Item1`, `Item2`, etc., and can > (int, string word) pair2 = (2, "Two"); > (int number, string word) pair3 = (3, "Three"); > (int Item1, string Item2) pair4 = (4, "Four"); -> (int Item2, string Item123) pair5 = (5, "Five"); // Error: "Item" names do not match their position +> // Error: "Item" names do not match their position +> (int Item2, string Item123) pair5 = (5, "Five"); > (int, string) pair6 = new ValueTuple(6, "Six"); > ValueTuple pair7 = (7, "Seven"); > Console.WriteLine($"{pair2.Item1}, {pair2.Item2}, {pair2.word}"); diff --git a/standard/unsafe-code.md b/standard/unsafe-code.md index 94ba08f93..3e7baebc3 100644 --- a/standard/unsafe-code.md +++ b/standard/unsafe-code.md @@ -267,7 +267,7 @@ In precise terms, a fixed variable is one of the following: All other variables are classified as moveable variables. -A static field is classified as a moveable variable. Also, a `ref` or `out` parameter is classified as a moveable variable, even if the argument given for the parameter is a fixed variable. Finally, a variable produced by dereferencing a pointer is always classified as a fixed variable. +A static field is classified as a moveable variable. Also, an `in`, `out`, or `ref` parameter is classified as a moveable variable, even if the argument given for the parameter is a fixed variable. Finally, a variable produced by dereferencing a pointer is always classified as a fixed variable. ## 23.5 Pointer conversions @@ -1047,30 +1047,24 @@ When the outermost containing struct variable of a fixed-size buffer member is a ## 23.9 Stack allocation -In an unsafe context, a local variable declaration ([§13.6.2](statements.md#1362-local-variable-declarations)) may include a stack allocation initializer, which allocates memory from the call stack. +See §stack-allocation for general information about the operator `stackalloc`. Here, the ability of that operator to result in a pointer is discussed. -```ANTLR -stackalloc_initializer - : 'stackalloc' unmanaged_type '[' expression ']' - ; -``` - -The *unmanaged_type* ([§8.8](types.md#88-unmanaged-types)) indicates the type of the items that will be stored in the newly allocated location, and the *expression* indicates the number of these items. Taken together, these specify the required allocation size. Since the size of a stack allocation cannot be negative, it is a compile-time error to specify the number of items as a *constant_expression* that evaluates to a negative value. - -A stack allocation initializer of the form stackalloc `T[E]` requires `T` to be an unmanaged type ([§8.8](types.md#88-unmanaged-types)) and `E` to be an expression implicitly convertible to type `int`. The construct allocates `E * sizeof(T)` bytes from the call stack and returns a pointer, of type `T*`, to the newly allocated block. If `E` is a negative value, then the behavior is undefined. If `E` is zero, then no allocation is made, and the pointer returned is implementation-defined. If there is not enough memory available to allocate a block of the given size, a `System.StackOverflowException` is thrown. - -The content of the newly allocated memory is undefined. - -Stack allocation initializers are not permitted in `catch` or `finally` blocks ([§13.11](statements.md#1311-the-try-statement)). - -> *Note*: There is no way to explicitly free memory allocated using stackalloc. *end note* - -All stack-allocated memory blocks created during the execution of a function member are automatically discarded when that function member returns. +> *Example*: +> +> ```csharp +> int* p1 = stackalloc int[3]; // memory uninitialized +> int* p2 = stackalloc int[3] { -10, -15, -30 }; // memory initialized +> int* p3 = stackalloc[] { 11, 12, 13 }; // type int is inferred +> var p4 = stackalloc[] { 11, 12, 13 }; // can't infer context, so pointer result assumed +> long* p5 = stackalloc[] { 11, 12, 13 }; // error; no conversion exists +> long* p6 = stackalloc[] { 11, 12L, 13 }; // converts 11 and 13, and returns long* +> long* p7 = stackalloc long[] { 11, 12, 13 }; // converts all and returns long* +> ``` +> +> *end example* -> *Note*: This corresponds to the `alloca` function, an extension commonly found in C and C++ implementations. *end note* - +Unlike access to arrays, access to the elements of a `stackalloc`ed block is an unsafe operation and is not range checked. - > *Example*: In the following code > > @@ -1109,10 +1103,34 @@ All stack-allocated memory blocks created during the execution of a function mem > } > ``` > -> a `stackalloc` initializer is used in the `IntToString` method to allocate a buffer of 16 characters on the stack. The buffer is automatically discarded when the method returns. +> a `stackalloc` expression is used in the `IntToString` method to allocate a buffer of 16 characters on the stack. The buffer is automatically discarded when the method returns. +> +> Note, however, that `IntToString` can be rewritten in safe mode; that is, without using pointers, as follows: +> +> ```csharp +> public static string IntToString(int value) +> { +> if (value == int.MinValue) +> { +> return "-2147483648"; // this value has no positive equivalent +> } +> int n = value >= 0 ? value : -value; +> Span buffer = stackalloc char[16]; +> int idx = 16; +> do +> { +> buffer[--idx] = (char)(n % 10 + '0'); +> n /= 10; +> } while (n != 0); +> if (value < 0) +> { +> buffer[--idx] = '-'; +> } +> return buffer.Slice(idx).ToString(); +> } +> +> ``` > > *end example* -Except for the `stackalloc` operator, C# provides no predefined constructs for managing non-garbage collected memory. Such services are typically provided by supporting class libraries or imported directly from the underlying operating system. - **End of conditionally normative text.** diff --git a/standard/variables.md b/standard/variables.md index 7f4e6f858..8412bb599 100644 --- a/standard/variables.md +++ b/standard/variables.md @@ -12,7 +12,7 @@ As described in the following subclauses, variables are either ***initially assi ### 9.2.1 General -C# defines seven categories of variables: static variables, instance variables, array elements, value parameters, reference parameters, output parameters, and local variables. The subclauses that follow describe each of these categories. +C# defines eight categories of variables: static variables, instance variables, array elements, value parameters, input parameters, reference parameters, output parameters, and local variables. The subclauses that follow describe each of these categories. > *Example*: In the following code > @@ -23,17 +23,15 @@ C# defines seven categories of variables: static variables, instance variables, > public static int x; > int y; > -> void F(int[] v, int a, ref int b, out int c) +> void F(int[] v, int a, ref int b, out int c, in int d) > { > int i = 1; -> c = a + b++; +> c = a + b++ + d; > } > } > ``` > -> `x` is a static variable, `y` is an instance variable, `v[0]` is an array element, `a` is a value parameter, `b` is a reference parameter, `c` is an output parameter, and `i` is a local variable. -> -> *end example* +> `x` is a static variable, `y` is an instance variable, `v[0]` is an array element, `a` is a value parameter, `b` is a reference parameter, `c` is an output parameter, `d` is an input parameter, and `i` is a local variable. *end example* ### 9.2.2 Static variables @@ -73,7 +71,7 @@ For the purpose of definite-assignment checking, an array element is considered ### 9.2.5 Value parameters -A parameter declared without a `ref` or `out` modifier is a ***value parameter***. +A parameter declared without an `in`, `out`, or `ref` modifier is a ***value parameter***. A value parameter comes into existence upon invocation of the function member (method, instance constructor, accessor, or operator) or anonymous function to which the parameter belongs, and is initialized with the value of the argument given in the invocation. A value parameter normally ceases to exist when execution of the function body completes. However, if the value parameter is captured by an anonymous function ([§12.19.6.2](expressions.md#121962-captured-outer-variables)), its lifetime extends at least until the delegate or expression tree created from that anonymous function is eligible for garbage collection. @@ -83,7 +81,7 @@ For the purpose of definite-assignment checking, a value parameter is considered A parameter declared with a `ref` modifier is a ***reference parameter***. -A reference parameter does not create a new storage location. Instead, a reference parameter represents the same storage location as the variable given as the argument in the function member, anonymous function, or local function invocation. Thus, the value of a reference parameter is always the same as the underlying variable. +A reference parameter is a reference variable (§ref-span-safety) which comes into existence upon invocation of the function member, delegate, anonymous function, or local function and its referent is initialized to the variable given as the argument in that invocation. A reference parameter ceases to exist when execution of the function body completes. Unlike value parameters a reference parameter may not be captured (§ref-span-safety-limitations). The following definite-assignment rules apply to reference parameters. @@ -98,7 +96,7 @@ For a `struct` type, within an instance method or instance accessor ([§12.2.1]( A parameter declared with an `out` modifier is an ***output parameter***. -An output parameter does not create a new storage location. Instead, an output parameter represents the same storage location as the variable given as the argument in the function member or delegate invocation. Thus, the value of an output parameter is always the same as the underlying variable. +An output parameter is a reference variable (§ref-span-safety) which comes into existence upon invocation of the function member, delegate, anonymous function, or local function and its referent is initialized to the variable given as the argument in that invocation. An output parameter ceases to exist when execution of the function body completes. Unlike value parameters an output parameter may not be captured (§ref-span-safety-limitations). The following definite-assignment rules apply to output parameters. @@ -109,7 +107,16 @@ The following definite-assignment rules apply to output parameters. - Within a function member or anonymous function, an output parameter is considered initially unassigned. - Every output parameter of a function member, anonymous function, or local function shall be definitely assigned ([§9.4](variables.md#94-definite-assignment)) before the function member, anonymous function, or local function returns normally. -Within an instance constructor of a struct type, the `this` keyword behaves exactly as an output or reference parameter of the struct type, depending on whether the constructor declaration includes a constructor initializer ([§12.8.13](expressions.md#12813-this-access)). +### §input-parameters-new-clause Input parameters + +A parameter declared with an `in` modifier is an ***input parameter***. + +An input parameter is a reference variable (§ref-span-safety) which comes into existence upon invocation of the function member, delegate, anonymous function, or local function and its referent is initialized to the *variable_reference* given as the argument in that invocation. An input parameter ceases to exist when execution of the function body completes. Unlike value parameters an input parameter may not be captured (§ref-span-safety-limitations). + +The following definite assignment rules apply to input parameters. + +- A variable shall be definitely assigned ([§9.4](variables.md#94-definite-assignment)) before it can be passed as an input parameter in a function member or delegate invocation. +- Within a function member, anonymous function, or local function an input parameter is considered initially assigned. ### 9.2.8 Local variables @@ -226,6 +233,8 @@ Definite assignment is a requirement in the following contexts: - the variable is a *struct_type* variable and occurs as the left operand of a member access. - A variable shall be definitely assigned at each location where it is passed as a reference parameter. > *Note*: This ensures that the function member being invoked can consider the reference parameter initially assigned. *end note* +- A variable shall be definitely assigned at each location where it is passed as an input parameter. + > *Note*: This ensures that the function member being invoked can consider the input parameter initially assigned. *end note* - All output parameters of a function member shall be definitely assigned at each location where the function member returns (through a return statement or through execution reaching the end of the function member body). > *Note*: This ensures that function members do not return undefined values in output parameters, thus enabling the compiler to consider a function member invocation that takes a variable as an output parameter equivalent to an assignment to the variable. *end note* - The `this` variable of a *struct_type* instance constructor shall be definitely assigned at each location where that instance constructor returns. @@ -240,6 +249,7 @@ The following categories of variables are classified as initially assigned: - Array elements. - Value parameters. - Reference parameters. +- Input parameters. - Variables declared in a `catch` clause or a `foreach` statement. ### 9.4.3 Initially unassigned variables @@ -666,11 +676,11 @@ or an object-creation expression *expr* of the form: 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 `ref` or `out` modifiers. -- 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ᵢ₋₁*. +- 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ᵢ* 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.16.5](expressions.md#128165-array-creation-expressions)), object initializers ([§12.8.16.3](expressions.md#128163-object-initializers)), collection initializers ([§12.8.16.4](expressions.md#128164-collection-initializers)) and anonymous object initializers ([§12.8.16.7](expressions.md#128167-anonymous-object-creation-expressions)), the definite-assignment state is determined by the expansion that these constructs are defined in terms of. @@ -845,9 +855,9 @@ For an expression *expr* of the form: For a *lambda_expression* or *anonymous_method_expression* *expr* with a body (either *block* or *expression*) *body*: -- The definite-assignment state of a parameter is the same as for a parameter of a named method ([§9.2.6](variables.md#926-reference-parameters), [§9.2.7](variables.md#927-output-parameters)). -- The definite-assignment state of an outer variable *v* before *body* is the same as the state of *v* before *expr*. That is, definite-assignment state of outer variables is inherited from the context of the anonymous function. -- The definite-assignment state of an outer variable *v* after *expr* is the same as the state of *v* before *expr*. +- The definite assignment state of a parameter is the same as for a parameter of a named method ([§9.2.6](variables.md#926-reference-parameters), [§9.2.7](variables.md#927-output-parameters), §input-parameters-new-clause). +- The definite assignment state of an outer variable *v* before *body* is the same as the state of *v* before *expr*. That is, definite assignment state of outer variables is inherited from the context of the anonymous function. +- The definite assignment state of an outer variable *v* after *expr* is the same as the state of *v* before *expr*. > *Example*: The example > @@ -988,3 +998,225 @@ variable_reference ## 9.6 Atomicity of variable references Reads and writes of the following data types shall be atomic: `bool`, `char`, `byte`, `sbyte`, `short`, `ushort`, `uint`, `int`, `float`, and reference types. In addition, reads and writes of enum types with an underlying type in the previous list shall also be atomic. Reads and writes of other types, including `long`, `ulong`, `double`, and `decimal`, as well as user-defined types, need not be atomic. Aside from the library functions designed for that purpose, there is no guarantee of atomic read-modify-write, such as in the case of increment or decrement. + +## §ref-span-safety Reference variables and returns + +### §ref-span-safety-general General + +A ***reference variable*** is a variable that refers to another variable, called the referent (§9.2.6). A reference variable is a local variable declared with the `ref` modifier. + +A reference variable stores a *variable_reference* (§9.6) to its referent and not the value of its referent. When a reference variable is used where a value is required its referent's value is returned; similarly when a reference variable is the target of an assignment it is the referent which is assigned to. The variable to which a reference variable refers, i.e. the stored *variable_reference* for its referent, can be changed using a ref assignment (`= ref`). + +> *Example:* The following example demonstrates a local reference variable whose referent is an element of an array: +> +> ```csharp +> public class C +> { +> +> public void M() +> { +> int[] arr = new int[10]; +> // element is a reference variable that refers to arr[5] +> ref int element = ref arr[5]; +> element += 5; // arr[5] has been incremented by 5 +> } +> } +> ``` +> +> *end example* + +A ***reference return*** is the *variable_reference* returned from a returns-by-ref method (§15.6.1). This *variable_reference* is the referent of the reference return. + +> *Example:* The following example demonstrates a reference return whose referent is an element of an array field: +> +> ```csharp +> public class C +> { +> private int[] arr = new int[10]; +> +> public readonly ref int M() +> { +> // element is a reference variable that refers to arr[5] +> ref int element = ref arr[5]; +> return ref element; // return reference to arr[5]; +> } +> } +> ``` +> +> *end example* + +### §ref-safe-contexts Ref safe contexts + +All reference variables obey safety rules that ensure the ref-safe-context of the reference variable is not greater than the ref-safe-context of its referent. + +For any variable, the ***ref-safe-context*** of that variable is the context where a *variable_reference* (§9.5) to that variable is valid. The referent of a reference variable must have a ref-safe-context that is at least as wide as the ref-safe-context of the reference variable itself. + +> *Note*: The compiler determines the safe context through a static analysis of the program text. The safe context reflects the lifetime of a variable at runtime. *end note* + +There are three ref-safe-contexts: + +- ***declaration-block***: The ref-safe-context of a *variable_reference* to a local variable ([§9.2.8](variables.md#928-local-variables)) extends from its declaration to the end of the scope in which it is declared, including into any nested *embedded-statement*s in this range. + + A *variable_reference* to a local variable is a valid referent for a reference variable only if the reference variable is declared within the ref-safe-context of that variable. + +- ***function-member***: Within a function a *variable_reference* to any of the following has a ref-safe-context of function-member: + + - Value parameters ([§9.2.5](variables.md#925-value-parameters)) on a function member declaration, including the implicit `this` of class member functions; and + - The implicit reference (`ref`) parameter ([§9.2.6](variables.md#926-reference-parameters)) `this` of a struct member function, along with its fields. + + A *variable_reference* with ref-safe-context of function-member is a valid referent only if the reference variable is declared in the same function member. + +- ***caller-context***: Within a function a *variable_reference* to any of the following has a ref-safe-context of caller-context: + - Reference (`ref`) parameters ([§9.2.6](variables.md#926-reference-parameters)) other than the implicit `this` of a struct member function; + - Member fields and elements of such parameters; + - Member fields of parameters of class type; and + - Elements of parameters of array type. + A *variable_reference* with ref-safe-context of caller-context can be the referent of a reference return. + +These values form a nesting relationship from narrowest (declaration-block) to widest (caller-context). Each nested block represents a different context. + +> *Example*: The following code shows examples of the different ref-safe-contexts. The declarations show the ref-safe-context for a referent to be the initializing expression for a `ref` variable. The examples show the ref-safe-context for a reference return: +> +> ```csharp +> public class C +> { +> // ref safe context of arr is "caller-context". +> // ref safe context of arr[i] is "caller-context". +> private int[] arr = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; +> +> // ref safe context is "caller-context" +> public ref int M1(ref int r1) +> { +> return ref r1; // r1 is safe to ref return +> } +> +> // ref safe context is "function-member" +> public ref int M2(int v1) +> { +> return ref v1; // error: v1 isn't safe to ref return +> } +> +> public ref int M3() +> { +> int v2 = 5; +> +> return ref arr[v2]; // arr[v2] is safe to ref return +> } +> +> public void M4(int p) +> { +> int v3 = 6; +> +> // context of r2 is declaration-block, ref safe context of p is function-member +> ref int r2 = ref p; +> +> // context of r3 is declaration-block, ref safe context of v3 is declaration-block +> ref int r3 = ref v3; +> +> // context of r4 is block, ref safe context of arr[v3] is caller-context +> ref int r4 = ref arr[v3]; +> } +> } +> ``` +> +> *end example.* + + + +> *Example*: For `struct` types, the implicit `this` parameter is passed as a `ref` parameter. The ref-safe-context of the fields of a `struct` type as function-member prevents returning those fields by reference return. This rule prevents the following code: +> +> ```csharp +> public struct S +> { +> private int n; +> +> // Disallowed: returning ref of a field. +> public ref int GetN() => ref n; +> } +> +> public ref int M() +> { +> S s = new S(); +> ref int numRef = ref s.GetN(); +> return ref numRef; // reference to local variable 'numRef' returned +> } +> +> ``` +> +> *end example.* + +#### §ref-span-safety-locals Local variable ref safe context + +For a local variable `v`: + +- If `v` is a reference variable, its ref-safe-context is the same as the ref-safe-context of its initializing expression. +- Otherwise its ref-safe-context is the context in which it was declared. + +#### §ref-span-safety-parameters Parameter ref safe context + +For a formal parameter `p`: + +- If `p` is a `ref`, or `in` parameter, its ref-safe-context is the caller-context. If `p` is an `in` parameter, it can't be returned as a writable `ref` but can be returned as `ref readonly`. +- If `p` is an `out` parameter, its ref-safe-context is the caller-context. +- Otherwise, if `p` is the `this` parameter of a struct type, its ref-safe-context is the function-member. +- Otherwise, the parameter is a value parameter, and its ref-safe-context is the function-member. + +#### §ref-span-safety-field-reference Field ref safe context + +For a variable designating a reference to a field, `e.F`: + +- If `e` is of a reference type, its ref-safe-context is the caller-context. +- Otherwise, if `e` is of a value type, its ref-safe-context is the same as the ref-safe-context of `e`. + +#### §ref-span-safety-operators Operators + +The conditional operator (§12.18), `c ? ref e1 : ref e2`, and reference assignment operator, `= ref e` (§12.21.1) have reference variables as operands and yield a reference variable. For those operators, the ref-safe-context of the result is the narrowest context among the ref-safe-contexts of all `ref` operands. + +#### §ref-span-safety-function-invocation Function invocation + +For a variable `c` resulting from a ref-returning function invocation, `ref e1.M(e2, ...)`, its ref-safe-context is the narrowest of the following contexts: + +- The caller-context. +- The ref-safe-context of all `ref` and `out` argument expressions (excluding the receiver). +- For each `in` parameter of the method, if there is a corresponding expression that is a variable, its ref-safe-context, otherwise the nearest enclosing context. +- The context of all argument expressions (including the receiver). + +> *Example*: the last bullet is necessary to handle code such as +> +> ```csharp +> ref int M2() +> { +> int v = 5; +> // Not valid. +> // ref safe context of "v" is block. +> // Therefore, ref safe context of the return value of M() is block. +> return ref M(ref v); +> } +> +> ref int M(ref int p) +> { +> return ref p; +> } +> ``` +> +> *end example* + +A property invocation and an indexer invocation (either `get` or `set`) is treated as a function invocation of the underlying accessor by the above rules. A local function invocation is a function invocation. + +#### §ref-span-safety-a-value Values + +A value's ref-safe-context is the nearest enclosing context. + +> *Note:* This occurs in an invocation such as `M(ref d.Length)` where `d` is of type `dynamic`. It is also consistent with arguments corresponding to `in` parameters. + +#### §ref-span-safety-constructor-invocations Constructor invocations + +A `new` expression that invokes a constructor obeys the same rules as a method invocation (§ref-span-safety-function-invocation) that is considered to return the type being constructed. + +#### §ref-span-safety-limitations Limitations on reference variables + +- Neither a `ref` parameter, nor a `ref` local, nor a parameter or local of a `ref struct` type shall be captured by lambda expression or local function. +- Neither a `ref` parameter nor a parameter of a `ref struct` type shall be an argument for an iterator method or an `async` method. +- Neither a `ref` local, nor a local of a `ref struct` type shall be in context at the point of a `yield return` statement or an `await` expression. +- For a ref reassignment `ref e1 = ref e2`, the ref-safe-context of `e2` must be at least as wide a context as the *ref-safe-context* of `e1`. +- For a ref return statement `return ref e1`, the ref-safe-context of `e1` must be the caller-context.