-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Description
Background and motivation
As part of the new extension everything feature, Roslyn will emit a new [ExtensionMarkerNameAttribute(...)] attribute in some cases, all documented in the linked spec. This issue tracks adding the attribute to the runtime as well, so it can be shared.
Relates to feature test plan dotnet/roslyn#76130
API Proposal
namespace System.Runtime.CompilerServices;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, Inherited = false)]
public sealed class ExtensionMarkerNameAttribute : Attribute
{
public ExtensionMarkerNameAttribute(string name)
=> Name = name;
public string Name { get; }
}Note: in .NET 10, we're only planning to use this attribute on methods and properties. The other targets may be used in later releases.
API Usage
The attribute will be disallowed to be used in source by newer C# compiler (like we do with other compiler-specific attributes, like [Dynamic], [IsReadOnly], [IsUnmanaged], ...).
Here's an example of how the attribute will be used in metadata:
public static class E
{
extension(int)
{
public static void M() { }
}
}.class public auto ansi abstract sealed beforefieldinit E
extends System.Object
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 )
// grouping type (carries the IL-level view of type parameters of extension declaration, which allows for stable public APIs)
.class nested public auto ansi sealed specialname '<Extension>$8048A6C8BE30A622530249B904B537EB'<$T0>
extends System.Object
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 )
// marker type (carries the type parameters of extension declaration)
.class nested public auto ansi abstract sealed specialname '<Marker>$2789E59A55056F0AD9E820EBD5BCDFBF'<T>
extends System.Object
{
// marker method (carries extension parameter)
.method public hidebysig specialname static void '<Extension>$' ( !T '' ) cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
IL_0000: ret
}
}
// extension method (stub)
.method public hidebysig static void M () cil managed
{
// attributes encodes '<Marker>$2789E59A55056F0AD9E820EBD5BCDFBF'
.custom instance void System.Runtime.CompilerServices.ExtensionMarkerNameAttribute::.ctor(string) = (
01 00 29 3c 4d 61 72 6b 65 72 3e 24 32 37 38 39
45 35 39 41 35 35 30 35 36 46 30 41 44 39 45 38
32 30 45 42 44 35 42 43 44 46 42 46 00 00
)
IL_0000: ldnull
IL_0001: throw
}
}
// implementation method
.method public hidebysig static void M<T> () cil managed
{
IL_0000: nop
IL_0001: ret
}
}
Alternative Designs
Name property or field
One thing I'd like to confirm is whether the Name property should be a property or a field. It looks like all the attributes embedded by Roslyn have a field, not a property.
AttributeTargets
Options:
- as proposed (targets we think we may need eventually)
- use the minimal set needed for C# 14 (only
.Methodand.Property) - use broader set, either adding
.Constructor(which we don't think we'll need) or usingAttributeTargets.All(includes other clearly irrelevant targets like.Assembly,.Module,.Parameter,.ReturnType, ...)
Note on forward compatibility:
The C# compiler in .NET 10 ignores any usage of the attribute in places that are unexpected/disallowed. Such usages could come from IL or from another compiler (which doesn't disallow using the attribute in source).
The compiler also ignores any member kinds that it doesn't understand/expect inside the extension metadata. For example, it ignores events, nested types, fields, etc.