Bug 235921 - [1.5][compiler] Incorrect generic signature attribute created for inner anonymous type
Summary: [1.5][compiler] Incorrect generic signature attribute created for inner anony...
Status: VERIFIED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 3.4   Edit
Hardware: PC Windows Vista
: P3 normal (vote)
Target Milestone: 3.4.1   Edit
Assignee: Philipe Mulet CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2008-06-05 16:08 EDT by Andrew Clement CLA
Modified: 2008-08-28 12:48 EDT (History)
1 user (show)

See Also:


Attachments
Proposed patch (26.91 KB, text/plain)
2008-06-09 05:48 EDT, Philipe Mulet CLA
no flags Details
In progress patch for 3.3.x (24.95 KB, patch)
2008-06-10 07:56 EDT, Philipe Mulet CLA
no flags Details | Diff
Patch for 3.3.x (24.90 KB, text/plain)
2008-06-10 09:58 EDT, Philipe Mulet CLA
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Andrew Clement CLA 2008-06-05 16:08:11 EDT
Came in via an AspectJ bug - but seems to be a base JDT compiler issue (seen in 3.3 and 3.4rc1)

AspectJ Bug: https://bugs.eclipse.org/bugs/show_bug.cgi?id=235829

Two test files. Looks a bit contrived but it is from a real application:

a/Adapter.java
---
package a;

public interface Adapter<T> {
  interface Setter<V> {}

  public <V> Setter<V> makeSetter();
}
---


a/b/Adapter.java
---
package a.b;

public class Adapter<T> implements a.Adapter<T> {

  public <V> Adapter.Setter<V> makeSetter() {
    return new Adapter.Setter<V>() {};
  }

}
---

The signature for the type 'a.b.Adapter$1' is generated as:

Ljava/lang/Object;La/b/Adapter$Setter<TV>;

when it should be:

Ljava/lang/Object;La/Adapter$Setter<TV>; 

(which is what javac produces).

The bug was not in Eclipse 3.1 and has appeared due to the changes in 

ParameterizedTypeBinding.genericTypeSignature()

But I'm not convinced the bug is in there, it may have just surfaced because of changes in there.

Within that method the enclosingType() of Adapter.Setter is a.b.Adapter, rather than a.Adapter (where Setter is actually declared).  And so when it is used to generate the signature for Adapter.Setter, it gets it wrong.  Previously (in Eclipse 3.1) the enclosingType was only used to generate the signature if it was parameterized (not the case here).

If the references in the second test file are all changed to explicitly be a.Adapter then it produces the correct signature.

It looks to be an issue in ParameterizedQualifiedTypeReference when resolving the references to Adapter.Setter.  Proceeding through the internalResolveType() method in there we determine that qualifiedType is a.b.Adapter (the closest one) - then when we go looking for the member type Setter, we dont find it on a.b.Adapter, proceed up the hierarchy and find it on a.Adapter.  We then get back into internalResolveType() and reach this code:

