Only allow instances of final classes in filled-new-array

Bug: b/293501981
Change-Id: I0554aaaba74509a5afff9147ba11e6cfd68c7e14
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/passes/ArrayConstructionSimplifier.java b/src/main/java/com/android/tools/r8/ir/conversion/passes/ArrayConstructionSimplifier.java
index 7ce1b99..03da617 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/passes/ArrayConstructionSimplifier.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/passes/ArrayConstructionSimplifier.java
@@ -4,12 +4,12 @@
 
 package com.android.tools.r8.ir.conversion.passes;
 
+import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
+
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.analysis.type.ArrayTypeElement;
-import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.code.ArrayPut;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.IRCode;
@@ -117,7 +117,7 @@
     RewriteArrayOptions rewriteOptions = options.rewriteArrayOptions();
     InstructionListIterator it = block.listIterator(code);
     while (it.hasNext()) {
-      FilledArrayCandidate candidate = computeFilledArrayCandidate(it.next(), rewriteOptions);
+      FilledArrayCandidate candidate = computeFilledArrayCandidate(code, it.next(), rewriteOptions);
       if (candidate == null) {
         continue;
       }
@@ -367,7 +367,7 @@
   }
 
   private FilledArrayCandidate computeFilledArrayCandidate(
-      Instruction instruction, RewriteArrayOptions options) {
+      IRCode code, Instruction instruction, RewriteArrayOptions options) {
     NewArrayEmpty newArrayEmpty = instruction.asNewArrayEmpty();
     if (newArrayEmpty == null) {
       return null;
@@ -392,52 +392,22 @@
         && arrayType != dexItemFactory.objectArrayType
         && !arrayType.isPrimitiveArrayType()) {
       DexType elementType = arrayType.toArrayElementType(dexItemFactory);
-      for (Instruction uniqueUser : newArrayEmpty.outValue().uniqueUsers()) {
-        if (uniqueUser.isArrayPut()
-            && uniqueUser.asArrayPut().array() == newArrayEmpty.outValue()
-            && !checkTypeOfArrayPut(uniqueUser.asArrayPut(), elementType)) {
-          return null;
-        }
+      if (!elementType.isClassType()) {
+        return null;
+      }
+      DexProgramClass clazz = null;
+      if (appView.enableWholeProgramOptimizations()) {
+        clazz = asProgramClassOrNull(appView.definitionFor(elementType, code.context()));
+      } else if (elementType == code.context().getHolderType()) {
+        clazz = code.context().getHolder();
+      }
+      if (clazz == null || !clazz.isFinal()) {
+        return null;
       }
     }
     return new FilledArrayCandidate(newArrayEmpty, size, encodeAsFilledNewArray);
   }
 
-  private boolean checkTypeOfArrayPut(ArrayPut arrayPut, DexType elementType) {
-    TypeElement valueType = arrayPut.value().getType();
-    if (!valueType.isPrimitiveType() && elementType == dexItemFactory.objectType) {
-      return true;
-    }
-    if (valueType.isNullType() && !elementType.isPrimitiveType()) {
-      return true;
-    }
-    if (elementType.isArrayType()) {
-      if (valueType.isNullType()) {
-        return true;
-      }
-      ArrayTypeElement arrayTypeElement = valueType.asArrayType();
-      if (arrayTypeElement == null
-          || arrayTypeElement.getNesting() != elementType.getNumberOfLeadingSquareBrackets()) {
-        return false;
-      }
-      valueType = arrayTypeElement.getBaseType();
-      elementType = elementType.toBaseType(dexItemFactory);
-    }
-    assert !valueType.isArrayType();
-    assert !elementType.isArrayType();
-    if (valueType.isPrimitiveType() && !elementType.isPrimitiveType()) {
-      return false;
-    }
-    if (valueType.isPrimitiveType()) {
-      return true;
-    }
-    DexClass clazz = appView.definitionFor(elementType);
-    if (clazz == null) {
-      return false;
-    }
-    return valueType.isClassType(elementType);
-  }
-
   private boolean canUseFilledNewArray(DexType arrayType, int size, RewriteArrayOptions options) {
     if (size < options.minSizeForFilledNewArray) {
       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 03a8229..0360999 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -1555,7 +1555,7 @@
     // issues. See b/283715197.
     public boolean canUseSubTypesInFilledNewArray() {
       assert isGeneratingDex();
-      return !canHaveBugPresentUntil(AndroidApiLevel.U);
+      return !canHaveBugPresentUntilInclusive(AndroidApiLevel.U);
     }
 
     // Dalvik doesn't handle new-filled-array with arrays as values. It fails with:
@@ -2486,6 +2486,14 @@
     return true;
   }
 
+  private boolean canHaveBugPresentUntilInclusive(AndroidApiLevel level) {
+    if (desugarState.isOn() || isGeneratingDex()) {
+      return level == null || !getMinApiLevel().isGreaterThan(level);
+    }
+    assert minApiLevel.equals(B);
+    return true;
+  }
+
   /**
    * Allow access modification of synthetic lambda implementation methods in D8 to avoid generating
    * an excessive amount of accessibility bridges. In R8, the lambda implementation methods are
diff --git a/src/test/java/com/android/tools/r8/workaround/FilledNewArrayFromSubtypeWithMissingInterfaceWorkaroundTest.java b/src/test/java/com/android/tools/r8/workaround/FilledNewArrayFromSubtypeWithMissingInterfaceWorkaroundTest.java
index de98a44..53794e3 100644
--- a/src/test/java/com/android/tools/r8/workaround/FilledNewArrayFromSubtypeWithMissingInterfaceWorkaroundTest.java
+++ b/src/test/java/com/android/tools/r8/workaround/FilledNewArrayFromSubtypeWithMissingInterfaceWorkaroundTest.java
@@ -6,16 +6,13 @@
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 
-import com.android.tools.r8.Dex2OatTestRunResult;
 import com.android.tools.r8.NeverInline;
 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.android.tools.r8.ToolHelper.DexVm.Version;
-import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.InstructionSubject;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
@@ -70,25 +67,15 @@
                 compileResult
                     .inspect(this::inspect)
                     .runDex2Oat(parameters.getRuntime())
-                    // TODO(b/293501981): Should not fail verification.
-                    .applyIf(
-                        parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N),
-                        Dex2OatTestRunResult::assertVerificationErrors,
-                        Dex2OatTestRunResult::assertNoVerificationErrors))
+                    .assertNoVerificationErrors())
         .run(parameters.getRuntime(), Main.class)
-        .applyIf(
-            parameters.isCfRuntime()
-                || !parameters.getDexRuntimeVersion().isEqualTo(Version.V13_0_0)
-                || parameters.getApiLevel() == AndroidApiLevel.B,
-            runResult -> runResult.assertFailureWithErrorThatThrows(NoClassDefFoundError.class),
-            runResult -> runResult.assertFailureWithErrorThatThrows(VerifyError.class));
+        .assertFailureWithErrorThatThrows(NoClassDefFoundError.class);
   }
 
   private void inspect(CodeInspector inspector) {
     MethodSubject mainMethodSubject = inspector.clazz(Main.class).mainMethod();
     assertThat(mainMethodSubject, isPresent());
-    assertEquals(
-        parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N),
+    assertFalse(
         mainMethodSubject.streamInstructions().anyMatch(InstructionSubject::isFilledNewArray));
   }