Add a test for lambda merging in presence of enum unboxing

Change-Id: Id79511f8a927ec23f04d217dde56e714b5ac8658
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 59c7b8e..0d6b606 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -445,6 +445,9 @@
       HorizontallyMergedLambdaClasses horizontallyMergedLambdaClasses) {
     assert this.horizontallyMergedLambdaClasses == null;
     this.horizontallyMergedLambdaClasses = horizontallyMergedLambdaClasses;
+    testing()
+        .horizontallyMergedLambdaClassesConsumer
+        .accept(dexItemFactory(), horizontallyMergedLambdaClasses);
   }
 
   /**
@@ -471,9 +474,7 @@
   public void setVerticallyMergedClasses(VerticallyMergedClasses verticallyMergedClasses) {
     assert this.verticallyMergedClasses == null;
     this.verticallyMergedClasses = verticallyMergedClasses;
-    if (testing().verticallyMergedClassesConsumer != null) {
-      testing().verticallyMergedClassesConsumer.accept(dexItemFactory(), verticallyMergedClasses);
-    }
+    testing().verticallyMergedClassesConsumer.accept(dexItemFactory(), verticallyMergedClasses);
   }
 
   public EnumValueInfoMapCollection unboxedEnums() {
@@ -481,7 +482,9 @@
   }
 
   public void setUnboxedEnums(EnumValueInfoMapCollection unboxedEnums) {
+    assert this.unboxedEnums.isEmpty();
     this.unboxedEnums = unboxedEnums;
+    testing().unboxedEnumsConsumer.accept(dexItemFactory(), unboxedEnums);
   }
 
   public boolean validateUnboxedEnumsHaveBeenPruned() {
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 505dfd5..285e397 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
@@ -7,6 +7,7 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.google.common.collect.ImmutableSet;
 import java.util.Set;
 
 public class HorizontallyMergedLambdaClasses implements MergedClasses {
@@ -17,6 +18,10 @@
     this.sources = sources;
   }
 
+  public static HorizontallyMergedLambdaClasses empty() {
+    return new HorizontallyMergedLambdaClasses(ImmutableSet.of());
+  }
+
   @Override
   public boolean verifyAllSourcesPruned(AppView<AppInfoWithLiveness> appView) {
     for (DexType source : sources) {
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 f475e89..b7964c8 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
@@ -22,8 +22,10 @@
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.EnumValueInfoMapCollection;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.classmerging.HorizontallyMergedLambdaClasses;
 import com.android.tools.r8.ir.analysis.TypeChecker;
 import com.android.tools.r8.ir.analysis.VerifyTypesHelper;
 import com.android.tools.r8.ir.analysis.constant.SparseConditionalConstantPropagation;
@@ -705,6 +707,8 @@
     }
     if (enumUnboxer != null) {
       enumUnboxer.unboxEnums(postMethodProcessorBuilder, executorService, feedback);
+    } else {
+      appView.setUnboxedEnums(EnumValueInfoMapCollection.empty());
     }
     if (!options.debug) {
       new TrivialFieldAccessReprocessor(appView.withLiveness(), postMethodProcessorBuilder)
@@ -935,7 +939,10 @@
     if (lambdaMerger != null) {
       lambdaMerger.applyLambdaClassMapping(
           application, this, feedback, builder, executorService);
+    } else {
+      appView.setHorizontallyMergedLambdaClasses(HorizontallyMergedLambdaClasses.empty());
     }
+    assert appView.horizontallyMergedLambdaClasses() != null;
   }
 
   private void generateDesugaredLibraryAPIWrappers(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
index 3bb366d..ee35087 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
@@ -252,15 +252,16 @@
         .filter(cls -> !appView.appInfo().isPinned(cls.type))
         .filter(
             cls ->
-                cls.getKotlinInfo().isSyntheticClass()
-                    && cls.getKotlinInfo().asSyntheticClass().isLambda()
+                appView.testing().kotlinLambdaMergerFactoryForClass.apply(cls) != null
                     && KotlinLambdaGroupIdFactory.hasValidAnnotations(kotlin, cls)
                     && !appView.appInfo().getClassToFeatureSplitMap().isInFeature(cls))
         .sorted((a, b) -> a.type.slowCompareTo(b.type)) // Ensure stable ordering.
         .forEachOrdered(
             lambda -> {
               try {
-                LambdaGroupId id = KotlinLambdaGroupIdFactory.create(appView, kotlin, lambda);
+                KotlinLambdaGroupIdFactory lambdaGroupIdFactory =
+                    appView.testing().kotlinLambdaMergerFactoryForClass.apply(lambda);
+                LambdaGroupId id = lambdaGroupIdFactory.validateAndCreate(appView, kotlin, lambda);
                 LambdaGroup group = groups.computeIfAbsent(id, LambdaGroupId::createGroup);
                 group.add(lambda);
                 lambdas.put(lambda.type, group);
@@ -341,6 +342,7 @@
       ExecutorService executorService)
       throws ExecutionException {
     if (lambdas.isEmpty()) {
+      appView.setHorizontallyMergedLambdaClasses(HorizontallyMergedLambdaClasses.empty());
       return;
     }
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroupIdFactory.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroupIdFactory.java
index 2e4e1a9..df72677 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroupIdFactory.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroupIdFactory.java
@@ -15,19 +15,22 @@
 import com.android.tools.r8.kotlin.Kotlin;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 
-final class JStyleLambdaGroupIdFactory extends KotlinLambdaGroupIdFactory {
-  static final JStyleLambdaGroupIdFactory INSTANCE = new JStyleLambdaGroupIdFactory();
+public final class JStyleLambdaGroupIdFactory extends KotlinLambdaGroupIdFactory {
+  private static final JStyleLambdaGroupIdFactory INSTANCE = new JStyleLambdaGroupIdFactory();
+
+  private JStyleLambdaGroupIdFactory() {}
+
+  public static JStyleLambdaGroupIdFactory getInstance() {
+    return INSTANCE;
+  }
 
   @Override
-  LambdaGroupId validateAndCreate(
+  public LambdaGroupId validateAndCreate(
       AppView<AppInfoWithLiveness> appView, Kotlin kotlin, DexClass lambda)
       throws LambdaStructureError {
     boolean accessRelaxed =
         appView.options().getProguardConfiguration().isAccessModificationAllowed();
 
-    assert lambda.getKotlinInfo().isSyntheticClass();
-    assert lambda.getKotlinInfo().asSyntheticClass().isJavaStyleLambda();
-
     // Ignore ACC_SUPER.
     ClassAccessFlags copy = lambda.accessFlags.copy();
     copy.unsetSuper();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroupIdFactory.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroupIdFactory.java
index 6f7650b..cc212bf 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroupIdFactory.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroupIdFactory.java
@@ -15,19 +15,22 @@
 import com.android.tools.r8.kotlin.Kotlin;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 
-final class KStyleLambdaGroupIdFactory extends KotlinLambdaGroupIdFactory {
-  static final KotlinLambdaGroupIdFactory INSTANCE = new KStyleLambdaGroupIdFactory();
+public final class KStyleLambdaGroupIdFactory extends KotlinLambdaGroupIdFactory {
+  private static final KStyleLambdaGroupIdFactory INSTANCE = new KStyleLambdaGroupIdFactory();
+
+  private KStyleLambdaGroupIdFactory() {}
+
+  public static KStyleLambdaGroupIdFactory getInstance() {
+    return INSTANCE;
+  }
 
   @Override
-  LambdaGroupId validateAndCreate(
+  public LambdaGroupId validateAndCreate(
       AppView<AppInfoWithLiveness> appView, Kotlin kotlin, DexClass lambda)
       throws LambdaStructureError {
     boolean accessRelaxed =
         appView.options().getProguardConfiguration().isAccessModificationAllowed();
 
-    assert lambda.getKotlinInfo().isSyntheticClass();
-    assert lambda.getKotlinInfo().asSyntheticClass().isKotlinStyleLambda();
-
     // Ignore ACC_SUPER.
     ClassAccessFlags copy = lambda.accessFlags.copy();
     copy.unsetSuper();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java
index 5a90f80..472d861 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.InnerClassAttribute;
 import com.android.tools.r8.ir.optimize.lambda.CaptureSignature;
@@ -23,27 +24,26 @@
   KotlinLambdaGroupIdFactory() {
   }
 
-  // Creates a lambda group id for kotlin style lambda. Should never return null, if the lambda
-  // does not pass pre-requirements (mostly by not meeting high-level structure expectations)
-  // should throw LambdaStructureError leaving the caller to decide if/how it needs to be reported.
+  public static KotlinLambdaGroupIdFactory getFactoryForClass(DexProgramClass clazz) {
+    if (clazz.getKotlinInfo().isSyntheticClass()
+        && clazz.getKotlinInfo().asSyntheticClass().isLambda()) {
+      if (clazz.getKotlinInfo().asSyntheticClass().isKotlinStyleLambda()) {
+        return KStyleLambdaGroupIdFactory.getInstance();
+      }
+      assert clazz.getKotlinInfo().asSyntheticClass().isJavaStyleLambda();
+      return JStyleLambdaGroupIdFactory.getInstance();
+    }
+    return null;
+  }
+
+  // Creates a lambda group id for a Java or Kotlin style lambda. Never returns null, but may throw
+  // a LambdaStructureError if the lambda does not pass pre-requirements (mostly by not meeting
+  // high-level structure expectations).
   //
   // At this point we only perform high-level checks before qualifying the lambda as a candidate
   // for merging and assigning lambda group id. We can NOT perform checks on method bodies since
   // they may not be converted yet, we'll do that in KStyleLambdaClassValidator.
-  public static LambdaGroupId create(
-      AppView<AppInfoWithLiveness> appView, Kotlin kotlin, DexClass lambda)
-      throws LambdaStructureError {
-
-    assert lambda.getKotlinInfo().isSyntheticClass();
-    if (lambda.getKotlinInfo().asSyntheticClass().isKotlinStyleLambda()) {
-      return KStyleLambdaGroupIdFactory.INSTANCE.validateAndCreate(appView, kotlin, lambda);
-    }
-
-    assert lambda.getKotlinInfo().asSyntheticClass().isJavaStyleLambda();
-    return JStyleLambdaGroupIdFactory.INSTANCE.validateAndCreate(appView, kotlin, lambda);
-  }
-
-  abstract LambdaGroupId validateAndCreate(
+  public abstract LambdaGroupId validateAndCreate(
       AppView<AppInfoWithLiveness> appView, Kotlin kotlin, DexClass lambda)
       throws LambdaStructureError;
 
diff --git a/src/main/java/com/android/tools/r8/utils/ConsumerUtils.java b/src/main/java/com/android/tools/r8/utils/ConsumerUtils.java
index c1c7d5a..8d66524 100644
--- a/src/main/java/com/android/tools/r8/utils/ConsumerUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ConsumerUtils.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.utils;
 
 import java.util.Set;
+import java.util.function.BiConsumer;
 import java.util.function.Consumer;
 
 public class ConsumerUtils {
@@ -21,6 +22,10 @@
     return ignore -> {};
   }
 
+  public static <S, T> BiConsumer<S, T> emptyBiConsumer() {
+    return (s, t) -> {};
+  }
+
   public static <T> ThrowingConsumer<T, RuntimeException> emptyThrowingConsumer() {
     return ignore -> {};
   }
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 eb2d165..60153de 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -33,14 +33,18 @@
 import com.android.tools.r8.graph.DexItem;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.EnumValueInfoMapCollection;
 import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.classmerging.HorizontallyMergedLambdaClasses;
 import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
 import com.android.tools.r8.inspector.internal.InspectorImpl;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.conversion.MethodProcessingId;
 import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
 import com.android.tools.r8.ir.optimize.Inliner;
+import com.android.tools.r8.ir.optimize.lambda.kotlin.KotlinLambdaGroupIdFactory;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.position.Position;
 import com.android.tools.r8.references.Reference;
@@ -1215,6 +1219,9 @@
 
     public BiConsumer<AppInfoWithLiveness, Enqueuer.Mode> enqueuerInspector = null;
 
+    public Function<DexProgramClass, KotlinLambdaGroupIdFactory> kotlinLambdaMergerFactoryForClass =
+        KotlinLambdaGroupIdFactory::getFactoryForClass;
+
     public BiConsumer<ProgramMethod, MethodProcessingId> methodProcessingIdConsumer = null;
 
     public Function<AppView<AppInfoWithLiveness>, RepackagingConfiguration>
@@ -1223,8 +1230,14 @@
                 new DefaultRepackagingConfiguration(
                     appView.dexItemFactory(), appView.options().getProguardConfiguration());
 
+    public BiConsumer<DexItemFactory, HorizontallyMergedLambdaClasses>
+        horizontallyMergedLambdaClassesConsumer = ConsumerUtils.emptyBiConsumer();
+
+    public BiConsumer<DexItemFactory, EnumValueInfoMapCollection> unboxedEnumsConsumer =
+        ConsumerUtils.emptyBiConsumer();
+
     public BiConsumer<DexItemFactory, VerticallyMergedClasses> verticallyMergedClassesConsumer =
-        null;
+        ConsumerUtils.emptyBiConsumer();
 
     public Consumer<Deque<SortedProgramMethodSet>> waveModifier = waves -> {};
 
diff --git a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
index 1fa6604..e73dec3 100644
--- a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
@@ -10,8 +10,6 @@
 import com.android.tools.r8.TestBase.Backend;
 import com.android.tools.r8.debug.DebugTestConfig;
 import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase.KeepRuleConsumer;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
 import com.android.tools.r8.testing.AndroidBuildVersion;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
@@ -19,6 +17,9 @@
 import com.android.tools.r8.utils.ForwardingOutputStream;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.ThrowingOutputStream;
+import com.android.tools.r8.utils.codeinspector.EnumUnboxingInspector;
+import com.android.tools.r8.utils.codeinspector.HorizontallyMergedLambdaClassesInspector;
+import com.android.tools.r8.utils.codeinspector.VerticallyMergedClassesInspector;
 import com.google.common.base.Suppliers;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
@@ -28,7 +29,6 @@
 import java.util.Collection;
 import java.util.List;
 import java.util.concurrent.ExecutionException;
-import java.util.function.BiConsumer;
 import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Supplier;
@@ -106,10 +106,34 @@
         });
   }
 
-  public T addVerticallyMergedClassesInspector(
-      BiConsumer<DexItemFactory, VerticallyMergedClasses> inspector) {
+  public T addEnumUnboxingInspector(Consumer<EnumUnboxingInspector> inspector) {
     return addOptionsModification(
-        options -> options.testing.verticallyMergedClassesConsumer = inspector);
+        options ->
+            options.testing.unboxedEnumsConsumer =
+                ((dexItemFactory, unboxedEnums) ->
+                    inspector.accept(new EnumUnboxingInspector(dexItemFactory, unboxedEnums))));
+  }
+
+  public T addHorizontallyMergedLambdaClassesInspector(
+      Consumer<HorizontallyMergedLambdaClassesInspector> inspector) {
+    return addOptionsModification(
+        options ->
+            options.testing.horizontallyMergedLambdaClassesConsumer =
+                ((dexItemFactory, horizontallyMergedLambdaClasses) ->
+                    inspector.accept(
+                        new HorizontallyMergedLambdaClassesInspector(
+                            dexItemFactory, horizontallyMergedLambdaClasses))));
+  }
+
+  public T addVerticallyMergedClassesInspector(
+      Consumer<VerticallyMergedClassesInspector> inspector) {
+    return addOptionsModification(
+        options ->
+            options.testing.verticallyMergedClassesConsumer =
+                ((dexItemFactory, verticallyMergedClasses) ->
+                    inspector.accept(
+                        new VerticallyMergedClassesInspector(
+                            dexItemFactory, verticallyMergedClasses))));
   }
 
   public CR compile() throws CompilationFailedException {
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/ClassesHaveBeenMergedTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/ClassesHaveBeenMergedTest.java
index 11330ba..6ed6dd9 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/ClassesHaveBeenMergedTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/ClassesHaveBeenMergedTest.java
@@ -13,9 +13,8 @@
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.VerticallyMergedClassesInspector;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -48,12 +47,12 @@
         .assertSuccess();
   }
 
-  private void inspectVerticallyMergedClasses(
-      DexItemFactory dexItemFactory, VerticallyMergedClasses verticallyMergedClasses) {
-    assertMergedIntoSubtype(GenericInterface.class, dexItemFactory, verticallyMergedClasses);
-    assertMergedIntoSubtype(GenericAbstractClass.class, dexItemFactory, verticallyMergedClasses);
-    assertMergedIntoSubtype(Outer.SuperClass.class, dexItemFactory, verticallyMergedClasses);
-    assertMergedIntoSubtype(SuperClass.class, dexItemFactory, verticallyMergedClasses);
+  private void inspectVerticallyMergedClasses(VerticallyMergedClassesInspector inspector) {
+    inspector.assertMergedIntoSubtype(
+        GenericInterface.class,
+        GenericAbstractClass.class,
+        Outer.SuperClass.class,
+        SuperClass.class);
   }
 
   private void inspect(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/ExceptionTablesTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/ExceptionTablesTest.java
index ababe2f..ffe849e 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/ExceptionTablesTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/ExceptionTablesTest.java
@@ -12,10 +12,9 @@
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.android.tools.r8.utils.codeinspector.VerticallyMergedClassesInspector;
 import java.util.stream.IntStream;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -49,10 +48,8 @@
         .assertSuccess();
   }
 
-  private void inspectVerticallyMergedClasses(
-      DexItemFactory dexItemFactory, VerticallyMergedClasses verticallyMergedClasses) {
-    assertMergedIntoSubtype(ExceptionA.class, dexItemFactory, verticallyMergedClasses);
-    assertMergedIntoSubtype(Exception1.class, dexItemFactory, verticallyMergedClasses);
+  private void inspectVerticallyMergedClasses(VerticallyMergedClassesInspector inspector) {
+    inspector.assertMergedIntoSubtype(ExceptionA.class, Exception1.class);
   }
 
   private void inspect(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/NoIllegalClassAccessWithAccessModificationsTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/NoIllegalClassAccessWithAccessModificationsTest.java
index a0e15f7..35e27eb 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/NoIllegalClassAccessWithAccessModificationsTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/NoIllegalClassAccessWithAccessModificationsTest.java
@@ -15,9 +15,8 @@
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.classmerging.vertical.testclasses.NoIllegalClassAccessWithAccessModificationsTestClasses;
 import com.android.tools.r8.classmerging.vertical.testclasses.NoIllegalClassAccessWithAccessModificationsTestClasses.SimpleInterfaceFactory;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.VerticallyMergedClassesInspector;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -51,10 +50,8 @@
         .assertSuccess();
   }
 
-  private void inspectVerticallyMergedClasses(
-      DexItemFactory dexItemFactory, VerticallyMergedClasses verticallyMergedClasses) {
-    assertMergedIntoSubtype(SimpleInterface.class, dexItemFactory, verticallyMergedClasses);
-    assertMergedIntoSubtype(OtherSimpleInterface.class, dexItemFactory, verticallyMergedClasses);
+  private void inspectVerticallyMergedClasses(VerticallyMergedClassesInspector inspector) {
+    inspector.assertMergedIntoSubtype(SimpleInterface.class, OtherSimpleInterface.class);
   }
 
   private void inspect(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/SyntheticBridgeSignaturesTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/SyntheticBridgeSignaturesTest.java
index 7282401..829e56b 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/SyntheticBridgeSignaturesTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/SyntheticBridgeSignaturesTest.java
@@ -13,11 +13,10 @@
 import com.android.tools.r8.R8TestCompileResult;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
 import com.android.tools.r8.ir.optimize.Inliner.Reason;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.VerticallyMergedClassesInspector;
 import com.google.common.collect.ImmutableSet;
 import java.util.List;
 import org.junit.Test;
@@ -74,10 +73,8 @@
     }
   }
 
-  private void inspectVerticallyMergedClasses(
-      DexItemFactory dexItemFactory, VerticallyMergedClasses verticallyMergedClasses) {
-    assertMergedIntoSubtype(A.class, dexItemFactory, verticallyMergedClasses);
-    assertMergedIntoSubtype(B.class, dexItemFactory, verticallyMergedClasses);
+  private void inspectVerticallyMergedClasses(VerticallyMergedClassesInspector inspector) {
+    inspector.assertMergedIntoSubtype(A.class, B.class);
   }
 
   private void inspect(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTestBase.java b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTestBase.java
index 106c8c0..c11fcbd 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTestBase.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTestBase.java
@@ -20,13 +20,6 @@
     this.parameters = parameters;
   }
 
-  public static void assertMergedIntoSubtype(
-      Class<?> clazz,
-      DexItemFactory dexItemFactory,
-      VerticallyMergedClasses verticallyMergedClasses) {
-    assertTrue(verticallyMergedClasses.hasBeenMergedIntoSubtype(toDexType(clazz, dexItemFactory)));
-  }
-
   public void runDebugTest(Class<?> mainClass, R8TestCompileResult compileResult) throws Throwable {
     assertTrue(parameters.isDexRuntime());
     new VerticalClassMergerDebugTestRunner(mainClass.getTypeName(), temp)
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/JStyleKotlinLambdaMergingWithEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/JStyleKotlinLambdaMergingWithEnumUnboxingTest.java
new file mode 100644
index 0000000..eb7b242
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/JStyleKotlinLambdaMergingWithEnumUnboxingTest.java
@@ -0,0 +1,122 @@
+// 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.kotlin.lambda;
+
+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.ToolHelper;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.ir.optimize.lambda.kotlin.JStyleLambdaGroupIdFactory;
+import com.android.tools.r8.ir.optimize.lambda.kotlin.KotlinLambdaGroupIdFactory;
+import com.android.tools.r8.kotlin.lambda.JStyleKotlinLambdaMergingWithEnumUnboxingTest.Main.EnumUnboxingCandidate;
+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 JStyleKotlinLambdaMergingWithEnumUnboxingTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return TestBase.getTestParameters().withDexRuntimes().withAllApiLevels().build();
+  }
+
+  public JStyleKotlinLambdaMergingWithEnumUnboxingTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addDefaultRuntimeLibrary(parameters)
+        .addLibraryFiles(ToolHelper.getKotlinStdlibJar())
+        .addKeepMainRule(Main.class)
+        .addOptionsModification(
+            options ->
+                options.testing.kotlinLambdaMergerFactoryForClass =
+                    this::getKotlinLambdaMergerFactoryForClass)
+        .addHorizontallyMergedLambdaClassesInspector(
+            inspector -> inspector.assertMerged(Lambda1.class, Lambda2.class))
+        .addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(EnumUnboxingCandidate.class))
+        .enableInliningAnnotations()
+        .enableNeverClassInliningAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("Lambda1.method()", "Lambda2.method()");
+  }
+
+  private KotlinLambdaGroupIdFactory getKotlinLambdaMergerFactoryForClass(DexProgramClass clazz) {
+    String typeName = clazz.getType().toSourceString();
+    if (typeName.equals(Lambda1.class.getTypeName())
+        || typeName.equals(Lambda2.class.getTypeName())) {
+      return JStyleLambdaGroupIdFactory.getInstance();
+    }
+    return null;
+  }
+
+  static class Main {
+
+    @NeverClassInline
+    public enum EnumUnboxingCandidate {
+      LAMBDA1,
+      LAMBDA2
+    }
+
+    public static void main(String[] args) {
+      accept(createLambda(EnumUnboxingCandidate.LAMBDA1));
+      accept(createLambda(EnumUnboxingCandidate.LAMBDA2));
+    }
+
+    @NeverInline
+    static I createLambda(EnumUnboxingCandidate value) {
+      switch (value) {
+        case LAMBDA1:
+          return new Lambda1();
+        case LAMBDA2:
+          return new Lambda2();
+        default:
+          throw new RuntimeException();
+      }
+    }
+
+    @NeverInline
+    static void accept(I instance) {
+      instance.method();
+    }
+  }
+
+  interface I {
+
+    void method();
+  }
+
+  @NeverClassInline
+  public static final class Lambda1 implements I {
+
+    @NeverInline
+    @Override
+    public final void method() {
+      System.out.println("Lambda1.method()");
+    }
+  }
+
+  @NeverClassInline
+  public static final class Lambda2 implements I {
+
+    @NeverInline
+    @Override
+    public final void method() {
+      System.out.println("Lambda2.method()");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KStyleKotlinLambdaMergingWithEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KStyleKotlinLambdaMergingWithEnumUnboxingTest.java
new file mode 100644
index 0000000..61abad2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KStyleKotlinLambdaMergingWithEnumUnboxingTest.java
@@ -0,0 +1,129 @@
+// 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.kotlin.lambda;
+
+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.ToolHelper;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.ir.optimize.lambda.kotlin.KStyleLambdaGroupIdFactory;
+import com.android.tools.r8.ir.optimize.lambda.kotlin.KotlinLambdaGroupIdFactory;
+import com.android.tools.r8.kotlin.lambda.KStyleKotlinLambdaMergingWithEnumUnboxingTest.Main.EnumUnboxingCandidate;
+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 KStyleKotlinLambdaMergingWithEnumUnboxingTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return TestBase.getTestParameters().withDexRuntimes().withAllApiLevels().build();
+  }
+
+  public KStyleKotlinLambdaMergingWithEnumUnboxingTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addProgramFiles(ToolHelper.getKotlinStdlibJar())
+        .addKeepMainRule(Main.class)
+        .addOptionsModification(
+            options ->
+                options.testing.kotlinLambdaMergerFactoryForClass =
+                    this::getKotlinLambdaMergerFactoryForClass)
+        .addHorizontallyMergedLambdaClassesInspector(
+            inspector -> inspector.assertMerged(Lambda1.class, Lambda2.class))
+        .addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(EnumUnboxingCandidate.class))
+        .enableInliningAnnotations()
+        .enableNeverClassInliningAnnotations()
+        .noMinification()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("Lambda1.method()", "Lambda2.method()");
+  }
+
+  private KotlinLambdaGroupIdFactory getKotlinLambdaMergerFactoryForClass(DexProgramClass clazz) {
+    String typeName = clazz.getType().toSourceString();
+    if (typeName.equals(Lambda1.class.getTypeName())
+        || typeName.equals(Lambda2.class.getTypeName())) {
+      return KStyleLambdaGroupIdFactory.getInstance();
+    }
+    return null;
+  }
+
+  static class Main {
+
+    @NeverClassInline
+    public enum EnumUnboxingCandidate {
+      LAMBDA1,
+      LAMBDA2
+    }
+
+    public static void main(String[] args) {
+      accept(createLambda(EnumUnboxingCandidate.LAMBDA1));
+      accept(createLambda(EnumUnboxingCandidate.LAMBDA2));
+    }
+
+    @NeverInline
+    static kotlin.jvm.functions.Function0<kotlin.Unit> createLambda(EnumUnboxingCandidate value) {
+      switch (value) {
+        case LAMBDA1:
+          return new Lambda1();
+        case LAMBDA2:
+          return new Lambda2();
+        default:
+          throw new RuntimeException();
+      }
+    }
+
+    @NeverInline
+    static void accept(kotlin.jvm.functions.Function0<kotlin.Unit> instance) {
+      instance.invoke();
+    }
+  }
+
+  @NeverClassInline
+  public static final class Lambda1 extends kotlin.jvm.internal.Lambda<kotlin.Unit>
+      implements kotlin.jvm.functions.Function0<kotlin.Unit> {
+
+    public Lambda1() {
+      super(0);
+    }
+
+    @NeverInline
+    @Override
+    public final kotlin.Unit invoke() {
+      System.out.println("Lambda1.method()");
+      return null;
+    }
+  }
+
+  @NeverClassInline
+  public static final class Lambda2 extends kotlin.jvm.internal.Lambda<kotlin.Unit>
+      implements kotlin.jvm.functions.Function0<kotlin.Unit> {
+
+    public Lambda2() {
+      super(0);
+    }
+
+    @NeverInline
+    @Override
+    public final kotlin.Unit invoke() {
+      System.out.println("Lambda2.method()");
+      return null;
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/EnumUnboxingInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/EnumUnboxingInspector.java
new file mode 100644
index 0000000..2ca4579
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/EnumUnboxingInspector.java
@@ -0,0 +1,35 @@
+// 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.utils.codeinspector;
+
+import static com.android.tools.r8.TestBase.toDexType;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.EnumValueInfoMapCollection;
+
+public class EnumUnboxingInspector {
+
+  private final DexItemFactory dexItemFactory;
+  private final EnumValueInfoMapCollection unboxedEnums;
+
+  public EnumUnboxingInspector(
+      DexItemFactory dexItemFactory, EnumValueInfoMapCollection unboxedEnums) {
+    this.dexItemFactory = dexItemFactory;
+    this.unboxedEnums = unboxedEnums;
+  }
+
+  public EnumUnboxingInspector assertUnboxed(Class<? extends Enum<?>> clazz) {
+    assertTrue(unboxedEnums.containsEnum(toDexType(clazz, dexItemFactory)));
+    return this;
+  }
+
+  public EnumUnboxingInspector assertUnboxed(Class<? extends Enum<?>>... classes) {
+    for (Class<? extends Enum<?>> clazz : classes) {
+      assertUnboxed(clazz);
+    }
+    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
new file mode 100644
index 0000000..4b3579f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedLambdaClassesInspector.java
@@ -0,0 +1,36 @@
+// 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.utils.codeinspector;
+
+import static com.android.tools.r8.TestBase.toDexType;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.classmerging.HorizontallyMergedLambdaClasses;
+
+public class HorizontallyMergedLambdaClassesInspector {
+
+  private final DexItemFactory dexItemFactory;
+  private final HorizontallyMergedLambdaClasses horizontallyMergedLambdaClasses;
+
+  public HorizontallyMergedLambdaClassesInspector(
+      DexItemFactory dexItemFactory,
+      HorizontallyMergedLambdaClasses horizontallyMergedLambdaClasses) {
+    this.dexItemFactory = dexItemFactory;
+    this.horizontallyMergedLambdaClasses = horizontallyMergedLambdaClasses;
+  }
+
+  public HorizontallyMergedLambdaClassesInspector assertMerged(Class<?> clazz) {
+    assertTrue(horizontallyMergedLambdaClasses.hasBeenMerged(toDexType(clazz, dexItemFactory)));
+    return this;
+  }
+
+  public HorizontallyMergedLambdaClassesInspector assertMerged(Class<?>... classes) {
+    for (Class<?> clazz : classes) {
+      assertMerged(clazz);
+    }
+    return this;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/VerticallyMergedClassesInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/VerticallyMergedClassesInspector.java
new file mode 100644
index 0000000..bd64711
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/VerticallyMergedClassesInspector.java
@@ -0,0 +1,35 @@
+// 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.utils.codeinspector;
+
+import static com.android.tools.r8.TestBase.toDexType;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
+
+public class VerticallyMergedClassesInspector {
+
+  private final DexItemFactory dexItemFactory;
+  private final VerticallyMergedClasses verticallyMergedClasses;
+
+  public VerticallyMergedClassesInspector(
+      DexItemFactory dexItemFactory, VerticallyMergedClasses verticallyMergedClasses) {
+    this.dexItemFactory = dexItemFactory;
+    this.verticallyMergedClasses = verticallyMergedClasses;
+  }
+
+  public VerticallyMergedClassesInspector assertMergedIntoSubtype(Class<?> clazz) {
+    assertTrue(verticallyMergedClasses.hasBeenMergedIntoSubtype(toDexType(clazz, dexItemFactory)));
+    return this;
+  }
+
+  public VerticallyMergedClassesInspector assertMergedIntoSubtype(Class<?>... classes) {
+    for (Class<?> clazz : classes) {
+      assertMergedIntoSubtype(clazz);
+    }
+    return this;
+  }
+}