Use resolved target when searching for assume* rules.

Bug: 110178673
Change-Id: Icc8693df7f825edf23e1719fb0abbf99d367a809
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
index e215f58..4596f3b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
@@ -263,10 +263,11 @@
 
     ProguardMemberRuleLookup lookup = lookupMemberRule(definition);
     if (lookup == null) {
-      // Since -assumenosideeffects rules are always applied to all overriding methods, we can
-      // simply check the target method, although this may be different from the dynamically
-      // targeted method.
-      DexEncodedMethod target = appView.definitionFor(invokedMethod);
+      // -assumenosideeffects rules are applied to upward visible and overriding methods, but only
+      // references that have actual definitions are marked by the root set builder. So, here, we
+      // try again with a resolved target, not the direct definition, which may not exist.
+      DexEncodedMethod target =
+          appView.appInfo().resolveMethod(invokedHolder, invokedMethod).asSingleTarget();
       lookup = lookupMemberRule(target);
     }
     boolean invokeReplaced = false;
diff --git a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsWithoutMatchingDefinitionTest.java b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsWithoutMatchingDefinitionTest.java
new file mode 100644
index 0000000..f008747
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsWithoutMatchingDefinitionTest.java
@@ -0,0 +1,153 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking.assumenosideeffects;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.DexIndexedConsumer.ArchiveConsumer;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Streams;
+import java.nio.file.Path;
+import java.util.Collection;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+class LibraryBase {
+  boolean isInEditMode() {
+    return System.currentTimeMillis() <= 0;
+  }
+}
+
+class LibrarySub extends LibraryBase {}
+
+class ProgramClass extends LibrarySub {
+  private final String test;
+
+  private ProgramClass() {
+    if (isInEditMode()) {
+      test = "test";
+    } else {
+      test = "production";
+    }
+  }
+
+  public static void main(String... args) {
+    System.out.println(new ProgramClass().test);
+  }
+}
+
+@RunWith(Parameterized.class)
+public class AssumenosideeffectsWithoutMatchingDefinitionTest extends TestBase {
+  private static final Class<?> MAIN = ProgramClass.class;
+  private static final String EXPECTED_OUTPUT = StringUtils.lines("production");
+
+  enum TestConfig {
+    RULE_THAT_DIRECTLY_REFERS_BASE,
+    RULE_THAT_DIRECTLY_REFERS_SUB,
+    RULE_WITH_EXTENDS_BASE,
+    RULE_WITH_EXTENDS_SUB;
+
+    public String getKeepRule() {
+      switch (this) {
+        case RULE_THAT_DIRECTLY_REFERS_BASE:
+          return StringUtils.lines(
+              "-assumenosideeffects class **.LibraryBase {",
+              "  boolean isInEditMode() return false;",
+              "}");
+        case RULE_THAT_DIRECTLY_REFERS_SUB:
+          return StringUtils.lines(
+              "-assumenosideeffects class **.LibrarySub {",
+              "  boolean isInEditMode() return false;",
+              "}");
+        case RULE_WITH_EXTENDS_BASE:
+          return StringUtils.lines(
+              "-assumenosideeffects class * extends **.LibraryBase {",
+              "  boolean isInEditMode() return false;",
+              "}");
+        case RULE_WITH_EXTENDS_SUB:
+          return StringUtils.lines(
+              "-assumenosideeffects class * extends **.LibrarySub {",
+              "  boolean isInEditMode() return false;",
+              "}");
+      }
+      throw new Unreachable();
+    }
+
+    public void inspect(CodeInspector inspector) {
+      ClassSubject main = inspector.clazz(MAIN);
+      assertThat(main, isPresent());
+
+      MethodSubject init = main.init();
+      assertThat(init, isPresent());
+      assertEquals(
+          0,
+          Streams.stream(init.iterateInstructions(
+              i -> i.isIf() || i.isIfNez() || i.isIfEqz())).count());
+    }
+  }
+
+  @Parameterized.Parameters(name = "{0} {1}")
+  public static Collection<Object[]> data() {
+    return buildParameters(getTestParameters().withAllRuntimes().build(), TestConfig.values());
+  }
+
+  private final TestParameters parameters;
+  private final TestConfig config;
+
+  public AssumenosideeffectsWithoutMatchingDefinitionTest(
+      TestParameters parameters, TestConfig config) {
+    this.parameters = parameters;
+    this.config = config;
+  }
+
+  @ClassRule
+  public static TemporaryFolder staticTemp = ToolHelper.getTemporaryFolderForTest();
+
+  private static Path libJarPath;
+  private static Path libDexPath;
+
+  @BeforeClass
+  public static void prepareLibJar() throws Exception {
+    libJarPath = staticTemp.newFile("lib.jar").toPath().toAbsolutePath();
+    writeClassesToJar(libJarPath, ImmutableList.of(LibraryBase.class, LibrarySub.class));
+    libDexPath = staticTemp.newFile("lib.dex").toPath().toAbsolutePath();
+    testForD8(staticTemp)
+        .addProgramFiles(libJarPath)
+        .setMinApi(AndroidApiLevel.B)
+        .setProgramConsumer(new ArchiveConsumer(libDexPath))
+        .compile();
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addLibraryFiles(libJarPath)
+        .addLibraryFiles(ToolHelper.getDefaultAndroidJar())
+        .addProgramClasses(MAIN)
+        .addKeepMainRule(MAIN)
+        .addKeepRules(config.getKeepRule())
+        .noMinification()
+        .setMinApi(parameters.getRuntime())
+        .compile()
+        .addRunClasspathFiles(parameters.isDexRuntime() ? libDexPath : libJarPath)
+        .run(parameters.getRuntime(), MAIN)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT)
+        .inspect(config::inspect);
+  }
+}