From ebd8c28f78f9c9f896702df81381a08afa920f2e Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Thu, 24 Mar 2022 17:26:42 -0700 Subject: [PATCH 01/14] Design to support ByRefLike types in Generics --- docs/design/features/byreflike-generics.md | 75 ++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 docs/design/features/byreflike-generics.md diff --git a/docs/design/features/byreflike-generics.md b/docs/design/features/byreflike-generics.md new file mode 100644 index 00000000000000..ad191c575b4b42 --- /dev/null +++ b/docs/design/features/byreflike-generics.md @@ -0,0 +1,75 @@ +# Generics parameters of ByRefLike types + +Using ByRefLike types in Generic parameters is possible by building upon support added for `ref` fields. Scenarios that would benefit most from this are those involving `Span`. For example, consider the following examples: + +- `Span` – Represents the general case where a ByRefLike type is used as a Generic parameter. This specific case would be desirable for a more efficient Reflection API. +- `Span>` – Nested `Span` types would be of benefit in the parsing result of strings. + +## Runtime impact + +Supporting ByRefLike type as Generic parameters will impact the following IL instructions: + +- `box` – Types with ByRefLike parameters used in fields cannot be boxed. +- `throw` – Requires an object reference on the stack so not directly impacted since boxing is not permitted. +- `stsfld` / `ldsfld` – Type fields of a ByRefLike parameter cannot be marked `static`. +- `newarr` / `stelem` / `ldelem` / `ldelema` – Arrays are not able to contain ByRefLike types. + - `newobj` – For multi-dimensional array construction. +- `constrained.callvirt` – This IL sequence must resolve to a method implemented on `object`, or a default interface method. +- Use of any Generic which doesn’t expect a ByRefLike parameter as a Generic parameter. + +## Proposal + +A new Attribute API will be defined to indicate which Generic parameters are permissible to be of any type—including ByRefLike types. The Attribute could be used by the compiler to implement a generic non-constraint. + +```csharp +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)] + public class GenericParameterSupportsAnyTypeAttribute : Attribute + { + /// + /// Constructs an instance with a Generic parameter index of 0. + /// + public GenericParameterSupportsAnyTypeAttribute() + => ParameterIndex = 0; + + /// + /// Constructs an instance with a Generic parameter index. + /// + /// Non-negative index of Generic parameter to apply to. + public GenericParameterSupportsAnyTypeAttribute(int parameterIndex) + => ParameterIndex = parameterIndex; + + /// + /// Generic Parameter Index. + /// + public int ParameterIndex { get; set; } + } +} +``` + +A new API will be implemented as a JIT intrinsic for determining if a parameter is ByRefLike. This API would represent a check to occur at JIT time code-gen to avoid taking paths that would be invalid for some values of `T`. + +```diff +namespace System +{ + public abstract partial class Type + { ++ [Intrinsic] ++ public static bool IsByRefLike(); + } +} +``` + +For dispatch to object implemented methods and to default interface methods, the behavior shall be that an `InvalidProgramException` should be thrown. The JIT would insert the following IL at code-gen time. + +``` +newobj instance void System.InvalidProgramException::.ctor() +throw +``` + +When boxing due to a constrained call that cannot be made, instead of allocating a normal boxed object, an object of `InvalidBoxedObject` type will be created. It will have implementations of the various overridable object methods which throw `InvalidProgramException`, and interface dispatch shall have a special case for attempting to invoke an interface method on such an object, that will also throw an `InvalidProgramException`.  + +## Open questions + +- This scenario is the inverse of [Generic constraints](https://docs.microsoft.com/dotnet/csharp/programming-guide/generics/constraints-on-type-parameters) as it is an "allow". What does this look like as a general case as to potentially support pointers in the future? See https://github.com/dotnet/runtime/issues/13627. From a927231df6464ee39571b0a3b01db727eedef547 Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Thu, 24 Mar 2022 17:37:13 -0700 Subject: [PATCH 02/14] Trim whitespace --- docs/design/features/byreflike-generics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/design/features/byreflike-generics.md b/docs/design/features/byreflike-generics.md index ad191c575b4b42..e33a171be3730d 100644 --- a/docs/design/features/byreflike-generics.md +++ b/docs/design/features/byreflike-generics.md @@ -68,7 +68,7 @@ newobj instance void System.InvalidProgramException::.ctor() throw ``` -When boxing due to a constrained call that cannot be made, instead of allocating a normal boxed object, an object of `InvalidBoxedObject` type will be created. It will have implementations of the various overridable object methods which throw `InvalidProgramException`, and interface dispatch shall have a special case for attempting to invoke an interface method on such an object, that will also throw an `InvalidProgramException`.  +When boxing due to a constrained call that cannot be made, instead of allocating a normal boxed object, an object of `InvalidBoxedObject` type will be created. It will have implementations of the various overridable object methods which throw `InvalidProgramException`, and interface dispatch shall have a special case for attempting to invoke an interface method on such an object, that will also throw an `InvalidProgramException`. ## Open questions From affe2c100a9c77e7804f43a60783a1317ad9518b Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Fri, 25 Mar 2022 18:11:46 -0700 Subject: [PATCH 03/14] Review feedback --- docs/design/features/byreflike-generics.md | 66 +++++++++++----------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/docs/design/features/byreflike-generics.md b/docs/design/features/byreflike-generics.md index e33a171be3730d..7bdd11d8b4cee6 100644 --- a/docs/design/features/byreflike-generics.md +++ b/docs/design/features/byreflike-generics.md @@ -10,57 +10,53 @@ Using ByRefLike types in Generic parameters is possible by building upon support Supporting ByRefLike type as Generic parameters will impact the following IL instructions: - `box` – Types with ByRefLike parameters used in fields cannot be boxed. -- `throw` – Requires an object reference on the stack so not directly impacted since boxing is not permitted. - `stsfld` / `ldsfld` – Type fields of a ByRefLike parameter cannot be marked `static`. - `newarr` / `stelem` / `ldelem` / `ldelema` – Arrays are not able to contain ByRefLike types. - `newobj` – For multi-dimensional array construction. - `constrained.callvirt` – This IL sequence must resolve to a method implemented on `object`, or a default interface method. -- Use of any Generic which doesn’t expect a ByRefLike parameter as a Generic parameter. + +If any of the above instructions are attempted to be used with a ByRefLike type, the runtime will throw an `InvalidProgramException`. + +The following instructions are already set up to support this feature since their behavior will be to fail as currently defined due to the inability to box a ByRefLike type. + +- `throw` – Requires an object reference to be on stack, which can never be a ByRefLike type. +- `unbox` / `unbox.any` – Requires an object reference to be on stack, which can never be a ByRefLike type. +- `isinst` – Will always place `null` on stack. +- `castclass` – Will always throw `System.InvalidCastException`. ## Proposal -A new Attribute API will be defined to indicate which Generic parameters are permissible to be of any type—including ByRefLike types. The Attribute could be used by the compiler to implement a generic non-constraint. +Support for the following would be indicated by the existing `RuntimeFeature.ByRefFields` mechanism. + +A new `GenericParameterAttributes` value will be defined which also represents metadata defined in the `CorGenericParamAttr` enumeration. Space is provided between the existing constraints group to permit constraint growth. -```csharp -namespace System.Runtime.CompilerServices +```diff +namespace System.Reflection { - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)] - public class GenericParameterSupportsAnyTypeAttribute : Attribute + [Flags] + public enum GenericParameterAttributes { - /// - /// Constructs an instance with a Generic parameter index of 0. - /// - public GenericParameterSupportsAnyTypeAttribute() - => ParameterIndex = 0; - - /// - /// Constructs an instance with a Generic parameter index. - /// - /// Non-negative index of Generic parameter to apply to. - public GenericParameterSupportsAnyTypeAttribute(int parameterIndex) - => ParameterIndex = parameterIndex; - - /// - /// Generic Parameter Index. - /// - public int ParameterIndex { get; set; } ++ SupportsByRefLike = 0x0100 } } ``` -A new API will be implemented as a JIT intrinsic for determining if a parameter is ByRefLike. This API would represent a check to occur at JIT time code-gen to avoid taking paths that would be invalid for some values of `T`. - ```diff -namespace System +typedef enum CorGenericParamAttr { - public abstract partial class Type - { -+ [Intrinsic] -+ public static bool IsByRefLike(); - } -} ++ gpSupportsByRefLike = 0x0100 // type argument can be ByRefLike +} CorGenericParamAttr; ``` +The expansion of metadata will impact at least the following: + +- ILDasm/ILAsm – https://github.com/dotnet/runtime +- Cecil – https://github.com/jbevain/cecil +- IL Trimmer – https://github.com/dotnet/linker +- C++/CLI – The MSVC team + +An API that is a JIT-time intrinsic will be needed to determine if a parameter is ByRefLike. This API would represent a check to occur at JIT time code-gen to avoid taking paths that would be invalid for some values of `T`. The existing `Type.IsByRefLike` property will be made an intrinsic (e.g., `typeof(T).IsByRefLike`). + For dispatch to object implemented methods and to default interface methods, the behavior shall be that an `InvalidProgramException` should be thrown. The JIT would insert the following IL at code-gen time. ``` @@ -70,6 +66,10 @@ throw When boxing due to a constrained call that cannot be made, instead of allocating a normal boxed object, an object of `InvalidBoxedObject` type will be created. It will have implementations of the various overridable object methods which throw `InvalidProgramException`, and interface dispatch shall have a special case for attempting to invoke an interface method on such an object, that will also throw an `InvalidProgramException`. +The `Reflection.Emit` API will need to be updated to respect the behavior of this flag. How it will handle support is an open question. + ## Open questions - This scenario is the inverse of [Generic constraints](https://docs.microsoft.com/dotnet/csharp/programming-guide/generics/constraints-on-type-parameters) as it is an "allow". What does this look like as a general case as to potentially support pointers in the future? See https://github.com/dotnet/runtime/issues/13627. + +- Should `Reflection` support this scenario initially? This includes calling and using API calls such as `MakeGenericType` / `MakeGenericMethod`. From 9d17a023ba2ab779dcad201c27872c136603daa8 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Fri, 25 Mar 2022 18:12:20 -0700 Subject: [PATCH 04/14] Remove words --- docs/design/features/byreflike-generics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/design/features/byreflike-generics.md b/docs/design/features/byreflike-generics.md index 7bdd11d8b4cee6..2c6c57875c4b9e 100644 --- a/docs/design/features/byreflike-generics.md +++ b/docs/design/features/byreflike-generics.md @@ -17,7 +17,7 @@ Supporting ByRefLike type as Generic parameters will impact the following IL ins If any of the above instructions are attempted to be used with a ByRefLike type, the runtime will throw an `InvalidProgramException`. -The following instructions are already set up to support this feature since their behavior will be to fail as currently defined due to the inability to box a ByRefLike type. +The following instructions are already set up to support this feature since their behavior will fail as currently defined due to the inability to box a ByRefLike type. - `throw` – Requires an object reference to be on stack, which can never be a ByRefLike type. - `unbox` / `unbox.any` – Requires an object reference to be on stack, which can never be a ByRefLike type. From 9cb2a571bcb6c58c755a5ba39507726b43d69a08 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Sat, 26 Mar 2022 09:45:12 -0700 Subject: [PATCH 05/14] Update based on feedback --- docs/design/features/byreflike-generics.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/design/features/byreflike-generics.md b/docs/design/features/byreflike-generics.md index 2c6c57875c4b9e..65783dd1686dd8 100644 --- a/docs/design/features/byreflike-generics.md +++ b/docs/design/features/byreflike-generics.md @@ -26,7 +26,7 @@ The following instructions are already set up to support this feature since thei ## Proposal -Support for the following would be indicated by the existing `RuntimeFeature.ByRefFields` mechanism. +Support for the following will be indicated by the `RuntimeFeature.GenericsAcceptByRefLike` property. A new `GenericParameterAttributes` value will be defined which also represents metadata defined in the `CorGenericParamAttr` enumeration. Space is provided between the existing constraints group to permit constraint growth. @@ -36,7 +36,7 @@ namespace System.Reflection [Flags] public enum GenericParameterAttributes { -+ SupportsByRefLike = 0x0100 ++ AcceptByRefLike = 0x0100 } } ``` @@ -44,7 +44,7 @@ namespace System.Reflection ```diff typedef enum CorGenericParamAttr { -+ gpSupportsByRefLike = 0x0100 // type argument can be ByRefLike ++ gpAcceptByRefLike = 0x0100 // type argument can be ByRefLike } CorGenericParamAttr; ``` From f2929685daa6030e55b09d8b61ffe793df9a5fde Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Mon, 28 Mar 2022 09:45:17 -0700 Subject: [PATCH 06/14] Add F# as being impacted. --- docs/design/features/byreflike-generics.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/design/features/byreflike-generics.md b/docs/design/features/byreflike-generics.md index 65783dd1686dd8..8c1b2616f69bfe 100644 --- a/docs/design/features/byreflike-generics.md +++ b/docs/design/features/byreflike-generics.md @@ -53,6 +53,7 @@ The expansion of metadata will impact at least the following: - ILDasm/ILAsm – https://github.com/dotnet/runtime - Cecil – https://github.com/jbevain/cecil - IL Trimmer – https://github.com/dotnet/linker +- F# – https://github.com/fsharp/fsharp - C++/CLI – The MSVC team An API that is a JIT-time intrinsic will be needed to determine if a parameter is ByRefLike. This API would represent a check to occur at JIT time code-gen to avoid taking paths that would be invalid for some values of `T`. The existing `Type.IsByRefLike` property will be made an intrinsic (e.g., `typeof(T).IsByRefLike`). From 2cb76f2297fcbcf4c3fad87f23f7d7c364744e87 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Tue, 29 Mar 2022 14:43:52 -0700 Subject: [PATCH 07/14] Feedback and clarify wording for constrained calls. --- docs/design/features/byreflike-generics.md | 33 +++++++++++++--------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/docs/design/features/byreflike-generics.md b/docs/design/features/byreflike-generics.md index 8c1b2616f69bfe..73c7eb618fb77e 100644 --- a/docs/design/features/byreflike-generics.md +++ b/docs/design/features/byreflike-generics.md @@ -13,7 +13,7 @@ Supporting ByRefLike type as Generic parameters will impact the following IL ins - `stsfld` / `ldsfld` – Type fields of a ByRefLike parameter cannot be marked `static`. - `newarr` / `stelem` / `ldelem` / `ldelema` – Arrays are not able to contain ByRefLike types. - `newobj` – For multi-dimensional array construction. -- `constrained.callvirt` – This IL sequence must resolve to a method implemented on `object`, or a default interface method. +- `constrained.callvirt` – If this IL sequence resolves to a method implemented on `object` or default interface method, an error will occur during the attempt to box the instance. If any of the above instructions are attempted to be used with a ByRefLike type, the runtime will throw an `InvalidProgramException`. @@ -24,11 +24,24 @@ The following instructions are already set up to support this feature since thei - `isinst` – Will always place `null` on stack. - `castclass` – Will always throw `System.InvalidCastException`. -## Proposal +## API Proposal -Support for the following will be indicated by the `RuntimeFeature.GenericsAcceptByRefLike` property. +Support for the following will be indicated by a new property. For .NET 7, the feature will be marked with `RequiresPreviewFeaturesAttribute` to indicate it is in [preview](https://github.com/dotnet/designs/blob/main/accepted/2021/preview-features/preview-features.md). -A new `GenericParameterAttributes` value will be defined which also represents metadata defined in the `CorGenericParamAttr` enumeration. Space is provided between the existing constraints group to permit constraint growth. +```diff +namespace System.Runtime.CompilerServices +{ + public static partial class RuntimeFeature + { ++ /// ++ /// Represents a runtime feature where byref-like types can be used in Generic parameters. ++ /// ++ public const string GenericsAcceptByRefLike = nameof(GenericsAcceptByRefLike); + } +} +``` + +A new `GenericParameterAttributes` value will be defined which also represents metadata defined in the `CorGenericParamAttr` enumeration. Space is provided between the existing constraints group to permit constraint growth while keeping the bit-mask contiguous. ```diff namespace System.Reflection @@ -56,6 +69,8 @@ The expansion of metadata will impact at least the following: - F# – https://github.com/fsharp/fsharp - C++/CLI – The MSVC team +## Semantic Proposal + An API that is a JIT-time intrinsic will be needed to determine if a parameter is ByRefLike. This API would represent a check to occur at JIT time code-gen to avoid taking paths that would be invalid for some values of `T`. The existing `Type.IsByRefLike` property will be made an intrinsic (e.g., `typeof(T).IsByRefLike`). For dispatch to object implemented methods and to default interface methods, the behavior shall be that an `InvalidProgramException` should be thrown. The JIT would insert the following IL at code-gen time. @@ -65,12 +80,4 @@ newobj instance void System.InvalidProgramException::.ctor() throw ``` -When boxing due to a constrained call that cannot be made, instead of allocating a normal boxed object, an object of `InvalidBoxedObject` type will be created. It will have implementations of the various overridable object methods which throw `InvalidProgramException`, and interface dispatch shall have a special case for attempting to invoke an interface method on such an object, that will also throw an `InvalidProgramException`. - -The `Reflection.Emit` API will need to be updated to respect the behavior of this flag. How it will handle support is an open question. - -## Open questions - -- This scenario is the inverse of [Generic constraints](https://docs.microsoft.com/dotnet/csharp/programming-guide/generics/constraints-on-type-parameters) as it is an "allow". What does this look like as a general case as to potentially support pointers in the future? See https://github.com/dotnet/runtime/issues/13627. - -- Should `Reflection` support this scenario initially? This includes calling and using API calls such as `MakeGenericType` / `MakeGenericMethod`. +The `Reflection.Emit` API will need to be updated to respect the behavior of this flag. The current Reflection API requires boxing of all types which makes usage of ByRefLike types impossible. Until the Reflection API can fully support ByRefLike types, this feature must be blocked when using Reflection. For example, API calls such as `MakeGenericType` / `MakeGenericMethod` are invalid. From 489f684be444f78eae1a67d93e352acf2d1824ec Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Tue, 29 Mar 2022 20:39:31 -0700 Subject: [PATCH 08/14] Add well-known IL sequences for boxing Record APIs where compiler constraint analysis should be suppressed. Defined analysis suppression attribute. --- docs/design/features/byreflike-generics.md | 42 +++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/docs/design/features/byreflike-generics.md b/docs/design/features/byreflike-generics.md index 73c7eb618fb77e..680e39a41edac5 100644 --- a/docs/design/features/byreflike-generics.md +++ b/docs/design/features/byreflike-generics.md @@ -15,7 +15,7 @@ Supporting ByRefLike type as Generic parameters will impact the following IL ins - `newobj` – For multi-dimensional array construction. - `constrained.callvirt` – If this IL sequence resolves to a method implemented on `object` or default interface method, an error will occur during the attempt to box the instance. -If any of the above instructions are attempted to be used with a ByRefLike type, the runtime will throw an `InvalidProgramException`. +If any of the above instructions are attempted to be used with a ByRefLike type, the runtime will throw an `InvalidProgramException`. Sequences involving some of the above instructions are considered optimizations and represent cases that will remain valid regardless of a `T` being ByRefLike. See "Special IL Sequences" section below for details. The following instructions are already set up to support this feature since their behavior will fail as currently defined due to the inability to box a ByRefLike type. @@ -41,6 +41,34 @@ namespace System.Runtime.CompilerServices } ``` +The compiler will need an indication for existing troublesome APIs where ByRefLike types will be permissable, but where the failure will be handled at runtime. An attribute will be created and added to these APIs. + +```csharp +namespace System.Runtime.CompilerServices +{ + /// + /// Indicates to the compiler that constrain checks should be suppressed + /// and will instead be enforced at run-time. + /// + public sealed class SuppressConstraintChecksAttribute : Attribute + { } +} +``` + +Troublesome APIs: +- [`Span`](https://docs.microsoft.com/dotnet/api/system.span-1) + - `public Span(T[]? array);` + - `public Span(T[]? array, int start, int length);` + - `public T[] ToArray();` + - `public static implicit operator Span(ArraySegment segment);` + - `public static implicit operator Span(T[]? array);` +- [`ReadOnlySpan`](https://docs.microsoft.com/dotnet/api/system.readonlyspan-1) + - `public ReadOnlySpan(T[]? array);` + - `public ReadOnlySpan(T[]? array, int start, int length);` + - `public T[] ToArray();` + - `public static implicit operator ReadOnlySpan(ArraySegment segment);` + - `public static implicit operator ReadOnlySpan(T[]? array);` + A new `GenericParameterAttributes` value will be defined which also represents metadata defined in the `CorGenericParamAttr` enumeration. Space is provided between the existing constraints group to permit constraint growth while keeping the bit-mask contiguous. ```diff @@ -81,3 +109,15 @@ throw ``` The `Reflection.Emit` API will need to be updated to respect the behavior of this flag. The current Reflection API requires boxing of all types which makes usage of ByRefLike types impossible. Until the Reflection API can fully support ByRefLike types, this feature must be blocked when using Reflection. For example, API calls such as `MakeGenericType` / `MakeGenericMethod` are invalid. + +## Special IL Sequences + +The following are IL sequences involving the `box` instruction. They are used for optimized scenarios and shall continue to be valid, even with ByRefLike types, in cases where the result can be computed at JIT time and elided safely. They will be added to the ECMA-335 addendum. + +`box` ; `unbox.any` + +`box` ; `br_true/false` + +`box` ; `isinst` ; `br_true/false` + +`box` ; `isinst` ; `unbox.any` From 9a66ba1aeee17ffb564566e38063d6a40d9c296d Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Tue, 29 Mar 2022 20:47:32 -0700 Subject: [PATCH 09/14] Minor nits. --- docs/design/features/byreflike-generics.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/design/features/byreflike-generics.md b/docs/design/features/byreflike-generics.md index 680e39a41edac5..efdc043eab26ed 100644 --- a/docs/design/features/byreflike-generics.md +++ b/docs/design/features/byreflike-generics.md @@ -99,25 +99,25 @@ The expansion of metadata will impact at least the following: ## Semantic Proposal -An API that is a JIT-time intrinsic will be needed to determine if a parameter is ByRefLike. This API would represent a check to occur at JIT time code-gen to avoid taking paths that would be invalid for some values of `T`. The existing `Type.IsByRefLike` property will be made an intrinsic (e.g., `typeof(T).IsByRefLike`). +An API that is a JIT-time intrinsic will be needed to determine if a parameter is ByRefLike. This API would represent a check to occur at JIT time to avoid taking paths that would be invalid for some values of `T`. The existing `Type.IsByRefLike` property will be made an intrinsic (e.g., `typeof(T).IsByRefLike`). -For dispatch to object implemented methods and to default interface methods, the behavior shall be that an `InvalidProgramException` should be thrown. The JIT would insert the following IL at code-gen time. +For dispatch to object implemented methods and to default interface methods, the behavior shall be that an `InvalidProgramException` should be thrown. The JIT will insert the following IL at code-gen time. ``` newobj instance void System.InvalidProgramException::.ctor() throw ``` -The `Reflection.Emit` API will need to be updated to respect the behavior of this flag. The current Reflection API requires boxing of all types which makes usage of ByRefLike types impossible. Until the Reflection API can fully support ByRefLike types, this feature must be blocked when using Reflection. For example, API calls such as `MakeGenericType` / `MakeGenericMethod` are invalid. +The `Reflection.Emit` API will be updated to respect ByRefLike support in Generics. The current Reflection API requires boxing of all types which makes usage of ByRefLike types impossible. Until the Reflection API can fully support ByRefLike types, this feature must be blocked when using Reflection. For example, API calls such as `MakeGenericType` / `MakeGenericMethod` will be invalid when `T` is ByRefLike. ## Special IL Sequences -The following are IL sequences involving the `box` instruction. They are used for optimized scenarios and shall continue to be valid, even with ByRefLike types, in cases where the result can be computed at JIT time and elided safely. They will be added to the ECMA-335 addendum. +The following are IL sequences involving the `box` instruction. They are used in scenario optimizations and shall continue to be valid, even with ByRefLike types, in cases where the result can be computed at JIT time and elided safely. They will be added to the ECMA-335 addendum. `box` ; `unbox.any` `box` ; `br_true/false` -`box` ; `isinst` ; `br_true/false` - `box` ; `isinst` ; `unbox.any` + +`box` ; `isinst` ; `br_true/false` From 0468624ebfb5d978cd289594c5fc1cb8f91e1231 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Wed, 30 Mar 2022 07:24:51 -0700 Subject: [PATCH 10/14] Update docs/design/features/byreflike-generics.md Co-authored-by: Jared Parsons --- docs/design/features/byreflike-generics.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/design/features/byreflike-generics.md b/docs/design/features/byreflike-generics.md index efdc043eab26ed..e9293bdb7419aa 100644 --- a/docs/design/features/byreflike-generics.md +++ b/docs/design/features/byreflike-generics.md @@ -50,6 +50,7 @@ namespace System.Runtime.CompilerServices /// Indicates to the compiler that constrain checks should be suppressed /// and will instead be enforced at run-time. /// + [AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property)] public sealed class SuppressConstraintChecksAttribute : Attribute { } } From 7044a94f1761b727e234575aaab96cdca4e618a9 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Wed, 30 Mar 2022 21:08:36 -0700 Subject: [PATCH 11/14] Review feedback --- docs/design/features/byreflike-generics.md | 23 +++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/docs/design/features/byreflike-generics.md b/docs/design/features/byreflike-generics.md index e9293bdb7419aa..1ef8527ee445d3 100644 --- a/docs/design/features/byreflike-generics.md +++ b/docs/design/features/byreflike-generics.md @@ -22,7 +22,9 @@ The following instructions are already set up to support this feature since thei - `throw` – Requires an object reference to be on stack, which can never be a ByRefLike type. - `unbox` / `unbox.any` – Requires an object reference to be on stack, which can never be a ByRefLike type. - `isinst` – Will always place `null` on stack. -- `castclass` – Will always throw `System.InvalidCastException`. +- `castclass` – Will always throw `InvalidCastException`. + +The expansion of ByRefLike types as Generic parameters does not relax restrictions on where ByRefLike types can be used. When `T` is ByRefLike, the use of `T` as a field will require the enclosing type to be ByRefLike. ## API Proposal @@ -51,12 +53,13 @@ namespace System.Runtime.CompilerServices /// and will instead be enforced at run-time. /// [AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property)] - public sealed class SuppressConstraintChecksAttribute : Attribute + internal sealed class SuppressConstraintChecksAttribute : Attribute { } } ``` Troublesome APIs: + - [`Span`](https://docs.microsoft.com/dotnet/api/system.span-1) - `public Span(T[]? array);` - `public Span(T[]? array, int start, int length);` @@ -92,7 +95,7 @@ typedef enum CorGenericParamAttr The expansion of metadata will impact at least the following: -- ILDasm/ILAsm – https://github.com/dotnet/runtime +- ILDasm/ILAsm/`System.Reflection.Metadata`/`System.Reflection.Emit` – https://github.com/dotnet/runtime - Cecil – https://github.com/jbevain/cecil - IL Trimmer – https://github.com/dotnet/linker - F# – https://github.com/fsharp/fsharp @@ -109,16 +112,18 @@ newobj instance void System.InvalidProgramException::.ctor() throw ``` -The `Reflection.Emit` API will be updated to respect ByRefLike support in Generics. The current Reflection API requires boxing of all types which makes usage of ByRefLike types impossible. Until the Reflection API can fully support ByRefLike types, this feature must be blocked when using Reflection. For example, API calls such as `MakeGenericType` / `MakeGenericMethod` will be invalid when `T` is ByRefLike. +Adding `gpAcceptByRefLike` to the metadata of a Generic parameter will be considered a non-breaking binary change. + +Enumerating of constructors/methods on `Span` and `ReadOnlySpan` may throw `TypeLoadException` if `T` is a ByRefLike type. See "Troublesome APIs" above for the list of APIs that cause this condition. ## Special IL Sequences -The following are IL sequences involving the `box` instruction. They are used in scenario optimizations and shall continue to be valid, even with ByRefLike types, in cases where the result can be computed at JIT time and elided safely. They will be added to the ECMA-335 addendum. +The following are IL sequences involving the `box` instruction. They are used for common C# language constructs and shall continue to be valid, even with ByRefLike types, in cases where the result can be computed at JIT time and elided safely. The conditions where each sequence is elided are described below and each condition will be added to the ECMA-335 addendum. -`box` ; `unbox.any` +`box` ; `unbox.any` – The box target type is equal to the unboxed target type. -`box` ; `br_true/false` +`box` ; `br_true/false` – The box target type is non-`Nullable`. -`box` ; `isinst` ; `unbox.any` +`box` ; `isinst` ; `unbox.any` – The box, `isint`, and unbox target types are all equal. -`box` ; `isinst` ; `br_true/false` +`box` ; `isinst` ; `br_true/false` – The box target type is equal to the unboxed target type or the box target type is `Nullable` and target type equalities can be computed. From 8eaf856d076b7b3f13fda98c47b6277e78916396 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Tue, 5 Apr 2022 10:57:47 -0700 Subject: [PATCH 12/14] Update to design doc --- docs/design/features/byreflike-generics.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/design/features/byreflike-generics.md b/docs/design/features/byreflike-generics.md index 1ef8527ee445d3..ee15cd4b708680 100644 --- a/docs/design/features/byreflike-generics.md +++ b/docs/design/features/byreflike-generics.md @@ -73,7 +73,7 @@ Troublesome APIs: - `public static implicit operator ReadOnlySpan(ArraySegment segment);` - `public static implicit operator ReadOnlySpan(T[]? array);` -A new `GenericParameterAttributes` value will be defined which also represents metadata defined in the `CorGenericParamAttr` enumeration. Space is provided between the existing constraints group to permit constraint growth while keeping the bit-mask contiguous. +A new `GenericParameterAttributes` value will be defined which also represents metadata defined in the `CorGenericParamAttr` enumeration. ```diff namespace System.Reflection @@ -81,7 +81,7 @@ namespace System.Reflection [Flags] public enum GenericParameterAttributes { -+ AcceptByRefLike = 0x0100 ++ AcceptByRefLike = 0x0020 } } ``` @@ -89,7 +89,7 @@ namespace System.Reflection ```diff typedef enum CorGenericParamAttr { -+ gpAcceptByRefLike = 0x0100 // type argument can be ByRefLike ++ gpAcceptByRefLike = 0x0020 // type argument can be ByRefLike } CorGenericParamAttr; ``` From 75fd91b41e2e2f39fa5f24ec60e0b7bd2d88d02f Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Fri, 8 Apr 2022 15:22:04 -0700 Subject: [PATCH 13/14] Misspelling --- docs/design/features/byreflike-generics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/design/features/byreflike-generics.md b/docs/design/features/byreflike-generics.md index ee15cd4b708680..c070f712d4dc04 100644 --- a/docs/design/features/byreflike-generics.md +++ b/docs/design/features/byreflike-generics.md @@ -49,7 +49,7 @@ The compiler will need an indication for existing troublesome APIs where ByRefLi namespace System.Runtime.CompilerServices { /// - /// Indicates to the compiler that constrain checks should be suppressed + /// Indicates to the compiler that constraint checks should be suppressed /// and will instead be enforced at run-time. /// [AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property)] From 465820777edd86516805d929efc927f239a12c90 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Fri, 15 Apr 2022 10:17:54 -0700 Subject: [PATCH 14/14] Update byreflike-generics.md --- docs/design/features/byreflike-generics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/design/features/byreflike-generics.md b/docs/design/features/byreflike-generics.md index c070f712d4dc04..e3a0e330b2a3bd 100644 --- a/docs/design/features/byreflike-generics.md +++ b/docs/design/features/byreflike-generics.md @@ -118,7 +118,7 @@ Enumerating of constructors/methods on `Span` and `ReadOnlySpan` may throw ## Special IL Sequences -The following are IL sequences involving the `box` instruction. They are used for common C# language constructs and shall continue to be valid, even with ByRefLike types, in cases where the result can be computed at JIT time and elided safely. The conditions where each sequence is elided are described below and each condition will be added to the ECMA-335 addendum. +The following are IL sequences involving the `box` instruction. They are used for common C# language constructs and shall continue to be valid, even with ByRefLike types, in cases where the result can be computed at JIT time and elided safely. These sequences must now be elided when the target type is ByRefLike. The conditions where each sequence is elided are described below and each condition will be added to the ECMA-335 addendum. `box` ; `unbox.any` – The box target type is equal to the unboxed target type.