Reapply "Towards raising a diagnostic for package-private cross-split references"

This reverts commit d29116564dc98ae2ade6877e48bbcb8f0d6fdb62.

Change-Id: I2000b92912ef887692f6fc23bd330ecc51af874d
diff --git a/src/main/java/com/android/tools/r8/features/IsolatedFeatureSplitsChecker.java b/src/main/java/com/android/tools/r8/features/IsolatedFeatureSplitsChecker.java
new file mode 100644
index 0000000..ee3e64f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/features/IsolatedFeatureSplitsChecker.java
@@ -0,0 +1,174 @@
+// 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.features;
+
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.FieldResolutionResult;
+import com.android.tools.r8.graph.FieldResolutionResult.SingleFieldResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.analysis.EnqueuerFieldAccessAnalysis;
+import com.android.tools.r8.graph.analysis.EnqueuerInvokeAnalysis;
+import com.android.tools.r8.graph.analysis.EnqueuerTypeAccessAnalysis;
+import com.android.tools.r8.shaking.Enqueuer;
+import com.android.tools.r8.shaking.EnqueuerWorklist;
+import com.android.tools.r8.utils.InternalOptions;
+
+// TODO(b/300247439): Also trace types referenced from new-array instructions, call sites, etc.
+public class IsolatedFeatureSplitsChecker
+    implements EnqueuerFieldAccessAnalysis, EnqueuerInvokeAnalysis, EnqueuerTypeAccessAnalysis {
+
+  @SuppressWarnings("UnusedVariable")
+  private final AppView<? extends AppInfoWithClassHierarchy> appView;
+
+  @SuppressWarnings("UnusedVariable")
+  private final Enqueuer enqueuer;
+
+  private IsolatedFeatureSplitsChecker(
+      AppView<? extends AppInfoWithClassHierarchy> appView, Enqueuer enqueuer) {
+    this.appView = appView;
+    this.enqueuer = enqueuer;
+  }
+
+  public static void register(
+      AppView<? extends AppInfoWithClassHierarchy> appView, Enqueuer enqueuer) {
+    if (enabled(appView, enqueuer)) {
+      IsolatedFeatureSplitsChecker checker = new IsolatedFeatureSplitsChecker(appView, enqueuer);
+      enqueuer
+          .registerFieldAccessAnalysis(checker)
+          .registerInvokeAnalysis(checker)
+          .registerTypeAccessAnalysis(checker);
+    }
+  }
+
+  private static boolean enabled(
+      AppView<? extends AppInfoWithClassHierarchy> appView, Enqueuer enqueuer) {
+    InternalOptions options = appView.options();
+    return options.hasFeatureSplitConfiguration()
+        && options.getFeatureSplitConfiguration().isIsolatedSplitsEnabled()
+        && enqueuer.getMode().isInitialTreeShaking();
+  }
+
+  @SuppressWarnings("UnusedVariable")
+  private void traceFieldAccess(FieldResolutionResult resolutionResult, ProgramMethod context) {
+    // TODO(b/300247439): Check access.
+  }
+
+  @SuppressWarnings("UnusedVariable")
+  private void traceMethodInvoke(MethodResolutionResult resolutionResult, ProgramMethod context) {
+    // TODO(b/300247439): Check access.
+  }
+
+  @SuppressWarnings("UnusedVariable")
+  private void traceTypeAccess(DexClass clazz, ProgramMethod context) {
+    // TODO(b/300247439): Check access.
+  }
+
+  // Field accesses.
+
+  @Override
+  public void traceInstanceFieldRead(
+      DexField field,
+      FieldResolutionResult resolutionResult,
+      ProgramMethod context,
+      EnqueuerWorklist worklist) {
+    traceFieldAccess(resolutionResult, context);
+  }
+
+  @Override
+  public void traceInstanceFieldWrite(
+      DexField field,
+      FieldResolutionResult resolutionResult,
+      ProgramMethod context,
+      EnqueuerWorklist worklist) {
+    traceFieldAccess(resolutionResult, context);
+  }
+
+  @Override
+  public void traceStaticFieldRead(
+      DexField field,
+      SingleFieldResolutionResult<?> resolutionResult,
+      ProgramMethod context,
+      EnqueuerWorklist worklist) {
+    traceFieldAccess(resolutionResult, context);
+  }
+
+  @Override
+  public void traceStaticFieldWrite(
+      DexField field,
+      FieldResolutionResult resolutionResult,
+      ProgramMethod context,
+      EnqueuerWorklist worklist) {
+    traceFieldAccess(resolutionResult, context);
+  }
+
+  // Method invokes.
+
+  @Override
+  public void traceInvokeStatic(
+      DexMethod invokedMethod, MethodResolutionResult resolutionResult, ProgramMethod context) {
+    traceMethodInvoke(resolutionResult, context);
+  }
+
+  @Override
+  public void traceInvokeDirect(
+      DexMethod invokedMethod, MethodResolutionResult resolutionResult, ProgramMethod context) {
+    traceMethodInvoke(resolutionResult, context);
+  }
+
+  @Override
+  public void traceInvokeInterface(
+      DexMethod invokedMethod, MethodResolutionResult resolutionResult, ProgramMethod context) {
+    traceMethodInvoke(resolutionResult, context);
+  }
+
+  @Override
+  public void traceInvokeSuper(
+      DexMethod invokedMethod, MethodResolutionResult resolutionResult, ProgramMethod context) {
+    traceMethodInvoke(resolutionResult, context);
+  }
+
+  @Override
+  public void traceInvokeVirtual(
+      DexMethod invokedMethod, MethodResolutionResult resolutionResult, ProgramMethod context) {
+    traceMethodInvoke(resolutionResult, context);
+  }
+
+  // Type accesses.
+
+  @Override
+  public void traceCheckCast(DexType type, DexClass clazz, ProgramMethod context) {
+    traceTypeAccess(clazz, context);
+  }
+
+  @Override
+  public void traceSafeCheckCast(DexType type, DexClass clazz, ProgramMethod context) {
+    traceTypeAccess(clazz, context);
+  }
+
+  @Override
+  public void traceConstClass(DexType type, DexClass clazz, ProgramMethod context) {
+    traceTypeAccess(clazz, context);
+  }
+
+  @Override
+  public void traceExceptionGuard(DexType type, DexClass clazz, ProgramMethod context) {
+    traceTypeAccess(clazz, context);
+  }
+
+  @Override
+  public void traceInstanceOf(DexType type, DexClass clazz, ProgramMethod context) {
+    traceTypeAccess(clazz, context);
+  }
+
+  @Override
+  public void traceNewInstance(DexType type, DexClass clazz, ProgramMethod context) {
+    traceTypeAccess(clazz, context);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerCheckCastAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerCheckCastAnalysis.java
index ad0dcbf..1ff19a6 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerCheckCastAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerCheckCastAnalysis.java
@@ -4,12 +4,13 @@
 
 package com.android.tools.r8.graph.analysis;
 
+import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramMethod;
 
 public interface EnqueuerCheckCastAnalysis {
 
-  void traceCheckCast(DexType type, ProgramMethod context);
+  void traceCheckCast(DexType type, DexClass clazz, ProgramMethod context);
 
-  void traceSafeCheckCast(DexType type, ProgramMethod context);
+  void traceSafeCheckCast(DexType type, DexClass clazz, ProgramMethod context);
 }
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerConstClassAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerConstClassAnalysis.java
new file mode 100644
index 0000000..61f127a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerConstClassAnalysis.java
@@ -0,0 +1,13 @@
+// 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.graph.analysis;
+
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
+
+public interface EnqueuerConstClassAnalysis {
+
+  void traceConstClass(DexType type, DexClass clazz, ProgramMethod context);
+}
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerExceptionGuardAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerExceptionGuardAnalysis.java
index 1bf392e..752e4a5 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerExceptionGuardAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerExceptionGuardAnalysis.java
@@ -4,9 +4,10 @@
 
 package com.android.tools.r8.graph.analysis;
 
+import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramMethod;
 
 public interface EnqueuerExceptionGuardAnalysis {
-  void traceExceptionGuard(DexType guard, ProgramMethod context);
+  void traceExceptionGuard(DexType guard, DexClass clazz, ProgramMethod context);
 }
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerFieldAccessAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerFieldAccessAnalysis.java
index 0642216..69eb560 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerFieldAccessAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerFieldAccessAnalysis.java
@@ -18,28 +18,24 @@
       FieldResolutionResult resolutionResult,
       ProgramMethod context,
       EnqueuerWorklist worklist) {}
-  ;
 
   default void traceInstanceFieldWrite(
       DexField field,
       FieldResolutionResult resolutionResult,
       ProgramMethod context,
       EnqueuerWorklist worklist) {}
-  ;
 
   default void traceStaticFieldRead(
       DexField field,
       SingleFieldResolutionResult<?> resolutionResult,
       ProgramMethod context,
       EnqueuerWorklist worklist) {}
-  ;
 
   default void traceStaticFieldWrite(
       DexField field,
       FieldResolutionResult resolutionResult,
       ProgramMethod context,
       EnqueuerWorklist worklist) {}
-  ;
 
   /**
    * Called when the Enqueuer has reached the final fixpoint. Each analysis may use this callback to
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerInstanceOfAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerInstanceOfAnalysis.java
index abda4d1..485a311 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerInstanceOfAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerInstanceOfAnalysis.java
@@ -4,9 +4,10 @@
 
 package com.android.tools.r8.graph.analysis;
 
+import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramMethod;
 
 public interface EnqueuerInstanceOfAnalysis {
-  void traceInstanceOf(DexType type, ProgramMethod context);
+  void traceInstanceOf(DexType type, DexClass clazz, ProgramMethod context);
 }
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerInvokeAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerInvokeAnalysis.java
index d0a39b3..d2ca27d 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerInvokeAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerInvokeAnalysis.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.graph.analysis;
 
 import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.MethodResolutionResult;
 import com.android.tools.r8.graph.ProgramMethod;
 
 public interface EnqueuerInvokeAnalysis {
@@ -12,13 +13,18 @@
    * Each traceInvokeXX method is called when a corresponding invoke is found while tracing a live
    * method.
    */
-  void traceInvokeStatic(DexMethod invokedMethod, ProgramMethod context);
+  void traceInvokeStatic(
+      DexMethod invokedMethod, MethodResolutionResult resolutionResult, ProgramMethod context);
 
-  void traceInvokeDirect(DexMethod invokedMethod, ProgramMethod context);
+  void traceInvokeDirect(
+      DexMethod invokedMethod, MethodResolutionResult resolutionResult, ProgramMethod context);
 
-  void traceInvokeInterface(DexMethod invokedMethod, ProgramMethod context);
+  void traceInvokeInterface(
+      DexMethod invokedMethod, MethodResolutionResult resolutionResult, ProgramMethod context);
 
-  void traceInvokeSuper(DexMethod invokedMethod, ProgramMethod context);
+  void traceInvokeSuper(
+      DexMethod invokedMethod, MethodResolutionResult resolutionResult, ProgramMethod context);
 
-  void traceInvokeVirtual(DexMethod invokedMethod, ProgramMethod context);
+  void traceInvokeVirtual(
+      DexMethod invokedMethod, MethodResolutionResult resolutionResult, ProgramMethod context);
 }
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerNewInstanceAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerNewInstanceAnalysis.java
new file mode 100644
index 0000000..880c91a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerNewInstanceAnalysis.java
@@ -0,0 +1,13 @@
+// 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.graph.analysis;
+
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
+
+public interface EnqueuerNewInstanceAnalysis {
+
+  void traceNewInstance(DexType type, DexClass clazz, ProgramMethod context);
+}
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerTypeAccessAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerTypeAccessAnalysis.java
new file mode 100644
index 0000000..3d44ef1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerTypeAccessAnalysis.java
@@ -0,0 +1,11 @@
+// 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.graph.analysis;
+
+public interface EnqueuerTypeAccessAnalysis
+    extends EnqueuerCheckCastAnalysis,
+        EnqueuerConstClassAnalysis,
+        EnqueuerExceptionGuardAnalysis,
+        EnqueuerInstanceOfAnalysis,
+        EnqueuerNewInstanceAnalysis {}
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/InvokeVirtualToInterfaceVerifyErrorWorkaround.java b/src/main/java/com/android/tools/r8/graph/analysis/InvokeVirtualToInterfaceVerifyErrorWorkaround.java
index 94bf7ce..70fca76 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/InvokeVirtualToInterfaceVerifyErrorWorkaround.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/InvokeVirtualToInterfaceVerifyErrorWorkaround.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.MethodResolutionResult;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.shaking.Enqueuer;
 import com.android.tools.r8.shaking.KeepInfo.Joiner;
