RFR: 8177466: Add compiler support for local variable type-inference

Maurizio Cimadamore maurizio.cimadamore at oracle.com
Fri Sep 22 10:52:17 UTC 2017

Here's another revision:





* Rewrote logic of type projection in Types.java to use an enum
* Added several javadoc on Types.java
* added call to baseType() in Attr::attribLazyConstantValue
* fixed an issue in TypeHarness where type var was created with null 
lower bound (as of this patch, javac is stricter and will assert on null 
bounds, as they are problematic to deal with when doing projections)

If there are no major comments, my plan is to push this on jdk10/master 
early next week.


On 19/09/17 16:04, Maurizio Cimadamore wrote:
> Hi,
> I have put together a slightly updated webrev:
> * as pointed out, one diagnostic used to say '9' instead of '10' (this 
> will likely need to change again because of new release cadence, but 
> good enough for now)
> * the previous webrev contained a spurious added file (TypeHarness) 
> which was caused by a glitch in the lvti branch in the amber repo.
> New webrev:
> http://cr.openjdk.java.net/~mcimadamore/8177466_v2/
> New diags:
> http://cr.openjdk.java.net/~mcimadamore/8177466_v2/diags.html
> Maurizio
> On 18/09/17 17:14, Maurizio Cimadamore wrote:
>> Hi,
>> this change adds support for local variable type inference (JEP 286 
>> [1]). A webrev of the change is available here:
>> http://cr.openjdk.java.net/~mcimadamore/8177466
>> The patch is relatively straightforward: implicitly typed locals are 
>> modeled in a similar fashion to implicit lambda parameters: their AST 
>> node is a JCVariableDecl whose 'vartype' field is not set (e.g. null).
>> There are few tricky parts to this changeset:
>> 1) tweak the parser to give 'var' special meaning depending on the 
>> version number and context
>> 2) Add logic in name resolution to catch bad reference to types named 
>> 'var'
>> 3) add logic to map initializer type back to a suitable variable 
>> declared type
>> As for (1), the parser has been extended so as to special case local 
>> variables with special name 'var', so that the type will be left out 
>> of the corresponding AST representing the variable declaration. This 
>> behavior will only affect latest source version.
>> The parser has a number of extra checks to prevent 'var to be used in 
>> places where it does not belong (according to the spec draft [2]); 
>> for instance, declaring a class whose name is 'var' is rejected in 
>> the parser. As a general rule, I tried to implement all such checks 
>> in the parser, as that gives very early and precise feedback about 
>> what's wrong with the code. The changes are implemented in Parser.java.
>> There are however errors which cannot be caught in the parser, and 
>> that's why (2) is needed. Basically, whenever 'var' is used in a 
>> position where it could be either a type or a package name, the 
>> parser can't simply rule that out, so we have to accept the code, and 
>> give an error if, later on, we discover that 'var' was really used in 
>> a type position (see changes in Resolve.java).
>> As far as (3) is concerned, we need to 'uncapture' captured types 
>> from initializers. That means that if we have a 'var' like this:
>> class Foo {
>>     void test() {
>>         var x = getClass().getSuperClass();
>>     }
>> }
>> The initializer type will be something like Class<? super #CAP>, 
>> where #CAP <: Foo
>> In this case, the compiler will project this type back to the less 
>> specific type Class<?>, and use that as the declared type for 'x'. 
>> This logic is defined in Types.java. As this logic is the same logic 
>> needed by jshell to render type of standalone expressions, jshell 
>> class VarTypePrinter has been removed and jshell has been rewired to 
>> point at the (now) official routine in Types. Jshell also needed 
>> several other tweaks to (i) accept 'var' and (ii) to deal with 
>> non-denotable types (intersection types and anonymous class types) 
>> that can be produced by the LVTI machinery (many thanks to Jan for 
>> doing those changes!)
>> As far as testing is concerned, I wrote several tests to check that 
>> the parser was behaving as expected; to check the behavior of the 
>> LVTI inference machinery, I wrote a test harness which leverages 
>> annotation on 'var' so that we can write down assertions such as:
>> @InferredType("java.util.List<? extends java.lang.String>")
>> var s = extString();
>> Regarding compiler diagnostics, for those interested, a comprehensive 
>> list of examples of new diagnostics triggered by the LVTI compiler 
>> can be found here:
>> http://cr.openjdk.java.net/~mcimadamore/8177466/lvti-diags.html
>> Finally, a finder has been added to detect local variable decls whose 
>> declared type can be replaced by 'var' - to enable it, the hidden 
>> option -XDfind=local should be used.
>> Thanks
>> Maurizio
>> [1] - http://openjdk.java.net/jeps/286
>> [2] - http://cr.openjdk.java.net/~dlsmith/local-var-inference.html

More information about the compiler-dev mailing list