Enable uninstantiated type optimization for interfaces

Bug: 214496607
Change-Id: I37b8bb48d86a90028e9430b0bd79dc47863ddb12
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 8315b20..547fd3f 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -150,21 +150,17 @@
   }
 
   public boolean isAlwaysNull(AppView<AppInfoWithLiveness> appView) {
-    return isAlwaysNull(appView.appInfo());
-  }
-
-  public boolean isAlwaysNull(AppInfoWithLiveness appInfo) {
     if (!isClassType()) {
       return false;
     }
-    DexProgramClass clazz = asProgramClassOrNull(appInfo.definitionFor(this));
+    DexProgramClass clazz = asProgramClassOrNull(appView.definitionFor(this));
     if (clazz == null) {
       return false;
     }
-    if (appInfo.options().enableUninstantiatedTypeOptimizationForInterfaces) {
-      return !appInfo.isInstantiatedDirectlyOrIndirectly(clazz);
+    if (clazz.isInterface() && appView.getOpenClosedInterfacesCollection().isMaybeOpen(clazz)) {
+      return false;
     }
-    return !clazz.isInterface() && !appInfo.isInstantiatedDirectlyOrIndirectly(clazz);
+    return !appView.appInfo().isInstantiatedDirectlyOrIndirectly(clazz);
   }
 
   public boolean isSamePackage(DexType other) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
index 5d34c84..04b738c 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
@@ -351,7 +351,7 @@
       }
 
       // Record access.
