Bug 111093 - More problems with IMethodBinding#isSubsignature(..)
Summary: More problems with IMethodBinding#isSubsignature(..)
Status: VERIFIED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 3.2   Edit
Hardware: PC Windows XP
: P3 normal (vote)
Target Milestone: 3.4 M4   Edit
Assignee: Kent Johnson CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2005-09-29 12:47 EDT by Markus Keller CLA
Modified: 2007-12-11 06:58 EST (History)
5 users (show)

See Also:


Attachments
Proposed fix and regression tests (11.05 KB, patch)
2007-10-10 07:16 EDT, Jerome Lanneluc CLA
no flags Details | Diff
New proposed fix and same regression tests (11.00 KB, patch)
2007-10-11 09:02 EDT, Jerome Lanneluc CLA
no flags Details | Diff
Kent's fix and more regression tests (14.31 KB, patch)
2007-10-15 07:30 EDT, Jerome Lanneluc CLA
no flags Details | Diff
Proposed patch (31.09 KB, patch)
2007-10-29 15:26 EDT, Kent Johnson CLA
no flags Details | Diff
patch for APT (1.03 KB, patch)
2007-10-29 19:31 EDT, Walter Harley CLA
no flags Details | Diff
Proposed patch (33.99 KB, patch)
2007-10-30 15:41 EDT, Kent Johnson CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Markus Keller CLA 2005-09-29 12:47:46 EDT
The MethodOverrideTest from bug 107110 reveals further problems with
IMethodBinding#isSubsignature(..):

Cases where method from B is *not* a subsignature of the method from A:

1.:
class A<T> {
    public void o1_xoo2(A<?> s) {}
}
class B<S> extends A<S> {
    @Override public void o1_xoo2(A<Object> s) {}
}

2.:
class A<T> {
    public void o1_xoo3(A<? extends T> s) {}
}
class B<S> extends A<S> {
    @Override public void o1_xoo3(A<? super S> s) {}
}

3.:
class A<S, T> {
    public void o2_xoo1(List<? extends T> t) {}
}
class B<V, W> extends A<W, V> {
    @Override public void o2_xoo1(List<? extends W> t) {}
}

4.
class A {
    public void o3_xoo1(List t) {}
}
class B extends A {
    @Override public void o3_xoo1(List<Object> t) {}
}

5.
class A<T> {
    public A() {}
    public void o4_xoo1(T t) {}
}
class B extends A<List<String>> {
    @Override public void o4_xoo1(List<?> t) {}
}

6.
class A<S> {
    public <X, Y> void tp1_xoo3(X x, Y y) {}
}
class B extends A<String> {
    @Override public <W, V> void tp1_xoo3(V x, W y) {}
}


And a case where the method from B *is* a subsignature of the method from A:
class A<S> {
    public <X, Y> void tp1_foo2(S s, X x, Y y) {}
}
class B extends A<String> {
    @Override public void tp1_foo2(String s, Object x, Object y) {}
}

BTW: The ASTView 1.0.5 also shows isSubsignature in the comparison tray:
http://dev.eclipse.org/viewcvs/index.cgi/%7Echeckout%7E/jdt-ui-home/plugins/org.eclipse.jdt.astview/AST-View-Site
Comment 1 Jerome Lanneluc CLA 2007-06-22 06:38:07 EDT
Sorry it took so long.

With 3.3RC4, what I'm seeing is:
- 1, 2, 3, and 4: method from B *is* a subsignature of the method from A. This looks correct to me since in the 4 cases, the erasure of the methods are the same
- 5 and 6: method from B is *not* a subsignature of the method from A. This looks correct to me since in both cases, the erasure of the methods are not the same
- last case: method from B is *not* a subsignature of the method from A. This looks correct to me since in both cases, the erasure of the methods are not the same

Markus, please reopen if you don't agree with this analysis
Comment 2 Martin Aeschlimann CLA 2007-09-27 11:36:10 EDT
Here's the latest output: (I20070921-0919)

a.) 
====================================
testOverride1
====================================
IMethodBinding.overrides(): false
IMethodBinding.isSubsignature(): true
Bindings.isSubsignature(): false
Override Annotation: false