@@ -53,7 +54,8 @@
   }
 
   @Override
-  public void traceInvokeVirtual(DexMethod invokedMethod, ProgramMethod context) {
+  public void traceInvokeVirtual(
+      DexMethod invokedMethod, MethodResolutionResult resolutionResult, ProgramMethod context) {
     if (isInterfaceInSomeApiLevel(invokedMethod.getHolderType())) {
       enqueuer.getKeepInfo().joinMethod(context, Joiner::disallowOptimization);
     }
@@ -69,22 +71,26 @@
   }
 
   @Override
-  public void traceInvokeDirect(DexMethod invokedMethod, ProgramMethod context) {
+  public void traceInvokeDirect(
+      DexMethod invokedMethod, MethodResolutionResult resolutionResult, ProgramMethod context) {
     // Intentionally empty.
   }
 
   @Override
-  public void traceInvokeInterface(DexMethod invokedMethod, ProgramMethod context) {
+  public void traceInvokeInterface(
+      DexMethod invokedMethod, MethodResolutionResult resolutionResult, ProgramMethod context) {
     // Intentionally empty.
   }
 
   @Override
-  public void traceInvokeStatic(DexMethod invokedMethod, ProgramMethod context) {
+  public void traceInvokeStatic(
+      DexMethod invokedMethod, MethodResolutionResult resolutionResult, ProgramMethod context) {
     // Intentionally empty.
   }
 
   @Override
-  public void traceInvokeSuper(DexMethod invokedMethod, ProgramMethod context) {
+  public void traceInvokeSuper(
+      DexMethod invokedMethod, MethodResolutionResult resolutionResult, ProgramMethod context) {
     // Intentionally empty.
   }
 }
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 ea7cbf5..b4f643a 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -27,6 +27,7 @@
 import com.android.tools.r8.errors.InterfaceDesugarMissingTypeDiagnostic;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
+import com.android.tools.r8.features.IsolatedFeatureSplitsChecker;
 import com.android.tools.r8.graph.AccessControl;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
@@ -90,10 +91,13 @@
 import com.android.tools.r8.graph.analysis.ApiModelAnalysis;
 import com.android.tools.r8.graph.analysis.EnqueuerAnalysis;
 import com.android.tools.r8.graph.analysis.EnqueuerCheckCastAnalysis;
+import com.android.tools.r8.graph.analysis.EnqueuerConstClassAnalysis;
 import com.android.tools.r8.graph.analysis.EnqueuerExceptionGuardAnalysis;
 import com.android.tools.r8.graph.analysis.EnqueuerFieldAccessAnalysis;
 import com.android.tools.r8.graph.analysis.EnqueuerInstanceOfAnalysis;
 import com.android.tools.r8.graph.analysis.EnqueuerInvokeAnalysis;
+import com.android.tools.r8.graph.analysis.EnqueuerNewInstanceAnalysis;
+import com.android.tools.r8.graph.analysis.EnqueuerTypeAccessAnalysis;
 import com.android.tools.r8.graph.analysis.GetArrayOfMissingTypeVerifyErrorWorkaround;
 import com.android.tools.r8.graph.analysis.InvokeVirtualToInterfaceVerifyErrorWorkaround;
 import com.android.tools.r8.graph.analysis.ResourceAccessAnalysis;
@@ -256,6 +260,8 @@
   private final Set<EnqueuerInstanceOfAnalysis> instanceOfAnalyses = new LinkedHashSet<>();
   private final Set<EnqueuerExceptionGuardAnalysis> exceptionGuardAnalyses = new LinkedHashSet<>();
   private final Set<EnqueuerCheckCastAnalysis> checkCastAnalyses = new LinkedHashSet<>();
+  private final Set<EnqueuerConstClassAnalysis> constClassAnalyses = new LinkedHashSet<>();
+  private final Set<EnqueuerNewInstanceAnalysis> newInstanceAnalyses = new LinkedHashSet<>();
 
   // Don't hold a direct pointer to app info (use appView).
   private AppInfoWithClassHierarchy appInfo;
@@ -508,6 +514,7 @@
       }
       appView.withGeneratedMessageLiteBuilderShrinker(
           shrinker -> registerAnalysis(shrinker.createEnqueuerAnalysis()));
+      IsolatedFeatureSplitsChecker.register(appView, this);
       ResourceAccessAnalysis.register(appView, this);
     }
 
@@ -585,11 +592,29 @@
     return this;
   }
 
+  public Enqueuer registerConstClassAnalysis(EnqueuerConstClassAnalysis analysis) {
+    constClassAnalyses.add(analysis);
+    return this;
+  }
+
   public Enqueuer registerExceptionGuardAnalysis(EnqueuerExceptionGuardAnalysis analysis) {
     exceptionGuardAnalyses.add(analysis);
     return this;
   }
 
+  public Enqueuer registerNewInstanceAnalysis(EnqueuerNewInstanceAnalysis analysis) {
+    newInstanceAnalyses.add(analysis);
+    return this;
+  }
+
+  public Enqueuer registerTypeAccessAnalysis(EnqueuerTypeAccessAnalysis analysis) {
+    return registerCheckCastAnalysis(analysis)
+        .registerConstClassAnalysis(analysis)
+        .registerExceptionGuardAnalysis(analysis)
+        .registerInstanceOfAnalysis(analysis)
+        .registerNewInstanceAnalysis(analysis);
+  }
+
   public void setAnnotationRemoverBuilder(AnnotationRemover.Builder annotationRemoverBuilder) {
     this.annotationRemoverBuilder = annotationRemoverBuilder;
   }
@@ -1227,23 +1252,23 @@
   }
 
   void traceCheckCast(DexType type, ProgramMethod currentMethod, boolean ignoreCompatRules) {
-    checkCastAnalyses.forEach(analysis -> analysis.traceCheckCast(type, currentMethod));
-    internalTraceConstClassOrCheckCast(type, currentMethod, ignoreCompatRules);
+    DexClass clazz = internalTraceConstClassOrCheckCast(type, currentMethod, ignoreCompatRules);
+    checkCastAnalyses.forEach(analysis -> analysis.traceCheckCast(type, clazz, currentMethod));
   }
 
   void traceSafeCheckCast(DexType type, ProgramMethod currentMethod) {
-    checkCastAnalyses.forEach(analysis -> analysis.traceSafeCheckCast(type, currentMethod));
-    internalTraceConstClassOrCheckCast(type, currentMethod, true);
+    DexClass clazz = internalTraceConstClassOrCheckCast(type, currentMethod, true);
+    checkCastAnalyses.forEach(analysis -> analysis.traceSafeCheckCast(type, clazz, currentMethod));
   }
 
