Canonicalize null frame type

Fixes: b/236586903
Change-Id: I255d3a40fc771112f26c941e34300e461f185dde
diff --git a/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java b/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java
index d1b5e9e..88afb27 100644
--- a/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java
+++ b/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java
@@ -551,7 +551,7 @@
       } else {
         return frameTypeType()
             + ".initialized("
-            + dexType(frameType.asInitializedReferenceType().getInitializedType())
+            + dexType(frameType.asInitializedNonNullReferenceType().getInitializedType())
             + ")";
       }
     }
diff --git a/src/main/java/com/android/tools/r8/cf/CfPrinter.java b/src/main/java/com/android/tools/r8/cf/CfPrinter.java
index c3dac32..7472176 100644
--- a/src/main/java/com/android/tools/r8/cf/CfPrinter.java
+++ b/src/main/java/com/android/tools/r8/cf/CfPrinter.java
@@ -457,7 +457,12 @@
 
   private void print(FrameType type) {
     if (type.isInitializedReferenceType()) {
-      appendType(type.asInitializedReferenceType().getInitializedType());
+      if (type.isNullType()) {
+        builder.append("null");
+      } else {
+        assert type.isInitializedNonNullReferenceType();
+        appendType(type.asInitializedNonNullReferenceType().getInitializedType());
+      }
     } else if (type.isUninitializedNew()) {
       builder.append("uninitialized ").append(getLabel(type.getUninitializedLabel()));
     } else {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfArrayLoad.java b/src/main/java/com/android/tools/r8/cf/code/CfArrayLoad.java
index 0bae2fd..cec4126 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfArrayLoad.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfArrayLoad.java
@@ -115,12 +115,12 @@
             (state, head) -> {
               if (head.isNullType()) {
                 return getType() == MemberType.OBJECT
-                    ? state.push(config, FrameType.initialized(DexItemFactory.nullValueType))
+                    ? state.push(config, FrameType.nullType())
                     : state.push(appView, config, getType());
               }
               return state.push(
                   config,
-                  head.asInitializedReferenceType()
+                  head.asInitializedNonNullReferenceType()
                       .getInitializedType()
                       .toArrayElementType(dexItemFactory));
             });
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFrame.java b/src/main/java/com/android/tools/r8/cf/code/CfFrame.java
index f989239..637ad81 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfFrame.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFrame.java
@@ -259,11 +259,9 @@
   }
 
   private void internalRegisterUse(UseRegistry<?> registry, FrameType frameType) {
-    if (frameType.isInitializedReferenceType()) {
-      if (frameType.isNullType()) {
-        return;
-      }
-      registry.registerTypeReference(frameType.asInitializedReferenceType().getInitializedType());
+    if (frameType.isInitializedNonNullReferenceType()) {
+      registry.registerTypeReference(
+          frameType.asInitializedNonNullReferenceType().getInitializedType());
     } else if (frameType.isUninitializedNew()) {
       registry.registerTypeReference(frameType.asUninitializedNew().getUninitializedNewType());
     }
@@ -303,12 +301,12 @@
   public static PreciseFrameType getInitializedFrameType(
       UninitializedFrameType unInit, UninitializedFrameType other, DexType newType) {
     if (unInit.isUninitializedThis() && other.isUninitializedThis()) {
-      return FrameType.initialized(newType);
+      return FrameType.initializedNonNullReference(newType);
     }
     if (unInit.isUninitializedNew()
         && other.isUninitializedNew()
         && unInit.getUninitializedLabel() == other.getUninitializedLabel()) {
-      return FrameType.initialized(newType);
+      return FrameType.initializedNonNullReference(newType);
     }
     return other;
   }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFrameVerificationHelper.java b/src/main/java/com/android/tools/r8/cf/code/CfFrameVerificationHelper.java
index b145f37..a4131e9 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfFrameVerificationHelper.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFrameVerificationHelper.java
@@ -151,7 +151,8 @@
               "Could not assign " + guard.getTypeName() + " to java.lang.Throwable",
               appView);
         }
-        Deque<PreciseFrameType> sourceStack = ImmutableDeque.of(FrameType.initialized(guard));
+        Deque<PreciseFrameType> sourceStack =
+            ImmutableDeque.of(FrameType.initializedNonNullReference(guard));
         AssignabilityResult assignabilityResult =
             assignability.isStackAssignable(sourceStack, destinationFrame.getStack());
         if (assignabilityResult.isFailed()) {
diff --git a/src/main/java/com/android/tools/r8/cf/code/frame/BaseFrameType.java b/src/main/java/com/android/tools/r8/cf/code/frame/BaseFrameType.java
index e8eb7b3..f7ce302 100644
--- a/src/main/java/com/android/tools/r8/cf/code/frame/BaseFrameType.java
+++ b/src/main/java/com/android/tools/r8/cf/code/frame/BaseFrameType.java
@@ -76,6 +76,11 @@
   }
 
   @Override
