Ensure value materializable in trivial phi removal

In the concrete issue reported the single field value that a phi resolved
to was not accessible in the context of the phi.

Bug: b/345248270
Change-Id: I41527a6df72726135c5c35fa9b4a8433f377f8e5
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/phis/EffectivelyTrivialPhiOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/phis/EffectivelyTrivialPhiOptimization.java
index bf3bc86..514ed67 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/phis/EffectivelyTrivialPhiOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/phis/EffectivelyTrivialPhiOptimization.java
@@ -182,7 +182,10 @@
       return new SingleValueOrValue(worklist.getSeenSet());
     }
     if (foundDifferentOperandValuesWithSameAbstractValue) {
-      if (representativeOperandAbstractValue.isSingleValue()) {
+      if (representativeOperandAbstractValue.isSingleValue()
+          && representativeOperandAbstractValue
+              .asSingleValue()
+              .isMaterializableInContext(appView, code.context())) {
         return new SingleValueOrValue(
             worklist.getSeenSet(), representativeOperandAbstractValue.asSingleValue());
       }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/effectivelytrivialphioptimization/Regress345248270ConstClassTest.java b/src/test/java/com/android/tools/r8/ir/optimize/effectivelytrivialphioptimization/Regress345248270ConstClassTest.java
index dcd81ac..684f3a4 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/effectivelytrivialphioptimization/Regress345248270ConstClassTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/effectivelytrivialphioptimization/Regress345248270ConstClassTest.java
@@ -3,10 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.optimize.effectivelytrivialphioptimization;
 
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
@@ -30,7 +26,7 @@
     return getTestParameters().withAllRuntimesAndApiLevels().build();
   }
 
-  private static final String EXPECTED_OUTPUT = StringUtils.lines("Hello, world!");
+  private static final String EXPECTED_OUTPUT = StringUtils.lines("true");
 
   @Test
   public void testD8() throws Exception {
@@ -46,24 +42,16 @@
 
   @Test
   public void testR8() throws Exception {
-    try {
-      testForR8(parameters.getBackend())
-          .addInnerClasses(getClass())
-          .addProgramClasses(
-              I.class, PublicAccessor.class, PublicAccessor.getPackagePrivateImplementationClass())
-          .addKeepMainRule(TestClass.class)
-          .setMinApi(parameters.getApiLevel())
-          .enableNeverClassInliningAnnotations()
-          .enableNoAccessModificationAnnotationsForMembers()
-          .run(parameters.getRuntime(), TestClass.class)
-          .assertSuccessWithOutput(EXPECTED_OUTPUT);
-      fail("Expected exception");
-    } catch (CompilationFailedException e) {
-      // TODO(b/345248270): Should not hit an assertion.
-      assertTrue(e.getCause() instanceof AssertionError);
-      return;
-    }
-    fail("Expected CompilationFailedException");
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addProgramClasses(
+            I.class, PublicAccessor.class, PublicAccessor.getPackagePrivateImplementationClass())
+        .addKeepMainRule(TestClass.class)
+        .setMinApi(parameters.getApiLevel())
+        .enableNeverClassInliningAnnotations()
+        .enableNoAccessModificationAnnotationsForMembers()
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
   }
 
   static class TestClass {
@@ -78,9 +66,15 @@
       return r;
     }
 
+    public static Class<?> test2() {
+      return System.currentTimeMillis() > 0
+          ? PublicAccessor.getPackagePrivateImplementationClass()
+          : null;
+    }
+
     public static void main(String[] args) {
-      test();
-      System.out.println("Hello, world!");
+      Class<?> c = test();
+      System.out.println(c == test2());
     }
   }
 }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/effectivelytrivialphioptimization/Regress345248270Test.java b/src/test/java/com/android/tools/r8/ir/optimize/effectivelytrivialphioptimization/Regress345248270Test.java
index aa916ba..0b0fcb3 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/effectivelytrivialphioptimization/Regress345248270Test.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/effectivelytrivialphioptimization/Regress345248270Test.java
@@ -3,16 +3,19 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.optimize.effectivelytrivialphioptimization;
 
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
 
-import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.ir.optimize.effectivelytrivialphioptimization.b345248270.I;
 import com.android.tools.r8.ir.optimize.effectivelytrivialphioptimization.b345248270.PublicAccessor;
 import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.Matchers;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -30,7 +33,7 @@
     return getTestParameters().withAllRuntimesAndApiLevels().build();
   }
 
-  private static final String EXPECTED_OUTPUT = StringUtils.lines("Hello, world!");
+  private static final String EXPECTED_OUTPUT = StringUtils.lines("true");
 
   @Test
   public void testD8() throws Exception {
@@ -46,24 +49,42 @@
 
   @Test
   public void testR8() throws Exception {
-    try {
-      testForR8(parameters.getBackend())
-          .addInnerClasses(getClass())
-          .addProgramClasses(
-              I.class, PublicAccessor.class, PublicAccessor.getPackagePrivateImplementationClass())
-          .addKeepMainRule(TestClass.class)
-          .setMinApi(parameters.getApiLevel())
-          .enableNeverClassInliningAnnotations()
-          .enableNoAccessModificationAnnotationsForMembers()
-          .run(parameters.getRuntime(), TestClass.class)
-          .assertSuccessWithOutput(EXPECTED_OUTPUT);
-      fail("Expected exception");
-    } catch (CompilationFailedException e) {
-      // TODO(b/345248270): Should not hit an assertion.
-      assertTrue(e.getCause() instanceof AssertionError);
-      return;
-    }
-    fail("Expected CompilationFailedException");
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addProgramClasses(
+            I.class, PublicAccessor.class, PublicAccessor.getPackagePrivateImplementationClass())
+        .addKeepMainRule(TestClass.class)
+        .setMinApi(parameters.getApiLevel())
+        .enableNeverClassInliningAnnotations()
+        .enableNoAccessModificationAnnotationsForMembers()
+        .run(parameters.getRuntime(), TestClass.class)
+        .inspect(
+            inspector -> {
+              assertThat(inspector.clazz(TestClass.class), isPresent());
+              // test is inlined into main.
+              assertThat(
+                  inspector.clazz(TestClass.class).uniqueMethodWithOriginalName("test"),
+                  Matchers.isAbsent());
+              ClassSubject packagePrivateImplementation =
+                  inspector.clazz(PublicAccessor.getPackagePrivateImplementationClass());
+              assertThat(packagePrivateImplementation, isPresent());
+              // No direct field access of the package private field.
+              assertTrue(
+                  inspector
+                      .clazz(TestClass.class)
+                      .mainMethod()
+                      .streamInstructions()
+                      .filter(InstructionSubject::isFieldAccess)
+                      .map(InstructionSubject::getField)
+                      .noneMatch(
+                          f ->
+                              f.getType()
+                                  .isIdenticalTo(
+                                      packagePrivateImplementation
+                                          .getDexProgramClass()
+                                          .getType())));
+            })
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
   }
 
   static class TestClass {
@@ -78,9 +99,15 @@
       return r;
     }
 
+    public static I test2() {
+      return System.currentTimeMillis() > 0
+          ? PublicAccessor.getPackagePrivateImplementation()
+          : null;
+    }
+
     public static void main(String[] args) {
-      test();
-      System.out.println("Hello, world!");
+      I i = test();
+      System.out.println(i == test2());
     }
   }
 }