Merge commit '17024bf79ef3ea7a81018e3bd7b67358e328c3d3' into dev-release

Change-Id: Iee886bcc9c9cfe23e1a57969fdb239e19cb5ea31
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs.json b/src/library_desugar/jdk11/desugar_jdk_libs.json
index 3d23d9f..0020bab 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs.json
@@ -403,12 +403,34 @@
         "java.lang.Object java.util.concurrent.atomic.AtomicReference#getAndAccumulate(java.lang.Object, java.util.function.BinaryOperator)": "java.util.concurrent.atomic.DesugarAtomicReference",
         "java.lang.Object java.util.concurrent.atomic.AtomicReference#getAndUpdate(java.util.function.UnaryOperator)": "java.util.concurrent.atomic.DesugarAtomicReference",
         "java.lang.Object java.util.concurrent.atomic.AtomicReference#updateAndGet(java.util.function.UnaryOperator)": "java.util.concurrent.atomic.DesugarAtomicReference",
+        "java.util.Collection java.util.Collections#checkedCollection(java.util.Collection, java.lang.Class)": "java.util.DesugarCollections",
+        "java.util.Set java.util.Collections#checkedSet(java.util.Set, java.lang.Class)": "java.util.DesugarCollections",
+        "java.util.SortedSet java.util.Collections#checkedSortedSet(java.util.SortedSet, java.lang.Class)": "java.util.DesugarCollections",
+        "java.util.List java.util.Collections#checkedList(java.util.List, java.lang.Class)": "java.util.DesugarCollections",
+        "java.util.Map java.util.Collections#checkedMap(java.util.Map, java.lang.Class, java.lang.Class)": "java.util.DesugarCollections",
+        "java.util.SortedMap java.util.Collections#checkedSortedMap(java.util.SortedMap, java.lang.Class, java.lang.Class)": "java.util.DesugarCollections",
+        "java.util.Collection java.util.Collections#unmodifiableCollection(java.util.Collection)": "java.util.DesugarCollections",
+        "java.util.Set java.util.Collections#unmodifiableSet(java.util.Set)": "java.util.DesugarCollections",
+        "java.util.SortedSet java.util.Collections#unmodifiableSortedSet(java.util.SortedSet)": "java.util.DesugarCollections",
+        "java.util.List java.util.Collections#unmodifiableList(java.util.List)": "java.util.DesugarCollections",
+        "java.util.Map java.util.Collections#unmodifiableMap(java.util.Map)": "java.util.DesugarCollections",
+        "java.util.SortedMap java.util.Collections#unmodifiableSortedMap(java.util.SortedMap)": "java.util.DesugarCollections",
+        "java.util.Collection java.util.Collections#synchronizedCollection(java.util.Collection)": "java.util.DesugarCollections",
+        "java.util.Set java.util.Collections#synchronizedSet(java.util.Set)": "java.util.DesugarCollections",
+        "java.util.SortedSet java.util.Collections#synchronizedSortedSet(java.util.SortedSet)": "java.util.DesugarCollections",
+        "java.util.List java.util.Collections#synchronizedList(java.util.List)": "java.util.DesugarCollections",
         "java.util.Map java.util.Collections#synchronizedMap(java.util.Map)": "java.util.DesugarCollections",
         "java.util.SortedMap java.util.Collections#synchronizedSortedMap(java.util.SortedMap)": "java.util.DesugarCollections",
         "long java.util.concurrent.atomic.AtomicLong#accumulateAndGet(long, java.util.function.LongBinaryOperator)": "java.util.concurrent.atomic.DesugarAtomicLong",
         "long java.util.concurrent.atomic.AtomicLong#getAndAccumulate(long, java.util.function.LongBinaryOperator)": "java.util.concurrent.atomic.DesugarAtomicLong",
         "long java.util.concurrent.atomic.AtomicLong#getAndUpdate(java.util.function.LongUnaryOperator)": "java.util.concurrent.atomic.DesugarAtomicLong",
         "long java.util.concurrent.atomic.AtomicLong#updateAndGet(java.util.function.LongUnaryOperator)": "java.util.concurrent.atomic.DesugarAtomicLong"
+      },
+      "api_generic_types_conversion": {
+        "java.util.Collection java.util.Hashtable#values()": [-1, "java.util.Collection java.util.DesugarCollections#bridge_synchronizedCollection(java.util.Collection, java.lang.Object)"],
+        "java.util.Set java.util.Hashtable#entrySet()": [-1, "java.util.Set java.util.DesugarCollections#bridge_synchronizedSet(java.util.Set, java.lang.Object)"],
+        "java.util.Set java.util.Hashtable#keySet()": [-1, "java.util.Set java.util.DesugarCollections#bridge_synchronizedSet(java.util.Set, java.lang.Object)"],
+        "java.util.List java.util.Vector#subList(int, int)": [-1, "java.util.List java.util.DesugarCollections#bridge_synchronizedList(java.util.List, java.lang.Object)"]
       }
     }
   ],
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_nio.json b/src/library_desugar/jdk11/desugar_jdk_libs_nio.json
index 0f81af4..aa8b1c0 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs_nio.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs_nio.json
@@ -571,12 +571,34 @@
         "java.lang.Object java.util.concurrent.atomic.AtomicReference#getAndAccumulate(java.lang.Object, java.util.function.BinaryOperator)": "java.util.concurrent.atomic.DesugarAtomicReference",
         "java.lang.Object java.util.concurrent.atomic.AtomicReference#getAndUpdate(java.util.function.UnaryOperator)": "java.util.concurrent.atomic.DesugarAtomicReference",
         "java.lang.Object java.util.concurrent.atomic.AtomicReference#updateAndGet(java.util.function.UnaryOperator)": "java.util.concurrent.atomic.DesugarAtomicReference",
