Bug 105479 - [jdt-compiler]error with covariant return types
Summary: [jdt-compiler]error with covariant return types
Status: RESOLVED FIXED
Alias: None
Product: AspectJ
Classification: Tools
Component: Compiler (show other bugs)
Version: DEVELOPMENT   Edit
Hardware: PC Linux
: P2 normal (vote)
Target Milestone: 1.5.0RC1   Edit
Assignee: Andrew Clement CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2005-07-28 12:55 EDT by Reid Hartenbower CLA
Modified: 2005-11-01 15:31 EST (History)
1 user (show)

See Also:


Attachments
jar containing aspect and test class (4.49 KB, application/octet-stream)
2005-10-28 14:38 EDT, Reid Hartenbower CLA
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Reid Hartenbower CLA 2005-07-28 12:55:31 EDT
The aspect below won't compile ('The return type is incompatible') using the 
latest from CVS as of 27-7-05 

public aspect ReturnTypeTest {
  private interface Test {
    Object getId();
  }
 
  class StringTest {
    public String getId() {
      return null;
    }
  }
 
  declare parents : StringTest implements Test;
}

from Andy Clement: 

note that this pure Java version of the program doesn't compile
with ajc but compiles with javac:

public class ReturnTypeTest {
  private interface Test {
    Object getId();
  }
 
  class StringTest implements Test {
    public String getId() {
      return null;
    }
  }
}

which tells us it is probably occurring because we haven't upgraded to
the final version of the 3.1 eclipse JDT compiler.
Comment 1 Andrew Clement CLA 2005-08-18 05:14:45 EDT
tagging as something that will be fixed when we upgrade to the 3.1 final compiler.
Comment 2 Andrew Clement CLA 2005-08-24 06:47:17 EDT
The pure java scenario is fixed now we have moved to the final 3.1 JDT compiler.
 The AspectJ variant that uses declare parents still doesnt work:

K:\ws\aspectj_ws\A\ReturnTypeTest.java:1 [error] The return type is incompatible
with ReturnTypeTest$Test.getId()
(no source information available)

1 error

thats with the build dated 20050824100440

it might possibly be fixed by another change I'm about to put in...
Comment 3 Adrian Colyer CLA 2005-08-26 11:41:26 EDT
for M4
Comment 4 Adrian Colyer CLA 2005-08-31 10:00:11 EDT
finally fixed! At the last hurdle, the test for assignability of return types in
binary weaving of declare parents was the wrong way round.

Fix commited in tree and will be available in published build from AspectJ
downloads page later on today.
Comment 5 Adrian Colyer CLA 2005-08-31 12:22:16 EDT
fix now available in latest published build
Comment 6 Reid Hartenbower CLA 2005-09-01 18:51:46 EDT
I have verified that covariance in return types now generates no compiler
errors; thank you.

However, the aspect below compiles, but loading the ReturnTypeTester class
results in a java.lang.AbstractMethodError when the
    Object Test.getId()
is called inside
    int Test.hashCode()
 
(see stack trace at bottom)

Thanks,
Reid

/*
 * ReturnTypeTest.java
 *
 * Created on September 1, 2005, 3:26 PM
 *
 */


public aspect ReturnTypeTest {
  private interface Test {
    Object getId();
    int hashCode();
  }
 
  public int Test.hashCode() {
    return getId().hashCode();
  }
 
  declare parents : ReturnTypeTester implements Test;
}

/*
 * ReturnTypeTester.java
 *
 * Created on September 1, 2005, 3:28 PM
 *
 */

import java.util.HashSet;
import java.util.Set;

public class ReturnTypeTester {
  static Set<ReturnTypeTester> set = new HashSet<ReturnTypeTester>();
  static {
    ReturnTypeTester tester = new ReturnTypeTester();
    set.add(tester);
  }
 
  public String getId() {
    return "id";
  }
}

stack trace:

java.lang.AbstractMethodError: ReturnTypeTester.getId()Ljava/lang/Object;
        at
