Fix concurrent method processing in D8 lambda desugaring

Fixes: 179976003
Change-Id: I397caf264df7dd6c49c3baed56b0c28e4251af33
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/D8MethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/D8MethodProcessor.java
index 38d3a32..bee8182 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/D8MethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/D8MethodProcessor.java
@@ -75,8 +75,9 @@
             executorService));
   }
 
-  public void scheduleDesugaredMethodsForProcessing(Iterable<ProgramMethod> methods) {
+  public D8MethodProcessor scheduleDesugaredMethodsForProcessing(Iterable<ProgramMethod> methods) {
     methods.forEach(this::scheduleDesugaredMethodForProcessing);
+    return this;
   }
 
   @Override
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 1cc9c0b..db7c135 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
@@ -371,11 +371,13 @@
   }
 
   private void synthesizeBridgesForNestBasedAccessesOnClasspath(
-      MethodProcessor methodProcessor, ExecutorService executorService) throws ExecutionException {
+      D8MethodProcessor methodProcessor, ExecutorService executorService)
+      throws ExecutionException {
     desugaring.withD8NestBasedAccessDesugaring(
         d8NestBasedAccessDesugaring ->
             d8NestBasedAccessDesugaring.synthesizeBridgesForNestBasedAccessesOnClasspath(
                 methodProcessor, executorService));
+    methodProcessor.awaitMethodProcessing();
   }
 
   private void reportNestDesugarDependencies() {
@@ -486,16 +488,18 @@
         ClassConverter.create(appView, this, methodProcessor).convertClasses(executorService);
 
     // The synthesis of accessibility bridges in lambda desugaring and nest based access desugaring
-    // will schedule synthesized methods for processing.
+    // will schedule and await the processing of synthesized methods.
     synthesizeBridgesForNestBasedAccessesOnClasspath(methodProcessor, executorService);
     synthesizeAccessibilityBridgesForLambdaClasses(appView, classConverterResult, methodProcessor);
-    methodProcessor.scheduleDesugaredMethodsForProcessing(
-        IterableUtils.flatMap(
-            classConverterResult.getSynthesizedLambdaClasses(),
-            lambdaClass -> lambdaClass.getLambdaProgramClass().programMethods()));
+    methodProcessor
+        .scheduleDesugaredMethodsForProcessing(
+            IterableUtils.flatMap(
+                classConverterResult.getSynthesizedLambdaClasses(),
+                lambdaClass -> lambdaClass.getLambdaProgramClass().programMethods()))
+        .awaitMethodProcessing();
 
-    // Await the processing of synthesized bridge methods.
-    methodProcessor.awaitMethodProcessing();
+    // There should be no outstanding method processing.
+    methodProcessor.verifyNoPendingMethodProcessing();
   }
 
   void convertMethods(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index 676e127..6c6f19a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.ir.desugar;
 
 import static com.android.tools.r8.ir.desugar.lambda.ForcefullyMovedLambdaMethodConsumer.emptyForcefullyMovedLambdaMethodConsumer;
+import static com.android.tools.r8.utils.ConsumerUtils.emptyConsumer;
 
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.errors.Unimplemented;
@@ -40,6 +41,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.function.Consumer;
 
 /**
  * Represents lambda class generated for a lambda descriptor in context of lambda instantiation
@@ -458,20 +460,22 @@
 
     // Ensure access of the referenced symbol(s).
     abstract ProgramMethod ensureAccessibility(
-        ForcefullyMovedLambdaMethodConsumer forcefullyMovedLambdaMethodConsumer);
+        ForcefullyMovedLambdaMethodConsumer forcefullyMovedLambdaMethodConsumer,
+        Consumer<ProgramMethod> needsProcessingConsumer);
 
     public final void ensureAccessibilityIfNeeded() {
-      ensureAccessibilityIfNeeded(emptyForcefullyMovedLambdaMethodConsumer());
+      ensureAccessibilityIfNeeded(emptyForcefullyMovedLambdaMethodConsumer(), emptyConsumer());
     }
 
     // Ensure access of the referenced symbol(s).
-    public final ProgramMethod ensureAccessibilityIfNeeded(
-        ForcefullyMovedLambdaMethodConsumer forcefullyMovedLambdaMethodConsumer) {
+    public final void ensureAccessibilityIfNeeded(
+        ForcefullyMovedLambdaMethodConsumer forcefullyMovedLambdaMethodConsumer,
+        Consumer<ProgramMethod> needsProcessingConsumer) {
       if (!hasEnsuredAccessibility) {
-        accessibilityBridge = ensureAccessibility(forcefullyMovedLambdaMethodConsumer);
+        accessibilityBridge =
+            ensureAccessibility(forcefullyMovedLambdaMethodConsumer, needsProcessingConsumer);
         hasEnsuredAccessibility = true;
       }
-      return accessibilityBridge;
     }
 
     boolean isInterface() {
@@ -495,7 +499,8 @@
 
     @Override
     ProgramMethod ensureAccessibility(
-        ForcefullyMovedLambdaMethodConsumer forcefullyMovedLambdaMethodConsumer) {
+        ForcefullyMovedLambdaMethodConsumer forcefullyMovedLambdaMethodConsumer,
+        Consumer<ProgramMethod> needsProcessingConsumer) {
       return null;
     }
   }
@@ -512,7 +517,8 @@
 
     @Override
     ProgramMethod ensureAccessibility(
-        ForcefullyMovedLambdaMethodConsumer forcefullyMovedLambdaMethodConsumer) {
+        ForcefullyMovedLambdaMethodConsumer forcefullyMovedLambdaMethodConsumer,
+        Consumer<ProgramMethod> needsProcessingConsumer) {
       // We already found the static method to be called, just relax its accessibility.
       MethodAccessFlags flags = target.getAccessFlags();
       flags.unsetPrivate();
@@ -533,7 +539,8 @@
 
     @Override
     ProgramMethod ensureAccessibility(
-        ForcefullyMovedLambdaMethodConsumer forcefullyMovedLambdaMethodConsumer) {
+        ForcefullyMovedLambdaMethodConsumer forcefullyMovedLambdaMethodConsumer,
+        Consumer<ProgramMethod> needsProcessingConsumer) {
       // For all instantiation points for which the compiler creates lambda$
       // methods, it creates these methods in the same class/interface.
       DexMethod implMethod = descriptor.implHandle.asMethod();
@@ -570,6 +577,9 @@
                     return newMethod;
                   });
       if (replacement != null) {
+        // Since we've copied the code object from an existing method, the code should already be
+        // processed, and thus we don't need to schedule it for processing in D8.
+        assert replacement.getCode().isDexCode();
         return new ProgramMethod(implMethodHolder, replacement);
       }
       // The method might already have been moved by another invoke-dynamic targeting it.
@@ -592,7 +602,8 @@
 
     @Override
     ProgramMethod ensureAccessibility(
-        ForcefullyMovedLambdaMethodConsumer forcefullyMovedLambdaMethodConsumer) {
+        ForcefullyMovedLambdaMethodConsumer forcefullyMovedLambdaMethodConsumer,
+        Consumer<ProgramMethod> needsProcessingConsumer) {
       return null;
     }
   }
@@ -606,20 +617,14 @@
 
     @Override
     ProgramMethod ensureAccessibility(
-        ForcefullyMovedLambdaMethodConsumer forcefullyMovedLambdaMethodConsumer) {
+        ForcefullyMovedLambdaMethodConsumer forcefullyMovedLambdaMethodConsumer,
+        Consumer<ProgramMethod> needsProcessingConsumer) {
       // When compiling with whole program optimization, check that we are not inplace modifying.
       // For all instantiation points for which the compiler creates lambda$
       // methods, it creates these methods in the same class/interface.
       DexMethod implMethod = descriptor.implHandle.asMethod();
       DexProgramClass implMethodHolder = appView.definitionFor(implMethod.holder).asProgramClass();
-      return modifyLambdaImplementationMethod(
-          implMethod, implMethodHolder, forcefullyMovedLambdaMethodConsumer);
-    }
 
-    private ProgramMethod modifyLambdaImplementationMethod(
-        DexMethod implMethod,
-        DexProgramClass implMethodHolder,
-        ForcefullyMovedLambdaMethodConsumer forcefullyMovedLambdaMethodConsumer) {
       DexEncodedMethod replacement =
           implMethodHolder
               .getMethodCollection()
@@ -647,6 +652,9 @@
                     return newMethod;
                   });
       if (replacement != null) {
+        // Since we've copied the code object from an existing method, the code should already be
+        // processed, and thus we don't need to schedule it for processing in D8.
+        assert replacement.getCode().isDexCode();
         return new ProgramMethod(implMethodHolder, replacement);
       }
       // The method might already have been moved by another invoke-dynamic targeting it.
@@ -668,7 +676,8 @@
 
     @Override
     ProgramMethod ensureAccessibility(
-        ForcefullyMovedLambdaMethodConsumer forcefullyMovedLambdaMethodConsumer) {
+        ForcefullyMovedLambdaMethodConsumer forcefullyMovedLambdaMethodConsumer,
+        Consumer<ProgramMethod> needsProcessingConsumer) {
       // Create a static accessor with proper accessibility.
       DexProgramClass accessorClass = appView.definitionForProgramType(callTarget.holder);
       assert accessorClass != null;
@@ -684,18 +693,20 @@
 
       // Always make the method public to provide access when r8 minification is allowed to move
       // the lambda class accessing this method to another package (-allowaccessmodification).
-      DexEncodedMethod accessorEncodedMethod =
-          new DexEncodedMethod(
-              callTarget,
-              MethodAccessFlags.createPublicStaticSynthetic(),
-              MethodTypeSignature.noSignature(),
-              DexAnnotationSet.empty(),
-              ParameterAnnotationsList.empty(),
-              AccessorMethodSourceCode.build(LambdaClass.this, callTarget),
-              true);
-
-      accessorClass.addDirectMethod(accessorEncodedMethod);
-      return new ProgramMethod(accessorClass, accessorEncodedMethod);
+      ProgramMethod accessorMethod =
+          new ProgramMethod(
+              accessorClass,
+              new DexEncodedMethod(
+                  callTarget,
+                  MethodAccessFlags.createPublicStaticSynthetic(),
+                  MethodTypeSignature.noSignature(),
+                  DexAnnotationSet.empty(),
+                  ParameterAnnotationsList.empty(),
+                  AccessorMethodSourceCode.build(LambdaClass.this, callTarget),
+                  true));
+      accessorClass.addDirectMethod(accessorMethod.getDefinition());
+      needsProcessingConsumer.accept(accessorMethod);
+      return accessorMethod;
     }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/lambda/D8LambdaDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/lambda/D8LambdaDesugaring.java
index 26ba8bb..2d89fce 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/lambda/D8LambdaDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/lambda/D8LambdaDesugaring.java
@@ -8,32 +8,33 @@
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.EnclosingMethodAttribute;
-import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.conversion.ClassConverterResult;
 import com.android.tools.r8.ir.conversion.D8MethodProcessor;
 import com.android.tools.r8.utils.collections.ProgramMethodSet;
 import java.util.IdentityHashMap;
 import java.util.Map;
+import java.util.concurrent.ExecutionException;
 
 public class D8LambdaDesugaring {
 
   public static void synthesizeAccessibilityBridgesForLambdaClasses(
       AppView<?> appView,
       ClassConverterResult classConverterResult,
-      D8MethodProcessor methodProcessor) {
+      D8MethodProcessor methodProcessor)
+      throws ExecutionException {
     Map<DexMethod, DexMethod> forcefullyMovedLambdaMethods = new IdentityHashMap<>();
     ProgramMethodSet seenAccessibilityBridges = ProgramMethodSet.createConcurrent();
     classConverterResult.forEachSynthesizedLambdaClassWithDeterministicOrdering(
         lambdaClass -> {
-          ProgramMethod accessibilityBridge =
-              lambdaClass.target.ensureAccessibilityIfNeeded(forcefullyMovedLambdaMethods::put);
-          if (accessibilityBridge != null
-              // TODO(b/179976003): This check should be redundant with the seen-set (?).
-              && !accessibilityBridge.getDefinition().getCode().isDexCode()
-              && seenAccessibilityBridges.add(accessibilityBridge)) {
-            methodProcessor.scheduleDesugaredMethodForProcessing(accessibilityBridge);
-          }
+          // Collect the accessibility bridges that require processing. Note that we cannot schedule
+          // the methods for processing directly here, since that would lead to concurrent IR
+          // processing meanwhile we update the program (insert bridges on existing classes).
+          lambdaClass.target.ensureAccessibilityIfNeeded(
+              forcefullyMovedLambdaMethods::put, seenAccessibilityBridges::add);
         });
+    methodProcessor
+        .scheduleDesugaredMethodsForProcessing(seenAccessibilityBridges)
+        .awaitMethodProcessing();
     rewriteEnclosingMethodAttributes(appView, forcefullyMovedLambdaMethods);
   }
 
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 a7fbd50..2f7edb2 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -255,9 +255,6 @@
    */
   private final SetWithReportedReason<DexProgramClass> liveTypes = new SetWithReportedReason<>();
 
-  private final Map<DexMethod, List<DexProgramClass>> liveTypesByEnclosingMethod =
-      new IdentityHashMap<>();
-
   /** Set of classes whose initializer may execute. */
   private final SetWithReportedReason<DexProgramClass> initializedClasses =
       new SetWithReportedReason<>();
@@ -1762,9 +1759,6 @@
               : this::ignoreMissingClass;
       if (enclosingMethod != null) {
         recordMethodReference(enclosingMethod, clazz, missingClassConsumer);
-        liveTypesByEnclosingMethod
-            .computeIfAbsent(enclosingMethod, ignore -> new ArrayList<>())
-            .add(clazz);
       } else {
         DexType enclosingClass = enclosingMethodAttribute.getEnclosingClass();
         recordTypeReference(enclosingClass, clazz, missingClassConsumer);