// Overridden: ----------------------------------
public class A<T> {
	public void o1_xoo2(A<?> s) {
	}
}
// Overriding: ----------------------------------
class B<S> extends A<S> {
	@Override
	public void o1_xoo2(A<Object> s) {
	}
}

b.)
====================================
testOverride1
====================================
IMethodBinding.overrides(): false
IMethodBinding.isSubsignature(): true
Bindings.isSubsignature(): false
Override Annotation: false

// Overridden: ----------------------------------
public class A<T> {
	public void o1_xoo3(A<? extends T> s) {
	}
}
// Overriding: ----------------------------------
class B<S> extends A<S> {
	@Override
	public void o1_xoo3(A<? super S> s) {
	}
}

c.)
====================================
testOverride2
====================================
IMethodBinding.overrides(): false
IMethodBinding.isSubsignature(): true
Bindings.isSubsignature(): false
Override Annotation: false

// Overridden: ----------------------------------
public class A<S, T> {
	public void o2_xoo1(List<? extends T> t) {
	}
}
// Overriding: ----------------------------------
class B<V, W> extends A<W, V> {
	@Override
	public void o2_xoo1(List<? extends W> t) {
	}
}

d.)
====================================
testOverride3
====================================
IMethodBinding.overrides(): false
IMethodBinding.isSubsignature(): true
Bindings.isSubsignature(): false
Override Annotation: false

// Overridden: ----------------------------------
public class A {
	public void o3_xoo1(List t) {
	}
}
// Overriding: ----------------------------------
class B extends A {
	@Override
	public void o3_xoo1(List<Object> t) {
	}
}

e.)
====================================
testOverride4
====================================
IMethodBinding.overrides(): false
IMethodBinding.isSubsignature(): true
Bindings.isSubsignature(): false
Override Annotation: false

// Overridden: ----------------------------------
public class A<T> {
	public void o4_xoo1(T t) {
	}
}
// Overriding: ----------------------------------
class B extends A<List<String>> {
	@Override
	public void o4_xoo1(List<?> t) {
	}
}

f.)
====================================
testOverrideMethodTypeParams1
====================================
IMethodBinding.overrides(): false
IMethodBinding.isSubsignature(): true
Bindings.isSubsignature(): true
Override Annotation: false

// Overridden: ----------------------------------
public class A<S> {
	public <X, Y> void tp1_xoo3(X x, Y y) {
	}
}
// Overriding: ----------------------------------
class B extends A<String> {
	@Override
	public <W, V> void tp1_xoo3(V x, W y) {
	}
}

g.)
====================================
testOverrideMethodTypeParams1
====================================
IMethodBinding.overrides(): true
IMethodBinding.isSubsignature(): false
Bindings.isSubsignature(): true
Override Annotation: true

// Overridden: ----------------------------------
public class A<S> {
	public <X, Y> void tp1_foo2(S s, X x, Y y) {
	}
}
// Overriding: ----------------------------------
class B extends A<String> {
	@Override
	public void tp1_foo2(String s, Object x, Object y) {
	}
}
----------------------------------------------------

My understanding is that a method overrides when
- the subtype's method has a signature that is a subsignature of the supertype's method
- and the two methods have compatible return types and throws clauses.

a.) - f.)
return types and throws clauses are the same: Why are 'overrides' and 'isSubsignature' different?

g.)
methods are not a subsignature. How can they then override?
Comment 3 Martin Aeschlimann CLA 2007-09-27 11:42:59 EDT
To get the output below:

Go to MethodOverrideTest, and set DEBUG_SHOWRESULTS to true.

bindingOverrides=
  overridingBinding.getMethodDeclaration().overrides(
                                 overriddenBinding.getMethodDeclaration());
bindingIsSubsignature=
  overridingBinding.isSubsignature(overriddenBinding);

override annotation:
  We test if there is a compilation error on the @Override annotation

Bindings.isSubsignature()
  Our own implementation, not 100% correct
Comment 4 Jerome Lanneluc CLA 2007-10-01 11:43:56 EDT
It looks like I was wrong on the definition of the subsignature. 

From the spec at http://java.sun.com/docs/books/jls/third_edition/html/classes.html#38649:

