RFR: 8177466: Add compiler support for local variable type-inference
Sergey Bylokhov
Sergey.Bylokhov at oracle.com
Wed Sep 20 00:20:12 UTC 2017
Just thoughts on a subject...
Its seems that most of "surprising" behavior comes from the reassigning,
The new "var" adds one new feature: it will be possible to have a
reference to the anonymous class and call the methods defined in it. So
it is not simply a syntactic sugar.
var probablySerializable = new Serializable() {
public void test() {
}
};
.....
probablySerializable.test();
And because of that the reassigned will not work:
var probablySerializable = new Serializable() {
public void test() {
}
};
probablySerializable = new Serializable() {
};
testVar.java:13: error: incompatible types: <anonymous Serializable>
cannot be converted to <anonymous Serializable>
name = new Serializable() {
On 9/19/17 13:24, Maurizio Cimadamore wrote:
> The type of this is not Component<?, ?> but Component<T, D>. So, when
> you use an explicit type, you force the type of 'cp1' to be less sharp,
> which then works for the followup assignment. But if you use 'var', the
> compiler will just infer whatever type the initializer has, which in
> this case is Component<T, D>. And the type of c2.getContainer
> (Component<#CAP1, #CAP2>) is not compatible with Component<T, D>.
>
> Maurizio
>
>
> On 19/09/17 17:44, Sergey Bylokhov wrote:
>> Hi, Maurizio.
>> One more question about generics, in the code below the "case1"
>> compiles successfully, but the "case2" produce an error:
>>
>> public class testVar {
>>
>> class Component<T extends Object, D extends Object> {
>>
>> final Container<?, ?> getContainer() {
>> return null;
>> }
>>
>> void windowToLocal() {
>> // case1
>> Component<?, ?> cp1 = this;
>> cp1 = cp1.getContainer();
>> // case2
>> var cp2 = this;
>> cp2 = cp2.getContainer();
>> }
>> }
>> class Container<T extends Object, D extends Object>
>> extends Component<T, D> {
>>
>> }
>> }
>>
>> //error
>> testVar.java:13: error: incompatible types:
>> testVar.Container<CAP#1,CAP#2> cannot be converted to
>> testVar.Component<T,D>
>> cp2 = cp2.getContainer();
>> ^
>> where T,D are type-variables:
>> T extends Object declared in class testVar.Component
>> D extends Object declared in class testVar.Component
>> where CAP#1,CAP#2 are fresh type-variables:
>> CAP#1 extends Object from capture of ?
>> CAP#2 extends Object from capture of ?
>> 1 error
>>
>> On 9/19/17 08: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
>>>>
>>>>
>>>
>>
>>
>
--
Best regards, Sergey.
More information about the compiler-dev
mailing list