JDK-8191842 : JShell: Inferred type information is lost when assigning types to a "var"
  • Type: Bug
  • Component: tools
  • Sub-Component: jshell
  • Affected Version: 10
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2017-11-23
  • Updated: 2018-03-26
  • Resolved: 2018-01-19
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 11
11 b01Fixed
Related Reports
Relates :  
Relates :  
Description
FULL PRODUCT VERSION :
java version "10-ea"
Java(TM) SE Runtime Environment (build 10-ea+32)
Java HotSpot(TM) 64-Bit Server VM (build 10-ea+32, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 10.0.15063]

A DESCRIPTION OF THE PROBLEM :
Type inference works for these cases:

Stream.of(1, 2, 3).map(j -> new Object() { int i = j; }).forEach(a -> System.out.println(a.i));
Stream.of(1, 2, 3).map(j -> new Object() { int i = j; }).collect(Collectors.toList()).forEach(a -> System.out.println(a.i));

But not for this one:

var list = Stream.of(1, 2, 3).map(j -> new Object() { int i = j; }).collect(Collectors.toList());
list.forEach(a -> System.out.println(a.i));


REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
var list = Stream.of(1, 2, 3).map(j -> new Object() { int i = j; }).collect(Collectors.toList());
list.forEach(a -> System.out.println(a.i));
---------- END SOURCE ----------


Comments
Review history captured for future reference --- Hi, When doing: var r = new Object() { String s; } the type of "r" is the anonymous class itself. JShell handles this by "upgrading" the anonymous class to be a member class, so that it can be properly used from other snippets. But JShell is not prepared for anonymous classes inside the expression (that are part of the resulting type), like: --- jshell> var list = Stream.of(1, 2, 3).map(j -> new Object() { int i = j; }).collect(Collectors.toList()); jshell> list.forEach(a -> System.out.println(a.i)); --- So, part of the proposed fix is to upgrade all anonymous classes to member classes if needed. Also, intersection types are cleared from fields before writing (the intersection types are installed to ensure var semantics for cases like: <Z extends Runnable & CharSequence> Z get1() { return null; } var i1 = get1(); ) Bug: https://bugs.openjdk.java.net/browse/JDK-8191842 Webrev: http://cr.openjdk.java.net/~jlahoda/8191842/webrev.00/ Thanks, Jan Nice generalization. Wrap: 90: this breaks array initialization -- this would no longer work: int[] d = {1, 2, 3} Eval: 559: This code should be designed to work the same for automatically created scratch variables -- aka $1, $2, ... -- they should also have anonymous types. ? 322: Can ei.anonymousClasses be used rather than depending on the naming added in Util.JSHELL_ANONYMOUS? 389, 419, 421, 486-493: the name "constructor" is confusing because it contains other things and not the complete constructor. One of the unwritten rules of wraps in that they consist of a complete component, this code has several hanging pieces. Note the two closing braces in 492, braces et. al. should always be matched in a wrap. The mixture of strings and wraps does make this challenging. I'd suggest doing the bodyParts/extraInit computation first, then build up each component as complete entities: constructor body, class body, class -- all wraps. ExpressionToTypeInfo: 355: Seems fragile to assume that the constructor is first -Robert Part 2: tests ToolSimpleTest is missing the bug number. VariablesTest needs a copyright update. Please add tests for: * anon classes with added method access * anon class scratch var -Robert Hi, Thanks for the comments. An updated patch is here: http://cr.openjdk.java.net/~jlahoda/8191842/webrev.01/ I addition to addressing the comments, it also fixes var behavior when the inferred type is inaccessible - that is allowed during normal compilation. Feedback is welcome, Jan Review (partial) -- Wrap: 71: The new parameter makes slightly worse a horribly out-of-date method comment (my bad), which could be deleted, but better actually documented -- there are a lot of parameters and they would benefit from an explanation certainly including the new one "enhanced". 82: Built as a list is a good choice. 91: Nit: The comment is helpful but much longer than anything around it and would be more readable formatted. Also, the example type "int" copied from the comment below doesn't make any sense in this context, and so is confusing, maybe they should both be "Foo". Eval: 106: Action at a distance. Document, or better, use another mechanism. 213: The parameter order and cast are inconsistent with all the other process* calls in this switch. 341-343/347: Inconsistent 380-529: Indenting and spacing off 389-393...: I don't get why TreeScanner is run in ExpressionToTypeInfo and then again in Eval with deep interlinking assumptions (like the order of the list). The only data added in the Eval scan is the 'NewClassTree node' which could be in AnonymousDescription. Then you could just iterate the AnonymousDescriptions. 410: I'd make this something less likely to be a captured variable name. That is a unique prefix. 418: ditto 399-445: Anonymous classes are infrequent enough to make this unlikely an issue, so can ignore this comment. The components of a wrap have overhead: they are stored independently for the duration and they are sequentially searched. Within this section these are all strings, so you could use StringBuilder, but the complexity of the extra variables would make that messy. You could just use string '+' rather then 2-5 'add' calls inline -- there is no value in separating adjacent strings (only for Wraps). And the result would probably be more readable. 436: BUG, you are double incrementing 'i'. Please include a test like: class D { D(int foo, String bar) { this.foo = foo; this.bar = bar; } int foo; String bar; } new D(34, "hi") { String z = foo + bar; } $3.z -Robert Hi, Thanks for all the comments so far. I tried to adjust the code and add comments. The updated patch is here: http://cr.openjdk.java.net/~jlahoda/8191842/webrev.02/ ----- Eval: Nicely documented! 1218: anonCount is not incremented, so you always have the same anonymous class name. Which, of course, is a problem if you have more than one: jshell> class A {} | created class A jshell> new A() { int foo() {return 66; }} $2 ==> $0@7dc7cbad jshell> new A() { int hhoo() {return 99; }} $3 ==> $0@548a9f61 jshell> $3.hhoo() An exception has occurred in the compiler (10-internal). Please file a bug against the Java compiler via ... java.lang.ClassCastException: jdk.compiler/com.sun.tools.javac.code.Symbol$ClassSymbol cannot be cast to jdk.compiler/com.sun.tools.javac.code.Symbol$MethodSymbol at jdk.compiler/com.sun.tools.javac.comp.TransTypes.visitApply(TransTypes.java:676) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCMethodInvocation.accept(JCTree.java:1634) ... Please include tests with multiple anon classes with member accesses. 402: Nit: "create and explicit class that extens" --> "create AN explicit class that EXTENDS" ExpressionToTypeInfo: 93: it is conventional, consistent with surrounding vars, and clearer to preface predicate names with "is" -- isPrimativeType 414: for variable declarations with an explicit type, it doesn't seem we should be processing anonymous classes in that case TaskFactory: The new code is, for every pass through Enter (of which there are several per new snippet), iterating through all the snippets, filtering for valid vars, looking up the wrap class containing the var, iterating through the members of that class to find the field representing the var, compiling a cast to use to set the type by force. The field type is restored before generation. One concern is that the performance decreases with the number of snippets. We need to stay aware of long-running JShell instances -- as for a user that leaves the jshell tool open for exploration. The filtering on fullTypeName being non-null for enhanced types limits the heavy processing to these rarer cases, so I'm not concerned I assume all types downstream of these variables are processed after Enter finishes? Thanks for the high-level description of setVariableType, however how it is implemented is complex enough to deserve documentation (something like the first paragraph). TreeDissector: OK TypePrinter: OK Util: OK VarSnippet: OK Wrap: Nicely documented! Tests: Please include a test covering the bug (above). Please include tests covering: captured variables extending a variety of complex library classes and interfaces varied argument types parameterized types VarSnippet.typeName() under various conditions -Robert
26-03-2018

