Merge commit '334da78fd9db078e556bf0e069c213ade19ea3da' into dev-release

Change-Id: Ie4e501631a7c3ce82289c969aa01d170e4648dc7
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/ContextDescriptor.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/ContextDescriptor.java
index 66bb6d5..eceec17 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/asm/ContextDescriptor.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/ContextDescriptor.java
@@ -6,12 +6,14 @@
 
 import static com.android.tools.r8.keepanno.ast.KeepTypePattern.fromDescriptor;
 
+import com.android.tools.r8.keepanno.asm.KeepEdgeReader.UserBindingsHelper;
+import com.android.tools.r8.keepanno.ast.KeepBindingReference;
+import com.android.tools.r8.keepanno.ast.KeepClassBindingReference;
 import com.android.tools.r8.keepanno.ast.KeepClassItemPattern;
 import com.android.tools.r8.keepanno.ast.KeepEdgeMetaInfo;
 import com.android.tools.r8.keepanno.ast.KeepFieldNamePattern;
 import com.android.tools.r8.keepanno.ast.KeepFieldPattern;
 import com.android.tools.r8.keepanno.ast.KeepFieldTypePattern;
-import com.android.tools.r8.keepanno.ast.KeepItemPattern;
 import com.android.tools.r8.keepanno.ast.KeepMemberItemPattern;
 import com.android.tools.r8.keepanno.ast.KeepMemberPattern;
 import com.android.tools.r8.keepanno.ast.KeepMethodNamePattern;
@@ -103,13 +105,14 @@
     return metaInfoBuilder;
   }
 
-  public KeepItemPattern toItemPattern() {
+  public KeepBindingReference defineBindingReference(UserBindingsHelper bindingsHelper) {
     KeepQualifiedClassNamePattern className =
         KeepQualifiedClassNamePattern.exactFromDescriptor(getClassDescriptor());
     KeepClassItemPattern classItem =
         KeepClassItemPattern.builder().setClassNamePattern(className).build();
+    KeepClassBindingReference classBinding = bindingsHelper.defineFreshClassBinding(classItem);
     if (isClassContext()) {
-      return classItem;
+      return classBinding;
     }
     KeepMemberPattern memberPattern;
     if (isMethodContext()) {
@@ -118,10 +121,12 @@
       assert isFieldContext();
       memberPattern = createFieldPatternFromDescriptor();
     }
-    return KeepMemberItemPattern.builder()
-        .setClassReference(classItem.toClassItemReference())
-        .setMemberPattern(memberPattern)
-        .build();
+    KeepMemberItemPattern memberItem =
+        KeepMemberItemPattern.builder()
+            .setClassBindingReference(classBinding)
+            .setMemberPattern(memberPattern)
+            .build();
+    return bindingsHelper.defineFreshMemberBinding(memberItem);
   }
 
   private KeepFieldPattern createFieldPatternFromDescriptor() {
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReader.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReader.java
index c6b6f64..641eb23 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReader.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReader.java
@@ -29,6 +29,7 @@
 import com.android.tools.r8.keepanno.ast.KeepBindings.KeepBindingSymbol;
 import com.android.tools.r8.keepanno.ast.KeepCheck;
 import com.android.tools.r8.keepanno.ast.KeepCheck.KeepCheckKind;
+import com.android.tools.r8.keepanno.ast.KeepClassBindingReference;
 import com.android.tools.r8.keepanno.ast.KeepClassItemPattern;
 import com.android.tools.r8.keepanno.ast.KeepClassItemReference;
 import com.android.tools.r8.keepanno.ast.KeepCondition;
@@ -45,9 +46,9 @@
 import com.android.tools.r8.keepanno.ast.KeepFieldTypePattern;
 import com.android.tools.r8.keepanno.ast.KeepInstanceOfPattern;
 import com.android.tools.r8.keepanno.ast.KeepItemPattern;
-import com.android.tools.r8.keepanno.ast.KeepItemReference;
 import com.android.tools.r8.keepanno.ast.KeepMemberAccessPattern;
 import com.android.tools.r8.keepanno.ast.KeepMemberAccessPattern.BuilderBase;
+import com.android.tools.r8.keepanno.ast.KeepMemberBindingReference;
 import com.android.tools.r8.keepanno.ast.KeepMemberItemPattern;
 import com.android.tools.r8.keepanno.ast.KeepMemberPattern;
 import com.android.tools.r8.keepanno.ast.KeepMethodAccessPattern;
@@ -76,6 +77,7 @@
 import java.util.Map;
 import java.util.function.BiPredicate;
 import java.util.function.Consumer;
+import java.util.function.Function;
 import java.util.function.Supplier;
 import org.objectweb.asm.AnnotationVisitor;
 import org.objectweb.asm.ClassReader;
@@ -216,13 +218,12 @@
         });
   }
 
-  private static KeepClassItemReference classReferenceFromName(String className) {
-    return KeepClassItemReference.fromClassNamePattern(
-        KeepQualifiedClassNamePattern.exact(className));
-  }
-
-  private static KeepConstraints getClassConstraintsOrDefault(KeepConstraints constraints) {
-    return constraints != null ? constraints : KeepConstraints.defaultConstraints();
+  private static KeepClassBindingReference classReferenceFromName(
+      String className, UserBindingsHelper bindingsHelper) {
+    return bindingsHelper.defineFreshClassBinding(
+        KeepClassItemPattern.builder()
+            .setClassNamePattern(KeepQualifiedClassNamePattern.exact(className))
+            .build());
   }
 
   /** Internal copy of the user-facing KeepItemKind */
@@ -376,11 +377,14 @@
         return new KeepEdgeVisitor(parsingContext, parent::accept, setContext);
       }
       if (descriptor.equals(AnnotationConstants.UsesReflection.DESCRIPTOR)) {
-        KeepClassItemPattern classItem =
-            KeepClassItemPattern.builder()
-                .setClassNamePattern(KeepQualifiedClassNamePattern.exact(className))
-                .build();
-        return new UsesReflectionVisitor(parsingContext, parent::accept, setContext, classItem);
+        return new UsesReflectionVisitor(
+            parsingContext,
+            parent::accept,
+            setContext,
+            bindingsHelper ->
+                KeepClassItemPattern.builder()
+                    .setClassNamePattern(KeepQualifiedClassNamePattern.exact(className))
+                    .build());
       }
       if (descriptor.equals(ForApi.DESCRIPTOR)) {
         return new ForApiClassVisitor(parsingContext, parent::accept, setContext, className);
@@ -450,7 +454,10 @@
     }
 
     private static KeepMemberItemPattern createMethodItemContext(
-        String className, String methodName, String methodDescriptor) {
+        String className,
+        String methodName,
+        String methodDescriptor,
+        UserBindingsHelper bindingsHelper) {
       String returnTypeDescriptor = Type.getReturnType(methodDescriptor).getDescriptor();
       Type[] argumentTypes = Type.getArgumentTypes(methodDescriptor);
       KeepMethodParametersPattern.Builder builder = KeepMethodParametersPattern.builder();
@@ -462,8 +469,9 @@
               ? KeepMethodReturnTypePattern.voidType()
               : KeepMethodReturnTypePattern.fromType(
                   KeepTypePattern.fromDescriptor(returnTypeDescriptor));
+
       return KeepMemberItemPattern.builder()
-          .setClassReference(classReferenceFromName(className))
+          .setClassBindingReference(classReferenceFromName(className, bindingsHelper))
           .setMemberPattern(
               KeepMethodPattern.builder()
                   .setNamePattern(KeepMethodNamePattern.exact(methodName))
@@ -519,14 +527,16 @@
             parsingContext,
             parent::accept,
             setContext,
-            createMethodItemContext(className, methodName, methodDescriptor));
+            bindingsHelper ->
+                createMethodItemContext(className, methodName, methodDescriptor, bindingsHelper));
       }
       if (descriptor.equals(AnnotationConstants.ForApi.DESCRIPTOR)) {
         return new ForApiMemberVisitor(
             parsingContext,
             parent::accept,
             setContext,
-            createMethodItemContext(className, methodName, methodDescriptor));
+            bindingsHelper ->
+                createMethodItemContext(className, methodName, methodDescriptor, bindingsHelper));
       }
       if (descriptor.equals(AnnotationConstants.UsedByReflection.DESCRIPTOR)
           || descriptor.equals(AnnotationConstants.UsedByNative.DESCRIPTOR)) {
@@ -534,14 +544,16 @@
             parsingContext,
             parent::accept,
             setContext,
-            createMethodItemContext(className, methodName, methodDescriptor));
+            bindingsHelper ->
+                createMethodItemContext(className, methodName, methodDescriptor, bindingsHelper));
       }
       if (descriptor.equals(AnnotationConstants.CheckRemoved.DESCRIPTOR)) {
         return new CheckRemovedMemberVisitor(
             parsingContext,
             parent::accept,
             setContext,
-            createMethodItemContext(className, methodName, methodDescriptor),
+            bindingsHelper ->
+                createMethodItemContext(className, methodName, methodDescriptor, bindingsHelper),
             KeepCheckKind.REMOVED);
       }
       if (descriptor.equals(AnnotationConstants.CheckOptimizedOut.DESCRIPTOR)) {
@@ -549,7 +561,8 @@
             parsingContext,
             parent::accept,
             setContext,
-            createMethodItemContext(className, methodName, methodDescriptor),
+            bindingsHelper ->
+                createMethodItemContext(className, methodName, methodDescriptor, bindingsHelper),
             KeepCheckKind.OPTIMIZED_OUT);
       }
       return null;
@@ -588,11 +601,14 @@
     }
 
     private static KeepMemberItemPattern createMemberItemContext(
-        String className, String fieldName, String fieldTypeDescriptor) {
+        String className,
+        String fieldName,
+        String fieldTypeDescriptor,
+        UserBindingsHelper bindingsHelper) {
       KeepFieldTypePattern typePattern =
           KeepFieldTypePattern.fromType(KeepTypePattern.fromDescriptor(fieldTypeDescriptor));
       return KeepMemberItemPattern.builder()
-          .setClassReference(classReferenceFromName(className))
+          .setClassBindingReference(classReferenceFromName(className, bindingsHelper))
           .setMemberPattern(
               KeepFieldPattern.builder()
                   .setNamePattern(KeepFieldNamePattern.exact(fieldName))
@@ -648,14 +664,16 @@
             parsingContext,
             parent::accept,
             setContext,
-            createMemberItemContext(className, fieldName, fieldTypeDescriptor));
+            bindingsHelper ->
+                createMemberItemContext(className, fieldName, fieldTypeDescriptor, bindingsHelper));
       }
       if (descriptor.equals(ForApi.DESCRIPTOR)) {
         return new ForApiMemberVisitor(
             parsingContext,
             parent::accept,
             setContext,
-            createMemberItemContext(className, fieldName, fieldTypeDescriptor));
+            bindingsHelper ->
+                createMemberItemContext(className, fieldName, fieldTypeDescriptor, bindingsHelper));
       }
       if (descriptor.equals(UsedByReflection.DESCRIPTOR)
           || descriptor.equals(AnnotationConstants.UsedByNative.DESCRIPTOR)) {
@@ -663,7 +681,8 @@
             parsingContext,
             parent::accept,
             setContext,
-            createMemberItemContext(className, fieldName, fieldTypeDescriptor));
+            bindingsHelper ->
+                createMemberItemContext(className, fieldName, fieldTypeDescriptor, bindingsHelper));
       }
       return null;
     }
@@ -674,10 +693,14 @@
     void accept(T result);
   }
 
-  private static class UserBindingsHelper {
+  public static class UserBindingsHelper {
     private final KeepBindings.Builder builder = KeepBindings.builder();
     private final Map<String, KeepBindingSymbol> userNames = new HashMap<>();
 
+    public boolean isUserBinding(KeepBindingSymbol symbol) {
+      return userNames.get(symbol.toString()) == symbol;
+    }
+
     public KeepBindingSymbol resolveUserBinding(String name) {
       return userNames.computeIfAbsent(name, builder::create);
     }
@@ -686,14 +709,65 @@
       builder.addBinding(resolveUserBinding(name), item);
     }
 
-    public KeepBindingSymbol defineFreshBinding(String name, KeepItemPattern item) {
+    public void replaceWithUserBinding(
+        String name, KeepBindingReference replacedBinding, ParsingContext parsingContext) {
+      if (isUserBinding(replacedBinding.getName())) {
+        // The language currently disallows aliasing bindings, thus a binding cannot directly be
+        // defined by a reference to another binding.
+        throw parsingContext.error(
+            "Invalid binding reference to '"
+                + replacedBinding
+                + "' in binding definition of '"
+                + name
+                + "'");
+      }
+      KeepItemPattern item = builder.deleteBinding(replacedBinding.getName());
+      defineUserBinding(name, item);
+    }
+
+    public KeepClassBindingReference defineFreshClassBinding(KeepClassItemPattern itemPattern) {
+      KeepBindingSymbol symbol = defineFreshBinding("CLASS", itemPattern);
+      return KeepClassBindingReference.forClass(symbol);
+    }
+
+    public KeepMemberBindingReference defineFreshMemberBinding(KeepMemberItemPattern itemPattern) {
+      KeepBindingSymbol symbol = defineFreshBinding("MEMBER", itemPattern);
+      return KeepMemberBindingReference.forMember(symbol);
+    }
+
+    public KeepBindingReference defineFreshItemBinding(String name, KeepItemPattern itemPattern) {
+      KeepBindingSymbol symbol = defineFreshBinding(name, itemPattern);
+      return KeepBindingReference.forItem(symbol, itemPattern);
+    }
+
+    private KeepBindingSymbol defineFreshBinding(String name, KeepItemPattern item) {
       KeepBindingSymbol symbol = builder.generateFreshSymbol(name);
       builder.addBinding(symbol, item);
       return symbol;
     }
 
-    public KeepItemPattern getItemForBinding(KeepBindingReference bindingReference) {
-      return builder.getItemForBinding(bindingReference.getName());
+    public KeepItemPattern getItem(KeepBindingReference bindingReference) {
+      return bindingReference.isClassType()
+          ? getClassItem(bindingReference.asClassBindingReference())
+          : getMemberItem(bindingReference.asMemberBindingReference());
+    }
+
+    public KeepClassItemPattern getClassItem(KeepClassBindingReference reference) {
+      KeepItemPattern item = builder.getItemForBinding(reference.getName());
+      assert item != null;
+      return item.asClassItemPattern();
+    }
+
+    public KeepMemberItemPattern getMemberItem(KeepMemberBindingReference reference) {
+      KeepItemPattern item = builder.getItemForBinding(reference.getName());
+      assert item != null;
+      return item.asMemberItemPattern();
+    }
+
+    public KeepClassItemPattern getClassItemForReference(KeepClassItemReference itemReference) {
+      return itemReference.isBindingReference()
+          ? getItem(itemReference.asBindingReference()).asClassItemPattern()
+          : itemReference.asClassItemPattern();
     }
 
     public KeepBindings build() {
@@ -829,15 +903,26 @@
                 "Invalid extracted annotation, must specify either an edge, a removed check, or an"
                     + " optimized-out check.");
       }
+      KeepEdgeMetaInfo metaInfo = context.applyToMetadata(KeepEdgeMetaInfo.builder()).build();
       KeepCheckKind kind = isCheckRemoved ? KeepCheckKind.REMOVED : KeepCheckKind.OPTIMIZED_OUT;
-      parent.accept(
-          KeepCheck.builder()
-              .setMetaInfo(context.applyToMetadata(KeepEdgeMetaInfo.builder()).build())
-              .setKind(kind)
-              .setItemPattern(context.toItemPattern())
-              .build());
+      UserBindingsHelper bindingsHelper = new UserBindingsHelper();
+      KeepBindingReference contextBinding = context.defineBindingReference(bindingsHelper);
+      parent.accept(buildKeepCheckFromItem(metaInfo, kind, contextBinding, bindingsHelper));
       super.visitEnd();
     }
+
+    private static KeepCheck buildKeepCheckFromItem(
+        KeepEdgeMetaInfo metaInfo,
+        KeepCheckKind kind,
+        KeepBindingReference itemReference,
+        UserBindingsHelper bindingsHelper) {
+      return KeepCheck.builder()
+          .setMetaInfo(metaInfo)
+          .setKind(kind)
+          .setBindings(bindingsHelper.build())
+          .setItemBindingReference(itemReference)
+          .build();
+    }
   }
 
   private static class KeepEdgeVisitor extends AnnotationVisitorBase {
@@ -952,6 +1037,12 @@
     }
 
     @Override
