Merge "Store profile report for each build in run_on_as_app.py"
diff --git a/build.gradle b/build.gradle
index 93d7e1b..7ff46eb 100644
--- a/build.gradle
+++ b/build.gradle
@@ -702,6 +702,11 @@
     workingDir = projectDir
 }
 
+task R8LibApiOnly {
+    dependsOn r8LibCreateTask("Api", "src/main/keep.txt", R8NoManifest, r8LibPath)
+    outputs.file r8LibPath
+}
+
 task R8LibNoDeps {
     dependsOn r8LibCreateTask(
             "NoDeps",
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index b500296..87886de 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
 
   // This field is accessed from release scripts using simple pattern matching.
   // Therefore, changing this field could break our release scripts.
-  public static final String LABEL = "1.4.21-dev";
+  public static final String LABEL = "1.5.0-dev";
 
   private Version() {
   }
diff --git a/src/main/java/com/android/tools/r8/cf/TypeVerificationHelper.java b/src/main/java/com/android/tools/r8/cf/TypeVerificationHelper.java
index 53907db..2349a3b 100644
--- a/src/main/java/com/android/tools/r8/cf/TypeVerificationHelper.java
+++ b/src/main/java/com/android/tools/r8/cf/TypeVerificationHelper.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
 import com.android.tools.r8.ir.code.Argument;
 import com.android.tools.r8.ir.code.ConstNumber;
@@ -219,7 +220,7 @@
   }
 
   private TypeLatticeElement getLatticeElement(DexType type) {
-    return TypeLatticeElement.fromDexType(type, true, appInfo);
+    return TypeLatticeElement.fromDexType(type, Nullability.maybeNull(), appInfo);
   }
 
   public Map<Value, TypeInfo> computeVerificationTypes() {
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index acf3e05..72c6903 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -3,6 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph;
 
+import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
+
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.dex.Marker;
 import com.android.tools.r8.graph.DexDebugEvent.AdvanceLine;
@@ -17,6 +19,7 @@
 import com.android.tools.r8.graph.DexMethodHandle.MethodHandleType;
 import com.android.tools.r8.ir.analysis.type.ArrayTypeLatticeElement;
 import com.android.tools.r8.ir.analysis.type.ClassTypeLatticeElement;
+import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.ReferenceTypeLatticeElement;
 import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
 import com.android.tools.r8.ir.code.Position;
@@ -993,40 +996,43 @@
   }
 
   public ReferenceTypeLatticeElement createReferenceTypeLatticeElement(
-      DexType type, boolean isNullable, AppInfo appInfo) {
-    ReferenceTypeLatticeElement typeLattice = referenceTypeLatticeElements.get(type);
-    if (typeLattice != null) {
-      return isNullable == typeLattice.isNullable() ? typeLattice
-          : typeLattice.getOrCreateDualLattice();
+      DexType type, Nullability nullability, AppInfo appInfo) {
+    ReferenceTypeLatticeElement primary = referenceTypeLatticeElements.get(type);
+    if (primary != null) {
+      return nullability == primary.nullability()
+          ? primary
+          : primary.getOrCreateVariant(nullability);
     }
     synchronized (type) {
-      typeLattice = referenceTypeLatticeElements.get(type);
-      if (typeLattice == null) {
+      primary = referenceTypeLatticeElements.get(type);
+      if (primary == null) {
         if (type.isClassType()) {
           if (!type.isUnknown() && type.isInterface()) {
-            typeLattice = new ClassTypeLatticeElement(
-                appInfo.dexItemFactory.objectType, isNullable, ImmutableSet.of(type));
+            primary = new ClassTypeLatticeElement(objectType, maybeNull(), ImmutableSet.of(type));
           } else {
             // In theory, `interfaces` is the least upper bound of implemented interfaces.
             // It is expensive to walk through type hierarchy; collect implemented interfaces; and
             // compute the least upper bound of two interface sets. Hence, lazy computations.
             // Most likely during lattice join. See {@link ClassTypeLatticeElement#getInterfaces}.
-            typeLattice = new ClassTypeLatticeElement(type, isNullable, appInfo);
+            primary = new ClassTypeLatticeElement(type, maybeNull(), appInfo);
           }
         } else {
           assert type.isArrayType();
           DexType elementType = type.toArrayElementType(this);
           TypeLatticeElement elementTypeLattice =
-              TypeLatticeElement.fromDexType(elementType, true, appInfo, true);
-          typeLattice = new ArrayTypeLatticeElement(elementTypeLattice, isNullable);
+              TypeLatticeElement.fromDexType(elementType, maybeNull(), appInfo, true);
+          primary = new ArrayTypeLatticeElement(elementTypeLattice, maybeNull());
         }
-        referenceTypeLatticeElements.put(type, typeLattice);
+        referenceTypeLatticeElements.put(type, primary);
       }
+      // Make sure that canonicalized version is MAYBE_NULL variant.
+      assert primary.nullability().isMaybeNull();
     }
-    // The call to getOrCreateDualLattice can't be under the DexType synchronized block, since that
+    // The call to getOrCreateVariant can't be under the DexType synchronized block, since that
     // can create deadlocks with ClassTypeLatticeElement::getInterfaces (both lock on the lattice).
-    return isNullable == typeLattice.isNullable() ? typeLattice
-        : typeLattice.getOrCreateDualLattice();
+    return nullability == primary.nullability()
+        ? primary
+        : primary.getOrCreateVariant(nullability);
   }
 
   private static <S extends PresortedComparable<S>> void assignSortedIndices(Collection<S> items,
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/TypeChecker.java b/src/main/java/com/android/tools/r8/ir/analysis/TypeChecker.java
index be7709c..53a6245 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/TypeChecker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/TypeChecker.java
@@ -73,7 +73,7 @@
             : instruction.asStaticPut().inValue();
     TypeLatticeElement valueType = value.getTypeLattice();
     TypeLatticeElement fieldType = TypeLatticeElement.fromDexType(
-        instruction.getField().type, valueType.isNullable(), appInfo);
+        instruction.getField().type, valueType.nullability(), appInfo);
     if (isSubtypeOf(valueType, fieldType)) {
       return true;
     }
@@ -91,13 +91,14 @@
   public boolean check(Throw instruction) {
     TypeLatticeElement valueType = instruction.exception().getTypeLattice();
     TypeLatticeElement throwableType = TypeLatticeElement.fromDexType(
-        appInfo.dexItemFactory.throwableType, valueType.isNullable(), appInfo);
+        appInfo.dexItemFactory.throwableType, valueType.nullability(), appInfo);
     return isSubtypeOf(valueType, throwableType);
   }
 
   private boolean isSubtypeOf(
       TypeLatticeElement expectedSubtype, TypeLatticeElement expectedSupertype) {
-    return expectedSubtype.lessThanOrEqual(expectedSupertype, appInfo)
+    return (expectedSubtype.isNullType() && expectedSupertype.isReference())
+        || expectedSubtype.lessThanOrEqual(expectedSupertype, appInfo)
         || expectedSubtype.isBasedOnMissingClass(appInfo);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/ArrayTypeLatticeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/ArrayTypeLatticeElement.java
index 6bb13a7..a88a71d 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/ArrayTypeLatticeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/ArrayTypeLatticeElement.java
@@ -3,6 +3,9 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.analysis.type;
 
+import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
+import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
+
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexType;
@@ -11,8 +14,9 @@
 
   private final TypeLatticeElement memberTypeLattice;
 
-  public ArrayTypeLatticeElement(TypeLatticeElement memberTypeLattice, boolean isNullable) {
-    super(isNullable, null);
+  public ArrayTypeLatticeElement(
+      TypeLatticeElement memberTypeLattice, Nullability nullability) {
+    super(nullability, null);
     this.memberTypeLattice = memberTypeLattice;
   }
 
@@ -55,28 +59,21 @@
   }
 
   @Override
-  public ReferenceTypeLatticeElement getOrCreateDualLattice() {
-    if (dual != null) {
-      return dual;
+  ReferenceTypeLatticeElement createVariant(Nullability nullability) {
+    if (this.nullability == nullability) {
+      return this;
     }
-    synchronized (this) {
-      if (dual == null) {
-        ArrayTypeLatticeElement dual =
-            new ArrayTypeLatticeElement(memberTypeLattice, !isNullable());
-        linkDualLattice(this, dual);
-      }
-    }
-    return this.dual;
+    return new ArrayTypeLatticeElement(memberTypeLattice, nullability);
   }
 
   @Override
   public TypeLatticeElement asNullable() {
-    return isNullable() ? this : getOrCreateDualLattice();
+    return nullability.isNullable() ? this : getOrCreateVariant(maybeNull());
   }
 
   @Override
   public TypeLatticeElement asNonNullable() {
-    return !isNullable() ? this : getOrCreateDualLattice();
+    return nullability.isDefinitelyNotNull() ? this : getOrCreateVariant(definitelyNotNull());
   }
 
   @Override
@@ -108,7 +105,7 @@
       return false;
     }
     ArrayTypeLatticeElement other = (ArrayTypeLatticeElement) o;
-    if (isNullable() != other.isNullable()) {
+    if (nullability() != other.nullability()) {
       return false;
     }
     if (type != null && other.type != null && !type.equals(other.type)) {
@@ -129,21 +126,21 @@
       // Return null indicating the join is the same as the member to avoid object allocation.
       return null;
     }
-    boolean isNullable = isNullable() || other.isNullable();
+    Nullability nullability = nullability().join(other.nullability());
     if (aMember.isArrayType() && bMember.isArrayType()) {
       ReferenceTypeLatticeElement join =
           aMember.asArrayTypeLatticeElement().join(bMember.asArrayTypeLatticeElement(), appInfo);
-      return join == null ? null : new ArrayTypeLatticeElement(join, isNullable);
+      return join == null ? null : new ArrayTypeLatticeElement(join, nullability);
     }
     if (aMember.isClassType() && bMember.isClassType()) {
       ClassTypeLatticeElement join =
           aMember.asClassTypeLatticeElement().join(bMember.asClassTypeLatticeElement(), appInfo);
-      return join == null ? null : new ArrayTypeLatticeElement(join, isNullable);
+      return join == null ? null : new ArrayTypeLatticeElement(join, nullability);
     }
     if (aMember.isPrimitive() || bMember.isPrimitive()) {
-      return objectClassType(appInfo, isNullable);
+      return objectClassType(appInfo, nullability);
     }
-    return objectArrayType(appInfo, isNullable);
+    return objectArrayType(appInfo, nullability);
   }
 
 }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/BottomTypeLatticeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/BottomTypeLatticeElement.java
index e86d66f..c44aa34 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/BottomTypeLatticeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/BottomTypeLatticeElement.java
@@ -9,13 +9,9 @@
 public class BottomTypeLatticeElement extends TypeLatticeElement {
   private static final BottomTypeLatticeElement INSTANCE = new BottomTypeLatticeElement();
 
-  private BottomTypeLatticeElement() {
-    super(true);
-  }
-
   @Override
-  public TypeLatticeElement asNullable() {
-    return this;
+  public Nullability nullability() {
+    return Nullability.maybeNull();
   }
 
   static BottomTypeLatticeElement getInstance() {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeLatticeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeLatticeElement.java
index c25395d..34ef919 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeLatticeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeLatticeElement.java
@@ -3,6 +3,9 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.analysis.type;
 
+import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
+import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
+
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexType;
@@ -20,17 +23,22 @@
   private Set<DexType> lazyInterfaces;
   private AppInfo appInfoForLazyInterfacesComputation;
 
-  public ClassTypeLatticeElement(DexType classType, boolean isNullable, Set<DexType> interfaces) {
-    this(classType, isNullable, interfaces, null);
+  public ClassTypeLatticeElement(
+      DexType classType, Nullability nullability, Set<DexType> interfaces) {
+    this(classType, nullability, interfaces, null);
   }
 
-  public ClassTypeLatticeElement(DexType classType, boolean isNullable, AppInfo appInfo) {
-    this(classType, isNullable, null, appInfo);
+  public ClassTypeLatticeElement(
+      DexType classType, Nullability nullability, AppInfo appInfo) {
+    this(classType, nullability, null, appInfo);
   }
 
   private ClassTypeLatticeElement(
-      DexType classType, boolean isNullable, Set<DexType> interfaces, AppInfo appInfo) {
-    super(isNullable, classType);
+      DexType classType,
+      Nullability nullability,
+      Set<DexType> interfaces,
+      AppInfo appInfo) {
+    super(nullability, classType);
     assert classType.isClassType();
     appInfoForLazyInterfacesComputation = appInfo;
     lazyInterfaces = interfaces;
@@ -57,29 +65,22 @@
   }
 
   @Override
-  public ReferenceTypeLatticeElement getOrCreateDualLattice() {
-    if (dual != null) {
-      return dual;
+  ReferenceTypeLatticeElement createVariant(Nullability nullability) {
+    if (this.nullability == nullability) {
+      return this;
     }
-    synchronized (this) {
-      if (dual == null) {
-        ClassTypeLatticeElement dual =
-            new ClassTypeLatticeElement(
-                type, !isNullable(), lazyInterfaces, appInfoForLazyInterfacesComputation);
-        linkDualLattice(this, dual);
-      }
-    }
-    return this.dual;
+    return new ClassTypeLatticeElement(
+        type, nullability, lazyInterfaces, appInfoForLazyInterfacesComputation);
   }
 
   @Override
   public TypeLatticeElement asNullable() {
-    return isNullable() ? this : getOrCreateDualLattice();
+    return nullability.isNullable() ? this : getOrCreateVariant(maybeNull());
   }
 
   @Override
   public TypeLatticeElement asNonNullable() {
-    return !isNullable() ? this : getOrCreateDualLattice();
+    return nullability.isDefinitelyNotNull() ? this : getOrCreateVariant(definitelyNotNull());
   }
 
   @Override
@@ -126,8 +127,8 @@
     if (lubItfs == null) {
       lubItfs = computeLeastUpperBoundOfInterfaces(appInfo, c1lubItfs, c2lubItfs);
     }
-    boolean isNullable = isNullable() || other.isNullable();
-    return new ClassTypeLatticeElement(lubType, isNullable, lubItfs);
+    Nullability nullability = nullability().join(other.nullability());
+    return new ClassTypeLatticeElement(lubType, nullability, lubItfs);
   }
 
   private enum InterfaceMarker {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/NullLatticeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/NullLatticeElement.java
deleted file mode 100644
index caad0a2..0000000
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/NullLatticeElement.java
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright (c) 2018, 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.analysis.type;
-
-/**
- * Encodes the following lattice.
- *
- * <pre>
- *          MAYBE NULL
- *          /        \
- *   DEFINITELY     DEFINITELY
- *      NULL         NOT NULL
- *          \        /
- *            BOTTOM
- * </pre>
- */
-public class NullLatticeElement {
-
-  private static final NullLatticeElement BOTTOM = new NullLatticeElement();
-  private static final NullLatticeElement DEFINITELY_NULL = new NullLatticeElement();
-  private static final NullLatticeElement DEFINITELY_NOT_NULL = new NullLatticeElement();
-  private static final NullLatticeElement MAYBE_NULL = new NullLatticeElement();
-
-  private NullLatticeElement() {}
-
-  public boolean isDefinitelyNull() {
-    return this == DEFINITELY_NULL;
-  }
-
-  public boolean isDefinitelyNotNull() {
-    return this == DEFINITELY_NOT_NULL;
-  }
-
-  public NullLatticeElement leastUpperBound(NullLatticeElement other) {
-    if (this == BOTTOM) {
-      return other;
-    }
-    if (this == other || other == BOTTOM) {
-      return this;
-    }
-    return MAYBE_NULL;
-  }
-
-  public boolean lessThanOrEqual(NullLatticeElement other) {
-    return leastUpperBound(other) == other;
-  }
-
-  static NullLatticeElement bottom() {
-    return BOTTOM;
-  }
-
-  static NullLatticeElement definitelyNull() {
-    return DEFINITELY_NULL;
-  }
-
-  static NullLatticeElement definitelyNotNull() {
-    return DEFINITELY_NOT_NULL;
-  }
-
-  static NullLatticeElement maybeNull() {
-    return MAYBE_NULL;
-  }
-}
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
new file mode 100644
index 0000000..c91374d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/Nullability.java
@@ -0,0 +1,79 @@
+// Copyright (c) 2018, 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.analysis.type;
+
+import com.android.tools.r8.errors.Unreachable;
+
+/**
+ * Encodes the following lattice.
+ *
+ * <pre>
+ *          MAYBE NULL
+ *          /        \
+ *   DEFINITELY     DEFINITELY
+ *      NULL         NOT NULL
+ * </pre>
+ */
+public class Nullability {
+
+  private static final Nullability DEFINITELY_NULL = new Nullability();
+  private static final Nullability DEFINITELY_NOT_NULL = new Nullability();
+  private static final Nullability MAYBE_NULL = new Nullability();
+
+  private Nullability() {}
+
+  public boolean isDefinitelyNull() {
+    return this == DEFINITELY_NULL;
+  }
+
+  public boolean isDefinitelyNotNull() {
+    return this == DEFINITELY_NOT_NULL;
+  }
+
+  public boolean isMaybeNull() {
+    return this == MAYBE_NULL;
+  }
+
+  public Nullability join(Nullability other) {
+    if (this == other) {
+      return this;
+    }
+    return MAYBE_NULL;
+  }
+
+  public boolean lessThanOrEqual(Nullability other) {
+    return join(other) == other;
+  }
+
+  public boolean isNullable() {
+    return this == MAYBE_NULL || this == DEFINITELY_NULL;
+  }
+
+  public static Nullability definitelyNull() {
+    return DEFINITELY_NULL;
+  }
+
+  public static Nullability definitelyNotNull() {
+    return DEFINITELY_NOT_NULL;
+  }
+
+  public static Nullability maybeNull() {
+    return MAYBE_NULL;
+  }
+
+  @Override
+  public String toString() {
+    if (this == MAYBE_NULL) {
+      return "@Nullable";
+    }
+    if (this == DEFINITELY_NULL) {
+      return "@Null";
+    }
+    if (this == DEFINITELY_NOT_NULL) {
+      return "@NotNull";
+    }
+    throw new Unreachable("Unknown Nullability.");
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/PrimitiveTypeLatticeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/PrimitiveTypeLatticeElement.java
index 4c633f5..50b1513 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/PrimitiveTypeLatticeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/PrimitiveTypeLatticeElement.java
@@ -14,13 +14,9 @@
  */
 public abstract class PrimitiveTypeLatticeElement extends TypeLatticeElement {
 
-  PrimitiveTypeLatticeElement() {
-    super(false);
-  }
-
   @Override
-  public TypeLatticeElement asNullable() {
-    return TypeLatticeElement.TOP;
+  public Nullability nullability() {
+    return Nullability.definitelyNotNull();
   }
 
   @Override
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 0db0ca4..2baaa52 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
@@ -11,29 +11,77 @@
 
 public class ReferenceTypeLatticeElement extends TypeLatticeElement {
   private static final ReferenceTypeLatticeElement NULL_INSTANCE =
-      new ReferenceTypeLatticeElement(true, DexItemFactory.nullValueType);
+      new ReferenceTypeLatticeElement(
+          Nullability.definitelyNull(), DexItemFactory.nullValueType);
 
   // TODO(b/72693244): Consider moving this to ClassTypeLatticeElement.
   final DexType type;
 
-  // Link between maybe-null and definitely-not-null reference type lattices.
-  ReferenceTypeLatticeElement dual;
+  final Nullability nullability;
+  // On-demand link between maybe-null (primary) and definitely-null reference type lattices.
+  private ReferenceTypeLatticeElement primaryOrNullVariant;
+  // On-demand link between maybe-null (primary) and definitely-not-null reference type lattices.
+  // This link will be null for non-primary variants.
+  private ReferenceTypeLatticeElement nonNullVariant;
 
-  public ReferenceTypeLatticeElement getOrCreateDualLattice() {
-    throw new Unreachable("Should be defined/used by class/array types.");
-  }
-
-  static void linkDualLattice(ReferenceTypeLatticeElement t1, ReferenceTypeLatticeElement t2) {
-    assert t1.dual == null && t2.dual == null;
-    t1.dual = t2;
-    t2.dual = t1;
-  }
-
-  ReferenceTypeLatticeElement(boolean isNullable, DexType type) {
-    super(isNullable);
+  ReferenceTypeLatticeElement(Nullability nullability, DexType type) {
+    this.nullability = nullability;
     this.type = type;
   }
 
+  public ReferenceTypeLatticeElement getOrCreateVariant(Nullability variantNullability) {
+    if (nullability == variantNullability) {
+      return this;
+    }
+    ReferenceTypeLatticeElement primary = nullability.isMaybeNull() ? this : primaryOrNullVariant;
+    synchronized (this) {
+      // If the link towards the factory-created, canonicalized MAYBE_NULL variant doesn't exist,
+      // we are in the middle of join() computation.
+      if (primary == null) {
+        primary = createVariant(Nullability.maybeNull());
+        linkVariant(primary, this);
+      }
+    }
+    if (variantNullability.isMaybeNull()) {
+      return primary;
+    }
+    synchronized (primary) {
+      ReferenceTypeLatticeElement variant =
+          variantNullability.isDefinitelyNull()
+              ? primary.primaryOrNullVariant
+              : primary.nonNullVariant;
+      if (variant == null) {
+        variant = createVariant(variantNullability);
+        linkVariant(primary, variant);
+      }
+      return variant;
+    }
+  }
+
+  ReferenceTypeLatticeElement createVariant(Nullability nullability) {
+    throw new Unreachable("Should be defined by class/array type lattice element");
+  }
+
+  private static void linkVariant(
+      ReferenceTypeLatticeElement primary, ReferenceTypeLatticeElement variant) {
+    assert primary.nullability().isMaybeNull();
+    assert variant.primaryOrNullVariant == null && variant.nonNullVariant == null;
+    variant.primaryOrNullVariant = primary;
+    if (variant.nullability().isDefinitelyNotNull()) {
+      assert primary.nonNullVariant == null;
+      primary.nonNullVariant = variant;
+    } else {
+      assert variant.nullability().isDefinitelyNull();
+      assert primary.primaryOrNullVariant == null;
+      primary.primaryOrNullVariant = variant;
+    }
+  }
+
+  @Override
+  public Nullability nullability() {
+    return nullability;
+  }
+
   static ReferenceTypeLatticeElement getNullTypeLatticeElement() {
     return NULL_INSTANCE;
   }
@@ -60,7 +108,7 @@
 
   @Override
   public String toString() {
-    return isNullableString() + type.toString();
+    return nullability.toString() + " " + type.toString();
   }
 
   @Override
@@ -72,7 +120,7 @@
       return false;
     }
     ReferenceTypeLatticeElement other = (ReferenceTypeLatticeElement) o;
-    if (this.isNullable() != other.isNullable()) {
+    if (nullability() != other.nullability()) {
       return false;
     }
     if (!type.equals(other.type)) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/TopTypeLatticeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/TopTypeLatticeElement.java
index 5221ae3..d0d0c79 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/TopTypeLatticeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/TopTypeLatticeElement.java
@@ -9,13 +9,9 @@
 public class TopTypeLatticeElement extends TypeLatticeElement {
   private static final TopTypeLatticeElement INSTANCE = new TopTypeLatticeElement();
 
-  private TopTypeLatticeElement() {
-    super(true);
-  }
-
   @Override
-  public TypeLatticeElement asNullable() {
-    return this;
+  public Nullability nullability() {
+    return Nullability.maybeNull();
   }
 
   static TopTypeLatticeElement getInstance() {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeAnalysis.java
index dd6a952..00f4af5 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeAnalysis.java
@@ -3,6 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.analysis.type;
 
+import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
+import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
 import static com.android.tools.r8.ir.analysis.type.TypeLatticeElement.fromDexType;
 
 import com.android.tools.r8.graph.AppInfo;
@@ -94,10 +96,10 @@
           // Receiver
           derived = fromDexType(encodedMethod.method.holder,
               // Now we try inlining even when the receiver could be null.
-              encodedMethod != context, appInfo);
+              encodedMethod == context ? definitelyNotNull() : maybeNull(), appInfo);
         } else {
           DexType argType = encodedMethod.method.proto.parameters.values[argumentsSeen];
-          derived = fromDexType(argType, true, appInfo);
+          derived = fromDexType(argType, maybeNull(), appInfo);
         }
         argumentsSeen++;
         updateTypeOfValue(outValue, derived);
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 14aad66..1544570 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
@@ -28,33 +28,20 @@
   public static final ReferenceTypeLatticeElement NULL =
       ReferenceTypeLatticeElement.getNullTypeLatticeElement();
 
-  // TODO(b/72693244): Switch to NullLatticeElement.
-  private final boolean isNullable;
-
-  TypeLatticeElement(boolean isNullable) {
-    this.isNullable = isNullable;
-  }
-
   public boolean isNullable() {
-    return isNullable;
+    return nullability().isNullable();
   }
 
-  public NullLatticeElement nullElement() {
-    if (isNullType()) {
-      return NullLatticeElement.definitelyNull();
-    }
-    if (!isNullable()) {
-      return NullLatticeElement.definitelyNotNull();
-    }
-    return NullLatticeElement.maybeNull();
-  }
+  public abstract Nullability nullability();
 
   /**
    * Defines how to join with null or switch to nullable lattice element.
    *
    * @return {@link TypeLatticeElement} a result of joining with null.
    */
-  public abstract TypeLatticeElement asNullable();
+  public TypeLatticeElement asNullable() {
+    return isNullable() ? this : TOP;
+  }
 
   /**
    * Defines how to switch to non-nullable lattice element.
@@ -65,10 +52,6 @@
     return BOTTOM;
   }
 
-  String isNullableString() {
-    return isNullable() ? "" : "@NonNull ";
-  }
-
   /**
    * Computes the least upper bound of the current and the other elements.
    *
@@ -107,9 +90,8 @@
     // From now on, this and other are precise reference types, i.e., either ArrayType or ClassType.
     assert isReference() && other.isReference();
     assert isPreciseType() && other.isPreciseType();
-    boolean isNullable = isNullable() || other.isNullable();
     if (getClass() != other.getClass()) {
-      return objectClassType(appInfo, isNullable);
+      return objectClassType(appInfo, nullability().join(other.nullability()));
     }
     // From now on, getClass() == other.getClass()
     if (isArrayType()) {
@@ -136,10 +118,10 @@
   }
 
   public static TypeLatticeElement joinTypes(
-      Iterable<DexType> types, boolean isNullable, AppInfo appInfo) {
+      Iterable<DexType> types, Nullability nullability, AppInfo appInfo) {
     TypeLatticeElement result = BOTTOM;
     for (DexType type : types) {
-      result = result.join(fromDexType(type, isNullable, appInfo), appInfo);
+      result = result.join(fromDexType(type, nullability, appInfo), appInfo);
     }
     return result;
   }
@@ -301,7 +283,7 @@
    * subtype of Throwable.
    */
   public boolean isDefinitelyNull() {
-    return nullElement().isDefinitelyNull();
+    return nullability().isDefinitelyNull();
   }
 
   public int requiredRegisters() {
@@ -309,40 +291,48 @@
     return isWide() ? 2 : 1;
   }
 
-  static ClassTypeLatticeElement objectClassType(AppInfo appInfo, boolean isNullable) {
-    return fromDexType(appInfo.dexItemFactory.objectType, isNullable, appInfo)
+  public static ClassTypeLatticeElement objectClassType(AppInfo appInfo, Nullability nullability) {
+    return fromDexType(appInfo.dexItemFactory.objectType, nullability, appInfo)
         .asClassTypeLatticeElement();
   }
 
-  static ArrayTypeLatticeElement objectArrayType(AppInfo appInfo, boolean isNullable) {
+  static ArrayTypeLatticeElement objectArrayType(AppInfo appInfo, Nullability nullability) {
     return fromDexType(
         appInfo.dexItemFactory.createArrayType(1, appInfo.dexItemFactory.objectType),
-        isNullable,
+        nullability,
         appInfo)
         .asArrayTypeLatticeElement();
   }
 
-  public static TypeLatticeElement classClassType(AppInfo appInfo) {
-    return fromDexType(appInfo.dexItemFactory.classType, false, appInfo);
+  public static ClassTypeLatticeElement classClassType(AppInfo appInfo, Nullability nullability) {
+    return fromDexType(appInfo.dexItemFactory.classType, nullability, appInfo)
+        .asClassTypeLatticeElement();
   }
 
-  public static TypeLatticeElement stringClassType(AppInfo appInfo) {
-    return fromDexType(appInfo.dexItemFactory.stringType, false, appInfo);
-  }
-
-  public static TypeLatticeElement fromDexType(DexType type, boolean isNullable, AppInfo appInfo) {
-    return fromDexType(type, isNullable, appInfo, false);
+  public static ClassTypeLatticeElement stringClassType(AppInfo appInfo, Nullability nullability) {
+    return fromDexType(appInfo.dexItemFactory.stringType, nullability, appInfo)
+        .asClassTypeLatticeElement();
   }
 
   public static TypeLatticeElement fromDexType(
-      DexType type, boolean isNullable, AppInfo appInfo, boolean asArrayElementType) {
+      DexType type, Nullability nullability, AppInfo appInfo) {
+    return fromDexType(type, nullability, appInfo, false);
+  }
+
+  public static TypeLatticeElement fromDexType(
+      DexType type,
+      Nullability nullability,
+      AppInfo appInfo,
+      boolean asArrayElementType) {
     if (type == DexItemFactory.nullValueType) {
+      assert !nullability.isDefinitelyNotNull();
       return NULL;
     }
     if (type.isPrimitiveType()) {
       return PrimitiveTypeLatticeElement.fromDexType(type, asArrayElementType);
     }
-    return appInfo.dexItemFactory.createReferenceTypeLatticeElement(type, isNullable, appInfo);
+    return appInfo.dexItemFactory.createReferenceTypeLatticeElement(
+        type, nullability, appInfo);
   }
 
   public boolean isValueTypeCompatible(TypeLatticeElement other) {
@@ -352,7 +342,7 @@
   }
 
   public TypeLatticeElement checkCast(AppInfo appInfo, DexType castType) {
-    TypeLatticeElement castTypeLattice = fromDexType(castType, isNullable(), appInfo);
+    TypeLatticeElement castTypeLattice = fromDexType(castType, nullability(), appInfo);
     if (lessThanOrEqual(castTypeLattice, appInfo)) {
       return this;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
index 1dcd217..a06227e 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
 import com.android.tools.r8.ir.code.Phi.RegisterReadType;
 import com.android.tools.r8.ir.conversion.DexBuilder;
@@ -1299,7 +1300,8 @@
 
   public static BasicBlock createRethrowBlock(
       IRCode code, Position position, DexType guard, AppInfo appInfo, InternalOptions options) {
-    TypeLatticeElement guardTypeLattice = TypeLatticeElement.fromDexType(guard, false, appInfo);
+    TypeLatticeElement guardTypeLattice =
+        TypeLatticeElement.fromDexType(guard, Nullability.definitelyNotNull(), appInfo);
     BasicBlock block = new BasicBlock();
     MoveException moveException = new MoveException(
         new Value(code.valueNumberGenerator.next(), guardTypeLattice, null),
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java
index 1f9c5e6..04f56f5 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java
@@ -370,8 +370,9 @@
     int i = 0;
     if (downcast != null) {
       Value receiver = invoke.inValues().get(0);
-      TypeLatticeElement castTypeLattice = TypeLatticeElement.fromDexType(
-          downcast, receiver.getTypeLattice().isNullable(), appInfo);
+      TypeLatticeElement castTypeLattice =
+          TypeLatticeElement.fromDexType(
+              downcast, receiver.getTypeLattice().nullability(), appInfo);
       CheckCast castInstruction =
           new CheckCast(code.createValue(castTypeLattice), receiver, downcast);
       castInstruction.setPosition(invoke.getPosition());
diff --git a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
index 596fdb8..2578384 100644
--- a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
+++ b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
@@ -128,7 +128,7 @@
 
     TypeLatticeElement outType = outValue().getTypeLattice();
     TypeLatticeElement castType =
-        TypeLatticeElement.fromDexType(getType(), inType.isNullable(), appInfo);
+        TypeLatticeElement.fromDexType(getType(), inType.nullability(), appInfo);
 
     if (inType.lessThanOrEqual(castType, appInfo)) {
       // Cast can be removed. Check that it is sound to replace all users of the out-value by the
@@ -145,7 +145,7 @@
       assert castType.asNullable().equals(outType.asNullable());
 
       // Check soundness of null information.
-      assert inType.nullElement().lessThanOrEqual(outType.nullElement());
+      assert inType.nullability().lessThanOrEqual(outType.nullability());
 
       // Since we cannot remove the cast the in-value must be different from null.
       assert !inType.isNullType();
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstClass.java b/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
index 0b8ef55..bd57f29 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
 import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
@@ -112,7 +113,7 @@
 
   @Override
   public TypeLatticeElement evaluate(AppInfo appInfo) {
-    return TypeLatticeElement.fromDexType(appInfo.dexItemFactory.classType, false, appInfo);
+    return TypeLatticeElement.classClassType(appInfo, Nullability.definitelyNotNull());
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java b/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java
index e0b57f1..2aa7f47 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.DexMethodHandle;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
 import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
@@ -99,7 +100,8 @@
 
   @Override
   public TypeLatticeElement evaluate(AppInfo appInfo) {
-    return TypeLatticeElement.fromDexType(appInfo.dexItemFactory.methodHandleType, false, appInfo);
+    return TypeLatticeElement.fromDexType(
+        appInfo.dexItemFactory.methodHandleType, Nullability.definitelyNotNull(), appInfo);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java b/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java
index fb23503..dd2b4ac 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
 import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
@@ -99,7 +100,8 @@
 
   @Override
   public TypeLatticeElement evaluate(AppInfo appInfo) {
-    return TypeLatticeElement.fromDexType(appInfo.dexItemFactory.methodTypeType, false, appInfo);
+    return TypeLatticeElement.fromDexType(
+        appInfo.dexItemFactory.methodTypeType, Nullability.definitelyNotNull(), appInfo);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstString.java b/src/main/java/com/android/tools/r8/ir/code/ConstString.java
index 18f467c..04e9a1b 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstString.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstString.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
 import com.android.tools.r8.ir.code.BasicBlock.ThrowingInfo;
 import com.android.tools.r8.ir.conversion.CfBuilder;
@@ -133,6 +134,6 @@
 
   @Override
   public TypeLatticeElement evaluate(AppInfo appInfo) {
-    return TypeLatticeElement.fromDexType(appInfo.dexItemFactory.stringType, false, appInfo);
+    return TypeLatticeElement.stringClassType(appInfo, Nullability.definitelyNotNull());
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java b/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java
index 2b54a4b..fa4e6c5 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
 import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
@@ -125,6 +126,6 @@
 
   @Override
   public TypeLatticeElement evaluate(AppInfo appInfo) {
-    return TypeLatticeElement.stringClassType(appInfo);
+    return TypeLatticeElement.stringClassType(appInfo, Nullability.definitelyNotNull());
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCode.java b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
index f961b80..8e0d572 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCode.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
@@ -811,6 +811,11 @@
     return new ConstNumber(out, 0);
   }
 
+  public ConstNumber createConstNull(DebugLocalInfo local) {
+    Value out = createValue(TypeLatticeElement.NULL, local);
+    return new ConstNumber(out, 0);
+  }
+
   public boolean doAllThrowingInstructionsHavePositions() {
     return allThrowingInstructionsHavePositions;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
index bcc9cef..d292aca 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
@@ -20,6 +20,7 @@
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
 import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
@@ -133,7 +134,7 @@
 
   @Override
   public TypeLatticeElement evaluate(AppInfo appInfo) {
-    return TypeLatticeElement.fromDexType(getField().type, true, appInfo);
+    return TypeLatticeElement.fromDexType(getField().type, Nullability.maybeNull(), appInfo);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Invoke.java b/src/main/java/com/android/tools/r8/ir/code/Invoke.java
index db87c13..48e2659 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Invoke.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Invoke.java
@@ -15,6 +15,7 @@
 import com.android.tools.r8.graph.DexMethodHandle.MethodHandleType;
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
 import com.android.tools.r8.ir.conversion.DexBuilder;
 import java.util.List;
@@ -266,6 +267,6 @@
     if (returnType.isVoidType()) {
       throw new Unreachable("void methods have no type.");
     }
-    return TypeLatticeElement.fromDexType(returnType, true, appInfo);
+    return TypeLatticeElement.fromDexType(returnType, Nullability.maybeNull(), appInfo);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java
index 2f28110..0c0171f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java
@@ -9,6 +9,7 @@
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
 import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
@@ -67,7 +68,7 @@
 
   @Override
   public TypeLatticeElement evaluate(AppInfo appInfo) {
-    return TypeLatticeElement.fromDexType(type, false, appInfo);
+    return TypeLatticeElement.fromDexType(type, Nullability.definitelyNotNull(), appInfo);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java b/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java
index c49650a..fbb7307 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
 import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
@@ -97,7 +98,7 @@
 
   @Override
   public TypeLatticeElement evaluate(AppInfo appInfo) {
-    return TypeLatticeElement.fromDexType(type, false, appInfo);
+    return TypeLatticeElement.fromDexType(type, Nullability.definitelyNotNull(), appInfo);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/MoveException.java b/src/main/java/com/android/tools/r8/ir/code/MoveException.java
index fae3557..325016e 100644
--- a/src/main/java/com/android/tools/r8/ir/code/MoveException.java
+++ b/src/main/java/com/android/tools/r8/ir/code/MoveException.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
 import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
@@ -102,7 +103,8 @@
 
   @Override
   public TypeLatticeElement evaluate(AppInfo appInfo) {
-    return TypeLatticeElement.fromDexType(exceptionType, false, appInfo);
+    return TypeLatticeElement.fromDexType(
+        exceptionType, Nullability.definitelyNotNull(), appInfo);
   }
 
   public DexType getExceptionType() {
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java b/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java
index 09ea5af..c010973 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
 import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
@@ -107,6 +108,6 @@
 
   @Override
   public TypeLatticeElement evaluate(AppInfo appInfo) {
-    return TypeLatticeElement.fromDexType(type, false, appInfo);
+    return TypeLatticeElement.fromDexType(type, Nullability.definitelyNotNull(), appInfo);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
index 747966c..fca7b7e 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
@@ -9,6 +9,7 @@
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
 import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
@@ -102,7 +103,7 @@
 
   @Override
   public TypeLatticeElement evaluate(AppInfo appInfo) {
-    return TypeLatticeElement.fromDexType(clazz, false, appInfo);
+    return TypeLatticeElement.fromDexType(clazz, Nullability.definitelyNotNull(), appInfo);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/StackValue.java b/src/main/java/com/android/tools/r8/ir/code/StackValue.java
index d85e602..20f10a4 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StackValue.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StackValue.java
@@ -5,6 +5,7 @@
 
 import com.android.tools.r8.cf.TypeVerificationHelper.TypeInfo;
 import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
 
 public class StackValue extends Value {
@@ -21,7 +22,8 @@
 
   public static StackValue create(TypeInfo typeInfo, int height, AppInfo appInfo) {
     return new StackValue(
-        typeInfo, TypeLatticeElement.fromDexType(typeInfo.getDexType(), true, appInfo), height);
+        typeInfo, TypeLatticeElement.fromDexType(
+            typeInfo.getDexType(), Nullability.maybeNull(), appInfo), height);
   }
 
   public int getHeight() {
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
index 4ea7fe8..af9c5c6 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
@@ -18,6 +18,7 @@
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
 import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
@@ -142,7 +143,7 @@
 
   @Override
   public TypeLatticeElement evaluate(AppInfo appInfo) {
-    return TypeLatticeElement.fromDexType(getField().type, true, appInfo);
+    return TypeLatticeElement.fromDexType(getField().type, Nullability.maybeNull(), appInfo);
   }
 
   @Override
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 b5a77c3..f95b988 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
@@ -775,7 +775,7 @@
   public boolean isNeverNull() {
     return neverNull
         || (definition != null && definition.isNonNull())
-        || (typeLattice.isReference() && typeLattice.nullElement().isDefinitelyNotNull());
+        || (typeLattice.isReference() && typeLattice.nullability().isDefinitelyNotNull());
   }
 
   public boolean canBeNull() {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
index 88148f0..a745659 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
@@ -22,6 +22,7 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
 import com.android.tools.r8.ir.code.CanonicalPositions;
 import com.android.tools.r8.ir.code.CatchHandlers;
@@ -358,7 +359,8 @@
         builder.addBooleanNonThisArgument(argumentRegister++);
       } else {
         TypeLatticeElement typeLattice =
-            TypeLatticeElement.fromDexType(type, true, builder.getAppInfo());
+            TypeLatticeElement.fromDexType(
+                type, Nullability.maybeNull(), builder.getAppInfo());
         builder.addNonThisArgument(argumentRegister, typeLattice);
         argumentRegister += typeLattice.requiredRegisters();
       }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
index 24487aa..78e5e7e 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
@@ -41,6 +41,7 @@
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
 import com.android.tools.r8.ir.code.CanonicalPositions;
 import com.android.tools.r8.ir.code.CatchHandlers;
@@ -312,7 +313,7 @@
   private List<TypeLatticeElement> computeArgumentTypes(AppInfo appInfo) {
     List<TypeLatticeElement> types = new ArrayList<>(proto.parameters.size());
     for (DexType type : proto.parameters.values) {
-      types.add(TypeLatticeElement.fromDexType(type, true, appInfo));
+      types.add(TypeLatticeElement.fromDexType(type, Nullability.maybeNull(), appInfo));
     }
     return types;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index 835399c..4035611 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -4,6 +4,9 @@
 
 package com.android.tools.r8.ir.conversion;
 
+import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
+import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
+
 import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.InternalCompilerError;
@@ -25,6 +28,7 @@
 import com.android.tools.r8.graph.GraphLense;
 import com.android.tools.r8.graph.GraphLense.RewrittenPrototypeDescription;
 import com.android.tools.r8.graph.GraphLense.RewrittenPrototypeDescription.RemovedArgumentInfo;
+import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.PrimitiveTypeLatticeElement;
 import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
 import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
@@ -174,8 +178,8 @@
     return appInfo;
   }
 
-  public TypeLatticeElement getTypeLattice(DexType type, boolean nullable) {
-    return TypeLatticeElement.fromDexType(type, nullable, appInfo);
+  public TypeLatticeElement getTypeLattice(DexType type, Nullability nullability) {
+    return TypeLatticeElement.fromDexType(type, nullability, appInfo);
   }
 
   // SSA construction uses a worklist of basic blocks reachable from the entry and their
@@ -815,7 +819,7 @@
     Position position = source.getCanonicalDebugPositionAtOffset(moveExceptionItem.targetOffset);
     if (moveExceptionDest >= 0) {
       TypeLatticeElement typeLattice =
-          TypeLatticeElement.fromDexType(moveExceptionItem.guard, false, appInfo);
+          TypeLatticeElement.fromDexType(moveExceptionItem.guard, definitelyNotNull(), appInfo);
       Value out = writeRegister(moveExceptionDest, typeLattice, ThrowingInfo.NO_THROW, null);
       MoveException moveException = new MoveException(out, moveExceptionItem.guard, options);
       moveException.setPosition(position);
@@ -876,8 +880,9 @@
     assert removedArgumentInfo == null; // Removal of receiver not yet supported.
     DebugLocalInfo local = getOutgoingLocal(register);
     boolean receiverCouldBeNull = context != null && context != method;
+    Nullability nullability = receiverCouldBeNull ? maybeNull() : definitelyNotNull();
     TypeLatticeElement receiver =
-        TypeLatticeElement.fromDexType(method.method.getHolder(), receiverCouldBeNull, appInfo);
+        TypeLatticeElement.fromDexType(method.method.getHolder(), nullability, appInfo);
     Value value = writeRegister(register, receiver, ThrowingInfo.NO_THROW, local);
     addInstruction(new Argument(value));
     value.markAsThis(receiverCouldBeNull);
@@ -1048,7 +1053,7 @@
   public void addCheckCast(int value, DexType type) {
     Value in = readRegister(value, ValueTypeConstraint.OBJECT);
     TypeLatticeElement castTypeLattice =
-        TypeLatticeElement.fromDexType(type, in.getTypeLattice().isNullable(), appInfo);
+        TypeLatticeElement.fromDexType(type, in.getTypeLattice().nullability(), appInfo);
     Value out = writeRegister(value, castTypeLattice, ThrowingInfo.CAN_THROW);
     CheckCast instruction = new CheckCast(out, in, type);
     assert instruction.instructionTypeCanThrow();
@@ -1092,7 +1097,8 @@
   }
 
   public void addConstClass(int dest, DexType type) {
-    TypeLatticeElement typeLattice = TypeLatticeElement.classClassType(appInfo);
+    TypeLatticeElement typeLattice =
+        TypeLatticeElement.classClassType(appInfo, definitelyNotNull());
     Value out = writeRegister(dest, typeLattice, ThrowingInfo.CAN_THROW);
     ConstClass instruction = new ConstClass(out, type);
     assert instruction.instructionTypeCanThrow();
@@ -1107,7 +1113,8 @@
           null /* sourceString */);
     }
     TypeLatticeElement typeLattice =
-        TypeLatticeElement.fromDexType(appInfo.dexItemFactory.methodHandleType, false, appInfo);
+        TypeLatticeElement.fromDexType(
+            appInfo.dexItemFactory.methodHandleType, definitelyNotNull(), appInfo);
     Value out = writeRegister(dest, typeLattice, ThrowingInfo.CAN_THROW);
     ConstMethodHandle instruction = new ConstMethodHandle(out, methodHandle);
     add(instruction);
@@ -1121,14 +1128,16 @@
           null /* sourceString */);
     }
     TypeLatticeElement typeLattice =
-        TypeLatticeElement.fromDexType(appInfo.dexItemFactory.methodTypeType, false, appInfo);
+        TypeLatticeElement.fromDexType(
+            appInfo.dexItemFactory.methodTypeType, definitelyNotNull(), appInfo);
     Value out = writeRegister(dest, typeLattice, ThrowingInfo.CAN_THROW);
     ConstMethodType instruction = new ConstMethodType(out, methodType);
     add(instruction);
   }
 
   public void addConstString(int dest, DexString string) {
-    TypeLatticeElement typeLattice = TypeLatticeElement.stringClassType(appInfo);
+    TypeLatticeElement typeLattice =
+        TypeLatticeElement.stringClassType(appInfo, definitelyNotNull());
     ThrowingInfo throwingInfo =
         options.isGeneratingClassFiles() ? ThrowingInfo.NO_THROW : ThrowingInfo.CAN_THROW;
     add(new ConstString(writeRegister(dest, typeLattice, throwingInfo), string, throwingInfo));
@@ -1136,7 +1145,8 @@
 
   public void addDexItemBasedConstString(int dest, DexReference item) {
     assert method.getOptimizationInfo().useIdentifierNameString();
-    TypeLatticeElement typeLattice = TypeLatticeElement.stringClassType(appInfo);
+    TypeLatticeElement typeLattice =
+        TypeLatticeElement.stringClassType(appInfo, definitelyNotNull());
     Value out = writeRegister(dest, typeLattice, ThrowingInfo.CAN_THROW);
     DexItemBasedConstString instruction = new DexItemBasedConstString(out, item);
     add(instruction);
@@ -1322,7 +1332,9 @@
   public void addInstanceGet(int dest, int object, DexField field) {
     Value in = readRegister(object, ValueTypeConstraint.OBJECT);
     Value out = writeRegister(
-        dest, TypeLatticeElement.fromDexType(field.type, true, appInfo), ThrowingInfo.CAN_THROW);
+        dest,
+        TypeLatticeElement.fromDexType(field.type, maybeNull(), appInfo),
+        ThrowingInfo.CAN_THROW);
     out.setKnownToBeBoolean(field.type == getFactory().booleanType);
     InstanceGet instruction = new InstanceGet(out, in, field);
     assert instruction.instructionTypeCanThrow();
@@ -1608,7 +1620,9 @@
     DexType outType = invoke.getReturnType();
     Value outValue =
         writeRegister(
-            dest, TypeLatticeElement.fromDexType(outType, true, appInfo), ThrowingInfo.CAN_THROW);
+            dest,
+            TypeLatticeElement.fromDexType(outType, maybeNull(), appInfo),
+            ThrowingInfo.CAN_THROW);
     outValue.setKnownToBeBoolean(outType.isBooleanType());
     invoke.setOutValue(outValue);
   }
@@ -1638,7 +1652,8 @@
   public void addNewArrayEmpty(int dest, int size, DexType type) {
     assert type.isArrayType();
     Value in = readRegister(size, ValueTypeConstraint.INT);
-    TypeLatticeElement arrayTypeLattice = TypeLatticeElement.fromDexType(type, false, appInfo);
+    TypeLatticeElement arrayTypeLattice =
+        TypeLatticeElement.fromDexType(type, definitelyNotNull(), appInfo);
     Value out = writeRegister(dest, arrayTypeLattice, ThrowingInfo.CAN_THROW);
     NewArrayEmpty instruction = new NewArrayEmpty(out, in, type);
     assert instruction.instructionTypeCanThrow();
@@ -1652,7 +1667,8 @@
   }
 
   public void addNewInstance(int dest, DexType type) {
-    TypeLatticeElement instanceType = TypeLatticeElement.fromDexType(type, false, appInfo);
+    TypeLatticeElement instanceType =
+        TypeLatticeElement.fromDexType(type, definitelyNotNull(), appInfo);
     Value out = writeRegister(dest, instanceType, ThrowingInfo.CAN_THROW);
     NewInstance instruction = new NewInstance(type, out);
     assert instruction.instructionTypeCanThrow();
@@ -1684,7 +1700,9 @@
 
   public void addStaticGet(int dest, DexField field) {
     Value out = writeRegister(
-        dest, TypeLatticeElement.fromDexType(field.type, true, appInfo), ThrowingInfo.CAN_THROW);
+        dest,
+        TypeLatticeElement.fromDexType(field.type, maybeNull(), appInfo),
+        ThrowingInfo.CAN_THROW);
     out.setKnownToBeBoolean(field.type == getFactory().booleanType);
     StaticGet instruction = new StaticGet(out, field);
     assert instruction.instructionTypeCanThrow();
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
index 42e501a..1387f75 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
@@ -15,6 +15,7 @@
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.JarApplicationReader;
+import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
 import com.android.tools.r8.ir.code.CatchHandlers;
 import com.android.tools.r8.ir.code.Cmp.Bias;
@@ -373,7 +374,8 @@
       builder.addThisArgument(slot.register);
     }
     for (Type type : parameterTypes) {
-      TypeLatticeElement typeLattice = builder.getTypeLattice(application.getType(type), true);
+      TypeLatticeElement typeLattice =
+          builder.getTypeLattice(application.getType(type), Nullability.maybeNull());
       Slot slot = state.readLocal(argumentRegister, type);
       if (type == Type.BOOLEAN_TYPE) {
         builder.addBooleanNonThisArgument(slot.register);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
index 5814486..08e4ee5 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
@@ -15,6 +15,7 @@
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.IRCode;
@@ -283,7 +284,8 @@
     if (lambdaInstanceValue == null) {
       // The out value might be empty in case it was optimized out.
       lambdaInstanceValue = code.createValue(
-          TypeLatticeElement.fromDexType(lambdaClass.type, true, appInfo));
+          TypeLatticeElement.fromDexType(
+              lambdaClass.type, Nullability.maybeNull(), appInfo));
     }
 
     // For stateless lambdas we replace InvokeCustom instruction with StaticGet
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/StringConcatRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/StringConcatRewriter.java
index 2be7320..8bad12a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/StringConcatRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/StringConcatRewriter.java
@@ -4,6 +4,8 @@
 
 package com.android.tools.r8.ir.desugar;
 
+import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
+
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.graph.AppInfo;
@@ -338,7 +340,8 @@
 
       // new-instance v0, StringBuilder
       TypeLatticeElement stringBuilderTypeLattice =
-          TypeLatticeElement.fromDexType(factory.stringBuilderType, false, appInfo);
+          TypeLatticeElement.fromDexType(
+              factory.stringBuilderType, definitelyNotNull(), appInfo);
       Value sbInstance = code.createValue(stringBuilderTypeLattice);
       appendInstruction(new NewInstance(factory.stringBuilderType, sbInstance));
 
@@ -360,7 +363,8 @@
       Value concatValue = invokeCustom.outValue();
       if (concatValue == null) {
         // The out value might be empty in case it was optimized out.
-        concatValue = code.createValue(TypeLatticeElement.stringClassType(appInfo));
+        concatValue =
+            code.createValue(TypeLatticeElement.stringClassType(appInfo, definitelyNotNull()));
       }
 
       // Replace the instruction.
@@ -438,7 +442,8 @@
 
       @Override
       Value getOrCreateValue() {
-        Value value = code.createValue(TypeLatticeElement.stringClassType(appInfo));
+        Value value =
+            code.createValue(TypeLatticeElement.stringClassType(appInfo, definitelyNotNull()));
         ThrowingInfo throwingInfo =
             code.options.isGeneratingClassFiles() ? ThrowingInfo.NO_THROW : ThrowingInfo.CAN_THROW;
         appendInstruction(new ConstString(value, factory.createString(str), throwingInfo));
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 5c1fae1..ba01321 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
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.ir.optimize;
 
+import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
 import static com.android.tools.r8.ir.optimize.ReflectionOptimizer.ClassNameComputationInfo.ClassNameComputationOption.CANONICAL_NAME;
 import static com.android.tools.r8.ir.optimize.ReflectionOptimizer.ClassNameComputationInfo.ClassNameComputationOption.NAME;
 import static com.android.tools.r8.ir.optimize.ReflectionOptimizer.ClassNameComputationInfo.ClassNameComputationOption.SIMPLE_NAME;
@@ -2100,9 +2101,9 @@
     TypeLatticeElement inTypeLattice = inValue.getTypeLattice();
     TypeLatticeElement outTypeLattice = outValue.getTypeLattice();
     TypeLatticeElement castTypeLattice =
-        TypeLatticeElement.fromDexType(castType, inTypeLattice.isNullable(), appInfo);
+        TypeLatticeElement.fromDexType(castType, inTypeLattice.nullability(), appInfo);
 
-    assert inTypeLattice.nullElement().lessThanOrEqual(outTypeLattice.nullElement());
+    assert inTypeLattice.nullability().lessThanOrEqual(outTypeLattice.nullability());
 
     if (inTypeLattice.lessThanOrEqual(castTypeLattice, appInfo)) {
       // 1) Trivial cast.
@@ -2152,7 +2153,7 @@
     Value inValue = instanceOf.value();
     TypeLatticeElement inType = inValue.getTypeLattice();
     TypeLatticeElement instanceOfType =
-        TypeLatticeElement.fromDexType(instanceOf.type(), inType.isNullable(), appInfo);
+        TypeLatticeElement.fromDexType(instanceOf.type(), inType.nullability(), appInfo);
 
     InstanceOfResult result = InstanceOfResult.UNKNOWN;
     if (inType.isDefinitelyNull()) {
@@ -3177,13 +3178,12 @@
         InstructionListIterator throwNullInsnIterator = throwNullBlock.listIterator();
 
         // Insert 'null' constant.
-        Value nullValue = code.createValue(TypeLatticeElement.NULL, gotoInsn.getLocalInfo());
-        ConstNumber nullConstant = new ConstNumber(nullValue, 0);
+        ConstNumber nullConstant = code.createConstNull(gotoInsn.getLocalInfo());
         nullConstant.setPosition(insn.getPosition());
         throwNullInsnIterator.add(nullConstant);
 
         // Replace Goto with Throw.
-        Throw notReachableThrow = new Throw(nullValue);
+        Throw notReachableThrow = new Throw(nullConstant.outValue());
         Instruction insnGoto = throwNullInsnIterator.next();
         assert insnGoto.isGoto();
         throwNullInsnIterator.replaceCurrentInstruction(notReachableThrow);
@@ -3486,7 +3486,8 @@
   }
 
   private Value addConstString(IRCode code, InstructionListIterator iterator, String s) {
-    TypeLatticeElement typeLattice = TypeLatticeElement.stringClassType(appInfo);
+    TypeLatticeElement typeLattice =
+        TypeLatticeElement.stringClassType(appInfo, definitelyNotNull());
     Value value = code.createValue(typeLattice);
     ThrowingInfo throwingInfo =
         options.isGeneratingClassFiles() ? ThrowingInfo.NO_THROW : ThrowingInfo.CAN_THROW;
@@ -3519,7 +3520,7 @@
     DexType javaLangSystemType = dexItemFactory.createType("Ljava/lang/System;");
     DexType javaIoPrintStreamType = dexItemFactory.createType("Ljava/io/PrintStream;");
     Value out = code.createValue(
-        TypeLatticeElement.fromDexType(javaIoPrintStreamType, false, appInfo));
+        TypeLatticeElement.fromDexType(javaIoPrintStreamType, definitelyNotNull(), appInfo));
 
     DexProto proto = dexItemFactory.createProto(dexItemFactory.voidType, dexItemFactory.objectType);
     DexMethod print = dexItemFactory.createMethod(javaIoPrintStreamType, proto, "print");
@@ -3584,7 +3585,7 @@
         iterator.add(new InvokeVirtual(print, null, ImmutableList.of(out, nul)));
         iterator = isNotNullBlock.listIterator();
         iterator.setInsertionPosition(position);
-        value = code.createValue(TypeLatticeElement.classClassType(appInfo));
+        value = code.createValue(TypeLatticeElement.classClassType(appInfo, definitelyNotNull()));
         iterator.add(new InvokeVirtual(dexItemFactory.objectMethods.getClass, value,
             ImmutableList.of(arguments.get(i))));
         iterator.add(new InvokeVirtual(print, null, ImmutableList.of(out, value)));
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java b/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
index 6da387f..b6bc6c3 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
@@ -143,7 +143,7 @@
           TypeLatticeElement receiverTypeLattice = receiver.getTypeLattice();
           TypeLatticeElement castTypeLattice =
               TypeLatticeElement.fromDexType(
-                  holderType, receiverTypeLattice.isNullable(), appView.appInfo());
+                  holderType, receiverTypeLattice.nullability(), appView.appInfo());
           // Avoid adding trivial cast and up-cast.
           // We should not use strictlyLessThan(castType, receiverType), which detects downcast,
           // due to side-casts, e.g., A (unused) < I, B < I, and cast from A to B.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
index 41faa04..4da9f8d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
@@ -14,6 +14,7 @@
 import com.android.tools.r8.graph.DexItem;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
 import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
 import com.android.tools.r8.ir.code.ConstNumber;
@@ -80,7 +81,8 @@
     if (replacement == null && rule != null
         && rule.hasReturnValue() && rule.getReturnValue().isField()) {
       DexField field = rule.getReturnValue().getField();
-      assert TypeLatticeElement.fromDexType(field.type, true, appInfo) == typeLattice;
+      assert typeLattice
+          == TypeLatticeElement.fromDexType(field.type, Nullability.maybeNull(), appInfo);
       DexEncodedField staticField = appInfo.lookupStaticTarget(field.clazz, field);
       if (staticField != null) {
         Value value = code.createValue(typeLattice, instruction.getLocalInfo());
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
index 7966d3a..4ab54e9 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
@@ -4,6 +4,9 @@
 
 package com.android.tools.r8.ir.optimize;
 
+import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
+import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
+
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppInfo;
@@ -357,7 +360,7 @@
     @Override
     public int createInstruction(IRBuilder builder, Outline outline, int argumentMapIndex) {
       TypeLatticeElement latticeElement =
-          TypeLatticeElement.fromDexType(clazz, false, builder.getAppInfo());
+          TypeLatticeElement.fromDexType(clazz, definitelyNotNull(), builder.getAppInfo());
       Value outValue =
           builder.writeRegister(outline.argumentCount(), latticeElement, ThrowingInfo.CAN_THROW);
       Instruction newInstruction = new NewInstance(clazz, outValue);
@@ -493,7 +496,8 @@
       Value outValue = null;
       if (hasOutValue) {
         TypeLatticeElement latticeElement =
-            TypeLatticeElement.fromDexType(method.proto.returnType, true, builder.getAppInfo());
+            TypeLatticeElement.fromDexType(
+                method.proto.returnType, maybeNull(), builder.getAppInfo());
         outValue =
             builder.writeRegister(outline.argumentCount(), latticeElement, ThrowingInfo.CAN_THROW);
       }
@@ -1398,7 +1402,7 @@
       // Fill in the Argument instructions in the argument block.
       for (int i = 0; i < outline.argumentTypes.size(); i++) {
         TypeLatticeElement typeLattice =
-            TypeLatticeElement.fromDexType(outline.argumentTypes.get(i), true, appInfo);
+            TypeLatticeElement.fromDexType(outline.argumentTypes.get(i), maybeNull(), appInfo);
         builder.addNonThisArgument(i, typeLattice);
       }
       builder.flushArgumentInstructions();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
index 511b077..fe09444 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.optimize;
 
+import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
 import static com.android.tools.r8.utils.DescriptorUtils.getCanonicalNameFromDescriptor;
 import static com.android.tools.r8.utils.DescriptorUtils.getClassNameFromDescriptor;
 import static com.android.tools.r8.utils.DescriptorUtils.getSimpleClassNameFromDescriptor;
@@ -124,7 +125,8 @@
         if (constraints == ConstraintWithTarget.NEVER) {
           continue;
         }
-        TypeLatticeElement typeLattice = TypeLatticeElement.classClassType(appInfo);
+        TypeLatticeElement typeLattice =
+            TypeLatticeElement.classClassType(appInfo, definitelyNotNull());
         Value value = code.createValue(typeLattice, invoke.getLocalInfo());
         ConstClass constClass = new ConstClass(value, type);
         it.replaceCurrentInstruction(constClass);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
index 6a43c03..14be4f3 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
@@ -23,6 +23,7 @@
 import com.android.tools.r8.graph.GraphLense.RewrittenPrototypeDescription.RemovedArgumentInfo;
 import com.android.tools.r8.graph.GraphLense.RewrittenPrototypeDescription.RemovedArgumentsInfo;
 import com.android.tools.r8.graph.TopDownClassHierarchyTraversal;
+import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.CatchHandlers;
@@ -455,7 +456,8 @@
                 : instruction.asStaticPut().inValue();
 
         TypeLatticeElement fieldLatticeType =
-            TypeLatticeElement.fromDexType(fieldType, true, appView.appInfo());
+            TypeLatticeElement.fromDexType(
+                fieldType, Nullability.maybeNull(), appView.appInfo());
         if (!value.getTypeLattice().lessThanOrEqual(fieldLatticeType, appView.appInfo())) {
           // Broken type hierarchy. See FieldTypeTest#test_brokenTypeHierarchy.
           assert options.testing.allowTypeErrors;
@@ -532,15 +534,14 @@
 
     // Insert constant null before the instruction.
     instructionIterator.previous();
-    Value nullValue = new Value(code.valueNumberGenerator.next(), TypeLatticeElement.NULL, null);
-    ConstNumber constNumberInstruction = new ConstNumber(nullValue, 0);
+    ConstNumber constNumberInstruction = code.createConstNull();
     // Note that we only keep position info for throwing instructions in release mode.
     constNumberInstruction.setPosition(options.debug ? instruction.getPosition() : Position.none());
     instructionIterator.add(constNumberInstruction);
     instructionIterator.next();
 
     // Replace the instruction by throw.
-    Throw throwInstruction = new Throw(nullValue);
+    Throw throwInstruction = new Throw(constNumberInstruction.outValue());
     for (Value inValue : instruction.inValues()) {
       if (inValue.hasLocalInfo()) {
         // Add this value as a debug value to avoid changing its live range.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/FieldValueHelper.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/FieldValueHelper.java
index 75d579c..425ce11 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/FieldValueHelper.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/FieldValueHelper.java
@@ -4,6 +4,8 @@
 
 package com.android.tools.r8.ir.optimize.classinliner;
 
+import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
+
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
@@ -92,7 +94,7 @@
           new Phi(
               code.valueNumberGenerator.next(),
               block,
-              TypeLatticeElement.fromDexType(field.type, true, appInfo),
+              TypeLatticeElement.fromDexType(field.type, maybeNull(), appInfo),
               null,
               RegisterReadType.NORMAL);
       ins.put(block, phi);
@@ -140,7 +142,8 @@
     assert root == valueProducingInsn;
     if (defaultValue == null) {
       // If we met newInstance it means that default value is supposed to be used.
-      defaultValue = code.createValue(TypeLatticeElement.fromDexType(field.type, true, appInfo));
+      defaultValue = code.createValue(
+          TypeLatticeElement.fromDexType(field.type, maybeNull(), appInfo));
       ConstNumber defaultValueInsn = new ConstNumber(defaultValue, 0);
       defaultValueInsn.setPosition(root.getPosition());
       LinkedList<Instruction> instructions = block.getInstructions();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupCodeStrategy.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupCodeStrategy.java
index c137cfd..71fbf44 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupCodeStrategy.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupCodeStrategy.java
@@ -4,6 +4,9 @@
 
 package com.android.tools.r8.ir.optimize.lambda.kotlin;
 
+import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
+import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
+
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItemFactory;
@@ -115,7 +118,8 @@
     NewInstance patchedNewInstance = new NewInstance(
         group.getGroupClassType(),
         context.code.createValue(
-            TypeLatticeElement.fromDexType(newInstance.clazz, false, context.appInfo)));
+            TypeLatticeElement.fromDexType(
+                newInstance.clazz, definitelyNotNull(), context.appInfo)));
     context.instructions().replaceCurrentInstruction(patchedNewInstance);
   }
 
@@ -162,7 +166,7 @@
     // Since all captured values of non-primitive types are stored in fields of type
     // java.lang.Object, we need to cast them to appropriate type to satisfy the verifier.
     TypeLatticeElement castTypeLattice =
-        TypeLatticeElement.fromDexType(fieldType, false, context.appInfo);
+        TypeLatticeElement.fromDexType(fieldType, definitelyNotNull(), context.appInfo);
     Value newValue = context.code.createValue(castTypeLattice, newInstanceGet.getLocalInfo());
     newInstanceGet.outValue().replaceUsers(newValue);
     CheckCast cast = new CheckCast(newValue, newInstanceGet.outValue(), fieldType);
@@ -188,7 +192,7 @@
     context.instructions().replaceCurrentInstruction(
         new StaticGet(
             context.code.createValue(
-                TypeLatticeElement.fromDexType(staticGet.getField().type, true, context.appInfo)),
+                TypeLatticeElement.fromDexType(staticGet.getField().type, maybeNull(), context.appInfo)),
             mapSingletonInstanceField(context.factory, staticGet.getField())));
   }
 
@@ -223,7 +227,7 @@
   private Value createValueForType(CodeProcessor context, DexType returnType) {
     return returnType == context.factory.voidType ? null :
         context.code.createValue(
-            TypeLatticeElement.fromDexType(returnType, true, context.appInfo));
+            TypeLatticeElement.fromDexType(returnType, maybeNull(), context.appInfo));
   }
 
   private List<Value> mapInitializerArgs(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
index fa0b28a..59d8717 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
@@ -4,6 +4,8 @@
 
 package com.android.tools.r8.ir.optimize.staticizer;
 
+import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
+
 import com.android.tools.r8.graph.DebugLocalInfo;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedField;
@@ -411,7 +413,8 @@
           it.replaceCurrentInstruction(
               new StaticGet(
                   code.createValue(
-                      TypeLatticeElement.fromDexType(field.type, true, classStaticizer.appInfo),
+                      TypeLatticeElement.fromDexType(
+                          field.type, maybeNull(), classStaticizer.appInfo),
                       outValue.getLocalInfo()),
                   field
               )
@@ -439,7 +442,7 @@
           Value newOutValue = method.proto.returnType.isVoidType() ? null
               : code.createValue(
                   TypeLatticeElement.fromDexType(
-                      method.proto.returnType, true, classStaticizer.appInfo),
+                      method.proto.returnType, maybeNull(), classStaticizer.appInfo),
                   outValue == null ? null : outValue.getLocalInfo());
           it.replaceCurrentInstruction(
               new InvokeStatic(newMethod, newOutValue, invoke.inValues()));
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java
index cdd85e4..0ff9331 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.optimize.string;
 
+import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
 import static com.android.tools.r8.ir.optimize.CodeRewriter.removeOrReplaceByDebugLocalWrite;
 import static com.android.tools.r8.ir.optimize.ReflectionOptimizer.ClassNameComputationInfo.ClassNameComputationOption.CANONICAL_NAME;
 import static com.android.tools.r8.ir.optimize.ReflectionOptimizer.ClassNameComputationInfo.ClassNameComputationOption.NAME;
@@ -242,7 +243,9 @@
       }
       if (name != null) {
         Value stringValue =
-            code.createValue(TypeLatticeElement.stringClassType(appInfo), invoke.getLocalInfo());
+            code.createValue(
+                TypeLatticeElement.stringClassType(appInfo, definitelyNotNull()),
+                invoke.getLocalInfo());
         ConstString constString =
             new ConstString(stringValue, factory.createString(name), throwingInfo);
         it.replaceCurrentInstruction(constString);
@@ -307,11 +310,13 @@
         TypeLatticeElement inType = in.getTypeLattice();
         if (inType.isNullType()) {
           Value nullStringValue =
-              code.createValue(TypeLatticeElement.stringClassType(appInfo), invoke.getLocalInfo());
+              code.createValue(
+                  TypeLatticeElement.stringClassType(appInfo, definitelyNotNull()),
+                  invoke.getLocalInfo());
           ConstString nullString = new ConstString(
               nullStringValue, factory.createString("null"), throwingInfo);
           it.replaceCurrentInstruction(nullString);
-        } else if (inType.nullElement().isDefinitelyNotNull()
+        } else if (inType.nullability().isDefinitelyNotNull()
             && inType.isClassType()
             && inType.asClassTypeLatticeElement().getClassType().equals(factory.stringType)) {
           Value out = invoke.outValue();
@@ -330,7 +335,7 @@
         assert invoke.inValues().size() == 1;
         Value in = invoke.getReceiver();
         TypeLatticeElement inType = in.getTypeLattice();
-        if (inType.nullElement().isDefinitelyNotNull()
+        if (inType.nullability().isDefinitelyNotNull()
             && inType.isClassType()
             && inType.asClassTypeLatticeElement().getClassType().equals(factory.stringType)) {
           Value out = invoke.outValue();
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/SpillMoveSet.java b/src/main/java/com/android/tools/r8/ir/regalloc/SpillMoveSet.java
index fa82d2b..8489866 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/SpillMoveSet.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/SpillMoveSet.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.ir.regalloc;
 
 import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.IRCode;
@@ -44,8 +45,7 @@
   public SpillMoveSet(LinearScanRegisterAllocator allocator, IRCode code, AppInfo appInfo) {
     this.allocator = allocator;
     this.code = code;
-    this.objectType =
-        TypeLatticeElement.fromDexType(appInfo.dexItemFactory.objectType, true, appInfo);
+    this.objectType = TypeLatticeElement.objectClassType(appInfo, Nullability.maybeNull());
     for (BasicBlock block : code.blocks) {
       blockStartMap.put(block.entry().getNumber(), block);
     }
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java
index 88bbc6c..8f51777 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
 import com.android.tools.r8.ir.code.Argument;
 import com.android.tools.r8.ir.code.CatchHandlers;
@@ -187,7 +188,8 @@
       receiverValue =
           builder.writeRegister(
               receiverRegister,
-              TypeLatticeElement.fromDexType(receiver, false, builder.getAppInfo()),
+              TypeLatticeElement.fromDexType(
+                  receiver, Nullability.definitelyNotNull(), builder.getAppInfo()),
               NO_THROW);
       builder.add(new Argument(receiverValue));
       receiverValue.markAsThis(false);
@@ -198,7 +200,8 @@
     for (int i = 0; i < parameters.length; i++) {
       // TODO(zerny): Why does this not call builder.addNonThisArgument?
       TypeLatticeElement typeLattice =
-          TypeLatticeElement.fromDexType(parameters[i], true, builder.getAppInfo());
+          TypeLatticeElement.fromDexType(
+              parameters[i], Nullability.maybeNull(), builder.getAppInfo());
       Value paramValue = builder.writeRegister(paramRegisters[i], typeLattice, NO_THROW);
       paramValues[i] = paramValue;
       builder.add(new Argument(paramValue));
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java b/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java
index 361c8b7..eadec82 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java
@@ -3,7 +3,10 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.analysis.type;
 
+import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
+import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
 import static com.android.tools.r8.ir.analysis.type.TypeLatticeElement.fromDexType;
+import static com.android.tools.r8.ir.analysis.type.TypeLatticeElement.stringClassType;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -121,9 +124,9 @@
       DexType mainClass = appInfo.dexItemFactory.createType(
           DescriptorUtils.javaTypeToDescriptor(NonNullAfterInvoke.class.getCanonicalName()));
       Map<Class<? extends Instruction>, TypeLatticeElement> expectedLattices = ImmutableMap.of(
-          InvokeVirtual.class, fromDexType(appInfo.dexItemFactory.stringType, true, appInfo),
-          NonNull.class, fromDexType(appInfo.dexItemFactory.stringType, false, appInfo),
-          NewInstance.class, fromDexType(assertionErrorType, false, appInfo));
+          InvokeVirtual.class, stringClassType(appInfo, maybeNull()),
+          NonNull.class, stringClassType(appInfo, definitelyNotNull()),
+          NewInstance.class, fromDexType(assertionErrorType, definitelyNotNull(), appInfo));
       forEachOutValue(irCode, (v, l) -> verifyClassTypeLattice(expectedLattices, mainClass, v, l));
     });
   }
@@ -137,9 +140,9 @@
       DexType mainClass = appInfo.dexItemFactory.createType(
           DescriptorUtils.javaTypeToDescriptor(NonNullAfterInvoke.class.getCanonicalName()));
       Map<Class<? extends Instruction>, TypeLatticeElement> expectedLattices = ImmutableMap.of(
-          InvokeVirtual.class, fromDexType(appInfo.dexItemFactory.stringType, true, appInfo),
-          NonNull.class, fromDexType(appInfo.dexItemFactory.stringType, false, appInfo),
-          NewInstance.class, fromDexType(assertionErrorType, false, appInfo));
+          InvokeVirtual.class, stringClassType(appInfo, maybeNull()),
+          NonNull.class, stringClassType(appInfo, definitelyNotNull()),
+          NewInstance.class, fromDexType(assertionErrorType, definitelyNotNull(), appInfo));
       forEachOutValue(irCode, (v, l) -> verifyClassTypeLattice(expectedLattices, mainClass, v, l));
     });
   }
@@ -154,8 +157,8 @@
           DescriptorUtils.javaTypeToDescriptor(NonNullAfterArrayAccess.class.getCanonicalName()));
       Map<Class<? extends Instruction>, TypeLatticeElement> expectedLattices = ImmutableMap.of(
           // An element inside a non-null array could be null.
-          ArrayGet.class, fromDexType(appInfo.dexItemFactory.stringType, true, appInfo),
-          NewInstance.class, fromDexType(assertionErrorType, false, appInfo));
+          ArrayGet.class, fromDexType(appInfo.dexItemFactory.stringType, maybeNull(), appInfo),
+          NewInstance.class, fromDexType(assertionErrorType, definitelyNotNull(), appInfo));
       forEachOutValue(irCode, (v, l) -> {
         if (l.isArrayType()) {
           ArrayTypeLatticeElement lattice = l.asArrayTypeLatticeElement();
@@ -183,8 +186,8 @@
           DescriptorUtils.javaTypeToDescriptor(NonNullAfterArrayAccess.class.getCanonicalName()));
       Map<Class<? extends Instruction>, TypeLatticeElement> expectedLattices = ImmutableMap.of(
           // An element inside a non-null array could be null.
-          ArrayGet.class, fromDexType(appInfo.dexItemFactory.stringType, true, appInfo),
-          NewInstance.class, fromDexType(assertionErrorType, false, appInfo));
+          ArrayGet.class, fromDexType(appInfo.dexItemFactory.stringType, maybeNull(), appInfo),
+          NewInstance.class, fromDexType(assertionErrorType, definitelyNotNull(), appInfo));
       forEachOutValue(irCode, (v, l) -> {
         if (l.isArrayType()) {
           ArrayTypeLatticeElement lattice = l.asArrayTypeLatticeElement();
@@ -213,11 +216,11 @@
       DexType testClass = appInfo.dexItemFactory.createType(
           DescriptorUtils.javaTypeToDescriptor(FieldAccessTest.class.getCanonicalName()));
       Map<Class<? extends Instruction>, TypeLatticeElement> expectedLattices = ImmutableMap.of(
-          Argument.class, fromDexType(testClass, true, appInfo),
-          NonNull.class, fromDexType(testClass, false, appInfo),
+          Argument.class, fromDexType(testClass, maybeNull(), appInfo),
+          NonNull.class, fromDexType(testClass, definitelyNotNull(), appInfo),
           // instance may not be initialized.
-          InstanceGet.class, fromDexType(appInfo.dexItemFactory.stringType, true, appInfo),
-          NewInstance.class, fromDexType(assertionErrorType, false, appInfo));
+          InstanceGet.class, fromDexType(appInfo.dexItemFactory.stringType, maybeNull(), appInfo),
+          NewInstance.class, fromDexType(assertionErrorType, definitelyNotNull(), appInfo));
       forEachOutValue(irCode, (v, l) -> verifyClassTypeLattice(expectedLattices, mainClass, v, l));
     });
   }
@@ -233,11 +236,11 @@
       DexType testClass = appInfo.dexItemFactory.createType(
           DescriptorUtils.javaTypeToDescriptor(FieldAccessTest.class.getCanonicalName()));
       Map<Class<? extends Instruction>, TypeLatticeElement> expectedLattices = ImmutableMap.of(
-          Argument.class, fromDexType(testClass, true, appInfo),
-          NonNull.class, fromDexType(testClass, false, appInfo),
+          Argument.class, fromDexType(testClass, maybeNull(), appInfo),
+          NonNull.class, fromDexType(testClass, definitelyNotNull(), appInfo),
           // instance may not be initialized.
-          InstanceGet.class, fromDexType(appInfo.dexItemFactory.stringType, true, appInfo),
-          NewInstance.class, fromDexType(assertionErrorType, false, appInfo));
+          InstanceGet.class, fromDexType(appInfo.dexItemFactory.stringType, maybeNull(), appInfo),
+          NewInstance.class, fromDexType(assertionErrorType, definitelyNotNull(), appInfo));
       forEachOutValue(irCode, (v, l) -> verifyClassTypeLattice(expectedLattices, mainClass, v, l));
     });
   }
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/type/TypeAnalysisTest.java b/src/test/java/com/android/tools/r8/ir/analysis/type/TypeAnalysisTest.java
index 09f6a01..d85b8dd 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/type/TypeAnalysisTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/type/TypeAnalysisTest.java
@@ -3,6 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.analysis.type;
 
+import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
+import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -282,9 +284,9 @@
     DexType test = appInfo.dexItemFactory.createType("LTest;");
     Map<Class<? extends Instruction>, TypeLatticeElement> expectedLattices = ImmutableMap.of(
         ArrayLength.class, INT,
-        ConstString.class, TypeLatticeElement.stringClassType(appInfo),
-        CheckCast.class, TypeLatticeElement.fromDexType(test, true, appInfo),
-        NewInstance.class, TypeLatticeElement.fromDexType(test, false, appInfo));
+        ConstString.class, TypeLatticeElement.stringClassType(appInfo, definitelyNotNull()),
+        CheckCast.class, TypeLatticeElement.fromDexType(test, maybeNull(), appInfo),
+        NewInstance.class, TypeLatticeElement.fromDexType(test, definitelyNotNull(), appInfo));
     IRCode irCode =
         method.buildIR(appInfo, GraphLense.getIdentityLense(), TEST_OPTIONS, Origin.unknown());
     TypeAnalysis analysis = new TypeAnalysis(appInfo, method);
@@ -313,9 +315,9 @@
             .getMethod();
     DexType test = appInfo.dexItemFactory.createType("LTest;");
     Map<Class<? extends Instruction>, TypeLatticeElement> expectedLattices = ImmutableMap.of(
-      ConstString.class, TypeLatticeElement.stringClassType(appInfo),
+      ConstString.class, TypeLatticeElement.stringClassType(appInfo, definitelyNotNull()),
       InstanceOf.class, INT,
-      StaticGet.class, TypeLatticeElement.fromDexType(test, true, appInfo));
+      StaticGet.class, TypeLatticeElement.fromDexType(test, maybeNull(), appInfo));
     IRCode irCode =
         method.buildIR(appInfo, GraphLense.getIdentityLense(), TEST_OPTIONS, Origin.unknown());
     TypeAnalysis analysis = new TypeAnalysis(appInfo, method);
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 fbfc09b..3aa4394 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
@@ -76,7 +76,7 @@
   }
 
   private TypeLatticeElement element(DexType type) {
-    return TypeLatticeElement.fromDexType(type, true, appInfo);
+    return TypeLatticeElement.fromDexType(type, Nullability.maybeNull(), appInfo);
   }
 
   private ArrayTypeLatticeElement array(int nesting, DexType base) {
@@ -506,7 +506,8 @@
   @Test
   public void testSelfOrderWithoutSubtypingInfo() {
     DexType type = appInfo.dexItemFactory.createType("Lmy/Type;");
-    TypeLatticeElement nonNullType = fromDexType(type, false, appInfo);
+    TypeLatticeElement nonNullType =
+        fromDexType(type, Nullability.definitelyNotNull(), appInfo);
     TypeLatticeElement nullableType = nonNullType.asNullable();
     // TODO(zerny): Once the null lattice is used for null info check that the class-type null is
     // also more specific that the nullableType.
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/TrivialGotoEliminationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/TrivialGotoEliminationTest.java
index 6f22e21..3582403 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/TrivialGotoEliminationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/TrivialGotoEliminationTest.java
@@ -9,6 +9,7 @@
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
 import com.android.tools.r8.ir.code.Argument;
 import com.android.tools.r8.ir.code.BasicBlock;
@@ -129,7 +130,12 @@
     block0.setNumber(0);
     Value value =
         new Value(
-            0, TypeLatticeElement.fromDexType(DexItemFactory.catchAllType, false, appInfo), null);
+            0,
+            TypeLatticeElement.fromDexType(
+                DexItemFactory.catchAllType,
+                Nullability.definitelyNotNull(),
+                appInfo),
+            null);
     instruction = new Argument(value);
     instruction.setPosition(position);
     block0.add(instruction);
diff --git a/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java b/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
index 929dfaa..80a3285 100644
--- a/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
@@ -9,6 +9,7 @@
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.IRCode;
@@ -328,7 +329,8 @@
     InternalOptions options = new InternalOptions();
     AppInfo appInfo = new AppInfo(DexApplication.builder(options.itemFactory, null).build());
     TypeLatticeElement objectType =
-        TypeLatticeElement.fromDexType(options.itemFactory.objectType, true, appInfo);
+        TypeLatticeElement.fromDexType(
+            options.itemFactory.objectType, Nullability.maybeNull(), appInfo);
     CollectMovesIterator moves = new CollectMovesIterator();
     int temp = 42;
     RegisterMoveScheduler scheduler = new RegisterMoveScheduler(moves, temp);
diff --git a/src/test/java/com/android/tools/r8/shaking/assumevalues/SynthesizedRulesFromApiLevelTest.java b/src/test/java/com/android/tools/r8/shaking/assumevalues/SynthesizedRulesFromApiLevelTest.java
index 32ae37a..dd11dc1 100644
--- a/src/test/java/com/android/tools/r8/shaking/assumevalues/SynthesizedRulesFromApiLevelTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/assumevalues/SynthesizedRulesFromApiLevelTest.java
@@ -15,6 +15,8 @@
 import com.android.tools.r8.D8Command;
 import com.android.tools.r8.OutputMode;
 import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm;
 import com.android.tools.r8.jasmin.JasminBuilder;
 import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
 import com.android.tools.r8.shaking.ProguardAssumeValuesRule;
@@ -27,6 +29,7 @@
 import java.nio.file.Path;
 import java.util.List;
 import java.util.function.Consumer;
+import org.junit.Assume;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -230,6 +233,7 @@
 
   @Test
   public void testNoExplicitAssumeValuesRuleNative() throws Exception {
+    Assume.assumeTrue(ToolHelper.getDexVm().isNewerThan(DexVm.ART_7_0_0_HOST));
     runTest(
         AndroidApiLevel.O_MR1,
         AndroidApiLevel.O_MR1,
@@ -240,6 +244,7 @@
 
   @Test
   public void testNoExplicitAssumeValuesRuleCompatPresent() throws Exception {
+    Assume.assumeTrue(ToolHelper.getDexVm().isNewerThan(DexVm.ART_7_0_0_HOST));
     runTest(
         AndroidApiLevel.O,
         AndroidApiLevel.O_MR1,
@@ -250,6 +255,7 @@
 
   @Test
   public void testNoExplicitAssumeValuesRuleCompatUsed() throws Exception {
+    Assume.assumeTrue(ToolHelper.getDexVm().isNewerThan(DexVm.ART_7_0_0_HOST));
     runTest(
         AndroidApiLevel.O,
         AndroidApiLevel.O,
@@ -260,6 +266,7 @@
 
   @Test
   public void testExplicitAssumeValuesRuleWhichMatchAndDontKeepCompat() throws Exception {
+    Assume.assumeTrue(ToolHelper.getDexVm().isNewerThan(DexVm.ART_7_0_0_HOST));
     runTest(
         AndroidApiLevel.O_MR1,
         AndroidApiLevel.O_MR1,
@@ -274,6 +281,7 @@
 
   @Test
   public void testExplicitAssumeValuesRulesWhichMatchAndKeepCompat() throws Exception {
+    Assume.assumeTrue(ToolHelper.getDexVm().isNewerThan(DexVm.ART_7_0_0_HOST));
     String[] rules = new String[] {
         "-assumevalues class * { int SDK_INT return 1..1000; }",
         "-assumevalues class * { % SDK_INT return 1..1000; }",
@@ -297,6 +305,7 @@
 
   @Test
   public void testExplicitAssumeValuesRulesWhichDoesNotMatch() throws Exception {
+    Assume.assumeTrue(ToolHelper.getDexVm().isNewerThan(DexVm.ART_7_0_0_HOST));
     String[] rules = new String[] {
         "-assumevalues class * { !public int SDK_INT return 1..1000; }",
         "-assumevalues class * { !static int SDK_INT return 1..1000; }",
diff --git a/tools/golem_build.py b/tools/golem_build.py
index d55def1..30d46bc 100755
--- a/tools/golem_build.py
+++ b/tools/golem_build.py
@@ -8,8 +8,8 @@
 import gradle
 import sys
 
-GRADLE_ARGS = ['--no-daemon', '-Pno_internal']
-BUILD_TARGETS = ['R8', 'D8', 'R8Lib', 'buildExampleJars', 'CompatDx',
+GRADLE_ARGS = ['--no-daemon']
+BUILD_TARGETS = ['R8', 'D8', 'R8LibApiOnly', 'buildExampleJars', 'CompatDx',
                  'downloadAndroidCts', 'downloadDx']
 
 def Main():
diff --git a/tools/internal_test.py b/tools/internal_test.py
index 4713daf..d34f74b 100755
--- a/tools/internal_test.py
+++ b/tools/internal_test.py
@@ -293,8 +293,7 @@
   git_hash = utils.get_HEAD_sha1()
   log('Running once with hash %s' % git_hash)
   # Run test.py internal testing.
-  # TODO(mkrogh) Change this to --r8lib when we have it working with dependencies relocated.
-  cmd = ['tools/test.py', '--only_internal', '--r8lib_no_deps']
+  cmd = ['tools/test.py', '--only_internal', '--r8lib']
   env = os.environ.copy()
   # Bot does not have a lot of memory.
   env['R8_GRADLE_CORES_PER_FORK'] = '8'