+        "java.util.Collection java.util.Collections#checkedCollection(java.util.Collection, java.lang.Class)": "java.util.DesugarCollections",
+        "java.util.Set java.util.Collections#checkedSet(java.util.Set, java.lang.Class)": "java.util.DesugarCollections",
+        "java.util.SortedSet java.util.Collections#checkedSortedSet(java.util.SortedSet, java.lang.Class)": "java.util.DesugarCollections",
+        "java.util.List java.util.Collections#checkedList(java.util.List, java.lang.Class)": "java.util.DesugarCollections",
+        "java.util.Map java.util.Collections#checkedMap(java.util.Map, java.lang.Class, java.lang.Class)": "java.util.DesugarCollections",
+        "java.util.SortedMap java.util.Collections#checkedSortedMap(java.util.SortedMap, java.lang.Class, java.lang.Class)": "java.util.DesugarCollections",
+        "java.util.Collection java.util.Collections#unmodifiableCollection(java.util.Collection)": "java.util.DesugarCollections",
+        "java.util.Set java.util.Collections#unmodifiableSet(java.util.Set)": "java.util.DesugarCollections",
+        "java.util.SortedSet java.util.Collections#unmodifiableSortedSet(java.util.SortedSet)": "java.util.DesugarCollections",
+        "java.util.List java.util.Collections#unmodifiableList(java.util.List)": "java.util.DesugarCollections",
+        "java.util.Map java.util.Collections#unmodifiableMap(java.util.Map)": "java.util.DesugarCollections",
+        "java.util.SortedMap java.util.Collections#unmodifiableSortedMap(java.util.SortedMap)": "java.util.DesugarCollections",
+        "java.util.Collection java.util.Collections#synchronizedCollection(java.util.Collection)": "java.util.DesugarCollections",
+        "java.util.Set java.util.Collections#synchronizedSet(java.util.Set)": "java.util.DesugarCollections",
+        "java.util.SortedSet java.util.Collections#synchronizedSortedSet(java.util.SortedSet)": "java.util.DesugarCollections",
+        "java.util.List java.util.Collections#synchronizedList(java.util.List)": "java.util.DesugarCollections",
         "java.util.Map java.util.Collections#synchronizedMap(java.util.Map)": "java.util.DesugarCollections",
         "java.util.SortedMap java.util.Collections#synchronizedSortedMap(java.util.SortedMap)": "java.util.DesugarCollections",
         "long java.util.concurrent.atomic.AtomicLong#accumulateAndGet(long, java.util.function.LongBinaryOperator)": "java.util.concurrent.atomic.DesugarAtomicLong",
         "long java.util.concurrent.atomic.AtomicLong#getAndAccumulate(long, java.util.function.LongBinaryOperator)": "java.util.concurrent.atomic.DesugarAtomicLong",
         "long java.util.concurrent.atomic.AtomicLong#getAndUpdate(java.util.function.LongUnaryOperator)": "java.util.concurrent.atomic.DesugarAtomicLong",
         "long java.util.concurrent.atomic.AtomicLong#updateAndGet(java.util.function.LongUnaryOperator)": "java.util.concurrent.atomic.DesugarAtomicLong"
+      },
+      "api_generic_types_conversion": {
+        "java.util.Collection java.util.Hashtable#values()": [-1, "java.util.Collection java.util.DesugarCollections#bridge_synchronizedCollection(java.util.Collection, java.lang.Object)"],
+        "java.util.Set java.util.Hashtable#entrySet()": [-1, "java.util.Set java.util.DesugarCollections#bridge_synchronizedSet(java.util.Set, java.lang.Object)"],
+        "java.util.Set java.util.Hashtable#keySet()": [-1, "java.util.Set java.util.DesugarCollections#bridge_synchronizedSet(java.util.Set, java.lang.Object)"],
+        "java.util.List java.util.Vector#subList(int, int)": [-1, "java.util.List java.util.DesugarCollections#bridge_synchronizedList(java.util.List, java.lang.Object)"]
       }
     },
     {
diff --git a/src/main/java/com/android/tools/r8/classmerging/Policy.java b/src/main/java/com/android/tools/r8/classmerging/Policy.java
index e7f52f3..7655762 100644
--- a/src/main/java/com/android/tools/r8/classmerging/Policy.java
+++ b/src/main/java/com/android/tools/r8/classmerging/Policy.java
@@ -8,7 +8,6 @@
 import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
 import com.android.tools.r8.horizontalclassmerging.MultiClassPolicyWithPreprocessing;
 import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy;
-import com.android.tools.r8.verticalclassmerging.policies.VerticalClassMergerPolicy;
 import com.android.tools.r8.verticalclassmerging.policies.VerticalClassMergerPolicyWithPreprocessing;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -60,16 +59,7 @@
     return false;
   }
 
-  public VerticalClassMergerPolicy asVerticalClassMergerPolicy() {
-    return null;
-  }
-
-  public boolean isVerticalClassMergerPolicyWithPreprocessing() {
-    return false;
-  }
-
-  public VerticalClassMergerPolicyWithPreprocessing<?>
-      asVerticalClassMergerPolicyWithPreprocessing() {
+  public VerticalClassMergerPolicyWithPreprocessing<?> asVerticalClassMergerPolicy() {
     return null;
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index ea46ea8..3c8c2fd 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -1141,9 +1141,8 @@
     return null;
   }
 
-  @SuppressWarnings("ReferenceEquality")
   public boolean isInSameNest(DexClass other) {
-    return isInANest() && other.isInANest() && getNestHost() == other.getNestHost();
+    return isInANest() && other.isInANest() && getNestHost().isIdenticalTo(other.getNestHost());
   }
 
   public void forEachNestMember(Consumer<DexType> consumer) {
diff --git a/src/main/java/com/android/tools/r8/utils/ArchiveResourceProvider.java b/src/main/java/com/android/tools/r8/utils/ArchiveResourceProvider.java
index 6532b8f..b73119d 100644
--- a/src/main/java/com/android/tools/r8/utils/ArchiveResourceProvider.java
+++ b/src/main/java/com/android/tools/r8/utils/ArchiveResourceProvider.java
@@ -93,8 +93,10 @@
     }
     if (!dexResources.isEmpty() && !classResources.isEmpty()) {
       throw new CompilationError(
-          "Cannot create android app from an archive '" + archive
-              + "' containing both DEX and Java-bytecode content");
+          "Cannot create android app from an archive '"
+              + archive
+              + "' containing both DEX and Java-bytecode content",
+          origin);
     }
     return !dexResources.isEmpty() ? dexResources : classResources;
   }
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/ClassMerger.java b/src/main/java/com/android/tools/r8/verticalclassmerging/ClassMerger.java
index a198871..86b9848 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/ClassMerger.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/ClassMerger.java
@@ -8,6 +8,7 @@
 import static com.android.tools.r8.ir.code.InvokeType.DIRECT;
 import static com.android.tools.r8.ir.code.InvokeType.STATIC;
 import static com.android.tools.r8.ir.code.InvokeType.VIRTUAL;
+import static java.util.function.Predicate.not;
 
 import com.android.tools.r8.cf.CfVersion;
 import com.android.tools.r8.errors.Unreachable;
@@ -93,6 +94,77 @@
     this.target = group.getTarget();
   }
 
