Bug 339139 - [compiler] HEAD contents of org.eclipse.wst.jsdt.core doesn't compile anymore
Summary: [compiler] HEAD contents of org.eclipse.wst.jsdt.core doesn't compile anymore
Status: VERIFIED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 3.7   Edit
Hardware: PC Windows 7
: P3 blocker (vote)
Target Milestone: 3.7 M6   Edit
Assignee: Ayushman Jain CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2011-03-07 14:34 EST by Olivier Thomann CLA
Modified: 2011-05-02 10:33 EDT (History)
5 users (show)

See Also:


Attachments
Proposed fix (1.09 KB, patch)
2011-03-07 15:28 EST, Olivier Thomann CLA
no flags Details | Diff
proposed fix extended (8.67 KB, patch)
2011-03-08 04:30 EST, Ayushman Jain CLA
no flags Details | Diff
fix + tests (14.64 KB, patch)
2011-03-08 06:53 EST, Ayushman Jain CLA
no flags Details | Diff
same fix with suggested changes (15.52 KB, patch)
2011-03-08 11:35 EST, Ayushman Jain CLA
no flags Details | Diff
released patch (15.01 KB, patch)
2011-03-08 11:47 EST, Ayushman Jain CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Olivier Thomann CLA 2011-03-07 14:34:46 EST
The source of the Scope.java class doesn't compile because of a NPE.

java.lang.NullPointerException
	at org.eclipse.jdt.internal.compiler.codegen.BranchLabel.branch(BranchLabel.java:127)
	at org.eclipse.jdt.internal.compiler.codegen.CodeStream.goto_(CodeStream.java:3129)
	at org.eclipse.jdt.internal.compiler.ast.BranchStatement.generateCode(BranchStatement.java:60)
	at org.eclipse.jdt.internal.compiler.ast.IfStatement.generateCode(IfStatement.java:183)
	at org.eclipse.jdt.internal.compiler.ast.ForStatement.generateCode(ForStatement.java:290)
	at org.eclipse.jdt.internal.compiler.ast.Block.generateCode(Block.java:51)
	at org.eclipse.jdt.internal.compiler.ast.ForStatement.generateCode(ForStatement.java:290)
	at org.eclipse.jdt.internal.compiler.ast.LabeledStatement.generateCode(LabeledStatement.java:104)
	at org.eclipse.jdt.internal.compiler.ast.Block.generateCode(Block.java:51)
	at org.eclipse.jdt.internal.compiler.ast.IfStatement.generateCode(IfStatement.java:183)
	at org.eclipse.jdt.internal.compiler.ast.Block.generateCode(Block.java:51)
	at org.eclipse.jdt.internal.compiler.ast.ForStatement.generateCode(ForStatement.java:290)
	at org.eclipse.jdt.internal.compiler.ast.LabeledStatement.generateCode(LabeledStatement.java:104)
	at org.eclipse.jdt.internal.compiler.ast.Block.generateCode(Block.java:51)
	at org.eclipse.jdt.internal.compiler.ast.IfStatement.generateCode(IfStatement.java:183)
	at org.eclipse.jdt.internal.compiler.ast.Block.generateCode(Block.java:51)
	at org.eclipse.jdt.internal.compiler.ast.IfStatement.generateCode(IfStatement.java:183)
	at org.eclipse.jdt.internal.compiler.ast.Block.generateCode(Block.java:51)
	at org.eclipse.jdt.internal.compiler.ast.ForStatement.generateCode(ForStatement.java:290)
	at org.eclipse.jdt.internal.compiler.ast.LabeledStatement.generateCode(LabeledStatement.java:104)
	at org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration.generateCode(AbstractMethodDeclaration.java:244)
	at org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration.generateCode(AbstractMethodDeclaration.java:182)
	at org.eclipse.jdt.internal.compiler.ast.TypeDeclaration.generateCode(TypeDeclaration.java:543)
	at org.eclipse.jdt.internal.compiler.ast.TypeDeclaration.generateCode(TypeDeclaration.java:612)
	at org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration.generateCode(CompilationUnitDeclaration.java:361)
	at org.eclipse.jdt.internal.compiler.Compiler.resolve(Compiler.java:919)
	at org.eclipse.jdt.internal.compiler.Compiler.resolve(Compiler.java:958)
	at org.eclipse.jdt.internal.core.CompilationUnitProblemFinder.process(CompilationUnitProblemFinder.java:202)
	at org.eclipse.jdt.internal.core.CompilationUnitProblemFinder.process(CompilationUnitProblemFinder.java:268)
	at org.eclipse.jdt.internal.core.ReconcileWorkingCopyOperation.makeConsistent(ReconcileWorkingCopyOperation.java:190)
	at org.eclipse.jdt.internal.core.ReconcileWorkingCopyOperation.executeOperation(ReconcileWorkingCopyOperation.java:89)
	at org.eclipse.jdt.internal.core.JavaModelOperation.run(JavaModelOperation.java:728)
	at org.eclipse.jdt.internal.core.JavaModelOperation.runOperation(JavaModelOperation.java:788)
	at org.eclipse.jdt.internal.core.CompilationUnit.reconcile(CompilationUnit.java:1244)
	at org.eclipse.jdt.internal.ui.text.java.JavaReconcilingStrategy.reconcile(JavaReconcilingStrategy.java:126)
	at org.eclipse.jdt.internal.ui.text.java.JavaReconcilingStrategy.access$0(JavaReconcilingStrategy.java:108)
	at org.eclipse.jdt.internal.ui.text.java.JavaReconcilingStrategy$1.run(JavaReconcilingStrategy.java:89)
	at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42)
	at org.eclipse.jdt.internal.ui.text.java.JavaReconcilingStrategy.reconcile(JavaReconcilingStrategy.java:87)
	at org.eclipse.jdt.internal.ui.text.java.JavaReconcilingStrategy.initialReconcile(JavaReconcilingStrategy.java:178)
	at org.eclipse.jdt.internal.ui.text.CompositeReconcilingStrategy.initialReconcile(CompositeReconcilingStrategy.java:114)
	at org.eclipse.jdt.internal.ui.text.JavaCompositeReconcilingStrategy.initialReconcile(JavaCompositeReconcilingStrategy.java:136)
	at org.eclipse.jface.text.reconciler.MonoReconciler.initialProcess(MonoReconciler.java:105)
	at org.eclipse.jdt.internal.ui.text.JavaReconciler.initialProcess(JavaReconciler.java:406)
	at org.eclipse.jface.text.reconciler.AbstractReconciler$BackgroundThread.run(AbstractReconciler.java:173)

