Disallow abstract methods on non-abstract classes

Bug: 158018192
Change-Id: Idbf83805290a2599f757e194ada419291c104002
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index c201290..09aeb11 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -176,12 +176,10 @@
 
   private boolean verifyNoAbstractMethodsOnNonAbstractClasses(
       Iterable<DexEncodedMethod> methods, InternalOptions options) {
-    if (options.canHaveDalvikAbstractMethodOnNonAbstractClassVerificationBug()) {
-      if (!isAbstract()) {
-        for (DexEncodedMethod method : methods) {
-          assert !method.isAbstract()
-              : "Non-abstract method on abstract class: `" + method.method.toSourceString() + "`";
-        }
+    if (options.canHaveDalvikAbstractMethodOnNonAbstractClassVerificationBug() && !isAbstract()) {
+      for (DexEncodedMethod method : methods) {
+        assert !method.isAbstract()
+            : "Non-abstract method on abstract class: `" + method.method.toSourceString() + "`";
       }
     }
     return true;
diff --git a/src/main/java/com/android/tools/r8/shaking/TreePruner.java b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
index 74a79ce..ec2ced1 100644
--- a/src/main/java/com/android/tools/r8/shaking/TreePruner.java
+++ b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
@@ -300,8 +300,7 @@
         // Final classes cannot be abstract, so we have to keep the method in that case.
         // Also some other kinds of methods cannot be abstract, so keep them around.
         boolean allowAbstract =
-            (!options.canHaveDalvikAbstractMethodOnNonAbstractClassVerificationBug()
-                    || clazz.isAbstract())
+            (options.canUseAbstractMethodOnNonAbstractClass() || clazz.isAbstract())
                 && !method.isFinal()
                 && !method.accessFlags.isNative()
                 && !method.accessFlags.isStrict()
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 d7ba750..00a2136 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -1246,6 +1246,22 @@
     return minApiLevel >= level.getLevel();
   }
 
+  /**
+   * Dex2Oat issues a warning for abstract methods on non-abstract classes, so we never allow this.
+   *
+   * <p>Note that having an invoke instruction that targets an abstract method on a non-abstract
+   * class will fail with a verification error on Dalvik. Therefore, this must not be more
+   * permissive than {@code return minApiLevel >= AndroidApiLevel.L.getLevel()}.
+   *
+   * <p>See b/132953944.
+   */
+  @SuppressWarnings("ConstantConditions")
+  public boolean canUseAbstractMethodOnNonAbstractClass() {
+    boolean result = false;
+    assert !(result && canHaveDalvikAbstractMethodOnNonAbstractClassVerificationBug());
+    return result;
+  }
+
   public boolean canUseConstClassInstructions(int cfVersion) {
     assert isGeneratingClassFiles();
     return cfVersion >= requiredCfVersionForConstClassInstructions();
diff --git a/src/test/java/com/android/tools/r8/R8TestCompileResult.java b/src/test/java/com/android/tools/r8/R8TestCompileResult.java
index db3e876..5dac954 100644
--- a/src/test/java/com/android/tools/r8/R8TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/R8TestCompileResult.java
@@ -68,16 +68,7 @@
 
   @Override
   public CodeInspector inspector() throws IOException {
-    return new CodeInspector(
-        app,
-        proguardMap,
-        options -> {
-          // TODO(b/158069726): The dex parser should not transform abstract methods on non-abstract
-          //  classes into empty throwing methods.
-          if (minApiLevel >= 0) {
-            options.minApiLevel = minApiLevel;
-          }
-        });
+    return new CodeInspector(app, proguardMap);
   }
 
   public GraphInspector graphInspector() throws IOException {
diff --git a/src/test/java/com/android/tools/r8/shaking/AbstractMethodOnNonAbstractClassTest.java b/src/test/java/com/android/tools/r8/shaking/AbstractMethodOnNonAbstractClassTest.java
index d9970f3..2f95543 100644
--- a/src/test/java/com/android/tools/r8/shaking/AbstractMethodOnNonAbstractClassTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/AbstractMethodOnNonAbstractClassTest.java
@@ -11,14 +11,10 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.Dex2OatTestRunResult;
 import com.android.tools.r8.R8TestCompileResult;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.ToolHelper.DexVm;
-import com.android.tools.r8.ToolHelper.DexVm.Version;
-import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
 import org.junit.Test;
@@ -56,29 +52,15 @@
     assertThat(classSubject, isPresent());
     assertFalse(classSubject.isAbstract());
 
+    // A.m() is also not made abstract in compat mode.
     MethodSubject methodSubject = classSubject.uniqueMethodWithName("m");
     assertThat(methodSubject, isPresent());
+    assertFalse(methodSubject.isAbstract());
 
     if (parameters.isDexRuntime()) {
-      Dex2OatTestRunResult dex2OatResult = compileResult.runDex2Oat(parameters.getRuntime());
-      if (parameters.getApiLevel().isLessThan(AndroidApiLevel.L)) {
-        // Dalvik does not allow abstract methods on non-abstract classes, so R8 emits an empty
-        // throwing method instead.
-        assertFalse(methodSubject.isAbstract());
-        dex2OatResult.assertStderrMatches(not(containsString(DEX2OAT_WARNING)));
-      } else {
-        // Verify that the method has become abstract.
-        assertTrue(methodSubject.isAbstract());
-
-        DexVm.Version version = parameters.getRuntime().asDex().getVm().getVersion();
-        if (version.equals(Version.V5_1_1) || version.equals(Version.V6_0_1)) {
-          // Art 5.1.1 and 6.0.1 did not report a warning for having an abstract method on a
-          // non-abstract class.
-          dex2OatResult.assertStderrMatches(not(containsString(DEX2OAT_WARNING)));
-        } else {
-          dex2OatResult.assertStderrMatches(containsString(DEX2OAT_WARNING));
-        }
-      }
+      compileResult
+          .runDex2Oat(parameters.getRuntime())
+          .assertStderrMatches(not(containsString(DEX2OAT_WARNING)));
     }
 
     compileResult
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
index 1156e55..b2edae2 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
@@ -136,14 +136,8 @@
   }
 
   public CodeInspector(AndroidApp app, String proguardMapContent) throws IOException {
-    this(app, proguardMapContent, null);
-  }
-
-  public CodeInspector(
-      AndroidApp app, String proguardMapContent, Consumer<InternalOptions> optionsConsumer)
-      throws IOException {
     this(
-        new ApplicationReader(app, runOptionsConsumer(optionsConsumer), Timing.empty())
+        new ApplicationReader(app, runOptionsConsumer(null), Timing.empty())
             .read(StringResource.fromString(proguardMapContent, Origin.unknown())));
   }