Misc. prerequisites for merging subsequent if rules

Change-Id: I256d835d64ae8655c0d6d8a8043de00f308dc7b9
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 25c9cbf..37c0cf3 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java
@@ -3,20 +3,22 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
+import static com.google.common.base.Predicates.alwaysTrue;
+
 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;
+import com.google.common.collect.Sets;
 import it.unimi.dsi.fastutil.objects.Object2BooleanArrayMap;
 import it.unimi.dsi.fastutil.objects.Object2BooleanMap;
 import it.unimi.dsi.fastutil.objects.Object2BooleanMap.Entry;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Predicate;
@@ -85,20 +87,26 @@
 
   public abstract boolean hasSpecificTypes();
 
-  public abstract List<DexType> getSpecificTypes();
+  public abstract Set<DexType> getSpecificTypes();
 
   public abstract boolean matches(DexType type);
 
-  protected Iterable<ProguardWildcard> getWildcards() {
-    return Collections::emptyIterator;
+  protected final Iterable<ProguardWildcard> getWildcards() {
+    return getWildcardsThatMatches(alwaysTrue());
+  }
+
+  protected <T extends ProguardWildcard> Iterable<T> getWildcardsThatMatches(
+      Predicate<? super ProguardWildcard> predicate) {
+    return IterableUtils.empty();
   }
 
   public boolean hasWildcards() {
     return getWildcards().iterator().hasNext();
   }
 
-  static Iterable<ProguardWildcard> getWildcardsOrEmpty(ProguardClassNameList nameList) {
-    return nameList == null ? Collections::emptyIterator : nameList.getWildcards();
+  static <T extends ProguardWildcard> Iterable<T> getWildcardsThatMatchesOrEmpty(
+      ProguardClassNameList nameList, Predicate<? super ProguardWildcard> predicate) {
+    return nameList != null ? nameList.getWildcardsThatMatches(predicate) : IterableUtils.empty();
   }
 
   protected ProguardClassNameList materialize(DexItemFactory dexItemFactory) {
@@ -162,7 +170,7 @@
     }
 
     @Override
-    public List<DexType> getSpecificTypes() {
+    public Set<DexType> getSpecificTypes() {
       return null;
     }
 
@@ -220,14 +228,18 @@
 
     @Override
     public boolean hasSpecificTypes() {
-      return className.hasSpecificType();
+      return className.hasSpecificType() || className.hasSpecificTypes();
     }
 
     @Override
-    public List<DexType> getSpecificTypes() {
-      return className.hasSpecificType()
-          ? Collections.singletonList(className.getSpecificType())
-          : null;
+    public Set<DexType> getSpecificTypes() {
+      if (className.hasSpecificType()) {
+        return Collections.singleton(className.getSpecificType());
+      }
+      if (className.hasSpecificTypes()) {
+        return className.getSpecificTypes();
+      }
+      return null;
     }
 
     @Override
@@ -236,8 +248,9 @@
     }
 
     @Override
-    protected Iterable<ProguardWildcard> getWildcards() {
-      return className.getWildcards();
+    protected <T extends ProguardWildcard> Iterable<T> getWildcardsThatMatches(
+        Predicate<? super ProguardWildcard> predicate) {
+      return className.getWildcardsThatMatches(predicate);
     }
 
     @Override
@@ -261,7 +274,7 @@
 
     private final ImmutableList<ProguardTypeMatcher> classNames;
 
-    private List<DexType> specificTypes;
+    private Set<DexType> specificTypes;
 
     private PositiveClassNameList(Collection<ProguardTypeMatcher> classNames) {
       this.classNames = ImmutableList.copyOf(classNames);
@@ -308,12 +321,22 @@
     }
 
     @Override
-    public List<DexType> getSpecificTypes() {
+    public Set<DexType> getSpecificTypes() {
       if (specificTypes == null) {
-        specificTypes =
-            classNames.stream().allMatch(ProguardTypeMatcher::hasSpecificType)
-                ? ListUtils.map(classNames, ProguardTypeMatcher::getSpecificType)
-                : Collections.emptyList();
+        if (Iterables.all(
+            classNames, className -> className.hasSpecificType() || className.hasSpecificTypes())) {
+          specificTypes = Sets.newIdentityHashSet();
+          for (var className : classNames) {
+            if (className.hasSpecificType()) {
+              specificTypes.add(className.getSpecificType());
+            } else {
+              assert className.hasSpecificTypes();
+              specificTypes.addAll(className.getSpecificTypes());
+            }
+          }
+        } else {
+          specificTypes = Collections.emptySet();
+        }
       }
       return specificTypes.isEmpty() ? null : specificTypes;
     }
@@ -325,8 +348,10 @@
     }
 
     @Override
-    protected Iterable<ProguardWildcard> getWildcards() {
-      return IterableUtils.flatMap(classNames, ProguardTypeMatcher::getWildcards);
+    protected <T extends ProguardWildcard> Iterable<T> getWildcardsThatMatches(
+        Predicate<? super ProguardWildcard> predicate) {
+      return IterableUtils.flatMap(
+          classNames, className -> className.getWildcardsThatMatches(predicate));
     }
 
     @Override
@@ -406,7 +431,7 @@
     }
 
     @Override
-    public List<DexType> getSpecificTypes() {
+    public Set<DexType> getSpecificTypes() {
       return null;
     }
 
@@ -424,8 +449,10 @@
     }
 
     @Override
-    protected Iterable<ProguardWildcard> getWildcards() {
-      return IterableUtils.flatMap(classNames.keySet(), ProguardTypeMatcher::getWildcards);
+    protected <T extends ProguardWildcard> Iterable<T> getWildcardsThatMatches(
+        Predicate<? super ProguardWildcard> predicate) {
+      return IterableUtils.flatMap(
+          classNames.keySet(), className -> className.getWildcardsThatMatches(predicate));
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java b/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java
index 0700049..63e6b5a 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java
@@ -252,6 +252,14 @@
     return source;
   }
 
+  public boolean hasMemberRules() {
+    return memberRules != null && !memberRules.isEmpty();
+  }
+
+  public ProguardMemberRule getMemberRule(int index) {
+    return memberRules.get(index);
+  }
+
   public List<ProguardMemberRule> getMemberRules() {
     return memberRules;
   }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index 5b11df3..ceb9671 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -1358,8 +1358,7 @@
               if (acceptString("<init>")) {
                 ProguardTypeMatcher typeMatcher =
                     ProguardTypeMatcher.create(first, ClassOrType.TYPE, dexItemFactory);
-                if (!typeMatcher.matchesSpecificType()
-                    || !typeMatcher.getSpecificType().isVoidType()) {
+                if (!typeMatcher.hasSpecificType() || !typeMatcher.getSpecificType().isVoidType()) {
                   throw parseError("Expected [access-flag]* void <init>");
                 }
                 ruleBuilder.setRuleType(ProguardMemberType.INIT);
@@ -1369,8 +1368,7 @@
               } else if (acceptString("<clinit>")) {
                 ProguardTypeMatcher typeMatcher =
                     ProguardTypeMatcher.create(first, ClassOrType.TYPE, dexItemFactory);
-                if (!typeMatcher.matchesSpecificType()
-                    || !typeMatcher.getSpecificType().isVoidType()) {
+                if (!typeMatcher.hasSpecificType() || !typeMatcher.getSpecificType().isVoidType()) {
                   throw parseError("Expected [access-flag]* void <clinit>");
                 }
                 ruleBuilder.setRuleType(ProguardMemberType.CLINIT);
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 4e80401..29933a5 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.shaking;
 
 import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
+import static com.google.common.base.Predicates.alwaysTrue;
 
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
@@ -16,11 +17,11 @@
 import com.android.tools.r8.graph.SubtypingInfo;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.position.Position;
+import com.android.tools.r8.shaking.ProguardWildcard.BackReference;
 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.Iterables;
-import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 import java.util.function.Predicate;
@@ -213,16 +214,29 @@
     return false;
   }
 
-  protected Iterable<ProguardWildcard> getWildcards() {
-    List<ProguardMemberRule> memberRules = getMemberRules();
+  protected boolean hasBackReferences() {
+    return !Iterables.isEmpty(getBackReferences());
+  }
+
+  public Iterable<BackReference> getBackReferences() {
+    return getWildcardsThatMatches(ProguardWildcard::isBackReference);
+  }
+
+  protected final Iterable<ProguardWildcard> getWildcards() {
+    return getWildcardsThatMatches(alwaysTrue());
+  }
+
+  protected <T extends ProguardWildcard> Iterable<T> getWildcardsThatMatches(
+      Predicate<? super ProguardWildcard> predicate) {
     return Iterables.concat(
-        ProguardTypeMatcher.getWildcardsOrEmpty(getClassAnnotations()),
-        ProguardClassNameList.getWildcardsOrEmpty(getClassNames()),
-        ProguardTypeMatcher.getWildcardsOrEmpty(getInheritanceAnnotations()),
-        ProguardTypeMatcher.getWildcardsOrEmpty(getInheritanceClassName()),
-        memberRules == null
-            ? Collections::emptyIterator
-            : IterableUtils.flatMap(memberRules, ProguardMemberRule::getWildcards));
+        ProguardTypeMatcher.getWildcardsThatMatchesOrEmpty(getClassAnnotations(), predicate),
+        ProguardClassNameList.getWildcardsThatMatchesOrEmpty(getClassNames(), predicate),
+        ProguardTypeMatcher.getWildcardsThatMatchesOrEmpty(getInheritanceAnnotations(), predicate),
+        ProguardTypeMatcher.getWildcardsThatMatchesOrEmpty(getInheritanceClassName(), predicate),
+        hasMemberRules()
+            ? IterableUtils.flatMap(
+                getMemberRules(), memberRule -> memberRule.getWildcardsThatMatches(predicate))
+            : IterableUtils.empty());
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java
index cb3757c..4953a20 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java
@@ -14,6 +14,7 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
 public class ProguardIfRule extends ProguardKeepRuleBase {
@@ -132,8 +133,11 @@
   }
 
   @Override
-  protected Iterable<ProguardWildcard> getWildcards() {
-    return Iterables.concat(super.getWildcards(), subsequentRule.getWildcards());
+  protected <T extends ProguardWildcard> Iterable<T> getWildcardsThatMatches(
+      Predicate<? super ProguardWildcard> predicate) {
+    return Iterables.concat(
+        super.getWildcardsThatMatches(predicate),
+        subsequentRule.getWildcardsThatMatches(predicate));
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java
index 326a95e..85a96c2 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java
@@ -44,7 +44,7 @@
     }
   }
 
-  protected ProguardKeepRule(
+  public ProguardKeepRule(
       Origin origin,
       Position position,
       String source,
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java
index 0720337..5ee286d 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java
@@ -3,6 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
+import static com.google.common.base.Predicates.alwaysTrue;
+
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClassAndField;
@@ -19,6 +21,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.function.Consumer;
+import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
 public class ProguardMemberRule {
@@ -114,7 +117,7 @@
   private final List<ProguardTypeMatcher> arguments;
   private final ProguardMemberRuleReturnValue returnValue;
 
-  private ProguardMemberRule(
+  public ProguardMemberRule(
       List<ProguardTypeMatcher> annotations,
       ProguardAccessFlags accessFlags,
       ProguardAccessFlags negatedAccessFlags,
@@ -156,6 +159,10 @@
     return ruleType;
   }
 
+  public boolean hasType() {
+    return type != null;
+  }
+
   public ProguardTypeMatcher getType() {
     return type;
   }
@@ -176,10 +183,6 @@
     return returnValue;
   }
 
-  public ProguardTypeMatcher getTypeMatcher() {
-    return type;
-  }
-
   public boolean matches(
       DexClassAndField field,
       AppView<?> appView,
@@ -322,14 +325,24 @@
     }
   }
 
-  Iterable<ProguardWildcard> getWildcards() {
+  public boolean hasBackReference() {
+    return IterableUtils.hasNext(getWildcardsThatMatches(ProguardWildcard::isBackReference));
+  }
+
+  final Iterable<ProguardWildcard> getWildcards() {
+    return getWildcardsThatMatches(alwaysTrue());
+  }
+
+  <T extends ProguardWildcard> Iterable<T> getWildcardsThatMatches(
+      Predicate<? super ProguardWildcard> predicate) {
     return Iterables.concat(
-        ProguardTypeMatcher.getWildcardsOrEmpty(annotations),
-        ProguardTypeMatcher.getWildcardsOrEmpty(type),
-        ProguardNameMatcher.getWildcardsOrEmpty(name),
+        ProguardTypeMatcher.getWildcardsThatMatchesOrEmpty(annotations, predicate),
+        ProguardTypeMatcher.getWildcardsThatMatchesOrEmpty(type, predicate),
+        ProguardNameMatcher.getWildcardsThatMatchesOrEmpty(name, predicate),
         arguments == null
-            ? Collections::emptyIterator
-            : IterableUtils.flatMap(arguments, ProguardTypeMatcher::getWildcards));
+            ? IterableUtils.empty()
+            : IterableUtils.flatMap(
+                arguments, argument -> argument.getWildcardsThatMatches(predicate)));
   }
 
   ProguardMemberRule materialize(DexItemFactory dexItemFactory) {
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardNameMatcher.java b/src/main/java/com/android/tools/r8/shaking/ProguardNameMatcher.java
index c217d45..ecd43b1 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardNameMatcher.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardNameMatcher.java
@@ -3,12 +3,15 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
+import static com.google.common.base.Predicates.alwaysTrue;
+
 import com.android.tools.r8.shaking.ProguardConfigurationParser.IdentifierPatternWithWildcards;
 import com.android.tools.r8.shaking.ProguardWildcard.BackReference;
 import com.android.tools.r8.shaking.ProguardWildcard.Pattern;
-import com.google.common.collect.ImmutableList;
+import com.android.tools.r8.utils.IterableUtils;
 import java.util.Collections;
 import java.util.List;
+import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
 public abstract class ProguardNameMatcher {
@@ -89,12 +92,20 @@
 
   public abstract boolean matches(String name);
 
-  protected Iterable<ProguardWildcard> getWildcards() {
-    return Collections::emptyIterator;
+  protected final Iterable<ProguardWildcard> getWildcards() {
+    return getWildcardsThatMatches(alwaysTrue());
   }
 
-  static Iterable<ProguardWildcard> getWildcardsOrEmpty(ProguardNameMatcher nameMatcher) {
-    return nameMatcher == null ? Collections::emptyIterator : nameMatcher.getWildcards();
+  protected <T extends ProguardWildcard> Iterable<T> getWildcardsThatMatches(
+      Predicate<? super ProguardWildcard> predicate) {
+    return IterableUtils.empty();
+  }
+
+  static <T extends ProguardWildcard> Iterable<T> getWildcardsThatMatchesOrEmpty(
+      ProguardNameMatcher nameMatcher, Predicate<? super ProguardWildcard> predicate) {
+    return nameMatcher != null
+        ? nameMatcher.getWildcardsThatMatches(predicate)
+        : IterableUtils.empty();
   }
 
   protected ProguardNameMatcher materialize() {
@@ -119,8 +130,9 @@
     }
 
     @Override
-    protected Iterable<ProguardWildcard> getWildcards() {
-      return ImmutableList.of(wildcard);
+    protected <T extends ProguardWildcard> Iterable<T> getWildcardsThatMatches(
+        Predicate<? super ProguardWildcard> predicate) {
+      return predicate.test(wildcard) ? Collections.singleton((T) wildcard) : IterableUtils.empty();
     }
 
     @Override
@@ -129,6 +141,16 @@
     }
 
     @Override
+    public boolean equals(Object obj) {
+      return obj instanceof MatchAllNames;
+    }
+
+    @Override
+    public int hashCode() {
+      return getClass().hashCode();
+    }
+
+    @Override
     public String toString() {
       return "*";
     }
@@ -154,8 +176,9 @@
     }
 
     @Override
-    protected Iterable<ProguardWildcard> getWildcards() {
-      return wildcards;
+    protected <T extends ProguardWildcard> Iterable<T> getWildcardsThatMatches(
+        Predicate<? super ProguardWildcard> predicate) {
+      return IterableUtils.filter(wildcards, predicate);
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java b/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java
index f9aef9b..62b82f8 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.shaking;
 
 import static com.android.tools.r8.utils.DescriptorUtils.javaTypeToDescriptor;
+import static com.google.common.base.Predicates.alwaysTrue;
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexItemFactory;
@@ -12,13 +13,18 @@
 import com.android.tools.r8.shaking.ProguardWildcard.BackReference;
 import com.android.tools.r8.shaking.ProguardWildcard.Pattern;
 import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.IterableUtils;
 import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.StringUtils.BraceType;
 import com.google.common.collect.ImmutableList;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
 
 public abstract class ProguardTypeMatcher {
 
@@ -60,18 +66,41 @@
     return false;
   }
 
-  protected Iterable<ProguardWildcard> getWildcards() {
-    return Collections::emptyIterator;
+  protected void forEachWildcard(Consumer<? super ProguardWildcard> consumer) {
+    // Intentionally empty.
   }
 
-  static Iterable<ProguardWildcard> getWildcardsOrEmpty(ProguardTypeMatcher typeMatcher) {
-    return typeMatcher == null ? Collections::emptyIterator : typeMatcher.getWildcards();
+  protected final Iterable<ProguardWildcard> getWildcards() {
+    return getWildcardsThatMatches(alwaysTrue());
   }
 
-  static Iterable<ProguardWildcard> getWildcardsOrEmpty(List<ProguardTypeMatcher> typeMatchers) {
-    List<ProguardWildcard> result = new ArrayList<>();
+  protected <T extends ProguardWildcard> Iterable<T> getWildcardsThatMatches(
+      Predicate<? super ProguardWildcard> predicate) {
+    return IterableUtils.empty();
+  }
+
+  protected static <T extends ProguardWildcard> Iterable<T> getWildcardsThatMatchesOrEmpty(
+      ProguardTypeMatcher typeMatcher, Predicate<? super ProguardWildcard> predicate) {
+    return typeMatcher != null
+        ? typeMatcher.getWildcardsThatMatches(predicate)
+        : IterableUtils.empty();
+  }
+
+  @SuppressWarnings("unchecked")
+  static <T extends ProguardWildcard> Iterable<T> getWildcardsThatMatchesOrEmpty(
+      List<ProguardTypeMatcher> typeMatchers, Predicate<? super ProguardWildcard> predicate) {
+    if (typeMatchers.isEmpty()) {
+      return IterableUtils.empty();
+    }
+    List<T> result = new ArrayList<>();
+    Consumer<ProguardWildcard> fn =
+        wildcard -> {
+          if (predicate.test(wildcard)) {
+            result.add((T) wildcard);
+          }
+        };
     for (ProguardTypeMatcher typeMatcher : typeMatchers) {
-      typeMatcher.getWildcards().forEach(result::add);
+      typeMatcher.forEachWildcard(fn);
     }
     return result;
   }
@@ -149,11 +178,17 @@
   }
 
   public DexType getSpecificType() {
+    assert false;
     return null;
   }
 
-  public final boolean matchesSpecificType() {
-    return getSpecificType() != null;
+  public boolean hasSpecificTypes() {
+    return false;
+  }
+
+  public Set<DexType> getSpecificTypes() {
+    assert false;
+    return null;
   }
 
   private static class MatchAllTypes extends ProguardTypeMatcher {
@@ -182,8 +217,15 @@
     }
 
     @Override
-    protected Iterable<ProguardWildcard> getWildcards() {
-      return ImmutableList.of(wildcard);
+    protected void forEachWildcard(Consumer<? super ProguardWildcard> consumer) {
+      consumer.accept(wildcard);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    protected <T extends ProguardWildcard> Iterable<T> getWildcardsThatMatches(
+        Predicate<? super ProguardWildcard> predicate) {
+      return predicate.test(wildcard) ? Collections.singleton((T) wildcard) : IterableUtils.empty();
     }
 
     @Override
@@ -272,8 +314,15 @@
     }
 
     @Override
-    protected Iterable<ProguardWildcard> getWildcards() {
-      return ImmutableList.of(wildcard);
+    protected void forEachWildcard(Consumer<? super ProguardWildcard> consumer) {
+      consumer.accept(wildcard);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    protected <T extends ProguardWildcard> Iterable<T> getWildcardsThatMatches(
+        Predicate<? super ProguardWildcard> predicate) {
+      return predicate.test(wildcard) ? Collections.singleton((T) wildcard) : IterableUtils.empty();
     }
 
     @Override
@@ -321,8 +370,15 @@
     }
 
     @Override
-    protected Iterable<ProguardWildcard> getWildcards() {
-      return ImmutableList.of(wildcard);
+    protected void forEachWildcard(Consumer<? super ProguardWildcard> consumer) {
+      consumer.accept(wildcard);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    protected <T extends ProguardWildcard> Iterable<T> getWildcardsThatMatches(
+        Predicate<? super ProguardWildcard> predicate) {
+      return predicate.test(wildcard) ? Collections.singleton((T) wildcard) : IterableUtils.empty();
     }
 
     @Override
@@ -394,6 +450,52 @@
     }
   }
 
+  public static class MatchSpecificTypes extends ProguardTypeMatcher {
+
+    public final Set<DexType> types;
+
+    public MatchSpecificTypes(Set<DexType> types) {
+      this.types = types;
+    }
+
+    @Override
+    public boolean hasSpecificTypes() {
+      return true;
+    }
+
+    @Override
+    public Set<DexType> getSpecificTypes() {
+      return types;
+    }
+
+    @Override
+    public boolean matches(DexType type) {
+      return types.contains(type);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      if (this == obj) {
+        return true;
+      }
+      if (!(obj instanceof MatchSpecificTypes)) {
+        return false;
+      }
+      MatchSpecificTypes other = (MatchSpecificTypes) obj;
+      return types.equals(other.types);
+    }
+
+    @Override
+    public int hashCode() {
+      return types.hashCode();
+    }
+
+    @Override
+    public String toString() {
+      return StringUtils.join(", ", types, DexType::getTypeName, BraceType.TUBORG);
+    }
+  }
+
   private static class MatchTypePattern extends ProguardTypeMatcher {
 
     private final String pattern;
@@ -419,8 +521,14 @@
     }
 
     @Override
-    protected Iterable<ProguardWildcard> getWildcards() {
-      return wildcards;
+    protected void forEachWildcard(Consumer<? super ProguardWildcard> consumer) {
+      wildcards.forEach(consumer);
+    }
+
+    @Override
+    protected <T extends ProguardWildcard> Iterable<T> getWildcardsThatMatches(
+        Predicate<? super ProguardWildcard> predicate) {
+      return IterableUtils.filter(wildcards, predicate);
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardWildcard.java b/src/main/java/com/android/tools/r8/shaking/ProguardWildcard.java
index b6cb8e5..a672c96 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardWildcard.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardWildcard.java
@@ -77,7 +77,7 @@
     }
   }
 
-  static class BackReference extends ProguardWildcard {
+  public static class BackReference extends ProguardWildcard {
     // Back-reference is not referable, hence the other type, Pattern, here.
     Pattern reference;
     final int referenceIndex;
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 9b6bc09..c94ce9c 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -37,6 +37,7 @@
 import com.android.tools.r8.utils.AbortException;
 import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.InternalOptions.PackageObfuscationMode;
+import com.android.tools.r8.utils.IterableUtils;
 import com.android.tools.r8.utils.KeepingDiagnosticHandler;
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.Reporter;
@@ -55,6 +56,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
+import java.util.Set;
 import java.util.function.BiConsumer;
 import java.util.function.Function;
 import java.util.function.Predicate;
@@ -258,9 +260,11 @@
     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().getSpecificTypes();
+    Set<DexType> classTypes = rules.get(0).getClassNames().getSpecificTypes();
     assertEquals(1, classTypes.size());
-    assertSame(dexItemFactory.createType("L-package-/-ClassNameWithDash-;"), classTypes.get(0));
+    assertSame(
+        dexItemFactory.createType("L-package-/-ClassNameWithDash-;"),
+        IterableUtils.first(classTypes));
     ProguardConfigurationRule rule = rules.get(0);
     assertEquals(2, rule.getMemberRules().size());
     int matches = 0;