From 33fe9aab0ba9990086b68de301b3b63ff80dda21 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Thu, 13 Aug 2026 06:03:13 -0400 Subject: [PATCH 1/6] add support for ref struct improvements Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 21570b74-c6f5-4fc5-ab6d-7546918d6ddb --- standard/lexical-structure.md | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/standard/lexical-structure.md b/standard/lexical-structure.md index aa79fa5f8..7f95f69f6 100644 --- a/standard/lexical-structure.md +++ b/standard/lexical-structure.md @@ -623,15 +623,29 @@ A ***contextual keyword*** is an identifier-like sequence of characters that has ```ANTLR contextual_keyword - : 'add' | 'alias' | 'and' | 'ascending' | 'async' - | 'await' | 'by' | 'Cdecl' | 'descending' | 'dynamic' - | 'equals' | 'Fastcall' | 'file' | 'from' | 'get' - | 'global' | 'group' | 'init' | 'into' | 'join' - | 'let' | 'managed' | 'nameof' | 'nint' | 'not' - | 'notnull' | 'nuint' | 'on' | 'or' | 'orderby' - | 'partial' | 'record' | 'remove' | 'required' | 'scoped' - | 'select' | 'set' | 'Stdcall' | 'Thiscall' | 'unmanaged' - | 'value' | 'var' | 'when' | 'where' | 'yield' + : 'add' | 'alias' + | 'allows' | 'and' + | 'ascending' | 'async' + | 'await' | 'by' + | 'Cdecl' | 'descending' + | 'dynamic' | 'equals' + | 'Fastcall' | 'file' + | 'from' | 'get' + | 'global' | 'group' + | 'init' | 'into' + | 'join' | 'let' + | 'managed' | 'nameof' + | 'nint' | 'not' + | 'notnull' | 'nuint' + | 'on' | 'or' + | 'orderby' | 'partial' + | 'record' | 'remove' + | 'required' | 'scoped' + | 'select' | 'set' + | 'Stdcall' | 'Thiscall' + | 'unmanaged' | 'value' + | 'var' | 'when' + | 'where' | 'yield' ; ``` From e4f856ac9d00d8f0b0bf5133e2ccddea18fc41da Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Thu, 13 Aug 2026 06:09:12 -0400 Subject: [PATCH 2/6] add support for ref struct improvements Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 21570b74-c6f5-4fc5-ab6d-7546918d6ddb --- standard/statements.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/standard/statements.md b/standard/statements.md index 33e67e45b..9fb6030a7 100644 --- a/standard/statements.md +++ b/standard/statements.md @@ -2071,9 +2071,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. @@ -2085,7 +2091,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 { From 773de364141e38eb884013c1f439a95d6c5e4651 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Thu, 13 Aug 2026 06:16:21 -0400 Subject: [PATCH 3/6] add support for ref struct improvements Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 21570b74-c6f5-4fc5-ab6d-7546918d6ddb --- standard/classes.md | 43 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/standard/classes.md b/standard/classes.md index 195820cbc..ef0031802 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*. @@ -644,6 +663,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: > > From 1b27ca5a13cf2af824644153ab4d71c49925274c Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Thu, 13 Aug 2026 06:18:59 -0400 Subject: [PATCH 4/6] add support for ref struct improvements Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 21570b74-c6f5-4fc5-ab6d-7546918d6ddb --- standard/structs.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/standard/structs.md b/standard/structs.md index 2f4c6eeae..0fa9e1593 100644 --- a/standard/structs.md +++ b/standard/structs.md @@ -95,7 +95,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. @@ -105,13 +104,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). From 545270fc0f28065be2523ea69fa93a0d16889886 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Thu, 13 Aug 2026 06:21:18 -0400 Subject: [PATCH 5/6] add support for ref struct improvements Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 21570b74-c6f5-4fc5-ab6d-7546918d6ddb --- standard/standard-library.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/standard-library.md b/standard/standard-library.md index 6f1ee5790..8ecaa9878 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); } From e9406ac10d0d825b85e5dd4c10e13a8d0e2242f5 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Thu, 13 Aug 2026 06:28:37 -0400 Subject: [PATCH 6/6] fix md Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 21570b74-c6f5-4fc5-ab6d-7546918d6ddb --- standard/classes.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/standard/classes.md b/standard/classes.md index ef0031802..681272bf4 100644 --- a/standard/classes.md +++ b/standard/classes.md @@ -675,13 +675,13 @@ class C {} ``` -Given `where T : allows ref struct`, `T` shall not +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`. +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: >