Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
Bug 345968 - [1.7][compiler] NPE while using diamond for inner class allocation
Summary: [1.7][compiler] NPE while using diamond for inner class allocation
Status: VERIFIED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 3.7   Edit
Hardware: All All
: P3 normal (vote)
Target Milestone: 3.7.1   Edit
Assignee: Srikanth Sankaran CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2011-05-16 11:21 EDT by Ayushman Jain CLA
Modified: 2011-08-05 02:54 EDT (History)
3 users (show)

See Also:
satyam.kandula: review+
amj87.iitr: review+


Attachments
Patch to fix the NPE (888 bytes, patch)
2011-05-20 05:53 EDT, Srikanth Sankaran CLA
no flags Details | Diff
Proposed patch + tests (11.64 KB, patch)
2011-05-21 07:50 EDT, Srikanth Sankaran CLA
no flags Details | Diff
Additional patch + test (3.27 KB, patch)
2011-05-24 03:52 EDT, Srikanth Sankaran CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Ayushman Jain CLA 2011-05-16 11:21:31 EDT
BETA_JAVA7

The following code snippet throws an NPE at 
org.eclipse.jdt.internal.compiler.lookup.ParameterizedMethodBinding.<init>(ParameterizedMethodBinding.java:122)

class X<T> {      
    X() {
    }
	
	
    class Y<Z> {
		 Y (Z a) {  
		    	System.out.println("const1");
		    }       
		    
		 Y(T a, Z b) {
		    	System.out.println("const2");
		    }
    }
    
    public static void main(String[] args) {
		X<String>.Y<String> x = new X<>().new Y<>("","");                             
	}
}

