RFR: JEP 359-Records: compiler code

Maurizio Cimadamore maurizio.cimadamore at oracle.com
Tue Nov 12 09:00:02 UTC 2019


I also took another look at tests - it seems to me that we're a bit thin 
in this department, I'll try to explain why.

* We need at least a combo test for the parser which tries records with 
different source levels and in different source positions (top-level, 
member, local), since we found several complex issues in this part of 
the parser

* I noted that, while we have many diagnostics tests, we have basically 
zero negative tests to check that the compiler is generating the desired 
error message _without any additional_ spurious errors. In principle, 
there should be at least one negative test for each record-related error 
message. This leads to replication with the diagnostics tests, but we've 
always done it this way. The diagnostic framework is for diagnostics - 
per se, it is insufficient to verify good quality of compiler output.

* I suggest maybe to group tests in different folders depending on the 
'category' they fall in. E.g. I see you have a reflection test and a 
serialization test, then some annotation processing tests - it would be 
nice to label these accordingly by putting them in separate folders.

* there's no test for checking what happens when javac is fed a 'bad 
record classfile attribute' (again a combo here is probably the right 
choice, given the number of ways the attribute can be wrong)

* I see no tests for constructor delegation to canonical constructor - 
again, here we should try different things, with different level of 
delegation (e.g. C1 -> CANONICAL, C1 -> C2 -> CANONICAL and so forth) to 
make sure javac is not tripped up by the indirections

* Probably DA/DU rules for compact constructor are, again, complex 
enough that when you start reasoning about 2-3 different record 
components, the number of different states go up pretty rapidly, and 
again a combo could be useful (completes normally, completes abruptly, 
explicitly assigns to x, implicitly assigns to x, ...).

* OriginTest seems a bit weak in that it tests _all implicit_ and _all 
explicit_ and nothing in between; maybe that's part of the test design, 
but looks odd

Maurizio



On 12/11/2019 08:42, Maurizio Cimadamore wrote:
> The compiler changes look good - I still see indentiation issues in 
> TypeEnter, especially in lines 1225-1238.
>
> Also, in JavacParser:
>
> * in  blockStatements we do this:
>
>  if (isRecordStart() && allowRecords) {
> 2629             dc = token.comment(CommentStyle.JAVADOC);
> 2630             return 
> List.of(recordDeclaration(F.at(pos).Modifiers(0), dc));
>
> But isRecordStart() can generate diagnostics - so should it be better 
> to invert the condition, as in:
>
> if (allowRecords && isRecordStart()) - to avoid spurious preview 
> warnings on source < 14?
>
> * how does this code avoids the problem described well by Jan in a 
> previous email in this thread - reporting below:
>
>> I think I was touching that in one of my comments. I am afraid it is 
>> probably not so simple - we could do the checks easily for top-level 
>> records, but not so easily for nested records. I.e.:
>> ---
>> class X {
>>     record R(int i) {
>>         return null;
>>     }
>> }
>> class record {}
>> ---
>>
>> is valid currently ("R" is a method which returns "record"), but not 
>> with the patch:
>> ---
>> $ javac /tmp/R.java
>> /tmp/R.java:2: error: records are a preview feature and are disabled 
>> by default.
>> record R(int i) {
>> ^
>>   (use --enable-preview to enable records)
>> /tmp/R.java:3: error: illegal start of type
>> return null; 
>
> Seems to me that we are using isRecordStarts in a member position - 
> meaning that a member record would be parsed even if it's just a 
> method returning a 'record' type (which is allowed, with warning).
>
> Maybe gate the isRecordStart with allowRecords? Actually, maybe we 
> should add  ' && allowRecords' check inside isRecordStart() all the 
> time, to make sure we don't pick up more records than intended?
>
> Maurizio
>
>
> On 12/11/2019 00:31, Vicente Romero wrote:
>> Hi all,
>>
>> I have uploaded another iteration to [3]. This iteration fixes a 
>> regression introduced with one of the fixes that were addressing 
>> preview review comments. In short this is the most important change:
>>
>> diff -r 5d6410bfafe0 -r 806e05ae98e3 
>> src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java
>> --- 
>> a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java 
>> Sun Nov 10 19:41:49 2019 +0000
>> +++ 
>> b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java 
>> Mon Nov 11 23:42:59 2019 +0000
>> @@ -1217,8 +1217,11 @@
>>                  if (sym.owner.owner.kind == PCK ||
>>                      (sym.owner.flags_field & STATIC) != 0)
>>                      mask |= STATIC;
>> -                else if ((flags & ENUM) != 0)
>> +                else if ((flags & ENUM) != 0) {
>>                      log.error(pos, Errors.EnumsMustBeStatic);
>> +                } else if ((flags & RECORD) != 0) {
>> +                    log.error(pos, 
>> Errors.RecordDeclarationNotAllowedInInnerClasses);
>> +                }
>>                  // Nested interfaces and enums are always STATIC 
>> (Spec ???)
>>                  if ((flags & (INTERFACE | ENUM | RECORD)) != 0 ) 
>> implicit = STATIC;
>>              } else {
>>
>> The regression introduced was that code like:
>>
>> class Outer {
>>     class Inner {
>>         record R() {}
>>     }
>> }
>>
>> started being accepted by the compiler. The reason is because the 
>> code that checks for static declarations inside inner classes in the 
>> compiler, checks for declared modifiers, but records are `implicitly` 
>> static as enums are. For this reason I'm now doing the check for 
>> records inside inner classes in the same place where a similar check 
>> for enums is done at Check::checkFlags. I also introduced a new error 
>> message, please see it inserted into the webrev.
>>
>> Thanks,
>> Vicente
>>
>> [3] 
>> http://cr.openjdk.java.net/~vromero/records.review/compiler/webrev.06
>>
>> On 11/10/19 6:48 AM, Vicente Romero wrote:
>>> Hi,
>>>
>>> I have uploaded another iteration to [2]. This one includes some 
>>> missing type and annotation javax.lang.model visitors,
>>>
>>> Thanks,
>>> Vicente
>>>
>>> [2] 
>>> http://cr.openjdk.java.net/~vromero/records.review/compiler/webrev.05
>>>
>>> On 11/8/19 5:02 PM, Vicente Romero wrote:
>>>> Hi all,
>>>>
>>>> Please review another iteration of the compiler implementation for 
>>>> records at [1]. New in this iteration:
>>>>
>>>> - I added an @implSpec comment to method: 
>>>> javax.lang.model.util.Elements::recordComponentFor
>>>> - moved the list of forbidden record component names to class Names
>>>> - added the code proposed by Maurizio to the parser with a small 
>>>> tweak for records declared as members
>>>> - modified the error message to indicate that any non-canonical 
>>>> constructor should invoke another constructor to: "constructor is 
>>>> not canonical, so its first statement must invoke another constructor"
>>>> - added method: 
>>>> com.sun.tools.sjavac.comp.PubapiVisitor::visitRecordComponent
>>>> - added new visitor: javax.lang.model.util.AbstractTypeVisitor14
>>>> - moved annotation processing tests for records to common place for 
>>>> other annotation processing tests
>>>>
>>>>
>>>> Thanks,
>>>> Vicente
>>>>
>>>> [1] 
>>>> http://cr.openjdk.java.net/~vromero/records.review/compiler/webrev.04
>>>
>>


More information about the amber-dev mailing list