Extend stack validator to explicitly check for uninitialized types

Change-Id: I775e80e18b6c7a83fe4009b71c740dad112a132f
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfArrayLength.java b/src/main/java/com/android/tools/r8/cf/code/CfArrayLength.java
index 22421de..4fa3241 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfArrayLength.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfArrayLength.java
@@ -82,6 +82,6 @@
       InitClassLens initClassLens) {
     // ..., arrayref →
     // ..., length
-    frameBuilder.popAndDiscard(factory.objectArrayType).push(factory.intType);
+    frameBuilder.popAndDiscardInitialized(factory.objectArrayType).push(factory.intType);
   }
 }
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 c82e53e..4f851a0 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
@@ -128,7 +128,7 @@
       InitClassLens initClassLens) {
     // ..., arrayref, index →
     // ..., value
-    frameBuilder.popAndDiscard(factory.objectArrayType, factory.intType);
+    frameBuilder.popAndDiscardInitialized(factory.objectArrayType, factory.intType);
     frameBuilder.push(FrameType.fromMemberType(type, factory));
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfArrayStore.java b/src/main/java/com/android/tools/r8/cf/code/CfArrayStore.java
index 5d64188..cc0cd63 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfArrayStore.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfArrayStore.java
@@ -120,6 +120,6 @@
     // ...
     frameBuilder
         .popAndDiscard(FrameType.fromMemberType(type, factory))
-        .popAndDiscard(factory.objectArrayType, factory.intType);
+        .popAndDiscardInitialized(factory.objectArrayType, factory.intType);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfCheckCast.java b/src/main/java/com/android/tools/r8/cf/code/CfCheckCast.java
index 721f3f7..277f2e2 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfCheckCast.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfCheckCast.java
@@ -102,6 +102,6 @@
       InitClassLens initClassLens) {
     // ..., objectref →
     // ..., objectref
-    frameBuilder.popAndDiscard(factory.objectType).push(type);
+    frameBuilder.popAndDiscardInitialized(factory.objectType).push(type);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
index be6176b..2e8eedf 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
@@ -3,6 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.cf.code;
 
+import static com.android.tools.r8.utils.BiPredicateUtils.or;
+
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
@@ -189,7 +191,7 @@
       case Opcodes.GETFIELD:
         // ..., objectref →
         // ..., value
-        frameBuilder.popAndDiscard(field.holder).push(field.type);
+        frameBuilder.popAndDiscardInitialized(field.holder).push(field.type);
         return;
       case Opcodes.GETSTATIC:
         // ..., →
@@ -199,12 +201,18 @@
       case Opcodes.PUTFIELD:
         // ..., objectref, value →
         // ...,
-        frameBuilder.popAndDiscard(field.holder, field.type);
+        frameBuilder
+            .popAndDiscardInitialized(field.type)
+            .pop(
+                field.holder,
+                or(
+                    frameBuilder::isUninitializedThisAndTarget,
+                    frameBuilder::isAssignableAndInitialized));
         return;
       case Opcodes.PUTSTATIC:
         // ..., value →
         // ...
-        frameBuilder.pop(field.type);
+        frameBuilder.popAndDiscardInitialized(field.type);
         return;
       default:
         throw new Unreachable("Unexpected opcode " + opcode);
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 713d764..b2b18b7 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
@@ -452,7 +452,7 @@
       DexType returnType,
       DexItemFactory factory,
       InitClassLens initClassLens) {
-    frameBuilder.verifyFrameAndSet(this);
+    frameBuilder.checkFrameAndSet(this);
   }
 
   public CfFrame markInstantiated(FrameType uninitializedType, DexType initType) {
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 7ce2440..bc0da18 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
@@ -4,6 +4,8 @@
 
 package com.android.tools.r8.cf.code;
 
+import static com.android.tools.r8.utils.BiPredicateUtils.or;
+
 import com.android.tools.r8.cf.code.CfFrame.FrameType;
 import com.android.tools.r8.graph.CfCodeStackMapValidatingException;
 import com.android.tools.r8.graph.DexItemFactory;
@@ -70,50 +72,60 @@
   }
 
   public FrameType readLocal(int index, DexType expectedType) {
-    verifyFrameIsSet();
+    checkFrameIsSet();
     FrameType frameType = currentFrame.getLocals().get(index);
     if (frameType == null) {
       throw CfCodeStackMapValidatingException.error("No local at index " + index);
     }
-    verifyIsAssignable(frameType, expectedType);
+    checkIsAssignable(
+        frameType,
+        expectedType,
+        or(
+            this::isUninitializedThisAndTarget,
+            this::isUninitializedNewAndTarget,
+            this::isAssignableAndInitialized));
     return frameType;
   }
 
   public void storeLocal(int index, FrameType frameType) {
-    verifyFrameIsSet();
+    checkFrameIsSet();
     currentFrame.getLocals().put(index, frameType);
   }
 
   public FrameType pop() {
-    verifyFrameIsSet();
+    checkFrameIsSet();
     if (currentFrame.getStack().isEmpty()) {
       throw CfCodeStackMapValidatingException.error("Cannot pop() from an empty stack");
     }
     return currentFrame.getStack().removeLast();
   }
 
-  public FrameType pop(DexType expectedType) {
+  public FrameType popInitialized(DexType expectedType) {
+    return pop(expectedType, this::isAssignableAndInitialized);
+  }
+
+  public FrameType pop(DexType expectedType, BiPredicate<FrameType, DexType> isAssignable) {
     FrameType frameType = pop();
-    verifyIsAssignable(frameType, expectedType);
+    checkIsAssignable(frameType, expectedType, isAssignable);
     return frameType;
   }
 
-  public CfFrameVerificationHelper popAndDiscard(DexType... expectedTypes) {
-    verifyFrameIsSet();
+  public CfFrameVerificationHelper popAndDiscardInitialized(DexType... expectedTypes) {
+    checkFrameIsSet();
     for (int i = expectedTypes.length - 1; i >= 0; i--) {
-      pop(expectedTypes[i]);
+      popInitialized(expectedTypes[i]);
     }
     return this;
   }
 
   public FrameType pop(FrameType expectedType) {
     FrameType frameType = pop();
-    verifyIsAssignable(frameType, expectedType);
+    checkIsAssignable(frameType, expectedType);
     return frameType;
   }
 
   public CfFrameVerificationHelper popAndDiscard(FrameType... expectedTypes) {
-    verifyFrameIsSet();
+    checkFrameIsSet();
     for (int i = expectedTypes.length - 1; i >= 0; i--) {
       pop(expectedTypes[i]);
     }
@@ -121,17 +133,20 @@
   }
 
   public void popAndInitialize(DexType context, DexType methodHolder) {
-    verifyFrameIsSet();
-    FrameType objectRef = pop(factory.objectType);
+    checkFrameIsSet();
+    FrameType objectRef =
+        pop(
+            factory.objectType,
+            or(this::isUninitializedThisAndTarget, this::isUninitializedNewAndTarget));
     CfFrame newFrame =
         currentFrame.markInstantiated(
             objectRef, objectRef.isUninitializedNew() ? methodHolder : context);
     setNoFrame();
-    verifyFrameAndSet(newFrame);
+    checkFrameAndSet(newFrame);
   }
 
   public CfFrameVerificationHelper push(FrameType type) {
-    verifyFrameIsSet();
+    checkFrameIsSet();
     currentFrame.getStack().addLast(type);
     return this;
   }
@@ -153,7 +168,7 @@
           if (destinationFrame == null) {
             throw CfCodeStackMapValidatingException.error("No frame for target catch range target");
           }
-          verifyStackIsAssignable(
+          checkStackIsAssignable(
               destinationFrame.getStack(), throwStack, factory, isJavaAssignable);
         }
       }
@@ -162,15 +177,15 @@
     return this;
   }
 
