Merge commit '2c35b3a531fc115c044e04fa036855835ad33a9a' into dev-release
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs.json b/src/library_desugar/jdk11/desugar_jdk_libs.json
index 609f920..5adf5d1 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs.json
@@ -1,5 +1,5 @@
 {
-  "identifier": "com.tools.android:desugar_jdk_libs_configuration:2.0.0",
+  "identifier": "com.tools.android:desugar_jdk_libs_configuration:2.0.2",
   "configuration_format_version": 100,
   "required_compilation_api_level": 30,
   "synthesized_library_classes_package_prefix": "j$.",
@@ -38,7 +38,26 @@
       "api_level_below_or_equal": 29,
       "rewrite_prefix": {
         "java.util.concurrent.Flow": "j$.util.concurrent.Flow"
-      }
+      },
+      "wrapper_conversion": [
+        "java.util.concurrent.Flow$Publisher",
+        "java.util.concurrent.Flow$Subscriber",
+        "java.util.concurrent.Flow$Subscription"
+      ]
+    },
+    {
+      "api_level_below_or_equal": 32,
+      "api_level_greater_or_equal": 24,
+      "emulate_interface": {
+        "java.util.Collection": "j$.util.Collection"
+      },
+      "dont_rewrite": [
+        "boolean java.util.Collection#removeIf(java.util.function.Predicate)",
+        "java.util.Spliterator java.util.Collection#spliterator()",
+        "java.util.stream.Stream java.util.Collection#parallelStream()",
+        "java.util.stream.Stream java.util.Collection#stream()",
+        "void java.util.Collection#forEach(java.util.function.Consumer)"
+      ]
     },
     {
       "api_level_below_or_equal": 23,
@@ -142,6 +161,12 @@
   ],
   "program_flags": [
     {
+      "api_level_below_or_equal": 33,
+      "amend_library_method": [
+        "public java.lang.Object[] java.util.Collection#toArray(java.util.function.IntFunction)"
+      ]
+    },
+    {
       "api_level_below_or_equal": 32,
       "api_level_greater_or_equal": 26,
       "covariant_retarget_method": {
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_legacy.json b/src/library_desugar/jdk11/desugar_jdk_libs_legacy.json
index 7d17a1d..c29c213 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs_legacy.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs_legacy.json
@@ -2,7 +2,7 @@
   "configuration_format_version": 5,
   "group_id" : "com.tools.android",
   "artifact_id" : "desugar_jdk_libs",
-  "version": "1.2.2",
+  "version": "1.2.3",
   "required_compilation_api_level": 30,
   "synthesized_library_classes_package_prefix": "j$.",
   "support_all_callbacks_from_library": true,
@@ -37,7 +37,12 @@
       "api_level_below_or_equal": 29,
       "rewrite_prefix": {
         "java.util.concurrent.Flow": "j$.util.concurrent.Flow"
-      }
+      },
+      "wrapper_conversion": [
+        "java.util.concurrent.Flow$Publisher",
+        "java.util.concurrent.Flow$Subscriber",
+        "java.util.concurrent.Flow$Subscription"
+      ]
     },
     {
       "api_level_below_or_equal": 23,
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_minimal.json b/src/library_desugar/jdk11/desugar_jdk_libs_minimal.json
index d13b947..15939bc 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs_minimal.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs_minimal.json
@@ -1,5 +1,5 @@
 {
-  "identifier": "com.tools.android:desugar_jdk_libs_configuration_minimal:2.0.0",
+  "identifier": "com.tools.android:desugar_jdk_libs_configuration_minimal:2.0.2",
   "configuration_format_version": 100,
   "required_compilation_api_level": 24,
   "synthesized_library_classes_package_prefix": "j$.",
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_nio.json b/src/library_desugar/jdk11/desugar_jdk_libs_nio.json
index e2f74e8..8d31143 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs_nio.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs_nio.json
@@ -1,5 +1,5 @@
 {
-  "identifier": "com.tools.android:desugar_jdk_libs_configuration_nio:2.0.0",
+  "identifier": "com.tools.android:desugar_jdk_libs_configuration_nio:2.0.2",
   "configuration_format_version": 100,
   "required_compilation_api_level": 30,
   "synthesized_library_classes_package_prefix": "j$.",
@@ -52,7 +52,26 @@
       "api_level_below_or_equal": 29,
       "rewrite_prefix": {
         "java.util.concurrent.Flow": "j$.util.concurrent.Flow"
-      }
+      },
+      "wrapper_conversion": [
+        "java.util.concurrent.Flow$Publisher",
+        "java.util.concurrent.Flow$Subscriber",
+        "java.util.concurrent.Flow$Subscription"
+      ]
+    },
+    {
+      "api_level_below_or_equal": 32,
+      "api_level_greater_or_equal": 24,
+      "emulate_interface": {
+        "java.util.Collection": "j$.util.Collection"
+      },
+      "dont_rewrite": [
+        "boolean java.util.Collection#removeIf(java.util.function.Predicate)",
+        "java.util.Spliterator java.util.Collection#spliterator()",
+        "java.util.stream.Stream java.util.Collection#parallelStream()",
+        "java.util.stream.Stream java.util.Collection#stream()",
+        "void java.util.Collection#forEach(java.util.function.Consumer)"
+      ]
     },
     {
       "api_level_below_or_equal": 25,
@@ -301,6 +320,12 @@
   ],
   "program_flags": [
     {
+      "api_level_below_or_equal": 33,
+      "amend_library_method": [
+        "public java.lang.Object[] java.util.Collection#toArray(java.util.function.IntFunction)"
+      ]
+    },
+    {
       "api_level_below_or_equal": 32,
       "api_level_greater_or_equal": 26,
       "covariant_retarget_method": {
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index d57d450..6ef6c416 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -370,6 +370,10 @@
         assert appView.rootSet().verifyKeptItemsAreKept(appView);
         appView.rootSet().checkAllRulesAreUsed(options);
 
+        if (options.apiModelingOptions().reportUnknownApiReferences) {
+          appView.apiLevelCompute().reportUnknownApiReferences();
+        }
+
         if (options.proguardSeedsConsumer != null) {
           ByteArrayOutputStream bytes = new ByteArrayOutputStream();
           PrintStream out = new PrintStream(bytes);
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiDiagnostic.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiDiagnostic.java
new file mode 100644
index 0000000..98471f2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiDiagnostic.java
@@ -0,0 +1,10 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.androidapi;
+
+import com.android.tools.r8.Diagnostic;
+
+/** Base class for api related diagnostics. */
+public abstract class AndroidApiDiagnostic implements Diagnostic {}
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelCompute.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelCompute.java
index 1c61851..5a0bd81 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelCompute.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelCompute.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.androidapi;
 
+import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.androidapi.ComputedApiLevel.KnownApiLevel;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexItemFactory;
@@ -44,6 +45,10 @@
 
   public abstract boolean isEnabled();
 
+  public void reportUnknownApiReferences() {
+    // Do nothing here.
+  }
+
   public ComputedApiLevel computeApiLevelForDefinition(
       DexMember<?, ?> reference, DexItemFactory factory, ComputedApiLevel unknownValue) {
     return computeApiLevelForDefinition(reference.getReferencedBaseTypes(factory), unknownValue);
@@ -105,10 +110,12 @@
 
     private final AndroidApiReferenceLevelCache cache;
     private final ComputedApiLevel minApiLevel;
+    private final DiagnosticsHandler diagnosticsHandler;
 
     public DefaultAndroidApiLevelCompute(AppView<?> appView) {
       this.cache = AndroidApiReferenceLevelCache.create(appView, this);
       this.minApiLevel = of(appView.options().getMinApiLevel());
+      this.diagnosticsHandler = appView.reporter();
     }
 
     @Override
@@ -131,5 +138,14 @@
         DexReference reference, ComputedApiLevel unknownValue) {
       return cache.lookup(reference, unknownValue);
     }
+
+    @Override
+    public void reportUnknownApiReferences() {
+      cache
+          .getUnknownReferencesToReport()
+          .forEach(
+              reference ->
+                  diagnosticsHandler.warning(new AndroidApiUnknownReferenceDiagnostic(reference)));
+    }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
index c4d2d5a..0e267e5 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
@@ -12,7 +12,9 @@
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.ConsumerUtils;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Sets;
 import java.util.List;
+import java.util.Set;
 import java.util.function.BiConsumer;
 
 public class AndroidApiReferenceLevelCache {
@@ -22,6 +24,10 @@
   private final AppView<?> appView;
   private final DexItemFactory factory;
 
+  // Collection of unknown references attempteed to be looked up.
+  private final Set<DexReference> unknownReferencesToReport = Sets.newConcurrentHashSet();
+  private final boolean reportUnknownReferences;
+
   private AndroidApiReferenceLevelCache(
       AppView<?> appView,
       AndroidApiLevelCompute apiLevelCompute,
@@ -32,6 +38,7 @@
     androidApiLevelDatabase =
         new AndroidApiLevelHashingDatabaseImpl(
             predefinedApiTypeLookupForHashing, appView.options(), appView.reporter());
+    reportUnknownReferences = appView.options().apiModelingOptions().reportUnknownApiReferences;
   }
 
   public static AndroidApiReferenceLevelCache create(
@@ -49,6 +56,10 @@
     return new AndroidApiReferenceLevelCache(appView, apiLevelCompute, builder.build());
   }
 
+  public Set<DexReference> getUnknownReferencesToReport() {
+    return unknownReferencesToReport;
+  }
+
   public ComputedApiLevel lookupMax(
       DexReference reference, ComputedApiLevel minApiLevel, ComputedApiLevel unknownValue) {
     assert !minApiLevel.isNotSetApiLevel();
@@ -98,8 +109,12 @@
             androidApiLevelDatabase::getTypeApiLevel,
             androidApiLevelDatabase::getFieldApiLevel,
             androidApiLevelDatabase::getMethodApiLevel);
-    return (foundApiLevel == null)
-        ? unknownValue
-        : apiLevelCompute.of(foundApiLevel.max(appView.options().getMinApiLevel()));
+    if (foundApiLevel == null) {
+      if (reportUnknownReferences) {
+        unknownReferencesToReport.add(reference);
+      }
+      return unknownValue;
+    }
+    return apiLevelCompute.of(foundApiLevel.max(appView.options().getMinApiLevel()));
   }
 }
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiUnknownReferenceDiagnostic.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiUnknownReferenceDiagnostic.java
new file mode 100644
index 0000000..fba87df
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiUnknownReferenceDiagnostic.java
@@ -0,0 +1,35 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.androidapi;
+
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+
+@Keep
+public class AndroidApiUnknownReferenceDiagnostic extends AndroidApiDiagnostic {
+
+  private final DexReference reference;
+
+  AndroidApiUnknownReferenceDiagnostic(DexReference reference) {
+    this.reference = reference;
+  }
+
+  @Override
+  public Origin getOrigin() {
+    return Origin.unknown();
+  }
+
+  @Override
+  public Position getPosition() {
+    return Position.UNKNOWN;
+  }
+
+  @Override
+  public String getDiagnosticMessage() {
+    return reference.toSourceString() + " cannot be found in the api database.";
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/errors/ProguardKeepRuleDiagnostic.java b/src/main/java/com/android/tools/r8/errors/ProguardKeepRuleDiagnostic.java
new file mode 100644
index 0000000..dcb6bb9
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/errors/ProguardKeepRuleDiagnostic.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.errors;
+
+import com.android.tools.r8.Diagnostic;
+
+/** Base class for diagnostics related to proguard keep rules. */
+public abstract class ProguardKeepRuleDiagnostic implements Diagnostic {}
diff --git a/src/main/java/com/android/tools/r8/errors/UnusedProguardKeepRuleDiagnostic.java b/src/main/java/com/android/tools/r8/errors/UnusedProguardKeepRuleDiagnostic.java
new file mode 100644
index 0000000..396f88b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/errors/UnusedProguardKeepRuleDiagnostic.java
@@ -0,0 +1,34 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.errors;
+
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+import com.android.tools.r8.shaking.ProguardConfigurationRule;
+
+@Keep
+public class UnusedProguardKeepRuleDiagnostic extends ProguardKeepRuleDiagnostic {
+
+  private final ProguardConfigurationRule rule;
+
+  public UnusedProguardKeepRuleDiagnostic(ProguardConfigurationRule rule) {
+    this.rule = rule;
+  }
+
+  @Override
+  public Origin getOrigin() {
+    return rule.getOrigin();
+  }
+
+  @Override
+  public Position getPosition() {
+    return rule.getPosition();
+  }
+
+  @Override
+  public String getDiagnosticMessage() {
+    return "Proguard configuration rule does not match anything: `" + rule + "`";
+  }
+}
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 ab044e3..751b635 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
@@ -359,10 +359,7 @@
     workaroundAbstractMethodOnNonAbstractClassVerificationBug(executor);
     DexApplication application = appView.appInfo().app();
     D8MethodProcessor methodProcessor = new D8MethodProcessor(this, executor);
-    InterfaceProcessor interfaceProcessor =
-        appView.options().isInterfaceMethodDesugaringEnabled()
-            ? new InterfaceProcessor(appView)
-            : null;
+    InterfaceProcessor interfaceProcessor = InterfaceProcessor.create(appView);
 
     timing.begin("IR conversion");
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
index aa8ca78..a42d6e5 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
@@ -93,17 +93,15 @@
     if (appView.options().enableTryWithResourcesDesugaring()) {
       desugarings.add(new TwrInstructionDesugaring(appView));
     }
-    if (appView.options().isInterfaceMethodDesugaringEnabled()) {
-      interfaceMethodRewriter =
-          new InterfaceMethodRewriter(
-              appView,
-              SetUtils.newImmutableSetExcludingNullItems(
-                  alwaysThrowingInstructionDesugaring,
-                  backportedMethodRewriter,
-                  desugaredLibraryRetargeter));
+    interfaceMethodRewriter =
+        InterfaceMethodRewriter.create(
+            appView,
+            SetUtils.newImmutableSetExcludingNullItems(
+                alwaysThrowingInstructionDesugaring,
+                backportedMethodRewriter,
+                desugaredLibraryRetargeter));
+    if (interfaceMethodRewriter != null) {
       desugarings.add(interfaceMethodRewriter);
-    } else {
-      interfaceMethodRewriter = null;
     }
     desugaredLibraryAPIConverter =
         appView.typeRewriter.isRewriting()
@@ -121,7 +119,9 @@
     desugarings.add(new LambdaInstructionDesugaring(appView));
     desugarings.add(new ConstantDynamicInstructionDesugaring(appView));
     desugarings.add(new InvokeSpecialToSelfDesugaring(appView));
-    if (appView.options().rewriteInvokeToPrivateInDesugar) {
+    if (appView.options().isGeneratingClassFiles()) {
+      // Nest desugaring has to be enabled to avoid other invokevirtual to private methods.
+      assert nestBasedAccessDesugaring != null;
       desugarings.add(new InvokeToPrivateRewriter());
     }
     desugarings.add(new StringConcatInstructionDesugaring(appView));
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/EmulatedInterfaceDescriptor.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/EmulatedInterfaceDescriptor.java
index fb96fe6..fee2962 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/EmulatedInterfaceDescriptor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/EmulatedInterfaceDescriptor.java
@@ -9,6 +9,13 @@
 import java.util.Map;
 import java.util.Objects;
 
+/**
+ * An EmulatedInterfaceDescriptor describes how emulated interfaces are desugared. The
+ * emulatedMethods encode the emulated dispatch logic for default methods. Note that there is an
+ * implicit decision here: If interface method desugaring is enabled (< 24), the static methods in
+ * the emulated interface are going to be desugared, else they are left in place. This means static
+ * methods need to be manually retargeted when interface method desugaring is not enabled.
+ */
 public class EmulatedInterfaceDescriptor implements SpecificationDescriptor {
   private final DexType rewrittenType;
   private final Map<DexMethod, EmulatedDispatchMethodDescriptor> emulatedMethods;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LibraryValidator.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LibraryValidator.java
index 567cc79..c6ea5b0 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LibraryValidator.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LibraryValidator.java
@@ -28,6 +28,8 @@
       levelType = app.dexItemFactory.createType("Ljava/util/concurrent/Flow;");
     } else if (requiredCompilationAPILevel.isEqualTo(AndroidApiLevel.N)) {
       levelType = app.dexItemFactory.createType("Ljava/util/function/Supplier;");
+    } else if (requiredCompilationAPILevel.isEqualTo(AndroidApiLevel.T)) {
+      levelType = app.dexItemFactory.createType("Ljava/lang/invoke/VarHandle;");
     } else {
       app.options.reporter.warning(
           "Unsupported requiredCompilationAPILevel: " + requiredCompilationAPILevel);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
index 16821510..eb710e9 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
@@ -4,6 +4,8 @@
 
 package com.android.tools.r8.ir.desugar.itf;
 
+import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.InterfaceMethodDesugaringMode.EMULATED_INTERFACE_ONLY;
+
 import com.android.tools.r8.cf.code.CfInvoke;
 import com.android.tools.r8.cf.code.CfNew;
 import com.android.tools.r8.cf.code.CfStackInstruction;
@@ -29,6 +31,7 @@
 import com.android.tools.r8.graph.MethodResolutionResult;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.DerivedMethod;
+import com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.InterfaceMethodDesugaringMode;
 import com.android.tools.r8.position.MethodPosition;
 import com.android.tools.r8.utils.BooleanBox;
 import com.android.tools.r8.utils.BooleanUtils;
@@ -374,7 +377,12 @@
   private final Map<DexProgramClass, List<ClassTypeSignature>> newExtraInterfaceSignatures =
       new ConcurrentHashMap<>();
 
-  ClassProcessor(AppView<?> appView, Predicate<ProgramMethod> isLiveMethod) {
+  private final InterfaceMethodDesugaringMode desugaringMode;
+
+  ClassProcessor(
+      AppView<?> appView,
+      Predicate<ProgramMethod> isLiveMethod,
+      InterfaceMethodDesugaringMode desugaringMode) {
     this.appView = appView;
     this.dexItemFactory = appView.dexItemFactory();
     this.helper = new InterfaceDesugaringSyntheticHelper(appView);
@@ -382,6 +390,7 @@
         !appView.options().canUseDefaultAndStaticInterfaceMethods()
             && !appView.options().machineDesugaredLibrarySpecification.isEmpty();
     this.isLiveMethod = isLiveMethod;
+    this.desugaringMode = desugaringMode;
   }
 
   private boolean isLiveMethod(DexClassAndMethod method) {
@@ -440,6 +449,9 @@
     assert iface.isInterface();
     assert iface.superType == dexItemFactory.objectType;
     assert !helper.isEmulatedInterface(iface.type);
+    if (desugaringMode == EMULATED_INTERFACE_ONLY) {
+      return SignaturesInfo.EMPTY;
+    }
     // Add non-library default methods as well as those for desugared library classes.
     if (!iface.isLibraryClass() || (needsLibraryInfo() && helper.isInDesugaredLibrary(iface))) {
       MethodSignatures signatures = getDefaultMethods(iface);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java
index eda530c..4ac2b73 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java
@@ -72,6 +72,23 @@
     this.shouldIgnoreFromReportsPredicate = getShouldIgnoreFromReportsPredicate(appView);
   }
 
+  public enum InterfaceMethodDesugaringMode {
+    ALL,
+    EMULATED_INTERFACE_ONLY,
+    NONE
+  }
+
+  public static InterfaceMethodDesugaringMode getInterfaceMethodDesugaringMode(
+      InternalOptions options) {
+    if (options.isInterfaceMethodDesugaringEnabled()) {
+      return InterfaceMethodDesugaringMode.ALL;
+    }
+    if (options.machineDesugaredLibrarySpecification.getEmulatedInterfaces().isEmpty()) {
+      return InterfaceMethodDesugaringMode.NONE;
+    }
+    return InterfaceMethodDesugaringMode.EMULATED_INTERFACE_ONLY;
+  }
+
   boolean isEmulatedInterface(DexType itf) {
     return appView
         .options()
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodProcessorFacade.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodProcessorFacade.java
index 434238a..a5ec16a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodProcessorFacade.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodProcessorFacade.java
@@ -9,6 +9,7 @@
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaring;
 import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.InterfaceMethodDesugaringMode;
 import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter.Flavor;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.google.common.collect.Iterables;
@@ -28,11 +29,13 @@
       AppView<?> appView,
       Flavor flavour,
       Predicate<ProgramMethod> isLiveMethod,
-      InterfaceProcessor interfaceProcessor) {
+      InterfaceProcessor interfaceProcessor,
+      InterfaceMethodDesugaringMode desugaringMode) {
     this.appView = appView;
     this.flavour = flavour;
+    assert interfaceProcessor != null;
     this.interfaceProcessor = interfaceProcessor;
-    this.classProcessor = new ClassProcessor(appView, isLiveMethod);
+    this.classProcessor = new ClassProcessor(appView, isLiveMethod, desugaringMode);
   }
 
   private boolean shouldProcess(DexProgramClass clazz, Flavor flavour) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
index 955183a..e91925d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
@@ -4,6 +4,11 @@
 
 package com.android.tools.r8.ir.desugar.itf;
 
+import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.InterfaceMethodDesugaringMode.ALL;
+import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.InterfaceMethodDesugaringMode.EMULATED_INTERFACE_ONLY;
+import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.InterfaceMethodDesugaringMode.NONE;
+import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.getInterfaceMethodDesugaringMode;
+
 import com.android.tools.r8.DesugarGraphConsumer;
 import com.android.tools.r8.cf.CfVersion;
 import com.android.tools.r8.cf.code.CfInstruction;
@@ -38,6 +43,7 @@
 import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.DerivedMethod;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
 import com.android.tools.r8.ir.desugar.icce.AlwaysThrowingInstructionDesugaring;
+import com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.InterfaceMethodDesugaringMode;
 import com.android.tools.r8.ir.desugar.lambda.LambdaInstructionDesugaring;
 import com.android.tools.r8.ir.desugar.stringconcat.StringConcatInstructionDesugaring;
 import com.android.tools.r8.ir.synthetic.ForwardMethodBuilder;
@@ -87,6 +93,9 @@
   private final AppView<?> appView;
   private final InternalOptions options;
   final DexItemFactory factory;
+  // If this is true, this will desugar only default methods from emulated interfaces present in
+  // the emulated interface descriptor.
+  private final InterfaceMethodDesugaringMode desugaringMode;
   private final InterfaceDesugaringSyntheticHelper helper;
   // The emulatedMethod set is there to avoid doing the emulated look-up too often.
   private final Set<DexString> emulatedMethods = Sets.newIdentityHashSet();
@@ -108,12 +117,28 @@
     ExcludeDexResources
   }
 
-  public InterfaceMethodRewriter(
+  public static InterfaceMethodRewriter create(
       AppView<?> appView, Set<CfInstructionDesugaring> precedingDesugarings) {
+    InterfaceMethodDesugaringMode desugaringMode =
+        getInterfaceMethodDesugaringMode(appView.options());
+    if (desugaringMode == NONE) {
+      return null;
+    }
+    return new InterfaceMethodRewriter(appView, precedingDesugarings, desugaringMode);
+  }
+
+  public InterfaceMethodRewriter(
+      AppView<?> appView,
+      Set<CfInstructionDesugaring> precedingDesugarings,
+      InterfaceMethodDesugaringMode desugaringMode) {
     this.appView = appView;
     this.precedingDesugarings = precedingDesugarings;
     this.options = appView.options();
     this.factory = appView.dexItemFactory();
+    assert desugaringMode == EMULATED_INTERFACE_ONLY || desugaringMode == ALL;
+    this.desugaringMode = desugaringMode;
+    assert desugaringMode == EMULATED_INTERFACE_ONLY
+        || appView.options().isInterfaceMethodDesugaringEnabled();
     this.helper = new InterfaceDesugaringSyntheticHelper(appView);
     initializeEmulatedInterfaceVariables();
   }
@@ -274,15 +299,18 @@
           .build();
     }
     // Continue with invoke type logic.
+    if (invoke.isInvokeVirtual() || invoke.isInvokeInterface()) {
+      return computeInvokeVirtualDispatch(holder, invoke, context);
+    }
+    if (desugaringMode == EMULATED_INTERFACE_ONLY) {
+      return DesugarDescription.nothing();
+    }
     if (invoke.isInvokeStatic()) {
       return computeInvokeStatic(holder, invoke, context);
     }
     if (invoke.isInvokeSpecial()) {
       return computeInvokeSpecial(holder, invoke, context);
     }
-    if (invoke.isInvokeVirtual() || invoke.isInvokeInterface()) {
-      return computeInvokeVirtualDispatch(holder, invoke, context);
-    }
     return DesugarDescription.nothing();
   }
 
@@ -839,14 +867,16 @@
 
   public InterfaceMethodProcessorFacade getPostProcessingDesugaringD8(
       Flavor flavour, InterfaceProcessor interfaceProcessor) {
-    return new InterfaceMethodProcessorFacade(appView, flavour, m -> true, interfaceProcessor);
+    return new InterfaceMethodProcessorFacade(
+        appView, flavour, m -> true, interfaceProcessor, desugaringMode);
   }
 
   public InterfaceMethodProcessorFacade getPostProcessingDesugaringR8(
       Flavor flavour,
       Predicate<ProgramMethod> isLiveMethod,
       InterfaceProcessor interfaceProcessor) {
-    return new InterfaceMethodProcessorFacade(appView, flavour, isLiveMethod, interfaceProcessor);
+    return new InterfaceMethodProcessorFacade(
+        appView, flavour, isLiveMethod, interfaceProcessor, desugaringMode);
   }
 
   private Origin getMethodOrigin(DexMethod method) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
index 59dcd83..8d2c8c6 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
@@ -4,6 +4,10 @@
 
 package com.android.tools.r8.ir.desugar.itf;
 
+import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.InterfaceMethodDesugaringMode.EMULATED_INTERFACE_ONLY;
+import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.InterfaceMethodDesugaringMode.NONE;
+import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.getInterfaceMethodDesugaringMode;
+
 import com.android.tools.r8.cf.code.CfInstruction;
 import com.android.tools.r8.cf.code.CfInvoke;
 import com.android.tools.r8.dex.code.DexInstruction;
@@ -25,6 +29,8 @@
 import com.android.tools.r8.graph.MethodCollection;
 import com.android.tools.r8.graph.NestedGraphLens;
 import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.EmulatedDispatchMethodDescriptor;
+import com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.InterfaceMethodDesugaringMode;
 import com.android.tools.r8.synthesis.SyntheticMethodBuilder;
 import com.android.tools.r8.utils.Pair;
 import com.android.tools.r8.utils.collections.BidirectionalManyToManyRepresentativeMap;
@@ -56,10 +62,21 @@
   private final InterfaceDesugaringSyntheticHelper helper;
   private final Map<DexProgramClass, PostProcessingInterfaceInfo> postProcessingInterfaceInfos =
       new ConcurrentHashMap<>();
+  private final InterfaceMethodDesugaringMode desugaringMode;
 
-  public InterfaceProcessor(AppView<?> appView) {
+  public static InterfaceProcessor create(AppView<?> appView) {
+    InterfaceMethodDesugaringMode desugaringMode =
+        getInterfaceMethodDesugaringMode(appView.options());
+    if (desugaringMode == NONE) {
+      return null;
+    }
+    return new InterfaceProcessor(appView, desugaringMode);
+  }
+
+  public InterfaceProcessor(AppView<?> appView, InterfaceMethodDesugaringMode desugaringMode) {
     this.appView = appView;
     helper = new InterfaceDesugaringSyntheticHelper(appView);
+    this.desugaringMode = desugaringMode;
   }
 
   public InterfaceDesugaringSyntheticHelper getHelper() {
@@ -72,6 +89,10 @@
     if (!method.getHolder().isInterface()) {
       return;
     }
+    if (desugaringMode == EMULATED_INTERFACE_ONLY) {
+      processEmulatedInterfaceOnly(method);
+      return;
+    }
     if (method.getDefinition().belongsToDirectPool()) {
       processDirectInterfaceMethod(method, eventConsumer);
     } else {
@@ -83,6 +104,25 @@
     }
   }
 
+  private void processEmulatedInterfaceOnly(ProgramMethod method) {
+    if (!appView.options().isDesugaredLibraryCompilation()) {
+      return;
+    }
+    if (method.getDefinition().belongsToDirectPool()) {
+      return;
+    }
+    if (helper.isEmulatedInterface(method.getHolderType())) {
+      EmulatedDispatchMethodDescriptor emulatedDispatchDescriptor =
+          helper.getEmulatedDispatchDescriptor(method.getHolder(), method);
+      if (emulatedDispatchDescriptor != null) {
+        processVirtualInterfaceMethod(method);
+        if (!interfaceMethodRemovalChangesApi(method)) {
+          getPostProcessingInterfaceInfo(method.getHolder()).setHasBridgesToRemove();
+        }
+      }
+    }
+  }
+
   static ProgramMethod ensureCompanionMethod(
       DexProgramClass iface,
       DexString methodName,
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
index c19aa5a..b9bc6bc 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
@@ -90,7 +90,7 @@
     }
 
     @Override
-    ProguardMap.Builder setCurrentMapVersion(MapVersionMappingInformation mapVersion) {
+    public ProguardMap.Builder setCurrentMapVersion(MapVersionMappingInformation mapVersion) {
       mapVersions.add(mapVersion);
       return this;
     }
diff --git a/src/main/java/com/android/tools/r8/naming/ComposingBuilder.java b/src/main/java/com/android/tools/r8/naming/ComposingBuilder.java
index 0b872f0..5b3e838 100644
--- a/src/main/java/com/android/tools/r8/naming/ComposingBuilder.java
+++ b/src/main/java/com/android/tools/r8/naming/ComposingBuilder.java
@@ -25,6 +25,7 @@
 import com.android.tools.r8.utils.Box;
 import com.android.tools.r8.utils.ChainableStringConsumer;
 import com.android.tools.r8.utils.ConsumerUtils;
+import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.SegmentTree;
 import com.android.tools.r8.utils.ThrowingBiFunction;
@@ -43,9 +44,9 @@
 import java.util.Map.Entry;
 import java.util.Objects;
 import java.util.Set;
+import java.util.TreeMap;
 import java.util.function.BiConsumer;
 import java.util.function.Consumer;
-import java.util.function.Function;
 
 public class ComposingBuilder {
 
@@ -72,6 +73,11 @@
   private final ComposingData committed = new ComposingData();
 
   private ComposingData current;
+  private final InternalOptions options;
+
+  public ComposingBuilder(InternalOptions options) {
+    this.options = options;
+  }
 
   public void compose(ClassNameMapper classNameMapper) throws MappingComposeException {
     current = new ComposingData();
@@ -105,7 +111,7 @@
     String originalName = classMapping.originalName;
     String renamedName = classMapping.renamedName;
     ComposingClassBuilder composingClassBuilder =
-        new ComposingClassBuilder(originalName, renamedName, committed, current);
+        new ComposingClassBuilder(originalName, renamedName, committed, current, options);
     ComposingClassBuilder duplicateMapping =
         current.classBuilders.put(renamedName, composingClassBuilder);
     if (duplicateMapping != null) {
@@ -411,13 +417,19 @@
     private final ComposingData current;
 
     private final ComposingClassBuilder committedPreviousClassBuilder;
+    private final InternalOptions options;
 
     private ComposingClassBuilder(
-        String originalName, String renamedName, ComposingData committed, ComposingData current) {
+        String originalName,
+        String renamedName,
+        ComposingData committed,
+        ComposingData current,
+        InternalOptions options) {
       this.originalName = originalName;
       this.renamedName = renamedName;
       this.current = current;
       this.committed = committed;
+      this.options = options;
       committedPreviousClassBuilder = committed.classBuilders.get(originalName);
     }
 
@@ -502,7 +514,8 @@
                 // The original can be discarded if it no longer exists or if the method is
                 // non-throwing.
                 if (mappedRangeResult.startOriginalPosition > 0
-                    && (originalRange == null || !newMappedRange.originalRange.isPreamble())) {
+                    && (originalRange == null || !newMappedRange.originalRange.isPreamble())
+                    && !options.mappingComposeOptions().allowNonExistingOriginalRanges) {
                   throw new MappingComposeException(
                       "Could not find original starting position of '"
                           + mappedRangeResult.lastRange
@@ -657,7 +670,7 @@
       //  6:10:void caller():19:19 -> y
       //  ...
       Box<Range> originalRange = new Box<>();
-      Function<Integer, List<MappedRange>> mappedRangesForPosition =
+      ExistingMapping mappedRangesForPosition =
           getExistingMapping(
               existingRanges, (start, end) -> originalRange.set(new Range(start, end)));
       List<MappedRange> newComposedRanges = new ArrayList<>();
@@ -682,7 +695,7 @@
           assert newRange.minifiedRange != null;
           // First check if the original range matches the existing minified range.
           List<MappedRange> existingMappedRanges =
-              mappedRangesForPosition.apply(
+              mappedRangesForPosition.getMappedRangesForPosition(
                   newRange.getFirstPositionOfOriginalRange(NO_RANGE_FROM));
           if (existingMappedRanges == null) {
             // If we cannot lookup the original position because it has been removed we compose with
@@ -700,8 +713,11 @@
                   "Unexpected missing original position for '" + newRange + "'.");
             }
           }
-          // Otherwise, we have found an existing range for the original position.
-          if (ListUtils.last(existingMappedRanges).minifiedRange.equals(newRange.originalRange)) {
+          // We have found an existing range for the original position.
+          MappedRange lastExistingMappedRange = ListUtils.last(existingMappedRanges);
+          // If the existing mapped minified range is equal to the original range of the new range
+          // then we have a perfect mapping that we can translate directly.
+          if (lastExistingMappedRange.minifiedRange.equals(newRange.originalRange)) {
             computeComposedMappedRange(
                 newComposedRanges,
                 newRange,
@@ -720,14 +736,17 @@
                 position <= newRange.minifiedRange.to;
                 position++) {
               List<MappedRange> existingMappedRangesForPosition =
-                  mappedRangesForPosition.apply(newRange.getOriginalLineNumber(position));
-              if (existingMappedRangesForPosition == null) {
-                throw new MappingComposeException(
-                    "Unexpected missing original position for '" + newRange + "'.");
+                  mappedRangesForPosition.getMappedRangesForPosition(
+                      newRange.getOriginalLineNumber(position));
+              MappedRange lastExistingMappedRangeForPosition = null;
+              if (existingMappedRangesForPosition != null) {
+                lastExistingMappedRangeForPosition =
+                    ListUtils.last(existingMappedRangesForPosition);
               }
-              if (!ListUtils.last(existingMappedRanges)
-                  .minifiedRange
-                  .equals(ListUtils.last(existingMappedRangesForPosition).minifiedRange)) {
+              if (lastExistingMappedRangeForPosition == null
+                  || !lastExistingMappedRange.minifiedRange.equals(
+                      lastExistingMappedRangeForPosition.minifiedRange)) {
+                // We have seen an existing range we have to compute a splitting for.
                 computeComposedMappedRange(
                     newComposedRanges,
                     newRange,
@@ -735,8 +754,49 @@
                     computedOutlineInformation,
                     lastStartingMinifiedFrom,
                     position - 1);
+                // Advance the last starting position to this point and advance the existing mapped
+                // ranges for this position.
                 lastStartingMinifiedFrom = position;
-                existingMappedRanges = existingMappedRangesForPosition;
+                if (existingMappedRangesForPosition == null) {
+                  if (!options.mappingComposeOptions().allowNonExistingOriginalRanges) {
+                    throw new MappingComposeException(
+                        "Unexpected missing original position for '" + newRange + "'.");
+                  }
+                  // We are at the first position of a hole. If we have existing ranges:
+                  // 1:1:void foo():41:41 -> a
+                  // 9:9:void foo():49:49 -> a
+                  // We may have a new range that is:
+                  // 21:29:void foo():1:9
+                  // We end up here at position 2 and we have already committed
+                  // 21:21:void foo():41:41.
+                  // We then introduce a "fake" normal range to simulate the result of retracing one
+                  // after the other to end up with:
+                  // 21:21:void foo():41:41
+                  // 22:28:void foo():2:8
+                  // 29:29:void foo():49:49.
+                  int startOriginalPosition = newRange.getOriginalLineNumber(position);
+                  Integer endOriginalPosition =
+                      mappedRangesForPosition.getCeilingForPosition(position);
+                  if (endOriginalPosition == null) {
+                    endOriginalPosition = newRange.getLastPositionOfOriginalRange() + 1;
+                  }
+                  Range newOriginalRange =
+                      new Range(startOriginalPosition, endOriginalPosition - 1);
+                  MappedRange nonExistingMappedRange =
+                      new MappedRange(
+                          newOriginalRange,
+                          lastExistingMappedRange.getOriginalSignature().asMethodSignature(),
+                          newOriginalRange,
+                          lastExistingMappedRange.renamedName);
+                  nonExistingMappedRange.setResidualSignatureInternal(
+                      lastExistingRange.getResidualSignatureInternal());
+                  lastExistingMappedRange = nonExistingMappedRange;
+                  existingMappedRanges = Collections.singletonList(nonExistingMappedRange);
+                  position += (endOriginalPosition - startOriginalPosition) - 1;
+                } else {
+                  lastExistingMappedRange = lastExistingMappedRangeForPosition;
+                  existingMappedRanges = existingMappedRangesForPosition;
+                }
               }
             }
             computeComposedMappedRange(
@@ -781,14 +841,20 @@
       return newComposedRanges;
     }
 
+    public interface ExistingMapping {
+
+      Integer getCeilingForPosition(int i);
+
+      List<MappedRange> getMappedRangesForPosition(int i);
+    }
+
     /***
      * Builds a position to mapped ranges for mappings for looking up all mapped ranges for a given
      * position.
      */
-    private Function<Integer, List<MappedRange>> getExistingMapping(
+    private ExistingMapping getExistingMapping(
         List<MappedRange> existingRanges, BiConsumer<Integer, Integer> positions) {
-      Int2ReferenceMap<List<MappedRange>> mappedRangesForPosition =
-          new Int2ReferenceOpenHashMap<>();
+      TreeMap<Integer, List<MappedRange>> mappedRangesForPosition = new TreeMap<>();
       List<MappedRange> currentRangesForPosition = new ArrayList<>();
       int startExisting = NO_RANGE_FROM;
       int endExisting = NO_RANGE_FROM;
@@ -818,12 +884,19 @@
       if (startExisting > NO_RANGE_FROM) {
         positions.accept(startExisting, endExisting);
       }
-      if (isCatchAll) {
-        List<MappedRange> finalMappedRangeList = currentRangesForPosition;
-        return ignored -> finalMappedRangeList;
-      } else {
-        return mappedRangesForPosition::get;
-      }
+      boolean finalIsCatchAll = isCatchAll;
+      List<MappedRange> finalCurrentRangesForPosition = currentRangesForPosition;
+      return new ExistingMapping() {
+        @Override
+        public Integer getCeilingForPosition(int i) {
+          return finalIsCatchAll ? i : mappedRangesForPosition.ceilingKey(i);
+        }
+
+        @Override
+        public List<MappedRange> getMappedRangesForPosition(int i) {
+          return finalIsCatchAll ? finalCurrentRangesForPosition : mappedRangesForPosition.get(i);
+        }
+      };
     }
 
     private void computeComposedMappedRange(
@@ -980,7 +1053,8 @@
     public ComposingClassBuilder commit(ComposingClassBuilder classBuilder)
         throws MappingComposeException {
       ComposingClassBuilder newClassBuilder =
-          new ComposingClassBuilder(originalName, classBuilder.renamedName, committed, null);
+          new ComposingClassBuilder(
+              originalName, classBuilder.renamedName, committed, null, options);
       composeMappingInformation(
           classBuilder.additionalMappingInfo,
           additionalMappingInfo,
diff --git a/src/main/java/com/android/tools/r8/naming/MappingComposer.java b/src/main/java/com/android/tools/r8/naming/MappingComposer.java
index 583bc05..636e699 100644
--- a/src/main/java/com/android/tools/r8/naming/MappingComposer.java
+++ b/src/main/java/com/android/tools/r8/naming/MappingComposer.java
@@ -4,6 +4,8 @@
 
 package com.android.tools.r8.naming;
 
+import com.android.tools.r8.utils.InternalOptions;
+
 /**
  * MappingComposer is a utility to do composition of mapping files to map line numbers correctly
  * when having shrunken input that will end up using DEX PC mappings.
@@ -11,8 +13,13 @@
 public class MappingComposer {
 
   public static String compose(ClassNameMapper... classNameMappers) throws MappingComposeException {
+    return compose(new InternalOptions(), classNameMappers);
+  }
+
+  public static String compose(InternalOptions options, ClassNameMapper... classNameMappers)
+      throws MappingComposeException {
     assert classNameMappers.length > 0;
-    ComposingBuilder builder = new ComposingBuilder();
+    ComposingBuilder builder = new ComposingBuilder(options);
     for (ClassNameMapper classNameMapper : classNameMappers) {
       builder.compose(classNameMapper);
     }
diff --git a/src/main/java/com/android/tools/r8/naming/MemberNaming.java b/src/main/java/com/android/tools/r8/naming/MemberNaming.java
index 9f3e5ed..043bb4c 100644
--- a/src/main/java/com/android/tools/r8/naming/MemberNaming.java
+++ b/src/main/java/com/android/tools/r8/naming/MemberNaming.java
@@ -87,6 +87,15 @@
     this.position = position;
   }
 
+  /** This is used internally in google3. */
+  @Deprecated
+  public Signature getRenamedSignature() {
+    if (residualSignature != null) {
+      return residualSignature;
+    }
+    return getOriginalSignature().asRenamed(renamedName);
+  }
+
   @Override
   public Signature getOriginalSignature() {
     return signature;
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 74202d3..1b77e82 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -510,7 +510,7 @@
     liveFields = new LiveFieldsSet(graphReporter::registerField);
     if (mode.isInitialTreeShaking()) {
       desugaring = CfInstructionDesugaringCollection.create(appView, appView.apiLevelCompute());
-      interfaceProcessor = new InterfaceProcessor(appView);
+      interfaceProcessor = InterfaceProcessor.create(appView);
     } else {
       desugaring = CfInstructionDesugaringCollection.empty();
       interfaceProcessor = null;
@@ -4055,6 +4055,7 @@
 
     // Move the pending methods and mark them live and ready for tracing.
     for (ProgramMethod method : pendingMethodMove) {
+      assert interfaceProcessor != null;
       ProgramMethod companion =
           interfaceProcessor
               .getHelper()
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
index fddac9e..9360393 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
@@ -13,6 +13,7 @@
 import com.android.tools.r8.errors.AssumeValuesMissingStaticFieldDiagnostic;
 import com.android.tools.r8.errors.InlinableStaticFinalFieldPreconditionDiagnostic;
 import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.errors.UnusedProguardKeepRuleDiagnostic;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.BottomUpClassHierarchyTraversal;
@@ -1884,9 +1885,7 @@
           }
         }
         if (!rule.isUsed() && options.testing.reportUnusedProguardConfigurationRules) {
-          String message = "Proguard configuration rule does not match anything: `" + rule + "`";
-          StringDiagnostic diagnostic = new StringDiagnostic(message, rule.getOrigin());
-          options.reporter.info(diagnostic);
+          options.reporter.info(new UnusedProguardKeepRuleDiagnostic(rule));
         }
       }
     }
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 3939dd5..f07a328 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -69,6 +69,7 @@
 import com.android.tools.r8.ir.desugar.nest.Nest;
 import com.android.tools.r8.ir.optimize.Inliner;
 import com.android.tools.r8.ir.optimize.enums.EnumDataMap;
+import com.android.tools.r8.naming.ClassNameMapper;
 import com.android.tools.r8.naming.MapVersion;
 import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagatorEventConsumer;
 import com.android.tools.r8.origin.Origin;
@@ -422,11 +423,6 @@
   public boolean createSingletonsForStatelessLambdas =
       System.getProperty("com.android.tools.r8.createSingletonsForStatelessLambdas") != null;
 
-  // Flag to control the representation of stateless lambdas.
-  // See b/222081665 for context.
-  public boolean rewriteInvokeToPrivateInDesugar =
-      System.getProperty("com.android.tools.r8.rewriteInvokeToPrivateInDesugar") != null;
-
   // Flag to allow record annotations in DEX. See b/231930852 for context.
   public boolean emitRecordAnnotationsInDex =
       System.getProperty("com.android.tools.r8.emitRecordAnnotationsInDex") != null;
@@ -832,6 +828,7 @@
       new KotlinOptimizationOptions();
   private final ApiModelTestingOptions apiModelTestingOptions = new ApiModelTestingOptions();
   private final DesugarSpecificOptions desugarSpecificOptions = new DesugarSpecificOptions();
+  private final MappingComposeOptions mappingComposeOptions = new MappingComposeOptions();
   private final ArtProfileOptions artProfileOptions = new ArtProfileOptions();
   private final StartupOptions startupOptions = new StartupOptions();
   private final StartupInstrumentationOptions startupInstrumentationOptions =
@@ -879,6 +876,10 @@
     return apiModelTestingOptions;
   }
 
+  public MappingComposeOptions mappingComposeOptions() {
+    return mappingComposeOptions;
+  }
+
   public DesugarSpecificOptions desugarSpecificOptions() {
     return desugarSpecificOptions;
   }
@@ -1732,6 +1733,14 @@
     }
   }
 
+  public static class MappingComposeOptions {
+    // TODO(b/241763080): Remove when enabled.
+    public boolean enableExperimentalMappingComposition = false;
+    // TODO(b/247136434): Disable for internal builds.
+    public boolean allowNonExistingOriginalRanges = true;
+    public Consumer<ClassNameMapper> generatedClassNameMapperConsumer = null;
+  }
+
   public static class ApiModelTestingOptions {
 
     // Flag to specify if we should load the database or not. The api database is used for
@@ -1750,6 +1759,8 @@
         System.getProperty("com.android.tools.r8.disableApiModeling") == null;
     public boolean enableOutliningOfMethods =
         System.getProperty("com.android.tools.r8.disableApiModeling") == null;
+    public boolean reportUnknownApiReferences =
+        System.getProperty("com.android.tools.r8.reportUnknownApiReferences") != null;
 
     // TODO(b/232823652): Enable when we can compute the offset correctly.
     public boolean useMemoryMappedByteBuffer = false;
diff --git a/src/main/java/com/android/tools/r8/utils/positions/LineNumberOptimizer.java b/src/main/java/com/android/tools/r8/utils/positions/LineNumberOptimizer.java
index de478a0..993ca5f 100644
--- a/src/main/java/com/android/tools/r8/utils/positions/LineNumberOptimizer.java
+++ b/src/main/java/com/android/tools/r8/utils/positions/LineNumberOptimizer.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.cf.code.CfInstruction;
 import com.android.tools.r8.cf.code.CfPosition;
 import com.android.tools.r8.debuginfo.DebugRepresentation.DebugRepresentationPredicate;
+import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.Code;
@@ -19,6 +20,8 @@
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.naming.MappingComposeException;
+import com.android.tools.r8.naming.MappingComposer;
 import com.android.tools.r8.naming.ProguardMapSupplier;
 import com.android.tools.r8.naming.ProguardMapSupplier.ProguardMapId;
 import com.android.tools.r8.shaking.KeepInfoCollection;
@@ -27,6 +30,7 @@
 import com.android.tools.r8.utils.OriginalSourceFiles;
 import com.android.tools.r8.utils.Timing;
 import com.android.tools.r8.utils.positions.MappedPositionToClassNameMapperBuilder.MappedPositionToClassNamingBuilder;
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.IdentityHashMap;
 import java.util.List;
@@ -46,6 +50,21 @@
     timing.begin("Line number remapping");
     ClassNameMapper mapper = run(appView, inputApp, originalSourceFiles, representation);
     timing.end();
+    if (appView.options().mappingComposeOptions().generatedClassNameMapperConsumer != null) {
+      appView.options().mappingComposeOptions().generatedClassNameMapperConsumer.accept(mapper);
+    }
+    if (appView.options().mappingComposeOptions().enableExperimentalMappingComposition) {
+      timing.begin("Proguard map composition");
+      try {
+        mapper =
+            ClassNameMapper.mapperFromStringWithExperimental(
+                MappingComposer.compose(
+                    appView.options(), appView.appInfo().app().getProguardMap(), mapper));
+      } catch (IOException | MappingComposeException e) {
+        throw new CompilationError(e.getMessage(), e);
+      }
+      timing.end();
+    }
     timing.begin("Write proguard map");
     ProguardMapId mapId = ProguardMapSupplier.create(mapper, appView.options()).writeProguardMap();
     timing.end();
diff --git a/src/main/java/com/android/tools/r8/utils/positions/MappedPositionToClassNameMapperBuilder.java b/src/main/java/com/android/tools/r8/utils/positions/MappedPositionToClassNameMapperBuilder.java
index 2b51ed8..b87a2b5 100644
--- a/src/main/java/com/android/tools/r8/utils/positions/MappedPositionToClassNameMapperBuilder.java
+++ b/src/main/java/com/android/tools/r8/utils/positions/MappedPositionToClassNameMapperBuilder.java
@@ -66,7 +66,7 @@
   private final OriginalSourceFiles originalSourceFiles;
   private final AppView<?> appView;
 
-  private final ClassNameMapper.Builder classNameMapperBuilder = ClassNameMapper.builder();
+  private final ClassNameMapper.Builder classNameMapperBuilder;
   private final Map<DexMethod, OutlineFixupBuilder> outlinesToFix = new IdentityHashMap<>();
   private final Map<DexType, String> prunedInlinedClasses = new IdentityHashMap<>();
 
@@ -79,6 +79,9 @@
       AppView<?> appView, OriginalSourceFiles originalSourceFiles) {
     this.appView = appView;
     this.originalSourceFiles = originalSourceFiles;
+    classNameMapperBuilder = ClassNameMapper.builder();
+    classNameMapperBuilder.setCurrentMapVersion(
+        appView.options().getMapFileVersion().toMapVersionMappingInformation());
   }
 
   public static int getMaxLineNumber() {
diff --git a/src/test/examplesJava11/collectiontoarray/Main.java b/src/test/examplesJava11/collectiontoarray/Main.java
new file mode 100644
index 0000000..91bbd8b
--- /dev/null
+++ b/src/test/examplesJava11/collectiontoarray/Main.java
@@ -0,0 +1,20 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package collectiontoarray;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class Main {
+  public static void main(String[] args) {
+    List<String> list = new ArrayList<>();
+    list.add("one");
+    list.add("two");
+    // This default method was added in Android T.
+    String[] toArray = list.toArray(String[]::new);
+    System.out.println(Arrays.toString(toArray));
+  }
+}
diff --git a/src/test/examplesJava9/flow/FlowExample.java b/src/test/examplesJava9/flow/FlowExample.java
index 4b02fca..caa0c24 100644
--- a/src/test/examplesJava9/flow/FlowExample.java
+++ b/src/test/examplesJava9/flow/FlowExample.java
@@ -21,7 +21,7 @@
     oneShotPublisher.awaitPublishing();
   }
 
-  static class OneShotPublisher implements Publisher<Boolean> {
+  public static class OneShotPublisher implements Publisher<Boolean> {
 
     private final ForkJoinPool executor = new ForkJoinPool(); // daemon-based
 
diff --git a/src/test/examplesJava9/flow/FlowExample2.java b/src/test/examplesJava9/flow/FlowExample2.java
new file mode 100644
index 0000000..5a69730
--- /dev/null
+++ b/src/test/examplesJava9/flow/FlowExample2.java
@@ -0,0 +1,18 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package flow;
+
+import java.util.concurrent.Flow.Publisher;
+
+public class FlowExample2 extends flowlib.FlowLib {
+
+  public static void main(String[] args) {
+    System.out.println(new FlowExample2().getPublisher().getClass().getSimpleName());
+  }
+
+  public Publisher<?> getPublisher() {
+    return new FlowExample.OneShotPublisher();
+  }
+}
diff --git a/src/test/examplesJava9/flowlib/FlowLib.java b/src/test/examplesJava9/flowlib/FlowLib.java
new file mode 100644
index 0000000..f9fa74c
--- /dev/null
+++ b/src/test/examplesJava9/flowlib/FlowLib.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package flowlib;
+
+import java.util.concurrent.Flow.Publisher;
+
+public abstract class FlowLib {
+  public abstract Publisher<?> getPublisher();
+}
diff --git a/src/test/java/com/android/tools/r8/ProguardTestBuilder.java b/src/test/java/com/android/tools/r8/ProguardTestBuilder.java
index 9b80d3b..d91646a 100644
--- a/src/test/java/com/android/tools/r8/ProguardTestBuilder.java
+++ b/src/test/java/com/android/tools/r8/ProguardTestBuilder.java
@@ -7,7 +7,6 @@
 import com.android.tools.r8.TestBase.Backend;
 import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.benchmarks.BenchmarkResults;
-import com.android.tools.r8.debug.DebugTestConfig;
 import com.android.tools.r8.errors.Unimplemented;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.FileUtils;
@@ -119,7 +118,8 @@
       }
       String proguardMap =
           Files.exists(mapFile) ? FileUtils.readTextFile(mapFile, Charsets.UTF_8) : "";
-      return new ProguardTestCompileResult(getState(), outJar, getMinApiLevel(), proguardMap);
+      return new ProguardTestCompileResult(
+          result, getState(), outJar, getMinApiLevel(), proguardMap);
     } catch (IOException e) {
       throw new CompilationFailedException(e);
     }
diff --git a/src/test/java/com/android/tools/r8/ProguardTestCompileResult.java b/src/test/java/com/android/tools/r8/ProguardTestCompileResult.java
index 01cc662..bf1ad43 100644
--- a/src/test/java/com/android/tools/r8/ProguardTestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/ProguardTestCompileResult.java
@@ -14,15 +14,18 @@
 public class ProguardTestCompileResult
     extends TestCompileResult<ProguardTestCompileResult, ProguardTestRunResult> {
 
+  private final ProcessResult result;
   private final Path outputJar;
   private final String proguardMap;
 
-  ProguardTestCompileResult(TestState state, Path outputJar, int minApiLevel, String proguardMap) {
+  ProguardTestCompileResult(
+      ProcessResult result, TestState state, Path outputJar, int minApiLevel, String proguardMap) {
     super(
         state,
         AndroidApp.builder().addProgramFiles(outputJar).build(),
         minApiLevel,
         OutputMode.ClassFile);
+    this.result = result;
     this.outputJar = outputJar;
     this.proguardMap = proguardMap;
   }
@@ -48,12 +51,12 @@
 
   @Override
   public String getStdout() {
-    throw new Unimplemented("Unexpected attempt to access stdout from Proguard");
+    return result.stdout;
   }
 
   @Override
   public String getStderr() {
-    throw new Unimplemented("Unexpected attempt to access stderr from Proguard");
+    return result.stderr;
   }
 
   @Override
diff --git a/src/test/java/com/android/tools/r8/debug/KotlinStdLibCompilationTest.java b/src/test/java/com/android/tools/r8/debug/KotlinStdLibCompilationTest.java
index 66dbfec..cf69ff4 100644
--- a/src/test/java/com/android/tools/r8/debug/KotlinStdLibCompilationTest.java
+++ b/src/test/java/com/android/tools/r8/debug/KotlinStdLibCompilationTest.java
@@ -8,13 +8,14 @@
 
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.DiagnosticsMatcher;
 import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestBase;
-import com.android.tools.r8.TestDiagnosticMessages;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestShrinkerBuilder;
 import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.errors.InterfaceDesugarMissingTypeDiagnostic;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import java.util.List;
 import org.junit.Test;
@@ -47,7 +48,12 @@
     testForD8()
         .addProgramFiles(kotlinTestParameters.getCompiler().getKotlinStdlibJar())
         .setMinApi(parameters.getApiLevel())
-        .compileWithExpectedDiagnostics(TestDiagnosticMessages::assertNoMessages);
+        // TODO(b/248244467): Remove if fixed.
+        .compileWithExpectedDiagnostics(
+            diagnostics ->
+                diagnostics.assertAllWarningsMatch(
+                    DiagnosticsMatcher.diagnosticType(
+                        InterfaceDesugarMissingTypeDiagnostic.class)));
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/desugar/InterfaceInvokePrivateTest.java b/src/test/java/com/android/tools/r8/desugar/InterfaceInvokePrivateTest.java
index 4055ea0..3ba706d 100644
--- a/src/test/java/com/android/tools/r8/desugar/InterfaceInvokePrivateTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/InterfaceInvokePrivateTest.java
@@ -12,7 +12,6 @@
 import com.android.tools.r8.TestRuntime.CfVm;
 import com.android.tools.r8.cf.CfVersion;
 import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.StringUtils;
 import java.io.IOException;
 import org.junit.Test;
@@ -30,16 +29,11 @@
   @Parameter(1)
   public CfVersion inputCfVersion;
 
-  @Parameter(2)
-  public boolean rewriteInvokeToPrivateInDesugar;
-
-  @Parameterized.Parameters(
-      name = "{0}, Input CfVersion = {1}, rewriteInvokeToPrivateInDesugar = {2}")
+  @Parameterized.Parameters(name = "{0}, Input CfVersion = {1})")
   public static Iterable<?> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().withDexRuntimes().withAllApiLevelsAlsoForCf().build(),
-        CfVersion.rangeInclusive(CfVersion.V1_8, CfVersion.V15),
-        BooleanUtils.values());
+        CfVersion.rangeInclusive(CfVersion.V1_8, CfVersion.V15));
   }
 
   private static final String EXPECTED_OUTPUT = StringUtils.unixLines("Hello, world!", "21", "6");
@@ -53,7 +47,6 @@
   public void testReference() throws Exception {
     assumeTrue(parameters.getRuntime().isCf());
     assumeTrue(parameters.getApiLevel().isEqualTo(AndroidApiLevel.B));
-    assumeTrue(rewriteInvokeToPrivateInDesugar);
 
     testForJvm()
         .addProgramClassFileData(transformIToPrivate(inputCfVersion))
@@ -76,9 +69,7 @@
 
   @Test
   public void testDesugar() throws Exception {
-    testForDesugaring(
-            parameters,
-            options -> options.rewriteInvokeToPrivateInDesugar = rewriteInvokeToPrivateInDesugar)
+    testForDesugaring(parameters)
         .addProgramClassFileData(transformIToPrivate(inputCfVersion))
         .addProgramClasses(TestRunner.class)
         .run(parameters.getRuntime(), TestRunner.class)
@@ -101,7 +92,7 @@
                     && parameters.getRuntime().asCf().isOlderThan(CfVm.JDK11)
                     && (DesugarTestConfiguration.isNotDesugared(c)
                         || (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N)
-                            && !rewriteInvokeToPrivateInDesugar)),
+                            && c == DesugarTestConfiguration.D8_DEX)),
             r -> r.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class),
             // All other conditions succeed.
             r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT));
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java
index bb51f2f..b5ca98b 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java
@@ -131,6 +131,17 @@
               + " java.nio.file.spi.FileSystemProvider.newDirectoryStream(java.nio.file.Path,"
               + " java.nio.file.DirectoryStream$Filter)");
 
+  // TODO(b/238179854): Investigate how to fix these.
+  private static final Set<String> MISSING_GENERIC_TYPE_CONVERSION_FLOW =
+      ImmutableSet.of(
+          "int java.util.concurrent.SubmissionPublisher.offer(java.lang.Object,"
+              + " java.util.function.BiPredicate)",
+          "java.util.List java.util.concurrent.SubmissionPublisher.getSubscribers()",
+          "void java.util.concurrent.SubmissionPublisher.<init>(java.util.concurrent.Executor, int,"
+              + " java.util.function.BiConsumer)",
+          "int java.util.concurrent.SubmissionPublisher.offer(java.lang.Object, long,"
+              + " java.util.concurrent.TimeUnit, java.util.function.BiPredicate)");
+
   private final LibraryDesugaringSpecification libraryDesugaringSpecification;
 
   @Parameters(name = "{0}, spec: {1}")
@@ -158,6 +169,9 @@
     if (libraryDesugaringSpecification == JDK11_PATH) {
       missing.addAll(MISSING_GENERIC_TYPE_CONVERSION_PATH);
     }
+    if (libraryDesugaringSpecification != JDK8) {
+      missing.addAll(MISSING_GENERIC_TYPE_CONVERSION_FLOW);
+    }
     return missing;
   }
 
@@ -194,7 +208,9 @@
         || type.startsWith("java.security.")
         || type.startsWith("java.net.")
         || type.startsWith("java.awt.")
-        || type.startsWith("java.util.concurrent.")
+        || (type.startsWith("java.util.concurrent.")
+            && (!type.startsWith("java.util.concurrent.Flow")
+                || libraryDesugaringSpecification == JDK8))
         || (!libraryDesugaringSpecification.hasNioFileDesugaring(AndroidApiLevel.B)
             && type.startsWith("java.nio."));
   }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/CollectionToArrayTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/CollectionToArrayTest.java
new file mode 100644
index 0000000..d3d901a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/CollectionToArrayTest.java
@@ -0,0 +1,63 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.desugaredlibrary.jdk11;
+
+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.ToolHelper;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
+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.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+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 CollectionToArrayTest extends DesugaredLibraryTestBase {
+
+  private final TestParameters parameters;
+  private final LibraryDesugaringSpecification libraryDesugaringSpecification;
+  private final CompilationSpecification compilationSpecification;
+
+  private static final Path INPUT_JAR =
+      Paths.get(ToolHelper.EXAMPLES_JAVA11_JAR_DIR + "collectiontoarray.jar");
+  private static final String EXPECTED_OUTPUT = StringUtils.lines("[one, two]");
+  private static final String MAIN_CLASS = "collectiontoarray.Main";
+
+  @Parameters(name = "{0}, spec: {1}, {2}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withDexRuntimes().withAllApiLevels().build(),
+        ImmutableList.of(JDK11, JDK11_PATH),
+        DEFAULT_SPECIFICATIONS);
+  }
+
+  public CollectionToArrayTest(
+      TestParameters parameters,
+      LibraryDesugaringSpecification libraryDesugaringSpecification,
+      CompilationSpecification compilationSpecification) {
+    this.parameters = parameters;
+    this.libraryDesugaringSpecification = libraryDesugaringSpecification;
+    this.compilationSpecification = compilationSpecification;
+  }
+
+  @Test
+  public void test() throws Throwable {
+    testForDesugaredLibrary(parameters, libraryDesugaringSpecification, compilationSpecification)
+        .addProgramFiles(INPUT_JAR)
+        .addKeepMainRule(MAIN_CLASS)
+        .run(parameters.getRuntime(), MAIN_CLASS)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/Flow2Test.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/Flow2Test.java
new file mode 100644
index 0000000..b0b4116
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/Flow2Test.java
@@ -0,0 +1,89 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.desugaredlibrary.jdk11;
+
+import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.D8_L8DEBUG;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11_LEGACY;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11_PATH;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
+import com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification;
+import com.android.tools.r8.desugar.desugaredlibrary.test.CustomLibrarySpecification;
+import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+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 Flow2Test extends DesugaredLibraryTestBase {
+
+  private final TestParameters parameters;
+  private final LibraryDesugaringSpecification libraryDesugaringSpecification;
+  private final CompilationSpecification compilationSpecification;
+
+  private static final AndroidApiLevel MIN_SUPPORTED = AndroidApiLevel.R;
+  private static final Path INPUT_JAR = Paths.get(ToolHelper.EXAMPLES_JAVA9_BUILD_DIR + "flow.jar");
+  private static final Path INPUT_LIB_JAR =
+      Paths.get(ToolHelper.EXAMPLES_JAVA9_BUILD_DIR + "flowlib.jar");
+  private static final String EXPECTED_OUTPUT = StringUtils.lines("OneShotPublisher");
+  private static final String MAIN_CLASS = "flow.FlowExample2";
+
+  @Parameters(name = "{0}, spec: {1}, {2}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters()
+            // The actual min version is V11 but we don't have it.
+            .withDexRuntimesStartingFromIncluding(Version.V12_0_0)
+            .withApiLevelsEndingAtExcluding(MIN_SUPPORTED)
+            .build(),
+        ImmutableList.of(JDK11, JDK11_LEGACY, JDK11_PATH),
+        ImmutableList.of(D8_L8DEBUG));
+  }
+
+  public Flow2Test(
+      TestParameters parameters,
+      LibraryDesugaringSpecification libraryDesugaringSpecification,
+      CompilationSpecification compilationSpecification) {
+    this.parameters = parameters;
+    this.libraryDesugaringSpecification = libraryDesugaringSpecification;
+    this.compilationSpecification = compilationSpecification;
+  }
+
+  @Test
+  public void test() throws Throwable {
+    testForDesugaredLibrary(parameters, libraryDesugaringSpecification, compilationSpecification)
+        .addProgramFiles(INPUT_JAR)
+        .addKeepMainRule(MAIN_CLASS)
+        .setCustomLibrarySpecification(
+            new CustomLibrarySpecification(INPUT_LIB_JAR, AndroidApiLevel.S))
+        .compile()
+        .inspect(this::assertWrapping)
+        .run(parameters.getRuntime(), MAIN_CLASS)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  private void assertWrapping(CodeInspector inspector) {
+    List<FoundMethodSubject> getPublisherMethods =
+        inspector
+            .clazz("flow.FlowExample2")
+            .allMethods(m -> m.getMethod().getName().toString().equals("getPublisher"));
+    int numMethods = parameters.getApiLevel().isLessThanOrEqualTo(AndroidApiLevel.Q) ? 2 : 1;
+    assertEquals(numMethods, getPublisherMethods.size());
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/CustomLibrarySpecification.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/CustomLibrarySpecification.java
index cd4de6e..1c38f5a 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/CustomLibrarySpecification.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/CustomLibrarySpecification.java
@@ -4,29 +4,44 @@
 
 package com.android.tools.r8.desugar.desugaredlibrary.test;
 
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.D8TestBuilder;
+import com.android.tools.r8.D8TestCompileResult;
+import com.android.tools.r8.SingleTestRunResult;
+import com.android.tools.r8.TestCompilerBuilder;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
 import java.util.Collection;
 
 public class CustomLibrarySpecification {
 
+  private final Collection<Path> jars;
   private final Collection<Class<?>> classes;
   private final AndroidApiLevel minApi;
 
-  public CustomLibrarySpecification(Class<?> clazz, AndroidApiLevel minApi) {
-    this(ImmutableList.of(clazz), minApi);
+  public CustomLibrarySpecification(Path jar, AndroidApiLevel minApi) {
+    this(ImmutableList.of(jar), ImmutableList.of(), minApi);
   }
 
-  public CustomLibrarySpecification(Collection<Class<?>> classes, AndroidApiLevel minApi) {
+  public CustomLibrarySpecification(Class<?> clazz, AndroidApiLevel minApi) {
+    this(ImmutableList.of(), ImmutableList.of(clazz), minApi);
+  }
+
+  public CustomLibrarySpecification(
+      Collection<Path> jars, Collection<Class<?>> classes, AndroidApiLevel minApi) {
+    this.jars = jars;
     this.classes = classes;
     this.minApi = minApi;
   }
 
-  public Collection<Class<?>> getClasses() {
-    return classes;
+  public D8TestCompileResult compileCustomLibrary(D8TestBuilder builder)
+      throws CompilationFailedException {
+    return builder.addProgramClasses(classes).addProgramFiles(jars).setMinApi(minApi).compile();
   }
 
-  public AndroidApiLevel getMinApi() {
-    return minApi;
+  public void addLibraryClasses(
+      TestCompilerBuilder<?, ?, ?, ? extends SingleTestRunResult<?>, ?> builder) {
+    builder.addLibraryClasses(classes).addLibraryFiles(jars);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/DesugaredLibraryTestBuilder.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/DesugaredLibraryTestBuilder.java
index e68c8bc..b67d511 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/DesugaredLibraryTestBuilder.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/DesugaredLibraryTestBuilder.java
@@ -106,7 +106,7 @@
   public DesugaredLibraryTestBuilder<T> setCustomLibrarySpecification(
       CustomLibrarySpecification customLibrarySpecification) {
     this.customLibrarySpecification = customLibrarySpecification;
-    builder.addLibraryClasses(customLibrarySpecification.getClasses());
+    customLibrarySpecification.addLibraryClasses(builder);
     return this;
   }
 
@@ -376,10 +376,7 @@
     if (customLibrarySpecification == null) {
       return null;
     }
-    return test.testForD8(parameters.getBackend())
-        .addProgramClasses(customLibrarySpecification.getClasses())
-        .setMinApi(customLibrarySpecification.getMinApi())
-        .compile();
+    return customLibrarySpecification.compileCustomLibrary(test.testForD8(parameters.getBackend()));
   }
 
   private L8TestCompileResult compileDesugaredLibrary(
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/LibraryDesugaringSpecification.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/LibraryDesugaringSpecification.java
index 0222cb7..9ccafc8 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/LibraryDesugaringSpecification.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/LibraryDesugaringSpecification.java
@@ -34,7 +34,7 @@
 public class LibraryDesugaringSpecification {
 
   public static Descriptor JDK8_DESCRIPTOR = new Descriptor(24, 26, -1, 26, 24);
-  public static Descriptor JDK11_DESCRIPTOR = new Descriptor(24, 30, -1, 30, -1);
+  public static Descriptor JDK11_DESCRIPTOR = new Descriptor(24, 30, -1, 32, -1);
   public static Descriptor EMPTY_DESCRIPTOR_24 = new Descriptor(-1, -1, -1, 24, -1);
   public static Descriptor JDK11_PATH_DESCRIPTOR = new Descriptor(24, 30, 26, 32, -1);
   public static Descriptor JDK11_LEGACY_DESCRIPTOR = new Descriptor(24, 10000, -1, 10000, 24);
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceStackTraceFunctionalCompositionTest.java b/src/test/java/com/android/tools/r8/retrace/RetraceStackTraceFunctionalCompositionTest.java
index 09a05ed..3c4c536 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceStackTraceFunctionalCompositionTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceStackTraceFunctionalCompositionTest.java
@@ -168,22 +168,36 @@
 
     // Compile rewritten R8 with R8 to obtain first level
     Pair<Path, Path> r8OfR8 = compileR8WithR8(rewrittenR8Jar);
-    List<String> firstLevelStackTraces = generateStackTraces(r8OfR8.getFirst());
 
     // If we retrace the entire file we should get the same result as the original.
-    List<String> retracedFirstLevelStackTraces = new ArrayList<>();
-    Retrace.run(
-        RetraceCommand.builder()
-            .setRetracedStackTraceConsumer(retracedFirstLevelStackTraces::addAll)
-            .setStackTrace(firstLevelStackTraces)
-            .setMappingSupplier(
-                ProguardMappingSupplier.builder()
-                    .setProguardMapProducer(ProguardMapProducer.fromPath(r8OfR8.getSecond()))
-                    .build())
-            .build());
+    List<String> retracedFirstLevelStackTraces =
+        retrace(r8OfR8.getSecond(), generateStackTraces(r8OfR8.getFirst()));
     Map<String, List<String>> firstRoundPartitions =
         partitionStacktraces(retracedFirstLevelStackTraces);
     comparePartitionedStackTraces(originalPartitions, firstRoundPartitions);
+
+    Pair<Path, Path> d8OfR8ofR8 = compileWithD8(r8OfR8.getFirst(), r8OfR8.getSecond());
+    List<String> retracedSecondLevelStackTraces =
+        retrace(d8OfR8ofR8.getSecond(), generateStackTraces(d8OfR8ofR8.getFirst()));
+    Map<String, List<String>> secondRoundPartitions =
+        partitionStacktraces(retracedSecondLevelStackTraces);
+    comparePartitionedStackTraces(originalPartitions, secondRoundPartitions);
+  }
+
+  private List<String> retrace(Path mappingFile, List<String> stacktraces) {
+    List<String> retracedLines = new ArrayList<>();
+    Retrace.run(
+        RetraceCommand.builder()
+            .setRetracedStackTraceConsumer(retracedLines::addAll)
+            .setStackTrace(stacktraces)
+            .setMappingSupplier(
+                ProguardMappingSupplier.builder()
+                    .setProguardMapProducer(ProguardMapProducer.fromPath(mappingFile))
+                    // TODO(b/241763080): Remove when stable.
+                    .setAllowExperimental(true)
+                    .build())
+            .build());
+    return retracedLines;
   }
 
   private void comparePartitionedStackTraces(
@@ -231,6 +245,8 @@
         .setMode(CompilationMode.RELEASE)
         .addProgramFiles(r8Input)
         .addKeepRuleFiles(MAIN_KEEP)
+        // TODO(b/241763080): Remove when stable version is default.
+        .enableExperimentalMapFileVersion()
         .allowUnusedProguardConfigurationRules()
         .addDontObfuscate()
         .compile()
@@ -239,6 +255,28 @@
     return Pair.create(jar, map);
   }
 
+  private Pair<Path, Path> compileWithD8(Path r8Input, Path previousMappingFile) throws Exception {
+    StringBuilder mappingComposed = new StringBuilder();
+    Path jar = temp.newFolder().toPath().resolve("out.jar");
+    Path map = temp.newFolder().toPath().resolve("out.map");
+    testForD8(Backend.CF)
+        .setMode(CompilationMode.RELEASE)
+        .addProgramFiles(r8Input)
+        .enableExperimentalMapFileVersion()
+        // TODO(b/241763080): Enable CF PC test mapping for this compilation.
+        .addOptionsModification(
+            options -> options.mappingComposeOptions().enableExperimentalMappingComposition = true)
+        .apply(
+            b ->
+                b.getBuilder()
+                    .setProguardInputMapFile(previousMappingFile)
+                    .setProguardMapConsumer((string, handler) -> mappingComposed.append(string)))
+        .compile()
+        .writeToZip(jar);
+    FileUtils.writeTextFile(map, mappingComposed.toString());
+    return Pair.create(jar, map);
+  }
+
   private List<String> generateStackTraces(Path r8Jar) throws Exception {
     File stacktraceOutput = new File(temp.newFolder(), "stacktraces.txt");
     stacktraceOutput.createNewFile();
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/ConditionalKeepIfFieldTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/ConditionalKeepIfFieldTest.java
new file mode 100644
index 0000000..fa8bf0b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/ConditionalKeepIfFieldTest.java
@@ -0,0 +1,88 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.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 static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.ProguardVersion;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestShrinkerBuilder;
+import java.io.IOException;
+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 ConditionalKeepIfFieldTest extends TestBase {
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withDefaultCfRuntime().build();
+  }
+
+  private TestParameters parameters;
+
+  public ConditionalKeepIfFieldTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    run(testForR8(parameters.getBackend()));
+  }
+
+  @Test
+  public void testPG() throws Exception {
+    assertTrue(parameters.isCfRuntime());
+    run(testForProguard(ProguardVersion.getLatest()).addDontWarn(getClass()));
+  }
+
+  private void run(TestShrinkerBuilder<?, ?, ?, ?, ?> builder)
+      throws IOException, ExecutionException, CompilationFailedException {
+    builder
+        .addProgramClasses(A.class, B.class, TestClass.class)
+        .addKeepRules("-if class * { *** *; } -keep class <1> { *; }")
+        .addKeepMainRule(TestClass.class)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutputLines("42", "0")
+        .inspect(
+            inspector -> {
+              // Check that the above rule only ends up keeping the class with an actual field.
+              assertThat(inspector.clazz(A.class), isAbsent());
+              assertThat(inspector.clazz(B.class), isPresent());
+            });
+  }
+
+  static class A {
+
+    public static void foo() {
+      System.out.println(42);
+    }
+  }
+
+  static class B {
+    public static int x;
+
+    public static void foo() {
+      System.out.println("" + x);
+    }
+  }
+
+  static class TestClass {
+
+    public static void main(String[] args) {
+      A.foo();
+      B.x = args.length;
+      B.foo();
+    }
+  }
+}