-      if (field.isProgramField() && appView.appInfo().mayPropagateValueFor(field)) {
+      if (field.isProgramField() && appView.appInfo().mayPropagateValueFor(appView, field)) {
         if (field.getAccessFlags().isStatic() == isStatic) {
           if (isWrite) {
             recordFieldAccessContext(definition, writtenFields, readFields);
@@ -396,7 +396,7 @@
     private void recordAccessThatCannotBeOptimized(
         DexClassAndField field, DexEncodedField definition) {
       constantFields.remove(definition);
-      if (field.isProgramField() && appView.appInfo().mayPropagateValueFor(field)) {
+      if (field.isProgramField() && appView.appInfo().mayPropagateValueFor(appView, field)) {
         destroyFieldAccessContexts(definition);
       }
     }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
index b4584c4..08edc70 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
@@ -124,7 +124,7 @@
 
   private boolean mayPropagateValueFor(DexClassAndField field) {
     if (field.isProgramField()) {
-      return appView.appInfo().mayPropagateValueFor(field.getReference());
+      return appView.appInfo().mayPropagateValueFor(appView, field.getReference());
     }
     return appView.appInfo().assumedValues.containsKey(field.getReference())
         || appView.appInfo().noSideEffects.containsKey(field.getReference());
@@ -132,7 +132,7 @@
 
   private boolean mayPropagateValueFor(DexClassAndMethod method) {
     if (method.isProgramMethod()) {
-      return appView.appInfo().mayPropagateValueFor(method.getReference());
+      return appView.appInfo().mayPropagateValueFor(appView, method.getReference());
     }
     return appView.appInfo().assumedValues.containsKey(method.getReference())
         || appView.appInfo().noSideEffects.containsKey(method.getReference());
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
index 715dfdd..d758988 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
@@ -40,6 +40,7 @@
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoCollection;
 import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.google.common.collect.Sets;
 import it.unimi.dsi.fastutil.objects.Reference2IntMap;
 import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
@@ -448,11 +449,13 @@
   }
 
   private void handleInvokeDirect(InvokeDirect invoke) {
-    if (!appView.enableWholeProgramOptimizations()) {
+    if (!appView.hasLiveness()) {
       killAllNonFinalActiveFields();
       return;
     }
 
+    AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
+
     DexClassAndMethod singleTarget = invoke.lookupSingleTarget(appView, method);
     if (singleTarget == null || !singleTarget.getDefinition().isInstanceInitializer()) {
       killAllNonFinalActiveFields();
@@ -470,7 +473,9 @@
     fieldInitializationInfos.forEachWithDeterministicOrder(
         appView,
         (field, info) -> {
-          if (!appView.appInfo().withLiveness().mayPropagateValueFor(field.getReference())) {
+          if (!appViewWithLiveness
+              .appInfo()
+              .mayPropagateValueFor(appViewWithLiveness, field.getReference())) {
             return;
           }
           if (info.isArgumentInitializationInfo()) {
@@ -485,7 +490,7 @@
             }
           } else if (info.isSingleValue()) {
             SingleValue value = info.asSingleValue();
-            if (value.isMaterializableInContext(appView.withLiveness(), method)) {
+            if (value.isMaterializableInContext(appViewWithLiveness, method)) {
               Value object = invoke.getReceiver().getAliasedValue();
               FieldAndObject fieldAndObject = new FieldAndObject(field.getReference(), object);
               if (field.isFinal()) {
@@ -782,12 +787,13 @@
   }
 
   private void applyObjectState(Value value, ObjectState objectState) {
+    AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
     objectState.forEachAbstractFieldValue(
         (field, fieldValue) -> {
-          if (appView.appInfoWithLiveness().mayPropagateValueFor(field)
+          if (appViewWithLiveness.appInfo().mayPropagateValueFor(appViewWithLiveness, field)
               && fieldValue.isSingleValue()) {
             SingleValue singleFieldValue = fieldValue.asSingleValue();
-            if (singleFieldValue.isMaterializableInContext(appView.withLiveness(), method)) {
+            if (singleFieldValue.isMaterializableInContext(appViewWithLiveness, method)) {
               activeState.putFinalInstanceField(
                   new FieldAndObject(field, value), new MaterializableValue(singleFieldValue));
             }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
index 65b9aaa..8853607 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
@@ -155,7 +155,7 @@
         .getFieldAccessInfoCollection()
         .get(field.getReference())
         .hasReflectiveAccess();
-    if (appView.appInfo().mayPropagateValueFor(field.getReference())) {
+    if (appView.appInfo().mayPropagateValueFor(appView, field.getReference())) {
       getFieldOptimizationInfoForUpdating(field).setAbstractValue(abstractValue);
     }
   }
@@ -192,7 +192,7 @@
   @Override
   public synchronized void methodReturnsAbstractValue(
       DexEncodedMethod method, AppView<AppInfoWithLiveness> appView, AbstractValue value) {
-    if (appView.appInfo().mayPropagateValueFor(method.getReference())) {
+    if (appView.appInfo().mayPropagateValueFor(appView, method.getReference())) {
       getMethodOptimizationInfoForUpdating(method).markReturnsAbstractValue(value);
     }
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
index a9e3cba..5b65a8a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
@@ -63,7 +63,7 @@
   @Override
   public void recordFieldHasAbstractValue(
       DexEncodedField field, AppView<AppInfoWithLiveness> appView, AbstractValue abstractValue) {
-    if (appView.appInfo().mayPropagateValueFor(field.getReference())) {
+    if (appView.appInfo().mayPropagateValueFor(appView, field.getReference())) {
       field.getMutableOptimizationInfo().setAbstractValue(abstractValue);
     }
   }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
index cca53b7..4599085 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
@@ -435,7 +435,7 @@
           // OK, this can be rewritten to have void return type.
           continue;
         }
-        if (!appView.appInfo().mayPropagateValueFor(method)) {
+        if (!appView.appInfo().mayPropagateValueFor(appView, method)) {
           return null;
         }
         AbstractValue returnValueForMethod =
diff --git a/src/main/java/com/android/tools/r8/optimize/interfaces/collection/NonEmptyOpenClosedInterfacesCollection.java b/src/main/java/com/android/tools/r8/optimize/interfaces/collection/NonEmptyOpenClosedInterfacesCollection.java
index da3fce8..b3382e0 100644
--- a/src/main/java/com/android/tools/r8/optimize/interfaces/collection/NonEmptyOpenClosedInterfacesCollection.java
+++ b/src/main/java/com/android/tools/r8/optimize/interfaces/collection/NonEmptyOpenClosedInterfacesCollection.java
@@ -11,7 +11,6 @@
 import com.android.tools.r8.utils.SetUtils;
 import java.util.Set;
 
-/** Computed oracle that may respond "definitely closed" for some or all interfaces. */
 public class NonEmptyOpenClosedInterfacesCollection extends OpenClosedInterfacesCollection {
 
   private final Set<DexType> openInterfaceTypes;
diff --git a/src/main/java/com/android/tools/r8/optimize/interfaces/collection/OpenClosedInterfacesCollection.java b/src/main/java/com/android/tools/r8/optimize/interfaces/collection/OpenClosedInterfacesCollection.java
index 1db6a4c..5b009ba 100644
--- a/src/main/java/com/android/tools/r8/optimize/interfaces/collection/OpenClosedInterfacesCollection.java
+++ b/src/main/java/com/android/tools/r8/optimize/interfaces/collection/OpenClosedInterfacesCollection.java
@@ -32,6 +32,10 @@
 
   public abstract boolean isDefinitelyClosed(DexClass clazz);
 
+  public final boolean isMaybeOpen(DexClass clazz) {
+    return !isDefinitelyClosed(clazz);
+  }
+
   public final boolean isDefinitelyInstanceOfStaticType(
       AppView<AppInfoWithLiveness> appView, Value value) {
     return isDefinitelyInstanceOfStaticType(
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index a55baf7..d601873 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -1038,28 +1038,33 @@
         && !keepInfo.getMethodInfo(method).isPinned(options());
   }
 
-  public boolean mayPropagateValueFor(DexClassAndMember<?, ?> member) {
+  public boolean mayPropagateValueFor(
+      AppView<AppInfoWithLiveness> appView, DexClassAndMember<?, ?> member) {
     assert checkIfObsolete();
-    return member.getReference().apply(this::mayPropagateValueFor, this::mayPropagateValueFor);
+    return member
+        .getReference()
+        .apply(
+            field -> mayPropagateValueFor(appView, field),
+            method -> mayPropagateValueFor(appView, method));
   }
 
-  public boolean mayPropagateValueFor(DexField field) {
+  public boolean mayPropagateValueFor(AppView<AppInfoWithLiveness> appView, DexField field) {
     assert checkIfObsolete();
     if (neverPropagateValue.contains(field)) {
       return false;
     }
-    if (isPinned(field) && !field.getType().isAlwaysNull(this)) {
+    if (isPinned(field) && !field.getType().isAlwaysNull(appView)) {
       return false;
     }
     return true;
   }
 
-  public boolean mayPropagateValueFor(DexMethod method) {
+  public boolean mayPropagateValueFor(AppView<AppInfoWithLiveness> appView, DexMethod method) {
     assert checkIfObsolete();
     if (neverPropagateValue.contains(method)) {
       return false;
     }
-    if (!method.getReturnType().isAlwaysNull(this)
+    if (!method.getReturnType().isAlwaysNull(appView)
         && !getKeepInfo().getMethodInfo(method, this).isOptimizationAllowed(options())) {
       return false;
     }
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 7246240..a11e201 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -331,8 +331,6 @@
   public final OutlineOptions outline = new OutlineOptions();
   public boolean enableInitializedClassesInInstanceMethodsAnalysis = true;
   public boolean enableRedundantFieldLoadElimination = true;
-  // Currently disabled, see b/146957343.
-  public boolean enableUninstantiatedTypeOptimizationForInterfaces = false;
   // TODO(b/138917494): Disable until we have numbers on potential performance penalties.
   public boolean enableRedundantConstNumberOptimization = false;
 
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenUninstantiatedInterfaceAlwaysNullTest.java b/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenUninstantiatedInterfaceAlwaysNullTest.java
new file mode 100644
index 0000000..816da5a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenUninstantiatedInterfaceAlwaysNullTest.java
@@ -0,0 +1,117 @@
+// 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.ir.optimize.interfaces;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.util.List;
+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;
+import org.objectweb.asm.Opcodes;
+
+@RunWith(Parameterized.class)
+public class OpenUninstantiatedInterfaceAlwaysNullTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void testJvm() throws Exception {
+    assumeTrue(parameters.isCfRuntime());
+    testForRuntime(parameters)
+        .addProgramClasses(getProgramClasses())
+        .addProgramClassFileData(getTransformedMainClass())
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(getExpectedOutputLines());
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    assumeTrue(parameters.isDexRuntime());
+    testForD8(parameters.getBackend())
+        .addProgramClasses(getProgramClasses())
+        .addProgramClassFileData(getTransformedMainClass())
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(getExpectedOutputLines());
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(getProgramClasses())
+        .addProgramClassFileData(getTransformedMainClass())
+        .addKeepClassAndMembersRules(Main.class)
+        .addOptionsModification(
+            options -> options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces())
+        // TODO(b/214496607): I should not be merged into A in the first place, since I is open.
+        .enableNoVerticalClassMergingAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(getExpectedOutputLines());
+  }
+
+  private List<Class<?>> getProgramClasses() {
+    return ImmutableList.of(I.class, A.class, B.class);
+  }
+
+  private byte[] getTransformedMainClass() throws IOException {
+    return transformer(Main.class)
+        .transformTypeInsnInMethod(
+            "getB",
+            (opcode, type, visitor) -> {
+              assertEquals(opcode, Opcodes.NEW);
+              assertEquals(type, binaryName(A.class));
+              visitor.visitTypeInsn(opcode, binaryName(B.class));
+            })
+        .transformMethodInsnInMethod(
+            "getB",
+            (opcode, owner, name, descriptor, isInterface, visitor) -> {
+              assertEquals(opcode, Opcodes.INVOKESPECIAL);
+              assertEquals(owner, binaryName(A.class));
+              assertEquals(name, "<init>");
+              visitor.visitMethodInsn(opcode, binaryName(B.class), name, descriptor, isInterface);
+            })
+        .transform();
+  }
+
+  private List<String> getExpectedOutputLines() {
+    return ImmutableList.of("false");
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      I b = getB();
+      System.out.println(b == null);
+    }
+
+    static I getB() {
+      return new A(); // transformed into new B().
+    }
+  }
+
+  @NoVerticalClassMerging
+  interface I {}
+
+  static class A implements I {}
+
+  static class B {}
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/B146957343.java b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/B146957343.java
index 3200360..82755bc 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/B146957343.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/B146957343.java
@@ -33,7 +33,7 @@
   }
 
   @Test
-  public void testWithoutR8() throws Exception {
+  public void testRuntime() throws Exception {
     testForRuntime(parameters)
         .addProgramClasses(I.class, J.class, Main.class)
         .addProgramClassFileData(getAimplementsI())
@@ -42,36 +42,15 @@
   }
 
   @Test
-  public void testWithUninstantiatedTypeOptimizationForInterfaces() throws Exception {
+  public void testR8() throws Exception {
     testForR8(parameters.getBackend())
         .addProgramClasses(I.class, J.class, Main.class)
         .addProgramClassFileData(getAimplementsI())
         .addKeepMainRule(Main.class)
+        .addOptionsModification(
+            options -> options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces())
         .enableInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
-        .addOptionsModification(
-            options -> {
-              options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces();
-              options.enableUninstantiatedTypeOptimizationForInterfaces = true;
-            })
-        .compile()
-        .run(parameters.getRuntime(), Main.class)
-        .assertFailureWithErrorThatThrows(NullPointerException.class);
-  }
-
-  @Test
-  public void testWithoutUninstantiatedTypeOptimizationForInterfaces() throws Exception {
-    testForR8(parameters.getBackend())
-        .addProgramClasses(I.class, J.class, Main.class)
-        .addProgramClassFileData(getAimplementsI())
-        .addKeepMainRule(Main.class)
-        .enableInliningAnnotations()
-        .setMinApi(parameters.getApiLevel())
-        .addOptionsModification(
-            options -> {
-              options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces();
-              options.enableUninstantiatedTypeOptimizationForInterfaces = false;
-            })
         .compile()
         .run(parameters.getRuntime(), Main.class)
         .assertSuccessWithOutputLines("In A.f()");
diff --git a/src/test/java/com/android/tools/r8/shaking/InvalidTypesTest.java b/src/test/java/com/android/tools/r8/shaking/InvalidTypesTest.java
index 2e1ed26..9d3e55f 100644
--- a/src/test/java/com/android/tools/r8/shaking/InvalidTypesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/InvalidTypesTest.java
@@ -16,7 +16,6 @@
 import com.android.tools.r8.R8TestRunResult;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestRunResult;
-import com.android.tools.r8.ToolHelper.DexVm.Version;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.jasmin.JasminBuilder;
 import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
@@ -40,8 +39,7 @@
     D8,
     JAVAC,
     PROGUARD,
-    R8,
-    R8_ENABLE_UNININSTANTATED_TYPE_OPTIMIZATION_FOR_INTERFACES
+    R8
   }
 
   private enum Mode {
@@ -68,8 +66,16 @@
         }
 
         switch (compiler) {
-          case D8:
+          case JAVAC:
+            return StringUtils.joinLines("Hello!", "Goodbye!", "");
+
           case DX:
+          case D8:
+          case R8:
+            if (parameters.isCfRuntime()) {
+              assert compiler == Compiler.R8;
+              return StringUtils.joinLines("Hello!", "Goodbye!", "");
+            }
             switch (parameters.getDexRuntimeVersion()) {
               case V4_0_4:
               case V4_4_4:
@@ -77,6 +83,14 @@
               case V12_0_0:
                 return StringUtils.joinLines("Hello!", "Goodbye!", "");
 
+              case V5_1_1:
+              case V6_0_1:
+              case V8_1_0:
+              case V9_0_0:
+              case DEFAULT:
+                return StringUtils.joinLines(
+                    "Hello!", "Unexpected outcome of checkcast", "Goodbye!", "");
+
               case V7_0_0:
               case V13_MASTER:
                 return StringUtils.joinLines(
@@ -87,50 +101,13 @@
                     "");
 
               default:
-                // Fallthrough.
+                throw new Unreachable();
             }
 
           case PROGUARD:
             return StringUtils.joinLines(
                 "Hello!", "Unexpected outcome of checkcast", "Goodbye!", "");
 
-          case R8:
-            if (parameters.isDexRuntime()) {
-              if (parameters
-                  .getDexRuntimeVersion()
-                  .isEqualToOneOf(
-                      Version.V5_1_1,
-                      Version.V6_0_1,
-                      Version.V8_1_0,
-                      Version.V9_0_0,
-                      Version.DEFAULT)) {
-                return StringUtils.joinLines(
-                    "Hello!", "Unexpected outcome of checkcast", "Goodbye!", "");
-              }
-              if (parameters
-                  .getDexRuntimeVersion()
-                  .isEqualToOneOf(Version.V7_0_0, Version.V13_MASTER)) {
-                return StringUtils.joinLines(
-                    "Hello!",
-                    "Unexpected outcome of checkcast",
-                    "Unexpected outcome of instanceof",
-                    "Goodbye!",
-                    "");
-              }
-            }
-            return StringUtils.joinLines("Hello!", "Goodbye!", "");
-
-          case R8_ENABLE_UNININSTANTATED_TYPE_OPTIMIZATION_FOR_INTERFACES:
-            return StringUtils.joinLines(
-                "Hello!",
-                "Unexpected outcome of getstatic",
-                "Unexpected outcome of checkcast",
-                "Goodbye!",
-                "");
-
-          case JAVAC:
-            return StringUtils.joinLines("Hello!", "Goodbye!", "");
-
           default:
             throw new Unreachable();
         }
@@ -152,7 +129,6 @@
 
         switch (compiler) {
           case R8:
-          case R8_ENABLE_UNININSTANTATED_TYPE_OPTIMIZATION_FOR_INTERFACES:
           case PROGUARD:
             // The unverifiable method has been removed as a result of tree shaking, so the code
             // does not fail with a verification error when trying to load class UnverifiableClass.
@@ -344,39 +320,6 @@
                                 + " type check and will be assumed to be unreachable.")))
             .run(parameters.getRuntime(), mainClass.name);
     checkTestRunResult(r8Result, Compiler.R8);
-
-    R8TestRunResult r8ResultWithUninstantiatedTypeOptimizationForInterfaces =
-        testForR8(parameters.getBackend())
-            .addProgramFiles(inputJar)
-            .addKeepMainRule(mainClass.name)
-            .addKeepRules(
-                "-keep class TestClass { public static I g; }",
-                "-neverinline class TestClass { public static void m(); }")
-            .enableProguardTestOptions()
-            .addOptionsModification(
-                options -> {
-                  if (mode == Mode.INVOKE_UNVERIFIABLE_METHOD) {
-                    options.testing.allowTypeErrors = true;
-                    options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces();
-                  } else if (mode == Mode.INVOKE_VERIFIABLE_METHOD_ON_UNVERIFIABLE_CLASS) {
-                    options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces();
-                  }
-                  options.enableUninstantiatedTypeOptimizationForInterfaces = true;
-                })
-            .allowDiagnosticWarningMessages(allowDiagnosticWarningMessages)
-            .setMinApi(parameters.getApiLevel())
-            .compile()
-            .applyIf(
-                allowDiagnosticWarningMessages,
-                result ->
-                    result.assertAllWarningMessagesMatch(
-                        equalTo(
-                            "The method `void UnverifiableClass.unverifiableMethod()` does not"
-                                + " type check and will be assumed to be unreachable.")))
-            .run(parameters.getRuntime(), mainClass.name);
-    checkTestRunResult(
-        r8ResultWithUninstantiatedTypeOptimizationForInterfaces,
-        Compiler.R8_ENABLE_UNININSTANTATED_TYPE_OPTIMIZATION_FOR_INTERFACES);
   }
 
   private void checkTestRunResult(TestRunResult<?> result, Compiler compiler) {
@@ -390,7 +333,6 @@
           result.assertSuccessWithOutput(getExpectedOutput(compiler));
         } else {
           if (compiler == Compiler.R8
-              || compiler == Compiler.R8_ENABLE_UNININSTANTATED_TYPE_OPTIMIZATION_FOR_INTERFACES
               || compiler == Compiler.PROGUARD) {
             result.assertSuccessWithOutput(getExpectedOutput(compiler));
           } else {
@@ -405,8 +347,7 @@
         if (useInterface) {
           result.assertSuccessWithOutput(getExpectedOutput(compiler));
         } else {
-          if (compiler == Compiler.R8
-              || compiler == Compiler.R8_ENABLE_UNININSTANTATED_TYPE_OPTIMIZATION_FOR_INTERFACES) {
+          if (compiler == Compiler.R8) {
             result
                 .assertFailureWithOutput(getExpectedOutput(compiler))
                 .assertFailureWithErrorThatMatches(