OK, so in the old world, D has m(Date).

Migration step 1: author: Date -> LocalDateTime

old class D { m(Date); }
Migration step 2: change method declarer: new class D {m(LDT);}
           and javac creates a forwarder m(Date); -> Date->LDT/m(LDT);

Now, D has m(LDT), with a forwarder from m(Date) -> m(LDT), with some sort of metadata stapled somewhere to effect the Date <--> LDT conversions.

class E extends D { m(Date); } which now overrides the forwarder.
We do not change class E. We do not recompile it (I don’t know what 
recompilation would do here?)

On recompilation, we could do one of three things:

1.  Error: you're overriding a bridge, fix your program!
2.  Warning: you're overriding a bridge, I'll fix it for you (compiler adapts m(Date) to m(LDT). 3.  Warning: you're overriding a bridge, I'll believe you, and the VM will fix it for you (bringing us back to where you started: "we do not change E."

Which we choose at compilation time doesn't really affect what the VM has to do (you still have to deal with the unrecompiled E), so we can make this decision later.

old class ClientD invokevirtual D.m(Date) receiver:E
Migration step 3: new class ClientD invokevirtual D.m(LDT) receiver:E
    resolution: finds D.m(LDT)
    selection: starts with E, there is no E.m(LDT) so call D.m(LDT)

OK, so at this point, the classfiles that have been loaded look like:

    class D {
        void m(LDT) { real method }
        @Forwarding(m(LDT)) abstract void m(Date);
    }

    class E extends D {
        @Override
        m(Date) { impl }
    }

So D has members m(LTD) and m(Date), the latter is a forwarder. Therefore E has the same members (instance methods are inherited).

Here's how I would imagine this turns into in the VM:

    class D {
        void m(LTD) { real method }
        void m(Date d) { m(adapt(d)); }  // generated forwarder
    }

    class E extends D {
        private void m$synthetic(Date d) { real method, body as present in classfile }
        void m(LTD ltd) { m$synthetic(adapt(ltd)); }  // generated reverser
    }


        resolves
        selects
invokevirtual D::m(LTD)
        D::m(LTD)
        E::m(LTD)
invokevirtual D::m(Date)
        D::m(Date)
        D::m(Date), forwards to invvir D::m(LTD)
In turn, selects E::m(LTD)
invokevirtual E::m(LTD)
        E::m(LTD)
        E::m(LTD)
invokevirtual E::m(Date)
        D::m(Date)
        D::m(Date), forwards to invvir D::m(LTD)
In turn, selects E::m(LTD)


In other words, we arrange that once the vtable is laid out, it is as if no one ever overrides the forwarders -- they only override the real method.  Hence the reverser is needed only where a class (like E) actually overrides a descriptor that corresponds to a forwarder.

It is my belief that the expected behavior is that we want to invoke E.m(Date) 
with asType signature matching.
To do that, I propose that if the vm detects overriding of a forwarder, that we 
need to generate a reverser:

    E.m(Date) overrides D.m(Date)// forwarder: Date->LDT/invoke D.m(LDT)/return 
conversion

The reverser that we want would be
    E.m(LDT) overrides D.m(LDT) // reverser: LDT->Date/invoke E.m(Date)/return 
reverse conversion

I think we want: a reverser for E::m(LTD), but not for E::m(Date). Are we saying the same thing?

Reply via email to