-  @SuppressWarnings("ReferenceEquality")
   void traceConstClass(
       DexType type,
       ProgramMethod currentMethod,
       ListIterator<? extends CfOrDexInstruction> iterator,
       boolean ignoreCompatRules) {
     handleLockCandidate(type, currentMethod, iterator);
-    internalTraceConstClassOrCheckCast(type, currentMethod, ignoreCompatRules);
+    DexClass clazz = internalTraceConstClassOrCheckCast(type, currentMethod, ignoreCompatRules);
+    constClassAnalyses.forEach(analysis -> analysis.traceConstClass(type, clazz, currentMethod));
   }
 
   private void handleLockCandidate(
@@ -1298,18 +1323,21 @@
     return result;
   }
 
-  private void internalTraceConstClassOrCheckCast(
+  private DexClass internalTraceConstClassOrCheckCast(
       DexType type, ProgramMethod currentMethod, boolean ignoreCompatRules) {
-    DexProgramClass baseClass = resolveBaseType(type, currentMethod);
+    DexClass baseClass = resolveBaseType(type, currentMethod);
     traceTypeReference(type, currentMethod);
     if (!forceProguardCompatibility || ignoreCompatRules) {
-      return;
+      return baseClass;
     }
-    if (baseClass != null) {
+    if (baseClass != null && baseClass.isProgramClass()) {
       // Don't require any constructor, see b/112386012.
+      DexProgramClass baseProgramClass = baseClass.asProgramClass();
       markClassAsInstantiatedWithCompatRule(
-          baseClass, () -> graphReporter.reportCompatInstantiated(baseClass, currentMethod));
+          baseProgramClass,
+          () -> graphReporter.reportCompatInstantiated(baseProgramClass, currentMethod));
     }
+    return baseClass;
   }
 
   void traceRecordFieldValues(DexField[] fields, ProgramMethod currentMethod) {
@@ -1401,14 +1429,16 @@
   }
 
   void traceInstanceOf(DexType type, ProgramMethod currentMethod) {
-    instanceOfAnalyses.forEach(analysis -> analysis.traceInstanceOf(type, currentMethod));
-    resolveBaseType(type, currentMethod);
+    DexClass clazz = resolveBaseType(type, currentMethod);
     traceTypeReference(type, currentMethod);
+    instanceOfAnalyses.forEach(analysis -> analysis.traceInstanceOf(type, clazz, currentMethod));
   }
 
-  void traceExceptionGuard(DexType guard, ProgramMethod currentMethod) {
-    exceptionGuardAnalyses.forEach(analysis -> analysis.traceExceptionGuard(guard, currentMethod));
-    traceTypeReference(guard, currentMethod);
+  void traceExceptionGuard(DexType type, ProgramMethod currentMethod) {
+    DexClass clazz = resolveBaseType(type, currentMethod);
+    traceTypeReference(type, currentMethod);
+    exceptionGuardAnalyses.forEach(
+        analysis -> analysis.traceExceptionGuard(type, clazz, currentMethod));
   }
 
   void traceInvokeDirect(DexMethod invokedMethod, ProgramMethod context) {
@@ -1449,8 +1479,10 @@
         methodAccessInfoCollection::registerInvokeDirectInContext, invokedMethod, context)) {
       return;
     }
-    handleInvokeOfDirectTarget(invokedMethod, context, reason);
-    invokeAnalyses.forEach(analysis -> analysis.traceInvokeDirect(invokedMethod, context));
+    MethodResolutionResult resolutionResult =
+        handleInvokeOfDirectTarget(invokedMethod, context, reason);
+    invokeAnalyses.forEach(
+        analysis -> analysis.traceInvokeDirect(invokedMethod, resolutionResult, context));
   }
 
   void traceInvokeInterface(DexMethod invokedMethod, ProgramMethod context) {
@@ -1467,8 +1499,8 @@
         methodAccessInfoCollection::registerInvokeInterfaceInContext, method, context)) {
       return;
     }