ReferenceBinding currentType = (ReferenceBinding) this.resolvedType;
if (qualifiedType == null) {
    qualifiedType = currentType.enclosingType(); // if member type

Here the currentType is the right member type Setter but the qualifiedType reference is set incorrectly to the wrong containing type for the memberType. (maybe it isn't incorrect, but its not a valid qualifier for that member type)

Because qualifiedType isn't fixed up, we continue down further and call this:

ParameterizedTypeBinding parameterizedType = scope.environment().createParameterizedType((ReferenceBinding)currentType.erasure(), argTypes, qualifiedType);

where the qualifiedType is used incorrectly as the enclosing type for the currentType.  And so the incorrect ParameterizedType a.b.Adapter.Setter is created and eventually its' signature gets used in the output class file.

I changed:

ReferenceBinding currentType = (ReferenceBinding) this.resolvedType;
if (qualifiedType == null) {
    qualifiedType = currentType.enclosingType(); // if member type

to

ReferenceBinding currentType = (ReferenceBinding) this.resolvedType;
if (qualifiedType == null || (currentType.isMemberType() && qualifiedType!=currentType.enclosingType())) {
    qualifiedType = currentType.enclosingType(); // if member type

adding an extra condition to allow for the member type not quite being found where we first guessed.	

This may or may not be an appropriate fix, but it fixes the testcase and passes all the other generics tests I have (not many...)
Comment 1 Andrew Clement CLA 2008-06-05 16:38:24 EDT
To see the failure in action (without needing to javap the class), compile this file too:

import java.lang.reflect.Type;

public class Main {
	
	public static void main(String[]argv) throws Exception {
		Class c = Class.forName("a.b.Adapter$1");
		Type[] ts = c.getGenericInterfaces();
		for (int i = 0; i < ts.length; i++) {
			Type type = ts[i];
			System.out.println(ts[i]);
		}
	}
}

when run it attempts to dereference the generic signature and goes bang due to a missing a.b.Adapter.Setter type:

Exception in thread "main" java.lang.TypeNotPresentException: Type a.b.Adapter$Setter not present
        at sun.reflect.generics.factory.CoreReflectionFactory.makeNamedType(CoreReflectionFactory.java:98)
        at sun.reflect.generics.visitor.Reifier.visitClassTypeSignature(Reifier.java:107)
        at sun.reflect.generics.tree.ClassTypeSignature.accept(ClassTypeSignature.java:31)
        at sun.reflect.generics.repository.ClassRepository.getSuperInterfaces(ClassRepository.java:82)
        at java.lang.Class.getGenericInterfaces(Class.java:788)
        at Main.main(Main.java:7)
Comment 2 Philipe Mulet CLA 2008-06-06 06:36:48 EDT
Other testcase, with simple reference broken:

import java.util.*;
class Adapter<T> {
  class Setter<V> {}
  public <V> Setter<V> makeSetter() { return null; }
}

public class X<T> extends Adapter<T> {
  public <V> X<T>.Setter<V> makeSetter() {
    return new X<T>().new Setter<V>() {};
  }
  void foo() {
	  List<Adapter<T>.Setter<T>> l = new ArrayList<X<T>.Setter<T>>();
  }
}

It incorrectly results in compiler errors, where it should compile clean.

Problem comes from indirect references to member type Adapter.Setter through X, since X gets remembered incorrectly into the result binding.
Comment 3 Philipe Mulet CLA 2008-06-06 07:59:39 EDT
The fix is near what you suggested indeed, since it was incorrectly replacing the enclosing type of the inherited member type. The condition is a little bit different, but same spirit. Also other type of references need to be fixed in similar way.

Marking for 3.4.1
Comment 4 Philipe Mulet CLA 2008-06-06 08:01:22 EDT
Added GenericTypeSignatureTest#test021
Added GenericTypeTest#test1341-1345.
Comment 5 Philipe Mulet CLA 2008-06-09 04:48:59 EDT
Regression tests are actually:

Added GenericTypeSignatureTest#test021
Added GenericTypeTest#test1342-1346.
Comment 6 Philipe Mulet CLA 2008-06-09 05:48:05 EDT
Created attachment 104134 [details]
Proposed patch
Comment 7 Philipe Mulet CLA 2008-06-10 07:56:44 EDT
Created attachment 104298 [details]
In progress patch for 3.3.x
Comment 8 Philipe Mulet CLA 2008-06-10 09:58:37 EDT
Created attachment 104318 [details]
Patch for 3.3.x

previous patch had extra '\n' in regression test CU name
Comment 9 Philipe Mulet CLA 2008-06-10 09:59:02 EDT
Released patch to 3.3.x maintenance branch
Comment 10 Philipe Mulet CLA 2008-06-23 12:58:27 EDT
Released for 3.5M1
Comment 11 Philipe Mulet CLA 2008-06-25 11:43:33 EDT
Released to 3.4 maintenance branch
Fixed
Comment 12 Philipe Mulet CLA 2008-06-25 11:48:14 EDT
Released for 3.4.1
Comment 13 Olivier Thomann CLA 2008-08-06 14:29:50 EDT
Verified for 3.5M1 using I20080805-1307
Comment 14 Frederic Fusier CLA 2008-08-28 12:48:06 EDT
Verified for 3.4.1 using build M20080827-2000.