Bug 541364 - The Eclipse Java Compiler produces wrong code which causes IncompatibleClassChangeError
Summary: The Eclipse Java Compiler produces wrong code which causes IncompatibleClassC...
Status: NEW
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 4.9   Edit
Hardware: PC Windows 10
: P3 major (vote)
Target Milestone: ---   Edit
Assignee: JDT-Core-Inbox CLA
QA Contact:
URL:
Whiteboard: stalebug bulk move
Keywords: regression
Depends on:
Blocks:
 
Reported: 2018-11-20 06:43 EST by Dmitry -- CLA
Modified: 2022-06-17 19:43 EDT (History)
2 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Dmitry -- CLA 2018-11-20 06:43:12 EST
I have found a situation when ejc produces wrong code which cases IncompatibleClassChangeError when run. I've create a test to reproduce it: https://bitbucket.org/M_A_K/eclipse-wrong-interface-bug/src

Steps to Reproduce:
0. You should have maven 3.3.9 or higher + JDK 8.
1. Download or pull source code into a folder.
2. Run: "mvn clean package" in the folder.

The test fails with stacktrace like this:
Running mak.test.eclipse.MyServiceTest
Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.114 sec <<< FAILURE!
testMyMethod(mak.test.eclipse.MyServiceTest)  Time elapsed: 0.051 sec  <<< ERROR!
java.lang.IncompatibleClassChangeError: Found interface mak.test.eclipse.entities.MyEntity, but class was expected
        at mak.test.eclipse.MyService.lambda$1(MyService.java:36)
        at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
        at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1382)
        at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)
        at java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:270)
        at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
        at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
        at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
        at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
        at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
        at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
        at mak.test.eclipse.MyService.myMethod(MyService.java:38)
        at mak.test.eclipse.MyServiceTest.testMyMethod(MyServiceTest.java:17)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
        at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
        at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
        at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
        at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
        at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
        at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
        at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
        at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
        at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
        at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
        at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
        at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
        at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
        at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:252)
        at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:141)
        at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:112)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:189)
        at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:165)
        at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:85)
        at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:115)
        at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:75)

You can check that javac produces correct code. Just run "mvn -Pjavac clean package" (This profile uses javac to compile instead of ejc.). The test runs without errors.
Comment 1 Dmitry -- CLA 2018-11-20 07:02:07 EST
Workaround: don't trust the compiler, point the type yourself

Replace

    final List<String> result = Stream.of(collection1.stream(),

for

    final List<String> result = Stream.<Stream<? extends MyEntity>>of(collection1.stream(),

Ejc produces correct code after it.
Comment 2 Stephan Herrmann CLA 2018-11-20 09:47:09 EST
Thanks for the repro.

Ecj versions up-to 3.14.0 produce working code, the problem can be reproduced starting with 3.15.0 (aka 4.9)

From this line
final MyInnerService<DomainObject<?>> toService = convert1(item.getTypeName());

3.14.0 produces
...
0: aload_0
1: aload_1
2: invokeinterface #108,  1          // InterfaceMethod mak/test/eclipse/entities/MyEntity.getTypeName:()Ljava/lang/String;
7: invokevirtual #114                // Method convert1:(Ljava/lang/String;)Lmak/test/eclipse/MyService$MyInnerService;
...

while 3.15.0 produces
...
0: aload_0
1: aload_1
2: invokevirtual #108                // Method mak/test/eclipse/entities/MyEntity.getTypeName:()Ljava/lang/String;
5: invokevirtual #114                // Method convert1:(Ljava/lang/String;)Lmak/test/eclipse/MyService$MyInnerService;
...

While there is absolutely no reason to think that MyEntity is a class, I could imagine that we internally use some ill-formed intersection type, which pretends to be a class but produces MyEntity as its erasure?
Comment 3 Manoj N Palat CLA 2019-02-11 03:59:28 EST
Bulk move out of 4.11
Comment 4 Manoj N Palat CLA 2019-08-27 02:05:57 EDT
Bulk move out of 4.13
Comment 5 Eclipse Genie CLA 2022-06-17 19:43:18 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.