This happens because
(1) the inner class uses the type argument of the enclosing class and
(2) the static factory method generated corresponding to the const.  Y(T a, Z b) has the arguments [null,Z'].
Comment 1 Ayushman Jain CLA 2011-05-17 11:31:06 EDT
tests
org.eclipse.jdt.core.tests.compiler.regression.GenericsRegressionTest_1_7._test0034() to 36 can be enabled after this is fixed.
Comment 2 Satyam Kandula CLA 2011-05-18 05:45:34 EDT
I will have a look at this.
Comment 3 Satyam Kandula CLA 2011-05-20 04:51:43 EDT
This test gives a compiler error with javac too. As far as I see, type is not being inferred in this case - Inner class constructor uses the outer class's type parameter. I couldn't find any references to inner classes in the spec. 
Srikanth, do you think this is illegal according to the spec or is it a bug with javac?
Comment 4 Srikanth Sankaran CLA 2011-05-20 05:16:35 EDT
(In reply to comment #3)
> This test gives a compiler error with javac too. As far as I see, type is not
> being inferred in this case - Inner class constructor uses the outer class's
> type parameter. I couldn't find any references to inner classes in the spec. 
> Srikanth, do you think this is illegal according to the spec or is it a bug
> with javac?

I think this code should be rejected and javac is doing the right thing.
Basically the outer <>'s type parameter is deduced to be Object and there can
be no inference on the subsequent <> that would produce an X2<String> that
has an enclosing type X<String>.

Obviously, eclipse should not result in an NPE and should produce a clean
error message.
Comment 5 Srikanth Sankaran CLA 2011-05-20 05:22:28 EDT
That said, it does looks suspicious that the following would
compile with javac:

// ------------
public class X {      
    X() {
    }

    class Y<T, Z> {
         Y (Z a) {  
                System.out.println("const1");
            }       

         Y(T a, Z b) {
                System.out.println("const2");
         }
    }

    public static void main(String[] args) {
        X.Y<String, String> x = new X().new Y<>("","");                       
    }
}


WHILE the following would not:

public class X<T> {      
    X() {
    }

    class Y<Z> {
         Y (Z a) {  
                System.out.println("const1");
            }       

         Y(T a, Z b) {
                System.out.println("const2");
         }
    }

    public static void main(String[] args) {
        X<String>.Y<String> x = new X<String>().new Y<>("","");                       
    }
}

So while I believe the program from comment#0 is ill-formed, I doubt
that javac is rejecting it on the right grounds.
Comment 6 Srikanth Sankaran CLA 2011-05-20 05:28:27 EDT
Basically we need to also rename the enclosing type's type
parameters, making provisions for type parameter's being
redefined.
Comment 7 Srikanth Sankaran CLA 2011-05-20 05:43:39 EDT
(In reply to comment #6)
> Basically we need to also rename the enclosing type's type
> parameters, making provisions for type parameter's being
> redefined.

We also don't handle the case of a generic method redefining
its enclosing's class's type variables. That case would need
to be handled too.
Comment 8 Srikanth Sankaran CLA 2011-05-20 05:53:51 EDT
Created attachment 196194 [details]
Patch to fix the NPE

This is a partial fix. We still need to (a) understand why the
inference still fails (b) handle the redefinition cases (c) handle
the case where the type variable of outer class is referenced.
Comment 9 Srikanth Sankaran CLA 2011-05-20 06:02:51 EDT
(In reply to comment #4)
> (In reply to comment #3)
> > Srikanth, do you think this is illegal according to the spec or is it a bug
> > with javac?

After thinking about some more I am not sure: I don't see the spec covering
this case at all.
Comment 10 Ayushman Jain CLA 2011-05-20 06:07:09 EDT
(In reply to comment #9)
> (In reply to comment #4)
> > (In reply to comment #3)
> > > Srikanth, do you think this is illegal according to the spec or is it a bug
> > > with javac?
> 
> After thinking about some more I am not sure: I don't see the spec covering
> this case at all.

I'm downloading the new javac7b142. I'll report back with how it behaves vis-a-vis the above cases.
We should also ask for a spec clarification. Was the inner class case intentionally left off or was it an oversight? It seems counter-intuitive to not have the diamond case work for an inner class from a user POV.
Comment 11 Srikanth Sankaran CLA 2011-05-20 06:29:40 EDT
(In reply to comment #10)

> Was the inner class case
> intentionally left off or was it an oversight? It seems counter-intuitive to
> not have the diamond case work for an inner class from a user POV.

<> does work with inner classes, as shown by the following example. 

class X<T> {      
    X() {
    }
    class Y<Z> {
         Y (Z a) {  
                System.out.println("const1");
            }       
         Y(String a, Z b) {
                System.out.println("const2");
            }
    }
    public static void main(String[] args) {
        X<String>.Y<String> x = new X<String>().new Y<>("","");                       
    }
}

When I said I don't see the spec covering this case at all, I was referring
to the fact that substitution function being defined speaks only of the
type parameters of C the class and c, the possibly generic constructor.
Comment 12 Srikanth Sankaran CLA 2011-05-20 09:44:07 EDT
We have confirmation from Sun that they have a bug in javac that
results in the following program being rejected:

public class X<T> {      
    X() {
    }

    class Y<Z> {
         Y (Z a) {  
                System.out.println("const1");
            }       

         Y(T a, Z b) {
                System.out.println("const2");
         }
    }

    public static void main(String[] args) {
        X<String>.Y<String> x1 = new X<String>().new Y<String>("",""); 
        X<String>.Y<String> x2 = new X<String>().new Y<>("",""); // javac wrong error 
    }
}

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7046778 )(not visible
externally as of now) has been raised.

Comment# 0 code should be rejected for reasons outlined in comment# 4.
Comment 13 Srikanth Sankaran CLA 2011-05-21 02:48:22 EDT
(In reply to comment #6)
> Basically we need to also rename the enclosing type's type
> parameters, making provisions for type parameter's being
> redefined.

[...]

(In reply to comment #7)

[...]

> We also don't handle the case of a generic method redefining
> its enclosing's class's type variables. That case would need
> to be handled too.

(In reply to comment #8)
> Created attachment 196194 [details]
> Patch to fix the NPE
> 
> This is a partial fix. We still need to (a) understand why the
> inference still fails (b) handle the redefinition cases (c) handle
> the case where the type variable of outer class is referenced.

Upon further analysis, these have all proved to be non-considerations
There is no action called for to handle hiding/redefinition of type
variables, as shown by the following case:

class X<T> {
	<T> X(T t) {
		
	}
	X<String> x = new X<>(""); 
}

We do synthesize the factory method to be 

       <T', T''> X<T'> <factory>(T'') {}

which is correct.

No action is needed because the map lookup key is not a string, but
is a type variable binding. So when a type variable is being redefined
the bindings are different even though the variable name is the same.

The one remaining task is to understand the behavior against:

public class X<T>  {
    class Y<Z>  {
        Y(T a, Z b) {
        }
    }
    public static void main(String[] args) {
        X<String>.Y<String>  x1 = new X<String>().new Y<String>("",""); // OK
        X<String>.Y<String>  x2 = new X<String>().new Y<>("",""); // ERROR
    }
}

See that both javac and eclipse fail to compile this code at this time - perhaps
there is something there.
Comment 14 Srikanth Sankaran CLA 2011-05-21 07:50:11 EDT
Created attachment 196266 [details]
Proposed patch + tests

This patch fixes all the known issues. Under test.
Comment 15 Srikanth Sankaran CLA 2011-05-21 09:45:59 EDT
All tests pass, Satyam please review - TIA.

Ayush, please verify the behavior on the disabled tests and enable
them if alright. Raise a follow up defect as needed - TIA.

My apologies for hijacking this defect - Curiosity as what could be
going on there got the better of me. I'll sleep better tonight :)
Comment 16 Satyam Kandula CLA 2011-05-23 06:45:00 EDT
+1 for the patch. 
I understood what mistake I was doing, I wasn't using genericType() and was struggling. Thanks, I understand this better now :).
Comment 17 Srikanth Sankaran CLA 2011-05-23 06:59:24 EDT
Released in BETA_JAVA7 branch. Ayush, do follow up to enable the
tests written for bug 345559 - Thanks.
Comment 18 Ayushman Jain CLA 2011-05-24 03:12:29 EDT
There's a problem with the fix. The loop introduced uses the enclosingType parameter passed to the getStaticFactory(..) method and ends up modifying it. So while this works fine in the first for loop i.e. for the first found constructor, this will break for subsequent constrcutors, since enclosingType will always be null from 2nd iteration of foor loop.
Comment 19 Srikanth Sankaran CLA 2011-05-24 03:39:15 EDT
(In reply to comment #18)
> There's a problem with the fix. The loop introduced uses the enclosingType
> parameter passed to the getStaticFactory(..) method and ends up modifying it.
> So while this works fine in the first for loop i.e. for the first found
> constructor, this will break for subsequent constrcutors, since enclosingType
> will always be null from 2nd iteration of foor loop.

Excellent catch !

I'll post a patch shortly.
Comment 20 Srikanth Sankaran CLA 2011-05-24 03:52:09 EDT
Created attachment 196406 [details]
Additional patch + test

Ayush, for further review, testing and verification, include this patch also.
Let me know if you see any other problems - Thanks,
Comment 21 Ayushman Jain CLA 2011-05-24 06:27:55 EDT
+1 with the new patch.
Comment 22 Srikanth Sankaran CLA 2011-05-24 06:52:00 EDT
Released additional fix & test into BETA_JAVA7 branch.
Comment 23 Srikanth Sankaran CLA 2011-05-24 06:52:17 EDT
.
Comment 24 Ayushman Jain CLA 2011-05-24 09:16:22 EDT
(In reply to comment #1)
> tests
> org.eclipse.jdt.core.tests.compiler.regression.GenericsRegressionTest_1_7._test0034()
> to 36 can be enabled after this is fixed.

Released.
Comment 25 Olivier Thomann CLA 2011-06-28 10:06:09 EDT
Verified using v_B65.