Back to Csharplang

Extensions WG 2024-06-07

meetings/working-groups/roles/extension-wg-2024-06-07.md

latest4.3 KB
Original Source

Extensions WG 2024-06-07

Type erasure encoding

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:

  • generic attribute (pinging David to ask limitations/bugs on older frameworks)
  • attribute with typeof (may not refer to type parameters)
  • nested type
  • nested type with parallel method

Decision: we'll spec out the parallel method in nested type design.

modopt

// 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

Generic attribute

// 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

Attribute with typeof

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 { }

Nested type

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;
    }
}

Nested type with parallel methods

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;
    }
}

Optimization? one secret method per type to encode

    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 { }
    }

Optimization? remove parameters that don't involve extensions/erased

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
    }

Properties

   // E this[E e]
   No parallel property, only have parallel accessors

Types?

   // class C<T> : I<E<T>> { }
   class C<T> : [SecretField("Field1")] I<Underlying>
   {
       private abstract class Secret
       {
          I<E<T>> Field1;
       }
   }

Translation from instance methods to static metadata methods

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?