[Proposal] Support for referencing an attributed member's generic instantiation in a typeof()
expression in a CustomAttribute type argument
#9397
Replies: 4 comments 11 replies
-
Great news! Just to confirm, does this mean with the |
Beta Was this translation helpful? Give feedback.
-
It would be good to clarify how |
Beta Was this translation helpful? Give feedback.
-
I am not sure how to interpret this, given that the context is either containing TypeDef, or containing MethodDef. Neither can represent an instantiation. Are you suggesting that |
Beta Was this translation helpful? Give feedback.
-
See #8198 I have a fully working prototype of runtime for this: it turns out that we only need some changes to S.P.CoreLib to enable this scenario, to support this we only need to pass the generic context while resolving generic attributes. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Today, the following is illegal:
In the .NET runtime team, we've had to work around this for some of the .NET interop source-generation APIs.
We've introduced the
GenericPlaceholder
struct to handle a subset of the use cases for our problem.The largest problem here is how to encode the generic arguments in the custom attributes. With the implementation of the
UnsafeAccessorTypeAttribute
, we on the runtime team have decided how we want to encode generic arguments based on the target of an attribute for a string argument that represents a full type name.Now that we've done so for that case, we could use the same design to enable encoding the custom attribute blobs for types: Type generic parameters are encoded as
!N
for the N-th generic parameter on the type, and method generic parameters are encoded as!!N
for the N-th generic parameter on the method.For the examples above, these would be the encodings:
typeof(T)
->"!0"
typeof(C<U>)
->"C[[!!0]], AssemblyName"
This proposal does not cover instantiating generic arguments with the generic parameters of the attributed elements, as that implementation would require significantly more effort on the runtime side.
For nested types, the encoding would be based on the ECMA encoding for generic types. In particular, a type nested in a generic has the generic parameters of all containing types prepended to its generic parameter list. The same is true for local funtions (they will have generic arguments corresponding to their containing methods prepended to their generic parameter list). Let's look at an example below:
A
CustomAttribute
'sValue
blob can use this encoding when theParent
points to a row in one of the following tables:Usage of this encoding with
CustomAttribute
rows withParent
columns pointing to other tables would be illegal.When a
CustomAttribute
'sParent
points to a row in any of the above tables, these are the rules for resolving the types:!N
-> Resolves equivalent to the signature typeELEMENT_TYPE_VAR N
.TypeDef
, resolves to the N'th generic argument of the containingTypeDef
TypeDef
, resolves to the N'th generic argument of the attributedTypeDef
.When a
CustomAttribute
'sParent
points to a row in theMethodDef
orParam
tables, these are the additional rules for resolving the types (previous rules are also valid):!!N
-> Resolved equivalent to the signature typeELEMENT_TYPE_MVAR N
Param
, resolves to the N'th generic argument of the containingMethodDef
MethodDef
, resolves to the N'th generic argument of the currentMethodDef
.For instantiated types, these are resolved based on the specific instantiation the attribute was discovered on.
Note
There is work to be done on the runtime side to pass the correct information where when resolving the custom attribute blobs (we currently don't propagate the information needed, but we could relatively easy).
Here's the spec changes for scoping. The diff is applied to https://github.com/dotnet/csharpstandard/tree/standard-v7/ after accounting for https://github.com/dotnet/csharplang/blob/main/proposals/csharp-11.0/extended-nameof-scope.md edits (nameof changes in bold, changes for this feature in strikethrough and italic):
Methods
The method’s type_parameters are in scope throughout the method_declaration, and can be used to form types throughout that scope in return_type or ref_return_type, method_body or ref_method_body, and type_parameter_constraints_clauses
but not in attributes, except within a nameof expression in attributes.Method Parameters
A method declaration creates a separate declaration space (§7.3) for parameters and type parameters. Names are introduced into this declaration space by the type parameter list and the formal parameter list of the method. Names are introduced into this declaration space by the type parameter list in attributes placed on the method or its parameters.
andNames are introduced into this declaration space by the formal parameter list of the method in nameof expressions in attributes placed on the method or its parameters. The body of the method, if any, is considered to be nested within this declaration space. It is an error for two members of a method declaration space to have the same name. It is an error for the method declaration space and the local variable declaration space of a nested declaration space to contain elements with the same name.Scopes
The scope of a type parameter declared by a type_parameter_list on a class_declaration (§15.2) is the class_base, type_parameter_constraints_clauses, attributes, and class_body of that class_declaration.
Note: Unlike members of a class, this scope does not extend to derived classes. end note
The scope of a type parameter declared by a type_parameter_list on a struct_declaration (§16.2) is the struct_interfaces, type_parameter_constraints_clauses, attributes, and struct_body of that struct_declaration.
The scope of a type parameter declared by a type_parameter_list on an interface_declaration (§18.2) is the interface_base, type_parameter_constraints_clauses , attributes, and interface_body of that interface_declaration.
The scope of a type parameter declared by a type_parameter_list on a delegate_declaration (§20.2) is the return_type, formal_parameter_list, attributes, and type_parameter_constraints_clauses of that delegate_declaration.
The scope of a type parameter declared by a type_parameter_list on a method_declaration (§15.6.1) is the attributes and method_declaration
** and nameof expressions in an attribute on the method declaration or its parameters**.Note
As per the quote below from https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/statements.md#1364-local-function-declarations (combined with https://github.com/dotnet/csharplang/blob/main/proposals/csharp-9.0/local-function-attributes.md), the same modification to scoping applies to local functions.
Beta Was this translation helpful? Give feedback.
All reactions