+    public boolean useBindingForClassAndMembers() {
+      // KeepForApi targets should be disjunctive CLASS_AND_MEMBERS
+      return false;
+    }
+
+    @Override
     public void visitEnd() {
       if (!getKind().isOnlyClass() && isDefaultMemberDeclaration()) {
         // If no member declarations have been made, set public & protected as the default.
@@ -964,15 +1055,14 @@
       // Default constraints should retain the expected meta-data, such as signatures, annotations
       // exception-throws etc.
       KeepConstraints defaultForApiConstraints = KeepConstraints.all();
-      Collection<KeepItemReference> items = getItemsWithoutBinding();
-      for (KeepItemReference item : items) {
-        if (item.isBindingReference()) {
-          throw parsingContext.error("cannot reference bindings");
-        }
+      Collection<KeepBindingReference> items = getItems();
+      for (KeepBindingReference bindingReference : items) {
+        KeepItemPattern item = bindingsHelper.getItem(bindingReference);
         KeepClassItemPattern classItemPattern = item.asClassItemPattern();
         if (classItemPattern == null) {
-          assert item.isMemberItemReference();
-          classItemPattern = item.asMemberItemPattern().getClassReference().asClassItemPattern();
+          assert item.isMemberItemPattern();
+          KeepClassItemReference classReference = item.asMemberItemPattern().getClassReference();
+          classItemPattern = bindingsHelper.getClassItemForReference(classReference);
         }
         String descriptor = KeepEdgeReaderUtils.getDescriptorFromClassTypeName(className);
         String itemDescriptor = classItemPattern.getClassNamePattern().getExactDescriptor();
@@ -987,7 +1077,7 @@
         }
         consequences.addTarget(
             KeepTarget.builder()
-                .setItemReference(item)
+                .setItemBindingReference(bindingReference)
                 .setConstraints(defaultForApiConstraints)
                 .build());
       }
@@ -1018,25 +1108,39 @@
         AnnotationParsingContext parsingContext,
         Parent<KeepEdge> parent,
         Consumer<KeepEdgeMetaInfo.Builder> addContext,
-        KeepMemberItemPattern context) {
+        Function<UserBindingsHelper, KeepMemberItemPattern> contextBuilder) {
       super(parsingContext);
       this.parsingContext = parsingContext;
       this.parent = parent;
       addContext.accept(metaInfoBuilder);
-      // Create a binding for the context such that the class and member are shared.
-      KeepClassItemPattern classContext = context.getClassReference().asClassItemPattern();
-      KeepBindingSymbol bindingSymbol = bindingsHelper.defineFreshBinding("CONTEXT", classContext);
-      KeepClassItemReference classReference =
-          KeepBindingReference.forClass(bindingSymbol).toClassItemReference();
-      consequences.addTarget(
-          KeepTarget.builder()
-              .setItemPattern(
-                  KeepMemberItemPattern.builder()
-                      .copyFrom(context)
-                      .setClassReference(classReference)
-                      .build())
-              .build());
-      consequences.addTarget(KeepTarget.builder().setItemReference(classReference).build());
+      KeepMemberItemPattern context = contextBuilder.apply(bindingsHelper);
+      KeepClassItemReference classReference = context.getClassReference();
+      if (classReference.isBindingReference()) {
+        consequences.addTarget(
+            KeepTarget.builder()
+                .setItemBindingReference(bindingsHelper.defineFreshMemberBinding(context))
+                .build());
+        consequences.addTarget(
+            KeepTarget.builder()
+                .setItemBindingReference(classReference.asBindingReference())
+                .build());
+      } else {
+        // Create a binding for the context such that the class and member are shared.
+        KeepClassItemPattern classContext = classReference.asClassItemPattern();
+        KeepClassBindingReference classBindingReference =
+            bindingsHelper.defineFreshClassBinding(classContext);
+        consequences.addTarget(
+            KeepTarget.builder()
+                .setItemBindingReference(
+                    bindingsHelper.defineFreshMemberBinding(
+                        KeepMemberItemPattern.builder()
+                            .copyFrom(context)
+                            .setClassBindingReference(classBindingReference)
+                            .build()))
+                .build());
+        consequences.addTarget(
+            KeepTarget.builder().setItemBindingReference(classBindingReference).build());
+      }
     }
 
     @Override
@@ -1200,34 +1304,24 @@
     }
 
     @Override