-    markVirtualMethodAsReachable(method, true, context, keepReason);
-    invokeAnalyses.forEach(analysis -> analysis.traceInvokeInterface(method, context));
+    MethodResolutionResult result = markVirtualMethodAsReachable(method, true, context, keepReason);
+    invokeAnalyses.forEach(analysis -> analysis.traceInvokeInterface(method, result, context));
   }
 
   void traceInvokeStatic(DexMethod invokedMethod, ProgramMethod context) {
@@ -1505,8 +1537,10 @@
         methodAccessInfoCollection::registerInvokeStaticInContext, invokedMethod, context)) {
       return;
     }
-    handleInvokeOfStaticTarget(invokedMethod, context, reason);
-    invokeAnalyses.forEach(analysis -> analysis.traceInvokeStatic(invokedMethod, context));
+    MethodResolutionResult resolutionResult =
+        handleInvokeOfStaticTarget(invokedMethod, context, reason);
+    invokeAnalyses.forEach(
+        analysis -> analysis.traceInvokeStatic(invokedMethod, resolutionResult, context));
   }
 
   void traceInvokeSuper(DexMethod invokedMethod, ProgramMethod context) {
@@ -1517,7 +1551,6 @@
       return;
     }
     worklist.enqueueMarkReachableSuperAction(invokedMethod, context);
