Bug 89470 - Generic Method override compatibility
Summary: Generic Method override compatibility
Status: VERIFIED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 3.1   Edit
Hardware: PC Windows XP
: P3 critical (vote)
Target Milestone: 3.1 M7   Edit
Assignee: Kent Johnson CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2005-03-30 03:39 EST by Carlos Aydos CLA
Modified: 2005-05-11 09:18 EDT (History)
2 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Carlos Aydos CLA 2005-03-30 03:39:21 EST
With both 3.1M4 and 3.1M5a the following code compiles without any problems.

public interface MyIface {
   public <T> void foo(T p);
}
public class MyClass implements MyIface{
   public <T extends Integer> void foo(T p) {
       //does something
   }
}

However, javac 1.5 gives me:

MyClass.java:1: MyClass is not abstract and does not override abstract method 
<T>foo(T) in MyIface
public class MyClass implements MyIface{
      ^
1 error


Additionally, the code underneath produces the following output with the 
eclipse compiler:

Base: 1
Sub: 1
Exception in thread "main" java.lang.ClassCastException: java.lang.String

And this output with javac:

Base: 1
Sub: 1
Exception in thread "main" java.lang.ClassCastException: java.lang.String

This seems to be a consistent and well-thought implementation given these two 
simple examples. Does someone else know more details about this issue?

Coconha

public class Signature {
   public Signature() {
       super();
       Sub s = new Sub();
       s.foo(new Integer(1));
       s.foo("as");
   }
   public class Base {
       public <T> void foo(T a) {
           System.out.println("Base: " + a);
       }
   }
   public class Sub extends Base {
       public <T extends Integer> void foo(T a) {
           super.foo(a);
           System.out.println("Sub: " + a);
       }
   }
   public static void main(String[] args) {
       new Signature();
   }
}


I opened the Signature$Sub.class generated both by javac and Eclipse with 
jclasslib bytecode viewer and found:

javac version ==> has only one method entry: 
- foo(java.lang.Integer) with a "public" access flag. 
The class file looks exactly the same if I compile it after manually applying 
Erasure.

eclipse version ==> has 2 entries: 
- foo(java.lang.Integer) with a "public" access flag
- foo(java.lang.Object) with a "public bridge synthetic" access flag
The class file looks exactly the same as in the javac version if I compile it 
after manually applying Erasure.
Comment 1 Carlos Aydos CLA 2005-03-30 03:42:31 EST
I am sorry, I mistyped something on previous comment:

Where I said:

>And this output with javac:

>Base: 1
>Sub: 1
>Exception in thread "main" java.lang.ClassCastException: java.lang.String

I actually meant:

Base: 1
Sub: 1
Base: as
Comment 2 Markus Keller CLA 2005-03-30 05:06:57 EST
Carlos, are you sure you wanted to set severity to "critical"? IMO, it's
"normal" or "minor". Click on the "Severity:" link above to see how severities
are used, e.g.:
  Critical    crashes, loss of data, severe memory leak
Comment 3 Carlos Aydos CLA 2005-03-30 08:41:07 EST
Markus,

You are right.

The reason I jumped and placed it as critical is that we started using 3.1 as 
our main development environment. And due to lack of experience with Erasure 
we coded a lot based on the assumption that MyIface/MyClass code example gets 
compiled without any problems.

To be more specific, we defined an interface for functors:

public interface UnaryFunction<S> {
    public <T> S execute(T p);
}

And we have by now more than 100 functors in our code based on this interface 
which are implemented in a similar way as below:

    public class MyClass<SS> {

        // more stuff here

        public class Func implements UnaryFunction<SS> {
            public <TT extends Integer> SS execute(TT s) {
                SS out = null;
                // does something
                return out;
            }
        }
    }

That means that when this bug is fixed, our entire project will not compile 
anymore as I will to rewrite my interface to something like:

public interface UnaryFunction<S,T> {
   public S execute(T p);
}

and change all functors accordingly. 

Taking into consideration that there might be more people out there that will 
be silly enough (like me! L) to fall in the same trap I think this bug was 
quite critical. I know the Bugzilla criteria describes critical as “crashes, 
loss of data,etc”. For someone like me, which is now informed about the issue, 
I consider this Bug as Minor or Normal, but for someone less informed, this 
could be a Blocker. So Critical was the compromise I found here.

BTW, do you have any tips for a easier work around to my problem described 
above?
 


Comment 4 Philipe Mulet CLA 2005-04-06 07:29:38 EDT
I would also qualify this defect as critical based on what you described.
Having the compiler misbehaves, and let people write bogus code is nasty.
We want to resolve this asap.
Comment 5 Philipe Mulet CLA 2005-04-06 07:33:54 EDT
I suspect problem comes from MethodVerifier15

boolean doTypeVariablesClash(MethodBinding one, MethodBinding substituteTwo) {
	TypeBinding[] currentVars = one.typeVariables;
	TypeBinding[] inheritedVars = substituteTwo.original().typeVariables;
	return currentVars.length != inheritedVars.length && currentVars.length > 0;
}

The variable clash detection doesn't perform bound checks.
Comment 6 Kent Johnson CLA 2005-04-06 10:44:53 EDT
Already have a fix - just needs to be tested.
Comment 7 Kent Johnson CLA 2005-04-06 14:02:19 EDT
Added MethodVerify test051
Comment 8 Olivier Thomann CLA 2005-05-11 09:18:41 EDT
The error is properly reported now.
Verified in I20050510-0010