-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add generic attributes proposal #4936
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
# Generic Attributes | ||
|
||
## Summary | ||
[summary]: #summary | ||
|
||
When generics were introduced in C# 2.0, attribute classes were not allowed to participate. We can make the language more composable by removing (rather, loosening) this restriction. The .NET Core runtime has added support for generic attributes. Now, all that's missing is support for generic attributes in the compiler. | ||
|
||
## Motivation | ||
[motivation]: #motivation | ||
|
||
Currently attribute authors can take a `System.Type` as a parameter and have users pass a `typeof` expression to provide the attribute with types that it needs. However, outside of analyzers, there's no way for an attribute author to constrain what types are allowed to be passed to an attribute via `typeof`. If attributes could be generic, then attribute authors could use the existing system of type parameter constraints to express the requirements for the types they take as input. | ||
|
||
## Detailed design | ||
[design]: #detailed-design | ||
|
||
The following section is amended: https://github.com/dotnet/csharplang/blob/main/spec/classes.md#base-classes | ||
|
||
> The direct base class of a class type must not be any of the following types: System.Array, System.Delegate, System.MulticastDelegate, System.Enum, or System.ValueType. ~~Furthermore, a generic class declaration cannot use System.Attribute as a direct or indirect base class.~~ | ||
|
||
One important note is that the following section of the spec is *unaffected* when referencing the point of usage of an attribute, i.e. within an attribute list: https://github.com/dotnet/csharplang/blob/main/spec/types.md#type-parameters | ||
|
||
> A type parameter cannot be used anywhere within an attribute. | ||
|
||
This means that when a generic attribute is used, its construction needs to be fully "closed", i.e. not containing any type parameters, which means the following is still disallowed: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is "closed" the correct term? "Opened" for generic types could refer to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think so: https://github.com/dotnet/csharplang/blob/main/spec/types.md#open-and-closed-types I think |
||
|
||
```cs | ||
using System; | ||
using System.Collections.Generic; | ||
|
||
public class Attr<T1> : Attribute { } | ||
|
||
public class Program<T2> | ||
{ | ||
[Attr<T2>] // error | ||
[Attr<List<T2>>] // error | ||
void M() { } | ||
} | ||
``` | ||
|
||
When a generic attribute is used in an attribute list, its type arguments have the same restrictions that `typeof` has on its argument. For example, `[Attr<dynamic>]` is an error. This is because "attribute-dependent" types like `dynamic`, `List<string?>`, `nint`, and so on can't be fully represented in the final IL for an attribute type argument, because there isn't a symbol to "attach" the `DynamicAttribute` or other well-known attribute to. | ||
|
||
## Drawbacks | ||
[drawbacks]: #drawbacks | ||
|
||
Removing the restriction, reasoning out the implications, and adding the appropriate tests is work. | ||
|
||
## Alternatives | ||
[alternatives]: #alternatives | ||
|
||
Attribute authors who want users to be able to discover the requirements for the types they provide to attributes need to write analyzers and guide their users to use those analyzers in their builds. | ||
|
||
## Unresolved questions | ||
[unresolved]: #unresolved-questions | ||
|
||
- [x] What does `AllowMultiple = false` mean on a generic attribute? If we have `[Attr<string>]` and `[Attr<object>]` both used on a symbol, does that mean "multiple" of the attribute are in use? | ||
- For now we are inclined to take the more restrictive route here and consider the attribute class's original definition when deciding whether multiple of it have been applied. In other words, `[Attr<string>]` and `[Attr<object>]` applied together is incompatible with `AllowMultiple = false`. | ||
|
||
## Design meetings | ||
|
||
- https://github.com/dotnet/csharplang/blob/main/meetings/2017/LDM-2017-02-21.md#generic-attributes | ||
- At the time there was a concern that we would have to gate the feature on whether the target runtime supports it. (However, we now only support C# 10 on .NET 6. It would be nice to for the implementation to be aware of what minimum target framework supports the feature, but seems less essential today.) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something about this line had me confused for a bit (as well as once before ~a year ago?), so I'm wondering if the spec needs to be rephrased to emphasize it refers to the attribute placement and not its definition.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does seem like it would be a little more clear to adjust the terminology to make attribute usages more distinct from their definitions.