Closed hohwille closed 8 years ago
OK. I was correct. Here is the minimal and simple isolated code to reproduce the bug in oracle compiler:
public interface GenericInterface<V> {
void setValue(V value);
}
public interface TypedInterface {
void setValue(Boolean value);
}
public interface CombinedInterface extends GenericInterface<Boolean>, TypedInterface {
default void set(boolean value) {
setValue(Boolean.valueOf(value));
}
}
Works in Eclipse but fails in Oracle java compiler:
CombinedInterface.java:[14,4] error: reference to setValue is ambiguous
I filed a bug at http://bugreport.java.com. Also I added workaround and travis is now green. I will keep this open until I have feedback from oracle (e.g. after I added the link to a public visible bug entry).
From: <Bug-Report-Daemon_WW@oracle.com>
Date: Mon, Jan 4, 2016 at 1:08 PM
Subject: Your Report (Review ID: JI-9028245) - reference is ambiguous with default method calling generic+typed method
To:*****
Dear Java Developer,
Thank you for reporting this issue.
We are evaluating this report and have assigned it a Review ID: JI-9028245. In the event this report is determined to be a defect or enhancement request, it will be referenced with a new Bug ID and will be listed on Bugs.java.com. For other related issues, please visit our Bug Database at http://bugs.java.com.
We try to process all newly posted bugs in a timely manner, but make no promises about the amount of time in which a bug might be fixed. If the issue just reported could have a major impact on your project, consider using one of the technical support offerings available at Oracle Support.
Regards,
Java Community Developer Support
BTW: As I added validation builder support I am now hitting more bugs in Oracle java compiler related to generic type inference and bounds matching that do not arise with Eclipse java compiler. These are however rather complex to isolate. I am unsure if I will report them as well, but it seems there is some work left to do in Oracle java compiler... Why do I have to be the one to hit those bugs? Arent there millions of other developers using Java with generics or am I the only one doing really complicated stuff with generics and others just tend to give up type safty rather than nesting generics like this:
protected <PROPERTY extends GenericPropertyImpl<? extends VALUE>, BUILDER extends ObjectValidatorBuilder<?, ? extends PropertyBuilder<? extends PROPERTY>, ?>> BUILDER withValdidator(
Function<PropertyBuilder<PROPERTY>, BUILDER> factory) {
The error is:
[ERROR] ...\IntegerPropertyImpl.java:[65,25] error: incompatible types: inference variable BUILDER has incompatible upper bounds
ObjectValidatorBuilder<?,? extends GenericPropertyImpl<Number>.PropertyBuilder<GenericPropertyImpl<? extends Number>>,?>,ValidatorBuilderInteger<GenericPropertyImpl<Number>.PropertyBuilder<IntegerPropertyImpl>>
[ERROR] where BUILDER,PROPERTY,VALUE are type-variables:
[ERROR] BUILDER extends ObjectValidatorBuilder<?,? extends GenericPropertyImpl<Number>.PropertyBuilder<PROPERTY>,?> declared in method <PROPERTY,BUILDER>withValdidator(Function<GenericPropertyImpl<VALUE>.PropertyBuilder<PROPERTY>,BUILDER>)
[ERROR] PROPERTY extends GenericPropertyImpl<? extends VALUE> declared in method <PROPERTY,BUILDER>withValdidator(Function<GenericPropertyImpl<VALUE>.PropertyBuilder<PROPERTY>,BUILDER>)
[ERROR] VALUE extends Object declared in class GenericPropertyImpl
Or
[ERROR] ...l\IntegerPropertyImpl.java:[65,25] error: incompatible types: inference variable PARENT has incompatible bounds
[ERROR] equality constraints: GenericPropertyImpl<Number>.PropertyBuilder<IntegerPropertyImpl>
[ERROR] lower bounds: GenericPropertyImpl<Number>.PropertyBuilder<GenericPropertyImpl<? extends Number>>
[ERROR] where PARENT is a type-variable:
[ERROR] PARENT extends Object declared in class ValidatorBuilderInteger
I tracked down the second issue and it turned out that here Oracle java compiler is not really wrong:
ObjectValidatorBuilder<?, ? extends PropertyBuilder<? extends PROPERTY>, ?>>
actually needs to be
ObjectValidatorBuilder<?, PropertyBuilder<? extends PROPERTY>, ?>>
After fixing this it works with both compilers. So Oracle java compiler is not wrong in the second case but just more strict than Eclipse java compiler.
I found further differences in Oracle vs. Eclipse compiler.
<V, PROPERTY extends ReadableProperty<V>> PropertyFactory<V, ? extends PROPERTY> getFactory(Class<PROPERTY> propertyType, Class<V> valueType, boolean polymorphic)
where:
public interface PropertyFactory<V, PROPERTY extends WritableProperty<V>> {
is called here:
protected <V, PROPERTY extends ReadableProperty<V>> AbstractProperty<V> createProperty(String name, GenericType<V> valueType, Bean bean, Class<PROPERTY> propertyClass) {
Class<V> valueClass = null;
if (valueType != null) {
valueClass = valueType.getRetrievalClass();
}
/*[410]*/ PropertyFactory<V, ?> factory = this.propertyFactoryManager.getFactory(propertyClass, valueClass, false);
This is accepted by Eclipse compiler but not by Oracle compiler which says:
[ERROR] ...\
BeanFactoryImpl.java:[410,74] error: incompatible types:
inference variable PROPERTY#1 has incompatible bounds
[ERROR] equality constraints: PROPERTY#2
[ERROR] upper bounds: WritableProperty<V#1>,ReadableProperty<V#1>
[ERROR] where PROPERTY#1,V#1,PROPERTY#2,V#2 are type-variables:
[ERROR] PROPERTY#1 extends ReadableProperty<V#1> declared in method <V#1,PROPERTY#1>getFactory(Class<PROPERTY#1>,Class<V#1>,boolean)
[ERROR] V#1 extends Object declared in method <V#1,PROPERTY#1>getFactory(Class<PROPERTY#1>,Class<V#1>,boolean)
[ERROR] PROPERTY#2 extends ReadableProperty<V#2> declared in method <V#2,PROPERTY#2>createProperty(String,GenericType<V#2>,Bean,Class<PROPERTY#2>)
[ERROR] V#2 extends Object declared in method <V#2,PROPERTY#2>createProperty(String,GenericType<V#2>,Bean,Class<PROPERTY#2>)
So besides the initial issue that I reported as bug in Oracle tracker all the other things I found indicate that Oracle compiler is very strict and picky while Eclipse compiler is sometimes a little lax. It seems that Java has become so complicated that people can hardly tell what is actually correct when it comes to advanced type inference. I better do not wait for Eclipse or Oracle to react but simply use workarounds to make my code compile.
I am closing as there is no issue in our project left. However there are issues in Oracle and Eclipse compilers left....
And another issue that is obviously a bug in Oracle java compiler that does not occur with Eclipse java compiler: https://travis-ci.org/m-m-m/util/builds/127054037
[ERROR] /home/travis/build/m-m-m/util/mmm-util-query-orientdb/src/main/java/net/sf/mmm/orient/impl/property/SinglePropertyBuilderLinkList.java:[66,37] error: incomparable types: Class<CAP#1> and Class<ObservableList>
After getting no response for my bug reports from Oracle, I wont even waste my time to create an isolated test-case for this bug (what should be easy to do for this obvious bug).
Another candidate that works fine with Eclipse compiler but fails with oracle javac:
public class GenericTest<A, B extends A> {
private final GenericTest<? super A, ? super B> parent;
public GenericTest(GenericTest<? super A, ? super B> parent) {
super();
this.parent = parent;
}
}
IMHO a wildcard (?) is as the name says a placeholder that is not a concrete type replacement. Therefore I can write GenericTest<?, ?>
and whatever concrete types will be filled into these wildcards (?) have to comply that the second extends the first without any notion in this declaration.
For what reason should this be different to ? extends ...
or ? super ...
?
IMHO this is a bug in oracle javac.
Another insane bug in Eclipse:
WritableCollectionProperty collectionProperty = (WritableCollectionProperty) property;
WritableProperty valueProperty = property.getValueProperty();
The above code gives me a compiler error:
The method getValueProperty() is undefined for the type WritableProperty<capture#15-of ?>
However the following cast lets the error go away in Eclipse:
WritableCollectionProperty collectionProperty = (WritableCollectionProperty) property;
WritableProperty valueProperty = ((WritableCollectionProperty) property).getValueProperty();
I finally loose my last trust in Java and Eclipse.
New bug in javac:
Error was (saving as the github action logs will disappear after some time):
Error: Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.11.0:compile (default-compile) on project mmm-property: Compilation failure: Compilation failure:
Error: /home/runner/work/property/property/core/src/main/java/io/github/mmm/property/factory/PropertyFactoryManager.java:[88,48] error: incompatible types: inference variable P#1 has incompatible bounds
Error: equality constraints: P#2
Error: lower bounds: WritableProperty<V#1>,ReadableProperty<V#1>
Error: where P#1,V#1,P#2,V#2 are type-variables:
Error: P#1 extends ReadableProperty<V#1> declared in method <V#1,P#1>getRequiredFactory(Class<P#1>,Class<V#1>)
Error: V#1 extends Object declared in method <V#1,P#1>getRequiredFactory(Class<P#1>,Class<V#1>)
Error: P#2 extends ReadableProperty<V#2> declared in method <V#2,P#2>create(Class<P#2>,Class<V#2>,String,PropertyMetadata<V#2>)
Error: V#2 extends Object declared in method <V#2,P#2>create(Class<P#2>,Class<V#2>,String,PropertyMetadata<V#2>)
Error: /home/runner/work/property/property/core/src/main/java/io/github/mmm/property/factory/PropertyFactoryManager.java:[118,48] error: incompatible types: inference variable P#1 has incompatible bounds
Error: equality constraints: P#2
Error: lower bounds: WritableProperty<V#1>,ReadableProperty<V#1>
Error: where P#1,V#1,P#2,V#2 are type-variables:
Error: P#1 extends ReadableProperty<V#1> declared in method <V#1,P#1>getRequiredFactory(Class<P#1>,Class<V#1>)
Error: V#1 extends Object declared in method <V#1,P#1>getRequiredFactory(Class<P#1>,Class<V#1>)
Error: P#2 extends ReadableProperty<V#2> declared in method <V#2,P#2>create(Class<P#2>,PropertyTypeInfo<V#2>,String,PropertyMetadata<V#2>)
Error: V#2 extends Object declared in method <V#2,P#2>create(Class<P#2>,PropertyTypeInfo<V#2>,String,PropertyMetadata<V#2>)
Error: /home/runner/work/property/property/core/src/main/java/io/github/mmm/property/factory/PropertyFactoryManager.java:[159,41] error: incompatible types: inference variable P#1 has incompatible bounds
Error: equality constraints: P#2
Error: lower bounds: WritableProperty<V#1>,ReadableProperty<V#1>
Error: where P#1,V#1,P#2,V#2 are type-variables:
Error: P#1 extends ReadableProperty<V#1> declared in method <V#1,P#1>getFactoryForPropertyType(Class<P#1>)
Error: V#1 extends Object declared in method <V#1,P#1>getFactoryForPropertyType(Class<P#1>)
Error: P#2 extends ReadableProperty<V#2> declared in method <V#2,P#2>getFactory(Class<P#2>,Class<V#2>)
Error: V#2 extends Object declared in method <V#2,P#2>getFactory(Class<P#2>,Class<V#2>)
Error: -> [Help 1]
We will review your report and have assigned it an internal review ID : 9076190. Depending upon the completeness of the report and our ability to reproduce the problem, either a new bug will be posted, or we will contact you for further information.
~This time I learned from the past experience that bugreport.java.com
is a big black hole and printed my bug report to a PDF file before I hit submit (filename JavacGenericBug.pdf
just in case I forget this and need to remember in a year or so).~
Bug is visible here:
https://bugs.java.com/bugdatabase/view_bug?bug_id=JDK-8319461
I already responded that most likely they have tested master
where the build obviously succeeds and one needs to do git checkout 54a637fbcac3fd06553e586e5c6a597e085cc6da
before compiling the code.
Tonight I have another jackpot. So here is the next javac bug:
https://github.com/m-m-m/entity/actions/runs/6751215450/job/18354915136
Error: /home/runner/work/entity/entity/bean/src/main/java/io/github/mmm/entity/bean/repository/AbstractEntityRepository.java:[40,35] error: method getClass in class Object cannot be applied to given types;
required: no arguments
found: E
reason: actual and formal argument lists differ in length
where E is a type-variable:
E extends EntityBean declared in class AbstractEntityRepository
From code:
public abstract class AbstractEntityRepository<E extends EntityBean> implements EntityRepository<E> {
...
public AbstractEntityRepository(E prototype, IdGenerator idGenerator) {
super();
this.prototype = prototype;
this.entityClass = ReadableBean.getClass(this.prototype);
* this is where the error is reported
And the called method has this signature:
static <B extends ReadableBean> Class<B> getClass(B bean) {
And for completeness:
public interface EntityBean extends WritableBean, Entity {
and
public interface WritableBean extends ReadableBean, WritablePath, MarshallingObject {
So EntityBean
is obviously a ReadableBean
and therefore the call of ReadableBean.getClass(this.prototype)
is valid.
The javac
should realize that E
can be filled into B
and therefore also the assignment is valid.
I fully understand that implementing a Java compiler supporting what has been specified with Java generics using upper and lower bounds is complex as hell. I once did implement code to resolve an arbitrary Type
with its owner Class
to the according raw class resolving type variables via reflection. Getting this correct was a nightmare and is just a small part of what javac has to solve. So the implementers deserve my respect.
However, what makes me wonder is why there are so many gaps in the TCK of javac that I must find all these bugs though quite some years have passed since Java 5 alias tiger was introducing generics.
Indeed the workaround was to rename my method from getClass(ReadableBean)
to getJavaClass(ReadableBean)
.
See the associated commits - this made the build green:
https://github.com/m-m-m/entity/actions/runs/6751318771
I was not aware that getClass
is a reserved keyword for javac - what a joke.
So downgrade for my API but whatever it takes to make it work and proceed with my visions...
p.s. I am too tired to report this again officially via bugreport.java.com. My hope was always that people who really care about Java might find this issue interesting and would pro-actively work on resolving these generic issues but this seems to be a scattered dream...
For the record - not javac but javadoc also has buggy differences between JDK and Eclipse: https://github.com/m-m-m/ui-api/blob/ec460770fc26eb6c47b4235596f6a4b8802cbf4c/menu/src/main/java/io/github/mmm/ui/api/widget/menu/UiAbstractMenuWithItems.java#L32
JavaDoc produces this warning:
[WARNING] Javadoc Warnings
[WARNING] D:\projects\mmm\workspaces\main\ui-api\menu\src\main\java\io\github\mmm\ui\api\widget\menu\UiAbstractMenuWithItems.java:32: warning: reference not found: #getChildIndex(UiAbstractMenuEntry)
[WARNING] * @param index is the {@link #getChildIndex(UiAbstractMenuEntry) index} where to insert the new {@link UiMenuItem}.
[WARNING] ^
However, Eclipse produces this reference syntax via auto-suggestion. If I manually change it to what JavaDoc seems to expect (#getChildIndex(UiWidget)
), I get this warning in Eclipse:
Javadoc: The method getChildIndex(UiAbstractMenuEntry) in the type UiComposite<UiAbstractMenuEntry> is not applicable for the arguments (UiWidget)
So who is right and who is wrong? For me the behavior of Eclipse makes sense and feels natural. Why should method references in JavaDoc differ from the references in the code? If I write this code inside the method body:
UiWidget widge = null;
getChildIndex(widget);
I get a compile error from javac because the method getChildIndex
requires a parameter of type UiAbstractMenuEntry
so why should I have to write #getChildIndex(UiWidget)
?
I would consider this a bug in the JavaDoc linter.
I have isolated the bug https://bugs.java.com/bugdatabase/view_bug?bug_id=JDK-8319461 into a single class:
public class JDK8319461 {
public <V, P extends ReadableProperty<V>> P create(Class<P> propertyType, Class<V> valueClass, String name) {
Factory factory = getFactory(propertyType, valueClass);
return null;
}
public <V, P extends ReadableProperty<V>> Factory<V, ? extends P> getFactory(Class<P> propertyType, Class<V> valueType) {
Factory<V, ? extends P> factory = null;
return factory;
}
public interface ReadableProperty<V> { }
public interface WritableProperty<V> extends ReadableProperty<V> { }
public interface Factory<V, P extends WritableProperty<V>> { }
}
I resent this to the oracle bug database so lets see if they still keep this bug closed without fixing anything.
To me the behavior of javac
makes no sense. If I can properly declare the method "getFactory" why should I be disallowed to actually call it then with the according signature?
Another insane bug in Eclipse:
WritableCollectionProperty collectionProperty = (WritableCollectionProperty) property; WritableProperty valueProperty = property.getValueProperty();
The above code gives me a compiler error:
The method getValueProperty() is undefined for the type WritableProperty<capture#15-of ?>
However the following cast lets the error go away in Eclipse:WritableCollectionProperty collectionProperty = (WritableCollectionProperty) property; WritableProperty valueProperty = ((WritableCollectionProperty) property).getValueProperty();
I finally loose my last trust in Java and Eclipse.
Could you isolate this into a standalone snippet?
Tonight I have another jackpot. So here is the next javac bug:
- https://github.com/m-m-m/entity/actions/runs/6751215450/job/18354915136
Error: /home/runner/work/entity/entity/bean/src/main/java/io/github/mmm/entity/bean/repository/AbstractEntityRepository.java:[40,35] error: method getClass in class Object cannot be applied to given types; required: no arguments found: E reason: actual and formal argument lists differ in length where E is a type-variable: E extends EntityBean declared in class AbstractEntityRepository
From code:
public abstract class AbstractEntityRepository<E extends EntityBean> implements EntityRepository<E> { ... public AbstractEntityRepository(E prototype, IdGenerator idGenerator) { super(); this.prototype = prototype; this.entityClass = ReadableBean.getClass(this.prototype); * this is where the error is reported
And the called method has this signature:
static <B extends ReadableBean> Class<B> getClass(B bean) {
I constructed the following extract from your snippets:
interface ReadableBean {}
interface WritableBean extends ReadableBean {}
interface Entity {}
interface EntityBean extends WritableBean, Entity {}
interface EntityRepository <E> {}
public abstract class AbstractEntityRepository<E extends EntityBean> implements EntityRepository<E> {
static <B extends ReadableBean> Class<B> getClass(B bean) { return null; }
public AbstractEntityRepository(E prototype) {
Class<?> entityClass = ReadableBean.getClass(prototype);
}
}
To me it looks like ReadableBean.getClass(this.prototype);
is a typo. In my reduced version saying getClass(this.prototype)
compiles fine in all compilers I tested. Am I missing anything?
To me it looks like ReadableBean.getClass(this.prototype); is a typo. In my reduced version saying getClass(this.prototype) compiles fine in all compilers I tested. Am I missing anything?
Yes, here you are missing something. This is indeed a bug in javac not in EJC and it was fixed with these commits:
If you want you can still follow the link I left pointing the the build from the github action and just by renaming the method from getClass
to getJavaClass
fixed the build. Before I got this from javac while EJC worked fine:
Error: /home/runner/work/entity/entity/bean/src/main/java/io/github/mmm/entity/bean/repository/AbstractEntityRepository.java:[40,35] error: method getClass in class Object cannot be applied to given types;
required: no arguments
found: E
reason: actual and formal argument lists differ in length
where E is a type-variable:
E extends EntityBean declared in class AbstractEntityRepository
So there seems to be a build-in magic in javac for the Object.getClass()
method that collides with custom getClass(...)
methods.
Thanks for investigating this issue deeper. IMHO from Eclipse PoV you are wasting your time if you also investigate problems I noted for javac
but if you happen to have good connections to Oracle javac developers and want to make use of that, feel more than welcome to keep going :)
Could you isolate this into a standalone snippet?
I will try it but will take me some weeks since that will require going back in the git history in multiple projects and investing several hours after I can get the bug somehow reproduced in Eclipse...
To me it looks like ReadableBean.getClass(this.prototype); is a typo. In my reduced version saying getClass(this.prototype) compiles fine in all compilers I tested. Am I missing anything?
Yes, here you are missing something.
I was missing several things :smile: - first I missed that the method was declared in ReadableBean, indeed. As I placed it into AbstractEntityRepository (see above) I figured that prefixing the method call with ReadableBean.
might be a typo on your side.
Now with that corrected it still compiles with all compilers I tried:
interface ReadableBean {
static <B extends ReadableBean> Class<B> getClass(B bean) { return null; }
}
interface WritableBean extends ReadableBean {}
interface Entity {}
interface EntityBean extends WritableBean, Entity {}
interface EntityRepository <E> {}
public abstract class AbstractEntityRepository<E extends EntityBean> implements EntityRepository<E> {
public AbstractEntityRepository(E prototype) {
Class<?> entityClass = ReadableBean.getClass(prototype);
}
}
So there most be more to it than you showed in the comment above.
In fact as long as overload resolution is involved, I wouldn't be sure about anything until I observed and analyzed the bug. There are indeed situations where overload resolution is forced to pick one method before even checking if that method is applicable (I recall one such situations where a static import was used, there may be others).
I found another nice one in EJC:
According to my understanding of the Java generic type system I can declare a generic type using only wildcards anytime as long as I provide exactly the correct number of wildcards (?
) as the actual type has generic types declared. There is no compiler check required in that case (maybe only inference) but the declaration itself can then never be incorrect as otherwise already the type itself would have been declared incorrect. All I am saying with the wildcard (?
) is that I do not care about the generic type. I would actually omit the wildcards in such case but for some odd reason the Java spec of generic types is messy and type inference will not happen in such case and I do get Object
even for types that do have an upper bound other than Object
.
I found another nice one in EJC:
If you can provide a minimal reproducer feel free to report this against jdt.
I found another nice one in EJC:
If you can provide a minimal reproducer feel free to report this against jdt.
I tried but due to magic the error also disappeared in its original place and I cannot reproduce it. So maybe some in-determinism or temporary issue but as it seems to real EJC bug I found...
I tried but due to magic the error also disappeared in its original place and I cannot reproduce it.
The bug got scared of the prospect of meeting me :smiling_imp:
With util-property we hit a bug in Oracle java compiler:
We have the interface
BooleanProperty
that is combining a generic methodsetValue(VALUE)
withVALUE
bound toBoolean
with a typed methodsetValue(Boolean)
. AlsoBooleanProperty
has a default method that is callingsetValue
with aBoolean
argument. This is causing the following error in oracle java compiler (invoked via maven):My environment:
The error does not occur with Eclipse java compiler and I am quite sure that it is actually a bug in Oracle java compiler.