Open interfaces analysis on cf code

Bug: b/214496607
Bug: b/236581210
Change-Id: I4f0ffb82a2df1d98c80e3a7f74c19bdb34847fe9
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 223fc66..72508bb 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -442,6 +442,7 @@
       assert appView.appInfo().hasLiveness();
       AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
 
+      // TODO(b/214496607): Evaluate build speed. Avoid fixpoint when frames are present.
       assert new CfOpenClosedInterfacesAnalysis(appViewWithLiveness).run(executorService);
 
       new StartupInstrumentation(appView).instrumentAllClasses(executorService);
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfArrayStore.java b/src/main/java/com/android/tools/r8/cf/code/CfArrayStore.java
index 918203f..41f6774 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfArrayStore.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfArrayStore.java
@@ -36,6 +36,16 @@
     return getStoreType();
   }
 
+  @Override
+  public boolean isArrayStore() {
+    return true;
+  }
+
+  @Override
+  public CfArrayStore asArrayStore() {
+    return this;
+  }
+
   private int getStoreType() {
     switch (getType()) {
       case OBJECT:
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
index 80813e1..9ff6d09 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
@@ -77,18 +77,6 @@
     return visitor.visit(this, other.asFieldInstruction(), CfFieldInstruction::specify);
   }
 
-  public boolean isFieldGet() {
-    return false;
-  }
-
-  public boolean isStaticFieldGet() {
-    return false;
-  }
-
-  public boolean isStaticFieldPut() {
-    return false;
-  }
-
   public abstract CfFieldInstruction createWithField(DexField field);
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFrameVerificationHelper.java b/src/main/java/com/android/tools/r8/cf/code/CfFrameVerificationHelper.java
index 1a62143..b145f37 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfFrameVerificationHelper.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFrameVerificationHelper.java
@@ -103,6 +103,11 @@
     return true;
   }
 
+  @Override
+  public boolean isStrengthenFramesEnabled() {
+    return false;
+  }
+
   public void seenLabel(CfLabel label) {
     if (tryCatchRangeLabels.contains(label)) {
       for (CfTryCatch tryCatchRange : tryCatchRanges) {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInstanceFieldRead.java b/src/main/java/com/android/tools/r8/cf/code/CfInstanceFieldRead.java
index 089f5ee..5384772 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInstanceFieldRead.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInstanceFieldRead.java
@@ -43,6 +43,11 @@
   }
 
   @Override
+  public boolean isInstanceFieldGet() {
+    return true;
+  }
+
+  @Override
   public CfFieldInstruction createWithField(DexField otherField) {
     return new CfInstanceFieldRead(otherField);
   }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInstanceFieldWrite.java b/src/main/java/com/android/tools/r8/cf/code/CfInstanceFieldWrite.java
index c492db1..7fffb3d 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInstanceFieldWrite.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInstanceFieldWrite.java
@@ -46,6 +46,21 @@
   }
 
   @Override
+  public boolean isFieldPut() {
+    return true;
+  }
+
+  @Override
+  public boolean isInstanceFieldPut() {
+    return true;
+  }
+
+  @Override
+  public CfInstanceFieldWrite asInstanceFieldPut() {
+    return this;
+  }
+
+  @Override
   void internalRegisterUse(
       UseRegistry<?> registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) {
     registry.registerInstanceFieldWrite(getField());
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
index 66e3d69..bc8e8fb 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
@@ -135,6 +135,14 @@
     return TraversalContinuation.doContinue(initialValue);
   }
 
+  public boolean isArrayStore() {
+    return false;
+  }
+
+  public CfArrayStore asArrayStore() {
+    return null;
+  }
+
   @Override
   public CfInstruction asCfInstruction() {
     return this;
@@ -182,6 +190,38 @@
     return false;
   }
 
+  public boolean isFieldGet() {
+    return false;
+  }
+
+  public boolean isInstanceFieldGet() {
+    return false;
+  }
+
+  public boolean isStaticFieldGet() {
+    return false;
+  }
+
+  public boolean isFieldPut() {
+    return false;
+  }
+
+  public boolean isInstanceFieldPut() {
+    return false;
+  }
+
+  public CfInstanceFieldWrite asInstanceFieldPut() {
+    return null;
+  }
+
+  public boolean isStaticFieldPut() {
+    return false;
+  }
+
+  public CfStaticFieldWrite asStaticFieldPut() {
+    return null;
+  }
+
   public CfGoto asGoto() {
     return null;
   }
@@ -311,6 +351,10 @@
     return false;
   }
 
+  public CfReturn asReturn() {
+    return null;
+  }
+
   public boolean isReturnVoid() {
     return false;
   }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfReturn.java b/src/main/java/com/android/tools/r8/cf/code/CfReturn.java
index fe5af15..8fb0024 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfReturn.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfReturn.java
@@ -106,6 +106,11 @@
   }
 
   @Override
+  public CfReturn asReturn() {
+    return this;
+  }
+
+  @Override
   public void buildIR(IRBuilder builder, CfState state, CfSourceCode code) {
     Slot pop = state.pop();
     builder.addReturn(pop.register);
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfStaticFieldWrite.java b/src/main/java/com/android/tools/r8/cf/code/CfStaticFieldWrite.java
index bdad2a3..c904d10 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfStaticFieldWrite.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfStaticFieldWrite.java
@@ -42,11 +42,21 @@
   }
 
   @Override
+  public boolean isFieldPut() {
+    return true;
+  }
+
+  @Override
   public boolean isStaticFieldPut() {
     return true;
   }
 
   @Override