+  public NullFrameType asNullType() {
+    return null;
+  }
+
+  @Override
   public boolean isObject() {
     return false;
   }
@@ -139,6 +144,16 @@
   }
 
   @Override
+  public boolean isInitializedNonNullReferenceType() {
+    return false;
+  }
+
+  @Override
+  public InitializedNonNullReferenceFrameType asInitializedNonNullReferenceType() {
+    return null;
+  }
+
+  @Override
   public boolean isWide() {
     return false;
   }
diff --git a/src/main/java/com/android/tools/r8/cf/code/frame/FrameType.java b/src/main/java/com/android/tools/r8/cf/code/frame/FrameType.java
index 823380f..6c5314b 100644
--- a/src/main/java/com/android/tools/r8/cf/code/frame/FrameType.java
+++ b/src/main/java/com/android/tools/r8/cf/code/frame/FrameType.java
@@ -65,7 +65,17 @@
 
   static InitializedReferenceFrameType initializedReference(DexType type) {
     assert type.isReferenceType();
-    return new InitializedReferenceFrameType(type);
+    return type.isNullValueType() ? nullType() : initializedNonNullReference(type);
+  }
+
+  static InitializedNonNullReferenceFrameType initializedNonNullReference(DexType type) {
+    assert type.isReferenceType();
+    assert !type.isNullValueType();
+    return new InitializedNonNullReferenceFrameType(type);
+  }
+
+  static NullFrameType nullType() {
+    return NullFrameType.SINGLETON;
   }
 
   static PrimitiveFrameType primitive(DexType type) {
@@ -116,7 +126,7 @@
     assert memberType.isPrecise();
     switch (memberType) {
       case OBJECT:
-        return FrameType.initialized(factory.objectType);
+        return FrameType.initializedNonNullReference(factory.objectType);
       case BOOLEAN_OR_BYTE:
       case CHAR:
       case SHORT:
@@ -165,6 +175,10 @@
 
   InitializedReferenceFrameType asInitializedReferenceType();
 
+  boolean isInitializedNonNullReferenceType();
+
+  InitializedNonNullReferenceFrameType asInitializedNonNullReferenceType();
+
   boolean isInt();
 
   boolean isLong();
@@ -175,6 +189,8 @@
 
   boolean isNullType();
 
+  NullFrameType asNullType();
+
   boolean isObject();
 
   boolean isOneWord();
diff --git a/src/main/java/com/android/tools/r8/cf/code/frame/InitializedNonNullReferenceFrameType.java b/src/main/java/com/android/tools/r8/cf/code/frame/InitializedNonNullReferenceFrameType.java
new file mode 100644
index 0000000..3f6ebeb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/cf/code/frame/InitializedNonNullReferenceFrameType.java
@@ -0,0 +1,147 @@
+// Copyright (c) 2022, 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.cf.code.frame;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexTypeUtils;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.naming.NamingLens;
+import org.objectweb.asm.Opcodes;
+
+public class InitializedNonNullReferenceFrameType extends BaseFrameType
+    implements InitializedReferenceFrameType {
+
+  private final DexType type;
+
+  InitializedNonNullReferenceFrameType(DexType type) {
+    assert type != null;
+    assert type.isReferenceType();
+    assert !type.isNullValueType();
+    this.type = type;
+  }
+
+  @Override
+  public boolean isPrecise() {
+    return true;
+  }
+
+  @Override
+  public PreciseFrameType asPrecise() {
+    return this;
+  }
+
+  @Override
+  public boolean isInitializedReferenceType() {
+    return true;
+  }
+
+  @Override
+  public InitializedNonNullReferenceFrameType asInitializedReferenceType() {
+    return this;
+  }
+
+  @Override
+  public boolean isInitializedNonNullReferenceType() {
+    return true;
+  }
+
+  @Override
+  public InitializedNonNullReferenceFrameType asInitializedNonNullReferenceType() {
+    return this;
+  }
+
+  @Override
+  public SingleFrameType join(
+      AppView<? extends AppInfoWithClassHierarchy> appView, SingleFrameType frameType) {
+    if (equals(frameType) || frameType.isNullType()) {
+      return this;
+    }
+    if (frameType.isOneWord() || frameType.isPrimitive() || frameType.isUninitialized()) {
+      return FrameType.oneWord();
+    }
+    DexType otherType = frameType.asInitializedNonNullReferenceType().getInitializedType();
+    DexType joinType =
+        DexTypeUtils.toDexType(
+            appView, type.toTypeElement(appView).join(otherType.toTypeElement(appView), appView));
+    return FrameType.initializedNonNullReference(joinType);
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj) {
+      return true;
+    }
+    if (obj == null || getClass() != obj.getClass()) {
+      return false;
+    }
+    InitializedNonNullReferenceFrameType initializedType =
+        (InitializedNonNullReferenceFrameType) obj;
+    return type == initializedType.type;
+  }
+
+  @Override
+  public int hashCode() {
+    return type.hashCode();
+  }
+
+  @Override
+  public String toString() {
+    return "Initialized(" + type.toString() + ")";
+  }
+
+  @Override
+  public Object getTypeOpcode(GraphLens graphLens, NamingLens namingLens) {
+    DexType rewrittenType = graphLens.lookupType(type);
+    assert rewrittenType != DexItemFactory.nullValueType;
+    switch (rewrittenType.toShorty()) {
+      case 'L':
+        return namingLens.lookupInternalName(rewrittenType);
+      case 'I':
+        return Opcodes.INTEGER;
+      case 'F':
+        return Opcodes.FLOAT;
+      case 'J':
+        return Opcodes.LONG;
+      case 'D':
+        return Opcodes.DOUBLE;
+      default:
+        throw new Unreachable("Unexpected value type: " + rewrittenType);
+    }
+  }
+
+  @Override
+  public SingleFrameType asSingle() {
+    return this;
+  }
+
+  @Override
+  public boolean isInitialized() {
+    return true;
+  }
+
+  @Override
+  public DexType getInitializedType() {
+    return type;
+  }
+
+  @Override
+  public DexType getInitializedType(DexItemFactory dexItemFactory) {
+    return getInitializedType();
+  }
+
+  @Override
+  public boolean isObject() {
+    return true;
+  }
+
+  @Override
+  public DexType getObjectType(DexType context) {
+    return type;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/cf/code/frame/InitializedReferenceFrameType.java b/src/main/java/com/android/tools/r8/cf/code/frame/InitializedReferenceFrameType.java
index 27ada3e..1fd0f73 100644
--- a/src/main/java/com/android/tools/r8/cf/code/frame/InitializedReferenceFrameType.java
+++ b/src/main/java/com/android/tools/r8/cf/code/frame/InitializedReferenceFrameType.java
@@ -4,154 +4,9 @@
 
 package com.android.tools.r8.cf.code.frame;
 
-import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.DexTypeUtils;
-import com.android.tools.r8.graph.GraphLens;
-import com.android.tools.r8.naming.NamingLens;
-import org.objectweb.asm.Opcodes;
 
-public class InitializedReferenceFrameType extends BaseFrameType
-    implements InitializedFrameType, SingleFrameType {
+public interface InitializedReferenceFrameType extends InitializedFrameType, SingleFrameType {
 
-  private final DexType type;
-
-  public InitializedReferenceFrameType(DexType type) {
-    assert type != null;
-    assert type.isReferenceType();
-    this.type = type;
-  }
-
-  @Override
-  public boolean isPrecise() {
-    return true;
-  }
-
-  @Override
-  public PreciseFrameType asPrecise() {
-    return this;
-  }
-
-  @Override
-  public boolean isInitializedReferenceType() {
-    return true;
-  }
-
-  @Override
-  public InitializedReferenceFrameType asInitializedReferenceType() {
-    return this;
-  }
-
-  @Override
-  public SingleFrameType join(
-      AppView<? extends AppInfoWithClassHierarchy> appView, SingleFrameType frameType) {
-    if (equals(frameType)) {
-      return this;
-    }
-    if (frameType.isOneWord() || frameType.isPrimitive() || frameType.isUninitialized()) {
-      return FrameType.oneWord();
-    }
-    DexType otherType = frameType.asInitializedReferenceType().getInitializedType();
-    assert type != otherType;
-    assert type.isReferenceType();
-    if (isNullType()) {
-      return otherType.isReferenceType() ? frameType : FrameType.oneWord();
-    }
-    if (frameType.isNullType()) {
-      return this;
-    }
-    assert type.isArrayType() || type.isClassType();
-    assert otherType.isArrayType() || otherType.isClassType();
-    DexType joinType =
-        DexTypeUtils.toDexType(
-            appView, type.toTypeElement(appView).join(otherType.toTypeElement(appView), appView));
-    return FrameType.initializedReference(joinType);
-  }
-
-  @Override
-  public boolean equals(Object obj) {
-    if (this == obj) {
-      return true;
-    }
-    if (obj == null || getClass() != obj.getClass()) {
-      return false;
-    }
-    InitializedReferenceFrameType initializedType = (InitializedReferenceFrameType) obj;
-    return type == initializedType.type;
-  }
-
-  @Override
-  public int hashCode() {
-    return type.hashCode();
-  }
-
-  @Override
-  public String toString() {
-    return "Initialized(" + type.toString() + ")";
-  }
-
-  @Override
-  public Object getTypeOpcode(GraphLens graphLens, NamingLens namingLens) {
-    DexType rewrittenType = graphLens.lookupType(type);
-    if (rewrittenType == DexItemFactory.nullValueType) {
-      return Opcodes.NULL;
-    }
-    switch (rewrittenType.toShorty()) {
-      case 'L':
-        return namingLens.lookupInternalName(rewrittenType);
-      case 'I':
-        return Opcodes.INTEGER;
-      case 'F':
-        return Opcodes.FLOAT;
-      case 'J':
-        return Opcodes.LONG;
-      case 'D':
-        return Opcodes.DOUBLE;
-      default:
-        throw new Unreachable("Unexpected value type: " + rewrittenType);
-    }
-  }
-
-  @Override
-  public SingleFrameType asSingle() {
-    return this;
-  }
-
-  @Override
-  public boolean isWide() {
-    return false;
-  }
-
-  @Override
-  public boolean isInitialized() {
-    return true;
-  }
-
-  public DexType getInitializedType() {
-    return type;
-  }
-
-  @Override
-  public DexType getInitializedType(DexItemFactory dexItemFactory) {
-    return getInitializedType();
-  }
-
-  @Override
-  public boolean isNullType() {
-    return type.isNullValueType();
-  }
-
-  @Override
-  public boolean isObject() {
-    return type.isReferenceType();
-  }
-
-  @Override
-  public DexType getObjectType(DexType context) {
-    assert isObject() : "Unexpected use of getObjectType() for non-object FrameType";
-    return type;
-  }
+  DexType getInitializedType();
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/frame/NullFrameType.java b/src/main/java/com/android/tools/r8/cf/code/frame/NullFrameType.java
new file mode 100644
index 0000000..ee4659a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/cf/code/frame/NullFrameType.java
@@ -0,0 +1,103 @@
+// Copyright (c) 2022, 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.cf.code.frame;
+
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.naming.NamingLens;
+import org.objectweb.asm.Opcodes;
+
+public class NullFrameType extends SingletonFrameType implements InitializedReferenceFrameType {
+
+  static final NullFrameType SINGLETON = new NullFrameType();
+
+  private NullFrameType() {}
+
+  @Override
+  public boolean isInitialized() {
+    return true;
+  }
+
+  @Override
+  public boolean isInitializedReferenceType() {
+    return true;
+  }
+
+  @Override
+  public NullFrameType asInitializedReferenceType() {
+    return this;
+  }
+
+  @Override
+  public boolean isNullType() {
+    return true;
+  }
+
+  @Override
+  public NullFrameType asNullType() {
+    return this;
+  }
+
+  @Override
+  public boolean isObject() {
+    return true;
+  }
+
+  @Override
+  public boolean isPrecise() {
+    return true;
+  }
+
+  @Override
+  public PreciseFrameType asPrecise() {
+    return this;
+  }
+
+  @Override
+  public SingleFrameType asSingle() {
+    return this;
+  }
+
+  @Override
+  public DexType getInitializedType(DexItemFactory dexItemFactory) {
+    return getInitializedType();
+  }
+
+  @Override
+  public DexType getInitializedType() {
+    return DexItemFactory.nullValueType;
+  }
+
+  @Override
+  public DexType getObjectType(DexType context) {
+    return getInitializedType();
+  }
+
+  @Override
+  public Object getTypeOpcode(GraphLens graphLens, NamingLens namingLens) {
+    return Opcodes.NULL;
+  }
+
+  @Override
+  public SingleFrameType join(
+      AppView<? extends AppInfoWithClassHierarchy> appView, SingleFrameType frameType) {
+    if (this == frameType) {
+      return this;
+    }
+    if (frameType.isOneWord() || frameType.isPrimitive() || frameType.isUninitialized()) {
+      return FrameType.oneWord();
+    }
+    assert frameType.isInitializedNonNullReferenceType();
+    return frameType;
+  }
+
+  @Override
+  public String toString() {
+    return "null";
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/cf/code/frame/PreciseFrameType.java b/src/main/java/com/android/tools/r8/cf/code/frame/PreciseFrameType.java
index dfa0786..250ce67 100644
--- a/src/main/java/com/android/tools/r8/cf/code/frame/PreciseFrameType.java
+++ b/src/main/java/com/android/tools/r8/cf/code/frame/PreciseFrameType.java
@@ -11,20 +11,17 @@
 
   @Override
   default PreciseFrameType map(Function<DexType, DexType> fn) {
-    if (isObject()) {
-      if (isInitialized()) {
-        DexType type = asInitializedReferenceType().getInitializedType();
-        DexType newType = fn.apply(type);
-        if (type != newType) {
-          return FrameType.initializedReference(newType);
-        }
+    if (isInitializedNonNullReferenceType()) {
+      DexType type = asInitializedNonNullReferenceType().getInitializedType();
+      DexType newType = fn.apply(type);
+      if (type != newType) {
+        return FrameType.initializedNonNullReference(newType);
       }
-      if (isUninitializedNew()) {
-        DexType type = getUninitializedNewType();
-        DexType newType = fn.apply(type);
-        if (type != newType) {
-          return FrameType.uninitializedNew(getUninitializedLabel(), newType);
-        }
+    } else if (isUninitializedNew()) {
+      DexType type = getUninitializedNewType();
+      DexType newType = fn.apply(type);
+      if (type != newType) {
+        return FrameType.uninitializedNew(getUninitializedLabel(), newType);
       }
     }
     return this;
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java
index 44289af..ec420af 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -1061,7 +1061,7 @@
                       || previousMethodSignature.isHorizontallyMergedInstanceInitializer(
                           dexItemFactory)
                   ? FrameType.uninitializedThis()
-                  : FrameType.initialized(previousMethodSignature.getHolderType()),
+                  : FrameType.initializedNonNullReference(previousMethodSignature.getHolderType()),
               helper);
       localIndex++;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ServiceLoaderSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/ServiceLoaderSourceCode.java
index 0aa439a..83730de 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/ServiceLoaderSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/ServiceLoaderSourceCode.java
@@ -87,7 +87,9 @@
     CfLabel tryCatchHandler = new CfLabel();
     builder.add(
         tryCatchHandler,
-        CfFrame.builder().push(FrameType.initialized(factory.throwableType)).build(),
+        CfFrame.builder()
+            .push(FrameType.initializedNonNullReference(factory.throwableType))
+            .build(),
         new CfStore(ValueType.OBJECT, 0),
         new CfNew(factory.serviceLoaderConfigurationErrorType),
         new CfStackInstruction(CfStackInstruction.Opcode.Dup),
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java
index 9788ee2..a4b79c0 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java
@@ -306,7 +306,7 @@
     instructions.add(initializedTrueSecond);
     instructions.add(
         CfFrame.builder()
-            .appendLocal(FrameType.initialized(builder.getFactory().objectType))
+            .appendLocal(FrameType.initializedNonNullReference(builder.getFactory().objectType))
             .build());
     instructions.add(new CfLoad(ValueType.OBJECT, 0));
     instructions.add(new CfMonitor(Monitor.Type.EXIT));
@@ -316,8 +316,8 @@
     instructions.add(tryCatchTarget);
     instructions.add(
         CfFrame.builder()
-            .appendLocal(FrameType.initialized(builder.getFactory().objectType))
-            .push(FrameType.initialized(builder.getFactory().throwableType))
+            .appendLocal(FrameType.initializedNonNullReference(builder.getFactory().objectType))
+            .push(FrameType.initializedNonNullReference(builder.getFactory().throwableType))
             .build());
     instructions.add(new CfStore(ValueType.OBJECT, 1));
     instructions.add(new CfLoad(ValueType.OBJECT, 0));
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/apiconverter/NullableConversionCfCodeProvider.java b/src/main/java/com/android/tools/r8/ir/synthetic/apiconverter/NullableConversionCfCodeProvider.java
index fb04da0..a32d51e 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/apiconverter/NullableConversionCfCodeProvider.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/apiconverter/NullableConversionCfCodeProvider.java
@@ -91,9 +91,9 @@
       CfFrame frame =
           CfFrame.builder()
               .appendLocal(FrameType.initialized(typeArray))
-              .appendLocal(FrameType.initialized(factory.intType))
+              .appendLocal(FrameType.intType())
               .appendLocal(FrameType.initialized(convertedTypeArray))
-              .appendLocal(FrameType.initialized(factory.intType))
+              .appendLocal(FrameType.intType())
               .build();
 
       // int t1 = arg.length;
diff --git a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/CfOpenClosedInterfacesAnalysis.java b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/CfOpenClosedInterfacesAnalysis.java
index f37e786..9e4771a 100644
--- a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/CfOpenClosedInterfacesAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/CfOpenClosedInterfacesAnalysis.java
@@ -172,13 +172,13 @@
         stack -> {
           FrameType array = stack.peekFirst();
           FrameType value = stack.peekLast();
-          if (array.isInitializedReferenceType()) {
+          if (array.isInitializedNonNullReferenceType()) {
             DexType arrayType = array.asInitializedReferenceType().getInitializedType();
             if (arrayType.isArrayType()) {
               processAssignment(
                   value, arrayType.toArrayElementType(dexItemFactory), openInterfaceConsumer);
             } else {
-              assert arrayType.isNullValueType();
+              assert false;
             }
           } else {
             assert false;
@@ -233,9 +233,9 @@
 
   private void processAssignment(
       FrameType fromType, DexType toType, Consumer<DexClass> openInterfaceConsumer) {
-    if (fromType.isInitializedReferenceType() && !fromType.isNullType()) {
+    if (fromType.isInitializedNonNullReferenceType()) {
       processAssignment(
-          fromType.asInitializedReferenceType().getInitializedType(),
+          fromType.asInitializedNonNullReferenceType().getInitializedType(),
           toType,
           openInterfaceConsumer);
     }
@@ -408,7 +408,9 @@
         } else {
           initialState =
               initialState.storeLocal(
-                  localIndex, FrameType.initialized(context.getHolderType()), config);
+                  localIndex,
+                  FrameType.initializedNonNullReference(context.getHolderType()),
+                  config);
         }
         localIndex++;
       }
diff --git a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/ConcreteCfFrameState.java b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/ConcreteCfFrameState.java
index 67cc043..f2a7432 100644
--- a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/ConcreteCfFrameState.java
+++ b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/ConcreteCfFrameState.java
@@ -231,8 +231,8 @@
   }
 
   private static boolean isArrayTypeOrNull(FrameType frameType) {
-    if (frameType.isInitializedReferenceType()
-        && frameType.asInitializedReferenceType().getInitializedType().isArrayType()) {
+    if (frameType.isInitializedNonNullReferenceType()
+        && frameType.asInitializedNonNullReferenceType().getInitializedType().isArrayType()) {
       return true;
     }
     return frameType.isNullType();
@@ -299,7 +299,7 @@
     ArrayDeque<PreciseFrameType> newStack = new ArrayDeque<>();
     int newStackHeight = 0;
     return new ConcreteCfFrameState(newLocals, newStack, newStackHeight)
-        .push(config, FrameType.initialized(guard));
+        .push(config, FrameType.initializedNonNullReference(guard));
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/ErroneousCfFrameState.java b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/ErroneousCfFrameState.java
index 1274958..9acfefe 100644
--- a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/ErroneousCfFrameState.java
+++ b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/ErroneousCfFrameState.java
@@ -54,15 +54,19 @@
 
   private static String format(FrameType frameType, FormatKind formatKind) {
     if (frameType.isInitialized()) {
-      if (frameType.isObject()) {
-        DexType initializedType = frameType.asInitializedReferenceType().getInitializedType();
-        if (initializedType.isArrayType()) {
-          return initializedType.getTypeName();
-        } else if (initializedType.isClassType()) {
-          return "initialized " + initializedType.getTypeName();
-        } else {
-          assert initializedType.isNullValueType();
+      if (frameType.isInitializedReferenceType()) {
+        if (frameType.isNullType()) {
           return "null";
+        } else {
+          assert frameType.isInitializedNonNullReferenceType();
+          DexType initializedType =
+              frameType.asInitializedNonNullReferenceType().getInitializedType();
+          if (initializedType.isArrayType()) {
+            return initializedType.getTypeName();
+          } else {
+            assert initializedType.isClassType();
+            return "initialized " + initializedType.getTypeName();
+          }
         }
       } else {
         assert frameType.isPrimitive();