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

Sergey Bylokhov Sergey.Bylokhov at oracle.com
Mon Sep 18 22:44:44 UTC 2017

Hi, Maurizio.
I am not sure is it expected or not, but in some cases the new 'var' 
produce some non-easy to read error messages:

var s = true ? new ArrayList<String>() : new ArrayList<Integer>();
s.add(new String());

testVar.java:9: error: no suitable method found for add(String)
         s.add(new String());
     method Collection.add(CAP#1) is not applicable
       (argument mismatch; String cannot be converted to CAP#1)
     method List.add(CAP#1) is not applicable
       (argument mismatch; String cannot be converted to CAP#1)
     method AbstractCollection.add(CAP#1) is not applicable
       (argument mismatch; String cannot be converted to CAP#1)
     method AbstractList.add(CAP#1) is not applicable
       (argument mismatch; String cannot be converted to CAP#1)
     method ArrayList.add(CAP#1) is not applicable
       (argument mismatch; String cannot be converted to CAP#1)
   where CAP#1 is a fresh type-variable:
     CAP#1 extends INT#1 from capture of ? extends INT#1
   where INT#1,INT#2 are intersection types:
     INT#1 extends Object,Serializable,Comparable<? extends INT#2>
     INT#2 extends Object,Serializable,Comparable<?>
Note: Some messages have been simplified; recompile with -Xdiags:verbose 
to get full output

On 9/18/17 09: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

Best regards, Sergey.

More information about the compiler-dev mailing list