implementation of record constructor auto-initialization [was Re: instance initializer]

Maurizio Cimadamore maurizio.cimadamore at oracle.com
Fri Sep 6 08:04:36 UTC 2019


(moving to amber-dev, since I want to focus on implementation issues here)

Vicente,
it is true that assessing whether a field is DA at the end of the 
constructor is a complex task. That said, we have machinery to do it in 
the compiler (Flow class).

My feeling here is that, perhaps, the compiler is attempting to do 
things too early in the pipeline; if the initialization code is added 
say, at Enter/TypeEnter, there's no way for the compiler to see what the 
initialization state of the fields actually is.

I think the best approach is to wait it out - that is, send the 
constructor unmodified through Attr, and then define some special logic 
in Flow that:

* issue errors if, at the end of record constructor there is some field 
that is neither DA nor DU
* do not issue errors at the end of a record constructor for DU fields
* keep a list of the DU fields for later

Then in Lower, we can look at the list, and add the missing initializers 
for the previously discovered DU fields. So, that cover the first part 
of your concerns.

The second, and more meaty part, is that if you have stuff like:

record R(int i, int j) {
     public R {  // compact constructor
         if (i < 0) {
             this.i = -i;
         } else {
             this.j = j;
         }
     }
}

You might be induced to think that the compiler has to put an 
initializer inside the if branch, and another inside the else (one for 
this.j, the other for this.i). But this is not actually the case; in 
this example, both this.i and this.j are neither DA nor DU at the end of 
the constructor -> error.

In other words, I think the rules stated by Brian already take into 
account the fact that the constructor can be messy, and that the only 
thing that auto-initialization is allowed to do, is to add a bunch of 
initializers _at the end_ of the constructor; if that doesn't work - 
error (because that means that there were some fields initialized in 
some paths but not in others).

Makes sense?

Maurizio

On 06/09/2019 00:47, Vicente Romero wrote:
>
>
> On 9/5/19 6:14 PM, forax at univ-mlv.fr wrote:
>>
>>
>> ------------------------------------------------------------------------
>>
>>     *De: *"Brian Goetz" <brian.goetz at oracle.com>
>>     *À: *"Tagir Valeev" <amaembo at gmail.com>, "Remi Forax"
>>     <forax at univ-mlv.fr>
>>     *Cc: *"amber-spec-experts" <amber-spec-experts at openjdk.java.net>
>>     *Envoyé: *Jeudi 5 Septembre 2019 23:26:34
>>     *Objet: *Re: Draft JLS spec for records
>>
>>
>>
>>         I don't think so. A compact constructor (or require
>>         initializer, as you propose) could be not the only
>>         constructor. An instance initializer is convenient because
>>         it's added to every constructor, regardless of whether it's
>>         compact or not. So the new thing doesn't supersede the
>>         instance initializer and I see no good reason to explicitly
>>         disable it.
>>
>>
>>     Vicente offered another reason why we might want to prohibit the
>>     instance initializer, if only out of expediency; it complicates
>>     the analysis of which fields are DA on all paths through the
>>     constructor (and therefore, do not need to be automatically
>>     initialized.) If you have a record:
>>
>>         record Foo(int i) {
>>             { this.i = 0; }
>>         }
>>
>>     then the canonical constructor has to see that `i` is always
>>     initialized by the static init, and therefore should be not
>>     initialized.  Worse, if we have:
>>
>>         record Foo(int i) {
>>             { if (tuesday) this.i = 0; }
>>         }
>>
>>     then we have to issue a compilation error, since we have fields
>>     that are neither DA nor DU at the end of the initializer.
>>
>>     None of this is impossible to do, of course; it's just not clear
>>     whether it's worth it, given the limited utility of instance
>>     initializers in records (because we've already banned instance
>>     fields.)
>>
>>
>> If we want to fix that with an the analysis, it will have to work on 
>> user-written code mixed with generated code,
>> it doesn't seem to be a good idea.
>
> right I was thinking about the case:
>
> record R(int i, int j) {
>     public R {  // compact constructor
>         if (i < 0) {
>             this.i = -i;
>         } else {
>             this.j = j;
>         }
>     }
> }
>
> which doesn't have instance initializer blocks. The thing is where to 
> draw between cases where the compiler will just issue an error and 
> cases in which it will generate the missing initializations. In this 
> particular case the compiler will have to be very clever and generate:
>
> public R {  // compact constructor
>         if (i < 0) {
>             this.i = -i;
>             this.j = j;  // automatic code
>         } else {
>             this.i = i; // automatic code
>             this.j = j;
>         }
>     }
>
> we can do it, it will be complex though, but do we want to do it? 
> Where to draw the line?
>
> Thanks,
> Vicente
>>
>> so it's Ok to not support instance initializers in record.
>>
>> Rémi
>>
>>
>>
>>
>


More information about the amber-dev mailing list