Only re-evaluate if rules with members on classes with newly live members

Bug: b/206086945
Change-Id: If93573d950c6855f9bd78b8b29173f08715137a9
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index e890a6a..c9ca7e4 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -838,7 +838,7 @@
   public static Iterable<DexProgramClass> asProgramClasses(
       Iterable<DexType> types, DexDefinitionSupplier definitions) {
     return () ->
-        new Iterator<DexProgramClass>() {
+        new Iterator<>() {
 
           private final Iterator<DexType> iterator = types.iterator();
 
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignatureEnqueuerAnalysis.java b/src/main/java/com/android/tools/r8/graph/GenericSignatureEnqueuerAnalysis.java
index 2599a50..ed13c75 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignatureEnqueuerAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignatureEnqueuerAnalysis.java
@@ -37,7 +37,7 @@
   }
 
   @Override
-  public void notifyMarkMethodAsTargeted(ProgramMethod method, EnqueuerWorklist worklist) {
+  public void processNewlyTargetedMethod(ProgramMethod method, EnqueuerWorklist worklist) {
     processSignature(method, method.getContext());
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/ApiModelAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/ApiModelAnalysis.java
index b1ca241..f16a2d8 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/ApiModelAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/ApiModelAnalysis.java
@@ -61,7 +61,7 @@
   }
 
   @Override
-  public void notifyMarkMethodAsTargeted(ProgramMethod method, EnqueuerWorklist worklist) {
+  public void processNewlyTargetedMethod(ProgramMethod method, EnqueuerWorklist worklist) {
     computeAndSetApiLevelForDefinition(method);
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java
index 791b3b4..8fd3ed4 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java
@@ -31,6 +31,8 @@
   public void processNewlyLiveField(
       ProgramField field, ProgramDefinition context, EnqueuerWorklist worklist) {}
 
+  public void processNewlyReferencedField(ProgramField field) {}
+
   /** Called when a method is found to be live. */
   public void processNewlyLiveMethod(
       ProgramMethod method,
@@ -45,7 +47,7 @@
   public void processTracedCode(
       ProgramMethod method, DefaultEnqueuerUseRegistry registry, EnqueuerWorklist worklist) {}
 
-  public void notifyMarkMethodAsTargeted(ProgramMethod method, EnqueuerWorklist worklist) {}
+  public void processNewlyTargetedMethod(ProgramMethod method, EnqueuerWorklist worklist) {}
 
   public void notifyMarkFieldAsReachable(ProgramField field, EnqueuerWorklist worklist) {}
 
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 022157e..96be3c1 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -1180,7 +1180,7 @@
     }
   }
 
-  private FieldAccessInfoImpl getOrCreateFieldAccessInfo(DexEncodedField field) {
+  private FieldAccessInfoImpl getOrCreateFieldAccessInfo(DexClassAndField field) {
     // Check if we have previously created a FieldAccessInfo object for the field definition.
     FieldAccessInfoImpl info = fieldAccessInfoCollection.get(field.getReference());
 
@@ -1188,6 +1188,12 @@
     if (info == null) {
       info = new FieldAccessInfoImpl(field.getReference());
       fieldAccessInfoCollection.extend(field.getReference(), info);
+
+      // Notify analyses.
+      if (field.isProgramField()) {
+        ProgramField programField = field.asProgramField();
+        analyses.forEach(analysis -> analysis.processNewlyReferencedField(programField));
+      }
     }
 
     return info;
@@ -1215,12 +1221,12 @@
         return true;
       }
 
-      DexEncodedField encodedField = seenResult.get().getDefinition();
-      info = getOrCreateFieldAccessInfo(encodedField);
+      DexClassAndField resolvedField = seenResult.get();
+      info = getOrCreateFieldAccessInfo(resolvedField);
 
       // If `field` is an indirect reference, then create a mapping for it, such that we don't have
       // to resolve the field the next time we see the reference.
-      if (field != encodedField.getReference()) {
+      if (field != resolvedField.getReference()) {
         fieldAccessInfoCollection.extend(field, info);
       }
     } else if (info == MISSING_FIELD_ACCESS_INFO) {
@@ -4965,7 +4971,7 @@
 
   // Package protected due to entry point from worklist.
   void markFieldAsKept(ProgramField field, KeepReason reason) {
-    FieldAccessInfoImpl fieldAccessInfo = getOrCreateFieldAccessInfo(field.getDefinition());
+    FieldAccessInfoImpl fieldAccessInfo = getOrCreateFieldAccessInfo(field);
     fieldAccessInfo.setHasReflectiveRead();
     fieldAccessInfo.setHasReflectiveWrite();
 
@@ -5084,7 +5090,7 @@
         markMethodAsLiveWithCompatRule(method);
       }
     }
-    analyses.forEach(analysis -> analysis.notifyMarkMethodAsTargeted(method, worklist));
+    analyses.forEach(analysis -> analysis.processNewlyTargetedMethod(method, worklist));
   }
 
   void traceMethodDefinitionExcludingCode(ProgramMethod method) {
diff --git a/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java b/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java
index c0c05d4..da2ca1d 100644
--- a/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java
+++ b/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java
@@ -29,6 +29,7 @@
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.function.Consumer;
+import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
 public class IfRuleEvaluator {
@@ -55,7 +56,8 @@
 
   public void processActiveIfRulesWithMembers(
       Map<Wrapper<ProguardIfRule>, Set<ProguardIfRule>> ifRules,
-      Set<DexProgramClass> effectivelyFakeLiveClasses)
+      Iterable<DexProgramClass> classesWithNewlyLiveMembers,
+      Predicate<DexProgramClass> isEffectivelyLive)
       throws ExecutionException {
     MapUtils.removeIf(
         ifRules,
@@ -68,10 +70,8 @@
           // rule and live types.
           for (DexProgramClass clazz :
               ifRuleKey.relevantCandidatesForRule(
-                  appView, subtypingInfo, appView.appInfo().classes())) {
-            if (!isEffectivelyLive(clazz, effectivelyFakeLiveClasses)) {
-              continue;
-            }
+                  appView, subtypingInfo, classesWithNewlyLiveMembers, isEffectivelyLive)) {
+            assert isEffectivelyLive.test(clazz);
             evaluateRuleOnEffectivelyLiveClass(
                 ifRuleKey,
                 ifRulesInEquivalence,
@@ -117,11 +117,6 @@
     tasks.await();
   }
 
-  private boolean isEffectivelyLive(
-      DexProgramClass clazz, Set<DexProgramClass> effectivelyFakeLiveClasses) {
-    return enqueuer.isTypeLive(clazz) || effectivelyFakeLiveClasses.contains(clazz);
-  }
-
   private void evaluateRuleOnEffectivelyLiveClass(
       ProguardIfRule ifRuleKey,
       Set<ProguardIfRule> ifRulesInEquivalence,
diff --git a/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluatorFactory.java b/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluatorFactory.java
index cc813e2..9fe386a 100644
--- a/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluatorFactory.java
+++ b/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluatorFactory.java
@@ -10,6 +10,9 @@
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.ProgramDefinition;
+import com.android.tools.r8.graph.ProgramField;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.SubtypingInfo;
 import com.android.tools.r8.graph.analysis.EnqueuerAnalysis;
 import com.android.tools.r8.shaking.RootSetUtils.ConsequentRootSet;
@@ -38,9 +41,9 @@
 
   private final Set<DexProgramClass> effectivelyFakeLiveClasses;
   private final Set<DexProgramClass> newlyLiveClasses = Sets.newIdentityHashSet();
+  private final Set<DexProgramClass> classesWithNewlyLiveMembers = Sets.newIdentityHashSet();
 
   private boolean seenFixpoint;
-  private long previousNumberOfLiveItems = 0;
 
   private final TaskCollection<?> tasks;
 
@@ -119,7 +122,7 @@
       Enqueuer enqueuer, EnqueuerWorklist worklist, ExecutorService executorService, Timing timing)
       throws ExecutionException {
     boolean isFirstFixpoint = setSeenFixpoint();
-    if (!shouldProcessActiveIfRulesWithMembers(enqueuer)
+    if (!shouldProcessActiveIfRulesWithMembers(isFirstFixpoint)
         && !shouldProcessActiveIfRulesWithoutMembers(isFirstFixpoint)) {
       return;
     }
@@ -129,14 +132,17 @@
             "Find consequent items for -if rules...",
             () -> processActiveIfRules(enqueuer, isFirstFixpoint));
     enqueuer.addConsequentRootSet(consequentRootSet);
-    long numberOfLiveItemsAtEnd = enqueuer.getNumberOfLiveItems();
-    assert numberOfLiveItemsAtEnd == numberOfLiveItemsAtStart;
-    previousNumberOfLiveItems = numberOfLiveItemsAtEnd;
+    assert enqueuer.getNumberOfLiveItems() == numberOfLiveItemsAtStart;
   }
 
-  private boolean shouldProcessActiveIfRulesWithMembers(Enqueuer enqueuer) {
-    return !activeIfRulesWithMembers.isEmpty()
-        && enqueuer.getNumberOfLiveItems() > previousNumberOfLiveItems;
+  private boolean shouldProcessActiveIfRulesWithMembers(boolean isFirstFixpoint) {
+    if (activeIfRulesWithMembers.isEmpty()) {
+      return false;
+    }
+    if (isFirstFixpoint && !effectivelyFakeLiveClasses.isEmpty()) {
+      return true;
+    }
+    return !classesWithNewlyLiveMembers.isEmpty();
   }
 
   private ConsequentRootSet processActiveIfRules(Enqueuer enqueuer, boolean isFirstFixpoint)
@@ -146,8 +152,8 @@
         ConsequentRootSet.builder(appView, enqueuer, subtypingInfo);
     IfRuleEvaluator evaluator =
         new IfRuleEvaluator(appView, subtypingInfo, enqueuer, consequentRootSetBuilder, tasks);
-    if (shouldProcessActiveIfRulesWithMembers(enqueuer)) {
-      processActiveIfRulesWithMembers(evaluator);
+    if (shouldProcessActiveIfRulesWithMembers(isFirstFixpoint)) {
+      processActiveIfRulesWithMembers(evaluator, isFirstFixpoint);
     }
     if (shouldProcessActiveIfRulesWithoutMembers(isFirstFixpoint)) {
       processActiveIfRulesWithoutMembers(evaluator, isFirstFixpoint);
@@ -155,9 +161,22 @@
     return consequentRootSetBuilder.buildConsequentRootSet();
   }
 
-  private void processActiveIfRulesWithMembers(IfRuleEvaluator evaluator)
+  private void processActiveIfRulesWithMembers(IfRuleEvaluator evaluator, boolean isFirstFixpoint)
       throws ExecutionException {
-    evaluator.processActiveIfRulesWithMembers(activeIfRulesWithMembers, effectivelyFakeLiveClasses);
+    if (isFirstFixpoint && !effectivelyFakeLiveClasses.isEmpty()) {
+      evaluator.processActiveIfRulesWithMembers(
+          activeIfRulesWithMembers,
+          Iterables.concat(effectivelyFakeLiveClasses, classesWithNewlyLiveMembers),
+          clazz ->
+              effectivelyFakeLiveClasses.contains(clazz)
+                  || classesWithNewlyLiveMembers.contains(clazz));
+    } else {
+      evaluator.processActiveIfRulesWithMembers(
+          activeIfRulesWithMembers,
+          classesWithNewlyLiveMembers,
+          classesWithNewlyLiveMembers::contains);
+    }
+    classesWithNewlyLiveMembers.clear();
   }
 
   private boolean shouldProcessActiveIfRulesWithoutMembers(boolean isFirstFixpoint) {
@@ -197,4 +216,36 @@
     }
     newlyLiveClasses.add(clazz);
   }
+
+  @Override
+  public void processNewlyLiveField(
+      ProgramField field, ProgramDefinition context, EnqueuerWorklist worklist) {
+    addClassWithNewlyLiveMembers(field.getHolder());
+  }
+
+  @Override
+  public void processNewlyReferencedField(ProgramField field) {
+    addClassWithNewlyLiveMembers(field.getHolder());
+  }
+
+  @Override
+  public void processNewlyLiveMethod(
+      ProgramMethod method,
+      ProgramDefinition context,
+      Enqueuer enqueuer,
+      EnqueuerWorklist worklist) {
+    addClassWithNewlyLiveMembers(method.getHolder());
+  }
+
+  @Override
+  public void processNewlyTargetedMethod(ProgramMethod method, EnqueuerWorklist worklist) {
+    addClassWithNewlyLiveMembers(method.getHolder());
+  }
+
+  private void addClassWithNewlyLiveMembers(DexProgramClass clazz) {
+    // In the first fixpoint we report all effectively fake live classes as changed.
+    if (seenFixpoint || !effectivelyFakeLiveClasses.contains(clazz)) {
+      classesWithNewlyLiveMembers.add(clazz);
+    }
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java b/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java
index 576588d..25c9cbf 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java
@@ -6,6 +6,7 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.utils.IterableUtils;
+import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.TraversalContinuation;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
@@ -82,7 +83,9 @@
   @Override
   public abstract int hashCode();
 
-  public abstract List<DexType> asSpecificDexTypes();
+  public abstract boolean hasSpecificTypes();
+
+  public abstract List<DexType> getSpecificTypes();
 
   public abstract boolean matches(DexType type);
 
@@ -154,7 +157,12 @@
     }
 
     @Override
-    public List<DexType> asSpecificDexTypes() {
+    public boolean hasSpecificTypes() {
+      return false;
+    }
+
+    @Override
+    public List<DexType> getSpecificTypes() {
       return null;
     }
 
@@ -211,9 +219,15 @@
     }
 
     @Override
-    public List<DexType> asSpecificDexTypes() {
-      DexType specific = className.getSpecificType();
-      return specific == null ? null : Collections.singletonList(specific);
+    public boolean hasSpecificTypes() {
+      return className.hasSpecificType();
+    }
+
+    @Override
+    public List<DexType> getSpecificTypes() {
+      return className.hasSpecificType()
+          ? Collections.singletonList(className.getSpecificType())
+          : null;
     }
 
     @Override
@@ -247,6 +261,8 @@
 
     private final ImmutableList<ProguardTypeMatcher> classNames;
 
+    private List<DexType> specificTypes;
+
     private PositiveClassNameList(Collection<ProguardTypeMatcher> classNames) {
       this.classNames = ImmutableList.copyOf(classNames);
     }
@@ -287,15 +303,23 @@
     }
 
     @Override
-    public List<DexType> asSpecificDexTypes() {
-      if (classNames.stream().allMatch(k -> k.getSpecificType() != null)) {
-        return classNames.stream().map(ProguardTypeMatcher::getSpecificType)
-            .collect(Collectors.toList());
-      }
-      return null;
+    public boolean hasSpecificTypes() {
+      return getSpecificTypes() != null;
     }
 
     @Override
+    public List<DexType> getSpecificTypes() {
+      if (specificTypes == null) {
+        specificTypes =
+            classNames.stream().allMatch(ProguardTypeMatcher::hasSpecificType)
+                ? ListUtils.map(classNames, ProguardTypeMatcher::getSpecificType)
+                : Collections.emptyList();
+      }
+      return specificTypes.isEmpty() ? null : specificTypes;
+    }
+
+
+    @Override
     public boolean matches(DexType type) {
       return Iterables.any(classNames, name -> name.matches(type));
     }
@@ -377,7 +401,12 @@
     }
 
     @Override
-    public List<DexType> asSpecificDexTypes() {
+    public boolean hasSpecificTypes() {
+      return false;
+    }
+
+    @Override
+    public List<DexType> getSpecificTypes() {
       return null;
     }
 
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
index 01c9118..4e80401 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
@@ -3,11 +3,12 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
+import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
+
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.ClassResolutionResult;
-import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexProgramClass;
@@ -18,11 +19,11 @@
 import com.android.tools.r8.utils.BooleanBox;
 import com.android.tools.r8.utils.IterableUtils;
 import com.android.tools.r8.utils.StringUtils;
-import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
+import java.util.function.Predicate;
 
 public abstract class ProguardConfigurationRule extends ProguardClassSpecification {
 
@@ -149,47 +150,53 @@
   Iterable<DexProgramClass> relevantCandidatesForRule(
       AppView<? extends AppInfoWithClassHierarchy> appView,
       SubtypingInfo subtypingInfo,
-      Iterable<DexProgramClass> defaultValue) {
-    List<DexType> specificTypes = getClassNames().asSpecificDexTypes();
-    if (specificTypes != null) {
-      return DexProgramClass.asProgramClasses(
-          specificTypes,
-          new DexDefinitionSupplier() {
-            @Override
-            public ClassResolutionResult contextIndependentDefinitionForWithResolutionResult(
-                DexType type) {
-              throw new Unreachable("Add support for multiple definitions with rule evaluation");
-            }
-
-            @Override
-            public DexClass definitionFor(DexType type) {
-              if (canReferenceDeadTypes) {
-                return appView.appInfo().definitionForWithoutExistenceAssert(type);
-              }
-              return appView.definitionFor(type);
-            }
-
-            @Override
-            public DexItemFactory dexItemFactory() {
-              return appView.dexItemFactory();
-            }
-          });
-    }
-    if (hasInheritanceClassName() && getInheritanceClassName().hasSpecificType()) {
+      Iterable<DexProgramClass> defaultValue,
+      Predicate<DexProgramClass> isRelevant) {
+    Iterable<DexType> specificTypes;
+    if (getClassNames().hasSpecificTypes()) {
+      specificTypes = getClassNames().getSpecificTypes();
+    } else if (hasInheritanceClassName() && getInheritanceClassName().hasSpecificType()) {
       DexType type = getInheritanceClassName().getSpecificType();
       if (appView.getVerticallyMergedClasses() != null
           && appView.getVerticallyMergedClasses().hasBeenMergedIntoSubtype(type)) {
         DexType target = appView.getVerticallyMergedClasses().getTargetFor(type);
-        DexClass clazz = appView.definitionFor(target);
-        assert clazz != null && clazz.isProgramClass();
-        return Iterables.concat(
-            ImmutableList.of(clazz.asProgramClass()),
-            DexProgramClass.asProgramClasses(subtypingInfo.subtypes(type), appView));
+        DexProgramClass clazz = asProgramClassOrNull(appView.definitionFor(target));
+        assert clazz != null;
+        specificTypes = IterableUtils.append(subtypingInfo.subtypes(type), clazz.getType());
       } else {
-        return DexProgramClass.asProgramClasses(subtypingInfo.subtypes(type), appView);
+        specificTypes = subtypingInfo.subtypes(type);
       }
+    } else {
+      return defaultValue;
     }
-    return defaultValue;
+    assert specificTypes != null;
+    return DexProgramClass.asProgramClasses(
+        specificTypes,
+        new DexDefinitionSupplier() {
+          @Override
+          public ClassResolutionResult contextIndependentDefinitionForWithResolutionResult(
+              DexType type) {
+            throw new Unreachable("Add support for multiple definitions with rule evaluation");
+          }
+
+          @Override
+          public DexProgramClass definitionFor(DexType type) {
+            DexProgramClass result =
+                asProgramClassOrNull(
+                    canReferenceDeadTypes
+                        ? appView.appInfo().definitionForWithoutExistenceAssert(type)
+                        : appView.definitionFor(type));
+            if (result != null && isRelevant.test(result)) {
+              return result;
+            }
+            return null;
+          }
+
+          @Override
+          public DexItemFactory dexItemFactory() {
+            return appView.dexItemFactory();
+          }
+        });
   }
 
   abstract String typeString();
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
index 46ef4f1..d2e7a82 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
@@ -346,11 +346,10 @@
 
     void runPerRule(TaskCollection<?> tasks, ProguardConfigurationRule rule, ProguardIfRule ifRule)
         throws ExecutionException {
-      List<DexType> specifics = rule.getClassNames().asSpecificDexTypes();
-      if (specifics != null) {
+      if (rule.getClassNames().hasSpecificTypes()) {
         // This keep rule only lists specific type matches.
         // This means there is no need to iterate over all classes.
-        for (DexType type : specifics) {
+        for (DexType type : rule.getClassNames().getSpecificTypes()) {
           DexClass clazz = application.definitionFor(type);
           // Ignore keep rule iff it does not reference a class in the app.
           if (clazz != null) {
@@ -363,7 +362,8 @@
       tasks.submit(
           () -> {
             for (DexProgramClass clazz :
-                rule.relevantCandidatesForRule(appView, subtypingInfo, application.classes())) {
+                rule.relevantCandidatesForRule(
+                    appView, subtypingInfo, application.classes(), alwaysTrue())) {
               process(clazz, rule, ifRule);
             }
             if (rule.applyToNonProgramClasses()) {
diff --git a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
index 564fd19..9b6bc09 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -258,7 +258,7 @@
     assertEquals(1, rules.size());
     assertEquals(ProguardClassType.CLASS, rules.get(0).getClassType());
     assertEquals(1, rules.get(0).getClassNames().size());
-    List<DexType> classTypes = rules.get(0).getClassNames().asSpecificDexTypes();
+    List<DexType> classTypes = rules.get(0).getClassNames().getSpecificTypes();
     assertEquals(1, classTypes.size());
     assertSame(dexItemFactory.createType("L-package-/-ClassNameWithDash-;"), classTypes.get(0));
     ProguardConfigurationRule rule = rules.get(0);
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/IfSimilarClassSpecificationBundlingTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/IfSimilarClassSpecificationBundlingTest.java
index 106fffa..4b9155a 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/IfSimilarClassSpecificationBundlingTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/IfSimilarClassSpecificationBundlingTest.java
@@ -92,8 +92,8 @@
   public void testBundlingOfIfRulesWithNonConstantSequent()
       throws IOException, CompilationFailedException, ExecutionException {
     runTest(
-        22,
-        36,
+        14,
+        18,
         "-if class **$R* { int keepA; }",
         "-keep class"
             + " com.android.tools.r8.shaking.ifrule.IfSimilarClassSpecificationBundlingTest$<2> {"