This might be related to the latest changes in the code generation changes around dead code.

I am investigating to isolate a test case.
Comment 1 Olivier Thomann CLA 2011-03-07 14:35:00 EST
A must fix for M6.
Comment 2 Olivier Thomann CLA 2011-03-07 15:05:00 EST
Reduced test case:
public abstract class X {
	private static Object[] bar() {
		return null;
	}
	protected final Object foo() {
			Object[] tab = null;
			if (tab != null) {
					Object[] v = bar();
					int length = tab.length;
					loop : for (int i = 0, max = v.length; i < max; i++) {
						Object o = v[i];
						if (o == null) {
							continue loop;
						}
						if (0 == length) {
							loop2 : for (int j = 0; j < length; j++) {
								Object o2 = null;
								for (int k = 0; k < length; k++) {
									if (o2 == tab[k]) {
										continue loop2;
									}
								}
								continue loop;
							}
							return o;
						}
					}
				}
		return null;
	}
}

This blows up with HEAD contents. It is related to the recent changes for dead code detection and code generation.
The code stream of the labels for the labeled statement are still null at code generation time causing the NPE.
Comment 3 Olivier Thomann CLA 2011-03-07 15:28:13 EST
Created attachment 190598 [details]
Proposed fix

With this patch, the code compiles again and still reports a dead code warning.
Comment 4 Olivier Thomann CLA 2011-03-07 15:30:16 EST
Stephan, Ayushman, please review the patch and propagate the same kind of changes to all possible locations.
I haven't checked in details other possible usages of UNREACHABLE instead of UNREACHABLE_OR_DEAD.
I also haven't run all tests yet.
Comment 5 Ayushman Jain CLA 2011-03-08 04:30:54 EST
Created attachment 190636 [details]
proposed fix extended

Here's the same fix extended to some other places which affect code gen later on. Testing this now.
Comment 6 Ayushman Jain CLA 2011-03-08 06:53:53 EST
Created attachment 190644 [details]
fix + tests

