Merge commit 'edb9c27116bd338ec6c12064b2a5dbe8cea88308' into dev-release
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index 501d4ae..e9dbc44 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -393,7 +393,7 @@
     assert !internal.debug;
     internal.debug = getMode() == CompilationMode.DEBUG;
     internal.programConsumer = getProgramConsumer();
-    if (internal.programConsumer instanceof ClassFileConsumer) {
+    if (internal.isGeneratingClassFiles()) {
       internal.cfToCfDesugar = true;
     }
     internal.mainDexListConsumer = getMainDexListConsumer();
diff --git a/src/main/java/com/android/tools/r8/cf/CfVersion.java b/src/main/java/com/android/tools/r8/cf/CfVersion.java
index f606b93..100656f 100644
--- a/src/main/java/com/android/tools/r8/cf/CfVersion.java
+++ b/src/main/java/com/android/tools/r8/cf/CfVersion.java
@@ -5,8 +5,8 @@
 
 import com.android.tools.r8.utils.structural.Equatable;
 import com.android.tools.r8.utils.structural.HashCodeVisitor;
-import com.android.tools.r8.utils.structural.StructuralAccept;
 import com.android.tools.r8.utils.structural.StructuralItem;
+import com.android.tools.r8.utils.structural.StructuralMapping;
 import com.android.tools.r8.utils.structural.StructuralSpecification;
 import org.objectweb.asm.Opcodes;
 
@@ -57,7 +57,7 @@
   }
 
   @Override
-  public StructuralAccept<CfVersion> getStructuralAccept() {
+  public StructuralMapping<CfVersion> getStructuralMapping() {
     return CfVersion::specify;
   }
 
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfArithmeticBinop.java b/src/main/java/com/android/tools/r8/cf/code/CfArithmeticBinop.java
index 7cfcf5b..e102cf7 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfArithmeticBinop.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfArithmeticBinop.java
@@ -52,9 +52,9 @@
   }
 
   @Override
