diff --git a/standard/classes.md b/standard/classes.md index 25ae0934d..e0ab18145 100644 --- a/standard/classes.md +++ b/standard/classes.md @@ -453,6 +453,11 @@ type_parameter_constraints_clause ; type_parameter_constraints + : restrictive_type_parameter_constraints (',' anti_constraints_clause)? + | anti_constraints_clause + ; + +restrictive_type_parameter_constraints : primary_constraint (',' secondary_constraints)? (',' constructor_constraint)? | secondary_constraints (',' constructor_constraint)? | constructor_constraint @@ -479,11 +484,25 @@ secondary_constraints constructor_constraint : 'new' '(' ')' ; + +anti_constraints_clause + : 'allows' anti_constraints + +anti_constraints + : anti_constraint (',' anti_constraint)* + +anti_constraint + : ref_struct_clause + +ref_struct_clause + : 'ref' 'struct' ``` -Each *type_parameter_constraints_clause* consists of the token `where`, followed by the name of a type parameter, followed by a colon and the list of constraints for that type parameter. There can be at most one `where` clause for each type parameter, and the `where` clauses can be listed in any order. Like the `get` and `set` tokens in a property accessor, the `where` token is not a keyword. +Each *type_parameter_constraints_clause* consists of the token `where`, followed by the name of a type parameter, followed by a colon and the list of constraints and anti-constraints (see definition below) for that type parameter. There can be at most one `where` clause for each type parameter, and the `where` clauses can be listed in any order. Like the `get` and `set` tokens in a property accessor, the `where` token is not a keyword. -The list of constraints given in a `where` clause can include any of the following components, in this order: a single primary constraint, one or more secondary constraints, and the constructor constraint, `new()`. +The list of constraints and anti-constraints given in a `where` clause can include any of the following components, in this order: a *primary_constraint*, one or more *secondary_constraint*s, a *constructor_constraint*, and an *anti_constraints_clause*. + +> *Note*: Although the grammar permits *anti_constraints* to contain multiple *anti_constraint*s, this is for future expansion, and in this edition of this specification the only anti-constraint is `ref struct`. *end note* A primary constraint can be a class type, the ***reference type constraint*** `class`, the ***value type constraint*** `struct`, the ***not null constraint*** `notnull`, the ***unmanaged type constraint*** `unmanaged`, or `default`. The class type and the reference type constraint can include the *nullable_type_annotation*. @@ -638,6 +657,26 @@ If the `where` clause for a type parameter includes a constructor constraint (wh It is a compile-time error for *type_parameter_constraints* having a *primary_constraint* of `struct` or `unmanaged` to also have a *constructor_constraint*. +Ordinarily, a ref struct type cannot be used as a type argument for a generic type or method. However, the presence of an *anti_constraints_clause* containing `allows ref struct` permits such a use. This clause is referred to as an ***anti-constraint***, as it expands the set of allowed type arguments rather than limits them like all other constraints. +When a type parameter has this anti-constraint, ref safety rules on all instances of that type parameter shall be enforced. + +The anti-constraint is not inherited from a type parameter type constraint. In the code below, `S` cannot be substituted with a ref struct: + +```csharp +class C + where T : allows ref struct + where S : T +{} +``` + +Given `where T : allows ref struct`, `T` shall not + +- Also be constrained to a known reference type +- Also be constrained with `class` or `class?` +- Be used as a generic argument unless the corresponding parameter also has the anti-constraint + +It is a compile-time error to invoke a non-virtual instance method (or property) on a type parameter with `allows ref struct`. + > *Example*: The following are examples of constraints: > > diff --git a/standard/lexical-structure.md b/standard/lexical-structure.md index 1f3634e1e..28910fcdf 100644 --- a/standard/lexical-structure.md +++ b/standard/lexical-structure.md @@ -605,7 +605,7 @@ A ***contextual keyword*** is an identifier-like sequence of characters that has ```ANTLR contextual_keyword - : 'add' | 'alias' | 'and' | 'ascending' | 'async' + : 'add' | 'alias' | 'allows' | 'and' | 'ascending' | 'async' | 'await' | 'by' | 'Cdecl' | 'descending'| 'dynamic' | 'equals' | 'Fastcall' | 'from' | 'get' | 'global' | 'group' | 'init' | 'into' | 'join' | 'let' diff --git a/standard/standard-library.md b/standard/standard-library.md index 0892bf571..c524ac1a0 100644 --- a/standard/standard-library.md +++ b/standard/standard-library.md @@ -138,7 +138,7 @@ namespace System void Dispose(); } - public interface IEquatable + public interface IEquatable where T : allows ref struct { bool Equals(T? other); } diff --git a/standard/statements.md b/standard/statements.md index 0c6d084d7..84a43350f 100644 --- a/standard/statements.md +++ b/standard/statements.md @@ -2067,9 +2067,15 @@ non_ref_local_variable_declaration ; ``` -A ***resource type*** is either a class or non-ref struct that implements either or both of the `System.IDisposable` or `System.IAsyncDisposable` interfaces, which includes a single parameterless method named `Dispose` and/or `DisposeAsync`; or a ref struct that includes a method named `Dispose` having the same signature as that declared by `System.IDisposable`. Code that is using a resource can call `Dispose` or `DisposeAsync` to indicate that the resource is no longer needed. +A ***resource type*** is either a class or struct that implements either or both of the `System.IDisposable` or `System.IAsyncDisposable` interfaces, which includes a single parameterless method named `Dispose` and/or `DisposeAsync`. A ref struct that includes a method named `Dispose` having the same signature as that declared by `System.IDisposable` is also a resource type. In this case, preference is given to a `Dispose` method that implements the pattern, and only if one is not found, shall `IDisposable` be used. -If the form of *resource_acquisition* is *non_ref_local_variable_declaration* then the type of the *non_ref_local_variable_declaration* shall be either `dynamic` or a resource type. If the form of *resource_acquisition* is *expression* then this expression shall have a resource type. If `await` is present, the resource type shall implement `System.IAsyncDisposable`. A `ref struct` type cannot be the resource type for a `using` statement with the `await` modifier. +A using statement shall recognize and use an implementation of `Idisposable` when the resource is a type parameter has the `ref struct` anti-constraint, and `Idisposable` is in its effective interfaces set. + +> *Note*: A pattern `Dispose` method will not be recognized on a type parameter that has the `ref struct` anti-constraint because an interface is not a ref struct. *end note* + +Code that is using a resource can call `Dispose` or `DisposeAsync` to indicate that the resource is no longer needed. + +If the form of *resource_acquisition* is *non_ref_local_variable_declaration* then the type of the *non_ref_local_variable_declaration* shall be either `dynamic` or a resource type. If the form of *resource_acquisition* is *expression* then this expression shall have a resource type. If `await` is present, the resource type shall implement `System.IAsyncDisposable`. Local variables declared in a *resource_acquisition* are read-only, and shall include an initializer. A compile-time error occurs if the embedded statement attempts to modify these local variables (via assignment or the `++` and `--` operators), take the address of them, or pass them as reference or output parameters. @@ -2081,7 +2087,7 @@ A `using` statement of the form using (ResourceType resource = «expression») «statement» ``` -corresponds to one of three possible formulations. For class and non-ref struct resources, when `ResourceType` is a non-nullable value type or a type parameter with the value type constraint ([§15.2.5](classes.md#1525-type-parameter-constraints)), the formulation is semantically equivalent to: +corresponds to one of three possible formulations. For class and struct resources, when `ResourceType` is a non-nullable value type or a type parameter with the value type constraint ([§15.2.5](classes.md#1525-type-parameter-constraints)), the formulation is semantically equivalent to: ```csharp { diff --git a/standard/structs.md b/standard/structs.md index 7d9047675..ece991e84 100644 --- a/standard/structs.md +++ b/standard/structs.md @@ -94,7 +94,6 @@ It is a compile-time error if a ref struct type is used in any of the following - 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. -- As a type argument. - As the type of a tuple element. - In an async method. - In an iterator. @@ -104,13 +103,14 @@ It is a compile-time error if a ref struct type is used in any of the following In addition, the following restrictions apply to a `ref struct` type: - A `ref struct` type shall not be boxed to `System.ValueType` or `System.Object`. -- 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. > *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. +Although a `ref struct` type may implement an interface, that `ref struct` type shall implement all instance members of that interface, even if they have default implementations. + ### 16.2.4 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-type-declarations).