-    invokeAnalyses.forEach(analysis -> analysis.traceInvokeSuper(invokedMethod, context));
   }
 
   void traceInvokeVirtual(DexMethod invokedMethod, ProgramMethod context) {
@@ -1544,8 +1577,10 @@
         methodAccessInfoCollection::registerInvokeVirtualInContext, invokedMethod, context)) {
       return;
     }
-    markVirtualMethodAsReachable(invokedMethod, false, context, reason);
-    invokeAnalyses.forEach(analysis -> analysis.traceInvokeVirtual(invokedMethod, context));
+    MethodResolutionResult resolutionResult =
+        markVirtualMethodAsReachable(invokedMethod, false, context, reason);
+    invokeAnalyses.forEach(
+        analysis -> analysis.traceInvokeVirtual(invokedMethod, resolutionResult, context));
   }
 
   void traceNewInstance(DexType type, ProgramMethod context) {
@@ -1557,11 +1592,13 @@
       return;
     }
 
-    traceNewInstance(
-        type,
-        context,
-        InstantiationReason.NEW_INSTANCE_INSTRUCTION,
-        KeepReason.instantiatedIn(context));
+    DexClass clazz =
+        traceNewInstance(
+            type,
+            context,
+            InstantiationReason.NEW_INSTANCE_INSTRUCTION,
+            KeepReason.instantiatedIn(context));
+    newInstanceAnalyses.forEach(analysis -> analysis.traceNewInstance(type, clazz, context));
   }
 
   void traceNewInstanceFromLambda(DexType type, ProgramMethod context) {
@@ -1569,19 +1606,22 @@
         type, context, InstantiationReason.LAMBDA, KeepReason.invokedFromLambdaCreatedIn(context));
   }
 