-  public void internalAcceptCompareTo(
+  public int internalAcceptCompareTo(
       CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
-    CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
+    return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
   }
 
   public Opcode getOpcode() {
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 44699bf..22421de 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
@@ -44,9 +44,9 @@
   }
 
   @Override
-  public void internalAcceptCompareTo(
+  public int internalAcceptCompareTo(
       CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
-    CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
+    return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
   }
 
   @Override
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 a8d3546..c82e53e 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
@@ -42,9 +42,9 @@
   }
 
   @Override
-  public void internalAcceptCompareTo(
+  public int internalAcceptCompareTo(
       CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
-    CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
+    return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
   }
 
   public MemberType getType() {
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 fa27c39..5d64188 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
@@ -44,9 +44,9 @@
   }
 
   @Override
-  public void internalAcceptCompareTo(
+  public int internalAcceptCompareTo(
       CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
-    CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
+    return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
   }
 
   private int getStoreType() {
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 b4b33b1..721f3f7 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
@@ -44,9 +44,9 @@
   }
 
   @Override
-  public void internalAcceptCompareTo(
+  public int internalAcceptCompareTo(
       CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
-    type.acceptCompareTo(((CfCheckCast) other).type, visitor);
+    return type.acceptCompareTo(((CfCheckCast) other).type, visitor);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfCmp.java b/src/main/java/com/android/tools/r8/cf/code/CfCmp.java
index d1d4bb9..9572e12 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfCmp.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfCmp.java
@@ -49,9 +49,9 @@
   }
 
   @Override
-  public void internalAcceptCompareTo(
+  public int internalAcceptCompareTo(
       CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
-    CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
+    return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
   }
 
   public Bias getBias() {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstClass.java b/src/main/java/com/android/tools/r8/cf/code/CfConstClass.java
index 7931528..863dce0 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstClass.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstClass.java
@@ -40,9 +40,9 @@
   }
 
   @Override
-  public void internalAcceptCompareTo(
+  public int internalAcceptCompareTo(
       CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
-    type.acceptCompareTo(((CfConstClass) other).type, visitor);
+    return type.acceptCompareTo(((CfConstClass) other).type, visitor);
   }
 
   public DexType getType() {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java
index 17c2abe..16f5e9a 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java
@@ -44,9 +44,9 @@
   }
 
   @Override
-  public void internalAcceptCompareTo(
+  public int internalAcceptCompareTo(
       CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
-    handle.acceptCompareTo(((CfConstMethodHandle) other).handle, visitor);
+    return handle.acceptCompareTo(((CfConstMethodHandle) other).handle, visitor);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java
index 85f125e..5bb9849 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java
@@ -44,9 +44,9 @@
   }
 
   @Override
-  public void internalAcceptCompareTo(
+  public int internalAcceptCompareTo(
       CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
-    type.acceptCompareTo(((CfConstMethodType) other).type, visitor);
+    return type.acceptCompareTo(((CfConstMethodType) other).type, visitor);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstNull.java b/src/main/java/com/android/tools/r8/cf/code/CfConstNull.java
index 7575c49..185981e 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstNull.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstNull.java
@@ -44,9 +44,9 @@
   }
 
   @Override
-  public void internalAcceptCompareTo(
+  public int internalAcceptCompareTo(
       CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
-    CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
+    return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstNumber.java b/src/main/java/com/android/tools/r8/cf/code/CfConstNumber.java
index 23f3a2d..7484e80 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstNumber.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstNumber.java
@@ -21,6 +21,7 @@
 import com.android.tools.r8.ir.optimize.InliningConstraints;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.StructuralSpecification;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
 
@@ -29,6 +30,10 @@
   private final long value;
   private final ValueType type;
 
+  private static void specify(StructuralSpecification<CfConstNumber, ?> spec) {
+    spec.withLong(CfConstNumber::getRawValue).withItem(CfConstNumber::getType);
+  }
+
   public CfConstNumber(long value, ValueType type) {
     this.value = value;
     this.type = type;
@@ -40,12 +45,9 @@
   }
 
   @Override
-  public void internalAcceptCompareTo(
+  public int internalAcceptCompareTo(
       CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
-    visitor.visit(
-        this,
-        (CfConstNumber) other,
-        spec -> spec.withLong(CfConstNumber::getRawValue).withItem(CfConstNumber::getType));
+    return visitor.visit(this, (CfConstNumber) other, CfConstNumber::specify);
   }
 
   public ValueType getType() {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstString.java b/src/main/java/com/android/tools/r8/cf/code/CfConstString.java
index 827e77e..376edfe 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstString.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstString.java
@@ -36,9 +36,9 @@
   }
 
   @Override
-  public void internalAcceptCompareTo(
+  public int internalAcceptCompareTo(
       CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
-    string.acceptCompareTo(other.asConstString().string, visitor);
+    return string.acceptCompareTo(other.asConstString().string, visitor);
   }
 
   public DexString getString() {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfDexItemBasedConstString.java b/src/main/java/com/android/tools/r8/cf/code/CfDexItemBasedConstString.java
index e880a23..75c5eda 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfDexItemBasedConstString.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfDexItemBasedConstString.java
@@ -43,9 +43,9 @@
   }
 
   @Override
-  public void internalAcceptCompareTo(
+  public int internalAcceptCompareTo(
       CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
-    visitor.visitDexReference(item, ((CfDexItemBasedConstString) other).item);
+    return visitor.visitDexReference(item, ((CfDexItemBasedConstString) other).item);
   }
 
   public DexReference getItem() {
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 978a43d..6176b49 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
@@ -60,9 +60,9 @@
   }
 
   @Override
-  public void internalAcceptCompareTo(
+  public int internalAcceptCompareTo(
       CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
-    visitor.visit(this, other.asFieldInstruction(), CfFieldInstruction::specify);
+    return visitor.visit(this, other.asFieldInstruction(), CfFieldInstruction::specify);
   }
 
   @Override
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 d89ef90..713d764 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
@@ -153,11 +153,11 @@
   }
 
   @Override
-  public void internalAcceptCompareTo(
+  public int internalAcceptCompareTo(
       CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
     // The frame should be determined by the code so it should for equal iff the code is equal.
     // Thus we just require the frame to be in place.
-    CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
+    return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
   }
 
   private static class InitializedType extends FrameType {
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 7b3ccf8..2f4a549 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
@@ -36,9 +36,9 @@
   }
 
   @Override
-  public void internalAcceptCompareTo(
+  public int internalAcceptCompareTo(
       CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
-    helper.compareLabels(target, ((CfGoto) other).target, visitor);
+    return helper.compareLabels(target, ((CfGoto) other).target, visitor);
   }
 
   @Override
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 6e00da7..b331673 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
@@ -44,12 +44,12 @@
   }
 
   @Override
-  public void internalAcceptCompareTo(
+  public int internalAcceptCompareTo(
       CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
     CfIf otherIf = (CfIf) other;
     assert kind == otherIf.kind;
     assert type == otherIf.type;
-    helper.compareLabels(target, otherIf.target, visitor);
+    return helper.compareLabels(target, otherIf.target, visitor);
   }
 
   public ValueType getType() {
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 35f9af3..45b426b 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
@@ -44,12 +44,12 @@
   }
 
   @Override
-  public void internalAcceptCompareTo(
+  public int internalAcceptCompareTo(
       CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
     CfIfCmp otherIf = (CfIfCmp) other;
     assert kind == otherIf.kind;
     assert type == otherIf.type;
-    helper.compareLabels(target, otherIf.target, visitor);
+    return helper.compareLabels(target, otherIf.target, visitor);
   }
 
   public Type getKind() {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfIinc.java b/src/main/java/com/android/tools/r8/cf/code/CfIinc.java
index 04a7599..933dfcd 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfIinc.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfIinc.java
@@ -20,6 +20,7 @@
 import com.android.tools.r8.ir.optimize.InliningConstraints;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.StructuralSpecification;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
 
@@ -28,6 +29,10 @@
   private final int var;
   private final int increment;
 
+  private static void specify(StructuralSpecification<CfIinc, ?> spec) {
+    spec.withInt(CfIinc::getLocalIndex).withInt(CfIinc::getIncrement);
+  }
+
   public CfIinc(int var, int increment) {
     this.var = var;
     this.increment = increment;
@@ -39,12 +44,9 @@
   }
 
   @Override
-  public void internalAcceptCompareTo(
+  public int internalAcceptCompareTo(
       CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
-    visitor.visit(
-        this,
-        (CfIinc) other,
-        spec -> spec.withInt(CfIinc::getLocalIndex).withInt(CfIinc::getIncrement));
+    return visitor.visit(this, (CfIinc) other, CfIinc::specify);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInitClass.java b/src/main/java/com/android/tools/r8/cf/code/CfInitClass.java
index 4d311fe..815500d 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInitClass.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInitClass.java
@@ -49,9 +49,9 @@
   }
 
   @Override
-  public void internalAcceptCompareTo(
+  public int internalAcceptCompareTo(
       CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
-    clazz.acceptCompareTo(((CfInitClass) other).clazz, visitor);
+    return clazz.acceptCompareTo(((CfInitClass) other).clazz, visitor);
   }
 
   @Override
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 85d9b59..c18eb57 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
@@ -43,9 +43,9 @@
   }
 
   @Override
-  public void internalAcceptCompareTo(
+  public int internalAcceptCompareTo(
       CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
-    type.acceptCompareTo(other.asInstanceOf().type, visitor);
+    return type.acceptCompareTo(other.asInstanceOf().type, visitor);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
index d4986b3..e7ae429 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
@@ -61,17 +61,16 @@
    * <p>If an instruction is uniquely determined by the "compare id" then the override should simply
    * call '{@code CfCompareHelper::compareIdUniquelyDeterminesEquality}'.
    */
-  public abstract void internalAcceptCompareTo(
+  public abstract int internalAcceptCompareTo(
       CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper);
 
-  public final void acceptCompareTo(
+  public final int acceptCompareTo(
       CfInstruction o, CompareToVisitor visitor, CfCompareHelper helper) {
-    int diff = getCompareToId() - o.getCompareToId();
-    if (diff == 0) {
-      internalAcceptCompareTo(o, visitor, helper);
-    } else {
-      visitor.visitInt(getCompareToId(), o.getCompareToId());
+    int diff = visitor.visitInt(getCompareToId(), o.getCompareToId());
+    if (diff != 0) {
+      return diff;
     }
+    return internalAcceptCompareTo(o, visitor, helper);
   }
 
   @Override
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 4d1af96..07b0e0c 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
@@ -34,6 +34,7 @@
 import com.android.tools.r8.ir.optimize.InliningConstraints;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.StructuralSpecification;
 import java.util.Arrays;
 import java.util.ListIterator;
 import org.objectweb.asm.MethodVisitor;
@@ -45,6 +46,10 @@
   private final int opcode;
   private final boolean itf;
 
+  private static void specify(StructuralSpecification<CfInvoke, ?> spec) {
+    spec.withBool(CfInvoke::isInterface).withItem(CfInvoke::getMethod);
+  }
+
   public CfInvoke(int opcode, DexMethod method, boolean itf) {
     assert Opcodes.INVOKEVIRTUAL <= opcode && opcode <= Opcodes.INVOKEINTERFACE;
     assert !(opcode == Opcodes.INVOKEVIRTUAL && itf) : "InvokeVirtual on interface type";
@@ -60,13 +65,10 @@
   }
 
   @Override
-  public void internalAcceptCompareTo(
+  public int internalAcceptCompareTo(
       CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
     CfInvoke otherInvoke = other.asInvoke();
-    visitor.visit(
-        this,
-        otherInvoke,
-        spec -> spec.withBool(CfInvoke::isInterface).withItem(CfInvoke::getMethod));
+    return visitor.visit(this, otherInvoke, CfInvoke::specify);
   }
 
   public DexMethod getMethod() {
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 fc1f3e5..e65d095 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
@@ -49,9 +49,9 @@
   }
 
   @Override
-  public void internalAcceptCompareTo(
+  public int internalAcceptCompareTo(
       CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
-    callSite.acceptCompareTo(((CfInvokeDynamic) other).callSite, visitor);
+    return callSite.acceptCompareTo(((CfInvokeDynamic) other).callSite, visitor);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfJsrRet.java b/src/main/java/com/android/tools/r8/cf/code/CfJsrRet.java
index d8cd219..812a17c 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfJsrRet.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfJsrRet.java
@@ -42,7 +42,7 @@
   }
 
   @Override
-  public void internalAcceptCompareTo(
+  public int internalAcceptCompareTo(
       CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
     throw error();
   }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfLabel.java b/src/main/java/com/android/tools/r8/cf/code/CfLabel.java
index dc6d863..5a3e4e7 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfLabel.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfLabel.java
@@ -39,9 +39,9 @@
   }
 
   @Override
-  public void internalAcceptCompareTo(
+  public int internalAcceptCompareTo(
       CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
-    helper.compareLabels(this, other.asLabel(), visitor);
+    return helper.compareLabels(this, other.asLabel(), visitor);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfLoad.java b/src/main/java/com/android/tools/r8/cf/code/CfLoad.java
index 4e48880..96a6d11 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfLoad.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfLoad.java
@@ -41,9 +41,9 @@
   }
 
   @Override
-  public void internalAcceptCompareTo(
+  public int internalAcceptCompareTo(
       CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
-    visitor.visitInt(var, other.asLoad().var);
+    return visitor.visitInt(var, other.asLoad().var);
   }
 
   private int getLoadType() {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfLogicalBinop.java b/src/main/java/com/android/tools/r8/cf/code/CfLogicalBinop.java
index d6264f7..19e5dd9 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfLogicalBinop.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfLogicalBinop.java
@@ -54,9 +54,9 @@
   }
 
   @Override
-  public void internalAcceptCompareTo(
+  public int internalAcceptCompareTo(
       CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
-    CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
+    return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
   }
 
   public NumericType getType() {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfMonitor.java b/src/main/java/com/android/tools/r8/cf/code/CfMonitor.java
index 520c168..3286da2 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfMonitor.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfMonitor.java
@@ -43,9 +43,9 @@
   }
 
   @Override
-  public void internalAcceptCompareTo(
+  public int internalAcceptCompareTo(
       CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
-    CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
+    return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
   }
 
   @Override
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 ff9ea71..9180511 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
@@ -22,6 +22,7 @@
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.StructuralSpecification;
 import java.util.ListIterator;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
@@ -31,6 +32,10 @@
   private final DexType type;
   private final int dimensions;
 
+  private static void specify(StructuralSpecification<CfMultiANewArray, ?> spec) {
+    spec.withInt(CfMultiANewArray::getDimensions).withItem(CfMultiANewArray::getType);
+  }
+
   public CfMultiANewArray(DexType type, int dimensions) {
     this.type = type;
     this.dimensions = dimensions;
@@ -50,12 +55,9 @@
   }
 
   @Override
-  public void internalAcceptCompareTo(
+  public int internalAcceptCompareTo(
       CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
-    visitor.visit(
-        this,
-        (CfMultiANewArray) other,
-        spec -> spec.withInt(CfMultiANewArray::getDimensions).withItem(CfMultiANewArray::getType));
+    return visitor.visit(this, (CfMultiANewArray) other, CfMultiANewArray::specify);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNeg.java b/src/main/java/com/android/tools/r8/cf/code/CfNeg.java
index dce1028..5de3787 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfNeg.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNeg.java
@@ -44,9 +44,9 @@
   }
 
   @Override
-  public void internalAcceptCompareTo(
+  public int internalAcceptCompareTo(
       CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
-    CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
+    return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNew.java b/src/main/java/com/android/tools/r8/cf/code/CfNew.java
index 87a407d..7140404 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfNew.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNew.java
@@ -44,9 +44,9 @@
   }
 
   @Override
-  public void internalAcceptCompareTo(
+  public int internalAcceptCompareTo(
       CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
-    type.acceptCompareTo(((CfNew) other).type, visitor);
+    return type.acceptCompareTo(((CfNew) other).type, visitor);
   }
 
   @Override
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 0dbf78e..bd36756 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
@@ -47,9 +47,9 @@
   }
 
   @Override
-  public void internalAcceptCompareTo(
+  public int internalAcceptCompareTo(
       CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
-    type.acceptCompareTo(((CfNewArray) other).type, visitor);
+    return type.acceptCompareTo(((CfNewArray) other).type, visitor);
   }
 
   private int getPrimitiveTypeCode() {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNop.java b/src/main/java/com/android/tools/r8/cf/code/CfNop.java
index 0a69673b..974d928 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfNop.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNop.java
@@ -30,9 +30,9 @@
   }
 
   @Override
-  public void internalAcceptCompareTo(
+  public int internalAcceptCompareTo(
       CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
-    CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
+    return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNumberConversion.java b/src/main/java/com/android/tools/r8/cf/code/CfNumberConversion.java
index 35c78a7..dc1bc2c 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfNumberConversion.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNumberConversion.java
@@ -46,9 +46,9 @@
   }
 
   @Override
-  public void internalAcceptCompareTo(
+  public int internalAcceptCompareTo(
       CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
-    CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
+    return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
   }
 
   public NumericType getFromType() {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfPosition.java b/src/main/java/com/android/tools/r8/cf/code/CfPosition.java
index df8612e..f524bab 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfPosition.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfPosition.java
@@ -27,6 +27,8 @@
   private final CfLabel label;
   private final Position position;
 
+
+
   public CfPosition(CfLabel label, Position position) {
     this.label = label;
     this.position = position;
@@ -38,9 +40,9 @@
   }
 
   @Override
-  public void internalAcceptCompareTo(
+  public int internalAcceptCompareTo(
       CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
-    visitor.visit(
+    return visitor.visit(
         this,
         (CfPosition) other,
         spec ->
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 b09fbea..51d1283 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
@@ -43,9 +43,9 @@
   }
 
   @Override
-  public void internalAcceptCompareTo(
+  public int internalAcceptCompareTo(
       CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
-    CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
+    return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
   }
 
   private int getOpcode() {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfReturnVoid.java b/src/main/java/com/android/tools/r8/cf/code/CfReturnVoid.java
index c52efe3..08de455 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfReturnVoid.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfReturnVoid.java
@@ -35,9 +35,9 @@
   }
 
   @Override
-  public void internalAcceptCompareTo(
+  public int internalAcceptCompareTo(
       CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
-    CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
+    return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
   }
 
   @Override
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 c6c39be..bfdbe0a 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
@@ -88,9 +88,9 @@
   }
 
   @Override
-  public void internalAcceptCompareTo(
+  public int internalAcceptCompareTo(
       CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
-    CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
+    return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
   }
 
   @Override
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 e424474..d74eb39 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
@@ -42,9 +42,9 @@
   }
 
   @Override
-  public void internalAcceptCompareTo(
+  public int internalAcceptCompareTo(
       CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
-    visitor.visitInt(var, other.asStore().var);
+    return visitor.visitInt(var, other.asStore().var);
   }
 
   private int getStoreType() {
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 86abb2e..cb915ff 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
@@ -51,11 +51,10 @@
   }
 
   @Override
-  public void internalAcceptCompareTo(
+  public int internalAcceptCompareTo(
       CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
     assert kind == ((CfSwitch) other).kind;
-
-    visitor.visit(
+    return visitor.visit(
         this,
         (CfSwitch) other,
         spec ->
@@ -156,5 +155,6 @@
     for (CfLabel target : targets) {
       frameBuilder.verifyTarget(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 7243175..aad20a8 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
@@ -36,9 +36,9 @@
   }
 
   @Override
-  public void internalAcceptCompareTo(
+  public int internalAcceptCompareTo(
       CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
-    CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
+    return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfTryCatch.java b/src/main/java/com/android/tools/r8/cf/code/CfTryCatch.java
index 6cc3b13..2f821be 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfTryCatch.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfTryCatch.java
@@ -48,8 +48,8 @@
     return new CfTryCatch(start, end, guards, targets);
   }
 
-  public void acceptCompareTo(CfTryCatch other, CompareToVisitor visitor, CfCompareHelper helper) {
-    visitor.visit(
+  public int acceptCompareTo(CfTryCatch other, CompareToVisitor visitor, CfCompareHelper helper) {
+    return visitor.visit(
         this,
         other,
         spec ->
diff --git a/src/main/java/com/android/tools/r8/dex/FileWriter.java b/src/main/java/com/android/tools/r8/dex/FileWriter.java
index c07e0f5..6f2f904 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -133,7 +133,7 @@
       Log.verbose(FileWriter.class, "Writing encoded annotation @ %08x", dest.position());
     }
     List<DexAnnotationElement> elements = new ArrayList<>(Arrays.asList(annotation.elements));
-    elements.sort((a, b) -> a.name.compareToWithNamingLens(b.name, mapping.getNamingLens()));
+    elements.sort((a, b) -> a.name.acceptCompareTo(b.name, mapping.getCompareToVisitor()));
     dest.putUleb128(mapping.getOffsetFor(annotation.type));
     dest.putUleb128(elements.size());
     for (DexAnnotationElement element : elements) {
@@ -351,6 +351,7 @@
   }
 
   private static String getKeyForDexCodeSorting(ProgramMethod method, ClassNameMapper proguardMap) {
+    // TODO(b/173999869): Could this instead compute sorting using dex items?
     Signature signature;
     String originalClassName;
     if (proguardMap != null) {
@@ -580,7 +581,8 @@
     }
     List<DexAnnotation> annotations = new ArrayList<>(Arrays.asList(set.annotations));
     annotations.sort(
-        (a, b) -> a.annotation.type.compareToWithNamingLens(b.annotation.type, namingLens));
+        (a, b) ->
+            a.annotation.type.acceptCompareTo(b.annotation.type, mapping.getCompareToVisitor()));
     dest.putInt(annotations.size());
     for (DexAnnotation annotation : annotations) {
       dest.putInt(mixedSectionOffsets.getOffsetFor(annotation));
@@ -613,10 +615,11 @@
     mixedSectionOffsets.setOffsetForAnnotationsDirectory(annotationDirectory, dest.align(4));
     dest.putInt(mixedSectionOffsets.getOffsetFor(annotationDirectory.getClazzAnnotations()));
     List<DexEncodedMethod> methodAnnotations =
-        annotationDirectory.sortMethodAnnotations(namingLens);
+        annotationDirectory.sortMethodAnnotations(mapping.getCompareToVisitor());
     List<DexEncodedMethod> parameterAnnotations =
-        annotationDirectory.sortParameterAnnotations(namingLens);
-    List<DexEncodedField> fieldAnnotations = annotationDirectory.sortFieldAnnotations(namingLens);
+        annotationDirectory.sortParameterAnnotations(mapping.getCompareToVisitor());
+    List<DexEncodedField> fieldAnnotations =
+        annotationDirectory.sortFieldAnnotations(mapping.getCompareToVisitor());
     dest.putInt(fieldAnnotations.size());
     dest.putInt(methodAnnotations.size());
     dest.putInt(parameterAnnotations.size());
@@ -630,7 +633,7 @@
 
   private void writeEncodedFields(List<DexEncodedField> unsortedFields) {
     List<DexEncodedField> fields = new ArrayList<>(unsortedFields);
-    fields.sort((a, b) -> a.field.compareToWithNamingLens(b.field, namingLens));
+    fields.sort((a, b) -> a.field.acceptCompareTo(b.field, mapping.getCompareToVisitor()));
     int currentOffset = 0;
     for (DexEncodedField field : fields) {
       assert field.validateDexValue(application.dexItemFactory);
@@ -646,7 +649,7 @@
   private void writeEncodedMethods(
       Iterable<DexEncodedMethod> unsortedMethods, boolean isSharedSynthetic) {
     List<DexEncodedMethod> methods = IterableUtils.toNewArrayList(unsortedMethods);
-    methods.sort((a, b) -> a.method.compareToWithNamingLens(b.method, namingLens));
+    methods.sort((a, b) -> a.method.acceptCompareTo(b.method, mapping.getCompareToVisitor()));
     int currentOffset = 0;
     for (DexEncodedMethod method : methods) {
       int nextOffset = mapping.getOffsetFor(method.method);
diff --git a/src/main/java/com/android/tools/r8/graph/AccessFlags.java b/src/main/java/com/android/tools/r8/graph/AccessFlags.java
index 910153a..e47b428 100644
--- a/src/main/java/com/android/tools/r8/graph/AccessFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/AccessFlags.java
@@ -4,8 +4,8 @@
 package com.android.tools.r8.graph;
 
 import com.android.tools.r8.dex.Constants;
-import com.android.tools.r8.utils.structural.StructuralAccept;
 import com.android.tools.r8.utils.structural.StructuralItem;
+import com.android.tools.r8.utils.structural.StructuralMapping;
 import com.android.tools.r8.utils.structural.StructuralSpecification;
 import com.google.common.collect.ImmutableList;
 import java.util.List;
@@ -61,7 +61,7 @@
   }
 
   @Override
-  public StructuralAccept<T> getStructuralAccept() {
+  public StructuralMapping<T> getStructuralMapping() {
     return AccessFlags::specify;
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index 1e7ec9c..ce2d937 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -9,7 +9,6 @@
 import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
 import com.android.tools.r8.graph.analysis.InitializedClassesInInstanceMethodsAnalysis.InitializedClassesInInstanceMethods;
 import com.android.tools.r8.graph.classmerging.HorizontallyMergedLambdaClasses;
-import com.android.tools.r8.graph.classmerging.MergedClasses;
 import com.android.tools.r8.graph.classmerging.MergedClassesCollection;
 import com.android.tools.r8.graph.classmerging.StaticallyMergedClasses;
 import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
@@ -455,12 +454,6 @@
     return collection;
   }
 
-  public boolean hasBeenMerged(DexProgramClass clazz) {
-    return MergedClasses.hasBeenMerged(horizontallyMergedClasses, clazz)
-        || MergedClasses.hasBeenMerged(horizontallyMergedLambdaClasses, clazz)
-        || MergedClasses.hasBeenMerged(verticallyMergedClasses, clazz);
-  }
-
   /**
    * Get the result of horizontal lambda class merging. Returns null if horizontal lambda class
    * merging has not been run.
@@ -636,7 +629,7 @@
 
     boolean changed = appView.setGraphLens(lens);
     assert changed;
-    assert application.verifyWithLens(lens);
+    assert application.verifyWithLens(appView.appInfo().app().asDirect(), lens);
 
     // The application has already been rewritten with the given applied lens. Therefore, we
     // temporarily replace that lens with a lens that does not have any rewritings to avoid the
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 6d3cedc..2e1eeb6 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -43,8 +43,8 @@
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
 import com.android.tools.r8.utils.structural.HashingVisitor;
-import com.android.tools.r8.utils.structural.StructuralAccept;
 import com.android.tools.r8.utils.structural.StructuralItem;
+import com.android.tools.r8.utils.structural.StructuralMapping;
 import com.google.common.base.Strings;
 import it.unimi.dsi.fastutil.ints.Int2IntArrayMap;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceAVLTreeMap;
@@ -112,9 +112,9 @@
       return end;
     }
 
-    public void acceptCompareTo(
+    public int acceptCompareTo(
         LocalVariableInfo other, CompareToVisitor visitor, CfCompareHelper helper) {
-      visitor.visit(
+      return visitor.visit(
           this,
           other,
           spec ->
@@ -164,7 +164,7 @@
   }
 
   @Override
-  public StructuralAccept<CfCode> getStructuralAccept() {
+  public StructuralMapping<CfCode> getStructuralMapping() {
     throw new Unreachable();
   }
 
@@ -254,9 +254,9 @@
   }
 
   @Override
-  public void acceptCompareTo(CfCode other, CompareToVisitor visitor) {
+  public int acceptCompareTo(CfCode other, CompareToVisitor visitor) {
     CfCompareHelper helper = new CfCompareHelper(this, other);
-    visitor.visit(
+    return visitor.visit(
         this,
         other,
         spec ->
@@ -695,7 +695,7 @@
 
   public boolean verifyFrames(
       DexEncodedMethod method, AppView<?> appView, Origin origin, boolean applyProtoTypeChanges) {
-    if (!appView.options().testing.readInputStackMaps
+    if (!appView.options().canUseInputStackMaps()
         || appView.options().testing.disableStackMapVerification) {
       stackMapStatus = StackMapStatus.INVALID_OR_NOT_PRESENT;
       return true;
diff --git a/src/main/java/com/android/tools/r8/graph/CfCompareHelper.java b/src/main/java/com/android/tools/r8/graph/CfCompareHelper.java
index 0203b0c..1108c72 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCompareHelper.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCompareHelper.java
@@ -45,11 +45,12 @@
   }
 
   // Helper to signal that the concrete instruction is uniquely determined by its ID/opcode.
-  public static void compareIdUniquelyDeterminesEquality(
+  public static int compareIdUniquelyDeterminesEquality(
       CfInstruction instruction1, CfInstruction instruction2) {
     assert instruction1.getClass() == instruction2.getClass();
     assert instruction1.getCompareToId() == instruction2.getCompareToId();
     assert instruction1.toString().equals(instruction2.toString());
+    return 0;
   }
 
   private static Reference2IntMap<CfLabel> getLabelOrdering(CfCode code) {
@@ -71,8 +72,8 @@
     this.code2 = code2;
   }
 
-  public void compareLabels(CfLabel label1, CfLabel label2, CompareToVisitor visitor) {
-    labelAcceptor().acceptCompareTo(label1, label2, visitor);
+  public int compareLabels(CfLabel label1, CfLabel label2, CompareToVisitor visitor) {
+    return labelAcceptor().acceptCompareTo(label1, label2, visitor);
   }
 
   public StructuralAcceptor<CfLabel> labelAcceptor() {
@@ -83,8 +84,8 @@
             private final Reference2IntMap<CfLabel> labels2 = getLabelOrdering(code2);
 
             @Override
-            public void acceptCompareTo(CfLabel item1, CfLabel item2, CompareToVisitor visitor) {
-              visitor.visitInt(labels1.getInt(item1), labels2.getInt(item2));
+            public int acceptCompareTo(CfLabel item1, CfLabel item2, CompareToVisitor visitor) {
+              return visitor.visitInt(labels1.getInt(item1), labels2.getInt(item2));
             }
 
             @Override
@@ -100,9 +101,9 @@
     CfCompareHelper helper = this;
     return new StructuralAcceptor<CfInstruction>() {
       @Override
-      public void acceptCompareTo(
+      public int acceptCompareTo(
           CfInstruction item1, CfInstruction item2, CompareToVisitor visitor) {
-        item1.acceptCompareTo(item2, visitor, helper);
+        return item1.acceptCompareTo(item2, visitor, helper);
       }
 
       @Override
@@ -116,8 +117,8 @@
     CfCompareHelper helper = this;
     return new StructuralAcceptor<CfTryCatch>() {
       @Override
-      public void acceptCompareTo(CfTryCatch item1, CfTryCatch item2, CompareToVisitor visitor) {
-        item1.acceptCompareTo(item2, visitor, helper);
+      public int acceptCompareTo(CfTryCatch item1, CfTryCatch item2, CompareToVisitor visitor) {
+        return item1.acceptCompareTo(item2, visitor, helper);
       }
 
       @Override
@@ -131,9 +132,9 @@
     CfCompareHelper helper = this;
     return new StructuralAcceptor<LocalVariableInfo>() {
       @Override
-      public void acceptCompareTo(
+      public int acceptCompareTo(
           LocalVariableInfo item1, LocalVariableInfo item2, CompareToVisitor visitor) {
-        item1.acceptCompareTo(item2, visitor, helper);
+        return item1.acceptCompareTo(item2, visitor, helper);
       }
 
       @Override
diff --git a/src/main/java/com/android/tools/r8/graph/DebugLocalInfo.java b/src/main/java/com/android/tools/r8/graph/DebugLocalInfo.java
index 56027f2..67a8eab 100644
--- a/src/main/java/com/android/tools/r8/graph/DebugLocalInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/DebugLocalInfo.java
@@ -5,8 +5,8 @@
 
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.utils.DescriptorUtils;
-import com.android.tools.r8.utils.structural.StructuralAccept;
 import com.android.tools.r8.utils.structural.StructuralItem;
+import com.android.tools.r8.utils.structural.StructuralMapping;
 import com.android.tools.r8.utils.structural.StructuralSpecification;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceAVLTreeMap;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
@@ -45,7 +45,7 @@
   }
 
   @Override
-  public StructuralAccept<DebugLocalInfo> getStructuralAccept() {
+  public StructuralMapping<DebugLocalInfo> getStructuralMapping() {
     return DebugLocalInfo::specify;
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
index 5f223f8..bcb7974 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
@@ -16,8 +16,8 @@
 import com.android.tools.r8.ir.desugar.CovariantReturnTypeAnnotationTransformer;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.Pair;
-import com.android.tools.r8.utils.structural.StructuralAccept;
 import com.android.tools.r8.utils.structural.StructuralItem;
+import com.android.tools.r8.utils.structural.StructuralMapping;
 import com.android.tools.r8.utils.structural.StructuralSpecification;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -35,7 +35,7 @@
   public final DexEncodedAnnotation annotation;
 
   private static void specify(StructuralSpecification<DexAnnotation, ?> spec) {
-    spec.withInt(a -> a.visibility).withItem(a -> a.annotation);
+    spec.withItem(a -> a.annotation).withInt(a -> a.visibility);
   }
 
   public DexAnnotation(int visibility, DexEncodedAnnotation annotation) {
@@ -49,7 +49,7 @@
   }
 
   @Override
-  public StructuralAccept<DexAnnotation> getStructuralAccept() {
+  public StructuralMapping<DexAnnotation> getStructuralMapping() {
     return DexAnnotation::specify;
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotationDirectory.java b/src/main/java/com/android/tools/r8/graph/DexAnnotationDirectory.java
index 2ee613c..ad9bf23 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotationDirectory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotationDirectory.java
@@ -5,7 +5,7 @@
 
 import com.android.tools.r8.dex.MixedSectionCollection;
 import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -45,18 +45,18 @@
     return clazz.annotations();
   }
 
-  public List<DexEncodedMethod> sortMethodAnnotations(NamingLens namingLens) {
-    methodAnnotations.sort((a, b) -> a.method.compareToWithNamingLens(b.method, namingLens));
+  public List<DexEncodedMethod> sortMethodAnnotations(CompareToVisitor visitor) {
+    methodAnnotations.sort((a, b) -> a.method.acceptCompareTo(b.method, visitor));
     return methodAnnotations;
   }
 
-  public List<DexEncodedMethod> sortParameterAnnotations(NamingLens namingLens) {
-    parameterAnnotations.sort((a, b) -> a.method.compareToWithNamingLens(b.method, namingLens));
+  public List<DexEncodedMethod> sortParameterAnnotations(CompareToVisitor visitor) {
+    parameterAnnotations.sort((a, b) -> a.method.acceptCompareTo(b.method, visitor));
     return parameterAnnotations;
   }
 
-  public List<DexEncodedField> sortFieldAnnotations(NamingLens namingLens) {
-    fieldAnnotations.sort((a, b) -> a.field.compareToWithNamingLens(b.field, namingLens));
+  public List<DexEncodedField> sortFieldAnnotations(CompareToVisitor visitor) {
+    fieldAnnotations.sort((a, b) -> a.field.acceptCompareTo(b.field, visitor));
     return fieldAnnotations;
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotationElement.java b/src/main/java/com/android/tools/r8/graph/DexAnnotationElement.java
index fc764ff..f351642 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotationElement.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotationElement.java
@@ -5,8 +5,8 @@
 
 import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.dex.MixedSectionCollection;
-import com.android.tools.r8.utils.structural.StructuralAccept;
 import com.android.tools.r8.utils.structural.StructuralItem;
+import com.android.tools.r8.utils.structural.StructuralMapping;
 import com.android.tools.r8.utils.structural.StructuralSpecification;
 
 public class DexAnnotationElement extends DexItem implements StructuralItem<DexAnnotationElement> {
@@ -30,7 +30,7 @@
   }
 
   @Override
-  public StructuralAccept<DexAnnotationElement> getStructuralAccept() {
+  public StructuralMapping<DexAnnotationElement> getStructuralMapping() {
     return DexAnnotationElement::specify;
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java b/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java
index 86188d6..e1a64b1 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java
@@ -9,8 +9,8 @@
 import com.android.tools.r8.dex.MixedSectionCollection;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.utils.ArrayUtils;
-import com.android.tools.r8.utils.structural.StructuralAccept;
 import com.android.tools.r8.utils.structural.StructuralItem;
+import com.android.tools.r8.utils.structural.StructuralMapping;
 import com.android.tools.r8.utils.structural.StructuralSpecification;
 import com.google.common.collect.Sets;
 import java.util.Arrays;
@@ -47,7 +47,7 @@
   }
 
   @Override
-  public StructuralAccept<DexAnnotationSet> getStructuralAccept() {
+  public StructuralMapping<DexAnnotationSet> getStructuralMapping() {
     return DexAnnotationSet::specify;
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/DexCallSite.java b/src/main/java/com/android/tools/r8/graph/DexCallSite.java
index 5d521bc..9cb4704 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCallSite.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCallSite.java
@@ -10,8 +10,8 @@
 import com.android.tools.r8.graph.DexValue.DexValueMethodHandle;
 import com.android.tools.r8.graph.DexValue.DexValueMethodType;
 import com.android.tools.r8.graph.DexValue.DexValueString;
-import com.android.tools.r8.utils.structural.StructuralAccept;
 import com.android.tools.r8.utils.structural.StructuralItem;
+import com.android.tools.r8.utils.structural.StructuralMapping;
 import com.android.tools.r8.utils.structural.StructuralSpecification;
 import com.google.common.io.BaseEncoding;
 import java.io.ByteArrayOutputStream;
@@ -108,7 +108,7 @@
   }
 
   @Override
-  public StructuralAccept<DexCallSite> getStructuralAccept() {
+  public StructuralMapping<DexCallSite> getStructuralMapping() {
     return DexCallSite::specify;
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/DexDefinition.java b/src/main/java/com/android/tools/r8/graph/DexDefinition.java
index 0153cd2..c3e6998 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDefinition.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDefinition.java
@@ -104,4 +104,16 @@
   public abstract boolean isStatic();
 
   public abstract boolean isStaticMember();
+
+  public boolean isNotProgramDefinition(AppView<?> appView) {
+    if (isDexClass()) {
+      return asDexClass().isNotProgramClass();
+    }
+    DexClass clazz = appView.definitionFor(asDexEncodedMember().getHolderType());
+    return clazz == null || clazz.isNotProgramClass();
+  }
+
+  public DexType getContextType() {
+    return isDexClass() ? asDexClass().type : asDexEncodedMember().getHolderType();
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedAnnotation.java b/src/main/java/com/android/tools/r8/graph/DexEncodedAnnotation.java
index bcefcd2..98df8c7 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedAnnotation.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedAnnotation.java
@@ -6,8 +6,8 @@
 import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.dex.MixedSectionCollection;
 import com.android.tools.r8.utils.ArrayUtils;
-import com.android.tools.r8.utils.structural.StructuralAccept;
 import com.android.tools.r8.utils.structural.StructuralItem;
+import com.android.tools.r8.utils.structural.StructuralMapping;
 import com.android.tools.r8.utils.structural.StructuralSpecification;
 import java.util.Arrays;
 import java.util.function.Consumer;
@@ -37,7 +37,7 @@
   }
 
   @Override
-  public StructuralAccept<DexEncodedAnnotation> getStructuralAccept() {
+  public StructuralMapping<DexEncodedAnnotation> getStructuralMapping() {
     return DexEncodedAnnotation::specify;
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
index 133ebe5..6839371 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -20,8 +20,8 @@
 import com.android.tools.r8.ir.optimize.info.MutableFieldOptimizationInfo;
 import com.android.tools.r8.kotlin.KotlinFieldLevelInfo;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.structural.StructuralAccept;
 import com.android.tools.r8.utils.structural.StructuralItem;
+import com.android.tools.r8.utils.structural.StructuralMapping;
 import com.android.tools.r8.utils.structural.StructuralSpecification;
 
 public class DexEncodedField extends DexEncodedMember<DexEncodedField, DexField>
@@ -75,7 +75,7 @@
   }
 
   @Override
-  public StructuralAccept<DexEncodedField> getStructuralAccept() {
+  public StructuralMapping<DexEncodedField> getStructuralMapping() {
     return DexEncodedField::specify;
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 6fbb596..7d807e9 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -358,18 +358,15 @@
         this, other, map, DexEncodedMethod::syntheticSpecify);
   }
 
-  private static void compareCodeObject(Code code1, Code code2, CompareToVisitor visitor) {
+  private static int compareCodeObject(Code code1, Code code2, CompareToVisitor visitor) {
     if (code1.isCfCode() && code2.isCfCode()) {
-      code1.asCfCode().acceptCompareTo(code2.asCfCode(), visitor);
-    } else if (code1.isDexCode() && code2.isDexCode()) {
-      visitor.visit(code1.asDexCode(), code2.asDexCode(), DexCode::compareTo);
-    } else {
-      throw new Unreachable(
-          "Unexpected attempt to compare incompatible synthetic objects: "
-              + code1
-              + " and "
-              + code2);
+      return code1.asCfCode().acceptCompareTo(code2.asCfCode(), visitor);
     }
+    if (code1.isDexCode() && code2.isDexCode()) {
+      return visitor.visit(code1.asDexCode(), code2.asDexCode(), DexCode::compareTo);
+    }
+    throw new Unreachable(
+        "Unexpected attempt to compare incompatible synthetic objects: " + code1 + " and " + code2);
   }
 
   private static void hashCodeObject(Code code, HashingVisitor visitor) {
@@ -846,6 +843,12 @@
     classFileVersion = Ordered.maxIgnoreNull(classFileVersion, version);
   }
 
+  public void downgradeClassFileVersion(CfVersion version) {
+    checkIfObsolete();
+    assert version != null;
+    classFileVersion = Ordered.minIgnoreNull(classFileVersion, version);
+  }
+
   public String qualifiedName() {
     checkIfObsolete();
     return method.qualifiedName();
diff --git a/src/main/java/com/android/tools/r8/graph/DexField.java b/src/main/java/com/android/tools/r8/graph/DexField.java
index dbb2d08..70bb89b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexField.java
@@ -8,7 +8,7 @@
 import com.android.tools.r8.references.FieldReference;
 import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
-import com.android.tools.r8.utils.structural.StructuralAccept;
+import com.android.tools.r8.utils.structural.StructuralMapping;
 import com.android.tools.r8.utils.structural.StructuralSpecification;
 import java.util.Collections;
 import java.util.function.BiConsumer;
@@ -28,7 +28,7 @@
     }
   }
 
-  private static void accept(StructuralSpecification<DexField, ?> spec) {
+  private static void specify(StructuralSpecification<DexField, ?> spec) {
     spec.withItem(DexField::getHolderType).withItem(DexField::getName).withItem(DexField::getType);
   }
 
@@ -38,8 +38,8 @@
   }
 
   @Override
-  public StructuralAccept<DexField> getStructuralAccept() {
-    return DexField::accept;
+  public StructuralMapping<DexField> getStructuralMapping() {
+    return DexField::specify;
   }
 
   public DexType getType() {
@@ -134,8 +134,8 @@
   }
 
   @Override
-  public void acceptCompareTo(DexField other, CompareToVisitor visitor) {
-    visitor.visitDexField(this, other);
+  public int acceptCompareTo(DexField other, CompareToVisitor visitor) {
+    return visitor.visitDexField(this, other);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethod.java b/src/main/java/com/android/tools/r8/graph/DexMethod.java
index 97f9b6d..c91ffdf 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethod.java
@@ -9,7 +9,7 @@
 import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.references.TypeReference;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
-import com.android.tools.r8.utils.structural.StructuralAccept;
+import com.android.tools.r8.utils.structural.StructuralMapping;
 import com.android.tools.r8.utils.structural.StructuralSpecification;
 import java.util.ArrayList;
 import java.util.List;
@@ -31,13 +31,13 @@
     }
   }
 
-  private static void accept(StructuralSpecification<DexMethod, ?> spec) {
+  private static void specify(StructuralSpecification<DexMethod, ?> spec) {
     spec.withItem(DexMethod::getHolderType).withItem(DexMethod::getName).withItem(m -> m.proto);
   }
 
   @Override
-  public StructuralAccept<DexMethod> getStructuralAccept() {
-    return DexMethod::accept;
+  public StructuralMapping<DexMethod> getStructuralMapping() {
+    return DexMethod::specify;
   }
 
   @Override
@@ -46,8 +46,8 @@
   }
 
   @Override
-  public void acceptCompareTo(DexMethod other, CompareToVisitor visitor) {
-    visitor.visitDexMethod(this, other);
+  public int acceptCompareTo(DexMethod other, CompareToVisitor visitor) {
+    return visitor.visitDexMethod(this, other);
   }
 
   public DexType getParameter(int index) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java b/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
index 5ee9c8f..b369871 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
@@ -8,7 +8,7 @@
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.ir.code.Invoke.Type;
 import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.utils.structural.StructuralAccept;
+import com.android.tools.r8.utils.structural.StructuralMapping;
 import com.android.tools.r8.utils.structural.StructuralSpecification;
 import java.util.Objects;
 import org.objectweb.asm.Handle;
@@ -320,7 +320,7 @@
   }
 
   @Override
-  public StructuralAccept<DexMethodHandle> getStructuralAccept() {
+  public StructuralMapping<DexMethodHandle> getStructuralMapping() {
     return DexMethodHandle::specify;
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index ee32f8a..3e92e74 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -20,6 +20,7 @@
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.TraversalContinuation;
+import com.android.tools.r8.utils.structural.Ordered;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 import java.util.ArrayList;
@@ -669,12 +670,16 @@
     this.initialClassFileVersion = initialClassFileVersion;
   }
 
+  public void downgradeInitialClassFileVersion(CfVersion version) {
+    assert version != null;
+    this.initialClassFileVersion = Ordered.minIgnoreNull(this.initialClassFileVersion, version);
+  }
+
   public boolean hasClassFileVersion() {
     return initialClassFileVersion != null;
   }
 
   public CfVersion getInitialClassFileVersion() {
-    assert initialClassFileVersion != null;
     return initialClassFileVersion;
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/DexProto.java b/src/main/java/com/android/tools/r8/graph/DexProto.java
index 3855408..36ad355 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProto.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProto.java
@@ -5,7 +5,7 @@
 
 import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.utils.structural.StructuralAccept;
+import com.android.tools.r8.utils.structural.StructuralMapping;
 import com.android.tools.r8.utils.structural.StructuralSpecification;
 import com.google.common.collect.Iterables;
 import java.util.Collections;
@@ -25,7 +25,7 @@
     this.parameters = parameters;
   }
 
-  private static void accept(StructuralSpecification<DexProto, ?> spec) {
+  private static void specify(StructuralSpecification<DexProto, ?> spec) {
     spec.withItem(DexProto::getReturnType)
         .withItem(p -> p.parameters)
         // TODO(b/172206529): Consider removing shorty.
@@ -33,8 +33,8 @@
   }
 
   @Override
-  public StructuralAccept<DexProto> getStructuralAccept() {
-    return DexProto::accept;
+  public StructuralMapping<DexProto> getStructuralMapping() {
+    return DexProto::specify;
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/graph/DexString.java b/src/main/java/com/android/tools/r8/graph/DexString.java
index 264a7aa..031ee9d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexString.java
+++ b/src/main/java/com/android/tools/r8/graph/DexString.java
@@ -12,7 +12,7 @@
 import com.android.tools.r8.utils.ThrowingCharIterator;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
 import com.android.tools.r8.utils.structural.HashingVisitor;
-import com.android.tools.r8.utils.structural.StructuralAccept;
+import com.android.tools.r8.utils.structural.StructuralMapping;
 import java.io.UTFDataFormatException;
 import java.util.Arrays;
 import java.util.NoSuchElementException;
@@ -41,7 +41,7 @@
   }
 
   @Override
-  public StructuralAccept<DexString> getStructuralAccept() {
+  public StructuralMapping<DexString> getStructuralMapping() {
     // Structural accept is never accessed as all accept methods are defined directly.
     throw new Unreachable();
   }
@@ -53,8 +53,8 @@
   }
 
   @Override
-  public void acceptCompareTo(DexString other, CompareToVisitor visitor) {
-    visitor.visitDexString(this, other);
+  public int acceptCompareTo(DexString other, CompareToVisitor visitor) {
+    return visitor.visitDexString(this, other);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/graph/DexType.java b/src/main/java/com/android/tools/r8/graph/DexType.java
index 0b13d80..249439c 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -26,7 +26,7 @@
 import com.android.tools.r8.utils.InternalOptions.OutlineOptions;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
 import com.android.tools.r8.utils.structural.HashingVisitor;
-import com.android.tools.r8.utils.structural.StructuralAccept;
+import com.android.tools.r8.utils.structural.StructuralMapping;
 import com.google.common.collect.ImmutableList;
 import java.util.Arrays;
 import java.util.List;
@@ -58,15 +58,15 @@
   }
 
   @Override
-  public StructuralAccept<DexType> getStructuralAccept() {
+  public StructuralMapping<DexType> getStructuralMapping() {
     // Structural accept is never accessed as all accept methods are defined directly.
     throw new Unreachable();
   }
 
   // DexType overrides accept to ensure the visitors always gets a visitDexType callback.
   @Override
-  public void acceptCompareTo(DexType other, CompareToVisitor visitor) {
-    visitor.visitDexType(this, other);
+  public int acceptCompareTo(DexType other, CompareToVisitor visitor) {
+    return visitor.visitDexType(this, other);
   }
 
   // DexType overrides accept to ensure the visitors always gets a visitDexType callback.
diff --git a/src/main/java/com/android/tools/r8/graph/DexTypeList.java b/src/main/java/com/android/tools/r8/graph/DexTypeList.java
index 595c5dd..076322a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexTypeList.java
+++ b/src/main/java/com/android/tools/r8/graph/DexTypeList.java
@@ -8,8 +8,8 @@
 import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.dex.MixedSectionCollection;
 import com.android.tools.r8.utils.ArrayUtils;
-import com.android.tools.r8.utils.structural.StructuralAccept;
 import com.android.tools.r8.utils.structural.StructuralItem;
+import com.android.tools.r8.utils.structural.StructuralMapping;
 import com.android.tools.r8.utils.structural.StructuralSpecification;
 import com.google.common.collect.Iterators;
 import java.util.Arrays;
@@ -72,7 +72,7 @@
   }
 
   @Override
-  public StructuralAccept<DexTypeList> getStructuralAccept() {
+  public StructuralMapping<DexTypeList> getStructuralMapping() {
     return DexTypeList::specify;
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/DexValue.java b/src/main/java/com/android/tools/r8/graph/DexValue.java
index a54daf8..97d304b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexValue.java
+++ b/src/main/java/com/android/tools/r8/graph/DexValue.java
@@ -25,8 +25,8 @@
 import com.android.tools.r8.utils.EncodedValueUtils;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
 import com.android.tools.r8.utils.structural.HashingVisitor;
-import com.android.tools.r8.utils.structural.StructuralAccept;
 import com.android.tools.r8.utils.structural.StructuralItem;
+import com.android.tools.r8.utils.structural.StructuralMapping;
 import java.util.Arrays;
 import java.util.function.Consumer;
 import org.objectweb.asm.Handle;
@@ -114,7 +114,7 @@
   }
 
   @Override
-  public final StructuralAccept<DexValue> getStructuralAccept() {
+  public final StructuralMapping<DexValue> getStructuralMapping() {
     // DexValue is not generic at its base type (and can't as we use it as a polymorphic value),
     // so each concrete value must implement polymorphic accept functions. This base class
     // implements (most of) the polymorphic checks and concrete types implement the internal
@@ -123,12 +123,12 @@
   }
 
   @Override
-  public final void acceptCompareTo(DexValue other, CompareToVisitor visitor) {
+  public final int acceptCompareTo(DexValue other, CompareToVisitor visitor) {
     // Order first on 'kind', only equal kinds then forward to the 'kind' specific internal compare.
     if (getValueKind() != other.getValueKind()) {
-      visitor.visitInt(getValueKind().toByte(), other.getValueKind().toByte());
+      return visitor.visitInt(getValueKind().toByte(), other.getValueKind().toByte());
     } else {
-      internalAcceptCompareTo(other, visitor);
+      return internalAcceptCompareTo(other, visitor);
     }
   }
 
@@ -139,7 +139,7 @@
     internalAcceptHashing(visitor);
   }
 
-  abstract void internalAcceptCompareTo(DexValue other, CompareToVisitor visitor);
+  abstract int internalAcceptCompareTo(DexValue other, CompareToVisitor visitor);
 
   abstract void internalAcceptHashing(HashingVisitor visitor);
 
@@ -478,8 +478,8 @@
     }
 
     @Override
-    void internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
-      visitor.visitInt(value, other.asDexValueByte().value);
+    int internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
+      return visitor.visitInt(value, other.asDexValueByte().value);
     }
 
     @Override
@@ -576,8 +576,8 @@
     }
 
     @Override
-    void internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
-      visitor.visitInt(value, other.asDexValueShort().getValue());
+    int internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
+      return visitor.visitInt(value, other.asDexValueShort().getValue());
     }
 
     public short getValue() {
@@ -663,8 +663,8 @@
     }
 
     @Override
-    void internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
-      visitor.visitInt(value, other.asDexValueChar().value);
+    int internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
+      return visitor.visitInt(value, other.asDexValueChar().value);
     }
 
     @Override
@@ -764,8 +764,8 @@
     }
 
     @Override
-    void internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
-      visitor.visitInt(value, other.asDexValueInt().value);
+    int internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
+      return visitor.visitInt(value, other.asDexValueInt().value);
     }
 
     public int getValue() {
@@ -851,8 +851,8 @@
     }
 
     @Override
-    void internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
-      visitor.visitLong(value, other.asDexValueLong().value);
+    int internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
+      return visitor.visitLong(value, other.asDexValueLong().value);
     }
 
     @Override
@@ -948,8 +948,8 @@
     }
 
     @Override
-    void internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
-      visitor.visitFloat(value, other.asDexValueFloat().value);
+    int internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
+      return visitor.visitFloat(value, other.asDexValueFloat().value);
     }
 
     public float getValue() {
@@ -1041,8 +1041,8 @@
     }
 
     @Override
-    void internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
-      visitor.visitDouble(value, other.asDexValueDouble().value);
+    int internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
+      return visitor.visitDouble(value, other.asDexValueDouble().value);
     }
 
     @Override
@@ -1201,10 +1201,12 @@
     }
 
     @Override
-    void internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
-      if (DexItemBasedValueString.compareAndCheckValueStrings(this, other, visitor)) {
-        value.acceptCompareTo(other.asDexValueString().value, visitor);
+    int internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
+      int order = DexItemBasedValueString.compareAndCheckValueStrings(this, other, visitor);
+      if (order != 0) {
+        return order;
       }
+      return value.acceptCompareTo(other.asDexValueString().value, visitor);
     }
 
     @Override
@@ -1266,16 +1268,12 @@
 
     // Helper to ensure a consistent order on DexValueString and DexItemBasedValueString which are
     // both defined to have kind 'string'.
-    static boolean compareAndCheckValueStrings(DexValue v1, DexValue v2, CompareToVisitor visitor) {
+    static int compareAndCheckValueStrings(DexValue v1, DexValue v2, CompareToVisitor visitor) {
       assert v1.getValueKind() == DexValueKind.STRING;
       assert v2.getValueKind() == DexValueKind.STRING;
       int order1 = v1.isDexItemBasedValueString() ? 1 : 0;
       int order2 = v2.isDexItemBasedValueString() ? 1 : 0;
-      boolean equal = order1 == order2;
-      if (!equal) {
-        visitor.visitInt(order1, order2);
-      }
-      return equal;
+      return visitor.visitInt(order1, order2);
     }
 
     private final NameComputationInfo<?> nameComputationInfo;
@@ -1291,10 +1289,12 @@
     }
 
     @Override
-    void internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
-      if (compareAndCheckValueStrings(this, other, visitor)) {
-        visitor.visitDexReference(value, other.asDexItemBasedValueString().value);
+    int internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
+      int order = compareAndCheckValueStrings(this, other, visitor);
+      if (order != 0) {
+        return order;
       }
+      return visitor.visitDexReference(value, other.asDexItemBasedValueString().value);
     }
 
     @Override
@@ -1368,8 +1368,8 @@
     }
 
     @Override
-    void internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
-      value.acceptCompareTo(other.asDexValueType().value, visitor);
+    int internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
+      return value.acceptCompareTo(other.asDexValueType().value, visitor);
     }
 
     @Override
@@ -1410,8 +1410,8 @@
     }
 
     @Override
-    void internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
-      value.acceptCompareTo(other.asDexValueField().value, visitor);
+    int internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
+      return value.acceptCompareTo(other.asDexValueField().value, visitor);
     }
 
     @Override
@@ -1452,8 +1452,8 @@
     }
 
     @Override
-    void internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
-      value.acceptCompareTo(other.asDexValueMethod().value, visitor);
+    int internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
+      return value.acceptCompareTo(other.asDexValueMethod().value, visitor);
     }
 
     @Override
@@ -1494,8 +1494,8 @@
     }
 
     @Override
-    void internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
-      value.acceptCompareTo(other.asDexValueEnum().value, visitor);
+    int internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
+      return value.acceptCompareTo(other.asDexValueEnum().value, visitor);
     }
 
     @Override
@@ -1536,8 +1536,8 @@
     }
 
     @Override
-    void internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
-      value.acceptCompareTo(other.asDexValueMethodType().value, visitor);
+    int internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
+      return value.acceptCompareTo(other.asDexValueMethodType().value, visitor);
     }
 
     @Override
@@ -1580,8 +1580,8 @@
     }
 
     @Override
-    void internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
-      visitor.visitItemArray(values, other.asDexValueArray().values);
+    int internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
+      return visitor.visitItemArray(values, other.asDexValueArray().values);
     }
 
     @Override
@@ -1689,8 +1689,8 @@
     }
 
     @Override
-    void internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
-      value.acceptCompareTo(other.asDexValueAnnotation().value, visitor);
+    int internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
+      return value.acceptCompareTo(other.asDexValueAnnotation().value, visitor);
     }
 
     @Override
@@ -1790,9 +1790,10 @@
     }
 
     @Override
-    void internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
+    int internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
       assert this == NULL;
       assert other == NULL;
+      return 0;
     }
 
     public Object getValue() {
@@ -1882,8 +1883,8 @@
     }
 
     @Override
-    void internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
-      visitor.visitBool(value, other.asDexValueBoolean().value);
+    int internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
+      return visitor.visitBool(value, other.asDexValueBoolean().value);
     }
 
     @Override
@@ -1967,8 +1968,8 @@
     }
 
     @Override
-    void internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
-      value.acceptCompareTo(other.asDexValueMethodHandle().value, visitor);
+    int internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
+      return value.acceptCompareTo(other.asDexValueMethodHandle().value, visitor);
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java b/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
index 54d6795..5a7d18f 100644
--- a/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
@@ -108,8 +108,8 @@
     return "DexApplication (direct)";
   }
 
-  public boolean verifyWithLens(GraphLens lens) {
-    assert mappingIsValid(lens, allClasses.keySet());
+  public boolean verifyWithLens(DirectMappedDexApplication beforeLensApplication, GraphLens lens) {
+    assert mappingIsValid(beforeLensApplication.programClasses(), lens);
     assert verifyCodeObjectsOwners();
     return true;
   }
@@ -119,18 +119,22 @@
         .allMatch(
             type ->
                 lens.lookupType(type) == type
-                    || MergedClasses.hasBeenMerged(appView.verticallyMergedClasses(), type)
-                    || MergedClasses.hasBeenMerged(appView.horizontallyMergedClasses(), type));
+                    || MergedClasses.hasBeenMergedIntoDifferentType(
+                        appView.verticallyMergedClasses(), type)
+                    || MergedClasses.hasBeenMergedIntoDifferentType(
+                        appView.horizontallyMergedClasses(), type));
     assert verifyCodeObjectsOwners();
     return true;
   }
 
-  private boolean mappingIsValid(GraphLens graphLens, Iterable<DexType> types) {
+  private boolean mappingIsValid(
+      List<DexProgramClass> classesBeforeLensApplication, GraphLens lens) {
     // The lens might either map to a different type that is already present in the application
     // (e.g. relinking a type) or it might encode a type that was renamed, in which case the
     // original type will point to a definition that was renamed.
-    for (DexType type : types) {
-      DexType renamed = graphLens.lookupType(type);
+    for (DexProgramClass clazz : classesBeforeLensApplication) {
+      DexType type = clazz.getType();
+      DexType renamed = lens.lookupType(type);
       if (renamed.isIntType()) {
         continue;
       }
diff --git a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
index cdafe4e..6a70cdd 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
@@ -998,7 +998,7 @@
       JarApplicationReader application, boolean reachabilitySensitive) {
     // TODO(b/166841731): We should compute our own from the compressed format.
     int parsingOptions =
-        application.options.testing.readInputStackMaps
+        application.options.canUseInputStackMaps()
             ? ClassReader.EXPAND_FRAMES
             : ClassReader.SKIP_FRAMES;
     ProguardConfiguration configuration = application.options.getProguardConfiguration();
diff --git a/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java b/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java
index 61d9439..f315247 100644
--- a/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java
+++ b/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java
@@ -8,8 +8,10 @@
 import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.utils.Timing;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
 import com.android.tools.r8.utils.structural.CompareToVisitorWithStringTable;
 import com.android.tools.r8.utils.structural.CompareToVisitorWithTypeTable;
+import com.android.tools.r8.utils.structural.StructuralItem;
 import it.unimi.dsi.fastutil.objects.Reference2IntLinkedOpenHashMap;
 import it.unimi.dsi.fastutil.objects.Reference2IntMap;
 import it.unimi.dsi.fastutil.objects.Reference2IntMap.Entry;
@@ -20,7 +22,6 @@
 import java.util.Comparator;
 import java.util.List;
 import java.util.function.Consumer;
-import java.util.function.ToIntFunction;
 import java.util.stream.Collectors;
 
 public class ObjectToOffsetMapping {
@@ -45,6 +46,8 @@
 
   private DexString firstJumboString;
 
+  private final CompareToVisitor compareToVisitor;
+
   public ObjectToOffsetMapping(
       AppView<?> appView,
       GraphLens graphLens,
@@ -76,40 +79,56 @@
     this.lensCodeRewriter = new LensCodeRewriterUtils(appView);
     timing.begin("Sort strings");
     this.strings = createSortedMap(strings, DexString::compareTo, this::setFirstJumboString);
+    CompareToVisitor visitor =
+        new CompareToVisitorWithStringTable(namingLens, this.strings::getInt);
     timing.end();
     timing.begin("Sort types");
-    this.types = createSortedMap(types, compareWithStringTable(), this::failOnOverflow);
+    this.types = createSortedMap(types, compare(visitor), this::failOnOverflow);
+    visitor =
+        new CompareToVisitorWithTypeTable(namingLens, this.strings::getInt, this.types::getInt);
     timing.end();
     timing.begin("Sort classes");
     this.classes = sortClasses(appView.appInfo(), classes, namingLens);
     timing.end();
     timing.begin("Sort protos");
-    this.protos = createSortedMap(protos, compareWithTypeTable(), this::failOnOverflow);
+    this.protos = createSortedMap(protos, compare(visitor), this::failOnOverflow);
     timing.end();
     timing.begin("Sort methods");
-    this.methods = createSortedMap(methods, compareWithTypeTable(), this::failOnOverflow);
+    this.methods = createSortedMap(methods, compare(visitor), this::failOnOverflow);
     timing.end();
     timing.begin("Sort fields");
-    this.fields = createSortedMap(fields, compareWithTypeTable(), this::failOnOverflow);
+    this.fields = createSortedMap(fields, compare(visitor), this::failOnOverflow);
     timing.end();
     timing.begin("Sort call-sites");
-    this.callSites = createSortedMap(callSites, DexCallSite::compareTo, this::failOnOverflow);
+    this.callSites = createSortedMap(callSites, compare(visitor), this::failOnOverflow);
     timing.end();
     timing.begin("Sort method handles");
-    this.methodHandles =
-        createSortedMap(methodHandles, compareWithTypeTable(), this::failOnOverflow);
+    this.methodHandles = createSortedMap(methodHandles, compare(visitor), this::failOnOverflow);
     timing.end();
+
+    ObjectToOffsetMapping mapping = this;
+    compareToVisitor =
+        new CompareToVisitorWithTypeTable(namingLens, this.strings::getInt, this.types::getInt) {
+
+          @Override
+          public int visitDexField(DexField field1, DexField field2) {
+            return Integer.compare(mapping.fields.getInt(field1), mapping.fields.getInt(field2));
+          }
+
+          @Override
+          public int visitDexMethod(DexMethod method1, DexMethod method2) {
+            return Integer.compare(
+                mapping.methods.getInt(method1), mapping.methods.getInt(method2));
+          }
+        };
   }
 
-  private <T extends NamingLensComparable<T>> Comparator<T> compareWithStringTable() {
-    return (a, b) ->
-        CompareToVisitorWithStringTable.run(
-            a, b, namingLens, (ToIntFunction<DexString>) strings::getInt);
+  public CompareToVisitor getCompareToVisitor() {
+    return compareToVisitor;
   }
 
-  private <T extends NamingLensComparable<T>> Comparator<T> compareWithTypeTable() {
-    return (a, b) ->
-        CompareToVisitorWithTypeTable.run(a, b, namingLens, strings::getInt, types::getInt);
+  private <T extends StructuralItem<T>> Comparator<T> compare(CompareToVisitor visitor) {
+    return (a, b) -> a.acceptCompareTo(b, visitor);
   }
 
   private void setFirstJumboString(DexString string) {
diff --git a/src/main/java/com/android/tools/r8/graph/ParameterAnnotationsList.java b/src/main/java/com/android/tools/r8/graph/ParameterAnnotationsList.java
index fb642d6..7628702 100644
--- a/src/main/java/com/android/tools/r8/graph/ParameterAnnotationsList.java
+++ b/src/main/java/com/android/tools/r8/graph/ParameterAnnotationsList.java
@@ -6,8 +6,8 @@
 import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.dex.MixedSectionCollection;
 import com.android.tools.r8.utils.ArrayUtils;
-import com.android.tools.r8.utils.structural.StructuralAccept;
 import com.android.tools.r8.utils.structural.StructuralItem;
+import com.android.tools.r8.utils.structural.StructuralMapping;
 import com.android.tools.r8.utils.structural.StructuralSpecification;
 import java.util.Arrays;
 import java.util.function.Consumer;
@@ -76,7 +76,7 @@
   }
 
   @Override
-  public StructuralAccept<ParameterAnnotationsList> getStructuralAccept() {
+  public StructuralMapping<ParameterAnnotationsList> getStructuralMapping() {
     return ParameterAnnotationsList::specify;
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/classmerging/HorizontallyMergedLambdaClasses.java b/src/main/java/com/android/tools/r8/graph/classmerging/HorizontallyMergedLambdaClasses.java
index d0948bf..0b5bc41 100644
--- a/src/main/java/com/android/tools/r8/graph/classmerging/HorizontallyMergedLambdaClasses.java
+++ b/src/main/java/com/android/tools/r8/graph/classmerging/HorizontallyMergedLambdaClasses.java
@@ -33,7 +33,7 @@
   }
 
   @Override
-  public boolean hasBeenMerged(DexType type) {
+  public boolean hasBeenMergedIntoDifferentType(DexType type) {
     return mergedClasses.containsKey(type);
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/classmerging/MergedClasses.java b/src/main/java/com/android/tools/r8/graph/classmerging/MergedClasses.java
index 8fa6995..25879f1 100644
--- a/src/main/java/com/android/tools/r8/graph/classmerging/MergedClasses.java
+++ b/src/main/java/com/android/tools/r8/graph/classmerging/MergedClasses.java
@@ -15,7 +15,7 @@
 
   void forEachMergeGroup(BiConsumer<Set<DexType>, DexType> consumer);
 
-  boolean hasBeenMerged(DexType type);
+  boolean hasBeenMergedIntoDifferentType(DexType type);
 
   boolean verifyAllSourcesPruned(AppView<AppInfoWithLiveness> appView);
 
@@ -23,17 +23,18 @@
    * Determine if the class has been merged by the merged classes object. If the merged classes is
    * null then return false.
    */
-  static boolean hasBeenMerged(MergedClasses mergedClasses, DexProgramClass clazz) {
-    return hasBeenMerged(mergedClasses, clazz.type);
+  static boolean hasBeenMergedIntoDifferentType(
+      MergedClasses mergedClasses, DexProgramClass clazz) {
+    return hasBeenMergedIntoDifferentType(mergedClasses, clazz.getType());
   }
 
   /**
    * Determine if the class has been merged by the merged classes object. If the merged classes is
    * null then return false.
    */
-  static boolean hasBeenMerged(MergedClasses mergedClasses, DexType type) {
+  static boolean hasBeenMergedIntoDifferentType(MergedClasses mergedClasses, DexType type) {
     if (mergedClasses != null) {
-      return mergedClasses.hasBeenMerged(type);
+      return mergedClasses.hasBeenMergedIntoDifferentType(type);
     }
     return false;
   }
diff --git a/src/main/java/com/android/tools/r8/graph/classmerging/MergedClassesCollection.java b/src/main/java/com/android/tools/r8/graph/classmerging/MergedClassesCollection.java
index 1225127..5875506 100644
--- a/src/main/java/com/android/tools/r8/graph/classmerging/MergedClassesCollection.java
+++ b/src/main/java/com/android/tools/r8/graph/classmerging/MergedClassesCollection.java
@@ -28,9 +28,9 @@
   }
 
   @Override
-  public boolean hasBeenMerged(DexType type) {
+  public boolean hasBeenMergedIntoDifferentType(DexType type) {
     for (MergedClasses mergedClasses : collection) {
-      if (mergedClasses.hasBeenMerged(type)) {
+      if (mergedClasses.hasBeenMergedIntoDifferentType(type)) {
         return true;
       }
     }
diff --git a/src/main/java/com/android/tools/r8/graph/classmerging/StaticallyMergedClasses.java b/src/main/java/com/android/tools/r8/graph/classmerging/StaticallyMergedClasses.java
index 7046e80..79e4ef7 100644
--- a/src/main/java/com/android/tools/r8/graph/classmerging/StaticallyMergedClasses.java
+++ b/src/main/java/com/android/tools/r8/graph/classmerging/StaticallyMergedClasses.java
@@ -34,7 +34,7 @@
   }
 
   @Override
-  public boolean hasBeenMerged(DexType type) {
+  public boolean hasBeenMergedIntoDifferentType(DexType type) {
     return false;
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/classmerging/VerticallyMergedClasses.java b/src/main/java/com/android/tools/r8/graph/classmerging/VerticallyMergedClasses.java
index 4b25311..6ba32e9 100644
--- a/src/main/java/com/android/tools/r8/graph/classmerging/VerticallyMergedClasses.java
+++ b/src/main/java/com/android/tools/r8/graph/classmerging/VerticallyMergedClasses.java
@@ -60,7 +60,7 @@
   }
 
   @Override
-  public boolean hasBeenMerged(DexType type) {
+  public boolean hasBeenMergedIntoDifferentType(DexType type) {
     return hasBeenMergedIntoSubtype(type);
   }
 
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java
index 76ab41a..4d6d93f 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java
@@ -42,21 +42,17 @@
     return mergedClasses.getKeys(type);
   }
 
+  @Override
   public boolean hasBeenMergedIntoDifferentType(DexType type) {
     return mergedClasses.hasKey(type);
   }
 
-  @Override
-  public boolean hasBeenMerged(DexType type) {
-    return hasBeenMergedIntoDifferentType(type);
-  }
-
   public boolean isMergeTarget(DexType type) {
     return mergedClasses.hasValue(type);
   }
 
   public boolean hasBeenMergedOrIsMergeTarget(DexType type) {
-    return hasBeenMerged(type) || isMergeTarget(type);
+    return this.hasBeenMergedIntoDifferentType(type) || isMergeTarget(type);
   }
 
   Map<DexType, DexType> getForwardMap() {
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/DontInlinePolicy.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/DontInlinePolicy.java
index 0fb2284..9612f98 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/DontInlinePolicy.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/DontInlinePolicy.java
@@ -29,6 +29,10 @@
   private boolean disallowInlining(ProgramMethod method) {
     Code code = method.getDefinition().getCode();
 
+    if (appView.appInfo().isNeverInlineMethod(method.getReference())) {
+      return true;
+    }
+
     // For non-jar/cf code we currently cannot guarantee that markForceInline() will succeed.
     if (code == null || !code.isCfCode()) {
       return true;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
index fd8dbf7..8e52af1 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexValue;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.DominatorTree;
@@ -85,6 +86,10 @@
     return null;
   }
 
+  StaticFieldValueAnalysis asStaticFieldValueAnalysis() {
+    return null;
+  }
+
   abstract boolean isSubjectToOptimization(DexEncodedField field);
 
   void recordFieldPut(DexEncodedField field, Instruction instruction) {
@@ -148,6 +153,16 @@
       boolean priorReadsWillReadSameValue =
           !classInitializerDefaultsResult.hasStaticValue(field) && fieldPut.value().isZero();
       if (!priorReadsWillReadSameValue && fieldMaybeReadBeforeInstruction(field, fieldPut)) {
+        if (!isInstanceFieldValueAnalysis()) {
+          // At this point the value read in the field can be only the default static value, if read
+          // prior to the put, or the value put, if read after the put. We still want to record it
+          // because the default static value is typically null/0, so code present after a null/0
+          // check can take advantage of the optimization.
+          DexValue valueBeforePut = classInitializerDefaultsResult.getStaticValue(field);
+          asStaticFieldValueAnalysis()
+              .updateFieldOptimizationInfoWith2Values(
+                  field, fieldPut, fieldPut.value(), valueBeforePut);
+        }
         continue;
       }
       updateFieldOptimizationInfo(field, fieldPut, fieldPut.value());
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java
index 443ac30..cc607ac 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java
@@ -149,8 +149,8 @@
             .getOptimizationInfo()
             .getInstanceInitializerInfo(invoke)
             .fieldInitializationInfos();
-    for (DexEncodedField field : singleTarget.getHolder().instanceFields()) {
-      assert isSubjectToOptimization(field);
+    for (DexEncodedField field :
+        singleTarget.getHolder().getDirectAndIndirectInstanceFields(appView)) {
       InstanceFieldInitializationInfo info = infos.get(field);
       if (info.isArgumentInitializationInfo()) {
         int argumentIndex = info.asArgumentInitializationInfo().getArgumentIndex();
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
index 9be6c55..3ab1305 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
@@ -9,16 +9,20 @@
 import static com.android.tools.r8.ir.code.Opcodes.STATIC_PUT;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.graph.DexValue.DexValueNull;
 import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
 import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
 import com.android.tools.r8.ir.analysis.value.AbstractValueFactory;
 import com.android.tools.r8.ir.analysis.value.EnumValuesObjectState;
+import com.android.tools.r8.ir.analysis.value.NullOrAbstractValue;
 import com.android.tools.r8.ir.analysis.value.ObjectState;
 import com.android.tools.r8.ir.analysis.value.SingleFieldValue;
 import com.android.tools.r8.ir.code.ArrayPut;
@@ -59,6 +63,11 @@
   }
 
   @Override
+  StaticFieldValueAnalysis asStaticFieldValueAnalysis() {
+    return this;
+  }
+
+  @Override
   void computeFieldOptimizationInfo(ClassInitializerDefaultsResult classInitializerDefaultsResult) {
     super.computeFieldOptimizationInfo(classInitializerDefaultsResult);
 
@@ -98,19 +107,21 @@
   @Override
   void updateFieldOptimizationInfo(DexEncodedField field, FieldInstruction fieldPut, Value value) {
     // Abstract value.
-    Value root = value.getAliasedValue();
-    AbstractValue abstractValue = root.getAbstractValue(appView, context);
-    if (abstractValue.isUnknown()) {
-      feedback.recordFieldHasAbstractValue(field, appView, computeSingleFieldValue(field, root));
-    } else {
-      feedback.recordFieldHasAbstractValue(field, appView, abstractValue);
-    }
+    feedback.recordFieldHasAbstractValue(field, appView, getOrComputeAbstractValue(value, field));
 
+    setDynamicType(field, value, false);
+  }
+
+  private void setDynamicType(DexEncodedField field, Value value, boolean maybeNull) {
     // Dynamic upper bound type.
     TypeElement fieldType =
         TypeElement.fromDexType(field.field.type, Nullability.maybeNull(), appView);
     TypeElement dynamicUpperBoundType = value.getDynamicUpperBoundType(appView);
     if (dynamicUpperBoundType.strictlyLessThan(fieldType, appView)) {
+      if (maybeNull && dynamicUpperBoundType.isDefinitelyNotNull()) {
+        assert dynamicUpperBoundType.isReferenceType();
+        dynamicUpperBoundType = dynamicUpperBoundType.asReferenceType().asMaybeNull();
+      }
       feedback.markFieldHasDynamicUpperBoundType(field, dynamicUpperBoundType);
     }
 
@@ -118,10 +129,35 @@
     ClassTypeElement dynamicLowerBoundType = value.getDynamicLowerBoundType(appView);
     if (dynamicLowerBoundType != null) {
       assert dynamicLowerBoundType.lessThanOrEqual(dynamicUpperBoundType, appView);
+      if (maybeNull && dynamicLowerBoundType.isDefinitelyNotNull()) {
+        dynamicLowerBoundType = dynamicLowerBoundType.asMaybeNull().asClassType();
+      }
       feedback.markFieldHasDynamicLowerBoundType(field, dynamicLowerBoundType);
     }
   }
 
+  public void updateFieldOptimizationInfoWith2Values(
+      DexEncodedField field, FieldInstruction fieldPut, Value valuePut, DexValue valueBeforePut) {
+    // We are interested in the AbstractValue only if it's null or a value, so we can use the value
+    // if the code is protected by a null check.
+    if (valueBeforePut != DexValueNull.NULL) {
+      return;
+    }
+    feedback.recordFieldHasAbstractValue(
+        field, appView, NullOrAbstractValue.create(getOrComputeAbstractValue(valuePut, field)));
+
+    setDynamicType(field, valuePut, true);
+  }
+
+  private AbstractValue getOrComputeAbstractValue(Value value, DexEncodedField field) {
+    Value root = value.getAliasedValue();
+    AbstractValue abstractValue = root.getAbstractValue(appView, context);
+    if (abstractValue.isUnknown()) {
+      return computeSingleFieldValue(field, root);
+    }
+    return abstractValue;
+  }
+
   private SingleFieldValue computeSingleFieldValue(DexEncodedField field, Value value) {
     assert !value.hasAliasedValue();
     SingleFieldValue result = computeSingleEnumFieldValue(value);
@@ -273,8 +309,12 @@
     }
 
     NewInstance newInstance = value.definition.asNewInstance();
+    // Some enums have direct subclasses, and the subclass is instantiated here.
     if (newInstance.clazz != context.getHolderType()) {
-      return null;
+      DexClass dexClass = appView.definitionFor(newInstance.clazz);
+      if (dexClass == null || dexClass.superType != context.getHolderType()) {
+        return null;
+      }
     }
 
     if (value.hasDebugUsers() || value.hasPhiUsers()) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java
index 41a0192..e53f27b 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java
@@ -88,6 +88,14 @@
     return false;
   }
 
+  public boolean isNullOrAbstractValue() {
+    return false;
+  }
+
+  public NullOrAbstractValue asNullOrAbstractValue() {
+    return null;
+  }
+
   public AbstractValue join(AbstractValue other) {
     if (isBottom() || other.isUnknown()) {
       return other;
@@ -98,6 +106,12 @@
     if (equals(other)) {
       return this;
     }
+    if (isNull()) {
+      return NullOrAbstractValue.create(other);
+    }
+    if (other.isNull()) {
+      return NullOrAbstractValue.create(this);
+    }
     return UnknownValue.getInstance();
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/NullOrAbstractValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/NullOrAbstractValue.java
new file mode 100644
index 0000000..2bf329d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/NullOrAbstractValue.java
@@ -0,0 +1,68 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.value;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+
+public class NullOrAbstractValue extends AbstractValue {
+
+  private final AbstractValue value;
+
+  private NullOrAbstractValue(AbstractValue value) {
+    this.value = value;
+  }
+
+  public static AbstractValue create(AbstractValue value) {
+    if (value.isBottom() || value.isUnknown() || value.isNull() || value.isNullOrAbstractValue()) {
+      return value;
+    }
+    return new NullOrAbstractValue(value);
+  }
+
+  @Override
+  public boolean isNonTrivial() {
+    return true;
+  }
+
+  @Override
+  public boolean isNullOrAbstractValue() {
+    return true;
+  }
+
+  @Override
+  public NullOrAbstractValue asNullOrAbstractValue() {
+    return this;
+  }
+
+  public AbstractValue getNonNullValue() {
+    return value;
+  }
+
+  @Override
+  public NullOrAbstractValue rewrittenWithLens(
+      AppView<AppInfoWithLiveness> appView, GraphLens lens) {
+    return new NullOrAbstractValue(value.rewrittenWithLens(appView, lens));
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (o == null) {
+      return false;
+    }
+    return this.getClass() == o.getClass() && value.equals(((NullOrAbstractValue) o).value);
+  }
+
+  @Override
+  public int hashCode() {
+    return value.hashCode() * 7;
+  }
+
+  @Override
+  public String toString() {
+    return "Null or " + value.toString();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ValueType.java b/src/main/java/com/android/tools/r8/ir/code/ValueType.java
index 8b06849..0ae9459 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ValueType.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ValueType.java
@@ -9,8 +9,9 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.analysis.type.PrimitiveTypeElement;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
-import com.android.tools.r8.utils.structural.StructuralAccept;
 import com.android.tools.r8.utils.structural.StructuralItem;
+import com.android.tools.r8.utils.structural.StructuralMapping;
+import com.android.tools.r8.utils.structural.StructuralSpecification;
 
 public enum ValueType implements StructuralItem<ValueType> {
   OBJECT,
@@ -19,14 +20,18 @@
   LONG,
   DOUBLE;
 
+  private static void specify(StructuralSpecification<ValueType, ?> spec) {
+    spec.withInt(Enum::ordinal);
+  }
+
   @Override
   public ValueType self() {
     return this;
   }
 
   @Override
-  public StructuralAccept<ValueType> getStructuralAccept() {
-    return spec -> spec.withInt(Enum::ordinal);
+  public StructuralMapping<ValueType> getStructuralMapping() {
+    return ValueType::specify;
   }
 
   public boolean isObject() {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 0974715..2ef58cd 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -550,10 +550,20 @@
           definition.getMutableOptimizationInfo().setReachabilitySensitive(isReachabilitySensitive);
           convertMethod(method);
         });
+    // The class file version is downgraded after compilation. Some of the desugaring might need
+    // the initial class file version to determine how far a method can be downgraded.
+    if (clazz.hasClassFileVersion()) {
+      clazz.downgradeInitialClassFileVersion(
+          appView.options().classFileVersionAfterDesugaring(clazz.getInitialClassFileVersion()));
+    }
   }
 
   private void convertMethod(ProgramMethod method) {
     DexEncodedMethod definition = method.getDefinition();
+    if (definition.hasClassFileVersion()) {
+      definition.downgradeClassFileVersion(
+          appView.options().classFileVersionAfterDesugaring(definition.getClassFileVersion()));
+    }
     if (definition.getCode() == null) {
       return;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index 6c1c172..bff68b1 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.ir.desugar;
 
 import com.android.tools.r8.DesugarGraphConsumer;
+import com.android.tools.r8.cf.CfVersion;
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.Unimplemented;
@@ -54,6 +55,7 @@
 import com.android.tools.r8.utils.Pair;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.android.tools.r8.utils.collections.SortedProgramMethodSet;
+import com.android.tools.r8.utils.structural.Ordered;
 import com.google.common.collect.Sets;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -214,6 +216,21 @@
     return emulatedInterfaces.get(itf);
   }
 
+  private void leavingStaticInvokeToInterface(
+      DexProgramClass holder, DexEncodedMethod encodedMethod) {
+    // When leaving static interface method invokes possibly upgrade the class file
+    // version, but don't go above the initial class file version. If the input was
+    // 1.7 or below, this will make a VerificationError on the input a VerificationError
+    // on the output. If the input was 1.8 or above the runtime behaviour (potential ICCE)
+    // will remain the same.
+    if (holder.hasClassFileVersion()) {
+      encodedMethod.upgradeClassFileVersion(
+          Ordered.min(CfVersion.V1_8, holder.getInitialClassFileVersion()));
+    } else {
+      encodedMethod.upgradeClassFileVersion(CfVersion.V1_8);
+    }
+  }
+
   // Rewrites the references to static and default interface methods.
   // NOTE: can be called for different methods concurrently.
   public void rewriteMethodReferences(IRCode code) {
@@ -257,6 +274,9 @@
             // NOTE: leave unchanged those calls to undefined targets. This may lead to runtime
             // exception but we can not report it as error since it can also be the intended
             // behavior.
+            if (invokeStatic.getInterfaceBit()) {
+              leavingStaticInvokeToInterface(context.getHolder(), encodedMethod);
+            }
             warnMissingType(encodedMethod.method, method.holder);
           } else if (clazz.isInterface()) {
             if (isNonDesugaredLibraryClass(clazz)) {
@@ -304,14 +324,25 @@
                         invokeStatic.outValue(),
                         invokeStatic.arguments()));
                 synchronized (synthesizedMethods) {
+                  // The synthetic dispatch class has static interface method invokes, so set
+                  // the class file version accordingly.
+                  newProgramMethod.getDefinition().upgradeClassFileVersion(CfVersion.V1_8);
                   synthesizedMethods.add(newProgramMethod);
                 }
+              } else {
+                // When leaving static interface method invokes upgrade the class file version.
+                encodedMethod.upgradeClassFileVersion(CfVersion.V1_8);
               }
             } else {
               instructions.replaceCurrentInstruction(
                   new InvokeStatic(staticAsMethodOfCompanionClass(method),
                       invokeStatic.outValue(), invokeStatic.arguments()));
             }
+          } else {
+            assert !clazz.isInterface();
+            if (invokeStatic.getInterfaceBit()) {
+              leavingStaticInvokeToInterface(context.getHolder(), encodedMethod);
+            }
           }
           continue;
         }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
index 88e1200..abe232f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
@@ -213,7 +213,7 @@
         ParameterAnnotationsList.empty(),
         code,
         true,
-        iface.hasClassFileVersion() ? iface.getInitialClassFileVersion() : null);
+        iface.getInitialClassFileVersion());
   }
 
   private void processVirtualInterfaceMethods(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java
index b1d8880..23342df 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java
@@ -89,6 +89,15 @@
       }
       return false;
     }
+
+    public DexValue getStaticValue(DexEncodedField field) {
+      assert hasStaticValue(field);
+      assert field.isStatic();
+      if (fieldsWithStaticValues != null && fieldsWithStaticValues.containsKey(field)) {
+        return fieldsWithStaticValues.get(field);
+      }
+      return field.getStaticValue();
+    }
   }
 
   private static class WaveDoneAction implements Action {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
index 033a233..b7addaa 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
@@ -14,13 +14,10 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfo;
-import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfoMap;
 import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
 import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
-import com.android.tools.r8.ir.analysis.value.ObjectState;
 import com.android.tools.r8.ir.analysis.value.SingleNumberValue;
 import com.android.tools.r8.ir.analysis.value.SingleStringValue;
 import com.android.tools.r8.ir.code.ArrayGet;
@@ -37,7 +34,6 @@
 import com.android.tools.r8.ir.code.InvokeVirtual;
 import com.android.tools.r8.ir.code.StaticGet;
 import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.ir.optimize.SwitchMapCollector;
 import com.android.tools.r8.ir.optimize.info.FieldOptimizationInfo;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.ArrayUtils;
@@ -103,40 +99,20 @@
 
         FieldOptimizationInfo optimizationInfo = definition.getOptimizationInfo();
         AbstractValue abstractValue = optimizationInfo.getAbstractValue();
-        if (!abstractValue.isSingleFieldValue()) {
-          continue;
-        }
-
-        ObjectState objectState = abstractValue.asSingleFieldValue().getState();
-        if (objectState.isEmpty()) {
-          continue;
-        }
 
         Value outValue = methodWithReceiver.outValue();
         if (isOrdinalInvoke) {
-          DexField ordinalField = appView.dexItemFactory().enumMembers.ordinalField;
-          DexEncodedField ordinalDefinition =
-              ordinalField.lookupOnClass(appView.definitionForHolder(ordinalField));
-          if (ordinalDefinition != null) {
-            SingleNumberValue ordinalValue =
-                objectState.getAbstractFieldValue(ordinalDefinition).asSingleNumberValue();
+          SingleNumberValue ordinalValue =
+              getOrdinalValue(code, abstractValue, methodWithReceiver.getReceiver().isNeverNull());
             if (ordinalValue != null) {
               iterator.replaceCurrentInstruction(
                   new ConstNumber(outValue, ordinalValue.getValue()));
-            }
           }
           continue;
         }
 
-        DexField nameField = appView.dexItemFactory().enumMembers.nameField;
-        DexEncodedField nameDefinition =
-            nameField.lookupOnClass(appView.definitionForHolder(nameField));
-        if (nameField == null) {
-          continue;
-        }
-
         SingleStringValue nameValue =
-            objectState.getAbstractFieldValue(nameDefinition).asSingleStringValue();
+            getNameValue(code, abstractValue, methodWithReceiver.getReceiver().isNeverNull());
         if (nameValue == null) {
           continue;
         }
@@ -228,7 +204,7 @@
         continue;
       }
 
-      Int2IntMap ordinalToTargetMap = computeOrdinalToTargetMap(switchInsn, info);
+      Int2IntMap ordinalToTargetMap = computeOrdinalToTargetMap(code, switchInsn, info);
       if (ordinalToTargetMap == null) {
         continue;
       }
@@ -315,24 +291,62 @@
     }
   }
 
-  private Int2IntMap computeOrdinalToTargetMap(IntSwitch switchInsn, EnumSwitchInfo info) {
-    Int2IntMap ordinalToTargetMap = new Int2IntArrayMap(switchInsn.numberOfKeys());
+  private Int2IntArrayMap computeOrdinalToTargetMap(
+      IRCode code, IntSwitch switchInsn, EnumSwitchInfo info) {
+    Int2IntArrayMap ordinalToTargetMap = new Int2IntArrayMap(switchInsn.numberOfKeys());
     for (int i = 0; i < switchInsn.numberOfKeys(); i++) {
       assert switchInsn.targetBlockIndices()[i] != switchInsn.getFallthroughBlockIndex();
       DexField field = info.indexMap.get(switchInsn.getKey(i));
-      EnumValueInfo valueInfo = info.valueInfoMap.getEnumValueInfo(field);
-      if (valueInfo != null) {
-        if (appView.appInfo().isPinned(field)) {
+      DexEncodedField enumInstanceField =
+          appView.appInfo().resolveField(field, code.context()).getResolvedField();
+      if (enumInstanceField == null) {
+        // The switch map refers to a field on the enum that does not exist in this compilation.
+      } else {
+        AbstractValue abstractValue = enumInstanceField.getOptimizationInfo().getAbstractValue();
+        // The rewriting effectively leaves in place the myEnum.ordinal() call, so if the value
+        // is null, the same NPE happens at runtime, we can assume the value is non null in the
+        // switch map after the ordinal call.
+        SingleNumberValue ordinalValue = getOrdinalValue(code, abstractValue, true);
+        if (ordinalValue == null) {
           return null;
         }
-        ordinalToTargetMap.put(valueInfo.ordinal, switchInsn.targetBlockIndices()[i]);
-      } else {
-        // The switch map refers to a field on the enum that does not exist in this compilation.
+        ordinalToTargetMap.put(
+            ordinalValue.asSingleNumberValue().getIntValue(), switchInsn.targetBlockIndices()[i]);
       }
     }
     return ordinalToTargetMap;
   }
 
+  private SingleStringValue getNameValue(
+      IRCode code, AbstractValue abstractValue, boolean neverNull) {
+    AbstractValue ordinalValue =
+        getEnumFieldValue(code, abstractValue, factory.enumMembers.nameField, neverNull);
+    return ordinalValue == null ? null : ordinalValue.asSingleStringValue();
+  }
+
+  private SingleNumberValue getOrdinalValue(
+      IRCode code, AbstractValue abstractValue, boolean neverNull) {
+    AbstractValue ordinalValue =
+        getEnumFieldValue(code, abstractValue, factory.enumMembers.ordinalField, neverNull);
+    return ordinalValue == null ? null : ordinalValue.asSingleNumberValue();
+  }
+
+  private AbstractValue getEnumFieldValue(
+      IRCode code, AbstractValue abstractValue, DexField field, boolean neverNull) {
+    if (neverNull && abstractValue.isNullOrAbstractValue()) {
+      abstractValue = abstractValue.asNullOrAbstractValue().getNonNullValue();
+    }
+    if (!abstractValue.isSingleFieldValue()) {
+      return null;
+    }
+    DexEncodedField encodedField =
+        appView.appInfo().resolveField(field, code.context()).getResolvedField();
+    if (encodedField == null) {
+      return null;
+    }
+    return abstractValue.asSingleFieldValue().getState().getAbstractFieldValue(encodedField);
+  }
+
   private static final class EnumSwitchInfo {
 
     final DexType enumClass;
@@ -340,21 +354,18 @@
     final Instruction arrayGet;
     public final Instruction staticGet;
     final Int2ReferenceMap<DexField> indexMap;
-    final EnumValueInfoMap valueInfoMap;
 
     private EnumSwitchInfo(
         DexType enumClass,
         Instruction ordinalInvoke,
         Instruction arrayGet,
         Instruction staticGet,
-        Int2ReferenceMap<DexField> indexMap,
-        EnumValueInfoMap valueInfoMap) {
+        Int2ReferenceMap<DexField> indexMap) {
       this.enumClass = enumClass;
       this.ordinalInvoke = ordinalInvoke;
       this.arrayGet = arrayGet;
       this.staticGet = staticGet;
       this.indexMap = indexMap;
-      this.valueInfoMap = valueInfoMap;
     }
   }
 
@@ -371,8 +382,7 @@
    *
    * </blockquote>
    *
-   * and extracts the components and the index and ordinal maps. See {@link
-   * EnumValueInfoMapCollector} and {@link SwitchMapCollector} for details.
+   * and extracts the components and the index and ordinal maps.
    */
   private EnumSwitchInfo analyzeSwitchOverEnum(IntSwitch switchInsn) {
     Instruction input = switchInsn.inValues().get(0).definition;
@@ -412,10 +422,6 @@
     }
     // Due to member rebinding, only the fields are certain to provide the actual enums class.
     DexType enumType = indexMap.values().iterator().next().holder;
-    EnumValueInfoMap valueInfoMap = appView.appInfo().getEnumValueInfoMap(enumType);
-    if (valueInfoMap == null) {
-      return null;
-    }
-    return new EnumSwitchInfo(enumType, ordinalInvoke, arrayGet, staticGet, indexMap, valueInfoMap);
+    return new EnumSwitchInfo(enumType, ordinalInvoke, arrayGet, staticGet, indexMap);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
index 58c9f2d..25688ce 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -263,10 +263,13 @@
               || options.isDesugaredLibraryCompilation()
               || options.cfToCfDesugar
           : "Expected class file version for " + method.method.toSourceString();
-      // TODO(b/146424042): We may call static methods on interface classes so we have to go for
-      //  Java 8.
-      assert MIN_VERSION_FOR_COMPILER_GENERATED_CODE.isLessThan(CfVersion.V1_8);
-      return options.cfToCfDesugar ? CfVersion.V1_8 : MIN_VERSION_FOR_COMPILER_GENERATED_CODE;
+      assert MIN_VERSION_FOR_COMPILER_GENERATED_CODE.isLessThan(
+          options.classFileVersionAfterDesugaring(InternalOptions.SUPPORTED_CF_VERSION));
+      // Any desugaring rewrites which cannot meet the default class file version after
+      // desugaring must upgrade the class file version during desugaring.
+      return options.cfToCfDesugar
+          ? options.classFileVersionAfterDesugaring(InternalOptions.SUPPORTED_CF_VERSION)
+          : MIN_VERSION_FOR_COMPILER_GENERATED_CODE;
     }
     return method.getClassFileVersion();
   }
diff --git a/src/main/java/com/android/tools/r8/repackaging/RepackagingConstraintGraph.java b/src/main/java/com/android/tools/r8/repackaging/RepackagingConstraintGraph.java
index b4760c1..bcbba53 100644
--- a/src/main/java/com/android/tools/r8/repackaging/RepackagingConstraintGraph.java
+++ b/src/main/java/com/android/tools/r8/repackaging/RepackagingConstraintGraph.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.repackaging;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexDefinition;
 import com.android.tools.r8.graph.DexEncodedMember;
 import com.android.tools.r8.graph.DexEncodedMethod;
@@ -41,10 +42,13 @@
   private final ProgramPackage pkg;
   private final Map<DexDefinition, Node> nodes = new IdentityHashMap<>();
   private final Set<Node> pinnedNodes = Sets.newIdentityHashSet();
+  private final Node libraryBoundaryNode;
 
   public RepackagingConstraintGraph(AppView<AppInfoWithLiveness> appView, ProgramPackage pkg) {
     this.appView = appView;
     this.pkg = pkg;
+    libraryBoundaryNode = createNode(appView.definitionFor(appView.dexItemFactory().objectType));
+    pinnedNodes.add(libraryBoundaryNode);
   }
 
   /** Returns true if all classes in the package can be repackaged. */
@@ -75,6 +79,13 @@
   }
 
   Node getNode(DexDefinition definition) {
+    if (definition.isNotProgramDefinition(appView)) {
+      String packageDescriptor = definition.getContextType().getPackageDescriptor();
+      if (packageDescriptor.equals(pkg.getPackageDescriptor())) {
+        return libraryBoundaryNode;
+      }
+      return null;
+    }
     return nodes.get(definition);
   }
 
@@ -110,7 +121,11 @@
     }
 
     // Trace the references to the inner and outer classes.
-    clazz.getInnerClasses().forEach(registry::registerInnerClassAttribute);
+    clazz
+        .getInnerClasses()
+        .forEach(
+            innerClassAttribute ->
+                registry.registerInnerClassAttribute(clazz, innerClassAttribute));
 
     // Trace the references from the enclosing method attribute.
     EnclosingMethodAttribute attr = clazz.getEnclosingMethodAttribute();
@@ -137,8 +152,8 @@
     definition.getProto().forEachType(registry::registerTypeReference);
 
     // Check if this overrides a package-private method.
-    DexProgramClass superClass =
-        appView.programDefinitionFor(method.getHolder().getSuperType(), method.getHolder());
+    DexClass superClass =
+        appView.definitionFor(method.getHolder().getSuperType(), method.getHolder());
     if (superClass != null) {
       registry.registerMemberAccess(
           appView.appInfo().resolveMethodOn(superClass, method.getReference()));
diff --git a/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java b/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java
index 57361d3..91484f7 100644
--- a/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.ClassAccessFlags;
 import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndMember;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
@@ -19,7 +20,6 @@
 import com.android.tools.r8.graph.InnerClassAttribute;
 import com.android.tools.r8.graph.MemberResolutionResult;
 import com.android.tools.r8.graph.ProgramDefinition;
-import com.android.tools.r8.graph.ProgramMember;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.ResolutionResult;
 import com.android.tools.r8.graph.SuccessfulMemberResolutionResult;
@@ -48,7 +48,7 @@
     this.node = constraintGraph.getNode(context.getDefinition());
   }
 
-  private boolean isOnlyAccessibleFromSamePackage(DexProgramClass referencedClass) {
+  private boolean isOnlyAccessibleFromSamePackage(DexClass referencedClass) {
     ClassAccessFlags accessFlags = referencedClass.getAccessFlags();
     if (accessFlags.isPackagePrivate()) {
       return true;
@@ -60,8 +60,8 @@
     return false;
   }
 
-  private boolean isOnlyAccessibleFromSamePackage(ProgramMember<?, ?> member) {
-    AccessFlags<?> accessFlags = member.getDefinition().getAccessFlags();
+  private boolean isOnlyAccessibleFromSamePackage(DexClassAndMember<?, ?> member) {
+    AccessFlags<?> accessFlags = member.getAccessFlags();
     if (accessFlags.isPackagePrivate()) {
       return true;
     }
@@ -96,22 +96,21 @@
     }
 
     // Check access to the initial resolution holder.
-    registerTypeAccess(successfulResolutionResult.getInitialResolutionHolder());
+    registerClassTypeAccess(successfulResolutionResult.getInitialResolutionHolder());
 
     // Similarly, check access to the resolved member.
-    ProgramMember<?, ?> resolvedMember =
-        successfulResolutionResult.getResolvedMember().asProgramMember(appInfo);
-    if (resolvedMember != null) {
+    DexClassAndMember<?, ?> resolutionPair = successfulResolutionResult.getResolutionPair();
+    if (resolutionPair != null) {
       RepackagingConstraintGraph.Node resolvedMemberNode =
-          constraintGraph.getNode(resolvedMember.getDefinition());
-      if (resolvedMemberNode != null && isOnlyAccessibleFromSamePackage(resolvedMember)) {
+          constraintGraph.getNode(resolutionPair.getDefinition());
+      if (resolvedMemberNode != null && isOnlyAccessibleFromSamePackage(resolutionPair)) {
         node.addNeighbor(resolvedMemberNode);
       }
     }
   }
 
   private void registerTypeAccess(DexType type) {
-    registerTypeAccess(type, this::registerTypeAccess);
+    registerTypeAccess(type, this::registerClassTypeAccess);
   }
 
   private void registerTypeAccess(DexType type, Consumer<DexClass> consumer) {
@@ -129,20 +128,17 @@
     }
   }
 
-  private void registerTypeAccess(DexClass clazz) {
-    registerTypeAccess(clazz, this::isOnlyAccessibleFromSamePackage);
+  private void registerClassTypeAccess(DexClass clazz) {
+    registerClassTypeAccess(clazz, this::isOnlyAccessibleFromSamePackage);
   }
 
-  private void registerTypeAccess(DexClass clazz, Predicate<DexProgramClass> predicate) {
+  private void registerClassTypeAccess(DexClass clazz, Predicate<DexClass> predicate) {
     // We only want to connect the current method node to the class node if the access requires the
-    // two nodes to be in the same package. Therefore, we ignore accesses to non-program classes
-    // and program classes outside the current package.
-    DexProgramClass programClass = clazz.asProgramClass();
-    if (programClass != null) {
-      RepackagingConstraintGraph.Node classNode = constraintGraph.getNode(programClass);
-      if (classNode != null && predicate.test(programClass)) {
-        node.addNeighbor(classNode);
-      }
+    // two nodes to be in the same package. Therefore, we ignore accesses to program classes outside
+    // the current package.
+    RepackagingConstraintGraph.Node classNode = constraintGraph.getNode(clazz);
+    if (classNode != null && predicate.test(clazz)) {
+      node.addNeighbor(classNode);
     }
   }
 
@@ -218,21 +214,22 @@
     if (enclosingMethodAttribute.getEnclosingClass() != null) {
       registerTypeAccess(
           enclosingMethodAttribute.getEnclosingClass(),
-          clazz -> registerTypeAccess(clazz, alwaysTrue()));
+          clazz -> registerClassTypeAccess(clazz, alwaysTrue()));
     }
     if (enclosingMethodAttribute.getEnclosingMethod() != null) {
       ProgramMethod method = registerMethodReference(enclosingMethodAttribute.getEnclosingMethod());
       if (method != null) {
-        registerTypeAccess(method.getHolder(), alwaysTrue());
+        registerClassTypeAccess(method.getHolder(), alwaysTrue());
       }
     }
   }
 
-  public void registerInnerClassAttribute(InnerClassAttribute innerClassAttribute) {
+  public void registerInnerClassAttribute(
+      DexProgramClass outer, InnerClassAttribute innerClassAttribute) {
     // For references in inner class attributes we add an edge from the context to the referenced
     // class even if the referenced class would be accessible from another package, to make sure
     // that we don't split such classes into different packages.
     innerClassAttribute.forEachType(
-        type -> registerTypeAccess(type, clazz -> registerTypeAccess(clazz, alwaysTrue())));
+        type -> registerTypeAccess(type, clazz -> registerClassTypeAccess(clazz, alwaysTrue())));
   }
 }
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
index 4d621a3..6fa737b 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
@@ -282,6 +282,8 @@
                 methodBuilder
                     .setAccessFlags(definition.accessFlags)
                     .setProto(definition.getProto())
+                    .setClassFileVersion(
+                        definition.hasClassFileVersion() ? definition.getClassFileVersion() : null)
                     .setCode(m -> definition.getCode());
               });
           DexProgramClass externalSyntheticClass = builder.build();
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
index ee17db8..9fcc7d9 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.synthesis;
 
+import com.android.tools.r8.cf.CfVersion;
 import com.android.tools.r8.graph.Code;
 import com.android.tools.r8.graph.DexAnnotationSet;
 import com.android.tools.r8.graph.DexEncodedMethod;
@@ -21,6 +22,7 @@
   private final SyntheticClassBuilder parent;
   private final String name;
   private DexProto proto = null;
+  private CfVersion classFileVersion;
   private SyntheticCodeGenerator codeGenerator = null;
   private MethodAccessFlags accessFlags = null;
 
@@ -34,6 +36,11 @@
     return this;
   }
 
+  public SyntheticMethodBuilder setClassFileVersion(CfVersion classFileVersion) {
+    this.classFileVersion = classFileVersion;
+    return this;
+  }
+
   public SyntheticMethodBuilder setCode(SyntheticCodeGenerator codeGenerator) {
     this.codeGenerator = codeGenerator;
     return this;
@@ -55,7 +62,8 @@
             DexAnnotationSet.empty(),
             ParameterAnnotationsList.empty(),
             getCodeObject(methodSignature),
-            isCompilerSynthesized);
+            isCompilerSynthesized,
+            classFileVersion);
     assert isValidSyntheticMethod(method);
     return method;
   }
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index f4cdbaa..cd3a2db 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -63,6 +63,7 @@
 import com.android.tools.r8.utils.IROrdering.IdentityIROrdering;
 import com.android.tools.r8.utils.IROrdering.NondeterministicIROrdering;
 import com.android.tools.r8.utils.collections.SortedProgramMethodSet;
+import com.android.tools.r8.utils.structural.Ordered;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Equivalence.Wrapper;
 import com.google.common.collect.ImmutableList;
@@ -595,6 +596,10 @@
         || getProguardConfiguration().getKeepAttributes().innerClasses;
   }
 
+  public boolean canUseInputStackMaps() {
+    return testing.readInputStackMaps ? testing.readInputStackMaps : isGeneratingClassFiles();
+  }
+
   public boolean printCfg = false;
   public String printCfgFile;
   public boolean ignoreMissingClasses = false;
@@ -1529,6 +1534,15 @@
     return (isGeneratingClassFiles() && !cfToCfDesugar) || hasMinApi(AndroidApiLevel.K);
   }
 
+  public CfVersion classFileVersionAfterDesugaring(CfVersion version) {
+    if (!isDesugaring()) {
+      return version;
+    }
+    CfVersion maxVersionAfterDesugar =
+        canUseDefaultAndStaticInterfaceMethods() ? CfVersion.V1_8 : CfVersion.V1_7;
+    return Ordered.min(maxVersionAfterDesugar, version);
+  }
+
   // The Apache Harmony-based AssertionError constructor which takes an Object on API 15 and older
   // calls the Error supertype constructor with null as the exception cause. This prevents
   // subsequent calls to initCause() because its implementation checks that cause==this before
diff --git a/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitor.java b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitor.java
index 32221a0..a0b2104 100644
--- a/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitor.java
+++ b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitor.java
@@ -17,45 +17,48 @@
 /** Base class for a visitor implementing compareTo on a structural item. */
 public abstract class CompareToVisitor {
 
-  public abstract void visitBool(boolean value1, boolean value2);
+  public abstract int visitBool(boolean value1, boolean value2);
 
-  public abstract void visitInt(int value1, int value2);
+  public abstract int visitInt(int value1, int value2);
 
-  public abstract void visitLong(long value1, long value2);
+  public abstract int visitLong(long value1, long value2);
 
-  public abstract void visitFloat(float value1, float value2);
+  public abstract int visitFloat(float value1, float value2);
 
-  public abstract void visitDouble(double value1, double value2);
+  public abstract int visitDouble(double value1, double value2);
 
   /** Base for visiting an enumeration of items. */
-  public abstract <S> void visitItemIterator(
+  public abstract <S> int visitItemIterator(
       Iterator<S> it1, Iterator<S> it2, CompareToAccept<S> compareToAccept);
 
-  public final <S extends StructuralItem<S>> void visitItemArray(S[] items1, S[] items2) {
-    visitItemCollection(Arrays.asList(items1), Arrays.asList(items2));
+  public final <S extends StructuralItem<S>> int visitItemArray(S[] items1, S[] items2) {
+    return visitItemCollection(Arrays.asList(items1), Arrays.asList(items2));
   }
 
-  public final <S extends StructuralItem<S>> void visitItemCollection(
+  public final <S extends StructuralItem<S>> int visitItemCollection(
       Collection<S> items1, Collection<S> items2) {
-    visitItemIterator(items1.iterator(), items2.iterator(), S::acceptCompareTo);
+    return visitItemIterator(
+        items1.iterator(),
+        items2.iterator(),
+        (s, other, visitor) -> s.acceptCompareTo(other, visitor));
   }
 
-  public abstract void visitDexString(DexString string1, DexString string2);
+  public abstract int visitDexString(DexString string1, DexString string2);
 
-  public abstract void visitDexType(DexType type1, DexType type2);
+  public abstract int visitDexType(DexType type1, DexType type2);
 
-  public void visitDexField(DexField field1, DexField field2) {
-    visit(field1, field2, field1.getStructuralAccept());
+  public int visitDexField(DexField field1, DexField field2) {
+    return visit(field1, field2, field1.getStructuralMapping());
   }
 
-  public void visitDexMethod(DexMethod method1, DexMethod method2) {
-    visit(method1, method2, method1.getStructuralAccept());
+  public int visitDexMethod(DexMethod method1, DexMethod method2) {
+    return visit(method1, method2, method1.getStructuralMapping());
   }
 
-  public abstract void visitDexReference(DexReference reference1, DexReference reference2);
+  public abstract int visitDexReference(DexReference reference1, DexReference reference2);
 
-  public abstract <S> void visit(S item1, S item2, StructuralAccept<S> accept);
+  public abstract <S> int visit(S item1, S item2, StructuralMapping<S> accept);
 
   @Deprecated
-  public abstract <S> void visit(S item1, S item2, Comparator<S> comparator);
+  public abstract <S> int visit(S item1, S item2, Comparator<S> comparator);
 }
diff --git a/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorBase.java b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorBase.java
index 1f517d0..ee264cc 100644
--- a/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorBase.java
+++ b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorBase.java
@@ -18,102 +18,75 @@
 /** Base class to share most visiting methods */
 public abstract class CompareToVisitorBase extends CompareToVisitor {
 
-  private int order = 0;
-
-  public final boolean stillEqual() {
-    return order == 0;
+  @Override
+  public final int visitBool(boolean value1, boolean value2) {
+    return Boolean.compare(value1, value2);
   }
 
-  public final int getOrder() {
+  @Override
+  public final int visitInt(int value1, int value2) {
+    return Integer.compare(value1, value2);
+  }
+
+  @Override
+  public int visitLong(long value1, long value2) {
+    return Long.compare(value1, value2);
+  }
+
+  @Override
+  public int visitFloat(float value1, float value2) {
+    return Float.compare(value1, value2);
+  }
+
+  @Override
+  public int visitDouble(double value1, double value2) {
+    return Double.compare(value1, value2);
+  }
+
+  @Override
+  public <S> int visitItemIterator(
+      Iterator<S> it1, Iterator<S> it2, CompareToAccept<S> compareToAccept) {
+    int order = 0;
+    while (order == 0 && it1.hasNext() && it2.hasNext()) {
+      order = compareToAccept.acceptCompareTo(it1.next(), it2.next(), this);
+    }
+    if (order == 0) {
+      order = visitBool(it1.hasNext(), it2.hasNext());
+    }
     return order;
   }
 
-  public final void setOrder(int order) {
-    this.order = order;
+  @Override
+  public int visitDexString(DexString string1, DexString string2) {
+    return string1.compareTo(string2);
   }
 
   @Override
-  public final void visitBool(boolean value1, boolean value2) {
-    if (stillEqual()) {
-      setOrder(Boolean.compare(value1, value2));
-    }
-  }
-
-  @Override
-  public final void visitInt(int value1, int value2) {
-    if (stillEqual()) {
-      setOrder(Integer.compare(value1, value2));
-    }
-  }
-
-  @Override
-  public void visitLong(long value1, long value2) {
-    if (stillEqual()) {
-      setOrder(Long.compare(value1, value2));
-    }
-  }
-
-  @Override
-  public void visitFloat(float value1, float value2) {
-    if (stillEqual()) {
-      setOrder(Float.compare(value1, value2));
-    }
-  }
-
-  @Override
-  public void visitDouble(double value1, double value2) {
-    if (stillEqual()) {
-      setOrder(Double.compare(value1, value2));
-    }
-  }
-
-  @Override
-  public <S> void visitItemIterator(
-      Iterator<S> it1, Iterator<S> it2, CompareToAccept<S> compareToAccept) {
-    while (stillEqual() && it1.hasNext() && it2.hasNext()) {
-      compareToAccept.acceptCompareTo(it1.next(), it2.next(), this);
-    }
-    if (stillEqual()) {
-      visitBool(it1.hasNext(), it2.hasNext());
-    }
-  }
-
-  @Override
-  public void visitDexString(DexString string1, DexString string2) {
-    if (stillEqual()) {
-      setOrder(string1.compareTo(string2));
-    }
-  }
-
-  @Override
-  public void visitDexReference(DexReference reference1, DexReference reference2) {
-    if (stillEqual()) {
-      visitInt(reference1.referenceTypeOrder(), reference2.referenceTypeOrder());
-      if (stillEqual()) {
-        assert reference1.getClass() == reference2.getClass();
-        if (reference1.isDexType()) {
-          visitDexType(reference1.asDexType(), reference2.asDexType());
-        } else if (reference1.isDexField()) {
-          visitDexField(reference1.asDexField(), reference2.asDexField());
-        } else {
-          visitDexMethod(reference1.asDexMethod(), reference2.asDexMethod());
-        }
+  public int visitDexReference(DexReference reference1, DexReference reference2) {
+    int order = visitInt(reference1.referenceTypeOrder(), reference2.referenceTypeOrder());
+    if (order == 0) {
+      assert reference1.getClass() == reference2.getClass();
+      if (reference1.isDexType()) {
+        order = visitDexType(reference1.asDexType(), reference2.asDexType());
+      } else if (reference1.isDexField()) {
+        order = visitDexField(reference1.asDexField(), reference2.asDexField());
+      } else {
+        order = visitDexMethod(reference1.asDexMethod(), reference2.asDexMethod());
       }
     }
+    return order;
   }
 
   @Override
-  public final <S> void visit(S item1, S item2, Comparator<S> comparator) {
-    if (stillEqual()) {
-      setOrder(comparator.compare(item1, item2));
-    }
+  public final <S> int visit(S item1, S item2, Comparator<S> comparator) {
+    return comparator.compare(item1, item2);
   }
 
   @Override
-  public final <S> void visit(S item1, S item2, StructuralAccept<S> accept) {
-    if (stillEqual()) {
-      accept.apply(new ItemSpecification<>(item1, item2, this));
-    }
+  public final <S> int visit(S item1, S item2, StructuralMapping<S> accept) {
+    ItemSpecification<S> itemVisitor = new ItemSpecification<>(item1, item2, this);
+    accept.apply(itemVisitor);
+    return itemVisitor.order;
   }
 
   private static class ItemSpecification<T>
@@ -122,6 +95,7 @@
     private final CompareToVisitorBase parent;
     private final T item1;
     private final T item2;
+    private int order = 0;
 
     private ItemSpecification(T item1, T item2, CompareToVisitorBase parent) {
       this.item1 = item1;
@@ -138,47 +112,47 @@
 
     @Override
     public ItemSpecification<T> withBool(Predicate<T> getter) {
-      if (parent.stillEqual()) {
-        parent.visitBool(getter.test(item1), getter.test(item2));
+      if (order == 0) {
+        order = parent.visitBool(getter.test(item1), getter.test(item2));
       }
       return this;
     }
 
     @Override
     public ItemSpecification<T> withInt(ToIntFunction<T> getter) {
-      if (parent.stillEqual()) {
-        parent.visitInt(getter.applyAsInt(item1), getter.applyAsInt(item2));
+      if (order == 0) {
+        order = parent.visitInt(getter.applyAsInt(item1), getter.applyAsInt(item2));
       }
       return this;
     }
 
     @Override
     public ItemSpecification<T> withLong(ToLongFunction<T> getter) {
-      if (parent.stillEqual()) {
-        parent.visitLong(getter.applyAsLong(item1), getter.applyAsLong(item2));
+      if (order == 0) {
+        order = parent.visitLong(getter.applyAsLong(item1), getter.applyAsLong(item2));
       }
       return this;
     }
 
     @Override
     public ItemSpecification<T> withDouble(ToDoubleFunction<T> getter) {
-      if (parent.stillEqual()) {
-        parent.visitDouble(getter.applyAsDouble(item1), getter.applyAsDouble(item2));
+      if (order == 0) {
+        order = parent.visitDouble(getter.applyAsDouble(item1), getter.applyAsDouble(item2));
       }
       return this;
     }
 
     @Override
     public ItemSpecification<T> withIntArray(Function<T, int[]> getter) {
-      if (parent.stillEqual()) {
+      if (order == 0) {
         int[] is1 = getter.apply(item1);
         int[] is2 = getter.apply(item2);
         int minLength = Math.min(is1.length, is2.length);
-        for (int i = 0; i < minLength && parent.stillEqual(); i++) {
-          parent.visitInt(is1[i], is2[i]);
+        for (int i = 0; i < minLength && order == 0; i++) {
+          order = parent.visitInt(is1[i], is2[i]);
         }
-        if (parent.stillEqual()) {
-          parent.visitInt(is1.length, is2.length);
+        if (order == 0) {
+          order = parent.visitInt(is1.length, is2.length);
         }
       }
       return this;
@@ -190,13 +164,13 @@
         Function<T, S> getter,
         CompareToAccept<S> compare,
         HashingAccept<S> hasher) {
-      if (parent.stillEqual()) {
+      if (order == 0) {
         boolean test1 = predicate.test(item1);
         boolean test2 = predicate.test(item2);
         if (test1 && test2) {
-          compare.acceptCompareTo(getter.apply(item1), getter.apply(item2), parent);
+          order = compare.acceptCompareTo(getter.apply(item1), getter.apply(item2), parent);
         } else {
-          parent.visitBool(test1, test2);
+          order = parent.visitBool(test1, test2);
         }
       }
       return this;
@@ -205,8 +179,8 @@
     @Override
     protected <S> ItemSpecification<T> withItemIterator(
         Function<T, Iterator<S>> getter, CompareToAccept<S> compare, HashingAccept<S> hasher) {
-      if (parent.stillEqual()) {
-        parent.visitItemIterator(getter.apply(item1), getter.apply(item2), compare);
+      if (order == 0) {
+        order = parent.visitItemIterator(getter.apply(item1), getter.apply(item2), compare);
       }
       return this;
     }
diff --git a/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithNamingLens.java b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithNamingLens.java
index 5295766..f581065 100644
--- a/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithNamingLens.java
+++ b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithNamingLens.java
@@ -11,15 +11,14 @@
 
 public class CompareToVisitorWithNamingLens extends CompareToVisitorBase {
 
-  public static <T> int run(T item1, T item2, NamingLens namingLens, StructuralAccept<T> visit) {
+  public static <T> int run(T item1, T item2, NamingLens namingLens, StructuralMapping<T> visit) {
     return run(item1, item2, namingLens, (i1, i2, visitor) -> visitor.visit(i1, i2, visit));
   }
 
   public static <T> int run(
       T item1, T item2, NamingLens namingLens, CompareToAccept<T> compareToAccept) {
     CompareToVisitorWithNamingLens state = new CompareToVisitorWithNamingLens(namingLens);
-    compareToAccept.acceptCompareTo(item1, item2, state);
-    return state.getOrder();
+    return compareToAccept.acceptCompareTo(item1, item2, state);
   }
 
   private final NamingLens namingLens;
@@ -29,35 +28,35 @@
   }
 
   @Override
-  public void visitDexType(DexType type1, DexType type2) {
-    if (stillEqual()) {
-      namingLens.lookupDescriptor(type1).acceptCompareTo(namingLens.lookupDescriptor(type2), this);
-    }
+  public int visitDexType(DexType type1, DexType type2) {
+    return namingLens
+        .lookupDescriptor(type1)
+        .acceptCompareTo(namingLens.lookupDescriptor(type2), this);
   }
 
   @Override
-  public void visitDexField(DexField field1, DexField field2) {
-    if (stillEqual()) {
-      field1.holder.acceptCompareTo(field2.holder, this);
-      if (stillEqual()) {
-        namingLens.lookupName(field1).acceptCompareTo(namingLens.lookupName(field2), this);
-        if (stillEqual()) {
-          field1.type.acceptCompareTo(field2.type, this);
-        }
-      }
+  public int visitDexField(DexField field1, DexField field2) {
+    int order = field1.holder.acceptCompareTo(field2.holder, this);
+    if (order != 0) {
+      return order;
     }
+    order = namingLens.lookupName(field1).acceptCompareTo(namingLens.lookupName(field2), this);
+    if (order != 0) {
+      return order;
+    }
+    return field1.type.acceptCompareTo(field2.type, this);
   }
 
   @Override
-  public void visitDexMethod(DexMethod method1, DexMethod method2) {
-    if (stillEqual()) {
-      method1.holder.acceptCompareTo(method2.holder, this);
-      if (stillEqual()) {
-        namingLens.lookupName(method1).acceptCompareTo(namingLens.lookupName(method2), this);
-        if (stillEqual()) {
-          method1.proto.acceptCompareTo(method2.proto, this);
-        }
-      }
+  public int visitDexMethod(DexMethod method1, DexMethod method2) {
+    int order = method1.holder.acceptCompareTo(method2.holder, this);
+    if (order != 0) {
+      return order;
     }
+    order = namingLens.lookupName(method1).acceptCompareTo(namingLens.lookupName(method2), this);
+    if (order != 0) {
+      return order;
+    }
+    return method1.proto.acceptCompareTo(method2.proto, this);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithStringTable.java b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithStringTable.java
index dfd9fa0..78fb663 100644
--- a/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithStringTable.java
+++ b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithStringTable.java
@@ -4,20 +4,11 @@
 package com.android.tools.r8.utils.structural;
 
 import com.android.tools.r8.graph.DexString;
-import com.android.tools.r8.graph.NamingLensComparable;
 import com.android.tools.r8.naming.NamingLens;
 import java.util.function.ToIntFunction;
 
 public class CompareToVisitorWithStringTable extends CompareToVisitorWithNamingLens {
 
-  public static <T extends NamingLensComparable<T>> int run(
-      T item1, T item2, NamingLens namingLens, ToIntFunction<DexString> stringTable) {
-    CompareToVisitorWithNamingLens state =
-        new CompareToVisitorWithStringTable(namingLens, stringTable);
-    item1.acceptCompareTo(item2, state);
-    return state.getOrder();
-  }
-
   private final ToIntFunction<DexString> stringTable;
 
   public CompareToVisitorWithStringTable(
@@ -27,9 +18,7 @@
   }
 
   @Override
-  public void visitDexString(DexString string1, DexString string2) {
-    if (stillEqual()) {
-      visitInt(stringTable.applyAsInt(string1), stringTable.applyAsInt(string2));
-    }
+  public int visitDexString(DexString string1, DexString string2) {
+    return visitInt(stringTable.applyAsInt(string1), stringTable.applyAsInt(string2));
   }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithTypeEquivalence.java b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithTypeEquivalence.java
index 62b1942..89bd02b 100644
--- a/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithTypeEquivalence.java
+++ b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithTypeEquivalence.java
@@ -8,15 +8,14 @@
 
 public class CompareToVisitorWithTypeEquivalence extends CompareToVisitorBase {
 
-  public static <T> int run(T item1, T item2, RepresentativeMap map, StructuralAccept<T> visit) {
+  public static <T> int run(T item1, T item2, RepresentativeMap map, StructuralMapping<T> visit) {
     return run(item1, item2, map, (i1, i2, visitor) -> visitor.visit(i1, i2, visit));
   }
 
   public static <T> int run(
       T item1, T item2, RepresentativeMap map, CompareToAccept<T> compareToAccept) {
     CompareToVisitorWithTypeEquivalence state = new CompareToVisitorWithTypeEquivalence(map);
-    compareToAccept.acceptCompareTo(item1, item2, state);
-    return state.getOrder();
+    return compareToAccept.acceptCompareTo(item1, item2, state);
   }
 
   private final RepresentativeMap representatives;
@@ -26,11 +25,9 @@
   }
 
   @Override
-  public void visitDexType(DexType type1, DexType type2) {
-    if (stillEqual()) {
-      DexType repr1 = representatives.getRepresentative(type1);
-      DexType repr2 = representatives.getRepresentative(type2);
-      repr1.getDescriptor().acceptCompareTo(repr2.getDescriptor(), this);
-    }
+  public int visitDexType(DexType type1, DexType type2) {
+    DexType repr1 = representatives.getRepresentative(type1);
+    DexType repr2 = representatives.getRepresentative(type2);
+    return repr1.getDescriptor().acceptCompareTo(repr2.getDescriptor(), this);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithTypeTable.java b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithTypeTable.java
index 94fb1f9..52a718a 100644
--- a/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithTypeTable.java
+++ b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithTypeTable.java
@@ -5,24 +5,11 @@
 
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.NamingLensComparable;
 import com.android.tools.r8.naming.NamingLens;
 import java.util.function.ToIntFunction;
 
 public class CompareToVisitorWithTypeTable extends CompareToVisitorWithStringTable {
 
-  public static <T extends NamingLensComparable<T>> int run(
-      T item1,
-      T item2,
-      NamingLens namingLens,
-      ToIntFunction<DexString> stringTable,
-      ToIntFunction<DexType> typeTable) {
-    CompareToVisitorWithNamingLens state =
-        new CompareToVisitorWithTypeTable(namingLens, stringTable, typeTable);
-    item1.acceptCompareTo(item2, state);
-    return state.getOrder();
-  }
-
   private final ToIntFunction<DexType> typeTable;
 
   public CompareToVisitorWithTypeTable(
@@ -34,9 +21,7 @@
   }
 
   @Override
-  public void visitDexType(DexType type1, DexType type2) {
-    if (stillEqual()) {
-      visitInt(typeTable.applyAsInt(type1), typeTable.applyAsInt(type2));
-    }
+  public int visitDexType(DexType type1, DexType type2) {
+    return visitInt(typeTable.applyAsInt(type1), typeTable.applyAsInt(type2));
   }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/structural/DefaultCompareToVisitor.java b/src/main/java/com/android/tools/r8/utils/structural/DefaultCompareToVisitor.java
index 9f91d19..aa3fd96 100644
--- a/src/main/java/com/android/tools/r8/utils/structural/DefaultCompareToVisitor.java
+++ b/src/main/java/com/android/tools/r8/utils/structural/DefaultCompareToVisitor.java
@@ -13,7 +13,7 @@
  */
 public class DefaultCompareToVisitor {
 
-  public static <T> int run(T item1, T item2, StructuralAccept<T> visit) {
+  public static <T> int run(T item1, T item2, StructuralMapping<T> visit) {
     return run(item1, item2, (i1, i2, visitor) -> visitor.visit(i1, i2, visit));
   }
 
diff --git a/src/main/java/com/android/tools/r8/utils/structural/DefaultHashingVisitor.java b/src/main/java/com/android/tools/r8/utils/structural/DefaultHashingVisitor.java
index 2ec503c..d1b6653 100644
--- a/src/main/java/com/android/tools/r8/utils/structural/DefaultHashingVisitor.java
+++ b/src/main/java/com/android/tools/r8/utils/structural/DefaultHashingVisitor.java
@@ -14,7 +14,7 @@
  */
 public class DefaultHashingVisitor {
 
-  public static <T> void run(T item, Hasher hasher, StructuralAccept<T> accept) {
+  public static <T> void run(T item, Hasher hasher, StructuralMapping<T> accept) {
     run(item, hasher, (i, visitor) -> visitor.visit(i, accept));
   }
 
diff --git a/src/main/java/com/android/tools/r8/utils/structural/HashCodeVisitor.java b/src/main/java/com/android/tools/r8/utils/structural/HashCodeVisitor.java
index 1b5b400..c64e0b8 100644
--- a/src/main/java/com/android/tools/r8/utils/structural/HashCodeVisitor.java
+++ b/src/main/java/com/android/tools/r8/utils/structural/HashCodeVisitor.java
@@ -23,7 +23,7 @@
  */
 public class HashCodeVisitor<T> extends StructuralSpecification<T, HashCodeVisitor<T>> {
 
-  public static <T> int run(T item, StructuralAccept<T> visit) {
+  public static <T> int run(T item, StructuralMapping<T> visit) {
     HashCodeVisitor<T> visitor = new HashCodeVisitor<>(item);
     visit.apply(visitor);
     return visitor.hashCode;
diff --git a/src/main/java/com/android/tools/r8/utils/structural/HashingVisitor.java b/src/main/java/com/android/tools/r8/utils/structural/HashingVisitor.java
index ed0f61e..696c454 100644
--- a/src/main/java/com/android/tools/r8/utils/structural/HashingVisitor.java
+++ b/src/main/java/com/android/tools/r8/utils/structural/HashingVisitor.java
@@ -43,18 +43,18 @@
   public abstract void visitDexType(DexType type);
 
   public void visitDexField(DexField field) {
-    visit(field, field.getStructuralAccept());
+    visit(field, field.getStructuralMapping());
   }
 
   public void visitDexMethod(DexMethod method) {
-    visit(method, method.getStructuralAccept());
+    visit(method, method.getStructuralMapping());
   }
 
   public void visitDexReference(DexReference reference) {
     reference.accept(this::visitDexType, this::visitDexField, this::visitDexMethod);
   }
 
-  public abstract <S> void visit(S item, StructuralAccept<S> accept);
+  public abstract <S> void visit(S item, StructuralMapping<S> accept);
 
   @Deprecated
   public abstract <S> void visit(S item, BiConsumer<S, Hasher> hasher);
diff --git a/src/main/java/com/android/tools/r8/utils/structural/HashingVisitorWithTypeEquivalence.java b/src/main/java/com/android/tools/r8/utils/structural/HashingVisitorWithTypeEquivalence.java
index 63989f5..ba499a3 100644
--- a/src/main/java/com/android/tools/r8/utils/structural/HashingVisitorWithTypeEquivalence.java
+++ b/src/main/java/com/android/tools/r8/utils/structural/HashingVisitorWithTypeEquivalence.java
@@ -20,7 +20,7 @@
 public class HashingVisitorWithTypeEquivalence extends HashingVisitor {
 
   public static <T> void run(
-      T item, Hasher hasher, RepresentativeMap map, StructuralAccept<T> accept) {
+      T item, Hasher hasher, RepresentativeMap map, StructuralMapping<T> accept) {
     run(item, hasher, map, (i, visitor) -> visitor.visit(i, accept));
   }
 
@@ -73,7 +73,7 @@
   }
 
   @Override
-  public <S> void visit(S item, StructuralAccept<S> accept) {
+  public <S> void visit(S item, StructuralMapping<S> accept) {
     accept.apply(new ItemSpecification<>(item, this));
   }
 
diff --git a/src/main/java/com/android/tools/r8/utils/structural/StructuralItem.java b/src/main/java/com/android/tools/r8/utils/structural/StructuralItem.java
index 92542f2..8740299 100644
--- a/src/main/java/com/android/tools/r8/utils/structural/StructuralItem.java
+++ b/src/main/java/com/android/tools/r8/utils/structural/StructuralItem.java
@@ -11,13 +11,13 @@
 
   T self();
 
-  StructuralAccept<T> getStructuralAccept();
+  StructuralMapping<T> getStructuralMapping();
 
   // CompareTo implementation and callbacks.
 
   @FunctionalInterface
   interface CompareToAccept<T> {
-    void acceptCompareTo(T item1, T item2, CompareToVisitor visitor);
+    int acceptCompareTo(T item1, T item2, CompareToVisitor visitor);
   }
 
   /**
@@ -43,8 +43,8 @@
   }
 
   /** Default accept for compareTo visitors. Override to change behavior. */
-  default void acceptCompareTo(T other, CompareToVisitor visitor) {
-    visitor.visit(self(), other, self().getStructuralAccept());
+  default int acceptCompareTo(T other, CompareToVisitor visitor) {
+    return visitor.visit(self(), other, self().getStructuralMapping());
   }
 
   // Hashing implemenation and callbacks.
@@ -61,7 +61,7 @@
    * ensure that the effect is in place for any HashingVisitor.
    */
   default void hash(Hasher hasher) {
-    DefaultHashingVisitor.run(self(), hasher, self().getStructuralAccept());
+    DefaultHashingVisitor.run(self(), hasher, StructuralItem::acceptHashing);
   }
 
   /** Hashing method to use from tests to avoid having guava types shared between R8 and tests. */
@@ -78,11 +78,11 @@
    * ensure that the effect is in place for any HashingVisitor.
    */
   default void hashWithTypeEquivalence(Hasher hasher, RepresentativeMap map) {
-    HashingVisitorWithTypeEquivalence.run(self(), hasher, map, self().getStructuralAccept());
+    HashingVisitorWithTypeEquivalence.run(self(), hasher, map, StructuralItem::acceptHashing);
   }
 
   /** Default accept for hashing visitors. Override to change behavior. */
   default void acceptHashing(HashingVisitor visitor) {
-    visitor.visit(self(), self().getStructuralAccept());
+    visitor.visit(self(), self().getStructuralMapping());
   }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/structural/StructuralAccept.java b/src/main/java/com/android/tools/r8/utils/structural/StructuralMapping.java
similarity index 71%
rename from src/main/java/com/android/tools/r8/utils/structural/StructuralAccept.java
rename to src/main/java/com/android/tools/r8/utils/structural/StructuralMapping.java
index ede348f..b9e6bc2 100644
--- a/src/main/java/com/android/tools/r8/utils/structural/StructuralAccept.java
+++ b/src/main/java/com/android/tools/r8/utils/structural/StructuralMapping.java
@@ -4,9 +4,7 @@
 package com.android.tools.r8.utils.structural;
 
 /** Mapping of a specification over an item. */
-// TODO(b/171867022): Rename this to StructuralMapping to avoid confusion with the Acceptor and
-//  accept classes.
 @FunctionalInterface
-public interface StructuralAccept<T> {
+public interface StructuralMapping<T> {
   void apply(StructuralSpecification<T, ?> spec);
 }
diff --git a/src/main/java/com/android/tools/r8/utils/structural/StructuralSpecification.java b/src/main/java/com/android/tools/r8/utils/structural/StructuralSpecification.java
index ff74111..891fbd7 100644
--- a/src/main/java/com/android/tools/r8/utils/structural/StructuralSpecification.java
+++ b/src/main/java/com/android/tools/r8/utils/structural/StructuralSpecification.java
@@ -66,18 +66,23 @@
 
   public final <S extends StructuralItem<S>> V withConditionalItem(
       Predicate<T> predicate, Function<T, S> getter) {
-    return withConditionalCustomItem(predicate, getter, S::acceptCompareTo, S::acceptHashing);
+    return withConditionalCustomItem(
+        predicate, getter, StructuralItem::acceptCompareTo, StructuralItem::acceptHashing);
   }
 
   public final <S extends StructuralItem<S>> V withItemCollection(
       Function<T, Collection<S>> getter) {
     return withItemIterator(
-        getter.andThen(Collection::iterator), S::acceptCompareTo, S::acceptHashing);
+        getter.andThen(Collection::iterator),
+        StructuralItem::acceptCompareTo,
+        StructuralItem::acceptHashing);
   }
 
   public final <S extends StructuralItem<S>> V withItemArray(Function<T, S[]> getter) {
     return withItemIterator(
-        getter.andThen(a -> Arrays.asList(a).iterator()), S::acceptCompareTo, S::acceptHashing);
+        getter.andThen(a -> Arrays.asList(a).iterator()),
+        StructuralItem::acceptCompareTo,
+        StructuralItem::acceptHashing);
   }
 
   /**
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index a8b5af7..5669af0 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -17,6 +17,7 @@
 import com.android.tools.r8.ToolHelper.DexVm;
 import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.cf.CfVersion;
 import com.android.tools.r8.code.Instruction;
 import com.android.tools.r8.dex.ApplicationReader;
 import com.android.tools.r8.errors.Unreachable;
@@ -1724,4 +1725,35 @@
         .compile()
         .writeToZip();
   }
+
+  protected static CfVersion extractClassFileVersion(byte[] classFileBytes) {
+    class ClassFileVersionExtractor extends ClassVisitor {
+      private int version;
+
+      private ClassFileVersionExtractor() {
+        super(ASM_VERSION);
+      }
+
+      @Override
+      public void visit(
+          int version,
+          int access,
+          String name,
+          String signature,
+          String superName,
+          String[] interfaces) {
+        this.version = version;
+      }
+
+      CfVersion getClassFileVersion() {
+        return CfVersion.fromRaw(version);
+      }
+    }
+
+    ClassReader reader = new ClassReader(classFileBytes);
+    ClassFileVersionExtractor extractor = new ClassFileVersionExtractor();
+    reader.accept(
+        extractor, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
+    return extractor.getClassFileVersion();
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/cf/stackmap/StackMapVerificationNoFrameForHandlerTest.java b/src/test/java/com/android/tools/r8/cf/stackmap/StackMapVerificationNoFrameForHandlerTest.java
index 625a3dc..7e8c85c 100644
--- a/src/test/java/com/android/tools/r8/cf/stackmap/StackMapVerificationNoFrameForHandlerTest.java
+++ b/src/test/java/com/android/tools/r8/cf/stackmap/StackMapVerificationNoFrameForHandlerTest.java
@@ -76,6 +76,7 @@
                 : transformer(MainDump.dump(), Reference.classFromClass(Main.class))
                     .stripFrames("main")
                     .transform())
+        .addOptionsModification(options -> options.testing.readInputStackMaps = true)
         .setMinApi(parameters.getApiLevel())
         .compileWithExpectedDiagnostics(this::verifyWarningsRegardingStackMap)
         .run(parameters.getRuntime(), Main.class)
@@ -94,6 +95,7 @@
         .addKeepMainRule(Main.class)
         .setMinApi(parameters.getApiLevel())
         .allowDiagnosticWarningMessages(!includeFrameInHandler)
+        .addOptionsModification(options -> options.testing.readInputStackMaps = true)
         .compileWithExpectedDiagnostics(this::verifyWarningsRegardingStackMap)
         .run(parameters.getRuntime(), Main.class)
         .assertSuccessWithOutputLines(EXPECTED_OUTPUT);
diff --git a/src/test/java/com/android/tools/r8/cf/stackmap/SwitchStackFrameFallThroughTest.java b/src/test/java/com/android/tools/r8/cf/stackmap/SwitchStackFrameFallThroughTest.java
new file mode 100644
index 0000000..e552c0a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/stackmap/SwitchStackFrameFallThroughTest.java
@@ -0,0 +1,177 @@
+// Copyright (c) 2020, 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 org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import java.io.IOException;
+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.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+@RunWith(Parameterized.class)
+public class SwitchStackFrameFallThroughTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+  }
+
+  public SwitchStackFrameFallThroughTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testJvm() throws Exception {
+    assumeTrue(parameters.isCfRuntime());
+    testForJvm()
+        .addProgramClassFileData(SwitchStackFrameFallThroughTest$MainDump.dump())
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("java.io.IOException");
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    assumeTrue(parameters.isDexRuntime());
+    testForD8(parameters.getBackend())
+        .addProgramClassFileData(SwitchStackFrameFallThroughTest$MainDump.dump())
+        .setMinApi(parameters.getApiLevel())
+        .addOptionsModification(options -> options.testing.readInputStackMaps = true)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("java.io.IOException");
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      Throwable throwable = null;
+      switch (args.length) {
+        case 0:
+          throwable = new IOException();
+          break;
+        case 1:
+          throwable = new RuntimeException();
+          break;
+      }
+      System.out.println(throwable.toString());
+    }
+  }
+
+  // The above is roughly the code that below, except we move the switch targets above the switch
+  // and then jump over them. We do this to check the fall-through of the switch.
+  public static class SwitchStackFrameFallThroughTest$MainDump implements Opcodes {
+
+    public static byte[] dump() {
+
+      ClassWriter classWriter = new ClassWriter(0);
+      MethodVisitor methodVisitor;
+
+      classWriter.visit(
+          V1_8,
+          ACC_PUBLIC | ACC_SUPER,
+          "com/android/tools/r8/cf/stackmap/SwitchStackFrameFallThroughTest$Main",
+          null,
+          "java/lang/Object",
+          null);
+
+      classWriter.visitSource("SwitchStackFrameFallThroughTest.java", null);
+
+      classWriter.visitInnerClass(
+          "com/android/tools/r8/cf/stackmap/SwitchStackFrameFallThroughTest$Main",
+          "com/android/tools/r8/cf/stackmap/SwitchStackFrameFallThroughTest",
+          "Main",
+          ACC_PUBLIC | ACC_STATIC);
+
+      {
+        methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+        methodVisitor.visitCode();
+        methodVisitor.visitVarInsn(ALOAD, 0);
+        methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+        methodVisitor.visitInsn(RETURN);
+        methodVisitor.visitMaxs(1, 1);
+        methodVisitor.visitEnd();
+      }
+      {
+        methodVisitor =
+            classWriter.visitMethod(
+                ACC_PUBLIC | ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
+        methodVisitor.visitCode();
+        // This is moved here without a jump.
+
+        Label label0 = new Label();
+        Label label1 = new Label();
+        Label label2 = new Label();
+        Label label3 = new Label();
+
+        methodVisitor.visitJumpInsn(GOTO, label2);
+
+        methodVisitor.visitLabel(label0);
+        methodVisitor.visitFrame(
+            Opcodes.F_FULL,
+            1,
+            new Object[] {"[Ljava/lang/String;"},
+            1,
+            new Object[] {Opcodes.NULL});
+        methodVisitor.visitInsn(POP);
+        methodVisitor.visitTypeInsn(NEW, "java/io/IOException");
+        methodVisitor.visitInsn(DUP);
+        methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/io/IOException", "<init>", "()V", false);
+        methodVisitor.visitVarInsn(ASTORE, 1);
+        methodVisitor.visitJumpInsn(GOTO, label3);
+
+        methodVisitor.visitLabel(label1);
+        methodVisitor.visitFrame(
+            Opcodes.F_FULL,
+            1,
+            new Object[] {"[Ljava/lang/String;"},
+            1,
+            new Object[] {Opcodes.NULL});
+        methodVisitor.visitInsn(POP);
+        methodVisitor.visitTypeInsn(NEW, "java/lang/RuntimeException");
+        methodVisitor.visitInsn(DUP);
+        methodVisitor.visitMethodInsn(
+            INVOKESPECIAL, "java/lang/RuntimeException", "<init>", "()V", false);
+        methodVisitor.visitVarInsn(ASTORE, 1);
+        methodVisitor.visitJumpInsn(GOTO, label3);
+
+        methodVisitor.visitLabel(label2);
+        methodVisitor.visitFrame(Opcodes.F_FULL, 1, new Object[] {"[Ljava/lang/String;"}, 0, null);
+        methodVisitor.visitInsn(ACONST_NULL);
+        methodVisitor.visitVarInsn(ALOAD, 0);
+        methodVisitor.visitInsn(ARRAYLENGTH);
+        methodVisitor.visitLookupSwitchInsn(label1, new int[] {0, 1}, new Label[] {label0, label1});
+        methodVisitor.visitLabel(label3);
+        methodVisitor.visitFrame(
+            Opcodes.F_FULL,
+            2,
+            new Object[] {"[Ljava/lang/String;", "java/lang/Throwable"},
+            0,
+            null);
+        methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+        methodVisitor.visitVarInsn(ALOAD, 1);
+        methodVisitor.visitMethodInsn(
+            INVOKEVIRTUAL, "java/lang/Throwable", "toString", "()Ljava/lang/String;", false);
+        methodVisitor.visitMethodInsn(
+            INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
+        methodVisitor.visitInsn(RETURN);
+        methodVisitor.visitMaxs(4, 2);
+        methodVisitor.visitEnd();
+      }
+      classWriter.visitEnd();
+
+      return classWriter.toByteArray();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java b/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java
index 3ae4175..c43c289 100644
--- a/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java
+++ b/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java
@@ -94,6 +94,7 @@
 
   private void readMethodTemplatesInto(CfCodePrinter codePrinter) throws IOException {
     InternalOptions options = new InternalOptions();
+    options.testing.readInputStackMaps = true;
     JarClassFileReader reader =
         new JarClassFileReader(
             new JarApplicationReader(options),
diff --git a/src/test/java/com/android/tools/r8/desugar/DesugarToClassFileInputCfVersion.java b/src/test/java/com/android/tools/r8/desugar/DesugarToClassFileInputCfVersion.java
index 49437ff..6b50afe 100644
--- a/src/test/java/com/android/tools/r8/desugar/DesugarToClassFileInputCfVersion.java
+++ b/src/test/java/com/android/tools/r8/desugar/DesugarToClassFileInputCfVersion.java
@@ -50,18 +50,11 @@
             .writeToZip();
 
     if (parameters.getRuntime().isCf()) {
-      // Run on the JVM given that Cf version is supported.
-      if (cfVersion <= parameters.getRuntime().asCf().getVm().getClassfileVersion()) {
-        testForJvm()
-            .addProgramFiles(jar)
-            .run(parameters.getRuntime(), TestClass.class)
-            .assertSuccessWithOutputLines("Hello, world!");
-      } else {
-        testForJvm()
-            .addProgramFiles(jar)
-            .run(parameters.getRuntime(), TestClass.class)
-            .assertFailureWithErrorThatThrows(UnsupportedClassVersionError.class);
-      }
+      // Run on the JVM given that Cf version is supported. When we desugar we now target 1.7 (51).
+      testForJvm()
+          .addProgramFiles(jar)
+          .run(parameters.getRuntime(), TestClass.class)
+          .assertSuccessWithOutputLines("Hello, world!");
     } else {
       assert parameters.getRuntime().isDex();
       // Convert to DEX without desugaring.
diff --git a/src/test/java/com/android/tools/r8/desugaring/DesugarCfVersion.java b/src/test/java/com/android/tools/r8/desugaring/DesugarCfVersion.java
new file mode 100644
index 0000000..493fe3d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugaring/DesugarCfVersion.java
@@ -0,0 +1,256 @@
+// Copyright (c) 2020, 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.desugaring;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.cf.CfVersion;
+import com.android.tools.r8.utils.ZipUtils;
+import com.google.common.io.ByteStreams;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class DesugarCfVersion extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withCfRuntimes().withAllApiLevelsAlsoForCf().build();
+  }
+
+  public DesugarCfVersion(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void test() throws Exception {
+    Path zip1 =
+        testForD8(parameters.getBackend())
+            .addProgramClassFileData(hide_1_8)
+            .setMinApi(parameters.getApiLevel())
+            .compile()
+            .writeToZip();
+
+    Path zip2 =
+        testForD8(parameters.getBackend())
+            .addProgramClassFileData(hide_1_7)
+            .setMinApi(parameters.getApiLevel())
+            .compile()
+            .writeToZip();
+
+    boolean canUseStaticAndDefaultInterfaceMethods =
+        parameters
+            .getApiLevel()
+            .isGreaterThanOrEqualTo(TestBase.apiLevelWithDefaultInterfaceMethodsSupport());
+    byte[] bytes1 = ByteStreams.toByteArray(Files.newInputStream(zip1));
+    byte[] bytes2 = ByteStreams.toByteArray(Files.newInputStream(zip2));
+    if (!canUseStaticAndDefaultInterfaceMethods) {
+      assertArrayEquals(bytes1, bytes2);
+    }
+
+    assertEquals(CfVersion.V1_8, extractClassFileVersion(hide_1_8));
+    assertEquals(CfVersion.V1_7, extractClassFileVersion(hide_1_7));
+    assertEquals(
+        canUseStaticAndDefaultInterfaceMethods ? CfVersion.V1_8 : CfVersion.V1_7,
+        extractClassFileVersion(
+            ZipUtils.readSingleEntry(zip1, "com/google/android/gms/common/internal/Hide.class")));
+    assertEquals(
+        CfVersion.V1_7,
+        extractClassFileVersion(
+            ZipUtils.readSingleEntry(zip2, "com/google/android/gms/common/internal/Hide.class")));
+  }
+
+  /*
+   Class file bytes for:
+
+   public interface com.google.android.gms.common.internal.Hide extends java.lang.annotation.Annotation
+     minor version: 0
+     major version: 52
+     flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+     this_class: #1                          // com/google/android/gms/common/internal/Hide
+     super_class: #2                         // java/lang/Object
+     interfaces: 1, fields: 0, methods: 0, attributes: 2
+   Constant pool:
+      #1 = Class              #19            // com/google/android/gms/common/internal/Hide
+      #2 = Class              #20            // java/lang/Object
+      #3 = Class              #21            // java/lang/annotation/Annotation
+      #4 = Utf8               SourceFile
+      #5 = Utf8               Hide.java
+      #6 = Utf8               RuntimeVisibleAnnotations
+      #7 = Utf8               Ljava/lang/annotation/Target;
+      #8 = Utf8               value
+      #9 = Utf8               Ljava/lang/annotation/ElementType;
+     #10 = Utf8               TYPE
+     #11 = Utf8               FIELD
+     #12 = Utf8               METHOD
+     #13 = Utf8               CONSTRUCTOR
+     #14 = Utf8               PACKAGE
+     #15 = Utf8               Ljava/lang/annotation/Retention;
+     #16 = Utf8               Ljava/lang/annotation/RetentionPolicy;
+     #17 = Utf8               CLASS
+     #18 = Utf8               Ljava/lang/annotation/Documented;
+     #19 = Utf8               com/google/android/gms/common/internal/Hide
+     #20 = Utf8               java/lang/Object
+     #21 = Utf8               java/lang/annotation/Annotation
+   {
+   }
+   SourceFile: "Hide.java"
+   RuntimeVisibleAnnotations:
+     0: #7(#8=[e#9.#10,e#9.#11,e#9.#12,e#9.#13,e#9.#14])
+       java.lang.annotation.Target(
+         value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR,Ljava/lang/annotation/ElementType;.PACKAGE]
+       )
+     1: #15(#8=e#16.#17)
+       java.lang.annotation.Retention(
+         value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+       )
+     2: #18()
+       java.lang.annotation.Documented
+
+  */
+
+  private static byte[] hide_1_8 =
+      new byte[] {
+        -54, -2, -70, -66, 0, 0, 0, 52, 0, 22, 7, 0, 19, 7, 0, 20,
+        7, 0, 21, 1, 0, 10, 83, 111, 117, 114, 99, 101, 70, 105, 108, 101,
+        1, 0, 9, 72, 105, 100, 101, 46, 106, 97, 118, 97, 1, 0, 25, 82,
+        117, 110, 116, 105, 109, 101, 86, 105, 115, 105, 98, 108, 101, 65, 110, 110,
+        111, 116, 97, 116, 105, 111, 110, 115, 1, 0, 29, 76, 106, 97, 118, 97,
+        47, 108, 97, 110, 103, 47, 97, 110, 110, 111, 116, 97, 116, 105, 111, 110,
+        47, 84, 97, 114, 103, 101, 116, 59, 1, 0, 5, 118, 97, 108, 117, 101,
+        1, 0, 34, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 97, 110,
+        110, 111, 116, 97, 116, 105, 111, 110, 47, 69, 108, 101, 109, 101, 110, 116,
+        84, 121, 112, 101, 59, 1, 0, 4, 84, 89, 80, 69, 1, 0, 5, 70,
+        73, 69, 76, 68, 1, 0, 6, 77, 69, 84, 72, 79, 68, 1, 0, 11,
+        67, 79, 78, 83, 84, 82, 85, 67, 84, 79, 82, 1, 0, 7, 80, 65,
+        67, 75, 65, 71, 69, 1, 0, 32, 76, 106, 97, 118, 97, 47, 108, 97,
+        110, 103, 47, 97, 110, 110, 111, 116, 97, 116, 105, 111, 110, 47, 82, 101,
+        116, 101, 110, 116, 105, 111, 110, 59, 1, 0, 38, 76, 106, 97, 118, 97,
+        47, 108, 97, 110, 103, 47, 97, 110, 110, 111, 116, 97, 116, 105, 111, 110,
+        47, 82, 101, 116, 101, 110, 116, 105, 111, 110, 80, 111, 108, 105, 99, 121,
+        59, 1, 0, 5, 67, 76, 65, 83, 83, 1, 0, 33, 76, 106, 97, 118,
+        97, 47, 108, 97, 110, 103, 47, 97, 110, 110, 111, 116, 97, 116, 105, 111,
+        110, 47, 68, 111, 99, 117, 109, 101, 110, 116, 101, 100, 59, 1, 0, 43,
+        99, 111, 109, 47, 103, 111, 111, 103, 108, 101, 47, 97, 110, 100, 114, 111,
+        105, 100, 47, 103, 109, 115, 47, 99, 111, 109, 109, 111, 110, 47, 105, 110,
+        116, 101, 114, 110, 97, 108, 47, 72, 105, 100, 101, 1, 0, 16, 106, 97,
+        118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101, 99, 116, 1, 0,
+        31, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 97, 110, 110, 111, 116,
+        97, 116, 105, 111, 110, 47, 65, 110, 110, 111, 116, 97, 116, 105, 111, 110,
+        38, 1, 0, 1, 0, 2, 0, 1, 0, 3, 0, 0, 0, 0, 0, 2,
+        0, 4, 0, 0, 0, 2, 0, 5, 0, 6, 0, 0, 0, 51, 0, 3,
+        0, 7, 0, 1, 0, 8, 91, 0, 5, 101, 0, 9, 0, 10, 101, 0,
+        9, 0, 11, 101, 0, 9, 0, 12, 101, 0, 9, 0, 13, 101, 0, 9,
+        0, 14, 0, 15, 0, 1, 0, 8, 101, 0, 16, 0, 17, 0, 18, 0,
+        0
+      };
+
+  /*
+   Class file bytes for:
+
+   public interface com.google.android.gms.common.internal.Hide extends java.lang.annotation.Annotation
+     minor version: 0
+     major version: 51
+     flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+     this_class: #2                          // com/google/android/gms/common/internal/Hide
+     super_class: #4                         // java/lang/Object
+     interfaces: 1, fields: 0, methods: 0, attributes: 2
+   Constant pool:
+      #1 = Utf8               com/google/android/gms/common/internal/Hide
+      #2 = Class              #1             // com/google/android/gms/common/internal/Hide
+      #3 = Utf8               java/lang/Object
+      #4 = Class              #3             // java/lang/Object
+      #5 = Utf8               java/lang/annotation/Annotation
+      #6 = Class              #5             // java/lang/annotation/Annotation
+      #7 = Utf8               Hide.java
+      #8 = Utf8               Ljava/lang/annotation/Target;
+      #9 = Utf8               value
+     #10 = Utf8               Ljava/lang/annotation/ElementType;
+     #11 = Utf8               TYPE
+     #12 = Utf8               FIELD
+     #13 = Utf8               METHOD
+     #14 = Utf8               CONSTRUCTOR
+     #15 = Utf8               PACKAGE
+     #16 = Utf8               Ljava/lang/annotation/Retention;
+     #17 = Utf8               Ljava/lang/annotation/RetentionPolicy;
+     #18 = Utf8               CLASS
+     #19 = Utf8               Ljava/lang/annotation/Documented;
+     #20 = Utf8               SourceFile
+     #21 = Utf8               RuntimeVisibleAnnotations
+   {
+   }
+   SourceFile: "Hide.java"
+   RuntimeVisibleAnnotations:
+     0: #8(#9=[e#10.#11,e#10.#12,e#10.#13,e#10.#14,e#10.#15])
+       java.lang.annotation.Target(
+         value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR,Ljava/lang/annotation/ElementType;.PACKAGE]
+       )
+     1: #16(#9=e#17.#18)
+       java.lang.annotation.Retention(
+         value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+       )
+     2: #19()
+       java.lang.annotation.Documented
+
+  */
+  private static byte[] hide_1_7 =
+      new byte[] {
+        -54, -2, -70, -66, 0, 0, 0, 51, 0, 22, 1, 0, 43, 99, 111, 109,
+        47, 103, 111, 111, 103, 108, 101, 47, 97, 110, 100, 114, 111, 105, 100, 47,
+        103, 109, 115, 47, 99, 111, 109, 109, 111, 110, 47, 105, 110, 116, 101, 114,
+        110, 97, 108, 47, 72, 105, 100, 101, 7, 0, 1, 1, 0, 16, 106, 97,
+        118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101, 99, 116, 7, 0,
+        3, 1, 0, 31, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 97, 110,
+        110, 111, 116, 97, 116, 105, 111, 110, 47, 65, 110, 110, 111, 116, 97, 116,
+        105, 111, 110, 7, 0, 5, 1, 0, 9, 72, 105, 100, 101, 46, 106, 97,
+        118, 97, 1, 0, 29, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47,
+        97, 110, 110, 111, 116, 97, 116, 105, 111, 110, 47, 84, 97, 114, 103, 101,
+        116, 59, 1, 0, 5, 118, 97, 108, 117, 101, 1, 0, 34, 76, 106, 97,
+        118, 97, 47, 108, 97, 110, 103, 47, 97, 110, 110, 111, 116, 97, 116, 105,
+        111, 110, 47, 69, 108, 101, 109, 101, 110, 116, 84, 121, 112, 101, 59, 1,
+        0, 4, 84, 89, 80, 69, 1, 0, 5, 70, 73, 69, 76, 68, 1, 0,
+        6, 77, 69, 84, 72, 79, 68, 1, 0, 11, 67, 79, 78, 83, 84, 82,
+        85, 67, 84, 79, 82, 1, 0, 7, 80, 65, 67, 75, 65, 71, 69, 1,
+        0, 32, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 97, 110, 110,
+        111, 116, 97, 116, 105, 111, 110, 47, 82, 101, 116, 101, 110, 116, 105, 111,
+        110, 59, 1, 0, 38, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47,
+        97, 110, 110, 111, 116, 97, 116, 105, 111, 110, 47, 82, 101, 116, 101, 110,
+        116, 105, 111, 110, 80, 111, 108, 105, 99, 121, 59, 1, 0, 5, 67, 76,
+        65, 83, 83, 1, 0, 33, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103,
+        47, 97, 110, 110, 111, 116, 97, 116, 105, 111, 110, 47, 68, 111, 99, 117,
+        109, 101, 110, 116, 101, 100, 59, 1, 0, 10, 83, 111, 117, 114, 99, 101,
+        70, 105, 108, 101, 1, 0, 25, 82, 117, 110, 116, 105, 109, 101, 86, 105,
+        115, 105, 98, 108, 101, 65, 110, 110, 111, 116, 97, 116, 105, 111, 110, 115,
+        38, 1, 0, 2, 0, 4, 0, 1, 0, 6, 0, 0, 0, 0, 0, 2,
+        0, 20, 0, 0, 0, 2, 0, 7, 0, 21, 0, 0, 0, 51, 0, 3,
+        0, 8, 0, 1, 0, 9, 91, 0, 5, 101, 0, 10, 0, 11, 101, 0,
+        10, 0, 12, 101, 0, 10, 0, 13, 101, 0, 10, 0, 14, 101, 0, 10,
+        0, 15, 0, 16, 0, 1, 0, 9, 101, 0, 17, 0, 18, 0, 19, 0,
+        0
+      };
+
+  // Code for generating the lists above.
+  public static void main(String[] args) throws IOException {
+    Path file = Paths.get(args[0]);
+    byte[] content = ByteStreams.toByteArray(Files.newInputStream(file));
+    final int bytesPerLine = 16;
+    for (int i = 0; i < content.length; i += bytesPerLine) {
+      for (int j = 0; j < bytesPerLine && i + j < content.length; j++) {
+        System.out.print(content[i + j] + ", ");
+      }
+      System.out.println();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/PrivateOverridePublicizerDevirtualizerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/PrivateOverridePublicizerDevirtualizerTest.java
new file mode 100644
index 0000000..bef0cd5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/PrivateOverridePublicizerDevirtualizerTest.java
@@ -0,0 +1,116 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.devirtualize;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.R8TestRunResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+// This is a reproduction of b/173812804.
+@RunWith(Parameterized.class)
+public class PrivateOverridePublicizerDevirtualizerTest extends TestBase {
+
+  private final TestParameters parameters;
+  private final String[] EXPECTED = new String[] {"A::foo", "B::foo"};
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public PrivateOverridePublicizerDevirtualizerTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testRuntime() throws Exception {
+    testForRuntime(parameters)
+        .addInnerClasses(PrivateOverridePublicizerDevirtualizerTest.class)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(EXPECTED);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    R8TestRunResult runResult =
+        testForR8(parameters.getBackend())
+            .addInnerClasses(PrivateOverridePublicizerDevirtualizerTest.class)
+            .enableInliningAnnotations()
+            .enableNoVerticalClassMergingAnnotations()
+            .enableNeverClassInliningAnnotations()
+            .addKeepMainRule(Main.class)
+            .allowAccessModification()
+            .noMinification()
+            .setMinApi(parameters.getApiLevel())
+            .compile()
+            .inspect(
+                inspector -> {
+                  ClassSubject classA = inspector.clazz(A.class);
+                  assertThat(classA, isPresent());
+                  MethodSubject fooA = classA.uniqueMethodWithName("foo");
+                  // TODO(b/173812804): This should not be removed.
+                  assertThat(fooA, not(isPresent()));
+                })
+            .run(parameters.getRuntime(), Main.class);
+    if (parameters.isDexRuntime()) {
+      // TODO(b/173812804): This should not fail verification
+      runResult.assertFailureWithErrorThatThrows(VerifyError.class);
+    } else {
+      // TODO(b/173812804): This should have been A::foo, B::foo.
+      runResult.assertSuccessWithOutputLines("B::foo", "B::foo");
+    }
+  }
+
+  @NeverClassInline
+  @NoVerticalClassMerging
+  public static class A {
+    @NeverInline
+    private void foo() {
+      System.out.println("A::foo"); // <-- this is made public by the publicizer
+    }
+
+    public void callFoo() {
+      foo();
+    }
+  }
+
+  @NeverClassInline
+  public static class B extends A {
+    @NeverInline
+    private void foo() {
+      System.out.println("B::foo");
+    }
+
+    @Override
+    @NeverInline
+    public void callFoo() {
+      // We end up inlining A::callFoo, and then, because the call to A::foo is public, we end up
+      // target B::foo in the virtualizer.
+      super.callFoo();
+      foo();
+    }
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      new B().callFoo();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/fields/FieldAnalysisTest.java b/src/test/java/com/android/tools/r8/ir/optimize/fields/FieldAnalysisTest.java
new file mode 100644
index 0000000..4089f0e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/fields/FieldAnalysisTest.java
@@ -0,0 +1,118 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.fields;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.StringUtils;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class FieldAnalysisTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static List<Object[]> data() {
+    return buildParameters(getTestParameters().withAllRuntimesAndApiLevels().build());
+  }
+
+  public FieldAnalysisTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  private static String EXPECTED_RESULT = StringUtils.lines("42", "7", "21", "42", "49", "28");
+
+  @Test
+  public void testD8AndJava() throws Exception {
+    testForRuntime(parameters.getRuntime(), parameters.getApiLevel())
+        .addInnerClasses(FieldAnalysisTest.class)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutput(EXPECTED_RESULT);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .setMinApi(parameters.getApiLevel())
+        .addInnerClasses(FieldAnalysisTest.class)
+        .addKeepMainRule(Main.class)
+        .enableInliningAnnotations()
+        .enableNeverClassInliningAnnotations()
+        .compile()
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutput(EXPECTED_RESULT);
+  }
+
+  @NeverClassInline
+  static class A {
+    int f = 42;
+
+    @NeverInline
+    A() {}
+
+    @NeverInline
+    A(int f) {
+      this.f = f;
+    }
+  }
+
+  @NeverClassInline
+  static class B extends A {
+    @NeverInline
+    B() {
+      super();
+    }
+
+    @NeverInline
+    B(int f) {
+      this();
+      this.f = f;
+    }
+
+    @NeverInline
+    B(int unused, int f) {
+      super(unused);
+      this.f = f;
+    }
+  }
+
+  @NeverClassInline
+  static class C extends A {
+    @NeverInline
+    C() {
+      super();
+    }
+
+    @NeverInline
+    C(int f) {
+      this();
+      this.f = f + this.f;
+    }
+
+    @NeverInline
+    C(int unused, int f) {
+      super(unused);
+      this.f = f + this.f;
+    }
+  }
+
+  static class Main {
+    public static void main(String[] args) {
+      System.out.println(new B().f);
+      System.out.println(new B(7).f);
+      System.out.println(new B(7, 21).f);
+
+      System.out.println(new C().f);
+      System.out.println(new C(7).f);
+      System.out.println(new C(7, 21).f);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/switches/SwitchMapInvalidInitTest.java b/src/test/java/com/android/tools/r8/ir/optimize/switches/SwitchMapInvalidInitTest.java
new file mode 100644
index 0000000..c8d8086
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/switches/SwitchMapInvalidInitTest.java
@@ -0,0 +1,113 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.optimize.switches;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class SwitchMapInvalidInitTest extends TestBase {
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+  }
+
+  public SwitchMapInvalidInitTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testInvalidInitSwitchMap() throws Exception {
+    testForR8(parameters.getBackend())
+        .addKeepMainRule(Main.class)
+        .enableInliningAnnotations()
+        .addInnerClasses(SwitchMapInvalidInitTest.class)
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspect(this::assertNoSwitchMap)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("init", "init", "init", "a", "b", "a", "b");
+  }
+
+  private void assertNoSwitchMap(CodeInspector codeInspector) {
+    // Main and the 2 enum classes.
+    assertEquals(3, codeInspector.allClasses().size());
+    assertThat(codeInspector.clazz(Main.class), isPresent());
+    assertThat(codeInspector.clazz(MyEnum1.class), isPresent());
+    assertThat(codeInspector.clazz(MyEnum2.class), isPresent());
+  }
+
+  @NeverClassInline
+  enum MyEnum1 {
+    // The System call cannot be traced, therefore enum field can be read before being set.
+    A(System.currentTimeMillis()),
+    B(0),
+    C(1);
+    private final long time;
+
+    MyEnum1(long time) {
+      this.time = time;
+    }
+  }
+
+  @NeverClassInline
+  enum MyEnum2 {
+    A,
+    B,
+    C;
+
+    MyEnum2() {
+      // The System call cannot be traced, therefore any enum field can be read before being set.
+      System.out.println("init");
+    }
+  }
+
+  public static class Main {
+    public static void main(String[] args) {
+      switch2(MyEnum2.A);
+      switch2(MyEnum2.B);
+      switch2(MyEnum2.C);
+      switch1(MyEnum1.A);
+      switch1(MyEnum1.B);
+      switch1(MyEnum1.C);
+    }
+
+    @NeverInline
+    private static void switch1(MyEnum1 e) {
+      switch (e) {
+        case A:
+          System.out.println("a");
+          break;
+        case B:
+          System.out.println("b");
+          break;
+      }
+    }
+
+    @NeverInline
+    private static void switch2(MyEnum2 e) {
+      switch (e) {
+        case A:
+          System.out.println("a");
+          break;
+        case B:
+          System.out.println("b");
+          break;
+      }
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/switches/SwitchMapWithEnumDependencyTest.java b/src/test/java/com/android/tools/r8/ir/optimize/switches/SwitchMapWithEnumDependencyTest.java
new file mode 100644
index 0000000..14d5f18
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/switches/SwitchMapWithEnumDependencyTest.java
@@ -0,0 +1,89 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.optimize.switches;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.R8TestCompileResult;
+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;
+
+@RunWith(Parameterized.class)
+public class SwitchMapWithEnumDependencyTest extends TestBase {
+
+  private final TestParameters parameters;
+  private static final String[] EXPECTED_RESULT = {"AD", "SB", "BE", "SC", "CF"};
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public SwitchMapWithEnumDependencyTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void test() throws Exception {
+    R8TestCompileResult compile =
+        testForR8(parameters.getBackend())
+            .addInnerClasses(SwitchMapWithEnumDependencyTest.class)
+            .addKeepMainRule(TestClass.class)
+            .noMinification()
+            .enableInliningAnnotations()
+            .setMinApi(parameters.getApiLevel())
+            .compile();
+    compile
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutputLines(EXPECTED_RESULT);
+  }
+
+  static class TestClass {
+    public static void main(String[] args) {
+      switchMethod();
+    }
+
+    @NeverInline
+    private static void switchMethod() {
+      for (MyEnum value : MyEnum.values()) {
+        switch (value) {
+          case B:
+            System.out.println("SB");
+            break;
+          case C:
+            System.out.println("SC");
+            break;
+        }
+        System.out.println(value.toString());
+      }
+    }
+  }
+
+  enum MyEnum {
+    A(MyEnumDependency.D),
+    B(MyEnumDependency.E),
+    C(MyEnumDependency.F);
+
+    final MyEnumDependency dependency;
+
+    MyEnum(MyEnumDependency dependency) {
+      this.dependency = dependency;
+    }
+
+    @Override
+    public String toString() {
+      return super.toString() + dependency.toString();
+    }
+  }
+
+  enum MyEnumDependency {
+    D,
+    E,
+    F
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/switches/SwitchMapWithSubtypesTest.java b/src/test/java/com/android/tools/r8/ir/optimize/switches/SwitchMapWithSubtypesTest.java
new file mode 100644
index 0000000..7bc82e8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/switches/SwitchMapWithSubtypesTest.java
@@ -0,0 +1,124 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.optimize.switches;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.R8TestCompileResult;
+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;
+
+@RunWith(Parameterized.class)
+public class SwitchMapWithSubtypesTest extends TestBase {
+
+  private final TestParameters parameters;
+  private static final String[] EXPECTED_RESULT = {
+    "ta", "SB", "tb", "SC", "C", "D", "tatrue", "SB", "tbtrue", "SC", "Cfalse", "Dfalse"
+  };
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public SwitchMapWithSubtypesTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void test() throws Exception {
+    R8TestCompileResult compile =
+        testForR8(parameters.getBackend())
+            .addInnerClasses(SwitchMapWithSubtypesTest.class)
+            .addKeepMainRule(TestClass.class)
+            .noMinification()
+            .enableInliningAnnotations()
+            .setMinApi(parameters.getApiLevel())
+            .compile();
+    compile
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutputLines(EXPECTED_RESULT);
+  }
+
+  static class TestClass {
+    public static void main(String[] args) {
+      switchMethod();
+      switchMethodSubtype();
+    }
+
+    @NeverInline
+    private static void switchMethod() {
+      for (MyEnum value : MyEnum.values()) {
+        switch (value) {
+          case B:
+            System.out.println("SB");
+            break;
+          case C:
+            System.out.println("SC");
+            break;
+        }
+        System.out.println(value.toString());
+      }
+    }
+
+    @NeverInline
+    private static void switchMethodSubtype() {
+      for (MyEnumSubtype value : MyEnumSubtype.values()) {
+        switch (value) {
+          case B:
+            System.out.println("SB");
+            break;
+          case C:
+            System.out.println("SC");
+            break;
+        }
+        System.out.println(value.toString() + value.subType);
+      }
+    }
+  }
+
+  enum MyEnum {
+    A {
+      @Override
+      public String toString() {
+        return "ta";
+      }
+    },
+    B {
+      @Override
+      public String toString() {
+        return "tb";
+      }
+    },
+    C,
+    D;
+  }
+
+  enum MyEnumSubtype {
+    A(true) {
+      @Override
+      public String toString() {
+        return "ta";
+      }
+    },
+    B(true) {
+      @Override
+      public String toString() {
+        return "tb";
+      }
+    },
+    C(false),
+    D(false);
+
+    final boolean subType;
+
+    MyEnumSubtype(boolean fieldSet) {
+      this.subType = fieldSet;
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateLibraryMethodTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateLibraryMethodTest.java
index 9eefd15..58155cc 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateLibraryMethodTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateLibraryMethodTest.java
@@ -4,10 +4,11 @@
 
 package com.android.tools.r8.repackage;
 
+import static org.hamcrest.MatcherAssert.assertThat;
+
 import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.R8TestRunResult;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -34,23 +35,23 @@
 
   @Test
   public void testR8() throws Exception {
-    R8TestRunResult runResult =
-        testForR8(parameters.getBackend())
-            .addLibraryClasses(Library.class)
-            .addDefaultRuntimeLibrary(parameters)
-            .addProgramClasses(Program.class, Main.class)
-            .apply(this::configureRepackaging)
-            .addKeepMainRule(Main.class)
-            .enableInliningAnnotations()
-            .setMinApi(parameters.getApiLevel())
-            .compile()
-            .addRunClasspathFiles(buildOnDexRuntime(parameters, Library.class))
-            .run(parameters.getRuntime(), Main.class);
-    if (parameters.isDexRuntime() && parameters.getDexRuntimeVersion() == Version.V8_1_0) {
-      runResult.assertSuccessWithOutputLines(EXPECTED);
-    } else {
-      runResult.assertFailureWithErrorThatThrows(IllegalAccessError.class);
-    }
+    testForR8(parameters.getBackend())
+        .addLibraryClasses(Library.class)
+        .addDefaultRuntimeLibrary(parameters)
+        .addProgramClasses(Program.class, Main.class)
+        .apply(this::configureRepackaging)
+        .addKeepMainRule(Main.class)
+        .enableInliningAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .addRunClasspathFiles(buildOnDexRuntime(parameters, Library.class))
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(EXPECTED)
+        .inspect(this::inspect);
+  }
+
+  private void inspect(CodeInspector inspector) {
+    assertThat(Program.class, isNotRepackaged(inspector));
   }
 
   public static class Library {
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateLibraryOverrideTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateLibraryOverrideTest.java
index 7773fef..ce1c039 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateLibraryOverrideTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateLibraryOverrideTest.java
@@ -4,9 +4,11 @@
 
 package com.android.tools.r8.repackage;
 
+import static org.hamcrest.MatcherAssert.assertThat;
+
 import com.android.tools.r8.NeverClassInline;
-import com.android.tools.r8.R8TestRunResult;
 import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -33,23 +35,23 @@
 
   @Test
   public void testR8() throws Exception {
-    R8TestRunResult runResult =
-        testForR8(parameters.getBackend())
-            .addLibraryClasses(Library.class)
-            .addDefaultRuntimeLibrary(parameters)
-            .addProgramClasses(Program.class, Main.class)
-            .apply(this::configureRepackaging)
-            .addKeepMainRule(Main.class)
-            .setMinApi(parameters.getApiLevel())
-            .enableNeverClassInliningAnnotations()
-            .compile()
-            .addRunClasspathFiles(buildOnDexRuntime(parameters, Library.class))
-            .run(parameters.getRuntime(), Main.class);
-    if (parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isDalvik()) {
-      runResult.assertSuccessWithOutputLines(EXPECTED);
-    } else {
-      runResult.assertSuccessWithOutputLines("Library::foo");
-    }
+    testForR8(parameters.getBackend())
+        .addLibraryClasses(Library.class)
+        .addDefaultRuntimeLibrary(parameters)
+        .addProgramClasses(Program.class, Main.class)
+        .apply(this::configureRepackaging)
+        .addKeepMainRule(Main.class)
+        .setMinApi(parameters.getApiLevel())
+        .enableNeverClassInliningAnnotations()
+        .compile()
+        .addRunClasspathFiles(buildOnDexRuntime(parameters, Library.class))
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(EXPECTED)
+        .inspect(this::inspect);
+  }
+
+  private void inspect(CodeInspector inspector) {
+    assertThat(Program.class, isNotRepackaged(inspector));
   }
 
   public static class Library {
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateLibraryTypeTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateLibraryTypeTest.java
index 1a35ded..90939fb 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateLibraryTypeTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateLibraryTypeTest.java
@@ -4,9 +4,12 @@
 
 package com.android.tools.r8.repackage;
 
+import static org.hamcrest.MatcherAssert.assertThat;
+
 import com.android.tools.r8.NeverClassInline;
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -49,7 +52,13 @@
         .setMinApi(parameters.getApiLevel())
         .addRunClasspathFiles(buildOnDexRuntime(parameters, Library.class, LibraryI.class))
         .run(parameters.getRuntime(), Main.class)
-        .assertFailureWithErrorThatThrows(IllegalAccessError.class);
+        .assertSuccessWithOutputLines(EXPECTED)
+        .inspect(this::inspect);
+  }
+
+  private void inspect(CodeInspector inspector) {
+    assertThat(Program.class, isNotRepackaged(inspector));
+    assertThat(ProgramSub.class, isNotRepackaged(inspector));
   }
 
   static class Library {}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java
index 0d99817..37dbe19 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java
@@ -47,7 +47,8 @@
   }
 
   public HorizontallyMergedClassesInspector assertClassNotMerged(Class<?> clazz) {
-    assertFalse(horizontallyMergedClasses.hasBeenMerged(toDexType(clazz, dexItemFactory)));
+    assertFalse(
+        horizontallyMergedClasses.hasBeenMergedIntoDifferentType(toDexType(clazz, dexItemFactory)));
     return this;
   }
 
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedLambdaClassesInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedLambdaClassesInspector.java
index 421b54a..619b415 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedLambdaClassesInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedLambdaClassesInspector.java
@@ -27,7 +27,9 @@
   }
 
   public HorizontallyMergedLambdaClassesInspector assertMerged(Class<?> clazz) {
-    assertTrue(horizontallyMergedLambdaClasses.hasBeenMerged(toDexType(clazz, dexItemFactory)));
+    assertTrue(
+        horizontallyMergedLambdaClasses.hasBeenMergedIntoDifferentType(
+            toDexType(clazz, dexItemFactory)));
     return this;
   }
 
@@ -39,7 +41,9 @@
   }
 
   public HorizontallyMergedLambdaClassesInspector assertClassNotMerged(Class<?> clazz) {
-    assertFalse(horizontallyMergedLambdaClasses.hasBeenMerged(toDexType(clazz, dexItemFactory)));
+    assertFalse(
+        horizontallyMergedLambdaClasses.hasBeenMergedIntoDifferentType(
+            toDexType(clazz, dexItemFactory)));
     return this;
   }
 
diff --git a/src/test/java/com/android/tools/r8/utils/structural/StructuralItemsCustomOrderTest.java b/src/test/java/com/android/tools/r8/utils/structural/StructuralItemsCustomOrderTest.java
index cfea69c..679200c 100644
--- a/src/test/java/com/android/tools/r8/utils/structural/StructuralItemsCustomOrderTest.java
+++ b/src/test/java/com/android/tools/r8/utils/structural/StructuralItemsCustomOrderTest.java
@@ -98,7 +98,7 @@
     private final int x;
     private final B b;
 
-    private static void accept(StructuralSpecification<A, ?> spec) {
+    private static void specify(StructuralSpecification<A, ?> spec) {
       spec.withInt(a -> a.x).withItem(a -> a.b);
     }
 
@@ -108,8 +108,8 @@
     }
 
     @Override
-    public StructuralAccept<A> getStructuralAccept() {
-      return A::accept;
+    public StructuralMapping<A> getStructuralMapping() {
+      return A::specify;
     }
 
     @Override
@@ -124,7 +124,7 @@
 
     @Override
     public final int hashCode() {
-      return HashCodeVisitor.run(this, A::accept);
+      return HashCodeVisitor.run(this, A::specify);
     }
   }
 
@@ -132,7 +132,7 @@
 
     private final int y;
 
-    private static void accept(StructuralSpecification<B, ?> spec) {
+    private static void specify(StructuralSpecification<B, ?> spec) {
       spec.withInt(b -> b.y);
     }
 
@@ -141,8 +141,8 @@
     }
 
     @Override
-    public StructuralAccept<B> getStructuralAccept() {
-      return B::accept;
+    public StructuralMapping<B> getStructuralMapping() {
+      return B::specify;
     }
 
     @Override
@@ -157,14 +157,14 @@
 
     @Override
     public final int hashCode() {
-      return HashCodeVisitor.run(this, B::accept);
+      return HashCodeVisitor.run(this, B::specify);
     }
 
     // Override allowing a change to the order of any type of compare-to visitation, e.g., with
     // and without a type equivalence map.
     @Override
-    public void acceptCompareTo(B other, CompareToVisitor visitor) {
-      visitor.visit(other, this, B::accept);
+    public int acceptCompareTo(B other, CompareToVisitor visitor) {
+      return visitor.visit(other, this, B::specify);
     }
   }
 }
diff --git a/src/test/java/com/android/tools/r8/utils/structural/StructuralItemsTest.java b/src/test/java/com/android/tools/r8/utils/structural/StructuralItemsTest.java
index 64481c3..b9580ed 100644
--- a/src/test/java/com/android/tools/r8/utils/structural/StructuralItemsTest.java
+++ b/src/test/java/com/android/tools/r8/utils/structural/StructuralItemsTest.java
@@ -117,7 +117,7 @@
     private final int x;
     private final B b;
 
-    private static void accept(StructuralSpecification<A, ?> spec) {
+    private static void specify(StructuralSpecification<A, ?> spec) {
       spec.withInt(a -> a.x).withItem(a -> a.b);
     }
 
@@ -127,8 +127,8 @@
     }
 
     @Override
-    public StructuralAccept<A> getStructuralAccept() {
-      return A::accept;
+    public StructuralMapping<A> getStructuralMapping() {
+      return A::specify;
     }
 
     @Override
@@ -143,7 +143,7 @@
 
     @Override
     public int hashCode() {
-      return HashCodeVisitor.run(this, A::accept);
+      return HashCodeVisitor.run(this, A::specify);
     }
   }
 
@@ -151,7 +151,7 @@
 
     private final int y;
 
-    private static void accept(StructuralSpecification<B, ?> spec) {
+    private static void specify(StructuralSpecification<B, ?> spec) {
       spec.withInt(b -> b.y);
     }
 
@@ -160,8 +160,8 @@
     }
 
     @Override
-    public StructuralAccept<B> getStructuralAccept() {
-      return B::accept;
+    public StructuralMapping<B> getStructuralMapping() {
+      return B::specify;
     }
 
     @Override
@@ -176,7 +176,7 @@
 
     @Override
     public int hashCode() {
-      return HashCodeVisitor.run(this, B::accept);
+      return HashCodeVisitor.run(this, B::specify);
     }
   }
 }
diff --git a/tools/compiledump.py b/tools/compiledump.py
index 5c818d6..ebf3371 100755
--- a/tools/compiledump.py
+++ b/tools/compiledump.py
@@ -47,6 +47,11 @@
     help='Path to an R8 jar.',
     default=None)
   parser.add_argument(
+    '-override',
+    help='Do not override any extracted dump in temp-dir',
+    default=False,
+    action='store_true')
+  parser.add_argument(
     '--nolib',
     help='Use the non-lib distribution (default uses the lib distribution)',
     default=False,
@@ -168,7 +173,13 @@
     return Dump(args.dump)
   dump_file = zipfile.ZipFile(os.path.abspath(args.dump), 'r')
   with utils.ChangedWorkingDirectory(temp):
-    dump_file.extractall()
+    if args.override or not os.path.isfile(
+        os.path.join(temp, 'proguard.config')):
+      print("Extracting into: %s" % temp)
+      dump_file.extractall()
+      if not os.path.isfile(os.path.join(temp, 'proguard.config')):
+        error("Did not extract into %s. Either the zip file is invalid or the "
+              "dump is missing files" % temp)
     return Dump(temp)
 
 def determine_build_properties(args, dump):
diff --git a/tools/run_on_app_dump.py b/tools/run_on_app_dump.py
index 0ceb3b0b..0d48887 100755
--- a/tools/run_on_app_dump.py
+++ b/tools/run_on_app_dump.py
@@ -319,8 +319,6 @@
     'url': 'https://github.com/chrisbanes/tivi',
     'revision': '8e2ddd8fe2d343264a66aa1ef8acbd4cc587e8ce',
     'folder': 'tivi',
-    # TODO(b/173974110): Enable recompilation.
-    'skip_recompilation': True,
   }),
   App({
     'id': 'com.keylesspalace.tusky',