Bug 322531 - [1.5][Generics] eclipse compiles code rejected by javac with incomparable types error.
Summary: [1.5][Generics] eclipse compiles code rejected by javac with incomparable typ...
Status: REOPENED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 3.6   Edit
Hardware: All All
: P3 major (vote)
Target Milestone: ---   Edit
Assignee: JDT-Core-Inbox CLA
QA Contact:
URL:
Whiteboard: stalebug
Keywords:
: 170064 323473 (view as bug list)
Depends on:
Blocks:
 
Reported: 2010-08-12 09:44 EDT by Achim Loerke CLA
Modified: 2023-01-30 17:13 EST (History)
6 users (show)

See Also:


Attachments
Proposed patch (18.22 KB, patch)
2010-08-25 01:14 EDT, Srikanth Sankaran CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Achim Loerke CLA 2010-08-12 09:44:43 EDT
The following snippet compiles fine using the build-in compiler in Eclipse 3.5.2 and 3.6 but yields an error using javac 1.6.0_21 and 1.5.0_22

import java.awt.event.ActionListener;
import java.util.ArrayList;
public class Test {
    public Test() {
    }
    public <T extends ActionListener> void test(Class<T> clazz) {
        boolean b = clazz == ArrayList.class;
    }
}

The message is

Test.java:7: incomparable types: java.lang.Class<T> and java.lang.Class<java.util.ArrayList>
boolean b = clazz == ArrayList.class;

Seems to me that javac is right since ArrayList is not a subclass of ActionListener.
Comment 1 Srikanth Sankaran CLA 2010-08-22 23:59:19 EDT
Indeed, this is a bug in eclipse. Will follow up.
Comment 2 Srikanth Sankaran CLA 2010-08-23 08:10:55 EDT
Slightly shorter test case:

interface I {}

public class Test {
	<T extends I> void main(Class<T> clazz) {
		boolean b = 
			clazz == Test.class ||  // eclipse - !Error, javac - Error
			I.class == Test.class;  // Both compilers complain here
	}
}
Comment 3 Srikanth Sankaran CLA 2010-08-23 08:38:13 EDT
Also interesting is:


interface I {}

public class Test {
	<T extends I> void main(Class<T> clazz, Test t) {
		boolean b = 
			 clazz == t.getClass() ||  // eclipse mum, javac mum
		            I.class == t.getClass(); // eclipse mum, javac scream 
		
	}
}

Again, at the outset, javac behaviour seems to be correct.
Comment 4 Srikanth Sankaran CLA 2010-08-23 23:22:07 EDT
Also interesting is that eclipse compiles the following without error:

import java.util.List;

interface I {}

public class Test {
	<T extends I> void main(List<T> clazz, List<Test> testList) {
		boolean b = clazz == testList;
	}
}

while javac complains.

but if you make Test a final class, eclipse also complains.

In the exact same contexts, eclipse has no hesitation in
flagging an assignment as being illegal.
Comment 5 Srikanth Sankaran CLA 2010-08-24 03:44:04 EDT
Here is another very interesting case:

interface List<E> {}
interface I {}
public class X implements I {
    void main(List<I> li, X t) {
        boolean b = 
            I.class == t.getClass();  // javac complains here
	b = 
	    li == t.getList();        // javac does not complain here
    }
    
    List<? extends Object> getList() {
    	return null;
    }
}