+  public CfStaticFieldWrite asStaticFieldPut() {
+    return this;
+  }
+
+  @Override
   void internalRegisterUse(
       UseRegistry<?> registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) {
     registry.registerStaticFieldWrite(getField());
diff --git a/src/main/java/com/android/tools/r8/graph/DexType.java b/src/main/java/com/android/tools/r8/graph/DexType.java
index 1efc762..36c45bc 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -157,8 +157,7 @@
     if (clazz == null) {
       return false;
     }
-    // TODO(b/214496607): Allow uninstantiated reasoning for closed interfaces.
-    if (clazz.isInterface()) {
+    if (clazz.isInterface() && appView.getOpenClosedInterfacesCollection().isMaybeOpen(clazz)) {
       return false;
     }
     return !appView.appInfo().isInstantiatedDirectlyOrIndirectly(clazz);
diff --git a/src/main/java/com/android/tools/r8/ir/code/MemberType.java b/src/main/java/com/android/tools/r8/ir/code/MemberType.java
index c85a687..a23e5b1 100644
--- a/src/main/java/com/android/tools/r8/ir/code/MemberType.java
+++ b/src/main/java/com/android/tools/r8/ir/code/MemberType.java
@@ -20,6 +20,10 @@
   INT_OR_FLOAT,
   LONG_OR_DOUBLE;
 
+  public boolean isObject() {
+    return this == OBJECT;
+  }
+
   public boolean isPrecise() {
     return this != INT_OR_FLOAT && this != LONG_OR_DOUBLE;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index e504e3d..226cb11 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -86,7 +86,6 @@
 import com.android.tools.r8.naming.IdentifierNameStringMarker;
 import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagator;
 import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagatorIROptimizer;
-import com.android.tools.r8.optimize.interfaces.analysis.OpenClosedInterfacesAnalysis;
 import com.android.tools.r8.position.MethodPosition;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.KeepMethodInfo;
@@ -142,7 +141,6 @@
   private final ServiceLoaderRewriter serviceLoaderRewriter;
   private final EnumValueOptimizer enumValueOptimizer;
   private final EnumUnboxer enumUnboxer;
-  private final OpenClosedInterfacesAnalysis openClosedInterfacesAnalysis;
 
   public final AssumeInserter assumeInserter;
   private final DynamicTypeOptimization dynamicTypeOptimization;
@@ -228,7 +226,6 @@
       this.methodOptimizationInfoCollector = null;
       this.enumValueOptimizer = null;
       this.enumUnboxer = EnumUnboxer.empty();
-      this.openClosedInterfacesAnalysis = OpenClosedInterfacesAnalysis.empty();
       this.assumeInserter = null;
       return;
     }
@@ -262,8 +259,6 @@
       this.memberValuePropagation = new R8MemberValuePropagation(appViewWithLiveness);
       this.methodOptimizationInfoCollector =
           new MethodOptimizationInfoCollector(appViewWithLiveness, this);
-      // TODO(b/214496607): Enable open/closed interfaces analysis.
-      this.openClosedInterfacesAnalysis = OpenClosedInterfacesAnalysis.empty();
       if (options.isMinifying()) {
         this.identifierNameStringMarker = new IdentifierNameStringMarker(appViewWithLiveness);
       } else {
@@ -299,7 +294,6 @@
       this.methodOptimizationInfoCollector = null;
       this.enumValueOptimizer = null;
       this.enumUnboxer = EnumUnboxer.empty();
-      this.openClosedInterfacesAnalysis = OpenClosedInterfacesAnalysis.empty();
     }
     this.stringSwitchRemover =
         options.isStringSwitchConversionEnabled()
@@ -669,7 +663,6 @@
     appView.withArgumentPropagator(
         argumentPropagator -> argumentPropagator.initializeCodeScanner(executorService, timing));
     enumUnboxer.prepareForPrimaryOptimizationPass(graphLensForPrimaryOptimizationPass);
-    openClosedInterfacesAnalysis.prepareForPrimaryOptimizationPass();
     outliner.prepareForPrimaryOptimizationPass(graphLensForPrimaryOptimizationPass);
 
     if (fieldAccessAnalysis != null) {
@@ -857,7 +850,6 @@
     if (inliner != null) {
       inliner.onLastWaveDone(postMethodProcessorBuilder, executorService, timing);
     }
-    openClosedInterfacesAnalysis.onPrimaryOptimizationPassComplete();
 
     // Ensure determinism of method-to-reprocess set.
     appView.testing().checkDeterminism(postMethodProcessorBuilder::dump);
@@ -1164,8 +1156,6 @@
     assert code.verifyTypes(appView);
     assert code.isConsistentSSA(appView);
 
-    openClosedInterfacesAnalysis.analyze(context, code);
-
     if (shouldPassThrough(context)) {
       // If the code is pass trough, do not finalize by overwriting the existing code.
       assert appView.enableWholeProgramOptimizations();
diff --git a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/CfAnalysisConfig.java b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/CfAnalysisConfig.java
index 0a2bec0..bd636c3 100644
--- a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/CfAnalysisConfig.java
+++ b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/CfAnalysisConfig.java
@@ -19,4 +19,6 @@
   int getMaxStack();
 
   boolean isImmediateSuperClassOfCurrentContext(DexType type);
+
+  boolean isStrengthenFramesEnabled();
 }
diff --git a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/CfOpenClosedInterfacesAnalysis.java b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/CfOpenClosedInterfacesAnalysis.java
index ae2e594..f37e786 100644
--- a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/CfOpenClosedInterfacesAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/CfOpenClosedInterfacesAnalysis.java
@@ -4,15 +4,22 @@
 
 package com.android.tools.r8.optimize.interfaces.analysis;
 
+import com.android.tools.r8.cf.code.CfArrayStore;
 import com.android.tools.r8.cf.code.CfAssignability;
+import com.android.tools.r8.cf.code.CfInstanceFieldWrite;
 import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.cf.code.CfReturn;
+import com.android.tools.r8.cf.code.CfStaticFieldWrite;
 import com.android.tools.r8.cf.code.CfSubtypingAssignability;
 import com.android.tools.r8.cf.code.frame.FrameType;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.Code;
+import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexClassAndMember;
 import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
@@ -23,36 +30,52 @@
 import com.android.tools.r8.ir.analysis.framework.intraprocedural.cf.CfBlock;
 import com.android.tools.r8.ir.analysis.framework.intraprocedural.cf.CfControlFlowGraph;
 import com.android.tools.r8.ir.analysis.framework.intraprocedural.cf.CfIntraproceduralDataflowAnalysis;
+import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
+import com.android.tools.r8.ir.analysis.type.InterfaceCollection;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.optimize.interfaces.collection.NonEmptyOpenClosedInterfacesCollection;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.InternalOptions.OpenClosedInterfacesOptions;
 import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.SetUtils;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.UnverifiableCfCodeDiagnostic;
+import com.android.tools.r8.utils.WorkList;
 import com.android.tools.r8.utils.collections.ProgramMethodMap;
+import com.google.common.collect.Sets;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
 
 public class CfOpenClosedInterfacesAnalysis {
 
   private final AppView<AppInfoWithLiveness> appView;
   private final CfAssignability assignability;
+  private final DexItemFactory dexItemFactory;
   private final InternalOptions options;
 
+  private final Set<DexClass> openInterfaces = Sets.newConcurrentHashSet();
+
   private final ProgramMethodMap<UnverifiableCfCodeDiagnostic> unverifiableCodeDiagnostics =
       ProgramMethodMap.createConcurrent();
 
   public CfOpenClosedInterfacesAnalysis(AppView<AppInfoWithLiveness> appView) {
-    InternalOptions options = appView.options();
     this.appView = appView;
     this.assignability = new CfSubtypingAssignability(appView);
-    this.options = options;
+    this.dexItemFactory = appView.dexItemFactory();
+    this.options = appView.options();
   }
 
   public boolean run(ExecutorService executorService) throws ExecutionException {
     processClasses(executorService);
+    setClosedInterfaces();
     reportUnverifiableCodeDiagnostics();
     return true;
   }
@@ -62,14 +85,15 @@
   }
 
   private void processClass(DexProgramClass clazz) {
-    clazz.forEachProgramMethodMatching(DexEncodedMethod::hasCode, this::processMethod);
+    clazz.forEachProgramMethodMatching(
+        DexEncodedMethod::hasCode, method -> openInterfaces.addAll(processMethod(method)));
   }
 
-  private void processMethod(ProgramMethod method) {
+  private Set<DexClass> processMethod(ProgramMethod method) {
     Code code = method.getDefinition().getCode();
     if (!code.isCfCode()) {
       assert code.isDefaultInstanceInitializerCode() || code.isDexCode() || code.isThrowNullCode();
-      return;
+      return Collections.emptySet();
     }
 
     CfCode cfCode = code.asCfCode();
@@ -79,29 +103,25 @@
         new CfIntraproceduralDataflowAnalysis<>(appView, CfFrameState.bottom(), cfg, transfer);
     DataflowAnalysisResult result = analysis.run(cfg.getEntryBlock());
     assert result.isSuccessfulAnalysisResult();
+
+    Set<DexClass> openInterfacesForMethod = Sets.newIdentityHashSet();
     for (CfBlock block : cfg.getBlocks()) {
       if (analysis.isIntermediateBlock(block)) {
         continue;
       }
       CfFrameState state = analysis.computeBlockEntryState(block);
+      if (state.isError()) {
+        return registerUnverifiableCode(method, 0, state.asError());
+      }
       do {
         for (int instructionIndex = block.getFirstInstructionIndex();
             instructionIndex <= block.getLastInstructionIndex();
             instructionIndex++) {
-          // TODO(b/214496607): Determine open interfaces.
           CfInstruction instruction = cfCode.getInstruction(instructionIndex);
+          processInstruction(method, instruction, state, openInterfacesForMethod::add);
           state = transfer.apply(instruction, state).asAbstractState();
           if (state.isError()) {
-            if (options.getCfCodeAnalysisOptions().isUnverifiableCodeReportingEnabled()) {
-              unverifiableCodeDiagnostics.put(
-                  method,
-                  new UnverifiableCfCodeDiagnostic(
-                      method.getMethodReference(),
-                      instructionIndex,
-                      state.asError().getMessage(),
-                      method.getOrigin()));
-            }
-            return;
+            return registerUnverifiableCode(method, instructionIndex, state.asError());
           }
         }
         if (analysis.isBlockWithIntermediateSuccessorBlock(block)) {
@@ -111,6 +131,198 @@
         }
       } while (block != null);
     }
+    return openInterfacesForMethod;
+  }
+
+  private void processInstruction(
+      ProgramMethod method,
+      CfInstruction instruction,
+      CfFrameState state,
+      Consumer<DexClass> openInterfaceConsumer) {
+    assert !state.isError();
+    if (state.isBottom()) {
+      // Represents that this instruction is unreachable from the method entry point.
+      return;
+    }
+    assert state.isConcrete();
+    ConcreteCfFrameState concreteState = state.asConcrete();
+    if (instruction.isArrayStore()) {
+      processArrayStore(instruction.asArrayStore(), concreteState, openInterfaceConsumer);
+    } else if (instruction.isInstanceFieldPut()) {
+      processInstanceFieldPut(
+          instruction.asInstanceFieldPut(), concreteState, openInterfaceConsumer);
+    } else if (instruction.isInvoke()) {
+      processInvoke(instruction.asInvoke(), concreteState, openInterfaceConsumer);
+    } else if (instruction.isReturn() && !instruction.isReturnVoid()) {
+      processReturn(instruction.asReturn(), method, concreteState, openInterfaceConsumer);
+    } else if (instruction.isStaticFieldPut()) {
+      processStaticFieldPut(instruction.asStaticFieldPut(), concreteState, openInterfaceConsumer);
+    }
+  }
+
+  private void processArrayStore(
+      CfArrayStore arrayStore,
+      ConcreteCfFrameState state,
+      Consumer<DexClass> openInterfaceConsumer) {
+    if (!arrayStore.getType().isObject()) {
+      return;
+    }
+    state.peekStackElements(
+        3,
+        stack -> {
+          FrameType array = stack.peekFirst();
+          FrameType value = stack.peekLast();
+          if (array.isInitializedReferenceType()) {
+            DexType arrayType = array.asInitializedReferenceType().getInitializedType();
+            if (arrayType.isArrayType()) {
+              processAssignment(
+                  value, arrayType.toArrayElementType(dexItemFactory), openInterfaceConsumer);
+            } else {
+              assert arrayType.isNullValueType();
+            }
+          } else {
+            assert false;
+          }
+        });
+  }
+
+  private void processInstanceFieldPut(
+      CfInstanceFieldWrite instanceFieldPut,
+      ConcreteCfFrameState state,
+      Consumer<DexClass> openInterfaceConsumer) {
+    state.peekStackElement(
+        head ->
+            processAssignment(head, instanceFieldPut.getField().getType(), openInterfaceConsumer),
+        options);
+  }
+
+  private void processInvoke(
+      CfInvoke invoke, ConcreteCfFrameState state, Consumer<DexClass> openInterfaceConsumer) {
+    DexMethod invokedMethod = invoke.getMethod();
+    state.peekStackElements(
+        invokedMethod.getNumberOfArguments(invoke.isInvokeStatic()),
+        arguments -> {
+          int argumentIndex = 0;
+          for (FrameType argument : arguments) {
+            DexType parameter =
+                invokedMethod.getArgumentType(argumentIndex, invoke.isInvokeStatic());
+            processAssignment(argument, parameter, openInterfaceConsumer);
+            argumentIndex++;
+          }
+        },
+        options);
+  }
+
+  private void processReturn(
+      CfReturn returnInstruction,
+      ProgramMethod context,
+      ConcreteCfFrameState state,
+      Consumer<DexClass> openInterfaceConsumer) {
+    state.peekStackElement(
+        head -> processAssignment(head, context.getReturnType(), openInterfaceConsumer), options);
+  }
+
+  private void processStaticFieldPut(
+      CfStaticFieldWrite staticFieldPut,
+      ConcreteCfFrameState state,
+      Consumer<DexClass> openInterfaceConsumer) {
+    state.peekStackElement(
+        head -> processAssignment(head, staticFieldPut.getField().getType(), openInterfaceConsumer),
+        options);
+  }
+
+  private void processAssignment(
+      FrameType fromType, DexType toType, Consumer<DexClass> openInterfaceConsumer) {
+    if (fromType.isInitializedReferenceType() && !fromType.isNullType()) {
+      processAssignment(
+          fromType.asInitializedReferenceType().getInitializedType(),
+          toType,
+          openInterfaceConsumer);
+    }
+  }
+
+  private void processAssignment(
+      DexType fromType, DexType toType, Consumer<DexClass> openInterfaceConsumer) {
+    processAssignment(
+        fromType.toTypeElement(appView), toType.toTypeElement(appView), openInterfaceConsumer);
+  }
+
+  private void processAssignment(
+      TypeElement fromType, TypeElement toType, Consumer<DexClass> openInterfaceConsumer) {
+    // If the type is an interface type, then check that the assigned value is a subtype of the
+    // interface type, or mark the interface as open.
+    if (!toType.isClassType()) {
+      return;
+    }
+    ClassTypeElement toClassType = toType.asClassType();
+    if (toClassType.getClassType() != dexItemFactory.objectType) {
+      return;
+    }
+    InterfaceCollection interfaceCollection = toClassType.getInterfaces();
+    interfaceCollection.forEachKnownInterface(
+        knownInterfaceType -> {
+          DexClass knownInterface = appView.definitionFor(knownInterfaceType);
+          if (knownInterface == null) {
+            return;
+          }
+          assert knownInterface.isInterface();
+          if (fromType.lessThanOrEqualUpToNullability(toType, appView)) {
+            return;
+          }
+          assert verifyOpenInterfaceWitnessIsSuppressed(fromType, knownInterface);
+          openInterfaceConsumer.accept(knownInterface);
+        });
+  }
+
+  private void setClosedInterfaces() {
+    // If open interfaces are not allowed and there are one or more suppressions, we should find at
+    // least one open interface.
+    OpenClosedInterfacesOptions openClosedInterfacesOptions =
+        options.getOpenClosedInterfacesOptions();
+    assert openClosedInterfacesOptions.isOpenInterfacesAllowed()
+            || !openClosedInterfacesOptions.hasSuppressions()
+            || !openInterfaces.isEmpty()
+        : "Expected to find at least one open interface";
+
+    includeParentOpenInterfaces();
+
+    appView.setOpenClosedInterfacesCollection(
+        new NonEmptyOpenClosedInterfacesCollection(
+            openInterfaces.stream()
+                .map(DexClass::getType)
+                .collect(
+                    Collectors.toCollection(
+                        () -> SetUtils.newIdentityHashSet(openInterfaces.size())))));
+  }
+
+  private void includeParentOpenInterfaces() {
+    // This includes all parent interfaces of each open interface in the set of open interfaces,
+    // by using the open interfaces as the seen set.
+    WorkList<DexClass> worklist = WorkList.newWorkList(openInterfaces);
+    worklist.addAllIgnoringSeenSet(openInterfaces);
+    while (worklist.hasNext()) {
+      DexClass openInterface = worklist.next();
+      for (DexType indirectOpenInterfaceType : openInterface.getInterfaces()) {
+        DexClass indirectOpenInterfaceDefinition = appView.definitionFor(indirectOpenInterfaceType);
+        if (indirectOpenInterfaceDefinition != null) {
+          worklist.addIfNotSeen(indirectOpenInterfaceDefinition);
+        }
+      }
+    }
+  }
+
+  private Set<DexClass> registerUnverifiableCode(
+      ProgramMethod method, int instructionIndex, ErroneousCfFrameState state) {
+    if (options.getCfCodeAnalysisOptions().isUnverifiableCodeReportingEnabled()) {
+      unverifiableCodeDiagnostics.put(
+          method,
+          new UnverifiableCfCodeDiagnostic(
+              method.getMethodReference(),
+              instructionIndex,
+              state.getMessage(),
+              method.getOrigin()));
+    }
+    return Collections.emptySet();
   }
 
   private void reportUnverifiableCodeDiagnostics() {
@@ -121,6 +333,17 @@
     methods.forEach(method -> reporter.warning(unverifiableCodeDiagnostics.get(method)));
   }
 
+  private boolean verifyOpenInterfaceWitnessIsSuppressed(
+      TypeElement valueType, DexClass openInterface) {
+    assert options.getOpenClosedInterfacesOptions().isSuppressed(appView, valueType, openInterface)
+        : "Unexpected open interface "
+            + openInterface.getTypeName()
+            + " (assignment: "
+            + valueType
+            + ")";
+    return true;
+  }
+
   private class TransferFunction
       implements AbstractTransferFunction<CfBlock, CfInstruction, CfFrameState> {
 
@@ -160,6 +383,11 @@
             public boolean isImmediateSuperClassOfCurrentContext(DexType type) {
               return type == context.getHolder().getSuperType();
             }
+
+            @Override
+            public boolean isStrengthenFramesEnabled() {
+              return true;
+            }
           };
       this.context = context;
     }
diff --git a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/ConcreteCfFrameState.java b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/ConcreteCfFrameState.java
index 531db8b..1c2e7ee 100644
--- a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/ConcreteCfFrameState.java
+++ b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/ConcreteCfFrameState.java
@@ -24,6 +24,7 @@
 import com.android.tools.r8.ir.code.ValueType;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.FunctionUtils;
+import com.android.tools.r8.utils.InternalOptions;
 import com.google.common.collect.Iterables;
 import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap;
 import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
@@ -35,6 +36,7 @@
 import java.util.Iterator;
 import java.util.Objects;
 import java.util.function.BiFunction;
+import java.util.function.Consumer;
 import java.util.function.UnaryOperator;
 
 public class ConcreteCfFrameState extends CfFrameState {
@@ -78,6 +80,9 @@
     if (assignabilityResult.isFailed()) {
       return error(assignabilityResult.asFailed().getMessage());
     }
+    if (config.isStrengthenFramesEnabled()) {
+      return this;
+    }
     CfFrame frameCopy = frame.mutableCopy();
     return new ConcreteCfFrameState(
         frameCopy.getMutableLocals(), frameCopy.getMutableStack(), stackHeight);
@@ -135,6 +140,29 @@
     return new ConcreteCfFrameState(locals, newStack, stackHeight);
   }
 
+  public void peekStackElement(Consumer<PreciseFrameType> consumer, InternalOptions options) {
+    if (!stack.isEmpty()) {
+      consumer.accept(stack.peekLast());
+    } else {
+      assert options.getTestingOptions().allowTypeErrors;
+    }
+  }
+
+  public void peekStackElements(
+      int number, Consumer<Deque<PreciseFrameType>> consumer, InternalOptions options) {
+    if (stack.size() >= number) {
+      Deque<PreciseFrameType> result = new ArrayDeque<>(number);
+      Iterator<PreciseFrameType> iterator = stack.descendingIterator();
+      while (iterator.hasNext() && number > 0) {
+        result.addFirst(iterator.next());
+        number--;
+      }
+      consumer.accept(result);
+    } else {
+      assert options.getTestingOptions().allowTypeErrors;
+    }
+  }
+
   @Override
   public CfFrameState pop() {
     return pop(FunctionUtils::getFirst);
diff --git a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/EmptyOpenClosedInterfacesAnalysis.java b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/EmptyOpenClosedInterfacesAnalysis.java
deleted file mode 100644
index 6e09082..0000000
--- a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/EmptyOpenClosedInterfacesAnalysis.java
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (c) 2022, 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.interfaces.analysis;
-
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.code.IRCode;
-
-public class EmptyOpenClosedInterfacesAnalysis extends OpenClosedInterfacesAnalysis {
-
-  private static final EmptyOpenClosedInterfacesAnalysis INSTANCE =
-      new EmptyOpenClosedInterfacesAnalysis();
-
-  private EmptyOpenClosedInterfacesAnalysis() {}
-
-  static EmptyOpenClosedInterfacesAnalysis getInstance() {
-    return INSTANCE;
-  }
-
-  @Override
-  public void analyze(ProgramMethod method, IRCode code) {
-    // Intentionally empty.
-  }
-
-  @Override
-  public void prepareForPrimaryOptimizationPass() {
-    // Intentionally empty.
-  }
-
-  @Override
-  public void onPrimaryOptimizationPassComplete() {
-    // Intentionally empty.
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/OpenClosedInterfacesAnalysis.java b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/OpenClosedInterfacesAnalysis.java
deleted file mode 100644
index dc13d50..0000000
--- a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/OpenClosedInterfacesAnalysis.java
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright (c) 2022, 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.interfaces.analysis;
-
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.code.IRCode;
-
-public abstract class OpenClosedInterfacesAnalysis {
-
-  public static EmptyOpenClosedInterfacesAnalysis empty() {
-    return EmptyOpenClosedInterfacesAnalysis.getInstance();
-  }
-
-  public abstract void analyze(ProgramMethod method, IRCode code);
-
-  public abstract void prepareForPrimaryOptimizationPass();
-
-  public abstract void onPrimaryOptimizationPassComplete();
-}
diff --git a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/OpenClosedInterfacesAnalysisImpl.java b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/OpenClosedInterfacesAnalysisImpl.java
deleted file mode 100644
index 4e474f4..0000000
--- a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/OpenClosedInterfacesAnalysisImpl.java
+++ /dev/null
@@ -1,200 +0,0 @@
-// Copyright (c) 2022, 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.interfaces.analysis;
-
-import static com.android.tools.r8.ir.code.Opcodes.ARRAY_PUT;
-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.STATIC_PUT;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.DexTypeList;
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
-import com.android.tools.r8.ir.analysis.type.InterfaceCollection;
-import com.android.tools.r8.ir.analysis.type.TypeElement;
-import com.android.tools.r8.ir.code.ArrayPut;
-import com.android.tools.r8.ir.code.FieldPut;
-import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.InvokeMethod;
-import com.android.tools.r8.ir.code.Return;
-import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.optimize.interfaces.collection.NonEmptyOpenClosedInterfacesCollection;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.InternalOptions.OpenClosedInterfacesOptions;
-import com.android.tools.r8.utils.SetUtils;
-import com.android.tools.r8.utils.WorkList;
-import com.google.common.collect.Sets;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-public class OpenClosedInterfacesAnalysisImpl extends OpenClosedInterfacesAnalysis {
-
-  private final AppView<AppInfoWithLiveness> appView;
-  private final DexItemFactory dexItemFactory;
-
-  private Set<DexClass> openInterfaces;
-
-  public OpenClosedInterfacesAnalysisImpl(AppView<AppInfoWithLiveness> appView) {
-    this.appView = appView;
-    this.dexItemFactory = appView.dexItemFactory();
-  }
-
-  @Override
-  public void analyze(ProgramMethod method, IRCode code) {
-    if (openInterfaces == null) {
-      return;
-    }
-    // Analyze each instruction that may assign to an interface type.
-    for (Instruction instruction : code.instructions()) {
-      switch (instruction.opcode()) {
-        case ARRAY_PUT:
-          analyzeArrayPut(instruction.asArrayPut());
-          break;
-        case INSTANCE_PUT:
-        case STATIC_PUT:
-          analyzeFieldPut(instruction.asFieldPut());
-          break;
-        case INVOKE_DIRECT:
-        case INVOKE_INTERFACE:
-        case INVOKE_STATIC:
-        case INVOKE_SUPER:
-        case INVOKE_VIRTUAL:
-          analyzeInvokeMethod(instruction.asInvokeMethod());
-          break;
-        case RETURN:
-          analyzeReturn(instruction.asReturn(), method);
-          break;
-        default:
-          break;
-      }
-    }
-  }
-
-  private void analyzeArrayPut(ArrayPut arrayPut) {
-    Value array = arrayPut.array();
-    TypeElement arrayType = array.getType();
-    if (!arrayType.isArrayType()) {
-      return;
-    }
-    TypeElement valueType = arrayPut.value().getType();
-    TypeElement arrayMemberType = arrayType.asArrayType().getMemberType();
-    checkAssignment(valueType, arrayMemberType);
-  }
-
-  private void analyzeFieldPut(FieldPut fieldPut) {
-    TypeElement valueType = fieldPut.value().getType();
-    TypeElement fieldType = fieldPut.getField().getTypeElement(appView);
-    checkAssignment(valueType, fieldType);
-  }
-
-  private void analyzeInvokeMethod(InvokeMethod invoke) {
-    DexTypeList parameters = invoke.getInvokedMethod().getParameters();
-    for (int parameterIndex = 0; parameterIndex < parameters.size(); parameterIndex++) {
-      Value argument = invoke.getArgumentForParameter(parameterIndex);
-      TypeElement argumentType = argument.getType();
-      TypeElement parameterType = parameters.get(parameterIndex).toTypeElement(appView);
-      checkAssignment(argumentType, parameterType);
-    }
-  }
-
-  private void analyzeReturn(Return returnInstruction, ProgramMethod context) {
-    if (returnInstruction.isReturnVoid()) {
-      return;
-    }
-    TypeElement valueType = returnInstruction.returnValue().getType();
-    TypeElement returnType = context.getReturnType().toTypeElement(appView);
-    checkAssignment(valueType, returnType);
-  }
-
-  private void checkAssignment(TypeElement fromType, TypeElement toType) {
-    // If the type is an interface type, then check that the assigned value is a subtype of the
-    // interface type, or mark the interface as open.
-    if (!toType.isClassType()) {
-      return;
-    }
-    ClassTypeElement toClassType = toType.asClassType();
-    if (toClassType.getClassType() != dexItemFactory.objectType) {
-      return;
-    }
-    InterfaceCollection interfaceCollection = toClassType.getInterfaces();
-    interfaceCollection.forEachKnownInterface(
-        knownInterfaceType -> {
-          DexClass knownInterface = appView.definitionFor(knownInterfaceType);
-          if (knownInterface == null) {
-            return;
-          }
-          assert knownInterface.isInterface();
-          if (fromType.lessThanOrEqualUpToNullability(toType, appView)) {
-            return;
-          }
-          assert verifyOpenInterfaceWitnessIsSuppressed(fromType, knownInterface);
-          openInterfaces.add(knownInterface);
-        });
-  }
-
-  @Override
-  public void prepareForPrimaryOptimizationPass() {
-    openInterfaces = Sets.newConcurrentHashSet();
-  }
-
-  @Override
-  public void onPrimaryOptimizationPassComplete() {
-    // If open interfaces are not allowed and there are one or more suppressions, we should find at
-    // least one open interface.
-    OpenClosedInterfacesOptions options = appView.options().getOpenClosedInterfacesOptions();
-    assert options.isOpenInterfacesAllowed()
-            || !options.hasSuppressions()
-            || !openInterfaces.isEmpty()
-        : "Expected to find at least one open interface";
-
-    includeParentOpenInterfaces();
-    appView.setOpenClosedInterfacesCollection(
-        new NonEmptyOpenClosedInterfacesCollection(
-            openInterfaces.stream()
-                .map(DexClass::getType)
-                .collect(
-                    Collectors.toCollection(
-                        () -> SetUtils.newIdentityHashSet(openInterfaces.size())))));
-    openInterfaces = null;
-  }
-
-  private void includeParentOpenInterfaces() {
-    // This includes all parent interfaces of each open interface in the set of open interfaces,
-    // by using the open interfaces as the seen set.
-    WorkList<DexClass> worklist = WorkList.newWorkList(openInterfaces);
-    worklist.addAllIgnoringSeenSet(openInterfaces);
-    while (worklist.hasNext()) {
-      DexClass openInterface = worklist.next();
-      for (DexType indirectOpenInterfaceType : openInterface.getInterfaces()) {
-        DexClass indirectOpenInterfaceDefinition = appView.definitionFor(indirectOpenInterfaceType);
-        if (indirectOpenInterfaceDefinition != null) {
-          worklist.addIfNotSeen(indirectOpenInterfaceDefinition);
-        }
-      }
-    }
-  }
-
-  private boolean verifyOpenInterfaceWitnessIsSuppressed(
-      TypeElement valueType, DexClass openInterface) {
-    OpenClosedInterfacesOptions options = appView.options().getOpenClosedInterfacesOptions();
-    assert options.isSuppressed(appView, valueType, openInterface)
-        : "Unexpected open interface "
-            + openInterface.getTypeName()
-            + " (assignment: "
-            + valueType
-            + ")";
-    return true;
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/optimize/interfaces/collection/DefaultOpenClosedInterfacesCollection.java b/src/main/java/com/android/tools/r8/optimize/interfaces/collection/DefaultOpenClosedInterfacesCollection.java
index f8aeb81..df264e4 100644
--- a/src/main/java/com/android/tools/r8/optimize/interfaces/collection/DefaultOpenClosedInterfacesCollection.java
+++ b/src/main/java/com/android/tools/r8/optimize/interfaces/collection/DefaultOpenClosedInterfacesCollection.java
@@ -22,8 +22,7 @@
 
   @Override
   public boolean isDefinitelyClosed(DexClass clazz) {
-    // TODO(b/214496607): Should return false.
-    return true;
+    return false;
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 1fecada..d338a11 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -1621,6 +1621,14 @@
       allowOpenInterfaces = false;
     }
 
+    public OpenClosedInterfacesOptions suppressSingleOpenInterface(ClassReference classReference) {
+      assert !allowOpenInterfaces;
+      suppressions.add(
+          (appView, valueType, openInterface) ->
+              openInterface.getTypeName().equals(classReference.getTypeName()));
+      return this;
+    }
+
     public void suppressAllOpenInterfaces() {
       assert !allowOpenInterfaces;
       suppressions.add((appView, valueType, openInterface) -> true);
diff --git a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
index f7ce233..8d6ee1d 100644
--- a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
+++ b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
@@ -102,9 +102,9 @@
           .addKeepRuleFiles(MAIN_KEEP)
           .addOptionsModification(
               options ->
-                  options
-                      .getOpenClosedInterfacesOptions()
-                      .suppressZipFileAssignmentsToJavaLangAutoCloseable())
+                  // TODO(b/236581210): Should only suppress AutoCloseable due to assignments from
+                  //  ZipFile.
+                  options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces())
           .compile()
           .apply(c -> FileUtils.writeTextFile(map, c.getProguardMap()))
           .writeToZip(jar);
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java
index 04e2842..c491fac 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java
@@ -68,6 +68,9 @@
         .addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_11_JAR)
         .addLibraryFiles(Jdk11TestUtils.getJdk11LibraryFiles(getStaticTemp()))
         .addKeepRuleFiles(MAIN_KEEP)
+        .addOptionsModification(
+            // TODO(b/236581210): There should be no open interfaces.
+            options -> options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces())
         .applyIf(
             desugar,
             builder ->
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java
index cb549f5..f27071b 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java
@@ -50,6 +50,9 @@
         .setMinApi(parameters.getApiLevel())
         .addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_11_JAR)
         .addKeepRuleFiles(MAIN_KEEP)
+        .addOptionsModification(
+            // TODO(b/236581210): There should be no open interfaces.
+            options -> options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces())
         .compile()
         .inspect(this::assertNotEmpty)
         .inspect(Java11R8CompilationTest::assertNoNests);
diff --git a/src/test/java/com/android/tools/r8/internal/GMSCoreLatestTest.java b/src/test/java/com/android/tools/r8/internal/GMSCoreLatestTest.java
index 60f6fa7..de90bf9 100644
--- a/src/test/java/com/android/tools/r8/internal/GMSCoreLatestTest.java
+++ b/src/test/java/com/android/tools/r8/internal/GMSCoreLatestTest.java
@@ -110,6 +110,8 @@
             "com.android.internal.location.ProviderRequest",
             "com.google.protobuf.java_com_google_android_libraries_performance_primes_release"
                 + "_gmscore__primes_bcdd2915GeneratedExtensionRegistryLite$Loader")
+        .addOptionsModification(
+            options -> options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces())
         .allowDiagnosticMessages()
         .allowUnusedProguardConfigurationRules()
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/internal/GMSCoreV10Test.java b/src/test/java/com/android/tools/r8/internal/GMSCoreV10Test.java
index b09cb00..afc1811 100644
--- a/src/test/java/com/android/tools/r8/internal/GMSCoreV10Test.java
+++ b/src/test/java/com/android/tools/r8/internal/GMSCoreV10Test.java
@@ -196,6 +196,8 @@
             "android.media.RemoteDisplayState$RemoteDisplayInfo",
             "com.android.internal.location.ProviderProperties",
             "com.android.internal.location.ProviderRequest")
+        .addOptionsModification(
+            options -> options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces())
         .allowDiagnosticMessages()
         .allowUnusedProguardConfigurationRules()
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeV1620Test.java b/src/test/java/com/android/tools/r8/internal/YouTubeV1620Test.java
index 56aed18..c8732a9 100644
--- a/src/test/java/com/android/tools/r8/internal/YouTubeV1620Test.java
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeV1620Test.java
@@ -91,6 +91,9 @@
                         keepAllProtosRule(),
                         keepDynamicMethodSignatureRule(),
                         keepNewMessageInfoSignatureRule())
+                    .addOptionsModification(
+                        options ->
+                            options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces())
                     .allowCheckDiscardedErrors(true));
     assertRewrittenProtoSchemasMatch(
         new CodeInspector(getProgramFiles()), r8CompileResult.inspector());
diff --git a/src/test/java/com/android/tools/r8/internal/opensourceapps/TiviTest.java b/src/test/java/com/android/tools/r8/internal/opensourceapps/TiviTest.java
index 128f780..eef3dcf 100644
--- a/src/test/java/com/android/tools/r8/internal/opensourceapps/TiviTest.java
+++ b/src/test/java/com/android/tools/r8/internal/opensourceapps/TiviTest.java
@@ -93,6 +93,8 @@
         .addClasspathFiles(outDirectory.resolve("classpath.jar"))
         .addLibraryFiles(outDirectory.resolve("library.jar"))
         .addKeepRuleFiles(outDirectory.resolve("proguard.config"))
+        .addOptionsModification(
+            options -> options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces())
         .setMinApi(AndroidApiLevel.M)
         .allowDiagnosticMessages()
         .allowUnnecessaryDontWarnWildcards()
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/type/MissingClassesJoinTest.java b/src/test/java/com/android/tools/r8/ir/analysis/type/MissingClassesJoinTest.java
index e654b89..ce06fd4 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/type/MissingClassesJoinTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/type/MissingClassesJoinTest.java
@@ -5,6 +5,9 @@
 package com.android.tools.r8.ir.analysis.type;
 
 import static com.android.tools.r8.DiagnosticsMatcher.diagnosticException;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
+import static org.hamcrest.CoreMatchers.allOf;
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.junit.Assert.assertFalse;
@@ -16,8 +19,11 @@
 import com.android.tools.r8.R8TestCompileResult;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.MethodReference;
 import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.MethodReferenceUtils;
 import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.UnverifiableCfCodeDiagnostic;
 import com.google.common.base.Throwables;
 import java.util.List;
 import org.junit.Test;
@@ -75,16 +81,27 @@
               .setMinApi(parameters.getApiLevel())
               .compileWithExpectedDiagnostics(
                   diagnostics -> {
-                    if (!allowTypeErrors) {
+                    if (allowTypeErrors) {
+                      MethodReference mainMethodReference =
+                          MethodReferenceUtils.mainMethod(TestClass.class);
+                      diagnostics.assertWarningsMatch(
+                          allOf(
+                              diagnosticType(UnverifiableCfCodeDiagnostic.class),
+                              diagnosticMessage(
+                                  containsString(
+                                      "Unverifiable code in `"
+                                          + MethodReferenceUtils.toSourceString(mainMethodReference)
+                                          + "`"))),
+                          diagnosticMessage(
+                              equalTo(
+                                  "The method `"
+                                      + MethodReferenceUtils.toSourceString(mainMethodReference)
+                                      + "` does not type check and will be assumed to "
+                                      + "be unreachable.")));
+                    } else {
                       diagnostics.assertErrorThatMatches(diagnosticException(AssertionError.class));
                     }
-                  })
-              .assertAllWarningMessagesMatch(
-                  equalTo(
-                      "The method `void "
-                          + TestClass.class.getTypeName()
-                          + ".main(java.lang.String[])` does not type check and will be assumed to"
-                          + " be unreachable."));
+                  });
 
       // Compilation fails unless type errors are allowed.
       assertTrue(allowTypeErrors);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/ArrayPutToInterfaceWithObjectMergingTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/ArrayPutToInterfaceWithObjectMergingTest.java
index 1107d60..1e43777 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/ArrayPutToInterfaceWithObjectMergingTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/ArrayPutToInterfaceWithObjectMergingTest.java
@@ -8,8 +8,10 @@
 
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.R8TestBuilder;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
 import com.android.tools.r8.utils.BooleanUtils;
 import java.io.IOException;
@@ -53,15 +55,15 @@
         .addKeepMainRule(Main.class)
         // Keep get() to prevent that we optimize it into having static return type A.
         .addKeepRules("-keepclassmembers class " + Main.class.getTypeName() + " { *** get(...); }")
-        .addNoVerticalClassMergingAnnotations()
+        .addOptionsModification(
+            options ->
+                options
+                    .getOpenClosedInterfacesOptions()
+                    .suppressSingleOpenInterface(Reference.classFromClass(I.class)))
         .applyIf(
-            !enableVerticalClassMerging,
-            testBuilder ->
-                testBuilder
-                    .addOptionsModification(
-                        options ->
-                            options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces())
-                    .enableNoVerticalClassMergingAnnotations())
+            enableVerticalClassMerging,
+            R8TestBuilder::addNoVerticalClassMergingAnnotations,
+            R8TestBuilder::enableNoVerticalClassMergingAnnotations)
         .addVerticallyMergedClassesInspector(
             inspector -> {
               if (enableVerticalClassMerging) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/InstancePutToInterfaceWithObjectMergingTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/InstancePutToInterfaceWithObjectMergingTest.java
index d294a77..aeca839 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/InstancePutToInterfaceWithObjectMergingTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/InstancePutToInterfaceWithObjectMergingTest.java
@@ -8,8 +8,10 @@
 
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.R8TestBuilder;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
 import com.android.tools.r8.utils.BooleanUtils;
 import java.io.IOException;
@@ -53,15 +55,15 @@
         .addKeepMainRule(Main.class)
         // Keep get() to prevent that we optimize it into having static return type A.
         .addKeepRules("-keepclassmembers class " + Main.class.getTypeName() + " { *** get(...); }")
-        .addNoVerticalClassMergingAnnotations()
+        .addOptionsModification(
+            options ->
+                options
+                    .getOpenClosedInterfacesOptions()
+                    .suppressSingleOpenInterface(Reference.classFromClass(I.class)))
         .applyIf(
-            !enableVerticalClassMerging,
-            testBuilder ->
-                testBuilder
-                    .addOptionsModification(
-                        options ->
-                            options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces())
-                    .enableNoVerticalClassMergingAnnotations())
+            enableVerticalClassMerging,
+            R8TestBuilder::addNoVerticalClassMergingAnnotations,
+            R8TestBuilder::enableNoVerticalClassMergingAnnotations)
         .addVerticallyMergedClassesInspector(
             inspector -> {
               if (enableVerticalClassMerging) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/ReturnObjectAsInterfaceMergingTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/ReturnObjectAsInterfaceMergingTest.java
index 87d60b4..f541549 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/ReturnObjectAsInterfaceMergingTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/ReturnObjectAsInterfaceMergingTest.java
@@ -8,8 +8,11 @@
 
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.R8TestBuilder;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ir.optimize.classmerger.vertical.StaticPutToInterfaceWithObjectMergingTest.I;
+import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
 import com.android.tools.r8.utils.BooleanUtils;
 import java.io.IOException;
@@ -53,15 +56,15 @@
         .addKeepMainRule(Main.class)
         // Keep get() to prevent that we optimize it into having static return type A.
         .addKeepRules("-keepclassmembers class " + Main.class.getTypeName() + " { *** get(...); }")
-        .addNoVerticalClassMergingAnnotations()
+        .addOptionsModification(
+            options ->
+                options
+                    .getOpenClosedInterfacesOptions()
+                    .suppressSingleOpenInterface(Reference.classFromClass(I.class)))
         .applyIf(
-            !enableVerticalClassMerging,
-            testBuilder ->
-                testBuilder
-                    .addOptionsModification(
-                        options ->
-                            options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces())
-                    .enableNoVerticalClassMergingAnnotations())
+            enableVerticalClassMerging,
+            R8TestBuilder::addNoVerticalClassMergingAnnotations,
+            R8TestBuilder::enableNoVerticalClassMergingAnnotations)
         .addVerticallyMergedClassesInspector(
             inspector -> {
               if (enableVerticalClassMerging) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/StaticPutToInterfaceWithObjectMergingTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/StaticPutToInterfaceWithObjectMergingTest.java
index 072b819..bfc7276 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/StaticPutToInterfaceWithObjectMergingTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/StaticPutToInterfaceWithObjectMergingTest.java
@@ -8,8 +8,10 @@
 
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.R8TestBuilder;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
 import com.android.tools.r8.utils.BooleanUtils;
 import java.io.IOException;
@@ -53,15 +55,15 @@
         .addKeepMainRule(Main.class)
         // Keep get() to prevent that we optimize it into having static return type A.
         .addKeepRules("-keepclassmembers class " + Main.class.getTypeName() + " { *** get(...); }")
-        .addNoVerticalClassMergingAnnotations()
+        .addOptionsModification(
+            options ->
+                options
+                    .getOpenClosedInterfacesOptions()
+                    .suppressSingleOpenInterface(Reference.classFromClass(I.class)))
         .applyIf(
-            !enableVerticalClassMerging,
-            testBuilder ->
-                testBuilder
-                    .addOptionsModification(
-                        options ->
-                            options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces())
-                    .enableNoVerticalClassMergingAnnotations())
+            enableVerticalClassMerging,
+            R8TestBuilder::addNoVerticalClassMergingAnnotations,
+            R8TestBuilder::enableNoVerticalClassMergingAnnotations)
         .addVerticallyMergedClassesInspector(
             inspector -> {
               if (enableVerticalClassMerging) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InterfaceInvokeWithObjectReceiverInliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InterfaceInvokeWithObjectReceiverInliningTest.java
index d74eb4b..3aaa789 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InterfaceInvokeWithObjectReceiverInliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InterfaceInvokeWithObjectReceiverInliningTest.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.R8TestBuilder;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.StringUtils;
@@ -62,11 +63,19 @@
         .addKeepMainRule(Main.class)
         // Keep get() to prevent that we optimize it into having static return type A.
         .addKeepRules("-keepclassmembers class " + Main.class.getTypeName() + " { *** get(...); }")
-        .addInliningAnnotations()
-        .addNoVerticalClassMergingAnnotations()
-        .applyIf(!enableInlining, R8TestBuilder::enableInliningAnnotations)
+        .addOptionsModification(
+            options ->
+                options
+                    .getOpenClosedInterfacesOptions()
+                    .suppressSingleOpenInterface(Reference.classFromClass(I.class)))
         .applyIf(
-            !enableVerticalClassMerging, R8TestBuilder::enableNoVerticalClassMergingAnnotations)
+            enableInlining,
+            R8TestBuilder::addInliningAnnotations,
+            R8TestBuilder::enableInliningAnnotations)
+        .applyIf(
+            enableVerticalClassMerging,
+            R8TestBuilder::addNoVerticalClassMergingAnnotations,
+            R8TestBuilder::enableNoVerticalClassMergingAnnotations)
         .addVerticallyMergedClassesInspector(
             inspector -> {
               if (enableVerticalClassMerging) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/StaticInvokeWithMultipleObjectsForInterfaceTypesTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/StaticInvokeWithMultipleObjectsForInterfaceTypesTest.java
index 4dc19d3..7bc991e 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/StaticInvokeWithMultipleObjectsForInterfaceTypesTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/StaticInvokeWithMultipleObjectsForInterfaceTypesTest.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.R8TestBuilder;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
 import com.android.tools.r8.utils.BooleanUtils;
 import java.io.IOException;
@@ -62,16 +63,20 @@
         // Keep getA() and getB() to prevent that we optimize it into having static return type A/B.
         .addKeepRules("-keepclassmembers class " + Main.class.getTypeName() + " { *** get?(...); }")
         .addInliningAnnotations()
-        .addNoVerticalClassMergingAnnotations()
-        .applyIf(!enableInlining, R8TestBuilder::enableInliningAnnotations)
+        .addOptionsModification(
+            options ->
+                options
+                    .getOpenClosedInterfacesOptions()
+                    .suppressSingleOpenInterface(Reference.classFromClass(I.class))
+                    .suppressSingleOpenInterface(Reference.classFromClass(J.class)))
         .applyIf(
-            !enableVerticalClassMerging,
-            testBuilder ->
-                testBuilder
-                    .addOptionsModification(
-                        options ->
-                            options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces())
-                    .enableNoVerticalClassMergingAnnotations())
+            enableInlining,
+            R8TestBuilder::addInliningAnnotations,
+            R8TestBuilder::enableInliningAnnotations)
+        .applyIf(
+            enableVerticalClassMerging,
+            R8TestBuilder::addNoVerticalClassMergingAnnotations,
+            R8TestBuilder::enableNoVerticalClassMergingAnnotations)
         .enableNoHorizontalClassMergingAnnotations()
         .setMinApi(parameters.getApiLevel())
         .compile()
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceCheckCastTest.java b/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceCheckCastTest.java
index e8d4e5f..c756f8c 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceCheckCastTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceCheckCastTest.java
@@ -41,7 +41,7 @@
         .addProgramClasses(getProgramClasses())
         .addProgramClassFileData(getTransformedMainClass())
         .run(parameters.getRuntime(), Main.class)
-        .assertSuccessWithOutputLines(getExpectedOutputLines(false));
+        .assertSuccessWithOutputLines(getExpectedOutputLines());
   }
 
   @Test
@@ -52,7 +52,7 @@
         .addProgramClassFileData(getTransformedMainClass())
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), Main.class)
-        .assertSuccessWithOutputLines(getExpectedOutputLines(false));
+        .assertSuccessWithOutputLines(getExpectedOutputLines());
   }
 
   @Test
@@ -68,7 +68,7 @@
         .enableNoVerticalClassMergingAnnotations()
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), Main.class)
-        .assertSuccessWithOutputLines(getExpectedOutputLines(true));
+        .assertSuccessWithOutputLines(getExpectedOutputLines());
   }
 
   private List<Class<?>> getProgramClasses() {
@@ -102,11 +102,7 @@
         .transform();
   }
 