ReturnTypeTest.ajc$interMethod$ReturnTypeTest$ReturnTypeTest$Test$hashCode(ReturnTypeTest.java:16)
        at ReturnTypeTester.hashCode(ReturnTypeTester.java:1)
        at java.util.HashMap.hash(HashMap.java:264)
        at java.util.HashMap.put(HashMap.java:382)
        at java.util.HashSet.add(HashSet.java:194)
        at ReturnTypeTester.<clinit>(ReturnTypeTester.java:15)








Comment 7 Adrian Colyer CLA 2005-09-02 06:06:00 EDT
I haven't been able to reproduce this bug this morning, I've tried variants both
with all types in the same file, and with the types declared in separate files.

Here's my test program, based on your input:

import java.util.HashSet;
import java.util.Set;

aspect ReturnTypeTest {
  private interface Test {
    Object getId();
    int hashCode();
  }
 
  public int Test.hashCode() {
	System.out.println("in Test.hashCode()");
    return getId().hashCode();
  }
 
  declare parents : ReturnTypeTester implements Test;
}

class ReturnTypeTester {
  static Set<ReturnTypeTester> set = new HashSet<ReturnTypeTester>();
  static {
    ReturnTypeTester tester = new ReturnTypeTester();
    set.add(tester);
  }
 
  public String getId() {
    return "id";
  }
}

public class pr105479part2 {
	
	public static void main(String[] args) {
		ReturnTypeTester rtt = new ReturnTypeTester();
		rtt.hashCode();
		System.out.println(rtt.getId());
		if (rtt.hashCode() != "id".hashCode()) throw new 
                     RuntimeException("dispatch failure");
	}
	
}

and the test specification:

    <ajc-test dir="bugs150" pr="105479" 
              title="override and covariance with decp - runtime">
        <compile files="pr105479part2.aj" options="-1.5"/>
        <run class="pr105479part2">
            <stdout>
                <line text="in Test.hashCode()"/>
                <line text="in Test.hashCode()"/>
                <line text="id"/>
                <line text="in Test.hashCode()"/>
            </stdout>
        </run>
    </ajc-test>

Is there anything else present in your application that could be contributing to
triggering the bug? Some advice applying to one of the methods involved perhaps?
Does the program in this comment compile and run for you successfully giving the
expected output? 

Thanks, Adrian.
Comment 8 Adrian Colyer CLA 2005-10-28 06:59:38 EDT
We'd like to get a fix for this into 1.5.0 RC1 if we can, but need to be able to reproduce the failure first. 
Are you doing any binary weaving in this scenario perhaps??
Comment 9 Reid Hartenbower CLA 2005-10-28 13:38:29 EDT
Sorry I've not had more time to address this.  I do compile-time binary weaving.
I will send more info after I make sure no other advice affects this class, but
in the meantime here is a new version of the test class (the advice is
unchanged) and the resulting output.  

public class ReturnTypeTester {
  static Set<ReturnTypeTester> set = new HashSet<ReturnTypeTester>();
  public static void main(String[] args ) {
    ReturnTypeTester tester = new ReturnTypeTester();
    Class[] classes = tester.getClass().getInterfaces();
    for(Class clas : classes) {
      System.out.println("clas " + clas);
    }
    Method[] methods = tester.getClass().getMethods();
    for(Method method : methods) {
      System.out.println("method " + method);
    }
    Field[] fields = tester.getClass().getFields();
    for(Field field : fields) {
      System.out.println("field " + field);
    }
    System.out.println("here");
    tester.hashCode();
    System.out.println(tester.getId());
    if (tester.hashCode() != "id".hashCode()) throw new 
         RuntimeException("dispatch failure");
    
    set.add(tester);
  }
  
  public String getId() {
    return "id";
  }
  
}