+  public void setup() {
+    setupInvokeSuperMapping();
+  }
+
+  private void setupInvokeSuperMapping() {
+    source.forEachProgramMethod(this::redirectSuperCallsToMethod);
+  }
+
+  private void redirectSuperCallsToMethod(ProgramMethod sourceMethod) {
+    if (sourceMethod.getAccessFlags().belongsToDirectPool()) {
+      redirectSuperCallsToDirectMethod(sourceMethod);
+    } else {
+      redirectSuperCallsToVirtualMethod(sourceMethod);
+    }
+  }
+
+  private void redirectSuperCallsToDirectMethod(ProgramMethod sourceMethod) {
+    DexEncodedMethod definition = sourceMethod.getDefinition();
+    if (appView.options().canUseNestBasedAccess()
+        && source.isInSameNest(target)
+        && definition.isInstance()
+        && !definition.isInstanceInitializer()
+        && AccessControl.isMemberAccessible(sourceMethod, source, target, appView).isTrue()) {
+      lensBuilder.mapVirtualMethodToDirectInType(sourceMethod.getReference(), sourceMethod, target);
+    }
+  }
+
+  private void redirectSuperCallsToVirtualMethod(ProgramMethod sourceMethod) {
+    if (sourceMethod.getAccessFlags().isAbstract()) {
+      return;
+    }
+    if (source.isInterface()) {
+      // If we merge a default interface method from interface I to its subtype C, then we need
+      // to rewrite invocations on the form "invoke-super I.m()" to "invoke-direct C.m$I()".
+      //
+      // Unlike when we merge a class into its subclass (the else-branch below), we should *not*
+      // rewrite any invocations on the form "invoke-super J.m()" to "invoke-direct C.m$I()",
+      // if I has a supertype J. This is due to the fact that invoke-super instructions that
+      // resolve to a method on an interface never hit an implementation below that interface.
+      lensBuilder.mapVirtualMethodToDirectInType(sourceMethod.getReference(), sourceMethod, target);
+    } else {
+      // If we merge class B into class C, and class C contains an invocation super.m(), then it
+      // is insufficient to rewrite "invoke-super B.m()" to "invoke-{direct,virtual} C.m$B()" (the
+      // method C.m$B denotes the direct/virtual method that has been created in C for B.m). In
+      // particular, there might be an instruction "invoke-super A.m()" in C that resolves to B.m
+      // at runtime (A is a superclass of B), which also needs to be rewritten to
+      // "invoke-{direct,virtual} C.m$B()".
+      //
+      // We handle this by adding a mapping for [target] and all of its supertypes.
+      DexProgramClass current = target;
+      while (current != null) {
+        // Only rewrite the invoke-super call if it does not lead to a NoSuchMethodError.
+        boolean resolutionSucceeds =
+            appView
+                .appInfo()
+                .resolveMethodOnClass(current, sourceMethod.getReference())
+                .isSingleResolution();
+        if (!resolutionSucceeds) {
+          break;
+        }
+        DexMethod signatureInHolder =
+            sourceMethod.getReference().withHolder(current, dexItemFactory);
+        lensBuilder.mapVirtualMethodToDirectInType(signatureInHolder, sourceMethod, target);
+        current =
+            current.hasSuperType()
+                ? asProgramClassOrNull(appView.definitionFor(current.getSuperType()))
+                : null;
+      }
+    }
+  }
+
   public void merge() {
     // Merge the class [clazz] into [targetClass] by adding all methods to
     // targetClass that are not currently contained.
@@ -132,51 +204,37 @@
             add(directMethods, resultingDirectMethod, MethodSignatureEquivalence.get());
             lensBuilder.recordMove(directMethod.getDefinition(), resultingDirectMethod);
             blockRedirectionOfSuperCalls(resultingDirectMethod);
-
-            // Private methods in the parent class may be targeted with invoke-super if the two
-            // classes are in the same nest. Ensure such calls are mapped to invoke-direct.
-            if (definition.isInstance()
-                && definition.isPrivate()
-                && AccessControl.isMemberAccessible(directMethod, source, target, appView)
-                    .isTrue()) {
-              lensBuilder.mapVirtualMethodToDirectInType(
-                  definition.getReference(), definition, target.getType());
-            }
           }
         });
 
