Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephan Herrmann2014-02-25 12:25:54 +0000
committerStephan Herrmann2014-02-25 13:15:26 +0000
commit745840be480d4e43f22d0c4f40c198cdee6926a4 (patch)
tree1fc539b8d95c72b6b0a8902ee0cc254efc741547
parentf700051affab74804180d3a8bb296f6cabd751a0 (diff)
downloadeclipse.jdt.core-745840be480d4e43f22d0c4f40c198cdee6926a4.tar.gz
eclipse.jdt.core-745840be480d4e43f22d0c4f40c198cdee6926a4.tar.xz
eclipse.jdt.core-745840be480d4e43f22d0c4f40c198cdee6926a4.zip
Bug 428985 - [1.8][null] help the type inference to find a
nullness-annotated type if possible - test & fix
-rw-r--r--org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullTypeAnnotationTest.java29
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BoundSet.java28
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeBound.java3
3 files changed, 59 insertions, 1 deletions
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullTypeAnnotationTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullTypeAnnotationTest.java
index 10125bd158..8132e223dd 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullTypeAnnotationTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullTypeAnnotationTest.java
@@ -2531,6 +2531,35 @@ public class NullTypeAnnotationTest extends AbstractNullAnnotationTest {
"----------\n");
}
+ // the only null annotation is on the target type, which propagates into the implicitly typed lambda argument
+ public void testNullTypeInference2d() {
+ runNegativeTestWithLibs(
+ new String[] {
+ "PolyNull.java",
+ "import org.eclipse.jdt.annotation.*;\n" +
+ "\n" +
+ "interface Func<T> {\n" +
+ " T a(T i);\n" +
+ "}\n" +
+ "public class PolyNull {\n" +
+ " <X> X extract(Func<X> f, X s) { return f.a(s); }\n" +
+ " @NonNull String testOK() {\n" +
+ " return extract(i -> i, \"hallo\");\n" +
+ " }\n" +
+ " @NonNull String testERR() {\n" +
+ " return extract(i -> null, \"hallo\"); // err\n" +
+ " }\n" +
+ "}\n"
+ },
+ getCompilerOptions(),
+ "----------\n" +
+ "1. ERROR in PolyNull.java (at line 12)\n" +
+ " return extract(i -> null, \"hallo\"); // err\n" +
+ " ^^^^\n" +
+ "Null type mismatch: required \'@NonNull String\' but the provided value is null\n" +
+ "----------\n");
+ }
+
// missing return type should not cause NPE
public void testBug415850_01() {
runNegativeTestWithLibs(
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BoundSet.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BoundSet.java
index 228cb5b0df..242027709a 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BoundSet.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BoundSet.java
@@ -72,17 +72,21 @@ class BoundSet {
public TypeBinding[] lowerBounds(boolean onlyProper, LookupEnvironment environment) {
TypeBinding[] boundTypes = new TypeBinding[this.superBounds.size()];
Iterator<TypeBound> it = this.superBounds.iterator();
+ long nullHints = 0;
int i = 0;
while(it.hasNext()) {
TypeBound current = it.next();
TypeBinding boundType = current.right;
- if (!onlyProper || boundType.isProperType(true))
+ if (!onlyProper || boundType.isProperType(true)) {
boundTypes[i++] = boundType;
+ nullHints |= current.nullHints;
+ }
}
if (i == 0)
return Binding.NO_TYPES;
if (i < boundTypes.length)
System.arraycopy(boundTypes, 0, boundTypes=new TypeBinding[i], 0, i);
+ useNullHints(nullHints, boundTypes, environment);
InferenceContext18.sortTypes(boundTypes);
return boundTypes;
}
@@ -91,12 +95,14 @@ class BoundSet {
ReferenceBinding[] rights = new ReferenceBinding[this.subBounds.size()];
TypeBinding simpleUpper = null;
Iterator<TypeBound> it = this.subBounds.iterator();
+ long nullHints = 0;
int i = 0;
while(it.hasNext()) {
TypeBinding right=it.next().right;
if (!onlyProper || right.isProperType(true)) {
if (right instanceof ReferenceBinding) {
rights[i++] = (ReferenceBinding) right;
+ nullHints |= right.tagBits & TagBits.AnnotationNullMASK;
} else {
if (simpleUpper != null)
return Binding.NO_TYPES; // shouldn't
@@ -110,6 +116,7 @@ class BoundSet {
return new TypeBinding[] { simpleUpper }; // no nullHints since not a reference type
if (i < rights.length)
System.arraycopy(rights, 0, rights=new ReferenceBinding[i], 0, i);
+ useNullHints(nullHints, rights, environment);
InferenceContext18.sortTypes(rights);
return rights;
}
@@ -223,6 +230,20 @@ class BoundSet {
}
return wrapperBound;
}
+ /**
+ * Not per JLS: enhance the given type bounds using the nullHints, if useful.
+ * Will only ever be effective if any TypeBounds carry nullHints,
+ * which only happens if any TypeBindings have non-zero (tagBits & AnnotationNullMASK),
+ * which only happens if null annotations are enabled in the first place.
+ */
+ private void useNullHints(long nullHints, TypeBinding[] boundTypes, LookupEnvironment environment) {
+ AnnotationBinding[] annot = environment.nullAnnotationsFromTagBits(nullHints);
+ if (annot != null) {
+ // only get here if exactly one of @NonNull or @Nullable was hinted; now apply this hint:
+ for (int i = 0; i < boundTypes.length; i++)
+ boundTypes[i] = environment.createAnnotatedType(boundTypes[i], annot);
+ }
+ }
}
// main storage of type bounds:
HashMap<InferenceVariable, ThreeSets> boundsPerVariable = new HashMap<InferenceVariable, ThreeSets>();
@@ -410,6 +431,11 @@ class BoundSet {
return false;
// TODO here and below: better checking if constraint really added to the boundset (optimization)?
hasUpdate = true;
+ // not per JLS: if the new constraint relates types where at least one has a null annotations,
+ // record all null tagBits as hints for the final inference solution.
+ long nullHints = (newConstraint.left.tagBits | newConstraint.right.tagBits) & TagBits.AnnotationNullMASK;
+ boundI.nullHints |= nullHints;
+ boundJ.nullHints |= nullHints;
}
ConstraintFormula[] typeArgumentConstraints = deriveTypeArgumentConstraints(boundI, boundJ);
if (typeArgumentConstraints != null) {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeBound.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeBound.java
index b8671b41a9..61335439c6 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeBound.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeBound.java
@@ -26,6 +26,9 @@ public class TypeBound extends ReductionResult {
// this flag contributes to the workaround controlled by InferenceContext18.ARGUMENT_CONSTRAINTS_ARE_SOFT:
boolean isSoft;
+
+ // here we accumulate null tagBits from any types that have been related to this type bound during incorporation:
+ long nullHints;
static TypeBound createBoundOrDependency(InferenceContext18 context, TypeBinding type, InferenceVariable variable) {
// Part of JLS8 sect 18.1.3:

Back to the top