Same fix with added tests.
Note that the change in LocalDeclaration is to make sure we treat a local declaration in a null analysed dead code as reachable. As a consequence, local variable unused warnings also show up inside dead code now, which is good. 
Olivier/Stephan, if you don't have any objections with this fix, I'll release it.
Comment 7 Stephan Herrmann CLA 2011-03-08 07:11:25 EST
(In reply to comment #5)
> Created attachment 190636 [details]
> proposed fix extended
> 
> Here's the same fix extended to some other places which affect code gen later
> on. Testing this now.

I had prepared a similar patch last night. But I must have been tired: 
I forgot to actually click submit..

So, while working independently we agree on the locations in FlowContext
and sub-classes. The common pattern is:

 - XFlowContext has a field initsOnY 
 - this field is initialized to FlowInfo.DEAD_END (!)
 - only during recordZFrom(..) is this field updated to something useful
 - this method only works if not dead code (need to use UNREACHABLE_OR_DEAD)

IMO in all those methods changing the outer if should suffice for the 
immediate issue. (The inner if in LoopingFlowContext only affects the
content of the flowInfo, it is unrelated to the NPE at hand).

With just these changes I ran tests.compiler and tests.model and they pass.

Also the occurrences in LocalDeclaration and MethodScope looked suspicious 
to me. I agree with changing these, too.

I missed the location in TryStatement, but that's a good catch, too.


On the semantic side I suspect FlowContext.recordSettingFinal(..) should
also be changed for correct analysis, but I don't see danger of an NPE here.
Comment 8 Stephan Herrmann CLA 2011-03-08 07:15:18 EST
Now we had a mid-air collision of comments. ....

(In reply to comment #6)
> Created attachment 190644 [details]
> fix + tests
> 
> Same fix with added tests.
> Note that the change in LocalDeclaration is to make sure we treat a local
> declaration in a null analysed dead code as reachable. As a consequence, local
> variable unused warnings also show up inside dead code now, which is good. 
> Olivier/Stephan, if you don't have any objections with this fix, I'll release
> it.

Maybe you can briefly comment on two points where our findings differ:
- why change the inner if in LoopingFlowContext?
- how about FlowContext.recordSettingFinal?

thanks,
Stephan
Comment 9 Ayushman Jain CLA 2011-03-08 08:59:29 EST
(In reply to comment #8)
> Maybe you can briefly comment on two points where our findings differ:
> - why change the inner if in LoopingFlowContext?
I couldn't really convince myself on changing it or leaving it as is. I was basically trying the following tweaked case (focus on the portion between the "///////.."):

public class X {
  
	private static Object[] bar() {
    	Object o = null;
    	
    	if (o != null) {
    		int a11 = 0; 
        	a11++;
	    	Object aa = null;
	    	if (aa == null) {
	    		aa.toString(); 
	    	}
    	}
        return null;
    }
    protected final Object foo() {
        Object[] tab = null;
        if (tab != null) {
                Object[] v = bar();
                int length = tab.length;
                loop : for (int i = 0, max = v.length; i < max; i++) {
                    Object o = v[i];
/////////////////////////////////////////////////
                    if (o == null) { 
                    	continue loop;    // CONTINUE 1
                    }
                    o = null;
                    if (o == null ) { 
                        continue loop;    // CONTINUE 2
                    }
////////////////////////////////////////////////////
                    if (0 == length) {
                        loop2 : for (int j = 0; j < length; j++) {
                            Object o2 = null;
                            for (int k = 0; k < length; k++) {
                                if (o2 == tab[k]) {
                                    continue loop2;
                                }
                            }
                            continue loop;
                        }
                        return o;
                    }
                }
            }
    return null;
}
}

I saw that if we just have the UNREACHABLE bit comparison, we don't merge the initsOnContinue  that we obtained from the CONTINUE 1, and just copy the flowInfo of the loop into the inits. I'm not sure if it'll have any consequence on code gen really. Frankly speaking, I wasn't able to make up my mind. We can use just the UNREACHABLE bit as well. 


> - how about FlowContext.recordSettingFinal?
I did consider this but didnt find a possibility of an NPE. Again, we can change this as well to be consistent.

Let me know what you think.
Comment 10 Stephan Herrmann CLA 2011-03-08 09:15:37 EST
(In reply to comment #9)
> Let me know what you think.

It's probably safest to use UNREACHABLE_OR_DEAD in all mentioned locations
even if no direct impact on codegen can be shown now (as to minimize the danger 
of null-analysis causing grief).

At the same time I suggest to open fups for the issues where we still want
to investigate the exact impact on analysis in general. This would affect
at least both locations mentioned in comment 8 .
That why we can take our time to find test cases for those and still cleanly 
fix the NPE now.
Comment 11 Ayushman Jain CLA 2011-03-08 11:35:32 EST
Created attachment 190668 [details]
same fix with suggested changes

I'll release this fix then. It passes all tests.
Comment 12 Ayushman Jain CLA 2011-03-08 11:46:26 EST
Released in HEAD for 3.7M6
Comment 13 Ayushman Jain CLA 2011-03-08 11:47:32 EST
Created attachment 190669 [details]
released patch

Same patch. Just fixed some comments.
Comment 14 Ayushman Jain CLA 2011-03-08 12:14:04 EST
(In reply to comment #10)
> At the same time I suggest to open fups for the issues where we still want
> to investigate the exact impact on analysis in general.
Done. See bug 339253
Comment 15 Olivier Thomann CLA 2011-03-10 09:15:13 EST
Verified for 3.7M6.
Comment 16 Jay Arthanareeswaran CLA 2011-05-02 10:33:45 EDT
Verified with build I20110428-0848 for 3.7RC0