Revert "Compute live methods for instantiated types based on super hierarchy."

This reverts commit 8778308da343628c311ceba61f3eb639b6925a09.
Bug: 149823703
Bug: 239464956
Reason for revert: b/149823703

Change-Id: I291201af0039872b5e5e11ed1a68fea8f6a2902f
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
index 9a07dd2..813ea84 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.ir.analysis.type.ClassTypeLatticeElement;
 import com.android.tools.r8.utils.SetUtils;
 import com.android.tools.r8.utils.WorkList;
+import com.android.tools.r8.utils.WorkList.EqualityTest;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
@@ -41,7 +42,7 @@
       DexType type,
       Consumer<DexProgramClass> subTypeConsumer,
       Consumer<DexCallSite> callSiteConsumer) {
-    WorkList<DexType> workList = WorkList.newIdentityWorkList();
+    WorkList<DexType> workList = new WorkList<>(EqualityTest.IDENTITY);
     workList.addIfNotSeen(type);
     workList.addIfNotSeen(allImmediateSubtypes(type));
     while (workList.hasNext()) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index c474718..0ca6685 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -821,15 +821,6 @@
     return !clinit.getOptimizationInfo().classInitializerMayBePostponed();
   }
 
-  public void forEachImmediateSupertype(Consumer<DexType> fn) {
-    if (superType != null) {
-      fn.accept(superType);
-    }
-    for (DexType iface : interfaces.values) {
-      fn.accept(iface);
-    }
-  }
-
   public Iterable<DexType> allImmediateSupertypes() {
     Iterator<DexType> iterator =
         superType != null
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 a960171..d431886 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -31,7 +31,6 @@
 import com.android.tools.r8.graph.DexAnnotationSet;
 import com.android.tools.r8.graph.DexCallSite;
 import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexClasspathClass;
 import com.android.tools.r8.graph.DexDefinition;
 import com.android.tools.r8.graph.DexEncodedField;
@@ -55,7 +54,6 @@
 import com.android.tools.r8.graph.FieldAccessInfoImpl;
 import com.android.tools.r8.graph.InnerClassAttribute;
 import com.android.tools.r8.graph.LookupResult;
-import com.android.tools.r8.graph.LookupResult.LookupResultSuccess;
 import com.android.tools.r8.graph.ObjectAllocationInfoCollectionImpl;
 import com.android.tools.r8.graph.PresortedComparable;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -97,7 +95,6 @@
 import com.android.tools.r8.utils.SetUtils;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.android.tools.r8.utils.Timing;
-import com.android.tools.r8.utils.WorkList;
 import com.google.common.base.Equivalence.Wrapper;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSortedSet;
@@ -293,11 +290,8 @@
   private final Set<DexEncodedMethod> pendingReflectiveUses = Sets.newLinkedHashSet();
 
   /** A cache for DexMethod that have been marked reachable. */
-  private final Map<DexProgramClass, Set<DexEncodedMethod>> reachableVirtualResolutions =
-      new IdentityHashMap<>();
-
   private final Map<DexMethod, MarkedResolutionTarget> virtualTargetsMarkedAsReachable =
-      new IdentityHashMap<>();
+      Maps.newIdentityHashMap();
 
   /**
    * A set of references we have reported missing to dedupe warnings.
@@ -421,24 +415,22 @@
     return getProgramClassOrNull(type) != null;
   }
 
-  private DexClass definitionFor(DexType type) {
-    DexClass clazz = appView.definitionFor(type);
-    if (clazz == null) {
-      reportMissingClass(type);
-      return null;
-    }
-    if (liveNonProgramTypes.add(clazz) && clazz.isLibraryClass()) {
-      // TODO(b/149201735): This likely needs to apply to classpath too.
-      ensureMethodsContinueToWidenAccess(clazz);
-      // Only libraries must not derive program. Classpath classes can, assuming correct keep rules.
-      warnIfLibraryTypeInheritsFromProgramType(clazz.asLibraryClass());
-    }
-    return clazz;
-  }
-
   private DexProgramClass getProgramClassOrNull(DexType type) {
-    DexClass clazz = definitionFor(type);
-    return clazz != null && clazz.isProgramClass() ? clazz.asProgramClass() : null;
+    DexClass clazz = appView.definitionFor(type);
+    if (clazz != null) {
+      if (clazz.isProgramClass()) {
+        return clazz.asProgramClass();
+      }
+      if (liveNonProgramTypes.add(clazz) && clazz.isLibraryClass()) {
+        // TODO(b/149201735): This likely needs to apply to classpath too.
+        ensureMethodsContinueToWidenAccess(clazz);
+        // TODO(b/149201158): This should apply to classpath too (likely even hard fail).
+        warnIfLibraryTypeInheritsFromProgramType(clazz.asLibraryClass());
+      }
+    } else {
+      reportMissingClass(type);
+    }
+    return null;
   }
 
   private void warnIfLibraryTypeInheritsFromProgramType(DexLibraryClass clazz) {
@@ -733,7 +725,6 @@
     // we have to look at the interface chain and mark default methods as reachable, not taking
     // the shadowing of other interface chains into account.
     // See https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.4.3.3
-    // TODO(b/148271337): Support lookupVirtualDispatchTarget(CallSite) and replace this.
     ScopedDexMethodSet seen = new ScopedDexMethodSet();
     for (DexType iface : descriptor.interfaces) {
       DexProgramClass ifaceClazz = getProgramClassOrNull(iface);
@@ -1696,107 +1687,95 @@
   }
 
   /**
-   * Marks all methods live that are overrides of reachable methods for a given class.
+   * Marks all methods live that can be reached by calls previously seen.
    *
-   * <p>Only reachable methods in the hierarchy of the given class and above are considered, and
-   * only the lowest such reachable target (ie, mirroring resolution). All library and classpath
-   * methods are considered reachable.
+   * <p>This should only be invoked if the given type newly becomes instantiated. In essence, this
+   * method replays all the invokes we have seen so far that could apply to this type and marks the
+   * corresponding methods live.
+   *
+   * <p>Only methods that are visible in this type are considered. That is, only those methods that
+   * are either defined directly on this type or that are defined on a supertype but are not
+   * shadowed by another inherited method. Furthermore, default methods from implemented interfaces
+   * that are not otherwise shadowed are considered, too.
+   *
+   * <p>Finally all methods on library types that resolve starting at the instantiated type are
+   * marked live.
    */
   private void transitionMethodsForInstantiatedClass(DexProgramClass instantiatedClass) {
-    assert !instantiatedClass.isAnnotation();
-    assert !instantiatedClass.isInterface();
     ScopedDexMethodSet seen = new ScopedDexMethodSet();
-    WorkList<DexType> worklist = WorkList.newIdentityWorkList();
-    // First we lookup and mark all targets on the instantiated class for each reachable method in
-    // the super chain (inclusive).
-    {
-      DexClass clazz = instantiatedClass;
-      while (clazz != null) {
-        if (clazz.isProgramClass()) {
-          markProgramMethodOverridesAsLive(instantiatedClass, clazz.asProgramClass(), seen);
-        } else {
-          markLibraryAndClasspathMethodOverridesAsLive(instantiatedClass, clazz);
+    Set<DexType> interfaces = Sets.newIdentityHashSet();
+    DexProgramClass current = instantiatedClass;
+    do {
+      // We only have to look at virtual methods here, as only those can actually be executed at
+      // runtime. Illegal dispatch situations and the corresponding exceptions are already handled
+      // by the reachability logic.
+      transitionReachableVirtualMethods(current, seen);
+      Collections.addAll(interfaces, current.interfaces.values);
+      current = getProgramClassOrNull(current.superType);
+    } while (current != null && !objectAllocationInfoCollection.isInstantiatedDirectly(current));
+
+    // The set now contains all virtual methods on the type and its supertype that are reachable.
+    // In a second step, we now look at interfaces. We have to do this in this order due to JVM
+    // semantics for default methods. A default method is only reachable if it is not overridden in
+    // any superclass. Also, it is not defined which default method is chosen if multiple
+    // interfaces define the same default method. Hence, for every interface (direct or indirect),
+    // we have to look at the interface chain and mark default methods as reachable, not taking
+    // the shadowing of other interface chains into account.
+    // See https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.4.3.3
+    for (DexType iface : interfaces) {
+      DexClass clazz = appView.definitionFor(iface);
+      if (clazz == null) {
+        reportMissingClass(iface);
+        // TODO(herhut): In essence, our subtyping chain is broken here. Handle that case better.
+        break;
+      }
+      transitionDefaultMethodsForInstantiatedClass(iface, seen);
+    }
+
+    // When tracing the main-dex content, library roots must be specified, thus there are no
+    // implicit edges from library methods.
+    if (getMode().isTracingMainDex()) {
+      return;
+    }
+
+    // When a type becomes live, all library methods on that type become live too.
+    // This is done by searching the library supertypes and then resolving each method defined by
+    // such a library type from the point of the instantiated type. If the resolved targets are in
+    // the program, i.e., the instantiated type has a method overidding a library method, then the
+    // program method is live.
+    Deque<DexClass> librarySearchItems = new ArrayDeque<>();
+    librarySearchItems.add(instantiatedClass);
+    while (!librarySearchItems.isEmpty()) {
+      DexClass clazz = librarySearchItems.pop();
+      if (clazz.isNotProgramClass()) {
+        markLibraryAndClasspathMethodOverridesAsLive(clazz, instantiatedClass);
+      }
+      if (clazz.superType != null) {
+        DexClass superClass = appView.definitionFor(clazz.superType);
+        if (superClass != null) {
+          librarySearchItems.add(superClass);
         }
-        worklist.addIfNotSeen(Arrays.asList(clazz.interfaces.values));
-        clazz = clazz.superType != null ? definitionFor(clazz.superType) : null;
+      }
+      for (DexType iface : clazz.interfaces.values) {
+        DexClass ifaceClass = appView.definitionFor(iface);
+        if (ifaceClass != null) {
+          librarySearchItems.add(ifaceClass);
+        }
       }
     }
-    // The targets for methods on the type and its supertype that are reachable are now marked.
-    // In a second step, we look at interfaces. We order the search this way such that a
-    // method reachable on a class takes precedence when reporting edges. That order mirrors JVM
-    // resolution/dispatch.
-    while (worklist.hasNext()) {
-      DexType type = worklist.next();
-      DexClass iface = definitionFor(type);
-      if (iface == null) {
-        continue;
-      }
-      assert iface.superType == appInfo.dexItemFactory().objectType;
-      if (iface.isNotProgramClass()) {
-        markLibraryAndClasspathMethodOverridesAsLive(instantiatedClass, iface);
-      } else {
-        markProgramMethodOverridesAsLive(instantiatedClass, iface.asProgramClass(), seen);
-      }
-      worklist.addIfNotSeen(Arrays.asList(iface.interfaces.values));
-    }
-  }
-
-  private Set<DexEncodedMethod> getReachableVirtualResolutions(DexProgramClass clazz) {
-    return reachableVirtualResolutions.getOrDefault(clazz, Collections.emptySet());
-  }
-
-  private void markProgramMethodOverridesAsLive(
-      DexProgramClass instantiatedClass,
-      DexProgramClass superClass,
-      ScopedDexMethodSet seenMethods) {
-    for (DexEncodedMethod resolution : getReachableVirtualResolutions(superClass)) {
-      if (seenMethods.addMethod(resolution)) {
-        markLiveOverrides(instantiatedClass, superClass, resolution);
-      }
-    }
-  }
-
-  private void markLiveOverrides(
-      DexProgramClass instantiatedClass,
-      DexProgramClass reachableHolder,
-      DexEncodedMethod reachableMethod) {
-    assert reachableHolder.type == reachableMethod.method.holder;
-    // The validity of the reachable method is checked at the point it becomes "reachable" and is
-    // resolved. If the method is private, then the dispatch is not "virtual" and the method is
-    // simply marked live on its holder.
-    if (reachableMethod.isPrivateMethod()) {
-      markVirtualMethodAsLive(
-          reachableHolder,
-          reachableMethod,
-          graphReporter.reportReachableMethodAsLive(
-              reachableMethod.method, new ProgramMethod(reachableHolder, reachableMethod)));
-      return;
-    }
-    // Otherwise, we set the initial holder type to be the holder of the reachable method, which
-    // ensures that access will be generally valid.
-    SingleResolutionResult result =
-        new SingleResolutionResult(reachableHolder, reachableHolder, reachableMethod);
-    DexClassAndMethod lookup = result.lookupVirtualDispatchTarget(instantiatedClass, appView);
-    if (lookup == null || !lookup.isProgramMethod() || lookup.getMethod().isAbstract()) {
-      return;
-    }
-    ProgramMethod method = lookup.asProgramMethod();
-    markVirtualMethodAsLive(
-        method.getHolder(),
-        method.getMethod(),
-        graphReporter.reportReachableMethodAsLive(reachableMethod.method, method));
   }
 
   private void markLibraryAndClasspathMethodOverridesAsLive(
-      DexProgramClass instantiatedClass, DexClass libraryClass) {
+      DexClass libraryClass, DexProgramClass instantiatedClass) {
     assert libraryClass.isNotProgramClass();
     assert !instantiatedClass.isInterface() || instantiatedClass.isAnnotation();
     for (DexEncodedMethod method : libraryClass.virtualMethods()) {
-      assert !method.isPrivateMethod();
-      // Note: It would be reasonable to not process methods already seen during the marking of
-      // program usages, but that would cause the methods to not be marked as library overrides.
-      markLibraryOrClasspathOverrideLive(
-          instantiatedClass, libraryClass, appInfo.resolveMethod(libraryClass, method.method));
+      // Note: it may be worthwhile to add a resolution cache here. If so, it must still ensure
+      // that all library override edges are reported to the kept-graph consumer.
+      ResolutionResult firstResolution =
+          appView.appInfo().resolveMethod(instantiatedClass, method.method);
+      markResolutionAsLive(libraryClass, firstResolution);
+      markOverridesAsLibraryMethodOverrides(method.method, instantiatedClass);
 
       // Due to API conversion, some overrides can be hidden since they will be rewritten. See
       // class comment of DesugaredLibraryAPIConverter and vivifiedType logic.
@@ -1808,50 +1787,47 @@
             DesugaredLibraryAPIConverter.methodWithVivifiedTypeInSignature(
                 method.method, method.method.holder, appView);
         assert methodToResolve != method.method;
-        markLibraryOrClasspathOverrideLive(
-            instantiatedClass,
-            libraryClass,
-            appInfo.resolveMethod(instantiatedClass, methodToResolve));
+        ResolutionResult secondResolution =
+            appView.appInfo().resolveMethod(instantiatedClass, methodToResolve);
+        markResolutionAsLive(libraryClass, secondResolution);
+        markOverridesAsLibraryMethodOverrides(methodToResolve, instantiatedClass);
       }
+
     }
   }
 
-  private void markLibraryOrClasspathOverrideLive(
-      DexProgramClass instantiatedClass,
-      DexClass libraryOrClasspathClass,
-      ResolutionResult resolution) {
-    DexClassAndMethod lookup = resolution.lookupVirtualDispatchTarget(instantiatedClass, appView);
-    if (lookup == null || !lookup.isProgramMethod() || lookup.getMethod().isAbstract()) {
-      return;
+  private void markResolutionAsLive(DexClass libraryClass, ResolutionResult resolution) {
+    if (resolution.isVirtualTarget()) {
+      DexEncodedMethod target = resolution.getSingleTarget();
+      DexProgramClass targetHolder = getProgramClassOrNull(target.method.holder);
+      if (targetHolder != null
+          && shouldMarkLibraryMethodOverrideAsReachable(targetHolder, target)) {
+        markVirtualMethodAsLive(
+            targetHolder, target, KeepReason.isLibraryMethod(targetHolder, libraryClass.type));
+      }
     }
-    DexProgramClass clazz = lookup.asProgramMethod().getHolder();
-    DexEncodedMethod target = lookup.getMethod();
-    if (shouldMarkLibraryMethodOverrideAsReachable(clazz, target)) {
-      markVirtualMethodAsLive(
-          clazz, target, KeepReason.isLibraryMethod(clazz, libraryOrClasspathClass.type));
-    }
-    markOverridesAsLibraryMethodOverrides(target, instantiatedClass);
   }
 
   private void markOverridesAsLibraryMethodOverrides(
-      DexEncodedMethod libraryMethodOverride, DexProgramClass instantiatedClass) {
-    libraryMethodOverride.setLibraryMethodOverride(OptionalBool.TRUE);
-    WorkList<DexType> worklist = WorkList.newIdentityWorkList();
-    instantiatedClass.forEachImmediateSupertype(worklist::addIfNotSeen);
-    while (worklist.hasNext()) {
-      DexType type = worklist.next();
-      DexProgramClass clazz = getProgramClassOrNull(type);
-      if (clazz == null) {
-        continue;
-      }
-      DexEncodedMethod override = clazz.lookupVirtualMethod(libraryMethodOverride.method);
-      if (override != null) {
-        if (override.isLibraryMethodOverride().isTrue()) {
+      DexMethod libraryMethod, DexProgramClass instantiatedClass) {
+    Set<DexProgramClass> visited = SetUtils.newIdentityHashSet(instantiatedClass);
+    Deque<DexProgramClass> worklist = DequeUtils.newArrayDeque(instantiatedClass);
+    while (!worklist.isEmpty()) {
+      DexProgramClass clazz = worklist.removeFirst();
+      assert visited.contains(clazz);
+      DexEncodedMethod libraryMethodOverride = clazz.lookupVirtualMethod(libraryMethod);
+      if (libraryMethodOverride != null) {
+        if (libraryMethodOverride.isLibraryMethodOverride().isTrue()) {
           continue;
         }
-        override.setLibraryMethodOverride(OptionalBool.TRUE);
+        libraryMethodOverride.setLibraryMethodOverride(OptionalBool.TRUE);
       }
-      clazz.forEachImmediateSupertype(worklist::addIfNotSeen);
+      for (DexType superType : clazz.allImmediateSupertypes()) {
+        DexProgramClass superClass = getProgramClassOrNull(superType);
+        if (superClass != null && visited.add(superClass)) {
+          worklist.add(superClass);
+        }
+      }
     }
   }
 
@@ -2197,10 +2173,6 @@
     // each possible target edge below.
     assert resolution.holder.isProgramClass();
 
-    reachableVirtualResolutions
-        .computeIfAbsent(resolution.holder.asProgramClass(), k -> Sets.newIdentityHashSet())
-        .add(resolution.method);
-
     assert interfaceInvoke == holder.isInterface();
     DexProgramClass context = contextOrNull == null ? null : contextOrNull.getHolder();
     LookupResult lookupResult =
@@ -2210,12 +2182,11 @@
     if (!lookupResult.isLookupResultSuccess()) {
       return;
     }
-    LookupResultSuccess lookupResultSuccess = lookupResult.asLookupResultSuccess();
-    for (DexEncodedMethod encodedPossibleTarget : lookupResultSuccess.getMethodTargets()) {
+    for (DexEncodedMethod encodedPossibleTarget :
+        lookupResult.asLookupResultSuccess().getMethodTargets()) {
       if (encodedPossibleTarget.isAbstract()) {
         continue;
       }
-      // TODO(b/139464956): Replace this downwards search once targets are found for live types.
       markPossibleTargetsAsReachable(resolution, encodedPossibleTarget);
     }
   }
@@ -2227,8 +2198,20 @@
     assert !encodedPossibleTarget.isAbstract();
     DexMethod possibleTarget = encodedPossibleTarget.method;
     DexProgramClass clazz = getProgramClassOrNull(possibleTarget.holder);
-    // If the holder type is uninstantiated (directly or indirectly) the method is not live yet.
-    if (clazz == null || !isInstantiatedOrHasInstantiatedSubtype(clazz)) {
+    if (clazz == null) {
+      return;
+    }
+    ReachableVirtualMethodsSet reachable =
+        reachableVirtualMethods.computeIfAbsent(clazz, ignore -> new ReachableVirtualMethodsSet());
+    if (!reachable.add(encodedPossibleTarget, reason)) {
+      return;
+    }
+
+    // If the holder type is instantiated, the method is live. Otherwise check whether we find
+    // a subtype that does not shadow this methods but is instantiated.
+    // Note that library classes are always considered instantiated, as we do not know where
+    // they are instantiated.
+    if (!isInstantiatedOrHasInstantiatedSubtype(clazz)) {
       return;
     }
 
@@ -2237,8 +2220,7 @@
       markVirtualMethodAsLive(
           clazz,
           encodedPossibleTarget,
-          graphReporter.reportReachableMethodAsLive(
-              reason.method.method, new ProgramMethod(clazz, encodedPossibleTarget)));
+          graphReporter.reportReachableMethodAsLive(encodedPossibleTarget, reason));
     } else {
       Deque<DexType> worklist =
           new ArrayDeque<>(appInfo.allImmediateSubtypes(possibleTarget.holder));
diff --git a/src/main/java/com/android/tools/r8/shaking/GraphReporter.java b/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
index 37be4b3..c172b52 100644
--- a/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
+++ b/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
@@ -26,7 +26,6 @@
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter;
 import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.references.TypeReference;
@@ -249,17 +248,6 @@
   }
 
   public KeepReasonWitness reportReachableMethodAsLive(
-      DexMethod overriddenMethod, ProgramMethod derivedMethod) {
-    if (keptGraphConsumer != null && overriddenMethod != derivedMethod.getMethod().method) {
-      return reportEdge(
-          getMethodGraphNode(overriddenMethod),
-          getMethodGraphNode(derivedMethod.getMethod().method),
-          EdgeKind.OverridingMethod);
-    }
-    return KeepReasonWitness.INSTANCE;
-  }
-
-  public KeepReasonWitness reportReachableMethodAsLive(
       DexEncodedMethod encodedMethod, MarkedResolutionTarget reason) {
     if (keptGraphConsumer != null) {
       return reportEdge(
diff --git a/src/main/java/com/android/tools/r8/utils/WorkList.java b/src/main/java/com/android/tools/r8/utils/WorkList.java
index 835c528..b631262 100644
--- a/src/main/java/com/android/tools/r8/utils/WorkList.java
+++ b/src/main/java/com/android/tools/r8/utils/WorkList.java
@@ -15,15 +15,7 @@
   private final Deque<T> workingList = new ArrayDeque<>();
   private final Set<T> seen;
 
-  public static <T> WorkList<T> newIdentityWorkList() {
-    return new WorkList<T>(EqualityTest.IDENTITY);
-  }
-
-  public WorkList() {
-    this(EqualityTest.HASH);
-  }
-
-  private WorkList(EqualityTest equalityTest) {
+  public WorkList(EqualityTest equalityTest) {
     if (equalityTest == EqualityTest.HASH) {
       seen = new HashSet<>();
     } else {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/FunctionConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/FunctionConversionTest.java
index 687a673..66cdcbc 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/FunctionConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/FunctionConversionTest.java
@@ -8,6 +8,7 @@
 
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
+import com.android.tools.r8.desugar.desugaredlibrary.conversiontests.MoreFunctionConversionTest.CustomLibClass;
 import com.android.tools.r8.ir.desugar.DesugaredLibraryWrapperSynthesizer;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.BooleanUtils;