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

Maurizio Cimadamore maurizio.cimadamore at oracle.com
Mon Sep 18 16:14:36 UTC 2017


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