+    public boolean useBindingForClassAndMembers() {
+      // When annotating a class with UsedByReflection and including member patterns, don't
+      // require the member patterns to match to retain the class.
+      return false;
+    }
+
+    @Override
     public void visitEnd() {
       if (getKind() == null && !isDefaultMemberDeclaration()) {
         // If no explict kind is set and member declarations have been made, keep the class too.
         visitEnum(null, Kind.DESCRIPTOR, Kind.CLASS_AND_MEMBERS);
       }
       super.visitEnd();
-      Collection<KeepItemReference> items = getItemsWithoutBinding();
-      for (KeepItemReference item : items) {
-        if (item.isBindingReference()) {
-          // TODO(b/248408342): The edge can have preconditions so it should support bindings!
-          throw parsingContext.error("cannot reference bindings");
-        }
-        KeepItemPattern itemPattern = item.asItemPattern();
-        KeepClassItemPattern holderPattern =
-            itemPattern.isClassItemPattern()
-                ? itemPattern.asClassItemPattern()
-                : itemPattern.asMemberItemPattern().getClassReference().asClassItemPattern();
-        String descriptor = KeepEdgeReaderUtils.getDescriptorFromClassTypeName(className);
-        String itemDescriptor = holderPattern.getClassNamePattern().getExactDescriptor();
-        if (!descriptor.equals(itemDescriptor)) {
-          throw parsingContext.error("must reference its class context " + className);
-        }
-        if (!holderPattern.getInstanceOfPattern().isAny()) {
-          throw parsingContext.error("cannot define an 'extends' pattern.");
-        }
+      for (KeepBindingReference bindingReference : getItems()) {
+        verifyItemStructure(bindingReference);
         consequences.addTarget(
             KeepTarget.builder()
-                .setItemPattern(itemPattern)
+                .setItemBindingReference(bindingReference)
                 .setConstraints(
                     constraintsParser.getValueOrDefault(KeepConstraints.defaultConstraints()))
                 .build());
@@ -1239,6 +1333,23 @@
               .setConsequences(consequences.build())
               .build());
     }
+
+    private void verifyItemStructure(KeepBindingReference bindingReference) {
+      KeepItemPattern itemPattern = bindingsHelper.getItem(bindingReference);
+      KeepClassItemPattern holderPattern =
+          itemPattern.isClassItemPattern()
+              ? itemPattern.asClassItemPattern()
+              : bindingsHelper.getClassItemForReference(
+                  itemPattern.asMemberItemPattern().getClassReference());
+      String descriptor = KeepEdgeReaderUtils.getDescriptorFromClassTypeName(className);
+      String itemDescriptor = holderPattern.getClassNamePattern().getExactDescriptor();
+      if (!descriptor.equals(itemDescriptor)) {
+        throw parsingContext.error("must reference its class context " + className);
+      }
+      if (!holderPattern.getInstanceOfPattern().isAny()) {
+        throw parsingContext.error("cannot define an 'extends' pattern.");
+      }
+    }
   }
 
   /**
@@ -1250,7 +1361,7 @@
 
     private final ParsingContext parsingContext;
     private final Parent<KeepEdge> parent;
-    private final KeepItemPattern context;
+    private final KeepMemberItemPattern context;
     private final KeepEdge.Builder builder = KeepEdge.builder();
     private final KeepEdgeMetaInfo.Builder metaInfoBuilder = KeepEdgeMetaInfo.builder();
     private final UserBindingsHelper bindingsHelper = new UserBindingsHelper();
@@ -1262,11 +1373,11 @@
         AnnotationParsingContext parsingContext,
         Parent<KeepEdge> parent,
         Consumer<KeepEdgeMetaInfo.Builder> addContext,
-        KeepItemPattern context) {
+        Function<UserBindingsHelper, KeepMemberItemPattern> contextBuilder) {
       super(parsingContext);
       this.parsingContext = parsingContext;
       this.parent = parent;
-      this.context = context;
+      this.context = contextBuilder.apply(bindingsHelper);
       addContext.accept(metaInfoBuilder);
       constraintsParser = new ConstraintDeclarationParser(parsingContext);
     }
@@ -1320,8 +1431,9 @@
       if (kind.isOnlyClass()) {
         throw parsingContext.error("kind must include its member");
       }
-      assert context.isMemberItemPattern();
       KeepMemberItemPattern memberContext = context.asMemberItemPattern();
+      KeepMemberBindingReference contextBinding =
+          bindingsHelper.defineFreshMemberBinding(memberContext);
       if (kind.includesClass()) {
         consequences.addTarget(
             KeepTarget.builder().setItemReference(memberContext.getClassReference()).build());
@@ -1331,7 +1443,7 @@
           KeepTarget.builder()
               .setConstraints(
                   constraintsParser.getValueOrDefault(KeepConstraints.defaultConstraints()))
-              .setItemPattern(context)
+              .setItemBindingReference(contextBinding)
               .build());
       parent.accept(
           builder
@@ -1367,11 +1479,15 @@
         AnnotationParsingContext parsingContext,
         Parent<KeepEdge> parent,
         Consumer<KeepEdgeMetaInfo.Builder> addContext,
-        KeepItemPattern context) {
+        Function<UserBindingsHelper, KeepItemPattern> contextBuilder) {
       super(parsingContext);
       this.parsingContext = parsingContext;
       this.parent = parent;
-      preconditions.addCondition(KeepCondition.builder().setItemPattern(context).build());
+      KeepItemPattern context = contextBuilder.apply(bindingsHelper);
+      KeepBindingReference contextBinding =
+          bindingsHelper.defineFreshItemBinding("CONTEXT", context);
+      preconditions.addCondition(
+          KeepCondition.builder().setItemBindingReference(contextBinding).build());
       addContext.accept(metaInfoBuilder);
     }
 
@@ -1531,11 +1647,12 @@
 
     @Override
     public void visitEnd() {
+      UserBindingsHelper bindingsHelper = new UserBindingsHelper();
       KeepItemVisitorBase itemVisitor =
           new KeepItemVisitorBase(parsingContext) {
             @Override
             public UserBindingsHelper getBindingsHelper() {
-              throw parsingContext.error("bindings not supported");
+              return bindingsHelper;
             }
           };
       itemVisitor.visit(Item.className, className);
@@ -1544,7 +1661,8 @@
           KeepCheck.builder()
               .setMetaInfo(metaInfoBuilder.build())
               .setKind(kind)
-              .setItemPattern(itemVisitor.getItemReference().asItemPattern())
+              .setBindings(itemVisitor.getBindingsHelper().build())
+              .setItemBindingReference(itemVisitor.getItemReference())
               .build());
     }
   }
@@ -1553,19 +1671,20 @@
   private static class CheckRemovedMemberVisitor extends AnnotationVisitorBase {
 
     private final Parent<KeepDeclaration> parent;
-    private final KeepItemPattern context;
+    private final KeepMemberBindingReference context;
     private final KeepEdgeMetaInfo.Builder metaInfoBuilder = KeepEdgeMetaInfo.builder();
     private final KeepCheckKind kind;
+    private final UserBindingsHelper bindingsHelper = new UserBindingsHelper();
 
     CheckRemovedMemberVisitor(
         AnnotationParsingContext parsingContext,
         Parent<KeepDeclaration> parent,
         Consumer<KeepEdgeMetaInfo.Builder> addContext,
-        KeepItemPattern context,
+        Function<UserBindingsHelper, KeepMemberItemPattern> contextBuilder,
         KeepCheckKind kind) {
       super(parsingContext);
       this.parent = parent;
-      this.context = context;
+      this.context = bindingsHelper.defineFreshMemberBinding(contextBuilder.apply(bindingsHelper));
       this.kind = kind;
       addContext.accept(metaInfoBuilder);
     }
@@ -1583,20 +1702,17 @@
     public void visitEnd() {
       super.visitEnd();
       parent.accept(
-          KeepCheck.builder()
-              .setMetaInfo(metaInfoBuilder.build())
-              .setKind(kind)
-              .setItemPattern(context)
-              .build());
+          ExtractedAnnotationVisitor.buildKeepCheckFromItem(
+              metaInfoBuilder.build(), kind, context, bindingsHelper));
     }
   }
 
-  private static class ClassDeclarationParser extends DeclarationParser<KeepClassItemReference> {
+  private static class ClassDeclarationParser extends DeclarationParser<KeepClassBindingReference> {
 
     private final ParsingContext parsingContext;
     private final Supplier<UserBindingsHelper> getBindingsHelper;
 
-    private KeepClassItemReference boundClassItemReference = null;
+    private KeepClassBindingReference boundClassReference = null;
     private final ClassNameParser classNameParser;
     private final ClassNameParser annotatedByParser;
     private final InstanceOfParser instanceOfParser;
@@ -1635,7 +1751,7 @@
     }
 
     private boolean isBindingReferenceDefined() {
-      return boundClassItemReference != null;
+      return boundClassReference != null;
     }
 
     private boolean classPatternsAreDeclared() {
@@ -1656,37 +1772,41 @@
       return isBindingReferenceDefined() || super.isDeclared();
     }
 
-    public KeepClassItemReference getValue() {
+    public KeepClassBindingReference getValue() {
       checkAllowedDefinitions();
       if (isBindingReferenceDefined()) {
-        return boundClassItemReference;
+        return boundClassReference;
       }
+      KeepClassItemPattern classItemPattern;
       if (classPatternsAreDeclared()) {
-        return KeepClassItemPattern.builder()
-            .setClassNamePattern(
-                classNameParser.getValueOrDefault(KeepQualifiedClassNamePattern.any()))
-            .setAnnotatedByPattern(OptionalPattern.ofNullable(annotatedByParser.getValue()))
-            .setInstanceOfPattern(instanceOfParser.getValueOrDefault(KeepInstanceOfPattern.any()))
-            .build()
-            .toClassItemReference();
+        classItemPattern =
+            KeepClassItemPattern.builder()
+                .setClassNamePattern(
+                    classNameParser.getValueOrDefault(KeepQualifiedClassNamePattern.any()))
+                .setAnnotatedByPattern(OptionalPattern.ofNullable(annotatedByParser.getValue()))
+                .setInstanceOfPattern(
+                    instanceOfParser.getValueOrDefault(KeepInstanceOfPattern.any()))
+                .build();
+      } else {
+        assert isDefault();
+        classItemPattern = KeepClassItemPattern.any();
       }
-      assert isDefault();
-      return KeepClassItemPattern.any().toClassItemReference();
+      return getBindingsHelper.get().defineFreshClassBinding(classItemPattern);
     }
 
-    public void setBindingReference(KeepClassItemReference bindingReference) {
+    public void setBindingReference(KeepClassBindingReference bindingReference) {
       if (isBindingReferenceDefined()) {
         throw parsingContext.error(
             "Cannot reference multiple class bindings for a single class item");
       }
-      this.boundClassItemReference = bindingReference;
+      this.boundClassReference = bindingReference;
     }
 
     @Override
     public boolean tryParse(String name, Object value) {
       if (name.equals(Item.classFromBinding) && value instanceof String) {
         KeepBindingSymbol symbol = getBindingsHelper.get().resolveUserBinding((String) value);
-        setBindingReference(KeepBindingReference.forClass(symbol).toClassItemReference());
+        setBindingReference(KeepBindingReference.forClass(symbol));
         return true;
       }
       return super.tryParse(name, value);
@@ -1932,8 +2052,12 @@
 
     public abstract UserBindingsHelper getBindingsHelper();
 
-    // Constructed item available once visitEnd has been called.
-    private KeepItemReference itemReference = null;
+    public boolean useBindingForClassAndMembers() {
+      return true;
+    }
+
+    // Constructed bindings available once visitEnd has been called.
+    private List<KeepBindingReference> itemReferences = null;
 
     KeepItemVisitorBase(ParsingContext parsingContext) {
       super(parsingContext);
@@ -1942,88 +2066,21 @@
       memberDeclaration = new MemberDeclarationParser(parsingContext);
     }
 
-    public Collection<KeepItemReference> getItemsWithoutBinding() {
-      if (itemReference == null) {
-        throw parsingContext.error("Item reference not finalized. Missing call to visitEnd()");
+    public Collection<KeepBindingReference> getItems() {
+      if (itemReferences == null || kind == null) {
+        throw parsingContext.error("Items not finalized. Missing call to visitEnd()");
       }
-      if (itemReference.isBindingReference()) {
-        return Collections.singletonList(itemReference);
-      }
-      // Kind is only null if item is a "binding reference".
-      if (kind == null) {
-        throw parsingContext.error("Unexpected state: unknown kind for an item pattern");
-      }
-      if (kind.includesClassAndMembers()) {
-        assert !itemReference.isBindingReference();
-        KeepItemPattern itemPattern = itemReference.asItemPattern();
-        KeepClassItemReference classReference;
-        KeepMemberItemPattern memberPattern;
-        if (itemPattern.isClassItemPattern()) {
-          classReference = itemPattern.asClassItemPattern().toClassItemReference();
-          memberPattern =
-              KeepMemberItemPattern.builder()
-                  .setClassReference(classReference)
-                  .setMemberPattern(KeepMemberPattern.allMembers())
-                  .build();
-        } else {
-          memberPattern = itemPattern.asMemberItemPattern();
-          classReference = memberPattern.getClassReference();
-        }
-        return ImmutableList.of(classReference, memberPattern.toItemReference());
-      } else {
-        return Collections.singletonList(itemReference);
-      }
+      return itemReferences;
     }
 
-    public Collection<KeepItemReference> getItemsWithBinding() {
-      if (itemReference == null) {
+    public KeepBindingReference getItemReference() {
+      if (itemReferences == null) {
         throw parsingContext.error("Item reference not finalized. Missing call to visitEnd()");
       }
-      if (itemReference.isBindingReference()) {
-        return Collections.singletonList(itemReference);
+      if (itemReferences.size() > 1) {
+        throw parsingContext.error("Ambiguous item reference.");
       }
-      // Kind is only null if item is a "binding reference".
-      if (kind == null) {
-        throw parsingContext.error("Unexpected state: unknown kind for an item pattern");
-      }
-      if (kind.includesClassAndMembers()) {
-        KeepItemPattern itemPattern = itemReference.asItemPattern();
-        // Ensure we have a member item linked to the correct class.
-        KeepMemberItemPattern memberItemPattern;
-        if (itemPattern.isClassItemPattern()) {
-          memberItemPattern =
-              KeepMemberItemPattern.builder()
-                  .setClassReference(itemPattern.asClassItemPattern().toClassItemReference())
-                  .build();
-        } else {
-          memberItemPattern = itemPattern.asMemberItemPattern();
-        }
-        // If the class is not a binding, introduce the binding and rewrite the member.
-        KeepClassItemReference classItemReference = memberItemPattern.getClassReference();
-        if (classItemReference.isClassItemPattern()) {
-          KeepClassItemPattern classItemPattern = classItemReference.asClassItemPattern();
-          KeepBindingSymbol symbol =
-              getBindingsHelper().defineFreshBinding("CLASS", classItemPattern);
-          classItemReference = KeepBindingReference.forClass(symbol).toClassItemReference();
-          memberItemPattern =
-              KeepMemberItemPattern.builder()
-                  .copyFrom(memberItemPattern)
-                  .setClassReference(classItemReference)
-                  .build();
-        }
-        assert classItemReference.isBindingReference();
-        assert memberItemPattern.getClassReference().equals(classItemReference);
-        return ImmutableList.of(classItemReference, memberItemPattern.toItemReference());
-      } else {
-        return Collections.singletonList(itemReference);
-      }
-    }
-
-    public KeepItemReference getItemReference() {
-      if (itemReference == null) {
-        throw parsingContext.error("Item reference not finalized. Missing call to visitEnd()");
-      }
-      return itemReference;
+      return itemReferences.get(0);
     }
 
     public ItemKind getKind() {
@@ -2082,16 +2139,53 @@
       return super.visitArray(name);
     }
 
+    private void visitEndWithMemberBindingReference() {
+      if (classDeclaration.isDeclared() || memberDeclaration.isDeclared()) {
+        throw parsingContext.error(
+            "Cannot define an item explicitly and via a member-binding reference");
+      }
+      KeepBindingSymbol symbol = getBindingsHelper().resolveUserBinding(memberBindingReference);
+      KeepMemberBindingReference memberBinding = KeepBindingReference.forMember(symbol);
+
+      // If no explicit kind is set, the member binding implies it is just members.
+      if (kind == null) {
+        kind = ItemKind.ONLY_MEMBERS;
+      }
+
+      if (!kind.includesClass()) {
+        itemReferences = Collections.singletonList(memberBinding);
+        return;
+      }
+
+      KeepClassItemReference holderReference =
+          getBindingsHelper().getItem(memberBinding).asMemberItemPattern().getClassReference();
+      itemReferences =
+          ImmutableList.of(ensureCorrectBindingForMemberHolder(holderReference), memberBinding);
+    }
+
+    private KeepClassBindingReference ensureCorrectBindingForMemberHolder(
+        KeepClassItemReference holderReference) {
+      if (holderReference.isBindingReference()) {
+        KeepClassBindingReference bindingReference =
+            holderReference.asBindingReference().asClassBindingReference();
+        return ensureCorrectBindingForMemberHolder(bindingReference);
+      }
+      return getBindingsHelper().defineFreshClassBinding(holderReference.asClassItemPattern());
+    }
+
+    private KeepClassBindingReference ensureCorrectBindingForMemberHolder(
+        KeepClassBindingReference bindingReference) {
+      return useBindingForClassAndMembers()
+          ? bindingReference
+          : getBindingsHelper()
+              .defineFreshClassBinding(getBindingsHelper().getClassItem(bindingReference));
+    }
+
     @Override
     public void visitEnd() {
       // Item defined by binding reference.
       if (memberBindingReference != null) {
-        if (classDeclaration.isDeclared() || memberDeclaration.isDeclared() || kind != null) {
-          throw parsingContext.error(
-              "Cannot define an item explicitly and via a member-binding reference");
-        }
-        KeepBindingSymbol symbol = getBindingsHelper().resolveUserBinding(memberBindingReference);
-        itemReference = KeepBindingReference.forMember(symbol).toItemReference();
+        visitEndWithMemberBindingReference();
         return;
       }
 
@@ -2116,7 +2210,7 @@
         if (memberDeclaration.isDeclared()) {
           throw parsingContext.error("Item pattern for members is incompatible with kind " + kind);
         }
-        itemReference = classDeclaration.getValue();
+        itemReferences = Collections.singletonList(classDeclaration.getValue());
         return;
       }
 
@@ -2146,13 +2240,19 @@
         }
       }
 
-      KeepClassItemReference classReference = classDeclaration.getValue();
-      KeepItemPattern itemPattern =
-          KeepMemberItemPattern.builder()
-              .setClassReference(classReference)
-              .setMemberPattern(memberPattern)
-              .build();
-      itemReference = itemPattern.toItemReference();
+      ImmutableList.Builder<KeepBindingReference> builder = ImmutableList.builder();
+      KeepClassBindingReference classReference = classDeclaration.getValue();
+      builder.add(
+          getBindingsHelper()
+              .defineFreshMemberBinding(
+                  KeepMemberItemPattern.builder()
+                      .setClassBindingReference(classReference)
+                      .setMemberPattern(memberPattern)
+                      .build()));
+      if (kind.includesClass()) {
+        builder.add(ensureCorrectBindingForMemberHolder(classReference));
+      }
+      itemReferences = builder.build();
     }
   }
 
@@ -2185,18 +2285,8 @@
     @Override
     public void visitEnd() {
       super.visitEnd();
-      KeepItemReference item = getItemReference();
-      // The language currently disallows aliasing bindings, thus a binding cannot directly be
-      // defined by a reference to another binding.
-      if (item.isBindingReference()) {
-        throw parsingContext.error(
-            "Invalid binding reference to '"
-                + item.asBindingReference()
-                + "' in binding definition of '"
-                + bindingName
-                + "'");
-      }
-      helper.defineUserBinding(bindingName, item.asItemPattern());
+      KeepBindingReference bindingReference = getItemReference();
+      helper.replaceWithUserBinding(bindingName, bindingReference, parsingContext);
     }
   }
 
@@ -2243,8 +2333,8 @@
       super.visitEnd();
       builder.setConstraints(
           constraintsParser.getValueOrDefault(KeepConstraints.defaultConstraints()));
-      for (KeepItemReference item : getItemsWithBinding()) {
-        parent.accept(builder.setItemReference(item).build());
+      for (KeepBindingReference item : getItems()) {
+        parent.accept(builder.setItemBindingReference(item).build());
       }
     }
   }
@@ -2271,7 +2361,7 @@
     @Override
     public void visitEnd() {
       super.visitEnd();
-      parent.accept(KeepCondition.builder().setItemReference(getItemReference()).build());
+      parent.accept(KeepCondition.builder().setItemBindingReference(getItemReference()).build());
     }
   }
 
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepBindings.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepBindings.java
index df6a224..46ee664 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepBindings.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepBindings.java
@@ -3,6 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.keepanno.ast;
 
+import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.IdentityHashMap;
@@ -57,6 +59,28 @@
             .collect(Collectors.joining(", "));
   }
 
+  public void verify(KeepItemReference... references) {
+    verify(Arrays.asList(references));
+  }
+
+  public void verify(Collection<KeepItemReference> references) {
+    for (KeepItemReference reference : references) {
+      if (reference.isBindingReference()) {
+        KeepBindingReference bindingReference = reference.asBindingReference();
+        if (!bindings.containsKey(bindingReference.getName())) {
+          throw new KeepEdgeException("Unbound reference to " + bindingReference);
+        }
+      } else {
+        KeepItemPattern itemPattern = reference.asItemPattern();
+        for (KeepBindingReference bindingReference : itemPattern.getBindingReferences()) {
+          if (!bindings.containsKey(bindingReference.getName())) {
+            throw new KeepEdgeException("Unbound reference to " + bindingReference);
+          }
+        }
+      }
+    }
+  }
+
   /**
    * A unique binding.
    *
@@ -187,6 +211,14 @@
       return this;
     }
 
+    public KeepItemPattern deleteBinding(KeepBindingSymbol symbol) {
+      KeepItemPattern removed = bindings.remove(symbol);
+      if (removed == null) {
+        throw new KeepEdgeException("Invalid deletion of binding '" + symbol + "'");
+      }
+      return removed;
+    }
+
     public KeepItemPattern getItemForBinding(KeepBindingSymbol symbol) {
       return bindings.get(symbol);
     }
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepCheck.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepCheck.java
index 1b2e42d..4c21cad 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepCheck.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepCheck.java
@@ -14,7 +14,8 @@
 
     private KeepEdgeMetaInfo metaInfo = KeepEdgeMetaInfo.none();
     private KeepCheckKind kind = KeepCheckKind.REMOVED;
-    private KeepItemPattern itemPattern;
+    private KeepBindings bindings = KeepBindings.none();
+    private KeepItemReference itemReference;
 
     public Builder setMetaInfo(KeepEdgeMetaInfo metaInfo) {
       this.metaInfo = metaInfo;
@@ -26,16 +27,27 @@
       return this;
     }
 
-    public Builder setItemPattern(KeepItemPattern itemPattern) {
-      this.itemPattern = itemPattern;
+    public Builder setBindings(KeepBindings bindings) {
+      this.bindings = bindings;
+      return this;
+    }
+
+    public Builder setItemBindingReference(KeepBindingReference bindingReference) {
+      this.itemReference = bindingReference.toItemReference();
+      return this;
+    }
+
+    public Builder setItemReference(KeepItemReference itemReference) {
+      this.itemReference = itemReference;
       return this;
     }
 
     public KeepCheck build() {
-      if (itemPattern == null) {
+      if (itemReference == null) {
         throw new KeepEdgeException("KeepCheck must have an item pattern.");
       }
-      return new KeepCheck(metaInfo, kind, itemPattern);
+      bindings.verify(itemReference);
+      return new KeepCheck(metaInfo, kind, bindings, itemReference);
     }
   }
 
@@ -45,12 +57,18 @@
 
   private final KeepEdgeMetaInfo metaInfo;
   private final KeepCheckKind kind;
-  private final KeepItemPattern itemPattern;
+  private final KeepBindings bindings;
+  private final KeepItemReference itemReference;
 
-  private KeepCheck(KeepEdgeMetaInfo metaInfo, KeepCheckKind kind, KeepItemPattern itemPattern) {
+  private KeepCheck(
+      KeepEdgeMetaInfo metaInfo,
+      KeepCheckKind kind,
+      KeepBindings bindings,
+      KeepItemReference itemReference) {
     this.metaInfo = metaInfo;
     this.kind = kind;
-    this.itemPattern = itemPattern;
+    this.bindings = bindings;
+    this.itemReference = itemReference;
   }
 
   @Override
@@ -66,12 +84,23 @@
     return kind;
   }
 
+  public KeepBindings getBindings() {
+    return bindings;
+  }
+
+  public KeepItemReference getItemReference() {
+    return itemReference;
+  }
+
   public KeepItemPattern getItemPattern() {
-    return itemPattern;
+    if (itemReference.isBindingReference()) {
+      return bindings.get(itemReference.asBindingReference()).getItem();
+    }
+    return itemReference.asItemPattern();
   }
 
   @Override
   public String toString() {
-    return "KeepCheck{kind=" + kind + ", item=" + itemPattern + "}";
+    return "KeepCheck{kind=" + kind + ", item=" + itemReference + "}";
   }
 }
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepClassItemPattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepClassItemPattern.java
index 2ecadbc..a051b62 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepClassItemPattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepClassItemPattern.java
@@ -77,15 +77,6 @@
   }
 
   @Override
-  public KeepItemReference toItemReference() {
-    return toClassItemReference();
-  }
-
-  public final KeepClassItemReference toClassItemReference() {
-    return KeepClassItemReference.fromClassItemPattern(this);
-  }
-
-  @Override
   public Collection<KeepBindingReference> getBindingReferences() {
     return Collections.emptyList();
   }
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepClassItemReference.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepClassItemReference.java
index 34a97b6..b912471 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepClassItemReference.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepClassItemReference.java
@@ -15,16 +15,6 @@
     return new ClassBinding(bindingReference);
   }
 
-  public static KeepClassItemReference fromClassItemPattern(KeepClassItemPattern classItemPattern) {
-    return new ClassItem(classItemPattern);
-  }
-
-  public static KeepClassItemReference fromClassNamePattern(
-      KeepQualifiedClassNamePattern classNamePattern) {
-    return new ClassItem(
-        KeepClassItemPattern.builder().setClassNamePattern(classNamePattern).build());
-  }
-
   @Override
   public final KeepClassItemReference asClassItemReference() {
     return this;
@@ -85,50 +75,4 @@
       return bindingReference.toString();
     }
   }
-
-  private static class ClassItem extends KeepClassItemReference {
-    private final KeepClassItemPattern classItemPattern;
-
-    private ClassItem(KeepClassItemPattern classItemPattern) {
-      assert classItemPattern != null;
-      this.classItemPattern = classItemPattern;
-    }
-
-    @Override
-    public KeepItemPattern asItemPattern() {
-      return classItemPattern;
-    }
-
-    @Override
-    public KeepClassItemPattern asClassItemPattern() {
-      return classItemPattern;
-    }
-
-    @Override
-    public Collection<KeepBindingReference> getBindingReferences() {
-      return Collections.emptyList();
-    }
-
-    @Override
-    public boolean equals(Object o) {
-      if (this == o) {
-        return true;
-      }
-      if (!(o instanceof ClassItem)) {
-        return false;
-      }
-      ClassItem someItem = (ClassItem) o;
-      return classItemPattern.equals(someItem.classItemPattern);
-    }
-
-    @Override
-    public int hashCode() {
-      return classItemPattern.hashCode();
-    }
-
-    @Override
-    public String toString() {
-      return classItemPattern.toString();
-    }
-  }
 }
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepCondition.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepCondition.java
index e9f7be2..b4aa28a 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepCondition.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepCondition.java
@@ -23,13 +23,14 @@
 
     private Builder() {}
 
-    public Builder setItemReference(KeepItemReference item) {
-      this.item = item;
+    public Builder setItemBindingReference(KeepBindingReference item) {
+      this.item = item.toItemReference();
       return this;
     }
 
-    public Builder setItemPattern(KeepItemPattern itemPattern) {
-      return setItemReference(itemPattern.toItemReference());
+    public Builder setItemReference(KeepItemReference item) {
+      this.item = item;
+      return this;
     }
 
     public KeepCondition build() {
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepItemPattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepItemPattern.java
index a455344..f11578e 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepItemPattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepItemPattern.java
@@ -19,14 +19,10 @@
  */
 public abstract class KeepItemPattern {
 
-  public static KeepItemPattern anyClass() {
+  public static KeepClassItemPattern anyClass() {
     return KeepClassItemPattern.any();
   }
 
-  public static KeepItemPattern anyMember() {
-    return KeepMemberItemPattern.any();
-  }
-
   public boolean isClassItemPattern() {
     return asClassItemPattern() != null;
   }
@@ -45,8 +41,6 @@
 
   public abstract Collection<KeepBindingReference> getBindingReferences();
 
-  public abstract KeepItemReference toItemReference();
-
   public final <T> T apply(
       Function<KeepClassItemPattern, T> onClass, Function<KeepMemberItemPattern, T> onMember) {
     if (isClassItemPattern()) {
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMemberBindingReference.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMemberBindingReference.java
index 443ea55..a79578f 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMemberBindingReference.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMemberBindingReference.java
@@ -22,6 +22,10 @@
     return KeepMemberItemReference.fromBindingReference(this);
   }
 
+  public KeepMemberItemReference toMemberItemReference() {
+    return KeepMemberItemReference.fromBindingReference(this);
+  }
+
   @Override
   public String toString() {
     return "member-ref(" + super.toString() + ")";
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMemberItemPattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMemberItemPattern.java
index 5184528..3fbe013 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMemberItemPattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMemberItemPattern.java
@@ -9,18 +9,13 @@
 
 public class KeepMemberItemPattern extends KeepItemPattern {
 
-  public static KeepMemberItemPattern any() {
-    return builder().build();
-  }
-
   public static Builder builder() {
     return new Builder();
   }
 
   public static class Builder {
 
-    private KeepClassItemReference classReference =
-        KeepClassItemPattern.any().toClassItemReference();
+    private KeepClassItemReference classReference = null;
     private KeepMemberPattern memberPattern = KeepMemberPattern.allMembers();
 
     private Builder() {}
@@ -30,6 +25,11 @@
           .setMemberPattern(pattern.getMemberPattern());
     }
 
+    public Builder setClassBindingReference(KeepClassBindingReference classReference) {
+      this.classReference = classReference.toClassItemReference();
+      return this;
+    }
+
     public Builder setClassReference(KeepClassItemReference classReference) {
       this.classReference = classReference;
       return this;
@@ -41,6 +41,10 @@
     }
 
     public KeepMemberItemPattern build() {
+      if (classReference == null) {
+        throw new KeepEdgeException(
+            "Invalid attempt to build a member pattern without a class reference");
+      }
       return new KeepMemberItemPattern(classReference, memberPattern);
     }
   }
@@ -61,11 +65,6 @@
     return this;
   }
 
-  @Override
-  public KeepItemReference toItemReference() {
-    return KeepMemberItemReference.fromMemberItemPattern(this);
-  }
-
   public KeepClassItemReference getClassReference() {
     return classReference;
   }
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMemberItemReference.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMemberItemReference.java
index d2ab55d..aa71f27 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMemberItemReference.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMemberItemReference.java
@@ -11,10 +11,6 @@
     return new MemberBinding(bindingReference);
   }
 
-  public static KeepMemberItemReference fromMemberItemPattern(KeepMemberItemPattern itemPattern) {
-    return new MemberItem(itemPattern);
-  }
-
   @Override
   public final KeepMemberItemReference asMemberItemReference() {
     return this;
@@ -38,28 +34,4 @@
       return bindingReference.toString();
     }
   }
-
-  private static final class MemberItem extends KeepMemberItemReference {
-
-    private final KeepMemberItemPattern itemPattern;
-
-    public MemberItem(KeepMemberItemPattern itemPattern) {
-      this.itemPattern = itemPattern;
-    }
-
-    @Override
-    public KeepItemPattern asItemPattern() {
-      return itemPattern;
-    }
-
-    @Override
-    public KeepMemberItemPattern asMemberItemPattern() {
-      return itemPattern;
-    }
-
-    @Override
-    public String toString() {
-      return itemPattern.toString();
-    }
-  }
 }
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepTarget.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepTarget.java
index 8367dfe..cbf039e 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepTarget.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepTarget.java
@@ -14,13 +14,14 @@
 
     private Builder() {}
 
