Bug 521212: [compiler] generic type bound compilation - ecj passes
Change-Id: Ib080877356b8a84d38cbed1d507185769fe2f634
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AbstractRegressionTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AbstractRegressionTest.java
index 288f391..2fcca7e 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AbstractRegressionTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AbstractRegressionTest.java
@@ -196,6 +196,9 @@
if ("1.6.0_10-beta".equals(rawVersion)) { // b24
return 1010;
}
+ if ("1.6.0_45".equals(rawVersion)) {
+ return 1045;
+ }
}
if (version == JavaCore.VERSION_1_7) {
if ("1.7.0-ea".equals(rawVersion)) {
@@ -207,6 +210,9 @@
if ("1.7.0_25".equals(rawVersion)) {
return 2500;
}
+ if ("1.7.0_80".equals(rawVersion)) {
+ return 8000;
+ }
}
if (version == JavaCore.VERSION_1_8) {
if ("1.8.0-ea".equals(rawVersion) || ("1.8.0".equals(rawVersion))) {
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericTypeTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericTypeTest.java
index 94b0540..6b1cc57 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericTypeTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericTypeTest.java
@@ -21875,6 +21875,11 @@
" Store<? extends Key<T>> store1;\n" +
" ^\n" +
"Bound mismatch: The type T is not a valid substitute for the bounded parameter <E extends Key<E>> of the type Key<E>\n" +
+ "----------\n" +
+ "2. ERROR in X.java (at line 6)\n" +
+ " Store<? extends Key<? extends T>> store2;\n" +
+ " ^^^^^^^^^^^\n" +
+ "Bound mismatch: The type ? extends T is not a valid substitute for the bounded parameter <E extends Key<E>> of the type Key<E>\n" +
"----------\n",
// javac options
JavacTestOptions.JavacHasABug.JavacBugFixed_6_10 /* javac test options */);
@@ -40821,9 +40826,8 @@
"");
}
//https://bugs.eclipse.org/bugs/show_bug.cgi?id=179902
-// FIXME javac8 rejects
public void test1167() {
- this.runConformTest(
+ this.runNegativeTest(
new String[] {
"Foo.java",
"public class Foo<F extends Enum<F>> {\n" +
@@ -40832,7 +40836,12 @@
" }\n" +
"}\n", // =================
},
- "");
+ "----------\n" +
+ "1. ERROR in Foo.java (at line 3)\n" +
+ " Bar(Foo<? extends B> bar) {}\n" +
+ " ^^^^^^^^^^^\n" +
+ "Bound mismatch: The type ? extends B is not a valid substitute for the bounded parameter <F extends Enum<F>> of the type Foo<F>\n" +
+ "----------\n");
}
//https://bugs.eclipse.org/bugs/show_bug.cgi?id=169049
public void test1168() {
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 1a014e7..4f53f63 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
@@ -5659,8 +5659,6 @@
// original test case, documenting existing compiler behavior
public void testBug456459a() {
runNegativeTest(
- false /*skipJavac */,
- JavacTestOptions.Excuse.JavacHasErrorsEclipseHasWarnings,
new String[] {
"EnumTest.java",
"import java.util.EnumSet;\n" +
@@ -5682,22 +5680,25 @@
" ^^^^^\n" +
"Class is a raw type. References to generic type Class<T> should be parameterized\n" +
"----------\n" +
- "2. WARNING in EnumTest.java (at line 9)\n" +
+ "2. ERROR in EnumTest.java (at line 9)\n" +
" EnumSet<? extends T> set = EnumSet.allOf(enumType);\n" +
- " ^^^^^^^^^^^^^^^^^^^^^^^\n" +
- "Type safety: Unchecked invocation allOf(Class) of the generic method allOf(Class<E>) of type EnumSet\n" +
+ " ^^^^^^^^^^^\n" +
+ "Bound mismatch: The type ? extends T is not a valid substitute for the bounded parameter <E extends Enum<E>> of the type EnumSet<E>\n" +
"----------\n" +
"3. WARNING in EnumTest.java (at line 9)\n" +
" EnumSet<? extends T> set = EnumSet.allOf(enumType);\n" +
" ^^^^^^^^^^^^^^^^^^^^^^^\n" +
- "Type safety: The expression of type EnumSet needs unchecked conversion to conform to EnumSet<? extends T>\n" +
+ "Type safety: Unchecked invocation allOf(Class) of the generic method allOf(Class<E>) of type EnumSet\n" +
"----------\n" +
"4. WARNING in EnumTest.java (at line 9)\n" +
" EnumSet<? extends T> set = EnumSet.allOf(enumType);\n" +
+ " ^^^^^^^^^^^^^^^^^^^^^^^\n" +
+ "Type safety: The expression of type EnumSet needs unchecked conversion to conform to EnumSet<? extends T>\n" +
+ "----------\n" +
+ "5. WARNING in EnumTest.java (at line 9)\n" +
+ " EnumSet<? extends T> set = EnumSet.allOf(enumType);\n" +
" ^^^^^^^^\n" +
- (this.complianceLevel < ClassFileConstants.JDK1_8
- ? "Type safety: The expression of type Class needs unchecked conversion to conform to Class<T&Enum<T&Enum<E>>>\n"
- : "Type safety: The expression of type Class needs unchecked conversion to conform to Class<Enum<Enum<E>>>\n") +
+ "Type safety: The expression of type Class needs unchecked conversion to conform to Class<Enum<Enum<E>>>\n" +
"----------\n");
}
// simple conflict introduced by additional wildcard bound
@@ -6125,5 +6126,24 @@
}
);
}
+public void testBug521212() {
+ runNegativeTest(
+ new String[] {
+ "X.java",
+ "class Y<U extends Z> {}\n" +
+ "class Z {}\n" +
+ "public class X<T> {\n" +
+ " public static <V> Y<? extends V> one() {\n" +
+ " return null;\n" +
+ " }\n" +
+ "}\n"
+ },
+ "----------\n" +
+ "1. ERROR in X.java (at line 4)\n" +
+ " public static <V> Y<? extends V> one() {\n" +
+ " ^^^^^^^^^^^\n" +
+ "Bound mismatch: The type ? extends V is not a valid substitute for the bounded parameter <U extends Z> of the type Y<U>\n" +
+ "----------\n");
+}
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/CaptureBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/CaptureBinding.java
index bf7a147..3ff29b6 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/CaptureBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/CaptureBinding.java
@@ -242,6 +242,8 @@
if (this.superclass.isSuperclassOf(substitutedVariableSuperclass)) {
this.setSuperClass(substitutedVariableSuperclass);
}
+ // TODO: there are cases were we need to compute glb(capturedWildcardBound, substitutedVariableSuperclass)
+ // but then when glb (perhaps triggered inside setFirstBound()) fails, how to report the error??
}
this.setSuperInterfaces(substitutedVariableInterfaces);
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedTypeBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedTypeBinding.java
index 1a04f46..8243867 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedTypeBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedTypeBinding.java
@@ -115,6 +115,7 @@
boolean hasErrors = false;
TypeVariableBinding[] typeVariables = this.type.typeVariables();
if (this.arguments != null && typeVariables != null) { // arguments may be null in error cases
+ // per JLS 4.5 we should capture 'this'
for (int i = 0, length = typeVariables.length; i < length; i++) {
BoundCheckStatus checkStatus = typeVariables[i].boundCheck(this, this.arguments[i], scope, argumentReferences[i]);
hasErrors |= checkStatus != BoundCheckStatus.OK;
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 8701d39..d54bd44 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
@@ -347,6 +347,9 @@
if (i == j) continue;
ReferenceBinding jType = result[j];
if (jType == null) continue;
+ if (isMalformedPair(iType, jType, null)) {
+ return null;
+ }
if (iType.isCompatibleWith(jType)) { // if Vi <: Vj, Vj is removed
if (result == types) { // defensive copy
System.arraycopy(result, 0, result = new ReferenceBinding[length], 0, length);
@@ -382,6 +385,9 @@
if (i == j) continue;
TypeBinding jType = result[j];
if (jType == null) continue;
+ if (isMalformedPair(iType, jType, scope)) {
+ return null;
+ }
if (iType.isCompatibleWith(jType, scope)) { // if Vi <: Vj, Vj is removed
if (result == types) { // defensive copy
System.arraycopy(result, 0, result = new TypeBinding[length], 0, length);
@@ -442,6 +448,24 @@
return trimmedResult;
}
+ static boolean isMalformedPair(TypeBinding t1, TypeBinding t2, Scope scope) {
+ // not spec-ed in JLS, but per email communication (2017-09-13) it should be
+ switch (t1.kind()) {
+ case Binding.TYPE:
+ case Binding.GENERIC_TYPE:
+ case Binding.PARAMETERIZED_TYPE:
+ case Binding.RAW_TYPE:
+ if (t1.isClass()) {
+ if (t2.getClass() == TypeVariableBinding.class) {
+ TypeBinding bound = ((TypeVariableBinding) t2).firstBound;
+ if (bound == null || !bound.erasure().isCompatibleWith(t1.erasure())) { // use of erasure is heuristic-based
+ return true; // malformed, because substitution could create a contradiction.
+ }
+ }
+ }
+ }
+ return false;
+ }
/**
* Returns an array of types, where original types got substituted given a substitution.
* Only allocate an array if anything is different.
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeVariableBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeVariableBinding.java
index f0dd9b2..1195ff7 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeVariableBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeVariableBinding.java
@@ -176,6 +176,16 @@
// non-object real superclass should have produced a valid 'match' above
return BoundCheckStatus.MISMATCH;
}
+ // not fully spec-ed in JLS, but based on email communication (2017-09-13):
+ // (a) bound check should apply capture
+ // (b) capture applies glb
+ // (c) and then the glb should be checked for well-formedness (see Scope.isMalformedPair() - this part missing in JLS).
+ // Since we don't do (a), nor (b) for this case, we just directly proceed to (b) here.
+ // For (a) see ParameterizedTypeBinding.boundCheck() - comment added as of this commit
+ // for (b) see CaptureBinding.initializeBounds() - comment added as of this commit
+ if (Scope.greaterLowerBound(new TypeBinding[] {substitutedSuperType, wildcardBound}, scope, this.environment) == null) {
+ return BoundCheckStatus.MISMATCH;
+ }
}
}
}