Community
Participate
Working Groups
"Missing code implementation in the compiler" compile error when a lambda body accesses an array-typed variable: package jsr335; public class LambdaAccessArray { public static void main(String[] args) throws InterruptedException { final int[] result= { 0 }; Thread t = new Thread(() -> { result[0]= 42; }); t.start(); t.join(); System.out.println(result[0]); } } The error doesn't occur when the lambda body is just an expression like this: Thread t = new Thread(() -> result[0]= 42); The problem is reported here: org.eclipse.jdt.internal.compiler.problem.ProblemReporter.needImplementation(ProblemReporter.java:6178) at org.eclipse.jdt.internal.compiler.codegen.CodeStream.generateOuterAccess(CodeStream.java:2286) at org.eclipse.jdt.internal.compiler.codegen.StackMapFrameCodeStream.generateOuterAccess(StackMapFrameCodeStream.java:364) at org.eclipse.jdt.internal.compiler.ast.SingleNameReference.generateCode(SingleNameReference.java:487) at org.eclipse.jdt.internal.compiler.ast.ArrayReference.generatePostIncrement(ArrayReference.java:166) at org.eclipse.jdt.internal.compiler.ast.PostfixExpression.generateCode(PostfixExpression.java:40) at org.eclipse.jdt.internal.compiler.ast.Expression.generateCode(Expression.java:695) at org.eclipse.jdt.internal.compiler.ast.Block.generateCode(Block.java:75) at org.eclipse.jdt.internal.compiler.ast.LambdaExpression.generateCode(LambdaExpression.java:751) at org.eclipse.jdt.internal.compiler.ast.LambdaExpression.generateCode(LambdaExpression.java:702) at org.eclipse.jdt.internal.compiler.ClassFile.addSpecialMethods(ClassFile.java:923) at org.eclipse.jdt.internal.compiler.ast.TypeDeclaration.generateCode(TypeDeclaration.java:573) at org.eclipse.jdt.internal.compiler.ast.TypeDeclaration.generateCode(TypeDeclaration.java:638) at org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration.generateCode(CompilationUnitDeclaration.java:369) at org.eclipse.jdt.internal.compiler.Compiler.process(Compiler.java:781) at org.eclipse.jdt.internal.compiler.ProcessTaskManager.run(ProcessTaskManager.java:137)
This is the side effect of running control/data flow analysis on the lambda body without it first being run on the enclosing method(s). We create a parallel universe as far as AST and FlowContext and FlowInfo abstractions go, but for scopes and bindings - we reuse. This is bad. Basically, during lambda's flow analysis, we find outer local array "result" as being uninitialized and set a bit on the binding. This is because this bit is required during code generation and flow info has evaporated at that point. I'll investigate.
(In reply to Markus Keller from comment #0) > "Missing code implementation in the compiler" compile error when a lambda > body accesses an array-typed variable: [...] > The error doesn't occur when the lambda body is just an expression like this: > > Thread t = new Thread(() -> result[0]= 42); For an expression bodied lambda we don't have to do control flow analysis. We know its value compatible if the expression is not of void type and it is void compatible if the expression can be standalone statement - so things are simple there.
Fix and tests released here: http://git.eclipse.org/c/jdt/eclipse.jdt.core.git/commit/?h=BETA_JAVA8&id=9cd17ece7cc1ac607f4f1302fc1a6d286dff9da1 We basically start the (overload resolution trial) flow analysis for lambda bodies against a perfect world: all outer locals are definitely assigned. It is not pertinent if they are otherwise - the eventual analysis will catch the errors.
I see that flow analysis is performed with null annotation analysis disabled. For my education: what results of flow analysis are actually needed in this context?
(In reply to Stephan Herrmann from comment #4) > I see that flow analysis is performed with null annotation analysis > disabled. For my education: what results of flow analysis are actually > needed in this context? This is the trial evaluation on a clone and not the original lambda. Since the null analysis switch is restored properly in the finally block, the eventual resolution would carry out null analysis if requested. Basically, during resolve stage, we need information on whether a lambda expression is value compatible or value compatible or both or none. This cannot be determined by say an ASTVisitor that looks for return statements. e.g: interface I { void foo (int x); } public class X { static void foo(I i) {} public static void main(String[] args) { boolean constantequalstotrue; constantequalstotrue = true; I i = (x) -> { if (x > 0) return x; }; // neither i = (x) -> { throw new RuntimeException(); }; // both i = (x) -> { while (constantequalstotrue); }; // both i = (x) -> { System.out.println(x); }; // void i = (x) -> { return x; }; // value } } LE.isCompatibleWith is implementing 15.12.2.1 and actually a bit more than that, we don't distinguish between potential compatibility and applicability and merge them together into compatibility. --- A lambda expression (15.27) is potentially compatible with a functional interface type (9.8) if all of the following are true: The arity of the targeted type's function type is the same as the arity of the lambda expression. If the targeted type's function type has a void return, then the lambda body is either a statement expression (14.8) or a void-compatible block (15.27.2). If the targeted type's function type has a (non-void) return type, then the lambda body is either an expression or a valuecompatible block (15.27.2). -- For part F, it used to be that the lambda had to be resolved + analyzed against every target type candidate. draft 0.7 has done away with this requirement. So we need at most two resolves + analyze irrespective of the number of candidate methods: one to discover the shape and one ultimate resolve. My cursory reading of part G, tells me we need to resolve + analyze n+1 times for inference - not sure, but we have the machinery in place.
Thanks for explaining. So, applicability check needs to know about UNREACHABLE, right? Makes sense. Good we separated UNREACHABLE_OR_DEAD from UNREACHABLE_BY_NULLANALYSIS :)