-    public Builder setItemReference(KeepItemReference item) {
-      this.item = item;
+    public Builder setItemBindingReference(KeepBindingReference reference) {
+      this.item = reference.toItemReference();
       return this;
     }
 
-    public Builder setItemPattern(KeepItemPattern itemPattern) {
-      return setItemReference(itemPattern.toItemReference());
+    public Builder setItemReference(KeepItemReference item) {
+      this.item = item;
+      return this;
     }
 
     public Builder setConstraints(KeepConstraints constraints) {
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractor.java b/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractor.java
index dba2375..3a618c9 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractor.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractor.java
@@ -6,10 +6,12 @@
 import com.android.tools.r8.keepanno.ast.KeepAttribute;
 import com.android.tools.r8.keepanno.ast.KeepBindingReference;
 import com.android.tools.r8.keepanno.ast.KeepBindings;
+import com.android.tools.r8.keepanno.ast.KeepBindings.Binding;
 import com.android.tools.r8.keepanno.ast.KeepBindings.KeepBindingSymbol;
 import com.android.tools.r8.keepanno.ast.KeepCheck;
 import com.android.tools.r8.keepanno.ast.KeepCheck.KeepCheckKind;
 import com.android.tools.r8.keepanno.ast.KeepClassItemPattern;
+import com.android.tools.r8.keepanno.ast.KeepClassItemReference;
 import com.android.tools.r8.keepanno.ast.KeepCondition;
 import com.android.tools.r8.keepanno.ast.KeepConstraints;
 import com.android.tools.r8.keepanno.ast.KeepDeclaration;
@@ -91,15 +93,24 @@
     Map<KeepBindingSymbol, KeepMemberPattern> memberPatterns;
     List<KeepBindingSymbol> targetMembers;
     KeepBindings.Builder builder = KeepBindings.builder();
-    KeepBindingSymbol symbol = builder.generateFreshSymbol("CLASS");
+    KeepBindingSymbol symbol;
     if (itemPattern.isClassItemPattern()) {
+      symbol = builder.generateFreshSymbol("CLASS");
       builder.addBinding(symbol, itemPattern);
       memberPatterns = Collections.emptyMap();
       targetMembers = Collections.emptyList();
     } else {
       KeepMemberItemPattern memberItemPattern = itemPattern.asMemberItemPattern();
-      assert memberItemPattern.getClassReference().isClassItemPattern();
-      builder.addBinding(symbol, memberItemPattern.getClassReference().asClassItemPattern());
+      KeepClassItemReference classReference = memberItemPattern.getClassReference();
+      if (classReference.isClassItemPattern()) {
+        symbol = builder.generateFreshSymbol("CLASS");
+        builder.addBinding(symbol, classReference.asClassItemPattern());
+      } else {
+        KeepBindingReference bindingReference = classReference.asBindingReference();
+        Binding binding = check.getBindings().get(bindingReference);
+        symbol = bindingReference.getName();
+        builder.addBinding(symbol, binding.getItem());
+      }
       KeepMemberPattern memberPattern = memberItemPattern.getMemberPattern();
       // This does not actually allocate a binding as the mapping is maintained in 'memberPatterns'.
       KeepBindingSymbol memberSymbol = new KeepBindingSymbol("MEMBERS");
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelDatabaseHelper.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelDatabaseHelper.java
index 58c1ae3..af5a909 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelDatabaseHelper.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelDatabaseHelper.java
@@ -22,6 +22,7 @@
     notModeledTypes.add("androidx.annotation.RecentlyNonNull");
     notModeledTypes.add("android.annotation.Nullable");
     notModeledTypes.add("android.annotation.NonNull");
+    notModeledTypes.add("android.annotation.FlaggedApi");
     return notModeledTypes;
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/Phi.java b/src/main/java/com/android/tools/r8/ir/code/Phi.java
index 7bafe1b..a6f39aa 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Phi.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Phi.java
@@ -200,15 +200,6 @@
     return operands;
   }
 
-  public boolean hasOperandThatMatches(Predicate<Value> predicate) {
-    for (Value operand : operands) {
-      if (predicate.test(operand)) {
-        return true;
-      }
-    }
-    return false;
-  }
-
   public void removeOperand(int index) {
     removeOperand(index, null, alwaysFalse());
   }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/DefaultFieldValueJoiner.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/DefaultFieldValueJoiner.java
index 45848ad..80cf637 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/DefaultFieldValueJoiner.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/DefaultFieldValueJoiner.java
@@ -7,8 +7,6 @@
 
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.Code;
-import com.android.tools.r8.graph.DefaultUseRegistryWithResult;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramField;
@@ -19,8 +17,6 @@
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
 import com.android.tools.r8.ir.analysis.value.AbstractValueFactory;
 import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.NewInstance;
 import com.android.tools.r8.ir.conversion.MethodConversionOptions;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteArrayTypeValueState;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteClassTypeValueState;
@@ -30,6 +26,7 @@
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.NonEmptyValueState;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.ValueState;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.IterableUtils;
 import com.android.tools.r8.utils.MapUtils;
 import com.android.tools.r8.utils.Pair;
 import com.android.tools.r8.utils.ThreadUtils;
@@ -38,7 +35,6 @@
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.Deque;
 import java.util.IdentityHashMap;
 import java.util.List;
@@ -48,7 +44,6 @@
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.function.Consumer;
-import java.util.function.Predicate;
 
 public class DefaultFieldValueJoiner {
 
@@ -74,9 +69,6 @@
     // Find all the fields where we need to determine if each field read is guaranteed to be
     // dominated by a write.
     Map<DexProgramClass, List<ProgramField>> fieldsOfInterest = getFieldsOfInterest();
-    if (fieldsOfInterest.isEmpty()) {
-      return Collections.emptyMap();
-    }
 
     // If constructor inlining is disabled, then we focus on whether each instance initializer
     // definitely assigns the given field before it is read. We do the same for final and static
@@ -102,12 +94,12 @@
     // constructor inlining, we find all new-instance instructions (including subtype allocations)
     // and check if the field is written on each allocation before it is possibly read.
     analyzeNewInstanceInstructions(
-        fieldsNotSubjectToInitializerAnalysis, fieldsWithLiveDefaultValue::add, executorService);
+        fieldsNotSubjectToInitializerAnalysis, fieldsWithLiveDefaultValue::add);
 
     return updateFlowGraphs(fieldsWithLiveDefaultValue, executorService);
   }
 
-  protected Map<DexProgramClass, List<ProgramField>> getFieldsOfInterest() {
+  private Map<DexProgramClass, List<ProgramField>> getFieldsOfInterest() {
     Map<DexProgramClass, List<ProgramField>> fieldsOfInterest = new IdentityHashMap<>();
     for (DexProgramClass clazz : appView.appInfo().classes()) {
       clazz.forEachProgramField(
@@ -248,101 +240,13 @@
 
   private void analyzeNewInstanceInstructions(
       Map<DexType, ProgramFieldSet> nonFinalInstanceFields,
-      Consumer<ProgramField> liveDefaultValueConsumer,
-      ExecutorService executorService)
-      throws ExecutionException {
-    // To simplify the analysis, we currently bail out for non-final classes.
-    // TODO(b/296030319): Handle non-final classes.
-    MapUtils.removeIf(
-        nonFinalInstanceFields,
-        (holderType, fields) -> {
-          assert !fields.isEmpty();
-          DexProgramClass holder = fields.iterator().next().getHolder();
-          // If the class is kept it could be instantiated directly, in which case all default field
-          // values could be live.
-          if (appView.getKeepInfo(holder).isPinned(appView.options())) {
-            fields.forEach(liveDefaultValueConsumer);
-            return true;
-          }
-          if (holder.isFinal() || !appView.appInfo().isInstantiatedIndirectly(holder)) {
-            // When the class is not explicitly marked final, the class could in principle have
-            // injected subclasses if it is pinned. However, none of the fields are pinned, so we
-            // should be allowed to reason about the field assignments in the program.
-            assert fields.stream()
-                .allMatch(
-                    field -> appView.getKeepInfo(field).isValuePropagationAllowed(appView, field));
-            return false;
-          }
-          fields.forEach(liveDefaultValueConsumer);
-          return true;
-        });
-
-    // We analyze all allocations of the classes that declare one of the given fields.
-    ThreadUtils.processMethods(
-        appView,
-        method ->
-            analyzeNewInstanceInstructionsInMethod(
-                nonFinalInstanceFields, liveDefaultValueConsumer, method),
-        appView.options().getThreadingModule(),
-        executorService);
-  }
-
-  private void analyzeNewInstanceInstructionsInMethod(
-      Map<DexType, ProgramFieldSet> nonFinalInstanceFields,
-      Consumer<ProgramField> liveDefaultValueConsumer,
-      ProgramMethod method) {
-    if (!maybeHasNewInstanceThatMatches(method, nonFinalInstanceFields::containsKey)) {
-      return;
+      Consumer<ProgramField> liveDefaultValueConsumer) {
+    // Conservatively treat all fields as maybe read before written.
+    // TODO(b/296030319): Implement analysis by building IR for all methods that instantiate the
+    //  relevant classes and analyzing the puts to the newly created instances.
+    for (ProgramField field : IterableUtils.flatten(nonFinalInstanceFields.values())) {
+      liveDefaultValueConsumer.accept(field);
     }
-    IRCode code = method.buildIR(appView, MethodConversionOptions.nonConverting());
-    for (NewInstance newInstance : code.<NewInstance>instructions(Instruction::isNewInstance)) {
-      ProgramFieldSet fieldsOfInterest = nonFinalInstanceFields.get(newInstance.getType());
-      if (fieldsOfInterest == null) {
-        continue;
-      }
-      FieldReadBeforeWriteDfsAnalysis analysis =
-          new FieldReadBeforeWriteDfsAnalysis(appView, code, fieldsOfInterest, newInstance) {
-
-            @Override
-            public AnalysisContinuation acceptFieldMaybeReadBeforeWrite(ProgramField field) {
-              // Remove this field from the `fieldsOfInterest`, so that we do not spend more time
-              // analyzing it.
-              if (fieldsOfInterest.remove(field)) {
-                liveDefaultValueConsumer.accept(field);
-              }
-              return AnalysisContinuation.abortIf(fieldsOfInterest.isEmpty());
-            }
-          };
-      analysis.run();
-      if (fieldsOfInterest.isEmpty()) {
-        nonFinalInstanceFields.remove(newInstance.getType());
-      }
-    }
-  }
-
-  private boolean maybeHasNewInstanceThatMatches(
-      ProgramMethod method, Predicate<DexType> predicate) {
-    Code code = method.getDefinition().getCode();
-    if (code == null || code.isSharedCodeObject()) {
-      return false;
-    }
-    if (code.isLirCode()) {
-      return code.asLirCode()
-          .hasConstantItemThatMatches(
-              constant -> constant instanceof DexType && predicate.test((DexType) constant));
-    }
-    assert appView.isCfByteCodePassThrough(method);
-    assert code.isCfCode();
-    return method.registerCodeReferencesWithResult(
-        new DefaultUseRegistryWithResult<>(appView, method, false) {
-
-          @Override
-          public void registerNewInstance(DexType type) {
-            if (predicate.test(type)) {
-              setResult(true);
-            }
-          }
-        });
   }
 
   private Map<FlowGraph, Deque<FlowGraphNode>> updateFlowGraphs(
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FieldReadBeforeWriteDfsAnalysis.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FieldReadBeforeWriteDfsAnalysis.java
deleted file mode 100644
index 1fdfb27..0000000
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FieldReadBeforeWriteDfsAnalysis.java
+++ /dev/null
@@ -1,387 +0,0 @@
-// Copyright (c) 2024, 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.optimize.argumentpropagation.propagation;
-
-import static com.android.tools.r8.ir.code.Opcodes.ASSUME;
-import static com.android.tools.r8.ir.code.Opcodes.CHECK_CAST;
-import static com.android.tools.r8.ir.code.Opcodes.CONST_NUMBER;
-import static com.android.tools.r8.ir.code.Opcodes.CONST_STRING;
-import static com.android.tools.r8.ir.code.Opcodes.GOTO;
-import static com.android.tools.r8.ir.code.Opcodes.INSTANCE_PUT;
-import static com.android.tools.r8.ir.code.Opcodes.INVOKE_DIRECT;
-import static com.android.tools.r8.ir.code.Opcodes.INVOKE_INTERFACE;
-import static com.android.tools.r8.ir.code.Opcodes.INVOKE_STATIC;
-import static com.android.tools.r8.ir.code.Opcodes.INVOKE_SUPER;
-import static com.android.tools.r8.ir.code.Opcodes.INVOKE_VIRTUAL;
-import static com.android.tools.r8.ir.code.Opcodes.RETURN;
-import static com.android.tools.r8.ir.code.Opcodes.THROW;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClassAndMethod;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.ProgramField;
-import com.android.tools.r8.ir.code.Assume;
-import com.android.tools.r8.ir.code.BasicBlock;
-import com.android.tools.r8.ir.code.BasicBlockInstructionIterator;
-import com.android.tools.r8.ir.code.CheckCast;
-import com.android.tools.r8.ir.code.ConstNumber;
-import com.android.tools.r8.ir.code.ConstString;
-import com.android.tools.r8.ir.code.Goto;
-import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.code.InstancePut;
-import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.InvokeDirect;
-import com.android.tools.r8.ir.code.InvokeMethod;
-import com.android.tools.r8.ir.code.NewInstance;
-import com.android.tools.r8.ir.code.Phi;
-import com.android.tools.r8.ir.code.Return;
-import com.android.tools.r8.ir.code.Throw;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.TraversalContinuation;
-import com.android.tools.r8.utils.WorkList;
-import com.android.tools.r8.utils.collections.ProgramFieldSet;
-
-/**
- * Analysis that is given an allocation site (a {@link NewInstance} instruction) and a set of fields
- * that belong to that newly allocated instance.
- *
- * <p>The analysis computes the subset of the given fields which are maybe read before they are
- * written. The default value of these fields is potentially read, whereas the default value of the
- * complement field set are guaranteed to never be read.
- *
- * <p>The analysis works by exploring all possible paths starting from the given allocation site to
- * the normal and the exceptional exits of the method, keeping track of which fields are definitely
- * written before they are read and which fields have maybe been read.
- */
-public abstract class FieldReadBeforeWriteDfsAnalysis extends FieldReadBeforeWriteDfsAnalysisState {
-
-  private final AppView<AppInfoWithLiveness> appView;
-  private final IRCode code;
-  private final DexItemFactory dexItemFactory;
-  // The set of fields to consider. Note that this is a concurrent set and that the caller may
-  // concurrently remove fields from the set. This may happen if we concurrently find a
-  // read-before-write of one of the fields.
-  private final ProgramFieldSet fields;
-  private final WorkList<WorkItem> worklist = WorkList.newIdentityWorkList();
-
-  private final FieldReadBeforeWriteDfsAnalysis self = this;
-
-  public FieldReadBeforeWriteDfsAnalysis(
-      AppView<AppInfoWithLiveness> appView,
-      IRCode code,
-      ProgramFieldSet fields,
-      NewInstance newInstance) {
-    super(newInstance);
-    this.appView = appView;
-    this.code = code;
-    this.dexItemFactory = appView.dexItemFactory();
-    this.fields = fields;
-  }
-
-  // Returns ABORT if all fields of interest are now maybe-read-before-written.
-  // Otherwise returns CONTINUE.
-  public abstract AnalysisContinuation acceptFieldMaybeReadBeforeWrite(ProgramField field);
-
-  public void run() {
-    worklist.addIfNotSeen(new InitialWorkItem());
-    worklist.run(WorkItem::process);
-  }
-
-  public enum AnalysisContinuation {
-    // Signals to abort the analysis completely (i.e., to break out of the DFS). This is used when
-    // we've reported all fields as being maybe read before written.
-    ABORT,
-    // Signals to continue the current DFS.
-    CONTINUE,
-    // Signals that all fields have been written before they are read on the current program path,
-    // meaning that the algorithm does not need to explore any further. The algorithm should instead
-    // backtrack and explore any other program paths.
-    BREAK;
-
-    static AnalysisContinuation abortIf(boolean condition) {
-      if (condition) {
-        return ABORT;
-      }
-      return CONTINUE;
-    }
-
-    boolean isAbort() {
-      return this == ABORT;
-    }
-
-    boolean isAbortOrContinue() {
-      return isAbort() || isContinue();
-    }
-
-    boolean isBreak() {
-      return this == BREAK;
-    }
-
-    boolean isContinue() {
-      return this == CONTINUE;
-    }
-
-    TraversalContinuation<?, ?> toTraversalContinuation() {
-      assert isAbortOrContinue();
-      return TraversalContinuation.breakIf(isAbort());
-    }
-  }
-
-  abstract class WorkItem {
-
-    abstract TraversalContinuation<?, ?> process();
-
-    void applyPhis(BasicBlock block) {
-      // TODO(b/339210038): When adding support for non-linear control flow, we need to implement
-      //  backtracking of this (i.e., we should remove the out value from the object aliases again).
-      for (Phi phi : block.getPhis()) {
-        if (phi.hasOperandThatMatches(self::isMaybeInstance)) {
-          addInstanceAlias(phi);
-        }
-      }
-    }
-
-    AnalysisContinuation applyInstructions(BasicBlockInstructionIterator instructionIterator) {
-      while (instructionIterator.hasNext()) {
-        Instruction instruction = instructionIterator.next();
-        assert !instruction.hasOutValue() || !isMaybeInstance(instruction.outValue());
-        AnalysisContinuation continuation;
-        // TODO(b/339210038): Extend this to many other instructions, such as ConstClass,
-        //  InstanceOf, *Binop, etc.
-        switch (instruction.opcode()) {
-          case ASSUME:
-            continuation = applyAssumeInstruction(instruction.asAssume());
-            break;
-          case CHECK_CAST:
-            continuation = applyCheckCastInstruction(instruction.asCheckCast());
-            break;
-          case CONST_NUMBER:
-            continuation = applyConstNumber(instruction.asConstNumber());
-            break;
-          case CONST_STRING:
-            continuation = applyConstString(instruction.asConstString());
-            break;
-          case GOTO:
-            continuation = applyGotoInstruction(instruction.asGoto());
-            break;
-          case INSTANCE_PUT:
-            continuation = applyInstancePut(instruction.asInstancePut());
-            break;
-          case INVOKE_DIRECT:
-          case INVOKE_INTERFACE:
-          case INVOKE_STATIC:
-          case INVOKE_SUPER:
-          case INVOKE_VIRTUAL:
-            continuation = applyInvokeMethodInstruction(instruction.asInvokeMethod());
-            break;
-          case RETURN:
-            continuation = applyReturnInstruction(instruction.asReturn());
-            break;
-          case THROW:
-            continuation = applyThrowInstruction(instruction.asThrow());
-            break;
-          default:
-            continuation = applyUnhandledInstruction();
-            break;
-        }
-        if (continuation.isAbort()) {
-          return continuation;
-        }
-        if (continuation.isBreak()) {
-          break;
-        }
-      }
-      return AnalysisContinuation.CONTINUE;
-    }
-
-    // TODO(b/339210038): When adding support for non-linear control flow, we need to implement
-    //  backtracking of this (i.e., we should remove the out value from the object aliases again).
-    private AnalysisContinuation applyAssumeInstruction(Assume assume) {
-      if (isMaybeInstance(assume.src())) {
-        addInstanceAlias(assume.outValue());
-      }
-      return AnalysisContinuation.CONTINUE;
-    }
-
-    // TODO(b/339210038): When adding support for non-linear control flow, we need to implement
-    //  backtracking of this (i.e., we should remove the out value from the object aliases again).
-    private AnalysisContinuation applyCheckCastInstruction(CheckCast checkCast) {
-      if (isMaybeInstance(checkCast.object())) {
-        addInstanceAlias(checkCast.outValue());
-      }
-      // If the instance has escaped to the heap and this check-cast instruction throws, then it is
-      // possible that the instance is retrieved from the heap and all fields are read.
-      return markRemainingFieldsAsMaybeReadBeforeWrittenIfInstanceIsEscaped();
-    }
-
-    private AnalysisContinuation applyConstNumber(ConstNumber unusedConstNumber) {
-      return AnalysisContinuation.CONTINUE;
-    }
-
-    private AnalysisContinuation applyConstString(ConstString unusedConstString) {
-      return AnalysisContinuation.CONTINUE;
-    }
-
-    private AnalysisContinuation applyGotoInstruction(Goto gotoInstruction) {
-      BasicBlock targetBlock = gotoInstruction.getTarget();
-      if (isBlockOnStack(targetBlock)) {
-        // Bail out in case of cycles.
-        return markRemainingFieldsAsMaybeReadBeforeWritten();
-      } else {
-        // Continue exploration into the successor block.
-        worklist.addIgnoringSeenSet(new ProcessBlockWorkItem(targetBlock));
-        return AnalysisContinuation.CONTINUE;
-      }
-    }
-
-    private AnalysisContinuation applyInstancePut(InstancePut instancePut) {
-      // If the instance has escaped and this instance-put instruction can throw, then the program
-      // can get the instance from the heap and read any field. Give up in this case.
-      if (isEscaped() && instancePut.instructionInstanceCanThrow(appView, code.context())) {
-        return markRemainingFieldsAsMaybeReadBeforeWritten();
-      }
-
-      // Record if this is a definite write to one of the fields of interest.
-      if (isDefinitelyInstance(instancePut.object())) {
-        ProgramField resolvedField =
-            instancePut.resolveField(appView, code.context()).getProgramField();
-        if (resolvedField != null && fields.contains(resolvedField)) {
-          addWrittenBeforeRead(resolvedField);
-        }
-
-        // If all fields of interest are written before read, then stop the exploration of the
-        // current program path (but continue to explore any program paths from previous unexplored
-        // branches).
-        if (fields.allMatch(self::isWrittenBeforeRead)) {
-          return AnalysisContinuation.BREAK;
-        }
-      }
-
-      // Record if the instance has escaped as a result of this instance-put.
-      if (!isEscaped() && isMaybeInstance(instancePut.value())) {
-        setEscaped(instancePut);
-      }
-      return AnalysisContinuation.CONTINUE;
-    }
-
-    private AnalysisContinuation applyInvokeMethodInstruction(InvokeMethod invoke) {
-      // Allow calls to java.lang.Object.<init>().
-      // TODO(b/339210038): Generalize this to other constructors.
-      if (invoke.isInvokeConstructor(dexItemFactory)
-          && isDefinitelyInstance(invoke.getFirstArgument())) {
-        DexClassAndMethod resolvedMethod =
-            invoke.resolveMethod(appView, code.context()).getResolutionPair();
-        if (resolvedMethod != null
-            && resolvedMethod
-                .getReference()
-                .isIdenticalTo(dexItemFactory.objectMembers.constructor)) {
-          return AnalysisContinuation.CONTINUE;
-        }
-      }
-
-      // Conservatively treat calls as reading any field if the receiver has escaped or is escaping.
-      if (!isEscaped()
-          && invoke.hasInValueThatMatches(self::isMaybeInstance)
-          && invoke.instructionMayHaveSideEffects(appView, code.context())) {
-        setEscaped(invoke);
-      }
-
-      if (isEscaped()) {
-        return markRemainingFieldsAsMaybeReadBeforeWritten();
-      }
-
-      // Otherwise, this is a call to a method where none of the arguments is an alias of the
-      // instance, and the instance has not escaped. Therefore, this call cannot read any of fields
-      // from the instance.
-      return AnalysisContinuation.CONTINUE;
-    }
-
-    private AnalysisContinuation applyReturnInstruction(Return unusedReturnInstruction) {
-      return markRemainingFieldsAsMaybeReadBeforeWritten();
-    }
-
-    private AnalysisContinuation applyThrowInstruction(Throw unusedThrowInstruction) {
-      return markRemainingFieldsAsMaybeReadBeforeWrittenIfInstanceIsEscaped();
-    }
-
-    private AnalysisContinuation applyUnhandledInstruction() {
-      return markRemainingFieldsAsMaybeReadBeforeWritten();
-    }
-
-    AnalysisContinuation markRemainingFieldsAsMaybeReadBeforeWritten() {
-      for (ProgramField field : fields) {
-        if (!isWrittenBeforeRead(field)) {
-          AnalysisContinuation continuation = acceptFieldMaybeReadBeforeWrite(field);
-          assert continuation.isAbortOrContinue();
-          if (continuation.isAbort()) {
-            return continuation;
-          }
-        }
-      }
-      // At this point we could also CONTINUE, but we check if the fields of interest have become
-      // empty as a result of concurrent modification.
-      return AnalysisContinuation.abortIf(fields.isEmpty());
-    }
-
-    AnalysisContinuation markRemainingFieldsAsMaybeReadBeforeWrittenIfInstanceIsEscaped() {
-      if (isEscaped()) {
-        return markRemainingFieldsAsMaybeReadBeforeWritten();
-      }
-      return AnalysisContinuation.CONTINUE;
-    }
-  }
-
-  class InitialWorkItem extends WorkItem {
-
-    @Override
-    TraversalContinuation<?, ?> process() {
-      // We start the analysis from the unique constructor invoke instead of from the NewInstance
-      // instruction, since no instructions before the constructor call can read any fields from the
-      // uninitialized this.
-      // TODO(b/339210038): In principle it may be possible for the NewInstance value to flow into a
-      //  phi before the unique constructor invoke. If this happens we would not record the phi as
-      //  an alias when starting the analysis from the invoke-direct.
-      InvokeDirect uniqueConstructorInvoke =
-          getNewInstance().getUniqueConstructorInvoke(dexItemFactory);
-      if (uniqueConstructorInvoke == null) {
-        return markRemainingFieldsAsMaybeReadBeforeWritten().toTraversalContinuation();
-      }
-      BasicBlock block = uniqueConstructorInvoke.getBlock();
-      // TODO(b/339210038): Maybe allow exceptional control flow.
-      if (block.hasCatchHandlers()) {
-        return markRemainingFieldsAsMaybeReadBeforeWritten().toTraversalContinuation();
-      }
-      addBlockToStack(block);
-      addInstanceAlias(getNewInstance().outValue());
-      BasicBlockInstructionIterator instructionIterator = block.iterator(uniqueConstructorInvoke);
-      // Start the analysis from the invoke-direct instruction. This is important if we can tell
-      // that the constructor definitely writes some fields.
-      instructionIterator.previous();
-      return applyInstructions(instructionIterator).toTraversalContinuation();
-    }
-  }
-
-  class ProcessBlockWorkItem extends WorkItem {
-
-    private final BasicBlock block;
-
-    ProcessBlockWorkItem(BasicBlock block) {
-      this.block = block;
-    }
-
-    @Override
-    TraversalContinuation<?, ?> process() {
-      // TODO(b/339210038): Maybe allow exceptional control flow.
-      if (block.hasCatchHandlers()) {
-        return TraversalContinuation.breakIf(
-            markRemainingFieldsAsMaybeReadBeforeWritten().isAbort());
-      }
-      addBlockToStack(block);
-      applyPhis(block);
-      AnalysisContinuation continuation = applyInstructions(block.iterator());
-      assert continuation.isAbortOrContinue();
-      return TraversalContinuation.breakIf(continuation.isAbort());
-    }
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FieldReadBeforeWriteDfsAnalysisState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FieldReadBeforeWriteDfsAnalysisState.java
deleted file mode 100644
index 7e24c03..0000000
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FieldReadBeforeWriteDfsAnalysisState.java
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright (c) 2024, 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.optimize.argumentpropagation.propagation;
-
-import com.android.tools.r8.graph.ProgramField;
-import com.android.tools.r8.ir.code.BasicBlock;
-import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.NewInstance;
-import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.utils.collections.ProgramFieldSet;
-import com.google.common.collect.Sets;
-import java.util.Set;
-
-/**
- * The state we track during the field-maybe-read-before-write/field-never-read-before-written
- * analysis.
- */
-public class FieldReadBeforeWriteDfsAnalysisState {
-
-  // The current allocation we are analyzing.
-  private final NewInstance newInstance;
-
-  // The first instruction on the current program path starting from the `newInstance` instruction
-  // from which the `newInstance` value escapes.
-  private Instruction escape = null;
-
-  // The set of values that *may* be aliases of the `newInstance` value.
-  private final Set<Value> instanceAliases = Sets.newIdentityHashSet();
-
-  // The set of blocks on the current program path.
-  private final Set<BasicBlock> stack = Sets.newIdentityHashSet();
-
-  // The set of fields that are guaranteed to be written before they are read on the current program
-  // path.
-  private final ProgramFieldSet writtenBeforeRead = ProgramFieldSet.create();
-
-  FieldReadBeforeWriteDfsAnalysisState(NewInstance newInstance) {
-    this.newInstance = newInstance;
-  }
-
-  void addInstanceAlias(Value instanceAlias) {
-    boolean changed = instanceAliases.add(instanceAlias);
-    assert changed;
-  }
-
-  void addBlockToStack(BasicBlock block) {
-    boolean changed = stack.add(block);
-    assert changed;
-  }
-
-  void addWrittenBeforeRead(ProgramField field) {
-    writtenBeforeRead.add(field);
-  }
-
-  NewInstance getNewInstance() {
-    return newInstance;
-  }
-
-  boolean isBlockOnStack(BasicBlock block) {
-    return stack.contains(block);
-  }
-
-  boolean isEscaped() {
-    return escape != null;
-  }
-
-  boolean isDefinitelyInstance(Value value) {
-    return value.getAliasedValue() == newInstance.outValue();
-  }
-
-  boolean isMaybeInstance(Value value) {
-    return instanceAliases.contains(value);
-  }
-
-  boolean isWrittenBeforeRead(ProgramField field) {
-    return writtenBeforeRead.contains(field);
-  }
-
-  void setEscaped(Instruction escape) {
-    assert !isEscaped();
-    this.escape = escape;
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InFlowPropagator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InFlowPropagator.java
index 81e3424..d62328d 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InFlowPropagator.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InFlowPropagator.java
@@ -46,7 +46,7 @@
   final AppView<AppInfoWithLiveness> appView;
   final Set<DexProgramClass> classesWithSingleCallerInlinedInstanceInitializers;
   final IRConverter converter;
-  protected final FieldStateCollection fieldStates;
+  final FieldStateCollection fieldStates;
   final MethodStateCollectionByReference methodStates;
 
   public InFlowPropagator(
@@ -113,15 +113,12 @@
 
   private Map<FlowGraph, Deque<FlowGraphNode>> includeDefaultValuesInFieldStates(
       List<FlowGraph> flowGraphs, ExecutorService executorService) throws ExecutionException {
-    DefaultFieldValueJoiner joiner = createDefaultFieldValueJoiner(flowGraphs);
+    DefaultFieldValueJoiner joiner =
+        new DefaultFieldValueJoiner(
+            appView, classesWithSingleCallerInlinedInstanceInitializers, fieldStates, flowGraphs);
     return joiner.joinDefaultFieldValuesForFieldsWithReadBeforeWrite(executorService);
   }
 
-  protected DefaultFieldValueJoiner createDefaultFieldValueJoiner(List<FlowGraph> flowGraphs) {
-    return new DefaultFieldValueJoiner(
-        appView, classesWithSingleCallerInlinedInstanceInitializers, fieldStates, flowGraphs);
-  }
-
   private void processFlowGraphs(List<FlowGraph> flowGraphs, ExecutorService executorService)
       throws ExecutionException {
     ThreadUtils.processItems(
diff --git a/src/main/java/com/android/tools/r8/optimize/compose/ComposeMethodProcessor.java b/src/main/java/com/android/tools/r8/optimize/compose/ComposeMethodProcessor.java
index 22ed377..8d7498c 100644
--- a/src/main/java/com/android/tools/r8/optimize/compose/ComposeMethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/optimize/compose/ComposeMethodProcessor.java
@@ -9,7 +9,6 @@
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.ProgramField;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.constant.SparseConditionalConstantPropagation;
@@ -34,8 +33,6 @@
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodState;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodStateCollectionByReference;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.ValueState;
-import com.android.tools.r8.optimize.argumentpropagation.propagation.DefaultFieldValueJoiner;
-import com.android.tools.r8.optimize.argumentpropagation.propagation.FlowGraph;
 import com.android.tools.r8.optimize.argumentpropagation.propagation.InFlowPropagator;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.IterableUtils;
@@ -45,8 +42,6 @@
 import com.android.tools.r8.utils.collections.ProgramMethodSet;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Sets;
-import java.util.Collections;
-import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
@@ -97,22 +92,7 @@
 
     InFlowPropagator inFlowPropagator =
         new InFlowPropagator(
-            appView, null, converter, codeScanner.getFieldStates(), codeScanner.getMethodStates()) {
-
-          @Override
-          protected DefaultFieldValueJoiner createDefaultFieldValueJoiner(
-              List<FlowGraph> flowGraphs) {
-            return new DefaultFieldValueJoiner(appView, null, fieldStates, flowGraphs) {
-
-              @Override
-              protected Map<DexProgramClass, List<ProgramField>> getFieldsOfInterest() {
-                // We do not rely on the optimization of any fields in the Composable optimization
-                // pass.
-                return Collections.emptyMap();
-              }
-            };
-          }
-        };
+            appView, null, converter, codeScanner.getFieldStates(), codeScanner.getMethodStates());
     inFlowPropagator.run(executorService);
 
     ArgumentPropagatorOptimizationInfoPopulator optimizationInfoPopulator =
diff --git a/src/main/java/com/android/tools/r8/shaking/rules/KeepAnnotationMatcher.java b/src/main/java/com/android/tools/r8/shaking/rules/KeepAnnotationMatcher.java
index 19a0d8b..fb47d43 100644
--- a/src/main/java/com/android/tools/r8/shaking/rules/KeepAnnotationMatcher.java
+++ b/src/main/java/com/android/tools/r8/shaking/rules/KeepAnnotationMatcher.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.graph.ProgramMember;
 import com.android.tools.r8.keepanno.ast.KeepAnnotationPattern;
 import com.android.tools.r8.keepanno.ast.KeepBindingReference;
+import com.android.tools.r8.keepanno.ast.KeepBindings;
 import com.android.tools.r8.keepanno.ast.KeepBindings.KeepBindingSymbol;
 import com.android.tools.r8.keepanno.ast.KeepCheck;
 import com.android.tools.r8.keepanno.ast.KeepCheck.KeepCheckKind;
@@ -34,6 +35,7 @@
 import com.android.tools.r8.keepanno.ast.KeepConstraintVisitor;
 import com.android.tools.r8.keepanno.ast.KeepConstraints;
 import com.android.tools.r8.keepanno.ast.KeepDeclaration;
+import com.android.tools.r8.keepanno.ast.KeepEdge;
 import com.android.tools.r8.keepanno.ast.KeepItemPattern;
 import com.android.tools.r8.keepanno.ast.KeepItemReference;
 import com.android.tools.r8.keepanno.ast.KeepMemberItemPattern;
@@ -355,8 +357,16 @@
     }
 
     private void continueWithNoClass(int classIndex) {
+      continueWithNoClassClearingMembers(classIndex, 0, null);
+    }
+
+    private void continueWithNoClassClearingMembers(
+        int classIndex, int memberInHolderIndex, IntList memberIndexTranslation) {
       if (schema.isOptionalClass(classIndex)) {
         assignment.setClass(classIndex, null);
+        for (int i = 0; i < memberInHolderIndex; i++) {
+          assignment.setMember(memberIndexTranslation.getInt(i), null);
+        }
         findMatchingClass(classIndex + 1);
       }
     }
@@ -404,9 +414,9 @@
                   holder.forEachProgramMethodMatching(
                       m -> predicates.matchesMethod(m, methodPattern), continueWithMember));
       if (didContinue.isFalse()) {
-        // No match for the member pattern existed, continue with empty member.
-        continueWithNoMember(
-            memberIndex, memberInHolderIndex + 1, memberIndexTranslation, holder, nextClassIndex);
+        // No match for the member pattern existed, continue with next class.
+        continueWithNoClassClearingMembers(
+            nextClassIndex - 1, memberInHolderIndex, memberIndexTranslation);
       }
     }
 
@@ -429,18 +439,6 @@
           definition.getContextClass(),
           nextClassIndex);
     }
-
-    private void continueWithNoMember(
-        int memberIndex,
-        int nextMemberInHolderIndex,
-        IntList memberIndexTranslation,
-        DexProgramClass holder,
-        int nextClassIndex) {
-      if (schema.isOptionalMember(memberIndex, nextClassIndex - 1)) {
-        assignment.setMember(memberIndex, null);
-        findMatchingMember(nextMemberInHolderIndex, memberIndexTranslation, holder, nextClassIndex);
-      }
-    }
   }
 
   /**
@@ -456,7 +454,6 @@
     final List<KeepClassItemPattern> classes = new ArrayList<>();
     final List<KeepMemberItemPattern> members = new ArrayList<>();
     final List<IntList> classMembers = new ArrayList<>();
-    final IntList boundClasses = new IntArrayList();
     final IntList preconditions = new IntArrayList();
     final IntList consequences = new IntArrayList();
     final List<KeepConstraints> constraints = new ArrayList<>();
@@ -480,16 +477,12 @@
     }
 
     private KeepItemPattern getItemForBinding(KeepBindingSymbol symbol) {
-      assert declaration.isKeepEdge();
-      return declaration.asKeepEdge().getBindings().get(symbol).getItem();
+      KeepBindings bindings = declaration.apply(KeepEdge::getBindings, KeepCheck::getBindings);
+      return bindings.get(symbol).getItem();
     }
 
     public boolean isOptionalClass(int classIndex) {
-      return classIndex >= preconditionClassesCount && !boundClasses.contains(classIndex);
-    }
-
-    public boolean isOptionalMember(int memberIndex, int classIndex) {
-      return memberIndex >= preconditionMembersCount && isOptionalClass(classIndex);
+      return classIndex >= preconditionClassesCount;
     }
 
     public static boolean isClassKeyReference(int keyRef) {
@@ -523,14 +516,7 @@
 
     private int defineBindingReference(KeepBindingReference reference) {
       return symbolToKey.computeIfAbsent(
-          reference.getName(),
-          symbol -> {
-            int bindingId = defineItemPattern(getItemForBinding(symbol));
-            if (isClassKeyReference(bindingId)) {
-              boundClasses.add(bindingId);
-            }
-            return bindingId;
-          });
+          reference.getName(), symbol -> defineItemPattern(getItemForBinding(symbol)));
     }
 
     private int defineItemPattern(KeepItemPattern item) {
diff --git a/src/main/java/com/android/tools/r8/utils/collections/ProgramFieldSet.java b/src/main/java/com/android/tools/r8/utils/collections/ProgramFieldSet.java
index 891593a..c778df3 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/ProgramFieldSet.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/ProgramFieldSet.java
@@ -8,15 +8,14 @@
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.ProgramField;
-import com.google.common.base.Predicate;
 import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Iterables;
 import com.google.common.collect.Sets;
 import java.util.IdentityHashMap;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Predicate;
 import java.util.stream.Stream;
 
 public class ProgramFieldSet implements Iterable<ProgramField> {
@@ -55,10 +54,6 @@
     backing.putAll(fields.backing);
   }
 
-  public boolean allMatch(Predicate<? super ProgramField> predicate) {
-    return Iterables.all(this, predicate);
-  }
-
   public boolean createAndAdd(DexProgramClass clazz, DexEncodedField definition) {
     return add(new ProgramField(clazz, definition));
   }
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java
index ef3db54..1244c0f 100644
--- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java
@@ -110,9 +110,9 @@
                   methodReferences.forEach(field -> numberOfMethods.increment())));
         });
     // These numbers will change when updating api-versions.xml
-    assertEquals(5904, parsedApiClasses.size());
-    assertEquals(30073, numberOfFields.get());
-    assertEquals(46091, numberOfMethods.get());
+    assertEquals(5971, parsedApiClasses.size());
+    assertEquals(30341, numberOfFields.get());
+    assertEquals(46572, numberOfMethods.get());
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/PackagePrivateMembersAccessedTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/PackagePrivateMembersAccessedTest.java
index fa46f4d..0d912b3 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/PackagePrivateMembersAccessedTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/PackagePrivateMembersAccessedTest.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.classmerging.horizontal;
 
-import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsentIf;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.MatcherAssert.assertThat;
 
@@ -36,9 +35,7 @@
         .assertSuccessWithOutputLines("foo", "hello", "5", "foobar")
         .inspect(
             codeInspector -> {
-              assertThat(
-                  codeInspector.clazz(C.class),
-                  isAbsentIf(parameters.canInitNewInstanceUsingSuperclassConstructor()));
+              assertThat(codeInspector.clazz(C.class), isPresent());
               assertThat(codeInspector.clazz(D.class), isPresent());
               assertThat(codeInspector.clazz(E.class), isPresent());
             });
diff --git a/src/test/java/com/android/tools/r8/globalsynthetics/GlobalSyntheticsEnsureClassesOutputTest.java b/src/test/java/com/android/tools/r8/globalsynthetics/GlobalSyntheticsEnsureClassesOutputTest.java
index e6ee76a..d988c22 100644
--- a/src/test/java/com/android/tools/r8/globalsynthetics/GlobalSyntheticsEnsureClassesOutputTest.java
+++ b/src/test/java/com/android/tools/r8/globalsynthetics/GlobalSyntheticsEnsureClassesOutputTest.java
@@ -58,7 +58,7 @@
         .setMinApi(AndroidApiLevel.K)
         .compile()
         .inspect(
-            inspector -> assertEquals(backend.isDex() ? 1077 : 4, inspector.allClasses().size()));
+            inspector -> assertEquals(backend.isDex() ? 1091 : 4, inspector.allClasses().size()));
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldWithConstructorInliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldWithConstructorInliningTest.java
index 142d565..f952fa9 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldWithConstructorInliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldWithConstructorInliningTest.java
@@ -3,10 +3,10 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.optimize.membervaluepropagation;
 
-import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentIf;
 import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
 
 import com.android.tools.r8.NeverClassInline;
 import com.android.tools.r8.TestBase;
@@ -42,11 +42,17 @@
         .inspect(
             inspector -> {
               ClassSubject aClassSubject = inspector.clazz(A.class);
-              assertThat(aClassSubject, isAbsent());
+              // TODO(b/339210038): Should always be absent.
+              assertThat(
+                  aClassSubject,
+                  isPresentIf(parameters.canInitNewInstanceUsingSuperclassConstructor()));
 
               MethodSubject mainMethodSubject = inspector.clazz(Main.class).mainMethod();
               assertThat(mainMethodSubject, isPresent());
-              assertTrue(mainMethodSubject.streamInstructions().anyMatch(i -> i.isConstNumber(42)));
+              // TODO(b/339210038): Should always contain 42.
+              assertEquals(
+                  parameters.canInitNewInstanceUsingSuperclassConstructor(),
+                  mainMethodSubject.streamInstructions().noneMatch(i -> i.isConstNumber(42)));
             })
         .run(parameters.getRuntime(), Main.class)
         .assertSuccessWithOutputLines("42");
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/LiveDefaultFieldValueOfReflectivelyInstantiatedClassTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/LiveDefaultFieldValueOfReflectivelyInstantiatedClassTest.java
deleted file mode 100644
index 358227d..0000000
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/LiveDefaultFieldValueOfReflectivelyInstantiatedClassTest.java
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright (c) 2024, 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.ir.optimize.membervaluepropagation;
-
-import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.TestBase;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameter;
-import org.junit.runners.Parameterized.Parameters;
-
-@RunWith(Parameterized.class)
-public class LiveDefaultFieldValueOfReflectivelyInstantiatedClassTest extends TestBase {
-
-  @Parameter(0)
-  public TestParameters parameters;
-
-  @Parameters(name = "{0}")
-  public static TestParametersCollection data() {
-    return getTestParameters().withAllRuntimesAndApiLevels().build();
-  }
-
-  @Test
-  public void test() throws Exception {
-    testForR8(parameters.getBackend())
-        .addInnerClasses(getClass())
-        .addKeepMainRule(Main.class)
-        .addKeepClassAndDefaultConstructor(BooleanBox.class)
-        .enableInliningAnnotations()
-        .setMinApi(parameters)
-        .run(parameters.getRuntime(), Main.class)
-        .assertSuccessWithOutputLines("false");
-  }
-
-  static class Main {
-
-    public static void main(String[] args) throws Exception {
-      Class<?> clazz = System.currentTimeMillis() > 0 ? BooleanBox.class : Object.class;
-      BooleanBox box = (BooleanBox) clazz.getDeclaredConstructor().newInstance();
-      System.out.println(box.value);
-      box.set();
-    }
-  }
-
-  static class BooleanBox {
-
-    boolean value;
-
-    BooleanBox() {}
-
-    @NeverInline
-    void set() {
-      value = true;
-    }
-  }
-}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/FieldInitializedByConstantArgumentMultipleConstructorsTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/FieldInitializedByConstantArgumentMultipleConstructorsTest.java
index 862fbc9..fca4473 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/FieldInitializedByConstantArgumentMultipleConstructorsTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/FieldInitializedByConstantArgumentMultipleConstructorsTest.java
@@ -4,8 +4,8 @@
 
 package com.android.tools.r8.ir.optimize.membervaluepropagation.fields;
 
-import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentIf;
 import static org.hamcrest.MatcherAssert.assertThat;
 
 import com.android.tools.r8.NeverClassInline;
@@ -18,20 +18,21 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameter;
-import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
 public class FieldInitializedByConstantArgumentMultipleConstructorsTest extends TestBase {
 
-  @Parameter(0)
-  public TestParameters parameters;
+  private final TestParameters parameters;
 
-  @Parameters(name = "{0}")
+  @Parameterized.Parameters(name = "{0}")
   public static TestParametersCollection data() {
     return getTestParameters().withAllRuntimesAndApiLevels().build();
   }
 
+  public FieldInitializedByConstantArgumentMultipleConstructorsTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
   @Test
   public void test() throws Exception {
     testForR8(parameters.getBackend())
@@ -50,7 +51,10 @@
     ClassSubject testClassSubject = inspector.clazz(TestClass.class);
     assertThat(testClassSubject, isPresent());
     assertThat(testClassSubject.uniqueMethodWithOriginalName("live"), isPresent());
-    assertThat(testClassSubject.uniqueMethodWithOriginalName("dead"), isAbsent());
+    // TODO(b/280275115): Constructor inlining regresses instance field value analysis.
+    assertThat(
+        testClassSubject.uniqueMethodWithOriginalName("dead"),
+        isPresentIf(parameters.canInitNewInstanceUsingSuperclassConstructor()));
   }
 
   static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepMainViaBindingTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepMainViaBindingTest.java
new file mode 100644
index 0000000..9246d9d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepMainViaBindingTest.java
@@ -0,0 +1,59 @@
+// Copyright (c) 2024, 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.keepanno;
+
+import com.android.tools.r8.keepanno.annotations.KeepBinding;
+import com.android.tools.r8.keepanno.annotations.KeepEdge;
+import com.android.tools.r8.keepanno.annotations.KeepItemKind;
+import com.android.tools.r8.keepanno.annotations.KeepTarget;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+
+@RunWith(Parameterized.class)
+public class KeepMainViaBindingTest extends KeepAnnoTestBase {
+
+  static final String EXPECTED = StringUtils.lines("Hello, world!");
+
+  @Parameter public KeepAnnoParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static List<KeepAnnoParameters> data() {
+    return createParameters(
+        getTestParameters().withDefaultRuntimes().withApiLevel(AndroidApiLevel.B).build());
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForKeepAnno(parameters)
+        .addProgramClasses(getInputClasses())
+        .setExcludedOuterClass(getClass())
+        .run(TestClass.class)
+        .assertSuccessWithOutput(EXPECTED);
+  }
+
+  public List<Class<?>> getInputClasses() {
+    return ImmutableList.of(TestClass.class);
+  }
+
+  static class TestClass {
+
+    @KeepEdge(
+        bindings =
+            @KeepBinding(
+                bindingName = "MainMethod",
+                classConstant = TestClass.class,
+                methodName = "main"),
+        consequences =
+            @KeepTarget(kind = KeepItemKind.CLASS_AND_METHODS, memberFromBinding = "MainMethod"))
+    public static void main(String[] args) throws Exception {
+      System.out.println("Hello, world!");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/keepanno/UsedByReflectionWithNonMatchingMemberPatternsTest.java b/src/test/java/com/android/tools/r8/keepanno/UsedByReflectionWithNonMatchingMemberPatternsTest.java
new file mode 100644
index 0000000..a77ec25
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/keepanno/UsedByReflectionWithNonMatchingMemberPatternsTest.java
@@ -0,0 +1,53 @@
+// Copyright (c) 2024, 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.keepanno;
+
+import com.android.tools.r8.keepanno.annotations.KeepItemKind;
+import com.android.tools.r8.keepanno.annotations.UsedByReflection;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+
+@RunWith(Parameterized.class)
+public class UsedByReflectionWithNonMatchingMemberPatternsTest extends KeepAnnoTestBase {
+
+  static final String EXPECTED = StringUtils.lines("Hello, world!");
+
+  @Parameter public KeepAnnoParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static List<KeepAnnoParameters> data() {
+    return createParameters(
+        getTestParameters().withDefaultRuntimes().withApiLevel(AndroidApiLevel.B).build());
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForKeepAnno(parameters)
+        .addProgramClasses(getInputClasses())
+        .setExcludedOuterClass(getClass())
+        // The fields part of the UsedByReflection will be unused.
+        .allowUnusedProguardConfigurationRules()
+        .run(TestClass.class)
+        .assertSuccessWithOutput(EXPECTED);
+  }
+
+  public List<Class<?>> getInputClasses() {
+    return ImmutableList.of(TestClass.class);
+  }
+
+  @UsedByReflection(kind = KeepItemKind.CLASS_AND_FIELDS)
+  static class TestClass {
+
+    @UsedByReflection
+    public static void main(String[] args) throws Exception {
+      System.out.println("Hello, world!");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/keepanno/ast/KeepEdgeAstTest.java b/src/test/java/com/android/tools/r8/keepanno/ast/KeepEdgeAstTest.java
index d2deccb..e0727be 100644
--- a/src/test/java/com/android/tools/r8/keepanno/ast/KeepEdgeAstTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/ast/KeepEdgeAstTest.java
@@ -40,15 +40,21 @@
 
   @Test
   public void testKeepAll() {
+    BindingsHelper helper = new BindingsHelper();
     KeepEdge edge =
         KeepEdge.builder()
             .setConsequences(
                 KeepConsequences.builder()
                     .addTarget(
-                        KeepTarget.builder().setItemPattern(KeepItemPattern.anyClass()).build())
+                        KeepTarget.builder()
+                            .setItemBindingReference(helper.freshAnyClass())
+                            .build())
                     .addTarget(
-                        KeepTarget.builder().setItemPattern(KeepItemPattern.anyMember()).build())
+                        KeepTarget.builder()
+                            .setItemBindingReference(helper.freshAnyMember())
+                            .build())
                     .build())
+            .setBindings(helper.build())
             .build();
     assertEquals(
         StringUtils.unixLines(
@@ -59,6 +65,7 @@
 
   @Test
   public void testSoftPinViaConstraints() {
+    BindingsHelper helper = new BindingsHelper();
     KeepConstraints constraints =
         KeepConstraints.builder()
             .add(KeepConstraint.classInstantiate())
@@ -72,15 +79,16 @@
                 KeepConsequences.builder()
                     .addTarget(
                         KeepTarget.builder()
-                            .setItemPattern(KeepItemPattern.anyClass())
+                            .setItemBindingReference(helper.freshAnyClass())
                             .setConstraints(constraints)
                             .build())
                     .addTarget(
                         KeepTarget.builder()
-                            .setItemPattern(KeepItemPattern.anyMember())
+                            .setItemBindingReference(helper.freshAnyMember())
                             .setConstraints(constraints)
                             .build())
                     .build())
+            .setBindings(helper.build())
             .build();
     // Pinning just the use constraints points will issue the full inverse of the known options,
     // e.g., 'allowaccessmodification'.
@@ -96,9 +104,11 @@
   }
   @Test
   public void testKeepClass() {
-    KeepTarget target = target(classItem(CLASS));
+    BindingsHelper helper = new BindingsHelper();
+    KeepTarget target = target(classItem(CLASS), helper);
     KeepConsequences consequences = KeepConsequences.builder().addTarget(target).build();
-    KeepEdge edge = KeepEdge.builder().setConsequences(consequences).build();
+    KeepEdge edge =
+        KeepEdge.builder().setConsequences(consequences).setBindings(helper.build()).build();
     assertEquals(
         StringUtils.unixLines(
             "-keep,allowaccessmodification class " + CLASS + " { void finalize(); }"),
@@ -107,20 +117,23 @@
 
   @Test
   public void testKeepInitIfReferenced() {
+    BindingsHelper helper = new BindingsHelper();
     KeepEdge edge =
         KeepEdge.builder()
             .setPreconditions(
                 KeepPreconditions.builder()
-                    .addCondition(KeepCondition.builder().setItemPattern(classItem(CLASS)).build())
+                    .addCondition(condition(classItem(CLASS), helper))
                     .build())
             .setConsequences(
                 KeepConsequences.builder()
                     .addTarget(
                         target(
-                            buildMemberItem(CLASS)
+                            buildMemberItem(CLASS, helper)
                                 .setMemberPattern(defaultInitializerPattern())
-                                .build()))
+                                .build(),
+                            helper))
                     .build())
+            .setBindings(helper.build())
             .build();
     assertEquals(
         StringUtils.unixLines(
@@ -130,13 +143,19 @@
 
   @Test
   public void testKeepInstanceIfReferenced() {
+    BindingsHelper helper = new BindingsHelper();
     KeepEdge edge =
         KeepEdge.builder()
             .setPreconditions(
                 KeepPreconditions.builder()
-                    .addCondition(KeepCondition.builder().setItemPattern(classItem(CLASS)).build())
+                    .addCondition(
+                        KeepCondition.builder()
+                            .setItemBindingReference(helper.freshClassBinding(classItem(CLASS)))
+                            .build())
                     .build())
-            .setConsequences(KeepConsequences.builder().addTarget(target(classItem(CLASS))).build())
+            .setConsequences(
+                KeepConsequences.builder().addTarget(target(classItem(CLASS), helper)).build())
+            .setBindings(helper.build())
             .build();
     assertEquals(
         StringUtils.unixLines(
@@ -150,21 +169,24 @@
 
   @Test
   public void testKeepInstanceAndInitIfReferenced() {
+    BindingsHelper helper = new BindingsHelper();
     KeepEdge edge =
         KeepEdge.builder()
             .setPreconditions(
                 KeepPreconditions.builder()
-                    .addCondition(KeepCondition.builder().setItemPattern(classItem(CLASS)).build())
+                    .addCondition(condition(classItem(CLASS), helper))
                     .build())
             .setConsequences(
                 KeepConsequences.builder()
-                    .addTarget(target(classItem(CLASS)))
+                    .addTarget(target(classItem(CLASS), helper))
                     .addTarget(
                         target(
-                            buildMemberItem(CLASS)
+                            buildMemberItem(CLASS, helper)
                                 .setMemberPattern(defaultInitializerPattern())
-                                .build()))
+                                .build(),
+                            helper))
                     .build())
+            .setBindings(helper.build())
             .build();
     assertEquals(
         StringUtils.unixLines(
@@ -179,28 +201,26 @@
 
   @Test
   public void testKeepInstanceAndInitIfReferencedWithBinding() {
-    KeepBindings.Builder bindings = KeepBindings.builder();
-    KeepBindingSymbol classSymbol = bindings.create("CLASS");
+    BindingsHelper helper = new BindingsHelper();
+    KeepClassBindingReference clazz = helper.freshClassBinding(classItem(CLASS));
     KeepEdge edge =
         KeepEdge.builder()
-            .setBindings(bindings.addBinding(classSymbol, classItem(CLASS)).build())
             .setPreconditions(
                 KeepPreconditions.builder()
-                    .addCondition(
-                        KeepCondition.builder()
-                            .setItemReference(classItemBinding(classSymbol))
-                            .build())
+                    .addCondition(KeepCondition.builder().setItemBindingReference(clazz).build())
                     .build())
             .setConsequences(
                 KeepConsequences.builder()
-                    .addTarget(target(classItemBinding(classSymbol)))
+                    .addTarget(target(clazz))
                     .addTarget(
                         target(
                             KeepMemberItemPattern.builder()
-                                .setClassReference(classItemBinding(classSymbol))
+                                .setClassBindingReference(clazz)
                                 .setMemberPattern(defaultInitializerPattern())
-                                .build()))
+                                .build(),
+                            helper))
                     .build())
+            .setBindings(helper.build())
             .build();
     assertEquals(
         StringUtils.unixLines(
@@ -212,19 +232,27 @@
         extract(edge));
   }
 
-  private KeepClassItemReference classItemBinding(KeepBindingSymbol bindingName) {
-    return KeepBindingReference.forClass(bindingName).toClassItemReference();
+  private KeepClassBindingReference classItemBinding(KeepBindingSymbol bindingName) {
+    return KeepBindingReference.forClass(bindingName);
   }
 
-  private KeepTarget target(KeepItemPattern item) {
-    return KeepTarget.builder().setItemPattern(item).build();
+  private KeepTarget target(KeepItemPattern item, BindingsHelper helper) {
+    return target(helper.freshItemBinding(item));
   }
 
-  private KeepTarget target(KeepItemReference item) {
-    return KeepTarget.builder().setItemReference(item).build();
+  private KeepTarget target(KeepBindingReference item) {
+    return KeepTarget.builder().setItemBindingReference(item).build();
   }
 
-  private KeepItemPattern classItem(String typeName) {
+  private KeepCondition condition(KeepItemPattern item, BindingsHelper helper) {
+    return condition(helper.freshItemBinding(item));
+  }
+
+  private KeepCondition condition(KeepBindingReference item) {
+    return KeepCondition.builder().setItemBindingReference(item).build();
+  }
+
+  private KeepClassItemPattern classItem(String typeName) {
     return buildClassItem(typeName).build();
   }
 
@@ -233,9 +261,14 @@
         .setClassNamePattern(KeepQualifiedClassNamePattern.exact(typeName));
   }
 
-  private KeepMemberItemPattern.Builder buildMemberItem(String typeName) {
+  private KeepMemberItemPattern.Builder buildMemberItem(String holderType, BindingsHelper helper) {
+    return buildMemberItem(helper.freshClassBinding(buildClassItem(holderType).build()));
+  }
+
+  private static KeepMemberItemPattern.Builder buildMemberItem(KeepClassBindingReference holder) {
     return KeepMemberItemPattern.builder()
-        .setClassReference(buildClassItem(typeName).build().toClassItemReference());
+        .setClassBindingReference(holder)
+        .setMemberPattern(KeepMemberPattern.allMembers());
   }
 
   private KeepMemberPattern defaultInitializerPattern() {
@@ -245,4 +278,37 @@
         .setReturnTypeVoid()
         .build();
   }
+
+  private static class BindingsHelper {
+    private final KeepBindings.Builder builder = KeepBindings.builder();
+
+    public KeepClassBindingReference freshClassBinding(KeepClassItemPattern pattern) {
+      KeepBindingSymbol symbol = builder.generateFreshSymbol("CLASS");
+      builder.addBinding(symbol, pattern);
+      return KeepClassBindingReference.forClass(symbol);
+    }
+
+    public KeepMemberBindingReference freshMemberBinding(KeepMemberItemPattern pattern) {
+      KeepBindingSymbol symbol = builder.generateFreshSymbol("MEMBER");
+      builder.addBinding(symbol, pattern);
+      return KeepMemberBindingReference.forMember(symbol);
+    }
+
+    public KeepBindingReference freshItemBinding(KeepItemPattern pattern) {
+      return pattern.apply(this::freshClassBinding, this::freshMemberBinding);
+    }
+
+    public KeepClassBindingReference freshAnyClass() {
+      return freshClassBinding(KeepItemPattern.anyClass());
+    }
+
+    public KeepMemberBindingReference freshAnyMember() {
+      return freshMemberBinding(
+          buildMemberItem(freshClassBinding(KeepItemPattern.anyClass())).build());
+    }
+
+    public KeepBindings build() {
+      return builder.build();
+    }
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlerdoublecheck/AssertionConfigurationAssertionHandlerKotlinDoubleCheckTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlerdoublecheck/AssertionConfigurationAssertionHandlerKotlinDoubleCheckTest.java
index 933d9e2..32e72b6 100644
--- a/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlerdoublecheck/AssertionConfigurationAssertionHandlerKotlinDoubleCheckTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlerdoublecheck/AssertionConfigurationAssertionHandlerKotlinDoubleCheckTest.java
@@ -91,7 +91,7 @@
   @Override
   protected void configureR8(R8FullTestBuilder builder) {
     boolean referencesNotNull =
-        !kotlinParameters.isOlderThan(KotlinCompilerVersion.KOTLINC_1_6_0)
+        !kotlinParameters.isOlderThan(KotlinCompilerVersion.KOTLINC_1_4_20)
             && !kotlinStdlibAsLibrary;
     builder.applyIf(referencesNotNull, b -> b.addDontWarn("org.jetbrains.annotations.NotNull"));
   }
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlersimple/AssertionConfigurationAssertionHandlerKotlinSimpleTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlersimple/AssertionConfigurationAssertionHandlerKotlinSimpleTest.java
index 44d3eca..6f190cd 100644
--- a/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlersimple/AssertionConfigurationAssertionHandlerKotlinSimpleTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlersimple/AssertionConfigurationAssertionHandlerKotlinSimpleTest.java
@@ -62,7 +62,7 @@
   @Override
   protected void configureR8(R8FullTestBuilder builder) {
     boolean referencesNotNull =
-        !kotlinParameters.isOlderThan(KotlinCompilerVersion.KOTLINC_1_6_0)
+        !kotlinParameters.isOlderThan(KotlinCompilerVersion.KOTLINC_1_4_20)
             && !kotlinStdlibAsLibrary
             && !useJvmAssertions;
     builder
diff --git a/src/test/testbase/java/com/android/tools/r8/KotlinCompilerTool.java b/src/test/testbase/java/com/android/tools/r8/KotlinCompilerTool.java
index c548593..8b7b4dd 100644
--- a/src/test/testbase/java/com/android/tools/r8/KotlinCompilerTool.java
+++ b/src/test/testbase/java/com/android/tools/r8/KotlinCompilerTool.java
@@ -3,10 +3,10 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8;
 
-import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_9_21;
 import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.MAX_SUPPORTED_VERSION;
 import static com.android.tools.r8.ToolHelper.isWindows;
 import static com.google.common.io.Files.getNameWithoutExtension;
+import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
@@ -26,7 +26,6 @@
 import com.google.common.hash.Hasher;
 import java.io.File;
 import java.io.IOException;
-import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
@@ -36,7 +35,9 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.function.Consumer;
+import java.util.jar.Manifest;
 import java.util.stream.Collectors;
+import java.util.zip.ZipFile;
 import org.junit.rules.TemporaryFolder;
 
 public class KotlinCompilerTool {
@@ -216,6 +217,21 @@
       return annotationJar;
     }
 
+    public void checkClasspath() throws IOException {
+      try (ZipFile zipFile = new ZipFile(compiler.toFile(), UTF_8)) {
+        Manifest manifest =
+            new Manifest(zipFile.getInputStream(zipFile.getEntry("META-INF/MANIFEST.MF")));
+        String classPath = manifest.getMainAttributes().getValue("Class-Path");
+        String[] classPathEntries = classPath.split(" ");
+        // Expect all classpath entries to be present ajacent to the compiler jar.
+        for (String classPathEntry : classPathEntries) {
+          assert !classPathEntry.contains("/");
+          assert Files.exists(compiler.getParent().resolve(classPathEntry))
+              : "Kotlin compiler missing classpath: " + classPathEntry;
+        }
+      }
+    }
+
     @Override
     public String toString() {
       return name;
@@ -439,6 +455,7 @@
 
   private CommandLineAndHasherConsumers buildCommandLineAndHasherConsumers(Path output)
       throws IOException {
+    compiler.checkClasspath();
     CommandLineAndHasherConsumers commandLineAndHasherConsumers =
         new CommandLineAndHasherConsumers();
     List<String> cmdline = commandLineAndHasherConsumers.cmdline;
@@ -460,7 +477,7 @@
     // Until now this is just command line files, no inputs, hash existing command
     String noneFileCommandLineArguments = StringUtils.join("", cmdline);
     commandLineAndHasherConsumers.hasherConsumers.add(
-        hasher -> hasher.putString(noneFileCommandLineArguments, StandardCharsets.UTF_8));
+        hasher -> hasher.putString(noneFileCommandLineArguments, UTF_8));
 
     for (Path source : sources) {
       cmdline.add(source.toString());
@@ -478,14 +495,14 @@
       for (Path path : classpath) {
         commandLineAndHasherConsumers.hasherConsumers.add(
             hasher -> {
-              hasher.putString("--cp", StandardCharsets.UTF_8);
+              hasher.putString("--cp", UTF_8);
               hasher.putBytes(Files.readAllBytes(path));
             });
       }
     }
     cmdline.addAll(additionalArguments);
     commandLineAndHasherConsumers.hasherConsumers.add(
-        hasher -> additionalArguments.forEach(s -> hasher.putString(s, StandardCharsets.UTF_8)));
+        hasher -> additionalArguments.forEach(s -> hasher.putString(s, UTF_8)));
     return commandLineAndHasherConsumers;
   }
 }
diff --git a/third_party/android_jar/lib-v35.tar.gz.sha1 b/third_party/android_jar/lib-v35.tar.gz.sha1
index f1f5b61..3543c83 100644
--- a/third_party/android_jar/lib-v35.tar.gz.sha1
+++ b/third_party/android_jar/lib-v35.tar.gz.sha1
@@ -1 +1 @@
-c9ad2358cc165f07fd68ef2151741ba12b26dcbc
\ No newline at end of file
+9ac3858ee6859c45500fa7c5254592516c5e73b7
\ No newline at end of file
diff --git a/third_party/api_database/api_database.tar.gz.sha1 b/third_party/api_database/api_database.tar.gz.sha1
index a3e3b5e..0d2c06c 100644
--- a/third_party/api_database/api_database.tar.gz.sha1
+++ b/third_party/api_database/api_database.tar.gz.sha1
@@ -1 +1 @@
-6fcae4d91999ef50dfbd2367c02270bc0ab9d2de
\ No newline at end of file
+b043b19332f3f7cd2578874b6d051462b55ac56a
\ No newline at end of file
diff --git a/tools/download_kotlin_dev.py b/tools/download_kotlin_dev.py
index aa73ba8..7359f1d 100755
--- a/tools/download_kotlin_dev.py
+++ b/tools/download_kotlin_dev.py
@@ -14,10 +14,14 @@
     url_request = urllib
 import os
 import sys
+import xml.etree.ElementTree
+
 
 JETBRAINS_KOTLIN_MAVEN_URL = "https://maven.pkg.jetbrains.space/kotlin/p/" \
                              "kotlin/bootstrap/org/jetbrains/kotlin/"
 KOTLIN_RELEASE_URL = JETBRAINS_KOTLIN_MAVEN_URL + "kotlin-compiler/"
+KOTLINC_LIB = os.path.join(utils.THIRD_PARTY, "kotlin",
+                   "kotlin-compiler-dev", "kotlinc", "lib")
 
 
 def download_newest():
@@ -55,12 +59,12 @@
         raise Exception('Url: %s \n returned %s' %
                         (KOTLIN_RELEASE_URL, response.getcode()))
 
+    # Check POM for expected dependencies.
+    check_pom(top_most_version_and_build)
+
     # We can now download all files related to the kotlin compiler version.
     print("Downloading version: " + top_most_version_and_build)
 
-    kotlinc_lib = os.path.join(utils.THIRD_PARTY, "kotlin",
-                               "kotlin-compiler-dev", "kotlinc", "lib")
-
     utils.DownloadFromGoogleCloudStorage(
         os.path.join(utils.THIRD_PARTY, "kotlin",
                      "kotlin-compiler-dev.tar.gz.sha1"))
@@ -68,20 +72,61 @@
     download_and_save(
         JETBRAINS_KOTLIN_MAVEN_URL +
         "kotlin-compiler/{0}/kotlin-compiler-{0}.jar".format(
-            top_most_version_and_build), kotlinc_lib, "kotlin-compiler.jar")
+            top_most_version_and_build), KOTLINC_LIB, "kotlin-compiler.jar")
     download_and_save(
         JETBRAINS_KOTLIN_MAVEN_URL +
         "kotlin-stdlib/{0}/kotlin-stdlib-{0}.jar".format(
-            top_most_version_and_build), kotlinc_lib, "kotlin-stdlib.jar")
+            top_most_version_and_build), KOTLINC_LIB, "kotlin-stdlib.jar")
+    download_and_save(
+        JETBRAINS_KOTLIN_MAVEN_URL +
+        "kotlin-stdlib-jdk8/{0}/kotlin-stdlib-jdk8-{0}.jar".format(
+            top_most_version_and_build), KOTLINC_LIB, "kotlin-stdlib-jdk8.jar")
+    # POM file has dependency on version 1.6.10 - download latest anyway.
     download_and_save(
         JETBRAINS_KOTLIN_MAVEN_URL +
         "kotlin-reflect/{0}/kotlin-reflect-{0}.jar".format(
-            top_most_version_and_build), kotlinc_lib, "kotlin-reflect.jar")
+            top_most_version_and_build), KOTLINC_LIB, "kotlin-reflect.jar")
     download_and_save(
         JETBRAINS_KOTLIN_MAVEN_URL +
         "kotlin-script-runtime/{0}/kotlin-script-runtime-{0}.jar".format(
-            top_most_version_and_build), kotlinc_lib,
+            top_most_version_and_build), KOTLINC_LIB,
         "kotlin-script-runtime.jar")
+    download_and_save(
+        "https://repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.6.4/kotlinx-coroutines-core-jvm-1.6.4.jar", KOTLINC_LIB, "kotlinx-coroutines-core-jvm.jar")
+    download_and_save(
+        "https://repo1.maven.org/maven2/org/jetbrains/intellij/deps/trove4j/1.0.20200330/trove4j-1.0.20200330.jar", KOTLINC_LIB, "trove4j.jar")
+
+
+def check_pom(top_most_version_and_build):
+    # Download POM, and check the expected dependencies.
+    download_and_save(
+        JETBRAINS_KOTLIN_MAVEN_URL +
+        "kotlin-compiler/{0}/kotlin-compiler-{0}.pom".format(
+            top_most_version_and_build), KOTLINC_LIB, "kotlin-compiler.pom")
+    pom_file = os.path.join(KOTLINC_LIB, "kotlin-compiler.pom")
+    ns = "http://maven.apache.org/POM/4.0.0"
+    xml.etree.ElementTree.register_namespace('', ns)
+    tree = xml.etree.ElementTree.ElementTree()
+    tree.parse(pom_file)
+    #return tree.getroot().find("{%s}dependencies" % ns).text
+    for dependency in tree.getroot().find("{%s}dependencies" % ns):
+        groupId = dependency.find("{%s}groupId" % ns).text
+        artifactId = dependency.find("{%s}artifactId" % ns).text
+        version = dependency.find("{%s}version" % ns).text
+        coordinates = (
+            '{groupId}:{artifactId}:{version}'
+                .format(groupId=groupId, artifactId=artifactId, version=version))
+        print('Dependecy: ' + coordinates)
+        expected_dependencies = set()
+        for artifactId in ("kotlin-stdlib", "kotlin-stdlib-jdk8", "kotlin-script-runtime"):
+            expected_dependencies.add(
+                'org.jetbrains.kotlin:{artifactId}:{version}'
+                    .format(artifactId=artifactId, version=top_most_version_and_build))
+        expected_dependencies.add('org.jetbrains.kotlin:kotlin-reflect:1.6.10')
+        expected_dependencies.add('org.jetbrains.intellij.deps:trove4j:1.0.20200330')
+        expected_dependencies.add('org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.6.4')
+        if not coordinates in expected_dependencies:
+            raise Exception('Unexpected dependency: ' + coordinates)
 
 
 def download_and_save(url, path, name):