clas interface ReturnTypeTest$Test
method public int ReturnTypeTester.hashCode()
method public static void ReturnTypeTester.main(java.lang.String[])
method public java.lang.String ReturnTypeTester.getId()
method public final native java.lang.Class java.lang.Object.getClass()
method public final native void java.lang.Object.wait(long) throws
java.lang.InterruptedException
method public final void java.lang.Object.wait(long,int) throws
java.lang.InterruptedException
method public final void java.lang.Object.wait() throws
java.lang.InterruptedException
method public boolean java.lang.Object.equals(java.lang.Object)
method public final native void java.lang.Object.notify()
method public final native void java.lang.Object.notifyAll()
method public java.lang.String java.lang.Object.toString()
method public abstract java.lang.Object aspects.ReturnTypeTest$Test.getId()
here
java.lang.AbstractMethodError: ReturnTypeTester.getId()Ljava/lang/Object;
        at org.apache.tools.ant.taskdefs.ExecuteJava.execute(ExecuteJava.java:172)
        at org.apache.tools.ant.taskdefs.Java.run(Java.java:705)
        at org.apache.tools.ant.taskdefs.Java.executeJava(Java.java:178)
        at org.apache.tools.ant.taskdefs.Java.execute(Java.java:84)
        at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:275)
        at org.apache.tools.ant.Task.perform(Task.java:364)
        at org.apache.tools.ant.Target.execute(Target.java:341)
        at org.apache.tools.ant.Target.performTasks(Target.java:369)
        at org.apache.tools.ant.Project.executeTarget(Project.java:1214)
        at org.apache.tools.ant.Project.executeTargets(Project.java:1062)
        at
org.apache.tools.ant.module.bridge.impl.BridgeImpl.run(BridgeImpl.java:234)
        at
org.apache.tools.ant.module.run.TargetExecutor.run(TargetExecutor.java:242)
        at org.netbeans.core.execution.RunClassThread.run(RunClassThread.java:125)
Caused by: java.lang.AbstractMethodError: ReturnTypeTester.getId()Ljava/lang/Object;
        at
aspects.ReturnTypeTest.ajc$interMethod$aspects_ReturnTypeTest$aspects_ReturnTypeTest$Test$hashCode(ReturnTypeTest.java:20)
        at ReturnTypeTester.hashCode(ReturnTypeTester.java:1)
        at ReturnTypeTester.main(ReturnTypeTester.java:35)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:585)
        at org.apache.tools.ant.taskdefs.ExecuteJava.run(ExecuteJava.java:193)
        at org.apache.tools.ant.taskdefs.ExecuteJava.execute(ExecuteJava.java:130)
        ... 12 more
--- Nested Exception ---
java.lang.AbstractMethodError: ReturnTypeTester.getId()Ljava/lang/Object;
        at
aspects.ReturnTypeTest.ajc$interMethod$aspects_ReturnTypeTest$aspects_ReturnTypeTest$Test$hashCode(ReturnTypeTest.java:20)
        at ReturnTypeTester.hashCode(ReturnTypeTester.java:1)
        at ReturnTypeTester.main(ReturnTypeTester.java:35)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:585)
        at org.apache.tools.ant.taskdefs.ExecuteJava.run(ExecuteJava.java:193)
        at org.apache.tools.ant.taskdefs.ExecuteJava.execute(ExecuteJava.java:130)
        at org.apache.tools.ant.taskdefs.Java.run(Java.java:705)
        at org.apache.tools.ant.taskdefs.Java.executeJava(Java.java:178)
        at org.apache.tools.ant.taskdefs.Java.execute(Java.java:84)
        at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:275)
        at org.apache.tools.ant.Task.perform(Task.java:364)
        at org.apache.tools.ant.Target.execute(Target.java:341)
        at org.apache.tools.ant.Target.performTasks(Target.java:369)
        at org.apache.tools.ant.Project.executeTarget(Project.java:1214)
        at org.apache.tools.ant.Project.executeTargets(Project.java:1062)
        at
org.apache.tools.ant.module.bridge.impl.BridgeImpl.run(BridgeImpl.java:234)
        at
org.apache.tools.ant.module.run.TargetExecutor.run(TargetExecutor.java:242)
        at org.netbeans.core.execution.RunClassThread.run(RunClassThread.java:125)
Comment 10 Reid Hartenbower CLA 2005-10-28 14:38:11 EDT
Created attachment 28941 [details]
jar containing aspect and test class

