Desugared library: don't generate callbacks for live non instantiated types

Bug: b/265905174
Change-Id: I1497363ceab5842f7e09f7fc604c794a49b02ffb
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 f85f8d5..16ad20d 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
@@ -442,9 +442,8 @@
     InterfaceMethodProcessorFacade interfaceDesugaring =
         instructionDesugaring.getInterfaceMethodPostProcessingDesugaringD8(
             ExcludeDexResources, interfaceProcessor);
-    CfPostProcessingDesugaringCollection.create(appView, interfaceDesugaring)
-        .postProcessingDesugaring(
-            appView.appInfo().classes(), m -> true, eventConsumer, executorService);
+    CfPostProcessingDesugaringCollection.create(appView, interfaceDesugaring, m -> true)
+        .postProcessingDesugaring(appView.appInfo().classes(), eventConsumer, executorService);
     methodProcessor.awaitMethodProcessing();
     eventConsumer.finalizeDesugaring();
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java
index 8770cd1..e3f1f89 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java
@@ -21,10 +21,12 @@
 public abstract class CfPostProcessingDesugaringCollection {
 
   public static CfPostProcessingDesugaringCollection create(
-      AppView<?> appView, InterfaceMethodProcessorFacade interfaceMethodProcessorFacade) {
+      AppView<?> appView,
+      InterfaceMethodProcessorFacade interfaceMethodProcessorFacade,
+      Predicate<ProgramMethod> isLiveMethod) {
     if (appView.options().desugarState.isOn()) {
       return NonEmptyCfPostProcessingDesugaringCollection.create(
-          appView, interfaceMethodProcessorFacade);
+          appView, interfaceMethodProcessorFacade, isLiveMethod);
     }
     return empty();
   }
@@ -35,7 +37,6 @@
 
   public abstract void postProcessingDesugaring(
       Collection<DexProgramClass> programClasses,
-      Predicate<ProgramMethod> isLiveMethod,
       CfPostProcessingDesugaringEventConsumer eventConsumer,
       ExecutorService executorService)
       throws ExecutionException;
