Fix nullability flop for definitely null nullability.
Bug: 141654799
Change-Id: I59fe263e975b79049098d6938462c92cee355485
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/Nullability.java b/src/main/java/com/android/tools/r8/ir/analysis/type/Nullability.java
index 11c1c8c..26b9030 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/Nullability.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/Nullability.java
@@ -58,6 +58,19 @@
return MAYBE_NULL;
}
+ public Nullability meet(Nullability other) {
+ if (this == MAYBE_NULL) {
+ return other;
+ }
+ if (other == MAYBE_NULL) {
+ return this;
+ }
+ if (this == other) {
+ return this;
+ }
+ return BOTTOM;
+ }
+
public boolean lessThanOrEqual(Nullability other) {
return join(other) == other;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/ReferenceTypeLatticeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/ReferenceTypeLatticeElement.java
index 8e42de0..f6af1f7 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/ReferenceTypeLatticeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/ReferenceTypeLatticeElement.java
@@ -75,8 +75,8 @@
public abstract ReferenceTypeLatticeElement getOrCreateVariant(Nullability nullability);
- public TypeLatticeElement asNotNull() {
- return getOrCreateVariant(Nullability.definitelyNotNull());
+ public TypeLatticeElement asMeetWithNotNull() {
+ return getOrCreateVariant(nullability.meet(Nullability.definitelyNotNull()));
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Assume.java b/src/main/java/com/android/tools/r8/ir/code/Assume.java
index 05ff7a1..9ba29fd 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Assume.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Assume.java
@@ -241,7 +241,7 @@
}
if (assumption.isAssumeNonNull()) {
assert src().getTypeLattice().isReference();
- return src().getTypeLattice().asReferenceTypeLatticeElement().asNotNull();
+ return src().getTypeLattice().asReferenceTypeLatticeElement().asMeetWithNotNull();
}
throw new Unimplemented();
}
@@ -280,7 +280,7 @@
assert isAssumeNonNull() : this;
assert inType.isReference() : inType;
assert inType.isNullType()
- || outType.equals(inType.asReferenceTypeLatticeElement().asNotNull())
+ || outType.equals(inType.asReferenceTypeLatticeElement().asMeetWithNotNull())
: "At " + this + System.lineSeparator() + outType + " != " + inType;
}
return true;
diff --git a/src/main/java/com/android/tools/r8/ir/code/Value.java b/src/main/java/com/android/tools/r8/ir/code/Value.java
index 2881de7..4105077 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Value.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Value.java
@@ -1142,9 +1142,9 @@
// i.e., towards something wider.
assert this.typeLattice.lessThanOrEqual(newType, appView)
: "During WIDENING, "
- + typeLattice
- + " < "
+ newType
+ + " < "
+ + typeLattice
+ " at "
+ (isPhi() ? asPhi().printPhi() : definition.toString());
setTypeLattice(newType);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java b/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java
index 45f8c45..8d3f18f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java
@@ -197,12 +197,12 @@
} else {
specializedArg = originalArg;
}
+ assert specializedArg != null && specializedArg.getTypeLattice().isReference();
if (dynamicType.isDefinitelyNotNull()) {
// If we already knew `arg` is never null, e.g., receiver, skip adding non-null.
if (!specializedArg.getTypeLattice().isDefinitelyNotNull()) {
- Value nonNullArg =
- code.createValue(
- specializedArg.getTypeLattice().asReferenceTypeLatticeElement().asNotNull());
+ Value nonNullArg = code.createValue(
+ specializedArg.getTypeLattice().asReferenceTypeLatticeElement().asMeetWithNotNull());
affectedValues.addAll(specializedArg.affectedValues());
specializedArg.replaceUsers(nonNullArg);
Assume<NonNullAssumption> assumeNotNull =
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java b/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java
index 02cfe3a..0adf3f7 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java
@@ -227,7 +227,7 @@
TypeLatticeElement typeLattice = knownToBeNonNullValue.getTypeLattice();
Value nonNullValue =
code.createValue(
- typeLattice.asReferenceTypeLatticeElement().asNotNull(),
+ typeLattice.asReferenceTypeLatticeElement().asMeetWithNotNull(),
knownToBeNonNullValue.getLocalInfo());
affectedValues.addAll(knownToBeNonNullValue.affectedValues());
Assume<NonNullAssumption> nonNull =
@@ -359,7 +359,7 @@
assert typeLattice.isReference();
Value nonNullValue =
code.createValue(
- typeLattice.asReferenceTypeLatticeElement().asNotNull(),
+ typeLattice.asReferenceTypeLatticeElement().asMeetWithNotNull(),
knownToBeNonNullValue.getLocalInfo());
affectedValues.addAll(knownToBeNonNullValue.affectedValues());
Assume<NonNullAssumption> nonNull =
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/type/TypeLatticeTest.java b/src/test/java/com/android/tools/r8/ir/analysis/type/TypeLatticeTest.java
index 25988b8..42908d2 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/type/TypeLatticeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/type/TypeLatticeTest.java
@@ -554,7 +554,9 @@
@Test
public void testNotNullOfNullGivesBottom() {
- assertEquals(Nullability.bottom(), ReferenceTypeLatticeElement.NULL.asNotNull().nullability());
+ assertEquals(
+ Nullability.bottom(),
+ ReferenceTypeLatticeElement.NULL.asMeetWithNotNull().nullability());
}
@Test
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/nonnull/B141654799.java b/src/test/java/com/android/tools/r8/ir/optimize/nonnull/B141654799.java
new file mode 100644
index 0000000..de6f2e7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/nonnull/B141654799.java
@@ -0,0 +1,71 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.optimize.nonnull;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class B141654799 extends TestBase {
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().build();
+ }
+
+ public B141654799(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(B141654799.class)
+ .enableInliningAnnotations()
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(parameters.getRuntime())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("The end")
+ .inspect(inspector -> {
+ ClassSubject main = inspector.clazz(TestClass.class);
+ assertThat(main, isPresent());
+ MethodSubject mainMethod = main.mainMethod();
+ assertThat(mainMethod, isPresent());
+ assertTrue(mainMethod.streamInstructions().noneMatch(i -> i.isIfEqz() || i.isIfNez()));
+ });
+ }
+
+ static class TestClass {
+ public static void main(String... args) {
+ TestClass x = null;
+ if (System.currentTimeMillis() > 0) {
+ TestClass y = (TestClass) returnsNull();
+ if (y != null) {
+ x = y;
+ }
+ }
+ if (x != null) {
+ System.out.println("Dead code: " + x.toString());
+ }
+ System.out.println("The end");
+ }
+
+ @NeverInline
+ static Object returnsNull() {
+ return null;
+ }
+ }
+}