rtt.jar with just test class and aspect produces the exception for me when
using the ant target below.

  <target name="run" description="run">
       <java classname="com.imageworks.ejb.ReturnTypeTester">
	 <classpath>
	    <path refid="aspectj.jars"/>
	    <pathelement location="rtt.jar" />
	 </classpath>
       </java>
   </target>
Comment 11 Andrew Clement CLA 2005-11-01 06:44:27 EST
Ok.  This appears to be a missing bridge method.  Because the signature of getId
in the subtype is 'String getId()' and yet the signature on the interface was
'Object getId()' then we need to create a forwarding bridge method in the
subtype called 'Object getId()' that simply delegates to 'String getId()'.  If I
disassemble the class file attached in the zip at the end of the bug, I find
these set of methods in the type:

public class com.imageworks.ejb.ReturnTypeTester extends java.lang.Object implem
ents com.imageworks.ejb.aspects.rtt.ReturnTypeTest$Test{
    static java.util.Set set;
    public com.imageworks.ejb.ReturnTypeTester();
    public static void main(java.lang.String[]);
    public java.lang.String getId();
    static {};
    public int hashCode();
}

with no bridge method.  If I disassemble my version of the scenario, I get:

public class ReturnTypeTester extends java.lang.Object implements ReturnTypeTest
$Test{
    static java.util.Set set;
    static {};
    public ReturnTypeTester();
    public java.lang.String getId();
    public static void main(java.lang.String[]);
    public java.lang.Object getId();  <<<<<<<<<<<<<<<<<< here
    public int hashCode();
}

see the extra bridge method for getId().

So...  can I ask, what level of the compiler are you currently seeing the
problem on (or what did you use to build your .class files for the zip?)

also, you say you are doing 'compile-time binary weaving' - I'm not quite sure
what that means.  Are you binary weaving the aspect after compiling the two
source files separately?  If you are then I know why it fails - as this scenario
fails for me exactly as you describe:

javac ReturnTypeTester.java
ajc ReturnTypeTest.java
ajc -inpath . -d output

then running the .class files in output:

D:\temp\pr105479\poo>java ReturnTypeTester
Exception in thread "main" java.lang.AbstractMethodError: ReturnTypeTester.getId
()Ljava/lang/Object;
        at ReturnTypeTest.ajc$interMethod$ReturnTypeTest$ReturnTypeTest$Test$has
hCode(ReturnTypeTest.java:8)
        at ReturnTypeTester.hashCode(ReturnTypeTester.java:1)
        at java.util.HashMap.hash(HashMap.java:264)
        at java.util.HashMap.put(HashMap.java:382)
        at java.util.HashSet.add(HashSet.java:194)
        at ReturnTypeTester.<clinit>(ReturnTypeTester.java:8)

and the reason it does is covered in bug 108101 case #3.  
Comment 12 Andrew Clement CLA 2005-11-01 12:02:10 EST
I've checked in a fix for the binary weaving case.  We now correctly create the
bridge method and it all works fine.
Comment 13 Reid Hartenbower CLA 2005-11-01 13:16:14 EST
I have verified the bridge methods supporting covariance are inserted by ajc
when weaving 'decp' relationships into javac-produced .class files.  I usually
do it this way--is there an advantage to building and weaving entirely with ajc?

I usually do daily check out and builds of the ajc, and then 'test' it by
building my applications.  It's not a lot of support, but I deeply appreciate
the oustanding work you guys are doing.

Thanks, Reid.

P.S. The signature of the bridge 'Object getId()' method is:
method public volatile java.lang.Object com.imageworks.ejb.ReturnTypeTester.getId()
What does volatile as a return-type modifier mean?
Comment 14 Andrew Clement CLA 2005-11-01 15:31:38 EST
Some of the modifier flags are 'reused' here and there - the volatile is likely
to be appearing as it clashes with the bridge modifier which I suspect has the
same number (i dont have the spec to hand to check).  So far sorting this out
hasnt become urgent...so I haven't done it.

If the compiler gets full control from the start from source through to woven
.class files then it will make far fewer mistakes (bugs!) creating bridge
methods.  However it's very useful that you run the way you do since it
highlights all the bugs in binary weaving ;)

thanks for checking the fix.