The mangling has to be stable across compilations with respect to
    any source- and binary-compatible changes to the pattern
    declaration.  One mangling that works quite well is to use the
    "symbolic-freedom encoding" of the erasure of the pattern
    descriptor.  Because the erasure of the descriptor is exactly as
    stable as any other method signature derived from source
    declarations, it will have the desired binary compatibility
properties, overriding will work as expected, etc.

I think we need a least to use a special name like <deconstructor> the same way we have <init>.

Yes.  Instance/static patterns will have names, so for them, we'll use the name as declared in the source.  Dtors have no names, just like ctors, so we have to invent something to stand in for that. <dtor> or similar is fine.

I agree that we also need to encode the method type descriptor (the carrier type) into the name, so the name of the method in the classfile should be <deconstructor+mangle> or <name+mangle> (or perhaps <pattern+name+mangle> ofr the pattern methods).

The key constraint is that the mangled name be stable with respect to compatible changes in the declaration.  The rest is just "classfile syntax."



    #### Return value

    In an earlier design, we used a pattern object (which was a bundle
    of method handles) as the return value of the pattern.  This
    enabled clients to invoke these via condy and bind method handles
    into the constant pool for deconstruction and static patterns.

    Either way, we make use of some sort of carrier object to carry
    the bindings from the pattern to the client; either we return the
    carrier from the pattern method, or there is a method on the
    pattern object that we invoke to get a carrier.  We have a few
    preferences about the carrier; we'd like to be able to late-bind
    to the actual implementation (i.e., we don't want to freeze the
    name of a carrier class in the method descriptor), and at least
    for records, we'd like to let the record instance itself be the
    carrier (since it is immutable and we can just invoke the
    accessors to get the bindings.)


So the return type is either Object (too hide the type of the carrier) or a lambda that returns an Object (PatternObject or PatternCarrier acting like a glorified lambda).

If the pattern method actually runs the match, then I think Object is right.  If the method returns a constant bundle of method handles, then it can return something like PatternHandle or a matcher lambda.  But I am no longer seeing the benefit in this extra layer of indirection, given how the other translation work has played out.



        Pattern {
            u2 attr_name;
            u4 attr_length;
            u2 patternFlags; // bitmask
            u2 patternName;  // index of UTF8 constant
            u2 patternDescr; // index of MethodType (or alternately
    UTF8) constant
            u2 attributes_count;
            attribute_info attributes[attributes_count];
        }

    This says that "this method is a pattern", reifies the name of the
    pattern (patternName), reifies the pattern descriptor
    (patternDescr) which encodes the types of the bindings as a method
    descriptor or MethodType, and has attributes which can carry
    annotations, parameter metadata, and signature metadata for the
    bindings.   The existing attributes (e.g. Signature,
    ParameterNames, RVAA) can be reused as is, with the interpretation
    that this is the signature (or names, or annos) of the *bindings*,
    not the input parameters.  Flags can carry things like
"deconstructor pattern" or "partial pattern" as needed.

From the classfile POV, a constructor is a method with a funny name in between brackets, i think deconstructor and pattern methods should work the same way.

Be careful of extrapolating from one data point.  Dtor are only one form of declared patterns; we also have to accomodate static and instance patterns.

Reply via email to