@@ -51,7 +52,9 @@
     }
 
     public static CfPostProcessingDesugaringCollection create(
-        AppView<?> appView, InterfaceMethodProcessorFacade interfaceMethodProcessorFacade) {
+        AppView<?> appView,
+        InterfaceMethodProcessorFacade interfaceMethodProcessorFacade,
+        Predicate<ProgramMethod> isLiveMethod) {
       ArrayList<CfPostProcessingDesugaring> desugarings = new ArrayList<>();
       if (appView.options().machineDesugaredLibrarySpecification.hasRetargeting()
           && !appView.options().isDesugaredLibraryCompilation()) {
@@ -62,7 +65,7 @@
       }
       DesugaredLibraryAPICallbackSynthesizer apiCallbackSynthesizor =
           appView.typeRewriter.isRewriting()
-              ? new DesugaredLibraryAPICallbackSynthesizer(appView)
+              ? new DesugaredLibraryAPICallbackSynthesizer(appView, isLiveMethod)
               : null;
       // At this point the desugaredLibraryAPIConverter is required to be last to generate
       // call-backs on the forwarding methods.
@@ -87,7 +90,6 @@
     @Override
     public void postProcessingDesugaring(
         Collection<DexProgramClass> programClasses,
-        Predicate<ProgramMethod> isLiveMethod,
         CfPostProcessingDesugaringEventConsumer eventConsumer,
         ExecutorService executorService)
         throws ExecutionException {
@@ -112,7 +114,6 @@
     @Override
     public void postProcessingDesugaring(
         Collection<DexProgramClass> programClasses,
-        Predicate<ProgramMethod> isLiveMethod,
         CfPostProcessingDesugaringEventConsumer eventConsumer,
         ExecutorService executorService)
         throws ExecutionException {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java
index f96680f..deeb8da 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java
@@ -5,6 +5,7 @@
 
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexClasspathClass;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -15,6 +16,8 @@
 import com.android.tools.r8.ir.desugar.itf.InterfaceProcessingDesugaringEventConsumer;
 import com.android.tools.r8.shaking.Enqueuer.SyntheticAdditions;
 import com.android.tools.r8.utils.collections.ProgramMethodSet;
+import java.util.Collections;
+import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.function.BiConsumer;
 
@@ -40,6 +43,8 @@
     return new R8PostProcessingDesugaringEventConsumer(additions, desugaring, missingClassConsumer);
   }
 
+  public abstract Set<DexMethod> getNewlyLiveMethods();
+
   public abstract void finalizeDesugaring() throws ExecutionException;
 
   public static class D8CfPostProcessingDesugaringEventConsumer
@@ -102,6 +107,11 @@
     }
 
     @Override
+    public Set<DexMethod> getNewlyLiveMethods() {
+      return Collections.emptySet();
+    }
+
+    @Override
     public void finalizeDesugaring() throws ExecutionException {
       assert methodProcessor.verifyNoPendingMethodProcessing();
       methodProcessor.newWave();
@@ -153,6 +163,13 @@
     }
 
     @Override
+    public Set<DexMethod> getNewlyLiveMethods() {
+      // Note: this answers the newly live methods up until the point where this is called.
+      // This has to be called in between post processing to be deterministic.
+      return additions.getNewlyLiveMethods();
+    }
+
+    @Override
     public void finalizeDesugaring() {
       // Intentionally empty.
     }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPICallbackSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPICallbackSynthesizer.java
index c1e11d4..8f6ad62 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPICallbackSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPICallbackSynthesizer.java
@@ -26,6 +26,7 @@
 import java.util.Collection;
 import java.util.Set;
 import java.util.concurrent.ExecutorService;
+import java.util.function.Predicate;
 
 public class DesugaredLibraryAPICallbackSynthesizer implements CfPostProcessingDesugaring {
 
@@ -35,9 +36,13 @@
   private final DesugaredLibraryWrapperSynthesizer wrapperSynthesizor;
   private final Set<DexMethod> trackedCallBackAPIs;
 
-  public DesugaredLibraryAPICallbackSynthesizer(AppView<?> appView) {
+  private final Predicate<ProgramMethod> isLiveMethod;
+
+  public DesugaredLibraryAPICallbackSynthesizer(
+      AppView<?> appView, Predicate<ProgramMethod> isLiveMethod) {
     this.appView = appView;
     this.factory = appView.dexItemFactory();
+    this.isLiveMethod = isLiveMethod;
     this.wrapperSynthesizor = new DesugaredLibraryWrapperSynthesizer(appView);
     if (appView.options().testing.trackDesugaredAPIConversions) {
       trackedCallBackAPIs = Sets.newConcurrentHashSet();
@@ -54,6 +59,7 @@
       ExecutorService executorService) {
     ProcessorContext processorContext = appView.createProcessorContext();
     MainThreadContext mainThreadContext = processorContext.createMainThreadContext();
+    Set<DexMethod> newlyLiveMethods = eventConsumer.getNewlyLiveMethods();
     assert noPendingWrappersOrConversions();
     for (DexProgramClass clazz : programClasses) {
       if (!appView.isAlreadyLibraryDesugared(clazz)) {
@@ -62,6 +68,10 @@
         // always be live in R8.
         for (ProgramMethod virtualProgramMethod : clazz.virtualProgramMethods()) {
           if (shouldRegisterCallback(virtualProgramMethod)) {
+            if (!isLiveMethod(virtualProgramMethod, newlyLiveMethods)) {
+              // This happens for live non instantiated types, library overrides are not live there.
+              continue;
+            }
             if (trackedCallBackAPIs != null) {
               trackedCallBackAPIs.add(virtualProgramMethod.getReference());
             }
@@ -82,6 +92,12 @@
     generateTrackingWarnings();
   }
 
+  private boolean isLiveMethod(
+      ProgramMethod virtualProgramMethod, Set<DexMethod> newlyLiveMethods) {
+    return isLiveMethod.test(virtualProgramMethod)
+        || newlyLiveMethods.contains(virtualProgramMethod.getReference());
+  }
+
   private boolean noPendingWrappersOrConversions() {
     for (DexProgramClass pendingSyntheticClass :
         appView.getSyntheticItems().getPendingSyntheticClasses()) {
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 213819d..c44c492 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -3910,6 +3910,10 @@
       assert old == null || old == clazz;
     }
 
+    public Set<DexMethod> getNewlyLiveMethods() {
+      return liveMethods.keySet();
+    }
+
     public void addLiveMethod(ProgramMethod method) {
       DexMethod signature = method.getDefinition().getReference();
       ProgramMethod old = liveMethods.put(signature, method);
@@ -4526,9 +4530,8 @@
     InterfaceMethodProcessorFacade interfaceDesugaring =
         desugaring.getInterfaceMethodPostProcessingDesugaringR8(
             ExcludeDexResources, liveMethods::contains, interfaceProcessor);
-    CfPostProcessingDesugaringCollection.create(appView, interfaceDesugaring)
-        .postProcessingDesugaring(
-            liveTypes.items, liveMethods::contains, eventConsumer, executorService);
+    CfPostProcessingDesugaringCollection.create(appView, interfaceDesugaring, liveMethods::contains)
+        .postProcessingDesugaring(liveTypes.items, eventConsumer, executorService);
 
     if (syntheticAdditions.isEmpty()) {
       return;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/GuavaMultiSetSpliteratorTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/GuavaMultiSetSpliteratorTest.java
new file mode 100644
index 0000000..c84d49f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/GuavaMultiSetSpliteratorTest.java
@@ -0,0 +1,189 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.desugaredlibrary;
+
+import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.D8_L8SHRINK;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.R8_L8SHRINK;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.getJdk8Jdk11;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification;
+import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMultiset;
+import com.google.common.collect.Multiset;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.jetbrains.annotations.NotNull;
+import org.junit.Assume;
+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 GuavaMultiSetSpliteratorTest extends DesugaredLibraryTestBase {
+
+  private final TestParameters parameters;
+  private final LibraryDesugaringSpecification libraryDesugaringSpecification;
+  private final CompilationSpecification compilationSpecification;
+
+  @Parameters(name = "{0}, spec: {1}, {2}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters()
+            .withDexRuntimes()
+            .withApiLevel(AndroidApiLevel.L)
+            .withAllApiLevels()
+            .build(),
+        getJdk8Jdk11(),
+        ImmutableList.of(D8_L8SHRINK, R8_L8SHRINK));
+  }
+
+  public GuavaMultiSetSpliteratorTest(
+      TestParameters parameters,
+      LibraryDesugaringSpecification libraryDesugaringSpecification,
+      CompilationSpecification compilationSpecification) {
+    this.parameters = parameters;
+    this.libraryDesugaringSpecification = libraryDesugaringSpecification;
+    this.compilationSpecification = compilationSpecification;
+  }
+
+  @Test
+  public void testGuava() throws Throwable {
+    if (!compilationSpecification.isProgramShrink()) {
+      // We need multidex for non shrinking build.
+      Assume.assumeTrue(parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L));
+    }
+    testForDesugaredLibrary(parameters, libraryDesugaringSpecification, compilationSpecification)
+        .addProgramFiles(ToolHelper.DEPS)
+        .addInnerClasses(getClass())
+        .addOptionsModification(opt -> opt.ignoreMissingClasses = true)
+        .allowDiagnosticWarningMessages()
+        .addKeepMainRule(Main.class)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("17744", "NullPointerException");
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      System.out.println(ImmutableMultiset.of().spliterator().characteristics());
+      try {
+        System.out.println(new MyMultiSet<>().spliterator().characteristics());
+      } catch (Exception e) {
+        System.out.println(e.getClass().getSimpleName());
+      }
+    }
+  }
+
+  public static class MyMultiSet<E> implements Multiset<E> {
+
+    @Override
+    public int size() {
+      return 0;
+    }
+
+    @Override
+    public boolean isEmpty() {
+      return false;
+    }
+
+    @Override
+    public int count(@Nullable Object element) {
+      return 0;
+    }
+
+    @Override
+    public int add(@Nullable E element, int occurrences) {
+      return 0;
+    }
+
+    @Override
+    public boolean add(E element) {
+      return false;
+    }
+
+    @Override
+    public int remove(@Nullable Object element, int occurrences) {
+      return 0;
+    }
+
+    @Override
+    public boolean remove(@Nullable Object element) {
+      return false;
+    }
+
+    @Override
+    public int setCount(E element, int count) {
+      return 0;
+    }
+
+    @Override
+    public boolean setCount(E element, int oldCount, int newCount) {
+      return false;
+    }
+
+    @Override
+    public Set<E> elementSet() {
+      return null;
+    }
+
+    @Override
+    public Set<Entry<E>> entrySet() {
+      return null;
+    }
+
+    @Override
+    public Iterator<E> iterator() {
+      return null;
+    }
+
+    @NotNull
+    @Override
+    public Object[] toArray() {
+      return new Object[0];
+    }
+
+    @NotNull
+    @Override
+    public <T> T[] toArray(@NotNull T[] ts) {
+      return null;
+    }
+
+    @Override
+    public boolean contains(@Nullable Object element) {
+      return false;
+    }
+
+    @Override
+    public boolean containsAll(Collection<?> elements) {
+      return false;
+    }
+
+    @Override
+    public boolean addAll(@NotNull Collection<? extends E> collection) {
+      return false;
+    }
+
+    @Override
+    public boolean removeAll(Collection<?> c) {
+      return false;
+    }
+
+    @Override
+    public boolean retainAll(Collection<?> c) {
+      return false;
+    }
+
+    @Override
+    public void clear() {}
+  }
+}