The signature of a method m1 is a subsignature of the signature of a method m2 if either
    * m2 has the same signature as m1, or
    * the signature of m1 is the same as the erasure of the signature of m2. 

IMethodBinding#isSubsignature(IMethodBinding) needs to be fixed.
Comment 5 Jerome Lanneluc CLA 2007-10-10 07:16:13 EDT
Created attachment 80030 [details]
Proposed fix and regression tests

Kent, what do you think of the fix? (

Note that IMethodBinding#isSubsignature(IMethodBinding) is implementing subsignature as defined in http://java.sun.com/docs/books/jls/third_edition/html/classes.html#8.4.2 and IMethodBinding#overrides(IMethodBinding) is implementing overrides as defined in http://java.sun.com/docs/books/jls/third_edition/html/classes.html#8.4.8.1.

Would it make sense to have these helpers on MethodVerifier?
Comment 6 Jerome Lanneluc CLA 2007-10-10 07:17:36 EDT
(forgot to add Kent on CC list)
Kent, please see comment 5.
Comment 7 Jerome Lanneluc CLA 2007-10-11 09:02:49 EDT
Created attachment 80133 [details]
New proposed fix and same regression tests

Fixes a small regression
Comment 8 Kent Johnson CLA 2007-10-12 15:35:39 EDT
I would try something like this :

 private boolean overrides(IMethodBinding overridenMethod, boolean checkVisibility) {
  try {
   org.eclipse.jdt.internal.compiler.lookup.MethodBinding overridenBinding = ((MethodBinding) overridenMethod).binding;
   if (!CharOperation.equals(this.binding.selector, overridenBinding.selector))
    return false;

   if (checkVisibility) {
    if (this.binding == overridenBinding || overridenBinding.isStatic() || this.binding.isStatic())
     return false;
    if (overridenBinding.isPrivate())
     return false;
    if (overridenBinding.isDefault() && overridenBinding.declaringClass.getPackage() != this.binding.declaringClass.getPackage())
     return false;
   }

   LookupEnvironment lookupEnvironment = this.resolver.lookupEnvironment();
   if (lookupEnvironment == null) return false;

   overridenBinding = overridenBinding.original();
   TypeBinding match = this.binding.declaringClass.findSuperTypeOriginatingFrom(overridenBinding.declaringClass);
   if (!(match instanceof ReferenceBinding)) return false;
   if (match == overridenBinding.declaringClass)
    return lookupEnvironment.methodVerifier().doesMethodOverride(this.binding, overridenBinding);

   org.eclipse.jdt.internal.compiler.lookup.MethodBinding[] superMethods = ((ReferenceBinding) match).getMethods(overridenBinding.selector);
   for (int i = 0, length = superMethods.length; i < length; i++)
    if (superMethods[i].original() == overridenBinding)
     return lookupEnvironment.methodVerifier().doesMethodOverride(this.binding, superMethods[i]);
   return false;
  } catch (AbortCompilation e) {
   // don't surface internal exception to clients
   // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=143013
   return false;
  }
 }



We can move these to the verifier if you want.
Comment 9 Jerome Lanneluc CLA 2007-10-15 07:30:25 EDT
Created attachment 80344 [details]
Kent's fix and more regression tests

Thanks Kent. Unfortunately, we are still misbehaving in the case of isSubsignature() with the 2 methods in different hierarchies. I attached a new test (test044()) that is currently failing with this last fix.

Otherwise, if this code could be moved to the method verifier, it would be great.
Comment 10 Kent Johnson CLA 2007-10-15 14:41:17 EDT
So in this case, the 2 methods are supposed to be "subsignatures" :

class X {
 void foo() {}
}

class Y {
 void foo() {}
}

Are they also subsignatures in this case ?

class X<T> {
 void foo(T t, Comparable<T> c) {}
}

class Y<U> {
 void foo(U u, Comparable<U> c) {}
}

Because if they are, then I think we will have a big problem since U != T.
Comment 11 Jerome Lanneluc CLA 2007-10-16 07:26:26 EDT
(In reply to comment #10)
> So in this case, the 2 methods are supposed to be "subsignatures" :
> 
> class X {
>  void foo() {}
> }
> 
> class Y {
>  void foo() {}
> }
> 
> Are they also subsignatures in this case ?
> 
> class X<T> {
>  void foo(T t, Comparable<T> c) {}
> }
> 
> class Y<U> {
>  void foo(U u, Comparable<U> c) {}
> }
> 
> Because if they are, then I think we will have a big problem since U != T.
> 
According to http://java.sun.com/docs/books/jls/third_edition/html/classes.html#8.4.2, Y#foo(T, Comparable<T>) is not a subsignature of X#foo(U, Comparable<U>) since the signatures are not equal, nor the signature of Y#foo(T, Comparable<T>)  is the same as the erasure of the signature of X#foo(U, Comparable<U>)
Comment 12 Jerome Lanneluc CLA 2007-10-23 09:38:39 EDT
Reassigning to Kent for a cleaner fix (to add an internal API on MethodVerifier)
Comment 13 Kent Johnson CLA 2007-10-24 15:42:27 EDT
David - I see 4 calls to the verifier from the CompletionEngine.

findLocalMethodDeclarations
findLocalMethods
findLocalMethodsFromFavorites
findLocalMethodsOfStaticImports

All these methods call MethodVerifier.doesMethodOverride()

We're trying to make this method more accurate for the DOM, but the code assists tests fail if enforce the rule that the first method is from a subtype of the second method.

Can you tell me what exactly you're looking for ? Do you just want to know if the method's parameters are equal ?
Comment 14 Kent Johnson CLA 2007-10-29 09:52:02 EDT
David - please see the questions in comment 13
Comment 15 Kent Johnson CLA 2007-10-29 15:26:29 EDT
Created attachment 81493 [details]
Proposed patch

This patch changes MethodVerifier.doesMethodOverride() to enforce the visibility & inheritance requirements of the DOM.

Some users such as Code Assist and APT may need to change their senders to MethodVerifier.isMethodSubsignature()
Comment 16 Kent Johnson CLA 2007-10-29 15:28:10 EDT
Walter - please try the patch & see if the APT senders of doesMethodOverride() need to be converted over to isMethodSubsignature().
Comment 17 Walter Harley CLA 2007-10-29 19:31:19 EDT
Created attachment 81523 [details]
patch for APT

That JDT patch does break org.eclipse.jdt.apt.compiler.tests.ModelUtilTests.  Our impl of ExecutableElement.hides(Element) is based on doesMethodOverride().  Changing to isMethodSubsignature() appears to be logically correct, and fixes the test.

We also call doesMethodOverride() when we list all visible entities in a type, in our impl of Elements.getAllMembers.  We might be able to optimize a little by calling isMethodSubsignature() there, since we're guaranteed to be in the context of a single type, but doesMethodOverride() also seems correct and passes tests.

I'll attach the necessary patch to o.e.jdt.compiler.apt.  Please either check this patch in at the same time as the JDT patch, or let Olivier or me know so we can check it in, to avoid breaking the APT tests.
Comment 18 David Audel CLA 2007-10-30 07:21:07 EDT
Answer to comment 12

Currently doesMethodOverride() is called to know if the same method in a subclass is already proposed as a possible completion or not.
Code Assist must not take visibility into account and doesn't need to verify inheritance requirements. With the proposed patch the right method to call seems to be isMethodSubsignature() but  the test CompletionTests_1_5#test0291() failed and should not.
Comment 19 Kent Johnson CLA 2007-10-30 09:59:23 EDT
Yes David, I saw the failure yesterday after I posted the patch.

Will investigate a 'newer' patch.
Comment 20 Kent Johnson CLA 2007-10-30 15:41:13 EDT
Created attachment 81635 [details]
Proposed patch

Fixed the problem with the Code assist test.

Integrated APT changes.

Walter - when you have time, please try out this patch. thx
Comment 21 Kent Johnson CLA 2007-11-02 14:11:48 EDT
Released into HEAD for 3.4M4

Includes changes to APT.ExecutableElementImpl & new tests (35-44) in CompatibilityRulesTests
Comment 22 David Audel CLA 2007-12-11 06:58:54 EST
Verified for 3.4M4 using build I20071210-1800.