which indicates that javac recognizes I.class to be a constant
class literal expresssion.
Comment 6 Srikanth Sankaran CLA 2010-08-24 06:31:48 EDT
(In reply to comment #4)
> Also interesting is that eclipse compiles the following without error:
> 
> import java.util.List;
> 
> interface I {}
> 
> public class Test {
>     <T extends I> void main(List<T> clazz, List<Test> testList) {
>         boolean b = clazz == testList;
>     }
> }
> 
> while javac complains.

I have raised Bug 323473 to track this.
Comment 7 Srikanth Sankaran CLA 2010-08-25 01:14:38 EDT
Created attachment 177385 [details]
Proposed patch
Comment 8 Srikanth Sankaran CLA 2010-08-25 01:15:42 EDT
All tests pass, Jay, please review. TIA.
Comment 9 Srikanth Sankaran CLA 2010-08-25 01:28:44 EDT
The problem here basically is that the eclipse compiler
was not recognizing (in the comparison context) that
an expression such as ArrayList.class is a compile time
constant, it being a class literal and that the type of
that expression is frozen cannot refer to any subtypes
whatsoever.
Comment 10 Jay Arthanareeswaran CLA 2010-09-03 07:51:25 EDT
Patch looks good. Sorry about the delay, Srikanth.
Comment 11 Srikanth Sankaran CLA 2010-09-05 23:28:49 EDT
Released in HEAD for 3.7 M2.
Comment 12 Olivier Thomann CLA 2010-09-14 10:09:35 EDT
Verified for 3.7M2 using I20100914-0100
Comment 13 Srikanth Sankaran CLA 2010-11-23 23:55:03 EST
Reopening since this causes more serious problems
(see bug 330869). Will look to fix this for M4.
Comment 14 Stephan Herrmann CLA 2010-11-24 17:40:18 EST
This seems to be a big twilight zone, best reflected in Philippe's comment:
bug 148046 comment 1.
The JLS arbitrarily defines what criteria can be used to "prove" two types
distinct. This is different from what javac checks, is different from
what common sense may conclude, which is different from ...

For the issue at hand JLS 4.5.1 has this simple rule:
"Two type arguments are provably distinct if neither of the arguments is a 
type variable or wildcard, and the two arguments are not the same type."

According to that rule we have no reason to report an error for any of the
examples that involve a type variable.

I made a quick test by adding to the top of isProvablyDistinctTypeArgument()
  	if (isTypeVariable() || otherArgument.isTypeVariable())
		return false;
This changed the outcome of 2 tests in GenericsRegressionTest
  - test322531d() and test322531f()
and 10 distinct tests in GenericTypeTest
In all cases common sense seems to agree with our compiler, but I doubt
that these are in line with the JLS.

Other related bugs are bug 158870, bug 89940 and bug 90437, which introduced
the method TypeBinding.isProvableDistinctSubType(), and many more related
issues I'm sure.

Srikanth, what's your strategy in this mine field? Is it time to make
strictness configurable (JLS - emulate javac - best effort)? Might be
cleanest but also a significant effort I'm afraid.
Comment 15 Srikanth Sankaran CLA 2010-11-29 00:38:27 EST
(In reply to comment #14)
> This seems to be a big twilight zone, best reflected in Philippe's comment:
> bug 148046 comment 1.
> The JLS arbitrarily defines what criteria can be used to "prove" two types
> distinct. This is different from what javac checks, is different from
> what common sense may conclude, which is different from ...
> 
> For the issue at hand JLS 4.5.1 has this simple rule:
> "Two type arguments are provably distinct if neither of the arguments is a 
> type variable or wildcard, and the two arguments are not the same type."
> 
> According to that rule we have no reason to report an error for any of the
> examples that involve a type variable.

I agree that there is room for discretion in these cases. What I would
like to do here is to address those cases where the distinction can be
discerned fairly obviously and match up eclipse behavior with javac, but
this is going to have to wait as I don't have time right now.

I'll revisit this later and see what is the best course of action.
Comment 16 Srikanth Sankaran CLA 2011-05-19 08:24:07 EDT
(In reply to comment #14)
> This seems to be a big twilight zone, best reflected in Philippe's comment:
> bug 148046 comment 1.
> The JLS arbitrarily defines what criteria can be used to "prove" two types
> distinct. This is different from what javac checks, is different from
> what common sense may conclude, which is different from ...
> 
> For the issue at hand JLS 4.5.1 has this simple rule:
> "Two type arguments are provably distinct if neither of the arguments is a 
> type variable or wildcard, and the two arguments are not the same type."

See JLS spec bug 6557279 and sun compiler bug 6932571.
Comment 17 Stephan Herrmann CLA 2011-05-19 09:14:19 EDT
(In reply to comment #16)
> See JLS spec bug 6557279 and sun compiler bug 6932571.

Do you have a working link to the spec bug?
All I get on oracles pages is: "This bug is not available."
Comment 18 Srikanth Sankaran CLA 2011-05-19 21:15:43 EDT
(In reply to comment #17)
> (In reply to comment #16)
> > See JLS spec bug 6557279 and sun compiler bug 6932571.
> 
> Do you have a working link to the spec bug?
> All I get on oracles pages is: "This bug is not available."

Yes, this link looks broken now - Here is the text from notes I had
made earlier:

JLS 4.5.1.1 should be clarified along these lines:
"Two type arguments are provably distinct if one of the following is true:
- Neither argument is a type variable or wildcard, and the two arguments are not the same type.
- One type argument is a type variable or wildcard, with an upper bound (from capture conversion, if necessary) is S; and the other type argument T is not a type variable or wildcard; and neither |S|<:|T| nor |T|<:|S|.
- Each type argument is a type variable or wildcard, with upper bounds (from capture conversion, if necessary) of S and T; and neither |S|<:|T| nor |T|<:|S|."
Comment 19 Srikanth Sankaran CLA 2011-05-23 10:21:11 EDT
Stephan, Olivier,

> JLS 4.5.1.1 should be clarified along these lines:
> "Two type arguments are provably distinct if one of the following is true:
> - Neither argument is a type variable or wildcard, and the two arguments are
> not the same type.
> - One type argument is a type variable or wildcard, with an upper bound (from
> capture conversion, if necessary) is S; and the other type argument T is not a
> type variable or wildcard; and neither |S|<:|T| nor |T|<:|S|.
> - Each type argument is a type variable or wildcard, with upper bounds (from
> capture conversion, if necessary) of S and T; and neither |S|<:|T| nor
> |T|<:|S|."

I have been staring at this text for a long time now - does not this make
List<? extends Number> and List<Object> not provably distinct ?  For:

"Two parameterized types are provably distinct if either of the following conditions
hold:
• They are invocations of distinct generic type declarations.
• Any of their type arguments are provably distinct."

Bullet 1 obviously holds. Looking at bullet 2,

Argument#1 = "? extends Number" and Argument#2 = Object
S = UpperBound(Argument#1) = Number
T = Object.

|S| <: |T|

What silly mistake/oversight am I committing here ?
Comment 20 Stephan Herrmann CLA 2011-05-23 15:44:25 EDT
(In reply to comment #19)

Hi Srikanth,

"Sorrry" I can't find a mistake in your reasoning.

Here's how I interpret the situation: the currently valid version of the JLS
is a strawman in this regard: as soon as we see a type variable or a wildcard
we are not allowed to mark any parameterized types as provably distinct. EOS.

The "improvement" you quote (let's assume it has any authorization,
to me it is little more than a rumour) takes the smallest possible step
in the desired direction: if we encounter any type variable or wildcard,
those must be really really different for the overall types to be considered
provably distinct.

So while List<Number> is provably distinct from List<Object>, 
the "new spec" indeed doesn't consider List<? extends Number> or 
List<E extends Number> as provably distinct from List<Object>.

Sure, if we have wildcard/typevariable with an upper bound and a class type
above the upper bound those can never be unified, but the "new spec"
simply doesn't exploit this fact and thus it is probably much weaker than
the current implementation of javac.

Sure the spec would become longer if all cases would be aggressively
spelled out to mark as provably distinct as many pairs of types as possible.
If one type argument is a typevariable or wildcard with an upper bound,
maybe different cases would need to be created for the other type 
argument (class type vs. interface type) to determine whether two-way 
incompatibility or one-way incompatibility must be required etc.

I'm sure between us we could prove lots of pairs of types distinct, which
the JLS shyly accepts as may-be-this-may-be-that.

My question is: do we see strong activity towards a really improved spec?

Otherwise, why not indeed consider different levels of type checking:
- strict JLS (weakest)
- approximate javac (medium)
- best effort (strongest)

I admit the medium level would be difficult to maintain, because all depends
on practical experiments, and the evolution of javac may be diffictul to
trace. But I don't see how one fixed algorithm can ever serve all masters.
Comment 21 Srikanth Sankaran CLA 2011-05-23 22:17:19 EDT
(In reply to comment #20)
> (In reply to comment #19)

> Here's how I interpret the situation: the currently valid version of the JLS
> is a strawman in this regard: as soon as we see a type variable or a wildcard
> we are not allowed to mark any parameterized types as provably distinct. EOS.
> 
> The "improvement" you quote (let's assume it has any authorization,
> to me it is little more than a rumour) takes the smallest possible step
> in the desired direction: if we encounter any type variable or wildcard,
> those must be really really different for the overall types to be considered
> provably distinct.

Thanks for taking a look and re-affirming the observation.
When I saw the spec bug being fixed, I rather naively expected
javac7 of the latest vintage to comply to this version.

In the end as you say, this _is_ only the smallest possible step
in the desired direction and is not a panacea for the all the
behavior differences between javac & ecj due to each one's
idiosyncratic interpretation of distinctness of types and 
type arguments.

> My question is: do we see strong activity towards a really improved spec?

There are some behavior differences between javac7 and javac6 which in
tandem with the spec bug seemed to indicate such activity, but it now
looks just a smoke screen.

> Otherwise, why not indeed consider different levels of type checking:
> - strict JLS (weakest)
> - approximate javac (medium)
> - best effort (strongest)

We have some use cases and user scenarios where compatibility
is paramount and that really narrows down the choice to the
medium level,

> I admit the medium level would be difficult to maintain, because all depends
> on practical experiments, and the evolution of javac may be diffictul to
> trace. But I don't see how one fixed algorithm can ever serve all masters.

Sigh.
Comment 22 Stephan Herrmann CLA 2011-05-24 01:00:35 EDT
(In reply to comment #21)
> > Otherwise, why not indeed consider different levels of type checking:
> > - strict JLS (weakest)
> > - approximate javac (medium)
> > - best effort (strongest)
> 
> We have some use cases and user scenarios where compatibility
> is paramount and that really narrows down the choice to the
> medium level,

I wasn't suggesting to abandon the medium level, but to make strictness
configurable. I don't mind making medium the default, but give people a
choice to push the compiler either way.

Or are you saying that *everybody* leave it at the (muddled) medium?

Maybe having the option to revert to medium, people get the courage
to use best effort during day-to-day business?
Comment 23 Srikanth Sankaran CLA 2011-05-24 10:36:00 EDT
(In reply to comment #22)
> (In reply to comment #21)
> > > Otherwise, why not indeed consider different levels of type checking:
> > > - strict JLS (weakest)
> > > - approximate javac (medium)
> > > - best effort (strongest)

[...]

> I wasn't suggesting to abandon the medium level, but to make strictness
> configurable. I don't mind making medium the default, but give people a
> choice to push the compiler either way.
> 
> Or are you saying that *everybody* leave it at the (muddled) medium?

Strict JLS mode is not going to serve any useful purpose given how
underspecified things are. If we make medium the default (which is the
only real choice we have), we anyway have the onerous burden of divining
javac's interpretation by little more than staring into tea leaves. And
you want to complicate things further by adding yet another mode ? :)

On a more serious note, I agree there is no gainsaying the value of a
strict mode, but it is a project for another day.
Comment 24 Srikanth Sankaran CLA 2011-06-03 03:09:50 EDT
Another test case from https://bugs.eclipse.org/bugs/show_bug.cgi?id=170064

public class Test {

    public static class Foo<T>{
    }

    public static class Bar<T> extends Foo<T>{
    }

    public <Z,E extends Foo<Z>> Class<E> test(){
        return (Class<E>) Bar.class;
    }

}
Comment 25 Srikanth Sankaran CLA 2011-06-03 03:10:54 EDT
*** Bug 170064 has been marked as a duplicate of this bug. ***
Comment 26 Srikanth Sankaran CLA 2011-06-03 03:25:33 EDT
Another test case from bug 323473


interface List<E> {}
interface I {}

public class X {
    <T extends I> void main(List<T> clazz, List<X> xList) {
        boolean b = clazz == xList ||
            xList == clazz;
    }
}
Comment 27 Srikanth Sankaran CLA 2011-06-03 03:26:17 EDT
*** Bug 323473 has been marked as a duplicate of this bug. ***
Comment 28 Stephan Herrmann CLA 2018-08-26 11:01:01 EDT
Despite all the intricacies, I believe comment 0 should be fixable on our side => during 4.10 time permitting.
Comment 29 Eclipse Genie CLA 2020-11-09 17:25:35 EST
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.
Comment 30 Eclipse Genie CLA 2023-01-30 17:13:09 EST
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.