-  private void traceNewInstance(
+  private DexClass traceNewInstance(
       DexType type,
       ProgramMethod context,
       InstantiationReason instantiationReason,
       KeepReason keepReason) {
-    DexProgramClass clazz = getProgramClassOrNull(type, context);
-    if (clazz != null) {
+    DexClass clazz = resolveBaseType(type, context);
+    if (clazz != null && clazz.isProgramClass()) {
+      DexProgramClass programClass = clazz.asProgramClass();
       if (clazz.isAnnotation() || clazz.isInterface()) {
-        markTypeAsLive(clazz, graphReporter.registerClass(clazz, keepReason));
+        markTypeAsLive(programClass, graphReporter.registerClass(programClass, keepReason));
       } else {
-        worklist.enqueueMarkInstantiatedAction(clazz, context, instantiationReason, keepReason);
+        worklist.enqueueMarkInstantiatedAction(
+            programClass, context, instantiationReason, keepReason);
       }
     }
+    return clazz;
   }
 
   void traceInstanceFieldRead(DexField field, ProgramMethod currentMethod) {
@@ -2347,13 +2387,12 @@
         appView, annotatedItem, annotation, isLive, annotatedKind, mode);
   }
 
-  private DexProgramClass resolveBaseType(DexType type, ProgramDefinition context) {
+  private DexClass resolveBaseType(DexType type, ProgramDefinition context) {
     if (type.isArrayType()) {
       return resolveBaseType(type.toBaseType(appView.dexItemFactory()), context);
     }
     if (type.isClassType()) {
-      DexProgramClass clazz =
-          asProgramClassOrNull(appView.definitionFor(type, context.getContextClass()));
+      DexClass clazz = appView.definitionFor(type, context.getContextClass());
       if (clazz != null) {
         checkAccess(clazz, context);
       }
@@ -2403,9 +2442,11 @@
         appInfo.resolveMethodLegacy(method, interfaceInvoke);
     methodResolutionResult.visitMethodResolutionResults(
         resolutionResult -> {
-          checkAccess(resolutionResult, context);
-          recordMethodReference(
-              method, resolutionResult.getResolutionPair().asProgramDerivedContext(context));
+          if (!resolutionResult.isArrayCloneMethodResult()) {
+            checkAccess(resolutionResult, context);
+            recordMethodReference(
+                method, resolutionResult.getResolutionPair().asProgramDerivedContext(context));
+          }
         },
         failedResolutionResult -> {
           markFailedMethodResolutionTargets(method, failedResolutionResult, context, reason);
@@ -2414,32 +2455,33 @@
     return methodResolutionResult;
   }
 
-  private void handleInvokeOfStaticTarget(
+  private MethodResolutionResult handleInvokeOfStaticTarget(
       DexMethod reference, ProgramDefinition context, KeepReason reason) {
-    resolveMethod(reference, context, reason)
-        .forEachMethodResolutionResult(
-            resolutionResult -> {
-              if (!resolutionResult.isSingleResolution()) {
-                return;
-              }
-              SingleResolutionResult<?> resolution = resolutionResult.asSingleResolution();
-              if (resolution.getResolvedHolder().isNotProgramClass()) {
-                return;
-              }
-              DexProgramClass clazz = resolution.getResolvedHolder().asProgramClass();
-              DexEncodedMethod encodedMethod = resolution.getResolvedMethod();
+    MethodResolutionResult resolutionResults = resolveMethod(reference, context, reason);
+    resolutionResults.forEachMethodResolutionResult(
+        resolutionResult -> {
+          if (!resolutionResult.isSingleResolution()) {
+            return;
+          }
+          SingleResolutionResult<?> resolution = resolutionResult.asSingleResolution();
+          if (resolution.getResolvedHolder().isNotProgramClass()) {
+            return;
+          }
+          DexProgramClass clazz = resolution.getResolvedHolder().asProgramClass();
+          DexEncodedMethod encodedMethod = resolution.getResolvedMethod();
 
-              // We have to mark the resolved method as targeted even if it cannot actually be
-              // invoked to make sure the invocation will keep failing in the appropriate way.
-              ProgramMethod method = new ProgramMethod(clazz, encodedMethod);
-              markMethodAsTargeted(method, reason);
+          // We have to mark the resolved method as targeted even if it cannot actually be
+          // invoked to make sure the invocation will keep failing in the appropriate way.
+          ProgramMethod method = new ProgramMethod(clazz, encodedMethod);
+          markMethodAsTargeted(method, reason);
 
-              // Only mark methods for which invocation will succeed at runtime live.
-              if (encodedMethod.isStatic()) {
-                markDirectAndIndirectClassInitializersAsLive(clazz);
-                markDirectStaticOrConstructorMethodAsLive(method, reason);
-              }
-            });
+          // Only mark methods for which invocation will succeed at runtime live.
+          if (encodedMethod.isStatic()) {
+            markDirectAndIndirectClassInitializersAsLive(clazz);
+            markDirectStaticOrConstructorMethodAsLive(method, reason);
+          }
+        });
+    return resolutionResults;
   }
 
   void markDirectAndIndirectClassInitializersAsLive(DexProgramClass clazz) {
@@ -2543,43 +2585,44 @@
     handleInvokeOfDirectTarget(method, context, reason);
   }
 
-  private void handleInvokeOfDirectTarget(
+  private MethodResolutionResult handleInvokeOfDirectTarget(
       DexMethod reference, ProgramDefinition context, KeepReason reason) {
-    resolveMethod(reference, context, reason)
-        .forEachMethodResolutionResult(
-            resolutionResult -> {
-              if (resolutionResult.isFailedResolution()) {
-                failedMethodResolutionTargets.add(reference);
-                return;
-              }
+    MethodResolutionResult resolutionResults = resolveMethod(reference, context, reason);
+    resolutionResults.forEachMethodResolutionResult(
+        resolutionResult -> {
+          if (resolutionResult.isFailedResolution()) {
+            failedMethodResolutionTargets.add(reference);
+            return;
+          }
 
-              if (!resolutionResult.isSingleResolution()
-                  || !resolutionResult.getResolvedHolder().isProgramClass()) {
-                return;
-              }
+          if (!resolutionResult.isSingleResolution()
+              || !resolutionResult.getResolvedHolder().isProgramClass()) {
+            return;
+          }
 
-              ProgramMethod resolvedMethod =
-                  resolutionResult.asSingleResolution().getResolvedProgramMethod();
+          ProgramMethod resolvedMethod =
+              resolutionResult.asSingleResolution().getResolvedProgramMethod();
 
-              // We have to mark the resolved method as targeted even if it cannot actually be
-              // invoked to make sure the invocation will keep failing in the appropriate way.
-              markMethodAsTargeted(resolvedMethod, reason);
+          // We have to mark the resolved method as targeted even if it cannot actually be
+          // invoked to make sure the invocation will keep failing in the appropriate way.
+          markMethodAsTargeted(resolvedMethod, reason);
 
-              // Only mark methods for which invocation will succeed at runtime live.
-              if (resolvedMethod.getAccessFlags().isStatic()) {
-                return;
-              }
+          // Only mark methods for which invocation will succeed at runtime live.
+          if (resolvedMethod.getAccessFlags().isStatic()) {
+            return;
+          }
 
-              markDirectStaticOrConstructorMethodAsLive(resolvedMethod, reason);
+          markDirectStaticOrConstructorMethodAsLive(resolvedMethod, reason);
 
-              // It is valid to have an invoke-direct instruction in a default interface method that
-              // targets another default method in the same interface. In a class, that would lead
-              // to a verification error. See also testInvokeSpecialToDefaultMethod.
-              if (resolvedMethod.getDefinition().isNonPrivateVirtualMethod()
-                  && virtualMethodsTargetedByInvokeDirect.add(resolvedMethod.getReference())) {
-                worklist.enqueueMarkMethodLiveAction(resolvedMethod, context, reason);
-              }
-            });
+          // It is valid to have an invoke-direct instruction in a default interface method that
+          // targets another default method in the same interface. In a class, that would lead
+          // to a verification error. See also testInvokeSpecialToDefaultMethod.
+          if (resolvedMethod.getDefinition().isNonPrivateVirtualMethod()
+              && virtualMethodsTargetedByInvokeDirect.add(resolvedMethod.getReference())) {
+            worklist.enqueueMarkMethodLiveAction(resolvedMethod, context, reason);
+          }
+        });
+    return resolutionResults;
   }
 
   private void ensureFromLibraryOrThrow(DexType type, DexLibraryClass context) {
@@ -3343,89 +3386,91 @@
     liveTypes.getItems().forEach(consumer);
   }
 
-  private void markVirtualMethodAsReachable(
+  private MethodResolutionResult markVirtualMethodAsReachable(
       DexMethod method, boolean interfaceInvoke, ProgramMethod context, KeepReason reason) {
-    if (method.holder.isArrayType()) {
+    MethodResolutionResult resolutionResults =
+        resolveMethod(method, context, reason, interfaceInvoke);
+    if (method.getHolderType().isArrayType()) {
       // This is an array type, so the actual class will be generated at runtime. We treat this
       // like an invoke on a direct subtype of java.lang.Object that has no further subtypes.
       // As it has no subtypes, it cannot affect liveness of the program we are processing.
       // Ergo, we can ignore it. We need to make sure that the element type is available, though.
-      markTypeAsLive(method.holder, context, reason);
-      return;
+      markTypeAsLive(method.getHolderType(), context, reason);
+      return resolutionResults;
     }
 
-    resolveMethod(method, context, reason, interfaceInvoke)
-        .forEachMethodResolutionResult(
-            resolutionResult -> {
-              if (!resolutionResult.isSingleResolution()) {
-                return;
-              }
-              SingleResolutionResult<?> resolution = resolutionResult.asSingleResolution();
-              // Note that all virtual methods derived from library methods are kept regardless of
-              // being reachable, so the following only needs to consider reachable targets in the
-              // program.
-              // TODO(b/70160030): Revise this to support tree shaking library methods on
-              //  non-escaping types.
-              DexProgramClass initialResolutionHolder =
-                  resolution.getInitialResolutionHolder().asProgramClass();
-              if (initialResolutionHolder == null) {
-                recordMethodReference(method, context);
-                return;
-              }
+    resolutionResults.forEachMethodResolutionResult(
+        resolutionResult -> {
+          if (!resolutionResult.isSingleResolution()) {
+            return;
+          }
+          SingleResolutionResult<?> resolution = resolutionResult.asSingleResolution();
+          // Note that all virtual methods derived from library methods are kept regardless of
+          // being reachable, so the following only needs to consider reachable targets in the
+          // program.
+          // TODO(b/70160030): Revise this to support tree shaking library methods on
+          //  non-escaping types.
+          DexProgramClass initialResolutionHolder =
+              resolution.getInitialResolutionHolder().asProgramClass();
+          if (initialResolutionHolder == null) {
+            recordMethodReference(method, context);
+            return;
+          }
 
-              if (resolution.getResolvedHolder().isNotProgramClass()) {
-                // TODO(b/70160030): If the resolution is on a library method, then the keep edge
-                //  needs to go directly to the target method in the program. Thus this method will
-                //  need to ensure that 'reason' is not already reported (eg, must be delayed /
-                //  non-witness) and report that for each possible target edge below.
-                return;
-              }
+          if (resolution.getResolvedHolder().isNotProgramClass()) {
+            // TODO(b/70160030): If the resolution is on a library method, then the keep edge
+            //  needs to go directly to the target method in the program. Thus this method will
+            //  need to ensure that 'reason' is not already reported (eg, must be delayed /
+            //  non-witness) and report that for each possible target edge below.
+            return;
+          }
 
-              DexProgramClass contextHolder = context.getContextClass();
-              // If the method has already been marked, just report the new reason for the resolved
-              // target and save the context to ensure correct lookup of virtual dispatch targets.
-              ResolutionSearchKey resolutionSearchKey =
-                  new ResolutionSearchKey(method, interfaceInvoke);
-              ProgramMethodSet seenContexts =
-                  getReachableVirtualTargets(initialResolutionHolder).get(resolutionSearchKey);
-              if (seenContexts != null) {
-                seenContexts.add(context);
-                graphReporter.registerMethod(resolution.getResolvedMethod(), reason);
-                return;
-              }
+          DexProgramClass contextHolder = context.getContextClass();
+          // If the method has already been marked, just report the new reason for the resolved
+          // target and save the context to ensure correct lookup of virtual dispatch targets.
+          ResolutionSearchKey resolutionSearchKey =
+              new ResolutionSearchKey(method, interfaceInvoke);
+          ProgramMethodSet seenContexts =
+              getReachableVirtualTargets(initialResolutionHolder).get(resolutionSearchKey);
+          if (seenContexts != null) {
+            seenContexts.add(context);
+            graphReporter.registerMethod(resolution.getResolvedMethod(), reason);
+            return;
+          }
 
-              // We have to mark the resolution targeted, even if it does not become live, we
-              // need at least an abstract version of it so that it can be targeted.
-              DexProgramClass resolvedHolder = resolution.getResolvedHolder().asProgramClass();
-              DexEncodedMethod resolvedMethod = resolution.getResolvedMethod();
-              markMethodAsTargeted(new ProgramMethod(resolvedHolder, resolvedMethod), reason);
-              if (resolution.isAccessibleForVirtualDispatchFrom(contextHolder, appView).isFalse()) {
-                // Not accessible from this context, so this call will cause a runtime exception.
-                return;
-              }
+          // We have to mark the resolution targeted, even if it does not become live, we
+          // need at least an abstract version of it so that it can be targeted.
+          DexProgramClass resolvedHolder = resolution.getResolvedHolder().asProgramClass();
+          DexEncodedMethod resolvedMethod = resolution.getResolvedMethod();
+          markMethodAsTargeted(new ProgramMethod(resolvedHolder, resolvedMethod), reason);
+          if (resolution.isAccessibleForVirtualDispatchFrom(contextHolder, appView).isFalse()) {
+            // Not accessible from this context, so this call will cause a runtime exception.
+            return;
+          }
 
-              // The method resolved and is accessible, so currently live overrides become live.
-              reachableVirtualTargets
-                  .computeIfAbsent(initialResolutionHolder, ignoreArgument(HashMap::new))
-                  .computeIfAbsent(resolutionSearchKey, ignoreArgument(ProgramMethodSet::create))
-                  .add(context);
+          // The method resolved and is accessible, so currently live overrides become live.
+          reachableVirtualTargets
+              .computeIfAbsent(initialResolutionHolder, ignoreArgument(HashMap::new))
+              .computeIfAbsent(resolutionSearchKey, ignoreArgument(ProgramMethodSet::create))
+              .add(context);
 
-              resolution
-                  .lookupVirtualDispatchTargets(
-                      contextHolder,
-                      appView,
-                      (type, subTypeConsumer, lambdaConsumer) ->
-                          objectAllocationInfoCollection.forEachInstantiatedSubType(
-                              type, subTypeConsumer, lambdaConsumer, appInfo),
-                      definition -> keepInfo.isPinned(definition, options, appInfo))
-                  .forEach(
-                      target ->
-                          markVirtualDispatchTargetAsLive(
-                              target,
-                              programMethod ->
-                                  graphReporter.reportReachableMethodAsLive(
-                                      resolvedMethod.getReference(), programMethod)));
-            });
+          resolution
+              .lookupVirtualDispatchTargets(
+                  contextHolder,
+                  appView,
+                  (type, subTypeConsumer, lambdaConsumer) ->
+                      objectAllocationInfoCollection.forEachInstantiatedSubType(
+                          type, subTypeConsumer, lambdaConsumer, appInfo),
+                  definition -> keepInfo.isPinned(definition, options, appInfo))
+              .forEach(
+                  target ->
+                      markVirtualDispatchTargetAsLive(
+                          target,
+                          programMethod ->
+                              graphReporter.reportReachableMethodAsLive(
+                                  resolvedMethod.getReference(), programMethod)));
+        });
+    return resolutionResults;
   }
 
   private void markVirtualDispatchTargetAsLive(
@@ -3542,54 +3587,56 @@
   }
 
   // Package protected due to entry point from worklist.
-  void markSuperMethodAsReachable(DexMethod reference, ProgramMethod from) {
-    KeepReason reason = KeepReason.targetedBySuperFrom(from);
-    resolveMethod(reference, from, reason)
-        .forEachMethodResolutionResult(
-            resolutionResult -> {
-              if (!resolutionResult.isSingleResolution()) {
-                return;
-              }
-              SingleResolutionResult<?> resolution = resolutionResult.asSingleResolution();
-              // If the resolution is in the program, mark it targeted.
-              if (resolution.getResolvedHolder().isProgramClass()) {
-                markMethodAsTargeted(
-                    new ProgramMethod(
-                        resolution.getResolvedHolder().asProgramClass(),
-                        resolution.getResolvedMethod()),
-                    reason);
-              }
-              // If invoke target is invalid (inaccessible or not an instance-method) record it and
-              // stop.
-              DexClassAndMethod target =
-                  resolution.lookupInvokeSuperTarget(from.getHolder(), appView);
-              if (target == null) {
-                failedMethodResolutionTargets.add(resolution.getResolvedMethod().getReference());
-                analyses.forEach(
-                    analyses ->
-                        analyses.notifyFailedMethodResolutionTarget(
-                            resolution.getResolvedMethod(), worklist));
-                return;
-              }
+  void markSuperMethodAsReachable(DexMethod reference, ProgramMethod context) {
+    KeepReason reason = KeepReason.targetedBySuperFrom(context);
+    MethodResolutionResult resolutionResults = resolveMethod(reference, context, reason);
+    resolutionResults.forEachMethodResolutionResult(
+        resolutionResult -> {
+          if (!resolutionResult.isSingleResolution()) {
+            return;
+          }
+          SingleResolutionResult<?> resolution = resolutionResult.asSingleResolution();
+          // If the resolution is in the program, mark it targeted.
+          if (resolution.getResolvedHolder().isProgramClass()) {
+            markMethodAsTargeted(
+                new ProgramMethod(
+                    resolution.getResolvedHolder().asProgramClass(),
+                    resolution.getResolvedMethod()),
+                reason);
+          }
+          // If invoke target is invalid (inaccessible or not an instance-method) record it and
+          // stop.
+          DexClassAndMethod target =
+              resolution.lookupInvokeSuperTarget(context.getHolder(), appView);
+          if (target == null) {
+            failedMethodResolutionTargets.add(resolution.getResolvedMethod().getReference());
+            analyses.forEach(
+                analyses ->
+                    analyses.notifyFailedMethodResolutionTarget(
+                        resolution.getResolvedMethod(), worklist));
+            return;
+          }
 
-              DexProgramClass clazz = target.getHolder().asProgramClass();
-              if (clazz == null) {
-                return;
-              }
+          DexProgramClass clazz = target.getHolder().asProgramClass();
+          if (clazz == null) {
+            return;
+          }
 
-              ProgramMethod method = target.asProgramMethod();
+          ProgramMethod method = target.asProgramMethod();
 
-              if (superInvokeDependencies
-                  .computeIfAbsent(from.getDefinition(), ignore -> ProgramMethodSet.create())
-                  .add(method)) {
-                if (liveMethods.contains(from)) {
-                  markMethodAsTargeted(method, KeepReason.invokedViaSuperFrom(from));
-                  if (!target.getAccessFlags().isAbstract()) {
-                    markVirtualMethodAsLive(method, KeepReason.invokedViaSuperFrom(from));
-                  }
-                }
+          if (superInvokeDependencies
+              .computeIfAbsent(context.getDefinition(), ignore -> ProgramMethodSet.create())
+              .add(method)) {
+            if (liveMethods.contains(context)) {
+              markMethodAsTargeted(method, KeepReason.invokedViaSuperFrom(context));
+              if (!target.getAccessFlags().isAbstract()) {
+                markVirtualMethodAsLive(method, KeepReason.invokedViaSuperFrom(context));
               }
-            });
+            }
+          }
+        });
+    invokeAnalyses.forEach(
+        analysis -> analysis.traceInvokeSuper(reference, resolutionResults, context));
   }
 
   // Returns the set of live types.
diff --git a/src/main/java/com/android/tools/r8/shaking/RuntimeTypeCheckInfo.java b/src/main/java/com/android/tools/r8/shaking/RuntimeTypeCheckInfo.java
index 5b6511d..045227d 100644
--- a/src/main/java/com/android/tools/r8/shaking/RuntimeTypeCheckInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/RuntimeTypeCheckInfo.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.shaking;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
@@ -83,22 +84,22 @@
     }
 
     @Override
-    public void traceCheckCast(DexType type, ProgramMethod context) {
+    public void traceCheckCast(DexType type, DexClass clazz, ProgramMethod context) {
       add(type, checkCastTypes);
     }
 
     @Override
-    public void traceSafeCheckCast(DexType type, ProgramMethod context) {
+    public void traceSafeCheckCast(DexType type, DexClass clazz, ProgramMethod context) {
       // Intentionally empty.
     }
 
     @Override
-    public void traceInstanceOf(DexType type, ProgramMethod context) {
+    public void traceInstanceOf(DexType type, DexClass clazz, ProgramMethod context) {
       add(type, instanceOfTypes);
     }
 
     @Override
-    public void traceExceptionGuard(DexType guard, ProgramMethod context) {
+    public void traceExceptionGuard(DexType guard, DexClass clazz, ProgramMethod context) {
       add(guard, exceptionGuardTypes);
     }
 
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 b71ad68..40b2d19 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -223,6 +223,14 @@
     return false;
   }
 
+  public boolean hasFeatureSplitConfiguration() {
+    return featureSplitConfiguration != null;
+  }
+
+  public FeatureSplitConfiguration getFeatureSplitConfiguration() {
+    return featureSplitConfiguration;
+  }
+
   public boolean hasProguardConfiguration() {
     return proguardConfiguration != null;
   }