Relax assertions during check-cast removal

This CL relaxes the following two assertions from the check-cast removal:
  assert outTypeLattice.equals(inTypeLattice);
  assert outTypeLattice.equals(castTypeLattice);

These asserts are overly restrictive as they require that the two types agree on the nullability, although they don't need to. For example, it is allowed that the in-type is not nullable and the out-type is nullable. This is a loss of precision, but still sound.

For convenience and clarity, this CL introduces a simple null-lattice that can be used for the soundness checks.

Change-Id: I098eb77be2eb032a6b8533851080ee90995040eb
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeLatticeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeLatticeElement.java
index 94a07ea..a09039c 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeLatticeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeLatticeElement.java
@@ -37,6 +37,7 @@
   public static final ReferenceTypeLatticeElement REFERENCE =
       ReferenceTypeLatticeElement.getReferenceTypeLatticeElement();
 
+  // TODO(b/72693244): Switch to NullLatticeElement.
   private final boolean isNullable;
 
   TypeLatticeElement(boolean isNullable) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 02c06da..17d407b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -1661,25 +1661,30 @@
         TypeLatticeElement outTypeLattice = outValue.getTypeLattice();
         TypeLatticeElement castTypeLattice =
             TypeLatticeElement.fromDexType(castType, appInfo, inTypeLattice.isNullable());
-        // 1) Trivial cast.
-        //   A a = ...
-        //   A a' = (A) a;
-        // 2) Up-cast: we already have finer type info.
-        //   A < B
-        //   A a = ...
-        //   B b = (B) a;
+
+        assert inTypeLattice.nullElement().lessThanOrEqual(outTypeLattice.nullElement());
+
         if (TypeLatticeElement.lessThanOrEqual(appInfo, inTypeLattice, castTypeLattice)) {
-          assert outTypeLattice.equals(inTypeLattice);
+          // 1) Trivial cast.
+          //   A a = ...
+          //   A a' = (A) a;
+          // 2) Up-cast: we already have finer type info.
+          //   A < B
+          //   A a = ...
+          //   B b = (B) a;
+          assert inTypeLattice.isNull()
+              || outTypeLattice.asNullable().equals(inTypeLattice.asNullable());
           needToRemoveTrivialPhis = needToRemoveTrivialPhis || outValue.numberOfPhiUsers() != 0;
           removeOrReplaceByDebugLocalWrite(checkCast, it, inValue, outValue);
-          continue;
+        } else {
+          // Otherwise, keep the checkcast to preserve verification errors. E.g., down-cast:
+          // A < B < C
+          // c = ...        // Even though we know c is of type A,
+          // a' = (B) c;    // (this could be removed, since chained below.)
+          // a'' = (A) a';  // this should remain for runtime verification.
+          assert !inTypeLattice.isNull();
+          assert outTypeLattice.asNullable().equals(castTypeLattice.asNullable());
         }
-        // Otherwise, keep the checkcast to preserve verification errors. E.g., down-cast:
-        // A < B < C
-        // c = ...        // Even though we know c is of type A,
-        // a' = (B) c;    // (this could be removed, since chained below.)
-        // a'' = (A) a';  // this should remain for runtime verification.
-        assert outTypeLattice.equals(castTypeLattice);
       }
     }
     // ... v1