-  private List<String> getExpectedOutputLines(boolean isR8) {
-    if (isR8) {
-      // TODO(b/214496607): R8 should not optimize the check-cast instruction since I is open.
-      return ImmutableList.of("OK", "OK");
-    }
+  private List<String> getExpectedOutputLines() {
     if (parameters.isDexRuntime()
         && parameters
             .getDexRuntimeVersion()
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceInliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceInliningTest.java
index e2b4a29..4eaa6a4 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceInliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceInliningTest.java
@@ -71,8 +71,7 @@
         .enableNoVerticalClassMergingAnnotations()
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), Main.class)
-        // TODO(b/214496607): Should succeed with the expected output.
-        .assertFailureWithErrorThatThrows(ClassCastException.class);
+        .assertSuccessWithOutputLines(getExpectedOutputLines());
   }
 
   private List<Class<?>> getProgramClasses() {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceInstanceofTest.java b/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceInstanceofTest.java
index 5a57dc4..dac3848 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceInstanceofTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceInstanceofTest.java
@@ -41,7 +41,7 @@
         .addProgramClasses(getProgramClasses())
         .addProgramClassFileData(getTransformedMainClass())
         .run(parameters.getRuntime(), Main.class)
-        .assertSuccessWithOutputLines(getExpectedOutputLines(false));
+        .assertSuccessWithOutputLines(getExpectedOutputLines());
   }
 
   @Test
