Bug 466960 - Compilation error using Generics between EJC and JavaC
Summary: Compilation error using Generics between EJC and JavaC
Status: NEW
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 4.5   Edit
Hardware: PC Windows 7
: P3 normal (vote)
Target Milestone: ---   Edit
Assignee: JDT-Core-Inbox CLA
QA Contact: Srikanth Sankaran CLA
URL:
Whiteboard: stalebug
Keywords:
Depends on:
Blocks: 564617
  Show dependency tree
 
Reported: 2015-05-11 04:15 EDT by Luca Masera CLA
Modified: 2023-06-08 04:26 EDT (History)
4 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Luca Masera CLA 2015-05-11 04:15:43 EDT
I think I've found a bug in the compiler of Eclipse. Or, at least, something that is not working when used with Java standard compiler. 

The following code will work in Eclipse. 

Assume some basic classes organized in this way: 

 public abstract class AbstractClass {...}
 public class ConcreteClassTypeA extends AbstractClass {...}
 public class ConcreteClassTypeB extends AbstractClass {...}

Then define this interface: 

 interface DoSomething<T extends AbstractClass> {
 
   // what it returns is not important
   public void doThis(final T aClass);

  ...
 } 

And, finally, this classes:

 // the abstract one has some commons
 public abstract class AbstractDoSomething<T extends AbstractClass> implements DoSomething<T> {
   
   @Override
   public void doThis(final AbstractClass aClass) {
    ... 
   } 

   ... 
 } 


 public class ConcreteTypeADoSomethings extends AbstractDoSomething<ConcreteClassTypeA> {
     
   @Override
   public void doThis(final ConcreteClassTypeA aClass) {
    ... 
   } 

   ... 
 } 


 public class ConcreteTypeBDoSomethings extends AbstractDoSomething<ConcreteClassTypeB> {
     
   @Override
   public void doThis(final ConcreteClassTypeB aClass) {
    ... 
   } 

   ... 
 } 

But, when it's run from the command line (i.e. from the maven goal compile), the result is an error. The last part of it is: 
"... overrides a method whose erasure is the same as another method, yet neither overrides the other"

I've searched and I've found that this behaviour (of the javac) comes from the need to support old code. So it looks a legacy "need", but it means also that it will break code that compiles in Eclipse.
Comment 1 Stephan Herrmann CLA 2015-05-11 12:41:45 EDT
(In reply to Luca Masera from comment #0)
> I've searched and I've found that this behaviour (of the javac) comes from
> the need to support old code. So it looks a legacy "need", but it means also
> that it will break code that compiles in Eclipse.

Thanks for your research. Could you please provide a link where you found this evaluation?
Comment 2 Luca Masera CLA 2015-05-11 15:53:55 EDT
http://stackoverflow.com/questions/1998544/method-has-the-same-erasure-as-another-method-in-type

Here is an explanation about generics and type erasure. 

When I've read it I've thought it was, more or less, similar. Right now I don't know it this is a good reference. Maybe the reason is the same, but I'm not so sure about it.
Comment 3 Stephan Herrmann CLA 2015-05-11 17:18:46 EDT
(In reply to Luca Masera from comment #2)
> http://stackoverflow.com/questions/1998544/method-has-the-same-erasure-as-
> another-method-in-type
> 
> Here is an explanation about generics and type erasure. 
> 
> When I've read it I've thought it was, more or less, similar. Right now I
> don't know it this is a good reference. Maybe the reason is the same, but
> I'm not so sure about it.

Thanks. So that background information only answers, what that specific error message means, not whether or not it is mandated when compiling this particular example.

More info:

javac 1.5 accepts the program (if you remove the @Override annotations). Starting with version 1.6, javac complains.

Ecj 3.2.2 did *not* accept the program, but starting with 3.3 it is accepted.

Acceptance seems not to depend on compliance levels.

Strange to see both compilers evolve in opposite directions ;P


Reduced compilable code:

//---
abstract class AbstractClass {}
class ConcreteClassTypeA extends AbstractClass {}
interface DoSomething<T extends AbstractClass> {
   public void doThis(final T aClass);
}
abstract class AbstractDoSomething<T extends AbstractClass> implements DoSomething<T> {
   public void doThis(final AbstractClass aClass) {}
} 
public class ConcreteTypeADoSomethings extends AbstractDoSomething<ConcreteClassTypeA> {
   public void doThis(final ConcreteClassTypeA aClass) {}
}
//---

If we look at the bytecode generated by ecj, we see these methods:
in DoSomething:
  public abstract void doThis(AbstractClass);
in AbstractDoSomething:
  public void doThis(AbstractClass);
in ConcreteTypeADoSomethings:
  public void doThis(ConcreteClassTypeA);
  public void doThis(AbstractClass);
the last of these methods is a bridge method, delegating to the method with more specific signature.

So, from a quick glance, I don't see which two methods should have the same erasure without overriding each other.

Generating the bridge method is mandated by implementing the interface method with parameter T, where:
- the interface uses the type bound (AbstractClass) as the erased parameter
- the implementing class instantiates the type parameter
The bridge correctly connects these two.

What looks funny, is the fact that the bridge "accidentally" overrides the method from the super class.
Comment 4 Timo Kinnunen CLA 2015-05-20 21:12:40 EDT
I see this compile error with NetBeans:
name clash: doThis(ConcreteClassTypeA) in ConcreteTypeADoSomethings overrides a method whose erasure is the same as another method, yet neither overrides the other
  first method:  doThis(AbstractClass) in AbstractDoSomething
  second method: doThis(T) in DoSomething
  where T is a type-variable:
    T extends AbstractClass declared in interface DoSomething

I think what javac is trying to say is that doThis(AbstractClass) in AbstractDoSomething no longer overrides doThis(T) in DoSomething once ConcreteTypeADoSomething has inherited it. I think javac is right in this case. Here's a slightly modified example:

abstract class AbstractClass {}
class ConcreteClassTypeA extends AbstractClass {}
interface DoSomething<T extends AbstractClass> {
	public void doThis(T aClass);
}
abstract class AbstractDoSomething<T extends AbstractClass> implements DoSomething<T> {
    public final @Override void doThis(AbstractClass aClass) {}
}
public class ConcreteTypeADoSomethings extends AbstractDoSomething<ConcreteClassTypeA> {
    public @Override void doThis(ConcreteClassTypeA aClass) {}

    public static void main(String[] args) {
        ConcreteTypeADoSomethings c = new ConcreteTypeADoSomethings();
        ConcreteClassTypeA cc = new ConcreteClassTypeA();
        AbstractClass ac = new AbstractClass() {};
        c.doThis(cc);
        c.doThis(ac);
    }
}
This compiles but fails for me at runtime with this error:
java.lang.VerifyError: class typing.ConcreteTypeADoSomethings overrides final method doThis.(Ltyping/AbstractClass;)V
Comment 5 Manoj N Palat CLA 2018-05-16 01:06:50 EDT
bulk move out of 4.8
Comment 6 Eclipse Genie CLA 2022-08-12 15:00:45 EDT
This bug hasn't had any activity in quite some time. Maybe the problem got resolved, was a duplicate of something else, or became less pressing for some reason - or maybe it's still relevant but just hasn't been looked at yet.

If you have further information on the current state of the bug, please add it. The information can be, for example, that the problem still occurs, that you still want the feature, that more information is needed, or that the bug is (for whatever reason) no longer relevant.

--
The automated Eclipse Genie.