Only generate FilledNewArray when puts match the concrete array type

Bug: b/283715197
Change-Id: Ie24d90160cceeecd790a5074d8dbcbac116fb58e
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index c0d3e6b..3988896 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -2263,7 +2263,7 @@
     }
   }
 
-  private FilledArrayCandidate computeFilledArrayCandiate(
+  private FilledArrayCandidate computeFilledArrayCandidate(
       Instruction instruction, RewriteArrayOptions options) {
     NewArrayEmpty newArrayEmpty = instruction.asNewArrayEmpty();
     if (newArrayEmpty == null) {
@@ -2284,6 +2284,18 @@
     if (!encodeAsFilledNewArray && !canUseFilledArrayData(arrayType, size, options)) {
       return null;
     }
+    // Check that all arguments to the array is the array type or that the array is type Object[].
+    if (!options.canUseSubTypesInFilledNewArray()
+        && arrayType != dexItemFactory.objectArrayType
+        && !arrayType.isPrimitiveArrayType()) {
+      DexType baseType = arrayType.toBaseType(dexItemFactory);
+      for (Instruction uniqueUser : newArrayEmpty.outValue().uniqueUsers()) {
+        if (uniqueUser.isArrayPut()
+            && !uniqueUser.asArrayPut().value().getType().isClassType(baseType)) {
+          return null;
+        }
+      }
+    }
     return new FilledArrayCandidate(newArrayEmpty, size, encodeAsFilledNewArray);
   }
 
@@ -2393,7 +2405,7 @@
     RewriteArrayOptions rewriteOptions = options.rewriteArrayOptions();
     InstructionListIterator it = block.listIterator(code);
     while (it.hasNext()) {
-      FilledArrayCandidate candidate = computeFilledArrayCandiate(it.next(), rewriteOptions);
+      FilledArrayCandidate candidate = computeFilledArrayCandidate(it.next(), rewriteOptions);
       if (candidate == null) {
         continue;
       }
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 2f66e6f..b2ce050 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -1519,6 +1519,13 @@
       return hasFeaturePresentFrom(AndroidApiLevel.N);
     }
 
+    // When adding support for emitting filled-new-array for sub-types, ART 13 (Api-level 33) had
+    // issues. See b/283715197.
+    public boolean canUseSubTypesInFilledNewArray() {
+      assert isGeneratingDex();
+      return !canHaveBugPresentUntil(AndroidApiLevel.U);
+    }
+
     // Dalvik doesn't handle new-filled-array with arrays as values. It fails with:
     // W(629880) VFY: [Ljava/lang/Integer; is not instance of Ljava/lang/Integer;  (dalvikvm)
     public boolean canUseFilledNewArrayOfArrays() {
diff --git a/src/test/java/com/android/tools/r8/TestParameters.java b/src/test/java/com/android/tools/r8/TestParameters.java
index 904d325..c792710 100644
--- a/src/test/java/com/android/tools/r8/TestParameters.java
+++ b/src/test/java/com/android/tools/r8/TestParameters.java
@@ -116,6 +116,10 @@
     return isDexRuntime() && getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N);
   }
 
+  public boolean canUseSubTypesInFilledNewArray() {
+    return isDexRuntime() && getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.U);
+  }
+
   public boolean runtimeWithClassValue() {
     assert isCfRuntime() || isDexRuntime();
     return isCfRuntime() || getDexRuntimeVersion().isNewerThanOrEqual(DexVm.Version.V14_0_0);
diff --git a/src/test/java/com/android/tools/r8/rewrite/arrays/FilledArrayDataRemoveCheckCastTest.java b/src/test/java/com/android/tools/r8/rewrite/arrays/FilledArrayDataRemoveCheckCastTest.java
index 1f4e6fb..71639f7 100644
--- a/src/test/java/com/android/tools/r8/rewrite/arrays/FilledArrayDataRemoveCheckCastTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/arrays/FilledArrayDataRemoveCheckCastTest.java
@@ -68,9 +68,9 @@
               assertEquals(
                   parameters.canUseFilledNewArrayOnNonStringObjects(),
                   filledNewArrayInIterateBaseClasses.isPresent());
-              // TODO(b/283715197): It should not be present.
               assertEquals(
-                  parameters.canUseFilledNewArrayOnNonStringObjects(),
+                  parameters.canUseFilledNewArrayOnNonStringObjects()
+                      && parameters.canUseSubTypesInFilledNewArray(),
                   filledNewArrayInIterateSubClasses.isPresent());
             });
   }