Bug 546161 - LambdaConversionException due to invalid instantiated method type argument to LambdaMetafactory::metafactory
Summary: LambdaConversionException due to invalid instantiated method type argument to...
Status: CLOSED MOVED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 4.11   Edit
Hardware: PC All
: P3 normal with 6 votes (vote)
Target Milestone: ---   Edit
Assignee: Srikanth Sankaran CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2019-04-05 11:31 EDT by Jorn Vernee CLA
Modified: 2023-06-12 06:22 EDT (History)
5 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Jorn Vernee CLA 2019-04-05 11:31:59 EDT
Code to reproduce:

```
public class Main {
    public static void main(String[] args) {
    	((Widget<?>) new Widget<>()).addListener(evt -> {});
    }
}

class Widget<E extends CharSequence> {
    void addListener(Listener<? super E> listener) {}
}

interface Listener<E extends CharSequence> {
    void m(E event);
}
```

Which throws:

```
Exception in thread "main" java.lang.BootstrapMethodError: bootstrap method initialization exception
	at java.base/java.lang.invoke.BootstrapMethodInvoker.invoke(BootstrapMethodInvoker.java:194)
	at java.base/java.lang.invoke.CallSite.makeSite(CallSite.java:307)
	at java.base/java.lang.invoke.MethodHandleNatives.linkCallSiteImpl(MethodHandleNatives.java:258)
	at java.base/java.lang.invoke.MethodHandleNatives.linkCallSite(MethodHandleNatives.java:248)
	at test/main.Main.main(Main.java:5)
Caused by: java.lang.invoke.LambdaConversionException: Type mismatch for instantiated parameter 0: class java.lang.Object is not a subtype of interface java.lang.CharSequence
	at java.base/java.lang.invoke.AbstractValidatingLambdaMetafactory.checkDescriptor(AbstractValidatingLambdaMetafactory.java:308)
	at java.base/java.lang.invoke.AbstractValidatingLambdaMetafactory.validateMetafactoryArgs(AbstractValidatingLambdaMetafactory.java:294)
	at java.base/java.lang.invoke.LambdaMetafactory.metafactory(LambdaMetafactory.java:328)
	at java.base/java.lang.invoke.BootstrapMethodInvoker.invoke(BootstrapMethodInvoker.java:127)
	... 4 more

```

inspecting `javap -v Main` shows following BSM entry:

```
BootstrapMethods:
  0: #43 REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
    Method arguments:
      #45 (Ljava/lang/CharSequence;)V
      #48 REF_invokeStatic main/Main.lambda$0:(Ljava/lang/Object;)V
      #49 (Ljava/lang/Object;)V
```

Where `(Ljava/lang/Object;)V` is rejected by LambdaMetafactory::metafactory code since http://hg.openjdk.java.net/jdk/jdk/diff/ee6b5bd26bf9/jdk/src/java.base/share/classes/java/lang/invoke/AbstractValidatingLambdaMetafactory.java

According to the documentation of the LambdametaFactory::metafactory, the third (non-default) argument:

"instantiatedMethodType - The signature and return type that should be enforced dynamically at invocation time. This may be the same as samMethodType, or may be a specialization of it."

Although I don't see "specialization" defined, evidently `(Ljava/lang/Object;)V` is not valid as a specialization of `(Ljava/lang/CharSequence;)V`.

javac generates `(Ljava/lang/CharSequence;)V` as the third argument of the BSM, which is also the erasure of `Listener::m`.

Eclipse seems to be in the wrong here. See also discussion on Stack Overflow: https://stackoverflow.com/q/55532055
Comment 1 Steve Liedtke CLA 2019-08-26 03:02:37 EDT
We are also seeing this on Ubuntu 18.04 with Oracle Java 12.0.2
Comment 2 Patrick Schmidt CLA 2021-08-12 09:51:14 EDT
Still experiencing this with the latest JDT (3.18.800.v20210611-1600).
Comment 3 Patrick Schmidt CLA 2021-10-13 05:08:32 EDT
It's somewhat frustrating that this doesn't event get acknowledged. This bug means that affected code compiled in Eclipse will not run in Java 11 or later.
While this shouldn't affect actual builds, it means that if you want to run your code during development you have to add a cast to every affected call, basically polluting your code.
BTW, this also affects VSCode since they also use the Eclipse Compiler.
Comment 4 Andrey Loskutov CLA 2021-10-13 05:59:33 EDT
(In reply to Patrick Schmidt from comment #3)
> It's somewhat frustrating that this doesn't event get acknowledged.

Now it is :-) Still fails on 4.22 master :-(

Slightly changed snippet compiles and runs without issues (note the missing cast):

public class Main {
    public static void main(String[] args) {
    	new Widget<>().addListener(evt -> {});
    }
}
class Widget<E extends CharSequence> {
    void addListener(Listener<? super E> listener) {}
}
interface Listener<E extends CharSequence> {
    void m(E event);
}
Comment 5 Patrick Schmidt CLA 2021-10-13 08:20:49 EDT
Maybe that snippet wasn't the best choice to illustrate the bug, if the cast has to be added in order for it to fail, instead of the other way around :) 

The answer of the linked Stackoverflow question provides a better example in that respect:

```
public class Main {    
    public static void main(String[] args) {
        getHasValue().addValueChangeListener(evt -> {});
    }

    public static HasValue<?, ?> getHasValue() {
        return null;
    }    
}

interface HasValue<E extends HasValue.ValueChangeEvent<V>,V> {    
    public static interface ValueChangeEvent<V> {}    
    public static interface ValueChangeListener<E extends HasValue.ValueChangeEvent<?>> {
        void valueChanged(E event);
    }    
    void addValueChangeListener(HasValue.ValueChangeListener<? super E> listener);
}
```

Adding the cast (ValueChangeListener<ValueChangeEvent<?>>) to evt will fix the issue.
Comment 6 Srikanth Sankaran CLA 2023-06-12 06:22:09 EDT
The proposed PR at https://github.com/eclipse-jdt/eclipse.jdt.core/pull/1082 fixes this problem and I have pushed the tests from here to that PR.