This seems to related to the logic that jshell has to recover anonymous class types - seems like if they are nested in some other type the original anon type is lost.
24-11-2017

Requested submitter to clarify the exact issue and observed on javac or jshell?
24-11-2017

There is no mention of jshell in the report. jshell looks having some issue in resolving inferences ==below is the output executed on 10 ea b33== -sh-4.2$ /scratch/fairoz/JAVA/jdk10/jdk-10-ea+33/bin/jshell | Welcome to JShell -- Version 10-ea | For an introduction type: /help intro jshell> import java.util.stream.Collectors; jshell> import java.util.stream.Stream; jshell> var list = Stream.of(1, 2, 3).map(j -> new Object() { int i = j; }).collect(Collectors.toList()); list ==> [1@42e26948, 1@57baeedf, 1@343f4d3d] jshell> list.forEach(a -> System.out.println(a.i)); | Error: | cannot find symbol | symbol: variable i | list.forEach(a -> System.out.println(a.i)); | ^-^ ==
24-11-2017

This issue is not reproducible on jdk10 ea b33, below are the results on various platforms JDK10 ea b33 on Linux - Pass JDK10 ea b33 on Windows7 - Pass JDK10 ea b33 on Windows10 - Pass JDK10 ea b33 on Windows12 - Pass Below is the output of attached test case running on 2012 executed on 10 ea b33. No compilation error C:\Users\fmatte\JI>"c:\Users\fmatte\JAVA\jdk10\jdk-10_ea+33\bin\javac.exe" Main.java C:\Users\fmatte\JI>"c:\Users\fmatte\JAVA\jdk10\jdk-10_ea+33\bin\java.exe" Main 1 2 3
24-11-2017

Clarification from submitter == Thanks for the feedback. I'm sorry for the confusion, yes this seems to be a jshell only issue. I hadn't checked with javac directly, only with jshell. ==
24-11-2017