@@ -52,7 +52,7 @@
         .addProgramClassFileData(getTransformedMainClass())
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), Main.class)
-        .assertSuccessWithOutputLines(getExpectedOutputLines(false));
+        .assertSuccessWithOutputLines(getExpectedOutputLines());
   }
 
   @Test
@@ -68,7 +68,7 @@
         .enableNoVerticalClassMergingAnnotations()
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), Main.class)
-        .assertSuccessWithOutputLines(getExpectedOutputLines(true));
+        .assertSuccessWithOutputLines(getExpectedOutputLines());
   }
 
   private List<Class<?>> getProgramClasses() {
@@ -95,11 +95,7 @@
         .transform();
   }
 
-  private List<String> getExpectedOutputLines(boolean isR8) {
-    if (isR8) {
-      // TODO(b/214496607): R8 should not optimize the instanceof instruction since I is open.
-      return ImmutableList.of("true", "true");
-    }
+  private List<String> getExpectedOutputLines() {
     if (parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isEqualTo(Version.V7_0_0)) {
       return ImmutableList.of("true", "true");
     }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenUninstantiatedInterfaceInstanceofTest.java b/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenUninstantiatedInterfaceInstanceofTest.java
index 68c055d..07b765f 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenUninstantiatedInterfaceInstanceofTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenUninstantiatedInterfaceInstanceofTest.java
@@ -40,7 +40,7 @@
         .addProgramClasses(getProgramClasses())
         .addProgramClassFileData(getTransformedMainClass())
         .run(parameters.getRuntime(), Main.class)
-        .assertSuccessWithOutputLines(getExpectedOutputLines(false));
+        .assertSuccessWithOutputLines(getExpectedOutputLines());
   }
 
   @Test
@@ -51,7 +51,7 @@
         .addProgramClassFileData(getTransformedMainClass())
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), Main.class)
-        .assertSuccessWithOutputLines(getExpectedOutputLines(false));
+        .assertSuccessWithOutputLines(getExpectedOutputLines());
   }
 
   @Test
