Bug 413958 - Function override returning inherited Generic Type
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest.java
index 87c8d60..bcc0eb7 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest.java
@@ -13,6 +13,7 @@
* bug 401456 - Code compiles from javac/intellij, but fails from eclipse
* bug 405706 - Eclipse compiler fails to give compiler error when return type is a inferred generic
* Bug 408441 - Type mismatch using Arrays.asList with 3 or more implementations of an interface with the interface type as the last parameter
+ * Bug 413958 - Function override returning inherited Generic Type
*******************************************************************************/
package org.eclipse.jdt.core.tests.compiler.regression;
@@ -33,8 +34,8 @@
// Static initializer to specify tests subset using TESTS_* static variables
// All specified tests which does not belong to the class are skipped...
static {
-// TESTS_NAMES = new String[] { "testBug408441" };
- TESTS_NAMES = new String[] { "test338350" };
+// TESTS_NAMES = new String[] { "testBug405706" };
+// TESTS_NAMES = new String[] { "testBug413958" };
// TESTS_NUMBERS = new int[] { 1465 };
// TESTS_RANGE = new int[] { 1097, -1 };
}
@@ -3123,4 +3124,146 @@
"}\n"
});
}
+
+// https://bugs.eclipse.org/413958 - Function override returning inherited Generic Type
+public void testBug413958_1() {
+ runConformTest(
+ new String[] {
+ "TestA.java",
+ "public class TestA { }\n",
+ "TestB.java",
+ "public class TestB { }\n",
+ "ReadOnlyWrapper.java",
+ "@SuppressWarnings(\"unchecked\")\n" +
+ "public class ReadOnlyWrapper<A extends TestA, B extends TestB> {\n" +
+ " protected A a;\n" +
+ " protected B b;\n" +
+ " public ReadOnlyWrapper(A ax,B bx){\n" +
+ " this.a = ax;\n" +
+ " this.b = bx;\n" +
+ " }\n" +
+ " public <X extends ReadOnlyWrapper<A,B>> X copy() {\n" +
+ " return (X) new ReadOnlyWrapper<A,B>(a,b);\n" +
+ " }\n" +
+ " public <TA extends TestA,TB extends TestB,X extends ReadOnlyWrapper<TA,TB>> X icopy() {\n" +
+ " return (X) new ReadOnlyWrapper<A,B>(a,b);\n" +
+ " }\n" +
+ " public A getA() {\n" +
+ " return this.a;\n" +
+ " }\n" +
+ " public B getB() {\n" +
+ " return this.b;\n" +
+ " }\n" +
+ "}",
+ "WritableWrapper.java",
+ "@SuppressWarnings(\"unchecked\")\n" +
+ "public class WritableWrapper<A extends TestA, B extends TestB> extends ReadOnlyWrapper<A, B> {\n" +
+ " public WritableWrapper(A ax,B bx){\n" +
+ " super(ax,bx);\n" +
+ " }\n" +
+ " @Override\n" +
+ " public <X extends ReadOnlyWrapper<A,B>> X copy() {\n" +
+ " return (X) new WritableWrapper<A, B>(a,b);\n" +
+ " }\n" +
+ " @Override\n" +
+ " public <TA extends TestA,TB extends TestB,X extends ReadOnlyWrapper<TA,TB>> X icopy() {\n" +
+ " // Works in Indigo, Fails in Kepler\n" +
+ " return (X) new WritableWrapper<A,B>(a,b);\n" +
+ " }\n" +
+ " public void setA(A ax) {\n" +
+ " this.a = ax;\n" +
+ " }\n" +
+ " public void setB(B bx) {\n" +
+ " this.b = bx;\n" +
+ " }\n" +
+ "}\n",
+ "TestGenerics.java",
+ "public class TestGenerics {\n" +
+ " public static void main(String [] args) {\n" +
+ " final WritableWrapper<TestA, TestB> v1 = new WritableWrapper<TestA, TestB>(new TestA(), new TestB());\n" +
+ " final WritableWrapper<TestA,TestB> v2 = v1.copy();\n" +
+ " final WritableWrapper<TestA,TestB> v3 = v1.icopy();\n" +
+ " }\n" +
+ "}\n"
+ });
+}
+// https://bugs.eclipse.org/413958 - Function override returning inherited Generic Type
+// variation showing different inference with / without a method parameter
+public void testBug413958_2() {
+ runNegativeTest(
+ new String[] {
+ "TestA.java",
+ "public class TestA { }\n",
+ "TestB.java",
+ "public class TestB { }\n",
+ "TestA2.java",
+ "public class TestA2 extends TestA { }\n",
+ "ReadOnlyWrapper.java",
+ "@SuppressWarnings(\"unchecked\")\n" +
+ "public class ReadOnlyWrapper<A extends TestA, B extends TestB> {\n" +
+ " protected A a;\n" +
+ " protected B b;\n" +
+ " public ReadOnlyWrapper(A ax,B bx){\n" +
+ " this.a = ax;\n" +
+ " this.b = bx;\n" +
+ " }\n" +
+ " public <X extends ReadOnlyWrapper<A,B>> X copy() {\n" +
+ " return (X) new ReadOnlyWrapper<A,B>(a,b);\n" +
+ " }\n" +
+ " public <TA extends TestA,TB extends TestB,X extends ReadOnlyWrapper<TA,TB>> X icopy() {\n" +
+ " return (X) new ReadOnlyWrapper<A,B>(a,b);\n" +
+ " }\n" +
+ " public <TA extends TestA,TB extends TestB,X extends ReadOnlyWrapper<TA,TB>> X icopy2(TA in) {\n" +
+ " return (X) new ReadOnlyWrapper<A,B>(a,b);\n" +
+ " }\n" +
+ " public A getA() {\n" +
+ " return this.a;\n" +
+ " }\n" +
+ " public B getB() {\n" +
+ " return this.b;\n" +
+ " }\n" +
+ "}",
+ "WritableWrapper.java",
+ "@SuppressWarnings(\"unchecked\")\n" +
+ "public class WritableWrapper<A extends TestA, B extends TestB> extends ReadOnlyWrapper<A, B> {\n" +
+ " public WritableWrapper(A ax,B bx){\n" +
+ " super(ax,bx);\n" +
+ " }\n" +
+ " @Override\n" +
+ " public <X extends ReadOnlyWrapper<A,B>> X copy() {\n" +
+ " return (X) new WritableWrapper<A, B>(a,b);\n" +
+ " }\n" +
+ " @Override\n" +
+ " public <TA extends TestA,TB extends TestB,X extends ReadOnlyWrapper<TA,TB>> X icopy() {\n" +
+ " return (X) new WritableWrapper<A,B>(a,b);\n" +
+ " }\n" +
+ " @Override\n" +
+ " public <TA extends TestA,TB extends TestB,X extends ReadOnlyWrapper<TA,TB>> X icopy2(TA in) {\n" +
+ " return (X) new WritableWrapper<A,B>(a,b);\n" +
+ " }\n" +
+ " public void setA(A ax) {\n" +
+ " this.a = ax;\n" +
+ " }\n" +
+ " public void setB(B bx) {\n" +
+ " this.b = bx;\n" +
+ " }\n" +
+ "}\n",
+ "TestGenerics.java",
+ "public class TestGenerics {\n" +
+ " public static void main(String [] args) {\n" +
+ " final WritableWrapper<TestA, TestB> v1 = new WritableWrapper<TestA, TestB>(new TestA(), new TestB());\n" +
+ " final WritableWrapper<TestA,TestB> v2 = v1.copy();\n" +
+ " final WritableWrapper<TestA,TestB> v3 = v1.icopy();\n" +
+ " final WritableWrapper<TestA2,TestB> v4 = v1.icopy();\n" +
+ " final WritableWrapper<TestA2,TestB> v5 = v1.icopy2(new TestA2());\n" +
+ " }\n" +
+ "}\n"
+ },
+ "----------\n" +
+ "1. ERROR in TestGenerics.java (at line 6)\n" +
+ " final WritableWrapper<TestA2,TestB> v4 = v1.icopy();\n" +
+ " ^^^^^^^^^^\n" +
+ "Type mismatch: cannot convert from ReadOnlyWrapper<TestA,TestB> to WritableWrapper<TestA2,TestB>\n" +
+ "----------\n");
+}
}
\ No newline at end of file
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedGenericMethodBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedGenericMethodBinding.java
index 9ca94f4..e67f45f 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedGenericMethodBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedGenericMethodBinding.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2012 IBM Corporation and others.
+ * Copyright (c) 2000, 2013 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -10,6 +10,7 @@
* Stephan Herrmann - Contributions for
* bug 186342 - [compiler][null] Using annotations for null checking
* bug 395002 - Self bound generic class doesn't resolve bounds properly for wildcards for certain parametrisation.
+ * bug 413958 - Function override returning inherited Generic Type
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;
@@ -261,7 +262,7 @@
if (substitute != null) continue nextTypeParameter; // already inferred previously
TypeBinding [] bounds = inferenceContext.getSubstitutes(current, TypeConstants.CONSTRAINT_EXTENDS);
if (bounds == null) continue nextTypeParameter;
- TypeBinding[] glb = Scope.greaterLowerBound(bounds, scope);
+ TypeBinding[] glb = Scope.greaterLowerBound(bounds, scope, scope.environment());
TypeBinding mostSpecificSubstitute = null;
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=341795 - Per 15.12.2.8, we should fully apply glb
if (glb != null) {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/Scope.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/Scope.java
index dddf2ca..1e91ba9 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/Scope.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/Scope.java
@@ -15,6 +15,7 @@
* bug 401271 - StackOverflowError when searching for a methods references
* bug 405706 - Eclipse compiler fails to give compiler error when return type is a inferred generic
* Bug 408441 - Type mismatch using Arrays.asList with 3 or more implementations of an interface with the interface type as the last parameter
+ * Bug 413958 - Function override returning inherited Generic Type
* Jesper S Moller - Contributions for
* Bug 378674 - "The method can be declared as static" is wrong
*******************************************************************************/
@@ -297,7 +298,7 @@
}
// 5.1.10
- public static TypeBinding[] greaterLowerBound(TypeBinding[] types, /*@Nullable*/ Scope scope) {
+ public static TypeBinding[] greaterLowerBound(TypeBinding[] types, /*@Nullable*/ Scope scope, LookupEnvironment environment) {
if (types == null) return null;
int length = types.length;
if (length == 0) return null;
@@ -319,10 +320,34 @@
} else if (!jType.isCompatibleWith(iType, scope)) {
// avoid creating unsatisfiable intersection types (see https://bugs.eclipse.org/405706):
if (iType.isParameterizedType() && jType.isParameterizedType()) {
- if (iType.original().isCompatibleWith(jType.original(), scope)
- || jType.original().isCompatibleWith(iType.original(), scope))
- {
- // parameterized types are incompatible due to incompatible type arguments => unsatisfiable
+ // if the wider of the two types (judged by originals) has type variables
+ // substitute those with their upper bounds and re-check (see https://bugs.eclipse.org/413958):
+ ParameterizedTypeBinding wideType, narrowType;
+ if (iType.original().isCompatibleWith(jType.original(), scope)) {
+ wideType = (ParameterizedTypeBinding) jType;
+ narrowType = (ParameterizedTypeBinding) iType;
+ } else if (jType.original().isCompatibleWith(iType.original(), scope)) {
+ wideType = (ParameterizedTypeBinding) iType;
+ narrowType = (ParameterizedTypeBinding) jType;
+ } else {
+ continue;
+ }
+ if (wideType.arguments == null)
+ continue; // assume we already have an error here
+ int numTypeArgs = wideType.arguments.length;
+ TypeBinding[] bounds = new TypeBinding[numTypeArgs];
+ for (int k = 0; k < numTypeArgs; k++) {
+ TypeBinding argument = wideType.arguments[k];
+ bounds[k] = argument.isTypeVariable() ? ((TypeVariableBinding)argument).upperBound() : argument;
+ }
+ ReferenceBinding wideOriginal = (ReferenceBinding) wideType.original();
+ TypeBinding substitutedWideType =
+ environment.createParameterizedType(wideOriginal, bounds, wideOriginal.enclosingType());
+ // if the narrow type is compatible with the substituted wide type, we keep silent,
+ // substituting type variables with proper types can still satisfy all constraints,
+ // otherwise ...
+ if (!narrowType.isCompatibleWith(substitutedWideType, scope)) {
+ // ... parameterized types are incompatible due to incompatible type arguments => unsatisfiable
return null;
}
}
@@ -433,7 +458,7 @@
TypeBinding [] bounds = new TypeBinding[1 + substitutedOtherBounds.length];
bounds[0] = substitutedBound;
System.arraycopy(substitutedOtherBounds, 0, bounds, 1, substitutedOtherBounds.length);
- TypeBinding[] glb = Scope.greaterLowerBound(bounds, null); // re-evaluate
+ TypeBinding[] glb = Scope.greaterLowerBound(bounds, null, substitution.environment()); // re-evaluate
if (glb != null && glb != bounds) {
substitutedBound = glb[0];
if (glb.length == 1) {
@@ -3351,7 +3376,7 @@
case Wildcard.SUPER :
// ? super U, ? super V
if (wildU.boundKind == Wildcard.SUPER) {
- TypeBinding[] glb = greaterLowerBound(new TypeBinding[]{wildU.bound,wildV.bound}, this);
+ TypeBinding[] glb = greaterLowerBound(new TypeBinding[]{wildU.bound,wildV.bound}, this, this.environment());
if (glb == null) return null;
return environment().createWildcard(genericType, rank, glb[0], null /*no extra bound*/, Wildcard.SUPER); // TODO (philippe) need to capture entire bounds
}
@@ -3367,7 +3392,7 @@
return environment().createWildcard(genericType, rank, lub, null /*no extra bound*/, Wildcard.EXTENDS);
// U, ? super V
case Wildcard.SUPER :
- TypeBinding[] glb = greaterLowerBound(new TypeBinding[]{u,wildV.bound}, this);
+ TypeBinding[] glb = greaterLowerBound(new TypeBinding[]{u,wildV.bound}, this, this.environment());
if (glb == null) return null;
return environment().createWildcard(genericType, rank, glb[0], null /*no extra bound*/, Wildcard.SUPER); // TODO (philippe) need to capture entire bounds
case Wildcard.UNBOUND :
@@ -3385,7 +3410,7 @@
return environment().createWildcard(genericType, rank, lub, null /*no extra bound*/, Wildcard.EXTENDS);
// U, ? super V
case Wildcard.SUPER :
- TypeBinding[] glb = greaterLowerBound(new TypeBinding[]{wildU.bound, v}, this);
+ TypeBinding[] glb = greaterLowerBound(new TypeBinding[]{wildU.bound, v}, this, this.environment());
if (glb == null) return null;
return environment().createWildcard(genericType, rank, glb[0], null /*no extra bound*/, Wildcard.SUPER); // TODO (philippe) need to capture entire bounds
case Wildcard.UNBOUND :