----- Original Message -----
> From: "Dan Heidinga" <heidi...@redhat.com>
> To: "Remi Forax" <fo...@univ-mlv.fr>
> Cc: "amber-spec-experts" <amber-spec-experts@openjdk.java.net>
> Sent: Tuesday, June 14, 2022 3:10:46 PM
> Subject: Re: with and binary backward compatibility

> Remi, to restate your concern slightly - it sounds like there may be
> binary (and source?) compatibility concerns with the implementation of
> with'ers.  You've pitched a possible implementation using
> invokedynamic (every compiler writer's favourite swiss army knife) but
> prefer something that is more explicit in the bytecode.

Yes, BTW there is a simple way to avoid those binary compatibility issues, 
restrict "with" to the nest or the package/module (like sealed).

> I'm still
> working my way through the full reconstruction document but I assume
> compatibility and implementation have been given some thought even if
> they don't show up in that document.

The problem is that Brian believes that all constructors and deconstructors are 
dual, i feel this is the wrong model, it does not work that way, a constructor 
if not canonical creates values before calling the canonical constructor while 
all deconstructors all match the same way but some provide fewer bindings than 
the others. The binary compatibility story of "with" shows that you can not do 
a circle point -> bindings -> point using any constructors but the canonical 
constructor.  

> 
> Let's aim for the high order bits first - figuring out if the feature
> is desirable and if the general direction works before deep diving
> into class file representation and binary compatibility.

agree

> 
> So I'd suggest putting a pin in this topic and circling back to it
> after further discussion of the core concept.
> 
> Just my two cents =)
> 
> --Dan

Rémi

> 
> On Tue, Jun 14, 2022 at 8:23 AM Remi Forax <fo...@univ-mlv.fr> wrote:
>>
>> Hi all,
>> Let say we have a Point with 2 components
>>   record Point(int x, int y) { }
>>
>> Then we change the record to add a 3rd components in a more or less backward
>> compatible way
>>   record Point(int x, int y, int z) {
>>     Point(int x, int y) {
>>       this(x, y, 0);  // creation of the new value 0
>>     }
>>   }
>>
>> Now, let say there is a 'with' somewhere in another code
>>
>>   var newPoint = point with { x = 3; };
>>
>> If this code is compiled when the record Point had only two components, so 
>> this
>> is equivalent to
>>
>>   Point(int x, int y) = point;  // i.e. int x = point.x(); int y = point.y();
>>   x = 3;
>>   var newPoint = new Point(x, y);
>>
>> The problem is that if we run that code with the new version of Point (the 
>> one
>> with 3 components),
>> newPoint.z is not equals to point.z but to 0, so once there is a 'with'
>> somewhere, there is no backward compatibility anymore.
>>
>> We can try to restore the backward compatibility by compiling to a slightly
>> different code using invokedynamic and a desugared method corresponding to 
>> the
>> body of the 'with'
>>
>>   var newPoint = invokedynamic (point) [desugared$method];  // equivalent to 
>> a
>>   call using with
>>
>>   static Point desugared$method(int x, int y, MethodHandle mh) {  // content 
>> of
>>   the body
>>     x = 3;
>>     return mh.invokeExact(x, y);
>>   }
>>
>> an at runtime, we generate a tree of method handles that does more or less
>>   stub(Point point) {
>>     return desugared$method(point.x(), point.y(), (a, b) -> new Point(a, b,
>>     point.z())
>>   }
>>
>> because this code is generated at runtime, it will be always compatible with 
>> the
>> latest version of Point.
>>
>> If we want to support this encoding, it means that the local variables of the
>> enclosing method need to be effectively final so the body of with can be 
>> lifted
>> to a private static method (exactly like a lambda).
>>
>>
>> If we generalize this a bit, we can also use the same trick for the record
>> pattern, in that case the pattern Point(int a, int b) is equivalent at 
>> runtime
>> to Point(int a, int b, _) once the runtime found that the canonical
>> deconstructor emits the values of 3 components.
>> I'm not sure it's a path i want to follow because i would prefer the record
>> pattern to match the shape excatly, but i find it more attractive than the 
>> idea
>> to have overloaded deconstructors.
>>
>> regards,
>> Rémi

Reply via email to