@@ -65,7 +65,7 @@
         .enableInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), Main.class)
-        .assertSuccessWithOutputLines(getExpectedOutputLines(true));
+        .assertSuccessWithOutputLines(getExpectedOutputLines());
   }
 
   private List<Class<?>> getProgramClasses() {
@@ -92,11 +92,7 @@
         .transform();
   }
 
-  private List<String> getExpectedOutputLines(boolean isR8) {
-    if (isR8) {
-      // TODO(b/214496607): R8 should not optimize the instanceof instruction since I is open.
-      return ImmutableList.of("true");
-    }
+  private List<String> getExpectedOutputLines() {
     if (parameters.isDexRuntime()) {
       if (parameters.getDexRuntimeVersion().isEqualTo(Version.V7_0_0)
           || parameters.getDexRuntimeVersion().isEqualTo(Version.V13_0_0)) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceRemovalTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceRemovalTest.java
index 12bc414..521d164 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceRemovalTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceRemovalTest.java
@@ -14,6 +14,7 @@
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import org.junit.Test;
@@ -40,6 +41,13 @@
     testForR8(parameters.getBackend())
         .addInnerClasses(UnusedInterfaceRemovalTest.class)
         .addKeepMainRule(TestClass.class)
+        .addOptionsModification(
+            options ->
+                // TODO(b/236581210): I is spuriously reported as being "open" due to lossy joins in
+                //  the analysis of cf code.
+                options
+                    .getOpenClosedInterfacesOptions()
+                    .suppressSingleOpenInterface(Reference.classFromClass(I.class)))
         .enableNoVerticalClassMergingAnnotations()
         .enableNoHorizontalClassMergingAnnotations()
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/InterfaceInvokeWithNonTrivialButImpreciseStaticTypeTest.java b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/InterfaceInvokeWithNonTrivialButImpreciseStaticTypeTest.java
index 836c703..307032a 100644
--- a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/InterfaceInvokeWithNonTrivialButImpreciseStaticTypeTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/InterfaceInvokeWithNonTrivialButImpreciseStaticTypeTest.java
@@ -9,6 +9,7 @@
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.references.Reference;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -31,6 +32,13 @@
     testForR8(parameters.getBackend())
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
+        .addOptionsModification(
+            options ->
+                // TODO(b/236581210): I is spuriously reported as being "open" due to lossy joins in
+                //  the analysis of cf code.
+                options
+                    .getOpenClosedInterfacesOptions()
+                    .suppressSingleOpenInterface(Reference.classFromClass(I.class)))
         .enableNoHorizontalClassMergingAnnotations()
         .enableNoVerticalClassMergingAnnotations()
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/regress/b78493232/Regress78493232_WithPhi.java b/src/test/java/com/android/tools/r8/regress/b78493232/Regress78493232_WithPhi.java
index 077e3c2..7028257 100644
--- a/src/test/java/com/android/tools/r8/regress/b78493232/Regress78493232_WithPhi.java
+++ b/src/test/java/com/android/tools/r8/regress/b78493232/Regress78493232_WithPhi.java
@@ -4,7 +4,9 @@
 package com.android.tools.r8.regress.b78493232;
 
 import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
-import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.startsWith;
 import static org.junit.Assume.assumeTrue;
 
 import com.android.tools.r8.AsmTestBase;
@@ -96,12 +98,14 @@
                 diagnostics ->
                     diagnostics.assertWarningsMatch(
                         diagnosticMessage(
-                            equalTo(
-                                "Unverifiable code in `java.lang.String regress78493232.Test."
-                                    + "methodCausingIssue(byte, short, int)` at instruction 53: "
-                                    + "Cannot join stacks, expected frame types at stack index 1 "
-                                    + "to join to a precise (non-top) type, but types null and "
-                                    + "uninitialized java.lang.String do not."))))
+                            allOf(
+                                startsWith(
+                                    "Unverifiable code in `java.lang.String regress78493232.Test."
+                                        + "methodCausingIssue(byte, short, int)`"),
+                                containsString(
+                                    "Cannot join stacks, expected frame types at stack index 1 "
+                                        + "to join to a precise (non-top) type, but types null "
+                                        + "and uninitialized java.lang.String do not.")))))
             .run(parameters.getRuntime(), MAIN);
     checkResult(result);
   }