-  private void verifyFrameIsSet() {
+  private void checkFrameIsSet() {
     if (currentFrame == NO_FRAME) {
       throw CfCodeStackMapValidatingException.error("Unexpected state change");
     }
   }
 
-  public void verifyFrameAndSet(CfFrame newFrame) {
+  public void checkFrameAndSet(CfFrame newFrame) {
     if (currentFrame != NO_FRAME) {
-      verifyFrame(newFrame);
+      checkFrame(newFrame);
     }
     setFrame(newFrame);
   }
@@ -182,7 +197,7 @@
             new Int2ReferenceAVLTreeMap<>(frame.getLocals()), new ArrayDeque<>(frame.getStack()));
   }
 
-  public void verifyExceptionEdges() {
+  public void checkExceptionEdges() {
     for (CfTryCatch currentCatchRange : currentCatchRanges) {
       for (CfLabel target : currentCatchRange.targets) {
         CfFrame destinationFrame = stateMap.get(target);
@@ -192,7 +207,7 @@
         // We have to check all current handler targets have assignable locals and a 1-element
         // stack assignable to throwable. It is not required that the the thrown error is
         // handled.
-        verifyLocalsIsAssignable(
+        checkLocalsIsAssignable(
             currentFrame.getLocals(), destinationFrame.getLocals(), factory, isJavaAssignable);
       }
     }
@@ -202,19 +217,19 @@
     return currentFrame;
   }
 
-  public void verifyTarget(CfLabel label) {
-    verifyFrame(stateMap.get(label));
+  public void checkTarget(CfLabel label) {
+    checkFrame(stateMap.get(label));
   }
 
-  public void verifyFrame(CfFrame destinationFrame) {
+  public void checkFrame(CfFrame destinationFrame) {
     if (destinationFrame == null) {
       throw CfCodeStackMapValidatingException.error("No destination frame");
     }
-    verifyFrame(destinationFrame.getLocals(), destinationFrame.getStack());
+    checkFrame(destinationFrame.getLocals(), destinationFrame.getStack());
   }
 
-  public void verifyFrame(Int2ReferenceSortedMap<FrameType> locals, Deque<FrameType> stack) {
-    verifyIsAssignable(
+  public void checkFrame(Int2ReferenceSortedMap<FrameType> locals, Deque<FrameType> stack) {
+    checkIsAssignable(
         currentFrame.getLocals(),
         currentFrame.getStack(),
         locals,
@@ -227,30 +242,37 @@
     currentFrame = NO_FRAME;
   }
 
-  public void clearStack() {
-    verifyFrameIsSet();
-    currentFrame.getStack().clear();
+  public boolean isUninitializedThisAndTarget(FrameType source, DexType target) {
+    if (!source.isUninitializedThis()) {
+      return false;
+    }
+    return target == factory.objectType || graphLens.lookupClassType(target) == context;
   }
 
-  public void verifyIsAssignable(FrameType source, DexType target) {
+  public boolean isUninitializedNewAndTarget(FrameType source, DexType target) {
+    if (!source.isUninitializedNew()) {
+      return false;
+    }
+    return target == factory.objectType || graphLens.lookupClassType(target) == context;
+  }
+
+  public boolean isAssignableAndInitialized(FrameType source, DexType target) {
     if (!source.isInitialized()) {
-      DexType rewrittenTarget = graphLens.lookupClassType(target);
-      if (source.isUninitializedThis() && rewrittenTarget == context) {
-        return;
-      }
-      if (rewrittenTarget == factory.objectType) {
-        return;
-      }
-      throw CfCodeStackMapValidatingException.error(
-          "The expected type " + source + " is not assignable to " + target.toSourceString());
+      return false;
     }
-    if (!isJavaAssignable.test(source.getInitializedType(), target)) {
-      throw CfCodeStackMapValidatingException.error(
-          "The expected type " + source + " is not assignable to " + target.toSourceString());
-    }
+    return isJavaAssignable.test(source.getInitializedType(), target);
   }
 
-  public void verifyIsAssignable(FrameType source, FrameType target) {
+  public void checkIsAssignable(
+      FrameType source, DexType target, BiPredicate<FrameType, DexType> predicate) {
+    if (predicate.test(source, target)) {
+      return;
+    }
+    throw CfCodeStackMapValidatingException.error(
+        "The expected type " + source + " is not assignable to " + target.toSourceString());
+  }
+
+  public void checkIsAssignable(FrameType source, FrameType target) {
     if (!canBeAssigned(source, target, factory, isJavaAssignable)) {
       throw CfCodeStackMapValidatingException.error(
           "The expected type " + source + " is not assignable to " + target);
@@ -258,18 +280,18 @@
   }
 
   // Based on https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.10.1.4.
-  public static void verifyIsAssignable(
+  public static void checkIsAssignable(
       Int2ReferenceSortedMap<FrameType> sourceLocals,
       Deque<FrameType> sourceStack,
       Int2ReferenceSortedMap<FrameType> destLocals,
       Deque<FrameType> destStack,
       DexItemFactory factory,
       BiPredicate<DexType, DexType> isJavaAssignable) {
-    verifyLocalsIsAssignable(sourceLocals, destLocals, factory, isJavaAssignable);
-    verifyStackIsAssignable(sourceStack, destStack, factory, isJavaAssignable);
+    checkLocalsIsAssignable(sourceLocals, destLocals, factory, isJavaAssignable);
+    checkStackIsAssignable(sourceStack, destStack, factory, isJavaAssignable);
   }
 
-  private static void verifyLocalsIsAssignable(
+  private static void checkLocalsIsAssignable(
       Int2ReferenceSortedMap<FrameType> sourceLocals,
       Int2ReferenceSortedMap<FrameType> destLocals,
       DexItemFactory factory,
@@ -305,7 +327,7 @@
     }
   }
 
-  private static void verifyStackIsAssignable(
+  private static void checkStackIsAssignable(
       Deque<FrameType> sourceStack,
       Deque<FrameType> destStack,
       DexItemFactory factory,
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfGoto.java b/src/main/java/com/android/tools/r8/cf/code/CfGoto.java
index 2f4a549..b3043c9 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfGoto.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfGoto.java
@@ -97,7 +97,7 @@
       DexType returnType,
       DexItemFactory factory,
       InitClassLens initClassLens) {
-    frameBuilder.verifyTarget(target);
+    frameBuilder.checkTarget(target);
     frameBuilder.setNoFrame();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfIf.java b/src/main/java/com/android/tools/r8/cf/code/CfIf.java
index b331673..7653d51 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfIf.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfIf.java
@@ -135,8 +135,8 @@
       InitClassLens initClassLens) {
     // ..., value →
     // ...
-    frameBuilder.pop(
+    frameBuilder.popAndDiscardInitialized(
         type.isObject() ? factory.objectType : type.toPrimitiveType().toDexType(factory));
-    frameBuilder.verifyTarget(target);
+    frameBuilder.checkTarget(target);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfIfCmp.java b/src/main/java/com/android/tools/r8/cf/code/CfIfCmp.java
index 45b426b..3e5c38a 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfIfCmp.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfIfCmp.java
@@ -138,7 +138,7 @@
     // ...
     DexType type =
         this.type.isObject() ? factory.objectType : this.type.toPrimitiveType().toDexType(factory);
-    frameBuilder.popAndDiscard(type, type);
-    frameBuilder.verifyTarget(target);
+    frameBuilder.popAndDiscardInitialized(type, type);
+    frameBuilder.checkTarget(target);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java b/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java
index c18eb57..3cc1f4b 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java
@@ -110,6 +110,6 @@
       InitClassLens initClassLens) {
     // ..., objectref →
     // ..., result
-    frameBuilder.popAndDiscard(factory.objectType).push(factory.intType);
+    frameBuilder.popAndDiscardInitialized(factory.objectType).push(factory.intType);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
index db81a5f..87cae25 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
@@ -354,11 +354,11 @@
     // OR, for static method calls:
     // ..., [arg1, [arg2 ...]] →
     // ...
-    frameBuilder.popAndDiscard(this.method.proto.parameters.values);
+    frameBuilder.popAndDiscardInitialized(this.method.proto.parameters.values);
     if (opcode == Opcodes.INVOKESPECIAL && method.isInstanceInitializer(factory)) {
       frameBuilder.popAndInitialize(context, method.holder);
     } else if (opcode != Opcodes.INVOKESTATIC) {
-      frameBuilder.pop(method.holder);
+      frameBuilder.popInitialized(method.holder);
     }
     if (this.method.proto.returnType != factory.voidType) {
       frameBuilder.push(this.method.proto.returnType);
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java b/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
index 88ffb82..3a98a8e 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
@@ -167,7 +167,7 @@
       InitClassLens initClassLens) {
     // ..., [arg1, [arg2 ...]] →
     // ...
-    frameBuilder.popAndDiscard(callSite.methodProto.parameters.values);
+    frameBuilder.popAndDiscardInitialized(callSite.methodProto.parameters.values);
     if (callSite.methodProto.returnType != factory.voidType) {
       frameBuilder.push(callSite.methodProto.returnType);
     }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java b/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java
index 9180511..bac5536 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java
@@ -114,7 +114,7 @@
     // ..., count1, [count2, ...] →
     // ..., arrayref
     for (int i = 0; i < dimensions; i++) {
-      frameBuilder.pop(factory.intType);
+      frameBuilder.popInitialized(factory.intType);
     }
     frameBuilder.push(type);
   }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNewArray.java b/src/main/java/com/android/tools/r8/cf/code/CfNewArray.java
index bd36756..78200b7 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfNewArray.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNewArray.java
@@ -150,6 +150,6 @@
     // ..., count →
     // ..., arrayref
     assert type.isArrayType();
-    frameBuilder.popAndDiscard(factory.intType).push(type);
+    frameBuilder.popAndDiscardInitialized(factory.intType).push(type);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfReturn.java b/src/main/java/com/android/tools/r8/cf/code/CfReturn.java
index 51d1283..2b4eed5 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfReturn.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfReturn.java
@@ -113,7 +113,7 @@
       DexItemFactory factory,
       InitClassLens initClassLens) {
     assert returnType != null;
-    frameBuilder.popAndDiscard(returnType);
+    frameBuilder.popAndDiscardInitialized(returnType);
     frameBuilder.setNoFrame();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfStackInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfStackInstruction.java
index bfdbe0a..4fd9829 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfStackInstruction.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfStackInstruction.java
@@ -368,7 +368,7 @@
         // ...
         final FrameType pop = frameBuilder.pop();
         if (!pop.isWide()) {
-          frameBuilder.verifyIsAssignable(pop, FrameType.oneWord());
+          frameBuilder.checkIsAssignable(pop, FrameType.oneWord());
           frameBuilder.pop(FrameType.oneWord());
         }
         return;
@@ -399,7 +399,7 @@
           FrameType value1 = frameBuilder.pop(FrameType.oneWord());
           FrameType value2 = frameBuilder.pop();
           if (!value2.isWide()) {
-            frameBuilder.verifyIsAssignable(value2, FrameType.oneWord());
+            frameBuilder.checkIsAssignable(value2, FrameType.oneWord());
             FrameType value3 = frameBuilder.pop(FrameType.oneWord());
             frameBuilder.push(value1).push(value3);
           } else {
@@ -417,7 +417,7 @@
           // ..., value, value
           FrameType value1 = frameBuilder.pop();
           if (!value1.isWide()) {
-            frameBuilder.verifyIsAssignable(value1, FrameType.oneWord());
+            frameBuilder.checkIsAssignable(value1, FrameType.oneWord());
             FrameType value2 = frameBuilder.pop(FrameType.oneWord());
             frameBuilder.push(value2).push(value1).push(value2);
           } else {
@@ -436,7 +436,7 @@
           FrameType value1 = frameBuilder.pop();
           FrameType value2 = frameBuilder.pop(FrameType.oneWord());
           if (!value1.isWide()) {
-            frameBuilder.verifyIsAssignable(value1, FrameType.oneWord());
+            frameBuilder.checkIsAssignable(value1, FrameType.oneWord());
             FrameType value3 = frameBuilder.pop(FrameType.oneWord());
             frameBuilder.push(value2).push(value1).push(value3);
           } else {
@@ -466,9 +466,9 @@
             FrameType value3 = frameBuilder.pop();
             if (!value3.isWide()) {
               // (1)
-              frameBuilder.verifyIsAssignable(value1, FrameType.oneWord());
-              frameBuilder.verifyIsAssignable(value2, FrameType.oneWord());
-              frameBuilder.verifyIsAssignable(value3, FrameType.oneWord());
+              frameBuilder.checkIsAssignable(value1, FrameType.oneWord());
+              frameBuilder.checkIsAssignable(value2, FrameType.oneWord());
+              frameBuilder.checkIsAssignable(value3, FrameType.oneWord());
               FrameType value4 = frameBuilder.pop(FrameType.oneWord());
               frameBuilder
                   .push(value2)
@@ -479,13 +479,13 @@
                   .push(value1);
             } else {
               // (3)
-              frameBuilder.verifyIsAssignable(value1, FrameType.oneWord());
-              frameBuilder.verifyIsAssignable(value2, FrameType.oneWord());
+              frameBuilder.checkIsAssignable(value1, FrameType.oneWord());
+              frameBuilder.checkIsAssignable(value2, FrameType.oneWord());
               frameBuilder.push(value2).push(value1).push(value3).push(value2).push(value1);
             }
           } else if (!value2.isWide()) {
             // (2)
-            frameBuilder.verifyIsAssignable(value2, FrameType.oneWord());
+            frameBuilder.checkIsAssignable(value2, FrameType.oneWord());
             FrameType value3 = frameBuilder.pop(FrameType.oneWord());
             frameBuilder.push(value1).push(value3).push(value2).push(value1);
           } else {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfStore.java b/src/main/java/com/android/tools/r8/cf/code/CfStore.java
index d74eb39..b87c87c 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfStore.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfStore.java
@@ -3,6 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.cf.code;
 
+import static com.android.tools.r8.utils.BiPredicateUtils.or;
+
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.cf.code.CfFrame.FrameType;
 import com.android.tools.r8.errors.Unreachable;
@@ -129,24 +131,34 @@
     FrameType pop = frameBuilder.pop();
     switch (type) {
       case OBJECT:
-        frameBuilder.verifyIsAssignable(pop, factory.objectType);
+        frameBuilder.checkIsAssignable(
+            pop,
+            factory.objectType,
+            or(
+                frameBuilder::isUninitializedThisAndTarget,
+                frameBuilder::isUninitializedNewAndTarget,
+                frameBuilder::isAssignableAndInitialized));
         frameBuilder.storeLocal(var, pop);
         return;
       case INT:
-        frameBuilder.verifyIsAssignable(pop, factory.intType);
+        frameBuilder.checkIsAssignable(
+            pop, factory.intType, frameBuilder::isAssignableAndInitialized);
         frameBuilder.storeLocal(var, FrameType.initialized(factory.intType));
         return;
       case FLOAT:
-        frameBuilder.verifyIsAssignable(pop, factory.floatType);
+        frameBuilder.checkIsAssignable(
+            pop, factory.floatType, frameBuilder::isAssignableAndInitialized);
         frameBuilder.storeLocal(var, FrameType.initialized(factory.floatType));
         return;
       case LONG:
-        frameBuilder.verifyIsAssignable(pop, factory.longType);
+        frameBuilder.checkIsAssignable(
+            pop, factory.longType, frameBuilder::isAssignableAndInitialized);
         frameBuilder.storeLocal(var, FrameType.initialized(factory.longType));
         frameBuilder.storeLocal(var + 1, FrameType.initialized(factory.longType));
         return;
       case DOUBLE:
-        frameBuilder.verifyIsAssignable(pop, factory.doubleType);
+        frameBuilder.checkIsAssignable(
+            pop, factory.doubleType, frameBuilder::isAssignableAndInitialized);
         frameBuilder.storeLocal(var, FrameType.initialized(factory.doubleType));
         frameBuilder.storeLocal(var + 1, FrameType.initialized(factory.doubleType));
         return;
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfSwitch.java b/src/main/java/com/android/tools/r8/cf/code/CfSwitch.java
index f07011a..b84fc14 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfSwitch.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfSwitch.java
@@ -149,10 +149,10 @@
       InitClassLens initClassLens) {
     // ..., index/key →
     // ...
-    frameBuilder.pop(factory.intType);
-    frameBuilder.verifyTarget(defaultTarget);
+    frameBuilder.popInitialized(factory.intType);
+    frameBuilder.checkTarget(defaultTarget);
     for (CfLabel target : targets) {
-      frameBuilder.verifyTarget(target);
+      frameBuilder.checkTarget(target);
     }
     frameBuilder.setNoFrame();
   }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfThrow.java b/src/main/java/com/android/tools/r8/cf/code/CfThrow.java
index aad20a8..90d25c5 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfThrow.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfThrow.java
@@ -95,7 +95,7 @@
       InitClassLens initClassLens) {
     // ..., objectref →
     // objectref
-    frameBuilder.pop(factory.throwableType);
+    frameBuilder.popInitialized(factory.throwableType);
     // The exception edges are verified in CfCode since this is a throwing instruction.
     frameBuilder.setNoFrame();
   }
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 ed43ca9..025a20e 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -305,7 +305,8 @@
       LensCodeRewriterUtils rewriter,
       MethodVisitor visitor) {
     GraphLens graphLens = appView.graphLens();
-    assert verifyFrames(method.getDefinition(), appView, null, false);
+    assert verifyFrames(method.getDefinition(), appView, null, false)
+        : "Could not validate stack map frames";
     DexItemFactory dexItemFactory = appView.dexItemFactory();
     InitClassLens initClassLens = appView.initClassLens();
     InternalOptions options = appView.options();
@@ -793,9 +794,9 @@
             appView.graphLens());
     if (stateMap.containsKey(null)) {
       assert !shouldComputeInitialFrame();
-      builder.verifyFrameAndSet(stateMap.get(null));
+      builder.checkFrameAndSet(stateMap.get(null));
     } else if (shouldComputeInitialFrame()) {
-      builder.verifyFrameAndSet(
+      builder.checkFrameAndSet(
           new CfFrame(
               computeInitialLocals(context, method, rewrittenDescription), new ArrayDeque<>()));
     }
@@ -807,7 +808,7 @@
         // affect the exceptional transfer (the exception edge is always a singleton stack).
         if (instruction.canThrow()) {
           assert !instruction.isStore();
-          builder.verifyExceptionEdges();
+          builder.checkExceptionEdges();
         }
         instruction.evaluate(
             builder, context, returnType, appView.dexItemFactory(), appView.initClassLens());
diff --git a/src/main/java/com/android/tools/r8/utils/BiPredicateUtils.java b/src/main/java/com/android/tools/r8/utils/BiPredicateUtils.java
index f470d35..5245876 100644
--- a/src/main/java/com/android/tools/r8/utils/BiPredicateUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/BiPredicateUtils.java
@@ -15,4 +15,16 @@
   public static <S, T> BiPredicate<S, T> alwaysTrue() {
     return (s, t) -> true;
   }
+
+  @SafeVarargs
+  public static <S, T> BiPredicate<S, T> or(BiPredicate<S, T>... predicates) {
+    return (s, t) -> {
+      for (BiPredicate<S, T> predicate : predicates) {
+        if (predicate.test(s, t)) {
+          return true;
+        }
+      }
+      return false;
+    };
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/cf/stackmap/UninitializedGetFieldTest.java b/src/test/java/com/android/tools/r8/cf/stackmap/UninitializedGetFieldTest.java
new file mode 100644
index 0000000..58ecdb8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/stackmap/UninitializedGetFieldTest.java
@@ -0,0 +1,192 @@
+// Copyright (c) 2021, 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.stackmap;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.cf.stackmap.UninitializedGetFieldTest.UninitializedGetFieldTest$MainDump.dump;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+@RunWith(Parameterized.class)
+public class UninitializedGetFieldTest extends TestBase {
+
+  private final String[] EXPECTED = new String[] {"Main::foo"};
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+  }
+
+  public UninitializedGetFieldTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testJvm() throws Exception {
+    assumeTrue(parameters.isCfRuntime());
+    testForJvm()
+        .addProgramClassFileData(dump())
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(EXPECTED);
+  }
+
+  @Test()
+  public void testD8Cf() throws Exception {
+    assumeTrue(parameters.isCfRuntime());
+    testForD8(parameters.getBackend())
+        .addProgramClassFileData(dump())
+        .setMinApi(parameters.getApiLevel())
+        .compileWithExpectedDiagnostics(
+            diagnostics -> {
+              diagnostics.assertNoWarningsMatch(
+                  diagnosticMessage(containsString("The expected type uninitialized")));
+            })
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(EXPECTED);
+  }
+
+  @Test
+  public void testD8Dex() throws Exception {
+    assumeTrue(parameters.isDexRuntime());
+    testForD8(parameters.getBackend())
+        .addProgramClassFileData(dump())
+        .setMinApi(parameters.getApiLevel())
+        .compileWithExpectedDiagnostics(
+            diagnostics -> {
+              diagnostics.assertNoWarningsMatch(
+                  diagnosticMessage(containsString("The expected type uninitialized")));
+            })
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(EXPECTED);
+  }
+
+  public static class Main {
+
+    private Object object;
+
+    private Main() {
+      this.object = new Object();
+      foo(this.object);
+    }
+
+    private void foo(Object object) {
+      System.out.println("Main::foo");
+    }
+
+    public static void main(String[] args) {
+      new Main();
+    }
+  }
+
+  // The dump is generated from the above code. The change made is to Main::<init> where we
+  // now putfield before initializing.
+  static class UninitializedGetFieldTest$MainDump implements Opcodes {
+
+    static byte[] dump() throws Exception {
+
+      ClassWriter classWriter = new ClassWriter(0);
+      FieldVisitor fieldVisitor;
+      MethodVisitor methodVisitor;
+
+      classWriter.visit(
+          V1_8,
+          ACC_PUBLIC | ACC_SUPER,
+          "com/android/tools/r8/cf/stackmap/UninitializedGetFieldTest$Main",
+          null,
+          "java/lang/Object",
+          null);
+
+      classWriter.visitInnerClass(
+          "com/android/tools/r8/cf/stackmap/UninitializedGetFieldTest$Main",
+          "com/android/tools/r8/cf/stackmap/UninitializedGetFieldTest",
+          "Main",
+          ACC_PUBLIC | ACC_STATIC);
+
+      {
+        fieldVisitor =
+            classWriter.visitField(ACC_PRIVATE, "object", "Ljava/lang/Object;", null, null);
+        fieldVisitor.visitEnd();
+      }
+      {
+        methodVisitor = classWriter.visitMethod(ACC_PRIVATE, "<init>", "()V", null, null);
+        methodVisitor.visitCode();
+        methodVisitor.visitVarInsn(ALOAD, 0);
+        methodVisitor.visitTypeInsn(NEW, "java/lang/Object");
+        methodVisitor.visitInsn(DUP);
+        methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+        methodVisitor.visitFieldInsn(
+            PUTFIELD,
+            "com/android/tools/r8/cf/stackmap/UninitializedGetFieldTest$Main",
+            "object",
+            "Ljava/lang/Object;");
+        methodVisitor.visitVarInsn(ALOAD, 0);
+        methodVisitor.visitFieldInsn(
+            GETFIELD,
+            "com/android/tools/r8/cf/stackmap/UninitializedGetFieldTest$Main",
+            "object",
+            "Ljava/lang/Object;");
+        methodVisitor.visitVarInsn(ALOAD, 0);
+        methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+        methodVisitor.visitVarInsn(ALOAD, 0);
+        methodVisitor.visitMethodInsn(
+            INVOKESPECIAL,
+            "com/android/tools/r8/cf/stackmap/UninitializedGetFieldTest$Main",
+            "foo",
+            "(Ljava/lang/Object;)V",
+            false);
+        methodVisitor.visitInsn(RETURN);
+        methodVisitor.visitMaxs(3, 1);
+        methodVisitor.visitEnd();
+      }
+      {
+        methodVisitor =
+            classWriter.visitMethod(ACC_PRIVATE, "foo", "(Ljava/lang/Object;)V", null, null);
+        methodVisitor.visitCode();
+        methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+        methodVisitor.visitLdcInsn("Main::foo");
+        methodVisitor.visitMethodInsn(
+            INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
+        methodVisitor.visitInsn(RETURN);
+        methodVisitor.visitMaxs(2, 2);
+        methodVisitor.visitEnd();
+      }
+      {
+        methodVisitor =
+            classWriter.visitMethod(
+                ACC_PUBLIC | ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
+        methodVisitor.visitCode();
+        methodVisitor.visitTypeInsn(
+            NEW, "com/android/tools/r8/cf/stackmap/UninitializedGetFieldTest$Main");
+        methodVisitor.visitInsn(DUP);
+        methodVisitor.visitMethodInsn(
+            INVOKESPECIAL,
+            "com/android/tools/r8/cf/stackmap/UninitializedGetFieldTest$Main",
+            "<init>",
+            "()V",
+            false);
+        methodVisitor.visitInsn(POP);
+        methodVisitor.visitInsn(RETURN);
+        methodVisitor.visitMaxs(2, 1);
+        methodVisitor.visitEnd();
+      }
+      classWriter.visitEnd();
+
+      return classWriter.toByteArray();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/cf/stackmap/UninitializedInstanceOfTest.java b/src/test/java/com/android/tools/r8/cf/stackmap/UninitializedInstanceOfTest.java
new file mode 100644
index 0000000..30b2a78
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/stackmap/UninitializedInstanceOfTest.java
@@ -0,0 +1,133 @@
+// Copyright (c) 2021, 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.stackmap;
+
+import static com.android.tools.r8.cf.stackmap.UninitializedInstanceOfTest.MainDump.dump;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRunResult;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+@RunWith(Parameterized.class)
+public class UninitializedInstanceOfTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+  }
+
+  @Test
+  public void testJvm() throws Exception {
+    assumeTrue(parameters.isCfRuntime());
+    testForJvm()
+        .addProgramClassFileData(dump())
+        .run(parameters.getRuntime(), Main.class)
+        .assertFailureWithErrorThatThrows(VerifyError.class);
+  }
+
+  @Test(expected = CompilationFailedException.class)
+  public void testD8Cf() throws Exception {
+    assumeTrue(parameters.isCfRuntime());
+    testForD8(parameters.getBackend())
+        .addProgramClassFileData(dump())
+        .setMinApi(parameters.getApiLevel())
+        .compileWithExpectedDiagnostics(
+            diagnostics -> {
+              diagnostics.assertWarningMessageThatMatches(
+                  containsString("The expected type uninitialized new is not assignable"));
+              diagnostics.assertErrorMessageThatMatches(
+                  containsString("Could not validate stack map frames"));
+            });
+  }
+
+  @Test()
+  public void testD8Dex() throws Exception {
+    assumeTrue(parameters.isDexRuntime());
+    boolean expectFailure = parameters.getDexRuntimeVersion().isAtLeast(Version.V7_0_0);
+    testForD8(parameters.getBackend())
+        .addProgramClassFileData(dump())
+        .setMinApi(parameters.getApiLevel())
+        .compileWithExpectedDiagnostics(
+            diagnostics -> {
+              diagnostics.assertWarningMessageThatMatches(
+                  containsString("The expected type uninitialized new is not assignable"));
+            })
+        .run(parameters.getRuntime(), Main.class)
+        .applyIf(
+            expectFailure,
+            result -> result.assertFailureWithErrorThatThrows(VerifyError.class),
+            TestRunResult::assertSuccessWithOutputLines);
+  }
+
+  public UninitializedInstanceOfTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  public static class Main {
+
+    // The dump is generated from the following code, where we just swap the instanceof with the
+    // initializer call.
+    public static void main(String[] args) {
+      boolean m = (new Object() instanceof Main);
+    }
+  }
+
+  static class MainDump implements Opcodes {
+
+    static byte[] dump() throws Exception {
+
+      ClassWriter classWriter = new ClassWriter(0);
+      MethodVisitor methodVisitor;
+
+      classWriter.visit(
+          V1_8,
+          ACC_PUBLIC | ACC_SUPER,
+          "com/android/tools/r8/cf/stackmap/UninitializedInstanceOfTest$Main",
+          null,
+          "java/lang/Object",
+          null);
+
+      classWriter.visitInnerClass(
+          "com/android/tools/r8/cf/stackmap/UninitializedInstanceOfTest$Main",
+          "com/android/tools/r8/cf/stackmap/UninitializedInstanceOfTest",
+          "Main",
+          ACC_PUBLIC | ACC_STATIC);
+
+      {
+        methodVisitor =
+            classWriter.visitMethod(
+                ACC_PUBLIC | ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
+        methodVisitor.visitCode();
+        methodVisitor.visitTypeInsn(NEW, "java/lang/Object");
+        methodVisitor.visitInsn(DUP);
+        // INSTANCEOF and INVOKESPECIAL is swapped.
+        methodVisitor.visitTypeInsn(
+            INSTANCEOF, "com/android/tools/r8/cf/stackmap/UninitializedInstanceOfTest$Main");
+        methodVisitor.visitVarInsn(ISTORE, 1);
+        methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+        methodVisitor.visitInsn(RETURN);
+        methodVisitor.visitMaxs(2, 2);
+        methodVisitor.visitEnd();
+      }
+      classWriter.visitEnd();
+
+      return classWriter.toByteArray();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/cf/stackmap/UninitializedNewCheckCastTest.java b/src/test/java/com/android/tools/r8/cf/stackmap/UninitializedNewCheckCastTest.java
new file mode 100644
index 0000000..86429af
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/stackmap/UninitializedNewCheckCastTest.java
@@ -0,0 +1,127 @@
+// Copyright (c) 2021, 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.stackmap;
+
+import static com.android.tools.r8.cf.stackmap.UninitializedNewCheckCastTest.MainDump.dump;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+@RunWith(Parameterized.class)
+public class UninitializedNewCheckCastTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+  }
+
+  @Test
+  public void testJvm() throws Exception {
+    assumeTrue(parameters.isCfRuntime());
+    testForJvm()
+        .addProgramClassFileData(dump())
+        .run(parameters.getRuntime(), Main.class)
+        .assertFailureWithErrorThatThrows(VerifyError.class);
+  }
+
+  @Test(expected = CompilationFailedException.class)
+  public void testD8Cf() throws Exception {
+    assumeTrue(parameters.isCfRuntime());
+    testForD8(parameters.getBackend())
+        .addProgramClassFileData(dump())
+        .setMinApi(parameters.getApiLevel())
+        .compileWithExpectedDiagnostics(
+            diagnostics -> {
+              diagnostics.assertWarningMessageThatMatches(
+                  containsString("The expected type uninitialized new is not assignable"));
+              diagnostics.assertErrorMessageThatMatches(
+                  containsString("Could not validate stack map frames"));
+            });
+  }
+
+  @Test
+  public void testD8Dex() throws Exception {
+    assumeTrue(parameters.isDexRuntime());
+    testForD8(parameters.getBackend())
+        .addProgramClassFileData(dump())
+        .setMinApi(parameters.getApiLevel())
+        .compileWithExpectedDiagnostics(
+            diagnostics -> {
+              diagnostics.assertWarningMessageThatMatches(
+                  containsString("The expected type uninitialized new is not assignable"));
+            })
+        .run(parameters.getRuntime(), Main.class)
+        .assertFailureWithErrorThatThrows(VerifyError.class);
+  }
+
+  public UninitializedNewCheckCastTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  public static class Main {
+
+    // The dump is generated from the following code, where we just swap the CheckCast with the
+    // initializer call.
+    public static void main(String[] args) {
+      Main m = (Main) new Object();
+    }
+  }
+
+  static class MainDump implements Opcodes {
+
+    static byte[] dump() {
+
+      ClassWriter classWriter = new ClassWriter(0);
+      MethodVisitor methodVisitor;
+
+      classWriter.visit(
+          V1_8,
+          ACC_PUBLIC | ACC_SUPER,
+          "com/android/tools/r8/cf/stackmap/UninitializedNewCheckCastTest$Main",
+          null,
+          "java/lang/Object",
+          null);
+
+      classWriter.visitInnerClass(
+          "com/android/tools/r8/cf/stackmap/UninitializedNewCheckCastTest$Main",
+          "com/android/tools/r8/cf/stackmap/UninitializedNewCheckCastTest",
+          "Main",
+          ACC_PUBLIC | ACC_STATIC);
+
+      {
+        methodVisitor =
+            classWriter.visitMethod(
+                ACC_PUBLIC | ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
+        methodVisitor.visitCode();
+        methodVisitor.visitTypeInsn(NEW, "java/lang/Object");
+        methodVisitor.visitInsn(DUP);
+        // CHECK-CAST has swapped position with INVOKESPECIAL.
+        methodVisitor.visitTypeInsn(
+            CHECKCAST, "com/android/tools/r8/cf/stackmap/UninitializedNewCheckCastTest$Main");
+        methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+        methodVisitor.visitVarInsn(ASTORE, 1);
+        methodVisitor.visitInsn(RETURN);
+        methodVisitor.visitMaxs(2, 2);
+        methodVisitor.visitEnd();
+      }
+      classWriter.visitEnd();
+
+      return classWriter.toByteArray();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/cf/stackmap/UninitializedPutFieldSelfTest.java b/src/test/java/com/android/tools/r8/cf/stackmap/UninitializedPutFieldSelfTest.java
new file mode 100644
index 0000000..9a52445
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/stackmap/UninitializedPutFieldSelfTest.java
@@ -0,0 +1,198 @@
+// Copyright (c) 2021, 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.stackmap;
+
+import static com.android.tools.r8.cf.stackmap.UninitializedPutFieldSelfTest.MainDump.dump;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+@RunWith(Parameterized.class)
+public class UninitializedPutFieldSelfTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+  }
+
+  @Test
+  public void testJvm() throws Exception {
+    assumeTrue(parameters.isCfRuntime());
+    testForJvm()
+        .addProgramClassFileData(dump())
+        .run(parameters.getRuntime(), Main.class)
+        .assertFailureWithErrorThatThrows(VerifyError.class);
+  }
+
+  @Test(expected = CompilationFailedException.class)
+  public void testD8Cf() throws Exception {
+    assumeTrue(parameters.isCfRuntime());
+    testForD8(parameters.getBackend())
+        .addProgramClassFileData(dump())
+        .setMinApi(parameters.getApiLevel())
+        .compileWithExpectedDiagnostics(
+            diagnostics -> {
+              diagnostics.assertWarningMessageThatMatches(
+                  containsString("The expected type uninitialized this is not assignable"));
+              diagnostics.assertErrorMessageThatMatches(
+                  containsString("Could not validate stack map frames"));
+            });
+  }
+
+  @Test
+  public void testD8Dex() throws Exception {
+    assumeTrue(parameters.isDexRuntime());
+    boolean willFailVerification =
+        parameters.getDexRuntimeVersion().isOlderThan(Version.V5_1_1)
+            || parameters.getDexRuntimeVersion().isNewerThan(Version.V6_0_1);
+    testForD8(parameters.getBackend())
+        .addProgramClassFileData(dump())
+        .setMinApi(parameters.getApiLevel())
+        .compileWithExpectedDiagnostics(
+            diagnostics -> {
+              diagnostics.assertWarningMessageThatMatches(
+                  containsString("The expected type uninitialized this is not assignable"));
+            })
+        .run(parameters.getRuntime(), Main.class)
+        .assertFailureWithErrorThatThrowsIf(willFailVerification, VerifyError.class)
+        .assertSuccessWithOutputLinesIf(!willFailVerification, "Main::foo");
+  }
+
+  public UninitializedPutFieldSelfTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  public static class Main {
+
+    private Main main;
+
+    private Main() {
+      this.main = this;
+      this.main.foo();
+    }
+
+    private void foo() {
+      System.out.println("Main::foo");
+    }
+
+    public static void main(String[] args) {
+      new Main();
+    }
+  }
+
+  // The dump is generated from the above code. The change that is made is to Main::<init> where we
+  // now putfield before initializing. That will try and assign an uninstantiated type to a field
+  // which is not allowed.
+  public static class MainDump implements Opcodes {
+
+    public static byte[] dump() {
+
+      ClassWriter classWriter = new ClassWriter(0);
+      FieldVisitor fieldVisitor;
+      MethodVisitor methodVisitor;
+
+      classWriter.visit(
+          V1_8,
+          ACC_PUBLIC | ACC_SUPER,
+          "com/android/tools/r8/cf/stackmap/UninitializedPutFieldSelfTest$Main",
+          null,
+          "java/lang/Object",
+          null);
+
+      classWriter.visitInnerClass(
+          "com/android/tools/r8/cf/stackmap/UninitializedPutFieldSelfTest$Main",
+          "com/android/tools/r8/cf/stackmap/UninitializedPutFieldSelfTest",
+          "Main",
+          ACC_PUBLIC | ACC_STATIC);
+
+      {
+        fieldVisitor =
+            classWriter.visitField(
+                ACC_PRIVATE,
+                "main",
+                "Lcom/android/tools/r8/cf/stackmap/UninitializedPutFieldSelfTest$Main;",
+                null,
+                null);
+        fieldVisitor.visitEnd();
+      }
+      {
+        methodVisitor = classWriter.visitMethod(ACC_PRIVATE, "<init>", "()V", null, null);
+        methodVisitor.visitCode();
+        methodVisitor.visitVarInsn(ALOAD, 0);
+        methodVisitor.visitVarInsn(ALOAD, 0);
+        methodVisitor.visitFieldInsn(
+            PUTFIELD,
+            "com/android/tools/r8/cf/stackmap/UninitializedPutFieldSelfTest$Main",
+            "main",
+            "Lcom/android/tools/r8/cf/stackmap/UninitializedPutFieldSelfTest$Main;");
+        methodVisitor.visitVarInsn(ALOAD, 0);
+        methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+        methodVisitor.visitVarInsn(ALOAD, 0);
+        methodVisitor.visitFieldInsn(
+            GETFIELD,
+            "com/android/tools/r8/cf/stackmap/UninitializedPutFieldSelfTest$Main",
+            "main",
+            "Lcom/android/tools/r8/cf/stackmap/UninitializedPutFieldSelfTest$Main;");
+        methodVisitor.visitMethodInsn(
+            INVOKESPECIAL,
+            "com/android/tools/r8/cf/stackmap/UninitializedPutFieldSelfTest$Main",
+            "foo",
+            "()V",
+            false);
+        methodVisitor.visitInsn(RETURN);
+        methodVisitor.visitMaxs(2, 1);
+        methodVisitor.visitEnd();
+      }
+      {
+        methodVisitor = classWriter.visitMethod(ACC_PRIVATE, "foo", "()V", null, null);
+        methodVisitor.visitCode();
+        methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+        methodVisitor.visitLdcInsn("Main::foo");
+        methodVisitor.visitMethodInsn(
+            INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
+        methodVisitor.visitInsn(RETURN);
+        methodVisitor.visitMaxs(2, 1);
+        methodVisitor.visitEnd();
+      }
+      {
+        methodVisitor =
+            classWriter.visitMethod(
+                ACC_PUBLIC | ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
+        methodVisitor.visitCode();
+        methodVisitor.visitTypeInsn(
+            NEW, "com/android/tools/r8/cf/stackmap/UninitializedPutFieldSelfTest$Main");
+        methodVisitor.visitInsn(DUP);
+        methodVisitor.visitMethodInsn(
+            INVOKESPECIAL,
+            "com/android/tools/r8/cf/stackmap/UninitializedPutFieldSelfTest$Main",
+            "<init>",
+            "()V",
+            false);
+        methodVisitor.visitInsn(POP);
+        methodVisitor.visitInsn(RETURN);
+        methodVisitor.visitMaxs(2, 1);
+        methodVisitor.visitEnd();
+      }
+      classWriter.visitEnd();
+
+      return classWriter.toByteArray();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/cf/stackmap/UninitializedPutFieldTest.java b/src/test/java/com/android/tools/r8/cf/stackmap/UninitializedPutFieldTest.java
new file mode 100644
index 0000000..e943ebc
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/stackmap/UninitializedPutFieldTest.java
@@ -0,0 +1,192 @@
+// Copyright (c) 2021, 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.stackmap;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.cf.stackmap.UninitializedPutFieldTest.MainDump.dump;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+@RunWith(Parameterized.class)
+public class UninitializedPutFieldTest extends TestBase {
+
+  private final String[] EXPECTED = new String[] {"Main::foo"};
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+  }
+
+  @Test
+  public void testJvm() throws Exception {
+    assumeTrue(parameters.isCfRuntime());
+    testForJvm()
+        .addProgramClassFileData(dump())
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(EXPECTED);
+  }
+
+  @Test()
+  public void testD8Cf() throws Exception {
+    assumeTrue(parameters.isCfRuntime());
+    testForD8(parameters.getBackend())
+        .addProgramClassFileData(dump())
+        .setMinApi(parameters.getApiLevel())
+        .compileWithExpectedDiagnostics(
+            diagnostics -> {
+              diagnostics.assertNoWarningsMatch(
+                  diagnosticMessage(containsString("The expected type uninitialized")));
+            })
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(EXPECTED);
+  }
+
+  @Test
+  public void testD8Dex() throws Exception {
+    assumeTrue(parameters.isDexRuntime());
+    testForD8(parameters.getBackend())
+        .addProgramClassFileData(dump())
+        .setMinApi(parameters.getApiLevel())
+        .compileWithExpectedDiagnostics(
+            diagnostics -> {
+              diagnostics.assertNoWarningsMatch(
+                  diagnosticMessage(containsString("The expected type uninitialized")));
+            })
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(EXPECTED);
+  }
+
+  public UninitializedPutFieldTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  public static class Main {
+
+    private Object object;
+
+    private Main() {
+      this.object = new Object();
+      foo(this.object);
+    }
+
+    private void foo(Object object) {
+      System.out.println("Main::foo");
+    }
+
+    public static void main(String[] args) {
+      new Main();
+    }
+  }
+
+  // The dump is generated from the above code. The change made is to Main::<init> where we
+  // now putfield before initializing this.
+  static class MainDump implements Opcodes {
+
+    static byte[] dump() throws Exception {
+
+      ClassWriter classWriter = new ClassWriter(0);
+      FieldVisitor fieldVisitor;
+      MethodVisitor methodVisitor;
+
+      classWriter.visit(
+          V1_8,
+          ACC_PUBLIC | ACC_SUPER,
+          "com/android/tools/r8/cf/stackmap/UninitializedPutFieldTest$Main",
+          null,
+          "java/lang/Object",
+          null);
+
+      classWriter.visitInnerClass(
+          "com/android/tools/r8/cf/stackmap/UninitializedPutFieldTest$Main",
+          "com/android/tools/r8/cf/stackmap/UninitializedPutFieldTest",
+          "Main",
+          ACC_PUBLIC | ACC_STATIC);
+
+      {
+        fieldVisitor =
+            classWriter.visitField(ACC_PRIVATE, "object", "Ljava/lang/Object;", null, null);
+        fieldVisitor.visitEnd();
+      }
+      {
+        methodVisitor = classWriter.visitMethod(ACC_PRIVATE, "<init>", "()V", null, null);
+        methodVisitor.visitCode();
+        methodVisitor.visitVarInsn(ALOAD, 0);
+        methodVisitor.visitTypeInsn(NEW, "java/lang/Object");
+        methodVisitor.visitInsn(DUP);
+        methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+        methodVisitor.visitFieldInsn(
+            PUTFIELD,
+            "com/android/tools/r8/cf/stackmap/UninitializedPutFieldTest$Main",
+            "object",
+            "Ljava/lang/Object;");
+        methodVisitor.visitVarInsn(ALOAD, 0);
+        methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+        methodVisitor.visitVarInsn(ALOAD, 0);
+        methodVisitor.visitVarInsn(ALOAD, 0);
+        methodVisitor.visitFieldInsn(
+            GETFIELD,
+            "com/android/tools/r8/cf/stackmap/UninitializedPutFieldTest$Main",
+            "object",
+            "Ljava/lang/Object;");
+        methodVisitor.visitMethodInsn(
+            INVOKESPECIAL,
+            "com/android/tools/r8/cf/stackmap/UninitializedPutFieldTest$Main",
+            "foo",
+            "(Ljava/lang/Object;)V",
+            false);
+        methodVisitor.visitInsn(RETURN);
+        methodVisitor.visitMaxs(3, 1);
+        methodVisitor.visitEnd();
+      }
+      {
+        methodVisitor =
+            classWriter.visitMethod(ACC_PRIVATE, "foo", "(Ljava/lang/Object;)V", null, null);
+        methodVisitor.visitCode();
+        methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+        methodVisitor.visitLdcInsn("Main::foo");
+        methodVisitor.visitMethodInsn(
+            INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
+        methodVisitor.visitInsn(RETURN);
+        methodVisitor.visitMaxs(2, 2);
+        methodVisitor.visitEnd();
+      }
+      {
+        methodVisitor =
+            classWriter.visitMethod(
+                ACC_PUBLIC | ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
+        methodVisitor.visitCode();
+        methodVisitor.visitTypeInsn(
+            NEW, "com/android/tools/r8/cf/stackmap/UninitializedPutFieldTest$Main");
+        methodVisitor.visitInsn(DUP);
+        methodVisitor.visitMethodInsn(
+            INVOKESPECIAL,
+            "com/android/tools/r8/cf/stackmap/UninitializedPutFieldTest$Main",
+            "<init>",
+            "()V",
+            false);
+        methodVisitor.visitInsn(POP);
+        methodVisitor.visitInsn(RETURN);
+        methodVisitor.visitMaxs(2, 1);
+        methodVisitor.visitEnd();
+      }
+      classWriter.visitEnd();
+
+      return classWriter.toByteArray();
+    }
+  }
+}