Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 41 additions & 2 deletions standard/classes.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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*.

Expand Down Expand Up @@ -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<T, S>
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:
>
> <!-- Example: {template:"standalone-lib-without-using", name:"TypeParameterConstraints1", replaceEllipsis:true} -->
Expand Down
2 changes: 1 addition & 1 deletion standard/lexical-structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
2 changes: 1 addition & 1 deletion standard/standard-library.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ namespace System
void Dispose();
}

public interface IEquatable<T>
public interface IEquatable<T> where T : allows ref struct
{
bool Equals(T? other);
}
Expand Down
12 changes: 9 additions & 3 deletions standard/statements.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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
{
Expand Down
4 changes: 2 additions & 2 deletions standard/structs.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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).
Expand Down
Loading