-    for (DexEncodedMethod virtualMethod : source.virtualMethods()) {
-      DexEncodedMethod shadowedBy = findMethodInTarget(virtualMethod);
+    for (DexEncodedMethod abstractMethod : source.virtualMethods(DexEncodedMethod::isAbstract)) {
+      DexEncodedMethod shadowedBy = findMethodInTarget(abstractMethod);
       if (shadowedBy != null) {
-        if (virtualMethod.isAbstract()) {
-          // Remove abstract/interface methods that are shadowed. The identity mapping below is
-          // needed to ensure we correctly fixup the mapping in case the signature refers to
-          // merged classes.
-          lensBuilder.recordSplit(virtualMethod, shadowedBy, null, null);
+        // Remove abstract/interface methods that are shadowed. The identity mapping below is
+        // needed to ensure we correctly fixup the mapping in case the signature refers to
+        // merged classes.
+        lensBuilder.recordSplit(abstractMethod, shadowedBy, null, null);
 
-          // The override now corresponds to the method in the parent, so unset its synthetic flag
-          // if the method in the parent is not synthetic.
-          if (!virtualMethod.isSyntheticMethod() && shadowedBy.isSyntheticMethod()) {
-            shadowedBy.getAccessFlags().demoteFromSynthetic();
-          }
-          continue;
+        // The override now corresponds to the method in the parent, so unset its synthetic flag
+        // if the method in the parent is not synthetic.
+        if (!abstractMethod.isSyntheticMethod() && shadowedBy.isSyntheticMethod()) {
+          shadowedBy.getAccessFlags().demoteFromSynthetic();
         }
       } else {
         // The method is not shadowed. If it is abstract, we can simply move it to the subclass.
-        // Non-abstract methods are handled below (they cannot simply be moved to the subclass as
-        // a virtual method, because they might be the target of an invoke-super instruction).
-        if (virtualMethod.isAbstract()) {
-          assert target.isAbstract();
-          // Update the holder of [virtualMethod] using renameMethod().
-          DexEncodedMethod resultingVirtualMethod =
-              renameMethod(virtualMethod, availableMethodSignatures, Rename.NEVER);
-          resultingVirtualMethod.setLibraryMethodOverride(virtualMethod.isLibraryMethodOverride());
-          lensBuilder.recordMove(virtualMethod, resultingVirtualMethod);
-          add(virtualMethods, resultingVirtualMethod, MethodSignatureEquivalence.get());
-          continue;
-        }
+        assert target.isAbstract();
+        // Update the holder of [virtualMethod] using renameMethod().
+        DexEncodedMethod resultingAbstractMethod =
+            renameMethod(abstractMethod, availableMethodSignatures, Rename.NEVER);
+        resultingAbstractMethod.setLibraryMethodOverride(abstractMethod.isLibraryMethodOverride());
+        lensBuilder.recordMove(abstractMethod, resultingAbstractMethod);
+        add(virtualMethods, resultingAbstractMethod, MethodSignatureEquivalence.get());
       }
+    }
 
+    for (DexEncodedMethod virtualMethod :
+        source.virtualMethods(not(DexEncodedMethod::isAbstract))) {
+      DexEncodedMethod shadowedBy = findMethodInTarget(virtualMethod);
       DexEncodedMethod resultingMethod;
       if (source.isInterface()) {
         // Moving a default interface method into its subtype. This method could be hit directly
@@ -210,10 +268,6 @@
           resultingMethod,
           MethodSignatureEquivalence.get());
 
-      // Record that invoke-super instructions in the target class should be redirected to the
-      // newly created direct method.
-      redirectSuperCallsInTarget(virtualMethod);
-
       DexEncodedMethod bridge = null;
       DexEncodedMethod override = shadowedBy;
       if (shadowedBy == null) {
@@ -468,66 +522,6 @@
         type -> true);
   }
 
-  private void redirectSuperCallsInTarget(DexEncodedMethod oldTarget) {
-    DexMethod oldTargetReference = oldTarget.getReference();
-    if (source.isInterface()) {
-      // If we merge a default interface method from interface I to its subtype C, then we need
-      // to rewrite invocations on the form "invoke-super I.m()" to "invoke-direct C.m$I()".
-      //
-      // Unlike when we merge a class into its subclass (the else-branch below), we should *not*
-      // rewrite any invocations on the form "invoke-super J.m()" to "invoke-direct C.m$I()",
-      // if I has a supertype J. This is due to the fact that invoke-super instructions that
-      // resolve to a method on an interface never hit an implementation below that interface.
-      lensBuilder.mapVirtualMethodToDirectInType(oldTargetReference, oldTarget, target.getType());
-    } else {
-      // If we merge class B into class C, and class C contains an invocation super.m(), then it
-      // is insufficient to rewrite "invoke-super B.m()" to "invoke-{direct,virtual} C.m$B()" (the
-      // method C.m$B denotes the direct/virtual method that has been created in C for B.m). In
-      // particular, there might be an instruction "invoke-super A.m()" in C that resolves to B.m
-      // at runtime (A is a superclass of B), which also needs to be rewritten to
-      // "invoke-{direct,virtual} C.m$B()".
-      //
-      // We handle this by adding a mapping for [target] and all of its supertypes.
-      DexProgramClass holder = target;
-      while (holder != null) {
-        DexMethod signatureInHolder = oldTargetReference.withHolder(holder, dexItemFactory);
-        // Only rewrite the invoke-super call if it does not lead to a NoSuchMethodError.
-        boolean resolutionSucceeds =
-            appView.appInfo().resolveMethodOnClass(holder, signatureInHolder).isSingleResolution();
-        if (resolutionSucceeds) {
-          lensBuilder.mapVirtualMethodToDirectInType(signatureInHolder, oldTarget, target.type);
-        } else {
-          break;
-        }
-
-        // Consider that A gets merged into B and B's subclass C gets merged into D. Instructions
-        // on the form "invoke-super {B,C,D}.m()" in D are changed into "invoke-direct D.m$C()" by
-        // the code above. However, instructions on the form "invoke-super A.m()" should also be
-        // changed into "invoke-direct D.m$C()". This is achieved by also considering the classes
-        // that have been merged into [holder].
-        Set<DexType> mergedTypes = verticallyMergedClassesBuilder.getSourcesFor(holder);
-        for (DexType type : mergedTypes) {
-          DexMethod signatureInType = oldTargetReference.withHolder(type, dexItemFactory);
-          // Resolution would have succeeded if the method used to be in [type], or if one of
-          // its super classes declared the method.
-          // TODO(b/315283244): Should not rely on lens for this. Instead precompute this before
-          //  merging any classes.
-          boolean resolutionSucceededBeforeMerge =
-              outerLensBuilder.hasMappingForSignatureInContext(holder, signatureInType)
-                  || appView.appInfo().lookupSuperTarget(signatureInHolder, holder, appView)
-                      != null;
-          if (resolutionSucceededBeforeMerge) {
-            lensBuilder.mapVirtualMethodToDirectInType(signatureInType, oldTarget, target.type);
-          }
-        }
-        holder =
-            holder.hasSuperType()
-                ? asProgramClassOrNull(appView.definitionFor(holder.getSuperType()))
-                : null;
-      }
-    }
-  }
-
   private void blockRedirectionOfSuperCalls(DexEncodedMethod method) {
     // We are merging a class B into C. The methods from B are being moved into C, and then we
     // subsequently rewrite the invoke-super instructions in C that hit a method in B, such that
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/ConnectedComponentVerticalClassMerger.java b/src/main/java/com/android/tools/r8/verticalclassmerging/ConnectedComponentVerticalClassMerger.java
index fdd9383..dc82f99 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/ConnectedComponentVerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/ConnectedComponentVerticalClassMerger.java
@@ -14,7 +14,7 @@
 public class ConnectedComponentVerticalClassMerger {
 
   private final AppView<AppInfoWithLiveness> appView;
-  private final Collection<VerticalMergeGroup> classesToMerge;
+  private final Collection<VerticalMergeGroup> groups;
 
   // The resulting graph lens that should be used after class merging.
   private final VerticalClassMergerGraphLens.Builder lensBuilder;
@@ -26,25 +26,31 @@
       VerticallyMergedClasses.builder();
 
   ConnectedComponentVerticalClassMerger(
-      AppView<AppInfoWithLiveness> appView, Collection<VerticalMergeGroup> classesToMerge) {
+      AppView<AppInfoWithLiveness> appView, Collection<VerticalMergeGroup> groups) {
     this.appView = appView;
-    this.classesToMerge = classesToMerge;
+    this.groups = groups;
     this.lensBuilder = new VerticalClassMergerGraphLens.Builder();
   }
 
   public boolean isEmpty() {
-    return classesToMerge.isEmpty();
+    return groups.isEmpty();
   }
 
   public VerticalClassMergerResult.Builder run() {
-    List<VerticalMergeGroup> classesToMergeSorted =
-        ListUtils.sort(classesToMerge, Comparator.comparing(group -> group.getSource().getType()));
-    for (VerticalMergeGroup group : classesToMergeSorted) {
-      ClassMerger classMerger =
-          new ClassMerger(
-              appView, lensBuilder, synthesizedBridges, verticallyMergedClassesBuilder, group);
-      classMerger.merge();
-    }
+    List<VerticalMergeGroup> groupsSorted =
+        ListUtils.sort(groups, Comparator.comparing(group -> group.getSource().getType()));
+    List<ClassMerger> classMergers =
+        ListUtils.map(
+            groupsSorted,
+            group ->
+                new ClassMerger(
+                    appView,
+                    lensBuilder,
+                    synthesizedBridges,
+                    verticallyMergedClassesBuilder,
+                    group));
+    classMergers.forEach(ClassMerger::setup);
+    classMergers.forEach(ClassMerger::merge);
     return VerticalClassMergerResult.builder(
         lensBuilder, synthesizedBridges, verticallyMergedClassesBuilder);
   }
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerGraphLens.java b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerGraphLens.java
index a3d1257..3987216 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerGraphLens.java
@@ -14,6 +14,7 @@
 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.ProgramMethod;
 import com.android.tools.r8.graph.lens.GraphLens;
 import com.android.tools.r8.graph.lens.MethodLookupResult;
 import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
@@ -515,9 +516,10 @@
       }
     }
 
-    public void mapVirtualMethodToDirectInType(DexMethod from, DexEncodedMethod to, DexType type) {
+    public void mapVirtualMethodToDirectInType(
+        DexMethod from, ProgramMethod to, DexProgramClass context) {
       contextualSuperToImplementationInContexts
-          .computeIfAbsent(type, ignoreKey(IdentityHashMap::new))
+          .computeIfAbsent(context.getType(), ignoreKey(IdentityHashMap::new))
           .put(from, to.getReference());
     }
 
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerPolicyExecutor.java b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerPolicyExecutor.java
index bab7656..1874199 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerPolicyExecutor.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerPolicyExecutor.java
@@ -35,7 +35,6 @@
 import com.android.tools.r8.verticalclassmerging.policies.SameNestPolicy;
 import com.android.tools.r8.verticalclassmerging.policies.SameStartupPartitionPolicy;
 import com.android.tools.r8.verticalclassmerging.policies.SuccessfulVirtualMethodResolutionInTargetPolicy;
-import com.android.tools.r8.verticalclassmerging.policies.VerticalClassMergerPolicy;
 import com.android.tools.r8.verticalclassmerging.policies.VerticalClassMergerPolicyWithPreprocessing;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -113,18 +112,8 @@
   protected LinkedList<VerticalMergeGroup> apply(
       Policy policy, LinkedList<VerticalMergeGroup> linkedGroups, ExecutorService executorService)
       throws ExecutionException {
-    if (policy.isVerticalClassMergerPolicy()) {
-      return apply(policy.asVerticalClassMergerPolicy(), linkedGroups);
-    } else {
-      assert policy.isVerticalClassMergerPolicyWithPreprocessing();
-      return apply(policy.asVerticalClassMergerPolicyWithPreprocessing(), linkedGroups);
-    }
-  }
-
-  private LinkedList<VerticalMergeGroup> apply(
-      VerticalClassMergerPolicy policy, LinkedList<VerticalMergeGroup> linkedGroups) {
-    linkedGroups.removeIf(group -> !policy.canMerge(group));
-    return linkedGroups;
+    assert policy.isVerticalClassMergerPolicy();
+    return apply(policy.asVerticalClassMergerPolicy(), linkedGroups);
   }
 
   private <T> LinkedList<VerticalMergeGroup> apply(
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/policies/VerticalClassMergerPolicy.java b/src/main/java/com/android/tools/r8/verticalclassmerging/policies/VerticalClassMergerPolicy.java
index 49f179f..b5a6390 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/policies/VerticalClassMergerPolicy.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/policies/VerticalClassMergerPolicy.java
@@ -3,20 +3,21 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.verticalclassmerging.policies;
 
-import com.android.tools.r8.classmerging.Policy;
 import com.android.tools.r8.verticalclassmerging.VerticalMergeGroup;
+import java.util.Collection;
 
-public abstract class VerticalClassMergerPolicy extends Policy {
+public abstract class VerticalClassMergerPolicy
+    extends VerticalClassMergerPolicyWithPreprocessing<Void> {
 
   public abstract boolean canMerge(VerticalMergeGroup group);
 
   @Override
-  public boolean isVerticalClassMergerPolicy() {
-    return true;
+  public final boolean canMerge(VerticalMergeGroup group, Void data) {
+    return canMerge(group);
   }
 
   @Override
-  public VerticalClassMergerPolicy asVerticalClassMergerPolicy() {
-    return this;
+  public final Void preprocess(Collection<VerticalMergeGroup> groups) {
+    return null;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/policies/VerticalClassMergerPolicyWithPreprocessing.java b/src/main/java/com/android/tools/r8/verticalclassmerging/policies/VerticalClassMergerPolicyWithPreprocessing.java
index 7737ebc..5efbe75 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/policies/VerticalClassMergerPolicyWithPreprocessing.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/policies/VerticalClassMergerPolicyWithPreprocessing.java
@@ -14,13 +14,12 @@
   public abstract T preprocess(Collection<VerticalMergeGroup> groups);
 
   @Override
-  public boolean isVerticalClassMergerPolicyWithPreprocessing() {
+  public boolean isVerticalClassMergerPolicy() {
     return true;
   }
 
   @Override
-  public VerticalClassMergerPolicyWithPreprocessing<T>
-      asVerticalClassMergerPolicyWithPreprocessing() {
+  public VerticalClassMergerPolicyWithPreprocessing<T> asVerticalClassMergerPolicy() {
     return this;
   }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugarCollectionsTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugarCollectionsTest.java
new file mode 100644
index 0000000..a740261
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugarCollectionsTest.java
@@ -0,0 +1,283 @@
+// Copyright (c) 2023, 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.desugar.desugaredlibrary;
+
+import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.DEFAULT_SPECIFICATIONS;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11_PATH;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification;
+import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.Spliterator;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.Vector;
+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 DesugarCollectionsTest extends DesugaredLibraryTestBase {
+
+  private final TestParameters parameters;
+  private final CompilationSpecification compilationSpecification;
+  private final LibraryDesugaringSpecification libraryDesugaringSpecification;
+
+  @Parameters(name = "{0}, spec: {1}, {2}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withDexRuntimesAndAllApiLevels().build(),
+        ImmutableList.of(JDK11, JDK11_PATH),
+        DEFAULT_SPECIFICATIONS);
+  }
+
+  public DesugarCollectionsTest(
+      TestParameters parameters,
+      LibraryDesugaringSpecification libraryDesugaringSpecification,
+      CompilationSpecification compilationSpecification) {
+    this.parameters = parameters;
+    this.compilationSpecification = compilationSpecification;
+    this.libraryDesugaringSpecification = libraryDesugaringSpecification;
+  }
+
+  @Test
+  public void testDesugarCollectionsTest() throws Throwable {
+    testForDesugaredLibrary(parameters, libraryDesugaringSpecification, compilationSpecification)
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutput(getExpectedResult());
+  }
+
+  private String getExpectedResult() {
+    List<String> result = new ArrayList<>();
+    for (int i = 0; i < 9; i++) {
+      result.add("item");
+    }
+    for (int i = 0; i < 6; i++) {
+      result.add("k v");
+    }
+    for (int i = 0; i < 6; i++) {
+      result.add("exception");
+    }
+    result.addAll(ImmutableList.of("one", "v", "k", "k=v"));
+    return StringUtils.lines(result);
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      successTest();
+      exceptionTest();
+      apiTest();
+    }
+
+    private static void apiTest() {
+      Vector<String> strings = new Vector<>();
+      strings.add("one");
+      strings.subList(0, 1).forEach(System.out::println);
+      Hashtable<String, String> table = new Hashtable<>();
+      table.put("k", "v");
+      table.values().forEach(System.out::println);
+      table.keySet().forEach(System.out::println);
+      table.entrySet().forEach(System.out::println);
+    }
+
+    private static void successTest() {
+      SortedSet<String> set = new TreeSet<>();
+      set.add("item");
+      Collections.unmodifiableCollection(set).forEach(System.out::println);
+      Collections.unmodifiableSet(set).forEach(System.out::println);
+      Collections.unmodifiableSortedSet(set).forEach(System.out::println);
+      Collections.synchronizedCollection(set).forEach(System.out::println);
+      Collections.synchronizedSet(set).forEach(System.out::println);
+      Collections.synchronizedSortedSet(set).forEach(System.out::println);
+      Collections.checkedCollection(set, String.class).forEach(System.out::println);
+      Collections.checkedSet(set, String.class).forEach(System.out::println);
+      Collections.checkedSortedSet(set, String.class).forEach(System.out::println);
+      SortedMap<String, String> map = new TreeMap<>();
+      map.put("k", "v");
+      Collections.unmodifiableMap(map).forEach((k, v) -> System.out.println(k + " " + v));
+      Collections.unmodifiableSortedMap(map).forEach((k, v) -> System.out.println(k + " " + v));
+      Collections.synchronizedMap(map).forEach((k, v) -> System.out.println(k + " " + v));
+      Collections.synchronizedSortedMap(map).forEach((k, v) -> System.out.println(k + " " + v));
+      Collections.checkedMap(map, String.class, String.class)
+          .forEach((k, v) -> System.out.println(k + " " + v));
+      Collections.checkedSortedMap(map, String.class, String.class)
+          .forEach((k, v) -> System.out.println(k + " " + v));
+    }
+
+    private static void exceptionTest() {
+      try {
+        Collections.unmodifiableCollection(new ImmutableList<>()).spliterator();
+        System.out.println("working");
+      } catch (UnsupportedOperationException e) {
+        System.out.println("exception");
+      }
+      try {
+        Collections.unmodifiableList(new ImmutableList<>()).spliterator();
+        System.out.println("working");
+      } catch (UnsupportedOperationException e) {
+        System.out.println("exception");
+      }
+      try {
+        Collections.synchronizedList(new ImmutableList<>()).spliterator();
+        System.out.println("working");
+      } catch (UnsupportedOperationException e) {
+        System.out.println("exception");
+      }
+      try {
+        Collections.synchronizedList(new ImmutableList<>()).spliterator();
+        System.out.println("working");
+      } catch (UnsupportedOperationException e) {
+        System.out.println("exception");
+      }
+      try {
+        Collections.checkedList(new ImmutableList<>(), Vector.class).spliterator();
+        System.out.println("working");
+      } catch (UnsupportedOperationException e) {
+        System.out.println("exception");
+      }
+      try {
+        Collections.checkedList(new ImmutableList<>(), Vector.class).spliterator();
+        System.out.println("working");
+      } catch (UnsupportedOperationException e) {
+        System.out.println("exception");
+      }
+    }
+
+    static class ImmutableList<T> implements List<T> {
+
+      @Override
+      public int size() {
+        return 0;
+      }
+
+      @Override
+      public boolean isEmpty() {
+        return false;
+      }
+
+      @Override
+      public boolean contains(Object o) {
+        return false;
+      }
+
+      @Override
+      public Iterator<T> iterator() {
+        return null;
+      }
+
+      @Override
+      public Object[] toArray() {
+        return new Object[0];
+      }
+
+      @Override
+      public <T1> T1[] toArray(T1[] a) {
+        return null;
+      }
+
+      @Override
+      public boolean add(T t) {
+        return false;
+      }
+
+      @Override
+      public boolean remove(Object o) {
+        return false;
+      }
+
+      @Override
+      public boolean containsAll(Collection<?> c) {
+        return false;
+      }
+
+      @Override
+      public boolean addAll(Collection<? extends T> c) {
+        return false;
+      }
+
+      @Override
+      public boolean addAll(int index, Collection<? extends T> c) {
+        return false;
+      }
+
+      @Override
+      public boolean removeAll(Collection<?> c) {
+        return false;
+      }
+
+      @Override
+      public boolean retainAll(Collection<?> c) {
+        return false;
+      }
+
+      @Override
+      public void clear() {}
+
+      @Override
+      public T get(int index) {
+        return null;
+      }
+
+      @Override
+      public T set(int index, T element) {
+        return null;
+      }
+
+      @Override
+      public void add(int index, T element) {}
+
+      @Override
+      public T remove(int index) {
+        return null;
+      }
+
+      @Override
+      public int indexOf(Object o) {
+        return 0;
+      }
+
+      @Override
+      public int lastIndexOf(Object o) {
+        return 0;
+      }
+
+      @Override
+      public ListIterator<T> listIterator() {
+        return null;
+      }
+
+      @Override
+      public ListIterator<T> listIterator(int index) {
+        return null;
+      }
+
+      @Override
+      public List<T> subList(int fromIndex, int toIndex) {
+        return null;
+      }
+
+      @Override
+      public Spliterator<T> spliterator() {
+        throw new UnsupportedOperationException();
+      }
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/files/ArchiveWithDexTest.java b/src/test/java/com/android/tools/r8/files/ArchiveWithDexTest.java
index 0c3bd18..85b25f3 100644
--- a/src/test/java/com/android/tools/r8/files/ArchiveWithDexTest.java
+++ b/src/test/java/com/android/tools/r8/files/ArchiveWithDexTest.java
@@ -4,6 +4,8 @@
 package com.android.tools.r8.files;
 
 import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticOrigin;
+import static org.hamcrest.CoreMatchers.allOf;
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
@@ -11,10 +13,15 @@
 
 import com.android.tools.r8.ArchiveProgramResourceProvider;
 import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.DexIndexedConsumer;
+import com.android.tools.r8.R8;
+import com.android.tools.r8.R8Command;
 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.origin.Origin;
+import com.android.tools.r8.origin.PathOrigin;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.ArchiveResourceProvider;
 import com.android.tools.r8.utils.BooleanBox;
@@ -33,6 +40,8 @@
 @RunWith(Parameterized.class)
 public class ArchiveWithDexTest extends TestBase {
 
+  static final String MESSAGE = "containing both DEX and Java-bytecode content";
+
   @Parameter() public TestParameters parameters;
 
   @Parameters(name = "{0}")
@@ -41,6 +50,7 @@
   }
 
   private static Path zipWithDexAndClass;
+  private static Origin zipWithDexAndClassOrigin;
 
   @BeforeClass
   public static void createZipWitDexAndClass() throws IOException {
@@ -51,6 +61,7 @@
     Files.createFile(zipContent.resolve("other.dex"));
     zipWithDexAndClass = getStaticTemp().newFolder().toPath().resolve("input.zip");
     ZipUtils.zip(zipWithDexAndClass, zipContent);
+    zipWithDexAndClassOrigin = new PathOrigin(zipWithDexAndClass);
   }
 
   private void checkOneDexFiles(Path archive) throws Exception {
@@ -85,6 +96,41 @@
   }
 
   @Test
+  public void testFileInputD8() {
+    assertThrows(
+        CompilationFailedException.class,
+        () ->
+            testForD8(Backend.DEX)
+                .addProgramFiles(zipWithDexAndClass)
+                .setMinApi(AndroidApiLevel.B)
+                .compileWithExpectedDiagnostics(
+                    diagnostics ->
+                        diagnostics.assertErrorsMatch(
+                            allOf(
+                                diagnosticMessage(containsString("input.zip")),
+                                diagnosticMessage(containsString(MESSAGE)),
+                                diagnosticOrigin(zipWithDexAndClassOrigin)))));
+  }
+
+  @Test
+  public void testProviderInputD8() {
+    assertThrows(
+        CompilationFailedException.class,
+        () ->
+            testForD8(Backend.DEX)
+                .addProgramResourceProviders(
+                    ArchiveProgramResourceProvider.fromArchive(zipWithDexAndClass))
+                .setMinApi(AndroidApiLevel.B)
+                .compileWithExpectedDiagnostics(
+                    diagnostics ->
+                        diagnostics.assertErrorsMatch(
+                            allOf(
+                                // When using providers the file name is not in the message.
+                                diagnosticMessage(containsString(MESSAGE)),
+                                diagnosticOrigin(zipWithDexAndClassOrigin)))));
+  }
+
+  @Test
   public void testR8() {
     assertThrows(
         CompilationFailedException.class,
@@ -97,10 +143,10 @@
                 .compileWithExpectedDiagnostics(
                     diagnostics ->
                         diagnostics.assertErrorsMatch(
-                            diagnosticMessage(
-                                containsString(
-                                    "Cannot create android app from an archive containing both DEX"
-                                        + " and Java-bytecode content.")))));
+                            allOf(
+                                // When using providers the file name is not in the message.
+                                diagnosticMessage(containsString(MESSAGE)),
+                                diagnosticOrigin(zipWithDexAndClassOrigin)))));
   }
 
   @Test
@@ -130,6 +176,19 @@
   }
 
   @Test
+  public void testRawCommandBuilderR8() throws Exception {
+    // Check that the error is wrapped and reported at the point of compiling (not arg building).
+    R8Command command =
+        R8Command.builder()
+            .addProgramResourceProvider(
+                ArchiveProgramResourceProvider.fromArchive(zipWithDexAndClass))
+            .setMinApiLevel(1)
+            .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
+            .build();
+    assertThrows(CompilationFailedException.class, () -> R8.run(command));
+  }
+
+  @Test
   public void testR8LegacyProgramResourceProviderDontIgnoreDex() {
     assertThrows(
         CompilationFailedException.class,
@@ -142,8 +201,10 @@
                 .compileWithExpectedDiagnostics(
                     diagnostics ->
                         diagnostics.assertErrorsMatch(
-                            diagnosticMessage(
-                                containsString("containing both DEX and Java-bytecode content")))));
+                            allOf(
+                                // When using providers the file name is not in the message.
+                                diagnosticMessage(containsString(MESSAGE)),
+                                diagnosticOrigin(zipWithDexAndClassOrigin)))));
   }
 
   static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/ConditionalRuleOnMemberFieldWithKeepInitTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/ConditionalRuleOnMemberFieldWithKeepInitTest.java
new file mode 100644
index 0000000..68db4f1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/ConditionalRuleOnMemberFieldWithKeepInitTest.java
@@ -0,0 +1,85 @@
+// Copyright (c) 2023, 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.shaking.ifrule;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+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 ConditionalRuleOnMemberFieldWithKeepInitTest extends TestBase {
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withDefaultDexRuntime().build();
+  }
+
+  private TestParameters parameters;
+
+  public ConditionalRuleOnMemberFieldWithKeepInitTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  private void testKeepRule(String keepRule, List<Class<?>> absent, List<Class<?>> present)
+      throws IOException, ExecutionException, CompilationFailedException {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(A.class, TestClass.class)
+        .addKeepRules(keepRule)
+        .addKeepMainRule(TestClass.class)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutputLines("42")
+        .inspect(
+            inspector -> {
+              absent.forEach(clazz -> assertThat(inspector.clazz(clazz), isAbsent()));
+              present.forEach(clazz -> assertThat(inspector.clazz(clazz), isPresent()));
+            });
+  }
+
+  @Test
+  public void testJustStarConditionalKeepClassMembers() throws Exception {
+    String keepRule = "-if class * -keepclasseswithmembers class <1> { <init>(); }";
+    testKeepRule(keepRule, ImmutableList.of(), ImmutableList.of(TestClass.class, A.class));
+  }
+
+  @Test
+  public void testJustStarConditionalKeepClass() throws Exception {
+    String keepRule = "-if class * -keep class <1> { <init>(); }";
+    testKeepRule(keepRule, ImmutableList.of(), ImmutableList.of(TestClass.class, A.class));
+  }
+
+  @Test
+  public void testStarWithMethodConditionalKeepClassMembers() throws Exception {
+    String keepRule = "-if class * { int z; } -keepclasseswithmembers class <1> { <init>(); }";
+    testKeepRule(keepRule, ImmutableList.of(), ImmutableList.of(TestClass.class, A.class));
+  }
+
+  @Test
+  public void testStarWithMethodConditionalKeepClass() throws Exception {
+    String keepRule = "-if class * { int z; } -keep class <1> { <init>(); }";
+    testKeepRule(keepRule, ImmutableList.of(), ImmutableList.of(TestClass.class, A.class));
+  }
+
+  static class A {
+    public static int z = 42;
+  }
+
+  static class TestClass {
+    public static void main(String[] args) {
+      System.out.println(A.z);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/ConditionalRuleOnMemberWithKeepInitSimpleInlineTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/ConditionalRuleOnMemberWithKeepInitSimpleInlineTest.java
new file mode 100644
index 0000000..f2300e3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/ConditionalRuleOnMemberWithKeepInitSimpleInlineTest.java
@@ -0,0 +1,87 @@
+// Copyright (c) 2023, 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.shaking.ifrule;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+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 ConditionalRuleOnMemberWithKeepInitSimpleInlineTest extends TestBase {
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withDefaultDexRuntime().build();
+  }
+
+  private TestParameters parameters;
+
+  public ConditionalRuleOnMemberWithKeepInitSimpleInlineTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  private void testKeepRule(String keepRule, List<Class<?>> absent, List<Class<?>> present)
+      throws IOException, ExecutionException, CompilationFailedException {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(A.class, TestClass.class)
+        .addKeepRules(keepRule)
+        .addKeepMainRule(TestClass.class)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutputLines("bar")
+        .inspect(
+            inspector -> {
+              absent.forEach(clazz -> assertThat(inspector.clazz(clazz), isAbsent()));
+              present.forEach(clazz -> assertThat(inspector.clazz(clazz), isPresent()));
+            });
+  }
+
+  @Test
+  public void testJustStarConditionalKeepClassMembers() throws Exception {
+    String keepRule = "-if class * -keepclasseswithmembers class <1> { <init>(); }";
+    testKeepRule(keepRule, ImmutableList.of(), ImmutableList.of(TestClass.class, A.class));
+  }
+
+  @Test
+  public void testJustStarConditionalKeepClass() throws Exception {
+    String keepRule = "-if class * -keep class <1> { <init>(); }";
+    testKeepRule(keepRule, ImmutableList.of(), ImmutableList.of(TestClass.class, A.class));
+  }
+
+  @Test
+  public void testStarWithMethodConditionalKeepClassMembers() throws Exception {
+    String keepRule = "-if class * { bar(); } -keepclasseswithmembers class <1> { <init>(); }";
+    testKeepRule(keepRule, ImmutableList.of(), ImmutableList.of(TestClass.class, A.class));
+  }
+
+  @Test
+  public void testStarWithMethodConditionalKeepClass() throws Exception {
+    String keepRule = "-if class * { bar(); } -keep class <1> { <init>(); }";
+    testKeepRule(keepRule, ImmutableList.of(), ImmutableList.of(TestClass.class, A.class));
+  }
+
+  static class A {
+    public static String bar() {
+      return "bar";
+    }
+  }
+
+  static class TestClass {
+    public static void main(String[] args) {
+      System.out.println(A.bar());
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/ConditionalRuleOnMemberWithKeepInitTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/ConditionalRuleOnMemberWithKeepInitTest.java
new file mode 100644
index 0000000..38ff7d5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/ConditionalRuleOnMemberWithKeepInitTest.java
@@ -0,0 +1,89 @@
+// Copyright (c) 2023, 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.shaking.ifrule;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+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 ConditionalRuleOnMemberWithKeepInitTest extends TestBase {
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withDefaultDexRuntime().build();
+  }
+
+  private TestParameters parameters;
+
+  public ConditionalRuleOnMemberWithKeepInitTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  private void testKeepRule(String keepRule, List<Class<?>> absent, List<Class<?>> present)
+      throws IOException, ExecutionException, CompilationFailedException {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(A.class, TestClass.class)
+        .addKeepRules(keepRule)
+        .addKeepMainRule(TestClass.class)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutputLines("bar")
+        .inspect(
+            inspector -> {
+              absent.forEach(clazz -> assertThat(inspector.clazz(clazz), isAbsent()));
+              present.forEach(clazz -> assertThat(inspector.clazz(clazz), isPresent()));
+            });
+  }
+
+  @Test
+  public void testJustStarConditionalKeepClassMembers() throws Exception {
+    String keepRule = "-if class * -keepclasseswithmembers class <1> { <init>(); }";
+    // TODO(b/316100042) We should keep A here
+    testKeepRule(keepRule, ImmutableList.of(A.class), ImmutableList.of(TestClass.class));
+  }
+
+  @Test
+  public void testJustStarConditionalKeepClass() throws Exception {
+    String keepRule = "-if class * -keep class <1> { <init>(); }";
+    // TODO(b/316100042) We should keep A here
+    testKeepRule(keepRule, ImmutableList.of(A.class), ImmutableList.of(TestClass.class));
+  }
+
+  @Test
+  public void testStarWithMethodConditionalKeepClassMembers() throws Exception {
+    String keepRule = "-if class * { bar(); } -keepclasseswithmembers class <1> { <init>(); }";
+    testKeepRule(keepRule, ImmutableList.of(), ImmutableList.of(TestClass.class, A.class));
+  }
+
+  @Test
+  public void testStarWithMethodConditionalKeepClass() throws Exception {
+    String keepRule = "-if class * { bar(); } -keep class <1> { <init>(); }";
+    testKeepRule(keepRule, ImmutableList.of(), ImmutableList.of(TestClass.class, A.class));
+  }
+
+  static class A {
+    public static void bar() {
+      System.out.println("bar");
+    }
+  }
+
+  static class TestClass {
+    public static void main(String[] args) {
+      A.bar();
+    }
+  }
+}
diff --git a/third_party/openjdk/desugar_jdk_libs_11.tar.gz.sha1 b/third_party/openjdk/desugar_jdk_libs_11.tar.gz.sha1
index 758e8f0..c5ef954 100644
--- a/third_party/openjdk/desugar_jdk_libs_11.tar.gz.sha1
+++ b/third_party/openjdk/desugar_jdk_libs_11.tar.gz.sha1
@@ -1 +1 @@
-47cfa6d72be316cdaff6e5042111a18215ac4405
\ No newline at end of file
+d50abf6c508c1332eb80f35f5631e99e2e8329b5
\ No newline at end of file