Ensure redundant array get elimination leads to narrowing

Bug: b/278573402
Change-Id: I5f4fd4f491ddac40d4e720862ab711220c0be145
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 a3c270a..178c021 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
@@ -12,10 +12,13 @@
 import com.android.tools.r8.graph.DexClassAndField;
 import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.FieldResolutionResult.SingleFieldResolutionResult;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
+import com.android.tools.r8.ir.analysis.type.Nullability;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.analysis.value.SingleFieldValue;
 import com.android.tools.r8.ir.analysis.value.SingleValue;
 import com.android.tools.r8.ir.analysis.value.objectstate.ObjectState;
@@ -105,6 +108,8 @@
     }
 
     void eliminateRedundantRead(InstructionListIterator it, Instruction redundant);
+
+    TypeElement getType(AppView<?> appView, TypeElement outType);
   }
 
   private class ExistingValue implements FieldValue {
@@ -128,6 +133,11 @@
       value.uniquePhiUsers().forEach(Phi::removeTrivialPhi);
     }
 
+    @Override
+    public TypeElement getType(AppView<?> appView, TypeElement outType) {
+      return value.getType();
+    }
+
     public Value getValue() {
       return value;
     }
@@ -153,6 +163,27 @@
       it.replaceCurrentInstruction(
           value.createMaterializingInstruction(appView.withClassHierarchy(), code, redundant));
     }
+
+    @Override
+    public TypeElement getType(AppView<?> appView, TypeElement outType) {
+      DexItemFactory dexItemFactory = appView.dexItemFactory();
+      if (value.isSingleStringValue() || value.isSingleDexItemBasedStringValue()) {
+        return dexItemFactory.stringType.toTypeElement(
+            RedundantFieldLoadAndStoreElimination.this.appView, Nullability.definitelyNotNull());
+      }
+      if (value.isSingleFieldValue()) {
+        return value.asSingleFieldValue().getField().getTypeElement(appView);
+      }
+      // For numbers (and null), we don't encode the type along with the value. Therefore, we
+      // fallback to the existing out type in this case.
+      assert value.isSingleNumberValue();
+      if (outType.isReferenceType()) {
+        assert value.isNull();
+        return TypeElement.getNull();
+      }
+      assert outType.isPrimitiveType();
+      return outType;
+    }
   }
 
   private abstract static class ArraySlot {
@@ -574,10 +605,12 @@
     ArraySlot arraySlot = ArraySlot.create(array, index, arrayGet.getMemberType());
     FieldValue replacement = activeState.getArraySlotValue(arraySlot);
     if (replacement != null) {
-      replacement.eliminateRedundantRead(it, arrayGet);
+      TypeElement outType = arrayGet.outValue().getType();
+      if (replacement.getType(appView, outType).lessThanOrEqual(outType, appView)) {
+        replacement.eliminateRedundantRead(it, arrayGet);
+      }
       return;
     }
-
     activeState.putArraySlotValue(arraySlot, new ExistingValue(arrayGet.outValue()));
   }
 
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/redundantarraygetelimination/ArrayGetTypePromotionTest.java b/src/test/java/com/android/tools/r8/ir/optimize/redundantarraygetelimination/ArrayGetTypePromotionTest.java
index 1f9edef..ad45f19 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/redundantarraygetelimination/ArrayGetTypePromotionTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/redundantarraygetelimination/ArrayGetTypePromotionTest.java
@@ -70,8 +70,7 @@
         .setMinApi(parameters)
         .compile()
         .run(parameters.getRuntime(), Main.class)
-        // TODO(b/278573402): Disallow redundant array load elimination.
-        .assertFailureWithErrorThatThrows(VerifyError.class);
+        .assertSuccessWithOutputLines(EXPECTED_OUTPUT);
   }
 
   @Test
@@ -85,8 +84,7 @@
         .setMinApi(parameters)
         .compile()
         .run(parameters.getRuntime(), Main.class)
-        // TODO(b/278573402): Disallow redundant array load elimination.
-        .assertFailureWithErrorThatThrows(VerifyError.class);
+        .assertSuccessWithOutputLines(EXPECTED_OUTPUT);
   }
 
   static class Main {