Fixed Bug 521818: [compiler] LambdaConversionException when using a
method reference for a generic SAM with intersection types as argument
Change-Id: I45bf1a6fda91be27a06d6a10d4b7126d22146dfd
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/LambdaExpressionsTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/LambdaExpressionsTest.java
index a3b21b8..9f5def5 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/LambdaExpressionsTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/LambdaExpressionsTest.java
@@ -6597,6 +6597,50 @@
}
);
}
+public void testBug521818() {
+ runConformTest(
+ new String[] {
+ "test/Main.java",
+ "package test;\n" +
+ "class C {}\n" +
+ "class D {\n" +
+ " <T extends C & Runnable> D(int i, T t) {" +
+ " System.out.println(\"D\");\n" +
+ "}\n" +
+ "}\n" +
+ "interface Goo {\n" +
+ " <T extends C & Runnable> String m(T p);\n" +
+ "}\n" +
+ "class A {\n" +
+ " public static <K extends Runnable> String bar(K a) {\n" +
+ " System.out.println(\"Bar\");\n" +
+ " return null;\n" +
+ " }\n" +
+ " public static <K extends Runnable> D baz(int i, K a) {\n" +
+ " System.out.println(\"Baz\");\n" +
+ " return null;\n" +
+ " }\n"+
+ "}\n" +
+ "interface Foo<Z extends C & Runnable> {\n" +
+ " D get(int i, Z z);\n" +
+ "}\n" +
+ "public class Main {\n" +
+ " public static void main(String[] args) {\n" +
+ " Foo<? extends C> h = A::baz;\n" +
+ " h.get(0, null);\n" +
+ " Foo<? extends C> h2 = D::new;\n" +
+ " h2.get(0, null);\n" +
+ " Goo g = A::bar;\n" +
+ " g.m(null);\n" +
+ " } \n" +
+ "}"
+ },
+ "Baz\n" +
+ "D\n" +
+ "Bar"
+
+ );
+}
public static Class testClass() {
return LambdaExpressionsTest.class;
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReferenceExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReferenceExpression.java
index 063ed9b..dec5751 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReferenceExpression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReferenceExpression.java
@@ -88,6 +88,7 @@
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
+import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding;
import org.eclipse.jdt.internal.compiler.parser.Parser;
import org.eclipse.jdt.internal.compiler.parser.Scanner;
@@ -271,10 +272,38 @@
// these cases are either too complicated, impossible to handle or result in significant code duplication
return (this.binding.isVarargs() ||
(isConstructorReference() && this.receiverType.syntheticOuterLocalVariables() != null && this.shouldCaptureInstance) ||
- this.requiresBridges()); // bridges.
+ this.requiresBridges() || // bridges.
+ !isDirectCodeGenPossible());
// To fix: We should opt for direct code generation wherever possible.
}
-
+ private boolean isDirectCodeGenPossible() {
+ if (this.binding != null) {
+ if (isMethodReference() && this.syntheticAccessor == null) {
+ if (TypeBinding.notEquals(this.binding.declaringClass, this.lhs.resolvedType.erasure())) {
+ // reference to a method declared by an inaccessible type accessed via a
+ // subtype - normally a bridge method would be present to facilitate
+ // this access, unless the method is final, in which case, direct access to
+ // the method is not possible, an implicit lambda is needed
+ if (!this.binding.declaringClass.canBeSeenBy(this.enclosingScope)) {
+ return !this.binding.isFinal();
+ }
+ }
+ }
+ TypeBinding[] descriptorParams = this.descriptor.parameters;
+ TypeBinding[] origParams = this.binding.original().parameters;
+ TypeBinding[] origDescParams = this.descriptor.original().parameters;
+ int offset = this.receiverPrecedesParameters ? 1 : 0;
+ for (int i = 0; i < descriptorParams.length - offset; i++) {
+ TypeBinding descType = descriptorParams[i + offset];
+ TypeBinding origDescType = origDescParams[i + offset];
+ if (descType.isIntersectionType18() ||
+ (descType.isTypeVariable() && ((TypeVariableBinding) descType).otherUpperBounds() != null)) {
+ return CharOperation.equals(origDescType.signature(), origParams[i].signature());
+ }
+ }
+ }
+ return true;
+ }
public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) {
this.actualMethodBinding = this.binding; // grab before synthetics come into play.
// Handle some special cases up front and transform them into implicit lambdas.