diff --git a/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java b/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java
index 369d584..6740f60 100644
--- a/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java
@@ -10,18 +10,15 @@
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.R8;
 import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.R8TestBuilder;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.references.ClassReference;
-import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -84,6 +81,7 @@
         .addDontWarnGoogle()
         .addDontWarnJavax()
         .addDontWarn("org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement")
+        .apply(this::suppressAllOpenInterfaces)
         .compile();
   }
 
@@ -97,6 +95,7 @@
         .addDontWarnGoogle()
         .addDontWarnJavax()
         .addDontWarn("org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement")
+        .apply(this::suppressAllOpenInterfaces)
         .compile();
   }
 
@@ -122,6 +121,7 @@
         .addDontWarnGoogle()
         .addDontWarnJavax()
         .addDontWarn("org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement")
+        .apply(this::suppressAllOpenInterfaces)
         .compile()
         .inspect(
             inspector -> {
@@ -211,13 +211,9 @@
             .addKeepMainRule(R8.class)
             .addKeepClassRules(CLASS_WITH_ANNOTATED_METHOD)
             .addKeepRules("-keepclassmembers class * { @" + PRESENT_ANNOTATION + " *** *(...); }")
-            .addOptionsModification(
-                options ->
-                    options
-                        .getOpenClosedInterfacesOptions()
-                        .suppressZipFileAssignmentsToJavaLangAutoCloseable())
             .addDontWarnGoogle()
             .addDontWarnJavaxNullableAnnotation()
+            .apply(this::suppressAllOpenInterfaces)
             .apply(this::configureHorizontalClassMerging)
             .compile()
             .graphInspector();
@@ -235,12 +231,8 @@
                     + " *** *(...); }")
             .addDontWarnGoogle()
             .addDontWarnJavaxNullableAnnotation()
-            .addOptionsModification(
-                options ->
-                    options
-                        .getOpenClosedInterfacesOptions()
-                        .suppressZipFileAssignmentsToJavaLangAutoCloseable())
             .apply(this::configureHorizontalClassMerging)
+            .apply(this::suppressAllOpenInterfaces)
             .compile()
             .graphInspector();
     assertRetainedClassesEqual(referenceInspector, ifThenKeepClassMembersInspector);
@@ -258,12 +250,8 @@
                     + " *** *(...); }")
             .addDontWarnGoogle()
             .addDontWarnJavaxNullableAnnotation()
