From f00798ed925eff1805a0abdb86247fd8c18cb916 Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Tue, 19 Mar 2024 16:01:50 -0700 Subject: [PATCH 1/4] Add examples for generic support. --- .../UnsafeAccessorAttribute.xml | 207 ++++++++++-------- 1 file changed, 121 insertions(+), 86 deletions(-) diff --git a/xml/System.Runtime.CompilerServices/UnsafeAccessorAttribute.xml b/xml/System.Runtime.CompilerServices/UnsafeAccessorAttribute.xml index f4bad7e63f9..4c1993a7257 100644 --- a/xml/System.Runtime.CompilerServices/UnsafeAccessorAttribute.xml +++ b/xml/System.Runtime.CompilerServices/UnsafeAccessorAttribute.xml @@ -31,94 +31,129 @@ Provides access to an inaccessible member of a specific type. - or . - -For , , , and , the type of the first argument of the annotated `extern static` method identifies the owning type. Only the specific type defined will be examined for inaccessible members. The type hierarchy is not walked looking for a match. - -The value of the first argument is treated as `this` pointer for instance fields and methods. - -The first argument must be passed as `ref` for instance fields and methods on structs. - -The value of the first argument is not used by the implementation for `static` fields and methods. - -The return value for an accessor to a field can be `ref` if setting of the field is desired. - - -Constructors can be accessed using or . - - -The return type is considered for the signature match. Modreqs and modopts are initially not considered for the signature match. However, if an ambiguity exists ignoring modreqs and modopts, a precise match is attempted. If an ambiguity still exists, is thrown. - -By default, the attributed method's name dictates the name of the method/field. This can cause confusion in some cases since language abstractions, like C# local functions, generate mangled IL names. The solution to this is to use the `nameof` mechanism and define the property. - + or . Generic parameters are supported, but must match the target in form and index (that is, type parameters must be type parameters and method parameters must be method parameters). The `extern static` method's generic parameters must also exactly match any constraints reflected on the target. If constraints do not match the method will throw . + +For , , , and , the type of the first argument of the annotated `extern static` method identifies the owning type. Only the specific type defined will be examined for inaccessible members. The type hierarchy is not walked looking for a match. + +The value of the first argument is treated as `this` pointer for instance fields and methods. + +The first argument must be passed as `ref` for instance fields and methods on structs. + +The value of the first argument is not used by the implementation for `static` fields and methods and may be `null`. + +The return value for an accessor to a field can be `ref` if setting of the field is desired. + + +Constructors can be accessed using or . + + +The return type is considered for the signature match. Modreqs and modopts are initially not considered for the signature match. However, if an ambiguity exists ignoring modreqs and modopts, a precise match is attempted. If an ambiguity still exists, is thrown. + +By default, the attributed method's name dictates the name of the method/field. This can cause confusion in some cases since language abstractions, like C# local functions, generate mangled IL names. The solution to this is to use the `nameof` mechanism and define the property. + ]]> - - public class Class - { - static void StaticPrivateMethod() { } - static int StaticPrivateField; - Class(int i) { PrivateField = i; } - void PrivateMethod() { } - int PrivateField; - int PrivateProperty { get => PrivateField; } - } - - public void CallStaticPrivateMethod() - { - StaticPrivateMethod(null); - - [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = nameof(StaticPrivateMethod))] - extern static void StaticPrivateMethod(Class c); - } - public void GetSetStaticPrivateField() - { - ref int f = ref GetSetStaticPrivateField(null); - - [UnsafeAccessor(UnsafeAccessorKind.StaticField, Name = "StaticPrivateField")] - extern static ref int GetSetStaticPrivateField(Class c); - } - public void CallPrivateConstructor() - { - Class c1 = PrivateCtor(1); - - Class c2 = (Class)RuntimeHelpers.GetUninitializedObject(typeof(Class)); - - PrivateCtorAsMethod(c2, 2); - - [UnsafeAccessor(UnsafeAccessorKind.Constructor)] - extern static Class PrivateCtor(int i); - - [UnsafeAccessor(UnsafeAccessorKind.Method, Name = ".ctor")] - extern static void PrivateCtorAsMethod(Class c, int i); - - } - public void CallPrivateMethod(Class c) - { - PrivateMethod(c); - - [UnsafeAccessor(UnsafeAccessorKind.Method, Name = nameof(PrivateMethod))] - extern static void PrivateMethod(Class c); - } - public void GetPrivateProperty(Class c) - { - int f = GetPrivateProperty(c); - - [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_PrivateProperty")] - extern static int GetPrivateProperty(Class c); - } - public void GetSetPrivateField(Class c) - { - ref int f = ref GetSetPrivateField(c); - - [UnsafeAccessor(UnsafeAccessorKind.Field, Name = "PrivateField")] - extern static ref int GetSetPrivateField(Class c); - } + + public class Class + { + static void StaticPrivateMethod() { } + static int StaticPrivateField; + Class(int i) { PrivateField = i; } + void PrivateMethod() { } + int PrivateField; + int PrivateProperty { get => PrivateField; } + } + + public void CallStaticPrivateMethod() + { + StaticPrivateMethod(null); + + [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = nameof(StaticPrivateMethod))] + extern static void StaticPrivateMethod(Class c); + } + public void GetSetStaticPrivateField() + { + ref int f = ref GetSetStaticPrivateField(null); + + [UnsafeAccessor(UnsafeAccessorKind.StaticField, Name = "StaticPrivateField")] + extern static ref int GetSetStaticPrivateField(Class c); + } + public void CallPrivateConstructor() + { + Class c1 = PrivateCtor(1); + + Class c2 = (Class)RuntimeHelpers.GetUninitializedObject(typeof(Class)); + + PrivateCtorAsMethod(c2, 2); + + [UnsafeAccessor(UnsafeAccessorKind.Constructor)] + extern static Class PrivateCtor(int i); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = ".ctor")] + extern static void PrivateCtorAsMethod(Class c, int i); + + } + public void CallPrivateMethod(Class c) + { + PrivateMethod(c); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = nameof(PrivateMethod))] + extern static void PrivateMethod(Class c); + } + public void GetPrivateProperty(Class c) + { + int f = GetPrivateProperty(c); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_PrivateProperty")] + extern static int GetPrivateProperty(Class c); + } + public void GetSetPrivateField(Class c) + { + ref int f = ref GetSetPrivateField(c); + + [UnsafeAccessor(UnsafeAccessorKind.Field, Name = "PrivateField")] + extern static ref int GetSetPrivateField(Class c); + } + + // Generic example + public class Class<T> + { + private T _field; + private void M(T t) { } + private void GM<U>(U u) { } + private void GMWithConstraints<U>(T t, U u) where T: U, IEquatable<T> { } + } + + class Accessors<V> + { + [UnsafeAccessor(UnsafeAccessorKind.Field, Name = "_field")] + extern static ref V GetSetPrivateField(Class<V> c); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "M")] + extern static void CallM(Class<V> c, V v); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "GM")] + extern static void CallGM<W>(Class<V> c, W w); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "GMWithConstraints")] + extern static void CallGMWithConstraints<W>(Class<V> c, V v, W w) where V: W, IEquatable<V>; + } + + public void AccessGenericType(Class<int> c) + { + ref int f = ref Accessors<int>.GetSetPrivateField(c); + + Accessors<int>.CallM(c, 1); + + Accessors<int>.CallGM<string>(c, string.Empty); + + Accessors<string>.CallGMWithConstraints<object>(c, string.Empty, new object()); + } @@ -186,7 +221,7 @@ By default, the attributed method's name dictates the name of the method/field. Gets or sets the name of the member to which access is provided. To be added. - The name defaults to the annotated method name if not specified. + The name defaults to the annotated method name if not specified. The name must be unset/null for . From 4bda805e59c845d1dc901c2c0a5222914986fcc7 Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Tue, 19 Mar 2024 17:04:14 -0700 Subject: [PATCH 2/4] Fix generic example --- .../UnsafeAccessorAttribute.xml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/xml/System.Runtime.CompilerServices/UnsafeAccessorAttribute.xml b/xml/System.Runtime.CompilerServices/UnsafeAccessorAttribute.xml index 4c1993a7257..2be2f54a5c3 100644 --- a/xml/System.Runtime.CompilerServices/UnsafeAccessorAttribute.xml +++ b/xml/System.Runtime.CompilerServices/UnsafeAccessorAttribute.xml @@ -126,22 +126,22 @@ By default, the attributed method's name dictates the name of the method/field. private T _field; private void M(T t) { } private void GM<U>(U u) { } - private void GMWithConstraints<U>(T t, U u) where T: U, IEquatable<T> { } + private void GMWithConstraints<U, V>(U u, V v) where U : V, IEquatable<U> { } } class Accessors<V> { [UnsafeAccessor(UnsafeAccessorKind.Field, Name = "_field")] - extern static ref V GetSetPrivateField(Class<V> c); + public extern static ref V GetSetPrivateField(Class<V> c); [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "M")] - extern static void CallM(Class<V> c, V v); + public extern static void CallM(Class<V> c, V v); [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "GM")] - extern static void CallGM<W>(Class<V> c, W w); + public extern static void CallGM<X>(Class<V> c, X x); [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "GMWithConstraints")] - extern static void CallGMWithConstraints<W>(Class<V> c, V v, W w) where V: W, IEquatable<V>; + public extern static void CallGMWithConstraints<X, Y>(Class<V> c, X x, Y y) where X : Y, IEquatable<X>; } public void AccessGenericType(Class<int> c) @@ -152,7 +152,7 @@ By default, the attributed method's name dictates the name of the method/field. Accessors<int>.CallGM<string>(c, string.Empty); - Accessors<string>.CallGMWithConstraints<object>(c, string.Empty, new object()); + Accessors<int>.CallGMWithConstraints<string, object>(c, string.Empty, new object()); } From 9e502f9c79da94e7c50841c98a526135f2053066 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Tue, 19 Mar 2024 20:54:29 -0700 Subject: [PATCH 3/4] Update xml/System.Runtime.CompilerServices/UnsafeAccessorAttribute.xml --- .../UnsafeAccessorAttribute.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/xml/System.Runtime.CompilerServices/UnsafeAccessorAttribute.xml b/xml/System.Runtime.CompilerServices/UnsafeAccessorAttribute.xml index 2be2f54a5c3..ca5dfc9cdb6 100644 --- a/xml/System.Runtime.CompilerServices/UnsafeAccessorAttribute.xml +++ b/xml/System.Runtime.CompilerServices/UnsafeAccessorAttribute.xml @@ -35,7 +35,9 @@ ## Remarks -You can apply this attribute to an `extern static` method. The implementation of the `extern static` method annotated with this attribute will be provided by the runtime based on the information in the attribute and the signature of the method that the attribute is applied to. The runtime will try to find the matching method or field and forward the call to it. If the matching method or field is not found, the body of the `extern static` method will throw or . Generic parameters are supported, but must match the target in form and index (that is, type parameters must be type parameters and method parameters must be method parameters). The `extern static` method's generic parameters must also exactly match any constraints reflected on the target. If constraints do not match the method will throw . +You can apply this attribute to an `extern static` method. The implementation of the `extern static` method annotated with this attribute will be provided by the runtime based on the information in the attribute and the signature of the method that the attribute is applied to. The runtime will try to find the matching method or field and forward the call to it. If the matching method or field is not found, the body of the `extern static` method will throw or . + +Generic parameters are supported since .NET 9. Generic parameters must match the target in form and index (that is, type parameters must be type parameters and method parameters must be method parameters). The `extern static` method's generic parameters must also exactly match any constraints reflected on the target. If constraints do not match the method will throw . For , , , and , the type of the first argument of the annotated `extern static` method identifies the owning type. Only the specific type defined will be examined for inaccessible members. The type hierarchy is not walked looking for a match. From d8285dc2ed4b2dff2bcf4af1bb06d6a2dbc78ae8 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Wed, 20 Mar 2024 10:18:19 -0700 Subject: [PATCH 4/4] Apply suggestions from code review Co-authored-by: Genevieve Warren <24882762+gewarren@users.noreply.github.com> --- .../UnsafeAccessorAttribute.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xml/System.Runtime.CompilerServices/UnsafeAccessorAttribute.xml b/xml/System.Runtime.CompilerServices/UnsafeAccessorAttribute.xml index ca5dfc9cdb6..00d0aa77313 100644 --- a/xml/System.Runtime.CompilerServices/UnsafeAccessorAttribute.xml +++ b/xml/System.Runtime.CompilerServices/UnsafeAccessorAttribute.xml @@ -37,7 +37,7 @@ You can apply this attribute to an `extern static` method. The implementation of the `extern static` method annotated with this attribute will be provided by the runtime based on the information in the attribute and the signature of the method that the attribute is applied to. The runtime will try to find the matching method or field and forward the call to it. If the matching method or field is not found, the body of the `extern static` method will throw or . -Generic parameters are supported since .NET 9. Generic parameters must match the target in form and index (that is, type parameters must be type parameters and method parameters must be method parameters). The `extern static` method's generic parameters must also exactly match any constraints reflected on the target. If constraints do not match the method will throw . +Generic parameters are supported since .NET 9. Generic parameters must match the target in form and index (that is, type parameters must be type parameters and method parameters must be method parameters). The `extern static` method's generic parameters must also exactly match any constraints reflected on the target. If constraints don't match, the method throws . For , , , and , the type of the first argument of the annotated `extern static` method identifies the owning type. Only the specific type defined will be examined for inaccessible members. The type hierarchy is not walked looking for a match. @@ -45,7 +45,7 @@ The value of the first argument is treated as `this` pointer for instance fields The first argument must be passed as `ref` for instance fields and methods on structs. -The value of the first argument is not used by the implementation for `static` fields and methods and may be `null`. +The value of the first argument is not used by the implementation for `static` fields and methods and can be `null`. The return value for an accessor to a field can be `ref` if setting of the field is desired.