meetings/working-groups/roles/extension-wg-2024-06-07.md
We need to encode the extension type (for extension-aware compiler) and the underlying type (erasure and for non-extension-aware tools).
We'll need to encode tuple names, dynamic bits, nullability, native integer for both types. Ideally, this should be done in a way that doesn't break tools that understand the current attribute/constructor encoding.
Options for encoding extension type:
Decision: we'll spec out the parallel method in nested type design.
// source
void M(E e)
// codegen
void M(C modopt(E) e)
virtual void M2(C c)
// other assembly
override void M2(E e)
Con: binary break to change API from extension to underlying type and back Con: problem for override at language level (might be solvable) Con: need to new solution encode the second set of tuples names/dynamic/nullability
// source
void M(E e)
// codegen
void M([Extension<E>] Underlying e) // System.Extension`1 (new)
Con: Attribute doesn't exist downlevel (we should synthesize) Con: Generic attribute not supported before .NET 7 (prove there's enough support, or block off) Con: need to new solution encode the second set of tuples names/dynamic/nullability
Limitation with typeof in attribute referencing type parameter (typeof encoded as fully-qualified name, context-free, but no such syntax exists for type parameters)
class C<T>
{
void M(E<List<T>> e)
// encoding: void M([Extension(typeof(E<List<T>>))] object e) // not possible
}
explicit extension E<U> for object { }
Doesn't work with method type parameter. We could use a generic private implementation nested type with same number of type parameters.
explicit extension E<T1, T2> where T2 : IDisposable { }
class C<T>
{
// source: void M<U>(E<T, U> e) where U : IDisposable
void M<U>([Extension("Secret1", "Field1")] Underlying e) // problem with method type parameter
private class Secret1<U> where U : IDisposable
{
E<T, U> Field1;
}
}
Pro: Nested type reduces pollution of members for other tools Pro: Runtime may not need to load the members of nested type (to be confirmed) Pro: natural encoding of tuple names/dynamic/etc Con: copies all the parameters Pro: it naturally offers a place to encode the second set of tuples names/dynamic/nullability information
Secret class is abstract and parallel methods are abstract Note: Hard to point backwards from parallel method, so attribute is on visible method
class C<T>
{
[SecretMethod("Method1")] // explicit matching, rather than complex auto-matching
U M<U>(U e, int x) where U : IDisposable
private abstract class Secret
{
abstract void/E<T, U1> Method1<U1>(E<T, U1> "", Secret "") where U1 : IDisposable;
}
}
private class Secret
{
// More parameter cause more methods, but we can share
E<T, U1> Method1<U1> where U1 : IDisposable { }
E<T, U1> Method1<U2> where U1 : IDisposable { }
}
We could use parameter names encoding ordinals from original method:
private class Secret
{
void Method1<U1>(E<T, U1> p0 /*, Secret x */) where U1 : IDisposable
}
// E this[E e]
No parallel property, only have parallel accessors
// class C<T> : I<E<T>> { }
class C<T> : [SecretField("Field1")] I<Underlying>
{
private abstract class Secret
{
I<E<T>> Field1;
}
}
CLS rules for events requires a specific signature (single parameter), but we need a second parameter. See ECMA 335 II.22.28. How bad is it? May break other compilers. Would those compilers allow to consume those methods or treat as invalid metadata? Disallow events?