-            .addOptionsModification(
-                options ->
-                    options
-                        .getOpenClosedInterfacesOptions()
-                        .suppressZipFileAssignmentsToJavaLangAutoCloseable())
             .apply(this::configureHorizontalClassMerging)
+            .apply(this::suppressAllOpenInterfaces)
             .compile()
             .graphInspector();
     assertRetainedClassesEqual(referenceInspector, ifThenKeepClassesWithMembersInspector);
@@ -283,12 +271,8 @@
                     + " <2> <3>(...); }")
             .addDontWarnGoogle()
             .addDontWarnJavaxNullableAnnotation()
-            .addOptionsModification(
-                options ->
-                    options
-                        .getOpenClosedInterfacesOptions()
-                        .suppressZipFileAssignmentsToJavaLangAutoCloseable())
             .apply(this::configureHorizontalClassMerging)
+            .apply(this::suppressAllOpenInterfaces)
             .compile()
             .graphInspector();
     assertRetainedClassesEqual(referenceInspector, ifHasMemberThenKeepClassInspector);
@@ -334,10 +318,9 @@
         notInConditional);
   }
 
-  private void assertAllClassesAreSynthetics(Set<String> classNames) {
-    for (String className : classNames) {
-      ClassReference classReference = Reference.classFromTypeName(className);
-      assertTrue(className, SyntheticItemsTestUtils.isExternalSynthetic(classReference));
-    }
+  private void suppressAllOpenInterfaces(R8TestBuilder<?> testBuilder) {
+    // TODO(b/236581210): There should be no open interfaces.
+    testBuilder.addOptionsModification(
+        options -> options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces());
   }
 }
diff --git a/src/test/java/com/android/tools/r8/shaking/keptgraph/WhyAreYouKeepingAllTest.java b/src/test/java/com/android/tools/r8/shaking/keptgraph/WhyAreYouKeepingAllTest.java
index 82d8fff..453028a 100644
--- a/src/test/java/com/android/tools/r8/shaking/keptgraph/WhyAreYouKeepingAllTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/keptgraph/WhyAreYouKeepingAllTest.java
@@ -48,10 +48,9 @@
         .addKeepRuleFiles(MAIN_KEEP)
         .addKeepRules(WHY_ARE_YOU_KEEPING_ALL)
         .addOptionsModification(
-            options ->
-                options
-                    .getOpenClosedInterfacesOptions()
-                    .suppressZipFileAssignmentsToJavaLangAutoCloseable())
+            // TODO(b/236581210): Should only suppress AutoCloseable due to assignments from
+            //  ZipFile.
+            options -> options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces())
         .collectStdout()
         .compile()
         .assertStdoutThatMatches(containsString("referenced in keep rule"))